]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/stats-collector.c
Makefile: Fix typo in localstatedir
[people/ms/network.git] / src / networkd / stats-collector.c
1 /*#############################################################################
2 # #
3 # IPFire.org - A linux based firewall #
4 # Copyright (C) 2023 IPFire Network Development Team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <stdlib.h>
22
23 #include <systemd/sd-bus.h>
24 #include <systemd/sd-event.h>
25
26 #include "logging.h"
27 #include "port.h"
28 #include "stats-collector.h"
29 #include "zone.h"
30
31 static int __nw_stats_collector_port(nw_daemon* daemon, nw_port* port, void* data) {
32 return nw_port_update_stats(port);
33 }
34
35 static int __nw_stats_collector_zone(nw_daemon* daemon, nw_zone* zone, void* data) {
36 return nw_zone_update_stats(zone);
37 }
38
39 int nw_stats_collector(sd_event_source* s, long unsigned int usec, void* data) {
40 nw_daemon* daemon = (nw_daemon*)data;
41 int r;
42
43 DEBUG("Stats collector has been called\n");
44
45 // Schedule the next call
46 r = sd_event_source_set_time(s, usec + NW_STATS_COLLECTOR_INTERVAL);
47 if (r < 0)
48 return r;
49
50 // Ports
51 r = nw_daemon_ports_walk(daemon, __nw_stats_collector_port, NULL);
52 if (r)
53 return r;
54
55 // Zones
56 r = nw_daemon_zones_walk(daemon, __nw_stats_collector_zone, NULL);
57 if (r)
58 return r;
59
60 return 0;
61 }
62
63 static int nw_stats_collector_emit_stats(nw_daemon* daemon, const char* path,
64 const char* interface, const char* member, const struct rtnl_link_stats64* stats64) {
65 sd_bus_message* m = NULL;
66 int r;
67
68 sd_bus* bus = nw_daemon_get_bus(daemon);
69
70 // Allocate a new message
71 r = sd_bus_message_new_signal(bus, &m, path, interface, member);
72 if (r < 0) {
73 errno = -r;
74 ERROR("Could not allocate bus message: %m\n");
75 goto ERROR;
76 }
77
78 // Open the container
79 r = sd_bus_message_open_container(m, 'a', "{st}");
80 if (r < 0) {
81 ERROR("Could not open container: %m\n");
82 goto ERROR;
83 }
84
85 const struct stats64_entry {
86 const char* key;
87 uint64_t value;
88 } entries[] = {
89 { "rx-packets", stats64->rx_packets },
90 { "tx-packets", stats64->tx_packets },
91 { "rx-bytes", stats64->rx_bytes },
92 { "tx-bytes", stats64->tx_bytes },
93 { "rx-errors", stats64->rx_errors },
94 { "tx-errors", stats64->tx_errors },
95 { "rx-dropped", stats64->rx_dropped },
96 { "tx-dropped", stats64->tx_dropped },
97 { "multicast", stats64->multicast },
98 { "collisions", stats64->collisions },
99
100 // Detailed RX errors
101 { "rx-length-errors", stats64->rx_length_errors },
102 { "rx-over-errors", stats64->rx_over_errors },
103 { "rx-crc-errors", stats64->rx_crc_errors },
104 { "rx-frame-errors", stats64->rx_frame_errors },
105 { "rx-fifo-errors", stats64->rx_fifo_errors },
106 { "rx-missed-errors", stats64->rx_missed_errors },
107
108 // Detailed TX errors
109 { "tx-aborted-errors", stats64->tx_aborted_errors },
110 { "tx-carrier-errors", stats64->tx_carrier_errors },
111 { "tx-fifo-errors", stats64->tx_fifo_errors },
112 { "tx-heartbeat-errors", stats64->tx_heartbeat_errors },
113 { "tx-window-errors", stats64->tx_window_errors },
114
115 { NULL },
116 };
117
118 for (const struct stats64_entry* e = entries; e->key; e++) {
119 r = sd_bus_message_append(m, "{st}", e->key, e->value);
120 if (r < 0) {
121 ERROR("Could not set stat value: %m\n");
122 goto ERROR;
123 }
124 }
125
126 // Close the container
127 r = sd_bus_message_close_container(m);
128 if (r < 0) {
129 ERROR("Could not close container: %m\n");
130 goto ERROR;
131 }
132
133 // Emit the signal
134 r = sd_bus_send(bus, m, NULL);
135 if (r < 0) {
136 ERROR("Could not emit the stats signal for %s: %m\n", path);
137 goto ERROR;
138 }
139
140 ERROR:
141 if (m)
142 sd_bus_message_unref(m);
143
144 return r;
145 }
146
147 int nw_stats_collector_emit_port_stats(nw_daemon* daemon, nw_port* port) {
148 const struct rtnl_link_stats64* stats64 = NULL;
149 char* path = NULL;
150 int r;
151
152 // Fetch the bus path
153 path = nw_port_bus_path(port);
154
155 // Fetch the stats
156 stats64 = nw_port_get_stats64(port);
157
158 // Emit the stats
159 r = nw_stats_collector_emit_stats(daemon, path,
160 "org.ipfire.network1.Port", "Stats", stats64);
161 if (r < 0) {
162 ERROR("Could not emit stats for port %s: %m\n", nw_port_name(port));
163 goto ERROR;
164 }
165
166 ERROR:
167 if (path)
168 free(path);
169
170 return r;
171 }
172
173 int nw_stats_collector_emit_zone_stats(nw_daemon* daemon, nw_zone* zone) {
174 const struct rtnl_link_stats64* stats64 = NULL;
175 char* path = NULL;
176 int r;
177
178 // Fetch the bus path
179 path = nw_zone_bus_path(zone);
180
181 // Fetch the stats
182 stats64 = nw_zone_get_stats64(zone);
183
184 // Emit the stats
185 r = nw_stats_collector_emit_stats(daemon, path,
186 "org.ipfire.network1.Zone", "Stats", stats64);
187 if (r < 0) {
188 ERROR("Could not emit stats for zone %s: %m\n", nw_zone_name(zone));
189 goto ERROR;
190 }
191
192 ERROR:
193 if (path)
194 free(path);
195
196 return r;
197 }