aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Chagin <dchagin@FreeBSD.org>2023-03-04 09:11:38 +0000
committerDmitry Chagin <dchagin@FreeBSD.org>2023-03-04 09:11:38 +0000
commit6c5786fd37cb588f577c7fa63c56fc2f59c38786 (patch)
treec114c9e97ab499836971c1ed7eb5a2a222fb6df0
parent290f7f4a0921d1606aae8568a04f12025fbf834c (diff)
downloadsrc-6c5786fd37cb588f577c7fa63c56fc2f59c38786.tar.gz
src-6c5786fd37cb588f577c7fa63c56fc2f59c38786.zip
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
-rw-r--r--sys/compat/linprocfs/linprocfs.c72
-rw-r--r--sys/compat/linux/linux.c160
-rw-r--r--sys/compat/linux/linux.h4
-rw-r--r--sys/compat/linux/linux_ioctl.c124
4 files changed, 217 insertions, 143 deletions
diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c
index d57c546eb637..a150019ae5ef 100644
--- a/sys/compat/linprocfs/linprocfs.c
+++ b/sys/compat/linprocfs/linprocfs.c
@@ -1479,11 +1479,48 @@ 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();
diff --git a/sys/compat/linux/linux.c b/sys/compat/linux/linux.c
index 2570a5fcc89c..a7844f4bbbcf 100644
--- a/sys/compat/linux/linux.c
+++ b/sys/compat/linux/linux.c
@@ -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));
-
- /* 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);
+ return (strlcpy(lxname, if_name(ifp), len));
+
+ /* 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);
}
diff --git a/sys/compat/linux/linux.h b/sys/compat/linux/linux.h
index 055d8e3b9cf6..e133c35010cf 100644
--- a/sys/compat/linux/linux.h
+++ b/sys/compat/linux/linux.h
@@ -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;
diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c
index bfab28f841a0..8900102bbbc7 100644
--- a/sys/compat/linux/linux_ioctl.c
+++ b/sys/compat/linux/linux_ioctl.c
@@ -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)