1 /* 2 Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> 3 <http://rt2x00.serialmonkey.com> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 /* 20 Module: rt2x00lib 21 Abstract: rt2x00 crypto specific routines. 22 */ 23 24 #include <linux/kernel.h> 25 #include <linux/module.h> 26 27 #include "rt2x00.h" 28 #include "rt2x00lib.h" 29 30 enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) 31 { 32 switch (key->cipher) { 33 case WLAN_CIPHER_SUITE_WEP40: 34 return CIPHER_WEP64; 35 case WLAN_CIPHER_SUITE_WEP104: 36 return CIPHER_WEP128; 37 case WLAN_CIPHER_SUITE_TKIP: 38 return CIPHER_TKIP; 39 case WLAN_CIPHER_SUITE_CCMP: 40 return CIPHER_AES; 41 default: 42 return CIPHER_NONE; 43 } 44 } 45 46 void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, 47 struct sk_buff *skb, 48 struct txentry_desc *txdesc) 49 { 50 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); 51 struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; 52 53 if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key) 54 return; 55 56 __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); 57 58 txdesc->cipher = rt2x00crypto_key_to_cipher(hw_key); 59 60 if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE) 61 __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags); 62 63 txdesc->key_idx = hw_key->hw_key_idx; 64 txdesc->iv_offset = txdesc->header_length; 65 txdesc->iv_len = hw_key->iv_len; 66 67 if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) 68 __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags); 69 70 if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) 71 __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags); 72 } 73 74 unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, 75 struct sk_buff *skb) 76 { 77 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); 78 struct ieee80211_key_conf *key = tx_info->control.hw_key; 79 unsigned int overhead = 0; 80 81 if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key) 82 return overhead; 83 84 /* 85 * Extend frame length to include IV/EIV/ICV/MMIC, 86 * note that these lengths should only be added when 87 * mac80211 does not generate it. 88 */ 89 overhead += key->icv_len; 90 91 if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) 92 overhead += key->iv_len; 93 94 if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { 95 if (key->cipher == WLAN_CIPHER_SUITE_TKIP) 96 overhead += 8; 97 } 98 99 return overhead; 100 } 101 102 void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, struct txentry_desc *txdesc) 103 { 104 struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); 105 106 if (unlikely(!txdesc->iv_len)) 107 return; 108 109 /* Copy IV/EIV data */ 110 memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); 111 } 112 113 void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc) 114 { 115 struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); 116 117 if (unlikely(!txdesc->iv_len)) 118 return; 119 120 /* Copy IV/EIV data */ 121 memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); 122 123 /* Move ieee80211 header */ 124 memmove(skb->data + txdesc->iv_len, skb->data, txdesc->iv_offset); 125 126 /* Pull buffer to correct size */ 127 skb_pull(skb, txdesc->iv_len); 128 txdesc->length -= txdesc->iv_len; 129 130 /* IV/EIV data has officially been stripped */ 131 skbdesc->flags |= SKBDESC_IV_STRIPPED; 132 } 133 134 void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length) 135 { 136 struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); 137 const unsigned int iv_len = 138 ((!!(skbdesc->iv[0])) * 4) + ((!!(skbdesc->iv[1])) * 4); 139 140 if (!(skbdesc->flags & SKBDESC_IV_STRIPPED)) 141 return; 142 143 skb_push(skb, iv_len); 144 145 /* Move ieee80211 header */ 146 memmove(skb->data, skb->data + iv_len, header_length); 147 148 /* Copy IV/EIV data */ 149 memcpy(skb->data + header_length, skbdesc->iv, iv_len); 150 151 /* IV/EIV data has returned into the frame */ 152 skbdesc->flags &= ~SKBDESC_IV_STRIPPED; 153 } 154 155 void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, 156 unsigned int header_length, 157 struct rxdone_entry_desc *rxdesc) 158 { 159 unsigned int payload_len = rxdesc->size - header_length; 160 unsigned int align = ALIGN_SIZE(skb, header_length); 161 unsigned int iv_len; 162 unsigned int icv_len; 163 unsigned int transfer = 0; 164 165 /* 166 * WEP64/WEP128: Provides IV & ICV 167 * TKIP: Provides IV/EIV & ICV 168 * AES: Provies IV/EIV & ICV 169 */ 170 switch (rxdesc->cipher) { 171 case CIPHER_WEP64: 172 case CIPHER_WEP128: 173 iv_len = 4; 174 icv_len = 4; 175 break; 176 case CIPHER_TKIP: 177 iv_len = 8; 178 icv_len = 4; 179 break; 180 case CIPHER_AES: 181 iv_len = 8; 182 icv_len = 8; 183 break; 184 default: 185 /* Unsupport type */ 186 return; 187 } 188 189 /* 190 * Make room for new data. There are 2 possibilities 191 * either the alignment is already present between 192 * the 802.11 header and payload. In that case we 193 * we have to move the header less then the iv_len 194 * since we can use the already available l2pad bytes 195 * for the iv data. 196 * When the alignment must be added manually we must 197 * move the header more then iv_len since we must 198 * make room for the payload move as well. 199 */ 200 if (rxdesc->dev_flags & RXDONE_L2PAD) { 201 skb_push(skb, iv_len - align); 202 skb_put(skb, icv_len); 203 204 /* Move ieee80211 header */ 205 memmove(skb->data + transfer, 206 skb->data + transfer + (iv_len - align), 207 header_length); 208 transfer += header_length; 209 } else { 210 skb_push(skb, iv_len + align); 211 if (align < icv_len) 212 skb_put(skb, icv_len - align); 213 else if (align > icv_len) 214 skb_trim(skb, rxdesc->size + iv_len + icv_len); 215 216 /* Move ieee80211 header */ 217 memmove(skb->data + transfer, 218 skb->data + transfer + iv_len + align, 219 header_length); 220 transfer += header_length; 221 } 222 223 /* Copy IV/EIV data */ 224 memcpy(skb->data + transfer, rxdesc->iv, iv_len); 225 transfer += iv_len; 226 227 /* 228 * Move payload for alignment purposes. Note that 229 * this is only needed when no l2 padding is present. 230 */ 231 if (!(rxdesc->dev_flags & RXDONE_L2PAD)) { 232 memmove(skb->data + transfer, 233 skb->data + transfer + align, 234 payload_len); 235 } 236 237 /* 238 * NOTE: Always count the payload as transferred, 239 * even when alignment was set to zero. This is required 240 * for determining the correct offset for the ICV data. 241 */ 242 transfer += payload_len; 243 244 /* 245 * Copy ICV data 246 * AES appends 8 bytes, we can't fill the upper 247 * 4 bytes, but mac80211 doesn't care about what 248 * we provide here anyway and strips it immediately. 249 */ 250 memcpy(skb->data + transfer, &rxdesc->icv, 4); 251 transfer += icv_len; 252 253 /* IV/EIV/ICV has been inserted into frame */ 254 rxdesc->size = transfer; 255 rxdesc->flags &= ~RX_FLAG_IV_STRIPPED; 256 } 257