]> git.ipfire.org Git - network.git/blame - src/networkd/link.c
networkd: Implement deleting links
[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>
87a1e1e0
MT
22#include <stddef.h>
23#include <stdlib.h>
f650f6cd 24#include <string.h>
87a1e1e0 25
766f08ca
MT
26#include <systemd/sd-netlink.h>
27
87a1e1e0
MT
28#include "daemon.h"
29#include "link.h"
766f08ca 30#include "links.h"
eaf649ee 31#include "logging.h"
f650f6cd 32#include "string.h"
87a1e1e0
MT
33
34struct nw_link {
2361667e 35 nw_daemon* daemon;
87a1e1e0
MT
36 int nrefs;
37
38 // Interface Index
39 int ifindex;
f650f6cd
MT
40
41 // Interface Name
dfd49c2c 42 char ifname[IFNAMSIZ];
f37df5ea 43
371b836a
MT
44 enum nw_link_state {
45 NW_LINK_UNKNOWN = 0,
46 NW_LINK_DESTROYED,
47 } state;
48
f37df5ea
MT
49 // MTU
50 uint32_t mtu;
51 uint32_t min_mtu;
52 uint32_t max_mtu;
c65300b4
MT
53
54 // Flags
55 unsigned int flags;
69ef50c7 56 uint8_t operstate;
87a1e1e0
MT
57};
58
2361667e 59int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
87a1e1e0 60 // Allocate a new object
2361667e 61 nw_link* l = calloc(1, sizeof(*l));
87a1e1e0
MT
62 if (!l)
63 return 1;
64
65 // Store a reference to the daemon
66 l->daemon = nw_daemon_ref(daemon);
67
68 // Initialize the reference counter
69 l->nrefs = 1;
70
71 // Store the ifindex
72 l->ifindex = ifindex;
73
766f08ca
MT
74 DEBUG("New link allocated (ifindex = %d)\n", l->ifindex);
75
87a1e1e0
MT
76 *link = l;
77
78 return 0;
79}
80
2361667e 81static void nw_link_free(nw_link* link) {
766f08ca
MT
82 DEBUG("Freeing link (ifindex = %d)\n", link->ifindex);
83
87a1e1e0
MT
84 if (link->daemon)
85 nw_daemon_unref(link->daemon);
86}
87
2361667e 88nw_link* nw_link_ref(nw_link* link) {
87a1e1e0
MT
89 link->nrefs++;
90
91 return link;
92}
93
2361667e 94nw_link* nw_link_unref(nw_link* link) {
87a1e1e0
MT
95 if (--link->nrefs > 0)
96 return link;
97
98 nw_link_free(link);
99 return NULL;
100}
eaf649ee 101
371b836a
MT
102/*
103 This is a helper function for when we pass a reference to the event loop
104 it will have to dereference the link instance later.
105*/
106static void __nw_link_unref(void* data) {
107 nw_link* link = (nw_link*)data;
108
109 nw_link_unref(link);
110}
111
2361667e 112int nw_link_ifindex(nw_link* link) {
766f08ca
MT
113 return link->ifindex;
114}
eaf649ee 115
207abe4e
MT
116const char* nw_link_ifname(nw_link* link) {
117 return link->ifname;
118}
119
dfd49c2c
MT
120// Carrier
121
122int nw_link_has_carrier(nw_link* link) {
123 return link->operstate == IF_OPER_UP;
124}
125
126static int nw_link_carrier_gained(nw_link* link) {
127 return 0; // XXX TODO
128}
129
130static int nw_link_carrier_lost(nw_link* link) {
131 return 0; // XXX TODO
132}
133
2361667e 134static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
f650f6cd
MT
135 const char* ifname = NULL;
136 int r;
137
138 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
139 if (r < 0) {
140 ERROR("Could not read link name for link %d: %m\n", link->ifindex);
141 return 1;
142 }
143
144 // Do nothing if the name is already set
145 if (strcmp(link->ifname, ifname) == 0)
146 return 0;
147
148 // Otherwise update the name
149 r = nw_string_set(link->ifname, ifname);
150 if (r) {
151 ERROR("Could not set link name: %m\n");
152 return 1;
153 }
154
155 DEBUG("Link %d has been renamed to '%s'\n", link->ifindex, link->ifname);
156
157 return 0;
158}
159
2361667e 160static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
f37df5ea
MT
161 uint32_t mtu = 0;
162 uint32_t min_mtu = 0;
163 uint32_t max_mtu = 0;
164 int r;
165
166 // Read the MTU
167 r = sd_netlink_message_read_u32(message, IFLA_MTU, &mtu);
168 if (r < 0) {
169 ERROR("Could not read MTU for link %d: %m\n", link->ifindex);
170 return r;
171 }
172
173 // Read the minimum MTU
174 r = sd_netlink_message_read_u32(message, IFLA_MIN_MTU, &min_mtu);
175 if (r < 0) {
176 ERROR("Could not read the minimum MTU for link %d: %m\n", link->ifindex);
177 return r;
178 }
179
180 // Read the maximum MTU
181 r = sd_netlink_message_read_u32(message, IFLA_MAX_MTU, &max_mtu);
182 if (r < 0) {
183 ERROR("Could not read the maximum MTU for link %d: %m\n", link->ifindex);
184 return r;
185 }
186
187 // Set the maximum MTU to infinity
188 if (!max_mtu)
189 max_mtu = UINT32_MAX;
190
191 // Store min/max MTU
192 link->min_mtu = min_mtu;
193 link->max_mtu = max_mtu;
194
195 // End here, if the MTU has not been changed
196 if (link->mtu == mtu)
197 return 0;
198
199 DEBUG("Link %d: MTU has changed to %" PRIu32 " (min: %" PRIu32 ", max: %" PRIu32 ")\n",
200 link->ifindex, link->mtu, link->min_mtu, link->max_mtu);
201
202 // Store MTU
203 link->mtu = mtu;
204
205 return 0;
206}
207
c65300b4
MT
208static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
209 unsigned int flags = 0;
69ef50c7 210 uint8_t operstate = 0;
c65300b4
MT
211 int r;
212
213 // Fetch flags
214 r = sd_rtnl_message_link_get_flags(message, &flags);
215 if (r < 0) {
1a70a686 216 DEBUG("Could not read link flags: %m\n");
c65300b4
MT
217 return 1;
218 }
219
69ef50c7
MT
220 // Fetch operstate
221 r = sd_netlink_message_read_u8(message, IFLA_OPERSTATE, &operstate);
222 if (r < 1) {
223 ERROR("Could not read operstate: %m\n");
224 return 1;
225 }
226
c65300b4 227 // End here if there have been no changes
69ef50c7 228 if (link->flags == flags && link->operstate == operstate)
c65300b4
MT
229 return 0;
230
231 // XXX We should log any changes here
232
dfd49c2c
MT
233 // Fetch current carrier state
234 const int had_carrier = nw_link_has_carrier(link);
235
69ef50c7 236 // Store the new flags & operstate
c65300b4 237 link->flags = flags;
69ef50c7
MT
238 link->operstate = operstate;
239
dfd49c2c
MT
240 // Notify if carrier was gained or lost
241 if (!had_carrier && nw_link_has_carrier(link)) {
242 r = nw_link_carrier_gained(link);
243 if (r < 0)
244 return r;
245
246 } else if (had_carrier && !nw_link_has_carrier(link)) {
247 r = nw_link_carrier_lost(link);
248 if (r < 0)
249 return r;
250 }
c65300b4
MT
251
252 return 0;
253}
254
f650f6cd
MT
255/*
256 This function is called whenever anything changes, so that we can
257 update our internal link object.
258*/
2361667e 259static int nw_link_update(nw_link* link, sd_netlink_message* message) {
f650f6cd
MT
260 int r;
261
262 // Update the interface name
263 r = nw_link_update_ifname(link, message);
264 if (r)
265 return r;
266
f37df5ea
MT
267 // Update the MTU
268 r = nw_link_update_mtu(link, message);
269 if (r)
270 return r;
271
c65300b4
MT
272 // Update flags
273 r = nw_link_update_flags(link, message);
274 if (r)
275 return r;
276
f650f6cd
MT
277 return 0;
278}
279
766f08ca 280int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
2361667e
MT
281 nw_links* links = NULL;
282 nw_link* link = NULL;
766f08ca
MT
283 const char* ifname = NULL;
284 int ifindex;
285 uint16_t type;
286 int r;
287
2361667e 288 nw_daemon* daemon = (nw_daemon*)data;
766f08ca
MT
289
290 // Fetch links
291 links = nw_daemon_links(daemon);
292 if (!links) {
293 r = 1;
294 goto ERROR;
295 }
296
297 // Check if this message could be received
298 if (sd_netlink_message_is_error(message)) {
299 r = sd_netlink_message_get_errno(message);
300 if (r < 0)
301 ERROR("Could not receive link message: %m\n");
302
303 goto IGNORE;
304 }
305
306 // Fetch the message type
307 r = sd_netlink_message_get_type(message, &type);
308 if (r < 0) {
309 ERROR("Could not fetch message type: %m\n");
310 goto IGNORE;
311 }
312
313 // Check type
314 switch (type) {
315 case RTM_NEWLINK:
316 case RTM_DELLINK:
317 break;
318
319 default:
320 ERROR("Received an unexpected message (type %u)\n", type);
321 goto IGNORE;
322 }
323
324 // Fetch the interface index
325 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
326 if (r < 0) {
327 ERROR("Could not fetch ifindex: %m\n");
328 goto IGNORE;
329 }
330
331 // Check interface index
332 if (ifindex <= 0) {
333 ERROR("Received an invalid ifindex\n");
334 goto IGNORE;
335 }
336
337 // Fetch the interface name
338 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
339 if (r < 0) {
340 ERROR("Received a netlink message without interface name: %m\n");
341 goto IGNORE;
342 }
343
344 // Try finding an existing link
345 link = nw_daemon_get_link_by_ifindex(daemon, ifindex);
346
347 switch (type) {
348 case RTM_NEWLINK:
349 // If the link doesn't exist, create it
350 if (!link) {
351 r = nw_link_create(&link, daemon, ifindex);
352 if (r) {
353 ERROR("Could not create link: %m\n");
354 goto ERROR;
355 }
b8a1a68c
MT
356
357 // Add it to the list
358 r = nw_links_add_link(links, link);
359 if (r)
360 goto ERROR;
766f08ca
MT
361 }
362
f650f6cd
MT
363 // Import any data from the netlink message
364 r = nw_link_update(link, message);
365 if (r)
366 goto ERROR;
766f08ca 367
766f08ca
MT
368 break;
369
370 case RTM_DELLINK:
371 if (link)
372 nw_links_drop_link(links, link);
373
374 break;
375 }
376
377IGNORE:
378 r = 0;
379
380ERROR:
381 if (links)
382 nw_links_unref(links);
383 if (link)
384 nw_link_unref(link);
385
386 return r;
eaf649ee 387}
371b836a
MT
388
389static int __nw_link_destroy(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
390 nw_link* link = (nw_link*)data;
391 int r;
392
393 // Check if the operation was successful
394 r = sd_netlink_message_get_errno(m);
395 if (r < 0) {
396 ERROR("Could not remove link %d: %m\n", link->ifindex);
397 // XXX We should extract the error message
398
399 return 0;
400 }
401
402 // Mark this link as destroyed
403 link->state = NW_LINK_DESTROYED;
404
405 return 0;
406}
407
408int nw_link_destroy(nw_link* link) {
409 sd_netlink_message* m = NULL;
410 int r;
411
412 sd_netlink* rtnl = nw_daemon_get_rtnl(link->daemon);
413 if (!rtnl)
414 return 1;
415
416 DEBUG("Destroying link %d\n", link->ifindex);
417
418 // Create a new message
419 r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, link->ifindex);
420 if (r < 0) {
421 ERROR("Could not allocate RTM_DELLINK message: %m\n");
422 goto ERROR;
423 }
424
425 // Send the message
426 r = sd_netlink_call_async(rtnl, NULL, m, __nw_link_destroy,
427 __nw_link_unref, nw_link_ref(link), -1, NULL);
428 if (r < 0) {
429 ERROR("Could not send rtnetlink message: %m\n");
430 goto ERROR;
431 }
432
433ERROR:
434 if (m)
435 sd_netlink_message_unref(m);
436
437 return r;
438}