qcacld-3.0: Handle NUD events within driver

Currently NUD events are used by Framework to detect
the Network Reachability. Framework issues disconnection
for NUD_FAILED event.

Now, NUD events are tracked within driver to detect the
reachability and based on TxRx traffic, driver takes decision
to issue disconnection.

Change-Id: I461610c220288ff1fd718bb7bc2dd8375588505c
CRs-Fixed: 2195796
This commit is contained in:
Alok Kumar 2018-03-23 17:05:11 +05:30 • committed by nshrivas
commit b64650c29a
12 changed files with 812 additions and 0 deletions

14
Kbuild
View file

@ -421,6 +421,10 @@ CONFIG_TRACE_RECORD_FEATURE := 1
#Flag to enable p2p debug feature
CONFIG_WLAN_FEATURE_P2P_DEBUG := 1
#Flag to enable nud tracking feature
CONFIG_WLAN_NUD_TRACKING := 1
ifeq ($(CONFIG_CFG80211),y)
HAVE_CFG80211 := 1
else
@ -558,6 +562,10 @@ ifeq ($(CONFIG_LITHIUM), y)
HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_rx_monitor.o
endif
ifeq ($(CONFIG_WLAN_NUD_TRACKING), 1)
HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_nud_tracking.o
endif
########### HOST DIAG LOG ###########
HOST_DIAG_LOG_DIR := $(WLAN_COMMON_ROOT)/utils/host_diag_log
@ -1876,6 +1884,7 @@ CDEFINES += -DFEATURE_OEM_DATA_SUPPORT
endif
####################################
ifeq ($(CONFIG_FEATURE_HTC_CREDIT_HISTORY), 1)
CDEFINES += -DFEATURE_HTC_CREDIT_HISTORY
endif
@ -2552,6 +2561,11 @@ ifeq ($(CONFIG_ARCH_SDM845), y)
CDEFINES += -DENABLE_SMMU_S1_TRANSLATION
endif
#Flag to enable NUD tracking
ifeq ($(CONFIG_WLAN_NUD_TRACKING), 1)
CDEFINES += -DWLAN_NUD_TRACKING
endif
KBUILD_CPPFLAGS += $(CDEFINES)
# Currently, for versions of gcc which support it, the kernel Makefile

View file

@ -96,6 +96,31 @@ struct hdd_context;
#define CFG_ENABLE_CONNECTED_SCAN_MAX (1)
#define CFG_ENABLE_CONNECTED_SCAN_DEFAULT (1)
#ifdef WLAN_NUD_TRACKING
/*
* <ini>
* gEnableNUDTracking - Will enable or disable NUD tracking within driver
* @Min: 0
* @Max: 1
* @Default: 0
*
* This ini is used to enable or disable NUD tracking within driver
*
* Related: None
*
* Supported Feature: STA
*
* Usage: External
*
* <ini>
*/
#define CFG_ENABLE_NUD_TRACKING_NAME "gEnableNUDTracking"
#define CFG_ENABLE_NUD_TRACKING_MIN (0)
#define CFG_ENABLE_NUD_TRACKING_MAX (1)
#define CFG_ENABLE_NUD_TRACKING_DEFAULT (0)
#endif
/*
* <ini>
* RTSThreshold - Will provide RTSThreshold
@ -14375,6 +14400,9 @@ struct hdd_config {
DECLARE_BITMAP(bExplicitCfg, MAX_CFG_INI_ITEMS);
/* Config parameters */
#ifdef WLAN_NUD_TRACKING
bool enable_nud_tracking;
#endif
bool enable_connected_scan;
uint32_t RTSThreshold;
uint32_t FragmentationThreshold;

View file

