freebsd-skq/sys/dev/ath/if_ath_keycache.c

498 lines
14 KiB
C
Raw Normal View History

/*-
* 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);
}