diff mbox

Disable high bitrates for WPA negotiations

Message ID 1358452572-7353-1-git-send-email-wiley@chromium.org
State Rejected
Headers show

Commit Message

Christopher Wiley Jan. 17, 2013, 7:56 p.m. UTC
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@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(-)

Comments

Ben Greear Jan. 17, 2013, 8:13 p.m. UTC | #1
On 01/17/2013 11:56 AM, Christopher Wiley wrote:
> 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.

Maybe add a CLI command or similar to re-enable the high
rates for those not interested in using dbus?

Also, it looks like this steps on any previous available-rates
selection logic in supplicant?  It would be nice if this
code took the configured rate settings into account,
having the logic in this patch never enable rates
previously disabled.

Thanks,
Ben
Ben Greear Jan. 18, 2013, 6:05 p.m. UTC | #2
On 01/18/2013 09:43 AM, Christopher Wiley wrote:
> On Thu, Jan 17, 2013 at 12:13 PM, Ben Greear <greearb@candelatech.com <mailto:greearb@candelatech.com>> wrote:
>
>     On 01/17/2013 11:56 AM, Christopher Wiley wrote:
>
>         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.
>
>
>     Maybe add a CLI command or similar to re-enable the high
>     rates for those not interested in using dbus?
>
>     Also, it looks like this steps on any previous available-rates
>     selection logic in supplicant?  It would be nice if this
>     code took the configured rate settings into account,
>     having the logic in this patch never enable rates
>     previously disabled.
>
>
>     Thanks,
>     Ben
>
>     --
>     Ben Greear <greearb@candelatech.com <mailto:greearb@candelatech.com>>
>     Candela Technologies Inc http://www.candelatech.com
>
>
> The CLI seems like a good idea.  I'll put up another CL that includes it.
>
> It is true that setting the bitrate mask ignores past masks.  What do you mean by previously disabled rates?  Are you referring to the bits disabling 802.11b rates?

It is possible to configure the available rates, including limiting the MCS rates to low values (including just MCS1).

It is also possible to disable /n rates entirely..I didn't read your code carefully enough to determine
if it conflicts with that or not.

I think your code should take the configured available rates/bands/protocols into account and never go outside those bounds,
and I don't think your previous patch quite did this.

Thanks,
Ben
Christopher Wiley Jan. 18, 2013, 8:02 p.m. UTC | #3
On Fri, Jan 18, 2013 at 10:05 AM, Ben Greear <greearb@candelatech.com>wrote:

> On 01/18/2013 09:43 AM, Christopher Wiley wrote:
>
>> On Thu, Jan 17, 2013 at 12:13 PM, Ben Greear <greearb@candelatech.com<mailto:
>> greearb@candelatech.**com <greearb@candelatech.com>>> wrote:
>>
>>     On 01/17/2013 11:56 AM, Christopher Wiley wrote:
>>
>>         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.
>>
>>
>>     Maybe add a CLI command or similar to re-enable the high
>>     rates for those not interested in using dbus?
>>
>>     Also, it looks like this steps on any previous available-rates
>>     selection logic in supplicant?  It would be nice if this
>>     code took the configured rate settings into account,
>>     having the logic in this patch never enable rates
>>     previously disabled.
>>
>>
>>     Thanks,
>>     Ben
>>
>>     --
>>     Ben Greear <greearb@candelatech.com <mailto:greearb@candelatech.**com<greearb@candelatech.com>
>> >>
>>
>>     Candela Technologies Inc http://www.candelatech.com
>>
>>
>> The CLI seems like a good idea.  I'll put up another CL that includes it.
>>
>> It is true that setting the bitrate mask ignores past masks.  What do you
>> mean by previously disabled rates?  Are you referring to the bits disabling
>> 802.11b rates?
>>
>
> It is possible to configure the available rates, including limiting the
> MCS rates to low values (including just MCS1).
>
> It is also possible to disable /n rates entirely..I didn't read your code
> carefully enough to determine
> if it conflicts with that or not.
>
> I think your code should take the configured available
> rates/bands/protocols into account and never go outside those bounds,
> and I don't think your previous patch quite did this.
>
>
> Thanks,
> Ben
>
>
> --
> Ben Greear <greearb@candelatech.com>
> Candela Technologies Inc  http://www.candelatech.com
>
>
I think that the HT_OVERRIDES logic plays pretty well with this patch.
 That logic disables rates by clearing bits in the supported MCS rates set,
