|
@@ -243,6 +243,10 @@ ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx)
|
|
|
u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN;
|
|
|
/* frame has qos control */
|
|
|
tid = qc[0] & QOS_CONTROL_TID_MASK;
|
|
|
+ if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
|
|
|
+ rx->u.rx.amsdu_frame = 1;
|
|
|
+ else
|
|
|
+ rx->u.rx.amsdu_frame = 0;
|
|
|
} else {
|
|
|
if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
|
|
|
/* Separate TID for management frames */
|
|
@@ -1196,6 +1200,125 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static ieee80211_txrx_result
|
|
|
+ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx)
|
|
|
+{
|
|
|
+ struct net_device *dev = rx->dev;
|
|
|
+ struct ieee80211_local *local = rx->local;
|
|
|
+ u16 fc, ethertype;
|
|
|
+ u8 *payload;
|
|
|
+ struct sk_buff *skb = rx->skb, *frame = NULL;
|
|
|
+ const struct ethhdr *eth;
|
|
|
+ int remaining, err;
|
|
|
+ u8 dst[ETH_ALEN];
|
|
|
+ u8 src[ETH_ALEN];
|
|
|
+ DECLARE_MAC_BUF(mac);
|
|
|
+
|
|
|
+ fc = rx->fc;
|
|
|
+ if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA))
|
|
|
+ return TXRX_CONTINUE;
|
|
|
+
|
|
|
+ if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
|
|
|
+ return TXRX_DROP;
|
|
|
+
|
|
|
+ if (!rx->u.rx.amsdu_frame)
|
|
|
+ return TXRX_CONTINUE;
|
|
|
+
|
|
|
+ err = ieee80211_data_to_8023(rx);
|
|
|
+ if (unlikely(err))
|
|
|
+ return TXRX_DROP;
|
|
|
+
|
|
|
+ skb->dev = dev;
|
|
|
+
|
|
|
+ dev->stats.rx_packets++;
|
|
|
+ dev->stats.rx_bytes += skb->len;
|
|
|
+
|
|
|
+ /* skip the wrapping header */
|
|
|
+ eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
|
|
|
+ if (!eth)
|
|
|
+ return TXRX_DROP;
|
|
|
+
|
|
|
+ while (skb != frame) {
|
|
|
+ u8 padding;
|
|
|
+ __be16 len = eth->h_proto;
|
|
|
+ unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
|
|
|
+
|
|
|
+ remaining = skb->len;
|
|
|
+ memcpy(dst, eth->h_dest, ETH_ALEN);
|
|
|
+ memcpy(src, eth->h_source, ETH_ALEN);
|
|
|
+
|
|
|
+ padding = ((4 - subframe_len) & 0x3);
|
|
|
+ /* the last MSDU has no padding */
|
|
|
+ if (subframe_len > remaining) {
|
|
|
+ printk(KERN_DEBUG "%s: wrong buffer size", dev->name);
|
|
|
+ return TXRX_DROP;
|
|
|
+ }
|
|
|
+
|
|
|
+ skb_pull(skb, sizeof(struct ethhdr));
|
|
|
+ /* if last subframe reuse skb */
|
|
|
+ if (remaining <= subframe_len + padding)
|
|
|
+ frame = skb;
|
|
|
+ else {
|
|
|
+ frame = dev_alloc_skb(local->hw.extra_tx_headroom +
|
|
|
+ subframe_len);
|
|
|
+
|
|
|
+ if (frame == NULL)
|
|
|
+ return TXRX_DROP;
|
|
|
+
|
|
|
+ skb_reserve(frame, local->hw.extra_tx_headroom +
|
|
|
+ sizeof(struct ethhdr));
|
|
|
+ memcpy(skb_put(frame, ntohs(len)), skb->data,
|
|
|
+ ntohs(len));
|
|
|
+
|
|
|
+ eth = (struct ethhdr *) skb_pull(skb, ntohs(len) +
|
|
|
+ padding);
|
|
|
+ if (!eth) {
|
|
|
+ printk(KERN_DEBUG "%s: wrong buffer size ",
|
|
|
+ dev->name);
|
|
|
+ dev_kfree_skb(frame);
|
|
|
+ return TXRX_DROP;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ skb_set_network_header(frame, 0);
|
|
|
+ frame->dev = dev;
|
|
|
+ frame->priority = skb->priority;
|
|
|
+ rx->skb = frame;
|
|
|
+
|
|
|
+ if ((ieee80211_drop_802_1x_pae(rx, 0)) ||
|
|
|
+ (ieee80211_drop_unencrypted(rx, 0))) {
|
|
|
+ if (skb == frame) /* last frame */
|
|
|
+ return TXRX_DROP;
|
|
|
+ dev_kfree_skb(frame);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ payload = frame->data;
|
|
|
+ ethertype = (payload[6] << 8) | payload[7];
|
|
|
+
|
|
|
+ if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
|
|
|
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
|
|
|
+ compare_ether_addr(payload,
|
|
|
+ bridge_tunnel_header) == 0)) {
|
|
|
+ /* remove RFC1042 or Bridge-Tunnel
|
|
|
+ * encapsulation and replace EtherType */
|
|
|
+ skb_pull(frame, 6);
|
|
|
+ memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
|
|
|
+ memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
|
|
|
+ } else {
|
|
|
+ memcpy(skb_push(frame, sizeof(__be16)), &len,
|
|
|
+ sizeof(__be16));
|
|
|
+ memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
|
|
|
+ memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ ieee80211_deliver_skb(rx);
|
|
|
+ }
|
|
|
+
|
|
|
+ return TXRX_QUEUED;
|
|
|
+}
|
|
|
+
|
|
|
static ieee80211_txrx_result
|
|
|
ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
|
|
|
{
|
|
@@ -1379,6 +1502,7 @@ ieee80211_rx_handler ieee80211_rx_handlers[] =
|
|
|
* are not passed to user space by these functions
|
|
|
*/
|
|
|
ieee80211_rx_h_remove_qos_control,
|
|
|
+ ieee80211_rx_h_amsdu,
|
|
|
ieee80211_rx_h_data,
|
|
|
ieee80211_rx_h_mgmt,
|
|
|
NULL
|