git: 2d6114f6d26b - main - libifconfig: Add an SR-IOV VF status query

Kevin Bowling <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src,gmane.os.freebsd.current.scm
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.