aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/cfg80211.h22
-rw-r--r--net/mac80211/rx.c106
-rw-r--r--net/wireless/util.c99
3 files changed, 137 insertions, 90 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 0884b9a0f778..542a477a94da 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1578,7 +1578,7 @@ unsigned int ieee80211_hdrlen(__le16 fc);
1578 * @addr: the device MAC address 1578 * @addr: the device MAC address
1579 * @iftype: the virtual interface type 1579 * @iftype: the virtual interface type
1580 */ 1580 */
1581int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, 1581int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
1582 enum nl80211_iftype iftype); 1582 enum nl80211_iftype iftype);
1583 1583
1584/** 1584/**
@@ -1589,10 +1589,28 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
1589 * @bssid: the network bssid (used only for iftype STATION and ADHOC) 1589 * @bssid: the network bssid (used only for iftype STATION and ADHOC)
1590 * @qos: build 802.11 QoS data frame 1590 * @qos: build 802.11 QoS data frame
1591 */ 1591 */
1592int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, 1592int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
1593 enum nl80211_iftype iftype, u8 *bssid, bool qos); 1593 enum nl80211_iftype iftype, u8 *bssid, bool qos);
1594 1594
1595/** 1595/**
1596 * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame
1597 *
1598 * Decode an IEEE 802.11n A-MSDU frame and convert it to a list of
1599 * 802.3 frames. The @list will be empty if the decode fails. The
1600 * @skb is consumed after the function returns.
1601 *
1602 * @skb: The input IEEE 802.11n A-MSDU frame.
1603 * @list: The output list of 802.3 frames. It must be allocated and
1604 * initialized by by the caller.
1605 * @addr: The device MAC address.
1606 * @iftype: The device interface type.
1607 * @extra_headroom: The hardware extra headroom for SKBs in the @list.
1608 */
1609void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
1610 const u8 *addr, enum nl80211_iftype iftype,
1611 const unsigned int extra_headroom);
1612
1613/**
1596 * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame 1614 * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
1597 * @skb: the data frame 1615 * @skb: the data frame
1598 */ 1616 */
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index dbfd684e3e2e..a182e423109b 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1541,16 +1541,10 @@ static ieee80211_rx_result debug_noinline
1541ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) 1541ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
1542{ 1542{
1543 struct net_device *dev = rx->sdata->dev; 1543 struct net_device *dev = rx->sdata->dev;
1544 struct ieee80211_local *local = rx->local; 1544 struct sk_buff *skb = rx->skb;
1545 u16 ethertype;
1546 u8 *payload;
1547 struct sk_buff *skb = rx->skb, *frame = NULL;
1548 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 1545 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
1549 __le16 fc = hdr->frame_control; 1546 __le16 fc = hdr->frame_control;
1550 const struct ethhdr *eth; 1547 struct sk_buff_head frame_list;
1551 int remaining, err;
1552 u8 dst[ETH_ALEN];
1553 u8 src[ETH_ALEN];
1554 1548
1555 if (unlikely(!ieee80211_is_data(fc))) 1549 if (unlikely(!ieee80211_is_data(fc)))
1556 return RX_CONTINUE; 1550 return RX_CONTINUE;
@@ -1561,94 +1555,34 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
1561 if (!(rx->flags & IEEE80211_RX_AMSDU)) 1555 if (!(rx->flags & IEEE80211_RX_AMSDU))
1562 return RX_CONTINUE; 1556 return RX_CONTINUE;
1563 1557
1564 err = __ieee80211_data_to_8023(rx); 1558 if (ieee80211_has_a4(hdr->frame_control) &&
1565 if (unlikely(err)) 1559 rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
1560 !rx->sdata->u.vlan.sta)
1566 return RX_DROP_UNUSABLE; 1561 return RX_DROP_UNUSABLE;
1567 1562
1568 skb->dev = dev; 1563 if (is_multicast_ether_addr(hdr->addr1) &&
1569 1564 ((rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
1570 dev->stats.rx_packets++; 1565 rx->sdata->u.vlan.sta) ||
1571 dev->stats.rx_bytes += skb->len; 1566 (rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
1572 1567 rx->sdata->u.mgd.use_4addr)))
1573 /* skip the wrapping header */
1574 eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
1575 if (!eth)
1576 return RX_DROP_UNUSABLE; 1568 return RX_DROP_UNUSABLE;
1577 1569
1578 while (skb != frame) { 1570 skb->dev = dev;
1579 u8 padding; 1571 __skb_queue_head_init(&frame_list);
1580 __be16 len = eth->h_proto;
1581 unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
1582
1583 remaining = skb->len;
1584 memcpy(dst, eth->h_dest, ETH_ALEN);
1585 memcpy(src, eth->h_source, ETH_ALEN);
1586 1572
1587 padding = ((4 - subframe_len) & 0x3); 1573 ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
1588 /* the last MSDU has no padding */ 1574 rx->sdata->vif.type,
1589 if (subframe_len > remaining) 1575 rx->local->hw.extra_tx_headroom);
1590 return RX_DROP_UNUSABLE;
1591 1576
1592 skb_pull(skb, sizeof(struct ethhdr)); 1577 while (!skb_queue_empty(&frame_list)) {
1593 /* if last subframe reuse skb */ 1578 rx->skb = __skb_dequeue(&frame_list);
1594 if (remaining <= subframe_len + padding)
1595 frame = skb;
1596 else {
1597 /*
1598 * Allocate and reserve two bytes more for payload
1599 * alignment since sizeof(struct ethhdr) is 14.
1600 */
1601 frame = dev_alloc_skb(
1602 ALIGN(local->hw.extra_tx_headroom, 4) +
1603 subframe_len + 2);
1604
1605 if (frame == NULL)
1606 return RX_DROP_UNUSABLE;
1607
1608 skb_reserve(frame,
1609 ALIGN(local->hw.extra_tx_headroom, 4) +
1610 sizeof(struct ethhdr) + 2);
1611 memcpy(skb_put(frame, ntohs(len)), skb->data,
1612 ntohs(len));
1613
1614 eth = (struct ethhdr *) skb_pull(skb, ntohs(len) +
1615 padding);
1616 if (!eth) {
1617 dev_kfree_skb(frame);
1618 return RX_DROP_UNUSABLE;
1619 }
1620 }
1621
1622 skb_reset_network_header(frame);
1623 frame->dev = dev;
1624 frame->priority = skb->priority;
1625 rx->skb = frame;
1626
1627 payload = frame->data;
1628 ethertype = (payload[6] << 8) | payload[7];
1629
1630 if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
1631 ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
1632 compare_ether_addr(payload,
1633 bridge_tunnel_header) == 0)) {
1634 /* remove RFC1042 or Bridge-Tunnel
1635 * encapsulation and replace EtherType */
1636 skb_pull(frame, 6);
1637 memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
1638 memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
1639 } else {
1640 memcpy(skb_push(frame, sizeof(__be16)),
1641 &len, sizeof(__be16));
1642 memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
1643 memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
1644 }
1645 1579
1646 if (!ieee80211_frame_allowed(rx, fc)) { 1580 if (!ieee80211_frame_allowed(rx, fc)) {
1647 if (skb == frame) /* last frame */ 1581 dev_kfree_skb(rx->skb);
1648 return RX_DROP_UNUSABLE;
1649 dev_kfree_skb(frame);
1650 continue; 1582 continue;
1651 } 1583 }
1584 dev->stats.rx_packets++;
1585 dev->stats.rx_bytes += rx->skb->len;
1652 1586
1653 ieee80211_deliver_skb(rx); 1587 ieee80211_deliver_skb(rx);
1654 } 1588 }
diff --git a/net/wireless/util.c b/net/wireless/util.c
index a3c841a255db..23557c1d0a9c 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -285,7 +285,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
285 } 285 }
286} 286}
287 287
288int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, 288int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
289 enum nl80211_iftype iftype) 289 enum nl80211_iftype iftype)
290{ 290{
291 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; 291 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -383,7 +383,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
383} 383}
384EXPORT_SYMBOL(ieee80211_data_to_8023); 384EXPORT_SYMBOL(ieee80211_data_to_8023);
385 385
386int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, 386int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
387 enum nl80211_iftype iftype, u8 *bssid, bool qos) 387 enum nl80211_iftype iftype, u8 *bssid, bool qos)
388{ 388{
389 struct ieee80211_hdr hdr; 389 struct ieee80211_hdr hdr;
@@ -497,6 +497,101 @@ int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
497} 497}
498EXPORT_SYMBOL(ieee80211_data_from_8023); 498EXPORT_SYMBOL(ieee80211_data_from_8023);
499 499
500
501void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
502 const u8 *addr, enum nl80211_iftype iftype,
503 const unsigned int extra_headroom)
504{
505 struct sk_buff *frame = NULL;
506 u16 ethertype;
507 u8 *payload;
508 const struct ethhdr *eth;
509 int remaining, err;
510 u8 dst[ETH_ALEN], src[ETH_ALEN];
511
512 err = ieee80211_data_to_8023(skb, addr, iftype);
513 if (err)
514 goto out;
515
516 /* skip the wrapping header */
517 eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
518 if (!eth)
519 goto out;
520
521 while (skb != frame) {
522 u8 padding;
523 __be16 len = eth->h_proto;
524 unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
525
526 remaining = skb->len;
527 memcpy(dst, eth->h_dest, ETH_ALEN);
528 memcpy(src, eth->h_source, ETH_ALEN);
529
530 padding = (4 - subframe_len) & 0x3;
531 /* the last MSDU has no padding */
532 if (subframe_len > remaining)
533 goto purge;
534
535 skb_pull(skb, sizeof(struct ethhdr));
536 /* reuse skb for the last subframe */
537 if (remaining <= subframe_len + padding)
538 frame = skb;
539 else {
540 unsigned int hlen = ALIGN(extra_headroom, 4);
541 /*
542 * Allocate and reserve two bytes more for payload
543 * alignment since sizeof(struct ethhdr) is 14.
544 */
545 frame = dev_alloc_skb(hlen + subframe_len + 2);
546 if (!frame)
547 goto purge;
548
549 skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
550 memcpy(skb_put(frame, ntohs(len)), skb->data,
551 ntohs(len));
552
553 eth = (struct ethhdr *)skb_pull(skb, ntohs(len) +
554 padding);
555 if (!eth) {
556 dev_kfree_skb(frame);
557 goto purge;
558 }
559 }
560
561 skb_reset_network_header(frame);
562 frame->dev = skb->dev;
563 frame->priority = skb->priority;
564
565 payload = frame->data;
566 ethertype = (payload[6] << 8) | payload[7];
567
568 if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
569 ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
570 compare_ether_addr(payload,
571 bridge_tunnel_header) == 0)) {
572 /* remove RFC1042 or Bridge-Tunnel
573 * encapsulation and replace EtherType */
574 skb_pull(frame, 6);
575 memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
576 memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
577 } else {
578 memcpy(skb_push(frame, sizeof(__be16)), &len,
579 sizeof(__be16));
580 memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
581 memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
582 }
583 __skb_queue_tail(list, frame);
584 }
585
586 return;
587
588 purge:
589 __skb_queue_purge(list);
590 out:
591 dev_kfree_skb(skb);
592}
593EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
594
500/* Given a data frame determine the 802.1p/1d tag to use. */ 595/* Given a data frame determine the 802.1p/1d tag to use. */
501unsigned int cfg80211_classify8021d(struct sk_buff *skb) 596unsigned int cfg80211_classify8021d(struct sk_buff *skb)
502{ 597{