1 /*
2 * Copyright (C) 2010-2026 Red Hat, Inc. All rights reserved.
3 *
4 * Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
5 *
6 * This software licensed under LGPL-2.0+
7 */
8
9 #include "config.h"
10
11 #ifdef KNET_LINUX
12
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <sys/ioctl.h>
19 #include <net/ethernet.h>
20 #include <net/if.h>
21 #include <linux/if_tun.h>
22 #include <netinet/ether.h>
23 #include <netlink/netlink.h>
24 #include <netlink/route/addr.h>
25 #include <netlink/route/link.h>
26 #include <netlink/errno.h>
27
28 #include "libnozzle.h"
29 #include "internals.h"
30
31 /*
32 * Convert libnl error codes to errno values for better error reporting.
33 * libnl functions return negative NLE_* error codes, which need translation
34 * to standard errno values that applications expect.
35 */
36 static int nlerr_to_errno(int nlerr)
37 {
38 if (nlerr >= 0)
39 return 0;
40
41 /*
42 * NLE_* error codes are small negative integers.
43 * Kernel errors passed through netlink are already errno values.
44 * Use NLE_MAX as threshold to distinguish between the two.
45 */
46 if (-nlerr > NLE_MAX) {
47 /* Already an errno value from kernel */
48 return -nlerr;
49 }
50
51 /* Common NLE_* to errno mappings */
52 switch (nlerr) {
53 case NLE_NOMEM:
54 return ENOMEM;
55 case NLE_EXIST:
56 return EEXIST;
57 case NLE_NOADDR:
58 return EADDRNOTAVAIL;
59 case NLE_OBJ_NOTFOUND:
60 return ENOENT;
61 case NLE_INVAL:
62 return EINVAL;
63 case NLE_BUSY:
64 return EBUSY;
65 case NLE_AGAIN:
66 return EAGAIN;
67 case NLE_NODEV:
68 return ENODEV;
69 case NLE_OPNOTSUPP:
70 return EOPNOTSUPP;
|
CID (unavailable; MK=80d8e61906ca2ab0e8b77b73d10a316f) (#10 of 10): Logically dead code (DEADCODE): |
|
(3) Event dead_error_condition: |
The "switch" governing value "nlerr" cannot be "28". |
|
(4) Event dead_error_begin: |
Execution cannot reach this statement: "case 28:". |
| Also see events: |
[cond_at_most][at_most] |
71 case NLE_PERM:
72 return EPERM;
73 default:
74 return EINVAL;
75 }
76 }
77
78 int _platform_init(struct nozzle_lib_config *lib_cfg)
79 {
80 lib_cfg->nlsock = nl_socket_alloc();
81 if (!lib_cfg->nlsock) {
82 errno = ENOMEM;
83 return -1;
84 }
85
86 if (nl_connect(lib_cfg->nlsock, NETLINK_ROUTE) < 0) {
87 nl_socket_free(lib_cfg->nlsock);
88 lib_cfg->nlsock = NULL;
89 errno = EBUSY;
90 return -1;
91 }
92
93 return 0;
94 }
95
96 void _platform_fini(struct nozzle_lib_config *lib_cfg)
97 {
98 if (lib_cfg->nlsock) {
99 nl_socket_free(lib_cfg->nlsock);
100 lib_cfg->nlsock = NULL;
101 }
102 }
103
104 int _platform_add_ip(nozzle_t nozzle, const char *ipaddr, const char *prefix, int secondary)
105 {
106 struct rtnl_addr *addr = NULL;
107 struct nl_addr *local_addr = NULL;
108 struct nl_addr *bcast_addr = NULL;
109 struct nl_cache *cache = NULL;
110 char *broadcast = NULL;
111 int fam;
112 int ifindex;
113 int nlerr;
114 int err = 0;
115 if (!strchr(ipaddr, ':')) {
116 fam = AF_INET;
117 broadcast = generate_v4_broadcast(ipaddr, prefix);
118 if (!broadcast) {
119 errno = EINVAL;
120 return -1;
121 }
122 } else {
123 fam = AF_INET6;
124 }
125
126 addr = rtnl_addr_alloc();
127 if (!addr) {
128 errno = ENOMEM;
129 err = -1;
130 goto out;
131 }
132
133 nlerr = rtnl_link_alloc_cache(lib_cfg.nlsock, AF_UNSPEC, &cache);
134 if (nlerr < 0) {
135 errno = nlerr_to_errno(nlerr);
136 err = -1;
137 goto out;
138 }
139
140 ifindex = rtnl_link_name2i(cache, nozzle->name);
141 if (ifindex == 0) {
142 errno = ENOENT;
143 err = -1;
144 goto out;
145 }
146
147 rtnl_addr_set_ifindex(addr, ifindex);
148
149 nlerr = nl_addr_parse(ipaddr, fam, &local_addr);
150 if (nlerr < 0) {
151 errno = nlerr_to_errno(nlerr);
152 err = -1;
153 goto out;
154 }
155
156 nlerr = rtnl_addr_set_local(addr, local_addr);
157 if (nlerr < 0) {
158 errno = nlerr_to_errno(nlerr);
159 err = -1;
160 goto out;
161 }
162
163 if (broadcast) {
164 nlerr = nl_addr_parse(broadcast, fam, &bcast_addr);
165 if (nlerr < 0) {
166 errno = nlerr_to_errno(nlerr);
167 err = -1;
168 goto out;
169 }
170
171 nlerr = rtnl_addr_set_broadcast(addr, bcast_addr);
172 if (nlerr < 0) {
173 errno = nlerr_to_errno(nlerr);
174 err = -1;
175 goto out;
176 }
177 }
178
179 rtnl_addr_set_prefixlen(addr, atoi(prefix));
180
181 nlerr = rtnl_addr_add(lib_cfg.nlsock, addr, 0);
182 if (nlerr < 0) {
183 errno = nlerr_to_errno(nlerr);
184 err = -1;
185 goto out;
186 }
187
188 out:
189 if (addr) {
190 rtnl_addr_put(addr);
191 }
192 if (local_addr) {
193 nl_addr_put(local_addr);
194 }
195 if (bcast_addr) {
196 nl_addr_put(bcast_addr);
197 }
198 if (cache) {
199 nl_cache_put(cache);
200 }
201 if (broadcast) {
202 free(broadcast);
203 }
204 return err;
205 }
206
207 int _platform_del_ip(nozzle_t nozzle, const char *ipaddr, const char *prefix, int secondary)
208 {
209 struct rtnl_addr *addr = NULL;
210 struct nl_addr *local_addr = NULL;
211 struct nl_cache *cache = NULL;
212 char *broadcast = NULL;
213 int fam;
214 int ifindex;
215 int nlerr;
216 int err = 0;
217 if (!strchr(ipaddr, ':')) {
218 fam = AF_INET;
219 broadcast = generate_v4_broadcast(ipaddr, prefix);
220 if (!broadcast) {
221 errno = EINVAL;
222 return -1;
223 }
224 } else {
225 fam = AF_INET6;
226 }
227
228 addr = rtnl_addr_alloc();
229 if (!addr) {
230 errno = ENOMEM;
231 err = -1;
232 goto out;
233 }
234
235 nlerr = rtnl_link_alloc_cache(lib_cfg.nlsock, AF_UNSPEC, &cache);
236 if (nlerr < 0) {
237 errno = nlerr_to_errno(nlerr);
238 err = -1;
239 goto out;
240 }
241
242 ifindex = rtnl_link_name2i(cache, nozzle->name);
243 if (ifindex == 0) {
244 errno = ENOENT;
245 err = -1;
246 goto out;
247 }
248
249 rtnl_addr_set_ifindex(addr, ifindex);
250
251 nlerr = nl_addr_parse(ipaddr, fam, &local_addr);
252 if (nlerr < 0) {
253 errno = nlerr_to_errno(nlerr);
254 err = -1;
255 goto out;
256 }
257
258 nlerr = rtnl_addr_set_local(addr, local_addr);
259 if (nlerr < 0) {
260 errno = nlerr_to_errno(nlerr);
261 err = -1;
262 goto out;
263 }
264
265 rtnl_addr_set_prefixlen(addr, atoi(prefix));
266
267 nlerr = rtnl_addr_delete(lib_cfg.nlsock, addr, 0);
268 if (nlerr < 0) {
269 errno = nlerr_to_errno(nlerr);
270 err = -1;
271 goto out;
272 }
273
274 out:
275 if (addr) {
276 rtnl_addr_put(addr);
277 }
278 if (local_addr) {
279 nl_addr_put(local_addr);
280 }
281 if (cache) {
282 nl_cache_put(cache);
283 }
284 if (broadcast) {
285 free(broadcast);
286 }
287 return err;
288 }
289
290 int _platform_create_tap(nozzle_t nozzle, char *devname, size_t devname_size)
291 {
292 struct ifreq ifr;
293 int savederrno;
294
295 nozzle->fd = open("/dev/net/tun", O_RDWR);
296 if (nozzle->fd < 0) {
297 return -1;
298 }
299
300 memset(&ifr, 0, sizeof(struct ifreq));
301 memmove(ifr.ifr_name, devname, IFNAMSIZ);
302 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
303
304 /*
305 * Use IFF_TUN_EXCL to prevent race conditions when creating named devices.
306 * Without this flag, another process could create the same device name
307 * between our check and creation, leading to unexpected behavior.
308 * Available since Linux 3.4. Fallback to non-exclusive if not supported.
309 */
310 if (strlen(devname) > 0) {
311 #ifdef IFF_TUN_EXCL
312 ifr.ifr_flags |= IFF_TUN_EXCL;
313 #endif
314 }
315
316 if (ioctl(nozzle->fd, TUNSETIFF, &ifr) < 0) {
317 savederrno = errno;
318 close(nozzle->fd);
319 nozzle->fd = -1;
320 errno = savederrno;
321 return -1;
322 }
323
324 if ((strlen(devname) > 0) && (strcmp(devname, ifr.ifr_name) != 0)) {
325 close(nozzle->fd);
326 nozzle->fd = -1;
327 errno = EBUSY;
328 return -1;
329 }
330
331 memmove(devname, ifr.ifr_name, IFNAMSIZ);
332 memmove(nozzle->name, ifr.ifr_name, IFNAMSIZ);
333
334 return nozzle->fd;
335 }
336
337 void _platform_close_tap(nozzle_t nozzle)
338 {
339 /* No platform-specific cleanup needed for Linux */
340 }
341
342 void _platform_destroy_tap(nozzle_t nozzle)
343 {
344 /* No platform-specific cleanup needed for Linux */
345 }
346
347 int _platform_get_mac(const nozzle_t nozzle, char **ether_addr)
348 {
349 struct ifreq ifr;
350 char mac[MACADDR_CHAR_MAX];
351 int err;
352
353 memset(&mac, 0, MACADDR_CHAR_MAX);
354 memset(&ifr, 0, sizeof(struct ifreq));
355 memmove(ifr.ifr_name, nozzle->name, IFNAMSIZ);
356
357 err = ioctl(lib_cfg.ioctlfd, SIOCGIFHWADDR, &ifr);
358 if (err) {
359 return -1;
360 }
361
362 ether_ntoa_r((struct ether_addr *)ifr.ifr_hwaddr.sa_data, mac);
363
364 *ether_addr = strdup(mac);
365 if (*ether_addr == NULL) {
366 errno = ENOMEM;
367 return -1;
368 }
369
370 return 0;
371 }
372
373 int _platform_set_mac(nozzle_t nozzle, const char *ether_addr)
374 {
375 struct ifreq ifr;
376 int err;
377
378 memset(&ifr, 0, sizeof(struct ifreq));
379 memmove(ifr.ifr_name, nozzle->name, IFNAMSIZ);
380
381 err = ioctl(lib_cfg.ioctlfd, SIOCGIFHWADDR, &ifr);
382 if (err) {
383 return -1;
384 }
385
386 memmove(ifr.ifr_hwaddr.sa_data, ether_aton(ether_addr), ETH_ALEN);
387
388 err = ioctl(lib_cfg.ioctlfd, SIOCSIFHWADDR, &ifr);
389 if (err) {
390 return -1;
391 }
392
393 return 0;
394 }
395
396 int _platform_get_mtu(const nozzle_t nozzle)
397 {
398 struct ifreq ifr;
399 int err;
400
401 memset(&ifr, 0, sizeof(ifr));
402 memmove(ifr.ifr_name, nozzle->name, IFNAMSIZ);
403
404 err = ioctl(lib_cfg.ioctlfd, SIOCGIFMTU, &ifr);
405 if (err) {
406 return -1;
407 }
408
409 return ifr.ifr_mtu;
410 }
411
412 #endif /* KNET_LINUX */
413