diff options
author | Zhu Yi <yi.zhu@intel.com> | 2009-11-30 21:18:37 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-12-22 13:31:15 -0500 |
commit | eaf85ca7fecb218fc41ff57c1642ca73b097aabb (patch) | |
tree | ad7965715f0cd67fe8946980ae2d1e9f3ea231a1 | |
parent | ca99861d5421c91f5a8fd3a77acb4b7be14f119d (diff) |
wireless: add ieee80211_amsdu_to_8023s
Move the A-MSDU handling code from mac80211 to cfg80211 so that more
drivers can use it. The new created function ieee80211_amsdu_to_8023s
converts an A-MSDU frame to a list of 802.3 frames.
Cc: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | include/net/cfg80211.h | 22 | ||||
-rw-r--r-- | net/mac80211/rx.c | 106 | ||||
-rw-r--r-- | net/wireless/util.c | 99 |
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 | */ |
1581 | int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | 1581 | int 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 | */ |
1592 | int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | 1592 | int 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 | */ | ||
1609 | void 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 | |||
1541 | ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) | 1541 | ieee80211_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 | ||
288 | int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr, | 288 | int 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 | } |
384 | EXPORT_SYMBOL(ieee80211_data_to_8023); | 384 | EXPORT_SYMBOL(ieee80211_data_to_8023); |
385 | 385 | ||
386 | int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr, | 386 | int 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 | } |
498 | EXPORT_SYMBOL(ieee80211_data_from_8023); | 498 | EXPORT_SYMBOL(ieee80211_data_from_8023); |
499 | 499 | ||
500 | |||
501 | void 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 | } | ||
593 | EXPORT_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. */ |
501 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) | 596 | unsigned int cfg80211_classify8021d(struct sk_buff *skb) |
502 | { | 597 | { |