]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/link.c
1edf20d72594718020a9ae1e5ef9a7772be840fb
[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-netlink.h>
28
29 #include "daemon.h"
30 #include "link.h"
31 #include "links.h"
32 #include "logging.h"
33 #include "string.h"
34
35 struct nw_link {
36 nw_daemon* daemon;
37 int nrefs;
38
39 // Interface Index
40 int ifindex;
41
42 // Interface Name
43 char ifname[IFNAMSIZ];
44
45 enum nw_link_state {
46 NW_LINK_UNKNOWN = 0,
47 NW_LINK_DESTROYED,
48 } state;
49
50 // Stats
51 struct rtnl_link_stats64 stats64;
52
53 // MTU
54 uint32_t mtu;
55 uint32_t min_mtu;
56 uint32_t max_mtu;
57
58 // Flags
59 unsigned int flags;
60 uint8_t operstate;
61 };
62
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));
66 if (!l)
67 return 1;
68
69 // Store a reference to the daemon
70 l->daemon = nw_daemon_ref(daemon);
71
72 // Initialize the reference counter
73 l->nrefs = 1;
74
75 // Store the ifindex
76 l->ifindex = ifindex;
77
78 DEBUG("New link allocated (ifindex = %d)\n", l->ifindex);
79
80 *link = l;
81
82 return 0;
83 }
84
85 static void nw_link_free(nw_link* link) {
86 DEBUG("Freeing link (ifindex = %d)\n", link->ifindex);
87
88 if (link->daemon)
89 nw_daemon_unref(link->daemon);
90 }
91
92 nw_link* nw_link_ref(nw_link* link) {
93 link->nrefs++;
94
95 return link;
96 }
97
98 nw_link* nw_link_unref(nw_link* link) {
99 if (--link->nrefs > 0)
100 return link;
101
102 nw_link_free(link);
103 return NULL;
104 }
105
106 /*
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.
109 */
110 static void __nw_link_unref(void* data) {
111 nw_link* link = (nw_link*)data;
112
113 nw_link_unref(link);
114 }
115
116 int nw_link_ifindex(nw_link* link) {
117 return link->ifindex;
118 }
119
120 const char* nw_link_ifname(nw_link* link) {
121 // Return NULL if name isn't set
122 if (!*link->ifname)
123 return NULL;
124
125 return link->ifname;
126 }
127
128 // Stats
129
130 const struct rtnl_link_stats64* nw_link_get_stats64(nw_link* link) {
131 return &link->stats64;
132 }
133
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;
137 int r;
138
139 sd_netlink* rtnl = nw_daemon_get_rtnl(link->daemon);
140 if (!rtnl)
141 return 1;
142
143 // Create a new message
144 r = sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, link->ifindex);
145 if (r < 0) {
146 ERROR("Could not allocate RTM_GETLINK message: %m\n");
147 goto ERROR;
148 }
149
150 // Send the message
151 r = sd_netlink_call_async(rtnl, NULL, m, callback,
152 __nw_link_unref, nw_link_ref(link), -1, NULL);
153 if (r < 0) {
154 ERROR("Could not send rtnetlink message: %m\n");
155 goto ERROR;
156 }
157
158 ERROR:
159 if (m)
160 sd_netlink_message_unref(m);
161
162 return r;
163 }
164
165 static int __nw_link_update_stats(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
166 nw_link* link = (nw_link*)data;
167 int r;
168
169 // Fetch the stats
170 r = sd_netlink_message_read(m, IFLA_STATS64, sizeof(link->stats64), &link->stats64);
171 if (r < 0)
172 return r;
173
174 DEBUG("Link %d: Stats updated\n", link->ifindex);
175
176 // Log stats
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);
187
188 // Notify ports that stats have been updated
189 r = nw_daemon_ports_walk(link->daemon, __nw_port_update_stats, link);
190 if (r)
191 return r;
192
193 // Notify zones that stats have been updated
194 r = nw_daemon_zones_walk(link->daemon, __nw_zone_update_stats, link);
195 if (r)
196 return r;
197
198 return 0;
199 }
200
201 int nw_link_update_stats(nw_link* link) {
202 return nw_link_call_getlink(link, __nw_link_update_stats);
203 }
204
205 // Carrier
206
207 int nw_link_has_carrier(nw_link* link) {
208 return link->operstate == IF_OPER_UP;
209 }
210
211 static int nw_link_carrier_gained(nw_link* link) {
212 return 0; // XXX TODO
213 }
214
215 static int nw_link_carrier_lost(nw_link* link) {
216 return 0; // XXX TODO
217 }
218
219 static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
220 const char* ifname = NULL;
221 int r;
222
223 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
224 if (r < 0) {
225 ERROR("Could not read link name for link %d: %m\n", link->ifindex);
226 return 1;
227 }
228
229 // Do nothing if the name is already set
230 if (strcmp(link->ifname, ifname) == 0)
231 return 0;
232
233 // Otherwise update the name
234 r = nw_string_set(link->ifname, ifname);
235 if (r) {
236 ERROR("Could not set link name: %m\n");
237 return 1;
238 }
239
240 DEBUG("Link %d has been renamed to '%s'\n", link->ifindex, link->ifname);
241
242 // Assign link to ports
243 nw_daemon_ports_walk(link->daemon, __nw_port_set_link, link);
244
245 // Assign link to zones
246 nw_daemon_zones_walk(link->daemon, __nw_zone_set_link, link);
247
248 return 0;
249 }
250
251 static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
252 uint32_t mtu = 0;
253 uint32_t min_mtu = 0;
254 uint32_t max_mtu = 0;
255 int r;
256
257 // Read the MTU
258 r = sd_netlink_message_read_u32(message, IFLA_MTU, &mtu);
259 if (r < 0) {
260 ERROR("Could not read MTU for link %d: %m\n", link->ifindex);
261 return r;
262 }
263
264 // Read the minimum MTU
265 r = sd_netlink_message_read_u32(message, IFLA_MIN_MTU, &min_mtu);
266 if (r < 0) {
267 ERROR("Could not read the minimum MTU for link %d: %m\n", link->ifindex);
268 return r;
269 }
270
271 // Read the maximum MTU
272 r = sd_netlink_message_read_u32(message, IFLA_MAX_MTU, &max_mtu);
273 if (r < 0) {
274 ERROR("Could not read the maximum MTU for link %d: %m\n", link->ifindex);
275 return r;
276 }
277
278 // Set the maximum MTU to infinity
279 if (!max_mtu)
280 max_mtu = UINT32_MAX;
281
282 // Store min/max MTU
283 link->min_mtu = min_mtu;
284 link->max_mtu = max_mtu;
285
286 // End here, if the MTU has not been changed
287 if (link->mtu == mtu)
288 return 0;
289
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);
292
293 // Store MTU
294 link->mtu = mtu;
295
296 return 0;
297 }
298
299 static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
300 unsigned int flags = 0;
301 uint8_t operstate = 0;
302 int r;
303
304 // Fetch flags
305 r = sd_rtnl_message_link_get_flags(message, &flags);
306 if (r < 0) {
307 DEBUG("Could not read link flags: %m\n");
308 return 1;
309 }
310
311 // Fetch operstate
312 r = sd_netlink_message_read_u8(message, IFLA_OPERSTATE, &operstate);
313 if (r < 1) {
314 ERROR("Could not read operstate: %m\n");
315 return 1;
316 }
317
318 // End here if there have been no changes
319 if (link->flags == flags && link->operstate == operstate)
320 return 0;
321
322 // XXX We should log any changes here
323
324 // Fetch current carrier state
325 const int had_carrier = nw_link_has_carrier(link);
326
327 // Store the new flags & operstate
328 link->flags = flags;
329 link->operstate = operstate;
330
331 // Notify if carrier was gained or lost
332 if (!had_carrier && nw_link_has_carrier(link)) {
333 r = nw_link_carrier_gained(link);
334 if (r < 0)
335 return r;
336
337 } else if (had_carrier && !nw_link_has_carrier(link)) {
338 r = nw_link_carrier_lost(link);
339 if (r < 0)
340 return r;
341 }
342
343 return 0;
344 }
345
346 /*
347 This function is called whenever anything changes, so that we can
348 update our internal link object.
349 */
350 static int nw_link_update(nw_link* link, sd_netlink_message* message) {
351 int r;
352
353 // Update the interface name
354 r = nw_link_update_ifname(link, message);
355 if (r)
356 return r;
357
358 // Update the MTU
359 r = nw_link_update_mtu(link, message);
360 if (r)
361 return r;
362
363 // Update flags
364 r = nw_link_update_flags(link, message);
365 if (r)
366 return r;
367
368 return 0;
369 }
370
371 int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
372 nw_links* links = NULL;
373 nw_link* link = NULL;
374 const char* ifname = NULL;
375 int ifindex;
376 uint16_t type;
377 int r;
378
379 nw_daemon* daemon = (nw_daemon*)data;
380
381 // Fetch links
382 links = nw_daemon_links(daemon);
383 if (!links) {
384 r = 1;
385 goto ERROR;
386 }
387
388 // Check if this message could be received
389 if (sd_netlink_message_is_error(message)) {
390 r = sd_netlink_message_get_errno(message);
391 if (r < 0)
392 ERROR("Could not receive link message: %m\n");
393
394 goto IGNORE;
395 }
396
397 // Fetch the message type
398 r = sd_netlink_message_get_type(message, &type);
399 if (r < 0) {
400 ERROR("Could not fetch message type: %m\n");
401 goto IGNORE;
402 }
403
404 // Check type
405 switch (type) {
406 case RTM_NEWLINK:
407 case RTM_DELLINK:
408 break;
409
410 default:
411 ERROR("Received an unexpected message (type %u)\n", type);
412 goto IGNORE;
413 }
414
415 // Fetch the interface index
416 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
417 if (r < 0) {
418 ERROR("Could not fetch ifindex: %m\n");
419 goto IGNORE;
420 }
421
422 // Check interface index
423 if (ifindex <= 0) {
424 ERROR("Received an invalid ifindex\n");
425 goto IGNORE;
426 }
427
428 // Fetch the interface name
429 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
430 if (r < 0) {
431 ERROR("Received a netlink message without interface name: %m\n");
432 goto IGNORE;
433 }
434
435 // Try finding an existing link
436 link = nw_daemon_get_link_by_ifindex(daemon, ifindex);
437
438 switch (type) {
439 case RTM_NEWLINK:
440 // If the link doesn't exist, create it
441 if (!link) {
442 r = nw_link_create(&link, daemon, ifindex);
443 if (r) {
444 ERROR("Could not create link: %m\n");
445 goto ERROR;
446 }
447
448 // Add it to the list
449 r = nw_links_add_link(links, link);
450 if (r)
451 goto ERROR;
452 }
453
454 // Import any data from the netlink message
455 r = nw_link_update(link, message);
456 if (r)
457 goto ERROR;
458
459 break;
460
461 case RTM_DELLINK:
462 if (link)
463 nw_links_drop_link(links, link);
464
465 break;
466 }
467
468 IGNORE:
469 r = 0;
470
471 ERROR:
472 if (links)
473 nw_links_unref(links);
474 if (link)
475 nw_link_unref(link);
476
477 return r;
478 }
479
480 static int __nw_link_destroy(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
481 nw_link* link = (nw_link*)data;
482 int r;
483
484 // Check if the operation was successful
485 r = sd_netlink_message_get_errno(m);
486 if (r < 0) {
487 ERROR("Could not remove link %d: %m\n", link->ifindex);
488 // XXX We should extract the error message
489
490 return 0;
491 }
492
493 // Mark this link as destroyed
494 link->state = NW_LINK_DESTROYED;
495
496 return 0;
497 }
498
499 int nw_link_destroy(nw_link* link) {
500 sd_netlink_message* m = NULL;
501 int r;
502
503 sd_netlink* rtnl = nw_daemon_get_rtnl(link->daemon);
504 if (!rtnl)
505 return 1;
506
507 DEBUG("Destroying link %d\n", link->ifindex);
508
509 // Create a new message
510 r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, link->ifindex);
511 if (r < 0) {
512 ERROR("Could not allocate RTM_DELLINK message: %m\n");
513 goto ERROR;
514 }
515
516 // Send the message
517 r = sd_netlink_call_async(rtnl, NULL, m, __nw_link_destroy,
518 __nw_link_unref, nw_link_ref(link), -1, NULL);
519 if (r < 0) {
520 ERROR("Could not send rtnetlink message: %m\n");
521 goto ERROR;
522 }
523
524 ERROR:
525 if (m)
526 sd_netlink_message_unref(m);
527
528 return r;
529 }