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