@ -98,6 +98,10 @@
#endif
#include "wlan_hdd_he.h"
#include <net/neighbour.h>
#include <net/netevent.h>
#include "wlan_hdd_nud_tracking.h"
/*
* Preprocessor definitions and constants
*/
@ -1157,6 +1161,12 @@ struct hdd_adapter {
/** Current MAC Address for the adapter */
struct qdf_mac_addr mac_addr;
#ifdef WLAN_NUD_TRACKING
struct hdd_nud_tracking_info nud_tracking;
#endif
bool disconnection_in_progress;
qdf_mutex_t disconnection_status_lock;
/**Event Flags*/
unsigned long event_flags;
@ -3109,4 +3119,13 @@ void hdd_driver_memdump_deinit(void);
*/
bool hdd_is_cli_iface_up(struct hdd_context *hdd_ctx);
/**
* hdd_set_disconnect_status() - set adapter disconnection status
* @hdd_adapter: Pointer to hdd adapter
* @disconnecting: Disconnect status to set
*
* Return: None
*/
void hdd_set_disconnect_status(struct hdd_adapter *adapter, bool disconnecting);
#endif /* end #if !defined(WLAN_HDD_MAIN_H) */

View file

@ -205,4 +205,11 @@ hdd_skb_fill_gso_size(struct net_device *dev, struct sk_buff *skb)
}
}
/**
* hdd_txrx_get_tx_ack_count() - get tx acked count
* @adapter: Pointer to adapter
*
* Return: tx acked count
*/
uint32_t hdd_txrx_get_tx_ack_count(struct hdd_adapter *adapter);
#endif /* end #if !defined(WLAN_HDD_TX_RX_H) */

View file

