]> git.ipfire.org Git - network.git/blob - src/networkd/link.c
2f95dc3070d310c04bc1008a8626d9bcc1b218e1
[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
50 int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
51 // Allocate a new object
52 nw_link* l = calloc(1, sizeof(*l));
53 if (!l)
54 return 1;
55
56 // Store a reference to the daemon
57 l->daemon = nw_daemon_ref(daemon);
58
59 // Initialize the reference counter
60 l->nrefs = 1;
61
62 // Store the ifindex
63 l->ifindex = ifindex;
64
65 DEBUG("New link allocated (ifindex = %d)\n", l->ifindex);
66
67 *link = l;
68
69 return 0;
70 }
71
72 static void nw_link_free(nw_link* link) {
73 DEBUG("Freeing link (ifindex = %d)\n", link->ifindex);
74
75 if (link->daemon)
76 nw_daemon_unref(link->daemon);
77 }
78
79 nw_link* nw_link_ref(nw_link* link) {
80 link->nrefs++;
81
82 return link;
83 }
84
85 nw_link* nw_link_unref(nw_link* link) {
86 if (--link->nrefs > 0)
87 return link;
88
89 nw_link_free(link);
90 return NULL;
91 }
92
93 int nw_link_ifindex(nw_link* link) {
94 return link->ifindex;
95 }
96
97 static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
98 const char* ifname = NULL;
99 int r;
100
101 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
102 if (r < 0) {
103 ERROR("Could not read link name for link %d: %m\n", link->ifindex);
104 return 1;
105 }
106
107 // Do nothing if the name is already set
108 if (strcmp(link->ifname, ifname) == 0)
109 return 0;
110
111 // Otherwise update the name
112 r = nw_string_set(link->ifname, ifname);
113 if (r) {
114 ERROR("Could not set link name: %m\n");
115 return 1;
116 }
117
118 DEBUG("Link %d has been renamed to '%s'\n", link->ifindex, link->ifname);
119
120 return 0;
121 }
122
123 static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
124 uint32_t mtu = 0;
125 uint32_t min_mtu = 0;
126 uint32_t max_mtu = 0;
127 int r;
128
129 // Read the MTU
130 r = sd_netlink_message_read_u32(message, IFLA_MTU, &mtu);
131 if (r < 0) {
132 ERROR("Could not read MTU for link %d: %m\n", link->ifindex);
133 return r;
134 }
135
136 // Read the minimum MTU
137 r = sd_netlink_message_read_u32(message, IFLA_MIN_MTU, &min_mtu);
138 if (r < 0) {
139 ERROR("Could not read the minimum MTU for link %d: %m\n", link->ifindex);
140 return r;
141 }
142
143 // Read the maximum MTU
144 r = sd_netlink_message_read_u32(message, IFLA_MAX_MTU, &max_mtu);
145 if (r < 0) {
146 ERROR("Could not read the maximum MTU for link %d: %m\n", link->ifindex);
147 return r;
148 }
149
150 // Set the maximum MTU to infinity
151 if (!max_mtu)
152 max_mtu = UINT32_MAX;
153
154 // Store min/max MTU
155 link->min_mtu = min_mtu;
156 link->max_mtu = max_mtu;
157
158 // End here, if the MTU has not been changed
159 if (link->mtu == mtu)
160 return 0;
161
162 DEBUG("Link %d: MTU has changed to %" PRIu32 " (min: %" PRIu32 ", max: %" PRIu32 ")\n",
163 link->ifindex, link->mtu, link->min_mtu, link->max_mtu);
164
165 // Store MTU
166 link->mtu = mtu;
167
168 return 0;
169 }
170
171 /*
172 This function is called whenever anything changes, so that we can
173 update our internal link object.
174 */
175 static int nw_link_update(nw_link* link, sd_netlink_message* message) {
176 int r;
177
178 // Update the interface name
179 r = nw_link_update_ifname(link, message);
180 if (r)
181 return r;
182
183 // Update the MTU
184 r = nw_link_update_mtu(link, message);
185 if (r)
186 return r;
187
188 return 0;
189 }
190
191 int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
192 nw_links* links = NULL;
193 nw_link* link = NULL;
194 const char* ifname = NULL;
195 int ifindex;
196 uint16_t type;
197 int r;
198
199 nw_daemon* daemon = (nw_daemon*)data;
200
201 // Fetch links
202 links = nw_daemon_links(daemon);
203 if (!links) {
204 r = 1;
205 goto ERROR;
206 }
207
208 // Check if this message could be received
209 if (sd_netlink_message_is_error(message)) {
210 r = sd_netlink_message_get_errno(message);
211 if (r < 0)
212 ERROR("Could not receive link message: %m\n");
213
214 goto IGNORE;
215 }
216
217 // Fetch the message type
218 r = sd_netlink_message_get_type(message, &type);
219 if (r < 0) {
220 ERROR("Could not fetch message type: %m\n");
221 goto IGNORE;
222 }
223
224 // Check type
225 switch (type) {
226 case RTM_NEWLINK:
227 case RTM_DELLINK:
228 break;
229
230 default:
231 ERROR("Received an unexpected message (type %u)\n", type);
232 goto IGNORE;
233 }
234
235 // Fetch the interface index
236 r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
237 if (r < 0) {
238 ERROR("Could not fetch ifindex: %m\n");
239 goto IGNORE;
240 }
241
242 // Check interface index
243 if (ifindex <= 0) {
244 ERROR("Received an invalid ifindex\n");
245 goto IGNORE;
246 }
247
248 // Fetch the interface name
249 r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
250 if (r < 0) {
251 ERROR("Received a netlink message without interface name: %m\n");
252 goto IGNORE;
253 }
254
255 // Try finding an existing link
256 link = nw_daemon_get_link_by_ifindex(daemon, ifindex);
257
258 switch (type) {
259 case RTM_NEWLINK:
260 // If the link doesn't exist, create it
261 if (!link) {
262 r = nw_link_create(&link, daemon, ifindex);
263 if (r) {
264 ERROR("Could not create link: %m\n");
265 goto ERROR;
266 }
267
268 // Add it to the list
269 r = nw_links_add_link(links, link);
270 if (r)
271 goto ERROR;
272 }
273
274 // Import any data from the netlink message
275 r = nw_link_update(link, message);
276 if (r)
277 goto ERROR;
278
279 break;
280
281 case RTM_DELLINK:
282 if (link)
283 nw_links_drop_link(links, link);
284
285 break;
286 }
287
288 IGNORE:
289 r = 0;
290
291 ERROR:
292 if (links)
293 nw_links_unref(links);
294 if (link)
295 nw_link_unref(link);
296
297 return r;
298 }