ath9k: Group Key fix for VAPs
authorDaniel Yingqiang Ma <yma.cool@gmail.com>
Tue, 13 Apr 2010 07:12:07 +0000 (15:12 +0800)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 20 Apr 2010 15:50:51 +0000 (11:50 -0400)
When I set up multiple VAPs with ath9k, I encountered an issue that
the traffic may be lost after a while.

The detailed phenomenon is
1. After a while the clients connected to one of these VAPs will get
into a state that no broadcast/multicast packets can be transfered
successfully while the unicast packets can be transfered normally.
2. Minutes latter the unitcast packets transfer will fail as well,
because the ARP entry is expired and it can't be freshed due to the
broadcast trouble.

It's caused by the group key overwritten and someone discussed this
issue in ath9k-devel maillist before, but haven't work out a fix yet.

I referred the method in madwifi, and made a patch for ath9k.
The method is to set the high bit of the sender(AP)'s address, and
associated that mac and the group key. It requires the hardware
supports multicast frame key search. It seems true for AR9160.

Not sure whether it's the correct way to fix this issue. But it seems
to work in my test. The patch is attached, feel free to revise it.

Signed-off-by: Daniel Yingqiang ma <yma.cool@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/main.c
include/net/mac80211.h
net/mac80211/key.c

index 1f4ea74bf4caf66b9a7e5c71bc42c939a49cd929..40136cf63fa48342a7e08541434726561f1ebc9d 100644 (file)
@@ -751,6 +751,7 @@ static int ath_key_config(struct ath_common *common,
        struct ath_hw *ah = common->ah;
        struct ath9k_keyval hk;
        const u8 *mac = NULL;
+       u8 gmac[ETH_ALEN];
        int ret = 0;
        int idx;
 
@@ -774,9 +775,30 @@ static int ath_key_config(struct ath_common *common,
        memcpy(hk.kv_val, key->key, key->keylen);
 
        if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
-               /* For now, use the default keys for broadcast keys. This may
-                * need to change with virtual interfaces. */
-               idx = key->keyidx;
+
+               if (key->ap_addr) {
+                       /*
+                        * Group keys on hardware that supports multicast frame
+                        * key search use a mac that is the sender's address with
+                        * the high bit set instead of the app-specified address.
+                        */
+                       memcpy(gmac, key->ap_addr, ETH_ALEN);
+                       gmac[0] |= 0x80;
+                       mac = gmac;
+
+                       if (key->alg == ALG_TKIP)
+                               idx = ath_reserve_key_cache_slot_tkip(common);
+                       else
+                               idx = ath_reserve_key_cache_slot(common);
+                       if (idx < 0)
+                               mac = NULL; /* no free key cache entries */
+               }
+
+               if (!mac) {
+                       /* For now, use the default keys for broadcast keys. This may
+                        * need to change with virtual interfaces. */
+                       idx = key->keyidx;
+               }
        } else if (key->keyidx) {
                if (WARN_ON(!sta))
                        return -EOPNOTSUPP;
index c49e6adcd8fa9255b2447f77a8cf0f5a64c1ee19..63e9d37e3e71296e9c8b9f29ffdec52e4a0c321e 100644 (file)
@@ -793,6 +793,7 @@ struct ieee80211_key_conf {
        u8 iv_len;
        u8 hw_key_idx;
        u8 flags;
+       u8 *ap_addr;
        s8 keyidx;
        u8 keylen;
        u8 key[0];
index 8160d9c5372ea09e8fbc6c01b54c3e00703d3188..75705bd419564766784d59497f9aaeac0a4a964b 100644 (file)
@@ -139,6 +139,7 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
                                     struct ieee80211_sub_if_data,
                                     u.ap);
 
+       key->conf.ap_addr = sdata->dev->dev_addr;
        ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf);
 
        if (!ret) {