18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2008, 2009 open80211s Ltd. 48c2ecf20Sopenharmony_ci * Copyright (C) 2019 Intel Corporation 58c2ecf20Sopenharmony_ci * Author: Luis Carlos Cobo <luisca@cozybit.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/gfp.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/random.h> 108c2ecf20Sopenharmony_ci#include <linux/rculist.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "ieee80211_i.h" 138c2ecf20Sopenharmony_ci#include "rate.h" 148c2ecf20Sopenharmony_ci#include "mesh.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define PLINK_CNF_AID(mgmt) ((mgmt)->u.action.u.self_prot.variable + 2) 178c2ecf20Sopenharmony_ci#define PLINK_GET_LLID(p) (p + 2) 188c2ecf20Sopenharmony_ci#define PLINK_GET_PLID(p) (p + 4) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define mod_plink_timer(s, t) (mod_timer(&s->mesh->plink_timer, \ 218c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(t))) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cienum plink_event { 248c2ecf20Sopenharmony_ci PLINK_UNDEFINED, 258c2ecf20Sopenharmony_ci OPN_ACPT, 268c2ecf20Sopenharmony_ci OPN_RJCT, 278c2ecf20Sopenharmony_ci OPN_IGNR, 288c2ecf20Sopenharmony_ci CNF_ACPT, 298c2ecf20Sopenharmony_ci CNF_RJCT, 308c2ecf20Sopenharmony_ci CNF_IGNR, 318c2ecf20Sopenharmony_ci CLS_ACPT, 328c2ecf20Sopenharmony_ci CLS_IGNR 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const char * const mplstates[] = { 368c2ecf20Sopenharmony_ci [NL80211_PLINK_LISTEN] = "LISTEN", 378c2ecf20Sopenharmony_ci [NL80211_PLINK_OPN_SNT] = "OPN-SNT", 388c2ecf20Sopenharmony_ci [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD", 398c2ecf20Sopenharmony_ci [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD", 408c2ecf20Sopenharmony_ci [NL80211_PLINK_ESTAB] = "ESTAB", 418c2ecf20Sopenharmony_ci [NL80211_PLINK_HOLDING] = "HOLDING", 428c2ecf20Sopenharmony_ci [NL80211_PLINK_BLOCKED] = "BLOCKED" 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic const char * const mplevents[] = { 468c2ecf20Sopenharmony_ci [PLINK_UNDEFINED] = "NONE", 478c2ecf20Sopenharmony_ci [OPN_ACPT] = "OPN_ACPT", 488c2ecf20Sopenharmony_ci [OPN_RJCT] = "OPN_RJCT", 498c2ecf20Sopenharmony_ci [OPN_IGNR] = "OPN_IGNR", 508c2ecf20Sopenharmony_ci [CNF_ACPT] = "CNF_ACPT", 518c2ecf20Sopenharmony_ci [CNF_RJCT] = "CNF_RJCT", 528c2ecf20Sopenharmony_ci [CNF_IGNR] = "CNF_IGNR", 538c2ecf20Sopenharmony_ci [CLS_ACPT] = "CLS_ACPT", 548c2ecf20Sopenharmony_ci [CLS_IGNR] = "CLS_IGNR" 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* We only need a valid sta if user configured a minimum rssi_threshold. */ 588c2ecf20Sopenharmony_cistatic bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata, 598c2ecf20Sopenharmony_ci struct sta_info *sta) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold; 628c2ecf20Sopenharmony_ci return rssi_threshold == 0 || 638c2ecf20Sopenharmony_ci (sta && 648c2ecf20Sopenharmony_ci (s8)-ewma_signal_read(&sta->rx_stats_avg.signal) > 658c2ecf20Sopenharmony_ci rssi_threshold); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/** 698c2ecf20Sopenharmony_ci * mesh_plink_fsm_restart - restart a mesh peer link finite state machine 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * @sta: mesh peer link to restart 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * Locking: this function must be called holding sta->mesh->plink_lock 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic inline void mesh_plink_fsm_restart(struct sta_info *sta) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci lockdep_assert_held(&sta->mesh->plink_lock); 788c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_LISTEN; 798c2ecf20Sopenharmony_ci sta->mesh->llid = sta->mesh->plid = sta->mesh->reason = 0; 808c2ecf20Sopenharmony_ci sta->mesh->plink_retries = 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * mesh_set_short_slot_time - enable / disable ERP short slot time. 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * The standard indirectly mandates mesh STAs to turn off short slot time by 878c2ecf20Sopenharmony_ci * disallowing advertising this (802.11-2012 8.4.1.4), but that doesn't mean we 888c2ecf20Sopenharmony_ci * can't be sneaky about it. Enable short slot time if all mesh STAs in the 898c2ecf20Sopenharmony_ci * MBSS support ERP rates. 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * Returns BSS_CHANGED_ERP_SLOT or 0 for no change. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_cistatic u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 968c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 978c2ecf20Sopenharmony_ci struct sta_info *sta; 988c2ecf20Sopenharmony_ci u32 erp_rates = 0, changed = 0; 998c2ecf20Sopenharmony_ci int i; 1008c2ecf20Sopenharmony_ci bool short_slot = false; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci sband = ieee80211_get_sband(sdata); 1038c2ecf20Sopenharmony_ci if (!sband) 1048c2ecf20Sopenharmony_ci return changed; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (sband->band == NL80211_BAND_5GHZ) { 1078c2ecf20Sopenharmony_ci /* (IEEE 802.11-2012 19.4.5) */ 1088c2ecf20Sopenharmony_ci short_slot = true; 1098c2ecf20Sopenharmony_ci goto out; 1108c2ecf20Sopenharmony_ci } else if (sband->band != NL80211_BAND_2GHZ) { 1118c2ecf20Sopenharmony_ci goto out; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci for (i = 0; i < sband->n_bitrates; i++) 1158c2ecf20Sopenharmony_ci if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G) 1168c2ecf20Sopenharmony_ci erp_rates |= BIT(i); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (!erp_rates) 1198c2ecf20Sopenharmony_ci goto out; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci rcu_read_lock(); 1228c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sta, &local->sta_list, list) { 1238c2ecf20Sopenharmony_ci if (sdata != sta->sdata || 1248c2ecf20Sopenharmony_ci sta->mesh->plink_state != NL80211_PLINK_ESTAB) 1258c2ecf20Sopenharmony_ci continue; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci short_slot = false; 1288c2ecf20Sopenharmony_ci if (erp_rates & sta->sta.supp_rates[sband->band]) 1298c2ecf20Sopenharmony_ci short_slot = true; 1308c2ecf20Sopenharmony_ci else 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci rcu_read_unlock(); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ciout: 1368c2ecf20Sopenharmony_ci if (sdata->vif.bss_conf.use_short_slot != short_slot) { 1378c2ecf20Sopenharmony_ci sdata->vif.bss_conf.use_short_slot = short_slot; 1388c2ecf20Sopenharmony_ci changed = BSS_CHANGED_ERP_SLOT; 1398c2ecf20Sopenharmony_ci mpl_dbg(sdata, "mesh_plink %pM: ERP short slot time %d\n", 1408c2ecf20Sopenharmony_ci sdata->vif.addr, short_slot); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci return changed; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/** 1468c2ecf20Sopenharmony_ci * mesh_set_ht_prot_mode - set correct HT protection mode 1478c2ecf20Sopenharmony_ci * @sdata: the (mesh) interface to handle 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * Section 9.23.3.5 of IEEE 80211-2012 describes the protection rules for HT 1508c2ecf20Sopenharmony_ci * mesh STA in a MBSS. Three HT protection modes are supported for now, non-HT 1518c2ecf20Sopenharmony_ci * mixed mode, 20MHz-protection and no-protection mode. non-HT mixed mode is 1528c2ecf20Sopenharmony_ci * selected if any non-HT peers are present in our MBSS. 20MHz-protection mode 1538c2ecf20Sopenharmony_ci * is selected if all peers in our 20/40MHz MBSS support HT and atleast one 1548c2ecf20Sopenharmony_ci * HT20 peer is present. Otherwise no-protection mode is selected. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_cistatic u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 1598c2ecf20Sopenharmony_ci struct sta_info *sta; 1608c2ecf20Sopenharmony_ci u16 ht_opmode; 1618c2ecf20Sopenharmony_ci bool non_ht_sta = false, ht20_sta = false; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci switch (sdata->vif.bss_conf.chandef.width) { 1648c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 1658c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_5: 1668c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_10: 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci default: 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci rcu_read_lock(); 1738c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sta, &local->sta_list, list) { 1748c2ecf20Sopenharmony_ci if (sdata != sta->sdata || 1758c2ecf20Sopenharmony_ci sta->mesh->plink_state != NL80211_PLINK_ESTAB) 1768c2ecf20Sopenharmony_ci continue; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20) 1798c2ecf20Sopenharmony_ci continue; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (!sta->sta.ht_cap.ht_supported) { 1828c2ecf20Sopenharmony_ci mpl_dbg(sdata, "nonHT sta (%pM) is present\n", 1838c2ecf20Sopenharmony_ci sta->sta.addr); 1848c2ecf20Sopenharmony_ci non_ht_sta = true; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci mpl_dbg(sdata, "HT20 sta (%pM) is present\n", sta->sta.addr); 1898c2ecf20Sopenharmony_ci ht20_sta = true; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci rcu_read_unlock(); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (non_ht_sta) 1948c2ecf20Sopenharmony_ci ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; 1958c2ecf20Sopenharmony_ci else if (ht20_sta && 1968c2ecf20Sopenharmony_ci sdata->vif.bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) 1978c2ecf20Sopenharmony_ci ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; 1988c2ecf20Sopenharmony_ci else 1998c2ecf20Sopenharmony_ci ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (sdata->vif.bss_conf.ht_operation_mode == ht_opmode) 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci sdata->vif.bss_conf.ht_operation_mode = ht_opmode; 2058c2ecf20Sopenharmony_ci sdata->u.mesh.mshcfg.ht_opmode = ht_opmode; 2068c2ecf20Sopenharmony_ci mpl_dbg(sdata, "selected new HT protection mode %d\n", ht_opmode); 2078c2ecf20Sopenharmony_ci return BSS_CHANGED_HT; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, 2118c2ecf20Sopenharmony_ci struct sta_info *sta, 2128c2ecf20Sopenharmony_ci enum ieee80211_self_protected_actioncode action, 2138c2ecf20Sopenharmony_ci u8 *da, u16 llid, u16 plid, u16 reason) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 2168c2ecf20Sopenharmony_ci struct sk_buff *skb; 2178c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 2188c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt; 2198c2ecf20Sopenharmony_ci bool include_plid = false; 2208c2ecf20Sopenharmony_ci u16 peering_proto = 0; 2218c2ecf20Sopenharmony_ci u8 *pos, ie_len = 4; 2228c2ecf20Sopenharmony_ci u8 ie_len_he_cap; 2238c2ecf20Sopenharmony_ci int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.self_prot); 2248c2ecf20Sopenharmony_ci int err = -ENOMEM; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci ie_len_he_cap = ieee80211_ie_len_he_cap(sdata, 2278c2ecf20Sopenharmony_ci NL80211_IFTYPE_MESH_POINT); 2288c2ecf20Sopenharmony_ci skb = dev_alloc_skb(local->tx_headroom + 2298c2ecf20Sopenharmony_ci hdr_len + 2308c2ecf20Sopenharmony_ci 2 + /* capability info */ 2318c2ecf20Sopenharmony_ci 2 + /* AID */ 2328c2ecf20Sopenharmony_ci 2 + 8 + /* supported rates */ 2338c2ecf20Sopenharmony_ci 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2348c2ecf20Sopenharmony_ci 2 + sdata->u.mesh.mesh_id_len + 2358c2ecf20Sopenharmony_ci 2 + sizeof(struct ieee80211_meshconf_ie) + 2368c2ecf20Sopenharmony_ci 2 + sizeof(struct ieee80211_ht_cap) + 2378c2ecf20Sopenharmony_ci 2 + sizeof(struct ieee80211_ht_operation) + 2388c2ecf20Sopenharmony_ci 2 + sizeof(struct ieee80211_vht_cap) + 2398c2ecf20Sopenharmony_ci 2 + sizeof(struct ieee80211_vht_operation) + 2408c2ecf20Sopenharmony_ci ie_len_he_cap + 2418c2ecf20Sopenharmony_ci 2 + 1 + sizeof(struct ieee80211_he_operation) + 2428c2ecf20Sopenharmony_ci sizeof(struct ieee80211_he_6ghz_oper) + 2438c2ecf20Sopenharmony_ci 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) + 2448c2ecf20Sopenharmony_ci 2 + 8 + /* peering IE */ 2458c2ecf20Sopenharmony_ci sdata->u.mesh.ie_len); 2468c2ecf20Sopenharmony_ci if (!skb) 2478c2ecf20Sopenharmony_ci return err; 2488c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 2498c2ecf20Sopenharmony_ci skb_reserve(skb, local->tx_headroom); 2508c2ecf20Sopenharmony_ci mgmt = skb_put_zero(skb, hdr_len); 2518c2ecf20Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 2528c2ecf20Sopenharmony_ci IEEE80211_STYPE_ACTION); 2538c2ecf20Sopenharmony_ci memcpy(mgmt->da, da, ETH_ALEN); 2548c2ecf20Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 2558c2ecf20Sopenharmony_ci memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); 2568c2ecf20Sopenharmony_ci mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED; 2578c2ecf20Sopenharmony_ci mgmt->u.action.u.self_prot.action_code = action; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (action != WLAN_SP_MESH_PEERING_CLOSE) { 2608c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 2618c2ecf20Sopenharmony_ci enum nl80211_band band; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci sband = ieee80211_get_sband(sdata); 2648c2ecf20Sopenharmony_ci if (!sband) { 2658c2ecf20Sopenharmony_ci err = -EINVAL; 2668c2ecf20Sopenharmony_ci goto free; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci band = sband->band; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* capability info */ 2718c2ecf20Sopenharmony_ci pos = skb_put_zero(skb, 2); 2728c2ecf20Sopenharmony_ci if (action == WLAN_SP_MESH_PEERING_CONFIRM) { 2738c2ecf20Sopenharmony_ci /* AID */ 2748c2ecf20Sopenharmony_ci pos = skb_put(skb, 2); 2758c2ecf20Sopenharmony_ci put_unaligned_le16(sta->sta.aid, pos); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci if (ieee80211_add_srates_ie(sdata, skb, true, band) || 2788c2ecf20Sopenharmony_ci ieee80211_add_ext_srates_ie(sdata, skb, true, band) || 2798c2ecf20Sopenharmony_ci mesh_add_rsn_ie(sdata, skb) || 2808c2ecf20Sopenharmony_ci mesh_add_meshid_ie(sdata, skb) || 2818c2ecf20Sopenharmony_ci mesh_add_meshconf_ie(sdata, skb)) 2828c2ecf20Sopenharmony_ci goto free; 2838c2ecf20Sopenharmony_ci } else { /* WLAN_SP_MESH_PEERING_CLOSE */ 2848c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_CTL_NO_ACK; 2858c2ecf20Sopenharmony_ci if (mesh_add_meshid_ie(sdata, skb)) 2868c2ecf20Sopenharmony_ci goto free; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Add Mesh Peering Management element */ 2908c2ecf20Sopenharmony_ci switch (action) { 2918c2ecf20Sopenharmony_ci case WLAN_SP_MESH_PEERING_OPEN: 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci case WLAN_SP_MESH_PEERING_CONFIRM: 2948c2ecf20Sopenharmony_ci ie_len += 2; 2958c2ecf20Sopenharmony_ci include_plid = true; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case WLAN_SP_MESH_PEERING_CLOSE: 2988c2ecf20Sopenharmony_ci if (plid) { 2998c2ecf20Sopenharmony_ci ie_len += 2; 3008c2ecf20Sopenharmony_ci include_plid = true; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci ie_len += 2; /* reason code */ 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci default: 3058c2ecf20Sopenharmony_ci err = -EINVAL; 3068c2ecf20Sopenharmony_ci goto free; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (WARN_ON(skb_tailroom(skb) < 2 + ie_len)) 3108c2ecf20Sopenharmony_ci goto free; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci pos = skb_put(skb, 2 + ie_len); 3138c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_PEER_MGMT; 3148c2ecf20Sopenharmony_ci *pos++ = ie_len; 3158c2ecf20Sopenharmony_ci memcpy(pos, &peering_proto, 2); 3168c2ecf20Sopenharmony_ci pos += 2; 3178c2ecf20Sopenharmony_ci put_unaligned_le16(llid, pos); 3188c2ecf20Sopenharmony_ci pos += 2; 3198c2ecf20Sopenharmony_ci if (include_plid) { 3208c2ecf20Sopenharmony_ci put_unaligned_le16(plid, pos); 3218c2ecf20Sopenharmony_ci pos += 2; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci if (action == WLAN_SP_MESH_PEERING_CLOSE) { 3248c2ecf20Sopenharmony_ci put_unaligned_le16(reason, pos); 3258c2ecf20Sopenharmony_ci pos += 2; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (action != WLAN_SP_MESH_PEERING_CLOSE) { 3298c2ecf20Sopenharmony_ci if (mesh_add_ht_cap_ie(sdata, skb) || 3308c2ecf20Sopenharmony_ci mesh_add_ht_oper_ie(sdata, skb) || 3318c2ecf20Sopenharmony_ci mesh_add_vht_cap_ie(sdata, skb) || 3328c2ecf20Sopenharmony_ci mesh_add_vht_oper_ie(sdata, skb) || 3338c2ecf20Sopenharmony_ci mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) || 3348c2ecf20Sopenharmony_ci mesh_add_he_oper_ie(sdata, skb) || 3358c2ecf20Sopenharmony_ci mesh_add_he_6ghz_cap_ie(sdata, skb)) 3368c2ecf20Sopenharmony_ci goto free; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (mesh_add_vendor_ies(sdata, skb)) 3408c2ecf20Sopenharmony_ci goto free; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_cifree: 3458c2ecf20Sopenharmony_ci kfree_skb(skb); 3468c2ecf20Sopenharmony_ci return err; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci/** 3508c2ecf20Sopenharmony_ci * __mesh_plink_deactivate - deactivate mesh peer link 3518c2ecf20Sopenharmony_ci * 3528c2ecf20Sopenharmony_ci * @sta: mesh peer link to deactivate 3538c2ecf20Sopenharmony_ci * 3548c2ecf20Sopenharmony_ci * Mesh paths with this peer as next hop should be flushed 3558c2ecf20Sopenharmony_ci * by the caller outside of plink_lock. 3568c2ecf20Sopenharmony_ci * 3578c2ecf20Sopenharmony_ci * Returns beacon changed flag if the beacon content changed. 3588c2ecf20Sopenharmony_ci * 3598c2ecf20Sopenharmony_ci * Locking: the caller must hold sta->mesh->plink_lock 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_cistatic u32 __mesh_plink_deactivate(struct sta_info *sta) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = sta->sdata; 3648c2ecf20Sopenharmony_ci u32 changed = 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci lockdep_assert_held(&sta->mesh->plink_lock); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (sta->mesh->plink_state == NL80211_PLINK_ESTAB) 3698c2ecf20Sopenharmony_ci changed = mesh_plink_dec_estab_count(sdata); 3708c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_BLOCKED; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ieee80211_mps_sta_status_update(sta); 3738c2ecf20Sopenharmony_ci changed |= ieee80211_mps_set_sta_local_pm(sta, 3748c2ecf20Sopenharmony_ci NL80211_MESH_POWER_UNKNOWN); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return changed; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/** 3808c2ecf20Sopenharmony_ci * mesh_plink_deactivate - deactivate mesh peer link 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * @sta: mesh peer link to deactivate 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * All mesh paths with this peer as next hop will be flushed 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ciu32 mesh_plink_deactivate(struct sta_info *sta) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = sta->sdata; 3898c2ecf20Sopenharmony_ci u32 changed; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci spin_lock_bh(&sta->mesh->plink_lock); 3928c2ecf20Sopenharmony_ci changed = __mesh_plink_deactivate(sta); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (!sdata->u.mesh.user_mpm) { 3958c2ecf20Sopenharmony_ci sta->mesh->reason = WLAN_REASON_MESH_PEER_CANCELED; 3968c2ecf20Sopenharmony_ci mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_CLOSE, 3978c2ecf20Sopenharmony_ci sta->sta.addr, sta->mesh->llid, 3988c2ecf20Sopenharmony_ci sta->mesh->plid, sta->mesh->reason); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->mesh->plink_lock); 4018c2ecf20Sopenharmony_ci if (!sdata->u.mesh.user_mpm) 4028c2ecf20Sopenharmony_ci del_timer_sync(&sta->mesh->plink_timer); 4038c2ecf20Sopenharmony_ci mesh_path_flush_by_nexthop(sta); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* make sure no readers can access nexthop sta from here on */ 4068c2ecf20Sopenharmony_ci synchronize_net(); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return changed; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, 4128c2ecf20Sopenharmony_ci struct sta_info *sta, 4138c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 4168c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 4178c2ecf20Sopenharmony_ci u32 rates, basic_rates = 0, changed = 0; 4188c2ecf20Sopenharmony_ci enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci sband = ieee80211_get_sband(sdata); 4218c2ecf20Sopenharmony_ci if (!sband) 4228c2ecf20Sopenharmony_ci return; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci rates = ieee80211_sta_get_rates(sdata, elems, sband->band, 4258c2ecf20Sopenharmony_ci &basic_rates); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci spin_lock_bh(&sta->mesh->plink_lock); 4288c2ecf20Sopenharmony_ci sta->rx_stats.last_rx = jiffies; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* rates and capabilities don't change during peering */ 4318c2ecf20Sopenharmony_ci if (sta->mesh->plink_state == NL80211_PLINK_ESTAB && 4328c2ecf20Sopenharmony_ci sta->mesh->processed_beacon) 4338c2ecf20Sopenharmony_ci goto out; 4348c2ecf20Sopenharmony_ci sta->mesh->processed_beacon = true; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (sta->sta.supp_rates[sband->band] != rates) 4378c2ecf20Sopenharmony_ci changed |= IEEE80211_RC_SUPP_RATES_CHANGED; 4388c2ecf20Sopenharmony_ci sta->sta.supp_rates[sband->band] = rates; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, 4418c2ecf20Sopenharmony_ci elems->ht_cap_elem, sta)) 4428c2ecf20Sopenharmony_ci changed |= IEEE80211_RC_BW_CHANGED; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, 4458c2ecf20Sopenharmony_ci elems->vht_cap_elem, sta); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, elems->he_cap, 4488c2ecf20Sopenharmony_ci elems->he_cap_len, 4498c2ecf20Sopenharmony_ci elems->he_6ghz_capa, 4508c2ecf20Sopenharmony_ci sta); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (bw != sta->sta.bandwidth) 4538c2ecf20Sopenharmony_ci changed |= IEEE80211_RC_BW_CHANGED; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* HT peer is operating 20MHz-only */ 4568c2ecf20Sopenharmony_ci if (elems->ht_operation && 4578c2ecf20Sopenharmony_ci !(elems->ht_operation->ht_param & 4588c2ecf20Sopenharmony_ci IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { 4598c2ecf20Sopenharmony_ci if (sta->sta.bandwidth != IEEE80211_STA_RX_BW_20) 4608c2ecf20Sopenharmony_ci changed |= IEEE80211_RC_BW_CHANGED; 4618c2ecf20Sopenharmony_ci sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (!test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) 4658c2ecf20Sopenharmony_ci rate_control_rate_init(sta); 4668c2ecf20Sopenharmony_ci else 4678c2ecf20Sopenharmony_ci rate_control_rate_update(local, sband, sta, changed); 4688c2ecf20Sopenharmony_ciout: 4698c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->mesh->plink_lock); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int mesh_allocate_aid(struct ieee80211_sub_if_data *sdata) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct sta_info *sta; 4758c2ecf20Sopenharmony_ci unsigned long *aid_map; 4768c2ecf20Sopenharmony_ci int aid; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci aid_map = kcalloc(BITS_TO_LONGS(IEEE80211_MAX_AID + 1), 4798c2ecf20Sopenharmony_ci sizeof(*aid_map), GFP_KERNEL); 4808c2ecf20Sopenharmony_ci if (!aid_map) 4818c2ecf20Sopenharmony_ci return -ENOMEM; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* reserve aid 0 for mcast indication */ 4848c2ecf20Sopenharmony_ci __set_bit(0, aid_map); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci rcu_read_lock(); 4878c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) 4888c2ecf20Sopenharmony_ci __set_bit(sta->sta.aid, aid_map); 4898c2ecf20Sopenharmony_ci rcu_read_unlock(); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci aid = find_first_zero_bit(aid_map, IEEE80211_MAX_AID + 1); 4928c2ecf20Sopenharmony_ci kfree(aid_map); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (aid > IEEE80211_MAX_AID) 4958c2ecf20Sopenharmony_ci return -ENOBUFS; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return aid; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic struct sta_info * 5018c2ecf20Sopenharmony_ci__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct sta_info *sta; 5048c2ecf20Sopenharmony_ci int aid; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (sdata->local->num_sta >= MESH_MAX_PLINKS) 5078c2ecf20Sopenharmony_ci return NULL; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci aid = mesh_allocate_aid(sdata); 5108c2ecf20Sopenharmony_ci if (aid < 0) 5118c2ecf20Sopenharmony_ci return NULL; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); 5148c2ecf20Sopenharmony_ci if (!sta) 5158c2ecf20Sopenharmony_ci return NULL; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_LISTEN; 5188c2ecf20Sopenharmony_ci sta->sta.wme = true; 5198c2ecf20Sopenharmony_ci sta->sta.aid = aid; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); 5228c2ecf20Sopenharmony_ci sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); 5238c2ecf20Sopenharmony_ci sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return sta; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic struct sta_info * 5298c2ecf20Sopenharmony_cimesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, 5308c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems, 5318c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rx_status) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct sta_info *sta = NULL; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Userspace handles station allocation */ 5368c2ecf20Sopenharmony_ci if (sdata->u.mesh.user_mpm || 5378c2ecf20Sopenharmony_ci sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) { 5388c2ecf20Sopenharmony_ci if (mesh_peer_accepts_plinks(elems) && 5398c2ecf20Sopenharmony_ci mesh_plink_availables(sdata)) { 5408c2ecf20Sopenharmony_ci int sig = 0; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (ieee80211_hw_check(&sdata->local->hw, SIGNAL_DBM)) 5438c2ecf20Sopenharmony_ci sig = rx_status->signal; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci cfg80211_notify_new_peer_candidate(sdata->dev, addr, 5468c2ecf20Sopenharmony_ci elems->ie_start, 5478c2ecf20Sopenharmony_ci elems->total_len, 5488c2ecf20Sopenharmony_ci sig, GFP_KERNEL); 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci } else 5518c2ecf20Sopenharmony_ci sta = __mesh_sta_info_alloc(sdata, addr); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci return sta; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci/* 5578c2ecf20Sopenharmony_ci * mesh_sta_info_get - return mesh sta info entry for @addr. 5588c2ecf20Sopenharmony_ci * 5598c2ecf20Sopenharmony_ci * @sdata: local meshif 5608c2ecf20Sopenharmony_ci * @addr: peer's address 5618c2ecf20Sopenharmony_ci * @elems: IEs from beacon or mesh peering frame. 5628c2ecf20Sopenharmony_ci * @rx_status: rx status for the frame for signal reporting 5638c2ecf20Sopenharmony_ci * 5648c2ecf20Sopenharmony_ci * Return existing or newly allocated sta_info under RCU read lock. 5658c2ecf20Sopenharmony_ci * (re)initialize with given IEs. 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_cistatic struct sta_info * 5688c2ecf20Sopenharmony_cimesh_sta_info_get(struct ieee80211_sub_if_data *sdata, 5698c2ecf20Sopenharmony_ci u8 *addr, struct ieee802_11_elems *elems, 5708c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rx_status) __acquires(RCU) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct sta_info *sta = NULL; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci rcu_read_lock(); 5758c2ecf20Sopenharmony_ci sta = sta_info_get(sdata, addr); 5768c2ecf20Sopenharmony_ci if (sta) { 5778c2ecf20Sopenharmony_ci mesh_sta_info_init(sdata, sta, elems); 5788c2ecf20Sopenharmony_ci } else { 5798c2ecf20Sopenharmony_ci rcu_read_unlock(); 5808c2ecf20Sopenharmony_ci /* can't run atomic */ 5818c2ecf20Sopenharmony_ci sta = mesh_sta_info_alloc(sdata, addr, elems, rx_status); 5828c2ecf20Sopenharmony_ci if (!sta) { 5838c2ecf20Sopenharmony_ci rcu_read_lock(); 5848c2ecf20Sopenharmony_ci return NULL; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci mesh_sta_info_init(sdata, sta, elems); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (sta_info_insert_rcu(sta)) 5908c2ecf20Sopenharmony_ci return NULL; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return sta; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci/* 5978c2ecf20Sopenharmony_ci * mesh_neighbour_update - update or initialize new mesh neighbor. 5988c2ecf20Sopenharmony_ci * 5998c2ecf20Sopenharmony_ci * @sdata: local meshif 6008c2ecf20Sopenharmony_ci * @addr: peer's address 6018c2ecf20Sopenharmony_ci * @elems: IEs from beacon or mesh peering frame 6028c2ecf20Sopenharmony_ci * @rx_status: rx status for the frame for signal reporting 6038c2ecf20Sopenharmony_ci * 6048c2ecf20Sopenharmony_ci * Initiates peering if appropriate. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_civoid mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, 6078c2ecf20Sopenharmony_ci u8 *hw_addr, 6088c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems, 6098c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rx_status) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct sta_info *sta; 6128c2ecf20Sopenharmony_ci u32 changed = 0; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci sta = mesh_sta_info_get(sdata, hw_addr, elems, rx_status); 6158c2ecf20Sopenharmony_ci if (!sta) 6168c2ecf20Sopenharmony_ci goto out; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci sta->mesh->connected_to_gate = elems->mesh_config->meshconf_form & 6198c2ecf20Sopenharmony_ci IEEE80211_MESHCONF_FORM_CONNECTED_TO_GATE; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (mesh_peer_accepts_plinks(elems) && 6228c2ecf20Sopenharmony_ci sta->mesh->plink_state == NL80211_PLINK_LISTEN && 6238c2ecf20Sopenharmony_ci sdata->u.mesh.accepting_plinks && 6248c2ecf20Sopenharmony_ci sdata->u.mesh.mshcfg.auto_open_plinks && 6258c2ecf20Sopenharmony_ci rssi_threshold_check(sdata, sta)) 6268c2ecf20Sopenharmony_ci changed = mesh_plink_open(sta); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci ieee80211_mps_frame_release(sta, elems); 6298c2ecf20Sopenharmony_ciout: 6308c2ecf20Sopenharmony_ci rcu_read_unlock(); 6318c2ecf20Sopenharmony_ci ieee80211_mbss_info_change_notify(sdata, changed); 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_civoid mesh_plink_timer(struct timer_list *t) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct mesh_sta *mesh = from_timer(mesh, t, plink_timer); 6378c2ecf20Sopenharmony_ci struct sta_info *sta; 6388c2ecf20Sopenharmony_ci u16 reason = 0; 6398c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 6408c2ecf20Sopenharmony_ci struct mesh_config *mshcfg; 6418c2ecf20Sopenharmony_ci enum ieee80211_self_protected_actioncode action = 0; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* 6448c2ecf20Sopenharmony_ci * This STA is valid because sta_info_destroy() will 6458c2ecf20Sopenharmony_ci * del_timer_sync() this timer after having made sure 6468c2ecf20Sopenharmony_ci * it cannot be readded (by deleting the plink.) 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_ci sta = mesh->plink_sta; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (sta->sdata->local->quiescing) 6518c2ecf20Sopenharmony_ci return; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci spin_lock_bh(&sta->mesh->plink_lock); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* If a timer fires just before a state transition on another CPU, 6568c2ecf20Sopenharmony_ci * we may have already extended the timeout and changed state by the 6578c2ecf20Sopenharmony_ci * time we've acquired the lock and arrived here. In that case, 6588c2ecf20Sopenharmony_ci * skip this timer and wait for the new one. 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_ci if (time_before(jiffies, sta->mesh->plink_timer.expires)) { 6618c2ecf20Sopenharmony_ci mpl_dbg(sta->sdata, 6628c2ecf20Sopenharmony_ci "Ignoring timer for %pM in state %s (timer adjusted)", 6638c2ecf20Sopenharmony_ci sta->sta.addr, mplstates[sta->mesh->plink_state]); 6648c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->mesh->plink_lock); 6658c2ecf20Sopenharmony_ci return; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* del_timer() and handler may race when entering these states */ 6698c2ecf20Sopenharmony_ci if (sta->mesh->plink_state == NL80211_PLINK_LISTEN || 6708c2ecf20Sopenharmony_ci sta->mesh->plink_state == NL80211_PLINK_ESTAB) { 6718c2ecf20Sopenharmony_ci mpl_dbg(sta->sdata, 6728c2ecf20Sopenharmony_ci "Ignoring timer for %pM in state %s (timer deleted)", 6738c2ecf20Sopenharmony_ci sta->sta.addr, mplstates[sta->mesh->plink_state]); 6748c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->mesh->plink_lock); 6758c2ecf20Sopenharmony_ci return; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci mpl_dbg(sta->sdata, 6798c2ecf20Sopenharmony_ci "Mesh plink timer for %pM fired on state %s\n", 6808c2ecf20Sopenharmony_ci sta->sta.addr, mplstates[sta->mesh->plink_state]); 6818c2ecf20Sopenharmony_ci sdata = sta->sdata; 6828c2ecf20Sopenharmony_ci mshcfg = &sdata->u.mesh.mshcfg; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci switch (sta->mesh->plink_state) { 6858c2ecf20Sopenharmony_ci case NL80211_PLINK_OPN_RCVD: 6868c2ecf20Sopenharmony_ci case NL80211_PLINK_OPN_SNT: 6878c2ecf20Sopenharmony_ci /* retry timer */ 6888c2ecf20Sopenharmony_ci if (sta->mesh->plink_retries < mshcfg->dot11MeshMaxRetries) { 6898c2ecf20Sopenharmony_ci u32 rand; 6908c2ecf20Sopenharmony_ci mpl_dbg(sta->sdata, 6918c2ecf20Sopenharmony_ci "Mesh plink for %pM (retry, timeout): %d %d\n", 6928c2ecf20Sopenharmony_ci sta->sta.addr, sta->mesh->plink_retries, 6938c2ecf20Sopenharmony_ci sta->mesh->plink_timeout); 6948c2ecf20Sopenharmony_ci get_random_bytes(&rand, sizeof(u32)); 6958c2ecf20Sopenharmony_ci sta->mesh->plink_timeout = sta->mesh->plink_timeout + 6968c2ecf20Sopenharmony_ci rand % sta->mesh->plink_timeout; 6978c2ecf20Sopenharmony_ci ++sta->mesh->plink_retries; 6988c2ecf20Sopenharmony_ci mod_plink_timer(sta, sta->mesh->plink_timeout); 6998c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_OPEN; 7008c2ecf20Sopenharmony_ci break; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci reason = WLAN_REASON_MESH_MAX_RETRIES; 7038c2ecf20Sopenharmony_ci fallthrough; 7048c2ecf20Sopenharmony_ci case NL80211_PLINK_CNF_RCVD: 7058c2ecf20Sopenharmony_ci /* confirm timer */ 7068c2ecf20Sopenharmony_ci if (!reason) 7078c2ecf20Sopenharmony_ci reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT; 7088c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_HOLDING; 7098c2ecf20Sopenharmony_ci mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); 7108c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_CLOSE; 7118c2ecf20Sopenharmony_ci break; 7128c2ecf20Sopenharmony_ci case NL80211_PLINK_HOLDING: 7138c2ecf20Sopenharmony_ci /* holding timer */ 7148c2ecf20Sopenharmony_ci del_timer(&sta->mesh->plink_timer); 7158c2ecf20Sopenharmony_ci mesh_plink_fsm_restart(sta); 7168c2ecf20Sopenharmony_ci break; 7178c2ecf20Sopenharmony_ci default: 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->mesh->plink_lock); 7218c2ecf20Sopenharmony_ci if (action) 7228c2ecf20Sopenharmony_ci mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr, 7238c2ecf20Sopenharmony_ci sta->mesh->llid, sta->mesh->plid, reason); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci sta->mesh->plink_timeout = timeout; 7298c2ecf20Sopenharmony_ci mod_timer(&sta->mesh->plink_timer, jiffies + msecs_to_jiffies(timeout)); 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic bool llid_in_use(struct ieee80211_sub_if_data *sdata, 7338c2ecf20Sopenharmony_ci u16 llid) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 7368c2ecf20Sopenharmony_ci bool in_use = false; 7378c2ecf20Sopenharmony_ci struct sta_info *sta; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci rcu_read_lock(); 7408c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sta, &local->sta_list, list) { 7418c2ecf20Sopenharmony_ci if (sdata != sta->sdata) 7428c2ecf20Sopenharmony_ci continue; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (!memcmp(&sta->mesh->llid, &llid, sizeof(llid))) { 7458c2ecf20Sopenharmony_ci in_use = true; 7468c2ecf20Sopenharmony_ci break; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci rcu_read_unlock(); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return in_use; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci u16 llid; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci do { 7598c2ecf20Sopenharmony_ci get_random_bytes(&llid, sizeof(llid)); 7608c2ecf20Sopenharmony_ci } while (llid_in_use(sdata, llid)); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci return llid; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ciu32 mesh_plink_open(struct sta_info *sta) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = sta->sdata; 7688c2ecf20Sopenharmony_ci u32 changed; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (!test_sta_flag(sta, WLAN_STA_AUTH)) 7718c2ecf20Sopenharmony_ci return 0; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci spin_lock_bh(&sta->mesh->plink_lock); 7748c2ecf20Sopenharmony_ci sta->mesh->llid = mesh_get_new_llid(sdata); 7758c2ecf20Sopenharmony_ci if (sta->mesh->plink_state != NL80211_PLINK_LISTEN && 7768c2ecf20Sopenharmony_ci sta->mesh->plink_state != NL80211_PLINK_BLOCKED) { 7778c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->mesh->plink_lock); 7788c2ecf20Sopenharmony_ci return 0; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_OPN_SNT; 7818c2ecf20Sopenharmony_ci mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout); 7828c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->mesh->plink_lock); 7838c2ecf20Sopenharmony_ci mpl_dbg(sdata, 7848c2ecf20Sopenharmony_ci "Mesh plink: starting establishment with %pM\n", 7858c2ecf20Sopenharmony_ci sta->sta.addr); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* set the non-peer mode to active during peering */ 7888c2ecf20Sopenharmony_ci changed = ieee80211_mps_local_status_update(sdata); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_OPEN, 7918c2ecf20Sopenharmony_ci sta->sta.addr, sta->mesh->llid, 0, 0); 7928c2ecf20Sopenharmony_ci return changed; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ciu32 mesh_plink_block(struct sta_info *sta) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci u32 changed; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci spin_lock_bh(&sta->mesh->plink_lock); 8008c2ecf20Sopenharmony_ci changed = __mesh_plink_deactivate(sta); 8018c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_BLOCKED; 8028c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->mesh->plink_lock); 8038c2ecf20Sopenharmony_ci mesh_path_flush_by_nexthop(sta); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci return changed; 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic void mesh_plink_close(struct ieee80211_sub_if_data *sdata, 8098c2ecf20Sopenharmony_ci struct sta_info *sta, 8108c2ecf20Sopenharmony_ci enum plink_event event) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; 8138c2ecf20Sopenharmony_ci u16 reason = (event == CLS_ACPT) ? 8148c2ecf20Sopenharmony_ci WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci sta->mesh->reason = reason; 8178c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_HOLDING; 8188c2ecf20Sopenharmony_ci mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata, 8228c2ecf20Sopenharmony_ci struct sta_info *sta) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; 8258c2ecf20Sopenharmony_ci u32 changed = 0; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci del_timer(&sta->mesh->plink_timer); 8288c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_ESTAB; 8298c2ecf20Sopenharmony_ci changed |= mesh_plink_inc_estab_count(sdata); 8308c2ecf20Sopenharmony_ci changed |= mesh_set_ht_prot_mode(sdata); 8318c2ecf20Sopenharmony_ci changed |= mesh_set_short_slot_time(sdata); 8328c2ecf20Sopenharmony_ci mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); 8338c2ecf20Sopenharmony_ci ieee80211_mps_sta_status_update(sta); 8348c2ecf20Sopenharmony_ci changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode); 8358c2ecf20Sopenharmony_ci return changed; 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci/** 8398c2ecf20Sopenharmony_ci * mesh_plink_fsm - step @sta MPM based on @event 8408c2ecf20Sopenharmony_ci * 8418c2ecf20Sopenharmony_ci * @sdata: interface 8428c2ecf20Sopenharmony_ci * @sta: mesh neighbor 8438c2ecf20Sopenharmony_ci * @event: peering event 8448c2ecf20Sopenharmony_ci * 8458c2ecf20Sopenharmony_ci * Return: changed MBSS flags 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_cistatic u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata, 8488c2ecf20Sopenharmony_ci struct sta_info *sta, enum plink_event event) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; 8518c2ecf20Sopenharmony_ci enum ieee80211_self_protected_actioncode action = 0; 8528c2ecf20Sopenharmony_ci u32 changed = 0; 8538c2ecf20Sopenharmony_ci bool flush = false; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr, 8568c2ecf20Sopenharmony_ci mplstates[sta->mesh->plink_state], mplevents[event]); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci spin_lock_bh(&sta->mesh->plink_lock); 8598c2ecf20Sopenharmony_ci switch (sta->mesh->plink_state) { 8608c2ecf20Sopenharmony_ci case NL80211_PLINK_LISTEN: 8618c2ecf20Sopenharmony_ci switch (event) { 8628c2ecf20Sopenharmony_ci case CLS_ACPT: 8638c2ecf20Sopenharmony_ci mesh_plink_fsm_restart(sta); 8648c2ecf20Sopenharmony_ci break; 8658c2ecf20Sopenharmony_ci case OPN_ACPT: 8668c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD; 8678c2ecf20Sopenharmony_ci sta->mesh->llid = mesh_get_new_llid(sdata); 8688c2ecf20Sopenharmony_ci mesh_plink_timer_set(sta, 8698c2ecf20Sopenharmony_ci mshcfg->dot11MeshRetryTimeout); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* set the non-peer mode to active during peering */ 8728c2ecf20Sopenharmony_ci changed |= ieee80211_mps_local_status_update(sdata); 8738c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_OPEN; 8748c2ecf20Sopenharmony_ci break; 8758c2ecf20Sopenharmony_ci default: 8768c2ecf20Sopenharmony_ci break; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci break; 8798c2ecf20Sopenharmony_ci case NL80211_PLINK_OPN_SNT: 8808c2ecf20Sopenharmony_ci switch (event) { 8818c2ecf20Sopenharmony_ci case OPN_RJCT: 8828c2ecf20Sopenharmony_ci case CNF_RJCT: 8838c2ecf20Sopenharmony_ci case CLS_ACPT: 8848c2ecf20Sopenharmony_ci mesh_plink_close(sdata, sta, event); 8858c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_CLOSE; 8868c2ecf20Sopenharmony_ci break; 8878c2ecf20Sopenharmony_ci case OPN_ACPT: 8888c2ecf20Sopenharmony_ci /* retry timer is left untouched */ 8898c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD; 8908c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_CONFIRM; 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci case CNF_ACPT: 8938c2ecf20Sopenharmony_ci sta->mesh->plink_state = NL80211_PLINK_CNF_RCVD; 8948c2ecf20Sopenharmony_ci mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout); 8958c2ecf20Sopenharmony_ci break; 8968c2ecf20Sopenharmony_ci default: 8978c2ecf20Sopenharmony_ci break; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci break; 9008c2ecf20Sopenharmony_ci case NL80211_PLINK_OPN_RCVD: 9018c2ecf20Sopenharmony_ci switch (event) { 9028c2ecf20Sopenharmony_ci case OPN_RJCT: 9038c2ecf20Sopenharmony_ci case CNF_RJCT: 9048c2ecf20Sopenharmony_ci case CLS_ACPT: 9058c2ecf20Sopenharmony_ci mesh_plink_close(sdata, sta, event); 9068c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_CLOSE; 9078c2ecf20Sopenharmony_ci break; 9088c2ecf20Sopenharmony_ci case OPN_ACPT: 9098c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_CONFIRM; 9108c2ecf20Sopenharmony_ci break; 9118c2ecf20Sopenharmony_ci case CNF_ACPT: 9128c2ecf20Sopenharmony_ci changed |= mesh_plink_establish(sdata, sta); 9138c2ecf20Sopenharmony_ci break; 9148c2ecf20Sopenharmony_ci default: 9158c2ecf20Sopenharmony_ci break; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci case NL80211_PLINK_CNF_RCVD: 9198c2ecf20Sopenharmony_ci switch (event) { 9208c2ecf20Sopenharmony_ci case OPN_RJCT: 9218c2ecf20Sopenharmony_ci case CNF_RJCT: 9228c2ecf20Sopenharmony_ci case CLS_ACPT: 9238c2ecf20Sopenharmony_ci mesh_plink_close(sdata, sta, event); 9248c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_CLOSE; 9258c2ecf20Sopenharmony_ci break; 9268c2ecf20Sopenharmony_ci case OPN_ACPT: 9278c2ecf20Sopenharmony_ci changed |= mesh_plink_establish(sdata, sta); 9288c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_CONFIRM; 9298c2ecf20Sopenharmony_ci break; 9308c2ecf20Sopenharmony_ci default: 9318c2ecf20Sopenharmony_ci break; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci break; 9348c2ecf20Sopenharmony_ci case NL80211_PLINK_ESTAB: 9358c2ecf20Sopenharmony_ci switch (event) { 9368c2ecf20Sopenharmony_ci case CLS_ACPT: 9378c2ecf20Sopenharmony_ci changed |= __mesh_plink_deactivate(sta); 9388c2ecf20Sopenharmony_ci changed |= mesh_set_ht_prot_mode(sdata); 9398c2ecf20Sopenharmony_ci changed |= mesh_set_short_slot_time(sdata); 9408c2ecf20Sopenharmony_ci mesh_plink_close(sdata, sta, event); 9418c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_CLOSE; 9428c2ecf20Sopenharmony_ci flush = true; 9438c2ecf20Sopenharmony_ci break; 9448c2ecf20Sopenharmony_ci case OPN_ACPT: 9458c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_CONFIRM; 9468c2ecf20Sopenharmony_ci break; 9478c2ecf20Sopenharmony_ci default: 9488c2ecf20Sopenharmony_ci break; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci break; 9518c2ecf20Sopenharmony_ci case NL80211_PLINK_HOLDING: 9528c2ecf20Sopenharmony_ci switch (event) { 9538c2ecf20Sopenharmony_ci case CLS_ACPT: 9548c2ecf20Sopenharmony_ci del_timer(&sta->mesh->plink_timer); 9558c2ecf20Sopenharmony_ci mesh_plink_fsm_restart(sta); 9568c2ecf20Sopenharmony_ci break; 9578c2ecf20Sopenharmony_ci case OPN_ACPT: 9588c2ecf20Sopenharmony_ci case CNF_ACPT: 9598c2ecf20Sopenharmony_ci case OPN_RJCT: 9608c2ecf20Sopenharmony_ci case CNF_RJCT: 9618c2ecf20Sopenharmony_ci action = WLAN_SP_MESH_PEERING_CLOSE; 9628c2ecf20Sopenharmony_ci break; 9638c2ecf20Sopenharmony_ci default: 9648c2ecf20Sopenharmony_ci break; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci break; 9678c2ecf20Sopenharmony_ci default: 9688c2ecf20Sopenharmony_ci /* should not get here, PLINK_BLOCKED is dealt with at the 9698c2ecf20Sopenharmony_ci * beginning of the function 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci spin_unlock_bh(&sta->mesh->plink_lock); 9748c2ecf20Sopenharmony_ci if (flush) 9758c2ecf20Sopenharmony_ci mesh_path_flush_by_nexthop(sta); 9768c2ecf20Sopenharmony_ci if (action) { 9778c2ecf20Sopenharmony_ci mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr, 9788c2ecf20Sopenharmony_ci sta->mesh->llid, sta->mesh->plid, 9798c2ecf20Sopenharmony_ci sta->mesh->reason); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci /* also send confirm in open case */ 9828c2ecf20Sopenharmony_ci if (action == WLAN_SP_MESH_PEERING_OPEN) { 9838c2ecf20Sopenharmony_ci mesh_plink_frame_tx(sdata, sta, 9848c2ecf20Sopenharmony_ci WLAN_SP_MESH_PEERING_CONFIRM, 9858c2ecf20Sopenharmony_ci sta->sta.addr, sta->mesh->llid, 9868c2ecf20Sopenharmony_ci sta->mesh->plid, 0); 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return changed; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci/* 9948c2ecf20Sopenharmony_ci * mesh_plink_get_event - get correct MPM event 9958c2ecf20Sopenharmony_ci * 9968c2ecf20Sopenharmony_ci * @sdata: interface 9978c2ecf20Sopenharmony_ci * @sta: peer, leave NULL if processing a frame from a new suitable peer 9988c2ecf20Sopenharmony_ci * @elems: peering management IEs 9998c2ecf20Sopenharmony_ci * @ftype: frame type 10008c2ecf20Sopenharmony_ci * @llid: peer's peer link ID 10018c2ecf20Sopenharmony_ci * @plid: peer's local link ID 10028c2ecf20Sopenharmony_ci * 10038c2ecf20Sopenharmony_ci * Return: new peering event for @sta, but PLINK_UNDEFINED should be treated as 10048c2ecf20Sopenharmony_ci * an error. 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_cistatic enum plink_event 10078c2ecf20Sopenharmony_cimesh_plink_get_event(struct ieee80211_sub_if_data *sdata, 10088c2ecf20Sopenharmony_ci struct sta_info *sta, 10098c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems, 10108c2ecf20Sopenharmony_ci enum ieee80211_self_protected_actioncode ftype, 10118c2ecf20Sopenharmony_ci u16 llid, u16 plid) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci enum plink_event event = PLINK_UNDEFINED; 10148c2ecf20Sopenharmony_ci u8 ie_len = elems->peering_len; 10158c2ecf20Sopenharmony_ci bool matches_local; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci matches_local = (ftype == WLAN_SP_MESH_PEERING_CLOSE || 10188c2ecf20Sopenharmony_ci mesh_matches_local(sdata, elems)); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* deny open request from non-matching peer */ 10218c2ecf20Sopenharmony_ci if (!matches_local && !sta) { 10228c2ecf20Sopenharmony_ci event = OPN_RJCT; 10238c2ecf20Sopenharmony_ci goto out; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (!sta) { 10278c2ecf20Sopenharmony_ci if (ftype != WLAN_SP_MESH_PEERING_OPEN) { 10288c2ecf20Sopenharmony_ci mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n"); 10298c2ecf20Sopenharmony_ci goto out; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci /* ftype == WLAN_SP_MESH_PEERING_OPEN */ 10328c2ecf20Sopenharmony_ci if (!mesh_plink_free_count(sdata)) { 10338c2ecf20Sopenharmony_ci mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); 10348c2ecf20Sopenharmony_ci goto out; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* new matching peer */ 10388c2ecf20Sopenharmony_ci event = OPN_ACPT; 10398c2ecf20Sopenharmony_ci goto out; 10408c2ecf20Sopenharmony_ci } else { 10418c2ecf20Sopenharmony_ci if (!test_sta_flag(sta, WLAN_STA_AUTH)) { 10428c2ecf20Sopenharmony_ci mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); 10438c2ecf20Sopenharmony_ci goto out; 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci if (sta->mesh->plink_state == NL80211_PLINK_BLOCKED) 10468c2ecf20Sopenharmony_ci goto out; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci switch (ftype) { 10508c2ecf20Sopenharmony_ci case WLAN_SP_MESH_PEERING_OPEN: 10518c2ecf20Sopenharmony_ci if (!matches_local) 10528c2ecf20Sopenharmony_ci event = OPN_RJCT; 10538c2ecf20Sopenharmony_ci else if (!mesh_plink_free_count(sdata) || 10548c2ecf20Sopenharmony_ci (sta->mesh->plid && sta->mesh->plid != plid)) 10558c2ecf20Sopenharmony_ci event = OPN_IGNR; 10568c2ecf20Sopenharmony_ci else 10578c2ecf20Sopenharmony_ci event = OPN_ACPT; 10588c2ecf20Sopenharmony_ci break; 10598c2ecf20Sopenharmony_ci case WLAN_SP_MESH_PEERING_CONFIRM: 10608c2ecf20Sopenharmony_ci if (!matches_local) 10618c2ecf20Sopenharmony_ci event = CNF_RJCT; 10628c2ecf20Sopenharmony_ci else if (!mesh_plink_free_count(sdata) || 10638c2ecf20Sopenharmony_ci sta->mesh->llid != llid || 10648c2ecf20Sopenharmony_ci (sta->mesh->plid && sta->mesh->plid != plid)) 10658c2ecf20Sopenharmony_ci event = CNF_IGNR; 10668c2ecf20Sopenharmony_ci else 10678c2ecf20Sopenharmony_ci event = CNF_ACPT; 10688c2ecf20Sopenharmony_ci break; 10698c2ecf20Sopenharmony_ci case WLAN_SP_MESH_PEERING_CLOSE: 10708c2ecf20Sopenharmony_ci if (sta->mesh->plink_state == NL80211_PLINK_ESTAB) 10718c2ecf20Sopenharmony_ci /* Do not check for llid or plid. This does not 10728c2ecf20Sopenharmony_ci * follow the standard but since multiple plinks 10738c2ecf20Sopenharmony_ci * per sta are not supported, it is necessary in 10748c2ecf20Sopenharmony_ci * order to avoid a livelock when MP A sees an 10758c2ecf20Sopenharmony_ci * establish peer link to MP B but MP B does not 10768c2ecf20Sopenharmony_ci * see it. This can be caused by a timeout in 10778c2ecf20Sopenharmony_ci * B's peer link establishment or B beign 10788c2ecf20Sopenharmony_ci * restarted. 10798c2ecf20Sopenharmony_ci */ 10808c2ecf20Sopenharmony_ci event = CLS_ACPT; 10818c2ecf20Sopenharmony_ci else if (sta->mesh->plid != plid) 10828c2ecf20Sopenharmony_ci event = CLS_IGNR; 10838c2ecf20Sopenharmony_ci else if (ie_len == 8 && sta->mesh->llid != llid) 10848c2ecf20Sopenharmony_ci event = CLS_IGNR; 10858c2ecf20Sopenharmony_ci else 10868c2ecf20Sopenharmony_ci event = CLS_ACPT; 10878c2ecf20Sopenharmony_ci break; 10888c2ecf20Sopenharmony_ci default: 10898c2ecf20Sopenharmony_ci mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n"); 10908c2ecf20Sopenharmony_ci break; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ciout: 10948c2ecf20Sopenharmony_ci return event; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic void 10988c2ecf20Sopenharmony_cimesh_process_plink_frame(struct ieee80211_sub_if_data *sdata, 10998c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt, 11008c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems, 11018c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rx_status) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci struct sta_info *sta; 11058c2ecf20Sopenharmony_ci enum plink_event event; 11068c2ecf20Sopenharmony_ci enum ieee80211_self_protected_actioncode ftype; 11078c2ecf20Sopenharmony_ci u32 changed = 0; 11088c2ecf20Sopenharmony_ci u8 ie_len = elems->peering_len; 11098c2ecf20Sopenharmony_ci u16 plid, llid = 0; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (!elems->peering) { 11128c2ecf20Sopenharmony_ci mpl_dbg(sdata, 11138c2ecf20Sopenharmony_ci "Mesh plink: missing necessary peer link ie\n"); 11148c2ecf20Sopenharmony_ci return; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci if (elems->rsn_len && 11188c2ecf20Sopenharmony_ci sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { 11198c2ecf20Sopenharmony_ci mpl_dbg(sdata, 11208c2ecf20Sopenharmony_ci "Mesh plink: can't establish link with secure peer\n"); 11218c2ecf20Sopenharmony_ci return; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci ftype = mgmt->u.action.u.self_prot.action_code; 11258c2ecf20Sopenharmony_ci if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || 11268c2ecf20Sopenharmony_ci (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || 11278c2ecf20Sopenharmony_ci (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 11288c2ecf20Sopenharmony_ci && ie_len != 8)) { 11298c2ecf20Sopenharmony_ci mpl_dbg(sdata, 11308c2ecf20Sopenharmony_ci "Mesh plink: incorrect plink ie length %d %d\n", 11318c2ecf20Sopenharmony_ci ftype, ie_len); 11328c2ecf20Sopenharmony_ci return; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (ftype != WLAN_SP_MESH_PEERING_CLOSE && 11368c2ecf20Sopenharmony_ci (!elems->mesh_id || !elems->mesh_config)) { 11378c2ecf20Sopenharmony_ci mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); 11388c2ecf20Sopenharmony_ci return; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci /* Note the lines below are correct, the llid in the frame is the plid 11418c2ecf20Sopenharmony_ci * from the point of view of this host. 11428c2ecf20Sopenharmony_ci */ 11438c2ecf20Sopenharmony_ci plid = get_unaligned_le16(PLINK_GET_LLID(elems->peering)); 11448c2ecf20Sopenharmony_ci if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || 11458c2ecf20Sopenharmony_ci (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) 11468c2ecf20Sopenharmony_ci llid = get_unaligned_le16(PLINK_GET_PLID(elems->peering)); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci /* WARNING: Only for sta pointer, is dropped & re-acquired */ 11498c2ecf20Sopenharmony_ci rcu_read_lock(); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci sta = sta_info_get(sdata, mgmt->sa); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (ftype == WLAN_SP_MESH_PEERING_OPEN && 11548c2ecf20Sopenharmony_ci !rssi_threshold_check(sdata, sta)) { 11558c2ecf20Sopenharmony_ci mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n", 11568c2ecf20Sopenharmony_ci mgmt->sa); 11578c2ecf20Sopenharmony_ci goto unlock_rcu; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci /* Now we will figure out the appropriate event... */ 11618c2ecf20Sopenharmony_ci event = mesh_plink_get_event(sdata, sta, elems, ftype, llid, plid); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (event == OPN_ACPT) { 11648c2ecf20Sopenharmony_ci rcu_read_unlock(); 11658c2ecf20Sopenharmony_ci /* allocate sta entry if necessary and update info */ 11668c2ecf20Sopenharmony_ci sta = mesh_sta_info_get(sdata, mgmt->sa, elems, rx_status); 11678c2ecf20Sopenharmony_ci if (!sta) { 11688c2ecf20Sopenharmony_ci mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); 11698c2ecf20Sopenharmony_ci goto unlock_rcu; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci sta->mesh->plid = plid; 11728c2ecf20Sopenharmony_ci } else if (!sta && event == OPN_RJCT) { 11738c2ecf20Sopenharmony_ci mesh_plink_frame_tx(sdata, NULL, WLAN_SP_MESH_PEERING_CLOSE, 11748c2ecf20Sopenharmony_ci mgmt->sa, 0, plid, 11758c2ecf20Sopenharmony_ci WLAN_REASON_MESH_CONFIG); 11768c2ecf20Sopenharmony_ci goto unlock_rcu; 11778c2ecf20Sopenharmony_ci } else if (!sta || event == PLINK_UNDEFINED) { 11788c2ecf20Sopenharmony_ci /* something went wrong */ 11798c2ecf20Sopenharmony_ci goto unlock_rcu; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci if (event == CNF_ACPT) { 11838c2ecf20Sopenharmony_ci /* 802.11-2012 13.3.7.2 - update plid on CNF if not set */ 11848c2ecf20Sopenharmony_ci if (!sta->mesh->plid) 11858c2ecf20Sopenharmony_ci sta->mesh->plid = plid; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci sta->mesh->aid = get_unaligned_le16(PLINK_CNF_AID(mgmt)); 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci changed |= mesh_plink_fsm(sdata, sta, event); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ciunlock_rcu: 11938c2ecf20Sopenharmony_ci rcu_read_unlock(); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci if (changed) 11968c2ecf20Sopenharmony_ci ieee80211_mbss_info_change_notify(sdata, changed); 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_civoid mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, 12008c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt, size_t len, 12018c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rx_status) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci struct ieee802_11_elems elems; 12048c2ecf20Sopenharmony_ci size_t baselen; 12058c2ecf20Sopenharmony_ci u8 *baseaddr; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci /* need action_code, aux */ 12088c2ecf20Sopenharmony_ci if (len < IEEE80211_MIN_ACTION_SIZE + 3) 12098c2ecf20Sopenharmony_ci return; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (sdata->u.mesh.user_mpm) 12128c2ecf20Sopenharmony_ci /* userspace must register for these */ 12138c2ecf20Sopenharmony_ci return; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (is_multicast_ether_addr(mgmt->da)) { 12168c2ecf20Sopenharmony_ci mpl_dbg(sdata, 12178c2ecf20Sopenharmony_ci "Mesh plink: ignore frame from multicast address\n"); 12188c2ecf20Sopenharmony_ci return; 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci baseaddr = mgmt->u.action.u.self_prot.variable; 12228c2ecf20Sopenharmony_ci baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; 12238c2ecf20Sopenharmony_ci if (mgmt->u.action.u.self_prot.action_code == 12248c2ecf20Sopenharmony_ci WLAN_SP_MESH_PEERING_CONFIRM) { 12258c2ecf20Sopenharmony_ci baseaddr += 4; 12268c2ecf20Sopenharmony_ci baselen += 4; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (baselen > len) 12298c2ecf20Sopenharmony_ci return; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems, 12328c2ecf20Sopenharmony_ci mgmt->bssid, NULL); 12338c2ecf20Sopenharmony_ci mesh_process_plink_frame(sdata, mgmt, &elems, rx_status); 12348c2ecf20Sopenharmony_ci} 1235