Add /sys/class/ieee80211/phyX/sta/*/key/* and /sys/class/net/X/keys/[0-3]/*
attributes.

Signed-off-by: Jiri Benc <[EMAIL PROTECTED]>

---

 net/d80211/ieee80211.c           |   33 ++++++
 net/d80211/ieee80211_i.h         |   11 ++
 net/d80211/ieee80211_ioctl.c     |  101 ++++++++++++-------
 net/d80211/ieee80211_key.h       |    2 
 net/d80211/ieee80211_sysfs.c     |   12 ++
 net/d80211/ieee80211_sysfs_sta.c |  206 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 326 insertions(+), 39 deletions(-)

9b2e2e2cc66e7fb3f1ae07d7e083ffa63dffbde7
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index e4ac701..1a03f07 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -84,15 +84,44 @@ ieee80211_key_data2conf(struct ieee80211
        return conf;
 }
 
+struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
+                                         int idx, size_t key_len, gfp_t flags)
+{
+       struct ieee80211_key *key;
+       int res;
+
+       key = kzalloc(sizeof(struct ieee80211_key) + key_len, flags);
+       if (!key)
+               return NULL;
+       if (sdata)
+               res = kobject_set_name(&key->kobj, "%d", idx);
+       else
+               res = kobject_set_name(&key->kobj, "key");
+       if (res) {
+               kfree(key);
+               return NULL;
+       }
+       ieee80211_key_sysfs_set_kset(key, sdata ? &sdata->key_kset : NULL);
+       kobject_init(&key->kobj);
+       return key;
+}
 
 void ieee80211_key_free(struct ieee80211_key *key)
 {
-       if (key && key->alg == ALG_CCMP)
+       if (key)
+               kobject_put(&key->kobj);
+}
+
+void ieee80211_key_release(struct kobject *kobj)
+{
+       struct ieee80211_key *key;
+
+       key = container_of(kobj, struct ieee80211_key, kobj);
+       if (key->alg == ALG_CCMP)
                ieee80211_aes_key_free(key->u.ccmp.tfm);
        kfree(key);
 }
 
-
 static int rate_list_match(int *rate_list, int rate)
 {
        int i;
diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h
index 212d12f..c1d7422 100644
--- a/net/d80211/ieee80211_i.h
+++ b/net/d80211/ieee80211_i.h
@@ -294,6 +294,7 @@ struct ieee80211_sub_if_data {
 #define NUM_DEFAULT_KEYS 4
         struct ieee80211_key *keys[NUM_DEFAULT_KEYS];
         struct ieee80211_key *default_key;
+       struct kset key_kset;
 
        struct ieee80211_if_ap *bss; /* BSS that this device belongs to */
 
@@ -527,7 +528,10 @@ int ieee80211_if_config(struct net_devic
 struct ieee80211_key_conf *
 ieee80211_key_data2conf(struct ieee80211_local *local,
                        struct ieee80211_key *data);
+struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
+                                         int idx, size_t key_len, gfp_t flags);
 void ieee80211_key_free(struct ieee80211_key *key);
+void ieee80211_key_release(struct kobject *kobj);
 void ieee80211_rx_mgmt(struct net_device *dev, struct sk_buff *skb,
                       struct ieee80211_rx_status *status, u32 msg_type);
 void ieee80211_prepare_rates(struct net_device *dev);