@ -69,6 +69,7 @@
#include "wlan_p2p_ucfg_api.h"
#include "wlan_ipa_ucfg_api.h"
#include "wlan_hdd_nud_tracking.h"
/* These are needed to recognize WPA and RSN suite types */
#define HDD_WPA_OUI_SIZE 4
#define HDD_RSN_OUI_SIZE 4
@ -1893,6 +1894,10 @@ static QDF_STATUS hdd_dis_connect_handler(struct hdd_adapter *adapter,
/* Unblock anyone waiting for disconnect to complete */
complete(&adapter->disconnect_comp_var);
hdd_nud_reset_tracking(adapter);
hdd_set_disconnect_status(adapter, false);
hdd_reset_limit_off_chan(adapter);
hdd_print_bss_info(sta_ctx);

View file

@ -349,6 +349,15 @@ cb_notify_set_roam_scan_hi_rssi_scan_params(struct hdd_context *hdd_ctx,
struct reg_table_entry g_registry_table[] = {
#ifdef WLAN_NUD_TRACKING
REG_VARIABLE(CFG_ENABLE_NUD_TRACKING_NAME, WLAN_PARAM_Integer,
struct hdd_config, enable_nud_tracking,
VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
CFG_ENABLE_NUD_TRACKING_DEFAULT,
CFG_ENABLE_NUD_TRACKING_MIN,
CFG_ENABLE_NUD_TRACKING_MAX),
#endif
REG_VARIABLE(CFG_ENABLE_CONNECTED_SCAN_NAME, WLAN_PARAM_Integer,
struct hdd_config, enable_connected_scan,
VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
@ -7446,6 +7455,8 @@ void hdd_cfg_print(struct hdd_context *hdd_ctx)
hdd_debug("Name = [%s] value = [0x%x]",
CFG_CHANNEL_SELECT_LOGIC_CONC_NAME,
hdd_ctx->config->channel_select_logic_conc);
hdd_nud_cfg_print(hdd_ctx);
}

View file

@ -18002,6 +18002,8 @@ static int wlan_hdd_cfg80211_connect_start(struct hdd_adapter *adapter,
hdd_conn_set_connection_state(adapter,
eConnectionState_Connecting);
hdd_set_disconnect_status(adapter, false);
qdf_runtime_pm_prevent_suspend(
&hdd_ctx->runtime_context.connect);
hdd_prevent_suspend_timeout(HDD_WAKELOCK_TIMEOUT_CONNECT,
@ -19739,6 +19741,15 @@ static int __wlan_hdd_cfg80211_disconnect(struct wiphy *wiphy,
if (0 != status)
return status;
qdf_mutex_acquire(&adapter->disconnection_status_lock);
if (adapter->disconnection_in_progress) {
qdf_mutex_release(&adapter->disconnection_status_lock);
hdd_debug("Disconnect is already in progress");
return 0;
}
adapter->disconnection_in_progress = true;
qdf_mutex_release(&adapter->disconnection_status_lock);
/* Issue disconnect request to SME, if station is in connected state */
if ((sta_ctx->conn_info.connState == eConnectionState_Associated) ||
(sta_ctx->conn_info.connState == eConnectionState_Connecting)) {
@ -19794,11 +19805,13 @@ static int __wlan_hdd_cfg80211_disconnect(struct wiphy *wiphy,
status = wlan_hdd_disconnect(adapter, reasonCode);
if (0 != status) {
hdd_err("wlan_hdd_disconnect failed, status: %d", status);
hdd_set_disconnect_status(adapter, false);
return -EINVAL;
}
} else {
hdd_err("Unexpected cfg disconnect called while in state: %d",
sta_ctx->conn_info.connState);
hdd_set_disconnect_status(adapter, false);
}
return status;

View file

@ -133,6 +133,7 @@
#include "wlan_disa_ucfg_api.h"
#include "wlan_ipa_ucfg_api.h"
#include <target_if.h>
#include "wlan_hdd_nud_tracking.h"
#ifdef CNSS_GENL
#include <net/cnss_nl.h>
@ -3672,6 +3673,7 @@ static struct hdd_adapter *hdd_alloc_station_adapter(struct hdd_context *hdd_ctx
adapter->offloads_configured = false;
adapter->is_link_up_service_needed = false;
adapter->disconnection_in_progress = false;
/* Init the net_device structure */
strlcpy(dev->name, name, IFNAMSIZ);
@ -4181,6 +4183,9 @@ static void hdd_cleanup_adapter(struct hdd_context *hdd_ctx, struct hdd_adapter
return;
}
hdd_nud_deinit_tracking(adapter);
qdf_mutex_destroy(&adapter->disconnection_status_lock);
hdd_debugfs_exit(adapter);
/*
@ -4677,6 +4682,10 @@ struct hdd_adapter *hdd_open_adapter(struct hdd_context *hdd_ctx, uint8_t sessio
goto err_free_netdev;
}
hdd_nud_init_tracking(adapter);
qdf_mutex_create(&adapter->disconnection_status_lock);
break;
case QDF_P2P_GO_MODE:
@ -4930,6 +4939,9 @@ QDF_STATUS hdd_stop_adapter(struct hdd_context *hdd_ctx,
hdd_enter();
hdd_nud_ignore_tracking(adapter, true);
hdd_nud_reset_tracking(adapter);
hdd_debug("Disabling queues");
wlan_hdd_netif_queue_control(adapter,
WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER,
@ -5311,6 +5323,10 @@ QDF_STATUS hdd_reset_all_adapters(struct hdd_context *hdd_ctx)
}
}
hdd_nud_reset_tracking(adapter);
hdd_nud_ignore_tracking(adapter, true);
hdd_set_disconnect_status(adapter, false);
hdd_softap_deinit_tx_rx(adapter);
/* Destroy vdev which will be recreated during reinit. */
@ -5916,6 +5932,7 @@ QDF_STATUS hdd_start_all_adapters(struct hdd_context *hdd_ctx)
hdd_tx_resume_cb,
hdd_tx_flow_control_is_pause);
hdd_nud_ignore_tracking(adapter, false);
break;
case QDF_SAP_MODE:
@ -6365,6 +6382,14 @@ static int hdd_wlan_register_ip6_notifier(struct hdd_context *hdd_ctx)
}
#endif
void hdd_set_disconnect_status(struct hdd_adapter *adapter, bool status)
{
qdf_mutex_acquire(&adapter->disconnection_status_lock);
adapter->disconnection_in_progress = status;
qdf_mutex_release(&adapter->disconnection_status_lock);
hdd_debug("setting disconnection status: %d", status);
}
/**
* hdd_register_notifiers - Register netdev notifiers.
* @hdd_ctx: HDD context
@ -6388,8 +6413,16 @@ static int hdd_register_notifiers(struct hdd_context *hdd_ctx)
goto unregister_ip6_notifier;
}
ret = hdd_nud_register_netevent_notifier(hdd_ctx);
if (ret) {
hdd_err("Failed to register netevent notifier: %d",
ret);
goto unregister_inetaddr_notifier;
}
return 0;
unregister_inetaddr_notifier:
unregister_inetaddr_notifier(&hdd_ctx->ipv4_notifier);
unregister_ip6_notifier:
hdd_wlan_unregister_ip6_notifier(hdd_ctx);
out:
@ -6407,6 +6440,7 @@ out:
*/
void hdd_unregister_notifiers(struct hdd_context *hdd_ctx)
{
hdd_nud_unregister_netevent_notifier(hdd_ctx);
hdd_wlan_unregister_ip6_notifier(hdd_ctx);
unregister_inetaddr_notifier(&hdd_ctx->ipv4_notifier);

View file

@ -0,0 +1,475 @@
/*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: contains nud event tracking main function definitions
*/
#include "wlan_hdd_main.h"
void hdd_nud_set_gateway_addr(struct hdd_adapter *adapter,
struct qdf_mac_addr gw_mac_addr)
{
qdf_mem_copy(adapter->nud_tracking.gw_mac_addr.bytes,
gw_mac_addr.bytes,
sizeof(struct qdf_mac_addr));
}
void hdd_nud_cfg_print(struct hdd_context *hdd_ctx)
{
hdd_debug("Name = [%s] value = [0x%x]",
CFG_ENABLE_NUD_TRACKING_NAME,
hdd_ctx->config->enable_nud_tracking);
}
void hdd_nud_incr_gw_rx_pkt_cnt(struct hdd_adapter *adapter,
struct qdf_mac_addr *mac_addr)
{
if (!adapter->nud_tracking.is_gw_rx_pkt_track_enabled)
return;
if (qdf_is_macaddr_equal(&adapter->nud_tracking.gw_mac_addr,
mac_addr))
qdf_atomic_inc(&adapter
->nud_tracking.tx_rx_stats.gw_rx_packets);
}
/**
* hdd_nud_flush_work() - flush pending nud work
* @adapter: Pointer to hdd adapter
*
* Return: None
*/
static inline void
hdd_nud_flush_work(struct hdd_adapter *adapter)
{
qdf_disable_work(&adapter->nud_tracking.nud_event_work);
}
void hdd_nud_deinit_tracking(struct hdd_adapter *adapter)
{
qdf_destroy_work(NULL, &adapter->nud_tracking.nud_event_work);
}
void hdd_nud_ignore_tracking(struct hdd_adapter *adapter, bool ignoring)
{
struct hdd_context *hdd_ctx;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (adapter->device_mode == QDF_STA_MODE &&
hdd_ctx->config->enable_nud_tracking)
adapter->nud_tracking.ignore_nud_tracking = ignoring;
}
void hdd_nud_reset_tracking(struct hdd_adapter *adapter)
{
struct hdd_context *hdd_ctx;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (adapter->device_mode == QDF_STA_MODE &&
hdd_ctx->config->enable_nud_tracking) {
hdd_debug("De Initialize the NUD tracking");
qdf_zero_macaddr(&adapter->nud_tracking.gw_mac_addr);
qdf_mem_zero(&adapter->nud_tracking.tx_rx_stats,
sizeof(struct hdd_nud_tx_rx_stats));
adapter->nud_tracking.curr_state = NUD_NONE;
qdf_atomic_set(&adapter
->nud_tracking.tx_rx_stats.gw_rx_packets, 0);
hdd_nud_flush_work(adapter);
}
}
/**
* hdd_nud_stats_info() - display wlan NUD stats info
* @hdd_adapter: Pointer to hdd adapter
*
* Return: None
*/
static void hdd_nud_stats_info(struct hdd_adapter *adapter)
{
hdd_debug("**** NUD STATS: ****");
hdd_debug("NUD Probe Tx : %d",
adapter->nud_tracking.tx_rx_stats.pre_tx_packets);
hdd_debug("NUD Probe Ack : %d",
adapter->nud_tracking.tx_rx_stats.pre_tx_acked);
hdd_debug("NUD Probe Rx : %d",
adapter->nud_tracking.tx_rx_stats.pre_rx_packets);
hdd_debug("NUD Failure Tx : %d",
adapter->nud_tracking.tx_rx_stats.post_tx_packets);
hdd_debug("NUD Failure Ack : %d",
adapter->nud_tracking.tx_rx_stats.post_tx_acked);
hdd_debug("NUD Failure Rx : %d",
adapter->nud_tracking.tx_rx_stats.post_rx_packets);
hdd_debug("NUD Gateway Rx : %d",
qdf_atomic_read(&adapter
->nud_tracking.tx_rx_stats.gw_rx_packets));
}
/**
* hdd_nud_capture_stats() - capture wlan NUD stats
* @hdd_adapter: Pointer to hdd adapter
* @nud_state: NUD state for which stats to capture
*
* Return: None
*/
static void hdd_nud_capture_stats(struct hdd_adapter *adapter,
uint8_t nud_state)
{
switch (nud_state) {
case NUD_INCOMPLETE:
case NUD_PROBE:
adapter->nud_tracking.tx_rx_stats.pre_tx_packets =
adapter->stats.tx_packets;
adapter->nud_tracking.tx_rx_stats.pre_rx_packets =
adapter->stats.rx_packets;
adapter->nud_tracking.tx_rx_stats.pre_tx_acked =
hdd_txrx_get_tx_ack_count(adapter);
break;
case NUD_FAILED:
adapter->nud_tracking.tx_rx_stats.post_tx_packets =
adapter->stats.tx_packets;
adapter->nud_tracking.tx_rx_stats.post_rx_packets =
adapter->stats.rx_packets;
adapter->nud_tracking.tx_rx_stats.post_tx_acked =
hdd_txrx_get_tx_ack_count(adapter);
break;
default:
break;
}
}
/**
* hdd_nud_honour_failure() - check if nud failure to be honored
* @hdd_adapter: Pointer to hdd_adapter
*
* Return: true if nud failure to be honored, else false.
*/
static bool hdd_nud_honour_failure(struct hdd_adapter *adapter)
{
uint32_t tx_transmitted;
uint32_t tx_acked;
uint32_t gw_rx_pkt;
tx_transmitted = adapter->nud_tracking.tx_rx_stats.post_tx_packets -
adapter->nud_tracking.tx_rx_stats.pre_tx_packets;
tx_acked = adapter->nud_tracking.tx_rx_stats.post_tx_acked -
adapter->nud_tracking.tx_rx_stats.pre_tx_acked;
gw_rx_pkt = qdf_atomic_read(&adapter
->nud_tracking.tx_rx_stats.gw_rx_packets);
if (!tx_transmitted || !tx_acked || !gw_rx_pkt) {
hdd_debug("NUD_FAILURE_HONORED");
hdd_nud_stats_info(adapter);
return true;
}
hdd_debug("NUD_FAILURE_NOT_HONORED");
hdd_nud_stats_info(adapter);
return false;
}
/**
* hdd_nud_set_tracking() - set the NUD tracking info
* @hdd_adapter: Pointer to hdd_adapter
* @nud_state: Current NUD state to set
* @capture_enabled: GW Rx packet to be capture or not
*
* Return: None
*/
static void hdd_nud_set_tracking(struct hdd_adapter *adapter,
uint8_t nud_state,
bool capture_enabled)
{
hdd_debug("set the NUD tracking");
adapter->nud_tracking.curr_state = nud_state;
qdf_atomic_set(&adapter->nud_tracking.tx_rx_stats.gw_rx_packets, 0);
adapter->nud_tracking.is_gw_rx_pkt_track_enabled = capture_enabled;
}
/**
* __hdd_nud_failure_work() - work for nud event
* @data: Pointer to hdd_adapter
*
* Return: None
*/
static void __hdd_nud_failure_work(void *data)
{
struct hdd_adapter *adapter;
struct hdd_context *hdd_ctx;
eConnectionState conn_state;
int status;
hdd_enter();
if (!data)
return;
adapter = (struct hdd_adapter *)data;
status = hdd_validate_adapter(adapter);
if (0 != status)
return;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return;
conn_state = (WLAN_HDD_GET_STATION_CTX_PTR(adapter))
->conn_info.connState;
if (eConnectionState_Associated != conn_state) {
hdd_debug("Not in Connected State");
return;
}
qdf_mutex_acquire(&adapter->disconnection_status_lock);
if (adapter->disconnection_in_progress) {
qdf_mutex_release(&adapter->disconnection_status_lock);
hdd_debug("Disconnect is already in progress");
return;
}
adapter->disconnection_in_progress = true;
qdf_mutex_release(&adapter->disconnection_status_lock);
hdd_debug("Disconnecting STA with session id: %d",
adapter->session_id);
/* Issue Disconnect */
status = wlan_hdd_disconnect(adapter, eCSR_DISCONNECT_REASON_DEAUTH);
if (0 != status) {
hdd_err("wlan_hdd_disconnect failed, status: %d", status);
hdd_set_disconnect_status(adapter, false);
}
hdd_exit();
}
/**
* hdd_nud_failure_work() - work for nud event
* @data: Pointer to hdd_adapter
*
* Return: None
*/
static void hdd_nud_failure_work(void *data)
{
cds_ssr_protect(__func__);
__hdd_nud_failure_work(data);
cds_ssr_unprotect(__func__);
}
void hdd_nud_init_tracking(struct hdd_adapter *adapter)
{
struct hdd_context *hdd_ctx;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (adapter->device_mode == QDF_STA_MODE &&
hdd_ctx->config->enable_nud_tracking) {
hdd_debug("Initialize the NUD tracking");
qdf_zero_macaddr(&adapter->nud_tracking.gw_mac_addr);
qdf_mem_zero(&adapter->nud_tracking.tx_rx_stats,
sizeof(struct hdd_nud_tx_rx_stats));
adapter->nud_tracking.curr_state = NUD_NONE;
adapter->nud_tracking.ignore_nud_tracking = false;
qdf_atomic_init(&adapter
->nud_tracking.tx_rx_stats.gw_rx_packets);
qdf_create_work(0, &adapter->nud_tracking.nud_event_work,
hdd_nud_failure_work,
(void *)adapter);
}
}
/**
* hdd_nud_process_failure_event() - processing NUD_FAILED event
* @hdd_adapter: Pointer to hdd_adapter
*
* Return: None
*/
static void hdd_nud_process_failure_event(struct hdd_adapter *adapter)
{
uint8_t curr_state;
curr_state = adapter->nud_tracking.curr_state;
if (curr_state == NUD_PROBE || curr_state == NUD_INCOMPLETE) {
hdd_nud_capture_stats(adapter, NUD_FAILED);
if (hdd_nud_honour_failure(adapter))
qdf_sched_work(0, &adapter
->nud_tracking.nud_event_work);
else
hdd_nud_set_tracking(adapter, NUD_NONE, false);
} else {
hdd_debug("NUD FAILED -> Current State [0x%x]", curr_state);
}
}
/**
* hdd_nud_filter_netevent() - filter netevents for STA interface
* @neighbour: Pointer to neighbour
*
* Return: None
*/
static void hdd_nud_filter_netevent(struct neighbour *neigh)
{
int status;
struct hdd_adapter *adapter;
struct hdd_context *hdd_ctx;
eConnectionState conn_state;
const struct net_device *netdev = neigh->dev;
hdd_enter();
hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return;
adapter = hdd_get_adapter_by_macaddr(hdd_ctx, netdev->dev_addr);
status = hdd_validate_adapter(adapter);
if (0 != status)
return;
if (adapter->device_mode != QDF_STA_MODE) {
hdd_err("Device_mode %s(%d) is not supported for NUD handling",
hdd_device_mode_to_string(adapter->device_mode),
adapter->device_mode);
return;
}
conn_state = (WLAN_HDD_GET_STATION_CTX_PTR(adapter))
->conn_info.connState;
if (eConnectionState_Associated != conn_state) {
hdd_debug("Not in Connected State");
return;
}
if (adapter->nud_tracking.ignore_nud_tracking) {
hdd_debug("NUD Tracking is Disabled");
return;
}
if (!qdf_is_macaddr_equal(&adapter->nud_tracking.gw_mac_addr,
(struct qdf_mac_addr *)&neigh->ha[0])) {
hdd_debug("NUD event not for registered GW");
return;
}
switch (neigh->nud_state) {
case NUD_PROBE:
case NUD_INCOMPLETE:
hdd_debug("NUD_START [0x%x]", neigh->nud_state);
hdd_nud_capture_stats(adapter, neigh->nud_state);
hdd_nud_set_tracking(adapter,
neigh->nud_state,
true);
break;
case NUD_REACHABLE:
hdd_debug("NUD_REACHABLE [0x%x]", neigh->nud_state);
hdd_nud_set_tracking(adapter, NUD_NONE, false);
break;
case NUD_FAILED:
hdd_debug("NUD_FAILED [0x%x]", neigh->nud_state);
hdd_nud_process_failure_event(adapter);
break;
default:
hdd_debug("NUD Event For Other State [0x%x]",
neigh->nud_state);
break;
}
hdd_exit();
}
/**
* __hdd_nud_netevent_cb() - netevent callback
* @nb: Pointer to notifier block
* @event: Net Event triggered
* @data: Pointer to neighbour struct
*
* Callback for netevent
*
* Return: None
*/
static void __hdd_nud_netevent_cb(struct notifier_block *nb,
unsigned long event,
void *data)
{
hdd_enter();
hdd_nud_filter_netevent(data);
hdd_exit();
}
/**
* hdd_nud_netevent_cb() - netevent callback
* @nb: Pointer to notifier block
* @event: Net Event triggered
* @data: Pointer to neighbour struct
*
* Callback for netevent
*
* Return: 0 on success
*/
static int hdd_nud_netevent_cb(struct notifier_block *nb, unsigned long event,
void *data)
{
switch (event) {
case NETEVENT_NEIGH_UPDATE:
cds_ssr_protect(__func__);
__hdd_nud_netevent_cb(nb, event, data);
cds_ssr_unprotect(__func__);
break;
case NETEVENT_REDIRECT:
default:
break;
}
return 0;
}
static struct notifier_block wlan_netevent_nb = {
.notifier_call = hdd_nud_netevent_cb
};
int hdd_nud_register_netevent_notifier(struct hdd_context *hdd_ctx)
{
int ret = 0;
if (hdd_ctx->config->enable_nud_tracking) {
ret = register_netevent_notifier(&wlan_netevent_nb);
if (!ret)
hdd_debug("Registered netevent notifier");
}
return ret;
}
void hdd_nud_unregister_netevent_notifier(struct hdd_context *hdd_ctx)
{
int ret;
if (hdd_ctx->config->enable_nud_tracking) {
ret = unregister_netevent_notifier(&wlan_netevent_nb);
if (!ret)
hdd_debug("Unregistered netevent notifier");
}
}

View file

@ -0,0 +1,193 @@
/*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: contains nud event tracking function declarations
*/
#ifndef _WLAN_NUD_TRACKING_H_
#define _WLAN_NUD_TRACKING_H_
#ifdef WLAN_NUD_TRACKING
/**
* struct hdd_nud_tx_rx_stats - Capture tx and rx count during NUD tracking
* @pre_tx_packets: Number of tx packets at NUD_PROBE event
* @pre_tx_acked: Number of tx acked at NUD_PROBE event
* @pre_rx_packets: Number of rx packets at NUD_PROBE event
* @post_tx_packets: Number of tx packets at NUD_FAILED event
* @post_tx_acked: Number of tx acked at NUD_FAILED event
* @post_rx_packets: Number of rx packets at NUD_FAILED event
* @gw_rx_packets: Number of rx packets from the registered gateway
* during the period from NUD_PROBE to NUD_FAILED
*/
struct hdd_nud_tx_rx_stats {
uint32_t pre_tx_packets;
uint32_t pre_tx_acked;
uint32_t pre_rx_packets;
uint32_t post_tx_packets;
uint32_t post_tx_acked;
uint32_t post_rx_packets;
qdf_atomic_t gw_rx_packets;
};
/**
* struct hdd_nud_tracking_info - structure to keep track for NUD information
* @curr_state: current state of NUD machine
* @ignore_nud_tracking: true if nud tracking is not required else false
* @tx_rx_stats: Number of packets during NUD tracking
* @gw_mac_addr: gateway mac address for which NUD events are tracked
* @nud_event_work: work to be scheduled during NUD_FAILED
* @is_gw_rx_pkt_track_enabled: true if rx pkt capturing is enabled for GW,
* else false
*/
struct hdd_nud_tracking_info {
uint8_t curr_state;
bool ignore_nud_tracking;
struct hdd_nud_tx_rx_stats tx_rx_stats;
struct qdf_mac_addr gw_mac_addr;
qdf_work_t nud_event_work;
bool is_gw_rx_pkt_track_enabled;
};
/**
* hdd_nud_set_gateway_addr() - set gateway mac address
* @adapter: Pointer to adapter
* @gw_mac_addr: mac address to be set
*
* Return: none
*/
void hdd_nud_set_gateway_addr(struct hdd_adapter *adapter,
struct qdf_mac_addr gw_mac_addr);
/**
* hdd_nud_cfg_print() - Print nud tracking related parameters
* @hdd_ctx: Pointer to HDD context
*
* Return: None
*/
void hdd_nud_cfg_print(struct hdd_context *hdd_ctx);
/**
* hdd_nud_incr_gw_rx_pkt_cnt() - Increment rx count for gateway
* @adapter: Pointer to adapter
* @mac_addr: Gateway mac address
*
* Return: None
*/
void hdd_nud_incr_gw_rx_pkt_cnt(struct hdd_adapter *adapter,
struct qdf_mac_addr *mac_addr);
/**
* hdd_nud_init_tracking() - initialize NUD tracking
* @hdd_adapter: Pointer to hdd adapter
*
* Return: None
*/
void hdd_nud_init_tracking(struct hdd_adapter *adapter);
/**
* hdd_nud_reset_tracking() - reset NUD tracking
* @hdd_adapter: Pointer to hdd adapter
*
* Return: None
*/
void hdd_nud_reset_tracking(struct hdd_adapter *adapter);
/**
* hdd_nud_deinit_tracking() - deinitialize NUD tracking
* @hdd_adapter: Pointer to hdd adapter
*
* Return: None
*/
void hdd_nud_deinit_tracking(struct hdd_adapter *adapter);
/**
* hdd_nud_ignore_tracking() - set/reset nud trackig status
* @data: Pointer to hdd_adapter
* @ignoring: Ignore status to set
*
* Return: None
*/
void hdd_nud_ignore_tracking(struct hdd_adapter *adapter,
bool ignoring);
/**
* hdd_nud_register_netevent_notifier - Register netevent notifiers.
* @hdd_ctx: HDD context
*
* Register netevent notifiers.
*
* Return: 0 on success and errno on failure
*/
int hdd_nud_register_netevent_notifier(struct hdd_context *hdd_ctx);
/**
* hdd_nud_unregister_netevent_notifier - Unregister netevent notifiers.
* @hdd_ctx: HDD context
*
* Unregister netevent notifiers.
*
* Return: None
*/
void hdd_nud_unregister_netevent_notifier(struct hdd_context *hdd_ctx);
#else
static inline void hdd_nud_set_gateway_addr(struct hdd_adapter *adapter,
struct qdf_mac_addr gw_mac_addr)
{
}
static inline void hdd_nud_cfg_print(struct hdd_context *hdd_ctx)
{
}
static inline void hdd_nud_incr_gw_rx_pkt_cnt(struct hdd_adapter *adapter,
struct qdf_mac_addr *mac_addr)
{
}
static inline void hdd_nud_init_tracking(struct hdd_adapter *adapter)
{
}
static inline void hdd_nud_reset_tracking(struct hdd_adapter *adapter)
{
}
static inline void hdd_nud_deinit_tracking(struct hdd_adapter *adapter)
{
}
static inline void hdd_nud_ignore_tracking(struct hdd_adapter *adapter,
bool status)
{
}
static inline int
hdd_nud_register_netevent_notifier(struct hdd_context *hdd_ctx)
{
return 0;
}
static inline void
hdd_nud_unregister_netevent_notifier(struct hdd_context *hdd_ctx)
{
}
#endif /* WLAN_NUD_TRACKING */
#endif /* end of _WLAN_NUD_TRACKING_H_ */

View file

@ -157,6 +157,8 @@ static int __wlan_hdd_cfg80211_set_gateway_params(struct wiphy *wiphy,
hdd_info("ipv4 addr: %pI4", req.ipv4_addr);
hdd_info("ipv6 addr: %pI6c", req.ipv6_addr);
hdd_nud_set_gateway_addr(adapter, req.gw_mac_addr);
status = sme_gateway_param_update(hdd_ctx->hHal, &req);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("sme_gateway_param_update failed(err=%d)", status);

View file

@ -65,6 +65,8 @@
#include "wlan_hdd_cfg80211.h"
#include <wlan_hdd_tsf.h>
#include "wlan_hdd_nud_tracking.h"
#ifdef QCA_LL_TX_FLOW_CONTROL_V2
/*
* Mapping Linux AC interpretation to SME AC.
@ -350,6 +352,12 @@ static inline struct sk_buff *hdd_skb_orphan(struct hdd_adapter *adapter,
}
#endif /* QCA_LL_LEGACY_TX_FLOW_CONTROL */
uint32_t hdd_txrx_get_tx_ack_count(struct hdd_adapter *adapter)
{
return cdp_get_tx_ack_stats(cds_get_context(QDF_MODULE_ID_SOC),
adapter->session_id);
}
/**
* qdf_event_eapol_log() - send event to wlan diag
* @skb: skb ptr
@ -1622,6 +1630,9 @@ QDF_STATUS hdd_rx_packet_cbk(void *context, qdf_nbuf_t rxBuf)
++adapter->stats.rx_packets;
adapter->stats.rx_bytes += skb->len;
/* Incr GW Rx count for NUD tracking based on GW mac addr */
hdd_nud_incr_gw_rx_pkt_cnt(adapter, mac_addr);
/* Check & drop replayed mcast packets (for IPV6) */
if (hdd_ctx->config->multicast_replay_filter &&
hdd_is_mcast_replay(skb)) {