diff options
Diffstat (limited to 'net/mac80211/util.c')
-rw-r--r-- | net/mac80211/util.c | 240 |
1 files changed, 207 insertions, 33 deletions
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index fdf432f14554..61876eb50b49 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/if_arp.h> | 20 | #include <linux/if_arp.h> |
21 | #include <linux/wireless.h> | 21 | #include <linux/wireless.h> |
22 | #include <linux/bitmap.h> | 22 | #include <linux/bitmap.h> |
23 | #include <linux/crc32.h> | ||
23 | #include <net/net_namespace.h> | 24 | #include <net/net_namespace.h> |
24 | #include <net/cfg80211.h> | 25 | #include <net/cfg80211.h> |
25 | #include <net/rtnetlink.h> | 26 | #include <net/rtnetlink.h> |
@@ -28,6 +29,7 @@ | |||
28 | #include "rate.h" | 29 | #include "rate.h" |
29 | #include "mesh.h" | 30 | #include "mesh.h" |
30 | #include "wme.h" | 31 | #include "wme.h" |
32 | #include "led.h" | ||
31 | 33 | ||
32 | /* privid for wiphys to determine whether they belong to us or not */ | 34 | /* privid for wiphys to determine whether they belong to us or not */ |
33 | void *mac80211_wiphy_privid = &mac80211_wiphy_privid; | 35 | void *mac80211_wiphy_privid = &mac80211_wiphy_privid; |
@@ -536,8 +538,16 @@ EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); | |||
536 | void ieee802_11_parse_elems(u8 *start, size_t len, | 538 | void ieee802_11_parse_elems(u8 *start, size_t len, |
537 | struct ieee802_11_elems *elems) | 539 | struct ieee802_11_elems *elems) |
538 | { | 540 | { |
541 | ieee802_11_parse_elems_crc(start, len, elems, 0, 0); | ||
542 | } | ||
543 | |||
544 | u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, | ||
545 | struct ieee802_11_elems *elems, | ||
546 | u64 filter, u32 crc) | ||
547 | { | ||
539 | size_t left = len; | 548 | size_t left = len; |
540 | u8 *pos = start; | 549 | u8 *pos = start; |
550 | bool calc_crc = filter != 0; | ||
541 | 551 | ||
542 | memset(elems, 0, sizeof(*elems)); | 552 | memset(elems, 0, sizeof(*elems)); |
543 | elems->ie_start = start; | 553 | elems->ie_start = start; |
@@ -551,7 +561,10 @@ void ieee802_11_parse_elems(u8 *start, size_t len, | |||
551 | left -= 2; | 561 | left -= 2; |
552 | 562 | ||
553 | if (elen > left) | 563 | if (elen > left) |
554 | return; | 564 | break; |
565 | |||
566 | if (calc_crc && id < 64 && (filter & BIT(id))) | ||
567 | crc = crc32_be(crc, pos - 2, elen + 2); | ||
555 | 568 | ||
556 | switch (id) { | 569 | switch (id) { |
557 | case WLAN_EID_SSID: | 570 | case WLAN_EID_SSID: |
@@ -575,8 +588,10 @@ void ieee802_11_parse_elems(u8 *start, size_t len, | |||
575 | elems->cf_params_len = elen; | 588 | elems->cf_params_len = elen; |
576 | break; | 589 | break; |
577 | case WLAN_EID_TIM: | 590 | case WLAN_EID_TIM: |
578 | elems->tim = pos; | 591 | if (elen >= sizeof(struct ieee80211_tim_ie)) { |
579 | elems->tim_len = elen; | 592 | elems->tim = (void *)pos; |
593 | elems->tim_len = elen; | ||
594 | } | ||
580 | break; | 595 | break; |
581 | case WLAN_EID_IBSS_PARAMS: | 596 | case WLAN_EID_IBSS_PARAMS: |
582 | elems->ibss_params = pos; | 597 | elems->ibss_params = pos; |
@@ -586,15 +601,20 @@ void ieee802_11_parse_elems(u8 *start, size_t len, | |||
586 | elems->challenge = pos; | 601 | elems->challenge = pos; |
587 | elems->challenge_len = elen; | 602 | elems->challenge_len = elen; |
588 | break; | 603 | break; |
589 | case WLAN_EID_WPA: | 604 | case WLAN_EID_VENDOR_SPECIFIC: |
590 | if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && | 605 | if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && |
591 | pos[2] == 0xf2) { | 606 | pos[2] == 0xf2) { |
592 | /* Microsoft OUI (00:50:F2) */ | 607 | /* Microsoft OUI (00:50:F2) */ |
608 | |||
609 | if (calc_crc) | ||
610 | crc = crc32_be(crc, pos - 2, elen + 2); | ||
611 | |||
593 | if (pos[3] == 1) { | 612 | if (pos[3] == 1) { |
594 | /* OUI Type 1 - WPA IE */ | 613 | /* OUI Type 1 - WPA IE */ |
595 | elems->wpa = pos; | 614 | elems->wpa = pos; |
596 | elems->wpa_len = elen; | 615 | elems->wpa_len = elen; |
597 | } else if (elen >= 5 && pos[3] == 2) { | 616 | } else if (elen >= 5 && pos[3] == 2) { |
617 | /* OUI Type 2 - WMM IE */ | ||
598 | if (pos[4] == 0) { | 618 | if (pos[4] == 0) { |
599 | elems->wmm_info = pos; | 619 | elems->wmm_info = pos; |
600 | elems->wmm_info_len = elen; | 620 | elems->wmm_info_len = elen; |
@@ -679,6 +699,8 @@ void ieee802_11_parse_elems(u8 *start, size_t len, | |||
679 | left -= elen; | 699 | left -= elen; |
680 | pos += elen; | 700 | pos += elen; |
681 | } | 701 | } |
702 | |||
703 | return crc; | ||
682 | } | 704 | } |
683 | 705 | ||
684 | void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) | 706 | void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) |
@@ -831,16 +853,73 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | |||
831 | ieee80211_tx_skb(sdata, skb, encrypt); | 853 | ieee80211_tx_skb(sdata, skb, encrypt); |
832 | } | 854 | } |
833 | 855 | ||
856 | int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | ||
857 | const u8 *ie, size_t ie_len) | ||
858 | { | ||
859 | struct ieee80211_supported_band *sband; | ||
860 | u8 *pos, *supp_rates_len, *esupp_rates_len = NULL; | ||
861 | int i; | ||
862 | |||
863 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | ||
864 | |||
865 | pos = buffer; | ||
866 | |||
867 | *pos++ = WLAN_EID_SUPP_RATES; | ||
868 | supp_rates_len = pos; | ||
869 | *pos++ = 0; | ||
870 | |||
871 | for (i = 0; i < sband->n_bitrates; i++) { | ||
872 | struct ieee80211_rate *rate = &sband->bitrates[i]; | ||
873 | |||
874 | if (esupp_rates_len) { | ||
875 | *esupp_rates_len += 1; | ||
876 | } else if (*supp_rates_len == 8) { | ||
877 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | ||
878 | esupp_rates_len = pos; | ||
879 | *pos++ = 1; | ||
880 | } else | ||
881 | *supp_rates_len += 1; | ||
882 | |||
883 | *pos++ = rate->bitrate / 5; | ||
884 | } | ||
885 | |||
886 | if (sband->ht_cap.ht_supported) { | ||
887 | __le16 tmp = cpu_to_le16(sband->ht_cap.cap); | ||
888 | |||
889 | *pos++ = WLAN_EID_HT_CAPABILITY; | ||
890 | *pos++ = sizeof(struct ieee80211_ht_cap); | ||
891 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); | ||
892 | memcpy(pos, &tmp, sizeof(u16)); | ||
893 | pos += sizeof(u16); | ||
894 | /* TODO: needs a define here for << 2 */ | ||
895 | *pos++ = sband->ht_cap.ampdu_factor | | ||
896 | (sband->ht_cap.ampdu_density << 2); | ||
897 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); | ||
898 | pos += sizeof(sband->ht_cap.mcs); | ||
899 | pos += 2 + 4 + 1; /* ext info, BF cap, antsel */ | ||
900 | } | ||
901 | |||
902 | /* | ||
903 | * If adding more here, adjust code in main.c | ||
904 | * that calculates local->scan_ies_len. | ||
905 | */ | ||
906 | |||
907 | if (ie) { | ||
908 | memcpy(pos, ie, ie_len); | ||
909 | pos += ie_len; | ||
910 | } | ||
911 | |||
912 | return pos - buffer; | ||
913 | } | ||
914 | |||
834 | void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, | 915 | void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, |
835 | u8 *ssid, size_t ssid_len, | 916 | const u8 *ssid, size_t ssid_len, |
836 | u8 *ie, size_t ie_len) | 917 | const u8 *ie, size_t ie_len) |
837 | { | 918 | { |
838 | struct ieee80211_local *local = sdata->local; | 919 | struct ieee80211_local *local = sdata->local; |
839 | struct ieee80211_supported_band *sband; | ||
840 | struct sk_buff *skb; | 920 | struct sk_buff *skb; |
841 | struct ieee80211_mgmt *mgmt; | 921 | struct ieee80211_mgmt *mgmt; |
842 | u8 *pos, *supp_rates, *esupp_rates = NULL; | 922 | u8 *pos; |
843 | int i; | ||
844 | 923 | ||
845 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + | 924 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + |
846 | ie_len); | 925 | ie_len); |
@@ -867,31 +946,9 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, | |||
867 | *pos++ = WLAN_EID_SSID; | 946 | *pos++ = WLAN_EID_SSID; |
868 | *pos++ = ssid_len; | 947 | *pos++ = ssid_len; |
869 | memcpy(pos, ssid, ssid_len); | 948 | memcpy(pos, ssid, ssid_len); |
949 | pos += ssid_len; | ||
870 | 950 | ||
871 | supp_rates = skb_put(skb, 2); | 951 | skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len)); |
872 | supp_rates[0] = WLAN_EID_SUPP_RATES; | ||
873 | supp_rates[1] = 0; | ||
874 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | ||
875 | |||
876 | for (i = 0; i < sband->n_bitrates; i++) { | ||
877 | struct ieee80211_rate *rate = &sband->bitrates[i]; | ||
878 | if (esupp_rates) { | ||
879 | pos = skb_put(skb, 1); | ||
880 | esupp_rates[1]++; | ||
881 | } else if (supp_rates[1] == 8) { | ||
882 | esupp_rates = skb_put(skb, 3); | ||
883 | esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES; | ||
884 | esupp_rates[1] = 1; | ||
885 | pos = &esupp_rates[2]; | ||
886 | } else { | ||
887 | pos = skb_put(skb, 1); | ||
888 | supp_rates[1]++; | ||
889 | } | ||
890 | *pos = rate->bitrate / 5; | ||
891 | } | ||
892 | |||
893 | if (ie) | ||
894 | memcpy(skb_put(skb, ie_len), ie, ie_len); | ||
895 | 952 | ||
896 | ieee80211_tx_skb(sdata, skb, 0); | 953 | ieee80211_tx_skb(sdata, skb, 0); |
897 | } | 954 | } |
@@ -931,3 +988,120 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local, | |||
931 | } | 988 | } |
932 | return supp_rates; | 989 | return supp_rates; |
933 | } | 990 | } |
991 | |||
992 | int ieee80211_reconfig(struct ieee80211_local *local) | ||
993 | { | ||
994 | struct ieee80211_hw *hw = &local->hw; | ||
995 | struct ieee80211_sub_if_data *sdata; | ||
996 | struct ieee80211_if_init_conf conf; | ||
997 | struct sta_info *sta; | ||
998 | unsigned long flags; | ||
999 | int res; | ||
1000 | |||
1001 | /* restart hardware */ | ||
1002 | if (local->open_count) { | ||
1003 | res = local->ops->start(hw); | ||
1004 | |||
1005 | ieee80211_led_radio(local, hw->conf.radio_enabled); | ||
1006 | } | ||
1007 | |||
1008 | /* add interfaces */ | ||
1009 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
1010 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && | ||
1011 | sdata->vif.type != NL80211_IFTYPE_MONITOR && | ||
1012 | netif_running(sdata->dev)) { | ||
1013 | conf.vif = &sdata->vif; | ||
1014 | conf.type = sdata->vif.type; | ||
1015 | conf.mac_addr = sdata->dev->dev_addr; | ||
1016 | res = local->ops->add_interface(hw, &conf); | ||
1017 | } | ||
1018 | } | ||
1019 | |||
1020 | /* add STAs back */ | ||
1021 | if (local->ops->sta_notify) { | ||
1022 | spin_lock_irqsave(&local->sta_lock, flags); | ||
1023 | list_for_each_entry(sta, &local->sta_list, list) { | ||
1024 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | ||
1025 | sdata = container_of(sdata->bss, | ||
1026 | struct ieee80211_sub_if_data, | ||
1027 | u.ap); | ||
1028 | |||
1029 | local->ops->sta_notify(hw, &sdata->vif, | ||
1030 | STA_NOTIFY_ADD, &sta->sta); | ||
1031 | } | ||
1032 | spin_unlock_irqrestore(&local->sta_lock, flags); | ||
1033 | } | ||
1034 | |||
1035 | /* Clear Suspend state so that ADDBA requests can be processed */ | ||
1036 | |||
1037 | rcu_read_lock(); | ||
1038 | |||
1039 | if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) { | ||
1040 | list_for_each_entry_rcu(sta, &local->sta_list, list) { | ||
1041 | clear_sta_flags(sta, WLAN_STA_SUSPEND); | ||
1042 | } | ||
1043 | } | ||
1044 | |||
1045 | rcu_read_unlock(); | ||
1046 | |||
1047 | /* setup RTS threshold */ | ||
1048 | if (local->ops->set_rts_threshold) | ||
1049 | local->ops->set_rts_threshold(hw, hw->wiphy->rts_threshold); | ||
1050 | |||
1051 | /* reconfigure hardware */ | ||
1052 | ieee80211_hw_config(local, ~0); | ||
1053 | |||
1054 | netif_addr_lock_bh(local->mdev); | ||
1055 | ieee80211_configure_filter(local); | ||
1056 | netif_addr_unlock_bh(local->mdev); | ||
1057 | |||
1058 | /* Finally also reconfigure all the BSS information */ | ||
1059 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
1060 | u32 changed = ~0; | ||
1061 | if (!netif_running(sdata->dev)) | ||
1062 | continue; | ||
1063 | switch (sdata->vif.type) { | ||
1064 | case NL80211_IFTYPE_STATION: | ||
1065 | /* disable beacon change bits */ | ||
1066 | changed &= ~IEEE80211_IFCC_BEACON; | ||
1067 | /* fall through */ | ||
1068 | case NL80211_IFTYPE_ADHOC: | ||
1069 | case NL80211_IFTYPE_AP: | ||
1070 | case NL80211_IFTYPE_MESH_POINT: | ||
1071 | /* | ||
1072 | * Driver's config_interface can fail if rfkill is | ||
1073 | * enabled. Accommodate this return code. | ||
1074 | * FIXME: When mac80211 has knowledge of rfkill | ||
1075 | * state the code below can change back to: | ||
1076 | * WARN(ieee80211_if_config(sdata, changed)); | ||
1077 | * ieee80211_bss_info_change_notify(sdata, ~0); | ||
1078 | */ | ||
1079 | if (ieee80211_if_config(sdata, changed)) | ||
1080 | printk(KERN_DEBUG "%s: failed to configure interface during resume\n", | ||
1081 | sdata->dev->name); | ||
1082 | else | ||
1083 | ieee80211_bss_info_change_notify(sdata, ~0); | ||
1084 | break; | ||
1085 | case NL80211_IFTYPE_WDS: | ||
1086 | break; | ||
1087 | case NL80211_IFTYPE_AP_VLAN: | ||
1088 | case NL80211_IFTYPE_MONITOR: | ||
1089 | /* ignore virtual */ | ||
1090 | break; | ||
1091 | case NL80211_IFTYPE_UNSPECIFIED: | ||
1092 | case __NL80211_IFTYPE_AFTER_LAST: | ||
1093 | WARN_ON(1); | ||
1094 | break; | ||
1095 | } | ||
1096 | } | ||
1097 | |||
1098 | /* add back keys */ | ||
1099 | list_for_each_entry(sdata, &local->interfaces, list) | ||
1100 | if (netif_running(sdata->dev)) | ||
1101 | ieee80211_enable_keys(sdata); | ||
1102 | |||
1103 | ieee80211_wake_queues_by_reason(hw, | ||
1104 | IEEE80211_QUEUE_STOP_REASON_SUSPEND); | ||
1105 | |||
1106 | return 0; | ||
1107 | } | ||