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   	{
(1) Event cond_at_most: Condition "nlerr >= 0", taking false branch. Now the value of "nlerr" is at most -1.
Also see events: [at_most][dead_error_condition][dead_error_begin]
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 */
(2) Event at_most: When switching on "nlerr", the value of "nlerr" must be at most -1.
Also see events: [cond_at_most][dead_error_condition][dead_error_begin]
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