mac80211: implement wds sta support (wds ap support work in progress, needs hostapd...
[openwrt.git] / package / mac80211 / patches / 510-mac80211_4addr_vlan.patch
diff --git a/package/mac80211/patches/510-mac80211_4addr_vlan.patch b/package/mac80211/patches/510-mac80211_4addr_vlan.patch
new file mode 100644 (file)
index 0000000..ac92fe5
--- /dev/null
@@ -0,0 +1,260 @@
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -208,6 +208,9 @@ struct ieee80211_if_wds {
+ struct ieee80211_if_vlan {
+       struct list_head list;
++
++      /* used for all tx if the VLAN is configured to 4-addr mode */
++      struct sta_info *sta;
+ };
+ struct mesh_stats {
+@@ -457,6 +460,8 @@ struct ieee80211_sub_if_data {
+       int force_unicast_rateidx; /* forced TX rateidx for unicast frames */
+       int max_ratectrl_rateidx; /* max TX rateidx for rate control */
++      bool use_4addr; /* use 4-address frames */
++
+       union {
+               struct ieee80211_if_ap ap;
+               struct ieee80211_if_wds wds;
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -36,6 +36,24 @@ static bool nl80211_type_check(enum nl80
+       }
+ }
++static bool nl80211_params_check(enum nl80211_iftype type,
++                               struct vif_params *params)
++{
++      if (!nl80211_type_check(type))
++              return false;
++
++      if (params->use_4addr > 0) {
++              switch(type) {
++              case NL80211_IFTYPE_AP_VLAN:
++              case NL80211_IFTYPE_STATION:
++                      break;
++              default:
++                      return false;
++              }
++      }
++      return true;
++}
++
+ static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
+                              enum nl80211_iftype type, u32 *flags,
+                              struct vif_params *params)
+@@ -45,7 +63,7 @@ static int ieee80211_add_iface(struct wi
+       struct ieee80211_sub_if_data *sdata;
+       int err;
+-      if (!nl80211_type_check(type))
++      if (!nl80211_params_check(type, params))
+               return -EINVAL;
+       err = ieee80211_if_add(local, name, &dev, type, params);
+@@ -75,7 +93,7 @@ static int ieee80211_change_iface(struct
+       if (netif_running(dev))
+               return -EBUSY;
+-      if (!nl80211_type_check(type))
++      if (!nl80211_params_check(type, params))
+               return -EINVAL;
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+@@ -89,6 +107,9 @@ static int ieee80211_change_iface(struct
+                                           params->mesh_id_len,
+                                           params->mesh_id);
++      if (params->use_4addr >= 0)
++              sdata->use_4addr = !!params->use_4addr;
++
+       if (sdata->vif.type != NL80211_IFTYPE_MONITOR || !flags)
+               return 0;
+@@ -806,6 +827,13 @@ static int ieee80211_change_station(stru
+                       return -EINVAL;
+               }
++              if (vlansdata->use_4addr) {
++                      if (vlansdata->u.vlan.sta)
++                              return -EBUSY;
++
++                      rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
++              }
++
+               sta->sdata = vlansdata;
+               ieee80211_send_layer2_update(sta);
+       }
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -489,6 +489,9 @@ static void __sta_info_unlink(struct sta
+       local->num_sta--;
+       local->sta_generation++;
++      if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
++              rcu_assign_pointer(sdata->u.vlan.sta, NULL);
++
+       if (local->ops->sta_notify) {
+               if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+                       sdata = container_of(sdata->bss,
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1046,7 +1046,10 @@ ieee80211_tx_prepare(struct ieee80211_su
+       hdr = (struct ieee80211_hdr *) skb->data;
+-      tx->sta = sta_info_get(local, hdr->addr1);
++      if ((sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && sdata->use_4addr)
++              tx->sta = rcu_dereference(sdata->u.vlan.sta);
++      if (!tx->sta)
++              tx->sta = sta_info_get(local, hdr->addr1);
+       if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
+           (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) {
+@@ -1608,7 +1611,7 @@ netdev_tx_t ieee80211_subif_start_xmit(s
+       const u8 *encaps_data;
+       int encaps_len, skip_header_bytes;
+       int nh_pos, h_pos;
+-      struct sta_info *sta;
++      struct sta_info *sta = NULL;
+       u32 sta_flags = 0;
+       if (unlikely(skb->len < ETH_HLEN)) {
+@@ -1625,8 +1628,25 @@ netdev_tx_t ieee80211_subif_start_xmit(s
+       fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
+       switch (sdata->vif.type) {
+-      case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
++              rcu_read_lock();
++              if (sdata->use_4addr)
++                      sta = rcu_dereference(sdata->u.vlan.sta);
++              if (sta) {
++                      fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
++                      /* RA TA DA SA */
++                      memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN);
++                      memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
++                      memcpy(hdr.addr3, skb->data, ETH_ALEN);
++                      memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
++                      hdrlen = 30;
++                      sta_flags = get_sta_flags(sta);
++              }
++              rcu_read_unlock();
++              if (sta)
++                      break;
++              /* fall through */
++      case NL80211_IFTYPE_AP:
+               fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+               /* DA BSSID SA */
+               memcpy(hdr.addr1, skb->data, ETH_ALEN);
+@@ -1700,12 +1720,21 @@ netdev_tx_t ieee80211_subif_start_xmit(s
+               break;
+ #endif
+       case NL80211_IFTYPE_STATION:
+-              fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
+-              /* BSSID SA DA */
+               memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN);
+-              memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+-              memcpy(hdr.addr3, skb->data, ETH_ALEN);
+-              hdrlen = 24;
++              if (sdata->use_4addr && ethertype != ETH_P_PAE) {
++                      fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
++                      /* RA TA DA SA */
++                      memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
++                      memcpy(hdr.addr3, skb->data, ETH_ALEN);
++                      memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
++                      hdrlen = 30;
++              } else {
++                      fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
++                      /* BSSID SA DA */
++                      memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
++                      memcpy(hdr.addr3, skb->data, ETH_ALEN);
++                      hdrlen = 24;
++              }
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               /* DA SA BSSID */
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -772,6 +772,7 @@ int ieee80211_if_change_type(struct ieee
+               ieee80211_mandatory_rates(sdata->local,
+                       sdata->local->hw.conf.channel->band);
+       sdata->drop_unencrypted = 0;
++      sdata->use_4addr = 0;
+       return 0;
+ }
+@@ -853,6 +854,9 @@ int ieee80211_if_add(struct ieee80211_lo
+                                           params->mesh_id_len,
+                                           params->mesh_id);
++      if (params && params->use_4addr >= 0)
++              sdata->use_4addr = !!params->use_4addr;
++
+       mutex_lock(&local->iflist_mtx);
+       list_add_tail_rcu(&sdata->list, &local->interfaces);
+       mutex_unlock(&local->iflist_mtx);
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -1237,6 +1237,13 @@ __ieee80211_data_to_8023(struct ieee8021
+ {
+       struct net_device *dev = rx->dev;
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++      struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
++
++      if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->use_4addr &&
++          ieee80211_has_a4(hdr->frame_control))
++              return -1;
++      if (sdata->use_4addr && is_multicast_ether_addr(hdr->addr1))
++              return -1;
+       return ieee80211_data_to_8023(rx->skb, dev->dev_addr, sdata->vif.type);
+ }
+@@ -1590,6 +1597,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_
+ {
+       struct net_device *dev = rx->dev;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
++      struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       __le16 fc = hdr->frame_control;
+       int err;
+@@ -1599,6 +1607,14 @@ ieee80211_rx_h_data(struct ieee80211_rx_
+       if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
+               return RX_DROP_MONITOR;
++      /*
++       * Allow the cooked monitor interface of an AP to see 4-addr frames so
++       * that a 4-addr station can be detected and moved into a separate VLAN
++       */
++      if (ieee80211_has_a4(hdr->frame_control) &&
++          sdata->vif.type == NL80211_IFTYPE_AP)
++              return RX_DROP_MONITOR;
++
+       err = __ieee80211_data_to_8023(rx);
+       if (unlikely(err))
+               return RX_DROP_UNUSABLE;
+@@ -2039,7 +2055,7 @@ static int prepare_for_handlers(struct i
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_STATION:
+-              if (!bssid)
++              if (!bssid && !sdata->use_4addr)
+                       return 0;
+               if (!multicast &&
+                   compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) {
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -320,7 +320,9 @@ int ieee80211_data_to_8023(struct sk_buf
+               break;
+       case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+               if (unlikely(iftype != NL80211_IFTYPE_WDS &&
+-                           iftype != NL80211_IFTYPE_MESH_POINT))
++                           iftype != NL80211_IFTYPE_MESH_POINT &&
++                           iftype != NL80211_IFTYPE_AP_VLAN &&
++                           iftype != NL80211_IFTYPE_STATION))
+                       return -1;
+               if (iftype == NL80211_IFTYPE_MESH_POINT) {
+                       struct ieee80211s_hdr *meshdr =
This page took 0.028937 seconds and 4 git commands to generate.