while keeping the mask at a default of all enabled.  This patch disables
rates by clearing bits in the mask, while not touching the set of supported
rates.  11n is disabled completely by clearing some bits in a capabilities
field, rather than clearing the mask.  Have I missed anything?

On the other hand, it's true that the patch stomps on the 11b rate
disabling.  I'll handle that.

Christopher Wiley
Ben Greear Jan. 18, 2013, 8:07 p.m. UTC | #4
On 01/18/2013 12:02 PM, Christopher Wiley wrote:

> I think that the HT_OVERRIDES logic plays pretty well with this patch.  That logic disables rates by clearing bits in the supported MCS rates set, while keeping
> the mask at a default of all enabled.  This patch disables rates by clearing bits in the mask, while not touching the set of supported rates.  11n is disabled
> completely by clearing some bits in a capabilities field, rather than clearing the mask.  Have I missed anything?
>
> On the other hand, it's true that the patch stomps on the 11b rate disabling.  I'll handle that.

Ok, that sounds fine then.

Probably will be a while before I've time, but if this makes it upstream, I'll test it out
with the HT over-rides and such when I get a chance.

Thanks,
Ben

>
> Christopher Wiley
Felix Fietkau Jan. 21, 2013, 12:10 p.m. UTC | #5
On 2013-01-17 8:56 PM, Christopher Wiley wrote:
> 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@chromium.org>
What driver / rate control algorithm are you using this workaround for?
Wouldn't it be better to fix the rate control module instead? Having a
rate control module start with high data rates and no proper fallback
this early seems like a bug or design flaw to me that should be
addressed properly instead of worked around with a patch like this.

- Felix
Felix Fietkau Jan. 23, 2013, 8:08 p.m. UTC | #6
On 2013-01-23 8:40 PM, Christopher Wiley wrote:
> On Mon, Jan 21, 2013 at 4:10 AM, Felix Fietkau <nbd@openwrt.org
> <mailto:nbd@openwrt.org>> wrote:
> 
>     On 2013-01-17 8:56 PM, Christopher Wiley wrote:
>     > 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@chromium.org
>     <mailto:wiley@chromium.org>>
>     What driver / rate control algorithm are you using this workaround for?
>     Wouldn't it be better to fix the rate control module instead? Having a
>     rate control module start with high data rates and no proper fallback
>     this early seems like a bug or design flaw to me that should be
>     addressed properly instead of worked around with a patch like this.
> 
>     - Felix
> 
>  
> This was happening on an ath9k module using
> the ath9k_btcoex_rate_control algorithm on a 9462 part specifically.  It
> is true that this is not a real fix for the underlying problem (which
> will need to be in the driver).  This patch just converts the total
> breakage of not being able to connect at all to a brief period of poor
> connectivity after high rates are enabled as the rate control algorithm
> winds down.  However, I think it is valuable to allow distributions to
> work around rate control bugs in the various drivers they have to use on
> their respective platforms.
I'm not really convinced that it is a good idea to carry such
workarounds just in case other drivers might be broken as well.

I also think it would be a *really* bad idea for distributions to just
enable a hack like this by default, or even suggest it to users.
While it may indeed succeed in working around a particular kind of bug
in a stupid rate control module, it might as well trigger a different
kind of bug in a different module.

I strongly believe that adding complexity and bloat to codebases for
unnecessary workarounds is bad in the long run and should be avoided if
possible.

How about just changing the ath9k rate control to initialize its tables
in a way that it will start with lower rates and work its way up.
Shouldn't be too hard, the module itself is not very complex.

