2007-05-05 22:46:38 +04:00
/*
* Copyright 2003 - 2005 Devicescape Software , Inc .
* Copyright ( c ) 2006 Jiri Benc < jbenc @ suse . cz >
* Copyright 2007 Johannes Berg < johannes @ sipsolutions . net >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kobject.h>
# include "ieee80211_i.h"
2008-04-08 23:14:40 +04:00
# include "key.h"
2007-05-05 22:46:38 +04:00
# include "debugfs.h"
# include "debugfs_key.h"
2007-08-29 01:01:54 +04:00
# define KEY_READ(name, prop, buflen, format_string) \
2007-05-05 22:46:38 +04:00
static ssize_t key_ # # name # # _read ( struct file * file , \
char __user * userbuf , \
size_t count , loff_t * ppos ) \
{ \
char buf [ buflen ] ; \
struct ieee80211_key * key = file - > private_data ; \
2007-08-29 01:01:54 +04:00
int res = scnprintf ( buf , buflen , format_string , key - > prop ) ; \
2007-05-05 22:46:38 +04:00
return simple_read_from_buffer ( userbuf , count , ppos , buf , res ) ; \
}
2007-08-29 01:01:54 +04:00
# define KEY_READ_D(name) KEY_READ(name, name, 20, "%d\n")
2007-08-29 01:01:55 +04:00
# define KEY_READ_X(name) KEY_READ(name, name, 20, "0x%x\n")
2007-05-05 22:46:38 +04:00
# define KEY_OPS(name) \
static const struct file_operations key_ # # name # # _ops = { \
. read = key_ # # name # # _read , \
. open = mac80211_open_file_generic , \
}
# define KEY_FILE(name, format) \
KEY_READ_ # # format ( name ) \
KEY_OPS ( name )
2007-08-29 01:01:54 +04:00
# define KEY_CONF_READ(name, buflen, format_string) \
KEY_READ ( conf_ # # name , conf . name , buflen , format_string )
# define KEY_CONF_READ_D(name) KEY_CONF_READ(name, 20, "%d\n")
# define KEY_CONF_OPS(name) \
static const struct file_operations key_ # # name # # _ops = { \
. read = key_conf_ # # name # # _read , \
. open = mac80211_open_file_generic , \
}
# define KEY_CONF_FILE(name, format) \
KEY_CONF_READ_ # # format ( name ) \
KEY_CONF_OPS ( name )
KEY_CONF_FILE ( keylen , D ) ;
KEY_CONF_FILE ( keyidx , D ) ;
KEY_CONF_FILE ( hw_key_idx , D ) ;
2007-08-29 01:01:55 +04:00
KEY_FILE ( flags , X ) ;
2007-05-05 22:46:38 +04:00
KEY_FILE ( tx_rx_count , D ) ;
2009-11-25 19:46:19 +03:00
KEY_READ ( ifindex , sdata - > name , IFNAMSIZ + 2 , " %s \n " ) ;
2007-08-29 01:01:55 +04:00
KEY_OPS ( ifindex ) ;
2007-05-05 22:46:38 +04:00
static ssize_t key_algorithm_read ( struct file * file ,
char __user * userbuf ,
size_t count , loff_t * ppos )
{
char * alg ;
struct ieee80211_key * key = file - > private_data ;
2007-08-29 01:01:54 +04:00
switch ( key - > conf . alg ) {
2007-05-05 22:46:38 +04:00
case ALG_WEP :
alg = " WEP \n " ;
break ;
case ALG_TKIP :
alg = " TKIP \n " ;
break ;
case ALG_CCMP :
alg = " CCMP \n " ;
break ;
2009-01-08 14:32:02 +03:00
case ALG_AES_CMAC :
alg = " AES-128-CMAC \n " ;
break ;
2007-05-05 22:46:38 +04:00
default :
return 0 ;
}
return simple_read_from_buffer ( userbuf , count , ppos , alg , strlen ( alg ) ) ;
}
KEY_OPS ( algorithm ) ;
static ssize_t key_tx_spec_read ( struct file * file , char __user * userbuf ,
size_t count , loff_t * ppos )
{
const u8 * tpn ;
char buf [ 20 ] ;
int len ;
struct ieee80211_key * key = file - > private_data ;
2007-08-29 01:01:54 +04:00
switch ( key - > conf . alg ) {
2007-05-05 22:46:38 +04:00
case ALG_WEP :
len = scnprintf ( buf , sizeof ( buf ) , " \n " ) ;
2007-08-29 01:01:52 +04:00
break ;
2007-05-05 22:46:38 +04:00
case ALG_TKIP :
len = scnprintf ( buf , sizeof ( buf ) , " %08x %04x \n " ,
2008-05-15 03:26:19 +04:00
key - > u . tkip . tx . iv32 ,
key - > u . tkip . tx . iv16 ) ;
2007-08-29 01:01:52 +04:00
break ;
2007-05-05 22:46:38 +04:00
case ALG_CCMP :
tpn = key - > u . ccmp . tx_pn ;
len = scnprintf ( buf , sizeof ( buf ) , " %02x%02x%02x%02x%02x%02x \n " ,
tpn [ 0 ] , tpn [ 1 ] , tpn [ 2 ] , tpn [ 3 ] , tpn [ 4 ] , tpn [ 5 ] ) ;
2007-08-29 01:01:52 +04:00
break ;
2009-01-08 14:32:02 +03:00
case ALG_AES_CMAC :
tpn = key - > u . aes_cmac . tx_pn ;
len = scnprintf ( buf , sizeof ( buf ) , " %02x%02x%02x%02x%02x%02x \n " ,
tpn [ 0 ] , tpn [ 1 ] , tpn [ 2 ] , tpn [ 3 ] , tpn [ 4 ] ,
tpn [ 5 ] ) ;
break ;
2007-05-05 22:46:38 +04:00
default :
return 0 ;
}
return simple_read_from_buffer ( userbuf , count , ppos , buf , len ) ;
}
KEY_OPS ( tx_spec ) ;
static ssize_t key_rx_spec_read ( struct file * file , char __user * userbuf ,
size_t count , loff_t * ppos )
{
struct ieee80211_key * key = file - > private_data ;
char buf [ 14 * NUM_RX_DATA_QUEUES + 1 ] , * p = buf ;
int i , len ;
const u8 * rpn ;
2007-08-29 01:01:54 +04:00
switch ( key - > conf . alg ) {
2007-05-05 22:46:38 +04:00
case ALG_WEP :
len = scnprintf ( buf , sizeof ( buf ) , " \n " ) ;
2007-08-29 01:01:52 +04:00
break ;
2007-05-05 22:46:38 +04:00
case ALG_TKIP :
for ( i = 0 ; i < NUM_RX_DATA_QUEUES ; i + + )
p + = scnprintf ( p , sizeof ( buf ) + buf - p ,
" %08x %04x \n " ,
2008-05-15 03:26:19 +04:00
key - > u . tkip . rx [ i ] . iv32 ,
key - > u . tkip . rx [ i ] . iv16 ) ;
2007-05-05 22:46:38 +04:00
len = p - buf ;
2007-08-29 01:01:52 +04:00
break ;
2007-05-05 22:46:38 +04:00
case ALG_CCMP :
for ( i = 0 ; i < NUM_RX_DATA_QUEUES ; i + + ) {
rpn = key - > u . ccmp . rx_pn [ i ] ;
p + = scnprintf ( p , sizeof ( buf ) + buf - p ,
" %02x%02x%02x%02x%02x%02x \n " ,
rpn [ 0 ] , rpn [ 1 ] , rpn [ 2 ] ,
rpn [ 3 ] , rpn [ 4 ] , rpn [ 5 ] ) ;
}
len = p - buf ;
2007-08-29 01:01:52 +04:00
break ;
2009-01-08 14:32:02 +03:00
case ALG_AES_CMAC :
rpn = key - > u . aes_cmac . rx_pn ;
p + = scnprintf ( p , sizeof ( buf ) + buf - p ,
" %02x%02x%02x%02x%02x%02x \n " ,
rpn [ 0 ] , rpn [ 1 ] , rpn [ 2 ] ,
rpn [ 3 ] , rpn [ 4 ] , rpn [ 5 ] ) ;
len = p - buf ;
break ;
2007-05-05 22:46:38 +04:00
default :
return 0 ;
}
return simple_read_from_buffer ( userbuf , count , ppos , buf , len ) ;
}
KEY_OPS ( rx_spec ) ;
static ssize_t key_replays_read ( struct file * file , char __user * userbuf ,
size_t count , loff_t * ppos )
{
struct ieee80211_key * key = file - > private_data ;
char buf [ 20 ] ;
int len ;
2009-01-08 14:32:02 +03:00
switch ( key - > conf . alg ) {
case ALG_CCMP :
len = scnprintf ( buf , sizeof ( buf ) , " %u \n " , key - > u . ccmp . replays ) ;
break ;
case ALG_AES_CMAC :
len = scnprintf ( buf , sizeof ( buf ) , " %u \n " ,
key - > u . aes_cmac . replays ) ;
break ;
default :
2007-05-05 22:46:38 +04:00
return 0 ;
2009-01-08 14:32:02 +03:00
}
2007-05-05 22:46:38 +04:00
return simple_read_from_buffer ( userbuf , count , ppos , buf , len ) ;
}
KEY_OPS ( replays ) ;
2009-01-08 14:32:02 +03:00
static ssize_t key_icverrors_read ( struct file * file , char __user * userbuf ,
size_t count , loff_t * ppos )
{
struct ieee80211_key * key = file - > private_data ;
char buf [ 20 ] ;
int len ;
switch ( key - > conf . alg ) {
case ALG_AES_CMAC :
len = scnprintf ( buf , sizeof ( buf ) , " %u \n " ,
key - > u . aes_cmac . icverrors ) ;
break ;
default :
return 0 ;
}
return simple_read_from_buffer ( userbuf , count , ppos , buf , len ) ;
}
KEY_OPS ( icverrors ) ;
2007-05-05 22:46:38 +04:00
static ssize_t key_key_read ( struct file * file , char __user * userbuf ,
size_t count , loff_t * ppos )
{
struct ieee80211_key * key = file - > private_data ;
2007-08-29 01:01:54 +04:00
int i , res , bufsize = 2 * key - > conf . keylen + 2 ;
2007-05-05 22:46:38 +04:00
char * buf = kmalloc ( bufsize , GFP_KERNEL ) ;
char * p = buf ;
2007-08-29 01:01:54 +04:00
for ( i = 0 ; i < key - > conf . keylen ; i + + )
p + = scnprintf ( p , bufsize + buf - p , " %02x " , key - > conf . key [ i ] ) ;
2007-05-05 22:46:38 +04:00
p + = scnprintf ( p , bufsize + buf - p , " \n " ) ;
res = simple_read_from_buffer ( userbuf , count , ppos , buf , p - buf ) ;
kfree ( buf ) ;
return res ;
}
KEY_OPS ( key ) ;
# define DEBUGFS_ADD(name) \
2009-10-27 14:59:03 +03:00
debugfs_create_file ( # name , 0400 , key - > debugfs . dir , \
key , & key_ # # name # # _ops ) ;
2007-05-05 22:46:38 +04:00
2008-04-08 19:56:52 +04:00
void ieee80211_debugfs_key_add ( struct ieee80211_key * key )
{
2007-08-29 01:01:52 +04:00
static int keycount ;
2008-04-08 19:56:52 +04:00
char buf [ 50 ] ;
struct sta_info * sta ;
2007-05-05 22:46:38 +04:00
2008-04-08 19:56:52 +04:00
if ( ! key - > local - > debugfs . keys )
2007-05-05 22:46:38 +04:00
return ;
2007-08-29 01:01:52 +04:00
sprintf ( buf , " %d " , keycount ) ;
2008-04-09 00:46:36 +04:00
key - > debugfs . cnt = keycount ;
2007-08-29 01:01:52 +04:00
keycount + + ;
2007-05-05 22:46:38 +04:00
key - > debugfs . dir = debugfs_create_dir ( buf ,
2008-04-08 19:56:52 +04:00
key - > local - > debugfs . keys ) ;
2007-05-05 22:46:38 +04:00
if ( ! key - > debugfs . dir )
return ;
2008-04-08 19:56:52 +04:00
rcu_read_lock ( ) ;
sta = rcu_dereference ( key - > sta ) ;
if ( sta )
2008-10-28 01:56:10 +03:00
sprintf ( buf , " ../../stations/%pM " , sta - > sta . addr ) ;
2008-04-08 19:56:52 +04:00
rcu_read_unlock ( ) ;
/* using sta as a boolean is fine outside RCU lock */
if ( sta )
key - > debugfs . stalink =
debugfs_create_symlink ( " station " , key - > debugfs . dir , buf ) ;
2007-05-05 22:46:38 +04:00
DEBUGFS_ADD ( keylen ) ;
2007-08-29 01:01:54 +04:00
DEBUGFS_ADD ( flags ) ;
2007-05-05 22:46:38 +04:00
DEBUGFS_ADD ( keyidx ) ;
DEBUGFS_ADD ( hw_key_idx ) ;
DEBUGFS_ADD ( tx_rx_count ) ;
DEBUGFS_ADD ( algorithm ) ;
DEBUGFS_ADD ( tx_spec ) ;
DEBUGFS_ADD ( rx_spec ) ;
DEBUGFS_ADD ( replays ) ;
2009-01-08 14:32:02 +03:00
DEBUGFS_ADD ( icverrors ) ;
2007-05-05 22:46:38 +04:00
DEBUGFS_ADD ( key ) ;
2007-08-29 01:01:55 +04:00
DEBUGFS_ADD ( ifindex ) ;
2007-05-05 22:46:38 +04:00
} ;
void ieee80211_debugfs_key_remove ( struct ieee80211_key * key )
{
if ( ! key )
return ;
2009-10-27 14:59:03 +03:00
debugfs_remove_recursive ( key - > debugfs . dir ) ;
2007-05-05 22:46:38 +04:00
key - > debugfs . dir = NULL ;
}
void ieee80211_debugfs_key_add_default ( struct ieee80211_sub_if_data * sdata )
{
char buf [ 50 ] ;
2008-05-03 03:04:47 +04:00
struct ieee80211_key * key ;
2007-05-05 22:46:38 +04:00
2009-10-27 14:59:03 +03:00
if ( ! sdata - > debugfs . dir )
2007-05-05 22:46:38 +04:00
return ;
2008-05-03 03:04:47 +04:00
/* this is running under the key lock */
key = sdata - > default_key ;
if ( key ) {
sprintf ( buf , " ../keys/%d " , key - > debugfs . cnt ) ;
2009-10-27 14:59:03 +03:00
sdata - > debugfs . default_key =
2008-05-03 03:04:47 +04:00
debugfs_create_symlink ( " default_key " ,
2009-10-27 14:59:03 +03:00
sdata - > debugfs . dir , buf ) ;
2008-05-03 03:04:47 +04:00
} else
ieee80211_debugfs_key_remove_default ( sdata ) ;
2007-05-05 22:46:38 +04:00
}
2008-05-03 03:04:47 +04:00
2007-05-05 22:46:38 +04:00
void ieee80211_debugfs_key_remove_default ( struct ieee80211_sub_if_data * sdata )
{
if ( ! sdata )
return ;
2009-10-27 14:59:03 +03:00
debugfs_remove ( sdata - > debugfs . default_key ) ;
sdata - > debugfs . default_key = NULL ;
2007-05-05 22:46:38 +04:00
}
2009-01-08 14:32:02 +03:00
void ieee80211_debugfs_key_add_mgmt_default ( struct ieee80211_sub_if_data * sdata )
{
char buf [ 50 ] ;
struct ieee80211_key * key ;
2009-10-27 14:59:03 +03:00
if ( ! sdata - > debugfs . dir )
2009-01-08 14:32:02 +03:00
return ;
/* this is running under the key lock */
key = sdata - > default_mgmt_key ;
if ( key ) {
sprintf ( buf , " ../keys/%d " , key - > debugfs . cnt ) ;
2009-10-27 14:59:03 +03:00
sdata - > debugfs . default_mgmt_key =
2009-01-08 14:32:02 +03:00
debugfs_create_symlink ( " default_mgmt_key " ,
2009-10-27 14:59:03 +03:00
sdata - > debugfs . dir , buf ) ;
2009-01-08 14:32:02 +03:00
} else
ieee80211_debugfs_key_remove_mgmt_default ( sdata ) ;
}
void ieee80211_debugfs_key_remove_mgmt_default ( struct ieee80211_sub_if_data * sdata )
{
if ( ! sdata )
return ;
2009-10-27 14:59:03 +03:00
debugfs_remove ( sdata - > debugfs . default_mgmt_key ) ;
sdata - > debugfs . default_mgmt_key = NULL ;
2009-01-08 14:32:02 +03:00
}
2007-05-05 22:46:38 +04:00
void ieee80211_debugfs_key_sta_del ( struct ieee80211_key * key ,
struct sta_info * sta )
{
debugfs_remove ( key - > debugfs . stalink ) ;
key - > debugfs . stalink = NULL ;
}