diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-12-23 07:15:38 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-12-28 16:54:58 -0500 |
commit | 8e664fb3fd2b04e3ac5fad7f046000ba54e0e275 (patch) | |
tree | cf811ffed7d7643b72608a5eebd25baffd57fcb3 /net | |
parent | 77c8144ad3ee7fae834e13cb7e83f5b7c8c5329e (diff) |
mac80211: split up and insert custom IEs correctly
Currently, we insert all user-specified IEs before the HT
IE for association, and after the HT IE for probe requests.
For association, that's correct only if the user-specified
IEs are RSN only, incorrect in all other cases including
WPA. Change this to split apart the user-specified IEs in
two places for association: before the HT IE (e.g. RSN),
after the HT IE (generally empty right now I think?) and
after WMM (all other vendor-specific IEs). For probes,
split the IEs in different places to be correct according
to the spec.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/ieee80211_i.h | 4 | ||||
-rw-r--r-- | net/mac80211/util.c | 134 | ||||
-rw-r--r-- | net/mac80211/work.c | 43 |
3 files changed, 154 insertions, 27 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 97b6076b492e..6ea4ffbf84d8 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -1148,6 +1148,10 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | |||
1148 | void ieee80211_recalc_smps(struct ieee80211_local *local, | 1148 | void ieee80211_recalc_smps(struct ieee80211_local *local, |
1149 | struct ieee80211_sub_if_data *forsdata); | 1149 | struct ieee80211_sub_if_data *forsdata); |
1150 | 1150 | ||
1151 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | ||
1152 | const u8 *ids, int n_ids, size_t offset); | ||
1153 | size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); | ||
1154 | |||
1151 | /* internal work items */ | 1155 | /* internal work items */ |
1152 | void ieee80211_work_init(struct ieee80211_local *local); | 1156 | void ieee80211_work_init(struct ieee80211_local *local); |
1153 | void ieee80211_add_work(struct ieee80211_work *wk); | 1157 | void ieee80211_add_work(struct ieee80211_work *wk); |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 5ffe9e831b66..1fdb80ff9241 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -881,30 +881,66 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
881 | enum ieee80211_band band) | 881 | enum ieee80211_band band) |
882 | { | 882 | { |
883 | struct ieee80211_supported_band *sband; | 883 | struct ieee80211_supported_band *sband; |
884 | u8 *pos, *supp_rates_len, *esupp_rates_len = NULL; | 884 | u8 *pos; |
885 | int i; | 885 | size_t offset = 0, noffset; |
886 | int supp_rates_len, i; | ||
886 | 887 | ||
887 | sband = local->hw.wiphy->bands[band]; | 888 | sband = local->hw.wiphy->bands[band]; |
888 | 889 | ||
889 | pos = buffer; | 890 | pos = buffer; |
890 | 891 | ||
892 | supp_rates_len = min_t(int, sband->n_bitrates, 8); | ||
893 | |||
891 | *pos++ = WLAN_EID_SUPP_RATES; | 894 | *pos++ = WLAN_EID_SUPP_RATES; |
892 | supp_rates_len = pos; | 895 | *pos++ = supp_rates_len; |
893 | *pos++ = 0; | ||
894 | |||
895 | for (i = 0; i < sband->n_bitrates; i++) { | ||
896 | struct ieee80211_rate *rate = &sband->bitrates[i]; | ||
897 | |||
898 | if (esupp_rates_len) { | ||
899 | *esupp_rates_len += 1; | ||
900 | } else if (*supp_rates_len == 8) { | ||
901 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | ||
902 | esupp_rates_len = pos; | ||
903 | *pos++ = 1; | ||
904 | } else | ||
905 | *supp_rates_len += 1; | ||
906 | 896 | ||
907 | *pos++ = rate->bitrate / 5; | 897 | for (i = 0; i < supp_rates_len; i++) { |
898 | int rate = sband->bitrates[i].bitrate; | ||
899 | *pos++ = (u8) (rate / 5); | ||
900 | } | ||
901 | |||
902 | /* insert "request information" if in custom IEs */ | ||
903 | if (ie && ie_len) { | ||
904 | static const u8 before_extrates[] = { | ||
905 | WLAN_EID_SSID, | ||
906 | WLAN_EID_SUPP_RATES, | ||
907 | WLAN_EID_REQUEST, | ||
908 | }; | ||
909 | noffset = ieee80211_ie_split(ie, ie_len, | ||
910 | before_extrates, | ||
911 | ARRAY_SIZE(before_extrates), | ||
912 | offset); | ||
913 | memcpy(pos, ie + offset, noffset - offset); | ||
914 | pos += noffset - offset; | ||
915 | offset = noffset; | ||
916 | } | ||
917 | |||
918 | if (sband->n_bitrates > i) { | ||
919 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | ||
920 | *pos++ = sband->n_bitrates - i; | ||
921 | |||
922 | for (; i < sband->n_bitrates; i++) { | ||
923 | int rate = sband->bitrates[i].bitrate; | ||
924 | *pos++ = (u8) (rate / 5); | ||
925 | } | ||
926 | } | ||
927 | |||
928 | /* insert custom IEs that go before HT */ | ||
929 | if (ie && ie_len) { | ||
930 | static const u8 before_ht[] = { | ||
931 | WLAN_EID_SSID, | ||
932 | WLAN_EID_SUPP_RATES, | ||
933 | WLAN_EID_REQUEST, | ||
934 | WLAN_EID_EXT_SUPP_RATES, | ||
935 | WLAN_EID_DS_PARAMS, | ||
936 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, | ||
937 | }; | ||
938 | noffset = ieee80211_ie_split(ie, ie_len, | ||
939 | before_ht, ARRAY_SIZE(before_ht), | ||
940 | offset); | ||
941 | memcpy(pos, ie + offset, noffset - offset); | ||
942 | pos += noffset - offset; | ||
943 | offset = noffset; | ||
908 | } | 944 | } |
909 | 945 | ||
910 | if (sband->ht_cap.ht_supported) { | 946 | if (sband->ht_cap.ht_supported) { |
@@ -936,9 +972,11 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
936 | * that calculates local->scan_ies_len. | 972 | * that calculates local->scan_ies_len. |
937 | */ | 973 | */ |
938 | 974 | ||
939 | if (ie) { | 975 | /* add any remaining custom IEs */ |
940 | memcpy(pos, ie, ie_len); | 976 | if (ie && ie_len) { |
941 | pos += ie_len; | 977 | noffset = ie_len; |
978 | memcpy(pos, ie + offset, noffset - offset); | ||
979 | pos += noffset - offset; | ||
942 | } | 980 | } |
943 | 981 | ||
944 | return pos - buffer; | 982 | return pos - buffer; |
@@ -1252,3 +1290,59 @@ void ieee80211_recalc_smps(struct ieee80211_local *local, | |||
1252 | /* changed flag is auto-detected for this */ | 1290 | /* changed flag is auto-detected for this */ |
1253 | ieee80211_hw_config(local, 0); | 1291 | ieee80211_hw_config(local, 0); |
1254 | } | 1292 | } |
1293 | |||
1294 | static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) | ||
1295 | { | ||
1296 | int i; | ||
1297 | |||
1298 | for (i = 0; i < n_ids; i++) | ||
1299 | if (ids[i] == id) | ||
1300 | return true; | ||
1301 | return false; | ||
1302 | } | ||
1303 | |||
1304 | /** | ||
1305 | * ieee80211_ie_split - split an IE buffer according to ordering | ||
1306 | * | ||
1307 | * @ies: the IE buffer | ||
1308 | * @ielen: the length of the IE buffer | ||
1309 | * @ids: an array with element IDs that are allowed before | ||
1310 | * the split | ||
1311 | * @n_ids: the size of the element ID array | ||
1312 | * @offset: offset where to start splitting in the buffer | ||
1313 | * | ||
1314 | * This function splits an IE buffer by updating the @offset | ||
1315 | * variable to point to the location where the buffer should be | ||
1316 | * split. | ||
1317 | * | ||
1318 | * It assumes that the given IE buffer is well-formed, this | ||
1319 | * has to be guaranteed by the caller! | ||
1320 | * | ||
1321 | * It also assumes that the IEs in the buffer are ordered | ||
1322 | * correctly, if not the result of using this function will not | ||
1323 | * be ordered correctly either, i.e. it does no reordering. | ||
1324 | * | ||
1325 | * The function returns the offset where the next part of the | ||
1326 | * buffer starts, which may be @ielen if the entire (remainder) | ||
1327 | * of the buffer should be used. | ||
1328 | */ | ||
1329 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | ||
1330 | const u8 *ids, int n_ids, size_t offset) | ||
1331 | { | ||
1332 | size_t pos = offset; | ||
1333 | |||
1334 | while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) | ||
1335 | pos += 2 + ies[pos + 1]; | ||
1336 | |||
1337 | return pos; | ||
1338 | } | ||
1339 | |||
1340 | size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) | ||
1341 | { | ||
1342 | size_t pos = offset; | ||
1343 | |||
1344 | while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC) | ||
1345 | pos += 2 + ies[pos + 1]; | ||
1346 | |||
1347 | return pos; | ||
1348 | } | ||
diff --git a/net/mac80211/work.c b/net/mac80211/work.c index c03c22d5bca3..affdd10b67ad 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c | |||
@@ -204,6 +204,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
204 | struct ieee80211_mgmt *mgmt; | 204 | struct ieee80211_mgmt *mgmt; |
205 | u8 *pos; | 205 | u8 *pos; |
206 | const u8 *ies; | 206 | const u8 *ies; |
207 | size_t offset = 0, noffset; | ||
207 | int i, len, count, rates_len, supp_rates_len; | 208 | int i, len, count, rates_len, supp_rates_len; |
208 | u16 capab; | 209 | u16 capab; |
209 | struct ieee80211_supported_band *sband; | 210 | struct ieee80211_supported_band *sband; |
@@ -337,14 +338,26 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
337 | } | 338 | } |
338 | } | 339 | } |
339 | 340 | ||
340 | /* | 341 | /* if present, add any custom IEs that go before HT */ |
341 | * XXX: These IEs could contain (vendor-specified) | ||
342 | * IEs that belong after HT -- the buffer may | ||
343 | * need to be split up. | ||
344 | */ | ||
345 | if (wk->ie_len && wk->ie) { | 342 | if (wk->ie_len && wk->ie) { |
346 | pos = skb_put(skb, wk->ie_len); | 343 | static const u8 before_ht[] = { |
347 | memcpy(pos, wk->ie, wk->ie_len); | 344 | WLAN_EID_SSID, |
345 | WLAN_EID_SUPP_RATES, | ||
346 | WLAN_EID_EXT_SUPP_RATES, | ||
347 | WLAN_EID_PWR_CAPABILITY, | ||
348 | WLAN_EID_SUPPORTED_CHANNELS, | ||
349 | WLAN_EID_RSN, | ||
350 | WLAN_EID_QOS_CAPA, | ||
351 | WLAN_EID_RRM_ENABLED_CAPABILITIES, | ||
352 | WLAN_EID_MOBILITY_DOMAIN, | ||
353 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, | ||
354 | }; | ||
355 | noffset = ieee80211_ie_split(wk->ie, wk->ie_len, | ||
356 | before_ht, ARRAY_SIZE(before_ht), | ||
357 | offset); | ||
358 | pos = skb_put(skb, noffset - offset); | ||
359 | memcpy(pos, wk->ie + offset, noffset - offset); | ||
360 | offset = noffset; | ||
348 | } | 361 | } |
349 | 362 | ||
350 | if (wk->assoc.use_11n && wk->assoc.wmm_used && | 363 | if (wk->assoc.use_11n && wk->assoc.wmm_used && |
@@ -352,6 +365,15 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
352 | ieee80211_add_ht_ie(skb, wk->assoc.ht_information_ie, | 365 | ieee80211_add_ht_ie(skb, wk->assoc.ht_information_ie, |
353 | sband, wk->chan, wk->assoc.smps); | 366 | sband, wk->chan, wk->assoc.smps); |
354 | 367 | ||
368 | /* if present, add any custom non-vendor IEs that go after HT */ | ||
369 | if (wk->ie_len && wk->ie) { | ||
370 | noffset = ieee80211_ie_split_vendor(wk->ie, wk->ie_len, | ||
371 | offset); | ||
372 | pos = skb_put(skb, noffset - offset); | ||
373 | memcpy(pos, wk->ie + offset, noffset - offset); | ||
374 | offset = noffset; | ||
375 | } | ||
376 | |||
355 | if (wk->assoc.wmm_used && local->hw.queues >= 4) { | 377 | if (wk->assoc.wmm_used && local->hw.queues >= 4) { |
356 | pos = skb_put(skb, 9); | 378 | pos = skb_put(skb, 9); |
357 | *pos++ = WLAN_EID_VENDOR_SPECIFIC; | 379 | *pos++ = WLAN_EID_VENDOR_SPECIFIC; |
@@ -365,6 +387,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
365 | *pos++ = 0; | 387 | *pos++ = 0; |
366 | } | 388 | } |
367 | 389 | ||
390 | /* add any remaining custom (i.e. vendor specific here) IEs */ | ||
391 | if (wk->ie_len && wk->ie) { | ||
392 | noffset = wk->ie_len; | ||
393 | pos = skb_put(skb, noffset - offset); | ||
394 | memcpy(pos, wk->ie + offset, noffset - offset); | ||
395 | } | ||
396 | |||
368 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | 397 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; |
369 | ieee80211_tx_skb(sdata, skb); | 398 | ieee80211_tx_skb(sdata, skb); |
370 | } | 399 | } |