summaryrefslogtreecommitdiffstats
path: root/sys/net80211
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net80211')
-rw-r--r--sys/net80211/_ieee80211.h119
-rw-r--r--sys/net80211/ieee80211.c1002
-rw-r--r--sys/net80211/ieee80211.h19
-rw-r--r--sys/net80211/ieee80211_acl.c116
-rw-r--r--sys/net80211/ieee80211_adhoc.c877
-rw-r--r--sys/net80211/ieee80211_adhoc.h35
-rw-r--r--sys/net80211/ieee80211_amrr.c178
-rw-r--r--sys/net80211/ieee80211_amrr.h53
-rw-r--r--sys/net80211/ieee80211_crypto.c256
-rw-r--r--sys/net80211/ieee80211_crypto.h93
-rw-r--r--sys/net80211/ieee80211_crypto_ccmp.c53
-rw-r--r--sys/net80211/ieee80211_crypto_none.c37
-rw-r--r--sys/net80211/ieee80211_crypto_tkip.c98
-rw-r--r--sys/net80211/ieee80211_crypto_wep.c48
-rw-r--r--sys/net80211/ieee80211_ddb.c789
-rw-r--r--sys/net80211/ieee80211_dfs.c372
-rw-r--r--sys/net80211/ieee80211_dfs.h57
-rw-r--r--sys/net80211/ieee80211_freebsd.c397
-rw-r--r--sys/net80211/ieee80211_freebsd.h318
-rw-r--r--sys/net80211/ieee80211_hostap.c2236
-rw-r--r--sys/net80211/ieee80211_hostap.h35
-rw-r--r--sys/net80211/ieee80211_ht.c591
-rw-r--r--sys/net80211/ieee80211_ht.h84
-rw-r--r--sys/net80211/ieee80211_input.c3250
-rw-r--r--sys/net80211/ieee80211_input.h156
-rw-r--r--sys/net80211/ieee80211_ioctl.c2511
-rw-r--r--sys/net80211/ieee80211_ioctl.h253
-rw-r--r--sys/net80211/ieee80211_monitor.c136
-rw-r--r--sys/net80211/ieee80211_monitor.h35
-rw-r--r--sys/net80211/ieee80211_node.c1605
-rw-r--r--sys/net80211/ieee80211_node.h257
-rw-r--r--sys/net80211/ieee80211_output.c2026
-rw-r--r--sys/net80211/ieee80211_phy.c472
-rw-r--r--sys/net80211/ieee80211_phy.h149
-rw-r--r--sys/net80211/ieee80211_power.c239
-rw-r--r--sys/net80211/ieee80211_power.h8
-rw-r--r--sys/net80211/ieee80211_proto.c1251
-rw-r--r--sys/net80211/ieee80211_proto.h190
-rw-r--r--sys/net80211/ieee80211_regdomain.c429
-rw-r--r--sys/net80211/ieee80211_regdomain.h96
-rw-r--r--sys/net80211/ieee80211_rssadapt.c273
-rw-r--r--sys/net80211/ieee80211_rssadapt.h101
-rw-r--r--sys/net80211/ieee80211_scan.c703
-rw-r--r--sys/net80211/ieee80211_scan.h147
-rw-r--r--sys/net80211/ieee80211_scan_ap.c408
-rw-r--r--sys/net80211/ieee80211_scan_sta.c837
-rw-r--r--sys/net80211/ieee80211_sta.c1647
-rw-r--r--sys/net80211/ieee80211_sta.h36
-rw-r--r--sys/net80211/ieee80211_var.h511
-rw-r--r--sys/net80211/ieee80211_wds.c865
-rw-r--r--sys/net80211/ieee80211_wds.h39
-rw-r--r--sys/net80211/ieee80211_xauth.c37
52 files changed, 18015 insertions, 8515 deletions
diff --git a/sys/net80211/_ieee80211.h b/sys/net80211/_ieee80211.h
index 9f50e3c4..d41659f 100644
--- a/sys/net80211/_ieee80211.h
+++ b/sys/net80211/_ieee80211.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,16 +28,31 @@
#ifndef _NET80211__IEEE80211_H_
#define _NET80211__IEEE80211_H_
+/*
+ * 802.11 implementation definitions.
+ *
+ * NB: this file is used by applications.
+ */
+
+/*
+ * PHY type; mostly used to identify FH phys.
+ */
enum ieee80211_phytype {
IEEE80211_T_DS, /* direct sequence spread spectrum */
IEEE80211_T_FH, /* frequency hopping */
IEEE80211_T_OFDM, /* frequency division multiplexing */
IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */
- IEEE80211_T_HT, /* high throughput, full GI */
+ IEEE80211_T_HT, /* high throughput */
};
#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
-/* XXX not really a mode; there are really multiple PHY's */
+/*
+ * PHY mode; this is not really a mode as multi-mode devices
+ * have multiple PHY's. Mode is mostly used as a shorthand
+ * for constraining which channels to consider in setting up
+ * operation. Modes used to be used more extensively when
+ * channels were identified as IEEE channel numbers.
+ */
enum ieee80211_phymode {
IEEE80211_MODE_AUTO = 0, /* autoselect */
IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */
@@ -52,13 +67,18 @@ enum ieee80211_phymode {
};
#define IEEE80211_MODE_MAX (IEEE80211_MODE_11NG+1)
+/*
+ * Operating mode. Devices do not necessarily support
+ * all modes; they indicate which are supported in their
+ * capabilities.
+ */
enum ieee80211_opmode {
- IEEE80211_M_STA = 1, /* infrastructure station */
IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */
+ IEEE80211_M_STA = 1, /* infrastructure station */
+ IEEE80211_M_WDS = 2, /* WDS link */
IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */
- IEEE80211_M_HOSTAP = 6, /* Software Access Point */
- IEEE80211_M_MONITOR = 8, /* Monitor mode */
- IEEE80211_M_WDS = 2 /* WDS link */
+ IEEE80211_M_HOSTAP = 4, /* Software Access Point */
+ IEEE80211_M_MONITOR = 5, /* Monitor mode */
};
#define IEEE80211_OPMODE_MAX (IEEE80211_M_MONITOR+1)
@@ -72,7 +92,11 @@ enum ieee80211_protmode {
};
/*
- * Authentication mode.
+ * Authentication mode. The open and shared key authentication
+ * modes are implemented within the 802.11 layer. 802.1x and
+ * WPA/802.11i are implemented in user mode by setting the
+ * 802.11 layer into IEEE80211_AUTH_8021X and deferring
+ * authentication to user space programs.
*/
enum ieee80211_authmode {
IEEE80211_AUTH_NONE = 0,
@@ -265,18 +289,28 @@ struct ieee80211_channel {
#define IEEE80211_NONQOS_TID WME_NUM_TID /* index for non-QoS sta */
/*
+ * The 802.11 spec says at most 2007 stations may be
+ * associated at once. For most AP's this is way more
+ * than is feasible so we use a default of 128. This
+ * number may be overridden by the driver and/or by
+ * user configuration but may not be less than IEEE80211_AID_MIN.
+ */
+#define IEEE80211_AID_DEF 128
+#define IEEE80211_AID_MIN 16
+
+/*
* 802.11 rate set.
*/
#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */
#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */
struct ieee80211_rateset {
- uint8_t rs_nrates;
- uint8_t rs_rates[IEEE80211_RATE_MAXSIZE];
+ uint8_t rs_nrates;
+ uint8_t rs_rates[IEEE80211_RATE_MAXSIZE];
};
/*
- * 802.11n variant of ieee80211_rateset. Instead
+ * 802.11n variant of ieee80211_rateset. Instead of
* legacy rates the entries are MCS rates. We define
* the structure such that it can be used interchangeably
* with an ieee80211_rateset (modulo structure size).
@@ -284,27 +318,58 @@ struct ieee80211_rateset {
#define IEEE80211_HTRATE_MAXSIZE 127
struct ieee80211_htrateset {
- uint8_t rs_nrates;
- uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE];
+ uint8_t rs_nrates;
+ uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE];
};
#define IEEE80211_RATE_MCS 0x80
/*
- * Roaming state visible to user space. There are two
- * thresholds that control whether roaming is considered;
- * when either is exceeded the 802.11 layer will check
- * the scan cache for another AP. If the cache is stale
- * then a scan may be triggered.
+ * Per-mode transmit parameters/controls visible to user space.
+ * These can be used to set fixed transmit rate for all operating
+ * modes or on a per-client basis according to the capabilities
+ * of the client (e.g. an 11b client associated to an 11g ap).
+ *
+ * MCS are distinguished from legacy rates by or'ing in 0x80.
+ */
+struct ieee80211_txparam {
+ uint8_t ucastrate; /* ucast data rate (legacy/MCS|0x80) */
+ uint8_t mgmtrate; /* mgmt frame rate (legacy/MCS|0x80) */
+ uint8_t mcastrate; /* multicast rate (legacy/MCS|0x80) */
+ uint8_t maxretry; /* max unicast data retry count */
+};
+
+/*
+ * Per-mode roaming state visible to user space. There are two
+ * thresholds that control whether roaming is considered; when
+ * either is exceeded the 802.11 layer will check the scan cache
+ * for another AP. If the cache is stale then a scan may be
+ * triggered.
+ */
+struct ieee80211_roamparam {
+ int8_t rssi; /* rssi thresh (.5 dBm) */
+ uint8_t rate; /* tx rate thresh (.5 Mb/s or MCS) */
+ uint16_t pad; /* reserve */
+};
+
+/*
+ * Regulatory Information.
+ */
+struct ieee80211_regdomain {
+ uint16_t regdomain; /* SKU */
+ uint16_t country; /* ISO country code */
+ uint8_t location; /* I (indoor), O (outdoor), other */
+ uint8_t ecm; /* Extended Channel Mode */
+ char isocc[2]; /* country code string */
+ short pad[2];
+};
+
+/*
+ * MIMO antenna/radio state.
*/
-struct ieee80211_roam {
- int8_t rssi11a; /* rssi thresh for 11a bss */
- int8_t rssi11b; /* for 11g sta in 11b bss */
- int8_t rssi11bOnly; /* for 11b sta */
- uint8_t pad1;
- uint8_t rate11a; /* rate thresh for 11a bss */
- uint8_t rate11b; /* for 11g sta in 11b bss */
- uint8_t rate11bOnly; /* for 11b sta */
- uint8_t pad2;
+struct ieee80211_mimo_info {
+ int8_t rssi[3]; /* per-antenna rssi */
+ int8_t noise[3]; /* per-antenna noise floor */
+ uint32_t evm[3]; /* EVM data */
};
#endif /* _NET80211__IEEE80211_H_ */
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
index dbeb7a3..952f420 100644
--- a/sys/net80211/ieee80211.c
+++ b/sys/net80211/ieee80211.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 generic handler
*/
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -38,10 +39,13 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_dl.h>
#include <net/if_media.h>
+#include <net/if_types.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
#include <net/bpf.h>
@@ -57,6 +61,20 @@ const char *ieee80211_phymode_name[] = {
"11na", /* IEEE80211_MODE_11NA */
"11ng", /* IEEE80211_MODE_11NG */
};
+static const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag);
+static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag);
+static int ieee80211_media_setup(struct ieee80211com *ic,
+ struct ifmedia *media, int caps, int addsta,
+ ifm_change_cb_t media_change, ifm_stat_cb_t media_stat);
+static void ieee80211com_media_status(struct ifnet *, struct ifmediareq *);
+static int ieee80211com_media_change(struct ifnet *);
+static int media_status(enum ieee80211_opmode,
+ const struct ieee80211_channel *);
+
+MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state");
/*
* Default supported rates for 802.11 operation (in IEEE .5Mb units).
@@ -75,66 +93,6 @@ static const struct ieee80211_rateset ieee80211_rateset_11g =
{ 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } };
#undef B
-static int media_status(enum ieee80211_opmode ,
- const struct ieee80211_channel *);
-
-/* list of all instances */
-SLIST_HEAD(ieee80211_list, ieee80211com);
-static struct ieee80211_list ieee80211_list =
- SLIST_HEAD_INITIALIZER(ieee80211_list);
-static uint8_t ieee80211_vapmap[32]; /* enough for 256 */
-static struct mtx ieee80211_vap_mtx;
-MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF);
-
-static void
-ieee80211_add_vap(struct ieee80211com *ic)
-{
-#define N(a) (sizeof(a)/sizeof(a[0]))
- int i;
- uint8_t b;
-
- mtx_lock(&ieee80211_vap_mtx);
- ic->ic_vap = 0;
- for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++)
- ic->ic_vap += NBBY;
- if (i == N(ieee80211_vapmap))
- panic("vap table full");
- for (b = ieee80211_vapmap[i]; b & 1; b >>= 1)
- ic->ic_vap++;
- setbit(ieee80211_vapmap, ic->ic_vap);
- SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next);
- mtx_unlock(&ieee80211_vap_mtx);
-#undef N
-}
-
-static void
-ieee80211_remove_vap(struct ieee80211com *ic)
-{
- mtx_lock(&ieee80211_vap_mtx);
- SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next);
- KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY,
- ("invalid vap id %d", ic->ic_vap));
- KASSERT(isset(ieee80211_vapmap, ic->ic_vap),
- ("vap id %d not allocated", ic->ic_vap));
- clrbit(ieee80211_vapmap, ic->ic_vap);
- mtx_unlock(&ieee80211_vap_mtx);
-}
-
-/*
- * Default reset method for use with the ioctl support. This
- * method is invoked after any state change in the 802.11
- * layer that should be propagated to the hardware but not
- * require re-initialization of the 802.11 state machine (e.g
- * rescanning for an ap). We always return ENETRESET which
- * should cause the driver to re-initialize the device. Drivers
- * can override this method to implement more optimized support.
- */
-static int
-ieee80211_default_reset(struct ifnet *ifp)
-{
- return ENETRESET;
-}
-
/*
* Fill in 802.11 available channel set, mark
* all available channels as active, and pick
@@ -153,6 +111,7 @@ ieee80211_chan_init(struct ieee80211com *ic)
KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
("invalid number of channels specified: %u", ic->ic_nchans));
memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
+ memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps));
setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO);
for (i = 0; i < ic->ic_nchans; i++) {
c = &ic->ic_channels[i];
@@ -186,9 +145,13 @@ ieee80211_chan_init(struct ieee80211com *ic)
memcpy(ic->ic_chan_active, ic->ic_chan_avail,
sizeof(ic->ic_chan_avail));
- ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
+ /* sort channel table to allow lookup optimizations */
+ ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans);
+
+ /* invalidate any previous state */
ic->ic_bsschan = IEEE80211_CHAN_ANYC;
ic->ic_prevchan = NULL;
+ ic->ic_csa_newchan = NULL;
/* arbitrarily pick the first channel */
ic->ic_curchan = &ic->ic_channels[0];
@@ -206,54 +169,44 @@ ieee80211_chan_init(struct ieee80211com *ic)
#undef DEFAULTRATES
}
+static void
+null_update_mcast(struct ifnet *ifp)
+{
+ if_printf(ifp, "need multicast update callback\n");
+}
+
+static void
+null_update_promisc(struct ifnet *ifp)
+{
+ if_printf(ifp, "need promiscuous mode update callback\n");
+}
+
+/*
+ * 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)
{
struct ifnet *ifp = ic->ic_ifp;
+ struct sockaddr_dl *sdl;
+ struct ifaddr *ifa;
- ether_ifattach(ifp, ic->ic_myaddr);
- ifp->if_output = ieee80211_output;
-
- bpfattach2(ifp, DLT_IEEE802_11,
- sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
-
- /* override the 802.3 setting */
- ifp->if_hdrlen = ic->ic_headroom
- + sizeof(struct ieee80211_qosframe_addr4)
- + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
- + IEEE80211_WEP_EXTIVLEN;
- /* XXX no way to recalculate on ifdetach */
- if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
- /* XXX sanity check... */
- max_linkhdr = ALIGN(ifp->if_hdrlen);
- max_hdr = max_linkhdr + max_protohdr;
- max_datalen = MHLEN - max_hdr;
- }
+ KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type));
+ IEEE80211_LOCK_INIT(ic, "ieee80211com");
+ TAILQ_INIT(&ic->ic_vaps);
/*
* Fill in 802.11 available channel set, mark all
* available channels as active, and pick a default
* channel if not already specified.
*/
- ieee80211_chan_init(ic);
+ ieee80211_media_init(ic);
- if (ic->ic_caps & IEEE80211_C_BGSCAN) /* enable if capable */
- ic->ic_flags |= IEEE80211_F_BGSCAN;
-#if 0
- /* XXX not until WME+WPA issues resolved */
- if (ic->ic_caps & IEEE80211_C_WME) /* enable if capable */
- ic->ic_flags |= IEEE80211_F_WME;
-#endif
- if (ic->ic_caps & IEEE80211_C_BURST)
- ic->ic_flags |= IEEE80211_F_BURST;
- ic->ic_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */
+ ic->ic_update_mcast = null_update_mcast;
+ ic->ic_update_promisc = null_update_promisc;
ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
- ic->ic_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
- ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT;
- IEEE80211_LOCK_INIT(ic, "ieee80211com");
- IEEE80211_BEACON_LOCK_INIT(ic, "beacon");
-
ic->ic_lintval = ic->ic_bintval;
ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
@@ -263,30 +216,42 @@ ieee80211_ifattach(struct ieee80211com *ic)
ieee80211_proto_attach(ic);
ieee80211_ht_attach(ic);
ieee80211_scan_attach(ic);
-
- ieee80211_add_vap(ic);
-
- ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */
-
- /*
- * Install a default reset method for the ioctl support.
- * The driver is expected to fill this in before calling us.
- */
- if (ic->ic_reset == NULL)
- ic->ic_reset = ieee80211_default_reset;
-
- KASSERT(ifp->if_llsoftc == NULL, ("oops, hosed"));
- ifp->if_llsoftc = ic;
+ ieee80211_regdomain_attach(ic);
+
+ ieee80211_sysctl_attach(ic);
+
+ ifp->if_addrlen = IEEE80211_ADDR_LEN;
+ ifp->if_hdrlen = 0;
+ if_attach(ifp);
+ ifp->if_mtu = IEEE80211_MTU_MAX;
+ ifp->if_broadcastaddr = ieee80211broadcastaddr;
+
+ 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), ic->ic_myaddr);
}
+/*
+ * Detach net80211 state on device detach. Tear down
+ * all vap's and reclaim all common state prior to the
+ * device state going away. Note we may call back into
+ * driver; it must be prepared for this.
+ */
void
ieee80211_ifdetach(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap;
- ieee80211_remove_vap(ic);
+ /* XXX ieee80211_stop_all? */
+ while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
+ ieee80211_vap_destroy(vap);
ieee80211_sysctl_detach(ic);
+ ieee80211_regdomain_detach(ic);
ieee80211_scan_detach(ic);
ieee80211_ht_detach(ic);
/* NB: must be called before ieee80211_node_detach */
@@ -297,10 +262,386 @@ ieee80211_ifdetach(struct ieee80211com *ic)
ifmedia_removeall(&ic->ic_media);
IEEE80211_LOCK_DESTROY(ic);
- IEEE80211_BEACON_LOCK_DESTROY(ic);
+ if_detach(ifp);
+}
+
+/*
+ * Default reset method for use with the ioctl support. This
+ * method is invoked after any state change in the 802.11
+ * layer that should be propagated to the hardware but not
+ * require re-initialization of the 802.11 state machine (e.g
+ * rescanning for an ap). We always return ENETRESET which
+ * should cause the driver to re-initialize the device. Drivers
+ * can override this method to implement more optimized support.
+ */
+static int
+default_reset(struct ieee80211vap *vap, u_long cmd)
+{
+ return ENETRESET;
+}
+
+/*
+ * Prepare a vap for use. Drivers use this call to
+ * setup net80211 state in new vap's prior attaching
+ * them with ieee80211_vap_attach (below).
+ */
+int
+ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
+{
+#define IEEE80211_C_OPMODE \
+ (IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \
+ IEEE80211_C_MONITOR | IEEE80211_C_WDS)
+ struct ifnet *ifp;
+
+ ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ if_printf(ic->ic_ifp, "%s: unable to allocate ifnet\n",
+ __func__);
+ return ENOMEM;
+ }
+ if_initname(ifp, name, unit);
+ ifp->if_softc = vap; /* back pointer */
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+ ifp->if_start = ieee80211_start;
+ ifp->if_ioctl = ieee80211_ioctl;
+ ifp->if_watchdog = NULL; /* NB: no watchdog routine */
+ ifp->if_init = ieee80211_init;
+ /* NB: input+output filled in by ether_ifattach */
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ vap->iv_ifp = ifp;
+ vap->iv_ic = ic;
+ vap->iv_flags = ic->ic_flags; /* propagate common flags */
+ vap->iv_flags_ext = ic->ic_flags_ext;
+ vap->iv_flags_ven = ic->ic_flags_ven;
+ vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE;
+ vap->iv_htcaps = ic->ic_htcaps;
+ vap->iv_opmode = opmode;
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ /* auto-enable s/w beacon miss support */
+ if (flags & IEEE80211_CLONE_NOBEACONS)
+ vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS;
+ break;
+ case IEEE80211_M_IBSS:
+ vap->iv_caps |= IEEE80211_C_IBSS;
+ break;
+ case IEEE80211_M_AHDEMO:
+ vap->iv_caps |= IEEE80211_C_AHDEMO;
+ break;
+ case IEEE80211_M_HOSTAP:
+ vap->iv_caps |= IEEE80211_C_HOSTAP;
+ break;
+ case IEEE80211_M_MONITOR:
+ vap->iv_caps |= IEEE80211_C_MONITOR;
+ break;
+ case IEEE80211_M_WDS:
+ vap->iv_caps |= IEEE80211_C_WDS;
+ /*
+ * WDS links must specify the bssid of the far end.
+ * For legacy operation this is a static relationship.
+ * For non-legacy operation the station must associate
+ * and be authorized to pass traffic. Plumbing the
+ * vap to the proper node happens when the vap
+ * transitions to RUN state.
+ */
+ IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid);
+ vap->iv_flags |= IEEE80211_F_DESBSSID;
+ if (flags & IEEE80211_CLONE_WDSLEGACY)
+ vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY;
+ break;
+ }
+ /*
+ * Enable various functionality by default if we're
+ * capable; the driver can override us if it knows better.
+ */
+ if (vap->iv_caps & IEEE80211_C_WME)
+ vap->iv_flags |= IEEE80211_F_WME;
+ if (vap->iv_caps & IEEE80211_C_BURST)
+ vap->iv_flags |= IEEE80211_F_BURST;
+ if (vap->iv_caps & IEEE80211_C_FF)
+ vap->iv_flags |= IEEE80211_F_FF;
+ if (vap->iv_caps & IEEE80211_C_TURBOP)
+ vap->iv_flags |= IEEE80211_F_TURBOP;
+ /* NB: bg scanning only makes sense for station mode right now */
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ (vap->iv_caps & IEEE80211_C_BGSCAN))
+ vap->iv_flags |= IEEE80211_F_BGSCAN;
+ vap->iv_flags |= IEEE80211_F_DOTH; /* XXX out of caps, just ena */
+ /* XXX out of caps, just ena */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ vap->iv_flags_ext |= IEEE80211_FEXT_DFS;
+
+ vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
+ vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT;
+ vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT;
+ /*
+ * Install a default reset method for the ioctl support;
+ * the driver can override this.
+ */
+ vap->iv_reset = default_reset;
+
+ IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr);
+
+ ieee80211_sysctl_vattach(vap);
+ ieee80211_crypto_vattach(vap);
+ ieee80211_node_vattach(vap);
+ ieee80211_power_vattach(vap);
+ ieee80211_proto_vattach(vap);
+ ieee80211_ht_vattach(vap);
+ ieee80211_scan_vattach(vap);
+ ieee80211_regdomain_vattach(vap);
+
+ return 0;
+#undef IEEE80211_C_OPMODE
+}
+
+/*
+ * Activate a vap. State should have been prepared with a
+ * call to ieee80211_vap_setup and by the driver. On return
+ * from this call the vap is ready for use.
+ */
+int
+ieee80211_vap_attach(struct ieee80211vap *vap,
+ ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
+{
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifmediareq imr;
+ int maxrate;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s parent %s flags 0x%x flags_ext 0x%x\n",
+ __func__, ieee80211_opmode_name[vap->iv_opmode],
+ ic->ic_ifp->if_xname, vap->iv_flags, vap->iv_flags_ext);
- bpfdetach(ifp);
+ /*
+ * Do late attach work that cannot happen until after
+ * the driver has had a chance to override defaults.
+ */
+ ieee80211_node_latevattach(vap);
+ ieee80211_power_latevattach(vap);
+
+ maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps,
+ vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat);
+ ieee80211_media_status(ifp, &imr);
+ /* NB: strip explicit mode; we're actually in autoselect */
+ ifmedia_set(&vap->iv_media, imr.ifm_active &~ IFM_MMASK);
+ if (maxrate)
+ ifp->if_baudrate = IF_Mbps(maxrate);
+
+ ether_ifattach(ifp, vap->iv_myaddr);
+ /* 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 */
+ bpfattach2(ifp, DLT_IEEE802_11, ifp->if_hdrlen, &vap->iv_rawbpf);
+
+ IEEE80211_LOCK(ic);
+ TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_WME);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_PCF);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_BURST);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ ieee80211_syncifflag_locked(ic, IFF_ALLMULTI);
+ IEEE80211_UNLOCK(ic);
+
+ return 1;
+}
+
+/*
+ * Tear down vap state and reclaim the ifnet.
+ * The driver is assumed to have prepared for
+ * this; e.g. by turning off interrupts for the
+ * underlying device.
+ */
+void
+ieee80211_vap_detach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n",
+ __func__, ieee80211_opmode_name[vap->iv_opmode],
+ ic->ic_ifp->if_xname);
+
+ IEEE80211_LOCK(ic);
+ /* block traffic from above */
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ /*
+ * Evil hack. Clear the backpointer from the ifnet to the
+ * vap so any requests from above will return an error or
+ * be ignored. In particular this short-circuits requests
+ * by the bridge to turn off promiscuous mode as a result
+ * of calling ether_ifdetach.
+ */
+ ifp->if_softc = NULL;
+ /*
+ * Stop the vap before detaching the ifnet. Ideally we'd
+ * do this in the other order so the ifnet is inaccessible
+ * while we cleanup internal state but that is hard.
+ */
+ ieee80211_stop_locked(vap);
+
+ /* XXX accumulate iv_stats in ic_stats? */
+ TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_WME);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_PCF);
+ ieee80211_syncflag_locked(ic, IEEE80211_F_BURST);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_HT);
+ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_USEHT40);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ ieee80211_syncifflag_locked(ic, IFF_ALLMULTI);
+ IEEE80211_UNLOCK(ic);
+
+ /* XXX can't hold com lock */
+ /* NB: bpfattach is called by ether_ifdetach and claims all taps */
ether_ifdetach(ifp);
+
+ ifmedia_removeall(&vap->iv_media);
+
+ ieee80211_regdomain_vdetach(vap);
+ ieee80211_scan_vdetach(vap);
+ ieee80211_ht_vdetach(vap);
+ /* NB: must be before ieee80211_node_vdetach */
+ ieee80211_proto_vdetach(vap);
+ ieee80211_crypto_vdetach(vap);
+ ieee80211_power_vdetach(vap);
+ ieee80211_node_vdetach(vap);
+ ieee80211_sysctl_vdetach(vap);
+}
+
+/*
+ * Synchronize flag bit state in the parent ifnet structure
+ * according to the state of all vap ifnet's. This is used,
+ * for example, to handle IFF_PROMISC and IFF_ALLMULTI.
+ */
+void
+ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap;
+ int bit, oflags;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ bit = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_ifp->if_flags & flag) {
+ /*
+ * XXX the bridge sets PROMISC but we don't want to
+ * enable it on the device, discard here so all the
+ * drivers don't need to special-case it
+ */
+ if (flag == IFF_PROMISC &&
+ vap->iv_opmode == IEEE80211_M_HOSTAP)
+ 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)
+ ic->ic_update_promisc(ifp);
+ else if (flag == IFF_ALLMULTI)
+ ic->ic_update_mcast(ifp);
+ }
+ }
+}
+
+/*
+ * Synchronize flag bit state in the com structure
+ * according to the state of all vap's. This is used,
+ * for example, to handle state changes via ioctls.
+ */
+static void
+ieee80211_syncflag_locked(struct ieee80211com *ic, int flag)
+{
+ struct ieee80211vap *vap;
+ int bit;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ bit = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_flags & flag) {
+ bit = 1;
+ break;
+ }
+ if (bit)
+ ic->ic_flags |= flag;
+ else
+ ic->ic_flags &= ~flag;
+}
+
+void
+ieee80211_syncflag(struct ieee80211vap *vap, int flag)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ if (flag < 0) {
+ flag = -flag;
+ vap->iv_flags &= ~flag;
+ } else
+ vap->iv_flags |= flag;
+ ieee80211_syncflag_locked(ic, flag);
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Synchronize flag bit state in the com structure
+ * according to the state of all vap's. This is used,
+ * for example, to handle state changes via ioctls.
+ */
+static void
+ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag)
+{
+ struct ieee80211vap *vap;
+ int bit;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ bit = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_flags_ext & flag) {
+ bit = 1;
+ break;
+ }
+ if (bit)
+ ic->ic_flags_ext |= flag;
+ else
+ ic->ic_flags_ext &= ~flag;
+}
+
+void
+ieee80211_syncflag_ext(struct ieee80211vap *vap, int flag)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ if (flag < 0) {
+ flag = -flag;
+ vap->iv_flags_ext &= ~flag;
+ } else
+ vap->iv_flags_ext |= flag;
+ ieee80211_syncflag_ext_locked(ic, flag);
+ IEEE80211_UNLOCK(ic);
}
static __inline int
@@ -416,7 +757,7 @@ ieee80211_ieee2mhz(u_int chan, u_int flags)
/*
* Locate a channel given a frequency+flags. We cache
- * the previous lookup to optimize swithing between two
+ * the previous lookup to optimize switching between two
* channels--as happens with dynamic turbo.
*/
struct ieee80211_channel *
@@ -467,80 +808,58 @@ ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags)
}
static void
-addmedia(struct ieee80211com *ic, int mode, int mword)
+addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword)
{
-#define TURBO(m) ((m) | IFM_IEEE80211_TURBO)
#define ADD(_ic, _s, _o) \
- ifmedia_add(&(_ic)->ic_media, \
+ ifmedia_add(media, \
IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
static const u_int mopts[IEEE80211_MODE_MAX] = {
- IFM_AUTO, /* IEEE80211_MODE_AUTO */
- IFM_IEEE80211_11A, /* IEEE80211_MODE_11A */
- IFM_IEEE80211_11B, /* IEEE80211_MODE_11B */
- IFM_IEEE80211_11G, /* IEEE80211_MODE_11G */
- IFM_IEEE80211_FH, /* IEEE80211_MODE_FH */
- TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_TURBO_A */
- TURBO(IFM_IEEE80211_11G), /* IEEE80211_MODE_TURBO_G */
- TURBO(IFM_IEEE80211_11A), /* IEEE80211_MODE_STURBO_A */
- IFM_IEEE80211_11NA, /* IEEE80211_MODE_11NA */
- IFM_IEEE80211_11NG, /* IEEE80211_MODE_11NG */
+ IFM_AUTO,
+ IFM_IEEE80211_11A,
+ IFM_IEEE80211_11B,
+ IFM_IEEE80211_11G,
+ IFM_IEEE80211_FH,
+ IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11G | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11NA,
+ IFM_IEEE80211_11NG,
};
u_int mopt;
- KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
mopt = mopts[mode];
- KASSERT(mopt != 0 || mode == IEEE80211_MODE_AUTO,
- ("no media mapping for mode %u", mode));
-
- ADD(ic, mword, mopt); /* e.g. 11a auto */
- if (ic->ic_caps & IEEE80211_C_IBSS)
- ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
- if (ic->ic_caps & IEEE80211_C_HOSTAP)
- ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP);
- if (ic->ic_caps & IEEE80211_C_AHDEMO)
- ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
- if (ic->ic_caps & IEEE80211_C_MONITOR)
- ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR);
+ if (addsta)
+ ADD(ic, mword, mopt); /* STA mode has no cap */
+ if (caps & IEEE80211_C_IBSS)
+ ADD(media, mword, mopt | IFM_IEEE80211_ADHOC);
+ if (caps & IEEE80211_C_HOSTAP)
+ ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP);
+ if (caps & IEEE80211_C_AHDEMO)
+ ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0);
+ if (caps & IEEE80211_C_MONITOR)
+ ADD(media, mword, mopt | IFM_IEEE80211_MONITOR);
+ if (caps & IEEE80211_C_WDS)
+ ADD(media, mword, mopt | IFM_IEEE80211_WDS);
#undef ADD
-#undef TURBO
}
/*
* Setup the media data structures according to the channel and
- * rate tables. This must be called by the driver after
- * ieee80211_attach and before most anything else.
+ * rate tables.
*/
-void
-ieee80211_media_init(struct ieee80211com *ic,
+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)
{
- struct ifnet *ifp = ic->ic_ifp;
int i, j, mode, rate, maxrate, mword, r;
const struct ieee80211_rateset *rs;
struct ieee80211_rateset allrates;
- /* NB: this works because the structure is initialized to zero */
- if (LIST_EMPTY(&ic->ic_media.ifm_list)) {
- /*
- * Do late attach work that must wait for any subclass
- * (i.e. driver) work such as overriding methods.
- */
- ieee80211_node_lateattach(ic);
- } else {
- /*
- * 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);
- }
- ieee80211_power_lateattach(ic);
-
/*
* Fill in media characteristics.
*/
- ifmedia_init(&ic->ic_media, 0, media_change, media_stat);
+ ifmedia_init(media, 0, media_change, media_stat);
maxrate = 0;
/*
* Add media for legacy operating modes.
@@ -549,7 +868,7 @@ ieee80211_media_init(struct ieee80211com *ic,
for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) {
if (isclr(ic->ic_modecaps, mode))
continue;
- addmedia(ic, mode, IFM_AUTO);
+ addmedia(media, caps, addsta, mode, IFM_AUTO);
if (mode == IEEE80211_MODE_AUTO)
continue;
rs = &ic->ic_sup_rates[mode];
@@ -558,7 +877,7 @@ ieee80211_media_init(struct ieee80211com *ic,
mword = ieee80211_rate2media(ic, rate, mode);
if (mword == 0)
continue;
- addmedia(ic, mode, mword);
+ addmedia(media, caps, addsta, mode, mword);
/*
* Add legacy rate to the collection of all rates.
*/
@@ -582,7 +901,8 @@ ieee80211_media_init(struct ieee80211com *ic,
if (mword == 0)
continue;
/* NB: remove media options from mword */
- addmedia(ic, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword));
+ addmedia(media, caps, addsta,
+ IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword));
}
/*
* Add HT/11n media. Note that we do not have enough
@@ -593,24 +913,51 @@ ieee80211_media_init(struct ieee80211com *ic,
for (; mode < IEEE80211_MODE_MAX; mode++) {
if (isclr(ic->ic_modecaps, mode))
continue;
- addmedia(ic, mode, IFM_AUTO);
- addmedia(ic, mode, IFM_IEEE80211_MCS);
+ addmedia(media, caps, addsta, mode, IFM_AUTO);
+ addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS);
}
if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) ||
isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) {
- addmedia(ic, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS);
+ addmedia(media, caps, addsta,
+ IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS);
/* XXX could walk htrates */
/* XXX known array size */
- if (ieee80211_htrates[15] > maxrate)
- maxrate = ieee80211_htrates[15];
+ if (ieee80211_htrates[15].ht40_rate_400ns > maxrate)
+ maxrate = ieee80211_htrates[15].ht40_rate_400ns;
+ }
+ return maxrate;
+}
+
+void
+ieee80211_media_init(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ int maxrate;
+
+ /* NB: this works because the structure is initialized to zero */
+ if (!LIST_EMPTY(&ic->ic_media.ifm_list)) {
+ /*
+ * We are re-initializing the channel list; clear
+ * the existing media state as the media routines
+ * don't suppress duplicates.
+ */
+ ifmedia_removeall(&ic->ic_media);
}
+ ieee80211_chan_init(ic);
+ /*
+ * Recalculate media settings in case new channel list changes
+ * the set of available modes.
+ */
+ maxrate = ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, 1,
+ ieee80211com_media_change, ieee80211com_media_status);
/* NB: strip explicit mode; we're actually in autoselect */
ifmedia_set(&ic->ic_media,
media_status(ic->ic_opmode, ic->ic_curchan) &~ IFM_MMASK);
-
if (maxrate)
ifp->if_baudrate = IF_Mbps(maxrate);
+
+ /* XXX need to propagate new media settings to vap's */
}
const struct ieee80211_rateset *
@@ -701,216 +1048,133 @@ ieee80211_announce_channels(struct ieee80211com *ic)
}
}
-/*
- * Find an instance by it's mac address.
- */
-struct ieee80211com *
-ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN])
-{
- struct ieee80211com *ic;
-
- /* XXX lock */
- SLIST_FOREACH(ic, &ieee80211_list, ic_next)
- if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr))
- return ic;
- return NULL;
-}
-
-static struct ieee80211com *
-ieee80211_find_instance(struct ifnet *ifp)
-{
- struct ieee80211com *ic;
-
- /* XXX lock */
- /* XXX not right for multiple instances but works for now */
- SLIST_FOREACH(ic, &ieee80211_list, ic_next)
- if (ic->ic_ifp == ifp)
- return ic;
- return NULL;
-}
-
-static int
-findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
-{
-#define IEEERATE(_ic,_m,_i) \
- ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
- int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
- for (i = 0; i < nrates; i++)
- if (IEEERATE(ic, mode, i) == rate)
- return i;
- return -1;
-#undef IEEERATE
-}
-
-/*
- * Convert a media specification to a rate index and possibly a mode
- * (if the rate is fixed and the mode is specified as ``auto'' then
- * we need to lock down the mode so the index is meanginful).
- */
static int
-checkrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
+media2mode(const struct ieee80211com *ic,
+ const struct ifmedia_entry *ime, enum ieee80211_phymode *mode)
{
-
- /*
- * Check the rate table for the specified/current phy.
- */
- if (mode == IEEE80211_MODE_AUTO) {
- int i;
- /*
- * In autoselect mode search for the rate.
- */
- for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
- if (isset(ic->ic_modecaps, i) &&
- findrate(ic, i, rate) != -1)
- return 1;
- }
- return 0;
- } else {
- /*
- * Mode is fixed, check for rate.
- */
- return (findrate(ic, mode, rate) != -1);
- }
-}
-
-/*
- * Handle a media change request.
- */
-int
-ieee80211_media_change(struct ifnet *ifp)
-{
- struct ieee80211com *ic;
- struct ifmedia_entry *ime;
- enum ieee80211_opmode newopmode;
- enum ieee80211_phymode newphymode;
- int newrate, error = 0;
-
- ic = ieee80211_find_instance(ifp);
- if (!ic) {
- if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
- return EINVAL;
- }
- ime = ic->ic_media.ifm_cur;
- /*
- * First, identify the phy mode.
- */
switch (IFM_MODE(ime->ifm_media)) {
case IFM_IEEE80211_11A:
- newphymode = IEEE80211_MODE_11A;
+ *mode = IEEE80211_MODE_11A;
break;
case IFM_IEEE80211_11B:
- newphymode = IEEE80211_MODE_11B;
+ *mode = IEEE80211_MODE_11B;
break;
case IFM_IEEE80211_11G:
- newphymode = IEEE80211_MODE_11G;
+ *mode = IEEE80211_MODE_11G;
break;
case IFM_IEEE80211_FH:
- newphymode = IEEE80211_MODE_FH;
+ *mode = IEEE80211_MODE_FH;
break;
case IFM_IEEE80211_11NA:
- newphymode = IEEE80211_MODE_11NA;
+ *mode = IEEE80211_MODE_11NA;
break;
case IFM_IEEE80211_11NG:
- newphymode = IEEE80211_MODE_11NG;
+ *mode = IEEE80211_MODE_11NG;
break;
case IFM_AUTO:
- newphymode = IEEE80211_MODE_AUTO;
+ *mode = IEEE80211_MODE_AUTO;
break;
default:
- return EINVAL;
+ return 0;
}
/*
* Turbo mode is an ``option''.
* XXX does not apply to AUTO
*/
if (ime->ifm_media & IFM_IEEE80211_TURBO) {
- if (newphymode == IEEE80211_MODE_11A) {
+ if (*mode == IEEE80211_MODE_11A) {
if (ic->ic_flags & IEEE80211_F_TURBOP)
- newphymode = IEEE80211_MODE_TURBO_A;
+ *mode = IEEE80211_MODE_TURBO_A;
else
- newphymode = IEEE80211_MODE_STURBO_A;
- } else if (newphymode == IEEE80211_MODE_11G)
- newphymode = IEEE80211_MODE_TURBO_G;
+ *mode = IEEE80211_MODE_STURBO_A;
+ } else if (*mode == IEEE80211_MODE_11G)
+ *mode = IEEE80211_MODE_TURBO_G;
else
- return EINVAL;
+ return 0;
}
/* XXX HT40 +/- */
- /*
- * Next, the fixed/variable rate.
- */
- newrate = ic->ic_fixed_rate;
- if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
- /*
- * Convert media subtype to rate.
- */
- newrate = ieee80211_media2rate(ime->ifm_media);
- if (newrate == 0 || !checkrate(ic, newphymode, newrate))
- return EINVAL;
- } else
- newrate = IEEE80211_FIXED_RATE_NONE;
+ return 1;
+}
+
+/*
+ * Handle a media change request on the underlying
+ * interface; we accept mode changes only.
+ */
+int
+ieee80211com_media_change(struct ifnet *ifp)
+{
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifmedia_entry *ime = ic->ic_media.ifm_cur;
+ enum ieee80211_phymode newphymode;
+ int error = 0;
/*
- * Deduce new operating mode but don't install it just yet.
+ * First, identify the phy mode.
*/
- if ((ime->ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) ==
- (IFM_IEEE80211_ADHOC|IFM_FLAG0))
- newopmode = IEEE80211_M_AHDEMO;
- else if (ime->ifm_media & IFM_IEEE80211_HOSTAP)
- newopmode = IEEE80211_M_HOSTAP;
- else if (ime->ifm_media & IFM_IEEE80211_ADHOC)
- newopmode = IEEE80211_M_IBSS;
- else if (ime->ifm_media & IFM_IEEE80211_MONITOR)
- newopmode = IEEE80211_M_MONITOR;
- else
- newopmode = IEEE80211_M_STA;
+ if (!media2mode(ic, ime, &newphymode))
+ return EINVAL;
+ /* NB: mode must be supported, no need to check */
/*
* Handle phy mode change.
*/
- if (ic->ic_des_mode != newphymode) { /* change phy mode */
- ic->ic_des_mode = newphymode;
- error = ENETRESET;
- }
+ IEEE80211_LOCK(ic);
+ if (ic->ic_curmode != newphymode) { /* change phy mode */
+ struct ieee80211vap *vap;
- /*
- * Committed to changes, install the rate setting.
- */
- if (ic->ic_fixed_rate != newrate) {
- ic->ic_fixed_rate = newrate; /* set fixed tx rate */
- error = ENETRESET;
+ (void) ieee80211_setmode(ic, newphymode);
+ /*
+ * Propagate new state to each vap.
+ */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ }
}
+ IEEE80211_UNLOCK(ic);
+ return error;
+}
- /*
- * Handle operating mode change.
- */
- if (ic->ic_opmode != newopmode) {
- ic->ic_opmode = newopmode;
- switch (newopmode) {
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_HOSTAP:
- case IEEE80211_M_STA:
- case IEEE80211_M_MONITOR:
- case IEEE80211_M_WDS:
- ic->ic_flags &= ~IEEE80211_F_IBSSON;
- break;
- case IEEE80211_M_IBSS:
- ic->ic_flags |= IEEE80211_F_IBSSON;
- break;
- }
+static int
+findrate(const struct ieee80211com *ic, enum ieee80211_phymode m, int r)
+{
+ int i, nrates;
+
+ for (i = 0, nrates = ic->ic_sup_rates[m].rs_nrates; i < nrates; i++)
+ if ((ic->ic_sup_rates[m].rs_rates[i] & IEEE80211_RATE_VAL) == r)
+ return i;
+ return -1;
+}
+
+/*
+ * Handle a media change request on the vap interface.
+ */
+int
+ieee80211_media_change(struct ifnet *ifp)
+{
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ifmedia_entry *ime = vap->iv_media.ifm_cur;
+ struct ieee80211com *ic = vap->iv_ic;
+ int newrate;
+
+ /* XXX this won't work unless ic_curmode is != IEEE80211_MODE_AUTO */
+ if (ic->ic_curmode == IEEE80211_MODE_AUTO)
+ return EINVAL;
+ if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) {
/*
- * Yech, slot time may change depending on the
- * operating mode so reset it to be sure everything
- * is setup appropriately.
+ * NB: this can only be used to specify a legacy rate.
*/
- ieee80211_reset_erp(ic);
- ieee80211_wme_initparams(ic); /* after opmode change */
- error = ENETRESET;
+ newrate = ieee80211_media2rate(ime->ifm_media);
+ if (newrate == 0)
+ return EINVAL;
+ if (findrate(ic, ic->ic_curmode, newrate) == -1)
+ return EINVAL;
+ } else {
+ newrate = IEEE80211_FIXED_RATE_NONE;
}
-#ifdef notdef
- if (error == 0)
- ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media);
-#endif
- return error;
+ if (newrate != vap->iv_txparms[ic->ic_curmode].ucastrate) {
+ vap->iv_txparms[ic->ic_curmode].ucastrate = newrate;
+ return ENETRESET;
+ }
+ return 0;
}
/*
@@ -939,7 +1203,7 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
status |= IFM_IEEE80211_ADHOC | IFM_FLAG0;
break;
case IEEE80211_M_WDS:
- /* should not come here */
+ status |= IFM_IEEE80211_WDS;
break;
}
if (IEEE80211_IS_CHAN_HTA(chan)) {
@@ -959,53 +1223,70 @@ media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan)
if (IEEE80211_IS_CHAN_TURBO(chan))
status |= IFM_IEEE80211_TURBO;
-
+#if 0
+ if (IEEE80211_IS_CHAN_HT20(chan))
+ status |= IFM_IEEE80211_HT20;
+ if (IEEE80211_IS_CHAN_HT40(chan))
+ status |= IFM_IEEE80211_HT40;
+#endif
return status;
}
+static void
+ieee80211com_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap;
+
+ imr->ifm_status = IFM_AVALID;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_ifp->if_flags & IFF_UP) {
+ imr->ifm_status |= IFM_ACTIVE;
+ break;
+ }
+ imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);
+ if (imr->ifm_status & IFM_ACTIVE)
+ imr->ifm_current = imr->ifm_active;
+}
+
void
ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct ieee80211com *ic;
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
enum ieee80211_phymode mode;
- const struct ieee80211_rateset *rs;
- ic = ieee80211_find_instance(ifp);
- if (!ic) {
- if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
- return;
- }
imr->ifm_status = IFM_AVALID;
/*
* NB: use the current channel's mode to lock down a xmit
* rate only when running; otherwise we may have a mismatch
* in which case the rate will not be convertible.
*/
- if (ic->ic_state == IEEE80211_S_RUN) {
+ if (vap->iv_state == IEEE80211_S_RUN) {
imr->ifm_status |= IFM_ACTIVE;
mode = ieee80211_chan2mode(ic->ic_curchan);
} else
mode = IEEE80211_MODE_AUTO;
- imr->ifm_active = media_status(ic->ic_opmode, ic->ic_curchan);
+ imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan);
/*
* Calculate a current rate if possible.
*/
- if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
+ if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) {
/*
* A fixed rate is set, report that.
*/
imr->ifm_active |= ieee80211_rate2media(ic,
- ic->ic_fixed_rate, mode);
- } else if (ic->ic_opmode == IEEE80211_M_STA) {
+ vap->iv_txparms[mode].ucastrate, mode);
+ } else if (vap->iv_opmode == IEEE80211_M_STA) {
/*
* In station mode report the current transmit rate.
- * XXX HT rate
*/
- rs = &ic->ic_bss->ni_rates;
imr->ifm_active |= ieee80211_rate2media(ic,
- rs->rs_rates[ic->ic_bss->ni_txrate], mode);
+ vap->iv_bss->ni_txrate, mode);
} else
imr->ifm_active |= IFM_AUTO;
+ if (imr->ifm_status & IFM_ACTIVE)
+ imr->ifm_current = imr->ifm_active;
}
/*
@@ -1024,11 +1305,10 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
* and used instead.
*/
if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B)
- ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], mode);
+ ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode);
ic->ic_curmode = mode;
ieee80211_reset_erp(ic); /* reset ERP state */
- ieee80211_wme_initparams(ic); /* reset WME stat */
return 0;
}
diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h
index 97f9365..3e91068 100644
--- a/sys/net80211/ieee80211.h
+++ b/sys/net80211/ieee80211.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -696,9 +696,13 @@ struct ieee80211_country_ie {
uint8_t schan; /* starting channel */
uint8_t nchan; /* number channels */
uint8_t maxtxpwr; /* tx power cap */
- } __packed band[10]; /* sub bands */
+ } __packed band[1]; /* sub bands (NB: var size) */
} __packed;
+#define IEEE80211_COUNTRY_MAX_BANDS 84 /* max possible bands */
+#define IEEE80211_COUNTRY_MAX_SIZE \
+ (sizeof(struct ieee80211_country_ie) + 3*(IEEE80211_COUNTRY_MAX_BANDS-1))
+
/*
* 802.11h Channel Switch Announcement (CSA).
*/
@@ -889,6 +893,9 @@ enum {
#define IEEE80211_WEP_IVLEN 3 /* 24bit */
#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */
#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */
+#define IEEE80211_WEP_TOTLEN (IEEE80211_WEP_IVLEN + \
+ IEEE80211_WEP_KIDLEN + \
+ IEEE80211_WEP_CRCLEN)
#define IEEE80211_WEP_NKID 4 /* number of key ids */
/*
@@ -924,12 +931,12 @@ enum {
/*
* The 802.11 spec says at most 2007 stations may be
* associated at once. For most AP's this is way more
- * than is feasible so we use a default of 128. This
- * number may be overridden by the driver and/or by
- * user configuration.
+ * than is feasible so we use a default of IEEE80211_AID_DEF.
+ * This number may be overridden by the driver and/or by
+ * user configuration but may not be less than IEEE80211_AID_MIN
+ * (see _ieee80211.h for implementation-specific settings).
*/
#define IEEE80211_AID_MAX 2007
-#define IEEE80211_AID_DEF 128
#define IEEE80211_AID(b) ((b) &~ 0xc000)
diff --git a/sys/net80211/ieee80211_acl.c b/sys/net80211/ieee80211_acl.c
index c5305d0..13407a4 100644
--- a/sys/net80211/ieee80211_acl.c
+++ b/sys/net80211/ieee80211_acl.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,7 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 MAC ACL support.
*
- * When this module is loaded the sender address of each received
+ * When this module is loaded the sender address of each auth mgt
* frame is passed to the iac_check method and the module indicates
* if the frame should be accepted or rejected. If the policy is
* set to ACL_POLICY_OPEN then all frames are accepted w/o checking
@@ -37,6 +37,8 @@ __FBSDID("$FreeBSD$");
* and if found the frame is either accepted (ACL_POLICY_ALLOW)
* or rejected (ACL_POLICY_DENT).
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -57,6 +59,12 @@ enum {
ACL_POLICY_OPEN = 0, /* open, don't check ACL's */
ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */
ACL_POLICY_DENY = 2, /* deny traffic from MAC */
+ /*
+ * NB: ACL_POLICY_RADIUS must be the same value as
+ * IEEE80211_MACCMD_POLICY_RADIUS because of the way
+ * acl_getpolicy() works.
+ */
+ ACL_POLICY_RADIUS = 7, /* defer to RADIUS ACL server */
};
#define ACL_HASHSIZE 32
@@ -72,7 +80,7 @@ struct aclstate {
int as_nacls;
TAILQ_HEAD(, acl) as_list; /* list of all ACL's */
LIST_HEAD(, acl) as_hash[ACL_HASHSIZE];
- struct ieee80211com *as_ic;
+ struct ieee80211vap *as_vap;
};
/* simple hash is enough for variation of macaddr */
@@ -81,10 +89,13 @@ struct aclstate {
MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
-static int acl_free_all(struct ieee80211com *);
+static int acl_free_all(struct ieee80211vap *);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
static int
-acl_attach(struct ieee80211com *ic)
+acl_attach(struct ieee80211vap *vap)
{
struct aclstate *as;
@@ -95,20 +106,24 @@ acl_attach(struct ieee80211com *ic)
ACL_LOCK_INIT(as, "acl");
TAILQ_INIT(&as->as_list);
as->as_policy = ACL_POLICY_OPEN;
- as->as_ic = ic;
- ic->ic_as = as;
+ as->as_vap = vap;
+ vap->iv_as = as;
+ nrefs++; /* NB: we assume caller locking */
return 1;
}
static void
-acl_detach(struct ieee80211com *ic)
+acl_detach(struct ieee80211vap *vap)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
- acl_free_all(ic);
- ic->ic_as = NULL;
+ KASSERT(nrefs > 0, ("imbalanced attach/detach"));
+ nrefs--; /* NB: we assume caller locking */
+
+ acl_free_all(vap);
+ vap->iv_as = NULL;
ACL_LOCK_DESTROY(as);
- FREE(as, M_DEVBUF);
+ FREE(as, M_80211_ACL);
}
static __inline struct acl *
@@ -137,12 +152,13 @@ _acl_free(struct aclstate *as, struct acl *acl)
}
static int
-acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
switch (as->as_policy) {
case ACL_POLICY_OPEN:
+ case ACL_POLICY_RADIUS:
return 1;
case ACL_POLICY_ALLOW:
return _find_acl(as, mac) != NULL;
@@ -153,15 +169,15 @@ acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
}
static int
-acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl, *new;
int hash;
MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
if (new == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s failed, no memory\n", ether_sprintf(mac));
/* XXX statistic */
return ENOMEM;
@@ -173,7 +189,7 @@ acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
ACL_UNLOCK(as);
FREE(new, M_80211_ACL);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s failed, already present\n",
ether_sprintf(mac));
return EEXIST;
@@ -185,15 +201,15 @@ acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
as->as_nacls++;
ACL_UNLOCK(as);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: add %s\n", ether_sprintf(mac));
return 0;
}
static int
-acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
+acl_remove(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl;
ACL_LOCK(as);
@@ -202,7 +218,7 @@ acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
_acl_free(as, acl);
ACL_UNLOCK(as);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: remove %s%s\n", ether_sprintf(mac),
acl == NULL ? ", not present" : "");
@@ -210,12 +226,12 @@ acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
}
static int
-acl_free_all(struct ieee80211com *ic)
+acl_free_all(struct ieee80211vap *vap)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
ACL_LOCK(as);
while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
@@ -226,11 +242,11 @@ acl_free_all(struct ieee80211com *ic)
}
static int
-acl_setpolicy(struct ieee80211com *ic, int policy)
+acl_setpolicy(struct ieee80211vap *vap, int policy)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
"ACL: set policy to %u\n", policy);
switch (policy) {
@@ -243,6 +259,9 @@ acl_setpolicy(struct ieee80211com *ic, int policy)
case IEEE80211_MACCMD_POLICY_DENY:
as->as_policy = ACL_POLICY_DENY;
break;
+ case IEEE80211_MACCMD_POLICY_RADIUS:
+ as->as_policy = ACL_POLICY_RADIUS;
+ break;
default:
return EINVAL;
}
@@ -250,24 +269,24 @@ acl_setpolicy(struct ieee80211com *ic, int policy)
}
static int
-acl_getpolicy(struct ieee80211com *ic)
+acl_getpolicy(struct ieee80211vap *vap)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
return as->as_policy;
}
static int
-acl_setioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
+acl_setioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
return EINVAL;
}
static int
-acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
+acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
- struct aclstate *as = ic->ic_as;
+ struct aclstate *as = vap->iv_as;
struct acl *acl;
struct ieee80211req_maclist *ap;
int error, space, i;
@@ -283,7 +302,7 @@ acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
return 0; /* NB: must not error */
}
MALLOC(ap, struct ieee80211req_maclist *, space,
- M_TEMP, M_NOWAIT);
+ M_TEMP, M_NOWAIT);
if (ap == NULL)
return ENOMEM;
i = 0;
@@ -317,31 +336,4 @@ static const struct ieee80211_aclator mac = {
.iac_setioctl = acl_setioctl,
.iac_getioctl = acl_getioctl,
};
-
-/*
- * Module glue.
- */
-static int
-wlan_acl_modevent(module_t mod, int type, void *unused)
-{
- switch (type) {
- case MOD_LOAD:
- if (bootverbose)
- printf("wlan: <802.11 MAC ACL support>\n");
- ieee80211_aclator_register(&mac);
- return 0;
- case MOD_UNLOAD:
- ieee80211_aclator_unregister(&mac);
- return 0;
- }
- return EINVAL;
-}
-
-static moduledata_t wlan_acl_mod = {
- "wlan_acl",
- wlan_acl_modevent,
- 0
-};
-DECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_acl, 1);
-MODULE_DEPEND(wlan_acl, wlan, 1, 1, 1);
+IEEE80211_ACL_MODULE(wlan_acl, mac, 1);
diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c
new file mode 100644
index 0000000..ce9c4cb
--- /dev/null
+++ b/sys/net80211/ieee80211_adhoc.c
@@ -0,0 +1,877 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 IBSS mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_adhoc.h>
+#include <net80211/ieee80211_input.h>
+
+#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 rssi, int noise, uint32_t rstamp);
+static void adhoc_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, uint32_t rstamp);
+
+void
+ieee80211_adhoc_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_IBSS] = adhoc_vattach;
+ ic->ic_vattach[IEEE80211_M_AHDEMO] = adhoc_vattach;
+}
+
+void
+ieee80211_adhoc_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+adhoc_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+adhoc_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = adhoc_newstate;
+ vap->iv_input = adhoc_input;
+ vap->iv_recv_mgmt = adhoc_recv_mgmt;
+ vap->iv_opdetach = adhoc_vdetach;
+}
+
+/*
+ * IEEE80211_M_IBSS+IEEE80211_M_AHDEMO vap state machine handler.
+ */
+static int
+adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+#ifdef IEEE80211_DEBUG
+ struct ieee80211com *ic = vap->iv_ic;
+#endif
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
+ callout_stop(&vap->iv_swbmiss);
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ default:
+ break;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_RUN: /* beacon miss */
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
+ !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ */
+ ieee80211_create_ibss(vap, vap->iv_des_chan);
+ break;
+ }
+ /*
+ * Initiate a scan. We can come here as a result
+ * of an IEEE80211_IOC_SCAN_REQ too in which case
+ * the vap will be marked with IEEE80211_FEXT_SCANREQ
+ * and the scan request parameters will be present
+ * in iv_scanreq. Otherwise we do the default.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
+ ieee80211_check_scan(vap,
+ vap->iv_scanreq_flags,
+ vap->iv_scanreq_duration,
+ vap->iv_scanreq_mindwell,
+ vap->iv_scanreq_maxdwell,
+ vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ } else
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ /*
+ * This can happen because of a change in state
+ * that requires a reset. Trigger a new scan
+ * unless we're in manual roaming mode in which
+ * case an application must issue an explicit request.
+ */
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_flags & IEEE80211_F_WPA) {
+ /* XXX validate prerequisites */
+ }
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap)) {
+ ieee80211_note(vap,
+ "synchronized with %s ssid ",
+ ether_sprintf(ni->ni_bssid));
+ ieee80211_print_essid(vap->iv_bss->ni_essid,
+ ni->ni_esslen);
+ /* XXX MCS/HT */
+ printf(" channel %d start %uMb\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ IEEE80211_RATE2MBS(ni->ni_txrate));
+ }
+#endif
+ break;
+ default:
+ goto invalid;
+ }
+ /*
+ * When 802.1x is not in use mark the port authorized
+ * at this point so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+ break;
+ case IEEE80211_S_SLEEP:
+ ieee80211_sta_pwrsave(vap, 0);
+ break;
+ default:
+ invalid:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
+ "%s: invalid state transition %s -> %s\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled. This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211vap *vap, int subtype)
+{
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return 1;
+ }
+ return 1;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+adhoc_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint8_t *bssid;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if (dir != IEEE80211_FC1_DIR_NODS)
+ bssid = wh->i_addr1;
+ else if (type == IEEE80211_FC0_TYPE_CTL)
+ bssid = wh->i_addr1;
+ else {
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_ANY, ni->ni_macaddr,
+ NULL, "too short (2): len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ bssid = wh->i_addr3;
+ }
+ /*
+ * Validate the bssid.
+ */
+ if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
+ !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ /*
+ * Data frame, cons up a node when it doesn't
+ * exist. This should probably done after an ACL check.
+ */
+ if (type == IEEE80211_FC0_TYPE_DATA &&
+ ni == vap->iv_bss &&
+ !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+ /*
+ * Fake up a node for this newly
+ * discovered member of the IBSS.
+ */
+ ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2);
+ if (ni == NULL) {
+ /* NB: stat kept for alloc failure */
+ goto err;
+ }
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >>
+ IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] &
+ IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ }
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ /* XXX no power-save support */
+
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
+ ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
+ else
+ ieee80211_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
+ ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ /* NB: only IBSS mode gets mgt frames */
+ if (vap->iv_opmode == IEEE80211_M_IBSS)
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "bad", "frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static int
+is11bclient(const uint8_t *rates, const uint8_t *xrates)
+{
+ static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
+ int i;
+
+ /* NB: the 11b clients we care about will not have xrates */
+ if (xrates != NULL || rates == NULL)
+ return 0;
+ for (i = 0; i < rates[1]; i++) {
+ int r = rates[2+i] & IEEE80211_RATE_VAL;
+ if (r > 2*11 || ((1<<r) & brates) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, uint32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm, *sfrm;
+ uint8_t *ssid, *rates, *xrates;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ struct ieee80211_scanparams scan;
+ /*
+ * We process beacon/probe response
+ * frames to discover neighbors.
+ */
+ if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ return;
+ /*
+ * Count frame now that we know it's to be processed.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ vap->iv_stats.is_rx_beacon++; /* XXX remove */
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
+ /*
+ * Actively scanning a channel marked passive;
+ * send a probe request now that we know there
+ * is 802.11 traffic present.
+ *
+ * XXX check if the beacon we recv'd gives
+ * us what we need and suppress the probe req
+ */
+ ieee80211_probe_curchan(vap, 1);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
+ }
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ return;
+ }
+ if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
+ if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+ /*
+ * Create a new entry in the neighbor table.
+ */
+ ni = ieee80211_add_neighbor(vap, wh, &scan);
+ } else if (ni->ni_capinfo == 0) {
+ /*
+ * Update faked node created on transmit.
+ * Note this also updates the tsf.
+ */
+ ieee80211_init_neighbor(ni, wh, &scan);
+ } else {
+ /*
+ * Record tsf for potential resync.
+ */
+ memcpy(ni->ni_tstamp.data, scan.tstamp,
+ sizeof(ni->ni_tstamp));
+ }
+ if (ni != NULL) {
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ }
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+ /* frame must be directed */
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */
+ return;
+ }
+
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ ssid = rates = xrates = NULL;
+ sfrm = frm;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
+ IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
+ if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL,
+ "%s", "no ssid with ssid suppression enabled");
+ vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
+ return;
+ }
+
+ /* XXX find a better class or define it's own */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
+ "%s", "recv probe req");
+ /*
+ * Some legacy 11b clients cannot hack a complete
+ * probe response frame. When the request includes
+ * only a bare-bones rate set, communicate this to
+ * the transmit side.
+ */
+ ieee80211_send_proberesp(vap, wh->i_addr2,
+ is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_ACTION: {
+ const struct ieee80211_action *ia;
+
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /*
+ * action frame format:
+ * [1] category
+ * [1] action
+ * [tlv] parameters
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action), return);
+ ia = (const struct ieee80211_action *) frm;
+
+ vap->iv_stats.is_rx_action++;
+ IEEE80211_NODE_STAT(ni, rx_action);
+
+ /* verify frame payloads but defer processing */
+ /* XXX maybe push this to method */
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbarequest),
+ return);
+ break;
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbaresponse),
+ return);
+ break;
+ case IEEE80211_ACTION_BA_DELBA:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_delba),
+ return);
+ break;
+ }
+ break;
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_txchwidth),
+ return);
+ break;
+ }
+ break;
+ }
+ ic->ic_recv_action(ni, frm, efrm);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ case IEEE80211_FC0_SUBTYPE_DISASSOC:
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+}
+#undef IEEE80211_VERIFY_LENGTH
+#undef IEEE80211_VERIFY_ELEMENT
diff --git a/sys/net80211/ieee80211_adhoc.h b/sys/net80211/ieee80211_adhoc.h
new file mode 100644
index 0000000..d8e19e5
--- /dev/null
+++ b/sys/net80211/ieee80211_adhoc.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_ADHOC_H_
+#define _NET80211_IEEE80211_ADHOC_H_
+
+/*
+ * Adhoc-mode (ibss+ahdemo) implementation definitions.
+ */
+void ieee80211_adhoc_attach(struct ieee80211com *);
+void ieee80211_adhoc_detach(struct ieee80211com *);
+#endif /* !_NET80211_IEEE80211_STA_H_ */
diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c
index 5b34768..a759e64 100644
--- a/sys/net80211/ieee80211_amrr.c
+++ b/sys/net80211/ieee80211_amrr.c
@@ -28,6 +28,8 @@ __FBSDID("$FreeBSD$");
* INRIA Sophia - Projet Planete
* http://www-sop.inria.fr/rapports/sophia/RR-5208.html
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
@@ -51,66 +53,90 @@ __FBSDID("$FreeBSD$");
((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
#define is_enough(amn) \
((amn)->amn_txcnt > 10)
-#define is_min_rate(ni) \
- ((ni)->ni_txrate == 0)
-#define is_max_rate(ni) \
- ((ni)->ni_txrate == (ni)->ni_rates.rs_nrates - 1)
-#define increase_rate(ni) \
- ((ni)->ni_txrate++)
-#define decrease_rate(ni) \
- ((ni)->ni_txrate--)
-#define reset_cnt(amn) \
- do { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; } while (0)
+
+static void amrr_sysctlattach(struct ieee80211_amrr *amrr,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
+
+void
+ieee80211_amrr_setinterval(struct ieee80211_amrr *amrr, int msecs)
+{
+ int t;
+
+ if (msecs < 100)
+ msecs = 100;
+ t = msecs_to_ticks(msecs);
+ amrr->amrr_interval = (t < 1) ? 1 : t;
+}
void
ieee80211_amrr_init(struct ieee80211_amrr *amrr,
- struct ieee80211com *ic, int amin, int amax)
+ struct ieee80211vap *vap, int amin, int amax, int interval)
{
/* XXX bounds check? */
amrr->amrr_min_success_threshold = amin;
amrr->amrr_max_success_threshold = amax;
- amrr->amrr_ic = ic;
+ ieee80211_amrr_setinterval(amrr, interval);
+
+ amrr_sysctlattach(amrr, vap->iv_sysctl, vap->iv_oid);
+}
+
+void
+ieee80211_amrr_cleanup(struct ieee80211_amrr *amrr)
+{
}
void
ieee80211_amrr_node_init(struct ieee80211_amrr *amrr,
- struct ieee80211_amrr_node *amn)
+ struct ieee80211_amrr_node *amn, struct ieee80211_node *ni)
{
+ const struct ieee80211_rateset *rs = &ni->ni_rates;
+
+ amn->amn_amrr = amrr;
amn->amn_success = 0;
amn->amn_recovery = 0;
amn->amn_txcnt = amn->amn_retrycnt = 0;
amn->amn_success_threshold = amrr->amrr_min_success_threshold;
+
+ /* pick initial rate */
+ for (amn->amn_rix = rs->rs_nrates - 1;
+ amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72;
+ amn->amn_rix--)
+ ;
+ ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+ amn->amn_ticks = ticks;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR initial rate %d", ni->ni_txrate);
}
-/*
- * Update ni->ni_txrate.
- */
-void
-ieee80211_amrr_choose(struct ieee80211_amrr *amrr, struct ieee80211_node *ni,
- struct ieee80211_amrr_node *amn)
+static int
+amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
+ struct ieee80211_node *ni)
{
- int need_change = 0;
+ int rix = amn->amn_rix;
+
+ KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
- if (is_success(amn) && is_enough(amn)) {
+ if (is_success(amn)) {
amn->amn_success++;
if (amn->amn_success >= amn->amn_success_threshold &&
- !is_max_rate(ni)) {
+ rix + 1 < ni->ni_rates.rs_nrates) {
amn->amn_recovery = 1;
amn->amn_success = 0;
- increase_rate(ni);
- IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL,
- "AMRR increasing rate %d (txcnt=%d "
- "retrycnt=%d)\n",
- ni->ni_rates.rs_rates[ni->ni_txrate] &
- IEEE80211_RATE_VAL,
+ rix++;
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
+ ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
- need_change = 1;
} else {
amn->amn_recovery = 0;
}
} else if (is_failure(amn)) {
amn->amn_success = 0;
- if (!is_min_rate(ni)) {
+ if (rix > 0) {
if (amn->amn_recovery) {
amn->amn_success_threshold *= 2;
if (amn->amn_success_threshold >
@@ -121,44 +147,80 @@ ieee80211_amrr_choose(struct ieee80211_amrr *amrr, struct ieee80211_node *ni,
amn->amn_success_threshold =
amrr->amrr_min_success_threshold;
}
- decrease_rate(ni);
- IEEE80211_DPRINTF(amrr->amrr_ic, IEEE80211_MSG_RATECTL,
- "AMRR decreasing rate %d (txcnt=%d "
- "retrycnt=%d)\n",
- ni->ni_rates.rs_rates[ni->ni_txrate] &
- IEEE80211_RATE_VAL,
+ rix--;
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
+ ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
- need_change = 1;
}
amn->amn_recovery = 0;
}
- if (is_enough(amn) || need_change)
- reset_cnt(amn);
+ /* reset counters */
+ amn->amn_txcnt = 0;
+ amn->amn_retrycnt = 0;
+
+ return rix;
}
/*
- * Module glue.
+ * Return the rate index to use in sending a data frame.
+ * Update our internal state if it's been long enough.
+ * If the rate changes we also update ni_txrate to match.
*/
+int
+ieee80211_amrr_choose(struct ieee80211_node *ni,
+ struct ieee80211_amrr_node *amn)
+{
+ struct ieee80211_amrr *amrr = amn->amn_amrr;
+ int rix;
+
+ if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
+ rix = amrr_update(amrr, amn, ni);
+ if (rix != amn->amn_rix) {
+ /* update public rate */
+ ni->ni_txrate =
+ ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+ amn->amn_rix = rix;
+ }
+ amn->amn_ticks = ticks;
+ } else
+ rix = amn->amn_rix;
+ return rix;
+}
+
static int
-amrr_modevent(module_t mod, int type, void *unused)
+amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
{
- switch (type) {
- case MOD_LOAD:
- if (bootverbose)
- printf("wlan_amrr: <AMRR Transmit Rate Control Algorithm>\n");
- return 0;
- case MOD_UNLOAD:
- return 0;
- }
- return EINVAL;
+ struct ieee80211_amrr *amrr = arg1;
+ int msecs = ticks_to_msecs(amrr->amrr_interval);
+ int error;
+
+ error = sysctl_handle_int(oidp, &msecs, 0, req);
+ if (error || !req->newptr)
+ return error;
+ ieee80211_amrr_setinterval(amrr, msecs);
+ return 0;
+}
+
+static void
+amrr_sysctlattach(struct ieee80211_amrr *amrr,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
+{
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, amrr,
+ 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
+ /* XXX bounds check values */
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "amrr_max_sucess_threshold", CTLFLAG_RW,
+ &amrr->amrr_max_success_threshold, 0, "");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "amrr_min_sucess_threshold", CTLFLAG_RW,
+ &amrr->amrr_min_success_threshold, 0, "");
}
-static moduledata_t amrr_mod = {
- "wlan_amrr",
- amrr_modevent,
- 0
-};
-DECLARE_MODULE(wlan_amrr, amrr_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_amrr, 1);
-MODULE_DEPEND(wlan_amrr, wlan, 1, 1, 1);
+/*
+ * Module glue.
+ */
+IEEE80211_RATE_MODULE(amrr, 1);
diff --git a/sys/net80211/ieee80211_amrr.h b/sys/net80211/ieee80211_amrr.h
index 947d617..c03f699 100644
--- a/sys/net80211/ieee80211_amrr.h
+++ b/sys/net80211/ieee80211_amrr.h
@@ -32,12 +32,12 @@
/*
* Rate control settings.
*/
-struct ieee80211com;
+struct ieee80211vap;
struct ieee80211_amrr {
u_int amrr_min_success_threshold;
u_int amrr_max_success_threshold;
- struct ieee80211com *amrr_ic;
+ int amrr_interval; /* update interval (ticks) */
};
#define IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD 1
@@ -47,18 +47,55 @@ struct ieee80211_amrr {
* Rate control state for a given node.
*/
struct ieee80211_amrr_node {
+ struct ieee80211_amrr *amn_amrr;/* backpointer */
+ int amn_rix; /* current rate index */
+ int amn_ticks; /* time of last update */
+ /* statistics */
+ u_int amn_txcnt;
u_int amn_success;
- u_int amn_recovery;
u_int amn_success_threshold;
- u_int amn_txcnt;
+ u_int amn_recovery;
u_int amn_retrycnt;
};
-void ieee80211_amrr_init(struct ieee80211_amrr *,
- struct ieee80211com *ic, int, int);
+void ieee80211_amrr_init(struct ieee80211_amrr *, struct ieee80211vap *,
+ int, int, int);
+void ieee80211_amrr_cleanup(struct ieee80211_amrr *);
+void ieee80211_amrr_setinterval(struct ieee80211_amrr *, int);
void ieee80211_amrr_node_init(struct ieee80211_amrr *,
+ struct ieee80211_amrr_node *, struct ieee80211_node *);
+int ieee80211_amrr_choose(struct ieee80211_node *,
struct ieee80211_amrr_node *);
-void ieee80211_amrr_choose(struct ieee80211_amrr *, struct ieee80211_node *,
- struct ieee80211_amrr_node *);
+#define IEEE80211_AMRR_SUCCESS 1
+#define IEEE80211_AMRR_FAILURE 0
+
+/*
+ * Update statistics with tx complete status. Ok is non-zero
+ * if the packet is known to be ACK'd. Retries has the number
+ * retransmissions (i.e. xmit attempts - 1).
+ */
+static __inline void
+ieee80211_amrr_tx_complete(struct ieee80211_amrr_node *amn,
+ int ok, int retries)
+{
+ amn->amn_txcnt++;
+ if (ok)
+ amn->amn_success++;
+ amn->amn_retrycnt += retries;
+}
+
+/*
+ * Set tx count/retry statistics explicitly. Intended for
+ * drivers that poll the device for statistics maintained
+ * in the device.
+ */
+static __inline void
+ieee80211_amrr_tx_update(struct ieee80211_amrr_node *amn,
+ int txcnt, int success, int retrycnt)
+{
+ amn->amn_txcnt = txcnt;
+ amn->amn_success = success;
+ amn->amn_retrycnt = retrycnt;
+}
#endif /* _NET80211_IEEE80211_AMRR_H_ */
diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c
index 83d7c3f..d644d0c 100644
--- a/sys/net80211/ieee80211_crypto.c
+++ b/sys/net80211/ieee80211_crypto.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,11 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 generic crypto support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
@@ -41,23 +45,25 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
+MALLOC_DEFINE(M_80211_CRYPTO, "80211crypto", "802.11 crypto state");
+
+static int _ieee80211_crypto_delkey(struct ieee80211vap *,
+ struct ieee80211_key *);
+
/*
* Table of registered cipher modules.
*/
static const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX];
-static int _ieee80211_crypto_delkey(struct ieee80211com *,
- struct ieee80211_key *);
-
/*
* Default "null" key management routines.
*/
static int
-null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
+null_key_alloc(struct ieee80211vap *vap, const struct ieee80211_key *k,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
- if (!(&ic->ic_nw_keys[0] <= k &&
- k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
+ if (!(&vap->iv_nw_keys[0] <= k &&
+ k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
/*
* Not in the global key table, the driver should handle this
* by allocating a slot in the h/w key table/cache. In
@@ -72,23 +78,23 @@ null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
return 0;
*keyix = 0; /* NB: use key index 0 for ucast key */
} else {
- *keyix = k - ic->ic_nw_keys;
+ *keyix = k - vap->iv_nw_keys;
}
*rxkeyix = IEEE80211_KEYIX_NONE; /* XXX maybe *keyix? */
return 1;
}
static int
-null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+null_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
return 1;
}
static int
-null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+null_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
const uint8_t mac[IEEE80211_ADDR_LEN])
{
return 1;
}
-static void null_key_update(struct ieee80211com *ic) {}
+static void null_key_update(struct ieee80211vap *vap) {}
/*
* Write-arounds for common operations.
@@ -100,70 +106,86 @@ cipher_detach(struct ieee80211_key *key)
}
static __inline void *
-cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key)
+cipher_attach(struct ieee80211vap *vap, struct ieee80211_key *key)
{
- return key->wk_cipher->ic_attach(ic, key);
+ return key->wk_cipher->ic_attach(vap, key);
}
/*
* Wrappers for driver key management methods.
*/
static __inline int
-dev_key_alloc(struct ieee80211com *ic,
+dev_key_alloc(struct ieee80211vap *vap,
const struct ieee80211_key *key,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
- return ic->ic_crypto.cs_key_alloc(ic, key, keyix, rxkeyix);
+ return vap->iv_key_alloc(vap, key, keyix, rxkeyix);
}
static __inline int
-dev_key_delete(struct ieee80211com *ic,
+dev_key_delete(struct ieee80211vap *vap,
const struct ieee80211_key *key)
{
- return ic->ic_crypto.cs_key_delete(ic, key);
+ return vap->iv_key_delete(vap, key);
}
static __inline int
-dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key,
+dev_key_set(struct ieee80211vap *vap, const struct ieee80211_key *key,
const uint8_t mac[IEEE80211_ADDR_LEN])
{
- return ic->ic_crypto.cs_key_set(ic, key, mac);
+ return vap->iv_key_set(vap, key, mac);
}
/*
- * Setup crypto support.
+ * Setup crypto support for a device/shared instance.
*/
void
ieee80211_crypto_attach(struct ieee80211com *ic)
{
- struct ieee80211_crypto_state *cs = &ic->ic_crypto;
+ /* NB: we assume everything is pre-zero'd */
+ ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
+}
+
+/*
+ * Teardown crypto support.
+ */
+void
+ieee80211_crypto_detach(struct ieee80211com *ic)
+{
+}
+
+/*
+ * Setup crypto support for a vap.
+ */
+void
+ieee80211_crypto_vattach(struct ieee80211vap *vap)
+{
int i;
/* NB: we assume everything is pre-zero'd */
- cs->cs_def_txkey = IEEE80211_KEYIX_NONE;
- cs->cs_max_keyix = IEEE80211_WEP_NKID;
- ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
+ vap->iv_max_keyix = IEEE80211_WEP_NKID;
+ vap->iv_def_txkey = IEEE80211_KEYIX_NONE;
for (i = 0; i < IEEE80211_WEP_NKID; i++)
- ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i],
+ ieee80211_crypto_resetkey(vap, &vap->iv_nw_keys[i],
IEEE80211_KEYIX_NONE);
/*
* Initialize the driver key support routines to noop entries.
* This is useful especially for the cipher test modules.
*/
- cs->cs_key_alloc = null_key_alloc;
- cs->cs_key_set = null_key_set;
- cs->cs_key_delete = null_key_delete;
- cs->cs_key_update_begin = null_key_update;
- cs->cs_key_update_end = null_key_update;
+ vap->iv_key_alloc = null_key_alloc;
+ vap->iv_key_set = null_key_set;
+ vap->iv_key_delete = null_key_delete;
+ vap->iv_key_update_begin = null_key_update;
+ vap->iv_key_update_end = null_key_update;
}
/*
- * Teardown crypto support.
+ * Teardown crypto support for a vap.
*/
void
-ieee80211_crypto_detach(struct ieee80211com *ic)
+ieee80211_crypto_vdetach(struct ieee80211vap *vap)
{
- ieee80211_crypto_delglobalkeys(ic);
+ ieee80211_crypto_delglobalkeys(vap);
}
/*
@@ -213,12 +235,14 @@ ieee80211_crypto_available(u_int cipher)
}
/* XXX well-known names! */
-static const char *cipher_modnames[] = {
+static const char *cipher_modnames[IEEE80211_CIPHER_MAX] = {
"wlan_wep", /* IEEE80211_CIPHER_WEP */
"wlan_tkip", /* IEEE80211_CIPHER_TKIP */
"wlan_aes_ocb", /* IEEE80211_CIPHER_AES_OCB */
"wlan_ccmp", /* IEEE80211_CIPHER_AES_CCM */
+ "#4", /* reserved */
"wlan_ckip", /* IEEE80211_CIPHER_CKIP */
+ "wlan_none", /* IEEE80211_CIPHER_NONE */
};
/*
@@ -231,14 +255,14 @@ static const char *cipher_modnames[] = {
* routines assume wk_cipher is setup.
*
* Locking must be handled by the caller using:
- * ieee80211_key_update_begin(ic);
- * ieee80211_key_update_end(ic);
+ * ieee80211_key_update_begin(vap);
+ * ieee80211_key_update_end(vap);
*/
int
-ieee80211_crypto_newkey(struct ieee80211com *ic,
+ieee80211_crypto_newkey(struct ieee80211vap *vap,
int cipher, int flags, struct ieee80211_key *key)
{
-#define N(a) (sizeof(a) / sizeof(a[0]))
+ struct ieee80211com *ic = vap->iv_ic;
const struct ieee80211_cipher *cip;
ieee80211_keyix keyix, rxkeyix;
void *keyctx;
@@ -248,9 +272,9 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* Validate cipher and set reference to cipher routines.
*/
if (cipher >= IEEE80211_CIPHER_MAX) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "%s: invalid cipher %u\n", __func__, cipher);
- ic->ic_stats.is_crypto_badcipher++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+ "%s: invalid cipher %u\n", __func__, cipher);
+ vap->iv_stats.is_crypto_badcipher++;
return 0;
}
cip = ciphers[cipher];
@@ -261,25 +285,21 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* than numbers and craft a module name based on the cipher
* name; e.g. wlan_cipher_<cipher-name>.
*/
- if (cipher < N(cipher_modnames)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "%s: unregistered cipher %u, load module %s\n",
- __func__, cipher, cipher_modnames[cipher]);
- ieee80211_load_module(cipher_modnames[cipher]);
- /*
- * If cipher module loaded it should immediately
- * call ieee80211_crypto_register which will fill
- * in the entry in the ciphers array.
- */
- cip = ciphers[cipher];
- }
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+ "%s: unregistered cipher %u, load module %s\n",
+ __func__, cipher, cipher_modnames[cipher]);
+ ieee80211_load_module(cipher_modnames[cipher]);
+ /*
+ * If cipher module loaded it should immediately
+ * call ieee80211_crypto_register which will fill
+ * in the entry in the ciphers array.
+ */
+ cip = ciphers[cipher];
if (cip == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "%s: unable to load cipher %u, module %s\n",
- __func__, cipher,
- cipher < N(cipher_modnames) ?
- cipher_modnames[cipher] : "<unknown>");
- ic->ic_stats.is_crypto_nocipher++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+ "%s: unable to load cipher %u, module %s\n",
+ __func__, cipher, cipher_modnames[cipher]);
+ vap->iv_stats.is_crypto_nocipher++;
return 0;
}
}
@@ -290,8 +310,8 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* If the hardware does not support the cipher then
* fallback to a host-based implementation.
*/
- if ((ic->ic_caps & (1<<cipher)) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ if ((ic->ic_cryptocaps & (1<<cipher)) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w support for cipher %s, falling back to s/w\n",
__func__, cip->ic_name);
flags |= IEEE80211_KEY_SWCRYPT;
@@ -302,8 +322,8 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
* the cipher modules honor it.
*/
if (cipher == IEEE80211_CIPHER_TKIP &&
- (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ (ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIPMIC) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w support for TKIP MIC, falling back to s/w\n",
__func__);
flags |= IEEE80211_KEY_SWMIC;
@@ -327,13 +347,13 @@ again:
* fails and we try to restore previous state.
*/
key->wk_flags = flags;
- keyctx = cip->ic_attach(ic, key);
+ keyctx = cip->ic_attach(vap, key);
if (keyctx == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unable to attach cipher %s\n",
__func__, cip->ic_name);
key->wk_flags = oflags; /* restore old flags */
- ic->ic_stats.is_crypto_attachfail++;
+ vap->iv_stats.is_crypto_attachfail++;
return 0;
}
cipher_detach(key);
@@ -354,7 +374,7 @@ again:
* crypto we also call the driver to give us a key index.
*/
if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
- if (!dev_key_alloc(ic, key, &keyix, &rxkeyix)) {
+ if (!dev_key_alloc(vap, key, &keyix, &rxkeyix)) {
/*
* Driver has no room; fallback to doing crypto
* in the host. We change the flags and start the
@@ -364,8 +384,8 @@ again:
* continues to use it.
*/
if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
- ic->ic_stats.is_crypto_swfallback++;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ vap->iv_stats.is_crypto_swfallback++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no h/w resources for cipher %s, "
"falling back to s/w\n", __func__,
cip->ic_name);
@@ -375,8 +395,8 @@ again:
flags |= IEEE80211_KEY_SWMIC;
goto again;
}
- ic->ic_stats.is_crypto_keyfail++;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ vap->iv_stats.is_crypto_keyfail++;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: unable to setup cipher %s\n",
__func__, cip->ic_name);
return 0;
@@ -385,24 +405,24 @@ again:
key->wk_rxkeyix = rxkeyix;
}
return 1;
-#undef N
}
/*
* Remove the key (no locking, for internal use).
*/
static int
-_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+_ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key)
{
ieee80211_keyix keyix;
KASSERT(key->wk_cipher != NULL, ("No cipher!"));
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: %s keyix %u flags 0x%x rsc %ju tsc %ju len %u\n",
__func__, key->wk_cipher->ic_name,
key->wk_keyix, key->wk_flags,
- key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
+ key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc,
+ key->wk_keylen);
keyix = key->wk_keyix;
if (keyix != IEEE80211_KEYIX_NONE) {
@@ -410,17 +430,17 @@ _ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
* Remove hardware entry.
*/
/* XXX key cache */
- if (!dev_key_delete(ic, key)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ if (!dev_key_delete(vap, key)) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: driver did not delete key index %u\n",
__func__, keyix);
- ic->ic_stats.is_crypto_delkey++;
+ vap->iv_stats.is_crypto_delkey++;
/* XXX recovery? */
}
}
cipher_detach(key);
memset(key, 0, sizeof(*key));
- ieee80211_crypto_resetkey(ic, key, IEEE80211_KEYIX_NONE);
+ ieee80211_crypto_resetkey(vap, key, IEEE80211_KEYIX_NONE);
return 1;
}
@@ -428,13 +448,13 @@ _ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
* Remove the specified key.
*/
int
-ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key)
{
int status;
- ieee80211_key_update_begin(ic);
- status = _ieee80211_crypto_delkey(ic, key);
- ieee80211_key_update_end(ic);
+ ieee80211_key_update_begin(vap);
+ status = _ieee80211_crypto_delkey(vap, key);
+ ieee80211_key_update_end(vap);
return status;
}
@@ -442,66 +462,67 @@ ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
* Clear the global key table.
*/
void
-ieee80211_crypto_delglobalkeys(struct ieee80211com *ic)
+ieee80211_crypto_delglobalkeys(struct ieee80211vap *vap)
{
int i;
- ieee80211_key_update_begin(ic);
+ ieee80211_key_update_begin(vap);
for (i = 0; i < IEEE80211_WEP_NKID; i++)
- (void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]);
- ieee80211_key_update_end(ic);
+ (void) _ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[i]);
+ ieee80211_key_update_end(vap);
}
/*
* Set the contents of the specified key.
*
* Locking must be handled by the caller using:
- * ieee80211_key_update_begin(ic);
- * ieee80211_key_update_end(ic);
+ * ieee80211_key_update_begin(vap);
+ * ieee80211_key_update_end(vap);
*/
int
-ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key,
+ieee80211_crypto_setkey(struct ieee80211vap *vap, struct ieee80211_key *key,
const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
const struct ieee80211_cipher *cip = key->wk_cipher;
KASSERT(cip != NULL, ("No cipher!"));
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: %s keyix %u flags 0x%x mac %s rsc %ju tsc %ju len %u\n",
__func__, cip->ic_name, key->wk_keyix,
key->wk_flags, ether_sprintf(macaddr),
- key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
+ key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc,
+ key->wk_keylen);
/*
* Give cipher a chance to validate key contents.
* XXX should happen before modifying state.
*/
if (!cip->ic_setkey(key)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: cipher %s rejected key index %u len %u flags 0x%x\n",
__func__, cip->ic_name, key->wk_keyix,
key->wk_keylen, key->wk_flags);
- ic->ic_stats.is_crypto_setkey_cipher++;
+ vap->iv_stats.is_crypto_setkey_cipher++;
return 0;
}
if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
/* XXX nothing allocated, should not happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
"%s: no key index; should not happen!\n", __func__);
- ic->ic_stats.is_crypto_setkey_nokey++;
+ vap->iv_stats.is_crypto_setkey_nokey++;
return 0;
}
- return dev_key_set(ic, key, macaddr);
+ return dev_key_set(vap, key, macaddr);
}
/*
* Add privacy headers appropriate for the specified key.
*/
struct ieee80211_key *
-ieee80211_crypto_encap(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m)
+ieee80211_crypto_encap(struct ieee80211_node *ni, struct mbuf *m)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_key *k;
struct ieee80211_frame *wh;
const struct ieee80211_cipher *cip;
@@ -516,16 +537,16 @@ ieee80211_crypto_encap(struct ieee80211com *ic,
wh = mtod(m, struct ieee80211_frame *);
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
- if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] no default transmit key (%s) deftxkey %u\n",
- ether_sprintf(wh->i_addr1), __func__,
- ic->ic_def_txkey);
- ic->ic_stats.is_tx_nodefkey++;
+ if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
+ wh->i_addr1,
+ "no default transmit key (%s) deftxkey %u",
+ __func__, vap->iv_def_txkey);
+ vap->iv_stats.is_tx_nodefkey++;
return NULL;
}
- keyid = ic->ic_def_txkey;
- k = &ic->ic_nw_keys[ic->ic_def_txkey];
+ keyid = vap->iv_def_txkey;
+ k = &vap->iv_nw_keys[vap->iv_def_txkey];
} else {
keyid = 0;
k = &ni->ni_ucastkey;
@@ -539,25 +560,24 @@ ieee80211_crypto_encap(struct ieee80211com *ic,
* received frame that has the WEP/Privacy bit set.
*/
struct ieee80211_key *
-ieee80211_crypto_decap(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
+ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
{
#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
#define IEEE80211_WEP_MINLEN \
(sizeof(struct ieee80211_frame) + \
IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_key *k;
struct ieee80211_frame *wh;
const struct ieee80211_cipher *cip;
- const uint8_t *ivp;
uint8_t keyid;
/* NB: this minimum size data frame could be bigger */
if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
"%s: WEP data frame too short, len %u\n",
__func__, m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++; /* XXX need unique stat? */
+ vap->iv_stats.is_rx_tooshort++; /* XXX need unique stat? */
return NULL;
}
@@ -568,11 +588,10 @@ ieee80211_crypto_decap(struct ieee80211com *ic,
* the key id in the header is meaningless (typically 0).
*/
wh = mtod(m, struct ieee80211_frame *);
- ivp = mtod(m, const uint8_t *) + hdrlen; /* XXX contig */
- keyid = ivp[IEEE80211_WEP_IVLEN];
+ m_copydata(m, hdrlen + IEEE80211_WEP_IVLEN, sizeof(keyid), &keyid);
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey))
- k = &ic->ic_nw_keys[keyid >> 6];
+ k = &vap->iv_nw_keys[keyid >> 6];
else
k = &ni->ni_ucastkey;
@@ -582,10 +601,9 @@ ieee80211_crypto_decap(struct ieee80211com *ic,
cip = k->wk_cipher;
if (m->m_len < hdrlen + cip->ic_header &&
(m = m_pullup(m, hdrlen + cip->ic_header)) == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] unable to pullup %s header\n",
- ether_sprintf(wh->i_addr2), cip->ic_name);
- ic->ic_stats.is_rx_wepfail++; /* XXX */
+ 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;
}
diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h
index ee99caa..999f139 100644
--- a/sys/net80211/ieee80211_crypto.h
+++ b/sys/net80211/ieee80211_crypto.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,6 +42,15 @@ struct ieee80211_wepkey {
uint8_t wk_key[IEEE80211_KEYBUF_SIZE];
};
+struct ieee80211_rsnparms {
+ uint8_t rsn_mcastcipher; /* mcast/group cipher */
+ uint8_t rsn_mcastkeylen; /* mcast key length */
+ uint8_t rsn_ucastcipher; /* selected unicast cipher */
+ uint8_t rsn_ucastkeylen; /* unicast key length */
+ uint8_t rsn_keymgmt; /* selected key mgmt algo */
+ uint16_t rsn_caps; /* capabilities */
+};
+
struct ieee80211_cipher;
/*
@@ -76,7 +85,8 @@ struct ieee80211_key {
uint8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
#define wk_txmic wk_key+IEEE80211_KEYBUF_SIZE+0 /* XXX can't () right */
#define wk_rxmic wk_key+IEEE80211_KEYBUF_SIZE+8 /* XXX can't () right */
- uint64_t wk_keyrsc; /* key receive sequence counter */
+ /* key receive sequence counter */
+ uint64_t wk_keyrsc[IEEE80211_TID_SIZE];
uint64_t wk_keytsc; /* key transmit sequence counter */
const struct ieee80211_cipher *wk_cipher;
void *wk_private; /* private cipher state */
@@ -84,61 +94,54 @@ struct ieee80211_key {
#define IEEE80211_KEY_COMMON /* common flags passed in by apps */\
(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP)
+#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1)
+
/*
* NB: these values are ordered carefully; there are lots of
- * of implications in any reordering. In particular beware
- * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ * of implications in any reordering. Beware that 4 is used
+ * only to indicate h/w TKIP MIC support in driver capabilities;
+ * there is no separate cipher support (it's rolled into the
+ * TKIP cipher support).
*/
#define IEEE80211_CIPHER_WEP 0
#define IEEE80211_CIPHER_TKIP 1
#define IEEE80211_CIPHER_AES_OCB 2
#define IEEE80211_CIPHER_AES_CCM 3
+#define IEEE80211_CIPHER_TKIPMIC 4 /* TKIP MIC capability */
#define IEEE80211_CIPHER_CKIP 5
#define IEEE80211_CIPHER_NONE 6 /* pseudo value */
#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1)
-#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1)
+/* capability bits in ic_cryptocaps/iv_cryptocaps */
+#define IEEE80211_CRYPTO_WEP (1<<IEEE80211_CIPHER_WEP)
+#define IEEE80211_CRYPTO_TKIP (1<<IEEE80211_CIPHER_TKIP)
+#define IEEE80211_CRYPTO_AES_OCB (1<<IEEE80211_CIPHER_AES_OCB)
+#define IEEE80211_CRYPTO_AES_CCM (1<<IEEE80211_CIPHER_AES_CCM)
+#define IEEE80211_CRYPTO_TKIPMIC (1<<IEEE80211_CIPHER_TKIPMIC)
+#define IEEE80211_CRYPTO_CKIP (1<<IEEE80211_CIPHER_CKIP)
#if defined(__KERNEL__) || defined(_KERNEL)
struct ieee80211com;
+struct ieee80211vap;
struct ieee80211_node;
struct mbuf;
-/*
- * Crypto state kept in each ieee80211com. Some of this
- * can/should be shared when virtual AP's are supported.
- *
- * XXX save reference to ieee80211com to properly encapsulate state.
- * XXX split out crypto capabilities from ic_caps
- */
-struct ieee80211_crypto_state {
- struct ieee80211_key cs_nw_keys[IEEE80211_WEP_NKID];
- ieee80211_keyix cs_def_txkey; /* default/group tx key index */
- uint16_t cs_max_keyix; /* max h/w key index */
-
- int (*cs_key_alloc)(struct ieee80211com *,
- const struct ieee80211_key *,
- ieee80211_keyix *, ieee80211_keyix *);
- int (*cs_key_delete)(struct ieee80211com *,
- const struct ieee80211_key *);
- int (*cs_key_set)(struct ieee80211com *,
- const struct ieee80211_key *,
- const uint8_t mac[IEEE80211_ADDR_LEN]);
- void (*cs_key_update_begin)(struct ieee80211com *);
- void (*cs_key_update_end)(struct ieee80211com *);
-};
+MALLOC_DECLARE(M_80211_CRYPTO);
void ieee80211_crypto_attach(struct ieee80211com *);
void ieee80211_crypto_detach(struct ieee80211com *);
-int ieee80211_crypto_newkey(struct ieee80211com *,
+void ieee80211_crypto_vattach(struct ieee80211vap *);
+void ieee80211_crypto_vdetach(struct ieee80211vap *);
+int ieee80211_crypto_newkey(struct ieee80211vap *,
int cipher, int flags, struct ieee80211_key *);
-int ieee80211_crypto_delkey(struct ieee80211com *,
+int ieee80211_crypto_delkey(struct ieee80211vap *,
struct ieee80211_key *);
-int ieee80211_crypto_setkey(struct ieee80211com *,
- struct ieee80211_key *, const uint8_t macaddr[IEEE80211_ADDR_LEN]);
-void ieee80211_crypto_delglobalkeys(struct ieee80211com *);
+int ieee80211_crypto_setkey(struct ieee80211vap *,
+ struct ieee80211_key *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+void ieee80211_crypto_delglobalkeys(struct ieee80211vap *);
/*
* Template for a supported cipher. Ciphers register with the
@@ -152,7 +155,7 @@ struct ieee80211_cipher {
u_int ic_header; /* size of privacy header (bytes) */
u_int ic_trailer; /* size of privacy trailer (bytes) */
u_int ic_miclen; /* size of mic trailer (bytes) */
- void* (*ic_attach)(struct ieee80211com *, struct ieee80211_key *);
+ 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 *,
@@ -170,16 +173,16 @@ void ieee80211_crypto_register(const struct ieee80211_cipher *);
void ieee80211_crypto_unregister(const struct ieee80211_cipher *);
int ieee80211_crypto_available(u_int cipher);
-struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
-struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *, int);
+struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211_node *,
+ struct mbuf *);
+struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211_node *,
+ struct mbuf *, int);
/*
* Check and remove any MIC.
*/
static __inline int
-ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k,
+ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k,
struct mbuf *m, int force)
{
const struct ieee80211_cipher *cip = k->wk_cipher;
@@ -190,7 +193,7 @@ ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k,
* Add any MIC.
*/
static __inline int
-ieee80211_crypto_enmic(struct ieee80211com *ic,
+ieee80211_crypto_enmic(struct ieee80211vap *vap,
struct ieee80211_key *k, struct mbuf *m, int force)
{
const struct ieee80211_cipher *cip = k->wk_cipher;
@@ -203,11 +206,11 @@ ieee80211_crypto_enmic(struct ieee80211com *ic,
* key data) is properly setup before a key is used.
*/
static __inline void
-ieee80211_crypto_resetkey(struct ieee80211com *ic,
+ieee80211_crypto_resetkey(struct ieee80211vap *vap,
struct ieee80211_key *k, ieee80211_keyix ix)
{
k->wk_cipher = &ieee80211_cipher_none;;
- k->wk_private = k->wk_cipher->ic_attach(ic, k);
+ k->wk_private = k->wk_cipher->ic_attach(vap, k);
k->wk_keyix = k->wk_rxkeyix = ix;
k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
}
@@ -215,10 +218,10 @@ ieee80211_crypto_resetkey(struct ieee80211com *ic,
/*
* Crypt-related notification methods.
*/
-void ieee80211_notify_replay_failure(struct ieee80211com *,
+void ieee80211_notify_replay_failure(struct ieee80211vap *,
const struct ieee80211_frame *, const struct ieee80211_key *,
- u_int64_t rsc);
-void ieee80211_notify_michael_failure(struct ieee80211com *,
+ uint64_t rsc);
+void ieee80211_notify_michael_failure(struct ieee80211vap *,
const struct ieee80211_frame *, u_int keyix);
#endif /* defined(__KERNEL__) || defined(_KERNEL) */
#endif /* _NET80211_IEEE80211_CRYPTO_H_ */
diff --git a/sys/net80211/ieee80211_crypto_ccmp.c b/sys/net80211/ieee80211_crypto_ccmp.c
index 525fe9a..d3b1af5 100644
--- a/sys/net80211/ieee80211_crypto_ccmp.c
+++ b/sys/net80211/ieee80211_crypto_ccmp.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
* AP driver. The code is used with the consent of the author and
* it's license is included below.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -53,11 +55,12 @@ __FBSDID("$FreeBSD$");
#define AES_BLOCK_LEN 16
struct ccmp_ctx {
- struct ieee80211com *cc_ic; /* for diagnostics */
+ struct ieee80211vap *cc_vap; /* for diagnostics+statistics */
+ struct ieee80211com *cc_ic;
rijndael_ctx cc_aes;
};
-static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *);
+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);
@@ -89,17 +92,18 @@ static int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn,
static int nrefs = 0;
static void *
-ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+ccmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct ccmp_ctx *ctx;
MALLOC(ctx, struct ccmp_ctx *, sizeof(struct ccmp_ctx),
- M_DEVBUF, M_NOWAIT | M_ZERO);
+ M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- ic->ic_stats.is_crypto_nomem++;
+ vap->iv_stats.is_crypto_nomem++;
return NULL;
}
- ctx->cc_ic = ic;
+ ctx->cc_vap = vap;
+ ctx->cc_ic = vap->iv_ic;
nrefs++; /* NB: we assume caller locking */
return ctx;
}
@@ -109,7 +113,7 @@ ccmp_detach(struct ieee80211_key *k)
{
struct ccmp_ctx *ctx = k->wk_private;
- FREE(ctx, M_DEVBUF);
+ FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -120,7 +124,7 @@ ccmp_setkey(struct ieee80211_key *k)
struct ccmp_ctx *ctx = k->wk_private;
if (k->wk_keylen != (128/NBBY)) {
- IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
"%s: Invalid key length %u, expecting %u\n",
__func__, k->wk_keylen, 128/NBBY);
return 0;
@@ -200,8 +204,9 @@ static int
ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct ccmp_ctx *ctx = k->wk_private;
+ struct ieee80211vap *vap = ctx->cc_vap;
struct ieee80211_frame *wh;
- uint8_t *ivp;
+ uint8_t *ivp, tid;
uint64_t pn;
/*
@@ -214,19 +219,19 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
/*
* No extended IV; discard frame.
*/
- IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] Missing ExtIV for AES-CCM cipher\n",
- ether_sprintf(wh->i_addr2));
- ctx->cc_ic->ic_stats.is_rx_ccmpformat++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "missing ExtIV for AES-CCM cipher");
+ vap->iv_stats.is_rx_ccmpformat++;
return 0;
}
+ tid = ieee80211_gettid(wh);
pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
- if (pn <= k->wk_keyrsc) {
+ if (pn <= k->wk_keyrsc[tid]) {
/*
* Replay violation.
*/
- ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn);
- ctx->cc_ic->ic_stats.is_rx_ccmpreplay++;
+ ieee80211_notify_replay_failure(vap, wh, k, pn);
+ vap->iv_stats.is_rx_ccmpreplay++;
return 0;
}
@@ -251,7 +256,7 @@ ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
/*
* Ok to update rsc now.
*/
- k->wk_keyrsc = pn;
+ k->wk_keyrsc[tid] = pn;
return 1;
}
@@ -406,7 +411,7 @@ ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN];
uint8_t *pos;
- ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+ ctx->cc_vap->iv_stats.is_crypto_ccmp++;
wh = mtod(m, struct ieee80211_frame *);
data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header);
@@ -544,6 +549,7 @@ static int
ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen)
{
struct ccmp_ctx *ctx = key->wk_private;
+ struct ieee80211vap *vap = ctx->cc_vap;
struct ieee80211_frame *wh;
uint8_t aad[2 * AES_BLOCK_LEN];
uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN];
@@ -553,7 +559,7 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen
uint8_t *pos;
u_int space;
- ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+ ctx->cc_vap->iv_stats.is_crypto_ccmp++;
wh = mtod(m, struct ieee80211_frame *);
data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer);
@@ -616,10 +622,9 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen
}
}
if (memcmp(mic, a, ccmp.ic_trailer) != 0) {
- IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] AES-CCM decrypt failed; MIC mismatch\n",
- ether_sprintf(wh->i_addr2));
- ctx->cc_ic->ic_stats.is_rx_ccmpmic++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "AES-CCM decrypt failed; MIC mismatch");
+ vap->iv_stats.is_rx_ccmpmic++;
return 0;
}
return 1;
diff --git a/sys/net80211/ieee80211_crypto_none.c b/sys/net80211/ieee80211_crypto_none.c
index 7fbb53d..b1ffbb4 100644
--- a/sys/net80211/ieee80211_crypto_none.c
+++ b/sys/net80211/ieee80211_crypto_none.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,10 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 NULL crypto support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
+#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/module.h>
@@ -42,7 +45,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
-static void *none_attach(struct ieee80211com *, struct ieee80211_key *);
+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);
@@ -66,9 +69,9 @@ const struct ieee80211_cipher ieee80211_cipher_none = {
};
static void *
-none_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+none_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
- return ic; /* for diagnostics+stats */
+ return vap; /* for diagnostics+stats */
}
static void
@@ -87,7 +90,7 @@ none_setkey(struct ieee80211_key *k)
static int
none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
#endif
@@ -96,17 +99,16 @@ none_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
* The specified key is not setup; this can
* happen, at least, when changing keys.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] key id %u is not set (encap)\n",
- ether_sprintf(wh->i_addr1), keyid>>6);
- ic->ic_stats.is_tx_badcipher++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr1,
+ "key id %u is not set (encap)", keyid>>6);
+ vap->iv_stats.is_tx_badcipher++;
return 0;
}
static int
none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
const uint8_t *ivp = (const uint8_t *)&wh[1];
@@ -117,27 +119,26 @@ none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
* happen, at least, when changing keys.
*/
/* XXX useful to know dst too */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] key id %u is not set (decap)\n",
- ether_sprintf(wh->i_addr2), ivp[IEEE80211_WEP_IVLEN] >> 6);
- ic->ic_stats.is_rx_badkeyid++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "key id %u is not set (decap)", ivp[IEEE80211_WEP_IVLEN] >> 6);
+ vap->iv_stats.is_rx_badkeyid++;
return 0;
}
static int
none_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
- ic->ic_stats.is_tx_badcipher++;
+ vap->iv_stats.is_tx_badcipher++;
return 0;
}
static int
none_demic(struct ieee80211_key *k, struct mbuf *m, int force)
{
- struct ieee80211com *ic = k->wk_private;
+ struct ieee80211vap *vap = k->wk_private;
- ic->ic_stats.is_rx_badkeyid++;
+ vap->iv_stats.is_rx_badkeyid++;
return 0;
}
diff --git a/sys/net80211/ieee80211_crypto_tkip.c b/sys/net80211/ieee80211_crypto_tkip.c
index 327306a..398246f 100644
--- a/sys/net80211/ieee80211_crypto_tkip.c
+++ b/sys/net80211/ieee80211_crypto_tkip.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
* AP driver. The code is used with the consent of the author and
* it's license is included below.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -49,7 +51,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
-static void *tkip_attach(struct ieee80211com *, struct ieee80211_key *);
+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);
@@ -77,10 +79,9 @@ typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t __u32;
typedef uint32_t u32;
-#define memmove(dst, src, n) ovbcopy(src, dst, n)
struct tkip_ctx {
- struct ieee80211com *tc_ic; /* for diagnostics */
+ struct ieee80211vap *tc_vap; /* for diagnostics+statistics */
u16 tx_ttak[5];
int tx_phase1_done;
@@ -104,18 +105,18 @@ static int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *,
static int nrefs = 0;
static void *
-tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+tkip_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct tkip_ctx *ctx;
MALLOC(ctx, struct tkip_ctx *, sizeof(struct tkip_ctx),
- M_DEVBUF, M_NOWAIT | M_ZERO);
+ M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- ic->ic_stats.is_crypto_nomem++;
+ vap->iv_stats.is_crypto_nomem++;
return NULL;
}
- ctx->tc_ic = ic;
+ ctx->tc_vap = vap;
nrefs++; /* NB: we assume caller locking */
return ctx;
}
@@ -125,7 +126,7 @@ tkip_detach(struct ieee80211_key *k)
{
struct tkip_ctx *ctx = k->wk_private;
- FREE(ctx, M_DEVBUF);
+ FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -137,7 +138,7 @@ tkip_setkey(struct ieee80211_key *k)
if (k->wk_keylen != (128/NBBY)) {
(void) ctx; /* XXX */
- IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+ IEEE80211_DPRINTF(ctx->tc_vap, IEEE80211_MSG_CRYPTO,
"%s: Invalid key length %u, expecting %u\n",
__func__, k->wk_keylen, 128/NBBY);
return 0;
@@ -153,22 +154,22 @@ static int
tkip_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
{
struct tkip_ctx *ctx = k->wk_private;
- struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211vap *vap = ctx->tc_vap;
+ struct ieee80211com *ic = vap->iv_ic;
uint8_t *ivp;
int hdrlen;
/*
* Handle TKIP counter measures requirement.
*/
- if (ic->ic_flags & IEEE80211_F_COUNTERM) {
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
#ifdef IEEE80211_DEBUG
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
#endif
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] Discard frame due to countermeasures (%s)\n",
- ether_sprintf(wh->i_addr2), __func__);
- ic->ic_stats.is_crypto_tkipcm++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "discard frame due to countermeasures (%s)", __func__);
+ vap->iv_stats.is_crypto_tkipcm++;
return 0;
}
hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
@@ -215,11 +216,12 @@ tkip_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) {
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
- struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211vap *vap = ctx->tc_vap;
+ struct ieee80211com *ic = vap->iv_ic;
int hdrlen;
uint8_t mic[IEEE80211_WEP_MICLEN];
- ic->ic_stats.is_crypto_tkipenmic++;
+ vap->iv_stats.is_crypto_tkipenmic++;
hdrlen = ieee80211_hdrspace(ic, wh);
@@ -247,9 +249,9 @@ static int
tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct tkip_ctx *ctx = k->wk_private;
- struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211vap *vap = ctx->tc_vap;
struct ieee80211_frame *wh;
- uint8_t *ivp;
+ uint8_t *ivp, tid;
/*
* Header should have extended IV and sequence number;
@@ -261,30 +263,29 @@ tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
/*
* No extended IV; discard frame.
*/
- IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] missing ExtIV for TKIP cipher\n",
- ether_sprintf(wh->i_addr2));
- ctx->tc_ic->ic_stats.is_rx_tkipformat++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "missing ExtIV for TKIP cipher");
+ vap->iv_stats.is_rx_tkipformat++;
return 0;
}
/*
* Handle TKIP counter measures requirement.
*/
- if (ic->ic_flags & IEEE80211_F_COUNTERM) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] discard frame due to countermeasures (%s)\n",
- ether_sprintf(wh->i_addr2), __func__);
- ic->ic_stats.is_crypto_tkipcm++;
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "discard frame due to countermeasures (%s)", __func__);
+ vap->iv_stats.is_crypto_tkipcm++;
return 0;
}
+ tid = ieee80211_gettid(wh);
ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5], ivp[6], ivp[7]);
- if (ctx->rx_rsc <= k->wk_keyrsc) {
+ if (ctx->rx_rsc <= k->wk_keyrsc[tid]) {
/*
* Replay violation; notify upper layer.
*/
- ieee80211_notify_replay_failure(ctx->tc_ic, wh, k, ctx->rx_rsc);
- ctx->tc_ic->ic_stats.is_rx_tkipreplay++;
+ ieee80211_notify_replay_failure(vap, wh, k, ctx->rx_rsc);
+ vap->iv_stats.is_rx_tkipreplay++;
return 0;
}
/*
@@ -322,15 +323,17 @@ static int
tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
{
struct tkip_ctx *ctx = k->wk_private;
+ struct ieee80211_frame *wh;
+ uint8_t tid;
+ wh = mtod(m, struct ieee80211_frame *);
if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) {
- struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
- struct ieee80211com *ic = ctx->tc_ic;
- int hdrlen = ieee80211_hdrspace(ic, wh);
+ struct ieee80211vap *vap = ctx->tc_vap;
+ int hdrlen = ieee80211_hdrspace(vap->iv_ic, wh);
u8 mic[IEEE80211_WEP_MICLEN];
u8 mic0[IEEE80211_WEP_MICLEN];
- ic->ic_stats.is_crypto_tkipdemic++;
+ vap->iv_stats.is_crypto_tkipdemic++;
michael_mic(ctx, k->wk_rxmic,
m, hdrlen, m->m_pkthdr.len - (hdrlen + tkip.ic_miclen),
@@ -339,7 +342,7 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
tkip.ic_miclen, mic0);
if (memcmp(mic, mic0, tkip.ic_miclen)) {
/* NB: 802.11 layer handles statistic and debug msg */
- ieee80211_notify_michael_failure(ic, wh,
+ ieee80211_notify_michael_failure(vap, wh,
k->wk_rxkeyix != IEEE80211_KEYIX_NONE ?
k->wk_rxkeyix : k->wk_keyix);
return 0;
@@ -353,7 +356,8 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
/*
* Ok to update rsc now that MIC has been verified.
*/
- k->wk_keyrsc = ctx->rx_rsc;
+ tid = ieee80211_gettid(wh);
+ k->wk_keyrsc[tid] = ctx->rx_rsc;
return 1;
}
@@ -904,7 +908,7 @@ tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
struct ieee80211_frame *wh;
uint8_t icv[IEEE80211_WEP_CRCLEN];
- ctx->tc_ic->ic_stats.is_crypto_tkip++;
+ ctx->tc_vap->iv_stats.is_crypto_tkip++;
wh = mtod(m, struct ieee80211_frame *);
if (!ctx->tx_phase1_done) {
@@ -932,17 +936,20 @@ tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
struct mbuf *m, int hdrlen)
{
struct ieee80211_frame *wh;
+ struct ieee80211vap *vap = ctx->tc_vap;
u32 iv32;
u16 iv16;
+ u8 tid;
- ctx->tc_ic->ic_stats.is_crypto_tkip++;
+ vap->iv_stats.is_crypto_tkip++;
wh = mtod(m, struct ieee80211_frame *);
/* NB: tkip_decap already verified header and left seq in rx_rsc */
iv16 = (u16) ctx->rx_rsc;
iv32 = (u32) (ctx->rx_rsc >> 16);
- if (iv32 != (u32)(key->wk_keyrsc >> 16) || !ctx->rx_phase1_done) {
+ tid = ieee80211_gettid(wh);
+ if (iv32 != (u32)(key->wk_keyrsc[tid] >> 16) || !ctx->rx_phase1_done) {
tkip_mixing_phase1(ctx->rx_ttak, key->wk_key,
wh->i_addr2, iv32);
ctx->rx_phase1_done = 1;
@@ -953,15 +960,14 @@ tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
if (wep_decrypt(ctx->rx_rc4key,
m, hdrlen + tkip.ic_header,
m->m_pkthdr.len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) {
- if (iv32 != (u32)(key->wk_keyrsc >> 16)) {
+ if (iv32 != (u32)(key->wk_keyrsc[tid] >> 16)) {
/* Previously cached Phase1 result was already lost, so
* it needs to be recalculated for the next packet. */
ctx->rx_phase1_done = 0;
}
- IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] TKIP ICV mismatch on decrypt\n",
- ether_sprintf(wh->i_addr2));
- ctx->tc_ic->ic_stats.is_rx_tkipicv++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "TKIP ICV mismatch on decrypt");
+ vap->iv_stats.is_rx_tkipicv++;
return 0;
}
return 1;
diff --git a/sys/net80211/ieee80211_crypto_wep.c b/sys/net80211/ieee80211_crypto_wep.c
index 81d15cc..df988a6 100644
--- a/sys/net80211/ieee80211_crypto_wep.c
+++ b/sys/net80211/ieee80211_crypto_wep.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 WEP crypto support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -45,7 +47,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
-static void *wep_attach(struct ieee80211com *, struct ieee80211_key *);
+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);
@@ -72,7 +74,8 @@ static int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
static int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
struct wep_ctx {
- struct ieee80211com *wc_ic; /* for diagnostics */
+ struct ieee80211vap *wc_vap; /* for diagnostics+statistics */
+ struct ieee80211com *wc_ic;
uint32_t wc_iv; /* initial vector for crypto */
};
@@ -80,18 +83,19 @@ struct wep_ctx {
static int nrefs = 0;
static void *
-wep_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+wep_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
{
struct wep_ctx *ctx;
MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx),
- M_DEVBUF, M_NOWAIT | M_ZERO);
+ M_80211_CRYPTO, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- ic->ic_stats.is_crypto_nomem++;
+ vap->iv_stats.is_crypto_nomem++;
return NULL;
}
- ctx->wc_ic = ic;
+ ctx->wc_vap = vap;
+ ctx->wc_ic = vap->iv_ic;
get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
nrefs++; /* NB: we assume caller locking */
return ctx;
@@ -102,7 +106,7 @@ wep_detach(struct ieee80211_key *k)
{
struct wep_ctx *ctx = k->wk_private;
- FREE(ctx, M_DEVBUF);
+ FREE(ctx, M_80211_CRYPTO);
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
nrefs--; /* NB: we assume caller locking */
}
@@ -208,6 +212,7 @@ static int
wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
{
struct wep_ctx *ctx = k->wk_private;
+ struct ieee80211vap *vap = ctx->wc_vap;
struct ieee80211_frame *wh;
wh = mtod(m, struct ieee80211_frame *);
@@ -219,10 +224,9 @@ wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
*/
if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
!wep_decrypt(k, m, hdrlen)) {
- IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO,
- "[%s] WEP ICV mismatch on decrypt\n",
- ether_sprintf(wh->i_addr2));
- ctx->wc_ic->ic_stats.is_rx_wepfail++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s", "WEP ICV mismatch on decrypt");
+ vap->iv_stats.is_rx_wepfail++;
return 0;
}
@@ -305,6 +309,7 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
{
#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
struct wep_ctx *ctx = key->wk_private;
+ struct ieee80211vap *vap = ctx->wc_vap;
struct mbuf *m = m0;
uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
uint8_t icv[IEEE80211_WEP_CRCLEN];
@@ -314,7 +319,7 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
uint8_t *pos;
u_int off, keylen;
- ctx->wc_ic->ic_stats.is_crypto_wep++;
+ vap->iv_stats.is_crypto_wep++;
/* NB: this assumes the header was pulled up */
memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
@@ -351,12 +356,12 @@ wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
}
if (m->m_next == NULL) {
if (data_len != 0) { /* out of data */
- IEEE80211_DPRINTF(ctx->wc_ic,
- IEEE80211_MSG_CRYPTO,
- "[%s] out of data for WEP (data_len %zu)\n",
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
ether_sprintf(mtod(m0,
struct ieee80211_frame *)->i_addr2),
+ "out of data for WEP (data_len %zu)",
data_len);
+ /* XXX stat */
return 0;
}
break;
@@ -387,6 +392,7 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
{
#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
struct wep_ctx *ctx = key->wk_private;
+ struct ieee80211vap *vap = ctx->wc_vap;
struct mbuf *m = m0;
uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
uint8_t icv[IEEE80211_WEP_CRCLEN];
@@ -396,7 +402,7 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
uint8_t *pos;
u_int off, keylen;
- ctx->wc_ic->ic_stats.is_crypto_wep++;
+ vap->iv_stats.is_crypto_wep++;
/* NB: this assumes the header was pulled up */
memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
@@ -435,11 +441,9 @@ wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
m = m->m_next;
if (m == NULL) {
if (data_len != 0) { /* out of data */
- IEEE80211_DPRINTF(ctx->wc_ic,
- IEEE80211_MSG_CRYPTO,
- "[%s] out of data for WEP (data_len %zu)\n",
- ether_sprintf(mtod(m0,
- struct ieee80211_frame *)->i_addr2),
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
+ mtod(m0, struct ieee80211_frame *)->i_addr2,
+ "out of data for WEP (data_len %zu)",
data_len);
return 0;
}
diff --git a/sys/net80211/ieee80211_ddb.c b/sys/net80211/ieee80211_ddb.c
new file mode 100644
index 0000000..5b78283
--- /dev/null
+++ b/sys/net80211/ieee80211_ddb.c
@@ -0,0 +1,789 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+#include "opt_wlan.h"
+
+#ifdef DDB
+/*
+ * IEEE 802.11 DDB support
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <ddb/ddb.h>
+
+#define IEEE80211_MSG_BITS \
+ "\20\3IOCTL\4WDS\5ACTION\6RATECTL\7ROAM\10INACT\11DOTH\12SUPERG" \
+ "\13WME\14ACL\15WPA\16RADKEYS\17RADDUMP\20RADIUS\21DOT1X\22POWER" \
+ "\23STATE\24OUTPUT\25SCAN\26AUTH\27ASSOC\30NODE\31ELEMID\32XRATE" \
+ "\33INPUT\34CRYPTO\35DUPMPKTS\36DEBUG\3711N"
+
+#define IEEE80211_F_BITS \
+ "\20\1TURBOP\2COMP\3FF\4BURST\5PRIVACY\6PUREG\10SCAN\11ASCAN\12SIBSS" \
+ "\13SHSLOT\14PMGTON\15DESBSSID\16WME\17BGSCAN\20SWRETRY\21TXPOW_FIXED" \
+ "\22IBSSON\23SHPREAMBLE\24DATAPAD\25USEPROT\26USERBARKER\27CSAPENDING" \
+ "\30WPA1\31WPA2\32DROPUNENC\33COUNTERM\34HIDESSID\35NOBRIDG\36PCF" \
+ "\37DOTH\40DWDS"
+
+#define IEEE80211_FEXT_BITS \
+ "\20\1NONHT_PR\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\12NONEPR_PR"\
+ "\13SWBMISS\14DFS\15DOTD\22WDSLEGACY\23PROBECHAN\24HT\25AMDPU_TX" \
+ "\26AMPDU_TX\27AMSDU_TX\30AMSDU_RX\31USEHT40\32PUREN\33SHORTGI20" \
+ "\34SHORTGI40\35HTCOMPAT"
+
+#define IEEE80211_FVEN_BITS "\20"
+
+#define IEEE80211_C_BITS \
+ "\20\7FF\10TURBOP\11IBSS\12PMGT" \
+ "\13HOSTAP\14AHDEMO\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE" \
+ "\21MONITOR\30WPA1\31WPA2\32BURST\33WME\34WDS\36BGSCAN" \
+ "\37TXFRAG"
+
+#define IEEE80211_C_CRYPTO_BITS \
+ "\20\1WEP\2TKIP\3AES\4AES_CCM\5TKIPMIC\6CKIP\12PMGT"
+
+#define IEEE80211_C_HTCAP_BITS \
+ "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
+ "\21AMPDU\22AMSDU\23HT"
+
+/* NB: policy bits not included */
+#define IEEE80211_CHAN_BITS \
+ "\20\5TURBO\6CCK\7OFDM\0102GHZ\0115GHZ\12PASSIVE\13DYN\14GFSK" \
+ "\15STURBO\16HALF\17QUARTER\20HT20\21HT40U\22HT40D\23DFS"
+
+#define IEEE80211_NODE_BITS \
+ "\20\1AUTH\2QOS\3ERP\5PWR_MGT\6AREF\7HT\10HTCOMPAT\11WPS\12TSN" \
+ "\13AMPDU_RX\14AMPDU_TX"
+
+#define IEEE80211_ERP_BITS \
+ "\20\1NON_ERP_PRESENT\2USE_PROTECTION\3LONG_PREAMBLE"
+
+#define IEEE80211_CAPINFO_BITS \
+ "\20\1ESS\2IBSS\3CF_POLLABLE\4CF_POLLREQ\5PRIVACY\6SHORT_PREAMBLE" \
+ "\7PBCC\10CHNL_AGILITY\11SPECTRUM_MGMT\13SHORT_SLOTTIME\14RSN" \
+ "\16DSSOFDM"
+
+#define IEEE80211_HTCAP_BITS \
+ "\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
+ "\13DELBA\14AMSDU(7935)\15DSSSCCK40\16PSMP\1740INTOLERANT" \
+ "\20LSIGTXOPPROT"
+
+#define IEEE80211_AGGR_BITS \
+ "\20\1IMMEDIATE\2XCHGPEND\3RUNNING\4SETUP\5NAK"
+
+static void _db_show_sta(const struct ieee80211_node *);
+static void _db_show_vap(const struct ieee80211vap *, int);
+static void _db_show_com(const struct ieee80211com *,
+ int showvaps, int showsta, int showprocs);
+
+static void _db_show_channel(const char *tag, const struct ieee80211_channel *);
+static void _db_show_ssid(const char *tag, int ix, int len, const uint8_t *);
+static void _db_show_appie(const char *tag, const struct ieee80211_appie *);
+static void _db_show_key(const char *tag, int ix, const struct ieee80211_key *);
+static void _db_show_roamparams(const char *tag, const void *arg,
+ const struct ieee80211_roamparam *rp);
+static void _db_show_txparams(const char *tag, const void *arg,
+ const struct ieee80211_txparam *tp);
+static void _db_show_stats(const struct ieee80211_stats *);
+
+DB_SHOW_COMMAND(sta, db_show_sta)
+{
+ if (!have_addr) {
+ db_printf("usage: show sta <addr>\n");
+ return;
+ }
+ _db_show_sta((const struct ieee80211_node *) addr);
+}
+
+DB_SHOW_COMMAND(vap, db_show_vap)
+{
+ int i, showprocs = 0;
+
+ if (!have_addr) {
+ db_printf("usage: show vap <addr>\n");
+ return;
+ }
+ for (i = 0; modif[i] != '\0'; i++)
+ switch (modif[i]) {
+ case 'a':
+ showprocs = 1;
+ break;
+ case 'p':
+ showprocs = 1;
+ break;
+ }
+ _db_show_vap((const struct ieee80211vap *) addr, showprocs);
+}
+
+DB_SHOW_COMMAND(com, db_show_com)
+{
+ const struct ieee80211com *ic;
+ int i, showprocs = 0, showvaps = 0, showsta = 0;
+
+ if (!have_addr) {
+ db_printf("usage: show com <addr>\n");
+ return;
+ }
+ for (i = 0; modif[i] != '\0'; i++)
+ switch (modif[i]) {
+ case 'a':
+ showsta = showvaps = showprocs = 1;
+ break;
+ case 's':
+ showsta = 1;
+ break;
+ case 'v':
+ showvaps = 1;
+ break;
+ case 'p':
+ showprocs = 1;
+ break;
+ }
+
+ ic = (const struct ieee80211com *) addr;
+ _db_show_com(ic, showvaps, showsta, showprocs);
+}
+
+DB_SHOW_ALL_COMMAND(vaps, db_show_all_vaps)
+{
+ const struct ifnet *ifp;
+ int i, showall = 0;
+
+ for (i = 0; modif[i] != '\0'; i++)
+ switch (modif[i]) {
+ case 'a':
+ showall = 1;
+ break;
+ }
+
+ TAILQ_FOREACH(ifp, &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);
+ }
+}
+
+static void
+_db_show_txampdu(const char *sep, int ix, const struct ieee80211_tx_ampdu *tap)
+{
+ db_printf("%stxampdu[%d]: %p flags %b ac %u\n",
+ sep, ix, tap, tap->txa_flags, IEEE80211_AGGR_BITS, tap->txa_ac);
+ db_printf("%s token %u qbytes %d qframes %d seqstart %u start %u wnd %u\n",
+ sep, tap->txa_token, tap->txa_qbytes, tap->txa_qframes,
+ tap->txa_seqstart, tap->txa_start, tap->txa_wnd);
+ db_printf("%s attempts %d nextrequest %d\n",
+ sep, tap->txa_attempts, tap->txa_nextrequest);
+ /* XXX packet q + timer */
+}
+
+static void
+_db_show_rxampdu(const char *sep, int ix, const struct ieee80211_rx_ampdu *rap)
+{
+ db_printf("%srxampdu[%d]: %p flags 0x%x tid %u\n",
+ sep, ix, rap, rap->rxa_flags, ix /*XXX */);
+ db_printf("%s qbytes %d qframes %d seqstart %u start %u wnd %u\n",
+ sep, rap->rxa_qbytes, rap->rxa_qframes,
+ rap->rxa_seqstart, rap->rxa_start, rap->rxa_wnd);
+ db_printf("%s age %d nframes %d\n",
+ sep, rap->rxa_age, rap->rxa_nframes);
+}
+
+static void
+_db_show_sta(const struct ieee80211_node *ni)
+{
+ int i;
+
+ db_printf("0x%p: mac %s refcnt %d\n", ni,
+ ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni));
+ db_printf("\tvap %p wdsvap %p ic %p table %p\n",
+ ni->ni_vap, ni->ni_wdsvap, ni->ni_ic, ni->ni_table);
+ db_printf("\tflags=%b\n", ni->ni_flags, IEEE80211_NODE_BITS);
+ db_printf("\tscangen %u authmode %u ath_flags 0x%x ath_defkeyix %u\n",
+ ni->ni_scangen, ni->ni_authmode,
+ ni->ni_ath_flags, ni->ni_ath_defkeyix);
+ db_printf("\tassocid 0x%x txpower %u vlan %u\n",
+ ni->ni_associd, ni->ni_txpower, ni->ni_vlan);
+ db_printf("\tjointime %d (%lu secs) challenge %p\n",
+ ni->ni_jointime, (unsigned long)(time_uptime - ni->ni_jointime),
+ ni->ni_challenge);
+ db_printf("\ties: data %p len %d\n", ni->ni_ies.data, ni->ni_ies.len);
+ db_printf("\t[wpa_ie %p rsn_ie %p wme_ie %p ath_ie %p\n",
+ ni->ni_ies.wpa_ie, ni->ni_ies.rsn_ie, ni->ni_ies.wme_ie,
+ ni->ni_ies.ath_ie);
+ db_printf("\t htcap_ie %p htinfo_ie %p]\n",
+ ni->ni_ies.htcap_ie, ni->ni_ies.htinfo_ie);
+ db_printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n",
+ ni->ni_txseqs[IEEE80211_NONQOS_TID],
+ ni->ni_rxseqs[IEEE80211_NONQOS_TID] >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxfragstamp);
+ db_printf("\trxfrag[0] %p rxfrag[1] %p rxfrag[2] %p\n",
+ ni->ni_rxfrag[0], ni->ni_rxfrag[1], ni->ni_rxfrag[2]);
+ db_printf("\trstamp %u avgrssi 0x%x (rssi %d) noise %d\n",
+ ni->ni_rstamp, ni->ni_avgrssi,
+ IEEE80211_RSSI_GET(ni->ni_avgrssi), ni->ni_noise);
+ db_printf("\tintval %u capinfo %b\n",
+ ni->ni_intval, ni->ni_capinfo, IEEE80211_CAPINFO_BITS);
+ db_printf("\tbssid %s", ether_sprintf(ni->ni_bssid));
+ _db_show_ssid(" essid ", 0, ni->ni_esslen, ni->ni_essid);
+ db_printf("\n");
+ _db_show_channel("\tchannel", ni->ni_chan);
+ db_printf("\n");
+ db_printf("\terp %b dtim_period %u dtim_count %u\n",
+ ni->ni_erp, IEEE80211_ERP_BITS,
+ ni->ni_dtim_period, ni->ni_dtim_count);
+
+ db_printf("\thtcap %b htparam 0x%x htctlchan %u ht2ndchan %u\n",
+ ni->ni_htcap, IEEE80211_HTCAP_BITS,
+ ni->ni_htparam, ni->ni_htctlchan, ni->ni_ht2ndchan);
+ db_printf("\thtopmode 0x%x htstbc 0x%x reqcw %u chw %u\n",
+ ni->ni_htopmode, ni->ni_htstbc, ni->ni_reqcw, ni->ni_chw);
+
+ /* XXX ampdu state */
+ for (i = 0; i < WME_NUM_AC; i++)
+ if (ni->ni_tx_ampdu[i].txa_flags & IEEE80211_AGGR_SETUP)
+ _db_show_txampdu("\t", i, &ni->ni_tx_ampdu[i]);
+ for (i = 0; i < WME_NUM_TID; i++)
+ if (ni->ni_rx_ampdu[i].rxa_nframes)
+ _db_show_rxampdu("\t", i, &ni->ni_rx_ampdu[i]);
+
+ db_printf("\tinact %u inact_reload %u txrate %u\n",
+ ni->ni_inact, ni->ni_inact_reload, ni->ni_txrate);
+ /* XXX savedq */
+ /* XXX wdsq */
+}
+
+static void
+_db_show_vap(const struct ieee80211vap *vap, int showprocs)
+{
+ const struct ieee80211com *ic = vap->iv_ic;
+ int i;
+
+ db_printf("%p:", vap);
+ db_printf(" bss %p", vap->iv_bss);
+ db_printf(" myaddr %s", ether_sprintf(vap->iv_myaddr));
+ db_printf("\n");
+
+ db_printf("\topmode %s", ieee80211_opmode_name[vap->iv_opmode]);
+ db_printf(" state %s", ieee80211_state_name[vap->iv_state]);
+ db_printf(" ifp %p", vap->iv_ifp);
+ db_printf("\n");
+
+ db_printf("\tic %p", vap->iv_ic);
+ db_printf(" media %p", &vap->iv_media);
+ db_printf(" bpf_if %p", vap->iv_rawbpf);
+ db_printf(" mgtsend %p", &vap->iv_mgtsend);
+#if 0
+ struct sysctllog *iv_sysctl; /* dynamic sysctl context */
+#endif
+ db_printf("\n");
+ db_printf("\tdebug=%b\n", vap->iv_debug, IEEE80211_MSG_BITS);
+
+ db_printf("\tflags=%b\n", vap->iv_flags, IEEE80211_F_BITS);
+ db_printf("\tflags_ext=%b\n", vap->iv_flags_ext, IEEE80211_FEXT_BITS);
+ db_printf("\tflags_ven=%b\n", vap->iv_flags_ven, IEEE80211_FVEN_BITS);
+ db_printf("\tcaps=%b\n", vap->iv_caps, IEEE80211_C_BITS);
+ db_printf("\thtcaps=%b\n", vap->iv_htcaps, IEEE80211_C_HTCAP_BITS);
+
+ _db_show_stats(&vap->iv_stats);
+
+ db_printf("\tinact_init %d", vap->iv_inact_init);
+ db_printf(" inact_auth %d", vap->iv_inact_auth);
+ db_printf(" inact_run %d", vap->iv_inact_run);
+ db_printf(" inact_probe %d", vap->iv_inact_probe);
+ db_printf("\n");
+
+ db_printf("\tdes_nssid %d", vap->iv_des_nssid);
+ if (vap->iv_des_nssid)
+ _db_show_ssid(" des_ssid[%u] ", 0,
+ vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
+ db_printf(" des_bssid %s", ether_sprintf(vap->iv_des_bssid));
+ db_printf("\n");
+ db_printf("\tdes_mode %d", vap->iv_des_mode);
+ _db_show_channel(" des_chan", vap->iv_des_chan);
+ db_printf("\n");
+#if 0
+ int iv_nicknamelen; /* XXX junk */
+ uint8_t iv_nickname[IEEE80211_NWID_LEN];
+#endif
+ db_printf("\tbgscanidle %u", vap->iv_bgscanidle);
+ db_printf(" bgscanintvl %u", vap->iv_bgscanintvl);
+ db_printf(" scanvalid %u", vap->iv_scanvalid);
+ db_printf("\n");
+ db_printf("\tscanreq_duration %u", vap->iv_scanreq_duration);
+ db_printf(" scanreq_mindwell %u", vap->iv_scanreq_mindwell);
+ db_printf(" scanreq_maxdwell %u", vap->iv_scanreq_maxdwell);
+ db_printf("\n");
+ db_printf(" scanreq_flags 0x%x", vap->iv_scanreq_flags);
+ db_printf("\tscanreq_nssid %d", vap->iv_scanreq_nssid);
+ for (i = 0; i < vap->iv_scanreq_nssid; i++)
+ _db_show_ssid(" scanreq_ssid[%u]", i,
+ vap->iv_scanreq_ssid[i].len, vap->iv_scanreq_ssid[i].ssid);
+ db_printf(" roaming %d", vap->iv_roaming);
+ db_printf("\n");
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++)
+ if (isset(ic->ic_modecaps, i)) {
+ _db_show_roamparams("\troamparms[%s]",
+ ieee80211_phymode_name[i], &vap->iv_roamparms[i]);
+ db_printf("\n");
+ }
+
+ db_printf("\tbmissthreshold %u", vap->iv_bmissthreshold);
+ db_printf(" bmiss_max %u", vap->iv_bmiss_count);
+ db_printf(" bmiss_max %d", vap->iv_bmiss_max);
+ db_printf("\n");
+ db_printf("\tswbmiss_count %u", vap->iv_swbmiss_count);
+ db_printf(" swbmiss_period %u", vap->iv_swbmiss_period);
+ db_printf(" swbmiss %p", &vap->iv_swbmiss);
+ db_printf("\n");
+
+ db_printf("\tampdu_rxmax %d", vap->iv_ampdu_rxmax);
+ db_printf(" ampdu_density %d", vap->iv_ampdu_density);
+ db_printf(" ampdu_limit %d", vap->iv_ampdu_limit);
+ db_printf(" amsdu_limit %d", vap->iv_amsdu_limit);
+ db_printf("\n");
+
+ db_printf("\tmax_aid %u", vap->iv_max_aid);
+ db_printf(" aid_bitmap %p", vap->iv_aid_bitmap);
+ db_printf("\n");
+ db_printf("\tsta_assoc %u", vap->iv_sta_assoc);
+ db_printf(" ps_sta %u", vap->iv_ps_sta);
+ db_printf(" ps_pending %u", vap->iv_ps_pending);
+ db_printf(" tim_len %u", vap->iv_tim_len);
+ db_printf(" tim_bitmap %p", vap->iv_tim_bitmap);
+ db_printf("\n");
+ db_printf("\tdtim_period %u", vap->iv_dtim_period);
+ db_printf(" dtim_count %u", vap->iv_dtim_count);
+ db_printf(" set_tim %p", vap->iv_set_tim);
+ db_printf(" csa_count %d", vap->iv_csa_count);
+ db_printf("\n");
+
+ db_printf("\trtsthreshold %u", vap->iv_rtsthreshold);
+ db_printf(" fragthreshold %u", vap->iv_fragthreshold);
+ db_printf(" inact_timer %d", vap->iv_inact_timer);
+ db_printf("\n");
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++)
+ if (isset(ic->ic_modecaps, i)) {
+ _db_show_txparams("\ttxparms[%s]",
+ ieee80211_phymode_name[i], &vap->iv_txparms[i]);
+ db_printf("\n");
+ }
+
+ /* application-specified IE's to attach to mgt frames */
+ _db_show_appie("\tappie_beacon", vap->iv_appie_beacon);
+ _db_show_appie("\tappie_probereq", vap->iv_appie_probereq);
+ _db_show_appie("\tappie_proberesp", vap->iv_appie_proberesp);
+ _db_show_appie("\tappie_assocreq", vap->iv_appie_assocreq);
+ _db_show_appie("\tappie_asscoresp", vap->iv_appie_assocresp);
+ _db_show_appie("\tappie_wpa", vap->iv_appie_wpa);
+ if (vap->iv_wpa_ie != NULL || vap->iv_rsn_ie != NULL) {
+ if (vap->iv_wpa_ie != NULL)
+ db_printf("\twpa_ie %p", vap->iv_wpa_ie);
+ if (vap->iv_rsn_ie != NULL)
+ db_printf("\trsn_ie %p", vap->iv_rsn_ie);
+ db_printf("\n");
+ }
+ db_printf("\tmax_keyix %u", vap->iv_max_keyix);
+ db_printf(" def_txkey %d", vap->iv_def_txkey);
+ db_printf("\n");
+ for (i = 0; i < IEEE80211_WEP_NKID; i++)
+ _db_show_key("\tnw_keys[%u]", i, &vap->iv_nw_keys[i]);
+
+ db_printf("\tauth %p", vap->iv_auth);
+ db_printf(" ec %p", vap->iv_ec);
+
+ db_printf(" acl %p", vap->iv_acl);
+ db_printf(" as %p", vap->iv_as);
+ db_printf("\n");
+
+ if (showprocs) {
+ db_printf("\tiv_key_alloc %p\n", vap->iv_key_alloc);
+ db_printf("\tiv_key_delete %p\n", vap->iv_key_delete);
+ db_printf("\tiv_key_set %p\n", vap->iv_key_set);
+ db_printf("\tiv_key_update_begin %p\n", vap->iv_key_update_begin);
+ db_printf("\tiv_key_update_end %p\n", vap->iv_key_update_end);
+ db_printf("\tiv_opdetach %p\n", vap->iv_opdetach);
+ db_printf("\tiv_input %p\n", vap->iv_input);
+ db_printf("\tiv_recv_mgmt %p\n", vap->iv_recv_mgmt);
+ db_printf("\tiv_deliver_data %p\n", vap->iv_deliver_data);
+ db_printf("\tiv_bmiss %p\n", vap->iv_bmiss);
+ db_printf("\tiv_reset %p\n", vap->iv_reset);
+ db_printf("\tiv_update_beacon %p\n", vap->iv_update_beacon);
+ db_printf("\tiv_newstate %p\n", vap->iv_newstate);
+ db_printf("\tiv_output %p\n", vap->iv_output);
+ }
+}
+
+static void
+_db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showprocs)
+{
+ struct ieee80211vap *vap;
+
+ db_printf("%p:", ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ db_printf(" %s(%p)", vap->iv_ifp->if_xname, vap);
+ db_printf("\n");
+ db_printf("\tifp %p", ic->ic_ifp);
+ db_printf(" comlock %p", &ic->ic_comlock);
+ db_printf("\n");
+ _db_show_stats(&ic->ic_stats);
+ 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(" myaddr %s", ether_sprintf(ic->ic_myaddr));
+ db_printf(" inact %p", &ic->ic_inact);
+ db_printf("\n");
+
+ db_printf("\tflags=%b\n", ic->ic_flags, IEEE80211_F_BITS);
+ db_printf("\tflags_ext=%b\n", ic->ic_flags_ext, IEEE80211_FEXT_BITS);
+ db_printf("\tflags_ven=%b\n", ic->ic_flags_ven, IEEE80211_FVEN_BITS);
+ db_printf("\tcaps=%b\n", ic->ic_caps, IEEE80211_C_BITS);
+ db_printf("\tcryptocaps=%b\n",
+ ic->ic_cryptocaps, IEEE80211_C_CRYPTO_BITS);
+ db_printf("\thtcaps=%b\n", ic->ic_htcaps, IEEE80211_HTCAP_BITS);
+
+#if 0
+ uint8_t ic_modecaps[2]; /* set of mode capabilities */
+#endif
+ db_printf("\tcurmode %u", ic->ic_curmode);
+ db_printf(" promisc %u", ic->ic_promisc);
+ db_printf(" allmulti %u", ic->ic_allmulti);
+ db_printf(" nrunning %u", ic->ic_nrunning);
+ db_printf("\n");
+ db_printf("\tbintval %u", ic->ic_bintval);
+ db_printf(" lintval %u", ic->ic_lintval);
+ db_printf(" holdover %u", ic->ic_holdover);
+ db_printf(" txpowlimit %u", ic->ic_txpowlimit);
+ db_printf("\n");
+#if 0
+ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+#endif
+ /*
+ * Channel state:
+ *
+ * ic_channels is the set of available channels for the device;
+ * it is setup by the driver
+ * ic_nchans is the number of valid entries in ic_channels
+ * ic_chan_avail is a bit vector of these channels used to check
+ * whether a channel is available w/o searching the channel table.
+ * ic_chan_active is a (potentially) constrained subset of
+ * ic_chan_avail that reflects any mode setting or user-specified
+ * limit on the set of channels to use/scan
+ * ic_curchan is the current channel the device is set to; it may
+ * be different from ic_bsschan when we are off-channel scanning
+ * or otherwise doing background work
+ * ic_bsschan is the channel selected for operation; it may
+ * be undefined (IEEE80211_CHAN_ANYC)
+ * ic_prevchan is a cached ``previous channel'' used to optimize
+ * lookups when switching back+forth between two channels
+ * (e.g. for dynamic turbo)
+ */
+ db_printf("\tnchans %d", ic->ic_nchans);
+#if 0
+ struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
+ uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES];
+ uint8_t ic_chan_active[IEEE80211_CHAN_BYTES];
+ uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES];
+#endif
+ db_printf("\n");
+ _db_show_channel("\tcurchan", ic->ic_curchan);
+ db_printf("\n");
+ _db_show_channel("\tbsschan", ic->ic_bsschan);
+ db_printf("\n");
+ _db_show_channel("\tprevchan", ic->ic_prevchan);
+ db_printf("\n");
+ db_printf("\tregdomain %p", &ic->ic_regdomain);
+ db_printf("\n");
+
+ _db_show_channel("\tcsa_newchan", ic->ic_csa_newchan);
+ db_printf(" csa_count %d", ic->ic_csa_count);
+ db_printf( "dfs %p", &ic->ic_dfs);
+ db_printf("\n");
+
+ db_printf("\tscan %p", ic->ic_scan);
+ db_printf(" lastdata %d", ic->ic_lastdata);
+ db_printf(" lastscan %d", ic->ic_lastscan);
+ db_printf("\n");
+
+ db_printf("\tmax_keyix %d", ic->ic_max_keyix);
+ db_printf(" sta %p", &ic->ic_sta);
+ db_printf(" wme %p", &ic->ic_wme);
+ db_printf("\n");
+
+ db_printf("\tprotmode %d", ic->ic_protmode);
+ db_printf(" nonerpsta %u", ic->ic_nonerpsta);
+ db_printf(" longslotsta %u", ic->ic_longslotsta);
+ db_printf(" lastnonerp %d", ic->ic_lastnonerp);
+ db_printf("\n");
+ db_printf("\tsta_assoc %u", ic->ic_sta_assoc);
+ db_printf(" ht_sta_assoc %u", ic->ic_ht_sta_assoc);
+ db_printf(" ht40_sta_assoc %u", ic->ic_ht40_sta_assoc);
+ db_printf("\n");
+ db_printf("\tcurhtprotmode 0x%x", ic->ic_curhtprotmode);
+ db_printf(" htprotmode %d", ic->ic_htprotmode);
+ db_printf(" lastnonht %d", ic->ic_lastnonht);
+ db_printf("\n");
+
+ if (showprocs) {
+ db_printf("\tic_vap_create %p\n", ic->ic_vap_create);
+ db_printf("\tic_vap_delete %p\n", ic->ic_vap_delete);
+#if 0
+ /* operating mode attachment */
+ ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX];
+#endif
+ db_printf("\tic_newassoc %p\n", ic->ic_newassoc);
+ db_printf("\tic_getradiocaps %p\n", ic->ic_getradiocaps);
+ db_printf("\tic_setregdomain %p\n", ic->ic_setregdomain);
+ db_printf("\tic_send_mgmt %p\n", ic->ic_send_mgmt);
+ db_printf("\tic_raw_xmit %p\n", ic->ic_raw_xmit);
+ db_printf("\tic_updateslot %p\n", ic->ic_updateslot);
+ db_printf("\tic_update_mcast %p\n", ic->ic_update_mcast);
+ db_printf("\tic_update_promisc %p\n", ic->ic_update_promisc);
+ db_printf("\tic_node_alloc %p\n", ic->ic_node_alloc);
+ db_printf("\tic_node_free %p\n", ic->ic_node_free);
+ db_printf("\tic_node_cleanup %p\n", ic->ic_node_cleanup);
+ db_printf("\tic_node_getrssi %p\n", ic->ic_node_getrssi);
+ db_printf("\tic_node_getsignal %p\n", ic->ic_node_getsignal);
+ db_printf("\tic_node_getmimoinfo %p\n", ic->ic_node_getmimoinfo);
+ db_printf("\tic_scan_start %p\n", ic->ic_scan_start);
+ db_printf("\tic_scan_end %p\n", ic->ic_scan_end);
+ db_printf("\tic_set_channel %p\n", ic->ic_set_channel);
+ db_printf("\tic_scan_curchan %p\n", ic->ic_scan_curchan);
+ db_printf("\tic_scan_mindwell %p\n", ic->ic_scan_mindwell);
+ db_printf("\tic_recv_action %p\n", ic->ic_recv_action);
+ db_printf("\tic_send_action %p\n", ic->ic_send_action);
+ db_printf("\tic_addba_request %p\n", ic->ic_addba_request);
+ db_printf("\tic_addba_response %p\n", ic->ic_addba_response);
+ db_printf("\tic_addba_stop %p\n", ic->ic_addba_stop);
+ }
+ if (showvaps && !TAILQ_EMPTY(&ic->ic_vaps)) {
+ db_printf("\n");
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ _db_show_vap(vap, showprocs);
+ }
+ if (showsta && !TAILQ_EMPTY(&ic->ic_sta.nt_node)) {
+ const struct ieee80211_node_table *nt = &ic->ic_sta;
+ const struct ieee80211_node *ni;
+
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+ db_printf("\n");
+ _db_show_sta(ni);
+ }
+ }
+}
+
+static void
+_db_show_channel(const char *tag, const struct ieee80211_channel *c)
+{
+ db_printf("%s ", tag);
+ if (c == NULL)
+ db_printf("<NULL>");
+ else if (c == IEEE80211_CHAN_ANYC)
+ db_printf("<ANY>");
+ else
+ db_printf("[%u (%u) flags=%b maxreg %u maxpow %u minpow %u state 0x%x extieee %u]",
+ c->ic_freq, c->ic_ieee,
+ c->ic_flags, IEEE80211_CHAN_BITS,
+ c->ic_maxregpower, c->ic_maxpower, c->ic_minpower,
+ c->ic_state, c->ic_extieee);
+}
+
+static void
+_db_show_ssid(const char *tag, int ix, int len, const uint8_t *ssid)
+{
+ const uint8_t *p;
+ int i;
+
+ db_printf(tag, ix);
+
+ if (len > IEEE80211_NWID_LEN)
+ len = IEEE80211_NWID_LEN;
+ /* determine printable or not */
+ for (i = 0, p = ssid; i < len; i++, p++) {
+ if (*p < ' ' || *p > 0x7e)
+ break;
+ }
+ if (i == len) {
+ db_printf("\"");
+ for (i = 0, p = ssid; i < len; i++, p++)
+ db_printf("%c", *p);
+ db_printf("\"");
+ } else {
+ db_printf("0x");
+ for (i = 0, p = ssid; i < len; i++, p++)
+ db_printf("%02x", *p);
+ }
+}
+
+static void
+_db_show_appie(const char *tag, const struct ieee80211_appie *ie)
+{
+ const uint8_t *p;
+ int i;
+
+ if (ie == NULL)
+ return;
+ db_printf("%s [0x", tag);
+ for (i = 0, p = ie->ie_data; i < ie->ie_len; i++, p++)
+ db_printf("%02x", *p);
+ db_printf("]\n");
+}
+
+static void
+_db_show_key(const char *tag, int ix, const struct ieee80211_key *wk)
+{
+ static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
+ const struct ieee80211_cipher *cip = wk->wk_cipher;
+ int keylen = wk->wk_keylen;
+
+ if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
+ return;
+ db_printf(tag, ix);
+ switch (cip->ic_cipher) {
+ case IEEE80211_CIPHER_WEP:
+ /* compatibility */
+ db_printf(" wepkey %u:%s", wk->wk_keyix+1,
+ keylen <= 5 ? "40-bit" :
+ keylen <= 13 ? "104-bit" : "128-bit");
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ if (keylen > 128/8)
+ keylen -= 128/8; /* ignore MIC for now */
+ db_printf(" TKIP %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_AES_OCB:
+ db_printf(" AES-OCB %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ db_printf(" AES-CCM %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_CKIP:
+ db_printf(" CKIP %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ case IEEE80211_CIPHER_NONE:
+ db_printf(" NULL %u:%u-bit", wk->wk_keyix+1, 8*keylen);
+ break;
+ default:
+ db_printf(" UNKNOWN (0x%x) %u:%u-bit",
+ cip->ic_cipher, wk->wk_keyix+1, 8*keylen);
+ break;
+ }
+ if (memcmp(wk->wk_key, zerodata, keylen) != 0) {
+ int i;
+
+ db_printf(" <");
+ for (i = 0; i < keylen; i++)
+ db_printf("%02x", wk->wk_key[i]);
+ db_printf(">");
+ if (cip->ic_cipher != IEEE80211_CIPHER_WEP &&
+ wk->wk_keyrsc[IEEE80211_NONQOS_TID] != 0)
+ db_printf(" rsc %ju", (uintmax_t)wk->wk_keyrsc[IEEE80211_NONQOS_TID]);
+ if (cip->ic_cipher != IEEE80211_CIPHER_WEP &&
+ wk->wk_keytsc != 0)
+ db_printf(" tsc %ju", (uintmax_t)wk->wk_keytsc);
+ if (wk->wk_flags != 0) {
+ const char *sep = " ";
+
+ if (wk->wk_flags & IEEE80211_KEY_XMIT)
+ db_printf("%stx", sep), sep = "+";
+ if (wk->wk_flags & IEEE80211_KEY_RECV)
+ db_printf("%srx", sep), sep = "+";
+ if (wk->wk_flags & IEEE80211_KEY_DEFAULT)
+ db_printf("%sdef", sep), sep = "+";
+ }
+ db_printf("\n");
+ }
+}
+
+static void
+printrate(const char *tag, int v)
+{
+ if (v == IEEE80211_FIXED_RATE_NONE)
+ db_printf(" %s <none>", tag);
+ else if (v == 11)
+ db_printf(" %s 5.5", tag);
+ else if (v & IEEE80211_RATE_MCS)
+ db_printf(" %s MCS%d", tag, v &~ IEEE80211_RATE_MCS);
+ else
+ db_printf(" %s %d", tag, v/2);
+}
+
+static void
+_db_show_roamparams(const char *tag, const void *arg,
+ const struct ieee80211_roamparam *rp)
+{
+
+ db_printf(tag, arg);
+ if (rp->rssi & 1)
+ db_printf(" rssi %u.5", rp->rssi/2);
+ else
+ db_printf(" rssi %u", rp->rssi/2);
+ printrate("rate", rp->rate);
+}
+
+static void
+_db_show_txparams(const char *tag, const void *arg,
+ const struct ieee80211_txparam *tp)
+{
+
+ db_printf(tag, arg);
+ printrate("ucastrate", tp->ucastrate);
+ printrate("mcastrate", tp->mcastrate);
+ printrate("mgmtrate", tp->mgmtrate);
+ db_printf(" maxretry %d", tp->maxretry);
+}
+
+static void
+_db_show_stats(const struct ieee80211_stats *is)
+{
+}
+#endif /* DDB */
diff --git a/sys/net80211/ieee80211_dfs.c b/sys/net80211/ieee80211_dfs.c
new file mode 100644
index 0000000..0351cb8
--- /dev/null
+++ b/sys/net80211/ieee80211_dfs.c
@@ -0,0 +1,372 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 DFS/Radar support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+
+MALLOC_DEFINE(M_80211_DFS, "80211dfs", "802.11 DFS state");
+
+/* XXX public for sysctl hookup */
+int ieee80211_nol_timeout = 30*60; /* 30 minutes */
+#define NOL_TIMEOUT msecs_to_ticks(ieee80211_nol_timeout*1000)
+int ieee80211_cac_timeout = 60; /* 60 seconds */
+#define CAC_TIMEOUT msecs_to_ticks(ieee80211_cac_timeout*1000)
+
+void
+ieee80211_dfs_attach(struct ieee80211com *ic)
+{
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+
+ callout_init(&dfs->nol_timer, CALLOUT_MPSAFE);
+ callout_init(&dfs->cac_timer, CALLOUT_MPSAFE);
+}
+
+void
+ieee80211_dfs_detach(struct ieee80211com *ic)
+{
+ /* NB: we assume no locking is needed */
+ ieee80211_dfs_reset(ic);
+}
+
+void
+ieee80211_dfs_reset(struct ieee80211com *ic)
+{
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ int i;
+
+ /* NB: we assume no locking is needed */
+ /* NB: cac_timer should be cleared by the state machine */
+ callout_drain(&dfs->nol_timer);
+ for (i = 0; i < ic->ic_nchans; i++)
+ ic->ic_channels[i].ic_state = 0;
+ dfs->lastchan = NULL;
+}
+
+static void
+cac_timeout(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ int i;
+
+ if (vap->iv_state != IEEE80211_S_CAC) /* NB: just in case */
+ return;
+ /*
+ * When radar is detected during a CAC we are woken
+ * up prematurely to switch to a new channel.
+ * Check the channel to decide how to act.
+ */
+ if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan)) {
+ ieee80211_notify_cac(ic, ic->ic_curchan,
+ IEEE80211_NOTIFY_CAC_RADAR);
+
+ if_printf(vap->iv_ifp,
+ "CAC timer on channel %u (%u MHz) stopped due to radar\n",
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+
+ /* XXX clobbers any existing desired channel */
+ /* NB: dfs->newchan may be NULL, that's ok */
+ vap->iv_des_chan = dfs->newchan;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ } else {
+ if_printf(vap->iv_ifp,
+ "CAC timer on channel %u (%u MHz) expired; "
+ "no radar detected\n",
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+ /*
+ * Mark all channels with the current frequency
+ * as having completed CAC; this keeps us from
+ * doing it again until we change channels.
+ */
+ for (i = 0; i < ic->ic_nchans; i++) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+ if (c->ic_freq == ic->ic_curchan->ic_freq)
+ c->ic_state |= IEEE80211_CHANSTATE_CACDONE;
+ }
+ ieee80211_notify_cac(ic, ic->ic_curchan,
+ IEEE80211_NOTIFY_CAC_EXPIRE);
+ ieee80211_cac_completeswitch(vap);
+ }
+}
+
+/*
+ * Initiate the CAC timer. The driver is responsible
+ * for setting up the hardware to scan for radar on the
+ * channnel, we just handle timing things out.
+ */
+void
+ieee80211_dfs_cac_start(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ callout_reset(&dfs->cac_timer, CAC_TIMEOUT, cac_timeout, vap);
+ if_printf(vap->iv_ifp, "start %d second CAC timer on channel %u (%u MHz)\n",
+ ticks_to_secs(CAC_TIMEOUT),
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+ ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_START);
+}
+
+/*
+ * Clear the CAC timer.
+ */
+void
+ieee80211_dfs_cac_stop(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ /* NB: racey but not important */
+ if (callout_pending(&dfs->cac_timer)) {
+ if_printf(vap->iv_ifp, "stop CAC timer on channel %u (%u MHz)\n",
+ ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
+ ieee80211_notify_cac(ic, ic->ic_curchan,
+ IEEE80211_NOTIFY_CAC_STOP);
+ }
+ /* XXX cannot use drain 'cuz holding a lock */
+ callout_stop(&dfs->cac_timer);
+}
+
+void
+ieee80211_dfs_cac_clear(struct ieee80211com *ic,
+ const struct ieee80211_channel *chan)
+{
+ int i;
+
+ for (i = 0; i < ic->ic_nchans; i++) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+ if (c->ic_freq == chan->ic_freq)
+ c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
+ }
+}
+
+static void
+dfs_timeout(void *arg)
+{
+ struct ieee80211com *ic = arg;
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ struct ieee80211_channel *c;
+ int i, oldest, now;
+
+ IEEE80211_LOCK(ic);
+ now = oldest = ticks;
+ for (i = 0; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (IEEE80211_IS_CHAN_RADAR(c)) {
+ if (time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) {
+ c->ic_state &= ~IEEE80211_CHANSTATE_RADAR;
+ if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) {
+ /*
+ * NB: do this here so we get only one
+ * msg instead of one for every channel
+ * table entry.
+ */
+ if_printf(ic->ic_ifp, "radar on channel"
+ " %u (%u MHz) cleared after timeout\n",
+ c->ic_ieee, c->ic_freq);
+ /* notify user space */
+ c->ic_state &=
+ ~IEEE80211_CHANSTATE_NORADAR;
+ ieee80211_notify_radar(ic, c);
+ }
+ } else if (dfs->nol_event[i] < oldest)
+ oldest = dfs->nol_event[i];
+ }
+ }
+ if (oldest != now) {
+ /* arrange to process next channel up for a status change */
+ callout_reset(&dfs->nol_timer, oldest + NOL_TIMEOUT,
+ dfs_timeout, ic);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+announce_radar(struct ifnet *ifp, const struct ieee80211_channel *curchan,
+ const struct ieee80211_channel *newchan)
+{
+ if (newchan == NULL)
+ if_printf(ifp, "radar detected on channel %u (%u MHz)\n",
+ curchan->ic_ieee, curchan->ic_freq);
+ else
+ if_printf(ifp, "radar detected on channel %u (%u MHz), "
+ "moving to channel %u (%u MHz)\n",
+ curchan->ic_ieee, curchan->ic_freq,
+ newchan->ic_ieee, newchan->ic_freq);
+}
+
+/*
+ * Handle a radar detection event on a channel. The channel is
+ * added to the NOL list and we record the time of the event.
+ * Entries are aged out after NOL_TIMEOUT. If radar was
+ * detected while doing CAC we force a state/channel change.
+ * Otherwise radar triggers a channel switch using the CSA
+ * mechanism (when the channel is the bss channel).
+ */
+void
+ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *chan)
+{
+ struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
+ int i, now;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ /*
+ * Mark all entries with this frequency. Notify user
+ * space and arrange for notification when the radar
+ * indication is cleared. Then kick the NOL processing
+ * thread if not already running.
+ */
+ now = ticks;
+ for (i = 0; i < ic->ic_nchans; i++) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+ if (c->ic_freq == chan->ic_freq) {
+ c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
+ c->ic_state |= IEEE80211_CHANSTATE_RADAR;
+ dfs->nol_event[i] = now;
+ }
+ }
+ ieee80211_notify_radar(ic, chan);
+ chan->ic_state |= IEEE80211_CHANSTATE_NORADAR;
+ if (!callout_pending(&dfs->nol_timer))
+ callout_reset(&dfs->nol_timer, NOL_TIMEOUT, dfs_timeout, ic);
+
+ /*
+ * If radar is detected on the bss channel while
+ * doing CAC; force a state change by scheduling the
+ * callout to be dispatched asap. Otherwise, if this
+ * event is for the bss channel then we must quiet
+ * traffic and schedule a channel switch.
+ *
+ * Note this allows us to receive notification about
+ * channels other than the bss channel; not sure
+ * that can/will happen but it's simple to support.
+ */
+ if (chan == ic->ic_bsschan) {
+ /* XXX need a way to defer to user app */
+ dfs->newchan = ieee80211_dfs_pickchannel(ic);
+
+ announce_radar(ic->ic_ifp, chan, dfs->newchan);
+
+ if (callout_pending(&dfs->cac_timer))
+ callout_reset(&dfs->nol_timer, 0, dfs_timeout, ic);
+ else if (dfs->newchan != NULL) {
+ /* XXX mode 1, switch count 2 */
+ /* XXX calculate switch count based on max
+ switch time and beacon interval? */
+ ieee80211_csa_startswitch(ic, dfs->newchan, 1, 2);
+ } else {
+ /*
+ * Spec says to stop all transmissions and
+ * wait on the current channel for an entry
+ * on the NOL to expire.
+ */
+ /*XXX*/
+ }
+ } else {
+ /*
+ * Issue rate-limited console msgs.
+ */
+ if (dfs->lastchan != chan) {
+ dfs->lastchan = chan;
+ dfs->cureps = 0;
+ announce_radar(ic->ic_ifp, chan, NULL);
+ } else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) {
+ announce_radar(ic->ic_ifp, chan, NULL);
+ }
+ }
+}
+
+struct ieee80211_channel *
+ieee80211_dfs_pickchannel(struct ieee80211com *ic)
+{
+ struct ieee80211_channel *c;
+ int i, flags;
+ uint16_t v;
+
+ /*
+ * Consult the scan cache first.
+ */
+ flags = ic->ic_curchan->ic_flags & IEEE80211_CHAN_ALL;
+ /*
+ * XXX if curchan is HT this will never find a channel
+ * XXX 'cuz we scan only legacy channels
+ */
+ c = ieee80211_scan_pickchannel(ic, flags);
+ if (c != NULL)
+ return c;
+ /*
+ * No channel found in scan cache; select a compatible
+ * one at random (skipping channels where radar has
+ * been detected).
+ */
+ get_random_bytes(&v, sizeof(v));
+ v %= ic->ic_nchans;
+ for (i = v; i < ic->ic_nchans; i++) {
+ c = &ic->ic_channels[i];
+ if (!IEEE80211_IS_CHAN_RADAR(c) &&
+ (c->ic_flags & flags) == flags)
+ return c;
+ }
+ for (i = 0; i < v; i++) {
+ c = &ic->ic_channels[i];
+ if (!IEEE80211_IS_CHAN_RADAR(c) &&
+ (c->ic_flags & flags) == flags)
+ return c;
+ }
+ if_printf(ic->ic_ifp, "HELP, no channel located to switch to!\n");
+ return NULL;
+}
diff --git a/sys/net80211/ieee80211_dfs.h b/sys/net80211/ieee80211_dfs.h
new file mode 100644
index 0000000..36d14aa
--- /dev/null
+++ b/sys/net80211/ieee80211_dfs.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_DFS_H_
+#define _NET80211_IEEE80211_DFS_H_
+
+/*
+ * 802.11h/DFS definitions.
+ */
+
+struct ieee80211_dfs_state {
+ int nol_event[IEEE80211_CHAN_MAX+1];
+ struct callout nol_timer; /* NOL list processing */
+ struct callout cac_timer; /* CAC timer */
+ struct timeval lastevent; /* time of last radar event */
+ int cureps; /* current events/second */
+ const struct ieee80211_channel *lastchan;/* chan w/ last radar event */
+ struct ieee80211_channel *newchan; /* chan selected next */
+};
+
+void ieee80211_dfs_attach(struct ieee80211com *);
+void ieee80211_dfs_detach(struct ieee80211com *);
+
+void ieee80211_dfs_reset(struct ieee80211com *);
+
+void ieee80211_dfs_cac_start(struct ieee80211vap *);
+void ieee80211_dfs_cac_stop(struct ieee80211vap *);
+void ieee80211_dfs_cac_clear(struct ieee80211com *,
+ const struct ieee80211_channel *);
+
+void ieee80211_dfs_notify_radar(struct ieee80211com *,
+ struct ieee80211_channel *);
+struct ieee80211_channel *ieee80211_dfs_pickchannel(struct ieee80211com *);
+#endif /* _NET80211_IEEE80211_DFS_H_ */
diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c
index 1cb7813..d0b1b69 100644
--- a/sys/net80211/ieee80211_freebsd.c
+++ b/sys/net80211/ieee80211_freebsd.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 support (FreeBSD-specific code)
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -41,7 +43,9 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
+#include <net/if_clone.h>
#include <net/if_media.h>
+#include <net/if_types.h>
#include <net/ethernet.h>
#include <net/route.h>
@@ -57,24 +61,112 @@ SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
extern int ieee80211_recv_bar_ena;
SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena,
0, "BAR frame processing (ena/dis)");
+extern int ieee80211_nol_timeout;
+SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW,
+ &ieee80211_nol_timeout, 0, "NOL timeout (secs)");
+extern int ieee80211_cac_timeout;
+SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW,
+ &ieee80211_cac_timeout, 0, "CAC timeout (secs)");
+
+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);
+}
-#ifdef IEEE80211_AMPDU_AGE
static int
-ieee80211_sysctl_ampdu_age(SYSCTL_HANDLER_ARGS)
+wlan_clone_create(struct if_clone *ifc, int unit, caddr_t params)
{
- extern int ieee80211_ampdu_age;
- int ampdu_age = ticks_to_msecs(ieee80211_ampdu_age);
+ struct ieee80211_clone_params cp;
+ struct ieee80211vap *vap;
+ struct ieee80211com *ic;
+ struct ifnet *ifp;
int error;
- error = sysctl_handle_int(oidp, &ampdu_age, 0, req);
+ error = copyin(params, &cp, sizeof(cp));
+ if (error)
+ return error;
+ ifp = ifunit(cp.icp_parent);
+ if (ifp == NULL)
+ return ENXIO;
+ if (ifp->if_type != IFT_IEEE80211) {
+ if_printf(ifp, "%s: reject, not an 802.11 device\n", __func__);
+ return EINVAL;
+ }
+ ic = ifp->if_l2com;
+ vap = ic->ic_vap_create(ic, ifc->ifc_name, unit,
+ cp.icp_opmode, cp.icp_flags, cp.icp_bssid,
+ cp.icp_flags & IEEE80211_CLONE_MACADDR ?
+ cp.icp_macaddr : ic->ic_myaddr);
+ return (vap == NULL ? EIO : 0);
+}
+
+static void
+wlan_clone_destroy(struct ifnet *ifp)
+{
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ ic->ic_vap_delete(vap);
+}
+IFC_SIMPLE_DECLARE(wlan, 0);
+
+void
+ieee80211_vap_destroy(struct ieee80211vap *vap)
+{
+ ifc_simple_destroy(&wlan_cloner, vap->iv_ifp);
+}
+
+static int
+ieee80211_sysctl_msecs_ticks(SYSCTL_HANDLER_ARGS)
+{
+ int msecs = ticks_to_msecs(*(int *)arg1);
+ int error, t;
+
+ error = sysctl_handle_int(oidp, &msecs, 0, req);
if (error || !req->newptr)
return error;
- ieee80211_ampdu_age = msecs_to_ticks(ampdu_age);
+ t = msecs_to_ticks(msecs);
+ *(int *)arg1 = (t < 1) ? 1 : t;
return 0;
}
-SYSCTL_PROC(_net_wlan, OID_AUTO, "ampdu_age", CTLFLAG_RW, NULL, 0,
- ieee80211_sysctl_ampdu_age, "A", "AMPDU max reorder age (ms)");
+
+#ifdef IEEE80211_AMPDU_AGE
+extern int ieee80211_ampdu_age;
+SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLFLAG_RW,
+ &ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "AMPDU max reorder age (ms)");
#endif
+extern int ieee80211_addba_timeout;
+SYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLFLAG_RW,
+ &ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "ADDBA request timeout (ms)");
+extern int ieee80211_addba_backoff;
+SYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLFLAG_RW,
+ &ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I",
+ "ADDBA request backoff (ms)");
+extern int ieee80211_addba_maxtries;
+SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW,
+ &ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff");
static int
ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
@@ -101,6 +193,17 @@ ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS)
void
ieee80211_sysctl_attach(struct ieee80211com *ic)
{
+}
+
+void
+ieee80211_sysctl_detach(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_sysctl_vattach(struct ieee80211vap *vap)
+{
+ struct ifnet *ifp = vap->iv_ifp;
struct sysctl_ctx_list *ctx;
struct sysctl_oid *oid;
char num[14]; /* sufficient for 32 bits */
@@ -108,57 +211,76 @@ ieee80211_sysctl_attach(struct ieee80211com *ic)
MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list),
M_DEVBUF, M_NOWAIT | M_ZERO);
if (ctx == NULL) {
- if_printf(ic->ic_ifp, "%s: cannot allocate sysctl context!\n",
+ if_printf(ifp, "%s: cannot allocate sysctl context!\n",
__func__);
return;
}
sysctl_ctx_init(ctx);
- snprintf(num, sizeof(num), "%u", ic->ic_vap);
+ snprintf(num, sizeof(num), "%u", ifp->if_dunit);
oid = SYSCTL_ADD_NODE(ctx, &SYSCTL_NODE_CHILDREN(_net, wlan),
OID_AUTO, num, CTLFLAG_RD, NULL, "");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A",
- "parent device");
+ "%parent", CTLFLAG_RD, vap->iv_ic, 0,
+ ieee80211_sysctl_parent, "A", "parent device");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "driver_caps", CTLFLAG_RW, &vap->iv_caps, 0,
+ "driver capabilities");
#ifdef IEEE80211_DEBUG
- ic->ic_debug = ieee80211_debug;
+ vap->iv_debug = ieee80211_debug;
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "debug", CTLFLAG_RW, &ic->ic_debug, 0,
+ "debug", CTLFLAG_RW, &vap->iv_debug, 0,
"control debugging printfs");
#endif
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "bmiss_max", CTLFLAG_RW, &vap->iv_bmiss_max, 0,
+ "consecutive beacon misses before scanning");
/* XXX inherit from tunables */
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_run", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0,
+ "inact_run", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_run, 0,
ieee80211_sysctl_inact, "I",
"station inactivity timeout (sec)");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0,
+ "inact_probe", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_probe, 0,
ieee80211_sysctl_inact, "I",
"station inactivity probe timeout (sec)");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0,
+ "inact_auth", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_auth, 0,
ieee80211_sysctl_inact, "I",
"station authentication timeout (sec)");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0,
+ "inact_init", CTLTYPE_INT | CTLFLAG_RW, &vap->iv_inact_init, 0,
ieee80211_sysctl_inact, "I",
"station initial state timeout (sec)");
- SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "driver_caps", CTLFLAG_RW, &ic->ic_caps, 0,
- "driver capabilities");
- SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
- "bmiss_max", CTLFLAG_RW, &ic->ic_bmiss_max, 0,
- "consecutive beacon misses before scanning");
- ic->ic_sysctl = ctx;
+ if (vap->iv_htcaps & IEEE80211_HTC_HT) {
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_bk", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_BK], 0,
+ "BK traffic tx aggr threshold (pps)");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_be", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_BE], 0,
+ "BE traffic tx aggr threshold (pps)");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_vo", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_VO], 0,
+ "VO traffic tx aggr threshold (pps)");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "ampdu_mintraffic_vi", CTLFLAG_RW,
+ &vap->iv_ampdu_mintraffic[WME_AC_VI], 0,
+ "VI traffic tx aggr threshold (pps)");
+ }
+ vap->iv_sysctl = ctx;
+ vap->iv_oid = oid;
}
void
-ieee80211_sysctl_detach(struct ieee80211com *ic)
+ieee80211_sysctl_vdetach(struct ieee80211vap *vap)
{
- if (ic->ic_sysctl != NULL) {
- sysctl_ctx_free(ic->ic_sysctl);
- FREE(ic->ic_sysctl, M_DEVBUF);
- ic->ic_sysctl = NULL;
+ if (vap->iv_sysctl != NULL) {
+ sysctl_ctx_free(vap->iv_sysctl);
+ FREE(vap->iv_sysctl, M_DEVBUF);
+ vap->iv_sysctl = NULL;
}
}
@@ -190,6 +312,33 @@ ieee80211_drain_ifq(struct ifqueue *ifq)
}
}
+void
+ieee80211_flush_ifq(struct ifqueue *ifq, struct ieee80211vap *vap)
+{
+ struct ieee80211_node *ni;
+ struct mbuf *m, **mprev;
+
+ IF_LOCK(ifq);
+ mprev = &ifq->ifq_head;
+ while ((m = *mprev) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ if (ni != NULL && ni->ni_vap == vap) {
+ *mprev = m->m_nextpkt; /* remove from list */
+ ifq->ifq_len--;
+
+ m_freem(m);
+ ieee80211_free_node(ni); /* reclaim ref */
+ } else
+ mprev = &m->m_nextpkt;
+ }
+ /* recalculate tail ptr */
+ m = ifq->ifq_head;
+ for (; m != NULL && m->m_nextpkt != NULL; m = m->m_nextpkt)
+ ;
+ ifq->ifq_tail = m;
+ IF_UNLOCK(ifq);
+}
+
/*
* As above, for mbufs allocated with m_gethdr/MGETHDR
* or initialized by M_COPY_PKTHDR.
@@ -290,66 +439,78 @@ get_random_bytes(void *p, size_t n)
}
}
-void
-ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc)
+/*
+ * Helper function for events that pass just a single mac address.
+ */
+static void
+notify_macaddr(struct ifnet *ifp, int op, const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_join_event iev;
memset(&iev, 0, sizeof(iev));
- if (ni == ic->ic_bss) {
- IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid);
- rt_ieee80211msg(ifp, newassoc ?
- RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC,
- &iev, sizeof(iev));
+ IEEE80211_ADDR_COPY(iev.iev_addr, mac);
+ rt_ieee80211msg(ifp, op, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode join",
+ (ni == vap->iv_bss) ? "bss " : "");
+
+ if (ni == vap->iv_bss) {
+ notify_macaddr(ifp, newassoc ?
+ RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, ni->ni_bssid);
if_link_state_change(ifp, LINK_STATE_UP);
} else {
- IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
- rt_ieee80211msg(ifp, newassoc ?
- RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN,
- &iev, sizeof(iev));
+ notify_macaddr(ifp, newassoc ?
+ RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, ni->ni_macaddr);
}
}
void
-ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_notify_node_leave(struct ieee80211_node *ni)
{
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_leave_event iev;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
- if (ni == ic->ic_bss) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode leave",
+ (ni == vap->iv_bss) ? "bss " : "");
+
+ if (ni == vap->iv_bss) {
rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0);
if_link_state_change(ifp, LINK_STATE_DOWN);
} else {
/* fire off wireless event station leaving */
- memset(&iev, 0, sizeof(iev));
- IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
- rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev));
+ notify_macaddr(ifp, RTM_IEEE80211_LEAVE, ni->ni_macaddr);
}
}
void
-ieee80211_notify_scan_done(struct ieee80211com *ic)
+ieee80211_notify_scan_done(struct ieee80211vap *vap)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done");
/* dispatch wireless event indicating scan completed */
rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
}
void
-ieee80211_notify_replay_failure(struct ieee80211com *ic,
+ieee80211_notify_replay_failure(struct ieee80211vap *vap,
const struct ieee80211_frame *wh, const struct ieee80211_key *k,
u_int64_t rsc)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>\n",
- ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
- (intmax_t) rsc, (intmax_t) k->wk_keyrsc,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "%s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>",
+ k->wk_cipher->ic_name, (intmax_t) rsc,
+ (intmax_t) k->wk_keyrsc[IEEE80211_NONQOS_TID],
k->wk_keyix, k->wk_rxkeyix);
if (ifp != NULL) { /* NB: for cipher test modules */
@@ -362,22 +523,21 @@ ieee80211_notify_replay_failure(struct ieee80211com *ic,
iev.iev_keyix = k->wk_rxkeyix;
else
iev.iev_keyix = k->wk_keyix;
- iev.iev_keyrsc = k->wk_keyrsc;
+ iev.iev_keyrsc = k->wk_keyrsc[0]; /* XXX need tid */
iev.iev_rsc = rsc;
rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
}
}
void
-ieee80211_notify_michael_failure(struct ieee80211com *ic,
+ieee80211_notify_michael_failure(struct ieee80211vap *vap,
const struct ieee80211_frame *wh, u_int keyix)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] michael MIC verification failed <keyix %u>\n",
- ether_sprintf(wh->i_addr2), keyix);
- ic->ic_stats.is_rx_tkipmic++;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
+ "michael MIC verification failed <keyix %u>", keyix);
+ vap->iv_stats.is_rx_tkipmic++;
if (ifp != NULL) { /* NB: for cipher test modules */
struct ieee80211_michael_event iev;
@@ -391,6 +551,107 @@ ieee80211_notify_michael_failure(struct ieee80211com *ic,
}
void
+ieee80211_notify_wds_discover(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ notify_macaddr(ifp, RTM_IEEE80211_WDS, ni->ni_macaddr);
+}
+
+void
+ieee80211_notify_csa(struct ieee80211com *ic,
+ const struct ieee80211_channel *c, int mode, int count)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_csa_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_flags = c->ic_flags;
+ iev.iev_freq = c->ic_freq;
+ iev.iev_ieee = c->ic_ieee;
+ iev.iev_mode = mode;
+ iev.iev_count = count;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_CSA, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_radar(struct ieee80211com *ic,
+ const struct ieee80211_channel *c)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_radar_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_flags = c->ic_flags;
+ iev.iev_freq = c->ic_freq;
+ iev.iev_ieee = c->ic_ieee;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_RADAR, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_cac(struct ieee80211com *ic,
+ const struct ieee80211_channel *c, enum ieee80211_notify_cac_event type)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_cac_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_flags = c->ic_flags;
+ iev.iev_freq = c->ic_freq;
+ iev.iev_ieee = c->ic_ieee;
+ iev.iev_type = type;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_CAC, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_node_deauth(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node deauth");
+
+ notify_macaddr(ifp, RTM_IEEE80211_DEAUTH, ni->ni_macaddr);
+}
+
+void
+ieee80211_notify_node_auth(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node auth");
+
+ notify_macaddr(ifp, RTM_IEEE80211_AUTH, ni->ni_macaddr);
+}
+
+void
+ieee80211_notify_country(struct ieee80211vap *vap,
+ const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t cc[2])
+{
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_country_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ IEEE80211_ADDR_COPY(iev.iev_addr, bssid);
+ iev.iev_cc[0] = cc[0];
+ iev.iev_cc[1] = cc[1];
+ rt_ieee80211msg(ifp, RTM_IEEE80211_COUNTRY, &iev, sizeof(iev));
+}
+
+void
+ieee80211_notify_radio(struct ieee80211com *ic, int state)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_radio_event iev;
+
+ memset(&iev, 0, sizeof(iev));
+ iev.iev_state = state;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_RADIO, &iev, sizeof(iev));
+}
+
+void
ieee80211_load_module(const char *modname)
{
@@ -413,8 +674,12 @@ wlan_modevent(module_t mod, int type, void *unused)
case MOD_LOAD:
if (bootverbose)
printf("wlan: <802.11 Link Layer>\n");
+ if_clone_attach(&wlan_cloner);
+ if_register_com_alloc(IFT_IEEE80211, wlan_alloc, wlan_free);
return 0;
case MOD_UNLOAD:
+ if_deregister_com_alloc(IFT_IEEE80211);
+ if_clone_detach(&wlan_cloner);
return 0;
}
return EINVAL;
diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h
index 0052dfe..bccc0cf 100644
--- a/sys/net80211/ieee80211_freebsd.h
+++ b/sys/net80211/ieee80211_freebsd.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2003-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,12 +28,18 @@
#define _NET80211_IEEE80211_FREEBSD_H_
#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
+
/*
* Common state locking definitions.
*/
typedef struct mtx ieee80211_com_lock_t;
#define IEEE80211_LOCK_INIT(_ic, _name) \
- mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", MTX_DEF)
+ mtx_init(&(_ic)->ic_comlock, _name, "802.11 com lock", \
+ MTX_DEF | MTX_RECURSE)
#define IEEE80211_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_comlock)
#define IEEE80211_LOCK(_ic) mtx_lock(&(_ic)->ic_comlock)
#define IEEE80211_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_comlock)
@@ -41,43 +47,61 @@ typedef struct mtx ieee80211_com_lock_t;
mtx_assert(&(_ic)->ic_comlock, MA_OWNED)
/*
- * Beacon locking definitions.
- */
-typedef struct mtx ieee80211_beacon_lock_t;
-#define IEEE80211_BEACON_LOCK_INIT(_ic, _name) \
- mtx_init(&(_ic)->ic_beaconlock, _name, "802.11 beacon lock", MTX_DEF)
-#define IEEE80211_BEACON_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_beaconlock)
-#define IEEE80211_BEACON_LOCK(_ic) mtx_lock(&(_ic)->ic_beaconlock)
-#define IEEE80211_BEACON_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_beaconlock)
-#define IEEE80211_BEACON_LOCK_ASSERT(_ic) \
- mtx_assert(&(_ic)->ic_beaconlock, MA_OWNED)
-
-/*
* Node locking definitions.
- * NB: MTX_DUPOK is because we don't generate per-interface strings.
*/
-typedef struct mtx ieee80211_node_lock_t;
-#define IEEE80211_NODE_LOCK_INIT(_nt, _name) \
- mtx_init(&(_nt)->nt_nodelock, _name, "802.11 node table", \
- MTX_DEF | MTX_DUPOK)
-#define IEEE80211_NODE_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_LOCK(_nt) mtx_lock(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_IS_LOCKED(_nt) mtx_owned(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_UNLOCK(_nt) mtx_unlock(&(_nt)->nt_nodelock)
-#define IEEE80211_NODE_LOCK_ASSERT(_nt) \
- mtx_assert(&(_nt)->nt_nodelock, MA_OWNED)
+typedef struct {
+ char name[16]; /* e.g. "ath0_node_lock" */
+ struct mtx mtx;
+} ieee80211_node_lock_t;
+#define IEEE80211_NODE_LOCK_INIT(_nt, _name) do { \
+ ieee80211_node_lock_t *nl = &(_nt)->nt_nodelock; \
+ snprintf(nl->name, sizeof(nl->name), "%s_node_lock", _name); \
+ mtx_init(&nl->mtx, NULL, nl->name, MTX_DEF | MTX_RECURSE); \
+} while (0)
+#define IEEE80211_NODE_LOCK_DESTROY(_nt) \
+ mtx_destroy(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_LOCK(_nt) \
+ mtx_lock(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_IS_LOCKED(_nt) \
+ mtx_owned(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_UNLOCK(_nt) \
+ mtx_unlock(&(_nt)->nt_nodelock.mtx)
+#define IEEE80211_NODE_LOCK_ASSERT(_nt) \
+ mtx_assert(&(_nt)->nt_nodelock.mtx, MA_OWNED)
/*
- * Node table scangen locking definitions.
+ * 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 mtx ieee80211_scan_lock_t;
-#define IEEE80211_SCAN_LOCK_INIT(_nt, _name) \
- mtx_init(&(_nt)->nt_scanlock, _name, "802.11 node scangen", MTX_DEF)
-#define IEEE80211_SCAN_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_scanlock)
-#define IEEE80211_SCAN_LOCK(_nt) mtx_lock(&(_nt)->nt_scanlock)
-#define IEEE80211_SCAN_UNLOCK(_nt) mtx_unlock(&(_nt)->nt_scanlock)
-#define IEEE80211_SCAN_LOCK_ASSERT(_nt) \
- mtx_assert(&(_nt)->nt_scanlock, MA_OWNED)
+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, NULL, sl->name, MTX_DEF); \
+} while (0)
+#define IEEE80211_NODE_ITERATE_LOCK_DESTROY(_nt) \
+ mtx_destroy(&(_nt)->nt_scanlock.mtx)
+#define IEEE80211_NODE_ITERATE_LOCK(_nt) \
+ mtx_lock(&(_nt)->nt_scanlock.mtx)
+#define IEEE80211_NODE_ITERATE_UNLOCK(_nt) \
+ mtx_unlock(&(_nt)->nt_scanlock.mtx)
+
+#define _AGEQ_ENQUEUE(_ifq, _m, _qlen, _age) do { \
+ (_m)->m_nextpkt = NULL; \
+ if ((_ifq)->ifq_tail != NULL) { \
+ _age -= M_AGE_GET((_ifq)->ifq_tail); \
+ (_ifq)->ifq_tail->m_nextpkt = (_m); \
+ } else { \
+ (_ifq)->ifq_head = (_m); \
+ } \
+ M_AGE_SET(_m, _age); \
+ (_ifq)->ifq_tail = (_m); \
+ (_qlen) = ++(_ifq)->ifq_len; \
+} while (0)
/*
* Per-node power-save queue definitions.
@@ -113,16 +137,7 @@ typedef struct mtx ieee80211_scan_lock_t;
_IF_DEQUEUE(&(_ni)->ni_savedq, m); \
} while (0)
#define _IEEE80211_NODE_SAVEQ_ENQUEUE(_ni, _m, _qlen, _age) do {\
- (_m)->m_nextpkt = NULL; \
- if ((_ni)->ni_savedq.ifq_tail != NULL) { \
- _age -= M_AGE_GET((_ni)->ni_savedq.ifq_tail); \
- (_ni)->ni_savedq.ifq_tail->m_nextpkt = (_m); \
- } else { \
- (_ni)->ni_savedq.ifq_head = (_m); \
- } \
- M_AGE_SET(_m, _age); \
- (_ni)->ni_savedq.ifq_tail = (_m); \
- (_qlen) = ++(_ni)->ni_savedq.ifq_len; \
+ _AGEQ_ENQUEUE(&ni->ni_savedq, _m, _qlen, _age); \
} while (0)
#define IEEE80211_TAPQ_INIT(_tap) do { \
@@ -147,6 +162,24 @@ typedef struct mtx ieee80211_scan_lock_t;
} while (0)
#endif /* IF_PREPEND_LIST */
+/* XXX temporary */
+#define IEEE80211_NODE_WDSQ_INIT(_ni, _name) do { \
+ mtx_init(&(_ni)->ni_wdsq.ifq_mtx, _name, "802.11 wds queue", MTX_DEF);\
+ (_ni)->ni_wdsq.ifq_maxlen = IEEE80211_PS_MAX_QUEUE; \
+} while (0)
+#define IEEE80211_NODE_WDSQ_DESTROY(_ni) do { \
+ mtx_destroy(&(_ni)->ni_wdsq.ifq_mtx); \
+} while (0)
+#define IEEE80211_NODE_WDSQ_QLEN(_ni) _IF_QLEN(&(_ni)->ni_wdsq)
+#define IEEE80211_NODE_WDSQ_LOCK(_ni) IF_LOCK(&(_ni)->ni_wdsq)
+#define IEEE80211_NODE_WDSQ_UNLOCK(_ni) IF_UNLOCK(&(_ni)->ni_wdsq)
+#define _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(_ni, _m) do { \
+ _IF_DEQUEUE(&(_ni)->ni_wdsq, m); \
+} while (0)
+#define _IEEE80211_NODE_WDSQ_ENQUEUE(_ni, _m, _qlen, _age) do { \
+ _AGEQ_ENQUEUE(&ni->ni_wdsq, _m, _qlen, _age); \
+} while (0)
+
/*
* 802.1x MAC ACL database locking definitions.
*/
@@ -182,43 +215,53 @@ int ieee80211_node_dectestref(struct ieee80211_node *ni);
#define ieee80211_node_refcnt(_ni) (_ni)->ni_refcnt
struct ifqueue;
+struct ieee80211vap;
void ieee80211_drain_ifq(struct ifqueue *);
+void ieee80211_flush_ifq(struct ifqueue *, struct ieee80211vap *);
+
+void ieee80211_vap_destroy(struct ieee80211vap *);
+
+#define IFNET_IS_UP_RUNNING(_ifp) \
+ (((_ifp)->if_flags & IFF_UP) && \
+ ((_ifp)->if_drv_flags & IFF_DRV_RUNNING))
#define msecs_to_ticks(ms) (((ms)*hz)/1000)
-#define ticks_to_msecs(t) ((t) / hz)
+#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 memmove(dst, src, n) ovbcopy(src, dst, n)
+
struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen);
/* tx path usage */
#define M_LINK0 M_PROTO1 /* WEP requested */
+#define M_WDS M_PROTO2 /* WDS frame */
+#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 0x20000 /* fast frame */
-#define M_TXCB 0x40000 /* do tx complete callback */
-#define M_80211_TX (0x60000|M_PROTO1|M_WME_AC_MASK|M_PROTO4|M_PROTO5)
+#define M_FF M_PROTO6 /* fast frame */
+#define M_TXCB M_PROTO7 /* do tx complete callback */
+#define M_80211_TX \
+ (M_LINK0|M_WDS|M_EAPOL|M_PWR_SAV|M_MORE_DATA|M_FF|M_TXCB)
/* rx path usage */
#define M_AMPDU M_PROTO1 /* A-MPDU processing done */
#define M_WEP M_PROTO2 /* WEP done by hardware */
#define M_80211_RX (M_AMPDU|M_WEP)
/*
- * Encode WME access control bits in the PROTO flags.
- * This is safe since it's passed directly in to the
- * driver and there's no chance someone else will clobber
- * them on us.
+ * Store WME access control bits in the vlan tag.
+ * This is safe since it's done after the packet is classified
+ * (where we use any previous tag) and because it's passed
+ * directly in to the driver and there's no chance someone
+ * else will clobber them on us.
*/
-#define M_WME_AC_MASK (M_PROTO2|M_PROTO3)
-/* XXX 5 is wrong if M_PROTO* are redefined */
-#define M_WME_AC_SHIFT 5
-
#define M_WME_SETAC(m, ac) \
- ((m)->m_flags = ((m)->m_flags &~ M_WME_AC_MASK) | \
- ((ac) << M_WME_AC_SHIFT))
-#define M_WME_GETAC(m) (((m)->m_flags >> M_WME_AC_SHIFT) & 0x3)
+ ((m)->m_pkthdr.ether_vtag = (ac))
+#define M_WME_GETAC(m) ((m)->m_pkthdr.ether_vtag)
/*
* Mbufs on the power save queue are tagged with an age and
@@ -246,16 +289,29 @@ struct ieee80211com;
void ieee80211_sysctl_attach(struct ieee80211com *);
void ieee80211_sysctl_detach(struct ieee80211com *);
+void ieee80211_sysctl_vattach(struct ieee80211vap *);
+void ieee80211_sysctl_vdetach(struct ieee80211vap *);
void ieee80211_load_module(const char *);
-#define IEEE80211_CRYPTO_MODULE(name, version) \
+/*
+ * A "policy module" is an adjunct module to net80211 that provides
+ * functionality that typically includes policy decisions. This
+ * modularity enables extensibility and vendor-supplied functionality.
+ */
+#define _IEEE80211_POLICY_MODULE(policy, name, version) \
+typedef void (*policy##_setup)(int); \
+SET_DECLARE(policy##_set, policy##_setup); \
static int \
-name##_modevent(module_t mod, int type, void *unused) \
+wlan_##name##_modevent(module_t mod, int type, void *unused) \
{ \
+ policy##_setup * const *iter, f; \
switch (type) { \
case MOD_LOAD: \
- ieee80211_crypto_register(&name); \
+ SET_FOREACH(iter, policy##_set) { \
+ f = (void*) *iter; \
+ f(type); \
+ } \
return 0; \
case MOD_UNLOAD: \
case MOD_QUIESCE: \
@@ -264,20 +320,100 @@ name##_modevent(module_t mod, int type, void *unused) \
nrefs); \
return EBUSY; \
} \
- if (type == MOD_UNLOAD) \
- ieee80211_crypto_unregister(&name); \
+ if (type == MOD_UNLOAD) { \
+ SET_FOREACH(iter, policy##_set) { \
+ f = (void*) *iter; \
+ f(type); \
+ } \
+ } \
return 0; \
} \
return EINVAL; \
} \
static moduledata_t name##_mod = { \
"wlan_" #name, \
- name##_modevent, \
+ wlan_##name##_modevent, \
0 \
}; \
DECLARE_MODULE(wlan_##name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\
MODULE_VERSION(wlan_##name, version); \
MODULE_DEPEND(wlan_##name, wlan, 1, 1, 1)
+
+/*
+ * Crypto modules implement cipher support.
+ */
+#define IEEE80211_CRYPTO_MODULE(name, version) \
+_IEEE80211_POLICY_MODULE(crypto, name, version); \
+static void \
+name##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_crypto_register(&name); \
+ else \
+ ieee80211_crypto_unregister(&name); \
+} \
+TEXT_SET(crypto##_set, name##_modevent)
+
+/*
+ * Scanner modules provide scanning policy.
+ */
+#define IEEE80211_SCANNER_MODULE(name, version) \
+ _IEEE80211_POLICY_MODULE(scanner, name, version)
+
+#define IEEE80211_SCANNER_ALG(name, alg, v) \
+static void \
+name##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_scanner_register(alg, &v); \
+ else \
+ ieee80211_scanner_unregister(alg, &v); \
+} \
+TEXT_SET(scanner_set, name##_modevent); \
+
+/*
+ * ACL modules implement acl policy.
+ */
+#define IEEE80211_ACL_MODULE(name, alg, version) \
+_IEEE80211_POLICY_MODULE(acl, name, version); \
+static void \
+alg##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_aclator_register(&alg); \
+ else \
+ ieee80211_aclator_unregister(&alg); \
+} \
+TEXT_SET(acl_set, alg##_modevent); \
+
+/*
+ * Authenticator modules handle 802.1x/WPA authentication.
+ */
+#define IEEE80211_AUTH_MODULE(name, version) \
+ _IEEE80211_POLICY_MODULE(auth, name, version)
+
+#define IEEE80211_AUTH_ALG(name, alg, v) \
+static void \
+name##_modevent(int type) \
+{ \
+ if (type == MOD_LOAD) \
+ ieee80211_authenticator_register(alg, &v); \
+ else \
+ ieee80211_authenticator_unregister(alg); \
+} \
+TEXT_SET(auth_set, name##_modevent)
+
+/*
+ * Rate control modules provide tx rate control support.
+ */
+#define IEEE80211_RATE_MODULE(alg, version) \
+_IEEE80211_POLICY_MODULE(rate, alg, version); \
+static void \
+alg##_modevent(int type) \
+{ \
+ /* XXX nothing to do until the rate control framework arrives */\
+} \
+TEXT_SET(rate##_set, alg##_modevent)
#endif /* _KERNEL */
/* XXX this stuff belongs elsewhere */
@@ -310,6 +446,50 @@ struct ieee80211_michael_event {
uint8_t iev_keyix; /* key id/index */
};
+struct ieee80211_wds_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_csa_event {
+ uint32_t iev_flags; /* channel flags */
+ uint16_t iev_freq; /* setting in Mhz */
+ uint8_t iev_ieee; /* IEEE channel number */
+ uint8_t iev_mode; /* CSA mode */
+ uint8_t iev_count; /* CSA count */
+};
+
+struct ieee80211_cac_event {
+ uint32_t iev_flags; /* channel flags */
+ uint16_t iev_freq; /* setting in Mhz */
+ uint8_t iev_ieee; /* IEEE channel number */
+ /* XXX timestamp? */
+ uint8_t iev_type; /* IEEE80211_NOTIFY_CAC_* */
+};
+
+struct ieee80211_radar_event {
+ uint32_t iev_flags; /* channel flags */
+ uint16_t iev_freq; /* setting in Mhz */
+ uint8_t iev_ieee; /* IEEE channel number */
+ /* XXX timestamp? */
+};
+
+struct ieee80211_auth_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_deauth_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_country_event {
+ uint8_t iev_addr[6];
+ uint8_t iev_cc[2]; /* ISO country code */
+};
+
+struct ieee80211_radio_event {
+ uint8_t iev_state; /* 1 on, 0 off */
+};
+
#define RTM_IEEE80211_ASSOC 100 /* station associate (bss mode) */
#define RTM_IEEE80211_REASSOC 101 /* station re-associate (bss mode) */
#define RTM_IEEE80211_DISASSOC 102 /* station disassociate (bss mode) */
@@ -319,6 +499,14 @@ struct ieee80211_michael_event {
#define RTM_IEEE80211_REPLAY 106 /* sequence counter replay detected */
#define RTM_IEEE80211_MICHAEL 107 /* Michael MIC failure detected */
#define RTM_IEEE80211_REJOIN 108 /* station re-associate (ap mode) */
+#define RTM_IEEE80211_WDS 109 /* WDS discovery (ap mode) */
+#define RTM_IEEE80211_CSA 110 /* Channel Switch Announcement event */
+#define RTM_IEEE80211_RADAR 111 /* radar event */
+#define RTM_IEEE80211_CAC 112 /* Channel Availability Check event */
+#define RTM_IEEE80211_DEAUTH 113 /* station deauthenticate */
+#define RTM_IEEE80211_AUTH 114 /* station authenticate (ap mode) */
+#define RTM_IEEE80211_COUNTRY 115 /* discovered country code (sta mode) */
+#define RTM_IEEE80211_RADIO 116 /* RF kill switch state change */
/*
* Structure prepended to raw packets sent through the bpf
diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c
new file mode 100644
index 0000000..3c15505
--- /dev/null
+++ b/sys/net80211/ieee80211_hostap.c
@@ -0,0 +1,2236 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 HOSTAP mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_hostap.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_wds.h>
+
+#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
+
+static void hostap_vattach(struct ieee80211vap *);
+static int hostap_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int hostap_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp);
+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 noise, uint32_t rstamp);
+static void hostap_recv_pspoll(struct ieee80211_node *, struct mbuf *);
+
+void
+ieee80211_hostap_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_HOSTAP] = hostap_vattach;
+}
+
+void
+ieee80211_hostap_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+hostap_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+hostap_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = hostap_newstate;
+ vap->iv_input = hostap_input;
+ vap->iv_recv_mgmt = hostap_recv_mgmt;
+ vap->iv_opdetach = hostap_vdetach;
+ vap->iv_deliver_data = hostap_deliver_data;
+}
+
+static void
+sta_disassoc(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = arg;
+
+ if (ni->ni_vap == vap && ni->ni_associd != 0) {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ ieee80211_node_leave(ni);
+ }
+}
+
+/*
+ * IEEE80211_M_HOSTAP vap state machine handler.
+ */
+static int
+hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ case IEEE80211_S_CAC:
+ ieee80211_dfs_cac_stop(vap);
+ break;
+ case IEEE80211_S_RUN:
+ ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap);
+ break;
+ default:
+ break;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ if (vap->iv_auth->ia_detach != NULL)
+ vap->iv_auth->ia_detach(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_CSA:
+ case IEEE80211_S_RUN:
+ ieee80211_iterate_nodes(&ic->ic_sta, sta_disassoc, vap);
+ /*
+ * Clear overlapping BSS state; the beacon frame
+ * will be reconstructed on transition to the RUN
+ * state and the timeout routines check if the flag
+ * is set before doing anything so this is sufficient.
+ */
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR;
+ /* fall thru... */
+ case IEEE80211_S_CAC:
+ /*
+ * NB: We may get here because of a manual channel
+ * change in which case we need to stop CAC
+ * XXX no need to stop if ostate RUN but it's ok
+ */
+ ieee80211_dfs_cac_stop(vap);
+ /* fall thru... */
+ case IEEE80211_S_INIT:
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
+ !IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ * ieee80211_create_ibss will call back to
+ * move us to RUN state.
+ */
+ ieee80211_create_ibss(vap, vap->iv_des_chan);
+ break;
+ }
+ /*
+ * Initiate a scan. We can come here as a result
+ * of an IEEE80211_IOC_SCAN_REQ too in which case
+ * the vap will be marked with IEEE80211_FEXT_SCANREQ
+ * and the scan request parameters will be present
+ * in iv_scanreq. Otherwise we do the default.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
+ ieee80211_check_scan(vap,
+ vap->iv_scanreq_flags,
+ vap->iv_scanreq_duration,
+ vap->iv_scanreq_mindwell,
+ vap->iv_scanreq_maxdwell,
+ vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ } else
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ /*
+ * A state change requires a reset; scan.
+ */
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ break;
+ }
+ break;
+ case IEEE80211_S_CAC:
+ /*
+ * Start CAC on a DFS channel. We come here when starting
+ * a bss on a DFS channel (see ieee80211_create_ibss).
+ */
+ ieee80211_dfs_cac_start(vap);
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_flags & IEEE80211_F_WPA) {
+ /* XXX validate prerequisites */
+ }
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ /*
+ * Already have a channel; bypass the
+ * scan and startup immediately.
+ * Note that ieee80211_create_ibss will call
+ * back to do a RUN->RUN state change.
+ */
+ ieee80211_create_ibss(vap,
+ ieee80211_ht_adjust_channel(ic,
+ ic->ic_curchan, vap->iv_flags_ext));
+ /* NB: iv_bss is changed on return */
+ break;
+ case IEEE80211_S_CAC:
+ /*
+ * NB: This is the normal state change when CAC
+ * expires and no radar was detected; no need to
+ * clear the CAC timer as it's already expired.
+ */
+ /* fall thru... */
+ case IEEE80211_S_CSA:
+ /*
+ * Update bss node channel to reflect where
+ * we landed after CSA.
+ */
+ ieee80211_node_set_chan(vap->iv_bss,
+ ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
+ ieee80211_htchanflags(vap->iv_bss->ni_chan)));
+ /* XXX bypass debug msgs */
+ break;
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_RUN:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap)) {
+ struct ieee80211_node *ni = vap->iv_bss;
+ ieee80211_note(vap,
+ "synchronized with %s ssid ",
+ ether_sprintf(ni->ni_bssid));
+ ieee80211_print_essid(ni->ni_essid,
+ ni->ni_esslen);
+ /* XXX MCS/HT */
+ printf(" channel %d start %uMb\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ IEEE80211_RATE2MBS(ni->ni_txrate));
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+ /*
+ * Start/stop the authenticator. We delay until here
+ * to allow configuration to happen out of order.
+ */
+ if (vap->iv_auth->ia_attach != NULL) {
+ /* XXX check failure */
+ vap->iv_auth->ia_attach(vap);
+ } else if (vap->iv_auth->ia_detach != NULL) {
+ vap->iv_auth->ia_detach(vap);
+ }
+ ieee80211_node_authorize(vap->iv_bss);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void
+hostap_deliver_data(struct ieee80211vap *vap,
+ struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ether_header *eh = mtod(m, struct ether_header *);
+ struct ifnet *ifp = vap->iv_ifp;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP,
+ ("gack, opmode %d", vap->iv_opmode));
+ /*
+ * Do accounting.
+ */
+ ifp->if_ipackets++;
+ IEEE80211_NODE_STAT(ni, rx_data);
+ IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
+ if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+ m->m_flags |= M_MCAST; /* XXX M_BCAST? */
+ IEEE80211_NODE_STAT(ni, rx_mcast);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_ucast);
+
+ /* clear driver/net80211 flags before passing up */
+ m->m_flags &= ~M_80211_RX;
+
+ /* perform as a bridge within the AP */
+ if ((vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0) {
+ struct mbuf *mcopy = NULL;
+
+ if (m->m_flags & M_MCAST) {
+ mcopy = m_copypacket(m, M_DONTWAIT);
+ if (mcopy == NULL)
+ ifp->if_oerrors++;
+ else
+ mcopy->m_flags |= M_MCAST;
+ } else {
+ /*
+ * Check if the destination is associated with the
+ * same vap and authorized to receive traffic.
+ * Beware of traffic destined for the vap itself;
+ * sending it will not work; just let it be delivered
+ * normally.
+ */
+ struct ieee80211_node *sta = ieee80211_find_vap_node(
+ &vap->iv_ic->ic_sta, vap, eh->ether_dhost);
+ if (sta != NULL) {
+ if (ieee80211_node_is_authorized(sta)) {
+ /*
+ * Beware of sending to ourself; this
+ * needs to happen via the normal
+ * input path.
+ */
+ if (sta != vap->iv_bss) {
+ mcopy = m;
+ m = NULL;
+ }
+ } else {
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(sta, rx_unauth);
+ }
+ ieee80211_free_node(sta);
+ }
+ }
+ if (mcopy != NULL) {
+ int len, err;
+ len = mcopy->m_pkthdr.len;
+ IFQ_HANDOFF(ifp, mcopy, err);
+ if (err) {
+ /* NB: IFQ_HANDOFF reclaims mcopy */
+ } else {
+ ifp->if_opackets++;
+ }
+ }
+ }
+ if (m != NULL) {
+ /*
+ * Mark frame as coming from vap's interface.
+ */
+ m->m_pkthdr.rcvif = ifp;
+ if (m->m_flags & M_MCAST) {
+ /*
+ * Spam DWDS vap's w/ multicast traffic.
+ */
+ /* XXX only if dwds in use? */
+ ieee80211_dwds_mcast(vap, m);
+ }
+ if (ni->ni_vlan != 0) {
+ /* attach vlan tag */
+ m->m_pkthdr.ether_vtag = ni->ni_vlan;
+ m->m_flags |= M_VLANTAG;
+ }
+ ifp->if_input(ifp, m);
+ }
+}
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled. This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211vap *vap, int subtype)
+{
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+hostap_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint8_t *bssid;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if (dir != IEEE80211_FC1_DIR_NODS)
+ bssid = wh->i_addr1;
+ else if (type == IEEE80211_FC0_TYPE_CTL)
+ bssid = wh->i_addr1;
+ else {
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_ANY, ni->ni_macaddr,
+ NULL, "too short (2): len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ bssid = wh->i_addr3;
+ }
+ /*
+ * Validate the bssid.
+ */
+ if (!(type == IEEE80211_FC0_TYPE_MGT &&
+ subtype == IEEE80211_FC0_SUBTYPE_BEACON) &&
+ !IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) &&
+ !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >>
+ IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] &
+ IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ }
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (!(dir == IEEE80211_FC1_DIR_TODS ||
+ (dir == IEEE80211_FC1_DIR_DSTODS &&
+ (vap->iv_flags & IEEE80211_F_DWDS)))) {
+ if (dir != IEEE80211_FC1_DIR_DSTODS) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT, wh, "data",
+ "incorrect dir 0x%x", dir);
+ } else {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT |
+ IEEE80211_MSG_WDS, wh,
+ "4-address data",
+ "%s", "DWDS not enabled");
+ }
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ /* check if source STA is associated */
+ if (ni == vap->iv_bss) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "unknown src");
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_AUTHED);
+ vap->iv_stats.is_rx_notassoc++;
+ goto err;
+ }
+ if (ni->ni_associd == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "unassoc src");
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_NOT_ASSOCED);
+ vap->iv_stats.is_rx_notassoc++;
+ goto err;
+ }
+
+ /*
+ * Check for power save state change.
+ * XXX out-of-order A-MPDU frames?
+ */
+ if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
+ (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
+ ieee80211_node_pwrsave(ni,
+ wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
+ /*
+ * For 4-address packets handle WDS discovery
+ * notifications. Once a WDS link is setup frames
+ * are just delivered to the WDS vap (see below).
+ */
+ if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap == NULL) {
+ if (!ieee80211_node_is_authorized(ni)) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT |
+ IEEE80211_MSG_WDS, wh,
+ "4-address data",
+ "%s", "unauthorized port");
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ ieee80211_dwds_discover(ni, m);
+ return type;
+ }
+
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ if (dir == IEEE80211_FC1_DIR_DSTODS && ni->ni_wdsvap != NULL)
+ ieee80211_deliver_data(ni->ni_wdsvap, ni, m);
+ else
+ hostap_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "mgt", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+ /* ensure return frames are unicast */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "source is multicast: %s",
+ ether_sprintf(wh->i_addr2));
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
+ ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
+ /*
+ * Only shared key auth frames with a challenge
+ * should be encrypted, discard all others.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL,
+ "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "WEP set but PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ goto out;
+ }
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PS_POLL:
+ hostap_recv_pspoll(ni, m);
+ break;
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ ieee80211_recv_bar(ni, m);
+ break;
+ }
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "bad", "frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static void
+hostap_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state));
+
+ if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "open auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX */
+ /*
+ * Clear any challenge text that may be there if
+ * a previous shared key auth failed and then an
+ * open auth is attempted.
+ */
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_80211_NODE);
+ ni->ni_challenge = NULL;
+ }
+ /* XXX hack to workaround calling convention */
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ (seq + 1) | (IEEE80211_STATUS_ALG<<16));
+ return;
+ }
+ if (seq != IEEE80211_AUTH_OPEN_REQUEST) {
+ vap->iv_stats.is_rx_bad_auth++;
+ return;
+ }
+ /* always accept open authentication requests */
+ if (ni == vap->iv_bss) {
+ ni = ieee80211_dup_bss(vap, wh->i_addr2);
+ if (ni == NULL)
+ return;
+ } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+ (void) ieee80211_ref_node(ni);
+ /*
+ * Mark the node as referenced to reflect that it's
+ * reference count has been bumped to insure it remains
+ * after the transaction completes.
+ */
+ ni->ni_flags |= IEEE80211_NODE_AREF;
+
+ if (vap->iv_acl != NULL &&
+ vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) {
+ /*
+ * When the ACL policy is set to RADIUS we defer the
+ * authorization to a user agent. Dispatch an event,
+ * a subsequent MLME call will decide the fate of the
+ * station. If the user agent is not present then the
+ * node will be reclaimed due to inactivity.
+ */
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, ni->ni_macaddr,
+ "%s", "station authentication defered (radius acl)");
+ ieee80211_notify_node_auth(ni);
+ } else {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni->ni_macaddr,
+ "%s", "station authenticated (open)");
+ /*
+ * When 802.1x is not in use mark the port
+ * authorized at this point so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+ }
+}
+
+static void
+hostap_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp,
+ uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t *challenge;
+ int allocbs, estatus;
+
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("state %d", vap->iv_state));
+
+ /*
+ * NB: this can happen as we allow pre-shared key
+ * authentication to be enabled w/o wep being turned
+ * on so that configuration of these can be done
+ * in any order. It may be better to enforce the
+ * ordering in which case this check would just be
+ * for sanity/consistency.
+ */
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", " PRIVACY is disabled");
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+ /*
+ * Pre-shared key authentication is evil; accept
+ * it only if explicitly configured (it is supported
+ * mainly for compatibility with clients like Mac OS X).
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
+ ni->ni_authmode != IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+
+ challenge = NULL;
+ if (frm + 1 < efrm) {
+ if ((frm[1] + 2) > (efrm - frm)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "ie %d/%d too long",
+ frm[0], (frm[1] + 2) - (efrm - frm));
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (*frm == IEEE80211_ELEMID_CHALLENGE)
+ challenge = frm;
+ frm += frm[1] + 2;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (challenge == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", "no challenge");
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad challenge len %d", challenge[1]);
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ default:
+ break;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_REQUEST:
+ if (ni == vap->iv_bss) {
+ ni = ieee80211_dup_bss(vap, wh->i_addr2);
+ if (ni == NULL) {
+ /* NB: no way to return an error */
+ return;
+ }
+ allocbs = 1;
+ } else {
+ if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+ (void) ieee80211_ref_node(ni);
+ allocbs = 0;
+ }
+ /*
+ * Mark the node as referenced to reflect that it's
+ * reference count has been bumped to insure it remains
+ * after the transaction completes.
+ */
+ ni->ni_flags |= IEEE80211_NODE_AREF;
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (!ieee80211_alloc_challenge(ni)) {
+ /* NB: don't return error so they rexmit */
+ return;
+ }
+ get_random_bytes(ni->ni_challenge,
+ IEEE80211_CHALLENGE_LEN);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+ ni, "shared key %sauth request", allocbs ? "" : "re");
+ /*
+ * When the ACL policy is set to RADIUS we defer the
+ * authorization to a user agent. Dispatch an event,
+ * a subsequent MLME call will decide the fate of the
+ * station. If the user agent is not present then the
+ * node will be reclaimed due to inactivity.
+ */
+ if (vap->iv_acl != NULL &&
+ vap->iv_acl->iac_getpolicy(vap) == IEEE80211_MACCMD_POLICY_RADIUS) {
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL,
+ ni->ni_macaddr,
+ "%s", "station authentication defered (radius acl)");
+ ieee80211_notify_node_auth(ni);
+ return;
+ }
+ break;
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (ni == vap->iv_bss) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key response",
+ "%s", "unknown station");
+ /* NB: don't send a response */
+ return;
+ }
+ if (ni->ni_challenge == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key response",
+ "%s", "no challenge recorded");
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (memcmp(ni->ni_challenge, &challenge[2],
+ challenge[1]) != 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key response",
+ "%s", "challenge mismatch");
+ vap->iv_stats.is_rx_auth_fail++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+ ni, "%s", "station authenticated (shared key)");
+ ieee80211_node_authorize(ni);
+ break;
+ default:
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad seq %d", seq);
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_SEQUENCE;
+ goto bad;
+ }
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ return;
+bad:
+ /*
+ * Send an error response; but only when operating as an AP.
+ */
+ /* XXX hack to workaround calling convention */
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ (seq + 1) | (estatus<<16));
+}
+
+/*
+ * Convert a WPA cipher selector OUI to an internal
+ * cipher algorithm. Where appropriate we also
+ * record any key length.
+ */
+static int
+wpa_cipher(const uint8_t *sel, uint8_t *keylen)
+{
+#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case WPA_SEL(WPA_CSE_NULL):
+ return IEEE80211_CIPHER_NONE;
+ case WPA_SEL(WPA_CSE_WEP40):
+ if (keylen)
+ *keylen = 40 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case WPA_SEL(WPA_CSE_WEP104):
+ if (keylen)
+ *keylen = 104 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case WPA_SEL(WPA_CSE_TKIP):
+ return IEEE80211_CIPHER_TKIP;
+ case WPA_SEL(WPA_CSE_CCMP):
+ return IEEE80211_CIPHER_AES_CCM;
+ }
+ return 32; /* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert a WPA key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+wpa_keymgmt(const uint8_t *sel)
+{
+#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case WPA_SEL(WPA_ASE_8021X_UNSPEC):
+ return WPA_ASE_8021X_UNSPEC;
+ case WPA_SEL(WPA_ASE_8021X_PSK):
+ return WPA_ASE_8021X_PSK;
+ case WPA_SEL(WPA_ASE_NONE):
+ return WPA_ASE_NONE;
+ }
+ return 0; /* NB: so is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Parse a WPA information element to collect parameters.
+ * Note that we do not validate security parameters; that
+ * is handled by the authenticator; the parsing done here
+ * is just for internal use in making operational decisions.
+ */
+static int
+ieee80211_parse_wpa(struct ieee80211vap *vap, const uint8_t *frm,
+ struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+ uint8_t len = frm[1];
+ uint32_t w;
+ int n;
+
+ /*
+ * Check the length once for fixed parts: OUI, type,
+ * version, mcast cipher, and 2 selector counts.
+ * Other, variable-length data, must be checked separately.
+ */
+ if ((vap->iv_flags & IEEE80211_F_WPA1) == 0) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "not WPA, flags 0x%x", vap->iv_flags);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ if (len < 14) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "too short, len %u", len);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 6, len -= 4; /* NB: len is payload only */
+ /* NB: iswapoui already validated the OUI and type */
+ w = LE_READ_2(frm);
+ if (w != WPA_VERSION) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "bad version %u", w);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 2, len -= 2;
+
+ memset(rsn, 0, sizeof(*rsn));
+
+ /* multicast/group cipher */
+ rsn->rsn_mcastcipher = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
+ frm += 4, len -= 4;
+
+ /* unicast ciphers */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4+2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "ucast cipher data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
+ frm += 4, len -= 4;
+ }
+ if (w & (1<<IEEE80211_CIPHER_TKIP))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+ else
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+ /* key management algorithms */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "key mgmt alg data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= wpa_keymgmt(frm);
+ frm += 4, len -= 4;
+ }
+ if (w & WPA_ASE_8021X_UNSPEC)
+ rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
+ else
+ rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+
+ if (len > 2) /* optional capabilities */
+ rsn->rsn_caps = LE_READ_2(frm);
+
+ return 0;
+}
+
+/*
+ * Convert an RSN cipher selector OUI to an internal
+ * cipher algorithm. Where appropriate we also
+ * record any key length.
+ */
+static int
+rsn_cipher(const uint8_t *sel, uint8_t *keylen)
+{
+#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case RSN_SEL(RSN_CSE_NULL):
+ return IEEE80211_CIPHER_NONE;
+ case RSN_SEL(RSN_CSE_WEP40):
+ if (keylen)
+ *keylen = 40 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case RSN_SEL(RSN_CSE_WEP104):
+ if (keylen)
+ *keylen = 104 / NBBY;
+ return IEEE80211_CIPHER_WEP;
+ case RSN_SEL(RSN_CSE_TKIP):
+ return IEEE80211_CIPHER_TKIP;
+ case RSN_SEL(RSN_CSE_CCMP):
+ return IEEE80211_CIPHER_AES_CCM;
+ case RSN_SEL(RSN_CSE_WRAP):
+ return IEEE80211_CIPHER_AES_OCB;
+ }
+ return 32; /* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert an RSN key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+rsn_keymgmt(const uint8_t *sel)
+{
+#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
+ uint32_t w = LE_READ_4(sel);
+
+ switch (w) {
+ case RSN_SEL(RSN_ASE_8021X_UNSPEC):
+ return RSN_ASE_8021X_UNSPEC;
+ case RSN_SEL(RSN_ASE_8021X_PSK):
+ return RSN_ASE_8021X_PSK;
+ case RSN_SEL(RSN_ASE_NONE):
+ return RSN_ASE_NONE;
+ }
+ return 0; /* NB: so is discarded */
+#undef RSN_SEL
+}
+
+/*
+ * Parse a WPA/RSN information element to collect parameters
+ * and validate the parameters against what has been
+ * configured for the system.
+ */
+static int
+ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm,
+ struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+ uint8_t len = frm[1];
+ uint32_t w;
+ int n;
+
+ /*
+ * Check the length once for fixed parts:
+ * version, mcast cipher, and 2 selector counts.
+ * Other, variable-length data, must be checked separately.
+ */
+ if ((vap->iv_flags & IEEE80211_F_WPA2) == 0) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "WPA", "not RSN, flags 0x%x", vap->iv_flags);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ if (len < 10) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "too short, len %u", len);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 2;
+ w = LE_READ_2(frm);
+ if (w != RSN_VERSION) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "bad version %u", w);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ frm += 2, len -= 2;
+
+ memset(rsn, 0, sizeof(*rsn));
+
+ /* multicast/group cipher */
+ rsn->rsn_mcastcipher = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
+ frm += 4, len -= 4;
+
+ /* unicast ciphers */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4+2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "ucast cipher data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
+ frm += 4, len -= 4;
+ }
+ if (w & (1<<IEEE80211_CIPHER_TKIP))
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+ else
+ rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+ /* key management algorithms */
+ n = LE_READ_2(frm);
+ frm += 2, len -= 2;
+ if (len < n*4) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+ wh, "RSN", "key mgmt alg data too short; len %u, n %u",
+ len, n);
+ return IEEE80211_REASON_IE_INVALID;
+ }
+ w = 0;
+ for (; n > 0; n--) {
+ w |= rsn_keymgmt(frm);
+ frm += 4, len -= 4;
+ }
+ if (w & RSN_ASE_8021X_UNSPEC)
+ rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
+ else
+ rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
+
+ /* optional RSN capabilities */
+ if (len > 2)
+ rsn->rsn_caps = LE_READ_2(frm);
+ /* XXXPMKID */
+
+ return 0;
+}
+
+/*
+ * WPA/802.11i assocation request processing.
+ */
+static int
+wpa_assocreq(struct ieee80211_node *ni, struct ieee80211_rsnparms *rsnparms,
+ const struct ieee80211_frame *wh, const uint8_t *wpa,
+ const uint8_t *rsn, uint16_t capinfo)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t reason;
+ int badwparsn;
+
+ ni->ni_flags &= ~(IEEE80211_NODE_WPS|IEEE80211_NODE_TSN);
+ if (wpa == NULL && rsn == NULL) {
+ if (vap->iv_flags_ext & IEEE80211_FEXT_WPS) {
+ /*
+ * W-Fi Protected Setup (WPS) permits
+ * clients to associate and pass EAPOL frames
+ * to establish initial credentials.
+ */
+ ni->ni_flags |= IEEE80211_NODE_WPS;
+ return 1;
+ }
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_TSN) &&
+ (capinfo & IEEE80211_CAPINFO_PRIVACY)) {
+ /*
+ * Transitional Security Network. Permits clients
+ * to associate and use WEP while WPA is configured.
+ */
+ ni->ni_flags |= IEEE80211_NODE_TSN;
+ return 1;
+ }
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+ wh, NULL, "%s", "no WPA/RSN IE in association request");
+ vap->iv_stats.is_rx_assoc_badwpaie++;
+ reason = IEEE80211_REASON_IE_INVALID;
+ goto bad;
+ }
+ /* assert right association security credentials */
+ badwparsn = 0; /* NB: to silence compiler */
+ switch (vap->iv_flags & IEEE80211_F_WPA) {
+ case IEEE80211_F_WPA1:
+ badwparsn = (wpa == NULL);
+ break;
+ case IEEE80211_F_WPA2:
+ badwparsn = (rsn == NULL);
+ break;
+ case IEEE80211_F_WPA1|IEEE80211_F_WPA2:
+ badwparsn = (wpa == NULL && rsn == NULL);
+ break;
+ }
+ if (badwparsn) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+ wh, NULL,
+ "%s", "missing WPA/RSN IE in association request");
+ vap->iv_stats.is_rx_assoc_badwpaie++;
+ reason = IEEE80211_REASON_IE_INVALID;
+ goto bad;
+ }
+ /*
+ * Parse WPA/RSN information element.
+ */
+ if (wpa != NULL)
+ reason = ieee80211_parse_wpa(vap, wpa, rsnparms, wh);
+ else
+ reason = ieee80211_parse_rsn(vap, rsn, rsnparms, wh);
+ if (reason != 0) {
+ /* XXX distinguish WPA/RSN? */
+ vap->iv_stats.is_rx_assoc_badwpaie++;
+ goto bad;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, ni,
+ "%s ie: mc %u/%u uc %u/%u key %u caps 0x%x",
+ wpa != NULL ? "WPA" : "RSN",
+ rsnparms->rsn_mcastcipher, rsnparms->rsn_mcastkeylen,
+ rsnparms->rsn_ucastcipher, rsnparms->rsn_ucastkeylen,
+ rsnparms->rsn_keymgmt, rsnparms->rsn_caps);
+
+ return 1;
+bad:
+ ieee80211_node_deauth(ni, reason);
+ return 0;
+}
+
+/* XXX find a better place for definition */
+struct l2_update_frame {
+ struct ether_header eh;
+ uint8_t dsap;
+ uint8_t ssap;
+ uint8_t control;
+ uint8_t xid[3];
+} __packed;
+
+/*
+ * Deliver a TGf L2UF frame on behalf of a station.
+ * This primes any bridge when the station is roaming
+ * between ap's on the same wired network.
+ */
+static void
+ieee80211_deliver_l2uf(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct mbuf *m;
+ struct l2_update_frame *l2uf;
+ struct ether_header *eh;
+
+ m = m_gethdr(M_NOWAIT, MT_DATA);
+ if (m == NULL) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "%s", "no mbuf for l2uf frame");
+ vap->iv_stats.is_rx_nobuf++; /* XXX not right */
+ return;
+ }
+ l2uf = mtod(m, struct l2_update_frame *);
+ eh = &l2uf->eh;
+ /* dst: Broadcast address */
+ IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr);
+ /* src: associated STA */
+ IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr);
+ eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh));
+
+ l2uf->dsap = 0;
+ l2uf->ssap = 0;
+ l2uf->control = 0xf5;
+ l2uf->xid[0] = 0x81;
+ l2uf->xid[1] = 0x80;
+ l2uf->xid[2] = 0x00;
+
+ m->m_pkthdr.len = m->m_len = sizeof(*l2uf);
+ hostap_deliver_data(vap, ni, m);
+}
+
+static void
+ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp, const char *tag, int rate)
+{
+ IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, %s rate set mismatch, rate/MCS %d",
+ reassoc ? "reassoc" : "assoc", tag, rate & IEEE80211_RATE_VAL);
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_BASIC_RATE);
+ ieee80211_node_leave(ni);
+}
+
+static void
+capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp, const char *tag, int capinfo)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, %s mismatch 0x%x",
+ reassoc ? "reassoc" : "assoc", tag, capinfo);
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_CAPINFO);
+ ieee80211_node_leave(ni);
+ vap->iv_stats.is_rx_assoc_capmismatch++;
+}
+
+static void
+htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int reassoc, int resp)
+{
+ IEEE80211_NOTE_MAC(ni->ni_vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc");
+ /* XXX no better code */
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_OTHER);
+ ieee80211_node_leave(ni);
+}
+
+static void
+authalgreject(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ int algo, int seq, int status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "unsupported alg %d", algo);
+ vap->iv_stats.is_rx_auth_unsupported++;
+ ieee80211_send_error(ni, wh->i_addr2, IEEE80211_FC0_SUBTYPE_AUTH,
+ seq | (status << 16));
+}
+
+static __inline int
+ishtmixed(const uint8_t *ie)
+{
+ const struct ieee80211_ie_htinfo *ht =
+ (const struct ieee80211_ie_htinfo *) ie;
+ return (ht->hi_byte2 & IEEE80211_HTINFO_OPMODE) ==
+ IEEE80211_HTINFO_OPMODE_MIXED;
+}
+
+static int
+is11bclient(const uint8_t *rates, const uint8_t *xrates)
+{
+ static const uint32_t brates = (1<<2*1)|(1<<2*2)|(1<<11)|(1<<2*11);
+ int i;
+
+ /* NB: the 11b clients we care about will not have xrates */
+ if (xrates != NULL || rates == NULL)
+ return 0;
+ for (i = 0; i < rates[1]; i++) {
+ int r = rates[2+i] & IEEE80211_RATE_VAL;
+ if (r > 2*11 || ((1<<r) & brates) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, uint32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm, *sfrm;
+ uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap;
+ int reassoc, resp;
+ uint8_t rate;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ struct ieee80211_scanparams scan;
+ /*
+ * We process beacon/probe response frames when scanning;
+ * otherwise we check beacon frames for overlapping non-ERP
+ * BSS in 11g and/or overlapping legacy BSS when in HT.
+ */
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
+ subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /* NB: accept off-channel frames */
+ if (ieee80211_parse_beacon(ni, m0, &scan) &~ IEEE80211_BPARSE_OFFCHAN)
+ return;
+ /*
+ * Count frame now that we know it's to be processed.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ vap->iv_stats.is_rx_beacon++; /* XXX remove */
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ if (scan.status == 0 && /* NB: on channel */
+ (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN)) {
+ /*
+ * Actively scanning a channel marked passive;
+ * send a probe request now that we know there
+ * is 802.11 traffic present.
+ *
+ * XXX check if the beacon we recv'd gives
+ * us what we need and suppress the probe req
+ */
+ ieee80211_probe_curchan(vap, 1);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
+ }
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ return;
+ }
+ /*
+ * Check beacon for overlapping bss w/ non ERP stations.
+ * If we detect one and protection is configured but not
+ * enabled, enable it and start a timer that'll bring us
+ * out if we stop seeing the bss.
+ */
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
+ scan.status == 0 && /* NB: on-channel */
+ ((scan.erp & 0x100) == 0 || /* NB: no ERP, 11b sta*/
+ (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) {
+ ic->ic_lastnonerp = ticks;
+ ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR;
+ if (ic->ic_protmode != IEEE80211_PROT_NONE &&
+ (ic->ic_flags & IEEE80211_F_USEPROT) == 0) {
+ IEEE80211_NOTE_FRAME(vap,
+ IEEE80211_MSG_ASSOC, wh,
+ "non-ERP present on channel %d "
+ "(saw erp 0x%x from channel %d), "
+ "enable use of protection",
+ ic->ic_curchan->ic_ieee,
+ scan.erp, scan.chan);
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ ieee80211_notify_erp(ic);
+ }
+ }
+ /*
+ * Check beacon for non-HT station on HT channel
+ * and update HT BSS occupancy as appropriate.
+ */
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) {
+ if (scan.status & IEEE80211_BPARSE_OFFCHAN) {
+ /*
+ * Off control channel; only check frames
+ * that come in the extension channel when
+ * operating w/ HT40.
+ */
+ if (!IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
+ break;
+ if (scan.chan != ic->ic_curchan->ic_extieee)
+ break;
+ }
+ if (scan.htinfo == NULL) {
+ ieee80211_htprot_update(ic,
+ IEEE80211_HTINFO_OPMODE_PROTOPT |
+ IEEE80211_HTINFO_NONHT_PRESENT);
+ } else if (ishtmixed(scan.htinfo)) {
+ /* XXX? take NONHT_PRESENT from beacon? */
+ ieee80211_htprot_update(ic,
+ IEEE80211_HTINFO_OPMODE_MIXED |
+ IEEE80211_HTINFO_NONHT_PRESENT);
+ }
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /*
+ * prreq frame format
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ */
+ ssid = rates = xrates = NULL;
+ sfrm = frm;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
+ IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
+ if ((vap->iv_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL,
+ "%s", "no ssid with ssid suppression enabled");
+ vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
+ return;
+ }
+
+ /* XXX find a better class or define it's own */
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_INPUT, wh->i_addr2,
+ "%s", "recv probe req");
+ /*
+ * Some legacy 11b clients cannot hack a complete
+ * probe response frame. When the request includes
+ * only a bare-bones rate set, communicate this to
+ * the transmit side.
+ */
+ ieee80211_send_proberesp(vap, wh->i_addr2,
+ is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0);
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_AUTH: {
+ uint16_t algo, seq, status;
+
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "%s", "wrong bssid");
+ vap->iv_stats.is_rx_wrongbss++; /*XXX unique stat?*/
+ return;
+ }
+ /*
+ * auth frame format
+ * [2] algorithm
+ * [2] sequence
+ * [2] status
+ * [tlv*] challenge
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+ algo = le16toh(*(uint16_t *)frm);
+ seq = le16toh(*(uint16_t *)(frm + 2));
+ status = le16toh(*(uint16_t *)(frm + 4));
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
+ "recv auth frame with algorithm %d seq %d", algo, seq);
+ /*
+ * Consult the ACL policy module if setup.
+ */
+ if (vap->iv_acl != NULL &&
+ !vap->iv_acl->iac_check(vap, wh->i_addr2)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ACL,
+ wh, NULL, "%s", "disallowed by ACL");
+ vap->iv_stats.is_rx_acl++;
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16));
+ return;
+ }
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
+ wh, NULL, "%s", "TKIP countermeasures enabled");
+ vap->iv_stats.is_rx_auth_countermeasures++;
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ IEEE80211_REASON_MIC_FAILURE);
+ return;
+ }
+ if (algo == IEEE80211_AUTH_ALG_SHARED)
+ hostap_auth_shared(ni, wh, frm + 6, efrm, rssi,
+ noise, rstamp, seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_OPEN)
+ hostap_auth_open(ni, wh, rssi, noise, rstamp,
+ seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_LEAP) {
+ authalgreject(ni, wh, algo,
+ seq+1, IEEE80211_STATUS_ALG);
+ return;
+ } else {
+ /*
+ * We assume that an unknown algorithm is the result
+ * of a decryption failure on a shared key auth frame;
+ * return a status code appropriate for that instead
+ * of IEEE80211_STATUS_ALG.
+ *
+ * NB: a seq# of 4 is intentional; the decrypted
+ * frame likely has a bogus seq value.
+ */
+ authalgreject(ni, wh, algo,
+ 4, IEEE80211_STATUS_CHALLENGE);
+ return;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
+ uint16_t capinfo, lintval;
+ struct ieee80211_rsnparms rsnparms;
+
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "%s", "wrong bssid");
+ vap->iv_stats.is_rx_assoc_bss++;
+ return;
+ }
+ if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
+ reassoc = 1;
+ resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
+ } else {
+ reassoc = 0;
+ resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
+ }
+ if (ni == vap->iv_bss) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2,
+ "deny %s request, sta not authenticated",
+ reassoc ? "reassoc" : "assoc");
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_ASSOC_NOT_AUTHED);
+ vap->iv_stats.is_rx_assoc_notauth++;
+ return;
+ }
+
+ /*
+ * asreq frame format
+ * [2] capability information
+ * [2] listen interval
+ * [6*] current AP address (reassoc only)
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ * [tlv] WPA or RSN
+ * [tlv] HT capabilities
+ * [tlv] Atheros capabilities
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return);
+ capinfo = le16toh(*(uint16_t *)frm); frm += 2;
+ lintval = le16toh(*(uint16_t *)frm); frm += 2;
+ if (reassoc)
+ frm += 6; /* ignore current AP info */
+ ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL;
+ sfrm = frm;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ case IEEE80211_ELEMID_RSN:
+ rsn = frm;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm))
+ wpa = frm;
+ else if (iswmeinfo(frm))
+ wme = frm;
+ else if (isatherosoui(frm))
+ ath = frm;
+ else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ if (ishtcapoui(frm) && htcap == NULL)
+ htcap = frm;
+ }
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, return);
+ IEEE80211_VERIFY_SSID(vap->iv_bss, ssid, return);
+ if (htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(htcap[1],
+ htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htcap)-2 :
+ sizeof(struct ieee80211_ie_htcap)-2,
+ return); /* XXX just NULL out? */
+ }
+
+ if ((vap->iv_flags & IEEE80211_F_WPA) &&
+ !wpa_assocreq(ni, &rsnparms, wh, wpa, rsn, capinfo))
+ return;
+ /* discard challenge after association */
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_80211_NODE);
+ ni->ni_challenge = NULL;
+ }
+ /* NB: 802.11 spec says to ignore station's privacy bit */
+ if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
+ capinfomismatch(ni, wh, reassoc, resp,
+ "capability", capinfo);
+ return;
+ }
+ /*
+ * Disallow re-associate w/ invalid slot time setting.
+ */
+ if (ni->ni_associd != 0 &&
+ IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
+ ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) {
+ capinfomismatch(ni, wh, reassoc, resp,
+ "slot time", capinfo);
+ return;
+ }
+ rate = ieee80211_setup_rates(ni, rates, xrates,
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (rate & IEEE80211_RATE_BASIC) {
+ ratesetmismatch(ni, wh, reassoc, resp, "legacy", rate);
+ vap->iv_stats.is_rx_assoc_norate++;
+ return;
+ }
+ /*
+ * If constrained to 11g-only stations reject an
+ * 11b-only station. We cheat a bit here by looking
+ * at the max negotiated xmit rate and assuming anyone
+ * with a best rate <24Mb/s is an 11b station.
+ */
+ if ((vap->iv_flags & IEEE80211_F_PUREG) && rate < 48) {
+ ratesetmismatch(ni, wh, reassoc, resp, "11g", rate);
+ vap->iv_stats.is_rx_assoc_norate++;
+ return;
+ }
+ /*
+ * Do HT rate set handling and setup HT node state.
+ */
+ ni->ni_chan = vap->iv_bss->ni_chan;
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) {
+ rate = ieee80211_setup_htrates(ni, htcap,
+ IEEE80211_F_DOFMCS | IEEE80211_F_DONEGO |
+ IEEE80211_F_DOBRS);
+ if (rate & IEEE80211_RATE_BASIC) {
+ ratesetmismatch(ni, wh, reassoc, resp,
+ "HT", rate);
+ vap->iv_stats.is_ht_assoc_norate++;
+ return;
+ }
+ ieee80211_ht_node_init(ni, htcap);
+ } else if (ni->ni_flags & IEEE80211_NODE_HT)
+ ieee80211_ht_node_cleanup(ni);
+ /*
+ * Allow AMPDU operation only with unencrypted traffic
+ * or AES-CCM; the 11n spec only specifies these ciphers
+ * so permitting any others is undefined and can lead
+ * to interoperability problems.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ (((vap->iv_flags & IEEE80211_F_WPA) &&
+ rsnparms.rsn_ucastcipher != IEEE80211_CIPHER_AES_CCM) ||
+ (vap->iv_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) {
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
+ "disallow HT use because WEP or TKIP requested, "
+ "capinfo 0x%x ucastcipher %d", capinfo,
+ rsnparms.rsn_ucastcipher);
+ ieee80211_ht_node_cleanup(ni);
+ vap->iv_stats.is_ht_assoc_downgrade++;
+ }
+ /*
+ * If constrained to 11n-only stations reject legacy stations.
+ */
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_PUREN) &&
+ (ni->ni_flags & IEEE80211_NODE_HT) == 0) {
+ htcapmismatch(ni, wh, reassoc, resp);
+ vap->iv_stats.is_ht_assoc_nohtcap++;
+ return;
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ ni->ni_intval = lintval;
+ ni->ni_capinfo = capinfo;
+ ni->ni_fhdwell = vap->iv_bss->ni_fhdwell;
+ ni->ni_fhindex = vap->iv_bss->ni_fhindex;
+ /*
+ * Store the IEs.
+ * XXX maybe better to just expand
+ */
+ if (ieee80211_ies_init(&ni->ni_ies, sfrm, efrm - sfrm)) {
+#define setie(_ie, _off) ieee80211_ies_setie(ni->ni_ies, _ie, _off)
+ if (wpa != NULL)
+ setie(wpa_ie, wpa - sfrm);
+ if (rsn != NULL)
+ setie(rsn_ie, rsn - sfrm);
+ if (htcap != NULL)
+ setie(htcap_ie, htcap - sfrm);
+ if (wme != NULL) {
+ setie(wme_ie, wme - sfrm);
+ /*
+ * Mark node as capable of QoS.
+ */
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ } else
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ if (ath != NULL) {
+ setie(ath_ie, ath - sfrm);
+ /*
+ * Parse ATH station parameters.
+ */
+ ieee80211_parse_ath(ni, ni->ni_ies.ath_ie);
+ } else
+ ni->ni_ath_flags = 0;
+#undef setie
+ } else {
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ ni->ni_ath_flags = 0;
+ }
+ ieee80211_node_join(ni, resp);
+ ieee80211_deliver_l2uf(ni);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ case IEEE80211_FC0_SUBTYPE_DISASSOC: {
+ uint16_t reason;
+
+ if (vap->iv_state != IEEE80211_S_RUN ||
+ /* NB: can happen when in promiscuous mode */
+ !IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ /*
+ * deauth/disassoc frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
+ if (subtype == IEEE80211_FC0_SUBTYPE_DEAUTH) {
+ vap->iv_stats.is_rx_deauth++;
+ IEEE80211_NODE_STAT(ni, rx_deauth);
+ } else {
+ vap->iv_stats.is_rx_disassoc++;
+ IEEE80211_NODE_STAT(ni, rx_disassoc);
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "recv %s (reason %d)", ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT], reason);
+ if (ni != vap->iv_bss)
+ ieee80211_node_leave(ni);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (vap->iv_state == IEEE80211_S_RUN) {
+ if (ieee80211_parse_action(ni, m0) == 0)
+ ic->ic_recv_action(ni, frm, efrm);
+ } else
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+}
+
+/*
+ * Process a received ps-poll frame.
+ */
+static void
+hostap_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m0)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_frame_min *wh;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct mbuf *m;
+ uint16_t aid;
+ int qlen;
+
+ wh = mtod(m0, struct ieee80211_frame_min *);
+ if (ni->ni_associd == 0) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+ (struct ieee80211_frame *) wh, NULL,
+ "%s", "unassociated station");
+ vap->iv_stats.is_ps_unassoc++;
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_ASSOCED);
+ return;
+ }
+
+ aid = le16toh(*(uint16_t *)wh->i_dur);
+ if (aid != ni->ni_associd) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+ (struct ieee80211_frame *) wh, NULL,
+ "aid mismatch: sta aid 0x%x poll aid 0x%x",
+ ni->ni_associd, aid);
+ vap->iv_stats.is_ps_badaid++;
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_NOT_ASSOCED);
+ return;
+ }
+
+ /* Okay, take the first queued packet and put it out... */
+ IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
+ if (m == NULL) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_POWER, wh->i_addr2,
+ "%s", "recv ps-poll, but queue empty");
+ ieee80211_send_nulldata(ieee80211_ref_node(ni));
+ vap->iv_stats.is_ps_qempty++; /* XXX node stat */
+ if (vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0); /* just in case */
+ return;
+ }
+ /*
+ * If there are more packets, set the more packets bit
+ * in the packet dispatched to the station; otherwise
+ * turn off the TIM bit.
+ */
+ if (qlen != 0) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "recv ps-poll, send packet, %u still queued", qlen);
+ m->m_flags |= M_MORE_DATA;
+ } else {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "%s", "recv ps-poll, send packet, queue empty");
+ if (vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
+ }
+ m->m_flags |= M_PWR_SAV; /* bypass PS handling */
+ IF_ENQUEUE(&ifp->if_snd, m);
+ if_start(ifp);
+}
diff --git a/sys/net80211/ieee80211_hostap.h b/sys/net80211/ieee80211_hostap.h
new file mode 100644
index 0000000..87f858d
--- /dev/null
+++ b/sys/net80211/ieee80211_hostap.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_HOSTAP_H_
+#define _NET80211_IEEE80211_HOSTAP_H_
+
+/*
+ * Hostap implementation definitions.
+ */
+void ieee80211_hostap_attach(struct ieee80211com *);
+void ieee80211_hostap_detach(struct ieee80211com *);
+#endif /* !_NET80211_IEEE80211_HOSTAP_H_ */
diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c
index 63fb39a..d8b2d3d 100644
--- a/sys/net80211/ieee80211_ht.c
+++ b/sys/net80211/ieee80211_ht.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
@@ -46,37 +47,34 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
/* define here, used throughout file */
#define MS(_v, _f) (((_v) & _f) >> _f##_S)
#define SM(_v, _f) (((_v) << _f##_S) & _f)
-/* XXX need max array size */
-/* NB: these are for HT20 w/ long GI */
-const int ieee80211_htrates[16] = {
- 13, /* IFM_IEEE80211_MCS0 */
- 26, /* IFM_IEEE80211_MCS1 */
- 39, /* IFM_IEEE80211_MCS2 */
- 52, /* IFM_IEEE80211_MCS3 */
- 78, /* IFM_IEEE80211_MCS4 */
- 104, /* IFM_IEEE80211_MCS5 */
- 117, /* IFM_IEEE80211_MCS6 */
- 130, /* IFM_IEEE80211_MCS7 */
- 26, /* IFM_IEEE80211_MCS8 */
- 52, /* IFM_IEEE80211_MCS9 */
- 78, /* IFM_IEEE80211_MCS10 */
- 104, /* IFM_IEEE80211_MCS11 */
- 156, /* IFM_IEEE80211_MCS12 */
- 208, /* IFM_IEEE80211_MCS13 */
- 234, /* IFM_IEEE80211_MCS14 */
- 260, /* IFM_IEEE80211_MCS15 */
+const struct ieee80211_mcs_rates ieee80211_htrates[16] = {
+ { 13, 14, 27, 30 }, /* MCS 0 */
+ { 26, 29, 54, 60 }, /* MCS 1 */
+ { 39, 43, 81, 90 }, /* MCS 2 */
+ { 52, 58, 108, 120 }, /* MCS 3 */
+ { 78, 87, 162, 180 }, /* MCS 4 */
+ { 104, 116, 216, 240 }, /* MCS 5 */
+ { 117, 130, 243, 270 }, /* MCS 6 */
+ { 130, 144, 270, 300 }, /* MCS 7 */
+ { 26, 29, 54, 60 }, /* MCS 8 */
+ { 52, 58, 108, 120 }, /* MCS 9 */
+ { 78, 87, 162, 180 }, /* MCS 10 */
+ { 104, 116, 216, 240 }, /* MCS 11 */
+ { 156, 173, 324, 360 }, /* MCS 12 */
+ { 208, 231, 432, 480 }, /* MCS 13 */
+ { 234, 260, 486, 540 }, /* MCS 14 */
+ { 260, 289, 540, 600 } /* MCS 15 */
};
static const struct ieee80211_htrateset ieee80211_rateset_11n =
{ 16, {
- /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- /* 39 52 78 104 117, 130 */
10, 11, 12, 13, 14, 15 }
};
@@ -85,11 +83,26 @@ static const struct ieee80211_htrateset ieee80211_rateset_11n =
int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */
#endif
int ieee80211_recv_bar_ena = 1;
+int ieee80211_addba_timeout = -1; /* timeout waiting for ADDBA response */
+int ieee80211_addba_backoff = -1; /* backoff after max ADDBA requests */
+int ieee80211_addba_maxtries = 3; /* max ADDBA requests before backoff */
-#define IEEE80211_AGGR_TIMEOUT msecs_to_ticks(250)
-#define IEEE80211_AGGR_MINRETRY msecs_to_ticks(10*1000)
-#define IEEE80211_AGGR_MAXTRIES 3
+/*
+ * Setup HT parameters that depends on the clock frequency.
+ */
+static void
+ieee80211_ht_setup(void)
+{
+#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);
+}
+SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_setup, NULL);
+static int ieee80211_ampdu_enable(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap);
static int ieee80211_addba_request(struct ieee80211_node *ni,
struct ieee80211_tx_ampdu *tap,
int dialogtoken, int baparamset, int batimeout);
@@ -104,56 +117,70 @@ static void ieee80211_aggr_recv_action(struct ieee80211_node *ni,
void
ieee80211_ht_attach(struct ieee80211com *ic)
{
-#ifdef IEEE80211_AMPDU_AGE
- if (ieee80211_ampdu_age == -1)
- ieee80211_ampdu_age = msecs_to_ticks(500);
-#endif
-
/* setup default aggregation policy */
ic->ic_recv_action = ieee80211_aggr_recv_action;
ic->ic_send_action = ieee80211_send_action;
+ ic->ic_ampdu_enable = ieee80211_ampdu_enable;
ic->ic_addba_request = ieee80211_addba_request;
ic->ic_addba_response = ieee80211_addba_response;
ic->ic_addba_stop = ieee80211_addba_stop;
ic->ic_htprotmode = IEEE80211_PROT_RTSCTS;
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
+}
+
+void
+ieee80211_ht_detach(struct ieee80211com *ic)
+{
+}
- /* XXX get from driver */
- ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
- ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
- ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
- ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839;
+void
+ieee80211_ht_vattach(struct ieee80211vap *vap)
+{
- if (ic->ic_htcaps & IEEE80211_HTC_HT) {
+ /* driver can override defaults */
+ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
+ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
+ vap->iv_ampdu_limit = vap->iv_ampdu_rxmax;
+ vap->iv_amsdu_limit = vap->iv_htcaps & IEEE80211_HTCAP_MAXAMSDU;
+ /* tx aggregation traffic thresholds */
+ vap->iv_ampdu_mintraffic[WME_AC_BK] = 128;
+ vap->iv_ampdu_mintraffic[WME_AC_BE] = 64;
+ vap->iv_ampdu_mintraffic[WME_AC_VO] = 32;
+ vap->iv_ampdu_mintraffic[WME_AC_VI] = 32;
+
+ if (vap->iv_htcaps & IEEE80211_HTC_HT) {
/*
* Device is HT capable; enable all HT-related
* facilities by default.
* XXX these choices may be too aggressive.
*/
- ic->ic_flags_ext |= IEEE80211_FEXT_HT
- | IEEE80211_FEXT_HTCOMPAT
- ;
- if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+ vap->iv_flags_ext |= IEEE80211_FEXT_HT
+ | IEEE80211_FEXT_HTCOMPAT
+ ;
+ if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20)
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI20;
/* XXX infer from channel list? */
- if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
- ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
- if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+ if (vap->iv_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
+ vap->iv_flags_ext |= IEEE80211_FEXT_USEHT40;
+ if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40)
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI40;
}
/* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
- if (ic->ic_htcaps & IEEE80211_HTC_AMPDU)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
- if (ic->ic_htcaps & IEEE80211_HTC_AMSDU)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+ if (vap->iv_htcaps & IEEE80211_HTC_AMPDU)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+ if (vap->iv_htcaps & IEEE80211_HTC_AMSDU)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
}
+ /* NB: disable default legacy WDS, too many issues right now */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_HT;
}
void
-ieee80211_ht_detach(struct ieee80211com *ic)
+ieee80211_ht_vdetach(struct ieee80211vap *vap)
{
}
@@ -170,7 +197,7 @@ ht_announce(struct ieee80211com *ic, int mode,
rs->rs_rates[i] | IEEE80211_RATE_MCS, mode);
if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS)
continue;
- rate = ieee80211_htrates[rs->rs_rates[i]];
+ rate = ieee80211_htrates[rs->rs_rates[i]].ht40_rate_400ns;
printf("%s%d%sMbps", (i != 0 ? " " : ""),
rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
}
@@ -205,14 +232,14 @@ ieee80211_get_suphtrates(struct ieee80211com *ic,
struct mbuf *
ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
int framelen;
struct mbuf *n;
/* discard 802.3 header inserted by ieee80211_decap */
m_adj(m, sizeof(struct ether_header));
- ic->ic_stats.is_amsdu_decap++;
+ vap->iv_stats.is_amsdu_decap++;
for (;;) {
/*
@@ -223,23 +250,23 @@ ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
*/
m = ieee80211_decap1(m, &framelen);
if (m == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "a-msdu", "%s", "decap failed");
- ic->ic_stats.is_amsdu_tooshort++;
+ vap->iv_stats.is_amsdu_tooshort++;
return NULL;
}
if (m->m_pkthdr.len == framelen)
break;
n = m_split(m, framelen, M_NOWAIT);
if (n == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "a-msdu",
"%s", "unable to split encapsulated frames");
- ic->ic_stats.is_amsdu_split++;
+ vap->iv_stats.is_amsdu_split++;
m_freem(m); /* NB: must reclaim */
return NULL;
}
- ieee80211_deliver_data(ic, ni, m);
+ vap->iv_deliver_data(vap, ni, m);
/*
* Remove frame contents; each intermediate frame
@@ -252,19 +279,6 @@ ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m)
}
/*
- * Start A-MPDU rx/re-order processing for the specified TID.
- */
-static void
-ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
-{
- memset(rap, 0, sizeof(*rap));
- rap->rxa_wnd = (bufsiz == 0) ?
- IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
- rap->rxa_start = start;
- rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND;
-}
-
-/*
* Purge all frames in the A-MPDU re-order queue.
*/
static void
@@ -289,13 +303,33 @@ ampdu_rx_purge(struct ieee80211_rx_ampdu *rap)
}
/*
+ * Start A-MPDU rx/re-order processing for the specified TID.
+ */
+static void
+ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
+{
+ if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) {
+ /*
+ * AMPDU previously setup and not terminated with a DELBA,
+ * flush the reorder q's in case anything remains.
+ */
+ ampdu_rx_purge(rap);
+ }
+ memset(rap, 0, sizeof(*rap));
+ rap->rxa_wnd = (bufsiz == 0) ?
+ IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
+ rap->rxa_start = start;
+ rap->rxa_flags |= IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND;
+}
+
+/*
* Stop A-MPDU rx processing for the specified TID.
*/
static void
ampdu_rx_stop(struct ieee80211_rx_ampdu *rap)
{
- rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND;
ampdu_rx_purge(rap);
+ rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND);
}
/*
@@ -309,7 +343,7 @@ ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
{
m->m_flags |= M_AMPDU; /* bypass normal processing */
/* NB: rssi, noise, and rstamp are ignored w/ M_AMPDU set */
- (void) ieee80211_input(ni->ni_ic, m, ni, 0, 0, 0);
+ (void) ieee80211_input(ni, m, 0, 0, 0);
}
/*
@@ -323,7 +357,7 @@ ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
static void
ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct mbuf *m;
int i;
@@ -353,14 +387,14 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
}
}
KASSERT(n == 0, ("lost %d frames", n));
- ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+ vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
}
/*
* Adjust the start of the BA window to
* reflect the frames just dispatched.
*/
rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
- ic->ic_stats.is_ampdu_rx_oor += i;
+ vap->iv_stats.is_ampdu_rx_oor += i;
}
#ifdef IEEE80211_AMPDU_AGE
@@ -370,7 +404,7 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
static void
ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct mbuf *m;
int i;
@@ -381,7 +415,7 @@ ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
rap->rxa_m[i] = NULL;
rap->rxa_qbytes -= m->m_pkthdr.len;
rap->rxa_qframes--;
- ic->ic_stats.is_ampdu_rx_oor++;
+ vap->iv_stats.is_ampdu_rx_oor++;
ampdu_dispatch(ni, m);
if (rap->rxa_qframes == 0)
@@ -399,7 +433,7 @@ static void
ampdu_rx_flush_upto(struct ieee80211_node *ni,
struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct mbuf *m;
ieee80211_seq seqno;
int i;
@@ -418,7 +452,7 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
rap->rxa_m[i] = NULL;
rap->rxa_qbytes -= m->m_pkthdr.len;
rap->rxa_qframes--;
- ic->ic_stats.is_ampdu_rx_oor++;
+ vap->iv_stats.is_ampdu_rx_oor++;
ampdu_dispatch(ni, m);
} else {
@@ -433,6 +467,10 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
*/
if (rap->rxa_qframes != 0) {
int n = rap->rxa_qframes, j;
+
+ /* NB: this loop assumes i > 0 and/or rxa_m[0] is NULL */
+ KASSERT(rap->rxa_m[0] == NULL,
+ ("%s: BA window slot 0 occupied", __func__));
for (j = i+1; j < rap->rxa_wnd; j++) {
if (rap->rxa_m[j] != NULL) {
rap->rxa_m[j-i] = rap->rxa_m[j];
@@ -446,7 +484,7 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
__func__, n, rap->rxa_qframes, i, rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
winstart));
- ic->ic_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+ vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
}
/*
* Move the start of the BA window; we use the
@@ -472,7 +510,7 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
(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 ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_qosframe *wh;
struct ieee80211_rx_ampdu *rap;
ieee80211_seq rxseq;
@@ -557,7 +595,7 @@ again:
* frame; flush the reorder buffer.
*/
if (rap->rxa_qframes != 0) {
- ic->ic_stats.is_ampdu_rx_age +=
+ vap->iv_stats.is_ampdu_rx_age +=
rap->rxa_qframes;
ampdu_rx_flush(ni, rap);
}
@@ -576,15 +614,15 @@ again:
rap->rxa_m[off] = m;
rap->rxa_qframes++;
rap->rxa_qbytes += m->m_pkthdr.len;
- ic->ic_stats.is_ampdu_rx_reorder++;
+ vap->iv_stats.is_ampdu_rx_reorder++;
} else {
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
ni->ni_macaddr, "a-mpdu duplicate",
"seqno %u tid %u BA win <%u:%u>",
rxseq, tid, rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1));
- ic->ic_stats.is_rx_dup++;
+ vap->iv_stats.is_rx_dup++;
IEEE80211_NODE_STAT(ni, rx_dup);
m_freem(m);
}
@@ -596,12 +634,12 @@ again:
* flush the reorder q and move the window.
* Sec 9.10.7.6 b) (D2.04 p.118 line 60)
*/
- IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"move BA win <%u:%u> (%u frames) rxseq %u tid %u",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid);
- ic->ic_stats.is_ampdu_rx_move++;
+ vap->iv_stats.is_ampdu_rx_move++;
/*
* The spec says to flush frames up to but not including:
@@ -620,14 +658,14 @@ again:
* Outside the BA window and out of range; toss.
* Sec 9.10.7.6 c) (D2.04 p.119 line 16)
*/
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
- "MSDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
+ "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid,
wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
- ic->ic_stats.is_ampdu_rx_drop++;
+ vap->iv_stats.is_ampdu_rx_drop++;
IEEE80211_NODE_STAT(ni, rx_drop);
m_freem(m);
return CONSUMED;
@@ -645,7 +683,7 @@ again:
void
ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_frame_bar *wh;
struct ieee80211_rx_ampdu *rap;
ieee80211_seq rxseq;
@@ -653,10 +691,10 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
if (!ieee80211_recv_bar_ena) {
#if 0
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_11N,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_11N,
ni->ni_macaddr, "BAR", "%s", "processing disabled");
#endif
- ic->ic_stats.is_ampdu_bar_bad++;
+ vap->iv_stats.is_ampdu_bar_bad++;
return;
}
wh = mtod(m0, struct ieee80211_frame_bar *);
@@ -667,13 +705,13 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
/*
* No ADDBA request yet, don't touch.
*/
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N,
ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid);
- ic->ic_stats.is_ampdu_bar_bad++;
+ vap->iv_stats.is_ampdu_bar_bad++;
return;
}
- ic->ic_stats.is_ampdu_bar_rx++;
+ vap->iv_stats.is_ampdu_bar_rx++;
rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
if (rxseq == rap->rxa_start)
return;
@@ -684,12 +722,12 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
* Flush the reorder q up to rxseq and move the window.
* Sec 9.10.7.6 a) (D2.04 p.119 line 22)
*/
- IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid);
- ic->ic_stats.is_ampdu_bar_move++;
+ vap->iv_stats.is_ampdu_bar_move++;
ampdu_rx_flush_upto(ni, rap, rxseq);
if (off >= rap->rxa_wnd) {
@@ -705,14 +743,14 @@ ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0)
* Out of range; toss.
* Sec 9.10.7.6 b) (D2.04 p.119 line 41)
*/
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr,
"BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s",
rap->rxa_start,
IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
rap->rxa_qframes, rxseq, tid,
wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
- ic->ic_stats.is_ampdu_bar_oow++;
+ vap->iv_stats.is_ampdu_bar_oow++;
IEEE80211_NODE_STAT(ni, rx_drop);
}
}
@@ -767,6 +805,8 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
*/
ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]);
IEEE80211_TAPQ_DESTROY(tap);
+ tap->txa_lastsample = 0;
+ tap->txa_avgpps = 0;
/* NB: clearing NAK means we may re-send ADDBA */
tap->txa_flags &=
~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
@@ -780,6 +820,45 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
IEEE80211_NODE_AMPDU);
}
+/*
+ * Age out HT resources for a station.
+ */
+void
+ieee80211_ht_node_age(struct ieee80211_node *ni)
+{
+#ifdef IEEE80211_AMPDU_AGE
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t tid;
+#endif
+
+ KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta"));
+
+#ifdef IEEE80211_AMPDU_AGE
+ for (tid = 0; tid < WME_NUM_TID; tid++) {
+ struct ieee80211_rx_ampdu *rap;
+
+ rap = &ni->ni_rx_ampdu[tid];
+ if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0)
+ continue;
+ if (rap->rxa_qframes == 0)
+ continue;
+ /*
+ * Check for frames sitting too long in the reorder queue.
+ * See above for more details on what's happening here.
+ */
+ /* XXX honor batimeout? */
+ if (ticks - rap->rxa_age > ieee80211_ampdu_age) {
+ /*
+ * Too long since we received the first
+ * frame; flush the reorder buffer.
+ */
+ vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes;
+ ampdu_rx_flush(ni, rap);
+ }
+ }
+#endif /* IEEE80211_AMPDU_AGE */
+}
+
static struct ieee80211_channel *
findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags)
{
@@ -832,11 +911,11 @@ ieee80211_ht_adjust_channel(struct ieee80211com *ic,
void
ieee80211_ht_wds_init(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_tx_ampdu *tap;
int ac;
- KASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT, ("no HT requested"));
+ KASSERT(vap->iv_flags_ext & IEEE80211_FEXT_HT, ("no HT requested"));
/* XXX check scan cache in case peer has an ap and we have info */
/*
@@ -845,11 +924,11 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
* AP) is suitable use it so we use the same location
* for the extension channel).
*/
- ni->ni_chan = ieee80211_ht_adjust_channel(ic, ni->ni_chan,
- ic->ic_flags_ext);
+ ni->ni_chan = ieee80211_ht_adjust_channel(ni->ni_ic,
+ ni->ni_chan, ieee80211_htchanflags(ni->ni_chan));
ni->ni_htcap = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20)
ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20;
if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40;
@@ -858,7 +937,7 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE;
else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40)
ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40;
} else {
ni->ni_chw = 20;
@@ -883,20 +962,30 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
static void
htinfo_notify(struct ieee80211com *ic)
{
- if (ic->ic_opmode != IEEE80211_M_HOSTAP)
- return;
- IEEE80211_NOTE(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
- ic->ic_bss,
- "HT bss occupancy change: %d sta, %d ht, "
- "%d ht40%s, HT protmode now 0x%x"
- , ic->ic_sta_assoc
- , ic->ic_ht_sta_assoc
- , ic->ic_ht40_sta_assoc
- , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ?
- ", non-HT sta present" : ""
- , ic->ic_curhtprotmode);
- ieee80211_beacon_notify(ic, IEEE80211_BEACON_HTINFO);
+ struct ieee80211vap *vap;
+ int first = 1;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP)
+ continue;
+ if (first) {
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
+ vap->iv_bss,
+ "HT bss occupancy change: %d sta, %d ht, "
+ "%d ht40%s, HT protmode now 0x%x"
+ , ic->ic_sta_assoc
+ , ic->ic_ht_sta_assoc
+ , ic->ic_ht40_sta_assoc
+ , (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ?
+ ", non-HT sta present" : ""
+ , ic->ic_curhtprotmode);
+ first = 0;
+ }
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO);
+ }
}
/*
@@ -908,13 +997,14 @@ htinfo_update(struct ieee80211com *ic)
{
uint8_t protmode;
- if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) {
- protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
- | IEEE80211_HTINFO_NONHT_PRESENT;
- } else if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
+ if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
protmode = IEEE80211_HTINFO_OPMODE_MIXED
- | IEEE80211_HTINFO_NONHT_PRESENT;
- } else if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
+ | IEEE80211_HTINFO_NONHT_PRESENT;
+ } else if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) {
+ protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
+ | IEEE80211_HTINFO_NONHT_PRESENT;
+ } else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) {
protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
} else {
@@ -964,15 +1054,36 @@ ieee80211_ht_node_leave(struct ieee80211_node *ni)
/*
* Public version of htinfo_update; used for processing
- * beacon frames from overlapping bss in hostap_recv_mgmt.
+ * beacon frames from overlapping bss.
+ *
+ * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED
+ * (on receipt of a beacon that advertises MIXED) or
+ * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon
+ * from an overlapping legacy bss). We treat MIXED with
+ * a higher precedence than PROTOPT (i.e. we will not change
+ * change PROTOPT -> MIXED; only MIXED -> PROTOPT). This
+ * corresponds to how we handle things in htinfo_update.
*/
void
-ieee80211_htinfo_update(struct ieee80211com *ic, int protmode)
+ieee80211_htprot_update(struct ieee80211com *ic, int protmode)
{
- if (protmode != ic->ic_curhtprotmode) {
- ic->ic_curhtprotmode = protmode;
- htinfo_notify(ic);
- }
+#define OPMODE(x) SM(x, IEEE80211_HTINFO_OPMODE)
+ if (protmode == ic->ic_curhtprotmode)
+ return;
+ if (OPMODE(ic->ic_curhtprotmode) == IEEE80211_HTINFO_OPMODE_MIXED &&
+ OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)
+ return;
+
+ /* track non-HT station presence */
+ KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT,
+ ("missing NONHT_PRESENT"));
+ ic->ic_flags_ext |= IEEE80211_FEXT_NONHT_PR;
+ ic->ic_lastnonht = ticks;
+
+ /* push beacon update */
+ ic->ic_curhtprotmode = protmode;
+ htinfo_notify(ic);
+#undef OPMODE
}
/*
@@ -991,7 +1102,7 @@ ieee80211_ht_timeout(struct ieee80211com *ic)
if ((ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) &&
time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
#if 0
- IEEE80211_NOTE(ic, IEEE80211_MSG_11N, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
"%s", "time out non-HT STA present on channel");
#endif
ic->ic_flags_ext &= ~IEEE80211_FEXT_NONHT_PR;
@@ -1011,7 +1122,7 @@ ieee80211_ht_timeout(struct ieee80211com *ic)
void
ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
if (ie[0] == IEEE80211_ELEMID_VENDOR) {
/*
@@ -1029,7 +1140,7 @@ ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie)
ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)];
/* XXX needed or will ieee80211_parse_htinfo always be called? */
ni->ni_chw = (ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20;
+ (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20;
}
/*
@@ -1044,6 +1155,7 @@ void
ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
{
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_ie_htinfo *htinfo;
struct ieee80211_channel *c;
uint16_t w;
@@ -1063,11 +1175,11 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
* identify the right channel to use. If we cannot locate it
* in the channel table then fallback to legacy operation.
*/
- htflags = (ic->ic_flags_ext & IEEE80211_FEXT_HT) ?
- IEEE80211_CHAN_HT20 : 0;
/* NB: honor operating mode constraint */
+ htflags = (vap->iv_flags_ext & IEEE80211_FEXT_HT) ?
+ IEEE80211_CHAN_HT20 : 0;
if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) {
+ (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)) {
if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE)
htflags = IEEE80211_CHAN_HT40U;
else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
@@ -1076,20 +1188,20 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags;
if (chanflags != ni->ni_chan->ic_flags) {
c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags);
- if (c == NULL && htflags != IEEE80211_CHAN_HT20) {
+ if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) {
/*
* No HT40 channel entry in our table; fall back
* to HT20 operation. This should not happen.
*/
c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20);
- IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
"no HT40 channel (freq %u), falling back to HT20",
ni->ni_chan->ic_freq);
/* XXX stat */
}
if (c != NULL && c != ni->ni_chan) {
- IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
"switch station to HT%d channel %u/0x%x",
IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
@@ -1108,7 +1220,7 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie)
int
ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_ie_htcap *htcap;
struct ieee80211_htrateset *rs;
int i;
@@ -1123,11 +1235,11 @@ ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags)
if (isclr(htcap->hc_mcsset, i))
continue;
if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
"WARNING, HT rate set too large; only "
"using %u rates", IEEE80211_HTRATE_MAXSIZE);
- ic->ic_stats.is_rx_rstoobig++;
+ vap->iv_stats.is_rx_rstoobig++;
break;
}
rs->rs_rates[rs->rs_nrates++] = i;
@@ -1152,7 +1264,7 @@ ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie)
htinfo = (const struct ieee80211_ie_htinfo *) ie;
rs = &ni->ni_htrates;
if (rs->rs_nrates == 0) {
- IEEE80211_NOTE(ni->ni_ic,
+ IEEE80211_NOTE(ni->ni_vap,
IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
"%s", "WARNING, empty HT rate set");
return;
@@ -1180,10 +1292,10 @@ static void
addba_start_timeout(struct ieee80211_tx_ampdu *tap)
{
/* XXX use CALLOUT_PENDING instead? */
- callout_reset(&tap->txa_timer, IEEE80211_AGGR_TIMEOUT,
+ callout_reset(&tap->txa_timer, ieee80211_addba_timeout,
addba_timeout, tap);
tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
- tap->txa_lastrequest = ticks;
+ tap->txa_nextrequest = ticks + ieee80211_addba_timeout;
}
static void
@@ -1274,6 +1386,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
const uint8_t *frm, const uint8_t *efrm)
{
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_action *ia;
struct ieee80211_rx_ampdu *rap;
struct ieee80211_tx_ampdu *tap;
@@ -1295,7 +1408,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
tid = MS(baparamset, IEEE80211_BAPS_TID);
bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"recv ADDBA request: dialogtoken %u "
"baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
@@ -1314,19 +1427,19 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
* violates the 11n spec and is mostly for testing).
*/
if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)) {
+ (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_RX)) {
ampdu_rx_start(rap, bufsiz,
MS(baseqctl, IEEE80211_BASEQ_START));
args[1] = IEEE80211_STATUS_SUCCESS;
} else {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "reject ADDBA request: %s",
ni->ni_flags & IEEE80211_NODE_AMPDU_RX ?
"administratively disabled" :
"not negotiated for station");
- ic->ic_stats.is_addba_reject++;
+ vap->iv_stats.is_addba_reject++;
args[1] = IEEE80211_STATUS_UNSPECIFIED;
}
/* XXX honor rap flags? */
@@ -1350,26 +1463,26 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
ac = TID_TO_WME_AC(tid);
tap = &ni->ni_tx_ampdu[ac];
if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni->ni_macaddr, "ADDBA response",
"no pending ADDBA, tid %d dialogtoken %u "
"code %d", tid, dialogtoken, code);
- ic->ic_stats.is_addba_norequest++;
+ vap->iv_stats.is_addba_norequest++;
return;
}
if (dialogtoken != tap->txa_token) {
- IEEE80211_DISCARD_MAC(ic,
+ IEEE80211_DISCARD_MAC(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni->ni_macaddr, "ADDBA response",
"dialogtoken mismatch: waiting for %d, "
"received %d, tid %d code %d",
tap->txa_token, dialogtoken, tid, code);
- ic->ic_stats.is_addba_badtoken++;
+ vap->iv_stats.is_addba_badtoken++;
return;
}
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"recv ADDBA response: dialogtoken %u code %d "
"baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
@@ -1385,7 +1498,7 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
tid = MS(baparamset, IEEE80211_DELBAPS_TID);
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"recv DELBA: baparamset 0x%x (tid %d initiator %d) "
"code %d", baparamset, tid,
@@ -1416,18 +1529,18 @@ void
ieee80211_recv_action(struct ieee80211_node *ni,
const uint8_t *frm, const uint8_t *efrm)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_action *ia;
int chw;
ia = (const struct ieee80211_action *) frm;
switch (ia->ia_category) {
case IEEE80211_ACTION_CAT_BA:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: BA action %d not implemented", __func__,
ia->ia_action);
- ic->ic_stats.is_rx_mgtdiscard++;
+ vap->iv_stats.is_rx_mgtdiscard++;
break;
case IEEE80211_ACTION_CAT_HT:
switch (ia->ia_action) {
@@ -1437,7 +1550,7 @@ ieee80211_recv_action(struct ieee80211_node *ni,
ni->ni_chw = chw;
ni->ni_flags |= IEEE80211_NODE_CHWUPDATE;
}
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: HT txchwidth, width %d (%s)",
__func__, chw,
@@ -1445,25 +1558,25 @@ ieee80211_recv_action(struct ieee80211_node *ni,
"new" : "no change");
break;
case IEEE80211_ACTION_HT_MIMOPWRSAVE:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: HT MIMO PS", __func__);
break;
default:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: HT action %d not implemented", __func__,
ia->ia_action);
- ic->ic_stats.is_rx_mgtdiscard++;
+ vap->iv_stats.is_rx_mgtdiscard++;
break;
}
break;
default:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: category %d not implemented", __func__,
ia->ia_category);
- ic->ic_stats.is_rx_mgtdiscard++;
+ vap->iv_stats.is_rx_mgtdiscard++;
break;
}
}
@@ -1473,6 +1586,39 @@ ieee80211_recv_action(struct ieee80211_node *ni,
*/
/*
+ * Check if A-MPDU should be requested/enabled for a stream.
+ * We require a traffic rate above a per-AC threshold and we
+ * also handle backoff from previous failed attempts.
+ *
+ * Drivers may override this method to bring in information
+ * such as link state conditions in making the decision.
+ */
+static int
+ieee80211_ampdu_enable(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[tap->txa_ac])
+ return 0;
+ /* XXX check rssi? */
+ if (tap->txa_attempts >= ieee80211_addba_maxtries &&
+ ticks < tap->txa_nextrequest) {
+ /*
+ * Don't retry too often; txa_nextrequest is set
+ * to the minimum interval we'll retry after
+ * ieee80211_addba_maxtries failed attempts are made.
+ */
+ return 0;
+ }
+ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
+ "%s: enable AMPDU on %s, avgpps %d pkts %d",
+ __func__, ieee80211_wme_acnames[tap->txa_ac],
+ tap->txa_avgpps, tap->txa_pkts);
+ return 1;
+}
+
+/*
* Request A-MPDU tx aggregation. Setup local state and
* issue an ADDBA request. BA use will only happen after
* the other end replies with ADDBA response.
@@ -1493,16 +1639,6 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
tap->txa_flags |= IEEE80211_AGGR_SETUP;
}
- if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES &&
- (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) {
- /*
- * Don't retry too often; IEEE80211_AGGR_MINRETRY
- * defines the minimum interval we'll retry after
- * IEEE80211_AGGR_MAXTRIES failed attempts to
- * negotiate use.
- */
- return 0;
- }
/* XXX hack for not doing proper locking */
tap->txa_flags &= ~IEEE80211_AGGR_NAK;
@@ -1521,12 +1657,14 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
/* NB: do first so there's no race against reply */
if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) {
/* unable to setup state, don't make request */
- IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_11N,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
ni, "%s: could not setup BA stream for AC %d",
__func__, tap->txa_ac);
/* defer next try so we don't slam the driver with requests */
- tap->txa_attempts = IEEE80211_AGGR_MAXTRIES;
- tap->txa_lastrequest = ticks;
+ tap->txa_attempts = ieee80211_addba_maxtries;
+ /* NB: check in case driver wants to override */
+ if (tap->txa_nextrequest <= ticks)
+ tap->txa_nextrequest = ticks + ieee80211_addba_backoff;
return 0;
}
tokens = dialogtoken; /* allocate token */
@@ -1542,13 +1680,14 @@ void
ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
{
struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
uint16_t args[4];
/* XXX locking */
if (IEEE80211_AMPDU_RUNNING(tap)) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "%s: stop BA stream for AC %d", __func__, tap->txa_ac);
- ic->ic_stats.is_ampdu_stop++;
+ vap->iv_stats.is_ampdu_stop++;
ic->ic_addba_stop(ni, tap);
args[0] = WME_AC_TO_TID(tap->txa_ac);
@@ -1557,10 +1696,10 @@ ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA,
IEEE80211_ACTION_BA_DELBA, args);
} else {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "%s: BA stream for AC %d not running",
__func__, tap->txa_ac);
- ic->ic_stats.is_ampdu_stop_failed++;
+ vap->iv_stats.is_ampdu_stop_failed++;
}
}
@@ -1573,14 +1712,14 @@ int
ieee80211_send_bar(struct ieee80211_node *ni,
const struct ieee80211_tx_ampdu *tap)
{
-#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_frame_min *wh;
struct mbuf *m;
uint8_t *frm;
@@ -1601,7 +1740,7 @@ ieee80211_send_bar(struct ieee80211_node *ni,
IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
wh->i_fc[1] = 0;
IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
tid = WME_AC_TO_TID(tap->txa_ac);
barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
@@ -1617,17 +1756,15 @@ ieee80211_send_bar(struct ieee80211_node *ni,
ADDSHORT(frm, barseqctl);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+ M_WME_SETAC(m, WME_AC_VO);
+
IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */
- IEEE80211_NOTE(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
ni, "send bar frame (tid %u start %u) on channel %u",
tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan));
- m->m_pkthdr.rcvif = (void *)ni;
- IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */
- if_start(ifp);
-
- return 0;
+ return ic->ic_raw_xmit(ni, m, NULL);
bad:
ieee80211_free_node(ni);
return ret;
@@ -1644,12 +1781,13 @@ int
ieee80211_send_action(struct ieee80211_node *ni,
int category, int action, uint16_t args[4])
{
-#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
+#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct mbuf *m;
uint8_t *frm;
@@ -1663,7 +1801,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
* the xmit is complete all the way in the driver. On error we
* will remove our reference.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
@@ -1685,7 +1823,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
case IEEE80211_ACTION_CAT_BA:
switch (action) {
case IEEE80211_ACTION_BA_ADDBA_REQUEST:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"send ADDBA request: dialogtoken %d "
"baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x",
@@ -1698,7 +1836,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
ADDSHORT(frm, args[3]); /* baseqctl */
break;
case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"send ADDBA response: dialogtoken %d status %d "
"baparamset 0x%x (tid %d) batimeout %d",
@@ -1718,7 +1856,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
ADDSHORT(frm, baparamset);
ADDSHORT(frm, args[2]); /* reason code */
- IEEE80211_NOTE(ic,
+ 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]);
@@ -1730,12 +1868,12 @@ ieee80211_send_action(struct ieee80211_node *ni,
case IEEE80211_ACTION_CAT_HT:
switch (action) {
case IEEE80211_ACTION_HT_TXCHWIDTH:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
ni, "send HT txchwidth: width %d",
- IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ? 40 : 20
+ IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20
);
- *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) ?
+ *frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ?
IEEE80211_A_HT_TXCHWIDTH_2040 :
IEEE80211_A_HT_TXCHWIDTH_20;
break;
@@ -1745,7 +1883,7 @@ ieee80211_send_action(struct ieee80211_node *ni,
break;
default:
badaction:
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
"%s: unsupported category %d action %d", __func__,
category, action);
@@ -1754,12 +1892,11 @@ ieee80211_send_action(struct ieee80211_node *ni,
}
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- ret = ieee80211_mgmt_output(ic, ni, m, IEEE80211_FC0_SUBTYPE_ACTION);
- if (ret != 0)
- goto bad;
- return 0;
+ return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION);
bad:
ieee80211_free_node(ni);
+ if (m != NULL)
+ m_freem(m);
return ret;
#undef ADDSHORT
#undef senderr
@@ -1794,12 +1931,12 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
uint16_t caps;
int rxmax, density;
/* HT capabilities */
- caps = ic->ic_htcaps & 0xffff;
+ caps = vap->iv_htcaps & 0xffff;
/*
* Note channel width depends on whether we are operating as
* a sta or not. When operating as a sta we are generating
@@ -1808,9 +1945,9 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
* how we've been setup (which might be different if a fixed
* channel is specified).
*/
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
/* override 20/40 use based on config */
- if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)
caps |= IEEE80211_HTCAP_CHWIDTH40;
else
caps &= ~IEEE80211_HTCAP_CHWIDTH40;
@@ -1819,17 +1956,17 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
} else {
/* override 20/40 use based on current channel */
- if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
caps |= IEEE80211_HTCAP_CHWIDTH40;
else
caps &= ~IEEE80211_HTCAP_CHWIDTH40;
- rxmax = ic->ic_ampdu_rxmax;
- density = ic->ic_ampdu_density;
+ rxmax = vap->iv_ampdu_rxmax;
+ density = vap->iv_ampdu_density;
}
/* adjust short GI based on channel and config */
- if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
caps &= ~IEEE80211_HTCAP_SHORTGI20;
- if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
(caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
caps &= ~IEEE80211_HTCAP_SHORTGI40;
ADDSHORT(frm, caps);
@@ -1909,23 +2046,25 @@ ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
* Update the HTINFO ie for a beacon frame.
*/
void
-ieee80211_ht_update_beacon(struct ieee80211com *ic,
+ieee80211_ht_update_beacon(struct ieee80211vap *vap,
struct ieee80211_beacon_offsets *bo)
{
#define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
+ const struct ieee80211_channel *bsschan = vap->iv_bss->ni_chan;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_ie_htinfo *ht =
(struct ieee80211_ie_htinfo *) bo->bo_htinfo;
/* XXX only update on channel change */
- ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+ ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan);
ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH;
- if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40U(bsschan))
ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
- else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
+ else if (IEEE80211_IS_CHAN_HT40D(bsschan))
ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW;
else
ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE;
- if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40(bsschan))
ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
/* protection mode */
@@ -1951,16 +2090,16 @@ ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni)
memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2);
/* primary/control channel center */
- *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+ *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
- if (IEEE80211_IS_CHAN_HT40U(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
- else if (IEEE80211_IS_CHAN_HT40D(ic->ic_bsschan))
+ else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
else
frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
- if (IEEE80211_IS_CHAN_HT40(ic->ic_bsschan))
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
frm[1] = ic->ic_curhtprotmode;
diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h
index d1d6699..80052cf 100644
--- a/sys/net80211/ieee80211_ht.h
+++ b/sys/net80211/ieee80211_ht.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -46,13 +46,16 @@ struct ieee80211_tx_ampdu {
#define IEEE80211_AGGR_NAK 0x0010 /* peer NAK'd ADDBA request */
uint8_t txa_ac;
uint8_t txa_token; /* dialog token */
+ int txa_lastsample; /* ticks @ last traffic sample */
+ int txa_pkts; /* packets over last sample interval */
+ int txa_avgpps; /* filtered traffic over window */
int txa_qbytes; /* data queued (bytes) */
short txa_qframes; /* data queued (frames) */
ieee80211_seq txa_seqstart;
ieee80211_seq txa_start;
uint16_t txa_wnd; /* BA window size */
- uint8_t txa_attempts; /* # setup attempts */
- int txa_lastrequest;/* time of last ADDBA request */
+ uint8_t txa_attempts; /* # ADDBA requests w/o a response */
+ int txa_nextrequest;/* soonest to make next ADDBA request */
struct ifqueue txa_q; /* packet queue */
struct callout txa_timer;
void *txa_private; /* driver-private storage */
@@ -67,6 +70,65 @@ struct ieee80211_tx_ampdu {
(((tap)->txa_flags & \
(IEEE80211_AGGR_RUNNING|IEEE80211_AGGR_XCHGPEND|IEEE80211_AGGR_NAK)) != 0)
+/*
+ * Traffic estimator support. We estimate packets/sec for
+ * each AC that is setup for AMPDU or will potentially be
+ * setup for AMPDU. The traffic rate can be used to decide
+ * when AMPDU should be setup (according to a threshold)
+ * and is available for drivers to do things like cache
+ * eviction when only a limited number of BA streams are
+ * available and more streams are requested than available.
+ */
+
+static void __inline
+ieee80211_txampdu_update_pps(struct ieee80211_tx_ampdu *tap)
+{
+ /* NB: scale factor of 2 was picked heuristically */
+ tap->txa_avgpps = ((tap->txa_avgpps << 2) -
+ tap->txa_avgpps + tap->txa_pkts) >> 2;
+}
+
+/*
+ * Count a packet towards the pps estimate.
+ */
+static void __inline
+ieee80211_txampdu_count_packet(struct ieee80211_tx_ampdu *tap)
+{
+ /* XXX bound loop/do more crude estimate? */
+ while (ticks - tap->txa_lastsample >= hz) {
+ ieee80211_txampdu_update_pps(tap);
+ /* reset to start new sample interval */
+ tap->txa_pkts = 0;
+ if (tap->txa_avgpps == 0) {
+ tap->txa_lastsample = ticks;
+ break;
+ }
+ tap->txa_lastsample += hz;
+ }
+ tap->txa_pkts++;
+}
+
+/*
+ * Get the current pps estimate. If the average is out of
+ * date due to lack of traffic then we decay the estimate
+ * to account for the idle time.
+ */
+static int __inline
+ieee80211_txampdu_getpps(struct ieee80211_tx_ampdu *tap)
+{
+ /* XXX bound loop/do more crude estimate? */
+ while (ticks - tap->txa_lastsample >= hz) {
+ ieee80211_txampdu_update_pps(tap);
+ tap->txa_pkts = 0;
+ if (tap->txa_avgpps == 0) {
+ tap->txa_lastsample = ticks;
+ break;
+ }
+ tap->txa_lastsample += hz;
+ }
+ return tap->txa_avgpps;
+}
+
struct ieee80211_rx_ampdu {
int rxa_flags;
int rxa_qbytes; /* data queued (bytes) */
@@ -81,10 +143,18 @@ struct ieee80211_rx_ampdu {
void ieee80211_ht_attach(struct ieee80211com *);
void ieee80211_ht_detach(struct ieee80211com *);
+void ieee80211_ht_vattach(struct ieee80211vap *);
+void ieee80211_ht_vdetach(struct ieee80211vap *);
void ieee80211_ht_announce(struct ieee80211com *);
-extern const int ieee80211_htrates[16];
+struct ieee80211_mcs_rates {
+ uint16_t ht20_rate_800ns;
+ uint16_t ht20_rate_400ns;
+ uint16_t ht40_rate_800ns;
+ uint16_t ht40_rate_400ns;
+};
+extern const struct ieee80211_mcs_rates ieee80211_htrates[16];
const struct ieee80211_htrateset *ieee80211_get_suphtrates(
struct ieee80211com *, const struct ieee80211_channel *);
@@ -98,12 +168,14 @@ int ieee80211_ampdu_reorder(struct ieee80211_node *, struct mbuf *);
void ieee80211_recv_bar(struct ieee80211_node *, struct mbuf *);
void ieee80211_ht_node_init(struct ieee80211_node *, const uint8_t *);
void ieee80211_ht_node_cleanup(struct ieee80211_node *);
+void ieee80211_ht_node_age(struct ieee80211_node *);
+
struct ieee80211_channel *ieee80211_ht_adjust_channel(struct ieee80211com *,
struct ieee80211_channel *, int);
void ieee80211_ht_wds_init(struct ieee80211_node *);
void ieee80211_ht_node_join(struct ieee80211_node *);
void ieee80211_ht_node_leave(struct ieee80211_node *);
-void ieee80211_htinfo_update(struct ieee80211com *, int protmode);
+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 *);
@@ -122,6 +194,6 @@ 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 ieee80211com *,
+void ieee80211_ht_update_beacon(struct ieee80211vap *,
struct ieee80211_beacon_offsets *);
#endif /* _NET80211_IEEE80211_HT_H_ */
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index 9238bdf..9ae653f 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -35,591 +37,75 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/socket.h>
-
-#include <net/if.h>
-#include <net/if_media.h>
+
#include <net/ethernet.h>
+#include <net/if.h>
#include <net/if_llc.h>
+#include <net/if_media.h>
#include <net/if_vlan_var.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
#include <net/bpf.h>
-#ifdef IEEE80211_DEBUG
-#include <machine/stdarg.h>
-
-/*
- * Decide if a received management frame should be
- * printed when debugging is enabled. This filters some
- * of the less interesting frames that come frequently
- * (e.g. beacons).
- */
-static __inline int
-doprint(struct ieee80211com *ic, int subtype)
-{
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_BEACON:
- return (ic->ic_flags & IEEE80211_F_SCAN);
- case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
- return (ic->ic_opmode == IEEE80211_M_IBSS);
- }
- return 1;
-}
-
-static const uint8_t *ieee80211_getbssid(struct ieee80211com *,
- const struct ieee80211_frame *);
-#endif /* IEEE80211_DEBUG */
-
-static struct mbuf *ieee80211_defrag(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *, int);
-static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *, int);
-static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *,
- const uint8_t *mac, int subtype, int arg);
-static struct mbuf *ieee80211_decap_fastframe(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
-static void ieee80211_recv_pspoll(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
+#ifdef INET
+#include <netinet/in.h>
+#include <net/ethernet.h>
+#endif
-/*
- * Process a received frame. The node associated with the sender
- * should be supplied. If nothing was found in the node table then
- * the caller is assumed to supply a reference to ic_bss instead.
- * The RSSI and a timestamp are also supplied. The RSSI data is used
- * during AP scanning to select a AP to associate with; it can have
- * any units so long as values have consistent units and higher values
- * mean ``better signal''. The receive timestamp is currently not used
- * by the 802.11 layer.
- */
int
-ieee80211_input(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp)
+ieee80211_input_all(struct ieee80211com *ic,
+ struct mbuf *m, int rssi, int noise, u_int32_t rstamp)
{
-#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
-#define HAS_SEQ(type) ((type & 0x4) == 0)
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_frame *wh;
- struct ieee80211_key *key;
- struct ether_header *eh;
- int hdrspace, need_tap;
- uint8_t dir, type, subtype, qos;
- uint8_t *bssid;
- uint16_t rxseq;
-
- if (m->m_flags & M_AMPDU) {
- /*
- * Fastpath for A-MPDU reorder q resubmission. Frames
- * w/ M_AMPDU marked have already passed through here
- * but were received out of order and been held on the
- * reorder queue. When resubmitted they are marked
- * with the M_AMPDU flag and we can bypass most of the
- * normal processing.
- */
- wh = mtod(m, struct ieee80211_frame *);
- type = IEEE80211_FC0_TYPE_DATA;
- dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
- subtype = IEEE80211_FC0_SUBTYPE_QOS;
- hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
- need_tap = 0;
- goto resubmit_ampdu;
- }
-
- KASSERT(ni != NULL, ("null node"));
- ni->ni_inact = ni->ni_inact_reload;
-
- need_tap = 1; /* mbuf need to be tapped. */
- type = -1; /* undefined */
- /*
- * In monitor mode, send everything directly to bpf.
- * XXX may want to include the CRC
- */
- if (ic->ic_opmode == IEEE80211_M_MONITOR)
- goto out;
-
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, NULL,
- "too short (1): len %u", m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++;
- goto out;
- }
- /*
- * Bit of a cheat here, we use a pointer for a 3-address
- * frame format but don't reference fields past outside
- * ieee80211_frame_min w/o first validating the data is
- * present.
- */
- wh = mtod(m, struct ieee80211_frame *);
-
- if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
- IEEE80211_FC0_VERSION_0) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
- ic->ic_stats.is_rx_badversion++;
- goto err;
- }
-
- dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
- type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
- subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
- if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- bssid = wh->i_addr2;
- if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
- /* not interested in */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- bssid, NULL, "%s", "not to bss");
- ic->ic_stats.is_rx_wrongbss++;
- goto out;
- }
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_HOSTAP:
- if (dir != IEEE80211_FC1_DIR_NODS)
- bssid = wh->i_addr1;
- else if (type == IEEE80211_FC0_TYPE_CTL)
- bssid = wh->i_addr1;
- else {
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
- IEEE80211_DISCARD_MAC(ic,
- IEEE80211_MSG_ANY, ni->ni_macaddr,
- NULL, "too short (2): len %u",
- m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++;
- goto out;
- }
- bssid = wh->i_addr3;
- }
- if (type != IEEE80211_FC0_TYPE_DATA)
- break;
- /*
- * Data frame, validate the bssid.
- */
- if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) &&
- !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
- /* not interested in */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- bssid, NULL, "%s", "not to bss");
- ic->ic_stats.is_rx_wrongbss++;
- goto out;
- }
- /*
- * For adhoc mode we cons up a node when it doesn't
- * exist. This should probably done after an ACL check.
- */
- if (ni == ic->ic_bss &&
- ic->ic_opmode != IEEE80211_M_HOSTAP &&
- !IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
- /*
- * Fake up a node for this newly
- * discovered member of the IBSS.
- */
- ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
- wh->i_addr2);
- if (ni == NULL) {
- /* NB: stat kept for alloc failure */
- goto err;
- }
- }
- break;
- default:
- goto out;
- }
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- if (HAS_SEQ(type)) {
- uint8_t tid;
- if (IEEE80211_QOS_HAS_SEQ(wh)) {
- tid = ((struct ieee80211_qosframe *)wh)->
- i_qos[0] & IEEE80211_QOS_TID;
- if (TID_TO_WME_AC(tid) >= WME_AC_VI)
- ic->ic_wme.wme_hipri_traffic++;
- tid++;
- } else
- tid = IEEE80211_NONQOS_TID;
- rxseq = le16toh(*(uint16_t *)wh->i_seq);
- if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
- (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
- SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
- /* duplicate, discard */
- IEEE80211_DISCARD_MAC(ic, 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);
- ic->ic_stats.is_rx_dup++;
- IEEE80211_NODE_STAT(ni, rx_dup);
- goto out;
- }
- ni->ni_rxseqs[tid] = rxseq;
- }
- }
-
- switch (type) {
- case IEEE80211_FC0_TYPE_DATA:
- hdrspace = ieee80211_hdrspace(ic, wh);
- if (m->m_len < hdrspace &&
- (m = m_pullup(m, hdrspace)) == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, NULL,
- "data too short: expecting %u", hdrspace);
- ic->ic_stats.is_rx_tooshort++;
- goto out; /* XXX */
- }
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- if (dir != IEEE80211_FC1_DIR_FROMDS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto out;
- }
- if ((ifp->if_flags & IFF_SIMPLEX) &&
- IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) {
- /*
- * In IEEE802.11 network, multicast packet
- * sent from me is broadcasted from AP.
- * It should be silently discarded for
- * SIMPLEX interface.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, NULL, "%s", "multicast echo");
- ic->ic_stats.is_rx_mcastecho++;
- goto out;
- }
- break;
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- if (dir != IEEE80211_FC1_DIR_NODS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto out;
- }
- /* XXX no power-save support */
- break;
- case IEEE80211_M_HOSTAP:
- if (dir != IEEE80211_FC1_DIR_TODS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto out;
- }
- /* check if source STA is associated */
- if (ni == ic->ic_bss) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "%s", "unknown src");
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_NOT_AUTHED);
- ic->ic_stats.is_rx_notassoc++;
- goto err;
- }
- if (ni->ni_associd == 0) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "%s", "unassoc src");
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_NOT_ASSOCED);
- ic->ic_stats.is_rx_notassoc++;
- goto err;
- }
-
- /*
- * Check for power save state change.
- * XXX out-of-order A-MPDU frames?
- */
- if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
- (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
- ieee80211_node_pwrsave(ni,
- wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
- break;
- default:
- /* XXX here to keep compiler happy */
- goto out;
- }
-
- /*
- * Handle A-MPDU re-ordering. The station must be
- * associated and negotiated HT. The frame must be
- * a QoS frame (not QoS null data) and not previously
- * processed for A-MPDU re-ordering. If the frame is
- * to be processed directly then ieee80211_ampdu_reorder
- * will return 0; otherwise it has consumed the mbuf
- * and we should do nothing more with it.
- */
- if ((ni->ni_flags & IEEE80211_NODE_HT) &&
- subtype == IEEE80211_FC0_SUBTYPE_QOS &&
- ieee80211_ampdu_reorder(ni, m) != 0) {
- m = NULL;
- goto out;
- }
- resubmit_ampdu:
-
- /*
- * Handle privacy requirements. Note that we
- * must not be preempted from here until after
- * we (potentially) call ieee80211_crypto_demic;
- * otherwise we may violate assumptions in the
- * crypto cipher modules used to do delayed update
- * of replay sequence numbers.
- */
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
- /*
- * Discard encrypted frames when privacy is off.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "WEP", "%s", "PRIVACY off");
- ic->ic_stats.is_rx_noprivacy++;
- IEEE80211_NODE_STAT(ni, rx_noprivacy);
- goto out;
- }
- key = ieee80211_crypto_decap(ic, ni, m, hdrspace);
- if (key == NULL) {
- /* NB: stats+msgs handled in crypto_decap */
- IEEE80211_NODE_STAT(ni, rx_wepfail);
- goto out;
- }
- wh = mtod(m, struct ieee80211_frame *);
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
- } else {
- /* XXX M_WEP and IEEE80211_F_PRIVACY */
- key = NULL;
- }
-
- /*
- * Save QoS bits for use below--before we strip the header.
- */
- if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
- qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
- ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
- ((struct ieee80211_qosframe *)wh)->i_qos[0];
- } else
- qos = 0;
+ struct ieee80211vap *vap;
+ int type = -1;
- /*
- * Next up, any fragmentation.
- */
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
- m = ieee80211_defrag(ic, ni, m, hdrspace);
- if (m == NULL) {
- /* Fragment dropped or frame not complete yet */
- goto out;
- }
- }
- wh = NULL; /* no longer valid, catch any uses */
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ieee80211_node *ni;
+ struct mbuf *mcopy;
/*
- * Next strip any MSDU crypto bits.
+ * WDS vap's only receive directed traffic from the
+ * station at the ``far end''. That traffic should
+ * be passed through the AP vap the station is associated
+ * to--so don't spam them with mcast frames.
*/
- if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- ni->ni_macaddr, "data", "%s", "demic error");
- ic->ic_stats.is_rx_demicfail++;
- IEEE80211_NODE_STAT(ni, rx_demicfail);
- goto out;
- }
-
- /* copy to listener after decrypt */
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
- need_tap = 0;
-
- /*
- * Finally, strip the 802.11 header.
- */
- m = ieee80211_decap(ic, m, hdrspace);
- if (m == NULL) {
- /* don't count Null data frames as errors */
- if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
- subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
- goto out;
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- ni->ni_macaddr, "data", "%s", "decap error");
- ic->ic_stats.is_rx_decap++;
- IEEE80211_NODE_STAT(ni, rx_decap);
- goto err;
- }
- eh = mtod(m, struct ether_header *);
- if (!ieee80211_node_is_authorized(ni)) {
+ if (vap->iv_opmode == IEEE80211_M_WDS)
+ continue;
+ if (TAILQ_NEXT(vap, iv_next) != NULL) {
/*
- * Deny any non-PAE frames received prior to
- * authorization. For open/shared-key
- * authentication the port is mark authorized
- * after authentication completes. For 802.1x
- * the port is not marked authorized by the
- * authenticator until the handshake has completed.
+ * Packet contents are changed by ieee80211_decap
+ * so do a deep copy of the packet.
*/
- if (eh->ether_type != htons(ETHERTYPE_PAE)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
- eh->ether_shost, "data",
- "unauthorized port: ether type 0x%x len %u",
- eh->ether_type, m->m_pkthdr.len);
- ic->ic_stats.is_rx_unauth++;
- IEEE80211_NODE_STAT(ni, rx_unauth);
- goto err;
+ mcopy = m_dup(m, M_DONTWAIT);
+ if (mcopy == NULL) {
+ /* XXX stat+msg */
+ continue;
}
} else {
- /*
- * When denying unencrypted frames, discard
- * any non-PAE frames received without encryption.
- */
- if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
- (key == NULL && (m->m_flags & M_WEP) == 0) &&
- eh->ether_type != htons(ETHERTYPE_PAE)) {
- /*
- * Drop unencrypted frames.
- */
- ic->ic_stats.is_rx_unencrypted++;
- IEEE80211_NODE_STAT(ni, rx_unencrypted);
- goto out;
- }
- }
- /* XXX require HT? */
- if (qos & IEEE80211_QOS_AMSDU) {
- m = ieee80211_decap_amsdu(ni, m);
- if (m == NULL)
- return IEEE80211_FC0_TYPE_DATA;
- } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
-#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
- m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
- struct llc *llc;
-
- /*
- * Check for fast-frame tunnel encapsulation.
- */
- if (m->m_len < FF_LLC_SIZE &&
- (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, "fast-frame",
- "%s", "m_pullup(llc) failed");
- ic->ic_stats.is_rx_tooshort++;
- return IEEE80211_FC0_TYPE_DATA;
- }
- llc = (struct llc *)(mtod(m, uint8_t *) +
- sizeof(struct ether_header));
- if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
- m_adj(m, FF_LLC_SIZE);
- m = ieee80211_decap_fastframe(ic, ni, m);
- if (m == NULL)
- return IEEE80211_FC0_TYPE_DATA;
- }
- }
-#undef FF_LLC_SIZE
- ieee80211_deliver_data(ic, ni, m);
- return IEEE80211_FC0_TYPE_DATA;
-
- case IEEE80211_FC0_TYPE_MGT:
- ic->ic_stats.is_rx_mgmt++;
- IEEE80211_NODE_STAT(ni, rx_mgmt);
- if (dir != IEEE80211_FC1_DIR_NODS) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "data", "unknown dir 0x%x", dir);
- ic->ic_stats.is_rx_wrongdir++;
- goto err;
- }
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
- ni->ni_macaddr, "mgt", "too short: len %u",
- m->m_pkthdr.len);
- ic->ic_stats.is_rx_tooshort++;
- goto out;
- }
-#ifdef IEEE80211_DEBUG
- if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) ||
- ieee80211_msg_dumppkts(ic)) {
- if_printf(ic->ic_ifp, "received %s from %s rssi %d\n",
- ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- ether_sprintf(wh->i_addr2), rssi);
- }
-#endif
- if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
- if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
- /*
- * Only shared key auth frames with a challenge
- * should be encrypted, discard all others.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "WEP set but not permitted");
- ic->ic_stats.is_rx_mgtdiscard++; /* XXX */
- goto out;
- }
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
- /*
- * Discard encrypted frames when privacy is off.
- */
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, "mgt", "%s", "WEP set but PRIVACY off");
- ic->ic_stats.is_rx_noprivacy++;
- goto out;
- }
- hdrspace = ieee80211_hdrspace(ic, wh);
- key = ieee80211_crypto_decap(ic, ni, m, hdrspace);
- if (key == NULL) {
- /* NB: stats+msgs handled in crypto_decap */
- goto out;
- }
- wh = mtod(m, struct ieee80211_frame *);
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
- }
- if (bpf_peers_present(ic->ic_rawbpf))
- bpf_mtap(ic->ic_rawbpf, m);
- (*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, noise, rstamp);
- m_freem(m);
- return IEEE80211_FC0_TYPE_MGT;
-
- case IEEE80211_FC0_TYPE_CTL:
- ic->ic_stats.is_rx_ctl++;
- IEEE80211_NODE_STAT(ni, rx_ctrl);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_PS_POLL:
- ieee80211_recv_pspoll(ic, ni, m);
- break;
- case IEEE80211_FC0_SUBTYPE_BAR:
- ieee80211_recv_bar(ni, m);
- break;
- }
+ mcopy = m;
+ m = NULL;
}
- goto out;
- default:
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, NULL, "bad frame type 0x%x", type);
- /* should not come here */
- break;
+ ni = ieee80211_ref_node(vap->iv_bss);
+ type = ieee80211_input(ni, mcopy, rssi, noise, rstamp);
+ ieee80211_free_node(ni);
}
-err:
- ifp->if_ierrors++;
-out:
- if (m != NULL) {
- if (bpf_peers_present(ic->ic_rawbpf) && need_tap)
- bpf_mtap(ic->ic_rawbpf, m);
+ if (m != NULL) /* no vaps, reclaim mbuf */
m_freem(m);
- }
return type;
-#undef SEQ_LEQ
}
/*
* This function reassemble fragments.
+ *
+ * XXX should handle 3 concurrent reassemblies per-spec.
*/
-static struct mbuf *
-ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct mbuf *m, int hdrspace)
+struct mbuf *
+ieee80211_defrag(struct ieee80211_node *ni, struct mbuf *m, int hdrspace)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
struct ieee80211_frame *lwh;
uint16_t rxseq;
@@ -681,7 +167,7 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
if (mfrag == NULL) {
if (fragno != 0) { /* !first fragment, discard */
- ic->ic_stats.is_rx_defrag++;
+ vap->iv_stats.is_rx_defrag++;
IEEE80211_NODE_STAT(ni, rx_defrag);
m_freem(m);
return NULL;
@@ -705,12 +191,14 @@ ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
}
void
-ieee80211_deliver_data(struct ieee80211com *ic,
+ieee80211_deliver_data(struct ieee80211vap *vap,
struct ieee80211_node *ni, struct mbuf *m)
{
struct ether_header *eh = mtod(m, struct ether_header *);
- struct ifnet *ifp = ic->ic_ifp;
+ struct ifnet *ifp = vap->iv_ifp;
+ /* NB: see hostap_deliver_data, this path doesn't handle hostap */
+ KASSERT(vap->iv_opmode != IEEE80211_M_HOSTAP, ("gack, hostap"));
/*
* Do accounting.
*/
@@ -722,66 +210,21 @@ ieee80211_deliver_data(struct ieee80211com *ic,
IEEE80211_NODE_STAT(ni, rx_mcast);
} else
IEEE80211_NODE_STAT(ni, rx_ucast);
+ m->m_pkthdr.rcvif = ifp;
/* clear driver/net80211 flags before passing up */
m->m_flags &= ~M_80211_RX;
- /* perform as a bridge within the AP */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
- struct mbuf *m1 = NULL;
-
- if (m->m_flags & M_MCAST) {
- m1 = m_dup(m, M_DONTWAIT);
- if (m1 == NULL)
- ifp->if_oerrors++;
- else
- m1->m_flags |= M_MCAST;
- } else {
- /*
- * Check if the destination is known; if so
- * and the port is authorized dispatch directly.
- */
- struct ieee80211_node *sta =
- ieee80211_find_node(&ic->ic_sta, eh->ether_dhost);
- if (sta != NULL) {
- if (ieee80211_node_is_authorized(sta)) {
- /*
- * Beware of sending to ourself; this
- * needs to happen via the normal
- * input path.
- */
- if (sta != ic->ic_bss) {
- m1 = m;
- m = NULL;
- }
- } else {
- ic->ic_stats.is_rx_unauth++;
- IEEE80211_NODE_STAT(sta, rx_unauth);
- }
- ieee80211_free_node(sta);
- }
- }
- if (m1 != NULL) {
- int error;
-
- /* XXX does not work well with WME */
- IFQ_HANDOFF(ifp, m1, error);
- }
- }
- if (m != NULL) {
- m->m_pkthdr.rcvif = ifp;
- if (ni->ni_vlan != 0) {
- /* attach vlan tag */
- m->m_pkthdr.ether_vtag = ni->ni_vlan;
- m->m_flags |= M_VLANTAG;
- }
- (*ifp->if_input)(ifp, m);
+ if (ni->ni_vlan != 0) {
+ /* attach vlan tag */
+ m->m_pkthdr.ether_vtag = ni->ni_vlan;
+ m->m_flags |= M_VLANTAG;
}
+ ifp->if_input(ifp, m);
}
-static struct mbuf *
-ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen)
+struct mbuf *
+ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen)
{
struct ieee80211_qosframe_addr4 wh; /* Max size address frames */
struct ether_header *eh;
@@ -916,28 +359,28 @@ ieee80211_decap1(struct mbuf *m, int *framelen)
* for delivery. The second frame is returned for delivery
* via the normal path.
*/
-static struct mbuf *
-ieee80211_decap_fastframe(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m)
+struct mbuf *
+ieee80211_decap_fastframe(struct ieee80211_node *ni, struct mbuf *m)
{
#define MS(x,f) (((x) & f) >> f##_S)
+ struct ieee80211vap *vap = ni->ni_vap;
uint32_t ath;
struct mbuf *n;
int framelen;
m_copydata(m, 0, sizeof(uint32_t), (caddr_t) &ath);
if (MS(ath, ATH_FF_PROTO) != ATH_FF_PROTO_L2TUNNEL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame",
"unsupport tunnel protocol, header 0x%x", ath);
- ic->ic_stats.is_ff_badhdr++;
+ vap->iv_stats.is_ff_badhdr++;
m_freem(m);
return NULL;
}
/* NB: skip header and alignment padding */
m_adj(m, roundup(sizeof(uint32_t) - 2, 4) + 2);
- ic->ic_stats.is_ff_decap++;
+ vap->iv_stats.is_ff_decap++;
/*
* Decap the first frame, bust it apart from the
@@ -946,21 +389,22 @@ ieee80211_decap_fastframe(struct ieee80211com *ic,
*/
m = ieee80211_decap1(m, &framelen);
if (m == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame", "%s", "first decap failed");
- ic->ic_stats.is_ff_tooshort++;
+ vap->iv_stats.is_ff_tooshort++;
return NULL;
}
n = m_split(m, framelen, M_NOWAIT);
if (n == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame",
"%s", "unable to split encapsulated frames");
- ic->ic_stats.is_ff_split++;
+ vap->iv_stats.is_ff_split++;
m_freem(m); /* NB: must reclaim */
return NULL;
}
- ieee80211_deliver_data(ic, ni, m); /* 1st of pair */
+ /* XXX not right for WDS */
+ vap->iv_deliver_data(vap, ni, m); /* 1st of pair */
/*
* Decap second frame.
@@ -968,9 +412,9 @@ ieee80211_decap_fastframe(struct ieee80211com *ic,
m_adj(n, roundup2(framelen, 4) - framelen); /* padding */
n = ieee80211_decap1(n, &framelen);
if (n == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
ni->ni_macaddr, "fast-frame", "%s", "second decap failed");
- ic->ic_stats.is_ff_tooshort++;
+ vap->iv_stats.is_ff_tooshort++;
}
/* XXX verify framelen against mbuf contents */
return n; /* 2nd delivered by caller */
@@ -984,7 +428,7 @@ int
ieee80211_setup_rates(struct ieee80211_node *ni,
const uint8_t *rates, const uint8_t *xrates, int flags)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_rateset *rs = &ni->ni_rates;
memset(rs, 0, sizeof(*rs));
@@ -998,11 +442,10 @@ ieee80211_setup_rates(struct ieee80211_node *ni,
nxrates = xrates[1];
if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE,
- "[%s] extended rate set too large;"
- " only using %u of %u rates\n",
- ether_sprintf(ni->ni_macaddr), nxrates, xrates[1]);
- ic->ic_stats.is_rx_rstoobig++;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE, ni,
+ "extended rate set too large; only using "
+ "%u of %u rates", nxrates, xrates[1]);
+ vap->iv_stats.is_rx_rstoobig++;
}
memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
rs->rs_nrates += nxrates;
@@ -1010,101 +453,6 @@ ieee80211_setup_rates(struct ieee80211_node *ni,
return ieee80211_fix_rate(ni, rs, flags);
}
-static void
-ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
- struct ieee80211_node *ni, int rssi, int noise, uint32_t rstamp,
- uint16_t seq, uint16_t status)
-{
-
- if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "open auth",
- "bad sta auth mode %u", ni->ni_authmode);
- ic->ic_stats.is_rx_bad_auth++; /* XXX */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- /*
- * Clear any challenge text that may be there if
- * a previous shared key auth failed and then an
- * open auth is attempted.
- */
- if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_80211_NODE);
- ni->ni_challenge = NULL;
- }
- /* XXX hack to workaround calling convention */
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq + 1) | (IEEE80211_STATUS_ALG<<16));
- }
- return;
- }
- switch (ic->ic_opmode) {
- case IEEE80211_M_IBSS:
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_MONITOR:
- case IEEE80211_M_WDS:
- /* should not come here */
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "open auth",
- "bad operating mode %u", ic->ic_opmode);
- break;
-
- case IEEE80211_M_HOSTAP:
- if (ic->ic_state != IEEE80211_S_RUN ||
- seq != IEEE80211_AUTH_OPEN_REQUEST) {
- ic->ic_stats.is_rx_bad_auth++;
- return;
- }
- /* always accept open authentication requests */
- if (ni == ic->ic_bss) {
- ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
- if (ni == NULL)
- return;
- } else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
- (void) ieee80211_ref_node(ni);
- /*
- * Mark the node as referenced to reflect that it's
- * reference count has been bumped to insure it remains
- * after the transaction completes.
- */
- ni->ni_flags |= IEEE80211_NODE_AREF;
-
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] station authenticated (open)\n",
- ether_sprintf(ni->ni_macaddr));
- /*
- * When 802.1x is not in use mark the port
- * authorized at this point so traffic can flow.
- */
- if (ni->ni_authmode != IEEE80211_AUTH_8021X)
- ieee80211_node_authorize(ni);
- break;
-
- case IEEE80211_M_STA:
- if (ic->ic_state != IEEE80211_S_AUTH ||
- seq != IEEE80211_AUTH_OPEN_RESPONSE) {
- ic->ic_stats.is_rx_bad_auth++;
- return;
- }
- if (status != 0) {
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] open auth failed (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), status);
- /* XXX can this happen? */
- if (ni != ic->ic_bss)
- ni->ni_fails++;
- ic->ic_stats.is_rx_auth_fail++;
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
- IEEE80211_SCAN_FAIL_STATUS);
- } else
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
- break;
- }
-}
-
/*
* Send a management frame error response to the specified
* station. If ni is associated with the station then use
@@ -1112,14 +460,26 @@ ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
* transmitting the frame and then free the reference so
* it will go away as soon as the frame has been transmitted.
*/
-static void
-ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
- const uint8_t *mac, int subtype, int arg)
+void
+ieee80211_send_error(struct ieee80211_node *ni,
+ const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg)
{
+ struct ieee80211vap *vap = ni->ni_vap;
int istmp;
- if (ni == ic->ic_bss) {
- ni = ieee80211_tmp_node(ic, mac);
+ if (ni == vap->iv_bss) {
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ /*
+ * XXX hack until we get rid of this routine.
+ * We can be called prior to the vap reaching
+ * run state under certain conditions in which
+ * case iv_bss->ni_chan will not be setup.
+ * Check for this explicitly and and just ignore
+ * the request.
+ */
+ return;
+ }
+ ni = ieee80211_tmp_node(vap, mac);
if (ni == NULL) {
/* XXX msg */
return;
@@ -1127,2204 +487,342 @@ ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
istmp = 1;
} else
istmp = 0;
- IEEE80211_SEND_MGMT(ic, ni, subtype, arg);
+ IEEE80211_SEND_MGMT(ni, subtype, arg);
if (istmp)
ieee80211_free_node(ni);
}
-static int
-alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni)
+int
+ieee80211_alloc_challenge(struct ieee80211_node *ni)
{
if (ni->ni_challenge == NULL)
MALLOC(ni->ni_challenge, uint32_t*, IEEE80211_CHALLENGE_LEN,
M_80211_NODE, M_NOWAIT);
if (ni->ni_challenge == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] shared key challenge alloc failed\n",
- ether_sprintf(ni->ni_macaddr));
+ IEEE80211_NOTE(ni->ni_vap,
+ IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, ni,
+ "%s", "shared key challenge alloc failed");
/* XXX statistic */
}
return (ni->ni_challenge != NULL);
}
-/* XXX TODO: add statistics */
-static void
-ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh,
- uint8_t *frm, uint8_t *efrm, struct ieee80211_node *ni,
- int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
-{
- uint8_t *challenge;
- int allocbs, estatus;
-
- /*
- * NB: this can happen as we allow pre-shared key
- * authentication to be enabled w/o wep being turned
- * on so that configuration of these can be done
- * in any order. It may be better to enforce the
- * ordering in which case this check would just be
- * for sanity/consistency.
- */
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "%s", " PRIVACY is disabled");
- estatus = IEEE80211_STATUS_ALG;
- goto bad;
- }
- /*
- * Pre-shared key authentication is evil; accept
- * it only if explicitly configured (it is supported
- * mainly for compatibility with clients like OS X).
- */
- if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
- ni->ni_authmode != IEEE80211_AUTH_SHARED) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad sta auth mode %u", ni->ni_authmode);
- ic->ic_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
- estatus = IEEE80211_STATUS_ALG;
- goto bad;
- }
-
- challenge = NULL;
- if (frm + 1 < efrm) {
- if ((frm[1] + 2) > (efrm - frm)) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "ie %d/%d too long",
- frm[0], (frm[1] + 2) - (efrm - frm));
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- if (*frm == IEEE80211_ELEMID_CHALLENGE)
- challenge = frm;
- frm += frm[1] + 2;
- }
- switch (seq) {
- case IEEE80211_AUTH_SHARED_CHALLENGE:
- case IEEE80211_AUTH_SHARED_RESPONSE:
- if (challenge == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "%s", "no challenge");
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad challenge len %d", challenge[1]);
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- default:
- break;
- }
- switch (ic->ic_opmode) {
- case IEEE80211_M_MONITOR:
- case IEEE80211_M_AHDEMO:
- case IEEE80211_M_IBSS:
- case IEEE80211_M_WDS:
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad operating mode %u", ic->ic_opmode);
- return;
- case IEEE80211_M_HOSTAP:
- if (ic->ic_state != IEEE80211_S_RUN) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad state %u", ic->ic_state);
- estatus = IEEE80211_STATUS_ALG; /* XXX */
- goto bad;
- }
- switch (seq) {
- case IEEE80211_AUTH_SHARED_REQUEST:
- if (ni == ic->ic_bss) {
- ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
- if (ni == NULL) {
- /* NB: no way to return an error */
- return;
- }
- allocbs = 1;
- } else {
- if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
- (void) ieee80211_ref_node(ni);
- allocbs = 0;
- }
- /*
- * Mark the node as referenced to reflect that it's
- * reference count has been bumped to insure it remains
- * after the transaction completes.
- */
- ni->ni_flags |= IEEE80211_NODE_AREF;
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- if (!alloc_challenge(ic, ni)) {
- /* NB: don't return error so they rexmit */
- return;
- }
- get_random_bytes(ni->ni_challenge,
- IEEE80211_CHALLENGE_LEN);
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] shared key %sauth request\n",
- ether_sprintf(ni->ni_macaddr),
- allocbs ? "" : "re");
- break;
- case IEEE80211_AUTH_SHARED_RESPONSE:
- if (ni == ic->ic_bss) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key response",
- "%s", "unknown station");
- /* NB: don't send a response */
- return;
- }
- if (ni->ni_challenge == NULL) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key response",
- "%s", "no challenge recorded");
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- if (memcmp(ni->ni_challenge, &challenge[2],
- challenge[1]) != 0) {
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key response",
- "%s", "challenge mismatch");
- ic->ic_stats.is_rx_auth_fail++;
- estatus = IEEE80211_STATUS_CHALLENGE;
- goto bad;
- }
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] station authenticated (shared key)\n",
- ether_sprintf(ni->ni_macaddr));
- ieee80211_node_authorize(ni);
- break;
- default:
- IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
- ni->ni_macaddr, "shared key auth",
- "bad seq %d", seq);
- ic->ic_stats.is_rx_bad_auth++;
- estatus = IEEE80211_STATUS_SEQUENCE;
- goto bad;
- }
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
- break;
-
- case IEEE80211_M_STA:
- if (ic->ic_state != IEEE80211_S_AUTH)
- return;
- switch (seq) {
- case IEEE80211_AUTH_SHARED_PASS:
- if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_80211_NODE);
- ni->ni_challenge = NULL;
- }
- if (status != 0) {
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
- "[%s] shared key auth failed (reason %d)\n",
- ether_sprintf(ieee80211_getbssid(ic, wh)),
- status);
- /* XXX can this happen? */
- if (ni != ic->ic_bss)
- ni->ni_fails++;
- ic->ic_stats.is_rx_auth_fail++;
- return;
- }
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
- break;
- case IEEE80211_AUTH_SHARED_CHALLENGE:
- if (!alloc_challenge(ic, ni))
- return;
- /* XXX could optimize by passing recvd challenge */
- memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
- break;
- default:
- IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH,
- wh, "shared key auth", "bad seq %d", seq);
- ic->ic_stats.is_rx_bad_auth++;
- return;
- }
- break;
- }
- return;
-bad:
- /*
- * Send an error response; but only when operating as an AP.
- */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- /* XXX hack to workaround calling convention */
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq + 1) | (estatus<<16));
- } else if (ic->ic_opmode == IEEE80211_M_STA) {
- /*
- * Kick the state machine. This short-circuits
- * using the mgt frame timeout to trigger the
- * state transition.
- */
- if (ic->ic_state == IEEE80211_S_AUTH)
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
- IEEE80211_SCAN_FAIL_STATUS);
- }
-}
-
-/* Verify the existence and length of __elem or get out. */
-#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \
- if ((__elem) == NULL) { \
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
- wh, ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- "%s", "no " #__elem ); \
- ic->ic_stats.is_rx_elem_missing++; \
- return; \
- } \
- if ((__elem)[1] > (__maxlen)) { \
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
- wh, ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- "bad " #__elem " len %d", (__elem)[1]); \
- ic->ic_stats.is_rx_elem_toobig++; \
- return; \
- } \
-} while (0)
-
-#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \
- if ((_len) < (_minlen)) { \
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
- wh, ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- "ie too short, got %d, expected %d", \
- (_len), (_minlen)); \
- ic->ic_stats.is_rx_elem_toosmall++; \
- _action; \
- } \
-} while (0)
-
-#ifdef IEEE80211_DEBUG
-static void
-ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag,
- uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid)
-{
- printf("[%s] discard %s frame, ssid mismatch: ",
- ether_sprintf(mac), tag);
- ieee80211_print_essid(ssid + 2, ssid[1]);
- printf("\n");
-}
-
-#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
- if ((_ssid)[1] != 0 && \
- ((_ssid)[1] != (_ni)->ni_esslen || \
- memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
- if (ieee80211_msg_input(ic)) \
- ieee80211_ssid_mismatch(ic, \
- ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- wh->i_addr2, _ssid); \
- ic->ic_stats.is_rx_ssidmismatch++; \
- return; \
- } \
-} while (0)
-#else /* !IEEE80211_DEBUG */
-#define IEEE80211_VERIFY_SSID(_ni, _ssid) do { \
- if ((_ssid)[1] != 0 && \
- ((_ssid)[1] != (_ni)->ni_esslen || \
- memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
- ic->ic_stats.is_rx_ssidmismatch++; \
- return; \
- } \
-} while (0)
-#endif /* !IEEE80211_DEBUG */
-
-/* unalligned little endian access */
-#define LE_READ_2(p) \
- ((uint16_t) \
- ((((const uint8_t *)(p))[0] ) | \
- (((const uint8_t *)(p))[1] << 8)))
-#define LE_READ_4(p) \
- ((uint32_t) \
- ((((const uint8_t *)(p))[0] ) | \
- (((const uint8_t *)(p))[1] << 8) | \
- (((const uint8_t *)(p))[2] << 16) | \
- (((const uint8_t *)(p))[3] << 24)))
-
-static __inline int
-iswpaoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
-}
-
-static __inline int
-iswmeoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
-}
-
-static __inline int
-iswmeparam(const uint8_t *frm)
-{
- return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
- frm[6] == WME_PARAM_OUI_SUBTYPE;
-}
-
-static __inline int
-iswmeinfo(const uint8_t *frm)
-{
- return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
- frm[6] == WME_INFO_OUI_SUBTYPE;
-}
-
-static __inline int
-isatherosoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
-}
-
-static __inline int
-ishtcapoui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI);
-}
-
-static __inline int
-ishtinfooui(const uint8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI);
-}
-
-/*
- * Convert a WPA cipher selector OUI to an internal
- * cipher algorithm. Where appropriate we also
- * record any key length.
- */
-static int
-wpa_cipher(uint8_t *sel, uint8_t *keylen)
-{
-#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case WPA_SEL(WPA_CSE_NULL):
- return IEEE80211_CIPHER_NONE;
- case WPA_SEL(WPA_CSE_WEP40):
- if (keylen)
- *keylen = 40 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case WPA_SEL(WPA_CSE_WEP104):
- if (keylen)
- *keylen = 104 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case WPA_SEL(WPA_CSE_TKIP):
- return IEEE80211_CIPHER_TKIP;
- case WPA_SEL(WPA_CSE_CCMP):
- return IEEE80211_CIPHER_AES_CCM;
- }
- return 32; /* NB: so 1<< is discarded */
-#undef WPA_SEL
-}
-
-/*
- * Convert a WPA key management/authentication algorithm
- * to an internal code.
- */
-static int
-wpa_keymgmt(uint8_t *sel)
-{
-#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case WPA_SEL(WPA_ASE_8021X_UNSPEC):
- return WPA_ASE_8021X_UNSPEC;
- case WPA_SEL(WPA_ASE_8021X_PSK):
- return WPA_ASE_8021X_PSK;
- case WPA_SEL(WPA_ASE_NONE):
- return WPA_ASE_NONE;
- }
- return 0; /* NB: so is discarded */
-#undef WPA_SEL
-}
-
-/*
- * Parse a WPA information element to collect parameters
- * and validate the parameters against what has been
- * configured for the system.
- */
-static int
-ieee80211_parse_wpa(struct ieee80211com *ic, uint8_t *frm,
- struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
-{
- uint8_t len = frm[1];
- uint32_t w;
- int n;
-
- /*
- * Check the length once for fixed parts: OUI, type,
- * version, mcast cipher, and 2 selector counts.
- * Other, variable-length data, must be checked separately.
- */
- if ((ic->ic_flags & IEEE80211_F_WPA1) == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "not WPA, flags 0x%x", ic->ic_flags);
- return IEEE80211_REASON_IE_INVALID;
- }
- if (len < 14) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "too short, len %u", len);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 6, len -= 4; /* NB: len is payload only */
- /* NB: iswapoui already validated the OUI and type */
- w = LE_READ_2(frm);
- if (w != WPA_VERSION) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "bad version %u", w);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 2, len -= 2;
-
- /* multicast/group cipher */
- w = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
- if (w != rsn->rsn_mcastcipher) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "mcast cipher mismatch; got %u, expected %u",
- w, rsn->rsn_mcastcipher);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 4, len -= 4;
-
- /* unicast ciphers */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4+2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "ucast cipher data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_ucastcipherset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "%s", "ucast cipher set empty");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & (1<<IEEE80211_CIPHER_TKIP))
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
- else
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
-
- /* key management algorithms */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "key mgmt alg data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= wpa_keymgmt(frm);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_keymgmtset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "%s", "no acceptable key mgmt alg");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & WPA_ASE_8021X_UNSPEC)
- rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
- else
- rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
-
- if (len > 2) /* optional capabilities */
- rsn->rsn_caps = LE_READ_2(frm);
-
- return 0;
-}
-
-/*
- * Convert an RSN cipher selector OUI to an internal
- * cipher algorithm. Where appropriate we also
- * record any key length.
- */
-static int
-rsn_cipher(uint8_t *sel, uint8_t *keylen)
-{
-#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case RSN_SEL(RSN_CSE_NULL):
- return IEEE80211_CIPHER_NONE;
- case RSN_SEL(RSN_CSE_WEP40):
- if (keylen)
- *keylen = 40 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case RSN_SEL(RSN_CSE_WEP104):
- if (keylen)
- *keylen = 104 / NBBY;
- return IEEE80211_CIPHER_WEP;
- case RSN_SEL(RSN_CSE_TKIP):
- return IEEE80211_CIPHER_TKIP;
- case RSN_SEL(RSN_CSE_CCMP):
- return IEEE80211_CIPHER_AES_CCM;
- case RSN_SEL(RSN_CSE_WRAP):
- return IEEE80211_CIPHER_AES_OCB;
- }
- return 32; /* NB: so 1<< is discarded */
-#undef WPA_SEL
-}
-
-/*
- * Convert an RSN key management/authentication algorithm
- * to an internal code.
- */
-static int
-rsn_keymgmt(uint8_t *sel)
-{
-#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
- uint32_t w = LE_READ_4(sel);
-
- switch (w) {
- case RSN_SEL(RSN_ASE_8021X_UNSPEC):
- return RSN_ASE_8021X_UNSPEC;
- case RSN_SEL(RSN_ASE_8021X_PSK):
- return RSN_ASE_8021X_PSK;
- case RSN_SEL(RSN_ASE_NONE):
- return RSN_ASE_NONE;
- }
- return 0; /* NB: so is discarded */
-#undef RSN_SEL
-}
-
-/*
- * Parse a WPA/RSN information element to collect parameters
- * and validate the parameters against what has been
- * configured for the system.
- */
-static int
-ieee80211_parse_rsn(struct ieee80211com *ic, uint8_t *frm,
- struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
-{
- uint8_t len = frm[1];
- uint32_t w;
- int n;
-
- /*
- * Check the length once for fixed parts:
- * version, mcast cipher, and 2 selector counts.
- * Other, variable-length data, must be checked separately.
- */
- if ((ic->ic_flags & IEEE80211_F_WPA2) == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "WPA", "not RSN, flags 0x%x", ic->ic_flags);
- return IEEE80211_REASON_IE_INVALID;
- }
- if (len < 10) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "too short, len %u", len);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 2;
- w = LE_READ_2(frm);
- if (w != RSN_VERSION) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "bad version %u", w);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 2, len -= 2;
-
- /* multicast/group cipher */
- w = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
- if (w != rsn->rsn_mcastcipher) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "mcast cipher mismatch; got %u, expected %u",
- w, rsn->rsn_mcastcipher);
- return IEEE80211_REASON_IE_INVALID;
- }
- frm += 4, len -= 4;
-
- /* unicast ciphers */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4+2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "ucast cipher data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_ucastcipherset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "%s", "ucast cipher set empty");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & (1<<IEEE80211_CIPHER_TKIP))
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
- else
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
-
- /* key management algorithms */
- n = LE_READ_2(frm);
- frm += 2, len -= 2;
- if (len < n*4) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "key mgmt alg data too short; len %u, n %u",
- len, n);
- return IEEE80211_REASON_IE_INVALID;
- }
- w = 0;
- for (; n > 0; n--) {
- w |= rsn_keymgmt(frm);
- frm += 4, len -= 4;
- }
- w &= rsn->rsn_keymgmtset;
- if (w == 0) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
- wh, "RSN", "%s", "no acceptable key mgmt alg");
- return IEEE80211_REASON_IE_INVALID;
- }
- if (w & RSN_ASE_8021X_UNSPEC)
- rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
- else
- rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
-
- /* optional RSN capabilities */
- if (len > 2)
- rsn->rsn_caps = LE_READ_2(frm);
- /* XXXPMKID */
-
- return 0;
-}
-
-static int
-ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm,
- const struct ieee80211_frame *wh)
-{
-#define MS(_v, _f) (((_v) & _f) >> _f##_S)
- struct ieee80211_wme_state *wme = &ic->ic_wme;
- u_int len = frm[1], qosinfo;
- int i;
-
- if (len < sizeof(struct ieee80211_wme_param)-2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
- wh, "WME", "too short, len %u", len);
- return -1;
- }
- qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
- qosinfo &= WME_QOSINFO_COUNT;
- /* XXX do proper check for wraparound */
- if (qosinfo == wme->wme_wmeChanParams.cap_info)
- return 0;
- frm += __offsetof(struct ieee80211_wme_param, params_acParams);
- for (i = 0; i < WME_NUM_AC; i++) {
- struct wmeParams *wmep =
- &wme->wme_wmeChanParams.cap_wmeParams[i];
- /* NB: ACI not used */
- wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
- wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
- wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
- wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
- wmep->wmep_txopLimit = LE_READ_2(frm+2);
- frm += 4;
- }
- wme->wme_wmeChanParams.cap_info = qosinfo;
- return 1;
-#undef MS
-}
-
-static int
-ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
- const struct ieee80211_frame *wh)
-{
- struct ieee80211com *ic = ni->ni_ic;
- const struct ieee80211_ath_ie *ath;
- u_int len = frm[1];
- int capschanged;
- uint16_t defkeyix;
-
- if (len < sizeof(struct ieee80211_ath_ie)-2) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
- wh, "Atheros", "too short, len %u", len);
- return -1;
- }
- ath = (const struct ieee80211_ath_ie *)frm;
- capschanged = (ni->ni_ath_flags != ath->ath_capability);
- defkeyix = LE_READ_2(ath->ath_defkeyix);
- if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
- ni->ni_ath_flags = ath->ath_capability;
- ni->ni_ath_defkeyix = defkeyix;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
- "[%s] ath ie change: new caps 0x%x defkeyix 0x%x\n",
- ether_sprintf(ni->ni_macaddr),
- ni->ni_ath_flags, ni->ni_ath_defkeyix);
- }
- if (IEEE80211_ATH_CAP(ic, ni, ATHEROS_CAP_TURBO_PRIME)) {
- uint16_t curflags, newflags;
-
- /*
- * Check for turbo mode switch. Calculate flags
- * for the new mode and effect the switch.
- */
- newflags = curflags = ic->ic_bsschan->ic_flags;
- /* NB: BOOST is not in ic_flags, so get it from the ie */
- if (ath->ath_capability & ATHEROS_CAP_BOOST)
- newflags |= IEEE80211_CHAN_TURBO;
- else
- newflags &= ~IEEE80211_CHAN_TURBO;
- if (newflags != curflags)
- ieee80211_dturbo_switch(ic, newflags);
- }
- return capschanged;
-}
-
void
-ieee80211_saveath(struct ieee80211_node *ni, uint8_t *ie)
+ieee80211_parse_ath(struct ieee80211_node *ni, uint8_t *ie)
{
const struct ieee80211_ath_ie *ath =
(const struct ieee80211_ath_ie *) ie;
ni->ni_ath_flags = ath->ath_capability;
ni->ni_ath_defkeyix = LE_READ_2(&ath->ath_defkeyix);
- ieee80211_saveie(&ni->ni_ath_ie, ie);
-}
-
-void
-ieee80211_saveie(uint8_t **iep, const uint8_t *ie)
-{
- u_int ielen = ie[1]+2;
- /*
- * Record information element for later use.
- */
- if (*iep == NULL || (*iep)[1] != ie[1]) {
- if (*iep != NULL)
- FREE(*iep, M_80211_NODE);
- MALLOC(*iep, void*, ielen, M_80211_NODE, M_NOWAIT);
- }
- if (*iep != NULL)
- memcpy(*iep, ie, ielen);
- /* XXX note failure */
}
-/* XXX find a better place for definition */
-struct l2_update_frame {
- struct ether_header eh;
- uint8_t dsap;
- uint8_t ssap;
- uint8_t control;
- uint8_t xid[3];
-} __packed;
-
/*
- * Deliver a TGf L2UF frame on behalf of a station.
- * This primes any bridge when the station is roaming
- * between ap's on the same wired network.
+ * Parse a Beacon or ProbeResponse frame and return the
+ * useful information in an ieee80211_scanparams structure.
+ * Status is set to 0 if no problems were found; otherwise
+ * a bitmask of IEEE80211_BPARSE_* items is returned that
+ * describes the problems detected.
*/
-static void
-ieee80211_deliver_l2uf(struct ieee80211_node *ni)
-{
- struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct mbuf *m;
- struct l2_update_frame *l2uf;
- struct ether_header *eh;
-
- m = m_gethdr(M_NOWAIT, MT_DATA);
- if (m == NULL) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
- "%s", "no mbuf for l2uf frame");
- ic->ic_stats.is_rx_nobuf++; /* XXX not right */
- return;
- }
- l2uf = mtod(m, struct l2_update_frame *);
- eh = &l2uf->eh;
- /* dst: Broadcast address */
- IEEE80211_ADDR_COPY(eh->ether_dhost, ifp->if_broadcastaddr);
- /* src: associated STA */
- IEEE80211_ADDR_COPY(eh->ether_shost, ni->ni_macaddr);
- eh->ether_type = htons(sizeof(*l2uf) - sizeof(*eh));
-
- l2uf->dsap = 0;
- l2uf->ssap = 0;
- l2uf->control = 0xf5;
- l2uf->xid[0] = 0x81;
- l2uf->xid[1] = 0x80;
- l2uf->xid[2] = 0x00;
-
- m->m_pkthdr.len = m->m_len = sizeof(*l2uf);
- ieee80211_deliver_data(ic, ni, m);
-}
-
-static __inline int
-contbgscan(struct ieee80211com *ic)
-{
- return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
- time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle));
-}
-
-static __inline int
-startbgscan(struct ieee80211com *ic)
-{
- return ((ic->ic_flags & IEEE80211_F_BGSCAN) &&
- !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
- time_after(ticks, ic->ic_lastscan + ic->ic_bgscanintvl) &&
- time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle));
-}
-
-static void
-ratesetmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
- int reassoc, int resp, const char *tag, int rate)
-{
- struct ieee80211com *ic = ni->ni_ic;
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] deny %s request, %s rate set mismatch, rate 0x%x\n",
- ether_sprintf(wh->i_addr2),
- reassoc ? "reassoc" : "assoc", tag, rate);
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
- ieee80211_node_leave(ic, ni);
- ic->ic_stats.is_rx_assoc_norate++;
-}
-
-static void
-capinfomismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
- int reassoc, int resp, const char *tag, int capinfo)
-{
- struct ieee80211com *ic = ni->ni_ic;
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] deny %s request, %s mismatch 0x%x\n",
- ether_sprintf(wh->i_addr2),
- reassoc ? "reassoc" : "assoc", tag, capinfo);
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_CAPINFO);
- ieee80211_node_leave(ic, ni);
- ic->ic_stats.is_rx_assoc_capmismatch++;
-}
-
-static void
-htcapmismatch(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
- int reassoc, int resp)
+int
+ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m,
+ struct ieee80211_scanparams *scan)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
-
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_ANY, wh->i_addr2,
- "deny %s request, %s missing HT ie", reassoc ? "reassoc" : "assoc");
- /* XXX no better code */
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_OTHER);
- ieee80211_node_leave(ic, ni);
-}
-
-void
-ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
- struct ieee80211_node *ni,
- int subtype, int rssi, int noise, uint32_t rstamp)
-{
-#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
-#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
struct ieee80211_frame *wh;
uint8_t *frm, *efrm;
- uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap, *htinfo;
- int reassoc, resp, allocbs;
- uint8_t rate;
- wh = mtod(m0, struct ieee80211_frame *);
+ wh = mtod(m, struct ieee80211_frame *);
frm = (uint8_t *)&wh[1];
- efrm = mtod(m0, uint8_t *) + m0->m_len;
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- case IEEE80211_FC0_SUBTYPE_BEACON: {
- struct ieee80211_scanparams scan;
-
- /*
- * We process beacon/probe response frames:
- * o when scanning, or
- * o station mode when associated (to collect state
- * updates such as 802.11g slot time), or
- * o adhoc mode (to discover neighbors)
- * Frames otherwise received are discarded.
- */
- if (!((ic->ic_flags & IEEE80211_F_SCAN) ||
- (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) ||
- ic->ic_opmode == IEEE80211_M_IBSS)) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * beacon/probe response frame format
- * [8] time stamp
- * [2] beacon interval
- * [2] capability information
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] country information
- * [tlv] parameter set (FH/DS)
- * [tlv] erp information
- * [tlv] extended supported rates
- * [tlv] WME
- * [tlv] WPA or RSN
- * [tlv] HT capabilities
- * [tlv] HT information
- * [tlv] Atheros capabilities
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 12, return);
- memset(&scan, 0, sizeof(scan));
- scan.tstamp = frm; frm += 8;
- scan.bintval = le16toh(*(uint16_t *)frm); frm += 2;
- scan.capinfo = le16toh(*(uint16_t *)frm); frm += 2;
- scan.bchan = IEEE80211_CHAN2IEEE(ic->ic_curchan);
- scan.curchan = ic->ic_curchan;
-
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- scan.ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- scan.rates = frm;
- break;
- case IEEE80211_ELEMID_COUNTRY:
- scan.country = frm;
- break;
- case IEEE80211_ELEMID_FHPARMS:
- if (ic->ic_phytype == IEEE80211_T_FH) {
- scan.fhdwell = LE_READ_2(&frm[2]);
- scan.bchan = IEEE80211_FH_CHAN(frm[4], frm[5]);
- scan.fhindex = frm[6];
- }
- break;
- case IEEE80211_ELEMID_DSPARMS:
- /*
- * XXX hack this since depending on phytype
- * is problematic for multi-mode devices.
- */
- if (ic->ic_phytype != IEEE80211_T_FH)
- scan.bchan = frm[2];
- break;
- case IEEE80211_ELEMID_TIM:
- /* XXX ATIM? */
- scan.tim = frm;
- scan.timoff = frm - mtod(m0, uint8_t *);
- break;
- case IEEE80211_ELEMID_IBSSPARMS:
- break;
- case IEEE80211_ELEMID_XRATES:
- scan.xrates = frm;
- break;
- case IEEE80211_ELEMID_ERP:
- if (frm[1] != 1) {
- IEEE80211_DISCARD_IE(ic,
- IEEE80211_MSG_ELEMID, wh, "ERP",
- "bad len %u", frm[1]);
- ic->ic_stats.is_rx_elem_toobig++;
- break;
- }
- scan.erp = frm[2];
- break;
- case IEEE80211_ELEMID_HTCAP:
- scan.htcap = frm;
- break;
- case IEEE80211_ELEMID_RSN:
- scan.rsn = frm;
- break;
- case IEEE80211_ELEMID_HTINFO:
- scan.htinfo = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (iswpaoui(frm))
- scan.wpa = frm;
- else if (iswmeparam(frm) || iswmeinfo(frm))
- scan.wme = frm;
- else if (isatherosoui(frm))
- scan.ath = frm;
- else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
- /*
- * Accept pre-draft HT ie's if the
- * standard ones have not been seen.
- */
- if (ishtcapoui(frm)) {
- if (scan.htcap == NULL)
- scan.htcap = frm;
- } else if (ishtinfooui(frm)) {
- if (scan.htinfo == NULL)
- scan.htcap = frm;
- }
- }
- break;
- default:
- IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
- wh, "unhandled",
- "id %u, len %u", *frm, frm[1]);
- ic->ic_stats.is_rx_elem_unknown++;
- break;
- }
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE);
- if (scan.xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(scan.xrates,
- IEEE80211_RATE_MAXSIZE - scan.rates[1]);
- IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN);
-#if IEEE80211_CHAN_MAX < 255
- if (scan.chan > IEEE80211_CHAN_MAX) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "invalid channel %u", scan.chan);
- ic->ic_stats.is_rx_badchan++;
- return;
- }
-#endif
- if (IEEE80211_CHAN2IEEE(scan.curchan) != scan.bchan &&
- ic->ic_phytype != IEEE80211_T_FH) {
- /*
- * Frame was received on a channel different from the
- * one indicated in the DS params element id;
- * silently discard it.
- *
- * NB: this can happen due to signal leakage.
- * But we should take it for FH phy because
- * the rssi value should be correct even for
- * different hop pattern in FH.
- */
- IEEE80211_DISCARD(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "for off-channel %u",
- IEEE80211_CHAN2IEEE(scan.curchan));
- ic->ic_stats.is_rx_chanmismatch++;
- return;
- }
- if (!(IEEE80211_BINTVAL_MIN <= scan.bintval &&
- scan.bintval <= IEEE80211_BINTVAL_MAX)) {
- IEEE80211_DISCARD(ic,
- IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "bogus beacon interval", scan.bintval);
- ic->ic_stats.is_rx_badbintval++;
- return;
- }
- /*
- * Process HT ie's. This is complicated by our
- * accepting both the standard ie's and the pre-draft
- * vendor OUI ie's that some vendors still use/require.
- */
- if (scan.htcap != NULL) {
- IEEE80211_VERIFY_LENGTH(scan.htcap[1],
- scan.htcap[0] == IEEE80211_ELEMID_VENDOR ?
- 4 + sizeof(struct ieee80211_ie_htcap)-2 :
- sizeof(struct ieee80211_ie_htcap)-2,
- scan.htcap = NULL);
- }
- if (scan.htinfo != NULL) {
- IEEE80211_VERIFY_LENGTH(scan.htinfo[1],
- scan.htinfo[0] == IEEE80211_ELEMID_VENDOR ?
- 4 + sizeof(struct ieee80211_ie_htinfo)-2 :
- sizeof(struct ieee80211_ie_htinfo)-2,
- scan.htinfo = NULL);
- }
-
- /*
- * Count frame now that we know it's to be processed.
- */
- if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
- ic->ic_stats.is_rx_beacon++; /* XXX remove */
- IEEE80211_NODE_STAT(ni, rx_beacons);
- } else
- IEEE80211_NODE_STAT(ni, rx_proberesp);
-
- /*
- * When operating in station mode, check for state updates.
- * Be careful to ignore beacons received while doing a
- * background scan. We consider only 11g/WMM stuff right now.
- */
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ni->ni_associd != 0 &&
- ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
- IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
- /* record tsf of last beacon */
- memcpy(ni->ni_tstamp.data, scan.tstamp,
- sizeof(ni->ni_tstamp));
- /* count beacon frame for s/w bmiss handling */
- ic->ic_swbmiss_count++;
- ic->ic_bmiss_count = 0;
- if (ni->ni_erp != scan.erp) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] erp change: was 0x%x, now 0x%x\n",
- ether_sprintf(wh->i_addr2),
- ni->ni_erp, scan.erp);
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
- (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
- ic->ic_flags |= IEEE80211_F_USEPROT;
- else
- ic->ic_flags &= ~IEEE80211_F_USEPROT;
- ni->ni_erp = scan.erp;
- /* XXX statistic */
- }
- if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] capabilities change: before 0x%x,"
- " now 0x%x\n",
- ether_sprintf(wh->i_addr2),
- ni->ni_capinfo, scan.capinfo);
- /*
- * NB: we assume short preamble doesn't
- * change dynamically
- */
- ieee80211_set_shortslottime(ic,
- IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
- (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
- ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
- | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
- /* XXX statistic */
- }
- if (scan.wme != NULL &&
- (ni->ni_flags & IEEE80211_NODE_QOS) &&
- ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0)
- ieee80211_wme_updateparams(ic);
- if (scan.ath != NULL)
- ieee80211_parse_athparams(ni, scan.ath, wh);
- if (scan.htcap != NULL)
- ieee80211_parse_htcap(ni, scan.htcap);
- if (scan.htinfo != NULL) {
- ieee80211_parse_htinfo(ni, scan.htinfo);
- if (ni->ni_chan != ic->ic_bsschan) {
- /*
- * Channel has been adjusted based on
- * negotiated HT parameters; force the
- * channel state to follow.
- */
- ieee80211_setbsschan(ic, ni->ni_chan);
- }
- }
- if (scan.tim != NULL) {
- struct ieee80211_tim_ie *tim =
- (struct ieee80211_tim_ie *) scan.tim;
-#if 0
- int aid = IEEE80211_AID(ni->ni_associd);
- int ix = aid / NBBY;
- int min = tim->tim_bitctl &~ 1;
- int max = tim->tim_len + min - 4;
- if ((tim->tim_bitctl&1) ||
- (min <= ix && ix <= max &&
- isset(tim->tim_bitmap - min, aid))) {
- /*
- * XXX Do not let bg scan kick off
- * we are expecting data.
- */
- ic->ic_lastdata = ticks;
- ieee80211_sta_pwrsave(ic, 0);
- }
-#endif
- ni->ni_dtim_count = tim->tim_count;
- ni->ni_dtim_period = tim->tim_period;
+ efrm = mtod(m, uint8_t *) + m->m_len;
+ scan->status = 0;
+ /*
+ * beacon/probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] capability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] country information
+ * [tlv] parameter set (FH/DS)
+ * [tlv] erp information
+ * [tlv] extended supported rates
+ * [tlv] WME
+ * [tlv] WPA or RSN
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Atheros capabilities
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 12,
+ return (scan->status = IEEE80211_BPARSE_BADIELEN));
+ memset(scan, 0, sizeof(*scan));
+ scan->tstamp = frm; frm += 8;
+ scan->bintval = le16toh(*(uint16_t *)frm); frm += 2;
+ scan->capinfo = le16toh(*(uint16_t *)frm); frm += 2;
+ scan->bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
+ scan->chan = scan->bchan;
+ scan->ies = frm;
+ scan->ies_len = efrm - frm;
+
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2,
+ return (scan->status = IEEE80211_BPARSE_BADIELEN));
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ scan->ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ scan->rates = frm;
+ break;
+ case IEEE80211_ELEMID_COUNTRY:
+ scan->country = frm;
+ break;
+ case IEEE80211_ELEMID_FHPARMS:
+ if (ic->ic_phytype == IEEE80211_T_FH) {
+ scan->fhdwell = LE_READ_2(&frm[2]);
+ scan->chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
+ scan->fhindex = frm[6];
}
+ break;
+ case IEEE80211_ELEMID_DSPARMS:
/*
- * If scanning, pass the info to the scan module.
- * Otherwise, check if it's the right time to do
- * a background scan. Background scanning must
- * be enabled and we must not be operating in the
- * turbo phase of dynamic turbo mode. Then,
- * it's been a while since the last background
- * scan and if no data frames have come through
- * recently, kick off a scan. Note that this
- * is the mechanism by which a background scan
- * is started _and_ continued each time we
- * return on-channel to receive a beacon from
- * our ap.
+ * XXX hack this since depending on phytype
+ * is problematic for multi-mode devices.
*/
- if (ic->ic_flags & IEEE80211_F_SCAN) {
- ieee80211_add_scan(ic, &scan, wh,
- subtype, rssi, noise, rstamp);
- } else if (contbgscan(ic)) {
- ieee80211_bg_scan(ic);
- } else if (startbgscan(ic)) {
-#if 0
- /* wakeup if we are sleeing */
- ieee80211_set_pwrsave(ic, 0);
-#endif
- ieee80211_bg_scan(ic);
- }
- return;
- }
- /*
- * If scanning, just pass information to the scan module.
- */
- if (ic->ic_flags & IEEE80211_F_SCAN) {
- if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
- /*
- * Actively scanning a channel marked passive;
- * send a probe request now that we know there
- * is 802.11 traffic present.
- *
- * XXX check if the beacon we recv'd gives
- * us what we need and suppress the probe req
- */
- ieee80211_probe_curchan(ic, 1);
- ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
- }
- ieee80211_add_scan(ic, &scan, wh,
- subtype, rssi, noise, rstamp);
- return;
- }
- if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
- if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
- /*
- * Create a new entry in the neighbor table.
- */
- ni = ieee80211_add_neighbor(ic, wh, &scan);
- } else if (ni->ni_capinfo == 0) {
- /*
- * Update faked node created on transmit.
- * Note this also updates the tsf.
- */
- ieee80211_init_neighbor(ni, wh, &scan);
- } else {
- /*
- * Record tsf for potential resync.
- */
- memcpy(ni->ni_tstamp.data, scan.tstamp,
- sizeof(ni->ni_tstamp));
- }
- if (ni != NULL) {
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- }
- }
- break;
- }
-
- case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
- if (ic->ic_opmode == IEEE80211_M_STA ||
- ic->ic_state != IEEE80211_S_RUN) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
- /* frame must be directed */
- ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */
- return;
- }
-
- /*
- * prreq frame format
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] extended supported rates
- * [tlv] Atheros capabilities
- */
- ssid = rates = xrates = ath = NULL;
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- case IEEE80211_ELEMID_XRATES:
- xrates = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (isatherosoui(frm))
- ath = frm;
+ if (ic->ic_phytype != IEEE80211_T_FH)
+ scan->chan = frm[2];
+ break;
+ case IEEE80211_ELEMID_TIM:
+ /* XXX ATIM? */
+ scan->tim = frm;
+ scan->timoff = frm - mtod(m, uint8_t *);
+ break;
+ case IEEE80211_ELEMID_IBSSPARMS:
+ case IEEE80211_ELEMID_CFPARMS:
+ /* NB: avoid debugging complaints */
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ scan->xrates = frm;
+ break;
+ case IEEE80211_ELEMID_ERP:
+ if (frm[1] != 1) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID, wh, "ERP",
+ "bad len %u", frm[1]);
+ vap->iv_stats.is_rx_elem_toobig++;
break;
}
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
- if (xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(xrates,
- IEEE80211_RATE_MAXSIZE - rates[1]);
- IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
- IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
- if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "no ssid with ssid suppression enabled");
- ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/
- return;
- }
-
- allocbs = 0;
- if (ni == ic->ic_bss) {
- if (ic->ic_opmode != IEEE80211_M_IBSS) {
- ni = ieee80211_tmp_node(ic, wh->i_addr2);
- allocbs = 1;
- } else if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+ scan->erp = frm[2] | 0x100;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ scan->htcap = frm;
+ break;
+ case IEEE80211_ELEMID_RSN:
+ scan->rsn = frm;
+ break;
+ case IEEE80211_ELEMID_HTINFO:
+ scan->htinfo = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm))
+ scan->wpa = frm;
+ else if (iswmeparam(frm) || iswmeinfo(frm))
+ scan->wme = frm;
+ else if (isatherosoui(frm))
+ scan->ath = frm;
+ else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
/*
- * XXX Cannot tell if the sender is operating
- * in ibss mode. But we need a new node to
- * send the response so blindly add them to the
- * neighbor table.
+ * Accept pre-draft HT ie's if the
+ * standard ones have not been seen.
*/
- ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
- wh->i_addr2);
- }
- if (ni == NULL)
- return;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] recv probe req\n", ether_sprintf(wh->i_addr2));
- ni->ni_rssi = rssi;
- ni->ni_rstamp = rstamp;
- rate = ieee80211_setup_rates(ni, rates, xrates,
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
- | IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (rate & IEEE80211_RATE_BASIC) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "recv'd rate set invalid");
- } else {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
- }
- if (allocbs) {
- /*
- * Temporary node created just to send a
- * response, reclaim immediately.
- */
- ieee80211_free_node(ni);
- } else if (ath != NULL)
- ieee80211_saveath(ni, ath);
- break;
-
- case IEEE80211_FC0_SUBTYPE_AUTH: {
- uint16_t algo, seq, status;
- /*
- * auth frame format
- * [2] algorithm
- * [2] sequence
- * [2] status
- * [tlv*] challenge
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
- algo = le16toh(*(uint16_t *)frm);
- seq = le16toh(*(uint16_t *)(frm + 2));
- status = le16toh(*(uint16_t *)(frm + 4));
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] recv auth frame with algorithm %d seq %d\n",
- ether_sprintf(wh->i_addr2), algo, seq);
- /*
- * Consult the ACL policy module if setup.
- */
- if (ic->ic_acl != NULL &&
- !ic->ic_acl->iac_check(ic, wh->i_addr2)) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL,
- wh, "auth", "%s", "disallowed by ACL");
- ic->ic_stats.is_rx_acl++;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16));
- }
- return;
- }
- if (ic->ic_flags & IEEE80211_F_COUNTERM) {
- IEEE80211_DISCARD(ic,
- IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
- wh, "auth", "%s", "TKIP countermeasures enabled");
- ic->ic_stats.is_rx_auth_countermeasures++;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH,
- IEEE80211_REASON_MIC_FAILURE);
- }
- return;
- }
- if (algo == IEEE80211_AUTH_ALG_SHARED)
- ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
- noise, rstamp, seq, status);
- else if (algo == IEEE80211_AUTH_ALG_OPEN)
- ieee80211_auth_open(ic, wh, ni, rssi, noise, rstamp,
- seq, status);
- else {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, "auth", "unsupported alg %d", algo);
- ic->ic_stats.is_rx_auth_unsupported++;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- /* XXX not right */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH,
- (seq+1) | (IEEE80211_STATUS_ALG<<16));
- }
- return;
- }
- break;
- }
-
- case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
- case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
- uint16_t capinfo, lintval;
- struct ieee80211_rsnparms rsnparms;
- uint8_t reason;
- int badwparsn;
-
- if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
- ic->ic_state != IEEE80211_S_RUN) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
-
- if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
- reassoc = 1;
- resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
- } else {
- reassoc = 0;
- resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
- }
- /*
- * asreq frame format
- * [2] capability information
- * [2] listen interval
- * [6*] current AP address (reassoc only)
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] extended supported rates
- * [tlv] WPA or RSN
- * [tlv] HT capabilities
- * [tlv] Atheros capabilities
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4), return);
- if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, ieee80211_mgt_subtype_name[subtype >>
- IEEE80211_FC0_SUBTYPE_SHIFT],
- "%s", "wrong bssid");
- ic->ic_stats.is_rx_assoc_bss++;
- return;
- }
- capinfo = le16toh(*(uint16_t *)frm); frm += 2;
- lintval = le16toh(*(uint16_t *)frm); frm += 2;
- if (reassoc)
- frm += 6; /* ignore current AP info */
- ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL;
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_SSID:
- ssid = frm;
- break;
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- case IEEE80211_ELEMID_XRATES:
- xrates = frm;
- break;
- /* XXX verify only one of RSN and WPA ie's? */
- case IEEE80211_ELEMID_RSN:
- rsn = frm;
- break;
- case IEEE80211_ELEMID_HTCAP:
- htcap = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (iswpaoui(frm))
- wpa = frm;
- else if (iswmeinfo(frm))
- wme = frm;
- else if (isatherosoui(frm))
- ath = frm;
- else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
- if (ishtcapoui(frm) && htcap == NULL)
- htcap = frm;
+ if (ishtcapoui(frm)) {
+ if (scan->htcap == NULL)
+ scan->htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (scan->htinfo == NULL)
+ scan->htcap = frm;
}
- break;
}
- frm += frm[1] + 2;
- }
- IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
- if (xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(xrates,
- IEEE80211_RATE_MAXSIZE - rates[1]);
- IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
- IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
- if (htcap != NULL) {
- IEEE80211_VERIFY_LENGTH(htcap[1],
- htcap[0] == IEEE80211_ELEMID_VENDOR ?
- 4 + sizeof(struct ieee80211_ie_htcap)-2 :
- sizeof(struct ieee80211_ie_htcap)-2,
- return); /* XXX just NULL out? */
- }
-
- if (ni == ic->ic_bss) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] deny %s request, sta not authenticated\n",
- ether_sprintf(wh->i_addr2),
- reassoc ? "reassoc" : "assoc");
- ieee80211_send_error(ic, ni, wh->i_addr2,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_ASSOC_NOT_AUTHED);
- ic->ic_stats.is_rx_assoc_notauth++;
- return;
- }
- /* assert right association security credentials */
- badwparsn = 0;
- switch (ic->ic_flags & IEEE80211_F_WPA) {
- case IEEE80211_F_WPA1:
- if (wpa == NULL)
- badwparsn = 1;
- break;
- case IEEE80211_F_WPA2:
- if (rsn == NULL)
- badwparsn = 1;
break;
- case IEEE80211_F_WPA1|IEEE80211_F_WPA2:
- if (wpa == NULL && rsn == NULL)
- badwparsn = 1;
+ default:
+ IEEE80211_DISCARD_IE(vap, IEEE80211_MSG_ELEMID,
+ wh, "unhandled",
+ "id %u, len %u", *frm, frm[1]);
+ vap->iv_stats.is_rx_elem_unknown++;
break;
}
- if (badwparsn) {
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
- "[%s] no WPA/RSN IE in association request\n",
- ether_sprintf(wh->i_addr2));
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_IE_INVALID);
- ieee80211_node_leave(ic, ni);
- ic->ic_stats.is_rx_assoc_badwpaie++;
- return;
- }
- if (wpa != NULL || rsn != NULL) {
- /*
- * Parse WPA/RSN information element. Note that
- * we initialize the param block from the node
- * state so that information in the IE overrides
- * our defaults. The resulting parameters are
- * installed below after the association is assured.
- */
- rsnparms = ni->ni_rsn;
- if (wpa != NULL)
- reason = ieee80211_parse_wpa(ic, wpa, &rsnparms, wh);
- else
- reason = ieee80211_parse_rsn(ic, rsn, &rsnparms, wh);
- if (reason != 0) {
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
- ieee80211_node_leave(ic, ni);
- /* XXX distinguish WPA/RSN? */
- ic->ic_stats.is_rx_assoc_badwpaie++;
- return;
- }
- IEEE80211_DPRINTF(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
- "[%s] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n",
- ether_sprintf(wh->i_addr2),
- wpa != NULL ? "WPA" : "RSN",
- rsnparms.rsn_mcastcipher, rsnparms.rsn_mcastkeylen,
- rsnparms.rsn_ucastcipher, rsnparms.rsn_ucastkeylen,
- rsnparms.rsn_keymgmt, rsnparms.rsn_caps);
- }
- /* discard challenge after association */
- if (ni->ni_challenge != NULL) {
- FREE(ni->ni_challenge, M_80211_NODE);
- ni->ni_challenge = NULL;
- }
- /* NB: 802.11 spec says to ignore station's privacy bit */
- if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
- capinfomismatch(ni, wh, reassoc, resp,
- "capability", capinfo);
- return;
- }
- /*
- * Disallow re-associate w/ invalid slot time setting.
- */
- if (ni->ni_associd != 0 &&
- IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
- ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME)) {
- capinfomismatch(ni, wh, reassoc, resp,
- "slot time", capinfo);
- return;
- }
- rate = ieee80211_setup_rates(ni, rates, xrates,
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (rate & IEEE80211_RATE_BASIC) {
- ratesetmismatch(ni, wh, reassoc, resp, "basic", rate);
- return;
- }
- /*
- * If constrained to 11g-only stations reject an
- * 11b-only station. We cheat a bit here by looking
- * at the max negotiated xmit rate and assuming anyone
- * with a best rate <24Mb/s is an 11b station.
- */
- if ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48) {
- ratesetmismatch(ni, wh, reassoc, resp, "11g", rate);
- return;
- }
- /* XXX enforce PUREN */
- /* 802.11n-specific rateset handling */
- if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) && htcap != NULL) {
- rate = ieee80211_setup_htrates(ni, htcap,
- IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO |
- IEEE80211_F_DOBRS);
- if (rate & IEEE80211_RATE_BASIC) {
- /* XXX 11n-specific stat */
- ratesetmismatch(ni, wh, reassoc, resp,
- "HT", rate);
- return;
- }
- ieee80211_ht_node_init(ni, htcap);
- } else if (ni->ni_flags & IEEE80211_NODE_HT)
- ieee80211_ht_node_cleanup(ni);
- /*
- * Allow AMPDU operation only with unencrypted traffic
- * or AES-CCM; the 11n spec only specifies these ciphers
- * so permitting any others is undefined and can lead
- * to interoperability problems.
- *
- * NB: We check for AES by looking at the GTK cipher
- * since the WPA/11i specs say the PTK cipher has
- * to be "as good or better".
- */
- if ((ni->ni_flags & IEEE80211_NODE_HT) &&
- (((ic->ic_flags & IEEE80211_F_WPA) &&
- rsnparms.rsn_mcastcipher != IEEE80211_CIPHER_AES_CCM) ||
- (ic->ic_flags & (IEEE80211_F_WPA|IEEE80211_F_PRIVACY)) == IEEE80211_F_PRIVACY)) {
- IEEE80211_NOTE(ic,
- IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni,
- "disallow HT use because WEP or TKIP requested, "
- "capinfo 0x%x mcastcipher %d", capinfo,
- rsnparms.rsn_mcastcipher);
- ieee80211_ht_node_cleanup(ni);
- ic->ic_stats.is_ht_assoc_downgrade++;
- }
- /*
- * If constrained to 11n-only stations reject legacy stations.
- */
- if ((ic->ic_flags_ext & IEEE80211_FEXT_PUREN) &&
- (ni->ni_flags & IEEE80211_NODE_HT) == 0) {
- htcapmismatch(ni, wh, reassoc, resp);
- ic->ic_stats.is_ht_assoc_nohtcap++;
- return;
- }
- ni->ni_rssi = rssi;
- ni->ni_noise = noise;
- ni->ni_rstamp = rstamp;
- ni->ni_intval = lintval;
- ni->ni_capinfo = capinfo;
- ni->ni_chan = ic->ic_bsschan;
- ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
- ni->ni_fhindex = ic->ic_bss->ni_fhindex;
- if (wpa != NULL) {
- /*
- * Record WPA parameters for station, mark
- * node as using WPA and record information element
- * for applications that require it.
- */
- ni->ni_rsn = rsnparms;
- ieee80211_saveie(&ni->ni_wpa_ie, wpa);
- } else if (ni->ni_wpa_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_wpa_ie, M_80211_NODE);
- ni->ni_wpa_ie = NULL;
- }
- if (rsn != NULL) {
- /*
- * Record RSN parameters for station, mark
- * node as using WPA and record information element
- * for applications that require it.
- */
- ni->ni_rsn = rsnparms;
- ieee80211_saveie(&ni->ni_rsn_ie, rsn);
- } else if (ni->ni_rsn_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_rsn_ie, M_80211_NODE);
- ni->ni_rsn_ie = NULL;
- }
- if (wme != NULL) {
- /*
- * Record WME parameters for station, mark node
- * as capable of QoS and record information
- * element for applications that require it.
- */
- ieee80211_saveie(&ni->ni_wme_ie, wme);
- ni->ni_flags |= IEEE80211_NODE_QOS;
- } else if (ni->ni_wme_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_wme_ie, M_80211_NODE);
- ni->ni_wme_ie = NULL;
- ni->ni_flags &= ~IEEE80211_NODE_QOS;
- }
- if (ath != NULL) {
- /*
- * Record ATH parameters for station, mark
- * node with appropriate capabilities, and
- * record the information element for
- * applications that require it.
- */
- ieee80211_saveath(ni, ath);
- } else if (ni->ni_ath_ie != NULL) {
- /*
- * Flush any state from a previous association.
- */
- FREE(ni->ni_ath_ie, M_80211_NODE);
- ni->ni_ath_ie = NULL;
- ni->ni_ath_flags = 0;
- }
- ieee80211_node_join(ic, ni, resp);
- ieee80211_deliver_l2uf(ni);
- break;
+ frm += frm[1] + 2;
}
-
- case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
- case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
- uint16_t capinfo, associd;
- uint16_t status;
-
- if (ic->ic_opmode != IEEE80211_M_STA ||
- ic->ic_state != IEEE80211_S_ASSOC) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
-
- /*
- * asresp frame format
- * [2] capability information
- * [2] status
- * [2] association ID
- * [tlv] supported rates
- * [tlv] extended supported rates
- * [tlv] WME
- * [tlv] HT capabilities
- * [tlv] HT info
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
- ni = ic->ic_bss;
- capinfo = le16toh(*(uint16_t *)frm);
- frm += 2;
- status = le16toh(*(uint16_t *)frm);
- frm += 2;
- if (status != 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] %sassoc failed (reason %d)\n",
- ether_sprintf(wh->i_addr2),
- ISREASSOC(subtype) ? "re" : "", status);
- if (ni != ic->ic_bss) /* XXX never true? */
- ni->ni_fails++;
- ic->ic_stats.is_rx_auth_fail++; /* XXX */
- return;
- }
- associd = le16toh(*(uint16_t *)frm);
- frm += 2;
-
- rates = xrates = wme = htcap = htinfo = NULL;
- while (efrm - frm > 1) {
- IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
- switch (*frm) {
- case IEEE80211_ELEMID_RATES:
- rates = frm;
- break;
- case IEEE80211_ELEMID_XRATES:
- xrates = frm;
- break;
- case IEEE80211_ELEMID_HTCAP:
- htcap = frm;
- break;
- case IEEE80211_ELEMID_HTINFO:
- htinfo = frm;
- break;
- case IEEE80211_ELEMID_VENDOR:
- if (iswmeoui(frm))
- wme = frm;
- else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
- /*
- * Accept pre-draft HT ie's if the
- * standard ones have not been seen.
- */
- if (ishtcapoui(frm)) {
- if (htcap == NULL)
- htcap = frm;
- } else if (ishtinfooui(frm)) {
- if (htinfo == NULL)
- htcap = frm;
- }
- }
- /* XXX Atheros OUI support */
- break;
- }
- frm += frm[1] + 2;
- }
-
- IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
- if (xrates != NULL)
- IEEE80211_VERIFY_ELEMENT(xrates,
- IEEE80211_RATE_MAXSIZE - rates[1]);
- rate = ieee80211_setup_rates(ni, rates, xrates,
- IEEE80211_F_JOIN |
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
- IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (rate & IEEE80211_RATE_BASIC) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] %sassoc failed (rate set mismatch)\n",
- ether_sprintf(wh->i_addr2),
- ISREASSOC(subtype) ? "re" : "");
- if (ni != ic->ic_bss) /* XXX never true? */
- ni->ni_fails++;
- ic->ic_stats.is_rx_assoc_norate++;
- ieee80211_new_state(ic, IEEE80211_S_SCAN,
- IEEE80211_SCAN_FAIL_STATUS);
- return;
- }
-
- ni->ni_capinfo = capinfo;
- ni->ni_associd = associd;
- if (wme != NULL &&
- ieee80211_parse_wmeparams(ic, wme, wh) >= 0) {
- ni->ni_flags |= IEEE80211_NODE_QOS;
- ieee80211_wme_updateparams(ic);
- } else
- ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ IEEE80211_VERIFY_ELEMENT(scan->rates, IEEE80211_RATE_MAXSIZE,
+ scan->status |= IEEE80211_BPARSE_RATES_INVALID);
+ if (scan->rates != NULL && scan->xrates != NULL) {
/*
- * Setup HT state according to the negotiation.
+ * NB: don't process XRATES if RATES is missing. This
+ * avoids a potential null ptr deref and should be ok
+ * as the return code will already note RATES is missing
+ * (so callers shouldn't otherwise process the frame).
*/
- if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
- htcap != NULL && htinfo != NULL) {
- ieee80211_ht_node_init(ni, htcap);
- ieee80211_parse_htinfo(ni, htinfo);
- ieee80211_setup_htrates(ni,
- htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
- ieee80211_setup_basic_htrates(ni, htinfo);
- if (ni->ni_chan != ic->ic_bsschan) {
- /*
- * Channel has been adjusted based on
- * negotiated HT parameters; force the
- * channel state to follow.
- */
- ieee80211_setbsschan(ic, ni->ni_chan);
- }
- }
- /*
- * Configure state now that we are associated.
- *
- * XXX may need different/additional driver callbacks?
- */
- if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
- (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
- ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
- ic->ic_flags &= ~IEEE80211_F_USEBARKER;
- } else {
- ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
- ic->ic_flags |= IEEE80211_F_USEBARKER;
- }
- ieee80211_set_shortslottime(ic,
- IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
- (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ IEEE80211_VERIFY_ELEMENT(scan->xrates,
+ IEEE80211_RATE_MAXSIZE - scan->rates[1],
+ scan->status |= IEEE80211_BPARSE_XRATES_INVALID);
+ }
+ IEEE80211_VERIFY_ELEMENT(scan->ssid, IEEE80211_NWID_LEN,
+ scan->status |= IEEE80211_BPARSE_SSID_INVALID);
+#if IEEE80211_CHAN_MAX < 255
+ if (scan->chan > IEEE80211_CHAN_MAX) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID,
+ wh, NULL, "invalid channel %u", scan->chan);
+ vap->iv_stats.is_rx_badchan++;
+ scan->status |= IEEE80211_BPARSE_CHAN_INVALID;
+ }
+#endif
+ if (scan->chan != scan->bchan && ic->ic_phytype != IEEE80211_T_FH) {
/*
- * Honor ERP protection.
+ * Frame was received on a channel different from the
+ * one indicated in the DS params element id;
+ * silently discard it.
*
- * NB: ni_erp should zero for non-11g operation.
- */
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
- (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
- ic->ic_flags |= IEEE80211_F_USEPROT;
- else
- ic->ic_flags &= ~IEEE80211_F_USEPROT;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] %sassoc success: %s preamble, %s slot time%s%s%s%s\n",
- ether_sprintf(wh->i_addr2),
- ISREASSOC(subtype) ? "re" : "",
- ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
- ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
- ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
- ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
- ni->ni_flags & IEEE80211_NODE_HT ?
- (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
- ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
- ", fast-frames" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
- ", turbo" : ""
- );
- ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
- break;
+ * NB: this can happen due to signal leakage.
+ * But we should take it for FH phy because
+ * the rssi value should be correct even for
+ * different hop pattern in FH.
+ */
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+ wh, NULL, "for off-channel %u", scan->chan);
+ vap->iv_stats.is_rx_chanmismatch++;
+ scan->status |= IEEE80211_BPARSE_OFFCHAN;
+ }
+ if (!(IEEE80211_BINTVAL_MIN <= scan->bintval &&
+ scan->bintval <= IEEE80211_BINTVAL_MAX)) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+ wh, NULL, "bogus beacon interval", scan->bintval);
+ vap->iv_stats.is_rx_badbintval++;
+ scan->status |= IEEE80211_BPARSE_BINTVAL_INVALID;
+ }
+ if (scan->country != NULL) {
+ /*
+ * Validate we have at least enough data to extract
+ * the country code. Not sure if we should return an
+ * error instead of discarding the IE; consider this
+ * being lenient as we don't depend on the data for
+ * correct operation.
+ */
+ IEEE80211_VERIFY_LENGTH(scan->country[1], 3 * sizeof(uint8_t),
+ scan->country = NULL);
}
-
- case IEEE80211_FC0_SUBTYPE_DEAUTH: {
- uint16_t reason;
-
- if (ic->ic_state == IEEE80211_S_SCAN) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * deauth frame format
- * [2] reason
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
- reason = le16toh(*(uint16_t *)frm);
- ic->ic_stats.is_rx_deauth++;
- IEEE80211_NODE_STAT(ni, rx_deauth);
-
- if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
- /* NB: can happen when in promiscuous mode */
- ic->ic_stats.is_rx_mgtdiscard++;
- break;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] recv deauthenticate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), reason);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ieee80211_new_state(ic, IEEE80211_S_AUTH,
- (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
- break;
- case IEEE80211_M_HOSTAP:
- if (ni != ic->ic_bss)
- ieee80211_node_leave(ic, ni);
- break;
- default:
- ic->ic_stats.is_rx_mgtdiscard++;
- break;
- }
- break;
+ /*
+ * Process HT ie's. This is complicated by our
+ * accepting both the standard ie's and the pre-draft
+ * vendor OUI ie's that some vendors still use/require.
+ */
+ if (scan->htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan->htcap[1],
+ scan->htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htcap)-2 :
+ sizeof(struct ieee80211_ie_htcap)-2,
+ scan->htcap = NULL);
}
+ if (scan->htinfo != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan->htinfo[1],
+ scan->htinfo[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof(struct ieee80211_ie_htinfo)-2 :
+ sizeof(struct ieee80211_ie_htinfo)-2,
+ scan->htinfo = NULL);
+ }
+ return scan->status;
+}
- case IEEE80211_FC0_SUBTYPE_DISASSOC: {
- uint16_t reason;
-
- if (ic->ic_state != IEEE80211_S_RUN &&
- ic->ic_state != IEEE80211_S_ASSOC &&
- ic->ic_state != IEEE80211_S_AUTH) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * disassoc frame format
- * [2] reason
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
- reason = le16toh(*(uint16_t *)frm);
- ic->ic_stats.is_rx_disassoc++;
- IEEE80211_NODE_STAT(ni, rx_disassoc);
+/*
+ * Parse an Action frame. Return 0 on success, non-zero on failure.
+ */
+int
+ieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ const struct ieee80211_action *ia;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm;
- if (!IEEE80211_ADDR_EQ(wh->i_addr1, ic->ic_myaddr)) {
- /* NB: can happen when in promiscuous mode */
- ic->ic_stats.is_rx_mgtdiscard++;
- break;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] recv disassociate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), reason);
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 0);
+ /*
+ * action frame format:
+ * [1] category
+ * [1] action
+ * [tlv] parameters
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m, u_int8_t *) + m->m_len;
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action), return EINVAL);
+ ia = (const struct ieee80211_action *) frm;
+
+ vap->iv_stats.is_rx_action++;
+ IEEE80211_NODE_STAT(ni, rx_action);
+
+ /* verify frame payloads but defer processing */
+ /* XXX maybe push this to method */
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbarequest),
+ return EINVAL);
break;
- case IEEE80211_M_HOSTAP:
- if (ni != ic->ic_bss)
- ieee80211_node_leave(ic, ni);
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_addbaresponse),
+ return EINVAL);
break;
- default:
- ic->ic_stats.is_rx_mgtdiscard++;
+ case IEEE80211_ACTION_BA_DELBA:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ba_delba),
+ return EINVAL);
break;
}
break;
- }
-
- case IEEE80211_FC0_SUBTYPE_ACTION: {
- const struct ieee80211_action *ia;
-
- if (ic->ic_state != IEEE80211_S_RUN &&
- ic->ic_state != IEEE80211_S_ASSOC &&
- ic->ic_state != IEEE80211_S_AUTH) {
- ic->ic_stats.is_rx_mgtdiscard++;
- return;
- }
- /*
- * action frame format:
- * [1] category
- * [1] action
- * [tlv] parameters
- */
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action), return);
- ia = (const struct ieee80211_action *) frm;
-
- ic->ic_stats.is_rx_action++;
- IEEE80211_NODE_STAT(ni, rx_action);
-
- /* verify frame payloads but defer processing */
- /* XXX maybe push this to method */
- switch (ia->ia_category) {
- case IEEE80211_ACTION_CAT_BA:
- switch (ia->ia_action) {
- case IEEE80211_ACTION_BA_ADDBA_REQUEST:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ba_addbarequest),
- return);
- break;
- case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ba_addbaresponse),
- return);
- break;
- case IEEE80211_ACTION_BA_DELBA:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ba_delba),
- return);
- break;
- }
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_txchwidth),
+ return EINVAL);
break;
- case IEEE80211_ACTION_CAT_HT:
- switch (ia->ia_action) {
- case IEEE80211_ACTION_HT_TXCHWIDTH:
- IEEE80211_VERIFY_LENGTH(efrm - frm,
- sizeof(struct ieee80211_action_ht_txchwidth),
- return);
- break;
- }
+ case IEEE80211_ACTION_HT_MIMOPWRSAVE:
+ IEEE80211_VERIFY_LENGTH(efrm - frm,
+ sizeof(struct ieee80211_action_ht_mimopowersave),
+ return EINVAL);
break;
}
- ic->ic_recv_action(ni, frm, efrm);
break;
}
-
- default:
- IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
- wh, "mgt", "subtype 0x%x not handled", subtype);
- ic->ic_stats.is_rx_badsubtype++;
- break;
- }
-#undef ISREASSOC
-#undef ISPROBE
-}
-#undef IEEE80211_VERIFY_LENGTH
-#undef IEEE80211_VERIFY_ELEMENT
-
-/*
- * Process a received ps-poll frame.
- */
-static void
-ieee80211_recv_pspoll(struct ieee80211com *ic,
- struct ieee80211_node *ni, struct mbuf *m0)
-{
- struct ieee80211_frame_min *wh;
- struct mbuf *m;
- uint16_t aid;
- int qlen;
-
- wh = mtod(m0, struct ieee80211_frame_min *);
- if (ni->ni_associd == 0) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
- (struct ieee80211_frame *) wh, "ps-poll",
- "%s", "unassociated station");
- ic->ic_stats.is_ps_unassoc++;
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_NOT_ASSOCED);
- return;
- }
-
- aid = le16toh(*(uint16_t *)wh->i_dur);
- if (aid != ni->ni_associd) {
- IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
- (struct ieee80211_frame *) wh, "ps-poll",
- "aid mismatch: sta aid 0x%x poll aid 0x%x",
- ni->ni_associd, aid);
- ic->ic_stats.is_ps_badaid++;
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_NOT_ASSOCED);
- return;
- }
-
- /* Okay, take the first queued packet and put it out... */
- IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
- if (m == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] recv ps-poll, but queue empty\n",
- ether_sprintf(wh->i_addr2));
- ieee80211_send_nulldata(ieee80211_ref_node(ni));
- ic->ic_stats.is_ps_qempty++; /* XXX node stat */
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0); /* just in case */
- return;
- }
- /*
- * If there are more packets, set the more packets bit
- * in the packet dispatched to the station; otherwise
- * turn off the TIM bit.
- */
- if (qlen != 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] recv ps-poll, send packet, %u still queued\n",
- ether_sprintf(ni->ni_macaddr), qlen);
- m->m_flags |= M_MORE_DATA;
- } else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] recv ps-poll, send packet, queue empty\n",
- ether_sprintf(ni->ni_macaddr));
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
- }
- m->m_flags |= M_PWR_SAV; /* bypass PS handling */
- IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+ return 0;
}
#ifdef IEEE80211_DEBUG
/*
* Debugging support.
*/
+void
+ieee80211_ssid_mismatch(struct ieee80211vap *vap, const char *tag,
+ uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid)
+{
+ printf("[%s] discard %s frame, ssid mismatch: ",
+ ether_sprintf(mac), tag);
+ ieee80211_print_essid(ssid + 2, ssid[1]);
+ printf("\n");
+}
/*
* Return the bssid of a frame.
*/
static const uint8_t *
-ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
+ieee80211_getbssid(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
{
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (vap->iv_opmode == IEEE80211_M_STA)
return wh->i_addr2;
if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS)
return wh->i_addr1;
@@ -3333,8 +831,10 @@ ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
return wh->i_addr3;
}
+#include <machine/stdarg.h>
+
void
-ieee80211_note(struct ieee80211com *ic, const char *fmt, ...)
+ieee80211_note(struct ieee80211vap *vap, const char *fmt, ...)
{
char buf[128]; /* XXX */
va_list ap;
@@ -3343,11 +843,11 @@ ieee80211_note(struct ieee80211com *ic, const char *fmt, ...)
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- if_printf(ic->ic_ifp, "%s", buf); /* NB: no \n */
+ if_printf(vap->iv_ifp, "%s", buf); /* NB: no \n */
}
void
-ieee80211_note_frame(struct ieee80211com *ic,
+ieee80211_note_frame(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *fmt, ...)
{
@@ -3357,12 +857,12 @@ ieee80211_note_frame(struct ieee80211com *ic,
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- if_printf(ic->ic_ifp, "[%s] %s\n",
- ether_sprintf(ieee80211_getbssid(ic, wh)), buf);
+ if_printf(vap->iv_ifp, "[%s] %s\n",
+ ether_sprintf(ieee80211_getbssid(vap, wh)), buf);
}
void
-ieee80211_note_mac(struct ieee80211com *ic,
+ieee80211_note_mac(struct ieee80211vap *vap,
const uint8_t mac[IEEE80211_ADDR_LEN],
const char *fmt, ...)
{
@@ -3372,22 +872,24 @@ ieee80211_note_mac(struct ieee80211com *ic,
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
- if_printf(ic->ic_ifp, "[%s] %s\n", ether_sprintf(mac), buf);
+ if_printf(vap->iv_ifp, "[%s] %s\n", ether_sprintf(mac), buf);
}
void
-ieee80211_discard_frame(struct ieee80211com *ic,
+ieee80211_discard_frame(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *type, const char *fmt, ...)
{
va_list ap;
- printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
- ether_sprintf(ieee80211_getbssid(ic, wh)));
- if (type != NULL)
+ 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);
- else
- printf("frame, ");
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
@@ -3395,14 +897,14 @@ ieee80211_discard_frame(struct ieee80211com *ic,
}
void
-ieee80211_discard_ie(struct ieee80211com *ic,
+ieee80211_discard_ie(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const char *type, const char *fmt, ...)
{
va_list ap;
- printf("[%s:%s] discard ", ic->ic_ifp->if_xname,
- ether_sprintf(ieee80211_getbssid(ic, wh)));
+ if_printf(vap->iv_ifp, "[%s] discard ",
+ ether_sprintf(ieee80211_getbssid(vap, wh)));
if (type != NULL)
printf("%s information element, ", type);
else
@@ -3414,13 +916,13 @@ ieee80211_discard_ie(struct ieee80211com *ic,
}
void
-ieee80211_discard_mac(struct ieee80211com *ic,
+ieee80211_discard_mac(struct ieee80211vap *vap,
const uint8_t mac[IEEE80211_ADDR_LEN],
const char *type, const char *fmt, ...)
{
va_list ap;
- printf("[%s:%s] discard ", ic->ic_ifp->if_xname, ether_sprintf(mac));
+ if_printf(vap->iv_ifp, "[%s] discard ", ether_sprintf(mac));
if (type != NULL)
printf("%s frame, ", type);
else
diff --git a/sys/net80211/ieee80211_input.h b/sys/net80211/ieee80211_input.h
new file mode 100644
index 0000000..dd41d7c
--- /dev/null
+++ b/sys/net80211/ieee80211_input.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_INPUT_H_
+#define _NET80211_IEEE80211_INPUT_H_
+
+/* Verify the existence and length of __elem or get out. */
+#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen, _action) do { \
+ if ((__elem) == NULL) { \
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
+ wh, NULL, "%s", "no " #__elem ); \
+ vap->iv_stats.is_rx_elem_missing++; \
+ _action; \
+ } else if ((__elem)[1] > (__maxlen)) { \
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
+ wh, NULL, "bad " #__elem " len %d", (__elem)[1]); \
+ vap->iv_stats.is_rx_elem_toobig++; \
+ _action; \
+ } \
+} while (0)
+
+#define IEEE80211_VERIFY_LENGTH(_len, _minlen, _action) do { \
+ if ((_len) < (_minlen)) { \
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ELEMID, \
+ wh, NULL, "ie too short, got %d, expected %d", \
+ (_len), (_minlen)); \
+ vap->iv_stats.is_rx_elem_toosmall++; \
+ _action; \
+ } \
+} while (0)
+
+#ifdef IEEE80211_DEBUG
+void ieee80211_ssid_mismatch(struct ieee80211vap *, const char *tag,
+ uint8_t mac[IEEE80211_ADDR_LEN], uint8_t *ssid);
+
+#define IEEE80211_VERIFY_SSID(_ni, _ssid, _action) do { \
+ if ((_ssid)[1] != 0 && \
+ ((_ssid)[1] != (_ni)->ni_esslen || \
+ memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
+ if (ieee80211_msg_input(vap)) \
+ ieee80211_ssid_mismatch(vap, \
+ ieee80211_mgt_subtype_name[subtype >> \
+ IEEE80211_FC0_SUBTYPE_SHIFT], \
+ wh->i_addr2, _ssid); \
+ vap->iv_stats.is_rx_ssidmismatch++; \
+ _action; \
+ } \
+} while (0)
+#else /* !IEEE80211_DEBUG */
+#define IEEE80211_VERIFY_SSID(_ni, _ssid, _action) do { \
+ if ((_ssid)[1] != 0 && \
+ ((_ssid)[1] != (_ni)->ni_esslen || \
+ memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \
+ vap->iv_stats.is_rx_ssidmismatch++; \
+ _action; \
+ } \
+} while (0)
+#endif /* !IEEE80211_DEBUG */
+
+/* unalligned little endian access */
+#define LE_READ_2(p) \
+ ((uint16_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8)))
+#define LE_READ_4(p) \
+ ((uint32_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8) | \
+ (((const uint8_t *)(p))[2] << 16) | \
+ (((const uint8_t *)(p))[3] << 24)))
+
+static __inline int
+iswpaoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static __inline int
+iswmeoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
+}
+
+static __inline int
+iswmeparam(const uint8_t *frm)
+{
+ return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+ frm[6] == WME_PARAM_OUI_SUBTYPE;
+}
+
+static __inline int
+iswmeinfo(const uint8_t *frm)
+{
+ return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+ frm[6] == WME_INFO_OUI_SUBTYPE;
+}
+
+static __inline int
+isatherosoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+static __inline int
+ishtcapoui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI);
+}
+
+static __inline int
+ishtinfooui(const uint8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI);
+}
+
+void ieee80211_deliver_data(struct ieee80211vap *,
+ struct ieee80211_node *, struct mbuf *);
+struct mbuf *ieee80211_defrag(struct ieee80211_node *,
+ struct mbuf *, int);
+struct mbuf *ieee80211_decap(struct ieee80211vap *, struct mbuf *, int);
+struct mbuf *ieee80211_decap1(struct mbuf *, int *);
+struct mbuf *ieee80211_decap_fastframe(struct ieee80211_node *,
+ struct mbuf *);
+int ieee80211_setup_rates(struct ieee80211_node *ni,
+ const uint8_t *rates, const uint8_t *xrates, int flags);
+void ieee80211_send_error(struct ieee80211_node *,
+ const uint8_t mac[IEEE80211_ADDR_LEN], int subtype, int arg);
+int ieee80211_alloc_challenge(struct ieee80211_node *);
+void ieee80211_parse_ath(struct ieee80211_node *, uint8_t *);
+int ieee80211_parse_beacon(struct ieee80211_node *, struct mbuf *,
+ struct ieee80211_scanparams *);
+int ieee80211_parse_action(struct ieee80211_node *, struct mbuf *);
+#endif /* _NET80211_IEEE80211_INPUT_H_ */
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 83627e8..3b088b1 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,14 +27,13 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include "opt_compat.h"
-
/*
* IEEE 802.11 ioctl support (FreeBSD-specific)
*/
#include "opt_inet.h"
#include "opt_ipx.h"
+#include "opt_wlan.h"
#include <sys/endian.h>
#include <sys/param.h>
@@ -61,33 +60,21 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_ioctl.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_input.h>
-#define IS_UP(_ic) \
- (((_ic)->ic_ifp->if_flags & IFF_UP) && \
- ((_ic)->ic_ifp->if_drv_flags & IFF_DRV_RUNNING))
-#define IS_UP_AUTO(_ic) \
- (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
-#define RESCAN 1
+#define IS_UP_AUTO(_vap) \
+ (IFNET_IS_UP_RUNNING(vap->iv_ifp) && \
+ (_vap)->iv_roaming == IEEE80211_ROAMING_AUTO)
+static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
static struct ieee80211_channel *findchannel(struct ieee80211com *,
int ieee, int mode);
-static int
-cap2cipher(int flag)
-{
- switch (flag) {
- case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP;
- case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB;
- case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM;
- case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP;
- case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP;
- }
- return -1;
-}
-
-static int
-ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
struct ieee80211req_key ik;
struct ieee80211_key *wk;
@@ -102,26 +89,26 @@ ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
return error;
kid = ik.ik_keyix;
if (kid == IEEE80211_KEYIX_NONE) {
- ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
+ ni = ieee80211_find_vap_node(&ic->ic_sta, vap, ik.ik_macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
wk = &ni->ni_ucastkey;
} else {
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- wk = &ic->ic_nw_keys[kid];
- IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr);
+ wk = &vap->iv_nw_keys[kid];
+ IEEE80211_ADDR_COPY(&ik.ik_macaddr, vap->iv_bss->ni_macaddr);
ni = NULL;
}
cip = wk->wk_cipher;
ik.ik_type = cip->ic_cipher;
ik.ik_keylen = wk->wk_keylen;
ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
- if (wk->wk_keyix == ic->ic_def_txkey)
+ if (wk->wk_keyix == vap->iv_def_txkey)
ik.ik_flags |= IEEE80211_KEY_DEFAULT;
if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
/* NB: only root can read key data */
- ik.ik_keyrsc = wk->wk_keyrsc;
+ ik.ik_keyrsc = wk->wk_keyrsc[IEEE80211_NONQOS_TID];
ik.ik_keytsc = wk->wk_keytsc;
memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
@@ -140,18 +127,20 @@ ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq)
return copyout(&ik, ireq->i_data, sizeof(ik));
}
-static int
-ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
if (sizeof(ic->ic_chan_active) < ireq->i_len)
ireq->i_len = sizeof(ic->ic_chan_active);
return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
}
-static int
-ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getchaninfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
int space;
space = __offsetof(struct ieee80211req_chaninfo,
@@ -162,8 +151,9 @@ ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
return copyout(&ic->ic_nchans, ireq->i_data, space);
}
-static int
-ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int req)
+static __noinline int
+ieee80211_ioctl_getwpaie(struct ieee80211vap *vap,
+ struct ieee80211req *ireq, int req)
{
struct ieee80211_node *ni;
struct ieee80211req_wpaie2 wpaie;
@@ -174,34 +164,34 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int
error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, wpaie.wpa_macaddr);
if (ni == NULL)
- return ENOENT; /* XXX */
+ return ENOENT;
memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
- if (ni->ni_wpa_ie != NULL) {
- int ielen = ni->ni_wpa_ie[1] + 2;
+ 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_wpa_ie, ielen);
+ 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_rsn_ie != NULL) {
- int ielen = ni->ni_rsn_ie[1] + 2;
+ 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_rsn_ie, ielen);
+ memcpy(wpaie.rsn_ie, ni->ni_ies.rsn_ie, ielen);
}
if (ireq->i_len > sizeof(struct ieee80211req_wpaie2))
ireq->i_len = sizeof(struct ieee80211req_wpaie2);
} else {
/* compatibility op, may overwrite wpa ie */
/* XXX check ic_flags? */
- if (ni->ni_rsn_ie != NULL) {
- int ielen = ni->ni_rsn_ie[1] + 2;
+ 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_rsn_ie, ielen);
+ 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);
@@ -210,8 +200,8 @@ ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq, int
return copyout(&wpaie, ireq->i_data, ireq->i_len);
}
-static int
-ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
uint8_t macaddr[IEEE80211_ADDR_LEN];
@@ -223,9 +213,9 @@ ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
if (ni == NULL)
- return EINVAL;
+ return ENOENT;
if (ireq->i_len > sizeof(struct ieee80211req_sta_stats))
ireq->i_len = sizeof(struct ieee80211req_sta_stats);
/* NB: copy out only the statistics */
@@ -235,149 +225,6 @@ ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
return error;
}
-static __inline uint8_t *
-copyie(uint8_t *cp, const uint8_t *ie)
-{
- if (ie != NULL) {
- memcpy(cp, ie, 2+ie[1]);
- cp += 2+ie[1];
- }
- return cp;
-}
-
-#ifdef COMPAT_FREEBSD6
-#define IEEE80211_IOC_SCAN_RESULTS_OLD 24
-
-struct scan_result_old {
- uint16_t isr_len; /* length (mult of 4) */
- uint16_t isr_freq; /* MHz */
- uint16_t isr_flags; /* channel flags */
- uint8_t isr_noise;
- uint8_t isr_rssi;
- uint8_t isr_intval; /* beacon interval */
- uint8_t isr_capinfo; /* capabilities */
- uint8_t isr_erp; /* ERP element */
- uint8_t isr_bssid[IEEE80211_ADDR_LEN];
- uint8_t isr_nrates;
- uint8_t isr_rates[IEEE80211_RATE_MAXSIZE];
- uint8_t isr_ssid_len; /* SSID length */
- uint8_t isr_ie_len; /* IE length */
- uint8_t isr_pad[5];
- /* variable length SSID followed by IE data */
-};
-
-struct oscanreq {
- struct scan_result_old *sr;
- size_t space;
-};
-
-static size_t
-old_scan_space(const struct ieee80211_scan_entry *se, int *ielen)
-{
- size_t len;
-
- *ielen = 0;
- if (se->se_wpa_ie != NULL)
- *ielen += 2+se->se_wpa_ie[1];
- if (se->se_wme_ie != NULL)
- *ielen += 2+se->se_wme_ie[1];
- /*
- * NB: ie's can be no more than 255 bytes and the max 802.11
- * packet is <3Kbytes so we are sure this doesn't overflow
- * 16-bits; if this is a concern we can drop the ie's.
- */
- len = sizeof(struct scan_result_old) + se->se_ssid[1] + *ielen;
- return roundup(len, sizeof(uint32_t));
-}
-
-static void
-old_get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
-{
- struct oscanreq *req = arg;
- int ielen;
-
- req->space += old_scan_space(se, &ielen);
-}
-
-static void
-old_get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
-{
- struct oscanreq *req = arg;
- struct scan_result_old *sr;
- int ielen, len, nr, nxr;
- uint8_t *cp;
-
- len = old_scan_space(se, &ielen);
- if (len > req->space)
- return;
-
- sr = req->sr;
- memset(sr, 0, sizeof(*sr));
- sr->isr_ssid_len = se->se_ssid[1];
- /* NB: beware of overflow, isr_ie_len is 8 bits */
- sr->isr_ie_len = (ielen > 255 ? 0 : ielen);
- sr->isr_len = len;
- sr->isr_freq = se->se_chan->ic_freq;
- sr->isr_flags = se->se_chan->ic_flags;
- sr->isr_rssi = se->se_rssi;
- sr->isr_noise = se->se_noise;
- sr->isr_intval = se->se_intval;
- sr->isr_capinfo = se->se_capinfo;
- sr->isr_erp = se->se_erp;
- IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
- nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
- memcpy(sr->isr_rates, se->se_rates+2, nr);
- nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
- memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
- sr->isr_nrates = nr + nxr;
-
- cp = (uint8_t *)(sr+1);
- memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
- cp += sr->isr_ssid_len;
- if (sr->isr_ie_len) {
- cp = copyie(cp, se->se_wpa_ie);
- cp = copyie(cp, se->se_wme_ie);
- }
-
- req->space -= len;
- req->sr = (struct scan_result_old *)(((uint8_t *)sr) + len);
-}
-
-static int
-old_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
-{
- struct oscanreq req;
- int error;
-
- if (ireq->i_len < sizeof(struct scan_result_old))
- return EFAULT;
-
- error = 0;
- req.space = 0;
- ieee80211_scan_iterate(ic, old_get_scan_space, &req);
- if (req.space > ireq->i_len)
- req.space = ireq->i_len;
- if (req.space > 0) {
- size_t space;
- void *p;
-
- space = req.space;
- /* XXX M_WAITOK after driver lock released */
- MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
- if (p == NULL)
- return ENOMEM;
- req.sr = p;
- ieee80211_scan_iterate(ic, old_get_scan_result, &req);
- ireq->i_len = space - req.space;
- error = copyout(p, ireq->i_data, ireq->i_len);
- FREE(p, M_TEMP);
- } else
- ireq->i_len = 0;
-
- return error;
-}
-#endif /* COMPAT_FREEBSD6 */
-
struct scanreq {
struct ieee80211req_scan_result *sr;
size_t space;
@@ -388,15 +235,7 @@ scan_space(const struct ieee80211_scan_entry *se, int *ielen)
{
size_t len;
- *ielen = 0;
- if (se->se_wpa_ie != NULL)
- *ielen += 2+se->se_wpa_ie[1];
- if (se->se_rsn_ie != NULL)
- *ielen += 2+se->se_rsn_ie[1];
- if (se->se_wme_ie != NULL)
- *ielen += 2+se->se_wme_ie[1];
- if (se->se_ath_ie != NULL)
- *ielen += 2+se->se_ath_ie[1];
+ *ielen = se->se_ies.len;
/*
* NB: ie's can be no more than 255 bytes and the max 802.11
* packet is <3Kbytes so we are sure this doesn't overflow
@@ -415,7 +254,7 @@ get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
req->space += scan_space(se, &ielen);
}
-static void
+static __noinline void
get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
{
struct scanreq *req = arg;
@@ -430,9 +269,9 @@ get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
sr = req->sr;
KASSERT(len <= 65535 && ielen <= 65535,
("len %u ssid %u ie %u", len, se->se_ssid[1], ielen));
+ sr->isr_len = len;
sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
sr->isr_ie_len = ielen;
- sr->isr_len = len;
sr->isr_freq = se->se_chan->ic_freq;
sr->isr_flags = se->se_chan->ic_flags;
sr->isr_rssi = se->se_rssi;
@@ -453,29 +292,26 @@ get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
if (ielen) {
cp += sr->isr_ssid_len;
- cp = copyie(cp, se->se_wpa_ie);
- cp = copyie(cp, se->se_rsn_ie);
- cp = copyie(cp, se->se_wme_ie);
- cp = copyie(cp, se->se_ath_ie);
- cp = copyie(cp, se->se_htcap_ie);
+ memcpy(cp, se->se_ies.data, ielen);
}
req->space -= len;
req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
}
-static int
-ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getscanresults(struct ieee80211vap *vap,
+ struct ieee80211req *ireq)
{
struct scanreq req;
int error;
- if (ireq->i_len < sizeof(struct ieee80211req_scan_result))
+ if (ireq->i_len < sizeof(struct scanreq))
return EFAULT;
error = 0;
req.space = 0;
- ieee80211_scan_iterate(ic, get_scan_space, &req);
+ ieee80211_scan_iterate(vap, get_scan_space, &req);
if (req.space > ireq->i_len)
req.space = ireq->i_len;
if (req.space > 0) {
@@ -488,7 +324,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire
if (p == NULL)
return ENOMEM;
req.sr = p;
- ieee80211_scan_iterate(ic, get_scan_result, &req);
+ 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);
@@ -499,7 +335,7 @@ ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ire
}
struct stainforeq {
- struct ieee80211com *ic;
+ struct ieee80211vap *vap;
struct ieee80211req_sta_info *si;
size_t space;
};
@@ -507,15 +343,7 @@ struct stainforeq {
static size_t
sta_space(const struct ieee80211_node *ni, size_t *ielen)
{
- *ielen = 0;
- if (ni->ni_wpa_ie != NULL)
- *ielen += 2+ni->ni_wpa_ie[1];
- if (ni->ni_rsn_ie != NULL)
- *ielen += 2+ni->ni_rsn_ie[1];
- if (ni->ni_wme_ie != NULL)
- *ielen += 2+ni->ni_wme_ie[1];
- if (ni->ni_ath_ie != NULL)
- *ielen += 2+ni->ni_ath_ie[1];
+ *ielen = ni->ni_ies.len;
return roundup(sizeof(struct ieee80211req_sta_info) + *ielen,
sizeof(uint32_t));
}
@@ -524,25 +352,28 @@ static void
get_sta_space(void *arg, struct ieee80211_node *ni)
{
struct stainforeq *req = arg;
- struct ieee80211com *ic = ni->ni_ic;
size_t ielen;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ 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 void
+static __noinline void
get_sta_info(void *arg, struct ieee80211_node *ni)
{
struct stainforeq *req = arg;
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211req_sta_info *si;
size_t ielen, len;
uint8_t *cp;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ if (req->vap != ni->ni_vap)
+ return;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
ni->ni_associd == 0) /* only associated stations */
return;
if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */
@@ -558,8 +389,8 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
si->isi_flags = ni->ni_chan->ic_flags;
si->isi_state = ni->ni_flags;
si->isi_authmode = ni->ni_authmode;
- ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
- si->isi_noise = 0; /* XXX */
+ vap->iv_ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
+ vap->iv_ic->ic_node_getmimoinfo(ni, &si->isi_mimo);
si->isi_capinfo = ni->ni_capinfo;
si->isi_erp = ni->ni_erp;
IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
@@ -568,7 +399,22 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
si->isi_nrates = 15;
memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
si->isi_txrate = ni->ni_txrate;
- si->isi_ie_len = ielen;
+ if (si->isi_txrate & IEEE80211_RATE_MCS) {
+ const struct ieee80211_mcs_rates *mcs =
+ &ieee80211_htrates[ni->ni_txrate &~ IEEE80211_RATE_MCS];
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+ if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
+ si->isi_txmbps = mcs->ht40_rate_800ns;
+ else
+ si->isi_txmbps = mcs->ht40_rate_400ns;
+ } else {
+ if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
+ si->isi_txmbps = mcs->ht20_rate_800ns;
+ else
+ si->isi_txmbps = mcs->ht20_rate_400ns;
+ }
+ } else
+ si->isi_txmbps = si->isi_txrate;
si->isi_associd = ni->ni_associd;
si->isi_txpower = ni->ni_txpower;
si->isi_vlan = ni->ni_vlan;
@@ -581,29 +427,29 @@ get_sta_info(void *arg, struct ieee80211_node *ni)
}
/* NB: leave all cases in case we relax ni_associd == 0 check */
if (ieee80211_node_is_authorized(ni))
- si->isi_inact = ic->ic_inact_run;
- else if (ni->ni_associd != 0)
- si->isi_inact = ic->ic_inact_auth;
+ si->isi_inact = vap->iv_inact_run;
+ else if (ni->ni_associd != 0 ||
+ (vap->iv_opmode == IEEE80211_M_WDS &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)))
+ si->isi_inact = vap->iv_inact_auth;
else
- si->isi_inact = ic->ic_inact_init;
+ si->isi_inact = vap->iv_inact_init;
si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
if (ielen) {
cp = ((uint8_t *)si) + si->isi_ie_off;
- cp = copyie(cp, ni->ni_wpa_ie);
- cp = copyie(cp, ni->ni_rsn_ie);
- cp = copyie(cp, ni->ni_wme_ie);
- cp = copyie(cp, ni->ni_ath_ie);
+ memcpy(cp, ni->ni_ies.data, ielen);
}
req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len);
req->space -= len;
}
-static int
-getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
+static __noinline int
+getstainfo_common(struct ieee80211vap *vap, struct ieee80211req *ireq,
struct ieee80211_node *ni, int off)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct stainforeq req;
size_t space;
void *p;
@@ -611,6 +457,7 @@ getstainfo_common(struct ieee80211com *ic, 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
@@ -620,7 +467,7 @@ getstainfo_common(struct ieee80211com *ic, struct ieee80211req *ireq,
if (req.space > 0) {
space = req.space;
/* XXX M_WAITOK after driver lock released */
- MALLOC(p, void *, space, M_TEMP, M_NOWAIT);
+ MALLOC(p, void *, space, M_TEMP, M_NOWAIT | M_ZERO);
if (p == NULL) {
error = ENOMEM;
goto bad;
@@ -641,8 +488,8 @@ bad:
return error;
}
-static int
-ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getstainfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
uint8_t macaddr[IEEE80211_ADDR_LEN];
const int off = __offsetof(struct ieee80211req_sta_req, info);
@@ -654,30 +501,18 @@ ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- if (IEEE80211_ADDR_EQ(macaddr, ic->ic_ifp->if_broadcastaddr)) {
+ if (IEEE80211_ADDR_EQ(macaddr, vap->iv_ifp->if_broadcastaddr)) {
ni = NULL;
} else {
- ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
if (ni == NULL)
- return EINVAL;
+ return ENOENT;
}
- return getstainfo_common(ic, ireq, ni, off);
+ return getstainfo_common(vap, ireq, ni, off);
}
-#ifdef COMPAT_FREEBSD6
-#define IEEE80211_IOC_STA_INFO_OLD 45
-
-static int
-old_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
-{
- if (ireq->i_len < sizeof(struct ieee80211req_sta_info))
- return EFAULT;
- return getstainfo_common(ic, ireq, NULL, 0);
-}
-#endif /* COMPAT_FREEBSD6 */
-
-static int
-ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
struct ieee80211req_sta_txpow txpow;
@@ -688,18 +523,19 @@ ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, &txpow, sizeof(txpow));
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
txpow.it_txpow = ni->ni_txpower;
error = copyout(&txpow, ireq->i_data, sizeof(txpow));
ieee80211_free_node(ni);
return error;
}
-static int
-ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
struct wmeParams *wmep;
int ac;
@@ -739,12 +575,12 @@ ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
return 0;
}
-static int
-ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
- const struct ieee80211_aclator *acl = ic->ic_acl;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
- return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq));
+ return (acl == NULL ? EINVAL : acl->iac_getioctl(vap, ireq));
}
/*
@@ -753,20 +589,151 @@ ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
* setting. Otherwise report the current setting.
*/
static int
-getathcap(struct ieee80211com *ic, int cap)
+getathcap(struct ieee80211vap *vap, int cap)
{
- if (ic->ic_opmode == IEEE80211_M_STA && ic->ic_state == IEEE80211_S_RUN)
- return IEEE80211_ATH_CAP(ic, ic->ic_bss, cap) != 0;
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_state == IEEE80211_S_RUN)
+ return IEEE80211_ATH_CAP(vap, vap->iv_bss, cap) != 0;
else
- return (ic->ic_flags & cap) != 0;
+ return (vap->iv_flags & cap) != 0;
}
-static int
-ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_getcurchan(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_channel *c;
+
if (ireq->i_len != sizeof(struct ieee80211_channel))
return EINVAL;
- return copyout(ic->ic_curchan, ireq->i_data, sizeof(*ic->ic_curchan));
+ /*
+ * vap's may have different operating channels when HT is
+ * in use. When in RUN state report the vap-specific channel.
+ * Otherwise return curchan.
+ */
+ if (vap->iv_state == IEEE80211_S_RUN)
+ c = vap->iv_bss->ni_chan;
+ else
+ c = ic->ic_curchan;
+ return copyout(c, ireq->i_data, sizeof(*c));
+}
+
+static int
+getappie(const struct ieee80211_appie *aie, struct ieee80211req *ireq)
+{
+ if (aie == NULL)
+ return EINVAL;
+ /* NB: truncate, caller can check length */
+ if (ireq->i_len > aie->ie_len)
+ ireq->i_len = aie->ie_len;
+ return copyout(aie->ie_data, ireq->i_data, ireq->i_len);
+}
+
+static int
+ieee80211_ioctl_getappie(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ uint8_t fc0;
+
+ fc0 = ireq->i_val & 0xff;
+ if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
+ return EINVAL;
+ /* NB: could check iv_opmode and reject but hardly worth the effort */
+ switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return getappie(vap->iv_appie_beacon, ireq);
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ return getappie(vap->iv_appie_proberesp, ireq);
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ return getappie(vap->iv_appie_assocresp, ireq);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return getappie(vap->iv_appie_probereq, ireq);
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ return getappie(vap->iv_appie_assocreq, ireq);
+ case IEEE80211_FC0_SUBTYPE_BEACON|IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ return getappie(vap->iv_appie_wpa, ireq);
+ }
+ return EINVAL;
+}
+
+static __noinline int
+ieee80211_ioctl_getregdomain(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ if (ireq->i_len != sizeof(ic->ic_regdomain))
+ return EINVAL;
+ return copyout(&ic->ic_regdomain, ireq->i_data,
+ sizeof(ic->ic_regdomain));
+}
+
+static __noinline int
+ieee80211_ioctl_getroam(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ if (ireq->i_len != sizeof(vap->iv_roamparms))
+ return EINVAL;
+ return copyout(vap->iv_roamparms, ireq->i_data,
+ sizeof(vap->iv_roamparms));
+}
+
+static __noinline int
+ieee80211_ioctl_gettxparams(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ if (ireq->i_len != sizeof(vap->iv_txparms))
+ return EINVAL;
+ return copyout(vap->iv_txparms, ireq->i_data, sizeof(vap->iv_txparms));
+}
+
+static __noinline int
+ieee80211_ioctl_getdevcaps(struct ieee80211com *ic,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211_devcaps_req *dc;
+ struct ieee80211req_chaninfo *ci;
+ int error;
+
+ if (ireq->i_len != sizeof(struct ieee80211_devcaps_req))
+ return EINVAL;
+ MALLOC(dc, struct ieee80211_devcaps_req *,
+ sizeof(struct ieee80211_devcaps_req), M_TEMP, M_NOWAIT | M_ZERO);
+ if (dc == NULL)
+ return ENOMEM;
+ dc->dc_drivercaps = ic->ic_caps;
+ dc->dc_cryptocaps = ic->ic_cryptocaps;
+ dc->dc_htcaps = ic->ic_htcaps;
+ ci = &dc->dc_chaninfo;
+ ic->ic_getradiocaps(ic, &ci->ic_nchans, ci->ic_chans);
+ ieee80211_sort_channels(ci->ic_chans, ci->ic_nchans);
+ error = copyout(dc, ireq->i_data, sizeof(*dc));
+ FREE(dc, M_TEMP);
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_getstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_sta_vlan vlan;
+ int error;
+
+ if (ireq->i_len != sizeof(vlan))
+ return EINVAL;
+ error = copyin(ireq->i_data, &vlan, sizeof(vlan));
+ if (error != 0)
+ return error;
+ if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) {
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ vlan.sv_macaddr);
+ if (ni == NULL)
+ return ENOENT;
+ } else
+ ni = ieee80211_ref_node(vap->iv_bss);
+ vlan.sv_vlan = ni->ni_vlan;
+ error = copyout(&vlan, ireq->i_data, sizeof(vlan));
+ ieee80211_free_node(ni);
+ return error;
}
/*
@@ -785,30 +752,28 @@ ieee80211_ioctl_getcurchan(struct ieee80211com *ic, struct ieee80211req *ireq)
* but special-casing the compilation of this one module in the
* build system would be awkward.
*/
-#ifdef __GNUC__
-__attribute__ ((noinline))
-#endif
-static int
-ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
+ struct ieee80211req *ireq)
{
- const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
- int error = 0;
- u_int kid, len, m;
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+ struct ieee80211com *ic = vap->iv_ic;
+ u_int kid, len;
uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
char tmpssid[IEEE80211_NWID_LEN];
+ int error = 0;
switch (ireq->i_type) {
case IEEE80211_IOC_SSID:
- switch (ic->ic_state) {
+ switch (vap->iv_state) {
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
- ireq->i_len = ic->ic_des_ssid[0].len;
- memcpy(tmpssid, ic->ic_des_ssid[0].ssid, ireq->i_len);
+ ireq->i_len = vap->iv_des_ssid[0].len;
+ memcpy(tmpssid, vap->iv_des_ssid[0].ssid, ireq->i_len);
break;
default:
- ireq->i_len = ic->ic_bss->ni_esslen;
- memcpy(tmpssid, ic->ic_bss->ni_essid,
- ireq->i_len);
+ ireq->i_len = vap->iv_bss->ni_esslen;
+ memcpy(tmpssid, vap->iv_bss->ni_essid, ireq->i_len);
break;
}
error = copyout(tmpssid, ireq->i_data, ireq->i_len);
@@ -817,9 +782,9 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ireq->i_val = 1;
break;
case IEEE80211_IOC_WEP:
- if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0)
ireq->i_val = IEEE80211_WEP_OFF;
- else if (ic->ic_flags & IEEE80211_F_DROPUNENC)
+ else if (vap->iv_flags & IEEE80211_F_DROPUNENC)
ireq->i_val = IEEE80211_WEP_ON;
else
ireq->i_val = IEEE80211_WEP_MIXED;
@@ -828,10 +793,10 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
kid = (u_int) ireq->i_val;
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
+ len = (u_int) vap->iv_nw_keys[kid].wk_keylen;
/* NB: only root can read WEP keys */
if (priv_check(curthread, PRIV_NET80211_GETKEY) == 0) {
- bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
+ bcopy(vap->iv_nw_keys[kid].wk_key, tmpkey, len);
} else {
bzero(tmpkey, len);
}
@@ -842,19 +807,19 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ireq->i_val = IEEE80211_WEP_NKID;
break;
case IEEE80211_IOC_WEPTXKEY:
- ireq->i_val = ic->ic_def_txkey;
+ ireq->i_val = vap->iv_def_txkey;
break;
case IEEE80211_IOC_AUTHMODE:
- if (ic->ic_flags & IEEE80211_F_WPA)
+ if (vap->iv_flags & IEEE80211_F_WPA)
ireq->i_val = IEEE80211_AUTH_WPA;
else
- ireq->i_val = ic->ic_bss->ni_authmode;
+ ireq->i_val = vap->iv_bss->ni_authmode;
break;
case IEEE80211_IOC_CHANNEL:
ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan);
break;
case IEEE80211_IOC_POWERSAVE:
- if (ic->ic_flags & IEEE80211_F_PMGTON)
+ if (vap->iv_flags & IEEE80211_F_PMGTON)
ireq->i_val = IEEE80211_POWERSAVE_ON;
else
ireq->i_val = IEEE80211_POWERSAVE_OFF;
@@ -863,42 +828,25 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ireq->i_val = ic->ic_lintval;
break;
case IEEE80211_IOC_RTSTHRESHOLD:
- ireq->i_val = ic->ic_rtsthreshold;
+ ireq->i_val = vap->iv_rtsthreshold;
break;
case IEEE80211_IOC_PROTMODE:
ireq->i_val = ic->ic_protmode;
break;
case IEEE80211_IOC_TXPOWER:
- if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
- return EINVAL;
- ireq->i_val = ic->ic_txpowlimit;
- break;
- case IEEE80211_IOC_MCASTCIPHER:
- ireq->i_val = rsn->rsn_mcastcipher;
- break;
- case IEEE80211_IOC_MCASTKEYLEN:
- ireq->i_val = rsn->rsn_mcastkeylen;
- break;
- case IEEE80211_IOC_UCASTCIPHERS:
- ireq->i_val = 0;
- for (m = 0x1; m != 0; m <<= 1)
- if (rsn->rsn_ucastcipherset & m)
- ireq->i_val |= 1<<cap2cipher(m);
- break;
- case IEEE80211_IOC_UCASTCIPHER:
- ireq->i_val = rsn->rsn_ucastcipher;
- break;
- case IEEE80211_IOC_UCASTKEYLEN:
- ireq->i_val = rsn->rsn_ucastkeylen;
- break;
- case IEEE80211_IOC_KEYMGTALGS:
- ireq->i_val = rsn->rsn_keymgmtset;
- break;
- case IEEE80211_IOC_RSNCAPS:
- ireq->i_val = rsn->rsn_caps;
+ /*
+ * Tx power limit is the min of max regulatory
+ * power, any user-set limit, and the max the
+ * radio can do.
+ */
+ ireq->i_val = 2*ic->ic_curchan->ic_maxregpower;
+ if (ireq->i_val > ic->ic_txpowlimit)
+ ireq->i_val = ic->ic_txpowlimit;
+ if (ireq->i_val > ic->ic_curchan->ic_maxpower)
+ ireq->i_val = ic->ic_curchan->ic_maxpower;
break;
case IEEE80211_IOC_WPA:
- switch (ic->ic_flags & IEEE80211_F_WPA) {
+ switch (vap->iv_flags & IEEE80211_F_WPA) {
case IEEE80211_F_WPA1:
ireq->i_val = 1;
break;
@@ -914,85 +862,63 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
}
break;
case IEEE80211_IOC_CHANLIST:
- error = ieee80211_ioctl_getchanlist(ic, ireq);
+ error = ieee80211_ioctl_getchanlist(vap, ireq);
break;
case IEEE80211_IOC_ROAMING:
- ireq->i_val = ic->ic_roaming;
+ ireq->i_val = vap->iv_roaming;
break;
case IEEE80211_IOC_PRIVACY:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_PRIVACY) != 0;
break;
case IEEE80211_IOC_DROPUNENCRYPTED:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_DROPUNENC) != 0;
break;
case IEEE80211_IOC_COUNTERMEASURES:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0;
- break;
- case IEEE80211_IOC_DRIVER_CAPS:
- ireq->i_val = ic->ic_caps>>16;
- ireq->i_len = ic->ic_caps&0xffff;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_COUNTERM) != 0;
break;
case IEEE80211_IOC_WME:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_WME) != 0;
break;
case IEEE80211_IOC_HIDESSID:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_HIDESSID) != 0;
break;
case IEEE80211_IOC_APBRIDGE:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0;
- break;
- case IEEE80211_IOC_OPTIE:
- if (ic->ic_opt_ie == NULL)
- return EINVAL;
- /* NB: truncate, caller can check length */
- if (ireq->i_len > ic->ic_opt_ie_len)
- ireq->i_len = ic->ic_opt_ie_len;
- error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len);
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0;
break;
case IEEE80211_IOC_WPAKEY:
- error = ieee80211_ioctl_getkey(ic, ireq);
+ error = ieee80211_ioctl_getkey(vap, ireq);
break;
case IEEE80211_IOC_CHANINFO:
- error = ieee80211_ioctl_getchaninfo(ic, ireq);
+ error = ieee80211_ioctl_getchaninfo(vap, ireq);
break;
case IEEE80211_IOC_BSSID:
if (ireq->i_len != IEEE80211_ADDR_LEN)
return EINVAL;
- error = copyout(ic->ic_state == IEEE80211_S_RUN ?
- ic->ic_bss->ni_bssid :
- ic->ic_des_bssid,
+ error = copyout(vap->iv_state == IEEE80211_S_RUN ?
+ vap->iv_bss->ni_bssid :
+ vap->iv_des_bssid,
ireq->i_data, ireq->i_len);
break;
case IEEE80211_IOC_WPAIE:
- error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
+ error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type);
break;
case IEEE80211_IOC_WPAIE2:
- error = ieee80211_ioctl_getwpaie(ic, ireq, ireq->i_type);
- break;
-#ifdef COMPAT_FREEBSD6
- case IEEE80211_IOC_SCAN_RESULTS_OLD:
- error = old_getscanresults(ic, ireq);
+ error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type);
break;
-#endif
case IEEE80211_IOC_SCAN_RESULTS:
- error = ieee80211_ioctl_getscanresults(ic, ireq);
+ error = ieee80211_ioctl_getscanresults(vap, ireq);
break;
case IEEE80211_IOC_STA_STATS:
- error = ieee80211_ioctl_getstastats(ic, ireq);
+ error = ieee80211_ioctl_getstastats(vap, ireq);
break;
case IEEE80211_IOC_TXPOWMAX:
- ireq->i_val = ic->ic_bss->ni_txpower;
+ ireq->i_val = vap->iv_bss->ni_txpower;
break;
case IEEE80211_IOC_STA_TXPOW:
- error = ieee80211_ioctl_getstatxpow(ic, ireq);
- break;
-#ifdef COMPAT_FREEBSD6
- case IEEE80211_IOC_STA_INFO_OLD:
- error = old_getstainfo(ic, ireq);
+ error = ieee80211_ioctl_getstatxpow(vap, ireq);
break;
-#endif
case IEEE80211_IOC_STA_INFO:
- error = ieee80211_ioctl_getstainfo(ic, ireq);
+ error = ieee80211_ioctl_getstainfo(vap, ireq);
break;
case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
@@ -1000,180 +926,163 @@ ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */
- error = ieee80211_ioctl_getwmeparam(ic, ireq);
+ error = ieee80211_ioctl_getwmeparam(vap, ireq);
break;
case IEEE80211_IOC_DTIM_PERIOD:
- ireq->i_val = ic->ic_dtim_period;
+ ireq->i_val = vap->iv_dtim_period;
break;
case IEEE80211_IOC_BEACON_INTERVAL:
/* NB: get from ic_bss for station mode */
- ireq->i_val = ic->ic_bss->ni_intval;
+ ireq->i_val = vap->iv_bss->ni_intval;
break;
case IEEE80211_IOC_PUREG:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_PUREG) != 0;
break;
case IEEE80211_IOC_FF:
- ireq->i_val = getathcap(ic, IEEE80211_F_FF);
+ ireq->i_val = getathcap(vap, IEEE80211_F_FF);
break;
case IEEE80211_IOC_TURBOP:
- ireq->i_val = getathcap(ic, IEEE80211_F_TURBOP);
+ ireq->i_val = getathcap(vap, IEEE80211_F_TURBOP);
break;
case IEEE80211_IOC_BGSCAN:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_BGSCAN) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_BGSCAN) != 0;
break;
case IEEE80211_IOC_BGSCAN_IDLE:
- ireq->i_val = ic->ic_bgscanidle*hz/1000; /* ms */
+ ireq->i_val = vap->iv_bgscanidle*hz/1000; /* ms */
break;
case IEEE80211_IOC_BGSCAN_INTERVAL:
- ireq->i_val = ic->ic_bgscanintvl/hz; /* seconds */
+ ireq->i_val = vap->iv_bgscanintvl/hz; /* seconds */
break;
case IEEE80211_IOC_SCANVALID:
- ireq->i_val = ic->ic_scanvalid/hz; /* seconds */
- break;
- case IEEE80211_IOC_ROAM_RSSI_11A:
- ireq->i_val = ic->ic_roam.rssi11a;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11B:
- ireq->i_val = ic->ic_roam.rssi11bOnly;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11G:
- ireq->i_val = ic->ic_roam.rssi11b;
- break;
- case IEEE80211_IOC_ROAM_RATE_11A:
- ireq->i_val = ic->ic_roam.rate11a;
- break;
- case IEEE80211_IOC_ROAM_RATE_11B:
- ireq->i_val = ic->ic_roam.rate11bOnly;
- break;
- case IEEE80211_IOC_ROAM_RATE_11G:
- ireq->i_val = ic->ic_roam.rate11b;
- break;
- case IEEE80211_IOC_MCAST_RATE:
- ireq->i_val = ic->ic_mcast_rate;
+ ireq->i_val = vap->iv_scanvalid/hz; /* seconds */
break;
case IEEE80211_IOC_FRAGTHRESHOLD:
- ireq->i_val = ic->ic_fragthreshold;
+ ireq->i_val = vap->iv_fragthreshold;
break;
case IEEE80211_IOC_MACCMD:
- error = ieee80211_ioctl_getmaccmd(ic, ireq);
+ error = ieee80211_ioctl_getmaccmd(vap, ireq);
break;
case IEEE80211_IOC_BURST:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_BURST) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_BURST) != 0;
break;
case IEEE80211_IOC_BMISSTHRESHOLD:
- ireq->i_val = ic->ic_bmissthreshold;
+ ireq->i_val = vap->iv_bmissthreshold;
break;
case IEEE80211_IOC_CURCHAN:
- error = ieee80211_ioctl_getcurchan(ic, ireq);
+ error = ieee80211_ioctl_getcurchan(vap, ireq);
break;
case IEEE80211_IOC_SHORTGI:
ireq->i_val = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI20)
ireq->i_val |= IEEE80211_HTCAP_SHORTGI20;
- if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SHORTGI40)
ireq->i_val |= IEEE80211_HTCAP_SHORTGI40;
break;
case IEEE80211_IOC_AMPDU:
ireq->i_val = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_TX)
ireq->i_val |= 1;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_RX)
ireq->i_val |= 2;
break;
case IEEE80211_IOC_AMPDU_LIMIT:
- ireq->i_val = ic->ic_ampdu_limit; /* XXX truncation? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ ireq->i_val = vap->iv_ampdu_rxmax;
+ else if (vap->iv_state == IEEE80211_S_RUN)
+ ireq->i_val = MS(vap->iv_bss->ni_htparam,
+ IEEE80211_HTCAP_MAXRXAMPDU);
+ else
+ ireq->i_val = vap->iv_ampdu_limit;
break;
case IEEE80211_IOC_AMPDU_DENSITY:
- ireq->i_val = ic->ic_ampdu_density;
+ if (vap->iv_state == IEEE80211_S_RUN)
+ ireq->i_val = MS(vap->iv_bss->ni_htparam,
+ IEEE80211_HTCAP_MPDUDENSITY);
+ else
+ ireq->i_val = vap->iv_ampdu_density;
break;
case IEEE80211_IOC_AMSDU:
ireq->i_val = 0;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_TX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMSDU_TX)
ireq->i_val |= 1;
- if (ic->ic_flags_ext & IEEE80211_FEXT_AMSDU_RX)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_AMSDU_RX)
ireq->i_val |= 2;
break;
case IEEE80211_IOC_AMSDU_LIMIT:
- ireq->i_val = ic->ic_amsdu_limit; /* XXX truncation? */
+ ireq->i_val = vap->iv_amsdu_limit; /* XXX truncation? */
break;
case IEEE80211_IOC_PUREN:
- ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_PUREN) != 0;
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_PUREN) != 0;
break;
case IEEE80211_IOC_DOTH:
- ireq->i_val = (ic->ic_flags & IEEE80211_F_DOTH) != 0;
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_DOTH) != 0;
+ break;
+ case IEEE80211_IOC_REGDOMAIN:
+ error = ieee80211_ioctl_getregdomain(vap, ireq);
+ break;
+ case IEEE80211_IOC_ROAM:
+ error = ieee80211_ioctl_getroam(vap, ireq);
+ break;
+ case IEEE80211_IOC_TXPARAMS:
+ error = ieee80211_ioctl_gettxparams(vap, ireq);
break;
case IEEE80211_IOC_HTCOMPAT:
- ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) != 0;
+ break;
+ case IEEE80211_IOC_DWDS:
+ ireq->i_val = (vap->iv_flags & IEEE80211_F_DWDS) != 0;
break;
case IEEE80211_IOC_INACTIVITY:
- ireq->i_val = (ic->ic_flags_ext & IEEE80211_FEXT_INACT) != 0;
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_INACT) != 0;
+ break;
+ case IEEE80211_IOC_APPIE:
+ error = ieee80211_ioctl_getappie(vap, ireq);
+ break;
+ case IEEE80211_IOC_WPS:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_WPS) != 0;
+ break;
+ case IEEE80211_IOC_TSN:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_TSN) != 0;
+ break;
+ case IEEE80211_IOC_DFS:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DFS) != 0;
+ break;
+ case IEEE80211_IOC_DOTD:
+ ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DOTD) != 0;
+ break;
+ case IEEE80211_IOC_DEVCAPS:
+ error = ieee80211_ioctl_getdevcaps(ic, ireq);
break;
case IEEE80211_IOC_HTPROTMODE:
ireq->i_val = ic->ic_htprotmode;
break;
case IEEE80211_IOC_HTCONF:
- if (ic->ic_flags_ext & IEEE80211_FEXT_HT) {
+ if (vap->iv_flags_ext & IEEE80211_FEXT_HT) {
ireq->i_val = 1;
- if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+ if (vap->iv_flags_ext & IEEE80211_FEXT_USEHT40)
ireq->i_val |= 2;
} else
ireq->i_val = 0;
break;
+ case IEEE80211_IOC_STA_VLAN:
+ error = ieee80211_ioctl_getstavlan(vap, ireq);
+ break;
default:
error = EINVAL;
break;
}
return error;
+#undef MS
}
-static int
-ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
-{
- int error;
- void *ie, *oie;
-
- /*
- * NB: Doing this for ap operation could be useful (e.g. for
- * WPA and/or WME) except that it typically is worthless
- * without being able to intervene when processing
- * association response frames--so disallow it for now.
- */
- if (ic->ic_opmode != IEEE80211_M_STA)
- return EINVAL;
- if (ireq->i_len > IEEE80211_MAX_OPT_IE)
- return EINVAL;
- /* NB: data.length is validated by the wireless extensions code */
- /* XXX M_WAITOK after driver lock released */
- if (ireq->i_len > 0) {
- MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_NOWAIT);
- if (ie == NULL)
- return ENOMEM;
- error = copyin(ireq->i_data, ie, ireq->i_len);
- if (error) {
- FREE(ie, M_DEVBUF);
- return error;
- }
- } else {
- ie = NULL;
- ireq->i_len = 0;
- }
- /* XXX sanity check data? */
- oie = ic->ic_opt_ie;
- ic->ic_opt_ie = ie;
- ic->ic_opt_ie_len = ireq->i_len;
- if (oie != NULL)
- FREE(oie, M_DEVBUF);
- return 0;
-}
-
-static int
-ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_key ik;
struct ieee80211_node *ni;
struct ieee80211_key *wk;
uint16_t kid;
- int error;
+ int error, i;
if (ireq->i_len != sizeof(ik))
return EINVAL;
@@ -1189,14 +1098,15 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
/* XXX unicast keys currently must be tx/rx */
if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
return EINVAL;
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ni = ieee80211_ref_node(ic->ic_bss);
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ ni = ieee80211_ref_node(vap->iv_bss);
if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) {
ieee80211_free_node(ni);
return EADDRNOTAVAIL;
}
} else {
- ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ ik.ik_macaddr);
if (ni == NULL)
return ENOENT;
}
@@ -1204,7 +1114,7 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
} else {
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- wk = &ic->ic_nw_keys[kid];
+ wk = &vap->iv_nw_keys[kid];
/*
* Global slots start off w/o any assigned key index.
* Force one here for consistency with IEEE80211_IOC_WEPKEY.
@@ -1214,31 +1124,32 @@ ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
ni = NULL;
}
error = 0;
- ieee80211_key_update_begin(ic);
- if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) {
+ ieee80211_key_update_begin(vap);
+ if (ieee80211_crypto_newkey(vap, ik.ik_type, ik.ik_flags, wk)) {
wk->wk_keylen = ik.ik_keylen;
/* NB: MIC presence is implied by cipher type */
if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
- wk->wk_keyrsc = ik.ik_keyrsc;
+ for (i = 0; i < IEEE80211_TID_SIZE; i++)
+ wk->wk_keyrsc[i] = ik.ik_keyrsc;
wk->wk_keytsc = 0; /* new key, reset */
memset(wk->wk_key, 0, sizeof(wk->wk_key));
memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
- if (!ieee80211_crypto_setkey(ic, wk,
+ if (!ieee80211_crypto_setkey(vap, wk,
ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
error = EIO;
else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
- ic->ic_def_txkey = kid;
+ vap->iv_def_txkey = kid;
} else
error = ENXIO;
- ieee80211_key_update_end(ic);
+ ieee80211_key_update_end(vap);
if (ni != NULL)
ieee80211_free_node(ni);
return error;
}
-static int
-ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_delkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_del_key dk;
int kid, error;
@@ -1253,14 +1164,15 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) {
struct ieee80211_node *ni;
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ni = ieee80211_ref_node(ic->ic_bss);
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+ ni = ieee80211_ref_node(vap->iv_bss);
if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) {
ieee80211_free_node(ni);
return EADDRNOTAVAIL;
}
} else {
- ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ dk.idk_macaddr);
if (ni == NULL)
return ENOENT;
}
@@ -1271,25 +1183,216 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
/* XXX error return */
- ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
+ ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[kid]);
}
return 0;
}
+struct mlmeop {
+ struct ieee80211vap *vap;
+ int op;
+ int reason;
+};
+
+static void
+mlmedebug(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
+ int op, int reason)
+{
+#ifdef IEEE80211_DEBUG
+ static const struct {
+ int mask;
+ const char *opstr;
+ } ops[] = {
+ { 0, "op#0" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_ASSOC, "assoc" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_ASSOC, "disassoc" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_AUTH, "deauth" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_AUTH, "authorize" },
+ { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
+ IEEE80211_MSG_AUTH, "unauthorize" },
+ };
+
+ if (op == IEEE80211_MLME_AUTH) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_IOCTL |
+ IEEE80211_MSG_STATE | IEEE80211_MSG_AUTH, mac,
+ "station authenticate %s via MLME (reason %d)",
+ reason == IEEE80211_STATUS_SUCCESS ? "ACCEPT" : "REJECT",
+ reason);
+ } else if (!(IEEE80211_MLME_ASSOC <= op && op <= IEEE80211_MLME_AUTH)) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, mac,
+ "unknown MLME request %d (reason %d)", op, reason);
+ } else if (reason == IEEE80211_STATUS_SUCCESS) {
+ IEEE80211_NOTE_MAC(vap, ops[op].mask, mac,
+ "station %s via MLME", ops[op].opstr);
+ } else {
+ IEEE80211_NOTE_MAC(vap, ops[op].mask, mac,
+ "station %s via MLME (reason %d)", ops[op].opstr, reason);
+ }
+#endif /* IEEE80211_DEBUG */
+}
+
static void
domlme(void *arg, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ieee80211req_mlme *mlme = arg;
+ struct mlmeop *mop = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (vap != mop->vap)
+ return;
+ /*
+ * NB: if ni_associd is zero then the node is already cleaned
+ * up and we don't need to do this (we're safely holding a
+ * reference but should otherwise not modify it's state).
+ */
+ if (ni->ni_associd == 0)
+ return;
+ mlmedebug(vap, ni->ni_macaddr, mop->op, mop->reason);
+ if (mop->op == IEEE80211_MLME_DEAUTH) {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ mop->reason);
+ } else {
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
+ mop->reason);
+ }
+ ieee80211_node_leave(ni);
+}
+
+static int
+setmlme_dropsta(struct ieee80211vap *vap,
+ const uint8_t mac[IEEE80211_ADDR_LEN], struct mlmeop *mlmeop)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node *ni;
+ int error = 0;
+
+ /* NB: the broadcast address means do 'em all */
+ if (!IEEE80211_ADDR_EQ(mac, ic->ic_ifp->if_broadcastaddr)) {
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_node_locked(nt, mac);
+ if (ni != NULL) {
+ domlme(mlmeop, ni);
+ ieee80211_free_node(ni);
+ } else
+ error = ENOENT;
+ IEEE80211_NODE_UNLOCK(nt);
+ } else {
+ ieee80211_iterate_nodes(nt, domlme, mlmeop);
+ }
+ return error;
+}
+
+static __noinline int
+setmlme_common(struct ieee80211vap *vap, int op,
+ const uint8_t mac[IEEE80211_ADDR_LEN], int reason)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node *ni;
+ struct mlmeop mlmeop;
+ int error;
- if (ni->ni_associd != 0) {
- IEEE80211_SEND_MGMT(ic, ni,
- mlme->im_op == IEEE80211_MLME_DEAUTH ?
- IEEE80211_FC0_SUBTYPE_DEAUTH :
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- mlme->im_reason);
+ error = 0;
+ switch (op) {
+ case IEEE80211_MLME_DISASSOC:
+ case IEEE80211_MLME_DEAUTH:
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_STA:
+ mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason);
+ /* XXX not quite right */
+ ieee80211_new_state(vap, IEEE80211_S_INIT, reason);
+ break;
+ case IEEE80211_M_HOSTAP:
+ mlmeop.vap = vap;
+ mlmeop.op = op;
+ mlmeop.reason = reason;
+ error = setmlme_dropsta(vap, mac, &mlmeop);
+ break;
+ case IEEE80211_M_WDS:
+ /* XXX user app should send raw frame? */
+ if (op != IEEE80211_MLME_DEAUTH) {
+ error = EINVAL;
+ break;
+ }
+#if 0
+ /* XXX accept any address, simplifies user code */
+ if (!IEEE80211_ADDR_EQ(mac, vap->iv_bss->ni_macaddr)) {
+ error = EINVAL;
+ break;
+ }
+#endif
+ mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason);
+ ni = ieee80211_ref_node(vap->iv_bss);
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
+ ieee80211_free_node(ni);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+ case IEEE80211_MLME_AUTHORIZE:
+ case IEEE80211_MLME_UNAUTHORIZE:
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_WDS) {
+ error = EINVAL;
+ break;
+ }
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_vap_node_locked(nt, vap, mac);
+ if (ni != NULL) {
+ mlmedebug(vap, mac, op, reason);
+ if (op == IEEE80211_MLME_AUTHORIZE)
+ ieee80211_node_authorize(ni);
+ else
+ ieee80211_node_unauthorize(ni);
+ ieee80211_free_node(ni);
+ } else
+ error = ENOENT;
+ IEEE80211_NODE_UNLOCK(nt);
+ break;
+ case IEEE80211_MLME_AUTH:
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP) {
+ error = EINVAL;
+ break;
+ }
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_vap_node_locked(nt, vap, mac);
+ if (ni != NULL) {
+ mlmedebug(vap, mac, op, reason);
+ if (reason == IEEE80211_STATUS_SUCCESS) {
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ /*
+ * For shared key auth, just continue the
+ * exchange. Otherwise when 802.1x is not in
+ * use mark the port authorized at this point
+ * so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X &&
+ ni->ni_challenge == NULL)
+ ieee80211_node_authorize(ni);
+ } else {
+ vap->iv_stats.is_rx_acl++;
+ ieee80211_send_error(ni, ni->ni_macaddr,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2|(reason<<16));
+ ieee80211_node_leave(ni);
+ }
+ ieee80211_free_node(ni);
+ } else
+ error = ENOENT;
+ IEEE80211_NODE_UNLOCK(nt);
+ break;
+ default:
+ error = EINVAL;
+ break;
}
- ieee80211_node_leave(ic, ni);
+ return error;
}
struct scanlookup {
@@ -1318,11 +1421,34 @@ mlmelookup(void *arg, const struct ieee80211_scan_entry *se)
look->se = se;
}
-static int
-ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+setmlme_assoc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
+ int ssid_len, const uint8_t ssid[IEEE80211_NWID_LEN])
+{
+ struct scanlookup lookup;
+
+ /* XXX ibss/ahdemo */
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ return EINVAL;
+
+ /* NB: this is racey if roaming is !manual */
+ lookup.se = NULL;
+ lookup.mac = mac;
+ lookup.esslen = ssid_len;
+ lookup.essid = ssid;
+ ieee80211_scan_iterate(vap, mlmelookup, &lookup);
+ if (lookup.se == NULL)
+ return ENOENT;
+ mlmedebug(vap, mac, IEEE80211_MLME_ASSOC, 0);
+ if (!ieee80211_sta_join(vap, lookup.se))
+ return EIO; /* XXX unique but could be better */
+ return 0;
+}
+
+static __noinline int
+ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211req_mlme mlme;
- struct ieee80211_node *ni;
int error;
if (ireq->i_len != sizeof(mlme))
@@ -1330,72 +1456,19 @@ ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, &mlme, sizeof(mlme));
if (error)
return error;
- switch (mlme.im_op) {
- case IEEE80211_MLME_ASSOC:
- /* XXX ibss/ahdemo */
- if (ic->ic_opmode == IEEE80211_M_STA) {
- struct scanlookup lookup;
-
- lookup.se = NULL;
- lookup.mac = mlme.im_macaddr;
- /* XXX use revised api w/ explicit ssid */
- lookup.esslen = ic->ic_des_ssid[0].len;
- lookup.essid = ic->ic_des_ssid[0].ssid;
- ieee80211_scan_iterate(ic, mlmelookup, &lookup);
- if (lookup.se != NULL &&
- ieee80211_sta_join(ic, lookup.se))
- return 0;
- }
- return EINVAL;
- case IEEE80211_MLME_DISASSOC:
- case IEEE80211_MLME_DEAUTH:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- /* XXX not quite right */
- ieee80211_new_state(ic, IEEE80211_S_INIT,
- mlme.im_reason);
- break;
- case IEEE80211_M_HOSTAP:
- /* NB: the broadcast address means do 'em all */
- if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) {
- if ((ni = ieee80211_find_node(&ic->ic_sta,
- mlme.im_macaddr)) == NULL)
- return EINVAL;
- domlme(&mlme, ni);
- ieee80211_free_node(ni);
- } else {
- ieee80211_iterate_nodes(&ic->ic_sta,
- domlme, &mlme);
- }
- break;
- default:
- return EINVAL;
- }
- break;
- case IEEE80211_MLME_AUTHORIZE:
- case IEEE80211_MLME_UNAUTHORIZE:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP)
- return EINVAL;
- ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr);
- if (ni == NULL)
- return EINVAL;
- if (mlme.im_op == IEEE80211_MLME_AUTHORIZE)
- ieee80211_node_authorize(ni);
- else
- ieee80211_node_unauthorize(ni);
- ieee80211_free_node(ni);
- break;
- default:
- return EINVAL;
- }
- return 0;
+ if (mlme.im_op == IEEE80211_MLME_ASSOC)
+ return setmlme_assoc(vap, mlme.im_macaddr,
+ vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
+ else
+ return setmlme_common(vap, mlme.im_op,
+ mlme.im_macaddr, mlme.im_reason);
}
-static int
-ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_macmac(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
uint8_t mac[IEEE80211_ADDR_LEN];
- const struct ieee80211_aclator *acl = ic->ic_acl;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
int error;
if (ireq->i_len != sizeof(mac))
@@ -1405,57 +1478,59 @@ ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
return error;
if (acl == NULL) {
acl = ieee80211_aclator_get("mac");
- if (acl == NULL || !acl->iac_attach(ic))
+ if (acl == NULL || !acl->iac_attach(vap))
return EINVAL;
- ic->ic_acl = acl;
+ vap->iv_acl = acl;
}
if (ireq->i_type == IEEE80211_IOC_ADDMAC)
- acl->iac_add(ic, mac);
+ acl->iac_add(vap, mac);
else
- acl->iac_remove(ic, mac);
+ acl->iac_remove(vap, mac);
return 0;
}
-static int
-ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
- const struct ieee80211_aclator *acl = ic->ic_acl;
+ const struct ieee80211_aclator *acl = vap->iv_acl;
switch (ireq->i_val) {
case IEEE80211_MACCMD_POLICY_OPEN:
case IEEE80211_MACCMD_POLICY_ALLOW:
case IEEE80211_MACCMD_POLICY_DENY:
+ case IEEE80211_MACCMD_POLICY_RADIUS:
if (acl == NULL) {
acl = ieee80211_aclator_get("mac");
- if (acl == NULL || !acl->iac_attach(ic))
+ if (acl == NULL || !acl->iac_attach(vap))
return EINVAL;
- ic->ic_acl = acl;
+ vap->iv_acl = acl;
}
- acl->iac_setpolicy(ic, ireq->i_val);
+ acl->iac_setpolicy(vap, ireq->i_val);
break;
case IEEE80211_MACCMD_FLUSH:
if (acl != NULL)
- acl->iac_flush(ic);
+ acl->iac_flush(vap);
/* NB: silently ignore when not in use */
break;
case IEEE80211_MACCMD_DETACH:
if (acl != NULL) {
- ic->ic_acl = NULL;
- acl->iac_detach(ic);
+ vap->iv_acl = NULL;
+ acl->iac_detach(vap);
}
break;
default:
if (acl == NULL)
return EINVAL;
else
- return acl->iac_setioctl(ic, ireq);
+ return acl->iac_setioctl(vap, ireq);
}
return 0;
}
-static int
-ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211req_chanlist list;
u_char chanlist[IEEE80211_CHAN_BYTES];
int i, j, nchan, error;
@@ -1491,11 +1566,12 @@ ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
isclr(chanlist, ic->ic_bsschan->ic_ieee))
ic->ic_bsschan = IEEE80211_CHAN_ANYC;
memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
- return IS_UP_AUTO(ic) ? ieee80211_init(ic, RESCAN) : 0;
+ ieee80211_scan_flush(vap);
+ return ENETRESET;
}
-static int
-ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
uint8_t macaddr[IEEE80211_ADDR_LEN];
@@ -1511,16 +1587,17 @@ ieee80211_ioctl_setstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
+ /* XXX require ni_vap == vap? */
memset(&ni->ni_stats, 0, sizeof(ni->ni_stats));
ieee80211_free_node(ni);
return 0;
}
-static int
-ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
struct ieee80211_node *ni;
struct ieee80211req_sta_txpow txpow;
@@ -1531,23 +1608,24 @@ ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
error = copyin(ireq->i_data, &txpow, sizeof(txpow));
if (error != 0)
return error;
- ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr);
if (ni == NULL)
- return EINVAL; /* XXX */
+ return ENOENT;
ni->ni_txpower = txpow.it_txpow;
ieee80211_free_node(ni);
return error;
}
-static int
-ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+static __noinline int
+ieee80211_ioctl_setwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
struct wmeParams *wmep, *chanp;
int isbss, ac;
if ((ic->ic_caps & IEEE80211_C_WME) == 0)
- return EINVAL;
+ return EOPNOTSUPP;
isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
@@ -1610,20 +1688,7 @@ ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
(ireq->i_val) == 0;
break;
}
- ieee80211_wme_updateparams(ic);
- return 0;
-}
-
-static int
-cipher2cap(int cipher)
-{
- switch (cipher) {
- case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP;
- case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES;
- case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM;
- case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP;
- case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP;
- }
+ ieee80211_wme_updateparams(vap);
return 0;
}
@@ -1666,10 +1731,7 @@ findchannel(struct ieee80211com *ic, int ieee, int mode)
u_int modeflags;
int i;
- KASSERT(mode < IEEE80211_MODE_MAX, ("bad mode %u", mode));
modeflags = chanflags[mode];
- KASSERT(modeflags != 0 || mode == IEEE80211_MODE_AUTO,
- ("no chanflags for mode %u", mode));
for (i = 0; i < ic->ic_nchans; i++) {
struct ieee80211_channel *c = &ic->ic_channels[i];
@@ -1735,44 +1797,56 @@ check_mode_consistency(const struct ieee80211_channel *c, int mode)
* change or a kick of the state machine.
*/
static int
-setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
+setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c)
{
+ struct ieee80211com *ic = vap->iv_ic;
int error;
if (c != IEEE80211_CHAN_ANYC) {
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
- !check_mode_consistency(c, ic->ic_des_mode))
- return EINVAL;
- if (ic->ic_state == IEEE80211_S_RUN && c == ic->ic_curchan)
+ if (IEEE80211_IS_CHAN_RADAR(c))
+ return EBUSY; /* XXX better code? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ if (IEEE80211_IS_CHAN_NOHOSTAP(c))
+ return EINVAL;
+ if (!check_mode_consistency(c, vap->iv_des_mode))
+ return EINVAL;
+ } else if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ if (IEEE80211_IS_CHAN_NOADHOC(c))
+ return EINVAL;
+ }
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ vap->iv_bss->ni_chan == c)
return 0; /* NB: nothing to do */
}
- ic->ic_des_chan = c;
+ vap->iv_des_chan = c;
error = 0;
- if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_WDS) &&
- ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+ if (vap->iv_opmode == IEEE80211_M_MONITOR &&
+ vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
/*
- * Monitor and wds modes can switch directly.
+ * Monitor mode can switch directly.
*/
- ic->ic_curchan = ic->ic_des_chan;
- if (ic->ic_state == IEEE80211_S_RUN)
- ic->ic_set_channel(ic);
+ if (IFNET_IS_UP_RUNNING(vap->iv_ifp)) {
+ /* XXX need state machine for other vap's to follow */
+ ieee80211_setcurchan(ic, vap->iv_des_chan);
+ vap->iv_bss->ni_chan = ic->ic_curchan;
+ } else
+ ic->ic_curchan = vap->iv_des_chan;
} else {
/*
* Need to go through the state machine in case we
* need to reassociate or the like. The state machine
* will pickup the desired channel and avoid scanning.
*/
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
- else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+ if (IS_UP_AUTO(vap))
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ else if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
/*
* When not up+running and a real channel has
* been specified fix the current channel so
* there is immediate feedback; e.g. via ifconfig.
*/
- ic->ic_curchan = ic->ic_des_chan;
+ ic->ic_curchan = vap->iv_des_chan;
}
}
return error;
@@ -1782,10 +1856,11 @@ setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
* Old api for setting the current channel; this is
* deprecated because channel numbers are ambiguous.
*/
-static int
-ieee80211_ioctl_setchannel(struct ieee80211com *ic,
+static __noinline int
+ieee80211_ioctl_setchannel(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c;
/* XXX 0xffff overflows 16-bit signed */
@@ -1797,7 +1872,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
} else {
struct ieee80211_channel *c2;
- c = findchannel(ic, ireq->i_val, ic->ic_des_mode);
+ c = findchannel(ic, ireq->i_val, vap->iv_des_mode);
if (c == NULL) {
c = findchannel(ic, ireq->i_val,
IEEE80211_MODE_AUTO);
@@ -1816,7 +1891,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
* 11g channel returned,
* otherwise we should be ok with what we've got.
*/
- switch (ic->ic_des_mode) {
+ switch (vap->iv_des_mode) {
case IEEE80211_MODE_11B:
if (IEEE80211_IS_CHAN_ANYG(c)) {
c2 = findchannel(ic, ireq->i_val,
@@ -1854,7 +1929,7 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
break;
}
}
- return setcurchan(ic, c);
+ return setcurchan(vap, c);
}
/*
@@ -1862,10 +1937,11 @@ ieee80211_ioctl_setchannel(struct ieee80211com *ic,
* channel description is provide so there is no ambiguity in
* identifying the channel.
*/
-static int
-ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
+static __noinline int
+ieee80211_ioctl_setcurchan(struct ieee80211vap *vap,
const struct ieee80211req *ireq)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel chan, *c;
int error;
@@ -1882,21 +1958,494 @@ ieee80211_ioctl_setcurchan(struct ieee80211com *ic,
if (c == NULL)
return EINVAL;
}
- return setcurchan(ic, c);
+ return setcurchan(vap, c);
+}
+
+static __noinline int
+ieee80211_ioctl_setregdomain(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211_regdomain_req *reg;
+ int error;
+
+ if (ireq->i_len != sizeof(struct ieee80211_regdomain_req))
+ return EINVAL;
+ MALLOC(reg, struct ieee80211_regdomain_req *,
+ sizeof(struct ieee80211_regdomain_req), M_TEMP, M_NOWAIT);
+ if (reg == NULL)
+ return ENOMEM;
+ error = copyin(ireq->i_data, reg, sizeof(*reg));
+ if (error == 0)
+ error = ieee80211_setregdomain(vap, reg);
+ FREE(reg, M_TEMP);
+
+ return (error == 0 ? ENETRESET : error);
+}
+
+static int
+ieee80211_ioctl_setroam(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ if (ireq->i_len != sizeof(vap->iv_roamparms))
+ return EINVAL;
+ /* XXX validate params */
+ /* XXX? ENETRESET to push to device? */
+ return copyin(ireq->i_data, vap->iv_roamparms,
+ sizeof(vap->iv_roamparms));
+}
+
+static int
+checkrate(const struct ieee80211_rateset *rs, int rate)
+{
+ int i;
+
+ if (rate == IEEE80211_FIXED_RATE_NONE)
+ return 1;
+ for (i = 0; i < rs->rs_nrates; i++)
+ if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rate)
+ return 1;
+ return 0;
+}
+
+static int
+checkmcs(int mcs)
+{
+ if (mcs == IEEE80211_FIXED_RATE_NONE)
+ return 1;
+ if ((mcs & IEEE80211_RATE_MCS) == 0) /* MCS always have 0x80 set */
+ return 0;
+ return (mcs & 0x7f) <= 15; /* XXX could search ht rate set */
+}
+
+static __noinline int
+ieee80211_ioctl_settxparams(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_txparams_req parms; /* XXX stack use? */
+ struct ieee80211_txparam *src, *dst;
+ const struct ieee80211_rateset *rs;
+ int error, i, changed;
+
+ if (ireq->i_len != sizeof(parms))
+ return EINVAL;
+ error = copyin(ireq->i_data, &parms, sizeof(parms));
+ if (error != 0)
+ return error;
+ changed = 0;
+ /* validate parameters and check if anything changed */
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_11NA; i++) {
+ if (isclr(ic->ic_modecaps, i))
+ continue;
+ src = &parms.params[i];
+ dst = &vap->iv_txparms[i];
+ rs = &ic->ic_sup_rates[i];
+ if (src->ucastrate != dst->ucastrate) {
+ if (!checkrate(rs, src->ucastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mcastrate != dst->mcastrate) {
+ if (!checkrate(rs, src->mcastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mgmtrate != dst->mgmtrate) {
+ if (!checkrate(rs, src->mgmtrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->maxretry != dst->maxretry) /* NB: no bounds */
+ changed++;
+ }
+ /* 11n parameters are handled differently */
+ for (; i < IEEE80211_MODE_MAX; i++) {
+ if (isclr(ic->ic_modecaps, i))
+ continue;
+ src = &parms.params[i];
+ dst = &vap->iv_txparms[i];
+ rs = &ic->ic_sup_rates[i == IEEE80211_MODE_11NA ?
+ IEEE80211_MODE_11A : IEEE80211_MODE_11G];
+ if (src->ucastrate != dst->ucastrate) {
+ if (!checkmcs(src->ucastrate) &&
+ !checkrate(rs, src->ucastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mcastrate != dst->mcastrate) {
+ if (!checkmcs(src->mcastrate) &&
+ !checkrate(rs, src->mcastrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->mgmtrate != dst->mgmtrate) {
+ if (!checkmcs(src->mgmtrate) &&
+ !checkrate(rs, src->mgmtrate))
+ return EINVAL;
+ changed++;
+ }
+ if (src->maxretry != dst->maxretry) /* NB: no bounds */
+ changed++;
+ }
+ if (changed) {
+ /*
+ * Copy new parameters in place and notify the
+ * driver so it can push state to the device.
+ */
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) {
+ if (isset(ic->ic_modecaps, i))
+ vap->iv_txparms[i] = parms.params[i];
+ }
+ /* XXX could be more intelligent,
+ e.g. don't reset if setting not being used */
+ return ENETRESET;
+ }
+ return 0;
+}
+
+/*
+ * Application Information Element support.
+ */
+static int
+setappie(struct ieee80211_appie **aie, const struct ieee80211req *ireq)
+{
+ struct ieee80211_appie *app = *aie;
+ struct ieee80211_appie *napp;
+ int error;
+
+ if (ireq->i_len == 0) { /* delete any existing ie */
+ if (app != NULL) {
+ *aie = NULL; /* XXX racey */
+ FREE(app, M_80211_NODE_IE);
+ }
+ return 0;
+ }
+ if (!(2 <= ireq->i_len && ireq->i_len <= IEEE80211_MAX_APPIE))
+ return EINVAL;
+ /*
+ * Allocate a new appie structure and copy in the user data.
+ * When done swap in the new structure. Note that we do not
+ * guard against users holding a ref to the old structure;
+ * this must be handled outside this code.
+ *
+ * XXX bad bad bad
+ */
+ MALLOC(napp, struct ieee80211_appie *,
+ sizeof(struct ieee80211_appie) + ireq->i_len, M_80211_NODE_IE, M_NOWAIT);
+ if (napp == NULL)
+ return ENOMEM;
+ /* XXX holding ic lock */
+ error = copyin(ireq->i_data, napp->ie_data, ireq->i_len);
+ if (error) {
+ FREE(napp, M_80211_NODE_IE);
+ return error;
+ }
+ napp->ie_len = ireq->i_len;
+ *aie = napp;
+ if (app != NULL)
+ FREE(app, M_80211_NODE_IE);
+ return 0;
+}
+
+static void
+setwparsnie(struct ieee80211vap *vap, uint8_t *ie, int space)
+{
+ /* validate data is present as best we can */
+ if (space == 0 || 2+ie[1] > space)
+ return;
+ if (ie[0] == IEEE80211_ELEMID_VENDOR)
+ vap->iv_wpa_ie = ie;
+ else if (ie[0] == IEEE80211_ELEMID_RSN)
+ vap->iv_rsn_ie = ie;
+}
+
+static __noinline int
+ieee80211_ioctl_setappie_locked(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq, int fc0)
+{
+ int error;
+
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+ switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_IBSS) {
+ error = EINVAL;
+ break;
+ }
+ error = setappie(&vap->iv_appie_beacon, ireq);
+ if (error == 0)
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_APPIE);
+ break;
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ error = setappie(&vap->iv_appie_proberesp, ireq);
+ break;
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ error = setappie(&vap->iv_appie_assocresp, ireq);
+ else
+ error = EINVAL;
+ break;
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ error = setappie(&vap->iv_appie_probereq, ireq);
+ break;
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ error = setappie(&vap->iv_appie_assocreq, ireq);
+ else
+ error = EINVAL;
+ break;
+ case (IEEE80211_APPIE_WPA & IEEE80211_FC0_SUBTYPE_MASK):
+ error = setappie(&vap->iv_appie_wpa, ireq);
+ if (error == 0) {
+ /*
+ * Must split single blob of data into separate
+ * WPA and RSN ie's because they go in different
+ * locations in the mgt frames.
+ * XXX use IEEE80211_IOC_WPA2 so user code does split
+ */
+ vap->iv_wpa_ie = NULL;
+ vap->iv_rsn_ie = NULL;
+ if (vap->iv_appie_wpa != NULL) {
+ struct ieee80211_appie *appie =
+ vap->iv_appie_wpa;
+ uint8_t *data = appie->ie_data;
+
+ /* XXX ie length validate is painful, cheat */
+ setwparsnie(vap, data, appie->ie_len);
+ setwparsnie(vap, data + 2 + data[1],
+ appie->ie_len - (2 + data[1]));
+ }
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ /*
+ * Must rebuild beacon frame as the update
+ * mechanism doesn't handle WPA/RSN ie's.
+ * Could extend it but it doesn't normally
+ * change; this is just to deal with hostapd
+ * plumbing the ie after the interface is up.
+ */
+ error = ENETRESET;
+ }
+ }
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_setappie(struct ieee80211vap *vap,
+ const struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ int error;
+ uint8_t fc0;
+
+ fc0 = ireq->i_val & 0xff;
+ if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
+ return EINVAL;
+ /* NB: could check iv_opmode and reject but hardly worth the effort */
+ IEEE80211_LOCK(ic);
+ error = ieee80211_ioctl_setappie_locked(vap, ireq, fc0);
+ IEEE80211_UNLOCK(ic);
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_chanswitch(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_chanswitch_req csr;
+ struct ieee80211_channel *c;
+ int error;
+
+ if (ireq->i_len != sizeof(csr))
+ return EINVAL;
+ error = copyin(ireq->i_data, &csr, sizeof(csr));
+ if (error != 0)
+ return error;
+ if ((vap->iv_flags & IEEE80211_F_DOTH) == 0)
+ return EINVAL;
+ c = ieee80211_find_channel(ic,
+ csr.csa_chan.ic_freq, csr.csa_chan.ic_flags);
+ if (c == NULL)
+ return ENOENT;
+ IEEE80211_LOCK(ic);
+ if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0)
+ ieee80211_csa_startswitch(ic, c, csr.csa_mode, csr.csa_count);
+ else
+ error = EBUSY;
+ IEEE80211_UNLOCK(ic);
+ return error;
+}
+
+static __noinline int
+ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+#define IEEE80211_IOC_SCAN_FLAGS \
+ (IEEE80211_IOC_SCAN_NOPICK | IEEE80211_IOC_SCAN_ACTIVE | \
+ IEEE80211_IOC_SCAN_PICK1ST | IEEE80211_IOC_SCAN_BGSCAN | \
+ IEEE80211_IOC_SCAN_ONCE | IEEE80211_IOC_SCAN_NOBCAST | \
+ IEEE80211_IOC_SCAN_NOJOIN | IEEE80211_IOC_SCAN_FLUSH | \
+ IEEE80211_IOC_SCAN_CHECK)
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_req sr; /* XXX off stack? */
+ int error, i;
+
+ /* NB: parent must be running */
+ if ((ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return ENXIO;
+
+ if (ireq->i_len != sizeof(sr))
+ return EINVAL;
+ error = copyin(ireq->i_data, &sr, sizeof(sr));
+ if (error != 0)
+ return error;
+ /* convert duration */
+ if (sr.sr_duration == IEEE80211_IOC_SCAN_FOREVER)
+ sr.sr_duration = IEEE80211_SCAN_FOREVER;
+ else {
+ if (sr.sr_duration < IEEE80211_IOC_SCAN_DURATION_MIN ||
+ sr.sr_duration > IEEE80211_IOC_SCAN_DURATION_MAX)
+ return EINVAL;
+ sr.sr_duration = msecs_to_ticks(sr.sr_duration);
+ if (sr.sr_duration < 1)
+ sr.sr_duration = 1;
+ }
+ /* convert min/max channel dwell */
+ if (sr.sr_mindwell != 0) {
+ sr.sr_mindwell = msecs_to_ticks(sr.sr_mindwell);
+ if (sr.sr_mindwell < 1)
+ sr.sr_mindwell = 1;
+ }
+ if (sr.sr_maxdwell != 0) {
+ sr.sr_maxdwell = msecs_to_ticks(sr.sr_maxdwell);
+ if (sr.sr_maxdwell < 1)
+ sr.sr_maxdwell = 1;
+ }
+ /* NB: silently reduce ssid count to what is supported */
+ if (sr.sr_nssid > IEEE80211_SCAN_MAX_SSID)
+ sr.sr_nssid = IEEE80211_SCAN_MAX_SSID;
+ for (i = 0; i < sr.sr_nssid; i++)
+ if (sr.sr_ssid[i].len > IEEE80211_NWID_LEN)
+ return EINVAL;
+ /* cleanse flags just in case, could reject if invalid flags */
+ sr.sr_flags &= IEEE80211_IOC_SCAN_FLAGS;
+ /*
+ * Add an implicit NOPICK if the vap is not marked UP. This
+ * allows applications to scan without joining a bss (or picking
+ * a channel and setting up a bss) and without forcing manual
+ * roaming mode--you just need to mark the parent device UP.
+ */
+ if ((vap->iv_ifp->if_flags & IFF_UP) == 0)
+ sr.sr_flags |= IEEE80211_IOC_SCAN_NOPICK;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: flags 0x%x%s duration 0x%x mindwell %u maxdwell %u nssid %d\n",
+ __func__, sr.sr_flags,
+ (vap->iv_ifp->if_flags & IFF_UP) == 0 ? " (!IFF_UP)" : "",
+ sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell, sr.sr_nssid);
+ /*
+ * If we are in INIT state then the driver has never had a chance
+ * to setup hardware state to do a scan; we must use the state
+ * machine to get us up to the SCAN state but once we reach SCAN
+ * state we then want to use the supplied params. Stash the
+ * parameters in the vap and mark IEEE80211_FEXT_SCANREQ; the
+ * state machines will recognize this and use the stashed params
+ * to issue the scan request.
+ *
+ * Otherwise just invoke the scan machinery directly.
+ */
+ IEEE80211_LOCK(ic);
+ if (vap->iv_state == IEEE80211_S_INIT) {
+ /* NB: clobbers previous settings */
+ vap->iv_scanreq_flags = sr.sr_flags;
+ vap->iv_scanreq_duration = sr.sr_duration;
+ vap->iv_scanreq_nssid = sr.sr_nssid;
+ for (i = 0; i < sr.sr_nssid; i++) {
+ vap->iv_scanreq_ssid[i].len = sr.sr_ssid[i].len;
+ memcpy(vap->iv_scanreq_ssid[i].ssid, sr.sr_ssid[i].ssid,
+ sr.sr_ssid[i].len);
+ }
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANREQ;
+ IEEE80211_UNLOCK(ic);
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ } else {
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ IEEE80211_UNLOCK(ic);
+ /* XXX neeed error return codes */
+ if (sr.sr_flags & IEEE80211_IOC_SCAN_CHECK) {
+ (void) ieee80211_check_scan(vap, sr.sr_flags,
+ sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell,
+ sr.sr_nssid,
+ /* NB: cheat, we assume structures are compatible */
+ (const struct ieee80211_scan_ssid *) &sr.sr_ssid[0]);
+ } else {
+ (void) ieee80211_start_scan(vap, sr.sr_flags,
+ sr.sr_duration, sr.sr_mindwell, sr.sr_maxdwell,
+ sr.sr_nssid,
+ /* NB: cheat, we assume structures are compatible */
+ (const struct ieee80211_scan_ssid *) &sr.sr_ssid[0]);
+ }
+ }
+ return error;
+#undef IEEE80211_IOC_SCAN_FLAGS
+}
+
+static __noinline int
+ieee80211_ioctl_setstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_sta_vlan vlan;
+ int error;
+
+ if (ireq->i_len != sizeof(vlan))
+ return EINVAL;
+ error = copyin(ireq->i_data, &vlan, sizeof(vlan));
+ if (error != 0)
+ return error;
+ if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) {
+ ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
+ vlan.sv_macaddr);
+ if (ni == NULL)
+ return ENOENT;
+ } else
+ ni = ieee80211_ref_node(vap->iv_bss);
+ ni->ni_vlan = vlan.sv_vlan;
+ ieee80211_free_node(ni);
+ return error;
+}
+
+static int
+isvap11g(const struct ieee80211vap *vap)
+{
+ const struct ieee80211_node *bss = vap->iv_bss;
+ return bss->ni_chan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_ANYG(bss->ni_chan);
}
static int
-ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+isvapht(const struct ieee80211vap *vap)
{
- static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
- struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+ const struct ieee80211_node *bss = vap->iv_bss;
+ return bss->ni_chan != IEEE80211_CHAN_ANYC &&
+ IEEE80211_IS_CHAN_HT(bss->ni_chan);
+}
+
+static __noinline int
+ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211req *ireq)
+{
+ struct ieee80211com *ic = vap->iv_ic;
int error;
const struct ieee80211_authenticator *auth;
uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
char tmpssid[IEEE80211_NWID_LEN];
uint8_t tmpbssid[IEEE80211_ADDR_LEN];
struct ieee80211_key *k;
- int j, caps;
u_int kid;
error = 0;
@@ -1908,39 +2457,37 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
error = copyin(ireq->i_data, tmpssid, ireq->i_len);
if (error)
break;
- memset(ic->ic_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
- ic->ic_des_ssid[0].len = ireq->i_len;
- memcpy(ic->ic_des_ssid[0].ssid, tmpssid, ireq->i_len);
- ic->ic_des_nssid = (ireq->i_len > 0);
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ memset(vap->iv_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
+ vap->iv_des_ssid[0].len = ireq->i_len;
+ memcpy(vap->iv_des_ssid[0].ssid, tmpssid, ireq->i_len);
+ vap->iv_des_nssid = (ireq->i_len > 0);
+ error = ENETRESET;
break;
case IEEE80211_IOC_WEP:
switch (ireq->i_val) {
case IEEE80211_WEP_OFF:
- ic->ic_flags &= ~IEEE80211_F_PRIVACY;
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ vap->iv_flags &= ~IEEE80211_F_PRIVACY;
+ vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
break;
case IEEE80211_WEP_ON:
- ic->ic_flags |= IEEE80211_F_PRIVACY;
- ic->ic_flags |= IEEE80211_F_DROPUNENC;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_DROPUNENC;
break;
case IEEE80211_WEP_MIXED:
- ic->ic_flags |= IEEE80211_F_PRIVACY;
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
break;
}
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ error = ENETRESET;
break;
case IEEE80211_IOC_WEPKEY:
kid = (u_int) ireq->i_val;
if (kid >= IEEE80211_WEP_NKID)
return EINVAL;
- k = &ic->ic_nw_keys[kid];
+ k = &vap->iv_nw_keys[kid];
if (ireq->i_len == 0) {
/* zero-len =>'s delete any existing key */
- (void) ieee80211_crypto_delkey(ic, k);
+ (void) ieee80211_crypto_delkey(vap, k);
break;
}
if (ireq->i_len > sizeof(tmpkey))
@@ -1949,24 +2496,24 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
error = copyin(ireq->i_data, tmpkey, ireq->i_len);
if (error)
break;
- ieee80211_key_update_begin(ic);
+ ieee80211_key_update_begin(vap);
k->wk_keyix = kid; /* NB: force fixed key id */
- if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
+ if (ieee80211_crypto_newkey(vap, IEEE80211_CIPHER_WEP,
IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) {
k->wk_keylen = ireq->i_len;
memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
- if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
+ if (!ieee80211_crypto_setkey(vap, k, vap->iv_myaddr))
error = EINVAL;
} else
error = EINVAL;
- ieee80211_key_update_end(ic);
+ ieee80211_key_update_end(vap);
break;
case IEEE80211_IOC_WEPTXKEY:
kid = (u_int) ireq->i_val;
if (kid >= IEEE80211_WEP_NKID &&
(uint16_t) kid != IEEE80211_KEYIX_NONE)
return EINVAL;
- ic->ic_def_txkey = kid;
+ vap->iv_def_txkey = kid;
break;
case IEEE80211_IOC_AUTHMODE:
switch (ireq->i_val) {
@@ -1984,74 +2531,66 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
}
switch (ireq->i_val) {
case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */
- ic->ic_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
ireq->i_val = IEEE80211_AUTH_8021X;
break;
case IEEE80211_AUTH_OPEN: /* open */
- ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
+ vap->iv_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
break;
case IEEE80211_AUTH_SHARED: /* shared-key */
case IEEE80211_AUTH_8021X: /* 802.1x */
- ic->ic_flags &= ~IEEE80211_F_WPA;
+ vap->iv_flags &= ~IEEE80211_F_WPA;
/* both require a key so mark the PRIVACY capability */
- ic->ic_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
break;
case IEEE80211_AUTH_AUTO: /* auto */
- ic->ic_flags &= ~IEEE80211_F_WPA;
+ vap->iv_flags &= ~IEEE80211_F_WPA;
/* XXX PRIVACY handling? */
/* XXX what's the right way to do this? */
break;
}
/* NB: authenticator attach/detach happens on state change */
- ic->ic_bss->ni_authmode = ireq->i_val;
+ vap->iv_bss->ni_authmode = ireq->i_val;
/* XXX mixed/mode/usage? */
- ic->ic_auth = auth;
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ vap->iv_auth = auth;
+ error = ENETRESET;
break;
case IEEE80211_IOC_CHANNEL:
- error = ieee80211_ioctl_setchannel(ic, ireq);
+ error = ieee80211_ioctl_setchannel(vap, ireq);
break;
case IEEE80211_IOC_POWERSAVE:
switch (ireq->i_val) {
case IEEE80211_POWERSAVE_OFF:
- if (ic->ic_flags & IEEE80211_F_PMGTON) {
- ic->ic_flags &= ~IEEE80211_F_PMGTON;
- error = ENETRESET;
+ if (vap->iv_flags & IEEE80211_F_PMGTON) {
+ ieee80211_syncflag(vap, -IEEE80211_F_PMGTON);
+ error = ERESTART;
}
break;
case IEEE80211_POWERSAVE_ON:
- if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
- error = EINVAL;
- else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
- ic->ic_flags |= IEEE80211_F_PMGTON;
- error = ENETRESET;
+ if ((vap->iv_caps & IEEE80211_C_PMGT) == 0)
+ error = EOPNOTSUPP;
+ else if ((vap->iv_flags & IEEE80211_F_PMGTON) == 0) {
+ ieee80211_syncflag(vap, IEEE80211_F_PMGTON);
+ error = ERESTART;
}
break;
default:
error = EINVAL;
break;
}
- if (error == ENETRESET) {
- /*
- * Switching in+out of power save mode
- * should not require a state change.
- */
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
- }
break;
case IEEE80211_IOC_POWERSAVESLEEP:
if (ireq->i_val < 0)
return EINVAL;
ic->ic_lintval = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_RTSTHRESHOLD:
if (!(IEEE80211_RTS_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_RTS_MAX))
return EINVAL;
- ic->ic_rtsthreshold = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_rtsthreshold = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_PROTMODE:
if (ireq->i_val > IEEE80211_PROT_RTSCTS)
@@ -2060,158 +2599,96 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
/* NB: if not operating in 11g this can wait */
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_TXPOWER:
if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
- return EINVAL;
+ return EOPNOTSUPP;
if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_TXPOWER_MAX))
return EINVAL;
ic->ic_txpowlimit = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_ROAMING:
if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
ireq->i_val <= IEEE80211_ROAMING_MANUAL))
return EINVAL;
- ic->ic_roaming = ireq->i_val;
+ vap->iv_roaming = ireq->i_val;
/* XXXX reset? */
break;
case IEEE80211_IOC_PRIVACY:
if (ireq->i_val) {
/* XXX check for key state? */
- ic->ic_flags |= IEEE80211_F_PRIVACY;
+ vap->iv_flags |= IEEE80211_F_PRIVACY;
} else
- ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+ vap->iv_flags &= ~IEEE80211_F_PRIVACY;
+ /* XXX ERESTART? */
break;
case IEEE80211_IOC_DROPUNENCRYPTED:
if (ireq->i_val)
- ic->ic_flags |= IEEE80211_F_DROPUNENC;
+ vap->iv_flags |= IEEE80211_F_DROPUNENC;
else
- ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
+ /* XXX ERESTART? */
break;
case IEEE80211_IOC_WPAKEY:
- error = ieee80211_ioctl_setkey(ic, ireq);
+ error = ieee80211_ioctl_setkey(vap, ireq);
break;
case IEEE80211_IOC_DELKEY:
- error = ieee80211_ioctl_delkey(ic, ireq);
+ error = ieee80211_ioctl_delkey(vap, ireq);
break;
case IEEE80211_IOC_MLME:
- error = ieee80211_ioctl_setmlme(ic, ireq);
- break;
- case IEEE80211_IOC_OPTIE:
- error = ieee80211_ioctl_setoptie(ic, ireq);
+ error = ieee80211_ioctl_setmlme(vap, ireq);
break;
case IEEE80211_IOC_COUNTERMEASURES:
if (ireq->i_val) {
- if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_COUNTERM;
+ if ((vap->iv_flags & IEEE80211_F_WPA) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_COUNTERM;
} else
- ic->ic_flags &= ~IEEE80211_F_COUNTERM;
+ vap->iv_flags &= ~IEEE80211_F_COUNTERM;
+ /* XXX ERESTART? */
break;
case IEEE80211_IOC_WPA:
if (ireq->i_val > 3)
return EINVAL;
/* XXX verify ciphers available */
- ic->ic_flags &= ~IEEE80211_F_WPA;
+ vap->iv_flags &= ~IEEE80211_F_WPA;
switch (ireq->i_val) {
case 1:
- ic->ic_flags |= IEEE80211_F_WPA1;
+ vap->iv_flags |= IEEE80211_F_WPA1;
break;
case 2:
- ic->ic_flags |= IEEE80211_F_WPA2;
+ vap->iv_flags |= IEEE80211_F_WPA2;
break;
case 3:
- ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
+ vap->iv_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
break;
}
- error = ENETRESET;
+ error = ERESTART; /* NB: can change beacon frame */
break;
case IEEE80211_IOC_WME:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_WME) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_WME;
+ if ((vap->iv_caps & IEEE80211_C_WME) == 0)
+ return EOPNOTSUPP;
+ ieee80211_syncflag(vap, IEEE80211_F_WME);
} else
- ic->ic_flags &= ~IEEE80211_F_WME;
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, 0);
+ ieee80211_syncflag(vap, -IEEE80211_F_WME);
+ error = ERESTART; /* NB: can change beacon frame */
break;
case IEEE80211_IOC_HIDESSID:
if (ireq->i_val)
- ic->ic_flags |= IEEE80211_F_HIDESSID;
+ vap->iv_flags |= IEEE80211_F_HIDESSID;
else
- ic->ic_flags &= ~IEEE80211_F_HIDESSID;
- error = ENETRESET;
+ vap->iv_flags &= ~IEEE80211_F_HIDESSID;
+ error = ERESTART; /* XXX ENETRESET? */
break;
case IEEE80211_IOC_APBRIDGE:
if (ireq->i_val == 0)
- ic->ic_flags |= IEEE80211_F_NOBRIDGE;
+ vap->iv_flags |= IEEE80211_F_NOBRIDGE;
else
- ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
- break;
- case IEEE80211_IOC_MCASTCIPHER:
- if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 &&
- !ieee80211_crypto_available(ireq->i_val))
- return EINVAL;
- rsn->rsn_mcastcipher = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_MCASTKEYLEN:
- if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
- return EINVAL;
- /* XXX no way to verify driver capability */
- rsn->rsn_mcastkeylen = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_UCASTCIPHERS:
- /*
- * Convert user-specified cipher set to the set
- * we can support (via hardware or software).
- * NB: this logic intentionally ignores unknown and
- * unsupported ciphers so folks can specify 0xff or
- * similar and get all available ciphers.
- */
- caps = 0;
- for (j = 1; j < 32; j++) /* NB: skip WEP */
- if ((ireq->i_val & (1<<j)) &&
- ((ic->ic_caps & cipher2cap(j)) ||
- ieee80211_crypto_available(j)))
- caps |= 1<<j;
- if (caps == 0) /* nothing available */
- return EINVAL;
- /* XXX verify ciphers ok for unicast use? */
- /* XXX disallow if running as it'll have no effect */
- rsn->rsn_ucastcipherset = caps;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_UCASTCIPHER:
- if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0)
- return EINVAL;
- rsn->rsn_ucastcipher = ireq->i_val;
- break;
- case IEEE80211_IOC_UCASTKEYLEN:
- if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
- return EINVAL;
- /* XXX no way to verify driver capability */
- rsn->rsn_ucastkeylen = ireq->i_val;
- break;
- case IEEE80211_IOC_DRIVER_CAPS:
- /* NB: for testing */
- ic->ic_caps = (((uint16_t) ireq->i_val) << 16) |
- ((uint16_t) ireq->i_len);
- break;
- case IEEE80211_IOC_KEYMGTALGS:
- /* XXX check */
- rsn->rsn_keymgmtset = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
- break;
- case IEEE80211_IOC_RSNCAPS:
- /* XXX check */
- rsn->rsn_caps = ireq->i_val;
- error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+ vap->iv_flags &= ~IEEE80211_F_NOBRIDGE;
break;
case IEEE80211_IOC_BSSID:
if (ireq->i_len != sizeof(tmpbssid))
@@ -2219,39 +2696,71 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
if (error)
break;
- IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
- if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
- ic->ic_flags &= ~IEEE80211_F_DESBSSID;
+ IEEE80211_ADDR_COPY(vap->iv_des_bssid, tmpbssid);
+ if (IEEE80211_ADDR_EQ(vap->iv_des_bssid, zerobssid))
+ vap->iv_flags &= ~IEEE80211_F_DESBSSID;
else
- ic->ic_flags |= IEEE80211_F_DESBSSID;
- if (IS_UP_AUTO(ic))
- error = ieee80211_init(ic, RESCAN);
+ vap->iv_flags |= IEEE80211_F_DESBSSID;
+ error = ENETRESET;
break;
case IEEE80211_IOC_CHANLIST:
- error = ieee80211_ioctl_setchanlist(ic, ireq);
+ error = ieee80211_ioctl_setchanlist(vap, ireq);
break;
+#define OLD_IEEE80211_IOC_SCAN_REQ 23
+#ifdef OLD_IEEE80211_IOC_SCAN_REQ
+ case OLD_IEEE80211_IOC_SCAN_REQ:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: active scan request\n", __func__);
+ /*
+ * If we are in INIT state then the driver has never
+ * had a chance to setup hardware state to do a scan;
+ * use the state machine to get us up the SCAN state.
+ * Otherwise just invoke the scan machinery to start
+ * a one-time scan.
+ */
+ if (vap->iv_state == IEEE80211_S_INIT)
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ else
+ (void) ieee80211_start_scan(vap,
+ IEEE80211_SCAN_ACTIVE |
+ IEEE80211_SCAN_NOPICK |
+ IEEE80211_SCAN_ONCE,
+ IEEE80211_SCAN_FOREVER, 0, 0,
+ /* XXX use ioctl params */
+ vap->iv_des_nssid, vap->iv_des_ssid);
+ break;
+#endif /* OLD_IEEE80211_IOC_SCAN_REQ */
case IEEE80211_IOC_SCAN_REQ:
- if (!IS_UP(ic))
- return EINVAL;
- (void) ieee80211_start_scan(ic,
- IEEE80211_SCAN_ACTIVE |
- IEEE80211_SCAN_NOPICK |
- IEEE80211_SCAN_ONCE, IEEE80211_SCAN_FOREVER,
- /* XXX use ioctl params */
- ic->ic_des_nssid, ic->ic_des_ssid);
+ error = ieee80211_ioctl_scanreq(vap, ireq);
+ break;
+ case IEEE80211_IOC_SCAN_CANCEL:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: cancel scan\n", __func__);
+ ieee80211_cancel_scan(vap);
+ break;
+ case IEEE80211_IOC_HTCONF:
+ if (ireq->i_val & 1)
+ ieee80211_syncflag_ext(vap, IEEE80211_FEXT_HT);
+ else
+ ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_HT);
+ if (ireq->i_val & 2)
+ ieee80211_syncflag_ext(vap, IEEE80211_FEXT_USEHT40);
+ else
+ ieee80211_syncflag_ext(vap, -IEEE80211_FEXT_USEHT40);
+ error = ENETRESET;
break;
case IEEE80211_IOC_ADDMAC:
case IEEE80211_IOC_DELMAC:
- error = ieee80211_ioctl_macmac(ic, ireq);
+ error = ieee80211_ioctl_macmac(vap, ireq);
break;
case IEEE80211_IOC_MACCMD:
- error = ieee80211_ioctl_setmaccmd(ic, ireq);
+ error = ieee80211_ioctl_setmaccmd(vap, ireq);
break;
case IEEE80211_IOC_STA_STATS:
- error = ieee80211_ioctl_setstastats(ic, ireq);
+ error = ieee80211_ioctl_setstastats(vap, ireq);
break;
case IEEE80211_IOC_STA_TXPOW:
- error = ieee80211_ioctl_setstatxpow(ic, ireq);
+ error = ieee80211_ioctl_setstatxpow(vap, ireq);
break;
case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
@@ -2259,22 +2768,22 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */
- error = ieee80211_ioctl_setwmeparam(ic, ireq);
+ error = ieee80211_ioctl_setwmeparam(vap, ireq);
break;
case IEEE80211_IOC_DTIM_PERIOD:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_opmode != IEEE80211_M_IBSS)
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_IBSS)
return EINVAL;
if (IEEE80211_DTIM_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_DTIM_MAX) {
- ic->ic_dtim_period = ireq->i_val;
+ vap->iv_dtim_period = ireq->i_val;
error = ENETRESET; /* requires restart */
} else
error = EINVAL;
break;
case IEEE80211_IOC_BEACON_INTERVAL:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_opmode != IEEE80211_M_IBSS)
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_IBSS)
return EINVAL;
if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_BINTVAL_MAX) {
@@ -2285,210 +2794,258 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
break;
case IEEE80211_IOC_PUREG:
if (ireq->i_val)
- ic->ic_flags |= IEEE80211_F_PUREG;
+ vap->iv_flags |= IEEE80211_F_PUREG;
else
- ic->ic_flags &= ~IEEE80211_F_PUREG;
+ vap->iv_flags &= ~IEEE80211_F_PUREG;
/* NB: reset only if we're operating on an 11g channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
+ if (isvap11g(vap))
error = ENETRESET;
break;
case IEEE80211_IOC_FF:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_FF) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_FF;
+ if ((vap->iv_caps & IEEE80211_C_FF) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_FF;
} else
- ic->ic_flags &= ~IEEE80211_F_FF;
- error = ENETRESET;
+ vap->iv_flags &= ~IEEE80211_F_FF;
+ error = ERESTART;
break;
case IEEE80211_IOC_TURBOP:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_TURBOP) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_TURBOP;
+ if ((vap->iv_caps & IEEE80211_C_TURBOP) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_TURBOP;
} else
- ic->ic_flags &= ~IEEE80211_F_TURBOP;
+ vap->iv_flags &= ~IEEE80211_F_TURBOP;
error = ENETRESET;
break;
case IEEE80211_IOC_BGSCAN:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_BGSCAN;
+ if ((vap->iv_caps & IEEE80211_C_BGSCAN) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags |= IEEE80211_F_BGSCAN;
} else
- ic->ic_flags &= ~IEEE80211_F_BGSCAN;
+ vap->iv_flags &= ~IEEE80211_F_BGSCAN;
break;
case IEEE80211_IOC_BGSCAN_IDLE:
if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN)
- ic->ic_bgscanidle = ireq->i_val*hz/1000;
+ vap->iv_bgscanidle = ireq->i_val*hz/1000;
else
error = EINVAL;
break;
case IEEE80211_IOC_BGSCAN_INTERVAL:
if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN)
- ic->ic_bgscanintvl = ireq->i_val*hz;
+ vap->iv_bgscanintvl = ireq->i_val*hz;
else
error = EINVAL;
break;
case IEEE80211_IOC_SCANVALID:
if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN)
- ic->ic_scanvalid = ireq->i_val*hz;
+ vap->iv_scanvalid = ireq->i_val*hz;
else
error = EINVAL;
break;
- case IEEE80211_IOC_ROAM_RSSI_11A:
- ic->ic_roam.rssi11a = ireq->i_val;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11B:
- ic->ic_roam.rssi11bOnly = ireq->i_val;
- break;
- case IEEE80211_IOC_ROAM_RSSI_11G:
- ic->ic_roam.rssi11b = ireq->i_val;
- break;
- case IEEE80211_IOC_ROAM_RATE_11A:
- ic->ic_roam.rate11a = ireq->i_val & IEEE80211_RATE_VAL;
- break;
- case IEEE80211_IOC_ROAM_RATE_11B:
- ic->ic_roam.rate11bOnly = ireq->i_val & IEEE80211_RATE_VAL;
- break;
- case IEEE80211_IOC_ROAM_RATE_11G:
- ic->ic_roam.rate11b = ireq->i_val & IEEE80211_RATE_VAL;
- break;
- case IEEE80211_IOC_MCAST_RATE:
- ic->ic_mcast_rate = ireq->i_val & IEEE80211_RATE_VAL;
- break;
case IEEE80211_IOC_FRAGTHRESHOLD:
- if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 &&
+ if ((vap->iv_caps & IEEE80211_C_TXFRAG) == 0 &&
ireq->i_val != IEEE80211_FRAG_MAX)
- return EINVAL;
+ return EOPNOTSUPP;
if (!(IEEE80211_FRAG_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_FRAG_MAX))
return EINVAL;
- ic->ic_fragthreshold = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_fragthreshold = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_BURST:
if (ireq->i_val) {
- if ((ic->ic_caps & IEEE80211_C_BURST) == 0)
- return EINVAL;
- ic->ic_flags |= IEEE80211_F_BURST;
+ if ((vap->iv_caps & IEEE80211_C_BURST) == 0)
+ return EOPNOTSUPP;
+ ieee80211_syncflag(vap, IEEE80211_F_BURST);
} else
- ic->ic_flags &= ~IEEE80211_F_BURST;
- error = ENETRESET; /* XXX maybe not for station? */
+ ieee80211_syncflag(vap, -IEEE80211_F_BURST);
+ error = ERESTART;
break;
case IEEE80211_IOC_BMISSTHRESHOLD:
if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val &&
ireq->i_val <= IEEE80211_HWBMISS_MAX))
return EINVAL;
- ic->ic_bmissthreshold = ireq->i_val;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_bmissthreshold = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_CURCHAN:
- error = ieee80211_ioctl_setcurchan(ic, ireq);
+ error = ieee80211_ioctl_setcurchan(vap, ireq);
break;
case IEEE80211_IOC_SHORTGI:
if (ireq->i_val) {
#define IEEE80211_HTCAP_SHORTGI \
(IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40)
- if (((ireq->i_val ^ ic->ic_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
+ if (((ireq->i_val ^ vap->iv_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
return EINVAL;
if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI20;
if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40)
- ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+ vap->iv_flags_ext |= IEEE80211_FEXT_SHORTGI40;
#undef IEEE80211_HTCAP_SHORTGI
} else
- ic->ic_flags_ext &=
+ vap->iv_flags_ext &=
~(IEEE80211_FEXT_SHORTGI20 | IEEE80211_FEXT_SHORTGI40);
- /* XXX kick state machine? */
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ error = ERESTART;
break;
case IEEE80211_IOC_AMPDU:
- if (ireq->i_val) {
- if ((ic->ic_htcaps & IEEE80211_HTC_AMPDU) == 0)
- return EINVAL;
- if (ireq->i_val & 1)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
- if (ireq->i_val & 2)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
- } else
- ic->ic_flags_ext &=
- ~(IEEE80211_FEXT_AMPDU_TX|IEEE80211_FEXT_AMPDU_RX);
+ if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMPDU) == 0)
+ return EINVAL;
+ if (ireq->i_val & 1)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMPDU_TX;
+ if (ireq->i_val & 2)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMPDU_RX;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
break;
case IEEE80211_IOC_AMPDU_LIMIT:
- /* XXX validate */
- ic->ic_ampdu_limit = ireq->i_val;
+ if (!(IEEE80211_HTCAP_MAXRXAMPDU_8K <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_HTCAP_MAXRXAMPDU_64K))
+ return EINVAL;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ vap->iv_ampdu_rxmax = ireq->i_val;
+ else
+ vap->iv_ampdu_limit = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_AMPDU_DENSITY:
- /* XXX validate */
- ic->ic_ampdu_density = ireq->i_val;
+ if (!(IEEE80211_HTCAP_MPDUDENSITY_NA <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_HTCAP_MPDUDENSITY_16))
+ return EINVAL;
+ vap->iv_ampdu_density = ireq->i_val;
+ error = ERESTART;
break;
case IEEE80211_IOC_AMSDU:
- if (ireq->i_val) {
- if ((ic->ic_htcaps & IEEE80211_HTC_AMSDU) == 0)
- return EINVAL;
- if (ireq->i_val & 1)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
- if (ireq->i_val & 2)
- ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
- } else
- ic->ic_flags_ext &=
- ~(IEEE80211_FEXT_AMSDU_TX|IEEE80211_FEXT_AMSDU_RX);
+ if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMSDU) == 0)
+ return EINVAL;
+ if (ireq->i_val & 1)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMSDU_TX;
+ if (ireq->i_val & 2)
+ vap->iv_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_AMSDU_RX;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
break;
case IEEE80211_IOC_AMSDU_LIMIT:
/* XXX validate */
- ic->ic_amsdu_limit = ireq->i_val; /* XXX truncation? */
+ vap->iv_amsdu_limit = ireq->i_val; /* XXX truncation? */
break;
case IEEE80211_IOC_PUREN:
if (ireq->i_val) {
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) == 0)
return EINVAL;
- ic->ic_flags_ext |= IEEE80211_FEXT_PUREN;
+ vap->iv_flags_ext |= IEEE80211_FEXT_PUREN;
} else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_PUREN;
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_PUREN;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
break;
case IEEE80211_IOC_DOTH:
if (ireq->i_val) {
#if 0
/* XXX no capability */
- if ((ic->ic_caps & IEEE80211_C_DOTH) == 0)
- return EINVAL;
+ if ((vap->iv_caps & IEEE80211_C_DOTH) == 0)
+ return EOPNOTSUPP;
#endif
- ic->ic_flags |= IEEE80211_F_DOTH;
+ vap->iv_flags |= IEEE80211_F_DOTH;
} else
- ic->ic_flags &= ~IEEE80211_F_DOTH;
- error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ vap->iv_flags &= ~IEEE80211_F_DOTH;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_REGDOMAIN:
+ error = ieee80211_ioctl_setregdomain(vap, ireq);
+ break;
+ case IEEE80211_IOC_ROAM:
+ error = ieee80211_ioctl_setroam(vap, ireq);
+ break;
+ case IEEE80211_IOC_TXPARAMS:
+ error = ieee80211_ioctl_settxparams(vap, ireq);
break;
case IEEE80211_IOC_HTCOMPAT:
if (ireq->i_val) {
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) == 0)
- return EINVAL;
- ic->ic_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags_ext |= IEEE80211_FEXT_HTCOMPAT;
} else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_HTCOMPAT;
/* NB: reset only if we're operating on an 11n channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
- error = ENETRESET;
+ if (isvapht(vap))
+ error = ERESTART;
+ break;
+ case IEEE80211_IOC_DWDS:
+ if (ireq->i_val) {
+ /* NB: DWDS only makes sense for WDS-capable devices */
+ if ((ic->ic_caps & IEEE80211_C_WDS) == 0)
+ return EOPNOTSUPP;
+ /* NB: DWDS is used only with ap+sta vaps */
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
+ vap->iv_opmode != IEEE80211_M_STA)
+ return EINVAL;
+ vap->iv_flags |= IEEE80211_F_DWDS;
+ } else
+ vap->iv_flags &= ~IEEE80211_F_DWDS;
break;
case IEEE80211_IOC_INACTIVITY:
if (ireq->i_val)
- ic->ic_flags_ext |= IEEE80211_FEXT_INACT;
+ vap->iv_flags_ext |= IEEE80211_FEXT_INACT;
+ else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_INACT;
+ break;
+ case IEEE80211_IOC_APPIE:
+ error = ieee80211_ioctl_setappie(vap, ireq);
+ break;
+ case IEEE80211_IOC_WPS:
+ if (ireq->i_val) {
+ if ((vap->iv_caps & IEEE80211_C_WPA) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags_ext |= IEEE80211_FEXT_WPS;
+ } else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_WPS;
+ break;
+ case IEEE80211_IOC_TSN:
+ if (ireq->i_val) {
+ if ((vap->iv_caps & IEEE80211_C_WPA) == 0)
+ return EOPNOTSUPP;
+ vap->iv_flags_ext |= IEEE80211_FEXT_TSN;
+ } else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_TSN;
+ break;
+ case IEEE80211_IOC_CHANSWITCH:
+ error = ieee80211_ioctl_chanswitch(vap, ireq);
+ break;
+ case IEEE80211_IOC_DFS:
+ if (ireq->i_val) {
+#if 0
+ /* XXX no capability */
+ if ((vap->iv_caps & IEEE80211_C_DFS) == 0)
+ return EOPNOTSUPP;
+#endif
+ /* NB: DFS requires 11h support */
+ if ((vap->iv_flags & IEEE80211_F_DOTH) == 0)
+ return EINVAL;
+ vap->iv_flags_ext |= IEEE80211_FEXT_DFS;
+ } else
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_DFS;
+ break;
+ case IEEE80211_IOC_DOTD:
+ if (ireq->i_val)
+ vap->iv_flags_ext |= IEEE80211_FEXT_DOTD;
else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_INACT;
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_DOTD;
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ error = ENETRESET;
break;
case IEEE80211_IOC_HTPROTMODE:
if (ireq->i_val > IEEE80211_PROT_RTSCTS)
@@ -2496,57 +3053,143 @@ ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211re
ic->ic_htprotmode = ireq->i_val ?
IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
/* NB: if not operating in 11n this can wait */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
+ if (isvapht(vap))
error = ERESTART;
break;
- case IEEE80211_IOC_HTCONF:
- if (ireq->i_val & 1)
- ic->ic_flags_ext |= IEEE80211_FEXT_HT;
- else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_HT;
- if (ireq->i_val & 2)
- ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
- else
- ic->ic_flags_ext &= ~IEEE80211_FEXT_USEHT40;
- error = ENETRESET;
+ case IEEE80211_IOC_STA_VLAN:
+ error = ieee80211_ioctl_setstavlan(vap, ireq);
break;
default:
error = EINVAL;
break;
}
- if (error == ENETRESET)
- error = IS_UP_AUTO(ic) ? ieee80211_init(ic, 0) : 0;
+ /*
+ * The convention is that ENETRESET means an operation
+ * requires a complete re-initialization of the device (e.g.
+ * changing something that affects the association state).
+ * ERESTART means the request may be handled with only a
+ * reload of the hardware state. We hand ERESTART requests
+ * to the iv_reset callback so the driver can decide. If
+ * a device does not fillin iv_reset then it defaults to one
+ * that returns ENETRESET. Otherwise a driver may return
+ * ENETRESET (in which case a full reset will be done) or
+ * 0 to mean there's no need to do anything (e.g. when the
+ * change has no effect on the driver/device).
+ */
+ if (error == ERESTART)
+ error = IFNET_IS_UP_RUNNING(vap->iv_ifp) ?
+ vap->iv_reset(vap, ireq->i_type) : 0;
+ if (error == ENETRESET) {
+ /* XXX need to re-think AUTO handling */
+ if (IS_UP_AUTO(vap))
+ ieee80211_init(vap);
+ error = 0;
+ }
return error;
}
+/*
+ * Rebuild the parent's multicast address list after an add/del
+ * of a multicast address for a vap. We have no way to tell
+ * what happened above to optimize the work so we purge the entire
+ * list and rebuild from scratch. This is way expensive.
+ * Note also the half-baked workaround for if_addmulti calling
+ * back to the parent device; there's no way to insert mcast
+ * entries quietly and/or cheaply.
+ */
+static void
+ieee80211_ioctl_updatemulti(struct ieee80211com *ic)
+{
+ struct ifnet *parent = ic->ic_ifp;
+ struct ieee80211vap *vap;
+ void *ioctl;
+
+ IEEE80211_LOCK(ic);
+ if_purgemaddrs(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)
+ (void) if_addmulti(parent, ifma->ifma_addr, NULL);
+ }
+ parent->if_ioctl = ioctl;
+
+ ic->ic_update_mcast(ic->ic_ifp);
+ IEEE80211_UNLOCK(ic);
+}
+
int
-ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
+ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap;
+ struct ieee80211com *ic;
int error = 0;
struct ifreq *ifr;
struct ifaddr *ifa; /* XXX */
+ vap = ifp->if_softc;
+ if (vap == NULL) {
+ /*
+ * During detach we clear the backpointer in the softc
+ * so any ioctl request through the ifnet that arrives
+ * before teardown is ignored/rejected. In particular
+ * this hack handles destroying a vap used by an app
+ * like wpa_supplicant that will respond to the vap
+ * being forced into INIT state by immediately trying
+ * to force it back up. We can yank this hack if/when
+ * we can destroy the ifnet before cleaning up vap state.
+ */
+ return ENXIO;
+ }
switch (cmd) {
+ case SIOCSIFFLAGS:
+ ic = vap->iv_ic;
+ IEEE80211_LOCK(ic);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ ieee80211_syncifflag_locked(ic, IFF_ALLMULTI);
+ if (ifp->if_flags & IFF_UP) {
+ /*
+ * Bring ourself up unless we're already operational.
+ * If we're the first vap and the parent is not up
+ * then it will automatically be brought up as a
+ * side-effect of bringing ourself up.
+ */
+ if (vap->iv_state == IEEE80211_S_INIT)
+ ieee80211_start_locked(vap);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ /*
+ * Stop ourself. If we are the last vap to be
+ * marked down the parent will also be taken down.
+ */
+ ieee80211_stop_locked(vap);
+ }
+ IEEE80211_UNLOCK(ic);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ieee80211_ioctl_updatemulti(vap->iv_ic);
+ break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
- error = ifmedia_ioctl(ifp, (struct ifreq *) data,
- &ic->ic_media, cmd);
+ ifr = (struct ifreq *)data;
+ error = ifmedia_ioctl(ifp, ifr, &vap->iv_media, cmd);
break;
case SIOCG80211:
- error = ieee80211_ioctl_get80211(ic, cmd,
+ error = ieee80211_ioctl_get80211(vap, cmd,
(struct ieee80211req *) data);
break;
case SIOCS80211:
error = priv_check(curthread, PRIV_NET80211_MANAGE);
if (error == 0)
- error = ieee80211_ioctl_set80211(ic, cmd,
+ error = ieee80211_ioctl_set80211(vap, cmd,
(struct ieee80211req *) data);
break;
case SIOCG80211STATS:
ifr = (struct ifreq *)data;
- copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
+ copyout(&vap->iv_stats, ifr->ifr_data, sizeof (vap->iv_stats));
break;
case SIOCSIFMTU:
ifr = (struct ifreq *)data;
@@ -2601,6 +3244,14 @@ ieee80211_ioctl(struct ieee80211com *ic, 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:
error = ether_ioctl(ifp, cmd, data);
break;
diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h
index e96ab52..c40b929 100644
--- a/sys/net80211/ieee80211_ioctl.h
+++ b/sys/net80211/ieee80211_ioctl.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -196,6 +196,8 @@ struct ieee80211_stats {
uint32_t is_tx_badstate; /* tx discard state != RUN */
uint32_t is_tx_notassoc; /* tx failed, sta not assoc */
uint32_t is_tx_classify; /* tx classification failed */
+ uint32_t is_dwds_mcast; /* discard mcast over dwds */
+ uint32_t is_dwds_qdrop; /* dwds pending frame q full */
uint32_t is_ht_assoc_nohtcap; /* non-HT sta rejected */
uint32_t is_ht_assoc_downgrade; /* HT sta forced to legacy */
uint32_t is_ht_assoc_norate; /* HT assoc w/ rate mismatch */
@@ -208,7 +210,12 @@ struct ieee80211_stats {
uint32_t is_ampdu_stop; /* A-MPDU stream stopped */
uint32_t is_ampdu_stop_failed; /* A-MPDU stream not running */
uint32_t is_ampdu_rx_reorder; /* A-MPDU held for rx reorder */
- uint32_t is_spare[16];
+ uint32_t is_scan_bg; /* background scans started */
+ uint8_t is_rx_deauth_code; /* last rx'd deauth reason */
+ uint8_t is_rx_disassoc_code; /* last rx'd disassoc reason */
+ uint8_t is_rx_authfail_code; /* last rx'd auth fail reason */
+ uint32_t is_beacon_miss; /* beacon miss notification */
+ uint32_t is_spare[14];
};
/*
@@ -264,6 +271,7 @@ struct ieee80211req_mlme {
#define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */
#define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */
#define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */
+#define IEEE80211_MLME_AUTH 6 /* authenticate station */
uint8_t im_ssid_len; /* length of optional ssid */
uint16_t im_reason; /* 802.11 reason code */
uint8_t im_macaddr[IEEE80211_ADDR_LEN];
@@ -281,6 +289,7 @@ enum {
IEEE80211_MACCMD_DETACH = 4, /* detach ACL policy */
IEEE80211_MACCMD_POLICY = 5, /* get ACL policy */
IEEE80211_MACCMD_LIST = 6, /* get ACL database */
+ IEEE80211_MACCMD_POLICY_RADIUS = 7, /* set policy: RADIUS managed */
};
struct ieee80211req_maclist {
@@ -335,7 +344,7 @@ struct ieee80211req_sta_stats {
* to retrieve other data like stats, unicast key, etc.
*/
struct ieee80211req_sta_info {
- uint16_t isi_len; /* length (mult of 4) */
+ uint16_t isi_len; /* total length (mult of 4) */
uint16_t isi_ie_off; /* offset to IE data */
uint16_t isi_ie_len; /* IE length */
uint16_t isi_freq; /* MHz */
@@ -350,7 +359,7 @@ struct ieee80211req_sta_info {
uint8_t isi_nrates;
/* negotiated rates */
uint8_t isi_rates[IEEE80211_RATE_MAXSIZE];
- uint8_t isi_txrate; /* index to isi_rates[] */
+ uint8_t isi_txrate; /* legacy/IEEE rate or MCS */
uint16_t isi_associd; /* assoc response */
uint16_t isi_txpower; /* current tx power */
uint16_t isi_vlan; /* vlan tag */
@@ -358,6 +367,9 @@ struct ieee80211req_sta_info {
uint16_t isi_txseqs[IEEE80211_TID_SIZE];/* tx seq #/TID */
uint16_t isi_rxseqs[IEEE80211_TID_SIZE];/* rx seq#/TID */
uint16_t isi_inact; /* inactivity timer */
+ uint16_t isi_txmbps; /* current tx rate in .5 Mb/s */
+ uint32_t isi_jointime; /* time of assoc/join */
+ struct ieee80211_mimo_info isi_mimo; /* MIMO info for 11n sta's */
/* XXX frag state? */
/* variable length IE data */
};
@@ -384,15 +396,98 @@ struct ieee80211req_sta_txpow {
};
/*
- * WME parameters are set and return using i_val and i_len.
- * i_val holds the value itself. i_len specifies the AC
- * and, as appropriate, then high bit specifies whether the
- * operation is to be applied to the BSS or ourself.
+ * WME parameters manipulated with IEEE80211_IOC_WME_CWMIN
+ * through IEEE80211_IOC_WME_ACKPOLICY are set and return
+ * using i_val and i_len. i_val holds the value itself.
+ * i_len specifies the AC and, as appropriate, then high bit
+ * specifies whether the operation is to be applied to the
+ * BSS or ourself.
*/
#define IEEE80211_WMEPARAM_SELF 0x0000 /* parameter applies to self */
#define IEEE80211_WMEPARAM_BSS 0x8000 /* parameter applies to BSS */
#define IEEE80211_WMEPARAM_VAL 0x7fff /* parameter value */
+/*
+ * Application Information Elements can be appended to a variety
+ * of frames with the IEE80211_IOC_APPIE request. This request
+ * piggybacks on a normal ieee80211req; the frame type is passed
+ * in i_val as the 802.11 FC0 bytes and the length of the IE data
+ * is passed in i_len. The data is referenced in i_data. If i_len
+ * is zero then any previously configured IE data is removed. At
+ * most IEEE80211_MAX_APPIE data be appened. Note that multiple
+ * IE's can be supplied; the data is treated opaquely.
+ */
+#define IEEE80211_MAX_APPIE 1024 /* max app IE data */
+/*
+ * Hack: the WPA authenticator uses this mechanism to specify WPA
+ * ie's that are used instead of the ones normally constructed using
+ * the cipher state setup with separate ioctls. This avoids issues
+ * like the authenticator ordering ie data differently than the
+ * net80211 layer and needing to keep separate state for WPA and RSN.
+ */
+#define IEEE80211_APPIE_WPA \
+ (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON | \
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+
+/*
+ * Station mode roaming parameters. These are maintained
+ * per band/mode and control the roaming algorithm.
+ */
+struct ieee80211_roamparams_req {
+ struct ieee80211_roamparam params[IEEE80211_MODE_MAX];
+};
+
+/*
+ * Transmit parameters. These can be used to set fixed transmit
+ * rate for each operating mode when operating as client or on a
+ * per-client basis according to the capabilities of the client
+ * (e.g. an 11b client associated to an 11g ap) when operating as
+ * an ap.
+ *
+ * MCS are distinguished from legacy rates by or'ing in 0x80.
+ */
+struct ieee80211_txparams_req {
+ struct ieee80211_txparam params[IEEE80211_MODE_MAX];
+};
+
+/*
+ * Set regulatory domain state with IEEE80211_IOC_REGDOMAIN.
+ * Note this is both the regulatory description and the channel
+ * list. The get request for IEEE80211_IOC_REGDOMAIN returns
+ * only the regdomain info; the channel list is obtained
+ * separately with IEEE80211_IOC_CHANINFO.
+ */
+struct ieee80211_regdomain_req {
+ struct ieee80211_regdomain rd;
+ struct ieee80211req_chaninfo chaninfo;
+};
+
+/*
+ * Get driver capabilities. Driver, hardware crypto, and
+ * HT/802.11n capabilities, and a table that describes what
+ * the radio can do.
+ */
+struct ieee80211_devcaps_req {
+ uint32_t dc_drivercaps; /* general driver caps */
+ uint32_t dc_cryptocaps; /* hardware crypto support */
+ uint32_t dc_htcaps; /* HT/802.11n support */
+ struct ieee80211req_chaninfo dc_chaninfo;
+};
+
+struct ieee80211_chanswitch_req {
+ struct ieee80211_channel csa_chan; /* new channel */
+ int csa_mode; /* CSA mode */
+ int csa_count; /* beacon count to switch */
+};
+
+/*
+ * Get/set per-station vlan tag.
+ */
+struct ieee80211req_sta_vlan {
+ uint8_t sv_macaddr[IEEE80211_ADDR_LEN];
+ uint16_t sv_vlan;
+};
+
#ifdef __FreeBSD__
/*
* FreeBSD-style ioctls.
@@ -443,8 +538,8 @@ struct ieee80211req {
#define IEEE80211_IOC_WPAKEY 19
#define IEEE80211_IOC_DELKEY 20
#define IEEE80211_IOC_MLME 21
-#define IEEE80211_IOC_OPTIE 22 /* optional info. element */
-#define IEEE80211_IOC_SCAN_REQ 23
+/* 22 was IEEE80211_IOC_OPTIE, replaced by IEEE80211_IOC_APPIE */
+/* 23 was IEEE80211_IOC_SCAN_REQ */
/* 24 was IEEE80211_IOC_SCAN_RESULTS */
#define IEEE80211_IOC_COUNTERMEASURES 25 /* WPA/TKIP countermeasures */
#define IEEE80211_IOC_WPA 26 /* WPA mode (0,1,2) */
@@ -452,14 +547,8 @@ struct ieee80211req {
#define IEEE80211_IOC_WME 28 /* WME mode (on, off) */
#define IEEE80211_IOC_HIDESSID 29 /* hide SSID mode (on, off) */
#define IEEE80211_IOC_APBRIDGE 30 /* AP inter-sta bridging */
-#define IEEE80211_IOC_MCASTCIPHER 31 /* multicast/default cipher */
-#define IEEE80211_IOC_MCASTKEYLEN 32 /* multicast key length */
-#define IEEE80211_IOC_UCASTCIPHERS 33 /* unicast cipher suites */
-#define IEEE80211_IOC_UCASTCIPHER 34 /* unicast cipher */
-#define IEEE80211_IOC_UCASTKEYLEN 35 /* unicast key length */
-#define IEEE80211_IOC_DRIVER_CAPS 36 /* driver capabilities */
-#define IEEE80211_IOC_KEYMGTALGS 37 /* key management algorithms */
-#define IEEE80211_IOC_RSNCAPS 38 /* RSN capabilities */
+/* 31-35,37-38 were for WPA authenticator settings */
+/* 36 was IEEE80211_IOC_DRIVER_CAPS */
#define IEEE80211_IOC_WPAIE 39 /* WPA information element */
#define IEEE80211_IOC_STA_STATS 40 /* per-station statistics */
#define IEEE80211_IOC_MACCMD 41 /* MAC ACL operation */
@@ -484,13 +573,7 @@ struct ieee80211req {
#define IEEE80211_IOC_BGSCAN_IDLE 60 /* bg scan idle threshold */
#define IEEE80211_IOC_BGSCAN_INTERVAL 61 /* bg scan interval */
#define IEEE80211_IOC_SCANVALID 65 /* scan cache valid threshold */
-#define IEEE80211_IOC_ROAM_RSSI_11A 66 /* rssi threshold in 11a */
-#define IEEE80211_IOC_ROAM_RSSI_11B 67 /* rssi threshold in 11b */
-#define IEEE80211_IOC_ROAM_RSSI_11G 68 /* rssi threshold in 11g */
-#define IEEE80211_IOC_ROAM_RATE_11A 69 /* tx rate threshold in 11a */
-#define IEEE80211_IOC_ROAM_RATE_11B 70 /* tx rate threshold in 11b */
-#define IEEE80211_IOC_ROAM_RATE_11G 71 /* tx rate threshold in 11g */
-#define IEEE80211_IOC_MCAST_RATE 72 /* tx rate for mcast frames */
+/* 66-72 were IEEE80211_IOC_ROAM_* and IEEE80211_IOC_MCAST_RATE */
#define IEEE80211_IOC_FRAGTHRESHOLD 73 /* tx fragmentation threshold */
#define IEEE80211_IOC_BURST 75 /* packet bursting */
#define IEEE80211_IOC_SCAN_RESULTS 76 /* get scan results */
@@ -506,13 +589,84 @@ struct ieee80211req {
#define IEEE80211_IOC_AMSDU_LIMIT 86 /* A-MSDU length limit */
#define IEEE80211_IOC_PUREN 87 /* pure 11n (no legacy sta's) */
#define IEEE80211_IOC_DOTH 88 /* 802.11h (on, off) */
-#define IEEE80211_IOC_REGDOMAIN 89 /* regulatory domain */
-#define IEEE80211_IOC_COUNTRYCODE 90 /* ISO country code */
-#define IEEE80211_IOC_LOCATION 91 /* indoor/outdoor/anywhere */
+/* 89-91 were regulatory items */
#define IEEE80211_IOC_HTCOMPAT 92 /* support pre-D1.10 HT ie's */
+#define IEEE80211_IOC_DWDS 93 /* DWDS/4-address handling */
#define IEEE80211_IOC_INACTIVITY 94 /* sta inactivity handling */
+#define IEEE80211_IOC_APPIE 95 /* application IE's */
+#define IEEE80211_IOC_WPS 96 /* WPS operation */
+#define IEEE80211_IOC_TSN 97 /* TSN operation */
+#define IEEE80211_IOC_DEVCAPS 98 /* driver+device capabilities */
+#define IEEE80211_IOC_CHANSWITCH 99 /* start 11h channel switch */
+#define IEEE80211_IOC_DFS 100 /* DFS (on, off) */
+#define IEEE80211_IOC_DOTD 101 /* 802.11d (on, off) */
#define IEEE80211_IOC_HTPROTMODE 102 /* HT protection (off, rts) */
+#define IEEE80211_IOC_SCAN_REQ 103 /* scan w/ specified params */
+#define IEEE80211_IOC_SCAN_CANCEL 104 /* cancel ongoing scan */
#define IEEE80211_IOC_HTCONF 105 /* HT config (off, HT20, HT40)*/
+#define IEEE80211_IOC_REGDOMAIN 106 /* regulatory domain info */
+#define IEEE80211_IOC_ROAM 107 /* roaming params en masse */
+#define IEEE80211_IOC_TXPARAMS 108 /* tx parameters */
+#define IEEE80211_IOC_STA_VLAN 109 /* per-station vlan tag */
+
+/*
+ * Parameters for controlling a scan requested with
+ * IEEE80211_IOC_SCAN_REQ.
+ *
+ * Active scans cause ProbeRequest frames to be issued for each
+ * specified ssid and, by default, a broadcast ProbeRequest frame.
+ * The set of ssid's is specified in the request.
+ *
+ * By default the scan will cause a BSS to be joined (in station/adhoc
+ * mode) or a channel to be selected for operation (hostap mode).
+ * To disable that specify IEEE80211_IOC_SCAN_NOPICK and if the
+ *
+ * If the station is currently associated to an AP then a scan request
+ * will cause the station to leave the current channel and potentially
+ * miss frames from the AP. Alternatively the station may notify the
+ * AP that it is going into power save mode before it leaves the channel.
+ * This ensures frames for the station are buffered by the AP. This is
+ * termed a ``bg scan'' and is requested with the IEEE80211_IOC_SCAN_BGSCAN
+ * flag. Background scans may take longer than foreground scans and may
+ * be preempted by traffic. If a station is not associated to an AP
+ * then a request for a background scan is automatically done in the
+ * foreground.
+ *
+ * The results of the scan request are cached by the system. This
+ * information is aged out and/or invalidated based on events like not
+ * being able to associated to an AP. To flush the current cache
+ * contents before doing a scan the IEEE80211_IOC_SCAN_FLUSH flag may
+ * be specified.
+ *
+ * By default the scan will be done until a suitable AP is located
+ * or a channel is found for use. A scan can also be constrained
+ * to be done once (IEEE80211_IOC_SCAN_ONCE) or to last for no more
+ * than a specified duration.
+ */
+struct ieee80211_scan_req {
+ int sr_flags;
+#define IEEE80211_IOC_SCAN_NOPICK 0x00001 /* scan only, no selection */
+#define IEEE80211_IOC_SCAN_ACTIVE 0x00002 /* active scan (probe req) */
+#define IEEE80211_IOC_SCAN_PICK1ST 0x00004 /* ``hey sailor'' mode */
+#define IEEE80211_IOC_SCAN_BGSCAN 0x00008 /* bg scan, exit ps at end */
+#define IEEE80211_IOC_SCAN_ONCE 0x00010 /* do one complete pass */
+#define IEEE80211_IOC_SCAN_NOBCAST 0x00020 /* don't send bcast probe req */
+#define IEEE80211_IOC_SCAN_NOJOIN 0x00040 /* no auto-sequencing */
+#define IEEE80211_IOC_SCAN_FLUSH 0x10000 /* flush scan cache first */
+#define IEEE80211_IOC_SCAN_CHECK 0x20000 /* check scan cache first */
+ u_int sr_duration; /* duration (ms) */
+#define IEEE80211_IOC_SCAN_DURATION_MIN 1
+#define IEEE80211_IOC_SCAN_DURATION_MAX 0x7fffffff
+#define IEEE80211_IOC_SCAN_FOREVER IEEE80211_IOC_SCAN_DURATION_MAX
+ u_int sr_mindwell; /* min channel dwelltime (ms) */
+ u_int sr_maxdwell; /* max channel dwelltime (ms) */
+ int sr_nssid;
+#define IEEE80211_IOC_SCAN_MAX_SSID 3
+ struct {
+ int len; /* length in bytes */
+ uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
+ } sr_ssid[IEEE80211_IOC_SCAN_MAX_SSID];
+};
/*
* Scan result data returned for IEEE80211_IOC_SCAN_RESULTS.
@@ -523,8 +677,8 @@ struct ieee80211req {
* in isr_len. Result records are rounded to a multiple of 4 bytes.
*/
struct ieee80211req_scan_result {
- uint16_t isr_len; /* length (mult of 4) */
- uint16_t isr_ie_off; /* offset to IE data */
+ uint16_t isr_len; /* total length (mult of 4) */
+ uint16_t isr_ie_off; /* offset to SSID+IE data */
uint16_t isr_ie_len; /* IE length */
uint16_t isr_freq; /* MHz */
uint16_t isr_flags; /* channel flags */
@@ -540,10 +694,47 @@ struct ieee80211req_scan_result {
/* variable length SSID followed by IE data */
};
+/*
+ * Virtual AP cloning parameters. The parent device must
+ * be a vap-capable device. All parameters specified with
+ * the clone request are fixed for the lifetime of the vap.
+ *
+ * There are two flavors of WDS vaps: legacy and dynamic.
+ * Legacy WDS operation implements a static binding between
+ * two stations encapsulating traffic in 4-address frames.
+ * Dynamic WDS vaps are created when a station associates to
+ * an AP and sends a 4-address frame. If the AP vap is
+ * configured to support WDS then this will generate an
+ * event to user programs listening on the routing socket
+ * and a Dynamic WDS vap will be created to handle traffic
+ * to/from that station. In both cases the bssid of the
+ * peer must be specified when creating the vap.
+ *
+ * By default a vap will inherit the mac address/bssid of
+ * the underlying device. To request a unique address the
+ * IEEE80211_CLONE_BSSID flag should be supplied. This is
+ * meaningless for WDS vaps as they share the bssid of an
+ * AP vap that must otherwise exist. Note that some devices
+ * may not be able to support multiple addresses.
+ *
+ * Station mode vap's normally depend on the device to notice
+ * when the AP stops sending beacon frames. If IEEE80211_CLONE_NOBEACONS
+ * is specified the net80211 layer will do this in s/w. This
+ * is mostly useful when setting up a WDS repeater/extender where
+ * an AP vap is combined with a sta vap and the device isn't able
+ * to track beacon frames in hardware.
+ */
struct ieee80211_clone_params {
char icp_parent[IFNAMSIZ]; /* parent device */
- int icp_opmode; /* operating mode */
+ uint16_t icp_opmode; /* operating mode */
+ uint16_t icp_flags; /* see below */
+ uint8_t icp_bssid[IEEE80211_ADDR_LEN]; /* for WDS links */
+ uint8_t icp_macaddr[IEEE80211_ADDR_LEN];/* local address */
};
+#define IEEE80211_CLONE_BSSID 0x0001 /* allocate unique mac/bssid */
+#define IEEE80211_CLONE_NOBEACONS 0x0002 /* don't setup beacon timers */
+#define IEEE80211_CLONE_WDSLEGACY 0x0004 /* legacy WDS processing */
+#define IEEE80211_CLONE_MACADDR 0x0008 /* use specified mac addr */
#endif /* __FreeBSD__ */
#endif /* _NET80211_IEEE80211_IOCTL_H_ */
diff --git a/sys/net80211/ieee80211_monitor.c b/sys/net80211/ieee80211_monitor.c
new file mode 100644
index 0000000..b1e98eb
--- /dev/null
+++ b/sys/net80211/ieee80211_monitor.c
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 Monitor mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_monitor.h>
+
+static void monitor_vattach(struct ieee80211vap *);
+static int monitor_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int monitor_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp);
+
+void
+ieee80211_monitor_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_MONITOR] = monitor_vattach;
+}
+
+void
+ieee80211_monitor_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+monitor_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+monitor_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = monitor_newstate;
+ vap->iv_input = monitor_input;
+ vap->iv_opdetach = monitor_vdetach;
+}
+
+/*
+ * IEEE80211_M_MONITOR vap state machine handler.
+ */
+static int
+monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ if (nstate == IEEE80211_S_RUN) {
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ ieee80211_create_ibss(vap, ic->ic_curchan);
+ break;
+ default:
+ break;
+ }
+ /*
+ * NB: this shouldn't be here but many people use
+ * monitor mode for raw packets; once we switch
+ * them over to adhoc demo mode remove this.
+ */
+ ieee80211_node_authorize(vap->iv_bss);
+ }
+ return 0;
+}
+
+/*
+ * Process a received frame in monitor mode.
+ */
+static int
+monitor_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ return -1;
+}
diff --git a/sys/net80211/ieee80211_monitor.h b/sys/net80211/ieee80211_monitor.h
new file mode 100644
index 0000000..d7dd8e9
--- /dev/null
+++ b/sys/net80211/ieee80211_monitor.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_MONITOR_H_
+#define _NET80211_IEEE80211_MONITOR_H_
+
+/*
+ * Monitor implementation definitions.
+ */
+void ieee80211_monitor_attach(struct ieee80211com *);
+void ieee80211_monitor_detach(struct ieee80211com *);
+#endif /* !_NET80211_IEEE80211_MONITOR_H_ */
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index 0ed8b79..58cb33f 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -40,18 +42,22 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_wds.h>
#include <net/bpf.h>
/*
* Association id's are managed with a bit vector.
*/
-#define IEEE80211_AID_SET(b, w) \
- ((w)[IEEE80211_AID(b) / 32] |= (1 << (IEEE80211_AID(b) % 32)))
-#define IEEE80211_AID_CLR(b, w) \
- ((w)[IEEE80211_AID(b) / 32] &= ~(1 << (IEEE80211_AID(b) % 32)))
-#define IEEE80211_AID_ISSET(b, w) \
- ((w)[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
+#define IEEE80211_AID_SET(_vap, b) \
+ ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] |= \
+ (1 << (IEEE80211_AID(b) % 32)))
+#define IEEE80211_AID_CLR(_vap, b) \
+ ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] &= \
+ ~(1 << (IEEE80211_AID(b) % 32)))
+#define IEEE80211_AID_ISSET(_vap, b) \
+ ((_vap)->iv_aid_bitmap[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
#ifdef IEEE80211_DEBUG_REFCNT
#define REFCNT_LOC "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line
@@ -64,117 +70,112 @@ static int ieee80211_sta_join1(struct ieee80211_node *);
static struct ieee80211_node *node_alloc(struct ieee80211_node_table *);
static void node_cleanup(struct ieee80211_node *);
static void node_free(struct ieee80211_node *);
+static void node_age(struct ieee80211_node *);
static int8_t node_getrssi(const struct ieee80211_node *);
static void node_getsignal(const struct ieee80211_node *, int8_t *, int8_t *);
+static void node_getmimoinfo(const struct ieee80211_node *,
+ struct ieee80211_mimo_info *);
-static void ieee80211_setup_node(struct ieee80211_node_table *,
- struct ieee80211_node *, const uint8_t *);
static void _ieee80211_free_node(struct ieee80211_node *);
static void ieee80211_node_table_init(struct ieee80211com *ic,
struct ieee80211_node_table *nt, const char *name,
int inact, int keymaxix);
-static void ieee80211_node_table_reset(struct ieee80211_node_table *);
+static void ieee80211_node_table_reset(struct ieee80211_node_table *,
+ struct ieee80211vap *);
+static void ieee80211_node_reclaim(struct ieee80211_node *);
static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
static void ieee80211_erp_timeout(struct ieee80211com *);
MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
+MALLOC_DEFINE(M_80211_NODE_IE, "80211nodeie", "802.11 node ie");
void
ieee80211_node_attach(struct ieee80211com *ic)
{
+ ieee80211_node_table_init(ic, &ic->ic_sta, "station",
+ IEEE80211_INACT_INIT, ic->ic_max_keyix);
+ callout_init(&ic->ic_inact, CALLOUT_MPSAFE);
+ callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
+ ieee80211_node_timeout, ic);
ic->ic_node_alloc = node_alloc;
ic->ic_node_free = node_free;
ic->ic_node_cleanup = node_cleanup;
+ ic->ic_node_age = node_age;
+ ic->ic_node_drain = node_age; /* NB: same as age */
ic->ic_node_getrssi = node_getrssi;
ic->ic_node_getsignal = node_getsignal;
+ ic->ic_node_getmimoinfo = node_getmimoinfo;
- /* default station inactivity timer setings */
- ic->ic_inact_init = IEEE80211_INACT_INIT;
- ic->ic_inact_auth = IEEE80211_INACT_AUTH;
- ic->ic_inact_run = IEEE80211_INACT_RUN;
- ic->ic_inact_probe = IEEE80211_INACT_PROBE;
-
- callout_init(&ic->ic_inact, CALLOUT_MPSAFE);
-
- /* NB: driver should override */
- ic->ic_max_aid = IEEE80211_AID_DEF;
-
+ /*
+ * Set flags to be propagated to all vap's;
+ * these define default behaviour/configuration.
+ */
ic->ic_flags_ext |= IEEE80211_FEXT_INACT; /* inactivity processing */
}
void
-ieee80211_node_lateattach(struct ieee80211com *ic)
+ieee80211_node_detach(struct ieee80211com *ic)
{
- struct ieee80211_rsnparms *rsn;
- if (ic->ic_max_aid > IEEE80211_AID_MAX)
- ic->ic_max_aid = IEEE80211_AID_MAX;
- MALLOC(ic->ic_aid_bitmap, uint32_t *,
- howmany(ic->ic_max_aid, 32) * sizeof(uint32_t),
- M_80211_NODE, M_NOWAIT | M_ZERO);
- if (ic->ic_aid_bitmap == NULL) {
- /* XXX no way to recover */
- printf("%s: no memory for AID bitmap!\n", __func__);
- ic->ic_max_aid = 0;
- }
+ callout_drain(&ic->ic_inact);
+ ieee80211_node_table_cleanup(&ic->ic_sta);
+}
- ieee80211_node_table_init(ic, &ic->ic_sta, "station",
- IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix);
+void
+ieee80211_node_vattach(struct ieee80211vap *vap)
+{
+ /* NB: driver can override */
+ vap->iv_max_aid = IEEE80211_AID_DEF;
- ieee80211_reset_bss(ic);
- /*
- * Setup "global settings" in the bss node so that
- * each new station automatically inherits them.
- */
- rsn = &ic->ic_bss->ni_rsn;
- /* WEP, TKIP, and AES-CCM are always supported */
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_WEP;
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_TKIP;
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_CCM;
- if (ic->ic_caps & IEEE80211_C_AES)
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_OCB;
- if (ic->ic_caps & IEEE80211_C_CKIP)
- rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_CKIP;
- /*
- * Default unicast cipher to WEP for 802.1x use. If
- * WPA is enabled the management code will set these
- * values to reflect.
- */
- rsn->rsn_ucastcipher = IEEE80211_CIPHER_WEP;
- rsn->rsn_ucastkeylen = 104 / NBBY;
- /*
- * WPA says the multicast cipher is the lowest unicast
- * cipher supported. But we skip WEP which would
- * otherwise be used based on this criteria.
- */
- rsn->rsn_mcastcipher = IEEE80211_CIPHER_TKIP;
- rsn->rsn_mcastkeylen = 128 / NBBY;
+ /* default station inactivity timer setings */
+ vap->iv_inact_init = IEEE80211_INACT_INIT;
+ vap->iv_inact_auth = IEEE80211_INACT_AUTH;
+ vap->iv_inact_run = IEEE80211_INACT_RUN;
+ vap->iv_inact_probe = IEEE80211_INACT_PROBE;
+}
- /*
- * We support both WPA-PSK and 802.1x; the one used
- * is determined by the authentication mode and the
- * setting of the PSK state.
- */
- rsn->rsn_keymgmtset = WPA_ASE_8021X_UNSPEC | WPA_ASE_8021X_PSK;
- rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+void
+ieee80211_node_latevattach(struct ieee80211vap *vap)
+{
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ /* XXX should we allow max aid to be zero? */
+ if (vap->iv_max_aid < IEEE80211_AID_MIN) {
+ vap->iv_max_aid = IEEE80211_AID_MIN;
+ if_printf(vap->iv_ifp,
+ "WARNING: max aid too small, changed to %d\n",
+ vap->iv_max_aid);
+ }
+ MALLOC(vap->iv_aid_bitmap, uint32_t *,
+ howmany(vap->iv_max_aid, 32) * sizeof(uint32_t),
+ M_80211_NODE, M_NOWAIT | M_ZERO);
+ if (vap->iv_aid_bitmap == NULL) {
+ /* XXX no way to recover */
+ printf("%s: no memory for AID bitmap, max aid %d!\n",
+ __func__, vap->iv_max_aid);
+ vap->iv_max_aid = 0;
+ }
+ }
+
+ ieee80211_reset_bss(vap);
- ic->ic_auth = ieee80211_authenticator_get(ic->ic_bss->ni_authmode);
+ vap->iv_auth = ieee80211_authenticator_get(vap->iv_bss->ni_authmode);
}
void
-ieee80211_node_detach(struct ieee80211com *ic)
+ieee80211_node_vdetach(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
- if (ic->ic_bss != NULL) {
- ieee80211_free_node(ic->ic_bss);
- ic->ic_bss = NULL;
+ ieee80211_node_table_reset(&ic->ic_sta, vap);
+ if (vap->iv_bss != NULL) {
+ ieee80211_free_node(vap->iv_bss);
+ vap->iv_bss = NULL;
}
- ieee80211_node_table_cleanup(&ic->ic_sta);
- if (ic->ic_aid_bitmap != NULL) {
- FREE(ic->ic_aid_bitmap, M_80211_NODE);
- ic->ic_aid_bitmap = NULL;
+ if (vap->iv_aid_bitmap != NULL) {
+ FREE(vap->iv_aid_bitmap, M_80211_NODE);
+ vap->iv_aid_bitmap = NULL;
}
}
@@ -185,20 +186,16 @@ ieee80211_node_detach(struct ieee80211com *ic)
void
ieee80211_node_authorize(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
-
ni->ni_flags |= IEEE80211_NODE_AUTH;
- ni->ni_inact_reload = ic->ic_inact_run;
+ ni->ni_inact_reload = ni->ni_vap->iv_inact_run;
ni->ni_inact = ni->ni_inact_reload;
}
void
ieee80211_node_unauthorize(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
-
ni->ni_flags &= ~IEEE80211_NODE_AUTH;
- ni->ni_inact_reload = ic->ic_inact_auth;
+ ni->ni_inact_reload = ni->ni_vap->iv_inact_auth;
if (ni->ni_inact > ni->ni_inact_reload)
ni->ni_inact = ni->ni_inact_reload;
}
@@ -206,18 +203,16 @@ ieee80211_node_unauthorize(struct ieee80211_node *ni)
/*
* Set/change the channel. The rate set is also updated as
* to insure a consistent view by drivers.
+ * XXX should be private but hostap needs it to deal with CSA
*/
-static void
-ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
+void
+ieee80211_node_set_chan(struct ieee80211_node *ni,
+ struct ieee80211_channel *chan)
{
- struct ieee80211_channel *chan = ic->ic_bsschan;
+ struct ieee80211com *ic = ni->ni_ic;
+
+ KASSERT(chan != IEEE80211_CHAN_ANYC, ("no channel"));
-#if 0
- KASSERT(chan != IEEE80211_CHAN_ANYC, ("bss channel not setup"));
-#else
- if (chan == IEEE80211_CHAN_ANYC) /* XXX while scanning */
- chan = ic->ic_curchan;
-#endif
ni->ni_chan = chan;
if (IEEE80211_IS_CHAN_HT(chan)) {
/*
@@ -232,31 +227,6 @@ ieee80211_node_set_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
ni->ni_rates = *ieee80211_get_suprates(ic, chan);
}
-/*
- * Probe the curent channel, if allowed, while scanning.
- * If the channel is not marked passive-only then send
- * a probe request immediately. Otherwise mark state and
- * listen for beacons on the channel; if we receive something
- * then we'll transmit a probe request.
- */
-void
-ieee80211_probe_curchan(struct ieee80211com *ic, int force)
-{
- struct ifnet *ifp = ic->ic_ifp;
-
- if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 || force) {
- /*
- * XXX send both broadcast+directed probe request
- */
- ieee80211_send_probereq(ic->ic_bss,
- ic->ic_myaddr, ifp->if_broadcastaddr,
- ifp->if_broadcastaddr,
- ic->ic_des_ssid[0].ssid, ic->ic_des_ssid[0].len,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- } else
- ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN;
-}
-
static __inline void
copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss)
{
@@ -264,90 +234,87 @@ copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss)
nbss->ni_authmode = obss->ni_authmode;
nbss->ni_txpower = obss->ni_txpower;
nbss->ni_vlan = obss->ni_vlan;
- nbss->ni_rsn = obss->ni_rsn;
/* XXX statistics? */
+ /* XXX legacy WDS bssid? */
}
void
-ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
+ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan)
{
- struct ieee80211_node_table *nt;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: creating ibss\n", __func__);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: creating ibss on channel %u\n", __func__,
+ ieee80211_chan2ieee(ic, chan));
- /*
- * Create the station/neighbor table. Note that for adhoc
- * mode we make the initial inactivity timer longer since
- * we create nodes only through discovery and they typically
- * are long-lived associations.
- */
- nt = &ic->ic_sta;
- IEEE80211_NODE_LOCK(nt);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- nt->nt_name = "station";
- nt->nt_inact_init = ic->ic_inact_init;
- } else {
- nt->nt_name = "neighbor";
- nt->nt_inact_init = ic->ic_inact_run;
- }
- IEEE80211_NODE_UNLOCK(nt);
-
- ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr);
if (ni == NULL) {
/* XXX recovery? */
return;
}
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
- ni->ni_esslen = ic->ic_des_ssid[0].len;
- memcpy(ni->ni_essid, ic->ic_des_ssid[0].ssid, ni->ni_esslen);
- if (ic->ic_bss != NULL)
- copy_bss(ni, ic->ic_bss);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr);
+ ni->ni_esslen = vap->iv_des_ssid[0].len;
+ memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen);
+ if (vap->iv_bss != NULL)
+ copy_bss(ni, vap->iv_bss);
ni->ni_intval = ic->ic_bintval;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
if (ic->ic_phytype == IEEE80211_T_FH) {
ni->ni_fhdwell = 200; /* XXX */
ni->ni_fhindex = 1;
}
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- ic->ic_flags |= IEEE80211_F_SIBSS;
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ vap->iv_flags |= IEEE80211_F_SIBSS;
ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS; /* XXX */
- if (ic->ic_flags & IEEE80211_F_DESBSSID)
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
+ if (vap->iv_flags & IEEE80211_F_DESBSSID)
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid);
else {
get_random_bytes(ni->ni_bssid, IEEE80211_ADDR_LEN);
/* clear group bit, add local bit */
ni->ni_bssid[0] = (ni->ni_bssid[0] &~ 0x01) | 0x02;
}
- } else if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
- if (ic->ic_flags & IEEE80211_F_DESBSSID)
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
+ } else if (vap->iv_opmode == IEEE80211_M_AHDEMO) {
+ if (vap->iv_flags & IEEE80211_F_DESBSSID)
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid);
else
memset(ni->ni_bssid, 0, IEEE80211_ADDR_LEN);
}
/*
* Fix the channel and related attributes.
*/
+ /* clear DFS CAC state on previous channel */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ ic->ic_bsschan->ic_freq != chan->ic_freq &&
+ IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan))
+ ieee80211_dfs_cac_clear(ic, ic->ic_bsschan);
ic->ic_bsschan = chan;
- ieee80211_node_set_chan(ic, ni);
+ ieee80211_node_set_chan(ni, chan);
ic->ic_curmode = ieee80211_chan2mode(chan);
/*
- * Do mode-specific rate setup.
+ * Do mode-specific setup.
*/
if (IEEE80211_IS_CHAN_FULL(chan)) {
if (IEEE80211_IS_CHAN_ANYG(chan)) {
/*
- * Use a mixed 11b/11g rate set.
+ * Use a mixed 11b/11g basic rate set.
*/
- ieee80211_set11gbasicrates(&ni->ni_rates,
- IEEE80211_MODE_11G);
+ ieee80211_setbasicrates(&ni->ni_rates,
+ IEEE80211_MODE_11G);
+ if (vap->iv_flags & IEEE80211_F_PUREG) {
+ /*
+ * Also mark OFDM rates basic so 11b
+ * stations do not join (WiFi compliance).
+ */
+ ieee80211_addbasicrates(&ni->ni_rates,
+ IEEE80211_MODE_11A);
+ }
} else if (IEEE80211_IS_CHAN_B(chan)) {
/*
* Force pure 11b rate set.
*/
- ieee80211_set11gbasicrates(&ni->ni_rates,
+ ieee80211_setbasicrates(&ni->ni_rates,
IEEE80211_MODE_11B);
}
}
@@ -362,23 +329,25 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
* etc. state).
*/
void
-ieee80211_reset_bss(struct ieee80211com *ic)
+ieee80211_reset_bss(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni, *obss;
- callout_drain(&ic->ic_inact);
- ieee80211_node_table_reset(&ic->ic_sta);
+ ieee80211_node_table_reset(&ic->ic_sta, vap);
+ /* XXX multi-bss: wrong */
ieee80211_reset_erp(ic);
- ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr);
KASSERT(ni != NULL, ("unable to setup inital BSS node"));
- obss = ic->ic_bss;
- ic->ic_bss = ieee80211_ref_node(ni);
+ obss = vap->iv_bss;
+ vap->iv_bss = ieee80211_ref_node(ni);
if (obss != NULL) {
copy_bss(ni, obss);
ni->ni_intval = ic->ic_bintval;
ieee80211_free_node(obss);
- }
+ } else
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr);
}
static int
@@ -399,20 +368,21 @@ match_ssid(const struct ieee80211_node *ni,
* Test a node for suitability/compatibility.
*/
static int
-check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
+check_bss(struct ieee80211vap *vap, struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
uint8_t rate;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
return 0;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
return 0;
} else {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
return 0;
}
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
return 0;
} else {
@@ -424,11 +394,11 @@ check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
if (rate & IEEE80211_RATE_BASIC)
return 0;
- if (ic->ic_des_nssid != 0 &&
- !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid))
+ if (vap->iv_des_nssid != 0 &&
+ !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid))
return 0;
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+ if ((vap->iv_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid))
return 0;
return 1;
}
@@ -438,22 +408,23 @@ check_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
* Display node suitability/compatibility.
*/
static void
-check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
+check_bss_debug(struct ieee80211vap *vap, struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
uint8_t rate;
int fail;
fail = 0;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
fail |= 0x01;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
fail |= 0x02;
} else {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
fail |= 0x02;
}
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
fail |= 0x04;
} else {
@@ -465,18 +436,17 @@ check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
IEEE80211_F_JOIN | IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
if (rate & IEEE80211_RATE_BASIC)
fail |= 0x08;
- if (ic->ic_des_nssid != 0 &&
- !match_ssid(ni, ic->ic_des_nssid, ic->ic_des_ssid))
+ if (vap->iv_des_nssid != 0 &&
+ !match_ssid(ni, vap->iv_des_nssid, vap->iv_des_ssid))
fail |= 0x10;
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
+ if ((vap->iv_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(vap->iv_des_bssid, ni->ni_bssid))
fail |= 0x20;
printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr));
printf(" %s%c", ether_sprintf(ni->ni_bssid), fail & 0x20 ? '!' : ' ');
printf(" %3d%c",
ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' ');
- printf(" %+4d", ni->ni_rssi);
printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
fail & 0x08 ? '!' : ' ');
printf(" %4s%c",
@@ -507,25 +477,28 @@ check_bss_debug(struct ieee80211com *ic, struct ieee80211_node *ni)
int
ieee80211_ibss_merge(struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
+#ifdef IEEE80211_DEBUG
struct ieee80211com *ic = ni->ni_ic;
+#endif
- if (ni == ic->ic_bss ||
- IEEE80211_ADDR_EQ(ni->ni_bssid, ic->ic_bss->ni_bssid)) {
+ 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(ic, ni)) {
+ if (!check_bss(vap, ni)) {
/* capabilities mismatch */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
"%s: merge failed, capabilities mismatch\n", __func__);
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_assoc(ic))
- check_bss_debug(ic, ni);
+ if (ieee80211_msg_assoc(vap))
+ check_bss_debug(vap, ni);
#endif
- ic->ic_stats.is_ibss_capmismatch++;
+ vap->iv_stats.is_ibss_capmismatch++;
return 0;
}
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
"%s: new bssid %s: %s preamble, %s slot time%s\n", __func__,
ether_sprintf(ni->ni_bssid),
ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
@@ -536,13 +509,72 @@ ieee80211_ibss_merge(struct ieee80211_node *ni)
}
/*
- * Change the bss channel.
+ * Calculate HT channel promotion flags for all vaps.
+ * This assumes ni_chan have been setup for each vap.
+ */
+static int
+gethtadjustflags(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+ int flags;
+
+ flags = 0;
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap->iv_state < IEEE80211_S_RUN)
+ continue;
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_WDS:
+ case IEEE80211_M_STA:
+ case IEEE80211_M_AHDEMO:
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_IBSS:
+ flags |= ieee80211_htchanflags(vap->iv_bss->ni_chan);
+ break;
+ default:
+ break;
+ }
+ }
+ return flags;
+}
+
+/*
+ * Check if the current channel needs to change based on whether
+ * any vap's are using HT20/HT40. This is used sync the state of
+ * ic_curchan after a channel width change on a running vap.
*/
void
-ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c)
+ieee80211_sync_curchan(struct ieee80211com *ic)
{
- ic->ic_bsschan = c;
- ic->ic_curchan = ic->ic_bsschan;
+ struct ieee80211_channel *c;
+
+ c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, gethtadjustflags(ic));
+ if (c != ic->ic_curchan) {
+ ic->ic_curchan = c;
+ ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
+ ic->ic_set_channel(ic);
+ }
+}
+
+/*
+ * Change the current channel. The request channel may be
+ * promoted if other vap's are operating with HT20/HT40.
+ */
+void
+ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
+{
+ if (ic->ic_htcaps & IEEE80211_HTC_HT) {
+ int flags = gethtadjustflags(ic);
+ /*
+ * Check for channel promotion required to support the
+ * set of running vap's. This assumes we are called
+ * after ni_chan is setup for each vap.
+ */
+ /* NB: this assumes IEEE80211_FEXT_USEHT40 > IEEE80211_FEXT_HT */
+ if (flags > ieee80211_htchanflags(c))
+ c = ieee80211_ht_adjust_channel(ic, c, flags);
+ }
+ ic->ic_bsschan = ic->ic_curchan = c;
ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
ic->ic_set_channel(ic);
}
@@ -554,61 +586,49 @@ ieee80211_setbsschan(struct ieee80211com *ic, struct ieee80211_channel *c)
static int
ieee80211_sta_join1(struct ieee80211_node *selbs)
{
+ struct ieee80211vap *vap = selbs->ni_vap;
struct ieee80211com *ic = selbs->ni_ic;
struct ieee80211_node *obss;
int canreassoc;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- struct ieee80211_node_table *nt;
- /*
- * Fillin the neighbor table; it will already
- * exist if we are simply switching mastership.
- * XXX ic_sta always setup so this is unnecessary?
- */
- nt = &ic->ic_sta;
- IEEE80211_NODE_LOCK(nt);
- nt->nt_name = "neighbor";
- nt->nt_inact_init = ic->ic_inact_run;
- IEEE80211_NODE_UNLOCK(nt);
- }
-
/*
* Committed to selbs, setup state.
*/
- obss = ic->ic_bss;
+ obss = vap->iv_bss;
/*
* Check if old+new node have the same address in which
* case we can reassociate when operating in sta mode.
*/
canreassoc = (obss != NULL &&
- ic->ic_state == IEEE80211_S_RUN &&
+ vap->iv_state == IEEE80211_S_RUN &&
IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr));
- ic->ic_bss = selbs; /* NB: caller assumed to bump refcnt */
+ vap->iv_bss = selbs; /* NB: caller assumed to bump refcnt */
if (obss != NULL) {
copy_bss(selbs, obss);
- ieee80211_free_node(obss);
+ ieee80211_node_reclaim(obss);
+ obss = NULL; /* NB: guard against later use */
}
/*
* Delete unusable rates; we've already checked
* that the negotiated rate set is acceptable.
*/
- ieee80211_fix_rate(ic->ic_bss, &ic->ic_bss->ni_rates,
+ ieee80211_fix_rate(vap->iv_bss, &vap->iv_bss->ni_rates,
IEEE80211_F_DODEL | IEEE80211_F_JOIN);
- ieee80211_setbsschan(ic, selbs->ni_chan);
+ ieee80211_setcurchan(ic, selbs->ni_chan);
/*
* Set the erp state (mostly the slot time) to deal with
* the auto-select case; this should be redundant if the
* mode is locked.
*/
ieee80211_reset_erp(ic);
- ieee80211_wme_initparams(ic);
+ ieee80211_wme_initparams(vap);
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
if (canreassoc) {
/* Reassociate */
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1);
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
} else {
/*
* Act as if we received a DEAUTH frame in case we
@@ -616,21 +636,22 @@ ieee80211_sta_join1(struct ieee80211_node *selbs)
* us to try to re-authenticate if we are operating
* as a station.
*/
- ieee80211_new_state(ic, IEEE80211_S_AUTH,
+ ieee80211_new_state(vap, IEEE80211_S_AUTH,
IEEE80211_FC0_SUBTYPE_DEAUTH);
}
} else
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
return 1;
}
int
-ieee80211_sta_join(struct ieee80211com *ic,
+ieee80211_sta_join(struct ieee80211vap *vap,
const struct ieee80211_scan_entry *se)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
- ni = ieee80211_alloc_node(&ic->ic_sta, se->se_macaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, se->se_macaddr);
if (ni == NULL) {
/* XXX msg */
return 0;
@@ -651,23 +672,21 @@ ieee80211_sta_join(struct ieee80211com *ic,
ni->ni_fhdwell = se->se_fhdwell;
ni->ni_fhindex = se->se_fhindex;
ni->ni_erp = se->se_erp;
- ni->ni_rssi = se->se_rssi;
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, se->se_rssi);
ni->ni_noise = se->se_noise;
- if (se->se_htcap_ie != NULL) {
- ieee80211_saveie(&ni->ni_htcap_ie, se->se_htcap_ie);
- ieee80211_parse_htcap(ni, ni->ni_htcap_ie);
+
+ if (ieee80211_ies_init(&ni->ni_ies, se->se_ies.data, se->se_ies.len)) {
+ ieee80211_ies_expand(&ni->ni_ies);
+ if (ni->ni_ies.ath_ie != NULL)
+ ieee80211_parse_ath(ni, ni->ni_ies.ath_ie);
+ 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 (se->se_wpa_ie != NULL)
- ieee80211_saveie(&ni->ni_wpa_ie, se->se_wpa_ie);
- if (se->se_rsn_ie != NULL)
- ieee80211_saveie(&ni->ni_rsn_ie, se->se_rsn_ie);
- if (se->se_wme_ie != NULL)
- ieee80211_saveie(&ni->ni_wme_ie, se->se_wme_ie);
- if (se->se_ath_ie != NULL)
- ieee80211_saveath(ni, se->se_ath_ie);
-
- ic->ic_dtim_period = se->se_dtimperiod;
- ic->ic_dtim_count = 0;
+
+ vap->iv_dtim_period = se->se_dtimperiod;
+ vap->iv_dtim_count = 0;
/* NB: must be after ni_chan is setup */
ieee80211_setup_rates(ni, se->se_rates, se->se_xrates,
@@ -681,10 +700,26 @@ ieee80211_sta_join(struct ieee80211com *ic,
* be passed in with a held reference.
*/
void
-ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_sta_leave(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
+
ic->ic_node_cleanup(ni);
- ieee80211_notify_node_leave(ic, ni);
+ ieee80211_notify_node_leave(ni);
+}
+
+/*
+ * Send a deauthenticate frame and drop the station.
+ */
+void
+ieee80211_node_deauth(struct ieee80211_node *ni, int reason)
+{
+ /* NB: bump the refcnt to be sure temporay nodes are not reclaimed */
+ ieee80211_ref_node(ni);
+ if (ni->ni_associd != 0)
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
+ ieee80211_node_leave(ni);
+ ieee80211_free_node(ni);
}
static struct ieee80211_node *
@@ -698,6 +733,80 @@ node_alloc(struct ieee80211_node_table *nt)
}
/*
+ * Initialize an ie blob with the specified data. If previous
+ * data exists re-use the data block. As a side effect we clear
+ * all references to specific ie's; the caller is required to
+ * recalculate them.
+ */
+int
+ieee80211_ies_init(struct ieee80211_ies *ies, const uint8_t *data, int len)
+{
+ /* NB: assumes data+len are the last fields */
+ memset(ies, 0, offsetof(struct ieee80211_ies, data));
+ if (ies->data != NULL && ies->len != len) {
+ /* data size changed */
+ FREE(ies->data, M_80211_NODE_IE);
+ ies->data = NULL;
+ }
+ if (ies->data == NULL) {
+ MALLOC(ies->data, uint8_t *, len, M_80211_NODE_IE, M_NOWAIT);
+ if (ies->data == NULL) {
+ ies->len = 0;
+ /* NB: pointers have already been zero'd above */
+ return 0;
+ }
+ }
+ memcpy(ies->data, data, len);
+ ies->len = len;
+ return 1;
+}
+
+/*
+ * Reclaim storage for an ie blob.
+ */
+void
+ieee80211_ies_cleanup(struct ieee80211_ies *ies)
+{
+ if (ies->data != NULL)
+ FREE(ies->data, M_80211_NODE_IE);
+}
+
+/*
+ * Expand an ie blob data contents and to fillin individual
+ * ie pointers. The data blob is assumed to be well-formed;
+ * we don't do any validity checking of ie lengths.
+ */
+void
+ieee80211_ies_expand(struct ieee80211_ies *ies)
+{
+ uint8_t *ie;
+ int ielen;
+
+ ie = ies->data;
+ ielen = ies->len;
+ while (ielen > 0) {
+ switch (ie[0]) {
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(ie))
+ ies->wpa_ie = ie;
+ else if (iswmeoui(ie))
+ ies->wme_ie = ie;
+ else if (isatherosoui(ie))
+ ies->ath_ie = ie;
+ break;
+ case IEEE80211_ELEMID_RSN:
+ ies->rsn_ie = ie;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ ies->htcap_ie = ie;
+ break;
+ }
+ ielen -= 2 + ie[1];
+ ie += 2 + ie[1];
+ }
+}
+
+/*
* Reclaim any resources in a node and reset any critical
* state. Typically nodes are free'd immediately after,
* but in some cases the storage may be reused so we need
@@ -707,17 +816,16 @@ static void
node_cleanup(struct ieee80211_node *ni)
{
#define N(a) (sizeof(a)/sizeof(a[0]))
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
int i;
/* NB: preserve ni_table */
if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
- if (ic->ic_opmode != IEEE80211_M_STA)
- ic->ic_ps_sta--;
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ vap->iv_ps_sta--;
ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] power save mode off, %u sta's in ps mode\n",
- ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "power save mode off, %u sta's in ps mode", vap->iv_ps_sta);
}
/*
* Cleanup any HT-related state.
@@ -735,8 +843,8 @@ node_cleanup(struct ieee80211_node *ni)
/*
* Drain power save queue and, if needed, clear TIM.
*/
- if (ieee80211_node_saveq_drain(ni) != 0 && ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
+ if (ieee80211_node_saveq_drain(ni) != 0 && vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
ni->ni_associd = 0;
if (ni->ni_challenge != NULL) {
@@ -773,39 +881,80 @@ node_free(struct ieee80211_node *ni)
struct ieee80211com *ic = ni->ni_ic;
ic->ic_node_cleanup(ni);
- if (ni->ni_wpa_ie != NULL)
- FREE(ni->ni_wpa_ie, M_80211_NODE);
- if (ni->ni_rsn_ie != NULL)
- FREE(ni->ni_rsn_ie, M_80211_NODE);
- if (ni->ni_wme_ie != NULL)
- FREE(ni->ni_wme_ie, M_80211_NODE);
- if (ni->ni_ath_ie != NULL)
- FREE(ni->ni_ath_ie, M_80211_NODE);
+ ieee80211_ies_cleanup(&ni->ni_ies);
IEEE80211_NODE_SAVEQ_DESTROY(ni);
+ IEEE80211_NODE_WDSQ_DESTROY(ni);
FREE(ni, M_80211_NODE);
}
+static void
+node_age(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+#if 0
+ IEEE80211_NODE_LOCK_ASSERT(&ic->ic_sta);
+#endif
+ /*
+ * Age frames on the power save queue.
+ */
+ if (ieee80211_node_saveq_age(ni) != 0 &&
+ IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 &&
+ vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
+ /*
+ * Age frames on the wds pending queue.
+ */
+ if (IEEE80211_NODE_WDSQ_QLEN(ni) != 0)
+ ieee80211_node_wdsq_age(ni);
+ /*
+ * Age out HT resources (e.g. frames on the
+ * A-MPDU reorder queues).
+ */
+ if (ni->ni_associd != 0 && (ni->ni_flags & IEEE80211_NODE_HT))
+ ieee80211_ht_node_age(ni);
+}
+
static int8_t
node_getrssi(const struct ieee80211_node *ni)
{
- return ni->ni_rssi;
+ uint32_t avgrssi = ni->ni_avgrssi;
+ int32_t rssi;
+
+ if (avgrssi == IEEE80211_RSSI_DUMMY_MARKER)
+ return 0;
+ rssi = IEEE80211_RSSI_GET(avgrssi);
+ return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
}
static void
node_getsignal(const struct ieee80211_node *ni, int8_t *rssi, int8_t *noise)
{
- *rssi = ni->ni_rssi;
+ *rssi = node_getrssi(ni);
*noise = ni->ni_noise;
}
static void
-ieee80211_setup_node(struct ieee80211_node_table *nt,
- struct ieee80211_node *ni, const uint8_t *macaddr)
+node_getmimoinfo(const struct ieee80211_node *ni,
+ struct ieee80211_mimo_info *info)
+{
+ /* XXX zero data? */
+}
+
+struct ieee80211_node *
+ieee80211_alloc_node(struct ieee80211_node_table *nt,
+ struct ieee80211vap *vap, const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node *ni;
int hash;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ ni = ic->ic_node_alloc(nt);
+ if (ni == NULL) {
+ vap->iv_stats.is_rx_nodealloc++;
+ return NULL;
+ }
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s %p<%s> in %s table\n", __func__, ni,
ether_sprintf(macaddr), nt->nt_name);
@@ -815,31 +964,22 @@ ieee80211_setup_node(struct ieee80211_node_table *nt,
ni->ni_chan = IEEE80211_CHAN_ANYC;
ni->ni_authmode = IEEE80211_AUTH_OPEN;
ni->ni_txpower = ic->ic_txpowlimit; /* max power */
- ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+ ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+ ni->ni_avgrssi = IEEE80211_RSSI_DUMMY_MARKER;
ni->ni_inact_reload = nt->nt_inact_init;
ni->ni_inact = ni->ni_inact_reload;
ni->ni_ath_defkeyix = 0x7fff;
IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
+ IEEE80211_NODE_WDSQ_INIT(ni, "unknown");
IEEE80211_NODE_LOCK(nt);
TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list);
LIST_INSERT_HEAD(&nt->nt_hash[hash], ni, ni_hash);
ni->ni_table = nt;
+ ni->ni_vap = vap;
ni->ni_ic = ic;
IEEE80211_NODE_UNLOCK(nt);
-}
-
-struct ieee80211_node *
-ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
-{
- struct ieee80211com *ic = nt->nt_ic;
- struct ieee80211_node *ni;
- ni = ic->ic_node_alloc(nt);
- if (ni != NULL)
- ieee80211_setup_node(nt, ni, macaddr);
- else
- ic->ic_stats.is_rx_nodealloc++;
return ni;
}
@@ -850,65 +990,129 @@ ieee80211_alloc_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
* once the send completes.
*/
struct ieee80211_node *
-ieee80211_tmp_node(struct ieee80211com *ic, const uint8_t *macaddr)
+ieee80211_tmp_node(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
ni = ic->ic_node_alloc(&ic->ic_sta);
if (ni != NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s %p<%s>\n", __func__, ni, ether_sprintf(macaddr));
+ ni->ni_table = NULL; /* NB: pedantic */
+ ni->ni_ic = ic; /* NB: needed to set channel */
+ ni->ni_vap = vap;
+
IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_bss->ni_bssid);
ieee80211_node_initref(ni); /* mark referenced */
- ni->ni_txpower = ic->ic_bss->ni_txpower;
/* NB: required by ieee80211_fix_rate */
- ieee80211_node_set_chan(ic, ni);
- ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey,
+ ieee80211_node_set_chan(ni, vap->iv_bss->ni_chan);
+ ni->ni_txpower = vap->iv_bss->ni_txpower;
+ ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey,
IEEE80211_KEYIX_NONE);
/* XXX optimize away */
IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
-
- ni->ni_table = NULL; /* NB: pedantic */
- ni->ni_ic = ic;
+ IEEE80211_NODE_WDSQ_INIT(ni, "unknown");
} else {
/* XXX msg */
- ic->ic_stats.is_rx_nodealloc++;
+ vap->iv_stats.is_rx_nodealloc++;
}
return ni;
}
struct ieee80211_node *
-ieee80211_dup_bss(struct ieee80211_node_table *nt, const uint8_t *macaddr)
+ieee80211_dup_bss(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
- struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_node *ni;
- ni = ic->ic_node_alloc(nt);
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, macaddr);
if (ni != NULL) {
- ieee80211_setup_node(nt, ni, macaddr);
/*
- * Inherit from ic_bss.
+ * Inherit from iv_bss.
*/
- ni->ni_authmode = ic->ic_bss->ni_authmode;
- ni->ni_txpower = ic->ic_bss->ni_txpower;
- ni->ni_vlan = ic->ic_bss->ni_vlan; /* XXX?? */
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
- ieee80211_node_set_chan(ic, ni);
- ni->ni_rsn = ic->ic_bss->ni_rsn;
- } else
- ic->ic_stats.is_rx_nodealloc++;
+ ni->ni_authmode = vap->iv_bss->ni_authmode;
+ ni->ni_txpower = vap->iv_bss->ni_txpower;
+ ni->ni_vlan = vap->iv_bss->ni_vlan; /* XXX?? */
+ IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_bss->ni_bssid);
+ ieee80211_node_set_chan(ni, vap->iv_bss->ni_chan);
+ }
return ni;
}
-static struct ieee80211_node *
+/*
+ * Create a bss node for a legacy WDS vap. The far end does
+ * not associate so we just create create a new node and
+ * simulate an association. The caller is responsible for
+ * installing the node as the bss node and handling any further
+ * setup work like authorizing the port.
+ */
+struct ieee80211_node *
+ieee80211_node_create_wds(struct ieee80211vap *vap,
+ const uint8_t bssid[IEEE80211_ADDR_LEN], struct ieee80211_channel *chan)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+
+ /* XXX check if node already in sta table? */
+ ni = ieee80211_alloc_node(&ic->ic_sta, vap, bssid);
+ if (ni != NULL) {
+ ni->ni_wdsvap = vap;
+ IEEE80211_ADDR_COPY(ni->ni_bssid, bssid);
+ /*
+ * Inherit any manually configured settings.
+ */
+ ni->ni_authmode = vap->iv_bss->ni_authmode;
+ ni->ni_txpower = vap->iv_bss->ni_txpower;
+ ni->ni_vlan = vap->iv_bss->ni_vlan;
+ ieee80211_node_set_chan(ni, chan);
+ /* NB: propagate ssid so available to WPA supplicant */
+ ni->ni_esslen = vap->iv_des_ssid[0].len;
+ memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen);
+ /* NB: no associd for peer */
+ /*
+ * There are no management frames to use to
+ * discover neighbor capabilities, so blindly
+ * propagate the local configuration.
+ */
+ if (vap->iv_flags & IEEE80211_F_WME)
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ if (vap->iv_flags & IEEE80211_F_FF)
+ ni->ni_flags |= IEEE80211_NODE_FF;
+ if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HT)) {
+ /*
+ * Device is HT-capable and HT is enabled for
+ * the vap; setup HT operation. On return
+ * ni_chan will be adjusted to an HT channel.
+ */
+ ieee80211_ht_wds_init(ni);
+ } else {
+ struct ieee80211_channel *c = ni->ni_chan;
+ /*
+ * Force a legacy channel to be used.
+ */
+ c = ieee80211_find_channel(ic,
+ c->ic_freq, c->ic_flags &~ IEEE80211_CHAN_HT);
+ KASSERT(c != NULL, ("no legacy channel, %u/%x",
+ ni->ni_chan->ic_freq, ni->ni_chan->ic_flags));
+ ni->ni_chan = c;
+ }
+ }
+ return ni;
+}
+
+struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-_ieee80211_find_node_debug(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, const char *func, int line)
+ieee80211_find_node_locked_debug(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
#else
-_ieee80211_find_node(struct ieee80211_node_table *nt,
- const uint8_t *macaddr)
+ieee80211_find_node_locked(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
{
struct ieee80211_node *ni;
@@ -921,7 +1125,7 @@ _ieee80211_find_node(struct ieee80211_node_table *nt,
if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
ieee80211_ref_node(ni); /* mark referenced */
#ifdef IEEE80211_DEBUG_REFCNT
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s (%s:%u) %p<%s> refcnt %d\n", __func__,
func, line,
ni, ether_sprintf(ni->ni_macaddr),
@@ -932,23 +1136,73 @@ _ieee80211_find_node(struct ieee80211_node_table *nt,
}
return NULL;
}
+
+struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-#define _ieee80211_find_node(nt, mac) \
- _ieee80211_find_node_debug(nt, mac, func, line)
+ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
+#else
+ieee80211_find_node(struct ieee80211_node_table *nt,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
+{
+ struct ieee80211_node *ni;
+
+ IEEE80211_NODE_LOCK(nt);
+ ni = ieee80211_find_node_locked(nt, macaddr);
+ IEEE80211_NODE_UNLOCK(nt);
+ return ni;
+}
struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_node_debug(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, const char *func, int line)
+ieee80211_find_vap_node_locked_debug(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
#else
-ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
+ieee80211_find_vap_node_locked(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
+#endif
+{
+ struct ieee80211_node *ni;
+ int hash;
+
+ IEEE80211_NODE_LOCK_ASSERT(nt);
+
+ hash = IEEE80211_NODE_HASH(macaddr);
+ LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
+ if (ni->ni_vap == vap &&
+ IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
+ ieee80211_ref_node(ni); /* mark referenced */
+#ifdef IEEE80211_DEBUG_REFCNT
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
+ "%s (%s:%u) %p<%s> refcnt %d\n", __func__,
+ func, line,
+ ni, ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni));
+#endif
+ return ni;
+ }
+ }
+ return NULL;
+}
+
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_vap_node_debug(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN], const char *func, int line)
+#else
+ieee80211_find_vap_node(struct ieee80211_node_table *nt,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
{
struct ieee80211_node *ni;
IEEE80211_NODE_LOCK(nt);
- ni = _ieee80211_find_node(nt, macaddr);
+ ni = ieee80211_find_vap_node_locked(nt, vap, macaddr);
IEEE80211_NODE_UNLOCK(nt);
return ni;
}
@@ -960,21 +1214,20 @@ ieee80211_find_node(struct ieee80211_node_table *nt, const uint8_t *macaddr)
* it's private state.
*/
struct ieee80211_node *
-ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
+ieee80211_fakeup_adhoc_node(struct ieee80211vap *vap,
const uint8_t macaddr[IEEE80211_ADDR_LEN])
{
- struct ieee80211com *ic = nt->nt_ic;
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: mac<%s>\n", __func__, ether_sprintf(macaddr));
- ni = ieee80211_dup_bss(nt, macaddr);
+ ni = ieee80211_dup_bss(vap, macaddr);
if (ni != NULL) {
+ struct ieee80211com *ic = vap->iv_ic;
+
/* XXX no rate negotiation; just dup */
- ni->ni_rates = ic->ic_bss->ni_rates;
- if (ic->ic_newassoc != NULL)
- ic->ic_newassoc(ni, 1);
- if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
+ ni->ni_rates = vap->iv_bss->ni_rates;
+ if (vap->iv_opmode == IEEE80211_M_AHDEMO) {
/*
* In adhoc demo mode there are no management
* frames to use to discover neighbor capabilities,
@@ -982,11 +1235,13 @@ ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
* so we can do interesting things (e.g. use
* WME to disable ACK's).
*/
- if (ic->ic_flags & IEEE80211_F_WME)
+ if (vap->iv_flags & IEEE80211_F_WME)
ni->ni_flags |= IEEE80211_NODE_QOS;
- if (ic->ic_flags & IEEE80211_F_FF)
+ if (vap->iv_flags & IEEE80211_F_FF)
ni->ni_flags |= IEEE80211_NODE_FF;
}
+ if (ic->ic_newassoc != NULL)
+ ic->ic_newassoc(ni, 1);
/* XXX not right for 802.1x/WPA */
ieee80211_node_authorize(ni);
}
@@ -1009,14 +1264,12 @@ ieee80211_init_neighbor(struct ieee80211_node *ni,
ni->ni_fhindex = sp->fhindex;
ni->ni_erp = sp->erp;
ni->ni_timoff = sp->timoff;
- if (sp->wme != NULL)
- ieee80211_saveie(&ni->ni_wme_ie, sp->wme);
- if (sp->wpa != NULL)
- ieee80211_saveie(&ni->ni_wpa_ie, sp->wpa);
- if (sp->rsn != NULL)
- ieee80211_saveie(&ni->ni_rsn_ie, sp->rsn);
- if (sp->ath != NULL)
- ieee80211_saveath(ni, sp->ath);
+
+ if (ieee80211_ies_init(&ni->ni_ies, sp->ies, sp->ies_len)) {
+ ieee80211_ies_expand(&ni->ni_ies);
+ if (ni->ni_ies.ath_ie != NULL)
+ ieee80211_parse_ath(ni, ni->ni_ies.ath_ie);
+ }
/* NB: must be after ni_chan is setup */
ieee80211_setup_rates(ni, sp->rates, sp->xrates,
@@ -1031,16 +1284,18 @@ ieee80211_init_neighbor(struct ieee80211_node *ni,
* driver has an opportunity to setup it's private state.
*/
struct ieee80211_node *
-ieee80211_add_neighbor(struct ieee80211com *ic,
+ieee80211_add_neighbor(struct ieee80211vap *vap,
const struct ieee80211_frame *wh,
const struct ieee80211_scanparams *sp)
{
struct ieee80211_node *ni;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: mac<%s>\n", __func__, ether_sprintf(wh->i_addr2));
- ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);/* XXX alloc_node? */
+ ni = ieee80211_dup_bss(vap, wh->i_addr2);/* XXX alloc_node? */
if (ni != NULL) {
+ struct ieee80211com *ic = vap->iv_ic;
+
ieee80211_init_neighbor(ni, wh, sp);
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, 1);
@@ -1077,16 +1332,13 @@ ieee80211_find_rxnode(struct ieee80211com *ic,
struct ieee80211_node_table *nt;
struct ieee80211_node *ni;
- /* XXX check ic_bss first in station mode */
/* XXX 4-address frames? */
nt = &ic->ic_sta;
IEEE80211_NODE_LOCK(nt);
if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/)
- ni = _ieee80211_find_node(nt, wh->i_addr1);
+ ni = ieee80211_find_node_locked(nt, wh->i_addr1);
else
- ni = _ieee80211_find_node(nt, wh->i_addr2);
- if (ni == NULL)
- ni = ieee80211_ref_node(ic->ic_bss);
+ ni = ieee80211_find_node_locked(nt, wh->i_addr2);
IEEE80211_NODE_UNLOCK(nt);
return ni;
@@ -1121,12 +1373,10 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
ni = NULL;
if (ni == NULL) {
if (IS_CTL(wh) && !IS_PSPOLL(wh) && !IS_BAR(wh) /*&& !IS_RTS(ah)*/)
- ni = _ieee80211_find_node(nt, wh->i_addr1);
+ ni = ieee80211_find_node_locked(nt, wh->i_addr1);
else
- ni = _ieee80211_find_node(nt, wh->i_addr2);
- if (ni == NULL)
- ni = ieee80211_ref_node(ic->ic_bss);
- if (nt->nt_keyixmap != NULL) {
+ ni = ieee80211_find_node_locked(nt, wh->i_addr2);
+ if (ni != NULL && nt->nt_keyixmap != NULL) {
/*
* If the station has a unicast key cache slot
* assigned update the key->node mapping table.
@@ -1135,7 +1385,8 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
/* XXX can keyixmap[keyix] != NULL? */
if (keyix < nt->nt_keyixmax &&
nt->nt_keyixmap[keyix] == NULL) {
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_NODE,
"%s: add key map entry %p<%s> refcnt %d\n",
__func__, ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni)+1);
@@ -1158,13 +1409,15 @@ ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
*/
struct ieee80211_node *
#ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_txnode_debug(struct ieee80211com *ic, const uint8_t *macaddr,
+ieee80211_find_txnode_debug(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
const char *func, int line)
#else
-ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
+ieee80211_find_txnode(struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN])
#endif
{
- struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node_table *nt = &vap->iv_ic->ic_sta;
struct ieee80211_node *ni;
/*
@@ -1175,11 +1428,13 @@ ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
*/
/* XXX can't hold lock across dup_bss 'cuz of recursive locking */
IEEE80211_NODE_LOCK(nt);
- if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
- ni = ieee80211_ref_node(ic->ic_bss);
+ if (vap->iv_opmode == IEEE80211_M_STA ||
+ vap->iv_opmode == IEEE80211_M_WDS ||
+ IEEE80211_IS_MULTICAST(macaddr))
+ ni = ieee80211_ref_node(vap->iv_bss);
else {
- ni = _ieee80211_find_node(nt, macaddr);
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ ni = ieee80211_find_node_locked(nt, macaddr);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
(ni != NULL && ni->ni_associd == 0)) {
/*
* Station is not associated; don't permit the
@@ -1193,91 +1448,44 @@ ieee80211_find_txnode(struct ieee80211com *ic, const uint8_t *macaddr)
IEEE80211_NODE_UNLOCK(nt);
if (ni == NULL) {
- if (ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO) {
/*
* In adhoc mode cons up a node for the destination.
* Note that we need an additional reference for the
- * caller to be consistent with _ieee80211_find_node.
+ * caller to be consistent with
+ * ieee80211_find_node_locked.
*/
- ni = ieee80211_fakeup_adhoc_node(nt, macaddr);
+ ni = ieee80211_fakeup_adhoc_node(vap, macaddr);
if (ni != NULL)
(void) ieee80211_ref_node(ni);
} else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
- "[%s] no node, discard frame (%s)\n",
- ether_sprintf(macaddr), __func__);
- ic->ic_stats.is_tx_nonode++;
- }
- }
- return ni;
-}
-
-/*
- * Like find but search based on the ssid too.
- */
-struct ieee80211_node *
-#ifdef IEEE80211_DEBUG_REFCNT
-ieee80211_find_node_with_ssid_debug(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid,
- const char *func, int line)
-#else
-ieee80211_find_node_with_ssid(struct ieee80211_node_table *nt,
- const uint8_t *macaddr, u_int ssidlen, const uint8_t *ssid)
-#endif
-{
-#define MATCH_SSID(ni, ssid, ssidlen) \
- (ni->ni_esslen == ssidlen && memcmp(ni->ni_essid, ssid, ssidlen) == 0)
- static const uint8_t zeromac[IEEE80211_ADDR_LEN];
- struct ieee80211_node *ni;
- int hash;
-
- IEEE80211_NODE_LOCK(nt);
- /*
- * A mac address that is all zero means match only the ssid;
- * otherwise we must match both.
- */
- if (IEEE80211_ADDR_EQ(macaddr, zeromac)) {
- TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
- if (MATCH_SSID(ni, ssid, ssidlen))
- break;
- }
- } else {
- hash = IEEE80211_NODE_HASH(macaddr);
- LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
- if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
- MATCH_SSID(ni, ssid, ssidlen))
- break;
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, macaddr,
+ "no node, discard frame (%s)", __func__);
+ vap->iv_stats.is_tx_nonode++;
}
}
- if (ni != NULL) {
- ieee80211_ref_node(ni); /* mark referenced */
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
- REFCNT_LOC, ni, ether_sprintf(ni->ni_macaddr),
- ieee80211_node_refcnt(ni));
- }
- IEEE80211_NODE_UNLOCK(nt);
return ni;
-#undef MATCH_SSID
}
static void
_ieee80211_free_node(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_node_table *nt = ni->ni_table;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s %p<%s> in %s table\n", __func__, ni,
ether_sprintf(ni->ni_macaddr),
nt != NULL ? nt->nt_name : "<gone>");
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ 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);
}
- ic->ic_node_free(ni);
+ vap->iv_ic->ic_node_free(ni);
}
void
@@ -1290,7 +1498,7 @@ ieee80211_free_node(struct ieee80211_node *ni)
struct ieee80211_node_table *nt = ni->ni_table;
#ifdef IEEE80211_DEBUG_REFCNT
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni,
ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1);
#endif
@@ -1310,7 +1518,8 @@ ieee80211_free_node(struct ieee80211_node *ni)
keyix = ni->ni_ucastkey.wk_rxkeyix;
if (keyix < nt->nt_keyixmax &&
nt->nt_keyixmap[keyix] == ni) {
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ 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;
@@ -1331,8 +1540,9 @@ ieee80211_free_node(struct ieee80211_node *ni)
int
ieee80211_node_delucastkey(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
- struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211vap *vap = ni->ni_vap;
+ /* XXX is ni_table safe? */
+ struct ieee80211_node_table *nt = &ni->ni_ic->ic_sta;
struct ieee80211_node *nikey;
ieee80211_keyix keyix;
int isowned, status;
@@ -1353,19 +1563,19 @@ ieee80211_node_delucastkey(struct ieee80211_node *ni)
if (!isowned)
IEEE80211_NODE_LOCK(nt);
keyix = ni->ni_ucastkey.wk_rxkeyix;
- status = ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+ status = ieee80211_crypto_delkey(vap, &ni->ni_ucastkey);
if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) {
nikey = nt->nt_keyixmap[keyix];
nt->nt_keyixmap[keyix] = NULL;;
} else
nikey = NULL;
if (!isowned)
- IEEE80211_NODE_UNLOCK(&ic->ic_sta);
+ IEEE80211_NODE_UNLOCK(nt);
if (nikey != NULL) {
KASSERT(nikey == ni,
("key map out of sync, ni %p nikey %p", ni, nikey));
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: delete key map entry %p<%s> refcnt %d\n",
__func__, ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni)-1);
@@ -1386,7 +1596,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
IEEE80211_NODE_LOCK_ASSERT(nt);
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s: remove %p<%s> from %s table, refcnt %d\n",
__func__, ni, ether_sprintf(ni->ni_macaddr),
nt->nt_name, ieee80211_node_refcnt(ni)-1);
@@ -1400,7 +1610,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
keyix = ni->ni_ucastkey.wk_rxkeyix;
if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax &&
nt->nt_keyixmap[keyix] == ni) {
- IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s: %p<%s> clear key map entry\n",
__func__, ni, ether_sprintf(ni->ni_macaddr));
nt->nt_keyixmap[keyix] = NULL;
@@ -1420,23 +1630,151 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
_ieee80211_free_node(ni);
}
+/*
+ * Reclaim a (bss) node. Decrement the refcnt and reclaim
+ * the node if the only other reference to it is in the sta
+ * table. This is effectively ieee80211_free_node followed
+ * by node_reclaim when the refcnt is 1 (after the free).
+ */
static void
-ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt)
+ieee80211_node_reclaim(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = nt->nt_ic;
- struct ieee80211_node *ni;
+ struct ieee80211_node_table *nt = ni->ni_table;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
- "%s: free all nodes in %s table\n", __func__, nt->nt_name);
+ KASSERT(nt != NULL, ("reclaim node not in table"));
+
+#ifdef IEEE80211_DEBUG_REFCNT
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
+ "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni,
+ ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1);
+#endif
+ IEEE80211_NODE_LOCK(nt);
+ if (ieee80211_node_dectestref(ni)) {
+ /*
+ * Last reference, reclaim state.
+ */
+ _ieee80211_free_node(ni);
+ nt = NULL;
+ } else if (ieee80211_node_refcnt(ni) == 1 &&
+ nt->nt_keyixmap != NULL) {
+ ieee80211_keyix keyix;
+ /*
+ * Check for a last reference in the key mapping table.
+ */
+ keyix = ni->ni_ucastkey.wk_rxkeyix;
+ if (keyix < nt->nt_keyixmax &&
+ nt->nt_keyixmap[keyix] == ni) {
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_NODE,
+ "%s: %p<%s> clear key map entry", __func__,
+ ni, ether_sprintf(ni->ni_macaddr));
+ nt->nt_keyixmap[keyix] = NULL;
+ ieee80211_node_decref(ni); /* XXX needed? */
+ _ieee80211_free_node(ni);
+ nt = NULL;
+ }
+ }
+ if (nt != NULL && ieee80211_node_refcnt(ni) == 1) {
+ /*
+ * Last reference is in the sta table; complete
+ * the reclaim. This handles bss nodes being
+ * recycled: the node has two references, one for
+ * iv_bss and one for the table. After dropping
+ * the iv_bss ref above we need to reclaim the sta
+ * table reference.
+ */
+ ieee80211_node_decref(ni); /* NB: be pendantic */
+ _ieee80211_free_node(ni);
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+}
- while ((ni = TAILQ_FIRST(&nt->nt_node)) != NULL) {
+/*
+ * Node table support.
+ */
+
+static void
+ieee80211_node_table_init(struct ieee80211com *ic,
+ struct ieee80211_node_table *nt,
+ const char *name, int inact, int keyixmax)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+
+ nt->nt_ic = ic;
+ IEEE80211_NODE_LOCK_INIT(nt, ifp->if_xname);
+ IEEE80211_NODE_ITERATE_LOCK_INIT(nt, ifp->if_xname);
+ TAILQ_INIT(&nt->nt_node);
+ nt->nt_name = name;
+ nt->nt_scangen = 1;
+ nt->nt_inact_init = inact;
+ nt->nt_keyixmax = keyixmax;
+ if (nt->nt_keyixmax > 0) {
+ MALLOC(nt->nt_keyixmap, struct ieee80211_node **,
+ keyixmax * sizeof(struct ieee80211_node *),
+ M_80211_NODE, M_NOWAIT | M_ZERO);
+ if (nt->nt_keyixmap == NULL)
+ if_printf(ic->ic_ifp,
+ "Cannot allocate key index map with %u entries\n",
+ keyixmax);
+ } else
+ nt->nt_keyixmap = NULL;
+}
+
+static void
+ieee80211_node_table_reset(struct ieee80211_node_table *nt,
+ struct ieee80211vap *match)
+{
+ struct ieee80211_node *ni, *next;
+
+ IEEE80211_NODE_LOCK(nt);
+ TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next) {
+ if (match != NULL && ni->ni_vap != match)
+ continue;
+ /* XXX can this happen? if so need's work */
if (ni->ni_associd != 0) {
- if (ic->ic_auth->ia_node_leave != NULL)
- ic->ic_auth->ia_node_leave(ic, ni);
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (vap->iv_auth->ia_node_leave != NULL)
+ vap->iv_auth->ia_node_leave(ni);
+ if (vap->iv_aid_bitmap != NULL)
+ IEEE80211_AID_CLR(vap, ni->ni_associd);
}
+ ni->ni_wdsvap = NULL; /* clear reference */
node_reclaim(nt, ni);
}
+ if (match != NULL && match->iv_opmode == IEEE80211_M_WDS) {
+ /*
+ * Make a separate pass to clear references to this vap
+ * held by DWDS entries. They will not be matched above
+ * because ni_vap will point to the ap vap but we still
+ * need to clear ni_wdsvap when the WDS vap is destroyed
+ * and/or reset.
+ */
+ TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next)
+ if (ni->ni_wdsvap == match)
+ ni->ni_wdsvap = NULL;
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+}
+
+static void
+ieee80211_node_table_cleanup(struct ieee80211_node_table *nt)
+{
+ ieee80211_node_table_reset(nt, NULL);
+ if (nt->nt_keyixmap != NULL) {
+#ifdef DIAGNOSTIC
+ /* XXX verify all entries are NULL */
+ int i;
+ for (i = 0; i < nt->nt_keyixmax; i++)
+ if (nt->nt_keyixmap[i] != NULL)
+ printf("%s: %s[%u] still active\n", __func__,
+ nt->nt_name, i);
+#endif
+ FREE(nt->nt_keyixmap, M_80211_NODE);
+ nt->nt_keyixmap = NULL;
+ }
+ IEEE80211_NODE_ITERATE_LOCK_DESTROY(nt);
+ IEEE80211_NODE_LOCK_DESTROY(nt);
}
/*
@@ -1450,16 +1788,14 @@ ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt)
* process each node only once.
*/
static void
-ieee80211_timeout_stations(struct ieee80211_node_table *nt)
+ieee80211_timeout_stations(struct ieee80211com *ic)
{
- struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211vap *vap;
struct ieee80211_node *ni;
- u_int gen;
- int isadhoc;
+ int gen = 0;
- isadhoc = (ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO);
- IEEE80211_SCAN_LOCK(nt);
+ IEEE80211_NODE_ITERATE_LOCK(nt);
gen = ++nt->nt_scangen;
restart:
IEEE80211_NODE_LOCK(nt);
@@ -1473,14 +1809,25 @@ restart:
* will be reclaimed when the last reference to them
* goes away (when frame xmits complete).
*/
- if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_STA) &&
+ vap = ni->ni_vap;
+ /*
+ * Only process stations when in RUN state. This
+ * insures, for example, that we don't timeout an
+ * inactive station during CAC. Note that CSA state
+ * is actually handled in ieee80211_node_timeout as
+ * it applies to more than timeout processing.
+ */
+ if (vap->iv_state != IEEE80211_S_RUN)
+ continue;
+ /* XXX can vap be NULL? */
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_STA) &&
(ni->ni_flags & IEEE80211_NODE_AREF) == 0)
continue;
/*
* Free fragment if not needed anymore
* (last fragment older than 1s).
- * XXX doesn't belong here
+ * XXX doesn't belong here, move to node_age
*/
if (ni->ni_rxfrag[0] != NULL &&
ticks > ni->ni_rxfragstamp + hz) {
@@ -1492,17 +1839,17 @@ restart:
/*
* Special case ourself; we may be idle for extended periods
* of time and regardless reclaiming our state is wrong.
+ * XXX run ic_node_age
*/
- if (ni == ic->ic_bss)
+ if (ni == vap->iv_bss)
continue;
- if (ni->ni_associd != 0 || isadhoc) {
+ if (ni->ni_associd != 0 ||
+ (vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO)) {
/*
- * Age frames on the power save queue.
+ * Age/drain resources held by the station.
*/
- if (ieee80211_node_saveq_age(ni) != 0 &&
- IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 &&
- ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
+ ic->ic_node_age(ni);
/*
* Probe the station before time it out. We
* send a null data frame which may not be
@@ -1515,11 +1862,11 @@ restart:
* of); this will get fixed more properly
* soon with better handling of the rate set.
*/
- if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) &&
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) &&
(0 < ni->ni_inact &&
- ni->ni_inact <= ic->ic_inact_probe) &&
+ ni->ni_inact <= vap->iv_inact_probe) &&
ni->ni_rates.rs_nrates != 0) {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_INACT | IEEE80211_MSG_NODE,
ni, "%s",
"probe station due to inactivity");
@@ -1537,9 +1884,9 @@ restart:
goto restart;
}
}
- if ((ic->ic_flags_ext & IEEE80211_FEXT_INACT) &&
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_INACT) &&
ni->ni_inact <= 0) {
- IEEE80211_NOTE(ic,
+ IEEE80211_NOTE(vap,
IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni,
"station timed out due to inactivity "
"(refcnt %u)", ieee80211_node_refcnt(ni));
@@ -1561,45 +1908,109 @@ restart:
ieee80211_ref_node(ni);
IEEE80211_NODE_UNLOCK(nt);
if (ni->ni_associd != 0) {
- IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_SEND_MGMT(ni,
IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_AUTH_EXPIRE);
}
- ieee80211_node_leave(ic, ni);
+ ieee80211_node_leave(ni);
ieee80211_free_node(ni);
- ic->ic_stats.is_node_timeout++;
+ vap->iv_stats.is_node_timeout++;
goto restart;
}
}
IEEE80211_NODE_UNLOCK(nt);
- IEEE80211_SCAN_UNLOCK(nt);
+ IEEE80211_NODE_ITERATE_UNLOCK(nt);
+}
+
+/*
+ * Aggressively reclaim resources. This should be used
+ * only in a critical situation to reclaim mbuf resources.
+ */
+void
+ieee80211_drain(struct ieee80211com *ic)
+{
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211vap *vap;
+ struct ieee80211_node *ni;
+
+ IEEE80211_NODE_LOCK(nt);
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+ /*
+ * Ignore entries for which have yet to receive an
+ * authentication frame. These are transient and
+ * will be reclaimed when the last reference to them
+ * goes away (when frame xmits complete).
+ */
+ vap = ni->ni_vap;
+ /*
+ * Only process stations when in RUN state. This
+ * insures, for example, that we don't timeout an
+ * inactive station during CAC. Note that CSA state
+ * is actually handled in ieee80211_node_timeout as
+ * it applies to more than timeout processing.
+ */
+ if (vap->iv_state != IEEE80211_S_RUN)
+ continue;
+ /* XXX can vap be NULL? */
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_STA) &&
+ (ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+ continue;
+ /*
+ * Free fragments.
+ * XXX doesn't belong here, move to node_drain
+ */
+ if (ni->ni_rxfrag[0] != NULL) {
+ m_freem(ni->ni_rxfrag[0]);
+ ni->ni_rxfrag[0] = NULL;
+ }
+ /*
+ * Drain resources held by the station.
+ */
+ ic->ic_node_drain(ni);
+ }
+ IEEE80211_NODE_UNLOCK(nt);
}
+/*
+ * Per-ieee80211com inactivity timer callback.
+ */
void
ieee80211_node_timeout(void *arg)
{
struct ieee80211com *ic = arg;
- ieee80211_scan_timeout(ic);
- ieee80211_timeout_stations(&ic->ic_sta);
-
- IEEE80211_LOCK(ic);
- ieee80211_erp_timeout(ic);
- ieee80211_ht_timeout(ic);
- IEEE80211_UNLOCK(ic);
+ /*
+ * Defer timeout processing if a channel switch is pending.
+ * We typically need to be mute so not doing things that
+ * might generate frames is good to handle in one place.
+ * Supressing the station timeout processing may extend the
+ * lifetime of inactive stations (by not decrementing their
+ * idle counters) but this should be ok unless the CSA is
+ * active for an unusually long time.
+ */
+ if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) {
+ ieee80211_scan_timeout(ic);
+ ieee80211_timeout_stations(ic);
+ IEEE80211_LOCK(ic);
+ ieee80211_erp_timeout(ic);
+ ieee80211_ht_timeout(ic);
+ IEEE80211_UNLOCK(ic);
+ }
callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
ieee80211_node_timeout, ic);
}
void
-ieee80211_iterate_nodes(struct ieee80211_node_table *nt, ieee80211_iter_func *f, void *arg)
+ieee80211_iterate_nodes(struct ieee80211_node_table *nt,
+ ieee80211_iter_func *f, void *arg)
{
struct ieee80211_node *ni;
u_int gen;
- IEEE80211_SCAN_LOCK(nt);
+ IEEE80211_NODE_ITERATE_LOCK(nt);
gen = ++nt->nt_scangen;
restart:
IEEE80211_NODE_LOCK(nt);
@@ -1615,7 +2026,7 @@ restart:
}
IEEE80211_NODE_UNLOCK(nt);
- IEEE80211_SCAN_UNLOCK(nt);
+ IEEE80211_NODE_ITERATE_UNLOCK(nt);
}
void
@@ -1633,14 +2044,14 @@ ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK,
ni->ni_rxfragstamp);
printf("\trstamp %u rssi %d noise %d intval %u capinfo 0x%x\n",
- ni->ni_rstamp, ni->ni_rssi, ni->ni_noise,
+ ni->ni_rstamp, node_getrssi(ni), ni->ni_noise,
ni->ni_intval, ni->ni_capinfo);
printf("\tbssid %s essid \"%.*s\" channel %u:0x%x\n",
ether_sprintf(ni->ni_bssid),
ni->ni_esslen, ni->ni_essid,
ni->ni_chan->ic_freq, ni->ni_chan->ic_flags);
- printf("\tfails %u inact %u txrate %u\n",
- ni->ni_fails, ni->ni_inact, ni->ni_txrate);
+ printf("\tinact %u txrate %u\n",
+ ni->ni_inact, ni->ni_txrate);
printf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n",
ni->ni_htcap, ni->ni_htparam,
ni->ni_htctlchan, ni->ni_ht2ndchan);
@@ -1658,16 +2069,22 @@ ieee80211_dump_nodes(struct ieee80211_node_table *nt)
void
ieee80211_notify_erp(struct ieee80211com *ic)
{
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
- ieee80211_beacon_notify(ic, IEEE80211_BEACON_ERP);
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_ERP);
}
/*
* Handle a station joining an 11g network.
*/
static void
-ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_node_join_11g(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
IEEE80211_LOCK_ASSERT(ic);
@@ -1680,7 +2097,7 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
*/
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
ic->ic_longslotsta++;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"station needs long slot time, count %d",
ic->ic_longslotsta);
/* XXX vap's w/ conflicting needs won't work */
@@ -1698,9 +2115,9 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
* then bump the counter and enable protection
* if configured.
*/
- if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) {
+ if (!ieee80211_iserp_rateset(&ni->ni_rates)) {
ic->ic_nonerpsta++;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"station is !ERP, %d non-ERP stations associated",
ic->ic_nonerpsta);
/*
@@ -1708,18 +2125,19 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
* then we must enable use of Barker preamble.
*/
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"%s", "station needs long preamble");
ic->ic_flags |= IEEE80211_F_USEBARKER;
ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
}
/*
- * If protection is configured, enable it.
+ * If protection is configured and this is the first
+ * indication we should use protection, enable it.
*/
if (ic->ic_protmode != IEEE80211_PROT_NONE &&
ic->ic_nonerpsta == 1 &&
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
"%s: enable use of protection\n", __func__);
ic->ic_flags |= IEEE80211_F_USEPROT;
ieee80211_notify_erp(ic);
@@ -1729,47 +2147,49 @@ ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
}
void
-ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp)
+ieee80211_node_join(struct ieee80211_node *ni, int resp)
{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
int newassoc;
if (ni->ni_associd == 0) {
uint16_t aid;
- IEEE80211_LOCK(ic);
+ KASSERT(vap->iv_aid_bitmap != NULL, ("no aid bitmap"));
/*
* It would be good to search the bitmap
* more efficiently, but this will do for now.
*/
- for (aid = 1; aid < ic->ic_max_aid; aid++) {
- if (!IEEE80211_AID_ISSET(aid,
- ic->ic_aid_bitmap))
+ for (aid = 1; aid < vap->iv_max_aid; aid++) {
+ if (!IEEE80211_AID_ISSET(vap, aid))
break;
}
- if (aid >= ic->ic_max_aid) {
- IEEE80211_UNLOCK(ic);
- IEEE80211_SEND_MGMT(ic, ni, resp,
+ if (aid >= vap->iv_max_aid) {
+ IEEE80211_SEND_MGMT(ni, resp,
IEEE80211_REASON_ASSOC_TOOMANY);
- ieee80211_node_leave(ic, ni);
+ ieee80211_node_leave(ni);
return;
}
ni->ni_associd = aid | 0xc000;
ni->ni_jointime = time_uptime;
- IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
+ IEEE80211_LOCK(ic);
+ IEEE80211_AID_SET(vap, ni->ni_associd);
+ vap->iv_sta_assoc++;
ic->ic_sta_assoc++;
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
ieee80211_ht_node_join(ni);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
- ieee80211_node_join_11g(ic, ni);
+ ieee80211_node_join_11g(ni);
IEEE80211_UNLOCK(ic);
newassoc = 1;
} else
newassoc = 0;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
"station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s",
IEEE80211_NODE_AID(ni),
ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
@@ -1779,20 +2199,20 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp
ni->ni_flags & IEEE80211_NODE_HT ?
(ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_FF) ?
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
", fast-frames" : "",
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_NODE_TURBOP) ?
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ?
", turbo" : ""
);
/* give driver a chance to setup state like ni_txrate */
if (ic->ic_newassoc != NULL)
ic->ic_newassoc(ni, newassoc);
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
+ IEEE80211_SEND_MGMT(ni, resp, IEEE80211_STATUS_SUCCESS);
/* tell the authenticator about new station */
- if (ic->ic_auth->ia_node_join != NULL)
- ic->ic_auth->ia_node_join(ic, ni);
- ieee80211_notify_node_join(ic, ni,
+ if (vap->iv_auth->ia_node_join != NULL)
+ vap->iv_auth->ia_node_join(ni);
+ ieee80211_notify_node_join(ni,
resp == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
}
@@ -1817,14 +2237,15 @@ disable_protection(struct ieee80211com *ic)
* Handle a station leaving an 11g network.
*/
static void
-ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_node_leave_11g(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
IEEE80211_LOCK_ASSERT(ic);
KASSERT(IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan),
- ("not in 11g, bss %u:0x%x, curmode %u", ic->ic_bsschan->ic_freq,
- ic->ic_bsschan->ic_flags, ic->ic_curmode));
+ ("not in 11g, bss %u:0x%x", ic->ic_bsschan->ic_freq,
+ ic->ic_bsschan->ic_flags));
/*
* If a long slot station do the slot time bookkeeping.
@@ -1833,7 +2254,7 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
KASSERT(ic->ic_longslotsta > 0,
("bogus long slot station count %d", ic->ic_longslotsta));
ic->ic_longslotsta--;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"long slot time station leaves, count now %d",
ic->ic_longslotsta);
if (ic->ic_longslotsta == 0) {
@@ -1843,7 +2264,8 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
*/
if ((ic->ic_caps & IEEE80211_C_SHSLOT) &&
ic->ic_opmode != IEEE80211_M_IBSS) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(ni->ni_vap,
+ IEEE80211_MSG_ASSOC,
"%s: re-enable use of short slot time\n",
__func__);
ieee80211_set_shortslottime(ic, 1);
@@ -1857,13 +2279,13 @@ ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
KASSERT(ic->ic_nonerpsta > 0,
("bogus non-ERP station count %d", ic->ic_nonerpsta));
ic->ic_nonerpsta--;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC, ni,
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"non-ERP station leaves, count now %d%s", ic->ic_nonerpsta,
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) ?
" (non-ERP sta present)" : "");
if (ic->ic_nonerpsta == 0 &&
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
"%s: disable use of protection\n", __func__);
disable_protection(ic);
}
@@ -1886,8 +2308,10 @@ ieee80211_erp_timeout(struct ieee80211com *ic)
if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) &&
time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "%s\n", "age out non-ERP sta present on channel");
+#if 0
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "%s", "age out non-ERP sta present on channel");
+#endif
ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
if (ic->ic_nonerpsta == 0)
disable_protection(ic);
@@ -1899,15 +2323,17 @@ ieee80211_erp_timeout(struct ieee80211com *ic)
* when operating as an ap.
*/
void
-ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_node_leave(struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_node_table *nt = ni->ni_table;
- IEEE80211_NOTE(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
"station with aid %d leaves", IEEE80211_NODE_AID(ni));
- KASSERT(ic->ic_opmode != IEEE80211_M_STA,
- ("unexpected operating mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode != IEEE80211_M_STA,
+ ("unexpected operating mode %u", vap->iv_opmode));
/*
* If node wasn't previously associated all
* we need to do is reclaim the reference.
@@ -1921,19 +2347,20 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
* association id as the authenticator uses the
* associd to locate it's state block.
*/
- if (ic->ic_auth->ia_node_leave != NULL)
- ic->ic_auth->ia_node_leave(ic, ni);
+ if (vap->iv_auth->ia_node_leave != NULL)
+ vap->iv_auth->ia_node_leave(ni);
IEEE80211_LOCK(ic);
- IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ IEEE80211_AID_CLR(vap, ni->ni_associd);
ni->ni_associd = 0;
+ vap->iv_sta_assoc--;
ic->ic_sta_assoc--;
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
ieee80211_ht_node_leave(ni);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) &&
IEEE80211_IS_CHAN_FULL(ic->ic_bsschan))
- ieee80211_node_leave_11g(ic, ni);
+ ieee80211_node_leave_11g(ni);
IEEE80211_UNLOCK(ic);
/*
* Cleanup station state. In particular clear various
@@ -1941,7 +2368,7 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
* is reused before the reference count goes to zero
* (and memory is reclaimed).
*/
- ieee80211_sta_leave(ic, ni);
+ ieee80211_sta_leave(ni);
done:
/*
* Remove the node from any table it's recorded in and
@@ -1957,121 +2384,89 @@ done:
ieee80211_free_node(ni);
}
+struct rssiinfo {
+ struct ieee80211vap *vap;
+ int rssi_samples;
+ uint32_t rssi_total;
+};
+
+static void
+get_hostap_rssi(void *arg, struct ieee80211_node *ni)
+{
+ struct rssiinfo *info = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+ int8_t rssi;
+
+ if (info->vap != vap)
+ return;
+ /* only associated stations */
+ if (ni->ni_associd == 0)
+ return;
+ rssi = vap->iv_ic->ic_node_getrssi(ni);
+ if (rssi != 0) {
+ info->rssi_samples++;
+ info->rssi_total += rssi;
+ }
+}
+
+static void
+get_adhoc_rssi(void *arg, struct ieee80211_node *ni)
+{
+ struct rssiinfo *info = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+ int8_t rssi;
+
+ if (info->vap != vap)
+ return;
+ /* only neighbors */
+ /* XXX check bssid */
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+ return;
+ rssi = vap->iv_ic->ic_node_getrssi(ni);
+ if (rssi != 0) {
+ info->rssi_samples++;
+ info->rssi_total += rssi;
+ }
+}
+
int8_t
-ieee80211_getrssi(struct ieee80211com *ic)
+ieee80211_getrssi(struct ieee80211vap *vap)
{
#define NZ(x) ((x) == 0 ? 1 : (x))
- struct ieee80211_node_table *nt = &ic->ic_sta;
- int rssi_samples;
- int32_t rssi_total;
- struct ieee80211_node *ni;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rssiinfo info;
- rssi_total = 0;
- rssi_samples = 0;
- switch (ic->ic_opmode) {
+ info.rssi_total = 0;
+ info.rssi_samples = 0;
+ info.vap = vap;
+ switch (vap->iv_opmode) {
case IEEE80211_M_IBSS: /* average of all ibss neighbors */
case IEEE80211_M_AHDEMO: /* average of all neighbors */
+ ieee80211_iterate_nodes(&ic->ic_sta, get_adhoc_rssi, &info);
+ break;
case IEEE80211_M_HOSTAP: /* average of all associated stations */
- /* XXX locking */
- TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS)) {
- int8_t rssi = ic->ic_node_getrssi(ni);
- if (rssi != 0) {
- rssi_samples++;
- rssi_total += rssi;
- }
- }
+ ieee80211_iterate_nodes(&ic->ic_sta, get_hostap_rssi, &info);
break;
case IEEE80211_M_MONITOR: /* XXX */
case IEEE80211_M_STA: /* use stats from associated ap */
default:
- if (ic->ic_bss != NULL)
- rssi_total = ic->ic_node_getrssi(ic->ic_bss);
- rssi_samples = 1;
+ if (vap->iv_bss != NULL)
+ info.rssi_total = ic->ic_node_getrssi(vap->iv_bss);
+ info.rssi_samples = 1;
break;
}
- return rssi_total / NZ(rssi_samples);
+ return info.rssi_total / NZ(info.rssi_samples);
#undef NZ
}
void
-ieee80211_getsignal(struct ieee80211com *ic, int8_t *rssi, int8_t *noise)
+ieee80211_getsignal(struct ieee80211vap *vap, int8_t *rssi, int8_t *noise)
{
- if (ic->ic_bss == NULL) /* NB: shouldn't happen */
+ if (vap->iv_bss == NULL) /* NB: shouldn't happen */
return;
- ic->ic_node_getsignal(ic->ic_bss, rssi, noise);
+ vap->iv_ic->ic_node_getsignal(vap->iv_bss, rssi, noise);
/* for non-station mode return avg'd rssi accounting */
- if (ic->ic_opmode != IEEE80211_M_STA)
- *rssi = ieee80211_getrssi(ic);
-}
-
-/*
- * Node table support.
- */
-
-static void
-ieee80211_node_table_init(struct ieee80211com *ic,
- struct ieee80211_node_table *nt,
- const char *name, int inact, int keyixmax)
-{
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
- "%s %s table, inact %u\n", __func__, name, inact);
-
- nt->nt_ic = ic;
- /* XXX need unit */
- IEEE80211_NODE_LOCK_INIT(nt, ic->ic_ifp->if_xname);
- IEEE80211_SCAN_LOCK_INIT(nt, ic->ic_ifp->if_xname);
- TAILQ_INIT(&nt->nt_node);
- nt->nt_name = name;
- nt->nt_scangen = 1;
- nt->nt_inact_init = inact;
- nt->nt_keyixmax = keyixmax;
- if (nt->nt_keyixmax > 0) {
- MALLOC(nt->nt_keyixmap, struct ieee80211_node **,
- keyixmax * sizeof(struct ieee80211_node *),
- M_80211_NODE, M_NOWAIT | M_ZERO);
- if (nt->nt_keyixmap == NULL)
- if_printf(ic->ic_ifp,
- "Cannot allocate key index map with %u entries\n",
- keyixmax);
- } else
- nt->nt_keyixmap = NULL;
-}
-
-static void
-ieee80211_node_table_reset(struct ieee80211_node_table *nt)
-{
-
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
- "%s %s table\n", __func__, nt->nt_name);
-
- IEEE80211_NODE_LOCK(nt);
- ieee80211_free_allnodes_locked(nt);
- IEEE80211_NODE_UNLOCK(nt);
-}
-
-static void
-ieee80211_node_table_cleanup(struct ieee80211_node_table *nt)
-{
-
- IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
- "%s %s table\n", __func__, nt->nt_name);
-
- IEEE80211_NODE_LOCK(nt);
- ieee80211_free_allnodes_locked(nt);
- if (nt->nt_keyixmap != NULL) {
- /* XXX verify all entries are NULL */
- int i;
- for (i = 0; i < nt->nt_keyixmax; i++)
- if (nt->nt_keyixmap[i] != NULL)
- printf("%s: %s[%u] still active\n", __func__,
- nt->nt_name, i);
- FREE(nt->nt_keyixmap, M_80211_NODE);
- nt->nt_keyixmap = NULL;
- }
- IEEE80211_SCAN_LOCK_DESTROY(nt);
- IEEE80211_NODE_LOCK_DESTROY(nt);
+ if (vap->iv_opmode != IEEE80211_M_STA)
+ *rssi = ieee80211_getrssi(vap);
}
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index 2799ed4..fb569a9 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,18 +32,16 @@
#include <net80211/ieee80211_ht.h> /* for aggregation state */
/*
- * Each ieee80211com instance has a single timer that fires once a
- * second. This is used to initiate various work depending on the
- * state of the instance: scanning (passive or active), ``transition''
- * (waiting for a response to a management frame when operating
- * as a station), and node inactivity processing (when operating
- * as an AP). For inactivity processing each node has a timeout
- * set in it's ni_inact field that is decremented on each timeout
- * and the node is reclaimed when the counter goes to zero. We
- * use different inactivity timeout values depending on whether
- * the node is associated and authorized (either by 802.1x or
- * open/shared key authentication) or associated but yet to be
- * authorized. The latter timeout is shorter to more aggressively
+ * Each ieee80211com instance has a single timer that fires every
+ * IEEE80211_INACT_WAIT seconds to handle "inactivity processing".
+ * This is used to do node inactivity processing when operating
+ * as an AP or in adhoc mode. For inactivity processing each node
+ * has a timeout set in it's ni_inact field that is decremented
+ * on each timeout and the node is reclaimed when the counter goes
+ * to zero. We use different inactivity timeout values depending
+ * on whether the node is associated and authorized (either by
+ * 802.1x or open/shared key authentication) or associated but yet
+ * to be authorized. The latter timeout is shorter to more aggressively
* reclaim nodes that leave part way through the 802.1x exchange.
*/
#define IEEE80211_INACT_WAIT 15 /* inactivity interval (secs) */
@@ -64,19 +62,28 @@
(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
IEEE80211_NODE_HASHSIZE)
-struct ieee80211_rsnparms {
- uint8_t rsn_mcastcipher; /* mcast/group cipher */
- uint8_t rsn_mcastkeylen; /* mcast key length */
- uint8_t rsn_ucastcipherset; /* unicast cipher set */
- uint8_t rsn_ucastcipher; /* selected unicast cipher */
- uint8_t rsn_ucastkeylen; /* unicast key length */
- uint8_t rsn_keymgmtset; /* key mangement algorithms */
- uint8_t rsn_keymgmt; /* selected key mgmt algo */
- uint16_t rsn_caps; /* capabilities */
-};
-
struct ieee80211_node_table;
struct ieee80211com;
+struct ieee80211vap;
+
+/*
+ * Information element ``blob''. We use this structure
+ * to capture management frame payloads that need to be
+ * retained. Information elemnts within the payload that
+ * we need to consult have references recorded.
+ */
+struct ieee80211_ies {
+ /* the following are either NULL or point within data */
+ uint8_t *wpa_ie; /* captured WPA ie */
+ uint8_t *rsn_ie; /* captured RSN ie */
+ uint8_t *wme_ie; /* captured WME ie */
+ uint8_t *ath_ie; /* captured Atheros ie */
+ uint8_t *htcap_ie; /* captured HTCAP ie */
+ uint8_t *htinfo_ie; /* captured HTINFO ie */
+ /* NB: these must be the last members of this structure */
+ uint8_t *data; /* frame data > 802.11 header */
+ int len; /* data size in bytes */
+};
/*
* Node specific information. Note that drivers are expected
@@ -85,11 +92,12 @@ struct ieee80211com;
* the ieee80211com structure.
*/
struct ieee80211_node {
- struct ieee80211com *ni_ic;
- struct ieee80211_node_table *ni_table;
- TAILQ_ENTRY(ieee80211_node) ni_list;
- LIST_ENTRY(ieee80211_node) ni_hash;
- u_int ni_refcnt;
+ struct ieee80211vap *ni_vap; /* associated vap */
+ struct ieee80211com *ni_ic; /* copy from vap to save deref*/
+ struct ieee80211_node_table *ni_table; /* NB: may be NULL */
+ TAILQ_ENTRY(ieee80211_node) ni_list; /* list of all nodes */
+ LIST_ENTRY(ieee80211_node) ni_hash; /* hash collision list */
+ u_int ni_refcnt; /* count of held references */
u_int ni_scangen; /* gen# for timeout scan */
uint8_t ni_authmode; /* authentication algorithm */
uint8_t ni_ath_flags; /* Atheros feature flags */
@@ -99,7 +107,7 @@ struct ieee80211_node {
#define IEEE80211_NODE_FF 0x0004 /* Fast Frame capable */
#define IEEE80211_NODE_XR 0x0008 /* Atheros WME enable */
#define IEEE80211_NODE_AR 0x0010 /* AR capable */
-#define IEEE80211_NODE_BOOST 0x0080
+#define IEEE80211_NODE_BOOST 0x0080
#define IEEE80211_NODE_PSUPDATE 0x0200 /* power save state changed */
#define IEEE80211_NODE_CHWUPDATE 0x0400 /* 11n channel width change */
uint16_t ni_flags; /* special-purpose state */
@@ -111,6 +119,8 @@ struct ieee80211_node {
#define IEEE80211_NODE_AREF 0x0020 /* authentication ref held */
#define IEEE80211_NODE_HT 0x0040 /* HT enabled */
#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */
+#define IEEE80211_NODE_WPS 0x0100 /* WPS association */
+#define IEEE80211_NODE_TSN 0x0200 /* TSN association */
#define IEEE80211_NODE_AMPDU_RX 0x0400 /* AMPDU rx enabled */
#define IEEE80211_NODE_AMPDU_TX 0x0800 /* AMPDU tx enabled */
uint16_t ni_ath_defkeyix;/* Atheros def key index */
@@ -119,22 +129,18 @@ struct ieee80211_node {
uint16_t ni_vlan; /* vlan tag */
uint32_t ni_jointime; /* time of join (secs) */
uint32_t *ni_challenge; /* shared-key challenge */
- uint8_t *ni_wpa_ie; /* captured WPA ie */
- uint8_t *ni_rsn_ie; /* captured RSN ie */
- uint8_t *ni_wme_ie; /* captured WME ie */
- uint8_t *ni_ath_ie; /* captured Atheros ie */
+ struct ieee80211_ies ni_ies; /* captured ie's */
/* tx seq per-tid */
uint16_t ni_txseqs[IEEE80211_TID_SIZE];
/* rx seq previous per-tid*/
uint16_t ni_rxseqs[IEEE80211_TID_SIZE];
uint32_t ni_rxfragstamp; /* time stamp of last rx frag */
struct mbuf *ni_rxfrag[3]; /* rx frag reassembly */
- struct ieee80211_rsnparms ni_rsn; /* RSN/WPA parameters */
struct ieee80211_key ni_ucastkey; /* unicast key */
/* hardware */
uint32_t ni_rstamp; /* recv timestamp */
- int8_t ni_rssi; /* recv ssi */
+ uint32_t ni_avgrssi; /* recv ssi state */
int8_t ni_noise; /* noise floor */
/* header */
@@ -144,7 +150,7 @@ struct ieee80211_node {
/* beacon, probe response */
union {
uint8_t data[8];
- uint64_t tsf;
+ u_int64_t tsf;
} ni_tstamp; /* from last rcv'd beacon */
uint16_t ni_intval; /* beacon interval */
uint16_t ni_capinfo; /* capabilities */
@@ -154,13 +160,12 @@ struct ieee80211_node {
struct ieee80211_channel *ni_chan;
uint16_t ni_fhdwell; /* FH only */
uint8_t ni_fhindex; /* FH only */
- uint8_t ni_erp; /* ERP from beacon/probe resp */
+ uint16_t ni_erp; /* ERP from beacon/probe resp */
uint16_t ni_timoff; /* byte offset to TIM ie */
uint8_t ni_dtim_period; /* DTIM period */
uint8_t ni_dtim_count; /* DTIM count for last bcn */
/* 11n state */
- uint8_t *ni_htcap_ie; /* captured HTCAP ie */
uint16_t ni_htcap; /* HT capabilities */
uint8_t ni_htparam; /* HT params */
uint8_t ni_htctlchan; /* HT control channel */
@@ -174,14 +179,18 @@ struct ieee80211_node {
struct ieee80211_rx_ampdu ni_rx_ampdu[WME_NUM_TID];
/* others */
- int ni_fails; /* failure count to associate */
short ni_inact; /* inactivity mark count */
short ni_inact_reload;/* inactivity reload value */
- int ni_txrate; /* index to ni_rates[] */
- struct ifqueue ni_savedq; /* ps-poll queue */
+ int ni_txrate; /* legacy rate/MCS */
+ struct ifqueue ni_savedq; /* ps-poll queue */
struct ieee80211_nodestats ni_stats; /* per-node statistics */
+
+ struct ieee80211vap *ni_wdsvap; /* associated WDS vap */
+ /* XXX move to vap? */
+ struct ifqueue ni_wdsq; /* wds pending queue */
};
MALLOC_DECLARE(M_80211_NODE);
+MALLOC_DECLARE(M_80211_NODE_IE);
#define IEEE80211_NODE_ATH (IEEE80211_NODE_FF | IEEE80211_NODE_TURBOP)
#define IEEE80211_NODE_AMPDU \
@@ -193,6 +202,38 @@ MALLOC_DECLARE(M_80211_NODE);
#define IEEE80211_NODE_STAT_ADD(ni,stat,v) (ni->ni_stats.ns_##stat += v)
#define IEEE80211_NODE_STAT_SET(ni,stat,v) (ni->ni_stats.ns_##stat = v)
+/*
+ * Filtered rssi calculation support. The receive rssi is maintained
+ * as an average over the last 10 frames received using a low pass filter
+ * (all frames for now, possibly need to be more selective). Calculations
+ * are designed such that a good compiler can optimize them. The avg
+ * rssi state should be initialized to IEEE80211_RSSI_DUMMY_MARKER and
+ * each sample incorporated with IEEE80211_RSSI_LPF. Use IEEE80211_RSSI_GET
+ * to extract the current value.
+ *
+ * Note that we assume rssi data are in the range [-127..127] and we
+ * discard values <-20. This is consistent with assumptions throughout
+ * net80211 that signal strength data are in .5 dBm units relative to
+ * the current noise floor (linear, not log).
+ */
+#define IEEE80211_RSSI_LPF_LEN 10
+#define IEEE80211_RSSI_DUMMY_MARKER 127
+/* NB: pow2 to optimize out * and / */
+#define IEEE80211_RSSI_EP_MULTIPLIER (1<<7)
+#define IEEE80211_RSSI_IN(x) ((x) * IEEE80211_RSSI_EP_MULTIPLIER)
+#define _IEEE80211_RSSI_LPF(x, y, len) \
+ (((x) != IEEE80211_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define IEEE80211_RSSI_LPF(x, y) do { \
+ if ((y) >= -20) { \
+ x = _IEEE80211_RSSI_LPF((x), IEEE80211_RSSI_IN((y)), \
+ IEEE80211_RSSI_LPF_LEN); \
+ } \
+} while (0)
+#define IEEE80211_RSSI_EP_RND(x, mul) \
+ ((((x) % (mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
+#define IEEE80211_RSSI_GET(x) \
+ IEEE80211_RSSI_EP_RND(x, IEEE80211_RSSI_EP_MULTIPLIER)
+
static __inline struct ieee80211_node *
ieee80211_ref_node(struct ieee80211_node *ni)
{
@@ -212,6 +253,9 @@ struct ieee80211com;
void ieee80211_node_attach(struct ieee80211com *);
void ieee80211_node_lateattach(struct ieee80211com *);
void ieee80211_node_detach(struct ieee80211com *);
+void ieee80211_node_vattach(struct ieee80211vap *);
+void ieee80211_node_latevattach(struct ieee80211vap *);
+void ieee80211_node_vdetach(struct ieee80211vap *);
static __inline int
ieee80211_node_is_authorized(const struct ieee80211_node *ni)
@@ -222,21 +266,32 @@ ieee80211_node_is_authorized(const struct ieee80211_node *ni)
void ieee80211_node_authorize(struct ieee80211_node *);
void ieee80211_node_unauthorize(struct ieee80211_node *);
-void ieee80211_probe_curchan(struct ieee80211com *, int);
-void ieee80211_create_ibss(struct ieee80211com*, struct ieee80211_channel *);
-void ieee80211_reset_bss(struct ieee80211com *);
-void ieee80211_setbsschan(struct ieee80211com *, struct ieee80211_channel *);
+void ieee80211_node_set_chan(struct ieee80211_node *,
+ struct ieee80211_channel *);
+void ieee80211_create_ibss(struct ieee80211vap*, struct ieee80211_channel *);
+void ieee80211_reset_bss(struct ieee80211vap *);
+void ieee80211_sync_curchan(struct ieee80211com *);
+void ieee80211_setcurchan(struct ieee80211com *, struct ieee80211_channel *);
int ieee80211_ibss_merge(struct ieee80211_node *);
struct ieee80211_scan_entry;
-int ieee80211_sta_join(struct ieee80211com *,
+int ieee80211_sta_join(struct ieee80211vap *,
const struct ieee80211_scan_entry *);
-void ieee80211_sta_leave(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_sta_leave(struct ieee80211_node *);
+void ieee80211_node_deauth(struct ieee80211_node *, int);
+
+int ieee80211_ies_init(struct ieee80211_ies *, const uint8_t *, int);
+void ieee80211_ies_cleanup(struct ieee80211_ies *);
+void ieee80211_ies_expand(struct ieee80211_ies *);
+#define ieee80211_ies_setie(_ies, _ie, _off) do { \
+ (_ies)._ie = (_ies).data + (_off); \
+} while (0)
/*
* Table of ieee80211_node instances. Each ieee80211com
- * has at least one for holding the scan candidates.
- * When operating as an access point or in ibss mode there
- * is a second table for associated stations or neighbors.
+ * has one that holds association stations (when operating
+ * as an ap) or neighbors (in ibss mode).
+ *
+ * XXX embed this in ieee80211com instead of indirect?
*/
struct ieee80211_node_table {
struct ieee80211com *nt_ic; /* back reference */
@@ -245,23 +300,41 @@ struct ieee80211_node_table {
LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE];
struct ieee80211_node **nt_keyixmap; /* key ix -> node map */
int nt_keyixmax; /* keyixmap size */
- const char *nt_name; /* for debugging */
+ const char *nt_name; /* table name for debug msgs */
ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */
- u_int nt_scangen; /* gen# for timeout scan */
+ u_int nt_scangen; /* gen# for iterators */
int nt_inact_init; /* initial node inact setting */
};
-struct ieee80211_node *ieee80211_alloc_node(
- struct ieee80211_node_table *, const uint8_t *);
-struct ieee80211_node *ieee80211_tmp_node(struct ieee80211com *,
- const uint8_t *macaddr);
-struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *,
- const uint8_t *);
+struct ieee80211_node *ieee80211_alloc_node(struct ieee80211_node_table *,
+ struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_tmp_node(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_dup_bss(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_node_create_wds(struct ieee80211vap *,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ struct ieee80211_channel *);
#ifdef IEEE80211_DEBUG_REFCNT
void ieee80211_free_node_debug(struct ieee80211_node *,
const char *func, int line);
+struct ieee80211_node *ieee80211_find_node_locked_debug(
+ struct ieee80211_node_table *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
+ const char *func, int line);
struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *,
- const uint8_t *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
+ const char *func, int line);
+struct ieee80211_node *ieee80211_find_vap_node_locked_debug(
+ struct ieee80211_node_table *,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
+ const char *func, int line);
+struct ieee80211_node *ieee80211_find_vap_node_debug(
+ struct ieee80211_node_table *,
+ const struct ieee80211vap *vap,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN],
const char *func, int line);
struct ieee80211_node * ieee80211_find_rxnode_debug(struct ieee80211com *,
const struct ieee80211_frame_min *,
@@ -270,42 +343,43 @@ struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
struct ieee80211com *,
const struct ieee80211_frame_min *, uint16_t keyix,
const char *func, int line);
-struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
- struct ieee80211com *,
- const struct ieee80211_frame_min *, uint16_t keyix,
- const char *func, int line);
-struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211com *,
+struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211vap *,
const uint8_t *,
const char *func, int line);
-struct ieee80211_node *ieee80211_find_node_with_ssid_debug(
- struct ieee80211_node_table *, const uint8_t *macaddr,
- u_int ssidlen, const uint8_t *ssid,
- const char *func, int line);
#define ieee80211_free_node(ni) \
ieee80211_free_node_debug(ni, __func__, __LINE__)
+#define ieee80211_find_node_locked(nt, mac) \
+ ieee80211_find_node_locked_debug(nt, mac, __func__, __LINE__)
#define ieee80211_find_node(nt, mac) \
ieee80211_find_node_debug(nt, mac, __func__, __LINE__)
-#define ieee80211_find_rxnode(nt, wh) \
- ieee80211_find_rxnode_debug(nt, wh, __func__, __LINE__)
-#define ieee80211_find_rxnode_withkey(nt, wh, keyix) \
- ieee80211_find_rxnode_withkey_debug(nt, wh, keyix, __func__, __LINE__)
-#define ieee80211_find_txnode(nt, mac) \
- ieee80211_find_txnode_debug(nt, mac, __func__, __LINE__)
-#define ieee80211_find_node_with_ssid(nt, mac, sl, ss) \
- ieee80211_find_node_with_ssid_debug(nt, mac, sl, ss, __func__, __LINE__)
+#define ieee80211_find_vap_node_locked(nt, vap, mac) \
+ ieee80211_find_vap_node_locked_debug(nt, vap, mac, __func__, __LINE__)
+#define ieee80211_find_vap_node(nt, vap, mac) \
+ ieee80211_find_vap_node_debug(nt, vap, mac, __func__, __LINE__)
+#define ieee80211_find_rxnode(ic, wh) \
+ ieee80211_find_rxnode_debug(ic, wh, __func__, __LINE__)
+#define ieee80211_find_rxnode_withkey(ic, wh, keyix) \
+ ieee80211_find_rxnode_withkey_debug(ic, wh, keyix, __func__, __LINE__)
+#define ieee80211_find_txnode(vap, mac) \
+ ieee80211_find_txnode_debug(vap, mac, __func__, __LINE__)
#else
void ieee80211_free_node(struct ieee80211_node *);
+struct ieee80211_node *ieee80211_find_node_locked(struct ieee80211_node_table *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node *ieee80211_find_node(struct ieee80211_node_table *,
- const uint8_t *);
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_find_vap_node_locked(
+ struct ieee80211_node_table *, const struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+struct ieee80211_node *ieee80211_find_vap_node(
+ struct ieee80211_node_table *, const struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_node * ieee80211_find_rxnode(struct ieee80211com *,
const struct ieee80211_frame_min *);
struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *,
const struct ieee80211_frame_min *, uint16_t keyix);
-struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *,
- const uint8_t *);
-struct ieee80211_node *ieee80211_find_node_with_ssid(
- struct ieee80211_node_table *, const uint8_t *macaddr,
- u_int ssidlen, const uint8_t *ssid);
+struct ieee80211_node *ieee80211_find_txnode(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
#endif
int ieee80211_node_delucastkey(struct ieee80211_node *);
void ieee80211_node_timeout(void *arg);
@@ -314,23 +388,22 @@ typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
void ieee80211_iterate_nodes(struct ieee80211_node_table *,
ieee80211_iter_func *, void *);
+void ieee80211_notify_erp(struct ieee80211com *);
void ieee80211_dump_node(struct ieee80211_node_table *,
struct ieee80211_node *);
void ieee80211_dump_nodes(struct ieee80211_node_table *);
-void ieee80211_notify_erp(struct ieee80211com *);
-
-struct ieee80211_node *ieee80211_fakeup_adhoc_node(
- struct ieee80211_node_table *, const uint8_t macaddr[]);
+struct ieee80211_node *ieee80211_fakeup_adhoc_node(struct ieee80211vap *,
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
struct ieee80211_scanparams;
void ieee80211_init_neighbor(struct ieee80211_node *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
-struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211com *,
+struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211vap *,
const struct ieee80211_frame *,
const struct ieee80211_scanparams *);
-void ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int);
-void ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *);
-int8_t ieee80211_getrssi(struct ieee80211com *);
-void ieee80211_getsignal(struct ieee80211com *, int8_t *, int8_t *);
+void ieee80211_node_join(struct ieee80211_node *,int);
+void ieee80211_node_leave(struct ieee80211_node *);
+int8_t ieee80211_getrssi(struct ieee80211vap *);
+void ieee80211_getsignal(struct ieee80211vap *, int8_t *, int8_t *);
#endif /* _NET80211_IEEE80211_NODE_H_ */
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index 6e31d1d..cc2a911 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,6 +28,7 @@
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -46,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_wds.h>
#ifdef INET
#include <netinet/in.h>
@@ -57,10 +59,10 @@ __FBSDID("$FreeBSD$");
#define ETHER_HEADER_COPY(dst, src) \
memcpy(dst, src, sizeof(struct ether_header))
-static struct mbuf *ieee80211_encap_fastframe(struct ieee80211com *ic,
+static struct mbuf *ieee80211_encap_fastframe(struct ieee80211vap *,
struct mbuf *m1, const struct ether_header *eh1,
struct mbuf *m2, const struct ether_header *eh2);
-static int ieee80211_fragment(struct ieee80211com *, struct mbuf *,
+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);
@@ -72,24 +74,344 @@ static void ieee80211_tx_mgt_cb(struct ieee80211_node *, void *, int);
* (e.g. beacons).
*/
static __inline int
-doprint(struct ieee80211com *ic, int subtype)
+doprint(struct ieee80211vap *vap, int subtype)
{
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- return (ic->ic_opmode == IEEE80211_M_IBSS);
+ return (vap->iv_opmode == IEEE80211_M_IBSS);
}
return 1;
}
#endif
/*
+ * Start method for vap's. All packets from the stack come
+ * through here. We handle common processing of the packets
+ * before dispatching them to the underlying device.
+ */
+void
+ieee80211_start(struct ifnet *ifp)
+{
+#define IS_DWDS(vap) \
+ (vap->iv_opmode == IEEE80211_M_WDS && \
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0)
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *parent = ic->ic_ifp;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ struct ether_header *eh;
+ int error;
+
+ /* NB: parent must be up and running */
+ if (!IFNET_IS_UP_RUNNING(parent)) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+ "%s: ignore queue, parent %s not up+running\n",
+ __func__, parent->if_xname);
+ /* XXX stat */
+ return;
+ }
+ if (vap->iv_state == IEEE80211_S_SLEEP) {
+ /*
+ * In power save, wakeup device for transmit.
+ */
+ ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
+ return;
+ }
+ /*
+ * No data frames go out unless we're running.
+ * Note in particular this covers CAC and CSA
+ * states (though maybe we should check muting
+ * for CSA).
+ */
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ IEEE80211_LOCK(ic);
+ /* re-check under the com lock to avoid races */
+ if (vap->iv_state != IEEE80211_S_RUN) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+ "%s: ignore queue, in %s state\n",
+ __func__, ieee80211_state_name[vap->iv_state]);
+ vap->iv_stats.is_tx_badstate++;
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ IEEE80211_UNLOCK(ic);
+ return;
+ }
+ IEEE80211_UNLOCK(ic);
+ }
+ for (;;) {
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ /*
+ * Sanitize mbuf flags for net80211 use. We cannot
+ * clear M_PWR_SAV because this 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);
+ /*
+ * 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);
+ }
+ }
+ 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;
+ }
+ /* XXX AUTH'd */
+ if (ni->ni_associd == 0) {
+ /*
+ * Destination is not associated; must special
+ * case DWDS where we point iv_bss at the node
+ * for the associated station.
+ * XXX adhoc mode?
+ */
+ if (ni != vap->iv_bss || IS_DWDS(vap)) {
+ 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;
+ }
+ }
+ 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?
+ */
+ 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;
+ }
+
+ BPF_MTAP(ifp, m); /* 802.11 tx path */
+
+ /*
+ * XXX When ni is associated with a WDS link then
+ * the vap will be the WDS vap but ni_vap will point
+ * to the ap vap the station associated to. Once
+ * we handoff the packet to the driver the callback
+ * to ieee80211_encap won't be able to tell if the
+ * packet should be encapsulated for WDS or not (e.g.
+ * multicast frames will not be handled correctly).
+ * We hack this by marking the mbuf so ieee80211_encap
+ * can do the right thing.
+ */
+ if (vap->iv_opmode == IEEE80211_M_WDS)
+ m->m_flags |= M_WDS;
+ else
+ m->m_flags &= ~M_WDS;
+
+ /*
+ * Stash the node pointer and hand the frame off to
+ * the underlying device. Note that we do this after
+ * any call to ieee80211_dwds_mcast because that code
+ * uses any existing value for rcvif.
+ */
+ m->m_pkthdr.rcvif = (void *)ni;
+
+ /* XXX defer if_start calls? */
+ IFQ_HANDOFF(parent, m, error);
+ if (error != 0) {
+ /* NB: IFQ_HANDOFF reclaims mbuf */
+ ieee80211_free_node(ni);
+ } else {
+ ifp->if_opackets++;
+ }
+ ic->ic_lastdata = ticks;
+ }
+#undef IS_DWDS
+}
+
+/*
+ * 802.11 output routine. This is (currently) used only to
+ * connect bpf write calls to the 802.11 layer for injecting
+ * raw 802.11 frames. Note we locate the ieee80211com from
+ * the ifnet using a spare field setup at attach time. This
+ * will go away when the virtual ap support comes in.
+ */
+int
+ieee80211_output(struct ifnet *ifp, struct mbuf *m,
+ struct sockaddr *dst, struct rtentry *rt0)
+{
+#define senderr(e) do { error = (e); goto bad;} while (0)
+ struct ieee80211_node *ni = NULL;
+ struct ieee80211vap *vap;
+ struct ieee80211_frame *wh;
+ int error;
+
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
+ /*
+ * Short-circuit requests if the vap is marked OACTIVE
+ * as this is used when tearing down state to indicate
+ * the vap may be gone. This can also happen because a
+ * packet came down through ieee80211_start before the
+ * vap entered RUN state in which case it's also ok to
+ * just drop the frame. This should not be necessary
+ * but callers of if_output don't check OACTIVE.
+ */
+ senderr(ENETDOWN);
+ }
+ vap = ifp->if_softc;
+ /*
+ * Hand to the 802.3 code if not tagged as
+ * a raw 802.11 frame.
+ */
+ if (dst->sa_family != AF_IEEE80211)
+ return vap->iv_output(ifp, m, dst, rt0);
+#ifdef MAC
+ error = mac_check_ifnet_transmit(ifp, m);
+ if (error)
+ senderr(error);
+#endif
+ if (ifp->if_flags & IFF_MONITOR)
+ senderr(ENETDOWN);
+ if (!IFNET_IS_UP_RUNNING(ifp))
+ senderr(ENETDOWN);
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
+ "block %s frame in CAC state\n", "raw data");
+ vap->iv_stats.is_tx_badstate++;
+ senderr(EIO); /* XXX */
+ }
+ /* XXX bypass bridge, pfil, carp, etc. */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack))
+ senderr(EIO); /* XXX */
+ wh = mtod(m, struct ieee80211_frame *);
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0)
+ senderr(EIO); /* XXX */
+
+ /* locate destination node */
+ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ case IEEE80211_FC1_DIR_FROMDS:
+ ni = ieee80211_find_txnode(vap, wh->i_addr1);
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ case IEEE80211_FC1_DIR_DSTODS:
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame))
+ senderr(EIO); /* XXX */
+ ni = ieee80211_find_txnode(vap, wh->i_addr3);
+ break;
+ default:
+ senderr(EIO); /* XXX */
+ }
+ if (ni == NULL) {
+ /*
+ * Permit packets w/ bpf params through regardless
+ * (see below about sa_len).
+ */
+ if (dst->sa_len == 0)
+ senderr(EHOSTUNREACH);
+ ni = ieee80211_ref_node(vap->iv_bss);
+ }
+
+ /*
+ * Sanitize mbuf for net80211 flags leaked from above.
+ *
+ * NB: This must be done before ieee80211_classify as
+ * it marks EAPOL in frames with M_EAPOL.
+ */
+ m->m_flags &= ~M_80211_TX;
+
+ /* calculate priority so drivers can find the tx queue */
+ /* XXX assumes an 802.3 frame */
+ if (ieee80211_classify(ni, m))
+ senderr(EIO); /* XXX */
+
+ BPF_MTAP(ifp, m);
+
+ /*
+ * NB: DLT_IEEE802_11_RADIO identifies the parameters are
+ * present by setting the sa_len field of the sockaddr (yes,
+ * this is a hack).
+ * NB: we assume sa_data is suitably aligned to cast.
+ */
+ return vap->iv_ic->ic_raw_xmit(ni, m,
+ (const struct ieee80211_bpf_params *)(dst->sa_len ?
+ dst->sa_data : NULL));
+bad:
+ if (m != NULL)
+ m_freem(m);
+ if (ni != NULL)
+ ieee80211_free_node(ni);
+ return error;
+#undef senderr
+}
+
+/*
* Set the direction field and address fields of an outgoing
* non-QoS frame. Note this should be called early on in
* constructing a frame as it sets i_fc[1]; other bits can
* then be or'd in.
*/
static void
-ieee80211_send_setup(struct ieee80211com *ic,
+ieee80211_send_setup(
struct ieee80211_node *ni,
struct ieee80211_frame *wh,
int type,
@@ -101,7 +423,9 @@ ieee80211_send_setup(struct ieee80211com *ic,
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
- switch (ic->ic_opmode) {
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ switch (vap->iv_opmode) {
case IEEE80211_M_STA:
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
@@ -123,9 +447,8 @@ ieee80211_send_setup(struct ieee80211com *ic,
break;
case IEEE80211_M_WDS:
wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
- /* XXX cheat, bssid holds RA */
- IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr1, da);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
IEEE80211_ADDR_COPY(wh->i_addr3, da);
IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, sa);
break;
@@ -139,6 +462,7 @@ ieee80211_send_setup(struct ieee80211com *ic,
IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
}
*(uint16_t *)&wh->i_dur[0] = 0;
+ /* XXX probe response use per-vap seq#? */
/* NB: use non-QoS tid */
*(uint16_t *)&wh->i_seq[0] =
htole16(ni->ni_txseqs[IEEE80211_NONQOS_TID] << IEEE80211_SEQ_SEQ_SHIFT);
@@ -151,55 +475,55 @@ ieee80211_send_setup(struct ieee80211com *ic,
* must have a reference as the pointer will be passed to the driver
* and potentially held for a long time. If the frame is successfully
* dispatched to the driver, then it is responsible for freeing the
- * reference (and potentially free'ing up any associated storage).
+ * reference (and potentially free'ing up any associated storage);
+ * otherwise deal with reclaiming any reference (on error).
*/
int
-ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
- struct mbuf *m, int type)
+ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type)
{
- struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
KASSERT(ni != NULL, ("null node"));
- /*
- * Yech, hack alert! We want to pass the node down to the
- * driver's start routine. If we don't do so then the start
- * routine must immediately look it up again and that can
- * cause a lock order reversal if, for example, this frame
- * is being sent because the station is being timedout and
- * the frame being sent is a DEAUTH message. We could stick
- * this in an m_tag and tack that on to the mbuf. However
- * that's rather expensive to do for every frame so instead
- * we stuff it in the rcvif field since outbound frames do
- * not (presently) use this.
- */
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
+ ni, "block %s frame in CAC state",
+ ieee80211_mgt_subtype_name[
+ (type & IEEE80211_FC0_SUBTYPE_MASK) >>
+ IEEE80211_FC0_SUBTYPE_SHIFT]);
+ vap->iv_stats.is_tx_badstate++;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ return EIO; /* XXX */
+ }
+
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
- if (m == NULL)
+ if (m == NULL) {
+ ieee80211_free_node(ni);
return ENOMEM;
- KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
- m->m_pkthdr.rcvif = (void *)ni;
+ }
wh = mtod(m, struct ieee80211_frame *);
- ieee80211_send_setup(ic, ni, wh,
+ ieee80211_send_setup(ni, wh,
IEEE80211_FC0_TYPE_MGT | type,
- ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
+ vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid);
if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) {
m->m_flags &= ~M_LINK0;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] encrypting frame (%s)\n",
- ether_sprintf(wh->i_addr1), __func__);
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr1,
+ "encrypting frame (%s)", __func__);
wh->i_fc[1] |= IEEE80211_FC1_WEP;
}
- if (ni->ni_flags & IEEE80211_NODE_QOS) {
- /* NB: force all management frames to the highest queue */
+ if (type != IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+ /* NB: force non-ProbeResp frames to the highest queue */
M_WME_SETAC(m, WME_AC_VO);
} else
M_WME_SETAC(m, WME_AC_BE);
#ifdef IEEE80211_DEBUG
/* avoid printing too many frames */
- if ((ieee80211_msg_debug(ic) && doprint(ic, type)) ||
- ieee80211_msg_dumppkts(ic)) {
+ if ((ieee80211_msg_debug(vap) && doprint(vap, type)) ||
+ ieee80211_msg_dumppkts(vap)) {
printf("[%s] send %s on channel %u\n",
ether_sprintf(wh->i_addr1),
ieee80211_mgt_subtype_name[
@@ -209,135 +533,8 @@ ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
}
#endif
IEEE80211_NODE_STAT(ni, tx_mgmt);
- IF_ENQUEUE(&ic->ic_mgtq, m);
- if_start(ifp);
- ifp->if_opackets++;
-
- return 0;
-}
-
-/*
- * Raw packet transmit stub for legacy drivers.
- * Send the packet through the mgt q so we bypass
- * the normal encapsulation work.
- */
-int
-ieee80211_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
- const struct ieee80211_bpf_params *params)
-{
- struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
-
- m->m_pkthdr.rcvif = (void *) ni;
- IF_ENQUEUE(&ic->ic_mgtq, m);
- if_start(ifp);
- ifp->if_opackets++;
-
- return 0;
-}
-
-/*
- * 802.11 output routine. This is (currently) used only to
- * connect bpf write calls to the 802.11 layer for injecting
- * raw 802.11 frames. Note we locate the ieee80211com from
- * the ifnet using a spare field setup at attach time. This
- * will go away when the virtual ap support comes in.
- */
-int
-ieee80211_output(struct ifnet *ifp, struct mbuf *m,
- struct sockaddr *dst, struct rtentry *rt0)
-{
-#define senderr(e) do { error = (e); goto bad;} while (0)
- struct ieee80211com *ic = ifp->if_llsoftc; /* XXX */
- struct ieee80211_node *ni = NULL;
- struct ieee80211_frame *wh;
- int error;
-
- /*
- * Hand to the 802.3 code if not tagged as
- * a raw 802.11 frame.
- */
- if (dst->sa_family != AF_IEEE80211)
- return ether_output(ifp, m, dst, rt0);
-#ifdef MAC
- error = mac_check_ifnet_transmit(ifp, m);
- if (error)
- senderr(error);
-#endif
- if (ifp->if_flags & IFF_MONITOR)
- senderr(ENETDOWN);
- if ((ifp->if_flags & IFF_UP) == 0)
- senderr(ENETDOWN);
- /* XXX bypass bridge, pfil, carp, etc. */
-
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_ack))
- senderr(EIO); /* XXX */
- wh = mtod(m, struct ieee80211_frame *);
- if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
- IEEE80211_FC0_VERSION_0)
- senderr(EIO); /* XXX */
-
- /* locate destination node */
- switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
- case IEEE80211_FC1_DIR_NODS:
- case IEEE80211_FC1_DIR_FROMDS:
- ni = ieee80211_find_txnode(ic, wh->i_addr1);
- break;
- case IEEE80211_FC1_DIR_TODS:
- case IEEE80211_FC1_DIR_DSTODS:
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame))
- senderr(EIO); /* XXX */
- ni = ieee80211_find_txnode(ic, wh->i_addr3);
- break;
- default:
- senderr(EIO); /* XXX */
- }
- if (ni == NULL) {
- /*
- * Permit packets w/ bpf params through regardless
- * (see below about sa_len).
- */
- if (dst->sa_len == 0)
- senderr(EHOSTUNREACH);
- ni = ieee80211_ref_node(ic->ic_bss);
- }
-
- /* XXX ctrl frames should go through */
- 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.
- */
- ieee80211_pwrsave(ni, m);
- error = 0;
- goto reclaim;
- }
-
- /* calculate priority so drivers can find the tx queue */
- /* XXX assumes an 802.3 frame */
- if (ieee80211_classify(ic, m, ni))
- senderr(EIO); /* XXX */
-
- BPF_MTAP(ifp, m);
- /*
- * 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 ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *)
- (dst->sa_len ? dst->sa_data : NULL));
-bad:
- if (m != NULL)
- m_freem(m);
-reclaim:
- if (ni != NULL)
- ieee80211_free_node(ni);
- return error;
-#undef senderr
+ return ic->ic_raw_xmit(ni, m, NULL);
}
/*
@@ -345,50 +542,61 @@ reclaim:
*
* NB: the caller is assumed to have setup a node reference
* for use; this is necessary to deal with a race condition
- * when probing for inactive stations.
+ * when probing for inactive stations. Like ieee80211_mgmt_output
+ * we must cleanup any node reference on error; however we
+ * can safely just unref it as we know it will never be the
+ * last reference to the node.
*/
int
ieee80211_send_nulldata(struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
struct mbuf *m;
struct ieee80211_frame *wh;
- MGETHDR(m, M_NOWAIT, MT_DATA);
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH,
+ ni, "block %s frame in CAC state", "null data");
+ ieee80211_unref_node(&ni);
+ vap->iv_stats.is_tx_badstate++;
+ return EIO; /* XXX */
+ }
+
+ m = m_gethdr(M_NOWAIT, MT_HEADER);
if (m == NULL) {
/* XXX debug msg */
ieee80211_unref_node(&ni);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return ENOMEM;
}
MH_ALIGN(m, sizeof(struct ieee80211_frame));
- m->m_pkthdr.rcvif = (void *) ni;
wh = mtod(m, struct ieee80211_frame *);
- ieee80211_send_setup(ic, ni, wh,
+ ieee80211_send_setup(ni, wh,
IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA,
- ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
- /* NB: power management bit is never sent by an AP */
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
- ic->ic_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_opmode != IEEE80211_M_WDS)
- wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
- m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+ vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid);
+ if (vap->iv_opmode != IEEE80211_M_WDS) {
+ /* NB: power management bit is never sent by an AP */
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+ vap->iv_opmode != IEEE80211_M_HOSTAP)
+ wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
+ m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+ } else {
+ /* NB: 4-address frame */
+ m->m_len = m->m_pkthdr.len =
+ sizeof(struct ieee80211_frame_addr4);
+ }
M_WME_SETAC(m, WME_AC_BE);
IEEE80211_NODE_STAT(ni, tx_data);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
- "[%s] send null data frame on channel %u, pwr mgt %s\n",
- ether_sprintf(ni->ni_macaddr),
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, ni,
+ "send null data frame on channel %u, pwr mgt %s",
ieee80211_chan2ieee(ic, ic->ic_curchan),
wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
- IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */
- if_start(ifp);
-
- return 0;
+ return ic->ic_raw_xmit(ni, m, NULL);
}
/*
@@ -398,13 +606,23 @@ ieee80211_send_nulldata(struct ieee80211_node *ni)
* applied.
*/
int
-ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni)
+ieee80211_classify(struct ieee80211_node *ni, struct mbuf *m)
{
+ const struct ether_header *eh = mtod(m, struct ether_header *);
int v_wme_ac, d_wme_ac, ac;
-#ifdef INET
- struct ether_header *eh;
-#endif
+ /*
+ * Always promote PAE/EAPOL frames to high priority.
+ */
+ if (eh->ether_type == htons(ETHERTYPE_PAE)) {
+ /* NB: mark so others don't need to check header */
+ m->m_flags |= M_EAPOL;
+ ac = WME_AC_VO;
+ goto done;
+ }
+ /*
+ * Non-qos traffic goes to BE.
+ */
if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) {
ac = WME_AC_BE;
goto done;
@@ -430,7 +648,6 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod
}
#ifdef INET
- eh = mtod(m, struct ether_header *);
if (eh->ether_type == htons(ETHERTYPE_IP)) {
uint8_t tos;
/*
@@ -459,13 +676,15 @@ ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_nod
/*
* Apply ACM policy.
*/
- if (ic->ic_opmode == IEEE80211_M_STA) {
+ if (ni->ni_vap->iv_opmode == IEEE80211_M_STA) {
static const int acmap[4] = {
WME_AC_BK, /* WME_AC_BE */
WME_AC_BK, /* WME_AC_BK */
WME_AC_BE, /* WME_AC_VI */
WME_AC_VI, /* WME_AC_VO */
};
+ struct ieee80211com *ic = ni->ni_ic;
+
while (ac != WME_AC_BK &&
ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm)
ac = acmap[ac];
@@ -482,11 +701,11 @@ done:
* and fail rudely if they don't find the space they need.
*/
static struct mbuf *
-ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
+ieee80211_mbuf_adjust(struct ieee80211vap *vap, int hdrsize,
struct ieee80211_key *key, struct mbuf *m)
{
#define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc))
- int needed_space = ic->ic_headroom + hdrsize;
+ int needed_space = vap->iv_ic->ic_headroom + hdrsize;
if (key != NULL) {
/* XXX belongs in crypto code? */
@@ -501,9 +720,9 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
if (key->wk_flags & (IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC)) {
m = m_unshare(m, M_NOWAIT);
if (m == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot get writable mbuf\n", __func__);
- ic->ic_stats.is_tx_nobuf++; /* XXX new stat */
+ vap->iv_stats.is_tx_nobuf++; /* XXX new stat */
return NULL;
}
}
@@ -520,9 +739,9 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) {
struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type);
if (n == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot expand storage\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
m_freem(m);
return NULL;
}
@@ -564,13 +783,14 @@ ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
* we fall back to the default transmit key.
*/
static __inline struct ieee80211_key *
-ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_crypto_getucastkey(struct ieee80211vap *vap,
+ struct ieee80211_node *ni)
{
if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) {
- if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
- IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey]))
+ if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE ||
+ IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey]))
return NULL;
- return &ic->ic_nw_keys[ic->ic_def_txkey];
+ return &vap->iv_nw_keys[vap->iv_def_txkey];
} else {
return &ni->ni_ucastkey;
}
@@ -582,12 +802,13 @@ ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
* the default tx key.
*/
static __inline struct ieee80211_key *
-ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_crypto_getmcastkey(struct ieee80211vap *vap,
+ struct ieee80211_node *ni)
{
- if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
- IEEE80211_KEY_UNDEFINED(&ic->ic_nw_keys[ic->ic_def_txkey]))
+ if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE ||
+ IEEE80211_KEY_UNDEFINED(&vap->iv_nw_keys[vap->iv_def_txkey]))
return NULL;
- return &ic->ic_nw_keys[ic->ic_def_txkey];
+ return &vap->iv_nw_keys[vap->iv_def_txkey];
}
/*
@@ -595,16 +816,21 @@ ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
* If an error is encountered NULL is returned. The caller is required
* to provide a node reference and pullup the ethernet header in the
* first mbuf.
+ *
+ * NB: Packet is assumed to be processed by ieee80211_classify which
+ * marked EAPOL frames w/ M_EAPOL.
*/
struct mbuf *
-ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
- struct ieee80211_node *ni)
+ieee80211_encap(struct ieee80211_node *ni, struct mbuf *m)
{
+#define WH4(wh) ((struct ieee80211_frame_addr4 *)(wh))
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
struct ether_header eh;
struct ieee80211_frame *wh;
struct ieee80211_key *key;
struct llc *llc;
- int hdrsize, datalen, addqos, txfrag, isff;
+ int hdrsize, hdrspace, datalen, addqos, txfrag, isff, is4addr;
/*
* Copy existing Ethernet header to a safe place. The
@@ -612,7 +838,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* reorganizing state for the final encapsulation.
*/
KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!"));
- memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
+ ETHER_HEADER_COPY(&eh, mtod(m, caddr_t));
/*
* Insure space for additional headers. First identify
@@ -626,23 +852,24 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* buffer may not be expanded as needed by the cipher
* routines, but they will/should discard it.
*/
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
- if (ic->ic_opmode == IEEE80211_M_STA ||
- !IEEE80211_IS_MULTICAST(eh.ether_dhost))
- key = ieee80211_crypto_getucastkey(ic, ni);
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_opmode == IEEE80211_M_STA ||
+ !IEEE80211_IS_MULTICAST(eh.ether_dhost) ||
+ (vap->iv_opmode == IEEE80211_M_WDS &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)))
+ key = ieee80211_crypto_getucastkey(vap, ni);
else
- key = ieee80211_crypto_getmcastkey(ic, ni);
- if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] no default transmit key (%s) deftxkey %u\n",
- ether_sprintf(eh.ether_dhost), __func__,
- ic->ic_def_txkey);
- ic->ic_stats.is_tx_nodefkey++;
+ key = ieee80211_crypto_getmcastkey(vap, ni);
+ if (key == NULL && (m->m_flags & M_EAPOL) == 0) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
+ eh.ether_dhost,
+ "no default transmit key (%s) deftxkey %u",
+ __func__, vap->iv_def_txkey);
+ vap->iv_stats.is_tx_nodefkey++;
goto bad;
}
} else
key = NULL;
- /* XXX 4-address format */
/*
* XXX Some ap's don't handle QoS-encapsulated EAPOL
* frames so suppress use. This may be an issue if other
@@ -651,13 +878,31 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* configurable.
*/
addqos = (ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) &&
- eh.ether_type != htons(ETHERTYPE_PAE);
+ (m->m_flags & M_EAPOL) == 0;
if (addqos)
hdrsize = sizeof(struct ieee80211_qosframe);
else
hdrsize = sizeof(struct ieee80211_frame);
+ /*
+ * 4-address frames need to be generated for:
+ * o packets sent through a WDS vap (M_WDS || IEEE80211_M_WDS)
+ * o packets relayed by a station operating with dynamic WDS
+ * (IEEE80211_M_STA+IEEE80211_F_DWDS and src address)
+ */
+ is4addr = (m->m_flags & M_WDS) ||
+ vap->iv_opmode == IEEE80211_M_WDS || /* XXX redundant? */
+ (vap->iv_opmode == IEEE80211_M_STA &&
+ (vap->iv_flags & IEEE80211_F_DWDS) &&
+ !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr));
+ if (is4addr)
+ hdrsize += IEEE80211_ADDR_LEN;
+ /*
+ * Honor driver DATAPAD requirement.
+ */
if (ic->ic_flags & IEEE80211_F_DATAPAD)
- hdrsize = roundup(hdrsize, sizeof(uint32_t));
+ hdrspace = roundup(hdrsize, sizeof(uint32_t));
+ else
+ hdrspace = hdrsize;
if ((isff = m->m_flags & M_FF) != 0) {
struct mbuf *m2;
@@ -671,7 +916,7 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
*/
m2 = m->m_nextpkt;
if (m2 == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: only one frame\n", __func__);
goto bad;
}
@@ -681,8 +926,8 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* layout; this allocates space according to what
* ieee80211_encap_fastframe will do.
*/
- m = ieee80211_mbuf_adjust(ic,
- hdrsize + sizeof(struct llc) + sizeof(uint32_t) + 2 +
+ m = ieee80211_mbuf_adjust(vap,
+ hdrspace + sizeof(struct llc) + sizeof(uint32_t) + 2 +
sizeof(struct ether_header),
key, m);
if (m == NULL) {
@@ -697,22 +942,22 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
* at the end of first frame.
*/
KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!"));
- memcpy(&eh2, mtod(m2, caddr_t), sizeof(struct ether_header));
- m2 = ieee80211_mbuf_adjust(ic,
+ ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t));
+ m2 = ieee80211_mbuf_adjust(vap,
ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header),
NULL, m2);
if (m2 == NULL) {
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
goto bad;
}
- m = ieee80211_encap_fastframe(ic, m, &eh, m2, &eh2);
+ m = ieee80211_encap_fastframe(vap, m, &eh, m2, &eh2);
if (m == NULL)
goto bad;
} else {
/*
* Normal frame.
*/
- m = ieee80211_mbuf_adjust(ic, hdrsize, key, m);
+ m = ieee80211_mbuf_adjust(vap, hdrspace, key, m);
if (m == NULL) {
/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
goto bad;
@@ -729,15 +974,21 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
}
datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */
- M_PREPEND(m, hdrsize, M_DONTWAIT);
+ M_PREPEND(m, hdrspace, M_DONTWAIT);
if (m == NULL) {
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
goto bad;
}
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
*(uint16_t *)wh->i_dur = 0;
- switch (ic->ic_opmode) {
+ if (is4addr) {
+ wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost);
+ IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost);
+ } else switch (vap->iv_opmode) {
case IEEE80211_M_STA:
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid);
@@ -750,10 +1001,10 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
/*
- * NB: always use the bssid from ic_bss as the
+ * NB: always use the bssid from iv_bss as the
* neighbor's may be stale after an ibss merge
*/
- IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_bssid);
break;
case IEEE80211_M_HOSTAP:
wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
@@ -762,42 +1013,47 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost);
break;
case IEEE80211_M_MONITOR:
- case IEEE80211_M_WDS:
+ case IEEE80211_M_WDS: /* NB: is4addr should always be true */
goto bad;
}
if (m->m_flags & M_MORE_DATA)
wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
if (addqos) {
- struct ieee80211_qosframe *qwh =
- (struct ieee80211_qosframe *) wh;
+ uint8_t *qos;
int ac, tid;
+ if (is4addr) {
+ qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos;
+ } else
+ qos = ((struct ieee80211_qosframe *) wh)->i_qos;
ac = M_WME_GETAC(m);
/* map from access class/queue to 11e header priorty value */
tid = WME_AC_TO_TID(ac);
- qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
+ qos[0] = tid & IEEE80211_QOS_TID;
/*
* 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. On the first
- * frame that goes out 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.
- * ieee80211_ampdu_request handles staggering requests
- * in case the receiver NAK's us or we are otherwise
- * unable to establish a BA stream.
+ * 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) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
+ (vap->iv_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac];
+ ieee80211_txampdu_count_packet(tap);
if (IEEE80211_AMPDU_RUNNING(tap)) {
/*
* Operational, mark frame for aggregation.
*/
- qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
- } else if (!IEEE80211_AMPDU_REQUESTED(tap)) {
+ qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
+ } else if (!IEEE80211_AMPDU_REQUESTED(tap) &&
+ ic->ic_ampdu_enable(ni, tap)) {
/*
* Not negotiated yet, request service.
*/
@@ -806,9 +1062,9 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
}
/* XXX works even when BA marked above */
if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
- qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
- qwh->i_qos[1] = 0;
- qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
+ qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
+ qos[1] = 0;
+ wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
*(uint16_t *)wh->i_seq =
htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
@@ -819,38 +1075,32 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
}
/* check if xmit fragmentation is required */
- txfrag = (m->m_pkthdr.len > ic->ic_fragthreshold &&
+ txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold &&
!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- (ic->ic_caps & IEEE80211_C_TXFRAG) &&
+ (vap->iv_caps & IEEE80211_C_TXFRAG) &&
!isff); /* NB: don't fragment ff's */
if (key != NULL) {
/*
* IEEE 802.1X: send EAPOL frames always in the clear.
* WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
*/
- if (eh.ether_type != htons(ETHERTYPE_PAE) ||
- ((ic->ic_flags & IEEE80211_F_WPA) &&
- (ic->ic_opmode == IEEE80211_M_STA ?
+ if ((m->m_flags & M_EAPOL) == 0 ||
+ ((vap->iv_flags & IEEE80211_F_WPA) &&
+ (vap->iv_opmode == IEEE80211_M_STA ?
!IEEE80211_KEY_UNDEFINED(key) :
!IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)))) {
wh->i_fc[1] |= IEEE80211_FC1_WEP;
- if (!ieee80211_crypto_enmic(ic, key, m, txfrag)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
- "[%s] enmic failed, discard frame\n",
- ether_sprintf(eh.ether_dhost));
- ic->ic_stats.is_crypto_enmicfail++;
+ if (!ieee80211_crypto_enmic(vap, key, m, txfrag)) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT,
+ eh.ether_dhost,
+ "%s", "enmic failed, discard frame");
+ vap->iv_stats.is_crypto_enmicfail++;
goto bad;
}
}
}
- /*
- * NB: frag flags may leak from above; they should only
- * be set on return to the caller if we fragment at
- * the 802.11 layer.
- */
- m->m_flags &= ~(M_FRAG | M_FIRSTFRAG);
- if (txfrag && !ieee80211_fragment(ic, m, hdrsize,
- key != NULL ? key->wk_cipher->ic_header : 0, ic->ic_fragthreshold))
+ if (txfrag && !ieee80211_fragment(vap, m, hdrsize,
+ key != NULL ? key->wk_cipher->ic_header : 0, vap->iv_fragthreshold))
goto bad;
IEEE80211_NODE_STAT(ni, tx_data);
@@ -860,11 +1110,16 @@ ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
IEEE80211_NODE_STAT(ni, tx_ucast);
IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen);
+ /* XXX fragmented frames not handled */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+
return m;
bad:
if (m != NULL)
m_freem(m);
return NULL;
+#undef WH4
}
/*
@@ -875,7 +1130,7 @@ bad:
* type that specifies the payload size).
*/
static struct mbuf *
-ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
+ieee80211_encap1(struct ieee80211vap *vap, struct mbuf *m,
const struct ether_header *eh)
{
struct llc *llc;
@@ -894,9 +1149,9 @@ ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
if (m == NULL) { /* XXX cannot happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for ether_header\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return NULL;
}
ETHER_HEADER_COPY(mtod(m, void *), eh);
@@ -914,7 +1169,7 @@ ieee80211_encap1(struct ieee80211com *ic, struct mbuf *m,
* problem (should not happen).
*/
static struct mbuf *
-ieee80211_encap_fastframe(struct ieee80211com *ic,
+ieee80211_encap_fastframe(struct ieee80211vap *vap,
struct mbuf *m1, const struct ether_header *eh1,
struct mbuf *m2, const struct ether_header *eh2)
{
@@ -925,12 +1180,12 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
/*
* First, each frame gets a standard encapsulation.
*/
- m1 = ieee80211_encap1(ic, m1, eh1);
+ m1 = ieee80211_encap1(vap, m1, eh1);
if (m1 == NULL) {
m_freem(m2);
return NULL;
}
- m2 = ieee80211_encap1(ic, m2, eh2);
+ m2 = ieee80211_encap1(vap, m2, eh2);
if (m2 == NULL) {
m_freem(m1);
return NULL;
@@ -969,18 +1224,18 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
m1->m_pkthdr.len += m2->m_pkthdr.len;
M_PREPEND(m1, sizeof(uint32_t)+2, M_DONTWAIT);
if (m1 == NULL) { /* XXX cannot happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for tunnel header\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return NULL;
}
memset(mtod(m1, void *), 0, sizeof(uint32_t)+2);
M_PREPEND(m1, sizeof(struct llc), M_DONTWAIT);
if (m1 == NULL) { /* XXX cannot happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no space for llc header\n", __func__);
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
return NULL;
}
llc = mtod(m1, struct llc *);
@@ -991,7 +1246,7 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
llc->llc_snap.org_code[2] = ATH_FF_SNAP_ORGCODE_2;
llc->llc_snap.ether_type = htons(ATH_FF_ETH_TYPE);
- ic->ic_stats.is_ff_encap++;
+ vap->iv_stats.is_ff_encap++;
return m1;
}
@@ -1005,7 +1260,7 @@ ieee80211_encap_fastframe(struct ieee80211com *ic,
* packet's mbufs but that is significantly more complicated.
*/
static int
-ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
+ieee80211_fragment(struct ieee80211vap *vap, struct mbuf *m0,
u_int hdrsize, u_int ciphdrsize, u_int mtu)
{
struct ieee80211_frame *wh, *whf;
@@ -1028,6 +1283,7 @@ ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
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)
@@ -1073,8 +1329,8 @@ ieee80211_fragment(struct ieee80211com *ic, struct mbuf *m0,
m_adj(m0, -(m0->m_pkthdr.len - (mtu - ciphdrsize)));
m0->m_flags |= M_FIRSTFRAG | M_FRAG;
- ic->ic_stats.is_tx_fragframes++;
- ic->ic_stats.is_tx_frags += fragno-1;
+ vap->iv_stats.is_tx_fragframes++;
+ vap->iv_stats.is_tx_frags += fragno-1;
return 1;
bad:
@@ -1125,7 +1381,7 @@ ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
}
/*
- * Add an ssid elemet to a frame.
+ * Add an ssid element to a frame.
*/
static uint8_t *
ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
@@ -1157,188 +1413,39 @@ ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic)
return frm;
}
+/*
+ * Add a CFParams element to a frame.
+ */
static uint8_t *
-ieee80211_setup_wpa_ie(struct ieee80211com *ic, uint8_t *ie)
+ieee80211_add_cfparms(uint8_t *frm, struct ieee80211com *ic)
{
-#define WPA_OUI_BYTES 0x00, 0x50, 0xf2
#define ADDSHORT(frm, v) do { \
frm[0] = (v) & 0xff; \
frm[1] = (v) >> 8; \
frm += 2; \
} while (0)
-#define ADDSELECTOR(frm, sel) do { \
- memcpy(frm, sel, 4); \
- frm += 4; \
-} while (0)
- static const uint8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE };
- static const uint8_t cipher_suite[][4] = {
- { WPA_OUI_BYTES, WPA_CSE_WEP40 }, /* NB: 40-bit */
- { WPA_OUI_BYTES, WPA_CSE_TKIP },
- { 0x00, 0x00, 0x00, 0x00 }, /* XXX WRAP */
- { WPA_OUI_BYTES, WPA_CSE_CCMP },
- { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */
- { WPA_OUI_BYTES, WPA_CSE_NULL },
- };
- static const uint8_t wep104_suite[4] =
- { WPA_OUI_BYTES, WPA_CSE_WEP104 };
- static const uint8_t key_mgt_unspec[4] =
- { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
- static const uint8_t key_mgt_psk[4] =
- { WPA_OUI_BYTES, WPA_ASE_8021X_PSK };
- const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
- uint8_t *frm = ie;
- uint8_t *selcnt;
-
- *frm++ = IEEE80211_ELEMID_VENDOR;
- *frm++ = 0; /* length filled in below */
- memcpy(frm, oui, sizeof(oui)); /* WPA OUI */
- frm += sizeof(oui);
- ADDSHORT(frm, WPA_VERSION);
-
- /* XXX filter out CKIP */
-
- /* multicast cipher */
- if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
- rsn->rsn_mcastkeylen >= 13)
- ADDSELECTOR(frm, wep104_suite);
- else
- ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
-
- /* unicast cipher list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
- }
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
- }
-
- /* authenticator selector list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_unspec);
- }
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_psk);
- }
-
- /* optional capabilities */
- if (rsn->rsn_caps != 0 && rsn->rsn_caps != RSN_CAP_PREAUTH)
- ADDSHORT(frm, rsn->rsn_caps);
-
- /* calculate element length */
- ie[1] = frm - ie - 2;
- KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
- ("WPA IE too big, %u > %zu",
- ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+ *frm++ = IEEE80211_ELEMID_CFPARMS;
+ *frm++ = 6;
+ *frm++ = 0; /* CFP count */
+ *frm++ = 2; /* CFP period */
+ ADDSHORT(frm, 0); /* CFP MaxDuration (TU) */
+ ADDSHORT(frm, 0); /* CFP CurRemaining (TU) */
return frm;
#undef ADDSHORT
-#undef ADDSELECTOR
-#undef WPA_OUI_BYTES
}
-static uint8_t *
-ieee80211_setup_rsn_ie(struct ieee80211com *ic, uint8_t *ie)
+static __inline uint8_t *
+add_appie(uint8_t *frm, const struct ieee80211_appie *ie)
{
-#define RSN_OUI_BYTES 0x00, 0x0f, 0xac
-#define ADDSHORT(frm, v) do { \
- frm[0] = (v) & 0xff; \
- frm[1] = (v) >> 8; \
- frm += 2; \
-} while (0)
-#define ADDSELECTOR(frm, sel) do { \
- memcpy(frm, sel, 4); \
- frm += 4; \
-} while (0)
- static const uint8_t cipher_suite[][4] = {
- { RSN_OUI_BYTES, RSN_CSE_WEP40 }, /* NB: 40-bit */
- { RSN_OUI_BYTES, RSN_CSE_TKIP },
- { RSN_OUI_BYTES, RSN_CSE_WRAP },
- { RSN_OUI_BYTES, RSN_CSE_CCMP },
- { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */
- { RSN_OUI_BYTES, RSN_CSE_NULL },
- };
- static const uint8_t wep104_suite[4] =
- { RSN_OUI_BYTES, RSN_CSE_WEP104 };
- static const uint8_t key_mgt_unspec[4] =
- { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
- static const uint8_t key_mgt_psk[4] =
- { RSN_OUI_BYTES, RSN_ASE_8021X_PSK };
- const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
- uint8_t *frm = ie;
- uint8_t *selcnt;
-
- *frm++ = IEEE80211_ELEMID_RSN;
- *frm++ = 0; /* length filled in below */
- ADDSHORT(frm, RSN_VERSION);
-
- /* XXX filter out CKIP */
-
- /* multicast cipher */
- if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
- rsn->rsn_mcastkeylen >= 13)
- ADDSELECTOR(frm, wep104_suite);
- else
- ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
-
- /* unicast cipher list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
- }
- if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
- selcnt[0]++;
- ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
- }
-
- /* authenticator selector list */
- selcnt = frm;
- ADDSHORT(frm, 0); /* selector count */
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_unspec);
- }
- if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
- selcnt[0]++;
- ADDSELECTOR(frm, key_mgt_psk);
- }
-
- /* optional capabilities */
- ADDSHORT(frm, rsn->rsn_caps);
- /* XXX PMKID */
-
- /* calculate element length */
- ie[1] = frm - ie - 2;
- KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
- ("RSN IE too big, %u > %zu",
- ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
- return frm;
-#undef ADDSELECTOR
-#undef ADDSHORT
-#undef RSN_OUI_BYTES
+ memcpy(frm, ie->ie_data, ie->ie_len);
+ return frm + ie->ie_len;
}
-/*
- * Add a WPA/RSN element to a frame.
- */
-static uint8_t *
-ieee80211_add_wpa(uint8_t *frm, struct ieee80211com *ic)
+static __inline uint8_t *
+add_ie(uint8_t *frm, const uint8_t *ie)
{
-
- KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!"));
- if (ic->ic_flags & IEEE80211_F_WPA2)
- frm = ieee80211_setup_rsn_ie(ic, frm);
- if (ic->ic_flags & IEEE80211_F_WPA1)
- frm = ieee80211_setup_wpa_ie(ic, frm);
- return frm;
+ memcpy(frm, ie, 2 + ie[1]);
+ return frm + 2 + ie[1];
}
#define WME_OUI_BYTES 0x00, 0x50, 0xf2
@@ -1432,6 +1539,94 @@ ieee80211_add_ath(uint8_t *frm, uint8_t caps, uint16_t defkeyix)
#undef ATH_OUI_BYTES
/*
+ * Add an 11h Power Constraint element to a frame.
+ */
+static uint8_t *
+ieee80211_add_powerconstraint(uint8_t *frm, struct ieee80211vap *vap)
+{
+ const struct ieee80211_channel *c = vap->iv_bss->ni_chan;
+ /* XXX per-vap tx power limit? */
+ int8_t limit = vap->iv_ic->ic_txpowlimit / 2;
+
+ frm[0] = IEEE80211_ELEMID_PWRCNSTR;
+ frm[1] = 1;
+ frm[2] = c->ic_maxregpower > limit ? c->ic_maxregpower - limit : 0;
+ return frm + 3;
+}
+
+/*
+ * Add an 11h Power Capability element to a frame.
+ */
+static uint8_t *
+ieee80211_add_powercapability(uint8_t *frm, const struct ieee80211_channel *c)
+{
+ frm[0] = IEEE80211_ELEMID_PWRCAP;
+ frm[1] = 2;
+ frm[2] = c->ic_minpower;
+ frm[3] = c->ic_maxpower;
+ return frm + 4;
+}
+
+/*
+ * Add an 11h Supported Channels element to a frame.
+ */
+static uint8_t *
+ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic)
+{
+ static const int ielen = 26;
+
+ frm[0] = IEEE80211_ELEMID_SUPPCHAN;
+ frm[1] = ielen;
+ /* XXX not correct */
+ memcpy(frm+2, ic->ic_chan_avail, ielen);
+ return frm + 2 + ielen;
+}
+
+/*
+ * Add an 11h Channel Switch Announcement element to a frame.
+ * Note that we use the per-vap CSA count to adjust the global
+ * counter so we can use this routine to form probe response
+ * frames and get the current count.
+ */
+static uint8_t *
+ieee80211_add_csa(uint8_t *frm, struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_csa_ie *csa = (struct ieee80211_csa_ie *) frm;
+
+ csa->csa_ie = IEEE80211_ELEMID_CHANSWITCHANN;
+ csa->csa_len = 3;
+ csa->csa_mode = 1; /* XXX force quiet on channel */
+ csa->csa_newchan = ieee80211_chan2ieee(ic, ic->ic_csa_newchan);
+ csa->csa_count = ic->ic_csa_count - vap->iv_csa_count;
+ return frm + sizeof(*csa);
+}
+
+/*
+ * Add an 11h country information element to a frame.
+ */
+static uint8_t *
+ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic)
+{
+
+ if (ic->ic_countryie == NULL ||
+ ic->ic_countryie_chan != ic->ic_bsschan) {
+ /*
+ * Handle lazy construction of ie. This is done on
+ * first use and after a channel change that requires
+ * re-calculation.
+ */
+ if (ic->ic_countryie != NULL)
+ free(ic->ic_countryie, M_80211_NODE_IE);
+ ic->ic_countryie = ieee80211_alloc_countryie(ic);
+ if (ic->ic_countryie == NULL)
+ return frm;
+ ic->ic_countryie_chan = ic->ic_bsschan;
+ }
+ return add_appie(frm, ic->ic_countryie);
+}
+
+/*
* Send a probe request frame with the specified ssid
* and any optional information element data.
*/
@@ -1440,21 +1635,28 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
const uint8_t sa[IEEE80211_ADDR_LEN],
const uint8_t da[IEEE80211_ADDR_LEN],
const uint8_t bssid[IEEE80211_ADDR_LEN],
- const uint8_t *ssid, size_t ssidlen,
- const void *optie, size_t optielen)
+ const uint8_t *ssid, size_t ssidlen)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
const struct ieee80211_rateset *rs;
struct mbuf *m;
uint8_t *frm;
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
+ "block %s frame in CAC state", "probe request");
+ vap->iv_stats.is_tx_badstate++;
+ return EIO; /* XXX */
+ }
+
/*
* Hold a reference on the node so it doesn't go away until after
* the xmit is complete all the way in the driver. On error we
* will remove our reference.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
@@ -1465,18 +1667,23 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
* prreq frame format
* [tlv] ssid
* [tlv] supported rates
+ * [tlv] RSN (optional)
* [tlv] extended supported rates
+ * [tlv] WPA (optional)
* [tlv] user-specified ie's
*/
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
- 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ + sizeof(struct ieee80211_ie_wpa)
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- + (optie != NULL ? optielen : 0)
+ + sizeof(struct ieee80211_ie_wpa)
+ + (vap->iv_appie_probereq != NULL ?
+ vap->iv_appie_probereq->ie_len : 0)
);
if (m == NULL) {
- ic->ic_stats.is_tx_nobuf++;
+ vap->iv_stats.is_tx_nobuf++;
ieee80211_free_node(ni);
return ENOMEM;
}
@@ -1484,62 +1691,70 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
frm = ieee80211_add_ssid(frm, ssid, ssidlen);
rs = ieee80211_get_suprates(ic, ic->ic_curchan);
frm = ieee80211_add_rates(frm, rs);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain? */
+ }
frm = ieee80211_add_xrates(frm, rs);
-
- if (optie != NULL) {
- memcpy(frm, optie, optielen);
- frm += optielen;
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain? */
}
+ if (vap->iv_appie_probereq != NULL)
+ frm = add_appie(frm, vap->iv_appie_probereq);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
if (m == NULL)
return ENOMEM;
- KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
- m->m_pkthdr.rcvif = (void *)ni;
wh = mtod(m, struct ieee80211_frame *);
- ieee80211_send_setup(ic, ni, wh,
+ ieee80211_send_setup(ni, wh,
IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
sa, da, bssid);
/* XXX power management? */
+ M_WME_SETAC(m, WME_AC_BE);
+
IEEE80211_NODE_STAT(ni, tx_probereq);
IEEE80211_NODE_STAT(ni, tx_mgmt);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
- "[%s] send probe req on channel %u\n",
- ether_sprintf(wh->i_addr1),
- ieee80211_chan2ieee(ic, ic->ic_curchan));
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+ "send probe req on channel %u bssid %s ssid \"%.*s\"\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid),
+ ssidlen, ssid);
- IF_ENQUEUE(&ic->ic_mgtq, m);
- if_start(ic->ic_ifp);
- return 0;
+ return ic->ic_raw_xmit(ni, m, NULL);
}
/*
* Calculate capability information for mgt frames.
*/
static uint16_t
-getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan)
+getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan)
{
+ struct ieee80211com *ic = vap->iv_ic;
uint16_t capinfo;
- KASSERT(ic->ic_opmode != IEEE80211_M_STA, ("station mode"));
+ KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode"));
- if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
capinfo = IEEE80211_CAPINFO_ESS;
- else if (ic->ic_opmode == IEEE80211_M_IBSS)
+ else if (vap->iv_opmode == IEEE80211_M_IBSS)
capinfo = IEEE80211_CAPINFO_IBSS;
else
capinfo = 0;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(chan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
if (ic->ic_flags & IEEE80211_F_SHSLOT)
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+ if (IEEE80211_IS_CHAN_5GHZ(chan) && (vap->iv_flags & IEEE80211_F_DOTH))
+ capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
return capinfo;
}
@@ -1549,12 +1764,13 @@ getcapinfo(struct ieee80211com *ic, struct ieee80211_channel *chan)
* count bumped to reflect our use for an indeterminant time.
*/
int
-ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
- int type, int arg)
+ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
{
#define HTFLAGS (IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT)
-#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
- const struct ieee80211_rateset *rs;
+#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_node *bss = vap->iv_bss;
struct mbuf *m;
uint8_t *frm;
uint16_t capinfo;
@@ -1567,7 +1783,7 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* the xmit is complete all the way in the driver. On error we
* will remove our reference.
*/
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
@@ -1575,112 +1791,6 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
ieee80211_ref_node(ni);
switch (type) {
- case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
- /*
- * probe response frame format
- * [8] time stamp
- * [2] beacon interval
- * [2] cabability information
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] parameter set (FH/DS)
- * [tlv] parameter set (IBSS)
- * [tlv] extended rate phy (ERP)
- * [tlv] extended supported rates
- * [tlv] WPA
- * [tlv] WME (optional)
- * [tlv] HT capabilities
- * [tlv] HT information
- * [tlv] Vendor OUI HT capabilities (optional)
- * [tlv] Vendor OUI HT information (optional)
- * [tlv] Atheros capabilities
- */
- m = ieee80211_getmgtframe(&frm,
- ic->ic_headroom + sizeof(struct ieee80211_frame),
- 8
- + sizeof(uint16_t)
- + sizeof(uint16_t)
- + 2 + IEEE80211_NWID_LEN
- + 2 + IEEE80211_RATE_SIZE
- + 7 /* max(7,3) */
- + 6
- + 3
- + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- /* XXX !WPA1+WPA2 fits w/o a cluster */
- + (ic->ic_flags & IEEE80211_F_WPA ?
- 2*sizeof(struct ieee80211_ie_wpa) : 0)
- + sizeof(struct ieee80211_wme_param)
- /* XXX check for cluster requirement */
- + 2*sizeof(struct ieee80211_ie_htcap) + 4
- + 2*sizeof(struct ieee80211_ie_htinfo) + 4
- + sizeof(struct ieee80211_ath_ie)
- );
- if (m == NULL)
- senderr(ENOMEM, is_tx_nobuf);
-
- memset(frm, 0, 8); /* timestamp should be filled later */
- frm += 8;
- *(uint16_t *)frm = htole16(ic->ic_bss->ni_intval);
- frm += 2;
- capinfo = getcapinfo(ic, ic->ic_curchan);
- *(uint16_t *)frm = htole16(capinfo);
- frm += 2;
-
- frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
- ic->ic_bss->ni_esslen);
- rs = ieee80211_get_suprates(ic, ic->ic_curchan);
- frm = ieee80211_add_rates(frm, rs);
-
- if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) {
- *frm++ = IEEE80211_ELEMID_FHPARMS;
- *frm++ = 5;
- *frm++ = ni->ni_fhdwell & 0x00ff;
- *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff;
- *frm++ = IEEE80211_FH_CHANSET(
- ieee80211_chan2ieee(ic, ic->ic_curchan));
- *frm++ = IEEE80211_FH_CHANPAT(
- ieee80211_chan2ieee(ic, ic->ic_curchan));
- *frm++ = ni->ni_fhindex;
- } else {
- *frm++ = IEEE80211_ELEMID_DSPARMS;
- *frm++ = 1;
- *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
- }
-
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
- *frm++ = IEEE80211_ELEMID_IBSSPARMS;
- *frm++ = 2;
- *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
- }
- if (ic->ic_flags & IEEE80211_F_WPA)
- frm = ieee80211_add_wpa(frm, ic);
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
- frm = ieee80211_add_erp(frm, ic);
- frm = ieee80211_add_xrates(frm, rs);
- /*
- * NB: legacy 11b clients do not get certain ie's.
- * The caller identifies such clients by passing
- * a token in arg to us. Could expand this to be
- * any legacy client for stuff like HT ie's.
- */
- if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
- arg != IEEE80211_SEND_LEGACY_11B) {
- frm = ieee80211_add_htcap(frm, ni);
- frm = ieee80211_add_htinfo(frm, ni);
- }
- if (ic->ic_flags & IEEE80211_F_WME)
- frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
- if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
- arg != IEEE80211_SEND_LEGACY_11B) {
- frm = ieee80211_add_htcap_vendor(frm, ni);
- frm = ieee80211_add_htinfo_vendor(frm, ni);
- }
- if (ni->ni_ath_ie != NULL)
- frm = ieee80211_add_ath(frm, ni->ni_ath_flags,
- ni->ni_ath_defkeyix);
- m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
- break;
case IEEE80211_FC0_SUBTYPE_AUTH:
status = arg >> 16;
@@ -1699,10 +1809,10 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
is_shared_key = has_challenge ||
arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
(arg == IEEE80211_AUTH_SHARED_REQUEST &&
- ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED);
+ bss->ni_authmode == IEEE80211_AUTH_SHARED);
m = ieee80211_getmgtframe(&frm,
- ic->ic_headroom + sizeof(struct ieee80211_frame),
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
3 * sizeof(uint16_t)
+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
sizeof(uint16_t)+IEEE80211_CHALLENGE_LEN : 0)
@@ -1725,9 +1835,8 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
m->m_pkthdr.len = m->m_len =
4 * sizeof(uint16_t) + IEEE80211_CHALLENGE_LEN;
if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] request encrypt frame (%s)\n",
- ether_sprintf(ni->ni_macaddr), __func__);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "request encrypt frame (%s)", __func__);
m->m_flags |= M_LINK0; /* WEP-encrypt, please */
}
} else
@@ -1739,15 +1848,14 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
else
IEEE80211_NODE_STAT(ni, tx_auth_fail);
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (vap->iv_opmode == IEEE80211_M_STA)
ieee80211_add_callback(m, ieee80211_tx_mgt_cb,
- (void *) ic->ic_state);
+ (void *) vap->iv_state);
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
- "[%s] send station deauthenticate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), arg);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "send station deauthenticate (reason %d)", arg);
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
sizeof(uint16_t));
@@ -1772,11 +1880,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
- * [tlv] WME
+ * [4] power capability (optional)
+ * [28] supported channels (optional)
* [tlv] HT capabilities
+ * [tlv] WME (optional)
* [tlv] Vendor OUI HT capabilities (optional)
* [tlv] Atheros capabilities (if negotiated)
- * [tlv] user-specified ie's
+ * [tlv] AppIE's (optional)
*/
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
@@ -1786,18 +1896,24 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + 4
+ + 2 + 26
+ sizeof(struct ieee80211_wme_info)
- + 2*sizeof(struct ieee80211_ie_htcap) + 4
+ + sizeof(struct ieee80211_ie_htcap)
+ + 4 + sizeof(struct ieee80211_ie_htcap)
+ sizeof(struct ieee80211_ath_ie)
- + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
+ + (vap->iv_appie_wpa != NULL ?
+ vap->iv_appie_wpa->ie_len : 0)
+ + (vap->iv_appie_assocreq != NULL ?
+ vap->iv_appie_assocreq->ie_len : 0)
);
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("wrong mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", vap->iv_opmode));
capinfo = IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
/*
* NB: Some 11a AP's reject the request when
@@ -1810,50 +1926,63 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
(ic->ic_caps & IEEE80211_C_SHSLOT))
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) &&
- (ic->ic_flags & IEEE80211_F_DOTH))
+ (vap->iv_flags & IEEE80211_F_DOTH))
capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
- KASSERT(ic->ic_bss->ni_intval != 0,
- ("beacon interval is zero!"));
+ KASSERT(bss->ni_intval != 0, ("beacon interval is zero!"));
*(uint16_t *)frm = htole16(howmany(ic->ic_lintval,
- ic->ic_bss->ni_intval));
+ bss->ni_intval));
frm += 2;
if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
- IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid);
+ IEEE80211_ADDR_COPY(frm, bss->ni_bssid);
frm += IEEE80211_ADDR_LEN;
}
frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
frm = ieee80211_add_rates(frm, &ni->ni_rates);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain? */
+ }
frm = ieee80211_add_xrates(frm, &ni->ni_rates);
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
- ni->ni_htcap_ie != NULL &&
- ni->ni_htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
+ if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) {
+ frm = ieee80211_add_powercapability(frm,
+ ic->ic_curchan);
+ frm = ieee80211_add_supportedchannels(frm, ic);
+ }
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) &&
+ ni->ni_ies.htcap_ie != NULL &&
+ ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
frm = ieee80211_add_htcap(frm, ni);
- if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain */
+ }
+ if ((ic->ic_flags & IEEE80211_F_WME) &&
+ ni->ni_ies.wme_ie != NULL)
frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
- if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
- ni->ni_htcap_ie != NULL &&
- ni->ni_htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_HT) &&
+ ni->ni_ies.htcap_ie != NULL &&
+ ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
frm = ieee80211_add_htcap_vendor(frm, ni);
- if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
+ if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS))
frm = ieee80211_add_ath(frm,
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
- (ic->ic_flags & IEEE80211_F_WPA) == 0 &&
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS),
+ (vap->iv_flags & IEEE80211_F_WPA) == 0 &&
ni->ni_authmode != IEEE80211_AUTH_8021X &&
- ic->ic_def_txkey != IEEE80211_KEYIX_NONE ?
- ic->ic_def_txkey : 0x7fff);
- if (ic->ic_opt_ie != NULL) {
- memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
- frm += ic->ic_opt_ie_len;
- }
+ vap->iv_def_txkey != IEEE80211_KEYIX_NONE ?
+ vap->iv_def_txkey : 0x7fff);
+ if (vap->iv_appie_assocreq != NULL)
+ frm = add_appie(frm, vap->iv_appie_assocreq);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
ieee80211_add_callback(m, ieee80211_tx_mgt_cb,
- (void *) ic->ic_state);
+ (void *) vap->iv_state);
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
@@ -1865,10 +1994,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
- * [tlv] WME (if enabled and STA enabled)
- * [tlv] HT capabilities (standard or vendor OUI)
- * [tlv] HT information (standard or vendor OUI)
- * [tlv] Atheros capabilities (if enabled and STA enabled)
+ * [tlv] HT capabilities (standard, if STA enabled)
+ * [tlv] HT information (standard, if STA enabled)
+ * [tlv] WME (if configured and STA enabled)
+ * [tlv] HT capabilities (vendor OUI, if STA enabled)
+ * [tlv] HT information (vendor OUI, if STA enabled)
+ * [tlv] Atheros capabilities (if STA enabled)
+ * [tlv] AppIE's (optional)
*/
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
@@ -1877,15 +2009,17 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+ sizeof(uint16_t)
+ 2 + IEEE80211_RATE_SIZE
+ 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- + sizeof(struct ieee80211_wme_param)
+ sizeof(struct ieee80211_ie_htcap) + 4
+ sizeof(struct ieee80211_ie_htinfo) + 4
+ + sizeof(struct ieee80211_wme_param)
+ sizeof(struct ieee80211_ath_ie)
+ + (vap->iv_appie_assocresp != NULL ?
+ vap->iv_appie_assocresp->ie_len : 0)
);
if (m == NULL)
senderr(ENOMEM, is_tx_nobuf);
- capinfo = getcapinfo(ic, ic->ic_curchan);
+ capinfo = getcapinfo(vap, bss->ni_chan);
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
@@ -1906,23 +2040,25 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
frm = ieee80211_add_htcap(frm, ni);
frm = ieee80211_add_htinfo(frm, ni);
}
- if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+ if ((vap->iv_flags & IEEE80211_F_WME) &&
+ ni->ni_ies.wme_ie != NULL)
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
if ((ni->ni_flags & HTFLAGS) == HTFLAGS) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
- if (IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS))
+ if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS))
frm = ieee80211_add_ath(frm,
- IEEE80211_ATH_CAP(ic, ni, IEEE80211_F_ATHEROS),
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS),
ni->ni_ath_defkeyix);
+ if (vap->iv_appie_assocresp != NULL)
+ frm = add_appie(frm, vap->iv_appie_assocresp);
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
break;
case IEEE80211_FC0_SUBTYPE_DISASSOC:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
- "[%s] send station disassociate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), arg);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "send station disassociate (reason %d)", arg);
m = ieee80211_getmgtframe(&frm,
ic->ic_headroom + sizeof(struct ieee80211_frame),
sizeof(uint16_t));
@@ -1936,17 +2072,13 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
break;
default:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] invalid mgmt frame type %u\n",
- ether_sprintf(ni->ni_macaddr), type);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
+ "invalid mgmt frame type %u", type);
senderr(EINVAL, is_tx_unknownmgt);
/* NOTREACHED */
}
- ret = ieee80211_mgmt_output(ic, ni, m, type);
- if (ret != 0)
- goto bad;
- return 0;
+ return ieee80211_mgmt_output(ni, m, type);
bad:
ieee80211_free_node(ni);
return ret;
@@ -1954,19 +2086,283 @@ bad:
#undef HTFLAGS
}
+/*
+ * Return an mbuf with a probe response frame in it.
+ * Space is left to prepend and 802.11 header at the
+ * front but it's left to the caller to fill in.
+ */
+struct mbuf *
+ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
+{
+ struct ieee80211vap *vap = bss->ni_vap;
+ struct ieee80211com *ic = bss->ni_ic;
+ const struct ieee80211_rateset *rs;
+ struct mbuf *m;
+ uint16_t capinfo;
+ uint8_t *frm;
+
+ /*
+ * probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] parameter set (FH/DS)
+ * [tlv] parameter set (IBSS)
+ * [tlv] country (optional)
+ * [tlv] RSN (optional)
+ * [3] power control (optional)
+ * [5] channel switch announcement (CSA) (optional)
+ * [tlv] extended rate phy (ERP)
+ * [tlv] extended supported rates
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] WPA (optional)
+ * [tlv] WME (optional)
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Vendor OUI HT information (optional)
+ * [tlv] Atheros capabilities
+ * [tlv] AppIE's (optional)
+ */
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame),
+ 8
+ + sizeof(uint16_t)
+ + sizeof(uint16_t)
+ + 2 + IEEE80211_NWID_LEN
+ + 2 + IEEE80211_RATE_SIZE
+ + 7 /* max(7,3) */
+ + IEEE80211_COUNTRY_MAX_SIZE
+ + sizeof(struct ieee80211_ie_wpa)
+ + 3
+ + sizeof(struct ieee80211_csa_ie)
+ + 3
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + sizeof(struct ieee80211_ie_htcap)
+ + sizeof(struct ieee80211_ie_htinfo)
+ + sizeof(struct ieee80211_ie_wpa)
+ + sizeof(struct ieee80211_wme_param)
+ + 4 + sizeof(struct ieee80211_ie_htcap)
+ + 4 + sizeof(struct ieee80211_ie_htinfo)
+ + sizeof(struct ieee80211_ath_ie)
+ + (vap->iv_appie_proberesp != NULL ?
+ vap->iv_appie_proberesp->ie_len : 0)
+ );
+ if (m == NULL) {
+ vap->iv_stats.is_tx_nobuf++;
+ return NULL;
+ }
+
+ memset(frm, 0, 8); /* timestamp should be filled later */
+ frm += 8;
+ *(uint16_t *)frm = htole16(bss->ni_intval);
+ frm += 2;
+ capinfo = getcapinfo(vap, bss->ni_chan);
+ *(uint16_t *)frm = htole16(capinfo);
+ frm += 2;
+
+ frm = ieee80211_add_ssid(frm, bss->ni_essid, bss->ni_esslen);
+ rs = ieee80211_get_suprates(ic, bss->ni_chan);
+ frm = ieee80211_add_rates(frm, rs);
+
+ if (IEEE80211_IS_CHAN_FHSS(bss->ni_chan)) {
+ *frm++ = IEEE80211_ELEMID_FHPARMS;
+ *frm++ = 5;
+ *frm++ = bss->ni_fhdwell & 0x00ff;
+ *frm++ = (bss->ni_fhdwell >> 8) & 0x00ff;
+ *frm++ = IEEE80211_FH_CHANSET(
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+ *frm++ = IEEE80211_FH_CHANPAT(
+ ieee80211_chan2ieee(ic, bss->ni_chan));
+ *frm++ = bss->ni_fhindex;
+ } else {
+ *frm++ = IEEE80211_ELEMID_DSPARMS;
+ *frm++ = 1;
+ *frm++ = ieee80211_chan2ieee(ic, bss->ni_chan);
+ }
+
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ *frm++ = IEEE80211_ELEMID_IBSSPARMS;
+ *frm++ = 2;
+ *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
+ }
+ if ((vap->iv_flags & IEEE80211_F_DOTH) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD))
+ frm = ieee80211_add_countryie(frm, ic);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain? */
+ }
+ if (vap->iv_flags & IEEE80211_F_DOTH) {
+ if (IEEE80211_IS_CHAN_5GHZ(bss->ni_chan))
+ frm = ieee80211_add_powerconstraint(frm, vap);
+ if (ic->ic_flags & IEEE80211_F_CSAPENDING)
+ frm = ieee80211_add_csa(frm, vap);
+ }
+ if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan))
+ frm = ieee80211_add_erp(frm, ic);
+ frm = ieee80211_add_xrates(frm, rs);
+ /*
+ * NB: legacy 11b clients do not get certain ie's.
+ * The caller identifies such clients by passing
+ * a token in legacy to us. Could expand this to be
+ * any legacy client for stuff like HT ie's.
+ */
+ if (IEEE80211_IS_CHAN_HT(bss->ni_chan) &&
+ legacy != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_htcap(frm, bss);
+ frm = ieee80211_add_htinfo(frm, bss);
+ }
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain? */
+ }
+ if (vap->iv_flags & IEEE80211_F_WME)
+ frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+ if (IEEE80211_IS_CHAN_HT(bss->ni_chan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
+ legacy != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_htcap_vendor(frm, bss);
+ frm = ieee80211_add_htinfo_vendor(frm, bss);
+ }
+ if (bss->ni_ies.ath_ie != NULL && legacy != IEEE80211_SEND_LEGACY_11B)
+ frm = ieee80211_add_ath(frm, bss->ni_ath_flags,
+ bss->ni_ath_defkeyix);
+ if (vap->iv_appie_proberesp != NULL)
+ frm = add_appie(frm, vap->iv_appie_proberesp);
+ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+
+ return m;
+}
+
+/*
+ * Send a probe response frame to the specified mac address.
+ * This does not go through the normal mgt frame api so we
+ * can specify the destination address and re-use the bss node
+ * for the sta reference.
+ */
+int
+ieee80211_send_proberesp(struct ieee80211vap *vap,
+ const uint8_t da[IEEE80211_ADDR_LEN], int legacy)
+{
+ struct ieee80211_node *bss = vap->iv_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_frame *wh;
+ struct mbuf *m;
+
+ if (vap->iv_state == IEEE80211_S_CAC) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss,
+ "block %s frame in CAC state", "probe response");
+ vap->iv_stats.is_tx_badstate++;
+ return EIO; /* XXX */
+ }
+
+ /*
+ * Hold a reference on the node so it doesn't go away until after
+ * the xmit is complete all the way in the driver. On error we
+ * will remove our reference.
+ */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
+ __func__, __LINE__, bss, ether_sprintf(bss->ni_macaddr),
+ ieee80211_node_refcnt(bss)+1);
+ ieee80211_ref_node(bss);
+
+ m = ieee80211_alloc_proberesp(bss, legacy);
+ if (m == NULL) {
+ ieee80211_free_node(bss);
+ return ENOMEM;
+ }
+
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ KASSERT(m != NULL, ("no room for header"));
+
+ wh = mtod(m, struct ieee80211_frame *);
+ ieee80211_send_setup(bss, wh,
+ IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP,
+ vap->iv_myaddr, da, bss->ni_bssid);
+ /* XXX power management? */
+
+ M_WME_SETAC(m, WME_AC_BE);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+ "send probe resp on channel %u to %s%s\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(da),
+ legacy ? " <legacy>" : "");
+ IEEE80211_NODE_STAT(bss, tx_mgmt);
+
+ return ic->ic_raw_xmit(bss, m, NULL);
+}
+
+/*
+ * Allocate and build a RTS (Request To Send) control frame.
+ */
+struct mbuf *
+ieee80211_alloc_rts(struct ieee80211com *ic,
+ const uint8_t ra[IEEE80211_ADDR_LEN],
+ const uint8_t ta[IEEE80211_ADDR_LEN],
+ uint16_t dur)
+{
+ struct ieee80211_frame_rts *rts;
+ struct mbuf *m;
+
+ /* XXX honor ic_headroom */
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ rts = mtod(m, struct ieee80211_frame_rts *);
+ rts->i_fc[0] = IEEE80211_FC0_VERSION_0 |
+ IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_RTS;
+ rts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ *(u_int16_t *)rts->i_dur = htole16(dur);
+ IEEE80211_ADDR_COPY(rts->i_ra, ra);
+ IEEE80211_ADDR_COPY(rts->i_ta, ta);
+
+ m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts);
+ }
+ return m;
+}
+
+/*
+ * Allocate and build a CTS (Clear To Send) control frame.
+ */
+struct mbuf *
+ieee80211_alloc_cts(struct ieee80211com *ic,
+ const uint8_t ra[IEEE80211_ADDR_LEN], uint16_t dur)
+{
+ struct ieee80211_frame_cts *cts;
+ struct mbuf *m;
+
+ /* XXX honor ic_headroom */
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ cts = mtod(m, struct ieee80211_frame_cts *);
+ cts->i_fc[0] = IEEE80211_FC0_VERSION_0 |
+ IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_CTS;
+ cts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ *(u_int16_t *)cts->i_dur = htole16(dur);
+ IEEE80211_ADDR_COPY(cts->i_ra, ra);
+
+ m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_cts);
+ }
+ return m;
+}
+
static void
ieee80211_tx_mgt_timeout(void *arg)
{
struct ieee80211_node *ni = arg;
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
- if (ic->ic_state != IEEE80211_S_INIT &&
- (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ 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(ic, IEEE80211_S_SCAN,
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
IEEE80211_SCAN_FAIL_TIMEOUT);
}
}
@@ -1974,7 +2370,7 @@ ieee80211_tx_mgt_timeout(void *arg)
static void
ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
{
- struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
enum ieee80211_state ostate = (enum ieee80211_state) arg;
/*
@@ -1988,27 +2384,20 @@ ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status)
*
* XXX what happens if !acked but response shows up before callback?
*/
- if (ic->ic_state == ostate)
- callout_reset(&ic->ic_mgtsend,
+ if (vap->iv_state == ostate)
+ callout_reset(&vap->iv_mgtsend,
status == 0 ? IEEE80211_TRANS_WAIT*hz : 0,
ieee80211_tx_mgt_timeout, ni);
}
-/*
- * Allocate a beacon frame and fillin the appropriate bits.
- */
-struct mbuf *
-ieee80211_beacon_alloc(struct ieee80211_node *ni,
- struct ieee80211_beacon_offsets *bo)
+static void
+ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
+ struct ieee80211_beacon_offsets *bo, struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_frame *wh;
- struct mbuf *m;
- int pktlen;
- uint8_t *frm;
+ struct ieee80211_rateset *rs = &ni->ni_rates;
uint16_t capinfo;
- struct ieee80211_rateset *rs;
/*
* beacon frame format
@@ -2018,46 +2407,23 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
* [tlv] ssid
* [tlv] supported rates
* [3] parameter set (DS)
+ * [8] CF parameter set (optional)
* [tlv] parameter set (IBSS/TIM)
- * [tlv] country code
+ * [tlv] country (optional)
+ * [tlv] RSN parameters
+ * [3] power control (optional)
+ * [5] channel switch announcement (CSA) (optional)
* [tlv] extended rate phy (ERP)
* [tlv] extended supported rates
- * [tlv] WME parameters
- * [tlv] WPA/RSN parameters
* [tlv] HT capabilities
* [tlv] HT information
+ * XXX Vendor-specific OIDs (e.g. Atheros)
+ * [tlv] WPA parameters
+ * [tlv] WME parameters
* [tlv] Vendor OUI HT capabilities (optional)
* [tlv] Vendor OUI HT information (optional)
- * XXX Vendor-specific OIDs (e.g. Atheros)
- * NB: we allocate the max space required for the TIM bitmap.
+ * [tlv] application data (optional)
*/
- rs = &ni->ni_rates;
- pktlen = 8 /* time stamp */
- + sizeof(uint16_t) /* beacon interval */
- + sizeof(uint16_t) /* capabilities */
- + 2 + ni->ni_esslen /* ssid */
- + 2 + IEEE80211_RATE_SIZE /* supported rates */
- + 2 + 1 /* DS parameters */
- + 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */
- + sizeof(struct ieee80211_country_ie) /* country code */
- + 2 + 1 /* ERP */
- + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
- + (ic->ic_caps & IEEE80211_C_WME ? /* WME */
- sizeof(struct ieee80211_wme_param) : 0)
- + (ic->ic_caps & IEEE80211_C_WPA ? /* WPA 1+2 */
- 2*sizeof(struct ieee80211_ie_wpa) : 0)
- /* XXX conditional? */
- + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */
- + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */
- ;
- m = ieee80211_getmgtframe(&frm,
- ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen);
- if (m == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: cannot get buf; size %u\n", __func__, pktlen);
- ic->ic_stats.is_tx_nobuf++;
- return NULL;
- }
memset(bo, 0, sizeof(*bo));
@@ -2065,68 +2431,169 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
frm += 8;
*(uint16_t *)frm = htole16(ni->ni_intval);
frm += 2;
- capinfo = getcapinfo(ic, ni->ni_chan);
+ capinfo = getcapinfo(vap, ni->ni_chan);
bo->bo_caps = (uint16_t *)frm;
*(uint16_t *)frm = htole16(capinfo);
frm += 2;
*frm++ = IEEE80211_ELEMID_SSID;
- if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) {
+ if ((vap->iv_flags & IEEE80211_F_HIDESSID) == 0) {
*frm++ = ni->ni_esslen;
memcpy(frm, ni->ni_essid, ni->ni_esslen);
frm += ni->ni_esslen;
} else
*frm++ = 0;
frm = ieee80211_add_rates(frm, rs);
- if (!IEEE80211_IS_CHAN_FHSS(ic->ic_bsschan)) {
+ if (!IEEE80211_IS_CHAN_FHSS(ni->ni_chan)) {
*frm++ = IEEE80211_ELEMID_DSPARMS;
*frm++ = 1;
- *frm++ = ieee80211_chan2ieee(ic, ic->ic_bsschan);
+ *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+ }
+ if (ic->ic_flags & IEEE80211_F_PCF) {
+ bo->bo_cfp = frm;
+ frm = ieee80211_add_cfparms(frm, ic);
}
bo->bo_tim = frm;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
*frm++ = IEEE80211_ELEMID_IBSSPARMS;
*frm++ = 2;
*frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
bo->bo_tim_len = 0;
- } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm;
tie->tim_ie = IEEE80211_ELEMID_TIM;
tie->tim_len = 4; /* length */
tie->tim_count = 0; /* DTIM count */
- tie->tim_period = ic->ic_dtim_period; /* DTIM period */
+ tie->tim_period = vap->iv_dtim_period; /* DTIM period */
tie->tim_bitctl = 0; /* bitmap control */
tie->tim_bitmap[0] = 0; /* Partial Virtual Bitmap */
frm += sizeof(struct ieee80211_tim_ie);
bo->bo_tim_len = 1;
}
bo->bo_tim_trailer = frm;
- if (ic->ic_flags & IEEE80211_F_DOTH)
- frm = ieee80211_add_countryie(frm, ic,
- ic->ic_countrycode, ic->ic_location);
- if (ic->ic_flags & IEEE80211_F_WPA)
- frm = ieee80211_add_wpa(frm, ic);
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
+ if ((vap->iv_flags & IEEE80211_F_DOTH) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD))
+ frm = ieee80211_add_countryie(frm, ic);
+ if (vap->iv_flags & IEEE80211_F_WPA2) {
+ if (vap->iv_rsn_ie != NULL)
+ frm = add_ie(frm, vap->iv_rsn_ie);
+ /* XXX else complain */
+ }
+ if (vap->iv_flags & IEEE80211_F_DOTH) {
+ if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan))
+ frm = ieee80211_add_powerconstraint(frm, vap);
+ bo->bo_csa = frm;
+ if (ic->ic_flags & IEEE80211_F_CSAPENDING)
+ frm = ieee80211_add_csa(frm, vap);
+ } else
+ bo->bo_csa = frm;
+ if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) {
bo->bo_erp = frm;
frm = ieee80211_add_erp(frm, ic);
}
frm = ieee80211_add_xrates(frm, rs);
- if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) {
+ 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 (ic->ic_flags & IEEE80211_F_WME) {
+ if (vap->iv_flags & IEEE80211_F_WPA1) {
+ if (vap->iv_wpa_ie != NULL)
+ frm = add_ie(frm, vap->iv_wpa_ie);
+ /* XXX else complain */
+ }
+ if (vap->iv_flags & IEEE80211_F_WME) {
bo->bo_wme = frm;
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
}
- if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan) &&
- (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
frm = ieee80211_add_htcap_vendor(frm, ni);
frm = ieee80211_add_htinfo_vendor(frm, ni);
}
+ if (vap->iv_appie_beacon != NULL) {
+ bo->bo_appie = frm;
+ bo->bo_appie_len = vap->iv_appie_beacon->ie_len;
+ frm = add_appie(frm, vap->iv_appie_beacon);
+ }
bo->bo_tim_trailer_len = frm - bo->bo_tim_trailer;
+ bo->bo_csa_trailer_len = frm - bo->bo_csa;
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+}
+
+/*
+ * Allocate a beacon frame and fillin the appropriate bits.
+ */
+struct mbuf *
+ieee80211_beacon_alloc(struct ieee80211_node *ni,
+ struct ieee80211_beacon_offsets *bo)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct mbuf *m;
+ int pktlen;
+ uint8_t *frm;
+
+ /*
+ * beacon frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [3] parameter set (DS)
+ * [8] CF parameter set (optional)
+ * [tlv] parameter set (IBSS/TIM)
+ * [tlv] country (optional)
+ * [3] power control (optional)
+ * [5] channel switch announcement (CSA) (optional)
+ * [tlv] extended rate phy (ERP)
+ * [tlv] extended supported rates
+ * [tlv] RSN parameters
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Vendor OUI HT information (optional)
+ * XXX Vendor-specific OIDs (e.g. Atheros)
+ * [tlv] WPA parameters
+ * [tlv] WME parameters
+ * [tlv] application data (optional)
+ * NB: we allocate the max space required for the TIM bitmap.
+ * XXX how big is this?
+ */
+ pktlen = 8 /* time stamp */
+ + sizeof(uint16_t) /* beacon interval */
+ + sizeof(uint16_t) /* capabilities */
+ + 2 + ni->ni_esslen /* ssid */
+ + 2 + IEEE80211_RATE_SIZE /* supported rates */
+ + 2 + 1 /* DS parameters */
+ + 2 + 6 /* CF parameters */
+ + 2 + 4 + vap->iv_tim_len /* DTIM/IBSSPARMS */
+ + IEEE80211_COUNTRY_MAX_SIZE /* country */
+ + 2 + 1 /* power control */
+ + sizeof(struct ieee80211_csa_ie) /* CSA */
+ + 2 + 1 /* ERP */
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + (vap->iv_caps & IEEE80211_C_WPA ? /* WPA 1+2 */
+ 2*sizeof(struct ieee80211_ie_wpa) : 0)
+ /* XXX conditional? */
+ + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */
+ + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */
+ + (vap->iv_caps & IEEE80211_C_WME ? /* WME */
+ sizeof(struct ieee80211_wme_param) : 0)
+ + IEEE80211_MAX_APPIE
+ ;
+ m = ieee80211_getmgtframe(&frm,
+ ic->ic_headroom + sizeof(struct ieee80211_frame), pktlen);
+ if (m == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
+ "%s: cannot get buf; size %u\n", __func__, pktlen);
+ vap->iv_stats.is_tx_nobuf++;
+ return NULL;
+ }
+ ieee80211_beacon_construct(m, frm, bo, ni);
M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
KASSERT(m != NULL, ("no space for 802.11 header?"));
@@ -2136,7 +2603,7 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
*(uint16_t *)wh->i_dur = 0;
IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
*(uint16_t *)wh->i_seq = 0;
@@ -2150,16 +2617,46 @@ int
ieee80211_beacon_update(struct ieee80211_node *ni,
struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int len_changed = 0;
uint16_t capinfo;
- IEEE80211_BEACON_LOCK(ic);
+ IEEE80211_LOCK(ic);
+ /*
+ * Handle 11h channel change when we've reached the count.
+ * We must recalculate the beacon frame contents to account
+ * for the new channel. Note we do this only for the first
+ * vap that reaches this point; subsequent vaps just update
+ * their beacon state to reflect the recalculated channel.
+ */
+ if (isset(bo->bo_flags, IEEE80211_BEACON_CSA) &&
+ vap->iv_csa_count == ic->ic_csa_count) {
+ vap->iv_csa_count = 0;
+ /*
+ * Effect channel change before reconstructing the beacon
+ * frame contents as many places reference ni_chan.
+ */
+ if (ic->ic_csa_newchan != NULL)
+ ieee80211_csa_completeswitch(ic);
+ /*
+ * NB: ieee80211_beacon_construct clears all pending
+ * updates in bo_flags so we don't need to explicitly
+ * clear IEEE80211_BEACON_CSA.
+ */
+ ieee80211_beacon_construct(m,
+ mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), bo, ni);
+
+ /* XXX do WME aggressive mode processing? */
+ IEEE80211_UNLOCK(ic);
+ return 1; /* just assume length changed */
+ }
+
/* XXX faster to recalculate entirely or just changes? */
- capinfo = getcapinfo(ic, ni->ni_chan);
+ capinfo = getcapinfo(vap, ni->ni_chan);
*bo->bo_caps = htole16(capinfo);
- if (ic->ic_flags & IEEE80211_F_WME) {
+ if (vap->iv_flags & IEEE80211_F_WME) {
struct ieee80211_wme_state *wme = &ic->ic_wme;
/*
@@ -2172,11 +2669,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
if (wme->wme_flags & WME_F_AGGRMODE) {
if (wme->wme_hipri_traffic >
wme->wme_hipri_switch_thresh) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: traffic %u, disable aggressive mode\n",
__func__, wme->wme_hipri_traffic);
wme->wme_flags &= ~WME_F_AGGRMODE;
- ieee80211_wme_updateparams_locked(ic);
+ ieee80211_wme_updateparams_locked(vap);
wme->wme_hipri_traffic =
wme->wme_hipri_switch_hysteresis;
} else
@@ -2184,11 +2681,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
} else {
if (wme->wme_hipri_traffic <=
wme->wme_hipri_switch_thresh) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: traffic %u, enable aggressive mode\n",
__func__, wme->wme_hipri_traffic);
wme->wme_flags |= WME_F_AGGRMODE;
- ieee80211_wme_updateparams_locked(ic);
+ ieee80211_wme_updateparams_locked(vap);
wme->wme_hipri_traffic = 0;
} else
wme->wme_hipri_traffic =
@@ -2200,12 +2697,12 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
}
}
- if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) {
- ieee80211_ht_update_beacon(ic, bo);
+ if (isset(bo->bo_flags, IEEE80211_BEACON_HTINFO)) {
+ ieee80211_ht_update_beacon(vap, bo);
clrbit(bo->bo_flags, IEEE80211_BEACON_HTINFO);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/
struct ieee80211_tim_ie *tie =
(struct ieee80211_tim_ie *) bo->bo_tim;
if (isset(bo->bo_flags, IEEE80211_BEACON_TIM)) {
@@ -2217,7 +2714,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
* data to make room. Note that we know there is
* contiguous space because ieee80211_beacon_allocate
* insures there is space in the mbuf to write a
- * maximal-size virtual bitmap (based on ic_max_aid).
+ * maximal-size virtual bitmap (based on iv_max_aid).
*/
/*
* Calculate the bitmap size and offset, copy any
@@ -2226,16 +2723,16 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
* Note that the tim bitmap must contain at least
* one byte and any offset must be even.
*/
- if (ic->ic_ps_pending != 0) {
+ if (vap->iv_ps_pending != 0) {
timoff = 128; /* impossibly large */
- for (i = 0; i < ic->ic_tim_len; i++)
- if (ic->ic_tim_bitmap[i]) {
+ for (i = 0; i < vap->iv_tim_len; i++)
+ if (vap->iv_tim_bitmap[i]) {
timoff = i &~ 1;
break;
}
KASSERT(timoff != 128, ("tim bitmap empty!"));
- for (i = ic->ic_tim_len-1; i >= timoff; i--)
- if (ic->ic_tim_bitmap[i])
+ for (i = vap->iv_tim_len-1; i >= timoff; i--)
+ if (vap->iv_tim_bitmap[i])
break;
timlen = 1 + (i - timoff);
} else {
@@ -2250,9 +2747,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
bo->bo_tim_trailer+adjust,
bo->bo_tim_trailer_len);
bo->bo_tim_trailer += adjust;
- bo->bo_wme += adjust;
bo->bo_erp += adjust;
bo->bo_htinfo += adjust;
+ bo->bo_appie += adjust;
+ bo->bo_wme += adjust;
+ bo->bo_csa += adjust;
bo->bo_tim_len = timlen;
/* update information element */
@@ -2260,14 +2759,14 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
tie->tim_bitctl = timoff;
len_changed = 1;
}
- memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff,
+ memcpy(tie->tim_bitmap, vap->iv_tim_bitmap + timoff,
bo->bo_tim_len);
clrbit(bo->bo_flags, IEEE80211_BEACON_TIM);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
"%s: TIM updated, pending %u, off %u, len %u\n",
- __func__, ic->ic_ps_pending, timoff, timlen);
+ __func__, vap->iv_ps_pending, timoff, timlen);
}
/* count down DTIM period */
if (tie->tim_count == 0)
@@ -2279,6 +2778,33 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
tie->tim_bitctl |= 1;
else
tie->tim_bitctl &= ~1;
+ if (isset(bo->bo_flags, IEEE80211_BEACON_CSA)) {
+ struct ieee80211_csa_ie *csa =
+ (struct ieee80211_csa_ie *) bo->bo_csa;
+
+ /*
+ * Insert or update CSA ie. If we're just starting
+ * to count down to the channel switch then we need
+ * to insert the CSA ie. Otherwise we just need to
+ * drop the count. The actual change happens above
+ * when the vap's count reaches the target count.
+ */
+ if (vap->iv_csa_count == 0) {
+ memmove(&csa[1], csa, bo->bo_csa_trailer_len);
+ bo->bo_erp += sizeof(*csa);
+ bo->bo_wme += sizeof(*csa);
+ bo->bo_appie += sizeof(*csa);
+ bo->bo_csa_trailer_len += sizeof(*csa);
+ bo->bo_tim_trailer_len += sizeof(*csa);
+ m->m_len += sizeof(*csa);
+ m->m_pkthdr.len += sizeof(*csa);
+
+ ieee80211_add_csa(bo->bo_csa, vap);
+ } else
+ csa->csa_count--;
+ vap->iv_csa_count++;
+ /* NB: don't clear IEEE80211_BEACON_CSA */
+ }
if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) {
/*
* ERP element needs updating.
@@ -2287,7 +2813,31 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
clrbit(bo->bo_flags, IEEE80211_BEACON_ERP);
}
}
- IEEE80211_BEACON_UNLOCK(ic);
+ if (isset(bo->bo_flags, IEEE80211_BEACON_APPIE)) {
+ const struct ieee80211_appie *aie = vap->iv_appie_beacon;
+ int aielen;
+ uint8_t *frm;
+
+ aielen = 0;
+ if (aie != NULL)
+ aielen += aie->ie_len;
+ if (aielen != bo->bo_appie_len) {
+ /* copy up/down trailer */
+ int adjust = aielen - bo->bo_appie_len;
+ ovbcopy(bo->bo_tim_trailer, bo->bo_tim_trailer+adjust,
+ bo->bo_tim_trailer_len);
+ bo->bo_tim_trailer += adjust;
+ bo->bo_appie += adjust;
+ bo->bo_appie_len = aielen;
+
+ len_changed = 1;
+ }
+ frm = bo->bo_appie;
+ if (aie != NULL)
+ frm = add_appie(frm, aie);
+ clrbit(bo->bo_flags, IEEE80211_BEACON_APPIE);
+ }
+ IEEE80211_UNLOCK(ic);
return len_changed;
}
diff --git a/sys/net80211/ieee80211_phy.c b/sys/net80211/ieee80211_phy.c
new file mode 100644
index 0000000..e1c847f
--- /dev/null
+++ b/sys/net80211/ieee80211_phy.c
@@ -0,0 +1,472 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11 PHY-related support.
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_phy.h>
+
+#ifdef notyet
+struct ieee80211_ds_plcp_hdr {
+ uint8_t i_signal;
+ uint8_t i_service;
+ uint16_t i_length;
+ uint16_t i_crc;
+} __packed;
+
+#endif /* notyet */
+
+/* shorthands to compact tables for readability */
+#define OFDM IEEE80211_T_OFDM
+#define CCK IEEE80211_T_CCK
+#define TURBO IEEE80211_T_TURBO
+#define PBCC (IEEE80211_T_HT+1) /* XXX */
+
+static struct ieee80211_rate_table ieee80211_11b_table = {
+ 4, /* number of rates, XXX no PBCC */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 1 Mb */ { CCK, 1000, 0x00, (0x80| 2), 0 },
+/* 2 Mb */ { CCK, 2000, 0x04, (0x80| 4), 1 },
+/* 5.5 Mb */ { CCK, 5500, 0x04, (0x80|11), 1 },
+/* 11 Mb */ { CCK, 11000, 0x04, (0x80|22), 1 },
+/* 22 Mb */ { PBCC, 22000, 0x04, 44, 3 }
+ },
+};
+
+
+static struct ieee80211_rate_table ieee80211_11g_table = {
+ 12, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 1 Mb */ { CCK, 1000, 0x00, (0x80| 2), 0 },
+/* 2 Mb */ { CCK, 2000, 0x04, (0x80| 4), 1 },
+/* 5.5 Mb */ { CCK, 5500, 0x04, (0x80|11), 2 },
+/* 11 Mb */ { CCK, 11000, 0x04, (0x80|22), 3 },
+/* 6 Mb */ { OFDM, 6000, 0x00, 12, 4 },
+/* 9 Mb */ { OFDM, 9000, 0x00, 18, 4 },
+/* 12 Mb */ { OFDM, 12000, 0x00, 24, 6 },
+/* 18 Mb */ { OFDM, 18000, 0x00, 36, 6 },
+/* 24 Mb */ { OFDM, 24000, 0x00, 48, 8 },
+/* 36 Mb */ { OFDM, 36000, 0x00, 72, 8 },
+/* 48 Mb */ { OFDM, 48000, 0x00, 96, 8 },
+/* 54 Mb */ { OFDM, 54000, 0x00, 108, 8 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_11a_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { OFDM, 6000, 0x00, (0x80|12), 0 },
+/* 9 Mb */ { OFDM, 9000, 0x00, 18, 0 },
+/* 12 Mb */ { OFDM, 12000, 0x00, (0x80|24), 2 },
+/* 18 Mb */ { OFDM, 18000, 0x00, 36, 2 },
+/* 24 Mb */ { OFDM, 24000, 0x00, (0x80|48), 4 },
+/* 36 Mb */ { OFDM, 36000, 0x00, 72, 4 },
+/* 48 Mb */ { OFDM, 48000, 0x00, 96, 4 },
+/* 54 Mb */ { OFDM, 54000, 0x00, 108, 4 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_half_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { OFDM, 3000, 0x00, (0x80| 6), 0 },
+/* 9 Mb */ { OFDM, 4500, 0x00, 9, 0 },
+/* 12 Mb */ { OFDM, 6000, 0x00, (0x80|12), 2 },
+/* 18 Mb */ { OFDM, 9000, 0x00, 18, 2 },
+/* 24 Mb */ { OFDM, 12000, 0x00, (0x80|24), 4 },
+/* 36 Mb */ { OFDM, 18000, 0x00, 36, 4 },
+/* 48 Mb */ { OFDM, 24000, 0x00, 48, 4 },
+/* 54 Mb */ { OFDM, 27000, 0x00, 54, 4 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_quarter_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { OFDM, 1500, 0x00, (0x80| 3), 0 },
+/* 9 Mb */ { OFDM, 2250, 0x00, 4, 0 },
+/* 12 Mb */ { OFDM, 3000, 0x00, (0x80| 6), 2 },
+/* 18 Mb */ { OFDM, 4500, 0x00, 9, 2 },
+/* 24 Mb */ { OFDM, 6000, 0x00, (0x80|12), 4 },
+/* 36 Mb */ { OFDM, 9000, 0x00, 18, 4 },
+/* 48 Mb */ { OFDM, 12000, 0x00, 24, 4 },
+/* 54 Mb */ { OFDM, 13500, 0x00, 27, 4 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_turbog_table = {
+ 7, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { TURBO, 6000, 0x00, (0x80|12), 0 },
+/* 12 Mb */ { TURBO, 12000, 0x00, (0x80|24), 1 },
+/* 18 Mb */ { TURBO, 18000, 0x00, 36, 1 },
+/* 24 Mb */ { TURBO, 24000, 0x00, (0x80|48), 3 },
+/* 36 Mb */ { TURBO, 36000, 0x00, 72, 3 },
+/* 48 Mb */ { TURBO, 48000, 0x00, 96, 3 },
+/* 54 Mb */ { TURBO, 54000, 0x00, 108, 3 }
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_turboa_table = {
+ 8, /* number of rates */
+ { 0 },
+ {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+/* 6 Mb */ { TURBO, 6000, 0x00, (0x80|12), 0 },
+/* 9 Mb */ { TURBO, 9000, 0x00, 18, 0 },
+/* 12 Mb */ { TURBO, 12000, 0x00, (0x80|24), 2 },
+/* 18 Mb */ { TURBO, 18000, 0x00, 36, 2 },
+/* 24 Mb */ { TURBO, 24000, 0x00, (0x80|48), 4 },
+/* 36 Mb */ { TURBO, 36000, 0x00, 72, 4 },
+/* 48 Mb */ { TURBO, 48000, 0x00, 96, 4 },
+/* 54 Mb */ { TURBO, 54000, 0x00, 108, 4 }
+ },
+};
+
+#undef OFDM
+#undef CCK
+#undef TURBO
+#undef XR
+
+/*
+ * Setup a rate table's reverse lookup table and fill in
+ * ack durations. The reverse lookup tables are assumed
+ * to be initialized to zero (or at least the first entry).
+ * We use this as a key that indicates whether or not
+ * we've previously setup the reverse lookup table.
+ *
+ * XXX not reentrant, but shouldn't matter
+ */
+static void
+ieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
+{
+#define N(a) (sizeof(a)/sizeof(a[0]))
+#define WLAN_CTRL_FRAME_SIZE \
+ (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
+
+ int i;
+
+ for (i = 0; i < N(rt->rateCodeToIndex); i++)
+ rt->rateCodeToIndex[i] = (uint8_t) -1;
+ for (i = 0; i < rt->rateCount; i++) {
+ uint8_t code = rt->info[i].dot11Rate;
+ uint8_t cix = rt->info[i].ctlRateIndex;
+ uint8_t ctl_rate = rt->info[cix].dot11Rate;
+
+ rt->rateCodeToIndex[code] = i;
+ if (code & IEEE80211_RATE_BASIC) {
+ /*
+ * Map w/o basic rate bit too.
+ */
+ code &= IEEE80211_RATE_VAL;
+ rt->rateCodeToIndex[code] = i;
+ }
+
+ /*
+ * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
+ * depends on whether they are marked as basic rates;
+ * the static tables are setup with an 11b-compatible
+ * 2Mb/s rate which will work but is suboptimal
+ *
+ * NB: Control rate is always less than or equal to the
+ * current rate, so control rate's reverse lookup entry
+ * has been installed and following call is safe.
+ */
+ rt->info[i].lpAckDuration = ieee80211_compute_duration(rt,
+ WLAN_CTRL_FRAME_SIZE, ctl_rate, 0);
+ rt->info[i].spAckDuration = ieee80211_compute_duration(rt,
+ WLAN_CTRL_FRAME_SIZE, ctl_rate, IEEE80211_F_SHPREAMBLE);
+ }
+
+#undef WLAN_CTRL_FRAME_SIZE
+#undef N
+}
+
+/* Setup all rate tables */
+static void
+ieee80211_phy_init(void)
+{
+#define N(arr) (int)(sizeof(arr) / sizeof(arr[0]))
+ static struct ieee80211_rate_table * const ratetables[] = {
+ &ieee80211_half_table,
+ &ieee80211_quarter_table,
+ &ieee80211_11a_table,
+ &ieee80211_11g_table,
+ &ieee80211_turbog_table,
+ &ieee80211_turboa_table,
+ &ieee80211_turboa_table,
+ &ieee80211_11a_table,
+ &ieee80211_11g_table,
+ &ieee80211_11b_table
+ };
+ int i;
+
+ for (i = 0; i < N(ratetables); ++i)
+ ieee80211_setup_ratetable(ratetables[i]);
+
+#undef N
+}
+SYSINIT(wlan_phy, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_phy_init, NULL);
+
+const struct ieee80211_rate_table *
+ieee80211_get_ratetable(struct ieee80211_channel *c)
+{
+ const struct ieee80211_rate_table *rt;
+
+ /* XXX HT */
+ if (IEEE80211_IS_CHAN_HALF(c))
+ rt = &ieee80211_half_table;
+ else if (IEEE80211_IS_CHAN_QUARTER(c))
+ rt = &ieee80211_quarter_table;
+ else if (IEEE80211_IS_CHAN_HTA(c))
+ rt = &ieee80211_11a_table; /* XXX */
+ else if (IEEE80211_IS_CHAN_HTG(c))
+ rt = &ieee80211_11g_table; /* XXX */
+ else if (IEEE80211_IS_CHAN_108G(c))
+ rt = &ieee80211_turbog_table;
+ else if (IEEE80211_IS_CHAN_ST(c))
+ rt = &ieee80211_turboa_table;
+ else if (IEEE80211_IS_CHAN_TURBO(c))
+ rt = &ieee80211_turboa_table;
+ else if (IEEE80211_IS_CHAN_A(c))
+ rt = &ieee80211_11a_table;
+ else if (IEEE80211_IS_CHAN_ANYG(c))
+ rt = &ieee80211_11g_table;
+ else if (IEEE80211_IS_CHAN_B(c))
+ rt = &ieee80211_11b_table;
+ else {
+ /* NB: should not get here */
+ panic("%s: no rate table for channel; freq %u flags 0x%x\n",
+ __func__, c->ic_freq, c->ic_flags);
+ }
+ return rt;
+}
+
+/*
+ * Convert PLCP signal/rate field to 802.11 rate (.5Mbits/s)
+ *
+ * Note we do no parameter checking; this routine is mainly
+ * used to derive an 802.11 rate for constructing radiotap
+ * header data for rx frames.
+ *
+ * XXX might be a candidate for inline
+ */
+uint8_t
+ieee80211_plcp2rate(uint8_t plcp, int ofdm)
+{
+ if (ofdm) {
+ static const uint8_t ofdm_plcp2rate[16] = {
+ [0xb] = 12,
+ [0xf] = 18,
+ [0xa] = 24,
+ [0xe] = 36,
+ [0x9] = 48,
+ [0xd] = 72,
+ [0x8] = 96,
+ [0xc] = 108
+ };
+ return ofdm_plcp2rate[plcp & 0xf];
+ } else {
+ static const uint8_t cck_plcp2rate[16] = {
+ [0xa] = 2, /* 0x0a */
+ [0x4] = 4, /* 0x14 */
+ [0x7] = 11, /* 0x37 */
+ [0xe] = 22, /* 0x6e */
+ [0xc] = 44, /* 0xdc , actually PBCC */
+ };
+ return cck_plcp2rate[plcp & 0xf];
+ }
+}
+
+/*
+ * Covert 802.11 rate to PLCP signal.
+ */
+uint8_t
+ieee80211_rate2plcp(int rate)
+{
+ switch (rate) {
+ /* CCK rates (returned values are device-dependent) */
+ case 2: return 0x0;
+ case 4: return 0x1;
+ case 11: return 0x2;
+ case 22: return 0x3;
+
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12: return 0xb;
+ case 18: return 0xf;
+ case 24: return 0xa;
+ case 36: return 0xe;
+ case 48: return 0x9;
+ case 72: return 0xd;
+ case 96: return 0x8;
+ case 108: return 0xc;
+ }
+ return 0xff; /* XXX unsupported/unknown rate */
+}
+/*
+ * Compute the time to transmit a frame of length frameLen bytes
+ * using the specified rate, phy, and short preamble setting.
+ * SIFS is included.
+ */
+uint16_t
+ieee80211_compute_duration(const struct ieee80211_rate_table *rt,
+ uint32_t frameLen, uint16_t rate, int isShortPreamble)
+{
+ uint8_t rix = rt->rateCodeToIndex[rate];
+ uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
+ uint32_t kbps;
+
+ KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
+ kbps = rt->info[rix].rateKbps;
+ if (kbps == 0) /* XXX bandaid for channel changes */
+ return 0;
+
+ switch (rt->info[rix].phy) {
+ case IEEE80211_T_CCK:
+#define CCK_SIFS_TIME 10
+#define CCK_PREAMBLE_BITS 144
+#define CCK_PLCP_BITS 48
+ phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
+ if (isShortPreamble && rt->info[rix].shortPreamble)
+ phyTime >>= 1;
+ numBits = frameLen << 3;
+ txTime = CCK_SIFS_TIME + phyTime
+ + ((numBits * 1000)/kbps);
+ break;
+#undef CCK_SIFS_TIME
+#undef CCK_PREAMBLE_BITS
+#undef CCK_PLCP_BITS
+
+ case IEEE80211_T_OFDM:
+#define OFDM_SIFS_TIME 16
+#define OFDM_PREAMBLE_TIME 20
+#define OFDM_PLCP_BITS 22
+#define OFDM_SYMBOL_TIME 4
+
+#define OFDM_SIFS_TIME_HALF 32
+#define OFDM_PREAMBLE_TIME_HALF 40
+#define OFDM_PLCP_BITS_HALF 22
+#define OFDM_SYMBOL_TIME_HALF 8
+
+#define OFDM_SIFS_TIME_QUARTER 64
+#define OFDM_PREAMBLE_TIME_QUARTER 80
+#define OFDM_PLCP_BITS_QUARTER 22
+#define OFDM_SYMBOL_TIME_QUARTER 16
+ if (rt == &ieee80211_half_table) {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("1/2 rate bps"));
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME_QUARTER
+ + OFDM_PREAMBLE_TIME_QUARTER
+ + (numSymbols * OFDM_SYMBOL_TIME_QUARTER);
+ } else if (rt == &ieee80211_quarter_table) {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("1/4 rate bps"));
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME_HALF
+ + OFDM_PREAMBLE_TIME_HALF
+ + (numSymbols * OFDM_SYMBOL_TIME_HALF);
+ } else { /* full rate channel */
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("full rate bps"));
+
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME
+ + OFDM_PREAMBLE_TIME
+ + (numSymbols * OFDM_SYMBOL_TIME);
+ }
+ break;
+
+#undef OFDM_SIFS_TIME
+#undef OFDM_PREAMBLE_TIME
+#undef OFDM_PLCP_BITS
+#undef OFDM_SYMBOL_TIME
+
+ case IEEE80211_T_TURBO:
+#define TURBO_SIFS_TIME 8
+#define TURBO_PREAMBLE_TIME 14
+#define TURBO_PLCP_BITS 22
+#define TURBO_SYMBOL_TIME 4
+ /* we still save OFDM rates in kbps - so double them */
+ bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000;
+ KASSERT(bitsPerSymbol != 0, ("turbo bps"));
+
+ numBits = TURBO_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME
+ + (numSymbols * TURBO_SYMBOL_TIME);
+ break;
+#undef TURBO_SIFS_TIME
+#undef TURBO_PREAMBLE_TIME
+#undef TURBO_PLCP_BITS
+#undef TURBO_SYMBOL_TIME
+
+ default:
+ panic("%s: unknown phy %u (rate %u)\n", __func__,
+ rt->info[rix].phy, rate);
+ break;
+ }
+ return txTime;
+}
diff --git a/sys/net80211/ieee80211_phy.h b/sys/net80211/ieee80211_phy.h
new file mode 100644
index 0000000..2b5adcf
--- /dev/null
+++ b/sys/net80211/ieee80211_phy.h
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NET80211_IEEE80211_PHY_H_
+#define _NET80211_IEEE80211_PHY_H_
+
+#ifdef _KERNEL
+/*
+ * IEEE 802.11 PHY-related definitions.
+ */
+
+/*
+ * Contention window (slots).
+ */
+#define IEEE80211_CW_MAX 1023 /* aCWmax */
+#define IEEE80211_CW_MIN_0 31 /* DS/CCK aCWmin, ERP aCWmin(0) */
+#define IEEE80211_CW_MIN_1 15 /* OFDM aCWmin, ERP aCWmin(1) */
+
+/*
+ * SIFS (microseconds).
+ */
+#define IEEE80211_DUR_SIFS 10 /* DS/CCK/ERP SIFS */
+#define IEEE80211_DUR_OFDM_SIFS 16 /* OFDM SIFS */
+
+/*
+ * Slot time (microseconds).
+ */
+#define IEEE80211_DUR_SLOT 20 /* DS/CCK slottime, ERP long slottime */
+#define IEEE80211_DUR_SHSLOT 9 /* ERP short slottime */
+#define IEEE80211_DUR_OFDM_SLOT 9 /* OFDM slottime */
+
+/*
+ * DIFS (microseconds).
+ */
+#define IEEE80211_DUR_DIFS(sifs, slot) ((sifs) + 2 * (slot))
+
+struct ieee80211_channel;
+
+struct ieee80211_rate_table {
+ int rateCount; /* NB: for proper padding */
+ uint8_t rateCodeToIndex[256]; /* back mapping */
+ struct {
+ uint8_t phy; /* CCK/OFDM/TURBO */
+ uint32_t rateKbps; /* transfer rate in kbs */
+ uint8_t shortPreamble; /* mask for enabling short
+ * preamble in CCK rate code */
+ uint8_t dot11Rate; /* value for supported rates
+ * info element of MLME */
+ uint8_t ctlRateIndex; /* index of next lower basic
+ * rate; used for dur. calcs */
+ uint16_t lpAckDuration; /* long preamble ACK dur. */
+ uint16_t spAckDuration; /* short preamble ACK dur. */
+ } info[32];
+};
+
+const struct ieee80211_rate_table *ieee80211_get_ratetable(
+ struct ieee80211_channel *);
+
+static __inline__ uint8_t
+ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
+{
+ uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+ KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
+ return rt->info[cix].dot11Rate;
+}
+
+static __inline__ uint8_t
+ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
+{
+ uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+ KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
+ return rt->info[cix].dot11Rate;
+}
+
+static __inline__ enum ieee80211_phytype
+ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate)
+{
+ uint8_t rix = rt->rateCodeToIndex[rate];
+ KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
+ return rt->info[rix].phy;
+}
+
+/*
+ * Calculate ACK field for
+ * o non-fragment data frames
+ * o management frames
+ * sent using rate, phy and short preamble setting.
+ */
+static __inline__ uint16_t
+ieee80211_ack_duration(const struct ieee80211_rate_table *rt,
+ uint8_t rate, int isShortPreamble)
+{
+ uint8_t rix = rt->rateCodeToIndex[rate];
+
+ KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
+ if (isShortPreamble) {
+ KASSERT(rt->info[rix].spAckDuration != 0,
+ ("shpreamble ack dur is not computed!\n"));
+ return rt->info[rix].spAckDuration;
+ } else {
+ KASSERT(rt->info[rix].lpAckDuration != 0,
+ ("lgpreamble ack dur is not computed!\n"));
+ return rt->info[rix].lpAckDuration;
+ }
+}
+
+/*
+ * Compute the time to transmit a frame of length frameLen bytes
+ * using the specified 802.11 rate code, phy, and short preamble
+ * setting.
+ *
+ * NB: SIFS is included.
+ */
+uint16_t ieee80211_compute_duration(const struct ieee80211_rate_table *,
+ uint32_t frameLen, uint16_t rate, int isShortPreamble);
+/*
+ * Convert PLCP signal/rate field to 802.11 rate code (.5Mbits/s)
+ */
+uint8_t ieee80211_plcp2rate(uint8_t, int);
+/*
+ * Convert 802.11 rate code to PLCP signal.
+ */
+uint8_t ieee80211_rate2plcp(int);
+#endif /* _KERNEL */
+#endif /* !_NET80211_IEEE80211_PHY_H_ */
diff --git a/sys/net80211/ieee80211_power.c b/sys/net80211/ieee80211_power.c
index e764e1f..2a7dda8 100644
--- a/sys/net80211/ieee80211_power.c
+++ b/sys/net80211/ieee80211_power.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 power save support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -43,43 +45,57 @@ __FBSDID("$FreeBSD$");
#include <net/bpf.h>
-static void ieee80211_set_tim(struct ieee80211_node *ni, int set);
+static void ieee80211_update_ps(struct ieee80211vap *, int);
+static int ieee80211_set_tim(struct ieee80211_node *, int);
+
+MALLOC_DEFINE(M_80211_POWER, "80211power", "802.11 power save state");
void
ieee80211_power_attach(struct ieee80211com *ic)
{
- if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS) {
+}
+
+void
+ieee80211_power_detach(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_power_vattach(struct ieee80211vap *vap)
+{
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
/* NB: driver should override */
- ic->ic_set_tim = ieee80211_set_tim;
+ vap->iv_update_ps = ieee80211_update_ps;
+ vap->iv_set_tim = ieee80211_set_tim;
}
}
void
-ieee80211_power_lateattach(struct ieee80211com *ic)
+ieee80211_power_latevattach(struct ieee80211vap *vap)
{
/*
* Allocate these only if needed. Beware that we
* know adhoc mode doesn't support ATIM yet...
*/
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- ic->ic_tim_len = howmany(ic->ic_max_aid,8) * sizeof(uint8_t);
- MALLOC(ic->ic_tim_bitmap, uint8_t *, ic->ic_tim_len,
- M_DEVBUF, M_NOWAIT | M_ZERO);
- if (ic->ic_tim_bitmap == NULL) {
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ vap->iv_tim_len = howmany(vap->iv_max_aid,8) * sizeof(uint8_t);
+ MALLOC(vap->iv_tim_bitmap, uint8_t *, vap->iv_tim_len,
+ M_80211_POWER, M_NOWAIT | M_ZERO);
+ if (vap->iv_tim_bitmap == NULL) {
printf("%s: no memory for TIM bitmap!\n", __func__);
/* XXX good enough to keep from crashing? */
- ic->ic_tim_len = 0;
+ vap->iv_tim_len = 0;
}
}
}
void
-ieee80211_power_detach(struct ieee80211com *ic)
+ieee80211_power_vdetach(struct ieee80211vap *vap)
{
- if (ic->ic_tim_bitmap != NULL) {
- FREE(ic->ic_tim_bitmap, M_DEVBUF);
- ic->ic_tim_bitmap = NULL;
+ if (vap->iv_tim_bitmap != NULL) {
+ FREE(vap->iv_tim_bitmap, M_80211_POWER);
+ vap->iv_tim_bitmap = NULL;
}
}
@@ -116,12 +132,16 @@ ieee80211_node_saveq_age(struct ieee80211_node *ni)
int discard = 0;
if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) {
+#ifdef IEEE80211_DEBUG
+ struct ieee80211vap *vap = ni->ni_vap;
+#endif
struct mbuf *m;
IEEE80211_NODE_SAVEQ_LOCK(ni);
while (IF_POLL(&ni->ni_savedq, m) != NULL &&
M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
-IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(ni->ni_macaddr), M_AGE_GET(m));/*XXX*/
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "discard frame, age %u", M_AGE_GET(m));
_IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
m_freem(m);
discard++;
@@ -130,7 +150,7 @@ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n"
M_AGE_SUB(m, IEEE80211_INACT_WAIT);
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- IEEE80211_NOTE(ni->ni_ic, IEEE80211_MSG_POWER, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"discard %u frames for age", discard);
IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard);
}
@@ -138,35 +158,52 @@ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n"
}
/*
- * Indicate whether there are frames queued for a station in power-save mode.
+ * Handle a change in the PS station occupancy.
*/
static void
+ieee80211_update_ps(struct ieee80211vap *vap, int nsta)
+{
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS,
+ ("operating mode %u", vap->iv_opmode));
+}
+
+/*
+ * Indicate whether there are frames queued for a station in power-save mode.
+ */
+static int
ieee80211_set_tim(struct ieee80211_node *ni, int set)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
uint16_t aid;
+ int changed;
- KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS,
- ("operating mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS,
+ ("operating mode %u", vap->iv_opmode));
aid = IEEE80211_AID(ni->ni_associd);
- KASSERT(aid < ic->ic_max_aid,
- ("bogus aid %u, max %u", aid, ic->ic_max_aid));
+ KASSERT(aid < vap->iv_max_aid,
+ ("bogus aid %u, max %u", aid, vap->iv_max_aid));
- IEEE80211_BEACON_LOCK(ic);
- if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) {
+ IEEE80211_LOCK(ic);
+ changed = (set != (isset(vap->iv_tim_bitmap, aid) != 0));
+ if (changed) {
if (set) {
- setbit(ic->ic_tim_bitmap, aid);
- ic->ic_ps_pending++;
+ setbit(vap->iv_tim_bitmap, aid);
+ vap->iv_ps_pending++;
} else {
- clrbit(ic->ic_tim_bitmap, aid);
- ic->ic_ps_pending--;
+ clrbit(vap->iv_tim_bitmap, aid);
+ vap->iv_ps_pending--;
}
- /* NB: we know ic is in RUN state so no need to check */
- ic->ic_update_beacon(ic, IEEE80211_BEACON_TIM);
+ /* NB: we know vap is in RUN state so no need to check */
+ vap->iv_update_beacon(vap, IEEE80211_BEACON_TIM);
}
- IEEE80211_BEACON_UNLOCK(ic);
+ IEEE80211_UNLOCK(ic);
+
+ return changed;
}
/*
@@ -177,6 +214,7 @@ ieee80211_set_tim(struct ieee80211_node *ni, int set)
void
ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
{
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int qlen, age;
@@ -184,13 +222,13 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
if (_IF_QFULL(&ni->ni_savedq)) {
_IF_DROP(&ni->ni_savedq);
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "[%s] pwr save q overflow, drops %d (size %d)\n",
- ether_sprintf(ni->ni_macaddr),
- ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
+ "pwr save q overflow, drops %d (size %d)",
+ ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE);
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_dumppkts(ic))
- ieee80211_dump_pkt(ic, mtod(m, caddr_t), m->m_len, -1, -1);
+ if (ieee80211_msg_dumppkts(vap))
+ ieee80211_dump_pkt(ni->ni_ic, mtod(m, caddr_t),
+ m->m_len, -1, -1);
#endif
m_freem(m);
return;
@@ -207,57 +245,26 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
_IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age);
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
- "[%s] save frame with age %d, %u now queued\n",
- ether_sprintf(ni->ni_macaddr), age, qlen);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "save frame with age %d, %u now queued", age, qlen);
- if (qlen == 1 && ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 1);
+ if (qlen == 1 && vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 1);
}
/*
- * Handle station power-save state change.
+ * Unload the frames from the ps q but don't send them
+ * to the driver yet. We do this in two stages to minimize
+ * locking but also because there's no easy way to preserve
+ * ordering given the existing ifnet access mechanisms.
+ * XXX could be optimized
*/
-void
-ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+static void
+pwrsave_flushq(struct ieee80211_node *ni)
{
- struct ieee80211com *ic = ni->ni_ic;
struct mbuf *m, *mhead, *mtail;
int mcount;
- if (enable) {
- if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
- ic->ic_ps_sta++;
- ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
- "power save mode on, %u sta's in ps mode", ic->ic_ps_sta);
- return;
- }
-
- if (ni->ni_flags & IEEE80211_NODE_PWR_MGT)
- ic->ic_ps_sta--;
- ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
- "power save mode off, %u sta's in ps mode", ic->ic_ps_sta);
- /* XXX if no stations in ps mode, flush mc frames */
-
- /*
- * Flush queued unicast frames.
- */
- if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) {
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0); /* just in case */
- return;
- }
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
- "flush ps queue, %u packets queue", IEEE80211_NODE_SAVEQ_QLEN(ni));
- /*
- * Unload the frames from the ps q but don't send them
- * to the driver yet. We do this in two stages to minimize
- * locking but also because there's no easy way to preserve
- * ordering given the existing ifnet access mechanisms.
- * XXX could be optimized
- */
IEEE80211_NODE_SAVEQ_LOCK(ni);
mcount = IEEE80211_NODE_SAVEQ_QLEN(ni);
mhead = mtail = NULL;
@@ -275,26 +282,67 @@ ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
IEEE80211_NODE_SAVEQ_UNLOCK(ni);
if (mhead != NULL) {
/* XXX need different driver interface */
- /* XXX bypasses q max */
- IF_PREPEND_LIST(&ic->ic_ifp->if_snd, mhead, mtail, mcount);
+ /* XXX bypasses q max and OACTIVE */
+ struct ifnet *ifp = ni->ni_vap->iv_ifp;
+ IF_PREPEND_LIST(&ifp->if_snd, mhead, mtail, mcount);
+ if_start(ifp);
+ }
+}
+
+/*
+ * Handle station power-save state change.
+ */
+void
+ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ int update;
+
+ update = 0;
+ if (enable) {
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
+ vap->iv_ps_sta++;
+ update = 1;
+ }
+ ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "power save mode on, %u sta's in ps mode", vap->iv_ps_sta);
+
+ if (update)
+ vap->iv_update_ps(vap, vap->iv_ps_sta);
+ } else {
+ if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
+ vap->iv_ps_sta--;
+ update = 1;
+ }
+ ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+ "power save mode off, %u sta's in ps mode", vap->iv_ps_sta);
+
+ /* NB: order here is intentional so TIM is clear before flush */
+ if (vap->iv_set_tim != NULL)
+ vap->iv_set_tim(ni, 0);
+ if (update) {
+ /* NB if no sta's in ps, driver should flush mc q */
+ vap->iv_update_ps(vap, vap->iv_ps_sta);
+ }
+ pwrsave_flushq(ni);
}
- if (ic->ic_set_tim != NULL)
- ic->ic_set_tim(ni, 0);
}
/*
* Handle power-save state change in station mode.
*/
void
-ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable)
+ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable)
{
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211_node *ni = vap->iv_bss;
int qlen;
if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0)))
return;
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"sta power save mode %s", enable ? "on" : "off");
if (!enable) {
ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
@@ -307,20 +355,9 @@ ieee80211_sta_pwrsave(struct ieee80211com *ic, int enable)
*/
qlen = IEEE80211_NODE_SAVEQ_QLEN(ni);
if (qlen != 0) {
- IEEE80211_NOTE(ic, IEEE80211_MSG_POWER, ni,
+ IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"flush ps queue, %u packets queued", qlen);
- for (;;) {
- struct mbuf *m;
-
- IEEE80211_NODE_SAVEQ_LOCK(ni);
- _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
- IEEE80211_NODE_SAVEQ_UNLOCK(ni);
- if (m == NULL)
- break;
- /* XXX need different driver interface */
- /* XXX bypasses q max */
- IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
- }
+ pwrsave_flushq(ni);
}
} else {
ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
diff --git a/sys/net80211/ieee80211_power.h b/sys/net80211/ieee80211_power.h
index c8461f6..27f5d5a 100644
--- a/sys/net80211/ieee80211_power.h
+++ b/sys/net80211/ieee80211_power.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,14 +30,16 @@
struct ieee80211com;
void ieee80211_power_attach(struct ieee80211com *);
-void ieee80211_power_lateattach(struct ieee80211com *);
void ieee80211_power_detach(struct ieee80211com *);
+void ieee80211_power_vattach(struct ieee80211vap *);
+void ieee80211_power_vdetach(struct ieee80211vap *);
+void ieee80211_power_latevattach(struct ieee80211vap *);
int ieee80211_node_saveq_drain(struct ieee80211_node *);
int ieee80211_node_saveq_age(struct ieee80211_node *);
void ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *);
void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
-void ieee80211_sta_pwrsave(struct ieee80211com *, int enable);
+void ieee80211_sta_pwrsave(struct ieee80211vap *, int enable);
void ieee80211_power_poll(struct ieee80211com *);
#endif /* _NET80211_IEEE80211_POWER_H_ */
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index e67d40c..fe03b15 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,25 +32,32 @@ __FBSDID("$FreeBSD$");
*/
#include "opt_inet.h"
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/taskqueue.h>
#include <sys/socket.h>
+#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h> /* XXX for ether_sprintf */
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_adhoc.h>
+#include <net80211/ieee80211_sta.h>
+#include <net80211/ieee80211_hostap.h>
+#include <net80211/ieee80211_wds.h>
+#include <net80211/ieee80211_monitor.h>
+#include <net80211/ieee80211_input.h>
/* XXX tunables */
#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */
#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */
-#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
-
const char *ieee80211_mgt_subtype_name[] = {
"assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
"probe_req", "probe_resp", "reserved#6", "reserved#7",
@@ -66,11 +73,9 @@ const char *ieee80211_ctl_subtype_name[] = {
const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = {
"IBSS", /* IEEE80211_M_IBSS */
"STA", /* IEEE80211_M_STA */
- "#2",
+ "WDS", /* IEEE80211_M_WDS */
"AHDEMO", /* IEEE80211_M_AHDEMO */
- "#4", "#5",
"HOSTAP", /* IEEE80211_M_HOSTAP */
- "#7",
"MONITOR" /* IEEE80211_M_MONITOR */
};
const char *ieee80211_state_name[IEEE80211_S_MAX] = {
@@ -91,11 +96,19 @@ const char *ieee80211_wme_acnames[] = {
"WME_UPSD",
};
-static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static void parent_updown(void *, int);
+static int ieee80211_new_state_locked(struct ieee80211vap *,
+ enum ieee80211_state, int);
-static void
-null_update_beacon(struct ieee80211com *ic, int item)
+static int
+null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
{
+ struct ifnet *ifp = ni->ni_ic->ic_ifp;
+
+ if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n");
+ m_freem(m);
+ return ENETDOWN;
}
void
@@ -103,54 +116,131 @@ ieee80211_proto_attach(struct ieee80211com *ic)
{
struct ifnet *ifp = ic->ic_ifp;
- /* XXX room for crypto */
- ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4);
-
- ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
- ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT;
- ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
- ic->ic_bmiss_max = IEEE80211_BMISS_MAX;
- callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE);
- callout_init(&ic->ic_mgtsend, CALLOUT_MPSAFE);
- ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT;
+ /* override the 802.3 setting */
+ ifp->if_hdrlen = ic->ic_headroom
+ + sizeof(struct ieee80211_qosframe_addr4)
+ + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN
+ + IEEE80211_WEP_EXTIVLEN;
+ /* XXX no way to recalculate on ifdetach */
+ if (ALIGN(ifp->if_hdrlen) > max_linkhdr) {
+ /* XXX sanity check... */
+ max_linkhdr = ALIGN(ifp->if_hdrlen);
+ max_hdr = max_linkhdr + max_protohdr;
+ max_datalen = MHLEN - max_hdr;
+ }
ic->ic_protmode = IEEE80211_PROT_CTSONLY;
- ic->ic_roaming = IEEE80211_ROAMING_AUTO;
+
+ TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp);
ic->ic_wme.wme_hipri_switch_hysteresis =
AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
- mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF);
-
- /* protocol state change handler */
- ic->ic_newstate = ieee80211_newstate;
- ic->ic_update_beacon = null_update_beacon;
-
/* initialize management frame handlers */
- ic->ic_recv_mgmt = ieee80211_recv_mgmt;
ic->ic_send_mgmt = ieee80211_send_mgmt;
- ic->ic_raw_xmit = ieee80211_raw_xmit;
+ ic->ic_raw_xmit = null_raw_xmit;
+
+ ieee80211_adhoc_attach(ic);
+ ieee80211_sta_attach(ic);
+ ieee80211_wds_attach(ic);
+ ieee80211_hostap_attach(ic);
+ ieee80211_monitor_attach(ic);
}
void
ieee80211_proto_detach(struct ieee80211com *ic)
{
+ ieee80211_monitor_detach(ic);
+ ieee80211_hostap_detach(ic);
+ ieee80211_wds_detach(ic);
+ ieee80211_adhoc_detach(ic);
+ ieee80211_sta_detach(ic);
+}
+
+static void
+null_update_beacon(struct ieee80211vap *vap, int item)
+{
+}
+
+void
+ieee80211_proto_vattach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ int i;
+
+ /* override the 802.3 setting */
+ ifp->if_hdrlen = ic->ic_ifp->if_hdrlen;
+
+ vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT;
+ vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT;
+ vap->iv_bmiss_max = IEEE80211_BMISS_MAX;
+ callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE);
+ callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE);
+ /*
+ * Install default tx rate handling: no fixed rate, lowest
+ * supported rate for mgmt and multicast frames. Default
+ * max retry count. These settings can be changed by the
+ * driver and/or user applications.
+ */
+ for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_11NA; i++) {
+ const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i];
+
+ vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE;
+ /* NB: we default to min supported rate for channel */
+ vap->iv_txparms[i].mgmtrate =
+ rs->rs_rates[0] & IEEE80211_RATE_VAL;
+ vap->iv_txparms[i].mcastrate =
+ rs->rs_rates[0] & IEEE80211_RATE_VAL;
+ vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT;
+ }
+ for (; i < IEEE80211_MODE_MAX; i++) {
+ vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE;
+ /* NB: default to MCS 0 */
+ vap->iv_txparms[i].mgmtrate = 0 | 0x80;
+ vap->iv_txparms[i].mcastrate = 0 | 0x80;
+ vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT;
+ }
+ vap->iv_roaming = IEEE80211_ROAMING_AUTO;
+
+ vap->iv_update_beacon = null_update_beacon;
+ vap->iv_deliver_data = ieee80211_deliver_data;
+
+ /* attach support for operating mode */
+ ic->ic_vattach[vap->iv_opmode](vap);
+}
+void
+ieee80211_proto_vdetach(struct ieee80211vap *vap)
+{
+#define FREEAPPIE(ie) do { \
+ if (ie != NULL) \
+ FREE(ie, M_80211_NODE_IE); \
+} while (0)
+ /*
+ * Detach operating mode module.
+ */
+ if (vap->iv_opdetach != NULL)
+ vap->iv_opdetach(vap);
/*
* This should not be needed as we detach when reseting
* the state but be conservative here since the
* authenticator may do things like spawn kernel threads.
*/
- if (ic->ic_auth->ia_detach)
- ic->ic_auth->ia_detach(ic);
-
- ieee80211_drain_ifq(&ic->ic_mgtq);
- mtx_destroy(&ic->ic_mgtq.ifq_mtx);
-
+ if (vap->iv_auth->ia_detach != NULL)
+ vap->iv_auth->ia_detach(vap);
/*
* Detach any ACL'ator.
*/
- if (ic->ic_acl != NULL)
- ic->ic_acl->iac_detach(ic);
+ if (vap->iv_acl != NULL)
+ vap->iv_acl->iac_detach(vap);
+
+ FREEAPPIE(vap->iv_appie_beacon);
+ FREEAPPIE(vap->iv_appie_probereq);
+ FREEAPPIE(vap->iv_appie_proberesp);
+ FREEAPPIE(vap->iv_appie_assocreq);
+ FREEAPPIE(vap->iv_appie_assocresp);
+ FREEAPPIE(vap->iv_appie_wpa);
+#undef FREEAPPIE
}
/*
@@ -363,16 +453,53 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
struct ieee80211_rateset *nrs, int flags)
{
#define RV(v) ((v) & IEEE80211_RATE_VAL)
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int i, j, rix, error;
- int okrate, badrate, fixedrate;
+ int okrate, badrate, fixedrate, ucastrate;
const struct ieee80211_rateset *srs;
uint8_t r;
error = 0;
okrate = badrate = 0;
+ ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate;
+ if (ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ /*
+ * Workaround awkwardness with fixed rate. We are called
+ * to check both the legacy rate set and the HT rate set
+ * but we must apply any legacy fixed rate check only to the
+ * legacy rate set and vice versa. We cannot tell what type
+ * of rate set we've been given (legacy or HT) but we can
+ * distinguish the fixed rate type (MCS have 0x80 set).
+ * So to deal with this the caller communicates whether to
+ * check MCS or legacy rate using the flags and we use the
+ * type of any fixed rate to avoid applying an MCS to a
+ * legacy rate and vice versa.
+ */
+ if (ucastrate & 0x80) {
+ if (flags & IEEE80211_F_DOFRATE)
+ flags &= ~IEEE80211_F_DOFRATE;
+ } else if ((ucastrate & 0x80) == 0) {
+ if (flags & IEEE80211_F_DOFMCS)
+ flags &= ~IEEE80211_F_DOFMCS;
+ }
+ /* NB: required to make MCS match below work */
+ ucastrate &= IEEE80211_RATE_VAL;
+ }
fixedrate = IEEE80211_FIXED_RATE_NONE;
- srs = ieee80211_get_suprates(ic, ni->ni_chan);
+ /*
+ * XXX we are called to process both MCS and legacy rates;
+ * we must use the appropriate basic rate set or chaos will
+ * ensue; for now callers that want MCS must supply
+ * IEEE80211_F_DOBRS; at some point we'll need to split this
+ * function so there are two variants, one for MCS and one
+ * for legacy rates.
+ */
+ if (flags & IEEE80211_F_DOBRS)
+ srs = (const struct ieee80211_rateset *)
+ ieee80211_get_suphtrates(ic, ni->ni_chan);
+ else
+ srs = ieee80211_get_suprates(ic, ni->ni_chan);
for (i = 0; i < nrs->rs_nrates; ) {
if (flags & IEEE80211_F_DOSORT) {
/*
@@ -391,7 +518,7 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
/*
* Check for fixed rate.
*/
- if (r == ic->ic_fixed_rate)
+ if (r == ucastrate)
fixedrate = r;
/*
* Check against supported rates.
@@ -431,9 +558,13 @@ ieee80211_fix_rate(struct ieee80211_node *ni,
i++;
}
if (okrate == 0 || error != 0 ||
- ((flags & IEEE80211_F_DOFRATE) && fixedrate != ic->ic_fixed_rate))
+ ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) &&
+ fixedrate != ucastrate)) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni,
+ "%s: flags 0x%x okrate %d error %d fixedrate 0x%x "
+ "ucastrate %x\n", __func__, fixedrate, ucastrate, flags);
return badrate | IEEE80211_RATE_BASIC;
- else
+ } else
return RV(okrate);
#undef RV
}
@@ -491,7 +622,7 @@ ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff)
* NB: the rate set is assumed to be sorted.
*/
int
-ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs)
+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 };
@@ -516,14 +647,15 @@ ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs)
}
/*
- * Mark the basic rates for the 11g rate table based on the
+ * Mark the basic rates for the rate table based on the
* operating mode. For real 11g we mark all the 11b rates
* and 6, 12, and 24 OFDM. For 11b compatibility we mark only
* 11b rates. There's also a pseudo 11a-mode used to mark only
* the basic OFDM rates.
*/
-void
-ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
+static void
+setbasicrates(struct ieee80211_rateset *rs,
+ enum ieee80211_phymode mode, int add)
{
static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = {
{ .rs_nrates = 0 }, /* IEEE80211_MODE_AUTO */
@@ -531,16 +663,17 @@ ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode
{ 2, { 2, 4 } }, /* IEEE80211_MODE_11B */
{ 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */
{ .rs_nrates = 0 }, /* IEEE80211_MODE_FH */
- /* IEEE80211_MODE_PUREG (not yet) */
- { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_TURBO_A */
+ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_TURBO_G (mixed b/g) */
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_STURBO_A */
{ 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */
- /* IEEE80211_MODE_11NG (mixed b/g) */
- { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11NG (mixed b/g) */
};
int i, j;
for (i = 0; i < rs->rs_nrates; i++) {
- rs->rs_rates[i] &= IEEE80211_RATE_VAL;
+ if (!add)
+ rs->rs_rates[i] &= IEEE80211_RATE_VAL;
for (j = 0; j < basic[mode].rs_nrates; j++)
if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
@@ -550,14 +683,40 @@ ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode
}
/*
- * WME protocol support. The following parameters come from the spec.
+ * Set the basic rates in a rate set.
+ */
+void
+ieee80211_setbasicrates(struct ieee80211_rateset *rs,
+ enum ieee80211_phymode mode)
+{
+ setbasicrates(rs, mode, 0);
+}
+
+/*
+ * Add basic rates to a rate set.
+ */
+void
+ieee80211_addbasicrates(struct ieee80211_rateset *rs,
+ enum ieee80211_phymode mode)
+{
+ setbasicrates(rs, mode, 1);
+}
+
+/*
+ * WME protocol support.
+ *
+ * The default 11a/b/g/n parameters come from the WiFi Alliance WMM
+ * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n
+ * Draft 2.0 Test Plan (Appendix D).
+ *
+ * Static/Dynamic Turbo mode settings come from Atheros.
*/
typedef struct phyParamType {
- uint8_t aifsn;
- uint8_t logcwmin;
- uint8_t logcwmax;
- uint16_t txopLimit;
- uint8_t acm;
+ uint8_t aifsn;
+ uint8_t logcwmin;
+ uint8_t logcwmax;
+ uint16_t txopLimit;
+ uint8_t acm;
} paramType;
static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
@@ -646,15 +805,18 @@ static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = {
{ 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */
};
-void
-ieee80211_wme_initparams(struct ieee80211com *ic)
+static void
+ieee80211_wme_initparams_locked(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
const paramType *pPhyParam, *pBssPhyParam;
struct wmeParams *wmep;
enum ieee80211_phymode mode;
int i;
+ IEEE80211_LOCK_ASSERT(ic);
+
if ((ic->ic_caps & IEEE80211_C_WME) == 0)
return;
@@ -704,7 +866,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
}
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s chan [acm %u aifsn %u log2(cwmin) %u "
"log2(cwmax) %u txpoLimit %u]\n", __func__
, ieee80211_wme_acnames[i]
@@ -721,7 +883,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
wmep->wmep_logcwmin = pBssPhyParam->logcwmin;
wmep->wmep_logcwmax = pBssPhyParam->logcwmax;
wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s bss [acm %u aifsn %u log2(cwmin) %u "
"log2(cwmax) %u txpoLimit %u]\n", __func__
, ieee80211_wme_acnames[i]
@@ -733,7 +895,7 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
);
}
/* NB: check ic_bss to avoid NULL deref on initial attach */
- if (ic->ic_bss != NULL) {
+ if (vap->iv_bss != NULL) {
/*
* Calculate agressive mode switching threshold based
* on beacon interval. This doesn't need locking since
@@ -741,16 +903,26 @@ ieee80211_wme_initparams(struct ieee80211com *ic)
* which point we start sending beacon frames.
*/
wme->wme_hipri_switch_thresh =
- (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100;
- ieee80211_wme_updateparams(ic);
+ (HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100;
+ ieee80211_wme_updateparams(vap);
}
}
+void
+ieee80211_wme_initparams(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ ieee80211_wme_initparams_locked(vap);
+ IEEE80211_UNLOCK(ic);
+}
+
/*
* Update WME parameters for ourself and the BSS.
*/
void
-ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
+ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
{
static const paramType phyParam[IEEE80211_MODE_MAX] = {
{ 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */
@@ -764,6 +936,7 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
{ 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/
{ 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/
};
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
const struct wmeParams *wmep;
struct wmeParams *chanp, *bssp;
@@ -806,11 +979,11 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
* BE uses agressive params to optimize performance of
* legacy/non-QoS traffic.
*/
- if ((ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ if ((vap->iv_opmode == IEEE80211_M_HOSTAP &&
(wme->wme_flags & WME_F_AGGRMODE) != 0) ||
- (ic->ic_opmode == IEEE80211_M_STA &&
- (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
- (ic->ic_flags & IEEE80211_F_WME) == 0) {
+ (vap->iv_opmode == IEEE80211_M_STA &&
+ (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
+ (vap->iv_flags & IEEE80211_F_WME) == 0) {
chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
@@ -820,9 +993,9 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
chanp->wmep_logcwmax = bssp->wmep_logcwmax =
phyParam[mode].logcwmax;
chanp->wmep_txopLimit = bssp->wmep_txopLimit =
- (ic->ic_flags & IEEE80211_F_BURST) ?
+ (vap->iv_flags & IEEE80211_F_BURST) ?
phyParam[mode].txopLimit : 0;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s [acm %u aifsn %u log2(cwmin) %u "
"log2(cwmax) %u txpoLimit %u]\n", __func__
, ieee80211_wme_acnames[WME_AC_BE]
@@ -834,7 +1007,8 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ /* XXX multi-bss */
+ 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] = {
3, /* IEEE80211_MODE_AUTO */
@@ -852,77 +1026,238 @@ ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode];
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: %s log2(cwmin) %u\n", __func__
, ieee80211_wme_acnames[WME_AC_BE]
, chanp->wmep_logcwmin
);
}
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */
/*
* Arrange for a beacon update and bump the parameter
* set number so associated stations load the new values.
*/
wme->wme_bssChanParams.cap_info =
(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
- ieee80211_beacon_notify(ic, IEEE80211_BEACON_WME);
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME);
}
wme->wme_update(ic);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: WME params updated, cap_info 0x%x\n", __func__,
- ic->ic_opmode == IEEE80211_M_STA ?
+ vap->iv_opmode == IEEE80211_M_STA ?
wme->wme_wmeChanParams.cap_info :
wme->wme_bssChanParams.cap_info);
}
void
-ieee80211_wme_updateparams(struct ieee80211com *ic)
+ieee80211_wme_updateparams(struct ieee80211vap *vap)
{
+ struct ieee80211com *ic = vap->iv_ic;
if (ic->ic_caps & IEEE80211_C_WME) {
- IEEE80211_BEACON_LOCK(ic);
- ieee80211_wme_updateparams_locked(ic);
- IEEE80211_BEACON_UNLOCK(ic);
+ IEEE80211_LOCK(ic);
+ ieee80211_wme_updateparams_locked(vap);
+ IEEE80211_UNLOCK(ic);
}
}
+static void
+parent_updown(void *arg, int npending)
+{
+ struct ifnet *parent = arg;
+
+ parent->if_ioctl(parent, SIOCSIFFLAGS, NULL);
+}
+
/*
- * Start a device. If this is the first vap running on the
- * underlying device then we first bring it up.
+ * 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.
*/
-int
-ieee80211_init(struct ieee80211com *ic, int forcescan)
+void
+ieee80211_start_locked(struct ieee80211vap *vap)
{
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *parent = ic->ic_ifp;
- IEEE80211_DPRINTF(ic,
+ IEEE80211_LOCK_ASSERT(ic);
+
+ IEEE80211_DPRINTF(vap,
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
- "%s\n", "start running");
+ "start running, %d vaps running\n", ic->ic_nrunning);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ /*
+ * Mark us running. Note that it's ok to do this first;
+ * if we need to bring the parent device up we defer that
+ * to avoid dropping the com lock. We expect the device
+ * to respond to being marked up by calling back into us
+ * through ieee80211_start_all at which point we'll come
+ * back in here and complete the work.
+ */
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ /*
+ * We are not running; if this we are the first vap
+ * to be brought up auto-up the parent if necessary.
+ */
+ if (ic->ic_nrunning++ == 0 &&
+ (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s: up parent %s\n", __func__, parent->if_xname);
+ parent->if_flags |= IFF_UP;
+ taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task);
+ return;
+ }
+ }
/*
- * Kick the 802.11 state machine as appropriate.
+ * If the parent is up and running, then kick the
+ * 802.11 state machine as appropriate.
*/
- if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) {
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ if ((parent->if_drv_flags & IFF_DRV_RUNNING) &&
+ vap->iv_roaming != IEEE80211_ROAMING_MANUAL) {
+ if (vap->iv_opmode == IEEE80211_M_STA) {
+#if 0
+ /* XXX bypasses scan too easily; disable for now */
+ /*
+ * Try to be intelligent about clocking the state
+ * machine. If we're currently in RUN state then
+ * we should be able to apply any new state/parameters
+ * simply by re-associating. Otherwise we need to
+ * re-scan to select an appropriate ap.
+ */
+ if (vap->iv_state >= IEEE80211_S_RUN)
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_ASSOC, 1);
+ else
+#endif
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_SCAN, 0);
} else {
/*
- * For monitor+wds modes there's nothing to do but
- * start running. Otherwise, if this is the first
+ * For monitor+wds mode there's nothing to do but
+ * start running. Otherwise if this is the first
* vap to be brought up, start a scan which may be
* preempted if the station is locked to a particular
* channel.
*/
- if (ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_WDS) {
- ic->ic_state = IEEE80211_S_INIT; /* XXX*/
- ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
- } else
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ /* XXX needed? */
+ ieee80211_new_state_locked(vap, IEEE80211_S_INIT, 0);
+ if (vap->iv_opmode == IEEE80211_M_MONITOR ||
+ vap->iv_opmode == IEEE80211_M_WDS)
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_RUN, -1);
+ else
+ ieee80211_new_state_locked(vap,
+ IEEE80211_S_SCAN, 0);
}
}
- return 0;
+}
+
+/*
+ * Start a single vap.
+ */
+void
+ieee80211_init(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+
+ /*
+ * This routine is publicly accessible through the vap's
+ * if_init method so guard against calls during detach.
+ * ieee80211_vap_detach null's the backpointer before
+ * tearing down state to signal any callback should be
+ * rejected/ignored.
+ */
+ if (vap != NULL) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s\n", __func__);
+
+ IEEE80211_LOCK(vap->iv_ic);
+ ieee80211_start_locked(vap);
+ IEEE80211_UNLOCK(vap->iv_ic);
+ }
+}
+
+/*
+ * Start all runnable vap's on a device.
+ */
+void
+ieee80211_start_all(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK(ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ifnet *ifp = vap->iv_ifp;
+ if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */
+ ieee80211_start_locked(vap);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Stop a vap. We force it down using the state machine
+ * then mark it's ifnet not running. If this is the last
+ * vap running on the underlying device then we close it
+ * too to insure it will be properly initialized when the
+ * next vap is brought up.
+ */
+void
+ieee80211_stop_locked(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ifnet *parent = ic->ic_ifp;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "stop running, %d vaps running\n", ic->ic_nrunning);
+
+ ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* mark us stopped */
+ if (--ic->ic_nrunning == 0 &&
+ (parent->if_drv_flags & IFF_DRV_RUNNING)) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "down parent %s\n", parent->if_xname);
+ parent->if_flags &= ~IFF_UP;
+ taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task);
+ }
+ }
+}
+
+void
+ieee80211_stop(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ ieee80211_stop_locked(vap);
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Stop all vap's running on a device.
+ */
+void
+ieee80211_stop_all(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK(ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ struct ifnet *ifp = vap->iv_ifp;
+ if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */
+ ieee80211_stop_locked(vap);
+ }
+ IEEE80211_UNLOCK(ic);
}
/*
@@ -932,19 +1267,20 @@ ieee80211_init(struct ieee80211com *ic, int forcescan)
* the driver to effect the change.
*/
void
-ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags)
+ieee80211_dturbo_switch(struct ieee80211vap *vap, int newflags)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *chan;
chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags);
if (chan == NULL) { /* XXX should not happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: no channel with freq %u flags 0x%x\n",
__func__, ic->ic_bsschan->ic_freq, newflags);
return;
}
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG,
"%s: %s -> %s (freq %u flags 0x%x)\n", __func__,
ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)],
ieee80211_phymode_name[ieee80211_chan2mode(chan)],
@@ -960,57 +1296,21 @@ ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags)
void
ieee80211_beacon_miss(struct ieee80211com *ic)
{
+ struct ieee80211vap *vap;
- if (ic->ic_flags & IEEE80211_F_SCAN) {
- /* XXX check ic_curchan != ic_bsschan? */
+ if (ic->ic_flags & IEEE80211_F_SCAN)
return;
- }
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
- "%s\n", "beacon miss");
-
- /*
- * Our handling is only meaningful for stations that are
- * associated; any other conditions else will be handled
- * through different means (e.g. the tx timeout on mgt frames).
- */
- if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_RUN)
- return;
-
- if (++ic->ic_bmiss_count < ic->ic_bmiss_max) {
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
/*
- * Send a directed probe req before falling back to a scan;
- * if we receive a response ic_bmiss_count will be reset.
- * Some cards mistakenly report beacon miss so this avoids
- * the expensive scan if the ap is still there.
+ * 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.
*/
- ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr,
- ic->ic_bss->ni_bssid, ic->ic_bss->ni_bssid,
- ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- return;
- }
- ic->ic_bmiss_count = 0;
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
- /*
- * If we receive a beacon miss interrupt when using
- * dynamic turbo, attempt to switch modes before
- * reassociating.
- */
- if (IEEE80211_ATH_CAP(ic, ic->ic_bss, IEEE80211_NODE_TURBOP))
- ieee80211_dturbo_switch(ic,
- ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
- /*
- * Try to reassociate before scanning for a new ap.
- */
- ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1);
- } else {
- /*
- * Somebody else is controlling state changes (e.g.
- * a user-mode app) don't do anything that would
- * confuse them; just drop into scan mode so they'll
- * notified of the state change and given control.
- */
- ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_state == IEEE80211_S_RUN &&
+ vap->iv_bmiss != NULL)
+ vap->iv_bmiss(vap);
}
}
@@ -1019,377 +1319,378 @@ ieee80211_beacon_miss(struct ieee80211com *ic)
* were received in the last period. If not post a
* beacon miss; otherwise reset the counter.
*/
-static void
+void
ieee80211_swbmiss(void *arg)
{
- struct ieee80211com *ic = arg;
+ struct ieee80211vap *vap = arg;
- if (ic->ic_swbmiss_count == 0) {
- ieee80211_beacon_miss(ic);
- if (ic->ic_bmiss_count == 0) /* don't re-arm timer */
+ if (vap->iv_swbmiss_count == 0) {
+ if (vap->iv_bmiss != NULL)
+ vap->iv_bmiss(vap);
+ if (vap->iv_bmiss_count == 0) /* don't re-arm timer */
return;
} else
- ic->ic_swbmiss_count = 0;
- callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period,
- ieee80211_swbmiss, ic);
+ vap->iv_swbmiss_count = 0;
+ callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
+ ieee80211_swbmiss, vap);
+}
+
+/*
+ * Start an 802.11h channel switch. We record the parameters,
+ * mark the operation pending, notify each vap through the
+ * beacon update mechanism so it can update the beacon frame
+ * contents, and then switch vap's to CSA state to block outbound
+ * traffic. Devices that handle CSA directly can use the state
+ * switch to do the right thing so long as they call
+ * ieee80211_csa_completeswitch when it's time to complete the
+ * channel change. Devices that depend on the net80211 layer can
+ * use ieee80211_beacon_update to handle the countdown and the
+ * channel switch.
+ */
+void
+ieee80211_csa_startswitch(struct ieee80211com *ic,
+ struct ieee80211_channel *c, int mode, int count)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ic->ic_csa_newchan = c;
+ ic->ic_csa_count = count;
+ /* XXX record mode? */
+ ic->ic_flags |= IEEE80211_F_CSAPENDING;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS)
+ ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA);
+ /* switch to CSA state to block outbound traffic */
+ if (vap->iv_state == IEEE80211_S_RUN)
+ ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0);
+ }
+ ieee80211_notify_csa(ic, c, mode, count);
+}
+
+/*
+ * Complete an 802.11h channel switch started by ieee80211_csa_startswitch.
+ * We clear state and move all vap's in CSA state to RUN state
+ * so they can again transmit.
+ */
+void
+ieee80211_csa_completeswitch(struct ieee80211com *ic)
+{
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending"));
+
+ ieee80211_setcurchan(ic, ic->ic_csa_newchan);
+ ic->ic_csa_newchan = NULL;
+ ic->ic_flags &= ~IEEE80211_F_CSAPENDING;
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_state == IEEE80211_S_CSA)
+ ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
+}
+
+/*
+ * Complete a DFS CAC started by ieee80211_dfs_cac_start.
+ * We clear state and move all vap's in CAC state to RUN state.
+ */
+void
+ieee80211_cac_completeswitch(struct ieee80211vap *vap0)
+{
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK(ic);
+ /*
+ * Complete CAC state change for lead vap first; then
+ * clock all the other vap's waiting.
+ */
+ KASSERT(vap0->iv_state == IEEE80211_S_CAC,
+ ("wrong state %d", vap0->iv_state));
+ ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0);
+
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_state == IEEE80211_S_CAC)
+ ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
+ IEEE80211_UNLOCK(ic);
}
+/*
+ * Force all vap's other than the specified vap to the INIT state
+ * and mark them as waiting for a scan to complete. These vaps
+ * will be brought up when the scan completes and the scanning vap
+ * reaches RUN state by wakeupwaiting.
+ * XXX if we do this in threads we can use sleep/wakeup.
+ */
static void
-sta_disassoc(void *arg, struct ieee80211_node *ni)
+markwaiting(struct ieee80211vap *vap0)
{
- struct ieee80211com *ic = arg;
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
- if (ni->ni_associd != 0) {
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_ASSOC_LEAVE);
- ieee80211_node_leave(ic, ni);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap == vap0)
+ continue;
+ if (vap->iv_state != IEEE80211_S_INIT) {
+ vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
+ }
}
}
+/*
+ * Wakeup all vap's waiting for a scan to complete. This is the
+ * companion to markwaiting (above) and is used to coordinate
+ * multiple vaps scanning.
+ */
static void
-sta_deauth(void *arg, struct ieee80211_node *ni)
+wakeupwaiting(struct ieee80211vap *vap0)
{
- struct ieee80211com *ic = arg;
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ieee80211vap *vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_ASSOC_LEAVE);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap == vap0)
+ continue;
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) {
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
+ /* NB: sta's cannot go INIT->RUN */
+ vap->iv_newstate(vap,
+ vap->iv_opmode == IEEE80211_M_STA ?
+ IEEE80211_S_SCAN : IEEE80211_S_RUN, 0);
+ }
+ }
}
/*
- * Handle deauth with reason. We retry only for
- * the cases where we might succeed. Otherwise
- * we downgrade the ap and scan.
+ * Handle post state change work common to all operating modes.
*/
static void
-sta_authretry(struct ieee80211com *ic, struct ieee80211_node *ni, int reason)
+ieee80211_newstate_cb(struct ieee80211vap *vap,
+ enum ieee80211_state nstate, int arg)
{
- switch (reason) {
- case IEEE80211_STATUS_SUCCESS:
- case IEEE80211_STATUS_TIMEOUT:
- case IEEE80211_REASON_ASSOC_EXPIRE:
- case IEEE80211_REASON_NOT_AUTHED:
- case IEEE80211_REASON_NOT_ASSOCED:
- case IEEE80211_REASON_ASSOC_LEAVE:
- case IEEE80211_REASON_ASSOC_NOT_AUTHED:
- IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
- break;
- default:
- ieee80211_scan_assoc_fail(ic, ic->ic_bss->ni_macaddr, reason);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid, ic->ic_des_ssid);
- break;
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s arg %d\n", __func__, ieee80211_state_name[nstate], arg);
+
+ if (nstate == IEEE80211_S_RUN) {
+ /*
+ * OACTIVE may be set on the vap if the upper layer
+ * tried to transmit (e.g. IPv6 NDP) before we reach
+ * RUN state. Clear it and restart xmit.
+ *
+ * Note this can also happen as a result of SLEEP->RUN
+ * (i.e. coming out of power save mode).
+ */
+ vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ if_start(vap->iv_ifp);
+
+ /* bring up any vaps waiting on us */
+ wakeupwaiting(vap);
+ } else if (nstate == IEEE80211_S_INIT) {
+ /*
+ * Flush the scan cache if we did the last scan (XXX?)
+ * and flush any frames on send queues from this vap.
+ * Note the mgt q is used only for legacy drivers and
+ * will go away shortly.
+ */
+ ieee80211_scan_flush(vap);
+
+ /* XXX NB: cast for altq */
+ ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap);
}
+ vap->iv_newstate_cb = NULL;
}
+/*
+ * Public interface for initiating a state machine change.
+ * This routine single-threads the request and coordinates
+ * the scheduling of multiple vaps for the purpose of selecting
+ * an operating channel. Specifically the following scenarios
+ * are handled:
+ * o only one vap can be selecting a channel so on transition to
+ * SCAN state if another vap is already scanning then
+ * mark the caller for later processing and return without
+ * doing anything (XXX? expectations by caller of synchronous operation)
+ * o only one vap can be doing CAC of a channel so on transition to
+ * CAC state if another vap is already scanning for radar then
+ * mark the caller for later processing and return without
+ * doing anything (XXX? expectations by caller of synchronous operation)
+ * o if another vap is already running when a request is made
+ * to SCAN then an operating channel has been chosen; bypass
+ * the scan and just join the channel
+ *
+ * Note that the state change call is done through the iv_newstate
+ * method pointer so any driver routine gets invoked. The driver
+ * will normally call back into operating mode-specific
+ * ieee80211_newstate routines (below) unless it needs to completely
+ * bypass the state machine (e.g. because the firmware has it's
+ * own idea how things should work). Bypassing the net80211 layer
+ * is usually a mistake and indicates lack of proper integration
+ * with the net80211 layer.
+ */
static int
-ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+ieee80211_new_state_locked(struct ieee80211vap *vap,
+ enum ieee80211_state nstate, int arg)
{
- struct ifnet *ifp = ic->ic_ifp;
- struct ieee80211_node *ni;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211vap *vp;
enum ieee80211_state ostate;
-
- ostate = ic->ic_state;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
- ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
- ic->ic_state = nstate; /* state transition */
- callout_stop(&ic->ic_mgtsend); /* XXX callout_drain */
- if (ostate != IEEE80211_S_SCAN)
- ieee80211_cancel_scan(ic); /* background scan */
- ni = ic->ic_bss; /* NB: no reference held */
- if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)
- callout_stop(&ic->ic_swbmiss);
- switch (nstate) {
- case IEEE80211_S_INIT:
- switch (ostate) {
- case IEEE80211_S_INIT:
- break;
- case IEEE80211_S_RUN:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_ASSOC_LEAVE);
- ieee80211_sta_leave(ic, ni);
- break;
- case IEEE80211_M_HOSTAP:
- ieee80211_iterate_nodes(&ic->ic_sta,
- sta_disassoc, ic);
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_ASSOC:
- switch (ic->ic_opmode) {
- case IEEE80211_M_STA:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_AUTH_LEAVE);
- break;
- case IEEE80211_M_HOSTAP:
- ieee80211_iterate_nodes(&ic->ic_sta,
- sta_deauth, ic);
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_SCAN:
- ieee80211_cancel_scan(ic);
- break;
- case IEEE80211_S_AUTH:
- break;
- default:
- break;
+ int nrunning, nscanning, rc;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ nrunning = nscanning = 0;
+ /* XXX can track this state instead of calculating */
+ TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) {
+ if (vp != vap) {
+ if (vp->iv_state >= IEEE80211_S_RUN)
+ nrunning++;
+ /* XXX doesn't handle bg scan */
+ /* NB: CAC+AUTH+ASSOC treated like SCAN */
+ else if (vp->iv_state > IEEE80211_S_INIT)
+ nscanning++;
}
- if (ostate != IEEE80211_S_INIT) {
- /* NB: optimize INIT -> INIT case */
- ieee80211_drain_ifq(&ic->ic_mgtq);
- ieee80211_reset_bss(ic);
- ieee80211_scan_flush(ic);
- }
- if (ic->ic_auth->ia_detach != NULL)
- ic->ic_auth->ia_detach(ic);
- break;
+ }
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate],
+ nrunning, nscanning);
+ switch (nstate) {
case IEEE80211_S_SCAN:
- switch (ostate) {
- case IEEE80211_S_INIT:
- createibss:
- if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
- ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO) &&
- ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
- /*
- * Already have a channel; bypass the
- * scan and startup immediately. Because
- * of this explicitly sync the scanner state.
- */
- ieee80211_scan_update(ic);
- ieee80211_create_ibss(ic, ic->ic_des_chan);
- } else {
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE |
- IEEE80211_SCAN_FLUSH,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid, ic->ic_des_ssid);
- }
- break;
- case IEEE80211_S_SCAN:
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
+ if (ostate == IEEE80211_S_INIT) {
/*
- * These can happen either because of a timeout
- * on an assoc/auth response or because of a
- * change in state that requires a reset. For
- * the former we're called with a non-zero arg
- * that is the cause for the failure; pass this
- * to the scan code so it can update state.
- * Otherwise trigger a new scan unless we're in
- * manual roaming mode in which case an application
- * must issue an explicit scan request.
+ * INIT -> SCAN happens on initial bringup.
*/
- if (arg != 0)
- ieee80211_scan_assoc_fail(ic,
- ic->ic_bss->ni_macaddr, arg);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid, ic->ic_des_ssid);
- break;
- case IEEE80211_S_RUN: /* beacon miss */
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ieee80211_sta_leave(ic, ni);
- ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
- ieee80211_check_scan(ic,
- IEEE80211_SCAN_ACTIVE,
- IEEE80211_SCAN_FOREVER,
- ic->ic_des_nssid,
- ic->ic_des_ssid);
- } else {
- ieee80211_iterate_nodes(&ic->ic_sta,
- sta_disassoc, ic);
- goto createibss;
- }
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_AUTH:
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("switch to %s state when operating in mode %u",
- ieee80211_state_name[nstate], ic->ic_opmode));
- switch (ostate) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 1);
- break;
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- switch (arg & 0xff) {
- case IEEE80211_FC0_SUBTYPE_AUTH:
- /* ??? */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 2);
- break;
- case IEEE80211_FC0_SUBTYPE_DEAUTH:
- sta_authretry(ic, ni, arg>>8);
- break;
- }
- break;
- case IEEE80211_S_RUN:
- switch (arg & 0xff) {
- case IEEE80211_FC0_SUBTYPE_AUTH:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 2);
- ic->ic_state = ostate; /* stay RUN */
- break;
- case IEEE80211_FC0_SUBTYPE_DEAUTH:
- ieee80211_sta_leave(ic, ni);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
- /* try to reauth */
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 1);
- }
- break;
- }
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_ASSOC:
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("switch to %s state when operating in mode %u",
- ieee80211_state_name[nstate], ic->ic_opmode));
- switch (ostate) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: invalid transition\n", __func__);
- break;
- case IEEE80211_S_AUTH:
- case IEEE80211_S_ASSOC:
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
- break;
- case IEEE80211_S_RUN:
- ieee80211_sta_leave(ic, ni);
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
- IEEE80211_SEND_MGMT(ic, ni, arg ?
- IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
- IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
- }
- break;
- default:
- break;
- }
- break;
- case IEEE80211_S_RUN:
- if (ic->ic_flags & IEEE80211_F_WPA) {
- /* XXX validate prerequisites */
- }
- switch (ostate) {
- case IEEE80211_S_INIT:
- if (ic->ic_opmode == IEEE80211_M_MONITOR ||
- ic->ic_opmode == IEEE80211_M_WDS ||
- ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ KASSERT(!(nscanning && nrunning),
+ ("%d scanning and %d running", nscanning, nrunning));
+ if (nscanning) {
/*
- * Already have a channel; bypass the
- * scan and startup immediately. Because
- * of this explicitly sync the scanner state.
+ * Someone is scanning, defer our state
+ * change until the work has completed.
*/
- ieee80211_scan_update(ic);
- ieee80211_create_ibss(ic,
- ieee80211_ht_adjust_channel(ic,
- ic->ic_curchan, ic->ic_flags_ext));
- break;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: defer %s -> %s\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
+ rc = 0;
+ goto done;
}
- /* fall thru... */
- case IEEE80211_S_AUTH:
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
- "%s: invalid transition\n", __func__);
- /* fall thru... */
- case IEEE80211_S_RUN:
- break;
- case IEEE80211_S_SCAN: /* adhoc/hostap mode */
- case IEEE80211_S_ASSOC: /* infra mode */
- KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates,
- ("%s: bogus xmit rate %u setup\n", __func__,
- ni->ni_txrate));
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_debug(ic)) {
- if (ic->ic_opmode == IEEE80211_M_STA)
- if_printf(ifp, "associated ");
+ if (nrunning) {
+ /*
+ * Someone is operating; just join the channel
+ * they have chosen.
+ */
+ /* XXX kill arg? */
+ /* XXX check each opmode, adhoc? */
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ nstate = IEEE80211_S_SCAN;
else
- if_printf(ifp, "synchronized ");
- printf("with %s ssid ",
- ether_sprintf(ni->ni_bssid));
- ieee80211_print_essid(ic->ic_bss->ni_essid,
- ni->ni_esslen);
- printf(" channel %d start %uMb\n",
- ieee80211_chan2ieee(ic, ic->ic_curchan),
- IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
- }
+ nstate = IEEE80211_S_RUN;
+#ifdef IEEE80211_DEBUG
+ if (nstate != IEEE80211_S_SCAN) {
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE,
+ "%s: override, now %s -> %s\n",
+ __func__,
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+ }
#endif
- if (ic->ic_opmode == IEEE80211_M_STA) {
- ieee80211_scan_assoc_success(ic,
- ni->ni_macaddr);
- ieee80211_notify_node_join(ic, ni,
- arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
}
- if_start(ifp); /* XXX not authorized yet */
- break;
- default:
- break;
+ } else {
+ /*
+ * SCAN was forced; e.g. on beacon miss. Force
+ * other running vap's to INIT state and mark
+ * them as waiting for the scan to complete. This
+ * insures they don't interfere with our scanning.
+ *
+ * XXX not always right, assumes ap follows sta
+ */
+ markwaiting(vap);
}
- if (ostate != IEEE80211_S_RUN &&
- ic->ic_opmode == IEEE80211_M_STA &&
- (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) {
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_opmode == IEEE80211_M_WDS &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) &&
+ nscanning) {
/*
- * Start s/w beacon miss timer for devices w/o
- * hardware support. We fudge a bit here since
- * we're doing this in software.
+ * Legacy WDS with someone else scanning; don't
+ * go online until that completes as we should
+ * follow the other vap to the channel they choose.
*/
- ic->ic_swbmiss_period = IEEE80211_TU_TO_TICKS(
- 2 * ic->ic_bmissthreshold * ni->ni_intval);
- ic->ic_swbmiss_count = 0;
- callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period,
- ieee80211_swbmiss, ic);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: defer %s -> %s (legacy WDS)\n", __func__,
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
+ vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
+ rc = 0;
+ goto done;
}
- /*
- * Start/stop the authenticator when operating as an
- * AP. We delay until here to allow configuration to
- * happen out of order.
- */
- if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */
- ic->ic_auth->ia_attach != NULL) {
- /* XXX check failure */
- ic->ic_auth->ia_attach(ic);
- } else if (ic->ic_auth->ia_detach != NULL) {
- ic->ic_auth->ia_detach(ic);
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
+ IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_DFS) &&
+ !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) {
+ /*
+ * This is a DFS channel, transition to CAC state
+ * instead of RUN. This allows us to initiate
+ * Channel Availability Check (CAC) as specified
+ * by 11h/DFS.
+ */
+ nstate = IEEE80211_S_CAC;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: override %s -> %s (DFS)\n", __func__,
+ ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate]);
}
- /*
- * When 802.1x is not in use mark the port authorized
- * at this point so traffic can flow.
- */
- if (ni->ni_authmode != IEEE80211_AUTH_8021X)
- ieee80211_node_authorize(ni);
- /*
- * Enable inactivity processing.
- * XXX
- */
- callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
- ieee80211_node_timeout, ic);
break;
+ case IEEE80211_S_INIT:
+ if (ostate == IEEE80211_S_INIT ) {
+ /* XXX don't believe this */
+ /* INIT -> INIT. nothing to do */
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
+ }
+ /* fall thru... */
default:
break;
}
- return 0;
+ /* XXX on transition RUN->CAC do we need to set nstate = iv_state? */
+ if (ostate != nstate) {
+ /*
+ * Arrange for work to happen after state change completes.
+ * If this happens asynchronously the caller must arrange
+ * for the com lock to be held.
+ */
+ vap->iv_newstate_cb = ieee80211_newstate_cb;
+ }
+ rc = vap->iv_newstate(vap, nstate, arg);
+ if (rc == 0 && vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, nstate, arg);
+done:
+ return rc;
+}
+
+int
+ieee80211_new_state(struct ieee80211vap *vap,
+ enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ int rc;
+
+ IEEE80211_LOCK(ic);
+ rc = ieee80211_new_state_locked(vap, nstate, arg);
+ IEEE80211_UNLOCK(ic);
+ return rc;
}
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
index 9f94f1c..ec7061d 100644
--- a/sys/net80211/ieee80211_proto.h
+++ b/sys/net80211/ieee80211_proto.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,63 +44,65 @@ enum ieee80211_state {
};
#define IEEE80211_S_MAX (IEEE80211_S_SLEEP+1)
-#define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \
- ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
-
-/*
- * The formation of some management frames requires guidance to
- * deal with legacy clients. When the client is identified as
- * "legacy 11b" this parameter can be passed in the arg param of a
- * IEEE80211_SEND_MGMT call.
- */
-#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */
-#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */
-#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */
+#define IEEE80211_SEND_MGMT(_ni,_type,_arg) \
+ ((*(_ni)->ni_ic->ic_send_mgmt)(_ni, _type, _arg))
extern const char *ieee80211_mgt_subtype_name[];
extern const char *ieee80211_phymode_name[];
void ieee80211_proto_attach(struct ieee80211com *);
void ieee80211_proto_detach(struct ieee80211com *);
+void ieee80211_proto_vattach(struct ieee80211vap *);
+void ieee80211_proto_vdetach(struct ieee80211vap *);
+
+void ieee80211_syncifflag_locked(struct ieee80211com *, int flag);
+void ieee80211_syncflag(struct ieee80211vap *, int flag);
+void ieee80211_syncflag_ext(struct ieee80211vap *, int flag);
-struct ieee80211_node;
-int ieee80211_input(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, int, uint32_t);
-void ieee80211_deliver_data(struct ieee80211com *,
- struct ieee80211_node *, struct mbuf *);
-struct mbuf *ieee80211_decap1(struct mbuf *, int *);
-int ieee80211_setup_rates(struct ieee80211_node *ni,
- const uint8_t *rates, const uint8_t *xrates, int flags);
-void ieee80211_saveie(uint8_t **, const uint8_t *);
-void ieee80211_saveath(struct ieee80211_node *, uint8_t *);
-void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *, int, int, int, uint32_t);
-int ieee80211_mgmt_output(struct ieee80211com *, struct ieee80211_node *,
- struct mbuf *, int type);
+#define ieee80211_input(ni, m, rssi, noise, rstamp) \
+ ((ni)->ni_vap->iv_input(ni, m, rssi, noise, rstamp))
+int ieee80211_input_all(struct ieee80211com *, struct mbuf *,
+ int, int, uint32_t);
+int ieee80211_mgmt_output(struct ieee80211_node *, struct mbuf *, int);
struct ieee80211_bpf_params;
int ieee80211_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
int ieee80211_output(struct ifnet *, struct mbuf *,
struct sockaddr *, struct rtentry *);
+void ieee80211_start(struct ifnet *);
int ieee80211_send_nulldata(struct ieee80211_node *);
-int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
- int, int);
+int ieee80211_classify(struct ieee80211_node *, struct mbuf *m);
+struct mbuf *ieee80211_encap(struct ieee80211_node *, struct mbuf *);
+int ieee80211_send_mgmt(struct ieee80211_node *, int, int);
+struct ieee80211_appie;
int ieee80211_send_probereq(struct ieee80211_node *ni,
const uint8_t sa[IEEE80211_ADDR_LEN],
const uint8_t da[IEEE80211_ADDR_LEN],
const uint8_t bssid[IEEE80211_ADDR_LEN],
- const uint8_t *ssid, size_t ssidlen,
- const void *optie, size_t optielen);
-int ieee80211_classify(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *);
-struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *,
- struct ieee80211_node *);
+ const uint8_t *ssid, size_t ssidlen);
+/*
+ * The formation of ProbeResponse frames requires guidance to
+ * deal with legacy clients. When the client is identified as
+ * "legacy 11b" ieee80211_send_proberesp is passed this token.
+ */
+#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */
+#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */
+#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */
+struct mbuf *ieee80211_alloc_proberesp(struct ieee80211_node *, int);
+int ieee80211_send_proberesp(struct ieee80211vap *,
+ const uint8_t da[IEEE80211_ADDR_LEN], int);
+struct mbuf *ieee80211_alloc_rts(struct ieee80211com *ic,
+ const uint8_t [IEEE80211_ADDR_LEN],
+ const uint8_t [IEEE80211_ADDR_LEN], uint16_t);
+struct mbuf *ieee80211_alloc_cts(struct ieee80211com *,
+ const uint8_t [IEEE80211_ADDR_LEN], uint16_t);
void ieee80211_reset_erp(struct ieee80211com *);
void ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
-int ieee80211_iserp_rateset(struct ieee80211com *,
- struct ieee80211_rateset *);
-void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
+int ieee80211_iserp_rateset(const struct ieee80211_rateset *);
+void ieee80211_setbasicrates(struct ieee80211_rateset *,
+ enum ieee80211_phymode);
+void ieee80211_addbasicrates(struct ieee80211_rateset *,
enum ieee80211_phymode);
/*
@@ -146,16 +148,16 @@ ieee80211_anyhdrsize(const void *data)
/*
* Template for an in-kernel authenticator. Authenticators
* register with the protocol code and are typically loaded
- * as separate modules as needed.
+ * as separate modules as needed. One special authenticator
+ * is xauth; it intercepts requests so that protocols like
+ * WPA can be handled in user space.
*/
struct ieee80211_authenticator {
const char *ia_name; /* printable name */
- int (*ia_attach)(struct ieee80211com *);
- void (*ia_detach)(struct ieee80211com *);
- void (*ia_node_join)(struct ieee80211com *,
- struct ieee80211_node *);
- void (*ia_node_leave)(struct ieee80211com *,
- struct ieee80211_node *);
+ int (*ia_attach)(struct ieee80211vap *);
+ void (*ia_detach)(struct ieee80211vap *);
+ void (*ia_node_join)(struct ieee80211_node *);
+ void (*ia_node_leave)(struct ieee80211_node *);
};
void ieee80211_authenticator_register(int type,
const struct ieee80211_authenticator *);
@@ -166,23 +168,23 @@ struct ieee80211req;
/*
* Template for an MAC ACL policy module. Such modules
* register with the protocol code and are passed the sender's
- * address of each received frame for validation.
+ * address of each received auth frame for validation.
*/
struct ieee80211_aclator {
const char *iac_name; /* printable name */
- int (*iac_attach)(struct ieee80211com *);
- void (*iac_detach)(struct ieee80211com *);
- int (*iac_check)(struct ieee80211com *,
+ int (*iac_attach)(struct ieee80211vap *);
+ void (*iac_detach)(struct ieee80211vap *);
+ int (*iac_check)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
- int (*iac_add)(struct ieee80211com *,
+ int (*iac_add)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
- int (*iac_remove)(struct ieee80211com *,
+ int (*iac_remove)(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
- int (*iac_flush)(struct ieee80211com *);
- int (*iac_setpolicy)(struct ieee80211com *, int);
- int (*iac_getpolicy)(struct ieee80211com *);
- int (*iac_setioctl)(struct ieee80211com *, struct ieee80211req *);
- int (*iac_getioctl)(struct ieee80211com *, struct ieee80211req *);
+ int (*iac_flush)(struct ieee80211vap *);
+ int (*iac_setpolicy)(struct ieee80211vap *, int);
+ int (*iac_getpolicy)(struct ieee80211vap *);
+ int (*iac_setioctl)(struct ieee80211vap *, struct ieee80211req *);
+ int (*iac_getioctl)(struct ieee80211vap *, struct ieee80211req *);
};
void ieee80211_aclator_register(const struct ieee80211_aclator *);
void ieee80211_aclator_unregister(const struct ieee80211_aclator *);
@@ -190,11 +192,12 @@ const struct ieee80211_aclator *ieee80211_aclator_get(const char *name);
/* flags for ieee80211_fix_rate() */
#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */
-#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */
+#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed legacy rate */
#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */
#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */
#define IEEE80211_F_DOBRS 0x00000010 /* check basic rate set */
#define IEEE80211_F_JOIN 0x00000020 /* sta joining our bss */
+#define IEEE80211_F_DOFMCS 0x00000040 /* use fixed HT rate */
int ieee80211_fix_rate(struct ieee80211_node *,
struct ieee80211_rateset *, int);
@@ -233,15 +236,38 @@ struct ieee80211_wme_state {
int (*wme_update)(struct ieee80211com *);
};
-void ieee80211_wme_initparams(struct ieee80211com *);
-void ieee80211_wme_updateparams(struct ieee80211com *);
-void ieee80211_wme_updateparams_locked(struct ieee80211com *);
+void ieee80211_wme_initparams(struct ieee80211vap *);
+void ieee80211_wme_updateparams(struct ieee80211vap *);
+void ieee80211_wme_updateparams_locked(struct ieee80211vap *);
+
+/*
+ * Return the WME TID from a QoS frame. If no TID
+ * is present return the index for the "non-QoS" entry.
+ */
+static __inline uint8_t
+ieee80211_gettid(const struct ieee80211_frame *wh)
+{
+ uint8_t tid;
+
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
+ tid = ((const struct ieee80211_qosframe *)wh)->
+ i_qos[0] & IEEE80211_QOS_TID;
+ tid++;
+ } else
+ tid = IEEE80211_NONQOS_TID;
+ return tid;
+}
-#define ieee80211_new_state(_ic, _nstate, _arg) \
- (((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
-int ieee80211_init(struct ieee80211com *, int forcescan);
-void ieee80211_dturbo_switch(struct ieee80211com *, int newflags);
+void ieee80211_start_locked(struct ieee80211vap *);
+void ieee80211_init(void *);
+void ieee80211_start_all(struct ieee80211com *);
+void ieee80211_stop_locked(struct ieee80211vap *);
+void ieee80211_stop(struct ieee80211vap *);
+void ieee80211_stop_all(struct ieee80211com *);
+void ieee80211_dturbo_switch(struct ieee80211vap *, int newflags);
+void ieee80211_swbmiss(void *arg);
void ieee80211_beacon_miss(struct ieee80211com *);
+int ieee80211_new_state(struct ieee80211vap *, enum ieee80211_state, int);
void ieee80211_print_essid(const uint8_t *, int);
void ieee80211_dump_pkt(struct ieee80211com *,
const uint8_t *, int, int, int);
@@ -275,7 +301,7 @@ struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *,
struct ieee80211_beacon_offsets *);
/*
- * Beacon frame updates are signaled through calls to ic_update_beacon
+ * Beacon frame updates are signaled through calls to iv_update_beacon
* with one of the IEEE80211_BEACON_* tokens defined below. For devices
* that construct beacon frames on the host this can trigger a rebuild
* or defer the processing. For devices that offload beacon frame
@@ -283,7 +309,7 @@ struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *,
* array in the ieee80211_beacon_offsets structure is intended to record
* deferred processing requirements; ieee80211_beacon_update uses the
* state to optimize work. Since this structure is owned by the driver
- * and not visible to the 802.11 layer drivers must supply an ic_update_beacon
+ * and not visible to the 802.11 layer drivers must supply an iv_update_beacon
* callback that marks the flag bits and schedules (as necessary) an update.
*/
enum {
@@ -299,14 +325,36 @@ enum {
int ieee80211_beacon_update(struct ieee80211_node *,
struct ieee80211_beacon_offsets *, struct mbuf *, int mcast);
+void ieee80211_csa_startswitch(struct ieee80211com *,
+ struct ieee80211_channel *, int mode, int count);
+void ieee80211_csa_completeswitch(struct ieee80211com *);
+void ieee80211_cac_completeswitch(struct ieee80211vap *);
+
/*
* Notification methods called from the 802.11 state machine.
* Note that while these are defined here, their implementation
* is OS-specific.
*/
-void ieee80211_notify_node_join(struct ieee80211com *,
- struct ieee80211_node *, int newassoc);
-void ieee80211_notify_node_leave(struct ieee80211com *,
- struct ieee80211_node *);
-void ieee80211_notify_scan_done(struct ieee80211com *);
+void ieee80211_notify_node_join(struct ieee80211_node *, int newassoc);
+void ieee80211_notify_node_leave(struct ieee80211_node *);
+void ieee80211_notify_scan_done(struct ieee80211vap *);
+void ieee80211_notify_wds_discover(struct ieee80211_node *);
+void ieee80211_notify_csa(struct ieee80211com *,
+ const struct ieee80211_channel *, int mode, int count);
+void ieee80211_notify_radar(struct ieee80211com *,
+ const struct ieee80211_channel *);
+enum ieee80211_notify_cac_event {
+ IEEE80211_NOTIFY_CAC_START = 0, /* CAC timer started */
+ IEEE80211_NOTIFY_CAC_STOP = 1, /* CAC intentionally stopped */
+ IEEE80211_NOTIFY_CAC_RADAR = 2, /* CAC stopped due to radar detectio */
+ IEEE80211_NOTIFY_CAC_EXPIRE = 3, /* CAC expired w/o radar */
+};
+void ieee80211_notify_cac(struct ieee80211com *,
+ const struct ieee80211_channel *,
+ enum ieee80211_notify_cac_event);
+void ieee80211_notify_node_deauth(struct ieee80211_node *);
+void ieee80211_notify_node_auth(struct ieee80211_node *);
+void ieee80211_notify_country(struct ieee80211vap *, const uint8_t [],
+ const uint8_t cc[2]);
+void ieee80211_notify_radio(struct ieee80211com *, int);
#endif /* _NET80211_IEEE80211_PROTO_H_ */
diff --git a/sys/net80211/ieee80211_regdomain.c b/sys/net80211/ieee80211_regdomain.c
index 7f1b3dc..4cf2dc0 100644
--- a/sys/net80211/ieee80211_regdomain.c
+++ b/sys/net80211/ieee80211_regdomain.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 regdomain support.
*/
+#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -37,26 +38,60 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <net/if.h>
-#include <net/if_arp.h>
-#include <net/if_dl.h>
#include <net/if_media.h>
-#include <net/if_types.h>
-#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
+static void
+null_getradiocaps(struct ieee80211com *ic, int *n, struct ieee80211_channel *c)
+{
+ /* just feed back the current channel list */
+ *n = ic->ic_nchans;
+ memcpy(c, ic->ic_channels,
+ ic->ic_nchans*sizeof(struct ieee80211_channel));
+}
+
+static int
+null_setregdomain(struct ieee80211com *ic,
+ struct ieee80211_regdomain *rd,
+ int nchans, struct ieee80211_channel chans[])
+{
+ return 0; /* accept anything */
+}
+
void
ieee80211_regdomain_attach(struct ieee80211com *ic)
{
- ic->ic_regdomain = 0; /* XXX */
- ic->ic_countrycode = CTRY_UNITED_STATES;/* XXX */
- ic->ic_location = 1+2; /* both */
+ 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 */
+ /* XXX? too late to setup default channel list */
+ }
+ ic->ic_getradiocaps = null_getradiocaps;
+ ic->ic_setregdomain = null_setregdomain;
}
void
ieee80211_regdomain_detach(struct ieee80211com *ic)
{
+ if (ic->ic_countryie != NULL) {
+ free(ic->ic_countryie, M_80211_NODE_IE);
+ ic->ic_countryie = NULL;
+ }
+}
+
+void
+ieee80211_regdomain_vattach(struct ieee80211vap *vap)
+{
+}
+
+void
+ieee80211_regdomain_vdetach(struct ieee80211vap *vap)
+{
}
static void
@@ -68,6 +103,7 @@ addchan(struct ieee80211com *ic, int ieee, int flags)
c->ic_freq = ieee80211_ieee2mhz(ieee, flags);
c->ic_ieee = ieee;
c->ic_flags = flags;
+ c->ic_extieee = 0;
}
/*
@@ -76,24 +112,27 @@ addchan(struct ieee80211com *ic, int ieee, int flags)
* when a driver does not obtain the channel list from another
* source (such as firmware).
*/
-void
+int
ieee80211_init_channels(struct ieee80211com *ic,
- int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm)
+ const struct ieee80211_regdomain *rd, const uint8_t bands[])
{
int i;
/* XXX just do something for now */
ic->ic_nchans = 0;
- if (isset(&bands, IEEE80211_MODE_11B) ||
- isset(&bands, IEEE80211_MODE_11G)) {
- for (i = 1; i <= (ecm ? 14 : 11); i++) {
- if (isset(&bands, IEEE80211_MODE_11B))
+ 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))
+ if (isset(bands, IEEE80211_MODE_11G))
addchan(ic, i, IEEE80211_CHAN_G);
}
}
- if (isset(&bands, IEEE80211_MODE_11A)) {
+ 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)
@@ -101,17 +140,73 @@ ieee80211_init_channels(struct ieee80211com *ic,
for (i = 149; i <= 161; i += 4)
addchan(ic, i, IEEE80211_CHAN_A);
}
- ic->ic_regdomain = rd;
- ic->ic_countrycode = cc;
- ic->ic_location = outdoor;
+ if (rd != NULL)
+ ic->ic_regdomain = *rd;
+
+ return 0;
+}
+
+static __inline int
+chancompar(const void *a, const void *b)
+{
+ const struct ieee80211_channel *ca = a;
+ const struct ieee80211_channel *cb = b;
+
+ return (ca->ic_freq == cb->ic_freq) ?
+ (ca->ic_flags & IEEE80211_CHAN_ALL) -
+ (cb->ic_flags & IEEE80211_CHAN_ALL) :
+ ca->ic_freq - cb->ic_freq;
+}
+
+/*
+ * Insertion sort.
+ */
+#define swap(_a, _b, _size) { \
+ uint8_t *s = _b; \
+ int i = _size; \
+ do { \
+ uint8_t tmp = *_a; \
+ *_a++ = *s; \
+ *s++ = tmp; \
+ } while (--i); \
+ _a -= _size; \
+}
+
+static void
+sort_channels(void *a, size_t n, size_t size)
+{
+ uint8_t *aa = a;
+ uint8_t *ai, *t;
+
+ KASSERT(n > 0, ("no channels"));
+ for (ai = aa+size; --n >= 1; ai += size)
+ for (t = ai; t > aa; t -= size) {
+ uint8_t *u = t - size;
+ if (chancompar(u, t) <= 0)
+ break;
+ swap(u, t, size);
+ }
}
+#undef swap
/*
- * Add Country Information IE.
+ * Order channels w/ the same frequency so that
+ * b < g < htg and a < hta. This is used to optimize
+ * channel table lookups and some user applications
+ * may also depend on it (though they should not).
*/
-uint8_t *
-ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
- enum ISOCountryCode cc, int location)
+void
+ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans)
+{
+ if (nchans > 0)
+ sort_channels(chans, nchans, sizeof(struct ieee80211_channel));
+}
+
+/*
+ * Allocate and construct a Country Information IE.
+ */
+struct ieee80211_appie *
+ieee80211_alloc_countryie(struct ieee80211com *ic)
{
#define CHAN_UNINTERESTING \
(IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO | \
@@ -131,35 +226,46 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
CHAN_UNINTERESTING | IEEE80211_CHAN_2GHZ, /* MODE_11NA */
CHAN_UNINTERESTING | IEEE80211_CHAN_5GHZ, /* MODE_11NG */
};
- struct ieee80211_country_ie *ie = (struct ieee80211_country_ie *)frm;
- const char *iso_name;
- uint8_t nextchan, chans[IEEE80211_CHAN_BYTES];
- int i, skip;
+ const struct ieee80211_regdomain *rd = &ic->ic_regdomain;
+ uint8_t nextchan, chans[IEEE80211_CHAN_BYTES], *frm;
+ struct ieee80211_appie *aie;
+ struct ieee80211_country_ie *ie;
+ int i, skip, nruns;
+ aie = malloc(IEEE80211_COUNTRY_MAX_SIZE, M_80211_NODE_IE,
+ M_NOWAIT | M_ZERO);
+ if (aie == NULL) {
+ if_printf(ic->ic_ifp,
+ "%s: unable to allocate memory for country ie\n", __func__);
+ /* XXX stat */
+ return NULL;
+ }
+ ie = (struct ieee80211_country_ie *) aie->ie_data;
ie->ie = IEEE80211_ELEMID_COUNTRY;
- iso_name = ieee80211_cctoiso(cc);
- if (iso_name == NULL) {
- if_printf(ic->ic_ifp, "bad country code %d ignored\n", cc);
- iso_name = " ";
+ if (rd->isocc[0] == '\0') {
+ if_printf(ic->ic_ifp, "no ISO country string for cc %d; "
+ "using blanks\n", rd->country);
+ ie->cc[0] = ie->cc[1] = ' ';
+ } else {
+ ie->cc[0] = rd->isocc[0];
+ ie->cc[1] = rd->isocc[1];
}
- ie->cc[0] = iso_name[0];
- ie->cc[1] = iso_name[1];
/*
- * Indoor/Outdoor portion of country string.
- * NB: this is not quite right, since we should have one of:
+ * Indoor/Outdoor portion of country string:
* 'I' indoor only
* 'O' outdoor only
* ' ' all enviroments
*/
- ie->cc[2] = ((location & 3) == 3 ? ' ' : location & 1 ? 'I' : 'O');
-
+ ie->cc[2] = (rd->location == 'I' ? 'I' :
+ rd->location == 'O' ? 'O' : ' ');
/*
* Run-length encoded channel+max tx power info.
*/
frm = (uint8_t *)&ie->band[0];
nextchan = 0; /* NB: impossible channel # */
+ nruns = 0;
memset(chans, 0, sizeof(chans));
- skip = skipflags[ic->ic_curmode];
+ skip = skipflags[ieee80211_chan2mode(ic->ic_bsschan)];
for (i = 0; i < ic->ic_nchans; i++) {
const struct ieee80211_channel *c = &ic->ic_channels[i];
@@ -170,12 +276,19 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
setbit(chans, c->ic_ieee);
if (c->ic_ieee != nextchan ||
c->ic_maxregpower != frm[-1]) { /* new run */
- /* XXX max of 83 runs */
+ if (nruns == IEEE80211_COUNTRY_MAX_BANDS) {
+ if_printf(ic->ic_ifp, "%s: country ie too big, "
+ "runs > max %d, truncating\n",
+ __func__, IEEE80211_COUNTRY_MAX_BANDS);
+ /* XXX stat? fail? */
+ break;
+ }
frm[0] = c->ic_ieee; /* starting channel # */
frm[1] = 1; /* # channels in run */
frm[2] = c->ic_maxregpower; /* tx power cap */
frm += 3;
nextchan = c->ic_ieee + 1; /* overflow? */
+ nruns++;
} else { /* extend run */
frm[-2]++;
nextchan++;
@@ -186,154 +299,114 @@ ieee80211_add_countryie(uint8_t *frm, struct ieee80211com *ic,
ie->len++;
*frm++ = 0;
}
- return frm;
+ aie->ie_len = frm - aie->ie_data;
+
+ return aie;
#undef CHAN_UNINTERESTING
}
-/*
- * Country Code Table for code-to-string conversion.
- */
-static const struct {
- enum ISOCountryCode iso_code;
- const char* iso_name;
-} country_strings[] = {
- { CTRY_DEBUG, "DB" }, /* NB: nonstandard */
- { CTRY_DEFAULT, "NA" }, /* NB: nonstandard */
- { CTRY_ALBANIA, "AL" },
- { CTRY_ALGERIA, "DZ" },
- { CTRY_ARGENTINA, "AR" },
- { CTRY_ARMENIA, "AM" },
- { CTRY_AUSTRALIA, "AU" },
- { CTRY_AUSTRIA, "AT" },
- { CTRY_AZERBAIJAN, "AZ" },
- { CTRY_BAHRAIN, "BH" },
- { CTRY_BELARUS, "BY" },
- { CTRY_BELGIUM, "BE" },
- { CTRY_BELIZE, "BZ" },
- { CTRY_BOLIVIA, "BO" },
- { CTRY_BRAZIL, "BR" },
- { CTRY_BRUNEI_DARUSSALAM, "BN" },
- { CTRY_BULGARIA, "BG" },
- { CTRY_CANADA, "CA" },
- { CTRY_CHILE, "CL" },
- { CTRY_CHINA, "CN" },
- { CTRY_COLOMBIA, "CO" },
- { CTRY_COSTA_RICA, "CR" },
- { CTRY_CROATIA, "HR" },
- { CTRY_CYPRUS, "CY" },
- { CTRY_CZECH, "CZ" },
- { CTRY_DENMARK, "DK" },
- { CTRY_DOMINICAN_REPUBLIC, "DO" },
- { CTRY_ECUADOR, "EC" },
- { CTRY_EGYPT, "EG" },
- { CTRY_EL_SALVADOR, "SV" },
- { CTRY_ESTONIA, "EE" },
- { CTRY_FINLAND, "FI" },
- { CTRY_FRANCE, "FR" },
- { CTRY_FRANCE2, "F2" },
- { CTRY_GEORGIA, "GE" },
- { CTRY_GERMANY, "DE" },
- { CTRY_GREECE, "GR" },
- { CTRY_GUATEMALA, "GT" },
- { CTRY_HONDURAS, "HN" },
- { CTRY_HONG_KONG, "HK" },
- { CTRY_HUNGARY, "HU" },
- { CTRY_ICELAND, "IS" },
- { CTRY_INDIA, "IN" },
- { CTRY_INDONESIA, "ID" },
- { CTRY_IRAN, "IR" },
- { CTRY_IRELAND, "IE" },
- { CTRY_ISRAEL, "IL" },
- { CTRY_ITALY, "IT" },
- { CTRY_JAMAICA, "JM" },
- { CTRY_JAPAN, "JP" },
- { CTRY_JAPAN1, "J1" },
- { CTRY_JAPAN2, "J2" },
- { CTRY_JAPAN3, "J3" },
- { CTRY_JAPAN4, "J4" },
- { CTRY_JAPAN5, "J5" },
- { CTRY_JORDAN, "JO" },
- { CTRY_KAZAKHSTAN, "KZ" },
- { CTRY_KOREA_NORTH, "KP" },
- { CTRY_KOREA_ROC, "KR" },
- { CTRY_KOREA_ROC2, "K2" },
- { CTRY_KUWAIT, "KW" },
- { CTRY_LATVIA, "LV" },
- { CTRY_LEBANON, "LB" },
- { CTRY_LIECHTENSTEIN, "LI" },
- { CTRY_LITHUANIA, "LT" },
- { CTRY_LUXEMBOURG, "LU" },
- { CTRY_MACAU, "MO" },
- { CTRY_MACEDONIA, "MK" },
- { CTRY_MALAYSIA, "MY" },
- { CTRY_MEXICO, "MX" },
- { CTRY_MONACO, "MC" },
- { CTRY_MOROCCO, "MA" },
- { CTRY_NETHERLANDS, "NL" },
- { CTRY_NEW_ZEALAND, "NZ" },
- { CTRY_NORWAY, "NO" },
- { CTRY_OMAN, "OM" },
- { CTRY_PAKISTAN, "PK" },
- { CTRY_PANAMA, "PA" },
- { CTRY_PERU, "PE" },
- { CTRY_PHILIPPINES, "PH" },
- { CTRY_POLAND, "PL" },
- { CTRY_PORTUGAL, "PT" },
- { CTRY_PUERTO_RICO, "PR" },
- { CTRY_QATAR, "QA" },
- { CTRY_ROMANIA, "RO" },
- { CTRY_RUSSIA, "RU" },
- { CTRY_SAUDI_ARABIA, "SA" },
- { CTRY_SINGAPORE, "SG" },
- { CTRY_SLOVAKIA, "SK" },
- { CTRY_SLOVENIA, "SI" },
- { CTRY_SOUTH_AFRICA, "ZA" },
- { CTRY_SPAIN, "ES" },
- { CTRY_SWEDEN, "SE" },
- { CTRY_SWITZERLAND, "CH" },
- { CTRY_SYRIA, "SY" },
- { CTRY_TAIWAN, "TW" },
- { CTRY_THAILAND, "TH" },
- { CTRY_TRINIDAD_Y_TOBAGO, "TT" },
- { CTRY_TUNISIA, "TN" },
- { CTRY_TURKEY, "TR" },
- { CTRY_UKRAINE, "UA" },
- { CTRY_UAE, "AE" },
- { CTRY_UNITED_KINGDOM, "GB" },
- { CTRY_UNITED_STATES, "US" },
- { CTRY_URUGUAY, "UY" },
- { CTRY_UZBEKISTAN, "UZ" },
- { CTRY_VENEZUELA, "VE" },
- { CTRY_VIET_NAM, "VN" },
- { CTRY_YEMEN, "YE" },
- { CTRY_ZIMBABWE, "ZW" }
-};
-
-const char *
-ieee80211_cctoiso(enum ISOCountryCode cc)
+static int
+allvapsdown(struct ieee80211com *ic)
{
-#define N(a) (sizeof(a) / sizeof(a[0]))
- int i;
+ struct ieee80211vap *vap;
- for (i = 0; i < N(country_strings); i++) {
- if (country_strings[i].iso_code == cc)
- return country_strings[i].iso_name;
- }
- return NULL;
-#undef N
+ IEEE80211_LOCK_ASSERT(ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_state != IEEE80211_S_INIT)
+ return 0;
+ return 1;
}
int
-ieee80211_isotocc(const char iso[2])
+ieee80211_setregdomain(struct ieee80211vap *vap,
+ struct ieee80211_regdomain_req *reg)
{
-#define N(a) (sizeof(a) / sizeof(a[0]))
- int i;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_channel *c;
+ int desfreq = 0, desflags = 0; /* XXX silence gcc complaint */
+ int error, i;
- for (i = 0; i < N(country_strings); i++) {
- if (country_strings[i].iso_name[0] == iso[0] &&
- country_strings[i].iso_name[1] == iso[1])
- return country_strings[i].iso_code;
+ if (reg->rd.location != 'I' && reg->rd.location != 'O' &&
+ reg->rd.location != ' ')
+ return EINVAL;
+ if (reg->rd.isocc[0] == '\0' || reg->rd.isocc[1] == '\0')
+ return EINVAL;
+ if (reg->chaninfo.ic_nchans >= IEEE80211_CHAN_MAX)
+ return EINVAL;
+ /*
+ * Calculate freq<->IEEE mapping and default max tx power
+ * for channels not setup. The driver can override these
+ * setting to reflect device properties/requirements.
+ */
+ for (i = 0; i < reg->chaninfo.ic_nchans; i++) {
+ c = &reg->chaninfo.ic_chans[i];
+ if (c->ic_freq == 0 || c->ic_flags == 0)
+ return EINVAL;
+ if (c->ic_maxregpower == 0)
+ return EINVAL;
+ if (c->ic_ieee == 0)
+ c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags);
+ if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0)
+ c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq +
+ (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20),
+ c->ic_flags);
+ if (c->ic_maxpower == 0)
+ c->ic_maxpower = 2*c->ic_maxregpower;
+ }
+ IEEE80211_LOCK(ic);
+ error = ic->ic_setregdomain(ic, &reg->rd,
+ reg->chaninfo.ic_nchans, reg->chaninfo.ic_chans);
+ if (error != 0) {
+ IEEE80211_UNLOCK(ic);
+ return error;
}
- return -1;
-#undef N
+ /* XXX bandaid; a running vap will likely crash */
+ if (!allvapsdown(ic)) {
+ IEEE80211_UNLOCK(ic);
+ return EBUSY;
+ }
+ /*
+ * Commit: copy in new channel table and reset media state.
+ * On return the state machines will be clocked so all vaps
+ * will reset their state.
+ *
+ * XXX ic_bsschan is marked undefined, must have vap's in
+ * INIT state or we blow up forcing stations off
+ */
+ /*
+ * Save any desired channel for restore below. Note this
+ * needs to be done for all vaps but for now we only do
+ * the one where the ioctl is issued.
+ */
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
+ desfreq = vap->iv_des_chan->ic_freq;
+ desflags = vap->iv_des_chan->ic_flags;
+ }
+ /* regdomain parameters */
+ memcpy(&ic->ic_regdomain, &reg->rd, sizeof(reg->rd));
+ /* channel table */
+ memcpy(ic->ic_channels, reg->chaninfo.ic_chans,
+ reg->chaninfo.ic_nchans * sizeof(struct ieee80211_channel));
+ ic->ic_nchans = reg->chaninfo.ic_nchans;
+ memset(&ic->ic_channels[ic->ic_nchans], 0,
+ (IEEE80211_CHAN_MAX - ic->ic_nchans) *
+ sizeof(struct ieee80211_channel));
+ ieee80211_media_init(ic);
+
+ /*
+ * Invalidate channel-related state.
+ */
+ if (ic->ic_countryie != NULL) {
+ free(ic->ic_countryie, M_80211_NODE_IE);
+ ic->ic_countryie = NULL;
+ }
+ ieee80211_scan_flush(vap);
+ ieee80211_dfs_reset(ic);
+ if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
+ /* NB: may be NULL if not present in new channel list */
+ vap->iv_des_chan = ieee80211_find_channel(ic, desfreq, desflags);
+ }
+ IEEE80211_UNLOCK(ic);
+
+ return 0;
}
diff --git a/sys/net80211/ieee80211_regdomain.h b/sys/net80211/ieee80211_regdomain.h
index 9c1345e..c9c0823 100644
--- a/sys/net80211/ieee80211_regdomain.h
+++ b/sys/net80211/ieee80211_regdomain.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,42 +43,92 @@ enum ISOCountryCode {
CTRY_ANDORRA = 20,
CTRY_ANGOLA = 24,
CTRY_ANGUILLA = 660,
- /* XXX correct remainder */
+ CTRY_ANTARTICA = 10,
+ CTRY_ANTIGUA = 28, /* Antigua and Barbuda */
CTRY_ARGENTINA = 32, /* Argentina */
CTRY_ARMENIA = 51, /* Armenia */
+ CTRY_ARUBA = 533, /* Aruba */
CTRY_AUSTRALIA = 36, /* Australia */
CTRY_AUSTRIA = 40, /* Austria */
CTRY_AZERBAIJAN = 31, /* Azerbaijan */
+ CTRY_BAHAMAS = 44, /* Bahamas */
CTRY_BAHRAIN = 48, /* Bahrain */
+ CTRY_BANGLADESH = 50, /* Bangladesh */
+ CTRY_BARBADOS = 52,
CTRY_BELARUS = 112, /* Belarus */
CTRY_BELGIUM = 56, /* Belgium */
- CTRY_BELIZE = 84, /* Belize */
+ CTRY_BELIZE = 84,
+ CTRY_BENIN = 204,
+ CTRY_BERMUDA = 60,
+ CTRY_BHUTAN = 64,
CTRY_BOLIVIA = 68, /* Bolivia */
+ CTRY_BOSNIA_AND_HERZEGOWINA = 70,
+ CTRY_BOTSWANA = 72,
+ CTRY_BOUVET_ISLAND = 74,
CTRY_BRAZIL = 76, /* Brazil */
+ CTRY_BRITISH_INDIAN_OCEAN_TERRITORY = 86,
CTRY_BRUNEI_DARUSSALAM = 96, /* Brunei Darussalam */
CTRY_BULGARIA = 100, /* Bulgaria */
+ CTRY_BURKINA_FASO = 854,
+ CTRY_BURUNDI = 108,
+ CTRY_CAMBODIA = 116,
+ CTRY_CAMEROON = 120,
CTRY_CANADA = 124, /* Canada */
+ CTRY_CAPE_VERDE = 132,
+ CTRY_CAYMAN_ISLANDS = 136,
+ CTRY_CENTRAL_AFRICAN_REPUBLIC = 140,
+ CTRY_CHAD = 148,
CTRY_CHILE = 152, /* Chile */
CTRY_CHINA = 156, /* People's Republic of China */
+ CTRY_CHRISTMAS_ISLAND = 162,
+ CTRY_COCOS_ISLANDS = 166,
CTRY_COLOMBIA = 170, /* Colombia */
+ CTRY_COMOROS = 174,
+ CTRY_CONGO = 178,
+ CTRY_COOK_ISLANDS = 184,
CTRY_COSTA_RICA = 188, /* Costa Rica */
- CTRY_CROATIA = 191, /* Croatia */
+ CTRY_COTE_DIVOIRE = 384,
+ CTRY_CROATIA = 191, /* Croatia (local name: Hrvatska) */
CTRY_CYPRUS = 196, /* Cyprus */
CTRY_CZECH = 203, /* Czech Republic */
CTRY_DENMARK = 208, /* Denmark */
+ CTRY_DJIBOUTI = 262,
+ CTRY_DOMINICA = 212,
CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */
+ CTRY_EAST_TIMOR = 626,
CTRY_ECUADOR = 218, /* Ecuador */
CTRY_EGYPT = 818, /* Egypt */
CTRY_EL_SALVADOR = 222, /* El Salvador */
+ CTRY_EQUATORIAL_GUINEA = 226,
+ CTRY_ERITREA = 232,
CTRY_ESTONIA = 233, /* Estonia */
+ CTRY_ETHIOPIA = 210,
+ CTRY_FALKLAND_ISLANDS = 238, /* (Malvinas) */
CTRY_FAEROE_ISLANDS = 234, /* Faeroe Islands */
+ CTRY_FIJI = 242,
CTRY_FINLAND = 246, /* Finland */
CTRY_FRANCE = 250, /* France */
- CTRY_FRANCE2 = 255, /* France2 */
+ CTRY_FRANCE2 = 255, /* France (Metropolitan) */
+ CTRY_FRENCH_GUIANA = 254,
+ CTRY_FRENCH_POLYNESIA = 258,
+ CTRY_FRENCH_SOUTHERN_TERRITORIES = 260,
+ CTRY_GABON = 266,
+ CTRY_GAMBIA = 270,
CTRY_GEORGIA = 268, /* Georgia */
CTRY_GERMANY = 276, /* Germany */
+ CTRY_GHANA = 288,
+ CTRY_GIBRALTAR = 292,
CTRY_GREECE = 300, /* Greece */
+ CTRY_GREENLAND = 304,
+ CTRY_GRENADA = 308,
+ CTRY_GUADELOUPE = 312,
+ CTRY_GUAM = 316,
CTRY_GUATEMALA = 320, /* Guatemala */
+ CTRY_GUINEA = 324,
+ CTRY_GUINEA_BISSAU = 624,
+ CTRY_GUYANA = 328,
+ /* XXX correct remainder */
+ CTRY_HAITI = 332,
CTRY_HONDURAS = 340, /* Honduras */
CTRY_HONG_KONG = 344, /* Hong Kong S.A.R., P.R.C. */
CTRY_HUNGARY = 348, /* Hungary */
@@ -113,9 +163,11 @@ enum ISOCountryCode {
CTRY_MACAU = 446, /* Macau */
CTRY_MACEDONIA = 807, /* the Former Yugoslav Republic of Macedonia */
CTRY_MALAYSIA = 458, /* Malaysia */
+ CTRY_MALTA = 470, /* Malta */
CTRY_MEXICO = 484, /* Mexico */
CTRY_MONACO = 492, /* Principality of Monaco */
CTRY_MOROCCO = 504, /* Morocco */
+ CTRY_NEPAL = 524, /* Nepal */
CTRY_NETHERLANDS = 528, /* Netherlands */
CTRY_NEW_ZEALAND = 554, /* New Zealand */
CTRY_NICARAGUA = 558, /* Nicaragua */
@@ -138,6 +190,7 @@ enum ISOCountryCode {
CTRY_SLOVENIA = 705, /* Slovenia */
CTRY_SOUTH_AFRICA = 710, /* South Africa */
CTRY_SPAIN = 724, /* Spain */
+ CTRY_SRILANKA = 144, /* Sri Lanka */
CTRY_SWEDEN = 752, /* Sweden */
CTRY_SWITZERLAND = 756, /* Switzerland */
CTRY_SYRIA = 760, /* Syria */
@@ -158,18 +211,39 @@ enum ISOCountryCode {
CTRY_ZIMBABWE = 716, /* Zimbabwe */
};
+enum RegdomainCode {
+ SKU_FCC = 0x10, /* FCC, aka United States */
+ SKU_CA = 0x20, /* North America, aka Canada */
+ SKU_ETSI = 0x30, /* Europe */
+ SKU_ETSI2 = 0x32, /* Europe w/o HT40 in 5GHz */
+ SKU_ETSI3 = 0x33, /* Europe - channel 36 */
+ SKU_FCC3 = 0x3a, /* FCC w/5470 band, 11h, DFS */
+ SKU_JAPAN = 0x40,
+ SKU_KOREA = 0x45,
+ SKU_APAC = 0x50, /* Asia Pacific */
+ SKU_APAC2 = 0x51, /* Asia Pacific w/ DFS on mid-band */
+ SKU_APAC3 = 0x5d, /* Asia Pacific w/o ISM band */
+ SKU_ROW = 0x81, /* China/Taiwan/Rest of World */
+ SKU_NONE = 0xf0, /* "Region Free" */
+ SKU_DEBUG = 0x1ff
+};
+
#if defined(__KERNEL__) || defined(_KERNEL)
#define CTRY_DEBUG 0x1ff /* debug */
#define CTRY_DEFAULT 0 /* default */
void ieee80211_regdomain_attach(struct ieee80211com *);
void ieee80211_regdomain_detach(struct ieee80211com *);
+void ieee80211_regdomain_vattach(struct ieee80211vap *);
+void ieee80211_regdomain_vdetach(struct ieee80211vap *);
-void ieee80211_init_channels(struct ieee80211com *ic,
- int rd, enum ISOCountryCode cc, int bands, int outdoor, int ecm);
-uint8_t *ieee80211_add_countryie(uint8_t *, struct ieee80211com *,
- enum ISOCountryCode cc, int location);
-const char *ieee80211_cctoiso(enum ISOCountryCode);
-int ieee80211_isotocc(const char iso[2]);
+int ieee80211_init_channels(struct ieee80211com *,
+ const struct ieee80211_regdomain *, const uint8_t bands[]);
+void ieee80211_sort_channels(struct ieee80211_channel chans[], int nchans);
+struct ieee80211_appie;
+struct ieee80211_appie *ieee80211_alloc_countryie(struct ieee80211com *);
+struct ieee80211_regdomain_req;
+int ieee80211_setregdomain(struct ieee80211vap *,
+ struct ieee80211_regdomain_req *);
#endif /* defined(__KERNEL__) || defined(_KERNEL) */
#endif /* _NET80211_IEEE80211_REGDOMAIN_H_ */
diff --git a/sys/net80211/ieee80211_rssadapt.c b/sys/net80211/ieee80211_rssadapt.c
new file mode 100644
index 0000000..f1fc409
--- /dev/null
+++ b/sys/net80211/ieee80211_rssadapt.c
@@ -0,0 +1,273 @@
+/* $FreeBSD$ */
+/* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David
+ * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_rssadapt.h>
+
+struct rssadapt_expavgctl {
+ /* RSS threshold decay. */
+ u_int rc_decay_denom;
+ u_int rc_decay_old;
+ /* RSS threshold update. */
+ u_int rc_thresh_denom;
+ u_int rc_thresh_old;
+ /* RSS average update. */
+ u_int rc_avgrssi_denom;
+ u_int rc_avgrssi_old;
+};
+
+static struct rssadapt_expavgctl master_expavgctl = {
+ rc_decay_denom : 16,
+ rc_decay_old : 15,
+ rc_thresh_denom : 8,
+ rc_thresh_old : 4,
+ rc_avgrssi_denom : 8,
+ rc_avgrssi_old : 4
+};
+
+#ifdef interpolate
+#undef interpolate
+#endif
+#define interpolate(parm, old, new) ((parm##_old * (old) + \
+ (parm##_denom - parm##_old) * (new)) / \
+ parm##_denom)
+
+static void rssadapt_sysctlattach(struct ieee80211_rssadapt *rs,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
+
+void
+ieee80211_rssadapt_setinterval(struct ieee80211_rssadapt *rs, int msecs)
+{
+ int t;
+
+ if (msecs < 100)
+ msecs = 100;
+ t = msecs_to_ticks(msecs);
+ rs->interval = (t < 1) ? 1 : t;
+}
+
+void
+ieee80211_rssadapt_init(struct ieee80211_rssadapt *rs, struct ieee80211vap *vap, int interval)
+{
+ rs->vap = vap;
+ ieee80211_rssadapt_setinterval(rs, interval);
+
+ rssadapt_sysctlattach(rs, vap->iv_sysctl, vap->iv_oid);
+}
+
+void
+ieee80211_rssadapt_cleanup(struct ieee80211_rssadapt *rs)
+{
+}
+
+static void
+rssadapt_updatestats(struct ieee80211_rssadapt_node *ra)
+{
+ long interval;
+
+ ra->ra_pktrate = (ra->ra_pktrate + 10*(ra->ra_nfail + ra->ra_nok))/2;
+ ra->ra_nfail = ra->ra_nok = 0;
+
+ /*
+ * A node is eligible for its rate to be raised every 1/10 to 10
+ * seconds, more eligible in proportion to recent packet rates.
+ */
+ interval = MAX(10*1000, 10*1000 / MAX(1, 10 * ra->ra_pktrate));
+ ra->ra_raise_interval = msecs_to_ticks(interval);
+}
+
+void
+ieee80211_rssadapt_node_init(struct ieee80211_rssadapt *rsa,
+ struct ieee80211_rssadapt_node *ra, struct ieee80211_node *ni)
+{
+ const struct ieee80211_rateset *rs = &ni->ni_rates;
+
+ ra->ra_rs = rsa;
+ ra->ra_rates = *rs;
+ rssadapt_updatestats(ra);
+
+ /* pick initial rate */
+ for (ra->ra_rix = rs->rs_nrates - 1;
+ ra->ra_rix > 0 && (rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL) > 72;
+ ra->ra_rix--)
+ ;
+ ni->ni_txrate = rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL;
+ ra->ra_ticks = ticks;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "RSSADAPT initial rate %d", ni->ni_txrate);
+}
+
+static __inline int
+bucket(int pktlen)
+{
+ int i, top, thridx;
+
+ for (i = 0, top = IEEE80211_RSSADAPT_BKT0;
+ i < IEEE80211_RSSADAPT_BKTS;
+ i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) {
+ thridx = i;
+ if (pktlen <= top)
+ break;
+ }
+ return thridx;
+}
+
+int
+ieee80211_rssadapt_choose(struct ieee80211_node *ni,
+ struct ieee80211_rssadapt_node *ra, u_int pktlen)
+{
+ const struct ieee80211_rateset *rs = &ra->ra_rates;
+ uint16_t (*thrs)[IEEE80211_RATE_SIZE];
+ int rix, rssi;
+
+ if ((ticks - ra->ra_ticks) > ra->ra_rs->interval) {
+ rssadapt_updatestats(ra);
+ ra->ra_ticks = ticks;
+ }
+
+ thrs = &ra->ra_rate_thresh[bucket(pktlen)];
+
+ /* XXX this is average rssi, should be using last value */
+ rssi = ni->ni_ic->ic_node_getrssi(ni);
+ for (rix = rs->rs_nrates-1; rix >= 0; rix--)
+ if ((*thrs)[rix] < (rssi << 8))
+ break;
+ if (rix != ra->ra_rix) {
+ /* update public rate */
+ ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+ ra->ra_rix = rix;
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "RSSADAPT new rate %d (pktlen %d rssi %d)",
+ ni->ni_txrate, pktlen, rssi);
+ }
+ return rix;
+}
+
+/*
+ * Adapt the data rate to suit the conditions. When a transmitted
+ * packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions,
+ * raise the RSS threshold for transmitting packets of similar length at
+ * the same data rate.
+ */
+void
+ieee80211_rssadapt_lower_rate(struct ieee80211_rssadapt_node *ra,
+ int pktlen, int rssi)
+{
+ uint16_t last_thr;
+ uint16_t (*thrs)[IEEE80211_RATE_SIZE];
+ u_int rix;
+
+ thrs = &ra->ra_rate_thresh[bucket(pktlen)];
+
+ rix = ra->ra_rix;
+ last_thr = (*thrs)[rix];
+ (*thrs)[rix] = interpolate(master_expavgctl.rc_thresh,
+ last_thr, (rssi << 8));
+
+ IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
+ "RSSADAPT lower threshold for rate %d (last_thr %d new thr %d rssi %d)\n",
+ ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
+ last_thr, (*thrs)[rix], rssi);
+}
+
+void
+ieee80211_rssadapt_raise_rate(struct ieee80211_rssadapt_node *ra,
+ int pktlen, int rssi)
+{
+ uint16_t (*thrs)[IEEE80211_RATE_SIZE];
+ uint16_t newthr, oldthr;
+ int rix;
+
+ thrs = &ra->ra_rate_thresh[bucket(pktlen)];
+
+ rix = ra->ra_rix;
+ if ((*thrs)[rix + 1] > (*thrs)[rix]) {
+ oldthr = (*thrs)[rix + 1];
+ if ((*thrs)[rix] == 0)
+ newthr = (rssi << 8);
+ else
+ newthr = (*thrs)[rix];
+ (*thrs)[rix + 1] = interpolate(master_expavgctl.rc_decay,
+ oldthr, newthr);
+
+ IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
+ "RSSADAPT raise threshold for rate %d (oldthr %d newthr %d rssi %d)\n",
+ ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
+ oldthr, newthr, rssi);
+
+ ra->ra_last_raise = ticks;
+ }
+}
+
+static int
+rssadapt_sysctl_interval(SYSCTL_HANDLER_ARGS)
+{
+ struct ieee80211_rssadapt *rs = arg1;
+ int msecs = ticks_to_msecs(rs->interval);
+ int error;
+
+ error = sysctl_handle_int(oidp, &msecs, 0, req);
+ if (error || !req->newptr)
+ return error;
+ ieee80211_rssadapt_setinterval(rs, msecs);
+ return 0;
+}
+
+static void
+rssadapt_sysctlattach(struct ieee80211_rssadapt *rs,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
+{
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "rssadapt_rate_interval", CTLTYPE_INT | CTLFLAG_RW, rs,
+ 0, rssadapt_sysctl_interval, "I", "rssadapt operation interval (ms)");
+}
+
+/*
+ * Module glue.
+ */
+IEEE80211_RATE_MODULE(rssadapt, 1);
diff --git a/sys/net80211/ieee80211_rssadapt.h b/sys/net80211/ieee80211_rssadapt.h
new file mode 100644
index 0000000..b454f43
--- /dev/null
+++ b/sys/net80211/ieee80211_rssadapt.h
@@ -0,0 +1,101 @@
+/* $FreeBSD$ */
+/* $NetBSD: ieee80211_rssadapt.h,v 1.4 2005/02/26 22:45:09 perry Exp $ */
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David
+ * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+#ifndef _NET80211_IEEE80211_RSSADAPT_H_
+#define _NET80211_IEEE80211_RSSADAPT_H_
+
+/* Data-rate adaptation loosely based on "Link Adaptation Strategy
+ * for IEEE 802.11 WLAN via Received Signal Strength Measurement"
+ * by Javier del Prado Pavon and Sunghyun Choi.
+ */
+
+/* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */
+#define IEEE80211_RSSADAPT_BKTS 3
+#define IEEE80211_RSSADAPT_BKT0 128
+#define IEEE80211_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */
+
+struct ieee80211_rssadapt {
+ struct ieee80211vap *vap;
+ int interval; /* update interval (ticks) */
+};
+
+struct ieee80211_rssadapt_node {
+ struct ieee80211_rssadapt *ra_rs; /* backpointer */
+ struct ieee80211_rateset ra_rates; /* negotiated rates */
+ int ra_rix; /* current rate index */
+ int ra_ticks; /* time of last update */
+ int ra_last_raise; /* time of last rate raise */
+ int ra_raise_interval; /* rate raise time threshold */
+
+ /* Tx failures in this update interval */
+ uint32_t ra_nfail;
+ /* Tx successes in this update interval */
+ uint32_t ra_nok;
+ /* exponential average packets/second */
+ uint32_t ra_pktrate;
+ /* RSSI threshold for each Tx rate */
+ uint16_t ra_rate_thresh[IEEE80211_RSSADAPT_BKTS]
+ [IEEE80211_RATE_SIZE];
+};
+
+void ieee80211_rssadapt_init(struct ieee80211_rssadapt *,
+ struct ieee80211vap *, int);
+void ieee80211_rssadapt_cleanup(struct ieee80211_rssadapt *);
+void ieee80211_rssadapt_setinterval(struct ieee80211_rssadapt *, int);
+void ieee80211_rssadapt_node_init(struct ieee80211_rssadapt *,
+ struct ieee80211_rssadapt_node *, struct ieee80211_node *);
+int ieee80211_rssadapt_choose(struct ieee80211_node *,
+ struct ieee80211_rssadapt_node *, u_int);
+
+/* NB: these are public only for the inline below */
+void ieee80211_rssadapt_raise_rate(struct ieee80211_rssadapt_node *,
+ int pktlen, int rssi);
+void ieee80211_rssadapt_lower_rate(struct ieee80211_rssadapt_node *,
+ int pktlen, int rssi);
+
+#define IEEE80211_RSSADAPT_SUCCESS 1
+#define IEEE80211_RSSADAPT_FAILURE 0
+
+static __inline void
+ieee80211_rssadapt_tx_complete(struct ieee80211_rssadapt_node *ra,
+ int success, int pktlen, int rssi)
+{
+ if (success) {
+ ra->ra_nok++;
+ if ((ra->ra_rix + 1) < ra->ra_rates.rs_nrates &&
+ (ticks - ra->ra_last_raise) >= ra->ra_raise_interval)
+ ieee80211_rssadapt_raise_rate(ra, pktlen, rssi);
+ } else {
+ ra->ra_nfail++;
+ ieee80211_rssadapt_lower_rate(ra, pktlen, rssi);
+ }
+}
+#endif /* _NET80211_IEEE80211_RSSADAPT_H_ */
diff --git a/sys/net80211/ieee80211_scan.c b/sys/net80211/ieee80211_scan.c
index 6f6b9a6..cec9673 100644
--- a/sys/net80211/ieee80211_scan.c
+++ b/sys/net80211/ieee80211_scan.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 scanning support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -72,7 +74,7 @@ struct scan_state {
/*
* Roaming-related defaults. RSSI thresholds are as returned by the
* driver (dBm). Transmit rate thresholds are IEEE rate codes (i.e
- * .5M units).
+ * .5M units) or MCS.
*/
#define ROAM_RSSI_11A_DEFAULT 14 /* rssi threshold for 11a bss */
#define ROAM_RSSI_11B_DEFAULT 14 /* rssi threshold for 11b bss */
@@ -80,10 +82,11 @@ struct scan_state {
#define ROAM_RATE_11A_DEFAULT 2*12 /* tx rate thresh for 11a bss */
#define ROAM_RATE_11B_DEFAULT 2*5 /* tx rate thresh for 11b bss */
#define ROAM_RATE_11BONLY_DEFAULT 2*1 /* tx rate thresh for 11b-only bss */
+#define ROAM_MCS_11N_DEFAULT 1 /* tx MCS thresh for 11n bss*/
static void scan_restart_pwrsav(void *);
-static void scan_curchan(struct ieee80211com *, unsigned long);
-static void scan_mindwell(struct ieee80211com *);
+static void scan_curchan(struct ieee80211_scan_state *, unsigned long);
+static void scan_mindwell(struct ieee80211_scan_state *);
static void scan_next(void *);
MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
@@ -93,29 +96,17 @@ ieee80211_scan_attach(struct ieee80211com *ic)
{
struct scan_state *ss;
- ic->ic_roaming = IEEE80211_ROAMING_AUTO;
-
MALLOC(ss, struct scan_state *, sizeof(struct scan_state),
M_80211_SCAN, M_NOWAIT | M_ZERO);
if (ss == NULL) {
ic->ic_scan = NULL;
return;
}
- callout_init(&ss->ss_scan_timer, CALLOUT_MPSAFE);
+ callout_init_mtx(&ss->ss_scan_timer, &ic->ic_comlock, 0);
ic->ic_scan = &ss->base;
ic->ic_scan_curchan = scan_curchan;
ic->ic_scan_mindwell = scan_mindwell;
-
- ic->ic_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz;
- ic->ic_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz;
- ic->ic_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz;
- ic->ic_roam.rssi11a = ROAM_RSSI_11A_DEFAULT;
- ic->ic_roam.rssi11b = ROAM_RSSI_11B_DEFAULT;
- ic->ic_roam.rssi11bOnly = ROAM_RSSI_11BONLY_DEFAULT;
- ic->ic_roam.rate11a = ROAM_RATE_11A_DEFAULT;
- ic->ic_roam.rate11b = ROAM_RATE_11B_DEFAULT;
- ic->ic_roam.rate11bOnly = ROAM_RATE_11BONLY_DEFAULT;
}
void
@@ -135,31 +126,94 @@ ieee80211_scan_detach(struct ieee80211com *ic)
}
}
+static __inline void
+setparams(struct ieee80211_roamparam *rp, int8_t rssi, uint8_t txrate)
+{
+ rp->rssi = rssi;
+ rp->rate = txrate;
+}
+
+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;
+
+ /* NB: only set supported modes so user apps can identify */
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11A))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11A],
+ ROAM_RSSI_11A_DEFAULT, ROAM_RATE_11A_DEFAULT);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11G))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11G],
+ ROAM_RSSI_11B_DEFAULT, ROAM_RATE_11B_DEFAULT);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11B))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11B],
+ ROAM_RSSI_11BONLY_DEFAULT, ROAM_RATE_11BONLY_DEFAULT);
+ /* NB: default turbo controls to be the same as !turbo */
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_TURBO_A))
+ vap->iv_roamparms[IEEE80211_MODE_TURBO_A] =
+ vap->iv_roamparms[IEEE80211_MODE_11A];
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_TURBO_G))
+ vap->iv_roamparms[IEEE80211_MODE_TURBO_G] =
+ vap->iv_roamparms[IEEE80211_MODE_11G];
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_STURBO_A))
+ vap->iv_roamparms[IEEE80211_MODE_STURBO_A] =
+ vap->iv_roamparms[IEEE80211_MODE_11A];
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11NA],
+ ROAM_RSSI_11A_DEFAULT, ROAM_MCS_11N_DEFAULT | 0x80);
+ if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG))
+ setparams(&vap->iv_roamparms[IEEE80211_MODE_11NG],
+ ROAM_RSSI_11B_DEFAULT, ROAM_MCS_11N_DEFAULT | 0x80);
+}
+
+void
+ieee80211_scan_vdetach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss;
+
+ IEEE80211_LOCK(ic);
+ ss = ic->ic_scan;
+ if (ss != NULL && ss->ss_vap == vap) {
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ /* XXX callout_drain */
+ callout_stop(&SCAN_PRIVATE(ss)->ss_scan_timer);
+ ic->ic_flags &= ~IEEE80211_F_SCAN;
+ }
+ if (ss->ss_ops != NULL) {
+ ss->ss_ops->scan_detach(ss);
+ ss->ss_ops = NULL;
+ }
+ ss->ss_vap = NULL;
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
/*
* Simple-minded scanner module support.
*/
-#define IEEE80211_SCANNER_MAX (IEEE80211_M_MONITOR+1)
-
-static const char *scan_modnames[IEEE80211_SCANNER_MAX] = {
+static const char *scan_modnames[IEEE80211_OPMODE_MAX] = {
"wlan_scan_sta", /* IEEE80211_M_IBSS */
"wlan_scan_sta", /* IEEE80211_M_STA */
"wlan_scan_wds", /* IEEE80211_M_WDS */
"wlan_scan_sta", /* IEEE80211_M_AHDEMO */
- "wlan_scan_4", /* n/a */
- "wlan_scan_5", /* n/a */
"wlan_scan_ap", /* IEEE80211_M_HOSTAP */
- "wlan_scan_7", /* n/a */
"wlan_scan_monitor", /* IEEE80211_M_MONITOR */
};
-static const struct ieee80211_scanner *scanners[IEEE80211_SCANNER_MAX];
+static const struct ieee80211_scanner *scanners[IEEE80211_OPMODE_MAX];
const struct ieee80211_scanner *
ieee80211_scanner_get(enum ieee80211_opmode mode)
{
- if (mode >= IEEE80211_SCANNER_MAX)
+ if (mode >= IEEE80211_OPMODE_MAX)
return NULL;
- /* NB: avoid monitor mode; there is no scan support */
- if (mode != IEEE80211_M_MONITOR && scanners[mode] == NULL)
+ if (scanners[mode] == NULL)
ieee80211_load_module(scan_modnames[mode]);
return scanners[mode];
}
@@ -168,7 +222,7 @@ void
ieee80211_scanner_register(enum ieee80211_opmode mode,
const struct ieee80211_scanner *scan)
{
- if (mode >= IEEE80211_SCANNER_MAX)
+ if (mode >= IEEE80211_OPMODE_MAX)
return;
scanners[mode] = scan;
}
@@ -177,7 +231,7 @@ void
ieee80211_scanner_unregister(enum ieee80211_opmode mode,
const struct ieee80211_scanner *scan)
{
- if (mode >= IEEE80211_SCANNER_MAX)
+ if (mode >= IEEE80211_OPMODE_MAX)
return;
if (scanners[mode] == scan)
scanners[mode] = NULL;
@@ -188,7 +242,7 @@ ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
{
int m;
- for (m = 0; m < IEEE80211_SCANNER_MAX; m++)
+ for (m = 0; m < IEEE80211_OPMODE_MAX; m++)
if (scanners[m] == scan)
scanners[m] = NULL;
}
@@ -201,35 +255,50 @@ ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
* ensure later callbacks find ss_ops set to properly
* reflect current operating mode.
*/
-int
-ieee80211_scan_update(struct ieee80211com *ic)
+static void
+scan_update_locked(struct ieee80211vap *vap,
+ const struct ieee80211_scanner *scan)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
- const struct ieee80211_scanner *scan;
- scan = ieee80211_scanner_get(ic->ic_opmode);
- IEEE80211_LOCK(ic);
- if (scan == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no scanner support for mode %u\n",
- __func__, ic->ic_opmode);
- /* XXX stat */
+ IEEE80211_LOCK_ASSERT(ic);
+
+#ifdef IEEE80211_DEBUG
+ if (ss->ss_vap != vap || ss->ss_ops != scan) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: current scanner is <%s:%s>, switch to <%s:%s>\n",
+ __func__,
+ ss->ss_vap != NULL ?
+ ss->ss_vap->iv_ifp->if_xname : "none",
+ ss->ss_vap != NULL ?
+ ieee80211_opmode_name[ss->ss_vap->iv_opmode] : "none",
+ vap->iv_ifp->if_xname,
+ ieee80211_opmode_name[vap->iv_opmode]);
}
- ss->ss_ic = ic;
+#endif
+ ss->ss_vap = vap;
if (ss->ss_ops != scan) {
- /* switch scanners; detach old, attach new */
- if (ss->ss_ops != NULL)
- ss->ss_ops->scan_detach(ss);
- if (scan != NULL && !scan->scan_attach(ss)) {
- /* XXX attach failure */
- /* XXX stat+msg */
- ss->ss_ops = NULL;
- } else
- ss->ss_ops = scan;
+ /*
+ * Switch scanners; detach old, attach new. Special
+ * case where a single scan module implements multiple
+ * policies by using different scan ops but a common
+ * core. We assume if the old and new attach methods
+ * are identical then it's ok to just change ss_ops
+ * and not flush the internal state of the module.
+ */
+ if (scan == NULL || ss->ss_ops == NULL ||
+ ss->ss_ops->scan_attach != scan->scan_attach) {
+ if (ss->ss_ops != NULL)
+ ss->ss_ops->scan_detach(ss);
+ if (scan != NULL && !scan->scan_attach(ss)) {
+ /* XXX attach failure */
+ /* XXX stat+msg */
+ scan = NULL;
+ }
+ }
+ ss->ss_ops = scan;
}
- IEEE80211_UNLOCK(ic);
-
- return (scan != NULL);
}
static void
@@ -263,7 +332,7 @@ channel_type(const struct ieee80211_channel *c)
void
ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
{
- struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211com *ic = ss->ss_vap->iv_ic;
const char *sep;
int i;
@@ -277,6 +346,18 @@ ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
}
}
+#ifdef IEEE80211_DEBUG
+static void
+scan_dump(struct ieee80211_scan_state *ss)
+{
+ struct ieee80211vap *vap = ss->ss_vap;
+
+ if_printf(vap->iv_ifp, "scan set ");
+ ieee80211_scan_dump_channels(ss);
+ printf(" dwell min %lu max %lu\n", ss->ss_mindwell, ss->ss_maxdwell);
+}
+#endif /* IEEE80211_DEBUG */
+
/*
* Enable station power save mode and start/restart the scanning thread.
*/
@@ -284,23 +365,24 @@ static void
scan_restart_pwrsav(void *arg)
{
struct scan_state *ss = (struct scan_state *) arg;
- struct ieee80211com *ic = ss->base.ss_ic;
- int delay;
+ struct ieee80211vap *vap = ss->base.ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
+ int ticksdelay;
- ieee80211_sta_pwrsave(ic, 1);
+ ieee80211_sta_pwrsave(vap, 1);
/*
- * Use an initial 1ms delay to insure the null
+ * Use an initial 1ms delay so the null
* data frame has a chance to go out.
* XXX 1ms is a lot, better to trigger scan
* on tx complete.
*/
- delay = hz/1000;
- if (delay < 1)
- delay = 1;
+ ticksdelay = msecs_to_ticks(1);
+ if (ticksdelay < 1)
+ ticksdelay = 1;
ic->ic_scan_start(ic); /* notify driver */
- ss->ss_scanend = ticks + delay + ss->ss_duration;
+ ss->ss_scanend = ticks + ticksdelay + ss->ss_duration;
ss->ss_iflags |= ISCAN_START;
- callout_reset(&ss->ss_scan_timer, delay, scan_next, ss);
+ callout_reset(&ss->ss_scan_timer, ticksdelay, scan_next, ss);
}
/*
@@ -313,17 +395,18 @@ scan_restart_pwrsav(void *arg)
static int
scan_restart(struct scan_state *ss, u_int duration)
{
- struct ieee80211com *ic = ss->base.ss_ic;
+ struct ieee80211vap *vap = ss->base.ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
int defer = 0;
if (ss->base.ss_next == ss->base.ss_last) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no channels to scan\n", __func__);
return 0;
}
- if (ic->ic_opmode == IEEE80211_M_STA &&
- ic->ic_state == IEEE80211_S_RUN) {
- if ((ic->ic_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_state == IEEE80211_S_RUN) {
+ if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
/*
* Initiate power save before going off-channel.
* Note that we cannot do this directly because
@@ -346,12 +429,12 @@ scan_restart(struct scan_state *ss, u_int duration)
}
static void
-copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss,
+copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss,
int nssid, const struct ieee80211_scan_ssid ssids[])
{
if (nssid > IEEE80211_SCAN_MAX_SSID) {
/* XXX printf */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: too many ssid %d, ignoring all of them\n",
__func__, nssid);
return;
@@ -363,76 +446,97 @@ copy_ssid(struct ieee80211com *ic, struct ieee80211_scan_state *ss,
/*
* Start a scan unless one is already going.
*/
-int
-ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration,
+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[])
{
- const struct ieee80211_scanner *scan;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
- scan = ieee80211_scanner_get(ic->ic_opmode);
- if (scan == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no scanner support for mode %u\n",
- __func__, ic->ic_opmode);
- /* XXX stat */
- return 0;
- }
+ IEEE80211_LOCK_ASSERT(ic);
- IEEE80211_LOCK(ic);
- if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n"
+ 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
- , ieee80211_phymode_name[ic->ic_des_mode]
+ , 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" : ""
);
- ss->ss_ic = ic;
- if (ss->ss_ops != scan) {
- /* switch scanners; detach old, attach new */
- if (ss->ss_ops != NULL)
- ss->ss_ops->scan_detach(ss);
- if (!scan->scan_attach(ss)) {
- /* XXX attach failure */
- /* XXX stat+msg */
- ss->ss_ops = NULL;
- } else
- ss->ss_ops = scan;
- }
+ scan_update_locked(vap, scan);
if (ss->ss_ops != NULL) {
if ((flags & IEEE80211_SCAN_NOSSID) == 0)
- copy_ssid(ic, ss, nssid, ssids);
+ 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)
- ic->ic_stats.is_scan_active++;
+ vap->iv_stats.is_scan_active++;
else
- ic->ic_stats.is_scan_passive++;
+ vap->iv_stats.is_scan_passive++;
if (flags & IEEE80211_SCAN_FLUSH)
ss->ss_ops->scan_flush(ss);
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
- ss->ss_ops->scan_start(ss, ic);
+ ss->ss_next = 0;
+ ss->ss_mindwell = mindwell;
+ ss->ss_maxdwell = maxdwell;
+ ss->ss_ops->scan_start(ss, vap);
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(vap))
+ scan_dump(ss);
+#endif /* IEEE80211_DEBUG */
if (scan_restart(SCAN_PRIVATE(ss), duration))
ic->ic_flags |= IEEE80211_F_SCAN;
}
} else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan already in progress\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
}
+ return (ic->ic_flags & IEEE80211_F_SCAN);
+}
+
+/*
+ * Start a scan unless one is already going.
+ */
+int
+ieee80211_start_scan(struct ieee80211vap *vap, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
+ u_int nssid, const struct ieee80211_scan_ssid ssids[])
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_scanner *scan;
+ int result;
+
+ scan = ieee80211_scanner_get(vap->iv_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for %s mode\n",
+ __func__, ieee80211_opmode_name[vap->iv_opmode]);
+ /* XXX stat */
+ return 0;
+ }
+
+ IEEE80211_LOCK(ic);
+ result = start_scan_locked(scan, vap, flags, duration,
+ mindwell, maxdwell, nssid, ssids);
IEEE80211_UNLOCK(ic);
- /* NB: racey, does it matter? */
- return (ic->ic_flags & IEEE80211_F_SCAN);
+ return result;
}
/*
@@ -440,11 +544,23 @@ ieee80211_start_scan(struct ieee80211com *ic, int flags, u_int duration,
* fails then kick off a new scan.
*/
int
-ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
+ieee80211_check_scan(struct ieee80211vap *vap, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[])
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
- int checkscanlist = 0;
+ const struct ieee80211_scanner *scan;
+ int checkscanlist = 0, result;
+
+ scan = ieee80211_scanner_get(vap->iv_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for %s mode\n",
+ __func__, vap->iv_opmode);
+ /* XXX stat */
+ return 0;
+ }
/*
* Check if there's a list of scan candidates already.
@@ -452,30 +568,35 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
*/
IEEE80211_LOCK(ic);
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: %s scan, duration %u, desired mode %s, %s%s%s%s\n"
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: %s scan, %s%s%s%s%s\n"
, __func__
, flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
- , duration
- , ieee80211_phymode_name[ic->ic_des_mode]
, flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
, flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
+ , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : ""
, flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
, flags & IEEE80211_SCAN_ONCE ? ", once" : ""
);
+ if (ss->ss_ops != scan) {
+ /* XXX re-use cache contents? e.g. adhoc<->sta */
+ flags |= IEEE80211_SCAN_FLUSH;
+ }
+ scan_update_locked(vap, scan);
if (ss->ss_ops != NULL) {
- /* XXX verify ss_ops matches ic->ic_opmode */
+ /* 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(ic, ss, nssid, ssids);
+ copy_ssid(vap, ss, nssid, ssids);
flags |= IEEE80211_SCAN_NOSSID;
}
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
- time_before(ticks, ic->ic_lastscan + ic->ic_scanvalid)) {
+ (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
@@ -486,30 +607,40 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
*/
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
ic->ic_flags |= IEEE80211_F_SCAN;
+ /* NB: need to use supplied flags in check below */
+ ss->ss_flags = flags & 0xff;
checkscanlist = 1;
}
}
- IEEE80211_UNLOCK(ic);
if (checkscanlist) {
- const struct ieee80211_scanner *scan;
-
- scan = ieee80211_scanner_get(ic->ic_opmode);
- if (scan == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no scanner support for mode %u\n",
- __func__, ic->ic_opmode);
- /* XXX stat */
- return 0;
- }
- if (scan == ss->ss_ops && ss->ss_ops->scan_end(ss, ic)) {
+ if (ss->ss_ops->scan_end(ss, vap)) {
/* found an ap, just clear the flag */
ic->ic_flags &= ~IEEE80211_F_SCAN;
+ ieee80211_notify_scan_done(vap);
+ IEEE80211_UNLOCK(ic);
return 1;
}
/* no ap, clear the flag before starting a scan */
ic->ic_flags &= ~IEEE80211_F_SCAN;
}
- return ieee80211_start_scan(ic, flags, duration, nssid, ssids);
+ result = start_scan_locked(scan, vap, flags, duration,
+ mindwell, maxdwell, nssid, ssids);
+ IEEE80211_UNLOCK(ic);
+
+ return result;
+}
+
+/*
+ * Check the scan cache for an ap/channel to use; if that fails
+ * then kick off a scan using the current settings.
+ */
+int
+ieee80211_check_scan_current(struct ieee80211vap *vap)
+{
+ return ieee80211_check_scan(vap,
+ IEEE80211_SCAN_ACTIVE,
+ IEEE80211_SCAN_FOREVER, 0, 0,
+ vap->iv_des_nssid, vap->iv_des_ssid);
}
/*
@@ -517,9 +648,20 @@ ieee80211_check_scan(struct ieee80211com *ic, int flags, u_int duration,
* then we start again using the existing channel list.
*/
int
-ieee80211_bg_scan(struct ieee80211com *ic)
+ieee80211_bg_scan(struct ieee80211vap *vap, int flags)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
+ const struct ieee80211_scanner *scan;
+
+ scan = ieee80211_scanner_get(vap->iv_opmode);
+ if (scan == NULL) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no scanner support for %s mode\n",
+ __func__, vap->iv_opmode);
+ /* XXX stat */
+ return 0;
+ }
IEEE80211_LOCK(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
@@ -532,13 +674,14 @@ ieee80211_bg_scan(struct ieee80211com *ic)
*/
duration = IEEE80211_SCAN_OFFCHANNEL;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ 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_ic = ic;
+ ss->ss_vap = vap;
/*
* A background scan does not select a new sta; it
* just refreshes the scan cache. Also, indicate
@@ -554,15 +697,30 @@ ieee80211_bg_scan(struct ieee80211com *ic)
* usual sta power save logic.
*/
ss->ss_flags |= IEEE80211_SCAN_NOPICK
- | IEEE80211_SCAN_BGSCAN;
+ | IEEE80211_SCAN_BGSCAN
+ | flags
+ ;
/* if previous scan completed, restart */
if (ss->ss_next >= ss->ss_last) {
- ss->ss_next = 0;
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
- ic->ic_stats.is_scan_active++;
+ vap->iv_stats.is_scan_active++;
else
- ic->ic_stats.is_scan_passive++;
- ss->ss_ops->scan_restart(ss, ic);
+ 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;
@@ -575,7 +733,7 @@ ieee80211_bg_scan(struct ieee80211com *ic)
/* XXX msg+stat */
}
} else {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s scan already in progress\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
}
@@ -586,17 +744,46 @@ ieee80211_bg_scan(struct ieee80211com *ic)
}
/*
+ * Cancel any scan currently going on for the specified vap.
+ */
+void
+ieee80211_cancel_scan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK(ic);
+ if ((ic->ic_flags & IEEE80211_F_SCAN) &&
+ ss->ss_vap == vap &&
+ (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: cancel %s scan\n", __func__,
+ ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
+ "active" : "passive");
+
+ /* clear bg scan NOPICK and mark cancel request */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
+ /* force it to fire asap */
+ callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
+ 0, scan_next, ss);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
* Cancel any scan currently going on.
*/
void
-ieee80211_cancel_scan(struct ieee80211com *ic)
+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(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: cancel %s scan\n", __func__,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
"active" : "passive");
@@ -616,15 +803,12 @@ ieee80211_cancel_scan(struct ieee80211com *ic)
* scanning themselves (e.g. for firmware-based devices).
*/
void
-ieee80211_scan_next(struct ieee80211com *ic)
+ieee80211_scan_next(struct ieee80211vap *vap)
{
- /*
- * XXX: We might need/want to decouple context here by either:
- * callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
- * or using a taskqueue. Let's see what kind of problems direct
- * dispatch has for now.
- */
- scan_next(ic->ic_scan);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
}
/*
@@ -632,12 +816,52 @@ ieee80211_scan_next(struct ieee80211com *ic)
* channels (e.g. for firmware-based devices).
*/
void
-ieee80211_scan_done(struct ieee80211com *ic)
+ieee80211_scan_done(struct ieee80211vap *vap)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss;
+ IEEE80211_LOCK(ic);
+ ss = ic->ic_scan;
ss->ss_next = ss->ss_last; /* all channels are complete */
scan_next(ss);
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
+ * Probe the curent channel, if allowed, while scanning.
+ * If the channel is not marked passive-only then send
+ * a probe request immediately. Otherwise mark state and
+ * listen for beacons on the channel; if we receive something
+ * then we'll transmit a probe request.
+ */
+void
+ieee80211_probe_curchan(struct ieee80211vap *vap, int force)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ifnet *ifp = vap->iv_ifp;
+ int i;
+
+ if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) && !force) {
+ ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN;
+ return;
+ }
+ /*
+ * Send directed probe requests followed by any
+ * broadcast probe request.
+ * XXX remove dependence on ic/vap->iv_bss
+ */
+ for (i = 0; i < ss->ss_nssid; i++)
+ ieee80211_send_probereq(vap->iv_bss,
+ vap->iv_myaddr, ifp->if_broadcastaddr,
+ ifp->if_broadcastaddr,
+ ss->ss_ssid[i].ssid, ss->ss_ssid[i].len);
+ if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0)
+ ieee80211_send_probereq(vap->iv_bss,
+ vap->iv_myaddr, ifp->if_broadcastaddr,
+ ifp->if_broadcastaddr,
+ "", 0);
}
/*
@@ -646,37 +870,16 @@ ieee80211_scan_done(struct ieee80211com *ic)
* Arrange for the channel change after maxdwell ticks.
*/
static void
-scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
+scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211vap *vap = ss->ss_vap;
- if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
- (ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
- struct ifnet *ifp = ic->ic_ifp;
- int i;
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
- /*
- * Send a broadcast probe request followed by
- * any specified directed probe requests.
- * XXX suppress broadcast probe req?
- * XXX remove dependence on ic/ic->ic_bss
- * XXX move to policy code?
- */
- ieee80211_send_probereq(ic->ic_bss,
- ic->ic_myaddr, ifp->if_broadcastaddr,
- ifp->if_broadcastaddr,
- "", 0,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- for (i = 0; i < ss->ss_nssid; i++)
- ieee80211_send_probereq(ic->ic_bss,
- ic->ic_myaddr, ifp->if_broadcastaddr,
- ifp->if_broadcastaddr,
- ss->ss_ssid[i].ssid,
- ss->ss_ssid[i].len,
- ic->ic_opt_ie, ic->ic_opt_ie_len);
- }
+ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+ ieee80211_probe_curchan(vap, 0);
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
- maxdwell, scan_next, ss);
+ maxdwell, scan_next, ss);
}
/*
@@ -684,10 +887,8 @@ scan_curchan(struct ieee80211com *ic, unsigned long maxdwell)
* change to the next channel asap.
*/
static void
-scan_mindwell(struct ieee80211com *ic)
+scan_mindwell(struct ieee80211_scan_state *ss)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
-
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
}
@@ -699,17 +900,16 @@ scan_next(void *arg)
{
#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD)
struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
- struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *chan;
unsigned long maxdwell, scanend;
- int scanning, scandone;
+ int scandone;
- IEEE80211_LOCK(ic);
- scanning = (ic->ic_flags & IEEE80211_F_SCAN) != 0;
- IEEE80211_UNLOCK(ic);
- if (!scanning) /* canceled */
- return;
+ IEEE80211_LOCK_ASSERT(ic);
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0)
+ return;
again:
scandone = (ss->ss_next >= ss->ss_last) ||
(SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
@@ -728,7 +928,7 @@ again:
else
maxdwell = ss->ss_maxdwell;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: chan %3d%c -> %3d%c [%s, dwell min %lu max %lu]\n",
__func__,
ieee80211_chan2ieee(ic, ic->ic_curchan),
@@ -751,7 +951,7 @@ again:
* sending a probe request (as needed), and arming the
* timeout to switch channels after maxdwell ticks.
*/
- ic->ic_scan_curchan(ic, maxdwell);
+ ic->ic_scan_curchan(ss, maxdwell);
SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell;
/* clear mindwell lock and initial channel change flush */
@@ -768,7 +968,7 @@ again:
/* return to the bss channel */
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
ic->ic_curchan != ic->ic_bsschan)
- change_channel(ic, ic->ic_bsschan);
+ ieee80211_setcurchan(ic, ic->ic_bsschan);
/* clear internal flags and any indication of a pick */
SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
@@ -781,20 +981,21 @@ again:
* rx frames alter the scan candidate list.
*/
if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
- !ss->ss_ops->scan_end(ss, ic) &&
+ !ss->ss_ops->scan_end(ss, vap) &&
(ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
time_before(ticks + ss->ss_mindwell, scanend)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ 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)
- ic->ic_stats.is_scan_active++;
+ vap->iv_stats.is_scan_active++;
else
- ic->ic_stats.is_scan_passive++;
+ vap->iv_stats.is_scan_passive++;
+ ss->ss_ops->scan_restart(ss, vap); /* XXX? */
ic->ic_scan_start(ic); /* notify driver */
goto again;
} else {
@@ -802,7 +1003,7 @@ again:
if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
scandone = 1;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: %s, "
"[ticks %u, dwell min %lu scanend %lu]\n",
__func__, scandone ? "done" : "stopped",
@@ -825,9 +1026,9 @@ again:
* waiting for us.
*/
if (scandone) {
- ieee80211_sta_pwrsave(ic, 0);
+ ieee80211_sta_pwrsave(vap, 0);
if (ss->ss_next >= ss->ss_last) {
- ieee80211_notify_scan_done(ic);
+ ieee80211_notify_scan_done(vap);
ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
}
}
@@ -841,32 +1042,48 @@ again:
#ifdef IEEE80211_DEBUG
static void
+dump_country(const uint8_t *ie)
+{
+ const struct ieee80211_country_ie *cie =
+ (const struct ieee80211_country_ie *) ie;
+ int i, nbands, schan, nchan;
+
+ if (cie->len < 3) {
+ printf(" <bogus country ie, len %d>", cie->len);
+ return;
+ }
+ printf(" country [%c%c%c", cie->cc[0], cie->cc[1], cie->cc[2]);
+ nbands = (cie->len - 3) / sizeof(cie->band[0]);
+ for (i = 0; i < nbands; i++) {
+ schan = cie->band[i].schan;
+ nchan = cie->band[i].nchan;
+ if (nchan != 1)
+ printf(" %u-%u,%u", schan, schan + nchan-1,
+ cie->band[i].maxtxpwr);
+ else
+ printf(" %u,%u", schan, cie->band[i].maxtxpwr);
+ }
+ printf("]");
+}
+
+static void
dump_probe_beacon(uint8_t subtype, int isnew,
const uint8_t mac[IEEE80211_ADDR_LEN],
- const struct ieee80211_scanparams *sp)
+ 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],
- IEEE80211_CHAN2IEEE(sp->curchan), sp->bchan);
+ sp->chan, sp->bchan);
ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
- printf("\n");
+ printf(" rssi %d\n", rssi);
if (isnew) {
printf("[%s] caps 0x%x bintval %u erp 0x%x",
ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp);
- if (sp->country != NULL) {
-#ifdef __FreeBSD__
- printf(" country info %*D",
- sp->country[1], sp->country+2, " ");
-#else
- int i;
- printf(" country info");
- for (i = 0; i < sp->country[1]; i++)
- printf(" %02x", sp->country[i+2]);
-#endif
- }
+ if (sp->country != NULL)
+ dump_country(sp->country);
printf("\n");
}
}
@@ -876,26 +1093,27 @@ dump_probe_beacon(uint8_t subtype, int isnew,
* Process a beacon or probe response frame.
*/
void
-ieee80211_add_scan(struct ieee80211com *ic,
+ieee80211_add_scan(struct ieee80211vap *vap,
const struct ieee80211_scanparams *sp,
const struct ieee80211_frame *wh,
int subtype, int rssi, int noise, int rstamp)
{
+ 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 (we defer the channel change to the timer
- * code to simplify locking on linux).
+ * timer routine.
*/
if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD)
return;
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN))
- dump_probe_beacon(subtype, 1, wh->i_addr2, sp);
+ 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, rstamp)) {
@@ -905,7 +1123,7 @@ ieee80211_add_scan(struct ieee80211com *ic,
*/
if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 &&
time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: chan %3d%c min dwell met (%u > %lu)\n",
__func__,
ieee80211_chan2ieee(ic, ic->ic_curchan),
@@ -914,9 +1132,9 @@ ieee80211_add_scan(struct ieee80211com *ic,
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL;
/*
* NB: trigger at next clock tick or wait for the
- * hardware
+ * hardware.
*/
- ic->ic_scan_mindwell(ic);
+ ic->ic_scan_mindwell(ss);
}
}
}
@@ -938,12 +1156,12 @@ ieee80211_scan_timeout(struct ieee80211com *ic)
* Mark a scan cache entry after a successful associate.
*/
void
-ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[])
+ieee80211_scan_assoc_success(struct ieee80211vap *vap, const uint8_t mac[])
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
if (ss->ss_ops != NULL) {
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN,
mac, "%s", __func__);
ss->ss_ops->scan_assoc_success(ss, mac);
}
@@ -953,13 +1171,13 @@ ieee80211_scan_assoc_success(struct ieee80211com *ic, const uint8_t mac[])
* Demerit a scan cache entry after failing to associate.
*/
void
-ieee80211_scan_assoc_fail(struct ieee80211com *ic,
+ieee80211_scan_assoc_fail(struct ieee80211vap *vap,
const uint8_t mac[], int reason)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
if (ss->ss_ops != NULL) {
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, mac,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, mac,
"%s: reason %u", __func__, reason);
ss->ss_ops->scan_assoc_fail(ss, mac, reason);
}
@@ -969,10 +1187,10 @@ ieee80211_scan_assoc_fail(struct ieee80211com *ic,
* Iterate over the contents of the scan cache.
*/
void
-ieee80211_scan_iterate(struct ieee80211com *ic,
+ieee80211_scan_iterate(struct ieee80211vap *vap,
ieee80211_scan_iter_func *f, void *arg)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
if (ss->ss_ops != NULL)
ss->ss_ops->scan_iterate(ss, f, arg);
@@ -982,13 +1200,36 @@ ieee80211_scan_iterate(struct ieee80211com *ic,
* Flush the contents of the scan cache.
*/
void
-ieee80211_scan_flush(struct ieee80211com *ic)
+ieee80211_scan_flush(struct ieee80211vap *vap)
{
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
- if (ss->ss_ops != NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s\n", __func__);
+ if (ss->ss_ops != NULL && ss->ss_vap == vap) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", __func__);
ss->ss_ops->scan_flush(ss);
}
}
+
+/*
+ * Check the scan cache for an ap/channel to use; if that
+ * fails then kick off a new scan.
+ */
+struct ieee80211_channel *
+ieee80211_scan_pickchannel(struct ieee80211com *ic, int flags)
+{
+ struct ieee80211_scan_state *ss = ic->ic_scan;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if (ss == NULL || ss->ss_ops == NULL || ss->ss_vap == NULL) {
+ /* XXX printf? */
+ return NULL;
+ }
+ if (ss->ss_ops->scan_pickchan == NULL) {
+ IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
+ "%s: scan module does not support picking a channel, "
+ "opmode %s\n", __func__, ss->ss_vap->iv_opmode);
+ return NULL;
+ }
+ return ss->ss_ops->scan_pickchan(ss, flags);
+}
diff --git a/sys/net80211/ieee80211_scan.h b/sys/net80211/ieee80211_scan.h
index fed5e0b..df6ce1f7 100644
--- a/sys/net80211/ieee80211_scan.h
+++ b/sys/net80211/ieee80211_scan.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2005-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,18 +27,69 @@
#ifndef _NET80211_IEEE80211_SCAN_H_
#define _NET80211_IEEE80211_SCAN_H_
+/*
+ * 802.11 scanning support.
+ *
+ * Scanning is the procedure by which a station locates a bss to join
+ * (infrastructure/ibss mode), or a channel to use (when operating as
+ * an ap or ibss master). Scans are either "active" or "passive". An
+ * active scan causes one or more probe request frames to be sent on
+ * visiting each channel. A passive request causes each channel in the
+ * scan set to be visited but no frames to be transmitted; the station
+ * only listens for traffic. Note that active scanning may still need
+ * to listen for traffic before sending probe request frames depending
+ * on regulatory constraints; the 802.11 layer handles this by generating
+ * a callback when scanning on a ``passive channel'' when the
+ * IEEE80211_FEXT_PROBECHAN flag is set.
+ *
+ * A scan operation involves constructing a set of channels to inspec
+ * (the scan set), visiting each channel and collecting information
+ * (e.g. what bss are present), and then analyzing the results to make
+ * decisions like which bss to join. This process needs to be as fast
+ * as possible so we do things like intelligently construct scan sets
+ * and dwell on a channel only as long as necessary. The scan code also
+ * maintains a cache of recent scan results and uses it to bypass scanning
+ * whenever possible. The scan cache is also used to enable roaming
+ * between access points when operating in infrastructure mode.
+ *
+ * Scanning is handled with pluggable modules that implement "policy"
+ * per-operating mode. The core scanning support provides an
+ * instrastructure to support these modules and exports a common api
+ * to the rest of the 802.11 layer. Policy modules decide what
+ * channels to visit, what state to record to make decisions (e.g. ap
+ * mode scanning for auto channel selection keeps significantly less
+ * state than sta mode scanning for an ap to associate to), and selects
+ * the final station/channel to return as the result of a scan.
+ *
+ * Scanning is done synchronously when initially bringing a vap to an
+ * operational state and optionally in the background to maintain the
+ * scan cache for doing roaming and rogue ap monitoring. Scanning is
+ * not tied to the 802.11 state machine that governs vaps though there
+ * is linkage to the IEEE80211_SCAN state. Only one vap at a time may
+ * be scanning; this scheduling policy is handled in ieee80211_new_state
+ * and is invisible to the scanning code.
+*/
#define IEEE80211_SCAN_MAX IEEE80211_CHAN_MAX
-struct ieee80211_scanner;
+struct ieee80211_scanner; /* scan policy state */
struct ieee80211_scan_ssid {
- int len; /* length in bytes */
- uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
+ int len; /* length in bytes */
+ uint8_t ssid[IEEE80211_NWID_LEN]; /* ssid contents */
};
-#define IEEE80211_SCAN_MAX_SSID 1
+#define IEEE80211_SCAN_MAX_SSID 1 /* max # ssid's to probe */
+/*
+ * Scan state visible to the 802.11 layer. Scan parameters and
+ * results are stored in this data structure. The ieee80211_scan_state
+ * structure is extended with space that is maintained private to
+ * the core scanning support. We allocate one instance and link it
+ * to the ieee80211com structure; then share it between all associated
+ * vaps. We could allocate multiple of these, e.g. to hold multiple
+ * scan results, but this is sufficient for current needs.
+ */
struct ieee80211_scan_state {
- struct ieee80211com *ss_ic;
+ struct ieee80211vap *ss_vap;
const struct ieee80211_scanner *ss_ops; /* policy hookup, see below */
void *ss_priv; /* scanner private state */
uint16_t ss_flags;
@@ -47,6 +98,8 @@ struct ieee80211_scan_state {
#define IEEE80211_SCAN_PICK1ST 0x0004 /* ``hey sailor'' mode */
#define IEEE80211_SCAN_BGSCAN 0x0008 /* bg scan, exit ps at end */
#define IEEE80211_SCAN_ONCE 0x0010 /* do one complete pass */
+#define IEEE80211_SCAN_NOBCAST 0x0020 /* no broadcast probe req */
+#define IEEE80211_SCAN_NOJOIN 0x0040 /* no auto-sequencing */
#define IEEE80211_SCAN_GOTPICK 0x1000 /* got candidate, can stop */
uint8_t ss_nssid; /* # ssid's to probe/match */
struct ieee80211_scan_ssid ss_ssid[IEEE80211_SCAN_MAX_SSID];
@@ -65,48 +118,64 @@ struct ieee80211_scan_state {
* ss_flags. It might be better to split this stuff out into
* a separate variable to avoid confusion.
*/
-#define IEEE80211_SCAN_FLUSH 0x10000 /* flush candidate table */
-#define IEEE80211_SCAN_NOSSID 0x20000 /* don't update ssid list */
+#define IEEE80211_SCAN_FLUSH 0x00010000 /* flush candidate table */
+#define IEEE80211_SCAN_NOSSID 0x80000000 /* don't update ssid list */
struct ieee80211com;
void ieee80211_scan_attach(struct ieee80211com *);
void ieee80211_scan_detach(struct ieee80211com *);
+void ieee80211_scan_vattach(struct ieee80211vap *);
+void ieee80211_scan_vdetach(struct ieee80211vap *);
void ieee80211_scan_dump_channels(const struct ieee80211_scan_state *);
-int ieee80211_scan_update(struct ieee80211com *);
#define IEEE80211_SCAN_FOREVER 0x7fffffff
-int ieee80211_start_scan(struct ieee80211com *, int flags, u_int duration,
+int ieee80211_start_scan(struct ieee80211vap *, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[]);
-int ieee80211_check_scan(struct ieee80211com *, int flags, u_int duration,
+int ieee80211_check_scan(struct ieee80211vap *, int flags,
+ u_int duration, u_int mindwell, u_int maxdwell,
u_int nssid, const struct ieee80211_scan_ssid ssids[]);
-int ieee80211_bg_scan(struct ieee80211com *);
-void ieee80211_cancel_scan(struct ieee80211com *);
-void ieee80211_scan_next(struct ieee80211com *);
-void ieee80211_scan_done(struct ieee80211com *);
+int ieee80211_check_scan_current(struct ieee80211vap *);
+int ieee80211_bg_scan(struct ieee80211vap *, int);
+void ieee80211_cancel_scan(struct ieee80211vap *);
+void ieee80211_cancel_anyscan(struct ieee80211vap *);
+void ieee80211_scan_next(struct ieee80211vap *);
+void ieee80211_scan_done(struct ieee80211vap *);
+void ieee80211_probe_curchan(struct ieee80211vap *, int);
+struct ieee80211_channel *ieee80211_scan_pickchannel(struct ieee80211com *, int);
struct ieee80211_scanparams;
-void ieee80211_add_scan(struct ieee80211com *,
+void ieee80211_add_scan(struct ieee80211vap *,
const struct ieee80211_scanparams *,
const struct ieee80211_frame *,
int subtype, int rssi, int noise, int rstamp);
void ieee80211_scan_timeout(struct ieee80211com *);
-void ieee80211_scan_assoc_success(struct ieee80211com *,
+void ieee80211_scan_assoc_success(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
enum {
IEEE80211_SCAN_FAIL_TIMEOUT = 1, /* no response to mgmt frame */
IEEE80211_SCAN_FAIL_STATUS = 2 /* negative response to " " */
};
-void ieee80211_scan_assoc_fail(struct ieee80211com *,
+void ieee80211_scan_assoc_fail(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN], int reason);
-void ieee80211_scan_flush(struct ieee80211com *);
+void ieee80211_scan_flush(struct ieee80211vap *);
struct ieee80211_scan_entry;
typedef void ieee80211_scan_iter_func(void *,
const struct ieee80211_scan_entry *);
-void ieee80211_scan_iterate(struct ieee80211com *,
+void ieee80211_scan_iterate(struct ieee80211vap *,
ieee80211_scan_iter_func, void *);
+enum {
+ IEEE80211_BPARSE_BADIELEN = 0x01, /* ie len past end of frame */
+ IEEE80211_BPARSE_RATES_INVALID = 0x02, /* invalid RATES ie */
+ IEEE80211_BPARSE_XRATES_INVALID = 0x04, /* invalid XRATES ie */
+ IEEE80211_BPARSE_SSID_INVALID = 0x08, /* invalid SSID ie */
+ IEEE80211_BPARSE_CHAN_INVALID = 0x10, /* invalid FH/DSPARMS chan */
+ IEEE80211_BPARSE_OFFCHAN = 0x20, /* DSPARMS chan != curchan */
+ IEEE80211_BPARSE_BINTVAL_INVALID= 0x40, /* invalid beacon interval */
+};
/*
* Parameters supplied when adding/updating an entry in a
@@ -116,14 +185,17 @@ void ieee80211_scan_iterate(struct ieee80211com *,
* All multi-byte values must be in host byte order.
*/
struct ieee80211_scanparams {
- uint16_t capinfo; /* 802.11 capabilities */
- uint16_t fhdwell; /* FHSS dwell interval */
- struct ieee80211_channel *curchan;
- uint8_t bchan; /* chan# advertised inside beacon */
+ uint8_t status; /* bitmask of IEEE80211_BPARSE_* */
+ uint8_t chan; /* channel # from FH/DSPARMS */
+ uint8_t bchan; /* curchan's channel # */
uint8_t fhindex;
- uint8_t erp;
+ uint16_t fhdwell; /* FHSS dwell interval */
+ uint16_t capinfo; /* 802.11 capabilities */
+ uint16_t erp; /* NB: 0x100 indicates ie present */
uint16_t bintval;
uint8_t timoff;
+ uint8_t *ies; /* all captured ies */
+ size_t ies_len; /* length of all captured ies */
uint8_t *tim;
uint8_t *tstamp;
uint8_t *country;
@@ -146,13 +218,14 @@ struct ieee80211_scanparams {
struct ieee80211_scan_entry {
uint8_t se_macaddr[IEEE80211_ADDR_LEN];
uint8_t se_bssid[IEEE80211_ADDR_LEN];
+ /* XXX can point inside se_ies */
uint8_t se_ssid[2+IEEE80211_NWID_LEN];
uint8_t se_rates[2+IEEE80211_RATE_MAXSIZE];
uint8_t se_xrates[2+IEEE80211_RATE_MAXSIZE];
uint32_t se_rstamp; /* recv timestamp */
union {
uint8_t data[8];
- uint64_t tsf;
+ u_int64_t tsf;
} se_tstamp; /* from last rcv'd beacon */
uint16_t se_intval; /* beacon interval (host byte order) */
uint16_t se_capinfo; /* capabilities (host byte order) */
@@ -160,16 +233,12 @@ struct ieee80211_scan_entry {
uint16_t se_timoff; /* byte offset to TIM ie */
uint16_t se_fhdwell; /* FH only (host byte order) */
uint8_t se_fhindex; /* FH only */
- uint8_t se_erp; /* ERP from beacon/probe resp */
+ uint8_t se_dtimperiod; /* DTIM period */
+ uint16_t se_erp; /* ERP from beacon/probe resp */
int8_t se_rssi; /* avg'd recv ssi */
int8_t se_noise; /* noise floor */
- uint8_t se_dtimperiod; /* DTIM period */
- uint8_t *se_wpa_ie; /* captured WPA ie */
- uint8_t *se_rsn_ie; /* captured RSN ie */
- uint8_t *se_wme_ie; /* captured WME ie */
- uint8_t *se_htcap_ie; /* captured HTP cap ie */
- uint8_t *se_htinfo_ie; /* captured HTP info ie */
- uint8_t *se_ath_ie; /* captured Atheros ie */
+ uint8_t se_cc[2]; /* captured country code */
+ struct ieee80211_ies se_ies; /* captured ie's */
u_int se_age; /* age of entry (0 on create) */
};
MALLOC_DECLARE(M_80211_SCAN);
@@ -184,14 +253,16 @@ struct ieee80211_scanner {
int (*scan_attach)(struct ieee80211_scan_state *);
int (*scan_detach)(struct ieee80211_scan_state *);
int (*scan_start)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_restart)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_cancel)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_end)(struct ieee80211_scan_state *,
- struct ieee80211com *);
+ struct ieee80211vap *);
int (*scan_flush)(struct ieee80211_scan_state *);
+ struct ieee80211_channel *(*scan_pickchan)(
+ struct ieee80211_scan_state *, int);
/* add an entry to the cache */
int (*scan_add)(struct ieee80211_scan_state *,
const struct ieee80211_scanparams *,
diff --git a/sys/net80211/ieee80211_scan_ap.c b/sys/net80211/ieee80211_scan_ap.c
deleted file mode 100644
index 300f440..0000000
--- a/sys/net80211/ieee80211_scan_ap.c
+++ /dev/null
@@ -1,408 +0,0 @@
-/*-
- * Copyright (c) 2002-2007 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 ap scanning support.
- */
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/module.h>
-
-#include <sys/socket.h>
-
-#include <net/if.h>
-#include <net/if_media.h>
-#include <net/ethernet.h>
-
-#include <net80211/ieee80211_var.h>
-
-#include <net/bpf.h>
-
-struct ap_state {
- int as_maxrssi[IEEE80211_CHAN_MAX];
-};
-
-static int ap_flush(struct ieee80211_scan_state *);
-
-/* number of references from net80211 layer */
-static int nrefs = 0;
-
-/*
- * Attach prior to any scanning work.
- */
-static int
-ap_attach(struct ieee80211_scan_state *ss)
-{
- struct ap_state *as;
-
- MALLOC(as, struct ap_state *, sizeof(struct ap_state),
- M_80211_SCAN, M_NOWAIT);
- ss->ss_priv = as;
- ap_flush(ss);
- nrefs++; /* NB: we assume caller locking */
- return 1;
-}
-
-/*
- * Cleanup any private state.
- */
-static int
-ap_detach(struct ieee80211_scan_state *ss)
-{
- struct ap_state *as = ss->ss_priv;
-
- if (as != NULL) {
- KASSERT(nrefs > 0, ("imbalanced attach/detach"));
- nrefs--; /* NB: we assume caller locking */
- FREE(as, M_80211_SCAN);
- }
- return 1;
-}
-
-/*
- * Flush all per-scan state.
- */
-static int
-ap_flush(struct ieee80211_scan_state *ss)
-{
- struct ap_state *as = ss->ss_priv;
-
- memset(as->as_maxrssi, 0, sizeof(as->as_maxrssi));
- ss->ss_last = 0; /* insure no channel will be picked */
- return 0;
-}
-
-static int
-find11gchannel(struct ieee80211com *ic, int i, int freq)
-{
- const struct ieee80211_channel *c;
- int j;
-
- /*
- * The normal ordering in the channel list is b channel
- * immediately followed by g so optimize the search for
- * this. We'll still do a full search just in case.
- */
- for (j = i+1; j < ic->ic_nchans; j++) {
- c = &ic->ic_channels[j];
- if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
- return 1;
- }
- for (j = 0; j < i; j++) {
- c = &ic->ic_channels[j];
- if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
- return 1;
- }
- return 0;
-}
-
-/*
- * Start an ap scan by populating the channel list.
- */
-static int
-ap_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- struct ieee80211_channel *c;
- int i;
-
- ss->ss_last = 0;
- if (ic->ic_des_mode == IEEE80211_MODE_AUTO) {
- for (i = 0; i < ic->ic_nchans; i++) {
- c = &ic->ic_channels[i];
- if (IEEE80211_IS_CHAN_TURBO(c)) {
-#ifdef IEEE80211_F_XR
- /* XR is not supported on turbo channels */
- if (ic->ic_flags & IEEE80211_F_XR)
- continue;
-#endif
- /* dynamic channels are scanned in base mode */
- if (!IEEE80211_IS_CHAN_ST(c))
- continue;
- } else if (IEEE80211_IS_CHAN_HT(c)) {
- /* HT channels are scanned in legacy */
- continue;
- } else {
- /*
- * Use any 11g channel instead of 11b one.
- */
- if (IEEE80211_IS_CHAN_B(c) &&
- find11gchannel(ic, i, c->ic_freq))
- continue;
- }
- if (ss->ss_last >= IEEE80211_SCAN_MAX)
- break;
- ss->ss_chans[ss->ss_last++] = c;
- }
- } else {
- static const u_int chanflags[IEEE80211_MODE_MAX] = {
- 0, /* IEEE80211_MODE_AUTO */
- IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */
- IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
- IEEE80211_CHAN_G, /* IEEE80211_MODE_11G */
- IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
- IEEE80211_CHAN_108A, /* IEEE80211_MODE_TURBO_A */
- IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */
- IEEE80211_CHAN_ST, /* IEEE80211_MODE_STURBO_A */
- IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA */
- IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG */
- };
- u_int modeflags;
-
- modeflags = chanflags[ic->ic_des_mode];
- if ((ic->ic_flags & IEEE80211_F_TURBOP) &&
- modeflags != IEEE80211_CHAN_ST) {
- if (ic->ic_des_mode == IEEE80211_MODE_11G)
- modeflags = IEEE80211_CHAN_108G;
- else
- modeflags = IEEE80211_CHAN_108A;
- }
- for (i = 0; i < ic->ic_nchans; i++) {
- c = &ic->ic_channels[i];
- if ((c->ic_flags & modeflags) != modeflags)
- continue;
-#ifdef IEEE80211_F_XR
- /* XR is not supported on turbo channels */
- if (IEEE80211_IS_CHAN_TURBO(c) &&
- (ic->ic_flags & IEEE80211_F_XR))
- continue;
-#endif
- if (ss->ss_last >= IEEE80211_SCAN_MAX)
- break;
- /*
- * Do not select static turbo channels if
- * the mode is not static turbo.
- */
- if (IEEE80211_IS_CHAN_STURBO(c) &&
- ic->ic_des_mode != IEEE80211_MODE_STURBO_A)
- continue;
- ss->ss_chans[ss->ss_last++] = c;
- }
- }
- ss->ss_next = 0;
- /* XXX tunables */
- ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
- ss->ss_maxdwell = msecs_to_ticks(300); /* 300ms */
-
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic)) {
- if_printf(ic->ic_ifp, "scan set ");
- ieee80211_scan_dump_channels(ss);
- printf(" dwell min %ld max %ld\n",
- ss->ss_mindwell, ss->ss_maxdwell);
- }
-#endif /* IEEE80211_DEBUG */
-
- return 0;
-}
-
-/*
- * Restart a bg scan.
- */
-static int
-ap_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- return 0;
-}
-
-/*
- * Cancel an ongoing scan.
- */
-static int
-ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- return 0;
-}
-
-/*
- * Record max rssi on channel.
- */
-static int
-ap_add(struct ieee80211_scan_state *ss,
- const struct ieee80211_scanparams *sp,
- const struct ieee80211_frame *wh,
- int subtype, int rssi, int noise, int rstamp)
-{
- struct ap_state *as = ss->ss_priv;
- struct ieee80211com *ic = ss->ss_ic;
- int chan;
-
- chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
- /* XXX better quantification of channel use? */
- /* XXX count bss's? */
- if (rssi > as->as_maxrssi[chan])
- as->as_maxrssi[chan] = rssi;
- /* XXX interference, turbo requirements */
- return 1;
-}
-
-/*
- * Pick a quiet channel to use for ap operation.
- */
-static int
-ap_end(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
-{
- struct ap_state *as = ss->ss_priv;
- int i, chan, bestchan, bestchanix;
-
- KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP,
- ("wrong opmode %u", ic->ic_opmode));
- /* XXX select channel more intelligently, e.g. channel spread, power */
- bestchan = -1;
- bestchanix = 0; /* NB: silence compiler */
- /* NB: use scan list order to preserve channel preference */
- for (i = 0; i < ss->ss_last; i++) {
- /*
- * If the channel is unoccupied the max rssi
- * should be zero; just take it. Otherwise
- * track the channel with the lowest rssi and
- * use that when all channels appear occupied.
- */
- /* XXX channel have interference? */
- chan = ieee80211_chan2ieee(ic, ss->ss_chans[i]);
-
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: channel %u rssi %d bestchan %d bestchan rssi %d\n",
- __func__, chan, as->as_maxrssi[chan],
- bestchan, bestchan != -1 ? as->as_maxrssi[bestchan] : 0);
-
- if (as->as_maxrssi[chan] == 0) {
- bestchan = chan;
- bestchanix = i;
- /* XXX use other considerations */
- break;
- }
- if (bestchan == -1 ||
- as->as_maxrssi[chan] < as->as_maxrssi[bestchan])
- bestchan = chan;
- }
- if (bestchan == -1) {
- /* no suitable channel, should not happen */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
- "%s: no suitable channel! (should not happen)\n", __func__);
- /* XXX print something? */
- return 0; /* restart scan */
- } else {
- struct ieee80211_channel *c;
-
- /* XXX notify all vap's? */
- /*
- * If this is a dynamic turbo frequency,
- * start with normal mode first.
- */
- c = ss->ss_chans[bestchanix];
- if (IEEE80211_IS_CHAN_TURBO(c) &&
- !IEEE80211_IS_CHAN_STURBO(c)) {
- c = ieee80211_find_channel(ic, c->ic_freq,
- c->ic_flags & ~IEEE80211_CHAN_TURBO);
- if (c == NULL) {
- /* should never happen ?? */
- return 0;
- }
- }
- ieee80211_create_ibss(ic,
- ieee80211_ht_adjust_channel(ic, c, ic->ic_flags_ext));
- return 1;
- }
-}
-
-static void
-ap_age(struct ieee80211_scan_state *ss)
-{
- /* XXX is there anything meaningful to do? */
-}
-
-static void
-ap_iterate(struct ieee80211_scan_state *ss,
- ieee80211_scan_iter_func *f, void *arg)
-{
- /* NB: nothing meaningful we can do */
-}
-
-static void
-ap_assoc_success(struct ieee80211_scan_state *ss,
- const uint8_t macaddr[IEEE80211_ADDR_LEN])
-{
- /* should not be called */
-}
-
-static void
-ap_assoc_fail(struct ieee80211_scan_state *ss,
- const uint8_t macaddr[IEEE80211_ADDR_LEN], int reason)
-{
- /* should not be called */
-}
-
-static const struct ieee80211_scanner ap_default = {
- .scan_name = "default",
- .scan_attach = ap_attach,
- .scan_detach = ap_detach,
- .scan_start = ap_start,
- .scan_restart = ap_restart,
- .scan_cancel = ap_cancel,
- .scan_end = ap_end,
- .scan_flush = ap_flush,
- .scan_add = ap_add,
- .scan_age = ap_age,
- .scan_iterate = ap_iterate,
- .scan_assoc_success = ap_assoc_success,
- .scan_assoc_fail = ap_assoc_fail,
-};
-
-/*
- * Module glue.
- */
-static int
-wlan_modevent(module_t mod, int type, void *unused)
-{
- switch (type) {
- case MOD_LOAD:
- ieee80211_scanner_register(IEEE80211_M_HOSTAP, &ap_default);
- return 0;
- case MOD_UNLOAD:
- case MOD_QUIESCE:
- if (nrefs) {
- printf("wlan_scan_ap: still in use (%u dynamic refs)\n",
- nrefs);
- return EBUSY;
- }
- if (type == MOD_UNLOAD)
- ieee80211_scanner_unregister_all(&ap_default);
- return 0;
- }
- return EINVAL;
-}
-
-static moduledata_t wlan_mod = {
- "wlan_scan_ap",
- wlan_modevent,
- 0
-};
-DECLARE_MODULE(wlan_scan_ap, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_scan_ap, 1);
-MODULE_DEPEND(wlan_scan_ap, wlan, 1, 1, 1);
diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c
index d152605..d1dc060 100644
--- a/sys/net80211/ieee80211_scan_sta.c
+++ b/sys/net80211/ieee80211_scan_sta.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$");
/*
* IEEE 802.11 station scanning support.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -41,6 +43,8 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_regdomain.h>
#include <net/bpf.h>
@@ -61,20 +65,6 @@ __FBSDID("$FreeBSD$");
#define STA_RSSI_MIN 8 /* min acceptable rssi */
#define STA_RSSI_MAX 40 /* max rssi for comparison */
-#define RSSI_LPF_LEN 10
-#define RSSI_DUMMY_MARKER 0x127
-#define RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */
-#define RSSI_IN(x) ((x) * RSSI_EP_MULTIPLIER)
-#define LPF_RSSI(x, y, len) \
- ((x != RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
-#define RSSI_LPF(x, y) do { \
- if ((y) >= -20) \
- x = LPF_RSSI((x), RSSI_IN((y)), RSSI_LPF_LEN); \
-} while (0)
-#define EP_RND(x, mul) \
- ((((x)%(mul)) >= ((mul)/2)) ? howmany(x, mul) : (x)/(mul))
-#define RSSI_GET(x) EP_RND(x, RSSI_EP_MULTIPLIER)
-
struct sta_entry {
struct ieee80211_scan_entry base;
TAILQ_ENTRY(sta_entry) se_list;
@@ -83,11 +73,14 @@ struct sta_entry {
uint8_t se_seen; /* seen during current scan */
uint8_t se_notseen; /* not seen in previous scans */
uint8_t se_flags;
+#define STA_SSID_MATCH 0x01
+#define STA_BSSID_MATCH 0x02
uint32_t se_avgrssi; /* LPF rssi state */
unsigned long se_lastupdate; /* time of last update */
unsigned long se_lastfail; /* time of last failure */
unsigned long se_lastassoc; /* time of last association */
u_int se_scangen; /* iterator scan gen# */
+ u_int se_countrygen; /* gen# of last cc notify */
};
#define STA_HASHSIZE 32
@@ -99,9 +92,12 @@ struct sta_table {
struct mtx 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_scangen */
- u_int st_scangen; /* gen# for iterator */
+ struct mtx st_scanlock; /* on st_scaniter */
+ u_int st_scaniter; /* gen# for iterator */
+ u_int st_scangen; /* scan generation # */
int st_newscan;
+ /* ap-related state */
+ int st_maxrssi[IEEE80211_CHAN_MAX];
};
static void sta_flush_table(struct sta_table *);
@@ -120,8 +116,16 @@ static void sta_flush_table(struct sta_table *);
#define MATCH_FAILS 0x040 /* too many failed auth attempts */
#define MATCH_NOTSEEN 0x080 /* not seen in recent scans */
#define MATCH_RSSI 0x100 /* rssi deemed too low to use */
-static int match_bss(struct ieee80211com *,
+#define MATCH_CC 0x200 /* country code mismatch */
+static int match_bss(struct ieee80211vap *,
const struct ieee80211_scan_state *, struct sta_entry *, int);
+static void adhoc_age(struct ieee80211_scan_state *);
+
+static __inline int
+isocmp(const uint8_t cc1[], const uint8_t cc2[])
+{
+ return (cc1[0] == cc2[0] && cc1[1] == cc2[1]);
+}
/* number of references from net80211 layer */
static int nrefs = 0;
@@ -191,18 +195,10 @@ sta_flush_table(struct sta_table *st)
TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
TAILQ_REMOVE(&st->st_entry, se, se_list);
LIST_REMOVE(se, se_hash);
+ ieee80211_ies_cleanup(&se->base.se_ies);
FREE(se, M_80211_SCAN);
}
-}
-
-static void
-saveie(uint8_t **iep, const uint8_t *ie)
-{
-
- if (ie == NULL)
- *iep = NULL;
- else
- ieee80211_saveie(iep, ie);
+ memset(st->st_maxrssi, 0, sizeof(st->st_maxrssi));
}
/*
@@ -221,10 +217,11 @@ sta_add(struct ieee80211_scan_state *ss,
IEEE80211_SCAN_PICK1ST)
struct sta_table *st = ss->ss_priv;
const uint8_t *macaddr = wh->i_addr2;
- struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = vap->iv_ic;
struct sta_entry *se;
struct ieee80211_scan_entry *ise;
- int hash, offchan;
+ int hash;
hash = STA_HASH(macaddr);
@@ -238,8 +235,8 @@ sta_add(struct ieee80211_scan_state *ss,
mtx_unlock(&st->st_lock);
return 0;
}
- se->se_scangen = st->st_scangen-1;
- se->se_avgrssi = RSSI_DUMMY_MARKER;
+ se->se_scangen = st->st_scaniter-1;
+ se->se_avgrssi = IEEE80211_RSSI_DUMMY_MARKER;
IEEE80211_ADDR_COPY(se->base.se_macaddr, macaddr);
TAILQ_INSERT_TAIL(&st->st_entry, se, se_list);
LIST_INSERT_HEAD(&st->st_hash[hash], se, se_hash);
@@ -254,23 +251,21 @@ found:
memcpy(ise->se_rates, sp->rates, 2+sp->rates[1]);
if (sp->xrates != NULL) {
/* XXX validate xrates[1] */
- KASSERT(sp->xrates[1] + sp->rates[1] <= IEEE80211_RATE_MAXSIZE,
+ KASSERT(sp->xrates[1] <= IEEE80211_RATE_MAXSIZE,
("xrate set too large: %u", sp->xrates[1]));
memcpy(ise->se_xrates, sp->xrates, 2+sp->xrates[1]);
} else
ise->se_xrates[1] = 0;
IEEE80211_ADDR_COPY(ise->se_bssid, wh->i_addr3);
- offchan = (IEEE80211_CHAN2IEEE(sp->curchan) != sp->bchan &&
- ic->ic_phytype != IEEE80211_T_FH);
- if (!offchan) {
+ if ((sp->status & IEEE80211_BPARSE_OFFCHAN) == 0) {
/*
* Record rssi data using extended precision LPF filter.
*
* NB: use only on-channel data to insure we get a good
* estimate of the signal we'll see when associated.
*/
- RSSI_LPF(se->se_avgrssi, rssi);
- ise->se_rssi = RSSI_GET(se->se_avgrssi);
+ IEEE80211_RSSI_LPF(se->se_avgrssi, rssi);
+ ise->se_rssi = IEEE80211_RSSI_GET(se->se_avgrssi);
ise->se_noise = noise;
}
ise->se_rstamp = rstamp;
@@ -280,24 +275,26 @@ found:
/*
* Beware of overriding se_chan for frames seen
* off-channel; this can cause us to attempt an
- * assocation on the wrong channel.
+ * association on the wrong channel.
*/
- if (offchan) {
+ if (sp->status & IEEE80211_BPARSE_OFFCHAN) {
struct ieee80211_channel *c;
/*
* Off-channel, locate the home/bss channel for the sta
- * using the value broadcast in the DSPARMS ie.
+ * using the value broadcast in the DSPARMS ie. We know
+ * sp->chan has this value because it's used to calculate
+ * IEEE80211_BPARSE_OFFCHAN.
*/
- c = ieee80211_find_channel_byieee(ic, sp->bchan,
- sp->curchan->ic_flags);
+ c = ieee80211_find_channel_byieee(ic, sp->chan,
+ ic->ic_curchan->ic_flags);
if (c != NULL) {
ise->se_chan = c;
} else if (ise->se_chan == NULL) {
/* should not happen, pick something */
- ise->se_chan = sp->curchan;
+ ise->se_chan = ic->ic_curchan;
}
} else
- ise->se_chan = sp->curchan;
+ ise->se_chan = ic->ic_curchan;
ise->se_fhdwell = sp->fhdwell;
ise->se_fhindex = sp->fhindex;
ise->se_erp = sp->erp;
@@ -307,17 +304,39 @@ found:
(const struct ieee80211_tim_ie *) sp->tim;
ise->se_dtimperiod = tim->tim_period;
}
- saveie(&ise->se_wme_ie, sp->wme);
- saveie(&ise->se_wpa_ie, sp->wpa);
- saveie(&ise->se_rsn_ie, sp->rsn);
- saveie(&ise->se_ath_ie, sp->ath);
- saveie(&ise->se_htcap_ie, sp->htcap);
- saveie(&ise->se_htinfo_ie, sp->htinfo);
+ if (sp->country != NULL) {
+ const struct ieee80211_country_ie *cie =
+ (const struct ieee80211_country_ie *) sp->country;
+ /*
+ * If 11d is enabled and we're attempting to join a bss
+ * that advertises it's country code then compare our
+ * current settings to what we fetched from the country ie.
+ * If our country code is unspecified or different then
+ * dispatch an event to user space that identifies the
+ * country code so our regdomain config can be changed.
+ */
+ /* XXX only for STA mode? */
+ if ((IEEE80211_IS_CHAN_11D(ise->se_chan) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) &&
+ (ic->ic_regdomain.country == CTRY_DEFAULT ||
+ !isocmp(cie->cc, ic->ic_regdomain.isocc))) {
+ /* only issue one notify event per scan */
+ if (se->se_countrygen != st->st_scangen) {
+ ieee80211_notify_country(vap, ise->se_bssid,
+ cie->cc);
+ se->se_countrygen = st->st_scangen;
+ }
+ }
+ ise->se_cc[0] = cie->cc[0];
+ ise->se_cc[1] = cie->cc[1];
+ }
+ /* NB: no need to setup ie ptrs; they are not (currently) used */
+ (void) ieee80211_ies_init(&ise->se_ies, sp->ies, sp->ies_len);
/* clear failure count after STA_FAIL_AGE passes */
if (se->se_fails && (ticks - se->se_lastfail) > STA_FAILS_AGE*hz) {
se->se_fails = 0;
- IEEE80211_NOTE_MAC(ic, IEEE80211_MSG_SCAN, macaddr,
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, macaddr,
"%s: fails %u", __func__, se->se_fails);
}
@@ -325,13 +344,16 @@ found:
se->se_seen = 1;
se->se_notseen = 0;
+ if (rssi > st->st_maxrssi[sp->bchan])
+ st->st_maxrssi[sp->bchan] = rssi;
+
mtx_unlock(&st->st_lock);
/*
* If looking for a quick choice and nothing's
* been found check here.
*/
- if (PICK1ST(ss) && match_bss(ic, ss, se, IEEE80211_MSG_SCAN) == 0)
+ if (PICK1ST(ss) && match_bss(vap, ss, se, IEEE80211_MSG_SCAN) == 0)
ss->ss_flags |= IEEE80211_SCAN_GOTPICK;
return 1;
@@ -343,11 +365,11 @@ found:
* Check if a channel is excluded by user request.
*/
static int
-isexcluded(struct ieee80211com *ic, const struct ieee80211_channel *c)
+isexcluded(struct ieee80211vap *vap, const struct ieee80211_channel *c)
{
- return (isclr(ic->ic_chan_active, c->ic_ieee) ||
- (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
- c->ic_freq != ic->ic_des_chan->ic_freq));
+ return (isclr(vap->iv_ic->ic_chan_active, c->ic_ieee) ||
+ (vap->iv_des_chan != IEEE80211_CHAN_ANYC &&
+ c->ic_freq != vap->iv_des_chan->ic_freq));
}
static struct ieee80211_channel *
@@ -387,11 +409,12 @@ static const u_int chanflags[IEEE80211_MODE_MAX] = {
};
static void
-add_channels(struct ieee80211com *ic,
+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;
@@ -403,89 +426,28 @@ add_channels(struct ieee80211com *ic,
break;
c = ieee80211_find_channel(ic, freq[i], modeflags);
- if (c != NULL && isexcluded(ic, c))
+ if (c == NULL || isexcluded(vap, c))
continue;
if (mode == IEEE80211_MODE_AUTO) {
/*
* XXX special-case 11b/g channels so we select
- * the g channel if both are present or there
- * are only g channels.
+ * the g channel if both are present.
*/
- if (c == NULL || IEEE80211_IS_CHAN_B(c)) {
- cg = find11gchannel(ic, i, freq[i]);
- if (cg != NULL)
- c = cg;
- }
+ if (IEEE80211_IS_CHAN_B(c) &&
+ (cg = find11gchannel(ic, i, c->ic_freq)) != NULL)
+ c = cg;
}
- if (c == NULL)
- continue;
-
ss->ss_chans[ss->ss_last++] = c;
}
#undef N
}
-static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */
-{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
-static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */
-{ 5170, 5190, 5210, 5230 };
-static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */
-{ 2412, 2437, 2462, 2442, 2472 };
-static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */
-{ 5745, 5765, 5785, 5805, 5825 };
-static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */
-{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
-static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */
-{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
-static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */
-{ 2484 };
-static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */
-{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
-static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */
-{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
-#ifdef ATH_TURBO_SCAN
-static const uint16_t rcl5[] = /* 3 static turbo channels */
-{ 5210, 5250, 5290 };
-static const uint16_t rcl6[] = /* 2 static turbo channels */
-{ 5760, 5800 };
-static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */
-{ 5540, 5580, 5620, 5660 };
-static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */
-{ 2437 };
-static const uint16_t rcl13[] = /* dynamic Turbo channels */
-{ 5200, 5240, 5280, 5765, 5805 };
-#endif /* ATH_TURBO_SCAN */
-
struct scanlist {
uint16_t mode;
uint16_t count;
const uint16_t *list;
};
-#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a
-
-static const struct scanlist staScanTable[] = {
- { IEEE80211_MODE_11B, X(rcl3) },
- { IEEE80211_MODE_11A, X(rcl1) },
- { IEEE80211_MODE_11A, X(rcl2) },
- { IEEE80211_MODE_11B, X(rcl8) },
- { IEEE80211_MODE_11B, X(rcl9) },
- { IEEE80211_MODE_11A, X(rcl4) },
-#ifdef ATH_TURBO_SCAN
- { IEEE80211_MODE_STURBO_A, X(rcl5) },
- { IEEE80211_MODE_STURBO_A, X(rcl6) },
- { IEEE80211_MODE_TURBO_A, X(rcl6x) },
- { IEEE80211_MODE_TURBO_A, X(rcl13) },
-#endif /* ATH_TURBO_SCAN */
- { IEEE80211_MODE_11A, X(rcl7) },
- { IEEE80211_MODE_11B, X(rcl10) },
- { IEEE80211_MODE_11A, X(rcl11) },
-#ifdef ATH_TURBO_SCAN
- { IEEE80211_MODE_TURBO_G, X(rcl12) },
-#endif /* ATH_TURBO_SCAN */
- { .list = NULL }
-};
-
static int
checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
{
@@ -500,16 +462,13 @@ checktable(const struct scanlist *scan, const struct ieee80211_channel *c)
}
static void
-sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic,
+sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
const struct scanlist table[])
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_channel *c;
int i;
- /*
- * Add the channels from the ic (from HAL) that are not present
- * in the staScanTable.
- */
for (i = 0; i < ic->ic_nchans; i++) {
if (ss->ss_last >= IEEE80211_SCAN_MAX)
break;
@@ -528,14 +487,14 @@ sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic,
* If a desired mode was specified, scan only
* channels that satisfy that constraint.
*/
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO &&
- ic->ic_des_mode != ieee80211_chan2mode(c))
+ if (vap->iv_des_mode != IEEE80211_MODE_AUTO &&
+ vap->iv_des_mode != ieee80211_chan2mode(c))
continue;
/*
* Skip channels excluded by user request.
*/
- if (isexcluded(ic, c))
+ if (isexcluded(vap, c))
continue;
/*
@@ -552,14 +511,10 @@ sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211com *ic,
}
}
-/*
- * Start a station-mode scan by populating the channel list.
- */
-static int
-sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+static void
+makescanlist(struct ieee80211_scan_state *ss, struct ieee80211vap *vap,
+ const struct scanlist table[])
{
-#define N(a) (sizeof(a)/sizeof(a[0]))
- struct sta_table *st = ss->ss_priv;
const struct scanlist *scan;
enum ieee80211_phymode mode;
@@ -569,20 +524,20 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* of channels for scanning. Any channels in the ordered
* list not in the master list will be discarded.
*/
- for (scan = staScanTable; scan->list != NULL; scan++) {
+ for (scan = table; scan->list != NULL; scan++) {
mode = scan->mode;
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO) {
+ if (vap->iv_des_mode != IEEE80211_MODE_AUTO) {
/*
* If a desired mode was specified, scan only
* channels that satisfy that constraint.
*/
- if (ic->ic_des_mode != mode) {
+ if (vap->iv_des_mode != mode) {
/*
* The scan table marks 2.4Ghz channels as b
* so if the desired mode is 11g, then use
* the 11b channel list but upgrade the mode.
*/
- if (ic->ic_des_mode != IEEE80211_MODE_11G ||
+ if (vap->iv_des_mode != IEEE80211_MODE_11G ||
mode != IEEE80211_MODE_11B)
continue;
mode = IEEE80211_MODE_11G; /* upgrade */
@@ -597,7 +552,7 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
}
#ifdef IEEE80211_F_XR
/* XR does not operate on turbo channels */
- if ((ic->ic_flags & IEEE80211_F_XR) &&
+ if ((vap->iv_flags & IEEE80211_F_XR) &&
(mode == IEEE80211_MODE_TURBO_A ||
mode == IEEE80211_MODE_TURBO_G ||
mode == IEEE80211_MODE_STURBO_A))
@@ -607,40 +562,98 @@ sta_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* Add the list of the channels; any that are not
* in the master channel list will be discarded.
*/
- add_channels(ic, ss, mode, scan->list, scan->count);
+ add_channels(vap, ss, mode, scan->list, scan->count);
}
/*
- * Add the channels from the ic (from HAL) that are not present
- * in the staScanTable.
+ * Add the channels from the ic that are not present
+ * in the table.
*/
- sweepchannels(ss, ic, staScanTable);
+ sweepchannels(ss, vap, table);
+}
- ss->ss_next = 0;
- /* XXX tunables */
- ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */
- ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+static const uint16_t rcl1[] = /* 8 FCC channel: 52, 56, 60, 64, 36, 40, 44, 48 */
+{ 5260, 5280, 5300, 5320, 5180, 5200, 5220, 5240 };
+static const uint16_t rcl2[] = /* 4 MKK channels: 34, 38, 42, 46 */
+{ 5170, 5190, 5210, 5230 };
+static const uint16_t rcl3[] = /* 2.4Ghz ch: 1,6,11,7,13 */
+{ 2412, 2437, 2462, 2442, 2472 };
+static const uint16_t rcl4[] = /* 5 FCC channel: 149, 153, 161, 165 */
+{ 5745, 5765, 5785, 5805, 5825 };
+static const uint16_t rcl7[] = /* 11 ETSI channel: 100,104,108,112,116,120,124,128,132,136,140 */
+{ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700 };
+static const uint16_t rcl8[] = /* 2.4Ghz ch: 2,3,4,5,8,9,10,12 */
+{ 2417, 2422, 2427, 2432, 2447, 2452, 2457, 2467 };
+static const uint16_t rcl9[] = /* 2.4Ghz ch: 14 */
+{ 2484 };
+static const uint16_t rcl10[] = /* Added Korean channels 2312-2372 */
+{ 2312, 2317, 2322, 2327, 2332, 2337, 2342, 2347, 2352, 2357, 2362, 2367, 2372 };
+static const uint16_t rcl11[] = /* Added Japan channels in 4.9/5.0 spectrum */
+{ 5040, 5060, 5080, 4920, 4940, 4960, 4980 };
+#ifdef ATH_TURBO_SCAN
+static const uint16_t rcl5[] = /* 3 static turbo channels */
+{ 5210, 5250, 5290 };
+static const uint16_t rcl6[] = /* 2 static turbo channels */
+{ 5760, 5800 };
+static const uint16_t rcl6x[] = /* 4 FCC3 turbo channels */
+{ 5540, 5580, 5620, 5660 };
+static const uint16_t rcl12[] = /* 2.4Ghz Turbo channel 6 */
+{ 2437 };
+static const uint16_t rcl13[] = /* dynamic Turbo channels */
+{ 5200, 5240, 5280, 5765, 5805 };
+#endif /* ATH_TURBO_SCAN */
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic)) {
- if_printf(ic->ic_ifp, "scan set ");
- ieee80211_scan_dump_channels(ss);
- printf(" dwell min %ld max %ld\n",
- ss->ss_mindwell, ss->ss_maxdwell);
- }
-#endif /* IEEE80211_DEBUG */
+#define X(a) .count = sizeof(a)/sizeof(a[0]), .list = a
+static const struct scanlist staScanTable[] = {
+ { IEEE80211_MODE_11B, X(rcl3) },
+ { IEEE80211_MODE_11A, X(rcl1) },
+ { IEEE80211_MODE_11A, X(rcl2) },
+ { IEEE80211_MODE_11B, X(rcl8) },
+ { IEEE80211_MODE_11B, X(rcl9) },
+ { IEEE80211_MODE_11A, X(rcl4) },
+#ifdef ATH_TURBO_SCAN
+ { IEEE80211_MODE_STURBO_A, X(rcl5) },
+ { IEEE80211_MODE_STURBO_A, X(rcl6) },
+ { IEEE80211_MODE_TURBO_A, X(rcl6x) },
+ { IEEE80211_MODE_TURBO_A, X(rcl13) },
+#endif /* ATH_TURBO_SCAN */
+ { IEEE80211_MODE_11A, X(rcl7) },
+ { IEEE80211_MODE_11B, X(rcl10) },
+ { IEEE80211_MODE_11A, X(rcl11) },
+#ifdef ATH_TURBO_SCAN
+ { IEEE80211_MODE_TURBO_G, X(rcl12) },
+#endif /* ATH_TURBO_SCAN */
+ { .list = NULL }
+};
+
+/*
+ * Start a station-mode scan by populating the channel list.
+ */
+static int
+sta_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+{
+ struct sta_table *st = ss->ss_priv;
+
+ makescanlist(ss, vap, staScanTable);
+
+ if (ss->ss_mindwell == 0)
+ ss->ss_mindwell = msecs_to_ticks(20); /* 20ms */
+ if (ss->ss_maxdwell == 0)
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+
+ st->st_scangen++;
st->st_newscan = 1;
return 0;
-#undef N
}
/*
- * Restart a bg scan.
+ * Restart a scan, typically a bg scan but can
+ * also be a fg scan that came up empty.
*/
static int
-sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_restart(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
@@ -652,18 +665,44 @@ sta_restart(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* Cancel an ongoing scan.
*/
static int
-sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
return 0;
}
-static uint8_t
+/* unalligned little endian access */
+#define LE_READ_2(p) \
+ ((uint16_t) \
+ ((((const uint8_t *)(p))[0] ) | \
+ (((const uint8_t *)(p))[1] << 8)))
+
+static int
maxrate(const struct ieee80211_scan_entry *se)
{
- uint8_t rmax, r;
- int i;
+ const struct ieee80211_ie_htcap *htcap =
+ (const struct ieee80211_ie_htcap *) se->se_ies.htcap_ie;
+ int rmax, r, i;
+ uint16_t caps;
rmax = 0;
+ if (htcap != NULL) {
+ /*
+ * HT station; inspect supported MCS and then adjust
+ * rate by channel width. Could also include short GI
+ * in this if we want to be extra accurate.
+ */
+ /* XXX assumes MCS15 is max */
+ for (i = 15; i >= 0 && isclr(htcap->hc_mcsset, i); i--)
+ ;
+ if (i >= 0) {
+ caps = LE_READ_2(&htcap->hc_cap);
+ /* XXX short/long GI */
+ if (caps & IEEE80211_HTCAP_CHWIDTH40)
+ rmax = ieee80211_htrates[i].ht40_rate_400ns;
+ else
+ rmax = ieee80211_htrates[i].ht40_rate_800ns;
+ }
+ }
for (i = 0; i < se->se_rates[1]; i++) {
r = se->se_rates[2+i] & IEEE80211_RATE_VAL;
if (r > rmax)
@@ -691,7 +730,7 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b)
if (((_a) ^ (_b)) & (_what)) \
return ((_a) & (_what)) ? 1 : -1; \
} while (0)
- uint8_t maxa, maxb;
+ int maxa, maxb;
int8_t rssia, rssib;
int weight;
@@ -722,12 +761,8 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b)
return maxa - maxb;
/* XXX use freq for channel preference */
/* for now just prefer 5Ghz band to all other bands */
- if (IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) &&
- !IEEE80211_IS_CHAN_5GHZ(b->base.se_chan))
- return 1;
- if (!IEEE80211_IS_CHAN_5GHZ(a->base.se_chan) &&
- IEEE80211_IS_CHAN_5GHZ(b->base.se_chan))
- return -1;
+ PREFER(IEEE80211_IS_CHAN_5GHZ(a->base.se_chan),
+ IEEE80211_IS_CHAN_5GHZ(b->base.se_chan), 1);
}
/* all things being equal, use signal level */
return a->base.se_rssi - b->base.se_rssi;
@@ -736,20 +771,23 @@ sta_compare(const struct sta_entry *a, const struct sta_entry *b)
/*
* Check rate set suitability and return the best supported rate.
+ * XXX inspect MCS for HT
*/
static int
-check_rate(struct ieee80211com *ic, const struct ieee80211_scan_entry *se)
+check_rate(struct ieee80211vap *vap, 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;
+ int i, j, nrs, r, okrate, badrate, fixedrate, ucastrate;
const uint8_t *rs;
- okrate = badrate = fixedrate = 0;
+ okrate = badrate = 0;
- srs = ieee80211_get_suprates(ic, se->se_chan);
+ srs = ieee80211_get_suprates(vap->iv_ic, se->se_chan);
nrs = se->se_rates[1];
rs = se->se_rates+2;
+ /* XXX MCS */
+ ucastrate = vap->iv_txparms[ieee80211_chan2mode(se->se_chan)].ucastrate;
fixedrate = IEEE80211_FIXED_RATE_NONE;
again:
for (i = 0; i < nrs; i++) {
@@ -758,7 +796,7 @@ again:
/*
* Check any fixed rate is included.
*/
- if (r == ic->ic_fixed_rate)
+ if (r == ucastrate)
fixedrate = r;
/*
* Check against our supported rates.
@@ -787,7 +825,7 @@ again:
}
back:
- if (okrate == 0 || ic->ic_fixed_rate != fixedrate)
+ if (okrate == 0 || ucastrate != fixedrate)
return badrate | IEEE80211_RATE_BASIC;
else
return RV(okrate);
@@ -812,13 +850,14 @@ match_ssid(const uint8_t *ie,
* Test a scan candidate for suitability/compatibility.
*/
static int
-match_bss(struct ieee80211com *ic,
+match_bss(struct ieee80211vap *vap,
const struct ieee80211_scan_state *ss, struct sta_entry *se0,
int debug)
{
+ struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_entry *se = &se0->base;
- uint8_t rate;
- int fail;
+ uint8_t rate;
+ int fail;
fail = 0;
if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, se->se_chan)))
@@ -830,18 +869,33 @@ match_bss(struct ieee80211com *ic,
* list so we check the desired mode here to weed them
* out.
*/
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO &&
+ if (vap->iv_des_mode != IEEE80211_MODE_AUTO &&
(se->se_chan->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
- chanflags[ic->ic_des_mode])
+ chanflags[vap->iv_des_mode])
fail |= MATCH_CHANNEL;
- if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (vap->iv_opmode == IEEE80211_M_IBSS) {
if ((se->se_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
fail |= MATCH_CAPINFO;
} else {
if ((se->se_capinfo & IEEE80211_CAPINFO_ESS) == 0)
fail |= MATCH_CAPINFO;
+ /*
+ * If 11d is enabled and we're attempting to join a bss
+ * that advertises it's country code then compare our
+ * current settings to what we fetched from the country ie.
+ * If our country code is unspecified or different then do
+ * not attempt to join the bss. We should have already
+ * dispatched an event to user space that identifies the
+ * new country code so our regdomain config should match.
+ */
+ if ((IEEE80211_IS_CHAN_11D(se->se_chan) ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_DOTD)) &&
+ se->se_cc[0] != 0 &&
+ (ic->ic_regdomain.country == CTRY_DEFAULT ||
+ !isocmp(se->se_cc, ic->ic_regdomain.isocc)))
+ fail |= MATCH_CC;
}
- if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ if (vap->iv_flags & IEEE80211_F_PRIVACY) {
if ((se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
fail |= MATCH_PRIVACY;
} else {
@@ -849,27 +903,27 @@ match_bss(struct ieee80211com *ic,
if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY)
fail |= MATCH_PRIVACY;
}
- rate = check_rate(ic, se);
+ rate = check_rate(vap, se);
if (rate & IEEE80211_RATE_BASIC)
fail |= MATCH_RATE;
if (ss->ss_nssid != 0 &&
!match_ssid(se->se_ssid, ss->ss_nssid, ss->ss_ssid))
fail |= MATCH_SSID;
- if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
- !IEEE80211_ADDR_EQ(ic->ic_des_bssid, se->se_bssid))
- fail |= MATCH_BSSID;
+ if ((vap->iv_flags & IEEE80211_F_DESBSSID) &&
+ !IEEE80211_ADDR_EQ(vap->iv_des_bssid, se->se_bssid))
+ fail |= MATCH_BSSID;
if (se0->se_fails >= STA_FAILS_MAX)
fail |= MATCH_FAILS;
- /* NB: entries may be present awaiting purge, skip */
if (se0->se_notseen >= STA_PURGE_SCANS)
fail |= MATCH_NOTSEEN;
if (se->se_rssi < STA_RSSI_MIN)
fail |= MATCH_RSSI;
#ifdef IEEE80211_DEBUG
- if (ieee80211_msg(ic, debug)) {
+ if (ieee80211_msg(vap, debug)) {
printf(" %c %s",
fail & MATCH_FAILS ? '=' :
fail & MATCH_NOTSEEN ? '^' :
+ fail & MATCH_CC ? '$' :
fail ? '-' : '+', ether_sprintf(se->se_macaddr));
printf(" %s%c", ether_sprintf(se->se_bssid),
fail & MATCH_BSSID ? '!' : ' ');
@@ -929,16 +983,17 @@ sta_dec_fails(struct sta_table *st)
}
static struct sta_entry *
-select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug)
+select_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, int debug)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se, *selbs = NULL;
- IEEE80211_DPRINTF(ic, debug, " %s\n",
+ IEEE80211_DPRINTF(vap, debug, " %s\n",
"macaddr bssid chan rssi rate flag wep essid");
mtx_lock(&st->st_lock);
TAILQ_FOREACH(se, &st->st_entry, se_list) {
- if (match_bss(ic, ss, se, debug) == 0) {
+ if (match_bss(vap, ss, se, debug) == 0) {
+ ieee80211_ies_expand(&se->base.se_ies);
if (selbs == NULL)
selbs = se;
else if (sta_compare(se, selbs) > 0)
@@ -955,13 +1010,13 @@ select_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic, int debug)
* to use to start an ibss network.
*/
static int
-sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *selbs;
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("wrong mode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", vap->iv_opmode));
if (st->st_newscan) {
sta_update_notseen(st);
@@ -982,8 +1037,10 @@ sta_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
*/
/* NB: unlocked read should be ok */
if (TAILQ_FIRST(&st->st_entry) == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scan candidate\n", __func__);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return 0;
notfound:
/*
* If nothing suitable was found decrement
@@ -996,8 +1053,10 @@ notfound:
st->st_newscan = 1;
return 0; /* restart scan */
}
- selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN);
- if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base))
+ selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return (selbs != NULL);
+ if (selbs == NULL || !ieee80211_sta_join(vap, &selbs->base))
goto notfound;
return 1; /* terminate scan */
}
@@ -1024,12 +1083,14 @@ sta_lookup(struct sta_table *st, const uint8_t macaddr[IEEE80211_ADDR_LEN])
}
static void
-sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
- struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni = vap->iv_bss;
struct sta_table *st = ss->ss_priv;
+ enum ieee80211_phymode mode;
struct sta_entry *se, *selbs;
- uint8_t roamRate, curRate;
+ uint8_t roamRate, curRate, ucastRate;
int8_t roamRssi, curRssi;
se = sta_lookup(st, ni->ni_macaddr);
@@ -1038,27 +1099,21 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
return;
}
- /* XXX do we need 11g too? */
- if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
- roamRate = ic->ic_roam.rate11b;
- roamRssi = ic->ic_roam.rssi11b;
- } else if (IEEE80211_IS_CHAN_B(ic->ic_bsschan)) {
- roamRate = ic->ic_roam.rate11bOnly;
- roamRssi = ic->ic_roam.rssi11bOnly;
- } else {
- roamRate = ic->ic_roam.rate11a;
- roamRssi = ic->ic_roam.rssi11a;
- }
+ mode = ieee80211_chan2mode(ic->ic_bsschan);
+ roamRate = vap->iv_roamparms[mode].rate;
+ roamRssi = vap->iv_roamparms[mode].rssi;
+ ucastRate = vap->iv_txparms[mode].ucastrate;
/* NB: the most up to date rssi is in the node, not the scan cache */
curRssi = ic->ic_node_getrssi(ni);
- if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
- curRate = ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL;
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM,
+ if (ucastRate == IEEE80211_FIXED_RATE_NONE) {
+ curRate = ni->ni_txrate;
+ roamRate &= IEEE80211_RATE_VAL;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM,
"%s: currssi %d currate %u roamrssi %d roamrate %u\n",
__func__, curRssi, curRate, roamRssi, roamRate);
} else {
curRate = roamRate; /* NB: insure compare below fails */
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_ROAM,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM,
"%s: currssi %d roamrssi %d\n", __func__, curRssi, roamRssi);
}
/*
@@ -1066,7 +1121,7 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* XXX deauth current ap
*/
if (curRate < roamRate || curRssi < roamRssi) {
- if (time_after(ticks, ic->ic_lastscan + ic->ic_scanvalid)) {
+ if (time_after(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
/*
* Scan cache contents are too old; force a scan now
* if possible so we have current state to make a
@@ -1076,19 +1131,19 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
* XXX force immediate switch on scan complete
*/
if (!IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
- time_after(ticks, ic->ic_lastdata + ic->ic_bgscanidle))
- ieee80211_bg_scan(ic);
+ time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle))
+ ieee80211_bg_scan(vap, 0);
return;
}
se->base.se_rssi = curRssi;
- selbs = select_bss(ss, ic, IEEE80211_MSG_ROAM);
+ selbs = select_bss(ss, vap, IEEE80211_MSG_ROAM);
if (selbs != NULL && selbs != se) {
- IEEE80211_DPRINTF(ic,
+ IEEE80211_DPRINTF(vap,
IEEE80211_MSG_ROAM | IEEE80211_MSG_DEBUG,
"%s: ROAM: curRate %u, roamRate %u, "
"curRssi %d, roamRssi %d\n", __func__,
curRate, roamRate, curRssi, roamRssi);
- ieee80211_sta_join(ic, &selbs->base);
+ ieee80211_sta_join(vap, &selbs->base);
}
}
}
@@ -1100,19 +1155,9 @@ sta_roam_check(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
static void
sta_age(struct ieee80211_scan_state *ss)
{
- struct ieee80211com *ic = ss->ss_ic;
- struct sta_table *st = ss->ss_priv;
- struct sta_entry *se, *next;
+ struct ieee80211vap *vap = ss->ss_vap;
- mtx_lock(&st->st_lock);
- TAILQ_FOREACH_SAFE(se, &st->st_entry, se_list, next) {
- if (se->se_notseen > STA_PURGE_SCANS) {
- TAILQ_REMOVE(&st->st_entry, se, se_list);
- LIST_REMOVE(se, se_hash);
- FREE(se, M_80211_SCAN);
- }
- }
- mtx_unlock(&st->st_lock);
+ adhoc_age(ss);
/*
* If rate control is enabled check periodically to see if
* we should roam from our current connection to one that
@@ -1122,13 +1167,13 @@ sta_age(struct ieee80211_scan_state *ss)
* XXX repeater station
* XXX do when !bgscan?
*/
- KASSERT(ic->ic_opmode == IEEE80211_M_STA,
- ("wrong mode %u", ic->ic_opmode));
- if (ic->ic_roaming == IEEE80211_ROAMING_AUTO &&
- (ic->ic_flags & IEEE80211_F_BGSCAN) &&
- ic->ic_state >= IEEE80211_S_RUN)
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+ ("wrong mode %u", vap->iv_opmode));
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO &&
+ (vap->iv_ic->ic_flags & IEEE80211_F_BGSCAN) &&
+ vap->iv_state >= IEEE80211_S_RUN)
/* XXX vap is implicit */
- sta_roam_check(ss, ic);
+ sta_roam_check(ss, vap);
}
/*
@@ -1144,7 +1189,7 @@ sta_iterate(struct ieee80211_scan_state *ss,
u_int gen;
mtx_lock(&st->st_scanlock);
- gen = st->st_scangen++;
+ gen = st->st_scaniter++;
restart:
mtx_lock(&st->st_lock);
TAILQ_FOREACH(se, &st->st_entry, se_list) {
@@ -1173,7 +1218,7 @@ sta_assoc_fail(struct ieee80211_scan_state *ss,
if (se != NULL) {
se->se_fails++;
se->se_lastfail = ticks;
- IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN,
+ IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN,
macaddr, "%s: reason %u fails %u",
__func__, reason, se->se_fails);
}
@@ -1190,7 +1235,7 @@ sta_assoc_success(struct ieee80211_scan_state *ss,
if (se != NULL) {
#if 0
se->se_fails = 0;
- IEEE80211_NOTE_MAC(ss->ss_ic, IEEE80211_MSG_SCAN,
+ IEEE80211_NOTE_MAC(ss->ss_vap, IEEE80211_MSG_SCAN,
macaddr, "%s: fails %u",
__func__, se->se_fails);
#endif
@@ -1240,93 +1285,30 @@ static const struct scanlist adhocScanTable[] = {
* Start an adhoc-mode scan by populating the channel list.
*/
static int
-adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+adhoc_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
-#define N(a) (sizeof(a)/sizeof(a[0]))
struct sta_table *st = ss->ss_priv;
- const struct scanlist *scan;
- enum ieee80211_phymode mode;
- ss->ss_last = 0;
- /*
- * Use the table of ordered channels to construct the list
- * of channels for scanning. Any channels in the ordered
- * list not in the master list will be discarded.
- */
- for (scan = adhocScanTable; scan->list != NULL; scan++) {
- mode = scan->mode;
- if (ic->ic_des_mode != IEEE80211_MODE_AUTO) {
- /*
- * If a desired mode was specified, scan only
- * channels that satisfy that constraint.
- */
- if (ic->ic_des_mode != mode) {
- /*
- * The scan table marks 2.4Ghz channels as b
- * so if the desired mode is 11g, then use
- * the 11b channel list but upgrade the mode.
- */
- if (ic->ic_des_mode != IEEE80211_MODE_11G ||
- mode != IEEE80211_MODE_11B)
- continue;
- mode = IEEE80211_MODE_11G; /* upgrade */
- }
- } else {
- /*
- * This lets add_channels upgrade an 11b channel
- * to 11g if available.
- */
- if (mode == IEEE80211_MODE_11B)
- mode = IEEE80211_MODE_AUTO;
- }
-#ifdef IEEE80211_F_XR
- /* XR does not operate on turbo channels */
- if ((ic->ic_flags & IEEE80211_F_XR) &&
- (mode == IEEE80211_MODE_TURBO_A ||
- mode == IEEE80211_MODE_TURBO_G))
- continue;
-#endif
- /*
- * Add the list of the channels; any that are not
- * in the master channel list will be discarded.
- */
- add_channels(ic, ss, mode, scan->list, scan->count);
- }
-
- /*
- * Add the channels from the ic (from HAL) that are not present
- * in the staScanTable.
- */
- sweepchannels(ss, ic, adhocScanTable);
-
- ss->ss_next = 0;
- /* XXX tunables */
- ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
- ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+ makescanlist(ss, vap, adhocScanTable);
-#ifdef IEEE80211_DEBUG
- if (ieee80211_msg_scan(ic)) {
- if_printf(ic->ic_ifp, "scan set ");
- ieee80211_scan_dump_channels(ss);
- printf(" dwell min %ld max %ld\n",
- ss->ss_mindwell, ss->ss_maxdwell);
- }
-#endif /* IEEE80211_DEBUG */
+ if (ss->ss_mindwell == 0)
+ ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
+ if (ss->ss_maxdwell == 0)
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+ st->st_scangen++;
st->st_newscan = 1;
return 0;
-#undef N
}
/*
* Select a channel to start an adhoc network on.
* The channel list was populated with appropriate
* channels so select one that looks least occupied.
- * XXX need regulatory domain constraints
*/
static struct ieee80211_channel *
-adhoc_pick_channel(struct ieee80211_scan_state *ss)
+adhoc_pick_channel(struct ieee80211_scan_state *ss, int flags)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *se;
@@ -1339,7 +1321,14 @@ adhoc_pick_channel(struct ieee80211_scan_state *ss)
mtx_lock(&st->st_lock);
for (i = 0; i < ss->ss_last; i++) {
c = ss->ss_chans[i];
- if (!checktable(adhocScanTable, c))
+ /* never consider a channel with radar */
+ if (IEEE80211_IS_CHAN_RADAR(c))
+ continue;
+ /* skip channels disallowed by regulatory settings */
+ if (IEEE80211_IS_CHAN_NOADHOC(c))
+ continue;
+ /* check channel attributes for band compatibility */
+ if (flags != 0 && (c->ic_flags & flags) != flags)
continue;
maxrssi = 0;
TAILQ_FOREACH(se, &st->st_entry, se_list) {
@@ -1361,15 +1350,15 @@ adhoc_pick_channel(struct ieee80211_scan_state *ss)
* to use to start an ibss network.
*/
static int
-adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
+adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
struct sta_table *st = ss->ss_priv;
struct sta_entry *selbs;
struct ieee80211_channel *chan;
- KASSERT(ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO,
- ("wrong opmode %u", ic->ic_opmode));
+ KASSERT(vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO,
+ ("wrong opmode %u", vap->iv_opmode));
if (st->st_newscan) {
sta_update_notseen(st);
@@ -1390,22 +1379,26 @@ adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211com *ic)
*/
/* NB: unlocked read should be ok */
if (TAILQ_FIRST(&st->st_entry) == NULL) {
- IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no scan candidate\n", __func__);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return 0;
notfound:
- if (ic->ic_des_nssid) {
+ if (vap->iv_des_nssid) {
/*
* No existing adhoc network to join and we have
* an ssid; start one up. If no channel was
* specified, try to select a channel.
*/
- if (ic->ic_des_chan == IEEE80211_CHAN_ANYC)
- chan = ieee80211_ht_adjust_channel(ic,
- adhoc_pick_channel(ss), ic->ic_flags_ext);
- else
- chan = ic->ic_des_chan;
+ if (vap->iv_des_chan == IEEE80211_CHAN_ANYC ||
+ IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) {
+ chan = ieee80211_ht_adjust_channel(vap->iv_ic,
+ adhoc_pick_channel(ss, 0),
+ vap->iv_flags_ext);
+ } else
+ chan = vap->iv_des_chan;
if (chan != NULL) {
- ieee80211_create_ibss(ic, chan);
+ ieee80211_create_ibss(vap, chan);
return 1;
}
}
@@ -1420,8 +1413,10 @@ notfound:
st->st_newscan = 1;
return 0; /* restart scan */
}
- selbs = select_bss(ss, ic, IEEE80211_MSG_SCAN);
- if (selbs == NULL || !ieee80211_sta_join(ic, &selbs->base))
+ selbs = select_bss(ss, vap, IEEE80211_MSG_SCAN);
+ if (ss->ss_flags & IEEE80211_SCAN_NOJOIN)
+ return (selbs != NULL);
+ if (selbs == NULL || !ieee80211_sta_join(vap, &selbs->base))
goto notfound;
return 1; /* terminate scan */
}
@@ -1440,6 +1435,7 @@ adhoc_age(struct ieee80211_scan_state *ss)
if (se->se_notseen > STA_PURGE_SCANS) {
TAILQ_REMOVE(&st->st_entry, se, se_list);
LIST_REMOVE(se, se_hash);
+ ieee80211_ies_cleanup(&se->base.se_ies);
FREE(se, M_80211_SCAN);
}
}
@@ -1455,6 +1451,7 @@ static const struct ieee80211_scanner adhoc_default = {
.scan_cancel = sta_cancel,
.scan_end = adhoc_pick_bss,
.scan_flush = sta_flush,
+ .scan_pickchan = adhoc_pick_channel,
.scan_add = sta_add,
.scan_age = adhoc_age,
.scan_iterate = sta_iterate,
@@ -1462,39 +1459,161 @@ static const struct ieee80211_scanner adhoc_default = {
.scan_assoc_success = sta_assoc_success,
};
+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;
+ ic->ic_update_promisc(ifp);
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+ap_reset_promisc(struct ieee80211com *ic)
+{
+ IEEE80211_LOCK(ic);
+ ieee80211_syncifflag_locked(ic, IFF_PROMISC);
+ IEEE80211_UNLOCK(ic);
+}
+
+static int
+ap_start(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+{
+ struct sta_table *st = ss->ss_priv;
+
+ makescanlist(ss, vap, staScanTable);
+
+ if (ss->ss_mindwell == 0)
+ ss->ss_mindwell = msecs_to_ticks(200); /* 200ms */
+ if (ss->ss_maxdwell == 0)
+ ss->ss_maxdwell = msecs_to_ticks(200); /* 200ms */
+
+ st->st_scangen++;
+ st->st_newscan = 1;
+
+ ap_force_promisc(vap->iv_ic);
+ return 0;
+}
+
/*
- * Module glue.
+ * Cancel an ongoing scan.
*/
static int
-wlan_modevent(module_t mod, int type, void *unused)
+ap_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
{
- switch (type) {
- case MOD_LOAD:
- ieee80211_scanner_register(IEEE80211_M_STA, &sta_default);
- ieee80211_scanner_register(IEEE80211_M_IBSS, &adhoc_default);
- ieee80211_scanner_register(IEEE80211_M_AHDEMO, &adhoc_default);
- return 0;
- case MOD_UNLOAD:
- case MOD_QUIESCE:
- if (nrefs) {
- printf("wlan_scan_sta: still in use (%u dynamic refs)\n",
- nrefs);
- return EBUSY;
+ ap_reset_promisc(vap->iv_ic);
+ return 0;
+}
+
+/*
+ * Pick a quiet channel to use for ap operation.
+ */
+static struct ieee80211_channel *
+ap_pick_channel(struct ieee80211_scan_state *ss, int flags)
+{
+ struct sta_table *st = ss->ss_priv;
+ struct ieee80211_channel *bestchan = NULL;
+ int i;
+
+ /* XXX select channel more intelligently, e.g. channel spread, power */
+ /* NB: use scan list order to preserve channel preference */
+ for (i = 0; i < ss->ss_last; i++) {
+ struct ieee80211_channel *chan = ss->ss_chans[i];
+ /*
+ * If the channel is unoccupied the max rssi
+ * should be zero; just take it. Otherwise
+ * track the channel with the lowest rssi and
+ * use that when all channels appear occupied.
+ */
+ if (IEEE80211_IS_CHAN_RADAR(chan))
+ continue;
+ if (IEEE80211_IS_CHAN_NOHOSTAP(chan))
+ continue;
+ /* check channel attributes for band compatibility */
+ if (flags != 0 && (chan->ic_flags & flags) != flags)
+ continue;
+ /* XXX channel have interference */
+ if (st->st_maxrssi[chan->ic_ieee] == 0) {
+ /* XXX use other considerations */
+ return chan;
}
- if (type == MOD_UNLOAD) {
- ieee80211_scanner_unregister_all(&sta_default);
- ieee80211_scanner_unregister_all(&adhoc_default);
+ if (bestchan == NULL ||
+ st->st_maxrssi[chan->ic_ieee] < st->st_maxrssi[bestchan->ic_ieee])
+ bestchan = chan;
+ }
+ return bestchan;
+}
+
+/*
+ * Pick a quiet channel to use for ap operation.
+ */
+static int
+ap_end(struct ieee80211_scan_state *ss, struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_channel *bestchan;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP,
+ ("wrong opmode %u", vap->iv_opmode));
+ bestchan = ap_pick_channel(ss, 0);
+ if (bestchan == NULL) {
+ /* no suitable channel, should not happen */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no suitable channel! (should not happen)\n", __func__);
+ /* XXX print something? */
+ return 0; /* restart scan */
+ }
+ /*
+ * If this is a dynamic turbo channel, start with the unboosted one.
+ */
+ if (IEEE80211_IS_CHAN_TURBO(bestchan)) {
+ bestchan = ieee80211_find_channel(ic, bestchan->ic_freq,
+ bestchan->ic_flags & ~IEEE80211_CHAN_TURBO);
+ if (bestchan == NULL) {
+ /* should never happen ?? */
+ return 0;
}
- return 0;
}
- return EINVAL;
+ ap_reset_promisc(ic);
+ if (ss->ss_flags & (IEEE80211_SCAN_NOPICK | IEEE80211_SCAN_NOJOIN)) {
+ /*
+ * Manual/background scan, don't select+join the
+ * bss, just return. The scanning framework will
+ * handle notification that this has completed.
+ */
+ ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
+ return 1;
+ }
+ ieee80211_create_ibss(vap,
+ ieee80211_ht_adjust_channel(ic, bestchan, vap->iv_flags_ext));
+ return 1;
}
-static moduledata_t wlan_mod = {
- "wlan_scan_sta",
- wlan_modevent,
- 0
+static const struct ieee80211_scanner ap_default = {
+ .scan_name = "default",
+ .scan_attach = sta_attach,
+ .scan_detach = sta_detach,
+ .scan_start = ap_start,
+ .scan_restart = sta_restart,
+ .scan_cancel = ap_cancel,
+ .scan_end = ap_end,
+ .scan_flush = sta_flush,
+ .scan_pickchan = ap_pick_channel,
+ .scan_add = sta_add,
+ .scan_age = adhoc_age,
+ .scan_iterate = sta_iterate,
+ .scan_assoc_success = sta_assoc_success,
+ .scan_assoc_fail = sta_assoc_fail,
};
-DECLARE_MODULE(wlan_scan_sta, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_scan_sta, 1);
-MODULE_DEPEND(wlan_scan_sta, wlan, 1, 1, 1);
+
+/*
+ * Module glue.
+ */
+IEEE80211_SCANNER_MODULE(sta, 1);
+IEEE80211_SCANNER_ALG(sta, IEEE80211_M_STA, sta_default);
+IEEE80211_SCANNER_ALG(ibss, IEEE80211_M_IBSS, adhoc_default);
+IEEE80211_SCANNER_ALG(ahdemo, IEEE80211_M_AHDEMO, adhoc_default);
+IEEE80211_SCANNER_ALG(ap, IEEE80211_M_HOSTAP, ap_default);
diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c
new file mode 100644
index 0000000..e3c57ad
--- /dev/null
+++ b/sys/net80211/ieee80211_sta.c
@@ -0,0 +1,1647 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 Station mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_sta.h>
+#include <net80211/ieee80211_input.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 rssi, int noise, uint32_t rstamp);
+static void sta_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, uint32_t rstamp);
+
+void
+ieee80211_sta_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_STA] = sta_vattach;
+}
+
+void
+ieee80211_sta_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+sta_vdetach(struct ieee80211vap *vap)
+{
+}
+
+static void
+sta_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = sta_newstate;
+ vap->iv_input = sta_input;
+ vap->iv_recv_mgmt = sta_recv_mgmt;
+ vap->iv_opdetach = sta_vdetach;
+ vap->iv_bmiss = sta_beacon_miss;
+}
+
+/*
+ * Handle a beacon miss event. The common code filters out
+ * spurious events that can happen when scanning and/or before
+ * reaching RUN state.
+ */
+static void
+sta_beacon_miss(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, ("scanning"));
+ KASSERT(vap->iv_state == IEEE80211_S_RUN,
+ ("wrong state %d", vap->iv_state));
+
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "beacon miss, mode %u state %s\n",
+ vap->iv_opmode, ieee80211_state_name[vap->iv_state]);
+
+ if (++vap->iv_bmiss_count < vap->iv_bmiss_max) {
+ /*
+ * Send a directed probe req before falling back to a
+ * scan; if we receive a response ic_bmiss_count will
+ * be reset. Some cards mistakenly report beacon miss
+ * so this avoids the expensive scan if the ap is
+ * still there.
+ */
+ ieee80211_send_probereq(vap->iv_bss, vap->iv_myaddr,
+ vap->iv_bss->ni_bssid, vap->iv_bss->ni_bssid,
+ vap->iv_bss->ni_essid, vap->iv_bss->ni_esslen);
+ return;
+ }
+ vap->iv_bmiss_count = 0;
+ vap->iv_stats.is_beacon_miss++;
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ /*
+ * If we receive a beacon miss interrupt when using
+ * dynamic turbo, attempt to switch modes before
+ * reassociating.
+ */
+ if (IEEE80211_ATH_CAP(vap, vap->iv_bss, IEEE80211_NODE_TURBOP))
+ ieee80211_dturbo_switch(vap,
+ ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO);
+ /*
+ * Try to reassociate before scanning for a new ap.
+ */
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
+ } else {
+ /*
+ * Somebody else is controlling state changes (e.g.
+ * a user-mode app) don't do anything that would
+ * confuse them; just drop into scan mode so they'll
+ * notified of the state change and given control.
+ */
+ ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+ }
+}
+
+/*
+ * Handle deauth with reason. We retry only for
+ * the cases where we might succeed. Otherwise
+ * we downgrade the ap and scan.
+ */
+static void
+sta_authretry(struct ieee80211vap *vap, struct ieee80211_node *ni, int reason)
+{
+ switch (reason) {
+ case IEEE80211_STATUS_SUCCESS: /* NB: MLME assoc */
+ case IEEE80211_STATUS_TIMEOUT:
+ case IEEE80211_REASON_ASSOC_EXPIRE:
+ case IEEE80211_REASON_NOT_AUTHED:
+ case IEEE80211_REASON_NOT_ASSOCED:
+ case IEEE80211_REASON_ASSOC_LEAVE:
+ case IEEE80211_REASON_ASSOC_NOT_AUTHED:
+ IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ default:
+ ieee80211_scan_assoc_fail(vap, vap->iv_bss->ni_macaddr, reason);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ }
+}
+
+/*
+ * IEEE80211_M_STA vap state machine handler.
+ * This routine handles the main states in the 802.11 protocol.
+ */
+static int
+sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s (%d)\n",
+ __func__, ieee80211_state_name[ostate],
+ ieee80211_state_name[nstate], arg);
+ vap->iv_state = nstate; /* state transition */
+ callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
+ callout_stop(&vap->iv_swbmiss);
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SLEEP:
+ /* XXX wakeup */
+ case IEEE80211_S_RUN:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DISASSOC,
+ IEEE80211_REASON_ASSOC_LEAVE);
+ ieee80211_sta_leave(ni);
+ break;
+ case IEEE80211_S_ASSOC:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ break;
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ default:
+ goto invalid;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ if (vap->iv_auth->ia_detach != NULL)
+ vap->iv_auth->ia_detach(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ /*
+ * Initiate a scan. We can come here as a result
+ * of an IEEE80211_IOC_SCAN_REQ too in which case
+ * the vap will be marked with IEEE80211_FEXT_SCANREQ
+ * and the scan request parameters will be present
+ * in iv_scanreq. Otherwise we do the default.
+ */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SCANREQ) {
+ ieee80211_check_scan(vap,
+ vap->iv_scanreq_flags,
+ vap->iv_scanreq_duration,
+ vap->iv_scanreq_mindwell,
+ vap->iv_scanreq_maxdwell,
+ vap->iv_scanreq_nssid, vap->iv_scanreq_ssid);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
+ } else
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ /*
+ * These can happen either because of a timeout
+ * on an assoc/auth response or because of a
+ * change in state that requires a reset. For
+ * the former we're called with a non-zero arg
+ * that is the cause for the failure; pass this
+ * to the scan code so it can update state.
+ * Otherwise trigger a new scan unless we're in
+ * manual roaming mode in which case an application
+ * must issue an explicit scan request.
+ */
+ if (arg != 0)
+ ieee80211_scan_assoc_fail(vap,
+ vap->iv_bss->ni_macaddr, arg);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ case IEEE80211_S_RUN: /* beacon miss */
+ /*
+ * Beacon miss. Notify user space and if not
+ * under control of a user application (roaming
+ * manual) kick off a scan to re-connect.
+ */
+ ieee80211_sta_leave(ni);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO)
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_AUTH:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ break;
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ switch (arg & 0xff) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ /* ??? */
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ break;
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ sta_authretry(vap, ni, arg>>8);
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ switch (arg & 0xff) {
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 2);
+ vap->iv_state = ostate; /* stay RUN */
+ break;
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ ieee80211_sta_leave(ni);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ /* try to reauth */
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ }
+ break;
+ }
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_ASSOC:
+ switch (ostate) {
+ case IEEE80211_S_AUTH:
+ case IEEE80211_S_ASSOC:
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ break;
+ case IEEE80211_S_SLEEP: /* cannot happen */
+ case IEEE80211_S_RUN:
+ ieee80211_sta_leave(ni);
+ if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) {
+ IEEE80211_SEND_MGMT(ni, arg ?
+ IEEE80211_FC0_SUBTYPE_REASSOC_REQ :
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+ }
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_flags & IEEE80211_F_WPA) {
+ /* XXX validate prerequisites */
+ }
+ switch (ostate) {
+ case IEEE80211_S_RUN:
+ break;
+ case IEEE80211_S_AUTH: /* when join is done in fw */
+ case IEEE80211_S_ASSOC:
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap)) {
+ ieee80211_note(vap, "%s with %s ssid ",
+ (vap->iv_opmode == IEEE80211_M_STA ?
+ "associated" : "synchronized"),
+ ether_sprintf(ni->ni_bssid));
+ ieee80211_print_essid(vap->iv_bss->ni_essid,
+ ni->ni_esslen);
+ /* XXX MCS/HT */
+ printf(" channel %d start %uMb\n",
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ IEEE80211_RATE2MBS(ni->ni_txrate));
+ }
+#endif
+ ieee80211_scan_assoc_success(vap, ni->ni_macaddr);
+ ieee80211_notify_node_join(ni,
+ arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+ break;
+ case IEEE80211_S_SLEEP:
+ ieee80211_sta_pwrsave(vap, 0);
+ break;
+ default:
+ goto invalid;
+ }
+ ieee80211_sync_curchan(ic);
+ if (ostate != IEEE80211_S_RUN &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)) {
+ /*
+ * Start s/w beacon miss timer for devices w/o
+ * hardware support. We fudge a bit here since
+ * we're doing this in software.
+ */
+ vap->iv_swbmiss_period = IEEE80211_TU_TO_TICKS(
+ 2 * vap->iv_bmissthreshold * ni->ni_intval);
+ vap->iv_swbmiss_count = 0;
+ callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period,
+ ieee80211_swbmiss, vap);
+ }
+ /*
+ * When 802.1x is not in use mark the port authorized
+ * at this point so traffic can flow.
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+ break;
+ case IEEE80211_S_SLEEP:
+ ieee80211_sta_pwrsave(vap, 0);
+ break;
+ default:
+ invalid:
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
+ "%s: invalid state transition %s -> %s\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Return non-zero if the frame is an echo of a multicast
+ * frame sent by ourself. The dir is known to be DSTODS.
+ */
+static __inline int
+isdstods_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
+{
+#define QWH4(wh) ((const struct ieee80211_qosframe_addr4 *)wh)
+#define WH4(wh) ((const struct ieee80211_frame_addr4 *)wh)
+ const uint8_t *sa;
+
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr3))
+ return 0;
+ sa = IEEE80211_QOS_HAS_SEQ(wh) ? QWH4(wh)->i_addr4 : WH4(wh)->i_addr4;
+ return IEEE80211_ADDR_EQ(sa, vap->iv_myaddr);
+#undef WH4
+#undef QWH4
+}
+
+/*
+ * Return non-zero if the frame is an echo of a multicast
+ * frame sent by ourself. The dir is known to be FROMDS.
+ */
+static __inline int
+isfromds_mcastecho(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
+{
+ KASSERT(vap->iv_opmode == IEEE80211_M_STA, ("wrong mode"));
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
+ return 0;
+ return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr);
+}
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled. This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211vap *vap, int subtype)
+{
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ return (vap->iv_ic->ic_flags & IEEE80211_F_SCAN);
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+sta_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint8_t *bssid;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ bssid = wh->i_addr2;
+ if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ bssid, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >>
+ IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] &
+ IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ }
+
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ (dir == IEEE80211_FC1_DIR_FROMDS ||
+ dir == IEEE80211_FC1_DIR_DSTODS) &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+ if (dir == IEEE80211_FC1_DIR_FROMDS) {
+ if ((ifp->if_flags & IFF_SIMPLEX) &&
+ isfromds_mcastecho(vap, wh)) {
+ /*
+ * In IEEE802.11 network, multicast
+ * packets sent from "me" are broadcast
+ * from the AP; silently discard for
+ * SIMPLEX interface.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "multicast echo");
+ vap->iv_stats.is_rx_mcastecho++;
+ goto out;
+ }
+ if ((vap->iv_flags & IEEE80211_F_DWDS) &&
+ IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ /*
+ * DWDS sta's must drop 3-address mcast frames
+ * as they will be sent separately as a 4-addr
+ * frame. Accepting the 3-addr frame will
+ * confuse the bridge into thinking the sending
+ * sta is located at the end of WDS link.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
+ "3-address data", "%s", "DWDS enabled");
+ vap->iv_stats.is_rx_mcastecho++;
+ goto out;
+ }
+ } else if (dir == IEEE80211_FC1_DIR_DSTODS) {
+ if ((vap->iv_flags & IEEE80211_F_DWDS) == 0) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_INPUT, wh, "4-address data",
+ "%s", "DWDS not enabled");
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ if ((ifp->if_flags & IFF_SIMPLEX) &&
+ isdstods_mcastecho(vap, wh)) {
+ /*
+ * In IEEE802.11 network, multicast
+ * packets sent from "me" are broadcast
+ * from the AP; silently discard for
+ * SIMPLEX interface.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
+ "4-address data", "%s", "multicast echo");
+ vap->iv_stats.is_rx_mcastecho++;
+ goto out;
+ }
+ } else {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh,
+ "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ ieee80211_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_msg_debug(vap) && doprint(vap, subtype)) ||
+ ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
+ /*
+ * Only shared key auth frames with a challenge
+ * should be encrypted, discard all others.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "mgt", "%s", "WEP set but PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ goto out;
+ }
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, NULL, "bad frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static void
+sta_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ int rssi, int noise, uint32_t rstamp, uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "open auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX */
+ return;
+ }
+ if (vap->iv_state != IEEE80211_S_AUTH ||
+ seq != IEEE80211_AUTH_OPEN_RESPONSE) {
+ vap->iv_stats.is_rx_bad_auth++;
+ return;
+ }
+ if (status != 0) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+ ni, "open auth failed (reason %d)", status);
+ vap->iv_stats.is_rx_auth_fail++;
+ vap->iv_stats.is_rx_authfail_code = status;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
+ } else
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
+}
+
+static void
+sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
+ uint8_t *frm, uint8_t *efrm, int rssi, int noise, uint32_t rstamp,
+ uint16_t seq, uint16_t status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint8_t *challenge;
+ int estatus;
+
+ /*
+ * NB: this can happen as we allow pre-shared key
+ * authentication to be enabled w/o wep being turned
+ * on so that configuration of these can be done
+ * in any order. It may be better to enforce the
+ * ordering in which case this check would just be
+ * for sanity/consistency.
+ */
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", " PRIVACY is disabled");
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+ /*
+ * Pre-shared key authentication is evil; accept
+ * it only if explicitly configured (it is supported
+ * mainly for compatibility with clients like OS X).
+ */
+ if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
+ ni->ni_authmode != IEEE80211_AUTH_SHARED) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad sta auth mode %u", ni->ni_authmode);
+ vap->iv_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */
+ estatus = IEEE80211_STATUS_ALG;
+ goto bad;
+ }
+
+ challenge = NULL;
+ if (frm + 1 < efrm) {
+ if ((frm[1] + 2) > (efrm - frm)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "ie %d/%d too long",
+ frm[0], (frm[1] + 2) - (efrm - frm));
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (*frm == IEEE80211_ELEMID_CHALLENGE)
+ challenge = frm;
+ frm += frm[1] + 2;
+ }
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ case IEEE80211_AUTH_SHARED_RESPONSE:
+ if (challenge == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "%s", "no challenge");
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
+ ni->ni_macaddr, "shared key auth",
+ "bad challenge len %d", challenge[1]);
+ vap->iv_stats.is_rx_bad_auth++;
+ estatus = IEEE80211_STATUS_CHALLENGE;
+ goto bad;
+ }
+ default:
+ break;
+ }
+ if (vap->iv_state != IEEE80211_S_AUTH)
+ return;
+ switch (seq) {
+ case IEEE80211_AUTH_SHARED_PASS:
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_80211_NODE);
+ ni->ni_challenge = NULL;
+ }
+ if (status != 0) {
+ IEEE80211_NOTE_FRAME(vap,
+ IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, wh,
+ "shared key auth failed (reason %d)", status);
+ vap->iv_stats.is_rx_auth_fail++;
+ vap->iv_stats.is_rx_authfail_code = status;
+ return;
+ }
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
+ break;
+ case IEEE80211_AUTH_SHARED_CHALLENGE:
+ if (!ieee80211_alloc_challenge(ni))
+ return;
+ /* XXX could optimize by passing recvd challenge */
+ memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
+ IEEE80211_SEND_MGMT(ni,
+ IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+ break;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_AUTH,
+ wh, "shared key auth", "bad seq %d", seq);
+ vap->iv_stats.is_rx_bad_auth++;
+ return;
+ }
+ return;
+bad:
+ /*
+ * Kick the state machine. This short-circuits
+ * using the mgt frame timeout to trigger the
+ * state transition.
+ */
+ if (vap->iv_state == IEEE80211_S_AUTH)
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
+}
+
+static int
+ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm,
+ const struct ieee80211_frame *wh)
+{
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+ struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme;
+ u_int len = frm[1], qosinfo;
+ int i;
+
+ if (len < sizeof(struct ieee80211_wme_param)-2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
+ wh, "WME", "too short, len %u", len);
+ return -1;
+ }
+ qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
+ qosinfo &= WME_QOSINFO_COUNT;
+ /* XXX do proper check for wraparound */
+ if (qosinfo == wme->wme_wmeChanParams.cap_info)
+ return 0;
+ frm += __offsetof(struct ieee80211_wme_param, params_acParams);
+ for (i = 0; i < WME_NUM_AC; i++) {
+ struct wmeParams *wmep =
+ &wme->wme_wmeChanParams.cap_wmeParams[i];
+ /* NB: ACI not used */
+ wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
+ wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
+ wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
+ wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
+ wmep->wmep_txopLimit = LE_READ_2(frm+2);
+ frm += 4;
+ }
+ wme->wme_wmeChanParams.cap_info = qosinfo;
+ return 1;
+#undef MS
+}
+
+static int
+ieee80211_parse_athparams(struct ieee80211_node *ni, uint8_t *frm,
+ const struct ieee80211_frame *wh)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ const struct ieee80211_ath_ie *ath;
+ u_int len = frm[1];
+ int capschanged;
+ uint16_t defkeyix;
+
+ if (len < sizeof(struct ieee80211_ath_ie)-2) {
+ IEEE80211_DISCARD_IE(vap,
+ IEEE80211_MSG_ELEMID | IEEE80211_MSG_SUPERG,
+ wh, "Atheros", "too short, len %u", len);
+ return -1;
+ }
+ ath = (const struct ieee80211_ath_ie *)frm;
+ capschanged = (ni->ni_ath_flags != ath->ath_capability);
+ defkeyix = LE_READ_2(ath->ath_defkeyix);
+ if (capschanged || defkeyix != ni->ni_ath_defkeyix) {
+ ni->ni_ath_flags = ath->ath_capability;
+ ni->ni_ath_defkeyix = defkeyix;
+ IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni,
+ "ath ie change: new caps 0x%x defkeyix 0x%x",
+ ni->ni_ath_flags, ni->ni_ath_defkeyix);
+ }
+ if (IEEE80211_ATH_CAP(vap, ni, ATHEROS_CAP_TURBO_PRIME)) {
+ uint16_t curflags, newflags;
+
+ /*
+ * Check for turbo mode switch. Calculate flags
+ * for the new mode and effect the switch.
+ */
+ newflags = curflags = vap->iv_ic->ic_bsschan->ic_flags;
+ /* NB: BOOST is not in ic_flags, so get it from the ie */
+ if (ath->ath_capability & ATHEROS_CAP_BOOST)
+ newflags |= IEEE80211_CHAN_TURBO;
+ else
+ newflags &= ~IEEE80211_CHAN_TURBO;
+ if (newflags != curflags)
+ ieee80211_dturbo_switch(vap, newflags);
+ }
+ return capschanged;
+}
+
+/*
+ * Return non-zero if a background scan may be continued:
+ * o bg scan is active
+ * o no channel switch is pending
+ * o there has not been any traffic recently
+ *
+ * Note we do not check if there is an administrative enable;
+ * this is only done to start the scan. We assume that any
+ * change in state will be accompanied by a request to cancel
+ * active scans which will otherwise cause this test to fail.
+ */
+static __inline int
+contbgscan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ return ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) &&
+ (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
+ vap->iv_state == IEEE80211_S_RUN && /* XXX? */
+ time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
+}
+
+/*
+ * Return non-zero if a backgrond scan may be started:
+ * o bg scanning is administratively enabled
+ * o no channel switch is pending
+ * o we are not boosted on a dynamic turbo channel
+ * o there has not been a scan recently
+ * o there has not been any traffic recently
+ */
+static __inline int
+startbgscan(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ return ((vap->iv_flags & IEEE80211_F_BGSCAN) &&
+ (ic->ic_flags & IEEE80211_F_CSAPENDING) == 0 &&
+ !IEEE80211_IS_CHAN_DTURBO(ic->ic_curchan) &&
+ time_after(ticks, ic->ic_lastscan + vap->iv_bgscanintvl) &&
+ time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle));
+}
+
+static void
+sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, uint32_t rstamp)
+{
+#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *frm, *efrm;
+ uint8_t *rates, *xrates, *wme, *htcap, *htinfo;
+ uint8_t rate;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (uint8_t *)&wh[1];
+ efrm = mtod(m0, uint8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON: {
+ struct ieee80211_scanparams scan;
+ /*
+ * We process beacon/probe response frames:
+ * o when scanning, or
+ * o station mode when associated (to collect state
+ * updates such as 802.11g slot time), or
+ * Frames otherwise received are discarded.
+ */
+ if (!((ic->ic_flags & IEEE80211_F_SCAN) || ni->ni_associd)) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ /* XXX probe response in sta mode when !scanning? */
+ if (ieee80211_parse_beacon(ni, m0, &scan) != 0)
+ return;
+ /*
+ * Count frame now that we know it's to be processed.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ vap->iv_stats.is_rx_beacon++; /* XXX remove */
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * When operating in station mode, check for state updates.
+ * Be careful to ignore beacons received while doing a
+ * background scan. We consider only 11g/WMM stuff right now.
+ */
+ if (ni->ni_associd != 0 &&
+ ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
+ IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
+ /* record tsf of last beacon */
+ memcpy(ni->ni_tstamp.data, scan.tstamp,
+ sizeof(ni->ni_tstamp));
+ /* count beacon frame for s/w bmiss handling */
+ vap->iv_swbmiss_count++;
+ vap->iv_bmiss_count = 0;
+ if (ni->ni_erp != scan.erp) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2,
+ "erp change: was 0x%x, now 0x%x",
+ ni->ni_erp, scan.erp);
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
+ (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ else
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ ni->ni_erp = scan.erp;
+ /* XXX statistic */
+ /* XXX driver notification */
+ }
+ if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2,
+ "capabilities change: was 0x%x, now 0x%x",
+ ni->ni_capinfo, scan.capinfo);
+ /*
+ * NB: we assume short preamble doesn't
+ * change dynamically
+ */
+ ieee80211_set_shortslottime(ic,
+ IEEE80211_IS_CHAN_A(ic->ic_bsschan) ||
+ (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME)
+ | (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME);
+ /* XXX statistic */
+ }
+ if (scan.wme != NULL &&
+ (ni->ni_flags & IEEE80211_NODE_QOS) &&
+ ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0)
+ ieee80211_wme_updateparams(vap);
+ if (scan.ath != NULL)
+ ieee80211_parse_athparams(ni, scan.ath, wh);
+ if (scan.htcap != NULL && scan.htinfo != NULL) {
+ ieee80211_parse_htcap(ni, scan.htcap);
+ ieee80211_parse_htinfo(ni, scan.htinfo);
+ /* XXX state changes? */
+ }
+ if (scan.tim != NULL) {
+ struct ieee80211_tim_ie *tim =
+ (struct ieee80211_tim_ie *) scan.tim;
+#if 0
+ int aid = IEEE80211_AID(ni->ni_associd);
+ int ix = aid / NBBY;
+ int min = tim->tim_bitctl &~ 1;
+ int max = tim->tim_len + min - 4;
+ if ((tim->tim_bitctl&1) ||
+ (min <= ix && ix <= max &&
+ isset(tim->tim_bitmap - min, aid))) {
+ /*
+ * XXX Do not let bg scan kick off
+ * we are expecting data.
+ */
+ ic->ic_lastdata = ticks;
+ ieee80211_sta_pwrsave(vap, 0);
+ }
+#endif
+ ni->ni_dtim_count = tim->tim_count;
+ ni->ni_dtim_period = tim->tim_period;
+ }
+ /*
+ * If scanning, pass the info to the scan module.
+ * Otherwise, check if it's the right time to do
+ * a background scan. Background scanning must
+ * be enabled and we must not be operating in the
+ * turbo phase of dynamic turbo mode. Then,
+ * it's been a while since the last background
+ * scan and if no data frames have come through
+ * recently, kick off a scan. Note that this
+ * is the mechanism by which a background scan
+ * is started _and_ continued each time we
+ * return on-channel to receive a beacon from
+ * our ap.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ } else if (contbgscan(vap)) {
+ ieee80211_bg_scan(vap, 0);
+ } else if (startbgscan(vap)) {
+ vap->iv_stats.is_scan_bg++;
+#if 0
+ /* wakeup if we are sleeing */
+ ieee80211_set_pwrsave(vap, 0);
+#endif
+ ieee80211_bg_scan(vap, 0);
+ }
+ return;
+ }
+ /*
+ * If scanning, just pass information to the scan module.
+ */
+ if (ic->ic_flags & IEEE80211_F_SCAN) {
+ if (ic->ic_flags_ext & IEEE80211_FEXT_PROBECHAN) {
+ /*
+ * Actively scanning a channel marked passive;
+ * send a probe request now that we know there
+ * is 802.11 traffic present.
+ *
+ * XXX check if the beacon we recv'd gives
+ * us what we need and suppress the probe req
+ */
+ ieee80211_probe_curchan(vap, 1);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN;
+ }
+ ieee80211_add_scan(vap, &scan, wh,
+ subtype, rssi, noise, rstamp);
+ return;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_AUTH: {
+ uint16_t algo, seq, status;
+ /*
+ * auth frame format
+ * [2] algorithm
+ * [2] sequence
+ * [2] status
+ * [tlv*] challenge
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+ algo = le16toh(*(uint16_t *)frm);
+ seq = le16toh(*(uint16_t *)(frm + 2));
+ status = le16toh(*(uint16_t *)(frm + 4));
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
+ "recv auth frame with algorithm %d seq %d", algo, seq);
+
+ if (vap->iv_flags & IEEE80211_F_COUNTERM) {
+ IEEE80211_DISCARD(vap,
+ IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
+ wh, "auth", "%s", "TKIP countermeasures enabled");
+ vap->iv_stats.is_rx_auth_countermeasures++;
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ ieee80211_send_error(ni, wh->i_addr2,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ IEEE80211_REASON_MIC_FAILURE);
+ }
+ return;
+ }
+ if (algo == IEEE80211_AUTH_ALG_SHARED)
+ sta_auth_shared(ni, wh, frm + 6, efrm, rssi,
+ noise, rstamp, seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_OPEN)
+ sta_auth_open(ni, wh, rssi, noise, rstamp,
+ seq, status);
+ else {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "auth", "unsupported alg %d", algo);
+ vap->iv_stats.is_rx_auth_unsupported++;
+ return;
+ }
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
+ uint16_t capinfo, associd;
+ uint16_t status;
+
+ if (vap->iv_state != IEEE80211_S_ASSOC) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+
+ /*
+ * asresp frame format
+ * [2] capability information
+ * [2] status
+ * [2] association ID
+ * [tlv] supported rates
+ * [tlv] extended supported rates
+ * [tlv] WME
+ * [tlv] HT capabilities
+ * [tlv] HT info
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 6, return);
+ ni = vap->iv_bss;
+ capinfo = le16toh(*(uint16_t *)frm);
+ frm += 2;
+ status = le16toh(*(uint16_t *)frm);
+ frm += 2;
+ if (status != 0) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2, "%sassoc failed (reason %d)",
+ ISREASSOC(subtype) ? "re" : "", status);
+ vap->iv_stats.is_rx_auth_fail++; /* XXX */
+ return;
+ }
+ associd = le16toh(*(uint16_t *)frm);
+ frm += 2;
+
+ rates = xrates = wme = htcap = htinfo = NULL;
+ while (efrm - frm > 1) {
+ IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
+ switch (*frm) {
+ case IEEE80211_ELEMID_RATES:
+ rates = frm;
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ xrates = frm;
+ break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
+ break;
+ case IEEE80211_ELEMID_HTINFO:
+ htinfo = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswmeoui(frm))
+ wme = frm;
+ else if (vap->iv_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ /*
+ * Accept pre-draft HT ie's if the
+ * standard ones have not been seen.
+ */
+ if (ishtcapoui(frm)) {
+ if (htcap == NULL)
+ htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (htinfo == NULL)
+ htcap = frm;
+ }
+ }
+ /* XXX Atheros OUI support */
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+
+ IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, return);
+ if (xrates != NULL)
+ IEEE80211_VERIFY_ELEMENT(xrates,
+ IEEE80211_RATE_MAXSIZE - rates[1], return);
+ rate = ieee80211_setup_rates(ni, rates, xrates,
+ IEEE80211_F_JOIN |
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
+ IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ if (rate & IEEE80211_RATE_BASIC) {
+ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
+ wh->i_addr2,
+ "%sassoc failed (rate set mismatch)",
+ ISREASSOC(subtype) ? "re" : "");
+ vap->iv_stats.is_rx_assoc_norate++;
+ ieee80211_new_state(vap, IEEE80211_S_SCAN,
+ IEEE80211_SCAN_FAIL_STATUS);
+ return;
+ }
+
+ ni->ni_capinfo = capinfo;
+ ni->ni_associd = associd;
+ if (ni->ni_jointime == 0)
+ ni->ni_jointime = time_uptime;
+ if (wme != NULL &&
+ ieee80211_parse_wmeparams(vap, wme, wh) >= 0) {
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ ieee80211_wme_updateparams(vap);
+ } else
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ /*
+ * Setup HT state according to the negotiation.
+ *
+ * NB: shouldn't need to check if HT use is enabled but some
+ * ap's send back HT ie's even when we don't indicate we
+ * are HT capable in our AssocReq.
+ */
+ if (htcap != NULL && htinfo != NULL &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_HT)) {
+ ieee80211_ht_node_init(ni, htcap);
+ ieee80211_parse_htinfo(ni, htinfo);
+ ieee80211_setup_htrates(ni, htcap,
+ IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+ ieee80211_setup_basic_htrates(ni, htinfo);
+ }
+ /*
+ * Configure state now that we are associated.
+ *
+ * XXX may need different/additional driver callbacks?
+ */
+ if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
+ (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
+ ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+ } else {
+ ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags |= IEEE80211_F_USEBARKER;
+ }
+ ieee80211_set_shortslottime(ic,
+ IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
+ (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ /*
+ * Honor ERP protection.
+ *
+ * NB: ni_erp should zero for non-11g operation.
+ */
+ if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
+ (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ else
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ IEEE80211_NOTE_MAC(vap,
+ IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2,
+ "%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s",
+ ISREASSOC(subtype) ? "re" : "",
+ IEEE80211_NODE_AID(ni),
+ ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
+ ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
+ ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
+ ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
+ ni->ni_flags & IEEE80211_NODE_HT ?
+ (ni->ni_chw == 20 ? ", HT20" : ", HT40") : "",
+ ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "",
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF) ?
+ ", fast-frames" : "",
+ IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_TURBOP) ?
+ ", turbo" : ""
+ );
+ ieee80211_new_state(vap, IEEE80211_S_RUN, subtype);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DEAUTH: {
+ uint16_t reason;
+
+ if (vap->iv_state == IEEE80211_S_SCAN) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
+ /* NB: can happen when in promiscuous mode */
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+
+ /*
+ * deauth frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
+
+ vap->iv_stats.is_rx_deauth++;
+ vap->iv_stats.is_rx_deauth_code = reason;
+ IEEE80211_NODE_STAT(ni, rx_deauth);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_AUTH, ni,
+ "recv deauthenticate (reason %d)", reason);
+ ieee80211_new_state(vap, IEEE80211_S_AUTH,
+ (reason << 8) | IEEE80211_FC0_SUBTYPE_DEAUTH);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_DISASSOC: {
+ uint16_t reason;
+
+ if (vap->iv_state != IEEE80211_S_RUN &&
+ vap->iv_state != IEEE80211_S_ASSOC &&
+ vap->iv_state != IEEE80211_S_AUTH) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ }
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) {
+ /* NB: can happen when in promiscuous mode */
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+
+ /*
+ * disassoc frame format
+ * [2] reason
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 2, return);
+ reason = le16toh(*(uint16_t *)frm);
+
+ vap->iv_stats.is_rx_disassoc++;
+ vap->iv_stats.is_rx_disassoc_code = reason;
+ IEEE80211_NODE_STAT(ni, rx_disassoc);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+ "recv disassociate (reason %d)", reason);
+ ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0);
+ break;
+ }
+
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (vap->iv_state == IEEE80211_S_RUN) {
+ if (ieee80211_parse_action(ni, m0) == 0)
+ ic->ic_recv_action(ni, frm, efrm);
+ } else
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ vap->iv_stats.is_rx_mgtdiscard++;
+ return;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+#undef ISREASSOC
+#undef ISPROBE
+}
diff --git a/sys/net80211/ieee80211_sta.h b/sys/net80211/ieee80211_sta.h
new file mode 100644
index 0000000..1508a7c
--- /dev/null
+++ b/sys/net80211/ieee80211_sta.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_STA_H_
+#define _NET80211_IEEE80211_STA_H_
+
+/*
+ * Station-mode implementation definitions.
+ */
+void ieee80211_sta_attach(struct ieee80211com *);
+void ieee80211_sta_detach(struct ieee80211com *);
+void ieee80211_sta_vattach(struct ieee80211vap *);
+#endif /* !_NET80211_IEEE80211_STA_H_ */
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index 3171cef..5ada0a2 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,9 +31,6 @@
/*
* Definitions for IEEE 802.11 drivers.
*/
-#define IEEE80211_DEBUG
-#undef IEEE80211_DEBUG_REFCNT /* node refcnt stuff */
-
/* NB: portability glue must go first */
#ifdef __NetBSD__
#include <net80211/ieee80211_netbsd.h>
@@ -48,6 +45,7 @@
#include <net80211/_ieee80211.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_dfs.h>
#include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */
#include <net80211/ieee80211_node.h>
#include <net80211/ieee80211_power.h>
@@ -75,8 +73,8 @@
#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */
#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */
-#define IEEE80211_FIXED_RATE_NONE -1
-#define IEEE80211_MCAST_RATE_DEFAULT (2*1) /* default mcast rate (1M) */
+#define IEEE80211_FIXED_RATE_NONE 0xff
+#define IEEE80211_TXMAX_DEFAULT 6 /* default ucast max retries */
#define IEEE80211_RTS_DEFAULT IEEE80211_RTS_MAX
#define IEEE80211_FRAG_DEFAULT IEEE80211_FRAG_MAX
@@ -85,40 +83,57 @@
#define IEEE80211_TU_TO_MS(x) (((x) * 1024) / 1000)
#define IEEE80211_TU_TO_TICKS(x)(((x) * 1024 * hz) / (1000 * 1000))
-struct ieee80211_aclator;
-struct sysctl_ctx_list;
+/*
+ * 802.11 control state is split into a common portion that maps
+ * 1-1 to a physical device and one or more "Virtual AP's" (VAP)
+ * that are bound to an ieee80211com instance and share a single
+ * underlying device. Each VAP has a corresponding OS device
+ * entity through which traffic flows and that applications use
+ * for issuing ioctls, etc.
+ */
+
+/*
+ * Data common to one or more virtual AP's. State shared by
+ * the underlying device and the net80211 layer is exposed here;
+ * e.g. device-specific callbacks.
+ */
+struct ieee80211vap;
+typedef void (*ieee80211vap_attach)(struct ieee80211vap *);
+
+struct ieee80211_appie {
+ uint16_t ie_len; /* size of ie_data */
+ uint8_t ie_data[]; /* user-specified IE's */
+};
struct ieee80211com {
- SLIST_ENTRY(ieee80211com) ic_next;
struct ifnet *ic_ifp; /* associated device */
ieee80211_com_lock_t ic_comlock; /* state update lock */
- ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */
+ TAILQ_HEAD(, ieee80211vap) ic_vaps; /* list of vap instances */
struct ieee80211_stats ic_stats; /* statistics */
- struct sysctl_ctx_list *ic_sysctl; /* dynamic sysctl context */
- uint32_t ic_debug; /* debug msg flags */
- int ic_vap; /* virtual AP index */
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 */
uint8_t ic_myaddr[IEEE80211_ADDR_LEN];
+ struct callout ic_inact; /* inactivity processing */
+ struct task ic_parent_task; /* deferred parent processing */
uint32_t ic_flags; /* state flags */
uint32_t ic_flags_ext; /* extended state flags */
uint32_t ic_flags_ven; /* vendor state flags */
uint32_t ic_caps; /* capabilities */
uint32_t ic_htcaps; /* HT capabilities */
+ uint32_t ic_cryptocaps; /* crypto capabilities */
uint8_t ic_modecaps[2]; /* set of mode capabilities */
- uint16_t ic_curmode; /* current mode */
- struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+ uint8_t ic_promisc; /* vap's needing promisc mode */
+ uint8_t ic_allmulti; /* vap's needing all multicast*/
+ uint8_t ic_nrunning; /* vap's marked running */
+ uint8_t ic_curmode; /* current mode */
uint16_t ic_bintval; /* beacon interval */
uint16_t ic_lintval; /* listen interval */
uint16_t ic_holdover; /* PM hold over duration */
uint16_t ic_txpowlimit; /* global tx power limit */
- int ic_ampdu_rxmax; /* A-MPDU rx limit (bytes) */
- int ic_ampdu_density;/* A-MPDU density */
- int ic_ampdu_limit; /* A-MPDU tx limit (bytes) */
- int ic_amsdu_limit; /* A-MSDU tx limit (bytes) */
+ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
/*
* Channel state:
@@ -148,30 +163,27 @@ struct ieee80211com {
struct ieee80211_channel *ic_curchan; /* current channel */
struct ieee80211_channel *ic_bsschan; /* bss channel */
struct ieee80211_channel *ic_prevchan; /* previous channel */
- int ic_countrycode; /* ISO country code */
- uint16_t ic_regdomain; /* regulatory domain */
- uint8_t ic_location; /* unknown, indoor, outdoor */
+ struct ieee80211_regdomain ic_regdomain;/* regulatory data */
+ struct ieee80211_appie *ic_countryie; /* calculated country ie */
+ struct ieee80211_channel *ic_countryie_chan;
+
+ /* 802.11h/DFS state */
+ struct ieee80211_channel *ic_csa_newchan;/* channel for doing CSA */
+ int ic_csa_count; /* count for doing CSA */
+ struct ieee80211_dfs_state ic_dfs; /* DFS state */
struct ieee80211_scan_state *ic_scan; /* scan state */
- enum ieee80211_roamingmode ic_roaming; /* roaming mode */
int ic_lastdata; /* time of last data frame */
int ic_lastscan; /* time last scan completed */
- int ic_des_nssid; /* # desired ssids */
- struct ieee80211_scan_ssid ic_des_ssid[1];/* desired ssid table */
- uint8_t ic_des_bssid[IEEE80211_ADDR_LEN];
- struct ieee80211_channel *ic_des_chan; /* desired channel */
- int ic_des_mode; /* desired phymode */
- u_int ic_bgscanidle; /* bg scan idle threshold */
- u_int ic_bgscanintvl; /* bg scan min interval */
- u_int ic_scanvalid; /* scan cache valid threshold */
- struct ieee80211_roam ic_roam; /* sta-mode roaming state */
+ /* NB: this is the union of all vap stations/neighbors */
+ int ic_max_keyix; /* max h/w key index */
struct ieee80211_node_table ic_sta; /* stations/neighbors */
+ /* XXX multi-bss: split out common/vap parts */
struct ieee80211_wme_state ic_wme; /* WME/WMM state */
- const struct ieee80211_aclator *ic_acl; /* aclator glue */
- void *ic_as; /* private aclator state */
+ /* XXX multi-bss: can per-vap be done/make sense? */
enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */
uint16_t ic_nonerpsta; /* # non-ERP stations */
uint16_t ic_longslotsta; /* # long slot time stations */
@@ -183,91 +195,56 @@ struct ieee80211com {
int ic_lastnonerp; /* last time non-ERP sta noted*/
int ic_lastnonht; /* last time non-HT sta noted */
- struct ifqueue ic_mgtq;
- enum ieee80211_state ic_state; /* 802.11 state */
- struct callout ic_mgtsend; /* mgmt frame response timer */
- uint32_t *ic_aid_bitmap; /* association id map */
- uint16_t ic_max_aid;
- uint16_t ic_ps_sta; /* stations in power save */
- uint16_t ic_ps_pending; /* ps sta's w/ pending frames */
- uint8_t *ic_tim_bitmap; /* power-save stations w/ data*/
- uint16_t ic_tim_len; /* ic_tim_bitmap size (bytes) */
- uint8_t ic_dtim_period; /* DTIM period */
- uint8_t ic_dtim_count; /* DTIM count for last bcn */
- struct bpf_if *ic_rawbpf; /* packet filter structure */
- struct ieee80211_node *ic_bss; /* information for this node */
- int ic_fixed_rate; /* 802.11 rate or -1 */
- int ic_mcast_rate; /* rate for mcast frames */
- uint16_t ic_rtsthreshold;
- uint16_t ic_fragthreshold;
- uint8_t ic_bmissthreshold;
- uint8_t ic_bmiss_count; /* current beacon miss count */
- int ic_bmiss_max; /* max bmiss before scan */
- uint16_t ic_swbmiss_count;/* beacons in last period */
- uint16_t ic_swbmiss_period;/* s/w bmiss period */
- struct callout ic_swbmiss; /* s/w beacon miss timer */
-
- uint16_t ic_txmin; /* min tx retry count */
- uint16_t ic_txmax; /* max tx retry count */
- uint16_t ic_txlifetime; /* tx lifetime */
- struct callout ic_inact; /* inactivity timer wait */
- void *ic_opt_ie; /* user-specified IE's */
- uint16_t ic_opt_ie_len; /* length of ni_opt_ie */
- int ic_inact_init; /* initial setting */
- int ic_inact_auth; /* auth but not assoc setting */
- int ic_inact_run; /* authorized setting */
- int ic_inact_probe; /* inactive probe time */
-
- /*
- * Cipher state/configuration.
- */
- struct ieee80211_crypto_state ic_crypto;
-#define ic_nw_keys ic_crypto.cs_nw_keys /* XXX compatibility */
-#define ic_def_txkey ic_crypto.cs_def_txkey /* XXX compatibility */
- /*
- * 802.1x glue. When an authenticator attaches it
- * fills in this section. We assume that when ic_ec
- * is setup that the methods are safe to call.
- */
- const struct ieee80211_authenticator *ic_auth;
- struct eapolcom *ic_ec;
-
+ /* virtual ap create/delete */
+ struct ieee80211vap* (*ic_vap_create)(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit,
+ int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+ void (*ic_vap_delete)(struct ieee80211vap *);
+ /* operating mode attachment */
+ ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX];
+ /* return hardware/radio capabilities */
+ void (*ic_getradiocaps)(struct ieee80211com *,
+ int *, struct ieee80211_channel []);
+ /* check and/or prepare regdomain state change */
+ int (*ic_setregdomain)(struct ieee80211com *,
+ struct ieee80211_regdomain *,
+ int, struct ieee80211_channel []);
/* send/recv 802.11 management frame */
- int (*ic_send_mgmt)(struct ieee80211com *,
- struct ieee80211_node *, int, int);
- void (*ic_recv_mgmt)(struct ieee80211com *,
- struct mbuf *, struct ieee80211_node *,
- int, int, int, uint32_t);
+ int (*ic_send_mgmt)(struct ieee80211_node *,
+ int, int);
/* send raw 802.11 frame */
int (*ic_raw_xmit)(struct ieee80211_node *,
struct mbuf *,
const struct ieee80211_bpf_params *);
- /* reset device state after 802.11 parameter/state change */
- int (*ic_reset)(struct ifnet *);
- /* [schedule] beacon frame update */
- void (*ic_update_beacon)(struct ieee80211com *, int);
/* update device state for 802.11 slot time change */
void (*ic_updateslot)(struct ifnet *);
+ /* handle multicast state changes */
+ void (*ic_update_mcast)(struct ifnet *);
+ /* handle promiscuous mode changes */
+ void (*ic_update_promisc)(struct ifnet *);
/* new station association callback/notification */
void (*ic_newassoc)(struct ieee80211_node *, int);
/* node state management */
- struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*);
+ struct ieee80211_node* (*ic_node_alloc)(struct ieee80211_node_table *);
void (*ic_node_free)(struct ieee80211_node *);
void (*ic_node_cleanup)(struct ieee80211_node *);
+ void (*ic_node_age)(struct ieee80211_node *);
+ void (*ic_node_drain)(struct ieee80211_node *);
int8_t (*ic_node_getrssi)(const struct ieee80211_node*);
void (*ic_node_getsignal)(const struct ieee80211_node*,
int8_t *, int8_t *);
+ void (*ic_node_getmimoinfo)(
+ const struct ieee80211_node*,
+ struct ieee80211_mimo_info *);
/* scanning support */
void (*ic_scan_start)(struct ieee80211com *);
void (*ic_scan_end)(struct ieee80211com *);
void (*ic_set_channel)(struct ieee80211com *);
- void (*ic_scan_curchan)(struct ieee80211com *,
+ void (*ic_scan_curchan)(struct ieee80211_scan_state *,
unsigned long);
- void (*ic_scan_mindwell)(struct ieee80211com *);
- /* per-vap eventually... */
- int (*ic_newstate)(struct ieee80211com *,
- enum ieee80211_state, int);
- void (*ic_set_tim)(struct ieee80211_node *, int);
+ void (*ic_scan_mindwell)(struct ieee80211_scan_state *);
/*
* 802.11n ADDBA support. A simple/generic implementation
@@ -282,6 +259,9 @@ struct ieee80211com {
int (*ic_send_action)(struct ieee80211_node *,
int category, int action,
uint16_t args[4]);
+ /* check if A-MPDU should be enabled this station+ac */
+ int (*ic_ampdu_enable)(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *);
/* start/stop doing A-MPDU tx aggregation for a station */
int (*ic_addba_request)(struct ieee80211_node *,
struct ieee80211_tx_ampdu *,
@@ -294,11 +274,154 @@ struct ieee80211com {
struct ieee80211_tx_ampdu *);
};
+struct ieee80211_aclator;
+
+struct ieee80211vap {
+ struct ifmedia iv_media; /* interface media config */
+ struct ifnet *iv_ifp; /* associated device */
+ struct bpf_if *iv_rawbpf; /* packet filter structure */
+ struct sysctl_ctx_list *iv_sysctl; /* dynamic sysctl context */
+ struct sysctl_oid *iv_oid; /* net.wlan.X sysctl oid */
+
+ TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */
+ struct ieee80211com *iv_ic; /* back ptr to common state */
+ uint32_t iv_debug; /* debug msg flags */
+ struct ieee80211_stats iv_stats; /* statistics */
+
+ uint8_t iv_myaddr[IEEE80211_ADDR_LEN];
+ uint32_t iv_flags; /* state flags */
+ uint32_t iv_flags_ext; /* extended state flags */
+ uint32_t iv_flags_ven; /* vendor state flags */
+ uint32_t iv_caps; /* capabilities */
+ uint32_t iv_htcaps; /* HT capabilities */
+ enum ieee80211_opmode iv_opmode; /* operation mode */
+ enum ieee80211_state iv_state; /* state machine state */
+ void (*iv_newstate_cb)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ struct callout iv_mgtsend; /* mgmt frame response timer */
+ /* inactivity timer settings */
+ int iv_inact_init; /* setting for new station */
+ int iv_inact_auth; /* auth but not assoc setting */
+ int iv_inact_run; /* authorized setting */
+ int iv_inact_probe; /* inactive probe time */
+
+ int iv_des_nssid; /* # desired ssids */
+ struct ieee80211_scan_ssid iv_des_ssid[1];/* desired ssid table */
+ uint8_t iv_des_bssid[IEEE80211_ADDR_LEN];
+ struct ieee80211_channel *iv_des_chan; /* desired channel */
+ uint16_t iv_des_mode; /* desired mode */
+ int iv_nicknamelen; /* XXX junk */
+ uint8_t iv_nickname[IEEE80211_NWID_LEN];
+ u_int iv_bgscanidle; /* bg scan idle threshold */
+ u_int iv_bgscanintvl; /* bg scan min interval */
+ u_int iv_scanvalid; /* scan cache valid threshold */
+ u_int iv_scanreq_duration;
+ u_int iv_scanreq_mindwell;
+ u_int iv_scanreq_maxdwell;
+ uint16_t iv_scanreq_flags;/* held scan request params */
+ uint8_t iv_scanreq_nssid;
+ struct ieee80211_scan_ssid iv_scanreq_ssid[IEEE80211_SCAN_MAX_SSID];
+ /* sta-mode roaming state */
+ enum ieee80211_roamingmode iv_roaming; /* roaming mode */
+ struct ieee80211_roamparam iv_roamparms[IEEE80211_MODE_MAX];
+
+ uint8_t iv_bmissthreshold;
+ uint8_t iv_bmiss_count; /* current beacon miss count */
+ int iv_bmiss_max; /* max bmiss before scan */
+ uint16_t iv_swbmiss_count;/* beacons in last period */
+ uint16_t iv_swbmiss_period;/* s/w bmiss period */
+ struct callout iv_swbmiss; /* s/w beacon miss timer */
+
+ int iv_ampdu_rxmax; /* A-MPDU rx limit (bytes) */
+ int iv_ampdu_density;/* A-MPDU density */
+ int iv_ampdu_limit; /* A-MPDU tx limit (bytes) */
+ int iv_amsdu_limit; /* A-MSDU tx limit (bytes) */
+ u_int iv_ampdu_mintraffic[WME_NUM_AC];
+
+ uint32_t *iv_aid_bitmap; /* association id map */
+ uint16_t iv_max_aid;
+ uint16_t iv_sta_assoc; /* stations associated */
+ uint16_t iv_ps_sta; /* stations in power save */
+ uint16_t iv_ps_pending; /* ps sta's w/ pending frames */
+ uint16_t iv_txseq; /* mcast xmit seq# space */
+ uint16_t iv_tim_len; /* ic_tim_bitmap size (bytes) */
+ uint8_t *iv_tim_bitmap; /* power-save stations w/ data*/
+ uint8_t iv_dtim_period; /* DTIM period */
+ uint8_t iv_dtim_count; /* DTIM count from last bcn */
+ /* set/unset aid pwrsav state */
+ int iv_csa_count; /* count for doing CSA */
+
+ struct ieee80211_node *iv_bss; /* information for this node */
+ struct ieee80211_txparam iv_txparms[IEEE80211_MODE_MAX];
+ uint16_t iv_rtsthreshold;
+ uint16_t iv_fragthreshold;
+ int iv_inact_timer; /* inactivity timer wait */
+ /* application-specified IE's to attach to mgt frames */
+ struct ieee80211_appie *iv_appie_beacon;
+ struct ieee80211_appie *iv_appie_probereq;
+ struct ieee80211_appie *iv_appie_proberesp;
+ struct ieee80211_appie *iv_appie_assocreq;
+ struct ieee80211_appie *iv_appie_assocresp;
+ struct ieee80211_appie *iv_appie_wpa;
+ uint8_t *iv_wpa_ie;
+ uint8_t *iv_rsn_ie;
+ uint16_t iv_max_keyix; /* max h/w key index */
+ ieee80211_keyix iv_def_txkey; /* default/group tx key index */
+ struct ieee80211_key iv_nw_keys[IEEE80211_WEP_NKID];
+ int (*iv_key_alloc)(struct ieee80211vap *,
+ const struct ieee80211_key *,
+ ieee80211_keyix *, ieee80211_keyix *);
+ int (*iv_key_delete)(struct ieee80211vap *,
+ const struct ieee80211_key *);
+ int (*iv_key_set)(struct ieee80211vap *,
+ const struct ieee80211_key *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+ void (*iv_key_update_begin)(struct ieee80211vap *);
+ void (*iv_key_update_end)(struct ieee80211vap *);
+
+ const struct ieee80211_authenticator *iv_auth; /* authenticator glue */
+ void *iv_ec; /* private auth state */
+
+ const struct ieee80211_aclator *iv_acl; /* acl glue */
+ void *iv_as; /* private aclator state */
+
+ /* operate-mode detach hook */
+ void (*iv_opdetach)(struct ieee80211vap *);
+ /* receive processing */
+ int (*iv_input)(struct ieee80211_node *,
+ struct mbuf *, int rssi, int noise,
+ uint32_t rstamp);
+ void (*iv_recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int, int, int, uint32_t);
+ void (*iv_deliver_data)(struct ieee80211vap *,
+ struct ieee80211_node *, struct mbuf *);
+#if 0
+ /* send processing */
+ int (*iv_send_mgmt)(struct ieee80211_node *,
+ int, int);
+#endif
+ /* beacon miss processing */
+ void (*iv_bmiss)(struct ieee80211vap *);
+ /* reset device state after 802.11 parameter/state change */
+ int (*iv_reset)(struct ieee80211vap *, u_long);
+ /* [schedule] beacon frame update */
+ void (*iv_update_beacon)(struct ieee80211vap *, int);
+ /* power save handling */
+ void (*iv_update_ps)(struct ieee80211vap *, int);
+ int (*iv_set_tim)(struct ieee80211_node *, int);
+ /* state machine processing */
+ int (*iv_newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ /* 802.3 output method for raw frame xmit */
+ int (*iv_output)(struct ifnet *, struct mbuf *,
+ struct sockaddr *, struct rtentry *);
+};
+MALLOC_DECLARE(M_80211_VAP);
+
#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN)
-/* ic_flags */
-/* NB: bits 0x4c available */
+/* ic_flags/iv_flags */
#define IEEE80211_F_TURBOP 0x00000001 /* CONF: ATH Turbo enabled*/
#define IEEE80211_F_COMP 0x00000002 /* CONF: ATH comp enabled */
#define IEEE80211_F_FF 0x00000004 /* CONF: ATH FF enabled */
@@ -322,6 +445,7 @@ struct ieee80211com {
#define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */
#define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */
#define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/
+#define IEEE80211_F_CSAPENDING 0x00400000 /* STATUS: chan switch pending*/
#define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */
#define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */
#define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */
@@ -329,22 +453,32 @@ struct ieee80211com {
#define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */
#define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */
#define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */
+#define IEEE80211_F_PCF 0x20000000 /* CONF: PCF enabled */
#define IEEE80211_F_DOTH 0x40000000 /* CONF: 11h enabled */
+#define IEEE80211_F_DWDS 0x80000000 /* CONF: Dynamic WDS enabled */
/* Atheros protocol-specific flags */
#define IEEE80211_F_ATHEROS \
(IEEE80211_F_FF | IEEE80211_F_COMP | IEEE80211_F_TURBOP)
/* Check if an Atheros capability was negotiated for use */
-#define IEEE80211_ATH_CAP(ic, ni, bit) \
- ((ic)->ic_flags & (ni)->ni_ath_flags & (bit))
+#define IEEE80211_ATH_CAP(vap, ni, bit) \
+ ((vap)->iv_flags & (ni)->ni_ath_flags & (bit))
-/* ic_flags_ext */
+/* ic_flags_ext/iv_flags_ext */
#define IEEE80211_FEXT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */
#define IEEE80211_FEXT_INACT 0x00000002 /* CONF: sta inact handling */
+#define IEEE80211_FEXT_SCANWAIT 0x00000004 /* STATUS: awaiting scan */
/* 0x00000006 reserved */
#define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: complete bgscan */
+#define IEEE80211_FEXT_WPS 0x00000010 /* CONF: WPS enabled */
+#define IEEE80211_FEXT_TSN 0x00000020 /* CONF: TSN enabled */
+#define IEEE80211_FEXT_SCANREQ 0x00000040 /* STATUS: scan req params */
+#define IEEE80211_FEXT_DFS 0x00000800 /* CONF: DFS enabled */
#define IEEE80211_FEXT_NONERP_PR 0x00000200 /* STATUS: non-ERP sta present*/
#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */
+#define IEEE80211_FEXT_DOTD 0x00001000 /* CONF: 11d enabled */
+/* NB: immutable: should be set only when creating a vap */
+#define IEEE80211_FEXT_WDSLEGACY 0x00010000 /* CONF: legacy WDS operation */
#define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/
#define IEEE80211_FEXT_HT 0x00080000 /* CONF: HT supported */
#define IEEE80211_FEXT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */
@@ -357,12 +491,8 @@ struct ieee80211com {
#define IEEE80211_FEXT_SHORTGI40 0x08000000 /* CONF: short GI in HT40 */
#define IEEE80211_FEXT_HTCOMPAT 0x10000000 /* CONF: HT vendor OUI's */
-/* ic_caps */
-#define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */
-#define IEEE80211_C_TKIP 0x00000002 /* CAPABILITY: TKIP available */
-#define IEEE80211_C_AES 0x00000004 /* CAPABILITY: AES OCB avail */
-#define IEEE80211_C_AES_CCM 0x00000008 /* CAPABILITY: AES CCM avail */
-#define IEEE80211_C_CKIP 0x00000020 /* CAPABILITY: CKIP available */
+/* ic_caps/iv_caps: device driver capabilities */
+/* 0x2f available */
#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 */
@@ -374,7 +504,7 @@ struct ieee80211com {
#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_TKIPMIC 0x00020000 /* CAPABILITY: TKIP MIC avail */
+/* 0x20000 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*/
@@ -386,10 +516,8 @@ struct ieee80211com {
#define IEEE80211_C_TXFRAG 0x40000000 /* CAPABILITY: tx fragments */
/* XXX protection/barker? */
-#define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */
-
/*
- * ic_htcaps: HT-specific device/driver capabilities
+ * 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.
@@ -401,18 +529,23 @@ struct ieee80211com {
void ieee80211_ifattach(struct ieee80211com *);
void ieee80211_ifdetach(struct ieee80211com *);
+int ieee80211_vap_setup(struct ieee80211com *, struct ieee80211vap *,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t macaddr[IEEE80211_ADDR_LEN]);
+int ieee80211_vap_attach(struct ieee80211vap *,
+ ifm_change_cb_t, ifm_stat_cb_t);
+void ieee80211_vap_detach(struct ieee80211vap *);
const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic,
const struct ieee80211_channel *);
void ieee80211_announce(struct ieee80211com *);
void ieee80211_announce_channels(struct ieee80211com *);
-void ieee80211_media_init(struct ieee80211com *,
- ifm_change_cb_t, ifm_stat_cb_t);
+void ieee80211_drain(struct ieee80211com *);
+void ieee80211_media_init(struct ieee80211com *);
struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]);
int ieee80211_media_change(struct ifnet *);
void ieee80211_media_status(struct ifnet *, struct ifmediareq *);
-int ieee80211_ioctl(struct ieee80211com *, u_long, caddr_t);
-int ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t);
-int ieee80211_cfgset(struct ieee80211com *, u_long, caddr_t);
+int ieee80211_ioctl(struct ifnet *, u_long, caddr_t);
int ieee80211_rate2media(struct ieee80211com *, int,
enum ieee80211_phymode);
int ieee80211_media2rate(int);
@@ -431,14 +564,14 @@ enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *);
* Key update synchronization methods. XXX should not be visible.
*/
static __inline void
-ieee80211_key_update_begin(struct ieee80211com *ic)
+ieee80211_key_update_begin(struct ieee80211vap *vap)
{
- ic->ic_crypto.cs_key_update_begin(ic);
+ vap->iv_key_update_begin(vap);
}
static __inline void
-ieee80211_key_update_end(struct ieee80211com *ic)
+ieee80211_key_update_end(struct ieee80211vap *vap)
{
- ic->ic_crypto.cs_key_update_end(ic);
+ vap->iv_key_update_end(vap);
}
/*
@@ -472,13 +605,25 @@ ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data)
}
/*
- * Notify a driver that beacon state has been updated.
+ * Notify a vap that beacon state has been updated.
*/
static __inline void
-ieee80211_beacon_notify(struct ieee80211com *ic, int what)
+ieee80211_beacon_notify(struct ieee80211vap *vap, int what)
+{
+ if (vap->iv_state == IEEE80211_S_RUN)
+ vap->iv_update_beacon(vap, what);
+}
+
+/*
+ * Calculate HT channel promotion flags for a channel.
+ * XXX belongs in ieee80211_ht.h but needs IEEE80211_FEXT_*
+ */
+static __inline int
+ieee80211_htchanflags(const struct ieee80211_channel *c)
{
- if (ic->ic_state == IEEE80211_S_RUN)
- ic->ic_update_beacon(ic, what);
+ return IEEE80211_IS_CHAN_HT40(c) ?
+ IEEE80211_FEXT_HT | IEEE80211_FEXT_USEHT40 :
+ IEEE80211_IS_CHAN_HT(c) ? IEEE80211_FEXT_HT : 0;
}
/*
@@ -525,44 +670,44 @@ ieee80211_beacon_notify(struct ieee80211com *ic, int what)
#define IEEE80211_MSG_ANY 0xffffffff /* anything */
#ifdef IEEE80211_DEBUG
-#define ieee80211_msg(_ic, _m) ((_ic)->ic_debug & (_m))
-#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note(_ic, _fmt, __VA_ARGS__); \
+#define ieee80211_msg(_vap, _m) ((_vap)->iv_debug & (_m))
+#define IEEE80211_DPRINTF(_vap, _m, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note(_vap, _fmt, __VA_ARGS__); \
} while (0)
-#define IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note_mac(_ic, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\
+#define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note_mac(_vap, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\
} while (0)
-#define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note_mac(_ic, _mac, _fmt, __VA_ARGS__); \
+#define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note_mac(_vap, _mac, _fmt, __VA_ARGS__); \
} while (0)
-#define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...) do { \
- if (ieee80211_msg(_ic, _m)) \
- ieee80211_note_frame(_ic, _wh, _fmt, __VA_ARGS__); \
+#define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...) do { \
+ if (ieee80211_msg(_vap, _m)) \
+ ieee80211_note_frame(_vap, _wh, _fmt, __VA_ARGS__); \
} while (0)
-void ieee80211_note(struct ieee80211com *ic, const char *fmt, ...);
-void ieee80211_note_mac(struct ieee80211com *ic,
- const uint8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...);
-void ieee80211_note_frame(struct ieee80211com *ic,
- const struct ieee80211_frame *wh, const char *fmt, ...);
-#define ieee80211_msg_debug(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_DEBUG)
-#define ieee80211_msg_dumppkts(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_DUMPPKTS)
-#define ieee80211_msg_input(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_INPUT)
-#define ieee80211_msg_radius(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_RADIUS)
-#define ieee80211_msg_dumpradius(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_RADDUMP)
-#define ieee80211_msg_dumpradkeys(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_RADKEYS)
-#define ieee80211_msg_scan(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_SCAN)
-#define ieee80211_msg_assoc(_ic) \
- ((_ic)->ic_debug & IEEE80211_MSG_ASSOC)
+void ieee80211_note(struct ieee80211vap *, const char *, ...);
+void ieee80211_note_mac(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN], const char *, ...);
+void ieee80211_note_frame(struct ieee80211vap *,
+ const struct ieee80211_frame *, const char *, ...);
+#define ieee80211_msg_debug(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_DEBUG)
+#define ieee80211_msg_dumppkts(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_DUMPPKTS)
+#define ieee80211_msg_input(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_INPUT)
+#define ieee80211_msg_radius(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_RADIUS)
+#define ieee80211_msg_dumpradius(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_RADDUMP)
+#define ieee80211_msg_dumpradkeys(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_RADKEYS)
+#define ieee80211_msg_scan(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_SCAN)
+#define ieee80211_msg_assoc(_vap) \
+ ((_vap)->iv_debug & IEEE80211_MSG_ASSOC)
/*
* Emit a debug message about discarding a frame or information
@@ -570,37 +715,37 @@ void ieee80211_note_frame(struct ieee80211com *ic,
* the frame header; the other is for when a header is not
* available or otherwise appropriate.
*/
-#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do { \
- if ((_ic)->ic_debug & (_m)) \
- ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\
+#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) do { \
+ if ((_vap)->iv_debug & (_m)) \
+ ieee80211_discard_frame(_vap, _wh, _type, _fmt, __VA_ARGS__);\
} while (0)
-#define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do { \
- if ((_ic)->ic_debug & (_m)) \
- ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\
+#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) do { \
+ if ((_vap)->iv_debug & (_m)) \
+ ieee80211_discard_ie(_vap, _wh, _type, _fmt, __VA_ARGS__);\
} while (0)
-#define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do { \
- if ((_ic)->ic_debug & (_m)) \
- ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\
+#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) do { \
+ if ((_vap)->iv_debug & (_m)) \
+ ieee80211_discard_mac(_vap, _mac, _type, _fmt, __VA_ARGS__);\
} while (0)
-void ieee80211_discard_frame(struct ieee80211com *,
+void ieee80211_discard_frame(struct ieee80211vap *,
const struct ieee80211_frame *, const char *type, const char *fmt, ...);
-void ieee80211_discard_ie(struct ieee80211com *,
+void ieee80211_discard_ie(struct ieee80211vap *,
const struct ieee80211_frame *, const char *type, const char *fmt, ...);
-void ieee80211_discard_mac(struct ieee80211com *,
+void ieee80211_discard_mac(struct ieee80211vap *,
const uint8_t mac[IEEE80211_ADDR_LEN], const char *type,
const char *fmt, ...);
#else
-#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...)
-#define IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...)
-#define IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...)
-#define IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...)
-#define ieee80211_msg_dumppkts(_ic) 0
-#define ieee80211_msg(_ic, _m) 0
-
-#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...)
-#define IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...)
-#define IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...)
+#define IEEE80211_DPRINTF(_vap, _m, _fmt, ...)
+#define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...)
+#define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...)
+#define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...)
+#define ieee80211_msg_dumppkts(_vap) 0
+#define ieee80211_msg(_vap, _m) 0
+
+#define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...)
+#define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...)
+#define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...)
#endif
#endif /* _NET80211_IEEE80211_VAR_H_ */
diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c
new file mode 100644
index 0000000..f3d90f9
--- /dev/null
+++ b/sys/net80211/ieee80211_wds.c
@@ -0,0 +1,865 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 WDS mode support.
+ */
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_llc.h>
+#include <net/ethernet.h>
+
+#include <net/bpf.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_wds.h>
+#include <net80211/ieee80211_input.h>
+
+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 rssi, int noise, uint32_t rstamp);
+static void wds_recv_mgmt(struct ieee80211_node *, struct mbuf *,
+ int subtype, int rssi, int noise, u_int32_t rstamp);
+
+void
+ieee80211_wds_attach(struct ieee80211com *ic)
+{
+ ic->ic_vattach[IEEE80211_M_WDS] = wds_vattach;
+}
+
+void
+ieee80211_wds_detach(struct ieee80211com *ic)
+{
+}
+
+static void
+wds_vdetach(struct ieee80211vap *vap)
+{
+ if (vap->iv_bss != NULL) {
+ /* XXX locking? */
+ if (vap->iv_bss->ni_wdsvap == vap)
+ vap->iv_bss->ni_wdsvap = NULL;
+ }
+}
+
+static void
+wds_vattach(struct ieee80211vap *vap)
+{
+ vap->iv_newstate = wds_newstate;
+ vap->iv_input = wds_input;
+ vap->iv_recv_mgmt = wds_recv_mgmt;
+ vap->iv_opdetach = wds_vdetach;
+}
+
+static int
+ieee80211_create_wds(struct ieee80211vap *vap, struct ieee80211_channel *chan)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node *ni, *obss;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
+ "%s: creating link to %s on channel %u\n", __func__,
+ ether_sprintf(vap->iv_des_bssid), ieee80211_chan2ieee(ic, chan));
+
+ /* NB: vap create must specify the bssid for the link */
+ KASSERT(vap->iv_flags & IEEE80211_F_DESBSSID, ("no bssid"));
+ /* NB: we should only be called on RUN transition */
+ KASSERT(vap->iv_state == IEEE80211_S_RUN, ("!RUN state"));
+
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
+ /*
+ * Dynamic/non-legacy WDS. Reference the associated
+ * station specified by the desired bssid setup at vap
+ * create. Point ni_wdsvap at the WDS vap so 4-address
+ * frames received through the associated AP vap will
+ * be dispatched upward (e.g. to a bridge) as though
+ * they arrived on the WDS vap.
+ */
+ IEEE80211_NODE_LOCK(nt);
+ obss = NULL;
+ ni = ieee80211_find_node_locked(&ic->ic_sta, vap->iv_des_bssid);
+ if (ni == NULL) {
+ /*
+ * Node went away before we could hookup. This
+ * should be ok; no traffic will flow and a leave
+ * event will be dispatched that should cause
+ * the vap to be destroyed.
+ */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
+ "%s: station %s went away\n",
+ __func__, ether_sprintf(vap->iv_des_bssid));
+ /* XXX stat? */
+ } else if (ni->ni_wdsvap != NULL) {
+ /*
+ * Node already setup with a WDS vap; we cannot
+ * allow multiple references so disallow. If
+ * ni_wdsvap points at us that's ok; we should
+ * do nothing anyway.
+ */
+ /* XXX printf instead? */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_WDS,
+ "%s: station %s in use with %s\n",
+ __func__, ether_sprintf(vap->iv_des_bssid),
+ ni->ni_wdsvap->iv_ifp->if_xname);
+ /* XXX stat? */
+ } else {
+ /*
+ * Committed to new node, setup state.
+ */
+ obss = vap->iv_bss;
+ vap->iv_bss = ni;
+ ni->ni_wdsvap = vap;
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+ if (obss != NULL) {
+ /* NB: deferred to avoid recursive lock */
+ ieee80211_free_node(obss);
+ }
+ } else {
+ /*
+ * Legacy WDS vap setup.
+ */
+ /*
+ * The far end does not associate so we just create
+ * create a new node and install it as the vap's
+ * bss node. We must simulate an association and
+ * authorize the port for traffic to flow.
+ * XXX check if node already in sta table?
+ */
+ ni = ieee80211_node_create_wds(vap, vap->iv_des_bssid, chan);
+ if (ni != NULL) {
+ obss = vap->iv_bss;
+ vap->iv_bss = ieee80211_ref_node(ni);
+ ni->ni_flags |= IEEE80211_NODE_AREF;
+ if (obss != NULL)
+ ieee80211_free_node(obss);
+ /* give driver a chance to setup state like ni_txrate */
+ if (ic->ic_newassoc != NULL)
+ ic->ic_newassoc(ni, 1);
+ /* tell the authenticator about new station */
+ if (vap->iv_auth->ia_node_join != NULL)
+ vap->iv_auth->ia_node_join(ni);
+ if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ni);
+
+ ieee80211_notify_node_join(ni, 1 /*newassoc*/);
+ /* XXX inject l2uf frame */
+ }
+ }
+
+ /*
+ * Flush pending frames now that were setup.
+ */
+ if (ni != NULL && IEEE80211_NODE_WDSQ_QLEN(ni) != 0) {
+ int8_t rssi, noise;
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "flush wds queue, %u packets queued",
+ IEEE80211_NODE_WDSQ_QLEN(ni));
+ ic->ic_node_getsignal(ni, &rssi, &noise);
+ for (;;) {
+ struct mbuf *m;
+
+ IEEE80211_NODE_WDSQ_LOCK(ni);
+ _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(ni, m);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+ if (m == NULL)
+ break;
+ /* XXX cheat and re-use last rstamp */
+ ieee80211_input(ni, m, rssi, noise, ni->ni_rstamp);
+ }
+ }
+ return (ni == NULL ? ENOENT : 0);
+}
+
+/*
+ * Propagate multicast frames of an ap vap to all DWDS links.
+ * The caller is assumed to have verified this frame is multicast.
+ */
+void
+ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
+{
+ struct ieee80211com *ic = vap0->iv_ic;
+ struct ifnet *parent = ic->ic_ifp;
+ const struct ether_header *eh = mtod(m, const struct ether_header *);
+ struct ieee80211_node *ni;
+ struct ieee80211vap *vap;
+ struct ifnet *ifp;
+ struct mbuf *mcopy;
+ int err;
+
+ KASSERT(ETHER_IS_MULTICAST(eh->ether_dhost),
+ ("%s not mcast", ether_sprintf(eh->ether_dhost)));
+
+ /* XXX locking */
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ /* only DWDS vaps are interesting */
+ if (vap->iv_opmode != IEEE80211_M_WDS ||
+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY))
+ continue;
+ /* if it came in this interface, don't send it back out */
+ ifp = vap->iv_ifp;
+ if (ifp == m->m_pkthdr.rcvif)
+ continue;
+ /*
+ * Duplicate the frame and send it. We don't need
+ * to classify or lookup the tx node; this was already
+ * done by the caller so we can just re-use the info.
+ */
+ mcopy = m_copypacket(m, M_DONTWAIT);
+ if (mcopy == NULL) {
+ ifp->if_oerrors++;
+ /* XXX stat + msg */
+ continue;
+ }
+ ni = ieee80211_find_txnode(vap, eh->ether_dhost);
+ if (ni == NULL) {
+ /* NB: ieee80211_find_txnode does stat+msg */
+ ifp->if_oerrors++;
+ m_freem(mcopy);
+ continue;
+ }
+ if (ieee80211_classify(ni, mcopy)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_OUTPUT | IEEE80211_MSG_WDS,
+ eh->ether_dhost, NULL,
+ "%s", "classification failure");
+ vap->iv_stats.is_tx_classify++;
+ ifp->if_oerrors++;
+ m_freem(mcopy);
+ ieee80211_free_node(ni);
+ continue;
+ }
+ mcopy->m_flags |= M_MCAST | M_WDS;
+ mcopy->m_pkthdr.rcvif = (void *) ni;
+
+ IFQ_HANDOFF(parent, mcopy, err);
+ if (err) {
+ /* NB: IFQ_HANDOFF reclaims mbuf */
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ } else
+ ifp->if_opackets++;
+ }
+}
+
+/*
+ * Handle DWDS discovery on receipt of a 4-address frame in
+ * ap mode. Queue the frame and post an event for someone
+ * to plumb the necessary WDS vap for this station. Frames
+ * received prior to the vap set running will then be reprocessed
+ * as if they were just received.
+ */
+void
+ieee80211_dwds_discover(struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ int qlen, age;
+
+ IEEE80211_NODE_WDSQ_LOCK(ni);
+ if (!_IF_QFULL(&ni->ni_wdsq)) {
+ /*
+ * Tag the frame with it's expiry time and insert
+ * it in the queue. The aging interval is 4 times
+ * the listen interval specified by the station.
+ * Frames that sit around too long are reclaimed
+ * using this information.
+ */
+ /* XXX handle overflow? */
+ /* XXX per/vap beacon interval? */
+ /* NB: TU -> secs */
+ age = ((ni->ni_intval * ic->ic_lintval) << 2) / 1024;
+ _IEEE80211_NODE_WDSQ_ENQUEUE(ni, m, qlen, age);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "save frame, %u now queued", qlen);
+ } else {
+ vap->iv_stats.is_dwds_qdrop++;
+ _IF_DROP(&ni->ni_wdsq);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_WDS,
+ mtod(m, struct ieee80211_frame *), "wds data",
+ "pending q overflow, drops %d (len %d)",
+ ni->ni_wdsq.ifq_drops, ni->ni_wdsq.ifq_len);
+
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_dumppkts(vap))
+ ieee80211_dump_pkt(ic, mtod(m, caddr_t),
+ m->m_len, -1, -1);
+#endif
+ /* XXX tail drop? */
+ m_freem(m);
+ }
+ ieee80211_notify_wds_discover(ni);
+}
+
+/*
+ * Age frames on the WDS pending queue. The aging interval is
+ * 4 times the listen interval specified by the station. This
+ * number is factored into the age calculations when the frame
+ * is placed on the queue. We store ages as time differences
+ * so we can check and/or adjust only the head of the list.
+ * If a frame's age exceeds the threshold then discard it.
+ * The number of frames discarded is returned to the caller.
+ */
+int
+ieee80211_node_wdsq_age(struct ieee80211_node *ni)
+{
+#ifdef IEEE80211_DEBUG
+ struct ieee80211vap *vap = ni->ni_vap;
+#endif
+ struct mbuf *m;
+ int discard = 0;
+
+ IEEE80211_NODE_WDSQ_LOCK(ni);
+ while (_IF_POLL(&ni->ni_wdsq, m) != NULL &&
+ M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "discard frame, age %u", M_AGE_GET(m));
+
+ /* XXX could be optimized */
+ _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(ni, m);
+ m_freem(m);
+ discard++;
+ }
+ if (m != NULL)
+ M_AGE_SUB(m, IEEE80211_INACT_WAIT);
+ IEEE80211_NODE_WDSQ_UNLOCK(ni);
+
+ IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
+ "discard %u frames for age", discard);
+#if 0
+ IEEE80211_NODE_STAT_ADD(ni, wds_discard, discard);
+#endif
+ return discard;
+}
+
+/*
+ * IEEE80211_M_WDS vap state machine handler.
+ */
+static int
+wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+ int error;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ostate = vap->iv_state;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ vap->iv_state = nstate; /* state transition */
+ callout_stop(&vap->iv_mgtsend); /* XXX callout_drain */
+ if (ostate != IEEE80211_S_SCAN)
+ ieee80211_cancel_scan(vap); /* background scan */
+ ni = vap->iv_bss; /* NB: no reference held */
+ if (vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS)
+ callout_stop(&vap->iv_swbmiss);
+ error = 0;
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ switch (ostate) {
+ case IEEE80211_S_SCAN:
+ ieee80211_cancel_scan(vap);
+ break;
+ default:
+ break;
+ }
+ if (ostate != IEEE80211_S_INIT) {
+ /* NB: optimize INIT -> INIT case */
+ ieee80211_reset_bss(vap);
+ }
+ break;
+ case IEEE80211_S_SCAN:
+ switch (ostate) {
+ case IEEE80211_S_INIT:
+ ieee80211_check_scan_current(vap);
+ break;
+ default:
+ break;
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (ostate == IEEE80211_S_INIT) {
+ /*
+ * Already have a channel; bypass the scan
+ * and startup immediately.
+ */
+ error = ieee80211_create_wds(vap, ic->ic_curchan);
+ }
+ break;
+ default:
+ break;
+ }
+ return error;
+}
+
+/*
+ * Process a received frame. The node associated with the sender
+ * should be supplied. If nothing was found in the node table then
+ * the caller is assumed to supply a reference to iv_bss instead.
+ * The RSSI and a timestamp are also supplied. The RSSI data is used
+ * during AP scanning to select a AP to associate with; it can have
+ * any units so long as values have consistent units and higher values
+ * mean ``better signal''. The receive timestamp is currently not used
+ * by the 802.11 layer.
+ */
+static int
+wds_input(struct ieee80211_node *ni, struct mbuf *m,
+ int rssi, int noise, uint32_t rstamp)
+{
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define HAS_SEQ(type) ((type & 0x4) == 0)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = vap->iv_ifp;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+ int hdrspace, need_tap;
+ uint8_t dir, type, subtype, qos;
+ uint16_t rxseq;
+
+ if (m->m_flags & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* XXX optimize? */
+ goto resubmit_ampdu;
+ }
+
+ KASSERT(ni != NULL, ("null node"));
+
+ need_tap = 1; /* mbuf need to be tapped. */
+ type = -1; /* undefined */
+
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "too short (1): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /*
+ * Bit of a cheat here, we use a pointer for a 3-address
+ * frame format but don't reference fields past outside
+ * ieee80211_frame_min w/o first validating the data is
+ * present.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+ IEEE80211_FC0_VERSION_0) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
+ vap->iv_stats.is_rx_badversion++;
+ goto err;
+ }
+
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
+ /* NB: WDS vap's do not scan */
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_addr4)) {
+ IEEE80211_DISCARD_MAC(vap,
+ IEEE80211_MSG_ANY, ni->ni_macaddr, NULL,
+ "too short (3): len %u", m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+ /* NB: the TA is implicitly verified by finding the wds peer node */
+ if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) &&
+ !IEEE80211_ADDR_EQ(wh->i_addr1, ifp->if_broadcastaddr)) {
+ /* not interested in */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ wh->i_addr1, NULL, "%s", "not to bss");
+ vap->iv_stats.is_rx_wrongbss++;
+ goto out;
+ }
+ IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi);
+ ni->ni_noise = noise;
+ ni->ni_rstamp = rstamp;
+ if (HAS_SEQ(type)) {
+ uint8_t tid = ieee80211_gettid(wh);
+ if (IEEE80211_QOS_HAS_SEQ(wh) &&
+ TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ rxseq = le16toh(*(uint16_t *)wh->i_seq);
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+ /* duplicate, discard */
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ wh->i_addr1, "duplicate",
+ "seqno <%u,%u> fragno <%u,%u> tid %u",
+ rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[tid] >> IEEE80211_SEQ_SEQ_SHIFT,
+ rxseq & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxseqs[tid] & IEEE80211_SEQ_FRAG_MASK,
+ tid);
+ vap->iv_stats.is_rx_dup++;
+ IEEE80211_NODE_STAT(ni, rx_dup);
+ goto out;
+ }
+ ni->ni_rxseqs[tid] = rxseq;
+ }
+ switch (type) {
+ case IEEE80211_FC0_TYPE_DATA:
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ if (m->m_len < hdrspace &&
+ (m = m_pullup(m, hdrspace)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, NULL,
+ "data too short: expecting %u", hdrspace);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (dir != IEEE80211_FC1_DIR_DSTODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto out;
+ }
+ /*
+ * Only legacy WDS traffic should take this path.
+ */
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "not legacy wds");
+ vap->iv_stats.is_rx_wrongdir++;/*XXX*/
+ goto out;
+ }
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1))
+ ni->ni_inact = ni->ni_inact_reload;
+ /*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ subtype == IEEE80211_FC0_SUBTYPE_QOS &&
+ ieee80211_ampdu_reorder(ni, m) != 0) {
+ m = NULL;
+ goto out;
+ }
+ resubmit_ampdu:
+
+ /*
+ * Handle privacy requirements. Note that we
+ * must not be preempted from here until after
+ * we (potentially) call ieee80211_crypto_demic;
+ * otherwise we may violate assumptions in the
+ * crypto cipher modules used to do delayed update
+ * of replay sequence numbers.
+ */
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) {
+ /*
+ * Discard encrypted frames when privacy is off.
+ */
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "WEP", "%s", "PRIVACY off");
+ vap->iv_stats.is_rx_noprivacy++;
+ IEEE80211_NODE_STAT(ni, rx_noprivacy);
+ goto out;
+ }
+ key = ieee80211_crypto_decap(ni, m, hdrspace);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
+ goto out;
+ }
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ } else {
+ /* XXX M_WEP and IEEE80211_F_PRIVACY */
+ key = NULL;
+ }
+
+ /*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else
+ qos = 0;
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ni, m, hdrspace);
+ if (m == NULL) {
+ /* Fragment dropped or frame not complete yet */
+ goto out;
+ }
+ }
+ wh = NULL; /* no longer valid, catch any uses */
+
+ /*
+ * Next strip any MSDU crypto bits.
+ */
+ if (key != NULL && !ieee80211_crypto_demic(vap, key, m, 0)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ vap->iv_stats.is_rx_demicfail++;
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
+ }
+
+ /* copy to listener after decrypt */
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ need_tap = 0;
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(vap, m, hdrspace);
+ if (m == NULL) {
+ /* XXX mask bit to check for both */
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA ||
+ subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)
+ goto out;
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "decap error");
+ vap->iv_stats.is_rx_decap++;
+ IEEE80211_NODE_STAT(ni, rx_decap);
+ goto err;
+ }
+ eh = mtod(m, struct ether_header *);
+ if (!ieee80211_node_is_authorized(ni)) {
+ /*
+ * Deny any non-PAE frames received prior to
+ * authorization. For open/shared-key
+ * authentication the port is mark authorized
+ * after authentication completes. For 802.1x
+ * the port is not marked authorized by the
+ * authenticator until the handshake has completed.
+ */
+ if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+ eh->ether_shost, "data",
+ "unauthorized port: ether type 0x%x len %u",
+ eh->ether_type, m->m_pkthdr.len);
+ vap->iv_stats.is_rx_unauth++;
+ IEEE80211_NODE_STAT(ni, rx_unauth);
+ goto err;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((vap->iv_flags & IEEE80211_F_DROPUNENC) &&
+ (key == NULL && (m->m_flags & M_WEP) == 0) &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ vap->iv_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
+ /* XXX require HT? */
+ if (qos & IEEE80211_QOS_AMSDU) {
+ m = ieee80211_decap_amsdu(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ } else if ((ni->ni_ath_flags & IEEE80211_NODE_FF) &&
+#define FF_LLC_SIZE (sizeof(struct ether_header) + sizeof(struct llc))
+ m->m_pkthdr.len >= 3*FF_LLC_SIZE) {
+ struct llc *llc;
+
+ /*
+ * Check for fast-frame tunnel encapsulation.
+ */
+ if (m->m_len < FF_LLC_SIZE &&
+ (m = m_pullup(m, FF_LLC_SIZE)) == NULL) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "fast-frame",
+ "%s", "m_pullup(llc) failed");
+ vap->iv_stats.is_rx_tooshort++;
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ llc = (struct llc *)(mtod(m, uint8_t *) +
+ sizeof(struct ether_header));
+ if (llc->llc_snap.ether_type == htons(ATH_FF_ETH_TYPE)) {
+ m_adj(m, FF_LLC_SIZE);
+ m = ieee80211_decap_fastframe(ni, m);
+ if (m == NULL)
+ return IEEE80211_FC0_TYPE_DATA;
+ }
+ }
+#undef FF_LLC_SIZE
+ ieee80211_deliver_data(vap, ni, m);
+ return IEEE80211_FC0_TYPE_DATA;
+
+ case IEEE80211_FC0_TYPE_MGT:
+ vap->iv_stats.is_rx_mgmt++;
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
+ if (dir != IEEE80211_FC1_DIR_NODS) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, "data", "incorrect dir 0x%x", dir);
+ vap->iv_stats.is_rx_wrongdir++;
+ goto err;
+ }
+ if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY,
+ ni->ni_macaddr, "mgt", "too short: len %u",
+ m->m_pkthdr.len);
+ vap->iv_stats.is_rx_tooshort++;
+ goto out;
+ }
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(vap) || ieee80211_msg_dumppkts(vap)) {
+ if_printf(ifp, "received %s from %s rssi %d\n",
+ ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ether_sprintf(wh->i_addr2), rssi);
+ }
+#endif
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+ wh, NULL, "%s", "WEP set but not permitted");
+ vap->iv_stats.is_rx_mgtdiscard++; /* XXX */
+ goto out;
+ }
+ if (bpf_peers_present(vap->iv_rawbpf))
+ bpf_mtap(vap->iv_rawbpf, m);
+ vap->iv_recv_mgmt(ni, m, subtype, rssi, noise, rstamp);
+ m_freem(m);
+ return IEEE80211_FC0_TYPE_MGT;
+
+ case IEEE80211_FC0_TYPE_CTL:
+ vap->iv_stats.is_rx_ctl++;
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
+ goto out;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "bad", "frame type 0x%x", type);
+ /* should not come here */
+ break;
+ }
+err:
+ ifp->if_ierrors++;
+out:
+ if (m != NULL) {
+ if (bpf_peers_present(vap->iv_rawbpf) && need_tap)
+ bpf_mtap(vap->iv_rawbpf, m);
+ m_freem(m);
+ }
+ return type;
+#undef SEQ_LEQ
+}
+
+static void
+wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, int rssi, int noise, u_int32_t rstamp)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_frame *wh;
+ u_int8_t *frm, *efrm;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ frm = (u_int8_t *)&wh[1];
+ efrm = mtod(m0, u_int8_t *) + m0->m_len;
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_DEAUTH:
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ case IEEE80211_FC0_SUBTYPE_AUTH:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ case IEEE80211_FC0_SUBTYPE_DISASSOC:
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (vap->iv_state != IEEE80211_S_RUN ||
+ IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ vap->iv_stats.is_rx_mgtdiscard++;
+ break;
+ }
+ ni->ni_inact = ni->ni_inact_reload;
+ if (ieee80211_parse_action(ni, m0) == 0)
+ ic->ic_recv_action(ni, frm, efrm);
+ break;
+ default:
+ IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
+ wh, "mgt", "subtype 0x%x not handled", subtype);
+ vap->iv_stats.is_rx_badsubtype++;
+ break;
+ }
+}
diff --git a/sys/net80211/ieee80211_wds.h b/sys/net80211/ieee80211_wds.h
new file mode 100644
index 0000000..c34fb6e
--- /dev/null
+++ b/sys/net80211/ieee80211_wds.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_WDS_H_
+#define _NET80211_IEEE80211_WDS_H_
+
+/*
+ * WDS implementation definitions.
+ */
+void ieee80211_wds_attach(struct ieee80211com *);
+void ieee80211_wds_detach(struct ieee80211com *);
+
+void ieee80211_dwds_mcast(struct ieee80211vap *, struct mbuf *);
+void ieee80211_dwds_discover(struct ieee80211_node *, struct mbuf *);
+int ieee80211_node_wdsq_age(struct ieee80211_node *);
+#endif /* !_NET80211_IEEE80211_WDS_H_ */
diff --git a/sys/net80211/ieee80211_xauth.c b/sys/net80211/ieee80211_xauth.c
index c829b6e..2341ffb 100644
--- a/sys/net80211/ieee80211_xauth.c
+++ b/sys/net80211/ieee80211_xauth.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2004 Video54 Technologies, Inc.
- * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,8 @@ __FBSDID("$FreeBSD$");
* of the available callbacks--the user mode authenticator process works
* entirely from messages about stations joining and leaving.
*/
+#include "opt_wlan.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
@@ -54,6 +56,9 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
+/* XXX number of references from net80211 layer; needed for module code */
+static int nrefs = 0;
+
/*
* One module handles everything for now. May want
* to split things up for embedded applications.
@@ -66,30 +71,6 @@ static const struct ieee80211_authenticator xauth = {
.ia_node_leave = NULL,
};
-/*
- * Module glue.
- */
-static int
-wlan_xauth_modevent(module_t mod, int type, void *unused)
-{
- switch (type) {
- case MOD_LOAD:
- ieee80211_authenticator_register(IEEE80211_AUTH_8021X, &xauth);
- ieee80211_authenticator_register(IEEE80211_AUTH_WPA, &xauth);
- return 0;
- case MOD_UNLOAD:
- ieee80211_authenticator_unregister(IEEE80211_AUTH_8021X);
- ieee80211_authenticator_unregister(IEEE80211_AUTH_WPA);
- return 0;
- }
- return EINVAL;
-}
-
-static moduledata_t wlan_xauth_mod = {
- "wlan_xauth",
- wlan_xauth_modevent,
- 0
-};
-DECLARE_MODULE(wlan_xauth, wlan_xauth_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan_xauth, 1);
-MODULE_DEPEND(wlan_xauth, wlan, 1, 1, 1);
+IEEE80211_AUTH_MODULE(xauth, 1);
+IEEE80211_AUTH_ALG(x8021x, IEEE80211_AUTH_8021X, xauth);
+IEEE80211_AUTH_ALG(wpa, IEEE80211_AUTH_WPA, xauth);
OpenPOWER on IntegriCloud