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-netlink.h>
43 char ifname
[IFNAMSIZ
];
51 struct rtnl_link_stats64 stats64
;
63 int nw_link_create(nw_link
** link
, nw_daemon
* daemon
, int ifindex
) {
64 // Allocate a new object
65 nw_link
* l
= calloc(1, sizeof(*l
));
69 // Store a reference to the daemon
70 l
->daemon
= nw_daemon_ref(daemon
);
72 // Initialize the reference counter
78 DEBUG("New link allocated (ifindex = %d)\n", l
->ifindex
);
85 static void nw_link_free(nw_link
* link
) {
86 DEBUG("Freeing link (ifindex = %d)\n", link
->ifindex
);
89 nw_daemon_unref(link
->daemon
);
92 nw_link
* nw_link_ref(nw_link
* link
) {
98 nw_link
* nw_link_unref(nw_link
* link
) {
99 if (--link
->nrefs
> 0)
107 This is a helper function for when we pass a reference to the event loop
108 it will have to dereference the link instance later.
110 static void __nw_link_unref(void* data
) {
111 nw_link
* link
= (nw_link
*)data
;
116 int nw_link_ifindex(nw_link
* link
) {
117 return link
->ifindex
;
120 const char* nw_link_ifname(nw_link
* link
) {
121 // Return NULL if name isn't set
130 const struct rtnl_link_stats64
* nw_link_get_stats64(nw_link
* link
) {
131 return &link
->stats64
;
134 static int nw_link_call_getlink(nw_link
* link
,
135 int (*callback
)(sd_netlink
* rtnl
, sd_netlink_message
* m
, void* data
)) {
136 sd_netlink_message
* m
= NULL
;
139 sd_netlink
* rtnl
= nw_daemon_get_rtnl(link
->daemon
);
143 // Create a new message
144 r
= sd_rtnl_message_new_link(rtnl
, &m
, RTM_GETLINK
, link
->ifindex
);
146 ERROR("Could not allocate RTM_GETLINK message: %m\n");
151 r
= sd_netlink_call_async(rtnl
, NULL
, m
, callback
,
152 __nw_link_unref
, nw_link_ref(link
), -1, NULL
);
154 ERROR("Could not send rtnetlink message: %m\n");
160 sd_netlink_message_unref(m
);
165 static int __nw_link_update_stats(sd_netlink
* rtnl
, sd_netlink_message
* m
, void* data
) {
166 nw_link
* link
= (nw_link
*)data
;
170 r
= sd_netlink_message_read(m
, IFLA_STATS64
, sizeof(link
->stats64
), &link
->stats64
);
174 DEBUG("Link %d: Stats updated\n", link
->ifindex
);
177 DEBUG(" Packets : RX: %12llu, TX: %12llu\n",
178 link
->stats64
.rx_packets
, link
->stats64
.tx_packets
);
179 DEBUG(" Bytes : RX: %12llu, TX: %12llu\n",
180 link
->stats64
.rx_bytes
, link
->stats64
.tx_bytes
);
181 DEBUG(" Errors : RX: %12llu, TX: %12llu\n",
182 link
->stats64
.rx_errors
, link
->stats64
.tx_errors
);
183 DEBUG(" Dropped : RX: %12llu, TX: %12llu\n",
184 link
->stats64
.rx_dropped
, link
->stats64
.rx_dropped
);
185 DEBUG(" Multicast : %llu\n", link
->stats64
.multicast
);
186 DEBUG(" Collisions : %llu\n", link
->stats64
.collisions
);
188 // Notify ports that stats have been updated
189 r
= nw_daemon_ports_walk(link
->daemon
, __nw_port_update_stats
, link
);
193 // Notify zones that stats have been updated
194 r
= nw_daemon_zones_walk(link
->daemon
, __nw_zone_update_stats
, link
);
201 int nw_link_update_stats(nw_link
* link
) {
202 return nw_link_call_getlink(link
, __nw_link_update_stats
);
207 int nw_link_has_carrier(nw_link
* link
) {
208 return link
->operstate
== IF_OPER_UP
;
211 static int nw_link_carrier_gained(nw_link
* link
) {
212 return 0; // XXX TODO
215 static int nw_link_carrier_lost(nw_link
* link
) {
216 return 0; // XXX TODO
219 static int nw_link_update_ifname(nw_link
* link
, sd_netlink_message
* message
) {
220 const char* ifname
= NULL
;
223 r
= sd_netlink_message_read_string(message
, IFLA_IFNAME
, &ifname
);
225 ERROR("Could not read link name for link %d: %m\n", link
->ifindex
);
229 // Do nothing if the name is already set
230 if (strcmp(link
->ifname
, ifname
) == 0)
233 // Otherwise update the name
234 r
= nw_string_set(link
->ifname
, ifname
);
236 ERROR("Could not set link name: %m\n");
240 DEBUG("Link %d has been renamed to '%s'\n", link
->ifindex
, link
->ifname
);
242 // Assign link to ports
243 nw_daemon_ports_walk(link
->daemon
, __nw_port_set_link
, link
);
245 // Assign link to zones
246 nw_daemon_zones_walk(link
->daemon
, __nw_zone_set_link
, link
);
251 static int nw_link_update_mtu(nw_link
* link
, sd_netlink_message
* message
) {
253 uint32_t min_mtu
= 0;
254 uint32_t max_mtu
= 0;
258 r
= sd_netlink_message_read_u32(message
, IFLA_MTU
, &mtu
);
260 ERROR("Could not read MTU for link %d: %m\n", link
->ifindex
);
264 // Read the minimum MTU
265 r
= sd_netlink_message_read_u32(message
, IFLA_MIN_MTU
, &min_mtu
);
267 ERROR("Could not read the minimum MTU for link %d: %m\n", link
->ifindex
);
271 // Read the maximum MTU
272 r
= sd_netlink_message_read_u32(message
, IFLA_MAX_MTU
, &max_mtu
);
274 ERROR("Could not read the maximum MTU for link %d: %m\n", link
->ifindex
);
278 // Set the maximum MTU to infinity
280 max_mtu
= UINT32_MAX
;
283 link
->min_mtu
= min_mtu
;
284 link
->max_mtu
= max_mtu
;
286 // End here, if the MTU has not been changed
287 if (link
->mtu
== mtu
)
290 DEBUG("Link %d: MTU has changed to %" PRIu32
" (min: %" PRIu32
", max: %" PRIu32
")\n",
291 link
->ifindex
, link
->mtu
, link
->min_mtu
, link
->max_mtu
);
299 static int nw_link_update_flags(nw_link
* link
, sd_netlink_message
* message
) {
300 unsigned int flags
= 0;
301 uint8_t operstate
= 0;
305 r
= sd_rtnl_message_link_get_flags(message
, &flags
);
307 DEBUG("Could not read link flags: %m\n");
313 r
= sd_netlink_message_read_u8(message
, IFLA_OPERSTATE
, &operstate
);
315 ERROR("Could not read operstate: %m\n");
320 // End here if there have been no changes
321 if (link
->flags
== flags
&& link
->operstate
== operstate
)
324 // XXX We should log any changes here
326 // Fetch current carrier state
327 const int had_carrier
= nw_link_has_carrier(link
);
329 // Store the new flags & operstate
331 link
->operstate
= operstate
;
333 // Notify if carrier was gained or lost
334 if (!had_carrier
&& nw_link_has_carrier(link
)) {
335 r
= nw_link_carrier_gained(link
);
339 } else if (had_carrier
&& !nw_link_has_carrier(link
)) {
340 r
= nw_link_carrier_lost(link
);
349 This function is called whenever anything changes, so that we can
350 update our internal link object.
352 static int nw_link_update(nw_link
* link
, sd_netlink_message
* message
) {
355 // Update the interface name
356 r
= nw_link_update_ifname(link
, message
);
361 r
= nw_link_update_mtu(link
, message
);
366 r
= nw_link_update_flags(link
, message
);
373 int nw_link_process(sd_netlink
* rtnl
, sd_netlink_message
* message
, void* data
) {
374 nw_links
* links
= NULL
;
375 nw_link
* link
= NULL
;
376 const char* ifname
= NULL
;
381 nw_daemon
* daemon
= (nw_daemon
*)data
;
384 links
= nw_daemon_links(daemon
);
390 // Check if this message could be received
391 if (sd_netlink_message_is_error(message
)) {
392 r
= sd_netlink_message_get_errno(message
);
394 ERROR("Could not receive link message: %m\n");
399 // Fetch the message type
400 r
= sd_netlink_message_get_type(message
, &type
);
402 ERROR("Could not fetch message type: %m\n");
413 ERROR("Received an unexpected message (type %u)\n", type
);
417 // Fetch the interface index
418 r
= sd_rtnl_message_link_get_ifindex(message
, &ifindex
);
420 ERROR("Could not fetch ifindex: %m\n");
424 // Check interface index
426 ERROR("Received an invalid ifindex\n");
430 // Fetch the interface name
431 r
= sd_netlink_message_read_string(message
, IFLA_IFNAME
, &ifname
);
433 ERROR("Received a netlink message without interface name: %m\n");
437 // Try finding an existing link
438 link
= nw_daemon_get_link_by_ifindex(daemon
, ifindex
);
442 // If the link doesn't exist, create it
444 r
= nw_link_create(&link
, daemon
, ifindex
);
446 ERROR("Could not create link: %m\n");
450 // Add it to the list
451 r
= nw_links_add_link(links
, link
);
456 // Import any data from the netlink message
457 r
= nw_link_update(link
, message
);
465 nw_links_drop_link(links
, link
);
475 nw_links_unref(links
);
482 static int __nw_link_destroy(sd_netlink
* rtnl
, sd_netlink_message
* m
, void* data
) {
483 nw_link
* link
= (nw_link
*)data
;
486 // Check if the operation was successful
487 r
= sd_netlink_message_get_errno(m
);
489 ERROR("Could not remove link %d: %m\n", link
->ifindex
);
490 // XXX We should extract the error message
495 // Mark this link as destroyed
496 link
->state
= NW_LINK_DESTROYED
;
501 int nw_link_destroy(nw_link
* link
) {
502 sd_netlink_message
* m
= NULL
;
505 sd_netlink
* rtnl
= nw_daemon_get_rtnl(link
->daemon
);
509 DEBUG("Destroying link %d\n", link
->ifindex
);
511 // Create a new message
512 r
= sd_rtnl_message_new_link(rtnl
, &m
, RTM_DELLINK
, link
->ifindex
);
514 ERROR("Could not allocate RTM_DELLINK message: %m\n");
519 r
= sd_netlink_call_async(rtnl
, NULL
, m
, __nw_link_destroy
,
520 __nw_link_unref
, nw_link_ref(link
), -1, NULL
);
522 ERROR("Could not send rtnetlink message: %m\n");
528 sd_netlink_message_unref(m
);