mac80211: fix support for iftype wds
[openwrt.git] / package / mac80211 / patches / 551-mac80211_fix_iftype_wds.patch
1 --- a/net/mac80211/rx.c
2 +++ b/net/mac80211/rx.c
3 @@ -2330,13 +2330,14 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_
4  
5         if (!ieee80211_vif_is_mesh(&sdata->vif) &&
6             sdata->vif.type != NL80211_IFTYPE_ADHOC &&
7 -           sdata->vif.type != NL80211_IFTYPE_STATION)
8 +           sdata->vif.type != NL80211_IFTYPE_STATION &&
9 +           sdata->vif.type != NL80211_IFTYPE_WDS)
10                 return RX_DROP_MONITOR;
11  
12         switch (stype) {
13         case cpu_to_le16(IEEE80211_STYPE_BEACON):
14         case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP):
15 -               /* process for all: mesh, mlme, ibss */
16 +               /* process for all: mesh, mlme, ibss, wds */
17                 break;
18         case cpu_to_le16(IEEE80211_STYPE_DEAUTH):
19         case cpu_to_le16(IEEE80211_STYPE_DISASSOC):
20 @@ -2716,7 +2717,10 @@ static int prepare_for_handlers(struct i
21                 }
22                 break;
23         case NL80211_IFTYPE_WDS:
24 -               if (bssid || !ieee80211_is_data(hdr->frame_control))
25 +               if (bssid) {
26 +                       if (!ieee80211_is_beacon(hdr->frame_control))
27 +                               return 0;
28 +               } else if (!ieee80211_is_data(hdr->frame_control))
29                         return 0;
30                 if (compare_ether_addr(sdata->u.wds.remote_addr, hdr->addr2))
31                         return 0;
32 --- a/net/mac80211/iface.c
33 +++ b/net/mac80211/iface.c
34 @@ -178,7 +178,6 @@ static int ieee80211_do_open(struct net_
35  {
36         struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
37         struct ieee80211_local *local = sdata->local;
38 -       struct sta_info *sta;
39         u32 changed = 0;
40         int res;
41         u32 hw_reconf_flags = 0;
42 @@ -290,27 +289,6 @@ static int ieee80211_do_open(struct net_
43  
44         set_bit(SDATA_STATE_RUNNING, &sdata->state);
45  
46 -       if (sdata->vif.type == NL80211_IFTYPE_WDS) {
47 -               /* Create STA entry for the WDS peer */
48 -               sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
49 -                                    GFP_KERNEL);
50 -               if (!sta) {
51 -                       res = -ENOMEM;
52 -                       goto err_del_interface;
53 -               }
54 -
55 -               /* no locking required since STA is not live yet */
56 -               sta->flags |= WLAN_STA_AUTHORIZED;
57 -
58 -               res = sta_info_insert(sta);
59 -               if (res) {
60 -                       /* STA has been freed */
61 -                       goto err_del_interface;
62 -               }
63 -
64 -               rate_control_rate_init(sta);
65 -       }
66 -
67         /*
68          * set_multicast_list will be invoked by the networking core
69          * which will check whether any increments here were done in
70 @@ -344,8 +322,7 @@ static int ieee80211_do_open(struct net_
71         netif_tx_start_all_queues(dev);
72  
73         return 0;
74 - err_del_interface:
75 -       drv_remove_interface(local, &sdata->vif);
76 +
77   err_stop:
78         if (!local->open_count)
79                 drv_stop(local);
80 @@ -717,6 +694,70 @@ static void ieee80211_if_setup(struct ne
81         dev->destructor = free_netdev;
82  }
83  
84 +static void ieee80211_wds_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
85 +                                        struct sk_buff *skb)
86 +{
87 +       struct ieee80211_local *local = sdata->local;
88 +       struct ieee80211_rx_status *rx_status;
89 +       struct ieee802_11_elems elems;
90 +       struct ieee80211_mgmt *mgmt;
91 +       struct sta_info *sta;
92 +       size_t baselen;
93 +       u32 rates = 0;
94 +       u16 stype;
95 +       bool new = false;
96 +       enum ieee80211_band band = local->hw.conf.channel->band;
97 +       struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
98 +
99 +       rx_status = IEEE80211_SKB_RXCB(skb);
100 +       mgmt = (struct ieee80211_mgmt *) skb->data;
101 +       stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
102 +
103 +       if (stype != IEEE80211_STYPE_BEACON)
104 +               return;
105 +
106 +       baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
107 +       if (baselen > skb->len)
108 +               return;
109 +
110 +       ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
111 +                              skb->len - baselen, &elems);
112 +
113 +       rates = ieee80211_sta_get_rates(local, &elems, band);
114 +
115 +       rcu_read_lock();
116 +
117 +       sta = sta_info_get(sdata, sdata->u.wds.remote_addr);
118 +
119 +       if (!sta) {
120 +               rcu_read_unlock();
121 +               sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
122 +                                    GFP_KERNEL);
123 +               if (!sta)
124 +                       return;
125 +
126 +               new = true;
127 +       }
128 +
129 +       sta->last_rx = jiffies;
130 +       sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
131 +
132 +       if (elems.ht_cap_elem)
133 +               ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
134 +                               elems.ht_cap_elem, &sta->sta.ht_cap);
135 +
136 +       if (elems.wmm_param)
137 +               set_sta_flags(sta, WLAN_STA_WME);
138 +
139 +       if (new) {
140 +               sta->flags = WLAN_STA_AUTHORIZED;
141 +               rate_control_rate_init(sta);
142 +               sta_info_insert_rcu(sta);
143 +       }
144 +
145 +       rcu_read_unlock();
146 +}
147 +
148  static void ieee80211_iface_work(struct work_struct *work)
149  {
150         struct ieee80211_sub_if_data *sdata =
151 @@ -821,6 +862,9 @@ static void ieee80211_iface_work(struct 
152                                 break;
153                         ieee80211_mesh_rx_queued_mgmt(sdata, skb);
154                         break;
155 +               case NL80211_IFTYPE_WDS:
156 +                       ieee80211_wds_rx_queued_mgmt(sdata, skb);
157 +                       break;
158                 default:
159                         WARN(1, "frame for unexpected interface type");
160                         break;