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
linux_ifnet_arrival(void * arg __unused,struct ifnet * ifp)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
linux_ifnet_departure(void * arg __unused,struct ifnet * ifp)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
linux_ifnet_init(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
linux_ifnet_uninit(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
linux_ifnet_vnet_init(void * arg __unused)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 static void
linux_ifnet_vnet_uninit(void * arg __unused)105 linux_ifnet_vnet_uninit(void *arg __unused)
106 {
107 /*
108 * All cloned interfaces are already gone at this point, as well
109 * as interfaces that were if_vmove'd into this vnet. However,
110 * if a jail has created IFT_ETHER interfaces in self, or has had
111 * physical Ethernet drivers attached in self, than we may have
112 * allocated entries in the unr(9), so clear it to avoid KASSERT.
113 */
114 clear_unrhdr(V_linux_eth_unr);
115 delete_unrhdr(V_linux_eth_unr);
116 }
117 VNET_SYSUNINIT(linux_ifnet_vnet_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY,
118 linux_ifnet_vnet_uninit, NULL);
119
120 /*
121 * Translate a FreeBSD interface name to a Linux interface name
122 * by interface index, and return the number of bytes copied to lxname.
123 */
124 int
ifname_bsd_to_linux_idx(u_int idx,char * lxname,size_t len)125 ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len)
126 {
127 struct epoch_tracker et;
128 struct ifnet *ifp;
129 int ret;
130
131 ret = 0;
132 CURVNET_SET(TD_TO_VNET(curthread));
133 NET_EPOCH_ENTER(et);
134 ifp = ifnet_byindex(idx);
135 if (ifp != NULL)
136 ret = ifname_bsd_to_linux_ifp(ifp, lxname, len);
137 NET_EPOCH_EXIT(et);
138 CURVNET_RESTORE();
139 return (ret);
140 }
141
142 /*
143 * Translate a FreeBSD interface name to a Linux interface name,
144 * and return the number of bytes copied to lxname, 0 if interface
145 * not found, -1 on error.
146 */
147 int
ifname_bsd_to_linux_ifp(const struct ifnet * ifp,char * lxname,size_t len)148 ifname_bsd_to_linux_ifp(const struct ifnet *ifp, char *lxname, size_t len)
149 {
150 /*
151 * Linux loopback interface name is lo (not lo0),
152 * we translate lo to lo0, loX to loX.
153 */
154 if (ifp->if_type == IFT_LOOP &&
155 strncmp(ifp->if_xname, "lo0", IFNAMSIZ) == 0)
156 return (strlcpy(lxname, "lo", len));
157
158 /* Short-circuit non ethernet interfaces. */
159 if (ifp->if_type != IFT_ETHER || use_real_ifnames)
160 return (strlcpy(lxname, ifp->if_xname, len));
161
162 /* Determine the (relative) unit number for ethernet interfaces. */
163 return (snprintf(lxname, len, "eth%d", ifp->if_linux_ethno));
164 }
165
166 /*
167 * Translate a Linux interface name to a FreeBSD interface name,
168 * and return the associated ifnet structure
169 * bsdname and lxname need to be least IFNAMSIZ bytes long, but
170 * can point to the same buffer.
171 */
172 struct ifname_linux_to_ifp_cb_s {
173 bool is_lo;
174 bool is_eth;
175 int unit;
176 const char *lxname;
177 if_t ifp;
178 };
179
180 static int
ifname_linux_to_ifp_cb(if_t ifp,void * arg)181 ifname_linux_to_ifp_cb(if_t ifp, void *arg)
182 {
183 struct ifname_linux_to_ifp_cb_s *cbs = arg;
184
185 NET_EPOCH_ASSERT();
186
187 /*
188 * Allow Linux programs to use FreeBSD names. Don't presume
189 * we never have an interface named "eth", so don't make
190 * the test optional based on is_eth.
191 */
192 if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0)
193 goto out;
194 if (cbs->is_eth && ifp->if_type == IFT_ETHER &&
195 ifp->if_linux_ethno == cbs->unit)
196 goto out;
197 if (cbs->is_lo && ifp->if_type == IFT_LOOP)
198 goto out;
199 return (0);
200
201 out:
202 cbs->ifp = ifp;
203 return (1);
204 }
205
206 struct ifnet *
ifname_linux_to_ifp(const char * lxname)207 ifname_linux_to_ifp(const char *lxname)
208 {
209 struct ifname_linux_to_ifp_cb_s arg = {
210 .lxname = lxname,
211 };
212 int len;
213 char *ep;
214
215 NET_EPOCH_ASSERT();
216
217 for (len = 0; len < LINUX_IFNAMSIZ; ++len)
218 if (!isalpha(lxname[len]) || lxname[len] == '\0')
219 break;
220 if (len == 0 || len == LINUX_IFNAMSIZ)
221 return (NULL);
222 /*
223 * Linux loopback interface name is lo (not lo0),
224 * we translate lo to lo0, loX to loX.
225 */
226 arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0);
227 arg.unit = (int)strtoul(lxname + len, &ep, 10);
228 if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) &&
229 arg.is_lo == 0)
230 return (NULL);
231 arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0);
232
233 if_foreach(ifname_linux_to_ifp_cb, &arg);
234 return (arg.ifp);
235 }
236
237 int
ifname_linux_to_bsd(struct thread * td,const char * lxname,char * bsdname)238 ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname)
239 {
240 struct epoch_tracker et;
241 struct ifnet *ifp;
242
243 CURVNET_SET(TD_TO_VNET(td));
244 NET_EPOCH_ENTER(et);
245 ifp = ifname_linux_to_ifp(lxname);
246 if (ifp != NULL && bsdname != NULL)
247 strlcpy(bsdname, if_name(ifp), IFNAMSIZ);
248 NET_EPOCH_EXIT(et);
249 CURVNET_RESTORE();
250 return (ifp != NULL ? 0 : EINVAL);
251 }
252
253 unsigned short
linux_ifflags(struct ifnet * ifp)254 linux_ifflags(struct ifnet *ifp)
255 {
256 unsigned short flags;
257
258 NET_EPOCH_ASSERT();
259
260 flags = if_getflags(ifp) | if_getdrvflags(ifp);
261 return (bsd_to_linux_ifflags(flags));
262 }
263
264 unsigned short
bsd_to_linux_ifflags(int fl)265 bsd_to_linux_ifflags(int fl)
266 {
267 unsigned short flags = 0;
268
269 if (fl & IFF_UP)
270 flags |= LINUX_IFF_UP;
271 if (fl & IFF_BROADCAST)
272 flags |= LINUX_IFF_BROADCAST;
273 if (fl & IFF_DEBUG)
274 flags |= LINUX_IFF_DEBUG;
275 if (fl & IFF_LOOPBACK)
276 flags |= LINUX_IFF_LOOPBACK;
277 if (fl & IFF_POINTOPOINT)
278 flags |= LINUX_IFF_POINTOPOINT;
279 if (fl & IFF_DRV_RUNNING)
280 flags |= LINUX_IFF_RUNNING;
281 if (fl & IFF_NOARP)
282 flags |= LINUX_IFF_NOARP;
283 if (fl & IFF_PROMISC)
284 flags |= LINUX_IFF_PROMISC;
285 if (fl & IFF_ALLMULTI)
286 flags |= LINUX_IFF_ALLMULTI;
287 if (fl & IFF_MULTICAST)
288 flags |= LINUX_IFF_MULTICAST;
289 return (flags);
290 }
291
292 static u_int
linux_ifhwaddr_cb(void * arg,struct ifaddr * ifa,u_int count)293 linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count)
294 {
295 struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
296 struct l_sockaddr *lsa = arg;
297
298 if (count > 0)
299 return (0);
300 if (sdl->sdl_type != IFT_ETHER)
301 return (0);
302 bzero(lsa, sizeof(*lsa));
303 lsa->sa_family = LINUX_ARPHRD_ETHER;
304 bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN);
305 return (1);
306 }
307
308 int
linux_ifhwaddr(struct ifnet * ifp,struct l_sockaddr * lsa)309 linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa)
310 {
311
312 NET_EPOCH_ASSERT();
313
314 if (ifp->if_type == IFT_LOOP) {
315 bzero(lsa, sizeof(*lsa));
316 lsa->sa_family = LINUX_ARPHRD_LOOPBACK;
317 return (0);
318 }
319 if (ifp->if_type != IFT_ETHER)
320 return (ENOENT);
321 if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0)
322 return (0);
323 return (ENOENT);
324 }
325