| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (c) 2008, 2009 open80211s Ltd. |
|---|
| 4 | + * Copyright (C) 2019 Intel Corporation |
|---|
| 3 | 5 | * Author: Luis Carlos Cobo <luisca@cozybit.com> |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 6 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 7 | | - * published by the Free Software Foundation. |
|---|
| 8 | 6 | */ |
|---|
| 9 | 7 | #include <linux/gfp.h> |
|---|
| 10 | 8 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 146 | 144 | |
|---|
| 147 | 145 | /** |
|---|
| 148 | 146 | * mesh_set_ht_prot_mode - set correct HT protection mode |
|---|
| 147 | + * @sdata: the (mesh) interface to handle |
|---|
| 149 | 148 | * |
|---|
| 150 | 149 | * Section 9.23.3.5 of IEEE 80211-2012 describes the protection rules for HT |
|---|
| 151 | 150 | * mesh STA in a MBSS. Three HT protection modes are supported for now, non-HT |
|---|
| .. | .. |
|---|
| 220 | 219 | bool include_plid = false; |
|---|
| 221 | 220 | u16 peering_proto = 0; |
|---|
| 222 | 221 | u8 *pos, ie_len = 4; |
|---|
| 222 | + u8 ie_len_he_cap; |
|---|
| 223 | 223 | int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.self_prot); |
|---|
| 224 | 224 | int err = -ENOMEM; |
|---|
| 225 | 225 | |
|---|
| 226 | + ie_len_he_cap = ieee80211_ie_len_he_cap(sdata, |
|---|
| 227 | + NL80211_IFTYPE_MESH_POINT); |
|---|
| 226 | 228 | skb = dev_alloc_skb(local->tx_headroom + |
|---|
| 227 | 229 | hdr_len + |
|---|
| 228 | 230 | 2 + /* capability info */ |
|---|
| .. | .. |
|---|
| 235 | 237 | 2 + sizeof(struct ieee80211_ht_operation) + |
|---|
| 236 | 238 | 2 + sizeof(struct ieee80211_vht_cap) + |
|---|
| 237 | 239 | 2 + sizeof(struct ieee80211_vht_operation) + |
|---|
| 240 | + ie_len_he_cap + |
|---|
| 241 | + 2 + 1 + sizeof(struct ieee80211_he_operation) + |
|---|
| 242 | + sizeof(struct ieee80211_he_6ghz_oper) + |
|---|
| 243 | + 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) + |
|---|
| 238 | 244 | 2 + 8 + /* peering IE */ |
|---|
| 239 | 245 | sdata->u.mesh.ie_len); |
|---|
| 240 | 246 | if (!skb) |
|---|
| .. | .. |
|---|
| 323 | 329 | if (mesh_add_ht_cap_ie(sdata, skb) || |
|---|
| 324 | 330 | mesh_add_ht_oper_ie(sdata, skb) || |
|---|
| 325 | 331 | mesh_add_vht_cap_ie(sdata, skb) || |
|---|
| 326 | | - mesh_add_vht_oper_ie(sdata, skb)) |
|---|
| 332 | + mesh_add_vht_oper_ie(sdata, skb) || |
|---|
| 333 | + mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) || |
|---|
| 334 | + mesh_add_he_oper_ie(sdata, skb) || |
|---|
| 335 | + mesh_add_he_6ghz_cap_ie(sdata, skb)) |
|---|
| 327 | 336 | goto free; |
|---|
| 328 | 337 | } |
|---|
| 329 | 338 | |
|---|
| .. | .. |
|---|
| 435 | 444 | ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, |
|---|
| 436 | 445 | elems->vht_cap_elem, sta); |
|---|
| 437 | 446 | |
|---|
| 447 | + ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, elems->he_cap, |
|---|
| 448 | + elems->he_cap_len, |
|---|
| 449 | + elems->he_6ghz_capa, |
|---|
| 450 | + sta); |
|---|
| 451 | + |
|---|
| 438 | 452 | if (bw != sta->sta.bandwidth) |
|---|
| 439 | 453 | changed |= IEEE80211_RC_BW_CHANGED; |
|---|
| 440 | 454 | |
|---|
| .. | .. |
|---|
| 513 | 527 | |
|---|
| 514 | 528 | static struct sta_info * |
|---|
| 515 | 529 | mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, |
|---|
| 516 | | - struct ieee802_11_elems *elems) |
|---|
| 530 | + struct ieee802_11_elems *elems, |
|---|
| 531 | + struct ieee80211_rx_status *rx_status) |
|---|
| 517 | 532 | { |
|---|
| 518 | 533 | struct sta_info *sta = NULL; |
|---|
| 519 | 534 | |
|---|
| .. | .. |
|---|
| 521 | 536 | if (sdata->u.mesh.user_mpm || |
|---|
| 522 | 537 | sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) { |
|---|
| 523 | 538 | if (mesh_peer_accepts_plinks(elems) && |
|---|
| 524 | | - mesh_plink_availables(sdata)) |
|---|
| 539 | + mesh_plink_availables(sdata)) { |
|---|
| 540 | + int sig = 0; |
|---|
| 541 | + |
|---|
| 542 | + if (ieee80211_hw_check(&sdata->local->hw, SIGNAL_DBM)) |
|---|
| 543 | + sig = rx_status->signal; |
|---|
| 544 | + |
|---|
| 525 | 545 | cfg80211_notify_new_peer_candidate(sdata->dev, addr, |
|---|
| 526 | 546 | elems->ie_start, |
|---|
| 527 | 547 | elems->total_len, |
|---|
| 528 | | - GFP_KERNEL); |
|---|
| 548 | + sig, GFP_KERNEL); |
|---|
| 549 | + } |
|---|
| 529 | 550 | } else |
|---|
| 530 | 551 | sta = __mesh_sta_info_alloc(sdata, addr); |
|---|
| 531 | 552 | |
|---|
| .. | .. |
|---|
| 538 | 559 | * @sdata: local meshif |
|---|
| 539 | 560 | * @addr: peer's address |
|---|
| 540 | 561 | * @elems: IEs from beacon or mesh peering frame. |
|---|
| 562 | + * @rx_status: rx status for the frame for signal reporting |
|---|
| 541 | 563 | * |
|---|
| 542 | 564 | * Return existing or newly allocated sta_info under RCU read lock. |
|---|
| 543 | 565 | * (re)initialize with given IEs. |
|---|
| 544 | 566 | */ |
|---|
| 545 | 567 | static struct sta_info * |
|---|
| 546 | 568 | mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, |
|---|
| 547 | | - u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU) |
|---|
| 569 | + u8 *addr, struct ieee802_11_elems *elems, |
|---|
| 570 | + struct ieee80211_rx_status *rx_status) __acquires(RCU) |
|---|
| 548 | 571 | { |
|---|
| 549 | 572 | struct sta_info *sta = NULL; |
|---|
| 550 | 573 | |
|---|
| .. | .. |
|---|
| 555 | 578 | } else { |
|---|
| 556 | 579 | rcu_read_unlock(); |
|---|
| 557 | 580 | /* can't run atomic */ |
|---|
| 558 | | - sta = mesh_sta_info_alloc(sdata, addr, elems); |
|---|
| 581 | + sta = mesh_sta_info_alloc(sdata, addr, elems, rx_status); |
|---|
| 559 | 582 | if (!sta) { |
|---|
| 560 | 583 | rcu_read_lock(); |
|---|
| 561 | 584 | return NULL; |
|---|
| .. | .. |
|---|
| 576 | 599 | * @sdata: local meshif |
|---|
| 577 | 600 | * @addr: peer's address |
|---|
| 578 | 601 | * @elems: IEs from beacon or mesh peering frame |
|---|
| 602 | + * @rx_status: rx status for the frame for signal reporting |
|---|
| 579 | 603 | * |
|---|
| 580 | 604 | * Initiates peering if appropriate. |
|---|
| 581 | 605 | */ |
|---|
| 582 | 606 | void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, |
|---|
| 583 | 607 | u8 *hw_addr, |
|---|
| 584 | | - struct ieee802_11_elems *elems) |
|---|
| 608 | + struct ieee802_11_elems *elems, |
|---|
| 609 | + struct ieee80211_rx_status *rx_status) |
|---|
| 585 | 610 | { |
|---|
| 586 | 611 | struct sta_info *sta; |
|---|
| 587 | 612 | u32 changed = 0; |
|---|
| 588 | 613 | |
|---|
| 589 | | - sta = mesh_sta_info_get(sdata, hw_addr, elems); |
|---|
| 614 | + sta = mesh_sta_info_get(sdata, hw_addr, elems, rx_status); |
|---|
| 590 | 615 | if (!sta) |
|---|
| 591 | 616 | goto out; |
|---|
| 617 | + |
|---|
| 618 | + sta->mesh->connected_to_gate = elems->mesh_config->meshconf_form & |
|---|
| 619 | + IEEE80211_MESHCONF_FORM_CONNECTED_TO_GATE; |
|---|
| 592 | 620 | |
|---|
| 593 | 621 | if (mesh_peer_accepts_plinks(elems) && |
|---|
| 594 | 622 | sta->mesh->plink_state == NL80211_PLINK_LISTEN && |
|---|
| .. | .. |
|---|
| 672 | 700 | break; |
|---|
| 673 | 701 | } |
|---|
| 674 | 702 | reason = WLAN_REASON_MESH_MAX_RETRIES; |
|---|
| 675 | | - /* fall through */ |
|---|
| 703 | + fallthrough; |
|---|
| 676 | 704 | case NL80211_PLINK_CNF_RCVD: |
|---|
| 677 | 705 | /* confirm timer */ |
|---|
| 678 | 706 | if (!reason) |
|---|
| .. | .. |
|---|
| 1069 | 1097 | static void |
|---|
| 1070 | 1098 | mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata, |
|---|
| 1071 | 1099 | struct ieee80211_mgmt *mgmt, |
|---|
| 1072 | | - struct ieee802_11_elems *elems) |
|---|
| 1100 | + struct ieee802_11_elems *elems, |
|---|
| 1101 | + struct ieee80211_rx_status *rx_status) |
|---|
| 1073 | 1102 | { |
|---|
| 1074 | 1103 | |
|---|
| 1075 | 1104 | struct sta_info *sta; |
|---|
| .. | .. |
|---|
| 1134 | 1163 | if (event == OPN_ACPT) { |
|---|
| 1135 | 1164 | rcu_read_unlock(); |
|---|
| 1136 | 1165 | /* allocate sta entry if necessary and update info */ |
|---|
| 1137 | | - sta = mesh_sta_info_get(sdata, mgmt->sa, elems); |
|---|
| 1166 | + sta = mesh_sta_info_get(sdata, mgmt->sa, elems, rx_status); |
|---|
| 1138 | 1167 | if (!sta) { |
|---|
| 1139 | 1168 | mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); |
|---|
| 1140 | 1169 | goto unlock_rcu; |
|---|
| .. | .. |
|---|
| 1199 | 1228 | if (baselen > len) |
|---|
| 1200 | 1229 | return; |
|---|
| 1201 | 1230 | } |
|---|
| 1202 | | - ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); |
|---|
| 1203 | | - mesh_process_plink_frame(sdata, mgmt, &elems); |
|---|
| 1231 | + ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems, |
|---|
| 1232 | + mgmt->bssid, NULL); |
|---|
| 1233 | + mesh_process_plink_frame(sdata, mgmt, &elems, rx_status); |
|---|
| 1204 | 1234 | } |
|---|