@@ -633,5 +637,12 @@ int ieee80211_sta_kset_sysfs_register(st
 void ieee80211_sta_kset_sysfs_unregister(struct ieee80211_local *local);
 int ieee80211_sta_sysfs_add(struct sta_info *sta);
 void ieee80211_sta_sysfs_remove(struct sta_info *sta);
+int ieee80211_key_kset_sysfs_register(struct ieee80211_sub_if_data *sdata);
+void ieee80211_key_kset_sysfs_unregister(struct ieee80211_sub_if_data *sdata);
+void ieee80211_key_sysfs_set_kset(struct ieee80211_key *key, struct kset 
*kset);
+int ieee80211_key_sysfs_add(struct ieee80211_key *key);
+void ieee80211_key_sysfs_remove(struct ieee80211_key *key);
+int ieee80211_key_sysfs_add_default(struct ieee80211_sub_if_data *sdata);
+void ieee80211_key_sysfs_remove_default(struct ieee80211_sub_if_data *sdata);
 
 #endif /* IEEE80211_I_H */
diff --git a/net/d80211/ieee80211_ioctl.c b/net/d80211/ieee80211_ioctl.c
index 562b9f3..7c2a847 100644
--- a/net/d80211/ieee80211_ioctl.c
+++ b/net/d80211/ieee80211_ioctl.c
@@ -470,7 +470,7 @@ static int ieee80211_set_encryption(stru
        struct ieee80211_local *local = dev->ieee80211_ptr;
        int ret = 0;
        struct sta_info *sta;
-       struct ieee80211_key **key;
+       struct ieee80211_key *key, *old_key;
        int try_hwaccel = 1;
         struct ieee80211_key_conf *keyconf;
         struct ieee80211_sub_if_data *sdata;
@@ -486,7 +486,7 @@ static int ieee80211_set_encryption(stru
                               dev->name, idx);
                        return -EINVAL;
                }
-               key = &sdata->keys[idx];
+               key = sdata->keys[idx];
 
                /* Disable hwaccel for default keys when the interface is not
                 * the default one.
@@ -525,7 +525,7 @@ #endif /* CONFIG_D80211_VERBOSE_DEBUG */
                        return -ENOENT;
                }
 
-               key = &sta->key;
+               key = sta->key;
        }
 
        /* FIX:
@@ -571,8 +571,8 @@ #endif /* CONFIG_D80211_VERBOSE_DEBUG */
 
        if (alg == ALG_NONE) {
                keyconf = NULL;
-               if (try_hwaccel && *key && local->hw->set_key &&
-                   (keyconf = ieee80211_key_data2conf(local, *key)) != NULL &&
+               if (try_hwaccel && key && local->hw->set_key &&
+                   (keyconf = ieee80211_key_data2conf(local, key)) != NULL &&
                    local->hw->set_key(dev, DISABLE_KEY, sta_addr,
                                       keyconf, sta ? sta->aid : 0)) {
                        if (err)
@@ -583,68 +583,101 @@ #endif /* CONFIG_D80211_VERBOSE_DEBUG */
                }
                kfree(keyconf);
 
-               if (sdata->default_key == *key)
+               if (key && sdata->default_key == key) {
+                       ieee80211_key_sysfs_remove_default(sdata);
                        sdata->default_key = NULL;
-               ieee80211_key_free(*key);
-               *key = NULL;
+               }
+               ieee80211_key_sysfs_remove(key);
+               if (sta)
+                       sta->key = NULL;
+               else
+                       sdata->keys[idx] = NULL;
+               ieee80211_key_free(key);
+               key = NULL;
        } else {
-               if (*key == NULL || (*key)->keylen < key_len) {
-                       ieee80211_key_free(*key);
-                       *key = kmalloc(sizeof(struct ieee80211_key) +
-                                      key_len, GFP_ATOMIC);
-                       if (*key == NULL) {
-                               ret = -ENOMEM;
-                               goto done;
-                       }
+               old_key = key;
+               key = ieee80211_key_alloc(sta ? NULL : sdata, idx, key_len,
+                                         GFP_KERNEL);
+               if (!key) {
+                       ret = -ENOMEM;
+                       goto err_out;
                }
-               memset(*key, 0, sizeof(struct ieee80211_key) + key_len);
+
                /* default to sw encryption; low-level driver sets these if the
                 * requested encryption is supported */
-               (*key)->hw_key_idx = HW_KEY_IDX_INVALID;
-               (*key)->force_sw_encrypt = 1;
+               key->hw_key_idx = HW_KEY_IDX_INVALID;
+               key->force_sw_encrypt = 1;
 
-               (*key)->alg = alg;
-               (*key)->keyidx = idx;
-               (*key)->keylen = key_len;
-               memcpy((*key)->key, _key, key_len);
+               key->alg = alg;
+               key->keyidx = idx;
+               key->keylen = key_len;
+               memcpy(key->key, _key, key_len);
                if (set_tx_key)
-                       (*key)->default_tx_key = 1;
+                       key->default_tx_key = 1;
 
                if (alg == ALG_CCMP) {
                        /* Initialize AES key state here as an optimization
                         * so that it does not need to be initialized for every
                         * packet. */
-                       (*key)->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
-                               (*key)->key);
-                       if ((*key)->u.ccmp.tfm == NULL) {
-                               kfree(*key);
-                               *key = NULL;
+                       key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
+                               key->key);
+                       if (key->u.ccmp.tfm == NULL) {
                                ret = -ENOMEM;
-                               goto done;
+                               goto err_free;
                        }
                }
 
+               if (old_key && sdata->default_key == old_key) {
+                       ieee80211_key_sysfs_remove_default(sdata);
+                       sdata->default_key = NULL;
+               }
+               ieee80211_key_sysfs_remove(old_key);
+               if (sta)
+                       sta->key = key;
+               else
+                       sdata->keys[idx] = key;
+               ieee80211_key_free(old_key);
+               if (sta)
+                       key->kobj.parent = &sta->kobj;
+               ret = ieee80211_key_sysfs_add(key);
+               if (ret)
+                       goto err_null;
+
                if (try_hwaccel &&
                    (alg == ALG_WEP || alg == ALG_TKIP || alg == ALG_CCMP)) {
                        int e = ieee80211_set_hw_encryption(dev, sta, sta_addr,
-                                                           *key);
+                                                           key);
                        if (err)
                                *err = e;
                }
        }
 
