]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/port.c
ports: Require type to be set at all times
[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 int nw_port_set_link(nw_port* port, nw_link* link) {
132 // Do nothing if the same link is being re-assigned
133 if (port->link == link)
134 return 0;
135
136 // Dereference the former link if set
137 if (port->link)
138 nw_link_unref(port->link);
139
140 // Store the new link
141 if (link) {
142 port->link = nw_link_ref(link);
143
144 DEBUG("Port %s: Assigned link %d\n", port->name, nw_link_ifindex(port->link));
145
146 // Or clear the pointer if no link has been provided
147 } else {
148 port->link = NULL;
149
150 DEBUG("Port %s: Removed link\n", port->name);
151 }
152
153 return 0;
154 }
155
156 static int nw_port_setup(nw_port* port) {
157 nw_link* link = NULL;
158 char path[PATH_MAX];
159 int r;
160
161 // Find the link
162 link = nw_daemon_get_link_by_name(port->daemon, port->name);
163 if (link) {
164 r = nw_port_set_link(port, link);
165 if (r)
166 goto ERROR;
167 }
168
169 // Compose the path to the main configuration file
170 r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
171 if (r)
172 goto ERROR;
173
174 // Initialize the configuration
175 r = nw_config_create(&port->config, path);
176 if (r)
177 goto ERROR;
178
179 // Perform some common initialization
180 r = nw_port_setup_common(port);
181 if (r)
182 goto ERROR;
183
184 // Call any custom initialization
185 switch (port->type) {
186 // These do not need any special initialization
187 case NW_PORT_DUMMY:
188 case NW_PORT_UNKNOWN:
189 break;
190 }
191
192 ERROR:
193 if (link)
194 nw_link_unref(link);
195
196 return r;
197 }
198
199 int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const char* name) {
200 int r;
201
202 // Allocate a new object
203 nw_port* p = calloc(1, sizeof(*p));
204 if (!p)
205 return 1;
206
207 // Store a reference to the daemon
208 p->daemon = nw_daemon_ref(daemon);
209
210 // Initialize reference counter
211 p->nrefs = 1;
212
213 // Store the type
214 p->type = type;
215
216 // Store the name
217 r = nw_string_set(p->name, name);
218 if (r)
219 goto ERROR;
220
221 // Setup the port
222 r = nw_port_setup(p);
223 if (r)
224 goto ERROR;
225
226 *port = p;
227 return 0;
228
229 ERROR:
230 nw_port_free(p);
231 return r;
232 }
233
234 int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
235 const char* name, const char* path) {
236 nw_config* config = NULL;
237 int r;
238
239 // Initialize the configuration
240 r = nw_config_create(&config, path);
241 if (r)
242 goto ERROR;
243
244 // Fetch the type
245 const char* type = nw_config_get(config, "TYPE");
246 if (!type) {
247 ERROR("Port configuration %s has no TYPE\n", path);
248 r = 1;
249 goto ERROR;
250 }
251
252 // Create a new port
253 r = nw_port_create(port, daemon, nw_port_type_from_string(type), name);
254 if (r)
255 goto ERROR;
256
257 ERROR:
258 if (config)
259 nw_config_unref(config);
260
261 return r;
262 }
263
264 nw_port* nw_port_ref(nw_port* port) {
265 port->nrefs++;
266
267 return port;
268 }
269
270 nw_port* nw_port_unref(nw_port* port) {
271 if (--port->nrefs > 0)
272 return port;
273
274 nw_port_free(port);
275 return NULL;
276 }
277
278 int nw_port_save(nw_port* port) {
279 int r;
280
281 r = nw_config_write(port->config);
282 if (r)
283 return r;
284
285 return 0;
286 }
287
288 const char* nw_port_name(nw_port* port) {
289 return port->name;
290 }
291
292 char* nw_port_bus_path(nw_port* port) {
293 char* p = NULL;
294 int r;
295
296 // Encode the bus path
297 r = sd_bus_path_encode("/org/ipfire/network1/port", port->name, &p);
298 if (r < 0)
299 return NULL;
300
301 return p;
302 }
303
304 int __nw_port_set_link(nw_daemon* daemon, nw_port* port, void* data) {
305 nw_link* link = (nw_link*)data;
306
307 // Fetch the link name
308 const char* ifname = nw_link_ifname(link);
309 if (!ifname) {
310 ERROR("Link does not have a name set\n");
311 return 1;
312 }
313
314 // Set link if the name matches
315 if (strcmp(port->name, ifname) == 0)
316 return nw_port_set_link(port, link);
317
318 // If we have the link set but the name did not match, we will remove it
319 else if (port->link == link)
320 return nw_port_set_link(port, NULL);
321
322 return 0;
323 }
324
325 int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) {
326 nw_link* link = (nw_link*)data;
327
328 // Drop the link if it matches
329 if (port->link == link)
330 return nw_port_set_link(port, NULL);
331
332 return 0;
333 }
334
335 const nw_address_t* nw_port_get_address(nw_port* port) {
336 return &port->address;
337 }
338
339 static int nw_port_is_disabled(nw_port* port) {
340 return nw_config_get_bool(port->config, "DISABLED");
341 }
342
343 static int nw_port_create_link(nw_port* port) {
344 return 0; // XXX TODO
345 }
346
347 int nw_port_reconfigure(nw_port* port) {
348 int r;
349
350 // If the port is disabled, we will try to destroy it
351 if (nw_port_is_disabled(port)) {
352 if (port->link) {
353 r = nw_link_destroy(port->link);
354 if (r)
355 return r;
356 }
357
358 return 0;
359 }
360
361 // If there is no link, we will try to create it
362 if (!port->link) {
363 r = nw_port_create_link(port);
364 if (r)
365 return r;
366 }
367
368 // XXX TODO
369
370 return 0;
371 }
372
373 int nw_port_has_carrier(nw_port* port) {
374 if (!port->link)
375 return 0;
376
377 return nw_link_has_carrier(port->link);
378 }
379
380 /*
381 Stats
382 */
383
384 const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port) {
385 if (!port->link)
386 return NULL;
387
388 return nw_link_get_stats64(port->link);
389 }
390
391 int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data) {
392 nw_link* link = (nw_link*)data;
393
394 // Emit stats if link matches
395 if (port->link == link)
396 return nw_stats_collector_emit_port_stats(daemon, port);
397
398 return 0;
399 }
400
401 int nw_port_update_stats(nw_port* port) {
402 if (port->link)
403 return nw_link_update_stats(port->link);
404
405 return 0;
406 }