1 /*
2  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all
7  * copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /**
20  * DOC: contains nud event tracking main function definitions
21  */
22 
23 #include "osif_sync.h"
24 #include "wlan_dp_main.h"
25 #include "wlan_dlm_ucfg_api.h"
26 #include "wlan_dp_cfg.h"
27 #include <cdp_txrx_misc.h>
28 #include "wlan_cm_roam_ucfg_api.h"
29 #include <wlan_cm_api.h>
30 #include "wlan_dp_nud_tracking.h"
31 #include "wlan_vdev_mgr_api.h"
32 
33 #ifdef WLAN_NUD_TRACKING
34 /**
35  * dp_txrx_get_tx_ack_count() - Get Tx Ack count
36  * @dp_intf: Pointer to dp_intf
37  *
38  * Return: number of Tx ack count
39  */
dp_txrx_get_tx_ack_count(struct wlan_dp_intf * dp_intf)40 static uint32_t dp_txrx_get_tx_ack_count(struct wlan_dp_intf *dp_intf)
41 {
42 	struct cdp_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC);
43 	struct wlan_dp_link *dp_link;
44 	struct wlan_dp_link *dp_link_next;
45 	uint32_t ack_count = 0;
46 
47 	dp_for_each_link_held_safe(dp_intf, dp_link, dp_link_next) {
48 		ack_count += cdp_get_tx_ack_stats(soc, dp_link->link_id);
49 	}
50 
51 	return ack_count;
52 }
53 
dp_nud_set_gateway_addr(struct wlan_objmgr_vdev * vdev,struct qdf_mac_addr gw_mac_addr)54 void dp_nud_set_gateway_addr(struct wlan_objmgr_vdev *vdev,
55 			     struct qdf_mac_addr gw_mac_addr)
56 {
57 	struct wlan_dp_link *dp_link = dp_get_vdev_priv_obj(vdev);
58 	struct wlan_dp_intf *dp_intf;
59 
60 	if (!dp_link) {
61 		dp_err("Unable to get DP link");
62 		return;
63 	}
64 
65 	dp_intf = dp_link->dp_intf;
66 	qdf_mem_copy(dp_intf->nud_tracking.gw_mac_addr.bytes,
67 		     gw_mac_addr.bytes,
68 		     sizeof(struct qdf_mac_addr));
69 	dp_intf->nud_tracking.is_gw_updated = true;
70 }
71 
dp_nud_incr_gw_rx_pkt_cnt(struct wlan_dp_intf * dp_intf,struct qdf_mac_addr * mac_addr)72 void dp_nud_incr_gw_rx_pkt_cnt(struct wlan_dp_intf *dp_intf,
73 			       struct qdf_mac_addr *mac_addr)
74 {
75 	struct dp_nud_tracking_info *nud_tracking = &dp_intf->nud_tracking;
76 
77 	if (!nud_tracking->is_gw_rx_pkt_track_enabled)
78 		return;
79 
80 	if (!nud_tracking->is_gw_updated)
81 		return;
82 
83 	if (qdf_is_macaddr_equal(&nud_tracking->gw_mac_addr,
84 				 mac_addr))
85 		qdf_atomic_inc(&nud_tracking->tx_rx_stats.gw_rx_packets);
86 }
87 
dp_nud_flush_work(struct wlan_dp_intf * dp_intf)88 void dp_nud_flush_work(struct wlan_dp_intf *dp_intf)
89 {
90 	struct wlan_dp_psoc_context *dp_ctx = dp_intf->dp_ctx;
91 
92 	if (dp_intf->device_mode == QDF_STA_MODE &&
93 	    dp_ctx->dp_cfg.enable_nud_tracking) {
94 		dp_info("Flush the NUD work");
95 		qdf_disable_work(&dp_intf->nud_tracking.nud_event_work);
96 	}
97 }
98 
dp_nud_deinit_tracking(struct wlan_dp_intf * dp_intf)99 void dp_nud_deinit_tracking(struct wlan_dp_intf *dp_intf)
100 {
101 	struct wlan_dp_psoc_context *dp_ctx = dp_intf->dp_ctx;
102 
103 	if (dp_intf->device_mode == QDF_STA_MODE &&
104 	    dp_ctx->dp_cfg.enable_nud_tracking) {
105 		dp_info("DeInitialize the NUD tracking");
106 		qdf_destroy_work(NULL, &dp_intf->nud_tracking.nud_event_work);
107 	}
108 }
109 
dp_nud_ignore_tracking(struct wlan_dp_intf * dp_intf,bool ignoring)110 void dp_nud_ignore_tracking(struct wlan_dp_intf *dp_intf, bool ignoring)
111 {
112 	struct wlan_dp_psoc_context *dp_ctx = dp_intf->dp_ctx;
113 
114 	if (dp_intf->device_mode == QDF_STA_MODE &&
115 	    dp_ctx->dp_cfg.enable_nud_tracking)
116 		dp_intf->nud_tracking.ignore_nud_tracking = ignoring;
117 }
118 
dp_nud_reset_tracking(struct wlan_dp_intf * dp_intf)119 void dp_nud_reset_tracking(struct wlan_dp_intf *dp_intf)
120 {
121 	struct wlan_dp_psoc_context *dp_ctx = dp_intf->dp_ctx;
122 
123 	if (dp_intf->device_mode == QDF_STA_MODE &&
124 	    dp_ctx->dp_cfg.enable_nud_tracking) {
125 		dp_info("Reset the NUD tracking");
126 
127 		qdf_zero_macaddr(&dp_intf->nud_tracking.gw_mac_addr);
128 		dp_intf->nud_tracking.is_gw_updated = false;
129 		qdf_mem_zero(&dp_intf->nud_tracking.tx_rx_stats,
130 			     sizeof(struct dp_nud_tx_rx_stats));
131 
132 		dp_intf->nud_tracking.curr_state = DP_NUD_NONE;
133 		qdf_atomic_set(&dp_intf
134 			       ->nud_tracking.tx_rx_stats.gw_rx_packets, 0);
135 	}
136 }
137 
138 /**
139  * dp_nud_stats_info() - display wlan NUD stats info
140  * @dp_intf: Pointer to dp_intf
141  *
142  * Return: None
143  */
dp_nud_stats_info(struct wlan_dp_intf * dp_intf)144 static void dp_nud_stats_info(struct wlan_dp_intf *dp_intf)
145 {
146 	struct wlan_objmgr_vdev *vdev;
147 	struct dp_nud_tx_rx_stats *tx_rx_stats =
148 		&dp_intf->nud_tracking.tx_rx_stats;
149 	struct wlan_dp_psoc_callbacks *cb = &dp_intf->dp_ctx->dp_ops;
150 	uint32_t pause_map;
151 
152 	vdev = dp_objmgr_get_vdev_by_user(dp_intf->def_link, WLAN_DP_ID);
153 	if (!vdev) {
154 		return;
155 	}
156 
157 	dp_info("**** NUD STATS: ****");
158 	dp_info("NUD Probe Tx  : %d", tx_rx_stats->pre_tx_packets);
159 	dp_info("NUD Probe Ack : %d", tx_rx_stats->pre_tx_acked);
160 	dp_info("NUD Probe Rx  : %d", tx_rx_stats->pre_rx_packets);
161 	dp_info("NUD Failure Tx  : %d", tx_rx_stats->post_tx_packets);
162 	dp_info("NUD Failure Ack : %d", tx_rx_stats->post_tx_acked);
163 	dp_info("NUD Failure Rx  : %d", tx_rx_stats->post_rx_packets);
164 	dp_info("NUD Gateway Rx  : %d",
165 		qdf_atomic_read(&tx_rx_stats->gw_rx_packets));
166 
167 	cb->os_if_dp_nud_stats_info(vdev);
168 
169 	pause_map = cb->dp_get_pause_map(cb->callback_ctx,
170 					 dp_intf->dev);
171 	dp_info("Current pause_map value %x", pause_map);
172 	dp_objmgr_put_vdev_by_user(vdev, WLAN_DP_ID);
173 }
174 
175 /**
176  * dp_nud_capture_stats() - capture wlan NUD stats
177  * @dp_intf: Pointer to dp_intf
178  * @nud_state: NUD state for which stats to capture
179  *
180  * Return: None
181  */
dp_nud_capture_stats(struct wlan_dp_intf * dp_intf,uint8_t nud_state)182 static void dp_nud_capture_stats(struct wlan_dp_intf *dp_intf,
183 				 uint8_t nud_state)
184 {
185 	switch (nud_state) {
186 	case DP_NUD_INCOMPLETE:
187 	case DP_NUD_PROBE:
188 		dp_intf->nud_tracking.tx_rx_stats.pre_tx_packets =
189 				dp_intf->stats.tx_packets;
190 		dp_intf->nud_tracking.tx_rx_stats.pre_rx_packets =
191 				dp_intf->stats.rx_packets;
192 		dp_intf->nud_tracking.tx_rx_stats.pre_tx_acked =
193 				dp_txrx_get_tx_ack_count(dp_intf);
194 		break;
195 	case DP_NUD_FAILED:
196 		dp_intf->nud_tracking.tx_rx_stats.post_tx_packets =
197 				dp_intf->stats.tx_packets;
198 		dp_intf->nud_tracking.tx_rx_stats.post_rx_packets =
199 				dp_intf->stats.rx_packets;
200 		dp_intf->nud_tracking.tx_rx_stats.post_tx_acked =
201 				dp_txrx_get_tx_ack_count(dp_intf);
202 		break;
203 	default:
204 		break;
205 	}
206 }
207 
208 /**
209  * dp_nud_honour_failure() - check if nud failure to be honored
210  * @dp_intf: Pointer to dp_intf
211  *
212  * Return: true if nud failure to be honored, else false.
213  */
dp_nud_honour_failure(struct wlan_dp_intf * dp_intf)214 static bool dp_nud_honour_failure(struct wlan_dp_intf *dp_intf)
215 {
216 	uint32_t tx_transmitted, tx_acked, gw_rx_pkt, rx_received;
217 	struct dp_nud_tracking_info *nud_tracking = &dp_intf->nud_tracking;
218 	struct wlan_objmgr_vdev *vdev;
219 	uint8_t bssid[QDF_MAC_ADDR_SIZE];
220 	bool ap_is_gateway;
221 
222 	vdev = dp_objmgr_get_vdev_by_user(dp_intf->def_link, WLAN_DP_ID);
223 	if (!vdev)
224 		goto fail;
225 	wlan_vdev_mgr_get_param_bssid(vdev, bssid);
226 	dp_objmgr_put_vdev_by_user(vdev, WLAN_DP_ID);
227 
228 	tx_transmitted = nud_tracking->tx_rx_stats.post_tx_packets -
229 		nud_tracking->tx_rx_stats.pre_tx_packets;
230 	tx_acked = nud_tracking->tx_rx_stats.post_tx_acked -
231 		nud_tracking->tx_rx_stats.pre_tx_acked;
232 	gw_rx_pkt = qdf_atomic_read(&nud_tracking->tx_rx_stats.gw_rx_packets);
233 	rx_received = nud_tracking->tx_rx_stats.post_rx_packets -
234 		nud_tracking->tx_rx_stats.pre_rx_packets;
235 	ap_is_gateway = qdf_is_macaddr_equal(&dp_intf->nud_tracking.gw_mac_addr,
236 					     (struct qdf_mac_addr *)bssid);
237 
238 	if (!tx_transmitted || !tx_acked ||
239 	    !(gw_rx_pkt || (ap_is_gateway && rx_received))) {
240 		dp_info("NUD_FAILURE_HONORED [mac:" QDF_MAC_ADDR_FMT "]",
241 			QDF_MAC_ADDR_REF(nud_tracking->gw_mac_addr.bytes));
242 		dp_nud_stats_info(dp_intf);
243 		return true;
244 	}
245 fail:
246 	dp_info("NUD_FAILURE_NOT_HONORED [mac:" QDF_MAC_ADDR_FMT "]",
247 		QDF_MAC_ADDR_REF(nud_tracking->gw_mac_addr.bytes));
248 
249 	dp_nud_stats_info(dp_intf);
250 
251 	return false;
252 }
253 
254 /**
255  * dp_nud_set_tracking() - set the NUD tracking info
256  * @dp_intf: Pointer to dp_intf
257  * @nud_state: Current NUD state to set
258  * @capture_enabled: GW Rx packet to be capture or not
259  *
260  * Return: None
261  */
dp_nud_set_tracking(struct wlan_dp_intf * dp_intf,uint8_t nud_state,bool capture_enabled)262 static void dp_nud_set_tracking(struct wlan_dp_intf *dp_intf,
263 				uint8_t nud_state,
264 				bool capture_enabled)
265 {
266 	dp_intf->nud_tracking.curr_state = nud_state;
267 	qdf_atomic_set(&dp_intf->nud_tracking.tx_rx_stats.gw_rx_packets, 0);
268 	dp_intf->nud_tracking.is_gw_rx_pkt_track_enabled = capture_enabled;
269 }
270 
271 /**
272  * dp_nud_failure_work() - work for nud event
273  * @data: Pointer to dp_intf
274  *
275  * Return: None
276  */
dp_nud_failure_work(void * data)277 static void dp_nud_failure_work(void *data)
278 {
279 	struct wlan_dp_intf *dp_intf = data;
280 	struct wlan_dp_psoc_context *dp_ctx = dp_intf->dp_ctx;
281 
282 	if (dp_intf->nud_tracking.curr_state != DP_NUD_FAILED) {
283 		dp_info("Not in NUD_FAILED state");
284 		return;
285 	}
286 
287 	dp_ctx->dp_ops.dp_nud_failure_work(dp_ctx->dp_ops.callback_ctx,
288 					   dp_intf->dev);
289 }
290 
dp_nud_init_tracking(struct wlan_dp_intf * dp_intf)291 void dp_nud_init_tracking(struct wlan_dp_intf *dp_intf)
292 {
293 	struct wlan_dp_psoc_context *dp_ctx = dp_intf->dp_ctx;
294 
295 	if (dp_intf->device_mode == QDF_STA_MODE &&
296 	    dp_ctx->dp_cfg.enable_nud_tracking) {
297 		dp_info("Initialize the NUD tracking");
298 
299 		qdf_zero_macaddr(&dp_intf->nud_tracking.gw_mac_addr);
300 		qdf_mem_zero(&dp_intf->nud_tracking.tx_rx_stats,
301 			     sizeof(struct dp_nud_tx_rx_stats));
302 
303 		dp_intf->nud_tracking.curr_state = DP_NUD_NONE;
304 		dp_intf->nud_tracking.ignore_nud_tracking = false;
305 		dp_intf->nud_tracking.is_gw_updated = false;
306 
307 		qdf_atomic_init(&dp_intf
308 				->nud_tracking.tx_rx_stats.gw_rx_packets);
309 		qdf_create_work(0, &dp_intf->nud_tracking.nud_event_work,
310 				dp_nud_failure_work, dp_intf);
311 	}
312 }
313 
314 /**
315  * dp_nud_process_failure_event() - processing NUD_FAILED event
316  * @dp_intf: Pointer to dp_intf
317  *
318  * Return: None
319  */
dp_nud_process_failure_event(struct wlan_dp_intf * dp_intf)320 static void dp_nud_process_failure_event(struct wlan_dp_intf *dp_intf)
321 {
322 	uint8_t curr_state;
323 
324 	curr_state = dp_intf->nud_tracking.curr_state;
325 	if (curr_state == DP_NUD_PROBE || curr_state == DP_NUD_INCOMPLETE) {
326 		dp_nud_capture_stats(dp_intf, DP_NUD_FAILED);
327 		if (dp_nud_honour_failure(dp_intf)) {
328 			dp_intf->nud_tracking.curr_state = DP_NUD_FAILED;
329 			qdf_sched_work(0, &dp_intf
330 					->nud_tracking.nud_event_work);
331 		} else {
332 			dp_info("NUD_START [0x%x]", DP_NUD_INCOMPLETE);
333 			dp_nud_capture_stats(dp_intf, DP_NUD_INCOMPLETE);
334 			dp_nud_set_tracking(dp_intf, DP_NUD_INCOMPLETE, true);
335 		}
336 	} else {
337 		dp_info("NUD FAILED -> Current State [0x%x]", curr_state);
338 	}
339 }
340 
341 /**
342  * dp_nud_filter_netevent() - filter netevents for STA interface
343  * @netdev_addr: Pointer to neighbour
344  * @gw_mac_addr: Gateway MAC address
345  * @nud_state: Current NUD state
346  *
347  * Return: None
348  */
dp_nud_filter_netevent(struct qdf_mac_addr * netdev_addr,struct qdf_mac_addr * gw_mac_addr,uint8_t nud_state)349 static void dp_nud_filter_netevent(struct qdf_mac_addr *netdev_addr,
350 				   struct qdf_mac_addr *gw_mac_addr,
351 				   uint8_t nud_state)
352 {
353 	int status;
354 	struct wlan_dp_intf *dp_intf;
355 	struct wlan_dp_link *dp_link;
356 	struct wlan_dp_psoc_context *dp_ctx;
357 	struct wlan_objmgr_vdev *vdev;
358 
359 	dp_enter();
360 	dp_ctx = dp_get_context();
361 	if (!dp_ctx) {
362 		dp_err("unable to get DP context");
363 		return;
364 	}
365 
366 	dp_intf = dp_get_intf_by_macaddr(dp_ctx, netdev_addr);
367 
368 	if (!dp_intf) {
369 		dp_err("Unable to get DP intf for MAC " QDF_MAC_ADDR_FMT,
370 		       QDF_MAC_ADDR_REF(netdev_addr->bytes));
371 		return;
372 	}
373 
374 	status = is_dp_intf_valid(dp_intf);
375 	if (status) {
376 		dp_err("invalid dp_intf");
377 		return;
378 	}
379 
380 	if (dp_intf->device_mode != QDF_STA_MODE)
381 		return;
382 
383 	if (dp_intf->nud_tracking.ignore_nud_tracking) {
384 		dp_info("NUD Tracking is Disabled");
385 		return;
386 	}
387 
388 	if (!dp_intf->nud_tracking.is_gw_updated) {
389 		dp_info("GW is not updated");
390 		return;
391 	}
392 
393 	/*
394 	 * NUD is used for STATION mode only, where all the MLO links
395 	 * are assumed to be connected. Hence use the deflink here to check
396 	 * if the interface is connected.
397 	 */
398 	dp_link = dp_intf->def_link;
399 	vdev = dp_objmgr_get_vdev_by_user(dp_link, WLAN_DP_ID);
400 	if (!vdev)
401 		return;
402 
403 	if (!wlan_cm_is_vdev_active(vdev)) {
404 		dp_objmgr_put_vdev_by_user(vdev, WLAN_DP_ID);
405 		dp_info("Not in Connected State");
406 		return;
407 	}
408 	dp_objmgr_put_vdev_by_user(vdev, WLAN_DP_ID);
409 
410 	if (!dp_link->conn_info.is_authenticated) {
411 		dp_info("client " QDF_MAC_ADDR_FMT
412 			" is in the middle of WPS/EAPOL exchange.",
413 			QDF_MAC_ADDR_REF(dp_intf->mac_addr.bytes));
414 		return;
415 	}
416 
417 	if (!qdf_is_macaddr_equal(&dp_intf->nud_tracking.gw_mac_addr,
418 				  gw_mac_addr)) {
419 		dp_info("MAC mismatch NUD state %d GW MAC "
420 			 QDF_MAC_ADDR_FMT " Event MAC " QDF_MAC_ADDR_FMT,
421 			nud_state,
422 			QDF_MAC_ADDR_REF(dp_intf->nud_tracking.gw_mac_addr.bytes),
423 			QDF_MAC_ADDR_REF(gw_mac_addr->bytes));
424 		return;
425 	}
426 
427 	if (dp_ctx->is_wiphy_suspended) {
428 		dp_info("wlan is suspended, ignore NUD event");
429 		return;
430 	}
431 
432 	switch (nud_state) {
433 	case DP_NUD_PROBE:
434 	case DP_NUD_INCOMPLETE:
435 		dp_info("DP_NUD_START [0x%x]", nud_state);
436 		dp_nud_capture_stats(dp_intf, nud_state);
437 		dp_nud_set_tracking(dp_intf, nud_state, true);
438 		break;
439 
440 	case DP_NUD_REACHABLE:
441 		dp_info("DP_NUD_REACHABLE [0x%x]", nud_state);
442 		dp_nud_set_tracking(dp_intf, DP_NUD_NONE, false);
443 		break;
444 
445 	case DP_NUD_FAILED:
446 		dp_info("DP_NUD_FAILED [0x%x]", nud_state);
447 		/*
448 		 * This condition is to handle the scenario where NUD_FAILED
449 		 * events are received without any NUD_PROBE/INCOMPLETE event
450 		 * post roaming. Nud state is set to NONE as part of roaming.
451 		 * NUD_FAILED is not honored when the curr state is any state
452 		 * other than NUD_PROBE/INCOMPLETE so post roaming, nud state
453 		 * is moved to DP_NUD_PROBE to honor future NUD_FAILED events.
454 		 */
455 		if (dp_intf->nud_tracking.curr_state == DP_NUD_NONE) {
456 			dp_nud_capture_stats(dp_intf, DP_NUD_PROBE);
457 			dp_nud_set_tracking(dp_intf, DP_NUD_PROBE, true);
458 		} else {
459 			dp_nud_process_failure_event(dp_intf);
460 		}
461 		break;
462 	default:
463 		dp_info("NUD Event For Other State [0x%x]",
464 			nud_state);
465 		break;
466 	}
467 	dp_exit();
468 }
469 
dp_nud_netevent_cb(struct qdf_mac_addr * netdev_addr,struct qdf_mac_addr * gw_mac_addr,uint8_t nud_state)470 void dp_nud_netevent_cb(struct qdf_mac_addr *netdev_addr,
471 			struct qdf_mac_addr *gw_mac_addr, uint8_t nud_state)
472 {
473 	dp_enter();
474 	dp_nud_filter_netevent(netdev_addr, gw_mac_addr, nud_state);
475 	dp_exit();
476 }
477 
dp_nud_indicate_roam(struct wlan_objmgr_vdev * vdev)478 void dp_nud_indicate_roam(struct wlan_objmgr_vdev *vdev)
479 {
480 	struct wlan_dp_link *dp_link = dp_get_vdev_priv_obj(vdev);
481 	struct wlan_dp_intf *dp_intf;
482 
483 	if (!dp_link) {
484 		dp_err("Unable to get DP link");
485 		return;
486 	}
487 
488 	dp_intf = dp_link->dp_intf;
489 	dp_nud_set_tracking(dp_intf, DP_NUD_NONE, false);
490 }
491 #endif
492