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