[PATCH] Disable high bitrates for WPA negotiations

Christopher Wiley wiley at chromium.org
Thu Jan 17 14:56:12 EST 2013


Temporarily disable "high" bitrates during association with a BSS.

Users of wpa_supplicant can set disable_high_bitrates=1 in their
interface config, which causes the driver to disable high bitrates on
the interface during associations.  The user can then call over DBus via
EnableHighBitrates() to re-enable high bitrates at their discretion.

This is intended to facilitate staying at low bitrates all the way
through DHCP negotiations and other one time initial connection setup
steps.  Some setup processes, like WPA negotiation, can time out before
the system rate control algorithm has a chance to wind down to sane
rates.

Signed-hostap: Christoper Wiley <wiley at chromium.org>
---
 doc/dbus.doxygen                        |   14 +++
 src/drivers/driver.h                    |   19 ++++
 src/drivers/driver_nl80211.c            |  158 ++++++++++++++++++++++++++-----
 wpa_supplicant/config.c                 |    1 +
 wpa_supplicant/config.h                 |   10 ++
 wpa_supplicant/dbus/dbus_new.c          |   10 ++
 wpa_supplicant/dbus/dbus_new_handlers.c |   63 ++++++++++++
 wpa_supplicant/dbus/dbus_new_handlers.h |   11 ++
 wpa_supplicant/driver_i.h               |    8 ++
 wpa_supplicant/sme.c                    |    1 +
 wpa_supplicant/wpa_supplicant.c         |   16 +++
 11 files changed, 286 insertions(+), 25 deletions(-)

diff --git a/doc/dbus.doxygen b/doc/dbus.doxygen
index a737ed7..17f686f 100644
--- a/doc/dbus.doxygen
+++ b/doc/dbus.doxygen
@@ -332,6 +332,15 @@ fi.w1.wpa_supplicant1.CreateInterface.
 	  <dd>Invalid entries were found in the passed argument.</dd>
 	</dl>
        </li>
+       <li>
+	<h3>EnableHighBitrates ( ) --> nothing</h3>
+	<p>Enable high bitrates on this interface if currently disabled.</p>
+	<h4>Possible errors</h4>
+	<dl>
+	  <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+	  <dd>Enabling high bitrates failed for an unknown reason.</dd>
+	</dl>
+      </li>
     </ul>
 
 \subsection dbus_interface_properties Properties
@@ -431,6 +440,11 @@ fi.w1.wpa_supplicant1.CreateInterface.
 	<h3>ScanInterval - i - (read/write)</h3>
 	<p>Time (in seconds) between scans for a suitable AP. Must be >= 0.</p>
       </li>
+
+      <li>
+	<h3>DisableHighBitrates- b - (read/write)</h3>
+	<p>Identical to disable_high_bitrates entry in %wpa_supplicant configuration file.</p>
+      </li>
     </ul>
 
 \subsection dbus_interface_signals Signals
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index b981172..94cef9f 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -559,6 +559,13 @@ struct wpa_driver_associate_params {
 	 */
 	const u8 *htcaps;       /* struct ieee80211_ht_capabilities * */
 	const u8 *htcaps_mask;  /* struct ieee80211_ht_capabilities * */
+
+	/**
+	 * disable_high_bitrates - Disable high bitrates on an interface when
+	 * associating/associated with this SSID.  (e.g. legacy rates over
+	 * 11Mbps and all but the lowest two MCS rates)
+	 */
+	int disable_high_bitrates;
 };
 
 enum hide_ssid {
@@ -2612,6 +2619,18 @@ struct wpa_driver_ops {
 	 * avoid frequency conflict in single channel concurrency.
 	 */
 	int (*switch_channel)(void *priv, unsigned int freq);
+
+	/**
+	 * enable_high_bitrates - Request driver to enable high bitrates.
+	 * @priv: private driver interface data.
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This may be overwritten by future associations to the same SSID,
+	 * each of which will disable high bitrates again if
+	 * disable_high_bitrates is set in the association parameters.
+	 */
+	int (*enable_high_bitrates)(void *priv);
 };
 
 
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 6d2ed28..bfe419a 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -339,6 +339,8 @@ static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
 static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
 static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
 				     int ifindex, int disabled);
