]> git.ipfire.org Git - people/ms/network.git/blame - src/networkd/link.c
link: Skip uevent when the device is renaming
[people/ms/network.git] / src / networkd / link.c
CommitLineData
87a1e1e0
MT
1/*#############################################################################
2# #
3# IPFire.org - A linux based firewall #
4# Copyright (C) 2023 IPFire Network Development Team #
5# #
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. #
10# #
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. #
15# #
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/>. #
18# #
19#############################################################################*/
20
dfd49c2c 21#include <linux/if.h>
15240e08 22#include <linux/if_link.h>
87a1e1e0
MT
23#include <stddef.h>
24#include <stdlib.h>
f650f6cd 25#include <string.h>
87a1e1e0 26
cf75e1db 27#include <systemd/sd-device.h>
766f08ca
MT
28#include <systemd/sd-netlink.h>
29
87a1e1e0 30#include "daemon.h"
69467266 31#include "json.h"
87a1e1e0 32#include "link.h"
766f08ca 33#include "links.h"
eaf649ee 34#include "logging.h"
f650f6cd 35#include "string.h"
87a1e1e0
MT
36
37struct nw_link {
2361667e 38 nw_daemon* daemon;
87a1e1e0
MT
39 int nrefs;
40
41 // Interface Index
42 int ifindex;
f650f6cd
MT
43
44 // Interface Name
dfd49c2c 45 char ifname[IFNAMSIZ];
f37df5ea 46
371b836a
MT
47 enum nw_link_state {
48 NW_LINK_UNKNOWN = 0,
49 NW_LINK_DESTROYED,
50 } state;
51
cf75e1db
MT
52 // Device
53 struct sd_device* device;
54
15240e08
MT
55 // Stats
56 struct rtnl_link_stats64 stats64;
57
f37df5ea
MT
58 // MTU
59 uint32_t mtu;
60 uint32_t min_mtu;
61 uint32_t max_mtu;
c65300b4
MT
62
63 // Flags
64 unsigned int flags;
69ef50c7 65 uint8_t operstate;
87a1e1e0
MT
66};
67
cf75e1db
MT
68static int nw_link_setup_device(nw_link* link) {
69 int r;
70
71 // Fetch sd-device
72 r = sd_device_new_from_ifindex(&link->device, link->ifindex);
73 if (r < 0) {
74 ERROR("Could not fetch sd-device for link %d: %s\n", link->ifindex, strerror(-r));
75 return r;
76 }
77
78 return 0;
79}
80
81static void nw_link_free(nw_link* link) {
82 DEBUG("Freeing link (ifindex = %d)\n", link->ifindex);
83
84 if (link->device)
85 sd_device_unref(link->device);
86 if (link->daemon)
87 nw_daemon_unref(link->daemon);
88}
89
2361667e 90int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
cf75e1db
MT
91 int r;
92
87a1e1e0 93 // Allocate a new object
2361667e 94 nw_link* l = calloc(1, sizeof(*l));
87a1e1e0 95 if (!l)
cf75e1db 96 return -errno;
87a1e1e0
MT
97
98 // Store a reference to the daemon
99 l->daemon = nw_daemon_ref(daemon);
100
101 // Initialize the reference counter
102 l->nrefs = 1;
103
104 // Store the ifindex
105 l->ifindex = ifindex;
106
766f08ca
MT
107 DEBUG("New link allocated (ifindex = %d)\n", l->ifindex);
108
cf75e1db
MT
109 r = nw_link_setup_device(l);
110 if (r < 0)
111 goto ERROR;
87a1e1e0 112
cf75e1db 113 *link = l;
87a1e1e0 114 return 0;
766f08ca 115
cf75e1db
MT
116ERROR:
117 nw_link_free(l);
118 return r;
87a1e1e0
MT
119}
120
2361667e 121nw_link* nw_link_ref(nw_link* link) {
87a1e1e0
MT
122 link->nrefs++;
123
124 return link;
125}
126
2361667e 127nw_link* nw_link_unref(nw_link* link) {
87a1e1e0
MT
128 if (--link->nrefs > 0)
129 return link;
130
131 nw_link_free(link);
132 return NULL;
133}
eaf649ee 134
371b836a
MT
135/*
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.
138*/
139static void __nw_link_unref(void* data) {
140 nw_link* link = (nw_link*)data;
141
142 nw_link_unref(link);
143}
144
2361667e 145int nw_link_ifindex(nw_link* link) {
766f08ca
MT
146 return link->ifindex;
147}
eaf649ee 148
207abe4e 149const char* nw_link_ifname(nw_link* link) {
611d4aca
MT
150 // Return NULL if name isn't set
151 if (!*link->ifname)
152 return NULL;
153
207abe4e
MT
154 return link->ifname;
155}
156
15240e08
MT
157// Stats
158
159const struct rtnl_link_stats64* nw_link_get_stats64(nw_link* link) {
160 return &link->stats64;
161}
162
163static 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;
166 int r;
167
168 sd_netlink* rtnl = nw_daemon_get_rtnl(link->daemon);
169 if (!rtnl)
170 return 1;
171
172 // Create a new message
173 r = sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, link->ifindex);
174 if (r < 0) {
175 ERROR("Could not allocate RTM_GETLINK message: %m\n");
176 goto ERROR;
177 }
178
179 // Send the message
180 r = sd_netlink_call_async(rtnl, NULL, m, callback,
181 __nw_link_unref, nw_link_ref(link), -1, NULL);
182 if (r < 0) {
183 ERROR("Could not send rtnetlink message: %m\n");
184 goto ERROR;
185 }
186
187ERROR:
188 if (m)
189 sd_netlink_message_unref(m);
190
191 return r;
192}
193
194static int __nw_link_update_stats(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
195 nw_link* link = (nw_link*)data;
196 int r;
197
198 // Fetch the stats
199 r = sd_netlink_message_read(m, IFLA_STATS64, sizeof(link->stats64), &link->stats64);
200 if (r < 0)
201 return r;
202
203 DEBUG("Link %d: Stats updated\n", link->ifindex);
204
205 // Log stats
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);
216
217 // Notify ports that stats have been updated
218 r = nw_daemon_ports_walk(link->daemon, __nw_port_update_stats, link);
219 if (r)
220 return r;
221
222 // Notify zones that stats have been updated
223 r = nw_daemon_zones_walk(link->daemon, __nw_zone_update_stats, link);
224 if (r)
225 return r;
226
227 return 0;
228}
229
230int nw_link_update_stats(nw_link* link) {
231 return nw_link_call_getlink(link, __nw_link_update_stats);
232}
233
dfd49c2c
MT
234// Carrier
235
236int nw_link_has_carrier(nw_link* link) {
237 return link->operstate == IF_OPER_UP;
238}
239
240static int nw_link_carrier_gained(nw_link* link) {
241 return 0; // XXX TODO
242}
243
244static int nw_link_carrier_lost(nw_link* link) {
245 return 0; // XXX TODO
246}
247
4d7437dd
MT
248static int nw_link_initialize(nw_link* link) {
249 sd_device *device = NULL;
250 int r;
251
252 // Fetch device
253 r = sd_device_new_from_ifindex(&device, link->ifindex);
254 if (r < 0) {
255 WARNING("Could not find device for link %d: %s\n", link->ifindex, strerror(-r));
256 r = 0;
257 goto ERROR;
258 }
259
260 // Check if device is initialized
261 r = sd_device_get_is_initialized(device);
262 switch (r) {
263 // Initialized - fallthrough
264 case 0:
265 break;
266
267 case 1:
268 DEBUG("The device has not been initialized, yet\n");
269 r = 0;
270 goto ERROR;
271
272 default:
273 WARNING("Could not check whether device is initialized, ignoring: %s\n",
274 strerror(-r));
275 goto ERROR;
276 }
277
278 // XXX Check renaming?!
279
280 if (link->device)
281 sd_device_unref(link->device);
282
283 // Store the device
284 link->device = sd_device_ref(device);
285
286 DEBUG("Link %d has been initialized\n", link->ifindex);
287
288ERROR:
289 if (device)
290 sd_device_unref(device);
291
292 return r;
293}
294
2361667e 295static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
f650f6cd
MT
296 const char* ifname = NULL;
297 int r;
298
299 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
300 if (r < 0) {
301 ERROR("Could not read link name for link %d: %m\n", link->ifindex);
302 return 1;
303 }
304
305 // Do nothing if the name is already set
306 if (strcmp(link->ifname, ifname) == 0)
307 return 0;
308
309 // Otherwise update the name
310 r = nw_string_set(link->ifname, ifname);
311 if (r) {
312 ERROR("Could not set link name: %m\n");
313 return 1;
314 }
315
316 DEBUG("Link %d has been renamed to '%s'\n", link->ifindex, link->ifname);
317
611d4aca
MT
318 // Assign link to ports
319 nw_daemon_ports_walk(link->daemon, __nw_port_set_link, link);
320
321 // Assign link to zones
322 nw_daemon_zones_walk(link->daemon, __nw_zone_set_link, link);
323
f650f6cd
MT
324 return 0;
325}
326
2361667e 327static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
f37df5ea
MT
328 uint32_t mtu = 0;
329 uint32_t min_mtu = 0;
330 uint32_t max_mtu = 0;
331 int r;
332
333 // Read the MTU
334 r = sd_netlink_message_read_u32(message, IFLA_MTU, &mtu);
335 if (r < 0) {
336 ERROR("Could not read MTU for link %d: %m\n", link->ifindex);
337 return r;
338 }
339
340 // Read the minimum MTU
341 r = sd_netlink_message_read_u32(message, IFLA_MIN_MTU, &min_mtu);
342 if (r < 0) {
343 ERROR("Could not read the minimum MTU for link %d: %m\n", link->ifindex);
344 return r;
345 }
346
347 // Read the maximum MTU
348 r = sd_netlink_message_read_u32(message, IFLA_MAX_MTU, &max_mtu);
349 if (r < 0) {
350 ERROR("Could not read the maximum MTU for link %d: %m\n", link->ifindex);
351 return r;
352 }
353
354 // Set the maximum MTU to infinity
355 if (!max_mtu)
356 max_mtu = UINT32_MAX;
357
358 // Store min/max MTU
359 link->min_mtu = min_mtu;
360 link->max_mtu = max_mtu;
361
362 // End here, if the MTU has not been changed
363 if (link->mtu == mtu)
364 return 0;
365
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);
368
369 // Store MTU
370 link->mtu = mtu;
371
372 return 0;
373}
374
c65300b4
MT
375static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
376 unsigned int flags = 0;
69ef50c7 377 uint8_t operstate = 0;
c65300b4
MT
378 int r;
379
380 // Fetch flags
381 r = sd_rtnl_message_link_get_flags(message, &flags);
382 if (r < 0) {
1a70a686 383 DEBUG("Could not read link flags: %m\n");
c65300b4
MT
384 return 1;
385 }
386
c403eb4c 387#if 0
69ef50c7
MT
388 // Fetch operstate
389 r = sd_netlink_message_read_u8(message, IFLA_OPERSTATE, &operstate);
390 if (r < 1) {
391 ERROR("Could not read operstate: %m\n");
392 return 1;
393 }
c403eb4c 394#endif
69ef50c7 395
c65300b4 396 // End here if there have been no changes
69ef50c7 397 if (link->flags == flags && link->operstate == operstate)
c65300b4
MT
398 return 0;
399
400 // XXX We should log any changes here
401
dfd49c2c
MT
402 // Fetch current carrier state
403 const int had_carrier = nw_link_has_carrier(link);
404
69ef50c7 405 // Store the new flags & operstate
c65300b4 406 link->flags = flags;
69ef50c7
MT
407 link->operstate = operstate;
408
dfd49c2c
MT
409 // Notify if carrier was gained or lost
410 if (!had_carrier && nw_link_has_carrier(link)) {
411 r = nw_link_carrier_gained(link);
412 if (r < 0)
413 return r;
414
415 } else if (had_carrier && !nw_link_has_carrier(link)) {
416 r = nw_link_carrier_lost(link);
417 if (r < 0)
418 return r;
419 }
c65300b4
MT
420
421 return 0;
422}
423
f650f6cd
MT
424/*
425 This function is called whenever anything changes, so that we can
426 update our internal link object.
427*/
2361667e 428static int nw_link_update(nw_link* link, sd_netlink_message* message) {
f650f6cd
MT
429 int r;
430
431 // Update the interface name
432 r = nw_link_update_ifname(link, message);
433 if (r)
434 return r;
435
f37df5ea
MT
436 // Update the MTU
437 r = nw_link_update_mtu(link, message);
438 if (r)
439 return r;
440
c65300b4
MT
441 // Update flags
442 r = nw_link_update_flags(link, message);
443 if (r)
444 return r;
445
f650f6cd
MT
446 return 0;
447}
448
766f08ca 449int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
2361667e
MT
450 nw_links* links = NULL;
451 nw_link* link = NULL;
766f08ca
MT
452 const char* ifname = NULL;
453 int ifindex;
454 uint16_t type;
455 int r;
456
2361667e 457 nw_daemon* daemon = (nw_daemon*)data;
766f08ca
MT
458
459 // Fetch links
460 links = nw_daemon_links(daemon);
461 if (!links) {
462 r = 1;
463 goto ERROR;
464 }
465
466 // Check if this message could be received
467 if (sd_netlink_message_is_error(message)) {
468 r = sd_netlink_message_get_errno(message);
469 if (r < 0)
470 ERROR("Could not receive link message: %m\n");
471
472 goto IGNORE;
473 }
474
475 // Fetch the message type
476 r = sd_netlink_message_get_type(message, &type);
477 if (r < 0) {
478 ERROR("Could not fetch message type: %m\n");
479 goto IGNORE;
480 }
481
482 // Check type
483 switch (type) {
484 case RTM_NEWLINK:
485 case RTM_DELLINK:
486 break;
487
488 default:
489 ERROR("Received an unexpected message (type %u)\n", type);
490 goto IGNORE;
491 }
492
493 // Fetch the interface index
494 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
495 if (r < 0) {
496 ERROR("Could not fetch ifindex: %m\n");
497 goto IGNORE;
498 }
499
500 // Check interface index
501 if (ifindex <= 0) {
502 ERROR("Received an invalid ifindex\n");
503 goto IGNORE;
504 }
505
506 // Fetch the interface name
507 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
508 if (r < 0) {
509 ERROR("Received a netlink message without interface name: %m\n");
510 goto IGNORE;
511 }
512
513 // Try finding an existing link
514 link = nw_daemon_get_link_by_ifindex(daemon, ifindex);
515
516 switch (type) {
517 case RTM_NEWLINK:
518 // If the link doesn't exist, create it
519 if (!link) {
520 r = nw_link_create(&link, daemon, ifindex);
521 if (r) {
522 ERROR("Could not create link: %m\n");
523 goto ERROR;
524 }
b8a1a68c 525
4d7437dd
MT
526 // Initialize the link
527 r = nw_link_initialize(link);
528 if (r < 0)
529 goto ERROR;
530
b8a1a68c
MT
531 // Add it to the list
532 r = nw_links_add_link(links, link);
533 if (r)
534 goto ERROR;
766f08ca
MT
535 }
536
f650f6cd
MT
537 // Import any data from the netlink message
538 r = nw_link_update(link, message);
539 if (r)
540 goto ERROR;
766f08ca 541
766f08ca
MT
542 break;
543
544 case RTM_DELLINK:
545 if (link)
546 nw_links_drop_link(links, link);
547
548 break;
549 }
550
551IGNORE:
552 r = 0;
553
554ERROR:
555 if (links)
556 nw_links_unref(links);
557 if (link)
558 nw_link_unref(link);
559
560 return r;
eaf649ee 561}
371b836a
MT
562
563static int __nw_link_destroy(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
564 nw_link* link = (nw_link*)data;
565 int r;
566
567 // Check if the operation was successful
568 r = sd_netlink_message_get_errno(m);
569 if (r < 0) {
570 ERROR("Could not remove link %d: %m\n", link->ifindex);
571 // XXX We should extract the error message
572
573 return 0;
574 }
575
576 // Mark this link as destroyed
577 link->state = NW_LINK_DESTROYED;
578
579 return 0;
580}
581
582int nw_link_destroy(nw_link* link) {
583 sd_netlink_message* m = NULL;
584 int r;
585
586 sd_netlink* rtnl = nw_daemon_get_rtnl(link->daemon);
587 if (!rtnl)
588 return 1;
589
590 DEBUG("Destroying link %d\n", link->ifindex);
591
592 // Create a new message
593 r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, link->ifindex);
594 if (r < 0) {
595 ERROR("Could not allocate RTM_DELLINK message: %m\n");
596 goto ERROR;
597 }
598
599 // Send the message
600 r = sd_netlink_call_async(rtnl, NULL, m, __nw_link_destroy,
601 __nw_link_unref, nw_link_ref(link), -1, NULL);
602 if (r < 0) {
603 ERROR("Could not send rtnetlink message: %m\n");
604 goto ERROR;
605 }
606
607ERROR:
608 if (m)
609 sd_netlink_message_unref(m);
610
611 return r;
612}
69467266 613
40e2c8ca
MT
614// uevent
615
7d01b16d
MT
616static int nw_link_uevent_device_is_renaming(sd_device* device) {
617 int r;
618
619 r = sd_device_get_property_value(device, "ID_RENAMING", NULL);
620 switch (r) {
621 case -ENOENT:
622 return 0;
623
624 case 0:
625 return 1;
626
627 default:
628 return r;
629 }
630}
631
40e2c8ca 632int nw_link_handle_uevent(nw_link* link, sd_device* device, sd_device_action_t action) {
7d01b16d
MT
633 int r;
634
635 // Check if the device is renaming
636 r = nw_link_uevent_device_is_renaming(device);
637 switch (r) {
638 // Not renaming - Fallthrough
639 case 0:
640 break;
641
642 case 1:
643 DEBUG("Device is renaming, skipping initialization\n");
644 return 0;
645
646 default:
647 ERROR("Could not determine whether the device is being renamed: %s\n",
648 strerror(-r));
649 return r;
650 }
651
40e2c8ca
MT
652 // We need to remove or replace the stored device as it is now outdated
653 if (link->device) {
654 sd_device_unref(link->device);
655
656 // If the device has been removed, we remove its reference
657 if (action == SD_DEVICE_REMOVE)
658 link->device = NULL;
659
660 // Otherwise we just store the new one
661 else
662 link->device = sd_device_ref(device);
663 }
664
665 return 0;
666}
667
69467266
MT
668// JSON
669
cf75e1db
MT
670static 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;
674 int r;
675
676 // Fetch driver
677 r = sd_device_get_driver(link->device, &driver);
678 if (r < 0 && r != -ENOENT)
679 return r;
680
681 // Add driver
682 if (driver) {
683 r = json_object_add_string(o, "Driver", driver);
684 if (r < 0)
685 return r;
686 }
687
688 // Fetch vendor
689 r = sd_device_get_property_value(link->device, "ID_VENDOR_FROM_DATABASE", &vendor);
690 if (r < 0) {
691 r = sd_device_get_property_value(link->device, "ID_VENDOR", &vendor);
692 if (r < 0 && r != -ENOENT)
693 return r;
694 }
695
696 // Add vendor
697 if (vendor) {
698 r = json_object_add_string(o, "Vendor", vendor);
699 if (r < 0)
700 return r;
701 }
702
703 // Fetch model
704 r = sd_device_get_property_value(link->device, "ID_MODEL_FROM_DATABASE", &model);
705 if (r < 0) {
706 r = sd_device_get_property_value(link->device, "ID_MODEL", &model);
707 if (r < 0 && r != -ENOENT)
708 return r;
709 }
710
711 // Add model
712 if (model) {
713 r = json_object_add_string(o, "Model", model);
714 if (r < 0)
715 return r;
716 }
717
718 return 0;
719}
720
69467266
MT
721int nw_link_to_json(nw_link* link, struct json_object* o) {
722 int r;
723
724 // Add ifindex
725 r = json_object_add_int64(o, "LinkIndex", link->ifindex);
726 if (r < 0)
727 return r;
728
cf75e1db
MT
729 // Add sd-device stuff
730 r = nw_link_device_to_json(link, o);
731 if (r < 0)
732 return r;
733
69467266
MT
734 return 0;
735}