linux(4): Migrate to IfAPI

Migrate linux and linprocfs to use the IfAPI interfaces instead of
direct ifnet accesses.
The code initially writed by jhibbits@, and adapted by me to 3ab3c9c2.

Reviewed by:		jhibbits
Differential Revision:  https://reviews.freebsd.org/D38735
This commit is contained in:
Dmitry Chagin 2023-03-04 12:11:38 +03:00
parent 290f7f4a09
commit 6c5786fd37
4 changed files with 216 additions and 142 deletions

View File

@ -1478,12 +1478,49 @@ linprocfs_doprocmem(PFS_FILL_ARGS)
/*
* Filler function for proc/net/dev
*/
static int
linprocfs_donetdev_cb(if_t ifp, void *arg)
{
char ifname[LINUX_IFNAMSIZ];
struct sbuf *sb = arg;
if (ifname_bsd_to_linux_ifp(ifp, ifname, sizeof(ifname)) <= 0)
return (ENODEV);
sbuf_printf(sb, "%6.6s: ", ifname);
sbuf_printf(sb, "%7ju %7ju %4ju %4ju %4lu %5lu %10lu %9ju ",
(uintmax_t)if_getcounter(ifp, IFCOUNTER_IBYTES),
(uintmax_t)if_getcounter(ifp, IFCOUNTER_IPACKETS),
(uintmax_t)if_getcounter(ifp, IFCOUNTER_IERRORS),
(uintmax_t)if_getcounter(ifp, IFCOUNTER_IQDROPS),
/* rx_missed_errors */
0UL, /* rx_fifo_errors */
0UL, /* rx_length_errors +
* rx_over_errors +
* rx_crc_errors +
* rx_frame_errors */
0UL, /* rx_compressed */
(uintmax_t)if_getcounter(ifp, IFCOUNTER_IMCASTS));
/* XXX-BZ rx only? */
sbuf_printf(sb, "%8ju %7ju %4ju %4ju %4lu %5ju %7lu %10lu\n",
(uintmax_t)if_getcounter(ifp, IFCOUNTER_OBYTES),
(uintmax_t)if_getcounter(ifp, IFCOUNTER_OPACKETS),
(uintmax_t)if_getcounter(ifp, IFCOUNTER_OERRORS),
(uintmax_t)if_getcounter(ifp, IFCOUNTER_OQDROPS),
0UL, /* tx_fifo_errors */
(uintmax_t)if_getcounter(ifp, IFCOUNTER_COLLISIONS),
0UL, /* tx_carrier_errors +
* tx_aborted_errors +
* tx_window_errors +
* tx_heartbeat_errors*/
0UL); /* tx_compressed */
return (0);
}
static int
linprocfs_donetdev(PFS_FILL_ARGS)
{
struct epoch_tracker et;
char ifname[16]; /* XXX LINUX_IFNAMSIZ */
struct ifnet *ifp;
sbuf_printf(sb, "%6s|%58s|%s\n"
"%6s|%58s|%58s\n",
@ -1494,36 +1531,7 @@ linprocfs_donetdev(PFS_FILL_ARGS)
CURVNET_SET(TD_TO_VNET(curthread));
NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
ifname_bsd_to_linux_ifp(ifp, ifname, sizeof(ifname));
sbuf_printf(sb, "%6.6s: ", ifname);
sbuf_printf(sb, "%7ju %7ju %4ju %4ju %4lu %5lu %10lu %9ju ",
(uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IBYTES),
(uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IPACKETS),
(uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IERRORS),
(uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IQDROPS),
/* rx_missed_errors */
0UL, /* rx_fifo_errors */
0UL, /* rx_length_errors +
* rx_over_errors +
* rx_crc_errors +
* rx_frame_errors */
0UL, /* rx_compressed */
(uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IMCASTS));
/* XXX-BZ rx only? */
sbuf_printf(sb, "%8ju %7ju %4ju %4ju %4lu %5ju %7lu %10lu\n",
(uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OBYTES),
(uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OPACKETS),
(uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OERRORS),
(uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OQDROPS),
0UL, /* tx_fifo_errors */
(uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_COLLISIONS),
0UL, /* tx_carrier_errors +
* tx_aborted_errors +
* tx_window_errors +
* tx_heartbeat_errors*/
0UL); /* tx_compressed */
}
if_foreach(linprocfs_donetdev_cb, sb);
NET_EPOCH_EXIT(et);
CURVNET_RESTORE();

View File

@ -287,13 +287,37 @@ ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len)
/*
* Translate a FreeBSD interface name to a Linux interface name,
* and return the number of bytes copied to lxname.
* and return the number of bytes copied to lxname, 0 if interface
* not found, -1 on error.
*/
struct ifname_bsd_to_linux_ifp_cb_s {
struct ifnet *ifp;
int ethno;
char *lxname;
size_t len;
};
static int
ifname_bsd_to_linux_ifp_cb(if_t ifp, void *arg)
{
struct ifname_bsd_to_linux_ifp_cb_s *cbs = arg;
if (ifp == cbs->ifp)
return (snprintf(cbs->lxname, cbs->len, "eth%d", cbs->ethno));
if (IFP_IS_ETH(ifp))
cbs->ethno++;
return (0);
}
int
ifname_bsd_to_linux_ifp(struct ifnet *ifp, char *lxname, size_t len)
{
struct ifnet *ifscan;
int unit;
struct ifname_bsd_to_linux_ifp_cb_s arg = {
.ifp = ifp,
.ethno = 0,
.lxname = lxname,
.len = len,
};
NET_EPOCH_ASSERT();
@ -306,17 +330,10 @@ ifname_bsd_to_linux_ifp(struct ifnet *ifp, char *lxname, size_t len)
/* Short-circuit non ethernet interfaces. */
if (!IFP_IS_ETH(ifp) || linux_use_real_ifname(ifp))
return (strlcpy(lxname, ifp->if_xname, len));
return (strlcpy(lxname, if_name(ifp), len));
/* Determine the (relative) unit number for ethernet interfaces. */
unit = 0;
CK_STAILQ_FOREACH(ifscan, &V_ifnet, if_link) {
if (ifscan == ifp)
return (snprintf(lxname, len, "eth%d", unit));
if (IFP_IS_ETH(ifscan))
unit++;
}
return (0);
/* Determine the (relative) unit number for ethernet interfaces. */
return (if_foreach(ifname_bsd_to_linux_ifp_cb, &arg));
}
/*
@ -325,14 +342,53 @@ ifname_bsd_to_linux_ifp(struct ifnet *ifp, char *lxname, size_t len)
* bsdname and lxname need to be least IFNAMSIZ bytes long, but
* can point to the same buffer.
*/
struct ifname_linux_to_bsd_cb_s {
bool is_lo;
bool is_eth;
int ethno;
int unit;
const char *lxname;
if_t ifp;
};
static int
ifname_linux_to_bsd_cb(if_t ifp, void *arg)
{
struct ifname_linux_to_bsd_cb_s *cbs = arg;
NET_EPOCH_ASSERT();
/*
* Allow Linux programs to use FreeBSD names. Don't presume
* we never have an interface named "eth", so don't make
* the test optional based on is_eth.
*/
if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0)
goto out;
if (cbs->is_eth && IFP_IS_ETH(ifp) && cbs->unit == cbs->ethno)
goto out;
if (cbs->is_lo && IFP_IS_LOOP(ifp))
goto out;
if (IFP_IS_ETH(ifp))
cbs->ethno++;
return (0);
out:
cbs->ifp = ifp;
return (1);
}
struct ifnet *
ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname)
{
struct ifnet *ifp;
int len, unit;
struct ifname_linux_to_bsd_cb_s arg = {
.ethno = 0,
.lxname = lxname,
.ifp = NULL,
};
struct epoch_tracker et;
int len, ret;
char *ep;
int index;
bool is_eth, is_lo;
for (len = 0; len < LINUX_IFNAMSIZ; ++len)
if (!isalpha(lxname[len]) || lxname[len] == '\0')
@ -343,34 +399,21 @@ ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname)
* Linux loopback interface name is lo (not lo0),
* we translate lo to lo0, loX to loX.
*/
is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0);
unit = (int)strtoul(lxname + len, &ep, 10);
arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0);
arg.unit = (int)strtoul(lxname + len, &ep, 10);
if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) &&
is_lo == 0)
arg.is_lo == 0)
return (NULL);
index = 0;
is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0);
arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0);
CURVNET_SET(TD_TO_VNET(td));
IFNET_RLOCK();
CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
/*
* Allow Linux programs to use FreeBSD names. Don't presume
* we never have an interface named "eth", so don't make
* the test optional based on is_eth.
*/
if (strncmp(ifp->if_xname, lxname, LINUX_IFNAMSIZ) == 0)
break;
if (is_eth && IFP_IS_ETH(ifp) && unit == index++)
break;
if (is_lo && IFP_IS_LOOP(ifp))
break;
}
IFNET_RUNLOCK();
NET_EPOCH_ENTER(et);
ret = if_foreach(ifname_linux_to_bsd_cb, &arg);
NET_EPOCH_EXIT(et);
CURVNET_RESTORE();
if (ifp != NULL && bsdname != NULL)
strlcpy(bsdname, ifp->if_xname, IFNAMSIZ);
return (ifp);
if (ret > 0 && arg.ifp != NULL && bsdname != NULL)
strlcpy(bsdname, if_name(arg.ifp), IFNAMSIZ);
return (arg.ifp);
}
void
@ -378,7 +421,7 @@ linux_ifflags(struct ifnet *ifp, short *flags)
{
unsigned short fl;
fl = (ifp->if_flags | ifp->if_drv_flags) & 0xffff;
fl = (if_getflags(ifp) | if_getdrvflags(ifp)) & 0xffff;
*flags = 0;
if (fl & IFF_UP)
*flags |= LINUX_IFF_UP;
@ -402,32 +445,35 @@ linux_ifflags(struct ifnet *ifp, short *flags)
*flags |= LINUX_IFF_MULTICAST;
}
static u_int
linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count)
{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
struct l_sockaddr *lsa = arg;
if (count > 0)
return (0);
if (sdl->sdl_type != IFT_ETHER)
return (0);
bzero(lsa, sizeof(*lsa));
lsa->sa_family = LINUX_ARPHRD_ETHER;
bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN);
return (1);
}
int
linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa)
{
struct ifaddr *ifa;
struct sockaddr_dl *sdl;
if (IFP_IS_LOOP(ifp)) {
bzero(lsa, sizeof(*lsa));
lsa->sa_family = LINUX_ARPHRD_LOOPBACK;
return (0);
}
if (!IFP_IS_ETH(ifp))
return (ENOENT);
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
sdl = (struct sockaddr_dl*)ifa->ifa_addr;
if (sdl != NULL && (sdl->sdl_family == AF_LINK) &&
(sdl->sdl_type == IFT_ETHER)) {
bzero(lsa, sizeof(*lsa));
lsa->sa_family = LINUX_ARPHRD_ETHER;
bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN);
return (0);
}
}
if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0)
return (0);
return (ENOENT);
}

