]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/link.c
ports: Implement destroying a port
[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 #if 0
312 // Fetch operstate
313 r = sd_netlink_message_read_u8(message, IFLA_OPERSTATE, &operstate);
314 if (r < 1) {
315 ERROR("Could not read operstate: %m\n");
316 return 1;
317 }
318 #endif
319
320 // End here if there have been no changes
321 if (link->flags == flags && link->operstate == operstate)
322 return 0;
323
324 // XXX We should log any changes here
325
326 // Fetch current carrier state
327 const int had_carrier = nw_link_has_carrier(link);
328
329 // Store the new flags & operstate
330 link->flags = flags;
331 link->operstate = operstate;
332
333 // Notify if carrier was gained or lost
334 if (!had_carrier && nw_link_has_carrier(link)) {
335 r = nw_link_carrier_gained(link);
336 if (r < 0)
337 return r;
338
339 } else if (had_carrier && !nw_link_has_carrier(link)) {
340 r = nw_link_carrier_lost(link);
341 if (r < 0)
342 return r;
343 }
344
345 return 0;
346 }
347
348 /*
349 This function is called whenever anything changes, so that we can
350 update our internal link object.
351 */
352 static int nw_link_update(nw_link* link, sd_netlink_message* message) {
353 int r;
354
355 // Update the interface name
356 r = nw_link_update_ifname(link, message);
357 if (r)
358 return r;
359
360 // Update the MTU
361 r = nw_link_update_mtu(link, message);
362 if (r)
363 return r;
364
365 // Update flags
366 r = nw_link_update_flags(link, message);
367 if (r)
368 return r;
369
370 return 0;
371 }
372
373 int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
374 nw_links* links = NULL;
375 nw_link* link = NULL;
376 const char* ifname = NULL;
377 int ifindex;
378 uint16_t type;
379 int r;
380
381 nw_daemon* daemon = (nw_daemon*)data;
382
383 // Fetch links
384 links = nw_daemon_links(daemon);
385 if (!links) {
386 r = 1;
387 goto ERROR;
388 }
389
390 // Check if this message could be received
391 if (sd_netlink_message_is_error(message)) {
392 r = sd_netlink_message_get_errno(message);
393 if (r < 0)
394 ERROR("Could not receive link message: %m\n");
395
396 goto IGNORE;
397 }
398
399 // Fetch the message type
400 r = sd_netlink_message_get_type(message, &type);
401 if (r < 0) {
402 ERROR("Could not fetch message type: %m\n");
403 goto IGNORE;
404 }
405
406 // Check type
407 switch (type) {
408 case RTM_NEWLINK:
409 case RTM_DELLINK:
410 break;
411
412 default:
413 ERROR("Received an unexpected message (type %u)\n", type);
414 goto IGNORE;
415 }
416
417 // Fetch the interface index
418 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
419 if (r < 0) {
420 ERROR("Could not fetch ifindex: %m\n");
421 goto IGNORE;
422 }
423
424 // Check interface index
425 if (ifindex <= 0) {
426 ERROR("Received an invalid ifindex\n");
427 goto IGNORE;
428 }
429
430 // Fetch the interface name
431 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
432 if (r < 0) {
433 ERROR("Received a netlink message without interface name: %m\n");
434 goto IGNORE;
435 }
436
437 // Try finding an existing link
438 link = nw_daemon_get_link_by_ifindex(daemon, ifindex);
439
440 switch (type) {
441 case RTM_NEWLINK:
442 // If the link doesn't exist, create it
443 if (!link) {
444 r = nw_link_create(&link, daemon, ifindex);
445 if (r) {
446 ERROR("Could not create link: %m\n");
447 goto ERROR;
448 }
449
450 // Add it to the list
451 r = nw_links_add_link(links, link);
452 if (r)
453 goto ERROR;
454 }
455
456 // Import any data from the netlink message
457 r = nw_link_update(link, message);
458 if (r)
459 goto ERROR;
460
461 break;
462
463 case RTM_DELLINK:
464 if (link)
465 nw_links_drop_link(links, link);
466
467 break;
468 }
469
470 IGNORE:
471 r = 0;
472
473 ERROR:
474 if (links)
475 nw_links_unref(links);
476 if (link)
477 nw_link_unref(link);
478
479 return r;
480 }
481
482 static int __nw_link_destroy(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
483 nw_link* link = (nw_link*)data;
484 int r;
485
486 // Check if the operation was successful
487 r = sd_netlink_message_get_errno(m);
488 if (r < 0) {
489 ERROR("Could not remove link %d: %m\n", link->ifindex);
490 // XXX We should extract the error message
491
492 return 0;
493 }
494
495 // Mark this link as destroyed
496 link->state = NW_LINK_DESTROYED;
497
498 return 0;
499 }
500
501 int nw_link_destroy(nw_link* link) {
502 sd_netlink_message* m = NULL;
503 int r;
504
505 sd_netlink* rtnl = nw_daemon_get_rtnl(link->daemon);
506 if (!rtnl)
507 return 1;
508
509 DEBUG("Destroying link %d\n", link->ifindex);
510
511 // Create a new message
512 r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, link->ifindex);
513 if (r < 0) {
514 ERROR("Could not allocate RTM_DELLINK message: %m\n");
515 goto ERROR;
516 }
517
518 // Send the message
519 r = sd_netlink_call_async(rtnl, NULL, m, __nw_link_destroy,
520 __nw_link_unref, nw_link_ref(link), -1, NULL);
521 if (r < 0) {
522 ERROR("Could not send rtnetlink message: %m\n");
523 goto ERROR;
524 }
525
526 ERROR:
527 if (m)
528 sd_netlink_message_unref(m);
529
530 return r;
531 }