Skip to content

Commit 607f110

Browse files
committed
linux: store Linux Ethernet interface number in struct ifnet
The old approach where we go through the list of interfaces and count them has bugs. One obvious bug with this dynamic translation is that once an Ethernet interface in the middle of the list goes away, all interfaces following it would change their Linux names. A bigger problem is the ifnet arrival and departure times. For example linsysfs has event handler for ifnet_arrival_event, and of course it wants to resolve the name. This accidentially works, due to a bug in if_attach() where we call if_link_ifnet() before invoking all the event handlers. Once the bug is fixed linsysfs won't be able to resolve the old way. The other side is ifnet_departure_event, where there is no bug, the eventhandlers are called after the if_unlink_ifnet(). This means old translation won't work for departure event handlers. One example is netlink. This change gives the Netlink a chance to emit a proper Linux interface departure message. However, there is another problem in Netlink, that the ifnet pointer is lost in the Netlink translation layer. Plug this with a cookie in netlink writer structure that can be set by the route layer and used by the Netlink Linux translation layer. This part of the diff seems unrelated, but it is hard to make it a separate change, as the old KPI goes away and to use the new one we need the pointer. Differential Revision: https://reviews.freebsd.org/D54077
1 parent fbf05d2 commit 607f110

File tree

11 files changed

+95
-78
lines changed

11 files changed

+95
-78
lines changed

sys/compat/linsysfs/linsysfs_net.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ linsysfs_if_addr(PFS_FILL_ARGS)
9090

9191
CURVNET_SET(TD_TO_VNET(td));
9292
NET_EPOCH_ENTER(et);
93-
ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name);
93+
ifp = ifname_linux_to_ifp(pn->pn_parent->pn_name);
9494
if (ifp != NULL && (error = linux_ifhwaddr(ifp, &lsa)) == 0)
9595
error = sbuf_printf(sb, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
9696
lsa.sa_data[0], lsa.sa_data[1], lsa.sa_data[2],
@@ -119,7 +119,7 @@ linsysfs_if_flags(PFS_FILL_ARGS)
119119

120120
CURVNET_SET(TD_TO_VNET(td));
121121
NET_EPOCH_ENTER(et);
122-
ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name);
122+
ifp = ifname_linux_to_ifp(pn->pn_parent->pn_name);
123123
if (ifp != NULL)
124124
error = sbuf_printf(sb, "0x%x\n", linux_ifflags(ifp));
125125
else
@@ -138,7 +138,7 @@ linsysfs_if_ifindex(PFS_FILL_ARGS)
138138

139139
CURVNET_SET(TD_TO_VNET(td));
140140
NET_EPOCH_ENTER(et);
141-
ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name);
141+
ifp = ifname_linux_to_ifp(pn->pn_parent->pn_name);
142142
if (ifp != NULL)
143143
error = sbuf_printf(sb, "%u\n", if_getindex(ifp));
144144
else
@@ -157,7 +157,7 @@ linsysfs_if_mtu(PFS_FILL_ARGS)
157157

158158
CURVNET_SET(TD_TO_VNET(td));
159159
NET_EPOCH_ENTER(et);
160-
ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name);
160+
ifp = ifname_linux_to_ifp( pn->pn_parent->pn_name);
161161
if (ifp != NULL)
162162
error = sbuf_printf(sb, "%u\n", if_getmtu(ifp));
163163
else
@@ -186,7 +186,7 @@ linsysfs_if_type(PFS_FILL_ARGS)
186186

