Commit | Line | Data |
---|---|---|
e9f207f0 JB |
1 | /* |
2 | * Copyright 2003-2005 Devicescape Software, Inc. | |
3 | * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> | |
4 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/kobject.h> | |
5a0e3ad6 | 12 | #include <linux/slab.h> |
e9f207f0 | 13 | #include "ieee80211_i.h" |
2c8dccc7 | 14 | #include "key.h" |
e9f207f0 JB |
15 | #include "debugfs.h" |
16 | #include "debugfs_key.h" | |
17 | ||
8f20fc24 | 18 | #define KEY_READ(name, prop, buflen, format_string) \ |
e9f207f0 JB |
19 | static ssize_t key_##name##_read(struct file *file, \ |
20 | char __user *userbuf, \ | |
21 | size_t count, loff_t *ppos) \ | |
22 | { \ | |
23 | char buf[buflen]; \ | |
24 | struct ieee80211_key *key = file->private_data; \ | |
8f20fc24 | 25 | int res = scnprintf(buf, buflen, format_string, key->prop); \ |
e9f207f0 JB |
26 | return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ |
27 | } | |
8f20fc24 | 28 | #define KEY_READ_D(name) KEY_READ(name, name, 20, "%d\n") |
11a843b7 | 29 | #define KEY_READ_X(name) KEY_READ(name, name, 20, "0x%x\n") |
e9f207f0 JB |
30 | |
31 | #define KEY_OPS(name) \ | |
32 | static const struct file_operations key_ ##name## _ops = { \ | |
33 | .read = key_##name##_read, \ | |
34 | .open = mac80211_open_file_generic, \ | |
35 | } | |
36 | ||
37 | #define KEY_FILE(name, format) \ | |
38 | KEY_READ_##format(name) \ | |
39 | KEY_OPS(name) | |
40 | ||
8f20fc24 JB |
41 | #define KEY_CONF_READ(name, buflen, format_string) \ |
42 | KEY_READ(conf_##name, conf.name, buflen, format_string) | |
43 | #define KEY_CONF_READ_D(name) KEY_CONF_READ(name, 20, "%d\n") | |
8f20fc24 JB |
44 | |
45 | #define KEY_CONF_OPS(name) \ | |
46 | static const struct file_operations key_ ##name## _ops = { \ | |
47 | .read = key_conf_##name##_read, \ | |
48 | .open = mac80211_open_file_generic, \ | |
49 | } | |
50 | ||
51 | #define KEY_CONF_FILE(name, format) \ | |
52 | KEY_CONF_READ_##format(name) \ | |
53 | KEY_CONF_OPS(name) | |
54 | ||
55 | KEY_CONF_FILE(keylen, D); | |
56 | KEY_CONF_FILE(keyidx, D); | |
57 | KEY_CONF_FILE(hw_key_idx, D); | |
11a843b7 | 58 | KEY_FILE(flags, X); |
e9f207f0 | 59 | KEY_FILE(tx_rx_count, D); |
47846c9b | 60 | KEY_READ(ifindex, sdata->name, IFNAMSIZ + 2, "%s\n"); |
e7a64f12 | 61 | KEY_OPS(ifindex); |
e9f207f0 JB |
62 | |
63 | static ssize_t key_algorithm_read(struct file *file, | |
64 | char __user *userbuf, | |
65 | size_t count, loff_t *ppos) | |
66 | { | |
67 | char *alg; | |
68 | struct ieee80211_key *key = file->private_data; | |
69 | ||
8f20fc24 | 70 | switch (key->conf.alg) { |
e9f207f0 JB |
71 | case ALG_WEP: |
72 | alg = "WEP\n"; | |
73 | break; | |
74 | case ALG_TKIP: | |
75 | alg = "TKIP\n"; | |
76 | break; | |
77 | case ALG_CCMP: | |
78 | alg = "CCMP\n"; | |
79 | break; | |
3cfcf6ac JM |
80 | case ALG_AES_CMAC: |
81 | alg = "AES-128-CMAC\n"; | |
82 | break; | |
e9f207f0 JB |
83 | default: |
84 | return 0; | |
85 | } | |
86 | return simple_read_from_buffer(userbuf, count, ppos, alg, strlen(alg)); | |
87 | } | |
88 | KEY_OPS(algorithm); | |
89 | ||
90 | static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, | |
91 | size_t count, loff_t *ppos) | |
92 | { | |
93 | const u8 *tpn; | |
94 | char buf[20]; | |
95 | int len; | |
96 | struct ieee80211_key *key = file->private_data; | |
97 | ||
8f20fc24 | 98 | switch (key->conf.alg) { |
e9f207f0 JB |
99 | case ALG_WEP: |
100 | len = scnprintf(buf, sizeof(buf), "\n"); | |
50339a67 | 101 | break; |
e9f207f0 JB |
102 | case ALG_TKIP: |
103 | len = scnprintf(buf, sizeof(buf), "%08x %04x\n", | |
b0f76b33 HH |
104 | key->u.tkip.tx.iv32, |
105 | key->u.tkip.tx.iv16); | |
50339a67 | 106 | break; |
e9f207f0 JB |
107 | case ALG_CCMP: |
108 | tpn = key->u.ccmp.tx_pn; | |
109 | len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", | |
110 | tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); | |
50339a67 | 111 | break; |
3cfcf6ac JM |
112 | case ALG_AES_CMAC: |
113 | tpn = key->u.aes_cmac.tx_pn; | |
114 | len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", | |
115 | tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], | |
116 | tpn[5]); | |
117 | break; | |
e9f207f0 JB |
118 | default: |
119 | return 0; | |
120 | } | |
121 | return simple_read_from_buffer(userbuf, count, ppos, buf, len); | |
122 | } | |
123 | KEY_OPS(tx_spec); | |
124 | ||
125 | static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, | |
126 | size_t count, loff_t *ppos) | |
127 | { | |
128 | struct ieee80211_key *key = file->private_data; | |
129 | char buf[14*NUM_RX_DATA_QUEUES+1], *p = buf; | |
130 | int i, len; | |
131 | const u8 *rpn; | |
132 | ||
8f20fc24 | 133 | switch (key->conf.alg) { |
e9f207f0 JB |
134 | case ALG_WEP: |
135 | len = scnprintf(buf, sizeof(buf), "\n"); | |
50339a67 | 136 | break; |
e9f207f0 JB |
137 | case ALG_TKIP: |
138 | for (i = 0; i < NUM_RX_DATA_QUEUES; i++) | |
139 | p += scnprintf(p, sizeof(buf)+buf-p, | |
140 | "%08x %04x\n", | |
b0f76b33 HH |
141 | key->u.tkip.rx[i].iv32, |
142 | key->u.tkip.rx[i].iv16); | |
e9f207f0 | 143 | len = p - buf; |
50339a67 | 144 | break; |
e9f207f0 JB |
145 | case ALG_CCMP: |
146 | for (i = 0; i < NUM_RX_DATA_QUEUES; i++) { | |
147 | rpn = key->u.ccmp.rx_pn[i]; | |
148 | p += scnprintf(p, sizeof(buf)+buf-p, | |
149 | "%02x%02x%02x%02x%02x%02x\n", | |
150 | rpn[0], rpn[1], rpn[2], | |
151 | rpn[3], rpn[4], rpn[5]); | |
152 | } | |
153 | len = p - buf; | |
50339a67 | 154 | break; |
3cfcf6ac JM |
155 | case ALG_AES_CMAC: |
156 | rpn = key->u.aes_cmac.rx_pn; | |
157 | p += scnprintf(p, sizeof(buf)+buf-p, | |
158 | "%02x%02x%02x%02x%02x%02x\n", | |
159 | rpn[0], rpn[1], rpn[2], | |
160 | rpn[3], rpn[4], rpn[5]); | |
161 | len = p - buf; | |
162 | break; | |
e9f207f0 JB |
163 | default: |
164 | return 0; | |
165 | } | |
166 | return simple_read_from_buffer(userbuf, count, ppos, buf, len); | |
167 | } | |
168 | KEY_OPS(rx_spec); | |
169 | ||
170 | static ssize_t key_replays_read(struct file *file, char __user *userbuf, | |
171 | size_t count, loff_t *ppos) | |
172 | { | |
173 | struct ieee80211_key *key = file->private_data; | |
174 | char buf[20]; | |
175 | int len; | |
176 | ||
3cfcf6ac JM |
177 | switch (key->conf.alg) { |
178 | case ALG_CCMP: | |
179 | len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); | |
180 | break; | |
181 | case ALG_AES_CMAC: | |
182 | len = scnprintf(buf, sizeof(buf), "%u\n", | |
183 | key->u.aes_cmac.replays); | |
184 | break; | |
185 | default: | |
e9f207f0 | 186 | return 0; |
3cfcf6ac | 187 | } |
e9f207f0 JB |
188 | return simple_read_from_buffer(userbuf, count, ppos, buf, len); |
189 | } | |
190 | KEY_OPS(replays); | |
191 | ||
3cfcf6ac JM |
192 | static ssize_t key_icverrors_read(struct file *file, char __user *userbuf, |
193 | size_t count, loff_t *ppos) | |
194 | { | |
195 | struct ieee80211_key *key = file->private_data; | |
196 | char buf[20]; | |
197 | int len; | |
198 | ||
199 | switch (key->conf.alg) { | |
200 | case ALG_AES_CMAC: | |
201 | len = scnprintf(buf, sizeof(buf), "%u\n", | |
202 | key->u.aes_cmac.icverrors); | |
203 | break; | |
204 | default: | |
205 | return 0; | |
206 | } | |
207 | return simple_read_from_buffer(userbuf, count, ppos, buf, len); | |
208 | } | |
209 | KEY_OPS(icverrors); | |
210 | ||
e9f207f0 JB |
211 | static ssize_t key_key_read(struct file *file, char __user *userbuf, |
212 | size_t count, loff_t *ppos) | |
213 | { | |
214 | struct ieee80211_key *key = file->private_data; | |
8f20fc24 | 215 | int i, res, bufsize = 2 * key->conf.keylen + 2; |
e9f207f0 JB |
216 | char *buf = kmalloc(bufsize, GFP_KERNEL); |
217 | char *p = buf; | |
218 | ||
8f20fc24 JB |
219 | for (i = 0; i < key->conf.keylen; i++) |
220 | p += scnprintf(p, bufsize + buf - p, "%02x", key->conf.key[i]); | |
e9f207f0 JB |
221 | p += scnprintf(p, bufsize+buf-p, "\n"); |
222 | res = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); | |
223 | kfree(buf); | |
224 | return res; | |
225 | } | |
226 | KEY_OPS(key); | |
227 | ||
228 | #define DEBUGFS_ADD(name) \ | |
7bcfaf2f JB |
229 | debugfs_create_file(#name, 0400, key->debugfs.dir, \ |
230 | key, &key_##name##_ops); | |
e9f207f0 | 231 | |
3b96766f JB |
232 | void ieee80211_debugfs_key_add(struct ieee80211_key *key) |
233 | { | |
50339a67 | 234 | static int keycount; |
3b96766f | 235 | char buf[50]; |
3b96766f | 236 | struct sta_info *sta; |
e9f207f0 | 237 | |
3b96766f | 238 | if (!key->local->debugfs.keys) |
e9f207f0 JB |
239 | return; |
240 | ||
50339a67 | 241 | sprintf(buf, "%d", keycount); |
d9c58f30 | 242 | key->debugfs.cnt = keycount; |
50339a67 | 243 | keycount++; |
e9f207f0 | 244 | key->debugfs.dir = debugfs_create_dir(buf, |
3b96766f | 245 | key->local->debugfs.keys); |
e9f207f0 JB |
246 | |
247 | if (!key->debugfs.dir) | |
248 | return; | |
249 | ||
3b96766f JB |
250 | rcu_read_lock(); |
251 | sta = rcu_dereference(key->sta); | |
252 | if (sta) | |
0c68ae26 | 253 | sprintf(buf, "../../stations/%pM", sta->sta.addr); |
3b96766f JB |
254 | rcu_read_unlock(); |
255 | ||
256 | /* using sta as a boolean is fine outside RCU lock */ | |
257 | if (sta) | |
258 | key->debugfs.stalink = | |
259 | debugfs_create_symlink("station", key->debugfs.dir, buf); | |
260 | ||
e9f207f0 | 261 | DEBUGFS_ADD(keylen); |
8f20fc24 | 262 | DEBUGFS_ADD(flags); |
e9f207f0 JB |
263 | DEBUGFS_ADD(keyidx); |
264 | DEBUGFS_ADD(hw_key_idx); | |
265 | DEBUGFS_ADD(tx_rx_count); | |
266 | DEBUGFS_ADD(algorithm); | |
267 | DEBUGFS_ADD(tx_spec); | |
268 | DEBUGFS_ADD(rx_spec); | |
269 | DEBUGFS_ADD(replays); | |
3cfcf6ac | 270 | DEBUGFS_ADD(icverrors); |
e9f207f0 | 271 | DEBUGFS_ADD(key); |
e7a64f12 | 272 | DEBUGFS_ADD(ifindex); |
e9f207f0 JB |
273 | }; |
274 | ||
e9f207f0 JB |
275 | void ieee80211_debugfs_key_remove(struct ieee80211_key *key) |
276 | { | |
277 | if (!key) | |
278 | return; | |
279 | ||
7bcfaf2f | 280 | debugfs_remove_recursive(key->debugfs.dir); |
e9f207f0 JB |
281 | key->debugfs.dir = NULL; |
282 | } | |
283 | void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata) | |
284 | { | |
285 | char buf[50]; | |
78520cad | 286 | struct ieee80211_key *key; |
e9f207f0 | 287 | |
7bcfaf2f | 288 | if (!sdata->debugfs.dir) |
e9f207f0 JB |
289 | return; |
290 | ||
78520cad JB |
291 | /* this is running under the key lock */ |
292 | ||
293 | key = sdata->default_key; | |
294 | if (key) { | |
295 | sprintf(buf, "../keys/%d", key->debugfs.cnt); | |
7bcfaf2f | 296 | sdata->debugfs.default_key = |
78520cad | 297 | debugfs_create_symlink("default_key", |
7bcfaf2f | 298 | sdata->debugfs.dir, buf); |
78520cad JB |
299 | } else |
300 | ieee80211_debugfs_key_remove_default(sdata); | |
e9f207f0 | 301 | } |
78520cad | 302 | |
e9f207f0 JB |
303 | void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata) |
304 | { | |
305 | if (!sdata) | |
306 | return; | |
307 | ||
7bcfaf2f JB |
308 | debugfs_remove(sdata->debugfs.default_key); |
309 | sdata->debugfs.default_key = NULL; | |
e9f207f0 | 310 | } |
e9f207f0 | 311 | |
3cfcf6ac JM |
312 | void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) |
313 | { | |
314 | char buf[50]; | |
315 | struct ieee80211_key *key; | |
316 | ||
7bcfaf2f | 317 | if (!sdata->debugfs.dir) |
3cfcf6ac JM |
318 | return; |
319 | ||
320 | /* this is running under the key lock */ | |
321 | ||
322 | key = sdata->default_mgmt_key; | |
323 | if (key) { | |
324 | sprintf(buf, "../keys/%d", key->debugfs.cnt); | |
7bcfaf2f | 325 | sdata->debugfs.default_mgmt_key = |
3cfcf6ac | 326 | debugfs_create_symlink("default_mgmt_key", |
7bcfaf2f | 327 | sdata->debugfs.dir, buf); |
3cfcf6ac JM |
328 | } else |
329 | ieee80211_debugfs_key_remove_mgmt_default(sdata); | |
330 | } | |
331 | ||
332 | void ieee80211_debugfs_key_remove_mgmt_default(struct ieee80211_sub_if_data *sdata) | |
333 | { | |
334 | if (!sdata) | |
335 | return; | |
336 | ||
7bcfaf2f JB |
337 | debugfs_remove(sdata->debugfs.default_mgmt_key); |
338 | sdata->debugfs.default_mgmt_key = NULL; | |
3cfcf6ac JM |
339 | } |
340 | ||
e9f207f0 JB |
341 | void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, |
342 | struct sta_info *sta) | |
343 | { | |
344 | debugfs_remove(key->debugfs.stalink); | |
345 | key->debugfs.stalink = NULL; | |
346 | } |