The branch main has been updated by kbowling:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=2d6114f6d26bf7dfa5ad94e1db9b09ee7108dc7a

commit 2d6114f6d26bf7dfa5ad94e1db9b09ee7108dc7a
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-08-09 02:03:20 +0000
Commit:     Kevin Bowling <[email protected]>
CommitDate: 2026-08-09 06:46:41 +0000

    libifconfig: Add an SR-IOV VF status query
    
    Provide a public helper which retrieves, unpacks, and validates the
    versioned VF status nvlist.  Validate the required VF indices and the
    shape and version of driver-specific extension namespaces while allowing
    unknown optional fields.
    
    The ioctl argument is not copied back when the command returns EFBIG.
    Start with a practical buffer and grow it geometrically rather than
    relying on the required length being observable.
    
    Use the helper in ifconfig so other consumers share the same transport
    and validation behavior.
---
 lib/libifconfig/Makefile      |   2 +-
 lib/libifconfig/Symbol.map    |   4 ++
 lib/libifconfig/libifconfig.c | 129 ++++++++++++++++++++++++++++++++++++++++++
 lib/libifconfig/libifconfig.h |  11 ++++
 sbin/ifconfig/ifvfstatus.c    |  60 +++-----------------
 share/mk/src.libnames.mk      |   4 +-
 6 files changed, 154 insertions(+), 56 deletions(-)

diff --git a/lib/libifconfig/Makefile b/lib/libifconfig/Makefile
index 02629eb88f25..48fc39d921bc 100644
--- a/lib/libifconfig/Makefile
+++ b/lib/libifconfig/Makefile
@@ -1,7 +1,7 @@
 LIB=           ifconfig
 INTERNALLIB=   true
 
-LIBADD=                m
+LIBADD=                m nv
 
 SHLIBDIR?=     /lib
 SHLIB_MAJOR=   2
diff --git a/lib/libifconfig/Symbol.map b/lib/libifconfig/Symbol.map
index 2e11ff963909..a793fcdefbda 100644
--- a/lib/libifconfig/Symbol.map
+++ b/lib/libifconfig/Symbol.map
@@ -84,3 +84,7 @@ FBSD_1.6 {
        ifconfig_sfp_rev_description;
        ifconfig_sfp_rev_symbol;
 };
+
+FBSD_1.9 {
+       ifconfig_get_vf_status;
+};
diff --git a/lib/libifconfig/libifconfig.c b/lib/libifconfig/libifconfig.c
index b450c15180db..3818cb689dbf 100644
--- a/lib/libifconfig/libifconfig.c
+++ b/lib/libifconfig/libifconfig.c
@@ -529,6 +529,135 @@ ifconfig_get_ifstatus(ifconfig_handle_t *h, const char 
*name,
        return (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFSTATUS, ifs));
 }
 
