aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorLuis R. Rodriguez <lrodriguez@atheros.com>2010-01-07 17:24:57 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-01-12 13:57:58 -0500
commitcc5d8a3772ee4e2ed29558ba548b4747959ba971 (patch)
treee90b703642d94e4f342e3ff1deba9966e59c4916 /net
parent08030db6e5275dda19ea1b3ab8a41c992799db4a (diff)
cfg80211: Fix country IE parsing for single channel triplets
This enhances the way we parse country IEs to minimize the number of regulatory rules that we create. It also fixes our current implementation which treated country IE triplets with only one channel as one independed regulatory rule even though adjecent rules were also being provided. Without this patch APs which send country IE information with a channel triplet for each individual channel will force cfg80211 to deny HT40 operation as a regulatory rule would have been created independently for each channel and as such configured only for 20 MHz operation. Although 802.11n APs which send country IEs triplets in this fassion are likely rare Benoit reports this against the Ubiquity NanoStation M5, with Country "FR" and HT40 enabled. Since we now have a helper which parses the triplets in intermediate steps we now take care extra care to process padding. Reported-by: Benoit PAPILLAULT <benoit.papillault@free.fr> Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/wireless/reg.c225
1 files changed, 221 insertions, 4 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 77d0bb6f6e7a..a5c2d3a6cbb2 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -485,6 +485,178 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
485} 485}
486 486
487/* 487/*
488 * Some APs may send a country IE triplet for each channel they
489 * support and while this is completely overkill and silly we still
490 * need to support it. We avoid making a single rule for each channel
491 * though and to help us with this we use this helper to find the
492 * actual subband end channel. These type of country IE triplet
493 * scenerios are handled then, all yielding two regulaotry rules from
494 * parsing a country IE:
495 *
496 * [1]
497 * [2]
498 * [36]
499 * [40]
500 *
501 * [1]
502 * [2-4]
503 * [5-12]
504 * [36]
505 * [40-44]
506 *
507 * [1-4]
508 * [5-7]
509 * [36-44]
510 * [48-64]
511 *
512 * [36-36]
513 * [40-40]
514 * [44-44]
515 * [48-48]
516 * [52-52]
517 * [56-56]
518 * [60-60]
519 * [64-64]
520 * [100-100]
521 * [104-104]
522 * [108-108]
523 * [112-112]
524 * [116-116]
525 * [120-120]
526 * [124-124]
527 * [128-128]
528 * [132-132]
529 * [136-136]
530 * [140-140]
531 *
532 * Returns 0 if the IE has been found to be invalid in the middle
533 * somewhere.
534 */
535static int max_subband_chan(int orig_cur_chan,
536 int orig_end_channel,
537 s8 orig_max_power,
538 u8 **country_ie,
539 u8 *country_ie_len)
540{
541 u8 *triplets_start = *country_ie;
542 u8 len_at_triplet = *country_ie_len;
543 int end_subband_chan = orig_end_channel;
544 enum ieee80211_band band;
545
546 /*
547 * We'll deal with padding for the caller unless
548 * its not immediate and we don't process any channels
549 */
550 if (*country_ie_len == 1) {
551 *country_ie += 1;
552 *country_ie_len -= 1;
553 return orig_end_channel;
554 }
555
556 /* Move to the next triplet and then start search */
557 *country_ie += 3;
558 *country_ie_len -= 3;
559
560 if (orig_cur_chan <= 14)
561 band = IEEE80211_BAND_2GHZ;
562 else
563 band = IEEE80211_BAND_5GHZ;
564
565 while (*country_ie_len >= 3) {
566 int end_channel = 0;
567 struct ieee80211_country_ie_triplet *triplet =
568 (struct ieee80211_country_ie_triplet *) *country_ie;
569 int cur_channel = 0, next_expected_chan;
570 enum ieee80211_band next_band = IEEE80211_BAND_2GHZ;
571
572 /* means last triplet is completely unrelated to this one */
573 if (triplet->ext.reg_extension_id >=
574 IEEE80211_COUNTRY_EXTENSION_ID) {
575 *country_ie -= 3;
576 *country_ie_len += 3;
577 break;
578 }
579
580 if (triplet->chans.first_channel == 0) {
581 *country_ie += 1;
582 *country_ie_len -= 1;
583 if (*country_ie_len != 0)
584 return 0;
585 break;
586 }
587
588 /* Monitonically increasing channel order */
589 if (triplet->chans.first_channel <= end_subband_chan)
590 return 0;
591
592 /* 2 GHz */
593 if (triplet->chans.first_channel <= 14) {
594 end_channel = triplet->chans.first_channel +
595 triplet->chans.num_channels - 1;
596 }
597 else {
598 end_channel = triplet->chans.first_channel +
599 (4 * (triplet->chans.num_channels - 1));
600 next_band = IEEE80211_BAND_5GHZ;
601 }
602
603 if (band != next_band) {
604 *country_ie -= 3;
605 *country_ie_len += 3;
606 break;
607 }
608
609 if (orig_max_power != triplet->chans.max_power) {
610 *country_ie -= 3;
611 *country_ie_len += 3;
612 break;
613 }
614
615 cur_channel = triplet->chans.first_channel;
616
617 /* The key is finding the right next expected channel */
618 if (band == IEEE80211_BAND_2GHZ)
619 next_expected_chan = end_subband_chan + 1;
620 else
621 next_expected_chan = end_subband_chan + 4;
622
623 if (cur_channel != next_expected_chan) {
624 *country_ie -= 3;
625 *country_ie_len += 3;
626 break;
627 }
628
629 end_subband_chan = end_channel;
630
631 /* Move to the next one */
632 *country_ie += 3;
633 *country_ie_len -= 3;
634
635 /*
636 * Padding needs to be dealt with if we processed
637 * some channels.
638 */
639 if (*country_ie_len == 1) {
640 *country_ie += 1;
641 *country_ie_len -= 1;
642 break;
643 }
644
645 /* If seen, the IE is invalid */
646 if (*country_ie_len == 2)
647 return 0;
648 }
649
650 if (end_subband_chan == orig_end_channel) {
651 *country_ie = triplets_start;
652 *country_ie_len = len_at_triplet;
653 return orig_end_channel;
654 }
655
656 return end_subband_chan;
657}
658
659/*
488 * Converts a country IE to a regulatory domain. A regulatory domain 660 * Converts a country IE to a regulatory domain. A regulatory domain
489 * structure has a lot of information which the IE doesn't yet have, 661 * structure has a lot of information which the IE doesn't yet have,
490 * so for the other values we use upper max values as we will intersect 662 * so for the other values we use upper max values as we will intersect
@@ -552,6 +724,19 @@ static struct ieee80211_regdomain *country_ie_2_rd(
552 continue; 724 continue;
553 } 725 }
554 726
727 /*
728 * APs can add padding to make length divisible
729 * by two, required by the spec.
730 */
731 if (triplet->chans.first_channel == 0) {
732 country_ie++;
733 country_ie_len--;
734 /* This is expected to be at the very end only */
735 if (country_ie_len != 0)
736 return NULL;
737 break;
738 }
739
555 /* 2 GHz */ 740 /* 2 GHz */
556 if (triplet->chans.first_channel <= 14) 741 if (triplet->chans.first_channel <= 14)
557 end_channel = triplet->chans.first_channel + 742 end_channel = triplet->chans.first_channel +
@@ -570,6 +755,20 @@ static struct ieee80211_regdomain *country_ie_2_rd(
570 (4 * (triplet->chans.num_channels - 1)); 755 (4 * (triplet->chans.num_channels - 1));
571 756
572 cur_channel = triplet->chans.first_channel; 757 cur_channel = triplet->chans.first_channel;
758
759 /*
760 * Enhancement for APs that send a triplet for every channel
761 * or for whatever reason sends triplets with multiple channels
762 * separated when in fact they should be together.
763 */
764 end_channel = max_subband_chan(cur_channel,
765 end_channel,
766 triplet->chans.max_power,
767 &country_ie,
768 &country_ie_len);
769 if (!end_channel)
770 return NULL;
771
573 cur_sub_max_channel = end_channel; 772 cur_sub_max_channel = end_channel;
574 773
575 /* Basic sanity check */ 774 /* Basic sanity check */
@@ -600,10 +799,13 @@ static struct ieee80211_regdomain *country_ie_2_rd(
600 799
601 last_sub_max_channel = cur_sub_max_channel; 800 last_sub_max_channel = cur_sub_max_channel;
602 801
603 country_ie += 3;
604 country_ie_len -= 3;
605 num_rules++; 802 num_rules++;
606 803
804 if (country_ie_len >= 3) {
805 country_ie += 3;
806 country_ie_len -= 3;
807 }
808
607 /* 809 /*
608 * Note: this is not a IEEE requirement but 810 * Note: this is not a IEEE requirement but
609 * simply a memory requirement 811 * simply a memory requirement
@@ -646,6 +848,12 @@ static struct ieee80211_regdomain *country_ie_2_rd(
646 continue; 848 continue;
647 } 849 }
648 850
851 if (triplet->chans.first_channel == 0) {
852 country_ie++;
853 country_ie_len--;
854 break;
855 }
856
649 reg_rule = &rd->reg_rules[i]; 857 reg_rule = &rd->reg_rules[i];
650 freq_range = &reg_rule->freq_range; 858 freq_range = &reg_rule->freq_range;
651 power_rule = &reg_rule->power_rule; 859 power_rule = &reg_rule->power_rule;
@@ -660,6 +868,12 @@ static struct ieee80211_regdomain *country_ie_2_rd(
660 end_channel = triplet->chans.first_channel + 868 end_channel = triplet->chans.first_channel +
661 (4 * (triplet->chans.num_channels - 1)); 869 (4 * (triplet->chans.num_channels - 1));
662 870
871 end_channel = max_subband_chan(triplet->chans.first_channel,
872 end_channel,
873 triplet->chans.max_power,
874 &country_ie,
875 &country_ie_len);
876
663 /* 877 /*
664 * The +10 is since the regulatory domain expects 878 * The +10 is since the regulatory domain expects
665 * the actual band edge, not the center of freq for 879 * the actual band edge, not the center of freq for
@@ -682,10 +896,13 @@ static struct ieee80211_regdomain *country_ie_2_rd(
682 power_rule->max_antenna_gain = DBI_TO_MBI(100); 896 power_rule->max_antenna_gain = DBI_TO_MBI(100);
683 power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power); 897 power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power);
684 898
685 country_ie += 3;
686 country_ie_len -= 3;
687 i++; 899 i++;
688 900
901 if (country_ie_len >= 3) {
902 country_ie += 3;
903 country_ie_len -= 3;
904 }
905
689 BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); 906 BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
690 } 907 }
691 908