| .. | .. |
|---|
| 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 | |
|---|