From b64650c29aadff19cb76dba8a6047156ff199bb7 Mon Sep 17 00:00:00 2001 From: Alok Kumar Date: Fri, 23 Mar 2018 17:05:11 +0530 Subject: [PATCH] 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 --- Kbuild | 14 + core/hdd/inc/wlan_hdd_cfg.h | 28 ++ core/hdd/inc/wlan_hdd_main.h | 19 ++ core/hdd/inc/wlan_hdd_tx_rx.h | 7 + core/hdd/src/wlan_hdd_assoc.c | 5 + core/hdd/src/wlan_hdd_cfg.c | 11 + core/hdd/src/wlan_hdd_cfg80211.c | 13 + core/hdd/src/wlan_hdd_main.c | 34 ++ core/hdd/src/wlan_hdd_nud_tracking.c | 475 ++++++++++++++++++++++++++ core/hdd/src/wlan_hdd_nud_tracking.h | 193 +++++++++++ core/hdd/src/wlan_hdd_subnet_detect.c | 2 + core/hdd/src/wlan_hdd_tx_rx.c | 11 + 12 files changed, 812 insertions(+) create mode 100644 core/hdd/src/wlan_hdd_nud_tracking.c create mode 100644 core/hdd/src/wlan_hdd_nud_tracking.h diff --git a/Kbuild b/Kbuild index 72236e00df11..d6a9805336bd 100644 --- a/Kbuild +++ b/Kbuild @@ -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 diff --git a/core/hdd/inc/wlan_hdd_cfg.h b/core/hdd/inc/wlan_hdd_cfg.h index 8526201a538b..982f7915f923 100644 --- a/core/hdd/inc/wlan_hdd_cfg.h +++ b/core/hdd/inc/wlan_hdd_cfg.h @@ -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 +/* + * + * 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 + * + * + */ + +#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 + /* * * 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; diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index 9c0b7251e8dc..13cabfbe49b7 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 +#include +#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) */ diff --git a/core/hdd/inc/wlan_hdd_tx_rx.h b/core/hdd/inc/wlan_hdd_tx_rx.h index c8c4d51c319c..7ffd112353fc 100644 --- a/core/hdd/inc/wlan_hdd_tx_rx.h +++ b/core/hdd/inc/wlan_hdd_tx_rx.h @@ -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) */ diff --git a/core/hdd/src/wlan_hdd_assoc.c b/core/hdd/src/wlan_hdd_assoc.c index 459b41e4a9c4..68c46ac17fe7 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 @@ 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); diff --git a/core/hdd/src/wlan_hdd_cfg.c b/core/hdd/src/wlan_hdd_cfg.c index f50a4e415cbb..cad8e15af074 100644 --- a/core/hdd/src/wlan_hdd_cfg.c +++ b/core/hdd/src/wlan_hdd_cfg.c @@ -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); } diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index 3787522389a0..dcf3e0e4dd8e 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -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; diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 510e1b8553c1..a23a78bec752 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 +#include "wlan_hdd_nud_tracking.h" #ifdef CNSS_GENL #include @@ -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); 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 000000000000..bceff985d4a9 --- /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 000000000000..4ba8d0c1fd35 --- /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 29f149b9417f..21fac9b25952 100644 --- a/core/hdd/src/wlan_hdd_subnet_detect.c +++ b/core/hdd/src/wlan_hdd_subnet_detect.c @@ -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); diff --git a/core/hdd/src/wlan_hdd_tx_rx.c b/core/hdd/src/wlan_hdd_tx_rx.c index af7829a3f929..e11ad6f7e098 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 +#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)) {