2008-10-31 04:43:33

by Luis R. Rodriguez

[permalink] [raw]
Subject: [RFC] wireless: Add 802.11d support

This adds 802.11d support to mac80211 and cfg80211.

Signed-off-by: Luis R. Rodriguez <[email protected]>
---

This is not complete, I'm missing the reg.c part, but figured
I'd send it out for review to get comments now. Anyone know what the
country IE extension thing is? A quick glance at 11d gave me nothing.

include/linux/ieee80211.h | 19 +++++
include/net/wireless.h | 11 +++
net/mac80211/mlme.c | 174 +++++++++++++++++++++++++++++++++++++++++++++
net/wireless/reg.c | 7 ++
4 files changed, 211 insertions(+), 0 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index aad9919..40f5e2a 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1032,6 +1032,25 @@ enum ieee80211_spectrum_mgmt_actioncode {
WLAN_ACTION_SPCT_CHL_SWITCH = 4,
};

+/* Extension IDs >= this one indicate the Country IE
+ * has extensions */
+#define IEEE80211_COUNTRY_EXTENSION_ID 201
+
+struct ieee80211_country_ie_triplet {
+ union {
+ struct {
+ u8 first_channel;
+ u8 num_channels;
+ u8 max_power;
+ } __attribute__ ((packed)) chans;
+ struct {
+ u8 extension_id;
+ u8 class;
+ u8 coverage;
+ } __attribute__ ((packed)) ext;
+ };
+} __attribute__ ((packed));
+
/* BACK action code */
enum ieee80211_back_actioncode {
WLAN_ACTION_ADDBA_REQ = 0,
diff --git a/include/net/wireless.h b/include/net/wireless.h
index 41294c5..f148a02 100644
--- a/include/net/wireless.h
+++ b/include/net/wireless.h
@@ -357,4 +357,15 @@ ieee80211_get_channel(struct wiphy *wiphy, int freq)
* for a regulatory domain structure for the respective country.
*/
extern void regulatory_hint(struct wiphy *wiphy, const char *alpha2);
+
+/**
+ * regulatory_hint_ie - wireless stack hints a regulatory domain
+ * @wiphy: the wireless device giving the hint (used only for reporting
+ * conflicts)
+ * @rd: a regulatory domain built from the received country IE
+ *
+ * We will intersect the rd ... etc
+ */
+extern int regulatory_hint_ie(struct wiphy *wiphy,
+ struct ieee80211_regdomain *rd);
#endif /* __NET_WIRELESS_H */
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index d5006b2..388d13c 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -648,6 +648,169 @@ static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata,
wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
}

