| 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; |
| 161 | |