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