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