diff options
author | adrian <adrian@FreeBSD.org> | 2011-03-02 17:19:54 +0000 |
---|---|---|
committer | adrian <adrian@FreeBSD.org> | 2011-03-02 17:19:54 +0000 |
commit | dad2200ab2ea4ee7a23fdb24cf98f477bf31e70c (patch) | |
tree | 8d935a7e40f8e8a60e212d2b74950c3fa0d08e85 /sys/dev/ath/if_ath_keycache.c | |
parent | 9791856c179074baee547d95afd0c7b32ea1d064 (diff) | |
download | FreeBSD-src-dad2200ab2ea4ee7a23fdb24cf98f477bf31e70c.zip FreeBSD-src-dad2200ab2ea4ee7a23fdb24cf98f477bf31e70c.tar.gz |
Break the keycache management functions out into if_ath_keycache.c .
Diffstat (limited to 'sys/dev/ath/if_ath_keycache.c')
-rw-r--r-- | sys/dev/ath/if_ath_keycache.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/sys/dev/ath/if_ath_keycache.c b/sys/dev/ath/if_ath_keycache.c new file mode 100644 index 0000000..842f766 --- /dev/null +++ b/sys/dev/ath/if_ath_keycache.c @@ -0,0 +1,497 @@ +/*- + * Copyright (c) 2002-2009 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Driver for the Atheros Wireless LAN controller. + * + * This software is derived from work of Atsushi Onoe; his contribution + * is greatly appreciated. + */ + +#include "opt_inet.h" +#include "opt_ath.h" +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sysctl.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/errno.h> +#include <sys/callout.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/kthread.h> +#include <sys/taskqueue.h> +#include <sys/priv.h> + +#include <machine/bus.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_llc.h> + +#include <net80211/ieee80211_var.h> + +#include <net/bpf.h> + +#include <dev/ath/if_athvar.h> + +#include <dev/ath/if_ath_debug.h> +#include <dev/ath/if_ath_keycache.h> + +#ifdef ATH_DEBUG +static void +ath_keyprint(struct ath_softc *sc, const char *tag, u_int ix, + const HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + static const char *ciphers[] = { + "WEP", + "AES-OCB", + "AES-CCM", + "CKIP", + "TKIP", + "CLR", + }; + int i, n; + + printf("%s: [%02u] %-7s ", tag, ix, ciphers[hk->kv_type]); + for (i = 0, n = hk->kv_len; i < n; i++) + printf("%02x", hk->kv_val[i]); + printf(" mac %s", ether_sprintf(mac)); + if (hk->kv_type == HAL_CIPHER_TKIP) { + printf(" %s ", sc->sc_splitmic ? "mic" : "rxmic"); + for (i = 0; i < sizeof(hk->kv_mic); i++) + printf("%02x", hk->kv_mic[i]); + if (!sc->sc_splitmic) { + printf(" txmic "); + for (i = 0; i < sizeof(hk->kv_txmic); i++) + printf("%02x", hk->kv_txmic[i]); + } + } + printf("\n"); +} +#endif + +/* + * Set a TKIP key into the hardware. This handles the + * potential distribution of key state to multiple key + * cache slots for TKIP. + */ +static int +ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k, + HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ +#define IEEE80211_KEY_XR (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV) + static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; + struct ath_hal *ah = sc->sc_ah; + + KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP, + ("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher)); + if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) { + if (sc->sc_splitmic) { + /* + * 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)); + KEYPRINTF(sc, k->wk_keyix, hk, zerobssid); + if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid)) + return 0; + + memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); + KEYPRINTF(sc, k->wk_keyix+32, hk, mac); + /* XXX delete tx key on failure? */ + return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac); + } else { + /* + * Room for both TX+RX MIC keys in one key cache + * slot, just set key at the first index; the hal + * will handle the rest. + */ + memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic)); + KEYPRINTF(sc, k->wk_keyix, hk, mac); + return ath_hal_keyset(ah, k->wk_keyix, hk, mac); + } + } else if (k->wk_flags & IEEE80211_KEY_XMIT) { + if (sc->sc_splitmic) { + /* + * NB: must pass MIC key in expected location when + * the keycache only holds one MIC key per entry. + */ + memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_txmic)); + } else + memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic)); + KEYPRINTF(sc, k->wk_keyix, hk, mac); + return ath_hal_keyset(ah, k->wk_keyix, hk, mac); + } else if (k->wk_flags & IEEE80211_KEY_RECV) { + memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); + KEYPRINTF(sc, k->wk_keyix, hk, mac); + return ath_hal_keyset(ah, k->wk_keyix, hk, mac); + } + return 0; +#undef IEEE80211_KEY_XR +} + +/* + * Set a net80211 key into the hardware. This handles the + * potential distribution of key state to multiple key + * cache slots for TKIP with hardware MIC support. + */ +int +ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, + struct ieee80211_node *bss) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + static const u_int8_t ciphermap[] = { + HAL_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */ + HAL_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */ + HAL_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */ + HAL_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */ + (u_int8_t) -1, /* 4 is not allocated */ + HAL_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */ + HAL_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */ + }; + struct ath_hal *ah = sc->sc_ah; + const struct ieee80211_cipher *cip = k->wk_cipher; + u_int8_t gmac[IEEE80211_ADDR_LEN]; + const u_int8_t *mac; + HAL_KEYVAL hk; + + memset(&hk, 0, sizeof(hk)); + /* + * Software crypto uses a "clear key" so non-crypto + * state kept in the key cache are maintained and + * so that rx frames have an entry to match. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { + KASSERT(cip->ic_cipher < N(ciphermap), + ("invalid cipher type %u", cip->ic_cipher)); + hk.kv_type = ciphermap[cip->ic_cipher]; + hk.kv_len = k->wk_keylen; + memcpy(hk.kv_val, k->wk_key, k->wk_keylen); + } else + hk.kv_type = HAL_CIPHER_CLR; + + if ((k->wk_flags & IEEE80211_KEY_GROUP) && sc->sc_mcastkey) { + /* + * Group keys on hardware that supports multicast frame + * key search use a MAC that is the sender's address with + * the multicast bit set instead of the app-specified address. + */ + IEEE80211_ADDR_COPY(gmac, bss->ni_macaddr); + gmac[0] |= 0x01; + mac = gmac; + } else + mac = k->wk_macaddr; + + if (hk.kv_type == HAL_CIPHER_TKIP && + (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { + return ath_keyset_tkip(sc, k, &hk, mac); + } else { + KEYPRINTF(sc, k->wk_keyix, &hk, mac); + return ath_hal_keyset(ah, k->wk_keyix, &hk, mac); + } +#undef N +} + +/* + * Allocate tx/rx key slots for TKIP. We allocate two slots for + * each key, one for decrypt/encrypt and the other for the MIC. + */ +static u_int16_t +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; + + KASSERT(sc->sc_splitmic, ("key cache !split")); + /* XXX could optimize */ + for (i = 0; i < N(sc->sc_keymap)/4; i++) { + u_int8_t b = sc->sc_keymap[i]; + if (b != 0xff) { + /* + * One or more slots in this byte are free. + */ + keyix = i*NBBY; + while (b & 1) { + again: + keyix++; + b >>= 1; + } + /* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */ + if (isset(sc->sc_keymap, keyix+32) || + isset(sc->sc_keymap, keyix+64) || + isset(sc->sc_keymap, keyix+32+64)) { + /* full pair unavailable */ + /* XXX statistic */ + if (keyix == (i+1)*NBBY) { + /* no slots were appropriate, advance */ + continue; + } + goto again; + } + setbit(sc->sc_keymap, keyix); + setbit(sc->sc_keymap, keyix+64); + setbit(sc->sc_keymap, keyix+32); + setbit(sc->sc_keymap, keyix+32+64); + DPRINTF(sc, ATH_DEBUG_KEYCACHE, + "%s: key pair %u,%u %u,%u\n", + __func__, keyix, keyix+64, + keyix+32, keyix+32+64); + *txkeyix = keyix; + *rxkeyix = keyix+32; + return 1; + } + } + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); + return 0; +#undef N +} + +/* + * Allocate tx/rx key slots for TKIP. We allocate two slots for + * each key, one for decrypt/encrypt and the other for the MIC. + */ +static u_int16_t +key_alloc_pair(struct ath_softc *sc, + ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + u_int i, keyix; + + KASSERT(!sc->sc_splitmic, ("key cache split")); + /* XXX could optimize */ + for (i = 0; i < N(sc->sc_keymap)/4; i++) { + u_int8_t b = sc->sc_keymap[i]; + if (b != 0xff) { + /* + * One or more slots in this byte are free. + */ + keyix = i*NBBY; + while (b & 1) { + again: + keyix++; + b >>= 1; + } + if (isset(sc->sc_keymap, keyix+64)) { + /* full pair unavailable */ + /* XXX statistic */ + if (keyix == (i+1)*NBBY) { + /* no slots were appropriate, advance */ + continue; + } + goto again; + } + setbit(sc->sc_keymap, keyix); + setbit(sc->sc_keymap, keyix+64); + DPRINTF(sc, ATH_DEBUG_KEYCACHE, + "%s: key pair %u,%u\n", + __func__, keyix, keyix+64); + *txkeyix = *rxkeyix = keyix; + return 1; + } + } + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); + return 0; +#undef N +} + +/* + * Allocate a single key cache slot. + */ +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; + + /* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */ + for (i = 0; i < N(sc->sc_keymap); i++) { + u_int8_t b = sc->sc_keymap[i]; + if (b != 0xff) { + /* + * One or more slots are free. + */ + keyix = i*NBBY; + while (b & 1) + keyix++, b >>= 1; + setbit(sc->sc_keymap, keyix); + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: key %u\n", + __func__, keyix); + *txkeyix = *rxkeyix = keyix; + return 1; + } + } + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__); + return 0; +#undef N +} + +/* + * Allocate one or more key cache slots for a uniacst key. The + * key itself is needed only to identify the cipher. For hardware + * TKIP with split cipher+MIC keys we allocate two key cache slot + * pairs so that we can setup separate TX and RX MIC keys. Note + * that the MIC key for a TKIP key at slot i is assumed by the + * hardware to be at slot i+64. This limits TKIP keys to the first + * 64 entries. + */ +int +ath_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, + ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) +{ + struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + + /* + * Group key allocation must be handled specially for + * parts that do not support multicast key cache search + * functionality. For those parts the key id must match + * the h/w key index so lookups find the right key. On + * parts w/ the key search facility we install the sender's + * mac address (with the high bit set) and let the hardware + * find the key w/o using the key id. This is preferred as + * it permits us to support multiple users for adhoc and/or + * multi-station operation. + */ + if (k->wk_keyix != IEEE80211_KEYIX_NONE) { + /* + * Only global keys should have key index assigned. + */ + if (!(&vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { + /* should not happen */ + DPRINTF(sc, ATH_DEBUG_KEYCACHE, + "%s: bogus group key\n", __func__); + return 0; + } + if (vap->iv_opmode != IEEE80211_M_HOSTAP || + !(k->wk_flags & IEEE80211_KEY_GROUP) || + !sc->sc_mcastkey) { + /* + * XXX we pre-allocate the global keys so + * have no way to check if they've already + * been allocated. + */ + *keyix = *rxkeyix = k - vap->iv_nw_keys; + return 1; + } + /* + * Group key and device supports multicast key search. + */ + k->wk_keyix = IEEE80211_KEYIX_NONE; + } + + /* + * We allocate two pair for TKIP when using the h/w to do + * the MIC. For everything else, including software crypto, + * we allocate a single entry. Note that s/w crypto requires + * a pass-through slot on the 5211 and 5212. The 5210 does + * not support pass-through cache entries and we map all + * those requests to slot 0. + */ + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + return key_alloc_single(sc, keyix, rxkeyix); + } else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP && + (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { + if (sc->sc_splitmic) + return key_alloc_2pair(sc, keyix, rxkeyix); + else + return key_alloc_pair(sc, keyix, rxkeyix); + } else { + return key_alloc_single(sc, keyix, rxkeyix); + } +} + +/* + * Delete an entry in the key cache allocated by ath_key_alloc. + */ +int +ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + struct ath_hal *ah = sc->sc_ah; + const struct ieee80211_cipher *cip = k->wk_cipher; + u_int keyix = k->wk_keyix; + + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix); + + ath_hal_keyreset(ah, keyix); + /* + * 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) + ath_hal_keyreset(ah, keyix+32); /* RX key */ + if (keyix >= IEEE80211_WEP_NKID) { + /* + * Don't touch keymap entries for global keys so + * they are never considered for dynamic allocation. + */ + clrbit(sc->sc_keymap, keyix); + if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && + (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { + clrbit(sc->sc_keymap, keyix+64); /* TX key MIC */ + if (sc->sc_splitmic) { + /* +32 for RX key, +32+64 for RX key MIC */ + clrbit(sc->sc_keymap, keyix+32); + clrbit(sc->sc_keymap, keyix+32+64); + } + } + } + return 1; +} + +/* + * Set the key cache contents for the specified key. Key cache + * slot(s) must already have been allocated by ath_key_alloc. + */ +int +ath_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, + const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + + return ath_keyset(sc, k, vap->iv_bss); +} |