diff options
-rw-r--r-- | sys/dev/ath/if_ath.c | 124 | ||||
-rw-r--r-- | sys/dev/ath/if_athvar.h | 1 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto.c | 29 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto.h | 23 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto_tkip.c | 4 | ||||
-rw-r--r-- | sys/net80211/ieee80211_freebsd.c | 12 | ||||
-rw-r--r-- | sys/net80211/ieee80211_freebsd.h | 5 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.c | 2 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.c | 270 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.h | 11 |
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 *, |