]> git.ipfire.org Git - people/ms/network.git/blame - src/networkd/link.c
ports: Add scaffolding for physical Ethernet interfaces
[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
2361667e 248static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
f650f6cd
MT
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
611d4aca
MT
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
f650f6cd
MT
277 return 0;
278}
279
2361667e 280static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
f37df5ea
MT
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
c65300b4
MT
328static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
329 unsigned int flags = 0;
69ef50c7 330 uint8_t operstate = 0;
c65300b4
MT
331 int r;
332
333 // Fetch flags
334 r = sd_rtnl_message_link_get_flags(message, &flags);
335 if (r < 0) {
1a70a686 336 DEBUG("Could not read link flags: %m\n");
c65300b4
MT
337 return 1;
338 }
339
c403eb4c 340#if 0
69ef50c7
MT
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 }
c403eb4c 347#endif
69ef50c7 348
c65300b4 349 // End here if there have been no changes
69ef50c7 350 if (link->flags == flags && link->operstate == operstate)
c65300b4
MT
351 return 0;
352
353 // XXX We should log any changes here
354
dfd49c2c
MT
355 // Fetch current carrier state
356 const int had_carrier = nw_link_has_carrier(link);
357
69ef50c7 358 // Store the new flags & operstate
c65300b4 359 link->flags = flags;
69ef50c7
MT
360 link->operstate = operstate;
361
dfd49c2c
MT
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 }
c65300b4
MT
373
374 return 0;
375}
376
f650f6cd
MT
377/*
378 This function is called whenever anything changes, so that we can
379 update our internal link object.
380*/
2361667e 381static int nw_link_update(nw_link* link, sd_netlink_message* message) {
f650f6cd
MT
382 int r;
383
384 // Update the interface name
385 r = nw_link_update_ifname(link, message);
386 if (r)
387 return r;
388
f37df5ea
MT
389 // Update the MTU
390 r = nw_link_update_mtu(link, message);
391 if (r)
392 return r;
393
c65300b4
MT
394 // Update flags
395 r = nw_link_update_flags(link, message);
396 if (r)
397 return r;
398
f650f6cd
MT
399 return 0;
400}
401
766f08ca 402int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
2361667e
MT
403 nw_links* links = NULL;
404 nw_link* link = NULL;
766f08ca
MT
405 const char* ifname = NULL;
406 int ifindex;
407 uint16_t type;
408 int r;
409
2361667e 410 nw_daemon* daemon = (nw_daemon*)data;
766f08ca
MT
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 }
b8a1a68c
MT
478
479 // Add it to the list
480 r = nw_links_add_link(links, link);
481 if (r)
482 goto ERROR;
766f08ca
MT
483 }
484
f650f6cd
MT
485 // Import any data from the netlink message
486 r = nw_link_update(link, message);
487 if (r)
488 goto ERROR;
766f08ca 489
766f08ca
MT
490 break;
491
492 case RTM_DELLINK:
493 if (link)
494 nw_links_drop_link(links, link);
495
496 break;
497 }
498
499IGNORE:
500 r = 0;
501
502ERROR:
503 if (links)
504 nw_links_unref(links);
505 if (link)
506 nw_link_unref(link);
507
508 return r;
eaf649ee 509}
371b836a
MT
510
511static 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
530int 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
555ERROR:
556 if (m)
557 sd_netlink_message_unref(m);
558
559 return r;
560}
69467266
MT
561
562// JSON
563
cf75e1db
MT
564static 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
69467266
MT
615int 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
cf75e1db
MT
623 // Add sd-device stuff
624 r = nw_link_device_to_json(link, o);
625 if (r < 0)
626 return r;
627
69467266
MT
628 return 0;
629}