]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/link.c
Makefile: Fix typo in localstatedir
[people/ms/network.git] / src / networkd / link.c
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
21 #include <linux/if.h>
22 #include <linux/if_link.h>
23 #include <stddef.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <systemd/sd-device.h>
28 #include <systemd/sd-netlink.h>
29
30 #include "daemon.h"
31 #include "json.h"
32 #include "link.h"
33 #include "links.h"
34 #include "logging.h"
35 #include "string.h"
36
37 struct nw_link {
38 nw_daemon* daemon;
39 int nrefs;
40
41 // Interface Index
42 int ifindex;
43
44 // Interface Name
45 char ifname[IFNAMSIZ];
46
47 enum nw_link_state {
48 NW_LINK_UNKNOWN = 0,
49 NW_LINK_DESTROYED,
50 } state;
51
52 // Device
53 struct sd_device* device;
54
55 // Stats
56 struct rtnl_link_stats64 stats64;
57
58 // MTU
59 uint32_t mtu;
60 uint32_t min_mtu;
61 uint32_t max_mtu;
62
63 // Flags
64 unsigned int flags;
65 uint8_t operstate;
66 };
67
68 static 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
81 static 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
90 int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
91 int r;
92
93 // Allocate a new object
94 nw_link* l = calloc(1, sizeof(*l));
95 if (!l)
96 return -errno;
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
107 DEBUG("New link allocated (ifindex = %d)\n", l->ifindex);
108
109 r = nw_link_setup_device(l);
110 if (r < 0)
111 goto ERROR;
112
113 *link = l;
114 return 0;
115
116 ERROR:
117 nw_link_free(l);
118 return r;
119 }
120
121 nw_link* nw_link_ref(nw_link* link) {
122 link->nrefs++;
123
124 return link;
125 }
126
127 nw_link* nw_link_unref(nw_link* link) {
128 if (--link->nrefs > 0)
129 return link;
130
131 nw_link_free(link);
132 return NULL;
133 }
134
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 */
139 static void __nw_link_unref(void* data) {
140 nw_link* link = (nw_link*)data;
141
142 nw_link_unref(link);
143 }
144
145 int nw_link_ifindex(nw_link* link) {
146 return link->ifindex;
147 }
148
149 const char* nw_link_ifname(nw_link* link) {
150 // Return NULL if name isn't set
151 if (!*link->ifname)
152 return NULL;
153
154 return link->ifname;
155 }
156
157 // Stats
158
159 const struct rtnl_link_stats64* nw_link_get_stats64(nw_link* link) {
160 return &link->stats64;
161 }
162
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;
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
187 ERROR:
188 if (m)
189 sd_netlink_message_unref(m);
190
191 return r;
192 }
193
194 static 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
230 int nw_link_update_stats(nw_link* link) {
231 return nw_link_call_getlink(link, __nw_link_update_stats);
232 }
233
234 // Carrier
235
236 int nw_link_has_carrier(nw_link* link) {
237 return link->operstate == IF_OPER_UP;
238 }
239
240 static int nw_link_carrier_gained(nw_link* link) {
241 return 0; // XXX TODO
242 }
243
244 static int nw_link_carrier_lost(nw_link* link) {
245 return 0; // XXX TODO
246 }
247
248 static 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
288 ERROR:
289 if (device)
290 sd_device_unref(device);
291
292 return r;
293 }
294
295 static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
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
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
324 return 0;
325 }
326
327 static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
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
375 static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
376 unsigned int flags = 0;
377 uint8_t operstate = 0;
378 int r;
379
380 // Fetch flags
381 r = sd_rtnl_message_link_get_flags(message, &flags);
382 if (r < 0) {
383 DEBUG("Could not read link flags: %m\n");
384 return 1;
385 }
386
387 #if 0
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 }
394 #endif
395
396 // End here if there have been no changes
397 if (link->flags == flags && link->operstate == operstate)
398 return 0;
399
400 // XXX We should log any changes here
401
402 // Fetch current carrier state
403 const int had_carrier = nw_link_has_carrier(link);
404
405 // Store the new flags & operstate
406 link->flags = flags;
407 link->operstate = operstate;
408
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 }
420
421 return 0;
422 }
423
424 /*
425 This function is called whenever anything changes, so that we can
426 update our internal link object.
427 */
428 static int nw_link_update(nw_link* link, sd_netlink_message* message) {
429 int r;
430
431 // Update the interface name
432 r = nw_link_update_ifname(link, message);
433 if (r)
434 return r;
435
436 // Update the MTU
437 r = nw_link_update_mtu(link, message);
438 if (r)
439 return r;
440
441 // Update flags
442 r = nw_link_update_flags(link, message);
443 if (r)
444 return r;
445
446 return 0;
447 }
448
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;
453 int ifindex;
454 uint16_t type;
455 int r;
456
457 nw_daemon* daemon = (nw_daemon*)data;
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 }
525
526 // Initialize the link
527 r = nw_link_initialize(link);
528 if (r < 0)
529 goto ERROR;
530
531 // Add it to the list
532 r = nw_links_add_link(links, link);
533 if (r)
534 goto ERROR;
535 }
536
537 // Import any data from the netlink message
538 r = nw_link_update(link, message);
539 if (r)
540 goto ERROR;
541
542 break;
543
544 case RTM_DELLINK:
545 if (link)
546 nw_links_drop_link(links, link);
547
548 break;
549 }
550
551 IGNORE:
552 r = 0;
553
554 ERROR:
555 if (links)
556 nw_links_unref(links);
557 if (link)
558 nw_link_unref(link);
559
560 return r;
561 }
562
563 static 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
582 int 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
607 ERROR:
608 if (m)
609 sd_netlink_message_unref(m);
610
611 return r;
612 }
613
614 // uevent
615
616 static 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
632 int nw_link_handle_uevent(nw_link* link, sd_device* device, sd_device_action_t action) {
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
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
668 // JSON
669
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;
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
721 int 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
729 // Add sd-device stuff
730 r = nw_link_device_to_json(link, o);
731 if (r < 0)
732 return r;
733
734 return 0;
735 }