+/* Converts a country IE to a regulatory domain. A regulatory domain
+ * structure has a lot of information which the IE doesn't yet have,
+ * so for the other values we use upper max values as we will intersect
+ * with our userspace regulaotry agent to get lower bounds. This needs
+ * review for 802.11j considerations, keep in mind the first_channel
+ * cannot be >= 201 as that means its an extension */
+struct ieee80211_regdomain *ieee80211_country_ie_2_rd(
+ u8 *country_ie, u8 country_ie_len)
+{
+ struct ieee80211_regdomain *rd = NULL;
+ u32 flags = 0;
+ u32 num_rules = 0, size_of_regd = 0;
+ u8 *triplets_start = NULL;
+ u8 len_at_triplet = 0;
+ unsigned int i = 0;
+ /* Used too often */
+ int triplet_size = sizeof(struct ieee80211_country_ie_triplet);
+
+ rd->alpha2[0] = country_ie[0];
+ rd->alpha2[1] = country_ie[1];
+
+ /*
+ * Third octet can be:
+ * 'I' - Indoor
+ * 'O' - Outdoor
+ *
+ * anything else we assume is no restrictions
+ */
+ if (country_ie[2] == 'I')
+ flags = NL80211_RRF_NO_OUTDOOR;
+ else if (country_ie[2] == 'O')
+ flags = NL80211_RRF_NO_INDOOR;
+
+ country_ie += 3;
+ country_ie_len -= 3;
+
+ triplets_start = country_ie;
+ len_at_triplet = country_ie_len;
+
+ /* We need to build a reg rule for each triplet, but first we must
+ * calculate the number of reg rules we will need. We will need one
+ * for each channel subband */
+ while (country_ie_len >= triplet_size) {
+ struct ieee80211_country_ie_triplet *triplet =
+ (struct ieee80211_country_ie_triplet *) country_ie;
+
+ if (triplet->ext.extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) {
+ country_ie += triplet_size;
+ country_ie_len -= triplet_size;
+ continue;
+ }
+
+ country_ie += triplet_size;
+ country_ie_len -= triplet_size;
+ num_rules++;
+
+ if (num_rules > NL80211_MAX_SUPP_REG_RULES)
+ return NULL;
+ }
+
+ country_ie = triplets_start;
+ country_ie_len = len_at_triplet;
+
+ size_of_regd = sizeof(struct ieee80211_regdomain) +
+ (num_rules * sizeof(struct ieee80211_reg_rule));
+
+ rd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ rd->n_reg_rules = num_rules;
+
+ /* This time around we fill in the rd */
+ while (country_ie_len >= triplet_size) {
+ struct ieee80211_country_ie_triplet *triplet =
+ (struct ieee80211_country_ie_triplet *) country_ie;
+ struct ieee80211_reg_rule *reg_rule = NULL;
+ struct ieee80211_freq_range *freq_range = NULL;
+ struct ieee80211_power_rule *power_rule = NULL;
+
+ /* Not sure what these guys are yet, or how to handle them, but
+ * it means we use the 'ext' part of the union instead
+ * of 'chan' */
+ if (triplet->ext.extension_id >=
+ IEEE80211_COUNTRY_EXTENSION_ID) {
+ country_ie += triplet_size;
+ country_ie_len -= triplet_size;
+ continue;
+ }
+
+ reg_rule = &rd->reg_rules[i];
+ freq_range = &reg_rule->freq_range;
+ power_rule = &reg_rule->power_rule;
+
+ reg_rule->flags = flags;
+
+ /* The +10 is since the regulatory domain expects
+ * the actual band edge, not the center of freq for
+ * its start and end freqs */
+ freq_range->start_freq_khz =
+ MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+ triplet->chans.first_channel) + 10);
+ freq_range->end_freq_khz =
+ MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+ triplet->chans.first_channel +
+ triplet->chans.num_channels) + 10);
+
+
+#ifdef CONFIG_WIRELESS_OLD_REGULATORY
+ /* We can't expect to have anything to intersect with...
+ * so use standard low values */
+ freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
+ power_rule->max_antenna_gain = DBI_TO_MBI(6);
+ power_rule->max_eirp = DBM_TO_MBM(20);
+#else
+ /* Large arbitrary values, we intersect later */
+ /* Increment this if we ever support >= 40 MHz channels
+ * in IEEE 802.11 */
+ freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
+ power_rule->max_antenna_gain = DBI_TO_MBI(100);
+ power_rule->max_eirp = DBM_TO_MBM(100);
+#endif
+
+ country_ie += triplet_size;
+ country_ie_len -= triplet_size;
+ i++;
+
+ BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
+ }
+ return rd;
+}
+
+static int ieee80211_sta_process_country_ie(struct ieee80211_if_sta *ifsta,
+ struct ieee80211_local *local,
+ u8 *country_ie, u8 country_ie_len)
+{
+ struct ieee80211_regdomain *rd = NULL;
+ int r = 0;
+
+ if (country_ie_len < 6)
+ return -EINVAL;
+
+ rd = ieee80211_country_ie_2_rd(country_ie, country_ie_len);
+ if (!rd)
+ return -EINVAL;
+
+ r = regulatory_hint_ie(local->hw.wiphy, rd);
+
+#ifdef CONFIG_WIRELESS_OLD_REGULATORY
+ /* If the userspace regulatory agent is installed when
+ * OLD_REGULATORY is enabled then great, if not then oh well,
+ * we'll use low ball values */
+ return 0;
+#endif
+ if (!r) {
+ printk(KERN_ERR "cfg80211: calling CRDA failed - "
+ "unable to get country regulatory information "
+ "for country IE\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
@@ -1686,6 +1849,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
u32 changed = 0;
bool erp_valid;
u8 erp_value = 0;
+ int r = 0;

/* Process beacon from the current BSS */
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
@@ -1745,6 +1909,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ap_ht_cap_flags);
}

+ if (elems.country_elem) {
+ r = ieee80211_sta_process_country_ie(ifsta, local,
+ elems.country_elem, elems.country_elem_len);
+#ifdef CONFIG_MAC80211_VERBOSE_SPECT_MGMT_DEBUG
+ if (!r)
+ printk(KERN_DEBUG "mac80211: Unable to parse "
+ "country IE\n");
+#endif
+ }
+
ieee80211_bss_info_change_notify(sdata, changed);
}

diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 4c7e39d..26a11cd 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -740,6 +740,13 @@ void regulatory_hint(struct wiphy *wiphy, const char *alpha2)
}
EXPORT_SYMBOL(regulatory_hint);

+int regulatory_hint_ie(struct wiphy *wiphy, struct ieee80211_regdomain *rd)
+{
+ /* Intersect rd with cfg80211_regdomain then if that is
+ * successfull we ... */
+ return 0;
+}
+EXPORT_SYMBOL(regulatory_hint_ie);

static void print_rd_rules(const struct ieee80211_regdomain *rd)
{
--
1.5.6.3



2008-10-31 18:49:14

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC] wireless: Add 802.11d support

