|
@@ -484,6 +484,178 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
|
|
|
#undef ONE_GHZ_IN_KHZ
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Some APs may send a country IE triplet for each channel they
|
|
|
+ * support and while this is completely overkill and silly we still
|
|
|
+ * need to support it. We avoid making a single rule for each channel
|
|
|
+ * though and to help us with this we use this helper to find the
|
|
|
+ * actual subband end channel. These type of country IE triplet
|
|
|
+ * scenerios are handled then, all yielding two regulaotry rules from
|
|
|
+ * parsing a country IE:
|
|
|
+ *
|
|
|
+ * [1]
|
|
|
+ * [2]
|
|
|
+ * [36]
|
|
|
+ * [40]
|
|
|
+ *
|
|
|
+ * [1]
|
|
|
+ * [2-4]
|
|
|
+ * [5-12]
|
|
|
+ * [36]
|
|
|
+ * [40-44]
|
|
|
+ *
|
|
|
+ * [1-4]
|
|
|
+ * [5-7]
|
|
|
+ * [36-44]
|
|
|
+ * [48-64]
|
|
|
+ *
|
|
|
+ * [36-36]
|
|
|
+ * [40-40]
|
|
|
+ * [44-44]
|
|
|
+ * [48-48]
|
|
|
+ * [52-52]
|
|
|
+ * [56-56]
|
|
|
+ * [60-60]
|
|
|
+ * [64-64]
|
|
|
+ * [100-100]
|
|
|
+ * [104-104]
|
|
|
+ * [108-108]
|
|
|
+ * [112-112]
|
|
|
+ * [116-116]
|
|
|
+ * [120-120]
|
|
|
+ * [124-124]
|
|
|
+ * [128-128]
|
|
|
+ * [132-132]
|
|
|
+ * [136-136]
|
|
|
+ * [140-140]
|
|
|
+ *
|
|
|
+ * Returns 0 if the IE has been found to be invalid in the middle
|
|
|
+ * somewhere.
|
|
|
+ */
|
|
|
+static int max_subband_chan(int orig_cur_chan,
|
|
|
+ int orig_end_channel,
|
|
|
+ s8 orig_max_power,
|
|
|
+ u8 **country_ie,
|
|
|
+ u8 *country_ie_len)
|
|
|
+{
|
|
|
+ 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
|
|
|
+ * its not immediate and we don't process any channels
|
|
|
+ */
|
|
|
+ if (*country_ie_len == 1) {
|
|
|
+ *country_ie += 1;
|
|
|
+ *country_ie_len -= 1;
|
|
|
+ return orig_end_channel;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Move to the next triplet and then start search */
|
|
|
+ *country_ie += 3;
|
|
|
+ *country_ie_len -= 3;
|
|
|
+
|
|
|
+ if (orig_cur_chan <= 14)
|
|
|
+ band = IEEE80211_BAND_2GHZ;
|
|
|
+ else
|
|
|
+ band = IEEE80211_BAND_5GHZ;
|
|
|
+
|
|
|
+ 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 >=
|
|
|
+ IEEE80211_COUNTRY_EXTENSION_ID) {
|
|
|
+ *country_ie -= 3;
|
|
|
+ *country_ie_len += 3;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (triplet->chans.first_channel == 0) {
|
|
|
+ *country_ie += 1;
|
|
|
+ *country_ie_len -= 1;
|
|
|
+ if (*country_ie_len != 0)
|
|
|
+ return 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Monitonically increasing channel order */
|
|
|
+ if (triplet->chans.first_channel <= end_subband_chan)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* 2 GHz */
|
|
|
+ if (triplet->chans.first_channel <= 14) {
|
|
|
+ end_channel = triplet->chans.first_channel +
|
|
|
+ triplet->chans.num_channels - 1;
|
|
|
+ }
|
|
|
+ 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 (orig_max_power != triplet->chans.max_power) {
|
|
|
+ *country_ie -= 3;
|
|
|
+ *country_ie_len += 3;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ cur_channel = triplet->chans.first_channel;
|
|
|
+
|
|
|
+ /* The key is finding the right next expected channel */
|
|
|
+ if (band == IEEE80211_BAND_2GHZ)
|
|
|
+ next_expected_chan = end_subband_chan + 1;
|
|
|
+ else
|
|
|
+ next_expected_chan = end_subband_chan + 4;
|
|
|
+
|
|
|
+ if (cur_channel != next_expected_chan) {
|
|
|
+ *country_ie -= 3;
|
|
|
+ *country_ie_len += 3;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ end_subband_chan = end_channel;
|
|
|
+
|
|
|
+ /* Move to the next one */
|
|
|
+ *country_ie += 3;
|
|
|
+ *country_ie_len -= 3;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Padding needs to be dealt with if we processed
|
|
|
+ * some channels.
|
|
|
+ */
|
|
|
+ if (*country_ie_len == 1) {
|
|
|
+ *country_ie += 1;
|
|
|
+ *country_ie_len -= 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If seen, the IE is invalid */
|
|
|
+ if (*country_ie_len == 2)
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (end_subband_chan == orig_end_channel) {
|
|
|
+ *country_ie = triplets_start;
|
|
|
+ *country_ie_len = len_at_triplet;
|
|
|
+ return orig_end_channel;
|
|
|
+ }
|
|
|
+
|
|
|
+ return end_subband_chan;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Converts a country IE to a regulatory domain. A regulatory domain
|
|
|
* structure has a lot of information which the IE doesn't yet have,
|
|
@@ -552,6 +724,19 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * APs can add padding to make length divisible
|
|
|
+ * by two, required by the spec.
|
|
|
+ */
|
|
|
+ if (triplet->chans.first_channel == 0) {
|
|
|
+ country_ie++;
|
|
|
+ country_ie_len--;
|
|
|
+ /* This is expected to be at the very end only */
|
|
|
+ if (country_ie_len != 0)
|
|
|
+ return NULL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
/* 2 GHz */
|
|
|
if (triplet->chans.first_channel <= 14)
|
|
|
end_channel = triplet->chans.first_channel +
|
|
@@ -570,6 +755,20 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
|
|
(4 * (triplet->chans.num_channels - 1));
|
|
|
|
|
|
cur_channel = triplet->chans.first_channel;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Enhancement for APs that send a triplet for every channel
|
|
|
+ * 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,
|
|
|
+ triplet->chans.max_power,
|
|
|
+ &country_ie,
|
|
|
+ &country_ie_len);
|
|
|
+ if (!end_channel)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
cur_sub_max_channel = end_channel;
|
|
|
|
|
|
/* Basic sanity check */
|
|
@@ -600,10 +799,13 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
|
|
|
|
|
last_sub_max_channel = cur_sub_max_channel;
|
|
|
|
|
|
- country_ie += 3;
|
|
|
- country_ie_len -= 3;
|
|
|
num_rules++;
|
|
|
|
|
|
+ if (country_ie_len >= 3) {
|
|
|
+ country_ie += 3;
|
|
|
+ country_ie_len -= 3;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* Note: this is not a IEEE requirement but
|
|
|
* simply a memory requirement
|
|
@@ -646,6 +848,12 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ if (triplet->chans.first_channel == 0) {
|
|
|
+ country_ie++;
|
|
|
+ country_ie_len--;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
reg_rule = &rd->reg_rules[i];
|
|
|
freq_range = ®_rule->freq_range;
|
|
|
power_rule = ®_rule->power_rule;
|
|
@@ -660,6 +868,12 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
|
|
end_channel = triplet->chans.first_channel +
|
|
|
(4 * (triplet->chans.num_channels - 1));
|
|
|
|
|
|
+ end_channel = max_subband_chan(triplet->chans.first_channel,
|
|
|
+ end_channel,
|
|
|
+ triplet->chans.max_power,
|
|
|
+ &country_ie,
|
|
|
+ &country_ie_len);
|
|
|
+
|
|
|
/*
|
|
|
* The +10 is since the regulatory domain expects
|
|
|
* the actual band edge, not the center of freq for
|
|
@@ -682,10 +896,13 @@ static struct ieee80211_regdomain *country_ie_2_rd(
|
|
|
power_rule->max_antenna_gain = DBI_TO_MBI(100);
|
|
|
power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power);
|
|
|
|
|
|
- country_ie += 3;
|
|
|
- country_ie_len -= 3;
|
|
|
i++;
|
|
|
|
|
|
+ if (country_ie_len >= 3) {
|
|
|
+ country_ie += 3;
|
|
|
+ country_ie_len -= 3;
|
|
|
+ }
|
|
|
+
|
|
|
BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
|
|
|
}
|
|
|
|