- Felix
Christopher Wiley Jan. 29, 2013, 5:55 p.m. UTC | #7
On Wed, Jan 23, 2013 at 12:08 PM, Felix Fietkau <nbd@openwrt.org> wrote:

> On 2013-01-23 8:40 PM, Christopher Wiley wrote:
> > On Mon, Jan 21, 2013 at 4:10 AM, Felix Fietkau <nbd@openwrt.org
> > <mailto:nbd@openwrt.org>> wrote:
> >
> >     On 2013-01-17 8:56 PM, Christopher Wiley wrote:
> >     > 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@chromium.org
> >     <mailto:wiley@chromium.org>>
> >     What driver / rate control algorithm are you using this workaround
> for?
> >     Wouldn't it be better to fix the rate control module instead? Having
> a
> >     rate control module start with high data rates and no proper fallback
> >     this early seems like a bug or design flaw to me that should be
> >     addressed properly instead of worked around with a patch like this.
> >
> >     - Felix
> >
> >
> > This was happening on an ath9k module using
> > the ath9k_btcoex_rate_control algorithm on a 9462 part specifically.  It
> > is true that this is not a real fix for the underlying problem (which
> > will need to be in the driver).  This patch just converts the total
> > breakage of not being able to connect at all to a brief period of poor
> > connectivity after high rates are enabled as the rate control algorithm
> > winds down.  However, I think it is valuable to allow distributions to
> > work around rate control bugs in the various drivers they have to use on
> > their respective platforms.
> I'm not really convinced that it is a good idea to carry such
> workarounds just in case other drivers might be broken as well.
>
> I also think it would be a *really* bad idea for distributions to just
> enable a hack like this by default, or even suggest it to users.
> While it may indeed succeed in working around a particular kind of bug
> in a stupid rate control module, it might as well trigger a different
> kind of bug in a different module.
>
> I strongly believe that adding complexity and bloat to codebases for
> unnecessary workarounds is bad in the long run and should be avoided if
> possible.
>
> How about just changing the ath9k rate control to initialize its tables
> in a way that it will start with lower rates and work its way up.
> Shouldn't be too hard, the module itself is not very complex.
>
> - Felix
>

I don't really think this is such a zero-sum game.  We're hardening
wpa_supplicant against a class of hard to reproduce errors and using an
existing kernel interface in a straightforward way.

If we're going to speculate about hypothetical bugs in other modules, we
should also speculate about bugs in other rate control modules.  This
particular sort of rate control bug is difficult because it is a hard to
reproduce corner case that snags an otherwise acceptable optimization: be
optimistic about rates and count on backoff to correct for errors.  I don't
really think its a bad idea for distributions to work around a driver bug
that has been known (even just on this list) for over a year.  That this
has gone on this long says a lot about the subtlety of this kind of bug.

I can't say I'm familiar with how rate control is done, but suppose I did
want to go in and write up a patch.  What files and functions would I start
looking at and poking to make an appropriate change?

Christopher Wiley
Jouni Malinen Feb. 9, 2013, 10:27 a.m. UTC | #8
On Thu, Jan 17, 2013 at 11:56:12AM -0800, Christopher Wiley wrote:
> 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.

I do have to agree with Felix on this one. It is difficult to see how
this would be the best approach for handling the issue. In general, I
have no problems in making sure the driver has all the information
available to it on the current state of the connection (and to large
extend, this should already be available to Linux kernel with the oper
state; DHCP could be handled separately if need be but even that could
be available based on current IP configuration).

Selecting actual TX rates (including filtering out some that may not be
expected to work for whatever reason) would fit better in the TX rate
control code or in the driver/802.11 stack (e.g., mac80211 could
potentially do this if for some reason we cannot change the rate control
algorithm easily).

I'm inclined not to apply this patch to wpa_supplicant. Instead, I would
recommend taking a look at improving the kernel side rate control
algorithms (e.g., net/mac80211/rc80211* and net/mac80211/rate.c in case
of mac80211-based drivers).
diff mbox

Patch

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