mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
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.
This commit is contained in:
1 parent
2c04cfa148
commit
2d6114f6d2
6 files changed
+154
-56
No files matched your search
@@ -1,7 +1,7 @@
|
||||
LIB= ifconfig
|
||||
INTERNALLIB= true
|
||||
|
||||
LIBADD= m
|
||||
LIBADD= m nv
|
||||
|
||||
SHLIBDIR?= /lib
|
||||
SHLIB_MAJOR= 2
|
||||
|
||||
@@ -84,3 +84,7 @@ FBSD_1.6 {
|
||||
ifconfig_sfp_rev_description;
|
||||
ifconfig_sfp_rev_symbol;
|
||||
};
|
||||
|
||||
FBSD_1.9 {
|
||||
ifconfig_get_vf_status;
|
||||
};
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user