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