summaryrefslogtreecommitdiffstats
path: root/sys/net80211
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2004-12-08 17:26:47 +0000
committersam <sam@FreeBSD.org>2004-12-08 17:26:47 +0000
commit2843bf259ec9ca475410e4e9e53e7cd47cd55333 (patch)
tree704c955cb68020dd8237664b417e3ee6141f028b /sys/net80211
parentd9ec783dcb4653bc5f6993ab5f154827f5a58922 (diff)
downloadFreeBSD-src-2843bf259ec9ca475410e4e9e53e7cd47cd55333.zip
FreeBSD-src-2843bf259ec9ca475410e4e9e53e7cd47cd55333.tar.gz
Update 802.11 support; too much new functionality to fully describe
here but it includes completed 802.11g, WPA, 802.11i, 802.1x, WME/WMM, AP-side power-save, crypto plugin framework, authenticator plugin framework, and access control plugin frameowrk.
Diffstat (limited to 'sys/net80211')
-rw-r--r--sys/net80211/_ieee80211.h188
-rw-r--r--sys/net80211/ieee80211.c459
-rw-r--r--sys/net80211/ieee80211.h414
-rw-r--r--sys/net80211/ieee80211_acl.c301
-rw-r--r--sys/net80211/ieee80211_crypto.c701
-rw-r--r--sys/net80211/ieee80211_crypto.h184
-rw-r--r--sys/net80211/ieee80211_crypto_ccmp.c603
-rw-r--r--sys/net80211/ieee80211_crypto_none.c149
-rw-r--r--sys/net80211/ieee80211_crypto_tkip.c987
-rw-r--r--sys/net80211/ieee80211_crypto_wep.c499
-rw-r--r--sys/net80211/ieee80211_freebsd.c338
-rw-r--r--sys/net80211/ieee80211_freebsd.h223
-rw-r--r--sys/net80211/ieee80211_input.c2333
-rw-r--r--sys/net80211/ieee80211_ioctl.c2101
-rw-r--r--sys/net80211/ieee80211_ioctl.h330
-rw-r--r--sys/net80211/ieee80211_node.c1746
-rw-r--r--sys/net80211/ieee80211_node.h255
-rw-r--r--sys/net80211/ieee80211_output.c1326
-rw-r--r--sys/net80211/ieee80211_proto.c759
-rw-r--r--sys/net80211/ieee80211_proto.h192
-rw-r--r--sys/net80211/ieee80211_radiotap.h4
-rw-r--r--sys/net80211/ieee80211_var.h383
-rw-r--r--sys/net80211/ieee80211_xauth.c100
23 files changed, 12388 insertions, 2187 deletions
diff --git a/sys/net80211/_ieee80211.h b/sys/net80211/_ieee80211.h
new file mode 100644
index 0000000..d0ed282
--- /dev/null
+++ b/sys/net80211/_ieee80211.h
@@ -0,0 +1,188 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2004 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * 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_H_
+#define _NET80211__IEEE80211_H_
+
+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 */
+};
+#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
+
+/* XXX not really a mode; there are really multiple PHY's */
+enum ieee80211_phymode {
+ IEEE80211_MODE_AUTO = 0, /* autoselect */
+ IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */
+ IEEE80211_MODE_11B = 2, /* 2GHz, CCK */
+ IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */
+ IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */
+ IEEE80211_MODE_TURBO_A = 5, /* 5GHz, OFDM, 2x clock */
+ IEEE80211_MODE_TURBO_G = 6, /* 2GHz, OFDM, 2x clock */
+};
+#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO_G+1)
+
+enum ieee80211_opmode {
+ IEEE80211_M_STA = 1, /* infrastructure station */
+ IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */
+ IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */
+ IEEE80211_M_HOSTAP = 6, /* Software Access Point */
+ IEEE80211_M_MONITOR = 8 /* Monitor mode */
+};
+
+/*
+ * 802.11g protection mode.
+ */
+enum ieee80211_protmode {
+ IEEE80211_PROT_NONE = 0, /* no protection */
+ IEEE80211_PROT_CTSONLY = 1, /* CTS to self */
+ IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */
+};
+
+/*
+ * Authentication mode.
+ */
+enum ieee80211_authmode {
+ IEEE80211_AUTH_NONE = 0,
+ IEEE80211_AUTH_OPEN = 1, /* open */
+ IEEE80211_AUTH_SHARED = 2, /* shared-key */
+ IEEE80211_AUTH_8021X = 3, /* 802.1x */
+ IEEE80211_AUTH_AUTO = 4, /* auto-select/accept */
+ /* NB: these are used only for ioctls */
+ IEEE80211_AUTH_WPA = 5, /* WPA/RSN w/ 802.1x/PSK */
+};
+
+/*
+ * Roaming mode is effectively who controls the operation
+ * of the 802.11 state machine when operating as a station.
+ * State transitions are controlled either by the driver
+ * (typically when management frames are processed by the
+ * hardware/firmware), the host (auto/normal operation of
+ * the 802.11 layer), or explicitly through ioctl requests
+ * when applications like wpa_supplicant want control.
+ */
+enum ieee80211_roamingmode {
+ IEEE80211_ROAMING_DEVICE= 0, /* driver/hardware control */
+ IEEE80211_ROAMING_AUTO = 1, /* 802.11 layer control */
+ IEEE80211_ROAMING_MANUAL= 2, /* application control */
+};
+
+/*
+ * Channels are specified by frequency and attributes.
+ */
+struct ieee80211_channel {
+ u_int16_t ic_freq; /* setting in Mhz */
+ u_int16_t ic_flags; /* see below */
+};
+
+#define IEEE80211_CHAN_MAX 255
+#define IEEE80211_CHAN_BYTES 32 /* howmany(IEEE80211_CHAN_MAX, NBBY) */
+#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */
+#define IEEE80211_CHAN_ANYC \
+ ((struct ieee80211_channel *) IEEE80211_CHAN_ANY)
+
+/* bits 0-3 are for private use by drivers */
+/* channel attributes */
+#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */
+#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */
+#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */
+#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */
+#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */
+#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */
+#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */
+#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */
+
+/*
+ * Useful combinations of channel characteristics.
+ */
+#define IEEE80211_CHAN_FHSS \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
+#define IEEE80211_CHAN_A \
+ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
+#define IEEE80211_CHAN_B \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
+#define IEEE80211_CHAN_PUREG \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
+#define IEEE80211_CHAN_G \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
+#define IEEE80211_CHAN_T \
+ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+#define IEEE80211_CHAN_108G \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+
+#define IEEE80211_IS_CHAN_FHSS(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
+#define IEEE80211_IS_CHAN_A(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
+#define IEEE80211_IS_CHAN_B(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
+#define IEEE80211_IS_CHAN_PUREG(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
+#define IEEE80211_IS_CHAN_G(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
+#define IEEE80211_IS_CHAN_T(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
+#define IEEE80211_IS_CHAN_108G(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)
+
+#define IEEE80211_IS_CHAN_2GHZ(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
+#define IEEE80211_IS_CHAN_5GHZ(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
+#define IEEE80211_IS_CHAN_OFDM(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0)
+#define IEEE80211_IS_CHAN_CCK(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0)
+#define IEEE80211_IS_CHAN_GFSK(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0)
+
+/* ni_chan encoding for FH phy */
+#define IEEE80211_FH_CHANMOD 80
+#define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat))
+#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1)
+#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD)
+
+/*
+ * 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 {
+ u_int8_t rs_nrates;
+ u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE];
+};
+
+#endif /* _NET80211__IEEE80211_H_ */
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
index 7bbeedc..d7e0122 100644
--- a/sys/net80211/ieee80211.c
+++ b/sys/net80211/ieee80211.c
@@ -1,6 +1,6 @@
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,70 +37,84 @@ __FBSDID("$FreeBSD$");
* IEEE 802.11 generic handler
*/
-#include "opt_inet.h"
-
#include <sys/param.h>
#include <sys/systm.h>
-#include <sys/mbuf.h>
-#include <sys/malloc.h>
#include <sys/kernel.h>
-#include <sys/module.h>
-#include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
-
-#include <machine/atomic.h>
+#include <sys/socket.h>
+
#include <net/if.h>
-#include <net/if_dl.h>
#include <net/if_media.h>
-#include <net/if_arp.h>
#include <net/ethernet.h>
-#include <net/if_llc.h>
-#include <net/route.h>
#include <net80211/ieee80211_var.h>
#include <net/bpf.h>
-#ifdef INET
-#include <netinet/in.h>
-#include <netinet/if_ether.h>
-#endif
-
-#ifdef IEEE80211_DEBUG
-int ieee80211_debug = 0;
-SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug,
- 0, "IEEE 802.11 media debugging printfs");
-#endif
-
-static void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
- enum ieee80211_phymode);
-
static const char *ieee80211_phymode_name[] = {
"auto", /* IEEE80211_MODE_AUTO */
"11a", /* IEEE80211_MODE_11A */
"11b", /* IEEE80211_MODE_11B */
"11g", /* IEEE80211_MODE_11G */
"FH", /* IEEE80211_MODE_FH */
- "turbo", /* IEEE80211_MODE_TURBO */
+ "turboA", /* IEEE80211_MODE_TURBO_A */
+ "turboG", /* IEEE80211_MODE_TURBO_G */
};
+/* list of all instances */
+SLIST_HEAD(ieee80211_list, ieee80211com);
+static struct ieee80211_list ieee80211_list =
+ SLIST_HEAD_INITIALIZER(ieee80211_list);
+static u_int8_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;
+ u_int8_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);
+}
+
void
-ieee80211_ifattach(struct ifnet *ifp)
+ieee80211_ifattach(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_channel *c;
int i;
ether_ifattach(ifp, ic->ic_myaddr);
bpfattach2(ifp, DLT_IEEE802_11,
sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
- ieee80211_crypto_attach(ifp);
+
+ ieee80211_crypto_attach(ic);
/*
* Fill in 802.11 available channel set, mark
@@ -135,7 +149,9 @@ ieee80211_ifattach(struct ifnet *ifp)
if (IEEE80211_IS_CHAN_FHSS(c))
ic->ic_modecaps |= 1<<IEEE80211_MODE_FH;
if (IEEE80211_IS_CHAN_T(c))
- ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO;
+ ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_A;
+ if (IEEE80211_IS_CHAN_108G(c))
+ ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_G;
}
}
/* validate ic->ic_curmode */
@@ -143,25 +159,45 @@ ieee80211_ifattach(struct ifnet *ifp)
ic->ic_curmode = IEEE80211_MODE_AUTO;
ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
+ /*
+ * Enable WME by default if we're capable.
+ */
+ if (ic->ic_caps & IEEE80211_C_WME)
+ ic->ic_flags |= IEEE80211_F_WME;
+
(void) ieee80211_setmode(ic, ic->ic_curmode);
if (ic->ic_lintval == 0)
- ic->ic_lintval = 100; /* default sleep */
+ ic->ic_lintval = IEEE80211_BINTVAL_DEFAULT;
ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */
+ ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT;
+ IEEE80211_BEACON_LOCK_INIT(ic, "beacon");
+
+ ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
- ieee80211_node_attach(ifp);
- ieee80211_proto_attach(ifp);
+ ieee80211_node_attach(ic);
+ ieee80211_proto_attach(ic);
+
+ ieee80211_add_vap(ic);
+
+ ieee80211_sysctl_attach(ic); /* NB: requires ic_vap */
}
void
-ieee80211_ifdetach(struct ifnet *ifp)
+ieee80211_ifdetach(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ifnet *ifp = ic->ic_ifp;
- ieee80211_proto_detach(ifp);
- ieee80211_crypto_detach(ifp);
- ieee80211_node_detach(ifp);
+ ieee80211_remove_vap(ic);
+
+ ieee80211_sysctl_detach(ic);
+ ieee80211_proto_detach(ic);
+ ieee80211_crypto_detach(ic);
+ ieee80211_node_detach(ic);
ifmedia_removeall(&ic->ic_media);
+
+ IEEE80211_BEACON_LOCK_DESTROY(ic);
+
bpfdetach(ifp);
ether_ifdetach(ifp);
}
@@ -203,11 +239,11 @@ ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c)
else if (c == IEEE80211_CHAN_ANYC)
return IEEE80211_CHAN_ANY;
else if (c != NULL) {
- if_printf(&ic->ic_if, "invalid channel freq %u flags %x\n",
+ if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n",
c->ic_freq, c->ic_flags);
return 0; /* XXX */
} else {
- if_printf(&ic->ic_if, "invalid channel (NULL)\n");
+ if_printf(ic->ic_ifp, "invalid channel (NULL)\n");
return 0; /* XXX */
}
}
@@ -244,13 +280,13 @@ ieee80211_ieee2mhz(u_int chan, u_int flags)
* ieee80211_attach and before most anything else.
*/
void
-ieee80211_media_init(struct ifnet *ifp,
+ieee80211_media_init(struct ieee80211com *ic,
ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
{
#define ADD(_ic, _s, _o) \
ifmedia_add(&(_ic)->ic_media, \
IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
- struct ieee80211com *ic = (void *)ifp;
+ struct ifnet *ifp = ic->ic_ifp;
struct ifmediareq imr;
int i, j, mode, rate, maxrate, mword, mopt, r;
struct ieee80211_rateset *rs;
@@ -260,7 +296,7 @@ ieee80211_media_init(struct ifnet *ifp,
* Do late attach work that must wait for any subclass
* (i.e. driver) work such as overriding methods.
*/
- ieee80211_node_lateattach(ifp);
+ ieee80211_node_lateattach(ic);
/*
* Fill in media characteristics.
@@ -276,6 +312,7 @@ ieee80211_media_init(struct ifnet *ifp,
IFM_IEEE80211_11G,
IFM_IEEE80211_FH,
IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+ IFM_IEEE80211_11G | IFM_IEEE80211_TURBO,
};
if ((ic->ic_modecaps & (1<<mode)) == 0)
continue;
@@ -291,16 +328,12 @@ ieee80211_media_init(struct ifnet *ifp,
ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR);
if (mode == IEEE80211_MODE_AUTO)
continue;
- if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
rs = &ic->ic_sup_rates[mode];
for (i = 0; i < rs->rs_nrates; i++) {
rate = rs->rs_rates[i];
mword = ieee80211_rate2media(ic, rate, mode);
if (mword == 0)
continue;
- printf("%s%d%sMbps", (i != 0 ? " " : ""),
- (rate & IEEE80211_RATE_VAL) / 2,
- ((rate & 0x1) != 0 ? ".5" : ""));
ADD(ic, mword, mopt);
if (ic->ic_caps & IEEE80211_C_IBSS)
ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
@@ -326,7 +359,6 @@ ieee80211_media_init(struct ifnet *ifp,
if (rate > maxrate)
maxrate = rate;
}
- printf("\n");
}
for (i = 0; i < allrates.rs_nrates; i++) {
mword = ieee80211_rate2media(ic, allrates.rs_rates[i],
@@ -352,6 +384,31 @@ ieee80211_media_init(struct ifnet *ifp,
#undef ADD
}
+void
+ieee80211_announce(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ int i, mode, rate, mword;
+ struct ieee80211_rateset *rs;
+
+ for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
+ if ((ic->ic_modecaps & (1<<mode)) == 0)
+ continue;
+ if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
+ rs = &ic->ic_sup_rates[mode];
+ for (i = 0; i < rs->rs_nrates; i++) {
+ rate = rs->rs_rates[i];
+ mword = ieee80211_rate2media(ic, rate, mode);
+ if (mword == 0)
+ continue;
+ printf("%s%d%sMbps", (i != 0 ? " " : ""),
+ (rate & IEEE80211_RATE_VAL) / 2,
+ ((rate & 0x1) != 0 ? ".5" : ""));
+ }
+ printf("\n");
+ }
+}
+
static int
findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
{
@@ -366,17 +423,50 @@ findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
}
/*
+ * Find an instance by it's mac address.
+ */
+struct ieee80211com *
+ieee80211_find_vap(const u_int8_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;
+}
+
+/*
* Handle a media change request.
*/
int
ieee80211_media_change(struct ifnet *ifp)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211com *ic;
struct ifmedia_entry *ime;
enum ieee80211_opmode newopmode;
enum ieee80211_phymode newphymode;
int i, j, 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.
@@ -401,13 +491,16 @@ ieee80211_media_change(struct ifnet *ifp)
return EINVAL;
}
/*
- * Turbo mode is an ``option''. Eventually it
- * needs to be applied to 11g too.
+ * Turbo mode is an ``option''.
+ * XXX does not apply to AUTO
*/
if (ime->ifm_media & IFM_IEEE80211_TURBO) {
- if (newphymode != IEEE80211_MODE_11A)
+ if (newphymode == IEEE80211_MODE_11A)
+ newphymode = IEEE80211_MODE_TURBO_A;
+ else if (newphymode == IEEE80211_MODE_11G)
+ newphymode = IEEE80211_MODE_TURBO_G;
+ else
return EINVAL;
- newphymode = IEEE80211_MODE_TURBO;
}
/*
* Validate requested mode is available.
@@ -514,14 +607,15 @@ ieee80211_media_change(struct ifnet *ifp)
break;
case IEEE80211_M_IBSS:
ic->ic_flags |= IEEE80211_F_IBSSON;
-#ifdef notdef
- if (ic->ic_curmode == IEEE80211_MODE_11G)
- ieee80211_set11gbasicrates(
- &ic->ic_suprates[newphymode],
- IEEE80211_MODE_11B);
-#endif
break;
}
+ /*
+ * Yech, slot time may change depending on the
+ * operating mode so reset it to be sure everything
+ * is setup appropriately.
+ */
+ ieee80211_reset_erp(ic);
+ ieee80211_wme_initparams(ic); /* after opmode change */
error = ENETRESET;
}
#ifdef notdef
@@ -534,24 +628,39 @@ ieee80211_media_change(struct ifnet *ifp)
void
ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- struct ieee80211com *ic = (void *)ifp;
- struct ieee80211_node *ni = NULL;
- int old_status = imr->ifm_status;
+ struct ieee80211com *ic;
+ 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;
imr->ifm_active = IFM_IEEE80211;
- if (ic->ic_state == IEEE80211_S_RUN) {
+ if (ic->ic_state == IEEE80211_S_RUN)
imr->ifm_status |= IFM_ACTIVE;
- ifp->if_link_state = LINK_STATE_UP;
+ /*
+ * Calculate a current rate if possible.
+ */
+ if (ic->ic_fixed_rate != -1) {
+ /*
+ * A fixed rate is set, report that.
+ */
+ rs = &ic->ic_sup_rates[ic->ic_curmode];
+ imr->ifm_active |= ieee80211_rate2media(ic,
+ rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode);
+ } else if (ic->ic_opmode == IEEE80211_M_STA) {
+ /*
+ * In station mode report the current transmit rate.
+ */
+ rs = &ic->ic_bss->ni_rates;
+ imr->ifm_active |= ieee80211_rate2media(ic,
+ rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode);
} else
- ifp->if_link_state = LINK_STATE_DOWN;
- imr->ifm_active |= IFM_AUTO;
+ imr->ifm_active |= IFM_AUTO;
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
- ni = ic->ic_bss;
- /* calculate rate subtype */
- imr->ifm_active |= ieee80211_rate2media(ic,
- ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
break;
case IEEE80211_M_IBSS:
imr->ifm_active |= IFM_IEEE80211_ADHOC;
@@ -579,58 +688,41 @@ ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
case IEEE80211_MODE_FH:
imr->ifm_active |= IFM_IEEE80211_FH;
break;
- case IEEE80211_MODE_TURBO:
+ case IEEE80211_MODE_TURBO_A:
imr->ifm_active |= IFM_IEEE80211_11A
| IFM_IEEE80211_TURBO;
break;
+ case IEEE80211_MODE_TURBO_G:
+ imr->ifm_active |= IFM_IEEE80211_11G
+ | IFM_IEEE80211_TURBO;
+ break;
}
-
- /* Notify that the link state has changed. */
- if (imr->ifm_status != old_status)
- rt_ifmsg(ifp);
}
void
-ieee80211_watchdog(struct ifnet *ifp)
-{
- struct ieee80211com *ic = (void *)ifp;
-
- if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0)
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
- if (ic->ic_inact_timer && --ic->ic_inact_timer == 0)
- ieee80211_timeout_nodes(ic);
-
- if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0)
- ifp->if_timer = 1;
-}
-
-/*
- * Mark the basic rates for the 11g rate table based on the
- * operating mode. For real 11g we mark all the 11b rates
- * and 6, 12, and 24 OFDM. For 11b compatibility we mark only
- * 11b rates. There's also a pseudo 11a-mode used to mark only
- * the basic OFDM rates.
- */
-static void
-ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
+ieee80211_watchdog(struct ieee80211com *ic)
{
- static const struct ieee80211_rateset basic[] = {
- { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */
- { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11B */
- { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */
- { 0 }, /* IEEE80211_MODE_FH */
- { 0 }, /* IEEE80211_MODE_TURBO */
- };
- int i, j;
-
- for (i = 0; i < rs->rs_nrates; i++) {
- rs->rs_rates[i] &= IEEE80211_RATE_VAL;
- for (j = 0; j < basic[mode].rs_nrates; j++)
- if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
- rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
- break;
- }
+ struct ieee80211_node_table *nt;
+ int need_inact_timer = 0;
+
+ if (ic->ic_state != IEEE80211_S_INIT) {
+ if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0)
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+ nt = &ic->ic_scan;
+ if (nt->nt_inact_timer) {
+ if (--nt->nt_inact_timer == 0)
+ nt->nt_timeout(nt);
+ need_inact_timer += nt->nt_inact_timer;
+ }
+ nt = ic->ic_sta;
+ if (nt != NULL && nt->nt_inact_timer) {
+ if (--nt->nt_inact_timer == 0)
+ nt->nt_timeout(nt);
+ need_inact_timer += nt->nt_inact_timer;
+ }
}
+ if (ic->ic_mgt_timer != 0 || need_inact_timer)
+ ic->ic_ifp->if_timer = 1;
}
/*
@@ -649,7 +741,8 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */
IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */
IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
- IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO */
+ IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */
+ IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */
};
struct ieee80211_channel *c;
u_int modeflags;
@@ -657,8 +750,9 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
/* validate new mode */
if ((ic->ic_modecaps & (1<<mode)) == 0) {
- IEEE80211_DPRINTF(("%s: mode %u not supported (caps 0x%x)\n",
- __func__, mode, ic->ic_modecaps));
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ "%s: mode %u not supported (caps 0x%x)\n",
+ __func__, mode, ic->ic_modecaps);
return EINVAL;
}
@@ -680,8 +774,8 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
}
}
if (i > IEEE80211_CHAN_MAX) {
- IEEE80211_DPRINTF(("%s: no channels found for mode %u\n",
- __func__, mode));
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ "%s: no channels found for mode %u\n", __func__, mode);
return EINVAL;
}
@@ -713,56 +807,82 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode)
ic->ic_ibss_chan = &ic->ic_channels[i];
break;
}
+ KASSERT(ic->ic_ibss_chan != NULL &&
+ isset(ic->ic_chan_active,
+ ieee80211_chan2ieee(ic, ic->ic_ibss_chan)),
+ ("Bad IBSS channel %u",
+ ieee80211_chan2ieee(ic, ic->ic_ibss_chan)));
}
+ /*
+ * If the desired channel is set but no longer valid then reset it.
+ */
+ if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+ isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan)))
+ ic->ic_des_chan = IEEE80211_CHAN_ANYC;
/*
- * Set/reset state flags that influence beacon contents, etc.
- *
- * XXX what if we have stations already associated???
- * XXX probably not right for autoselect?
+ * Do mode-specific rate setup.
*/
- if (ic->ic_caps & IEEE80211_C_SHPREAMBLE)
- ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
if (mode == IEEE80211_MODE_11G) {
- if (ic->ic_caps & IEEE80211_C_SHSLOT)
- ic->ic_flags |= IEEE80211_F_SHSLOT;
+ /*
+ * Use a mixed 11b/11g rate set.
+ */
ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
IEEE80211_MODE_11G);
- } else {
- ic->ic_flags &= ~IEEE80211_F_SHSLOT;
+ } else if (mode == IEEE80211_MODE_11B) {
+ /*
+ * Force pure 11b rate set.
+ */
+ ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
+ IEEE80211_MODE_11B);
}
+ /*
+ * Setup an initial rate set according to the
+ * current/default channel selected above. This
+ * will be changed when scanning but must exist
+ * now so driver have a consistent state of ic_ibss_chan.
+ */
+ if (ic->ic_bss) /* NB: can be called before lateattach */
+ ic->ic_bss->ni_rates = ic->ic_sup_rates[mode];
ic->ic_curmode = mode;
+ ieee80211_reset_erp(ic); /* reset ERP state */
+ ieee80211_wme_initparams(ic); /* reset WME stat */
+
return 0;
#undef N
}
/*
* Return the phy mode for with the specified channel so the
- * caller can select a rate set. This is problematic and the
- * work here assumes how things work elsewhere in this code.
+ * caller can select a rate set. This is problematic for channels
+ * where multiple operating modes are possible (e.g. 11g+11b).
+ * In those cases we defer to the current operating mode when set.
*/
enum ieee80211_phymode
ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan)
{
- /*
- * NB: this assumes the channel would not be supplied to us
- * unless it was already compatible with the current mode.
- */
- if (ic->ic_curmode != IEEE80211_MODE_AUTO)
- return ic->ic_curmode;
- /*
- * In autoselect mode; deduce a mode based on the channel
- * characteristics. We assume that turbo-only channels
- * are not considered when the channel set is constructed.
- */
- if (IEEE80211_IS_CHAN_5GHZ(chan))
+ if (IEEE80211_IS_CHAN_5GHZ(chan)) {
+ /*
+ * This assumes all 11a turbo channels are also
+ * usable withut turbo, which is currently true.
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_TURBO_A)
+ return IEEE80211_MODE_TURBO_A;
return IEEE80211_MODE_11A;
- else if (IEEE80211_IS_CHAN_FHSS(chan))
+ } else if (IEEE80211_IS_CHAN_FHSS(chan))
return IEEE80211_MODE_FH;
- else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN))
+ else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) {
+ /*
+ * This assumes all 11g channels are also usable
+ * for 11b, which is currently true.
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_TURBO_G)
+ return IEEE80211_MODE_TURBO_G;
+ if (ic->ic_curmode == IEEE80211_MODE_11B)
+ return IEEE80211_MODE_11B;
return IEEE80211_MODE_11G;
- else
+ } else
return IEEE80211_MODE_11B;
}
@@ -812,7 +932,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m
mask = rate & IEEE80211_RATE_VAL;
switch (mode) {
case IEEE80211_MODE_11A:
- case IEEE80211_MODE_TURBO:
+ case IEEE80211_MODE_TURBO_A:
mask |= IFM_IEEE80211_11A;
break;
case IEEE80211_MODE_11B:
@@ -830,6 +950,7 @@ ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode m
/* NB: hack, 11g matches both 11b+11a rates */
/* fall thru... */
case IEEE80211_MODE_11G:
+ case IEEE80211_MODE_TURBO_G:
mask |= IFM_IEEE80211_11G;
break;
}
@@ -869,33 +990,3 @@ ieee80211_media2rate(int mword)
ieeerates[IFM_SUBTYPE(mword)] : 0;
#undef N
}
-
-/*
- * Module glue.
- *
- * NB: the module name is "wlan" for compatibility with NetBSD.
- */
-
-static int
-ieee80211_modevent(module_t mod, int type, void *unused)
-{
- switch (type) {
- case MOD_LOAD:
- if (bootverbose)
- printf("wlan: <802.11 Link Layer>\n");
- return 0;
- case MOD_UNLOAD:
- return 0;
- }
- return EINVAL;
-}
-
-static moduledata_t ieee80211_mod = {
- "wlan",
- ieee80211_modevent,
- 0
-};
-DECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan, 1);
-MODULE_DEPEND(wlan, rc4, 1, 1, 1);
-MODULE_DEPEND(wlan, ether, 1, 1, 1);
diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h
index f21c99a..420534e 100644
--- a/sys/net80211/ieee80211.h
+++ b/sys/net80211/ieee80211.h
@@ -1,6 +1,7 @@
+/* $NetBSD: ieee80211.h,v 1.4 2003/10/15 11:43:51 dyoung Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,7 +50,7 @@ struct ieee80211_plcp_hdr {
u_int8_t i_service;
u_int16_t i_length;
u_int16_t i_crc;
-} __attribute__((__packed__));
+} __packed;
#define IEEE80211_PLCP_SFD 0xF3A0
#define IEEE80211_PLCP_SERVICE 0x00
@@ -66,7 +67,7 @@ struct ieee80211_frame {
u_int8_t i_seq[2];
/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
/* see below */
-} __attribute__((__packed__));
+} __packed;
struct ieee80211_qosframe {
u_int8_t i_fc[2];
@@ -78,7 +79,7 @@ struct ieee80211_qosframe {
u_int8_t i_qos[2];
/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
/* see below */
-} __attribute__((__packed__));
+} __packed;
struct ieee80211_qoscntl {
u_int8_t i_qos[2];
@@ -92,7 +93,7 @@ struct ieee80211_frame_addr4 {
u_int8_t i_addr3[IEEE80211_ADDR_LEN];
u_int8_t i_seq[2];
u_int8_t i_addr4[IEEE80211_ADDR_LEN];
-} __attribute__((__packed__));
+} __packed;
struct ieee80211_qosframe_addr4 {
@@ -104,48 +105,7 @@ struct ieee80211_qosframe_addr4 {
u_int8_t i_seq[2];
u_int8_t i_addr4[IEEE80211_ADDR_LEN];
u_int8_t i_qos[2];
-} __attribute__((__packed__));
-
-/*
- * Management Notification Frame
- */
-struct ieee80211_mnf {
- u_int8_t mnf_category;
- u_int8_t mnf_action;
- u_int8_t mnf_dialog;
- u_int8_t mnf_status;
-} __attribute__((__packed__));
-#define MNF_SETUP_REQ 0
-#define MNF_SETUP_RESP 1
-#define MNF_TEARDOWN 2
-
-/*
- * WME/802.11e Tspec Element
- */
-struct ieee80211_wme_tspec {
- u_int8_t ts_id;
- u_int8_t ts_len;
- u_int8_t ts_oui[3];
- u_int8_t ts_oui_type;
- u_int8_t ts_oui_subtype;
- u_int8_t ts_version;
- u_int8_t ts_tsinfo[3];
- u_int8_t ts_nom_msdu[2];
- u_int8_t ts_max_msdu[2];
- u_int8_t ts_min_svc[4];
- u_int8_t ts_max_svc[4];
- u_int8_t ts_inactv_intv[4];
- u_int8_t ts_susp_intv[4];
- u_int8_t ts_start_svc[4];
- u_int8_t ts_min_rate[4];
- u_int8_t ts_mean_rate[4];
- u_int8_t ts_max_burst[4];
- u_int8_t ts_min_phy[4];
- u_int8_t ts_peak_rate[4];
- u_int8_t ts_delay[4];
- u_int8_t ts_surplus[2];
- u_int8_t ts_medium_time[2];
-} __attribute__((__packed__));
+} __packed;
#define IEEE80211_FC0_VERSION_MASK 0x03
#define IEEE80211_FC0_VERSION_SHIFT 0
@@ -187,6 +147,7 @@ struct ieee80211_wme_tspec {
#define IEEE80211_FC0_SUBTYPE_CFPOLL 0x60
#define IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK 0x70
#define IEEE80211_FC0_SUBTYPE_QOS 0x80
+#define IEEE80211_FC0_SUBTYPE_QOS_NULL 0xc0
#define IEEE80211_FC1_DIR_MASK 0x03
#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */
@@ -210,9 +171,121 @@ struct ieee80211_wme_tspec {
#define IEEE80211_QOS_TXOP 0x00ff
/* bit 8 is reserved */
-#define IEEE80211_QOS_ACKPOLICY 0x0600
-#define IEEE80211_QOS_ESOP 0x0800
-#define IEEE80211_QOS_TID 0xf000
+#define IEEE80211_QOS_ACKPOLICY 0x60
+#define IEEE80211_QOS_ACKPOLICY_S 5
+#define IEEE80211_QOS_ESOP 0x10
+#define IEEE80211_QOS_ESOP_S 4
+#define IEEE80211_QOS_TID 0x0f
+
+/* does frame have QoS sequence control data */
+#define IEEE80211_QOS_HAS_SEQ(wh) \
+ (((wh)->i_fc[0] & \
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \
+ (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS))
+
+/*
+ * WME/802.11e information element.
+ */
+struct ieee80211_wme_info {
+ u_int8_t wme_id; /* IEEE80211_ELEMID_VENDOR */
+ u_int8_t wme_len; /* length in bytes */
+ u_int8_t wme_oui[3]; /* 0x00, 0x50, 0xf2 */
+ u_int8_t wme_type; /* OUI type */
+ u_int8_t wme_subtype; /* OUI subtype */
+ u_int8_t wme_version; /* spec revision */
+ u_int8_t wme_info; /* QoS info */
+} __packed;
+
+/*
+ * WME/802.11e Tspec Element
+ */
+struct ieee80211_wme_tspec {
+ u_int8_t ts_id;
+ u_int8_t ts_len;
+ u_int8_t ts_oui[3];
+ u_int8_t ts_oui_type;
+ u_int8_t ts_oui_subtype;
+ u_int8_t ts_version;
+ u_int8_t ts_tsinfo[3];
+ u_int8_t ts_nom_msdu[2];
+ u_int8_t ts_max_msdu[2];
+ u_int8_t ts_min_svc[4];
+ u_int8_t ts_max_svc[4];
+ u_int8_t ts_inactv_intv[4];
+ u_int8_t ts_susp_intv[4];
+ u_int8_t ts_start_svc[4];
+ u_int8_t ts_min_rate[4];
+ u_int8_t ts_mean_rate[4];
+ u_int8_t ts_max_burst[4];
+ u_int8_t ts_min_phy[4];
+ u_int8_t ts_peak_rate[4];
+ u_int8_t ts_delay[4];
+ u_int8_t ts_surplus[2];
+ u_int8_t ts_medium_time[2];
+} __packed;
+
+/*
+ * WME AC parameter field
+ */
+struct ieee80211_wme_acparams {
+ u_int8_t acp_aci_aifsn;
+ u_int8_t acp_logcwminmax;
+ u_int16_t acp_txop;
+} __packed;
+
+#define WME_NUM_AC 4 /* 4 AC categories */
+
+#define WME_PARAM_ACI 0x60 /* Mask for ACI field */
+#define WME_PARAM_ACI_S 5 /* Shift for ACI field */
+#define WME_PARAM_ACM 0x10 /* Mask for ACM bit */
+#define WME_PARAM_ACM_S 4 /* Shift for ACM bit */
+#define WME_PARAM_AIFSN 0x0f /* Mask for aifsn field */
+#define WME_PARAM_AIFSN_S 0 /* Shift for aifsn field */
+#define WME_PARAM_LOGCWMIN 0x0f /* Mask for CwMin field (in log) */
+#define WME_PARAM_LOGCWMIN_S 0 /* Shift for CwMin field */
+#define WME_PARAM_LOGCWMAX 0xf0 /* Mask for CwMax field (in log) */
+#define WME_PARAM_LOGCWMAX_S 4 /* Shift for CwMax field */
+
+#define WME_AC_TO_TID(_ac) ( \
+ ((_ac) == WME_AC_VO) ? 6 : \
+ ((_ac) == WME_AC_VI) ? 5 : \
+ ((_ac) == WME_AC_BK) ? 1 : \
+ 0)
+
+#define TID_TO_WME_AC(_tid) ( \
+ ((_tid) < 1) ? WME_AC_BE : \
+ ((_tid) < 3) ? WME_AC_BK : \
+ ((_tid) < 6) ? WME_AC_VI : \
+ WME_AC_VO)
+
+/*
+ * WME Parameter Element
+ */
+struct ieee80211_wme_param {
+ u_int8_t param_id;
+ u_int8_t param_len;
+ u_int8_t param_oui[3];
+ u_int8_t param_oui_type;
+ u_int8_t param_oui_sybtype;
+ u_int8_t param_version;
+ u_int8_t param_qosInfo;
+#define WME_QOSINFO_COUNT 0x0f /* Mask for param count field */
+ u_int8_t param_reserved;
+ struct ieee80211_wme_acparams params_acParams[WME_NUM_AC];
+} __packed;
+
+/*
+ * Management Notification Frame
+ */
+struct ieee80211_mnf {
+ u_int8_t mnf_category;
+ u_int8_t mnf_action;
+ u_int8_t mnf_dialog;
+ u_int8_t mnf_status;
+} __packed;
+#define MNF_SETUP_REQ 0
+#define MNF_SETUP_RESP 1
+#define MNF_TEARDOWN 2
/*
* Control frames.
@@ -223,7 +296,7 @@ struct ieee80211_frame_min {
u_int8_t i_addr1[IEEE80211_ADDR_LEN];
u_int8_t i_addr2[IEEE80211_ADDR_LEN];
/* FCS */
-} __attribute__((__packed__));
+} __packed;
struct ieee80211_frame_rts {
u_int8_t i_fc[2];
@@ -231,21 +304,21 @@ struct ieee80211_frame_rts {
u_int8_t i_ra[IEEE80211_ADDR_LEN];
u_int8_t i_ta[IEEE80211_ADDR_LEN];
/* FCS */
-} __attribute__((__packed__));
+} __packed;
struct ieee80211_frame_cts {
u_int8_t i_fc[2];
u_int8_t i_dur[2];
u_int8_t i_ra[IEEE80211_ADDR_LEN];
/* FCS */
-} __attribute__((__packed__));
+} __packed;
struct ieee80211_frame_ack {
u_int8_t i_fc[2];
u_int8_t i_dur[2];
u_int8_t i_ra[IEEE80211_ADDR_LEN];
/* FCS */
-} __attribute__((__packed__));
+} __packed;
struct ieee80211_frame_pspoll {
u_int8_t i_fc[2];
@@ -253,7 +326,7 @@ struct ieee80211_frame_pspoll {
u_int8_t i_bssid[IEEE80211_ADDR_LEN];
u_int8_t i_ta[IEEE80211_ADDR_LEN];
/* FCS */
-} __attribute__((__packed__));
+} __packed;
struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */
u_int8_t i_fc[2];
@@ -261,7 +334,7 @@ struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */
u_int8_t i_ra[IEEE80211_ADDR_LEN];
u_int8_t i_bssid[IEEE80211_ADDR_LEN];
/* FCS */
-} __attribute__((__packed__));
+} __packed;
/*
* BEACON management packets
@@ -275,7 +348,7 @@ struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */
* octet information[length]
*/
-typedef uint8_t *ieee80211_mgt_beacon_t;
+typedef u_int8_t *ieee80211_mgt_beacon_t;
#define IEEE80211_BEACON_INTERVAL(beacon) \
((beacon)[8] | ((beacon)[9] << 8))
@@ -301,6 +374,8 @@ typedef uint8_t *ieee80211_mgt_beacon_t;
* 802.11i/WPA information element (maximally sized).
*/
struct ieee80211_ie_wpa {
+ u_int8_t wpa_id; /* IEEE80211_ELEMID_VENDOR */
+ u_int8_t wpa_len; /* length in bytes */
u_int8_t wpa_oui[3]; /* 0x00, 0x50, 0xf2 */
u_int8_t wpa_type; /* OUI type */
u_int16_t wpa_version; /* spec revision */
@@ -309,95 +384,70 @@ struct ieee80211_ie_wpa {
u_int32_t wpa_uciphers[8];/* ciphers */
u_int16_t wpa_authselcnt; /* authentication selector cnt*/
u_int32_t wpa_authsels[8];/* selectors */
-} __attribute__((__packed__));
+ u_int16_t wpa_caps; /* 802.11i capabilities */
+ u_int16_t wpa_pmkidcnt; /* 802.11i pmkid count */
+ u_int16_t wpa_pmkids[8]; /* 802.11i pmkids */
+} __packed;
/*
- * Management information elements
+ * Management information element payloads.
*/
-struct ieee80211_information {
- char ssid[IEEE80211_NWID_LEN+1];
- struct rates {
- u_int8_t *p;
- } rates;
- struct fh {
- u_int16_t dwell;
- u_int8_t set;
- u_int8_t pattern;
- u_int8_t index;
- } fh;
- struct ds {
- u_int8_t channel;
- } ds;
- struct cf {
- u_int8_t count;
- u_int8_t period;
- u_int8_t maxdur[2];
- u_int8_t dur[2];
- } cf;
- struct tim {
- u_int8_t count;
- u_int8_t period;
- u_int8_t bitctl;
- /* u_int8_t pvt[251]; The driver needs to use this. */
- } tim;
- struct ibss {
- u_int16_t atim;
- } ibss;
- struct challenge {
- u_int8_t *p;
- u_int8_t len;
- } challenge;
- struct erp {
- u_int8_t flags;
- } erp;
- struct country {
- u_int8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */
- struct {
- u_int8_t schan; /* starting channel */
- u_int8_t nchan; /* number channels */
- u_int8_t maxtxpwr;
- } band[4]; /* up to 4 sub bands */
- } country;
- struct ath {
- u_int8_t flags;
- } ath;
- struct ieee80211_ie_wpa wpa;
-};
enum {
- IEEE80211_ELEMID_SSID = 0,
- IEEE80211_ELEMID_RATES = 1,
- IEEE80211_ELEMID_FHPARMS = 2,
- IEEE80211_ELEMID_DSPARMS = 3,
- IEEE80211_ELEMID_CFPARMS = 4,
- IEEE80211_ELEMID_TIM = 5,
- IEEE80211_ELEMID_IBSSPARMS = 6,
- IEEE80211_ELEMID_COUNTRY = 7,
- IEEE80211_ELEMID_CHALLENGE = 16,
+ IEEE80211_ELEMID_SSID = 0,
+ IEEE80211_ELEMID_RATES = 1,
+ IEEE80211_ELEMID_FHPARMS = 2,
+ IEEE80211_ELEMID_DSPARMS = 3,
+ IEEE80211_ELEMID_CFPARMS = 4,
+ IEEE80211_ELEMID_TIM = 5,
+ IEEE80211_ELEMID_IBSSPARMS = 6,
+ IEEE80211_ELEMID_COUNTRY = 7,
+ IEEE80211_ELEMID_CHALLENGE = 16,
/* 17-31 reserved for challenge text extension */
- IEEE80211_ELEMID_ERP = 42,
- IEEE80211_ELEMID_XRATES = 50,
- IEEE80211_ELEMID_TPC = 150,
- IEEE80211_ELEMID_CCKM = 156,
- IEEE80211_ELEMID_VENDOR = 221, /* vendor private */
+ IEEE80211_ELEMID_ERP = 42,
+ IEEE80211_ELEMID_RSN = 48,
+ IEEE80211_ELEMID_XRATES = 50,
+ IEEE80211_ELEMID_TPC = 150,
+ IEEE80211_ELEMID_CCKM = 156,
+ IEEE80211_ELEMID_VENDOR = 221, /* vendor private */
};
-#define IEEE80211_CHALLENGE_LEN 128
-
-#define IEEE80211_RATE_BASIC 0x80
-#define IEEE80211_RATE_VAL 0x7f
+struct ieee80211_tim_ie {
+ u_int8_t tim_ie; /* IEEE80211_ELEMID_TIM */
+ u_int8_t tim_len;
+ u_int8_t tim_count; /* DTIM count */
+ u_int8_t tim_period; /* DTIM period */
+ u_int8_t tim_bitctl; /* bitmap control */
+ u_int8_t tim_bitmap[1]; /* variable-length bitmap */
+} __packed;
+
+struct ieee80211_country_ie {
+ u_int8_t ie; /* IEEE80211_ELEMID_COUNTRY */
+ u_int8_t len;
+ u_int8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */
+ struct {
+ u_int8_t schan; /* starting channel */
+ u_int8_t nchan; /* number channels */
+ u_int8_t maxtxpwr; /* tx power cap */
+ } band[4] __packed; /* up to 4 sub bands */
+} __packed;
+
+#define IEEE80211_CHALLENGE_LEN 128
+
+#define IEEE80211_RATE_BASIC 0x80
+#define IEEE80211_RATE_VAL 0x7f
/* EPR information element flags */
-#define IEEE80211_ERP_NON_ERP_PRESENT 0x01
-#define IEEE80211_ERP_USE_PROTECTION 0x02
-#define IEEE80211_ERP_BARKER_MODE 0x04
+#define IEEE80211_ERP_NON_ERP_PRESENT 0x01
+#define IEEE80211_ERP_USE_PROTECTION 0x02
+#define IEEE80211_ERP_LONG_PREAMBLE 0x04
/* Atheros private advanced capabilities info */
-#define ATHEROS_CAP_TURBO_PRIME 0x01
-#define ATHEROS_CAP_COMPRESSION 0x02
-#define ATHEROS_CAP_FAST_FRAME 0x04
+#define ATHEROS_CAP_TURBO_PRIME 0x01
+#define ATHEROS_CAP_COMPRESSION 0x02
+#define ATHEROS_CAP_FAST_FRAME 0x04
/* bits 3-6 reserved */
-#define ATHEROS_CAP_BOOST 0x80
+#define ATHEROS_CAP_BOOST 0x80
#define ATH_OUI 0x7f0300 /* Atheros OUI */
#define ATH_OUI_TYPE 0x01
@@ -405,12 +455,11 @@ enum {
#define WPA_OUI 0xf25000
#define WPA_OUI_TYPE 0x01
-#define WPA_OUI_VERSION 1 /* current supported version */
+#define WPA_VERSION 1 /* current supported version */
#define WPA_CSE_NULL 0x00
#define WPA_CSE_WEP40 0x01
#define WPA_CSE_TKIP 0x02
-#define WPA_CSE_WRAP 0x03 /* WPA2/802.11i */
#define WPA_CSE_CCMP 0x04
#define WPA_CSE_WEP104 0x05
@@ -418,6 +467,34 @@ enum {
#define WPA_ASE_8021X_UNSPEC 0x01
#define WPA_ASE_8021X_PSK 0x02
+#define RSN_OUI 0xac0f00
+#define RSN_VERSION 1 /* current supported version */
+
+#define RSN_CSE_NULL 0x00
+#define RSN_CSE_WEP40 0x01
+#define RSN_CSE_TKIP 0x02
+#define RSN_CSE_WRAP 0x03
+#define RSN_CSE_CCMP 0x04
+#define RSN_CSE_WEP104 0x05
+
+#define RSN_ASE_NONE 0x00
+#define RSN_ASE_8021X_UNSPEC 0x01
+#define RSN_ASE_8021X_PSK 0x02
+
+#define RSN_CAP_PREAUTH 0x01
+
+#define WME_OUI 0xf25000
+#define WME_OUI_TYPE 0x02
+#define WME_INFO_OUI_SUBTYPE 0x00
+#define WME_PARAM_OUI_SUBTYPE 0x01
+#define WME_VERSION 1
+
+/* WME stream classes */
+#define WME_AC_BE 0 /* best effort */
+#define WME_AC_BK 1 /* background */
+#define WME_AC_VI 2 /* video */
+#define WME_AC_VO 3 /* voice */
+
/*
* AUTH management packets
*
@@ -438,9 +515,9 @@ typedef u_int8_t *ieee80211_mgt_auth_t;
#define IEEE80211_AUTH_STATUS(auth) \
((auth)[4] | ((auth)[5] << 8))
-#define IEEE80211_AUTH_ALG_OPEN 0x0000
-#define IEEE80211_AUTH_ALG_SHARED 0x0001
-#define IEEE80211_AUTH_ALG_LEAP 0x0080
+#define IEEE80211_AUTH_ALG_OPEN 0x0000
+#define IEEE80211_AUTH_ALG_SHARED 0x0001
+#define IEEE80211_AUTH_ALG_LEAP 0x0080
enum {
IEEE80211_AUTH_OPEN_REQUEST = 1,
@@ -496,13 +573,24 @@ enum {
IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26,
};
-#define IEEE80211_WEP_KEYLEN 5 /* 40bit */
-#define IEEE80211_WEP_IVLEN 3 /* 24bit */
-#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */
-#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */
-#define IEEE80211_WEP_NKID 4 /* number of key ids */
+#define IEEE80211_WEP_KEYLEN 5 /* 40bit */
+#define IEEE80211_WEP_IVLEN 3 /* 24bit */
+#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */
+#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */
+#define IEEE80211_WEP_NKID 4 /* number of key ids */
-#define IEEE80211_CRC_LEN 4
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP. For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+#define IEEE80211_WEP_EXTIV 0x20
+#define IEEE80211_WEP_EXTIVLEN 4 /* extended IV length */
+#define IEEE80211_WEP_MICLEN 8 /* trailing MIC */
+
+#define IEEE80211_CRC_LEN 4
/*
* Maximum acceptable MTU is:
@@ -511,26 +599,40 @@ enum {
* Min is arbitrarily chosen > IEEE80211_MIN_LEN. The default
* mtu is Ethernet-compatible; it's set by ether_ifattach.
*/
-#define IEEE80211_MTU_MAX 2290
-#define IEEE80211_MTU_MIN 32
+#define IEEE80211_MTU_MAX 2290
+#define IEEE80211_MTU_MIN 32
-#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \
+#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \
(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN))
+#define IEEE80211_ACK_LEN \
+ (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
#define IEEE80211_MIN_LEN \
(sizeof(struct ieee80211_frame_min) + IEEE80211_CRC_LEN)
+/*
+ * The 802.11 spec says at most 2007 stations may be
+ * associated at once. For most AP's this is way more
+ * than is feasible so we use a default of 128. This
+ * number may be overridden by the driver and/or by
+ * user configuration.
+ */
+#define IEEE80211_AID_MAX 2007
+#define IEEE80211_AID_DEF 128
+
+#define IEEE80211_AID(b) ((b) &~ 0xc000)
+#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)))
+
/*
* RTS frame length parameters. The default is specified in
* the 802.11 spec. The max may be wrong for jumbo frames.
*/
-#define IEEE80211_RTS_DEFAULT 512
-#define IEEE80211_RTS_MIN 1
-#define IEEE80211_RTS_MAX IEEE80211_MAX_LEN
-
-enum {
- IEEE80211_AUTH_NONE = 0,
- IEEE80211_AUTH_OPEN = 1,
- IEEE80211_AUTH_SHARED = 2,
-};
+#define IEEE80211_RTS_DEFAULT 512
+#define IEEE80211_RTS_MIN 1
+#define IEEE80211_RTS_MAX IEEE80211_MAX_LEN
#endif /* _NET80211_IEEE80211_H_ */
diff --git a/sys/net80211/ieee80211_acl.c b/sys/net80211/ieee80211_acl.c
new file mode 100644
index 0000000..e04fa13
--- /dev/null
+++ b/sys/net80211/ieee80211_acl.c
@@ -0,0 +1,301 @@
+/*-
+ * Copyright (c) 2004 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11 MAC ACL support.
+ *
+ * When this module is loaded the sender address of each received
+ * frame is passed to the iac_check method and the module indicates
+ * if the frame should be accepted or rejected. If the policy is
+ * set to ACL_POLICY_OPEN then all frames are accepted w/o checking
+ * the address. Otherwise, the address is looked up in the database
+ * and if found the frame is either accepted (ACL_POLICY_ALLOW)
+ * or rejected (ACL_POLICY_DENT).
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/route.h>
+
+#include <net80211/ieee80211_var.h>
+
+enum {
+ ACL_POLICY_OPEN = 0, /* open, don't check ACL's */
+ ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */
+ ACL_POLICY_DENY = 2, /* deny traffic from MAC */
+};
+
+#define ACL_HASHSIZE 32
+
+struct acl {
+ TAILQ_ENTRY(acl) acl_list;
+ LIST_ENTRY(acl) acl_hash;
+ u_int8_t acl_macaddr[IEEE80211_ADDR_LEN];
+};
+struct aclstate {
+ acl_lock_t as_lock;
+ int as_policy;
+ TAILQ_HEAD(, acl) as_list; /* list of all ACL's */
+ LIST_HEAD(, acl) as_hash[ACL_HASHSIZE];
+ struct ieee80211com *as_ic;
+};
+
+/* simple hash is enough for variation of macaddr */
+#define ACL_HASH(addr) \
+ (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
+
+MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
+
+static int acl_free_all(struct ieee80211com *);
+
+static int
+acl_attach(struct ieee80211com *ic)
+{
+ struct aclstate *as;
+
+ MALLOC(as, struct aclstate *, sizeof(struct aclstate),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (as == NULL)
+ return 0;
+ ACL_LOCK_INIT(as, "acl");
+ TAILQ_INIT(&as->as_list);
+ as->as_policy = ACL_POLICY_OPEN;
+ as->as_ic = ic;
+ ic->ic_as = as;
+ return 1;
+}
+
+static void
+acl_detach(struct ieee80211com *ic)
+{
+ struct aclstate *as = ic->ic_as;
+
+ acl_free_all(ic);
+ ic->ic_as = NULL;
+ ACL_LOCK_DESTROY(as);
+ FREE(as, M_DEVBUF);
+}
+
+static inline struct acl *
+_find_acl(struct aclstate *as, const u_int8_t *macaddr)
+{
+ struct acl *acl;
+ int hash;
+
+ hash = ACL_HASH(macaddr);
+ LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
+ if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
+ return acl;
+ }
+ return NULL;
+}
+
+static void
+_acl_free(struct aclstate *as, struct acl *acl)
+{
+ ACL_LOCK_ASSERT(as);
+
+ TAILQ_REMOVE(&as->as_list, acl, acl_list);
+ LIST_REMOVE(acl, acl_hash);
+ FREE(acl, M_80211_ACL);
+}
+
+static int
+acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct aclstate *as = ic->ic_as;
+
+ switch (as->as_policy) {
+ case ACL_POLICY_OPEN:
+ return 1;
+ case ACL_POLICY_ALLOW:
+ return _find_acl(as, mac) != NULL;
+ case ACL_POLICY_DENY:
+ return _find_acl(as, mac) == NULL;
+ }
+ return 0; /* should not happen */
+}
+
+static int
+acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct aclstate *as = ic->ic_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,
+ "ACL: add %s failed, no memory\n", ether_sprintf(mac));
+ /* XXX statistic */
+ return ENOMEM;
+ }
+
+ ACL_LOCK(as);
+ hash = ACL_HASH(mac);
+ LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
+ if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
+ ACL_UNLOCK(as);
+ FREE(new, M_80211_ACL);
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ "ACL: add %s failed, already present\n",
+ ether_sprintf(mac));
+ return EEXIST;
+ }
+ }
+ IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
+ TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
+ LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
+ ACL_UNLOCK(as);
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ "ACL: add %s\n", ether_sprintf(mac));
+ return 0;
+}
+
+static int
+acl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct aclstate *as = ic->ic_as;
+ struct acl *acl;
+
+ ACL_LOCK(as);
+ acl = _find_acl(as, mac);
+ if (acl != NULL)
+ _acl_free(as, acl);
+ ACL_UNLOCK(as);
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ "ACL: remove %s%s\n", ether_sprintf(mac),
+ acl == NULL ? ", not present" : "");
+
+ return (acl == NULL ? ENOENT : 0);
+}
+
+static int
+acl_free_all(struct ieee80211com *ic)
+{
+ struct aclstate *as = ic->ic_as;
+ struct acl *acl;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
+
+ ACL_LOCK(as);
+ while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
+ _acl_free(as, acl);
+ ACL_UNLOCK(as);
+
+ return 0;
+}
+
+static int
+acl_setpolicy(struct ieee80211com *ic, int policy)
+{
+ struct aclstate *as = ic->ic_as;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+ "ACL: set policy to %u\n", policy);
+
+ switch (policy) {
+ case IEEE80211_MACCMD_POLICY_OPEN:
+ as->as_policy = ACL_POLICY_OPEN;
+ break;
+ case IEEE80211_MACCMD_POLICY_ALLOW:
+ as->as_policy = ACL_POLICY_ALLOW;
+ break;
+ case IEEE80211_MACCMD_POLICY_DENY:
+ as->as_policy = ACL_POLICY_DENY;
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int
+acl_getpolicy(struct ieee80211com *ic)
+{
+ struct aclstate *as = ic->ic_as;
+
+ return as->as_policy;
+}
+
+static const struct ieee80211_aclator mac = {
+ .iac_name = "mac",
+ .iac_attach = acl_attach,
+ .iac_detach = acl_detach,
+ .iac_check = acl_check,
+ .iac_add = acl_add,
+ .iac_remove = acl_remove,
+ .iac_flush = acl_free_all,
+ .iac_setpolicy = acl_setpolicy,
+ .iac_getpolicy = acl_getpolicy,
+};
+
+/*
+ * 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);
diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c
index 6cb9ee0..5e71215 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, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,290 +33,527 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include "opt_inet.h"
-
+/*
+ * IEEE 802.11 generic crypto support.
+ */
#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/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
-
-#include <machine/atomic.h>
-
+
#include <net/if.h>
-#include <net/if_dl.h>
#include <net/if_media.h>
-#include <net/if_arp.h>
-#include <net/ethernet.h>
-#include <net/if_llc.h>
+#include <net/ethernet.h> /* XXX ETHER_HDR_LEN */
#include <net80211/ieee80211_var.h>
-#include <net/bpf.h>
+/*
+ * 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)
+{
+ return IEEE80211_KEYIX_NONE;
+}
+static int
+null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+ return 1;
+}
+static int
+null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+ const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+ return 1;
+}
+static void null_key_update(struct ieee80211com *ic) {}
+
+/*
+ * Write-arounds for common operations.
+ */
+static __inline void
+cipher_detach(struct ieee80211_key *key)
+{
+ key->wk_cipher->ic_detach(key);
+}
-#ifdef INET
-#include <netinet/in.h>
-#include <netinet/if_ether.h>
-#endif
+static __inline void *
+cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+ return key->wk_cipher->ic_attach(ic, key);
+}
-#include <crypto/rc4/rc4.h>
-#define arc4_ctxlen() sizeof (struct rc4_state)
-#define arc4_setkey(_c,_k,_l) rc4_init(_c,_k,_l)
-#define arc4_encrypt(_c,_d,_s,_l) rc4_crypt(_c,_s,_d,_l)
+/*
+ * Wrappers for driver key management methods.
+ */
+static __inline int
+dev_key_alloc(struct ieee80211com *ic,
+ const struct ieee80211_key *key)
+{
+ return ic->ic_crypto.cs_key_alloc(ic, key);
+}
+
+static __inline int
+dev_key_delete(struct ieee80211com *ic,
+ const struct ieee80211_key *key)
+{
+ return ic->ic_crypto.cs_key_delete(ic, key);
+}
-static void ieee80211_crc_init(void);
-static u_int32_t ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len);
+static __inline int
+dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key,
+ const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+ return ic->ic_crypto.cs_key_set(ic, key, mac);
+}
+/*
+ * Setup crypto support.
+ */
void
-ieee80211_crypto_attach(struct ifnet *ifp)
+ieee80211_crypto_attach(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_crypto_state *cs = &ic->ic_crypto;
+ int i;
+ /* NB: we assume everything is pre-zero'd */
+ cs->cs_def_txkey = IEEE80211_KEYIX_NONE;
+ ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
+ for (i = 0; i < IEEE80211_WEP_NKID; i++)
+ ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i], i);
/*
- * Setup crypto support.
+ * Initialize the driver key support routines to noop entries.
+ * This is useful especially for the cipher test modules.
*/
- ieee80211_crc_init();
- ic->ic_iv = arc4random();
+ 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;
}
+/*
+ * Teardown crypto support.
+ */
void
-ieee80211_crypto_detach(struct ifnet *ifp)
+ieee80211_crypto_detach(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
+ ieee80211_crypto_delglobalkeys(ic);
+}
- if (ic->ic_wep_ctx != NULL) {
- free(ic->ic_wep_ctx, M_DEVBUF);
- ic->ic_wep_ctx = NULL;
+/*
+ * Register a crypto cipher module.
+ */
+void
+ieee80211_crypto_register(const struct ieee80211_cipher *cip)
+{
+ if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
+ printf("%s: cipher %s has an invalid cipher index %u\n",
+ __func__, cip->ic_name, cip->ic_cipher);
+ return;
}
+ if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
+ printf("%s: cipher %s registered with a different template\n",
+ __func__, cip->ic_name);
+ return;
+ }
+ ciphers[cip->ic_cipher] = cip;
}
-struct mbuf *
-ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag)
+/*
+ * Unregister a crypto cipher module.
+ */
+void
+ieee80211_crypto_unregister(const struct ieee80211_cipher *cip)
{
- struct ieee80211com *ic = (void *)ifp;
- struct mbuf *m, *n, *n0;
- struct ieee80211_frame *wh;
- int i, left, len, moff, noff, kid;
- u_int32_t iv, crc;
- u_int8_t *ivp;
- void *ctx;
- u_int8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
- u_int8_t crcbuf[IEEE80211_WEP_CRCLEN];
-
- n0 = NULL;
- if ((ctx = ic->ic_wep_ctx) == NULL) {
- ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT);
- if (ctx == NULL) {
- ic->ic_stats.is_crypto_nomem++;
- goto fail;
- }
- ic->ic_wep_ctx = ctx;
- }
- m = m0;
- left = m->m_pkthdr.len;
- MGET(n, M_DONTWAIT, m->m_type);
- n0 = n;
- if (n == NULL) {
- if (txflag)
- ic->ic_stats.is_tx_nombuf++;
- else
- ic->ic_stats.is_rx_nombuf++;
- goto fail;
+ if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
+ printf("%s: cipher %s has an invalid cipher index %u\n",
+ __func__, cip->ic_name, cip->ic_cipher);
+ return;
}
- M_MOVE_PKTHDR(n, m);
- len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN;
- if (txflag) {
- n->m_pkthdr.len += len;
- } else {
- n->m_pkthdr.len -= len;
- left -= len;
+ if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
+ printf("%s: cipher %s registered with a different template\n",
+ __func__, cip->ic_name);
+ return;
}
- n->m_len = MHLEN;
- if (n->m_pkthdr.len >= MINCLSIZE) {
- MCLGET(n, M_DONTWAIT);
- if (n->m_flags & M_EXT)
- n->m_len = n->m_ext.ext_size;
+ /* NB: don't complain about not being registered */
+ /* XXX disallow if references */
+ ciphers[cip->ic_cipher] = NULL;
+}
+
+int
+ieee80211_crypto_available(u_int cipher)
+{
+ return cipher < IEEE80211_CIPHER_MAX && ciphers[cipher] != NULL;
+}
+
+/* XXX well-known names! */
+static const char *cipher_modnames[] = {
+ "wlan_wep", /* IEEE80211_CIPHER_WEP */
+ "wlan_tkip", /* IEEE80211_CIPHER_TKIP */
+ "wlan_aes_ocb", /* IEEE80211_CIPHER_AES_OCB */
+ "wlan_ccmp", /* IEEE80211_CIPHER_AES_CCM */
+ "wlan_ckip", /* IEEE80211_CIPHER_CKIP */
+};
+
+/*
+ * Establish a relationship between the specified key and cipher
+ * and, if not a global key, allocate a hardware index from the
+ * driver. Note that we may be called for global keys but they
+ * should have a key index already setup so the only work done
+ * is to setup the cipher reference.
+ *
+ * This must be the first call applied to a key; all the other key
+ * routines assume wk_cipher is setup.
+ *
+ * Locking must be handled by the caller using:
+ * ieee80211_key_update_begin(ic);
+ * ieee80211_key_update_end(ic);
+ */
+int
+ieee80211_crypto_newkey(struct ieee80211com *ic,
+ int cipher, struct ieee80211_key *key)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ const struct ieee80211_cipher *cip;
+ void *keyctx;
+ int oflags;
+
+ /*
+ * 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++;
+ return 0;
}
- len = sizeof(struct ieee80211_frame);
- memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len);
- wh = mtod(n, struct ieee80211_frame *);
- left -= len;
- moff = len;
- noff = len;
- if (txflag) {
- kid = ic->ic_wep_txkey;
- wh->i_fc[1] |= IEEE80211_FC1_WEP;
- iv = ic->ic_iv;
+ cip = ciphers[cipher];
+ if (cip == NULL) {
/*
- * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
- * (B, 255, N) with 3 <= B < 8
+ * Auto-load cipher module if we have a well-known name
+ * for it. It might be better to use string names rather
+ * than numbers and craft a module name based on the cipher
+ * name; e.g. wlan_cipher_<cipher-name>.
*/
- if (iv >= 0x03ff00 &&
- (iv & 0xf8ff00) == 0x00ff00)
- iv += 0x000100;
- ic->ic_iv = iv + 1;
- /* put iv in little endian to prepare 802.11i */
- ivp = mtod(n, u_int8_t *) + noff;
- for (i = 0; i < IEEE80211_WEP_IVLEN; i++) {
- ivp[i] = iv & 0xff;
- iv >>= 8;
+ 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];
}
- ivp[IEEE80211_WEP_IVLEN] = kid << 6; /* pad and keyid */
- noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
- } else {
- wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
- ivp = mtod(m, u_int8_t *) + moff;
- kid = ivp[IEEE80211_WEP_IVLEN] >> 6;
- moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
- }
- memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN);
- memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key,
- ic->ic_nw_keys[kid].wk_len);
- arc4_setkey(ctx, keybuf,
- IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len);
-
- /* encrypt with calculating CRC */
- crc = ~0;
- while (left > 0) {
- len = m->m_len - moff;
- if (len == 0) {
- m = m->m_next;
- moff = 0;
- continue;
+ 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++;
+ return 0;
}
- if (len > n->m_len - noff) {
- len = n->m_len - noff;
- if (len == 0) {
- MGET(n->m_next, M_DONTWAIT, n->m_type);
- if (n->m_next == NULL) {
- if (txflag)
- ic->ic_stats.is_tx_nombuf++;
- else
- ic->ic_stats.is_rx_nombuf++;
- goto fail;
- }
- n = n->m_next;
- n->m_len = MLEN;
- if (left >= MINCLSIZE) {
- MCLGET(n, M_DONTWAIT);
- if (n->m_flags & M_EXT)
- n->m_len = n->m_ext.ext_size;
- }
- noff = 0;
- continue;
- }
+ }
+
+ oflags = key->wk_flags;
+ /*
+ * If the hardware does not support the cipher then
+ * fallback to a host-based implementation.
+ */
+ key->wk_flags &= ~(IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC);
+ if ((ic->ic_caps & (1<<cipher)) == 0) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "%s: no h/w support for cipher %s, falling back to s/w\n",
+ __func__, cip->ic_name);
+ key->wk_flags |= IEEE80211_KEY_SWCRYPT;
+ }
+ /*
+ * Hardware TKIP with software MIC is an important
+ * combination; we handle it by flagging each key,
+ * the cipher modules honor it.
+ */
+ if (cipher == IEEE80211_CIPHER_TKIP &&
+ (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "%s: no h/w support for TKIP MIC, falling back to s/w\n",
+ __func__);
+ key->wk_flags |= IEEE80211_KEY_SWMIC;
+ }
+
+ /*
+ * Bind cipher to key instance. Note we do this
+ * after checking the device capabilities so the
+ * cipher module can optimize space usage based on
+ * whether or not it needs to do the cipher work.
+ */
+ if (key->wk_cipher != cip || key->wk_flags != oflags) {
+again:
+ keyctx = cip->ic_attach(ic, key);
+ if (keyctx == NULL) {
+ IEEE80211_DPRINTF(ic, 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++;
+ return 0;
}
- if (len > left)
- len = left;
- arc4_encrypt(ctx, mtod(n, caddr_t) + noff,
- mtod(m, caddr_t) + moff, len);
- if (txflag)
- crc = ieee80211_crc_update(crc,
- mtod(m, u_int8_t *) + moff, len);
- else
- crc = ieee80211_crc_update(crc,
- mtod(n, u_int8_t *) + noff, len);
- left -= len;
- moff += len;
- noff += len;
+ cipher_detach(key);
+ key->wk_cipher = cip; /* XXX refcnt? */
+ key->wk_private = keyctx;
}
- crc = ~crc;
- if (txflag) {
- *(u_int32_t *)crcbuf = htole32(crc);
- if (n->m_len >= noff + sizeof(crcbuf))
- n->m_len = noff + sizeof(crcbuf);
- else {
- n->m_len = noff;
- MGET(n->m_next, M_DONTWAIT, n->m_type);
- if (n->m_next == NULL) {
- ic->ic_stats.is_tx_nombuf++;
- goto fail;
+
+ /*
+ * Ask the driver for a key index if we don't have one.
+ * Note that entries in the global key table always have
+ * an index; this means it's safe to call this routine
+ * for these entries just to setup the reference to the
+ * cipher template. Note also that when using software
+ * crypto we also call the driver to give us a key index.
+ */
+ if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+ key->wk_keyix = dev_key_alloc(ic, key);
+ if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+ /*
+ * Driver has no room; fallback to doing crypto
+ * in the host. We change the flags and start the
+ * procedure over. If we get back here then there's
+ * no hope and we bail. Note that this can leave
+ * the key in a inconsistent state if the caller
+ * continues to use it.
+ */
+ if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
+ ic->ic_stats.is_crypto_swfallback++;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "%s: no h/w resources for cipher %s, "
+ "falling back to s/w\n", __func__,
+ cip->ic_name);
+ oflags = key->wk_flags;
+ key->wk_flags |= IEEE80211_KEY_SWCRYPT;
+ if (cipher == IEEE80211_CIPHER_TKIP)
+ key->wk_flags |= IEEE80211_KEY_SWMIC;
+ goto again;
}
- n = n->m_next;
- n->m_len = sizeof(crcbuf);
- noff = 0;
- }
- arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf,
- sizeof(crcbuf));
- } else {
- n->m_len = noff;
- for (noff = 0; noff < sizeof(crcbuf); noff += len) {
- len = sizeof(crcbuf) - noff;
- if (len > m->m_len - moff)
- len = m->m_len - moff;
- if (len > 0)
- arc4_encrypt(ctx, crcbuf + noff,
- mtod(m, caddr_t) + moff, len);
- m = m->m_next;
- moff = 0;
+ ic->ic_stats.is_crypto_keyfail++;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "%s: unable to setup cipher %s\n",
+ __func__, cip->ic_name);
+ return 0;
}
- if (crc != le32toh(*(u_int32_t *)crcbuf)) {
-#ifdef IEEE80211_DEBUG
- if (ieee80211_debug) {
- if_printf(ifp, "decrypt CRC error\n");
- if (ieee80211_debug > 1)
- ieee80211_dump_pkt(n0->m_data,
- n0->m_len, -1, -1);
- }
-#endif
- ic->ic_stats.is_rx_decryptcrc++;
- goto fail;
+ }
+ return 1;
+#undef N
+}
+
+/*
+ * Remove the key (no locking, for internal use).
+ */
+static int
+_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+ u_int16_t keyix;
+
+ KASSERT(key->wk_cipher != NULL, ("No cipher!"));
+
+ keyix = key->wk_keyix;
+ if (keyix != IEEE80211_KEYIX_NONE) {
+ /*
+ * Remove hardware entry.
+ */
+ /* XXX key cache */
+ if (!dev_key_delete(ic, key)) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "%s: driver did not delete key index %u\n",
+ __func__, keyix);
+ ic->ic_stats.is_crypto_delkey++;
+ /* XXX recovery? */
}
}
- m_freem(m0);
- return n0;
+ cipher_detach(key);
+ memset(key, 0, sizeof(*key));
+ key->wk_cipher = &ieee80211_cipher_none;
+ key->wk_private = cipher_attach(ic, key);
+ /* NB: cannot depend on key index to decide this */
+ if (&ic->ic_nw_keys[0] <= key &&
+ key < &ic->ic_nw_keys[IEEE80211_WEP_NKID])
+ key->wk_keyix = keyix; /* preserve shared key state */
+ else
+ key->wk_keyix = IEEE80211_KEYIX_NONE;
+ return 1;
+}
+
+/*
+ * Remove the specified key.
+ */
+int
+ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+ int status;
+
+ ieee80211_key_update_begin(ic);
+ status = _ieee80211_crypto_delkey(ic, key);
+ ieee80211_key_update_end(ic);
+ return status;
+}
- fail:
- m_freem(m0);
- m_freem(n0);
- return NULL;
+/*
+ * Clear the global key table.
+ */
+void
+ieee80211_crypto_delglobalkeys(struct ieee80211com *ic)
+{
+ int i;
+
+ ieee80211_key_update_begin(ic);
+ for (i = 0; i < IEEE80211_WEP_NKID; i++)
+ (void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]);
+ ieee80211_key_update_end(ic);
}
/*
- * CRC 32 -- routine from RFC 2083
+ * 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);
*/
+int
+ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key,
+ const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+{
+ const struct ieee80211_cipher *cip = key->wk_cipher;
-/* Table of CRCs of all 8-bit messages */
-static u_int32_t ieee80211_crc_table[256];
+ KASSERT(cip != NULL, ("No cipher!"));
-/* Make the table for a fast CRC. */
-static void
-ieee80211_crc_init(void)
+ /*
+ * 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,
+ "%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++;
+ return 0;
+ }
+ if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+ /* XXX nothing allocated, should not happen */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "%s: no key index; should not happen!\n", __func__);
+ ic->ic_stats.is_crypto_setkey_nokey++;
+ return 0;
+ }
+ return dev_key_set(ic, 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)
{
- u_int32_t c;
- int n, k;
-
- for (n = 0; n < 256; n++) {
- c = (u_int32_t)n;
- for (k = 0; k < 8; k++) {
- if (c & 1)
- c = 0xedb88320UL ^ (c >> 1);
- else
- c = c >> 1;
+ struct ieee80211_key *k;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_cipher *cip;
+ u_int8_t keyix;
+
+ /*
+ * Multicast traffic always uses the multicast key.
+ * Otherwise if a unicast key is set we use that and
+ * it is always key index 0. When no unicast key is
+ * set we fall back to the default transmit key.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+ ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
+ if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "%s: No default xmit key for frame to %s\n",
+ __func__, ether_sprintf(wh->i_addr1));
+ ic->ic_stats.is_tx_nodefkey++;
+ return NULL;
}
- ieee80211_crc_table[n] = c;
+ keyix = ic->ic_def_txkey;
+ k = &ic->ic_nw_keys[ic->ic_def_txkey];
+ } else {
+ keyix = 0;
+ k = &ni->ni_ucastkey;
}
+ cip = k->wk_cipher;
+ return (cip->ic_encap(k, m, keyix<<6) ? k : NULL);
}
/*
- * Update a running CRC with the bytes buf[0..len-1]--the CRC
- * should be initialized to all 1's, and the transmitted value
- * is the 1's complement of the final running CRC
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame that has the WEP/Privacy bit set.
*/
-
-static u_int32_t
-ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len)
+struct ieee80211_key *
+ieee80211_crypto_decap(struct ieee80211com *ic,
+ struct ieee80211_node *ni, struct mbuf *m)
{
- u_int8_t *endbuf;
+#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
+#define IEEE80211_WEP_MINLEN \
+ (sizeof(struct ieee80211_frame) + ETHER_HDR_LEN + \
+ IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN)
+ struct ieee80211_key *k;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_cipher *cip;
+ u_int8_t *ivp;
+ u_int8_t keyid;
+ int hdrlen;
+
+ /* NB: this minimum size data frame could be bigger */
+ if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
+ IEEE80211_DPRINTF(ic, 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? */
+ return NULL;
+ }
+
+ /*
+ * Locate the key. If unicast and there is no unicast
+ * key then we fall back to the key id in the header.
+ * This assumes unicast keys are only configured when
+ * the key id in the header is meaningless (typically 0).
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ hdrlen = ieee80211_hdrsize(wh);
+ ivp = mtod(m, u_int8_t *) + hdrlen; /* XXX contig */
+ keyid = ivp[IEEE80211_WEP_IVLEN];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+ ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none)
+ k = &ic->ic_nw_keys[keyid >> 6];
+ else
+ k = &ni->ni_ucastkey;
+
+ /*
+ * Insure crypto header is contiguous for all decap work.
+ */
+ cip = k->wk_cipher;
+ if (m->m_len < hdrlen + cip->ic_header &&
+ (m = m_pullup(m, hdrlen + cip->ic_header)) == NULL) {
+ IEEE80211_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 */
+ return 0;
+ }
- for (endbuf = buf + len; buf < endbuf; buf++)
- crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
- return crc;
+ return (cip->ic_decap(k, m) ? k : NULL);
+#undef IEEE80211_WEP_MINLEN
+#undef IEEE80211_WEP_HDRLEN
}
diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h
index 89b13dc..d7a238a 100644
--- a/sys/net80211/ieee80211_crypto.h
+++ b/sys/net80211/ieee80211_crypto.h
@@ -1,6 +1,7 @@
+/* $NetBSD: ieee80211_crypto.h,v 1.2 2003/09/14 01:14:55 dyoung Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -38,13 +39,184 @@
* 802.11 protocol crypto-related definitions.
*/
#define IEEE80211_KEYBUF_SIZE 16
+#define IEEE80211_MICBUF_SIZE (8+8) /* space for both tx+rx keys */
+/*
+ * Old WEP-style key. Deprecated.
+ */
struct ieee80211_wepkey {
- int wk_len;
- u_int8_t wk_key[IEEE80211_KEYBUF_SIZE];
+ u_int wk_len; /* key length in bytes */
+ u_int8_t wk_key[IEEE80211_KEYBUF_SIZE];
+};
+
+struct ieee80211_cipher;
+
+/*
+ * Crypto key state. There is sufficient room for all supported
+ * ciphers (see below). The underlying ciphers are handled
+ * separately through loadable cipher modules that register with
+ * the generic crypto support. A key has a reference to an instance
+ * of the cipher; any per-key state is hung off wk_private by the
+ * cipher when it is attached. Ciphers are automatically called
+ * to detach and cleanup any such state when the key is deleted.
+ *
+ * The generic crypto support handles encap/decap of cipher-related
+ * frame contents for both hardware- and software-based implementations.
+ * A key requiring software crypto support is automatically flagged and
+ * the cipher is expected to honor this and do the necessary work.
+ * Ciphers such as TKIP may also support mixed hardware/software
+ * encrypt/decrypt and MIC processing.
+ */
+/* XXX need key index typedef */
+/* XXX pack better? */
+/* XXX 48-bit rsc/tsc */
+struct ieee80211_key {
+ u_int8_t wk_keylen; /* key length in bytes */
+ u_int8_t wk_flags;
+#define IEEE80211_KEY_XMIT 0x01 /* key used for xmit */
+#define IEEE80211_KEY_RECV 0x02 /* key used for recv */
+#define IEEE80211_KEY_SWCRYPT 0x04 /* host-based encrypt/decrypt */
+#define IEEE80211_KEY_SWMIC 0x08 /* host-based enmic/demic */
+ u_int16_t wk_keyix; /* key index */
+ u_int8_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 */
+ u_int64_t wk_keyrsc; /* key receive sequence counter */
+ u_int64_t wk_keytsc; /* key transmit sequence counter */
+ const struct ieee80211_cipher *wk_cipher;
+ void *wk_private; /* private cipher state */
+};
+
+/*
+ * 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.
+ */
+#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_CKIP 5
+#define IEEE80211_CIPHER_NONE 6 /* pseudo value */
+
+#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1)
+
+#define IEEE80211_KEYIX_NONE ((u_int16_t) -1)
+
+#if defined(__KERNEL__) || defined(_KERNEL)
+
+struct ieee80211com;
+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];
+ u_int16_t cs_def_txkey; /* default/group tx key index */
+
+ int (*cs_key_alloc)(struct ieee80211com *,
+ const struct ieee80211_key *);
+ int (*cs_key_delete)(struct ieee80211com *,
+ const struct ieee80211_key *);
+ int (*cs_key_set)(struct ieee80211com *,
+ const struct ieee80211_key *,
+ const u_int8_t mac[IEEE80211_ADDR_LEN]);
+ void (*cs_key_update_begin)(struct ieee80211com *);
+ void (*cs_key_update_end)(struct ieee80211com *);
+};
+
+extern void ieee80211_crypto_attach(struct ieee80211com *);
+extern void ieee80211_crypto_detach(struct ieee80211com *);
+extern int ieee80211_crypto_newkey(struct ieee80211com *,
+ int cipher, struct ieee80211_key *);
+extern int ieee80211_crypto_delkey(struct ieee80211com *,
+ struct ieee80211_key *);
+extern int ieee80211_crypto_setkey(struct ieee80211com *,
+ struct ieee80211_key *, const u_int8_t macaddr[IEEE80211_ADDR_LEN]);
+extern void ieee80211_crypto_delglobalkeys(struct ieee80211com *);
+
+/*
+ * Template for a supported cipher. Ciphers register with the
+ * crypto code and are typically loaded as separate modules
+ * (the null cipher is always present).
+ * XXX may need refcnts
+ */
+struct ieee80211_cipher {
+ const char *ic_name; /* printable name */
+ u_int ic_cipher; /* IEEE80211_CIPHER_* */
+ u_int ic_header; /* size of privacy header (bytes) */
+ u_int ic_trailer; /* size of privacy trailer (bytes) */
+ u_int ic_miclen; /* size of mic trailer (bytes) */
+ void* (*ic_attach)(struct ieee80211com *, struct ieee80211_key *);
+ void (*ic_detach)(struct ieee80211_key *);
+ int (*ic_setkey)(struct ieee80211_key *);
+ int (*ic_encap)(struct ieee80211_key *, struct mbuf *,
+ u_int8_t keyid);
+ int (*ic_decap)(struct ieee80211_key *, struct mbuf *);
+ int (*ic_enmic)(struct ieee80211_key *, struct mbuf *);
+ int (*ic_demic)(struct ieee80211_key *, struct mbuf *);
};
+extern const struct ieee80211_cipher ieee80211_cipher_none;
+
+extern void ieee80211_crypto_register(const struct ieee80211_cipher *);
+extern void ieee80211_crypto_unregister(const struct ieee80211_cipher *);
+extern int ieee80211_crypto_available(u_int cipher);
-extern void ieee80211_crypto_attach(struct ifnet *);
-extern void ieee80211_crypto_detach(struct ifnet *);
-extern struct mbuf *ieee80211_wep_crypt(struct ifnet *, struct mbuf *, int);
+extern struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *,
+ struct ieee80211_node *, struct mbuf *);
+extern struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *,
+ struct ieee80211_node *, struct mbuf *);
+
+/*
+ * Check and remove any MIC.
+ */
+static __inline int
+ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k,
+ struct mbuf *m)
+{
+ const struct ieee80211_cipher *cip = k->wk_cipher;
+ return (cip->ic_miclen > 0 ? cip->ic_demic(k, m) : 1);
+}
+
+/*
+ * Add any MIC.
+ */
+static __inline int
+ieee80211_crypto_enmic(struct ieee80211com *ic,
+ struct ieee80211_key *k, struct mbuf *m)
+{
+ const struct ieee80211_cipher *cip = k->wk_cipher;
+ return (cip->ic_miclen > 0 ? cip->ic_enmic(k, m) : 1);
+}
+
+/*
+ * Reset key state to an unused state. The crypto
+ * key allocation mechanism insures other state (e.g.
+ * key data) is properly setup before a key is used.
+ */
+static __inline void
+ieee80211_crypto_resetkey(struct ieee80211com *ic,
+ struct ieee80211_key *k, u_int16_t ix)
+{
+ k->wk_cipher = &ieee80211_cipher_none;;
+ k->wk_private = k->wk_cipher->ic_attach(ic, k);
+ k->wk_keyix = ix;
+ k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+}
+
+/*
+ * Crypt-related notification methods.
+ */
+extern void ieee80211_notify_replay_failure(struct ieee80211com *,
+ const struct ieee80211_frame *, const struct ieee80211_key *,
+ u_int64_t rsc);
+extern void ieee80211_notify_michael_failure(struct ieee80211com *,
+ 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
new file mode 100644
index 0000000..b3373b2
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto_ccmp.c
@@ -0,0 +1,603 @@
+/*-
+ * Copyright (c) 2002-2004 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11i AES-CCMP crypto support.
+ *
+ * Part of this module is derived from similar code in the Host
+ * AP driver. The code is used with the consent of the author and
+ * it's license is included below.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <crypto/rijndael/rijndael.h>
+
+#define AES_BLOCK_LEN 16
+
+struct ccmp_ctx {
+ struct ieee80211com *cc_ic; /* for diagnostics */
+ rijndael_ctx cc_aes;
+};
+
+static void *ccmp_attach(struct ieee80211com *, 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 *, u_int8_t keyid);
+static int ccmp_decap(struct ieee80211_key *, struct mbuf *);
+static int ccmp_enmic(struct ieee80211_key *, struct mbuf *);
+static int ccmp_demic(struct ieee80211_key *, struct mbuf *);
+
+static const struct ieee80211_cipher ccmp = {
+ .ic_name = "AES-CCM",
+ .ic_cipher = IEEE80211_CIPHER_AES_CCM,
+ .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+ IEEE80211_WEP_EXTIVLEN,
+ .ic_trailer = IEEE80211_WEP_MICLEN,
+ .ic_miclen = 0,
+ .ic_attach = ccmp_attach,
+ .ic_detach = ccmp_detach,
+ .ic_setkey = ccmp_setkey,
+ .ic_encap = ccmp_encap,
+ .ic_decap = ccmp_decap,
+ .ic_enmic = ccmp_enmic,
+ .ic_demic = ccmp_demic,
+};
+
+static int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
+static int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn,
+ struct mbuf *, int hdrlen);
+
+static void *
+ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+ struct ccmp_ctx *ctx;
+
+ MALLOC(ctx, struct ccmp_ctx *, sizeof(struct ccmp_ctx),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ctx == NULL) {
+ ic->ic_stats.is_crypto_nomem++;
+ return NULL;
+ }
+ ctx->cc_ic = ic;
+ return ctx;
+}
+
+static void
+ccmp_detach(struct ieee80211_key *k)
+{
+ struct ccmp_ctx *ctx = k->wk_private;
+
+ FREE(ctx, M_DEVBUF);
+}
+
+static int
+ccmp_setkey(struct ieee80211_key *k)
+{
+ struct ccmp_ctx *ctx = k->wk_private;
+
+ if (k->wk_keylen != (128/NBBY)) {
+ IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+ "%s: Invalid key length %u, expecting %u\n",
+ __func__, k->wk_keylen, 128/NBBY);
+ return 0;
+ }
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+ rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY);
+ return 1;
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+ccmp_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+{
+ u_int8_t *ivp;
+ int hdrlen;
+
+ hdrlen = ieee80211_hdrsize(mtod(m, void *));
+
+ /*
+ * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+ */
+ M_PREPEND(m, ccmp.ic_header, M_NOWAIT);
+ if (m == NULL)
+ return 0;
+ ivp = mtod(m, u_int8_t *);
+ ovbcopy(ivp + ccmp.ic_header, ivp, hdrlen);
+ ivp += hdrlen;
+
+ k->wk_keytsc++; /* XXX wrap at 48 bits */
+ ivp[0] = k->wk_keytsc >> 0; /* PN0 */
+ ivp[1] = k->wk_keytsc >> 8; /* PN1 */
+ ivp[2] = 0; /* Reserved */
+ ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */
+ ivp[4] = k->wk_keytsc >> 16; /* PN2 */
+ ivp[5] = k->wk_keytsc >> 24; /* PN3 */
+ ivp[6] = k->wk_keytsc >> 32; /* PN4 */
+ ivp[7] = k->wk_keytsc >> 40; /* PN5 */
+
+ /*
+ * Finally, do software encrypt if neeed.
+ */
+ if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+ !ccmp_encrypt(k, m, hdrlen))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+static int
+ccmp_enmic(struct ieee80211_key *k, struct mbuf *m)
+{
+
+ return 1;
+}
+
+static __inline uint64_t
+READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
+{
+ uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
+ uint16_t iv16 = (b4 << 0) | (b5 << 8);
+ return (((uint64_t)iv16) << 32) | iv32;
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame. The specified key should be correct but
+ * is also verified.
+ */
+static int
+ccmp_decap(struct ieee80211_key *k, struct mbuf *m)
+{
+ struct ccmp_ctx *ctx = k->wk_private;
+ struct ieee80211_frame *wh;
+ uint8_t *ivp;
+ uint64_t pn;
+ int hdrlen;
+
+ /*
+ * Header should have extended IV and sequence number;
+ * verify the former and validate the latter.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ hdrlen = ieee80211_hdrsize(wh);
+ ivp = mtod(m, uint8_t *) + hdrlen;
+ if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+ /*
+ * 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++;
+ return 0;
+ }
+ pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
+ if (pn <= k->wk_keyrsc) {
+ /*
+ * Replay violation.
+ */
+ ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn);
+ ctx->cc_ic->ic_stats.is_rx_ccmpreplay++;
+ return 0;
+ }
+
+ /*
+ * Check if the device handled the decrypt in hardware.
+ * If so we just strip the header; otherwise we need to
+ * handle the decrypt in software. Note that for the
+ * latter we leave the header in place for use in the
+ * decryption work.
+ */
+ if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+ !ccmp_decrypt(k, pn, m, hdrlen))
+ return 0;
+
+ /*
+ * Copy up 802.11 header and strip crypto bits.
+ */
+ ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + ccmp.ic_header, hdrlen);
+ m_adj(m, ccmp.ic_header);
+ m_adj(m, -ccmp.ic_trailer);
+
+ /*
+ * Ok to update rsc now.
+ */
+ k->wk_keyrsc = pn;
+
+ return 1;
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+static int
+ccmp_demic(struct ieee80211_key *k, struct mbuf *m)
+{
+ return 1;
+}
+
+static __inline void
+xor_block(uint8_t *b, const uint8_t *a, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ b[i] ^= a[i];
+}
+
+/*
+ * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ */
+
+static void
+ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh,
+ u_int64_t pn, size_t dlen,
+ uint8_t b0[AES_BLOCK_LEN], uint8_t aad[2 * AES_BLOCK_LEN],
+ uint8_t auth[AES_BLOCK_LEN], uint8_t s0[AES_BLOCK_LEN])
+{
+#define IS_4ADDRESS(wh) \
+ ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+#define IS_QOS_DATA(wh) IEEE80211_QOS_HAS_SEQ(wh)
+
+ /* CCM Initial Block:
+ * Flag (Include authentication header, M=3 (8-octet MIC),
+ * L=1 (2-octet Dlen))
+ * Nonce: 0x00 | A2 | PN
+ * Dlen */
+ b0[0] = 0x59;
+ /* NB: b0[1] set below */
+ IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2);
+ b0[8] = pn >> 40;
+ b0[9] = pn >> 32;
+ b0[10] = pn >> 24;
+ b0[11] = pn >> 16;
+ b0[12] = pn >> 8;
+ b0[13] = pn >> 0;
+ b0[14] = (dlen >> 8) & 0xff;
+ b0[15] = dlen & 0xff;
+
+ /* AAD:
+ * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
+ * A1 | A2 | A3
+ * SC with bits 4..15 (seq#) masked to zero
+ * A4 (if present)
+ * QC (if present)
+ */
+ aad[0] = 0; /* AAD length >> 8 */
+ /* NB: aad[1] set below */
+ aad[2] = wh->i_fc[0] & 0x8f; /* XXX magic #s */
+ aad[3] = wh->i_fc[1] & 0xc7; /* XXX magic #s */
+ /* NB: we know 3 addresses are contiguous */
+ memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
+ aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
+ aad[23] = 0; /* all bits masked */
+ /*
+ * Construct variable-length portion of AAD based
+ * on whether this is a 4-address frame/QOS frame.
+ * We always zero-pad to 32 bytes before running it
+ * through the cipher.
+ *
+ * We also fill in the priority bits of the CCM
+ * initial block as we know whether or not we have
+ * a QOS frame.
+ */
+ if (IS_4ADDRESS(wh)) {
+ IEEE80211_ADDR_COPY(aad + 24,
+ ((struct ieee80211_frame_addr4 *)wh)->i_addr4);
+ if (IS_QOS_DATA(wh)) {
+ struct ieee80211_qosframe_addr4 *qwh4 =
+ (struct ieee80211_qosframe_addr4 *) wh;
+ aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */
+ aad[31] = 0;
+ b0[1] = aad[30];
+ aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
+ } else {
+ *(u_int16_t *)&aad[30] = 0;
+ b0[1] = 0;
+ aad[1] = 22 + IEEE80211_ADDR_LEN;
+ }
+ } else {
+ if (IS_QOS_DATA(wh)) {
+ struct ieee80211_qosframe *qwh =
+ (struct ieee80211_qosframe*) wh;
+ aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */
+ aad[25] = 0;
+ b0[1] = aad[24];
+ aad[1] = 22 + 2;
+ } else {
+ *(u_int16_t *)&aad[24] = 0;
+ b0[1] = 0;
+ aad[1] = 22;
+ }
+ *(u_int16_t *)&aad[26] = 0;
+ *(u_int32_t *)&aad[28] = 0;
+ }
+
+ /* Start with the first block and AAD */
+ rijndael_encrypt(ctx, b0, auth);
+ xor_block(auth, aad, AES_BLOCK_LEN);
+ rijndael_encrypt(ctx, auth, auth);
+ xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
+ rijndael_encrypt(ctx, auth, auth);
+ b0[0] &= 0x07;
+ b0[14] = b0[15] = 0;
+ rijndael_encrypt(ctx, b0, s0);
+#undef IS_QOS_DATA
+#undef IS_4ADDRESS
+}
+
+#define CCMP_ENCRYPT(_i, _b, _b0, _pos, _e, _len) do { \
+ /* Authentication */ \
+ xor_block(_b, _pos, _len); \
+ rijndael_encrypt(&ctx->cc_aes, _b, _b); \
+ /* Encryption, with counter */ \
+ _b0[14] = (_i >> 8) & 0xff; \
+ _b0[15] = _i & 0xff; \
+ rijndael_encrypt(&ctx->cc_aes, _b0, _e); \
+ xor_block(_pos, _e, _len); \
+} while (0)
+
+static int
+ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
+{
+ struct ccmp_ctx *ctx = key->wk_private;
+ struct ieee80211_frame *wh;
+ struct mbuf *m = m0;
+ int data_len, i;
+ uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN],
+ e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN];
+ uint8_t *pos;
+ u_int space;
+
+ ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header);
+ ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc,
+ data_len, b0, aad, b, s0);
+
+ i = 1;
+ pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header;
+ /* NB: assumes header is entirely in first mbuf */
+ space = m->m_len - (hdrlen + ccmp.ic_header);
+ for (;;) {
+ if (space > data_len)
+ space = data_len;
+ /*
+ * Do full blocks.
+ */
+ while (space >= AES_BLOCK_LEN) {
+ CCMP_ENCRYPT(i, b, b0, pos, e, AES_BLOCK_LEN);
+ pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;
+ data_len -= AES_BLOCK_LEN;
+ i++;
+ }
+ if (data_len <= 0) /* no more data */
+ break;
+ m = m->m_next;
+ if (m == NULL) { /* last buffer */
+ if (space != 0) {
+ /*
+ * Short last block.
+ */
+ CCMP_ENCRYPT(i, b, b0, pos, e, space);
+ }
+ break;
+ }
+ if (space != 0) {
+ uint8_t *pos_next;
+ u_int space_next;
+ u_int len;
+
+ /*
+ * Block straddles buffers, split references. We
+ * do not handle splits that require >2 buffers.
+ */
+ pos_next = mtod(m, uint8_t *);
+ len = min(data_len, AES_BLOCK_LEN);
+ space_next = len > space ? len - space : 0;
+ KASSERT(m->m_len >= space_next,
+ ("not enough data in following buffer, "
+ "m_len %u need %u\n", m->m_len, space_next));
+
+ xor_block(b+space, pos_next, space_next);
+ CCMP_ENCRYPT(i, b, b0, pos, e, space);
+ xor_block(pos_next, e+space, space_next);
+ data_len -= len;
+ /* XXX could check for data_len <= 0 */
+ i++;
+
+ pos = pos_next + space_next;
+ space = m->m_len - space_next;
+ } else {
+ /*
+ * Setup for next buffer.
+ */
+ pos = mtod(m, uint8_t *);
+ space = m->m_len;
+ }
+ }
+ /* tack on MIC */
+ xor_block(b, s0, ccmp.ic_trailer);
+ return m_append(m0, ccmp.ic_trailer, b);
+}
+#undef CCMP_ENCRYPT
+
+#define CCMP_DECRYPT(_i, _b, _b0, _pos, _a, _len) do { \
+ /* Decrypt, with counter */ \
+ _b0[14] = (_i >> 8) & 0xff; \
+ _b0[15] = _i & 0xff; \
+ rijndael_encrypt(&ctx->cc_aes, _b0, _b); \
+ xor_block(_pos, _b, _len); \
+ /* Authentication */ \
+ xor_block(_a, _pos, _len); \
+ rijndael_encrypt(&ctx->cc_aes, _a, _a); \
+} while (0)
+
+static int
+ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen)
+{
+ struct ccmp_ctx *ctx = key->wk_private;
+ struct ieee80211_frame *wh;
+ uint8_t aad[2 * AES_BLOCK_LEN];
+ uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN];
+ uint8_t mic[AES_BLOCK_LEN];
+ size_t data_len;
+ int i;
+ uint8_t *pos;
+ u_int space;
+
+ ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer);
+ ccmp_init_blocks(&ctx->cc_aes, wh, pn, data_len, b0, aad, a, b);
+ m_copydata(m, m->m_pkthdr.len - ccmp.ic_trailer, ccmp.ic_trailer, mic);
+ xor_block(mic, b, ccmp.ic_trailer);
+
+ i = 1;
+ pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header;
+ space = m->m_len - (hdrlen + ccmp.ic_header);
+ for (;;) {
+ if (space > data_len)
+ space = data_len;
+ while (space >= AES_BLOCK_LEN) {
+ CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN);
+ pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;
+ data_len -= AES_BLOCK_LEN;
+ i++;
+ }
+ if (data_len <= 0) /* no more data */
+ break;
+ m = m->m_next;
+ if (m == NULL) { /* last buffer */
+ if (space != 0) /* short last block */
+ CCMP_DECRYPT(i, b, b0, pos, a, space);
+ break;
+ }
+ if (space != 0) {
+ uint8_t *pos_next;
+ u_int space_next;
+ u_int len;
+
+ /*
+ * Block straddles buffers, split references. We
+ * do not handle splits that require >2 buffers.
+ */
+ pos_next = mtod(m, uint8_t *);
+ len = min(data_len, AES_BLOCK_LEN);
+ space_next = len > space ? len - space : 0;
+ KASSERT(m->m_len >= space_next,
+ ("not enough data in following buffer, "
+ "m_len %u need %u\n", m->m_len, space_next));
+
+ xor_block(b+space, pos_next, space_next);
+ CCMP_DECRYPT(i, b, b0, pos, a, space);
+ xor_block(pos_next, b+space, space_next);
+ data_len -= len;
+ i++;
+
+ pos = pos_next + space_next;
+ space = m->m_len - space_next;
+ } else {
+ /*
+ * Setup for next buffer.
+ */
+ pos = mtod(m, uint8_t *);
+ space = m->m_len;
+ }
+ }
+ if (memcmp(mic, a, ccmp.ic_trailer) != 0) {
+ IEEE80211_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++;
+ return 0;
+ }
+ return 1;
+}
+#undef CCMP_DECRYPT
+
+/*
+ * Module glue.
+ */
+static int
+ccmp_modevent(module_t mod, int type, void *unused)
+{
+ switch (type) {
+ case MOD_LOAD:
+ ieee80211_crypto_register(&ccmp);
+ return 0;
+ case MOD_UNLOAD:
+ ieee80211_crypto_unregister(&ccmp);
+ return 0;
+ }
+ return EINVAL;
+}
+
+static moduledata_t ccmp_mod = {
+ "wlan_ccmp",
+ ccmp_modevent,
+ 0
+};
+DECLARE_MODULE(wlan_ccmp, ccmp_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_ccmp, 1);
+MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1);
diff --git a/sys/net80211/ieee80211_crypto_none.c b/sys/net80211/ieee80211_crypto_none.c
new file mode 100644
index 0000000..6d53ea9
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto_none.c
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2002-2004 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11 NULL crypto support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+static void *none_attach(struct ieee80211com *, 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 *, u_int8_t);
+static int none_decap(struct ieee80211_key *, struct mbuf *);
+static int none_enmic(struct ieee80211_key *, struct mbuf *);
+static int none_demic(struct ieee80211_key *, struct mbuf *);
+
+const struct ieee80211_cipher ieee80211_cipher_none = {
+ .ic_name = "NONE",
+ .ic_cipher = IEEE80211_CIPHER_NONE,
+ .ic_header = 0,
+ .ic_trailer = 0,
+ .ic_miclen = 0,
+ .ic_attach = none_attach,
+ .ic_detach = none_detach,
+ .ic_setkey = none_setkey,
+ .ic_encap = none_encap,
+ .ic_decap = none_decap,
+ .ic_enmic = none_enmic,
+ .ic_demic = none_demic,
+};
+
+static void *
+none_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+ return ic; /* for diagnostics+stats */
+}
+
+static void
+none_detach(struct ieee80211_key *k)
+{
+ (void) k;
+}
+
+static int
+none_setkey(struct ieee80211_key *k)
+{
+ (void) k;
+ return 1;
+}
+
+static int
+none_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+{
+ struct ieee80211com *ic = k->wk_private;
+#ifdef IEEE80211_DEBUG
+ struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+#endif
+
+ /*
+ * The specified key is not setup; this can
+ * happen, at least, when changing keys.
+ */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "[%s] key (id %u) is invalid\n",
+ ether_sprintf(wh->i_addr1), keyid>>6);
+ ic->ic_stats.is_tx_badcipher++;
+ return 0;
+}
+
+static int
+none_decap(struct ieee80211_key *k, struct mbuf *m)
+{
+ struct ieee80211com *ic = k->wk_private;
+#ifdef IEEE80211_DEBUG
+ struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+ const u_int8_t *ivp = (const u_int8_t *)&wh[1];
+#endif
+
+ /*
+ * The specified key is not setup; this can
+ * happen, at least, when changing keys.
+ */
+ /* XXX useful to know dst too */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "[%s] key (id %u) is invalid\n",
+ ether_sprintf(wh->i_addr2), ivp[IEEE80211_WEP_IVLEN] >> 6);
+ ic->ic_stats.is_rx_badkeyid++;
+ return 0;
+}
+
+static int
+none_enmic(struct ieee80211_key *k, struct mbuf *m)
+{
+ struct ieee80211com *ic = k->wk_private;
+
+ ic->ic_stats.is_tx_badcipher++;
+ return 0;
+}
+
+static int
+none_demic(struct ieee80211_key *k, struct mbuf *m)
+{
+ struct ieee80211com *ic = k->wk_private;
+
+ ic->ic_stats.is_rx_badkeyid++;
+ return 0;
+}
diff --git a/sys/net80211/ieee80211_crypto_tkip.c b/sys/net80211/ieee80211_crypto_tkip.c
new file mode 100644
index 0000000..89ff8ba
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto_tkip.c
@@ -0,0 +1,987 @@
+/*-
+ * Copyright (c) 2002-2004 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11i TKIP crypto support.
+ *
+ * Part of this module is derived from similar code in the Host
+ * AP driver. The code is used with the consent of the author and
+ * it's license is included below.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/endian.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+static void *tkip_attach(struct ieee80211com *, 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, u_int8_t keyid);
+static int tkip_enmic(struct ieee80211_key *, struct mbuf *);
+static int tkip_decap(struct ieee80211_key *, struct mbuf *);
+static int tkip_demic(struct ieee80211_key *, struct mbuf *);
+
+static const struct ieee80211_cipher tkip = {
+ .ic_name = "TKIP",
+ .ic_cipher = IEEE80211_CIPHER_TKIP,
+ .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+ IEEE80211_WEP_EXTIVLEN,
+ .ic_trailer = IEEE80211_WEP_CRCLEN,
+ .ic_miclen = IEEE80211_WEP_MICLEN,
+ .ic_attach = tkip_attach,
+ .ic_detach = tkip_detach,
+ .ic_setkey = tkip_setkey,
+ .ic_encap = tkip_encap,
+ .ic_decap = tkip_decap,
+ .ic_enmic = tkip_enmic,
+ .ic_demic = tkip_demic,
+};
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t __u32;
+typedef uint32_t u32;
+#define memmove(dst, src, n) ovbcopy(src, dst, n)
+
+struct tkip_ctx {
+ struct ieee80211com *tc_ic; /* for diagnostics */
+
+ u16 tx_ttak[5];
+ int tx_phase1_done;
+ u8 tx_rc4key[16]; /* XXX for test module; make locals? */
+
+ u16 rx_ttak[5];
+ int rx_phase1_done;
+ u8 rx_rc4key[16]; /* XXX for test module; make locals? */
+ uint64_t rx_rsc; /* held until MIC verified */
+};
+
+static void michael_mic(struct tkip_ctx *, const u8 *key,
+ struct mbuf *m, u_int off, size_t data_len,
+ u8 mic[IEEE80211_WEP_MICLEN]);
+static int tkip_encrypt(struct tkip_ctx *, struct ieee80211_key *,
+ struct mbuf *, int hdr_len);
+static int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *,
+ struct mbuf *, int hdr_len);
+
+static void *
+tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+ struct tkip_ctx *ctx;
+
+ MALLOC(ctx, struct tkip_ctx *, sizeof(struct tkip_ctx),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ctx == NULL) {
+ ic->ic_stats.is_crypto_nomem++;
+ return NULL;
+ }
+
+ ctx->tc_ic = ic;
+ return ctx;
+}
+
+static void
+tkip_detach(struct ieee80211_key *k)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+
+ FREE(ctx, M_DEVBUF);
+}
+
+static int
+tkip_setkey(struct ieee80211_key *k)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+
+ if (k->wk_keylen != (128/NBBY)) {
+ (void) ctx; /* XXX */
+ IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+ "%s: Invalid key length %u, expecting %u\n",
+ __func__, k->wk_keylen, 128/NBBY);
+ return 0;
+ }
+ k->wk_keytsc = 1; /* TSC starts at 1 */
+ return 1;
+}
+
+/*
+ * Add privacy headers and do any s/w encryption required.
+ */
+static int
+tkip_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+ struct ieee80211com *ic = ctx->tc_ic;
+ u_int8_t *ivp;
+ int hdrlen;
+
+ /*
+ * Handle TKIP counter measures requirement.
+ */
+ if (ic->ic_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++;
+ return 0;
+ }
+ hdrlen = ieee80211_hdrsize(mtod(m, void *));
+
+ /*
+ * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+ */
+ M_PREPEND(m, tkip.ic_header, M_NOWAIT);
+ if (m == NULL)
+ return 0;
+ ivp = mtod(m, u_int8_t *);
+ memmove(ivp, ivp + tkip.ic_header, hdrlen);
+ ivp += hdrlen;
+
+ ivp[0] = k->wk_keytsc >> 8; /* TSC1 */
+ ivp[1] = (ivp[0] | 0x20) & 0x7f; /* WEP seed */
+ ivp[2] = k->wk_keytsc >> 0; /* TSC0 */
+ ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */
+ ivp[4] = k->wk_keytsc >> 16; /* TSC2 */
+ ivp[5] = k->wk_keytsc >> 24; /* TSC3 */
+ ivp[6] = k->wk_keytsc >> 32; /* TSC4 */
+ ivp[7] = k->wk_keytsc >> 40; /* TSC5 */
+
+ /*
+ * Finally, do software encrypt if neeed.
+ */
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+ if (!tkip_encrypt(ctx, k, m, hdrlen))
+ return 0;
+ /* NB: tkip_encrypt handles wk_keytsc */
+ } else
+ k->wk_keytsc++; /* XXX wrap at 48 bits */
+
+ return 1;
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+static int
+tkip_enmic(struct ieee80211_key *k, struct mbuf *m)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+
+ if (k->wk_flags & IEEE80211_KEY_SWMIC) {
+ struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+ int hdrlen = ieee80211_hdrsize(wh);
+ uint8_t mic[IEEE80211_WEP_MICLEN];
+
+ ctx->tc_ic->ic_stats.is_crypto_tkipenmic++;
+
+ michael_mic(ctx, k->wk_txmic,
+ m, hdrlen, m->m_pkthdr.len - hdrlen, mic);
+ return m_append(m, tkip.ic_miclen, mic);
+ }
+ return 1;
+}
+
+static __inline uint64_t
+READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
+{
+ uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
+ uint16_t iv16 = (b4 << 0) | (b5 << 8);
+ return (((uint64_t)iv16) << 32) | iv32;
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame. If necessary, decrypt the frame using
+ * the specified key.
+ */
+static int
+tkip_decap(struct ieee80211_key *k, struct mbuf *m)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+ struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211_frame *wh;
+ uint8_t *ivp;
+ int hdrlen;
+
+ /*
+ * Header should have extended IV and sequence number;
+ * verify the former and validate the latter.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+ hdrlen = ieee80211_hdrsize(wh);
+ ivp = mtod(m, uint8_t *) + hdrlen;
+ if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+ /*
+ * 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++;
+ 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++;
+ return 0;
+ }
+
+ ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5],
+ ivp[6], ivp[7]);
+ if (ctx->rx_rsc <= k->wk_keyrsc) {
+ /*
+ * 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++;
+ return 0;
+ }
+ /*
+ * NB: We can't update the rsc in the key until MIC is verified.
+ *
+ * We assume we are not preempted between doing the check above
+ * and updating wk_keyrsc when stripping the MIC in tkip_demic.
+ * Otherwise we might process another packet and discard it as
+ * a replay.
+ */
+
+ /*
+ * Check if the device handled the decrypt in hardware.
+ * If so we just strip the header; otherwise we need to
+ * handle the decrypt in software.
+ */
+ if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+ !tkip_decrypt(ctx, k, m, hdrlen))
+ return 0;
+
+ /*
+ * Copy up 802.11 header and strip crypto bits.
+ */
+ memmove(mtod(m, uint8_t *) + tkip.ic_header, mtod(m, void *), hdrlen);
+ m_adj(m, tkip.ic_header);
+ m_adj(m, -tkip.ic_trailer);
+
+ return 1;
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+static int
+tkip_demic(struct ieee80211_key *k, struct mbuf *m)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+
+ if (k->wk_flags & IEEE80211_KEY_SWMIC) {
+ struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+ int hdrlen = ieee80211_hdrsize(wh);
+ u8 mic[IEEE80211_WEP_MICLEN];
+ u8 mic0[IEEE80211_WEP_MICLEN];
+
+ ctx->tc_ic->ic_stats.is_crypto_tkipdemic++;
+
+ michael_mic(ctx, k->wk_rxmic,
+ m, hdrlen, m->m_pkthdr.len - (hdrlen + tkip.ic_miclen),
+ mic);
+ m_copydata(m, m->m_pkthdr.len - tkip.ic_miclen,
+ tkip.ic_miclen, mic0);
+ if (memcmp(mic, mic0, tkip.ic_miclen)) {
+ /* NB: 802.11 layer handles statistic and debug msg */
+ ieee80211_notify_michael_failure(ctx->tc_ic, wh,
+ k->wk_keyix);
+ return 0;
+ }
+ }
+ /*
+ * Strip MIC from the tail.
+ */
+ m_adj(m, -tkip.ic_miclen);
+
+ /*
+ * Ok to update rsc now that MIC has been verified.
+ */
+ k->wk_keyrsc = ctx->rx_rsc;
+
+ return 1;
+}
+
+/*
+ * Host AP crypt: host-based TKIP encryption implementation for Host AP driver
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ */
+
+static const __u32 crc32_table[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
+
+static __inline u16 RotR1(u16 val)
+{
+ return (val >> 1) | (val << 15);
+}
+
+static __inline u8 Lo8(u16 val)
+{
+ return val & 0xff;
+}
+
+static __inline u8 Hi8(u16 val)
+{
+ return val >> 8;
+}
+
+static __inline u16 Lo16(u32 val)
+{
+ return val & 0xffff;
+}
+
+static __inline u16 Hi16(u32 val)
+{
+ return val >> 16;
+}
+
+static __inline u16 Mk16(u8 hi, u8 lo)
+{
+ return lo | (((u16) hi) << 8);
+}
+
+static __inline u16 Mk16_le(const u16 *v)
+{
+ return le16toh(*v);
+}
+
+static const u16 Sbox[256] = {
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+static __inline u16 _S_(u16 v)
+{
+ u16 t = Sbox[Hi8(v)];
+ return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+#define PHASE1_LOOP_COUNT 8
+
+static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32)
+{
+ int i, j;
+
+ /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+ TTAK[0] = Lo16(IV32);
+ TTAK[1] = Hi16(IV32);
+ TTAK[2] = Mk16(TA[1], TA[0]);
+ TTAK[3] = Mk16(TA[3], TA[2]);
+ TTAK[4] = Mk16(TA[5], TA[4]);
+
+ for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+ j = 2 * (i & 1);
+ TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+ TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+ TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+ TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+ TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+ }
+}
+
+#ifndef _BYTE_ORDER
+#error "Don't know native byte order"
+#endif
+
+static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
+ u16 IV16)
+{
+ /* Make temporary area overlap WEP seed so that the final copy can be
+ * avoided on little endian hosts. */
+ u16 *PPK = (u16 *) &WEPSeed[4];
+
+ /* Step 1 - make copy of TTAK and bring in TSC */
+ PPK[0] = TTAK[0];
+ PPK[1] = TTAK[1];
+ PPK[2] = TTAK[2];
+ PPK[3] = TTAK[3];
+ PPK[4] = TTAK[4];
+ PPK[5] = TTAK[4] + IV16;
+
+ /* Step 2 - 96-bit bijective mixing using S-box */
+ PPK[0] += _S_(PPK[5] ^ Mk16_le((const u16 *) &TK[0]));
+ PPK[1] += _S_(PPK[0] ^ Mk16_le((const u16 *) &TK[2]));
+ PPK[2] += _S_(PPK[1] ^ Mk16_le((const u16 *) &TK[4]));
+ PPK[3] += _S_(PPK[2] ^ Mk16_le((const u16 *) &TK[6]));
+ PPK[4] += _S_(PPK[3] ^ Mk16_le((const u16 *) &TK[8]));
+ PPK[5] += _S_(PPK[4] ^ Mk16_le((const u16 *) &TK[10]));
+
+ PPK[0] += RotR1(PPK[5] ^ Mk16_le((const u16 *) &TK[12]));
+ PPK[1] += RotR1(PPK[0] ^ Mk16_le((const u16 *) &TK[14]));
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+
+ /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+ * WEPSeed[0..2] is transmitted as WEP IV */
+ WEPSeed[0] = Hi8(IV16);
+ WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+ WEPSeed[2] = Lo8(IV16);
+ WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((const u16 *) &TK[0])) >> 1);
+
+#if _BYTE_ORDER == _BIG_ENDIAN
+ {
+ int i;
+ for (i = 0; i < 6; i++)
+ PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
+ }
+#endif
+}
+
+static void
+wep_encrypt(u8 *key, struct mbuf *m0, u_int off, size_t data_len,
+ uint8_t icv[IEEE80211_WEP_CRCLEN])
+{
+ u32 i, j, k, crc;
+ size_t buflen;
+ u8 S[256];
+ u8 *pos;
+ struct mbuf *m;
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+
+ /* Setup RC4 state */
+ for (i = 0; i < 256; i++)
+ S[i] = i;
+ j = 0;
+ for (i = 0; i < 256; i++) {
+ j = (j + S[i] + key[i & 0x0f]) & 0xff;
+ S_SWAP(i, j);
+ }
+
+ /* Compute CRC32 over unencrypted data and apply RC4 to data */
+ crc = ~0;
+ i = j = 0;
+ m = m0;
+ pos = mtod(m, uint8_t *) + off;
+ buflen = m->m_len - off;
+ for (;;) {
+ if (buflen > data_len)
+ buflen = data_len;
+ data_len -= buflen;
+ for (k = 0; k < buflen; k++) {
+ crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ *pos++ ^= S[(S[i] + S[j]) & 0xff];
+ }
+ m = m->m_next;
+ if (m == NULL) {
+ KASSERT(data_len == 0,
+ ("out of buffers with data_len %u\n", data_len));
+ break;
+ }
+ pos = mtod(m, uint8_t *);
+ buflen = m->m_len;
+ }
+ crc = ~crc;
+
+ /* Append little-endian CRC32 and encrypt it to produce ICV */
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ icv[k] ^= S[(S[i] + S[j]) & 0xff];
+ }
+}
+
+static int
+wep_decrypt(u8 *key, struct mbuf *m, u_int off, size_t data_len)
+{
+ u32 i, j, k, crc;
+ u8 S[256];
+ u8 *pos, icv[4];
+ size_t buflen;
+
+ /* Setup RC4 state */
+ for (i = 0; i < 256; i++)
+ S[i] = i;
+ j = 0;
+ for (i = 0; i < 256; i++) {
+ j = (j + S[i] + key[i & 0x0f]) & 0xff;
+ S_SWAP(i, j);
+ }
+
+ /* Apply RC4 to data and compute CRC32 over decrypted data */
+ crc = ~0;
+ i = j = 0;
+ pos = mtod(m, uint8_t *) + off;
+ buflen = m->m_len - off;
+ for (;;) {
+ if (buflen > data_len)
+ buflen = data_len;
+ data_len -= buflen;
+ for (k = 0; k < buflen; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ *pos ^= S[(S[i] + S[j]) & 0xff];
+ crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
+ pos++;
+ }
+ m = m->m_next;
+ if (m == NULL) {
+ KASSERT(data_len == 0,
+ ("out of buffers with data_len %u\n", data_len));
+ break;
+ }
+ pos = mtod(m, uint8_t *);
+ buflen = m->m_len;
+ }
+ crc = ~crc;
+
+ /* Encrypt little-endian CRC32 and verify that it matches with the
+ * received ICV */
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ for (k = 0; k < 4; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
+ /* ICV mismatch - drop frame */
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static __inline u32 rotl(u32 val, int bits)
+{
+ return (val << bits) | (val >> (32 - bits));
+}
+
+
+static __inline u32 rotr(u32 val, int bits)
+{
+ return (val >> bits) | (val << (32 - bits));
+}
+
+
+static __inline u32 xswap(u32 val)
+{
+ return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
+}
+
+
+#define michael_block(l, r) \
+do { \
+ r ^= rotl(l, 17); \
+ l += r; \
+ r ^= xswap(l); \
+ l += r; \
+ r ^= rotl(l, 3); \
+ l += r; \
+ r ^= rotr(l, 2); \
+ l += r; \
+} while (0)
+
+
+static __inline u32 get_le32_split(u8 b0, u8 b1, u8 b2, u8 b3)
+{
+ return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
+}
+
+static __inline u32 get_le32(const u8 *p)
+{
+ return get_le32_split(p[0], p[1], p[2], p[3]);
+}
+
+
+static __inline void put_le32(u8 *p, u32 v)
+{
+ p[0] = v;
+ p[1] = v >> 8;
+ p[2] = v >> 16;
+ p[3] = v >> 24;
+}
+
+/*
+ * Craft pseudo header used to calculate the MIC.
+ */
+static void
+michael_mic_hdr(const struct ieee80211_frame *wh0, uint8_t hdr[16])
+{
+ const struct ieee80211_frame_addr4 *wh =
+ (const struct ieee80211_frame_addr4 *) wh0;
+
+ switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+ case IEEE80211_FC1_DIR_NODS:
+ IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */
+ IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2);
+ break;
+ case IEEE80211_FC1_DIR_TODS:
+ IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */
+ IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2);
+ break;
+ case IEEE80211_FC1_DIR_FROMDS:
+ IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */
+ IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr3);
+ break;
+ case IEEE80211_FC1_DIR_DSTODS:
+ IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */
+ IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr4);
+ break;
+ }
+
+ hdr[12] = 0; /* XXX qos priority */
+ hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+static void
+michael_mic(struct tkip_ctx *ctx, const u8 *key,
+ struct mbuf *m, u_int off, size_t data_len,
+ u8 mic[IEEE80211_WEP_MICLEN])
+{
+ uint8_t hdr[16];
+ u32 l, r;
+ const uint8_t *data;
+ u_int space;
+
+ michael_mic_hdr(mtod(m, struct ieee80211_frame *), hdr);
+
+ l = get_le32(key);
+ r = get_le32(key + 4);
+
+ /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */
+ l ^= get_le32(hdr);
+ michael_block(l, r);
+ l ^= get_le32(&hdr[4]);
+ michael_block(l, r);
+ l ^= get_le32(&hdr[8]);
+ michael_block(l, r);
+ l ^= get_le32(&hdr[12]);
+ michael_block(l, r);
+
+ /* first buffer has special handling */
+ data = mtod(m, const uint8_t *) + off;
+ space = m->m_len - off;
+ for (;;) {
+ if (space > data_len)
+ space = data_len;
+ /* collect 32-bit blocks from current buffer */
+ while (space >= sizeof(uint32_t)) {
+ l ^= get_le32(data);
+ michael_block(l, r);
+ data += sizeof(uint32_t), space -= sizeof(uint32_t);
+ data_len -= sizeof(uint32_t);
+ }
+ if (data_len < sizeof(uint32_t))
+ break;
+ m = m->m_next;
+ if (m == NULL) {
+ KASSERT(0, ("out of data, data_len %u\n", data_len));
+ break;
+ }
+ if (space != 0) {
+ const uint8_t *data_next;
+ /*
+ * Block straddles buffers, split references.
+ */
+ data_next = mtod(m, const uint8_t *);
+ KASSERT(m->m_len >= sizeof(uint32_t) - space,
+ ("not enough data in following buffer, "
+ "m_len %u need %u\n", m->m_len,
+ sizeof(uint32_t) - space));
+ switch (space) {
+ case 1:
+ l ^= get_le32_split(data[0], data_next[0],
+ data_next[1], data_next[2]);
+ data = data_next + 3;
+ space = m->m_len - 3;
+ break;
+ case 2:
+ l ^= get_le32_split(data[0], data[1],
+ data_next[0], data_next[1]);
+ data = data_next + 2;
+ space = m->m_len - 2;
+ break;
+ case 3:
+ l ^= get_le32_split(data[0], data[1],
+ data[2], data_next[0]);
+ data = data_next + 1;
+ space = m->m_len - 1;
+ break;
+ }
+ michael_block(l, r);
+ data_len -= sizeof(uint32_t);
+ } else {
+ /*
+ * Setup for next buffer.
+ */
+ data = mtod(m, const uint8_t *);
+ space = m->m_len;
+ }
+ }
+ /* Last block and padding (0x5a, 4..7 x 0) */
+ switch (data_len) {
+ case 0:
+ l ^= get_le32_split(0x5a, 0, 0, 0);
+ break;
+ case 1:
+ l ^= get_le32_split(data[0], 0x5a, 0, 0);
+ break;
+ case 2:
+ l ^= get_le32_split(data[0], data[1], 0x5a, 0);
+ break;
+ case 3:
+ l ^= get_le32_split(data[0], data[1], data[2], 0x5a);
+ break;
+ }
+ michael_block(l, r);
+ /* l ^= 0; */
+ michael_block(l, r);
+
+ put_le32(mic, l);
+ put_le32(mic + 4, r);
+}
+
+static int
+tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
+ struct mbuf *m, int hdrlen)
+{
+ struct ieee80211_frame *wh;
+ uint8_t icv[IEEE80211_WEP_CRCLEN];
+
+ ctx->tc_ic->ic_stats.is_crypto_tkip++;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ if (!ctx->tx_phase1_done) {
+ tkip_mixing_phase1(ctx->tx_ttak, key->wk_key, wh->i_addr2,
+ (u32)(key->wk_keytsc >> 16));
+ ctx->tx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(ctx->tx_rc4key, key->wk_key, ctx->tx_ttak,
+ (u16) key->wk_keytsc);
+
+ wep_encrypt(ctx->tx_rc4key,
+ m, hdrlen + tkip.ic_header,
+ m->m_pkthdr.len - (hdrlen + tkip.ic_header),
+ icv);
+ (void) m_append(m, IEEE80211_WEP_CRCLEN, icv); /* XXX check return */
+
+ key->wk_keytsc++; /* XXX wrap at 48 bits */
+ if ((u16)(key->wk_keytsc) == 0)
+ ctx->tx_phase1_done = 0;
+ return 1;
+}
+
+static int
+tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
+ struct mbuf *m, int hdrlen)
+{
+ struct ieee80211_frame *wh;
+ u32 iv32;
+ u16 iv16;
+
+ ctx->tc_ic->ic_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) {
+ tkip_mixing_phase1(ctx->rx_ttak, key->wk_key,
+ wh->i_addr2, iv32);
+ ctx->rx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(ctx->rx_rc4key, key->wk_key, ctx->rx_ttak, iv16);
+
+ /* NB: m is unstripped; deduct headers + ICV to get payload */
+ if (wep_decrypt(ctx->rx_rc4key,
+ m, hdrlen + tkip.ic_header,
+ m->m_pkthdr.len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) {
+ if (iv32 != (u32)(key->wk_keyrsc >> 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++;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Module glue.
+ */
+static int
+tkip_modevent(module_t mod, int type, void *unused)
+{
+ switch (type) {
+ case MOD_LOAD:
+ ieee80211_crypto_register(&tkip);
+ return 0;
+ case MOD_UNLOAD:
+ ieee80211_crypto_unregister(&tkip);
+ return 0;
+ }
+ return EINVAL;
+}
+
+static moduledata_t tkip_mod = {
+ "wlan_tkip",
+ tkip_modevent,
+ 0
+};
+DECLARE_MODULE(wlan_tkip, tkip_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_tkip, 1);
+MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1);
diff --git a/sys/net80211/ieee80211_crypto_wep.c b/sys/net80211/ieee80211_crypto_wep.c
new file mode 100644
index 0000000..2edb5bb
--- /dev/null
+++ b/sys/net80211/ieee80211_crypto_wep.c
@@ -0,0 +1,499 @@
+/*-
+ * Copyright (c) 2002-2004 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * IEEE 802.11 WEP crypto support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/endian.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+
+static void *wep_attach(struct ieee80211com *, 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 *, u_int8_t keyid);
+static int wep_decap(struct ieee80211_key *, struct mbuf *);
+static int wep_enmic(struct ieee80211_key *, struct mbuf *);
+static int wep_demic(struct ieee80211_key *, struct mbuf *);
+
+static const struct ieee80211_cipher wep = {
+ .ic_name = "WEP",
+ .ic_cipher = IEEE80211_CIPHER_WEP,
+ .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN,
+ .ic_trailer = IEEE80211_WEP_CRCLEN,
+ .ic_miclen = 0,
+ .ic_attach = wep_attach,
+ .ic_detach = wep_detach,
+ .ic_setkey = wep_setkey,
+ .ic_encap = wep_encap,
+ .ic_decap = wep_decap,
+ .ic_enmic = wep_enmic,
+ .ic_demic = wep_demic,
+};
+
+static int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
+static int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
+
+struct wep_ctx {
+ struct ieee80211com *wc_ic; /* for diagnostics */
+ u_int32_t wc_iv; /* initial vector for crypto */
+};
+
+static void *
+wep_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+ struct wep_ctx *ctx;
+
+ MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ctx == NULL) {
+ ic->ic_stats.is_crypto_nomem++;
+ return NULL;
+ }
+
+ ctx->wc_ic = ic;
+ get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
+ return ctx;
+}
+
+static void
+wep_detach(struct ieee80211_key *k)
+{
+ struct wep_ctx *ctx = k->wk_private;
+
+ FREE(ctx, M_DEVBUF);
+}
+
+static int
+wep_setkey(struct ieee80211_key *k)
+{
+ return k->wk_keylen >= 40/NBBY;
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+wep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+{
+ struct wep_ctx *ctx = k->wk_private;
+ u_int32_t iv;
+ u_int8_t *ivp;
+ int hdrlen;
+
+ hdrlen = ieee80211_hdrsize(mtod(m, void *));
+
+ /*
+ * Copy down 802.11 header and add the IV + KeyID.
+ */
+ M_PREPEND(m, wep.ic_header, M_NOWAIT);
+ if (m == NULL)
+ return 0;
+ ivp = mtod(m, u_int8_t *);
+ ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
+ ivp += hdrlen;
+
+ /*
+ * XXX
+ * IV must not duplicate during the lifetime of the key.
+ * But no mechanism to renew keys is defined in IEEE 802.11
+ * for WEP. And the IV may be duplicated at other stations
+ * because the session key itself is shared. So we use a
+ * pseudo random IV for now, though it is not the right way.
+ *
+ * NB: Rather than use a strictly random IV we select a
+ * random one to start and then increment the value for
+ * each frame. This is an explicit tradeoff between
+ * overhead and security. Given the basic insecurity of
+ * WEP this seems worthwhile.
+ */
+
+ /*
+ * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
+ * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255
+ */
+ iv = ctx->wc_iv;
+ if ((iv & 0xff00) == 0xff00) {
+ int B = (iv & 0xff0000) >> 16;
+ if (3 <= B && B < 16)
+ iv += 0x0100;
+ }
+ ctx->wc_iv = iv + 1;
+
+ /*
+ * NB: Preserve byte order of IV for packet
+ * sniffers; it doesn't matter otherwise.
+ */
+#if _BYTE_ORDER == _BIG_ENDIAN
+ ivp[0] = iv >> 0;
+ ivp[1] = iv >> 8;
+ ivp[2] = iv >> 16;
+#else
+ ivp[2] = iv >> 0;
+ ivp[1] = iv >> 8;
+ ivp[0] = iv >> 16;
+#endif
+ ivp[3] = keyid;
+
+ /*
+ * Finally, do software encrypt if neeed.
+ */
+ if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+ !wep_encrypt(k, m, hdrlen))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+static int
+wep_enmic(struct ieee80211_key *k, struct mbuf *m)
+{
+
+ return 1;
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame. If necessary, decrypt the frame using
+ * the specified key.
+ */
+static int
+wep_decap(struct ieee80211_key *k, struct mbuf *m)
+{
+ struct wep_ctx *ctx = k->wk_private;
+ struct ieee80211_frame *wh;
+ int hdrlen;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ hdrlen = ieee80211_hdrsize(wh);
+
+ /*
+ * Check if the device handled the decrypt in hardware.
+ * If so we just strip the header; otherwise we need to
+ * handle the decrypt in software.
+ */
+ if ((k->wk_flags & IEEE80211_KEY_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++;
+ return 0;
+ }
+
+ /*
+ * Copy up 802.11 header and strip crypto bits.
+ */
+ ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen);
+ m_adj(m, wep.ic_header);
+ m_adj(m, -wep.ic_trailer);
+
+ return 1;
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+static int
+wep_demic(struct ieee80211_key *k, struct mbuf *skb)
+{
+ return 1;
+}
+
+static const uint32_t crc32_table[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
+
+static int
+wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
+{
+#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+ struct wep_ctx *ctx = key->wk_private;
+ struct mbuf *m = m0;
+ u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+ uint8_t icv[IEEE80211_WEP_CRCLEN];
+ uint32_t i, j, k, crc;
+ size_t buflen, data_len;
+ uint8_t S[256];
+ uint8_t *pos;
+ u_int off, keylen;
+
+ ctx->wc_ic->ic_stats.is_crypto_wep++;
+
+ /* NB: this assumes the header was pulled up */
+ memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
+ memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
+
+ /* Setup RC4 state */
+ for (i = 0; i < 256; i++)
+ S[i] = i;
+ j = 0;
+ keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
+ for (i = 0; i < 256; i++) {
+ j = (j + S[i] + rc4key[i % keylen]) & 0xff;
+ S_SWAP(i, j);
+ }
+
+ off = hdrlen + wep.ic_header;
+ data_len = m->m_pkthdr.len - off;
+
+ /* Compute CRC32 over unencrypted data and apply RC4 to data */
+ crc = ~0;
+ i = j = 0;
+ pos = mtod(m, uint8_t *) + off;
+ buflen = m->m_len - off;
+ for (;;) {
+ if (buflen > data_len)
+ buflen = data_len;
+ data_len -= buflen;
+ for (k = 0; k < buflen; k++) {
+ crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ *pos++ ^= S[(S[i] + S[j]) & 0xff];
+ }
+ if (m->m_next == NULL) {
+ if (data_len != 0) { /* out of data */
+ IEEE80211_DPRINTF(ctx->wc_ic,
+ IEEE80211_MSG_CRYPTO,
+ "[%s] out of data for WEP (data_len %u)\n",
+ ether_sprintf(mtod(m0,
+ struct ieee80211_frame *)->i_addr2),
+ data_len);
+ return 0;
+ }
+ break;
+ }
+ m = m->m_next;
+ pos = mtod(m, uint8_t *);
+ buflen = m->m_len;
+ }
+ crc = ~crc;
+
+ /* Append little-endian CRC32 and encrypt it to produce ICV */
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ icv[k] ^= S[(S[i] + S[j]) & 0xff];
+ }
+ return m_append(m0, IEEE80211_WEP_CRCLEN, icv);
+#undef S_SWAP
+}
+
+static int
+wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
+{
+#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+ struct wep_ctx *ctx = key->wk_private;
+ struct mbuf *m = m0;
+ u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+ uint8_t icv[IEEE80211_WEP_CRCLEN];
+ uint32_t i, j, k, crc;
+ size_t buflen, data_len;
+ uint8_t S[256];
+ uint8_t *pos;
+ u_int off, keylen;
+
+ ctx->wc_ic->ic_stats.is_crypto_wep++;
+
+ /* NB: this assumes the header was pulled up */
+ memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
+ memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
+
+ /* Setup RC4 state */
+ for (i = 0; i < 256; i++)
+ S[i] = i;
+ j = 0;
+ keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
+ for (i = 0; i < 256; i++) {
+ j = (j + S[i] + rc4key[i % keylen]) & 0xff;
+ S_SWAP(i, j);
+ }
+
+ off = hdrlen + wep.ic_header;
+ data_len = m->m_pkthdr.len - (off + wep.ic_trailer),
+
+ /* Compute CRC32 over unencrypted data and apply RC4 to data */
+ crc = ~0;
+ i = j = 0;
+ pos = mtod(m, uint8_t *) + off;
+ buflen = m->m_len - off;
+ for (;;) {
+ if (buflen > data_len)
+ buflen = data_len;
+ data_len -= buflen;
+ for (k = 0; k < buflen; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ *pos ^= S[(S[i] + S[j]) & 0xff];
+ crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
+ pos++;
+ }
+ m = m->m_next;
+ if (m == NULL) {
+ if (data_len != 0) { /* out of data */
+ IEEE80211_DPRINTF(ctx->wc_ic,
+ IEEE80211_MSG_CRYPTO,
+ "[%s] out of data for WEP (data_len %u)\n",
+ ether_sprintf(mtod(m0,
+ struct ieee80211_frame *)->i_addr2),
+ data_len);
+ return 0;
+ }
+ break;
+ }
+ pos = mtod(m, uint8_t *);
+ buflen = m->m_len;
+ }
+ crc = ~crc;
+
+ /* Encrypt little-endian CRC32 and verify that it matches with
+ * received ICV */
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ /* XXX assumes ICV is contiguous in mbuf */
+ if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
+ /* ICV mismatch - drop frame */
+ return 0;
+ }
+ }
+ return 1;
+#undef S_SWAP
+}
+
+/*
+ * Module glue.
+ */
+static int
+wep_modevent(module_t mod, int type, void *unused)
+{
+ switch (type) {
+ case MOD_LOAD:
+ ieee80211_crypto_register(&wep);
+ return 0;
+ case MOD_UNLOAD:
+ ieee80211_crypto_unregister(&wep);
+ return 0;
+ }
+ return EINVAL;
+}
+
+static moduledata_t wep_mod = {
+ "wlan_wep",
+ wep_modevent,
+ 0
+};
+DECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_wep, 1);
+MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1);
+MODULE_DEPEND(wlan_wep, rc4, 1, 1, 1);
diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c
new file mode 100644
index 0000000..26ce11b
--- /dev/null
+++ b/sys/net80211/ieee80211_freebsd.c
@@ -0,0 +1,338 @@
+/*-
+ * Copyright (c) 2003-2004 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 support (FreeBSD-specific code)
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/linker.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/route.h>
+
+#include <net80211/ieee80211_var.h>
+
+SYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters");
+
+#ifdef IEEE80211_DEBUG
+int ieee80211_debug = 0;
+SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
+ 0, "debugging printfs");
+#endif
+
+static int
+ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
+{
+ int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT;
+ int error;
+
+ error = sysctl_handle_int(oidp, &inact, 0, req);
+ if (error || !req->newptr)
+ return error;
+ *(int *)arg1 = inact / IEEE80211_INACT_WAIT;
+ return 0;
+}
+
+static int
+ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS)
+{
+ struct ieee80211com *ic = arg1;
+ const char *name = ic->ic_ifp->if_xname;
+
+ return SYSCTL_OUT(req, name, strlen(name));
+}
+
+void
+ieee80211_sysctl_attach(struct ieee80211com *ic)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *oid;
+ char num[14]; /* sufficient for 32 bits */
+
+ 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",
+ __func__);
+ return;
+ }
+ sysctl_ctx_init(ctx);
+ snprintf(num, sizeof(num), "%u", ic->ic_vap);
+ 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");
+#ifdef IEEE80211_DEBUG
+ ic->ic_debug = ieee80211_debug;
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "debug", CTLFLAG_RW, &ic->ic_debug, 0,
+ "control debugging printfs");
+#endif
+ /* XXX inherit from tunables */
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "inact", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_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,
+ 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,
+ 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,
+ ieee80211_sysctl_inact, "I",
+ "station initial state timeout (sec)");
+ ic->ic_sysctl = ctx;
+}
+
+void
+ieee80211_sysctl_detach(struct ieee80211com *ic)
+{
+
+ if (ic->ic_sysctl != NULL) {
+ sysctl_ctx_free(ic->ic_sysctl);
+ ic->ic_sysctl = NULL;
+ }
+}
+
+int
+ieee80211_node_dectestref(struct ieee80211_node *ni)
+{
+ /* XXX need equivalent of atomic_dec_and_test */
+ atomic_subtract_int(&ni->ni_refcnt, 1);
+ return atomic_cmpset_int(&ni->ni_refcnt, 0, 1);
+}
+
+/*
+ * Allocate and setup a management frame of the specified
+ * size. We return the mbuf and a pointer to the start
+ * of the contiguous data area that's been reserved based
+ * on the packet length. The data area is forced to 32-bit
+ * alignment and the buffer length to a multiple of 4 bytes.
+ * This is done mainly so beacon frames (that require this)
+ * can use this interface too.
+ */
+struct mbuf *
+ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
+{
+ struct mbuf *m;
+ u_int len;
+
+ /*
+ * NB: we know the mbuf routines will align the data area
+ * so we don't need to do anything special.
+ */
+ /* XXX 4-address frame? */
+ len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
+ KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
+ if (len < MINCLSIZE) {
+ m = m_gethdr(M_NOWAIT, MT_HEADER);
+ /*
+ * Align the data in case additional headers are added.
+ * This should only happen when a WEP header is added
+ * which only happens for shared key authentication mgt
+ * frames which all fit in MHLEN.
+ */
+ if (m != NULL)
+ MH_ALIGN(m, len);
+ } else
+ m = m_getcl(M_NOWAIT, MT_HEADER, M_PKTHDR);
+ if (m != NULL) {
+ m->m_data += sizeof(struct ieee80211_frame);
+ *frm = m->m_data;
+ }
+ return m;
+}
+
+#include <sys/libkern.h>
+
+void
+get_random_bytes(void *p, size_t n)
+{
+ u_int8_t *dp = p;
+
+ while (n > 0) {
+ u_int32_t v = arc4random();
+ size_t nb = n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n;
+ bcopy(&v, dp, n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n);
+ dp += sizeof(u_int32_t), n -= nb;
+ }
+}
+
+void
+ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_join_event iev;
+
+ if (ni == ic->ic_bss) {
+ memset(&iev, 0, sizeof(iev));
+ IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid);
+ rt_ieee80211msg(ifp, newassoc ?
+ RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC,
+ &iev, sizeof(iev));
+ if_link_state_change(ifp, LINK_STATE_UP);
+ } else if (newassoc) {
+ /* fire off wireless event only for new station */
+ memset(&iev, 0, sizeof(iev));
+ IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
+ rt_ieee80211msg(ifp, RTM_IEEE80211_JOIN, &iev, sizeof(iev));
+ }
+}
+
+void
+ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_leave_event iev;
+
+ if (ni == ic->ic_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));
+ }
+}
+
+void
+ieee80211_notify_scan_done(struct ieee80211com *ic)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: notify scan done\n", ic->ic_ifp->if_xname);
+
+ /* dispatch wireless event indicating scan completed */
+ rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
+}
+
+void
+ieee80211_notify_replay_failure(struct ieee80211com *ic,
+ const struct ieee80211_frame *wh, const struct ieee80211_key *k,
+ u_int64_t rsc)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "[%s] %s replay detected <rsc %llu, csc %llu>\n",
+ ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
+ rsc, k->wk_keyrsc);
+
+ if (ifp != NULL) { /* NB: for cipher test modules */
+ struct ieee80211_replay_event iev;
+
+ IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
+ IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
+ iev.iev_cipher = k->wk_cipher->ic_cipher;
+ iev.iev_keyix = k->wk_keyix;
+ iev.iev_keyrsc = k->wk_keyrsc;
+ iev.iev_rsc = rsc;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
+ }
+}
+
+void
+ieee80211_notify_michael_failure(struct ieee80211com *ic,
+ const struct ieee80211_frame *wh, u_int keyix)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "[%s] Michael MIC verification failed <keyidx %d>\n",
+ ether_sprintf(wh->i_addr2), keyix);
+ ic->ic_stats.is_rx_tkipmic++;
+
+ if (ifp != NULL) { /* NB: for cipher test modules */
+ struct ieee80211_michael_event iev;
+
+ IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
+ IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
+ iev.iev_cipher = IEEE80211_CIPHER_TKIP;
+ iev.iev_keyix = keyix;
+ rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev));
+ }
+}
+
+void
+ieee80211_load_module(const char *modname)
+{
+ struct thread *td = curthread;
+
+ if (suser(td) == 0 && securelevel_gt(td->td_ucred, 0) == 0) {
+ mtx_lock(&Giant);
+ (void) linker_load_module(modname, NULL, NULL, NULL, NULL);
+ mtx_unlock(&Giant);
+ }
+}
+
+/*
+ * Module glue.
+ *
+ * NB: the module name is "wlan" for compatibility with NetBSD.
+ */
+static int
+wlan_modevent(module_t mod, int type, void *unused)
+{
+ switch (type) {
+ case MOD_LOAD:
+ if (bootverbose)
+ printf("wlan: <802.11 Link Layer>\n");
+ return 0;
+ case MOD_UNLOAD:
+ return 0;
+ }
+ return EINVAL;
+}
+
+static moduledata_t wlan_mod = {
+ "wlan",
+ wlan_modevent,
+ 0
+};
+DECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan, 1);
+MODULE_DEPEND(wlan, ether, 1, 1, 1);
diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h
new file mode 100644
index 0000000..bb0e912
--- /dev/null
+++ b/sys/net80211/ieee80211_freebsd.h
@@ -0,0 +1,223 @@
+/*-
+ * Copyright (c) 2003, 2004 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_FREEBSD_H_
+#define _NET80211_IEEE80211_FREEBSD_H_
+
+/*
+ * 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.
+ */
+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)
+#define IEEE80211_NODE_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_nodelock)
+#define IEEE80211_NODE_LOCK(_nt) mtx_lock(&(_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)
+
+/*
+ * Node table scangen locking definitions.
+ */
+typedef struct mtx ieee80211_scan_lock_t;
+#define IEEE80211_SCAN_LOCK_INIT(_nt, _name) \
+ mtx_init(&(_nt)->nt_scanlock, _name, "802.11 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)
+
+/*
+ * Per-node power-save queue definitions.
+ */
+#define IEEE80211_NODE_SAVEQ_INIT(_ni, _name) do { \
+ mtx_init(&(_ni)->ni_savedq.ifq_mtx, _name, "802.11 ps queue", MTX_DEF);\
+ (_ni)->ni_savedq.ifq_maxlen = IEEE80211_PS_MAX_QUEUE; \
+} while (0)
+#define IEEE80211_NODE_SAVEQ_DESTROY(_ni) \
+ mtx_destroy(&(_ni)->ni_savedq.ifq_mtx)
+#define IEEE80211_NODE_SAVEQ_QLEN(_ni) \
+ _IF_QLEN(&(_ni)->ni_savedq)
+#define IEEE80211_NODE_SAVEQ_LOCK(_ni) do { \
+ IF_LOCK(&(_ni)->ni_savedq); \
+} while (0)
+#define IEEE80211_NODE_SAVEQ_UNLOCK(_ni) do { \
+ IF_UNLOCK(&(_ni)->ni_savedq); \
+} while (0)
+#define IEEE80211_NODE_SAVEQ_DEQUEUE(_ni, _m, _qlen) do { \
+ IEEE80211_NODE_SAVEQ_LOCK(_ni); \
+ _IF_DEQUEUE(&(_ni)->ni_savedq, _m); \
+ (_qlen) = IEEE80211_NODE_SAVEQ_QLEN(_ni); \
+ IEEE80211_NODE_SAVEQ_UNLOCK(_ni); \
+} while (0)
+#define IEEE80211_NODE_SAVEQ_DRAIN(_ni, _qlen) do { \
+ IEEE80211_NODE_SAVEQ_LOCK(_ni); \
+ (_qlen) = IEEE80211_NODE_SAVEQ_QLEN(_ni); \
+ _IF_DRAIN(&(_ni)->ni_savedq); \
+ IEEE80211_NODE_SAVEQ_UNLOCK(_ni); \
+} while (0)
+/* XXX could be optimized */
+#define _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(_ni, _m) do { \
+ _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; \
+} while (0)
+
+/*
+ * 802.1x MAC ACL database locking definitions.
+ */
+typedef struct mtx acl_lock_t;
+#define ACL_LOCK_INIT(_as, _name) \
+ mtx_init(&(_as)->as_lock, _name, "802.11 ACL", MTX_DEF)
+#define ACL_LOCK_DESTROY(_as) mtx_destroy(&(_as)->as_lock)
+#define ACL_LOCK(_as) mtx_lock(&(_as)->as_lock)
+#define ACL_UNLOCK(_as) mtx_unlock(&(_as)->as_lock)
+#define ACL_LOCK_ASSERT(_as) \
+ mtx_assert((&(_as)->as_lock), MA_OWNED)
+
+/*
+ * Node reference counting definitions.
+ *
+ * ieee80211_node_initref initialize the reference count to 1
+ * ieee80211_node_incref add a reference
+ * ieee80211_node_decref remove a reference
+ * ieee80211_node_dectestref remove a reference and return 1 if this
+ * is the last reference, otherwise 0
+ * ieee80211_node_refcnt reference count for printing (only)
+ */
+#include <machine/atomic.h>
+
+#define ieee80211_node_initref(_ni) \
+ do { ((_ni)->ni_refcnt = 1); } while (0)
+#define ieee80211_node_incref(_ni) \
+ atomic_add_int(&(_ni)->ni_refcnt, 1)
+#define ieee80211_node_decref(_ni) \
+ atomic_subtract_int(&(_ni)->ni_refcnt, 1)
+struct ieee80211_node;
+extern int ieee80211_node_dectestref(struct ieee80211_node *ni);
+#define ieee80211_node_refcnt(_ni) (_ni)->ni_refcnt
+
+extern struct mbuf *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen);
+#define M_LINK0 M_PROTO1 /* WEP requested */
+#define M_PWR_SAV M_PROTO4 /* bypass PS handling */
+/*
+ * 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.
+ */
+#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)
+
+/*
+ * Mbufs on the power save queue are tagged with an age and
+ * timed out. We reuse the hardware checksum field in the
+ * mbuf packet header to store this data.
+ */
+#define M_AGE_SET(m,v) (m->m_pkthdr.csum_data = v)
+#define M_AGE_GET(m) (m->m_pkthdr.csum_data)
+#define M_AGE_SUB(m,adj) (m->m_pkthdr.csum_data -= adj)
+
+extern void get_random_bytes(void *, size_t);
+
+struct ieee80211com;
+
+void ieee80211_sysctl_attach(struct ieee80211com *);
+void ieee80211_sysctl_detach(struct ieee80211com *);
+
+void ieee80211_load_module(const char *);
+
+/* XXX this stuff belongs elsewhere */
+/*
+ * Message formats for messages from the net80211 layer to user
+ * applications via the routing socket. These messages are appended
+ * to an if_announcemsghdr structure.
+ */
+struct ieee80211_join_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_leave_event {
+ uint8_t iev_addr[6];
+};
+
+struct ieee80211_replay_event {
+ uint8_t iev_src[6]; /* src MAC */
+ uint8_t iev_dst[6]; /* dst MAC */
+ uint8_t iev_cipher; /* cipher type */
+ uint8_t iev_keyix; /* key id/index */
+ uint64_t iev_keyrsc; /* RSC from key */
+ uint64_t iev_rsc; /* RSC from frame */
+};
+
+struct ieee80211_michael_event {
+ uint8_t iev_src[6]; /* src MAC */
+ uint8_t iev_dst[6]; /* dst MAC */
+ uint8_t iev_cipher; /* cipher type */
+ uint8_t iev_keyix; /* key id/index */
+};
+
+#define RTM_IEEE80211_ASSOC 100 /* station associate (bss mode) */
+#define RTM_IEEE80211_REASSOC 101 /* station re-associate (bss mode) */
+#define RTM_IEEE80211_DISASSOC 102 /* station disassociate (bss mode) */
+#define RTM_IEEE80211_JOIN 103 /* station join (ap mode) */
+#define RTM_IEEE80211_LEAVE 104 /* station leave (ap mode) */
+#define RTM_IEEE80211_SCAN 105 /* scan complete, results available */
+#define RTM_IEEE80211_REPLAY 106 /* sequence counter replay detected */
+#define RTM_IEEE80211_MICHAEL 107 /* Michael MIC failure detected */
+
+#endif /* _NET80211_IEEE80211_FREEBSD_H_ */
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index 74aa1c4..c3f8d03 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, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,38 +33,85 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include "opt_inet.h"
-
#include <sys/param.h>
-#include <sys/systm.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/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
-
-#include <machine/atomic.h>
+#include <sys/socket.h>
+
#include <net/if.h>
-#include <net/if_dl.h>
#include <net/if_media.h>
-#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_llc.h>
+#include <net/if_vlan_var.h>
#include <net80211/ieee80211_var.h>
#include <net/bpf.h>
-#ifdef INET
-#include <netinet/in.h>
-#include <netinet/if_ether.h>
-#endif
+#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;
+}
+
+/*
+ * Emit a debug message about discarding a frame or information
+ * element. One format is for extracting the mac address from
+ * the frame header; the other is for when a header is not
+ * available or otherwise appropriate.
+ */
+#define IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do { \
+ if ((_ic)->ic_debug & (_m)) \
+ ieee80211_discard_frame(_ic, _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__);\
+} 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__);\
+} while (0)
+
+static const u_int8_t *ieee80211_getbssid(struct ieee80211com *,
+ const struct ieee80211_frame *);
+static void ieee80211_discard_frame(struct ieee80211com *,
+ const struct ieee80211_frame *, const char *type, const char *fmt, ...);
+static void ieee80211_discard_ie(struct ieee80211com *,
+ const struct ieee80211_frame *, const char *type, const char *fmt, ...);
+static void ieee80211_discard_mac(struct ieee80211com *,
+ const u_int8_t mac[IEEE80211_ADDR_LEN], const char *type,
+ const char *fmt, ...);
+#else
+#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, ...)
+#endif /* IEEE80211_DEBUG */
+
+static struct mbuf *ieee80211_defrag(struct ieee80211com *,
+ struct ieee80211_node *, struct mbuf *);
+static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *);
+static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
+static void ieee80211_recv_pspoll(struct ieee80211com *,
+ struct ieee80211_node *, struct mbuf *);
/*
* Process a received frame. The node associated with the sender
@@ -77,19 +124,22 @@ __FBSDID("$FreeBSD$");
* by the 802.11 layer.
*/
void
-ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
- int rssi, u_int32_t rstamp)
+ieee80211_input(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
{
- struct ieee80211com *ic = (void *)ifp;
+#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;
- struct mbuf *m1;
- int len;
+ int len, hdrsize, off;
u_int8_t dir, type, subtype;
u_int8_t *bssid;
u_int16_t rxseq;
KASSERT(ni != NULL, ("null node"));
+ ni->ni_inact = ni->ni_inact_reload;
/* trim CRC here so WEP can find its own CRC at the end of packet. */
if (m->m_flags & M_HASFCS) {
@@ -106,38 +156,40 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
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) {
- if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "receive packet with wrong version: %x\n",
- wh->i_fc[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;
- /*
- * NB: We are not yet prepared to handle control frames,
- * but permitting drivers to send them to us allows
- * them to go through bpf tapping at the 802.11 layer.
- */
- if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
- /* XXX statistic */
- IEEE80211_DPRINTF2(("%s: frame too short, len %u\n",
- __func__, m->m_pkthdr.len));
- ic->ic_stats.is_rx_tooshort++;
- goto out; /* XXX */
- }
- if (ic->ic_state != IEEE80211_S_SCAN) {
+ 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:
- if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) {
+ bssid = wh->i_addr2;
+ if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
/* not interested in */
- IEEE80211_DPRINTF2(("%s: discard frame from "
- "bss %s\n", __func__,
- ether_sprintf(wh->i_addr2)));
+ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+ bssid, NULL, "%s", "not to bss");
ic->ic_stats.is_rx_wrongbss++;
goto out;
}
@@ -145,43 +197,119 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
case IEEE80211_M_IBSS:
case IEEE80211_M_AHDEMO:
case IEEE80211_M_HOSTAP:
- if (dir == IEEE80211_FC1_DIR_NODS)
- bssid = wh->i_addr3;
- else
+ 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_DPRINTF2(("%s: discard frame from "
- "bss %s\n", __func__,
- ether_sprintf(bssid)));
+ 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) {
+ /*
+ * Fake up a node for this newly
+ * discovered member of the IBSS.
+ */
+ ni = ieee80211_fakeup_adhoc_node(ic->ic_sta,
+ type == IEEE80211_FC0_TYPE_CTL ?
+ wh->i_addr1 : wh->i_addr2);
+ if (ni == NULL) {
+ /* NB: stat kept for alloc failure */
+ goto err;
+ }
+ }
break;
- case IEEE80211_M_MONITOR:
- goto out;
default:
- /* XXX catch bad values */
- break;
+ goto out;
}
ni->ni_rssi = rssi;
ni->ni_rstamp = rstamp;
- rxseq = ni->ni_rxseq;
- ni->ni_rxseq =
- le16toh(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
- /* TODO: fragment */
- if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
- rxseq == ni->ni_rxseq) {
- /* duplicate, silently discarded */
- ic->ic_stats.is_rx_dup++; /* XXX per-station stat */
- goto out;
+ if (HAS_SEQ(type)) {
+ u_int8_t tid;
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
+ tid = ((struct ieee80211_qosframe *)wh)->
+ i_qos[0] & IEEE80211_QOS_TID;
+ if (tid >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ tid++;
+ } else
+ tid = 0;
+ rxseq = le16toh(*(u_int16_t *)wh->i_seq);
+ if ((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;
}
- ni->ni_inact = 0;
}
switch (type) {
case IEEE80211_FC0_TYPE_DATA:
+ hdrsize = ieee80211_hdrsize(wh);
+ if (ic->ic_flags & IEEE80211_F_DATAPAD)
+ hdrsize = roundup(hdrsize, sizeof(u_int32_t));
+ if (m->m_len < hdrsize &&
+ (m = m_pullup(m, hdrsize)) == NULL) {
+ IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+ wh, "data", "too short: len %u, expecting %u",
+ m->m_pkthdr.len, hdrsize);
+ ic->ic_stats.is_rx_tooshort++;
+ goto out; /* XXX */
+ }
+ if (subtype & IEEE80211_FC0_SUBTYPE_QOS) {
+ /* XXX discard if node w/o IEEE80211_NODE_QOS? */
+ /*
+ * Strip QoS control and any padding so only a
+ * stock 802.11 header is at the front.
+ */
+ /* XXX 4-address QoS frame */
+ off = hdrsize - sizeof(struct ieee80211_frame);
+ ovbcopy(mtod(m, u_int8_t *), mtod(m, u_int8_t *) + off,
+ hdrsize - off);
+ m_adj(m, off);
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[0] &= ~IEEE80211_FC0_SUBTYPE_QOS;
+ } else {
+ /* XXX copy up for 4-address frames w/ padding */
+ }
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
if (dir != IEEE80211_FC1_DIR_FROMDS) {
@@ -197,6 +325,8 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
* 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;
}
@@ -207,6 +337,7 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
ic->ic_stats.is_rx_wrongdir++;
goto out;
}
+ /* XXX no power-save support */
break;
case IEEE80211_M_HOSTAP:
if (dir != IEEE80211_FC1_DIR_TODS) {
@@ -215,62 +346,156 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
}
/* check if source STA is associated */
if (ni == ic->ic_bss) {
- IEEE80211_DPRINTF(("%s: data from unknown src "
- "%s\n", __func__,
- ether_sprintf(wh->i_addr2)));
+ IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "unknown src");
/* NB: caller deals with reference */
- ni = ieee80211_dup_bss(ic, wh->i_addr2);
+ ni = ieee80211_dup_bss(ic->ic_sta, wh->i_addr2);
if (ni != NULL) {
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_NOT_AUTHED);
- ieee80211_free_node(ic, ni);
+ ieee80211_free_node(ni);
}
ic->ic_stats.is_rx_notassoc++;
goto err;
}
if (ni->ni_associd == 0) {
- IEEE80211_DPRINTF(("ieee80211_input: "
- "data from unassoc src %s\n",
- ether_sprintf(wh->i_addr2)));
+ IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+ wh, "data", "%s", "unassoc src");
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_DISASSOC,
IEEE80211_REASON_NOT_ASSOCED);
- ieee80211_unref_node(&ni);
ic->ic_stats.is_rx_notassoc++;
goto err;
}
+
+ /*
+ * Check for power save state change.
+ */
+ 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;
- case IEEE80211_M_MONITOR:
- break;
+ default:
+ /* XXX here to keep compiler happy */
+ 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 (ic->ic_flags & IEEE80211_F_WEPON) {
- m = ieee80211_wep_crypt(ifp, m, 0);
- if (m == NULL) {
- ic->ic_stats.is_rx_wepfail++;
- goto err;
- }
- wh = mtod(m, struct ieee80211_frame *);
- } else {
- ic->ic_stats.is_rx_nowep++;
+ 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);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ IEEE80211_NODE_STAT(ni, rx_wepfail);
goto out;
}
+ wh = mtod(m, struct ieee80211_frame *);
+ } else {
+ key = NULL;
+ }
+
+ /*
+ * Next up, any fragmentation.
+ */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ m = ieee80211_defrag(ic, ni, m);
+ 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(ic, key, m)) {
+ IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+ ni->ni_macaddr, "data", "%s", "demic error");
+ IEEE80211_NODE_STAT(ni, rx_demicfail);
+ goto out;
}
+
/* copy to listener after decrypt */
if (ic->ic_rawbpf)
bpf_mtap(ic->ic_rawbpf, m);
- m = ieee80211_decap(ifp, m);
+
+ /*
+ * Finally, strip the 802.11 header.
+ */
+ m = ieee80211_decap(ic, m);
if (m == NULL) {
+ /* don't count Null data frames as errors */
+ if (subtype == IEEE80211_FC0_SUBTYPE_NODATA)
+ 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)) {
+ /*
+ * 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(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;
+ }
+ } else {
+ /*
+ * When denying unencrypted frames, discard
+ * any non-PAE frames received without encryption.
+ */
+ if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
+ key == NULL &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ /*
+ * Drop unencrypted frames.
+ */
+ ic->ic_stats.is_rx_unencrypted++;
+ IEEE80211_NODE_STAT(ni, rx_unencrypted);
+ goto out;
+ }
+ }
ifp->if_ipackets++;
+ IEEE80211_NODE_STAT(ni, rx_data);
+ IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
/* perform as a bridge within the AP */
- m1 = NULL;
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- eh = mtod(m, struct ether_header *);
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
+ struct mbuf *m1 = NULL;
+
if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
m1 = m_copypacket(m, M_DONTWAIT);
if (m1 == NULL)
@@ -278,13 +503,19 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
else
m1->m_flags |= M_MCAST;
} else {
- ni = ieee80211_find_node(ic, eh->ether_dhost);
- if (ni != NULL) {
- if (ni->ni_associd != 0) {
+ /* XXX this dups work done in ieee80211_encap */
+ /* check if destination is associated */
+ struct ieee80211_node *ni1 =
+ ieee80211_find_node(ic->ic_sta,
+ eh->ether_dhost);
+ if (ni1 != NULL) {
+ /* XXX check if authorized */
+ if (ni1->ni_associd != 0) {
m1 = m;
m = NULL;
}
- ieee80211_free_node(ic, ni);
+ /* XXX statistic? */
+ ieee80211_free_node(ni1);
}
}
if (m1 != NULL) {
@@ -295,61 +526,65 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
ifp->if_obytes += len;
}
}
- if (m != NULL)
+ if (m != NULL) {
+ if (ni->ni_vlan != 0) {
+ /* attach vlan tag */
+ /* XXX goto err? */
+ VLAN_INPUT_TAG(ifp, m, ni->ni_vlan, goto out);
+ }
(*ifp->if_input)(ifp, m);
+ }
return;
case IEEE80211_FC0_TYPE_MGT:
+ IEEE80211_NODE_STAT(ni, rx_mgmt);
if (dir != IEEE80211_FC1_DIR_NODS) {
ic->ic_stats.is_rx_wrongdir++;
goto err;
}
- if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
- ic->ic_stats.is_rx_ahdemo_mgt++;
+ 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;
}
- subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
-
- /* drop frames without interest */
- if (ic->ic_state == IEEE80211_S_SCAN) {
- if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
- subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
- ic->ic_stats.is_rx_mgtdiscard++;
+#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;
}
- } else {
- if (ic->ic_opmode != IEEE80211_M_IBSS &&
- subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
- ic->ic_stats.is_rx_mgtdiscard++;
+ 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;
}
- }
-
- if (ifp->if_flags & IFF_DEBUG) {
- /* avoid to print too many frames */
- int doprint = 0;
-
- switch (subtype) {
- case IEEE80211_FC0_SUBTYPE_BEACON:
- if (ic->ic_state == IEEE80211_S_SCAN)
- doprint = 1;
- break;
- case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
- if (ic->ic_opmode == IEEE80211_M_IBSS)
- doprint = 1;
- break;
- default:
- doprint = 1;
- break;
+ key = ieee80211_crypto_decap(ic, ni, m);
+ if (key == NULL) {
+ /* NB: stats+msgs handled in crypto_decap */
+ goto out;
}
-#ifdef IEEE80211_DEBUG
- doprint += ieee80211_debug;
-#endif
- if (doprint)
- 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);
}
if (ic->ic_rawbpf)
bpf_mtap(ic->ic_rawbpf, m);
@@ -358,10 +593,19 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
return;
case IEEE80211_FC0_TYPE_CTL:
+ IEEE80211_NODE_STAT(ni, rx_ctrl);
ic->ic_stats.is_rx_ctl++;
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PS_POLL:
+ ieee80211_recv_pspoll(ic, ni, m);
+ break;
+ }
+ }
goto out;
default:
- IEEE80211_DPRINTF(("%s: bad type %x\n", __func__, type));
+ IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+ wh, NULL, "bad frame type 0x%x", type);
/* should not come here */
break;
}
@@ -373,19 +617,108 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
bpf_mtap(ic->ic_rawbpf, m);
m_freem(m);
}
+#undef SEQ_LEQ
+}
+
+/*
+ * This function reassemble fragments.
+ */
+static struct mbuf *
+ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
+ struct mbuf *m)
+{
+ struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+ struct ieee80211_frame *lwh;
+ u_int16_t rxseq;
+ u_int8_t fragno;
+ u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;
+ struct mbuf *mfrag;
+
+ KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?"));
+
+ rxseq = le16toh(*(u_int16_t *)wh->i_seq);
+ fragno = rxseq & IEEE80211_SEQ_FRAG_MASK;
+
+ /* Quick way out, if there's nothing to defragment */
+ if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL)
+ return m;
+
+ /*
+ * Remove frag to insure it doesn't get reaped by timer.
+ */
+ if (ni->ni_table == NULL) {
+ /*
+ * Should never happen. If the node is orphaned (not in
+ * the table) then input packets should not reach here.
+ * Otherwise, a concurrent request that yanks the table
+ * should be blocked by other interlocking and/or by first
+ * shutting the driver down. Regardless, be defensive
+ * here and just bail
+ */
+ /* XXX need msg+stat */
+ m_freem(m);
+ return NULL;
+ }
+ IEEE80211_NODE_LOCK(ni->ni_table);
+ mfrag = ni->ni_rxfrag[0];
+ ni->ni_rxfrag[0] = NULL;
+ IEEE80211_NODE_UNLOCK(ni->ni_table);
+
+ /*
+ * Validate new fragment is in order and
+ * related to the previous ones.
+ */
+ if (mfrag != NULL) {
+ u_int16_t last_rxseq;
+
+ lwh = mtod(mfrag, struct ieee80211_frame *);
+ last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq);
+ /* NB: check seq # and frag together */
+ if (rxseq != last_rxseq+1 ||
+ !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) ||
+ !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) {
+ /*
+ * Unrelated fragment or no space for it,
+ * clear current fragments.
+ */
+ m_freem(mfrag);
+ mfrag = NULL;
+ }
+ }
+
+ if (mfrag == NULL) {
+ if (fragno != 0) { /* !first fragment, discard */
+ IEEE80211_NODE_STAT(ni, rx_defrag);
+ m_freem(m);
+ return NULL;
+ }
+ mfrag = m;
+ } else { /* concatenate */
+ m_cat(mfrag, m);
+ /* NB: m_cat doesn't update the packet header */
+ mfrag->m_pkthdr.len += m->m_pkthdr.len;
+ /* track last seqnum and fragno */
+ lwh = mtod(mfrag, struct ieee80211_frame *);
+ *(u_int16_t *) lwh->i_seq = *(u_int16_t *) wh->i_seq;
+ }
+ if (more_frag) { /* more to come, save */
+ ni->ni_rxfrag[0] = mfrag;
+ mfrag = NULL;
+ }
+ return mfrag;
}
-struct mbuf *
-ieee80211_decap(struct ifnet *ifp, struct mbuf *m)
+static struct mbuf *
+ieee80211_decap(struct ieee80211com *ic, struct mbuf *m)
{
+ struct ieee80211_frame wh; /* NB: QoS stripped above */
struct ether_header *eh;
- struct ieee80211_frame wh;
struct llc *llc;
- if (m->m_len < sizeof(wh) + sizeof(*llc)) {
- m = m_pullup(m, sizeof(wh) + sizeof(*llc));
- if (m == NULL)
- return NULL;
+ if (m->m_len < sizeof(wh) + sizeof(*llc) &&
+ (m = m_pullup(m, sizeof(wh) + sizeof(*llc))) == NULL) {
+ /* XXX stat, msg */
+ return NULL;
}
memcpy(&wh, mtod(m, caddr_t), sizeof(wh));
llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh));
@@ -413,7 +746,8 @@ ieee80211_decap(struct ifnet *ifp, struct mbuf *m)
break;
case IEEE80211_FC1_DIR_DSTODS:
/* not yet supported */
- IEEE80211_DPRINTF(("%s: DS to DS\n", __func__));
+ IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+ &wh, "data", "%s", "DS to DS not supported");
m_freem(m);
return NULL;
}
@@ -495,9 +829,10 @@ ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni,
nxrates = xrates[1];
if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
- IEEE80211_DPRINTF(("%s: extended rate set too large;"
- " only using %u of %u rates\n",
- __func__, nxrates, xrates[1]));
+ 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++;
}
memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
@@ -506,21 +841,318 @@ ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni,
return ieee80211_fix_rate(ic, ni, flags);
}
+static void
+ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
+ struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq,
+ u_int16_t status)
+{
+
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_IBSS:
+ if (ic->ic_state != IEEE80211_S_RUN ||
+ seq != IEEE80211_AUTH_OPEN_REQUEST) {
+ ic->ic_stats.is_rx_bad_auth++;
+ return;
+ }
+ ieee80211_new_state(ic, IEEE80211_S_AUTH,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+
+ case IEEE80211_M_AHDEMO:
+ /* should not come here */
+ 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
+ (void) ieee80211_ref_node(ni);
+ ni->ni_inact_reload = ic->ic_inact_auth;
+ 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));
+ 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++;
+ return;
+ }
+ ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ break;
+ case IEEE80211_M_MONITOR:
+ break;
+ }
+}
+
+static int
+alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ if (ni->ni_challenge == NULL)
+ MALLOC(ni->ni_challenge, u_int32_t*, IEEE80211_CHALLENGE_LEN,
+ M_DEVBUF, 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));
+ /* XXX statistic */
+ }
+ return (ni->ni_challenge != NULL);
+}
+
+/* XXX TODO: add statistics */
+static void
+ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh,
+ u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi,
+ u_int32_t rstamp, u_int16_t seq, u_int16_t status)
+{
+ u_int8_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:
+ 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 {
+ (void) ieee80211_ref_node(ni);
+ allocbs = 0;
+ }
+ ni->ni_rssi = rssi;
+ 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;
+ }
+ ni->ni_inact_reload = ic->ic_inact_auth;
+ IEEE80211_DPRINTF(ic,
+ IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+ "[%s] station authenticated (shared key)\n",
+ ether_sprintf(ni->ni_macaddr));
+ 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_DEVBUF);
+ 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,
+ wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ 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_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_AUTH,
+ (seq + 1) | (estatus<<16));
+ }
+}
+
/* Verify the existence and length of __elem or get out. */
#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \
if ((__elem) == NULL) { \
- IEEE80211_DPRINTF(("%s: no " #__elem "in %s frame\n", \
- __func__, ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT])); \
+ 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_DPRINTF(("%s: bad " #__elem " len %d in %s " \
- "frame from %s\n", __func__, (__elem)[1], \
- ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- ether_sprintf(wh->i_addr2))); \
+ 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; \
} \
@@ -528,26 +1160,516 @@ ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni,
#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \
if ((_len) < (_minlen)) { \
- IEEE80211_DPRINTF(("%s: %s frame too short from %s\n", \
- __func__, \
- ieee80211_mgt_subtype_name[subtype >> \
- IEEE80211_FC0_SUBTYPE_SHIFT], \
- ether_sprintf(wh->i_addr2))); \
+ IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID, \
+ wh, ieee80211_mgt_subtype_name[subtype >> \
+ IEEE80211_FC0_SUBTYPE_SHIFT], \
+ "%s", "ie too short"); \
ic->ic_stats.is_rx_elem_toosmall++; \
return; \
} \
} while (0)
+#ifdef IEEE80211_DEBUG
+static void
+ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag,
+ u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_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) \
+ ((u_int16_t) \
+ ((((const u_int8_t *)(p))[0] ) | \
+ (((const u_int8_t *)(p))[1] << 8)))
+#define LE_READ_4(p) \
+ ((u_int32_t) \
+ ((((const u_int8_t *)(p))[0] ) | \
+ (((const u_int8_t *)(p))[1] << 8) | \
+ (((const u_int8_t *)(p))[2] << 16) | \
+ (((const u_int8_t *)(p))[3] << 24)))
+
+static int __inline
+iswpaoui(const u_int8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static int __inline
+iswmeoui(const u_int8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
+}
+
+static int __inline
+iswmeparam(const u_int8_t *frm)
+{
+ return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+ frm[6] == WME_PARAM_OUI_SUBTYPE;
+}
+
+static int __inline
+iswmeinfo(const u_int8_t *frm)
+{
+ return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+ frm[6] == WME_INFO_OUI_SUBTYPE;
+}
+
+static int __inline
+isatherosoui(const u_int8_t *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+/*
+ * Convert a WPA cipher selector OUI to an internal
+ * cipher algorithm. Where appropriate we also
+ * record any key length.
+ */
+static int
+wpa_cipher(u_int8_t *sel, u_int8_t *keylen)
+{
+#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
+ u_int32_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(u_int8_t *sel)
+{
+#define WPA_SEL(x) (((x)<<24)|WPA_OUI)
+ u_int32_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, u_int8_t *frm,
+ struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+ u_int8_t len = frm[1];
+ u_int32_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.
+ */
+ KASSERT(ic->ic_flags & IEEE80211_F_WPA1,
+ ("not WPA, flags 0x%x", ic->ic_flags));
+ 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(u_int8_t *sel, u_int8_t *keylen)
+{
+#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
+ u_int32_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(u_int8_t *sel)
+{
+#define RSN_SEL(x) (((x)<<24)|RSN_OUI)
+ u_int32_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, u_int8_t *frm,
+ struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+ u_int8_t len = frm[1];
+ u_int32_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.
+ */
+ KASSERT(ic->ic_flags & IEEE80211_F_WPA2,
+ ("not RSN, flags 0x%x", ic->ic_flags));
+ 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, u_int8_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 0;
+ }
+ 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 void
+ieee80211_saveie(u_int8_t **iep, const u_int8_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_DEVBUF);
+ MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT);
+ }
+ if (*iep != NULL)
+ memcpy(*iep, ie, ielen);
+ /* XXX note failure */
+}
+
+#ifdef IEEE80211_DEBUG
+static void
+dump_probe_beacon(u_int8_t subtype, int isnew,
+ const u_int8_t mac[IEEE80211_ADDR_LEN],
+ u_int8_t chan, u_int8_t bchan, u_int16_t capinfo, u_int16_t bintval,
+ u_int8_t erp, u_int8_t *ssid, u_int8_t *country)
+{
+ printf("[%s] %s%s on chan %u (bss chan %u) ",
+ ether_sprintf(mac), isnew ? "new " : "",
+ ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
+ chan, bchan);
+ ieee80211_print_essid(ssid + 2, ssid[1]);
+ printf("\n");
+
+ if (isnew) {
+ printf("[%s] caps 0x%x bintval %u erp 0x%x",
+ ether_sprintf(mac), capinfo, bintval, erp);
+ if (country) {
+#ifdef __FreeBSD__
+ printf(" country info %*D", country[1], country+2, " ");
+#else
+ int i;
+ printf(" country info");
+ for (i = 0; i < country[1]; i++)
+ printf(" %02x", country[i+2]);
+#endif
+ }
+ printf("\n");
+ }
+}
+#endif /* IEEE80211_DEBUG */
+
void
ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
struct ieee80211_node *ni,
int subtype, int rssi, u_int32_t rstamp)
{
- struct ifnet *ifp = &ic->ic_if;
+#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
struct ieee80211_frame *wh;
u_int8_t *frm, *efrm;
- u_int8_t *ssid, *rates, *xrates;
- int reassoc, resp, newassoc, allocbs;
+ u_int8_t *ssid, *rates, *xrates, *wpa, *wme;
+ int reassoc, resp, allocbs;
wh = mtod(m0, struct ieee80211_frame *);
frm = (u_int8_t *)&wh[1];
@@ -555,18 +1677,34 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
switch (subtype) {
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
case IEEE80211_FC0_SUBTYPE_BEACON: {
- u_int8_t *tstamp, *bintval, *capinfo, *country;
+ u_int8_t *tstamp, *country;
u_int8_t chan, bchan, fhindex, erp;
+ u_int16_t capinfo, bintval, timoff;
u_int16_t fhdwell;
- int isprobe;
- if (ic->ic_opmode != IEEE80211_M_IBSS &&
- ic->ic_state != IEEE80211_S_SCAN) {
- /* XXX: may be useful for background scan */
+ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+ /*
+ * Count beacon frames specially, some drivers
+ * use this info to do things like update LED's.
+ */
+ ic->ic_stats.is_rx_beacon++;
+ IEEE80211_NODE_STAT(ni, rx_beacons);
+ } else
+ IEEE80211_NODE_STAT(ni, rx_proberesp);
+ /*
+ * 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;
}
- isprobe = (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP);
-
/*
* beacon/probe response frame format
* [8] time stamp
@@ -578,17 +1716,20 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
* [tlv] parameter set (FH/DS)
* [tlv] erp information
* [tlv] extended supported rates
+ * [tlv] WME
+ * [tlv] WPA or RSN
*/
IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
- tstamp = frm; frm += 8;
- bintval = frm; frm += 2;
- capinfo = frm; frm += 2;
- ssid = rates = xrates = country = NULL;
+ tstamp = frm; frm += 8;
+ bintval = le16toh(*(u_int16_t *)frm); frm += 2;
+ capinfo = le16toh(*(u_int16_t *)frm); frm += 2;
+ ssid = rates = xrates = country = wpa = wme = NULL;
bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
chan = bchan;
fhdwell = 0;
fhindex = 0;
erp = 0;
+ timoff = 0;
while (frm < efrm) {
switch (*frm) {
case IEEE80211_ELEMID_SSID:
@@ -602,7 +1743,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
break;
case IEEE80211_ELEMID_FHPARMS:
if (ic->ic_phytype == IEEE80211_T_FH) {
- fhdwell = (frm[3] << 8) | frm[2];
+ fhdwell = LE_READ_2(&frm[2]);
chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
fhindex = frm[6];
}
@@ -616,6 +1757,8 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
chan = frm[2];
break;
case IEEE80211_ELEMID_TIM:
+ /* XXX ATIM? */
+ timoff = frm - mtod(m0, u_int8_t *);
break;
case IEEE80211_ELEMID_IBSSPARMS:
break;
@@ -624,17 +1767,28 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
break;
case IEEE80211_ELEMID_ERP:
if (frm[1] != 1) {
- IEEE80211_DPRINTF(("%s: invalid ERP "
- "element; length %u, expecting "
- "1\n", __func__, frm[1]));
+ IEEE80211_DISCARD_IE(ic,
+ IEEE80211_MSG_ELEMID, wh, "ERP",
+ "bad len %u", frm[1]);
ic->ic_stats.is_rx_elem_toobig++;
break;
}
erp = frm[2];
break;
+ case IEEE80211_ELEMID_RSN:
+ wpa = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm))
+ wpa = frm;
+ else if (iswmeparam(frm) || iswmeinfo(frm))
+ wme = frm;
+ /* XXX Atheros OUI support */
+ break;
default:
- IEEE80211_DPRINTF2(("%s: element id %u/len %u "
- "ignored\n", __func__, *frm, frm[1]));
+ IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
+ wh, "unhandled",
+ "id %u, len %u", *frm, frm[1]);
ic->ic_stats.is_rx_elem_unknown++;
break;
}
@@ -647,10 +1801,10 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
chan > IEEE80211_CHAN_MAX ||
#endif
isclr(ic->ic_chan_active, chan)) {
- IEEE80211_DPRINTF(("%s: ignore %s with invalid channel "
- "%u\n", __func__,
- isprobe ? "probe response" : "beacon",
- chan));
+ IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,
+ wh, ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ "invalid channel %u", chan);
ic->ic_stats.is_rx_badchan++;
return;
}
@@ -665,108 +1819,146 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
* the rssi value should be correct even for
* different hop pattern in FH.
*/
- IEEE80211_DPRINTF(("%s: ignore %s on channel %u marked "
- "for channel %u\n", __func__,
- isprobe ? "probe response" : "beacon",
- bchan, chan));
+ IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,
+ wh, ieee80211_mgt_subtype_name[subtype >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ "for off-channel %u\n", chan);
ic->ic_stats.is_rx_chanmismatch++;
return;
}
/*
- * Use mac and channel for lookup so we collect all
- * potential AP's when scanning. Otherwise we may
- * see the same AP on multiple channels and will only
- * record the last one. We could filter APs here based
- * on rssi, etc. but leave that to the end of the scan
- * so we can keep the selection criteria in one spot.
- * This may result in a bloat of the scanned AP list but
- * it shouldn't be too much.
+ * 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.
*/
- ni = ieee80211_lookup_node(ic, wh->i_addr2,
- &ic->ic_channels[chan]);
-#ifdef IEEE80211_DEBUG
- if (ieee80211_debug &&
- (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) {
- printf("%s: %s%s on chan %u (bss chan %u) ",
- __func__, (ni == NULL ? "new " : ""),
- isprobe ? "probe response" : "beacon",
- chan, bchan);
- ieee80211_print_essid(ssid + 2, ssid[1]);
- printf(" from %s\n", ether_sprintf(wh->i_addr2));
- printf("%s: caps 0x%x bintval %u erp 0x%x\n",
- __func__, le16toh(*(u_int16_t *)capinfo),
- le16toh(*(u_int16_t *)bintval), erp);
- if (country)
- printf("%s: country info %*D\n",
- __func__, country[1], country+2, " ");
+ 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))) {
+ if (ni->ni_erp != 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, erp);
+ if (erp & IEEE80211_ERP_USE_PROTECTION)
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ else
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ ni->ni_erp = erp;
+ /* XXX statistic */
+ }
+ if ((ni->ni_capinfo ^ 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, capinfo);
+ /*
+ * NB: we assume short preamble doesn't
+ * change dynamically
+ */
+ ieee80211_set_shortslottime(ic,
+ ic->ic_curmode == IEEE80211_MODE_11A ||
+ (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ ni->ni_capinfo = capinfo;
+ /* XXX statistic */
+ }
+ if (wme != NULL &&
+ ieee80211_parse_wmeparams(ic, wme, wh))
+ ieee80211_wme_updateparams(ic);
+ /* NB: don't need the rest of this */
+ return;
}
+
+ if (ni == ic->ic_bss) {
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(ic))
+ dump_probe_beacon(subtype, 1,
+ wh->i_addr2, chan, bchan, capinfo,
+ bintval, erp, ssid, country);
#endif
- if (ni == NULL) {
- ni = ieee80211_alloc_node(ic, wh->i_addr2);
+ /*
+ * Create a new entry. If scanning the entry goes
+ * in the scan cache. Otherwise, be particular when
+ * operating in adhoc mode--only take nodes marked
+ * as ibss participants so we don't populate our
+ * neighbor table with unintersting sta's.
+ */
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ if ((capinfo & IEEE80211_CAPINFO_IBSS) == 0)
+ return;
+ ni = ieee80211_fakeup_adhoc_node(ic->ic_sta,
+ wh->i_addr2);
+ } else
+ ni = ieee80211_dup_bss(&ic->ic_scan, wh->i_addr2);
if (ni == NULL)
return;
ni->ni_esslen = ssid[1];
memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
memcpy(ni->ni_essid, ssid + 2, ssid[1]);
- allocbs = 1;
- } else if (ssid[1] != 0 && isprobe) {
+ } else if (ssid[1] != 0 &&
+ (ISPROBE(subtype) || ni->ni_esslen == 0)) {
/*
- * Update ESSID at probe response to adopt hidden AP by
- * Lucent/Cisco, which announces null ESSID in beacon.
+ * Update ESSID at probe response to adopt
+ * hidden AP by Lucent/Cisco, which announces
+ * null ESSID in beacon.
*/
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(ic) ||
+ ieee80211_msg_debug(ic))
+ dump_probe_beacon(subtype, 0,
+ wh->i_addr2, chan, bchan, capinfo,
+ bintval, erp, ssid, country);
+#endif
ni->ni_esslen = ssid[1];
memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
memcpy(ni->ni_essid, ssid + 2, ssid[1]);
- allocbs = 0;
- } else
- allocbs = 0;
+ }
+ ni->ni_scangen = ic->ic_scan.nt_scangen;
IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
ni->ni_rssi = rssi;
ni->ni_rstamp = rstamp;
- memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp));
- ni->ni_intval = le16toh(*(u_int16_t *)bintval);
- ni->ni_capinfo = le16toh(*(u_int16_t *)capinfo);
- /* XXX validate channel # */
+ memcpy(ni->ni_tstamp.data, tstamp, sizeof(ni->ni_tstamp));
+ ni->ni_intval = bintval;
+ ni->ni_capinfo = capinfo;
ni->ni_chan = &ic->ic_channels[chan];
ni->ni_fhdwell = fhdwell;
ni->ni_fhindex = fhindex;
ni->ni_erp = erp;
- /* NB: must be after ni_chan is setup */
- ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT);
/*
- * When scanning we record results (nodes) with a zero
- * refcnt. Otherwise we want to hold the reference for
- * ibss neighbors so the nodes don't get released prematurely.
- * Anything else can be discarded (XXX and should be handled
- * above so we don't do so much work).
+ * Record the byte offset from the mac header to
+ * the start of the TIM information element for
+ * use by hardware and/or to speedup software
+ * processing of beacon frames.
*/
- if (ic->ic_state == IEEE80211_S_SCAN)
- ieee80211_unref_node(&ni); /* NB: do not free */
- else if (ic->ic_opmode == IEEE80211_M_IBSS &&
- allocbs && isprobe) {
- /*
- * Fake an association so the driver can setup it's
- * private state. The rate set has been setup above;
- * there is no handshake as in ap/station operation.
- */
- if (ic->ic_newassoc)
- (*ic->ic_newassoc)(ic, ni, 1);
- /* NB: hold reference */
- } else {
- /* XXX optimize to avoid work done above */
- ieee80211_free_node(ic, ni);
- }
+ ni->ni_timoff = timoff;
+ /*
+ * Record optional information elements that might be
+ * used by applications or drivers.
+ */
+ if (wme != NULL)
+ ieee80211_saveie(&ni->ni_wme_ie, wme);
+ if (wpa != NULL)
+ ieee80211_saveie(&ni->ni_wpa_ie, wpa);
+ /* NB: must be after ni_chan is setup */
+ ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT);
break;
}
case IEEE80211_FC0_SUBTYPE_PROBE_REQ: {
u_int8_t rate;
- if (ic->ic_opmode == IEEE80211_M_STA)
+ if (ic->ic_opmode == IEEE80211_M_STA ||
+ ic->ic_state != IEEE80211_S_RUN) {
+ ic->ic_stats.is_rx_mgtdiscard++;
return;
- if (ic->ic_state != IEEE80211_S_RUN)
+ }
+ if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+ /* frame must be directed */
+ ic->ic_stats.is_rx_mgtdiscard++; /* XXX stat */
return;
+ }
/*
* prreq frame format
@@ -791,43 +1983,45 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
}
IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
- if (ssid[1] != 0 &&
- (ssid[1] != ic->ic_bss->ni_esslen ||
- memcmp(ssid + 2, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen) != 0)) {
-#ifdef IEEE80211_DEBUG
- if (ieee80211_debug) {
- printf("%s: ssid unmatch ", __func__);
- ieee80211_print_essid(ssid + 2, ssid[1]);
- printf(" from %s\n", ether_sprintf(wh->i_addr2));
- }
-#endif
- ic->ic_stats.is_rx_ssidmismatch++;
- return;
- }
+ IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
if (ni == ic->ic_bss) {
- ni = ieee80211_dup_bss(ic, wh->i_addr2);
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ /*
+ * 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.
+ */
+ ni = ieee80211_fakeup_adhoc_node(ic->ic_sta,
+ wh->i_addr2);
+ } else
+ ni = ieee80211_dup_bss(ic->ic_sta, wh->i_addr2);
if (ni == NULL)
return;
- IEEE80211_DPRINTF(("%s: new req from %s\n",
- __func__, ether_sprintf(wh->i_addr2)));
allocbs = 1;
} else
allocbs = 0;
+ 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(ic, ni, rates, xrates,
- IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
- | IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+ IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
+ | IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
if (rate & IEEE80211_RATE_BASIC) {
- IEEE80211_DPRINTF(("%s: rate negotiation failed: %s\n",
- __func__,ether_sprintf(wh->i_addr2)));
+ 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)
- ieee80211_free_node(ic, ni);
+ if (allocbs && ic->ic_opmode != IEEE80211_M_IBSS) {
+ /* reclaim immediately */
+ ieee80211_free_node(ni);
+ }
break;
}
@@ -844,94 +2038,63 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
algo = le16toh(*(u_int16_t *)frm);
seq = le16toh(*(u_int16_t *)(frm + 2));
status = le16toh(*(u_int16_t *)(frm + 4));
- if (algo != IEEE80211_AUTH_ALG_OPEN) {
- /* TODO: shared key auth */
- IEEE80211_DPRINTF(("%s: unsupported auth %d from %s\n",
- __func__, algo, ether_sprintf(wh->i_addr2)));
- ic->ic_stats.is_rx_auth_unsupported++;
+ 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++;
return;
}
- switch (ic->ic_opmode) {
- case IEEE80211_M_IBSS:
- if (ic->ic_state != IEEE80211_S_RUN || seq != 1) {
- IEEE80211_DPRINTF(("%s: discard auth from %s; "
- "state %u, seq %u\n", __func__,
- ether_sprintf(wh->i_addr2),
- ic->ic_state, seq));
- ic->ic_stats.is_rx_bad_auth++;
- break;
- }
- ieee80211_new_state(ic, IEEE80211_S_AUTH,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
- break;
-
- case IEEE80211_M_AHDEMO:
- /* should not come here */
- break;
-
- case IEEE80211_M_HOSTAP:
- if (ic->ic_state != IEEE80211_S_RUN || seq != 1) {
- IEEE80211_DPRINTF(("%s: discard auth from %s; "
- "state %u, seq %u\n", __func__,
- ether_sprintf(wh->i_addr2),
- ic->ic_state, seq));
- ic->ic_stats.is_rx_bad_auth++;
- break;
- }
- if (ni == ic->ic_bss) {
- ni = ieee80211_alloc_node(ic, wh->i_addr2);
- if (ni == NULL)
- return;
- IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
- ni->ni_rssi = rssi;
- ni->ni_rstamp = rstamp;
- ni->ni_chan = ic->ic_bss->ni_chan;
- allocbs = 1;
- } else
- allocbs = 0;
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_AUTH, 2);
- if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "station %s %s authenticated\n",
- (allocbs ? "newly" : "already"),
- ether_sprintf(ni->ni_macaddr));
- break;
-
- case IEEE80211_M_STA:
- if (ic->ic_state != IEEE80211_S_AUTH || seq != 2) {
- IEEE80211_DPRINTF(("%s: discard auth from %s; "
- "state %u, seq %u\n", __func__,
- ether_sprintf(wh->i_addr2),
- ic->ic_state, seq));
- ic->ic_stats.is_rx_bad_auth++;
- break;
- }
- if (status != 0) {
- if_printf(&ic->ic_if,
- "authentication failed (reason %d) for %s\n",
- status,
- ether_sprintf(wh->i_addr3));
- if (ni != ic->ic_bss)
- ni->ni_fails++;
- ic->ic_stats.is_rx_auth_fail++;
- 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);
}
- ieee80211_new_state(ic, IEEE80211_S_ASSOC,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
- break;
- case IEEE80211_M_MONITOR:
- break;
+ return;
}
+ if (algo == IEEE80211_AUTH_ALG_SHARED)
+ ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
+ rstamp, seq, status);
+ else if (algo == IEEE80211_AUTH_ALG_OPEN)
+ ieee80211_auth_open(ic, wh, ni, rssi, 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: {
u_int16_t capinfo, bintval;
+ struct ieee80211_rsnparms rsn;
+ u_int8_t reason;
if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
- (ic->ic_state != IEEE80211_S_RUN))
+ ic->ic_state != IEEE80211_S_RUN) {
+ ic->ic_stats.is_rx_mgtdiscard++;
return;
+ }
if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
reassoc = 1;
@@ -948,11 +2111,14 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
+ * [tlv] WPA or RSN
*/
IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
- IEEE80211_DPRINTF(("%s: ignore other bss from %s\n",
- __func__, ether_sprintf(wh->i_addr2)));
+ 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;
}
@@ -960,7 +2126,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
bintval = le16toh(*(u_int16_t *)frm); frm += 2;
if (reassoc)
frm += 6; /* ignore current AP info */
- ssid = rates = xrates = NULL;
+ ssid = rates = xrates = wpa = wme = NULL;
while (frm < efrm) {
switch (*frm) {
case IEEE80211_ELEMID_SSID:
@@ -972,48 +2138,88 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
+ /* XXX verify only one of RSN and WPA ie's? */
+ case IEEE80211_ELEMID_RSN:
+ wpa = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm)) {
+ if (ic->ic_flags & IEEE80211_F_WPA1)
+ wpa = frm;
+ } else if (iswmeinfo(frm))
+ wme = frm;
+ /* XXX Atheros OUI support */
+ break;
}
frm += frm[1] + 2;
}
IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
- if (ssid[1] != ic->ic_bss->ni_esslen ||
- memcmp(ssid + 2, ic->ic_bss->ni_essid, ssid[1]) != 0) {
-#ifdef IEEE80211_DEBUG
- if (ieee80211_debug) {
- printf("%s: ssid unmatch ", __func__);
- ieee80211_print_essid(ssid + 2, ssid[1]);
- printf(" from %s\n", ether_sprintf(wh->i_addr2));
- }
-#endif
- ic->ic_stats.is_rx_ssidmismatch++;
- return;
- }
+ IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
+
if (ni == ic->ic_bss) {
- IEEE80211_DPRINTF(("%s: not authenticated for %s\n",
- __func__, ether_sprintf(wh->i_addr2)));
- ni = ieee80211_dup_bss(ic, wh->i_addr2);
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ "[%s] deny %s request, sta not authenticated\n",
+ ether_sprintf(wh->i_addr2),
+ reassoc ? "reassoc" : "assoc");
+ ni = ieee80211_dup_bss(ic->ic_sta, wh->i_addr2);
if (ni != NULL) {
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_ASSOC_NOT_AUTHED);
- ieee80211_free_node(ic, ni);
+ ieee80211_free_node(ni);
}
ic->ic_stats.is_rx_assoc_notauth++;
return;
}
- /* XXX per-node cipher suite */
+ if (wpa != NULL) {
+ /*
+ * Parse WPA 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.
+ */
+ rsn = ni->ni_rsn;
+ if (wpa[0] != IEEE80211_ELEMID_RSN)
+ reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh);
+ else
+ reason = ieee80211_parse_rsn(ic, wpa, &rsn, 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[0] != IEEE80211_ELEMID_RSN ? "WPA" : "RSN",
+ rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen,
+ rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen,
+ rsn.rsn_keymgmt, rsn.rsn_caps);
+ }
+ /* discard challenge after association */
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_DEVBUF);
+ ni->ni_challenge = NULL;
+ }
/* XXX some stations use the privacy bit for handling APs
that suport both encrypted and unencrypted traffic */
+ /* NB: PRIVACY flag bits are assumed to match */
if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 ||
- (capinfo & IEEE80211_CAPINFO_PRIVACY) !=
- ((ic->ic_flags & IEEE80211_F_WEPON) ?
- IEEE80211_CAPINFO_PRIVACY : 0)) {
- IEEE80211_DPRINTF(("%s: capability mismatch %x for %s\n",
- __func__, capinfo, ether_sprintf(wh->i_addr2)));
- ni->ni_associd = 0;
+ (capinfo & IEEE80211_CAPINFO_PRIVACY) ^
+ (ic->ic_flags & IEEE80211_F_PRIVACY)) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ "[%s] deny %s request, capability mismatch 0x%x\n",
+ ether_sprintf(wh->i_addr2),
+ reassoc ? "reassoc" : "assoc", capinfo);
IEEE80211_SEND_MGMT(ic, ni, resp,
IEEE80211_STATUS_CAPINFO);
+ ieee80211_node_leave(ic, ni);
ic->ic_stats.is_rx_assoc_capmismatch++;
return;
}
@@ -1021,11 +2227,13 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
if (ni->ni_rates.rs_nrates == 0) {
- IEEE80211_DPRINTF(("%s: rate unmatch for %s\n",
- __func__, ether_sprintf(wh->i_addr2)));
- ni->ni_associd = 0;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ "[%s] deny %s request, rate set mismatch\n",
+ ether_sprintf(wh->i_addr2),
+ reassoc ? "reassoc" : "assoc");
IEEE80211_SEND_MGMT(ic, ni, resp,
IEEE80211_STATUS_BASIC_RATE);
+ ieee80211_node_leave(ic, ni);
ic->ic_stats.is_rx_assoc_norate++;
return;
}
@@ -1036,33 +2244,51 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
ni->ni_chan = ic->ic_bss->ni_chan;
ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
ni->ni_fhindex = ic->ic_bss->ni_fhindex;
- if (ni->ni_associd == 0) {
- /* XXX handle rollover at 2007 */
- /* XXX guarantee uniqueness */
- ni->ni_associd = 0xc000 | ic->ic_bss->ni_associd++;
- newassoc = 1;
- } else
- newassoc = 0;
- /* XXX for 11g must turn off short slot time if long
- slot time sta associates */
- IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
- if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "station %s %s associated\n",
- (newassoc ? "newly" : "already"),
- ether_sprintf(ni->ni_macaddr));
- /* give driver a chance to setup state like ni_txrate */
- if (ic->ic_newassoc)
- (*ic->ic_newassoc)(ic, ni, newassoc);
+ if (wpa != NULL) {
+ /*
+ * Record WPA/RSN parameters for station, mark
+ * node as using WPA and record information element
+ * for applications that require it.
+ */
+ ni->ni_rsn = rsn;
+ 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_DEVBUF);
+ ni->ni_wpa_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_DEVBUF);
+ ni->ni_wme_ie = NULL;
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ }
+ ieee80211_node_join(ic, ni, resp);
break;
}
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
+ u_int16_t capinfo, associd;
u_int16_t status;
if (ic->ic_opmode != IEEE80211_M_STA ||
- ic->ic_state != IEEE80211_S_ASSOC)
+ ic->ic_state != IEEE80211_S_ASSOC) {
+ ic->ic_stats.is_rx_mgtdiscard++;
return;
+ }
/*
* asresp frame format
@@ -1071,26 +2297,28 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
* [2] association ID
* [tlv] supported rates
* [tlv] extended supported rates
+ * [tlv] WME
*/
IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
ni = ic->ic_bss;
- ni->ni_capinfo = le16toh(*(u_int16_t *)frm);
+ capinfo = le16toh(*(u_int16_t *)frm);
frm += 2;
-
status = le16toh(*(u_int16_t *)frm);
frm += 2;
if (status != 0) {
- if_printf(ifp, "association failed (reason %d) for %s\n",
- status, ether_sprintf(wh->i_addr3));
- if (ni != ic->ic_bss)
+ 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++;
+ ic->ic_stats.is_rx_auth_fail++; /* XXX */
return;
}
- ni->ni_associd = le16toh(*(u_int16_t *)frm);
+ associd = le16toh(*(u_int16_t *)frm);
frm += 2;
- rates = xrates = NULL;
+ rates = xrates = wpa = wme = NULL;
while (frm < efrm) {
switch (*frm) {
case IEEE80211_ELEMID_RATES:
@@ -1099,6 +2327,11 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswmeoui(frm))
+ wme = frm;
+ /* XXX Atheros OUI support */
+ break;
}
frm += frm[1] + 2;
}
@@ -1107,14 +2340,70 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
ieee80211_setup_rates(ic, ni, rates, xrates,
IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (ni->ni_rates.rs_nrates != 0)
- ieee80211_new_state(ic, IEEE80211_S_RUN,
- wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+ if (ni->ni_rates.rs_nrates == 0) {
+ 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++;
+ return;
+ }
+
+ ni->ni_capinfo = capinfo;
+ ni->ni_associd = associd;
+ if (wme != NULL && ieee80211_parse_wmeparams(ic, wme, wh)) {
+ ni->ni_flags |= IEEE80211_NODE_QOS;
+ ieee80211_wme_updateparams(ic);
+ } else
+ ni->ni_flags &= ~IEEE80211_NODE_QOS;
+ /*
+ * Configure state now that we are associated.
+ *
+ * XXX may need different/additional driver callbacks?
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_11A ||
+ (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,
+ ic->ic_curmode == IEEE80211_MODE_11A ||
+ (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+ /*
+ * Honor ERP protection.
+ *
+ * NB: ni_erp should zero for non-11g operation.
+ * XXX check ic_curmode anyway?
+ */
+ if (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\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" : ""
+ );
+ ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
break;
}
case IEEE80211_FC0_SUBTYPE_DEAUTH: {
u_int16_t reason;
+
+ if (ic->ic_state == IEEE80211_S_SCAN) {
+ ic->ic_stats.is_rx_mgtdiscard++;
+ return;
+ }
/*
* deauth frame format
* [2] reason
@@ -1122,6 +2411,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
reason = le16toh(*(u_int16_t *)frm);
ic->ic_stats.is_rx_deauth++;
+ IEEE80211_NODE_STAT(ni, rx_deauth);
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
ieee80211_new_state(ic, IEEE80211_S_AUTH,
@@ -1129,15 +2419,15 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
break;
case IEEE80211_M_HOSTAP:
if (ni != ic->ic_bss) {
- if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "station %s deauthenticated"
- " by peer (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), reason);
- /* node will be free'd on return */
- ieee80211_unref_node(&ni);
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+ "station %s deauthenticated by peer "
+ "(reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), reason);
+ ieee80211_node_leave(ic, ni);
}
break;
default:
+ ic->ic_stats.is_rx_mgtdiscard++;
break;
}
break;
@@ -1145,6 +2435,12 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
case IEEE80211_FC0_SUBTYPE_DISASSOC: {
u_int16_t reason;
+
+ if (ic->ic_state != IEEE80211_S_RUN &&
+ ic->ic_state != IEEE80211_S_AUTH) {
+ ic->ic_stats.is_rx_mgtdiscard++;
+ return;
+ }
/*
* disassoc frame format
* [2] reason
@@ -1152,6 +2448,7 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
reason = le16toh(*(u_int16_t *)frm);
ic->ic_stats.is_rx_disassoc++;
+ IEEE80211_NODE_STAT(ni, rx_disassoc);
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
ieee80211_new_state(ic, IEEE80211_S_ASSOC,
@@ -1159,25 +2456,227 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
break;
case IEEE80211_M_HOSTAP:
if (ni != ic->ic_bss) {
- if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "station %s disassociated"
- " by peer (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), reason);
- ni->ni_associd = 0;
- /* XXX node reclaimed how? */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "[%s] sta disassociated by peer (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), reason);
+ ieee80211_node_leave(ic, ni);
}
break;
default:
+ ic->ic_stats.is_rx_mgtdiscard++;
break;
}
break;
}
default:
- IEEE80211_DPRINTF(("%s: mgmt frame with subtype 0x%x not "
- "handled\n", __func__, subtype));
+ 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
+
+/*
+ * Handle station power-save state change.
+ */
+static void
+ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mbuf *m;
+
+ if (enable) {
+ if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
+ ic->ic_ps_sta++;
+ ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+ "[%s] power save mode on, %u sta's in ps mode\n",
+ ether_sprintf(ni->ni_macaddr), 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_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);
+ /* XXX if no stations in ps mode, flush mc frames */
+
+ /*
+ * Flush queued unicast frames.
+ */
+ if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) {
+ ic->ic_set_tim(ic, ni, 0); /* just in case */
+ return;
+ }
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+ "[%s] flush ps queue, %u packets queued\n",
+ ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_SAVEQ_QLEN(ni));
+ for (;;) {
+ int qlen;
+
+ IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
+ if (m == NULL)
+ break;
+ /*
+ * If this is the last packet, turn off the TIM bit.
+ * If there are more packets, set the more packets bit
+ * in the packet dispatched to the station.
+ */
+ if (qlen != 0) {
+ struct ieee80211_frame_min *wh =
+ mtod(m, struct ieee80211_frame_min *);
+ wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+ }
+ /* XXX need different driver interface */
+ /* XXX bypasses q max */
+ IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+ }
+}
+
+/*
+ * 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;
+ u_int16_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(*(u_int16_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(ic, ni);
+ ic->ic_stats.is_ps_qempty++; /* XXX node stat */
+ ic->ic_set_tim(ic, 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);
+ wh = mtod(m, struct ieee80211_frame_min *);
+ wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+ } else {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+ "[%s] recv ps-poll, send packet, queue empty\n",
+ ether_sprintf(ni->ni_macaddr));
+ ic->ic_set_tim(ic, ni, 0);
+ }
+ m->m_flags |= M_PWR_SAV; /* bypass PS handling */
+ IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+}
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Debugging support.
+ */
+
+/*
+ * Return the bssid of a frame.
+ */
+static const u_int8_t *
+ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
+{
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ return wh->i_addr2;
+ if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS)
+ return wh->i_addr1;
+ if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
+ return wh->i_addr1;
+ return wh->i_addr3;
+}
+
+static void
+ieee80211_discard_frame(struct ieee80211com *ic,
+ const struct ieee80211_frame *wh,
+ const char *type, const char *fmt, ...)
+{
+ va_list ap;
+
+ printf("[%s] discard ", ether_sprintf(ieee80211_getbssid(ic, wh)));
+ if (type != NULL)
+ printf(" %s frame, ", type);
+ else
+ printf(" frame, ");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+}
+
+static void
+ieee80211_discard_ie(struct ieee80211com *ic,
+ const struct ieee80211_frame *wh,
+ const char *type, const char *fmt, ...)
+{
+ va_list ap;
+
+ printf("[%s] discard ", ether_sprintf(ieee80211_getbssid(ic, wh)));
+ if (type != NULL)
+ printf(" %s information element, ", type);
+ else
+ printf(" information element, ");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+}
+
+static void
+ieee80211_discard_mac(struct ieee80211com *ic,
+ const u_int8_t mac[IEEE80211_ADDR_LEN],
+ const char *type, const char *fmt, ...)
+{
+ va_list ap;
+
+ printf("[%s] discard ", ether_sprintf(mac));
+ if (type != NULL)
+ printf(" %s frame, ", type);
+ else
+ printf(" frame, ");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+}
+#endif /* IEEE80211_DEBUG */
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 7599591..059d215 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, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -67,26 +67,129 @@ __FBSDID("$FreeBSD$");
#include <dev/wi/if_wavelan_ieee.h>
+#define IS_UP(_ic) \
+ (((_ic)->ic_ifp->if_flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))
+#define IS_UP_AUTO(_ic) \
+ (IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
+
/*
* XXX
* Wireless LAN specific configuration interface, which is compatible
* with wicontrol(8).
*/
+struct wi_read_ap_args {
+ int i; /* result count */
+ struct wi_apinfo *ap; /* current entry in result buffer */
+ caddr_t max; /* result buffer bound */
+};
+
+static void
+wi_read_ap_result(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct wi_read_ap_args *sa = arg;
+ struct wi_apinfo *ap = sa->ap;
+ struct ieee80211_rateset *rs;
+ int j;
+
+ if ((caddr_t)(ap + 1) > sa->max)
+ return;
+ memset(ap, 0, sizeof(struct wi_apinfo));
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr);
+ ap->namelen = ic->ic_des_esslen;
+ if (ic->ic_des_esslen)
+ memcpy(ap->name, ic->ic_des_essid,
+ ic->ic_des_esslen);
+ } else {
+ IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid);
+ ap->namelen = ni->ni_esslen;
+ if (ni->ni_esslen)
+ memcpy(ap->name, ni->ni_essid,
+ ni->ni_esslen);
+ }
+ ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan);
+ ap->signal = ic->ic_node_getrssi(ni);
+ ap->capinfo = ni->ni_capinfo;
+ ap->interval = ni->ni_intval;
+ rs = &ni->ni_rates;
+ for (j = 0; j < rs->rs_nrates; j++) {
+ if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) {
+ ap->rate = (rs->rs_rates[j] &
+ IEEE80211_RATE_VAL) * 5; /* XXX */
+ }
+ }
+ sa->i++;
+ sa->ap++;
+}
+
+struct wi_read_prism2_args {
+ int i; /* result count */
+ struct wi_scan_res *res;/* current entry in result buffer */
+ caddr_t max; /* result buffer bound */
+};
+
+static void
+wi_read_prism2_result(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct wi_read_prism2_args *sa = arg;
+ struct wi_scan_res *res = sa->res;
+
+ if ((caddr_t)(res + 1) > sa->max)
+ return;
+ res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
+ res->wi_noise = 0;
+ res->wi_signal = ic->ic_node_getrssi(ni);
+ IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid);
+ res->wi_interval = ni->ni_intval;
+ res->wi_capinfo = ni->ni_capinfo;
+ res->wi_ssid_len = ni->ni_esslen;
+ memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN);
+ /* NB: assumes wi_srates holds <= ni->ni_rates */
+ memcpy(res->wi_srates, ni->ni_rates.rs_rates,
+ sizeof(res->wi_srates));
+ if (ni->ni_rates.rs_nrates < 10)
+ res->wi_srates[ni->ni_rates.rs_nrates] = 0;
+ res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate];
+ res->wi_rsvd = 0;
+
+ sa->i++;
+ sa->res++;
+}
+
+struct wi_read_sigcache_args {
+ int i; /* result count */
+ struct wi_sigcache *wsc;/* current entry in result buffer */
+ caddr_t max; /* result buffer bound */
+};
+
+static void
+wi_read_sigcache(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct wi_read_sigcache_args *sa = arg;
+ struct wi_sigcache *wsc = sa->wsc;
+
+ if ((caddr_t)(wsc + 1) > sa->max)
+ return;
+ memset(wsc, 0, sizeof(struct wi_sigcache));
+ IEEE80211_ADDR_COPY(wsc->macsrc, ni->ni_macaddr);
+ wsc->signal = ic->ic_node_getrssi(ni);
+
+ sa->wsc++;
+ sa->i++;
+}
+
int
-ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
+ieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ifnet *ifp = ic->ic_ifp;
int i, j, error;
struct ifreq *ifr = (struct ifreq *)data;
struct wi_req wreq;
struct wi_ltv_keys *keys;
- struct wi_apinfo *ap;
- struct ieee80211_node *ni;
- struct ieee80211_rateset *rs;
- struct wi_sigcache wsc;
- struct wi_scan_p2_hdr *p2;
- struct wi_scan_res *res;
error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
if (error)
@@ -153,8 +256,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
case WI_RID_COMMS_QUALITY:
wreq.wi_val[0] = 0; /* quality */
- wreq.wi_val[1] =
- htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss));
+ wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss));
wreq.wi_val[2] = 0; /* noise */
wreq.wi_len = 3;
break;
@@ -199,7 +301,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
wreq.wi_len = 1;
break;
case WI_RID_ROAMING_MODE:
- wreq.wi_val[0] = htole16(1); /* enabled ... not supported */
+ wreq.wi_val[0] = htole16(ic->ic_roaming); /* XXX map */
wreq.wi_len = 1;
break;
case WI_RID_SYSTEM_SCALE:
@@ -220,8 +322,7 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
wreq.wi_len = 1;
break;
case WI_RID_WEP_AVAIL:
- wreq.wi_val[0] =
- htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0);
+ wreq.wi_val[0] = htole16(1); /* always available */
wreq.wi_len = 1;
break;
case WI_RID_CNFAUTHMODE:
@@ -230,11 +331,11 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
case WI_RID_ENCRYPTION:
wreq.wi_val[0] =
- htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0);
+ htole16((ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0);
wreq.wi_len = 1;
break;
case WI_RID_TX_CRYPT_KEY:
- wreq.wi_val[0] = htole16(ic->ic_wep_txkey);
+ wreq.wi_val[0] = htole16(ic->ic_def_txkey);
wreq.wi_len = 1;
break;
case WI_RID_DEFLT_CRYPT_KEYS:
@@ -248,124 +349,71 @@ ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
}
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
keys->wi_keys[i].wi_keylen =
- htole16(ic->ic_nw_keys[i].wk_len);
+ htole16(ic->ic_nw_keys[i].wk_keylen);
memcpy(keys->wi_keys[i].wi_keydat,
- ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len);
+ ic->ic_nw_keys[i].wk_key,
+ ic->ic_nw_keys[i].wk_keylen);
}
wreq.wi_len = sizeof(*keys) / 2;
break;
case WI_RID_MAX_DATALEN:
- wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN); /* TODO: frag */
+ wreq.wi_val[0] = htole16(ic->ic_fragthreshold);
wreq.wi_len = 1;
break;
case WI_RID_IFACE_STATS:
/* XXX: should be implemented in lower drivers */
break;
case WI_RID_READ_APS:
- if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
- /*
- * Don't return results until active scan completes.
- */
- if (ic->ic_state == IEEE80211_S_SCAN &&
- (ic->ic_flags & IEEE80211_F_ASCAN)) {
- error = EINPROGRESS;
- break;
- }
- }
- i = 0;
- ap = (void *)((char *)wreq.wi_val + sizeof(i));
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
- if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1))
- break;
- memset(ap, 0, sizeof(*ap));
- if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr);
- ap->namelen = ic->ic_des_esslen;
- if (ic->ic_des_esslen)
- memcpy(ap->name, ic->ic_des_essid,
- ic->ic_des_esslen);
- } else {
- IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid);
- ap->namelen = ni->ni_esslen;
- if (ni->ni_esslen)
- memcpy(ap->name, ni->ni_essid,
- ni->ni_esslen);
- }
- ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan);
- ap->signal = (*ic->ic_node_getrssi)(ic, ni);
- ap->capinfo = ni->ni_capinfo;
- ap->interval = ni->ni_intval;
- rs = &ni->ni_rates;
- for (j = 0; j < rs->rs_nrates; j++) {
- if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) {
- ap->rate = (rs->rs_rates[j] &
- IEEE80211_RATE_VAL) * 5; /* XXX */
- }
- }
- i++;
- ap++;
- }
- memcpy(wreq.wi_val, &i, sizeof(i));
- wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2;
+ /*
+ * Don't return results until active scan completes.
+ */
+ if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) {
+ struct wi_read_ap_args args;
+
+ args.i = 0;
+ args.ap = (void *)((char *)wreq.wi_val + sizeof(i));
+ args.max = (void *)(&wreq + 1);
+ ieee80211_iterate_nodes(&ic->ic_scan,
+ wi_read_ap_result, &args);
+ memcpy(wreq.wi_val, &args.i, sizeof(args.i));
+ wreq.wi_len = (sizeof(int) +
+ sizeof(struct wi_apinfo) * args.i) / 2;
+ } else
+ error = EINPROGRESS;
break;
case WI_RID_PRISM2:
- wreq.wi_val[0] = 1; /* XXX lie so SCAN_RES can give rates */
+ /* NB: we lie so WI_RID_SCAN_RES can include rates */
+ wreq.wi_val[0] = 1;
wreq.wi_len = sizeof(u_int16_t) / 2;
break;
case WI_RID_SCAN_RES: /* compatibility interface */
- if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
- ic->ic_state == IEEE80211_S_SCAN &&
- (ic->ic_flags & IEEE80211_F_ASCAN)) {
+ if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) {
+ struct wi_read_prism2_args args;
+ struct wi_scan_p2_hdr *p2;
+
+ /* NB: use Prism2 format so we can include rate info */
+ p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
+ args.i = 0;
+ args.res = (void *)&p2[1];
+ args.max = (void *)(&wreq + 1);
+ ieee80211_iterate_nodes(&ic->ic_scan,
+ wi_read_prism2_result, &args);
+ p2->wi_rsvd = 0;
+ p2->wi_reason = args.i;
+ wreq.wi_len = (sizeof(*p2) +
+ sizeof(struct wi_scan_res) * args.i) / 2;
+ } else
error = EINPROGRESS;
- break;
- }
- /* NB: we use the Prism2 format so we can return rate info */
- p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
- res = (void *)&p2[1];
- i = 0;
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
- if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1))
- break;
- res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
- res->wi_noise = 0;
- res->wi_signal = (*ic->ic_node_getrssi)(ic, ni);
- IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid);
- res->wi_interval = ni->ni_intval;
- res->wi_capinfo = ni->ni_capinfo;
- res->wi_ssid_len = ni->ni_esslen;
- memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN);
- /* NB: assumes wi_srates holds <= ni->ni_rates */
- memcpy(res->wi_srates, ni->ni_rates.rs_rates,
- sizeof(res->wi_srates));
- if (ni->ni_rates.rs_nrates < 10)
- res->wi_srates[ni->ni_rates.rs_nrates] = 0;
- res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate];
- res->wi_rsvd = 0;
- res++, i++;
- }
- p2->wi_rsvd = 0;
- p2->wi_reason = i;
- wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2;
break;
- case WI_RID_READ_CACHE:
- i = 0;
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
- if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1)
- break;
- IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr);
- memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc));
- wsc.signal = (*ic->ic_node_getrssi)(ic, ni);
- wsc.noise = 0;
- wsc.quality = 0;
- memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i,
- &wsc, sizeof(wsc));
- i++;
- }
- wreq.wi_len = sizeof(wsc) * i / 2;
- break;
- case WI_RID_SCAN_APS:
- error = EINVAL;
+ case WI_RID_READ_CACHE: {
+ struct wi_read_sigcache_args args;
+ args.i = 0;
+ args.wsc = (struct wi_sigcache *) wreq.wi_val;
+ args.max = (void *)(&wreq + 1);
+ ieee80211_iterate_nodes(&ic->ic_scan, wi_read_sigcache, &args);
+ wreq.wi_len = sizeof(struct wi_sigcache) * args.i / 2;
break;
+ }
default:
error = EINVAL;
break;
@@ -397,11 +445,20 @@ findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
* the active list as the place to start the scan.
*/
static int
-ieee80211_setupscan(struct ieee80211com *ic)
+ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[])
{
- u_char *chanlist = ic->ic_chan_active;
int i;
+ /*
+ * XXX don't permit a scan to be started unless we
+ * know the device is ready. For the moment this means
+ * the device is marked up as this is the required to
+ * initialize the hardware. It would be better to permit
+ * scanning prior to being up but that'll require some
+ * changes to the infrastructure.
+ */
+ if (!IS_UP(ic))
+ return EINVAL;
if (ic->ic_ibss_chan == NULL ||
isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
@@ -416,21 +473,21 @@ found:
if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+ memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
/*
- * XXX don't permit a scan to be started unless we
- * know the device is ready. For the moment this means
- * the device is marked up as this is the required to
- * initialize the hardware. It would be better to permit
- * scanning prior to being up but that'll require some
- * changes to the infrastructure.
+ * We force the state to INIT before calling ieee80211_new_state
+ * to get ieee80211_begin_scan called. We really want to scan w/o
+ * altering the current state but that's not possible right now.
*/
- return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET;
+ /* XXX handle proberequest case */
+ ic->ic_state = IEEE80211_S_INIT; /* XXX bypass state machine */
+ return 0;
}
int
-ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
+ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ifnet *ifp = ic->ic_ifp;
int i, j, len, error, rate;
struct ifreq *ifr = (struct ifreq *)data;
struct wi_ltv_keys *keys;
@@ -470,7 +527,9 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
isclr(ic->ic_chan_active, i))
return EINVAL;
ic->ic_ibss_chan = &ic->ic_channels[i];
- if (ic->ic_flags & IEEE80211_F_SIBSS)
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ else
error = ENETRESET;
break;
case WI_RID_CURRENT_CHAN:
@@ -516,7 +575,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
}
if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
ic->ic_opmode = le16toh(wreq.wi_val[0]);
- error = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
}
break;
#if 0
@@ -564,7 +623,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
return EINVAL;
setrate:
ic->ic_fixed_rate = i;
- error = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
break;
case WI_RID_CUR_TX_RATE:
return EPERM;
@@ -584,14 +643,14 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
ic->ic_flags |= IEEE80211_F_IBSSON;
if (ic->ic_opmode == IEEE80211_M_IBSS &&
ic->ic_state == IEEE80211_S_SCAN)
- error = ENETRESET;
+ error = IS_UP_AUTO(ic) ? ENETRESET : 0;
}
} else {
if (ic->ic_flags & IEEE80211_F_IBSSON) {
ic->ic_flags &= ~IEEE80211_F_IBSSON;
if (ic->ic_flags & IEEE80211_F_SIBSS) {
ic->ic_flags &= ~IEEE80211_F_SIBSS;
- error = ENETRESET;
+ error = IS_UP_AUTO(ic) ? ENETRESET : 0;
}
}
}
@@ -605,8 +664,10 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
case WI_RID_ROAMING_MODE:
if (len != 2)
return EINVAL;
- if (le16toh(wreq.wi_val[0]) != 1)
+ i = le16toh(wreq.wi_val[0]);
+ if (i > IEEE80211_ROAMING_MANUAL)
return EINVAL; /* not supported */
+ ic->ic_roaming = i;
break;
case WI_RID_SYSTEM_SCALE:
if (len != 2)
@@ -622,12 +683,12 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
return EINVAL;
if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
ic->ic_flags |= IEEE80211_F_PMGTON;
- error = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
}
} else {
if (ic->ic_flags & IEEE80211_F_PMGTON) {
ic->ic_flags &= ~IEEE80211_F_PMGTON;
- error = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
}
}
break;
@@ -636,7 +697,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
return EINVAL;
ic->ic_lintval = le16toh(wreq.wi_val[0]);
if (ic->ic_flags & IEEE80211_F_PMGTON)
- error = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
break;
case WI_RID_CUR_BEACON_INT:
return EPERM;
@@ -645,8 +706,11 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
case WI_RID_CNFAUTHMODE:
if (len != 2)
return EINVAL;
- if (le16toh(wreq.wi_val[0]) != 1)
- return EINVAL; /* TODO: shared key auth */
+ i = le16toh(wreq.wi_val[0]);
+ if (i > IEEE80211_AUTH_WPA)
+ return EINVAL;
+ ic->ic_bss->ni_authmode = i; /* XXX ENETRESET? */
+ error = ENETRESET;
break;
case WI_RID_ENCRYPTION:
if (len != 2)
@@ -654,13 +718,13 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
if (wreq.wi_val[0] != 0) {
if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
return EINVAL;
- if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) {
- ic->ic_flags |= IEEE80211_F_WEPON;
+ if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
error = ENETRESET;
}
} else {
- if (ic->ic_flags & IEEE80211_F_WEPON) {
- ic->ic_flags &= ~IEEE80211_F_WEPON;
+ if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ ic->ic_flags &= ~IEEE80211_F_PRIVACY;
error = ENETRESET;
}
}
@@ -671,7 +735,8 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
i = le16toh(wreq.wi_val[0]);
if (i >= IEEE80211_WEP_NKID)
return EINVAL;
- ic->ic_wep_txkey = i;
+ ic->ic_def_txkey = i;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
break;
case WI_RID_DEFLT_CRYPT_KEYS:
if (len != sizeof(struct wi_ltv_keys))
@@ -681,15 +746,20 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
len = le16toh(keys->wi_keys[i].wi_keylen);
if (len != 0 && len < IEEE80211_WEP_KEYLEN)
return EINVAL;
- if (len > sizeof(ic->ic_nw_keys[i].wk_key))
+ if (len > IEEE80211_KEYBUF_SIZE)
return EINVAL;
}
- memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys));
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ struct ieee80211_key *k = &ic->ic_nw_keys[i];
+
len = le16toh(keys->wi_keys[i].wi_keylen);
- ic->ic_nw_keys[i].wk_len = len;
- memcpy(ic->ic_nw_keys[i].wk_key,
- keys->wi_keys[i].wi_keydat, len);
+ k->wk_keylen = len;
+ k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+ memset(k->wk_key, 0, sizeof(k->wk_key));
+ memcpy(k->wk_key, keys->wi_keys[i].wi_keydat, len);
+#if 0
+ k->wk_type = IEEE80211_CIPHER_WEP;
+#endif
}
error = ENETRESET;
break;
@@ -699,10 +769,8 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
len = le16toh(wreq.wi_val[0]);
if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
return EINVAL;
- if (len != IEEE80211_MAX_LEN)
- return EINVAL; /* TODO: fragment */
ic->ic_fragthreshold = len;
- error = ENETRESET;
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
break;
case WI_RID_IFACE_STATS:
error = EPERM;
@@ -710,7 +778,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
case WI_RID_SCAN_REQ: /* XXX wicontrol */
if (ic->ic_opmode == IEEE80211_M_HOSTAP)
break;
- error = ieee80211_setupscan(ic);
+ error = ieee80211_setupscan(ic, ic->ic_chan_avail);
if (error == 0)
error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
break;
@@ -742,9 +810,7 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
}
setbit(chanlist, i);
}
- memcpy(ic->ic_chan_active, chanlist,
- sizeof(ic->ic_chan_active));
- error = ieee80211_setupscan(ic);
+ error = ieee80211_setupscan(ic, chanlist);
if (wreq.wi_type == WI_RID_CHANNEL_LIST) {
/* NB: ignore error from ieee80211_setupscan */
error = ENETRESET;
@@ -755,311 +821,1532 @@ ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
error = EINVAL;
break;
}
+ if (error == ENETRESET && !IS_UP_AUTO(ic))
+ error = 0;
return error;
}
-int
-ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+static struct ieee80211_channel *
+getcurchan(struct ieee80211com *ic)
+{
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ return ic->ic_des_chan;
+ default:
+ return ic->ic_ibss_chan;
+ }
+}
+
+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)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_key ik;
+ struct ieee80211_key *wk;
+ const struct ieee80211_cipher *cip;
+ u_int kid;
+ int error;
+
+ if (ireq->i_len != sizeof(ik))
+ return EINVAL;
+ error = copyin(ireq->i_data, &ik, sizeof(ik));
+ if (error)
+ return error;
+ kid = ik.ik_keyix;
+ if (kid == IEEE80211_KEYIX_NONE) {
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, ik.ik_macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ 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);
+ 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)
+ ik.ik_flags |= IEEE80211_KEY_DEFAULT;
+ if (suser(curthread) == 0) {
+ /* NB: only root can read key data */
+ ik.ik_keyrsc = wk->wk_keyrsc;
+ ik.ik_keytsc = wk->wk_keytsc;
+ memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
+ if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
+ memcpy(ik.ik_keydata+wk->wk_keylen,
+ wk->wk_key + IEEE80211_KEYBUF_SIZE,
+ IEEE80211_MICBUF_SIZE);
+ ik.ik_keylen += IEEE80211_MICBUF_SIZE;
+ }
+ } else {
+ ik.ik_keyrsc = 0;
+ ik.ik_keytsc = 0;
+ memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata));
+ }
+ if (ni != NULL)
+ ieee80211_free_node(ni);
+ return copyout(&ik, ireq->i_data, sizeof(ik));
+}
+
+static int
+ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+
+ 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)
+{
+ struct ieee80211req_chaninfo chans; /* XXX off stack? */
+ int i, space;
+
+ /*
+ * Since channel 0 is not available for DS, channel 1
+ * is assigned to LSB on WaveLAN.
+ */
+ if (ic->ic_phytype == IEEE80211_T_DS)
+ i = 1;
+ else
+ i = 0;
+ memset(&chans, 0, sizeof(chans));
+ for (; i <= IEEE80211_CHAN_MAX; i++)
+ if (isset(ic->ic_chan_avail, i)) {
+ struct ieee80211_channel *c = &ic->ic_channels[i];
+ chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq;
+ chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags;
+ chans.ic_nchans++;
+ }
+ space = __offsetof(struct ieee80211req_chaninfo,
+ ic_chans[chans.ic_nchans]);
+ if (space > ireq->i_len)
+ space = ireq->i_len;
+ return copyout(&chans, ireq->i_data, space);
+}
+
+static int
+ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_wpaie wpaie;
+ int error;
+
+ if (ireq->i_len < IEEE80211_ADDR_LEN)
+ return EINVAL;
+ error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN);
+ if (error != 0)
+ return error;
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, wpaie.wpa_macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
+ if (ni->ni_wpa_ie != NULL) {
+ int ielen = ni->ni_wpa_ie[1] + 2;
+ if (ielen > sizeof(wpaie.wpa_ie))
+ ielen = sizeof(wpaie.wpa_ie);
+ memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
+ }
+ ieee80211_free_node(ni);
+ if (ireq->i_len > sizeof(wpaie))
+ ireq->i_len = sizeof(wpaie);
+ return copyout(&wpaie, ireq->i_data, ireq->i_len);
+}
+
+static int
+ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ u_int8_t macaddr[IEEE80211_ADDR_LEN];
+ const int off = __offsetof(struct ieee80211req_sta_stats, is_stats);
+ int error;
+
+ if (ireq->i_len < off)
+ return EINVAL;
+ error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
+ if (error != 0)
+ return error;
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ if (ireq->i_len > sizeof(struct ieee80211req_sta_stats))
+ ireq->i_len = sizeof(struct ieee80211req_sta_stats);
+ /* NB: copy out only the statistics */
+ error = copyout(&ni->ni_stats, (u_int8_t *) ireq->i_data + off,
+ ireq->i_len - off);
+ ieee80211_free_node(ni);
+ return error;
+}
+
+static void
+get_scan_result(struct ieee80211req_scan_result *sr,
+ const struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ memset(sr, 0, sizeof(*sr));
+ sr->isr_ssid_len = ni->ni_esslen;
+ if (ni->ni_wpa_ie != NULL)
+ sr->isr_ie_len += 2+ni->ni_wpa_ie[1];
+ if (ni->ni_wme_ie != NULL)
+ sr->isr_ie_len += 2+ni->ni_wme_ie[1];
+ sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len;
+ sr->isr_len = roundup(sr->isr_len, sizeof(u_int32_t));
+ if (ni->ni_chan != IEEE80211_CHAN_ANYC) {
+ sr->isr_freq = ni->ni_chan->ic_freq;
+ sr->isr_flags = ni->ni_chan->ic_flags;
+ }
+ sr->isr_rssi = ic->ic_node_getrssi(ni);
+ sr->isr_intval = ni->ni_intval;
+ sr->isr_capinfo = ni->ni_capinfo;
+ sr->isr_erp = ni->ni_erp;
+ IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid);
+ sr->isr_nrates = ni->ni_rates.rs_nrates;
+ if (sr->isr_nrates > 15)
+ sr->isr_nrates = 15;
+ memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates);
+}
+
+static int
+ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ union {
+ struct ieee80211req_scan_result res;
+ char data[512]; /* XXX shrink? */
+ } u;
+ struct ieee80211req_scan_result *sr = &u.res;
+ struct ieee80211_node_table *nt;
+ struct ieee80211_node *ni;
+ int error, space;
+ u_int8_t *p, *cp;
+
+ p = ireq->i_data;
+ space = ireq->i_len;
+ error = 0;
+ /* XXX locking */
+ nt = &ic->ic_scan;
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+ /* NB: skip pre-scan node state */
+ if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+ continue;
+ get_scan_result(sr, ni);
+ if (sr->isr_len > sizeof(u))
+ continue; /* XXX */
+ if (space < sr->isr_len)
+ break;
+ cp = (u_int8_t *)(sr+1);
+ memcpy(cp, ni->ni_essid, ni->ni_esslen);
+ cp += ni->ni_esslen;
+ if (ni->ni_wpa_ie != NULL) {
+ memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]);
+ cp += 2+ni->ni_wpa_ie[1];
+ }
+ if (ni->ni_wme_ie != NULL) {
+ memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]);
+ cp += 2+ni->ni_wme_ie[1];
+ }
+ error = copyout(sr, p, sr->isr_len);
+ if (error)
+ break;
+ p += sr->isr_len;
+ space -= sr->isr_len;
+ }
+ ireq->i_len -= space;
+ return error;
+}
+
+static void
+get_sta_info(struct ieee80211req_sta_info *si, const struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+
+ si->isi_ie_len = 0;
+ if (ni->ni_wpa_ie != NULL)
+ si->isi_ie_len += 2+ni->ni_wpa_ie[1];
+ if (ni->ni_wme_ie != NULL)
+ si->isi_ie_len += 2+ni->ni_wme_ie[1];
+ si->isi_len = sizeof(*si) + si->isi_ie_len, sizeof(u_int32_t);
+ si->isi_len = roundup(si->isi_len, sizeof(u_int32_t));
+ si->isi_freq = ni->ni_chan->ic_freq;
+ si->isi_flags = ni->ni_chan->ic_flags;
+ si->isi_state = ni->ni_flags;
+ si->isi_authmode = ni->ni_authmode;
+ si->isi_rssi = ic->ic_node_getrssi(ni);
+ si->isi_capinfo = ni->ni_capinfo;
+ si->isi_erp = ni->ni_erp;
+ IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
+ si->isi_nrates = ni->ni_rates.rs_nrates;
+ if (si->isi_nrates > 15)
+ si->isi_nrates = 15;
+ memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
+ si->isi_txrate = ni->ni_txrate;
+ si->isi_associd = ni->ni_associd;
+ si->isi_txpower = ni->ni_txpower;
+ si->isi_vlan = ni->ni_vlan;
+ if (ni->ni_flags & IEEE80211_NODE_QOS) {
+ memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs));
+ memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs));
+ } else {
+ si->isi_txseqs[0] = ni->ni_txseqs[0];
+ si->isi_rxseqs[0] = ni->ni_rxseqs[0];
+ }
+ if (ic->ic_opmode == IEEE80211_M_IBSS || ni->ni_associd != 0)
+ si->isi_inact = ic->ic_inact_run;
+ else if (ieee80211_node_is_authorized(ni))
+ si->isi_inact = ic->ic_inact_auth;
+ else
+ si->isi_inact = ic->ic_inact_init;
+ si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
+}
+
+static int
+ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ union {
+ struct ieee80211req_sta_info info;
+ char data[512]; /* XXX shrink? */
+ } u;
+ struct ieee80211req_sta_info *si = &u.info;
+ struct ieee80211_node_table *nt;
+ struct ieee80211_node *ni;
+ int error, space;
+ u_int8_t *p, *cp;
+
+ nt = ic->ic_sta;
+ if (nt == NULL)
+ return EINVAL;
+ p = ireq->i_data;
+ space = ireq->i_len;
+ error = 0;
+ /* XXX locking */
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+ get_sta_info(si, ni);
+ if (si->isi_len > sizeof(u))
+ continue; /* XXX */
+ if (space < si->isi_len)
+ break;
+ cp = (u_int8_t *)(si+1);
+ if (ni->ni_wpa_ie != NULL) {
+ memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]);
+ cp += 2+ni->ni_wpa_ie[1];
+ }
+ if (ni->ni_wme_ie != NULL) {
+ memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]);
+ cp += 2+ni->ni_wme_ie[1];
+ }
+ error = copyout(si, p, si->isi_len);
+ if (error)
+ break;
+ p += si->isi_len;
+ space -= si->isi_len;
+ }
+ ireq->i_len -= space;
+ return error;
+}
+
+static int
+ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_sta_txpow txpow;
+ int error;
+
+ if (ireq->i_len != sizeof(txpow))
+ return EINVAL;
+ error = copyin(ireq->i_data, &txpow, sizeof(txpow));
+ if (error != 0)
+ return error;
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, txpow.it_macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ 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)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_wme_state *wme = &ic->ic_wme;
+ struct wmeParams *wmep;
+ int ac;
+
+ if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+ return EINVAL;
+
+ ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
+ if (ac >= WME_NUM_AC)
+ ac = WME_AC_BE;
+ if (ireq->i_len & IEEE80211_WMEPARAM_BSS)
+ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+ else
+ wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
+ ireq->i_val = wmep->wmep_logcwmin;
+ break;
+ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
+ ireq->i_val = wmep->wmep_logcwmax;
+ break;
+ case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
+ ireq->i_val = wmep->wmep_aifsn;
+ break;
+ case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
+ ireq->i_val = wmep->wmep_txopLimit;
+ break;
+ case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
+ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+ ireq->i_val = wmep->wmep_acm;
+ break;
+ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/
+ wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+ ireq->i_val = !wmep->wmep_noackPolicy;
+ break;
+ }
+ return 0;
+}
+
+static int
+ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+{
+ const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
int error = 0;
- u_int kid, len;
- struct ieee80211req *ireq;
- struct ifreq *ifr;
+ u_int kid, len, m;
u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
char tmpssid[IEEE80211_NWID_LEN];
- struct ieee80211_channel *chan;
- struct ifaddr *ifa; /* XXX */
- switch (cmd) {
- case SIOCSIFMEDIA:
- case SIOCGIFMEDIA:
- error = ifmedia_ioctl(ifp, (struct ifreq *) data,
- &ic->ic_media, cmd);
- break;
- case SIOCG80211:
- ireq = (struct ieee80211req *) data;
- switch (ireq->i_type) {
- case IEEE80211_IOC_SSID:
- switch (ic->ic_state) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- ireq->i_len = ic->ic_des_esslen;
- memcpy(tmpssid, ic->ic_des_essid, ireq->i_len);
- break;
- default:
- ireq->i_len = ic->ic_bss->ni_esslen;
- memcpy(tmpssid, ic->ic_bss->ni_essid,
- ireq->i_len);
- break;
- }
- error = copyout(tmpssid, ireq->i_data, ireq->i_len);
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_SSID:
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
+ ireq->i_len = ic->ic_des_esslen;
+ memcpy(tmpssid, ic->ic_des_essid, ireq->i_len);
break;
- case IEEE80211_IOC_NUMSSIDS:
+ default:
+ ireq->i_len = ic->ic_bss->ni_esslen;
+ memcpy(tmpssid, ic->ic_bss->ni_essid,
+ ireq->i_len);
+ break;
+ }
+ error = copyout(tmpssid, ireq->i_data, ireq->i_len);
+ break;
+ case IEEE80211_IOC_NUMSSIDS:
+ ireq->i_val = 1;
+ break;
+ case IEEE80211_IOC_WEP:
+ if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+ ireq->i_val = IEEE80211_WEP_OFF;
+ else if (ic->ic_flags & IEEE80211_F_DROPUNENC)
+ ireq->i_val = IEEE80211_WEP_ON;
+ else
+ ireq->i_val = IEEE80211_WEP_MIXED;
+ break;
+ case IEEE80211_IOC_WEPKEY:
+ kid = (u_int) ireq->i_val;
+ if (kid >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
+ /* NB: only root can read WEP keys */
+ if (suser(curthread) == 0) {
+ bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
+ } else {
+ bzero(tmpkey, len);
+ }
+ ireq->i_len = len;
+ error = copyout(tmpkey, ireq->i_data, len);
+ break;
+ case IEEE80211_IOC_NUMWEPKEYS:
+ ireq->i_val = IEEE80211_WEP_NKID;
+ break;
+ case IEEE80211_IOC_WEPTXKEY:
+ ireq->i_val = ic->ic_def_txkey;
+ break;
+ case IEEE80211_IOC_AUTHMODE:
+ if (ic->ic_flags & IEEE80211_F_WPA)
+ ireq->i_val = IEEE80211_AUTH_WPA;
+ else
+ ireq->i_val = ic->ic_bss->ni_authmode;
+ break;
+ case IEEE80211_IOC_CHANNEL:
+ ireq->i_val = ieee80211_chan2ieee(ic, getcurchan(ic));
+ break;
+ case IEEE80211_IOC_POWERSAVE:
+ if (ic->ic_flags & IEEE80211_F_PMGTON)
+ ireq->i_val = IEEE80211_POWERSAVE_ON;
+ else
+ ireq->i_val = IEEE80211_POWERSAVE_OFF;
+ break;
+ case IEEE80211_IOC_POWERSAVESLEEP:
+ ireq->i_val = ic->ic_lintval;
+ break;
+ case IEEE80211_IOC_RTSTHRESHOLD:
+ ireq->i_val = ic->ic_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;
+ break;
+ case IEEE80211_IOC_WPA:
+ switch (ic->ic_flags & IEEE80211_F_WPA) {
+ case IEEE80211_F_WPA1:
ireq->i_val = 1;
break;
- case IEEE80211_IOC_WEP:
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
- ireq->i_val = IEEE80211_WEP_NOSUP;
- } else {
- if (ic->ic_flags & IEEE80211_F_WEPON) {
- ireq->i_val =
- IEEE80211_WEP_MIXED;
- } else {
- ireq->i_val =
- IEEE80211_WEP_OFF;
- }
- }
+ case IEEE80211_F_WPA2:
+ ireq->i_val = 2;
break;
- case IEEE80211_IOC_WEPKEY:
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
- error = EINVAL;
- break;
- }
- kid = (u_int) ireq->i_val;
- if (kid >= IEEE80211_WEP_NKID) {
- error = EINVAL;
- break;
- }
- len = (u_int) ic->ic_nw_keys[kid].wk_len;
- /* NB: only root can read WEP keys */
- if (suser(curthread) == 0) {
- bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
- } else {
- bzero(tmpkey, len);
- }
- ireq->i_len = len;
- error = copyout(tmpkey, ireq->i_data, len);
+ case IEEE80211_F_WPA1 | IEEE80211_F_WPA2:
+ ireq->i_val = 3;
break;
- case IEEE80211_IOC_NUMWEPKEYS:
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
- error = EINVAL;
- else
- ireq->i_val = IEEE80211_WEP_NKID;
+ default:
+ ireq->i_val = 0;
break;
- case IEEE80211_IOC_WEPTXKEY:
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
- error = EINVAL;
- else
- ireq->i_val = ic->ic_wep_txkey;
+ }
+ break;
+ case IEEE80211_IOC_CHANLIST:
+ error = ieee80211_ioctl_getchanlist(ic, ireq);
+ break;
+ case IEEE80211_IOC_ROAMING:
+ ireq->i_val = ic->ic_roaming;
+ break;
+ case IEEE80211_IOC_PRIVACY:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
+ break;
+ case IEEE80211_IOC_DROPUNENCRYPTED:
+ ireq->i_val = (ic->ic_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;
+ break;
+ case IEEE80211_IOC_WME:
+ ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0;
+ break;
+ case IEEE80211_IOC_HIDESSID:
+ ireq->i_val = (ic->ic_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);
+ break;
+ case IEEE80211_IOC_WPAKEY:
+ error = ieee80211_ioctl_getkey(ic, ireq);
+ break;
+ case IEEE80211_IOC_CHANINFO:
+ error = ieee80211_ioctl_getchaninfo(ic, 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,
+ ireq->i_data, ireq->i_len);
+ break;
+ case IEEE80211_IOC_WPAIE:
+ error = ieee80211_ioctl_getwpaie(ic, ireq);
+ break;
+ case IEEE80211_IOC_SCAN_RESULTS:
+ error = ieee80211_ioctl_getscanresults(ic, ireq);
+ break;
+ case IEEE80211_IOC_STA_STATS:
+ error = ieee80211_ioctl_getstastats(ic, ireq);
+ break;
+ case IEEE80211_IOC_TXPOWMAX:
+ ireq->i_val = ic->ic_bss->ni_txpower;
+ break;
+ case IEEE80211_IOC_STA_TXPOW:
+ error = ieee80211_ioctl_getstatxpow(ic, ireq);
+ break;
+ case IEEE80211_IOC_STA_INFO:
+ error = ieee80211_ioctl_getstainfo(ic, ireq);
+ break;
+ case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
+ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
+ case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
+ case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
+ case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
+ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */
+ error = ieee80211_ioctl_getwmeparam(ic, ireq);
+ break;
+ case IEEE80211_IOC_DTIM_PERIOD:
+ ireq->i_val = ic->ic_dtim_period;
+ break;
+ case IEEE80211_IOC_BEACON_INTERVAL:
+ /* NB: get from ic_bss for station mode */
+ ireq->i_val = ic->ic_bss->ni_intval;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+static int
+ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ int error;
+ void *ie;
+
+ /*
+ * 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 */
+ MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_WAITOK);
+ if (ie == NULL)
+ return ENOMEM;
+ error = copyin(ireq->i_data, ie, ireq->i_len);
+ /* XXX sanity check data? */
+ if (ic->ic_opt_ie != NULL)
+ FREE(ic->ic_opt_ie, M_DEVBUF);
+ ic->ic_opt_ie = ie;
+ ic->ic_opt_ie_len = ireq->i_len;
+ return 0;
+}
+
+static int
+ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211req_key ik;
+ struct ieee80211_node *ni;
+ struct ieee80211_key *wk;
+ u_int16_t kid;
+ int error;
+
+ if (ireq->i_len != sizeof(ik))
+ return EINVAL;
+ error = copyin(ireq->i_data, &ik, sizeof(ik));
+ if (error)
+ return error;
+ /* NB: cipher support is verified by ieee80211_crypt_newkey */
+ /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
+ if (ik.ik_keylen > sizeof(ik.ik_keydata))
+ return E2BIG;
+ kid = ik.ik_keyix;
+ if (kid == IEEE80211_KEYIX_NONE) {
+ /* XXX unicast keys currently must be tx/rx */
+ if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
+ return EINVAL;
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ ni = ic->ic_bss;
+ if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid))
+ return EADDRNOTAVAIL;
+ } else {
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, ik.ik_macaddr);
+ if (ni == NULL)
+ return ENOENT;
+ }
+ wk = &ni->ni_ucastkey;
+ } else {
+ if (kid >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ wk = &ic->ic_nw_keys[kid];
+ ni = NULL;
+ }
+ error = 0;
+ ieee80211_key_update_begin(ic);
+ if (ieee80211_crypto_newkey(ic, ik.ik_type, 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;
+ wk->wk_keytsc = 0; /* new key, reset */
+ wk->wk_flags |=
+ ik.ik_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV);
+ 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,
+ ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
+ error = EIO;
+ else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
+ ic->ic_def_txkey = kid;
+ } else
+ error = ENXIO;
+ ieee80211_key_update_end(ic);
+ if (ni != NULL)
+ ieee80211_free_node(ni);
+ return error;
+}
+
+static int
+ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211req_del_key dk;
+ int kid, error;
+
+ if (ireq->i_len != sizeof(dk))
+ return EINVAL;
+ error = copyin(ireq->i_data, &dk, sizeof(dk));
+ if (error)
+ return error;
+ kid = dk.idk_keyix;
+ /* XXX u_int8_t -> u_int16_t */
+ if (dk.idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) {
+ struct ieee80211_node *ni;
+
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, dk.idk_macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ /* XXX error return */
+ ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+ ieee80211_free_node(ni);
+ } else {
+ if (kid >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ /* XXX error return */
+ ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
+ }
+ return 0;
+}
+
+static void
+domlme(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211req_mlme *mlme = arg;
+
+ 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);
+ }
+ ieee80211_node_leave(ic, ni);
+}
+
+static int
+ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211req_mlme mlme;
+ struct ieee80211_node *ni;
+ int error;
+
+ if (ireq->i_len != sizeof(mlme))
+ return EINVAL;
+ error = copyin(ireq->i_data, &mlme, sizeof(mlme));
+ if (error)
+ return error;
+ switch (mlme.im_op) {
+ case IEEE80211_MLME_ASSOC:
+ if (ic->ic_opmode != IEEE80211_M_STA)
+ return EINVAL;
+ /* XXX must be in S_SCAN state? */
+
+ if (ic->ic_des_esslen != 0) {
+ /*
+ * Desired ssid specified; must match both bssid and
+ * ssid to distinguish ap advertising multiple ssid's.
+ */
+ ni = ieee80211_find_node_with_ssid(&ic->ic_scan,
+ mlme.im_macaddr,
+ ic->ic_des_esslen, ic->ic_des_essid);
+ } else {
+ /*
+ * Normal case; just match bssid.
+ */
+ ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr);
+ }
+ if (ni == NULL)
+ return EINVAL;
+ if (!ieee80211_sta_join(ic, ni)) {
+ ieee80211_free_node(ni);
+ return EINVAL;
+ }
+ break;
+ 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_IOC_AUTHMODE:
- ireq->i_val = IEEE80211_AUTH_OPEN;
+ 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 (ic->ic_sta == NULL ||
+ (ni = ieee80211_find_node(ic->ic_sta,
+ mlme.im_macaddr)) == NULL)
+ return EINVAL;
+ domlme(&mlme, ni);
+ ieee80211_free_node(ni);
+ } else {
+ if (ic->ic_sta != NULL)
+ ieee80211_iterate_nodes(ic->ic_sta,
+ domlme, &mlme);
+ }
break;
- case IEEE80211_IOC_CHANNEL:
- switch (ic->ic_state) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
- if (ic->ic_opmode == IEEE80211_M_STA)
- chan = ic->ic_des_chan;
- else
- chan = ic->ic_ibss_chan;
- break;
- default:
- chan = ic->ic_bss->ni_chan;
- break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case IEEE80211_MLME_AUTHORIZE:
+ case IEEE80211_MLME_UNAUTHORIZE:
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ return EINVAL;
+ if (ic->ic_sta == NULL)
+ 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(ic, ni);
+ else
+ ieee80211_node_unauthorize(ic, ni);
+ ieee80211_free_node(ni);
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int
+ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ u_int8_t mac[IEEE80211_ADDR_LEN];
+ const struct ieee80211_aclator *acl = ic->ic_acl;
+ int error;
+
+ if (ireq->i_len != sizeof(mac))
+ return EINVAL;
+ error = copyin(ireq->i_data, mac, ireq->i_len);
+ if (error)
+ return error;
+ if (acl == NULL) {
+ acl = ieee80211_aclator_get("mac");
+ if (acl == NULL || !acl->iac_attach(ic))
+ return EINVAL;
+ ic->ic_acl = acl;
+ }
+ if (ireq->i_type == IEEE80211_IOC_ADDMAC)
+ acl->iac_add(ic, mac);
+ else
+ acl->iac_remove(ic, mac);
+ return 0;
+}
+
+static int
+ieee80211_ioctl_maccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ const struct ieee80211_aclator *acl = ic->ic_acl;
+
+ switch (ireq->i_val) {
+ case IEEE80211_MACCMD_POLICY_OPEN:
+ case IEEE80211_MACCMD_POLICY_ALLOW:
+ case IEEE80211_MACCMD_POLICY_DENY:
+ if (acl == NULL) {
+ acl = ieee80211_aclator_get("mac");
+ if (acl == NULL || !acl->iac_attach(ic))
+ return EINVAL;
+ ic->ic_acl = acl;
+ }
+ acl->iac_setpolicy(ic, ireq->i_val);
+ break;
+ case IEEE80211_MACCMD_FLUSH:
+ if (acl != NULL)
+ acl->iac_flush(ic);
+ /* NB: silently ignore when not in use */
+ break;
+ case IEEE80211_MACCMD_DETACH:
+ if (acl != NULL) {
+ ic->ic_acl = NULL;
+ acl->iac_detach(ic);
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int
+ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211req_chanlist list;
+ u_char chanlist[IEEE80211_CHAN_BYTES];
+ int i, j, error;
+
+ if (ireq->i_len != sizeof(list))
+ return EINVAL;
+ error = copyin(ireq->i_data, &list, sizeof(list));
+ if (error)
+ return error;
+ memset(chanlist, 0, sizeof(chanlist));
+ /*
+ * Since channel 0 is not available for DS, channel 1
+ * is assigned to LSB on WaveLAN.
+ */
+ if (ic->ic_phytype == IEEE80211_T_DS)
+ i = 1;
+ else
+ i = 0;
+ for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
+ /*
+ * NB: silently discard unavailable channels so users
+ * can specify 1-255 to get all available channels.
+ */
+ if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i))
+ setbit(chanlist, i);
+ }
+ if (ic->ic_ibss_chan == NULL ||
+ isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
+ for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
+ if (isset(chanlist, i)) {
+ ic->ic_ibss_chan = &ic->ic_channels[i];
+ goto found;
}
- ireq->i_val = ieee80211_chan2ieee(ic, chan);
+ return EINVAL; /* no active channels */
+found:
+ ;
+ }
+ memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
+ if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
+ isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
+ ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+ return IS_UP_AUTO(ic) ? ENETRESET : 0;
+}
+
+static int
+ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ struct ieee80211_node *ni;
+ struct ieee80211req_sta_txpow txpow;
+ int error;
+
+ if (ireq->i_len != sizeof(txpow))
+ return EINVAL;
+ error = copyin(ireq->i_data, &txpow, sizeof(txpow));
+ if (error != 0)
+ return error;
+ if (ic->ic_sta == NULL)
+ return EINVAL;
+ ni = ieee80211_find_node(ic->ic_sta, txpow.it_macaddr);
+ if (ni == NULL)
+ return EINVAL; /* XXX */
+ ni->ni_txpower = txpow.it_txpow;
+ ieee80211_free_node(ni);
+ return error;
+}
+
+static int
+ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+ 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;
+
+ isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
+ ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
+ if (ac >= WME_NUM_AC)
+ ac = WME_AC_BE;
+ if (isbss) {
+ chanp = &wme->wme_bssChanParams.cap_wmeParams[ac];
+ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+ } else {
+ chanp = &wme->wme_chanParams.cap_wmeParams[ac];
+ wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+ }
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
+ if (isbss) {
+ wmep->wmep_logcwmin = ireq->i_val;
+ if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+ chanp->wmep_logcwmin = ireq->i_val;
+ } else {
+ wmep->wmep_logcwmin = chanp->wmep_logcwmin =
+ ireq->i_val;
+ }
+ break;
+ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
+ if (isbss) {
+ wmep->wmep_logcwmax = ireq->i_val;
+ if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+ chanp->wmep_logcwmax = ireq->i_val;
+ } else {
+ wmep->wmep_logcwmax = chanp->wmep_logcwmax =
+ ireq->i_val;
+ }
+ break;
+ case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
+ if (isbss) {
+ wmep->wmep_aifsn = ireq->i_val;
+ if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+ chanp->wmep_aifsn = ireq->i_val;
+ } else {
+ wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val;
+ }
+ break;
+ case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
+ if (isbss) {
+ wmep->wmep_txopLimit = ireq->i_val;
+ if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+ chanp->wmep_txopLimit = ireq->i_val;
+ } else {
+ wmep->wmep_txopLimit = chanp->wmep_txopLimit =
+ ireq->i_val;
+ }
+ break;
+ case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
+ wmep->wmep_acm = ireq->i_val;
+ if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+ chanp->wmep_acm = ireq->i_val;
+ break;
+ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/
+ wmep->wmep_noackPolicy = chanp->wmep_noackPolicy =
+ (ireq->i_val) == 0;
+ break;
+ }
+ ieee80211_wme_updateparams(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;
+ }
+ return 0;
+}
+
+static int
+ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+{
+ static const u_int8_t zerobssid[IEEE80211_ADDR_LEN];
+ struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+ int error;
+ const struct ieee80211_authenticator *auth;
+ u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
+ char tmpssid[IEEE80211_NWID_LEN];
+ u_int8_t tmpbssid[IEEE80211_ADDR_LEN];
+ struct ieee80211_key *k;
+ int j, caps;
+ u_int kid;
+
+ error = 0;
+ switch (ireq->i_type) {
+ case IEEE80211_IOC_SSID:
+ if (ireq->i_val != 0 ||
+ ireq->i_len > IEEE80211_NWID_LEN)
+ return EINVAL;
+ error = copyin(ireq->i_data, tmpssid, ireq->i_len);
+ if (error)
break;
- case IEEE80211_IOC_POWERSAVE:
- if (ic->ic_flags & IEEE80211_F_PMGTON)
- ireq->i_val = IEEE80211_POWERSAVE_ON;
- else
- ireq->i_val = IEEE80211_POWERSAVE_OFF;
+ memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
+ ic->ic_des_esslen = ireq->i_len;
+ memcpy(ic->ic_des_essid, tmpssid, ireq->i_len);
+ 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;
+ break;
+ case IEEE80211_WEP_ON:
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
+ ic->ic_flags |= IEEE80211_F_DROPUNENC;
break;
- case IEEE80211_IOC_POWERSAVESLEEP:
- ireq->i_val = ic->ic_lintval;
+ case IEEE80211_WEP_MIXED:
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
+ ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
break;
- case IEEE80211_IOC_RTSTHRESHOLD:
- ireq->i_val = ic->ic_rtsthreshold;
+ }
+ 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];
+ if (ireq->i_len == 0) {
+ /* zero-len =>'s delete any existing key */
+ (void) ieee80211_crypto_delkey(ic, k);
break;
- case IEEE80211_IOC_PROTMODE:
- ireq->i_val = ic->ic_protmode;
+ }
+ if (ireq->i_len > sizeof(tmpkey))
+ return EINVAL;
+ memset(tmpkey, 0, sizeof(tmpkey));
+ error = copyin(ireq->i_data, tmpkey, ireq->i_len);
+ if (error)
break;
- case IEEE80211_IOC_TXPOWER:
- if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+ ieee80211_key_update_begin(ic);
+ if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, k)) {
+ k->wk_keylen = ireq->i_len;
+ k->wk_flags |=
+ IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+ memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
+ if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
error = EINVAL;
- else
- ireq->i_val = ic->ic_txpower;
+ } else
+ error = EINVAL;
+ ieee80211_key_update_end(ic);
+ break;
+ case IEEE80211_IOC_WEPTXKEY:
+ kid = (u_int) ireq->i_val;
+ if (kid >= IEEE80211_WEP_NKID)
+ return EINVAL;
+ ic->ic_def_txkey = kid;
+ error = ERESTART; /* push to hardware */
+ break;
+ case IEEE80211_IOC_AUTHMODE:
+ switch (ireq->i_val) {
+ case IEEE80211_AUTH_WPA:
+ case IEEE80211_AUTH_8021X: /* 802.1x */
+ case IEEE80211_AUTH_OPEN: /* open */
+ case IEEE80211_AUTH_SHARED: /* shared-key */
+ case IEEE80211_AUTH_AUTO: /* auto */
+ auth = ieee80211_authenticator_get(ireq->i_val);
+ if (auth == NULL)
+ return EINVAL;
break;
default:
- error = EINVAL;
+ return EINVAL;
+ }
+ switch (ireq->i_val) {
+ case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */
+ ic->ic_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);
+ break;
+ case IEEE80211_AUTH_SHARED: /* shared-key */
+ case IEEE80211_AUTH_8021X: /* 802.1x */
+ ic->ic_flags &= ~IEEE80211_F_WPA;
+ /* both require a key so mark the PRIVACY capability */
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
+ break;
+ case IEEE80211_AUTH_AUTO: /* auto */
+ ic->ic_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;
+ /* XXX mixed/mode/usage? */
+ ic->ic_auth = auth;
+ error = ENETRESET;
break;
- case SIOCS80211:
- error = suser(curthread);
- if (error)
- break;
- ireq = (struct ieee80211req *) data;
- switch (ireq->i_type) {
- case IEEE80211_IOC_SSID:
- if (ireq->i_val != 0 ||
- ireq->i_len > IEEE80211_NWID_LEN) {
- error = EINVAL;
- break;
- }
- error = copyin(ireq->i_data, tmpssid, ireq->i_len);
- if (error)
- break;
- memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
- ic->ic_des_esslen = ireq->i_len;
- memcpy(ic->ic_des_essid, tmpssid, ireq->i_len);
+ case IEEE80211_IOC_CHANNEL:
+ /* XXX 0xffff overflows 16-bit signed */
+ if (ireq->i_val == 0 ||
+ ireq->i_val == (int16_t) IEEE80211_CHAN_ANY)
+ ic->ic_des_chan = IEEE80211_CHAN_ANYC;
+ else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX ||
+ isclr(ic->ic_chan_active, ireq->i_val)) {
+ return EINVAL;
+ } else
+ ic->ic_ibss_chan = ic->ic_des_chan =
+ &ic->ic_channels[ireq->i_val];
+ switch (ic->ic_state) {
+ case IEEE80211_S_INIT:
+ case IEEE80211_S_SCAN:
error = ENETRESET;
break;
- case IEEE80211_IOC_WEP:
+ default:
/*
- * These cards only support one mode so
- * we just turn wep on if what ever is
- * passed in is not OFF.
+ * If the desired channel has changed (to something
+ * other than any) and we're not already scanning,
+ * then kick the state machine.
*/
- if (ireq->i_val == IEEE80211_WEP_OFF) {
- ic->ic_flags &= ~IEEE80211_F_WEPON;
- } else {
- ic->ic_flags |= IEEE80211_F_WEPON;
- }
- error = ENETRESET;
- break;
- case IEEE80211_IOC_WEPKEY:
- if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
- error = EINVAL;
- break;
- }
- kid = (u_int) ireq->i_val;
- if (kid >= IEEE80211_WEP_NKID) {
- error = EINVAL;
- break;
- }
- if (ireq->i_len > sizeof(tmpkey)) {
- error = EINVAL;
- break;
- }
- memset(tmpkey, 0, sizeof(tmpkey));
- error = copyin(ireq->i_data, tmpkey, ireq->i_len);
- if (error)
- break;
- memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey,
- sizeof(tmpkey));
- ic->ic_nw_keys[kid].wk_len = ireq->i_len;
- error = ENETRESET;
+ if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+ ic->ic_bss->ni_chan != ic->ic_des_chan &&
+ (ic->ic_flags & IEEE80211_F_SCAN) == 0)
+ error = ENETRESET;
break;
- case IEEE80211_IOC_WEPTXKEY:
- kid = (u_int) ireq->i_val;
- if (kid >= IEEE80211_WEP_NKID) {
- error = EINVAL;
- break;
+ }
+ if (error == ENETRESET && ic->ic_opmode == IEEE80211_M_MONITOR)
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ 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;
}
- ic->ic_wep_txkey = kid;
- error = ENETRESET;
- break;
-#if 0
- case IEEE80211_IOC_AUTHMODE:
- sc->wi_authmode = ireq->i_val;
break;
-#endif
- case IEEE80211_IOC_CHANNEL:
- /* XXX 0xffff overflows 16-bit signed */
- if (ireq->i_val == 0 ||
- ireq->i_val == (int16_t) IEEE80211_CHAN_ANY)
- ic->ic_des_chan = IEEE80211_CHAN_ANYC;
- else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX ||
- isclr(ic->ic_chan_active, ireq->i_val)) {
+ case IEEE80211_POWERSAVE_ON:
+ if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
error = EINVAL;
- break;
- } else
- ic->ic_ibss_chan = ic->ic_des_chan =
- &ic->ic_channels[ireq->i_val];
- switch (ic->ic_state) {
- case IEEE80211_S_INIT:
- case IEEE80211_S_SCAN:
+ else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
+ ic->ic_flags |= IEEE80211_F_PMGTON;
error = ENETRESET;
- break;
- default:
- if (ic->ic_opmode == IEEE80211_M_STA) {
- if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
- ic->ic_bss->ni_chan != ic->ic_des_chan)
- error = ENETRESET;
- } else {
- if (ic->ic_bss->ni_chan != ic->ic_ibss_chan)
- error = ENETRESET;
- }
- break;
}
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;
- }
- 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;
- }
- break;
- default:
- error = EINVAL;
- break;
- }
+ default:
+ error = EINVAL;
break;
- case IEEE80211_IOC_POWERSAVESLEEP:
- if (ireq->i_val < 0) {
- error = EINVAL;
- break;
- }
- ic->ic_lintval = ireq->i_val;
- error = ENETRESET;
+ }
+ 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;
+ 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;
+ break;
+ case IEEE80211_IOC_PROTMODE:
+ if (ireq->i_val > IEEE80211_PROT_RTSCTS)
+ return EINVAL;
+ ic->ic_protmode = ireq->i_val;
+ /* NB: if not operating in 11g this can wait */
+ if (ic->ic_curmode == IEEE80211_MODE_11G)
+ error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+ break;
+ case IEEE80211_IOC_TXPOWER:
+ if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+ return EINVAL;
+ 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;
+ 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;
+ /* XXXX reset? */
+ break;
+ case IEEE80211_IOC_PRIVACY:
+ if (ireq->i_val) {
+ /* XXX check for key state? */
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
+ } else
+ ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+ break;
+ case IEEE80211_IOC_DROPUNENCRYPTED:
+ if (ireq->i_val)
+ ic->ic_flags |= IEEE80211_F_DROPUNENC;
+ else
+ ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+ break;
+ case IEEE80211_IOC_WPAKEY:
+ error = ieee80211_ioctl_setkey(ic, ireq);
+ break;
+ case IEEE80211_IOC_DELKEY:
+ error = ieee80211_ioctl_delkey(ic, ireq);
+ break;
+ case IEEE80211_IOC_MLME:
+ error = ieee80211_ioctl_setmlme(ic, ireq);
+ break;
+ case IEEE80211_IOC_OPTIE:
+ error = ieee80211_ioctl_setoptie(ic, 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;
+ } else
+ ic->ic_flags &= ~IEEE80211_F_COUNTERM;
+ break;
+ case IEEE80211_IOC_WPA:
+ if (ireq->i_val > 3)
+ return EINVAL;
+ /* XXX verify ciphers available */
+ ic->ic_flags &= ~IEEE80211_F_WPA;
+ switch (ireq->i_val) {
+ case 1:
+ ic->ic_flags |= IEEE80211_F_WPA1;
break;
- case IEEE80211_IOC_RTSTHRESHOLD:
- if (!(IEEE80211_RTS_MIN < ireq->i_val &&
- ireq->i_val < IEEE80211_RTS_MAX)) {
- error = EINVAL;
- break;
- }
- ic->ic_rtsthreshold = ireq->i_val;
- error = ENETRESET;
+ case 2:
+ ic->ic_flags |= IEEE80211_F_WPA2;
break;
- case IEEE80211_IOC_PROTMODE:
- if (ireq->i_val > IEEE80211_PROT_RTSCTS) {
- error = EINVAL;
- break;
- }
- ic->ic_protmode = ireq->i_val;
- /* NB: if not operating in 11g this can wait */
- if (ic->ic_curmode == IEEE80211_MODE_11G)
- error = ENETRESET;
+ case 3:
+ ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
break;
- case IEEE80211_IOC_TXPOWER:
- if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) {
- error = EINVAL;
- break;
- }
- if (!(IEEE80211_TXPOWER_MIN < ireq->i_val &&
- ireq->i_val < IEEE80211_TXPOWER_MAX)) {
- error = EINVAL;
- break;
- }
- ic->ic_txpower = ireq->i_val;
- error = ENETRESET;
+ }
+ error = ENETRESET; /* XXX? */
+ 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;
+ } else
+ ic->ic_flags &= ~IEEE80211_F_WME;
+ error = ENETRESET; /* XXX maybe not for station? */
+ break;
+ case IEEE80211_IOC_HIDESSID:
+ if (ireq->i_val)
+ ic->ic_flags |= IEEE80211_F_HIDESSID;
+ else
+ ic->ic_flags &= ~IEEE80211_F_HIDESSID;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_APBRIDGE:
+ if (ireq->i_val == 0)
+ ic->ic_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 = (((u_int16_t) ireq->i_val) << 16) |
+ ((u_int16_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;
+ break;
+ case IEEE80211_IOC_BSSID:
+ /* NB: should only be set when in STA mode */
+ if (ic->ic_opmode != IEEE80211_M_STA)
+ return EINVAL;
+ if (ireq->i_len != sizeof(tmpbssid))
+ return EINVAL;
+ error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
+ if (error)
break;
- default:
- error = EINVAL;
+ IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
+ if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
+ ic->ic_flags &= ~IEEE80211_F_DESBSSID;
+ else
+ ic->ic_flags |= IEEE80211_F_DESBSSID;
+ error = ENETRESET;
+ break;
+ case IEEE80211_IOC_CHANLIST:
+ error = ieee80211_ioctl_setchanlist(ic, ireq);
+ break;
+ case IEEE80211_IOC_SCAN_REQ:
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) /* XXX ignore */
break;
- }
+ error = ieee80211_setupscan(ic, ic->ic_chan_avail);
+ if (error == 0) /* XXX background scan */
+ error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+ break;
+ case IEEE80211_IOC_ADDMAC:
+ case IEEE80211_IOC_DELMAC:
+ error = ieee80211_ioctl_macmac(ic, ireq);
+ break;
+ case IEEE80211_IOC_MACCMD:
+ error = ieee80211_ioctl_maccmd(ic, ireq);
+ break;
+ case IEEE80211_IOC_STA_TXPOW:
+ error = ieee80211_ioctl_setstatxpow(ic, ireq);
+ break;
+ case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
+ case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
+ case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
+ case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
+ case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
+ case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (bss only) */
+ error = ieee80211_ioctl_setwmeparam(ic, ireq);
+ break;
+ case IEEE80211_IOC_DTIM_PERIOD:
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_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;
+ 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)
+ return EINVAL;
+ if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
+ ireq->i_val <= IEEE80211_BINTVAL_MAX) {
+ ic->ic_lintval = ireq->i_val;
+ error = ENETRESET; /* requires restart */
+ } else
+ error = EINVAL;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ if (error == ENETRESET && !IS_UP_AUTO(ic))
+ error = 0;
+ return error;
+}
+
+int
+ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ int error = 0;
+ struct ifreq *ifr;
+ struct ifaddr *ifa; /* XXX */
+
+ switch (cmd) {
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, (struct ifreq *) data,
+ &ic->ic_media, cmd);
+ break;
+ case SIOCG80211:
+ error = ieee80211_ioctl_get80211(ic, cmd,
+ (struct ieee80211req *) data);
+ break;
+ case SIOCS80211:
+ error = suser(curthread);
+ if (error == 0)
+ error = ieee80211_ioctl_set80211(ic, cmd,
+ (struct ieee80211req *) data);
break;
case SIOCGIFGENERIC:
- error = ieee80211_cfgget(ifp, cmd, data);
+ error = ieee80211_cfgget(ic, cmd, data);
break;
case SIOCSIFGENERIC:
error = suser(curthread);
if (error)
break;
- error = ieee80211_cfgset(ifp, cmd, data);
+ error = ieee80211_cfgset(ic, cmd, data);
break;
case SIOCG80211STATS:
ifr = (struct ifreq *)data;
diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h
index d8c2b02..3c63e13 100644
--- a/sys/net80211/ieee80211_ioctl.h
+++ b/sys/net80211/ieee80211_ioctl.h
@@ -37,7 +37,61 @@
/*
* IEEE 802.11 ioctls.
*/
+#include <net80211/_ieee80211.h>
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_crypto.h>
+/*
+ * Per/node (station) statistics available when operating as an AP.
+ */
+struct ieee80211_nodestats {
+ u_int32_t ns_rx_data; /* rx data frames */
+ u_int32_t ns_rx_mgmt; /* rx management frames */
+ u_int32_t ns_rx_ctrl; /* rx control frames */
+ u_int32_t ns_rx_ucast; /* rx unicast frames */
+ u_int32_t ns_rx_mcast; /* rx multi/broadcast frames */
+ u_int64_t ns_rx_bytes; /* rx data count (bytes) */
+ u_int64_t ns_rx_beacons; /* rx beacon frames */
+ u_int32_t ns_rx_proberesp; /* rx probe response frames */
+
+ u_int32_t ns_rx_dup; /* rx discard 'cuz dup */
+ u_int32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */
+ u_int32_t ns_rx_wepfail; /* rx wep processing failed */
+ u_int32_t ns_rx_demicfail; /* rx demic failed */
+ u_int32_t ns_rx_decap; /* rx decapsulation failed */
+ u_int32_t ns_rx_defrag; /* rx defragmentation failed */
+ u_int32_t ns_rx_disassoc; /* rx disassociation */
+ u_int32_t ns_rx_deauth; /* rx deauthentication */
+ u_int32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */
+ u_int32_t ns_rx_unauth; /* rx on unauthorized port */
+ u_int32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */
+
+ u_int32_t ns_tx_data; /* tx data frames */
+ u_int32_t ns_tx_mgmt; /* tx management frames */
+ u_int32_t ns_tx_ucast; /* tx unicast frames */
+ u_int32_t ns_tx_mcast; /* tx multi/broadcast frames */
+ u_int64_t ns_tx_bytes; /* tx data count (bytes) */
+ u_int32_t ns_tx_probereq; /* tx probe request frames */
+
+ u_int32_t ns_tx_novlantag; /* tx discard 'cuz no tag */
+ u_int32_t ns_tx_vlanmismatch; /* tx discard 'cuz bad tag */
+
+ u_int32_t ns_ps_discard; /* ps discard 'cuz of age */
+
+ /* MIB-related state */
+ u_int32_t ns_tx_assoc; /* [re]associations */
+ u_int32_t ns_tx_assoc_fail; /* [re]association failures */
+ u_int32_t ns_tx_auth; /* [re]authentications */
+ u_int32_t ns_tx_auth_fail; /* [re]authentication failures*/
+ u_int32_t ns_tx_deauth; /* deauthentications */
+ u_int32_t ns_tx_deauth_code; /* last deauth reason */
+ u_int32_t ns_tx_disassoc; /* disassociations */
+ u_int32_t ns_tx_disassoc_code; /* last disassociation reason */
+};
+
+/*
+ * Summary statistics.
+ */
struct ieee80211_stats {
u_int32_t is_rx_badversion; /* rx frame with bad version */
u_int32_t is_rx_tooshort; /* rx frame too short */
@@ -46,11 +100,13 @@ struct ieee80211_stats {
u_int32_t is_rx_wrongdir; /* rx w/ wrong direction */
u_int32_t is_rx_mcastecho; /* rx discard 'cuz mcast echo */
u_int32_t is_rx_notassoc; /* rx discard 'cuz sta !assoc */
- u_int32_t is_rx_nowep; /* rx w/ wep but wep !config */
+ u_int32_t is_rx_noprivacy; /* rx w/ wep but privacy off */
+ u_int32_t is_rx_unencrypted; /* rx w/o wep and privacy on */
u_int32_t is_rx_wepfail; /* rx wep processing failed */
u_int32_t is_rx_decap; /* rx decapsulation failed */
u_int32_t is_rx_mgtdiscard; /* rx discard mgt frames */
u_int32_t is_rx_ctl; /* rx discard ctrl frames */
+ u_int32_t is_rx_beacon; /* rx beacon frames */
u_int32_t is_rx_rstoobig; /* rx rate set truncated */
u_int32_t is_rx_elem_missing; /* rx required element missing*/
u_int32_t is_rx_elem_toobig; /* rx element too big */
@@ -62,26 +118,228 @@ struct ieee80211_stats {
u_int32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */
u_int32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */
u_int32_t is_rx_auth_fail; /* rx sta auth failure */
+ u_int32_t is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */
u_int32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */
u_int32_t is_rx_assoc_notauth; /* rx assoc w/o auth */
u_int32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */
u_int32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */
+ u_int32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */
u_int32_t is_rx_deauth; /* rx deauthentication */
u_int32_t is_rx_disassoc; /* rx disassociation */
u_int32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/
- u_int32_t is_rx_nombuf; /* rx failed for lack of mbuf */
+ u_int32_t is_rx_nobuf; /* rx failed for lack of buf */
u_int32_t is_rx_decryptcrc; /* rx decrypt failed on crc */
u_int32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/
u_int32_t is_rx_bad_auth; /* rx bad auth request */
- u_int32_t is_tx_nombuf; /* tx failed for lack of mbuf */
+ u_int32_t is_rx_unauth; /* rx on unauthorized port */
+ u_int32_t is_rx_badkeyid; /* rx w/ incorrect keyid */
+ u_int32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */
+ u_int32_t is_rx_ccmpformat; /* rx format bad (CCMP) */
+ u_int32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */
+ u_int32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */
+ u_int32_t is_rx_tkipformat; /* rx format bad (TKIP) */
+ u_int32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */
+ u_int32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */
+ u_int32_t is_rx_badcipher; /* rx failed 'cuz key type */
+ u_int32_t is_rx_nocipherctx; /* rx failed 'cuz key !setup */
+ u_int32_t is_rx_acl; /* rx discard 'cuz acl policy */
+ u_int32_t is_tx_nobuf; /* tx failed for lack of buf */
u_int32_t is_tx_nonode; /* tx failed for no node */
u_int32_t is_tx_unknownmgt; /* tx of unknown mgt frame */
+ u_int32_t is_tx_badcipher; /* tx failed 'cuz key type */
+ u_int32_t is_tx_nodefkey; /* tx failed 'cuz no defkey */
+ u_int32_t is_tx_noheadroom; /* tx failed 'cuz no space */
u_int32_t is_scan_active; /* active scans started */
u_int32_t is_scan_passive; /* passive scans started */
u_int32_t is_node_timeout; /* nodes timed out inactivity */
u_int32_t is_crypto_nomem; /* no memory for crypto ctx */
+ u_int32_t is_crypto_tkip; /* tkip crypto done in s/w */
+ u_int32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */
+ u_int32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */
+ u_int32_t is_crypto_tkipcm; /* tkip counter measures */
+ u_int32_t is_crypto_ccmp; /* ccmp crypto done in s/w */
+ u_int32_t is_crypto_wep; /* wep crypto done in s/w */
+ u_int32_t is_crypto_setkey_cipher;/* cipher rejected key */
+ u_int32_t is_crypto_setkey_nokey; /* no key index for setkey */
+ u_int32_t is_crypto_delkey; /* driver key delete failed */
+ u_int32_t is_crypto_badcipher; /* unknown cipher */
+ u_int32_t is_crypto_nocipher; /* cipher not available */
+ u_int32_t is_crypto_attachfail; /* cipher attach failed */
+ u_int32_t is_crypto_swfallback; /* cipher fallback to s/w */
+ u_int32_t is_crypto_keyfail; /* driver key alloc failed */
+ u_int32_t is_ibss_capmismatch; /* merge failed-cap mismatch */
+ u_int32_t is_ibss_norate; /* merge failed-rate mismatch */
+ u_int32_t is_ps_unassoc; /* ps-poll for unassoc. sta */
+ u_int32_t is_ps_badaid; /* ps-poll w/ incorrect aid */
+ u_int32_t is_ps_qempty; /* ps-poll w/ nothing to send */
+};
+
+/*
+ * Max size of optional information elements. We artificially
+ * constrain this; it's limited only by the max frame size (and
+ * the max parameter size of the wireless extensions).
+ */
+#define IEEE80211_MAX_OPT_IE 256
+
+/*
+ * WPA/RSN get/set key request. Specify the key/cipher
+ * type and whether the key is to be used for sending and/or
+ * receiving. The key index should be set only when working
+ * with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
+ * Otherwise a unicast/pairwise key is specified by the bssid
+ * (on a station) or mac address (on an ap). They key length
+ * must include any MIC key data; otherwise it should be no
+ more than IEEE80211_KEYBUF_SIZE.
+ */
+struct ieee80211req_key {
+ u_int8_t ik_type; /* key/cipher type */
+ u_int8_t ik_pad;
+ u_int16_t ik_keyix; /* key index */
+ u_int8_t ik_keylen; /* key length in bytes */
+ u_int8_t ik_flags;
+/* NB: IEEE80211_KEY_XMIT and IEEE80211_KEY_RECV defined elsewhere */
+#define IEEE80211_KEY_DEFAULT 0x80 /* default xmit key */
+ u_int8_t ik_macaddr[IEEE80211_ADDR_LEN];
+ u_int64_t ik_keyrsc; /* key receive sequence counter */
+ u_int64_t ik_keytsc; /* key transmit sequence counter */
+ u_int8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+};
+
+/*
+ * Delete a key either by index or address. Set the index
+ * to IEEE80211_KEYIX_NONE when deleting a unicast key.
+ */
+struct ieee80211req_del_key {
+ u_int8_t idk_keyix; /* key index */
+ u_int8_t idk_macaddr[IEEE80211_ADDR_LEN];
+};
+
+/*
+ * MLME state manipulation request. IEEE80211_MLME_ASSOC
+ * only makes sense when operating as a station. The other
+ * requests can be used when operating as a station or an
+ * ap (to effect a station).
+ */
+struct ieee80211req_mlme {
+ u_int8_t im_op; /* operation to perform */
+#define IEEE80211_MLME_ASSOC 1 /* associate station */
+#define IEEE80211_MLME_DISASSOC 2 /* disassociate station */
+#define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */
+#define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */
+#define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */
+ u_int16_t im_reason; /* 802.11 reason code */
+ u_int8_t im_macaddr[IEEE80211_ADDR_LEN];
+};
+
+/*
+ * MAC ACL operations.
+ */
+enum {
+ IEEE80211_MACCMD_POLICY_OPEN = 0, /* set policy: no ACL's */
+ IEEE80211_MACCMD_POLICY_ALLOW = 1, /* set policy: allow traffic */
+ IEEE80211_MACCMD_POLICY_DENY = 2, /* set policy: deny traffic */
+ IEEE80211_MACCMD_FLUSH = 3, /* flush ACL database */
+ IEEE80211_MACCMD_DETACH = 4, /* detach ACL policy */
};
+/*
+ * Set the active channel list. Note this list is
+ * intersected with the available channel list in
+ * calculating the set of channels actually used in
+ * scanning.
+ */
+struct ieee80211req_chanlist {
+ u_int8_t ic_channels[IEEE80211_CHAN_BYTES];
+};
+
+/*
+ * Get the active channel list info.
+ */
+struct ieee80211req_chaninfo {
+ u_int ic_nchans;
+ struct ieee80211_channel ic_chans[IEEE80211_CHAN_MAX];
+};
+
+/*
+ * Retrieve the WPA/RSN information element for an associated station.
+ */
+struct ieee80211req_wpaie {
+ u_int8_t wpa_macaddr[IEEE80211_ADDR_LEN];
+ u_int8_t wpa_ie[IEEE80211_MAX_OPT_IE];
+};
+
+/*
+ * Retrieve per-node statistics.
+ */
+struct ieee80211req_sta_stats {
+ union {
+ /* NB: explicitly force 64-bit alignment */
+ u_int8_t macaddr[IEEE80211_ADDR_LEN];
+ u_int64_t pad;
+ } is_u;
+ struct ieee80211_nodestats is_stats;
+};
+
+/*
+ * Station information block; the mac address is used
+ * to retrieve other data like stats, unicast key, etc.
+ */
+struct ieee80211req_sta_info {
+ u_int16_t isi_len; /* length (mult of 4) */
+ u_int16_t isi_freq; /* MHz */
+ u_int16_t isi_flags; /* channel flags */
+ u_int16_t isi_state; /* state flags */
+ u_int8_t isi_authmode; /* authentication algorithm */
+ u_int8_t isi_rssi;
+ u_int8_t isi_capinfo; /* capabilities */
+ u_int8_t isi_erp; /* ERP element */
+ u_int8_t isi_macaddr[IEEE80211_ADDR_LEN];
+ u_int8_t isi_nrates;
+ /* negotiated rates */
+ u_int8_t isi_rates[IEEE80211_RATE_MAXSIZE];
+ u_int8_t isi_txrate; /* index to isi_rates[] */
+ u_int16_t isi_ie_len; /* IE length */
+ u_int16_t isi_associd; /* assoc response */
+ u_int16_t isi_txpower; /* current tx power */
+ u_int16_t isi_vlan; /* vlan tag */
+ u_int16_t isi_txseqs[17]; /* seq to be transmitted */
+ u_int16_t isi_rxseqs[17]; /* seq previous for qos frames*/
+ u_int16_t isi_inact; /* inactivity timer */
+ /* XXX frag state? */
+ /* variable length IE data */
+};
+
+/*
+ * Retrieve per-station information; to retrieve all
+ * specify a mac address of ff:ff:ff:ff:ff:ff.
+ */
+struct ieee80211req_sta_req {
+ union {
+ /* NB: explicitly force 64-bit alignment */
+ u_int8_t macaddr[IEEE80211_ADDR_LEN];
+ u_int64_t pad;
+ } is_u;
+ struct ieee80211req_sta_info info[1]; /* variable length */
+};
+
+/*
+ * Get/set per-station tx power cap.
+ */
+struct ieee80211req_sta_txpow {
+ u_int8_t it_macaddr[IEEE80211_ADDR_LEN];
+ u_int8_t it_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.
+ */
+#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 */
+
#ifdef __FreeBSD__
/*
* FreeBSD-style ioctls.
@@ -123,11 +381,69 @@ struct ieee80211req {
#define IEEE80211_PROTMODE_OFF 0
#define IEEE80211_PROTMODE_CTS 1
#define IEEE80211_PROTMODE_RTSCTS 2
-#define IEEE80211_IOC_TXPOWER 14
+#define IEEE80211_IOC_TXPOWER 14 /* global tx power limit */
+#define IEEE80211_IOC_BSSID 15
+#define IEEE80211_IOC_ROAMING 16 /* roaming mode */
+#define IEEE80211_IOC_PRIVACY 17 /* privacy invoked */
+#define IEEE80211_IOC_DROPUNENCRYPTED 18 /* discard unencrypted frames */
+#define IEEE80211_IOC_WPAKEY 19
+#define IEEE80211_IOC_DELKEY 20
+#define IEEE80211_IOC_MLME 21
+#define IEEE80211_IOC_OPTIE 22 /* optional info. element */
+#define IEEE80211_IOC_SCAN_REQ 23
+#define IEEE80211_IOC_SCAN_RESULTS 24
+#define IEEE80211_IOC_COUNTERMEASURES 25 /* WPA/TKIP countermeasures */
+#define IEEE80211_IOC_WPA 26 /* WPA mode (0,1,2) */
+#define IEEE80211_IOC_CHANLIST 27 /* channel list */
+#define IEEE80211_IOC_WME 28 /* WME mode (on, off) */
+#define IEEE80211_IOC_HIDESSID 29 /* hide SSID mode (on, off) */
+#define IEEE80211_IOC_APBRIDGE 30 /* AP inter-sta bridging */
+#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 */
+#define IEEE80211_IOC_WPAIE 39 /* WPA information element */
+#define IEEE80211_IOC_STA_STATS 40 /* per-station statistics */
+#define IEEE80211_IOC_MACCMD 41 /* MAC ACL operation */
+#define IEEE80211_IOC_CHANINFO 42 /* channel info list */
+#define IEEE80211_IOC_TXPOWMAX 43 /* max tx power for channel */
+#define IEEE80211_IOC_STA_TXPOW 44 /* per-station tx power limit */
+#define IEEE80211_IOC_STA_INFO 45 /* station/neighbor info */
+#define IEEE80211_IOC_WME_CWMIN 46 /* WME: ECWmin */
+#define IEEE80211_IOC_WME_CWMAX 47 /* WME: ECWmax */
+#define IEEE80211_IOC_WME_AIFS 48 /* WME: AIFSN */
+#define IEEE80211_IOC_WME_TXOPLIMIT 49 /* WME: txops limit */
+#define IEEE80211_IOC_WME_ACM 50 /* WME: ACM (bss only) */
+#define IEEE80211_IOC_WME_ACKPOLICY 51 /* WME: ACK policy (!bss only)*/
+#define IEEE80211_IOC_DTIM_PERIOD 52 /* DTIM period (beacons) */
+#define IEEE80211_IOC_BEACON_INTERVAL 53 /* beacon interval (ms) */
+#define IEEE80211_IOC_ADDMAC 54 /* add sta to MAC ACL table */
+#define IEEE80211_IOC_DELMAC 55 /* del sta from MAC ACL table */
-#ifndef IEEE80211_CHAN_ANY
-#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */
-#endif
+/*
+ * Scan result data returned for IEEE80211_IOC_SCAN_RESULTS.
+ */
+struct ieee80211req_scan_result {
+ u_int16_t isr_len; /* length (mult of 4) */
+ u_int16_t isr_freq; /* MHz */
+ u_int16_t isr_flags; /* channel flags */
+ u_int8_t isr_noise;
+ u_int8_t isr_rssi;
+ u_int8_t isr_intval; /* beacon interval */
+ u_int8_t isr_capinfo; /* capabilities */
+ u_int8_t isr_erp; /* ERP element */
+ u_int8_t isr_bssid[IEEE80211_ADDR_LEN];
+ u_int8_t isr_nrates;
+ u_int8_t isr_rates[IEEE80211_RATE_MAXSIZE];
+ u_int8_t isr_ssid_len; /* SSID length */
+ u_int8_t isr_ie_len; /* IE length */
+ u_int8_t isr_pad[5];
+ /* variable length SSID followed by IE data */
+};
#define SIOCG80211STATS _IOWR('i', 236, struct ifreq)
#endif /* __FreeBSD__ */
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index 5d4f0f5..708e7fa 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, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,120 +33,249 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include "opt_inet.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/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
-
-#include <machine/atomic.h>
#include <net/if.h>
-#include <net/if_dl.h>
#include <net/if_media.h>
-#include <net/if_arp.h>
#include <net/ethernet.h>
-#include <net/if_llc.h>
#include <net80211/ieee80211_var.h>
#include <net/bpf.h>
-#ifdef INET
-#include <netinet/in.h>
-#include <netinet/if_ether.h>
-#endif
+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 u_int8_t node_getrssi(const struct ieee80211_node *);
+
+static void ieee80211_setup_node(struct ieee80211_node_table *,
+ struct ieee80211_node *, const u_int8_t *);
+static void _ieee80211_free_node(struct ieee80211_node *);
+static void ieee80211_free_allnodes(struct ieee80211_node_table *);
-static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *);
-static void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *);
-static void ieee80211_node_copy(struct ieee80211com *,
- struct ieee80211_node *, const struct ieee80211_node *);
-static u_int8_t ieee80211_node_getrssi(struct ieee80211com *,
- struct ieee80211_node *);
+static void ieee80211_timeout_scan_candidates(struct ieee80211_node_table *);
+static void ieee80211_timeout_stations(struct ieee80211_node_table *);
-static void ieee80211_setup_node(struct ieee80211com *ic,
- struct ieee80211_node *ni, u_int8_t *macaddr);
-static void _ieee80211_free_node(struct ieee80211com *,
- struct ieee80211_node *);
+static void ieee80211_set_tim(struct ieee80211com *,
+ struct ieee80211_node *, int set);
+
+static void ieee80211_node_table_init(struct ieee80211com *ic,
+ struct ieee80211_node_table *nt, const char *name, int inact,
+ void (*timeout)(struct ieee80211_node_table *));
+static struct ieee80211_node_table *ieee80211_node_table_alloc(
+ struct ieee80211com *ic, const char *name, int inact,
+ void (*timeout)(struct ieee80211_node_table *));
+static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
void
-ieee80211_node_attach(struct ifnet *ifp)
+ieee80211_node_attach(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
- /* XXX need unit */
- IEEE80211_NODE_LOCK_INIT(ic, ifp->if_xname);
- TAILQ_INIT(&ic->ic_node);
- ic->ic_node_alloc = ieee80211_node_alloc;
- ic->ic_node_free = ieee80211_node_free;
- ic->ic_node_copy = ieee80211_node_copy;
- ic->ic_node_getrssi = ieee80211_node_getrssi;
- ic->ic_scangen = 1;
+ ic->ic_sta = NULL; /* defer to when we need it */
+ ieee80211_node_table_init(ic, &ic->ic_scan, "scan",
+ IEEE80211_INACT_SCAN, ieee80211_timeout_scan_candidates);
+
+ ic->ic_node_alloc = node_alloc;
+ ic->ic_node_free = node_free;
+ ic->ic_node_cleanup = node_cleanup;
+ ic->ic_node_getrssi = node_getrssi;
+
+ /* 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;
+
+ /* XXX defer */
+ if (ic->ic_max_aid == 0)
+ ic->ic_max_aid = IEEE80211_AID_DEF;
+ else if (ic->ic_max_aid > IEEE80211_AID_MAX)
+ ic->ic_max_aid = IEEE80211_AID_MAX;
+ MALLOC(ic->ic_aid_bitmap, u_int32_t *,
+ howmany(ic->ic_max_aid, 32) * sizeof(u_int32_t),
+ M_DEVBUF, 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;
+ }
+
+ /* XXX defer until using hostap/ibss mode */
+ ic->ic_tim_len = howmany(ic->ic_max_aid, 8) * sizeof(u_int8_t);
+ MALLOC(ic->ic_tim_bitmap, u_int8_t *, ic->ic_tim_len,
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ic->ic_tim_bitmap == NULL) {
+ /* XXX no way to recover */
+ printf("%s: no memory for TIM bitmap!\n", __func__);
+ }
+ ic->ic_set_tim = ieee80211_set_tim; /* NB: driver should override */
}
void
-ieee80211_node_lateattach(struct ifnet *ifp)
+ieee80211_node_lateattach(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
struct ieee80211_node *ni;
+ struct ieee80211_rsnparms *rsn;
- ni = (*ic->ic_node_alloc)(ic);
+ ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr);
KASSERT(ni != NULL, ("unable to setup inital BSS node"));
- ni->ni_chan = IEEE80211_CHAN_ANYC;
- ic->ic_bss = ni;
- ic->ic_txpower = IEEE80211_TXPOWER_MAX;
+ /*
+ * Setup "global settings" in the bss node so that
+ * each new station automatically inherits them.
+ */
+ rsn = &ni->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;
+
+ /*
+ * 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;
+
+ ic->ic_bss = ieee80211_ref_node(ni); /* hold reference */
+ ic->ic_auth = ieee80211_authenticator_get(ni->ni_authmode);
+}
+
+void
+ieee80211_node_detach(struct ieee80211com *ic)
+{
+
+ if (ic->ic_bss != NULL) {
+ ieee80211_free_node(ic->ic_bss);
+ ic->ic_bss = NULL;
+ }
+ ieee80211_node_table_cleanup(&ic->ic_scan);
+ if (ic->ic_sta != NULL) {
+ ieee80211_node_table_free(ic->ic_sta);
+ ic->ic_sta = NULL;
+ }
+ if (ic->ic_aid_bitmap != NULL) {
+ FREE(ic->ic_aid_bitmap, M_DEVBUF);
+ ic->ic_aid_bitmap = NULL;
+ }
+ if (ic->ic_tim_bitmap != NULL) {
+ FREE(ic->ic_tim_bitmap, M_DEVBUF);
+ ic->ic_tim_bitmap = NULL;
+ }
}
+/*
+ * Port authorize/unauthorize interfaces for use by an authenticator.
+ */
+
void
-ieee80211_node_detach(struct ifnet *ifp)
+ieee80211_node_authorize(struct ieee80211com *ic, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = (void *)ifp;
+ ni->ni_flags |= IEEE80211_NODE_AUTH;
+}
- if (ic->ic_bss != NULL)
- (*ic->ic_node_free)(ic, ic->ic_bss);
- ieee80211_free_allnodes(ic);
- IEEE80211_NODE_LOCK_DESTROY(ic);
+void
+ieee80211_node_unauthorize(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ ni->ni_flags &= ~IEEE80211_NODE_AUTH;
+}
+
+/*
+ * Set/change the channel. The rate set is also updated as
+ * to insure a consistent view by drivers.
+ */
+static __inline void
+ieee80211_set_chan(struct ieee80211com *ic,
+ struct ieee80211_node *ni, struct ieee80211_channel *chan)
+{
+ ni->ni_chan = chan;
+ ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
}
/*
* AP scanning support.
*/
+#ifdef IEEE80211_DEBUG
+static void
+dump_chanlist(const u_char chans[])
+{
+ const char *sep;
+ int i;
+
+ sep = " ";
+ for (i = 0; i < IEEE80211_CHAN_MAX; i++)
+ if (isset(chans, i)) {
+ printf("%s%u", sep, i);
+ sep = ", ";
+ }
+}
+#endif /* IEEE80211_DEBUG */
+
/*
- * Initialize the active channel set based on the set
+ * Initialize the channel set to scan based on the
* of available channels and the current PHY mode.
*/
static void
-ieee80211_reset_scan(struct ifnet *ifp)
+ieee80211_reset_scan(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
- memcpy(ic->ic_chan_scan, ic->ic_chan_active,
- sizeof(ic->ic_chan_active));
+ /* XXX ic_des_chan should be handled with ic_chan_active */
+ if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+ memset(ic->ic_chan_scan, 0, sizeof(ic->ic_chan_scan));
+ setbit(ic->ic_chan_scan,
+ ieee80211_chan2ieee(ic, ic->ic_des_chan));
+ } else
+ memcpy(ic->ic_chan_scan, ic->ic_chan_active,
+ sizeof(ic->ic_chan_active));
/* NB: hack, setup so next_scan starts with the first channel */
if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC)
- ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX];
+ ieee80211_set_chan(ic, ic->ic_bss,
+ &ic->ic_channels[IEEE80211_CHAN_MAX]);
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_scan(ic)) {
+ printf("%s: scan set:", __func__);
+ dump_chanlist(ic->ic_chan_scan);
+ printf(" start chan %u\n",
+ ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
+ }
+#endif /* IEEE80211_DEBUG */
}
/*
* Begin an active scan.
*/
void
-ieee80211_begin_scan(struct ifnet *ifp)
+ieee80211_begin_scan(struct ieee80211com *ic, int reset)
{
- struct ieee80211com *ic = (void *)ifp;
+ ic->ic_scan.nt_scangen++;
/*
* In all but hostap mode scanning starts off in
* an active mode before switching to passive.
@@ -156,95 +285,174 @@ ieee80211_begin_scan(struct ifnet *ifp)
ic->ic_stats.is_scan_active++;
} else
ic->ic_stats.is_scan_passive++;
- if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "begin %s scan\n",
- (ic->ic_flags & IEEE80211_F_ASCAN) ?
- "active" : "passive");
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "begin %s scan, scangen %u\n",
+ (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive",
+ ic->ic_scan.nt_scangen);
/*
- * Clear scan state and flush any previously seen
- * AP's. Note that the latter assumes we don't act
- * as both an AP and a station, otherwise we'll
- * potentially flush state of stations associated
- * with us.
+ * Clear scan state and flush any previously seen AP's.
*/
- ieee80211_reset_scan(ifp);
- ieee80211_free_allnodes(ic);
+ ieee80211_reset_scan(ic);
+ if (reset)
+ ieee80211_free_allnodes(&ic->ic_scan);
+
+ ic->ic_flags |= IEEE80211_F_SCAN;
/* Scan the next channel. */
- ieee80211_next_scan(ifp);
+ ieee80211_next_scan(ic);
}
/*
* Switch to the next channel marked for scanning.
*/
-void
-ieee80211_next_scan(struct ifnet *ifp)
+int
+ieee80211_next_scan(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
struct ieee80211_channel *chan;
+ /*
+ * Insure any previous mgt frame timeouts don't fire.
+ * This assumes the driver does the right thing in
+ * flushing anything queued in the driver and below.
+ */
+ ic->ic_mgt_timer = 0;
+
chan = ic->ic_bss->ni_chan;
- for (;;) {
+ do {
if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX])
chan = &ic->ic_channels[0];
if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) {
+ clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan));
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: chan %d->%d\n", __func__,
+ ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan),
+ ieee80211_chan2ieee(ic, chan));
+ ieee80211_set_chan(ic, ic->ic_bss, chan);
+#ifdef notyet
+ /* XXX driver state change */
/*
- * Honor channels marked passive-only
- * during an active scan.
+ * Scan next channel. If doing an active scan
+ * and the channel is not marked passive-only
+ * then send a probe request. Otherwise just
+ * listen for beacons on the channel.
*/
- if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 ||
- (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0)
- break;
- }
- if (chan == ic->ic_bss->ni_chan) {
- ieee80211_end_scan(ifp);
- return;
+ if ((ic->ic_flags & IEEE80211_F_ASCAN) &&
+ (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
+ }
+#else
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+#endif
+ return 1;
}
- }
- clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan));
- IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n",
- ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan),
- ieee80211_chan2ieee(ic, chan)));
- ic->ic_bss->ni_chan = chan;
- ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+ } while (chan != ic->ic_bss->ni_chan);
+ ieee80211_end_scan(ic);
+ return 0;
}
void
ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
{
struct ieee80211_node *ni;
- struct ifnet *ifp = &ic->ic_if;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: creating ibss\n", __func__);
+
+ /*
+ * 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.
+ */
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ ic->ic_sta = ieee80211_node_table_alloc(ic,
+ "station", ic->ic_inact_init,
+ ieee80211_timeout_stations);
+ else
+ ic->ic_sta = ieee80211_node_table_alloc(ic,
+ "neighbor", ic->ic_inact_run,
+ ieee80211_timeout_stations);
+ if (ic->ic_sta == NULL) {
+ /*
+ * Should remain in SCAN state and retry.
+ */
+ /* XXX stat+msg */
+ return;
+ }
ni = ic->ic_bss;
- if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "creating ibss\n");
- ic->ic_flags |= IEEE80211_F_SIBSS;
- ni->ni_chan = chan;
- ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr);
IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
- if (ic->ic_opmode == IEEE80211_M_IBSS)
- ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */
ni->ni_esslen = ic->ic_des_esslen;
memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
ni->ni_rssi = 0;
ni->ni_rstamp = 0;
- memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp));
+ ni->ni_tstamp.tsf = 0;
ni->ni_intval = ic->ic_lintval;
- ni->ni_capinfo = IEEE80211_CAPINFO_IBSS;
- if (ic->ic_flags & IEEE80211_F_WEPON)
+ ni->ni_capinfo = 0;
+ ni->ni_erp = 0;
+ if (ic->ic_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;
+ ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS; /* XXX */
+ ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */
+ }
+ /*
+ * Fix the channel and related attributes.
+ */
+ ieee80211_set_chan(ic, ni, chan);
+ ic->ic_curmode = ieee80211_chan2mode(ic, chan);
+ /*
+ * Do mode-specific rate setup.
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_11G) {
+ /*
+ * Use a mixed 11b/11g rate set.
+ */
+ ieee80211_set11gbasicrates(&ni->ni_rates, IEEE80211_MODE_11G);
+ } else if (ic->ic_curmode == IEEE80211_MODE_11B) {
+ /*
+ * Force pure 11b rate set.
+ */
+ ieee80211_set11gbasicrates(&ni->ni_rates, IEEE80211_MODE_11B);
+ }
+ /*
+ * 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_new_state(ic, IEEE80211_S_RUN, -1);
}
+void
+ieee80211_reset_bss(struct ieee80211com *ic)
+{
+ struct ieee80211_node *ni, *obss;
+
+ ieee80211_node_table_reset(&ic->ic_scan);
+ ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr);
+ KASSERT(ni != NULL, ("unable to setup inital BSS node"));
+ obss = ic->ic_bss;
+ ic->ic_bss = ieee80211_ref_node(ni);
+ if (obss != NULL)
+ ieee80211_free_node(obss);
+ if (ic->ic_sta != NULL) {
+ ieee80211_node_table_free(ic->ic_sta);
+ ic->ic_sta = NULL;
+ }
+}
+
static int
-ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni)
+ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
{
- struct ieee80211com *ic = (void *)ifp;
u_int8_t rate;
int fail;
@@ -261,7 +469,7 @@ ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni)
if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
fail |= 0x02;
}
- if (ic->ic_flags & IEEE80211_F_WEPON) {
+ if (ic->ic_flags & IEEE80211_F_PRIVACY) {
if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
fail |= 0x04;
} else {
@@ -280,7 +488,7 @@ ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni)
!IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
fail |= 0x20;
#ifdef IEEE80211_DEBUG
- if (ifp->if_flags & IFF_DEBUG) {
+ if (ieee80211_msg_scan(ic)) {
printf(" %c %s", fail ? '-' : '+',
ether_sprintf(ni->ni_macaddr));
printf(" %s%c", ether_sprintf(ni->ni_bssid),
@@ -306,49 +514,137 @@ ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni)
return fail;
}
+static __inline u_int8_t
+maxrate(const struct ieee80211_node *ni)
+{
+ const struct ieee80211_rateset *rs = &ni->ni_rates;
+ /* NB: assumes rate set is sorted (happens on frame receive) */
+ return rs->rs_rates[rs->rs_nrates-1] & IEEE80211_RATE_VAL;
+}
+
+/*
+ * Compare the capabilities of two nodes and decide which is
+ * more desirable (return >0 if a is considered better). Note
+ * that we assume compatibility/usability has already been checked
+ * so we don't need to (e.g. validate whether privacy is supported).
+ * Used to select the best scan candidate for association in a BSS.
+ */
+static int
+ieee80211_node_compare(struct ieee80211com *ic,
+ const struct ieee80211_node *a,
+ const struct ieee80211_node *b)
+{
+ u_int8_t maxa, maxb;
+ u_int8_t rssia, rssib;
+
+ /* privacy support preferred */
+ if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) &&
+ (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+ return 1;
+ if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0 &&
+ (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY))
+ return -1;
+
+ /* best/max rate preferred if signal level close enough XXX */
+ maxa = maxrate(a);
+ maxb = maxrate(b);
+ rssia = ic->ic_node_getrssi(a);
+ rssib = ic->ic_node_getrssi(b);
+ if (maxa != maxb && abs(rssib - rssia) < 5)
+ 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->ni_chan) &&
+ !IEEE80211_IS_CHAN_5GHZ(b->ni_chan))
+ return 1;
+ if (!IEEE80211_IS_CHAN_5GHZ(a->ni_chan) &&
+ IEEE80211_IS_CHAN_5GHZ(b->ni_chan))
+ return -1;
+
+ /* all things being equal, use signal level */
+ return rssia - rssib;
+}
+
/*
* Complete a scan of potential channels.
*/
void
-ieee80211_end_scan(struct ifnet *ifp)
+ieee80211_end_scan(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
struct ieee80211_node *ni, *nextbs, *selbs;
- int i, fail;
+ struct ieee80211_node_table *nt;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "end %s scan\n",
+ (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive");
- ic->ic_flags &= ~IEEE80211_F_ASCAN;
- ni = TAILQ_FIRST(&ic->ic_node);
+ ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN);
+ nt = &ic->ic_scan;
+ ni = TAILQ_FIRST(&nt->nt_node);
+
+ ieee80211_notify_scan_done(ic);
if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
- /* XXX off stack? */
- u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)];
+ u_int8_t maxrssi[IEEE80211_CHAN_MAX]; /* XXX off stack? */
+ int i, bestchan;
+ u_int8_t rssi;
+
/*
* The passive scan to look for existing AP's completed,
* select a channel to camp on. Identify the channels
* that already have one or more AP's and try to locate
- * an unnoccupied one. If that fails, pick a random
- * channel from the active set.
+ * an unnoccupied one. If that fails, pick a channel that
+ * looks to be quietest.
*/
+ memset(maxrssi, 0, sizeof(maxrssi));
for (; ni != NULL; ni = nextbs) {
ieee80211_ref_node(ni);
nextbs = TAILQ_NEXT(ni, ni_list);
- setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan));
- ieee80211_free_node(ic, ni);
+ rssi = ic->ic_node_getrssi(ni);
+ i = ieee80211_chan2ieee(ic, ni->ni_chan);
+ if (rssi > maxrssi[i])
+ maxrssi[i] = rssi;
+ ieee80211_unref_node(&ni);
}
+ /* XXX select channel more intelligently */
+ bestchan = -1;
for (i = 0; i < IEEE80211_CHAN_MAX; i++)
- if (isset(ic->ic_chan_active, i) && isclr(occupied, i))
- break;
- if (i == IEEE80211_CHAN_MAX) {
- fail = arc4random() & 3; /* random 0-3 */
- for (i = 0; i < IEEE80211_CHAN_MAX; i++)
- if (isset(ic->ic_chan_active, i) && fail-- == 0)
+ if (isset(ic->ic_chan_active, 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 (maxrssi[i] == 0) {
+ bestchan = i;
break;
+ }
+ if (maxrssi[i] < maxrssi[bestchan])
+ bestchan = i;
+ }
+ if (bestchan != -1) {
+ ieee80211_create_ibss(ic, &ic->ic_channels[bestchan]);
+ return;
}
- ieee80211_create_ibss(ic, &ic->ic_channels[i]);
- return;
+ /* no suitable channel, should not happen */
}
+
+ /*
+ * When manually sequencing the state machine; scan just once
+ * regardless of whether we have a candidate or not. The
+ * controlling application is expected to setup state and
+ * initiate an association.
+ */
+ if (ic->ic_roaming == IEEE80211_ROAMING_MANUAL)
+ return;
+ /*
+ * Automatic sequencing; look for a candidate and
+ * if found join the network.
+ */
if (ni == NULL) {
- IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__));
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: no scan candidate\n", __func__);
notfound:
if (ic->ic_opmode == IEEE80211_M_IBSS &&
(ic->ic_flags & IEEE80211_F_IBSSON) &&
@@ -359,13 +655,14 @@ ieee80211_end_scan(struct ifnet *ifp)
/*
* Reset the list of channels to scan and start again.
*/
- ieee80211_reset_scan(ifp);
- ieee80211_next_scan(ifp);
+ ieee80211_reset_scan(ic);
+ ic->ic_flags |= IEEE80211_F_SCAN;
+ ieee80211_next_scan(ic);
return;
}
selbs = NULL;
- if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "\tmacaddr bssid chan rssi rate flag wep essid\n");
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "\t%s\n",
+ "macaddr bssid chan rssi rate flag wep essid");
for (; ni != NULL; ni = nextbs) {
ieee80211_ref_node(ni);
nextbs = TAILQ_NEXT(ni, ni_list);
@@ -375,14 +672,18 @@ ieee80211_end_scan(struct ifnet *ifp)
* during my scan. So delete the entry for the AP
* and retry to associate if there is another beacon.
*/
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+ "%s: skip scan candidate %s, fails %u\n",
+ __func__, ether_sprintf(ni->ni_macaddr),
+ ni->ni_fails);
if (ni->ni_fails++ > 2)
- ieee80211_free_node(ic, ni);
+ ieee80211_free_node(ni);
continue;
}
- if (ieee80211_match_bss(ifp, ni) == 0) {
+ if (ieee80211_match_bss(ic, ni) == 0) {
if (selbs == NULL)
selbs = ni;
- else if (ni->ni_rssi > selbs->ni_rssi) {
+ else if (ieee80211_node_compare(ic, ni, selbs) > 0) {
ieee80211_unref_node(&selbs);
selbs = ni;
} else
@@ -393,148 +694,401 @@ ieee80211_end_scan(struct ifnet *ifp)
}
if (selbs == NULL)
goto notfound;
- (*ic->ic_node_copy)(ic, ic->ic_bss, selbs);
+ if (!ieee80211_sta_join(ic, selbs)) {
+ ieee80211_unref_node(&selbs);
+ goto notfound;
+ }
+}
+
+/*
+ * Handle 802.11 ad hoc network merge. The
+ * convention, set by the Wireless Ethernet Compatibility Alliance
+ * (WECA), is that an 802.11 station will change its BSSID to match
+ * the "oldest" 802.11 ad hoc network, on the same channel, that
+ * has the station's desired SSID. The "oldest" 802.11 network
+ * sends beacons with the greatest TSF timestamp.
+ *
+ * The caller is assumed to validate TSF's before attempting a merge.
+ *
+ * Return !0 if the BSSID changed, 0 otherwise.
+ */
+int
+ieee80211_ibss_merge(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+ if (IEEE80211_ADDR_EQ(ni->ni_bssid, ic->ic_bss->ni_bssid)) {
+ /* unchanged, nothing to do */
+ return 0;
+ }
+ if (ieee80211_match_bss(ic, ni) != 0) { /* capabilities mismatch */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "%s: merge failed, capabilities mismatch\n", __func__);
+ ic->ic_stats.is_ibss_capmismatch++;
+ return 0;
+ }
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "%s: new bssid %s: %s preamble, %s slot time%s\n", __func__,
+ ether_sprintf(ni->ni_bssid),
+ ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
+ ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
+ ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : ""
+ );
+ return ieee80211_sta_join(ic, ni);
+}
+
+/*
+ * Join the specified IBSS/BSS network. The node is assumed to
+ * be passed in with a held reference.
+ */
+int
+ieee80211_sta_join(struct ieee80211com *ic, struct ieee80211_node *selbs)
+{
+ struct ieee80211_node *obss;
+
if (ic->ic_opmode == IEEE80211_M_IBSS) {
- ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE |
+ /*
+ * Check rate set before committing to this node.
+ */
+ ieee80211_fix_rate(ic, selbs, IEEE80211_F_DOFRATE |
IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
- if (ic->ic_bss->ni_rates.rs_nrates == 0) {
+ if (selbs->ni_rates.rs_nrates == 0) {
selbs->ni_fails++;
- ieee80211_unref_node(&selbs);
- goto notfound;
+ ic->ic_stats.is_ibss_norate++;
+ return 0;
}
- ieee80211_unref_node(&selbs);
/*
- * Discard scan set; the nodes have a refcnt of zero
- * and have not asked the driver to setup private
- * node state. Let them be repopulated on demand either
- * through transmission (ieee80211_find_txnode) or receipt
- * of a probe response (to be added).
+ * Create the neighbor table.
*/
- ieee80211_free_allnodes(ic);
+ ic->ic_sta = ieee80211_node_table_alloc(ic,
+ "neighbor", ic->ic_inact_run,
+ ieee80211_timeout_stations);
+ if (ic->ic_sta == NULL) {
+ /*
+ * Should remain in SCAN state and retry.
+ */
+ /* XXX stat+msg */
+ return 0;
+ }
+ }
+
+ /*
+ * Committed to selbs, setup state.
+ */
+ obss = ic->ic_bss;
+ ic->ic_bss = selbs;
+ if (obss != NULL)
+ ieee80211_free_node(obss);
+ /*
+ * Set the erp state (mostly the slot time) to deal with
+ * the auto-select case; this should be redundant if the
+ * mode is locked.
+ */
+ ic->ic_curmode = ieee80211_chan2mode(ic, selbs->ni_chan);
+ ieee80211_reset_erp(ic);
+ ieee80211_wme_initparams(ic);
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
- } else {
- ieee80211_unref_node(&selbs);
+ else
ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
- }
+ return 1;
+}
+
+/*
+ * Leave the specified IBSS/BSS network. The node is assumed to
+ * be passed in with a held reference.
+ */
+void
+ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ ic->ic_node_cleanup(ni);
+ ieee80211_notify_node_leave(ic, ni);
}
static struct ieee80211_node *
-ieee80211_node_alloc(struct ieee80211com *ic)
+node_alloc(struct ieee80211_node_table *nt)
{
struct ieee80211_node *ni;
+
MALLOC(ni, struct ieee80211_node *, sizeof(struct ieee80211_node),
M_80211_NODE, M_NOWAIT | M_ZERO);
return ni;
}
+/*
+ * Reclaim any resources in a node and reset any critical
+ * state. Typically nodes are free'd immediately after,
+ * but in some cases the storage may be reused so we need
+ * to insure consistent state (should probably fix that).
+ */
static void
-ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
+node_cleanup(struct ieee80211_node *ni)
{
- FREE(ni, M_80211_NODE);
+#define N(a) (sizeof(a)/sizeof(a[0]))
+ struct ieee80211com *ic = ni->ni_ic;
+ int i, qlen;
+
+ /* NB: preserve ni_table */
+ if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
+ ic->ic_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);
+ }
+
+ /*
+ * Drain power save queue and, if needed, clear TIM.
+ */
+ IEEE80211_NODE_SAVEQ_DRAIN(ni, qlen);
+ if (qlen != 0 && ic->ic_set_tim != NULL)
+ ic->ic_set_tim(ic, ni, 0);
+
+ ni->ni_associd = 0;
+ if (ni->ni_challenge != NULL) {
+ FREE(ni->ni_challenge, M_DEVBUF);
+ ni->ni_challenge = NULL;
+ }
+ /*
+ * Preserve SSID, WPA, and WME ie's so the bss node is
+ * reusable during a re-auth/re-assoc state transition.
+ * If we remove these data they will not be recreated
+ * because they come from a probe-response or beacon frame
+ * which cannot be expected prior to the association-response.
+ * This should not be an issue when operating in other modes
+ * as stations leaving always go through a full state transition
+ * which will rebuild this state.
+ *
+ * XXX does this leave us open to inheriting old state?
+ */
+ for (i = 0; i < N(ni->ni_rxfrag); i++)
+ if (ni->ni_rxfrag[i] != NULL) {
+ m_freem(ni->ni_rxfrag[i]);
+ ni->ni_rxfrag[i] = NULL;
+ }
+ ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+#undef N
}
static void
-ieee80211_node_copy(struct ieee80211com *ic,
- struct ieee80211_node *dst, const struct ieee80211_node *src)
+node_free(struct ieee80211_node *ni)
{
- *dst = *src;
+ struct ieee80211com *ic = ni->ni_ic;
+
+ ic->ic_node_cleanup(ni);
+ if (ni->ni_wpa_ie != NULL)
+ FREE(ni->ni_wpa_ie, M_DEVBUF);
+ if (ni->ni_wme_ie != NULL)
+ FREE(ni->ni_wme_ie, M_DEVBUF);
+ IEEE80211_NODE_SAVEQ_DESTROY(ni);
+ FREE(ni, M_80211_NODE);
}
static u_int8_t
-ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni)
+node_getrssi(const struct ieee80211_node *ni)
{
return ni->ni_rssi;
}
static void
-ieee80211_setup_node(struct ieee80211com *ic,
- struct ieee80211_node *ni, u_int8_t *macaddr)
+ieee80211_setup_node(struct ieee80211_node_table *nt,
+ struct ieee80211_node *ni, const u_int8_t *macaddr)
{
+ struct ieee80211com *ic = nt->nt_ic;
int hash;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ "%s %s in %s table\n", __func__,
+ ether_sprintf(macaddr), nt->nt_name);
+
IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
hash = IEEE80211_NODE_HASH(macaddr);
- ni->ni_refcnt = 1; /* mark referenced */
- IEEE80211_NODE_LOCK(ic);
- TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list);
- LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash);
- /*
- * Note we don't enable the inactive timer when acting
- * as a station. Nodes created in this mode represent
- * AP's identified while scanning. If we time them out
- * then several things happen: we can't return the data
- * to users to show the list of AP's we encountered, and
- * more importantly, we'll incorrectly deauthenticate
- * ourself because the inactivity timer will kick us off.
- */
- if (ic->ic_opmode != IEEE80211_M_STA)
- ic->ic_inact_timer = IEEE80211_INACT_WAIT;
- IEEE80211_NODE_UNLOCK(ic);
+ ieee80211_node_initref(ni); /* mark referenced */
+ ni->ni_chan = IEEE80211_CHAN_ANYC;
+ ni->ni_authmode = IEEE80211_AUTH_OPEN;
+ ni->ni_txpower = ic->ic_txpowlimit; /* max power */
+ ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+ ni->ni_inact = ni->ni_inact_reload = nt->nt_inact_init;
+ IEEE80211_NODE_SAVEQ_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_ic = ic;
+ IEEE80211_NODE_UNLOCK(nt);
}
struct ieee80211_node *
-ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr)
+ieee80211_alloc_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
{
- struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
+ struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node *ni;
+
+ ni = ic->ic_node_alloc(nt);
if (ni != NULL)
- ieee80211_setup_node(ic, ni, macaddr);
+ ieee80211_setup_node(nt, ni, macaddr);
else
ic->ic_stats.is_rx_nodealloc++;
return ni;
}
struct ieee80211_node *
-ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr)
+ieee80211_dup_bss(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
{
- struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
+ struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node *ni;
+
+ ni = ic->ic_node_alloc(nt);
if (ni != NULL) {
- ieee80211_setup_node(ic, ni, macaddr);
+ ieee80211_setup_node(nt, ni, macaddr);
/*
* Inherit from ic_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);
- ni->ni_chan = ic->ic_bss->ni_chan;
+ ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan);
+ ni->ni_rsn = ic->ic_bss->ni_rsn;
} else
ic->ic_stats.is_rx_nodealloc++;
return ni;
}
static struct ieee80211_node *
-_ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr)
+#ifdef IEEE80211_DEBUG_REFCNT
+_ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+ const u_int8_t *macaddr, const char *func, int line)
+#else
+_ieee80211_find_node(struct ieee80211_node_table *nt,
+ const u_int8_t *macaddr)
+#endif
{
struct ieee80211_node *ni;
int hash;
- IEEE80211_NODE_LOCK_ASSERT(ic);
+ IEEE80211_NODE_LOCK_ASSERT(nt);
hash = IEEE80211_NODE_HASH(macaddr);
- LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+ LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
- atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */
+ ieee80211_ref_node(ni); /* mark referenced */
+#ifdef IEEE80211_DEBUG_REFCNT
+ IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ "%s (%s:%u) %s refcnt %d\n", __func__, func, line,
+ ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni));
+#endif
return ni;
}
}
return NULL;
}
+#ifdef IEEE80211_DEBUG_REFCNT
+#define _ieee80211_find_node(nt, mac) \
+ _ieee80211_find_node_debug(nt, mac, func, line)
+#endif
struct ieee80211_node *
-ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr)
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+ const u_int8_t *macaddr, const char *func, int line)
+#else
+ieee80211_find_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+#endif
{
struct ieee80211_node *ni;
- IEEE80211_NODE_LOCK(ic);
- ni = _ieee80211_find_node(ic, macaddr);
- IEEE80211_NODE_UNLOCK(ic);
+ IEEE80211_NODE_LOCK(nt);
+ ni = _ieee80211_find_node(nt, macaddr);
+ IEEE80211_NODE_UNLOCK(nt);
+ return ni;
+}
+
+/*
+ * Fake up a node; this handles node discovery in adhoc mode.
+ * Note that for the driver's benefit we we treat this like
+ * an association so the driver has an opportunity to setup
+ * it's private state.
+ */
+struct ieee80211_node *
+ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
+ const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+{
+ struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node *ni;
+
+ ni = ieee80211_dup_bss(nt, macaddr);
+ if (ni != NULL) {
+ /* XXX no rate negotiation; just dup */
+ ni->ni_rates = ic->ic_bss->ni_rates;
+ if (ic->ic_newassoc)
+ ic->ic_newassoc(ic, ni, 1);
+ /* XXX not right for 802.1x/WPA */
+ ieee80211_node_authorize(ic, ni);
+ ieee80211_ref_node(ni); /* hold reference */
+ }
return ni;
}
/*
+ * Locate the node for sender, track state, and then pass the
+ * (referenced) node up to the 802.11 layer for its use. We
+ * are required to pass some node so we fall back to ic_bss
+ * when this frame is from an unknown sender. The 802.11 layer
+ * knows this means the sender wasn't in the node table and
+ * acts accordingly.
+ */
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_rxnode_debug(struct ieee80211com *ic,
+ const struct ieee80211_frame_min *wh, const char *func, int line)
+#else
+ieee80211_find_rxnode(struct ieee80211com *ic,
+ const struct ieee80211_frame_min *wh)
+#endif
+{
+#define IS_CTL(wh) \
+ ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+#define IS_PSPOLL(wh) \
+ ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
+ struct ieee80211_node_table *nt;
+ struct ieee80211_node *ni;
+
+ /* XXX may want scanned nodes in the neighbor table for adhoc */
+ if (ic->ic_opmode == IEEE80211_M_STA ||
+ ic->ic_opmode == IEEE80211_M_MONITOR ||
+ (ic->ic_flags & IEEE80211_F_SCAN))
+ nt = &ic->ic_scan;
+ else
+ nt = ic->ic_sta;
+ /* XXX check ic_bss first in station mode */
+ /* XXX 4-address frames? */
+ IEEE80211_NODE_LOCK(nt);
+ if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+ ni = _ieee80211_find_node(nt, wh->i_addr1);
+ else
+ ni = _ieee80211_find_node(nt, wh->i_addr2);
+ IEEE80211_NODE_UNLOCK(nt);
+
+ return (ni != NULL ? ni : ieee80211_ref_node(ic->ic_bss));
+#undef IS_PSPOLL
+#undef IS_CTL
+}
+
+/*
* Return a reference to the appropriate node for sending
* a data frame. This handles node discovery in adhoc networks.
*/
struct ieee80211_node *
-ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr)
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_txnode_debug(struct ieee80211com *ic, const u_int8_t *macaddr,
+ const char *func, int line)
+#else
+ieee80211_find_txnode(struct ieee80211com *ic, const u_int8_t *macaddr)
+#endif
{
+ struct ieee80211_node_table *nt = ic->ic_sta;
struct ieee80211_node *ni;
/*
@@ -542,31 +1096,23 @@ ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr)
* unless we are operating in station mode or this is a
* multicast/broadcast frame.
*/
- if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
- return ic->ic_bss;
+ if (nt == NULL || IEEE80211_IS_MULTICAST(macaddr))
+ return ieee80211_ref_node(ic->ic_bss);
/* XXX can't hold lock across dup_bss 'cuz of recursive locking */
- IEEE80211_NODE_LOCK(ic);
- ni = _ieee80211_find_node(ic, macaddr);
- IEEE80211_NODE_UNLOCK(ic);
- if (ni == NULL &&
- (ic->ic_opmode == IEEE80211_M_IBSS ||
- ic->ic_opmode == IEEE80211_M_AHDEMO)) {
- /*
- * Fake up a node; this handles node discovery in
- * adhoc mode. Note that for the driver's benefit
- * we we treat this like an association so the driver
- * has an opportunity to setup it's private state.
- *
- * XXX need better way to handle this; issue probe
- * request so we can deduce rate set, etc.
- */
- ni = ieee80211_dup_bss(ic, macaddr);
- if (ni != NULL) {
- /* XXX no rate negotiation; just dup */
- ni->ni_rates = ic->ic_bss->ni_rates;
- if (ic->ic_newassoc)
- (*ic->ic_newassoc)(ic, ni, 1);
+ IEEE80211_NODE_LOCK(nt);
+ ni = _ieee80211_find_node(nt, macaddr);
+ IEEE80211_NODE_UNLOCK(nt);
+
+ if (ni == NULL) {
+ if (ic->ic_opmode == IEEE80211_M_IBSS ||
+ ic->ic_opmode == IEEE80211_M_AHDEMO)
+ ni = ieee80211_fakeup_adhoc_node(nt, macaddr);
+ 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;
@@ -576,117 +1122,775 @@ ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr)
* Like find but search based on the channel too.
*/
struct ieee80211_node *
-ieee80211_lookup_node(struct ieee80211com *ic,
- u_int8_t *macaddr, struct ieee80211_channel *chan)
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_node_with_channel_debug(struct ieee80211_node_table *nt,
+ const u_int8_t *macaddr, struct ieee80211_channel *chan,
+ const char *func, int line)
+#else
+ieee80211_find_node_with_channel(struct ieee80211_node_table *nt,
+ const u_int8_t *macaddr, struct ieee80211_channel *chan)
+#endif
{
struct ieee80211_node *ni;
int hash;
hash = IEEE80211_NODE_HASH(macaddr);
- IEEE80211_NODE_LOCK(ic);
- LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+ IEEE80211_NODE_LOCK(nt);
+ LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
ni->ni_chan == chan) {
- atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */
+ ieee80211_ref_node(ni); /* mark referenced */
+ IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+#ifdef IEEE80211_DEBUG_REFCNT
+ "%s (%s:%u) %s refcnt %d\n", __func__, func, line,
+#else
+ "%s %s refcnt %d\n", __func__,
+#endif
+ ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni));
break;
}
}
- IEEE80211_NODE_UNLOCK(ic);
+ IEEE80211_NODE_UNLOCK(nt);
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 u_int8_t *macaddr, u_int ssidlen, const u_int8_t *ssid,
+ const char *func, int line)
+#else
+ieee80211_find_node_with_ssid(struct ieee80211_node_table *nt,
+ const u_int8_t *macaddr, u_int ssidlen, const u_int8_t *ssid)
+#endif
+{
+ struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node *ni;
+ int hash;
+
+ hash = IEEE80211_NODE_HASH(macaddr);
+ IEEE80211_NODE_LOCK(nt);
+ LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
+ if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
+ ni->ni_esslen == ic->ic_des_esslen &&
+ memcmp(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen) == 0) {
+ ieee80211_ref_node(ni); /* mark referenced */
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+#ifdef IEEE80211_DEBUG_REFCNT
+ "%s (%s:%u) %s refcnt %d\n", __func__, func, line,
+#else
+ "%s %s refcnt %d\n", __func__,
+#endif
+ ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni));
+ break;
+ }
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+ return ni;
+}
+
+
static void
-_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+_ieee80211_free_node(struct ieee80211_node *ni)
{
- KASSERT(ni != ic->ic_bss, ("freeing bss node"));
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_node_table *nt = ni->ni_table;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ "%s %s in %s table\n", __func__, ether_sprintf(ni->ni_macaddr),
+ nt != NULL ? nt->nt_name : "<gone>");
- TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
- LIST_REMOVE(ni, ni_hash);
- if (TAILQ_EMPTY(&ic->ic_node))
- ic->ic_inact_timer = 0;
- (*ic->ic_node_free)(ic, ni);
+ IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+ if (nt != NULL) {
+ TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
+ LIST_REMOVE(ni, ni_hash);
+ }
+ ic->ic_node_free(ni);
}
void
-ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line)
+#else
+ieee80211_free_node(struct ieee80211_node *ni)
+#endif
{
- KASSERT(ni != ic->ic_bss, ("freeing ic_bss"));
+ struct ieee80211_node_table *nt = ni->ni_table;
- /* XXX need equivalent of atomic_dec_and_test */
- atomic_subtract_int(&ni->ni_refcnt, 1);
- if (atomic_cmpset_int(&ni->ni_refcnt, 0, 1)) {
- IEEE80211_NODE_LOCK(ic);
- _ieee80211_free_node(ic, ni);
- IEEE80211_NODE_UNLOCK(ic);
+#ifdef IEEE80211_DEBUG_REFCNT
+ IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ "%s (%s:%u) %s refcnt %d\n", __func__, func, line,
+ ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)-1);
+#endif
+ if (ieee80211_node_dectestref(ni)) {
+ /*
+ * Beware; if the node is marked gone then it's already
+ * been removed from the table and we cannot assume the
+ * table still exists. Regardless, there's no need to lock
+ * the table.
+ */
+ if (ni->ni_table != NULL) {
+ IEEE80211_NODE_LOCK(nt);
+ _ieee80211_free_node(ni);
+ IEEE80211_NODE_UNLOCK(nt);
+ } else
+ _ieee80211_free_node(ni);
}
}
-void
-ieee80211_free_allnodes(struct ieee80211com *ic)
+/*
+ * Reclaim a node. If this is the last reference count then
+ * do the normal free work. Otherwise remove it from the node
+ * table and mark it gone by clearing the back-reference.
+ */
+static void
+node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
{
+
+ if (!ieee80211_node_dectestref(ni)) {
+ /*
+ * Other references are present, just remove the
+ * node from the table so it cannot be found. When
+ * the references are dropped storage will be
+ * reclaimed. This normally only happens for ic_bss.
+ */
+ TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
+ LIST_REMOVE(ni, ni_hash);
+ ni->ni_table = NULL; /* clear reference */
+ } else
+ _ieee80211_free_node(ni);
+}
+
+static void
+ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt)
+{
+ struct ieee80211com *ic = nt->nt_ic;
struct ieee80211_node *ni;
- IEEE80211_NODE_LOCK(ic);
- while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL)
- _ieee80211_free_node(ic, ni);
- IEEE80211_NODE_UNLOCK(ic);
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ "%s: free all nodes in %s table\n", __func__, nt->nt_name);
+
+ while ((ni = TAILQ_FIRST(&nt->nt_node)) != NULL) {
+ 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);
+ }
+ node_reclaim(nt, ni);
+ }
+ ieee80211_reset_erp(ic);
+}
+
+static void
+ieee80211_free_allnodes(struct ieee80211_node_table *nt)
+{
+
+ IEEE80211_NODE_LOCK(nt);
+ ieee80211_free_allnodes_locked(nt);
+ IEEE80211_NODE_UNLOCK(nt);
+}
+
+/*
+ * Timeout entries in the scan cache.
+ */
+static void
+ieee80211_timeout_scan_candidates(struct ieee80211_node_table *nt)
+{
+ struct ieee80211com *ic = nt->nt_ic;
+ struct ieee80211_node *ni, *tni;
+
+ IEEE80211_NODE_LOCK(nt);
+ ni = ic->ic_bss;
+ /* XXX belongs elsewhere */
+ if (ni->ni_rxfrag[0] != NULL && ticks > ni->ni_rxfragstamp + hz) {
+ m_freem(ni->ni_rxfrag[0]);
+ ni->ni_rxfrag[0] = NULL;
+ }
+ TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, tni) {
+ if (ni->ni_inact && --ni->ni_inact == 0) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ "[%s] scan candidate purged from cache "
+ "(refcnt %u)\n", ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni)-1);
+ node_reclaim(nt, ni);
+ }
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+
+ nt->nt_inact_timer = IEEE80211_INACT_WAIT;
}
/*
- * Timeout inactive nodes. Note that we cannot hold the node
- * lock while sending a frame as this would lead to a LOR.
- * Instead we use a generation number to mark nodes that we've
- * scanned and drop the lock and restart a scan if we have to
- * time out a node. Since we are single-threaded by virtue of
+ * Timeout inactive stations and do related housekeeping.
+ * Note that we cannot hold the node lock while sending a
+ * frame as this would lead to a LOR. Instead we use a
+ * generation number to mark nodes that we've scanned and
+ * drop the lock and restart a scan if we have to time out
+ * a node. Since we are single-threaded by virtue of
* controlling the inactivity timer we can be sure this will
* process each node only once.
*/
-void
-ieee80211_timeout_nodes(struct ieee80211com *ic)
+static void
+ieee80211_timeout_stations(struct ieee80211_node_table *nt)
{
+ struct ieee80211com *ic = nt->nt_ic;
struct ieee80211_node *ni;
- u_int gen = ic->ic_scangen++; /* NB: ok 'cuz single-threaded*/
+ u_int gen;
+ IEEE80211_SCAN_LOCK(nt);
+ gen = nt->nt_scangen++;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ "%s: sta scangen %u\n", __func__, gen);
restart:
- IEEE80211_NODE_LOCK(ic);
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ IEEE80211_NODE_LOCK(nt);
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
if (ni->ni_scangen == gen) /* previously handled */
continue;
ni->ni_scangen = gen;
- if (++ni->ni_inact > IEEE80211_INACT_MAX) {
- IEEE80211_DPRINTF(("station %s timed out "
- "due to inactivity (%u secs)\n",
- ether_sprintf(ni->ni_macaddr),
- ni->ni_inact));
+ /*
+ * Free fragment if not needed anymore
+ * (last fragment older than 1s).
+ * XXX doesn't belong here
+ */
+ if (ni->ni_rxfrag[0] != NULL &&
+ ticks > ni->ni_rxfragstamp + hz) {
+ m_freem(ni->ni_rxfrag[0]);
+ ni->ni_rxfrag[0] = NULL;
+ }
+ ni->ni_inact--;
+ if (ni->ni_associd != 0) {
/*
- * Send a deauthenticate frame.
+ * Age frames on the power save queue. The
+ * aging interval is 4 times the listen
+ * interval specified by the station. This
+ * number is factored into the age calculations
+ * when the frame is placed on the queue. We
+ * store ages as time differences we can check
+ * and/or adjust only the head of the list.
+ */
+ if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) {
+ struct mbuf *m;
+ int discard = 0;
+
+ IEEE80211_NODE_SAVEQ_LOCK(ni);
+ while (IF_POLL(&ni->ni_savedq, m) != NULL &&
+ M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
+IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, "[%s] discard frame, age %u\n", ether_sprintf(ni->ni_macaddr), M_AGE_GET(m));/*XXX*/
+ _IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
+ m_freem(m);
+ discard++;
+ }
+ if (m != NULL)
+ M_AGE_SUB(m, IEEE80211_INACT_WAIT);
+ IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+
+ if (discard != 0) {
+ IEEE80211_DPRINTF(ic,
+ IEEE80211_MSG_POWER,
+ "[%s] discard %u frames for age\n",
+ ether_sprintf(ni->ni_macaddr),
+ discard);
+ IEEE80211_NODE_STAT_ADD(ni,
+ ps_discard, discard);
+ if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0)
+ ic->ic_set_tim(ic, ni, 0);
+ }
+ }
+ /*
+ * Probe the station before time it out. We
+ * send a null data frame which may not be
+ * universally supported by drivers (need it
+ * for ps-poll support so it should be...).
+ */
+ if (ni->ni_inact == ic->ic_inact_probe) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ "[%s] probe station due to inactivity\n",
+ ether_sprintf(ni->ni_macaddr));
+ IEEE80211_NODE_UNLOCK(nt);
+ ieee80211_send_nulldata(ic, ni);
+ /* XXX stat? */
+ goto restart;
+ }
+ }
+ if (ni->ni_inact <= 0) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ "[%s] station timed out due to inactivity "
+ "(refcnt %u)\n", ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni));
+ /*
+ * Send a deauthenticate frame and drop the station.
+ * This is somewhat complicated due to reference counts
+ * and locking. At this point a station will typically
+ * have a reference count of 1. ieee80211_node_leave
+ * will do a "free" of the node which will drop the
+ * reference count. But in the meantime a reference
+ * wil be held by the deauth frame. The actual reclaim
+ * of the node will happen either after the tx is
+ * completed or by ieee80211_node_leave.
*
- * Drop the node lock before sending the
- * deauthentication frame in case the driver takes
- * a lock, as this will result in a LOR between the
- * node lock and the driver lock.
+ * Separately we must drop the node lock before sending
+ * in case the driver takes a lock, as this will result
+ * in LOR between the node lock and the driver lock.
*/
- IEEE80211_NODE_UNLOCK(ic);
- IEEE80211_SEND_MGMT(ic, ni,
- IEEE80211_FC0_SUBTYPE_DEAUTH,
- IEEE80211_REASON_AUTH_EXPIRE);
- ieee80211_free_node(ic, ni);
+ IEEE80211_NODE_UNLOCK(nt);
+ if (ni->ni_associd != 0) {
+ IEEE80211_SEND_MGMT(ic, ni,
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_EXPIRE);
+ }
+ ieee80211_node_leave(ic, ni);
ic->ic_stats.is_node_timeout++;
goto restart;
}
}
- if (!TAILQ_EMPTY(&ic->ic_node))
- ic->ic_inact_timer = IEEE80211_INACT_WAIT;
- IEEE80211_NODE_UNLOCK(ic);
+ IEEE80211_NODE_UNLOCK(nt);
+
+ IEEE80211_SCAN_UNLOCK(nt);
+
+ nt->nt_inact_timer = IEEE80211_INACT_WAIT;
}
void
-ieee80211_iterate_nodes(struct ieee80211com *ic, 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);
+ gen = nt->nt_scangen++;
+
+ IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ "%s: sta scangen %u\n", __func__, gen);
+restart:
+ IEEE80211_NODE_LOCK(nt);
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+ if (ni->ni_scangen != gen) {
+ ni->ni_scangen = gen;
+ (void) ieee80211_ref_node(ni);
+ IEEE80211_NODE_UNLOCK(nt);
+ (*f)(arg, ni);
+ ieee80211_free_node(ni);
+ goto restart;
+ }
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+
+ IEEE80211_SCAN_UNLOCK(nt);
+}
+
+void
+ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
+{
+ printf("0x%p: mac %s refcnt %d\n", ni,
+ ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni));
+ printf("\tscangen %u authmode %u flags 0x%x\n",
+ ni->ni_scangen, ni->ni_authmode, ni->ni_flags);
+ printf("\tassocid 0x%x txpower %u vlan %u\n",
+ ni->ni_associd, ni->ni_txpower, ni->ni_vlan);
+ printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n",
+ ni->ni_txseqs[0],
+ ni->ni_rxseqs[0] >> IEEE80211_SEQ_SEQ_SHIFT,
+ ni->ni_rxseqs[0] & IEEE80211_SEQ_FRAG_MASK,
+ ni->ni_rxfragstamp);
+ printf("\trstamp %u rssi %u intval %u capinfo 0x%x\n",
+ ni->ni_rstamp, ni->ni_rssi, 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);
+}
+
+void
+ieee80211_dump_nodes(struct ieee80211_node_table *nt)
+{
+ ieee80211_iterate_nodes(nt,
+ (ieee80211_iter_func *) ieee80211_dump_node, nt);
+}
+
+/*
+ * Handle a station joining an 11g network.
+ */
+static void
+ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+ /*
+ * Station isn't capable of short slot time. Bump
+ * the count of long slot time stations and disable
+ * use of short slot time. Note that the actual switch
+ * over to long slot time use may not occur until the
+ * next beacon transmission (per sec. 7.3.1.4 of 11g).
+ */
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
+ ic->ic_longslotsta++;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "[%s] station needs long slot time, count %d\n",
+ ether_sprintf(ni->ni_macaddr), ic->ic_longslotsta);
+ /* XXX vap's w/ conflicting needs won't work */
+ ieee80211_set_shortslottime(ic, 0);
+ }
+ /*
+ * If the new station is not an ERP station
+ * then bump the counter and enable protection
+ * if configured.
+ */
+ if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) {
+ ic->ic_nonerpsta++;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "[%s] station is !ERP, %d non-ERP stations associated\n",
+ ether_sprintf(ni->ni_macaddr), ic->ic_nonerpsta);
+ /*
+ * If protection is configured, enable it.
+ */
+ if (ic->ic_protmode != IEEE80211_PROT_NONE) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "%s: enable use of protection\n", __func__);
+ ic->ic_flags |= IEEE80211_F_USEPROT;
+ }
+ /*
+ * If station does not support short preamble
+ * then we must enable use of Barker preamble.
+ */
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "[%s] station needs long preamble\n",
+ ether_sprintf(ni->ni_macaddr));
+ ic->ic_flags |= IEEE80211_F_USEBARKER;
+ ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+ }
+ } else
+ ni->ni_flags |= IEEE80211_NODE_ERP;
+}
+
+void
+ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp)
+{
+ int newassoc;
+
+ if (ni->ni_associd == 0) {
+ u_int16_t aid;
+
+ /*
+ * 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))
+ break;
+ }
+ if (aid >= ic->ic_max_aid) {
+ IEEE80211_SEND_MGMT(ic, ni, resp,
+ IEEE80211_REASON_ASSOC_TOOMANY);
+ ieee80211_node_leave(ic, ni);
+ return;
+ }
+ ni->ni_associd = aid | 0xc000;
+ IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
+ ic->ic_sta_assoc++;
+ newassoc = 1;
+ if (ic->ic_curmode == IEEE80211_MODE_11G)
+ ieee80211_node_join_11g(ic, ni);
+ } else
+ newassoc = 0;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG,
+ "[%s] station %s associated at aid %d\n",
+ ether_sprintf(ni->ni_macaddr), newassoc ? "newly" : "already",
+ IEEE80211_NODE_AID(ni));
+
+ /* give driver a chance to setup state like ni_txrate */
+ if (ic->ic_newassoc)
+ ic->ic_newassoc(ic, ni, newassoc);
+ ni->ni_inact_reload = ic->ic_inact_run;
+ IEEE80211_SEND_MGMT(ic, 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, newassoc);
+}
+
+/*
+ * Handle a station leaving an 11g network.
+ */
+static void
+ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+ KASSERT(ic->ic_curmode == IEEE80211_MODE_11G,
+ ("not in 11g, curmode %x", ic->ic_curmode));
+
+ /*
+ * If a long slot station do the slot time bookkeeping.
+ */
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
+ KASSERT(ic->ic_longslotsta > 0,
+ ("bogus long slot station count %d", ic->ic_longslotsta));
+ ic->ic_longslotsta--;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "[%s] long slot time station leaves, count now %d\n",
+ ether_sprintf(ni->ni_macaddr), ic->ic_longslotsta);
+ if (ic->ic_longslotsta == 0) {
+ /*
+ * Re-enable use of short slot time if supported
+ * and not operating in IBSS mode (per spec).
+ */
+ if ((ic->ic_caps & IEEE80211_C_SHSLOT) &&
+ ic->ic_opmode != IEEE80211_M_IBSS) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "%s: re-enable use of short slot time\n",
+ __func__);
+ ieee80211_set_shortslottime(ic, 1);
+ }
+ }
+ }
+ /*
+ * If a non-ERP station do the protection-related bookkeeping.
+ */
+ if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) {
+ KASSERT(ic->ic_nonerpsta > 0,
+ ("bogus non-ERP station count %d", ic->ic_nonerpsta));
+ ic->ic_nonerpsta--;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "[%s] non-ERP station leaves, count now %d\n",
+ ether_sprintf(ni->ni_macaddr), ic->ic_nonerpsta);
+ if (ic->ic_nonerpsta == 0) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "%s: disable use of protection\n", __func__);
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ /* XXX verify mode? */
+ if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "%s: re-enable use of short preamble\n",
+ __func__);
+ ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+ }
+ }
+ }
+}
+
+/*
+ * Handle bookkeeping for station deauthentication/disassociation
+ * when operating as an ap.
+ */
+void
+ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG,
+ "[%s] station with aid %d leaves\n",
+ ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_AID(ni));
+
+ KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_IBSS ||
+ ic->ic_opmode == IEEE80211_M_AHDEMO,
+ ("unexpected operating mode %u", ic->ic_opmode));
+ /*
+ * If node wasn't previously associated all
+ * we need to do is reclaim the reference.
+ */
+ /* XXX ibss mode bypasses 11g and notification */
+ if (ni->ni_associd == 0)
+ goto done;
+ /*
+ * Tell the authenticator the station is leaving.
+ * Note that we must do this before yanking the
+ * association id as the authenticator uses the
+ * associd to locate it's state block.
+ */
+ if (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);
+ ni->ni_associd = 0;
+ ic->ic_sta_assoc--;
+
+ if (ic->ic_curmode == IEEE80211_MODE_11G)
+ ieee80211_node_leave_11g(ic, ni);
+ /*
+ * Cleanup station state. In particular clear various
+ * state that might otherwise be reused if the node
+ * is reused before the reference count goes to zero
+ * (and memory is reclaimed).
+ */
+ ieee80211_sta_leave(ic, ni);
+done:
+ ni->ni_inact_reload = ic->ic_inact_init; /* just in case */
+ ieee80211_free_node(ni);
+}
+
+u_int8_t
+ieee80211_getrssi(struct ieee80211com *ic)
+{
+#define NZ(x) ((x) == 0 ? 1 : (x))
+ struct ieee80211_node_table *nt = ic->ic_sta;
+ u_int32_t rssi_samples, rssi_total;
+ struct ieee80211_node *ni;
+
+ rssi_total = 0;
+ rssi_samples = 0;
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_IBSS: /* average of all ibss neighbors */
+ nt = ic->ic_sta;
+ if (nt == NULL)
+ break;
+ /* XXX locking */
+ TAILQ_FOREACH(ni, &ic->ic_sta->nt_node, ni_list)
+ if (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) {
+ rssi_samples++;
+ rssi_total += ic->ic_node_getrssi(ni);
+ }
+ break;
+ case IEEE80211_M_AHDEMO: /* average of all neighbors */
+ nt = ic->ic_sta;
+ if (nt == NULL)
+ break;
+ /* XXX locking */
+ TAILQ_FOREACH(ni, &ic->ic_sta->nt_node, ni_list) {
+ rssi_samples++;
+ rssi_total += ic->ic_node_getrssi(ni);
+ }
+ break;
+ case IEEE80211_M_HOSTAP: /* average of all associated stations */
+ nt = ic->ic_sta;
+ if (nt == NULL)
+ break;
+ /* XXX locking */
+ TAILQ_FOREACH(ni, &ic->ic_sta->nt_node, ni_list)
+ if (IEEE80211_AID(ni->ni_associd) != 0) {
+ rssi_samples++;
+ rssi_total += ic->ic_node_getrssi(ni);
+ }
+ 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;
+ break;
+ }
+ return rssi_total / NZ(rssi_samples);
+#undef NZ
+}
+
+/*
+ * Indicate whether there are frames queued for a station in power-save mode.
+ */
+static void
+ieee80211_set_tim(struct ieee80211com *ic, struct ieee80211_node *ni, int set)
+{
+ u_int16_t aid;
+
+ KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_IBSS,
+ ("operating mode %u", ic->ic_opmode));
+
+ aid = IEEE80211_AID(ni->ni_associd);
+ KASSERT(aid < ic->ic_max_aid,
+ ("bogus aid %u, max %u", aid, ic->ic_max_aid));
+
+ IEEE80211_BEACON_LOCK(ic);
+ if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) {
+ if (set) {
+ setbit(ic->ic_tim_bitmap, aid);
+ ic->ic_ps_pending++;
+ } else {
+ clrbit(ic->ic_tim_bitmap, aid);
+ ic->ic_ps_pending--;
+ }
+ ic->ic_flags |= IEEE80211_F_TIMUPDATE;
+ }
+ IEEE80211_BEACON_UNLOCK(ic);
+}
+
+/*
+ * Node table support.
+ */
+
+static void
+ieee80211_node_table_init(struct ieee80211com *ic,
+ struct ieee80211_node_table *nt,
+ const char *name, int inact,
+ void (*timeout)(struct ieee80211_node_table *))
+{
+
+ 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_timeout = timeout;
+}
+
+static struct ieee80211_node_table *
+ieee80211_node_table_alloc(struct ieee80211com *ic,
+ const char *name, int inact,
+ void (*timeout)(struct ieee80211_node_table *))
+{
+ struct ieee80211_node_table *nt;
+
+ MALLOC(nt, struct ieee80211_node_table *,
+ sizeof(struct ieee80211_node_table),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (nt == NULL) {
+ printf("%s: no memory node table!\n", __func__);
+ return NULL;
+ }
+ ieee80211_node_table_init(ic, nt, name, inact, timeout);
+ return nt;
+}
+
+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);
+ nt->nt_inact_timer = 0;
+ 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_free_allnodes_locked(nt);
+ IEEE80211_SCAN_LOCK_DESTROY(nt);
+ IEEE80211_NODE_LOCK_DESTROY(nt);
+}
+
+/*
+ * NB: public for use in ieee80211_proto.c
+ */
+void
+ieee80211_node_table_free(struct ieee80211_node_table *nt)
+{
+
+ IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+ "%s %s table\n", __func__, nt->nt_name);
- IEEE80211_NODE_LOCK(ic);
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
- (*f)(arg, ni);
- IEEE80211_NODE_UNLOCK(ic);
+ IEEE80211_NODE_LOCK(nt);
+ nt->nt_inact_timer = 0;
+ ieee80211_node_table_cleanup(nt);
+ FREE(nt, M_DEVBUF);
}
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index dfb8d4e..abd8cd2 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -34,24 +34,52 @@
#ifndef _NET80211_IEEE80211_NODE_H_
#define _NET80211_IEEE80211_NODE_H_
-#define IEEE80211_PSCAN_WAIT 5 /* passive scan wait */
-#define IEEE80211_TRANS_WAIT 5 /* transition wait */
-#define IEEE80211_INACT_WAIT 5 /* inactivity timer interval */
-#define IEEE80211_INACT_MAX (300/IEEE80211_INACT_WAIT)
+#include <net80211/ieee80211_ioctl.h> /* for ieee80211_nodestats */
+
+/*
+ * 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
+ * reclaim nodes that leave part way through the 802.1x exchange.
+ */
+#define IEEE80211_INACT_WAIT 15 /* inactivity interval (secs) */
+#define IEEE80211_INACT_INIT (30/IEEE80211_INACT_WAIT) /* initial */
+#define IEEE80211_INACT_AUTH (180/IEEE80211_INACT_WAIT) /* associated but not authorized */
+#define IEEE80211_INACT_RUN (300/IEEE80211_INACT_WAIT) /* authorized */
+#define IEEE80211_INACT_PROBE (30/IEEE80211_INACT_WAIT) /* probe */
+#define IEEE80211_INACT_SCAN (300/IEEE80211_INACT_WAIT) /* scanned */
+
+#define IEEE80211_TRANS_WAIT 5 /* mgt frame tx timer (secs) */
#define IEEE80211_NODE_HASHSIZE 32
/* simple hash is enough for variation of macaddr */
#define IEEE80211_NODE_HASH(addr) \
- (((u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE)
-
-#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */
-#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */
+ (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
+ IEEE80211_NODE_HASHSIZE)
-struct ieee80211_rateset {
- u_int8_t rs_nrates;
- u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE];
+struct ieee80211_rsnparms {
+ u_int8_t rsn_mcastcipher; /* mcast/group cipher */
+ u_int8_t rsn_mcastkeylen; /* mcast key length */
+ u_int8_t rsn_ucastcipherset; /* unicast cipher set */
+ u_int8_t rsn_ucastcipher; /* selected unicast cipher */
+ u_int8_t rsn_ucastkeylen; /* unicast key length */
+ u_int8_t rsn_keymgmtset; /* key mangement algorithms */
+ u_int8_t rsn_keymgmt; /* selected key mgmt algo */
+ u_int16_t rsn_caps; /* capabilities */
};
+struct ieee80211_node_table;
+struct ieee80211com;
+
/*
* Node specific information. Note that drivers are expected
* to derive from this structure to add device-specific per-node
@@ -59,10 +87,31 @@ struct ieee80211_rateset {
* 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;
u_int ni_scangen; /* gen# for timeout scan */
+ u_int8_t ni_authmode; /* authentication algorithm */
+ u_int16_t ni_flags; /* special-purpose state */
+#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */
+#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */
+#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */
+/* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */
+#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */
+ u_int16_t ni_associd; /* assoc response */
+ u_int16_t ni_txpower; /* current transmit power */
+ u_int16_t ni_vlan; /* vlan tag */
+ u_int32_t *ni_challenge; /* shared-key challenge */
+ u_int8_t *ni_wpa_ie; /* captured WPA/RSN ie */
+ u_int8_t *ni_wme_ie; /* captured WME ie */
+ u_int16_t ni_txseqs[17]; /* tx seq per-tid */
+ u_int16_t ni_rxseqs[17]; /* rx seq previous per-tid*/
+ u_int32_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 */
u_int32_t ni_rstamp; /* recv timestamp */
@@ -73,90 +122,168 @@ struct ieee80211_node {
u_int8_t ni_bssid[IEEE80211_ADDR_LEN];
/* beacon, probe response */
- u_int8_t ni_tstamp[8]; /* from last rcv'd beacon */
+ union {
+ u_int8_t data[8];
+ u_int64_t tsf;
+ } ni_tstamp; /* from last rcv'd beacon */
u_int16_t ni_intval; /* beacon interval */
u_int16_t ni_capinfo; /* capabilities */
u_int8_t ni_esslen;
u_int8_t ni_essid[IEEE80211_NWID_LEN];
struct ieee80211_rateset ni_rates; /* negotiated rate set */
- u_int8_t *ni_country; /* country information XXX */
struct ieee80211_channel *ni_chan;
u_int16_t ni_fhdwell; /* FH only */
u_int8_t ni_fhindex; /* FH only */
- u_int8_t ni_erp; /* 11g only */
-
-#ifdef notyet
- /* DTIM and contention free period (CFP) */
- u_int8_t ni_dtimperiod;
- u_int8_t ni_cfpperiod; /* # of DTIMs between CFPs */
- u_int16_t ni_cfpduremain; /* remaining cfp duration */
- u_int16_t ni_cfpmaxduration;/* max CFP duration in TU */
- u_int16_t ni_nextdtim; /* time to next DTIM */
- u_int16_t ni_timoffset;
-#endif
+ u_int8_t ni_erp; /* ERP from beacon/probe resp */
+ u_int16_t ni_timoff; /* byte offset to TIM ie */
/* others */
- u_int16_t ni_associd; /* assoc response */
- u_int16_t ni_txseq; /* seq to be transmitted */
- u_int16_t ni_rxseq; /* seq previous received */
int ni_fails; /* failure count to associate */
- int ni_inact; /* inactivity mark count */
+ 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 */
+ struct ieee80211_nodestats ni_stats; /* per-node statistics */
};
+MALLOC_DECLARE(M_80211_NODE);
+
+#define IEEE80211_NODE_AID(ni) IEEE80211_AID(ni->ni_associd)
+
+#define IEEE80211_NODE_STAT(ni,stat) (ni->ni_stats.ns_##stat++)
+#define IEEE80211_NODE_STAT_ADD(ni,stat,v) (ni->ni_stats.ns_##stat += v)
+#define IEEE80211_NODE_STAT_SET(ni,stat,v) (ni->ni_stats.ns_##stat = v)
static __inline struct ieee80211_node *
ieee80211_ref_node(struct ieee80211_node *ni)
{
- atomic_add_int(&ni->ni_refcnt, 1);
+ ieee80211_node_incref(ni);
return ni;
}
static __inline void
ieee80211_unref_node(struct ieee80211_node **ni)
{
- atomic_subtract_int(&(*ni)->ni_refcnt, 1);
+ ieee80211_node_decref(*ni);
*ni = NULL; /* guard against use */
}
-#define IEEE80211_NODE_LOCK_INIT(_ic, _name) \
- mtx_init(&(_ic)->ic_nodelock, _name, "802.11 node table", MTX_DEF)
-#define IEEE80211_NODE_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_nodelock)
-#define IEEE80211_NODE_LOCK(_ic) mtx_lock(&(_ic)->ic_nodelock)
-#define IEEE80211_NODE_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_nodelock)
-#define IEEE80211_NODE_LOCK_ASSERT(_ic) \
- mtx_assert(&(_ic)->ic_nodelock, MA_OWNED)
-
struct ieee80211com;
-#ifdef MALLOC_DECLARE
-MALLOC_DECLARE(M_80211_NODE);
-#endif
+extern void ieee80211_node_attach(struct ieee80211com *);
+extern void ieee80211_node_lateattach(struct ieee80211com *);
+extern void ieee80211_node_detach(struct ieee80211com *);
+
+static __inline int
+ieee80211_node_is_authorized(const struct ieee80211_node *ni)
+{
+ return (ni->ni_flags & IEEE80211_NODE_AUTH);
+}
-extern void ieee80211_node_attach(struct ifnet *);
-extern void ieee80211_node_lateattach(struct ifnet *);
-extern void ieee80211_node_detach(struct ifnet *);
-
-extern void ieee80211_begin_scan(struct ifnet *);
-extern void ieee80211_next_scan(struct ifnet *);
-extern void ieee80211_end_scan(struct ifnet *);
-extern struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *,
- u_int8_t *);
-extern struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *,
- u_int8_t *);
-extern struct ieee80211_node *ieee80211_find_node(struct ieee80211com *,
- u_int8_t *);
-extern struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *,
- u_int8_t *);
-extern struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *,
- u_int8_t *macaddr, struct ieee80211_channel *);
-extern void ieee80211_free_node(struct ieee80211com *,
+extern void ieee80211_node_authorize(struct ieee80211com *,
struct ieee80211_node *);
-extern void ieee80211_free_allnodes(struct ieee80211com *);
+extern void ieee80211_node_unauthorize(struct ieee80211com *,
+ struct ieee80211_node *);
+
+extern void ieee80211_begin_scan(struct ieee80211com *, int);
+extern int ieee80211_next_scan(struct ieee80211com *);
+extern void ieee80211_create_ibss(struct ieee80211com*,
+ struct ieee80211_channel *);
+extern void ieee80211_reset_bss(struct ieee80211com *);
+extern void ieee80211_end_scan(struct ieee80211com *);
+extern int ieee80211_ibss_merge(struct ieee80211com *,
+ struct ieee80211_node *);
+extern int ieee80211_sta_join(struct ieee80211com *,
+ struct ieee80211_node *);
+extern void ieee80211_sta_leave(struct ieee80211com *,
+ struct ieee80211_node *);
+
+/*
+ * 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.
+ */
+struct ieee80211_node_table {
+ struct ieee80211com *nt_ic; /* back reference */
+ ieee80211_node_lock_t nt_nodelock; /* on node table */
+ TAILQ_HEAD(, ieee80211_node) nt_node; /* information of all nodes */
+ LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE];
+ const char *nt_name; /* for debugging */
+ ieee80211_scan_lock_t nt_scanlock; /* on nt_scangen */
+ u_int nt_scangen; /* gen# for timeout scan */
+ int nt_inact_timer; /* inactivity timer */
+ int nt_inact_init; /* initial node inact setting */
+
+ void (*nt_timeout)(struct ieee80211_node_table *);
+};
+extern void ieee80211_node_table_reset(struct ieee80211_node_table *);
+extern void ieee80211_node_table_free(struct ieee80211_node_table *);
+
+extern struct ieee80211_node *ieee80211_alloc_node(
+ struct ieee80211_node_table *, const u_int8_t *);
+extern struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *,
+ const u_int8_t *);
+#ifdef IEEE80211_DEBUG_REFCNT
+extern void ieee80211_free_node_debug(struct ieee80211_node *,
+ const char *func, int line);
+extern struct ieee80211_node *ieee80211_find_node_debug(
+ struct ieee80211_node_table *, const u_int8_t *,
+ const char *func, int line);
+extern struct ieee80211_node * ieee80211_find_rxnode_debug(
+ struct ieee80211com *, const struct ieee80211_frame_min *,
+ const char *func, int line);
+extern struct ieee80211_node *ieee80211_find_txnode_debug(
+ struct ieee80211com *, const u_int8_t *,
+ const char *func, int line);
+extern struct ieee80211_node *ieee80211_find_node_with_channel_debug(
+ struct ieee80211_node_table *, const u_int8_t *macaddr,
+ struct ieee80211_channel *, const char *func, int line);
+extern struct ieee80211_node *ieee80211_find_node_with_ssid_debug(
+ struct ieee80211_node_table *, const u_int8_t *macaddr,
+ u_int ssidlen, const u_int8_t *ssid,
+ const char *func, int line);
+#define ieee80211_free_node(ni) \
+ ieee80211_free_node_debug(ni, __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_txnode(nt, mac) \
+ ieee80211_find_txnode_debug(nt, mac, __func__, __LINE__)
+#define ieee80211_find_node_with_channel(nt, mac, c) \
+ ieee80211_find_node_with_channel_debug(nt, mac, c, __func__, __LINE__)
+#define ieee80211_find_node_with_ssid(nt, mac, sl, ss) \
+ ieee80211_find_node_with_ssid_debug(nt, mac, sl, ss, __func__, __LINE__)
+#else
+extern void ieee80211_free_node(struct ieee80211_node *);
+extern struct ieee80211_node *ieee80211_find_node(
+ struct ieee80211_node_table *, const u_int8_t *);
+extern struct ieee80211_node * ieee80211_find_rxnode(
+ struct ieee80211com *, const struct ieee80211_frame_min *);
+extern struct ieee80211_node *ieee80211_find_txnode(
+ struct ieee80211com *, const u_int8_t *);
+extern struct ieee80211_node *ieee80211_find_node_with_channel(
+ struct ieee80211_node_table *, const u_int8_t *macaddr,
+ struct ieee80211_channel *);
+extern struct ieee80211_node *ieee80211_find_node_with_ssid(
+ struct ieee80211_node_table *, const u_int8_t *macaddr,
+ u_int ssidlen, const u_int8_t *ssid);
+#endif
+
typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
-extern void ieee80211_iterate_nodes(struct ieee80211com *ic,
+extern void ieee80211_iterate_nodes(struct ieee80211_node_table *,
ieee80211_iter_func *, void *);
-extern void ieee80211_timeout_nodes(struct ieee80211com *);
-extern void ieee80211_create_ibss(struct ieee80211com* ,
- struct ieee80211_channel *);
+extern void ieee80211_dump_node(struct ieee80211_node_table *,
+ struct ieee80211_node *);
+extern void ieee80211_dump_nodes(struct ieee80211_node_table *);
+
+extern struct ieee80211_node *ieee80211_fakeup_adhoc_node(
+ struct ieee80211_node_table *nt,
+ const u_int8_t macaddr[]);
+extern void ieee80211_node_join(struct ieee80211com *,
+ struct ieee80211_node *, int);
+extern void ieee80211_node_leave(struct ieee80211com *,
+ struct ieee80211_node *);
+extern u_int8_t ieee80211_getrssi(struct ieee80211com *ic);
#endif /* _NET80211_IEEE80211_NODE_H_ */
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index b08fff0..219bc25 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, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -38,32 +38,43 @@ __FBSDID("$FreeBSD$");
#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/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
-#include <machine/atomic.h>
+#include <sys/socket.h>
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <net/if_media.h>
-#include <net/if_arp.h>
+#include <net/bpf.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 <net/bpf.h>
-
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Decide if an outbound management frame should be
+ * printed when debugging is enabled. This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211com *ic, int subtype)
+{
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ return (ic->ic_opmode == IEEE80211_M_IBSS);
+ }
+ return 1;
+}
#endif
/*
@@ -74,14 +85,13 @@ __FBSDID("$FreeBSD$");
* reference (and potentially free'ing up any associated storage).
*/
static int
-ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
+ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
struct mbuf *m, int type)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ifnet *ifp = ic->ic_ifp;
struct ieee80211_frame *wh;
KASSERT(ni != NULL, ("null node"));
- ni->ni_inact = 0;
/*
* Yech, hack alert! We want to pass the node down to the
@@ -106,28 +116,46 @@ ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
*(u_int16_t *)wh->i_dur = 0;
*(u_int16_t *)wh->i_seq =
- htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
- ni->ni_txseq++;
- IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
- IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
- IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+ htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseqs[0]++;
+ /*
+ * Hack. When sending PROBE_REQ frames while scanning we
+ * explicitly force a broadcast rather than (as before) clobber
+ * ni_macaddr and ni_bssid. This is stopgap, we need a way
+ * to communicate this directly rather than do something
+ * implicit based on surrounding state.
+ */
+ if (type == IEEE80211_FC0_SUBTYPE_PROBE_REQ &&
+ (ic->ic_flags & IEEE80211_F_SCAN)) {
+ IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr);
+ } else {
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+ }
- if (ifp->if_flags & IFF_DEBUG) {
- /* avoid to print too many frames */
- if (ic->ic_opmode == IEEE80211_M_IBSS ||
+ 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__);
+ wh->i_fc[1] |= IEEE80211_FC1_WEP;
+ }
#ifdef IEEE80211_DEBUG
- ieee80211_debug > 1 ||
-#endif
- (type & IEEE80211_FC0_SUBTYPE_MASK) !=
- IEEE80211_FC0_SUBTYPE_PROBE_RESP)
- if_printf(ifp, "sending %s to %s on channel %u\n",
- ieee80211_mgt_subtype_name[
- (type & IEEE80211_FC0_SUBTYPE_MASK)
- >> IEEE80211_FC0_SUBTYPE_SHIFT],
- ether_sprintf(ni->ni_macaddr),
- ieee80211_chan2ieee(ic, ni->ni_chan));
+ /* avoid printing too many frames */
+ if ((ieee80211_msg_debug(ic) && doprint(ic, type)) ||
+ ieee80211_msg_dumppkts(ic)) {
+ printf("[%s] send %s on channel %u\n",
+ ether_sprintf(wh->i_addr1),
+ ieee80211_mgt_subtype_name[
+ (type & IEEE80211_FC0_SUBTYPE_MASK) >>
+ IEEE80211_FC0_SUBTYPE_SHIFT],
+ ieee80211_chan2ieee(ic, ni->ni_chan));
}
-
+#endif
+ IEEE80211_NODE_STAT(ni, tx_mgmt);
IF_ENQUEUE(&ic->ic_mgtq, m);
ifp->if_timer = 1;
if_start(ifp);
@@ -135,41 +163,315 @@ ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
}
/*
- * Encapsulate an outbound data frame. The mbuf chain is updated and
- * a reference to the destination node is returned. If an error is
- * encountered NULL is returned and the node reference will also be NULL.
- *
- * NB: The caller is responsible for free'ing a returned node reference.
- * The convention is ic_bss is not reference counted; the caller must
- * maintain that.
+ * Send a null data frame to the specified node.
+ */
+int
+ieee80211_send_nulldata(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct mbuf *m;
+ struct ieee80211_frame *wh;
+
+ MGETHDR(m, M_NOWAIT, MT_HEADER);
+ if (m == NULL) {
+ /* XXX debug msg */
+ ic->ic_stats.is_tx_nobuf++;
+ return ENOMEM;
+ }
+ m->m_pkthdr.rcvif = (void *) ieee80211_ref_node(ni);
+
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA |
+ IEEE80211_FC0_SUBTYPE_NODATA;
+ *(u_int16_t *)wh->i_dur = 0;
+ *(u_int16_t *)wh->i_seq =
+ htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseqs[0]++;
+
+ /* XXX WDS */
+ wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+ IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
+ IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_myaddr);
+ m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+
+ IEEE80211_NODE_STAT(ni, tx_data);
+
+ IF_ENQUEUE(&ic->ic_mgtq, m); /* cheat */
+ if_start(ifp);
+
+ return 0;
+}
+
+/*
+ * Insure there is sufficient contiguous space to encapsulate the
+ * 802.11 data frame. If room isn't already there, arrange for it.
+ * Drivers and cipher modules assume we have done the necessary work
+ * and fail rudely if they don't find the space they need.
+ */
+static struct mbuf *
+ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
+ struct ieee80211_key *key, struct mbuf *m)
+{
+#define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc))
+ int needed_space = hdrsize;
+
+ if (key != NULL) {
+ /* XXX belongs in crypto code? */
+ needed_space += key->wk_cipher->ic_header;
+ /* XXX frags */
+ }
+ /*
+ * We know we are called just before stripping an Ethernet
+ * header and prepending an LLC header. This means we know
+ * there will be
+ * sizeof(struct ether_header) - sizeof(struct llc)
+ * bytes recovered to which we need additional space for the
+ * 802.11 header and any crypto header.
+ */
+ 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,
+ "%s: cannot expand storage\n", __func__);
+ ic->ic_stats.is_tx_nobuf++;
+ m_freem(m);
+ return NULL;
+ }
+ KASSERT(needed_space <= MHLEN,
+ ("not enough room, need %u got %u\n", needed_space, MHLEN));
+ /*
+ * Setup new mbuf to have leading space to prepend the
+ * 802.11 header and any crypto header bits that are
+ * required (the latter are added when the driver calls
+ * back to ieee80211_crypto_encap to do crypto encapsulation).
+ */
+ /* NB: must be first 'cuz it clobbers m_data */
+ m_move_pkthdr(n, m);
+ n->m_len = 0; /* NB: m_gethdr does not set */
+ n->m_data += needed_space;
+ /*
+ * Pull up Ethernet header to create the expected layout.
+ * We could use m_pullup but that's overkill (i.e. we don't
+ * need the actual data) and it cannot fail so do it inline
+ * for speed.
+ */
+ /* NB: struct ether_header is known to be contiguous */
+ n->m_len += sizeof(struct ether_header);
+ m->m_len -= sizeof(struct ether_header);
+ m->m_data += sizeof(struct ether_header);
+ /*
+ * Replace the head of the chain.
+ */
+ n->m_next = m;
+ m = n;
+ }
+ return m;
+#undef TO_BE_RECLAIMED
+}
+
+/*
+ * Assign priority to a frame based on any vlan tag assigned
+ * to the station and/or any Diffserv setting in an IP header.
+ * Finally, if an ACM policy is setup (in station mode) it's
+ * applied.
+ */
+int
+ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni)
+{
+ int v_wme_ac, d_wme_ac, ac;
+#ifdef INET
+ struct ether_header *eh;
+#endif
+
+ if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) {
+ ac = WME_AC_BE;
+ goto done;
+ }
+
+ /*
+ * If node has a vlan tag then all traffic
+ * to it must have a matching tag.
+ */
+ v_wme_ac = 0;
+ if (ni->ni_vlan != 0) {
+ struct m_tag *mtag = VLAN_OUTPUT_TAG(ic->ic_ifp, m);
+ if (mtag != NULL) {
+ IEEE80211_NODE_STAT(ni, tx_novlantag);
+ return 1;
+ }
+ if (EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)) !=
+ EVL_VLANOFTAG(ni->ni_vlan)) {
+ IEEE80211_NODE_STAT(ni, tx_vlanmismatch);
+ return 1;
+ }
+ /* map vlan priority to AC */
+ switch (EVL_PRIOFTAG(ni->ni_vlan)) {
+ case 1:
+ case 2:
+ v_wme_ac = WME_AC_BK;
+ break;
+ case 0:
+ case 3:
+ v_wme_ac = WME_AC_BE;
+ break;
+ case 4:
+ case 5:
+ v_wme_ac = WME_AC_VI;
+ break;
+ case 6:
+ case 7:
+ v_wme_ac = WME_AC_VO;
+ break;
+ }
+ }
+
+#ifdef INET
+ eh = mtod(m, struct ether_header *);
+ if (eh->ether_type == htons(ETHERTYPE_IP)) {
+ const struct ip *ip = (struct ip *)
+ (mtod(m, u_int8_t *) + sizeof (*eh));
+ /*
+ * IP frame, map the TOS field.
+ */
+ switch (ip->ip_tos) {
+ case 0x08:
+ case 0x20:
+ d_wme_ac = WME_AC_BK; /* background */
+ break;
+ case 0x28:
+ case 0xa0:
+ d_wme_ac = WME_AC_VI; /* video */
+ break;
+ case 0x30: /* voice */
+ case 0xe0:
+ case 0x88: /* XXX UPSD */
+ case 0xb8:
+ d_wme_ac = WME_AC_VO;
+ break;
+ default:
+ d_wme_ac = WME_AC_BE;
+ break;
+ }
+ } else {
+#endif /* INET */
+ d_wme_ac = WME_AC_BE;
+#ifdef INET
+ }
+#endif
+ /*
+ * Use highest priority AC.
+ */
+ if (v_wme_ac > d_wme_ac)
+ ac = v_wme_ac;
+ else
+ ac = d_wme_ac;
+
+ /*
+ * Apply ACM policy.
+ */
+ if (ic->ic_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 */
+ };
+ while (ac != WME_AC_BK &&
+ ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm)
+ ac = acmap[ac];
+ }
+done:
+ M_WME_SETAC(m, ac);
+ return 0;
+}
+
+/*
+ * Return the transmit key to use in sending a frame to the specified
+ * destination. Multicast traffic always uses the group key which is
+ * installed the default tx key. Otherwise if a unicast key is set
+ * we use that. When no unicast key is set we fall back to the default
+ * transmit key unless WPA is enabled in which case there should be
+ * a unicast frame so we don't want to use a default key (which in
+ * this case is the group/multicast key).
+ */
+static __inline struct ieee80211_key *
+ieee80211_crypto_getkey(struct ieee80211com *ic,
+ const u_int8_t mac[IEEE80211_ADDR_LEN], struct ieee80211_node *ni)
+{
+#define KEY_UNDEFINED(k) ((k).wk_cipher == &ieee80211_cipher_none)
+ if (IEEE80211_IS_MULTICAST(mac)) {
+ if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
+ KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
+ return NULL;
+ return &ic->ic_nw_keys[ic->ic_def_txkey];
+ } else if (KEY_UNDEFINED(ni->ni_ucastkey)) {
+ if ((ic->ic_flags & IEEE80211_F_WPA) ||
+ ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
+ KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
+ return NULL;
+ return &ic->ic_nw_keys[ic->ic_def_txkey];
+ } else {
+ return &ni->ni_ucastkey;
+ }
+#undef KEY_UNDEFINED
+}
+
+/*
+ * Encapsulate an outbound data frame. The mbuf chain is updated.
+ * If an error is encountered NULL is returned. The caller is required
+ * to provide a node reference and pullup the ethernet header in the
+ * first mbuf.
*/
struct mbuf *
-ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni)
+ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni)
{
- struct ieee80211com *ic = (void *)ifp;
struct ether_header eh;
struct ieee80211_frame *wh;
- struct ieee80211_node *ni = NULL;
+ struct ieee80211_key *key;
struct llc *llc;
+ int hdrsize, datalen;
- if (m->m_len < sizeof(struct ether_header)) {
- m = m_pullup(m, sizeof(struct ether_header));
- if (m == NULL) {
- ic->ic_stats.is_tx_nombuf++;
- goto bad;
- }
- }
+ KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!"));
memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
- ni = ieee80211_find_txnode(ic, eh.ether_dhost);
- if (ni == NULL) {
- IEEE80211_DPRINTF(("%s: no node for dst %s, discard frame\n",
- __func__, ether_sprintf(eh.ether_dhost)));
- ic->ic_stats.is_tx_nonode++;
+ /*
+ * Insure space for additional headers. First identify
+ * transmit key to use in calculating any buffer adjustments
+ * required. This is also used below to do privacy
+ * encapsulation work. Then calculate the 802.11 header
+ * size and any padding required by the driver.
+ *
+ * Note key may be NULL if we fall back to the default
+ * transmit key and that is not set. In that case the
+ * buffer may not be expanded as needed by the cipher
+ * routines, but they will/should discard it.
+ */
+ if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+ key = ieee80211_crypto_getkey(ic, eh.ether_dhost, ni);
+ if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+ "[%s] no default transmit key\n",
+ ether_sprintf(ni->ni_macaddr));
+ /* XXX statistic */
+ }
+ } else
+ key = NULL;
+ /* XXX 4-address format */
+ if (ni->ni_flags & IEEE80211_NODE_QOS)
+ hdrsize = sizeof(struct ieee80211_qosframe);
+ else
+ hdrsize = sizeof(struct ieee80211_frame);
+ if (ic->ic_flags & IEEE80211_F_DATAPAD)
+ hdrsize = roundup(hdrsize, sizeof(u_int32_t));
+ m = ieee80211_mbuf_adjust(ic, hdrsize, key, m);
+ if (m == NULL) {
+ /* NB: ieee80211_mbuf_adjust handles msgs+statistics */
goto bad;
}
- ni->ni_inact = 0;
+ /* NB: this could be optimized because of ieee80211_mbuf_adjust */
m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
llc = mtod(m, struct llc *);
llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
@@ -178,17 +480,16 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni)
llc->llc_snap.org_code[1] = 0;
llc->llc_snap.org_code[2] = 0;
llc->llc_snap.ether_type = eh.ether_type;
- M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ datalen = m->m_pkthdr.len; /* NB: w/o 802.11 header */
+
+ M_PREPEND(m, hdrsize, M_DONTWAIT);
if (m == NULL) {
- ic->ic_stats.is_tx_nombuf++;
+ ic->ic_stats.is_tx_nobuf++;
goto bad;
}
wh = mtod(m, struct ieee80211_frame *);
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
*(u_int16_t *)wh->i_dur = 0;
- *(u_int16_t *)wh->i_seq =
- htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
- ni->ni_txseq++;
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
@@ -212,21 +513,62 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni)
case IEEE80211_M_MONITOR:
goto bad;
}
- *pni = ni;
+ if (eh.ether_type != htons(ETHERTYPE_PAE) ||
+ (key != NULL && (ic->ic_flags & IEEE80211_F_WPA))) {
+ /*
+ * IEEE 802.1X: send EAPOL frames always in the clear.
+ * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
+ */
+ if (key != NULL) {
+ wh->i_fc[1] |= IEEE80211_FC1_WEP;
+ /* XXX do fragmentation */
+ if (!ieee80211_crypto_enmic(ic, key, m)) {
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+ "[%s] enmic failed, discard frame\n",
+ ether_sprintf(eh.ether_dhost));
+ /* XXX statistic */
+ goto bad;
+ }
+ }
+ }
+
+ if (ni->ni_flags & IEEE80211_NODE_QOS) {
+ struct ieee80211_qosframe *qwh =
+ (struct ieee80211_qosframe *) wh;
+ int ac, tid;
+
+ 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;
+ if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
+ qwh->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S;
+ qwh->i_qos[1] = 0;
+ qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
+
+ *(u_int16_t *)wh->i_seq =
+ htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseqs[tid]++;
+ } else {
+ *(u_int16_t *)wh->i_seq =
+ htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
+ ni->ni_txseqs[0]++;
+ }
+
+ IEEE80211_NODE_STAT(ni, tx_data);
+ IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen);
+
return m;
bad:
if (m != NULL)
m_freem(m);
- if (ni && ni != ic->ic_bss)
- ieee80211_free_node(ic, ni);
- *pni = NULL;
return NULL;
}
/*
* Add a supported rates element id to a frame.
*/
-u_int8_t *
+static u_int8_t *
ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
{
int nrates;
@@ -243,7 +585,7 @@ ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
/*
* Add an extended supported rates element id to a frame.
*/
-u_int8_t *
+static u_int8_t *
ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
{
/*
@@ -271,20 +613,278 @@ ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
return frm + len;
}
-static struct mbuf *
-ieee80211_getmbuf(int flags, int type, u_int pktlen)
+/*
+ * Add an erp element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic)
{
- struct mbuf *m;
+ u_int8_t erp;
+
+ *frm++ = IEEE80211_ELEMID_ERP;
+ *frm++ = 1;
+ erp = 0;
+ if (ic->ic_nonerpsta != 0)
+ erp |= IEEE80211_ERP_NON_ERP_PRESENT;
+ if (ic->ic_flags & IEEE80211_F_USEPROT)
+ erp |= IEEE80211_ERP_USE_PROTECTION;
+ if (ic->ic_flags & IEEE80211_F_USEBARKER)
+ erp |= IEEE80211_ERP_LONG_PREAMBLE;
+ *frm++ = erp;
+ return frm;
+}
+
+static u_int8_t *
+ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie)
+{
+#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 u_int8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE };
+ static const u_int8_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 u_int8_t wep104_suite[4] =
+ { WPA_OUI_BYTES, WPA_CSE_WEP104 };
+ static const u_int8_t key_mgt_unspec[4] =
+ { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
+ static const u_int8_t key_mgt_psk[4] =
+ { WPA_OUI_BYTES, WPA_ASE_8021X_PSK };
+ const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+ u_int8_t *frm = ie;
+ u_int8_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);
- KASSERT(pktlen <= MCLBYTES, ("802.11 packet too large: %u", pktlen));
- if (pktlen <= MHLEN)
- MGETHDR(m, flags, type);
+ /* XXX filter out CKIP */
+
+ /* multicast cipher */
+ if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
+ rsn->rsn_mcastkeylen >= 13)
+ ADDSELECTOR(frm, wep104_suite);
else
- m = m_getcl(flags, type, M_PKTHDR);
- return m;
+ 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)
+ 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 > %u",
+ ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+ return frm;
+#undef ADDSHORT
+#undef ADDSELECTOR
+#undef WPA_OUI_BYTES
+}
+
+static u_int8_t *
+ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *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 u_int8_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 u_int8_t wep104_suite[4] =
+ { RSN_OUI_BYTES, RSN_CSE_WEP104 };
+ static const u_int8_t key_mgt_unspec[4] =
+ { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
+ static const u_int8_t key_mgt_psk[4] =
+ { RSN_OUI_BYTES, RSN_ASE_8021X_PSK };
+ const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+ u_int8_t *frm = ie;
+ u_int8_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 */
+ if (rsn->rsn_caps != 0)
+ 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 > %u",
+ ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+ return frm;
+#undef ADDSELECTOR
+#undef ADDSHORT
+#undef RSN_OUI_BYTES
+}
+
+/*
+ * Add a WPA/RSN element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic)
+{
+
+ 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;
+}
+
+#define WME_OUI_BYTES 0x00, 0x50, 0xf2
+/*
+ * Add a WME information element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_wme_info(u_int8_t *frm, struct ieee80211_wme_state *wme)
+{
+ static const struct ieee80211_wme_info info = {
+ .wme_id = IEEE80211_ELEMID_VENDOR,
+ .wme_len = sizeof(struct ieee80211_wme_info) - 2,
+ .wme_oui = { WME_OUI_BYTES },
+ .wme_type = WME_OUI_TYPE,
+ .wme_subtype = WME_INFO_OUI_SUBTYPE,
+ .wme_version = WME_VERSION,
+ .wme_info = 0,
+ };
+ memcpy(frm, &info, sizeof(info));
+ return frm + sizeof(info);
}
/*
+ * Add a WME parameters element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_wme_param(u_int8_t *frm, struct ieee80211_wme_state *wme)
+{
+#define SM(_v, _f) (((_v) << _f##_S) & _f)
+#define ADDSHORT(frm, v) do { \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+} while (0)
+ /* NB: this works 'cuz a param has an info at the front */
+ static const struct ieee80211_wme_info param = {
+ .wme_id = IEEE80211_ELEMID_VENDOR,
+ .wme_len = sizeof(struct ieee80211_wme_param) - 2,
+ .wme_oui = { WME_OUI_BYTES },
+ .wme_type = WME_OUI_TYPE,
+ .wme_subtype = WME_PARAM_OUI_SUBTYPE,
+ .wme_version = WME_VERSION,
+ };
+ int i;
+
+ memcpy(frm, &param, sizeof(param));
+ frm += __offsetof(struct ieee80211_wme_info, wme_info);
+ *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */
+ *frm++ = 0; /* reserved field */
+ for (i = 0; i < WME_NUM_AC; i++) {
+ const struct wmeParams *ac =
+ &wme->wme_bssChanParams.cap_wmeParams[i];
+ *frm++ = SM(i, WME_PARAM_ACI)
+ | SM(ac->wmep_acm, WME_PARAM_ACM)
+ | SM(ac->wmep_aifsn, WME_PARAM_AIFSN)
+ ;
+ *frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX)
+ | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN)
+ ;
+ ADDSHORT(frm, ac->wmep_txopLimit);
+ }
+ return frm;
+#undef SM
+#undef ADDSHORT
+}
+#undef WME_OUI_BYTES
+
+/*
* Send a management frame. The node is for the destination (or ic_bss
* when in station mode). Nodes other than ic_bss have their reference
* count bumped to reflect our use for an indeterminant time.
@@ -294,12 +894,11 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
int type, int arg)
{
#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
- struct ifnet *ifp = &ic->ic_if;
struct mbuf *m;
u_int8_t *frm;
enum ieee80211_phymode mode;
u_int16_t capinfo;
- int ret, timer;
+ int has_challenge, is_shared_key, ret, timer, status;
KASSERT(ni != NULL, ("null node"));
@@ -308,8 +907,13 @@ 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.
*/
- if (ni != ic->ic_bss)
- ieee80211_ref_node(ni);
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+ "ieee80211_ref_node (%s:%u) %s refcnt %d\n",
+ __func__, __LINE__,
+ ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni)+1);
+ ieee80211_ref_node(ni);
+
timer = 0;
switch (type) {
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
@@ -318,22 +922,34 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
+ * [tlv] WME (optional)
+ * [tlv] user-specified ie's
*/
- m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
- 2 + ic->ic_des_esslen
+ m = ieee80211_getmgtframe(&frm,
+ 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
- + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + sizeof(struct ieee80211_wme_param)
+ + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
+ );
if (m == NULL)
- senderr(ENOMEM, is_tx_nombuf);
- m->m_data += sizeof(struct ieee80211_frame);
- frm = mtod(m, u_int8_t *);
+ senderr(ENOMEM, is_tx_nobuf);
+
frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen);
mode = ieee80211_chan2mode(ic, ni->ni_chan);
frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
+ if (ic->ic_flags & IEEE80211_F_WME)
+ frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+ if (ic->ic_opt_ie != NULL) {
+ memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
+ frm += ic->ic_opt_ie_len;
+ }
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
- timer = IEEE80211_TRANS_WAIT;
+ IEEE80211_NODE_STAT(ni, tx_probereq);
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ timer = IEEE80211_TRANS_WAIT;
break;
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
@@ -346,19 +962,26 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* [tlv] supported rates
* [tlv] parameter set (FH/DS)
* [tlv] parameter set (IBSS)
+ * [tlv] extended rate phy (ERP)
* [tlv] extended supported rates
+ * [tlv] WPA
*/
- m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
- 8 + 2 + 2 + 2
- + 2 + ni->ni_esslen
+ m = ieee80211_getmgtframe(&frm,
+ 8
+ + sizeof(u_int16_t)
+ + sizeof(u_int16_t)
+ + 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
- + (ic->ic_phytype == IEEE80211_T_FH ? 7 : 3)
+ + 7 /* max(7,3) */
+ 6
- + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ + 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)
+ );
if (m == NULL)
- senderr(ENOMEM, is_tx_nombuf);
- m->m_data += sizeof(struct ieee80211_frame);
- frm = mtod(m, u_int8_t *);
+ senderr(ENOMEM, is_tx_nobuf);
memset(frm, 0, 8); /* timestamp should be filled later */
frm += 8;
@@ -368,17 +991,19 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
capinfo = IEEE80211_CAPINFO_IBSS;
else
capinfo = IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_WEPON)
+ if (ic->ic_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHSLOT)
+ capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
*(u_int16_t *)frm = htole16(capinfo);
frm += 2;
frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
ic->ic_bss->ni_esslen);
- frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates);
+ frm = ieee80211_add_rates(frm, &ni->ni_rates);
if (ic->ic_phytype == IEEE80211_T_FH) {
*frm++ = IEEE80211_ELEMID_FHPARMS;
@@ -400,44 +1025,97 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
*frm++ = IEEE80211_ELEMID_IBSSPARMS;
*frm++ = 2;
*frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
- } else { /* IEEE80211_M_HOSTAP */
- /* TODO: TIM */
- *frm++ = IEEE80211_ELEMID_TIM;
- *frm++ = 4; /* length */
- *frm++ = 0; /* DTIM count */
- *frm++ = 1; /* DTIM period */
- *frm++ = 0; /* bitmap control */
- *frm++ = 0; /* Partial Virtual Bitmap (variable length) */
}
- frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates);
+ if (ic->ic_flags & IEEE80211_F_WPA)
+ frm = ieee80211_add_wpa(frm, ic);
+ if (ic->ic_curmode == IEEE80211_MODE_11G)
+ frm = ieee80211_add_erp(frm, ic);
+ frm = ieee80211_add_xrates(frm, &ni->ni_rates);
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
break;
case IEEE80211_FC0_SUBTYPE_AUTH:
- MGETHDR(m, M_DONTWAIT, MT_DATA);
+ status = arg >> 16;
+ arg &= 0xffff;
+ has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE ||
+ arg == IEEE80211_AUTH_SHARED_RESPONSE) &&
+ ni->ni_challenge != NULL);
+
+ /*
+ * Deduce whether we're doing open authentication or
+ * shared key authentication. We do the latter if
+ * we're in the middle of a shared key authentication
+ * handshake or if we're initiating an authentication
+ * request and configured to use shared key.
+ */
+ is_shared_key = has_challenge ||
+ arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
+ (arg == IEEE80211_AUTH_SHARED_REQUEST &&
+ ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED);
+
+ m = ieee80211_getmgtframe(&frm,
+ 3 * sizeof(u_int16_t)
+ + (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
+ sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0)
+ );
if (m == NULL)
- senderr(ENOMEM, is_tx_nombuf);
- MH_ALIGN(m, 2 * 3);
- m->m_pkthdr.len = m->m_len = 6;
- frm = mtod(m, u_int8_t *);
- /* TODO: shared key auth */
- ((u_int16_t *)frm)[0] = htole16(IEEE80211_AUTH_ALG_OPEN);
+ senderr(ENOMEM, is_tx_nobuf);
+
+ ((u_int16_t *)frm)[0] =
+ (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED)
+ : htole16(IEEE80211_AUTH_ALG_OPEN);
((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */
- ((u_int16_t *)frm)[2] = 0; /* status */
+ ((u_int16_t *)frm)[2] = htole16(status);/* status */
+
+ if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
+ ((u_int16_t *)frm)[3] =
+ htole16((IEEE80211_CHALLENGE_LEN << 8) |
+ IEEE80211_ELEMID_CHALLENGE);
+ memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge,
+ IEEE80211_CHALLENGE_LEN);
+ m->m_pkthdr.len = m->m_len =
+ 4 * sizeof(u_int16_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__);
+ m->m_flags |= M_LINK0; /* WEP-encrypt, please */
+ }
+ } else
+ m->m_pkthdr.len = m->m_len = 3 * sizeof(u_int16_t);
+
+ /* XXX not right for shared key */
+ if (status == IEEE80211_STATUS_SUCCESS)
+ IEEE80211_NODE_STAT(ni, tx_auth);
+ else
+ IEEE80211_NODE_STAT(ni, tx_auth_fail);
+
+ /*
+ * When 802.1x is not in use mark the port
+ * authorized at this point so traffic can flow.
+ */
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ status == IEEE80211_STATUS_SUCCESS &&
+ ni->ni_authmode != IEEE80211_AUTH_8021X)
+ ieee80211_node_authorize(ic, ni);
if (ic->ic_opmode == IEEE80211_M_STA)
timer = IEEE80211_TRANS_WAIT;
break;
case IEEE80211_FC0_SUBTYPE_DEAUTH:
- if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "station %s deauthenticate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), arg);
- MGETHDR(m, M_DONTWAIT, MT_DATA);
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+ "[%s] send station deauthenticate (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), arg);
+ m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
if (m == NULL)
- senderr(ENOMEM, is_tx_nombuf);
- MH_ALIGN(m, 2);
- m->m_pkthdr.len = m->m_len = 2;
- *mtod(m, u_int16_t *) = htole16(arg); /* reason */
+ senderr(ENOMEM, is_tx_nobuf);
+ *(u_int16_t *)frm = htole16(arg); /* reason */
+ m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
+
+ IEEE80211_NODE_STAT(ni, tx_deauth);
+ IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg);
+
+ ieee80211_node_unauthorize(ic, ni); /* port closed */
break;
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
@@ -450,25 +1128,28 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
+ * [tlv] WME
+ * [tlv] user-specified ie's
*/
- m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
- sizeof(capinfo)
+ m = ieee80211_getmgtframe(&frm,
+ sizeof(u_int16_t)
+ sizeof(u_int16_t)
+ IEEE80211_ADDR_LEN
- + 2 + ni->ni_esslen
+ + 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
- + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + sizeof(struct ieee80211_wme_info)
+ + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
+ );
if (m == NULL)
- senderr(ENOMEM, is_tx_nombuf);
- m->m_data += sizeof(struct ieee80211_frame);
- frm = mtod(m, u_int8_t *);
+ senderr(ENOMEM, is_tx_nobuf);
capinfo = 0;
if (ic->ic_opmode == IEEE80211_M_IBSS)
capinfo |= IEEE80211_CAPINFO_IBSS;
else /* IEEE80211_M_STA */
capinfo |= IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_WEPON)
+ if (ic->ic_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
/*
* NB: Some 11a AP's reject the request when
@@ -477,7 +1158,8 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
- if (ic->ic_flags & IEEE80211_F_SHSLOT)
+ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) &&
+ (ic->ic_caps & IEEE80211_C_SHSLOT))
capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
*(u_int16_t *)frm = htole16(capinfo);
frm += 2;
@@ -493,6 +1175,12 @@ ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
frm = ieee80211_add_rates(frm, &ni->ni_rates);
frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+ if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+ frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
+ if (ic->ic_opt_ie != NULL) {
+ memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
+ frm += ic->ic_opt_ie_len;
+ }
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
timer = IEEE80211_TRANS_WAIT;
@@ -507,67 +1195,395 @@ 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)
*/
- m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
- sizeof(capinfo)
+ m = ieee80211_getmgtframe(&frm,
+ sizeof(u_int16_t)
+ sizeof(u_int16_t)
+ sizeof(u_int16_t)
+ 2 + IEEE80211_RATE_SIZE
- + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+ + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+ + sizeof(struct ieee80211_wme_param)
+ );
if (m == NULL)
- senderr(ENOMEM, is_tx_nombuf);
- m->m_data += sizeof(struct ieee80211_frame);
- frm = mtod(m, u_int8_t *);
+ senderr(ENOMEM, is_tx_nobuf);
capinfo = IEEE80211_CAPINFO_ESS;
- if (ic->ic_flags & IEEE80211_F_WEPON)
+ if (ic->ic_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHSLOT)
+ capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
*(u_int16_t *)frm = htole16(capinfo);
frm += 2;
*(u_int16_t *)frm = htole16(arg); /* status */
frm += 2;
- if (arg == IEEE80211_STATUS_SUCCESS)
+ if (arg == IEEE80211_STATUS_SUCCESS) {
*(u_int16_t *)frm = htole16(ni->ni_associd);
+ IEEE80211_NODE_STAT(ni, tx_assoc);
+ } else
+ IEEE80211_NODE_STAT(ni, tx_assoc_fail);
frm += 2;
frm = ieee80211_add_rates(frm, &ni->ni_rates);
frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+ if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+ frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
break;
case IEEE80211_FC0_SUBTYPE_DISASSOC:
- if (ifp->if_flags & IFF_DEBUG)
- if_printf(ifp, "station %s disassociate (reason %d)\n",
- ether_sprintf(ni->ni_macaddr), arg);
- MGETHDR(m, M_DONTWAIT, MT_DATA);
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+ "[%s] send station disassociate (reason %d)\n",
+ ether_sprintf(ni->ni_macaddr), arg);
+ m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
if (m == NULL)
- senderr(ENOMEM, is_tx_nombuf);
- MH_ALIGN(m, 2);
- m->m_pkthdr.len = m->m_len = 2;
- *mtod(m, u_int16_t *) = htole16(arg); /* reason */
+ senderr(ENOMEM, is_tx_nobuf);
+ *(u_int16_t *)frm = htole16(arg); /* reason */
+ m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
+
+ IEEE80211_NODE_STAT(ni, tx_disassoc);
+ IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg);
break;
default:
- IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n",
- __func__, type));
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ "[%s] invalid mgmt frame type %u\n",
+ ether_sprintf(ni->ni_macaddr), type);
senderr(EINVAL, is_tx_unknownmgt);
/* NOTREACHED */
}
- ret = ieee80211_mgmt_output(ifp, ni, m, type);
+ ret = ieee80211_mgmt_output(ic, ni, m, type);
if (ret == 0) {
if (timer)
ic->ic_mgt_timer = timer;
} else {
bad:
- if (ni != ic->ic_bss) /* remove ref we added */
- ieee80211_free_node(ic, ni);
+ ieee80211_free_node(ni);
}
return ret;
#undef senderr
}
+
+/*
+ * Allocate a beacon frame and fillin the appropriate bits.
+ */
+struct mbuf *
+ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni,
+ struct ieee80211_beacon_offsets *bo)
+{
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_frame *wh;
+ struct mbuf *m;
+ int pktlen;
+ u_int8_t *frm, *efrm;
+ u_int16_t capinfo;
+ struct ieee80211_rateset *rs;
+
+ /*
+ * beacon frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] cabability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [3] parameter set (DS)
+ * [tlv] parameter set (IBSS/TIM)
+ * [tlv] extended rate phy (ERP)
+ * [tlv] extended supported rates
+ * [tlv] WME parameters
+ * [tlv] WPA/RSN parameters
+ * XXX Vendor-specific OIDs (e.g. Atheros)
+ * NB: we allocate the max space required for the TIM bitmap.
+ */
+ rs = &ni->ni_rates;
+ pktlen = 8 /* time stamp */
+ + sizeof(u_int16_t) /* beacon interval */
+ + sizeof(u_int16_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 */
+ + 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)
+ ;
+ m = ieee80211_getmgtframe(&frm, 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(frm, 0, 8); /* XXX timestamp is set by hardware/driver */
+ frm += 8;
+ *(u_int16_t *)frm = htole16(ni->ni_intval);
+ frm += 2;
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ capinfo = IEEE80211_CAPINFO_IBSS;
+ else
+ capinfo = IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+ IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+ capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHSLOT)
+ capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+ bo->bo_caps = (u_int16_t *)frm;
+ *(u_int16_t *)frm = htole16(capinfo);
+ frm += 2;
+ *frm++ = IEEE80211_ELEMID_SSID;
+ if ((ic->ic_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 (ic->ic_curmode != IEEE80211_MODE_FH) {
+ *frm++ = IEEE80211_ELEMID_DSPARMS;
+ *frm++ = 1;
+ *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+ }
+ bo->bo_tim = frm;
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ *frm++ = IEEE80211_ELEMID_IBSSPARMS;
+ *frm++ = 2;
+ *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */
+ bo->bo_tim_len = 0;
+ } else {
+ 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_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_trailer = frm;
+ if (ic->ic_flags & IEEE80211_F_WME) {
+ bo->bo_wme = frm;
+ frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+ }
+ if (ic->ic_flags & IEEE80211_F_WPA)
+ frm = ieee80211_add_wpa(frm, ic);
+ if (ic->ic_curmode == IEEE80211_MODE_11G)
+ frm = ieee80211_add_erp(frm, ic);
+ efrm = ieee80211_add_xrates(frm, rs);
+ bo->bo_trailer_len = efrm - bo->bo_trailer;
+ m->m_pkthdr.len = m->m_len = efrm - mtod(m, u_int8_t *);
+
+ M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
+ KASSERT(m != NULL, ("no space for 802.11 header?"));
+ wh = mtod(m, struct ieee80211_frame *);
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
+ IEEE80211_FC0_SUBTYPE_BEACON;
+ wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+ *(u_int16_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_addr3, ni->ni_bssid);
+ *(u_int16_t *)wh->i_seq = 0;
+
+ return m;
+}
+
+/*
+ * Update the dynamic parts of a beacon frame based on the current state.
+ */
+int
+ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni,
+ struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast)
+{
+ int len_changed = 0;
+ u_int16_t capinfo;
+
+ IEEE80211_BEACON_LOCK(ic);
+ /* XXX faster to recalculate entirely or just changes? */
+ if (ic->ic_opmode == IEEE80211_M_IBSS)
+ capinfo = IEEE80211_CAPINFO_IBSS;
+ else
+ capinfo = IEEE80211_CAPINFO_ESS;
+ if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ capinfo |= IEEE80211_CAPINFO_PRIVACY;
+ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+ IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+ capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHSLOT)
+ capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+ *bo->bo_caps = htole16(capinfo);
+
+ if (ic->ic_flags & IEEE80211_F_WME) {
+ struct ieee80211_wme_state *wme = &ic->ic_wme;
+
+ /*
+ * Check for agressive mode change. When there is
+ * significant high priority traffic in the BSS
+ * throttle back BE traffic by using conservative
+ * parameters. Otherwise BE uses agressive params
+ * to optimize performance of legacy/non-QoS traffic.
+ */
+ if (wme->wme_flags & WME_F_AGGRMODE) {
+ if (wme->wme_hipri_traffic >
+ wme->wme_hipri_switch_thresh) {
+ IEEE80211_DPRINTF(ic, 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);
+ wme->wme_hipri_traffic =
+ wme->wme_hipri_switch_hysteresis;
+ } else
+ wme->wme_hipri_traffic = 0;
+ } else {
+ if (wme->wme_hipri_traffic <=
+ wme->wme_hipri_switch_thresh) {
+ IEEE80211_DPRINTF(ic, 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);
+ wme->wme_hipri_traffic = 0;
+ } else
+ wme->wme_hipri_traffic =
+ wme->wme_hipri_switch_hysteresis;
+ }
+ if (ic->ic_flags & IEEE80211_F_WMEUPDATE) {
+ (void) ieee80211_add_wme_param(bo->bo_wme, wme);
+ ic->ic_flags &= ~IEEE80211_F_WMEUPDATE;
+ }
+ }
+
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* NB: no IBSS support*/
+ struct ieee80211_tim_ie *tie =
+ (struct ieee80211_tim_ie *) bo->bo_tim;
+ if (ic->ic_flags & IEEE80211_F_TIMUPDATE) {
+ u_int timlen, timoff, i;
+ /*
+ * ATIM/DTIM needs updating. If it fits in the
+ * current space allocated then just copy in the
+ * new bits. Otherwise we need to move any trailing
+ * data to make room. Note that we know there is
+ * contiguous space because ieee80211_beacon_allocate
+ * insures there is space in the mbuf to write a
+ * maximal-size virtual bitmap (based on ic_max_aid).
+ */
+ /*
+ * Calculate the bitmap size and offset, copy any
+ * trailer out of the way, and then copy in the
+ * new bitmap and update the information element.
+ * Note that the tim bitmap must contain at least
+ * one byte and any offset must be even.
+ */
+ if (ic->ic_ps_pending != 0) {
+ timoff = 128; /* impossibly large */
+ for (i = 0; i < ic->ic_tim_len; i++)
+ if (ic->ic_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])
+ break;
+ timlen = 1 + (i - timoff);
+ } else {
+ timoff = 0;
+ timlen = 1;
+ }
+ if (timlen != bo->bo_tim_len) {
+ /* copy up/down trailer */
+ ovbcopy(bo->bo_trailer, tie->tim_bitmap+timlen,
+ bo->bo_trailer_len);
+ bo->bo_trailer = tie->tim_bitmap+timlen;
+ bo->bo_wme = bo->bo_trailer;
+ bo->bo_tim_len = timlen;
+
+ /* update information element */
+ tie->tim_len = 3 + timlen;
+ tie->tim_bitctl = timoff;
+ len_changed = 1;
+ }
+ memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff,
+ bo->bo_tim_len);
+
+ ic->ic_flags &= ~IEEE80211_F_TIMUPDATE;
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+ "%s: TIM updated, pending %u, off %u, len %u\n",
+ __func__, ic->ic_ps_pending, timoff, timlen);
+ }
+ /* count down DTIM period */
+ if (tie->tim_count == 0)
+ tie->tim_count = tie->tim_period - 1;
+ else
+ tie->tim_count--;
+ /* update state for buffered multicast frames on DTIM */
+ if (mcast && (tie->tim_count == 1 || tie->tim_period == 1))
+ tie->tim_bitctl |= 1;
+ else
+ tie->tim_bitctl &= ~1;
+ }
+ IEEE80211_BEACON_UNLOCK(ic);
+
+ return len_changed;
+}
+
+/*
+ * Save an outbound packet for a node in power-save sleep state.
+ * The new packet is placed on the node's saved queue, and the TIM
+ * is changed, if necessary.
+ */
+void
+ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni,
+ struct mbuf *m)
+{
+ int qlen, age;
+
+ IEEE80211_NODE_SAVEQ_LOCK(ni);
+ 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);
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_dumppkts(ic))
+ ieee80211_dump_pkt(mtod(m, caddr_t), m->m_len, -1, -1);
+#endif
+ m_freem(m);
+ return;
+ }
+ /*
+ * 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? */
+ age = ((ni->ni_intval * ic->ic_lintval) << 2) / 1024; /* TU -> secs */
+ _IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age);
+ IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+ "[%s] save frame, %u now queued\n",
+ ether_sprintf(ni->ni_macaddr), qlen);
+
+ if (qlen == 1)
+ ic->ic_set_tim(ic, ni, 1);
+}
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index 1f4301a..1764b52 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, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -40,35 +40,20 @@ __FBSDID("$FreeBSD$");
#include "opt_inet.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/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
-
-#include <machine/atomic.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+
#include <net/if.h>
-#include <net/if_dl.h>
#include <net/if_media.h>
-#include <net/if_arp.h>
-#include <net/ethernet.h>
-#include <net/if_llc.h>
+#include <net/ethernet.h> /* XXX for ether_sprintf */
#include <net80211/ieee80211_var.h>
-#include <net/bpf.h>
-
-#ifdef INET
-#include <netinet/in.h>
-#include <netinet/if_ether.h>
-#endif
+/* 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)
@@ -78,6 +63,12 @@ const char *ieee80211_mgt_subtype_name[] = {
"beacon", "atim", "disassoc", "auth",
"deauth", "reserved#13", "reserved#14", "reserved#15"
};
+const char *ieee80211_ctl_subtype_name[] = {
+ "reserved#0", "reserved#1", "reserved#2", "reserved#3",
+ "reserved#3", "reserved#5", "reserved#6", "reserved#7",
+ "reserved#8", "reserved#9", "ps_poll", "rts",
+ "cts", "ack", "cf_end", "cf_end_ack"
+};
const char *ieee80211_state_name[IEEE80211_S_MAX] = {
"INIT", /* IEEE80211_S_INIT */
"SCAN", /* IEEE80211_S_SCAN */
@@ -85,15 +76,23 @@ const char *ieee80211_state_name[IEEE80211_S_MAX] = {
"ASSOC", /* IEEE80211_S_ASSOC */
"RUN" /* IEEE80211_S_RUN */
};
+const char *ieee80211_wme_acnames[] = {
+ "WME_AC_BE",
+ "WME_AC_BK",
+ "WME_AC_VI",
+ "WME_AC_VO",
+ "WME_UPSD",
+};
static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
void
-ieee80211_proto_attach(struct ifnet *ifp)
+ieee80211_proto_attach(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
+ struct ifnet *ifp = ic->ic_ifp;
- ifp->if_hdrlen = sizeof(struct ieee80211_frame);
+ /* XXX room for crypto */
+ ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4);
#ifdef notdef
ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
@@ -103,6 +102,10 @@ ieee80211_proto_attach(struct ifnet *ifp)
ic->ic_fragthreshold = 2346; /* XXX not used yet */
ic->ic_fixed_rate = -1; /* no fixed rate */
ic->ic_protmode = IEEE80211_PROT_CTSONLY;
+ ic->ic_roaming = IEEE80211_ROAMING_AUTO;
+
+ 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);
@@ -115,19 +118,125 @@ ieee80211_proto_attach(struct ifnet *ifp)
}
void
-ieee80211_proto_detach(struct ifnet *ifp)
+ieee80211_proto_detach(struct ieee80211com *ic)
{
- struct ieee80211com *ic = (void *)ifp;
+
+ /*
+ * 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);
IF_DRAIN(&ic->ic_mgtq);
mtx_destroy(&ic->ic_mgtq.ifq_mtx);
+
+ /*
+ * Detach any ACL'ator.
+ */
+ if (ic->ic_acl != NULL)
+ ic->ic_acl->iac_detach(ic);
}
+/*
+ * Simple-minded authenticator module support.
+ */
+
+#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1)
+/* XXX well-known names */
+static const char *auth_modnames[IEEE80211_AUTH_MAX] = {
+ "wlan_internal", /* IEEE80211_AUTH_NONE */
+ "wlan_internal", /* IEEE80211_AUTH_OPEN */
+ "wlan_internal", /* IEEE80211_AUTH_SHARED */
+ "wlan_xauth", /* IEEE80211_AUTH_8021X */
+ "wlan_internal", /* IEEE80211_AUTH_AUTO */
+ "wlan_xauth", /* IEEE80211_AUTH_WPA */
+};
+static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX];
+
+static const struct ieee80211_authenticator auth_internal = {
+ .ia_name = "wlan_internal",
+ .ia_attach = NULL,
+ .ia_detach = NULL,
+ .ia_node_join = NULL,
+ .ia_node_leave = NULL,
+};
+
+/*
+ * Setup internal authenticators once; they are never unregistered.
+ */
+static void
+ieee80211_auth_setup(void)
+{
+ ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal);
+ ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal);
+ ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal);
+}
+SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL);
+
+const struct ieee80211_authenticator *
+ieee80211_authenticator_get(int auth)
+{
+ if (auth >= IEEE80211_AUTH_MAX)
+ return NULL;
+ if (authenticators[auth] == NULL)
+ ieee80211_load_module(auth_modnames[auth]);
+ return authenticators[auth];
+}
+
+void
+ieee80211_authenticator_register(int type,
+ const struct ieee80211_authenticator *auth)
+{
+ if (type >= IEEE80211_AUTH_MAX)
+ return;
+ authenticators[type] = auth;
+}
+
+void
+ieee80211_authenticator_unregister(int type)
+{
+
+ if (type >= IEEE80211_AUTH_MAX)
+ return;
+ authenticators[type] = NULL;
+}
+
+/*
+ * Very simple-minded ACL module support.
+ */
+/* XXX just one for now */
+static const struct ieee80211_aclator *acl = NULL;
+
void
-ieee80211_print_essid(u_int8_t *essid, int len)
+ieee80211_aclator_register(const struct ieee80211_aclator *iac)
{
+ printf("wlan: %s acl policy registered\n", iac->iac_name);
+ acl = iac;
+}
+
+void
+ieee80211_aclator_unregister(const struct ieee80211_aclator *iac)
+{
+ if (acl == iac)
+ acl = NULL;
+ printf("wlan: %s acl policy unregistered\n", iac->iac_name);
+}
+
+const struct ieee80211_aclator *
+ieee80211_aclator_get(const char *name)
+{
+ if (acl == NULL)
+ ieee80211_load_module("wlan_acl");
+ return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL;
+}
+
+void
+ieee80211_print_essid(const u_int8_t *essid, int len)
+{
+ const u_int8_t *p;
int i;
- u_int8_t *p;
if (len > IEEE80211_NWID_LEN)
len = IEEE80211_NWID_LEN;
@@ -149,12 +258,12 @@ ieee80211_print_essid(u_int8_t *essid, int len)
}
void
-ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi)
+ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi)
{
- struct ieee80211_frame *wh;
+ const struct ieee80211_frame *wh;
int i;
- wh = (struct ieee80211_frame *)buf;
+ wh = (const struct ieee80211_frame *)buf;
switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
case IEEE80211_FC1_DIR_NODS:
printf("NODS %s", ether_sprintf(wh->i_addr2));
@@ -172,7 +281,7 @@ ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi)
printf("(%s)", ether_sprintf(wh->i_addr2));
break;
case IEEE80211_FC1_DIR_DSTODS:
- printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1]));
+ printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1]));
printf("->%s", ether_sprintf(wh->i_addr3));
printf("(%s", ether_sprintf(wh->i_addr2));
printf("->%s)", ether_sprintf(wh->i_addr1));
@@ -191,8 +300,13 @@ ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi)
printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
break;
}
- if (wh->i_fc[1] & IEEE80211_FC1_WEP)
- printf(" WEP");
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ int i;
+ printf(" WEP [IV");
+ for (i = 0; i < IEEE80211_WEP_IVLEN; i++)
+ printf(" %.02x", buf[sizeof(*wh)+i]);
+ printf(" KID %u]", buf[sizeof(*wh)+i] >> 6);
+ }
if (rate >= 0)
printf(" %dM", rate / 2);
if (rssi >= 0)
@@ -213,12 +327,18 @@ ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags
{
#define RV(v) ((v) & IEEE80211_RATE_VAL)
int i, j, ignore, error;
- int okrate, badrate;
+ int okrate, badrate, fixedrate;
struct ieee80211_rateset *srs, *nrs;
u_int8_t r;
+ /*
+ * If the fixed rate check was requested but no
+ * fixed has been defined then just remove it.
+ */
+ if ((flags & IEEE80211_F_DOFRATE) && ic->ic_fixed_rate < 0)
+ flags &= ~IEEE80211_F_DOFRATE;
error = 0;
- okrate = badrate = 0;
+ okrate = badrate = fixedrate = 0;
srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
nrs = &ni->ni_rates;
for (i = 0; i < nrs->rs_nrates; ) {
@@ -239,17 +359,10 @@ ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags
badrate = r;
if (flags & IEEE80211_F_DOFRATE) {
/*
- * Apply fixed rate constraint. Note that we do
- * not apply the constraint to basic rates as
- * otherwise we may not be able to associate if
- * the rate set we submit to the AP is invalid
- * (e.g. fix rate at 36Mb/s which is not a basic
- * rate for 11a operation).
+ * Check any fixed rate is included.
*/
- if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 &&
- ic->ic_fixed_rate >= 0 &&
- r != RV(srs->rs_rates[ic->ic_fixed_rate]))
- ignore++;
+ if (r == RV(srs->rs_rates[ic->ic_fixed_rate]))
+ fixedrate = r;
}
if (flags & IEEE80211_F_DONEGO) {
/*
@@ -299,23 +412,423 @@ ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags
okrate = nrs->rs_rates[i];
i++;
}
- if (okrate == 0 || error != 0)
+ if (okrate == 0 || error != 0 ||
+ ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0))
return badrate | IEEE80211_RATE_BASIC;
else
return RV(okrate);
#undef RV
}
+/*
+ * Reset 11g-related state.
+ */
+void
+ieee80211_reset_erp(struct ieee80211com *ic)
+{
+ ic->ic_flags &= ~IEEE80211_F_USEPROT;
+ ic->ic_nonerpsta = 0;
+ ic->ic_longslotsta = 0;
+ /*
+ * Short slot time is enabled only when operating in 11g
+ * and not in an IBSS. We must also honor whether or not
+ * the driver is capable of doing it.
+ */
+ ieee80211_set_shortslottime(ic,
+ ic->ic_curmode == IEEE80211_MODE_11A ||
+ (ic->ic_curmode == IEEE80211_MODE_11G &&
+ ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ (ic->ic_caps & IEEE80211_C_SHSLOT)));
+ /*
+ * Set short preamble and ERP barker-preamble flags.
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_11A ||
+ (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
+ ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+ } else {
+ ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+ ic->ic_flags |= IEEE80211_F_USEBARKER;
+ }
+}
+
+/*
+ * Set the short slot time state and notify the driver.
+ */
+void
+ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff)
+{
+ if (onoff)
+ ic->ic_flags |= IEEE80211_F_SHSLOT;
+ else
+ ic->ic_flags &= ~IEEE80211_F_SHSLOT;
+ /* notify driver */
+ if (ic->ic_updateslot != NULL)
+ ic->ic_updateslot(ic->ic_ifp);
+}
+
+/*
+ * Check if the specified rate set supports ERP.
+ * NB: the rate set is assumed to be sorted.
+ */
+int
+ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 };
+ int i, j;
+
+ if (rs->rs_nrates < N(rates))
+ return 0;
+ for (i = 0; i < N(rates); i++) {
+ for (j = 0; j < rs->rs_nrates; j++) {
+ int r = rs->rs_rates[j] & IEEE80211_RATE_VAL;
+ if (rates[i] == r)
+ goto next;
+ if (r > rates[i])
+ return 0;
+ }
+ return 0;
+ next:
+ ;
+ }
+ return 1;
+#undef N
+}
+
+/*
+ * Mark the basic rates for the 11g 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 const struct ieee80211_rateset basic[] = {
+ { 0 }, /* IEEE80211_MODE_AUTO */
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */
+ { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */
+ { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */
+ { 0 }, /* IEEE80211_MODE_FH */
+ /* IEEE80211_MODE_PUREG (not yet) */
+ { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+ };
+ int i, j;
+
+ for (i = 0; i < rs->rs_nrates; i++) {
+ rs->rs_rates[i] &= IEEE80211_RATE_VAL;
+ for (j = 0; j < basic[mode].rs_nrates; j++)
+ if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
+ rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
+ break;
+ }
+ }
+}
+
+/*
+ * WME protocol support. The following parameters come from the spec.
+ */
+typedef struct phyParamType {
+ u_int8_t aifsn;
+ u_int8_t logcwmin;
+ u_int8_t logcwmax;
+ u_int16_t txopLimit;
+ u_int8_t acm;
+} paramType;
+
+static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
+ { 3, 4, 6 }, /* IEEE80211_MODE_AUTO */
+ { 3, 4, 6 }, /* IEEE80211_MODE_11A */
+ { 3, 5, 7 }, /* IEEE80211_MODE_11B */
+ { 3, 4, 6 }, /* IEEE80211_MODE_11G */
+ { 3, 5, 7 }, /* IEEE80211_MODE_FH */
+ { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_A */
+ { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_G */
+};
+static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = {
+ { 7, 4, 10 }, /* IEEE80211_MODE_AUTO */
+ { 7, 4, 10 }, /* IEEE80211_MODE_11A */
+ { 7, 5, 10 }, /* IEEE80211_MODE_11B */
+ { 7, 4, 10 }, /* IEEE80211_MODE_11G */
+ { 7, 5, 10 }, /* IEEE80211_MODE_FH */
+ { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_A */
+ { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_G */
+};
+static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = {
+ { 1, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */
+ { 1, 3, 4, 94 }, /* IEEE80211_MODE_11A */
+ { 1, 4, 5, 188 }, /* IEEE80211_MODE_11B */
+ { 1, 3, 4, 94 }, /* IEEE80211_MODE_11G */
+ { 1, 4, 5, 188 }, /* IEEE80211_MODE_FH */
+ { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */
+};
+static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = {
+ { 1, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */
+ { 1, 2, 3, 47 }, /* IEEE80211_MODE_11A */
+ { 1, 3, 4, 102 }, /* IEEE80211_MODE_11B */
+ { 1, 2, 3, 47 }, /* IEEE80211_MODE_11G */
+ { 1, 3, 4, 102 }, /* IEEE80211_MODE_FH */
+ { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */
+};
+
+static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = {
+ { 3, 4, 10 }, /* IEEE80211_MODE_AUTO */
+ { 3, 4, 10 }, /* IEEE80211_MODE_11A */
+ { 3, 5, 10 }, /* IEEE80211_MODE_11B */
+ { 3, 4, 10 }, /* IEEE80211_MODE_11G */
+ { 3, 5, 10 }, /* IEEE80211_MODE_FH */
+ { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_A */
+ { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_G */
+};
+static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = {
+ { 2, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */
+ { 2, 3, 4, 94 }, /* IEEE80211_MODE_11A */
+ { 2, 4, 5, 188 }, /* IEEE80211_MODE_11B */
+ { 2, 3, 4, 94 }, /* IEEE80211_MODE_11G */
+ { 2, 4, 5, 188 }, /* IEEE80211_MODE_FH */
+ { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */
+ { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */
+};
+static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = {
+ { 2, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */
+ { 2, 2, 3, 47 }, /* IEEE80211_MODE_11A */
+ { 2, 3, 4, 102 }, /* IEEE80211_MODE_11B */
+ { 2, 2, 3, 47 }, /* IEEE80211_MODE_11G */
+ { 2, 3, 4, 102 }, /* IEEE80211_MODE_FH */
+ { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */
+};
+
+void
+ieee80211_wme_initparams(struct ieee80211com *ic)
+{
+ struct ieee80211_wme_state *wme = &ic->ic_wme;
+ const paramType *pPhyParam, *pBssPhyParam;
+ struct wmeParams *wmep;
+ int i;
+
+ if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+ return;
+
+ for (i = 0; i < WME_NUM_AC; i++) {
+ switch (i) {
+ case WME_AC_BK:
+ pPhyParam = &phyParamForAC_BK[ic->ic_curmode];
+ pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode];
+ break;
+ case WME_AC_VI:
+ pPhyParam = &phyParamForAC_VI[ic->ic_curmode];
+ pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode];
+ break;
+ case WME_AC_VO:
+ pPhyParam = &phyParamForAC_VO[ic->ic_curmode];
+ pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode];
+ break;
+ case WME_AC_BE:
+ default:
+ pPhyParam = &phyParamForAC_BE[ic->ic_curmode];
+ pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode];
+ break;
+ }
+
+ wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ wmep->wmep_acm = pPhyParam->acm;
+ wmep->wmep_aifsn = pPhyParam->aifsn;
+ wmep->wmep_logcwmin = pPhyParam->logcwmin;
+ wmep->wmep_logcwmax = pPhyParam->logcwmax;
+ wmep->wmep_txopLimit = pPhyParam->txopLimit;
+ } else {
+ wmep->wmep_acm = pBssPhyParam->acm;
+ wmep->wmep_aifsn = pBssPhyParam->aifsn;
+ wmep->wmep_logcwmin = pBssPhyParam->logcwmin;
+ wmep->wmep_logcwmax = pBssPhyParam->logcwmax;
+ wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
+
+ }
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ "%s: %s chan [acm %u aifsn %u log2(cwmin) %u "
+ "log2(cwmax) %u txpoLimit %u]\n", __func__
+ , ieee80211_wme_acnames[i]
+ , wmep->wmep_acm
+ , wmep->wmep_aifsn
+ , wmep->wmep_logcwmin
+ , wmep->wmep_logcwmax
+ , wmep->wmep_txopLimit
+ );
+
+ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
+ wmep->wmep_acm = pBssPhyParam->acm;
+ wmep->wmep_aifsn = pBssPhyParam->aifsn;
+ wmep->wmep_logcwmin = pBssPhyParam->logcwmin;
+ wmep->wmep_logcwmax = pBssPhyParam->logcwmax;
+ wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ "%s: %s bss [acm %u aifsn %u log2(cwmin) %u "
+ "log2(cwmax) %u txpoLimit %u]\n", __func__
+ , ieee80211_wme_acnames[i]
+ , wmep->wmep_acm
+ , wmep->wmep_aifsn
+ , wmep->wmep_logcwmin
+ , wmep->wmep_logcwmax
+ , wmep->wmep_txopLimit
+ );
+ }
+ /* NB: check ic_bss to avoid NULL deref on initial attach */
+ if (ic->ic_bss != NULL) {
+ /*
+ * Calculate agressive mode switching threshold based
+ * on beacon interval. This doesn't need locking since
+ * we're only called before entering the RUN state at
+ * which point we start sending beacon frames.
+ */
+ wme->wme_hipri_switch_thresh =
+ (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100;
+ ieee80211_wme_updateparams(ic);
+ }
+}
+
+/*
+ * Update WME parameters for ourself and the BSS.
+ */
+void
+ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
+{
+ static const paramType phyParam[IEEE80211_MODE_MAX] = {
+ { 2, 4, 10, 64 }, /* IEEE80211_MODE_AUTO */
+ { 2, 4, 10, 64 }, /* IEEE80211_MODE_11A */
+ { 2, 5, 10, 64 }, /* IEEE80211_MODE_11B */
+ { 2, 4, 10, 64 }, /* IEEE80211_MODE_11G */
+ { 2, 5, 10, 64 }, /* IEEE80211_MODE_FH */
+ { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_G */
+ };
+ struct ieee80211_wme_state *wme = &ic->ic_wme;
+ const struct wmeParams *wmep;
+ struct wmeParams *chanp, *bssp;
+ int i;
+
+ /* set up the channel access parameters for the physical device */
+ for (i = 0; i < WME_NUM_AC; i++) {
+ chanp = &wme->wme_chanParams.cap_wmeParams[i];
+ wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
+ chanp->wmep_aifsn = wmep->wmep_aifsn;
+ chanp->wmep_logcwmin = wmep->wmep_logcwmin;
+ chanp->wmep_logcwmax = wmep->wmep_logcwmax;
+ chanp->wmep_txopLimit = wmep->wmep_txopLimit;
+
+ chanp = &wme->wme_bssChanParams.cap_wmeParams[i];
+ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
+ chanp->wmep_aifsn = wmep->wmep_aifsn;
+ chanp->wmep_logcwmin = wmep->wmep_logcwmin;
+ chanp->wmep_logcwmax = wmep->wmep_logcwmax;
+ chanp->wmep_txopLimit = wmep->wmep_txopLimit;
+ }
+
+ /*
+ * This implements agressive mode as found in certain
+ * vendors' AP's. When there is significant high
+ * priority (VI/VO) traffic in the BSS throttle back BE
+ * traffic by using conservative parameters. Otherwise
+ * BE uses agressive params to optimize performance of
+ * legacy/non-QoS traffic.
+ */
+ if ((ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ (wme->wme_flags & WME_F_AGGRMODE) == 0) ||
+ (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
+ (ic->ic_flags & IEEE80211_F_WME) == 0) {
+ chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
+ bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
+
+ chanp->wmep_aifsn = bssp->wmep_aifsn =
+ phyParam[ic->ic_curmode].aifsn;
+ chanp->wmep_logcwmin = bssp->wmep_logcwmin =
+ phyParam[ic->ic_curmode].logcwmin;
+ chanp->wmep_logcwmax = bssp->wmep_logcwmax =
+ phyParam[ic->ic_curmode].logcwmax;
+ chanp->wmep_txopLimit = bssp->wmep_txopLimit =
+ (ic->ic_caps & IEEE80211_C_BURST) ?
+ phyParam[ic->ic_curmode].txopLimit : 0;
+ IEEE80211_DPRINTF(ic, 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]
+ , chanp->wmep_acm
+ , chanp->wmep_aifsn
+ , chanp->wmep_logcwmin
+ , chanp->wmep_logcwmax
+ , chanp->wmep_txopLimit
+ );
+ }
+
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) == 0) {
+ static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = {
+ 3, /* IEEE80211_MODE_AUTO */
+ 3, /* IEEE80211_MODE_11A */
+ 4, /* IEEE80211_MODE_11B */
+ 3, /* IEEE80211_MODE_11G */
+ 4, /* IEEE80211_MODE_FH */
+ 3, /* IEEE80211_MODE_TURBO_A */
+ 3, /* IEEE80211_MODE_TURBO_G */
+ };
+ chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
+ bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
+
+ chanp->wmep_logcwmin = bssp->wmep_logcwmin =
+ logCwMin[ic->ic_curmode];
+ IEEE80211_DPRINTF(ic, 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? */
+ /*
+ * 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;
+ ic->ic_flags |= IEEE80211_F_WMEUPDATE;
+ }
+
+ wme->wme_update(ic);
+
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+ "%s: WME params updated, cap_info 0x%x\n", __func__,
+ ic->ic_opmode == IEEE80211_M_STA ?
+ wme->wme_wmeChanParams.cap_info :
+ wme->wme_bssChanParams.cap_info);
+}
+
+void
+ieee80211_wme_updateparams(struct ieee80211com *ic)
+{
+
+ if (ic->ic_caps & IEEE80211_C_WME) {
+ IEEE80211_BEACON_LOCK(ic);
+ ieee80211_wme_updateparams_locked(ic);
+ IEEE80211_BEACON_UNLOCK(ic);
+ }
+}
+
static int
-ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt)
+ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
{
- struct ifnet *ifp = &ic->ic_if;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ieee80211_node_table *nt;
struct ieee80211_node *ni;
enum ieee80211_state ostate;
ostate = ic->ic_state;
- IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__,
- ieee80211_state_name[ostate], ieee80211_state_name[nstate]));
+ 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 */
ni = ic->ic_bss; /* NB: no reference held */
switch (nstate) {
@@ -329,22 +842,28 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_DISASSOC,
IEEE80211_REASON_ASSOC_LEAVE);
+ ieee80211_sta_leave(ic, ni);
break;
case IEEE80211_M_HOSTAP:
- IEEE80211_NODE_LOCK(ic);
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ nt = ic->ic_sta;
+ if (nt == NULL) { /* XXX cannot happen */
+ if_printf(ifp, "no sta table (run)\n");
+ break;
+ }
+ IEEE80211_NODE_LOCK(nt);
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
if (ni->ni_associd == 0)
continue;
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_DISASSOC,
IEEE80211_REASON_ASSOC_LEAVE);
}
- IEEE80211_NODE_UNLOCK(ic);
+ IEEE80211_NODE_UNLOCK(nt);
break;
default:
break;
}
- /* FALLTHRU */
+ goto reset;
case IEEE80211_S_ASSOC:
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
@@ -353,42 +872,41 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt
IEEE80211_REASON_AUTH_LEAVE);
break;
case IEEE80211_M_HOSTAP:
- IEEE80211_NODE_LOCK(ic);
- TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+ nt = ic->ic_sta;
+ if (nt == NULL) { /* XXX cannot happen */
+ if_printf(ifp, "no sta table (assoc)\n");
+ break;
+ }
+ IEEE80211_NODE_LOCK(nt);
+ TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_AUTH_LEAVE);
}
- IEEE80211_NODE_UNLOCK(ic);
+ IEEE80211_NODE_UNLOCK(nt);
break;
default:
break;
}
- /* FALLTHRU */
+ goto reset;
case IEEE80211_S_AUTH:
case IEEE80211_S_SCAN:
+ reset:
ic->ic_mgt_timer = 0;
IF_DRAIN(&ic->ic_mgtq);
- if (ic->ic_wep_ctx != NULL) {
- free(ic->ic_wep_ctx, M_DEVBUF);
- ic->ic_wep_ctx = NULL;
- }
- ieee80211_free_allnodes(ic);
+ ieee80211_reset_bss(ic);
+ ieee80211_crypto_delglobalkeys(ic);
break;
}
+ if (ic->ic_auth->ia_detach != NULL)
+ ic->ic_auth->ia_detach(ic);
break;
case IEEE80211_S_SCAN:
- ic->ic_flags &= ~IEEE80211_F_SIBSS;
- /* initialize bss for probe request */
- IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr);
- IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr);
- ni->ni_rates = ic->ic_sup_rates[
- ieee80211_chan2mode(ic, ni->ni_chan)];
- ni->ni_associd = 0;
- ni->ni_rstamp = 0;
switch (ostate) {
case IEEE80211_S_INIT:
- if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ 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) {
/*
* AP operation and we already have a channel;
@@ -396,51 +914,53 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt
*/
ieee80211_create_ibss(ic, ic->ic_des_chan);
} else {
- ieee80211_begin_scan(ifp);
+ ieee80211_begin_scan(ic, arg);
}
break;
case IEEE80211_S_SCAN:
- /* scan next */
- if (ic->ic_flags & IEEE80211_F_ASCAN) {
+ /*
+ * Scan next. If doing an active scan and the
+ * channel is not marked passive-only then send
+ * a probe request. Otherwise just listen for
+ * beacons on the channel.
+ */
+ if ((ic->ic_flags & IEEE80211_F_ASCAN) &&
+ (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
}
break;
case IEEE80211_S_RUN:
/* beacon miss */
- if (ifp->if_flags & IFF_DEBUG) {
- /* XXX bssid clobbered above */
- if_printf(ifp, "no recent beacons from %s;"
- " rescanning\n",
- ether_sprintf(ic->ic_bss->ni_bssid));
- }
- ieee80211_free_allnodes(ic);
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE,
+ "no recent beacons from %s; rescanning\n",
+ ether_sprintf(ic->ic_bss->ni_bssid));
+ ieee80211_sta_leave(ic, ni);
+ ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */
/* FALLTHRU */
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
/* timeout restart scan */
- ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr);
+ ni = ieee80211_find_node(&ic->ic_scan,
+ ic->ic_bss->ni_macaddr);
if (ni != NULL) {
ni->ni_fails++;
ieee80211_unref_node(&ni);
}
- ieee80211_begin_scan(ifp);
+ ieee80211_begin_scan(ic, arg);
break;
}
break;
case IEEE80211_S_AUTH:
switch (ostate) {
case IEEE80211_S_INIT:
- IEEE80211_DPRINTF(("%s: invalid transition\n",
- __func__));
- break;
case IEEE80211_S_SCAN:
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_AUTH, 1);
break;
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
- switch (mgt) {
+ switch (arg) {
case IEEE80211_FC0_SUBTYPE_AUTH:
/* ??? */
IEEE80211_SEND_MGMT(ic, ni,
@@ -452,7 +972,7 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt
}
break;
case IEEE80211_S_RUN:
- switch (mgt) {
+ switch (arg) {
case IEEE80211_FC0_SUBTYPE_AUTH:
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_AUTH, 2);
@@ -462,6 +982,7 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt
/* try to reauth */
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_AUTH, 1);
+ ieee80211_sta_leave(ic, ni);
break;
}
break;
@@ -472,8 +993,8 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt
case IEEE80211_S_INIT:
case IEEE80211_S_SCAN:
case IEEE80211_S_ASSOC:
- IEEE80211_DPRINTF(("%s: invalid transition\n",
- __func__));
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ "%s: invalid transition\n", __func__);
break;
case IEEE80211_S_AUTH:
IEEE80211_SEND_MGMT(ic, ni,
@@ -482,28 +1003,35 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt
case IEEE80211_S_RUN:
IEEE80211_SEND_MGMT(ic, ni,
IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+ ieee80211_sta_leave(ic, ni);
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)
+ break;
+ /* fall thru... */
case IEEE80211_S_AUTH:
case IEEE80211_S_RUN:
- IEEE80211_DPRINTF(("%s: invalid transition\n",
- __func__));
+ IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+ "%s: invalid transition\n", __func__);
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));
- if (ifp->if_flags & IFF_DEBUG) {
- if_printf(ifp, " ");
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_msg_debug(ic)) {
if (ic->ic_opmode == IEEE80211_M_STA)
- printf("associated ");
+ if_printf(ifp, "associated ");
else
- printf("synchronized ");
+ if_printf(ifp, "synchronized ");
printf("with %s ssid ",
ether_sprintf(ni->ni_bssid));
ieee80211_print_essid(ic->ic_bss->ni_essid,
@@ -512,10 +1040,39 @@ ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt
ieee80211_chan2ieee(ic, ni->ni_chan),
IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
}
+#endif
ic->ic_mgt_timer = 0;
- if_start(ifp);
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ ieee80211_notify_node_join(ic, ni,
+ arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+ if_start(ifp); /* XXX not authorized yet */
break;
}
+ /*
+ * 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);
+ }
+ /*
+ * 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(ic, ni);
+ /*
+ * Enable inactivity processing.
+ * XXX
+ */
+ ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT;
+ if (ic->ic_sta != NULL)
+ ic->ic_sta->nt_inact_timer = IEEE80211_INACT_WAIT;
break;
}
return 0;
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
index 8c85003..f793f8e 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, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -52,27 +52,193 @@ enum ieee80211_state {
extern const char *ieee80211_mgt_subtype_name[];
-extern void ieee80211_proto_attach(struct ifnet *);
-extern void ieee80211_proto_detach(struct ifnet *);
+extern void ieee80211_proto_attach(struct ieee80211com *);
+extern void ieee80211_proto_detach(struct ieee80211com *);
struct ieee80211_node;
-extern void ieee80211_input(struct ifnet *, struct mbuf *,
+extern void ieee80211_input(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *, int, u_int32_t);
extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *, int, int, u_int32_t);
+extern int ieee80211_send_nulldata(struct ieee80211com *,
+ struct ieee80211_node *);
extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
int, int);
-extern struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *,
- struct ieee80211_node **);
-extern struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *);
-extern u_int8_t *ieee80211_add_rates(u_int8_t *frm,
- const struct ieee80211_rateset *);
+extern int ieee80211_classify(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
+extern struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
+extern void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *,
+ struct mbuf *);
+
+extern void ieee80211_reset_erp(struct ieee80211com *);
+extern void ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
+extern int ieee80211_iserp_rateset(struct ieee80211com *,
+ struct ieee80211_rateset *);
+extern void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
+ enum ieee80211_phymode);
+
+/*
+ * Return the size of the 802.11 header for a management or data frame.
+ */
+static inline int
+ieee80211_hdrsize(const void *data)
+{
+ const struct ieee80211_frame *wh = data;
+ int size = sizeof(struct ieee80211_frame);
+
+ /* NB: we don't handle control frames */
+ KASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL,
+ ("%s: control frame", __func__));
+ if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+ size += IEEE80211_ADDR_LEN;
+ if (IEEE80211_QOS_HAS_SEQ(wh))
+ size += sizeof(u_int16_t);
+ return size;
+}
+
+/*
+ * Return the size of the 802.11 header; handles any type of frame.
+ */
+static inline int
+ieee80211_anyhdrsize(const void *data)
+{
+ const struct ieee80211_frame *wh = data;
+
+ if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
+ switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
+ case IEEE80211_FC0_SUBTYPE_CTS:
+ case IEEE80211_FC0_SUBTYPE_ACK:
+ return sizeof(struct ieee80211_frame_ack);
+ }
+ return sizeof(struct ieee80211_frame_min);
+ } else
+ return ieee80211_hdrsize(data);
+}
+
+/*
+ * Template for an in-kernel authenticator. Authenticators
+ * register with the protocol code and are typically loaded
+ * as separate modules as needed.
+ */
+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 *);
+};
+extern void ieee80211_authenticator_register(int type,
+ const struct ieee80211_authenticator *);
+extern void ieee80211_authenticator_unregister(int type);
+extern const struct ieee80211_authenticator *
+ ieee80211_authenticator_get(int auth);
+
+/*
+ * 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.
+ */
+struct ieee80211_aclator {
+ const char *iac_name; /* printable name */
+ int (*iac_attach)(struct ieee80211com *);
+ void (*iac_detach)(struct ieee80211com *);
+ int (*iac_check)(struct ieee80211com *,
+ const u_int8_t mac[IEEE80211_ADDR_LEN]);
+ int (*iac_add)(struct ieee80211com *,
+ const u_int8_t mac[IEEE80211_ADDR_LEN]);
+ int (*iac_remove)(struct ieee80211com *,
+ const u_int8_t mac[IEEE80211_ADDR_LEN]);
+ int (*iac_flush)(struct ieee80211com *);
+ int (*iac_setpolicy)(struct ieee80211com *, int);
+ int (*iac_getpolicy)(struct ieee80211com *);
+};
+extern void ieee80211_aclator_register(const struct ieee80211_aclator *);
+extern void ieee80211_aclator_unregister(const struct ieee80211_aclator *);
+extern 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_DONEGO 0x00000004 /* calc negotiated rate */
+#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */
+extern int ieee80211_fix_rate(struct ieee80211com *,
+ struct ieee80211_node *, int);
+
+/*
+ * WME/WMM support.
+ */
+struct wmeParams {
+ u_int8_t wmep_acm;
+ u_int8_t wmep_aifsn;
+ u_int8_t wmep_logcwmin; /* log2(cwmin) */
+ u_int8_t wmep_logcwmax; /* log2(cwmax) */
+ u_int8_t wmep_txopLimit;
+ u_int8_t wmep_noackPolicy; /* 0 (ack), 1 (no ack) */
+};
+
+struct chanAccParams {
+ u_int8_t cap_info; /* version of the current set */
+ struct wmeParams cap_wmeParams[WME_NUM_AC];
+};
+
+struct ieee80211_wme_state {
+ u_int wme_flags;
+#define WME_F_AGGRMODE 0x00000001 /* STATUS: WME agressive mode */
+ u_int wme_hipri_traffic; /* VI/VO frames in beacon interval */
+ u_int wme_hipri_switch_thresh;/* agressive mode switch thresh */
+ u_int wme_hipri_switch_hysteresis;/* agressive mode switch hysteresis */
+
+ struct wmeParams wme_params[4]; /* from assoc resp for each AC*/
+ struct chanAccParams wme_wmeChanParams; /* WME params applied to self */
+ struct chanAccParams wme_wmeBssChanParams;/* WME params bcast to stations */
+ struct chanAccParams wme_chanParams; /* params applied to self */
+ struct chanAccParams wme_bssChanParams; /* params bcast to stations */
+
+ int (*wme_update)(struct ieee80211com *);
+};
+
+extern void ieee80211_wme_initparams(struct ieee80211com *);
+extern void ieee80211_wme_updateparams(struct ieee80211com *);
+extern void ieee80211_wme_updateparams_locked(struct ieee80211com *);
+
#define ieee80211_new_state(_ic, _nstate, _arg) \
(((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
-extern u_int8_t *ieee80211_add_xrates(u_int8_t *frm,
- const struct ieee80211_rateset *);
-extern void ieee80211_print_essid(u_int8_t *, int);
-extern void ieee80211_dump_pkt(u_int8_t *, int, int, int);
+extern void ieee80211_print_essid(const u_int8_t *, int);
+extern void ieee80211_dump_pkt(const u_int8_t *, int, int, int);
extern const char *ieee80211_state_name[IEEE80211_S_MAX];
+extern const char *ieee80211_wme_acnames[];
+
+/*
+ * Beacon frames constructed by ieee80211_beacon_alloc
+ * have the following structure filled in so drivers
+ * can update the frame later w/ minimal overhead.
+ */
+struct ieee80211_beacon_offsets {
+ u_int16_t *bo_caps; /* capabilities */
+ u_int8_t *bo_tim; /* start of atim/dtim */
+ u_int8_t *bo_wme; /* start of WME parameters */
+ u_int8_t *bo_trailer; /* start of fixed-size trailer */
+ u_int16_t bo_tim_len; /* atim/dtim length in bytes */
+ u_int16_t bo_trailer_len; /* trailer length in bytes */
+};
+extern struct mbuf *ieee80211_beacon_alloc(struct ieee80211com *,
+ struct ieee80211_node *, struct ieee80211_beacon_offsets *);
+extern int ieee80211_beacon_update(struct ieee80211com *,
+ struct ieee80211_node *, struct ieee80211_beacon_offsets *,
+ struct mbuf *, int broadcast);
+
+/*
+ * Notification methods called from the 802.11 state machine.
+ * Note that while these are defined here, their implementation
+ * is OS-specific.
+ */
+extern void ieee80211_notify_node_join(struct ieee80211com *,
+ struct ieee80211_node *, int newassoc);
+extern void ieee80211_notify_node_leave(struct ieee80211com *,
+ struct ieee80211_node *);
+extern void ieee80211_notify_scan_done(struct ieee80211com *);
#endif /* _NET80211_IEEE80211_PROTO_H_ */
diff --git a/sys/net80211/ieee80211_radiotap.h b/sys/net80211/ieee80211_radiotap.h
index 68e9367..e36a911 100644
--- a/sys/net80211/ieee80211_radiotap.h
+++ b/sys/net80211/ieee80211_radiotap.h
@@ -46,11 +46,11 @@
* function of...") that I cannot set false expectations for lawyerly
* readers.
*/
-#ifdef _KERNEL
+#if defined(__KERNEL__) || defined(_KERNEL)
#ifndef DLT_IEEE802_11_RADIO
#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus WLAN header */
#endif
-#endif /* _KERNEL */
+#endif /* defined(__KERNEL__) || defined(_KERNEL) */
/* The radio capture header precedes the 802.11 header. */
struct ieee80211_radiotap_header {
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index c5ef197..ad98613 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, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,124 +37,54 @@
/*
* 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>
+#elif __FreeBSD__
+#include <net80211/ieee80211_freebsd.h>
+#elif __linux__
+#include <net80211/ieee80211_linux.h>
+#else
+#error "No support for your operating system!"
+#endif
+
+#include <net80211/_ieee80211.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_crypto.h>
#include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */
#include <net80211/ieee80211_node.h>
#include <net80211/ieee80211_proto.h>
-#define IEEE80211_CHAN_MAX 255
-#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */
-#define IEEE80211_CHAN_ANYC \
- ((struct ieee80211_channel *) IEEE80211_CHAN_ANY)
-
-#define IEEE80211_TXPOWER_MAX 100 /* max power */
-#define IEEE80211_TXPOWER_MIN 0 /* kill radio (if possible) */
-
-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 */
-};
-#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
-
-/* XXX not really a mode; there are really multiple PHY's */
-enum ieee80211_phymode {
- IEEE80211_MODE_AUTO = 0, /* autoselect */
- IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */
- IEEE80211_MODE_11B = 2, /* 2GHz, CCK */
- IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */
- IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */
- IEEE80211_MODE_TURBO = 5, /* 5GHz, OFDM, 2x clock */
-};
-#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO+1)
+#define IEEE80211_TXPOWER_MAX 100 /* .5 dbM (XXX units?) */
+#define IEEE80211_TXPOWER_MIN 0 /* kill radio */
-enum ieee80211_opmode {
- IEEE80211_M_STA = 1, /* infrastructure station */
- IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */
- IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */
- IEEE80211_M_HOSTAP = 6, /* Software Access Point */
- IEEE80211_M_MONITOR = 8 /* Monitor mode */
-};
+#define IEEE80211_DTIM_MAX 15 /* max DTIM period */
+#define IEEE80211_DTIM_MIN 1 /* min DTIM period */
+#define IEEE80211_DTIM_DEFAULT 1 /* default DTIM period */
-/*
- * 802.11g protection mode.
- */
-enum ieee80211_protmode {
- IEEE80211_PROT_NONE = 0, /* no protection */
- IEEE80211_PROT_CTSONLY = 1, /* CTS to self */
- IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */
-};
+#define IEEE80211_BINTVAL_MAX 500 /* max beacon interval (ms) */
+#define IEEE80211_BINTVAL_MIN 25 /* min beacon interval */
+#define IEEE80211_BINTVAL_DEFAULT 100 /* default beacon interval */
-/*
- * Channels are specified by frequency and attributes.
- */
-struct ieee80211_channel {
- u_int16_t ic_freq; /* setting in Mhz */
- u_int16_t ic_flags; /* see below */
-};
+#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */
+#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */
-/* bits 0-3 are for private use by drivers */
-/* channel attributes */
-#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */
-#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */
-#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */
-#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */
-#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */
-#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */
-#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */
-#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */
-
-/*
- * Useful combinations of channel characteristics.
- */
-#define IEEE80211_CHAN_FHSS \
- (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
-#define IEEE80211_CHAN_A \
- (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
-#define IEEE80211_CHAN_B \
- (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
-#define IEEE80211_CHAN_PUREG \
- (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
-#define IEEE80211_CHAN_G \
- (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
-#define IEEE80211_CHAN_T \
- (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
-
-#define IEEE80211_IS_CHAN_FHSS(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
-#define IEEE80211_IS_CHAN_A(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
-#define IEEE80211_IS_CHAN_B(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
-#define IEEE80211_IS_CHAN_PUREG(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
-#define IEEE80211_IS_CHAN_G(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
-#define IEEE80211_IS_CHAN_T(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
-
-#define IEEE80211_IS_CHAN_2GHZ(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
-#define IEEE80211_IS_CHAN_5GHZ(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
-#define IEEE80211_IS_CHAN_OFDM(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0)
-#define IEEE80211_IS_CHAN_CCK(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0)
-#define IEEE80211_IS_CHAN_GFSK(_c) \
- (((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0)
-
-/* ni_chan encoding for FH phy */
-#define IEEE80211_FH_CHANMOD 80
-#define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat))
-#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1)
-#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD)
+struct ieee80211_aclator;
+struct sysctl_ctx_list;
struct ieee80211com {
- struct arpcom ic_ac;
+ SLIST_ENTRY(ieee80211com) ic_next;
+ struct ifnet *ic_ifp; /* associated device */
+ struct ieee80211_stats ic_stats; /* statistics */
+ struct sysctl_ctx_list *ic_sysctl; /* dynamic sysctl context */
+ u_int32_t ic_debug; /* debug msg flags */
+ int ic_vap; /* virtual AP index */
+ ieee80211_beacon_lock_t ic_beaconlock; /* beacon update lock */
+
+ int (*ic_reset)(struct ifnet *);
void (*ic_recv_mgmt)(struct ieee80211com *,
struct mbuf *, struct ieee80211_node *,
int, int, u_int32_t);
@@ -164,12 +94,16 @@ struct ieee80211com {
enum ieee80211_state, int);
void (*ic_newassoc)(struct ieee80211com *,
struct ieee80211_node *, int);
+ void (*ic_updateslot)(struct ifnet *);
+ void (*ic_set_tim)(struct ieee80211com *,
+ struct ieee80211_node *, int);
u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
- u_char ic_chan_avail[roundup(IEEE80211_CHAN_MAX,NBBY)];
- u_char ic_chan_active[roundup(IEEE80211_CHAN_MAX, NBBY)];
- u_char ic_chan_scan[roundup(IEEE80211_CHAN_MAX,NBBY)];
+ u_int8_t ic_chan_avail[IEEE80211_CHAN_BYTES];
+ u_int8_t ic_chan_active[IEEE80211_CHAN_BYTES];
+ u_int8_t ic_chan_scan[IEEE80211_CHAN_BYTES];
+ struct ieee80211_node_table ic_scan; /* scan candidates */
struct ifqueue ic_mgtq;
u_int32_t ic_flags; /* state flags */
u_int32_t ic_caps; /* capabilities */
@@ -179,6 +113,16 @@ struct ieee80211com {
enum ieee80211_opmode ic_opmode; /* operation mode */
enum ieee80211_state ic_state; /* 802.11 state */
enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */
+ enum ieee80211_roamingmode ic_roaming; /* roaming mode */
+ struct ieee80211_node_table *ic_sta; /* stations/neighbors */
+ u_int32_t *ic_aid_bitmap; /* association id map */
+ u_int16_t ic_max_aid;
+ u_int16_t ic_sta_assoc; /* stations associated */
+ u_int16_t ic_ps_sta; /* stations in power save */
+ u_int16_t ic_ps_pending; /* ps sta's w/ pending frames */
+ u_int8_t *ic_tim_bitmap; /* power-save stations w/ data*/
+ u_int16_t ic_tim_len; /* ic_tim_bitmap size (bytes) */
+ u_int16_t ic_dtim_period; /* DTIM period */
struct ifmedia ic_media; /* interface media config */
struct bpf_if *ic_rawbpf; /* packet filter structure */
struct ieee80211_node *ic_bss; /* information for this node */
@@ -186,90 +130,134 @@ struct ieee80211com {
int ic_fixed_rate; /* index to ic_sup_rates[] */
u_int16_t ic_rtsthreshold;
u_int16_t ic_fragthreshold;
- struct mtx ic_nodelock; /* on node table */
- u_int ic_scangen; /* gen# for timeout scan */
- struct ieee80211_node *(*ic_node_alloc)(struct ieee80211com *);
- void (*ic_node_free)(struct ieee80211com *,
- struct ieee80211_node *);
- void (*ic_node_copy)(struct ieee80211com *,
- struct ieee80211_node *,
- const struct ieee80211_node *);
- u_int8_t (*ic_node_getrssi)(struct ieee80211com *,
- struct ieee80211_node *);
- TAILQ_HEAD(, ieee80211_node) ic_node; /* information of all nodes */
- LIST_HEAD(, ieee80211_node) ic_hash[IEEE80211_NODE_HASHSIZE];
+ struct ieee80211_node *(*ic_node_alloc)(struct ieee80211_node_table*);
+ void (*ic_node_free)(struct ieee80211_node *);
+ void (*ic_node_cleanup)(struct ieee80211_node *);
+ u_int8_t (*ic_node_getrssi)(const struct ieee80211_node*);
u_int16_t ic_lintval; /* listen interval */
u_int16_t ic_holdover; /* PM hold over duration */
u_int16_t ic_txmin; /* min tx retry count */
u_int16_t ic_txmax; /* max tx retry count */
u_int16_t ic_txlifetime; /* tx lifetime */
- u_int16_t ic_txpower; /* tx power setting (dbM) */
+ u_int16_t ic_txpowlimit; /* global tx power limit */
u_int16_t ic_bmisstimeout;/* beacon miss threshold (ms) */
+ u_int16_t ic_nonerpsta; /* # non-ERP stations */
+ u_int16_t ic_longslotsta; /* # long slot time stations */
int ic_mgt_timer; /* mgmt timeout */
int ic_inact_timer; /* inactivity timer wait */
int ic_des_esslen;
u_int8_t ic_des_essid[IEEE80211_NWID_LEN];
struct ieee80211_channel *ic_des_chan; /* desired channel */
u_int8_t ic_des_bssid[IEEE80211_ADDR_LEN];
- struct ieee80211_wepkey ic_nw_keys[IEEE80211_WEP_NKID];
- int ic_wep_txkey; /* default tx key index */
- void *ic_wep_ctx; /* wep crypt context */
- u_int32_t ic_iv; /* initial vector for wep */
- struct ieee80211_stats ic_stats; /* statistics */
+ void *ic_opt_ie; /* user-specified IE's */
+ u_int16_t ic_opt_ie_len; /* length of ni_opt_ie */
+ /*
+ * Inactivity timer settings for nodes.
+ */
+ 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 */
+
+ /*
+ * WME/WMM state.
+ */
+ struct ieee80211_wme_state ic_wme;
+
+ /*
+ * 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;
+
+ /*
+ * Access control glue. When a control agent attaches
+ * it fills in this section. We assume that when ic_ac
+ * is setup that the methods are safe to call.
+ */
+ const struct ieee80211_aclator *ic_acl;
+ void *ic_as;
};
-#define ic_if ic_ac.ac_if
-#define ic_softc ic_if.if_softc
#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 */
-#define IEEE80211_F_ASCAN 0x00000001 /* STATUS: active scan */
-#define IEEE80211_F_SIBSS 0x00000002 /* STATUS: start IBSS */
-#define IEEE80211_F_WEPON 0x00000100 /* CONF: WEP enabled */
-#define IEEE80211_F_IBSSON 0x00000200 /* CONF: IBSS creation enable */
-#define IEEE80211_F_PMGTON 0x00000400 /* CONF: Power mgmt enable */
-#define IEEE80211_F_DESBSSID 0x00000800 /* CONF: des_bssid is set */
-#define IEEE80211_F_SCANAP 0x00001000 /* CONF: Scanning AP */
-#define IEEE80211_F_ROAMING 0x00002000 /* CONF: roaming enabled */
-#define IEEE80211_F_SWRETRY 0x00004000 /* CONF: sw tx retry enabled */
-#define IEEE80211_F_TXPMGT 0x00018000 /* STATUS: tx power */
-#define IEEE80211_F_TXPOW_OFF 0x00000000 /* TX Power: radio disabled */
-#define IEEE80211_F_TXPOW_FIXED 0x00008000 /* TX Power: fixed rate */
-#define IEEE80211_F_TXPOW_AUTO 0x00010000 /* TX Power: undefined */
-#define IEEE80211_F_SHSLOT 0x00020000 /* CONF: short slot time */
-#define IEEE80211_F_SHPREAMBLE 0x00040000 /* CONF: short preamble */
+/* NB: bits 0x6f available */
+/* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */
+#define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */
+#define IEEE80211_F_SCAN 0x00000080 /* STATUS: scanning */
+#define IEEE80211_F_ASCAN 0x00000100 /* STATUS: active scan */
+#define IEEE80211_F_SIBSS 0x00000200 /* STATUS: start IBSS */
+/* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */
+#define IEEE80211_F_SHSLOT 0x00000400 /* STATUS: use short slot time*/
+#define IEEE80211_F_PMGTON 0x00000800 /* CONF: Power mgmt enable */
+#define IEEE80211_F_DESBSSID 0x00001000 /* CONF: des_bssid is set */
+#define IEEE80211_F_WME 0x00002000 /* CONF: enable WME use */
+#define IEEE80211_F_ROAMING 0x00004000 /* CONF: roaming enabled (???)*/
+#define IEEE80211_F_SWRETRY 0x00008000 /* CONF: sw tx retry enabled */
+#define IEEE80211_F_TXPOW_FIXED 0x00010000 /* TX Power: fixed rate */
+#define IEEE80211_F_IBSSON 0x00020000 /* CONF: IBSS creation enable */
+#define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: use short preamble */
+#define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */
#define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */
#define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/
+#define IEEE80211_F_TIMUPDATE 0x00400000 /* STATUS: update beacon tim */
+#define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */
+#define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */
+#define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */
+#define IEEE80211_F_DROPUNENC 0x02000000 /* CONF: drop unencrypted */
+#define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */
+#define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */
+#define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */
+#define IEEE80211_F_WMEUPDATE 0x20000000 /* STATUS: update beacon wme */
/* ic_caps */
#define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */
-#define IEEE80211_C_IBSS 0x00000002 /* CAPABILITY: IBSS available */
-#define IEEE80211_C_PMGT 0x00000004 /* CAPABILITY: Power mgmt */
-#define IEEE80211_C_HOSTAP 0x00000008 /* CAPABILITY: HOSTAP avail */
-#define IEEE80211_C_AHDEMO 0x00000010 /* CAPABILITY: Old Adhoc Demo */
-#define IEEE80211_C_SWRETRY 0x00000020 /* CAPABILITY: sw tx retry */
-#define IEEE80211_C_TXPMGT 0x00000040 /* CAPABILITY: tx power mgmt */
-#define IEEE80211_C_SHSLOT 0x00000080 /* CAPABILITY: short slottime */
-#define IEEE80211_C_SHPREAMBLE 0x00000100 /* CAPABILITY: short preamble */
-#define IEEE80211_C_MONITOR 0x00000200 /* CAPABILITY: monitor mode */
+#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 */
+#define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */
+#define IEEE80211_C_PMGT 0x00000200 /* CAPABILITY: Power mgmt */
+#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */
+#define IEEE80211_C_AHDEMO 0x00000800 /* CAPABILITY: Old Adhoc Demo */
+#define IEEE80211_C_SWRETRY 0x00001000 /* CAPABILITY: sw tx retry */
+#define IEEE80211_C_TXPMGT 0x00002000 /* CAPABILITY: tx power mgmt */
+#define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */
+#define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */
+#define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */
+#define IEEE80211_C_TKIPMIC 0x00020000 /* CAPABILITY: TKIP MIC avail */
+#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */
+#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */
+#define IEEE80211_C_WPA 0x01800000 /* CAPABILITY: WPA1+WPA2 avail*/
+#define IEEE80211_C_BURST 0x02000000 /* CAPABILITY: frame bursting */
+#define IEEE80211_C_WME 0x04000000 /* CAPABILITY: WME avail */
+/* XXX protection/barker? */
-/* flags for ieee80211_fix_rate() */
-#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */
-#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */
-#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */
-#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */
+#define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */
-void ieee80211_ifattach(struct ifnet *);
-void ieee80211_ifdetach(struct ifnet *);
-void ieee80211_media_init(struct ifnet *, ifm_change_cb_t, ifm_stat_cb_t);
+void ieee80211_ifattach(struct ieee80211com *);
+void ieee80211_ifdetach(struct ieee80211com *);
+void ieee80211_announce(struct ieee80211com *);
+void ieee80211_media_init(struct ieee80211com *,
+ ifm_change_cb_t, ifm_stat_cb_t);
+struct ieee80211com *ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]);
int ieee80211_media_change(struct ifnet *);
void ieee80211_media_status(struct ifnet *, struct ifmediareq *);
-int ieee80211_ioctl(struct ifnet *, u_long, caddr_t);
-int ieee80211_cfgget(struct ifnet *, u_long, caddr_t);
-int ieee80211_cfgset(struct ifnet *, u_long, caddr_t);
-void ieee80211_watchdog(struct ifnet *);
-int ieee80211_fix_rate(struct ieee80211com *, struct ieee80211_node *, int);
+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);
+void ieee80211_watchdog(struct ieee80211com *);
int ieee80211_rate2media(struct ieee80211com *, int,
enum ieee80211_phymode);
int ieee80211_media2rate(int);
@@ -280,14 +268,65 @@ int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode);
enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *,
struct ieee80211_channel *);
-#define IEEE80211_DEBUG
+/*
+ * Key update synchronization methods. XXX should not be visible.
+ */
+static __inline void
+ieee80211_key_update_begin(struct ieee80211com *ic)
+{
+ ic->ic_crypto.cs_key_update_begin(ic);
+}
+static __inline void
+ieee80211_key_update_end(struct ieee80211com *ic)
+{
+ ic->ic_crypto.cs_key_update_end(ic);
+}
+
+#define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */
+#define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */
+#define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */
+#define IEEE80211_MSG_INPUT 0x08000000 /* input handling */
+#define IEEE80211_MSG_XRATE 0x04000000 /* rate set handling */
+#define IEEE80211_MSG_ELEMID 0x02000000 /* element id parsing */
+#define IEEE80211_MSG_NODE 0x01000000 /* node handling */
+#define IEEE80211_MSG_ASSOC 0x00800000 /* association handling */
+#define IEEE80211_MSG_AUTH 0x00400000 /* authentication handling */
+#define IEEE80211_MSG_SCAN 0x00200000 /* scanning */
+#define IEEE80211_MSG_OUTPUT 0x00100000 /* output handling */
+#define IEEE80211_MSG_STATE 0x00080000 /* state machine */
+#define IEEE80211_MSG_POWER 0x00040000 /* power save handling */
+#define IEEE80211_MSG_DOT1X 0x00020000 /* 802.1x authenticator */
+#define IEEE80211_MSG_DOT1XSM 0x00010000 /* 802.1x state machine */
+#define IEEE80211_MSG_RADIUS 0x00008000 /* 802.1x radius client */
+#define IEEE80211_MSG_RADDUMP 0x00004000 /* dump 802.1x radius packets */
+#define IEEE80211_MSG_RADKEYS 0x00002000 /* dump 802.1x keys */
+#define IEEE80211_MSG_WPA 0x00001000 /* WPA/RSN protocol */
+#define IEEE80211_MSG_ACL 0x00000800 /* ACL handling */
+#define IEEE80211_MSG_WME 0x00000400 /* WME protocol */
+
+#define IEEE80211_MSG_ANY 0xffffffff /* anything */
+
#ifdef IEEE80211_DEBUG
-extern int ieee80211_debug;
-#define IEEE80211_DPRINTF(X) if (ieee80211_debug) printf X
-#define IEEE80211_DPRINTF2(X) if (ieee80211_debug>1) printf X
+#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...) do { \
+ if (_ic->ic_debug & (_m)) \
+ printf(_fmt, __VA_ARGS__); \
+} while (0)
+#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)
#else
-#define IEEE80211_DPRINTF(X)
-#define IEEE80211_DPRINTF2(X)
+#define IEEE80211_DPRINTF(_ic, _m, _fmt, ...)
#endif
#endif /* _NET80211_IEEE80211_VAR_H_ */
diff --git a/sys/net80211/ieee80211_xauth.c b/sys/net80211/ieee80211_xauth.c
new file mode 100644
index 0000000..d97b38d
--- /dev/null
+++ b/sys/net80211/ieee80211_xauth.c
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 2004 Video54 Technologies, Inc.
+ * 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 the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * External authenticator placeholder module.
+ *
+ * This support is optional; it is only used when the 802.11 layer's
+ * authentication mode is set to use 802.1x or WPA is enabled separately
+ * (for WPA-PSK). If compiled as a module this code does not need
+ * to be present unless 802.1x/WPA is in use.
+ *
+ * The authenticator hooks into the 802.11 layer. At present we use none
+ * of the available callbacks--the user mode authenticator process works
+ * entirely from messages about stations joining and leaving.
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/route.h>
+
+#include <net80211/ieee80211_var.h>
+
+/*
+ * One module handles everything for now. May want
+ * to split things up for embedded applications.
+ */
+static const struct ieee80211_authenticator xauth = {
+ .ia_name = "external",
+ .ia_attach = NULL,
+ .ia_detach = NULL,
+ .ia_node_join = NULL,
+ .ia_node_leave = NULL,
+};
+
+/*
+ * 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);
OpenPOWER on IntegriCloud