-       if (set_tx_key || (sta == NULL && sdata->default_key == NULL)) {
-                sdata->default_key = *key;
+       if (set_tx_key || (sta == NULL && sdata->default_key == NULL && key)) {
+               sdata->default_key = key;
+               if (ieee80211_key_sysfs_add_default(sdata))
+                       printk(KERN_WARNING "%s: cannot create symlink to "
+                              "default key\n", dev->name);
                if (local->hw->set_key_idx &&
                    local->hw->set_key_idx(dev, idx))
                        printk(KERN_DEBUG "%s: failed to set TX key idx for "
                               "low-level driver\n", dev->name);
        }
 
- done:
        if (sta)
                sta_info_put(sta);
 
+       return 0;
+
+err_null:
+       if (sta)
+               sta->key = NULL;
+       else
+               sdata->keys[idx] = NULL;
+err_free:
+       ieee80211_key_free(key);
+err_out:
+       if (sta)
+               sta_info_put(sta);
        return ret;
 }
 
diff --git a/net/d80211/ieee80211_key.h b/net/d80211/ieee80211_key.h
index 4c688cd..64f2363 100644
--- a/net/d80211/ieee80211_key.h
+++ b/net/d80211/ieee80211_key.h
@@ -40,6 +40,8 @@ #define CCMP_PN_LEN 6
 #define NUM_RX_DATA_QUEUES 17
 
 struct ieee80211_key {
+       struct kobject kobj;
+
        int hw_key_idx; /* filled and used by low-level driver */
        ieee80211_key_alg alg;
        union {
diff --git a/net/d80211/ieee80211_sysfs.c b/net/d80211/ieee80211_sysfs.c
index 12d16fd..10bf45c 100644
--- a/net/d80211/ieee80211_sysfs.c
+++ b/net/d80211/ieee80211_sysfs.c
@@ -709,16 +709,22 @@ int ieee80211_sysfs_add_netdevice(struct
        res = sysfs_create_link(&dev->class_dev.kobj, &local->class_dev.kobj,
                                "wiphy");
        if (res)
-               goto out;
+               goto err_out;
        res = ieee80211_add_if_group(&dev->class_dev.kobj, dev);
        if (res)
-               sysfs_remove_link(&dev->class_dev.kobj, "wiphy");
-out:
+               goto err_link;
+       res = ieee80211_key_kset_sysfs_register(IEEE80211_DEV_TO_SUB_IF(dev));
+       return res;
+
+err_link:
+       sysfs_remove_link(&dev->class_dev.kobj, "wiphy");
+err_out:
        return res;
 }
 
 void ieee80211_sysfs_remove_netdevice(struct net_device *dev)
 {
+       ieee80211_key_kset_sysfs_unregister(IEEE80211_DEV_TO_SUB_IF(dev));
        ieee80211_remove_if_group(&dev->class_dev.kobj, dev);
        sysfs_remove_link(&dev->class_dev.kobj, "wiphy");
 }
diff --git a/net/d80211/ieee80211_sysfs_sta.c b/net/d80211/ieee80211_sysfs_sta.c
index 07de564..94c6dd8 100644
--- a/net/d80211/ieee80211_sysfs_sta.c
+++ b/net/d80211/ieee80211_sysfs_sta.c
@@ -10,14 +10,22 @@
 #include <linux/kobject.h>
 #include <linux/sysfs.h>
 #include "ieee80211_i.h"
+#include "ieee80211_key.h"
 #include "sta_info.h"
 
 static ssize_t sta_sysfs_show(struct kobject *, struct attribute *, char *);
+static ssize_t key_sysfs_show(struct kobject *, struct attribute *, char *);
 
 static struct sysfs_ops sta_ktype_ops = {
        .show = sta_sysfs_show,
 };
 
+static struct sysfs_ops key_ktype_ops = {
+       .show = key_sysfs_show,
+};
+
+/* sta attributtes */
+
 #define STA_SHOW(name, field, format_string)                           \
 static ssize_t show_sta_##name(const struct sta_info *sta, char *buf)  \
 {                                                                      \
@@ -183,12 +191,153 @@ #endif
        NULL
 };
 
+/* keys attributtes */
+
+struct key_attribute {
+       struct attribute attr;
+       ssize_t (*show)(const struct ieee80211_key *, char *buf);
+       ssize_t (*store)(struct ieee80211_key *, const char *buf,
+                        size_t count);
+};
+
+#define KEY_SHOW(name, field, format_string)                           \
+static ssize_t show_key_##name(const struct ieee80211_key *key, char *buf)\
+{                                                                      \
+       return sprintf(buf, format_string, key->field);                 \
+}
+#define KEY_SHOW_D(name, field) KEY_SHOW(name, field, "%d\n")
+
+#define __KEY_ATTR(name)                                               \
+static struct key_attribute key_attr_##name =                          \
+       __ATTR(name, S_IRUSR, show_key_##name, NULL)
+
+#define KEY_ATTR(name, field, format)                                  \
+               KEY_SHOW_##format(name, field)                          \
+               __KEY_ATTR(name)
+
+KEY_ATTR(length, keylen, D);
+KEY_ATTR(sw_encrypt, force_sw_encrypt, D);
+KEY_ATTR(index, keyidx, D);
+KEY_ATTR(hw_index, hw_key_idx, D);
+KEY_ATTR(tx_rx_count, tx_rx_count, D);
+
+static ssize_t show_key_algorithm(const struct ieee80211_key *key, char *buf)
+{
+       char *alg;
+
+       switch (key->alg) {
+       case ALG_WEP:
+               alg = "WEP";
+               break;
+       case ALG_TKIP:
+               alg = "TKIP";
+               break;
+       case ALG_CCMP:
+               alg = "CCMP";
+               break;
+       default:
+               return 0;
+       }
+       return sprintf(buf, "%s\n", alg);
+}
+__KEY_ATTR(algorithm);
+
+static ssize_t show_key_tx_spec(const struct ieee80211_key *key, char *buf)
+{
+       const u8 *tpn;
+
+       switch (key->alg) {
+       case ALG_WEP:
+               return sprintf(buf, "\n");
+       case ALG_TKIP:
+               return sprintf(buf, "%08x %04x\n", key->u.tkip.iv32,
+                              key->u.tkip.iv16);
+       case ALG_CCMP:
+               tpn = key->u.ccmp.tx_pn;
+               return sprintf(buf, "%02x%02x%02x%02x%02x%02x\n", tpn[0],
+                              tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]);
+       default:
+               return 0;
+       }
+}
+__KEY_ATTR(tx_spec);
+
+static ssize_t show_key_rx_spec(const struct ieee80211_key *key, char *buf)
+{
+       int i;
+       const u8 *rpn;
+       char *p = buf;
+
+       switch (key->alg) {
+       case ALG_WEP:
+               return sprintf(buf, "\n");
+       case ALG_TKIP:
+               for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+                       p += sprintf(p, "%08x %04x\n",
+                                    key->u.tkip.iv32_rx[i],
+                                    key->u.tkip.iv16_rx[i]);
+               return (p - buf);
+       case ALG_CCMP:
+               for (i = 0; i < NUM_RX_DATA_QUEUES; i++) {
+                       rpn = key->u.ccmp.rx_pn[i];
+                       p += sprintf(p, "%02x%02x%02x%02x%02x%02x\n", rpn[0],
+                                    rpn[1], rpn[2], rpn[3], rpn[4], rpn[5]);
+               }
+               return (p - buf);
+       default:
+               return 0;
+       }
+}
+__KEY_ATTR(rx_spec);
+
+static ssize_t show_key_replays(const struct ieee80211_key *key, char *buf)
+{
+       if (key->alg != ALG_CCMP)
+               return 0;
+       return sprintf(buf, "%u\n", key->u.ccmp.replays);
+}
+__KEY_ATTR(replays);
+
+static ssize_t show_key_key(const struct ieee80211_key *key, char *buf)
+{
+       int i;
+       char *p = buf;
+
+       for (i = 0; i < key->keylen; i++)
+               p += sprintf(p, "%02x", key->key[i]);
+       p += sprintf(p, "\n");
+       return (p - buf);
+}
+__KEY_ATTR(key);
+
+static struct attribute *key_ktype_attrs[] = {
+       &key_attr_length.attr,
+       &key_attr_sw_encrypt.attr,
+       &key_attr_index.attr,
+       &key_attr_hw_index.attr,
+       &key_attr_tx_rx_count.attr,
+       &key_attr_algorithm.attr,
+       &key_attr_tx_spec.attr,
+       &key_attr_rx_spec.attr,
+       &key_attr_replays.attr,
+       &key_attr_key.attr,
+       NULL
+};
+
+/* structures and functions */
+
 static struct kobj_type sta_ktype = {
        .release = sta_info_release,
        .sysfs_ops = &sta_ktype_ops,
        .default_attrs = sta_ktype_attrs,
 };
 
+static struct kobj_type key_ktype = {
+       .release = ieee80211_key_release,
+       .sysfs_ops = &key_ktype_ops,
+       .default_attrs = key_ktype_attrs,
+};
+
 static ssize_t sta_sysfs_show(struct kobject *kobj, struct attribute *attr,
                              char *buf)
 {
@@ -200,6 +349,17 @@ static ssize_t sta_sysfs_show(struct kob
        return sta_attr->show(sta, buf);
 }
 
+static ssize_t key_sysfs_show(struct kobject *kobj, struct attribute *attr,
+                             char *buf)
+{
+       struct key_attribute *key_attr;
+       struct ieee80211_key *key;
+
+       key_attr = container_of(attr, struct key_attribute, attr);
+       key = container_of(kobj, struct ieee80211_key, kobj);
+       return key_attr->show(key, buf);
+}
+
 int ieee80211_sta_kset_sysfs_register(struct ieee80211_local *local)
 {
        int res;
@@ -217,6 +377,23 @@ void ieee80211_sta_kset_sysfs_unregister
        kset_unregister(&local->sta_kset);
 }
 
+int ieee80211_key_kset_sysfs_register(struct ieee80211_sub_if_data *sdata)
+{
+       int res;
+
+       res = kobject_set_name(&sdata->key_kset.kobj, "keys");
+       if (res)
+               return res;
+       sdata->key_kset.kobj.parent = &sdata->dev->class_dev.kobj;
+       sdata->key_kset.ktype = &key_ktype;
+       return kset_register(&sdata->key_kset);
+}
+
+void ieee80211_key_kset_sysfs_unregister(struct ieee80211_sub_if_data *sdata)
+{
+       kset_unregister(&sdata->key_kset);
+}
+
 int ieee80211_sta_sysfs_add(struct sta_info *sta)
 {
        return kobject_add(&sta->kobj);
@@ -226,3 +403,32 @@ void ieee80211_sta_sysfs_remove(struct s
 {
        kobject_del(&sta->kobj);
 }
+
+void ieee80211_key_sysfs_set_kset(struct ieee80211_key *key, struct kset *kset)
+{
+       key->kobj.kset = kset;
+       if (!kset)
+               key->kobj.ktype = &key_ktype;
+}
+
+int ieee80211_key_sysfs_add(struct ieee80211_key *key)
+{
+       return kobject_add(&key->kobj);
+}
+
+void ieee80211_key_sysfs_remove(struct ieee80211_key *key)
+{
+       if (key)
+               kobject_del(&key->kobj);
+}
+
+int ieee80211_key_sysfs_add_default(struct ieee80211_sub_if_data *sdata)
+{
+       return sysfs_create_link(&sdata->key_kset.kobj,
+                                &sdata->default_key->kobj, "default");
+}
+
+void ieee80211_key_sysfs_remove_default(struct ieee80211_sub_if_data *sdata)
+{
+       sysfs_remove_link(&sdata->key_kset.kobj, "default");
+}
-- 
1.3.0

-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to