Commit 84920e3e authored by Luis R. Rodriguez's avatar Luis R. Rodriguez Committed by John W. Linville

cfg80211: make regulatory_hint_11d() band specific

In practice APs do not send country IE channel triplets for channels
the AP is not operating on and if they were to do so they would have
to use the regulatory extension which we currently do not process.
No AP has been seen in practice that does this though so just drop
those country IEs.

Additionally it has been noted the first series of country IE
channels triplets are specific to the band the AP sends. Propagate
the band on which the country IE was found on reject the country
IE then if the triplets are ever oustide of the band.

Although we now won't process country IE information with multiple
band information we leave the intersection work as is as it is
technically possible for someone to want to eventually process these
type of country IEs with regulatory extensions.

Cc: Jouni Malinen <jouni.malinen@atheros.com>
Cc: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarLuis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 93895757
...@@ -484,6 +484,34 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, ...@@ -484,6 +484,34 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
#undef ONE_GHZ_IN_KHZ #undef ONE_GHZ_IN_KHZ
} }
/*
* This is a work around for sanity checking ieee80211_channel_to_frequency()'s
* work. ieee80211_channel_to_frequency() can for example currently provide a
* 2 GHz channel when in fact a 5 GHz channel was desired. An example would be
* an AP providing channel 8 on a country IE triplet when it sent this on the
* 5 GHz band, that channel is designed to be channel 8 on 5 GHz, not a 2 GHz
* channel.
*
* This can be removed once ieee80211_channel_to_frequency() takes in a band.
*/
static bool chan_in_band(int chan, enum ieee80211_band band)
{
int center_freq = ieee80211_channel_to_frequency(chan);
switch (band) {
case IEEE80211_BAND_2GHZ:
if (center_freq <= 2484)
return true;
return false;
case IEEE80211_BAND_5GHZ:
if (center_freq >= 5005)
return true;
return false;
default:
return false;
}
}
/* /*
* Some APs may send a country IE triplet for each channel they * Some APs may send a country IE triplet for each channel they
* support and while this is completely overkill and silly we still * support and while this is completely overkill and silly we still
...@@ -532,7 +560,8 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, ...@@ -532,7 +560,8 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
* Returns 0 if the IE has been found to be invalid in the middle * Returns 0 if the IE has been found to be invalid in the middle
* somewhere. * somewhere.
*/ */
static int max_subband_chan(int orig_cur_chan, static int max_subband_chan(enum ieee80211_band band,
int orig_cur_chan,
int orig_end_channel, int orig_end_channel,
s8 orig_max_power, s8 orig_max_power,
u8 **country_ie, u8 **country_ie,
...@@ -541,7 +570,6 @@ static int max_subband_chan(int orig_cur_chan, ...@@ -541,7 +570,6 @@ static int max_subband_chan(int orig_cur_chan,
u8 *triplets_start = *country_ie; u8 *triplets_start = *country_ie;
u8 len_at_triplet = *country_ie_len; u8 len_at_triplet = *country_ie_len;
int end_subband_chan = orig_end_channel; int end_subband_chan = orig_end_channel;
enum ieee80211_band band;
/* /*
* We'll deal with padding for the caller unless * We'll deal with padding for the caller unless
...@@ -557,17 +585,14 @@ static int max_subband_chan(int orig_cur_chan, ...@@ -557,17 +585,14 @@ static int max_subband_chan(int orig_cur_chan,
*country_ie += 3; *country_ie += 3;
*country_ie_len -= 3; *country_ie_len -= 3;
if (orig_cur_chan <= 14) if (!chan_in_band(orig_cur_chan, band))
band = IEEE80211_BAND_2GHZ; return 0;
else
band = IEEE80211_BAND_5GHZ;
while (*country_ie_len >= 3) { while (*country_ie_len >= 3) {
int end_channel = 0; int end_channel = 0;
struct ieee80211_country_ie_triplet *triplet = struct ieee80211_country_ie_triplet *triplet =
(struct ieee80211_country_ie_triplet *) *country_ie; (struct ieee80211_country_ie_triplet *) *country_ie;
int cur_channel = 0, next_expected_chan; int cur_channel = 0, next_expected_chan;
enum ieee80211_band next_band = IEEE80211_BAND_2GHZ;
/* means last triplet is completely unrelated to this one */ /* means last triplet is completely unrelated to this one */
if (triplet->ext.reg_extension_id >= if (triplet->ext.reg_extension_id >=
...@@ -592,6 +617,9 @@ static int max_subband_chan(int orig_cur_chan, ...@@ -592,6 +617,9 @@ static int max_subband_chan(int orig_cur_chan,
if (triplet->chans.first_channel <= end_subband_chan) if (triplet->chans.first_channel <= end_subband_chan)
return 0; return 0;
if (!chan_in_band(triplet->chans.first_channel, band))
return 0;
/* 2 GHz */ /* 2 GHz */
if (triplet->chans.first_channel <= 14) { if (triplet->chans.first_channel <= 14) {
end_channel = triplet->chans.first_channel + end_channel = triplet->chans.first_channel +
...@@ -600,14 +628,10 @@ static int max_subband_chan(int orig_cur_chan, ...@@ -600,14 +628,10 @@ static int max_subband_chan(int orig_cur_chan,
else { else {
end_channel = triplet->chans.first_channel + end_channel = triplet->chans.first_channel +
(4 * (triplet->chans.num_channels - 1)); (4 * (triplet->chans.num_channels - 1));
next_band = IEEE80211_BAND_5GHZ;
} }
if (band != next_band) { if (!chan_in_band(end_channel, band))
*country_ie -= 3; return 0;
*country_ie_len += 3;
break;
}
if (orig_max_power != triplet->chans.max_power) { if (orig_max_power != triplet->chans.max_power) {
*country_ie -= 3; *country_ie -= 3;
...@@ -666,6 +690,7 @@ static int max_subband_chan(int orig_cur_chan, ...@@ -666,6 +690,7 @@ static int max_subband_chan(int orig_cur_chan,
* with our userspace regulatory agent to get lower bounds. * with our userspace regulatory agent to get lower bounds.
*/ */
static struct ieee80211_regdomain *country_ie_2_rd( static struct ieee80211_regdomain *country_ie_2_rd(
enum ieee80211_band band,
u8 *country_ie, u8 *country_ie,
u8 country_ie_len, u8 country_ie_len,
u32 *checksum) u32 *checksum)
...@@ -743,8 +768,11 @@ static struct ieee80211_regdomain *country_ie_2_rd( ...@@ -743,8 +768,11 @@ static struct ieee80211_regdomain *country_ie_2_rd(
if (triplet->chans.num_channels == 0) if (triplet->chans.num_channels == 0)
return NULL; return NULL;
if (!chan_in_band(triplet->chans.first_channel, band))
return NULL;
/* 2 GHz */ /* 2 GHz */
if (triplet->chans.first_channel <= 14) if (band == IEEE80211_BAND_2GHZ)
end_channel = triplet->chans.first_channel + end_channel = triplet->chans.first_channel +
triplet->chans.num_channels - 1; triplet->chans.num_channels - 1;
else else
...@@ -767,7 +795,8 @@ static struct ieee80211_regdomain *country_ie_2_rd( ...@@ -767,7 +795,8 @@ static struct ieee80211_regdomain *country_ie_2_rd(
* or for whatever reason sends triplets with multiple channels * or for whatever reason sends triplets with multiple channels
* separated when in fact they should be together. * separated when in fact they should be together.
*/ */
end_channel = max_subband_chan(cur_channel, end_channel = max_subband_chan(band,
cur_channel,
end_channel, end_channel,
triplet->chans.max_power, triplet->chans.max_power,
&country_ie, &country_ie,
...@@ -775,6 +804,9 @@ static struct ieee80211_regdomain *country_ie_2_rd( ...@@ -775,6 +804,9 @@ static struct ieee80211_regdomain *country_ie_2_rd(
if (!end_channel) if (!end_channel)
return NULL; return NULL;
if (!chan_in_band(end_channel, band))
return NULL;
cur_sub_max_channel = end_channel; cur_sub_max_channel = end_channel;
/* Basic sanity check */ /* Basic sanity check */
...@@ -867,14 +899,15 @@ static struct ieee80211_regdomain *country_ie_2_rd( ...@@ -867,14 +899,15 @@ static struct ieee80211_regdomain *country_ie_2_rd(
reg_rule->flags = flags; reg_rule->flags = flags;
/* 2 GHz */ /* 2 GHz */
if (triplet->chans.first_channel <= 14) if (band == IEEE80211_BAND_2GHZ)
end_channel = triplet->chans.first_channel + end_channel = triplet->chans.first_channel +
triplet->chans.num_channels -1; triplet->chans.num_channels -1;
else else
end_channel = triplet->chans.first_channel + end_channel = triplet->chans.first_channel +
(4 * (triplet->chans.num_channels - 1)); (4 * (triplet->chans.num_channels - 1));
end_channel = max_subband_chan(triplet->chans.first_channel, end_channel = max_subband_chan(band,
triplet->chans.first_channel,
end_channel, end_channel,
triplet->chans.max_power, triplet->chans.max_power,
&country_ie, &country_ie,
...@@ -1981,8 +2014,9 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy, ...@@ -1981,8 +2014,9 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy,
* therefore cannot iterate over the rdev list here. * therefore cannot iterate over the rdev list here.
*/ */
void regulatory_hint_11d(struct wiphy *wiphy, void regulatory_hint_11d(struct wiphy *wiphy,
u8 *country_ie, enum ieee80211_band band,
u8 country_ie_len) u8 *country_ie,
u8 country_ie_len)
{ {
struct ieee80211_regdomain *rd = NULL; struct ieee80211_regdomain *rd = NULL;
char alpha2[2]; char alpha2[2];
...@@ -2028,7 +2062,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, ...@@ -2028,7 +2062,7 @@ void regulatory_hint_11d(struct wiphy *wiphy,
wiphy_idx_valid(last_request->wiphy_idx))) wiphy_idx_valid(last_request->wiphy_idx)))
goto out; goto out;
rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); rd = country_ie_2_rd(band, country_ie, country_ie_len, &checksum);
if (!rd) { if (!rd) {
REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n"); REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n");
goto out; goto out;
......
...@@ -41,14 +41,25 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, ...@@ -41,14 +41,25 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
* regulatory_hint_11d - hints a country IE as a regulatory domain * regulatory_hint_11d - hints a country IE as a regulatory domain
* @wiphy: the wireless device giving the hint (used only for reporting * @wiphy: the wireless device giving the hint (used only for reporting
* conflicts) * conflicts)
* @band: the band on which the country IE was received on. This determines
* the band we'll process the country IE channel triplets for.
* @country_ie: pointer to the country IE * @country_ie: pointer to the country IE
* @country_ie_len: length of the country IE * @country_ie_len: length of the country IE
* *
* We will intersect the rd with the what CRDA tells us should apply * We will intersect the rd with the what CRDA tells us should apply
* for the alpha2 this country IE belongs to, this prevents APs from * for the alpha2 this country IE belongs to, this prevents APs from
* sending us incorrect or outdated information against a country. * sending us incorrect or outdated information against a country.
*
* The AP is expected to provide Country IE channel triplets for the
* band it is on. It is technically possible for APs to send channel
* country IE triplets even for channels outside of the band they are
* in but for that they would have to use the regulatory extension
* in combination with a triplet but this behaviour is currently
* not observed. For this reason if a triplet is seen with channel
* information for a band the BSS is not present in it will be ignored.
*/ */
void regulatory_hint_11d(struct wiphy *wiphy, void regulatory_hint_11d(struct wiphy *wiphy,
enum ieee80211_band band,
u8 *country_ie, u8 *country_ie,
u8 country_ie_len); u8 country_ie_len);
......
...@@ -454,6 +454,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, ...@@ -454,6 +454,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
* - and country_ie[1] which is the IE length * - and country_ie[1] which is the IE length
*/ */
regulatory_hint_11d(wdev->wiphy, regulatory_hint_11d(wdev->wiphy,
bss->channel->band,
country_ie + 2, country_ie + 2,
country_ie[1]); country_ie[1]);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment