[PATCH] wpa_supplicant: Prefer 5 GHz networks over 2.4 GHz networks.

Dan Williams dcbw at redhat.com
Mon Aug 15 18:41:46 EDT 2011


On Fri, 2011-08-05 at 16:23 -0700, Gary Morain wrote:
> In scan.c, merge a channel's noise value into the scan results.  When comparing
> scan results, compute the signal-to-noise ratio and use it when available.
> Prefer a 5 GHz network if its SNR is really big (> 30) or if its SNR is
> relatively close to the other network's.

Honestly I'd rather have something like a "bands" option on a
per-netblock basis, so you could do this:

bands=5
bands=5,2
bands=2,5
bands=2

ie, the connection manager can set whichever band it prefers, or leave
it out entirely to let the supplicant do whatever it's already doing.
The problem with micro-optimizations like this are that they often
break, and they aren't necessarily clear.  I'd advocate for simpler,
clearer rules here, with behavior set by the connection manager, rather
than continually more complicated matching rules in the supplicant
itself...

Dan

> ---
>  src/drivers/driver.h              |   15 +++++++
>  src/drivers/driver_nl80211.c      |   76 +++++++++++++++++++++++++++++++++++++
>  wpa_supplicant/driver_i.h         |    8 ++++
>  wpa_supplicant/scan.c             |   30 ++++++++++++---
>  wpa_supplicant/wpa_supplicant_i.h |   10 +++++
>  5 files changed, 133 insertions(+), 6 deletions(-)
> 
> diff --git a/src/drivers/driver.h b/src/drivers/driver.h
> index e2d0f8b..1c118fb 100644
> --- a/src/drivers/driver.h
> +++ b/src/drivers/driver.h
> @@ -1250,6 +1250,21 @@ struct wpa_driver_ops {
>  	 struct wpa_scan_results * (*get_scan_results2)(void *priv);
>  
>  	/**
> +	 * get_noise_for_scan_results - Sets the 'noise' field of struct
> +	 * wpa_scan_res
> +	 * @priv: private driver interface data
> +	 * @scan_results: Can results obtained through get_scan_results2.
> +	 *
> +	 * This function gets the survey results from the driver.  For each
> +	 * frequency in the scan results, the noise is updated if the survey
> +	 * results have a noise value for that frequency.
> +	 *
> +	 * Returns: 0 on success. -1 on failure.
> +	 */
> +	int (*get_noise_for_scan_results)(void *priv,
> +					  struct wpa_scan_results *scan_results);
> +
> +	/**
>  	 * set_country - Set country
>  	 * @priv: Private driver interface data
>  	 * @alpha2: country to which to switch to
> diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
> index 1cb2196..2501685 100644
> --- a/src/drivers/driver_nl80211.c
> +++ b/src/drivers/driver_nl80211.c
> @@ -1278,6 +1278,81 @@ static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
>  }
>  
> 
> +static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
> +{
> +	struct nlattr *tb[NL80211_ATTR_MAX + 1];
> +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
> +	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
> +	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
> +		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
> +		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
> +	};
> +	struct wpa_scan_results *scan_results = arg;
> +	struct wpa_scan_res *scan_res;
> +	size_t i;
> +
> +	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
> +		  genlmsg_attrlen(gnlh, 0), NULL);
> +
> +	if (!tb[NL80211_ATTR_SURVEY_INFO]) {
> +		wpa_printf(MSG_DEBUG, "nl80211: survey data missing!");
> +		return NL_SKIP;
> +	}
> +
> +	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
> +			     tb[NL80211_ATTR_SURVEY_INFO],
> +			     survey_policy)) {
> +		wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested "
> +			   "attributes!");
> +		return NL_SKIP;
> +	}
> +
> +	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
> +		return NL_SKIP;
> +
> +	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
> +		return NL_SKIP;
> +
> +	for (i = 0; i < scan_results->num; ++i) {
> +		scan_res = scan_results->res[i];
> +		if (!scan_res)
> +			continue;
> +		if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
> +		    scan_res->freq)
> +			continue;
> +		if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
> +			continue;
> +		scan_res->noise = (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
> +		scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
> +	}
> +
> +	return NL_SKIP;
> +}
> +
> +
> +static int nl80211_get_noise_for_scan_results(void *priv,
> +					      struct wpa_scan_results *scan_res)
> +{
> +	struct i802_bss *bss = priv;
> +	struct wpa_driver_nl80211_data *drv = bss->drv;
> +	struct nl_msg *msg;
> +
> +	msg = nlmsg_alloc();
> +	if (!msg)
> +		return -ENOMEM;
> +
> +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
> +		    NLM_F_DUMP, NL80211_CMD_GET_SURVEY, 0);
> +
> +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
> +
> +	return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
> +				  scan_res);
> + nla_put_failure:
> +	return -ENOBUFS;
> +}
> +
> +
>  static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
>  			      struct nlattr *tb[])
>  {
> @@ -6846,6 +6921,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
>  	.set_key = wpa_driver_nl80211_set_key,
>  	.scan2 = wpa_driver_nl80211_scan,
>  	.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
> +	.get_noise_for_scan_results = nl80211_get_noise_for_scan_results,
>  	.deauthenticate = wpa_driver_nl80211_deauthenticate,
>  	.disassociate = wpa_driver_nl80211_disassociate,
>  	.authenticate = wpa_driver_nl80211_authenticate,
> diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
> index 79fdddd..b72d23f 100644
> --- a/wpa_supplicant/driver_i.h
> +++ b/wpa_supplicant/driver_i.h
> @@ -87,6 +87,14 @@ static inline struct wpa_scan_results * wpa_drv_get_scan_results2(
>  	return NULL;
>  }
>  
> +static inline void wpa_drv_get_noise_for_scan_results(
> +	struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_results)
> +{
> +	if (wpa_s->driver->get_noise_for_scan_results)
> +		wpa_s->driver->get_noise_for_scan_results(wpa_s->drv_priv,
> +							  scan_results);
> +}
> +
>  static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid)
>  {
>  	if (wpa_s->driver->get_bssid) {
> diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
> index 7689ff6..c6340ea 100644
> --- a/wpa_supplicant/scan.c
> +++ b/wpa_supplicant/scan.c
> @@ -686,11 +686,15 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
>   * better. */
>  static int wpa_scan_result_compar(const void *a, const void *b)
>  {
> +#define IS_5GHZ(n) (n > 4000)
> +#define MIN(a,b) a < b ? a : b
> +
>  	struct wpa_scan_res **_wa = (void *) a;
>  	struct wpa_scan_res **_wb = (void *) b;
>  	struct wpa_scan_res *wa = *_wa;
>  	struct wpa_scan_res *wb = *_wb;
>  	int wpa_a, wpa_b, maxrate_a, maxrate_b;
> +	int snr_a, snr_b;
>  
>  	/* WPA/WPA2 support preferred */
>  	wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
> @@ -711,23 +715,36 @@ static int wpa_scan_result_compar(const void *a, const void *b)
>  	    (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
>  		return -1;
>  
> -	/* best/max rate preferred if signal level close enough XXX */
> -	if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
> +	snr_a = MIN(wa->level - wa->noise, GREAT_SNR);
> +	snr_b = MIN(wb->level - wb->noise, GREAT_SNR);
> +
> +        wpa_printf(MSG_DEBUG, "Channel a: freq:%d level:%d noise:%d snr:%d",
> +		   wa->freq, wa->level, wa->noise, snr_a);
> +        wpa_printf(MSG_DEBUG, "Channel b: freq:%d level:%d noise:%d snr:%d",
> +		   wb->freq, wb->level, wb->noise, snr_b);
> +
> +
> +	/* best/max rate preferred if SNR close enough XXX */
> +        if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
>  	    (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
>  		maxrate_a = wpa_scan_get_max_rate(wa);
>  		maxrate_b = wpa_scan_get_max_rate(wb);
>  		if (maxrate_a != maxrate_b)
>  			return maxrate_b - maxrate_a;
> +		if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
> +			return IS_5GHZ(wa->freq) ? -1 : 1;
>  	}
>  
>  	/* use freq for channel preference */
>  
> -	/* all things being equal, use signal level; if signal levels are
> +	/* all things being equal, use SNR; if SNRs are
>  	 * identical, use quality values since some drivers may only report
>  	 * that value and leave the signal level zero */
> -	if (wb->level == wa->level)
> +	if (snr_b == snr_a)
>  		return wb->qual - wa->qual;
> -	return wb->level - wa->level;
> +	return snr_b - snr_a;
> +#undef MIN
> +#undef IS_5GHZ
>  }
>  
> 
> @@ -778,7 +795,6 @@ static int wpa_scan_result_wps_compar(const void *a, const void *b)
>  }
>  #endif /* CONFIG_WPS */
>  
> -
>  /**
>   * wpa_supplicant_get_scan_results - Get scan results
>   * @wpa_s: Pointer to wpa_supplicant data
> @@ -807,6 +823,8 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
>  		return NULL;
>  	}
>  
> +	wpa_drv_get_noise_for_scan_results(wpa_s, scan_res);
> +
>  #ifdef CONFIG_WPS
>  	if (wpas_wps_in_progress(wpa_s)) {
>  		wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
> diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
> index c1a8808..ef5760f 100644
> --- a/wpa_supplicant/wpa_supplicant_i.h
> +++ b/wpa_supplicant/wpa_supplicant_i.h
> @@ -29,6 +29,16 @@ extern const char *wpa_supplicant_full_license4;
>  extern const char *wpa_supplicant_full_license5;
>  #endif /* CONFIG_NO_STDOUT_DEBUG */
>  
> +/*
> + * Channels with a great SNR can operate at full rate.  What is a great SNR?
> + * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general
> + * rule of thumb is that any SNR above 20 is good."  This one
> + * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23
> + * recommends 25 as a minimum SNR for 54 Mbps data rate.  30 is chosen here as a
> + * conservative value.
> + */
> +#define GREAT_SNR 30
> +
>  struct wpa_sm;
>  struct wpa_supplicant;
>  struct ibss_rsn;




More information about the HostAP mailing list