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>
34 #include "port-bonding.h"
35 #include "port-dummy.h"
36 #include "port-ethernet.h"
37 #include "port-veth.h"
38 #include "port-vlan.h"
39 #include "stats-collector.h"
42 static const nw_string_table_t nw_port_type_id
[] = {
43 { NW_PORT_BONDING
, "bonding" },
44 { NW_PORT_DUMMY
, "dummy" },
45 { NW_PORT_ETHERNET
, "ethernet" },
46 { NW_PORT_VETH
, "veth", },
47 { NW_PORT_VLAN
, "vlan" },
51 NW_STRING_TABLE_LOOKUP(nw_port_type_id_t
, nw_port_type_id
)
53 static void nw_port_free(nw_port
* port
) {
55 nw_link_unref(port
->link
);
57 nw_config_unref(port
->config
);
59 nw_daemon_unref(port
->daemon
);
64 static int nw_port_set_link(nw_port
* port
, nw_link
* link
) {
65 // Do nothing if the same link is being re-assigned
66 if (port
->link
== link
)
69 // Dereference the former link if set
71 nw_link_unref(port
->link
);
75 port
->link
= nw_link_ref(link
);
77 DEBUG("Port %s: Assigned link %d\n", port
->name
, nw_link_ifindex(port
->link
));
79 // Or clear the pointer if no link has been provided
83 DEBUG("Port %s: Removed link\n", port
->name
);
89 static int nw_port_setup(nw_port
* port
) {
94 link
= nw_daemon_get_link_by_name(port
->daemon
, port
->name
);
96 r
= nw_port_set_link(port
, link
);
101 // Generate a random Ethernet address
102 r
= nw_address_generate(&port
->address
);
104 ERROR("Could not generate an Ethernet address: %s\n", strerror(-r
));
109 r
= NW_CONFIG_OPTION_ADDRESS(port
->config
, "ADDRESS", &port
->address
);
113 // Call any custom initialization
114 if (NW_PORT_TYPE(port
)->setup
) {
115 r
= NW_PORT_TYPE(port
)->setup(port
);
120 // Parse the configuration
121 r
= nw_config_options_read(port
->config
);
123 ERROR("Could not read configuration for port %s: %s\n", port
->name
, strerror(-r
));
134 static int nw_port_validate(nw_port
* port
) {
137 // Validate the port configuration
138 if (NW_PORT_TYPE(port
)->validate
) {
139 r
= NW_PORT_TYPE(port
)->validate(port
);
141 ERROR("Could not check configuration for %s: %s\n", port
->name
, strerror(-r
));
147 int nw_port_create(nw_port
** port
, nw_daemon
* daemon
,
148 const nw_port_type_id_t type
, const char* name
, nw_config
* config
) {
151 // Allocate a new object
152 nw_port
* p
= calloc(1, sizeof(*p
));
156 // Store a reference to the daemon
157 p
->daemon
= nw_daemon_ref(daemon
);
159 // Initialize reference counter
164 case NW_PORT_BONDING
:
165 p
->type
= &nw_port_type_bonding
;
169 p
->type
= &nw_port_type_dummy
;
172 case NW_PORT_ETHERNET
:
173 p
->type
= &nw_port_type_ethernet
;
177 p
->type
= &nw_port_type_veth
;
181 p
->type
= &nw_port_type_vlan
;
186 r
= nw_string_set(p
->name
, name
);
190 // Copy the configuration
191 r
= nw_config_copy(config
, &p
->config
);
196 r
= nw_port_setup(p
);
200 // Validate the configuration
201 r
= nw_port_validate(p
);
203 // Configuration is valid
207 // Configuration is invalid
209 ERROR("%s: Invalid configuration\n", p
->name
);
225 int nw_port_open(nw_port
** port
, nw_daemon
* daemon
, const char* name
, FILE* f
) {
226 nw_config
* config
= NULL
;
229 // Initialize the configuration
230 r
= nw_config_create(&config
, f
);
235 const char* type
= nw_config_get(config
, "TYPE");
237 ERROR("Port %s has no TYPE\n", name
);
243 r
= nw_port_create(port
, daemon
, nw_port_type_id_from_string(type
), name
, config
);
249 nw_config_unref(config
);
254 nw_port
* nw_port_ref(nw_port
* port
) {
260 nw_port
* nw_port_unref(nw_port
* port
) {
261 if (--port
->nrefs
> 0)
269 This is a helper function for when we pass a reference to the event loop
270 it will have to dereference the port instance later.
272 static void __nw_port_unref(void* data
) {
273 nw_port
* port
= (nw_port
*)data
;
278 int nw_port_destroy(nw_port
* port
) {
279 nw_configd
* configd
= NULL
;
282 DEBUG("Destroying port %s\n", port
->name
);
284 // Destroy the physical link (if exists)
286 r
= nw_link_destroy(port
->link
);
291 // Dereference the port from other ports
292 r
= nw_daemon_ports_walk(port
->daemon
, __nw_port_drop_port
, port
);
296 // Dereference the port from other zones
297 r
= nw_daemon_zones_walk(port
->daemon
, __nw_zone_drop_port
, port
);
301 // Fetch the configuration directory
302 configd
= nw_daemon_configd(port
->daemon
, "ports");
304 r
= nw_configd_unlink(configd
, port
->name
, 0);
311 nw_configd_unref(configd
);
316 int __nw_port_drop_port(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
317 nw_port
* dropped_port
= (nw_port
*)data
;
320 switch (port
->type
->id
) {
322 if (port
->vlan
.parent
== dropped_port
) {
323 r
= nw_port_set_vlan_parent(port
, NULL
);
329 case NW_PORT_BONDING
:
331 case NW_PORT_ETHERNET
:
339 int nw_port_save(nw_port
* port
) {
340 nw_configd
* configd
= NULL
;
344 // Fetch configuration directory
345 configd
= nw_daemon_configd(port
->daemon
, "ports");
352 f
= nw_configd_fopen(configd
, port
->name
, "w");
358 // Write out the configuration
359 r
= nw_config_options_write(port
->config
);
363 // Write the configuration
364 r
= nw_config_write(port
->config
, f
);
370 nw_configd_unref(configd
);
374 ERROR("Could not save configuration for port %s: %s\n", port
->name
, strerror(-r
));
379 const char* nw_port_name(nw_port
* port
) {
383 char* nw_port_bus_path(nw_port
* port
) {
387 // Encode the bus path
388 r
= sd_bus_path_encode("/org/ipfire/network1/port", port
->name
, &p
);
395 int __nw_port_set_link(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
396 nw_link
* link
= (nw_link
*)data
;
398 // Fetch the link name
399 const char* ifname
= nw_link_ifname(link
);
401 ERROR("Link does not have a name set\n");
405 // Set link if the name matches
406 if (strcmp(port
->name
, ifname
) == 0)
407 return nw_port_set_link(port
, link
);
409 // If we have the link set but the name did not match, we will remove it
410 else if (port
->link
== link
)
411 return nw_port_set_link(port
, NULL
);
416 int __nw_port_drop_link(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
417 nw_link
* link
= (nw_link
*)data
;
419 // Drop the link if it matches
420 if (port
->link
== link
)
421 return nw_port_set_link(port
, NULL
);
426 static nw_link
* nw_port_get_link(nw_port
* port
) {
427 // Fetch the link if not set
429 port
->link
= nw_daemon_get_link_by_name(port
->daemon
, port
->name
);
434 return nw_link_ref(port
->link
);
437 const nw_address_t
* nw_port_get_address(nw_port
* port
) {
438 return &port
->address
;
441 static int nw_port_is_disabled(nw_port
* port
) {
442 return nw_config_get_bool(port
->config
, "DISABLED");
445 nw_port
* nw_port_get_parent_port(nw_port
* port
) {
446 if (!NW_PORT_TYPE(port
)->get_parent_port
)
449 return NW_PORT_TYPE(port
)->get_parent_port(port
);
452 static nw_link
* nw_port_get_parent_link(nw_port
* port
) {
453 nw_port
* parent
= NULL
;
454 nw_link
* link
= NULL
;
457 parent
= nw_port_get_parent_port(port
);
462 link
= nw_port_get_link(parent
);
466 nw_port_unref(parent
);
471 static int __nw_port_create_link(sd_netlink
* rtnl
, sd_netlink_message
* m
, void* data
) {
472 nw_port
* port
= (nw_port
*)data
;
475 // Check if the operation was successful
476 r
= sd_netlink_message_get_errno(m
);
478 ERROR("Could not create port %s: %s\n", port
->name
, strerror(-r
));
479 // XXX We should extract the error message
484 DEBUG("Successfully created %s\n", port
->name
);
489 static int nw_port_create_link(nw_port
* port
) {
490 sd_netlink_message
* m
= NULL
;
491 nw_link
* link
= NULL
;
494 sd_netlink
* rtnl
= nw_daemon_get_rtnl(port
->daemon
);
496 DEBUG("Creating port %s...\n", port
->name
);
499 if (!NW_PORT_TYPE(port
)->kind
) {
500 ERROR("Port type has no kind\n");
506 r
= sd_rtnl_message_new_link(rtnl
, &m
, RTM_NEWLINK
, 0);
508 ERROR("Could not create netlink message: %m\n");
513 r
= sd_netlink_message_append_string(m
, IFLA_IFNAME
, port
->name
);
515 ERROR("Could not set port name: %s\n", strerror(-r
));
519 // XXX Set common things like MTU, etc.
521 // Set Ethernet address
522 r
= sd_netlink_message_append_ether_addr(m
, IFLA_ADDRESS
, &port
->address
);
524 ERROR("Could not set MAC address: %s\n", strerror(-r
));
528 // Fetch the parent link
529 link
= nw_port_get_parent_link(port
);
531 r
= sd_netlink_message_append_u32(m
, IFLA_LINK
, nw_link_ifindex(link
));
536 // Open an IFLA_LINKINFO container
537 r
= sd_netlink_message_open_container(m
, IFLA_LINKINFO
);
541 // Run the custom setup
542 if (NW_PORT_TYPE(port
)->create_link
) {
543 r
= sd_netlink_message_open_container_union(m
, IFLA_INFO_DATA
, NW_PORT_TYPE(port
)->kind
);
545 ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r
));
549 r
= NW_PORT_TYPE(port
)->create_link(port
, m
);
551 ERROR("Could not create port %s: %m\n", port
->name
);
555 // Close the container
556 r
= sd_netlink_message_close_container(m
);
560 // Just set IFLA_INFO_KIND if there is no custom function
562 r
= sd_netlink_message_append_string(m
, IFLA_INFO_KIND
, NW_PORT_TYPE(port
)->kind
);
567 // Close the container
568 r
= sd_netlink_message_close_container(m
);
573 r
= sd_netlink_call_async(rtnl
, NULL
, m
, __nw_port_create_link
,
574 __nw_port_unref
, nw_port_ref(port
), -1, NULL
);
576 ERROR("Could not send netlink message: %s\n", strerror(-r
));
584 sd_netlink_message_unref(m
);
591 int nw_port_reconfigure(nw_port
* port
) {
594 // If the port is disabled, we will try to destroy it
595 if (nw_port_is_disabled(port
)) {
597 r
= nw_link_destroy(port
->link
);
605 // If there is no link, we will try to create it
607 return nw_port_create_link(port
);
614 int nw_port_has_carrier(nw_port
* port
) {
618 return nw_link_has_carrier(port
->link
);
625 const struct rtnl_link_stats64
* nw_port_get_stats64(nw_port
* port
) {
629 return nw_link_get_stats64(port
->link
);
632 int __nw_port_update_stats(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
633 nw_link
* link
= (nw_link
*)data
;
635 // Emit stats if link matches
636 if (port
->link
== link
)
637 return nw_stats_collector_emit_port_stats(daemon
, port
);
642 int nw_port_update_stats(nw_port
* port
) {
644 return nw_link_update_stats(port
->link
);
649 int nw_port_check_type(nw_port
* port
, const nw_port_type_id_t type
) {
650 if (port
->type
->id
== type
)
660 int nw_port_to_json(nw_port
* port
, struct json_object
** object
) {
661 char* address
= NULL
;
664 // Create a new JSON object
665 struct json_object
* o
= json_object_new_object();
672 r
= json_object_add_string(o
, "Name", port
->name
);
677 r
= json_object_add_string(o
, "Type", nw_port_type_id_to_string(port
->type
->id
));
682 address
= nw_address_to_string(&port
->address
);
684 r
= json_object_add_string(o
, "Address", address
);
691 r
= nw_link_to_json(port
->link
, o
);
697 if (NW_PORT_TYPE(port
)->to_json
) {
698 r
= NW_PORT_TYPE(port
)->to_json(port
, o
);
706 // Return a reference to the created object
707 *object
= json_object_ref(o
);
713 json_object_unref(o
);