3669 lines
99 KiB
C
3669 lines
99 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;
|
|
}
|
|
|
|
#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;
|
|
|
|
if (IS_CH_WAITING(rfctl) && !IS_UNDER_CAC(rfctl))
|
|
goto exit;
|
|
|
|
if (plink_ctl->num >= RTW_MESH_MAX_PEER_CANDIDATES)
|
|
goto exit;
|
|
|
|
/* 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)
|
|
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;
|
|
|
|
#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;
|
|
char 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));
|
|
|
|
for (i = 0; i < flush_num; i++) {
|
|
u8 sta_addr[ETH_ALEN];
|
|
|
|
sta = rtw_get_stainfo_by_offset(stapriv, flush_list[i]);
|
|
_rtw_memcpy(sta_addr, sta->cmn.mac_addr, ETH_ALEN);
|
|
|
|
ap_free_sta(adapter, sta, _TRUE, WLAN_REASON_DEAUTH_LEAVING, _FALSE);
|
|
rtw_mesh_expire_peer(adapter, sta_addr);
|
|
}
|
|
|
|
#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
|
|
|
|
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;
|
|
|
|
ret = MLME_IS_MESH(adapter)
|
|
&& check_fwstate(&adapter->mlmepriv, WIFI_ASOC_STATE) == _TRUE
|
|
&& (!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) || MSTATE_AP_LD_NUM(&mstate_no_self)
|
|
|| MSTATE_ADHOC_LD_NUM(&mstate_no_self) || MSTATE_MESH_LD_NUM(&mstate_no_self))
|
|
ret = 0;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
u8 max_cand_ch = 0;
|
|
u8 max_cand_cnt = 0;
|
|
|
|
for (i = 0; i < rfctl->max_chan_nums; i++)
|
|
rfctl->channel_set[i].mesh_candidate_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, 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
|
|
) {
|
|
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])
|
|
) {
|
|
rfctl->channel_set[ch_set_idx].mesh_candidate_cnt++;
|
|
if (max_cand_cnt < rfctl->channel_set[ch_set_idx].mesh_candidate_cnt) {
|
|
max_cand_cnt = rfctl->channel_set[ch_set_idx].mesh_candidate_cnt;
|
|
max_cand_ch = rfctl->channel_set[ch_set_idx].ChannelNum;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_exit_critical_bh(&(mlme->scanned_queue.lock), &irqL);
|
|
|
|
return max_cand_ch;
|
|
}
|
|
#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;
|
|
|
|
#if CONFIG_RTW_MESH_OFFCH_CAND
|
|
#define OFFCH_CAND_TITLE_FMT " %-17s"
|
|
#define OFFCH_CAND_VALUE_FMT " %17u"
|
|
#define OFFCH_CAND_TITLE_ARG , "offch_find_int_ms"
|
|
#define OFFCH_CAND_VALUE_ARG , peer_sel_policy->offch_find_int_ms
|
|
#else
|
|
#define OFFCH_CAND_TITLE_FMT ""
|
|
#define OFFCH_CAND_VALUE_FMT ""
|
|
#define OFFCH_CAND_TITLE_ARG
|
|
#define OFFCH_CAND_VALUE_ARG
|
|
#endif
|
|
|
|
RTW_PRINT_SEL(sel,
|
|
"%-12s"
|
|
OFFCH_CAND_TITLE_FMT
|
|
"\n"
|
|
, "scanr_exp_ms"
|
|
OFFCH_CAND_TITLE_ARG
|
|
);
|
|
RTW_PRINT_SEL(sel,
|
|
"%12u"
|
|
OFFCH_CAND_VALUE_FMT
|
|
"\n"
|
|
, peer_sel_policy->scanr_exp_ms
|
|
OFFCH_CAND_VALUE_ARG
|
|
);
|
|
}
|
|
|
|
void dump_mesh_networks(void *sel, _adapter *adapter)
|
|
{
|
|
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;
|
|
u8 *mesh_conf_ie;
|
|
sint mesh_conf_ie_len;
|
|
struct wlan_network **mesh_networks;
|
|
u8 mesh_network_cnt = 0;
|
|
int i;
|
|
|
|
mesh_networks = rtw_zvmalloc(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\n"
|
|
, "bssid", "ch", "rssi", "age", "mesh_id", "nop", "fwd", "cto");
|
|
|
|
|
|
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);
|
|
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 \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' : ' '
|
|
);
|
|
}
|
|
|
|
rtw_vmfree(mesh_networks, mlme->max_bss_cnt * sizeof(struct wlan_network *));
|
|
}
|
|
|
|
int rtw_sae_check_frames(_adapter *adapter, const u8 *buf, u32 len, u8 tx)
|
|
{
|
|
const u8 *frame_body = buf + sizeof(struct rtw_ieee80211_hdr_3addr);
|
|
u16 alg;
|
|
u16 seq;
|
|
u16 status;
|
|
int ret = 0;
|
|
|
|
alg = RTW_GET_LE16(frame_body);
|
|
if (alg != 3)
|
|
goto exit;
|
|
|
|
seq = RTW_GET_LE16(frame_body + 2);
|
|
status = RTW_GET_LE16(frame_body + 4);
|
|
|
|
RTW_INFO("RTW_%s:AUTH alg:0x%04x, seq:0x%04x, status:0x%04x\n"
|
|
, (tx == _TRUE) ? "Tx" : "Rx", alg, seq, status);
|
|
|
|
ret = 1;
|
|
|
|
#if CONFIG_RTW_MESH_PEER_BLACKLIST
|
|
if (tx && seq == 1)
|
|
rtw_mesh_plink_set_peer_conf_timeout(adapter, GetAddr1Ptr(buf));
|
|
#endif
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
#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:0x0000 llid:0x0000 chosen_pmk:0x00000000000000000000000000000000
|
|
* aid:0x0000 pid:0x0000 llid:0x0000 plid:0x0000 chosen_pmk:0x00000000000000000000000000000000
|
|
* pid:0x0000 llid:0x0000 plid:0x0000 reason:0x0000 chosen_pmk:0x00000000000000000000000000000000
|
|
*/
|
|
#define MPM_LOG_BUF_LEN 96 /* 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:0x%04x ", 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:0x%04x ", 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:0x%04x ", 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:0x%04x ", 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:0x%04x ", 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;
|
|
}
|
|
|
|
_rtw_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 (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);
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
_rtw_memset(ent->chosen_pmk, 0, 16);
|
|
#ifdef CONFIG_RTW_MESH_AEK
|
|
_rtw_memset(ent->sel_pcs, 0, 4);
|
|
_rtw_memset(ent->l_nonce, 0, 32);
|
|
_rtw_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);
|
|
|
|
_rtw_spinlock_free(&plink_ctl->lock);
|
|
|
|
#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);
|
|
|
|
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);
|
|
|
|
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);
|
|
}
|
|
|
|
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_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 = 0;
|
|
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;
|
|
|
|
_rtw_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;
|
|
|
|
_rtw_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 */
|
|
|