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