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