xref: /freebsd/sys/compat/linux/linux_if.c (revision d15f2551b25f79ddcbe289faa95e655100b952da)
1 /*-
2  * Copyright (c) 2015 Dmitry Chagin <dchagin@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/kernel.h>
29 #include <sys/ctype.h>
30 #include <sys/eventhandler.h>
31 #include <sys/jail.h>
32 #include <sys/socket.h>
33 #include <sys/sysctl.h>
34 #include <net/if.h>
35 #include <net/if_dl.h>
36 #include <net/if_types.h>
37 #include <net/if_var.h>
38 #include <net/if_private.h>
39 #include <net/vnet.h>
40 
41 #include <compat/linux/linux.h>
42 #include <compat/linux/linux_common.h>
43 #include <compat/linux/linux_mib.h>
44 
45 _Static_assert(LINUX_IFNAMSIZ == IFNAMSIZ, "Linux IFNAMSIZ");
46 
47 static bool use_real_ifnames = true;
48 static int
49 sysctl_linux_use_real_ifnames(SYSCTL_HANDLER_ARGS)
50 {
51 	int error;
52 
53 	error = sysctl_handle_bool(oidp, &use_real_ifnames, arg2, req);
54 	if (error != 0 || req->newptr == NULL)
55 		return (error);
56 	if (!use_real_ifnames)
57 		gone_in(17, "linux(4): %s: conversion of native interface "
58 		    "names to ethN is not needed to emulate modern Linux. "
59 		    "See https://systemd.io/PREDICTABLE_INTERFACE_NAMES. ",
60 		    oidp->oid_name);
61 	return (0);
62 }
63 SYSCTL_PROC(_compat_linux, OID_AUTO, use_real_ifnames,
64     CTLTYPE_U8 | CTLFLAG_RWTUN, 0, 0, sysctl_linux_use_real_ifnames, "CU",
65     "Use FreeBSD interface names instead of generating ethN aliases");
66 
67 VNET_DEFINE_STATIC(struct unrhdr *, linux_eth_unr);
68 #define	V_linux_eth_unr	VNET(linux_eth_unr)
69 
70 static eventhandler_tag ifnet_arrival_tag;
71 static eventhandler_tag ifnet_departure_tag;
72 
73 static void
74 linux_ifnet_arrival(void *arg __unused, struct ifnet *ifp)
75 {
76 	if (ifp->if_type == IFT_ETHER)
77 		ifp->if_linux_ethno = alloc_unr(V_linux_eth_unr);
78 }
79 
80 static void
81 linux_ifnet_departure(void *arg __unused, struct ifnet *ifp)
82 {
83 	if (ifp->if_type == IFT_ETHER)
84 		free_unr(V_linux_eth_unr, ifp->if_linux_ethno);
85 }
86 
87 void
88 linux_ifnet_init(void)
89 {
90 	ifnet_arrival_tag = EVENTHANDLER_REGISTER(ifnet_arrival_event,
91 	    linux_ifnet_arrival, NULL, EVENTHANDLER_PRI_FIRST);
92 	ifnet_departure_tag = EVENTHANDLER_REGISTER(ifnet_departure_event,
93 	    linux_ifnet_departure, NULL, EVENTHANDLER_PRI_LAST);
94 }
95 
96 void
97 linux_ifnet_uninit(void)
98 {
99 	EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ifnet_arrival_tag);
100 	EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifnet_departure_tag);
101 }
102 
103 static void
104 linux_ifnet_vnet_init(void *arg __unused)
105 {
106 	struct epoch_tracker et;
107 	struct if_iter it;
108 	if_t ifp;
109 
110 	V_linux_eth_unr = new_unrhdr(0, INT_MAX, NULL);
111 	NET_EPOCH_ENTER(et);
112 	for (ifp = if_iter_start(&it); ifp != NULL; ifp = if_iter_next(&it))
113 		linux_ifnet_arrival(NULL, ifp);
114 	NET_EPOCH_EXIT(et);
115 }
116 VNET_SYSINIT(linux_ifnet_vnet_init, SI_SUB_PROTO_IF, SI_ORDER_ANY,
117     linux_ifnet_vnet_init, NULL);
118 
119 static void
120 linux_ifnet_vnet_uninit(void *arg __unused)
121 {
122 	/*
123 	 * All cloned interfaces are already gone at this point, as well
124 	 * as interfaces that were if_vmove'd into this vnet.  However,
125 	 * if a jail has created IFT_ETHER interfaces in self, or has had
126 	 * physical Ethernet drivers attached in self, than we may have
127 	 * allocated entries in the unr(9), so clear it to avoid KASSERT.
128 	 */
129 	clear_unrhdr(V_linux_eth_unr);
130 	delete_unrhdr(V_linux_eth_unr);
131 }
132 VNET_SYSUNINIT(linux_ifnet_vnet_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY,
133     linux_ifnet_vnet_uninit, NULL);
134 
135 /*
136  * Translate a FreeBSD interface name to a Linux interface name
137  * by interface index, and return the number of bytes copied to lxname.
138  */
139 int
140 ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len)
141 {
142 	struct epoch_tracker et;
143 	struct ifnet *ifp;
144 	int ret;
145 
146 	ret = 0;
147 	CURVNET_SET(TD_TO_VNET(curthread));
148 	NET_EPOCH_ENTER(et);
149 	ifp = ifnet_byindex(idx);
150 	if (ifp != NULL)
151 		ret = ifname_bsd_to_linux_ifp(ifp, lxname, len);
152 	NET_EPOCH_EXIT(et);
153 	CURVNET_RESTORE();
154 	return (ret);
155 }
156 
157 /*
158  * Translate a FreeBSD interface name to a Linux interface name,
159  * and return the number of bytes copied to lxname, 0 if interface
160  * not found, -1 on error.
161  */
162 int
163 ifname_bsd_to_linux_ifp(const struct ifnet *ifp, char *lxname, size_t len)
164 {
165 	/*
166 	 * Linux loopback interface name is lo (not lo0),
167 	 * we translate lo to lo0, loX to loX.
168 	 */
169 	if (ifp->if_type == IFT_LOOP &&
170 	    strncmp(ifp->if_xname, "lo0", IFNAMSIZ) == 0)
171 		return (strlcpy(lxname, "lo", len));
172 
173 	/* Short-circuit non ethernet interfaces. */
174 	if (ifp->if_type != IFT_ETHER || use_real_ifnames)
175 		return (strlcpy(lxname, ifp->if_xname, len));
176 
177 	/* Determine the (relative) unit number for ethernet interfaces. */
178 	return (snprintf(lxname, len, "eth%d", ifp->if_linux_ethno));
179 }
180 
181 /*
182  * Translate a Linux interface name to a FreeBSD interface name,
183  * and return the associated ifnet structure
184  * bsdname and lxname need to be least IFNAMSIZ bytes long, but
185  * can point to the same buffer.
186  */
187 struct ifname_linux_to_ifp_cb_s {
188 	bool		is_lo;
189 	bool		is_eth;
190 	int		unit;
191 	const char	*lxname;
192 	if_t		ifp;
193 };
194 
195 static int
196 ifname_linux_to_ifp_cb(if_t ifp, void *arg)
197 {
198 	struct ifname_linux_to_ifp_cb_s *cbs = arg;
199 
200 	NET_EPOCH_ASSERT();
201 
202 	/*
203 	 * Allow Linux programs to use FreeBSD names. Don't presume
204 	 * we never have an interface named "eth", so don't make
205 	 * the test optional based on is_eth.
206 	 */
207 	if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0)
208 		goto out;
209 	if (cbs->is_eth && ifp->if_type == IFT_ETHER &&
210 	    ifp->if_linux_ethno == cbs->unit)
211 		goto out;
212 	if (cbs->is_lo && ifp->if_type == IFT_LOOP)
213 		goto out;
214 	return (0);
215 
216 out:
217 	cbs->ifp = ifp;
218 	return (1);
219 }
220 
221 struct ifnet *
222 ifname_linux_to_ifp(const char *lxname)
223 {
224 	struct ifname_linux_to_ifp_cb_s arg = {
225 		.lxname = lxname,
226 	};
227 	int len;
228 	char *ep;
229 
230 	NET_EPOCH_ASSERT();
231 
232 	for (len = 0; len < LINUX_IFNAMSIZ; ++len)
233 		if (!isalpha(lxname[len]) || lxname[len] == '\0')
234 			break;
235 	if (len == 0 || len == LINUX_IFNAMSIZ)
236 		return (NULL);
237 	/*
238 	 * Linux loopback interface name is lo (not lo0),
239 	 * we translate lo to lo0, loX to loX.
240 	 */
241 	arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0);
242 	arg.unit = (int)strtoul(lxname + len, &ep, 10);
243 	if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) &&
244 	    arg.is_lo == 0)
245 		return (NULL);
246 	arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0);
247 
248 	if_foreach(ifname_linux_to_ifp_cb, &arg);
249 	return (arg.ifp);
250 }
251 
252 int
253 ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname)
254 {
255 	struct epoch_tracker et;
256 	struct ifnet *ifp;
257 
258 	CURVNET_SET(TD_TO_VNET(td));
259 	NET_EPOCH_ENTER(et);
260 	ifp = ifname_linux_to_ifp(lxname);
261 	if (ifp != NULL && bsdname != NULL)
262 		strlcpy(bsdname, if_name(ifp), IFNAMSIZ);
263 	NET_EPOCH_EXIT(et);
264 	CURVNET_RESTORE();
265 	return (ifp != NULL ? 0 : EINVAL);
266 }
267 
268 unsigned short
269 linux_ifflags(struct ifnet *ifp)
270 {
271 	unsigned short flags;
272 
273 	NET_EPOCH_ASSERT();
274 
275 	flags = if_getflags(ifp) | if_getdrvflags(ifp);
276 	return (bsd_to_linux_ifflags(flags));
277 }
278 
279 unsigned short
280 bsd_to_linux_ifflags(int fl)
281 {
282 	unsigned short flags = 0;
283 
284 	if (fl & IFF_UP)
285 		flags |= LINUX_IFF_UP;
286 	if (fl & IFF_BROADCAST)
287 		flags |= LINUX_IFF_BROADCAST;
288 	if (fl & IFF_DEBUG)
289 		flags |= LINUX_IFF_DEBUG;
290 	if (fl & IFF_LOOPBACK)
291 		flags |= LINUX_IFF_LOOPBACK;
292 	if (fl & IFF_POINTOPOINT)
293 		flags |= LINUX_IFF_POINTOPOINT;
294 	if (fl & IFF_DRV_RUNNING)
295 		flags |= LINUX_IFF_RUNNING;
296 	if (fl & IFF_NOARP)
297 		flags |= LINUX_IFF_NOARP;
298 	if (fl & IFF_PROMISC)
299 		flags |= LINUX_IFF_PROMISC;
300 	if (fl & IFF_ALLMULTI)
301 		flags |= LINUX_IFF_ALLMULTI;
302 	if (fl & IFF_MULTICAST)
303 		flags |= LINUX_IFF_MULTICAST;
304 	return (flags);
305 }
306 
307 static u_int
308 linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count)
309 {
310 	struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
311 	struct l_sockaddr *lsa = arg;
312 
313 	if (count > 0)
314 		return (0);
315 	if (sdl->sdl_type != IFT_ETHER)
316 		return (0);
317 	bzero(lsa, sizeof(*lsa));
318 	lsa->sa_family = LINUX_ARPHRD_ETHER;
319 	bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN);
320 	return (1);
321 }
322 
323 int
324 linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa)
325 {
326 
327 	NET_EPOCH_ASSERT();
328 
329 	if (ifp->if_type == IFT_LOOP) {
330 		bzero(lsa, sizeof(*lsa));
331 		lsa->sa_family = LINUX_ARPHRD_LOOPBACK;
332 		return (0);
333 	}
334 	if (ifp->if_type != IFT_ETHER)
335 		return (ENOENT);
336 	if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0)
337 		return (0);
338 	return (ENOENT);
339 }
340