summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/ath/if_ath.c124
-rw-r--r--sys/dev/ath/if_athvar.h1
-rw-r--r--sys/net80211/ieee80211_crypto.c29
-rw-r--r--sys/net80211/ieee80211_crypto.h23
-rw-r--r--sys/net80211/ieee80211_crypto_tkip.c4
-rw-r--r--sys/net80211/ieee80211_freebsd.c12
-rw-r--r--sys/net80211/ieee80211_freebsd.h5
-rw-r--r--sys/net80211/ieee80211_ioctl.c2
-rw-r--r--sys/net80211/ieee80211_node.c270
-rw-r--r--sys/net80211/ieee80211_node.h11
10 files changed, 324 insertions, 157 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index 7e91f30..ac99401 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -112,7 +112,8 @@ static void ath_fatal_proc(void *, int);
static void ath_rxorn_proc(void *, int);
static void ath_bmiss_proc(void *, int);
static int ath_key_alloc(struct ieee80211com *,
- const struct ieee80211_key *);
+ const struct ieee80211_key *,
+ ieee80211_keyix *, ieee80211_keyix *);
static int ath_key_delete(struct ieee80211com *,
const struct ieee80211_key *);
static int ath_key_set(struct ieee80211com *, const struct ieee80211_key *,
@@ -568,6 +569,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
ic->ic_recv_mgmt = ath_recv_mgmt;
sc->sc_newstate = ic->ic_newstate;
ic->ic_newstate = ath_newstate;
+ ic->ic_crypto.cs_max_keyix = sc->sc_keymax;
ic->ic_crypto.cs_key_alloc = ath_key_alloc;
ic->ic_crypto.cs_key_delete = ath_key_delete;
ic->ic_crypto.cs_key_set = ath_key_set;
@@ -1262,7 +1264,7 @@ ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k,
KASSERT(sc->sc_splitmic, ("key cache !split"));
if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
/*
- * TX key goes at first index, RX key at +32.
+ * TX key goes at first index, RX key at the rx index.
* The hal handles the MIC keys at index+64.
*/
memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic));
@@ -1357,7 +1359,8 @@ ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k,
* each key, one for decrypt/encrypt and the other for the MIC.
*/
static u_int16_t
-key_alloc_2pair(struct ath_softc *sc)
+key_alloc_2pair(struct ath_softc *sc,
+ ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
{
#define N(a) (sizeof(a)/sizeof(a[0]))
u_int i, keyix;
@@ -1396,19 +1399,22 @@ key_alloc_2pair(struct ath_softc *sc)
"%s: key pair %u,%u %u,%u\n",
__func__, keyix, keyix+64,
keyix+32, keyix+32+64);
- return keyix;
+ *txkeyix = keyix;
+ *rxkeyix = keyix+32;
+ return 1;
}
}
DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__);
- return IEEE80211_KEYIX_NONE;
+ return 0;
#undef N
}
/*
* Allocate a single key cache slot.
*/
-static u_int16_t
-key_alloc_single(struct ath_softc *sc)
+static int
+key_alloc_single(struct ath_softc *sc,
+ ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
{
#define N(a) (sizeof(a)/sizeof(a[0]))
u_int i, keyix;
@@ -1426,11 +1432,12 @@ key_alloc_single(struct ath_softc *sc)
setbit(sc->sc_keymap, keyix);
DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: key %u\n",
__func__, keyix);
- return keyix;
+ *txkeyix = *rxkeyix = keyix;
+ return 1;
}
}
DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__);
- return IEEE80211_KEYIX_NONE;
+ return 0;
#undef N
}
@@ -1444,7 +1451,8 @@ key_alloc_single(struct ath_softc *sc)
* 64 entries.
*/
static int
-ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k)
+ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
+ ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
struct ath_softc *sc = ic->ic_ifp->if_softc;
@@ -1460,21 +1468,19 @@ ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k)
* multi-station operation.
*/
if ((k->wk_flags & IEEE80211_KEY_GROUP) && !sc->sc_mcastkey) {
- u_int keyix;
-
if (!(&ic->ic_nw_keys[0] <= k &&
k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
/* should not happen */
DPRINTF(sc, ATH_DEBUG_KEYCACHE,
"%s: bogus group key\n", __func__);
- return IEEE80211_KEYIX_NONE;
+ return 0;
}
- keyix = k - ic->ic_nw_keys;
/*
* XXX we pre-allocate the global keys so
* have no way to check if they've already been allocated.
*/
- return keyix;
+ *keyix = *rxkeyix = k - ic->ic_nw_keys;
+ return 1;
}
/*
@@ -1486,12 +1492,12 @@ ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k)
* those requests to slot 0.
*/
if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
- return key_alloc_single(sc);
+ return key_alloc_single(sc, keyix, rxkeyix);
} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
(k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) {
- return key_alloc_2pair(sc);
+ return key_alloc_2pair(sc, keyix, rxkeyix);
} else {
- return key_alloc_single(sc);
+ return key_alloc_single(sc, keyix, rxkeyix);
}
}
@@ -1504,32 +1510,17 @@ ath_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
struct ath_softc *sc = ic->ic_ifp->if_softc;
struct ath_hal *ah = sc->sc_ah;
const struct ieee80211_cipher *cip = k->wk_cipher;
- struct ieee80211_node *ni;
u_int keyix = k->wk_keyix;
DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix);
ath_hal_keyreset(ah, keyix);
/*
- * Check the key->node map and flush any ref.
- */
- ni = sc->sc_keyixmap[keyix];
- if (ni != NULL) {
- ieee80211_free_node(ni);
- sc->sc_keyixmap[keyix] = NULL;
- }
- /*
* Handle split tx/rx keying required for TKIP with h/w MIC.
*/
if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
- (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) {
+ (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic)
ath_hal_keyreset(ah, keyix+32); /* RX key */
- ni = sc->sc_keyixmap[keyix+32];
- if (ni != NULL) { /* as above... */
- ieee80211_free_node(ni);
- sc->sc_keyixmap[keyix+32] = NULL;
- }
- }
if (keyix >= IEEE80211_WEP_NKID) {
/*
* Don't touch keymap entries for global keys so
@@ -2821,50 +2812,22 @@ rx_accept:
/*
* Locate the node for sender, track state, and then
* pass the (referenced) node up to the 802.11 layer
- * for its use. If the sender is unknown spam the
- * frame; it'll be dropped where it's not wanted.
+ * for its use.
*/
- if (ds->ds_rxstat.rs_keyix != HAL_RXKEYIX_INVALID &&
- (ni = sc->sc_keyixmap[ds->ds_rxstat.rs_keyix]) != NULL) {
- /*
- * Fast path: node is present in the key map;
- * grab a reference for processing the frame.
- */
- an = ATH_NODE(ieee80211_ref_node(ni));
- ATH_RSSI_LPF(an->an_avgrssi, ds->ds_rxstat.rs_rssi);
- type = ieee80211_input(ic, m, ni,
- ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp);
- } else {
- /*
- * Locate the node for sender, track state, and then
- * pass the (referenced) node up to the 802.11 layer
- * for its use.
- */
- ni = ieee80211_find_rxnode(ic,
- mtod(m, const struct ieee80211_frame_min *));
- /*
- * Track rx rssi and do any rx antenna management.
- */
- an = ATH_NODE(ni);
- ATH_RSSI_LPF(an->an_avgrssi, ds->ds_rxstat.rs_rssi);
- /*
- * Send frame up for processing.
- */
- type = ieee80211_input(ic, m, ni,
- ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp);
- if (ni != ic->ic_bss) {
- u_int16_t keyix;
- /*
- * If the station has a key cache slot assigned
- * update the key->node mapping table.
- */
- keyix = ni->ni_ucastkey.wk_keyix;
- if (keyix != IEEE80211_KEYIX_NONE &&
- sc->sc_keyixmap[keyix] == NULL)
- sc->sc_keyixmap[keyix] =
- ieee80211_ref_node(ni);
- }
- }
+ ni = ieee80211_find_rxnode_withkey(ic,
+ mtod(m, const struct ieee80211_frame_min *),
+ ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID ?
+ IEEE80211_KEYIX_NONE : ds->ds_rxstat.rs_keyix);
+ /*
+ * Track rx rssi and do any rx antenna management.
+ */
+ an = ATH_NODE(ni);
+ ATH_RSSI_LPF(an->an_avgrssi, ds->ds_rxstat.rs_rssi);
+ /*
+ * Send frame up for processing.
+ */
+ type = ieee80211_input(ic, m, ni,
+ ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp);
ieee80211_free_node(ni);
if (sc->sc_diversity) {
/*
@@ -4265,10 +4228,9 @@ ath_setup_stationkey(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
struct ath_softc *sc = ic->ic_ifp->if_softc;
- u_int16_t keyix;
+ ieee80211_keyix keyix, rxkeyix;
- keyix = ath_key_alloc(ic, &ni->ni_ucastkey);
- if (keyix == IEEE80211_KEYIX_NONE) {
+ if (!ath_key_alloc(ic, &ni->ni_ucastkey, &keyix, &rxkeyix)) {
/*
* Key cache is full; we'll fall back to doing
* the more expensive lookup in software. Note
@@ -4276,7 +4238,9 @@ ath_setup_stationkey(struct ieee80211_node *ni)
*/
/* XXX msg+statistic */
} else {
+ /* XXX locking? */
ni->ni_ucastkey.wk_keyix = keyix;
+ ni->ni_ucastkey.wk_rxkeyix = rxkeyix;
/* NB: this will create a pass-thru key entry */
ath_keyset(sc, &ni->ni_ucastkey, ni->ni_macaddr, ic->ic_bss);
}
diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h
index b6e0aaf..21c319e 100644
--- a/sys/dev/ath/if_athvar.h
+++ b/sys/dev/ath/if_athvar.h
@@ -224,7 +224,6 @@ struct ath_softc {
HAL_INT sc_imask; /* interrupt mask copy */
u_int sc_keymax; /* size of key cache */
u_int8_t sc_keymap[ATH_KEYBYTES];/* key use bit map */
- struct ieee80211_node *sc_keyixmap[ATH_KEYMAX];/* key ix->node map */
u_int sc_ledpin; /* GPIO pin for driving LED */
u_int sc_ledon; /* pin setting for LED on */
diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c
index 1771ea1..e9ce135 100644
--- a/sys/net80211/ieee80211_crypto.c
+++ b/sys/net80211/ieee80211_crypto.c
@@ -59,7 +59,8 @@ static int _ieee80211_crypto_delkey(struct ieee80211com *,
* Default "null" key management routines.
*/
static int
-null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k)
+null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
+ ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
if (!(&ic->ic_nw_keys[0] <= k &&
k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
@@ -73,12 +74,14 @@ null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k)
* packets through untouched when marked with the WEP bit
* and key index 0.
*/
- if ((k->wk_flags & IEEE80211_KEY_GROUP) == 0)
- return 0; /* NB: use key index 0 for ucast key */
- else
- return IEEE80211_KEYIX_NONE;
+ if (k->wk_flags & IEEE80211_KEY_GROUP)
+ return 0;
+ *keyix = 0; /* NB: use key index 0 for ucast key */
+ } else {
+ *keyix = k - ic->ic_nw_keys;
}
- return k - ic->ic_nw_keys;
+ *rxkeyix = IEEE80211_KEYIX_NONE; /* XXX maybe *keyix? */
+ return 1;
}
static int
null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
@@ -113,9 +116,10 @@ cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key)
*/
static __inline int
dev_key_alloc(struct ieee80211com *ic,
- const struct ieee80211_key *key)
+ const struct ieee80211_key *key,
+ ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
- return ic->ic_crypto.cs_key_alloc(ic, key);
+ return ic->ic_crypto.cs_key_alloc(ic, key, keyix, rxkeyix);
}
static __inline int
@@ -143,6 +147,7 @@ ieee80211_crypto_attach(struct ieee80211com *ic)
/* NB: we assume everything is pre-zero'd */
cs->cs_def_txkey = IEEE80211_KEYIX_NONE;
+ cs->cs_max_keyix = IEEE80211_WEP_NKID;
ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
for (i = 0; i < IEEE80211_WEP_NKID; i++)
ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i],
@@ -241,6 +246,7 @@ ieee80211_crypto_newkey(struct ieee80211com *ic,
{
#define N(a) (sizeof(a) / sizeof(a[0]))
const struct ieee80211_cipher *cip;
+ ieee80211_keyix keyix, rxkeyix;
void *keyctx;
int oflags;
@@ -354,8 +360,7 @@ again:
* 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) {
+ if (!dev_key_alloc(ic, key, &keyix, &rxkeyix)) {
/*
* Driver has no room; fallback to doing crypto
* in the host. We change the flags and start the
@@ -382,6 +387,8 @@ again:
__func__, cip->ic_name);
return 0;
}
+ key->wk_keyix = keyix;
+ key->wk_rxkeyix = rxkeyix;
}
return 1;
#undef N
@@ -393,7 +400,7 @@ again:
static int
_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
{
- u_int16_t keyix;
+ ieee80211_keyix keyix;
KASSERT(key->wk_cipher != NULL, ("No cipher!"));
diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h
index 19b6dcd..f006a7a 100644
--- a/sys/net80211/ieee80211_crypto.h
+++ b/sys/net80211/ieee80211_crypto.h
@@ -66,18 +66,19 @@ struct ieee80211_cipher;
* 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 */
+typedef u_int16_t ieee80211_keyix; /* h/w key index */
+
struct ieee80211_key {
u_int8_t wk_keylen; /* key length in bytes */
- u_int8_t wk_flags;
+ u_int8_t wk_pad;
+ u_int16_t wk_flags;
#define IEEE80211_KEY_XMIT 0x01 /* key used for xmit */
#define IEEE80211_KEY_RECV 0x02 /* key used for recv */
#define IEEE80211_KEY_GROUP 0x04 /* key used for WPA group operation */
#define IEEE80211_KEY_SWCRYPT 0x10 /* host-based encrypt/decrypt */
#define IEEE80211_KEY_SWMIC 0x20 /* host-based enmic/demic */
- u_int16_t wk_keyix; /* key index */
+ ieee80211_keyix wk_keyix; /* h/w key index */
+ ieee80211_keyix wk_rxkeyix; /* optional h/w rx 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 */
@@ -103,7 +104,7 @@ struct ieee80211_key {
#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1)
-#define IEEE80211_KEYIX_NONE ((u_int16_t) -1)
+#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1)
#if defined(__KERNEL__) || defined(_KERNEL)
@@ -120,10 +121,12 @@ struct mbuf;
*/
struct ieee80211_crypto_state {
struct ieee80211_key cs_nw_keys[IEEE80211_WEP_NKID];
- u_int16_t cs_def_txkey; /* default/group tx key index */
+ ieee80211_keyix cs_def_txkey; /* default/group tx key index */
+ u_int16_t cs_max_keyix; /* max h/w key index */
int (*cs_key_alloc)(struct ieee80211com *,
- const struct ieee80211_key *);
+ const struct ieee80211_key *,
+ ieee80211_keyix *, ieee80211_keyix *);
int (*cs_key_delete)(struct ieee80211com *,
const struct ieee80211_key *);
int (*cs_key_set)(struct ieee80211com *,
@@ -204,11 +207,11 @@ ieee80211_crypto_enmic(struct ieee80211com *ic,
*/
static __inline void
ieee80211_crypto_resetkey(struct ieee80211com *ic,
- struct ieee80211_key *k, u_int16_t ix)
+ struct ieee80211_key *k, ieee80211_keyix ix)
{
k->wk_cipher = &ieee80211_cipher_none;;
k->wk_private = k->wk_cipher->ic_attach(ic, k);
- k->wk_keyix = ix;
+ k->wk_keyix = k->wk_rxkeyix = ix;
k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
}
diff --git a/sys/net80211/ieee80211_crypto_tkip.c b/sys/net80211/ieee80211_crypto_tkip.c
index c4ab09f..6775ee7 100644
--- a/sys/net80211/ieee80211_crypto_tkip.c
+++ b/sys/net80211/ieee80211_crypto_tkip.c
@@ -339,7 +339,9 @@ tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
tkip.ic_miclen, mic0);
if (memcmp(mic, mic0, tkip.ic_miclen)) {
/* NB: 802.11 layer handles statistic and debug msg */
- ieee80211_notify_michael_failure(ic, wh, k->wk_keyix);
+ ieee80211_notify_michael_failure(ic, wh,
+ k->wk_rxkeyix != IEEE80211_KEYIX_NONE ?
+ k->wk_rxkeyix : k->wk_keyix);
return 0;
}
}
diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c
index 90231bb..dd37250 100644
--- a/sys/net80211/ieee80211_freebsd.c
+++ b/sys/net80211/ieee80211_freebsd.c
@@ -261,9 +261,10 @@ ieee80211_notify_replay_failure(struct ieee80211com *ic,
struct ifnet *ifp = ic->ic_ifp;
IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
- "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u>\n",
- ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
- (intmax_t) rsc, (intmax_t) k->wk_keyrsc, k->wk_keyix);
+ "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>\n",
+ ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
+ (intmax_t) rsc, (intmax_t) k->wk_keyrsc,
+ k->wk_keyix, k->wk_rxkeyix);
if (ifp != NULL) { /* NB: for cipher test modules */
struct ieee80211_replay_event iev;
@@ -271,7 +272,10 @@ ieee80211_notify_replay_failure(struct ieee80211com *ic,
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;
+ if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE)
+ iev.iev_keyix = k->wk_rxkeyix;
+ else
+ iev.iev_keyix = k->wk_keyix;
iev.iev_keyrsc = k->wk_keyrsc;
iev.iev_rsc = rsc;
rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h
index 17483b6..70a40c1 100644
--- a/sys/net80211/ieee80211_freebsd.h
+++ b/sys/net80211/ieee80211_freebsd.h
@@ -43,12 +43,15 @@ typedef struct mtx ieee80211_beacon_lock_t;
/*
* Node locking definitions.
+ * NB: MTX_DUPOK is because we don't generate per-interface strings.
*/
typedef struct mtx ieee80211_node_lock_t;
#define IEEE80211_NODE_LOCK_INIT(_nt, _name) \
- mtx_init(&(_nt)->nt_nodelock, _name, "802.11 node table", MTX_DEF)
+ mtx_init(&(_nt)->nt_nodelock, _name, "802.11 node table", \
+ MTX_DEF | MTX_DUPOK)
#define IEEE80211_NODE_LOCK_DESTROY(_nt) mtx_destroy(&(_nt)->nt_nodelock)
#define IEEE80211_NODE_LOCK(_nt) mtx_lock(&(_nt)->nt_nodelock)
+#define IEEE80211_NODE_IS_LOCKED(_nt) mtx_owned(&(_nt)->nt_nodelock)
#define IEEE80211_NODE_UNLOCK(_nt) mtx_unlock(&(_nt)->nt_nodelock)
#define IEEE80211_NODE_LOCK_ASSERT(_nt) \
mtx_assert(&(_nt)->nt_nodelock, MA_OWNED)
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 4fd452e..0bb2631 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -1630,7 +1630,7 @@ ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
return ENOENT;
}
/* XXX error return */
- ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+ ieee80211_node_delucastkey(ni);
ieee80211_free_node(ni);
} else {
if (kid >= IEEE80211_WEP_NKID)
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index ff8cfcd..af7c1fc 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -75,7 +75,8 @@ static void ieee80211_timeout_stations(struct ieee80211_node_table *);
static void ieee80211_set_tim(struct ieee80211_node *, int set);
static void ieee80211_node_table_init(struct ieee80211com *ic,
- struct ieee80211_node_table *nt, const char *name, int inact,
+ struct ieee80211_node_table *nt, const char *name,
+ int inact, int keyixmax,
void (*timeout)(struct ieee80211_node_table *));
static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
@@ -85,11 +86,6 @@ void
ieee80211_node_attach(struct ieee80211com *ic)
{
- ieee80211_node_table_init(ic, &ic->ic_sta, "station",
- IEEE80211_INACT_INIT, ieee80211_timeout_stations);
- 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;
@@ -101,10 +97,17 @@ ieee80211_node_attach(struct ieee80211com *ic)
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)
+ /* NB: driver should override */
+ ic->ic_max_aid = IEEE80211_AID_DEF;
+ ic->ic_set_tim = ieee80211_set_tim;
+}
+
+void
+ieee80211_node_lateattach(struct ieee80211com *ic)
+{
+ struct ieee80211_rsnparms *rsn;
+
+ if (ic->ic_max_aid > IEEE80211_AID_MAX)
ic->ic_max_aid = IEEE80211_AID_MAX;
MALLOC(ic->ic_aid_bitmap, u_int32_t *,
howmany(ic->ic_max_aid, 32) * sizeof(u_int32_t),
@@ -123,22 +126,20 @@ ieee80211_node_attach(struct ieee80211com *ic)
/* 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 ieee80211com *ic)
-{
- struct ieee80211_node *ni;
- struct ieee80211_rsnparms *rsn;
+ ieee80211_node_table_init(ic, &ic->ic_sta, "station",
+ IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix,
+ ieee80211_timeout_stations);
+ ieee80211_node_table_init(ic, &ic->ic_scan, "scan",
+ IEEE80211_INACT_SCAN, 0,
+ ieee80211_timeout_scan_candidates);
- ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr);
- KASSERT(ni != NULL, ("unable to setup inital BSS node"));
+ ieee80211_reset_bss(ic);
/*
* Setup "global settings" in the bss node so that
* each new station automatically inherits them.
*/
- rsn = &ni->ni_rsn;
+ rsn = &ic->ic_bss->ni_rsn;
/* WEP, TKIP, and AES-CCM are always supported */
rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_WEP;
rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_TKIP;
@@ -170,8 +171,7 @@ ieee80211_node_lateattach(struct ieee80211com *ic)
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);
+ ic->ic_auth = ieee80211_authenticator_get(ic->ic_bss->ni_authmode);
}
void
@@ -380,7 +380,7 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
}
IEEE80211_NODE_UNLOCK(nt);
- ni = ieee80211_alloc_node(nt, ic->ic_myaddr);
+ ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr);
if (ni == NULL) {
/* XXX recovery? */
return;
@@ -879,7 +879,10 @@ node_cleanup(struct ieee80211_node *ni)
m_freem(ni->ni_rxfrag[i]);
ni->ni_rxfrag[i] = NULL;
}
- ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+ /*
+ * Must be careful here to remove any key map entry w/o a LOR.
+ */
+ ieee80211_node_delucastkey(ni);
#undef N
}
@@ -1082,6 +1085,10 @@ ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
return ni;
}
+#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)
/*
* Locate the node for sender, track state, and then pass the
* (referenced) node up to the 802.11 layer for its use. We
@@ -1099,10 +1106,6 @@ 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;
@@ -1120,12 +1123,77 @@ ieee80211_find_rxnode(struct ieee80211com *ic,
ni = _ieee80211_find_node(nt, wh->i_addr1);
else
ni = _ieee80211_find_node(nt, wh->i_addr2);
+ if (ni == NULL)
+ ni = ieee80211_ref_node(ic->ic_bss);
IEEE80211_NODE_UNLOCK(nt);
- return (ni != NULL ? ni : ieee80211_ref_node(ic->ic_bss));
+ return ni;
+}
+
+/*
+ * Like ieee80211_find_rxnode but use the supplied h/w
+ * key index as a hint to locate the node in the key
+ * mapping table. If an entry is present at the key
+ * index we return it; otherwise do a normal lookup and
+ * update the mapping table if the station has a unicast
+ * key assigned to it.
+ */
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_rxnode_withkey_debug(struct ieee80211com *ic,
+ const struct ieee80211_frame_min *wh, ieee80211_keyix keyix,
+ const char *func, int line)
+#else
+ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
+ const struct ieee80211_frame_min *wh, ieee80211_keyix keyix)
+#endif
+{
+ struct ieee80211_node_table *nt;
+ struct ieee80211_node *ni;
+
+ 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;
+ IEEE80211_NODE_LOCK(nt);
+ if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax)
+ ni = nt->nt_keyixmap[keyix];
+ else
+ ni = NULL;
+ if (ni == NULL) {
+ 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);
+ if (ni == NULL)
+ ni = ieee80211_ref_node(ic->ic_bss);
+ if (nt->nt_keyixmap != NULL) {
+ /*
+ * If the station has a unicast key cache slot
+ * assigned update the key->node mapping table.
+ */
+ keyix = ni->ni_ucastkey.wk_rxkeyix;
+ /* XXX can keyixmap[keyix] != NULL? */
+ if (keyix < nt->nt_keyixmax &&
+ nt->nt_keyixmap[keyix] == NULL) {
+ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ "%s: add key map entry %p<%s> refcnt %d\n",
+ __func__, ni, ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni)+1);
+ nt->nt_keyixmap[keyix] = ieee80211_ref_node(ni);
+ }
+ }
+ } else {
+ ieee80211_ref_node(ni);
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+
+ return ni;
+}
#undef IS_PSPOLL
#undef IS_CTL
-}
/*
* Return a reference to the appropriate node for sending
@@ -1144,15 +1212,16 @@ ieee80211_find_txnode(struct ieee80211com *ic, const u_int8_t *macaddr)
/*
* The destination address should be in the node table
- * unless we are operating in station mode or this is a
- * multicast/broadcast frame.
+ * unless this is a multicast/broadcast frame. We can
+ * also optimize station mode operation, all frames go
+ * to the bss node.
*/
- if (ic->ic_opmode == IEEE80211_M_STA || 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(nt);
- ni = _ieee80211_find_node(nt, macaddr);
+ if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
+ ni = ieee80211_ref_node(ic->ic_bss);
+ else
+ ni = _ieee80211_find_node(nt, macaddr);
IEEE80211_NODE_UNLOCK(nt);
if (ni == NULL) {
@@ -1302,23 +1371,87 @@ ieee80211_free_node(struct ieee80211_node *ni)
"%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni,
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);
+ if (nt != NULL) {
+ IEEE80211_NODE_LOCK(nt);
+ if (ieee80211_node_dectestref(ni)) {
+ /*
+ * Last reference, reclaim state.
+ */
_ieee80211_free_node(ni);
- IEEE80211_NODE_UNLOCK(nt);
- } else
+ } else if (ieee80211_node_refcnt(ni) == 1 &&
+ nt->nt_keyixmap != NULL) {
+ ieee80211_keyix keyix;
+ /*
+ * Check for a last reference in the key mapping table.
+ */
+ keyix = ni->ni_ucastkey.wk_rxkeyix;
+ if (keyix < nt->nt_keyixmax &&
+ nt->nt_keyixmap[keyix] == ni) {
+ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ "%s: %p<%s> clear key map entry", __func__,
+ ni, ether_sprintf(ni->ni_macaddr));
+ nt->nt_keyixmap[keyix] = NULL;
+ ieee80211_node_decref(ni); /* XXX needed? */
+ _ieee80211_free_node(ni);
+ }
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+ } else {
+ if (ieee80211_node_dectestref(ni))
_ieee80211_free_node(ni);
}
}
/*
+ * Reclaim a unicast key and clear any key cache state.
+ */
+int
+ieee80211_node_delucastkey(struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_node *nikey;
+ ieee80211_keyix keyix;
+ int isowned, status;
+
+ /*
+ * NB: We must beware of LOR here; deleting the key
+ * can cause the crypto layer to block traffic updates
+ * which can generate a LOR against the node table lock;
+ * grab it here and stash the key index for our use below.
+ *
+ * Must also beware of recursion on the node table lock.
+ * When called from node_cleanup we may already have
+ * the node table lock held. Unfortunately there's no
+ * way to separate out this path so we must do this
+ * conditionally.
+ */
+ isowned = IEEE80211_NODE_IS_LOCKED(nt);
+ if (!isowned)
+ IEEE80211_NODE_LOCK(nt);
+ keyix = ni->ni_ucastkey.wk_rxkeyix;
+ status = ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+ if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) {
+ nikey = nt->nt_keyixmap[keyix];
+ nt->nt_keyixmap[keyix] = NULL;;
+ } else
+ nikey = NULL;
+ if (!isowned)
+ IEEE80211_NODE_UNLOCK(&ic->ic_sta);
+
+ if (nikey != NULL) {
+ KASSERT(nikey == ni,
+ ("key map out of sync, ni %p nikey %p", ni, nikey));
+ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ "%s: delete key map entry %p<%s> refcnt %d\n",
+ __func__, ni, ether_sprintf(ni->ni_macaddr),
+ ieee80211_node_refcnt(ni)-1);
+ ieee80211_free_node(ni);
+ }
+ return status;
+}
+
+/*
* Reclaim a node. If this is the last reference count then
* do the normal free work. Otherwise remove it from the node
* table and mark it gone by clearing the back-reference.
@@ -1326,11 +1459,30 @@ ieee80211_free_node(struct ieee80211_node *ni)
static void
node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
{
+ ieee80211_keyix keyix;
+
+ IEEE80211_NODE_LOCK_ASSERT(nt);
IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
"%s: remove %p<%s> from %s table, refcnt %d\n",
__func__, ni, ether_sprintf(ni->ni_macaddr),
nt->nt_name, ieee80211_node_refcnt(ni)-1);
+ /*
+ * Clear any entry in the unicast key mapping table.
+ * We need to do it here so rx lookups don't find it
+ * in the mapping table even if it's not in the hash
+ * table. We cannot depend on the mapping table entry
+ * being cleared because the node may not be free'd.
+ */
+ keyix = ni->ni_ucastkey.wk_rxkeyix;
+ if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax &&
+ nt->nt_keyixmap[keyix] == ni) {
+ IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+ "%s: %p<%s> clear key map entry\n",
+ __func__, ni, ether_sprintf(ni->ni_macaddr));
+ nt->nt_keyixmap[keyix] = NULL;
+ ieee80211_node_decref(ni); /* NB: don't need free */
+ }
if (!ieee80211_node_dectestref(ni)) {
/*
* Other references are present, just remove the
@@ -1934,7 +2086,7 @@ ieee80211_set_tim(struct ieee80211_node *ni, int set)
static void
ieee80211_node_table_init(struct ieee80211com *ic,
struct ieee80211_node_table *nt,
- const char *name, int inact,
+ const char *name, int inact, int keyixmax,
void (*timeout)(struct ieee80211_node_table *))
{
@@ -1950,6 +2102,17 @@ ieee80211_node_table_init(struct ieee80211com *ic,
nt->nt_scangen = 1;
nt->nt_inact_init = inact;
nt->nt_timeout = timeout;
+ nt->nt_keyixmax = keyixmax;
+ if (nt->nt_keyixmax > 0) {
+ MALLOC(nt->nt_keyixmap, struct ieee80211_node **,
+ keyixmax * sizeof(struct ieee80211_node *),
+ M_80211_NODE, M_NOWAIT | M_ZERO);
+ if (nt->nt_keyixmap == NULL)
+ if_printf(ic->ic_ifp,
+ "Cannot allocate key index map with %u entries\n",
+ keyixmax);
+ } else
+ nt->nt_keyixmap = NULL;
}
void
@@ -1972,7 +2135,18 @@ ieee80211_node_table_cleanup(struct ieee80211_node_table *nt)
IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
"%s %s table\n", __func__, nt->nt_name);
+ IEEE80211_NODE_LOCK(nt);
ieee80211_free_allnodes_locked(nt);
+ if (nt->nt_keyixmap != NULL) {
+ /* XXX verify all entries are NULL */
+ int i;
+ for (i = 0; i < nt->nt_keyixmax; i++)
+ if (nt->nt_keyixmap[i] != NULL)
+ printf("%s: %s[%u] still active\n", __func__,
+ nt->nt_name, i);
+ FREE(nt->nt_keyixmap, M_80211_NODE);
+ nt->nt_keyixmap = NULL;
+ }
IEEE80211_SCAN_LOCK_DESTROY(nt);
IEEE80211_NODE_LOCK_DESTROY(nt);
}
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index c2941c4..6b3934e 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -211,6 +211,8 @@ struct ieee80211_node_table {
u_int nt_scangen; /* gen# for timeout scan */
int nt_inact_timer; /* inactivity timer */
int nt_inact_init; /* initial node inact setting */
+ struct ieee80211_node **nt_keyixmap; /* key ix -> node map */
+ int nt_keyixmax; /* keyixmap size */
void (*nt_timeout)(struct ieee80211_node_table *);
};
@@ -231,6 +233,10 @@ struct ieee80211_node *ieee80211_find_node_debug(
struct ieee80211_node * ieee80211_find_rxnode_debug(
struct ieee80211com *, const struct ieee80211_frame_min *,
const char *func, int line);
+struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
+ struct ieee80211com *,
+ const struct ieee80211_frame_min *, u_int16_t keyix,
+ const char *func, int line);
struct ieee80211_node *ieee80211_find_txnode_debug(
struct ieee80211com *, const u_int8_t *,
const char *func, int line);
@@ -247,6 +253,8 @@ struct ieee80211_node *ieee80211_find_node_with_ssid_debug(
ieee80211_find_node_debug(nt, mac, __func__, __LINE__)
#define ieee80211_find_rxnode(nt, wh) \
ieee80211_find_rxnode_debug(nt, wh, __func__, __LINE__)
+#define ieee80211_find_rxnode_withkey(nt, wh, keyix) \
+ ieee80211_find_rxnode_withkey_debug(nt, wh, keyix, __func__, __LINE__)
#define ieee80211_find_txnode(nt, mac) \
ieee80211_find_txnode_debug(nt, mac, __func__, __LINE__)
#define ieee80211_find_node_with_channel(nt, mac, c) \
@@ -259,6 +267,8 @@ struct ieee80211_node *ieee80211_find_node(
struct ieee80211_node_table *, const u_int8_t *);
struct ieee80211_node * ieee80211_find_rxnode(
struct ieee80211com *, const struct ieee80211_frame_min *);
+struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *,
+ const struct ieee80211_frame_min *, u_int16_t keyix);
struct ieee80211_node *ieee80211_find_txnode(
struct ieee80211com *, const u_int8_t *);
struct ieee80211_node *ieee80211_find_node_with_channel(
@@ -268,6 +278,7 @@ 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
+int ieee80211_node_delucastkey(struct ieee80211_node *);
typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
void ieee80211_iterate_nodes(struct ieee80211_node_table *,
OpenPOWER on IntegriCloud