+static int nl80211_disable_high_bitrates(struct wpa_driver_nl80211_data *drv,
+					 int ifindex, int disabled);
 
 static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
 static int wpa_driver_nl80211_authenticate_retry(
@@ -6971,9 +6973,21 @@ skip_auth_type:
 			   "(%s)", ret, strerror(-ret));
 		goto nla_put_failure;
 	}
-	ret = 0;
 	wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully");
 
+	ret = nl80211_disable_high_bitrates(drv, drv->ifindex,
+					    params->disable_high_bitrates);
+	if (ret) {
+		/*
+		 * Not all drivers with an nl80211 interface support this API.
+		 * Don't kill those association attempts because we've failed
+		 * to provide this functionality.
+		 */
+		wpa_printf(MSG_ERROR, "nl80211: Failed to disable high "
+			   "bitrates");
+	}
+	ret = 0;
+
 nla_put_failure:
 	nlmsg_free(msg);
 	return ret;
@@ -7158,10 +7172,22 @@ static int wpa_driver_nl80211_associate(
 		nl80211_dump_scan(drv);
 		goto nla_put_failure;
 	}
-	ret = 0;
 	wpa_printf(MSG_DEBUG, "nl80211: Association request send "
 		   "successfully");
 
+	ret = nl80211_disable_high_bitrates(drv, drv->ifindex,
+					    params->disable_high_bitrates);
+	if (ret) {
+		/*
+		 * Not all drivers with an nl80211 interface support this API.
+		 * Don't kill those association attempts because we've failed
+		 * to provide this functionality.
+		 */
+		wpa_printf(MSG_ERROR, "nl80211: Failed to disable high "
+			   "bitrates");
+	}
+	ret = 0;
+
 nla_put_failure:
 	nlmsg_free(msg);
 	return ret;
@@ -8533,13 +8559,25 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
 	return -1;
 }
 
-
-static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
-				     int ifindex, int disabled)
-{
-	struct nl_msg *msg;
+/**
+ * Enable or disable rates considered for use by the driver.  Passing NULL for
+ * a set of allowed bands enables all bitrates in that band.  This function
+ * completely overwrites previous calls, rather than adding or removing rates.
+ */
+static int nl80211_set_allowed_rates(struct wpa_driver_nl80211_data *drv,
+				     int ifindex,
+				     const char *legacy_2ghz_rates,
+				     int legacy_2ghz_rates_len,
+				     const char *legacy_5ghz_rates,
+				     int legacy_5ghz_rates_len,
+				     const char *mcs_2ghz_rates,
+				     int mcs_2ghz_rates_len,
+				     const char *mcs_5ghz_rates,
+				     int mcs_5ghz_rates_len)
+{
+	struct nl_msg *msg = NULL;
 	struct nlattr *bands, *band;
-	int ret;
+	int ret = -1;
 
 	msg = nlmsg_alloc();
 	if (!msg)
@@ -8552,19 +8590,41 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
 	if (!bands)
 		goto nla_put_failure;
 
-	/*
-	 * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
-	 * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
-	 * rates. All 5 GHz rates are left enabled.
-	 */
-	band = nla_nest_start(msg, NL80211_BAND_2GHZ);
-	if (!band)
-		goto nla_put_failure;
-	if (disabled) {
-		NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
-			"\x0c\x12\x18\x24\x30\x48\x60\x6c");
+	if (legacy_2ghz_rates && legacy_2ghz_rates_len >= 0) {
+		band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+		if (!band)
+			goto nla_put_failure;
+		NLA_PUT(msg, NL80211_TXRATE_LEGACY,
+			legacy_2ghz_rates_len, legacy_2ghz_rates);
+		nla_nest_end(msg, band);
+	}
+
+	if (legacy_5ghz_rates && legacy_5ghz_rates_len >= 0) {
+		band = nla_nest_start(msg, NL80211_BAND_5GHZ);
+		if (!band)
+			goto nla_put_failure;
+		NLA_PUT(msg, NL80211_TXRATE_LEGACY,
+			legacy_5ghz_rates_len, legacy_5ghz_rates);
+		nla_nest_end(msg, band);
+	}
+
+	if (mcs_2ghz_rates && mcs_2ghz_rates_len >= 0) {
+		band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+		if (!band)
+			goto nla_put_failure;
+		NLA_PUT(msg, NL80211_TXRATE_MCS,
+			mcs_2ghz_rates_len, mcs_2ghz_rates);
+		nla_nest_end(msg, band);
+	}
+
+	if (mcs_5ghz_rates && mcs_5ghz_rates_len >= 0) {
+		band = nla_nest_start(msg, NL80211_BAND_5GHZ);
+		if (!band)
+			goto nla_put_failure;
+		NLA_PUT(msg, NL80211_TXRATE_MCS,
+			mcs_5ghz_rates_len, mcs_5ghz_rates);
+		nla_nest_end(msg, band);
 	}
-	nla_nest_end(msg, band);
 
 	nla_nest_end(msg, bands);
 
@@ -8573,14 +8633,53 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
-	} else
-		drv->disabled_11b_rates = disabled;
-
-	return ret;
+	}
 
 nla_put_failure:
 	nlmsg_free(msg);
