.. | .. |
---|
262 | 262 | } |
---|
263 | 263 | EXPORT_SYMBOL(cfg80211_is_element_inherited); |
---|
264 | 264 | |
---|
265 | | -static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen, |
---|
266 | | - const u8 *subelement, size_t subie_len, |
---|
267 | | - u8 *new_ie, gfp_t gfp) |
---|
| 265 | +static size_t cfg80211_copy_elem_with_frags(const struct element *elem, |
---|
| 266 | + const u8 *ie, size_t ie_len, |
---|
| 267 | + u8 **pos, u8 *buf, size_t buf_len) |
---|
268 | 268 | { |
---|
269 | | - u8 *pos, *tmp; |
---|
270 | | - const u8 *tmp_old, *tmp_new; |
---|
271 | | - const struct element *non_inherit_elem; |
---|
272 | | - u8 *sub_copy; |
---|
273 | | - |
---|
274 | | - /* copy subelement as we need to change its content to |
---|
275 | | - * mark an ie after it is processed. |
---|
276 | | - */ |
---|
277 | | - sub_copy = kmemdup(subelement, subie_len, gfp); |
---|
278 | | - if (!sub_copy) |
---|
| 269 | + if (WARN_ON((u8 *)elem < ie || elem->data > ie + ie_len || |
---|
| 270 | + elem->data + elem->datalen > ie + ie_len)) |
---|
279 | 271 | return 0; |
---|
280 | 272 | |
---|
281 | | - pos = &new_ie[0]; |
---|
| 273 | + if (elem->datalen + 2 > buf + buf_len - *pos) |
---|
| 274 | + return 0; |
---|
282 | 275 | |
---|
283 | | - /* set new ssid */ |
---|
284 | | - tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len); |
---|
285 | | - if (tmp_new) { |
---|
286 | | - memcpy(pos, tmp_new, tmp_new[1] + 2); |
---|
287 | | - pos += (tmp_new[1] + 2); |
---|
| 276 | + memcpy(*pos, elem, elem->datalen + 2); |
---|
| 277 | + *pos += elem->datalen + 2; |
---|
| 278 | + |
---|
| 279 | + /* Finish if it is not fragmented */ |
---|
| 280 | + if (elem->datalen != 255) |
---|
| 281 | + return *pos - buf; |
---|
| 282 | + |
---|
| 283 | + ie_len = ie + ie_len - elem->data - elem->datalen; |
---|
| 284 | + ie = (const u8 *)elem->data + elem->datalen; |
---|
| 285 | + |
---|
| 286 | + for_each_element(elem, ie, ie_len) { |
---|
| 287 | + if (elem->id != WLAN_EID_FRAGMENT) |
---|
| 288 | + break; |
---|
| 289 | + |
---|
| 290 | + if (elem->datalen + 2 > buf + buf_len - *pos) |
---|
| 291 | + return 0; |
---|
| 292 | + |
---|
| 293 | + memcpy(*pos, elem, elem->datalen + 2); |
---|
| 294 | + *pos += elem->datalen + 2; |
---|
| 295 | + |
---|
| 296 | + if (elem->datalen != 255) |
---|
| 297 | + break; |
---|
288 | 298 | } |
---|
289 | 299 | |
---|
290 | | - /* get non inheritance list if exists */ |
---|
291 | | - non_inherit_elem = |
---|
292 | | - cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, |
---|
293 | | - sub_copy, subie_len); |
---|
| 300 | + return *pos - buf; |
---|
| 301 | +} |
---|
294 | 302 | |
---|
295 | | - /* go through IEs in ie (skip SSID) and subelement, |
---|
296 | | - * merge them into new_ie |
---|
| 303 | +static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen, |
---|
| 304 | + const u8 *subie, size_t subie_len, |
---|
| 305 | + u8 *new_ie, size_t new_ie_len) |
---|
| 306 | +{ |
---|
| 307 | + const struct element *non_inherit_elem, *parent, *sub; |
---|
| 308 | + u8 *pos = new_ie; |
---|
| 309 | + u8 id, ext_id; |
---|
| 310 | + unsigned int match_len; |
---|
| 311 | + |
---|
| 312 | + non_inherit_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, |
---|
| 313 | + subie, subie_len); |
---|
| 314 | + |
---|
| 315 | + /* We copy the elements one by one from the parent to the generated |
---|
| 316 | + * elements. |
---|
| 317 | + * If they are not inherited (included in subie or in the non |
---|
| 318 | + * inheritance element), then we copy all occurrences the first time |
---|
| 319 | + * we see this element type. |
---|
297 | 320 | */ |
---|
298 | | - tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen); |
---|
299 | | - tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie; |
---|
| 321 | + for_each_element(parent, ie, ielen) { |
---|
| 322 | + if (parent->id == WLAN_EID_FRAGMENT) |
---|
| 323 | + continue; |
---|
300 | 324 | |
---|
301 | | - while (tmp_old + 2 - ie <= ielen && |
---|
302 | | - tmp_old + tmp_old[1] + 2 - ie <= ielen) { |
---|
303 | | - if (tmp_old[0] == 0) { |
---|
304 | | - tmp_old++; |
---|
| 325 | + if (parent->id == WLAN_EID_EXTENSION) { |
---|
| 326 | + if (parent->datalen < 1) |
---|
| 327 | + continue; |
---|
| 328 | + |
---|
| 329 | + id = WLAN_EID_EXTENSION; |
---|
| 330 | + ext_id = parent->data[0]; |
---|
| 331 | + match_len = 1; |
---|
| 332 | + } else { |
---|
| 333 | + id = parent->id; |
---|
| 334 | + match_len = 0; |
---|
| 335 | + } |
---|
| 336 | + |
---|
| 337 | + /* Find first occurrence in subie */ |
---|
| 338 | + sub = cfg80211_find_elem_match(id, subie, subie_len, |
---|
| 339 | + &ext_id, match_len, 0); |
---|
| 340 | + |
---|
| 341 | + /* Copy from parent if not in subie and inherited */ |
---|
| 342 | + if (!sub && |
---|
| 343 | + cfg80211_is_element_inherited(parent, non_inherit_elem)) { |
---|
| 344 | + if (!cfg80211_copy_elem_with_frags(parent, |
---|
| 345 | + ie, ielen, |
---|
| 346 | + &pos, new_ie, |
---|
| 347 | + new_ie_len)) |
---|
| 348 | + return 0; |
---|
| 349 | + |
---|
305 | 350 | continue; |
---|
306 | 351 | } |
---|
307 | 352 | |
---|
308 | | - if (tmp_old[0] == WLAN_EID_EXTENSION) |
---|
309 | | - tmp = (u8 *)cfg80211_find_ext_ie(tmp_old[2], sub_copy, |
---|
310 | | - subie_len); |
---|
311 | | - else |
---|
312 | | - tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy, |
---|
313 | | - subie_len); |
---|
| 353 | + /* Already copied if an earlier element had the same type */ |
---|
| 354 | + if (cfg80211_find_elem_match(id, ie, (u8 *)parent - ie, |
---|
| 355 | + &ext_id, match_len, 0)) |
---|
| 356 | + continue; |
---|
314 | 357 | |
---|
315 | | - if (!tmp) { |
---|
316 | | - const struct element *old_elem = (void *)tmp_old; |
---|
| 358 | + /* Not inheriting, copy all similar elements from subie */ |
---|
| 359 | + while (sub) { |
---|
| 360 | + if (!cfg80211_copy_elem_with_frags(sub, |
---|
| 361 | + subie, subie_len, |
---|
| 362 | + &pos, new_ie, |
---|
| 363 | + new_ie_len)) |
---|
| 364 | + return 0; |
---|
317 | 365 | |
---|
318 | | - /* ie in old ie but not in subelement */ |
---|
319 | | - if (cfg80211_is_element_inherited(old_elem, |
---|
320 | | - non_inherit_elem)) { |
---|
321 | | - memcpy(pos, tmp_old, tmp_old[1] + 2); |
---|
322 | | - pos += tmp_old[1] + 2; |
---|
323 | | - } |
---|
324 | | - } else { |
---|
325 | | - /* ie in transmitting ie also in subelement, |
---|
326 | | - * copy from subelement and flag the ie in subelement |
---|
327 | | - * as copied (by setting eid field to WLAN_EID_SSID, |
---|
328 | | - * which is skipped anyway). |
---|
329 | | - * For vendor ie, compare OUI + type + subType to |
---|
330 | | - * determine if they are the same ie. |
---|
331 | | - */ |
---|
332 | | - if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) { |
---|
333 | | - if (tmp_old[1] >= 5 && tmp[1] >= 5 && |
---|
334 | | - !memcmp(tmp_old + 2, tmp + 2, 5)) { |
---|
335 | | - /* same vendor ie, copy from |
---|
336 | | - * subelement |
---|
337 | | - */ |
---|
338 | | - memcpy(pos, tmp, tmp[1] + 2); |
---|
339 | | - pos += tmp[1] + 2; |
---|
340 | | - tmp[0] = WLAN_EID_SSID; |
---|
341 | | - } else { |
---|
342 | | - memcpy(pos, tmp_old, tmp_old[1] + 2); |
---|
343 | | - pos += tmp_old[1] + 2; |
---|
344 | | - } |
---|
345 | | - } else { |
---|
346 | | - /* copy ie from subelement into new ie */ |
---|
347 | | - memcpy(pos, tmp, tmp[1] + 2); |
---|
348 | | - pos += tmp[1] + 2; |
---|
349 | | - tmp[0] = WLAN_EID_SSID; |
---|
350 | | - } |
---|
| 366 | + sub = cfg80211_find_elem_match(id, |
---|
| 367 | + sub->data + sub->datalen, |
---|
| 368 | + subie_len + subie - |
---|
| 369 | + (sub->data + |
---|
| 370 | + sub->datalen), |
---|
| 371 | + &ext_id, match_len, 0); |
---|
351 | 372 | } |
---|
352 | | - |
---|
353 | | - if (tmp_old + tmp_old[1] + 2 - ie == ielen) |
---|
354 | | - break; |
---|
355 | | - |
---|
356 | | - tmp_old += tmp_old[1] + 2; |
---|
357 | 373 | } |
---|
358 | 374 | |
---|
359 | | - /* go through subelement again to check if there is any ie not |
---|
360 | | - * copied to new ie, skip ssid, capability, bssid-index ie |
---|
| 375 | + /* The above misses elements that are included in subie but not in the |
---|
| 376 | + * parent, so do a pass over subie and append those. |
---|
| 377 | + * Skip the non-tx BSSID caps and non-inheritance element. |
---|
361 | 378 | */ |
---|
362 | | - tmp_new = sub_copy; |
---|
363 | | - while (tmp_new + 2 - sub_copy <= subie_len && |
---|
364 | | - tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) { |
---|
365 | | - if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP || |
---|
366 | | - tmp_new[0] == WLAN_EID_SSID)) { |
---|
367 | | - memcpy(pos, tmp_new, tmp_new[1] + 2); |
---|
368 | | - pos += tmp_new[1] + 2; |
---|
| 379 | + for_each_element(sub, subie, subie_len) { |
---|
| 380 | + if (sub->id == WLAN_EID_NON_TX_BSSID_CAP) |
---|
| 381 | + continue; |
---|
| 382 | + |
---|
| 383 | + if (sub->id == WLAN_EID_FRAGMENT) |
---|
| 384 | + continue; |
---|
| 385 | + |
---|
| 386 | + if (sub->id == WLAN_EID_EXTENSION) { |
---|
| 387 | + if (sub->datalen < 1) |
---|
| 388 | + continue; |
---|
| 389 | + |
---|
| 390 | + id = WLAN_EID_EXTENSION; |
---|
| 391 | + ext_id = sub->data[0]; |
---|
| 392 | + match_len = 1; |
---|
| 393 | + |
---|
| 394 | + if (ext_id == WLAN_EID_EXT_NON_INHERITANCE) |
---|
| 395 | + continue; |
---|
| 396 | + } else { |
---|
| 397 | + id = sub->id; |
---|
| 398 | + match_len = 0; |
---|
369 | 399 | } |
---|
370 | | - if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len) |
---|
371 | | - break; |
---|
372 | | - tmp_new += tmp_new[1] + 2; |
---|
| 400 | + |
---|
| 401 | + /* Processed if one was included in the parent */ |
---|
| 402 | + if (cfg80211_find_elem_match(id, ie, ielen, |
---|
| 403 | + &ext_id, match_len, 0)) |
---|
| 404 | + continue; |
---|
| 405 | + |
---|
| 406 | + if (!cfg80211_copy_elem_with_frags(sub, subie, subie_len, |
---|
| 407 | + &pos, new_ie, new_ie_len)) |
---|
| 408 | + return 0; |
---|
373 | 409 | } |
---|
374 | 410 | |
---|
375 | | - kfree(sub_copy); |
---|
376 | 411 | return pos - new_ie; |
---|
377 | 412 | } |
---|
378 | 413 | |
---|
.. | .. |
---|
606 | 641 | |
---|
607 | 642 | ret = cfg80211_calc_short_ssid(ies, &ssid_elem, &s_ssid_tmp); |
---|
608 | 643 | if (ret) |
---|
609 | | - return ret; |
---|
| 644 | + return 0; |
---|
610 | 645 | |
---|
611 | 646 | /* RNR IE may contain more than one NEIGHBOR_AP_INFO */ |
---|
612 | 647 | while (pos + sizeof(*ap_info) <= end) { |
---|
.. | .. |
---|
2170 | 2205 | new_ie_len = cfg80211_gen_new_ie(ie, ielen, |
---|
2171 | 2206 | profile, |
---|
2172 | 2207 | profile_len, new_ie, |
---|
2173 | | - gfp); |
---|
| 2208 | + IEEE80211_MAX_DATA_LEN); |
---|
2174 | 2209 | if (!new_ie_len) |
---|
2175 | 2210 | continue; |
---|
2176 | 2211 | |
---|