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