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
diff --git a/Kbuild b/Kbuild
index 72236e0..d6a9805 100644
--- a/Kbuild
+++ b/Kbuild
@@ -421,6 +421,10 @@
#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 @@
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 @@
endif
####################################
+
ifeq ($(CONFIG_FEATURE_HTC_CREDIT_HISTORY), 1)
CDEFINES += -DFEATURE_HTC_CREDIT_HISTORY
endif
@@ -2552,6 +2561,11 @@
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
diff --git a/core/hdd/inc/wlan_hdd_cfg.h b/core/hdd/inc/wlan_hdd_cfg.h
index 8526201..982f791 100644
--- a/core/hdd/inc/wlan_hdd_cfg.h
+++ b/core/hdd/inc/wlan_hdd_cfg.h
@@ -96,6 +96,31 @@
#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 @@
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;
diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h
index 9c0b725..13cabfb 100644
--- a/core/hdd/inc/wlan_hdd_main.h
+++ b/core/hdd/inc/wlan_hdd_main.h
@@ -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 @@
/** 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 @@
*/
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) */
diff --git a/core/hdd/inc/wlan_hdd_tx_rx.h b/core/hdd/inc/wlan_hdd_tx_rx.h
index c8c4d51..7ffd112 100644
--- a/core/hdd/inc/wlan_hdd_tx_rx.h
+++ b/core/hdd/inc/wlan_hdd_tx_rx.h
@@ -205,4 +205,11 @@
}
}
+/**
+ * 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) */
diff --git a/core/hdd/src/wlan_hdd_assoc.c b/core/hdd/src/wlan_hdd_assoc.c
index 459b41e..68c46ac 100644
--- a/core/hdd/src/wlan_hdd_assoc.c
+++ b/core/hdd/src/wlan_hdd_assoc.c
@@ -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 @@
/* 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);
diff --git a/core/hdd/src/wlan_hdd_cfg.c b/core/hdd/src/wlan_hdd_cfg.c
index f50a4e4..cad8e15 100644
--- a/core/hdd/src/wlan_hdd_cfg.c
+++ b/core/hdd/src/wlan_hdd_cfg.c
@@ -349,6 +349,15 @@
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 @@
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);
}
diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c
index 3787522..dcf3e0e 100644
--- a/core/hdd/src/wlan_hdd_cfg80211.c
+++ b/core/hdd/src/wlan_hdd_cfg80211.c
@@ -18002,6 +18002,8 @@
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 @@
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 @@
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;
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c
index 510e1b8..a23a78b 100644
--- a/core/hdd/src/wlan_hdd_main.c
+++ b/core/hdd/src/wlan_hdd_main.c
@@ -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 @@
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 @@
return;
}
+ hdd_nud_deinit_tracking(adapter);
+ qdf_mutex_destroy(&adapter->disconnection_status_lock);
+
hdd_debugfs_exit(adapter);
/*
@@ -4677,6 +4682,10 @@
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 @@
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 @@
}
}
+ 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 @@
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 @@
}
#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 @@
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 @@
*/
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);
diff --git a/core/hdd/src/wlan_hdd_nud_tracking.c b/core/hdd/src/wlan_hdd_nud_tracking.c
new file mode 100644
index 0000000..bceff98
--- /dev/null
+++ b/core/hdd/src/wlan_hdd_nud_tracking.c
@@ -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");
+ }
+}
diff --git a/core/hdd/src/wlan_hdd_nud_tracking.h b/core/hdd/src/wlan_hdd_nud_tracking.h
new file mode 100644
index 0000000..4ba8d0c
--- /dev/null
+++ b/core/hdd/src/wlan_hdd_nud_tracking.h
@@ -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_ */
diff --git a/core/hdd/src/wlan_hdd_subnet_detect.c b/core/hdd/src/wlan_hdd_subnet_detect.c
index 29f149b..21fac9b 100644
--- a/core/hdd/src/wlan_hdd_subnet_detect.c
+++ b/core/hdd/src/wlan_hdd_subnet_detect.c
@@ -157,6 +157,8 @@
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);
diff --git a/core/hdd/src/wlan_hdd_tx_rx.c b/core/hdd/src/wlan_hdd_tx_rx.c
index af7829a..e11ad6f 100644
--- a/core/hdd/src/wlan_hdd_tx_rx.c
+++ b/core/hdd/src/wlan_hdd_tx_rx.c
@@ -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 @@
}
#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 @@
++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)) {