162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved. 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Contact Information: 862306a36Sopenharmony_ci Intel Linux Wireless <ilw@linux.intel.com> 962306a36Sopenharmony_ci Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci******************************************************************************/ 1262306a36Sopenharmony_ci#include <linux/compiler.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/if_arp.h> 1562306a36Sopenharmony_ci#include <linux/in6.h> 1662306a36Sopenharmony_ci#include <linux/in.h> 1762306a36Sopenharmony_ci#include <linux/ip.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/netdevice.h> 2162306a36Sopenharmony_ci#include <linux/proc_fs.h> 2262306a36Sopenharmony_ci#include <linux/skbuff.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/tcp.h> 2562306a36Sopenharmony_ci#include <linux/types.h> 2662306a36Sopenharmony_ci#include <linux/wireless.h> 2762306a36Sopenharmony_ci#include <linux/etherdevice.h> 2862306a36Sopenharmony_ci#include <linux/uaccess.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "libipw.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci802.11 Data Frame 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci ,-------------------------------------------------------------------. 3762306a36Sopenharmony_ciBytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | 3862306a36Sopenharmony_ci |------|------|---------|---------|---------|------|---------|------| 3962306a36Sopenharmony_ciDesc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs | 4062306a36Sopenharmony_ci | | tion | (BSSID) | | | ence | data | | 4162306a36Sopenharmony_ci `--------------------------------------------------| |------' 4262306a36Sopenharmony_ciTotal: 28 non-data bytes `----.----' 4362306a36Sopenharmony_ci | 4462306a36Sopenharmony_ci .- 'Frame data' expands, if WEP enabled, to <----------' 4562306a36Sopenharmony_ci | 4662306a36Sopenharmony_ci V 4762306a36Sopenharmony_ci ,-----------------------. 4862306a36Sopenharmony_ciBytes | 4 | 0-2296 | 4 | 4962306a36Sopenharmony_ci |-----|-----------|-----| 5062306a36Sopenharmony_ciDesc. | IV | Encrypted | ICV | 5162306a36Sopenharmony_ci | | Packet | | 5262306a36Sopenharmony_ci `-----| |-----' 5362306a36Sopenharmony_ci `-----.-----' 5462306a36Sopenharmony_ci | 5562306a36Sopenharmony_ci .- 'Encrypted Packet' expands to 5662306a36Sopenharmony_ci | 5762306a36Sopenharmony_ci V 5862306a36Sopenharmony_ci ,---------------------------------------------------. 5962306a36Sopenharmony_ciBytes | 1 | 1 | 1 | 3 | 2 | 0-2304 | 6062306a36Sopenharmony_ci |------|------|---------|----------|------|---------| 6162306a36Sopenharmony_ciDesc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP | 6262306a36Sopenharmony_ci | DSAP | SSAP | | | | Packet | 6362306a36Sopenharmony_ci | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | | 6462306a36Sopenharmony_ci `---------------------------------------------------- 6562306a36Sopenharmony_ciTotal: 8 non-data bytes 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci802.3 Ethernet Data Frame 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci ,-----------------------------------------. 7062306a36Sopenharmony_ciBytes | 6 | 6 | 2 | Variable | 4 | 7162306a36Sopenharmony_ci |-------|-------|------|-----------|------| 7262306a36Sopenharmony_ciDesc. | Dest. | Source| Type | IP Packet | fcs | 7362306a36Sopenharmony_ci | MAC | MAC | | | | 7462306a36Sopenharmony_ci `-----------------------------------------' 7562306a36Sopenharmony_ciTotal: 18 non-data bytes 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciIn the event that fragmentation is required, the incoming payload is split into 7862306a36Sopenharmony_ciN parts of size ieee->fts. The first fragment contains the SNAP header and the 7962306a36Sopenharmony_ciremaining packets are just data. 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ciIf encryption is enabled, each fragment payload size is reduced by enough space 8262306a36Sopenharmony_cito add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP) 8362306a36Sopenharmony_ciSo if you have 1500 bytes of payload with ieee->fts set to 500 without 8462306a36Sopenharmony_ciencryption it will take 3 frames. With WEP it will take 4 frames as the 8562306a36Sopenharmony_cipayload of each frame is reduced to 492 bytes. 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci* SKB visualization 8862306a36Sopenharmony_ci* 8962306a36Sopenharmony_ci* ,- skb->data 9062306a36Sopenharmony_ci* | 9162306a36Sopenharmony_ci* | ETHERNET HEADER ,-<-- PAYLOAD 9262306a36Sopenharmony_ci* | | 14 bytes from skb->data 9362306a36Sopenharmony_ci* | 2 bytes for Type --> ,T. | (sizeof ethhdr) 9462306a36Sopenharmony_ci* | | | | 9562306a36Sopenharmony_ci* |,-Dest.--. ,--Src.---. | | | 9662306a36Sopenharmony_ci* | 6 bytes| | 6 bytes | | | | 9762306a36Sopenharmony_ci* v | | | | | | 9862306a36Sopenharmony_ci* 0 | v 1 | v | v 2 9962306a36Sopenharmony_ci* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 10062306a36Sopenharmony_ci* ^ | ^ | ^ | 10162306a36Sopenharmony_ci* | | | | | | 10262306a36Sopenharmony_ci* | | | | `T' <---- 2 bytes for Type 10362306a36Sopenharmony_ci* | | | | 10462306a36Sopenharmony_ci* | | '---SNAP--' <-------- 6 bytes for SNAP 10562306a36Sopenharmony_ci* | | 10662306a36Sopenharmony_ci* `-IV--' <-------------------- 4 bytes for IV (WEP) 10762306a36Sopenharmony_ci* 10862306a36Sopenharmony_ci* SNAP HEADER 10962306a36Sopenharmony_ci* 11062306a36Sopenharmony_ci*/ 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 }; 11362306a36Sopenharmony_cistatic u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int libipw_copy_snap(u8 * data, __be16 h_proto) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct libipw_snap_hdr *snap; 11862306a36Sopenharmony_ci u8 *oui; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci snap = (struct libipw_snap_hdr *)data; 12162306a36Sopenharmony_ci snap->dsap = 0xaa; 12262306a36Sopenharmony_ci snap->ssap = 0xaa; 12362306a36Sopenharmony_ci snap->ctrl = 0x03; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX)) 12662306a36Sopenharmony_ci oui = P802_1H_OUI; 12762306a36Sopenharmony_ci else 12862306a36Sopenharmony_ci oui = RFC1042_OUI; 12962306a36Sopenharmony_ci snap->oui[0] = oui[0]; 13062306a36Sopenharmony_ci snap->oui[1] = oui[1]; 13162306a36Sopenharmony_ci snap->oui[2] = oui[2]; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16)); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return SNAP_SIZE + sizeof(u16); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int libipw_encrypt_fragment(struct libipw_device *ieee, 13962306a36Sopenharmony_ci struct sk_buff *frag, int hdr_len) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct lib80211_crypt_data *crypt = 14262306a36Sopenharmony_ci ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; 14362306a36Sopenharmony_ci int res; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (crypt == NULL) 14662306a36Sopenharmony_ci return -1; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* To encrypt, frame format is: 14962306a36Sopenharmony_ci * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */ 15062306a36Sopenharmony_ci atomic_inc(&crypt->refcnt); 15162306a36Sopenharmony_ci res = 0; 15262306a36Sopenharmony_ci if (crypt->ops && crypt->ops->encrypt_mpdu) 15362306a36Sopenharmony_ci res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci atomic_dec(&crypt->refcnt); 15662306a36Sopenharmony_ci if (res < 0) { 15762306a36Sopenharmony_ci printk(KERN_INFO "%s: Encryption failed: len=%d.\n", 15862306a36Sopenharmony_ci ieee->dev->name, frag->len); 15962306a36Sopenharmony_ci ieee->ieee_stats.tx_discards++; 16062306a36Sopenharmony_ci return -1; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_civoid libipw_txb_free(struct libipw_txb *txb) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci int i; 16962306a36Sopenharmony_ci if (unlikely(!txb)) 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci for (i = 0; i < txb->nr_frags; i++) 17262306a36Sopenharmony_ci if (txb->fragments[i]) 17362306a36Sopenharmony_ci dev_kfree_skb_any(txb->fragments[i]); 17462306a36Sopenharmony_ci kfree(txb); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic struct libipw_txb *libipw_alloc_txb(int nr_frags, int txb_size, 17862306a36Sopenharmony_ci int headroom, gfp_t gfp_mask) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct libipw_txb *txb; 18162306a36Sopenharmony_ci int i; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci txb = kmalloc(struct_size(txb, fragments, nr_frags), gfp_mask); 18462306a36Sopenharmony_ci if (!txb) 18562306a36Sopenharmony_ci return NULL; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci memset(txb, 0, sizeof(struct libipw_txb)); 18862306a36Sopenharmony_ci txb->nr_frags = nr_frags; 18962306a36Sopenharmony_ci txb->frag_size = txb_size; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci for (i = 0; i < nr_frags; i++) { 19262306a36Sopenharmony_ci txb->fragments[i] = __dev_alloc_skb(txb_size + headroom, 19362306a36Sopenharmony_ci gfp_mask); 19462306a36Sopenharmony_ci if (unlikely(!txb->fragments[i])) { 19562306a36Sopenharmony_ci i--; 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci skb_reserve(txb->fragments[i], headroom); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci if (unlikely(i != nr_frags)) { 20162306a36Sopenharmony_ci while (i >= 0) 20262306a36Sopenharmony_ci dev_kfree_skb_any(txb->fragments[i--]); 20362306a36Sopenharmony_ci kfree(txb); 20462306a36Sopenharmony_ci return NULL; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci return txb; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int libipw_classify(struct sk_buff *skb) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct ethhdr *eth; 21262306a36Sopenharmony_ci struct iphdr *ip; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci eth = (struct ethhdr *)skb->data; 21562306a36Sopenharmony_ci if (eth->h_proto != htons(ETH_P_IP)) 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci ip = ip_hdr(skb); 21962306a36Sopenharmony_ci switch (ip->tos & 0xfc) { 22062306a36Sopenharmony_ci case 0x20: 22162306a36Sopenharmony_ci return 2; 22262306a36Sopenharmony_ci case 0x40: 22362306a36Sopenharmony_ci return 1; 22462306a36Sopenharmony_ci case 0x60: 22562306a36Sopenharmony_ci return 3; 22662306a36Sopenharmony_ci case 0x80: 22762306a36Sopenharmony_ci return 4; 22862306a36Sopenharmony_ci case 0xa0: 22962306a36Sopenharmony_ci return 5; 23062306a36Sopenharmony_ci case 0xc0: 23162306a36Sopenharmony_ci return 6; 23262306a36Sopenharmony_ci case 0xe0: 23362306a36Sopenharmony_ci return 7; 23462306a36Sopenharmony_ci default: 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* Incoming skb is converted to a txb which consists of 24062306a36Sopenharmony_ci * a block of 802.11 fragment packets (stored as skbs) */ 24162306a36Sopenharmony_cinetdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct libipw_device *ieee = netdev_priv(dev); 24462306a36Sopenharmony_ci struct libipw_txb *txb = NULL; 24562306a36Sopenharmony_ci struct libipw_hdr_3addrqos *frag_hdr; 24662306a36Sopenharmony_ci int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size, 24762306a36Sopenharmony_ci rts_required; 24862306a36Sopenharmony_ci unsigned long flags; 24962306a36Sopenharmony_ci int encrypt, host_encrypt, host_encrypt_msdu; 25062306a36Sopenharmony_ci __be16 ether_type; 25162306a36Sopenharmony_ci int bytes, fc, hdr_len; 25262306a36Sopenharmony_ci struct sk_buff *skb_frag; 25362306a36Sopenharmony_ci struct libipw_hdr_3addrqos header = {/* Ensure zero initialized */ 25462306a36Sopenharmony_ci .duration_id = 0, 25562306a36Sopenharmony_ci .seq_ctl = 0, 25662306a36Sopenharmony_ci .qos_ctl = 0 25762306a36Sopenharmony_ci }; 25862306a36Sopenharmony_ci u8 dest[ETH_ALEN], src[ETH_ALEN]; 25962306a36Sopenharmony_ci struct lib80211_crypt_data *crypt; 26062306a36Sopenharmony_ci int priority = skb->priority; 26162306a36Sopenharmony_ci int snapped = 0; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority)) 26462306a36Sopenharmony_ci return NETDEV_TX_BUSY; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci spin_lock_irqsave(&ieee->lock, flags); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* If there is no driver handler to take the TXB, dont' bother 26962306a36Sopenharmony_ci * creating it... */ 27062306a36Sopenharmony_ci if (!ieee->hard_start_xmit) { 27162306a36Sopenharmony_ci printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name); 27262306a36Sopenharmony_ci goto success; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) { 27662306a36Sopenharmony_ci printk(KERN_WARNING "%s: skb too small (%d).\n", 27762306a36Sopenharmony_ci ieee->dev->name, skb->len); 27862306a36Sopenharmony_ci goto success; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci ether_type = ((struct ethhdr *)skb->data)->h_proto; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) && 28662306a36Sopenharmony_ci ieee->sec.encrypt; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci host_encrypt = ieee->host_encrypt && encrypt && crypt; 28962306a36Sopenharmony_ci host_encrypt_msdu = ieee->host_encrypt_msdu && encrypt && crypt; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (!encrypt && ieee->ieee802_1x && 29262306a36Sopenharmony_ci ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) { 29362306a36Sopenharmony_ci dev->stats.tx_dropped++; 29462306a36Sopenharmony_ci goto success; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Save source and destination addresses */ 29862306a36Sopenharmony_ci skb_copy_from_linear_data(skb, dest, ETH_ALEN); 29962306a36Sopenharmony_ci skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (host_encrypt) 30262306a36Sopenharmony_ci fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | 30362306a36Sopenharmony_ci IEEE80211_FCTL_PROTECTED; 30462306a36Sopenharmony_ci else 30562306a36Sopenharmony_ci fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (ieee->iw_mode == IW_MODE_INFRA) { 30862306a36Sopenharmony_ci fc |= IEEE80211_FCTL_TODS; 30962306a36Sopenharmony_ci /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ 31062306a36Sopenharmony_ci memcpy(header.addr1, ieee->bssid, ETH_ALEN); 31162306a36Sopenharmony_ci memcpy(header.addr2, src, ETH_ALEN); 31262306a36Sopenharmony_ci memcpy(header.addr3, dest, ETH_ALEN); 31362306a36Sopenharmony_ci } else if (ieee->iw_mode == IW_MODE_ADHOC) { 31462306a36Sopenharmony_ci /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ 31562306a36Sopenharmony_ci memcpy(header.addr1, dest, ETH_ALEN); 31662306a36Sopenharmony_ci memcpy(header.addr2, src, ETH_ALEN); 31762306a36Sopenharmony_ci memcpy(header.addr3, ieee->bssid, ETH_ALEN); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci hdr_len = LIBIPW_3ADDR_LEN; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) { 32262306a36Sopenharmony_ci fc |= IEEE80211_STYPE_QOS_DATA; 32362306a36Sopenharmony_ci hdr_len += 2; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci skb->priority = libipw_classify(skb); 32662306a36Sopenharmony_ci header.qos_ctl |= cpu_to_le16(skb->priority & LIBIPW_QCTL_TID); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci header.frame_ctl = cpu_to_le16(fc); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Advance the SKB to the start of the payload */ 33162306a36Sopenharmony_ci skb_pull(skb, sizeof(struct ethhdr)); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Determine total amount of storage required for TXB packets */ 33462306a36Sopenharmony_ci bytes = skb->len + SNAP_SIZE + sizeof(u16); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Encrypt msdu first on the whole data packet. */ 33762306a36Sopenharmony_ci if ((host_encrypt || host_encrypt_msdu) && 33862306a36Sopenharmony_ci crypt && crypt->ops && crypt->ops->encrypt_msdu) { 33962306a36Sopenharmony_ci int res = 0; 34062306a36Sopenharmony_ci int len = bytes + hdr_len + crypt->ops->extra_msdu_prefix_len + 34162306a36Sopenharmony_ci crypt->ops->extra_msdu_postfix_len; 34262306a36Sopenharmony_ci struct sk_buff *skb_new = dev_alloc_skb(len); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (unlikely(!skb_new)) 34562306a36Sopenharmony_ci goto failed; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len); 34862306a36Sopenharmony_ci skb_put_data(skb_new, &header, hdr_len); 34962306a36Sopenharmony_ci snapped = 1; 35062306a36Sopenharmony_ci libipw_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)), 35162306a36Sopenharmony_ci ether_type); 35262306a36Sopenharmony_ci skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len); 35362306a36Sopenharmony_ci res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv); 35462306a36Sopenharmony_ci if (res < 0) { 35562306a36Sopenharmony_ci LIBIPW_ERROR("msdu encryption failed\n"); 35662306a36Sopenharmony_ci dev_kfree_skb_any(skb_new); 35762306a36Sopenharmony_ci goto failed; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 36062306a36Sopenharmony_ci skb = skb_new; 36162306a36Sopenharmony_ci bytes += crypt->ops->extra_msdu_prefix_len + 36262306a36Sopenharmony_ci crypt->ops->extra_msdu_postfix_len; 36362306a36Sopenharmony_ci skb_pull(skb, hdr_len); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (host_encrypt || ieee->host_open_frag) { 36762306a36Sopenharmony_ci /* Determine fragmentation size based on destination (multicast 36862306a36Sopenharmony_ci * and broadcast are not fragmented) */ 36962306a36Sopenharmony_ci if (is_multicast_ether_addr(dest) || 37062306a36Sopenharmony_ci is_broadcast_ether_addr(dest)) 37162306a36Sopenharmony_ci frag_size = MAX_FRAG_THRESHOLD; 37262306a36Sopenharmony_ci else 37362306a36Sopenharmony_ci frag_size = ieee->fts; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Determine amount of payload per fragment. Regardless of if 37662306a36Sopenharmony_ci * this stack is providing the full 802.11 header, one will 37762306a36Sopenharmony_ci * eventually be affixed to this fragment -- so we must account 37862306a36Sopenharmony_ci * for it when determining the amount of payload space. */ 37962306a36Sopenharmony_ci bytes_per_frag = frag_size - hdr_len; 38062306a36Sopenharmony_ci if (ieee->config & 38162306a36Sopenharmony_ci (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) 38262306a36Sopenharmony_ci bytes_per_frag -= LIBIPW_FCS_LEN; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Each fragment may need to have room for encryption 38562306a36Sopenharmony_ci * pre/postfix */ 38662306a36Sopenharmony_ci if (host_encrypt && crypt && crypt->ops) 38762306a36Sopenharmony_ci bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len + 38862306a36Sopenharmony_ci crypt->ops->extra_mpdu_postfix_len; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Number of fragments is the total 39162306a36Sopenharmony_ci * bytes_per_frag / payload_per_fragment */ 39262306a36Sopenharmony_ci nr_frags = bytes / bytes_per_frag; 39362306a36Sopenharmony_ci bytes_last_frag = bytes % bytes_per_frag; 39462306a36Sopenharmony_ci if (bytes_last_frag) 39562306a36Sopenharmony_ci nr_frags++; 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci bytes_last_frag = bytes_per_frag; 39862306a36Sopenharmony_ci } else { 39962306a36Sopenharmony_ci nr_frags = 1; 40062306a36Sopenharmony_ci bytes_per_frag = bytes_last_frag = bytes; 40162306a36Sopenharmony_ci frag_size = bytes + hdr_len; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci rts_required = (frag_size > ieee->rts 40562306a36Sopenharmony_ci && ieee->config & CFG_LIBIPW_RTS); 40662306a36Sopenharmony_ci if (rts_required) 40762306a36Sopenharmony_ci nr_frags++; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* When we allocate the TXB we allocate enough space for the reserve 41062306a36Sopenharmony_ci * and full fragment bytes (bytes_per_frag doesn't include prefix, 41162306a36Sopenharmony_ci * postfix, header, FCS, etc.) */ 41262306a36Sopenharmony_ci txb = libipw_alloc_txb(nr_frags, frag_size, 41362306a36Sopenharmony_ci ieee->tx_headroom, GFP_ATOMIC); 41462306a36Sopenharmony_ci if (unlikely(!txb)) { 41562306a36Sopenharmony_ci printk(KERN_WARNING "%s: Could not allocate TXB\n", 41662306a36Sopenharmony_ci ieee->dev->name); 41762306a36Sopenharmony_ci goto failed; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci txb->encrypted = encrypt; 42062306a36Sopenharmony_ci if (host_encrypt) 42162306a36Sopenharmony_ci txb->payload_size = frag_size * (nr_frags - 1) + 42262306a36Sopenharmony_ci bytes_last_frag; 42362306a36Sopenharmony_ci else 42462306a36Sopenharmony_ci txb->payload_size = bytes; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (rts_required) { 42762306a36Sopenharmony_ci skb_frag = txb->fragments[0]; 42862306a36Sopenharmony_ci frag_hdr = skb_put(skb_frag, hdr_len); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* 43162306a36Sopenharmony_ci * Set header frame_ctl to the RTS. 43262306a36Sopenharmony_ci */ 43362306a36Sopenharmony_ci header.frame_ctl = 43462306a36Sopenharmony_ci cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); 43562306a36Sopenharmony_ci memcpy(frag_hdr, &header, hdr_len); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* 43862306a36Sopenharmony_ci * Restore header frame_ctl to the original data setting. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci header.frame_ctl = cpu_to_le16(fc); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (ieee->config & 44362306a36Sopenharmony_ci (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) 44462306a36Sopenharmony_ci skb_put(skb_frag, 4); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci txb->rts_included = 1; 44762306a36Sopenharmony_ci i = 1; 44862306a36Sopenharmony_ci } else 44962306a36Sopenharmony_ci i = 0; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci for (; i < nr_frags; i++) { 45262306a36Sopenharmony_ci skb_frag = txb->fragments[i]; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (host_encrypt) 45562306a36Sopenharmony_ci skb_reserve(skb_frag, 45662306a36Sopenharmony_ci crypt->ops->extra_mpdu_prefix_len); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci frag_hdr = skb_put_data(skb_frag, &header, hdr_len); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* If this is not the last fragment, then add the MOREFRAGS 46162306a36Sopenharmony_ci * bit to the frame control */ 46262306a36Sopenharmony_ci if (i != nr_frags - 1) { 46362306a36Sopenharmony_ci frag_hdr->frame_ctl = 46462306a36Sopenharmony_ci cpu_to_le16(fc | IEEE80211_FCTL_MOREFRAGS); 46562306a36Sopenharmony_ci bytes = bytes_per_frag; 46662306a36Sopenharmony_ci } else { 46762306a36Sopenharmony_ci /* The last fragment takes the remaining length */ 46862306a36Sopenharmony_ci bytes = bytes_last_frag; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (i == 0 && !snapped) { 47262306a36Sopenharmony_ci libipw_copy_snap(skb_put 47362306a36Sopenharmony_ci (skb_frag, SNAP_SIZE + sizeof(u16)), 47462306a36Sopenharmony_ci ether_type); 47562306a36Sopenharmony_ci bytes -= SNAP_SIZE + sizeof(u16); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* Advance the SKB... */ 48162306a36Sopenharmony_ci skb_pull(skb, bytes); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* Encryption routine will move the header forward in order 48462306a36Sopenharmony_ci * to insert the IV between the header and the payload */ 48562306a36Sopenharmony_ci if (host_encrypt) 48662306a36Sopenharmony_ci libipw_encrypt_fragment(ieee, skb_frag, hdr_len); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (ieee->config & 48962306a36Sopenharmony_ci (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) 49062306a36Sopenharmony_ci skb_put(skb_frag, 4); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci success: 49462306a36Sopenharmony_ci spin_unlock_irqrestore(&ieee->lock, flags); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (txb) { 49962306a36Sopenharmony_ci netdev_tx_t ret = (*ieee->hard_start_xmit)(txb, dev, priority); 50062306a36Sopenharmony_ci if (ret == NETDEV_TX_OK) { 50162306a36Sopenharmony_ci dev->stats.tx_packets++; 50262306a36Sopenharmony_ci dev->stats.tx_bytes += txb->payload_size; 50362306a36Sopenharmony_ci return NETDEV_TX_OK; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci libipw_txb_free(txb); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return NETDEV_TX_OK; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci failed: 51262306a36Sopenharmony_ci spin_unlock_irqrestore(&ieee->lock, flags); 51362306a36Sopenharmony_ci netif_stop_queue(dev); 51462306a36Sopenharmony_ci dev->stats.tx_errors++; 51562306a36Sopenharmony_ci return NETDEV_TX_BUSY; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_xmit); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ciEXPORT_SYMBOL(libipw_txb_free); 520