187187
CURVNET_SET(TD_TO_VNET(td));
188188
NET_EPOCH_ENTER(et);
189-
ifp = ifname_linux_to_ifp(td, pn->pn_parent->pn_name);
189+
ifp = ifname_linux_to_ifp(pn->pn_parent->pn_name);
190190
if (ifp != NULL && (error = linux_ifhwaddr(ifp, &lsa)) == 0)
191191
error = sbuf_printf(sb, "%d\n", lsa.sa_family);
192192
else
@@ -207,7 +207,7 @@ linsysfs_if_visible(PFS_VIS_ARGS)
207207
visible = 0;
208208
CURVNET_SET(TD_TO_VNET(td));
209209
NET_EPOCH_ENTER(et);
210-
ifp = ifname_linux_to_ifp(td, pn->pn_name);
210+
ifp = ifname_linux_to_ifp(pn->pn_name);
211211
if (ifp != NULL) {
212212
TAILQ_FOREACH_SAFE(nq, &ifp_nodes_q, ifp_nodes_next, nq_tmp) {
213213
if (nq->ifp == ifp && nq->vnet == curvnet) {

sys/compat/linux/linux.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@ struct l_statx {
359359
ktrstruct("l_sigset_t", (s), l)
360360
#endif
361361

362+
void linux_ifnet_init(void);
363+
void linux_ifnet_uninit(void);
362364
void linux_netlink_register(void);
363365
void linux_netlink_deregister(void);
364366

sys/compat/linux/linux_common.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,15 @@ linux_common_modevent(module_t mod, int type, void *data)
5656
linux_osd_jail_register();
5757
SET_FOREACH(ldhp, linux_device_handler_set)
5858
linux_device_register_handler(*ldhp);
59+
linux_ifnet_init();
5960
linux_netlink_register();
6061
break;
6162
case MOD_UNLOAD:
6263
linux_dev_shm_destroy();
6364
linux_osd_jail_deregister();
6465
SET_FOREACH(ldhp, linux_device_handler_set)
6566
linux_device_unregister_handler(*ldhp);
67+
linux_ifnet_uninit();
6668
linux_netlink_deregister();
6769
break;
6870
default:

sys/compat/linux/linux_common.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@
2828
#ifndef _LINUX_COMMON_H_
2929
#define _LINUX_COMMON_H_
3030

31-
int ifname_bsd_to_linux_ifp(struct ifnet *, char *, size_t);
31+
int ifname_bsd_to_linux_ifp(const struct ifnet *, char *, size_t);
3232
int ifname_bsd_to_linux_idx(u_int, char *, size_t);
33-
int ifname_bsd_to_linux_name(const char *, char *, size_t);
34-
struct ifnet *ifname_linux_to_ifp(struct thread *, const char *);
33+
struct ifnet *ifname_linux_to_ifp( const char *);
3534
int ifname_linux_to_bsd(struct thread *, const char *, char *);
3635

3736
unsigned short linux_ifflags(struct ifnet *);

sys/compat/linux/linux_if.c

Lines changed: 71 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,18 @@
2424
*/
2525

2626
#include <sys/param.h>
27+
#include <sys/systm.h>
2728
#include <sys/ctype.h>
29+
#include <sys/eventhandler.h>
2830
#include <sys/jail.h>
2931
#include <sys/socket.h>
3032
#include <sys/sysctl.h>
3133
#include <net/if.h>
3234
#include <net/if_dl.h>
3335
#include <net/if_types.h>
3436
#include <net/if_var.h>
37+
#include <net/if_private.h>
38+
#include <net/vnet.h>
3539

3640
#include <compat/linux/linux.h>
3741
#include <compat/linux/linux_common.h>
@@ -44,33 +48,67 @@ SYSCTL_BOOL(_compat_linux, OID_AUTO, use_real_ifnames, CTLFLAG_RWTUN,
4448
&use_real_ifnames, 0,
4549
"Use FreeBSD interface names instead of generating ethN aliases");
4650

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)
51+
VNET_DEFINE_STATIC(struct unrhdr *, linux_eth_unr);
52+
#define V_linux_eth_unr VNET(linux_eth_unr)
5253

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)
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)
5959
{
60-
struct epoch_tracker et;
61-
struct ifnet *ifp;
62-
int ret;
60+
if (ifp->if_type == IFT_ETHER)
61+
ifp->if_linux_ethno = alloc_unr(V_linux_eth_unr);
62+
}
6363

64-
CURVNET_ASSERT_SET();
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+
}
6570

66-
ret = 0;
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);
6795
NET_EPOCH_ENTER(et);
68-
ifp = ifunit(bsdname);
69-
if (ifp != NULL)
70-
ret = ifname_bsd_to_linux_ifp(ifp, lxname, len);
96+
for (ifp = if_iter_start(&it); ifp != NULL; ifp = if_iter_next(&it))
97+
linux_ifnet_arrival(NULL, ifp);
7198
NET_EPOCH_EXIT(et);
72-
return (ret);
7399
}
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
74112

