diff options
Diffstat (limited to 'net/ieee80211/ieee80211_rx.c')
-rw-r--r-- | net/ieee80211/ieee80211_rx.c | 334 |
1 files changed, 315 insertions, 19 deletions
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c index d1ae28280d7e..2c4613527dfd 100644 --- a/net/ieee80211/ieee80211_rx.c +++ b/net/ieee80211/ieee80211_rx.c | |||
@@ -534,6 +534,9 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, | |||
534 | 534 | ||
535 | /* Nullfunc frames may have PS-bit set, so they must be passed to | 535 | /* Nullfunc frames may have PS-bit set, so they must be passed to |
536 | * hostap_handle_sta_rx() before being dropped here. */ | 536 | * hostap_handle_sta_rx() before being dropped here. */ |
537 | |||
538 | stype &= ~IEEE80211_STYPE_QOS_DATA; | ||
539 | |||
537 | if (stype != IEEE80211_STYPE_DATA && | 540 | if (stype != IEEE80211_STYPE_DATA && |
538 | stype != IEEE80211_STYPE_DATA_CFACK && | 541 | stype != IEEE80211_STYPE_DATA_CFACK && |
539 | stype != IEEE80211_STYPE_DATA_CFPOLL && | 542 | stype != IEEE80211_STYPE_DATA_CFPOLL && |
@@ -758,6 +761,264 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, | |||
758 | 761 | ||
759 | #define MGMT_FRAME_FIXED_PART_LENGTH 0x24 | 762 | #define MGMT_FRAME_FIXED_PART_LENGTH 0x24 |
760 | 763 | ||
764 | static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; | ||
765 | |||
766 | /* | ||
767 | * Make ther structure we read from the beacon packet has | ||
768 | * the right values | ||
769 | */ | ||
770 | static int ieee80211_verify_qos_info(struct ieee80211_qos_information_element | ||
771 | *info_element, int sub_type) | ||
772 | { | ||
773 | |||
774 | if (info_element->qui_subtype != sub_type) | ||
775 | return -1; | ||
776 | if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN)) | ||
777 | return -1; | ||
778 | if (info_element->qui_type != QOS_OUI_TYPE) | ||
779 | return -1; | ||
780 | if (info_element->version != QOS_VERSION_1) | ||
781 | return -1; | ||
782 | |||
783 | return 0; | ||
784 | } | ||
785 | |||
786 | /* | ||
787 | * Parse a QoS parameter element | ||
788 | */ | ||
789 | static int ieee80211_read_qos_param_element(struct ieee80211_qos_parameter_info | ||
790 | *element_param, struct ieee80211_info_element | ||
791 | *info_element) | ||
792 | { | ||
793 | int ret = 0; | ||
794 | u16 size = sizeof(struct ieee80211_qos_parameter_info) - 2; | ||
795 | |||
796 | if ((info_element == NULL) || (element_param == NULL)) | ||
797 | return -1; | ||
798 | |||
799 | if (info_element->id == QOS_ELEMENT_ID && info_element->len == size) { | ||
800 | memcpy(element_param->info_element.qui, info_element->data, | ||
801 | info_element->len); | ||
802 | element_param->info_element.elementID = info_element->id; | ||
803 | element_param->info_element.length = info_element->len; | ||
804 | } else | ||
805 | ret = -1; | ||
806 | if (ret == 0) | ||
807 | ret = ieee80211_verify_qos_info(&element_param->info_element, | ||
808 | QOS_OUI_PARAM_SUB_TYPE); | ||
809 | return ret; | ||
810 | } | ||
811 | |||
812 | /* | ||
813 | * Parse a QoS information element | ||
814 | */ | ||
815 | static int ieee80211_read_qos_info_element(struct | ||
816 | ieee80211_qos_information_element | ||
817 | *element_info, struct ieee80211_info_element | ||
818 | *info_element) | ||
819 | { | ||
820 | int ret = 0; | ||
821 | u16 size = sizeof(struct ieee80211_qos_information_element) - 2; | ||
822 | |||
823 | if (element_info == NULL) | ||
824 | return -1; | ||
825 | if (info_element == NULL) | ||
826 | return -1; | ||
827 | |||
828 | if ((info_element->id == QOS_ELEMENT_ID) && (info_element->len == size)) { | ||
829 | memcpy(element_info->qui, info_element->data, | ||
830 | info_element->len); | ||
831 | element_info->elementID = info_element->id; | ||
832 | element_info->length = info_element->len; | ||
833 | } else | ||
834 | ret = -1; | ||
835 | |||
836 | if (ret == 0) | ||
837 | ret = ieee80211_verify_qos_info(element_info, | ||
838 | QOS_OUI_INFO_SUB_TYPE); | ||
839 | return ret; | ||
840 | } | ||
841 | |||
842 | /* | ||
843 | * Write QoS parameters from the ac parameters. | ||
844 | */ | ||
845 | static int ieee80211_qos_convert_ac_to_parameters(struct | ||
846 | ieee80211_qos_parameter_info | ||
847 | *param_elm, struct | ||
848 | ieee80211_qos_parameters | ||
849 | *qos_param) | ||
850 | { | ||
851 | int rc = 0; | ||
852 | int i; | ||
853 | struct ieee80211_qos_ac_parameter *ac_params; | ||
854 | u32 txop; | ||
855 | u8 cw_min; | ||
856 | u8 cw_max; | ||
857 | |||
858 | for (i = 0; i < QOS_QUEUE_NUM; i++) { | ||
859 | ac_params = &(param_elm->ac_params_record[i]); | ||
860 | |||
861 | qos_param->aifs[i] = (ac_params->aci_aifsn) & 0x0F; | ||
862 | qos_param->aifs[i] -= (qos_param->aifs[i] < 2) ? 0 : 2; | ||
863 | |||
864 | cw_min = ac_params->ecw_min_max & 0x0F; | ||
865 | qos_param->cw_min[i] = (u16) ((1 << cw_min) - 1); | ||
866 | |||
867 | cw_max = (ac_params->ecw_min_max & 0xF0) >> 4; | ||
868 | qos_param->cw_max[i] = (u16) ((1 << cw_max) - 1); | ||
869 | |||
870 | qos_param->flag[i] = | ||
871 | (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00; | ||
872 | |||
873 | txop = le16_to_cpu(ac_params->tx_op_limit) * 32; | ||
874 | qos_param->tx_op_limit[i] = (u16) txop; | ||
875 | } | ||
876 | return rc; | ||
877 | } | ||
878 | |||
879 | /* | ||
880 | * we have a generic data element which it may contain QoS information or | ||
881 | * parameters element. check the information element length to decide | ||
882 | * which type to read | ||
883 | */ | ||
884 | static int ieee80211_parse_qos_info_param_IE(struct ieee80211_info_element | ||
885 | *info_element, | ||
886 | struct ieee80211_network *network) | ||
887 | { | ||
888 | int rc = 0; | ||
889 | struct ieee80211_qos_parameters *qos_param = NULL; | ||
890 | struct ieee80211_qos_information_element qos_info_element; | ||
891 | |||
892 | rc = ieee80211_read_qos_info_element(&qos_info_element, info_element); | ||
893 | |||
894 | if (rc == 0) { | ||
895 | network->qos_data.param_count = qos_info_element.ac_info & 0x0F; | ||
896 | network->flags |= NETWORK_HAS_QOS_INFORMATION; | ||
897 | } else { | ||
898 | struct ieee80211_qos_parameter_info param_element; | ||
899 | |||
900 | rc = ieee80211_read_qos_param_element(¶m_element, | ||
901 | info_element); | ||
902 | if (rc == 0) { | ||
903 | qos_param = &(network->qos_data.parameters); | ||
904 | ieee80211_qos_convert_ac_to_parameters(¶m_element, | ||
905 | qos_param); | ||
906 | network->flags |= NETWORK_HAS_QOS_PARAMETERS; | ||
907 | network->qos_data.param_count = | ||
908 | param_element.info_element.ac_info & 0x0F; | ||
909 | } | ||
910 | } | ||
911 | |||
912 | if (rc == 0) { | ||
913 | IEEE80211_DEBUG_QOS("QoS is supported\n"); | ||
914 | network->qos_data.supported = 1; | ||
915 | } | ||
916 | return rc; | ||
917 | } | ||
918 | |||
919 | static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct ieee80211_assoc_response | ||
920 | *frame, struct ieee80211_rx_stats *stats) | ||
921 | { | ||
922 | struct ieee80211_network network_resp; | ||
923 | struct ieee80211_network *network = &network_resp; | ||
924 | struct ieee80211_info_element *info_element; | ||
925 | struct net_device *dev = ieee->dev; | ||
926 | u16 left; | ||
927 | |||
928 | network->flags = 0; | ||
929 | network->qos_data.active = 0; | ||
930 | network->qos_data.supported = 0; | ||
931 | network->qos_data.param_count = 0; | ||
932 | network->qos_data.old_param_count = 0; | ||
933 | |||
934 | //network->atim_window = le16_to_cpu(frame->aid) & (0x3FFF); | ||
935 | network->atim_window = le16_to_cpu(frame->aid); | ||
936 | network->listen_interval = le16_to_cpu(frame->status); | ||
937 | |||
938 | info_element = frame->info_element; | ||
939 | left = stats->len - sizeof(*frame); | ||
940 | |||
941 | while (left >= sizeof(struct ieee80211_info_element)) { | ||
942 | if (sizeof(struct ieee80211_info_element) + | ||
943 | info_element->len > left) { | ||
944 | IEEE80211_DEBUG_QOS("ASSOC RESP: parse failed: " | ||
945 | "info_element->len + 2 > left : " | ||
946 | "info_element->len+2=%zd left=%d, id=%d.\n", | ||
947 | info_element->len + | ||
948 | sizeof(struct | ||
949 | ieee80211_info_element), | ||
950 | left, info_element->id); | ||
951 | return 1; | ||
952 | } | ||
953 | |||
954 | switch (info_element->id) { | ||
955 | case MFIE_TYPE_SSID: | ||
956 | if (ieee80211_is_empty_essid(info_element->data, | ||
957 | info_element->len)) { | ||
958 | network->flags |= NETWORK_EMPTY_ESSID; | ||
959 | break; | ||
960 | } | ||
961 | |||
962 | network->ssid_len = min(info_element->len, | ||
963 | (u8) IW_ESSID_MAX_SIZE); | ||
964 | memcpy(network->ssid, info_element->data, | ||
965 | network->ssid_len); | ||
966 | if (network->ssid_len < IW_ESSID_MAX_SIZE) | ||
967 | memset(network->ssid + network->ssid_len, 0, | ||
968 | IW_ESSID_MAX_SIZE - network->ssid_len); | ||
969 | |||
970 | IEEE80211_DEBUG_QOS("MFIE_TYPE_SSID: '%s' len=%d.\n", | ||
971 | network->ssid, network->ssid_len); | ||
972 | break; | ||
973 | |||
974 | case MFIE_TYPE_TIM: | ||
975 | IEEE80211_DEBUG_QOS("MFIE_TYPE_TIM: ignored\n"); | ||
976 | break; | ||
977 | |||
978 | case MFIE_TYPE_IBSS_SET: | ||
979 | IEEE80211_DEBUG_QOS("MFIE_TYPE_IBSS_SET: ignored\n"); | ||
980 | break; | ||
981 | |||
982 | case MFIE_TYPE_CHALLENGE: | ||
983 | IEEE80211_DEBUG_QOS("MFIE_TYPE_CHALLENGE: ignored\n"); | ||
984 | break; | ||
985 | |||
986 | case MFIE_TYPE_GENERIC: | ||
987 | IEEE80211_DEBUG_QOS("MFIE_TYPE_GENERIC: %d bytes\n", | ||
988 | info_element->len); | ||
989 | ieee80211_parse_qos_info_param_IE(info_element, | ||
990 | network); | ||
991 | break; | ||
992 | |||
993 | case MFIE_TYPE_RSN: | ||
994 | IEEE80211_DEBUG_QOS("MFIE_TYPE_RSN: %d bytes\n", | ||
995 | info_element->len); | ||
996 | break; | ||
997 | |||
998 | case MFIE_TYPE_QOS_PARAMETER: | ||
999 | printk("QoS Error need to parse QOS_PARAMETER IE\n"); | ||
1000 | break; | ||
1001 | |||
1002 | default: | ||
1003 | IEEE80211_DEBUG_QOS("unsupported IE %d\n", | ||
1004 | info_element->id); | ||
1005 | break; | ||
1006 | } | ||
1007 | |||
1008 | left -= sizeof(struct ieee80211_info_element) + | ||
1009 | info_element->len; | ||
1010 | info_element = (struct ieee80211_info_element *) | ||
1011 | &info_element->data[info_element->len]; | ||
1012 | } | ||
1013 | |||
1014 | if (ieee->handle_assoc_response != NULL) | ||
1015 | ieee->handle_assoc_response(dev, frame, network); | ||
1016 | |||
1017 | return 0; | ||
1018 | } | ||
1019 | |||
1020 | /***************************************************/ | ||
1021 | |||
761 | static inline int ieee80211_is_ofdm_rate(u8 rate) | 1022 | static inline int ieee80211_is_ofdm_rate(u8 rate) |
762 | { | 1023 | { |
763 | switch (rate & ~IEEE80211_BASIC_RATE_MASK) { | 1024 | switch (rate & ~IEEE80211_BASIC_RATE_MASK) { |
@@ -786,6 +1047,9 @@ static inline int ieee80211_network_init(struct ieee80211_device *ieee, struct i | |||
786 | struct ieee80211_info_element *info_element; | 1047 | struct ieee80211_info_element *info_element; |
787 | u16 left; | 1048 | u16 left; |
788 | u8 i; | 1049 | u8 i; |
1050 | network->qos_data.active = 0; | ||
1051 | network->qos_data.supported = 0; | ||
1052 | network->qos_data.param_count = 0; | ||
789 | 1053 | ||
790 | /* Pull out fixed field data */ | 1054 | /* Pull out fixed field data */ |
791 | memcpy(network->bssid, beacon->header.addr3, ETH_ALEN); | 1055 | memcpy(network->bssid, beacon->header.addr3, ETH_ALEN); |
@@ -813,13 +1077,11 @@ static inline int ieee80211_network_init(struct ieee80211_device *ieee, struct i | |||
813 | 1077 | ||
814 | info_element = beacon->info_element; | 1078 | info_element = beacon->info_element; |
815 | left = stats->len - sizeof(*beacon); | 1079 | left = stats->len - sizeof(*beacon); |
816 | while (left >= sizeof(struct ieee80211_info_element)) { | 1080 | while (left >= sizeof(*info_element)) { |
817 | if (sizeof(struct ieee80211_info_element) + info_element->len > | 1081 | if (sizeof(*info_element) + info_element->len > left) { |
818 | left) { | ||
819 | IEEE80211_DEBUG_SCAN | 1082 | IEEE80211_DEBUG_SCAN |
820 | ("SCAN: parse failed: info_element->len + 2 > left : info_element->len+2=%Zd left=%d.\n", | 1083 | ("SCAN: parse failed: info_element->len + 2 > left : info_element->len+2=%Zd left=%d.\n", |
821 | info_element->len + | 1084 | info_element->len + sizeof(*info_element), left); |
822 | sizeof(struct ieee80211_info_element), left); | ||
823 | return 1; | 1085 | return 1; |
824 | } | 1086 | } |
825 | 1087 | ||
@@ -847,15 +1109,14 @@ static inline int ieee80211_network_init(struct ieee80211_device *ieee, struct i | |||
847 | #ifdef CONFIG_IEEE80211_DEBUG | 1109 | #ifdef CONFIG_IEEE80211_DEBUG |
848 | p = rates_str; | 1110 | p = rates_str; |
849 | #endif | 1111 | #endif |
850 | network->rates_len = | 1112 | network->rates_len = min(info_element->len, |
851 | min(info_element->len, MAX_RATES_LENGTH); | 1113 | MAX_RATES_LENGTH); |
852 | for (i = 0; i < network->rates_len; i++) { | 1114 | for (i = 0; i < network->rates_len; i++) { |
853 | network->rates[i] = info_element->data[i]; | 1115 | network->rates[i] = info_element->data[i]; |
854 | #ifdef CONFIG_IEEE80211_DEBUG | 1116 | #ifdef CONFIG_IEEE80211_DEBUG |
855 | p += snprintf(p, | 1117 | p += snprintf(p, sizeof(rates_str) - |
856 | sizeof(rates_str) - (p - | 1118 | (p - rates_str), "%02X ", |
857 | rates_str), | 1119 | network->rates[i]); |
858 | "%02X ", network->rates[i]); | ||
859 | #endif | 1120 | #endif |
860 | if (ieee80211_is_ofdm_rate | 1121 | if (ieee80211_is_ofdm_rate |
861 | (info_element->data[i])) { | 1122 | (info_element->data[i])) { |
@@ -875,15 +1136,14 @@ static inline int ieee80211_network_init(struct ieee80211_device *ieee, struct i | |||
875 | #ifdef CONFIG_IEEE80211_DEBUG | 1136 | #ifdef CONFIG_IEEE80211_DEBUG |
876 | p = rates_str; | 1137 | p = rates_str; |
877 | #endif | 1138 | #endif |
878 | network->rates_ex_len = | 1139 | network->rates_ex_len = min(info_element->len, |
879 | min(info_element->len, MAX_RATES_EX_LENGTH); | 1140 | MAX_RATES_EX_LENGTH); |
880 | for (i = 0; i < network->rates_ex_len; i++) { | 1141 | for (i = 0; i < network->rates_ex_len; i++) { |
881 | network->rates_ex[i] = info_element->data[i]; | 1142 | network->rates_ex[i] = info_element->data[i]; |
882 | #ifdef CONFIG_IEEE80211_DEBUG | 1143 | #ifdef CONFIG_IEEE80211_DEBUG |
883 | p += snprintf(p, | 1144 | p += snprintf(p, sizeof(rates_str) - |
884 | sizeof(rates_str) - (p - | 1145 | (p - rates_str), "%02X ", |
885 | rates_str), | 1146 | network->rates[i]); |
886 | "%02X ", network->rates[i]); | ||
887 | #endif | 1147 | #endif |
888 | if (ieee80211_is_ofdm_rate | 1148 | if (ieee80211_is_ofdm_rate |
889 | (info_element->data[i])) { | 1149 | (info_element->data[i])) { |
@@ -929,6 +1189,10 @@ static inline int ieee80211_network_init(struct ieee80211_device *ieee, struct i | |||
929 | case MFIE_TYPE_GENERIC: | 1189 | case MFIE_TYPE_GENERIC: |
930 | IEEE80211_DEBUG_SCAN("MFIE_TYPE_GENERIC: %d bytes\n", | 1190 | IEEE80211_DEBUG_SCAN("MFIE_TYPE_GENERIC: %d bytes\n", |
931 | info_element->len); | 1191 | info_element->len); |
1192 | if (!ieee80211_parse_qos_info_param_IE(info_element, | ||
1193 | network)) | ||
1194 | break; | ||
1195 | |||
932 | if (info_element->len >= 4 && | 1196 | if (info_element->len >= 4 && |
933 | info_element->data[0] == 0x00 && | 1197 | info_element->data[0] == 0x00 && |
934 | info_element->data[1] == 0x50 && | 1198 | info_element->data[1] == 0x50 && |
@@ -950,14 +1214,18 @@ static inline int ieee80211_network_init(struct ieee80211_device *ieee, struct i | |||
950 | network->rsn_ie_len); | 1214 | network->rsn_ie_len); |
951 | break; | 1215 | break; |
952 | 1216 | ||
1217 | case MFIE_TYPE_QOS_PARAMETER: | ||
1218 | printk(KERN_ERR | ||
1219 | "QoS Error need to parse QOS_PARAMETER IE\n"); | ||
1220 | break; | ||
1221 | |||
953 | default: | 1222 | default: |
954 | IEEE80211_DEBUG_SCAN("unsupported IE %d\n", | 1223 | IEEE80211_DEBUG_SCAN("unsupported IE %d\n", |
955 | info_element->id); | 1224 | info_element->id); |
956 | break; | 1225 | break; |
957 | } | 1226 | } |
958 | 1227 | ||
959 | left -= sizeof(struct ieee80211_info_element) + | 1228 | left -= sizeof(*info_element) + info_element->len; |
960 | info_element->len; | ||
961 | info_element = (struct ieee80211_info_element *) | 1229 | info_element = (struct ieee80211_info_element *) |
962 | &info_element->data[info_element->len]; | 1230 | &info_element->data[info_element->len]; |
963 | } | 1231 | } |
@@ -1004,6 +1272,9 @@ static inline int is_same_network(struct ieee80211_network *src, | |||
1004 | static inline void update_network(struct ieee80211_network *dst, | 1272 | static inline void update_network(struct ieee80211_network *dst, |
1005 | struct ieee80211_network *src) | 1273 | struct ieee80211_network *src) |
1006 | { | 1274 | { |
1275 | int qos_active; | ||
1276 | u8 old_param; | ||
1277 | |||
1007 | memcpy(&dst->stats, &src->stats, sizeof(struct ieee80211_rx_stats)); | 1278 | memcpy(&dst->stats, &src->stats, sizeof(struct ieee80211_rx_stats)); |
1008 | dst->capability = src->capability; | 1279 | dst->capability = src->capability; |
1009 | memcpy(dst->rates, src->rates, src->rates_len); | 1280 | memcpy(dst->rates, src->rates, src->rates_len); |
@@ -1026,6 +1297,28 @@ static inline void update_network(struct ieee80211_network *dst, | |||
1026 | dst->rsn_ie_len = src->rsn_ie_len; | 1297 | dst->rsn_ie_len = src->rsn_ie_len; |
1027 | 1298 | ||
1028 | dst->last_scanned = jiffies; | 1299 | dst->last_scanned = jiffies; |
1300 | qos_active = src->qos_data.active; | ||
1301 | old_param = dst->qos_data.old_param_count; | ||
1302 | if (dst->flags & NETWORK_HAS_QOS_MASK) | ||
1303 | memcpy(&dst->qos_data, &src->qos_data, | ||
1304 | sizeof(struct ieee80211_qos_data)); | ||
1305 | else { | ||
1306 | dst->qos_data.supported = src->qos_data.supported; | ||
1307 | dst->qos_data.param_count = src->qos_data.param_count; | ||
1308 | } | ||
1309 | |||
1310 | if (dst->qos_data.supported == 1) { | ||
1311 | if (dst->ssid_len) | ||
1312 | IEEE80211_DEBUG_QOS | ||
1313 | ("QoS the network %s is QoS supported\n", | ||
1314 | dst->ssid); | ||
1315 | else | ||
1316 | IEEE80211_DEBUG_QOS | ||
1317 | ("QoS the network is QoS supported\n"); | ||
1318 | } | ||
1319 | dst->qos_data.active = qos_active; | ||
1320 | dst->qos_data.old_param_count = old_param; | ||
1321 | |||
1029 | /* dst->last_associate is not overwritten */ | 1322 | /* dst->last_associate is not overwritten */ |
1030 | } | 1323 | } |
1031 | 1324 | ||
@@ -1167,6 +1460,9 @@ void ieee80211_rx_mgt(struct ieee80211_device *ieee, | |||
1167 | IEEE80211_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n", | 1460 | IEEE80211_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n", |
1168 | WLAN_FC_GET_STYPE(le16_to_cpu | 1461 | WLAN_FC_GET_STYPE(le16_to_cpu |
1169 | (header->frame_ctl))); | 1462 | (header->frame_ctl))); |
1463 | ieee80211_handle_assoc_resp(ieee, | ||
1464 | (struct ieee80211_assoc_response *) | ||
1465 | header, stats); | ||
1170 | break; | 1466 | break; |
1171 | 1467 | ||
1172 | case IEEE80211_STYPE_REASSOC_RESP: | 1468 | case IEEE80211_STYPE_REASSOC_RESP: |