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 #############################################################################*/
22 #include <linux/if_link.h>
27 #include <systemd/sd-device.h>
28 #include <systemd/sd-netlink.h>
45 char ifname
[IFNAMSIZ
];
53 struct sd_device
* device
;
56 struct rtnl_link_stats64 stats64
;
68 static int nw_link_setup_device(nw_link
* link
) {
72 r
= sd_device_new_from_ifindex(&link
->device
, link
->ifindex
);
74 ERROR("Could not fetch sd-device for link %d: %s\n", link
->ifindex
, strerror(-r
));
81 static void nw_link_free(nw_link
* link
) {
82 DEBUG("Freeing link (ifindex = %d)\n", link
->ifindex
);
85 sd_device_unref(link
->device
);
87 nw_daemon_unref(link
->daemon
);
90 int nw_link_create(nw_link
** link
, nw_daemon
* daemon
, int ifindex
) {
93 // Allocate a new object
94 nw_link
* l
= calloc(1, sizeof(*l
));
98 // Store a reference to the daemon
99 l
->daemon
= nw_daemon_ref(daemon
);
101 // Initialize the reference counter
105 l
->ifindex
= ifindex
;
107 DEBUG("New link allocated (ifindex = %d)\n", l
->ifindex
);
109 r
= nw_link_setup_device(l
);
121 nw_link
* nw_link_ref(nw_link
* link
) {
127 nw_link
* nw_link_unref(nw_link
* link
) {
128 if (--link
->nrefs
> 0)
136 This is a helper function for when we pass a reference to the event loop
137 it will have to dereference the link instance later.
139 static void __nw_link_unref(void* data
) {
140 nw_link
* link
= (nw_link
*)data
;
145 int nw_link_ifindex(nw_link
* link
) {
146 return link
->ifindex
;
149 const char* nw_link_ifname(nw_link
* link
) {
150 // Return NULL if name isn't set
159 const struct rtnl_link_stats64
* nw_link_get_stats64(nw_link
* link
) {
160 return &link
->stats64
;
163 static int nw_link_call_getlink(nw_link
* link
,
164 int (*callback
)(sd_netlink
* rtnl
, sd_netlink_message
* m
, void* data
)) {
165 sd_netlink_message
* m
= NULL
;
168 sd_netlink
* rtnl
= nw_daemon_get_rtnl(link
->daemon
);
172 // Create a new message
173 r
= sd_rtnl_message_new_link(rtnl
, &m
, RTM_GETLINK
, link
->ifindex
);
175 ERROR("Could not allocate RTM_GETLINK message: %m\n");
180 r
= sd_netlink_call_async(rtnl
, NULL
, m
, callback
,
181 __nw_link_unref
, nw_link_ref(link
), -1, NULL
);
183 ERROR("Could not send rtnetlink message: %m\n");
189 sd_netlink_message_unref(m
);
194 static int __nw_link_update_stats(sd_netlink
* rtnl
, sd_netlink_message
* m
, void* data
) {
195 nw_link
* link
= (nw_link
*)data
;
199 r
= sd_netlink_message_read(m
, IFLA_STATS64
, sizeof(link
->stats64
), &link
->stats64
);
203 DEBUG("Link %d: Stats updated\n", link
->ifindex
);
206 DEBUG(" Packets : RX: %12llu, TX: %12llu\n",
207 link
->stats64
.rx_packets
, link
->stats64
.tx_packets
);
208 DEBUG(" Bytes : RX: %12llu, TX: %12llu\n",
209 link
->stats64
.rx_bytes
, link
->stats64
.tx_bytes
);
210 DEBUG(" Errors : RX: %12llu, TX: %12llu\n",
211 link
->stats64
.rx_errors
, link
->stats64
.tx_errors
);
212 DEBUG(" Dropped : RX: %12llu, TX: %12llu\n",
213 link
->stats64
.rx_dropped
, link
->stats64
.rx_dropped
);
214 DEBUG(" Multicast : %llu\n", link
->stats64
.multicast
);
215 DEBUG(" Collisions : %llu\n", link
->stats64
.collisions
);
217 // Notify ports that stats have been updated
218 r
= nw_daemon_ports_walk(link
->daemon
, __nw_port_update_stats
, link
);
222 // Notify zones that stats have been updated
223 r
= nw_daemon_zones_walk(link
->daemon
, __nw_zone_update_stats
, link
);
230 int nw_link_update_stats(nw_link
* link
) {
231 return nw_link_call_getlink(link
, __nw_link_update_stats
);
236 int nw_link_has_carrier(nw_link
* link
) {
237 return link
->operstate
== IF_OPER_UP
;
240 static int nw_link_carrier_gained(nw_link
* link
) {
241 return 0; // XXX TODO
244 static int nw_link_carrier_lost(nw_link
* link
) {
245 return 0; // XXX TODO
248 static int nw_link_initialize(nw_link
* link
) {
249 sd_device
*device
= NULL
;
253 r
= sd_device_new_from_ifindex(&device
, link
->ifindex
);
255 WARNING("Could not find device for link %d: %s\n", link
->ifindex
, strerror(-r
));
260 // Check if device is initialized
261 r
= sd_device_get_is_initialized(device
);
263 // Initialized - fallthrough
268 DEBUG("The device has not been initialized, yet\n");
273 WARNING("Could not check whether device is initialized, ignoring: %s\n",
278 // XXX Check renaming?!
281 sd_device_unref(link
->device
);
284 link
->device
= sd_device_ref(device
);
286 DEBUG("Link %d has been initialized\n", link
->ifindex
);
290 sd_device_unref(device
);
295 static int nw_link_update_ifname(nw_link
* link
, sd_netlink_message
* message
) {
296 const char* ifname
= NULL
;
299 r
= sd_netlink_message_read_string(message
, IFLA_IFNAME
, &ifname
);
301 ERROR("Could not read link name for link %d: %m\n", link
->ifindex
);
305 // Do nothing if the name is already set
306 if (strcmp(link
->ifname
, ifname
) == 0)
309 // Otherwise update the name
310 r
= nw_string_set(link
->ifname
, ifname
);
312 ERROR("Could not set link name: %m\n");
316 DEBUG("Link %d has been renamed to '%s'\n", link
->ifindex
, link
->ifname
);
318 // Assign link to ports
319 nw_daemon_ports_walk(link
->daemon
, __nw_port_set_link
, link
);
321 // Assign link to zones
322 nw_daemon_zones_walk(link
->daemon
, __nw_zone_set_link
, link
);
327 static int nw_link_update_mtu(nw_link
* link
, sd_netlink_message
* message
) {
329 uint32_t min_mtu
= 0;
330 uint32_t max_mtu
= 0;
334 r
= sd_netlink_message_read_u32(message
, IFLA_MTU
, &mtu
);
336 ERROR("Could not read MTU for link %d: %m\n", link
->ifindex
);
340 // Read the minimum MTU
341 r
= sd_netlink_message_read_u32(message
, IFLA_MIN_MTU
, &min_mtu
);
343 ERROR("Could not read the minimum MTU for link %d: %m\n", link
->ifindex
);
347 // Read the maximum MTU
348 r
= sd_netlink_message_read_u32(message
, IFLA_MAX_MTU
, &max_mtu
);
350 ERROR("Could not read the maximum MTU for link %d: %m\n", link
->ifindex
);
354 // Set the maximum MTU to infinity
356 max_mtu
= UINT32_MAX
;
359 link
->min_mtu
= min_mtu
;
360 link
->max_mtu
= max_mtu
;
362 // End here, if the MTU has not been changed
363 if (link
->mtu
== mtu
)
366 DEBUG("Link %d: MTU has changed to %" PRIu32
" (min: %" PRIu32
", max: %" PRIu32
")\n",
367 link
->ifindex
, link
->mtu
, link
->min_mtu
, link
->max_mtu
);
375 static int nw_link_update_flags(nw_link
* link
, sd_netlink_message
* message
) {
376 unsigned int flags
= 0;
377 uint8_t operstate
= 0;
381 r
= sd_rtnl_message_link_get_flags(message
, &flags
);
383 DEBUG("Could not read link flags: %m\n");
389 r
= sd_netlink_message_read_u8(message
, IFLA_OPERSTATE
, &operstate
);
391 ERROR("Could not read operstate: %m\n");
396 // End here if there have been no changes
397 if (link
->flags
== flags
&& link
->operstate
== operstate
)
400 // XXX We should log any changes here
402 // Fetch current carrier state
403 const int had_carrier
= nw_link_has_carrier(link
);
405 // Store the new flags & operstate
407 link
->operstate
= operstate
;
409 // Notify if carrier was gained or lost
410 if (!had_carrier
&& nw_link_has_carrier(link
)) {
411 r
= nw_link_carrier_gained(link
);
415 } else if (had_carrier
&& !nw_link_has_carrier(link
)) {
416 r
= nw_link_carrier_lost(link
);
425 This function is called whenever anything changes, so that we can
426 update our internal link object.
428 static int nw_link_update(nw_link
* link
, sd_netlink_message
* message
) {
431 // Update the interface name
432 r
= nw_link_update_ifname(link
, message
);
437 r
= nw_link_update_mtu(link
, message
);
442 r
= nw_link_update_flags(link
, message
);
449 int nw_link_process(sd_netlink
* rtnl
, sd_netlink_message
* message
, void* data
) {
450 nw_links
* links
= NULL
;
451 nw_link
* link
= NULL
;
452 const char* ifname
= NULL
;
457 nw_daemon
* daemon
= (nw_daemon
*)data
;
460 links
= nw_daemon_links(daemon
);
466 // Check if this message could be received
467 if (sd_netlink_message_is_error(message
)) {
468 r
= sd_netlink_message_get_errno(message
);
470 ERROR("Could not receive link message: %m\n");
475 // Fetch the message type
476 r
= sd_netlink_message_get_type(message
, &type
);
478 ERROR("Could not fetch message type: %m\n");
489 ERROR("Received an unexpected message (type %u)\n", type
);
493 // Fetch the interface index
494 r
= sd_rtnl_message_link_get_ifindex(message
, &ifindex
);
496 ERROR("Could not fetch ifindex: %m\n");
500 // Check interface index
502 ERROR("Received an invalid ifindex\n");
506 // Fetch the interface name
507 r
= sd_netlink_message_read_string(message
, IFLA_IFNAME
, &ifname
);
509 ERROR("Received a netlink message without interface name: %m\n");
513 // Try finding an existing link
514 link
= nw_daemon_get_link_by_ifindex(daemon
, ifindex
);
518 // If the link doesn't exist, create it
520 r
= nw_link_create(&link
, daemon
, ifindex
);
522 ERROR("Could not create link: %m\n");
526 // Initialize the link
527 r
= nw_link_initialize(link
);
531 // Add it to the list
532 r
= nw_links_add_link(links
, link
);
537 // Import any data from the netlink message
538 r
= nw_link_update(link
, message
);
546 nw_links_drop_link(links
, link
);
556 nw_links_unref(links
);
563 static int __nw_link_destroy(sd_netlink
* rtnl
, sd_netlink_message
* m
, void* data
) {
564 nw_link
* link
= (nw_link
*)data
;
567 // Check if the operation was successful
568 r
= sd_netlink_message_get_errno(m
);
570 ERROR("Could not remove link %d: %m\n", link
->ifindex
);
571 // XXX We should extract the error message
576 // Mark this link as destroyed
577 link
->state
= NW_LINK_DESTROYED
;
582 int nw_link_destroy(nw_link
* link
) {
583 sd_netlink_message
* m
= NULL
;
586 sd_netlink
* rtnl
= nw_daemon_get_rtnl(link
->daemon
);
590 DEBUG("Destroying link %d\n", link
->ifindex
);
592 // Create a new message
593 r
= sd_rtnl_message_new_link(rtnl
, &m
, RTM_DELLINK
, link
->ifindex
);
595 ERROR("Could not allocate RTM_DELLINK message: %m\n");
600 r
= sd_netlink_call_async(rtnl
, NULL
, m
, __nw_link_destroy
,
601 __nw_link_unref
, nw_link_ref(link
), -1, NULL
);
603 ERROR("Could not send rtnetlink message: %m\n");
609 sd_netlink_message_unref(m
);
616 static int nw_link_uevent_device_is_renaming(sd_device
* device
) {
619 r
= sd_device_get_property_value(device
, "ID_RENAMING", NULL
);
632 int nw_link_handle_uevent(nw_link
* link
, sd_device
* device
, sd_device_action_t action
) {
635 // Check if the device is renaming
636 r
= nw_link_uevent_device_is_renaming(device
);
638 // Not renaming - Fallthrough
643 DEBUG("Device is renaming, skipping initialization\n");
647 ERROR("Could not determine whether the device is being renamed: %s\n",
652 // We need to remove or replace the stored device as it is now outdated
654 sd_device_unref(link
->device
);
656 // If the device has been removed, we remove its reference
657 if (action
== SD_DEVICE_REMOVE
)
660 // Otherwise we just store the new one
662 link
->device
= sd_device_ref(device
);
670 static int nw_link_device_to_json(nw_link
* link
, struct json_object
* o
) {
671 const char* driver
= NULL
;
672 const char* vendor
= NULL
;
673 const char* model
= NULL
;
677 r
= sd_device_get_driver(link
->device
, &driver
);
678 if (r
< 0 && r
!= -ENOENT
)
683 r
= json_object_add_string(o
, "Driver", driver
);
689 r
= sd_device_get_property_value(link
->device
, "ID_VENDOR_FROM_DATABASE", &vendor
);
691 r
= sd_device_get_property_value(link
->device
, "ID_VENDOR", &vendor
);
692 if (r
< 0 && r
!= -ENOENT
)
698 r
= json_object_add_string(o
, "Vendor", vendor
);
704 r
= sd_device_get_property_value(link
->device
, "ID_MODEL_FROM_DATABASE", &model
);
706 r
= sd_device_get_property_value(link
->device
, "ID_MODEL", &model
);
707 if (r
< 0 && r
!= -ENOENT
)
713 r
= json_object_add_string(o
, "Model", model
);
721 int nw_link_to_json(nw_link
* link
, struct json_object
* o
) {
725 r
= json_object_add_int64(o
, "LinkIndex", link
->ifindex
);
729 // Add sd-device stuff
730 r
= nw_link_device_to_json(link
, o
);