diff options
-rw-r--r-- | net/wireless/reg.c | 225 |
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 | */ | ||
535 | static 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 = ®_rule->freq_range; | 858 | freq_range = ®_rule->freq_range; |
651 | power_rule = ®_rule->power_rule; | 859 | power_rule = ®_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 | ||