Skip to contentSkip to conversation

refactor station state management

RFC In review Johannes Berg 2 participants 7 messages Dec 13, 2011 – Dec 14, 2011 mac80211

lore
Share & tools
lore

Johannes Berg

author maintainer
#0 lore raw

cover refactor station state management

Here's a first step towards something I talked to some of you about -- making drivers more aware of station state.

Because experience tells me that drivers will be written for the common case, and forget about uncommon sequences, this first step enforces proper ordering of states, now a station can move only between these states like this:
NONE <--> AUTHENTICATED <--> ASSOCIATED <--> AUTHORIZED

As a consequence, IBSS stations and WDS peers need to go through ASSOCIATED state (and get ASSOC flag set), but this doesn't seem to be an issue, in fact it makes at least one place in the code simpler.

This patchset still has two issues:
* secure mesh is broken right now I think, its state change possibilities are rather confusing (need help to fix this, at least explanations)
* TLDS isn't handled (maybe not an issue?)

My further plan is the following:
1) figure out the issues above
2) notify drivers of these state changes
3) refactor auth/assoc process as described before
a) don't track multiple authentications
b) don't try to keep an existing authentication/association
c) set channel first etc.
4) remove dummy station code
a) add regular station entry before auth
b) notify to AUTH state when auth succeeds
c) notify to ASSOC state when assoc succeeds

This patchset is but the first step on this journey, at the end of which we will hopefully find multi-channel support :-)

johannes

author maintainer
#1 lore raw

RFC 1/4 mac80211: use station mutex in configuration

From: Johannes Berg <[email protected]>

There's no need to use RCU here, we can just lock
the station mutex instead. This allows the code
to sleep, which is necessary for later patches.

---
 net/mac80211/cfg.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
