nl80211: Add support for full station state operations
authorAyala Beker <ayala.beker@intel.com>
Tue, 16 Feb 2016 09:54:31 +0000 (11:54 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 20 Feb 2016 16:25:13 +0000 (18:25 +0200)
This provides means for determining whether the driver supports full AP
station state and setting the needed STA flags for using this
functionality.

Signed-off-by: Ayala Beker <ayala.beker@intel.com>
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/drivers/driver_nl80211_capa.c

index a4f6704..3e9e0a7 100644 (file)
@@ -1260,8 +1260,13 @@ struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY   0x0000004000000000ULL
 /** Driver supports simultaneous off-channel operations */
 #define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS       0x0000008000000000ULL
+/** Driver supports full AP client state */
+#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE  0x0000010000000000ULL
        u64 flags;
 
+#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
+       (drv_flags & WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE)
+
 #define WPA_DRIVER_SMPS_MODE_STATIC                    0x00000001
 #define WPA_DRIVER_SMPS_MODE_DYNAMIC                   0x00000002
        unsigned int smps_modes;
@@ -1508,6 +1513,7 @@ struct wpa_bss_params {
 #define WPA_STA_MFP BIT(3)
 #define WPA_STA_TDLS_PEER BIT(4)
 #define WPA_STA_AUTHENTICATED BIT(5)
+#define WPA_STA_ASSOCIATED BIT(6)
 
 enum tdls_oper {
        TDLS_DISCOVERY_REQ,
@@ -2369,12 +2375,17 @@ struct wpa_driver_ops {
         * @params: Station parameters
         * Returns: 0 on success, -1 on failure
         *
-        * This function is used to add a station entry to the driver once the
-        * station has completed association. This is only used if the driver
+        * This function is used to add or set (params->set 1) a station
+        * entry in the driver. Adding STA entries is used only if the driver
         * does not take care of association processing.
         *
-        * With TDLS, this function is also used to add or set (params->set 1)
-        * TDLS peer entries.
+        * With drivers that don't support full AP client state, this function
+        * is used to add a station entry to the driver once the station has
+        * completed association.
+        *
+        * With TDLS, this function is used to add or set (params->set 1)
+        * TDLS peer entries (even with drivers that do not support full AP
+        * client state).
         */
        int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
 
index e4c90a9..d09be88 100644 (file)
@@ -3748,6 +3748,8 @@ static u32 sta_flags_nl80211(int flags)
                f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
        if (flags & WPA_STA_AUTHENTICATED)
                f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+       if (flags & WPA_STA_ASSOCIATED)
+               f |= BIT(NL80211_STA_FLAG_ASSOCIATED);
 
        return f;
 }
@@ -3800,7 +3802,17 @@ static int wpa_driver_nl80211_sta_add(void *priv,
        if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
                goto fail;
 
-       if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) {
+       /*
+        * Set the below properties only in one of the following cases:
+        * 1. New station is added, already associated.
+        * 2. Set WPA_STA_TDLS_PEER station.
+        * 3. Set an already added unassociated station, if driver supports
+        * full AP client state. (Set these properties after station became
+        * associated will be rejected by the driver).
+        */
+       if (!params->set || (params->flags & WPA_STA_TDLS_PEER) ||
+           (params->set && FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
+            (params->flags & WPA_STA_ASSOCIATED))) {
                wpa_hexdump(MSG_DEBUG, "  * supported rates",
                            params->supp_rates, params->supp_rates_len);
                wpa_printf(MSG_DEBUG, "  * capability=0x%x",
@@ -3848,9 +3860,12 @@ static int wpa_driver_nl80211_sta_add(void *priv,
                        /*
                         * cfg80211 validates that AID is non-zero, so we have
                         * to make this a non-zero value for the TDLS case where
-                        * a dummy STA entry is used for now.
+                        * a dummy STA entry is used for now and for a station
+                        * that is still not associated.
                         */
-                       wpa_printf(MSG_DEBUG, "  * aid=1 (TDLS workaround)");
+                       wpa_printf(MSG_DEBUG, "  * aid=1 (%s workaround)",
+                                  (params->flags & WPA_STA_TDLS_PEER) ?
+                                  "TDLS" : "UNASSOC_STA");
                        if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1))
                                goto fail;
                }
@@ -3863,6 +3878,15 @@ static int wpa_driver_nl80211_sta_add(void *priv,
                wpa_printf(MSG_DEBUG, "  * peer_aid=%u", params->aid);
                if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid))
                        goto fail;
+       } else if (FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
+                  (params->flags & WPA_STA_ASSOCIATED)) {
+               wpa_printf(MSG_DEBUG, "  * aid=%u", params->aid);
+               wpa_printf(MSG_DEBUG, "  * listen_interval=%u",
+                          params->listen_interval);
+               if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid) ||
+                   nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+                               params->listen_interval))
+                       goto fail;
        }
 
        if (params->vht_opmode_enabled) {
@@ -3893,6 +3917,36 @@ static int wpa_driver_nl80211_sta_add(void *priv,
        os_memset(&upd, 0, sizeof(upd));
        upd.set = sta_flags_nl80211(params->flags);
        upd.mask = upd.set | sta_flags_nl80211(params->flags_mask);
+
+       /*
+        * If the driver doesn't support full AP client state, ignore ASSOC/AUTH
+        * flags, as nl80211 driver moves a new station, by default, into
+        * associated state.
+        *
+        * On the other hand, if the driver supports that feature and the
+        * station is added in unauthenticated state, set the
+        * authenticated/associated bits in the mask to prevent moving this
+        * station to associated state before it is actually associated.
+        *
+        * This is irrelevant for mesh mode where the station is added to the
+        * driver as authenticated already, and ASSOCIATED isn't part of the
+        * nl80211 API.
+        */
+       if (!is_mesh_interface(drv->nlmode)) {
+               if (!FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Ignore ASSOC/AUTH flags since driver doesn't support full AP client state");
+                       upd.mask &= ~(BIT(NL80211_STA_FLAG_ASSOCIATED) |
+                                     BIT(NL80211_STA_FLAG_AUTHENTICATED));
+               } else if (!params->set &&
+                          !(params->flags & WPA_STA_TDLS_PEER)) {
+                       if (!(params->flags & WPA_STA_AUTHENTICATED))
+                               upd.mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+                       if (!(params->flags & WPA_STA_ASSOCIATED))
+                               upd.mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+               }
+       }
+
        wpa_printf(MSG_DEBUG, "  * flags set=0x%x mask=0x%x",
                   upd.set, upd.mask);
        if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
index 8c3ba49..14a93a0 100644 (file)
@@ -428,6 +428,9 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info,
 
        if (flags & NL80211_FEATURE_HT_IBSS)
                capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
+
+       if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE)
+               capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
 }