]>
git.ipfire.org Git - people/ms/network.git/blob - src/networkd/port.c
1 /*#############################################################################
3 # IPFire.org - A linux based firewall #
4 # Copyright (C) 2023 IPFire Network Development Team #
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. #
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. #
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/>. #
19 #############################################################################*/
26 #include <systemd/sd-bus.h>
33 #include "port-bonding.h"
34 #include "port-dummy.h"
35 #include "port-vlan.h"
36 #include "stats-collector.h"
39 static const struct nw_port_type_map
{
42 } nw_port_type_map
[] = {
43 { NW_PORT_BONDING
, "bonding" },
44 { NW_PORT_DUMMY
, "dummy" },
45 { NW_PORT_VLAN
, "vlan" },
46 { NW_PORT_UNKNOWN
, NULL
},
49 static nw_port_type_t
nw_port_type_from_string(const char* s
) {
50 const struct nw_port_type_map
* map
= NULL
;
52 for (map
= nw_port_type_map
; *map
->name
; map
++) {
53 if (strcmp(map
->name
, s
) == 0)
57 return NW_PORT_UNKNOWN
;
60 static void nw_port_free(nw_port
* port
) {
62 nw_link_unref(port
->link
);
64 nw_config_unref(port
->config
);
66 nw_daemon_unref(port
->daemon
);
71 static int nw_port_setup_address(nw_port
* port
) {
72 char* __address
= NULL
;
75 // Read ADDRESS from configuration
76 const char* s
= nw_config_get(port
->config
, "ADDRESS");
78 ERROR("Port %s: Address is not set\n", port
->name
);
83 r
= nw_address_from_string(&port
->address
, s
);
85 ERROR("Port %s: Could not parse address: %m\n", port
->name
);
89 // Check if this address is usable
90 r
= nw_address_is_multicast(&port
->address
);
92 DEBUG("Port %s: Multicast bit is set on Ethernet address\n", port
->name
);
99 // Generate a random Ethernet address
100 r
= nw_address_generate(&port
->address
);
102 ERROR("Could not generate a random Ethernet address: %m\n");
106 // Format the generated address
107 __address
= nw_address_to_string(&port
->address
);
109 ERROR("Generated a random Ethernet address for %s: %s\n", port
->name
, __address
);
118 static int nw_port_setup_common(nw_port
* port
) {
122 r
= nw_port_setup_address(port
);
129 static 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
)
134 // Dereference the former link if set
136 nw_link_unref(port
->link
);
138 // Store the new link
140 port
->link
= nw_link_ref(link
);
142 DEBUG("Port %s: Assigned link %d\n", port
->name
, nw_link_ifindex(port
->link
));
144 // Or clear the pointer if no link has been provided
148 DEBUG("Port %s: Removed link\n", port
->name
);
154 static int nw_port_setup(nw_port
* port
) {
155 nw_link
* link
= NULL
;
160 link
= nw_daemon_get_link_by_name(port
->daemon
, port
->name
);
162 r
= nw_port_set_link(port
, link
);
167 // Compose the path to the main configuration file
168 r
= nw_path_join(path
, PORT_CONFIG_DIR
, port
->name
);
172 // Initialize the configuration
173 r
= nw_config_create(&port
->config
, path
);
177 // Perform some common initialization
178 r
= nw_port_setup_common(port
);
182 // Call any custom initialization
183 if (NW_PORT_OPS(port
)->config_read
) {
184 r
= NW_PORT_OPS(port
)->config_read(port
);
196 int nw_port_create(nw_port
** port
, nw_daemon
* daemon
, nw_port_type_t type
, const char* name
) {
199 // Allocate a new object
200 nw_port
* p
= calloc(1, sizeof(*p
));
204 // Store a reference to the daemon
205 p
->daemon
= nw_daemon_ref(daemon
);
207 // Initialize reference counter
215 case NW_PORT_BONDING
:
216 p
->info
= &nw_port_info_bonding
;
220 p
->info
= &nw_port_info_dummy
;
224 p
->info
= &nw_port_info_vlan
;
229 r
= nw_string_set(p
->name
, name
);
234 r
= nw_port_setup(p
);
246 int nw_port_create_from_config(nw_port
** port
, nw_daemon
* daemon
,
247 const char* name
, const char* path
) {
248 nw_config
* config
= NULL
;
251 // Initialize the configuration
252 r
= nw_config_create(&config
, path
);
257 const char* type
= nw_config_get(config
, "TYPE");
259 ERROR("Port configuration %s has no TYPE\n", path
);
265 r
= nw_port_create(port
, daemon
, nw_port_type_from_string(type
), name
);
271 nw_config_unref(config
);
276 nw_port
* nw_port_ref(nw_port
* port
) {
282 nw_port
* nw_port_unref(nw_port
* port
) {
283 if (--port
->nrefs
> 0)
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.
294 static void __nw_port_unref(void* data
) {
295 nw_port
* port
= (nw_port
*)data
;
300 int nw_port_destroy(nw_port
* port
) {
303 DEBUG("Destroying port %s\n", port
->name
);
305 // Destroy the physical link (if exists)
307 r
= nw_link_destroy(port
->link
);
312 // Dereference the port from other ports
313 r
= nw_daemon_ports_walk(port
->daemon
, __nw_port_drop_port
, port
);
317 // Dereference the port from other zones
318 r
= nw_daemon_zones_walk(port
->daemon
, __nw_zone_drop_port
, port
);
322 // Destroy the configuration
323 r
= nw_config_destroy(port
->config
);
328 port
->type
= NW_PORT_UNKNOWN
;
333 int __nw_port_drop_port(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
334 nw_port
* dropped_port
= (nw_port
*)data
;
337 switch (port
->type
) {
339 if (port
->vlan
.parent
== dropped_port
) {
340 r
= nw_port_set_vlan_parent(port
, NULL
);
346 case NW_PORT_BONDING
:
348 case NW_PORT_UNKNOWN
:
355 int nw_port_save(nw_port
* port
) {
358 // Call the custom handler
359 if (NW_PORT_OPS(port
)->config_write
) {
360 r
= NW_PORT_OPS(port
)->config_write(port
);
365 // Write the configuration
366 r
= nw_config_write(port
->config
);
373 ERROR("Could not save configuration for port %s: %m\n", port
->name
);
378 const char* nw_port_name(nw_port
* port
) {
382 char* nw_port_bus_path(nw_port
* port
) {
386 // Encode the bus path
387 r
= sd_bus_path_encode("/org/ipfire/network1/port", port
->name
, &p
);
394 int __nw_port_set_link(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
395 nw_link
* link
= (nw_link
*)data
;
397 // Fetch the link name
398 const char* ifname
= nw_link_ifname(link
);
400 ERROR("Link does not have a name set\n");
404 // Set link if the name matches
405 if (strcmp(port
->name
, ifname
) == 0)
406 return nw_port_set_link(port
, link
);
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
);
415 int __nw_port_drop_link(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
416 nw_link
* link
= (nw_link
*)data
;
418 // Drop the link if it matches
419 if (port
->link
== link
)
420 return nw_port_set_link(port
, NULL
);
425 static nw_link
* nw_port_get_link(nw_port
* port
) {
426 // Fetch the link if not set
428 port
->link
= nw_daemon_get_link_by_name(port
->daemon
, port
->name
);
433 return nw_link_ref(port
->link
);
436 const nw_address_t
* nw_port_get_address(nw_port
* port
) {
437 return &port
->address
;
440 static int nw_port_is_disabled(nw_port
* port
) {
441 return nw_config_get_bool(port
->config
, "DISABLED");
444 static nw_link
* nw_port_get_parent_link(nw_port
* port
) {
445 nw_port
* parent
= NULL
;
446 nw_link
* link
= NULL
;
448 // Do nothing if not implemented
449 if (!NW_PORT_OPS(port
)->get_parent_port
)
453 parent
= NW_PORT_OPS(port
)->get_parent_port(port
);
458 link
= nw_port_get_link(parent
);
462 nw_port_unref(parent
);
467 static int __nw_port_create_link(sd_netlink
* rtnl
, sd_netlink_message
* m
, void* data
) {
468 nw_port
* port
= (nw_port
*)data
;
471 // Check if the operation was successful
472 r
= sd_netlink_message_get_errno(m
);
474 ERROR("Could not create port %s: %s\n", port
->name
, strerror(-r
));
475 // XXX We should extract the error message
480 DEBUG("Successfully created %s\n", port
->name
);
485 static int nw_port_create_link(nw_port
* port
) {
486 sd_netlink_message
* m
= NULL
;
487 nw_link
* link
= NULL
;
490 sd_netlink
* rtnl
= nw_daemon_get_rtnl(port
->daemon
);
492 DEBUG("Creating port %s...\n", port
->name
);
495 r
= sd_rtnl_message_new_link(rtnl
, &m
, RTM_NEWLINK
, 0);
497 ERROR("Could not create netlink message: %m\n");
502 r
= sd_netlink_message_append_string(m
, IFLA_IFNAME
, port
->name
);
504 ERROR("Could not set port name: %s\n", strerror(-r
));
508 // XXX Set common things like MTU, etc.
510 // Set Ethernet address
511 r
= sd_netlink_message_append_ether_addr(m
, IFLA_ADDRESS
, &port
->address
);
513 ERROR("Could not set MAC address: %s\n", strerror(-r
));
517 // Fetch the parent link
518 link
= nw_port_get_parent_link(port
);
520 r
= sd_netlink_message_append_u32(m
, IFLA_LINK
, nw_link_ifindex(link
));
525 // Open an IFLA_LINKINFO container
526 r
= sd_netlink_message_open_container(m
, IFLA_LINKINFO
);
530 // Run the custom setup
531 if (NW_PORT_OPS(port
)->create_link
) {
532 r
= sd_netlink_message_open_container_union(m
, IFLA_INFO_DATA
, NW_PORT_INFO(port
)->kind
);
534 ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r
));
538 r
= NW_PORT_OPS(port
)->create_link(port
, m
);
540 ERROR("Could not create port %s: %m\n", port
->name
);
544 // Close the container
545 r
= sd_netlink_message_close_container(m
);
549 // Just set IFLA_INFO_KIND if there is no custom function
551 r
= sd_netlink_message_append_string(m
, IFLA_INFO_KIND
, NW_PORT_INFO(port
)->kind
);
556 // Close the container
557 r
= sd_netlink_message_close_container(m
);
562 r
= sd_netlink_call_async(rtnl
, NULL
, m
, __nw_port_create_link
,
563 __nw_port_unref
, nw_port_ref(port
), -1, NULL
);
565 ERROR("Could not send netlink message: %s\n", strerror(-r
));
573 sd_netlink_message_unref(m
);
580 int nw_port_reconfigure(nw_port
* port
) {
583 // If the port is disabled, we will try to destroy it
584 if (nw_port_is_disabled(port
)) {
586 r
= nw_link_destroy(port
->link
);
594 // If there is no link, we will try to create it
596 return nw_port_create_link(port
);
603 int nw_port_has_carrier(nw_port
* port
) {
607 return nw_link_has_carrier(port
->link
);
614 const struct rtnl_link_stats64
* nw_port_get_stats64(nw_port
* port
) {
618 return nw_link_get_stats64(port
->link
);
621 int __nw_port_update_stats(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
622 nw_link
* link
= (nw_link
*)data
;
624 // Emit stats if link matches
625 if (port
->link
== link
)
626 return nw_stats_collector_emit_port_stats(daemon
, port
);
631 int nw_port_update_stats(nw_port
* port
) {
633 return nw_link_update_stats(port
->link
);
638 int nw_port_check_type(nw_port
* port
, const nw_port_type_t type
) {
639 if (port
->type
== type
)