On Fri, Oct 31, 2008 at 3:15 AM, Johannes Berg
<[email protected]> wrote:
> On Thu, 2008-10-30 at 21:43 -0700, Luis R. Rodriguez wrote:
>
>> +struct ieee80211_regdomain *ieee80211_country_ie_2_rd(
>> + u8 *country_ie, u8 country_ie_len)
>> +{
>> + struct ieee80211_regdomain *rd = NULL;
>> + u32 flags = 0;
>> + u32 num_rules = 0, size_of_regd = 0;
>> + u8 *triplets_start = NULL;
>> + u8 len_at_triplet = 0;
>> + unsigned int i = 0;
>> + /* Used too often */
>> + int triplet_size = sizeof(struct ieee80211_country_ie_triplet);
>> +
>> + rd->alpha2[0] = country_ie[0];
>> + rd->alpha2[1] = country_ie[1];

typo, this is supposed to be alpha2[0] etc using the stack, and then
later we copy to the rd after it is kzalloc()'d.

>> + /*
>> + * Third octet can be:
>> + * 'I' - Indoor
>> + * 'O' - Outdoor
>> + *
>> + * anything else we assume is no restrictions
>> + */
>> + if (country_ie[2] == 'I')
>> + flags = NL80211_RRF_NO_OUTDOOR;
>> + else if (country_ie[2] == 'O')
>> + flags = NL80211_RRF_NO_INDOOR;
>> +
>> + country_ie += 3;
>> + country_ie_len -= 3;
>
> This needs to verify that the country IE is valid to start with, i.e. >=
> 3 bytes long.

Sure, I added a check before the call but I'll also add one here the
first few bytes we gobble up for initial parsing.

> <snipped, will review algorithm and stuff later>
>
>> +#ifdef CONFIG_WIRELESS_OLD_REGULATORY
>> + /* We can't expect to have anything to intersect with...
>> + * so use standard low values */
>> + freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
>> + power_rule->max_antenna_gain = DBI_TO_MBI(6);
>> + power_rule->max_eirp = DBM_TO_MBM(20);
>> +#else
>> + /* Large arbitrary values, we intersect later */
>> + /* Increment this if we ever support >= 40 MHz channels
>> + * in IEEE 802.11 */
>> + freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
>> + power_rule->max_antenna_gain = DBI_TO_MBI(100);
>> + power_rule->max_eirp = DBM_TO_MBM(100);
>> +#endif
>
> Why is this necessary? In case of OLD_REGULATORY we _do_ have values to
> intersect with, from the old hardcoded stuff. Please don't treat that so
> special, just treat old regulatory as though we had pre-loaded some
> regulatory information into the kernel.

Good point.

>> +#ifdef CONFIG_WIRELESS_OLD_REGULATORY
>> + /* If the userspace regulatory agent is installed when
>> + * OLD_REGULATORY is enabled then great, if not then oh well,
>> + * we'll use low ball values */
>> + return 0;
>> +#endif
>
> Same here.

Sure.

>> + if (!r) {
>> + printk(KERN_ERR "cfg80211: calling CRDA failed - "
>> + "unable to get country regulatory information "
>> + "for country IE\n");
>> + return -EINVAL;
>> + }
>
> This printk is misplaced, if it should be there at all it should be in
> cfg80211, mac80211 need not know anything about crda. Also, !r is
> incorrect, just remove the whole code block and do
>
> return regulatory_hint_ie(...);

Sure, thanks.

> Also, the printk shouldn't be there at all because otherwise it'll
> happen all the time when a country IE is received w/o crda installed,
> flooding the logs.

ACK

>> + if (elems.country_elem) {
>> + r = ieee80211_sta_process_country_ie(ifsta, local,
>> + elems.country_elem, elems.country_elem_len);
>
> Some code here or in the processing function should compare the country
> element and only process it if it changed to avoid calling crda 10 times
> per second....

Yeah I had that in mind, and will add it, I'll export a routine on
cfg80211 to check the alpha2. Keep in mind though that the country may
already be "CR" but the IE may be different than what was there if the
user was the one who last set it. I'll add an early check though.

>> +#ifdef CONFIG_MAC80211_VERBOSE_SPECT_MGMT_DEBUG
>> + if (!r)
>
> !r is wrong again

Thanks for the early review.

Luis

2008-10-31 05:23:40

by Pat Erley

[permalink] [raw]
Subject: Re: [RFC] wireless: Add 802.11d support

Luis R. Rodriguez wrote:

>
> This is not complete, I'm missing the reg.c part, but figured
> I'd send it out for review to get comments now. Anyone know what the
> country IE extension thing is? A quick glance at 11d gave me nothing.

http://www.winncom.com/pdf/802.11d-2001.pdf

Section 7.3.2.12

Might answer your question. I had the document open while reading up on
this stuff.


2008-10-31 10:07:42

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] wireless: Add 802.11d support

On Fri, 2008-10-31 at 01:16 -0400, pat-lkml wrote:
> Luis R. Rodriguez wrote:
>
> >
> > This is not complete, I'm missing the reg.c part, but figured
> > I'd send it out for review to get comments now. Anyone know what the
> > country IE extension thing is? A quick glance at 11d gave me nothing.
>
> http://www.winncom.com/pdf/802.11d-2001.pdf
>
> Section 7.3.2.12

Was something reordered? In 802.11-2007, 7.3.2.12 is "Request
information element".

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2008-10-31 06:10:41

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC] wireless: Add 802.11d support