net/mac80211/cfg.c +6 -6
@@ -960,11 +960,11 @@ static int ieee80211_change_station(stru
 	struct sta_info *sta;
 	struct ieee80211_sub_if_data *vlansdata;
 
-	rcu_read_lock();
+	mutex_lock(&local->sta_mtx);
 
 	sta = sta_info_get_bss(sdata, mac);
 	if (!sta) {
-		rcu_read_unlock();
+		mutex_unlock(&local->sta_mtx);
 		return -ENOENT;
 	}
 
@@ -972,7 +972,7 @@ static int ieee80211_change_station(stru
 	if ((params->sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
 	    !!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) !=
 	    !!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
-		rcu_read_unlock();
+		mutex_unlock(&local->sta_mtx);
 		return -EINVAL;
 	}
 
@@ -981,13 +981,13 @@ static int ieee80211_change_station(stru
 
 		if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
 		    vlansdata->vif.type != NL80211_IFTYPE_AP) {
-			rcu_read_unlock();
+			mutex_unlock(&local->sta_mtx);
 			return -EINVAL;
 		}
 
 		if (params->vlan->ieee80211_ptr->use_4addr) {
 			if (vlansdata->u.vlan.sta) {
-				rcu_read_unlock();
+				mutex_unlock(&local->sta_mtx);
 				return -EBUSY;
 			}
 
@@ -1003,7 +1003,7 @@ static int ieee80211_change_station(stru
 	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates)
 		rate_control_rate_init(sta);
 
-	rcu_read_unlock();
+	mutex_unlock(&local->sta_mtx);
 
 	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
 	    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))
author maintainer
#2 lore raw

RFC 2/4 mac80211: refactor station state transitions

In reply to Johannes Berg

From: Johannes Berg <[email protected]>

Station entries can have various states, the most important ones being auth, assoc and authorized. This patch prepares us for telling the driver about these states, we don't want to confuse drivers with strange transitions, so with this we enforce that they move in the right order between them (back and forth); some transitions might happen before the driver even knows about the station, but at least runtime transitions will be ordered correctly.

As a consequence, IBSS and MESH stations will now have the ASSOC flag set (so they can transition to AUTHORIZED), and we can get rid of a special case in TX processing.

TBD: WLAN_STA_TDLS_PEER_AUTH?
TBD: secure mesh AUTH transitions?

---
 net/mac80211/cfg.c        |   44 +++++++++++++++++++++-----------
 net/mac80211/ibss.c       |    5 ++-
 net/mac80211/iface.c      |    5 ++-
 net/mac80211/mesh_plink.c |    7 +++--
 net/mac80211/mlme.c       |    6 ++--
 net/mac80211/sta_info.c   |   62 +++++++++++++++++++++++++++++++++++++++++-----
 net/mac80211/sta_info.h   |   36 ++++++++++++++++++++++++++
 net/mac80211/tx.c         |    1 
 8 files changed, 136 insertions(+), 30 deletions(-)
net/mac80211/sta_info.c +56 -6
@@ -204,16 +204,17 @@ struct sta_info *sta_info_get_by_idx(str
 }
 
 /**
- * __sta_info_free - internal STA free helper
+ * sta_info_free - free STA
  *
  * @local: pointer to the global information
  * @sta: STA info to free
  *
  * This function must undo everything done by sta_info_alloc()
- * that may happen before sta_info_insert().
+ * that may happen before sta_info_insert(). It may only be
+ * called when sta_info_insert() has not been attempted (and
+ * if that fails, the station is freed anyway.)
  */
-static void __sta_info_free(struct ieee80211_local *local,
-			    struct sta_info *sta)
+void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 {
 	if (sta->rate_ctrl) {
 		rate_control_free_sta(sta);
@@ -598,7 +599,7 @@ int sta_info_insert_rcu(struct sta_info
 	return 0;
  out_free:
 	BUG_ON(!err);
-	__sta_info_free(local, sta);
+	sta_info_free(local, sta);
 	return err;
 }
 
@@ -949,7 +950,7 @@ static int __must_check __sta_info_destr
 	}
 #endif
 
-	__sta_info_free(local, sta);
+	sta_info_free(local, sta);
 
 	return 0;
 }
@@ -1513,3 +1514,52 @@ void ieee80211_sta_set_buffered(struct i
 	sta_info_recalc_tim(sta);
 }
 EXPORT_SYMBOL(ieee80211_sta_set_buffered);
+
+int sta_info_move_state_checked(struct sta_info *sta,
+				enum ieee80211_sta_state new_state)
+{
+	might_sleep(); /* for driver notify later */
+
+	if (sta->sta_state == new_state)
+		return 0;
+
+	switch (new_state) {
+	case IEEE80211_STA_NONE:
+		if (sta->sta_state == IEEE80211_STA_AUTH)
+			clear_bit(WLAN_STA_AUTH, &sta->_flags);
+		else
+			return -EINVAL;
+		break;
+	case IEEE80211_STA_AUTH:
+		if (sta->sta_state == IEEE80211_STA_NONE)
+			set_bit(WLAN_STA_AUTH, &sta->_flags);
+		else if (sta->sta_state == IEEE80211_STA_ASSOC)
+			clear_bit(WLAN_STA_ASSOC, &sta->_flags);
+		else
+			return -EINVAL;
+		break;
+	case IEEE80211_STA_ASSOC:
+		if (sta->sta_state == IEEE80211_STA_AUTH)
+			set_bit(WLAN_STA_ASSOC, &sta->_flags);
+		else if (sta->sta_state == IEEE80211_STA_AUTHORIZED)
+			clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
+		else
+			return -EINVAL;
+		break;
+	case IEEE80211_STA_AUTHORIZED:
+		if (sta->sta_state == IEEE80211_STA_ASSOC)
+			set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
+		else
+			return -EINVAL;
+		break;
+	default:
+		WARN(1, "invalid state %d", new_state);
+		return -EINVAL;
+	}
+
+	printk(KERN_DEBUG "%s: moving STA %pM to state %d\n",
+		sta->sdata->name, sta->sta.addr, new_state);
+	sta->sta_state = new_state;
+
+	return 0;
+}
net/mac80211/sta_info.h +36 -0
@@ -73,6 +73,13 @@ enum ieee80211_sta_info_flags {
 	WLAN_STA_4ADDR_EVENT,
 };
 
+enum ieee80211_sta_state {
+	IEEE80211_STA_NONE,
+	IEEE80211_STA_AUTH,
+	IEEE80211_STA_ASSOC,
+	IEEE80211_STA_AUTHORIZED,
+};
+
 #define STA_TID_NUM 16
 #define ADDBA_RESP_INTERVAL HZ
 #define HT_AGG_MAX_RETRIES		0x3
@@ -262,6 +269,7 @@ struct sta_ampdu_mlme {
  * @dummy: indicate a dummy station created for receiving
  *	EAP frames before association
  * @sta: station information we share with the driver
+ * @sta_state: duplicates information about station state (for debug)
  */
 struct sta_info {
 	/* General information, mostly static */
@@ -283,6 +291,8 @@ struct sta_info {
 
 	bool uploaded;
 
+	enum ieee80211_sta_state sta_state;
+
 	/* use the accessors defined below */
 	unsigned long _flags;
 
@@ -371,12 +381,18 @@ static inline enum nl80211_plink_state s
 static inline void set_sta_flag(struct sta_info *sta,
 				enum ieee80211_sta_info_flags flag)
 {
+	WARN_ON(flag == WLAN_STA_AUTH ||
+		flag == WLAN_STA_ASSOC ||
+		flag == WLAN_STA_AUTHORIZED);
 	set_bit(flag, &sta->_flags);
 }
 
 static inline void clear_sta_flag(struct sta_info *sta,
 				  enum ieee80211_sta_info_flags flag)
 {
+	WARN_ON(flag == WLAN_STA_AUTH ||
+		flag == WLAN_STA_ASSOC ||
+		flag == WLAN_STA_AUTHORIZED);
 	clear_bit(flag, &sta->_flags);
 }
 
@@ -389,15 +405,32 @@ static inline int test_sta_flag(struct s
 static inline int test_and_clear_sta_flag(struct sta_info *sta,
 					  enum ieee80211_sta_info_flags flag)
 {
+	WARN_ON(flag == WLAN_STA_AUTH ||
+		flag == WLAN_STA_ASSOC ||
+		flag == WLAN_STA_AUTHORIZED);
 	return test_and_clear_bit(flag, &sta->_flags);
 }
 
 static inline int test_and_set_sta_flag(struct sta_info *sta,
 					enum ieee80211_sta_info_flags flag)
 {
+	WARN_ON(flag == WLAN_STA_AUTH ||
+		flag == WLAN_STA_ASSOC ||
+		flag == WLAN_STA_AUTHORIZED);
 	return test_and_set_bit(flag, &sta->_flags);
 }
 
+int sta_info_move_state_checked(struct sta_info *sta,
+				enum ieee80211_sta_state new_state);
+
+static inline void sta_info_move_state(struct sta_info *sta,
+				       enum ieee80211_sta_state new_state)
+{
+	int ret = sta_info_move_state_checked(sta, new_state);
+	WARN_ON_ONCE(ret);
+}
+
+
 void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
 			     struct tid_ampdu_tx *tid_tx);
 
@@ -489,6 +522,9 @@ struct sta_info *sta_info_get_by_idx(str
  */
 struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 				u8 *addr, gfp_t gfp);
+
+void sta_info_free(struct ieee80211_local *local, struct sta_info *sta);
+
 /*
  * Insert STA info into hash table/list, returns zero or a
  * -EEXIST if (if the same MAC address is already present).
net/mac80211/cfg.c +29 -15

79 lines not on this page · raw message

net/mac80211/ibss.c +4 -1
@@ -512,7 +512,10 @@ struct sta_info *ieee80211_ibss_add_sta(
 		return NULL;
 
 	sta->last_rx = jiffies;
-	set_sta_flag(sta, WLAN_STA_AUTHORIZED);
+
+	sta_info_move_state(sta, IEEE80211_STA_AUTH);
+	sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+	sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
 
 	/* make sure mandatory rates are always added */
 	sta->sta.supp_rates[band] = supp_rates |
net/mac80211/iface.c +3 -2
@@ -318,8 +318,9 @@ static int ieee80211_do_open(struct net_
 			goto err_del_interface;
 		}
 
-		/* no atomic bitop required since STA is not live yet */
-		set_sta_flag(sta, WLAN_STA_AUTHORIZED);
+		sta_info_move_state(sta, IEEE80211_STA_AUTH);
+		sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+		sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
 
 		res = sta_info_insert(sta);
 		if (res) {
net/mac80211/mesh_plink.c +5 -2
@@ -96,9 +96,12 @@ static struct sta_info *mesh_plink_alloc
 	if (!sta)
 		return NULL;
 
-	set_sta_flag(sta, WLAN_STA_AUTH);
-	set_sta_flag(sta, WLAN_STA_AUTHORIZED);
+	sta_info_move_state(sta, IEEE80211_STA_AUTH);
+	sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+	sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+
 	set_sta_flag(sta, WLAN_STA_WME);
+
 	sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
 	if (elems->ht_cap_elem)
 		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
net/mac80211/mlme.c +3 -3
@@ -1577,10 +1577,10 @@ static bool ieee80211_assoc_success(stru
 		return false;
 	}
 
-	set_sta_flag(sta, WLAN_STA_AUTH);
-	set_sta_flag(sta, WLAN_STA_ASSOC);
+	sta_info_move_state(sta, IEEE80211_STA_AUTH);
+	sta_info_move_state(sta, IEEE80211_STA_ASSOC);
 	if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
-		set_sta_flag(sta, WLAN_STA_AUTHORIZED);
+		sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
 
 	rates = 0;
 	basic_rates = 0;
net/mac80211/tx.c +0 -1
@@ -295,7 +295,6 @@ ieee80211_tx_h_check_assoc(struct ieee80
 
 	if (likely(tx->flags & IEEE80211_TX_UNICAST)) {
 		if (unlikely(!assoc &&
-			     tx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
 			     ieee80211_is_data(hdr->frame_control))) {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 			printk(KERN_DEBUG "%s: dropped data frame to not "
#3 lore raw

Re: [RFC 2/4] mac80211: refactor station state transitions

Quoted 12 lines · Johannes Berg

From: Johannes Berg <[email protected]>

Station entries can have various states, the most important ones being auth, assoc and authorized. This patch prepares us for telling the driver about these states, we don't want to confuse drivers with strange transitions, so with this we enforce that they move in the right order between them (back and forth); some transitions might happen before the driver even knows about the station, but at least runtime transitions will be ordered correctly.

Quoted 7 lines

As a consequence, IBSS and MESH stations will now have the ASSOC flag set (so they can transition to AUTHORIZED), and we can get rid of a special case in TX processing.

TBD: WLAN_STA_TDLS_PEER_AUTH?
TBD: secure mesh AUTH transitions?

This patch makes it impossible to add a new unauthenticated station, AFAICT. Maybe this is not even desirable from mac80211's POV? The reason the secure mesh daemon adds a new unauthenticated peer is to stop NL80211_NEW_PEER_CANDIDATE notifications while userspace is handling the authentication process, and we need a station entry for ieee80211_mgmt_tx().

Some other mechanism could be implemented to halt new peer notifications, or just allow userspace to add a station in state NONE (ieee80211_tx_h_check_assoc() doesn't even check association flag for mesh interfaces).

Thomas

Quoted 366 lines

---
 net/mac80211/cfg.c        |   44 +++++++++++++++++++++----------- net/mac80211/ibss.c       |    5 ++-
 net/mac80211/iface.c      |    5 ++-
 net/mac80211/mesh_plink.c |    7 +++--
 net/mac80211/mlme.c       |    6 ++--
 net/mac80211/sta_info.c   |   62 +++++++++++++++++++++++++++++++++++++++++----- net/mac80211/sta_info.h   |   36 ++++++++++++++++++++++++++ net/mac80211/tx.c         |    1

 8 files changed, 136 insertions(+), 30 deletions(-)
net/mac80211/sta_info.c   2011-12-13 21:03:19.000000000 +0100 renamed from net/mac80211/sta_info.c   2011-12-13 21:01:26.000000000 +0100 +5 -4
@@ -204,16 +204,17 @@ struct sta_info *sta_info_get_by_idx(str
  }
 
  /**
- * __sta_info_free - internal STA free helper
+ * sta_info_free - free STA
  *
  * @local: pointer to the global information
  * @sta: STA info to free
  *
  * This function must undo everything done by sta_info_alloc()
- * that may happen before sta_info_insert().
+ * that may happen before sta_info_insert(). It may only be
+ * called when sta_info_insert() has not been attempted (and
+ * if that fails, the station is freed anyway.)
  */
-static void __sta_info_free(struct ieee80211_local *local,
-                           struct sta_info *sta)
+void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
  {
        if (sta->rate_ctrl) {
                rate_control_free_sta(sta);
@@ -598,7 +599,7 @@ int sta_info_insert_rcu(struct sta_info
        return 0;

 out_free:
       BUG_ON(!err);
-       __sta_info_free(local, sta);
+       sta_info_free(local, sta);
       return err;
 }

net/mac80211/sta_info.c   2011-12-13 21:03:19.000000000 +0100 renamed from net/mac80211/sta_info.c   2011-12-13 21:01:26.000000000 +0100 +50 -1
@@ -949,7 +950,7 @@ static int __must_check __sta_info_destr
        }
  #endif
 
-       __sta_info_free(local, sta);
+       sta_info_free(local, sta);
 
        return 0;
  }
@@ -1513,3 +1514,52 @@ void ieee80211_sta_set_buffered(struct i
        sta_info_recalc_tim(sta);
  }
  EXPORT_SYMBOL(ieee80211_sta_set_buffered);
+
+int sta_info_move_state_checked(struct sta_info *sta,
+                               enum ieee80211_sta_state new_state)
+{
+       might_sleep(); /* for driver notify later */
+
+       if (sta->sta_state == new_state)
+               return 0;
+
+       switch (new_state) {
+       case IEEE80211_STA_NONE:
+               if (sta->sta_state == IEEE80211_STA_AUTH)
+                       clear_bit(WLAN_STA_AUTH, &sta->_flags);
+               else
+                       return -EINVAL;
+               break;
+       case IEEE80211_STA_AUTH:
+               if (sta->sta_state == IEEE80211_STA_NONE)
+                       set_bit(WLAN_STA_AUTH, &sta->_flags);
+               else if (sta->sta_state == IEEE80211_STA_ASSOC)
+                       clear_bit(WLAN_STA_ASSOC, &sta->_flags);
+               else
+                       return -EINVAL;
+               break;
+       case IEEE80211_STA_ASSOC:
+               if (sta->sta_state == IEEE80211_STA_AUTH)
+                       set_bit(WLAN_STA_ASSOC, &sta->_flags);
+               else if (sta->sta_state == IEEE80211_STA_AUTHORIZED)
+                       clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
+               else
+                       return -EINVAL;
+               break;
+       case IEEE80211_STA_AUTHORIZED:
+               if (sta->sta_state == IEEE80211_STA_ASSOC)
+                       set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
+               else
+                       return -EINVAL;
+               break;
+       default:
+               WARN(1, "invalid state %d", new_state);
+               return -EINVAL;
+       }
+
+       printk(KERN_DEBUG "%s: moving STA %pM to state %d\n",
+               sta->sdata->name, sta->sta.addr, new_state);
+       sta->sta_state = new_state;
+
+       return 0;
+}
net/mac80211/sta_info.h   2011-12-13 21:03:10.000000000 +0100 renamed from net/mac80211/sta_info.h   2011-12-13 21:01:26.000000000 +0100 +36 -0
@@ -73,6 +73,13 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_4ADDR_EVENT,
  };
 
+enum ieee80211_sta_state {
+       IEEE80211_STA_NONE,
+       IEEE80211_STA_AUTH,
+       IEEE80211_STA_ASSOC,
+       IEEE80211_STA_AUTHORIZED,
+};
+
  #define STA_TID_NUM 16
  #define ADDBA_RESP_INTERVAL HZ
  #define HT_AGG_MAX_RETRIES             0x3
@@ -262,6 +269,7 @@ struct sta_ampdu_mlme {
  * @dummy: indicate a dummy station created for receiving
  *     EAP frames before association
  * @sta: station information we share with the driver
+ * @sta_state: duplicates information about station state (for debug)
  */
  struct sta_info {
        /* General information, mostly static */
@@ -283,6 +291,8 @@ struct sta_info {
 
        bool uploaded;
 
+       enum ieee80211_sta_state sta_state;
+
        /* use the accessors defined below */
        unsigned long _flags;
 
@@ -371,12 +381,18 @@ static inline enum nl80211_plink_state s
  static inline void set_sta_flag(struct sta_info *sta,
                                enum ieee80211_sta_info_flags flag)
  {
+       WARN_ON(flag == WLAN_STA_AUTH ||
+               flag == WLAN_STA_ASSOC ||
+               flag == WLAN_STA_AUTHORIZED);
        set_bit(flag, &sta->_flags);
  }
 
  static inline void clear_sta_flag(struct sta_info *sta,
                                  enum ieee80211_sta_info_flags flag)
  {
+       WARN_ON(flag == WLAN_STA_AUTH ||
+               flag == WLAN_STA_ASSOC ||
+               flag == WLAN_STA_AUTHORIZED);
        clear_bit(flag, &sta->_flags);
  }
 
@@ -389,15 +405,32 @@ static inline int test_sta_flag(struct s
  static inline int test_and_clear_sta_flag(struct sta_info *sta,
                                          enum ieee80211_sta_info_flags flag)
  {
+       WARN_ON(flag == WLAN_STA_AUTH ||
+               flag == WLAN_STA_ASSOC ||
+               flag == WLAN_STA_AUTHORIZED);
        return test_and_clear_bit(flag, &sta->_flags);
  }
 
  static inline int test_and_set_sta_flag(struct sta_info *sta,
                                        enum ieee80211_sta_info_flags flag)
  {
+       WARN_ON(flag == WLAN_STA_AUTH ||
+               flag == WLAN_STA_ASSOC ||
+               flag == WLAN_STA_AUTHORIZED);
        return test_and_set_bit(flag, &sta->_flags);
  }
 
+int sta_info_move_state_checked(struct sta_info *sta,
+                               enum ieee80211_sta_state new_state);
+
+static inline void sta_info_move_state(struct sta_info *sta,
+                                      enum ieee80211_sta_state new_state)
+{
+       int ret = sta_info_move_state_checked(sta, new_state);
+       WARN_ON_ONCE(ret);
+}
+
+
  void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
                             struct tid_ampdu_tx *tid_tx);
 
@@ -489,6 +522,9 @@ struct sta_info *sta_info_get_by_idx(str
  */
  struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                                u8 *addr, gfp_t gfp);
+
+void sta_info_free(struct ieee80211_local *local, struct sta_info *sta);
+
  /*
  * Insert STA info into hash table/list, returns zero or a
  * -EEXIST if (if the same MAC address is already present).
net/mac80211/cfg.c        2011-12-13 21:03:10.000000000 +0100 renamed from net/mac80211/cfg.c        2011-12-13 21:01:28.000000000 +0100 +6 -4
@@ -746,10 +746,11 @@ static void ieee80211_send_layer2_update
        netif_rx_ni(skb);
  }
 
-static void sta_apply_parameters(struct ieee80211_local *local,
-                                struct sta_info *sta,
-                                struct station_parameters *params)
+static int sta_apply_parameters(struct ieee80211_local *local,
+                               struct sta_info *sta,
+                               struct station_parameters *params)
  {
+       int ret = 0;
        u32 rates;
        int i, j;
        struct ieee80211_supported_band *sband;
@@ -763,11 +764,25 @@ static void sta_apply_parameters(struct
 
        if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
                if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
-                       set_sta_flag(sta, WLAN_STA_AUTHORIZED);
+                       ret = sta_info_move_state_checked(sta,
+                                       IEEE80211_STA_AUTHORIZED);

               else
-                       clear_sta_flag(sta, WLAN_STA_AUTHORIZED);
+                       ret = sta_info_move_state_checked(sta,
+                                       IEEE80211_STA_ASSOC); }

+       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
+               if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
+                       ret = sta_info_move_state_checked(sta,
+                                       IEEE80211_STA_AUTH);
+               else
+                       ret = sta_info_move_state_checked(sta,
+                                       IEEE80211_STA_NONE);
+       }

net/mac80211/cfg.c        2011-12-13 21:03:10.000000000 +0100 renamed from net/mac80211/cfg.c        2011-12-13 21:01:28.000000000 +0100 +4 -0
+
+       if (ret)
+               return ret;
+

       if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);

net/mac80211/cfg.c        2011-12-13 21:03:10.000000000 +0100 renamed from net/mac80211/cfg.c        2011-12-13 21:01:28.000000000 +0100 +9 -10
@@ -792,13 +807,6 @@ static void sta_apply_parameters(struct
                        clear_sta_flag(sta, WLAN_STA_MFP);
        }
 
-       if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
-               if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
-                       set_sta_flag(sta, WLAN_STA_AUTH);
-               else
-                       clear_sta_flag(sta, WLAN_STA_AUTH);
-       }
-
        if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
                if (set & BIT(NL80211_STA_FLAG_TDLS_PEER))
                        set_sta_flag(sta, WLAN_STA_TDLS_PEER);
@@ -870,6 +878,8 @@ static void sta_apply_parameters(struct
                        }
  #endif
        }
+
+       return 0;
  }
 
  static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
@@ -906,10 +916,14 @@ static int ieee80211_add_station(struct
        if (!sta)
                return -ENOMEM;
 
-       set_sta_flag(sta, WLAN_STA_AUTH);
-       set_sta_flag(sta, WLAN_STA_ASSOC);
+       sta_info_move_state(sta, IEEE80211_STA_AUTH);
+       sta_info_move_state(sta, IEEE80211_STA_ASSOC);
 
-       sta_apply_parameters(local, sta, params);
+       err = sta_apply_parameters(local, sta, params);
+       if (err) {
+               sta_info_free(local, sta);
+               return err;
+       }
 
        /*
         * for TDLS, rate control should be initialized only when supported
net/mac80211/ibss.c       2011-12-13 21:03:10.000000000 +0100 renamed from net/mac80211/ibss.c       2011-12-13 21:01:26.000000000 +0100 +4 -1
@@ -512,7 +512,10 @@ struct sta_info *ieee80211_ibss_add_sta(
                return NULL;
 
        sta->last_rx = jiffies;
-       set_sta_flag(sta, WLAN_STA_AUTHORIZED);
+
+       sta_info_move_state(sta, IEEE80211_STA_AUTH);
+       sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+       sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
 
        /* make sure mandatory rates are always added */

       sta->sta.supp_rates[band] = supp_rates |

net/mac80211/iface.c      2011-12-13 21:03:10.000000000 +0100 renamed from net/mac80211/iface.c      2011-12-13 21:01:26.000000000 +0100 +3 -2
@@ -318,8 +318,9 @@ static int ieee80211_do_open(struct net_
                        goto err_del_interface;
                }
 
-               /* no atomic bitop required since STA is not live yet */
-               set_sta_flag(sta, WLAN_STA_AUTHORIZED);
+               sta_info_move_state(sta, IEEE80211_STA_AUTH);
+               sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+               sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
 
                res = sta_info_insert(sta);
                if (res) {
net/mac80211/mesh_plink.c 2011-12-13 21:03:10.000000000 +0100 renamed from net/mac80211/mesh_plink.c 2011-12-13 21:01:26.000000000 +0100 +5 -2
@@ -96,9 +96,12 @@ static struct sta_info *mesh_plink_alloc
        if (!sta)
                return NULL;
 
-       set_sta_flag(sta, WLAN_STA_AUTH);
-       set_sta_flag(sta, WLAN_STA_AUTHORIZED);
+       sta_info_move_state(sta, IEEE80211_STA_AUTH);
+       sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+       sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+
        set_sta_flag(sta, WLAN_STA_WME);
+
        sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
        if (elems->ht_cap_elem)
                ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
net/mac80211/mlme.c       2011-12-13 21:03:10.000000000 +0100 renamed from net/mac80211/mlme.c       2011-12-13 21:01:26.000000000 +0100 +3 -3
@@ -1577,10 +1577,10 @@ static bool ieee80211_assoc_success(stru
                return false;
        }
 
-       set_sta_flag(sta, WLAN_STA_AUTH);
-       set_sta_flag(sta, WLAN_STA_ASSOC);
+       sta_info_move_state(sta, IEEE80211_STA_AUTH);
+       sta_info_move_state(sta, IEEE80211_STA_ASSOC);
        if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
-               set_sta_flag(sta, WLAN_STA_AUTHORIZED);
+               sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
 
        rates = 0;
        basic_rates = 0;
net/mac80211/tx.c 2011-12-13 21:03:10.000000000 +0100 renamed from net/mac80211/tx.c 2011-12-13 21:01:26.000000000 +0100 +0 -0
@@ -295,7 +295,6 @@ ieee80211_tx_h_check_assoc(struct ieee80
 
        if (likely(tx->flags & IEEE80211_TX_UNICAST)) {

               if (unlikely(!assoc &&
-                            tx->sdata->vif.type != NL80211_IFTYPE_ADHOC && ieee80211_is_data(hdr->frame_control))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
                       printk(KERN_DEBUG "%s: dropped data frame to not "

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

author maintainer
#4 lore raw

Re: [RFC 2/4] mac80211: refactor station state transitions

Quoted 22 lines · Thomas Pedersen
Quoted 12 lines · Johannes Berg

From: Johannes Berg <[email protected]>

Station entries can have various states, the most important ones being auth, assoc and authorized. This patch prepares us for telling the driver about these states, we don't want to confuse drivers with strange transitions, so with this we enforce that they move in the right order between them (back and forth); some transitions might happen before the driver even knows about the station, but at least runtime transitions will be ordered correctly.

Quoted 7 lines

As a consequence, IBSS and MESH stations will now have the ASSOC flag set (so they can transition to AUTHORIZED), and we can get rid of a special case in TX processing.

TBD: WLAN_STA_TDLS_PEER_AUTH?
TBD: secure mesh AUTH transitions?

Quoted 6 lines

This patch makes it impossible to add a new unauthenticated station, AFAICT. Maybe this is not even desirable from mac80211's POV? The reason the secure mesh daemon adds a new unauthenticated peer is to stop NL80211_NEW_PEER_CANDIDATE notifications while userspace is handling the authentication process, and we need a station entry for ieee80211_mgmt_tx().

Ah, yes. I had to sleep over this -- now I understand why I was confused. I'll amend the patch series to properly manage more of this in cfg80211. I have a feeling we should also allow userspace to manage the ASSOC state, but can we require it from the secure mesh userspace daemon? It would break backward compatibility if we do ... I'll try to find a workaround :)

johannes

author maintainer
#5 lore raw

RFC 3/4 mac80211: unwind station state on destroy

In reply to Johannes Berg

From: Johannes Berg <[email protected]>

Before destroying a station entry, unwind its
state so drivers and other code can rely on
state transitions happening.

---
 net/mac80211/sta_info.c |    3 +++
 net/mac80211/sta_info.h |    1 +
 2 files changed, 4 insertions(+)
net/mac80211/sta_info.h +1 -0
@@ -74,6 +74,7 @@ enum ieee80211_sta_info_flags {
 };
 
 enum ieee80211_sta_state {
+	/* NOTE: These need to be ordered correctly! */
 	IEEE80211_STA_NONE,
 	IEEE80211_STA_AUTH,
 	IEEE80211_STA_ASSOC,
net/mac80211/sta_info.c +3 -0
@@ -905,6 +905,9 @@ static int __must_check __sta_info_destr
 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
 
+	while (sta->sta_state > IEEE80211_STA_NONE)
+		sta_info_move_state(sta, sta->sta_state - 1);
+
 	if (sta->uploaded) {
 		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 			sdata = container_of(sdata->bss,
author maintainer
#6 lore raw

RFC 4/4 mac80211: count authorized stations per BSS

In reply to Johannes Berg

From: Johannes Berg <[email protected]>

Currently, each AP interface will send multicast traffic if any interface has a station entry even if that station entry is allocated only. With the new station state management we can easily fix it by adding a counter that counts each authorized station only and send multicast traffic only when the correct interface has at least one authorized station.

---
 net/mac80211/debugfs_netdev.c |    2 ++
 net/mac80211/ieee80211_i.h    |    1 +
 net/mac80211/sta_info.c       |   14 +++++++++-----
 net/mac80211/tx.c             |   19 ++++++++-----------
 4 files changed, 20 insertions(+), 16 deletions(-)
net/mac80211/debugfs_netdev.c +2 -0
@@ -321,6 +321,7 @@ static ssize_t ieee80211_if_parse_tkip_m
 __IEEE80211_IF_FILE_W(tkip_mic_test);
 
 /* AP attributes */
+IEEE80211_IF_FILE(num_sta_authorized, u.ap.num_sta_authorized, ATOMIC);
 IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
 IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC);
 
@@ -458,6 +459,7 @@ static void add_ap_files(struct ieee8021
 	DEBUGFS_ADD(rc_rateidx_mask_2ghz);
 	DEBUGFS_ADD(rc_rateidx_mask_5ghz);
 
+	DEBUGFS_ADD(num_sta_authorized);
 	DEBUGFS_ADD(num_sta_ps);
 	DEBUGFS_ADD(dtim_count);
 	DEBUGFS_ADD(num_buffered_multicast);
net/mac80211/ieee80211_i.h +1 -0
@@ -243,6 +243,7 @@ struct ieee80211_if_ap {
 	u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)];
 	struct sk_buff_head ps_bc_buf;
 	atomic_t num_sta_ps; /* number of stations in PS mode */
+	atomic_t num_sta_authorized; /* number of authorized stations */
 	int dtim_count;
 	bool dtim_bc_mc;
 };
net/mac80211/sta_info.c +9 -5
@@ -1542,17 +1542,21 @@ int sta_info_move_state_checked(struct s
 			return -EINVAL;
 		break;
 	case IEEE80211_STA_ASSOC:
-		if (sta->sta_state == IEEE80211_STA_AUTH)
+		if (sta->sta_state == IEEE80211_STA_AUTH) {
 			set_bit(WLAN_STA_ASSOC, &sta->_flags);
-		else if (sta->sta_state == IEEE80211_STA_AUTHORIZED)
+		} else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
+			if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
+				atomic_dec(&sta->sdata->u.ap.num_sta_authorized);
 			clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
-		else
+		} else
 			return -EINVAL;
 		break;
 	case IEEE80211_STA_AUTHORIZED:
-		if (sta->sta_state == IEEE80211_STA_ASSOC)
+		if (sta->sta_state == IEEE80211_STA_ASSOC) {
+			if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
+				atomic_inc(&sta->sdata->u.ap.num_sta_authorized);
 			set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
-		else
+		} else
 			return -EINVAL;
 		break;
 	default:
net/mac80211/tx.c +8 -11
@@ -304,17 +304,14 @@ ieee80211_tx_h_check_assoc(struct ieee80
 			I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);
 			return TX_DROP;
 		}
-	} else {
-		if (unlikely(ieee80211_is_data(hdr->frame_control) &&
-			     tx->local->num_sta == 0 &&
-			     tx->sdata->vif.type != NL80211_IFTYPE_ADHOC)) {
-			/*
-			 * No associated STAs - no need to send multicast
-			 * frames.
-			 */
-			return TX_DROP;
-		}
-		return TX_CONTINUE;
+	} else if (unlikely(tx->sdata->vif.type == NL80211_IFTYPE_AP &&
+			    ieee80211_is_data(hdr->frame_control) &&
+			    !atomic_read(&tx->sdata->u.ap.num_sta_authorized))) {
+		/*
+		 * No associated STAs - no need to send multicast
+		 * frames.
+		 */
+		return TX_DROP;
 	}
 
 	return TX_CONTINUE;