#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
* support and while this is completely overkill and silly we still
* Returns 0 if the IE has been found to be invalid in the middle
* 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,
s8 orig_max_power,
u8 **country_ie,
u8 *triplets_start = *country_ie;
u8 len_at_triplet = *country_ie_len;
int end_subband_chan = orig_end_channel;
- enum ieee80211_band band;
/*
* We'll deal with padding for the caller unless
*country_ie += 3;
*country_ie_len -= 3;
- if (orig_cur_chan <= 14)
- band = IEEE80211_BAND_2GHZ;
- else
- band = IEEE80211_BAND_5GHZ;
+ if (!chan_in_band(orig_cur_chan, band))
+ return 0;
while (*country_ie_len >= 3) {
int end_channel = 0;
struct ieee80211_country_ie_triplet *triplet =
(struct ieee80211_country_ie_triplet *) *country_ie;
int cur_channel = 0, next_expected_chan;
- enum ieee80211_band next_band = IEEE80211_BAND_2GHZ;
/* means last triplet is completely unrelated to this one */
if (triplet->ext.reg_extension_id >=
if (triplet->chans.first_channel <= end_subband_chan)
return 0;
+ if (!chan_in_band(triplet->chans.first_channel, band))
+ return 0;
+
/* 2 GHz */
if (triplet->chans.first_channel <= 14) {
end_channel = triplet->chans.first_channel +
else {
end_channel = triplet->chans.first_channel +
(4 * (triplet->chans.num_channels - 1));
- next_band = IEEE80211_BAND_5GHZ;
}
- if (band != next_band) {
- *country_ie -= 3;
- *country_ie_len += 3;
- break;
- }
+ if (!chan_in_band(end_channel, band))
+ return 0;
if (orig_max_power != triplet->chans.max_power) {
*country_ie -= 3;
* with our userspace regulatory agent to get lower bounds.
*/
static struct ieee80211_regdomain *country_ie_2_rd(
+ enum ieee80211_band band,
u8 *country_ie,
u8 country_ie_len,
u32 *checksum)
if (triplet->chans.num_channels == 0)
return NULL;
+ if (!chan_in_band(triplet->chans.first_channel, band))
+ return NULL;
+
/* 2 GHz */
- if (triplet->chans.first_channel <= 14)
+ if (band == IEEE80211_BAND_2GHZ)
end_channel = triplet->chans.first_channel +
triplet->chans.num_channels - 1;
else
* or for whatever reason sends triplets with multiple channels
* 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,
triplet->chans.max_power,
&country_ie,
if (!end_channel)
return NULL;
+ if (!chan_in_band(end_channel, band))
+ return NULL;
+
cur_sub_max_channel = end_channel;
/* Basic sanity check */
reg_rule->flags = flags;
/* 2 GHz */
- if (triplet->chans.first_channel <= 14)
+ if (band == IEEE80211_BAND_2GHZ)
end_channel = triplet->chans.first_channel +
triplet->chans.num_channels -1;
else
end_channel = triplet->chans.first_channel +
(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,
triplet->chans.max_power,
&country_ie,
* therefore cannot iterate over the rdev list here.
*/
void regulatory_hint_11d(struct wiphy *wiphy,
- u8 *country_ie,
- u8 country_ie_len)
+ enum ieee80211_band band,
+ u8 *country_ie,
+ u8 country_ie_len)
{
struct ieee80211_regdomain *rd = NULL;
char alpha2[2];
wiphy_idx_valid(last_request->wiphy_idx)))
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) {
REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n");
goto out;
* regulatory_hint_11d - hints a country IE as a regulatory domain
* @wiphy: the wireless device giving the hint (used only for reporting
* 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_len: length of the country IE
*
* 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
* 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,
+ enum ieee80211_band band,
u8 *country_ie,
u8 country_ie_len);