]> git.ipfire.org Git - people/ms/network.git/blame - src/networkd/port.c
ports: Implement destroying a port
[people/ms/network.git] / src / networkd / port.c
CommitLineData
abeab069
MT
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
827435c8 21#include <limits.h>
abeab069 22#include <net/if.h>
2e7f4705 23#include <stdint.h>
abeab069
MT
24#include <stdlib.h>
25
7297ba7f
MT
26#include <systemd/sd-bus.h>
27
2e7f4705 28#include "address.h"
abeab069 29#include "config.h"
30d4ab67 30#include "link.h"
827435c8 31#include "logging.h"
abeab069 32#include "port.h"
15240e08
MT
33#include "stats-collector.h"
34#include "string.h"
abeab069
MT
35
36struct nw_port {
2361667e 37 nw_daemon* daemon;
abeab069
MT
38 int nrefs;
39
02801f0d
MT
40 // Link
41 nw_link* link;
42
827435c8 43 nw_port_type_t type;
c7761af8 44 char name[IF_NAMESIZE];
abeab069
MT
45
46 // Configuration
2361667e 47 nw_config *config;
2e7f4705
MT
48
49 // Common attributes
50 nw_address_t address;
c7761af8
MT
51
52 // VLAN settings
53 struct nw_port_vlan {
54 nw_port* parent;
55 int id;
56
57 // If the parent has not been read from the configuration we will
58 // save the name and try to find it later.
59 char __parent_name[IF_NAMESIZE];
60 } vlan;
abeab069
MT
61};
62
827435c8
MT
63static const struct nw_port_type_map {
64 nw_port_type_t type;
65 const char* name;
66} nw_port_type_map[] = {
c7761af8
MT
67 { NW_PORT_DUMMY, "dummy" },
68 { NW_PORT_VLAN, "vlan" },
827435c8
MT
69 { NW_PORT_UNKNOWN, NULL },
70};
71
72static nw_port_type_t nw_port_type_from_string(const char* s) {
73 const struct nw_port_type_map* map = NULL;
74
75 for (map = nw_port_type_map; *map->name; map++) {
76 if (strcmp(map->name, s) == 0)
77 return map->type;
78 }
79
80 return NW_PORT_UNKNOWN;
81}
82
2361667e 83static void nw_port_free(nw_port* port) {
02801f0d
MT
84 if (port->link)
85 nw_link_unref(port->link);
abeab069
MT
86 if (port->config)
87 nw_config_unref(port->config);
30d4ab67
MT
88 if (port->daemon)
89 nw_daemon_unref(port->daemon);
abeab069
MT
90
91 free(port);
92}
93
2361667e 94static int nw_port_setup_address(nw_port* port) {
2e7f4705
MT
95 int r;
96
97 // Read ADDRESS from configuration
98 const char* s = nw_config_get(port->config, "ADDRESS");
99 if (!s) {
100 ERROR("Port %s: Address isn't set\n", port->name);
9a93da54 101 goto ERROR;
2e7f4705
MT
102 }
103
104 // Parse the address
105 r = nw_address_from_string(&port->address, s);
106 if (r) {
107 ERROR("Port %s: Could not parse address: %m\n", port->name);
9a93da54 108 goto ERROR;
2e7f4705
MT
109 }
110
d81d9cf5
MT
111 // Check if this address is usable
112 r = nw_address_is_multicast(&port->address);
113 if (r) {
114 DEBUG("Port %s: Multicast bit is set on Ethernet address\n", port->name);
115 goto ERROR;
116 }
2e7f4705 117
9a93da54
MT
118 return 0;
119
120ERROR:
121 // Generate a random Ethernet address
122 r = nw_address_generate(&port->address);
123 if (r) {
124 ERROR("Could not generate a random Ethernet address: %m\n");
125 return r;
126 }
127
2e7f4705
MT
128 return 0;
129}
130
2361667e 131static int nw_port_setup_common(nw_port* port) {
2e7f4705
MT
132 int r;
133
134 // Address
135 r = nw_port_setup_address(port);
136 if (r)
137 return r;
138
139 return 0;
827435c8
MT
140}
141
c7761af8
MT
142static int nw_port_setup_vlan(nw_port* port) {
143 int r;
144
145 // VLAN ID
146 int id = nw_config_get_int(port->config, "VLAN_ID", NW_VLAN_ID_INVALID);
147 if (id) {
148 r = nw_port_set_vlan_id(port, id);
149 if (r)
150 return r;
151 }
152
153 // Parent Port
154 const char* parent = nw_config_get(port->config, "VLAN_PARENT");
155 if (parent) {
156 r = nw_string_set(port->vlan.__parent_name, parent);
157 if (r)
158 return r;
159 }
160
161 return 0;
162}
163
611d4aca
MT
164static int nw_port_set_link(nw_port* port, nw_link* link) {
165 // Do nothing if the same link is being re-assigned
166 if (port->link == link)
167 return 0;
168
169 // Dereference the former link if set
170 if (port->link)
171 nw_link_unref(port->link);
172
173 // Store the new link
174 if (link) {
175 port->link = nw_link_ref(link);
176
177 DEBUG("Port %s: Assigned link %d\n", port->name, nw_link_ifindex(port->link));
178
179 // Or clear the pointer if no link has been provided
180 } else {
181 port->link = NULL;
182
183 DEBUG("Port %s: Removed link\n", port->name);
184 }
185
186 return 0;
187}
188
2361667e 189static int nw_port_setup(nw_port* port) {
611d4aca 190 nw_link* link = NULL;
827435c8
MT
191 char path[PATH_MAX];
192 int r;
193
02801f0d 194 // Find the link
611d4aca
MT
195 link = nw_daemon_get_link_by_name(port->daemon, port->name);
196 if (link) {
197 r = nw_port_set_link(port, link);
198 if (r)
199 goto ERROR;
02801f0d
MT
200 }
201
827435c8
MT
202 // Compose the path to the main configuration file
203 r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
204 if (r)
611d4aca 205 goto ERROR;
827435c8
MT
206
207 // Initialize the configuration
208 r = nw_config_create(&port->config, path);
209 if (r)
611d4aca 210 goto ERROR;
827435c8 211
827435c8
MT
212 // Perform some common initialization
213 r = nw_port_setup_common(port);
214 if (r)
611d4aca 215 goto ERROR;
827435c8
MT
216
217 // Call any custom initialization
218 switch (port->type) {
c7761af8
MT
219 case NW_PORT_VLAN:
220 r = nw_port_setup_vlan(port);
221 if (r)
222 goto ERROR;
223 break;
224
827435c8
MT
225 // These do not need any special initialization
226 case NW_PORT_DUMMY:
227 case NW_PORT_UNKNOWN:
228 break;
229 }
230
611d4aca
MT
231ERROR:
232 if (link)
233 nw_link_unref(link);
234
235 return r;
827435c8
MT
236}
237
06bc93d3 238int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const char* name) {
abeab069
MT
239 int r;
240
241 // Allocate a new object
2361667e 242 nw_port* p = calloc(1, sizeof(*p));
abeab069
MT
243 if (!p)
244 return 1;
245
30d4ab67
MT
246 // Store a reference to the daemon
247 p->daemon = nw_daemon_ref(daemon);
248
abeab069
MT
249 // Initialize reference counter
250 p->nrefs = 1;
251
06bc93d3
MT
252 // Store the type
253 p->type = type;
254
abeab069
MT
255 // Store the name
256 r = nw_string_set(p->name, name);
257 if (r)
258 goto ERROR;
259
827435c8
MT
260 // Setup the port
261 r = nw_port_setup(p);
262 if (r)
263 goto ERROR;
264
abeab069
MT
265 *port = p;
266 return 0;
267
268ERROR:
269 nw_port_free(p);
270 return r;
271}
272
06bc93d3
MT
273int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
274 const char* name, const char* path) {
275 nw_config* config = NULL;
276 int r;
277
278 // Initialize the configuration
279 r = nw_config_create(&config, path);
280 if (r)
281 goto ERROR;
282
283 // Fetch the type
284 const char* type = nw_config_get(config, "TYPE");
285 if (!type) {
286 ERROR("Port configuration %s has no TYPE\n", path);
287 r = 1;
288 goto ERROR;
289 }
290
291 // Create a new port
292 r = nw_port_create(port, daemon, nw_port_type_from_string(type), name);
293 if (r)
294 goto ERROR;
295
296ERROR:
297 if (config)
298 nw_config_unref(config);
299
300 return r;
301}
302
2361667e 303nw_port* nw_port_ref(nw_port* port) {
abeab069
MT
304 port->nrefs++;
305
306 return port;
307}
308
2361667e 309nw_port* nw_port_unref(nw_port* port) {
abeab069
MT
310 if (--port->nrefs > 0)
311 return port;
312
313 nw_port_free(port);
314 return NULL;
315}
316
c403eb4c
MT
317int nw_port_destroy(nw_port* port) {
318 int r;
319
320 DEBUG("Destroying port %s\n", port->name);
321
322 // Destroy the physical link (if exists)
323 if (port->link) {
324 r = nw_link_destroy(port->link);
325 if (r)
326 return r;
327 }
328
329 // Dereference the port from other ports
330 r = nw_daemon_ports_walk(port->daemon, __nw_port_drop_port, port);
331 if (r)
332 return r;
333
334 // Dereference the port from other zones
335 r = nw_daemon_zones_walk(port->daemon, __nw_zone_drop_port, port);
336 if (r)
337 return r;
338
339 // Destroy the configuration
340 r = nw_config_destroy(port->config);
341 if (r)
342 return r;
343
344 // Reset type
345 port->type = NW_PORT_UNKNOWN;
346
347 return 0;
348}
349
350int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
351 nw_port* dropped_port = (nw_port*)data;
352 int r;
353
354 switch (port->type) {
355 case NW_PORT_VLAN:
356 if (port->vlan.parent == dropped_port) {
357 r = nw_port_set_vlan_parent(port, NULL);
358 if (r)
359 return r;
360 }
361 break;
362
363 case NW_PORT_DUMMY:
364 case NW_PORT_UNKNOWN:
365 break;
366 }
367
368 return 0;
369}
370
c7761af8
MT
371static int nw_port_save_vlan(nw_port* port) {
372 int r;
373
374 // VLAN ID
375 r = nw_config_set_int(port->config, "VLAN_ID", port->vlan.id);
376 if (r)
377 return r;
378
379 // Parent Port
380 r = nw_config_set(port->config, "VLAN_PARENT",
381 (port->vlan.parent) ? nw_port_name(port->vlan.parent) : port->vlan.__parent_name);
382 if (r)
383 return r;
384
385 return 0;
386}
387
605e975f
MT
388int nw_port_save(nw_port* port) {
389 int r;
390
c7761af8
MT
391 switch (port->type) {
392 // VLAN
393 case NW_PORT_VLAN:
394 r = nw_port_save_vlan(port);
395 if (r)
396 goto ERROR;
397 break;
398
399 // These types do not have any special settings
400 case NW_PORT_DUMMY:
401 case NW_PORT_UNKNOWN:
402 break;
403 }
404
605e975f
MT
405 r = nw_config_write(port->config);
406 if (r)
407 return r;
408
409 return 0;
c7761af8
MT
410
411ERROR:
412 ERROR("Could not save configuration for port %s: %m\n", port->name);
413
414 return 1;
605e975f
MT
415}
416
2361667e 417const char* nw_port_name(nw_port* port) {
abeab069
MT
418 return port->name;
419}
7297ba7f 420
2361667e 421char* nw_port_bus_path(nw_port* port) {
7297ba7f
MT
422 char* p = NULL;
423 int r;
424
425 // Encode the bus path
426 r = sd_bus_path_encode("/org/ipfire/network1/port", port->name, &p);
427 if (r < 0)
428 return NULL;
429
430 return p;
431}
4a383286 432
611d4aca
MT
433int __nw_port_set_link(nw_daemon* daemon, nw_port* port, void* data) {
434 nw_link* link = (nw_link*)data;
435
436 // Fetch the link name
437 const char* ifname = nw_link_ifname(link);
438 if (!ifname) {
439 ERROR("Link does not have a name set\n");
440 return 1;
441 }
442
443 // Set link if the name matches
444 if (strcmp(port->name, ifname) == 0)
445 return nw_port_set_link(port, link);
446
447 // If we have the link set but the name did not match, we will remove it
448 else if (port->link == link)
449 return nw_port_set_link(port, NULL);
450
451 return 0;
452}
453
454int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) {
455 nw_link* link = (nw_link*)data;
02801f0d 456
611d4aca
MT
457 // Drop the link if it matches
458 if (port->link == link)
459 return nw_port_set_link(port, NULL);
460
461 return 0;
30d4ab67
MT
462}
463
2361667e 464const nw_address_t* nw_port_get_address(nw_port* port) {
4a383286
MT
465 return &port->address;
466}
20375a08 467
371b836a
MT
468static int nw_port_is_disabled(nw_port* port) {
469 return nw_config_get_bool(port->config, "DISABLED");
470}
471
96b1b84d
MT
472static int nw_port_create_link(nw_port* port) {
473 return 0; // XXX TODO
474}
475
b9769b09 476int nw_port_reconfigure(nw_port* port) {
371b836a
MT
477 int r;
478
479 // If the port is disabled, we will try to destroy it
480 if (nw_port_is_disabled(port)) {
481 if (port->link) {
482 r = nw_link_destroy(port->link);
483 if (r)
484 return r;
485 }
486
487 return 0;
488 }
489
96b1b84d
MT
490 // If there is no link, we will try to create it
491 if (!port->link) {
492 r = nw_port_create_link(port);
493 if (r)
494 return r;
495 }
496
371b836a 497 // XXX TODO
96b1b84d
MT
498
499 return 0;
b9769b09
MT
500}
501
20375a08 502int nw_port_has_carrier(nw_port* port) {
02801f0d
MT
503 if (!port->link)
504 return 0;
20375a08 505
02801f0d 506 return nw_link_has_carrier(port->link);
20375a08 507}
15240e08
MT
508
509/*
510 Stats
511*/
512
513const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port) {
514 if (!port->link)
515 return NULL;
516
517 return nw_link_get_stats64(port->link);
518}
519
520int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data) {
521 nw_link* link = (nw_link*)data;
522
523 // Emit stats if link matches
524 if (port->link == link)
525 return nw_stats_collector_emit_port_stats(daemon, port);
526
527 return 0;
528}
529
530int nw_port_update_stats(nw_port* port) {
531 if (port->link)
532 return nw_link_update_stats(port->link);
533
534 return 0;
535}
c7761af8
MT
536
537static int nw_port_check_type(nw_port* port, const nw_port_type_t type) {
538 if (port->type == type)
539 return 0;
540
541 errno = ENOTSUP;
542 return -errno;
543}
544
545/*
546 VLAN
547*/
548int nw_port_get_vlan_id(nw_port* port) {
549 int r;
550
551 // Check type
552 r = nw_port_check_type(port, NW_PORT_VLAN);
553 if (r < 0)
554 return r;
555
556 return port->vlan.id;
557}
558
559int nw_port_set_vlan_id(nw_port* port, int id) {
560 int r;
561
562 // Check type
563 r = nw_port_check_type(port, NW_PORT_VLAN);
564 if (r < 0)
565 return r;
566
567 // Check if the VLAN ID is within range
568 if (id < NW_VLAN_ID_MIN || id > NW_VLAN_ID_MAX)
569 return -EINVAL;
570
571 // Store the ID
572 port->vlan.id = id;
573
574 DEBUG("Port %s: Set VLAN ID to %d\n", port->name, port->vlan.id);
575
576 return 0;
577}
578
579nw_port* nw_port_get_vlan_parent(nw_port* port) {
580 int r;
581
582 // Check type
583 r = nw_port_check_type(port, NW_PORT_VLAN);
584 if (r < 0)
585 return NULL;
586
587 // Try to find a reference to the parent if none exists
588 if (!port->vlan.parent && *port->vlan.__parent_name)
589 port->vlan.parent = nw_daemon_get_port_by_name(port->daemon, port->vlan.__parent_name);
590
591 if (port->vlan.parent)
592 return nw_port_ref(port->vlan.parent);
593
594 // No port found
595 return NULL;
596}
597
598int nw_port_set_vlan_parent(nw_port* port, nw_port* parent) {
599 int r;
600
601 // Check type
602 r = nw_port_check_type(port, NW_PORT_VLAN);
603 if (r < 0)
604 return r;
605
606 // Reset the former parent name
607 nw_string_empty(port->vlan.__parent_name);
608
609 // Dereference the former parent
610 if (port->vlan.parent) {
611 nw_port_unref(port->vlan.parent);
612 port->vlan.parent = NULL;
613 }
614
615 // Store the new parent
616 if (parent)
617 port->vlan.parent = nw_port_ref(parent);
618
619 DEBUG("Port %s: Set VLAN parent to %s\n", port->name, nw_port_name(port->vlan.parent));
620
621 return 0;
622}