mac80211: fix num_mcast_sta counting issues
authorFelix Fietkau <nbd@openwrt.org>
Mon, 23 Apr 2012 17:49:03 +0000 (19:49 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 24 Apr 2012 18:54:28 +0000 (14:54 -0400)
Moving a STA to an AP VLAN prevents num_mcast_sta from being decremented
once the STA leaves, because sta->sdata changes. Fix this by checking
for AP VLANs as well.

Also exclude 4-addr VLAN stations from num_mcast_sta - remote 4-addr
stations ignore 3-address multicast frames anyway. In a typical bridge
configuration they receive the same packets as 4-address unicast.

This patch also fixes clearing the sdata->u.vlan.sta pointer when the
STA is removed from a 4-addr VLAN.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/mac80211/cfg.c
net/mac80211/sta_info.c

index 510a745c31082c129fcc453a9212f29627b50252..70b2af2315a622042960cda4e1296f2265e0522b 100644 (file)
@@ -1005,6 +1005,9 @@ static int ieee80211_change_station(struct wiphy *wiphy,
        }
 
        if (params->vlan && params->vlan != sta->sdata->dev) {
+               bool prev_4addr = false;
+               bool new_4addr = false;
+
                vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
                if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
@@ -1020,9 +1023,25 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                        }
 
                        rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
+                       new_4addr = true;
+               }
+
+               if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+                   sta->sdata->u.vlan.sta) {
+                       rcu_assign_pointer(sta->sdata->u.vlan.sta, NULL);
+                       prev_4addr = true;
                }
 
                sta->sdata = vlansdata;
+
+               if (sta->sta_state == IEEE80211_STA_AUTHORIZED &&
+                   prev_4addr != new_4addr) {
+                       if (new_4addr)
+                               atomic_dec(&sta->sdata->bss->num_mcast_sta);
+                       else
+                               atomic_inc(&sta->sdata->bss->num_mcast_sta);
+               }
+
                ieee80211_send_layer2_update(sta);
        }
 
index 4c04eb5e4caef38330fa7f2d824c64e03899cbe0..97a9d6639fb9c2b5846dd05cec95b8da99f23e9d 100644 (file)
@@ -1417,15 +1417,19 @@ int sta_info_move_state(struct sta_info *sta,
                if (sta->sta_state == IEEE80211_STA_AUTH) {
                        set_bit(WLAN_STA_ASSOC, &sta->_flags);
                } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
-                       if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
-                               atomic_dec(&sta->sdata->u.ap.num_mcast_sta);
+                       if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+                           (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+                            !sta->sdata->u.vlan.sta))
+                               atomic_dec(&sta->sdata->bss->num_mcast_sta);
                        clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
                }
                break;
        case IEEE80211_STA_AUTHORIZED:
                if (sta->sta_state == IEEE80211_STA_ASSOC) {
-                       if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
-                               atomic_inc(&sta->sdata->u.ap.num_mcast_sta);
+                       if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+                           (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+                            !sta->sdata->u.vlan.sta))
+                               atomic_inc(&sta->sdata->bss->num_mcast_sta);
                        set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
                }
                break;