Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 09 Aug 2026 06:47:45 +0000
From:      Kevin Bowling <kbowling@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 2d6114f6d26b - main - libifconfig: Add an SR-IOV VF status query
Message-ID:  <6a782291.18052.6103210b@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kbowling:

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

commit 2d6114f6d26bf7dfa5ad94e1db9b09ee7108dc7a
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-09 02:03:20 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
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
 


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a782291.18052.6103210b>