diff options
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 319 |
1 files changed, 291 insertions, 28 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 87ea60d84c3c..5f8071de7950 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -43,6 +43,15 @@ | |||
43 | #include "regdb.h" | 43 | #include "regdb.h" |
44 | #include "nl80211.h" | 44 | #include "nl80211.h" |
45 | 45 | ||
46 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
47 | #define REG_DBG_PRINT(format, args...) \ | ||
48 | do { \ | ||
49 | printk(KERN_DEBUG format , ## args); \ | ||
50 | } while (0) | ||
51 | #else | ||
52 | #define REG_DBG_PRINT(args...) | ||
53 | #endif | ||
54 | |||
46 | /* Receipt of information from last regulatory request */ | 55 | /* Receipt of information from last regulatory request */ |
47 | static struct regulatory_request *last_request; | 56 | static struct regulatory_request *last_request; |
48 | 57 | ||
@@ -476,12 +485,212 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, | |||
476 | } | 485 | } |
477 | 486 | ||
478 | /* | 487 | /* |
488 | * This is a work around for sanity checking ieee80211_channel_to_frequency()'s | ||
489 | * work. ieee80211_channel_to_frequency() can for example currently provide a | ||
490 | * 2 GHz channel when in fact a 5 GHz channel was desired. An example would be | ||
491 | * an AP providing channel 8 on a country IE triplet when it sent this on the | ||
492 | * 5 GHz band, that channel is designed to be channel 8 on 5 GHz, not a 2 GHz | ||
493 | * channel. | ||
494 | * | ||
495 | * This can be removed once ieee80211_channel_to_frequency() takes in a band. | ||
496 | */ | ||
497 | static bool chan_in_band(int chan, enum ieee80211_band band) | ||
498 | { | ||
499 | int center_freq = ieee80211_channel_to_frequency(chan); | ||
500 | |||
501 | switch (band) { | ||
502 | case IEEE80211_BAND_2GHZ: | ||
503 | if (center_freq <= 2484) | ||
504 | return true; | ||
505 | return false; | ||
506 | case IEEE80211_BAND_5GHZ: | ||
507 | if (center_freq >= 5005) | ||
508 | return true; | ||
509 | return false; | ||
510 | default: | ||
511 | return false; | ||
512 | } | ||
513 | } | ||
514 | |||
515 | /* | ||
516 | * Some APs may send a country IE triplet for each channel they | ||
517 | * support and while this is completely overkill and silly we still | ||
518 | * need to support it. We avoid making a single rule for each channel | ||
519 | * though and to help us with this we use this helper to find the | ||
520 | * actual subband end channel. These type of country IE triplet | ||
521 | * scenerios are handled then, all yielding two regulaotry rules from | ||
522 | * parsing a country IE: | ||
523 | * | ||
524 | * [1] | ||
525 | * [2] | ||
526 | * [36] | ||
527 | * [40] | ||
528 | * | ||
529 | * [1] | ||
530 | * [2-4] | ||
531 | * [5-12] | ||
532 | * [36] | ||
533 | * [40-44] | ||
534 | * | ||
535 | * [1-4] | ||
536 | * [5-7] | ||
537 | * [36-44] | ||
538 | * [48-64] | ||
539 | * | ||
540 | * [36-36] | ||
541 | * [40-40] | ||
542 | * [44-44] | ||
543 | * [48-48] | ||
544 | * [52-52] | ||
545 | * [56-56] | ||
546 | * [60-60] | ||
547 | * [64-64] | ||
548 | * [100-100] | ||
549 | * [104-104] | ||
550 | * [108-108] | ||
551 | * [112-112] | ||
552 | * [116-116] | ||
553 | * [120-120] | ||
554 | * [124-124] | ||
555 | * [128-128] | ||
556 | * [132-132] | ||
557 | * [136-136] | ||
558 | * [140-140] | ||
559 | * | ||
560 | * Returns 0 if the IE has been found to be invalid in the middle | ||
561 | * somewhere. | ||
562 | */ | ||
563 | static int max_subband_chan(enum ieee80211_band band, | ||
564 | int orig_cur_chan, | ||
565 | int orig_end_channel, | ||
566 | s8 orig_max_power, | ||
567 | u8 **country_ie, | ||
568 | u8 *country_ie_len) | ||
569 | { | ||
570 | u8 *triplets_start = *country_ie; | ||
571 | u8 len_at_triplet = *country_ie_len; | ||
572 | int end_subband_chan = orig_end_channel; | ||
573 | |||
574 | /* | ||
575 | * We'll deal with padding for the caller unless | ||
576 | * its not immediate and we don't process any channels | ||
577 | */ | ||
578 | if (*country_ie_len == 1) { | ||
579 | *country_ie += 1; | ||
580 | *country_ie_len -= 1; | ||
581 | return orig_end_channel; | ||
582 | } | ||
583 | |||
584 | /* Move to the next triplet and then start search */ | ||
585 | *country_ie += 3; | ||
586 | *country_ie_len -= 3; | ||
587 | |||
588 | if (!chan_in_band(orig_cur_chan, band)) | ||
589 | return 0; | ||
590 | |||
591 | while (*country_ie_len >= 3) { | ||
592 | int end_channel = 0; | ||
593 | struct ieee80211_country_ie_triplet *triplet = | ||
594 | (struct ieee80211_country_ie_triplet *) *country_ie; | ||
595 | int cur_channel = 0, next_expected_chan; | ||
596 | |||
597 | /* means last triplet is completely unrelated to this one */ | ||
598 | if (triplet->ext.reg_extension_id >= | ||
599 | IEEE80211_COUNTRY_EXTENSION_ID) { | ||
600 | *country_ie -= 3; | ||
601 | *country_ie_len += 3; | ||
602 | break; | ||
603 | } | ||
604 | |||
605 | if (triplet->chans.first_channel == 0) { | ||
606 | *country_ie += 1; | ||
607 | *country_ie_len -= 1; | ||
608 | if (*country_ie_len != 0) | ||
609 | return 0; | ||
610 | break; | ||
611 | } | ||
612 | |||
613 | if (triplet->chans.num_channels == 0) | ||
614 | return 0; | ||
615 | |||
616 | /* Monitonically increasing channel order */ | ||
617 | if (triplet->chans.first_channel <= end_subband_chan) | ||
618 | return 0; | ||
619 | |||
620 | if (!chan_in_band(triplet->chans.first_channel, band)) | ||
621 | return 0; | ||
622 | |||
623 | /* 2 GHz */ | ||
624 | if (triplet->chans.first_channel <= 14) { | ||
625 | end_channel = triplet->chans.first_channel + | ||
626 | triplet->chans.num_channels - 1; | ||
627 | } | ||
628 | else { | ||
629 | end_channel = triplet->chans.first_channel + | ||
630 | (4 * (triplet->chans.num_channels - 1)); | ||
631 | } | ||
632 | |||
633 | if (!chan_in_band(end_channel, band)) | ||
634 | return 0; | ||
635 | |||
636 | if (orig_max_power != triplet->chans.max_power) { | ||
637 | *country_ie -= 3; | ||
638 | *country_ie_len += 3; | ||
639 | break; | ||
640 | } | ||
641 | |||
642 | cur_channel = triplet->chans.first_channel; | ||
643 | |||
644 | /* The key is finding the right next expected channel */ | ||
645 | if (band == IEEE80211_BAND_2GHZ) | ||
646 | next_expected_chan = end_subband_chan + 1; | ||
647 | else | ||
648 | next_expected_chan = end_subband_chan + 4; | ||
649 | |||
650 | if (cur_channel != next_expected_chan) { | ||
651 | *country_ie -= 3; | ||
652 | *country_ie_len += 3; | ||
653 | break; | ||
654 | } | ||
655 | |||
656 | end_subband_chan = end_channel; | ||
657 | |||
658 | /* Move to the next one */ | ||
659 | *country_ie += 3; | ||
660 | *country_ie_len -= 3; | ||
661 | |||
662 | /* | ||
663 | * Padding needs to be dealt with if we processed | ||
664 | * some channels. | ||
665 | */ | ||
666 | if (*country_ie_len == 1) { | ||
667 | *country_ie += 1; | ||
668 | *country_ie_len -= 1; | ||
669 | break; | ||
670 | } | ||
671 | |||
672 | /* If seen, the IE is invalid */ | ||
673 | if (*country_ie_len == 2) | ||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | if (end_subband_chan == orig_end_channel) { | ||
678 | *country_ie = triplets_start; | ||
679 | *country_ie_len = len_at_triplet; | ||
680 | return orig_end_channel; | ||
681 | } | ||
682 | |||
683 | return end_subband_chan; | ||
684 | } | ||
685 | |||
686 | /* | ||
479 | * Converts a country IE to a regulatory domain. A regulatory domain | 687 | * Converts a country IE to a regulatory domain. A regulatory domain |
480 | * structure has a lot of information which the IE doesn't yet have, | 688 | * structure has a lot of information which the IE doesn't yet have, |
481 | * so for the other values we use upper max values as we will intersect | 689 | * so for the other values we use upper max values as we will intersect |
482 | * with our userspace regulatory agent to get lower bounds. | 690 | * with our userspace regulatory agent to get lower bounds. |
483 | */ | 691 | */ |
484 | static struct ieee80211_regdomain *country_ie_2_rd( | 692 | static struct ieee80211_regdomain *country_ie_2_rd( |
693 | enum ieee80211_band band, | ||
485 | u8 *country_ie, | 694 | u8 *country_ie, |
486 | u8 country_ie_len, | 695 | u8 country_ie_len, |
487 | u32 *checksum) | 696 | u32 *checksum) |
@@ -543,10 +752,29 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
543 | continue; | 752 | continue; |
544 | } | 753 | } |
545 | 754 | ||
755 | /* | ||
756 | * APs can add padding to make length divisible | ||
757 | * by two, required by the spec. | ||
758 | */ | ||
759 | if (triplet->chans.first_channel == 0) { | ||
760 | country_ie++; | ||
761 | country_ie_len--; | ||
762 | /* This is expected to be at the very end only */ | ||
763 | if (country_ie_len != 0) | ||
764 | return NULL; | ||
765 | break; | ||
766 | } | ||
767 | |||
768 | if (triplet->chans.num_channels == 0) | ||
769 | return NULL; | ||
770 | |||
771 | if (!chan_in_band(triplet->chans.first_channel, band)) | ||
772 | return NULL; | ||
773 | |||
546 | /* 2 GHz */ | 774 | /* 2 GHz */ |
547 | if (triplet->chans.first_channel <= 14) | 775 | if (band == IEEE80211_BAND_2GHZ) |
548 | end_channel = triplet->chans.first_channel + | 776 | end_channel = triplet->chans.first_channel + |
549 | triplet->chans.num_channels; | 777 | triplet->chans.num_channels - 1; |
550 | else | 778 | else |
551 | /* | 779 | /* |
552 | * 5 GHz -- For example in country IEs if the first | 780 | * 5 GHz -- For example in country IEs if the first |
@@ -561,6 +789,24 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
561 | (4 * (triplet->chans.num_channels - 1)); | 789 | (4 * (triplet->chans.num_channels - 1)); |
562 | 790 | ||
563 | cur_channel = triplet->chans.first_channel; | 791 | cur_channel = triplet->chans.first_channel; |
792 | |||
793 | /* | ||
794 | * Enhancement for APs that send a triplet for every channel | ||
795 | * or for whatever reason sends triplets with multiple channels | ||
796 | * separated when in fact they should be together. | ||
797 | */ | ||
798 | end_channel = max_subband_chan(band, | ||
799 | cur_channel, | ||
800 | end_channel, | ||
801 | triplet->chans.max_power, | ||
802 | &country_ie, | ||
803 | &country_ie_len); | ||
804 | if (!end_channel) | ||
805 | return NULL; | ||
806 | |||
807 | if (!chan_in_band(end_channel, band)) | ||
808 | return NULL; | ||
809 | |||
564 | cur_sub_max_channel = end_channel; | 810 | cur_sub_max_channel = end_channel; |
565 | 811 | ||
566 | /* Basic sanity check */ | 812 | /* Basic sanity check */ |
@@ -591,10 +837,13 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
591 | 837 | ||
592 | last_sub_max_channel = cur_sub_max_channel; | 838 | last_sub_max_channel = cur_sub_max_channel; |
593 | 839 | ||
594 | country_ie += 3; | ||
595 | country_ie_len -= 3; | ||
596 | num_rules++; | 840 | num_rules++; |
597 | 841 | ||
842 | if (country_ie_len >= 3) { | ||
843 | country_ie += 3; | ||
844 | country_ie_len -= 3; | ||
845 | } | ||
846 | |||
598 | /* | 847 | /* |
599 | * Note: this is not a IEEE requirement but | 848 | * Note: this is not a IEEE requirement but |
600 | * simply a memory requirement | 849 | * simply a memory requirement |
@@ -637,6 +886,12 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
637 | continue; | 886 | continue; |
638 | } | 887 | } |
639 | 888 | ||
889 | if (triplet->chans.first_channel == 0) { | ||
890 | country_ie++; | ||
891 | country_ie_len--; | ||
892 | break; | ||
893 | } | ||
894 | |||
640 | reg_rule = &rd->reg_rules[i]; | 895 | reg_rule = &rd->reg_rules[i]; |
641 | freq_range = ®_rule->freq_range; | 896 | freq_range = ®_rule->freq_range; |
642 | power_rule = ®_rule->power_rule; | 897 | power_rule = ®_rule->power_rule; |
@@ -644,13 +899,20 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
644 | reg_rule->flags = flags; | 899 | reg_rule->flags = flags; |
645 | 900 | ||
646 | /* 2 GHz */ | 901 | /* 2 GHz */ |
647 | if (triplet->chans.first_channel <= 14) | 902 | if (band == IEEE80211_BAND_2GHZ) |
648 | end_channel = triplet->chans.first_channel + | 903 | end_channel = triplet->chans.first_channel + |
649 | triplet->chans.num_channels; | 904 | triplet->chans.num_channels -1; |
650 | else | 905 | else |
651 | end_channel = triplet->chans.first_channel + | 906 | end_channel = triplet->chans.first_channel + |
652 | (4 * (triplet->chans.num_channels - 1)); | 907 | (4 * (triplet->chans.num_channels - 1)); |
653 | 908 | ||
909 | end_channel = max_subband_chan(band, | ||
910 | triplet->chans.first_channel, | ||
911 | end_channel, | ||
912 | triplet->chans.max_power, | ||
913 | &country_ie, | ||
914 | &country_ie_len); | ||
915 | |||
654 | /* | 916 | /* |
655 | * The +10 is since the regulatory domain expects | 917 | * The +10 is since the regulatory domain expects |
656 | * the actual band edge, not the center of freq for | 918 | * the actual band edge, not the center of freq for |
@@ -671,12 +933,15 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
671 | */ | 933 | */ |
672 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); | 934 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); |
673 | power_rule->max_antenna_gain = DBI_TO_MBI(100); | 935 | power_rule->max_antenna_gain = DBI_TO_MBI(100); |
674 | power_rule->max_eirp = DBM_TO_MBM(100); | 936 | power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power); |
675 | 937 | ||
676 | country_ie += 3; | ||
677 | country_ie_len -= 3; | ||
678 | i++; | 938 | i++; |
679 | 939 | ||
940 | if (country_ie_len >= 3) { | ||
941 | country_ie += 3; | ||
942 | country_ie_len -= 3; | ||
943 | } | ||
944 | |||
680 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); | 945 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); |
681 | } | 946 | } |
682 | 947 | ||
@@ -972,25 +1237,21 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
972 | if (r == -ERANGE && | 1237 | if (r == -ERANGE && |
973 | last_request->initiator == | 1238 | last_request->initiator == |
974 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 1239 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
975 | #ifdef CONFIG_CFG80211_REG_DEBUG | 1240 | REG_DBG_PRINT("cfg80211: Leaving channel %d MHz " |
976 | printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz " | ||
977 | "intact on %s - no rule found in band on " | 1241 | "intact on %s - no rule found in band on " |
978 | "Country IE\n", | 1242 | "Country IE\n", |
979 | chan->center_freq, wiphy_name(wiphy)); | 1243 | chan->center_freq, wiphy_name(wiphy)); |
980 | #endif | ||
981 | } else { | 1244 | } else { |
982 | /* | 1245 | /* |
983 | * In this case we know the country IE has at least one reg rule | 1246 | * In this case we know the country IE has at least one reg rule |
984 | * for the band so we respect its band definitions | 1247 | * for the band so we respect its band definitions |
985 | */ | 1248 | */ |
986 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
987 | if (last_request->initiator == | 1249 | if (last_request->initiator == |
988 | NL80211_REGDOM_SET_BY_COUNTRY_IE) | 1250 | NL80211_REGDOM_SET_BY_COUNTRY_IE) |
989 | printk(KERN_DEBUG "cfg80211: Disabling " | 1251 | REG_DBG_PRINT("cfg80211: Disabling " |
990 | "channel %d MHz on %s due to " | 1252 | "channel %d MHz on %s due to " |
991 | "Country IE\n", | 1253 | "Country IE\n", |
992 | chan->center_freq, wiphy_name(wiphy)); | 1254 | chan->center_freq, wiphy_name(wiphy)); |
993 | #endif | ||
994 | flags |= IEEE80211_CHAN_DISABLED; | 1255 | flags |= IEEE80211_CHAN_DISABLED; |
995 | chan->flags = flags; | 1256 | chan->flags = flags; |
996 | } | 1257 | } |
@@ -1685,7 +1946,7 @@ int regulatory_hint_user(const char *alpha2) | |||
1685 | request->wiphy_idx = WIPHY_IDX_STALE; | 1946 | request->wiphy_idx = WIPHY_IDX_STALE; |
1686 | request->alpha2[0] = alpha2[0]; | 1947 | request->alpha2[0] = alpha2[0]; |
1687 | request->alpha2[1] = alpha2[1]; | 1948 | request->alpha2[1] = alpha2[1]; |
1688 | request->initiator = NL80211_REGDOM_SET_BY_USER, | 1949 | request->initiator = NL80211_REGDOM_SET_BY_USER; |
1689 | 1950 | ||
1690 | queue_regulatory_request(request); | 1951 | queue_regulatory_request(request); |
1691 | 1952 | ||
@@ -1753,8 +2014,9 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy, | |||
1753 | * therefore cannot iterate over the rdev list here. | 2014 | * therefore cannot iterate over the rdev list here. |
1754 | */ | 2015 | */ |
1755 | void regulatory_hint_11d(struct wiphy *wiphy, | 2016 | void regulatory_hint_11d(struct wiphy *wiphy, |
1756 | u8 *country_ie, | 2017 | enum ieee80211_band band, |
1757 | u8 country_ie_len) | 2018 | u8 *country_ie, |
2019 | u8 country_ie_len) | ||
1758 | { | 2020 | { |
1759 | struct ieee80211_regdomain *rd = NULL; | 2021 | struct ieee80211_regdomain *rd = NULL; |
1760 | char alpha2[2]; | 2022 | char alpha2[2]; |
@@ -1800,9 +2062,11 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
1800 | wiphy_idx_valid(last_request->wiphy_idx))) | 2062 | wiphy_idx_valid(last_request->wiphy_idx))) |
1801 | goto out; | 2063 | goto out; |
1802 | 2064 | ||
1803 | rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); | 2065 | rd = country_ie_2_rd(band, country_ie, country_ie_len, &checksum); |
1804 | if (!rd) | 2066 | if (!rd) { |
2067 | REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n"); | ||
1805 | goto out; | 2068 | goto out; |
2069 | } | ||
1806 | 2070 | ||
1807 | /* | 2071 | /* |
1808 | * This will not happen right now but we leave it here for the | 2072 | * This will not happen right now but we leave it here for the |
@@ -1870,13 +2134,12 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, | |||
1870 | if (!reg_beacon) | 2134 | if (!reg_beacon) |
1871 | return -ENOMEM; | 2135 | return -ENOMEM; |
1872 | 2136 | ||
1873 | #ifdef CONFIG_CFG80211_REG_DEBUG | 2137 | REG_DBG_PRINT("cfg80211: Found new beacon on " |
1874 | printk(KERN_DEBUG "cfg80211: Found new beacon on " | 2138 | "frequency: %d MHz (Ch %d) on %s\n", |
1875 | "frequency: %d MHz (Ch %d) on %s\n", | 2139 | beacon_chan->center_freq, |
1876 | beacon_chan->center_freq, | 2140 | ieee80211_frequency_to_channel(beacon_chan->center_freq), |
1877 | ieee80211_frequency_to_channel(beacon_chan->center_freq), | 2141 | wiphy_name(wiphy)); |
1878 | wiphy_name(wiphy)); | 2142 | |
1879 | #endif | ||
1880 | memcpy(®_beacon->chan, beacon_chan, | 2143 | memcpy(®_beacon->chan, beacon_chan, |
1881 | sizeof(struct ieee80211_channel)); | 2144 | sizeof(struct ieee80211_channel)); |
1882 | 2145 | ||