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 int nw_port_create(nw_port
** port
, nw_daemon
* daemon
,
131 const nw_port_type_id_t type
, const char* name
, nw_config
* config
) {
134 // Allocate a new object
135 nw_port
* p
= calloc(1, sizeof(*p
));
139 // Store a reference to the daemon
140 p
->daemon
= nw_daemon_ref(daemon
);
142 // Initialize reference counter
147 case NW_PORT_BONDING
:
148 p
->type
= &nw_port_type_bonding
;
152 p
->type
= &nw_port_type_dummy
;
156 p
->type
= &nw_port_type_vlan
;
161 r
= nw_string_set(p
->name
, name
);
165 // Copy the configuration
166 r
= nw_config_copy(config
, &p
->config
);
171 r
= nw_port_setup(p
);
183 int nw_port_create_from_config(nw_port
** port
, nw_daemon
* daemon
,
184 const char* name
, const char* path
) {
185 nw_config
* config
= NULL
;
188 // Initialize the configuration
189 r
= nw_config_create(&config
, path
);
194 const char* type
= nw_config_get(config
, "TYPE");
196 ERROR("Port configuration %s has no TYPE\n", path
);
202 r
= nw_port_create(port
, daemon
, nw_port_type_id_from_string(type
), name
, config
);
208 nw_config_unref(config
);
213 nw_port
* nw_port_ref(nw_port
* port
) {
219 nw_port
* nw_port_unref(nw_port
* port
) {
220 if (--port
->nrefs
> 0)
228 This is a helper function for when we pass a reference to the event loop
229 it will have to dereference the port instance later.
231 static void __nw_port_unref(void* data
) {
232 nw_port
* port
= (nw_port
*)data
;
237 int nw_port_destroy(nw_port
* port
) {
240 DEBUG("Destroying port %s\n", port
->name
);
242 // Destroy the physical link (if exists)
244 r
= nw_link_destroy(port
->link
);
249 // Dereference the port from other ports
250 r
= nw_daemon_ports_walk(port
->daemon
, __nw_port_drop_port
, port
);
254 // Dereference the port from other zones
255 r
= nw_daemon_zones_walk(port
->daemon
, __nw_zone_drop_port
, port
);
259 // Destroy the configuration
260 r
= nw_config_destroy(port
->config
);
267 int __nw_port_drop_port(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
268 nw_port
* dropped_port
= (nw_port
*)data
;
271 switch (port
->type
->id
) {
273 if (port
->vlan
.parent
== dropped_port
) {
274 r
= nw_port_set_vlan_parent(port
, NULL
);
280 case NW_PORT_BONDING
:
288 int nw_port_save(nw_port
* port
) {
291 // Write out the configuration
292 r
= nw_config_options_write(port
->config
);
296 // Write the configuration
297 r
= nw_config_write(port
->config
);
304 ERROR("Could not save configuration for port %s: %s\n", port
->name
, strerror(-r
));
309 const char* nw_port_name(nw_port
* port
) {
313 char* nw_port_bus_path(nw_port
* port
) {
317 // Encode the bus path
318 r
= sd_bus_path_encode("/org/ipfire/network1/port", port
->name
, &p
);
325 int __nw_port_set_link(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
326 nw_link
* link
= (nw_link
*)data
;
328 // Fetch the link name
329 const char* ifname
= nw_link_ifname(link
);
331 ERROR("Link does not have a name set\n");
335 // Set link if the name matches
336 if (strcmp(port
->name
, ifname
) == 0)
337 return nw_port_set_link(port
, link
);
339 // If we have the link set but the name did not match, we will remove it
340 else if (port
->link
== link
)
341 return nw_port_set_link(port
, NULL
);
346 int __nw_port_drop_link(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
347 nw_link
* link
= (nw_link
*)data
;
349 // Drop the link if it matches
350 if (port
->link
== link
)
351 return nw_port_set_link(port
, NULL
);
356 static nw_link
* nw_port_get_link(nw_port
* port
) {
357 // Fetch the link if not set
359 port
->link
= nw_daemon_get_link_by_name(port
->daemon
, port
->name
);
364 return nw_link_ref(port
->link
);
367 const nw_address_t
* nw_port_get_address(nw_port
* port
) {
368 return &port
->address
;
371 static int nw_port_is_disabled(nw_port
* port
) {
372 return nw_config_get_bool(port
->config
, "DISABLED");
375 nw_port
* nw_port_get_parent_port(nw_port
* port
) {
376 if (!NW_PORT_TYPE(port
)->get_parent_port
)
379 return NW_PORT_TYPE(port
)->get_parent_port(port
);
382 static nw_link
* nw_port_get_parent_link(nw_port
* port
) {
383 nw_port
* parent
= NULL
;
384 nw_link
* link
= NULL
;
387 parent
= nw_port_get_parent_port(port
);
392 link
= nw_port_get_link(parent
);
396 nw_port_unref(parent
);
401 static int __nw_port_create_link(sd_netlink
* rtnl
, sd_netlink_message
* m
, void* data
) {
402 nw_port
* port
= (nw_port
*)data
;
405 // Check if the operation was successful
406 r
= sd_netlink_message_get_errno(m
);
408 ERROR("Could not create port %s: %s\n", port
->name
, strerror(-r
));
409 // XXX We should extract the error message
414 DEBUG("Successfully created %s\n", port
->name
);
419 static int nw_port_create_link(nw_port
* port
) {
420 sd_netlink_message
* m
= NULL
;
421 nw_link
* link
= NULL
;
424 sd_netlink
* rtnl
= nw_daemon_get_rtnl(port
->daemon
);
426 DEBUG("Creating port %s...\n", port
->name
);
429 if (!NW_PORT_TYPE(port
)->kind
) {
430 ERROR("Port type has no kind\n");
436 r
= sd_rtnl_message_new_link(rtnl
, &m
, RTM_NEWLINK
, 0);
438 ERROR("Could not create netlink message: %m\n");
443 r
= sd_netlink_message_append_string(m
, IFLA_IFNAME
, port
->name
);
445 ERROR("Could not set port name: %s\n", strerror(-r
));
449 // XXX Set common things like MTU, etc.
451 // Set Ethernet address
452 r
= sd_netlink_message_append_ether_addr(m
, IFLA_ADDRESS
, &port
->address
);
454 ERROR("Could not set MAC address: %s\n", strerror(-r
));
458 // Fetch the parent link
459 link
= nw_port_get_parent_link(port
);
461 r
= sd_netlink_message_append_u32(m
, IFLA_LINK
, nw_link_ifindex(link
));
466 // Open an IFLA_LINKINFO container
467 r
= sd_netlink_message_open_container(m
, IFLA_LINKINFO
);
471 // Run the custom setup
472 if (NW_PORT_TYPE(port
)->create_link
) {
473 r
= sd_netlink_message_open_container_union(m
, IFLA_INFO_DATA
, NW_PORT_TYPE(port
)->kind
);
475 ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r
));
479 r
= NW_PORT_TYPE(port
)->create_link(port
, m
);
481 ERROR("Could not create port %s: %m\n", port
->name
);
485 // Close the container
486 r
= sd_netlink_message_close_container(m
);
490 // Just set IFLA_INFO_KIND if there is no custom function
492 r
= sd_netlink_message_append_string(m
, IFLA_INFO_KIND
, NW_PORT_TYPE(port
)->kind
);
497 // Close the container
498 r
= sd_netlink_message_close_container(m
);
503 r
= sd_netlink_call_async(rtnl
, NULL
, m
, __nw_port_create_link
,
504 __nw_port_unref
, nw_port_ref(port
), -1, NULL
);
506 ERROR("Could not send netlink message: %s\n", strerror(-r
));
514 sd_netlink_message_unref(m
);
521 int nw_port_reconfigure(nw_port
* port
) {
524 // If the port is disabled, we will try to destroy it
525 if (nw_port_is_disabled(port
)) {
527 r
= nw_link_destroy(port
->link
);
535 // If there is no link, we will try to create it
537 return nw_port_create_link(port
);
544 int nw_port_has_carrier(nw_port
* port
) {
548 return nw_link_has_carrier(port
->link
);
555 const struct rtnl_link_stats64
* nw_port_get_stats64(nw_port
* port
) {
559 return nw_link_get_stats64(port
->link
);
562 int __nw_port_update_stats(nw_daemon
* daemon
, nw_port
* port
, void* data
) {
563 nw_link
* link
= (nw_link
*)data
;
565 // Emit stats if link matches
566 if (port
->link
== link
)
567 return nw_stats_collector_emit_port_stats(daemon
, port
);
572 int nw_port_update_stats(nw_port
* port
) {
574 return nw_link_update_stats(port
->link
);
579 int nw_port_check_type(nw_port
* port
, const nw_port_type_id_t type
) {
580 if (port
->type
->id
== type
)
590 int nw_port_to_json(nw_port
* port
, struct json_object
** object
) {
591 char* address
= NULL
;
594 // Create a new JSON object
595 struct json_object
* o
= json_object_new_object();
602 r
= json_object_add_string(o
, "Name", port
->name
);
607 r
= json_object_add_string(o
, "Type", nw_port_type_id_to_string(port
->type
->id
));
612 address
= nw_address_to_string(&port
->address
);
614 r
= json_object_add_string(o
, "Address", address
);
620 if (NW_PORT_TYPE(port
)->to_json
) {
621 r
= NW_PORT_TYPE(port
)->to_json(port
, o
);
629 // Return a reference to the created object
630 *object
= json_object_ref(o
);
636 json_object_unref(o
);