]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/link.c
networkd: Store operstate, too
[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 <net/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[IF_NAMESIZE];
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 static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
102 const char* ifname = NULL;
103 int r;
104
105 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
106 if (r < 0) {
107 ERROR("Could not read link name for link %d: %m\n", link->ifindex);
108 return 1;
109 }
110
111 // Do nothing if the name is already set
112 if (strcmp(link->ifname, ifname) == 0)
113 return 0;
114
115 // Otherwise update the name
116 r = nw_string_set(link->ifname, ifname);
117 if (r) {
118 ERROR("Could not set link name: %m\n");
119 return 1;
120 }
121
122 DEBUG("Link %d has been renamed to '%s'\n", link->ifindex, link->ifname);
123
124 return 0;
125 }
126
127 static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
128 uint32_t mtu = 0;
129 uint32_t min_mtu = 0;
130 uint32_t max_mtu = 0;
131 int r;
132
133 // Read the MTU
134 r = sd_netlink_message_read_u32(message, IFLA_MTU, &mtu);
135 if (r < 0) {
136 ERROR("Could not read MTU for link %d: %m\n", link->ifindex);
137 return r;
138 }
139
140 // Read the minimum MTU
141 r = sd_netlink_message_read_u32(message, IFLA_MIN_MTU, &min_mtu);
142 if (r < 0) {
143 ERROR("Could not read the minimum MTU for link %d: %m\n", link->ifindex);
144 return r;
145 }
146
147 // Read the maximum MTU
148 r = sd_netlink_message_read_u32(message, IFLA_MAX_MTU, &max_mtu);
149 if (r < 0) {
150 ERROR("Could not read the maximum MTU for link %d: %m\n", link->ifindex);
151 return r;
152 }
153
154 // Set the maximum MTU to infinity
155 if (!max_mtu)
156 max_mtu = UINT32_MAX;
157
158 // Store min/max MTU
159 link->min_mtu = min_mtu;
160 link->max_mtu = max_mtu;
161
162 // End here, if the MTU has not been changed
163 if (link->mtu == mtu)
164 return 0;
165
166 DEBUG("Link %d: MTU has changed to %" PRIu32 " (min: %" PRIu32 ", max: %" PRIu32 ")\n",
167 link->ifindex, link->mtu, link->min_mtu, link->max_mtu);
168
169 // Store MTU
170 link->mtu = mtu;
171
172 return 0;
173 }
174
175 static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
176 unsigned int flags = 0;
177 uint8_t operstate = 0;
178 int r;
179
180 // Fetch flags
181 r = sd_rtnl_message_link_get_flags(message, &flags);
182 if (r < 0) {
183 return DEBUG("Could not read link flags: %m\n");
184 return 1;
185 }
186
187 // Fetch operstate
188 r = sd_netlink_message_read_u8(message, IFLA_OPERSTATE, &operstate);
189 if (r < 1) {
190 ERROR("Could not read operstate: %m\n");
191 return 1;
192 }
193
194 // End here if there have been no changes
195 if (link->flags == flags && link->operstate == operstate)
196 return 0;
197
198 // XXX We should log any changes here
199
200 // Store the new flags & operstate
201 link->flags = flags;
202 link->operstate = operstate;
203
204
205 return 0;
206 }
207
208 /*
209 This function is called whenever anything changes, so that we can
210 update our internal link object.
211 */
212 static int nw_link_update(nw_link* link, sd_netlink_message* message) {
213 int r;
214
215 // Update the interface name
216 r = nw_link_update_ifname(link, message);
217 if (r)
218 return r;
219
220 // Update the MTU
221 r = nw_link_update_mtu(link, message);
222 if (r)
223 return r;
224
225 // Update flags
226 r = nw_link_update_flags(link, message);
227 if (r)
228 return r;
229
230 return 0;
231 }
232
233 int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
234 nw_links* links = NULL;
235 nw_link* link = NULL;
236 const char* ifname = NULL;
237 int ifindex;
238 uint16_t type;
239 int r;
240
241 nw_daemon* daemon = (nw_daemon*)data;
242
243 // Fetch links
244 links = nw_daemon_links(daemon);
245 if (!links) {
246 r = 1;
247 goto ERROR;
248 }
249
250 // Check if this message could be received
251 if (sd_netlink_message_is_error(message)) {
252 r = sd_netlink_message_get_errno(message);
253 if (r < 0)
254 ERROR("Could not receive link message: %m\n");
255
256 goto IGNORE;
257 }
258
259 // Fetch the message type
260 r = sd_netlink_message_get_type(message, &type);
261 if (r < 0) {
262 ERROR("Could not fetch message type: %m\n");
263 goto IGNORE;
264 }
265
266 // Check type
267 switch (type) {
268 case RTM_NEWLINK:
269 case RTM_DELLINK:
270 break;
271
272 default:
273 ERROR("Received an unexpected message (type %u)\n", type);
274 goto IGNORE;
275 }
276
277 // Fetch the interface index
278 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
279 if (r < 0) {
280 ERROR("Could not fetch ifindex: %m\n");
281 goto IGNORE;
282 }
283
284 // Check interface index
285 if (ifindex <= 0) {
286 ERROR("Received an invalid ifindex\n");
287 goto IGNORE;
288 }
289
290 // Fetch the interface name
291 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
292 if (r < 0) {
293 ERROR("Received a netlink message without interface name: %m\n");
294 goto IGNORE;
295 }
296
297 // Try finding an existing link
298 link = nw_daemon_get_link_by_ifindex(daemon, ifindex);
299
300 switch (type) {
301 case RTM_NEWLINK:
302 // If the link doesn't exist, create it
303 if (!link) {
304 r = nw_link_create(&link, daemon, ifindex);
305 if (r) {
306 ERROR("Could not create link: %m\n");
307 goto ERROR;
308 }
309
310 // Add it to the list
311 r = nw_links_add_link(links, link);
312 if (r)
313 goto ERROR;
314 }
315
316 // Import any data from the netlink message
317 r = nw_link_update(link, message);
318 if (r)
319 goto ERROR;
320
321 break;
322
323 case RTM_DELLINK:
324 if (link)
325 nw_links_drop_link(links, link);
326
327 break;
328 }
329
330 IGNORE:
331 r = 0;
332
333 ERROR:
334 if (links)
335 nw_links_unref(links);
336 if (link)
337 nw_link_unref(link);
338
339 return r;
340 }