Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/compat/linux/linux_if.c
96295 views
1
/*-
2
* Copyright (c) 2015 Dmitry Chagin <[email protected]>
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
static void
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
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
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
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 *
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
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
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
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
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
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
326