]> git.ipfire.org Git - network.git/blame - src/networkd/link.c
networkd: Try to reconfigure all ports and zones on startup
[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
MT
43
44 // MTU
45 uint32_t mtu;
46 uint32_t min_mtu;
47 uint32_t max_mtu;
c65300b4
MT
48
49 // Flags
50 unsigned int flags;
69ef50c7 51 uint8_t operstate;
87a1e1e0
MT
52};
53
2361667e 54int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
87a1e1e0 55 // Allocate a new object
2361667e 56 nw_link* l = calloc(1, sizeof(*l));
87a1e1e0
MT
57 if (!l)
58 return 1;
59
60 // Store a reference to the daemon
61 l->daemon = nw_daemon_ref(daemon);
62
63 // Initialize the reference counter
64 l->nrefs = 1;
65
66 // Store the ifindex
67 l->ifindex = ifindex;
68
766f08ca
MT
69 DEBUG("New link allocated (ifindex = %d)\n", l->ifindex);
70
87a1e1e0
MT
71 *link = l;
72
73 return 0;
74}
75
2361667e 76static void nw_link_free(nw_link* link) {
766f08ca
MT
77 DEBUG("Freeing link (ifindex = %d)\n", link->ifindex);
78
87a1e1e0
MT
79 if (link->daemon)
80 nw_daemon_unref(link->daemon);
81}
82
2361667e 83nw_link* nw_link_ref(nw_link* link) {
87a1e1e0
MT
84 link->nrefs++;
85
86 return link;
87}
88
2361667e 89nw_link* nw_link_unref(nw_link* link) {
87a1e1e0
MT
90 if (--link->nrefs > 0)
91 return link;
92
93 nw_link_free(link);
94 return NULL;
95}
eaf649ee 96
2361667e 97int nw_link_ifindex(nw_link* link) {
766f08ca
MT
98 return link->ifindex;
99}
eaf649ee 100
207abe4e
MT
101const char* nw_link_ifname(nw_link* link) {
102 return link->ifname;
103}
104
dfd49c2c
MT
105// Carrier
106
107int nw_link_has_carrier(nw_link* link) {
108 return link->operstate == IF_OPER_UP;
109}
110
111static int nw_link_carrier_gained(nw_link* link) {
112 return 0; // XXX TODO
113}
114
115static int nw_link_carrier_lost(nw_link* link) {
116 return 0; // XXX TODO
117}
118
2361667e 119static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
f650f6cd
MT
120 const char* ifname = NULL;
121 int r;
122
123 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
124 if (r < 0) {
125 ERROR("Could not read link name for link %d: %m\n", link->ifindex);
126 return 1;
127 }
128
129 // Do nothing if the name is already set
130 if (strcmp(link->ifname, ifname) == 0)
131 return 0;
132
133 // Otherwise update the name
134 r = nw_string_set(link->ifname, ifname);
135 if (r) {
136 ERROR("Could not set link name: %m\n");
137 return 1;
138 }
139
140 DEBUG("Link %d has been renamed to '%s'\n", link->ifindex, link->ifname);
141
142 return 0;
143}
144
2361667e 145static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
f37df5ea
MT
146 uint32_t mtu = 0;
147 uint32_t min_mtu = 0;
148 uint32_t max_mtu = 0;
149 int r;
150
151 // Read the MTU
152 r = sd_netlink_message_read_u32(message, IFLA_MTU, &mtu);
153 if (r < 0) {
154 ERROR("Could not read MTU for link %d: %m\n", link->ifindex);
155 return r;
156 }
157
158 // Read the minimum MTU
159 r = sd_netlink_message_read_u32(message, IFLA_MIN_MTU, &min_mtu);
160 if (r < 0) {
161 ERROR("Could not read the minimum MTU for link %d: %m\n", link->ifindex);
162 return r;
163 }
164
165 // Read the maximum MTU
166 r = sd_netlink_message_read_u32(message, IFLA_MAX_MTU, &max_mtu);
167 if (r < 0) {
168 ERROR("Could not read the maximum MTU for link %d: %m\n", link->ifindex);
169 return r;
170 }
171
172 // Set the maximum MTU to infinity
173 if (!max_mtu)
174 max_mtu = UINT32_MAX;
175
176 // Store min/max MTU
177 link->min_mtu = min_mtu;
178 link->max_mtu = max_mtu;
179
180 // End here, if the MTU has not been changed
181 if (link->mtu == mtu)
182 return 0;
183
184 DEBUG("Link %d: MTU has changed to %" PRIu32 " (min: %" PRIu32 ", max: %" PRIu32 ")\n",
185 link->ifindex, link->mtu, link->min_mtu, link->max_mtu);
186
187 // Store MTU
188 link->mtu = mtu;
189
190 return 0;
191}
192
c65300b4
MT
193static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
194 unsigned int flags = 0;
69ef50c7 195 uint8_t operstate = 0;
c65300b4
MT
196 int r;
197
198 // Fetch flags
199 r = sd_rtnl_message_link_get_flags(message, &flags);
200 if (r < 0) {
1a70a686 201 DEBUG("Could not read link flags: %m\n");
c65300b4
MT
202 return 1;
203 }
204
69ef50c7
MT
205 // Fetch operstate
206 r = sd_netlink_message_read_u8(message, IFLA_OPERSTATE, &operstate);
207 if (r < 1) {
208 ERROR("Could not read operstate: %m\n");
209 return 1;
210 }
211
c65300b4 212 // End here if there have been no changes
69ef50c7 213 if (link->flags == flags && link->operstate == operstate)
c65300b4
MT
214 return 0;
215
216 // XXX We should log any changes here
217
dfd49c2c
MT
218 // Fetch current carrier state
219 const int had_carrier = nw_link_has_carrier(link);
220
69ef50c7 221 // Store the new flags & operstate
c65300b4 222 link->flags = flags;
69ef50c7
MT
223 link->operstate = operstate;
224
dfd49c2c
MT
225 // Notify if carrier was gained or lost
226 if (!had_carrier && nw_link_has_carrier(link)) {
227 r = nw_link_carrier_gained(link);
228 if (r < 0)
229 return r;
230
231 } else if (had_carrier && !nw_link_has_carrier(link)) {
232 r = nw_link_carrier_lost(link);
233 if (r < 0)
234 return r;
235 }
c65300b4
MT
236
237 return 0;
238}
239
f650f6cd
MT
240/*
241 This function is called whenever anything changes, so that we can
242 update our internal link object.
243*/
2361667e 244static int nw_link_update(nw_link* link, sd_netlink_message* message) {
f650f6cd
MT
245 int r;
246
247 // Update the interface name
248 r = nw_link_update_ifname(link, message);
249 if (r)
250 return r;
251
f37df5ea
MT
252 // Update the MTU
253 r = nw_link_update_mtu(link, message);
254 if (r)
255 return r;
256
c65300b4
MT
257 // Update flags
258 r = nw_link_update_flags(link, message);
259 if (r)
260 return r;
261
f650f6cd
MT
262 return 0;
263}
264
766f08ca 265int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
2361667e
MT
266 nw_links* links = NULL;
267 nw_link* link = NULL;
766f08ca
MT
268 const char* ifname = NULL;
269 int ifindex;
270 uint16_t type;
271 int r;
272
2361667e 273 nw_daemon* daemon = (nw_daemon*)data;
766f08ca
MT
274
275 // Fetch links
276 links = nw_daemon_links(daemon);
277 if (!links) {
278 r = 1;
279 goto ERROR;
280 }
281
282 // Check if this message could be received
283 if (sd_netlink_message_is_error(message)) {
284 r = sd_netlink_message_get_errno(message);
285 if (r < 0)
286 ERROR("Could not receive link message: %m\n");
287
288 goto IGNORE;
289 }
290
291 // Fetch the message type
292 r = sd_netlink_message_get_type(message, &type);
293 if (r < 0) {
294 ERROR("Could not fetch message type: %m\n");
295 goto IGNORE;
296 }
297
298 // Check type
299 switch (type) {
300 case RTM_NEWLINK:
301 case RTM_DELLINK:
302 break;
303
304 default:
305 ERROR("Received an unexpected message (type %u)\n", type);
306 goto IGNORE;
307 }
308
309 // Fetch the interface index
310 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
311 if (r < 0) {
312 ERROR("Could not fetch ifindex: %m\n");
313 goto IGNORE;
314 }
315
316 // Check interface index
317 if (ifindex <= 0) {
318 ERROR("Received an invalid ifindex\n");
319 goto IGNORE;
320 }
321
322 // Fetch the interface name
323 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
324 if (r < 0) {
325 ERROR("Received a netlink message without interface name: %m\n");
326 goto IGNORE;
327 }
328
329 // Try finding an existing link
330 link = nw_daemon_get_link_by_ifindex(daemon, ifindex);
331
332 switch (type) {
333 case RTM_NEWLINK:
334 // If the link doesn't exist, create it
335 if (!link) {
336 r = nw_link_create(&link, daemon, ifindex);
337 if (r) {
338 ERROR("Could not create link: %m\n");
339 goto ERROR;
340 }
b8a1a68c
MT
341
342 // Add it to the list
343 r = nw_links_add_link(links, link);
344 if (r)
345 goto ERROR;
766f08ca
MT
346 }
347
f650f6cd
MT
348 // Import any data from the netlink message
349 r = nw_link_update(link, message);
350 if (r)
351 goto ERROR;
766f08ca 352
766f08ca
MT
353 break;
354
355 case RTM_DELLINK:
356 if (link)
357 nw_links_drop_link(links, link);
358
359 break;
360 }
361
362IGNORE:
363 r = 0;
364
365ERROR:
366 if (links)
367 nw_links_unref(links);
368 if (link)
369 nw_link_unref(link);
370
371 return r;
eaf649ee 372}