1 /*
2  * Copyright (c) 2016 Intel Corporation
3  * Copyright (c) 2023 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(net_shell);
10 
11 #include <zephyr/net/net_stats.h>
12 #include <zephyr/net/ethernet.h>
13 
14 #include "net_shell_private.h"
15 
16 #include "../ip/net_stats.h"
17 
18 enum net_shell_stats_format {
19 	NET_SHELL_STATS_FORMAT_DEFAULT,
20 	NET_SHELL_STATS_FORMAT_KEY_VALUE,
21 	NET_SHELL_STATS_FORMAT_HEX_BLOB,
22 	NET_SHELL_STATS_FORMAT_BOTH
23 };
24 
25 /** Shell stats options passed via user_data */
26 struct net_shell_stats_options {
27 	enum net_shell_stats_format format;
28 	uint32_t type;  /* Bitmask of ethernet_stats_type */
29 };
30 
31 #if defined(CONFIG_NET_STATISTICS)
32 
33 
34 #if NET_TC_COUNT > 1
priority2str(enum net_priority priority)35 static const char *priority2str(enum net_priority priority)
36 {
37 	switch (priority) {
38 	case NET_PRIORITY_BK:
39 		return "BK"; /* Background */
40 	case NET_PRIORITY_BE:
41 		return "BE"; /* Best effort */
42 	case NET_PRIORITY_EE:
43 		return "EE"; /* Excellent effort */
44 	case NET_PRIORITY_CA:
45 		return "CA"; /* Critical applications */
46 	case NET_PRIORITY_VI:
47 		return "VI"; /* Video, < 100 ms latency and jitter */
48 	case NET_PRIORITY_VO:
49 		return "VO"; /* Voice, < 10 ms latency and jitter  */
50 	case NET_PRIORITY_IC:
51 		return "IC"; /* Internetwork control */
52 	case NET_PRIORITY_NC:
53 		return "NC"; /* Network control */
54 	}
55 
56 	return "??";
57 }
58 #endif
59 
60 #if defined(CONFIG_NET_STATISTICS_ETHERNET) && \
61 					defined(CONFIG_NET_STATISTICS_USER_API)
print_eth_stats(struct net_if * iface,struct net_stats_eth * data,const struct shell * sh,struct net_shell_user_data * user_data)62 static void print_eth_stats(struct net_if *iface, struct net_stats_eth *data,
63 			    const struct shell *sh, struct net_shell_user_data *user_data)
64 {
65 	struct net_shell_stats_options *opts = NULL;
66 	uint32_t type = ETHERNET_STATS_TYPE_ALL;
67 
68 	if (user_data != NULL && user_data->user_data != NULL) {
69 		opts = (struct net_shell_stats_options *)user_data->user_data;
70 		type = opts->type;
71 	}
72 
73 	/* Print common stats if requested */
74 	if (type & ETHERNET_STATS_TYPE_COMMON) {
75 		PR("Statistics for Ethernet interface %p [%d]\n", iface,
76 		       net_if_get_by_iface(iface));
77 
78 		PR("Bytes received   : %llu\n", data->bytes.received);
79 		PR("Bytes sent       : %llu\n", data->bytes.sent);
80 		PR("Packets received : %u\n", data->pkts.rx);
81 		PR("Packets sent     : %u\n", data->pkts.tx);
82 		PR("Bcast received   : %u\n", data->broadcast.rx);
83 		PR("Bcast sent       : %u\n", data->broadcast.tx);
84 		PR("Mcast received   : %u\n", data->multicast.rx);
85 		PR("Mcast sent       : %u\n", data->multicast.tx);
86 
87 		PR("Send errors      : %u\n", data->errors.tx);
88 		PR("Receive errors   : %u\n", data->errors.rx);
89 		PR("Collisions       : %u\n", data->collisions);
90 		PR("Send Drops       : %u\n", data->tx_dropped);
91 		PR("Send timeouts    : %u\n", data->tx_timeout_count);
92 		PR("Send restarts    : %u\n", data->tx_restart_queue);
93 		PR("Unknown protocol : %u\n", data->unknown_protocol);
94 
95 		PR("Checksum offload : RX good %u errors %u\n",
96 		   data->csum.rx_csum_offload_good,
97 		   data->csum.rx_csum_offload_errors);
98 		PR("Flow control     : RX xon %u xoff %u TX xon %u xoff %u\n",
99 		   data->flow_control.rx_flow_control_xon,
100 		   data->flow_control.rx_flow_control_xoff,
101 		   data->flow_control.tx_flow_control_xon,
102 		   data->flow_control.tx_flow_control_xoff);
103 		PR("ECC errors       : uncorrected %u corrected %u\n",
104 		   data->error_details.uncorr_ecc_errors,
105 		   data->error_details.corr_ecc_errors);
106 		PR("HW timestamp     : RX cleared %u TX timeout %u skipped %u\n",
107 		   data->hw_timestamp.rx_hwtstamp_cleared,
108 		   data->hw_timestamp.tx_hwtstamp_timeouts,
109 		   data->hw_timestamp.tx_hwtstamp_skipped);
110 
111 		PR("RX errors : %5s %5s %5s %5s %5s %5s %5s %5s %5s %5s %5s\n",
112 		   "Len", "Over", "CRC", "Frame", "NoBuf", "Miss", "Long", "Short",
113 		   "Align", "DMA", "Alloc");
114 		PR("            %5u %5u %5u %5u %5u %5u %5u %5u %5u %5u %5u\n",
115 		   data->error_details.rx_length_errors,
116 		   data->error_details.rx_over_errors,
117 		   data->error_details.rx_crc_errors,
118 		   data->error_details.rx_frame_errors,
119 		   data->error_details.rx_no_buffer_count,
120 		   data->error_details.rx_missed_errors,
121 		   data->error_details.rx_long_length_errors,
122 		   data->error_details.rx_short_length_errors,
123 		   data->error_details.rx_align_errors,
124 		   data->error_details.rx_dma_failed,
125 		   data->error_details.rx_buf_alloc_failed);
126 		PR("TX errors : %5s %8s %5s %10s %7s %5s\n",
127 		   "Abort", "Carrier", "Fifo", "Heartbeat", "Window", "DMA");
128 		PR("            %5u %8u %5u %10u %7u %5u\n",
129 		   data->error_details.tx_aborted_errors,
130 		   data->error_details.tx_carrier_errors,
131 		   data->error_details.tx_fifo_errors,
132 		   data->error_details.tx_heartbeat_errors,
133 		   data->error_details.tx_window_errors,
134 		   data->error_details.tx_dma_failed);
135 	}
136 
137 #if defined(CONFIG_NET_STATISTICS_ETHERNET_VENDOR)
138 	/* Print vendor stats if requested - format options only apply here */
139 	if ((type & ETHERNET_STATS_TYPE_VENDOR) && data->vendor) {
140 		enum net_shell_stats_format format = NET_SHELL_STATS_FORMAT_DEFAULT;
141 		size_t i = 0;
142 
143 		if (opts != NULL) {
144 			format = opts->format;
145 		}
146 
147 		PR("Vendor specific statistics for Ethernet interface %p [%d]:\n",
148 		   iface, net_if_get_by_iface(iface));
149 
150 		/* Print key-value pairs if requested */
151 		if (format == NET_SHELL_STATS_FORMAT_DEFAULT ||
152 		    format == NET_SHELL_STATS_FORMAT_KEY_VALUE ||
153 		    format == NET_SHELL_STATS_FORMAT_BOTH) {
154 			do {
155 				PR("%s : %u\n", data->vendor[i].key, data->vendor[i].value);
156 				i++;
157 			} while (data->vendor[i].key != NULL);
158 		}
159 
160 		/* Print hex blob if requested */
161 		if (format == NET_SHELL_STATS_FORMAT_HEX_BLOB ||
162 		    format == NET_SHELL_STATS_FORMAT_BOTH) {
163 			/* Suitable for parsing */
164 			PR("Vendor stats hex blob: ");
165 			for (i = 0; data->vendor[i].key != NULL; i++) {
166 				uint32_t v = data->vendor[i].value;
167 
168 				PR("%02x%02x%02x%02x",
169 				   (uint8_t)(v & 0xFF),
170 				   (uint8_t)((v >> 8) & 0xFF),
171 				   (uint8_t)((v >> 16) & 0xFF),
172 				   (uint8_t)((v >> 24) & 0xFF));
173 			}
174 			PR("\n");
175 		}
176 	}
177 #endif /* CONFIG_NET_STATISTICS_ETHERNET_VENDOR */
178 }
179 #endif /* CONFIG_NET_STATISTICS_ETHERNET && CONFIG_NET_STATISTICS_USER_API */
180 
181 #if defined(CONFIG_NET_STATISTICS_PPP) && \
182 					defined(CONFIG_NET_STATISTICS_USER_API)
print_ppp_stats(struct net_if * iface,struct net_stats_ppp * data,const struct shell * sh)183 static void print_ppp_stats(struct net_if *iface, struct net_stats_ppp *data,
184 			    const struct shell *sh)
185 {
186 	PR("Frames recv    %u\n", data->pkts.rx);
187 	PR("Frames sent    %u\n", data->pkts.tx);
188 	PR("Frames dropped %u\n", data->drop);
189 	PR("Bad FCS        %u\n", data->chkerr);
190 }
191 #endif /* CONFIG_NET_STATISTICS_PPP && CONFIG_NET_STATISTICS_USER_API */
192 
193 #if !defined(CONFIG_NET_NATIVE)
194 #define GET_STAT(a, b) 0
195 #endif
196 
197 #if defined(CONFIG_NET_PKT_TXTIME_STATS_DETAIL) || \
198 	defined(CONFIG_NET_PKT_RXTIME_STATS_DETAIL)
199 #if (NET_TC_TX_COUNT > 1) || (NET_TC_RX_COUNT > 1)
get_net_pkt_tc_stats_detail(struct net_if * iface,int i,bool is_tx)200 static char *get_net_pkt_tc_stats_detail(struct net_if *iface, int i,
201 					  bool is_tx)
202 {
203 	static char extra_stats[sizeof("\t[0=xxxx us]") +
204 				sizeof("->xxxx") *
205 				NET_PKT_DETAIL_STATS_COUNT];
206 	int j, total = 0, pos = 0;
207 
208 	pos += snprintk(extra_stats, sizeof(extra_stats), "\t[0");
209 
210 	for (j = 0; j < NET_PKT_DETAIL_STATS_COUNT; j++) {
211 		net_stats_t count = 0;
212 		uint32_t avg;
213 
214 		if (is_tx) {
215 #if defined(CONFIG_NET_PKT_TXTIME_STATS_DETAIL) && (NET_TC_TX_COUNT > 1)
216 			count = GET_STAT(iface,
217 					 tc.sent[i].tx_time_detail[j].count);
218 #endif
219 		} else {
220 #if defined(CONFIG_NET_PKT_RXTIME_STATS_DETAIL) && (NET_TC_RX_COUNT > 1)
221 			count = GET_STAT(iface,
222 					 tc.recv[i].rx_time_detail[j].count);
223 #endif
224 		}
225 
226 		if (count == 0) {
227 			break;
228 		}
229 
230 		if (is_tx) {
231 #if defined(CONFIG_NET_PKT_TXTIME_STATS_DETAIL) && (NET_TC_TX_COUNT > 1)
232 			avg = (uint32_t)(GET_STAT(iface,
233 					   tc.sent[i].tx_time_detail[j].sum) /
234 				      (uint64_t)count);
235 #endif
236 		} else {
237 #if defined(CONFIG_NET_PKT_RXTIME_STATS_DETAIL) && (NET_TC_RX_COUNT > 1)
238 			avg = (uint32_t)(GET_STAT(iface,
239 					   tc.recv[i].rx_time_detail[j].sum) /
240 				      (uint64_t)count);
241 #endif
242 		}
243 
244 		if (avg == 0) {
245 			continue;
246 		}
247 
248 		total += avg;
249 
250 		pos += snprintk(extra_stats + pos, sizeof(extra_stats) - pos,
251 				"->%u", avg);
252 	}
253 
254 	if (total == 0U) {
255 		return "\0";
256 	}
257 
258 	pos += snprintk(extra_stats + pos, sizeof(extra_stats) - pos,
259 				"=%u us]", total);
260 
261 	return extra_stats;
262 }
263 #endif /* (NET_TC_TX_COUNT > 1) || (NET_TC_RX_COUNT > 1) */
264 
265 #if (NET_TC_TX_COUNT <= 1) || (NET_TC_RX_COUNT <= 1)
get_net_pkt_stats_detail(struct net_if * iface,bool is_tx)266 static char *get_net_pkt_stats_detail(struct net_if *iface, bool is_tx)
267 {
268 	static char extra_stats[sizeof("\t[0=xxxx us]") + sizeof("->xxxx") *
269 				NET_PKT_DETAIL_STATS_COUNT];
270 	int j, total = 0, pos = 0;
271 
272 	pos += snprintk(extra_stats, sizeof(extra_stats), "\t[0");
273 
274 	for (j = 0; j < NET_PKT_DETAIL_STATS_COUNT; j++) {
275 		net_stats_t count;
276 		uint32_t avg;
277 
278 		if (is_tx) {
279 #if defined(CONFIG_NET_PKT_TXTIME_STATS_DETAIL)
280 			count = GET_STAT(iface, tx_time_detail[j].count);
281 #endif
282 		} else {
283 #if defined(CONFIG_NET_PKT_RXTIME_STATS_DETAIL)
284 			count = GET_STAT(iface, rx_time_detail[j].count);
285 #endif
286 		}
287 
288 		if (count == 0) {
289 			break;
290 		}
291 
292 		if (is_tx) {
293 #if defined(CONFIG_NET_PKT_TXTIME_STATS_DETAIL)
294 			avg = (uint32_t)(GET_STAT(iface,
295 					       tx_time_detail[j].sum) /
296 				      (uint64_t)count);
297 #endif
298 		} else {
299 #if defined(CONFIG_NET_PKT_RXTIME_STATS_DETAIL)
300 			avg = (uint32_t)(GET_STAT(iface,
301 					       rx_time_detail[j].sum) /
302 				      (uint64_t)count);
303 #endif
304 		}
305 
306 		if (avg == 0) {
307 			continue;
308 		}
309 
310 		total += avg;
311 
312 		pos += snprintk(extra_stats + pos,
313 				sizeof(extra_stats) - pos,
314 				"->%u", avg);
315 	}
316 
317 	if (total == 0U) {
318 		return "\0";
319 	}
320 
321 	pos += snprintk(extra_stats + pos, sizeof(extra_stats) - pos,
322 			"=%u us]", total);
323 
324 	return extra_stats;
325 }
326 #endif /* (NET_TC_TX_COUNT == 1) || (NET_TC_RX_COUNT == 1) */
327 
328 #else /* CONFIG_NET_PKT_TXTIME_STATS_DETAIL || CONFIG_NET_PKT_RXTIME_STATS_DETAIL */
329 
330 #if defined(CONFIG_NET_PKT_TXTIME_STATS) || \
331 	defined(CONFIG_NET_PKT_RXTIME_STATS)
332 
333 #if (NET_TC_TX_COUNT > 1) || (NET_TC_RX_COUNT > 1)
get_net_pkt_tc_stats_detail(struct net_if * iface,int i,bool is_tx)334 static char *get_net_pkt_tc_stats_detail(struct net_if *iface, int i,
335 					 bool is_tx)
336 {
337 	ARG_UNUSED(iface);
338 	ARG_UNUSED(i);
339 	ARG_UNUSED(is_tx);
340 
341 	return "\0";
342 }
343 #endif
344 
345 #if (NET_TC_TX_COUNT == 1) || (NET_TC_RX_COUNT == 1)
get_net_pkt_stats_detail(struct net_if * iface,bool is_tx)346 static char *get_net_pkt_stats_detail(struct net_if *iface, bool is_tx)
347 {
348 	ARG_UNUSED(iface);
349 	ARG_UNUSED(is_tx);
350 
351 	return "\0";
352 }
353 #endif
354 #endif /* CONFIG_NET_PKT_TXTIME_STATS) || CONFIG_NET_PKT_RXTIME_STATS */
355 #endif /* CONFIG_NET_PKT_TXTIME_STATS_DETAIL || CONFIG_NET_PKT_RXTIME_STATS_DETAIL */
356 
print_tc_tx_stats(const struct shell * sh,struct net_if * iface)357 static void print_tc_tx_stats(const struct shell *sh, struct net_if *iface)
358 {
359 #if NET_TC_TX_COUNT > 1
360 	int i;
361 
362 	PR("TX traffic class statistics:\n");
363 
364 #if defined(CONFIG_NET_PKT_TXTIME_STATS)
365 	PR("TC  Priority\tSent pkts\tbytes\ttime\n");
366 
367 	for (i = 0; i < NET_TC_TX_COUNT; i++) {
368 		net_stats_t count = GET_STAT(iface,
369 					     tc.sent[i].tx_time.count);
370 		if (count == 0) {
371 			PR("[%d] %s (%u)\t%u\t\t%llu\t-\n", i,
372 			   priority2str(GET_STAT(iface, tc.sent[i].priority)),
373 			   GET_STAT(iface, tc.sent[i].priority),
374 			   GET_STAT(iface, tc.sent[i].pkts),
375 			   GET_STAT(iface, tc.sent[i].bytes));
376 		} else {
377 			PR("[%d] %s (%u)\t%u\t\t%llu\t%u us%s\n", i,
378 			   priority2str(GET_STAT(iface, tc.sent[i].priority)),
379 			   GET_STAT(iface, tc.sent[i].priority),
380 			   GET_STAT(iface, tc.sent[i].pkts),
381 			   GET_STAT(iface, tc.sent[i].bytes),
382 			   (uint32_t)(GET_STAT(iface,
383 					    tc.sent[i].tx_time.sum) /
384 				   (uint64_t)count),
385 			   get_net_pkt_tc_stats_detail(iface, i, true));
386 		}
387 	}
388 #else
389 	PR("TC  Priority\tSent pkts\tbytes\n");
390 
391 	for (i = 0; i < NET_TC_TX_COUNT; i++) {
392 		PR("[%d] %s (%u)\t%u\t\t%llu\n", i,
393 		   priority2str(GET_STAT(iface, tc.sent[i].priority)),
394 		   GET_STAT(iface, tc.sent[i].priority),
395 		   GET_STAT(iface, tc.sent[i].pkts),
396 		   GET_STAT(iface, tc.sent[i].bytes));
397 	}
398 #endif /* CONFIG_NET_PKT_TXTIME_STATS */
399 #else
400 	ARG_UNUSED(sh);
401 
402 #if defined(CONFIG_NET_PKT_TXTIME_STATS)
403 	net_stats_t count = GET_STAT(iface, tx_time.count);
404 
405 	if (count != 0) {
406 		PR("Avg %s net_pkt (%u) time %" PRIu32 " us%s\n", "TX", count,
407 		   (uint32_t)(GET_STAT(iface, tx_time.sum) / (uint64_t)count),
408 		   get_net_pkt_stats_detail(iface, true));
409 	}
410 #else
411 	ARG_UNUSED(iface);
412 #endif /* CONFIG_NET_PKT_TXTIME_STATS */
413 #endif /* NET_TC_TX_COUNT > 1 */
414 }
415 
print_tc_rx_stats(const struct shell * sh,struct net_if * iface)416 static void print_tc_rx_stats(const struct shell *sh, struct net_if *iface)
417 {
418 #if NET_TC_RX_COUNT > 1
419 	int i;
420 
421 	PR("RX traffic class statistics:\n");
422 
423 #if defined(CONFIG_NET_PKT_RXTIME_STATS)
424 	PR("TC  Priority\tRecv pkts\tDrop pkts\tbytes\ttime\n");
425 
426 	for (i = 0; i < NET_TC_RX_COUNT; i++) {
427 		net_stats_t count = GET_STAT(iface,
428 					     tc.recv[i].rx_time.count);
429 		if (count == 0) {
430 			PR("[%d] %s (%u)\t%u\t%u\t\t%llu\t-\n", i,
431 			   priority2str(GET_STAT(iface, tc.recv[i].priority)),
432 			   GET_STAT(iface, tc.recv[i].priority),
433 			   GET_STAT(iface, tc.recv[i].pkts),
434 			   GET_STAT(iface, tc.recv[i].dropped),
435 			   GET_STAT(iface, tc.recv[i].bytes));
436 		} else {
437 			PR("[%d] %s (%u)\t%u\t%u\t\t%llu\t%u us%s\n", i,
438 			   priority2str(GET_STAT(iface, tc.recv[i].priority)),
439 			   GET_STAT(iface, tc.recv[i].priority),
440 			   GET_STAT(iface, tc.recv[i].pkts),
441 			   GET_STAT(iface, tc.recv[i].dropped),
442 			   GET_STAT(iface, tc.recv[i].bytes),
443 			   (uint32_t)(GET_STAT(iface,
444 					    tc.recv[i].rx_time.sum) /
445 				   (uint64_t)count),
446 			   get_net_pkt_tc_stats_detail(iface, i, false));
447 		}
448 	}
449 #else
450 	PR("TC  Priority\tRecv pkts\tDrop pkts\tbytes\n");
451 
452 	for (i = 0; i < NET_TC_RX_COUNT; i++) {
453 		PR("[%d] %s (%u)\t%u\t%u\t\t%llu\n", i,
454 		   priority2str(GET_STAT(iface, tc.recv[i].priority)),
455 		   GET_STAT(iface, tc.recv[i].priority),
456 		   GET_STAT(iface, tc.recv[i].pkts),
457 		   GET_STAT(iface, tc.recv[i].dropped),
458 		   GET_STAT(iface, tc.recv[i].bytes));
459 	}
460 #endif /* CONFIG_NET_PKT_RXTIME_STATS */
461 #else
462 	ARG_UNUSED(sh);
463 
464 #if defined(CONFIG_NET_PKT_RXTIME_STATS)
465 	net_stats_t count = GET_STAT(iface, rx_time.count);
466 
467 	if (count != 0) {
468 		PR("Avg %s net_pkt (%u) time %" PRIu32 " us%s\n", "RX", count,
469 		   (uint32_t)(GET_STAT(iface, rx_time.sum) / (uint64_t)count),
470 		   get_net_pkt_stats_detail(iface, false));
471 	}
472 #else
473 	ARG_UNUSED(iface);
474 #endif /* CONFIG_NET_PKT_RXTIME_STATS */
475 
476 #endif /* NET_TC_RX_COUNT > 1 */
477 }
478 
print_net_pm_stats(const struct shell * sh,struct net_if * iface)479 static void print_net_pm_stats(const struct shell *sh, struct net_if *iface)
480 {
481 #if defined(CONFIG_NET_STATISTICS_POWER_MANAGEMENT)
482 	PR("PM suspend stats:\n");
483 	PR("\tLast time     : %u ms\n",
484 	   GET_STAT(iface, pm.last_suspend_time));
485 	PR("\tAverage time  : %u ms\n",
486 	   (uint32_t)(GET_STAT(iface, pm.overall_suspend_time) /
487 		   GET_STAT(iface, pm.suspend_count)));
488 	PR("\tTotal time    : %" PRIu64 " ms\n",
489 	   GET_STAT(iface, pm.overall_suspend_time));
490 	PR("\tHow many times: %u\n",
491 	   GET_STAT(iface, pm.suspend_count));
492 #else
493 	ARG_UNUSED(sh);
494 	ARG_UNUSED(iface);
495 #endif
496 }
497 
net_shell_print_statistics(struct net_if * iface,void * user_data)498 static void net_shell_print_statistics(struct net_if *iface, void *user_data)
499 {
500 	struct net_shell_user_data *data = user_data;
501 	const struct shell *sh = data->sh;
502 
503 	if (iface) {
504 		const char *extra;
505 
506 		PR("\nInterface %p (%s) [%d]\n", iface,
507 		   iface2str(iface, &extra), net_if_get_by_iface(iface));
508 		PR("===========================%s\n", extra);
509 	} else {
510 		PR("\nGlobal statistics\n");
511 		PR("=================\n");
512 	}
513 
514 #if defined(CONFIG_NET_STATISTICS_IPV6) && defined(CONFIG_NET_NATIVE_IPV6)
515 	PR("IPv6 recv      %u\tsent\t%u\tdrop\t%u\tforwarded\t%u\n",
516 	   GET_STAT(iface, ipv6.recv),
517 	   GET_STAT(iface, ipv6.sent),
518 	   GET_STAT(iface, ipv6.drop),
519 	   GET_STAT(iface, ipv6.forwarded));
520 #if defined(CONFIG_NET_STATISTICS_IPV6_ND)
521 	PR("IPv6 ND recv   %u\tsent\t%u\tdrop\t%u\n",
522 	   GET_STAT(iface, ipv6_nd.recv),
523 	   GET_STAT(iface, ipv6_nd.sent),
524 	   GET_STAT(iface, ipv6_nd.drop));
525 #endif /* CONFIG_NET_STATISTICS_IPV6_ND */
526 #if defined(CONFIG_NET_STATISTICS_IPV6_PMTU)
527 	PR("IPv6 PMTU recv %u\tsent\t%u\tdrop\t%u\n",
528 	   GET_STAT(iface, ipv6_pmtu.recv),
529 	   GET_STAT(iface, ipv6_pmtu.sent),
530 	   GET_STAT(iface, ipv6_pmtu.drop));
531 #endif /* CONFIG_NET_STATISTICS_IPV6_PMTU */
532 #if defined(CONFIG_NET_STATISTICS_MLD)
533 	PR("IPv6 MLD recv  %u\tsent\t%u\tdrop\t%u\n",
534 	   GET_STAT(iface, ipv6_mld.recv),
535 	   GET_STAT(iface, ipv6_mld.sent),
536 	   GET_STAT(iface, ipv6_mld.drop));
537 #endif /* CONFIG_NET_STATISTICS_MLD */
538 #endif /* CONFIG_NET_STATISTICS_IPV6 */
539 
540 #if defined(CONFIG_NET_STATISTICS_IPV4) && defined(CONFIG_NET_NATIVE_IPV4)
541 	PR("IPv4 recv      %u\tsent\t%u\tdrop\t%u\tforwarded\t%u\n",
542 	   GET_STAT(iface, ipv4.recv),
543 	   GET_STAT(iface, ipv4.sent),
544 	   GET_STAT(iface, ipv4.drop),
545 	   GET_STAT(iface, ipv4.forwarded));
546 #endif /* CONFIG_NET_STATISTICS_IPV4 */
547 
548 	PR("IP vhlerr      %u\thblener\t%u\tlblener\t%u\n",
549 	   GET_STAT(iface, ip_errors.vhlerr),
550 	   GET_STAT(iface, ip_errors.hblenerr),
551 	   GET_STAT(iface, ip_errors.lblenerr));
552 	PR("IP fragerr     %u\tchkerr\t%u\tprotoer\t%u\n",
553 	   GET_STAT(iface, ip_errors.fragerr),
554 	   GET_STAT(iface, ip_errors.chkerr),
555 	   GET_STAT(iface, ip_errors.protoerr));
556 
557 #if defined(CONFIG_NET_STATISTICS_IPV4_PMTU)
558 	PR("IPv4 PMTU recv %u\tsent\t%u\tdrop\t%u\n",
559 	   GET_STAT(iface, ipv4_pmtu.recv),
560 	   GET_STAT(iface, ipv4_pmtu.sent),
561 	   GET_STAT(iface, ipv4_pmtu.drop));
562 #endif /* CONFIG_NET_STATISTICS_IPV4_PMTU */
563 
564 #if defined(CONFIG_NET_STATISTICS_ICMP) && defined(CONFIG_NET_NATIVE_IPV4)
565 	PR("ICMP recv      %u\tsent\t%u\tdrop\t%u\n",
566 	   GET_STAT(iface, icmp.recv),
567 	   GET_STAT(iface, icmp.sent),
568 	   GET_STAT(iface, icmp.drop));
569 	PR("ICMP typeer    %u\tchkerr\t%u\n",
570 	   GET_STAT(iface, icmp.typeerr),
571 	   GET_STAT(iface, icmp.chkerr));
572 #endif
573 #if defined(CONFIG_NET_STATISTICS_IGMP)
574 	PR("IGMP recv      %u\tsent\t%u\tdrop\t%u\n",
575 	   GET_STAT(iface, ipv4_igmp.recv),
576 	   GET_STAT(iface, ipv4_igmp.sent),
577 	   GET_STAT(iface, ipv4_igmp.drop));
578 #endif /* CONFIG_NET_STATISTICS_IGMP */
579 #if defined(CONFIG_NET_STATISTICS_UDP) && defined(CONFIG_NET_NATIVE_UDP)
580 	PR("UDP recv       %u\tsent\t%u\tdrop\t%u\n",
581 	   GET_STAT(iface, udp.recv),
582 	   GET_STAT(iface, udp.sent),
583 	   GET_STAT(iface, udp.drop));
584 	PR("UDP chkerr     %u\n",
585 	   GET_STAT(iface, udp.chkerr));
586 #endif
587 
588 #if defined(CONFIG_NET_STATISTICS_TCP) && defined(CONFIG_NET_NATIVE_TCP)
589 	PR("TCP bytes recv %llu\tsent\t%llu\tresent\t%u\n",
590 	   GET_STAT(iface, tcp.bytes.received),
591 	   GET_STAT(iface, tcp.bytes.sent),
592 	   GET_STAT(iface, tcp.resent));
593 	PR("TCP seg recv   %u\tsent\t%u\tdrop\t%u\n",
594 	   GET_STAT(iface, tcp.recv),
595 	   GET_STAT(iface, tcp.sent),
596 	   GET_STAT(iface, tcp.seg_drop));
597 	PR("TCP seg resent %u\tchkerr\t%u\tackerr\t%u\n",
598 	   GET_STAT(iface, tcp.rexmit),
599 	   GET_STAT(iface, tcp.chkerr),
600 	   GET_STAT(iface, tcp.ackerr));
601 	PR("TCP seg rsterr %u\trst\t%u\n",
602 	   GET_STAT(iface, tcp.rsterr),
603 	   GET_STAT(iface, tcp.rst));
604 	PR("TCP conn drop  %u\tconnrst\t%u\n",
605 	   GET_STAT(iface, tcp.conndrop),
606 	   GET_STAT(iface, tcp.connrst));
607 	PR("TCP pkt drop   %u\n", GET_STAT(iface, tcp.drop));
608 #endif
609 #if defined(CONFIG_NET_STATISTICS_DNS)
610 	PR("DNS recv       %u\tsent\t%u\tdrop\t%u\n",
611 	   GET_STAT(iface, dns.recv),
612 	   GET_STAT(iface, dns.sent),
613 	   GET_STAT(iface, dns.drop));
614 #endif /* CONFIG_NET_STATISTICS_DNS */
615 #if defined(CONFIG_NET_STATISTICS_PKT_FILTER)
616 	PR("Filter drop rx %u"
617 	   IF_ENABLED(CONFIG_NET_PKT_FILTER_IPV4_HOOK, ("\tIPv4\t%u"))
618 	   IF_ENABLED(CONFIG_NET_PKT_FILTER_IPV6_HOOK, ("\tIPv6\t%u"))
619 	   IF_ENABLED(CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK, ("\tlocal\t%u"))
620 	   "\ttx\t%u\n",
621 	   GET_STAT(iface, pkt_filter.rx.drop),
622 	   IF_ENABLED(CONFIG_NET_PKT_FILTER_IPV4_HOOK,
623 		      (GET_STAT(iface, pkt_filter.rx.ipv4_drop),))
624 	   IF_ENABLED(CONFIG_NET_PKT_FILTER_IPV6_HOOK,
625 		      (GET_STAT(iface, pkt_filter.rx.ipv6_drop),))
626 	   IF_ENABLED(CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK,
627 		      (GET_STAT(iface, pkt_filter.rx.local_drop),))
628 	   GET_STAT(iface, pkt_filter.tx.drop));
629 #endif /* CONFIG_NET_STATISTICS_DNS */
630 
631 	PR("Bytes received %llu\n", GET_STAT(iface, bytes.received));
632 	PR("Bytes sent     %llu\n", GET_STAT(iface, bytes.sent));
633 	PR("Processing err %u\n", GET_STAT(iface, processing_error));
634 
635 	print_tc_tx_stats(sh, iface);
636 	print_tc_rx_stats(sh, iface);
637 
638 #if defined(CONFIG_NET_STATISTICS_ETHERNET) && \
639 					defined(CONFIG_NET_STATISTICS_USER_API)
640 	if (iface && net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
641 		const struct ethernet_api *eth_api;
642 		struct net_stats_eth *eth_data = NULL;
643 		uint32_t type = ETHERNET_STATS_TYPE_ALL;
644 
645 		if (data != NULL && data->user_data != NULL) {
646 			struct net_shell_stats_options *opts = data->user_data;
647 
648 			type = opts->type;
649 		}
650 
651 		eth_api = net_if_get_device(iface)->api;
652 		if (eth_api != NULL) {
653 			/* Use get_stats_type if available for type filtering */
654 			if (eth_api->get_stats_type != NULL) {
655 				eth_data = eth_api->get_stats_type(
656 					net_if_get_device(iface), type);
657 			} else if (eth_api->get_stats != NULL) {
658 				eth_data = eth_api->get_stats(
659 					net_if_get_device(iface));
660 			}
661 		}
662 
663 		if (eth_data != NULL) {
664 			print_eth_stats(iface, eth_data, sh, data);
665 		}
666 	}
667 #endif /* CONFIG_NET_STATISTICS_ETHERNET && CONFIG_NET_STATISTICS_USER_API */
668 
669 #if defined(CONFIG_NET_STATISTICS_PPP) && \
670 					defined(CONFIG_NET_STATISTICS_USER_API)
671 	if (iface && net_if_l2(iface) == &NET_L2_GET_NAME(PPP)) {
672 		struct net_stats_ppp ppp_data;
673 		int ret;
674 
675 		ret = net_mgmt(NET_REQUEST_STATS_GET_PPP, iface,
676 			       &ppp_data, sizeof(ppp_data));
677 		if (!ret) {
678 			print_ppp_stats(iface, &ppp_data, sh);
679 		}
680 	}
681 #endif /* CONFIG_NET_STATISTICS_PPP && CONFIG_NET_STATISTICS_USER_API */
682 
683 	print_net_pm_stats(sh, iface);
684 }
685 
net_shell_print_statistics_all(struct net_shell_user_data * data)686 static void net_shell_print_statistics_all(struct net_shell_user_data *data)
687 {
688 	if (IS_ENABLED(CONFIG_NET_STATISTICS_PER_INTERFACE)) {
689 		net_if_foreach(net_shell_print_statistics, data);
690 	}
691 }
692 
parse_stats_options(size_t argc,char * argv[],int start_idx,struct net_shell_stats_options * opts)693 static void parse_stats_options(size_t argc, char *argv[], int start_idx,
694 				struct net_shell_stats_options *opts)
695 {
696 	opts->format = NET_SHELL_STATS_FORMAT_DEFAULT;
697 	opts->type = ETHERNET_STATS_TYPE_ALL;
698 
699 	for (int i = start_idx; i < argc && argv[i] != NULL; i++) {
700 		/* Format options */
701 		if (strcmp(argv[i], "key-value") == 0) {
702 			opts->format = NET_SHELL_STATS_FORMAT_KEY_VALUE;
703 		} else if (strcmp(argv[i], "hex-blob") == 0) {
704 			opts->format = NET_SHELL_STATS_FORMAT_HEX_BLOB;
705 		} else if (strcmp(argv[i], "both") == 0) {
706 			opts->format = NET_SHELL_STATS_FORMAT_BOTH;
707 		/* Type filter options */
708 		} else if (strcmp(argv[i], "common") == 0) {
709 			opts->type = ETHERNET_STATS_TYPE_COMMON;
710 		} else if (strcmp(argv[i], "vendor") == 0) {
711 			opts->type = ETHERNET_STATS_TYPE_VENDOR;
712 		} else if (strcmp(argv[i], "all") == 0) {
713 			opts->type = ETHERNET_STATS_TYPE_ALL;
714 		}
715 	}
716 }
717 #endif /* CONFIG_NET_STATISTICS */
718 
cmd_net_stats_all(const struct shell * sh,size_t argc,char * argv[])719 int cmd_net_stats_all(const struct shell *sh, size_t argc, char *argv[])
720 {
721 #if defined(CONFIG_NET_STATISTICS)
722 	struct net_shell_user_data user_data;
723 	struct net_shell_stats_options opts;
724 
725 	user_data.sh = sh;
726 
727 	/* Parse options starting from argv[1] */
728 	parse_stats_options(argc, argv, 1, &opts);
729 
730 	user_data.user_data = &opts;
731 
732 	/* Print global network statistics */
733 	net_shell_print_statistics_all(&user_data);
734 #else
735 	ARG_UNUSED(argc);
736 	ARG_UNUSED(argv);
737 
738 	PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_STATISTICS",
739 		"statistics");
740 #endif
741 
742 	return 0;
743 }
744 
cmd_net_stats_iface(const struct shell * sh,size_t argc,char * argv[])745 int cmd_net_stats_iface(const struct shell *sh, size_t argc, char *argv[])
746 {
747 #if defined(CONFIG_NET_STATISTICS)
748 #if defined(CONFIG_NET_STATISTICS_PER_INTERFACE)
749 	struct net_shell_user_data data;
750 	struct net_shell_stats_options opts;
751 	struct net_if *iface;
752 	char *endptr;
753 	int idx;
754 #endif
755 #endif
756 
757 #if defined(CONFIG_NET_STATISTICS)
758 #if defined(CONFIG_NET_STATISTICS_PER_INTERFACE)
759 	if (argv[1] == NULL) {
760 		PR_WARNING("Network interface index missing!\n");
761 		return -ENOEXEC;
762 	}
763 
764 	idx = strtol(argv[1], &endptr, 10);
765 	if (*endptr != '\0') {
766 		PR_WARNING("Invalid index %s\n", argv[1]);
767 		return -ENOEXEC;
768 	}
769 
770 	iface = net_if_get_by_index(idx);
771 	if (!iface) {
772 		PR_WARNING("No such interface in index %d\n", idx);
773 		return -ENOEXEC;
774 	}
775 
776 	data.sh = sh;
777 
778 	/* Parse options starting from argv[2] */
779 	parse_stats_options(argc, argv, 2, &opts);
780 
781 	data.user_data = &opts;
782 
783 	net_shell_print_statistics(iface, &data);
784 #else
785 	PR_INFO("Per network interface statistics not collected.\n");
786 	PR_INFO("Please enable CONFIG_NET_STATISTICS_PER_INTERFACE\n");
787 #endif /* CONFIG_NET_STATISTICS_PER_INTERFACE */
788 #else
789 	ARG_UNUSED(argc);
790 	ARG_UNUSED(argv);
791 
792 	PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_STATISTICS",
793 		"statistics");
794 #endif
795 
796 	return 0;
797 }
798 
cmd_net_stats(const struct shell * sh,size_t argc,char * argv[])799 static int cmd_net_stats(const struct shell *sh, size_t argc, char *argv[])
800 {
801 #if defined(CONFIG_NET_STATISTICS)
802 	if (!argv[1]) {
803 		cmd_net_stats_all(sh, argc, argv);
804 		return 0;
805 	}
806 
807 	if (strcmp(argv[1], "reset") == 0) {
808 		net_stats_reset(NULL);
809 	} else {
810 		/* Pass arguments directly - cmd_net_stats_iface expects index in argv[1] */
811 		cmd_net_stats_iface(sh, argc, argv);
812 	}
813 #else
814 	ARG_UNUSED(argc);
815 	ARG_UNUSED(argv);
816 
817 	PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_STATISTICS",
818 		"statistics");
819 #endif
820 
821 	return 0;
822 }
823 
824 #if defined(CONFIG_NET_SHELL_DYN_CMD_COMPLETION)
825 
826 #include "iface_dynamic.h"
827 
828 #endif /* CONFIG_NET_SHELL_DYN_CMD_COMPLETION */
829 
830 SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_stats,
831 	SHELL_CMD(all, NULL,
832 		  "Show network statistics for all network interfaces.\n"
833 		  "Usage: net stats all [common|vendor|all] [key-value|hex-blob|both]",
834 		  cmd_net_stats_all),
835 	SHELL_CMD(iface, IFACE_DYN_CMD,
836 		  "'net stats <index> [options]' shows network statistics for "
837 		  "one specific network interface.\n"
838 		  "Type filter:\n"
839 		  "  common: Only common stats (skips FW query)\n"
840 		  "  vendor: Only vendor-specific stats\n"
841 		  "  all:    All stats (default)\n"
842 		  "Vendor stats format (only applies when vendor stats shown):\n"
843 		  "  key-value: Key-value pairs (default)\n"
844 		  "  hex-blob:  Hex blob for parsing\n"
845 		  "  both:      Both formats",
846 		  cmd_net_stats_iface),
847 	SHELL_SUBCMD_SET_END
848 );
849 
850 SHELL_SUBCMD_ADD((net), stats, &net_cmd_stats,
851 		 "Show network statistics.",
852 		 cmd_net_stats, 1, 4);
853