75113
/*
76114
* Translate a FreeBSD interface name to a Linux interface name
@@ -99,50 +137,23 @@ ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len)
99137
* and return the number of bytes copied to lxname, 0 if interface
100138
* not found, -1 on error.
101139
*/
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-
121140
int
122-
ifname_bsd_to_linux_ifp(struct ifnet *ifp, char *lxname, size_t len)
141+
ifname_bsd_to_linux_ifp(const struct ifnet *ifp, char *lxname, size_t len)
123142
{
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-
133143
/*
134144
* Linux loopback interface name is lo (not lo0),
135145
* we translate lo to lo0, loX to loX.
136146
*/
137-
if (IFP_IS_LOOP(ifp) && strncmp(if_name(ifp), "lo0", IFNAMSIZ) == 0)
147+
if (ifp->if_type == IFT_LOOP &&
148+
strncmp(ifp->if_xname, "lo0", IFNAMSIZ) == 0)
138149
return (strlcpy(lxname, "lo", len));
139150

140151
/* Short-circuit non ethernet interfaces. */
141-
if (!IFP_IS_ETH(ifp) || use_real_ifnames)
142-
return (strlcpy(lxname, if_name(ifp), len));
152+
if (ifp->if_type != IFT_ETHER || use_real_ifnames)
153+
return (strlcpy(lxname, ifp->if_xname, len));
143154

144155
/* Determine the (relative) unit number for ethernet interfaces. */
145-
return (if_foreach(ifname_bsd_to_linux_ifp_cb, &arg));
156+
return (snprintf(lxname, len, "eth%d", ifp->if_linux_ethno));
146157
}
147158

148159
/*
@@ -154,7 +165,6 @@ ifname_bsd_to_linux_ifp(struct ifnet *ifp, char *lxname, size_t len)
154165
struct ifname_linux_to_ifp_cb_s {
155166
bool is_lo;
156167
bool is_eth;
157-
int ethno;
158168
int unit;
159169
const char *lxname;
160170
if_t ifp;
@@ -174,12 +184,11 @@ ifname_linux_to_ifp_cb(if_t ifp, void *arg)
174184
*/
175185
if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0)
176186
goto out;
177-
if (cbs->is_eth && IFP_IS_ETH(ifp) && cbs->unit == cbs->ethno)
187+
if (cbs->is_eth && ifp->if_type == IFT_ETHER &&
188+
ifp->if_linux_ethno == cbs->unit)
178189
goto out;
179-
if (cbs->is_lo && IFP_IS_LOOP(ifp))
190+
if (cbs->is_lo && ifp->if_type == IFT_LOOP)
180191
goto out;
181-
if (IFP_IS_ETH(ifp))
182-
cbs->ethno++;
183192
return (0);
184193

185194
out:
@@ -188,12 +197,10 @@ ifname_linux_to_ifp_cb(if_t ifp, void *arg)
188197
}
189198

190199
struct ifnet *
191-
ifname_linux_to_ifp(struct thread *td, const char *lxname)
200+
ifname_linux_to_ifp(const char *lxname)
192201
{
193202
struct ifname_linux_to_ifp_cb_s arg = {
194-
.ethno = 0,
195203
.lxname = lxname,
196-
.ifp = NULL,
197204
};
198205
int len;
199206
char *ep;
@@ -228,7 +235,7 @@ ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname)
228235

229236
CURVNET_SET(TD_TO_VNET(td));
230237
NET_EPOCH_ENTER(et);
231-
ifp = ifname_linux_to_ifp(td, lxname);
238+
ifp = ifname_linux_to_ifp(lxname);
232239
if (ifp != NULL && bsdname != NULL)
233240
strlcpy(bsdname, if_name(ifp), IFNAMSIZ);
234241
NET_EPOCH_EXIT(et);
@@ -297,12 +304,12 @@ linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa)
297304

298305
NET_EPOCH_ASSERT();
299306

