mirror of
https://github.com/Mange/rtl8192eu-linux-driver
synced 2024-11-01 03:05:34 +00:00
2a467a7923
Convert all rtw_zvmalloc calls within the driver to use the existing kernel vzalloc function, which has the same semantics. Also rewrite the two places where it is mentioned in comments to say vzalloc, and remove the redundant cast to struct adapter * in ./os_dep/usb_intf.c as vzalloc returns void *. The reason for the conversion is that rtw_zvmalloc is just a preprocessor definition for _rtw_zvmalloc which itself is just an inline wrapper around vmalloc which then zeroes the memory out. As vzalloc does the same thing via usage of __GFP_ZERO, this code is redundant and can subsequently be removed. Link: https://lore.kernel.org/r/20210818234853.208448-5-phil@philpotter.co.uk
4079 lines
110 KiB
C
4079 lines
110 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright(c) 2007 - 2017 Realtek Corporation.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
*****************************************************************************/
|
|
#define _RTW_MESH_C_
|
|
|
|
#ifdef CONFIG_RTW_MESH
|
|
#include <drv_types.h>
|
|
|
|
const char *_rtw_mesh_plink_str[] = {
|
|
"UNKNOWN",
|
|
"LISTEN",
|
|
"OPN_SNT",
|
|
"OPN_RCVD",
|
|
"CNF_RCVD",
|
|
"ESTAB",
|
|
"HOLDING",
|
|
"BLOCKED",
|
|
};
|
|
|
|
const char *_rtw_mesh_ps_str[] = {
|
|
"UNKNOWN",
|
|
"ACTIVE",
|
|
"LSLEEP",
|
|
"DSLEEP",
|
|
};
|
|
|
|
const char *_action_self_protected_str[] = {
|
|
"ACT_SELF_PROTECTED_RSVD",
|
|
"MESH_OPEN",
|
|
"MESH_CONF",
|
|
"MESH_CLOSE",
|
|
"MESH_GK_INFORM",
|
|
"MESH_GK_ACK",
|
|
};
|
|
|
|
inline u8 *rtw_set_ie_mesh_id(u8 *buf, u32 *buf_len, const char *mesh_id, u8 id_len)
|
|
{
|
|
return rtw_set_ie(buf, WLAN_EID_MESH_ID, id_len, mesh_id, buf_len);
|
|
}
|
|
|
|
inline u8 *rtw_set_ie_mesh_config(u8 *buf, u32 *buf_len
|
|
, u8 path_sel_proto, u8 path_sel_metric, u8 congest_ctl_mode, u8 sync_method, u8 auth_proto
|
|
, u8 num_of_peerings, bool cto_mgate, bool cto_as
|
|
, bool accept_peerings, bool mcca_sup, bool mcca_en, bool forwarding
|
|
, bool mbca_en, bool tbtt_adj, bool ps_level)
|
|
{
|
|
|
|
u8 conf[7] = {0};
|
|
|
|
SET_MESH_CONF_ELE_PATH_SEL_PROTO_ID(conf, path_sel_proto);
|
|
SET_MESH_CONF_ELE_PATH_SEL_METRIC_ID(conf, path_sel_metric);
|
|
SET_MESH_CONF_ELE_CONGEST_CTRL_MODE_ID(conf, congest_ctl_mode);
|
|
SET_MESH_CONF_ELE_SYNC_METHOD_ID(conf, sync_method);
|
|
SET_MESH_CONF_ELE_AUTH_PROTO_ID(conf, auth_proto);
|
|
|
|
SET_MESH_CONF_ELE_CTO_MGATE(conf, cto_mgate);
|
|
SET_MESH_CONF_ELE_NUM_OF_PEERINGS(conf, num_of_peerings);
|
|
SET_MESH_CONF_ELE_CTO_AS(conf, cto_as);
|
|
|
|
SET_MESH_CONF_ELE_ACCEPT_PEERINGS(conf, accept_peerings);
|
|
SET_MESH_CONF_ELE_MCCA_SUP(conf, mcca_sup);
|
|
SET_MESH_CONF_ELE_MCCA_EN(conf, mcca_en);
|
|
SET_MESH_CONF_ELE_FORWARDING(conf, forwarding);
|
|
SET_MESH_CONF_ELE_MBCA_EN(conf, mbca_en);
|
|
SET_MESH_CONF_ELE_TBTT_ADJ(conf, tbtt_adj);
|
|
SET_MESH_CONF_ELE_PS_LEVEL(conf, ps_level);
|
|
|
|
return rtw_set_ie(buf, WLAN_EID_MESH_CONFIG, 7, conf, buf_len);
|
|
}
|
|
|
|
inline u8 *rtw_set_ie_mpm(u8 *buf, u32 *buf_len
|
|
, u8 proto_id, u16 llid, u16 *plid, u16 *reason, u8 *chosen_pmk)
|
|
{
|
|
u8 data[24] = {0};
|
|
u8 *pos = data;
|
|
|
|
RTW_PUT_LE16(pos, proto_id);
|
|
pos += 2;
|
|
|
|
RTW_PUT_LE16(pos, llid);
|
|
pos += 2;
|
|
|
|
if (plid) {
|
|
RTW_PUT_LE16(pos, *plid);
|
|
pos += 2;
|
|
}
|
|
|
|
if (reason) {
|
|
RTW_PUT_LE16(pos, *reason);
|
|
pos += 2;
|
|
}
|
|
|
|
if (chosen_pmk) {
|
|
_rtw_memcpy(pos, chosen_pmk, 16);
|
|
pos += 16;
|
|
}
|
|
|
|
return rtw_set_ie(buf, WLAN_EID_MPM, pos - data, data, buf_len);
|
|
}
|
|
|
|
bool rtw_bss_is_forwarding(WLAN_BSSID_EX *bss)
|
|
{
|
|
u8 *ie;
|
|
int ie_len;
|
|
bool ret = 0;
|
|
|
|
ie = rtw_get_ie(BSS_EX_TLV_IES(bss), WLAN_EID_MESH_CONFIG, &ie_len,
|
|
BSS_EX_TLV_IES_LEN(bss));
|
|
if (!ie || ie_len != 7)
|
|
goto exit;
|
|
|
|
ret = GET_MESH_CONF_ELE_FORWARDING(ie + 2);
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
bool rtw_bss_is_cto_mgate(WLAN_BSSID_EX *bss)
|
|
{
|
|
u8 *ie;
|
|
int ie_len;
|
|
bool ret = 0;
|
|
|
|
ie = rtw_get_ie(BSS_EX_TLV_IES(bss), WLAN_EID_MESH_CONFIG, &ie_len,
|
|
BSS_EX_TLV_IES_LEN(bss));
|
|
if (!ie || ie_len != 7)
|
|
goto exit;
|
|
|
|
ret = GET_MESH_CONF_ELE_CTO_MGATE(ie + 2);
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
int rtw_bss_is_same_mbss(WLAN_BSSID_EX *a, WLAN_BSSID_EX *b)
|
|
{
|
|
int ret = 0;
|
|
u8 *a_mconf_ie, *b_mconf_ie;
|
|
sint a_mconf_ie_len, b_mconf_ie_len;
|
|
|
|
if (a->InfrastructureMode != Ndis802_11_mesh)
|
|
goto exit;
|
|
a_mconf_ie = rtw_get_ie(BSS_EX_TLV_IES(a), WLAN_EID_MESH_CONFIG, &a_mconf_ie_len, BSS_EX_TLV_IES_LEN(a));
|
|
if (!a_mconf_ie || a_mconf_ie_len != 7)
|
|
goto exit;
|
|
if (b->InfrastructureMode != Ndis802_11_mesh)
|
|
goto exit;
|
|
b_mconf_ie = rtw_get_ie(BSS_EX_TLV_IES(b), WLAN_EID_MESH_CONFIG, &b_mconf_ie_len, BSS_EX_TLV_IES_LEN(b));
|
|
if (!b_mconf_ie || b_mconf_ie_len != 7)
|
|
goto exit;
|
|
|
|
if (a->mesh_id.SsidLength != b->mesh_id.SsidLength
|
|
|| _rtw_memcmp(a->mesh_id.Ssid, b->mesh_id.Ssid, a->mesh_id.SsidLength) == _FALSE)
|
|
goto exit;
|
|
|
|
if (_rtw_memcmp(a_mconf_ie + 2, b_mconf_ie + 2, 5) == _FALSE)
|
|
goto exit;
|
|
|
|
ret = 1;
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
int rtw_bss_is_candidate_mesh_peer(WLAN_BSSID_EX *self, WLAN_BSSID_EX *target, u8 ch, u8 add_peer)
|
|
{
|
|
int ret = 0;
|
|
u8 *mconf_ie;
|
|
sint mconf_ie_len;
|
|
int i, j;
|
|
|
|
if (!rtw_bss_is_same_mbss(self, target))
|
|
goto exit;
|
|
|
|
if (ch && self->Configuration.DSConfig != target->Configuration.DSConfig)
|
|
goto exit;
|
|
|
|
if (add_peer) {
|
|
/* Accept additional mesh peerings */
|
|
mconf_ie = rtw_get_ie(BSS_EX_TLV_IES(target), WLAN_EID_MESH_CONFIG, &mconf_ie_len, BSS_EX_TLV_IES_LEN(target));
|
|
if (!mconf_ie || mconf_ie_len != 7)
|
|
goto exit;
|
|
if (GET_MESH_CONF_ELE_ACCEPT_PEERINGS(mconf_ie + 2) == 0)
|
|
goto exit;
|
|
}
|
|
|
|
/* BSSBasicRateSet */
|
|
for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) {
|
|
if (target->SupportedRates[i] == 0)
|
|
break;
|
|
if (target->SupportedRates[i] & 0x80) {
|
|
u8 match = 0;
|
|
|
|
if (!ch) {
|
|
/* off-channel, check target with our hardcode capability */
|
|
if (target->Configuration.DSConfig > 14)
|
|
match = rtw_is_basic_rate_ofdm(target->SupportedRates[i]);
|
|
else
|
|
match = rtw_is_basic_rate_mix(target->SupportedRates[i]);
|
|
} else {
|
|
for (j = 0; j < NDIS_802_11_LENGTH_RATES_EX; j++) {
|
|
if (self->SupportedRates[j] == 0)
|
|
break;
|
|
if (self->SupportedRates[j] == target->SupportedRates[i]) {
|
|
match = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!match)
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
|
|
/* BSSBasicMCSSet */
|
|
|
|
/* 802.1X connected to AS ? */
|
|
|
|
ret = 1;
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
void rtw_mesh_bss_peering_status(WLAN_BSSID_EX *bss, u8 *nop, u8 *accept)
|
|
{
|
|
u8 *ie;
|
|
int ie_len;
|
|
|
|
if (nop)
|
|
*nop = 0;
|
|
if (accept)
|
|
*accept = 0;
|
|
|
|
ie = rtw_get_ie(BSS_EX_TLV_IES(bss), WLAN_EID_MESH_CONFIG, &ie_len,
|
|
BSS_EX_TLV_IES_LEN(bss));
|
|
if (!ie || ie_len != 7)
|
|
goto exit;
|
|
|
|
if (nop)
|
|
*nop = GET_MESH_CONF_ELE_NUM_OF_PEERINGS(ie + 2);
|
|
if (accept)
|
|
*accept = GET_MESH_CONF_ELE_ACCEPT_PEERINGS(ie + 2);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
void rtw_mesh_update_scanned_acnode_status(_adapter *adapter, struct wlan_network *scanned)
|
|
{
|
|
bool acnode;
|
|
u8 nop, accept;
|
|
|
|
rtw_mesh_bss_peering_status(&scanned->network, &nop, &accept);
|
|
|
|
acnode = !nop && accept;
|
|
|
|
if (acnode && scanned->acnode_stime == 0) {
|
|
scanned->acnode_stime = rtw_get_current_time();
|
|
if (scanned->acnode_stime == 0)
|
|
scanned->acnode_stime++;
|
|
} else if (!acnode) {
|
|
scanned->acnode_stime = 0;
|
|
scanned->acnode_notify_etime = 0;
|
|
}
|
|
}
|
|
|
|
bool rtw_mesh_scanned_is_acnode_confirmed(_adapter *adapter, struct wlan_network *scanned)
|
|
{
|
|
return scanned->acnode_stime
|
|
&& rtw_get_passing_time_ms(scanned->acnode_stime)
|
|
> adapter->mesh_cfg.peer_sel_policy.acnode_conf_timeout_ms;
|
|
}
|
|
|
|
static bool rtw_mesh_scanned_is_acnode_allow_notify(_adapter *adapter, struct wlan_network *scanned)
|
|
{
|
|
return scanned->acnode_notify_etime
|
|
&& rtw_time_after(scanned->acnode_notify_etime, rtw_get_current_time());
|
|
}
|
|
|
|
bool rtw_mesh_acnode_prevent_allow_sacrifice(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
bool allow = 0;
|
|
|
|
if (!mcfg->peer_sel_policy.acnode_prevent
|
|
|| mcfg->max_peer_links <= 1
|
|
|| stapriv->asoc_list_cnt < mcfg->max_peer_links)
|
|
goto exit;
|
|
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
if (rtw_mesh_cto_mgate_required(adapter))
|
|
goto exit;
|
|
#endif
|
|
|
|
allow = 1;
|
|
|
|
exit:
|
|
return allow;
|
|
}
|
|
|
|
static bool rtw_mesh_acnode_candidate_exist(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
struct mlme_priv *mlme = &adapter->mlmepriv;
|
|
_queue *queue = &(mlme->scanned_queue);
|
|
_list *head, *list;
|
|
_irqL irqL;
|
|
struct wlan_network *scanned = NULL;
|
|
struct sta_info *sta = NULL;
|
|
bool need = 0;
|
|
|
|
_enter_critical_bh(&(mlme->scanned_queue.lock), &irqL);
|
|
|
|
head = get_list_head(queue);
|
|
list = get_next(head);
|
|
while (!rtw_end_of_queue_search(head, list)) {
|
|
scanned = LIST_CONTAINOR(list, struct wlan_network, list);
|
|
list = get_next(list);
|
|
|
|
if (rtw_get_passing_time_ms(scanned->last_scanned) < mcfg->peer_sel_policy.scanr_exp_ms
|
|
&& rtw_mesh_scanned_is_acnode_confirmed(adapter, scanned)
|
|
&& (!mcfg->rssi_threshold || mcfg->rssi_threshold <= scanned->network.Rssi)
|
|
#if CONFIG_RTW_MACADDR_ACL
|
|
&& rtw_access_ctrl(adapter, scanned->network.MacAddress) == _TRUE
|
|
#endif
|
|
&& rtw_bss_is_candidate_mesh_peer(&mlme->cur_network.network, &scanned->network, 1, 1)
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
&& !rtw_mesh_peer_blacklist_search(adapter, scanned->network.MacAddress)
|
|
#endif
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
&& rtw_mesh_cto_mgate_network_filter(adapter, scanned)
|
|
#endif
|
|
) {
|
|
need = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
_exit_critical_bh(&(mlme->scanned_queue.lock), &irqL);
|
|
|
|
exit:
|
|
return need;
|
|
}
|
|
|
|
static int rtw_mesh_acnode_prevent_sacrifice_chk(_adapter *adapter, struct sta_info **sac, struct sta_info *com)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
int updated = 0;
|
|
|
|
/*
|
|
* TODO: compare next_hop reference cnt of forwarding info
|
|
* don't sacrifice working next_hop or choose sta with least cnt
|
|
*/
|
|
|
|
if (*sac == NULL) {
|
|
updated = 1;
|
|
goto exit;
|
|
}
|
|
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
if (mcfg->peer_sel_policy.cto_mgate_require
|
|
&& !mcfg->dot11MeshGateAnnouncementProtocol
|
|
) {
|
|
if (IS_CTO_MGATE_CONF_TIMEOUT(com->plink)) {
|
|
if (!IS_CTO_MGATE_CONF_TIMEOUT((*sac)->plink)) {
|
|
/* blacklist > not blacklist */
|
|
updated = 1;
|
|
goto exit;
|
|
}
|
|
} else if (!IS_CTO_MGATE_CONF_DISABLED(com->plink)) {
|
|
if (IS_CTO_MGATE_CONF_DISABLED((*sac)->plink)) {
|
|
/* confirming > disabled */
|
|
updated = 1;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
exit:
|
|
if (updated)
|
|
*sac = com;
|
|
|
|
return updated;
|
|
}
|
|
|
|
struct sta_info *_rtw_mesh_acnode_prevent_pick_sacrifice(_adapter *adapter)
|
|
{
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
_list *head, *list;
|
|
struct sta_info *sta, *sacrifice = NULL;
|
|
u8 nop;
|
|
|
|
head = &stapriv->asoc_list;
|
|
list = get_next(head);
|
|
while (rtw_end_of_queue_search(head, list) == _FALSE) {
|
|
sta = LIST_CONTAINOR(list, struct sta_info, asoc_list);
|
|
list = get_next(list);
|
|
|
|
if (!sta->plink || !sta->plink->scanned) {
|
|
rtw_warn_on(1);
|
|
continue;
|
|
}
|
|
|
|
rtw_mesh_bss_peering_status(&sta->plink->scanned->network, &nop, NULL);
|
|
if (nop < 2)
|
|
continue;
|
|
|
|
rtw_mesh_acnode_prevent_sacrifice_chk(adapter, &sacrifice, sta);
|
|
}
|
|
|
|
return sacrifice;
|
|
}
|
|
|
|
struct sta_info *rtw_mesh_acnode_prevent_pick_sacrifice(_adapter *adapter)
|
|
{
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
struct sta_info *sacrifice = NULL;
|
|
|
|
enter_critical_bh(&stapriv->asoc_list_lock);
|
|
|
|
sacrifice = _rtw_mesh_acnode_prevent_pick_sacrifice(adapter);
|
|
|
|
exit_critical_bh(&stapriv->asoc_list_lock);
|
|
|
|
return sacrifice;
|
|
}
|
|
|
|
static void rtw_mesh_acnode_rsvd_chk(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
u8 acnode_rsvd = 0;
|
|
|
|
if (rtw_mesh_acnode_prevent_allow_sacrifice(adapter)
|
|
&& rtw_mesh_acnode_prevent_pick_sacrifice(adapter)
|
|
&& rtw_mesh_acnode_candidate_exist(adapter))
|
|
acnode_rsvd = 1;
|
|
|
|
if (plink_ctl->acnode_rsvd != acnode_rsvd) {
|
|
plink_ctl->acnode_rsvd = acnode_rsvd;
|
|
RTW_INFO(FUNC_ADPT_FMT" acnode_rsvd = %d\n", FUNC_ADPT_ARG(adapter), plink_ctl->acnode_rsvd);
|
|
update_beacon(adapter, WLAN_EID_MESH_CONFIG, NULL, 1);
|
|
}
|
|
}
|
|
|
|
static void rtw_mesh_acnode_set_notify_etime(_adapter *adapter, u8 *rframe_whdr)
|
|
{
|
|
if (adapter->mesh_info.plink_ctl.acnode_rsvd) {
|
|
struct wlan_network *scanned = rtw_find_network(&adapter->mlmepriv.scanned_queue, get_addr2_ptr(rframe_whdr));
|
|
|
|
if (rtw_mesh_scanned_is_acnode_confirmed(adapter, scanned)) {
|
|
scanned->acnode_notify_etime = rtw_get_current_time()
|
|
+ rtw_ms_to_systime(adapter->mesh_cfg.peer_sel_policy.acnode_notify_timeout_ms);
|
|
if (scanned->acnode_notify_etime == 0)
|
|
scanned->acnode_notify_etime++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void dump_mesh_acnode_prevent_settings(void *sel, _adapter *adapter)
|
|
{
|
|
struct mesh_peer_sel_policy *peer_sel_policy = &adapter->mesh_cfg.peer_sel_policy;
|
|
|
|
RTW_PRINT_SEL(sel, "%-6s %-12s %-14s\n"
|
|
, "enable", "conf_timeout", "nofity_timeout");
|
|
RTW_PRINT_SEL(sel, "%6u %12u %14u\n"
|
|
, peer_sel_policy->acnode_prevent
|
|
, peer_sel_policy->acnode_conf_timeout_ms
|
|
, peer_sel_policy->acnode_notify_timeout_ms);
|
|
}
|
|
#endif /* CONFIG_RTW_MESH_ACNODE_PREVENT */
|
|
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
int rtw_mesh_peer_blacklist_add(_adapter *adapter, const u8 *addr)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
return rtw_blacklist_add(&plink_ctl->peer_blacklist, addr
|
|
, mcfg->peer_sel_policy.peer_blacklist_timeout_ms);
|
|
}
|
|
|
|
int rtw_mesh_peer_blacklist_del(_adapter *adapter, const u8 *addr)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
return rtw_blacklist_del(&plink_ctl->peer_blacklist, addr);
|
|
}
|
|
|
|
int rtw_mesh_peer_blacklist_search(_adapter *adapter, const u8 *addr)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
return rtw_blacklist_search(&plink_ctl->peer_blacklist, addr);
|
|
}
|
|
|
|
void rtw_mesh_peer_blacklist_flush(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
rtw_blacklist_flush(&plink_ctl->peer_blacklist);
|
|
}
|
|
|
|
void dump_mesh_peer_blacklist(void *sel, _adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
dump_blacklist(sel, &plink_ctl->peer_blacklist, "blacklist");
|
|
}
|
|
|
|
void dump_mesh_peer_blacklist_settings(void *sel, _adapter *adapter)
|
|
{
|
|
struct mesh_peer_sel_policy *peer_sel_policy = &adapter->mesh_cfg.peer_sel_policy;
|
|
|
|
RTW_PRINT_SEL(sel, "%-12s %-17s\n"
|
|
, "conf_timeout", "blacklist_timeout");
|
|
RTW_PRINT_SEL(sel, "%12u %17u\n"
|
|
, peer_sel_policy->peer_conf_timeout_ms
|
|
, peer_sel_policy->peer_blacklist_timeout_ms);
|
|
}
|
|
#endif /* CONFIG_RTW_MESH_PEER_BLACKLIST */
|
|
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
u8 rtw_mesh_cto_mgate_required(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct mlme_ext_priv *mlmeext = &adapter->mlmeextpriv;
|
|
|
|
return mcfg->peer_sel_policy.cto_mgate_require
|
|
&& !rtw_bss_is_cto_mgate(&(mlmeext->mlmext_info.network));
|
|
}
|
|
|
|
u8 rtw_mesh_cto_mgate_network_filter(_adapter *adapter, struct wlan_network *scanned)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct mlme_ext_priv *mlmeext = &adapter->mlmeextpriv;
|
|
|
|
return !rtw_mesh_cto_mgate_required(adapter)
|
|
|| (rtw_bss_is_cto_mgate(&scanned->network)
|
|
&& !rtw_mesh_cto_mgate_blacklist_search(adapter, scanned->network.MacAddress));
|
|
}
|
|
|
|
int rtw_mesh_cto_mgate_blacklist_add(_adapter *adapter, const u8 *addr)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
return rtw_blacklist_add(&plink_ctl->cto_mgate_blacklist, addr
|
|
, mcfg->peer_sel_policy.cto_mgate_blacklist_timeout_ms);
|
|
}
|
|
|
|
int rtw_mesh_cto_mgate_blacklist_del(_adapter *adapter, const u8 *addr)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
return rtw_blacklist_del(&plink_ctl->cto_mgate_blacklist, addr);
|
|
}
|
|
|
|
int rtw_mesh_cto_mgate_blacklist_search(_adapter *adapter, const u8 *addr)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
return rtw_blacklist_search(&plink_ctl->cto_mgate_blacklist, addr);
|
|
}
|
|
|
|
void rtw_mesh_cto_mgate_blacklist_flush(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
rtw_blacklist_flush(&plink_ctl->cto_mgate_blacklist);
|
|
}
|
|
|
|
void dump_mesh_cto_mgate_blacklist(void *sel, _adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
dump_blacklist(sel, &plink_ctl->cto_mgate_blacklist, "blacklist");
|
|
}
|
|
|
|
void dump_mesh_cto_mgate_blacklist_settings(void *sel, _adapter *adapter)
|
|
{
|
|
struct mesh_peer_sel_policy *peer_sel_policy = &adapter->mesh_cfg.peer_sel_policy;
|
|
|
|
RTW_PRINT_SEL(sel, "%-12s %-17s\n"
|
|
, "conf_timeout", "blacklist_timeout");
|
|
RTW_PRINT_SEL(sel, "%12u %17u\n"
|
|
, peer_sel_policy->cto_mgate_conf_timeout_ms
|
|
, peer_sel_policy->cto_mgate_blacklist_timeout_ms);
|
|
}
|
|
|
|
static void rtw_mesh_cto_mgate_blacklist_chk(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
_queue *blist = &plink_ctl->cto_mgate_blacklist;
|
|
_list *list, *head;
|
|
struct blacklist_ent *ent = NULL;
|
|
struct wlan_network *scanned = NULL;
|
|
|
|
enter_critical_bh(&blist->lock);
|
|
head = &blist->queue;
|
|
list = get_next(head);
|
|
while (rtw_end_of_queue_search(head, list) == _FALSE) {
|
|
ent = LIST_CONTAINOR(list, struct blacklist_ent, list);
|
|
list = get_next(list);
|
|
|
|
if (rtw_time_after(rtw_get_current_time(), ent->exp_time)) {
|
|
rtw_list_delete(&ent->list);
|
|
rtw_mfree(ent, sizeof(struct blacklist_ent));
|
|
continue;
|
|
}
|
|
|
|
scanned = rtw_find_network(&adapter->mlmepriv.scanned_queue, ent->addr);
|
|
if (!scanned)
|
|
continue;
|
|
|
|
if (rtw_bss_is_forwarding(&scanned->network)) {
|
|
rtw_list_delete(&ent->list);
|
|
rtw_mfree(ent, sizeof(struct blacklist_ent));
|
|
}
|
|
}
|
|
|
|
exit_critical_bh(&blist->lock);
|
|
}
|
|
#endif /* CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST */
|
|
|
|
void rtw_chk_candidate_peer_notify(_adapter *adapter, struct wlan_network *scanned)
|
|
{
|
|
struct rf_ctl_t *rfctl = adapter_to_rfctl(adapter);
|
|
struct mlme_priv *mlme = &adapter->mlmepriv;
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
bool acnode = 0;
|
|
|
|
if (IS_CH_WAITING(rfctl) && !IS_UNDER_CAC(rfctl))
|
|
goto exit;
|
|
|
|
if (plink_ctl->num >= RTW_MESH_MAX_PEER_CANDIDATES)
|
|
goto exit;
|
|
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
if (plink_ctl->acnode_rsvd) {
|
|
acnode = rtw_mesh_scanned_is_acnode_confirmed(adapter, scanned);
|
|
if (acnode && !rtw_mesh_scanned_is_acnode_allow_notify(adapter, scanned))
|
|
goto exit;
|
|
}
|
|
#endif
|
|
|
|
/* wpa_supplicant's auto peer will initiate peering when candidate peer is reported without max_peer_links consideration */
|
|
if (plink_ctl->num >= mcfg->max_peer_links + acnode ? 1 : 0)
|
|
goto exit;
|
|
|
|
if (rtw_get_passing_time_ms(scanned->last_scanned) >= mcfg->peer_sel_policy.scanr_exp_ms
|
|
|| (mcfg->rssi_threshold && mcfg->rssi_threshold > scanned->network.Rssi)
|
|
|| !rtw_bss_is_candidate_mesh_peer(&mlme->cur_network.network, &scanned->network, 1, 1)
|
|
#if CONFIG_RTW_MACADDR_ACL
|
|
|| rtw_access_ctrl(adapter, scanned->network.MacAddress) == _FALSE
|
|
#endif
|
|
|| rtw_mesh_plink_get(adapter, scanned->network.MacAddress)
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
|| rtw_mesh_peer_blacklist_search(adapter, scanned->network.MacAddress)
|
|
#endif
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
|| !rtw_mesh_cto_mgate_network_filter(adapter, scanned)
|
|
#endif
|
|
)
|
|
goto exit;
|
|
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
if (acnode) {
|
|
scanned->acnode_notify_etime = 0;
|
|
RTW_INFO(FUNC_ADPT_FMT" acnode "MAC_FMT"\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(scanned->network.MacAddress));
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_IOCTL_CFG80211
|
|
rtw_cfg80211_notify_new_peer_candidate(adapter->rtw_wdev
|
|
, scanned->network.MacAddress
|
|
, BSS_EX_TLV_IES(&scanned->network)
|
|
, BSS_EX_TLV_IES_LEN(&scanned->network)
|
|
, GFP_ATOMIC
|
|
);
|
|
#endif
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void rtw_mesh_peer_status_chk(_adapter *adapter)
|
|
{
|
|
struct mlme_priv *mlme = &adapter->mlmepriv;
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *plink;
|
|
_list *head, *list;
|
|
struct sta_info *sta = NULL;
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
int stainfo_offset;
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
u8 cto_mgate, forwarding, mgate;
|
|
#endif
|
|
u8 flush;
|
|
s8 flush_list[NUM_STA];
|
|
u8 flush_num = 0;
|
|
int i;
|
|
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
if (rtw_mesh_cto_mgate_required(adapter)) {
|
|
/* active scan on operating channel */
|
|
issue_probereq_ex(adapter, &adapter->mlmepriv.cur_network.network.mesh_id, NULL, 0, 0, 0, 0);
|
|
}
|
|
#endif
|
|
|
|
enter_critical_bh(&(plink_ctl->lock));
|
|
|
|
/* check established peers */
|
|
enter_critical_bh(&stapriv->asoc_list_lock);
|
|
|
|
head = &stapriv->asoc_list;
|
|
list = get_next(head);
|
|
while (rtw_end_of_queue_search(head, list) == _FALSE) {
|
|
sta = LIST_CONTAINOR(list, struct sta_info, asoc_list);
|
|
list = get_next(list);
|
|
|
|
if (!sta->plink || !sta->plink->scanned) {
|
|
rtw_warn_on(1);
|
|
continue;
|
|
}
|
|
plink = sta->plink;
|
|
flush = 0;
|
|
|
|
/* remove unsuitable peer */
|
|
if (!rtw_bss_is_candidate_mesh_peer(&mlme->cur_network.network, &plink->scanned->network, 1, 0)
|
|
#if CONFIG_RTW_MACADDR_ACL
|
|
|| rtw_access_ctrl(adapter, plink->addr) == _FALSE
|
|
#endif
|
|
) {
|
|
flush = 1;
|
|
goto flush_add;
|
|
}
|
|
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
cto_mgate = rtw_bss_is_cto_mgate(&(plink->scanned->network));
|
|
forwarding = rtw_bss_is_forwarding(&(plink->scanned->network));
|
|
mgate = rtw_mesh_gate_search(minfo->mesh_paths, sta->cmn.mac_addr);
|
|
|
|
/* CTO_MGATE required, remove peer without CTO_MGATE */
|
|
if (rtw_mesh_cto_mgate_required(adapter) && !cto_mgate) {
|
|
flush = 1;
|
|
goto flush_add;
|
|
}
|
|
|
|
/* cto_mgate_conf status update */
|
|
if (IS_CTO_MGATE_CONF_DISABLED(plink)) {
|
|
if (cto_mgate && !forwarding && !mgate)
|
|
SET_CTO_MGATE_CONF_END_TIME(plink, mcfg->peer_sel_policy.cto_mgate_conf_timeout_ms);
|
|
else
|
|
rtw_mesh_cto_mgate_blacklist_del(adapter, sta->cmn.mac_addr);
|
|
} else {
|
|
/* cto_mgate_conf ongoing */
|
|
if (cto_mgate && !forwarding && !mgate) {
|
|
if (IS_CTO_MGATE_CONF_TIMEOUT(plink)) {
|
|
rtw_mesh_cto_mgate_blacklist_add(adapter, sta->cmn.mac_addr);
|
|
|
|
/* CTO_MGATE required, remove peering can't achieve CTO_MGATE */
|
|
if (rtw_mesh_cto_mgate_required(adapter)) {
|
|
flush = 1;
|
|
goto flush_add;
|
|
}
|
|
}
|
|
} else {
|
|
SET_CTO_MGATE_CONF_DISABLED(plink);
|
|
rtw_mesh_cto_mgate_blacklist_del(adapter, sta->cmn.mac_addr);
|
|
}
|
|
}
|
|
#endif /* CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST */
|
|
|
|
flush_add:
|
|
if (flush) {
|
|
rtw_list_delete(&sta->asoc_list);
|
|
stapriv->asoc_list_cnt--;
|
|
STA_SET_MESH_PLINK(sta, NULL);
|
|
|
|
stainfo_offset = rtw_stainfo_offset(stapriv, sta);
|
|
if (stainfo_offset_valid(stainfo_offset))
|
|
flush_list[flush_num++] = stainfo_offset;
|
|
else
|
|
rtw_warn_on(1);
|
|
}
|
|
}
|
|
|
|
exit_critical_bh(&stapriv->asoc_list_lock);
|
|
|
|
/* check non-established peers */
|
|
for (i = 0; i < RTW_MESH_MAX_PEER_CANDIDATES; i++) {
|
|
plink = &plink_ctl->ent[i];
|
|
if (plink->valid != _TRUE || plink->plink_state == RTW_MESH_PLINK_ESTAB)
|
|
continue;
|
|
|
|
/* remove unsuitable peer */
|
|
if (!rtw_bss_is_candidate_mesh_peer(&mlme->cur_network.network, &plink->scanned->network, 1, 1)
|
|
#if CONFIG_RTW_MACADDR_ACL
|
|
|| rtw_access_ctrl(adapter, plink->addr) == _FALSE
|
|
#endif
|
|
) {
|
|
_rtw_mesh_expire_peer_ent(adapter, plink);
|
|
continue;
|
|
}
|
|
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
/* peer confirm check timeout, add to black list */
|
|
if (IS_PEER_CONF_TIMEOUT(plink)) {
|
|
rtw_mesh_peer_blacklist_add(adapter, plink->addr);
|
|
_rtw_mesh_expire_peer_ent(adapter, plink);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
exit_critical_bh(&(plink_ctl->lock));
|
|
|
|
if (flush_num) {
|
|
u8 sta_addr[ETH_ALEN];
|
|
u8 updated = _FALSE;
|
|
|
|
for (i = 0; i < flush_num; i++) {
|
|
sta = rtw_get_stainfo_by_offset(stapriv, flush_list[i]);
|
|
_rtw_memcpy(sta_addr, sta->cmn.mac_addr, ETH_ALEN);
|
|
|
|
updated |= ap_free_sta(adapter, sta, _TRUE, WLAN_REASON_DEAUTH_LEAVING, _FALSE);
|
|
rtw_mesh_expire_peer(adapter, sta_addr);
|
|
}
|
|
|
|
associated_clients_update(adapter, updated, STA_INFO_UPDATE_ALL);
|
|
}
|
|
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
/* loop cto_mgate_blacklist to remove ent according to scan_r */
|
|
rtw_mesh_cto_mgate_blacklist_chk(adapter);
|
|
#endif
|
|
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
rtw_mesh_acnode_rsvd_chk(adapter);
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
#if CONFIG_RTW_MESH_OFFCH_CAND
|
|
static u8 rtw_mesh_offch_cto_mgate_required(_adapter *adapter)
|
|
{
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct mlme_priv *mlme = &adapter->mlmepriv;
|
|
_queue *queue = &(mlme->scanned_queue);
|
|
_list *head, *pos;
|
|
struct wlan_network *scanned = NULL;
|
|
u8 ret = 0;
|
|
|
|
if (!rtw_mesh_cto_mgate_required(adapter))
|
|
goto exit;
|
|
|
|
enter_critical_bh(&(mlme->scanned_queue.lock));
|
|
|
|
head = get_list_head(queue);
|
|
pos = get_next(head);
|
|
while (!rtw_end_of_queue_search(head, pos)) {
|
|
scanned = LIST_CONTAINOR(pos, struct wlan_network, list);
|
|
|
|
if (rtw_get_passing_time_ms(scanned->last_scanned) < mcfg->peer_sel_policy.scanr_exp_ms
|
|
&& (!mcfg->rssi_threshold || mcfg->rssi_threshold <= scanned->network.Rssi)
|
|
#if CONFIG_RTW_MACADDR_ACL
|
|
&& rtw_access_ctrl(adapter, scanned->network.MacAddress) == _TRUE
|
|
#endif
|
|
&& rtw_bss_is_candidate_mesh_peer(&mlme->cur_network.network, &scanned->network, 1, 1)
|
|
&& rtw_bss_is_cto_mgate(&scanned->network)
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
&& !rtw_mesh_peer_blacklist_search(adapter, scanned->network.MacAddress)
|
|
#endif
|
|
&& !rtw_mesh_cto_mgate_blacklist_search(adapter, scanned->network.MacAddress)
|
|
)
|
|
break;
|
|
|
|
pos = get_next(pos);
|
|
}
|
|
|
|
if (rtw_end_of_queue_search(head, pos))
|
|
ret = 1;
|
|
|
|
exit_critical_bh(&(mlme->scanned_queue.lock));
|
|
|
|
exit:
|
|
return ret;
|
|
#else
|
|
return 0;
|
|
#endif /* CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST */
|
|
}
|
|
|
|
u8 rtw_mesh_offch_candidate_accepted(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
u8 ret = 0;
|
|
|
|
if (!adapter->mesh_cfg.peer_sel_policy.offch_cand)
|
|
goto exit;
|
|
|
|
ret = MLME_IS_MESH(adapter) && MLME_IS_ASOC(adapter)
|
|
&& (!plink_ctl->num || rtw_mesh_offch_cto_mgate_required(adapter))
|
|
;
|
|
|
|
#ifdef CONFIG_CONCURRENT_MODE
|
|
if (ret) {
|
|
struct mi_state mstate_no_self;
|
|
|
|
rtw_mi_status_no_self(adapter, &mstate_no_self);
|
|
if (MSTATE_STA_LD_NUM(&mstate_no_self))
|
|
ret = 0;
|
|
}
|
|
#endif
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* this function is called under off channel candidate is required
|
|
* the channel with maximum candidate count is selected
|
|
*/
|
|
u8 rtw_mesh_select_operating_ch(_adapter *adapter)
|
|
{
|
|
struct rf_ctl_t *rfctl = adapter_to_rfctl(adapter);
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct mlme_priv *mlme = &adapter->mlmepriv;
|
|
_queue *queue = &(mlme->scanned_queue);
|
|
_list *head, *pos;
|
|
_irqL irqL;
|
|
struct wlan_network *scanned = NULL;
|
|
int i;
|
|
/* statistics for candidate accept peering */
|
|
u8 cand_ap_cnt[MAX_CHANNEL_NUM] = {0};
|
|
u8 max_cand_ap_ch = 0;
|
|
u8 max_cand_ap_cnt = 0;
|
|
/* statistics for candidate including not accept peering */
|
|
u8 cand_cnt[MAX_CHANNEL_NUM] = {0};
|
|
u8 max_cand_ch = 0;
|
|
u8 max_cand_cnt = 0;
|
|
|
|
_enter_critical_bh(&(mlme->scanned_queue.lock), &irqL);
|
|
|
|
head = get_list_head(queue);
|
|
pos = get_next(head);
|
|
while (!rtw_end_of_queue_search(head, pos)) {
|
|
scanned = LIST_CONTAINOR(pos, struct wlan_network, list);
|
|
pos = get_next(pos);
|
|
|
|
if (rtw_get_passing_time_ms(scanned->last_scanned) < mcfg->peer_sel_policy.scanr_exp_ms
|
|
&& (!mcfg->rssi_threshold || mcfg->rssi_threshold <= scanned->network.Rssi)
|
|
#if CONFIG_RTW_MACADDR_ACL
|
|
&& rtw_access_ctrl(adapter, scanned->network.MacAddress) == _TRUE
|
|
#endif
|
|
&& rtw_bss_is_candidate_mesh_peer(&mlme->cur_network.network, &scanned->network, 0, 0)
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
&& !rtw_mesh_peer_blacklist_search(adapter, scanned->network.MacAddress)
|
|
#endif
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
&& rtw_mesh_cto_mgate_network_filter(adapter, scanned)
|
|
#endif
|
|
) {
|
|
int ch_set_idx = rtw_chset_search_ch(rfctl->channel_set, scanned->network.Configuration.DSConfig);
|
|
|
|
if (ch_set_idx >= 0
|
|
&& !CH_IS_NON_OCP(&rfctl->channel_set[ch_set_idx])
|
|
) {
|
|
u8 nop, accept;
|
|
|
|
rtw_mesh_bss_peering_status(&scanned->network, &nop, &accept);
|
|
cand_cnt[ch_set_idx]++;
|
|
if (max_cand_cnt < cand_cnt[ch_set_idx]) {
|
|
max_cand_cnt = cand_cnt[ch_set_idx];
|
|
max_cand_ch = rfctl->channel_set[ch_set_idx].ChannelNum;
|
|
}
|
|
if (accept) {
|
|
cand_ap_cnt[ch_set_idx]++;
|
|
if (max_cand_ap_cnt < cand_ap_cnt[ch_set_idx]) {
|
|
max_cand_ap_cnt = cand_ap_cnt[ch_set_idx];
|
|
max_cand_ap_ch = rfctl->channel_set[ch_set_idx].ChannelNum;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_exit_critical_bh(&(mlme->scanned_queue.lock), &irqL);
|
|
|
|
return max_cand_ap_ch ? max_cand_ap_ch : max_cand_ch;
|
|
}
|
|
|
|
void dump_mesh_offch_cand_settings(void *sel, _adapter *adapter)
|
|
{
|
|
struct mesh_peer_sel_policy *peer_sel_policy = &adapter->mesh_cfg.peer_sel_policy;
|
|
|
|
RTW_PRINT_SEL(sel, "%-6s %-11s\n"
|
|
, "enable", "find_int_ms");
|
|
RTW_PRINT_SEL(sel, "%6u %11u\n"
|
|
, peer_sel_policy->offch_cand, peer_sel_policy->offch_find_int_ms);
|
|
}
|
|
#endif /* CONFIG_RTW_MESH_OFFCH_CAND */
|
|
|
|
void dump_mesh_peer_sel_policy(void *sel, _adapter *adapter)
|
|
{
|
|
struct mesh_peer_sel_policy *peer_sel_policy = &adapter->mesh_cfg.peer_sel_policy;
|
|
|
|
RTW_PRINT_SEL(sel, "%-12s\n", "scanr_exp_ms");
|
|
RTW_PRINT_SEL(sel, "%12u\n", peer_sel_policy->scanr_exp_ms);
|
|
}
|
|
|
|
void dump_mesh_networks(void *sel, _adapter *adapter)
|
|
{
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
#define NSTATE_TITLE_FMT_ACN " %-5s"
|
|
#define NSTATE_VALUE_FMT_ACN " %5d"
|
|
#define NSTATE_TITLE_ARG_ACN , "acn"
|
|
#define NSTATE_VALUE_ARG_ACN , (acn_ms < 99999 ? acn_ms : 99999)
|
|
#else
|
|
#define NSTATE_TITLE_FMT_ACN ""
|
|
#define NSTATE_VALUE_FMT_ACN ""
|
|
#define NSTATE_TITLE_ARG_ACN
|
|
#define NSTATE_VALUE_ARG_ACN
|
|
#endif
|
|
|
|
struct mlme_priv *mlme = &(adapter->mlmepriv);
|
|
_queue *queue = &(mlme->scanned_queue);
|
|
struct wlan_network *network;
|
|
_list *list, *head;
|
|
u8 same_mbss;
|
|
u8 candidate;
|
|
struct mesh_plink_ent *plink;
|
|
u8 blocked;
|
|
u8 established;
|
|
s32 age_ms;
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
s32 acn_ms;
|
|
#endif
|
|
u8 *mesh_conf_ie;
|
|
sint mesh_conf_ie_len;
|
|
struct wlan_network **mesh_networks;
|
|
u8 mesh_network_cnt = 0;
|
|
int i;
|
|
|
|
mesh_networks = vzalloc(mlme->max_bss_cnt * sizeof(struct wlan_network *));
|
|
if (!mesh_networks)
|
|
return;
|
|
|
|
enter_critical_bh(&queue->lock);
|
|
head = get_list_head(queue);
|
|
list = get_next(head);
|
|
|
|
while (rtw_end_of_queue_search(head, list) == _FALSE) {
|
|
network = LIST_CONTAINOR(list, struct wlan_network, list);
|
|
list = get_next(list);
|
|
|
|
if (network->network.InfrastructureMode != Ndis802_11_mesh)
|
|
continue;
|
|
|
|
mesh_conf_ie = rtw_get_ie(BSS_EX_TLV_IES(&network->network), WLAN_EID_MESH_CONFIG
|
|
, &mesh_conf_ie_len, BSS_EX_TLV_IES_LEN(&network->network));
|
|
if (!mesh_conf_ie || mesh_conf_ie_len != 7)
|
|
continue;
|
|
|
|
mesh_networks[mesh_network_cnt++] = network;
|
|
}
|
|
|
|
exit_critical_bh(&queue->lock);
|
|
|
|
RTW_PRINT_SEL(sel, " %-17s %-3s %-4s %-5s %-32s %-3s %-3s %-3s"
|
|
NSTATE_TITLE_FMT_ACN
|
|
"\n"
|
|
, "bssid", "ch", "rssi", "age", "mesh_id", "nop", "fwd", "cto"
|
|
NSTATE_TITLE_ARG_ACN
|
|
);
|
|
|
|
for (i = 0; i < mesh_network_cnt; i++) {
|
|
network = mesh_networks[i];
|
|
|
|
if (network->network.InfrastructureMode != Ndis802_11_mesh)
|
|
continue;
|
|
|
|
mesh_conf_ie = rtw_get_ie(BSS_EX_TLV_IES(&network->network), WLAN_EID_MESH_CONFIG
|
|
, &mesh_conf_ie_len, BSS_EX_TLV_IES_LEN(&network->network));
|
|
if (!mesh_conf_ie || mesh_conf_ie_len != 7)
|
|
continue;
|
|
|
|
age_ms = rtw_get_passing_time_ms(network->last_scanned);
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
if (network->acnode_stime == 0)
|
|
acn_ms = 0;
|
|
else
|
|
acn_ms = rtw_get_passing_time_ms(network->acnode_stime);
|
|
#endif
|
|
same_mbss = 0;
|
|
candidate = 0;
|
|
plink = NULL;
|
|
blocked = 0;
|
|
established = 0;
|
|
|
|
if (MLME_IS_MESH(adapter) && MLME_IS_ASOC(adapter)) {
|
|
plink = rtw_mesh_plink_get(adapter, network->network.MacAddress);
|
|
if (plink && plink->plink_state == RTW_MESH_PLINK_ESTAB)
|
|
established = 1;
|
|
else if (plink && plink->plink_state == RTW_MESH_PLINK_BLOCKED)
|
|
blocked = 1;
|
|
else if (plink)
|
|
;
|
|
else if (rtw_bss_is_candidate_mesh_peer(&mlme->cur_network.network, &network->network, 0, 1))
|
|
candidate = 1;
|
|
else if (rtw_bss_is_same_mbss(&mlme->cur_network.network, &network->network))
|
|
same_mbss = 1;
|
|
}
|
|
|
|
RTW_PRINT_SEL(sel, "%c "MAC_FMT" %3d %4ld %5d %-32s %c%2u %3u %c%c "
|
|
NSTATE_VALUE_FMT_ACN
|
|
"\n"
|
|
, established ? 'E' : (blocked ? 'B' : (plink ? 'N' : (candidate ? 'C' : (same_mbss ? 'S' : ' '))))
|
|
, MAC_ARG(network->network.MacAddress)
|
|
, network->network.Configuration.DSConfig
|
|
, network->network.Rssi
|
|
, age_ms < 99999 ? age_ms : 99999
|
|
, network->network.mesh_id.Ssid
|
|
, GET_MESH_CONF_ELE_ACCEPT_PEERINGS(mesh_conf_ie + 2) ? '+' : ' '
|
|
, GET_MESH_CONF_ELE_NUM_OF_PEERINGS(mesh_conf_ie + 2)
|
|
, GET_MESH_CONF_ELE_FORWARDING(mesh_conf_ie + 2)
|
|
, GET_MESH_CONF_ELE_CTO_MGATE(mesh_conf_ie + 2) ? 'G' : ' '
|
|
, GET_MESH_CONF_ELE_CTO_AS(mesh_conf_ie + 2) ? 'A' : ' '
|
|
NSTATE_VALUE_ARG_ACN
|
|
);
|
|
}
|
|
|
|
vfree(mesh_networks);
|
|
}
|
|
|
|
void rtw_mesh_adjust_chbw(u8 req_ch, u8 *req_bw, u8 *req_offset)
|
|
{
|
|
if (req_ch >= 5 && req_ch <= 9) {
|
|
/* prevent secondary channel offset mismatch */
|
|
if (*req_bw > CHANNEL_WIDTH_20) {
|
|
*req_bw = CHANNEL_WIDTH_20;
|
|
*req_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void rtw_mesh_sae_check_frames(_adapter *adapter, const u8 *buf, u32 len, u8 tx, u16 alg, u16 seq, u16 status)
|
|
{
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
if (tx && seq == 1)
|
|
rtw_mesh_plink_set_peer_conf_timeout(adapter, GetAddr1Ptr(buf));
|
|
#endif
|
|
}
|
|
|
|
#if CONFIG_RTW_MPM_TX_IES_SYNC_BSS
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
static int rtw_mpm_ampe_dec(_adapter *adapter, struct mesh_plink_ent *plink
|
|
, u8 *fhead, size_t flen, u8* fbody, u8 *mic_ie, u8 *ampe_buf)
|
|
{
|
|
int ret = _FAIL, verify_ret;
|
|
const u8 *aad[] = {adapter_mac_addr(adapter), plink->addr, fbody};
|
|
const size_t aad_len[] = {ETH_ALEN, ETH_ALEN, mic_ie - fbody};
|
|
u8 *iv_crypt;
|
|
size_t iv_crypt_len = flen - (mic_ie + 2 - fhead);
|
|
|
|
iv_crypt = rtw_malloc(iv_crypt_len);
|
|
if (!iv_crypt)
|
|
goto exit;
|
|
|
|
_rtw_memcpy(iv_crypt, mic_ie + 2, iv_crypt_len);
|
|
|
|
verify_ret = aes_siv_decrypt(plink->aek, iv_crypt, iv_crypt_len
|
|
, 3, aad, aad_len, ampe_buf);
|
|
|
|
rtw_mfree(iv_crypt, iv_crypt_len);
|
|
|
|
if (verify_ret) {
|
|
RTW_WARN("verify error, aek_valid=%u\n", plink->aek_valid);
|
|
goto exit;
|
|
} else if (*ampe_buf != WLAN_EID_AMPE) {
|
|
RTW_WARN("plaintext is not AMPE IE\n");
|
|
goto exit;
|
|
} else if (AES_BLOCK_SIZE + 2 + *(ampe_buf + 1) > iv_crypt_len) {
|
|
RTW_WARN("plaintext AMPE IE length is not valid\n");
|
|
goto exit;
|
|
}
|
|
|
|
ret = _SUCCESS;
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
static int rtw_mpm_ampe_enc(_adapter *adapter, struct mesh_plink_ent *plink
|
|
, u8* fbody, u8 *mic_ie, u8 *ampe_buf, bool inverse)
|
|
{
|
|
int ret = _FAIL, protect_ret;
|
|
const u8 *aad[3];
|
|
const size_t aad_len[3] = {ETH_ALEN, ETH_ALEN, mic_ie - fbody};
|
|
u8 *ampe_ie;
|
|
size_t ampe_ie_len = *(ampe_buf + 1) + 2; /* including id & len */
|
|
|
|
if (inverse) {
|
|
aad[0] = plink->addr;
|
|
aad[1] = adapter_mac_addr(adapter);
|
|
} else {
|
|
aad[0] = adapter_mac_addr(adapter);
|
|
aad[1] = plink->addr;
|
|
}
|
|
aad[2] = fbody;
|
|
|
|
ampe_ie = rtw_malloc(ampe_ie_len);
|
|
if (!ampe_ie)
|
|
goto exit;
|
|
|
|
_rtw_memcpy(ampe_ie, ampe_buf, ampe_ie_len);
|
|
|
|
protect_ret = aes_siv_encrypt(plink->aek, ampe_ie, ampe_ie_len
|
|
, 3, aad, aad_len, mic_ie + 2);
|
|
|
|
rtw_mfree(ampe_ie, ampe_ie_len);
|
|
|
|
if (protect_ret) {
|
|
RTW_WARN("protect error, aek_valid=%u\n", plink->aek_valid);
|
|
goto exit;
|
|
}
|
|
|
|
ret = _SUCCESS;
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_RTW_MESH_AEK */
|
|
|
|
static int rtw_mpm_tx_ies_sync_bss(_adapter *adapter, struct mesh_plink_ent *plink
|
|
, u8 *fhead, size_t flen, u8* fbody, u8 tlv_ies_offset, u8 *mpm_ie, u8 *mic_ie
|
|
, u8 **nbuf, size_t *nlen)
|
|
{
|
|
int ret = _FAIL;
|
|
struct mlme_priv *mlme = &(adapter->mlmepriv);
|
|
struct mlme_ext_priv *mlmeext = &adapter->mlmeextpriv;
|
|
struct mlme_ext_info *mlmeinfo = &(mlmeext->mlmext_info);
|
|
WLAN_BSSID_EX *network = &(mlmeinfo->network);
|
|
uint left;
|
|
u8 *pos;
|
|
|
|
uint mpm_ielen = *(mpm_ie + 1);
|
|
u8 *fpos;
|
|
u8 *new_buf = NULL;
|
|
size_t new_len = 0;
|
|
|
|
u8 *new_fhead;
|
|
size_t new_flen;
|
|
u8 *new_fbody;
|
|
u8 *new_mic_ie;
|
|
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
u8 *ampe_buf = NULL;
|
|
size_t ampe_buf_len = 0;
|
|
|
|
/* decode */
|
|
if (mic_ie) {
|
|
ampe_buf_len = flen - (mic_ie + 2 + AES_BLOCK_SIZE - fhead);
|
|
ampe_buf = rtw_malloc(ampe_buf_len);
|
|
if (!ampe_buf)
|
|
goto exit;
|
|
|
|
if (rtw_mpm_ampe_dec(adapter, plink, fhead, flen, fbody, mic_ie, ampe_buf) != _SUCCESS)
|
|
goto exit;
|
|
|
|
if (*(ampe_buf + 1) >= 68) {
|
|
_rtw_memcpy(plink->sel_pcs, ampe_buf + 2, 4);
|
|
_rtw_memcpy(plink->l_nonce, ampe_buf + 6, 32);
|
|
_rtw_memcpy(plink->p_nonce, ampe_buf + 38, 32);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* count for new frame length */
|
|
new_len = sizeof(struct rtw_ieee80211_hdr_3addr) + tlv_ies_offset;
|
|
left = BSS_EX_TLV_IES_LEN(network);
|
|
pos = BSS_EX_TLV_IES(network);
|
|
while (left >= 2) {
|
|
u8 id, elen;
|
|
|
|
id = *pos++;
|
|
elen = *pos++;
|
|
left -= 2;
|
|
|
|
if (elen > left)
|
|
break;
|
|
|
|
switch (id) {
|
|
case WLAN_EID_SSID:
|
|
case WLAN_EID_DS_PARAMS:
|
|
case WLAN_EID_TIM:
|
|
break;
|
|
default:
|
|
new_len += 2 + elen;
|
|
}
|
|
|
|
left -= elen;
|
|
pos += elen;
|
|
}
|
|
new_len += mpm_ielen + 2;
|
|
if (mic_ie)
|
|
new_len += AES_BLOCK_SIZE + 2 + ampe_buf_len;
|
|
|
|
/* alloc new frame */
|
|
new_buf = rtw_malloc(new_len);
|
|
if (!new_buf) {
|
|
rtw_warn_on(1);
|
|
goto exit;
|
|
}
|
|
|
|
/* build new frame */
|
|
_rtw_memcpy(new_buf, fhead, sizeof(struct rtw_ieee80211_hdr_3addr) + tlv_ies_offset);
|
|
new_fhead = new_buf;
|
|
new_flen = new_len;
|
|
new_fbody = new_fhead + sizeof(struct rtw_ieee80211_hdr_3addr);
|
|
|
|
fpos = new_fbody + tlv_ies_offset;
|
|
left = BSS_EX_TLV_IES_LEN(network);
|
|
pos = BSS_EX_TLV_IES(network);
|
|
while (left >= 2) {
|
|
u8 id, elen;
|
|
|
|
id = *pos++;
|
|
elen = *pos++;
|
|
left -= 2;
|
|
|
|
if (elen > left)
|
|
break;
|
|
|
|
switch (id) {
|
|
case WLAN_EID_SSID:
|
|
case WLAN_EID_DS_PARAMS:
|
|
case WLAN_EID_TIM:
|
|
break;
|
|
default:
|
|
fpos = rtw_set_ie(fpos, id, elen, pos, NULL);
|
|
if (id == WLAN_EID_MESH_CONFIG)
|
|
fpos = rtw_set_ie(fpos, WLAN_EID_MPM, mpm_ielen, mpm_ie + 2, NULL);
|
|
}
|
|
|
|
left -= elen;
|
|
pos += elen;
|
|
}
|
|
if (mic_ie) {
|
|
new_mic_ie = fpos;
|
|
*fpos++ = WLAN_EID_MIC;
|
|
*fpos++ = AES_BLOCK_SIZE;
|
|
}
|
|
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
/* encode */
|
|
if (mic_ie) {
|
|
int enc_ret = rtw_mpm_ampe_enc(adapter, plink, new_fbody, new_mic_ie, ampe_buf, 0);
|
|
if (enc_ret != _SUCCESS)
|
|
goto exit;
|
|
}
|
|
#endif
|
|
|
|
*nlen = new_len;
|
|
*nbuf = new_buf;
|
|
|
|
ret = _SUCCESS;
|
|
|
|
exit:
|
|
if (ret != _SUCCESS && new_buf)
|
|
rtw_mfree(new_buf, new_len);
|
|
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
if (ampe_buf)
|
|
rtw_mfree(ampe_buf, ampe_buf_len);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_RTW_MPM_TX_IES_SYNC_BSS */
|
|
|
|
struct mpm_frame_info {
|
|
u8 *aid;
|
|
u16 aid_v;
|
|
u8 *pid;
|
|
u16 pid_v;
|
|
u8 *llid;
|
|
u16 llid_v;
|
|
u8 *plid;
|
|
u16 plid_v;
|
|
u8 *reason;
|
|
u16 reason_v;
|
|
u8 *chosen_pmk;
|
|
};
|
|
|
|
/*
|
|
* pid:00000 llid:00000 chosen_pmk:0x00000000000000000000000000000000
|
|
* aid:00000 pid:00000 llid:00000 plid:00000 chosen_pmk:0x00000000000000000000000000000000
|
|
* pid:00000 llid:00000 plid:00000 reason:00000 chosen_pmk:0x00000000000000000000000000000000
|
|
*/
|
|
#define MPM_LOG_BUF_LEN 92 /* this length is limited for legal combination */
|
|
static void rtw_mpm_info_msg(struct mpm_frame_info *mpm_info, u8 *mpm_log_buf)
|
|
{
|
|
int cnt = 0;
|
|
|
|
if (mpm_info->aid) {
|
|
cnt += snprintf(mpm_log_buf + cnt, MPM_LOG_BUF_LEN - cnt - 1, "aid:%u ", mpm_info->aid_v);
|
|
if (cnt >= MPM_LOG_BUF_LEN - 1)
|
|
goto exit;
|
|
}
|
|
if (mpm_info->pid) {
|
|
cnt += snprintf(mpm_log_buf + cnt, MPM_LOG_BUF_LEN - cnt - 1, "pid:%u ", mpm_info->pid_v);
|
|
if (cnt >= MPM_LOG_BUF_LEN - 1)
|
|
goto exit;
|
|
}
|
|
if (mpm_info->llid) {
|
|
cnt += snprintf(mpm_log_buf + cnt, MPM_LOG_BUF_LEN - cnt - 1, "llid:%u ", mpm_info->llid_v);
|
|
if (cnt >= MPM_LOG_BUF_LEN - 1)
|
|
goto exit;
|
|
}
|
|
if (mpm_info->plid) {
|
|
cnt += snprintf(mpm_log_buf + cnt, MPM_LOG_BUF_LEN - cnt - 1, "plid:%u ", mpm_info->plid_v);
|
|
if (cnt >= MPM_LOG_BUF_LEN - 1)
|
|
goto exit;
|
|
}
|
|
if (mpm_info->reason) {
|
|
cnt += snprintf(mpm_log_buf + cnt, MPM_LOG_BUF_LEN - cnt - 1, "reason:%u ", mpm_info->reason_v);
|
|
if (cnt >= MPM_LOG_BUF_LEN - 1)
|
|
goto exit;
|
|
}
|
|
if (mpm_info->chosen_pmk) {
|
|
cnt += snprintf(mpm_log_buf + cnt, MPM_LOG_BUF_LEN - cnt - 1, "chosen_pmk:0x"KEY_FMT, KEY_ARG(mpm_info->chosen_pmk));
|
|
if (cnt >= MPM_LOG_BUF_LEN - 1)
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
static int rtw_mpm_check_frames(_adapter *adapter, u8 action, const u8 **buf, size_t *len, u8 tx)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *plink = NULL;
|
|
u8 *nbuf = NULL;
|
|
size_t nlen = 0;
|
|
u8 *fhead = (u8 *)*buf;
|
|
size_t flen = *len;
|
|
u8 *peer_addr = tx ? GetAddr1Ptr(fhead) : get_addr2_ptr(fhead);
|
|
u8 *frame_body = fhead + sizeof(struct rtw_ieee80211_hdr_3addr);
|
|
struct mpm_frame_info mpm_info;
|
|
u8 tlv_ies_offset;
|
|
u8 *mpm_ie = NULL;
|
|
uint mpm_ielen = 0;
|
|
u8 *mic_ie = NULL;
|
|
uint mic_ielen = 0;
|
|
int ret = 0;
|
|
u8 mpm_log_buf[MPM_LOG_BUF_LEN] = {0};
|
|
|
|
if (action == RTW_ACT_SELF_PROTECTED_MESH_OPEN)
|
|
tlv_ies_offset = 4;
|
|
else if (action == RTW_ACT_SELF_PROTECTED_MESH_CONF)
|
|
tlv_ies_offset = 6;
|
|
else if (action == RTW_ACT_SELF_PROTECTED_MESH_CLOSE)
|
|
tlv_ies_offset = 2;
|
|
else {
|
|
rtw_warn_on(1);
|
|
goto exit;
|
|
}
|
|
|
|
plink = rtw_mesh_plink_get(adapter, peer_addr);
|
|
if (!plink && (tx == _TRUE || action == RTW_ACT_SELF_PROTECTED_MESH_CONF)) {
|
|
/* warning message if no plink when: 1.TX all MPM or 2.RX CONF */
|
|
RTW_WARN("RTW_%s:%s without plink of "MAC_FMT"\n"
|
|
, (tx == _TRUE) ? "Tx" : "Rx", action_self_protected_str(action), MAC_ARG(peer_addr));
|
|
goto exit;
|
|
}
|
|
|
|
memset(&mpm_info, 0, sizeof(struct mpm_frame_info));
|
|
|
|
if (action == RTW_ACT_SELF_PROTECTED_MESH_CONF) {
|
|
mpm_info.aid = (u8 *)frame_body + 4;
|
|
mpm_info.aid_v = RTW_GET_LE16(mpm_info.aid);
|
|
}
|
|
|
|
mpm_ie = rtw_get_ie(fhead + sizeof(struct rtw_ieee80211_hdr_3addr) + tlv_ies_offset
|
|
, WLAN_EID_MPM, &mpm_ielen
|
|
, flen - sizeof(struct rtw_ieee80211_hdr_3addr) - tlv_ies_offset);
|
|
if (!mpm_ie || mpm_ielen < 2 + 2)
|
|
goto exit;
|
|
|
|
mpm_info.pid = mpm_ie + 2;
|
|
mpm_info.pid_v = RTW_GET_LE16(mpm_info.pid);
|
|
mpm_info.llid = mpm_info.pid + 2;
|
|
mpm_info.llid_v = RTW_GET_LE16(mpm_info.llid);
|
|
|
|
switch (action) {
|
|
case RTW_ACT_SELF_PROTECTED_MESH_OPEN:
|
|
/* pid:2, llid:2, (chosen_pmk:16) */
|
|
if (mpm_info.pid_v == 0 && mpm_ielen == 4)
|
|
;
|
|
else if (mpm_info.pid_v == 1 && mpm_ielen == 20)
|
|
mpm_info.chosen_pmk = mpm_info.llid + 2;
|
|
else
|
|
goto exit;
|
|
break;
|
|
case RTW_ACT_SELF_PROTECTED_MESH_CONF:
|
|
/* pid:2, llid:2, plid:2, (chosen_pmk:16) */
|
|
mpm_info.plid = mpm_info.llid + 2;
|
|
mpm_info.plid_v = RTW_GET_LE16(mpm_info.plid);
|
|
if (mpm_info.pid_v == 0 && mpm_ielen == 6)
|
|
;
|
|
else if (mpm_info.pid_v == 1 && mpm_ielen == 22)
|
|
mpm_info.chosen_pmk = mpm_info.plid + 2;
|
|
else
|
|
goto exit;
|
|
break;
|
|
case RTW_ACT_SELF_PROTECTED_MESH_CLOSE:
|
|
/* pid:2, llid:2, (plid:2), reason:2, (chosen_pmk:16) */
|
|
if (mpm_info.pid_v == 0 && mpm_ielen == 6) {
|
|
/* MPM, without plid */
|
|
mpm_info.reason = mpm_info.llid + 2;
|
|
mpm_info.reason_v = RTW_GET_LE16(mpm_info.reason);
|
|
} else if (mpm_info.pid_v == 0 && mpm_ielen == 8) {
|
|
/* MPM, with plid */
|
|
mpm_info.plid = mpm_info.llid + 2;
|
|
mpm_info.plid_v = RTW_GET_LE16(mpm_info.plid);
|
|
mpm_info.reason = mpm_info.plid + 2;
|
|
mpm_info.reason_v = RTW_GET_LE16(mpm_info.reason);
|
|
} else if (mpm_info.pid_v == 1 && mpm_ielen == 22) {
|
|
/* AMPE, without plid */
|
|
mpm_info.reason = mpm_info.llid + 2;
|
|
mpm_info.reason_v = RTW_GET_LE16(mpm_info.reason);
|
|
mpm_info.chosen_pmk = mpm_info.reason + 2;
|
|
} else if (mpm_info.pid_v == 1 && mpm_ielen == 24) {
|
|
/* AMPE, with plid */
|
|
mpm_info.plid = mpm_info.llid + 2;
|
|
mpm_info.plid_v = RTW_GET_LE16(mpm_info.plid);
|
|
mpm_info.reason = mpm_info.plid + 2;
|
|
mpm_info.reason_v = RTW_GET_LE16(mpm_info.reason);
|
|
mpm_info.chosen_pmk = mpm_info.reason + 2;
|
|
} else
|
|
goto exit;
|
|
break;
|
|
};
|
|
|
|
if (mpm_info.pid_v == 1) {
|
|
mic_ie = rtw_get_ie(fhead + sizeof(struct rtw_ieee80211_hdr_3addr) + tlv_ies_offset
|
|
, WLAN_EID_MIC, &mic_ielen
|
|
, flen - sizeof(struct rtw_ieee80211_hdr_3addr) - tlv_ies_offset);
|
|
if (!mic_ie || mic_ielen != AES_BLOCK_SIZE)
|
|
goto exit;
|
|
}
|
|
|
|
#if CONFIG_RTW_MPM_TX_IES_SYNC_BSS
|
|
if ((action == RTW_ACT_SELF_PROTECTED_MESH_OPEN || action == RTW_ACT_SELF_PROTECTED_MESH_CONF)
|
|
&& tx == _TRUE
|
|
) {
|
|
#define DBG_RTW_MPM_TX_IES_SYNC_BSS 0
|
|
|
|
if (mpm_info.pid_v == 1 && (!plink || !MESH_PLINK_AEK_VALID(plink))) {
|
|
RTW_WARN("AEK not ready, IEs can't sync with BSS\n");
|
|
goto bypass_sync_bss;
|
|
}
|
|
|
|
if (DBG_RTW_MPM_TX_IES_SYNC_BSS) {
|
|
RTW_INFO(FUNC_ADPT_FMT" before:\n", FUNC_ADPT_ARG(adapter));
|
|
dump_ies(RTW_DBGDUMP
|
|
, fhead + sizeof(struct rtw_ieee80211_hdr_3addr) + tlv_ies_offset
|
|
, flen - sizeof(struct rtw_ieee80211_hdr_3addr) - tlv_ies_offset);
|
|
}
|
|
|
|
rtw_mpm_tx_ies_sync_bss(adapter, plink
|
|
, fhead, flen, frame_body, tlv_ies_offset, mpm_ie, mic_ie
|
|
, &nbuf, &nlen);
|
|
if (!nbuf)
|
|
goto exit;
|
|
|
|
/* update pointer & len for new frame */
|
|
fhead = nbuf;
|
|
flen = nlen;
|
|
frame_body = fhead + sizeof(struct rtw_ieee80211_hdr_3addr);
|
|
if (mpm_info.pid_v == 1) {
|
|
mic_ie = rtw_get_ie(fhead + sizeof(struct rtw_ieee80211_hdr_3addr) + tlv_ies_offset
|
|
, WLAN_EID_MIC, &mic_ielen
|
|
, flen - sizeof(struct rtw_ieee80211_hdr_3addr) - tlv_ies_offset);
|
|
}
|
|
|
|
if (DBG_RTW_MPM_TX_IES_SYNC_BSS) {
|
|
RTW_INFO(FUNC_ADPT_FMT" after:\n", FUNC_ADPT_ARG(adapter));
|
|
dump_ies(RTW_DBGDUMP
|
|
, fhead + sizeof(struct rtw_ieee80211_hdr_3addr) + tlv_ies_offset
|
|
, flen - sizeof(struct rtw_ieee80211_hdr_3addr) - tlv_ies_offset);
|
|
}
|
|
}
|
|
bypass_sync_bss:
|
|
#endif /* CONFIG_RTW_MPM_TX_IES_SYNC_BSS */
|
|
|
|
if (!plink)
|
|
goto mpm_log;
|
|
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
if (action == RTW_ACT_SELF_PROTECTED_MESH_OPEN) {
|
|
if (tx)
|
|
rtw_mesh_plink_set_peer_conf_timeout(adapter, peer_addr);
|
|
|
|
} else
|
|
#endif
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
if (action == RTW_ACT_SELF_PROTECTED_MESH_CLOSE) {
|
|
if (tx && mpm_info.reason && mpm_info.reason_v == WLAN_REASON_MESH_MAX_PEERS) {
|
|
if (rtw_mesh_scanned_is_acnode_confirmed(adapter, plink->scanned)
|
|
&& rtw_mesh_acnode_prevent_allow_sacrifice(adapter)
|
|
) {
|
|
struct sta_info *sac = rtw_mesh_acnode_prevent_pick_sacrifice(adapter);
|
|
|
|
if (sac) {
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
_irqL irqL;
|
|
u8 sta_addr[ETH_ALEN];
|
|
u8 updated = _FALSE;
|
|
|
|
_enter_critical_bh(&stapriv->asoc_list_lock, &irqL);
|
|
if (!rtw_is_list_empty(&sac->asoc_list)) {
|
|
rtw_list_delete(&sac->asoc_list);
|
|
stapriv->asoc_list_cnt--;
|
|
STA_SET_MESH_PLINK(sac, NULL);
|
|
}
|
|
_exit_critical_bh(&stapriv->asoc_list_lock, &irqL);
|
|
RTW_INFO(FUNC_ADPT_FMT" sacrifice "MAC_FMT" for acnode\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(sac->cmn.mac_addr));
|
|
|
|
_rtw_memcpy(sta_addr, sac->cmn.mac_addr, ETH_ALEN);
|
|
updated = ap_free_sta(adapter, sac, 0, 0, 1);
|
|
rtw_mesh_expire_peer(stapriv->padapter, sta_addr);
|
|
|
|
associated_clients_update(adapter, updated, STA_INFO_UPDATE_ALL);
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
#endif
|
|
if (action == RTW_ACT_SELF_PROTECTED_MESH_CONF) {
|
|
_irqL irqL;
|
|
u8 *ies = NULL;
|
|
u16 ies_len = 0;
|
|
|
|
_enter_critical_bh(&(plink_ctl->lock), &irqL);
|
|
|
|
plink = _rtw_mesh_plink_get(adapter, peer_addr);
|
|
if (!plink)
|
|
goto release_plink_ctl;
|
|
|
|
if (tx == _FALSE) {
|
|
ies = plink->rx_conf_ies;
|
|
ies_len = plink->rx_conf_ies_len;
|
|
plink->rx_conf_ies = NULL;
|
|
plink->rx_conf_ies_len = 0;
|
|
|
|
plink->llid = mpm_info.plid_v;
|
|
plink->plid = mpm_info.llid_v;
|
|
plink->peer_aid = mpm_info.aid_v;
|
|
if (mpm_info.pid_v == 1)
|
|
_rtw_memcpy(plink->chosen_pmk, mpm_info.chosen_pmk, 16);
|
|
}
|
|
#ifdef CONFIG_RTW_MESH_DRIVER_AID
|
|
else {
|
|
ies = plink->tx_conf_ies;
|
|
ies_len = plink->tx_conf_ies_len;
|
|
plink->tx_conf_ies = NULL;
|
|
plink->tx_conf_ies_len = 0;
|
|
}
|
|
#endif
|
|
|
|
if (ies && ies_len)
|
|
rtw_mfree(ies, ies_len);
|
|
|
|
#ifndef CONFIG_RTW_MESH_DRIVER_AID
|
|
if (tx == _TRUE)
|
|
goto release_plink_ctl; /* no need to copy tx conf ies */
|
|
#endif
|
|
|
|
/* copy mesh confirm IEs */
|
|
if (mpm_info.pid_v == 1) /* not include MIC & encrypted AMPE */
|
|
ies_len = (mic_ie - fhead) - sizeof(struct rtw_ieee80211_hdr_3addr) - 2;
|
|
else
|
|
ies_len = flen - sizeof(struct rtw_ieee80211_hdr_3addr) - 2;
|
|
|
|
ies = rtw_zmalloc(ies_len);
|
|
if (ies) {
|
|
_rtw_memcpy(ies, fhead + sizeof(struct rtw_ieee80211_hdr_3addr) + 2, ies_len);
|
|
if (tx == _FALSE) {
|
|
plink->rx_conf_ies = ies;
|
|
plink->rx_conf_ies_len = ies_len;
|
|
}
|
|
#ifdef CONFIG_RTW_MESH_DRIVER_AID
|
|
else {
|
|
plink->tx_conf_ies = ies;
|
|
plink->tx_conf_ies_len = ies_len;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
release_plink_ctl:
|
|
_exit_critical_bh(&(plink_ctl->lock), &irqL);
|
|
}
|
|
|
|
mpm_log:
|
|
rtw_mpm_info_msg(&mpm_info, mpm_log_buf);
|
|
RTW_INFO("RTW_%s:%s %s\n"
|
|
, (tx == _TRUE) ? "Tx" : "Rx"
|
|
, action_self_protected_str(action)
|
|
, mpm_log_buf
|
|
);
|
|
|
|
ret = 1;
|
|
|
|
exit:
|
|
if (nbuf) {
|
|
if (ret == 1) {
|
|
*buf = nbuf;
|
|
*len = nlen;
|
|
} else
|
|
rtw_mfree(nbuf, nlen);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rtw_mesh_check_frames(_adapter *adapter, const u8 **buf, size_t *len, u8 tx)
|
|
{
|
|
int is_mesh_frame = -1;
|
|
const u8 *frame_body;
|
|
u8 category, action;
|
|
|
|
frame_body = *buf + sizeof(struct rtw_ieee80211_hdr_3addr);
|
|
category = frame_body[0];
|
|
|
|
if (category == RTW_WLAN_CATEGORY_SELF_PROTECTED) {
|
|
action = frame_body[1];
|
|
switch (action) {
|
|
case RTW_ACT_SELF_PROTECTED_MESH_OPEN:
|
|
case RTW_ACT_SELF_PROTECTED_MESH_CONF:
|
|
case RTW_ACT_SELF_PROTECTED_MESH_CLOSE:
|
|
rtw_mpm_check_frames(adapter, action, buf, len, tx);
|
|
is_mesh_frame = action;
|
|
break;
|
|
case RTW_ACT_SELF_PROTECTED_MESH_GK_INFORM:
|
|
case RTW_ACT_SELF_PROTECTED_MESH_GK_ACK:
|
|
RTW_INFO("RTW_%s:%s\n", (tx == _TRUE) ? "Tx" : "Rx", action_self_protected_str(action));
|
|
is_mesh_frame = action;
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
exit:
|
|
return is_mesh_frame;
|
|
}
|
|
|
|
int rtw_mesh_check_frames_tx(_adapter *adapter, const u8 **buf, size_t *len)
|
|
{
|
|
return rtw_mesh_check_frames(adapter, buf, len, _TRUE);
|
|
}
|
|
|
|
int rtw_mesh_check_frames_rx(_adapter *adapter, const u8 *buf, size_t len)
|
|
{
|
|
return rtw_mesh_check_frames(adapter, &buf, &len, _FALSE);
|
|
}
|
|
|
|
int rtw_mesh_on_auth(_adapter *adapter, union recv_frame *rframe)
|
|
{
|
|
u8 *whdr = rframe->u.hdr.rx_data;
|
|
|
|
#if CONFIG_RTW_MACADDR_ACL
|
|
if (rtw_access_ctrl(adapter, get_addr2_ptr(whdr)) == _FALSE)
|
|
return _SUCCESS;
|
|
#endif
|
|
|
|
if (!rtw_mesh_plink_get(adapter, get_addr2_ptr(whdr))) {
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
rtw_mesh_acnode_set_notify_etime(adapter, whdr);
|
|
#endif
|
|
|
|
if (adapter_to_rfctl(adapter)->offch_state == OFFCHS_NONE)
|
|
issue_probereq(adapter, &adapter->mlmepriv.cur_network.network.mesh_id, get_addr2_ptr(whdr));
|
|
|
|
/* only peer being added (checked by notify conditions) is allowed */
|
|
return _SUCCESS;
|
|
}
|
|
|
|
rtw_cfg80211_rx_mframe(adapter, rframe, NULL);
|
|
return _SUCCESS;
|
|
}
|
|
|
|
unsigned int on_action_self_protected(_adapter *adapter, union recv_frame *rframe)
|
|
{
|
|
unsigned int ret = _FAIL;
|
|
struct sta_info *sta = NULL;
|
|
u8 *pframe = rframe->u.hdr.rx_data;
|
|
uint frame_len = rframe->u.hdr.len;
|
|
u8 *frame_body = (u8 *)(pframe + sizeof(struct rtw_ieee80211_hdr_3addr));
|
|
u8 category;
|
|
u8 action;
|
|
|
|
/* check RA matches or not */
|
|
if (!_rtw_memcmp(adapter_mac_addr(adapter), GetAddr1Ptr(pframe), ETH_ALEN))
|
|
goto exit;
|
|
|
|
category = frame_body[0];
|
|
if (category != RTW_WLAN_CATEGORY_SELF_PROTECTED)
|
|
goto exit;
|
|
|
|
action = frame_body[1];
|
|
switch (action) {
|
|
case RTW_ACT_SELF_PROTECTED_MESH_OPEN:
|
|
case RTW_ACT_SELF_PROTECTED_MESH_CONF:
|
|
case RTW_ACT_SELF_PROTECTED_MESH_CLOSE:
|
|
case RTW_ACT_SELF_PROTECTED_MESH_GK_INFORM:
|
|
case RTW_ACT_SELF_PROTECTED_MESH_GK_ACK:
|
|
if (!(MLME_IS_MESH(adapter) && MLME_IS_ASOC(adapter)))
|
|
goto exit;
|
|
#ifdef CONFIG_IOCTL_CFG80211
|
|
#if CONFIG_RTW_MACADDR_ACL
|
|
if (rtw_access_ctrl(adapter, get_addr2_ptr(pframe)) == _FALSE)
|
|
goto exit;
|
|
#endif
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
if (rtw_mesh_cto_mgate_required(adapter)
|
|
/* only peer being added (checked by notify conditions) is allowed */
|
|
&& !rtw_mesh_plink_get(adapter, get_addr2_ptr(pframe)))
|
|
goto exit;
|
|
#endif
|
|
rtw_cfg80211_rx_action(adapter, rframe, NULL);
|
|
ret = _SUCCESS;
|
|
#endif /* CONFIG_IOCTL_CFG80211 */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
const u8 ae_to_mesh_ctrl_len[] = {
|
|
6,
|
|
12, /* MESH_FLAGS_AE_A4 */
|
|
18, /* MESH_FLAGS_AE_A5_A6 */
|
|
0,
|
|
};
|
|
|
|
unsigned int on_action_mesh(_adapter *adapter, union recv_frame *rframe)
|
|
{
|
|
unsigned int ret = _FAIL;
|
|
struct sta_info *sta = NULL;
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
u8 *pframe = rframe->u.hdr.rx_data;
|
|
uint frame_len = rframe->u.hdr.len;
|
|
u8 *frame_body = (u8 *)(pframe + sizeof(struct rtw_ieee80211_hdr_3addr));
|
|
u8 category;
|
|
u8 action;
|
|
|
|
if (!MLME_IS_MESH(adapter))
|
|
goto exit;
|
|
|
|
/* check stainfo exist? */
|
|
|
|
category = frame_body[0];
|
|
if (category != RTW_WLAN_CATEGORY_MESH)
|
|
goto exit;
|
|
|
|
action = frame_body[1];
|
|
switch (action) {
|
|
case RTW_ACT_MESH_HWMP_PATH_SELECTION:
|
|
rtw_mesh_rx_path_sel_frame(adapter, rframe);
|
|
ret = _SUCCESS;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
bool rtw_mesh_update_bss_peering_status(_adapter *adapter, WLAN_BSSID_EX *bss)
|
|
{
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
u8 num_of_peerings = stapriv->asoc_list_cnt;
|
|
bool accept_peerings = stapriv->asoc_list_cnt < mcfg->max_peer_links;
|
|
u8 *ie;
|
|
int ie_len;
|
|
bool updated = 0;
|
|
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
accept_peerings |= plink_ctl->acnode_rsvd;
|
|
#endif
|
|
|
|
ie = rtw_get_ie(BSS_EX_TLV_IES(bss), WLAN_EID_MESH_CONFIG, &ie_len, BSS_EX_TLV_IES_LEN(bss));
|
|
if (!ie || ie_len != 7) {
|
|
rtw_warn_on(1);
|
|
goto exit;
|
|
}
|
|
|
|
if (GET_MESH_CONF_ELE_NUM_OF_PEERINGS(ie + 2) != num_of_peerings) {
|
|
SET_MESH_CONF_ELE_NUM_OF_PEERINGS(ie + 2, num_of_peerings);
|
|
updated = 1;
|
|
}
|
|
|
|
if (GET_MESH_CONF_ELE_ACCEPT_PEERINGS(ie + 2) != accept_peerings) {
|
|
SET_MESH_CONF_ELE_ACCEPT_PEERINGS(ie + 2, accept_peerings);
|
|
updated = 1;
|
|
}
|
|
|
|
exit:
|
|
return updated;
|
|
}
|
|
|
|
bool rtw_mesh_update_bss_formation_info(_adapter *adapter, WLAN_BSSID_EX *bss)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
u8 cto_mgate = (minfo->num_gates || mcfg->dot11MeshGateAnnouncementProtocol);
|
|
u8 cto_as = 0;
|
|
u8 *ie;
|
|
int ie_len;
|
|
bool updated = 0;
|
|
|
|
ie = rtw_get_ie(BSS_EX_TLV_IES(bss), WLAN_EID_MESH_CONFIG, &ie_len,
|
|
BSS_EX_TLV_IES_LEN(bss));
|
|
if (!ie || ie_len != 7) {
|
|
rtw_warn_on(1);
|
|
goto exit;
|
|
}
|
|
|
|
if (GET_MESH_CONF_ELE_CTO_MGATE(ie + 2) != cto_mgate) {
|
|
SET_MESH_CONF_ELE_CTO_MGATE(ie + 2, cto_mgate);
|
|
updated = 1;
|
|
}
|
|
|
|
if (GET_MESH_CONF_ELE_CTO_AS(ie + 2) != cto_as) {
|
|
SET_MESH_CONF_ELE_CTO_AS(ie + 2, cto_as);
|
|
updated = 1;
|
|
}
|
|
|
|
exit:
|
|
return updated;
|
|
}
|
|
|
|
bool rtw_mesh_update_bss_forwarding_state(_adapter *adapter, WLAN_BSSID_EX *bss)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
u8 forward = mcfg->dot11MeshForwarding;
|
|
u8 *ie;
|
|
int ie_len;
|
|
bool updated = 0;
|
|
|
|
ie = rtw_get_ie(BSS_EX_TLV_IES(bss), WLAN_EID_MESH_CONFIG, &ie_len,
|
|
BSS_EX_TLV_IES_LEN(bss));
|
|
if (!ie || ie_len != 7) {
|
|
rtw_warn_on(1);
|
|
goto exit;
|
|
}
|
|
|
|
if (GET_MESH_CONF_ELE_FORWARDING(ie + 2) != forward) {
|
|
SET_MESH_CONF_ELE_FORWARDING(ie + 2, forward);
|
|
updated = 1;
|
|
}
|
|
|
|
exit:
|
|
return updated;
|
|
}
|
|
|
|
struct mesh_plink_ent *_rtw_mesh_plink_get(_adapter *adapter, const u8 *hwaddr)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *ent = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < RTW_MESH_MAX_PEER_CANDIDATES; i++) {
|
|
if (plink_ctl->ent[i].valid == _TRUE
|
|
&& _rtw_memcmp(plink_ctl->ent[i].addr, hwaddr, ETH_ALEN) == _TRUE
|
|
) {
|
|
ent = &plink_ctl->ent[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return ent;
|
|
}
|
|
|
|
struct mesh_plink_ent *rtw_mesh_plink_get(_adapter *adapter, const u8 *hwaddr)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *ent = NULL;
|
|
_irqL irqL;
|
|
|
|
_enter_critical_bh(&(plink_ctl->lock), &irqL);
|
|
ent = _rtw_mesh_plink_get(adapter, hwaddr);
|
|
_exit_critical_bh(&(plink_ctl->lock), &irqL);
|
|
|
|
exit:
|
|
return ent;
|
|
}
|
|
|
|
struct mesh_plink_ent *rtw_mesh_plink_get_no_estab_by_idx(_adapter *adapter, u8 idx)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *ent = NULL;
|
|
int i, j = 0;
|
|
_irqL irqL;
|
|
|
|
_enter_critical_bh(&(plink_ctl->lock), &irqL);
|
|
for (i = 0; i < RTW_MESH_MAX_PEER_CANDIDATES; i++) {
|
|
if (plink_ctl->ent[i].valid == _TRUE
|
|
&& plink_ctl->ent[i].plink_state != RTW_MESH_PLINK_ESTAB
|
|
) {
|
|
if (j == idx) {
|
|
ent = &plink_ctl->ent[i];
|
|
break;
|
|
}
|
|
j++;
|
|
}
|
|
}
|
|
_exit_critical_bh(&(plink_ctl->lock), &irqL);
|
|
|
|
return ent;
|
|
}
|
|
|
|
int _rtw_mesh_plink_add(_adapter *adapter, const u8 *hwaddr)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *ent = NULL;
|
|
u8 exist = _FALSE;
|
|
int i;
|
|
|
|
for (i = 0; i < RTW_MESH_MAX_PEER_CANDIDATES; i++) {
|
|
if (plink_ctl->ent[i].valid == _TRUE
|
|
&& _rtw_memcmp(plink_ctl->ent[i].addr, hwaddr, ETH_ALEN) == _TRUE
|
|
) {
|
|
ent = &plink_ctl->ent[i];
|
|
exist = _TRUE;
|
|
break;
|
|
}
|
|
|
|
if (ent == NULL && plink_ctl->ent[i].valid == _FALSE)
|
|
ent = &plink_ctl->ent[i];
|
|
}
|
|
|
|
if (exist == _FALSE && ent) {
|
|
_rtw_memcpy(ent->addr, hwaddr, ETH_ALEN);
|
|
ent->valid = _TRUE;
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
ent->aek_valid = 0;
|
|
#endif
|
|
ent->llid = 0;
|
|
ent->plid = 0;
|
|
memset(ent->chosen_pmk, 0, 16);
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
memset(ent->sel_pcs, 0, 4);
|
|
memset(ent->l_nonce, 0, 32);
|
|
memset(ent->p_nonce, 0, 32);
|
|
#endif
|
|
ent->plink_state = RTW_MESH_PLINK_LISTEN;
|
|
#ifndef CONFIG_RTW_MESH_DRIVER_AID
|
|
ent->aid = 0;
|
|
#endif
|
|
ent->peer_aid = 0;
|
|
SET_PEER_CONF_DISABLED(ent);
|
|
SET_CTO_MGATE_CONF_DISABLED(ent);
|
|
plink_ctl->num++;
|
|
}
|
|
|
|
exit:
|
|
return exist == _TRUE ? RTW_ALREADY : (ent ? _SUCCESS : _FAIL);
|
|
}
|
|
|
|
int rtw_mesh_plink_add(_adapter *adapter, const u8 *hwaddr)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
_irqL irqL;
|
|
int ret;
|
|
|
|
_enter_critical_bh(&(plink_ctl->lock), &irqL);
|
|
ret = _rtw_mesh_plink_add(adapter, hwaddr);
|
|
_exit_critical_bh(&(plink_ctl->lock), &irqL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int rtw_mesh_plink_set_state(_adapter *adapter, const u8 *hwaddr, u8 state)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *ent = NULL;
|
|
_irqL irqL;
|
|
|
|
_enter_critical_bh(&(plink_ctl->lock), &irqL);
|
|
ent = _rtw_mesh_plink_get(adapter, hwaddr);
|
|
if (ent)
|
|
ent->plink_state = state;
|
|
_exit_critical_bh(&(plink_ctl->lock), &irqL);
|
|
|
|
exit:
|
|
return ent ? _SUCCESS : _FAIL;
|
|
}
|
|
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
int rtw_mesh_plink_set_aek(_adapter *adapter, const u8 *hwaddr, const u8 *aek)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *ent = NULL;
|
|
_irqL irqL;
|
|
|
|
_enter_critical_bh(&(plink_ctl->lock), &irqL);
|
|
ent = _rtw_mesh_plink_get(adapter, hwaddr);
|
|
if (ent) {
|
|
_rtw_memcpy(ent->aek, aek, 32);
|
|
ent->aek_valid = 1;
|
|
}
|
|
_exit_critical_bh(&(plink_ctl->lock), &irqL);
|
|
|
|
exit:
|
|
return ent ? _SUCCESS : _FAIL;
|
|
}
|
|
#endif
|
|
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
int rtw_mesh_plink_set_peer_conf_timeout(_adapter *adapter, const u8 *hwaddr)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *ent = NULL;
|
|
_irqL irqL;
|
|
|
|
_enter_critical_bh(&(plink_ctl->lock), &irqL);
|
|
ent = _rtw_mesh_plink_get(adapter, hwaddr);
|
|
if (ent) {
|
|
if (IS_PEER_CONF_DISABLED(ent))
|
|
SET_PEER_CONF_END_TIME(ent, mcfg->peer_sel_policy.peer_conf_timeout_ms);
|
|
}
|
|
_exit_critical_bh(&(plink_ctl->lock), &irqL);
|
|
|
|
exit:
|
|
return ent ? _SUCCESS : _FAIL;
|
|
}
|
|
#endif
|
|
|
|
void _rtw_mesh_plink_del_ent(_adapter *adapter, struct mesh_plink_ent *ent)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
|
|
ent->valid = _FALSE;
|
|
#ifdef CONFIG_RTW_MESH_DRIVER_AID
|
|
if (ent->tx_conf_ies && ent->tx_conf_ies_len)
|
|
rtw_mfree(ent->tx_conf_ies, ent->tx_conf_ies_len);
|
|
ent->tx_conf_ies = NULL;
|
|
ent->tx_conf_ies_len = 0;
|
|
#endif
|
|
if (ent->rx_conf_ies && ent->rx_conf_ies_len)
|
|
rtw_mfree(ent->rx_conf_ies, ent->rx_conf_ies_len);
|
|
ent->rx_conf_ies = NULL;
|
|
ent->rx_conf_ies_len = 0;
|
|
if (ent->scanned)
|
|
ent->scanned = NULL;
|
|
plink_ctl->num--;
|
|
}
|
|
|
|
int rtw_mesh_plink_del(_adapter *adapter, const u8 *hwaddr)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *ent = NULL;
|
|
u8 exist = _FALSE;
|
|
int i;
|
|
_irqL irqL;
|
|
|
|
_enter_critical_bh(&(plink_ctl->lock), &irqL);
|
|
for (i = 0; i < RTW_MESH_MAX_PEER_CANDIDATES; i++) {
|
|
if (plink_ctl->ent[i].valid == _TRUE
|
|
&& _rtw_memcmp(plink_ctl->ent[i].addr, hwaddr, ETH_ALEN) == _TRUE
|
|
) {
|
|
ent = &plink_ctl->ent[i];
|
|
exist = _TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (exist == _TRUE)
|
|
_rtw_mesh_plink_del_ent(adapter, ent);
|
|
|
|
_exit_critical_bh(&(plink_ctl->lock), &irqL);
|
|
|
|
exit:
|
|
return exist == _TRUE ? _SUCCESS : RTW_ALREADY;
|
|
}
|
|
|
|
void rtw_mesh_plink_ctl_init(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
int i;
|
|
|
|
_rtw_spinlock_init(&plink_ctl->lock);
|
|
plink_ctl->num = 0;
|
|
for (i = 0; i < RTW_MESH_MAX_PEER_CANDIDATES; i++)
|
|
plink_ctl->ent[i].valid = _FALSE;
|
|
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
_rtw_init_queue(&plink_ctl->peer_blacklist);
|
|
#endif
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
_rtw_init_queue(&plink_ctl->cto_mgate_blacklist);
|
|
#endif
|
|
}
|
|
|
|
void rtw_mesh_plink_ctl_deinit(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *ent;
|
|
int i;
|
|
_irqL irqL;
|
|
|
|
_enter_critical_bh(&(plink_ctl->lock), &irqL);
|
|
for (i = 0; i < RTW_MESH_MAX_PEER_CANDIDATES; i++) {
|
|
ent = &plink_ctl->ent[i];
|
|
#ifdef CONFIG_RTW_MESH_DRIVER_AID
|
|
if (ent->tx_conf_ies && ent->tx_conf_ies_len)
|
|
rtw_mfree(ent->tx_conf_ies, ent->tx_conf_ies_len);
|
|
#endif
|
|
if (ent->rx_conf_ies && ent->rx_conf_ies_len)
|
|
rtw_mfree(ent->rx_conf_ies, ent->rx_conf_ies_len);
|
|
}
|
|
_exit_critical_bh(&(plink_ctl->lock), &irqL);
|
|
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
rtw_mesh_peer_blacklist_flush(adapter);
|
|
_rtw_deinit_queue(&plink_ctl->peer_blacklist);
|
|
#endif
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
rtw_mesh_cto_mgate_blacklist_flush(adapter);
|
|
_rtw_deinit_queue(&plink_ctl->cto_mgate_blacklist);
|
|
#endif
|
|
}
|
|
|
|
void dump_mesh_plink_ctl(void *sel, _adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *ent;
|
|
int i;
|
|
|
|
RTW_PRINT_SEL(sel, "num:%u\n", plink_ctl->num);
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
RTW_PRINT_SEL(sel, "acnode_rsvd:%u\n", plink_ctl->acnode_rsvd);
|
|
#endif
|
|
|
|
for (i = 0; i < RTW_MESH_MAX_PEER_CANDIDATES; i++) {
|
|
ent = &plink_ctl->ent[i];
|
|
if (!ent->valid)
|
|
continue;
|
|
|
|
RTW_PRINT_SEL(sel, "\n");
|
|
RTW_PRINT_SEL(sel, "peer:"MAC_FMT"\n", MAC_ARG(ent->addr));
|
|
RTW_PRINT_SEL(sel, "plink_state:%s\n", rtw_mesh_plink_str(ent->plink_state));
|
|
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
if (ent->aek_valid)
|
|
RTW_PRINT_SEL(sel, "aek:"KEY_FMT KEY_FMT"\n", KEY_ARG(ent->aek), KEY_ARG(ent->aek + 16));
|
|
#endif
|
|
|
|
RTW_PRINT_SEL(sel, "llid:%u, plid:%u\n", ent->llid, ent->plid);
|
|
#ifndef CONFIG_RTW_MESH_DRIVER_AID
|
|
RTW_PRINT_SEL(sel, "aid:%u\n", ent->aid);
|
|
#endif
|
|
RTW_PRINT_SEL(sel, "peer_aid:%u\n", ent->peer_aid);
|
|
|
|
RTW_PRINT_SEL(sel, "chosen_pmk:"KEY_FMT"\n", KEY_ARG(ent->chosen_pmk));
|
|
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
RTW_PRINT_SEL(sel, "sel_pcs:%02x%02x%02x%02x\n"
|
|
, ent->sel_pcs[0], ent->sel_pcs[1], ent->sel_pcs[2], ent->sel_pcs[3]);
|
|
RTW_PRINT_SEL(sel, "l_nonce:"KEY_FMT KEY_FMT"\n", KEY_ARG(ent->l_nonce), KEY_ARG(ent->l_nonce + 16));
|
|
RTW_PRINT_SEL(sel, "p_nonce:"KEY_FMT KEY_FMT"\n", KEY_ARG(ent->p_nonce), KEY_ARG(ent->p_nonce + 16));
|
|
#endif
|
|
|
|
#ifdef CONFIG_RTW_MESH_DRIVER_AID
|
|
RTW_PRINT_SEL(sel, "tx_conf_ies:%p, len:%u\n", ent->tx_conf_ies, ent->tx_conf_ies_len);
|
|
#endif
|
|
RTW_PRINT_SEL(sel, "rx_conf_ies:%p, len:%u\n", ent->rx_conf_ies, ent->rx_conf_ies_len);
|
|
RTW_PRINT_SEL(sel, "scanned:%p\n", ent->scanned);
|
|
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
if (!IS_PEER_CONF_DISABLED(ent)) {
|
|
if (!IS_PEER_CONF_TIMEOUT(ent))
|
|
RTW_PRINT_SEL(sel, "peer_conf:%d\n", rtw_systime_to_ms(ent->peer_conf_end_time - rtw_get_current_time()));
|
|
else
|
|
RTW_PRINT_SEL(sel, "peer_conf:TIMEOUT\n");
|
|
}
|
|
#endif
|
|
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
if (!IS_CTO_MGATE_CONF_DISABLED(ent)) {
|
|
if (!IS_CTO_MGATE_CONF_TIMEOUT(ent))
|
|
RTW_PRINT_SEL(sel, "cto_mgate_conf:%d\n", rtw_systime_to_ms(ent->cto_mgate_conf_end_time - rtw_get_current_time()));
|
|
else
|
|
RTW_PRINT_SEL(sel, "cto_mgate_conf:TIMEOUT\n");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* this function is called with plink_ctl being locked */
|
|
int rtw_mesh_peer_establish(_adapter *adapter, struct mesh_plink_ent *plink, struct sta_info *sta)
|
|
{
|
|
#ifndef DBG_RTW_MESH_PEER_ESTABLISH
|
|
#define DBG_RTW_MESH_PEER_ESTABLISH 0
|
|
#endif
|
|
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
u8 *tlv_ies;
|
|
u16 tlv_ieslen;
|
|
struct rtw_ieee802_11_elems elems;
|
|
_irqL irqL;
|
|
int i;
|
|
int ret = _FAIL;
|
|
|
|
if (!plink->rx_conf_ies || !plink->rx_conf_ies_len) {
|
|
RTW_INFO(FUNC_ADPT_FMT" no rx confirm from sta "MAC_FMT"\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(sta->cmn.mac_addr));
|
|
goto exit;
|
|
}
|
|
|
|
if (plink->rx_conf_ies_len < 4) {
|
|
RTW_INFO(FUNC_ADPT_FMT" confirm from sta "MAC_FMT" too short\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(sta->cmn.mac_addr));
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef CONFIG_RTW_MESH_DRIVER_AID
|
|
if (!plink->tx_conf_ies || !plink->tx_conf_ies_len) {
|
|
RTW_INFO(FUNC_ADPT_FMT" no tx confirm to sta "MAC_FMT"\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(sta->cmn.mac_addr));
|
|
goto exit;
|
|
}
|
|
|
|
if (plink->tx_conf_ies_len < 4) {
|
|
RTW_INFO(FUNC_ADPT_FMT" confirm to sta "MAC_FMT" too short\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(sta->cmn.mac_addr));
|
|
goto exit;
|
|
}
|
|
#endif
|
|
|
|
tlv_ies = plink->rx_conf_ies + 4;
|
|
tlv_ieslen = plink->rx_conf_ies_len - 4;
|
|
|
|
if (DBG_RTW_MESH_PEER_ESTABLISH)
|
|
dump_ies(RTW_DBGDUMP, tlv_ies, tlv_ieslen);
|
|
|
|
if (rtw_ieee802_11_parse_elems(tlv_ies, tlv_ieslen, &elems, 1) == ParseFailed) {
|
|
RTW_INFO(FUNC_ADPT_FMT" sta "MAC_FMT" sent invalid confirm\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(sta->cmn.mac_addr));
|
|
goto exit;
|
|
}
|
|
|
|
SET_PEER_CONF_DISABLED(plink);
|
|
if (rtw_bss_is_cto_mgate(&plink->scanned->network)
|
|
&& !rtw_bss_is_forwarding(&plink->scanned->network))
|
|
SET_CTO_MGATE_CONF_END_TIME(plink, mcfg->peer_sel_policy.cto_mgate_conf_timeout_ms);
|
|
else
|
|
SET_CTO_MGATE_CONF_DISABLED(plink);
|
|
|
|
sta->state &= (~WIFI_FW_AUTH_SUCCESS);
|
|
sta->state |= WIFI_FW_ASSOC_STATE;
|
|
|
|
rtw_ap_parse_sta_capability(adapter, sta, plink->rx_conf_ies);
|
|
|
|
if (rtw_ap_parse_sta_supported_rates(adapter, sta, tlv_ies, tlv_ieslen) != _STATS_SUCCESSFUL_)
|
|
goto exit;
|
|
|
|
if (rtw_ap_parse_sta_security_ie(adapter, sta, &elems) != _STATS_SUCCESSFUL_)
|
|
goto exit;
|
|
|
|
rtw_ap_parse_sta_wmm_ie(adapter, sta, tlv_ies, tlv_ieslen);
|
|
#ifdef CONFIG_RTS_FULL_BW
|
|
/*check vendor IE*/
|
|
rtw_parse_sta_vendor_ie_8812(adapter, sta, tlv_ies, tlv_ieslen);
|
|
#endif/*CONFIG_RTS_FULL_BW*/
|
|
|
|
rtw_ap_parse_sta_ht_ie(adapter, sta, &elems);
|
|
rtw_ap_parse_sta_vht_ie(adapter, sta, &elems);
|
|
|
|
/* AID */
|
|
#ifdef CONFIG_RTW_MESH_DRIVER_AID
|
|
sta->cmn.aid = RTW_GET_LE16(plink->tx_conf_ies + 2);
|
|
#else
|
|
sta->cmn.aid = plink->aid;
|
|
#endif
|
|
stapriv->sta_aid[sta->cmn.aid - 1] = sta;
|
|
RTW_INFO(FUNC_ADPT_FMT" sta "MAC_FMT" aid:%u\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(sta->cmn.mac_addr), sta->cmn.aid);
|
|
|
|
sta->state &= (~WIFI_FW_ASSOC_STATE);
|
|
sta->state |= WIFI_FW_ASSOC_SUCCESS;
|
|
|
|
sta->local_mps = RTW_MESH_PS_ACTIVE;
|
|
|
|
rtw_ewma_err_rate_init(&sta->metrics.err_rate);
|
|
rtw_ewma_err_rate_add(&sta->metrics.err_rate, 1);
|
|
/* init data_rate to 1M */
|
|
sta->metrics.data_rate = 10;
|
|
|
|
_enter_critical_bh(&stapriv->asoc_list_lock, &irqL);
|
|
if (rtw_is_list_empty(&sta->asoc_list)) {
|
|
STA_SET_MESH_PLINK(sta, plink);
|
|
/* TBD: up layer timeout mechanism */
|
|
/* sta->expire_to = mcfg->plink_timeout / 2; */
|
|
rtw_list_insert_tail(&sta->asoc_list, &stapriv->asoc_list);
|
|
stapriv->asoc_list_cnt++;
|
|
}
|
|
_exit_critical_bh(&stapriv->asoc_list_lock, &irqL);
|
|
|
|
bss_cap_update_on_sta_join(adapter, sta);
|
|
sta_info_update(adapter, sta);
|
|
report_add_sta_event(adapter, sta->cmn.mac_addr);
|
|
|
|
ret = _SUCCESS;
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
void rtw_mesh_expire_peer_notify(_adapter *adapter, const u8 *peer_addr)
|
|
{
|
|
u8 null_ssid[2] = {0, 0};
|
|
|
|
#ifdef CONFIG_IOCTL_CFG80211
|
|
rtw_cfg80211_notify_new_peer_candidate(adapter->rtw_wdev
|
|
, peer_addr
|
|
, null_ssid
|
|
, 2
|
|
, GFP_ATOMIC
|
|
);
|
|
#endif
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
static u8 *rtw_mesh_construct_peer_mesh_close(_adapter *adapter, struct mesh_plink_ent *plink, u16 reason, u32 *len)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
u8 *frame = NULL, *pos;
|
|
u32 flen;
|
|
struct rtw_ieee80211_hdr *whdr;
|
|
|
|
if (minfo->mesh_auth_id && !MESH_PLINK_AEK_VALID(plink))
|
|
goto exit;
|
|
|
|
flen = sizeof(struct rtw_ieee80211_hdr_3addr)
|
|
+ 2 /* category, action */
|
|
+ 2 + minfo->mesh_id_len /* mesh id */
|
|
+ 2 + 8 + (minfo->mesh_auth_id ? 16 : 0) /* mpm */
|
|
+ (minfo->mesh_auth_id ? 2 + AES_BLOCK_SIZE : 0) /* mic */
|
|
+ (minfo->mesh_auth_id ? 70 : 0) /* ampe */
|
|
;
|
|
|
|
pos = frame = rtw_zmalloc(flen);
|
|
if (!frame)
|
|
goto exit;
|
|
|
|
whdr = (struct rtw_ieee80211_hdr *)frame;
|
|
_rtw_memcpy(whdr->addr1, adapter_mac_addr(adapter), ETH_ALEN);
|
|
_rtw_memcpy(whdr->addr2, plink->addr, ETH_ALEN);
|
|
_rtw_memcpy(whdr->addr3, adapter_mac_addr(adapter), ETH_ALEN);
|
|
|
|
set_frame_sub_type(frame, WIFI_ACTION);
|
|
|
|
pos += sizeof(struct rtw_ieee80211_hdr_3addr);
|
|
*(pos++) = RTW_WLAN_CATEGORY_SELF_PROTECTED;
|
|
*(pos++) = RTW_ACT_SELF_PROTECTED_MESH_CLOSE;
|
|
|
|
pos = rtw_set_ie_mesh_id(pos, NULL, minfo->mesh_id, minfo->mesh_id_len);
|
|
|
|
pos = rtw_set_ie_mpm(pos, NULL
|
|
, minfo->mesh_auth_id ? 1 : 0
|
|
, plink->plid
|
|
, &plink->llid
|
|
, &reason
|
|
, minfo->mesh_auth_id ? plink->chosen_pmk : NULL);
|
|
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
if (minfo->mesh_auth_id) {
|
|
u8 ampe_buf[70];
|
|
int enc_ret;
|
|
|
|
*pos = WLAN_EID_MIC;
|
|
*(pos + 1) = AES_BLOCK_SIZE;
|
|
|
|
ampe_buf[0] = WLAN_EID_AMPE;
|
|
ampe_buf[1] = 68;
|
|
_rtw_memcpy(ampe_buf + 2, plink->sel_pcs, 4);
|
|
_rtw_memcpy(ampe_buf + 6, plink->p_nonce, 32);
|
|
_rtw_memcpy(ampe_buf + 38, plink->l_nonce, 32);
|
|
|
|
enc_ret = rtw_mpm_ampe_enc(adapter, plink
|
|
, frame + sizeof(struct rtw_ieee80211_hdr_3addr)
|
|
, pos, ampe_buf, 1);
|
|
if (enc_ret != _SUCCESS) {
|
|
rtw_mfree(frame, flen);
|
|
frame = NULL;
|
|
goto exit;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
*len = flen;
|
|
|
|
exit:
|
|
return frame;
|
|
}
|
|
|
|
void _rtw_mesh_expire_peer_ent(_adapter *adapter, struct mesh_plink_ent *plink)
|
|
{
|
|
#if defined(CONFIG_RTW_MESH_STA_DEL_DISASOC)
|
|
_rtw_mesh_plink_del_ent(adapter, plink);
|
|
rtw_cfg80211_indicate_sta_disassoc(adapter, plink->addr, 0);
|
|
#else
|
|
u8 *frame = NULL;
|
|
u32 flen;
|
|
|
|
if (plink->plink_state == RTW_MESH_PLINK_ESTAB)
|
|
frame = rtw_mesh_construct_peer_mesh_close(adapter, plink, WLAN_REASON_MESH_CLOSE, &flen);
|
|
|
|
if (frame) {
|
|
struct mlme_ext_priv *mlmeext = &adapter->mlmeextpriv;
|
|
struct wireless_dev *wdev = adapter->rtw_wdev;
|
|
s32 freq = rtw_ch2freq(mlmeext->cur_channel);
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
|
|
rtw_cfg80211_rx_mgmt(wdev, freq, 0, frame, flen, GFP_ATOMIC);
|
|
#else
|
|
cfg80211_rx_action(adapter->pnetdev, freq, frame, flen, GFP_ATOMIC);
|
|
#endif
|
|
|
|
rtw_mfree(frame, flen);
|
|
} else {
|
|
rtw_mesh_expire_peer_notify(adapter, plink->addr);
|
|
RTW_INFO(FUNC_ADPT_FMT" set "MAC_FMT" plink unknown\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(plink->addr));
|
|
plink->plink_state = RTW_MESH_PLINK_UNKNOWN;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void rtw_mesh_expire_peer(_adapter *adapter, const u8 *peer_addr)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct mesh_plink_pool *plink_ctl = &minfo->plink_ctl;
|
|
struct mesh_plink_ent *plink;
|
|
_irqL irqL;
|
|
|
|
_enter_critical_bh(&(plink_ctl->lock), &irqL);
|
|
|
|
plink = _rtw_mesh_plink_get(adapter, peer_addr);
|
|
if (!plink)
|
|
goto exit;
|
|
|
|
_rtw_mesh_expire_peer_ent(adapter, plink);
|
|
|
|
exit:
|
|
_exit_critical_bh(&(plink_ctl->lock), &irqL);
|
|
}
|
|
|
|
u8 rtw_mesh_ps_annc(_adapter *adapter, u8 ps)
|
|
{
|
|
_irqL irqL;
|
|
_list *head, *list;
|
|
struct sta_info *sta;
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
u8 sta_alive_num = 0, i;
|
|
char sta_alive_list[NUM_STA];
|
|
u8 annc_cnt = 0;
|
|
|
|
if (rtw_linked_check(adapter) == _FALSE)
|
|
goto exit;
|
|
|
|
_enter_critical_bh(&stapriv->asoc_list_lock, &irqL);
|
|
|
|
head = &stapriv->asoc_list;
|
|
list = get_next(head);
|
|
while ((rtw_end_of_queue_search(head, list)) == _FALSE) {
|
|
int stainfo_offset;
|
|
|
|
sta = LIST_CONTAINOR(list, struct sta_info, asoc_list);
|
|
list = get_next(list);
|
|
|
|
stainfo_offset = rtw_stainfo_offset(stapriv, sta);
|
|
if (stainfo_offset_valid(stainfo_offset))
|
|
sta_alive_list[sta_alive_num++] = stainfo_offset;
|
|
}
|
|
_exit_critical_bh(&stapriv->asoc_list_lock, &irqL);
|
|
|
|
for (i = 0; i < sta_alive_num; i++) {
|
|
sta = rtw_get_stainfo_by_offset(stapriv, sta_alive_list[i]);
|
|
if (!sta)
|
|
continue;
|
|
|
|
issue_qos_nulldata(adapter, sta->cmn.mac_addr, 7, ps, 3, 500);
|
|
annc_cnt++;
|
|
}
|
|
|
|
exit:
|
|
return annc_cnt;
|
|
}
|
|
|
|
static void mpath_tx_tasklet_hdl(void *priv)
|
|
{
|
|
_adapter *adapter = (_adapter *)priv;
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct xmit_frame *xframe;
|
|
_list *list, *head;
|
|
_list tmp;
|
|
u32 tmp_len;
|
|
s32 res;
|
|
|
|
_rtw_init_listhead(&tmp);
|
|
|
|
while (1) {
|
|
tmp_len = 0;
|
|
enter_critical_bh(&minfo->mpath_tx_queue.lock);
|
|
if (minfo->mpath_tx_queue_len) {
|
|
rtw_list_splice_init(&minfo->mpath_tx_queue.queue, &tmp);
|
|
tmp_len = minfo->mpath_tx_queue_len;
|
|
minfo->mpath_tx_queue_len = 0;
|
|
}
|
|
exit_critical_bh(&minfo->mpath_tx_queue.lock);
|
|
|
|
if (!tmp_len)
|
|
break;
|
|
|
|
head = &tmp;
|
|
list = get_next(head);
|
|
while (rtw_end_of_queue_search(head, list) == _FALSE) {
|
|
xframe = LIST_CONTAINOR(list, struct xmit_frame, list);
|
|
list = get_next(list);
|
|
rtw_list_delete(&xframe->list);
|
|
res = rtw_xmit_posthandle(adapter, xframe, xframe->pkt);
|
|
if (res < 0) {
|
|
#ifdef DBG_TX_DROP_FRAME
|
|
RTW_INFO("DBG_TX_DROP_FRAME %s rtw_xmit fail\n", __FUNCTION__);
|
|
#endif
|
|
adapter->xmitpriv.tx_drop++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void rtw_mpath_tx_queue_flush(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct xmit_frame *xframe;
|
|
_list *list, *head;
|
|
_list tmp;
|
|
|
|
_rtw_init_listhead(&tmp);
|
|
|
|
enter_critical_bh(&minfo->mpath_tx_queue.lock);
|
|
rtw_list_splice_init(&minfo->mpath_tx_queue.queue, &tmp);
|
|
minfo->mpath_tx_queue_len = 0;
|
|
exit_critical_bh(&minfo->mpath_tx_queue.lock);
|
|
|
|
head = &tmp;
|
|
list = get_next(head);
|
|
while (rtw_end_of_queue_search(head, list) == _FALSE) {
|
|
xframe = LIST_CONTAINOR(list, struct xmit_frame, list);
|
|
list = get_next(list);
|
|
rtw_list_delete(&xframe->list);
|
|
rtw_free_xmitframe(&adapter->xmitpriv, xframe);
|
|
}
|
|
}
|
|
|
|
#ifdef PLATFORM_LINUX /* 3.10 ~ 4.13 checked */
|
|
#if defined(CONFIG_SLUB)
|
|
#include <linux/slub_def.h>
|
|
#elif defined(CONFIG_SLAB)
|
|
#include <linux/slab_def.h>
|
|
#endif
|
|
typedef struct kmem_cache rtw_mcache;
|
|
#endif
|
|
|
|
rtw_mcache *rtw_mcache_create(const char *name, size_t size)
|
|
{
|
|
#ifdef PLATFORM_LINUX /* 3.10 ~ 4.13 checked */
|
|
return kmem_cache_create(name, size, 0, 0, NULL);
|
|
#else
|
|
#error "TBD\n";
|
|
#endif
|
|
}
|
|
|
|
void rtw_mcache_destroy(rtw_mcache *s)
|
|
{
|
|
#ifdef PLATFORM_LINUX /* 3.10 ~ 4.13 checked */
|
|
kmem_cache_destroy(s);
|
|
#else
|
|
#error "TBD\n";
|
|
#endif
|
|
}
|
|
|
|
void *_rtw_mcache_alloc(rtw_mcache *cachep)
|
|
{
|
|
#ifdef PLATFORM_LINUX /* 3.10 ~ 4.13 checked */
|
|
return kmem_cache_alloc(cachep, GFP_ATOMIC);
|
|
#else
|
|
#error "TBD\n";
|
|
#endif
|
|
}
|
|
|
|
void _rtw_mcache_free(rtw_mcache *cachep, void *objp)
|
|
{
|
|
#ifdef PLATFORM_LINUX /* 3.10 ~ 4.13 checked */
|
|
kmem_cache_free(cachep, objp);
|
|
#else
|
|
#error "TBD\n";
|
|
#endif
|
|
}
|
|
|
|
#ifdef DBG_MEM_ALLOC
|
|
inline void *dbg_rtw_mcache_alloc(rtw_mcache *cachep, const enum mstat_f flags, const char *func, const int line)
|
|
{
|
|
void *p;
|
|
u32 sz = cachep->size;
|
|
|
|
if (match_mstat_sniff_rules(flags, sz))
|
|
RTW_INFO("DBG_MEM_ALLOC %s:%d %s(%u)\n", func, line, __func__, sz);
|
|
|
|
p = _rtw_mcache_alloc(cachep);
|
|
|
|
rtw_mstat_update(
|
|
flags
|
|
, p ? MSTAT_ALLOC_SUCCESS : MSTAT_ALLOC_FAIL
|
|
, sz
|
|
);
|
|
|
|
return p;
|
|
}
|
|
|
|
inline void dbg_rtw_mcache_free(rtw_mcache *cachep, void *pbuf, const enum mstat_f flags, const char *func, const int line)
|
|
{
|
|
u32 sz = cachep->size;
|
|
|
|
if (match_mstat_sniff_rules(flags, sz))
|
|
RTW_INFO("DBG_MEM_ALLOC %s:%d %s(%u)\n", func, line, __func__, sz);
|
|
|
|
_rtw_mcache_free(cachep, pbuf);
|
|
|
|
rtw_mstat_update(
|
|
flags
|
|
, MSTAT_FREE
|
|
, sz
|
|
);
|
|
}
|
|
|
|
#define rtw_mcache_alloc(cachep) dbg_rtw_mcache_alloc(cachep, MSTAT_TYPE_PHY, __FUNCTION__, __LINE__)
|
|
#define rtw_mcache_free(cachep, objp) dbg_rtw_mcache_free(cachep, objp, MSTAT_TYPE_PHY, __FUNCTION__, __LINE__)
|
|
#else
|
|
#define rtw_mcache_alloc(cachep) _rtw_mcache_alloc(cachep)
|
|
#define rtw_mcache_free(cachep, objp) _rtw_mcache_free(cachep, objp)
|
|
#endif /* DBG_MEM_ALLOC */
|
|
|
|
/* Mesh Received Cache */
|
|
#define RTW_MRC_BUCKETS 256 /* must be a power of 2 */
|
|
#define RTW_MRC_QUEUE_MAX_LEN 4
|
|
#define RTW_MRC_TIMEOUT_MS (3 * 1000)
|
|
|
|
/**
|
|
* struct rtw_mrc_entry - entry in the Mesh Received Cache
|
|
*
|
|
* @seqnum: mesh sequence number of the frame
|
|
* @exp_time: expiration time of the entry
|
|
* @msa: mesh source address of the frame
|
|
* @list: hashtable list pointer
|
|
*
|
|
* The Mesh Received Cache keeps track of the latest received frames that
|
|
* have been received by a mesh interface and discards received frames
|
|
* that are found in the cache.
|
|
*/
|
|
struct rtw_mrc_entry {
|
|
rtw_hlist_node list;
|
|
systime exp_time;
|
|
u32 seqnum;
|
|
u8 msa[ETH_ALEN];
|
|
};
|
|
|
|
struct rtw_mrc {
|
|
rtw_hlist_head bucket[RTW_MRC_BUCKETS];
|
|
u32 idx_mask;
|
|
rtw_mcache *cache;
|
|
};
|
|
|
|
static int rtw_mrc_init(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
char cache_name[IFNAMSIZ + 8 + 1];
|
|
int i;
|
|
|
|
minfo->mrc = rtw_malloc(sizeof(struct rtw_mrc));
|
|
if (!minfo->mrc)
|
|
return -ENOMEM;
|
|
minfo->mrc->idx_mask = RTW_MRC_BUCKETS - 1;
|
|
for (i = 0; i < RTW_MRC_BUCKETS; i++)
|
|
rtw_hlist_head_init(&minfo->mrc->bucket[i]);
|
|
|
|
sprintf(cache_name, "rtw_mrc_%s", ADPT_ARG(adapter));
|
|
minfo->mrc->cache = rtw_mcache_create(cache_name, sizeof(struct rtw_mrc_entry));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rtw_mrc_free(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct rtw_mrc *mrc = minfo->mrc;
|
|
struct rtw_mrc_entry *p;
|
|
rtw_hlist_node *np, *n;
|
|
int i;
|
|
|
|
if (!mrc)
|
|
return;
|
|
|
|
for (i = 0; i < RTW_MRC_BUCKETS; i++) {
|
|
rtw_hlist_for_each_entry_safe(p, np, n, &mrc->bucket[i], list) {
|
|
rtw_hlist_del(&p->list);
|
|
rtw_mcache_free(mrc->cache, p);
|
|
}
|
|
}
|
|
|
|
rtw_mcache_destroy(mrc->cache);
|
|
|
|
rtw_mfree(mrc, sizeof(struct rtw_mrc));
|
|
minfo->mrc = NULL;
|
|
}
|
|
|
|
/**
|
|
* rtw_mrc_check - Check frame in mesh received cache and add if absent.
|
|
*
|
|
* @adapter: interface
|
|
* @msa: mesh source address
|
|
* @seq: mesh seq number
|
|
*
|
|
* Returns: 0 if the frame is not in the cache, nonzero otherwise.
|
|
*
|
|
* Checks using the mesh source address and the mesh sequence number if we have
|
|
* received this frame lately. If the frame is not in the cache, it is added to
|
|
* it.
|
|
*/
|
|
static int rtw_mrc_check(_adapter *adapter, const u8 *msa, u32 seq)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct rtw_mrc *mrc = minfo->mrc;
|
|
int entries = 0;
|
|
u8 idx;
|
|
struct rtw_mrc_entry *p;
|
|
rtw_hlist_node *np, *n;
|
|
u8 timeout;
|
|
|
|
if (!mrc)
|
|
return -1;
|
|
|
|
idx = seq & mrc->idx_mask;
|
|
rtw_hlist_for_each_entry_safe(p, np, n, &mrc->bucket[idx], list) {
|
|
++entries;
|
|
timeout = rtw_time_after(rtw_get_current_time(), p->exp_time);
|
|
if (timeout || entries == RTW_MRC_QUEUE_MAX_LEN) {
|
|
if (!timeout)
|
|
minfo->mshstats.mrc_del_qlen++;
|
|
|
|
rtw_hlist_del(&p->list);
|
|
rtw_mcache_free(mrc->cache, p);
|
|
--entries;
|
|
} else if ((seq == p->seqnum) && _rtw_memcmp(msa, p->msa, ETH_ALEN) == _TRUE)
|
|
return -1;
|
|
}
|
|
|
|
p = rtw_mcache_alloc(mrc->cache);
|
|
if (!p)
|
|
return 0;
|
|
|
|
p->seqnum = seq;
|
|
p->exp_time = rtw_get_current_time() + rtw_ms_to_systime(RTW_MRC_TIMEOUT_MS);
|
|
_rtw_memcpy(p->msa, msa, ETH_ALEN);
|
|
rtw_hlist_add_head(&p->list, &mrc->bucket[idx]);
|
|
return 0;
|
|
}
|
|
|
|
static int rtw_mesh_decache(_adapter *adapter, const u8 *msa, u32 seq)
|
|
{
|
|
return rtw_mrc_check(adapter, msa, seq);
|
|
}
|
|
|
|
#ifndef RTW_MESH_SCAN_RESULT_EXP_MS
|
|
#define RTW_MESH_SCAN_RESULT_EXP_MS (10 * 1000)
|
|
#endif
|
|
|
|
#ifndef RTW_MESH_ACNODE_PREVENT
|
|
#define RTW_MESH_ACNODE_PREVENT 0
|
|
#endif
|
|
#ifndef RTW_MESH_ACNODE_CONF_TIMEOUT_MS
|
|
#define RTW_MESH_ACNODE_CONF_TIMEOUT_MS (20 * 1000)
|
|
#endif
|
|
#ifndef RTW_MESH_ACNODE_NOTIFY_TIMEOUT_MS
|
|
#define RTW_MESH_ACNODE_NOTIFY_TIMEOUT_MS (2 * 1000)
|
|
#endif
|
|
|
|
#ifndef RTW_MESH_OFFCH_CAND
|
|
#define RTW_MESH_OFFCH_CAND 1
|
|
#endif
|
|
#ifndef RTW_MESH_OFFCH_CAND_FIND_INT_MS
|
|
#define RTW_MESH_OFFCH_CAND_FIND_INT_MS (10 * 1000)
|
|
#endif
|
|
|
|
#ifndef RTW_MESH_PEER_CONF_TIMEOUT_MS
|
|
#define RTW_MESH_PEER_CONF_TIMEOUT_MS (20 * 1000)
|
|
#endif
|
|
#ifndef RTW_MESH_PEER_BLACKLIST_TIMEOUT_MS
|
|
#define RTW_MESH_PEER_BLACKLIST_TIMEOUT_MS (20 * 1000)
|
|
#endif
|
|
|
|
#ifndef RTW_MESH_CTO_MGATE_REQUIRE
|
|
#define RTW_MESH_CTO_MGATE_REQUIRE 0
|
|
#endif
|
|
#ifndef RTW_MESH_CTO_MGATE_CONF_TIMEOUT_MS
|
|
#define RTW_MESH_CTO_MGATE_CONF_TIMEOUT_MS (20 * 1000)
|
|
#endif
|
|
#ifndef RTW_MESH_CTO_MGATE_BLACKLIST_TIMEOUT_MS
|
|
#define RTW_MESH_CTO_MGATE_BLACKLIST_TIMEOUT_MS (20 * 1000)
|
|
#endif
|
|
|
|
void rtw_mesh_cfg_init_peer_sel_policy(struct rtw_mesh_cfg *mcfg)
|
|
{
|
|
struct mesh_peer_sel_policy *sel_policy = &mcfg->peer_sel_policy;
|
|
|
|
sel_policy->scanr_exp_ms = RTW_MESH_SCAN_RESULT_EXP_MS;
|
|
|
|
#if CONFIG_RTW_MESH_ACNODE_PREVENT
|
|
sel_policy->acnode_prevent = RTW_MESH_ACNODE_PREVENT;
|
|
sel_policy->acnode_conf_timeout_ms = RTW_MESH_ACNODE_CONF_TIMEOUT_MS;
|
|
sel_policy->acnode_notify_timeout_ms = RTW_MESH_ACNODE_NOTIFY_TIMEOUT_MS;
|
|
#endif
|
|
|
|
#if CONFIG_RTW_MESH_OFFCH_CAND
|
|
sel_policy->offch_cand = RTW_MESH_OFFCH_CAND;
|
|
sel_policy->offch_find_int_ms = RTW_MESH_OFFCH_CAND_FIND_INT_MS;
|
|
#endif
|
|
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
sel_policy->peer_conf_timeout_ms = RTW_MESH_PEER_CONF_TIMEOUT_MS;
|
|
sel_policy->peer_blacklist_timeout_ms = RTW_MESH_PEER_BLACKLIST_TIMEOUT_MS;
|
|
#endif
|
|
|
|
#if CONFIG_RTW_MESH_CTO_MGATE_BLACKLIST
|
|
sel_policy->cto_mgate_require = RTW_MESH_CTO_MGATE_REQUIRE;
|
|
sel_policy->cto_mgate_conf_timeout_ms = RTW_MESH_CTO_MGATE_CONF_TIMEOUT_MS;
|
|
sel_policy->cto_mgate_blacklist_timeout_ms = RTW_MESH_CTO_MGATE_BLACKLIST_TIMEOUT_MS;
|
|
#endif
|
|
}
|
|
|
|
void rtw_mesh_cfg_init(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
|
|
mcfg->max_peer_links = RTW_MESH_MAX_PEER_LINKS;
|
|
mcfg->plink_timeout = RTW_MESH_PEER_LINK_TIMEOUT;
|
|
|
|
mcfg->dot11MeshTTL = RTW_MESH_TTL;
|
|
mcfg->element_ttl = RTW_MESH_DEFAULT_ELEMENT_TTL;
|
|
mcfg->dot11MeshHWMPmaxPREQretries = RTW_MESH_MAX_PREQ_RETRIES;
|
|
mcfg->path_refresh_time = RTW_MESH_PATH_REFRESH_TIME;
|
|
mcfg->min_discovery_timeout = RTW_MESH_MIN_DISCOVERY_TIMEOUT;
|
|
mcfg->dot11MeshHWMPactivePathTimeout = RTW_MESH_PATH_TIMEOUT;
|
|
mcfg->dot11MeshHWMPpreqMinInterval = RTW_MESH_PREQ_MIN_INT;
|
|
mcfg->dot11MeshHWMPperrMinInterval = RTW_MESH_PERR_MIN_INT;
|
|
mcfg->dot11MeshHWMPnetDiameterTraversalTime = RTW_MESH_DIAM_TRAVERSAL_TIME;
|
|
mcfg->dot11MeshHWMPRootMode = RTW_IEEE80211_ROOTMODE_NO_ROOT;
|
|
mcfg->dot11MeshHWMPRannInterval = RTW_MESH_RANN_INTERVAL;
|
|
mcfg->dot11MeshGateAnnouncementProtocol = _FALSE;
|
|
mcfg->dot11MeshForwarding = _TRUE;
|
|
mcfg->rssi_threshold = 0;
|
|
mcfg->dot11MeshHWMPactivePathToRootTimeout = RTW_MESH_PATH_TO_ROOT_TIMEOUT;
|
|
mcfg->dot11MeshHWMProotInterval = RTW_MESH_ROOT_INTERVAL;
|
|
mcfg->dot11MeshHWMPconfirmationInterval = RTW_MESH_ROOT_CONFIRMATION_INTERVAL;
|
|
mcfg->path_gate_timeout_factor = 3;
|
|
rtw_mesh_cfg_init_peer_sel_policy(mcfg);
|
|
#ifdef CONFIG_RTW_MESH_ADD_ROOT_CHK
|
|
mcfg->sane_metric_delta = RTW_MESH_SANE_METRIC_DELTA;
|
|
mcfg->max_root_add_chk_cnt = RTW_MESH_MAX_ROOT_ADD_CHK_CNT;
|
|
#endif
|
|
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
mcfg->b2u_flags_msrc = 0;
|
|
mcfg->b2u_flags_mfwd = RTW_MESH_B2U_GA_UCAST;
|
|
#endif
|
|
}
|
|
|
|
void rtw_mesh_cfg_init_max_peer_links(_adapter *adapter, u8 stack_conf)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
|
|
mcfg->max_peer_links = RTW_MESH_MAX_PEER_LINKS;
|
|
|
|
if (mcfg->max_peer_links > stack_conf)
|
|
mcfg->max_peer_links = stack_conf;
|
|
}
|
|
|
|
void rtw_mesh_cfg_init_plink_timeout(_adapter *adapter, u32 stack_conf)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
|
|
mcfg->plink_timeout = stack_conf;
|
|
}
|
|
|
|
void rtw_mesh_init_mesh_info(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
|
|
memset(minfo, 0, sizeof(struct rtw_mesh_info));
|
|
|
|
rtw_mesh_plink_ctl_init(adapter);
|
|
|
|
minfo->last_preq = rtw_get_current_time();
|
|
/* minfo->last_sn_update = rtw_get_current_time(); */
|
|
minfo->next_perr = rtw_get_current_time();
|
|
|
|
atomic_set(&minfo->mpaths, 0);
|
|
rtw_mesh_pathtbl_init(adapter);
|
|
|
|
_rtw_init_queue(&minfo->mpath_tx_queue);
|
|
tasklet_init(&minfo->mpath_tx_tasklet
|
|
, (void(*)(unsigned long))mpath_tx_tasklet_hdl
|
|
, (unsigned long)adapter);
|
|
|
|
rtw_mrc_init(adapter);
|
|
|
|
_rtw_init_listhead(&minfo->preq_queue.list);
|
|
_rtw_spinlock_init(&minfo->mesh_preq_queue_lock);
|
|
|
|
rtw_init_timer(&adapter->mesh_path_timer, adapter, rtw_ieee80211_mesh_path_timer, adapter);
|
|
rtw_init_timer(&adapter->mesh_path_root_timer, adapter, rtw_ieee80211_mesh_path_root_timer, adapter);
|
|
rtw_init_timer(&adapter->mesh_atlm_param_req_timer, adapter, rtw_mesh_atlm_param_req_timer, adapter);
|
|
_init_workitem(&adapter->mesh_work, rtw_mesh_work_hdl, NULL);
|
|
}
|
|
|
|
void rtw_mesh_deinit_mesh_info(_adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
|
|
tasklet_kill(&minfo->mpath_tx_tasklet);
|
|
rtw_mpath_tx_queue_flush(adapter);
|
|
_rtw_deinit_queue(&adapter->mesh_info.mpath_tx_queue);
|
|
|
|
rtw_mrc_free(adapter);
|
|
|
|
rtw_mesh_pathtbl_unregister(adapter);
|
|
|
|
rtw_mesh_plink_ctl_deinit(adapter);
|
|
|
|
_cancel_workitem_sync(&adapter->mesh_work);
|
|
_cancel_timer_ex(&adapter->mesh_path_timer);
|
|
_cancel_timer_ex(&adapter->mesh_path_root_timer);
|
|
_cancel_timer_ex(&adapter->mesh_atlm_param_req_timer);
|
|
}
|
|
|
|
/**
|
|
* rtw_mesh_nexthop_resolve - lookup next hop; conditionally start path discovery
|
|
*
|
|
* @skb: 802.11 frame to be sent
|
|
* @sdata: network subif the frame will be sent through
|
|
*
|
|
* Lookup next hop for given skb and start path discovery if no
|
|
* forwarding information is found.
|
|
*
|
|
* Returns: 0 if the next hop was found and -ENOENT if the frame was queued.
|
|
* skb is freeed here if no mpath could be allocated.
|
|
*/
|
|
int rtw_mesh_nexthop_resolve(_adapter *adapter,
|
|
struct xmit_frame *xframe)
|
|
{
|
|
struct pkt_attrib *attrib = &xframe->attrib;
|
|
struct rtw_mesh_path *mpath;
|
|
struct xmit_frame *xframe_to_free = NULL;
|
|
u8 *target_addr = attrib->mda;
|
|
int err = 0;
|
|
int ret = _SUCCESS;
|
|
|
|
rtw_rcu_read_lock();
|
|
err = rtw_mesh_nexthop_lookup(adapter, target_addr, attrib->msa, attrib->ra);
|
|
if (!err)
|
|
goto endlookup;
|
|
|
|
/* no nexthop found, start resolving */
|
|
mpath = rtw_mesh_path_lookup(adapter, target_addr);
|
|
if (!mpath) {
|
|
mpath = rtw_mesh_path_add(adapter, target_addr);
|
|
if (IS_ERR(mpath)) {
|
|
xframe->pkt = NULL; /* free pkt outside */
|
|
rtw_mesh_path_discard_frame(adapter, xframe);
|
|
err = PTR_ERR(mpath);
|
|
ret = _FAIL;
|
|
goto endlookup;
|
|
}
|
|
}
|
|
|
|
if (!(mpath->flags & RTW_MESH_PATH_RESOLVING))
|
|
rtw_mesh_queue_preq(mpath, RTW_PREQ_Q_F_START);
|
|
|
|
enter_critical_bh(&mpath->frame_queue.lock);
|
|
|
|
if (mpath->frame_queue_len >= RTW_MESH_FRAME_QUEUE_LEN) {
|
|
xframe_to_free = LIST_CONTAINOR(get_next(get_list_head(&mpath->frame_queue)), struct xmit_frame, list);
|
|
rtw_list_delete(&(xframe_to_free->list));
|
|
mpath->frame_queue_len--;
|
|
}
|
|
|
|
rtw_list_insert_tail(&xframe->list, get_list_head(&mpath->frame_queue));
|
|
mpath->frame_queue_len++;
|
|
|
|
exit_critical_bh(&mpath->frame_queue.lock);
|
|
|
|
ret = RTW_RA_RESOLVING;
|
|
if (xframe_to_free)
|
|
rtw_mesh_path_discard_frame(adapter, xframe_to_free);
|
|
|
|
endlookup:
|
|
rtw_rcu_read_unlock();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* rtw_mesh_nexthop_lookup - put the appropriate next hop on a mesh frame. Calling
|
|
* this function is considered "using" the associated mpath, so preempt a path
|
|
* refresh if this mpath expires soon.
|
|
*
|
|
* @skb: 802.11 frame to be sent
|
|
* @sdata: network subif the frame will be sent through
|
|
*
|
|
* Returns: 0 if the next hop was found. Nonzero otherwise.
|
|
*/
|
|
int rtw_mesh_nexthop_lookup(_adapter *adapter,
|
|
const u8 *mda, const u8 *msa, u8 *ra)
|
|
{
|
|
struct rtw_mesh_path *mpath;
|
|
struct sta_info *next_hop;
|
|
const u8 *target_addr = mda;
|
|
int err = -ENOENT;
|
|
|
|
rtw_rcu_read_lock();
|
|
mpath = rtw_mesh_path_lookup(adapter, target_addr);
|
|
|
|
if (!mpath || !(mpath->flags & RTW_MESH_PATH_ACTIVE))
|
|
goto endlookup;
|
|
|
|
if (rtw_time_after(rtw_get_current_time(),
|
|
mpath->exp_time -
|
|
rtw_ms_to_systime(adapter->mesh_cfg.path_refresh_time)) &&
|
|
_rtw_memcmp(adapter_mac_addr(adapter), msa, ETH_ALEN) == _TRUE &&
|
|
!(mpath->flags & RTW_MESH_PATH_RESOLVING) &&
|
|
!(mpath->flags & RTW_MESH_PATH_FIXED)) {
|
|
rtw_mesh_queue_preq(mpath, RTW_PREQ_Q_F_START | RTW_PREQ_Q_F_REFRESH);
|
|
}
|
|
|
|
next_hop = rtw_rcu_dereference(mpath->next_hop);
|
|
if (next_hop) {
|
|
_rtw_memcpy(ra, next_hop->cmn.mac_addr, ETH_ALEN);
|
|
err = 0;
|
|
}
|
|
|
|
endlookup:
|
|
rtw_rcu_read_unlock();
|
|
return err;
|
|
}
|
|
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
static bool rtw_mesh_data_bmc_to_uc(_adapter *adapter
|
|
, const u8 *da, const u8 *sa, const u8 *mda, const u8 *msa
|
|
, u8 ae_need, const u8 *ori_ta, u8 mfwd_ttl
|
|
, _list *b2u_list, u8 *b2u_num, u32 *b2u_mseq)
|
|
{
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
struct xmit_priv *xmitpriv = &adapter->xmitpriv;
|
|
_irqL irqL;
|
|
_list *head, *list;
|
|
struct sta_info *sta;
|
|
char b2u_sta_id[NUM_STA];
|
|
u8 b2u_sta_num = 0;
|
|
bool bmc_need = _FALSE;
|
|
int i;
|
|
|
|
_enter_critical_bh(&stapriv->asoc_list_lock, &irqL);
|
|
head = &stapriv->asoc_list;
|
|
list = get_next(head);
|
|
|
|
while ((rtw_end_of_queue_search(head, list)) == _FALSE) {
|
|
int stainfo_offset;
|
|
|
|
sta = LIST_CONTAINOR(list, struct sta_info, asoc_list);
|
|
list = get_next(list);
|
|
|
|
stainfo_offset = rtw_stainfo_offset(stapriv, sta);
|
|
if (stainfo_offset_valid(stainfo_offset))
|
|
b2u_sta_id[b2u_sta_num++] = stainfo_offset;
|
|
}
|
|
_exit_critical_bh(&stapriv->asoc_list_lock, &irqL);
|
|
|
|
if (!b2u_sta_num)
|
|
goto exit;
|
|
|
|
for (i = 0; i < b2u_sta_num; i++) {
|
|
struct xmit_frame *b2uframe;
|
|
struct pkt_attrib *attrib;
|
|
|
|
sta = rtw_get_stainfo_by_offset(stapriv, b2u_sta_id[i]);
|
|
if (!(sta->state & _FW_LINKED)
|
|
|| _rtw_memcmp(sta->cmn.mac_addr, msa, ETH_ALEN) == _TRUE
|
|
|| (ori_ta && _rtw_memcmp(sta->cmn.mac_addr, ori_ta, ETH_ALEN) == _TRUE)
|
|
|| is_broadcast_mac_addr(sta->cmn.mac_addr)
|
|
|| is_zero_mac_addr(sta->cmn.mac_addr))
|
|
continue;
|
|
|
|
b2uframe = rtw_alloc_xmitframe(xmitpriv);
|
|
if (!b2uframe) {
|
|
bmc_need = _TRUE;
|
|
break;
|
|
}
|
|
|
|
if ((*b2u_num)++ == 0 && !ori_ta) {
|
|
*b2u_mseq = (cpu_to_le32(adapter->mesh_info.mesh_seqnum));
|
|
adapter->mesh_info.mesh_seqnum++;
|
|
}
|
|
|
|
attrib = &b2uframe->attrib;
|
|
|
|
attrib->mb2u = 1;
|
|
attrib->mseq = *b2u_mseq;
|
|
attrib->mfwd_ttl = ori_ta ? mfwd_ttl : 0;
|
|
_rtw_memcpy(attrib->ra, sta->cmn.mac_addr, ETH_ALEN);
|
|
_rtw_memcpy(attrib->ta, adapter_mac_addr(adapter), ETH_ALEN);
|
|
_rtw_memcpy(attrib->mda, mda, ETH_ALEN);
|
|
_rtw_memcpy(attrib->msa, msa, ETH_ALEN);
|
|
_rtw_memcpy(attrib->dst, da, ETH_ALEN);
|
|
_rtw_memcpy(attrib->src, sa, ETH_ALEN);
|
|
attrib->mesh_frame_mode = ae_need ? MESH_UCAST_PX_DATA : MESH_UCAST_DATA;
|
|
|
|
rtw_list_insert_tail(&b2uframe->list, b2u_list);
|
|
}
|
|
|
|
exit:
|
|
return bmc_need;
|
|
}
|
|
|
|
void dump_mesh_b2u_flags(void *sel, _adapter *adapter)
|
|
{
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
|
|
RTW_PRINT_SEL(sel, "%4s %4s\n", "msrc", "mfwd");
|
|
RTW_PRINT_SEL(sel, "0x%02x 0x%02x\n", mcfg->b2u_flags_msrc, mcfg->b2u_flags_mfwd);
|
|
}
|
|
#endif /* CONFIG_RTW_MESH_DATA_BMC_TO_UC */
|
|
|
|
int rtw_mesh_addr_resolve(_adapter *adapter, struct xmit_frame *xframe, _pkt *pkt, _list *b2u_list)
|
|
{
|
|
struct pkt_file pktfile;
|
|
struct ethhdr etherhdr;
|
|
struct pkt_attrib *attrib;
|
|
struct rtw_mesh_path *mpath = NULL, *mppath = NULL;
|
|
u8 is_da_mcast;
|
|
u8 ae_need;
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
bool bmc_need = _TRUE;
|
|
u8 b2u_num = 0;
|
|
u32 b2u_mseq = 0;
|
|
#endif
|
|
int res = _SUCCESS;
|
|
|
|
_rtw_open_pktfile(pkt, &pktfile);
|
|
if (_rtw_pktfile_read(&pktfile, (u8 *)ðerhdr, ETH_HLEN) != ETH_HLEN) {
|
|
res = _FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
xframe->pkt = pkt;
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
_rtw_init_listhead(b2u_list);
|
|
#endif
|
|
|
|
is_da_mcast = IS_MCAST(etherhdr.h_dest);
|
|
if (!is_da_mcast) {
|
|
struct sta_info *next_hop;
|
|
bool mpp_lookup = 1;
|
|
|
|
mpath = rtw_mesh_path_lookup(adapter, etherhdr.h_dest);
|
|
if (mpath) {
|
|
mpp_lookup = 0;
|
|
next_hop = rtw_rcu_dereference(mpath->next_hop);
|
|
if (!next_hop
|
|
|| !(mpath->flags & (RTW_MESH_PATH_ACTIVE | RTW_MESH_PATH_RESOLVING))
|
|
) {
|
|
/* mpath is not valid, search mppath */
|
|
mpp_lookup = 1;
|
|
}
|
|
}
|
|
|
|
if (mpp_lookup) {
|
|
mppath = rtw_mpp_path_lookup(adapter, etherhdr.h_dest);
|
|
if (mppath)
|
|
mppath->exp_time = rtw_get_current_time();
|
|
}
|
|
|
|
if (mppath && mpath)
|
|
rtw_mesh_path_del(adapter, mpath->dst);
|
|
|
|
ae_need = _rtw_memcmp(adapter_mac_addr(adapter), etherhdr.h_source, ETH_ALEN) == _FALSE
|
|
|| (mppath && _rtw_memcmp(mppath->mpp, etherhdr.h_dest, ETH_ALEN) == _FALSE);
|
|
} else {
|
|
ae_need = _rtw_memcmp(adapter_mac_addr(adapter), etherhdr.h_source, ETH_ALEN) == _FALSE;
|
|
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
if (rtw_msrc_b2u_policy_chk(adapter->mesh_cfg.b2u_flags_msrc, etherhdr.h_dest)) {
|
|
bmc_need = rtw_mesh_data_bmc_to_uc(adapter
|
|
, etherhdr.h_dest, etherhdr.h_source
|
|
, etherhdr.h_dest, adapter_mac_addr(adapter), ae_need, NULL, 0
|
|
, b2u_list, &b2u_num, &b2u_mseq);
|
|
if (bmc_need == _FALSE) {
|
|
res = RTW_BMC_NO_NEED;
|
|
goto exit;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
attrib = &xframe->attrib;
|
|
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
if (b2u_num) {
|
|
attrib->mb2u = 1;
|
|
attrib->mseq = b2u_mseq;
|
|
} else
|
|
attrib->mb2u = 0;
|
|
#endif
|
|
|
|
attrib->mfwd_ttl = 0;
|
|
_rtw_memcpy(attrib->dst, etherhdr.h_dest, ETH_ALEN);
|
|
_rtw_memcpy(attrib->src, etherhdr.h_source, ETH_ALEN);
|
|
_rtw_memcpy(attrib->ta, adapter_mac_addr(adapter), ETH_ALEN);
|
|
|
|
if (is_da_mcast) {
|
|
attrib->mesh_frame_mode = ae_need ? MESH_BMCAST_PX_DATA : MESH_BMCAST_DATA;
|
|
_rtw_memcpy(attrib->ra, attrib->dst, ETH_ALEN);
|
|
_rtw_memcpy(attrib->msa, adapter_mac_addr(adapter), ETH_ALEN);
|
|
} else {
|
|
attrib->mesh_frame_mode = ae_need ? MESH_UCAST_PX_DATA : MESH_UCAST_DATA;
|
|
_rtw_memcpy(attrib->mda, (mppath && ae_need) ? mppath->mpp : attrib->dst, ETH_ALEN);
|
|
_rtw_memcpy(attrib->msa, adapter_mac_addr(adapter), ETH_ALEN);
|
|
/* RA needs to be resolved */
|
|
res = rtw_mesh_nexthop_resolve(adapter, xframe);
|
|
}
|
|
|
|
exit:
|
|
return res;
|
|
}
|
|
|
|
s8 rtw_mesh_tx_set_whdr_mctrl_len(u8 mesh_frame_mode, struct pkt_attrib *attrib)
|
|
{
|
|
u8 ret = 0;
|
|
switch (mesh_frame_mode) {
|
|
case MESH_UCAST_DATA:
|
|
attrib->hdrlen = WLAN_HDR_A4_QOS_LEN;
|
|
/* mesh flag + mesh TTL + Mesh SN. no ext addr. */
|
|
attrib->meshctrl_len = 6;
|
|
break;
|
|
case MESH_BMCAST_DATA:
|
|
attrib->hdrlen = WLAN_HDR_A3_QOS_LEN;
|
|
/* mesh flag + mesh TTL + Mesh SN. no ext addr. */
|
|
attrib->meshctrl_len = 6;
|
|
break;
|
|
case MESH_UCAST_PX_DATA:
|
|
attrib->hdrlen = WLAN_HDR_A4_QOS_LEN;
|
|
/* mesh flag + mesh TTL + Mesh SN + extaddr1 + extaddr2. */
|
|
attrib->meshctrl_len = 18;
|
|
break;
|
|
case MESH_BMCAST_PX_DATA:
|
|
attrib->hdrlen = WLAN_HDR_A3_QOS_LEN;
|
|
/* mesh flag + mesh TTL + Mesh SN + extaddr1 */
|
|
attrib->meshctrl_len = 12;
|
|
break;
|
|
default:
|
|
RTW_WARN("Invalid mesh frame mode:%u\n", mesh_frame_mode);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void rtw_mesh_tx_build_mctrl(_adapter *adapter, struct pkt_attrib *attrib, u8 *buf)
|
|
{
|
|
struct rtw_ieee80211s_hdr *mctrl = (struct rtw_ieee80211s_hdr *)buf;
|
|
|
|
memset(mctrl, 0, XATTRIB_GET_MCTRL_LEN(attrib));
|
|
|
|
if (attrib->mfwd_ttl
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
|| attrib->mb2u
|
|
#endif
|
|
) {
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
if (!attrib->mfwd_ttl)
|
|
mctrl->ttl = adapter->mesh_cfg.dot11MeshTTL;
|
|
else
|
|
#endif
|
|
mctrl->ttl = attrib->mfwd_ttl;
|
|
|
|
mctrl->seqnum = (cpu_to_le32(attrib->mseq));
|
|
} else {
|
|
mctrl->ttl = adapter->mesh_cfg.dot11MeshTTL;
|
|
mctrl->seqnum = (cpu_to_le32(adapter->mesh_info.mesh_seqnum));
|
|
adapter->mesh_info.mesh_seqnum++;
|
|
}
|
|
|
|
switch (attrib->mesh_frame_mode){
|
|
case MESH_UCAST_DATA:
|
|
case MESH_BMCAST_DATA:
|
|
break;
|
|
case MESH_UCAST_PX_DATA:
|
|
mctrl->flags |= MESH_FLAGS_AE_A5_A6;
|
|
_rtw_memcpy(mctrl->eaddr1, attrib->dst, ETH_ALEN);
|
|
_rtw_memcpy(mctrl->eaddr2, attrib->src, ETH_ALEN);
|
|
break;
|
|
case MESH_BMCAST_PX_DATA:
|
|
mctrl->flags |= MESH_FLAGS_AE_A4;
|
|
_rtw_memcpy(mctrl->eaddr1, attrib->src, ETH_ALEN);
|
|
break;
|
|
case MESH_MHOP_UCAST_ACT:
|
|
/* TBD */
|
|
break;
|
|
case MESH_MHOP_BMCAST_ACT:
|
|
/* TBD */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
u8 rtw_mesh_tx_build_whdr(_adapter *adapter, struct pkt_attrib *attrib
|
|
, u16 *fctrl, struct rtw_ieee80211_hdr *whdr)
|
|
{
|
|
switch (attrib->mesh_frame_mode) {
|
|
case MESH_UCAST_DATA: /* 1, 1, RA, TA, mDA(=DA), mSA(=SA) */
|
|
case MESH_UCAST_PX_DATA: /* 1, 1, RA, TA, mDA, mSA, [DA, SA] */
|
|
SetToDs(fctrl);
|
|
SetFrDs(fctrl);
|
|
_rtw_memcpy(whdr->addr1, attrib->ra, ETH_ALEN);
|
|
_rtw_memcpy(whdr->addr2, attrib->ta, ETH_ALEN);
|
|
_rtw_memcpy(whdr->addr3, attrib->mda, ETH_ALEN);
|
|
_rtw_memcpy(whdr->addr4, attrib->msa, ETH_ALEN);
|
|
break;
|
|
case MESH_BMCAST_DATA: /* 0, 1, RA(DA), TA, mSA(SA) */
|
|
case MESH_BMCAST_PX_DATA: /* 0, 1, RA(DA), TA, mSA, [SA] */
|
|
SetFrDs(fctrl);
|
|
_rtw_memcpy(whdr->addr1, attrib->ra, ETH_ALEN);
|
|
_rtw_memcpy(whdr->addr2, attrib->ta, ETH_ALEN);
|
|
_rtw_memcpy(whdr->addr3, attrib->msa, ETH_ALEN);
|
|
break;
|
|
case MESH_MHOP_UCAST_ACT:
|
|
/* TBD */
|
|
RTW_INFO("MESH_MHOP_UCAST_ACT\n");
|
|
break;
|
|
case MESH_MHOP_BMCAST_ACT:
|
|
/* TBD */
|
|
RTW_INFO("MESH_MHOP_BMCAST_ACT\n");
|
|
break;
|
|
default:
|
|
RTW_WARN("Invalid mesh frame mode\n");
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rtw_mesh_rx_data_validate_hdr(_adapter *adapter, union recv_frame *rframe, struct sta_info **sta)
|
|
{
|
|
struct sta_priv *stapriv = &adapter->stapriv;
|
|
struct rx_pkt_attrib *rattrib = &rframe->u.hdr.attrib;
|
|
u8 *whdr = get_recvframe_data(rframe);
|
|
u8 is_ra_bmc = 0;
|
|
u8 a4_shift = 0;
|
|
u8 ps;
|
|
u8 *qc;
|
|
u8 mps_mode = RTW_MESH_PS_UNKNOWN;
|
|
sint ret = _FAIL;
|
|
|
|
if (!(MLME_STATE(adapter) & WIFI_ASOC_STATE))
|
|
goto exit;
|
|
|
|
if (!rattrib->qos)
|
|
goto exit;
|
|
|
|
switch (rattrib->to_fr_ds) {
|
|
case 1:
|
|
if (!IS_MCAST(GetAddr1Ptr(whdr)))
|
|
goto exit;
|
|
*sta = rtw_get_stainfo(stapriv, get_addr2_ptr(whdr));
|
|
if (*sta == NULL) {
|
|
ret = _SUCCESS; /* return _SUCCESS to drop at sta checking */
|
|
goto exit;
|
|
}
|
|
_rtw_memcpy(rattrib->ra, GetAddr1Ptr(whdr), ETH_ALEN);
|
|
_rtw_memcpy(rattrib->ta, get_addr2_ptr(whdr), ETH_ALEN);
|
|
_rtw_memcpy(rattrib->mda, GetAddr1Ptr(whdr), ETH_ALEN);
|
|
_rtw_memcpy(rattrib->msa, GetAddr3Ptr(whdr), ETH_ALEN); /* may change after checking AMSDU subframe header */
|
|
_rtw_memcpy(rattrib->dst, GetAddr1Ptr(whdr), ETH_ALEN);
|
|
_rtw_memcpy(rattrib->src, GetAddr3Ptr(whdr), ETH_ALEN); /* may change after checking mesh ctrl field */
|
|
_rtw_memcpy(rattrib->bssid, get_addr2_ptr(whdr), ETH_ALEN);
|
|
is_ra_bmc = 1;
|
|
break;
|
|
case 3:
|
|
if (IS_MCAST(GetAddr1Ptr(whdr)))
|
|
goto exit;
|
|
*sta = rtw_get_stainfo(stapriv, get_addr2_ptr(whdr));
|
|
if (*sta == NULL) {
|
|
ret = _SUCCESS; /* return _SUCCESS to drop at sta checking */
|
|
goto exit;
|
|
}
|
|
_rtw_memcpy(rattrib->ra, GetAddr1Ptr(whdr), ETH_ALEN);
|
|
_rtw_memcpy(rattrib->ta, get_addr2_ptr(whdr), ETH_ALEN);
|
|
_rtw_memcpy(rattrib->mda, GetAddr3Ptr(whdr), ETH_ALEN); /* may change after checking AMSDU subframe header */
|
|
_rtw_memcpy(rattrib->msa, GetAddr4Ptr(whdr), ETH_ALEN); /* may change after checking AMSDU subframe header */
|
|
_rtw_memcpy(rattrib->dst, GetAddr3Ptr(whdr), ETH_ALEN); /* may change after checking mesh ctrl field */
|
|
_rtw_memcpy(rattrib->src, GetAddr4Ptr(whdr), ETH_ALEN); /* may change after checking mesh ctrl field */
|
|
_rtw_memcpy(rattrib->bssid, get_addr2_ptr(whdr), ETH_ALEN);
|
|
a4_shift = ETH_ALEN;
|
|
break;
|
|
default:
|
|
goto exit;
|
|
}
|
|
|
|
qc = whdr + WLAN_HDR_A3_LEN + a4_shift;
|
|
ps = GetPwrMgt(whdr);
|
|
mps_mode = ps ? (is_ra_bmc || (get_mps_lv(qc)) ? RTW_MESH_PS_DSLEEP : RTW_MESH_PS_LSLEEP) : RTW_MESH_PS_ACTIVE;
|
|
|
|
if (ps) {
|
|
if (!((*sta)->state & WIFI_SLEEP_STATE))
|
|
stop_sta_xmit(adapter, *sta);
|
|
} else {
|
|
if ((*sta)->state & WIFI_SLEEP_STATE)
|
|
wakeup_sta_to_xmit(adapter, *sta);
|
|
}
|
|
|
|
if (is_ra_bmc)
|
|
(*sta)->nonpeer_mps = mps_mode;
|
|
else {
|
|
(*sta)->peer_mps = mps_mode;
|
|
if (mps_mode != RTW_MESH_PS_ACTIVE && (*sta)->nonpeer_mps == RTW_MESH_PS_ACTIVE)
|
|
(*sta)->nonpeer_mps = RTW_MESH_PS_DSLEEP;
|
|
}
|
|
|
|
if (get_frame_sub_type(whdr) & BIT(6)) {
|
|
/* No data, will not indicate to upper layer, temporily count it here */
|
|
count_rx_stats(adapter, rframe, *sta);
|
|
ret = RTW_RX_HANDLED;
|
|
goto exit;
|
|
}
|
|
|
|
rattrib->mesh_ctrl_present = get_mctrl_present(qc) ? 1 : 0;
|
|
if (!rattrib->mesh_ctrl_present)
|
|
goto exit;
|
|
|
|
ret = _SUCCESS;
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
int rtw_mesh_rx_data_validate_mctrl(_adapter *adapter, union recv_frame *rframe
|
|
, const struct rtw_ieee80211s_hdr *mctrl, const u8 *mda, const u8 *msa
|
|
, u8 *mctrl_len
|
|
, const u8 **da, const u8 **sa)
|
|
{
|
|
struct rx_pkt_attrib *rattrib = &rframe->u.hdr.attrib;
|
|
u8 mlen;
|
|
u8 ae;
|
|
int ret = _SUCCESS;
|
|
|
|
ae = mctrl->flags & MESH_FLAGS_AE;
|
|
mlen = ae_to_mesh_ctrl_len[ae];
|
|
switch (rattrib->to_fr_ds) {
|
|
case 1:
|
|
*da = mda;
|
|
if (ae == MESH_FLAGS_AE_A4)
|
|
*sa = mctrl->eaddr1;
|
|
else if (ae == 0)
|
|
*sa = msa;
|
|
else
|
|
ret = _FAIL;
|
|
break;
|
|
case 3:
|
|
if (ae == MESH_FLAGS_AE_A5_A6) {
|
|
*da = mctrl->eaddr1;
|
|
*sa = mctrl->eaddr2;
|
|
} else if (ae == 0) {
|
|
*da = mda;
|
|
*sa = msa;
|
|
} else
|
|
ret = _FAIL;
|
|
break;
|
|
default:
|
|
ret = _FAIL;
|
|
}
|
|
|
|
if (ret == _FAIL) {
|
|
#ifdef DBG_RX_DROP_FRAME
|
|
RTW_INFO("DBG_RX_DROP_FRAME "FUNC_ADPT_FMT" invalid tfDS:%u AE:%u combination ra="MAC_FMT" ta="MAC_FMT"\n"
|
|
, FUNC_ADPT_ARG(adapter), rattrib->to_fr_ds, ae, MAC_ARG(rattrib->ra), MAC_ARG(rattrib->ta));
|
|
#endif
|
|
*mctrl_len = 0;
|
|
} else
|
|
*mctrl_len = mlen;
|
|
|
|
return ret;
|
|
}
|
|
|
|
inline int rtw_mesh_rx_validate_mctrl_non_amsdu(_adapter *adapter, union recv_frame *rframe)
|
|
{
|
|
struct rx_pkt_attrib *rattrib = &rframe->u.hdr.attrib;
|
|
const u8 *da, *sa;
|
|
int ret;
|
|
|
|
ret = rtw_mesh_rx_data_validate_mctrl(adapter, rframe
|
|
, (struct rtw_ieee80211s_hdr *)(get_recvframe_data(rframe) + rattrib->hdrlen + rattrib->iv_len)
|
|
, rattrib->mda, rattrib->msa
|
|
, &rattrib->mesh_ctrl_len
|
|
, &da, &sa);
|
|
|
|
if (ret == _SUCCESS) {
|
|
_rtw_memcpy(rattrib->dst, da, ETH_ALEN);
|
|
_rtw_memcpy(rattrib->src, sa, ETH_ALEN);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* rtw_mesh_rx_nexthop_resolve - lookup next hop; conditionally start path discovery
|
|
*
|
|
* @skb: 802.11 frame to be sent
|
|
* @sdata: network subif the frame will be sent through
|
|
*
|
|
* Lookup next hop for given skb and start path discovery if no
|
|
* forwarding information is found.
|
|
*
|
|
* Returns: 0 if the next hop was found and -ENOENT if the frame was queued.
|
|
* skb is freeed here if no mpath could be allocated.
|
|
*/
|
|
static int rtw_mesh_rx_nexthop_resolve(_adapter *adapter,
|
|
const u8 *mda, const u8 *msa, u8 *ra)
|
|
{
|
|
struct rtw_mesh_path *mpath;
|
|
struct xmit_frame *xframe_to_free = NULL;
|
|
int err = 0;
|
|
int ret = _SUCCESS;
|
|
|
|
rtw_rcu_read_lock();
|
|
err = rtw_mesh_nexthop_lookup(adapter, mda, msa, ra);
|
|
if (!err)
|
|
goto endlookup;
|
|
|
|
/* no nexthop found, start resolving */
|
|
mpath = rtw_mesh_path_lookup(adapter, mda);
|
|
if (!mpath) {
|
|
mpath = rtw_mesh_path_add(adapter, mda);
|
|
if (IS_ERR(mpath)) {
|
|
err = PTR_ERR(mpath);
|
|
ret = _FAIL;
|
|
goto endlookup;
|
|
}
|
|
}
|
|
|
|
if (!(mpath->flags & RTW_MESH_PATH_RESOLVING))
|
|
rtw_mesh_queue_preq(mpath, RTW_PREQ_Q_F_START);
|
|
|
|
ret = _FAIL;
|
|
|
|
endlookup:
|
|
rtw_rcu_read_unlock();
|
|
return ret;
|
|
}
|
|
|
|
#define RTW_MESH_DECACHE_BMC 1
|
|
#define RTW_MESH_DECACHE_UC 0
|
|
|
|
#define RTW_MESH_FORWARD_MDA_SELF_COND 0
|
|
#define DBG_RTW_MESH_FORWARD_MDA_SELF_COND 0
|
|
int rtw_mesh_rx_msdu_act_check(union recv_frame *rframe
|
|
, const u8 *mda, const u8 *msa
|
|
, const u8 *da, const u8 *sa
|
|
, struct rtw_ieee80211s_hdr *mctrl
|
|
, struct xmit_frame **fwd_frame, _list *b2u_list)
|
|
{
|
|
_adapter *adapter = rframe->u.hdr.adapter;
|
|
struct rtw_mesh_cfg *mcfg = &adapter->mesh_cfg;
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct rx_pkt_attrib *rattrib = &rframe->u.hdr.attrib;
|
|
struct rtw_mesh_path *mppath;
|
|
u8 is_mda_bmc = IS_MCAST(mda);
|
|
u8 is_mda_self = !is_mda_bmc && _rtw_memcmp(mda, adapter_mac_addr(adapter), ETH_ALEN);
|
|
struct xmit_frame *xframe;
|
|
struct pkt_attrib *xattrib;
|
|
u8 fwd_ra[ETH_ALEN] = {0};
|
|
u8 fwd_mpp[ETH_ALEN] = {0}; /* forward to other gate */
|
|
u32 fwd_mseq;
|
|
int act = 0;
|
|
u8 ae_need;
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
bool bmc_need = _TRUE;
|
|
u8 b2u_num = 0;
|
|
#endif
|
|
|
|
/* fwd info lifetime update */
|
|
#if 0
|
|
if (!is_mda_self)
|
|
mDA(A3) fwinfo.lifetime
|
|
mSA(A4) fwinfo.lifetime
|
|
Precursor-to-mDA(A2) fwinfo.lifetime
|
|
#endif
|
|
|
|
/* update/create pxoxy info for SA, mSA */
|
|
if ((mctrl->flags & MESH_FLAGS_AE)
|
|
&& sa != msa && _rtw_memcmp(sa, msa, ETH_ALEN) == _FALSE
|
|
) {
|
|
const u8 *proxied_addr = sa;
|
|
const u8 *mpp_addr = msa;
|
|
|
|
rtw_rcu_read_lock();
|
|
mppath = rtw_mpp_path_lookup(adapter, proxied_addr);
|
|
if (!mppath)
|
|
rtw_mpp_path_add(adapter, proxied_addr, mpp_addr);
|
|
else {
|
|
enter_critical_bh(&mppath->state_lock);
|
|
if (_rtw_memcmp(mppath->mpp, mpp_addr, ETH_ALEN) == _FALSE)
|
|
_rtw_memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
|
|
mppath->exp_time = rtw_get_current_time();
|
|
exit_critical_bh(&mppath->state_lock);
|
|
}
|
|
rtw_rcu_read_unlock();
|
|
}
|
|
|
|
/* mSA is self, need no further process */
|
|
if (_rtw_memcmp(msa, adapter_mac_addr(adapter), ETH_ALEN) == _TRUE)
|
|
goto exit;
|
|
|
|
fwd_mseq = le32_to_cpu(mctrl->seqnum);
|
|
|
|
/* check duplicate MSDU from mSA */
|
|
if (((RTW_MESH_DECACHE_BMC && is_mda_bmc)
|
|
|| (RTW_MESH_DECACHE_UC && !is_mda_bmc))
|
|
&& rtw_mesh_decache(adapter, msa, fwd_mseq)
|
|
) {
|
|
minfo->mshstats.dropped_frames_duplicate++;
|
|
goto exit;
|
|
}
|
|
|
|
if (is_mda_bmc) {
|
|
/* mDA is bmc addr */
|
|
act |= RTW_RX_MSDU_ACT_INDICATE;
|
|
if (!mcfg->dot11MeshForwarding)
|
|
goto exit;
|
|
goto fwd_chk;
|
|
|
|
} else if (!is_mda_self) {
|
|
/* mDA is unicast but not self */
|
|
if (!mcfg->dot11MeshForwarding) {
|
|
rtw_mesh_path_error_tx(adapter
|
|
, adapter->mesh_cfg.element_ttl
|
|
, mda, 0
|
|
, WLAN_REASON_MESH_PATH_NOFORWARD
|
|
, rattrib->ta
|
|
);
|
|
#ifdef DBG_RX_DROP_FRAME
|
|
RTW_INFO("DBG_RX_DROP_FRAME "FUNC_ADPT_FMT" mDA("MAC_FMT") not self, !dot11MeshForwarding\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(mda));
|
|
#endif
|
|
goto exit;
|
|
}
|
|
|
|
if (rtw_mesh_rx_nexthop_resolve(adapter, mda, msa, fwd_ra) != _SUCCESS) {
|
|
/* mDA is unknown */
|
|
rtw_mesh_path_error_tx(adapter
|
|
, adapter->mesh_cfg.element_ttl
|
|
, mda, 0
|
|
, WLAN_REASON_MESH_PATH_NOFORWARD
|
|
, rattrib->ta
|
|
);
|
|
#ifdef DBG_RX_DROP_FRAME
|
|
RTW_INFO("DBG_RX_DROP_FRAME "FUNC_ADPT_FMT" mDA("MAC_FMT") unknown\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(mda));
|
|
#endif
|
|
minfo->mshstats.dropped_frames_no_route++;
|
|
goto exit;
|
|
|
|
} else {
|
|
/* mDA is known in fwd info */
|
|
#if 0
|
|
if (TA is not in precursors)
|
|
goto exit;
|
|
#endif
|
|
goto fwd_chk;
|
|
}
|
|
|
|
} else {
|
|
/* mDA is self */
|
|
#if RTW_MESH_FORWARD_MDA_SELF_COND
|
|
if (da == mda
|
|
|| _rtw_memcmp(da, adapter_mac_addr(adapter), ETH_ALEN)
|
|
) {
|
|
/* DA is self, indicate */
|
|
act |= RTW_RX_MSDU_ACT_INDICATE;
|
|
goto exit;
|
|
}
|
|
|
|
if (rtw_get_iface_by_macddr(adapter, da)) {
|
|
/* DA is buddy, indicate */
|
|
act |= RTW_RX_MSDU_ACT_INDICATE;
|
|
#if DBG_RTW_MESH_FORWARD_MDA_SELF_COND
|
|
RTW_INFO(FUNC_ADPT_FMT" DA("MAC_FMT") is buddy("ADPT_FMT")\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(da), ADPT_ARG(rtw_get_iface_by_macddr(adapter, da)));
|
|
#endif
|
|
goto exit;
|
|
}
|
|
|
|
/* DA is not self or buddy */
|
|
if (rtw_mesh_nexthop_lookup(adapter, da, msa, fwd_ra) == 0) {
|
|
/* DA is known in fwd info */
|
|
if (!mcfg->dot11MeshForwarding) {
|
|
/* path error to? */
|
|
#if defined(DBG_RX_DROP_FRAME) || DBG_RTW_MESH_FORWARD_MDA_SELF_COND
|
|
RTW_INFO("DBG_RX_DROP_FRAME "FUNC_ADPT_FMT" DA("MAC_FMT") not self, !dot11MeshForwarding\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(da));
|
|
#endif
|
|
goto exit;
|
|
}
|
|
mda = da;
|
|
#if DBG_RTW_MESH_FORWARD_MDA_SELF_COND
|
|
RTW_INFO(FUNC_ADPT_FMT" fwd to DA("MAC_FMT"), fwd_RA("MAC_FMT")\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(da), MAC_ARG(fwd_ra));
|
|
#endif
|
|
goto fwd_chk;
|
|
}
|
|
|
|
rtw_rcu_read_lock();
|
|
mppath = rtw_mpp_path_lookup(adapter, da);
|
|
if (mppath) {
|
|
if (_rtw_memcmp(mppath->mpp, adapter_mac_addr(adapter), ETH_ALEN) == _FALSE) {
|
|
/* DA is proxied by others */
|
|
if (!mcfg->dot11MeshForwarding) {
|
|
/* path error to? */
|
|
#if defined(DBG_RX_DROP_FRAME) || DBG_RTW_MESH_FORWARD_MDA_SELF_COND
|
|
RTW_INFO("DBG_RX_DROP_FRAME "FUNC_ADPT_FMT" DA("MAC_FMT") is proxied by ("MAC_FMT"), !dot11MeshForwarding\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(da), MAC_ARG(mppath->mpp));
|
|
#endif
|
|
rtw_rcu_read_unlock();
|
|
goto exit;
|
|
}
|
|
_rtw_memcpy(fwd_mpp, mppath->mpp, ETH_ALEN);
|
|
mda = fwd_mpp;
|
|
msa = adapter_mac_addr(adapter);
|
|
rtw_rcu_read_unlock();
|
|
|
|
/* resolve RA */
|
|
if (rtw_mesh_nexthop_lookup(adapter, mda, msa, fwd_ra) != 0) {
|
|
minfo->mshstats.dropped_frames_no_route++;
|
|
#if defined(DBG_RX_DROP_FRAME) || DBG_RTW_MESH_FORWARD_MDA_SELF_COND
|
|
RTW_INFO("DBG_RX_DROP_FRAME "FUNC_ADPT_FMT" DA("MAC_FMT") is proxied by ("MAC_FMT"), RA resolve fail\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(da), MAC_ARG(mppath->mpp));
|
|
#endif
|
|
goto exit;
|
|
}
|
|
#if DBG_RTW_MESH_FORWARD_MDA_SELF_COND
|
|
RTW_INFO(FUNC_ADPT_FMT" DA("MAC_FMT") is proxied by ("MAC_FMT"), fwd_RA("MAC_FMT")\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(da), MAC_ARG(mppath->mpp), MAC_ARG(fwd_ra));
|
|
#endif
|
|
goto fwd_chk; /* forward to other gate */
|
|
} else {
|
|
#if DBG_RTW_MESH_FORWARD_MDA_SELF_COND
|
|
RTW_INFO(FUNC_ADPT_FMT" DA("MAC_FMT") is proxied by self\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(da));
|
|
#endif
|
|
}
|
|
}
|
|
rtw_rcu_read_unlock();
|
|
|
|
if (!mppath) {
|
|
#if DBG_RTW_MESH_FORWARD_MDA_SELF_COND
|
|
RTW_INFO(FUNC_ADPT_FMT" DA("MAC_FMT") unknown\n"
|
|
, FUNC_ADPT_ARG(adapter), MAC_ARG(da));
|
|
#endif
|
|
/* DA is unknown */
|
|
#if 0 /* TODO: flags with AE bit */
|
|
rtw_mesh_path_error_tx(adapter
|
|
, adapter->mesh_cfg.element_ttl
|
|
, mda, adapter->mesh_info.last_sn_update
|
|
, WLAN_REASON_MESH_PATH_NOPROXY
|
|
, msa
|
|
);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* indicate to DS for both cases:
|
|
* 1.) DA is proxied by self
|
|
* 2.) DA is unknown
|
|
*/
|
|
#endif /* RTW_MESH_FORWARD_MDA_SELF_COND */
|
|
act |= RTW_RX_MSDU_ACT_INDICATE;
|
|
goto exit;
|
|
}
|
|
|
|
fwd_chk:
|
|
|
|
if (adapter->stapriv.asoc_list_cnt <= 1)
|
|
goto exit;
|
|
|
|
if (mctrl->ttl == 1) {
|
|
minfo->mshstats.dropped_frames_ttl++;
|
|
if (!act) {
|
|
#ifdef DBG_RX_DROP_FRAME
|
|
RTW_INFO("DBG_RX_DROP_FRAME "FUNC_ADPT_FMT" ttl reaches 0, not forwarding\n"
|
|
, FUNC_ADPT_ARG(adapter));
|
|
#endif
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
_rtw_init_listhead(b2u_list);
|
|
#endif
|
|
|
|
ae_need = _rtw_memcmp(da , mda, ETH_ALEN) == _FALSE
|
|
|| _rtw_memcmp(sa , msa, ETH_ALEN) == _FALSE;
|
|
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
if (is_mda_bmc
|
|
&& rtw_mfwd_b2u_policy_chk(mcfg->b2u_flags_mfwd, mda, rattrib->to_fr_ds == 3)
|
|
) {
|
|
bmc_need = rtw_mesh_data_bmc_to_uc(adapter
|
|
, da, sa, mda, msa, ae_need, rframe->u.hdr.psta->cmn.mac_addr, mctrl->ttl - 1
|
|
, b2u_list, &b2u_num, &fwd_mseq);
|
|
}
|
|
|
|
if (bmc_need == _TRUE)
|
|
#endif
|
|
{
|
|
xframe = rtw_alloc_xmitframe(&adapter->xmitpriv);
|
|
if (!xframe) {
|
|
#ifdef DBG_TX_DROP_FRAME
|
|
RTW_INFO("DBG_TX_DROP_FRAME "FUNC_ADPT_FMT" rtw_alloc_xmitframe fail\n"
|
|
, FUNC_ADPT_ARG(adapter));
|
|
#endif
|
|
goto exit;
|
|
}
|
|
|
|
xattrib = &xframe->attrib;
|
|
|
|
#if CONFIG_RTW_MESH_DATA_BMC_TO_UC
|
|
if (b2u_num)
|
|
xattrib->mb2u = 1;
|
|
else
|
|
xattrib->mb2u = 0;
|
|
#endif
|
|
xattrib->mfwd_ttl = mctrl->ttl - 1;
|
|
xattrib->mseq = fwd_mseq;
|
|
_rtw_memcpy(xattrib->dst, da, ETH_ALEN);
|
|
_rtw_memcpy(xattrib->src, sa, ETH_ALEN);
|
|
_rtw_memcpy(xattrib->mda, mda, ETH_ALEN);
|
|
_rtw_memcpy(xattrib->msa, msa, ETH_ALEN);
|
|
_rtw_memcpy(xattrib->ta, adapter_mac_addr(adapter), ETH_ALEN);
|
|
|
|
if (is_mda_bmc) {
|
|
xattrib->mesh_frame_mode = ae_need ? MESH_BMCAST_PX_DATA : MESH_BMCAST_DATA;
|
|
_rtw_memcpy(xattrib->ra, mda, ETH_ALEN);
|
|
} else {
|
|
xattrib->mesh_frame_mode = ae_need ? MESH_UCAST_PX_DATA : MESH_UCAST_DATA;
|
|
_rtw_memcpy(xattrib->ra, fwd_ra, ETH_ALEN);
|
|
}
|
|
|
|
*fwd_frame = xframe;
|
|
}
|
|
|
|
act |= RTW_RX_MSDU_ACT_FORWARD;
|
|
if (is_mda_bmc)
|
|
minfo->mshstats.fwded_mcast++;
|
|
else
|
|
minfo->mshstats.fwded_unicast++;
|
|
minfo->mshstats.fwded_frames++;
|
|
|
|
exit:
|
|
return act;
|
|
}
|
|
|
|
void dump_mesh_stats(void *sel, _adapter *adapter)
|
|
{
|
|
struct rtw_mesh_info *minfo = &adapter->mesh_info;
|
|
struct rtw_mesh_stats *stats = &minfo->mshstats;
|
|
|
|
RTW_PRINT_SEL(sel, "fwd_bmc:%u\n", stats->fwded_mcast);
|
|
RTW_PRINT_SEL(sel, "fwd_uc:%u\n", stats->fwded_unicast);
|
|
|
|
RTW_PRINT_SEL(sel, "drop_ttl:%u\n", stats->dropped_frames_ttl);
|
|
RTW_PRINT_SEL(sel, "drop_no_route:%u\n", stats->dropped_frames_no_route);
|
|
RTW_PRINT_SEL(sel, "drop_congestion:%u\n", stats->dropped_frames_congestion);
|
|
RTW_PRINT_SEL(sel, "drop_dup:%u\n", stats->dropped_frames_duplicate);
|
|
|
|
RTW_PRINT_SEL(sel, "mrc_del_qlen:%u\n", stats->mrc_del_qlen);
|
|
}
|
|
#endif /* CONFIG_RTW_MESH */
|
|
|