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