300-
if (IFP_IS_LOOP(ifp)) {
307+
if (ifp->if_type == IFT_LOOP) {
301308
bzero(lsa, sizeof(*lsa));
302309
lsa->sa_family = LINUX_ARPHRD_LOOPBACK;
303310
return (0);
304311
}
305-
if (!IFP_IS_ETH(ifp))
312+
if (ifp->if_type != IFT_ETHER)
306313
return (ENOENT);
307314
if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0)
308315
return (0);

sys/compat/linux/linux_netlink.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,9 @@ nlmsg_translate_ifname_nla(struct nlattr *nla, struct nl_writer *nw)
249249
{
250250
char ifname[LINUX_IFNAMSIZ];
251251

252-
if (ifname_bsd_to_linux_name((char *)(nla + 1), ifname,
253-
sizeof(ifname)) <= 0)
252+
if (nw->ifp == NULL)
254253
return (false);
254+
(void)ifname_bsd_to_linux_ifp(nw->ifp, ifname, sizeof(ifname));
255255
return (nlattr_add_string(nw, IFLA_IFNAME, ifname));
256256
}
257257

@@ -564,7 +564,7 @@ nlmsg_to_linux(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_writer *nw)
564564
}
565565

566566
static struct nl_buf *
567-
nlmsgs_to_linux(struct nl_buf *orig, struct nlpcb *nlp)
567+
nlmsgs_to_linux(struct nl_buf *orig, struct nlpcb *nlp, const struct ifnet *ifp)
568568
{
569569
struct nl_writer nw;
570570
u_int offset, msglen;
@@ -573,6 +573,7 @@ nlmsgs_to_linux(struct nl_buf *orig, struct nlpcb *nlp)
573573
orig->datalen + SCRATCH_BUFFER_SIZE, nlp, false)))
574574
return (NULL);
575575

576+
nw.ifp = ifp;
576577
/* Assume correct headers. Buffer IS mutable */
577578
for (offset = 0;
578579
offset + sizeof(struct nlmsghdr) <= orig->datalen;

sys/net/if_private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct ifnet {
6565
void *if_linkmib; /* link-type-specific MIB data */
6666
size_t if_linkmiblen; /* length of above data */
6767
u_int if_refcount; /* reference count */
68+
u_int if_linux_ethno; /* linux name id for IFT_ETHER */
6869

6970
/* These fields are shared with struct if_data. */
7071
uint8_t if_type; /* ethernet, tokenring, etc */

sys/netlink/netlink_io.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ nl_send(struct nl_writer *nw, struct nlpcb *nlp)
217217
}
218218

219219
if (nlp->nl_linux && linux_netlink_p != NULL) {
220-
nb = linux_netlink_p->msgs_to_linux(nw->buf, nlp);
220+
nb = linux_netlink_p->msgs_to_linux(nw->buf, nlp, nw->ifp);
221221
nl_buf_free(nw->buf);
222222
nw->buf = NULL;
223223
if (nb == NULL)

sys/netlink/netlink_linux.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ struct nlpcb;
3737
struct nl_pstate;
3838
struct nl_writer;
3939

40-
typedef struct nl_buf * msgs_to_linux_cb_t(struct nl_buf *, struct nlpcb *);
40+
typedef struct nl_buf * msgs_to_linux_cb_t(struct nl_buf *, struct nlpcb *,
41+
const struct ifnet *);
4142
typedef int msg_from_linux_cb_t(int netlink_family, struct nlmsghdr **hdr,
4243
struct nl_pstate *npt);
4344

sys/netlink/netlink_message_writer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
struct nl_buf;
4141
struct nl_writer;
42+
struct ifnet;
4243
typedef bool nl_writer_cb(struct nl_writer *nw);
4344

4445
struct nl_writer {
@@ -53,6 +54,7 @@ struct nl_writer {
5354
int priv;
5455
} group;
5556
};
57+
const struct ifnet *ifp; /* Used by Linux translation only */
5658
u_int num_messages; /* Number of messages in the buffer */
5759
int malloc_flag; /* M_WAITOK or M_NOWAIT */
5860
bool ignore_limit; /* If true, ignores RCVBUF limit */

0 commit comments

Comments
 (0)