diff options
author | Felix Fietkau <nbd@openwrt.org> | 2009-11-10 14:10:05 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-11-11 17:02:10 -0500 |
commit | f14543ee4d0681df1377b976cba704557ba220d3 (patch) | |
tree | f148485869f6638030207c069742c1918647be6a /net/mac80211/tx.c | |
parent | 8b787643ca0a5130c647109d77fe512f89cfa611 (diff) |
mac80211: implement support for 4-address frames for AP and client mode
In some situations it might be useful to run a network with an
Access Point and multiple clients, but with each client bridged
to a network behind it. For this to work, both the client and the
AP need to transmit 4-address frames, containing both source and
destination MAC addresses.
With this patch, you can configure a client to communicate using
only 4-address frames for data traffic.
On the AP side you can enable 4-address frames for individual
clients by isolating them in separate AP VLANs which are configured
in 4-address mode.
Such an AP VLAN will be limited to one client only, and this client
will be used as the destination for all traffic on its interface,
regardless of the destination MAC address in the packet headers.
The advantage of this mode compared to regular WDS mode is that it's
easier to configure and does not require a static list of peer MAC
addresses on any side.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/tx.c')
-rw-r--r-- | net/mac80211/tx.c | 45 |
1 files changed, 37 insertions, 8 deletions
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index bfaa43e096d2..2f3345c5f7cf 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -1051,7 +1051,10 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, | |||
1051 | 1051 | ||
1052 | hdr = (struct ieee80211_hdr *) skb->data; | 1052 | hdr = (struct ieee80211_hdr *) skb->data; |
1053 | 1053 | ||
1054 | tx->sta = sta_info_get(local, hdr->addr1); | 1054 | if ((sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && sdata->use_4addr) |
1055 | tx->sta = rcu_dereference(sdata->u.vlan.sta); | ||
1056 | if (!tx->sta) | ||
1057 | tx->sta = sta_info_get(local, hdr->addr1); | ||
1055 | 1058 | ||
1056 | if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && | 1059 | if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && |
1057 | (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) { | 1060 | (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) { |
@@ -1613,7 +1616,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
1613 | const u8 *encaps_data; | 1616 | const u8 *encaps_data; |
1614 | int encaps_len, skip_header_bytes; | 1617 | int encaps_len, skip_header_bytes; |
1615 | int nh_pos, h_pos; | 1618 | int nh_pos, h_pos; |
1616 | struct sta_info *sta; | 1619 | struct sta_info *sta = NULL; |
1617 | u32 sta_flags = 0; | 1620 | u32 sta_flags = 0; |
1618 | 1621 | ||
1619 | if (unlikely(skb->len < ETH_HLEN)) { | 1622 | if (unlikely(skb->len < ETH_HLEN)) { |
@@ -1630,8 +1633,25 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
1630 | fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); | 1633 | fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); |
1631 | 1634 | ||
1632 | switch (sdata->vif.type) { | 1635 | switch (sdata->vif.type) { |
1633 | case NL80211_IFTYPE_AP: | ||
1634 | case NL80211_IFTYPE_AP_VLAN: | 1636 | case NL80211_IFTYPE_AP_VLAN: |
1637 | rcu_read_lock(); | ||
1638 | if (sdata->use_4addr) | ||
1639 | sta = rcu_dereference(sdata->u.vlan.sta); | ||
1640 | if (sta) { | ||
1641 | fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); | ||
1642 | /* RA TA DA SA */ | ||
1643 | memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN); | ||
1644 | memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); | ||
1645 | memcpy(hdr.addr3, skb->data, ETH_ALEN); | ||
1646 | memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); | ||
1647 | hdrlen = 30; | ||
1648 | sta_flags = get_sta_flags(sta); | ||
1649 | } | ||
1650 | rcu_read_unlock(); | ||
1651 | if (sta) | ||
1652 | break; | ||
1653 | /* fall through */ | ||
1654 | case NL80211_IFTYPE_AP: | ||
1635 | fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); | 1655 | fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); |
1636 | /* DA BSSID SA */ | 1656 | /* DA BSSID SA */ |
1637 | memcpy(hdr.addr1, skb->data, ETH_ALEN); | 1657 | memcpy(hdr.addr1, skb->data, ETH_ALEN); |
@@ -1705,12 +1725,21 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, | |||
1705 | break; | 1725 | break; |
1706 | #endif | 1726 | #endif |
1707 | case NL80211_IFTYPE_STATION: | 1727 | case NL80211_IFTYPE_STATION: |
1708 | fc |= cpu_to_le16(IEEE80211_FCTL_TODS); | ||
1709 | /* BSSID SA DA */ | ||
1710 | memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); | 1728 | memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN); |
1711 | memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); | 1729 | if (sdata->use_4addr && ethertype != ETH_P_PAE) { |
1712 | memcpy(hdr.addr3, skb->data, ETH_ALEN); | 1730 | fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); |
1713 | hdrlen = 24; | 1731 | /* RA TA DA SA */ |
1732 | memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); | ||
1733 | memcpy(hdr.addr3, skb->data, ETH_ALEN); | ||
1734 | memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); | ||
1735 | hdrlen = 30; | ||
1736 | } else { | ||
1737 | fc |= cpu_to_le16(IEEE80211_FCTL_TODS); | ||
1738 | /* BSSID SA DA */ | ||
1739 | memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); | ||
1740 | memcpy(hdr.addr3, skb->data, ETH_ALEN); | ||
1741 | hdrlen = 24; | ||
1742 | } | ||
1714 | break; | 1743 | break; |
1715 | case NL80211_IFTYPE_ADHOC: | 1744 | case NL80211_IFTYPE_ADHOC: |
1716 | /* DA SA BSSID */ | 1745 | /* DA SA BSSID */ |