.. | .. |
---|
4 | 4 | * |
---|
5 | 5 | * Copyright (c) 2009, Jouni Malinen <j@w1.fi> |
---|
6 | 6 | * Copyright (c) 2015 Intel Deutschland GmbH |
---|
| 7 | + * Copyright (C) 2019 Intel Corporation |
---|
7 | 8 | */ |
---|
8 | 9 | |
---|
9 | 10 | #include <linux/kernel.h> |
---|
.. | .. |
---|
21 | 22 | |
---|
22 | 23 | |
---|
23 | 24 | void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, |
---|
24 | | - const u8 *buf, size_t len, int uapsd_queues) |
---|
| 25 | + const u8 *buf, size_t len, int uapsd_queues, |
---|
| 26 | + const u8 *req_ies, size_t req_ies_len) |
---|
25 | 27 | { |
---|
26 | 28 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
---|
27 | 29 | struct wiphy *wiphy = wdev->wiphy; |
---|
28 | 30 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
---|
29 | 31 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
---|
30 | 32 | struct cfg80211_connect_resp_params cr; |
---|
| 33 | + const u8 *resp_ie = mgmt->u.assoc_resp.variable; |
---|
| 34 | + size_t resp_ie_len = len - offsetof(struct ieee80211_mgmt, |
---|
| 35 | + u.assoc_resp.variable); |
---|
| 36 | + |
---|
| 37 | + if (bss->channel->band == NL80211_BAND_S1GHZ) { |
---|
| 38 | + resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable; |
---|
| 39 | + resp_ie_len = len - offsetof(struct ieee80211_mgmt, |
---|
| 40 | + u.s1g_assoc_resp.variable); |
---|
| 41 | + } |
---|
31 | 42 | |
---|
32 | 43 | memset(&cr, 0, sizeof(cr)); |
---|
33 | 44 | cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code); |
---|
34 | 45 | cr.bssid = mgmt->bssid; |
---|
35 | 46 | cr.bss = bss; |
---|
36 | | - cr.resp_ie = mgmt->u.assoc_resp.variable; |
---|
37 | | - cr.resp_ie_len = |
---|
38 | | - len - offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); |
---|
| 47 | + cr.req_ie = req_ies; |
---|
| 48 | + cr.req_ie_len = req_ies_len; |
---|
| 49 | + cr.resp_ie = resp_ie; |
---|
| 50 | + cr.resp_ie_len = resp_ie_len; |
---|
39 | 51 | cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; |
---|
40 | 52 | |
---|
41 | 53 | trace_cfg80211_send_rx_assoc(dev, bss); |
---|
.. | .. |
---|
52 | 64 | return; |
---|
53 | 65 | } |
---|
54 | 66 | |
---|
55 | | - nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues); |
---|
| 67 | + nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues, |
---|
| 68 | + req_ies, req_ies_len); |
---|
56 | 69 | /* update current_bss etc., consumes the bss reference */ |
---|
57 | 70 | __cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS); |
---|
58 | 71 | } |
---|
.. | .. |
---|
272 | 285 | |
---|
273 | 286 | p1 = (u8*)(ht_capa); |
---|
274 | 287 | p2 = (u8*)(ht_capa_mask); |
---|
275 | | - for (i = 0; i<sizeof(*ht_capa); i++) |
---|
| 288 | + for (i = 0; i < sizeof(*ht_capa); i++) |
---|
276 | 289 | p1[i] &= p2[i]; |
---|
277 | 290 | } |
---|
278 | 291 | |
---|
279 | | -/* Do a logical ht_capa &= ht_capa_mask. */ |
---|
| 292 | +/* Do a logical vht_capa &= vht_capa_mask. */ |
---|
280 | 293 | void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, |
---|
281 | 294 | const struct ieee80211_vht_cap *vht_capa_mask) |
---|
282 | 295 | { |
---|
.. | .. |
---|
421 | 434 | |
---|
422 | 435 | __le16 frame_type; |
---|
423 | 436 | |
---|
| 437 | + bool multicast_rx; |
---|
| 438 | + |
---|
424 | 439 | u8 match[]; |
---|
425 | 440 | }; |
---|
426 | 441 | |
---|
427 | | -static void |
---|
428 | | -cfg80211_process_mlme_unregistrations(struct cfg80211_registered_device *rdev) |
---|
| 442 | +static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev) |
---|
429 | 443 | { |
---|
| 444 | + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); |
---|
| 445 | + struct wireless_dev *tmp; |
---|
430 | 446 | struct cfg80211_mgmt_registration *reg; |
---|
| 447 | + struct mgmt_frame_regs upd = {}; |
---|
431 | 448 | |
---|
432 | 449 | ASSERT_RTNL(); |
---|
433 | 450 | |
---|
434 | | - spin_lock_bh(&rdev->mlme_unreg_lock); |
---|
435 | | - while ((reg = list_first_entry_or_null(&rdev->mlme_unreg, |
---|
436 | | - struct cfg80211_mgmt_registration, |
---|
437 | | - list))) { |
---|
438 | | - list_del(®->list); |
---|
439 | | - spin_unlock_bh(&rdev->mlme_unreg_lock); |
---|
440 | | - |
---|
441 | | - if (rdev->ops->mgmt_frame_register) { |
---|
442 | | - u16 frame_type = le16_to_cpu(reg->frame_type); |
---|
443 | | - |
---|
444 | | - rdev_mgmt_frame_register(rdev, reg->wdev, |
---|
445 | | - frame_type, false); |
---|
446 | | - } |
---|
447 | | - |
---|
448 | | - kfree(reg); |
---|
449 | | - |
---|
450 | | - spin_lock_bh(&rdev->mlme_unreg_lock); |
---|
| 451 | + spin_lock_bh(&rdev->mgmt_registrations_lock); |
---|
| 452 | + if (!wdev->mgmt_registrations_need_update) { |
---|
| 453 | + spin_unlock_bh(&rdev->mgmt_registrations_lock); |
---|
| 454 | + return; |
---|
451 | 455 | } |
---|
452 | | - spin_unlock_bh(&rdev->mlme_unreg_lock); |
---|
| 456 | + |
---|
| 457 | + rcu_read_lock(); |
---|
| 458 | + list_for_each_entry_rcu(tmp, &rdev->wiphy.wdev_list, list) { |
---|
| 459 | + list_for_each_entry(reg, &tmp->mgmt_registrations, list) { |
---|
| 460 | + u32 mask = BIT(le16_to_cpu(reg->frame_type) >> 4); |
---|
| 461 | + u32 mcast_mask = 0; |
---|
| 462 | + |
---|
| 463 | + if (reg->multicast_rx) |
---|
| 464 | + mcast_mask = mask; |
---|
| 465 | + |
---|
| 466 | + upd.global_stypes |= mask; |
---|
| 467 | + upd.global_mcast_stypes |= mcast_mask; |
---|
| 468 | + |
---|
| 469 | + if (tmp == wdev) { |
---|
| 470 | + upd.interface_stypes |= mask; |
---|
| 471 | + upd.interface_mcast_stypes |= mcast_mask; |
---|
| 472 | + } |
---|
| 473 | + } |
---|
| 474 | + } |
---|
| 475 | + rcu_read_unlock(); |
---|
| 476 | + |
---|
| 477 | + wdev->mgmt_registrations_need_update = 0; |
---|
| 478 | + spin_unlock_bh(&rdev->mgmt_registrations_lock); |
---|
| 479 | + |
---|
| 480 | + rdev_update_mgmt_frame_registrations(rdev, wdev, &upd); |
---|
453 | 481 | } |
---|
454 | 482 | |
---|
455 | | -void cfg80211_mlme_unreg_wk(struct work_struct *wk) |
---|
| 483 | +void cfg80211_mgmt_registrations_update_wk(struct work_struct *wk) |
---|
456 | 484 | { |
---|
457 | 485 | struct cfg80211_registered_device *rdev; |
---|
| 486 | + struct wireless_dev *wdev; |
---|
458 | 487 | |
---|
459 | 488 | rdev = container_of(wk, struct cfg80211_registered_device, |
---|
460 | | - mlme_unreg_wk); |
---|
| 489 | + mgmt_registrations_update_wk); |
---|
461 | 490 | |
---|
462 | 491 | rtnl_lock(); |
---|
463 | | - cfg80211_process_mlme_unregistrations(rdev); |
---|
| 492 | + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) |
---|
| 493 | + cfg80211_mgmt_registrations_update(wdev); |
---|
464 | 494 | rtnl_unlock(); |
---|
465 | 495 | } |
---|
466 | 496 | |
---|
467 | 497 | int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, |
---|
468 | 498 | u16 frame_type, const u8 *match_data, |
---|
469 | | - int match_len) |
---|
| 499 | + int match_len, bool multicast_rx, |
---|
| 500 | + struct netlink_ext_ack *extack) |
---|
470 | 501 | { |
---|
471 | | - struct wiphy *wiphy = wdev->wiphy; |
---|
472 | | - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
---|
| 502 | + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); |
---|
473 | 503 | struct cfg80211_mgmt_registration *reg, *nreg; |
---|
474 | 504 | int err = 0; |
---|
475 | 505 | u16 mgmt_type; |
---|
| 506 | + bool update_multicast = false; |
---|
476 | 507 | |
---|
477 | 508 | if (!wdev->wiphy->mgmt_stypes) |
---|
478 | 509 | return -EOPNOTSUPP; |
---|
479 | 510 | |
---|
480 | | - if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT) |
---|
| 511 | + if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT) { |
---|
| 512 | + NL_SET_ERR_MSG(extack, "frame type not management"); |
---|
481 | 513 | return -EINVAL; |
---|
| 514 | + } |
---|
482 | 515 | |
---|
483 | | - if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) |
---|
| 516 | + if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) { |
---|
| 517 | + NL_SET_ERR_MSG(extack, "Invalid frame type"); |
---|
484 | 518 | return -EINVAL; |
---|
| 519 | + } |
---|
485 | 520 | |
---|
486 | 521 | mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; |
---|
487 | | - if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type))) |
---|
| 522 | + if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type))) { |
---|
| 523 | + NL_SET_ERR_MSG(extack, |
---|
| 524 | + "Registration to specific type not supported"); |
---|
488 | 525 | return -EINVAL; |
---|
| 526 | + } |
---|
| 527 | + |
---|
| 528 | + /* |
---|
| 529 | + * To support Pre Association Security Negotiation (PASN), registration |
---|
| 530 | + * for authentication frames should be supported. However, as some |
---|
| 531 | + * versions of the user space daemons wrongly register to all types of |
---|
| 532 | + * authentication frames (which might result in unexpected behavior) |
---|
| 533 | + * allow such registration if the request is for a specific |
---|
| 534 | + * authentication algorithm number. |
---|
| 535 | + */ |
---|
| 536 | + if (wdev->iftype == NL80211_IFTYPE_STATION && |
---|
| 537 | + (frame_type & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_AUTH && |
---|
| 538 | + !(match_data && match_len >= 2)) { |
---|
| 539 | + NL_SET_ERR_MSG(extack, |
---|
| 540 | + "Authentication algorithm number required"); |
---|
| 541 | + return -EINVAL; |
---|
| 542 | + } |
---|
489 | 543 | |
---|
490 | 544 | nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); |
---|
491 | 545 | if (!nreg) |
---|
492 | 546 | return -ENOMEM; |
---|
493 | 547 | |
---|
494 | | - spin_lock_bh(&wdev->mgmt_registrations_lock); |
---|
| 548 | + spin_lock_bh(&rdev->mgmt_registrations_lock); |
---|
495 | 549 | |
---|
496 | 550 | list_for_each_entry(reg, &wdev->mgmt_registrations, list) { |
---|
497 | 551 | int mlen = min(match_len, reg->match_len); |
---|
.. | .. |
---|
500 | 554 | continue; |
---|
501 | 555 | |
---|
502 | 556 | if (memcmp(reg->match, match_data, mlen) == 0) { |
---|
| 557 | + if (reg->multicast_rx != multicast_rx) { |
---|
| 558 | + update_multicast = true; |
---|
| 559 | + reg->multicast_rx = multicast_rx; |
---|
| 560 | + break; |
---|
| 561 | + } |
---|
| 562 | + NL_SET_ERR_MSG(extack, "Match already configured"); |
---|
503 | 563 | err = -EALREADY; |
---|
504 | 564 | break; |
---|
505 | 565 | } |
---|
506 | 566 | } |
---|
507 | 567 | |
---|
508 | | - if (err) { |
---|
509 | | - kfree(nreg); |
---|
| 568 | + if (err) |
---|
510 | 569 | goto out; |
---|
| 570 | + |
---|
| 571 | + if (update_multicast) { |
---|
| 572 | + kfree(nreg); |
---|
| 573 | + } else { |
---|
| 574 | + memcpy(nreg->match, match_data, match_len); |
---|
| 575 | + nreg->match_len = match_len; |
---|
| 576 | + nreg->nlportid = snd_portid; |
---|
| 577 | + nreg->frame_type = cpu_to_le16(frame_type); |
---|
| 578 | + nreg->wdev = wdev; |
---|
| 579 | + nreg->multicast_rx = multicast_rx; |
---|
| 580 | + list_add(&nreg->list, &wdev->mgmt_registrations); |
---|
511 | 581 | } |
---|
| 582 | + wdev->mgmt_registrations_need_update = 1; |
---|
| 583 | + spin_unlock_bh(&rdev->mgmt_registrations_lock); |
---|
512 | 584 | |
---|
513 | | - memcpy(nreg->match, match_data, match_len); |
---|
514 | | - nreg->match_len = match_len; |
---|
515 | | - nreg->nlportid = snd_portid; |
---|
516 | | - nreg->frame_type = cpu_to_le16(frame_type); |
---|
517 | | - nreg->wdev = wdev; |
---|
518 | | - list_add(&nreg->list, &wdev->mgmt_registrations); |
---|
519 | | - spin_unlock_bh(&wdev->mgmt_registrations_lock); |
---|
520 | | - |
---|
521 | | - /* process all unregistrations to avoid driver confusion */ |
---|
522 | | - cfg80211_process_mlme_unregistrations(rdev); |
---|
523 | | - |
---|
524 | | - if (rdev->ops->mgmt_frame_register) |
---|
525 | | - rdev_mgmt_frame_register(rdev, wdev, frame_type, true); |
---|
| 585 | + cfg80211_mgmt_registrations_update(wdev); |
---|
526 | 586 | |
---|
527 | 587 | return 0; |
---|
528 | 588 | |
---|
529 | 589 | out: |
---|
530 | | - spin_unlock_bh(&wdev->mgmt_registrations_lock); |
---|
| 590 | + kfree(nreg); |
---|
| 591 | + spin_unlock_bh(&rdev->mgmt_registrations_lock); |
---|
531 | 592 | |
---|
532 | 593 | return err; |
---|
533 | 594 | } |
---|
.. | .. |
---|
538 | 599 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
---|
539 | 600 | struct cfg80211_mgmt_registration *reg, *tmp; |
---|
540 | 601 | |
---|
541 | | - spin_lock_bh(&wdev->mgmt_registrations_lock); |
---|
| 602 | + spin_lock_bh(&rdev->mgmt_registrations_lock); |
---|
542 | 603 | |
---|
543 | 604 | list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { |
---|
544 | 605 | if (reg->nlportid != nlportid) |
---|
545 | 606 | continue; |
---|
546 | 607 | |
---|
547 | 608 | list_del(®->list); |
---|
548 | | - spin_lock(&rdev->mlme_unreg_lock); |
---|
549 | | - list_add_tail(®->list, &rdev->mlme_unreg); |
---|
550 | | - spin_unlock(&rdev->mlme_unreg_lock); |
---|
| 609 | + kfree(reg); |
---|
551 | 610 | |
---|
552 | | - schedule_work(&rdev->mlme_unreg_wk); |
---|
| 611 | + wdev->mgmt_registrations_need_update = 1; |
---|
| 612 | + schedule_work(&rdev->mgmt_registrations_update_wk); |
---|
553 | 613 | } |
---|
554 | 614 | |
---|
555 | | - spin_unlock_bh(&wdev->mgmt_registrations_lock); |
---|
| 615 | + spin_unlock_bh(&rdev->mgmt_registrations_lock); |
---|
556 | 616 | |
---|
557 | 617 | if (nlportid && rdev->crit_proto_nlportid == nlportid) { |
---|
558 | 618 | rdev->crit_proto_nlportid = 0; |
---|
.. | .. |
---|
566 | 626 | void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) |
---|
567 | 627 | { |
---|
568 | 628 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); |
---|
| 629 | + struct cfg80211_mgmt_registration *reg, *tmp; |
---|
569 | 630 | |
---|
570 | | - spin_lock_bh(&wdev->mgmt_registrations_lock); |
---|
571 | | - spin_lock(&rdev->mlme_unreg_lock); |
---|
572 | | - list_splice_tail_init(&wdev->mgmt_registrations, &rdev->mlme_unreg); |
---|
573 | | - spin_unlock(&rdev->mlme_unreg_lock); |
---|
574 | | - spin_unlock_bh(&wdev->mgmt_registrations_lock); |
---|
| 631 | + spin_lock_bh(&rdev->mgmt_registrations_lock); |
---|
| 632 | + list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { |
---|
| 633 | + list_del(®->list); |
---|
| 634 | + kfree(reg); |
---|
| 635 | + } |
---|
| 636 | + wdev->mgmt_registrations_need_update = 1; |
---|
| 637 | + spin_unlock_bh(&rdev->mgmt_registrations_lock); |
---|
575 | 638 | |
---|
576 | | - cfg80211_process_mlme_unregistrations(rdev); |
---|
| 639 | + cfg80211_mgmt_registrations_update(wdev); |
---|
577 | 640 | } |
---|
578 | 641 | |
---|
579 | 642 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, |
---|
.. | .. |
---|
692 | 755 | return rdev_mgmt_tx(rdev, wdev, params, cookie); |
---|
693 | 756 | } |
---|
694 | 757 | |
---|
695 | | -bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_dbm, |
---|
696 | | - const u8 *buf, size_t len, u32 flags) |
---|
| 758 | +bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm, |
---|
| 759 | + const u8 *buf, size_t len, u32 flags) |
---|
697 | 760 | { |
---|
698 | 761 | struct wiphy *wiphy = wdev->wiphy; |
---|
699 | 762 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
---|
.. | .. |
---|
719 | 782 | data = buf + ieee80211_hdrlen(mgmt->frame_control); |
---|
720 | 783 | data_len = len - ieee80211_hdrlen(mgmt->frame_control); |
---|
721 | 784 | |
---|
722 | | - spin_lock_bh(&wdev->mgmt_registrations_lock); |
---|
| 785 | + spin_lock_bh(&rdev->mgmt_registrations_lock); |
---|
723 | 786 | |
---|
724 | 787 | list_for_each_entry(reg, &wdev->mgmt_registrations, list) { |
---|
725 | 788 | if (reg->frame_type != ftype) |
---|
.. | .. |
---|
743 | 806 | break; |
---|
744 | 807 | } |
---|
745 | 808 | |
---|
746 | | - spin_unlock_bh(&wdev->mgmt_registrations_lock); |
---|
| 809 | + spin_unlock_bh(&rdev->mgmt_registrations_lock); |
---|
747 | 810 | |
---|
748 | 811 | trace_cfg80211_return_bool(result); |
---|
749 | 812 | return result; |
---|
750 | 813 | } |
---|
751 | | -EXPORT_SYMBOL(cfg80211_rx_mgmt); |
---|
| 814 | +EXPORT_SYMBOL(cfg80211_rx_mgmt_khz); |
---|
752 | 815 | |
---|
753 | 816 | void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev) |
---|
754 | 817 | { |
---|
.. | .. |
---|
888 | 951 | sizeof(struct cfg80211_chan_def)); |
---|
889 | 952 | queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); |
---|
890 | 953 | cfg80211_sched_dfs_chan_update(rdev); |
---|
891 | | - /* fall through */ |
---|
| 954 | + fallthrough; |
---|
892 | 955 | case NL80211_RADAR_CAC_ABORTED: |
---|
893 | 956 | wdev->cac_started = false; |
---|
894 | 957 | break; |
---|