]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/port.c
networkd: Collect stats regulary and emit them on dbus
[people/ms/network.git] / src / networkd / port.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 <limits.h>
22 #include <net/if.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25
26 #include <systemd/sd-bus.h>
27
28 #include "address.h"
29 #include "config.h"
30 #include "link.h"
31 #include "logging.h"
32 #include "port.h"
33 #include "stats-collector.h"
34 #include "string.h"
35
36 struct nw_port {
37 nw_daemon* daemon;
38 int nrefs;
39
40 // Link
41 nw_link* link;
42
43 char name[IF_NAMESIZE];
44 nw_port_type_t type;
45
46 // Configuration
47 nw_config *config;
48
49 // Common attributes
50 nw_address_t address;
51 };
52
53 static const struct nw_port_type_map {
54 nw_port_type_t type;
55 const char* name;
56 } nw_port_type_map[] = {
57 { NW_PORT_DUMMY, "dummy" },
58 { NW_PORT_UNKNOWN, NULL },
59 };
60
61 static nw_port_type_t nw_port_type_from_string(const char* s) {
62 const struct nw_port_type_map* map = NULL;
63
64 for (map = nw_port_type_map; *map->name; map++) {
65 if (strcmp(map->name, s) == 0)
66 return map->type;
67 }
68
69 return NW_PORT_UNKNOWN;
70 }
71
72 static void nw_port_free(nw_port* port) {
73 if (port->link)
74 nw_link_unref(port->link);
75 if (port->config)
76 nw_config_unref(port->config);
77 if (port->daemon)
78 nw_daemon_unref(port->daemon);
79
80 free(port);
81 }
82
83 static int nw_port_setup_address(nw_port* port) {
84 int r;
85
86 // Read ADDRESS from configuration
87 const char* s = nw_config_get(port->config, "ADDRESS");
88 if (!s) {
89 ERROR("Port %s: Address isn't set\n", port->name);
90 goto ERROR;
91 }
92
93 // Parse the address
94 r = nw_address_from_string(&port->address, s);
95 if (r) {
96 ERROR("Port %s: Could not parse address: %m\n", port->name);
97 goto ERROR;
98 }
99
100 // Check if this address is usable
101 r = nw_address_is_multicast(&port->address);
102 if (r) {
103 DEBUG("Port %s: Multicast bit is set on Ethernet address\n", port->name);
104 goto ERROR;
105 }
106
107 return 0;
108
109 ERROR:
110 // Generate a random Ethernet address
111 r = nw_address_generate(&port->address);
112 if (r) {
113 ERROR("Could not generate a random Ethernet address: %m\n");
114 return r;
115 }
116
117 return 0;
118 }
119
120 static int nw_port_setup_common(nw_port* port) {
121 int r;
122
123 // Address
124 r = nw_port_setup_address(port);
125 if (r)
126 return r;
127
128 return 0;
129 }
130
131 static nw_port_type_t nw_port_setup_type(nw_port* port) {
132 const char* type = nw_config_get(port->config, "TYPE");
133 if (!type)
134 return NW_PORT_UNKNOWN;
135
136 return nw_port_type_from_string(type);
137 }
138
139 static int nw_port_set_link(nw_port* port, nw_link* link) {
140 // Do nothing if the same link is being re-assigned
141 if (port->link == link)
142 return 0;
143
144 // Dereference the former link if set
145 if (port->link)
146 nw_link_unref(port->link);
147
148 // Store the new link
149 if (link) {
150 port->link = nw_link_ref(link);
151
152 DEBUG("Port %s: Assigned link %d\n", port->name, nw_link_ifindex(port->link));
153
154 // Or clear the pointer if no link has been provided
155 } else {
156 port->link = NULL;
157
158 DEBUG("Port %s: Removed link\n", port->name);
159 }
160
161 return 0;
162 }
163
164 static int nw_port_setup(nw_port* port) {
165 nw_link* link = NULL;
166 char path[PATH_MAX];
167 int r;
168
169 // Find the link
170 link = nw_daemon_get_link_by_name(port->daemon, port->name);
171 if (link) {
172 r = nw_port_set_link(port, link);
173 if (r)
174 goto ERROR;
175 }
176
177 // Compose the path to the main configuration file
178 r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
179 if (r)
180 goto ERROR;
181
182 // Initialize the configuration
183 r = nw_config_create(&port->config, path);
184 if (r)
185 goto ERROR;
186
187 // Determine type
188 port->type = nw_port_setup_type(port);
189 if (!port->type) {
190 ERROR("Could not determine type of port %s\n", port->name);
191 goto ERROR;
192 }
193
194 // Perform some common initialization
195 r = nw_port_setup_common(port);
196 if (r)
197 goto ERROR;
198
199 // Call any custom initialization
200 switch (port->type) {
201 // These do not need any special initialization
202 case NW_PORT_DUMMY:
203 case NW_PORT_UNKNOWN:
204 break;
205 }
206
207 ERROR:
208 if (link)
209 nw_link_unref(link);
210
211 return r;
212 }
213
214 int nw_port_create(nw_port** port, nw_daemon* daemon, const char* name) {
215 int r;
216
217 // Allocate a new object
218 nw_port* p = calloc(1, sizeof(*p));
219 if (!p)
220 return 1;
221
222 // Store a reference to the daemon
223 p->daemon = nw_daemon_ref(daemon);
224
225 // Initialize reference counter
226 p->nrefs = 1;
227
228 // Store the name
229 r = nw_string_set(p->name, name);
230 if (r)
231 goto ERROR;
232
233 // Setup the port
234 r = nw_port_setup(p);
235 if (r)
236 goto ERROR;
237
238 *port = p;
239 return 0;
240
241 ERROR:
242 nw_port_free(p);
243 return r;
244 }
245
246 nw_port* nw_port_ref(nw_port* port) {
247 port->nrefs++;
248
249 return port;
250 }
251
252 nw_port* nw_port_unref(nw_port* port) {
253 if (--port->nrefs > 0)
254 return port;
255
256 nw_port_free(port);
257 return NULL;
258 }
259
260 int nw_port_save(nw_port* port) {
261 int r;
262
263 r = nw_config_write(port->config);
264 if (r)
265 return r;
266
267 return 0;
268 }
269
270 const char* nw_port_name(nw_port* port) {
271 return port->name;
272 }
273
274 char* nw_port_bus_path(nw_port* port) {
275 char* p = NULL;
276 int r;
277
278 // Encode the bus path
279 r = sd_bus_path_encode("/org/ipfire/network1/port", port->name, &p);
280 if (r < 0)
281 return NULL;
282
283 return p;
284 }
285
286 int __nw_port_set_link(nw_daemon* daemon, nw_port* port, void* data) {
287 nw_link* link = (nw_link*)data;
288
289 // Fetch the link name
290 const char* ifname = nw_link_ifname(link);
291 if (!ifname) {
292 ERROR("Link does not have a name set\n");
293 return 1;
294 }
295
296 // Set link if the name matches
297 if (strcmp(port->name, ifname) == 0)
298 return nw_port_set_link(port, link);
299
300 // If we have the link set but the name did not match, we will remove it
301 else if (port->link == link)
302 return nw_port_set_link(port, NULL);
303
304 return 0;
305 }
306
307 int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) {
308 nw_link* link = (nw_link*)data;
309
310 // Drop the link if it matches
311 if (port->link == link)
312 return nw_port_set_link(port, NULL);
313
314 return 0;
315 }
316
317 const nw_address_t* nw_port_get_address(nw_port* port) {
318 return &port->address;
319 }
320
321 static int nw_port_is_disabled(nw_port* port) {
322 return nw_config_get_bool(port->config, "DISABLED");
323 }
324
325 static int nw_port_create_link(nw_port* port) {
326 return 0; // XXX TODO
327 }
328
329 int nw_port_reconfigure(nw_port* port) {
330 int r;
331
332 // If the port is disabled, we will try to destroy it
333 if (nw_port_is_disabled(port)) {
334 if (port->link) {
335 r = nw_link_destroy(port->link);
336 if (r)
337 return r;
338 }
339
340 return 0;
341 }
342
343 // If there is no link, we will try to create it
344 if (!port->link) {
345 r = nw_port_create_link(port);
346 if (r)
347 return r;
348 }
349
350 // XXX TODO
351
352 return 0;
353 }
354
355 int nw_port_has_carrier(nw_port* port) {
356 if (!port->link)
357 return 0;
358
359 return nw_link_has_carrier(port->link);
360 }
361
362 /*
363 Stats
364 */
365
366 const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port) {
367 if (!port->link)
368 return NULL;
369
370 return nw_link_get_stats64(port->link);
371 }
372
373 int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data) {
374 nw_link* link = (nw_link*)data;
375
376 // Emit stats if link matches
377 if (port->link == link)
378 return nw_stats_collector_emit_port_stats(daemon, port);
379
380 return 0;
381 }
382
383 int nw_port_update_stats(nw_port* port) {
384 if (port->link)
385 return nw_link_update_stats(port->link);
386
387 return 0;
388 }