-	return -1;
+	return ret;
+}
+
+
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+				     int ifindex, int disabled)
+{
+	/*
+	 * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+	 * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+	 * rates. All 5 GHz rates are left enabled.
+	 */
+	if (disabled) {
+		return nl80211_set_allowed_rates(drv, ifindex,
+						 "\x0c\x12\x18\x24\x30\x48\x60\x6c", 8,
+						 NULL, 0,
+						 NULL, 0,
+						 NULL, 0);
+	}
+	return nl80211_set_allowed_rates(drv, ifindex, NULL, 0, NULL, 0,
+					 NULL, 0, NULL, 0);
+}
+
+
+static int nl80211_disable_high_bitrates(struct wpa_driver_nl80211_data *drv,
+				     int ifindex, int disabled)
+{
+	/*
+	 * Allow only legacy bitrates less than 12 Mbps and the two slowest MCS
+	 * rates for outgoing traffic.
+	 */
+	if (disabled) {
+		wpa_printf(MSG_DEBUG, "nl80211: Disabling high bitrates");
+		return nl80211_set_allowed_rates(drv, ifindex,
+						 "\x02\x04\x0b\x0c\x16", 5,
+						 "\x02\x04\x0b\x0c\x16", 5,
+						 "\x00\x01", 2,
+						 "\x00\x01", 2);
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Enabling high bitrates");
+	return nl80211_set_allowed_rates(drv, ifindex, NULL, 0, NULL, 0,
+					 NULL, 0, NULL, 0);
 }
 
 
@@ -9016,6 +9115,14 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
 }
 
 
+static int nl80211_enable_high_bitrates(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	return nl80211_disable_high_bitrates(drv, drv->ifindex, 0);
+}
+
+
 static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
 {
 	struct nl_msg *msg;
@@ -9352,4 +9459,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.send_tdls_mgmt = nl80211_send_tdls_mgmt,
 	.tdls_oper = nl80211_tdls_oper,
 #endif /* CONFIG_TDLS */
+	.enable_high_bitrates = nl80211_enable_high_bitrates,
 };
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 2c52c68..8db50c4 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2990,6 +2990,7 @@ static const struct global_parse_data global_fields[] = {
 	{ INT(okc), 0 },
 	{ INT(pmf), 0 },
 	{ FUNC(sae_groups), 0 },
+	{ INT_RANGE(disable_high_bitrates, 0, 1), 0 },
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 0c3cb9a..ef5c158 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -807,6 +807,16 @@ struct wpa_config {
 	 * groups will be tried in the indicated order.
 	 */
 	int *sae_groups;
+
+	/**
+	 * disable_high_bitrates - Disable high bitrates on new associations
+	 *
+	 * By default, we use all available bitrates negotiated with an AP
+	 * during association.  However, this flag can be set to disable the use
+	 * of high bitrates for frame transmission (if supported by the driver).
+	 * The user can later re-enable high bitrates via EnableHighBitrates().
+	 */
+	int disable_high_bitrates;
 };
 
 
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 8bc6618..ebe4d9d 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -2649,6 +2649,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
 	  }
 	},
 #endif /* CONFIG_AP */
+	{ "EnableHighBitrates", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) &wpas_dbus_handler_enable_high_bitrates,
+	  {
+		  END_ARGS
+	  }
+	},
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
 
@@ -2725,6 +2731,10 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
 	  wpas_dbus_getter_scan_interval,
 	  wpas_dbus_setter_scan_interval
 	},
