2008-08-04 16:37:44 +02:00
/*
2009-11-08 16:39:55 +01:00
Copyright ( C ) 2004 - 2009 Ivo van Doorn < IvDoorn @ gmail . com >
2008-08-04 16:37:44 +02:00
< http : //rt2x00.serialmonkey.com>
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the
Free Software Foundation , Inc . ,
59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
/*
Module : rt2x00lib
Abstract : rt2x00 crypto specific routines .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include "rt2x00.h"
# include "rt2x00lib.h"
enum cipher rt2x00crypto_key_to_cipher ( struct ieee80211_key_conf * key )
{
switch ( key - > alg ) {
case ALG_WEP :
2009-05-21 21:47:03 +08:00
if ( key - > keylen = = WLAN_KEY_LEN_WEP40 )
2008-08-04 16:37:44 +02:00
return CIPHER_WEP64 ;
else
return CIPHER_WEP128 ;
case ALG_TKIP :
return CIPHER_TKIP ;
case ALG_CCMP :
return CIPHER_AES ;
default :
return CIPHER_NONE ;
}
}
2008-12-03 17:29:48 +01:00
void rt2x00crypto_create_tx_descriptor ( struct queue_entry * entry ,
struct txentry_desc * txdesc )
{
2008-12-20 10:58:33 +01:00
struct rt2x00_dev * rt2x00dev = entry - > queue - > rt2x00dev ;
2008-12-03 17:29:48 +01:00
struct ieee80211_tx_info * tx_info = IEEE80211_SKB_CB ( entry - > skb ) ;
struct ieee80211_key_conf * hw_key = tx_info - > control . hw_key ;
2009-06-16 20:46:45 +02:00
if ( ! test_bit ( CONFIG_SUPPORT_HW_CRYPTO , & rt2x00dev - > flags ) | | ! hw_key )
2008-12-20 10:58:33 +01:00
return ;
2008-12-03 17:29:48 +01:00
__set_bit ( ENTRY_TXD_ENCRYPT , & txdesc - > flags ) ;
txdesc - > cipher = rt2x00crypto_key_to_cipher ( hw_key ) ;
if ( hw_key - > flags & IEEE80211_KEY_FLAG_PAIRWISE )
__set_bit ( ENTRY_TXD_ENCRYPT_PAIRWISE , & txdesc - > flags ) ;
txdesc - > key_idx = hw_key - > hw_key_idx ;
2009-04-26 16:08:50 +02:00
txdesc - > iv_offset = txdesc - > header_length ;
2009-04-26 16:08:30 +02:00
txdesc - > iv_len = hw_key - > iv_len ;
2008-12-03 17:29:48 +01:00
if ( ! ( hw_key - > flags & IEEE80211_KEY_FLAG_GENERATE_IV ) )
__set_bit ( ENTRY_TXD_ENCRYPT_IV , & txdesc - > flags ) ;
if ( ! ( hw_key - > flags & IEEE80211_KEY_FLAG_GENERATE_MMIC ) )
__set_bit ( ENTRY_TXD_ENCRYPT_MMIC , & txdesc - > flags ) ;
}
2008-12-20 10:58:33 +01:00
unsigned int rt2x00crypto_tx_overhead ( struct rt2x00_dev * rt2x00dev ,
struct sk_buff * skb )
2008-08-04 16:37:44 +02:00
{
2008-12-20 10:58:33 +01:00
struct ieee80211_tx_info * tx_info = IEEE80211_SKB_CB ( skb ) ;
2008-08-04 16:37:44 +02:00
struct ieee80211_key_conf * key = tx_info - > control . hw_key ;
unsigned int overhead = 0 ;
2009-06-16 20:46:45 +02:00
if ( ! test_bit ( CONFIG_SUPPORT_HW_CRYPTO , & rt2x00dev - > flags ) | | ! key )
2008-12-20 10:58:33 +01:00
return overhead ;
2008-08-04 16:37:44 +02:00
/*
* Extend frame length to include IV / EIV / ICV / MMIC ,
* note that these lengths should only be added when
* mac80211 does not generate it .
*/
2008-10-05 18:02:48 +02:00
overhead + = key - > icv_len ;
2008-08-04 16:37:44 +02:00
if ( ! ( key - > flags & IEEE80211_KEY_FLAG_GENERATE_IV ) )
2008-10-05 18:02:48 +02:00
overhead + = key - > iv_len ;
2008-08-04 16:37:44 +02:00
if ( ! ( key - > flags & IEEE80211_KEY_FLAG_GENERATE_MMIC ) ) {
if ( key - > alg = = ALG_TKIP )
overhead + = 8 ;
}
return overhead ;
}
2009-04-26 16:08:30 +02:00
void rt2x00crypto_tx_copy_iv ( struct sk_buff * skb , struct txentry_desc * txdesc )
2008-12-02 18:20:42 +01:00
{
struct skb_frame_desc * skbdesc = get_skb_frame_desc ( skb ) ;
2009-04-26 16:08:30 +02:00
if ( unlikely ( ! txdesc - > iv_len ) )
2008-12-02 18:20:42 +01:00
return ;
/* Copy IV/EIV data */
2009-04-26 16:08:30 +02:00
memcpy ( skbdesc - > iv , skb - > data + txdesc - > iv_offset , txdesc - > iv_len ) ;
2008-12-02 18:20:42 +01:00
}
2009-04-26 16:08:30 +02:00
void rt2x00crypto_tx_remove_iv ( struct sk_buff * skb , struct txentry_desc * txdesc )
2008-08-04 16:37:44 +02:00
{
struct skb_frame_desc * skbdesc = get_skb_frame_desc ( skb ) ;
2009-04-26 16:08:30 +02:00
if ( unlikely ( ! txdesc - > iv_len ) )
2008-08-04 16:37:44 +02:00
return ;
/* Copy IV/EIV data */
2009-04-26 16:08:30 +02:00
memcpy ( skbdesc - > iv , skb - > data + txdesc - > iv_offset , txdesc - > iv_len ) ;
2008-08-04 16:37:44 +02:00
/* Move ieee80211 header */
2009-04-26 16:08:30 +02:00
memmove ( skb - > data + txdesc - > iv_len , skb - > data , txdesc - > iv_offset ) ;
2008-08-04 16:37:44 +02:00
/* Pull buffer to correct size */
2009-04-26 16:08:30 +02:00
skb_pull ( skb , txdesc - > iv_len ) ;
2008-08-04 16:37:44 +02:00
2009-07-17 21:39:19 +02:00
/* IV/EIV data has officially been stripped */
2009-04-26 16:08:50 +02:00
skbdesc - > flags | = SKBDESC_IV_STRIPPED ;
2008-08-04 16:37:44 +02:00
}
2009-04-26 16:08:50 +02:00
void rt2x00crypto_tx_insert_iv ( struct sk_buff * skb , unsigned int header_length )
2008-08-04 16:37:44 +02:00
{
struct skb_frame_desc * skbdesc = get_skb_frame_desc ( skb ) ;
const unsigned int iv_len =
2008-12-02 18:19:48 +01:00
( ( ! ! ( skbdesc - > iv [ 0 ] ) ) * 4 ) + ( ( ! ! ( skbdesc - > iv [ 1 ] ) ) * 4 ) ;
2008-08-04 16:37:44 +02:00
2009-04-26 16:08:50 +02:00
if ( ! ( skbdesc - > flags & SKBDESC_IV_STRIPPED ) )
2008-08-04 16:37:44 +02:00
return ;
skb_push ( skb , iv_len ) ;
/* Move ieee80211 header */
memmove ( skb - > data , skb - > data + iv_len , header_length ) ;
/* Copy IV/EIV data */
2008-12-02 18:19:48 +01:00
memcpy ( skb - > data + header_length , skbdesc - > iv , iv_len ) ;
2008-08-04 16:37:44 +02:00
/* IV/EIV data has returned into the frame */
2009-04-26 16:08:50 +02:00
skbdesc - > flags & = ~ SKBDESC_IV_STRIPPED ;
2008-08-04 16:37:44 +02:00
}
2009-08-29 20:30:45 +02:00
void rt2x00crypto_rx_insert_iv ( struct sk_buff * skb ,
2008-08-04 16:37:44 +02:00
unsigned int header_length ,
struct rxdone_entry_desc * rxdesc )
{
unsigned int payload_len = rxdesc - > size - header_length ;
2009-04-26 16:08:50 +02:00
unsigned int align = ALIGN_SIZE ( skb , header_length ) ;
2008-08-04 16:37:44 +02:00
unsigned int iv_len ;
unsigned int icv_len ;
unsigned int transfer = 0 ;
/*
* WEP64 / WEP128 : Provides IV & ICV
* TKIP : Provides IV / EIV & ICV
* AES : Provies IV / EIV & ICV
*/
switch ( rxdesc - > cipher ) {
case CIPHER_WEP64 :
case CIPHER_WEP128 :
iv_len = 4 ;
icv_len = 4 ;
break ;
case CIPHER_TKIP :
iv_len = 8 ;
icv_len = 4 ;
break ;
case CIPHER_AES :
iv_len = 8 ;
icv_len = 8 ;
break ;
default :
/* Unsupport type */
return ;
}
/*
2009-04-26 16:08:50 +02:00
* Make room for new data . There are 2 possibilities
* either the alignment is already present between
* the 802.11 header and payload . In that case we
* we have to move the header less then the iv_len
* since we can use the already available l2pad bytes
* for the iv data .
* When the alignment must be added manually we must
* move the header more then iv_len since we must
* make room for the payload move as well .
2008-08-04 16:37:44 +02:00
*/
2009-08-29 20:30:45 +02:00
if ( rxdesc - > dev_flags & RXDONE_L2PAD ) {
2009-04-26 16:08:50 +02:00
skb_push ( skb , iv_len - align ) ;
skb_put ( skb , icv_len ) ;
2008-08-04 16:37:44 +02:00
2009-04-26 16:08:50 +02:00
/* Move ieee80211 header */
memmove ( skb - > data + transfer ,
skb - > data + transfer + ( iv_len - align ) ,
header_length ) ;
transfer + = header_length ;
} else {
skb_push ( skb , iv_len + align ) ;
if ( align < icv_len )
skb_put ( skb , icv_len - align ) ;
else if ( align > icv_len )
skb_trim ( skb , rxdesc - > size + iv_len + icv_len ) ;
/* Move ieee80211 header */
memmove ( skb - > data + transfer ,
skb - > data + transfer + iv_len + align ,
header_length ) ;
transfer + = header_length ;
}
2008-08-04 16:37:44 +02:00
2008-12-02 18:19:48 +01:00
/* Copy IV/EIV data */
memcpy ( skb - > data + transfer , rxdesc - > iv , iv_len ) ;
transfer + = iv_len ;
2008-08-04 16:37:44 +02:00
2009-04-26 16:08:50 +02:00
/*
* Move payload for alignment purposes . Note that
* this is only needed when no l2 padding is present .
*/
2009-08-29 20:30:45 +02:00
if ( ! ( rxdesc - > dev_flags & RXDONE_L2PAD ) ) {
2008-08-04 16:37:44 +02:00
memmove ( skb - > data + transfer ,
skb - > data + transfer + align ,
payload_len ) ;
}
/*
* NOTE : Always count the payload as transfered ,
* even when alignment was set to zero . This is required
* for determining the correct offset for the ICV data .
*/
transfer + = payload_len ;
2008-12-02 18:19:48 +01:00
/*
* Copy ICV data
* AES appends 8 bytes , we can ' t fill the upper
* 4 bytes , but mac80211 doesn ' t care about what
* we provide here anyway and strips it immediately .
*/
memcpy ( skb - > data + transfer , & rxdesc - > icv , 4 ) ;
transfer + = icv_len ;
2008-08-04 16:37:44 +02:00
/* IV/EIV/ICV has been inserted into frame */
rxdesc - > size = transfer ;
rxdesc - > flags & = ~ RX_FLAG_IV_STRIPPED ;
}