]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/link.c
ports: Add scaffolding for physical Ethernet interfaces
[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_update_ifname(nw_link* link, sd_netlink_message* message) {
249 const char* ifname = NULL;
250 int r;
251
252 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
253 if (r < 0) {
254 ERROR("Could not read link name for link %d: %m\n", link->ifindex);
255 return 1;
256 }
257
258 // Do nothing if the name is already set
259 if (strcmp(link->ifname, ifname) == 0)
260 return 0;
261
262 // Otherwise update the name
263 r = nw_string_set(link->ifname, ifname);
264 if (r) {
265 ERROR("Could not set link name: %m\n");
266 return 1;
267 }
268
269 DEBUG("Link %d has been renamed to '%s'\n", link->ifindex, link->ifname);
270
271 // Assign link to ports
272 nw_daemon_ports_walk(link->daemon, __nw_port_set_link, link);
273
274 // Assign link to zones
275 nw_daemon_zones_walk(link->daemon, __nw_zone_set_link, link);
276
277 return 0;
278 }
279
280 static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
281 uint32_t mtu = 0;
282 uint32_t min_mtu = 0;
283 uint32_t max_mtu = 0;
284 int r;
285
286 // Read the MTU
287 r = sd_netlink_message_read_u32(message, IFLA_MTU, &mtu);
288 if (r < 0) {
289 ERROR("Could not read MTU for link %d: %m\n", link->ifindex);
290 return r;
291 }
292
293 // Read the minimum MTU
294 r = sd_netlink_message_read_u32(message, IFLA_MIN_MTU, &min_mtu);
295 if (r < 0) {
296 ERROR("Could not read the minimum MTU for link %d: %m\n", link->ifindex);
297 return r;
298 }
299
300 // Read the maximum MTU
301 r = sd_netlink_message_read_u32(message, IFLA_MAX_MTU, &max_mtu);
302 if (r < 0) {
303 ERROR("Could not read the maximum MTU for link %d: %m\n", link->ifindex);
304 return r;
305 }
306
307 // Set the maximum MTU to infinity
308 if (!max_mtu)
309 max_mtu = UINT32_MAX;
310
311 // Store min/max MTU
312 link->min_mtu = min_mtu;
313 link->max_mtu = max_mtu;
314
315 // End here, if the MTU has not been changed
316 if (link->mtu == mtu)
317 return 0;
318
319 DEBUG("Link %d: MTU has changed to %" PRIu32 " (min: %" PRIu32 ", max: %" PRIu32 ")\n",
320 link->ifindex, link->mtu, link->min_mtu, link->max_mtu);
321
322 // Store MTU
323 link->mtu = mtu;
324
325 return 0;
326 }
327
328 static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
329 unsigned int flags = 0;
330 uint8_t operstate = 0;
331 int r;
332
333 // Fetch flags
334 r = sd_rtnl_message_link_get_flags(message, &flags);
335 if (r < 0) {
336 DEBUG("Could not read link flags: %m\n");
337 return 1;
338 }
339
340 #if 0
341 // Fetch operstate
342 r = sd_netlink_message_read_u8(message, IFLA_OPERSTATE, &operstate);
343 if (r < 1) {
344 ERROR("Could not read operstate: %m\n");
345 return 1;
346 }
347 #endif
348
349 // End here if there have been no changes
350 if (link->flags == flags && link->operstate == operstate)
351 return 0;
352
353 // XXX We should log any changes here
354
355 // Fetch current carrier state
356 const int had_carrier = nw_link_has_carrier(link);
357
358 // Store the new flags & operstate
359 link->flags = flags;
360 link->operstate = operstate;
361
362 // Notify if carrier was gained or lost
363 if (!had_carrier && nw_link_has_carrier(link)) {
364 r = nw_link_carrier_gained(link);
365 if (r < 0)
366 return r;
367
368 } else if (had_carrier && !nw_link_has_carrier(link)) {
369 r = nw_link_carrier_lost(link);
370 if (r < 0)
371 return r;
372 }
373
374 return 0;
375 }
376
377 /*
378 This function is called whenever anything changes, so that we can
379 update our internal link object.
380 */
381 static int nw_link_update(nw_link* link, sd_netlink_message* message) {
382 int r;
383
384 // Update the interface name
385 r = nw_link_update_ifname(link, message);
386 if (r)
387 return r;
388
389 // Update the MTU
390 r = nw_link_update_mtu(link, message);
391 if (r)
392 return r;
393
394 // Update flags
395 r = nw_link_update_flags(link, message);
396 if (r)
397 return r;
398
399 return 0;
400 }
401
402 int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
403 nw_links* links = NULL;
404 nw_link* link = NULL;
405 const char* ifname = NULL;
406 int ifindex;
407 uint16_t type;
408 int r;
409
410 nw_daemon* daemon = (nw_daemon*)data;
411
412 // Fetch links
413 links = nw_daemon_links(daemon);
414 if (!links) {
415 r = 1;
416 goto ERROR;
417 }
418
419 // Check if this message could be received
420 if (sd_netlink_message_is_error(message)) {
421 r = sd_netlink_message_get_errno(message);
422 if (r < 0)
423 ERROR("Could not receive link message: %m\n");
424
425 goto IGNORE;
426 }
427
428 // Fetch the message type
429 r = sd_netlink_message_get_type(message, &type);
430 if (r < 0) {
431 ERROR("Could not fetch message type: %m\n");
432 goto IGNORE;
433 }
434
435 // Check type
436 switch (type) {
437 case RTM_NEWLINK:
438 case RTM_DELLINK:
439 break;
440
441 default:
442 ERROR("Received an unexpected message (type %u)\n", type);
443 goto IGNORE;
444 }
445
446 // Fetch the interface index
447 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
448 if (r < 0) {
449 ERROR("Could not fetch ifindex: %m\n");
450 goto IGNORE;
451 }
452
453 // Check interface index
454 if (ifindex <= 0) {
455 ERROR("Received an invalid ifindex\n");
456 goto IGNORE;
457 }
458
459 // Fetch the interface name
460 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
461 if (r < 0) {
462 ERROR("Received a netlink message without interface name: %m\n");
463 goto IGNORE;
464 }
465
466 // Try finding an existing link
467 link = nw_daemon_get_link_by_ifindex(daemon, ifindex);
468
469 switch (type) {
470 case RTM_NEWLINK:
471 // If the link doesn't exist, create it
472 if (!link) {
473 r = nw_link_create(&link, daemon, ifindex);
474 if (r) {
475 ERROR("Could not create link: %m\n");
476 goto ERROR;
477 }
478
479 // Add it to the list
480 r = nw_links_add_link(links, link);
481 if (r)
482 goto ERROR;
483 }
484
485 // Import any data from the netlink message
486 r = nw_link_update(link, message);
487 if (r)
488 goto ERROR;
489
490 break;
491
492 case RTM_DELLINK:
493 if (link)
494 nw_links_drop_link(links, link);
495
496 break;
497 }
498
499 IGNORE:
500 r = 0;
501
502 ERROR:
503 if (links)
504 nw_links_unref(links);
505 if (link)
506 nw_link_unref(link);
507
508 return r;
509 }
510
511 static int __nw_link_destroy(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
512 nw_link* link = (nw_link*)data;
513 int r;
514
515 // Check if the operation was successful
516 r = sd_netlink_message_get_errno(m);
517 if (r < 0) {
518 ERROR("Could not remove link %d: %m\n", link->ifindex);
519 // XXX We should extract the error message
520
521 return 0;
522 }
523
524 // Mark this link as destroyed
525 link->state = NW_LINK_DESTROYED;
526
527 return 0;
528 }
529
530 int nw_link_destroy(nw_link* link) {
531 sd_netlink_message* m = NULL;
532 int r;
533
534 sd_netlink* rtnl = nw_daemon_get_rtnl(link->daemon);
535 if (!rtnl)
536 return 1;
537
538 DEBUG("Destroying link %d\n", link->ifindex);
539
540 // Create a new message
541 r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, link->ifindex);
542 if (r < 0) {
543 ERROR("Could not allocate RTM_DELLINK message: %m\n");
544 goto ERROR;
545 }
546
547 // Send the message
548 r = sd_netlink_call_async(rtnl, NULL, m, __nw_link_destroy,
549 __nw_link_unref, nw_link_ref(link), -1, NULL);
550 if (r < 0) {
551 ERROR("Could not send rtnetlink message: %m\n");
552 goto ERROR;
553 }
554
555 ERROR:
556 if (m)
557 sd_netlink_message_unref(m);
558
559 return r;
560 }
561
562 // JSON
563
564 static int nw_link_device_to_json(nw_link* link, struct json_object* o) {
565 const char* driver = NULL;
566 const char* vendor = NULL;
567 const char* model = NULL;
568 int r;
569
570 // Fetch driver
571 r = sd_device_get_driver(link->device, &driver);
572 if (r < 0 && r != -ENOENT)
573 return r;
574
575 // Add driver
576 if (driver) {
577 r = json_object_add_string(o, "Driver", driver);
578 if (r < 0)
579 return r;
580 }
581
582 // Fetch vendor
583 r = sd_device_get_property_value(link->device, "ID_VENDOR_FROM_DATABASE", &vendor);
584 if (r < 0) {
585 r = sd_device_get_property_value(link->device, "ID_VENDOR", &vendor);
586 if (r < 0 && r != -ENOENT)
587 return r;
588 }
589
590 // Add vendor
591 if (vendor) {
592 r = json_object_add_string(o, "Vendor", vendor);
593 if (r < 0)
594 return r;
595 }
596
597 // Fetch model
598 r = sd_device_get_property_value(link->device, "ID_MODEL_FROM_DATABASE", &model);
599 if (r < 0) {
600 r = sd_device_get_property_value(link->device, "ID_MODEL", &model);
601 if (r < 0 && r != -ENOENT)
602 return r;
603 }
604
605 // Add model
606 if (model) {
607 r = json_object_add_string(o, "Model", model);
608 if (r < 0)
609 return r;
610 }
611
612 return 0;
613 }
614
615 int nw_link_to_json(nw_link* link, struct json_object* o) {
616 int r;
617
618 // Add ifindex
619 r = json_object_add_int64(o, "LinkIndex", link->ifindex);
620 if (r < 0)
621 return r;
622
623 // Add sd-device stuff
624 r = nw_link_device_to_json(link, o);
625 if (r < 0)
626 return r;
627
628 return 0;
629 }