1e9f207f0SJiri Benc /* 2e9f207f0SJiri Benc * Copyright 2003-2005 Devicescape Software, Inc. 3e9f207f0SJiri Benc * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> 4e9f207f0SJiri Benc * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 5e9f207f0SJiri Benc * 6e9f207f0SJiri Benc * This program is free software; you can redistribute it and/or modify 7e9f207f0SJiri Benc * it under the terms of the GNU General Public License version 2 as 8e9f207f0SJiri Benc * published by the Free Software Foundation. 9e9f207f0SJiri Benc */ 10e9f207f0SJiri Benc 11e9f207f0SJiri Benc #include <linux/kobject.h> 12e9f207f0SJiri Benc #include "ieee80211_i.h" 132c8dccc7SJohannes Berg #include "key.h" 14e9f207f0SJiri Benc #include "debugfs.h" 15e9f207f0SJiri Benc #include "debugfs_key.h" 16e9f207f0SJiri Benc 178f20fc24SJohannes Berg #define KEY_READ(name, prop, buflen, format_string) \ 18e9f207f0SJiri Benc static ssize_t key_##name##_read(struct file *file, \ 19e9f207f0SJiri Benc char __user *userbuf, \ 20e9f207f0SJiri Benc size_t count, loff_t *ppos) \ 21e9f207f0SJiri Benc { \ 22e9f207f0SJiri Benc char buf[buflen]; \ 23e9f207f0SJiri Benc struct ieee80211_key *key = file->private_data; \ 248f20fc24SJohannes Berg int res = scnprintf(buf, buflen, format_string, key->prop); \ 25e9f207f0SJiri Benc return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ 26e9f207f0SJiri Benc } 278f20fc24SJohannes Berg #define KEY_READ_D(name) KEY_READ(name, name, 20, "%d\n") 2811a843b7SJohannes Berg #define KEY_READ_X(name) KEY_READ(name, name, 20, "0x%x\n") 29e9f207f0SJiri Benc 30e9f207f0SJiri Benc #define KEY_OPS(name) \ 31e9f207f0SJiri Benc static const struct file_operations key_ ##name## _ops = { \ 32e9f207f0SJiri Benc .read = key_##name##_read, \ 33e9f207f0SJiri Benc .open = mac80211_open_file_generic, \ 34e9f207f0SJiri Benc } 35e9f207f0SJiri Benc 36e9f207f0SJiri Benc #define KEY_FILE(name, format) \ 37e9f207f0SJiri Benc KEY_READ_##format(name) \ 38e9f207f0SJiri Benc KEY_OPS(name) 39e9f207f0SJiri Benc 408f20fc24SJohannes Berg #define KEY_CONF_READ(name, buflen, format_string) \ 418f20fc24SJohannes Berg KEY_READ(conf_##name, conf.name, buflen, format_string) 428f20fc24SJohannes Berg #define KEY_CONF_READ_D(name) KEY_CONF_READ(name, 20, "%d\n") 438f20fc24SJohannes Berg 448f20fc24SJohannes Berg #define KEY_CONF_OPS(name) \ 458f20fc24SJohannes Berg static const struct file_operations key_ ##name## _ops = { \ 468f20fc24SJohannes Berg .read = key_conf_##name##_read, \ 478f20fc24SJohannes Berg .open = mac80211_open_file_generic, \ 488f20fc24SJohannes Berg } 498f20fc24SJohannes Berg 508f20fc24SJohannes Berg #define KEY_CONF_FILE(name, format) \ 518f20fc24SJohannes Berg KEY_CONF_READ_##format(name) \ 528f20fc24SJohannes Berg KEY_CONF_OPS(name) 538f20fc24SJohannes Berg 548f20fc24SJohannes Berg KEY_CONF_FILE(keylen, D); 558f20fc24SJohannes Berg KEY_CONF_FILE(keyidx, D); 568f20fc24SJohannes Berg KEY_CONF_FILE(hw_key_idx, D); 5711a843b7SJohannes Berg KEY_FILE(flags, X); 58e9f207f0SJiri Benc KEY_FILE(tx_rx_count, D); 59e7a64f12SJohannes Berg KEY_READ(ifindex, sdata->dev->ifindex, 20, "%d\n"); 60e7a64f12SJohannes Berg KEY_OPS(ifindex); 61e9f207f0SJiri Benc 62e9f207f0SJiri Benc static ssize_t key_algorithm_read(struct file *file, 63e9f207f0SJiri Benc char __user *userbuf, 64e9f207f0SJiri Benc size_t count, loff_t *ppos) 65e9f207f0SJiri Benc { 66e9f207f0SJiri Benc char *alg; 67e9f207f0SJiri Benc struct ieee80211_key *key = file->private_data; 68e9f207f0SJiri Benc 698f20fc24SJohannes Berg switch (key->conf.alg) { 70e9f207f0SJiri Benc case ALG_WEP: 71e9f207f0SJiri Benc alg = "WEP\n"; 72e9f207f0SJiri Benc break; 73e9f207f0SJiri Benc case ALG_TKIP: 74e9f207f0SJiri Benc alg = "TKIP\n"; 75e9f207f0SJiri Benc break; 76e9f207f0SJiri Benc case ALG_CCMP: 77e9f207f0SJiri Benc alg = "CCMP\n"; 78e9f207f0SJiri Benc break; 793cfcf6acSJouni Malinen case ALG_AES_CMAC: 803cfcf6acSJouni Malinen alg = "AES-128-CMAC\n"; 813cfcf6acSJouni Malinen break; 82e9f207f0SJiri Benc default: 83e9f207f0SJiri Benc return 0; 84e9f207f0SJiri Benc } 85e9f207f0SJiri Benc return simple_read_from_buffer(userbuf, count, ppos, alg, strlen(alg)); 86e9f207f0SJiri Benc } 87e9f207f0SJiri Benc KEY_OPS(algorithm); 88e9f207f0SJiri Benc 89e9f207f0SJiri Benc static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, 90e9f207f0SJiri Benc size_t count, loff_t *ppos) 91e9f207f0SJiri Benc { 92e9f207f0SJiri Benc const u8 *tpn; 93e9f207f0SJiri Benc char buf[20]; 94e9f207f0SJiri Benc int len; 95e9f207f0SJiri Benc struct ieee80211_key *key = file->private_data; 96e9f207f0SJiri Benc 978f20fc24SJohannes Berg switch (key->conf.alg) { 98e9f207f0SJiri Benc case ALG_WEP: 99e9f207f0SJiri Benc len = scnprintf(buf, sizeof(buf), "\n"); 10050339a67SJohannes Berg break; 101e9f207f0SJiri Benc case ALG_TKIP: 102e9f207f0SJiri Benc len = scnprintf(buf, sizeof(buf), "%08x %04x\n", 103b0f76b33SHarvey Harrison key->u.tkip.tx.iv32, 104b0f76b33SHarvey Harrison key->u.tkip.tx.iv16); 10550339a67SJohannes Berg break; 106e9f207f0SJiri Benc case ALG_CCMP: 107e9f207f0SJiri Benc tpn = key->u.ccmp.tx_pn; 108e9f207f0SJiri Benc len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", 109e9f207f0SJiri Benc tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); 11050339a67SJohannes Berg break; 1113cfcf6acSJouni Malinen case ALG_AES_CMAC: 1123cfcf6acSJouni Malinen tpn = key->u.aes_cmac.tx_pn; 1133cfcf6acSJouni Malinen len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", 1143cfcf6acSJouni Malinen tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], 1153cfcf6acSJouni Malinen tpn[5]); 1163cfcf6acSJouni Malinen break; 117e9f207f0SJiri Benc default: 118e9f207f0SJiri Benc return 0; 119e9f207f0SJiri Benc } 120e9f207f0SJiri Benc return simple_read_from_buffer(userbuf, count, ppos, buf, len); 121e9f207f0SJiri Benc } 122e9f207f0SJiri Benc KEY_OPS(tx_spec); 123e9f207f0SJiri Benc 124e9f207f0SJiri Benc static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, 125e9f207f0SJiri Benc size_t count, loff_t *ppos) 126e9f207f0SJiri Benc { 127e9f207f0SJiri Benc struct ieee80211_key *key = file->private_data; 128e9f207f0SJiri Benc char buf[14*NUM_RX_DATA_QUEUES+1], *p = buf; 129e9f207f0SJiri Benc int i, len; 130e9f207f0SJiri Benc const u8 *rpn; 131e9f207f0SJiri Benc 1328f20fc24SJohannes Berg switch (key->conf.alg) { 133e9f207f0SJiri Benc case ALG_WEP: 134e9f207f0SJiri Benc len = scnprintf(buf, sizeof(buf), "\n"); 13550339a67SJohannes Berg break; 136e9f207f0SJiri Benc case ALG_TKIP: 137e9f207f0SJiri Benc for (i = 0; i < NUM_RX_DATA_QUEUES; i++) 138e9f207f0SJiri Benc p += scnprintf(p, sizeof(buf)+buf-p, 139e9f207f0SJiri Benc "%08x %04x\n", 140b0f76b33SHarvey Harrison key->u.tkip.rx[i].iv32, 141b0f76b33SHarvey Harrison key->u.tkip.rx[i].iv16); 142e9f207f0SJiri Benc len = p - buf; 14350339a67SJohannes Berg break; 144e9f207f0SJiri Benc case ALG_CCMP: 145e9f207f0SJiri Benc for (i = 0; i < NUM_RX_DATA_QUEUES; i++) { 146e9f207f0SJiri Benc rpn = key->u.ccmp.rx_pn[i]; 147e9f207f0SJiri Benc p += scnprintf(p, sizeof(buf)+buf-p, 148e9f207f0SJiri Benc "%02x%02x%02x%02x%02x%02x\n", 149e9f207f0SJiri Benc rpn[0], rpn[1], rpn[2], 150e9f207f0SJiri Benc rpn[3], rpn[4], rpn[5]); 151e9f207f0SJiri Benc } 152e9f207f0SJiri Benc len = p - buf; 15350339a67SJohannes Berg break; 1543cfcf6acSJouni Malinen case ALG_AES_CMAC: 1553cfcf6acSJouni Malinen rpn = key->u.aes_cmac.rx_pn; 1563cfcf6acSJouni Malinen p += scnprintf(p, sizeof(buf)+buf-p, 1573cfcf6acSJouni Malinen "%02x%02x%02x%02x%02x%02x\n", 1583cfcf6acSJouni Malinen rpn[0], rpn[1], rpn[2], 1593cfcf6acSJouni Malinen rpn[3], rpn[4], rpn[5]); 1603cfcf6acSJouni Malinen len = p - buf; 1613cfcf6acSJouni Malinen break; 162e9f207f0SJiri Benc default: 163e9f207f0SJiri Benc return 0; 164e9f207f0SJiri Benc } 165e9f207f0SJiri Benc return simple_read_from_buffer(userbuf, count, ppos, buf, len); 166e9f207f0SJiri Benc } 167e9f207f0SJiri Benc KEY_OPS(rx_spec); 168e9f207f0SJiri Benc 169e9f207f0SJiri Benc static ssize_t key_replays_read(struct file *file, char __user *userbuf, 170e9f207f0SJiri Benc size_t count, loff_t *ppos) 171e9f207f0SJiri Benc { 172e9f207f0SJiri Benc struct ieee80211_key *key = file->private_data; 173e9f207f0SJiri Benc char buf[20]; 174e9f207f0SJiri Benc int len; 175e9f207f0SJiri Benc 1763cfcf6acSJouni Malinen switch (key->conf.alg) { 1773cfcf6acSJouni Malinen case ALG_CCMP: 178e9f207f0SJiri Benc len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); 1793cfcf6acSJouni Malinen break; 1803cfcf6acSJouni Malinen case ALG_AES_CMAC: 1813cfcf6acSJouni Malinen len = scnprintf(buf, sizeof(buf), "%u\n", 1823cfcf6acSJouni Malinen key->u.aes_cmac.replays); 1833cfcf6acSJouni Malinen break; 1843cfcf6acSJouni Malinen default: 1853cfcf6acSJouni Malinen return 0; 1863cfcf6acSJouni Malinen } 187e9f207f0SJiri Benc return simple_read_from_buffer(userbuf, count, ppos, buf, len); 188e9f207f0SJiri Benc } 189e9f207f0SJiri Benc KEY_OPS(replays); 190e9f207f0SJiri Benc 1913cfcf6acSJouni Malinen static ssize_t key_icverrors_read(struct file *file, char __user *userbuf, 1923cfcf6acSJouni Malinen size_t count, loff_t *ppos) 1933cfcf6acSJouni Malinen { 1943cfcf6acSJouni Malinen struct ieee80211_key *key = file->private_data; 1953cfcf6acSJouni Malinen char buf[20]; 1963cfcf6acSJouni Malinen int len; 1973cfcf6acSJouni Malinen 1983cfcf6acSJouni Malinen switch (key->conf.alg) { 1993cfcf6acSJouni Malinen case ALG_AES_CMAC: 2003cfcf6acSJouni Malinen len = scnprintf(buf, sizeof(buf), "%u\n", 2013cfcf6acSJouni Malinen key->u.aes_cmac.icverrors); 2023cfcf6acSJouni Malinen break; 2033cfcf6acSJouni Malinen default: 2043cfcf6acSJouni Malinen return 0; 2053cfcf6acSJouni Malinen } 2063cfcf6acSJouni Malinen return simple_read_from_buffer(userbuf, count, ppos, buf, len); 2073cfcf6acSJouni Malinen } 2083cfcf6acSJouni Malinen KEY_OPS(icverrors); 2093cfcf6acSJouni Malinen 210e9f207f0SJiri Benc static ssize_t key_key_read(struct file *file, char __user *userbuf, 211e9f207f0SJiri Benc size_t count, loff_t *ppos) 212e9f207f0SJiri Benc { 213e9f207f0SJiri Benc struct ieee80211_key *key = file->private_data; 2148f20fc24SJohannes Berg int i, res, bufsize = 2 * key->conf.keylen + 2; 215e9f207f0SJiri Benc char *buf = kmalloc(bufsize, GFP_KERNEL); 216e9f207f0SJiri Benc char *p = buf; 217e9f207f0SJiri Benc 2188f20fc24SJohannes Berg for (i = 0; i < key->conf.keylen; i++) 2198f20fc24SJohannes Berg p += scnprintf(p, bufsize + buf - p, "%02x", key->conf.key[i]); 220e9f207f0SJiri Benc p += scnprintf(p, bufsize+buf-p, "\n"); 221e9f207f0SJiri Benc res = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); 222e9f207f0SJiri Benc kfree(buf); 223e9f207f0SJiri Benc return res; 224e9f207f0SJiri Benc } 225e9f207f0SJiri Benc KEY_OPS(key); 226e9f207f0SJiri Benc 227e9f207f0SJiri Benc #define DEBUGFS_ADD(name) \ 228e9f207f0SJiri Benc key->debugfs.name = debugfs_create_file(#name, 0400,\ 229e9f207f0SJiri Benc key->debugfs.dir, key, &key_##name##_ops); 230e9f207f0SJiri Benc 2313b96766fSJohannes Berg void ieee80211_debugfs_key_add(struct ieee80211_key *key) 232e9f207f0SJiri Benc { 23350339a67SJohannes Berg static int keycount; 2343b96766fSJohannes Berg char buf[50]; 2353b96766fSJohannes Berg struct sta_info *sta; 236e9f207f0SJiri Benc 2373b96766fSJohannes Berg if (!key->local->debugfs.keys) 238e9f207f0SJiri Benc return; 239e9f207f0SJiri Benc 24050339a67SJohannes Berg sprintf(buf, "%d", keycount); 241d9c58f30SJohannes Berg key->debugfs.cnt = keycount; 24250339a67SJohannes Berg keycount++; 243e9f207f0SJiri Benc key->debugfs.dir = debugfs_create_dir(buf, 2443b96766fSJohannes Berg key->local->debugfs.keys); 245e9f207f0SJiri Benc 246e9f207f0SJiri Benc if (!key->debugfs.dir) 247e9f207f0SJiri Benc return; 248e9f207f0SJiri Benc 2493b96766fSJohannes Berg rcu_read_lock(); 2503b96766fSJohannes Berg sta = rcu_dereference(key->sta); 2513b96766fSJohannes Berg if (sta) 2520c68ae26SJohannes Berg sprintf(buf, "../../stations/%pM", sta->sta.addr); 2533b96766fSJohannes Berg rcu_read_unlock(); 2543b96766fSJohannes Berg 2553b96766fSJohannes Berg /* using sta as a boolean is fine outside RCU lock */ 2563b96766fSJohannes Berg if (sta) 2573b96766fSJohannes Berg key->debugfs.stalink = 2583b96766fSJohannes Berg debugfs_create_symlink("station", key->debugfs.dir, buf); 2593b96766fSJohannes Berg 260e9f207f0SJiri Benc DEBUGFS_ADD(keylen); 2618f20fc24SJohannes Berg DEBUGFS_ADD(flags); 262e9f207f0SJiri Benc DEBUGFS_ADD(keyidx); 263e9f207f0SJiri Benc DEBUGFS_ADD(hw_key_idx); 264e9f207f0SJiri Benc DEBUGFS_ADD(tx_rx_count); 265e9f207f0SJiri Benc DEBUGFS_ADD(algorithm); 266e9f207f0SJiri Benc DEBUGFS_ADD(tx_spec); 267e9f207f0SJiri Benc DEBUGFS_ADD(rx_spec); 268e9f207f0SJiri Benc DEBUGFS_ADD(replays); 2693cfcf6acSJouni Malinen DEBUGFS_ADD(icverrors); 270e9f207f0SJiri Benc DEBUGFS_ADD(key); 271e7a64f12SJohannes Berg DEBUGFS_ADD(ifindex); 272e9f207f0SJiri Benc }; 273e9f207f0SJiri Benc 274e9f207f0SJiri Benc #define DEBUGFS_DEL(name) \ 275e9f207f0SJiri Benc debugfs_remove(key->debugfs.name); key->debugfs.name = NULL; 276e9f207f0SJiri Benc 277e9f207f0SJiri Benc void ieee80211_debugfs_key_remove(struct ieee80211_key *key) 278e9f207f0SJiri Benc { 279e9f207f0SJiri Benc if (!key) 280e9f207f0SJiri Benc return; 281e9f207f0SJiri Benc 282e9f207f0SJiri Benc DEBUGFS_DEL(keylen); 2838f20fc24SJohannes Berg DEBUGFS_DEL(flags); 284e9f207f0SJiri Benc DEBUGFS_DEL(keyidx); 285e9f207f0SJiri Benc DEBUGFS_DEL(hw_key_idx); 286e9f207f0SJiri Benc DEBUGFS_DEL(tx_rx_count); 287e9f207f0SJiri Benc DEBUGFS_DEL(algorithm); 288e9f207f0SJiri Benc DEBUGFS_DEL(tx_spec); 289e9f207f0SJiri Benc DEBUGFS_DEL(rx_spec); 290e9f207f0SJiri Benc DEBUGFS_DEL(replays); 2913cfcf6acSJouni Malinen DEBUGFS_DEL(icverrors); 292e9f207f0SJiri Benc DEBUGFS_DEL(key); 293e7a64f12SJohannes Berg DEBUGFS_DEL(ifindex); 294e9f207f0SJiri Benc 295e9f207f0SJiri Benc debugfs_remove(key->debugfs.stalink); 296e9f207f0SJiri Benc key->debugfs.stalink = NULL; 297e9f207f0SJiri Benc debugfs_remove(key->debugfs.dir); 298e9f207f0SJiri Benc key->debugfs.dir = NULL; 299e9f207f0SJiri Benc } 300e9f207f0SJiri Benc void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata) 301e9f207f0SJiri Benc { 302e9f207f0SJiri Benc char buf[50]; 30378520cadSJohannes Berg struct ieee80211_key *key; 304e9f207f0SJiri Benc 305e9f207f0SJiri Benc if (!sdata->debugfsdir) 306e9f207f0SJiri Benc return; 307e9f207f0SJiri Benc 30878520cadSJohannes Berg /* this is running under the key lock */ 30978520cadSJohannes Berg 31078520cadSJohannes Berg key = sdata->default_key; 31178520cadSJohannes Berg if (key) { 31278520cadSJohannes Berg sprintf(buf, "../keys/%d", key->debugfs.cnt); 3132b58b209SJouni Malinen sdata->common_debugfs.default_key = 31478520cadSJohannes Berg debugfs_create_symlink("default_key", 31578520cadSJohannes Berg sdata->debugfsdir, buf); 31678520cadSJohannes Berg } else 31778520cadSJohannes Berg ieee80211_debugfs_key_remove_default(sdata); 318e9f207f0SJiri Benc } 31978520cadSJohannes Berg 320e9f207f0SJiri Benc void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata) 321e9f207f0SJiri Benc { 322e9f207f0SJiri Benc if (!sdata) 323e9f207f0SJiri Benc return; 324e9f207f0SJiri Benc 3252b58b209SJouni Malinen debugfs_remove(sdata->common_debugfs.default_key); 3262b58b209SJouni Malinen sdata->common_debugfs.default_key = NULL; 327e9f207f0SJiri Benc } 328e9f207f0SJiri Benc 3293cfcf6acSJouni Malinen void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) 3303cfcf6acSJouni Malinen { 3313cfcf6acSJouni Malinen char buf[50]; 3323cfcf6acSJouni Malinen struct ieee80211_key *key; 3333cfcf6acSJouni Malinen 3343cfcf6acSJouni Malinen if (!sdata->debugfsdir) 3353cfcf6acSJouni Malinen return; 3363cfcf6acSJouni Malinen 3373cfcf6acSJouni Malinen /* this is running under the key lock */ 3383cfcf6acSJouni Malinen 3393cfcf6acSJouni Malinen key = sdata->default_mgmt_key; 3403cfcf6acSJouni Malinen if (key) { 3413cfcf6acSJouni Malinen sprintf(buf, "../keys/%d", key->debugfs.cnt); 3423cfcf6acSJouni Malinen sdata->common_debugfs.default_mgmt_key = 3433cfcf6acSJouni Malinen debugfs_create_symlink("default_mgmt_key", 3443cfcf6acSJouni Malinen sdata->debugfsdir, buf); 3453cfcf6acSJouni Malinen } else 3463cfcf6acSJouni Malinen ieee80211_debugfs_key_remove_mgmt_default(sdata); 3473cfcf6acSJouni Malinen } 3483cfcf6acSJouni Malinen 3493cfcf6acSJouni Malinen void ieee80211_debugfs_key_remove_mgmt_default(struct ieee80211_sub_if_data *sdata) 3503cfcf6acSJouni Malinen { 3513cfcf6acSJouni Malinen if (!sdata) 3523cfcf6acSJouni Malinen return; 3533cfcf6acSJouni Malinen 3543cfcf6acSJouni Malinen debugfs_remove(sdata->common_debugfs.default_mgmt_key); 3553cfcf6acSJouni Malinen sdata->common_debugfs.default_mgmt_key = NULL; 3563cfcf6acSJouni Malinen } 3573cfcf6acSJouni Malinen 358e9f207f0SJiri Benc void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, 359e9f207f0SJiri Benc struct sta_info *sta) 360e9f207f0SJiri Benc { 361e9f207f0SJiri Benc debugfs_remove(key->debugfs.stalink); 362e9f207f0SJiri Benc key->debugfs.stalink = NULL; 363e9f207f0SJiri Benc } 364