On Thu, Oct 30, 2008 at 10:16 PM, pat-lkml <[email protected]> wrote:
> Luis R. Rodriguez wrote:
>
>>
>> This is not complete, I'm missing the reg.c part, but figured
>> I'd send it out for review to get comments now. Anyone know what the
>> country IE extension thing is? A quick glance at 11d gave me nothing.
>
> http://www.winncom.com/pdf/802.11d-2001.pdf
>
> Section 7.3.2.12
>
> Might answer your question. I had the document open while reading up on
> this stuff.

Yeah that's the section I read most of the day, it doesn't mention it at all.

Luis

2008-10-31 10:15:47

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC] wireless: Add 802.11d support

On Thu, 2008-10-30 at 21:43 -0700, Luis R. Rodriguez wrote:

> +struct ieee80211_regdomain *ieee80211_country_ie_2_rd(
> + u8 *country_ie, u8 country_ie_len)
> +{
> + struct ieee80211_regdomain *rd = NULL;
> + u32 flags = 0;
> + u32 num_rules = 0, size_of_regd = 0;
> + u8 *triplets_start = NULL;
> + u8 len_at_triplet = 0;
> + unsigned int i = 0;
> + /* Used too often */
> + int triplet_size = sizeof(struct ieee80211_country_ie_triplet);
> +
> + rd->alpha2[0] = country_ie[0];
> + rd->alpha2[1] = country_ie[1];
> +
> + /*
> + * Third octet can be:
> + * 'I' - Indoor
> + * 'O' - Outdoor
> + *
> + * anything else we assume is no restrictions
> + */
> + if (country_ie[2] == 'I')
> + flags = NL80211_RRF_NO_OUTDOOR;
> + else if (country_ie[2] == 'O')
> + flags = NL80211_RRF_NO_INDOOR;
> +
> + country_ie += 3;
> + country_ie_len -= 3;

This needs to verify that the country IE is valid to start with, i.e. >=
3 bytes long.

<snipped, will review algorithm and stuff later>

> +#ifdef CONFIG_WIRELESS_OLD_REGULATORY
> + /* We can't expect to have anything to intersect with...
> + * so use standard low values */
> + freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
> + power_rule->max_antenna_gain = DBI_TO_MBI(6);
> + power_rule->max_eirp = DBM_TO_MBM(20);
> +#else
> + /* Large arbitrary values, we intersect later */
> + /* Increment this if we ever support >= 40 MHz channels
> + * in IEEE 802.11 */
> + freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
> + power_rule->max_antenna_gain = DBI_TO_MBI(100);
> + power_rule->max_eirp = DBM_TO_MBM(100);
> +#endif

Why is this necessary? In case of OLD_REGULATORY we _do_ have values to
intersect with, from the old hardcoded stuff. Please don't treat that so
special, just treat old regulatory as though we had pre-loaded some
regulatory information into the kernel.


> +#ifdef CONFIG_WIRELESS_OLD_REGULATORY
> + /* If the userspace regulatory agent is installed when
> + * OLD_REGULATORY is enabled then great, if not then oh well,
> + * we'll use low ball values */
> + return 0;
> +#endif

Same here.

> + if (!r) {
> + printk(KERN_ERR "cfg80211: calling CRDA failed - "
> + "unable to get country regulatory information "
> + "for country IE\n");
> + return -EINVAL;
> + }

This printk is misplaced, if it should be there at all it should be in
cfg80211, mac80211 need not know anything about crda. Also, !r is
incorrect, just remove the whole code block and do

return regulatory_hint_ie(...);

Also, the printk shouldn't be there at all because otherwise it'll
happen all the time when a country IE is received w/o crda installed,
flooding the logs.

> + if (elems.country_elem) {
> + r = ieee80211_sta_process_country_ie(ifsta, local,
> + elems.country_elem, elems.country_elem_len);

Some code here or in the processing function should compare the country
element and only process it if it changed to avoid calling crda 10 times
per second....

> +#ifdef CONFIG_MAC80211_VERBOSE_SPECT_MGMT_DEBUG
> + if (!r)

!r is wrong again

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part