The branch main has been updated by dchagin:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=6c5786fd37cb588f577c7fa63c56fc2f59c38786

commit 6c5786fd37cb588f577c7fa63c56fc2f59c38786
Author:     Dmitry Chagin <[email protected]>
AuthorDate: 2023-03-04 09:11:38 +0000
Commit:     Dmitry Chagin <[email protected]>
CommitDate: 2023-03-04 09:11:38 +0000

    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
---
 sys/compat/linprocfs/linprocfs.c |  72 ++++++++++--------
 sys/compat/linux/linux.c         | 160 +++++++++++++++++++++++++--------------
 sys/compat/linux/linux.h         |   4 +-
 sys/compat/linux/linux_ioctl.c   | 124 +++++++++++++++++-------------
 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
@@ -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();
 
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)

Reply via email to