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-vlan.h"
37 #include "stats-collector.h"
40 static const nw_string_table_t nw_port_type_id
[] = {
41 { NW_PORT_BONDING
, "bonding" },
42 { NW_PORT_DUMMY
, "dummy" },
43 { NW_PORT_VLAN
, "vlan" },
47 NW_STRING_TABLE_LOOKUP(nw_port_type_id_t
, nw_port_type_id
)
49 static void nw_port_free(nw_port
* port
) {
51 nw_link_unref(port
->link
);
53 nw_config_unref(port
->config
);
55 nw_daemon_unref(port
->daemon
);
60 static int nw_port_set_link(nw_port
* port
, nw_link
* link
) {
61 // Do nothing if the same link is being re-assigned
62 if (port
->link
== link
)
65 // Dereference the former link if set
67 nw_link_unref(port
->link
);
71 port
->link
= nw_link_ref(link
);
73 DEBUG("Port %s: Assigned link %d\n", port
->name
, nw_link_ifindex(port
->link
));
75 // Or clear the pointer if no link has been provided
79 DEBUG("Port %s: Removed link\n", port
->name
);
85 static int nw_port_setup(nw_port
* port
) {
90 link
= nw_daemon_get_link_by_name(port
->daemon
, port
->name
);
92 r
= nw_port_set_link(port
, link
);
97 // Generate a random Ethernet address
98 r
= nw_address_generate(&port
->address
);
100 ERROR("Could not generate an Ethernet address: %s\n", strerror(-r
));
105 r
= NW_CONFIG_OPTION_ADDRESS(port
->config
, "ADDRESS", &port
->address
);
109 // Call any custom initialization
110 if (NW_PORT_TYPE(port
)->setup
) {
111 r
= NW_PORT_TYPE(port
)->setup(port
);
116 // Parse the configuration
117 r
= nw_config_options_read(port
->config
);
119 ERROR("Could not read configuration for port %s: %s\n", port
->name
, strerror(-r
));
130 static int nw_port_validate(nw_port
* port
) {
133 // Validate the port configuration
134 if (NW_PORT_TYPE(port
)->validate
) {
135 r
= NW_PORT_TYPE(port
)->validate(port
);
137 ERROR("Could not check configuration for %s: %s\n", port
->name
, strerror(-r
));
143 int nw_port_create(nw_port
** port
, nw_daemon
* daemon
,
144 const nw_port_type_id_t type
, const char* name
, nw_config
* config
) {
147 // Allocate a new object
148 nw_port
* p
= calloc(1, sizeof(*p
));
152 // Store a reference to the daemon
153 p
->daemon
= nw_daemon_ref(daemon
);
155 // Initialize reference counter
160 case NW_PORT_BONDING
:
161 p
->type
= &nw_port_type_bonding
;
165 p
->type
= &nw_port_type_dummy
;
169 p
->type
= &nw_port_type_vlan
;
174 r
= nw_string_set(p
->name
, name
);
178 // Copy the configuration
179 r
= nw_config_copy(config
, &p
->config
);
184 r
= nw_port_setup(p
);
188 // Validate the configuration
189 r
= nw_port_validate(p
);
191 // Configuration is valid
195 // Configuration is invalid
197 ERROR("%s: Invalid configuration\n", p
->name
);
213 int nw_port_open(nw_port
** port
, nw_daemon
* daemon
, const char* name
) {
214 nw_config
* config
= NULL
;
220 r
= nw_string_format(path
, "ports/%s", name
);
224 // Open the configuration file
225 f
= nw_daemon_config_fopen(daemon
, path
, "r");
231 // Initialize the configuration
232 r
= nw_config_create(&config
, f
);
237 const char* type
= nw_config_get(config
, "TYPE");
239 ERROR("Port configuration %s has no TYPE\n", path
);
245 r
= nw_port_create(port
, daemon
, nw_port_type_id_from_string(type
), name
, config
);
251 nw_config_unref(config
);
258 nw_port
* nw_port_ref(nw_port
* port
) {
264 nw_port
* nw_port_unref(nw_port
* port
) {
265 if (--port
->nrefs
> 0)
273 This is a helper function for when we pass a reference to the event loop
274 it will have to dereference the port instance later.
276 static void __nw_port_unref(void* data
) {
277 nw_port
* port
= (nw_port
*)data
;
282 int nw_port_destroy(nw_port
* port
) {
285 DEBUG("Destroying port %s\n", port
->name
);
287 // Destroy the physical link (if exists)
289 r
= nw_link_destroy(port
->link
);
294 // Dereference the port from other ports
295 r
= nw_daemon_ports_walk(port
->daemon
, __nw_port_drop_port
, port
);
299 // Dereference the port from other zones
300 r
= nw_daemon_zones_walk(port
->daemon
, __nw_zone_drop_port
, port
);
304 // Destroy the configuration
305 r
= nw_config_destroy(port
->config
);
312 int __nw_port_drop_port(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
313 nw_port
* dropped_port
= (nw_port
*)data
;
316 switch (port
->type
->id
) {
318 if (port
->vlan
.parent
== dropped_port
) {
319 r
= nw_port_set_vlan_parent(port
, NULL
);
325 case NW_PORT_BONDING
:
333 int nw_port_save(nw_port
* port
) {
339 r
= nw_string_format(path
, "ports/%s", port
->name
);
344 f
= nw_daemon_config_fopen(port
->daemon
, path
, "w");
350 // Write out the configuration
351 r
= nw_config_options_write(port
->config
);
355 // Write the configuration
356 r
= nw_config_write(port
->config
, f
);
364 ERROR("Could not save configuration for port %s: %s\n", port
->name
, strerror(-r
));
369 const char* nw_port_name(nw_port
* port
) {
373 char* nw_port_bus_path(nw_port
* port
) {
377 // Encode the bus path
378 r
= sd_bus_path_encode("/org/ipfire/network1/port", port
->name
, &p
);
385 int __nw_port_set_link(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
386 nw_link
* link
= (nw_link
*)data
;
388 // Fetch the link name
389 const char* ifname
= nw_link_ifname(link
);
391 ERROR("Link does not have a name set\n");
395 // Set link if the name matches
396 if (strcmp(port
->name
, ifname
) == 0)
397 return nw_port_set_link(port
, link
);
399 // If we have the link set but the name did not match, we will remove it
400 else if (port
->link
== link
)
401 return nw_port_set_link(port
, NULL
);
406 int __nw_port_drop_link(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
407 nw_link
* link
= (nw_link
*)data
;
409 // Drop the link if it matches
410 if (port
->link
== link
)
411 return nw_port_set_link(port
, NULL
);
416 static nw_link
* nw_port_get_link(nw_port
* port
) {
417 // Fetch the link if not set
419 port
->link
= nw_daemon_get_link_by_name(port
->daemon
, port
->name
);
424 return nw_link_ref(port
->link
);
427 const nw_address_t
* nw_port_get_address(nw_port
* port
) {
428 return &port
->address
;
431 static int nw_port_is_disabled(nw_port
* port
) {
432 return nw_config_get_bool(port
->config
, "DISABLED");
435 nw_port
* nw_port_get_parent_port(nw_port
* port
) {
436 if (!NW_PORT_TYPE(port
)->get_parent_port
)
439 return NW_PORT_TYPE(port
)->get_parent_port(port
);
442 static nw_link
* nw_port_get_parent_link(nw_port
* port
) {
443 nw_port
* parent
= NULL
;
444 nw_link
* link
= NULL
;
447 parent
= nw_port_get_parent_port(port
);
452 link
= nw_port_get_link(parent
);
456 nw_port_unref(parent
);
461 static int __nw_port_create_link(sd_netlink
* rtnl
, sd_netlink_message
* m
, void* data
) {
462 nw_port
* port
= (nw_port
*)data
;
465 // Check if the operation was successful
466 r
= sd_netlink_message_get_errno(m
);
468 ERROR("Could not create port %s: %s\n", port
->name
, strerror(-r
));
469 // XXX We should extract the error message
474 DEBUG("Successfully created %s\n", port
->name
);
479 static int nw_port_create_link(nw_port
* port
) {
480 sd_netlink_message
* m
= NULL
;
481 nw_link
* link
= NULL
;
484 sd_netlink
* rtnl
= nw_daemon_get_rtnl(port
->daemon
);
486 DEBUG("Creating port %s...\n", port
->name
);
489 if (!NW_PORT_TYPE(port
)->kind
) {
490 ERROR("Port type has no kind\n");
496 r
= sd_rtnl_message_new_link(rtnl
, &m
, RTM_NEWLINK
, 0);
498 ERROR("Could not create netlink message: %m\n");
503 r
= sd_netlink_message_append_string(m
, IFLA_IFNAME
, port
->name
);
505 ERROR("Could not set port name: %s\n", strerror(-r
));
509 // XXX Set common things like MTU, etc.
511 // Set Ethernet address
512 r
= sd_netlink_message_append_ether_addr(m
, IFLA_ADDRESS
, &port
->address
);
514 ERROR("Could not set MAC address: %s\n", strerror(-r
));
518 // Fetch the parent link
519 link
= nw_port_get_parent_link(port
);
521 r
= sd_netlink_message_append_u32(m
, IFLA_LINK
, nw_link_ifindex(link
));
526 // Open an IFLA_LINKINFO container
527 r
= sd_netlink_message_open_container(m
, IFLA_LINKINFO
);
531 // Run the custom setup
532 if (NW_PORT_TYPE(port
)->create_link
) {
533 r
= sd_netlink_message_open_container_union(m
, IFLA_INFO_DATA
, NW_PORT_TYPE(port
)->kind
);
535 ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r
));
539 r
= NW_PORT_TYPE(port
)->create_link(port
, m
);
541 ERROR("Could not create port %s: %m\n", port
->name
);
545 // Close the container
546 r
= sd_netlink_message_close_container(m
);
550 // Just set IFLA_INFO_KIND if there is no custom function
552 r
= sd_netlink_message_append_string(m
, IFLA_INFO_KIND
, NW_PORT_TYPE(port
)->kind
);
557 // Close the container
558 r
= sd_netlink_message_close_container(m
);
563 r
= sd_netlink_call_async(rtnl
, NULL
, m
, __nw_port_create_link
,
564 __nw_port_unref
, nw_port_ref(port
), -1, NULL
);
566 ERROR("Could not send netlink message: %s\n", strerror(-r
));
574 sd_netlink_message_unref(m
);
581 int nw_port_reconfigure(nw_port
* port
) {
584 // If the port is disabled, we will try to destroy it
585 if (nw_port_is_disabled(port
)) {
587 r
= nw_link_destroy(port
->link
);
595 // If there is no link, we will try to create it
597 return nw_port_create_link(port
);
604 int nw_port_has_carrier(nw_port
* port
) {
608 return nw_link_has_carrier(port
->link
);
615 const struct rtnl_link_stats64
* nw_port_get_stats64(nw_port
* port
) {
619 return nw_link_get_stats64(port
->link
);
622 int __nw_port_update_stats(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
623 nw_link
* link
= (nw_link
*)data
;
625 // Emit stats if link matches
626 if (port
->link
== link
)
627 return nw_stats_collector_emit_port_stats(daemon
, port
);
632 int nw_port_update_stats(nw_port
* port
) {
634 return nw_link_update_stats(port
->link
);
639 int nw_port_check_type(nw_port
* port
, const nw_port_type_id_t type
) {
640 if (port
->type
->id
== type
)
650 int nw_port_to_json(nw_port
* port
, struct json_object
** object
) {
651 char* address
= NULL
;
654 // Create a new JSON object
655 struct json_object
* o
= json_object_new_object();
662 r
= json_object_add_string(o
, "Name", port
->name
);
667 r
= json_object_add_string(o
, "Type", nw_port_type_id_to_string(port
->type
->id
));
672 address
= nw_address_to_string(&port
->address
);
674 r
= json_object_add_string(o
, "Address", address
);
680 if (NW_PORT_TYPE(port
)->to_json
) {
681 r
= NW_PORT_TYPE(port
)->to_json(port
, o
);
689 // Return a reference to the created object
690 *object
= json_object_ref(o
);
696 json_object_unref(o
);