]> git.ipfire.org Git - network.git/blame - src/networkd/link.c
ports: Implement destroying a port
[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
766f08ca
MT
27#include <systemd/sd-netlink.h>
28
87a1e1e0
MT
29#include "daemon.h"
30#include "link.h"
766f08ca 31#include "links.h"
eaf649ee 32#include "logging.h"
f650f6cd 33#include "string.h"
87a1e1e0
MT
34
35struct nw_link {
2361667e 36 nw_daemon* daemon;
87a1e1e0
MT
37 int nrefs;
38
39 // Interface Index
40 int ifindex;
f650f6cd
MT
41
42 // Interface Name
dfd49c2c 43 char ifname[IFNAMSIZ];
f37df5ea 44
371b836a
MT
45 enum nw_link_state {
46 NW_LINK_UNKNOWN = 0,
47 NW_LINK_DESTROYED,
48 } state;
49
15240e08
MT
50 // Stats
51 struct rtnl_link_stats64 stats64;
52
f37df5ea
MT
53 // MTU
54 uint32_t mtu;
55 uint32_t min_mtu;
56 uint32_t max_mtu;
c65300b4
MT
57
58 // Flags
59 unsigned int flags;
69ef50c7 60 uint8_t operstate;
87a1e1e0
MT
61};
62
2361667e 63int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
87a1e1e0 64 // Allocate a new object
2361667e 65 nw_link* l = calloc(1, sizeof(*l));
87a1e1e0
MT
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
766f08ca
MT
78 DEBUG("New link allocated (ifindex = %d)\n", l->ifindex);
79
87a1e1e0
MT
80 *link = l;
81
82 return 0;
83}
84
2361667e 85static void nw_link_free(nw_link* link) {
766f08ca
MT
86 DEBUG("Freeing link (ifindex = %d)\n", link->ifindex);
87
87a1e1e0
MT
88 if (link->daemon)
89 nw_daemon_unref(link->daemon);
90}
91
2361667e 92nw_link* nw_link_ref(nw_link* link) {
87a1e1e0
MT
93 link->nrefs++;
94
95 return link;
96}
97
2361667e 98nw_link* nw_link_unref(nw_link* link) {
87a1e1e0
MT
99 if (--link->nrefs > 0)
100 return link;
101
102 nw_link_free(link);
103 return NULL;
104}
eaf649ee 105
371b836a
MT
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*/
110static void __nw_link_unref(void* data) {
111 nw_link* link = (nw_link*)data;
112
113 nw_link_unref(link);
114}
115
2361667e 116int nw_link_ifindex(nw_link* link) {
766f08ca
MT
117 return link->ifindex;
118}
eaf649ee 119
207abe4e 120const char* nw_link_ifname(nw_link* link) {
611d4aca
MT
121 // Return NULL if name isn't set
122 if (!*link->ifname)
123 return NULL;
124
207abe4e
MT
125 return link->ifname;
126}
127
15240e08
MT
128// Stats
129
130const struct rtnl_link_stats64* nw_link_get_stats64(nw_link* link) {
131 return &link->stats64;
132}
133
134static 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
158ERROR:
159 if (m)
160 sd_netlink_message_unref(m);
161
162 return r;
163}
164
165static 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
201int nw_link_update_stats(nw_link* link) {
202 return nw_link_call_getlink(link, __nw_link_update_stats);
203}
204
dfd49c2c
MT
205// Carrier
206
207int nw_link_has_carrier(nw_link* link) {
208 return link->operstate == IF_OPER_UP;
209}
210
211static int nw_link_carrier_gained(nw_link* link) {
212 return 0; // XXX TODO
213}
214
215static int nw_link_carrier_lost(nw_link* link) {
216 return 0; // XXX TODO
217}
218
2361667e 219static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
f650f6cd
MT
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
611d4aca
MT
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
f650f6cd
MT
248 return 0;
249}
250
2361667e 251static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
f37df5ea
MT
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
c65300b4
MT
299static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
300 unsigned int flags = 0;
69ef50c7 301 uint8_t operstate = 0;
c65300b4
MT
302 int r;
303
304 // Fetch flags
305 r = sd_rtnl_message_link_get_flags(message, &flags);
306 if (r < 0) {
1a70a686 307 DEBUG("Could not read link flags: %m\n");
c65300b4
MT
308 return 1;
309 }
310
c403eb4c 311#if 0
69ef50c7
MT
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 }
c403eb4c 318#endif
69ef50c7 319
c65300b4 320 // End here if there have been no changes
69ef50c7 321 if (link->flags == flags && link->operstate == operstate)
c65300b4
MT
322 return 0;
323
324 // XXX We should log any changes here
325
dfd49c2c
MT
326 // Fetch current carrier state
327 const int had_carrier = nw_link_has_carrier(link);
328
69ef50c7 329 // Store the new flags & operstate
c65300b4 330 link->flags = flags;
69ef50c7
MT
331 link->operstate = operstate;
332
dfd49c2c
MT
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 }
c65300b4
MT
344
345 return 0;
346}
347
f650f6cd
MT
348/*
349 This function is called whenever anything changes, so that we can
350 update our internal link object.
351*/
2361667e 352static int nw_link_update(nw_link* link, sd_netlink_message* message) {
f650f6cd
MT
353 int r;
354
355 // Update the interface name
356 r = nw_link_update_ifname(link, message);
357 if (r)
358 return r;
359
f37df5ea
MT
360 // Update the MTU
361 r = nw_link_update_mtu(link, message);
362 if (r)
363 return r;
364
c65300b4
MT
365 // Update flags
366 r = nw_link_update_flags(link, message);
367 if (r)
368 return r;
369
f650f6cd
MT
370 return 0;
371}
372
766f08ca 373int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
2361667e
MT
374 nw_links* links = NULL;
375 nw_link* link = NULL;
766f08ca
MT
376 const char* ifname = NULL;
377 int ifindex;
378 uint16_t type;
379 int r;
380
2361667e 381 nw_daemon* daemon = (nw_daemon*)data;
766f08ca
MT
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 }
b8a1a68c
MT
449
450 // Add it to the list
451 r = nw_links_add_link(links, link);
452 if (r)
453 goto ERROR;
766f08ca
MT
454 }
455
f650f6cd
MT
456 // Import any data from the netlink message
457 r = nw_link_update(link, message);
458 if (r)
459 goto ERROR;
766f08ca 460
766f08ca
MT
461 break;
462
463 case RTM_DELLINK:
464 if (link)
465 nw_links_drop_link(links, link);
466
467 break;
468 }
469
470IGNORE:
471 r = 0;
472
473ERROR:
474 if (links)
475 nw_links_unref(links);
476 if (link)
477 nw_link_unref(link);
478
479 return r;
eaf649ee 480}
371b836a
MT
481
482static 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
501int 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
526ERROR:
527 if (m)
528 sd_netlink_message_unref(m);
529
530 return r;
531}