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