root/sys/dev/ath/if_ath_keycache.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * 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>
/*
 * 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_var.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>
#include <dev/ath/if_ath_misc.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,
                            ieee80211_crypto_get_key_txmic_data(k),
                            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,
                            ieee80211_crypto_get_key_rxmic_data(k),
                            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,
                            ieee80211_crypto_get_key_rxmic_data(k),
                            sizeof(hk->kv_mic));
                        memcpy(hk->kv_txmic,
                            ieee80211_crypto_get_key_txmic_data(k),
                            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,
                            ieee80211_crypto_get_key_txmic_data(k),
                            sizeof(hk->kv_txmic));
                } else
                        memcpy(hk->kv_txmic,
                            ieee80211_crypto_get_key_txmic_data(k),
                            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,
                    ieee80211_crypto_get_key_rxmic_data(k),
                    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, struct ieee80211vap *vap,
        const struct ieee80211_key *k,
        struct ieee80211_node *bss)
{
        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;
        int ret;

        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 < nitems(ciphermap),
                        ("invalid cipher type %u", cip->ic_cipher));
                hk.kv_type = ciphermap[cip->ic_cipher];
                hk.kv_len = ieee80211_crypto_get_key_len(k);
                memcpy(hk.kv_val,
                    ieee80211_crypto_get_key_data(k),
                    ieee80211_crypto_get_key_len(k));
        } else
                hk.kv_type = HAL_CIPHER_CLR;

        /*
         * If we're installing a clear cipher key and
         * the hardware doesn't support that, just succeed.
         * Leave it up to the net80211 layer to figure it out.
         */
        if (hk.kv_type == HAL_CIPHER_CLR && sc->sc_hasclrkey == 0) {
                return (1);
        }

        /*
         * XXX TODO: check this:
         * 
         * Group keys on hardware that supports multicast frame
         * key search should only be done in adhoc/hostap mode,
         * not STA mode.
         *
         * XXX TODO: what about mesh, tdma?
         */
#if 0
        if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
             vap->iv_opmode == IEEE80211_M_IBSS) &&
#else
        if (
#endif
            (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;

        ATH_LOCK(sc);
        ath_power_set_power_state(sc, HAL_PM_AWAKE);
        if (hk.kv_type == HAL_CIPHER_TKIP &&
            (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
                ret = ath_keyset_tkip(sc, k, &hk, mac);
        } else {
                KEYPRINTF(sc, k->wk_keyix, &hk, mac);
                ret = ath_hal_keyset(ah, k->wk_keyix, &hk, mac);
        }
        ath_power_restore_power_state(sc);
        ATH_UNLOCK(sc);

        return (ret);
}

/*
 * 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)
{
        u_int i, keyix;

        KASSERT(sc->sc_splitmic, ("key cache !split"));
        /* XXX could optimize */
        for (i = 0; i < nitems(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;
}

/*
 * 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)
{
        u_int i, keyix;

        KASSERT(!sc->sc_splitmic, ("key cache split"));
        /* XXX could optimize */
        for (i = 0; i < nitems(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;
}

/*
 * Allocate a single key cache slot.
 */
static int
key_alloc_single(struct ath_softc *sc,
        ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
{
        u_int i, keyix;

        if (sc->sc_hasclrkey == 0) {
                /*
                 * Map to slot 0 for the AR5210.
                 */
                *txkeyix = *rxkeyix = 0;
                return (1);
        }

        /* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
        for (i = 0; i < nitems(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;
}

/*
 * 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_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 (!ieee80211_is_key_global(vap, k)) {
                        /* 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 =
                            ieee80211_crypto_get_key_wepidx(vap, k);
                        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_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_LOCK(sc);
        ath_power_set_power_state(sc, HAL_PM_AWAKE);
        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);
                        }
                }
        }
        ath_power_restore_power_state(sc);
        ATH_UNLOCK(sc);
        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)
{
        struct ath_softc *sc = vap->iv_ic->ic_softc;

        return ath_keyset(sc, vap, k, vap->iv_bss);
}