View File

@ -287,8 +287,8 @@ struct l_statx {
/*
* Criteria for interface name translation
*/
#define IFP_IS_ETH(ifp) ((ifp)->if_type == IFT_ETHER)
#define IFP_IS_LOOP(ifp) ((ifp)->if_type == IFT_LOOP)
#define IFP_IS_ETH(ifp) (if_gettype(ifp) == IFT_ETHER)
#define IFP_IS_LOOP(ifp) (if_gettype(ifp) == IFT_LOOP)
struct ifnet;

View File

@ -2108,40 +2108,91 @@ linux_ioctl_ifname(struct thread *td, struct l_ifreq *uifr)
/*
* Implement the SIOCGIFCONF ioctl
*/
static u_int
linux_ifconf_ifaddr_cb(void *arg, struct ifaddr *ifa, u_int count)
{
#ifdef COMPAT_LINUX32
struct l_ifconf *ifc;
#else
struct ifconf *ifc;
#endif
ifc = arg;
ifc->ifc_len += sizeof(struct l_ifreq);
return (1);
}
static int
linux_ifconf_ifnet_cb(if_t ifp, void *arg)
{
if_foreach_addr_type(ifp, AF_INET, linux_ifconf_ifaddr_cb, arg);
return (0);
}
struct linux_ifconfig_ifaddr_cb2_s {
struct l_ifreq ifr;
struct sbuf *sb;
size_t max_len;
size_t valid_len;
};
static u_int
linux_ifconf_ifaddr_cb2(void *arg, struct ifaddr *ifa, u_int len)
{
struct linux_ifconfig_ifaddr_cb2_s *cbs = arg;
struct sockaddr *sa = ifa->ifa_addr;
cbs->ifr.ifr_addr.sa_family = LINUX_AF_INET;
memcpy(cbs->ifr.ifr_addr.sa_data, sa->sa_data,
sizeof(cbs->ifr.ifr_addr.sa_data));
sbuf_bcat(cbs->sb, &cbs->ifr, sizeof(cbs->ifr));
cbs->max_len += sizeof(cbs->ifr);
if (sbuf_error(cbs->sb) == 0)
cbs->valid_len = sbuf_len(cbs->sb);
return (1);
}
static int
linux_ifconf_ifnet_cb2(if_t ifp, void *arg)
{
struct linux_ifconfig_ifaddr_cb2_s *cbs = arg;
bzero(&cbs->ifr, sizeof(cbs->ifr));
ifname_bsd_to_linux_ifp(ifp, cbs->ifr.ifr_name,
sizeof(cbs->ifr.ifr_name));
/* Walk the address list */
if_foreach_addr_type(ifp, AF_INET, linux_ifconf_ifaddr_cb2, cbs);
return (0);
}
static int
linux_ifconf(struct thread *td, struct ifconf *uifc)
{
struct linux_ifconfig_ifaddr_cb2_s cbs;
struct epoch_tracker et;
#ifdef COMPAT_LINUX32
struct l_ifconf ifc;
#else
struct ifconf ifc;
#endif
struct l_ifreq ifr;
struct ifnet *ifp;
struct ifaddr *ifa;
struct sbuf *sb;
int error, full = 0, valid_len, max_len;
int error, full;
error = copyin(uifc, &ifc, sizeof(ifc));
if (error != 0)
return (error);
max_len = maxphys - 1;
full = 0;
cbs.max_len = maxphys - 1;
CURVNET_SET(TD_TO_VNET(td));
/* handle the 'request buffer size' case */
if ((l_uintptr_t)ifc.ifc_buf == PTROUT(NULL)) {
ifc.ifc_len = 0;
NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
struct sockaddr *sa = ifa->ifa_addr;
if (sa->sa_family == AF_INET)
ifc.ifc_len += sizeof(ifr);
}
}
if_foreach(linux_ifconf_ifnet_cb, &ifc);
NET_EPOCH_EXIT(et);
error = copyout(&ifc, uifc, sizeof(ifc));
CURVNET_RESTORE();
@ -2154,56 +2205,25 @@ linux_ifconf(struct thread *td, struct ifconf *uifc)
}
again:
if (ifc.ifc_len <= max_len) {
max_len = ifc.ifc_len;
if (ifc.ifc_len <= cbs.max_len) {
cbs.max_len = ifc.ifc_len;
full = 1;
}
sb = sbuf_new(NULL, NULL, max_len + 1, SBUF_FIXEDLEN);
max_len = 0;
valid_len = 0;
cbs.sb = sb = sbuf_new(NULL, NULL, cbs.max_len + 1, SBUF_FIXEDLEN);
cbs.max_len = 0;
cbs.valid_len = 0;
/* Return all AF_INET addresses of all interfaces */
NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
int addrs = 0;
bzero(&ifr, sizeof(ifr));
ifname_bsd_to_linux_ifp(ifp, ifr.ifr_name,
sizeof(ifr.ifr_name));
/* Walk the address list */
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
struct sockaddr *sa = ifa->ifa_addr;
if (sa->sa_family == AF_INET) {
ifr.ifr_addr.sa_family = LINUX_AF_INET;
memcpy(ifr.ifr_addr.sa_data, sa->sa_data,
sizeof(ifr.ifr_addr.sa_data));
sbuf_bcat(sb, &ifr, sizeof(ifr));
max_len += sizeof(ifr);
addrs++;
}
if (sbuf_error(sb) == 0)
valid_len = sbuf_len(sb);
}
if (addrs == 0) {
bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
sbuf_bcat(sb, &ifr, sizeof(ifr));
max_len += sizeof(ifr);
if (sbuf_error(sb) == 0)
valid_len = sbuf_len(sb);
}
}
if_foreach(linux_ifconf_ifnet_cb2, &cbs);
NET_EPOCH_EXIT(et);
if (valid_len != max_len && !full) {
if (cbs.valid_len != cbs.max_len && !full) {
sbuf_delete(sb);
goto again;
}
ifc.ifc_len = valid_len;
ifc.ifc_len = cbs.valid_len;
sbuf_finish(sb);
error = copyout(sbuf_data(sb), PTRIN(ifc.ifc_buf), ifc.ifc_len);
if (error == 0)