xref: /freebsd/sys/compat/linux/linux_if.c (revision fbf05d2147b1add8b760be166c4b1fd4499ebce8)
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/ctype.h>
28 #include <sys/jail.h>
29 #include <sys/socket.h>
30 #include <sys/sysctl.h>
31 #include <net/if.h>
32 #include <net/if_dl.h>
33 #include <net/if_types.h>
34 #include <net/if_var.h>
35 
36 #include <compat/linux/linux.h>
37 #include <compat/linux/linux_common.h>
38 #include <compat/linux/linux_mib.h>
39 
40 _Static_assert(LINUX_IFNAMSIZ == IFNAMSIZ, "Linux IFNAMSIZ");
41 
42 static bool use_real_ifnames = false;
43 SYSCTL_BOOL(_compat_linux, OID_AUTO, use_real_ifnames, CTLFLAG_RWTUN,
44     &use_real_ifnames, 0,
45     "Use FreeBSD interface names instead of generating ethN aliases");
46 
47 /*
48  * Criteria for interface name translation
49  */
50 #define	IFP_IS_ETH(ifp)		(if_gettype(ifp) == IFT_ETHER)
51 #define	IFP_IS_LOOP(ifp)	(if_gettype(ifp) == IFT_LOOP)
52 
53 /*
54  * Translate a FreeBSD interface name to a Linux interface name
55  * by interface name, and return the number of bytes copied to lxname.
56  */
57 int
58 ifname_bsd_to_linux_name(const char *bsdname, char *lxname, size_t len)
59 {
60 	struct epoch_tracker et;
61 	struct ifnet *ifp;
62 	int ret;
63 
64 	CURVNET_ASSERT_SET();
65 
66 	ret = 0;
67 	NET_EPOCH_ENTER(et);
68 	ifp = ifunit(bsdname);
69 	if (ifp != NULL)
70 		ret = ifname_bsd_to_linux_ifp(ifp, lxname, len);
71 	NET_EPOCH_EXIT(et);
72 	return (ret);
73 }
74 
75 /*
76  * Translate a FreeBSD interface name to a Linux interface name
77  * by interface index, and return the number of bytes copied to lxname.
78  */
79 int
80 ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len)
81 {
82 	struct epoch_tracker et;
83 	struct ifnet *ifp;
84 	int ret;
85 
86 	ret = 0;
87 	CURVNET_SET(TD_TO_VNET(curthread));
88 	NET_EPOCH_ENTER(et);
89 	ifp = ifnet_byindex(idx);
90 	if (ifp != NULL)
91 		ret = ifname_bsd_to_linux_ifp(ifp, lxname, len);
92 	NET_EPOCH_EXIT(et);
93 	CURVNET_RESTORE();
94 	return (ret);
95 }
96 
97 /*
98  * Translate a FreeBSD interface name to a Linux interface name,
99  * and return the number of bytes copied to lxname, 0 if interface
100  * not found, -1 on error.
101  */
102 struct ifname_bsd_to_linux_ifp_cb_s {
103 	struct ifnet	*ifp;
104 	int		ethno;
105 	char		*lxname;
106 	size_t		len;
107 };
108 
109 static int
110 ifname_bsd_to_linux_ifp_cb(if_t ifp, void *arg)
111 {
112 	struct ifname_bsd_to_linux_ifp_cb_s *cbs = arg;
113 
114 	if (ifp == cbs->ifp)
115 		return (snprintf(cbs->lxname, cbs->len, "eth%d", cbs->ethno));
116 	if (IFP_IS_ETH(ifp))
117 		cbs->ethno++;
118 	return (0);
119 }
120 
121 int
122 ifname_bsd_to_linux_ifp(struct ifnet *ifp, char *lxname, size_t len)
123 {
124 	struct ifname_bsd_to_linux_ifp_cb_s arg = {
125 		.ifp = ifp,
126 		.ethno = 0,
127 		.lxname = lxname,
128 		.len = len,
129 	};
130 
131 	NET_EPOCH_ASSERT();
132 
133 	/*
134 	 * Linux loopback interface name is lo (not lo0),
135 	 * we translate lo to lo0, loX to loX.
136 	 */
137 	if (IFP_IS_LOOP(ifp) && strncmp(if_name(ifp), "lo0", IFNAMSIZ) == 0)
138 		return (strlcpy(lxname, "lo", len));
139 
140 	/* Short-circuit non ethernet interfaces. */
141 	if (!IFP_IS_ETH(ifp) || use_real_ifnames)
142 		return (strlcpy(lxname, if_name(ifp), len));
143 
144 	/* Determine the (relative) unit number for ethernet interfaces. */
145 	return (if_foreach(ifname_bsd_to_linux_ifp_cb, &arg));
146 }
147 
148 /*
149  * Translate a Linux interface name to a FreeBSD interface name,
150  * and return the associated ifnet structure
151  * bsdname and lxname need to be least IFNAMSIZ bytes long, but
152  * can point to the same buffer.
153  */
154 struct ifname_linux_to_ifp_cb_s {
155 	bool		is_lo;
156 	bool		is_eth;
157 	int		ethno;
158 	int		unit;
159 	const char	*lxname;
160 	if_t		ifp;
161 };
162 
163 static int
164 ifname_linux_to_ifp_cb(if_t ifp, void *arg)
165 {
166 	struct ifname_linux_to_ifp_cb_s *cbs = arg;
167 
168 	NET_EPOCH_ASSERT();
169 
170 	/*
171 	 * Allow Linux programs to use FreeBSD names. Don't presume
172 	 * we never have an interface named "eth", so don't make
173 	 * the test optional based on is_eth.
174 	 */
175 	if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0)
176 		goto out;
177 	if (cbs->is_eth && IFP_IS_ETH(ifp) && cbs->unit == cbs->ethno)
178 		goto out;
179 	if (cbs->is_lo && IFP_IS_LOOP(ifp))
180 		goto out;
181 	if (IFP_IS_ETH(ifp))
182 		cbs->ethno++;
183 	return (0);
184 
185 out:
186 	cbs->ifp = ifp;
187 	return (1);
188 }
189 
190 struct ifnet *
191 ifname_linux_to_ifp(struct thread *td, const char *lxname)
192 {
193 	struct ifname_linux_to_ifp_cb_s arg = {
194 		.ethno = 0,
195 		.lxname = lxname,
196 		.ifp = NULL,
197 	};
198 	int len;
199 	char *ep;
200 
201 	NET_EPOCH_ASSERT();
202 
203 	for (len = 0; len < LINUX_IFNAMSIZ; ++len)
204 		if (!isalpha(lxname[len]) || lxname[len] == '\0')
205 			break;
206 	if (len == 0 || len == LINUX_IFNAMSIZ)
207 		return (NULL);
208 	/*
209 	 * Linux loopback interface name is lo (not lo0),
210 	 * we translate lo to lo0, loX to loX.
211 	 */
212 	arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0);
213 	arg.unit = (int)strtoul(lxname + len, &ep, 10);
214 	if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) &&
215 	    arg.is_lo == 0)
216 		return (NULL);
217 	arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0);
218 
219 	if_foreach(ifname_linux_to_ifp_cb, &arg);
220 	return (arg.ifp);
221 }
222 
223 int
224 ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname)
225 {
226 	struct epoch_tracker et;
227 	struct ifnet *ifp;
228 
229 	CURVNET_SET(TD_TO_VNET(td));
230 	NET_EPOCH_ENTER(et);
231 	ifp = ifname_linux_to_ifp(td, lxname);
232 	if (ifp != NULL && bsdname != NULL)
233 		strlcpy(bsdname, if_name(ifp), IFNAMSIZ);
234 	NET_EPOCH_EXIT(et);
235 	CURVNET_RESTORE();
236 	return (ifp != NULL ? 0 : EINVAL);
237 }
238 
239 unsigned short
240 linux_ifflags(struct ifnet *ifp)
241 {
242 	unsigned short flags;
243 
244 	NET_EPOCH_ASSERT();
245 
246 	flags = if_getflags(ifp) | if_getdrvflags(ifp);
247 	return (bsd_to_linux_ifflags(flags));
248 }
249 
250 unsigned short
251 bsd_to_linux_ifflags(int fl)
252 {
253 	unsigned short flags = 0;
254 
255 	if (fl & IFF_UP)
256 		flags |= LINUX_IFF_UP;
257 	if (fl & IFF_BROADCAST)
258 		flags |= LINUX_IFF_BROADCAST;
259 	if (fl & IFF_DEBUG)
260 		flags |= LINUX_IFF_DEBUG;
261 	if (fl & IFF_LOOPBACK)
262 		flags |= LINUX_IFF_LOOPBACK;
263 	if (fl & IFF_POINTOPOINT)
264 		flags |= LINUX_IFF_POINTOPOINT;
265 	if (fl & IFF_DRV_RUNNING)
266 		flags |= LINUX_IFF_RUNNING;
267 	if (fl & IFF_NOARP)
268 		flags |= LINUX_IFF_NOARP;
269 	if (fl & IFF_PROMISC)
270 		flags |= LINUX_IFF_PROMISC;
271 	if (fl & IFF_ALLMULTI)
272 		flags |= LINUX_IFF_ALLMULTI;
273 	if (fl & IFF_MULTICAST)
274 		flags |= LINUX_IFF_MULTICAST;
275 	return (flags);
276 }
277 
278 static u_int
279 linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count)
280 {
281 	struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
282 	struct l_sockaddr *lsa = arg;
283 
284 	if (count > 0)
285 		return (0);
286 	if (sdl->sdl_type != IFT_ETHER)
287 		return (0);
288 	bzero(lsa, sizeof(*lsa));
289 	lsa->sa_family = LINUX_ARPHRD_ETHER;
290 	bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN);
291 	return (1);
292 }
293 
294 int
295 linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa)
296 {
297 
298 	NET_EPOCH_ASSERT();
299 
300 	if (IFP_IS_LOOP(ifp)) {
301 		bzero(lsa, sizeof(*lsa));
302 		lsa->sa_family = LINUX_ARPHRD_LOOPBACK;
303 		return (0);
304 	}
305 	if (!IFP_IS_ETH(ifp))
306 		return (ENOENT);
307 	if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0)
308 		return (0);
309 	return (ENOENT);
310 }
311