1 /*
2 * Copyright (c) 2016 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #if defined(CONFIG_NET_STATISTICS_PERIODIC_OUTPUT)
8 #define NET_LOG_LEVEL LOG_LEVEL_INF
9 #else
10 #define NET_LOG_LEVEL CONFIG_NET_STATISTICS_LOG_LEVEL
11 #endif
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(net_stats, NET_LOG_LEVEL);
15
16 #include <zephyr/kernel.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <zephyr/net/net_core.h>
21 #include <zephyr/net/prometheus/collector.h>
22 #include <zephyr/net/prometheus/counter.h>
23 #include <zephyr/net/prometheus/gauge.h>
24 #include <zephyr/net/prometheus/histogram.h>
25 #include <zephyr/net/prometheus/summary.h>
26
27 #include "net_stats.h"
28 #include "net_private.h"
29
30 /* Global network statistics.
31 *
32 * The variable needs to be global so that the GET_STAT() macro can access it
33 * from net_shell.c
34 */
35 struct net_stats net_stats = { 0 };
36
37 #if defined(CONFIG_NET_STATISTICS_PERIODIC_OUTPUT)
38
39 #define PRINT_STATISTICS_INTERVAL (30 * MSEC_PER_SEC)
40
41 #if NET_TC_COUNT > 1
priority2str(enum net_priority priority)42 static const char *priority2str(enum net_priority priority)
43 {
44 switch (priority) {
45 case NET_PRIORITY_BK:
46 return "BK"; /* Background */
47 case NET_PRIORITY_BE:
48 return "BE"; /* Best effort */
49 case NET_PRIORITY_EE:
50 return "EE"; /* Excellent effort */
51 case NET_PRIORITY_CA:
52 return "CA"; /* Critical applications */
53 case NET_PRIORITY_VI:
54 return "VI"; /* Video, < 100 ms latency and jitter */
55 case NET_PRIORITY_VO:
56 return "VO"; /* Voice, < 10 ms latency and jitter */
57 case NET_PRIORITY_IC:
58 return "IC"; /* Internetwork control */
59 case NET_PRIORITY_NC:
60 return "NC"; /* Network control */
61 }
62
63 return "??";
64 }
65 #endif
66
cmp_val(uint64_t val1,uint64_t val2)67 static inline int64_t cmp_val(uint64_t val1, uint64_t val2)
68 {
69 return (int64_t)(val1 - val2);
70 }
71
stats(struct net_if * iface)72 static inline void stats(struct net_if *iface)
73 {
74 static uint64_t next_print;
75 uint64_t curr = k_uptime_get();
76 int64_t cmp = cmp_val(curr, next_print);
77 int i;
78
79 if (!next_print || (abs(cmp) > PRINT_STATISTICS_INTERVAL)) {
80 if (iface) {
81 NET_INFO("Interface %p [%d]", iface,
82 net_if_get_by_iface(iface));
83 } else {
84 NET_INFO("Global statistics:");
85 }
86
87 #if defined(CONFIG_NET_STATISTICS_IPV6)
88 NET_INFO("IPv6 recv %d\tsent\t%d\tdrop\t%d\tforwarded\t%d",
89 GET_STAT(iface, ipv6.recv),
90 GET_STAT(iface, ipv6.sent),
91 GET_STAT(iface, ipv6.drop),
92 GET_STAT(iface, ipv6.forwarded));
93 #if defined(CONFIG_NET_STATISTICS_IPV6_ND)
94 NET_INFO("IPv6 ND recv %d\tsent\t%d\tdrop\t%d",
95 GET_STAT(iface, ipv6_nd.recv),
96 GET_STAT(iface, ipv6_nd.sent),
97 GET_STAT(iface, ipv6_nd.drop));
98 #endif /* CONFIG_NET_STATISTICS_IPV6_ND */
99 #if defined(CONFIG_NET_STATISTICS_IPV6_PMTU)
100 NET_INFO("IPv6 PMTU recv %d\tsent\t%d\tdrop\t%d",
101 GET_STAT(iface, ipv6_pmtu.recv),
102 GET_STAT(iface, ipv6_pmtu.sent),
103 GET_STAT(iface, ipv6_pmtu.drop));
104 #endif /* CONFIG_NET_STATISTICS_IPV6_PMTU */
105 #if defined(CONFIG_NET_STATISTICS_MLD)
106 NET_INFO("IPv6 MLD recv %d\tsent\t%d\tdrop\t%d",
107 GET_STAT(iface, ipv6_mld.recv),
108 GET_STAT(iface, ipv6_mld.sent),
109 GET_STAT(iface, ipv6_mld.drop));
110 #endif /* CONFIG_NET_STATISTICS_MLD */
111 #endif /* CONFIG_NET_STATISTICS_IPV6 */
112
113 #if defined(CONFIG_NET_STATISTICS_IPV4)
114 NET_INFO("IPv4 recv %d\tsent\t%d\tdrop\t%d\tforwarded\t%d",
115 GET_STAT(iface, ipv4.recv),
116 GET_STAT(iface, ipv4.sent),
117 GET_STAT(iface, ipv4.drop),
118 GET_STAT(iface, ipv4.forwarded));
119 #endif /* CONFIG_NET_STATISTICS_IPV4 */
120
121 NET_INFO("IP vhlerr %d\thblener\t%d\tlblener\t%d",
122 GET_STAT(iface, ip_errors.vhlerr),
123 GET_STAT(iface, ip_errors.hblenerr),
124 GET_STAT(iface, ip_errors.lblenerr));
125 NET_INFO("IP fragerr %d\tchkerr\t%d\tprotoer\t%d",
126 GET_STAT(iface, ip_errors.fragerr),
127 GET_STAT(iface, ip_errors.chkerr),
128 GET_STAT(iface, ip_errors.protoerr));
129
130 #if defined(CONFIG_NET_STATISTICS_IPV4_PMTU)
131 NET_INFO("IPv4 PMTU recv %d\tsent\t%d\tdrop\t%d",
132 GET_STAT(iface, ipv4_pmtu.recv),
133 GET_STAT(iface, ipv4_pmtu.sent),
134 GET_STAT(iface, ipv4_pmtu.drop));
135 #endif /* CONFIG_NET_STATISTICS_IPV4_PMTU */
136
137 NET_INFO("ICMP recv %d\tsent\t%d\tdrop\t%d",
138 GET_STAT(iface, icmp.recv),
139 GET_STAT(iface, icmp.sent),
140 GET_STAT(iface, icmp.drop));
141 NET_INFO("ICMP typeer %d\tchkerr\t%d",
142 GET_STAT(iface, icmp.typeerr),
143 GET_STAT(iface, icmp.chkerr));
144
145 #if defined(CONFIG_NET_STATISTICS_UDP)
146 NET_INFO("UDP recv %d\tsent\t%d\tdrop\t%d",
147 GET_STAT(iface, udp.recv),
148 GET_STAT(iface, udp.sent),
149 GET_STAT(iface, udp.drop));
150 NET_INFO("UDP chkerr %d",
151 GET_STAT(iface, udp.chkerr));
152 #endif
153
154 #if defined(CONFIG_NET_STATISTICS_TCP)
155 NET_INFO("TCP bytes recv %u\tsent\t%d",
156 GET_STAT(iface, tcp.bytes.received),
157 GET_STAT(iface, tcp.bytes.sent));
158 NET_INFO("TCP seg recv %d\tsent\t%d\tdrop\t%d",
159 GET_STAT(iface, tcp.recv),
160 GET_STAT(iface, tcp.sent),
161 GET_STAT(iface, tcp.drop));
162 NET_INFO("TCP seg resent %d\tchkerr\t%d\tackerr\t%d",
163 GET_STAT(iface, tcp.resent),
164 GET_STAT(iface, tcp.chkerr),
165 GET_STAT(iface, tcp.ackerr));
166 NET_INFO("TCP seg rsterr %d\trst\t%d\tre-xmit\t%d",
167 GET_STAT(iface, tcp.rsterr),
168 GET_STAT(iface, tcp.rst),
169 GET_STAT(iface, tcp.rexmit));
170 NET_INFO("TCP conn drop %d\tconnrst\t%d",
171 GET_STAT(iface, tcp.conndrop),
172 GET_STAT(iface, tcp.connrst));
173 #endif
174
175 NET_INFO("Bytes received %u", GET_STAT(iface, bytes.received));
176 NET_INFO("Bytes sent %u", GET_STAT(iface, bytes.sent));
177 NET_INFO("Processing err %d",
178 GET_STAT(iface, processing_error));
179
180 #if NET_TC_COUNT > 1
181 #if NET_TC_TX_COUNT > 1
182 NET_INFO("TX traffic class statistics:");
183 NET_INFO("TC Priority\tSent pkts\tbytes");
184
185 for (i = 0; i < NET_TC_TX_COUNT; i++) {
186 NET_INFO("[%d] %s (%d)\t%d\t\t%d", i,
187 priority2str(GET_STAT(iface,
188 tc.sent[i].priority)),
189 GET_STAT(iface, tc.sent[i].priority),
190 GET_STAT(iface, tc.sent[i].pkts),
191 GET_STAT(iface, tc.sent[i].bytes));
192 }
193 #endif
194
195 #if NET_TC_RX_COUNT > 1
196 NET_INFO("RX traffic class statistics:");
197 NET_INFO("TC Priority\tRecv pkts\tbytes");
198
199 for (i = 0; i < NET_TC_RX_COUNT; i++) {
200 NET_INFO("[%d] %s (%d)\t%d\t\t%d", i,
201 priority2str(GET_STAT(iface,
202 tc.recv[i].priority)),
203 GET_STAT(iface, tc.recv[i].priority),
204 GET_STAT(iface, tc.recv[i].pkts),
205 GET_STAT(iface, tc.recv[i].bytes));
206 }
207 #endif
208 #else /* NET_TC_COUNT > 1 */
209 ARG_UNUSED(i);
210 #endif /* NET_TC_COUNT > 1 */
211
212 #if defined(CONFIG_NET_STATISTICS_POWER_MANAGEMENT)
213 NET_INFO("Power management statistics:");
214 NET_INFO("Last suspend time: %u ms",
215 GET_STAT(iface, pm.last_suspend_time));
216 NET_INFO("Got suspended %d times",
217 GET_STAT(iface, pm.suspend_count));
218 NET_INFO("Average suspend time: %u ms",
219 (uint32_t)(GET_STAT(iface, pm.overall_suspend_time) /
220 GET_STAT(iface, pm.suspend_count)));
221 NET_INFO("Total suspended time: %llu ms",
222 GET_STAT(iface, pm.overall_suspend_time));
223 #endif
224 next_print = curr + PRINT_STATISTICS_INTERVAL;
225 }
226 }
227
net_print_statistics_iface(struct net_if * iface)228 void net_print_statistics_iface(struct net_if *iface)
229 {
230 /* In order to make the info print lines shorter, use shorter
231 * function name.
232 */
233 stats(iface);
234 }
235
iface_cb(struct net_if * iface,void * user_data)236 static void iface_cb(struct net_if *iface, void *user_data)
237 {
238 net_print_statistics_iface(iface);
239 }
240
net_print_statistics_all(void)241 void net_print_statistics_all(void)
242 {
243 net_if_foreach(iface_cb, NULL);
244 }
245
net_print_statistics(void)246 void net_print_statistics(void)
247 {
248 net_print_statistics_iface(NULL);
249 }
250
251 #endif /* CONFIG_NET_STATISTICS_PERIODIC_OUTPUT */
252
253 #if defined(CONFIG_NET_STATISTICS_USER_API)
254
net_stats_get(uint32_t mgmt_request,struct net_if * iface,void * data,size_t len)255 static int net_stats_get(uint32_t mgmt_request, struct net_if *iface,
256 void *data, size_t len)
257 {
258 size_t len_chk = 0;
259 void *src = NULL;
260
261 switch (NET_MGMT_GET_COMMAND(mgmt_request)) {
262 case NET_REQUEST_STATS_CMD_GET_ALL:
263 len_chk = sizeof(struct net_stats);
264 #if defined(CONFIG_NET_STATISTICS_PER_INTERFACE)
265 src = iface ? &iface->stats : &net_stats;
266 #else
267 src = &net_stats;
268 #endif
269 break;
270 case NET_REQUEST_STATS_CMD_GET_PROCESSING_ERROR:
271 len_chk = sizeof(net_stats_t);
272 src = GET_STAT_ADDR(iface, processing_error);
273 break;
274 case NET_REQUEST_STATS_CMD_GET_BYTES:
275 len_chk = sizeof(struct net_stats_bytes);
276 src = GET_STAT_ADDR(iface, bytes);
277 break;
278 case NET_REQUEST_STATS_CMD_GET_IP_ERRORS:
279 len_chk = sizeof(struct net_stats_ip_errors);
280 src = GET_STAT_ADDR(iface, ip_errors);
281 break;
282 #if defined(CONFIG_NET_STATISTICS_IPV4)
283 case NET_REQUEST_STATS_CMD_GET_IPV4:
284 len_chk = sizeof(struct net_stats_ip);
285 src = GET_STAT_ADDR(iface, ipv4);
286 break;
287 #endif
288 #if defined(CONFIG_NET_STATISTICS_IPV6)
289 case NET_REQUEST_STATS_CMD_GET_IPV6:
290 len_chk = sizeof(struct net_stats_ip);
291 src = GET_STAT_ADDR(iface, ipv6);
292 break;
293 #endif
294 #if defined(CONFIG_NET_STATISTICS_IPV6_ND)
295 case NET_REQUEST_STATS_CMD_GET_IPV6_ND:
296 len_chk = sizeof(struct net_stats_ipv6_nd);
297 src = GET_STAT_ADDR(iface, ipv6_nd);
298 break;
299 #endif
300 #if defined(CONFIG_NET_STATISTICS_IPV6_PMTU)
301 case NET_REQUEST_STATS_CMD_GET_IPV6_PMTU:
302 len_chk = sizeof(struct net_stats_ipv6_pmtu);
303 src = GET_STAT_ADDR(iface, ipv6_pmtu);
304 break;
305 #endif
306 #if defined(CONFIG_NET_STATISTICS_IPV4_PMTU)
307 case NET_REQUEST_STATS_CMD_GET_IPV4_PMTU:
308 len_chk = sizeof(struct net_stats_ipv4_pmtu);
309 src = GET_STAT_ADDR(iface, ipv4_pmtu);
310 break;
311 #endif
312 #if defined(CONFIG_NET_STATISTICS_ICMP)
313 case NET_REQUEST_STATS_CMD_GET_ICMP:
314 len_chk = sizeof(struct net_stats_icmp);
315 src = GET_STAT_ADDR(iface, icmp);
316 break;
317 #endif
318 #if defined(CONFIG_NET_STATISTICS_UDP)
319 case NET_REQUEST_STATS_CMD_GET_UDP:
320 len_chk = sizeof(struct net_stats_udp);
321 src = GET_STAT_ADDR(iface, udp);
322 break;
323 #endif
324 #if defined(CONFIG_NET_STATISTICS_TCP)
325 case NET_REQUEST_STATS_CMD_GET_TCP:
326 len_chk = sizeof(struct net_stats_tcp);
327 src = GET_STAT_ADDR(iface, tcp);
328 break;
329 #endif
330 #if defined(CONFIG_NET_STATISTICS_POWER_MANAGEMENT)
331 case NET_REQUEST_STATS_GET_PM:
332 len_chk = sizeof(struct net_stats_pm);
333 src = GET_STAT_ADDR(iface, pm);
334 break;
335 #endif
336 }
337
338 if (len != len_chk || !src) {
339 return -EINVAL;
340 }
341
342 memcpy(data, src, len);
343
344 return 0;
345 }
346
347 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_ALL,
348 net_stats_get);
349
350 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_PROCESSING_ERROR,
351 net_stats_get);
352
353 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_BYTES,
354 net_stats_get);
355
356 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IP_ERRORS,
357 net_stats_get);
358
359 #if defined(CONFIG_NET_STATISTICS_IPV4)
360 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV4,
361 net_stats_get);
362 #endif
363
364 #if defined(CONFIG_NET_STATISTICS_IPV6)
365 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV6,
366 net_stats_get);
367 #endif
368
369 #if defined(CONFIG_NET_STATISTICS_IPV6_ND)
370 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV6_ND,
371 net_stats_get);
372 #endif
373
374 #if defined(CONFIG_NET_STATISTICS_IPV6_PMTU)
375 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV6_PMTU,
376 net_stats_get);
377 #endif
378
379 #if defined(CONFIG_NET_STATISTICS_IPV4_PMTU)
380 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IPV4_PMTU,
381 net_stats_get);
382 #endif
383
384 #if defined(CONFIG_NET_STATISTICS_ICMP)
385 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_ICMP,
386 net_stats_get);
387 #endif
388
389 #if defined(CONFIG_NET_STATISTICS_UDP)
390 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_UDP,
391 net_stats_get);
392 #endif
393
394 #if defined(CONFIG_NET_STATISTICS_TCP)
395 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_TCP,
396 net_stats_get);
397 #endif
398
399 #if defined(CONFIG_NET_STATISTICS_POWER_MANAGEMENT)
400 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_PM,
401 net_stats_get);
402 #endif
403
404 #endif /* CONFIG_NET_STATISTICS_USER_API */
405
net_stats_reset(struct net_if * iface)406 void net_stats_reset(struct net_if *iface)
407 {
408 if (iface) {
409 net_if_stats_reset(iface);
410 return;
411 }
412
413 net_if_stats_reset_all();
414 memset(&net_stats, 0, sizeof(net_stats));
415 }
416
417 #if defined(CONFIG_NET_STATISTICS_VIA_PROMETHEUS)
register_prometheus_metrics(struct net_if * iface)418 static void register_prometheus_metrics(struct net_if *iface)
419 {
420 int total_count = 0;
421
422 /* Find the correct collector for this interface */
423 STRUCT_SECTION_FOREACH(prometheus_collector, entry) {
424 if (entry->user_data == (void *)iface) {
425 iface->collector = entry;
426 break;
427 }
428 }
429
430 if (iface->collector == NULL) {
431 NET_DBG("No collector found for interface %d",
432 net_if_get_by_iface(iface));
433 return;
434 }
435
436 STRUCT_SECTION_FOREACH(prometheus_counter, entry) {
437 if (entry->base.collector != iface->collector) {
438 continue;
439 }
440
441 prometheus_collector_register_metric(iface->collector,
442 &entry->base);
443 total_count++;
444 }
445
446 STRUCT_SECTION_FOREACH(prometheus_gauge, entry) {
447 if (entry->base.collector != iface->collector) {
448 continue;
449 }
450
451 prometheus_collector_register_metric(iface->collector,
452 &entry->base);
453 total_count++;
454 }
455
456 STRUCT_SECTION_FOREACH(prometheus_summary, entry) {
457 if (entry->base.collector != iface->collector) {
458 continue;
459 }
460
461 prometheus_collector_register_metric(iface->collector,
462 &entry->base);
463 total_count++;
464 }
465
466 STRUCT_SECTION_FOREACH(prometheus_histogram, entry) {
467 if (entry->base.collector != iface->collector) {
468 continue;
469 }
470
471 prometheus_collector_register_metric(iface->collector,
472 &entry->base);
473 total_count++;
474 }
475
476 NET_DBG("Registered %d metrics for interface %d", total_count,
477 net_if_get_by_iface(iface));
478 }
479
480 /* Do not update metrics one by one as that would require searching
481 * each individual metric from the collector. Instead, let the
482 * Prometheus API scrape the data from net_stats stored in net_if when
483 * needed.
484 */
net_stats_prometheus_scrape(struct prometheus_collector * collector,struct prometheus_metric * metric,void * user_data)485 int net_stats_prometheus_scrape(struct prometheus_collector *collector,
486 struct prometheus_metric *metric,
487 void *user_data)
488 {
489 struct net_if *iface = user_data;
490 net_stats_t value;
491
492 if (!iface) {
493 return -EINVAL;
494 }
495
496 if (iface->collector != collector) {
497 return -EINVAL;
498 }
499
500 /* Update the metrics */
501 if (metric->type == PROMETHEUS_COUNTER) {
502 struct prometheus_counter *counter =
503 CONTAINER_OF(metric, struct prometheus_counter, base);
504
505 if (counter->user_data == NULL) {
506 return -EAGAIN;
507 }
508
509 value = *((net_stats_t *)counter->user_data);
510
511 prometheus_counter_set(counter, (uint64_t)value);
512
513 } else if (metric->type == PROMETHEUS_GAUGE) {
514 struct prometheus_gauge *gauge =
515 CONTAINER_OF(metric, struct prometheus_gauge, base);
516
517 if (gauge->user_data == NULL) {
518 return -EAGAIN;
519 }
520
521 value = *((net_stats_t *)gauge->user_data);
522
523 prometheus_gauge_set(gauge, (double)value);
524
525 } else if (metric->type == PROMETHEUS_HISTOGRAM) {
526 struct prometheus_histogram *histogram =
527 CONTAINER_OF(metric, struct prometheus_histogram, base);
528
529 if (histogram->user_data == NULL) {
530 return -EAGAIN;
531 }
532
533 } else if (metric->type == PROMETHEUS_SUMMARY) {
534 struct prometheus_summary *summary =
535 CONTAINER_OF(metric, struct prometheus_summary, base);
536
537 if (summary->user_data == NULL) {
538 return -EAGAIN;
539 }
540
541 if (IS_ENABLED(CONFIG_NET_PKT_TXTIME_STATS) &&
542 strstr(metric->name, "_tx_time_summary") == 0) {
543 IF_ENABLED(CONFIG_NET_PKT_TXTIME_STATS,
544 (struct net_stats_tx_time *tx_time =
545 (struct net_stats_tx_time *)summary->user_data;
546
547 prometheus_summary_observe_set(
548 summary,
549 (double)tx_time->sum,
550 (unsigned long)tx_time->count)));
551 } else if (IS_ENABLED(CONFIG_NET_PKT_RXTIME_STATS) &&
552 strstr(metric->name, "_rx_time_summary") == 0) {
553 IF_ENABLED(CONFIG_NET_PKT_RXTIME_STATS,
554 (struct net_stats_rx_time *rx_time =
555 (struct net_stats_rx_time *)summary->user_data;
556
557 prometheus_summary_observe_set(
558 summary,
559 (double)rx_time->sum,
560 (unsigned long)rx_time->count)));
561 }
562 } else {
563 NET_DBG("Unknown metric type %d", metric->type);
564 }
565
566 return 0;
567 }
568
net_stats_prometheus_init(struct net_if * iface)569 void net_stats_prometheus_init(struct net_if *iface)
570 {
571 register_prometheus_metrics(iface);
572 }
573 #endif /* CONFIG_NET_STATISTICS_VIA_PROMETHEUS */
574