+static int
+ifconfig_vf_status_validate(const nvlist_t *status)
+{
+       const nvlist_t * const *vfs;
+       const nvlist_t *extensions, *vf;
+       const char *name;
+       void *cookie;
+       size_t i, num_vfs;
+       int type;
+
+       if (!nvlist_exists_number(status, IFVF_STATUS_VERSION_KEY))
+               return (EBADMSG);
+       if (nvlist_get_number(status, IFVF_STATUS_VERSION_KEY) !=
+           IFVF_STATUS_VERSION)
+               return (EPROTONOSUPPORT);
+       if (!nvlist_exists_nvlist_array(status, IFVF_STATUS_VFS))
+               return (EBADMSG);
+
+       vfs = nvlist_get_nvlist_array(status, IFVF_STATUS_VFS, &num_vfs);
+       for (i = 0; i < num_vfs; i++) {
+               vf = vfs[i];
+               if (!nvlist_exists_number(vf, IFVF_STATUS_INDEX))
+                       return (EBADMSG);
+               if (!nvlist_exists(vf, IFVF_STATUS_EXTENSIONS))
+                       continue;
+               if (!nvlist_exists_nvlist(vf, IFVF_STATUS_EXTENSIONS))
+                       return (EBADMSG);
+               extensions = nvlist_get_nvlist(vf, IFVF_STATUS_EXTENSIONS);
+               cookie = NULL;
+               while ((name = nvlist_next(extensions, &type, &cookie)) != 
NULL) {
+                       if (type != NV_TYPE_NVLIST)
+                               return (EBADMSG);
+                       if (!nvlist_exists_number(nvlist_get_nvlist(extensions,
+                           name), IFVF_STATUS_EXT_VERSION))
+                               return (EBADMSG);
+               }
+       }
+       return (0);
+}
+
+int
+ifconfig_get_vf_status(ifconfig_handle_t *h, const char *name,
+    nvlist_t **statusp)
+{
+       struct ifreq ifr;
+       nvlist_t *status;
+       void *buf, *newbuf;
+       size_t namelen;
+       u_int buflen, nextlen;
+       int error;
+
+       if (h == NULL || name == NULL || statusp == NULL) {
+               if (h != NULL)
+                       ifconfig_error(h, OTHER, EINVAL);
+               return (-1);
+       }
+       *statusp = NULL;
+       namelen = strnlen(name, IFNAMSIZ);
+       if (namelen == IFNAMSIZ) {
+               ifconfig_error(h, OTHER, ENAMETOOLONG);
+               return (-1);
+       }
+
+       /*
+        * ioctl(2) does not copy an _IOWR argument back to userspace when the
+        * command returns EFBIG, so the kernel's required length is not
+        * observable on a short-buffer error.  Start with enough space for the
+        * common case and grow geometrically instead of relying on length.
+        */
+       buflen = 16 * 1024;
+       buf = malloc(buflen);
+       if (buf == NULL) {
+               ifconfig_error(h, OTHER, ENOMEM);
+               return (-1);
+       }
+       for (;;) {
+               memset(&ifr, 0, sizeof(ifr));
+               memcpy(ifr.ifr_name, name, namelen + 1);
+               ifr.ifr_vf_status_nv.buffer = buf;
+               ifr.ifr_vf_status_nv.buf_length = buflen;
+               if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFVFSTATUS, &ifr) == 0)
+                       break;
+               if (ifconfig_err_errno(h) != EFBIG ||
+                   buflen == IFR_VF_STATUS_NV_MAXBUFSIZE ||
+                   ifr.ifr_vf_status_nv.length >
+                   IFR_VF_STATUS_NV_MAXBUFSIZE) {
+                       free(buf);
+                       return (-1);
+               }
+               if (ifr.ifr_vf_status_nv.length > buflen)
+                       nextlen = ifr.ifr_vf_status_nv.length;
+               else if (buflen > IFR_VF_STATUS_NV_MAXBUFSIZE / 2)
+                       nextlen = IFR_VF_STATUS_NV_MAXBUFSIZE;
+               else
+                       nextlen = buflen * 2;
+               newbuf = realloc(buf, nextlen);
+               if (newbuf == NULL) {
+                       free(buf);
+                       ifconfig_error(h, OTHER, ENOMEM);
+                       return (-1);
+               }
+               buf = newbuf;
+               buflen = nextlen;
+       }
+
+       if (ifr.ifr_vf_status_nv.length == 0 ||
+           ifr.ifr_vf_status_nv.length > buflen) {
+               free(buf);
+               ifconfig_error(h, OTHER, EBADMSG);
+               return (-1);
+       }
+       status = nvlist_unpack(buf, ifr.ifr_vf_status_nv.length, 0);
+       free(buf);
+       if (status == NULL) {
+               ifconfig_error(h, OTHER, EBADMSG);
+               return (-1);
+       }
+       error = ifconfig_vf_status_validate(status);
+       if (error != 0) {
+               nvlist_destroy(status);
+               ifconfig_error(h, OTHER, error);
+               return (-1);
+       }
+
+       ifconfig_error_clear(h);
+       *statusp = status;
+       return (0);
+}
+
 int
 ifconfig_destroy_interface(ifconfig_handle_t *h, const char *name)
 {
diff --git a/lib/libifconfig/libifconfig.h b/lib/libifconfig/libifconfig.h
index 817f52bd094e..130b002252c5 100644
--- a/lib/libifconfig/libifconfig.h
+++ b/lib/libifconfig/libifconfig.h
@@ -26,6 +26,7 @@
 
 #pragma once
 
+#include <sys/nv.h>
 #include <sys/types.h>
 
 #include <net/if.h>
@@ -198,6 +199,16 @@ int ifconfig_get_groups(ifconfig_handle_t *h, const char 
*name,
 int ifconfig_get_ifstatus(ifconfig_handle_t *h, const char *name,
     struct ifstat *stat);
 
+/** Retrieve structured SR-IOV VF status for an interface.
+ * @param h    An open ifconfig state object
+ * @param name The PF interface name
+ * @param statusp Return argument.  The caller owns the returned nvlist and
+ *                must destroy it with nvlist_destroy().
+ * @return     0 on success, -1 on failure
+ */
+int ifconfig_get_vf_status(ifconfig_handle_t *h, const char *name,
+    nvlist_t **statusp);
+
 /** Retrieve the interface media information
  * @param h    An open ifconfig state object
  * @param name The interface name
diff --git a/sbin/ifconfig/ifvfstatus.c b/sbin/ifconfig/ifvfstatus.c
index af857944a12a..12f495450260 100644
--- a/sbin/ifconfig/ifvfstatus.c
+++ b/sbin/ifconfig/ifvfstatus.c
@@ -27,9 +27,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <sys/ioctl.h>
 #include <sys/nv.h>
-#include <sys/socket.h>
 
 #include <net/ethernet.h>
 #include <net/if.h>
@@ -37,7 +35,6 @@
 #include <err.h>
 #include <errno.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
 #include "ifconfig.h"
@@ -63,64 +60,21 @@ vf_status(if_ctx *ctx)
 {
        const nvlist_t * const *vfs;
        const nvlist_t *vf;
-       struct ifreq ifr;
        const void *mac;
        nvlist_t *status;
        const char *mode, *state;
        size_t maclen, num_vfs;
        uint64_t speed;
-       void *buf, *newbuf;
-       u_int buflen, nextlen;
        bool printed;
+       int error;
 
-       buflen = 16 * 1024;
-       buf = malloc(buflen);
-       if (buf == NULL)
-               err(1, "malloc");
-       for (;;) {
-               memset(&ifr, 0, sizeof(ifr));
-               strlcpy(ifr.ifr_name, ctx->ifname, sizeof(ifr.ifr_name));
-               ifr.ifr_vf_status_nv.buffer = buf;
-               ifr.ifr_vf_status_nv.buf_length = buflen;
-               if (ioctl_ctx(ctx, SIOCGIFVFSTATUS, &ifr) == 0)
-                       break;
-               if (errno == EINVAL || errno == ENOTTY || errno == ENXIO ||
-                   errno == ENOTSUP || errno == EOPNOTSUPP) {
-                       free(buf);
+       if (ifconfig_get_vf_status(lifh, ctx->ifname, &status) != 0) {
+               error = ifconfig_err_errno(lifh);
+               if (error == EINVAL || error == ENOTTY || error == ENXIO ||
+                   error == ENOTSUP || error == EOPNOTSUPP)
                        return;
-               }
-               if (errno != EFBIG ||
-                   buflen == IFR_VF_STATUS_NV_MAXBUFSIZE ||
-                   ifr.ifr_vf_status_nv.length >
-                   IFR_VF_STATUS_NV_MAXBUFSIZE) {
-                       free(buf);
-                       warn("SIOCGIFVFSTATUS");
-                       return;
-               }
-               if (ifr.ifr_vf_status_nv.length > buflen)
-                       nextlen = ifr.ifr_vf_status_nv.length;
-               else if (buflen > IFR_VF_STATUS_NV_MAXBUFSIZE / 2)
-                       nextlen = IFR_VF_STATUS_NV_MAXBUFSIZE;
-               else
-                       nextlen = buflen * 2;
-               newbuf = realloc(buf, nextlen);
-               if (newbuf == NULL) {
-                       free(buf);
-                       err(1, "realloc");
-               }
-               buf = newbuf;
-               buflen = nextlen;
-       }
-
-       status = nvlist_unpack(buf, ifr.ifr_vf_status_nv.length, 0);
-       free(buf);
-       if (status == NULL ||
-           !nvlist_exists_number(status, IFVF_STATUS_VERSION_KEY) ||
-           nvlist_get_number(status, IFVF_STATUS_VERSION_KEY) !=
-           IFVF_STATUS_VERSION ||
-           !nvlist_exists_nvlist_array(status, IFVF_STATUS_VFS)) {
-               warnx("SIOCGIFVFSTATUS returned an unsupported format");
-               nvlist_destroy(status);
+               errno = error;
+               warn("SIOCGIFVFSTATUS");
                return;
        }
 
diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk
index b3c4e1861e24..589049bea777 100644
--- a/share/mk/src.libnames.mk
+++ b/share/mk/src.libnames.mk
@@ -379,7 +379,7 @@ _DP_gmock=  gtest
 _DP_gmock_main=        gmock
 _DP_gtest=     pthread regex
 _DP_gtest_main=        gtest
-_DP_ifconfig=  m
+_DP_ifconfig=  m nv
 _DP_ipf=       kvm
 _DP_iscsiutil= md
 _DP_kldelf=    elf
@@ -512,7 +512,7 @@ _DP_zpool=  md pthread z icp spl nvpair avl umem
 _DP_zutil=     avl geom m
 _DP_be=                zfs spl nvpair zfsbootenv
 _DP_netmap=
-_DP_ifconfig=  m
+_DP_ifconfig=  m nv
 _DP_pfctl=     nv
 _DP_krb5ss=            edit
 

Reply via email to