+	{ "DisableHighBitrates", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
+	  wpas_dbus_getter_disable_high_bitrates,
+	  wpas_dbus_setter_disable_high_bitrates
+	},
 #ifdef CONFIG_WPS
 	{ "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
 	  wpas_dbus_getter_process_credentials,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 5e06932..eaf2737 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -1920,6 +1920,19 @@ DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
 #endif /* CONFIG_AUTOSCAN */
 
 
+DBusMessage * wpas_dbus_handler_enable_high_bitrates(DBusMessage *message,
+	struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply = NULL;
+	if (wpa_supplicant_enable_high_bitrates(wpa_s)) {
+		reply = wpas_dbus_error_unknown_error(message,
+						      "Failed to enable high "
+						      "rates.");
+	}
+	return reply;
+}
+
+
 /**
  * wpas_dbus_getter_capabilities - Return interface capabilities
  * @iter: Pointer to incoming dbus message iter
@@ -2659,6 +2672,56 @@ dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
 
 
 /**
+ * wpas_dbus_getter_disable_high_bitrates - Disable high bitrates for
+ * each association on this interface.
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "DisableHighBitrates" property.
+ */
+dbus_bool_t wpas_dbus_getter_disable_high_bitrates(DBusMessageIter *iter,
+						   DBusError *error,
+						   void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_bool_t disable_high_bitrates = FALSE;
+	if (wpa_s->conf->disable_high_bitrates)
+		disable_high_bitrates = TRUE;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+						&disable_high_bitrates, error);
+}
+
+
+/**
+ * wpas_dbus_setter_disable_high_bitrates - Disable high bitrates for
+ * each association on this interface.
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "DisableHighBitrates" property.
+ */
+dbus_bool_t wpas_dbus_setter_disable_high_bitrates(DBusMessageIter *iter,
+						   DBusError *error,
+						   void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_bool_t disable_high_bitrates;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+					      &disable_high_bitrates))
+		return FALSE;
+
+	wpa_s->conf->disable_high_bitrates = disable_high_bitrates;
+	return TRUE;
+}
+
+
+/**
  * wpas_dbus_getter_ifname - Get interface name
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index aa56550..aa8f6fa 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -128,6 +128,9 @@ DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
 DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
 					 struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_enable_high_bitrates(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
 dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
 					  DBusError *error, void *user_data);
 
@@ -184,6 +187,14 @@ dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
 					   DBusError *error,
 					   void *user_data);
 
+dbus_bool_t wpas_dbus_getter_disable_high_bitrates(DBusMessageIter *iter,
+						   DBusError *error,
+						   void *user_data);
+
+dbus_bool_t wpas_dbus_setter_disable_high_bitrates(DBusMessageIter *iter,
+						   DBusError *error,
+						   void *user_data);
+
 dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
 				    void *user_data);
 
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 847600d..88b2c56 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -689,4 +689,12 @@ static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
 				       buf_len);
 }
 
+static inline int wpa_drv_enable_high_bitrates(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->enable_high_bitrates) {
+		return wpa_s->driver->enable_high_bitrates(wpa_s->drv_priv);
+	}
+	return -1;
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 30f9779..a8a2d02 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -653,6 +653,7 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 	params.htcaps_mask = (u8 *) &htcaps_mask;
 	wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, &params);
 #endif /* CONFIG_HT_OVERRIDES */
+	params.disable_high_bitrates = wpa_s->conf->disable_high_bitrates;
 #ifdef CONFIG_IEEE80211R
 	if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
 		params.wpa_ie = wpa_s->sme.ft_ies;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 64f5c1b..295802f 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1581,6 +1581,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 	wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_HT_OVERRIDES */
 
+	params.disable_high_bitrates = wpa_s->conf->disable_high_bitrates;
+
 	ret = wpa_drv_associate(wpa_s, &params);
 	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -1883,6 +1885,20 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
 
 
 /**
+ * wpa_supplicant_enable_high_bitrates - Enable high bitrates on an interface.
+ * @wpa_s: wpa_supplicant structure for a network interface
+ *
+ * Enables high bitrates on the interface until the next association.  If
+ * disable_high_bitrates is set on the next association, high rates will be
+ * disabled again.
+ */
+int wpa_supplicant_enable_high_bitrates(struct wpa_supplicant *wpa_s)
+{
+	return wpa_drv_enable_high_bitrates(wpa_s);
+}
+
+
+/**
  * wpa_supplicant_set_ap_scan - Set AP scan mode for interface
  * @wpa_s: wpa_supplicant structure for a network interface
  * @ap_scan: AP scan mode
-- 
1.7.7.3



More information about the HostAP mailing list