From nobody Sun Sep 06 13:52:02 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hdBTG3pd0z6r8Ch for ; Sun, 06 Sep 2026 13:52:02 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hdBTG1yZdz3rr4 for ; Sun, 06 Sep 2026 13:52:02 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1788702722; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=i0NVMe9hogoWj/HoolaG6ja4QhIrKKPqafXl7Y2QGi0=; b=FaGZLl1eKwGOqZe8bD/1Q7UFT0F3Sz1xEPcRKGM+h9xj4ctcAtHZsD7lNc1jb0R/1K/qGs 6/5qT+jprfoM20r8NyFiVbel+Dz+3wTexEPOTCWH4L+spmIpC/0eFLB9meP4nhzl7Hbent AkxaLDfllFzesgqcmWfSeWZKWHANvarVEf8a3xjAf+CXOXSDZt5Y+L9YLviJ7N/3TpriHd a5uok0VCne9T46UpY7bQvOSSXBwbPZQphNtIdS3dQTiXHAR0bz+DQZZfuxXqBINycAd+cy f8JU6FM3NxyEHkS6bUhV1NYMN/A0lWWrfzylSmyalpl37aRAfusbVwCrFRtPLA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1788702722; a=rsa-sha256; cv=none; b=FvTlS+/fyd4sSPBt/e6J57cLBOyddncA5tahe/efi9jRfkHHEc920v0CV1ttTtk+hhOz3D wn2FoDzRorTeGqJS7wVyCoYO4II9wCxM0NafsqfGr6Gz2+LDpTfdi0sk0X/E3OXKhlMlKE V5JWKybbialTRWPVbic/c+8xlGfW7DGnNIHVk0TZ603DvADI02FHPIRt0SB7mqJiekfM+1 7dCidC+jsSt6/FOEa+G47mbBn5G3Qol2d9Fgs/mDDvFIArcaEseyJb6yrXhlCfoWp98d1I 4HHVdMA6lIhrvj/B3PjXbY7m9DZY4TJNn+sObVKGSrMRf6DrSPk2v0QqaRqFrQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1788702722; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=i0NVMe9hogoWj/HoolaG6ja4QhIrKKPqafXl7Y2QGi0=; b=Y8qe1y8qJmdEEfqC047HTDH9+9djc6qxVW+ujUce4s1940XNE+NPiv71fwvd8GieU45GPi Des4TM/4MnVzyf4wJv7KLqlcK7VG0338McAaAkiUs2yNlNqlBTun5cv6v88pWt2Z0iQdJ/ PpJnOqtjEDOAKM3mnUjB6NWt1cU20j6Q0SeEgYQ5FnKnATxbD3qUOnLat65KYCspU6Mtyr A74CzfFHwaLnkK6klAQxPfyd3eI6IhqPmrJHbBVcGZR3UEEZnz252oQ9jJc5bnkG49EJSK pMNR6Q1a1xeEL1YNCp9A/c7ws93AuiF5Rtlx6dx+Ebv/YrrwKJenzn+mseA1ng== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hdBTG0sTTzb1W for ; Sun, 06 Sep 2026 13:52:02 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 36abf by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sun, 06 Sep 2026 13:52:02 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kevin Bowling Subject: git: a8eca531e23b - main - libifconfig: Add a native SR-IOV VF status query List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kbowling X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: a8eca531e23b49b71115216a9d54bd78dd16d87b Auto-Submitted: auto-generated Date: Sun, 06 Sep 2026 13:52:02 +0000 Message-Id: <6a9d7002.36abf.27802086@gitrepo.freebsd.org> The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=a8eca531e23b49b71115216a9d54bd78dd16d87b commit a8eca531e23b49b71115216a9d54bd78dd16d87b Author: Kevin Bowling AuthorDate: 2026-08-10 22:29:49 +0000 Commit: Kevin Bowling CommitDate: 2026-09-06 13:51:53 +0000 libifconfig: Add a native SR-IOV VF status query Request RTEXT_FILTER_VF through route Netlink and parse the common VF status schema into typed public structures. Preserve per-field presence using IFLAF_VF_* attribute numbers as mask bit indices so callers can distinguish omitted values from false or zero. Validate required VF indices, repeated driver namespaces, and their versioned typed fields while allowing unknown optional attributes. Return VF records through a pointer vector so append-only growth of the public VF structure does not change the array stride seen by existing consumers. Sponsored by: BBOX.io Differential Revision: https://reviews.freebsd.org/D58777 --- lib/libifconfig/Symbol.map | 5 + lib/libifconfig/libifconfig.h | 109 ++++++++++++++ lib/libifconfig/libifconfig_nl.c | 303 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 417 insertions(+) diff --git a/lib/libifconfig/Symbol.map b/lib/libifconfig/Symbol.map index 2e11ff963909..edb265410bef 100644 --- a/lib/libifconfig/Symbol.map +++ b/lib/libifconfig/Symbol.map @@ -84,3 +84,8 @@ FBSD_1.6 { ifconfig_sfp_rev_description; ifconfig_sfp_rev_symbol; }; + +FBSD_1.9 { + ifconfig_free_vf_status; + ifconfig_get_vf_status; +}; diff --git a/lib/libifconfig/libifconfig.h b/lib/libifconfig/libifconfig.h index 817f52bd094e..dd2fd16dba92 100644 --- a/lib/libifconfig/libifconfig.h +++ b/lib/libifconfig/libifconfig.h @@ -28,8 +28,10 @@ #include +#include #include #include /* for ifbvlan_set_t */ +#include #include #include @@ -198,6 +200,113 @@ 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); +/* + * SR-IOV VF status schema contract. + * + * Bits in the presence mask are indexed by IFLAF_VF_* and distinguish an + * omitted fact from false or zero. Driver-specific facts remain owned by a + * stable, versioned driver namespace: that driver defines the names, types, + * and meanings of its fields. Their named, typed form lets generic consumers + * carry or display extensions without knowing each driver's schema. Consumers + * that interpret extensions must ignore unknown namespaces and fields. + * Additive optional fields retain a namespace version; an incompatible type or + * semantic change requires a new version. All pointed-to storage belongs to + * the returned status object and is released by ifconfig_free_vf_status(). + * VF records are returned through a pointer vector so append-only growth of + * struct ifconfig_vf_info does not change the array stride seen by existing + * consumers. VLAN PCP and protocol describe the PF-administered access VLAN; + * they do not describe trunk-filter entries. + */ + +enum ifconfig_vf_vlan_mode { + IFCONFIG_VF_VLAN_UNKNOWN = IFLAF_VF_VLAN_UNKNOWN, + IFCONFIG_VF_VLAN_ACCESS = IFLAF_VF_VLAN_ACCESS, + IFCONFIG_VF_VLAN_TRUNK = IFLAF_VF_VLAN_TRUNK, +}; + +enum ifconfig_vf_link_state { + IFCONFIG_VF_LINK_UNKNOWN = IFLAF_VF_LINK_UNKNOWN, + IFCONFIG_VF_LINK_DOWN = IFLAF_VF_LINK_DOWN, + IFCONFIG_VF_LINK_UP = IFLAF_VF_LINK_UP, + IFCONFIG_VF_LINK_AUTO = IFLAF_VF_LINK_AUTO, +}; + +enum ifconfig_vf_extension_type { + IFCONFIG_VF_EXT_BOOL = 1, + IFCONFIG_VF_EXT_NUMBER, + IFCONFIG_VF_EXT_STRING, + IFCONFIG_VF_EXT_BINARY, +}; + +struct ifconfig_vf_extension_field { + char *name; + enum ifconfig_vf_extension_type type; + union { + bool boolean; + uint64_t number; + char *string; + struct { + void *data; + size_t length; + } binary; + } value; +}; + +struct ifconfig_vf_extension { + char *name; + uint32_t version; + size_t num_fields; + struct ifconfig_vf_extension_field *fields; +}; + +struct ifconfig_vf_info { + uint64_t fields; + uint64_t min_tx_rate_bps; /* Zero means no guaranteed allocation. */ + uint64_t max_tx_rate_bps; /* Zero means unlimited. */ + uint32_t index; + uint32_t vlan_count; + uint32_t vlan_limit; + uint16_t tx_queue_count; + uint16_t rx_queue_count; + uint16_t vlan; + uint16_t vlan_proto; /* Host-order Ethernet type. */ + uint8_t vlan_pcp; + uint8_t mac[ETHER_ADDR_LEN]; + enum ifconfig_vf_vlan_mode vlan_mode; + enum ifconfig_vf_link_state link_state_policy; + bool configured; + bool initialized; + bool allow_set_mac; + bool allow_set_vlan; + bool mac_anti_spoof; + bool allow_promisc; + bool traffic_allowed; + bool fault_blocked; + bool quarantined; + char *api_version; + size_t num_extensions; + struct ifconfig_vf_extension *extensions; +}; + +struct ifconfig_vf_status { + uint64_t pf_link_speed; + enum ifconfig_vf_link_state pf_link_state; + bool pf_link_state_present; + bool pf_link_speed_present; + size_t num_vfs; + struct ifconfig_vf_info **vfs; +}; + +/** Retrieve structured SR-IOV VF status for an interface through rtnetlink. + * @param h An open ifconfig state object + * @param name The PF interface name + * @param statusp Return argument. Free it with ifconfig_free_vf_status(). + * @return 0 on success, -1 on failure + */ +int ifconfig_get_vf_status(ifconfig_handle_t *h, const char *name, + struct ifconfig_vf_status **statusp); +void ifconfig_free_vf_status(struct ifconfig_vf_status *status); + /** Retrieve the interface media information * @param h An open ifconfig state object * @param name The interface name diff --git a/lib/libifconfig/libifconfig_nl.c b/lib/libifconfig/libifconfig_nl.c index 7d9decabe26f..ea514ee76c5b 100644 --- a/lib/libifconfig/libifconfig_nl.c +++ b/lib/libifconfig/libifconfig_nl.c @@ -6,15 +6,26 @@ #include #include +#include #include #include +#include +#include +#include + #include "libifconfig.h" #include "libifconfig_internal.h" static int ifconfig_modify_flags(ifconfig_handle_t *h, const char *ifname, int ifi_flags, int ifi_change); +struct ifconfig_vf_status_storage { + /* The public object must remain first so free can recover this object. */ + struct ifconfig_vf_status public; + struct ifconfig_vf_info *vf_info; +}; + static int ifconfig_modify_flags(ifconfig_handle_t *h, const char *ifname, int ifi_flags, int ifi_change) @@ -70,3 +81,295 @@ ifconfig_set_up(ifconfig_handle_t *h, const char *ifname, bool up) return (ifconfig_modify_flags(h, ifname, flag, IFF_UP)); } + +void +ifconfig_free_vf_status(struct ifconfig_vf_status *status) +{ + struct ifconfig_vf_status_storage *storage; + struct ifconfig_vf_extension_field *field; + struct ifconfig_vf_extension *extension; + struct ifconfig_vf_info *vf; + size_t i, j, k; + + if (status == NULL) + return; + storage = (struct ifconfig_vf_status_storage *)(void *)status; + for (i = 0; status->vfs != NULL && i < status->num_vfs; i++) { + vf = status->vfs[i]; + if (vf == NULL) + continue; + free(vf->api_version); + for (j = 0; j < vf->num_extensions; j++) { + extension = &vf->extensions[j]; + free(extension->name); + for (k = 0; k < extension->num_fields; k++) { + field = &extension->fields[k]; + free(field->name); + if (field->type == IFCONFIG_VF_EXT_STRING) + free(field->value.string); + else if (field->type == IFCONFIG_VF_EXT_BINARY) + free(field->value.binary.data); + } + free(extension->fields); + } + free(vf->extensions); + } + free(storage->vf_info); + free(status->vfs); + free(storage); +} + +static int +ifconfig_copy_vf_extension_field(struct ifconfig_vf_extension_field *dst, + const struct snl_parsed_vf_driver_field *src) +{ + size_t length; + + dst->name = strdup(src->name); + if (dst->name == NULL) + return (ENOMEM); + switch (src->type) { + case SNL_VFDF_BOOL: + dst->type = IFCONFIG_VF_EXT_BOOL; + dst->value.boolean = src->boolean; + break; + case SNL_VFDF_NUMBER: + dst->type = IFCONFIG_VF_EXT_NUMBER; + dst->value.number = src->number; + break; + case SNL_VFDF_STRING: + dst->type = IFCONFIG_VF_EXT_STRING; + dst->value.string = strdup(src->string); + if (dst->value.string == NULL) + return (ENOMEM); + break; + case SNL_VFDF_BINARY: + dst->type = IFCONFIG_VF_EXT_BINARY; + length = NLA_DATA_LEN(src->binary); + if (length == 0) + return (EBADMSG); + dst->value.binary.data = malloc(length); + if (dst->value.binary.data == NULL) + return (ENOMEM); + memcpy(dst->value.binary.data, NLA_DATA(src->binary), length); + dst->value.binary.length = length; + break; + default: + return (EBADMSG); + } + return (0); +} + +static int +ifconfig_copy_vf_extensions(struct ifconfig_vf_info *dst, + const struct snl_parsed_vf *src) +{ + const struct snl_parsed_vf_driver_field *src_field; + const struct snl_parsed_vf_driver *src_extension; + struct ifconfig_vf_extension *extension; + size_t i, j, num_extensions, num_fields; + int error; + + num_extensions = src->drivers.count; + if (num_extensions == 0) + return (0); + dst->extensions = calloc(num_extensions, sizeof(*dst->extensions)); + if (dst->extensions == NULL) + return (ENOMEM); + dst->num_extensions = num_extensions; + for (i = 0; i < dst->num_extensions; i++) { + src_extension = src->drivers.items[i]; + extension = &dst->extensions[i]; + extension->name = strdup(src_extension->name); + if (extension->name == NULL) + return (ENOMEM); + extension->version = src_extension->version; + num_fields = src_extension->fields.count; + if (num_fields == 0) + continue; + extension->fields = calloc(num_fields, + sizeof(*extension->fields)); + if (extension->fields == NULL) + return (ENOMEM); + extension->num_fields = num_fields; + for (j = 0; j < extension->num_fields; j++) { + src_field = src_extension->fields.items[j]; + error = ifconfig_copy_vf_extension_field( + &extension->fields[j], src_field); + if (error != 0) + return (error); + } + } + return (0); +} + +static int +ifconfig_copy_vf(struct ifconfig_vf_info *dst, + const struct snl_parsed_vf *src) +{ + + dst->fields = src->attrs; + dst->min_tx_rate_bps = src->min_tx_rate_bps; + dst->max_tx_rate_bps = src->max_tx_rate_bps; + dst->index = src->index; + dst->vlan_count = src->vlan_count; + dst->vlan_limit = src->vlan_limit; + dst->tx_queue_count = src->tx_queue_count; + dst->rx_queue_count = src->rx_queue_count; + dst->vlan = src->vlan; + dst->vlan_proto = src->vlan_proto; + dst->vlan_pcp = src->vlan_pcp; + if ((src->attrs & (1ULL << IFLAF_VF_MAC)) != 0) { + if (NLA_DATA_LEN(src->mac) != sizeof(dst->mac)) + return (EBADMSG); + memcpy(dst->mac, NLA_DATA(src->mac), sizeof(dst->mac)); + } + dst->vlan_mode = (enum ifconfig_vf_vlan_mode)src->vlan_mode; + dst->link_state_policy = + (enum ifconfig_vf_link_state)src->link_state_policy; + dst->configured = src->configured; + dst->initialized = src->initialized; + dst->allow_set_mac = src->allow_set_mac; + dst->allow_set_vlan = src->allow_set_vlan; + dst->mac_anti_spoof = src->mac_anti_spoof; + dst->allow_promisc = src->allow_promisc; + dst->traffic_allowed = src->traffic_allowed; + dst->fault_blocked = src->fault_blocked; + dst->quarantined = src->quarantined; + if ((src->attrs & (1ULL << IFLAF_VF_API_VERSION)) != 0) { + dst->api_version = strdup(src->api_version); + if (dst->api_version == NULL) + return (ENOMEM); + } + return (ifconfig_copy_vf_extensions(dst, src)); +} + +int +ifconfig_get_vf_status(ifconfig_handle_t *h, const char *name, + struct ifconfig_vf_status **statusp) +{ + struct ifconfig_vf_status_storage *storage; + struct ifconfig_vf_status *status; + struct snl_parsed_link link = {}; + struct snl_errmsg_data e = {}; + struct snl_state ss = {}; + struct snl_writer nw = {}; + struct nlmsghdr *hdr; + ifconfig_errtype errtype; + uint32_t seq; + size_t i; + int error; + + if (h == NULL || name == NULL || statusp == NULL) { + if (h != NULL) + ifconfig_error(h, OTHER, EINVAL); + return (-1); + } + *statusp = NULL; + if (strnlen(name, IFNAMSIZ) == IFNAMSIZ) { + ifconfig_error(h, OTHER, ENAMETOOLONG); + return (-1); + } + if (!snl_init(&ss, NETLINK_ROUTE)) { + ifconfig_error(h, NETLINK, ENOTSUP); + return (-1); + } + errtype = NETLINK; + + snl_init_writer(&ss, &nw); + hdr = snl_create_msg_request(&nw, NL_RTM_GETLINK); + if (hdr == NULL || + snl_reserve_msg_object(&nw, struct ifinfomsg) == NULL || + !snl_add_msg_attr_string(&nw, IFLA_IFNAME, name) || + !snl_add_msg_attr_u32(&nw, IFLA_EXT_MASK, RTEXT_FILTER_VF) || + (hdr = snl_finalize_msg(&nw)) == NULL) { + error = ENOMEM; + goto fail; + } + seq = hdr->nlmsg_seq; + if (!snl_send_message(&ss, hdr)) { + error = EIO; + goto fail; + } + error = snl_grow_rxbuf_to_next_message(&ss); + if (error != 0) { + if (error == ENOMEM) + errtype = OTHER; + goto fail; + } + hdr = snl_read_reply(&ss, seq); + if (hdr == NULL) { + error = EIO; + goto fail; + } + if (hdr->nlmsg_type == NLMSG_ERROR) { + if (!snl_parse_errmsg(&ss, hdr, &e) || e.error == 0) + error = EBADMSG; + else + error = e.error; + goto fail; + } + if (hdr->nlmsg_type != NL_RTM_NEWLINK || + !snl_parse_nlmsg(&ss, hdr, &snl_rtm_link_parser, &link)) { + error = EBADMSG; + goto fail; + } + if (!link.iflaf_vf_status.present) { + error = EOPNOTSUPP; + goto fail; + } + if (link.iflaf_vf_status.error != 0) { + error = link.iflaf_vf_status.error; + goto fail; + } + if (link.ifla_num_vf != link.iflaf_vf_status.vfs.count) { + error = EBADMSG; + goto fail; + } + + storage = calloc(1, sizeof(*storage)); + if (storage == NULL) { + errtype = OTHER; + error = ENOMEM; + goto fail; + } + status = &storage->public; + status->pf_link_state = (enum ifconfig_vf_link_state) + link.iflaf_vf_status.pf_link_state; + status->pf_link_state_present = true; + status->pf_link_speed = link.iflaf_vf_status.pf_link_speed; + status->pf_link_speed_present = status->pf_link_speed != 0; + status->num_vfs = link.iflaf_vf_status.vfs.count; + if (status->num_vfs != 0) { + status->vfs = calloc(status->num_vfs, sizeof(*status->vfs)); + storage->vf_info = calloc(status->num_vfs, + sizeof(*storage->vf_info)); + if (status->vfs == NULL || storage->vf_info == NULL) { + errtype = OTHER; + error = ENOMEM; + goto fail_status; + } + for (i = 0; i < status->num_vfs; i++) + status->vfs[i] = &storage->vf_info[i]; + } + for (i = 0; i < status->num_vfs; i++) { + error = ifconfig_copy_vf(status->vfs[i], + link.iflaf_vf_status.vfs.items[i]); + if (error != 0) { + if (error == ENOMEM) + errtype = OTHER; + goto fail_status; + } + } + ifconfig_error_clear(h); + *statusp = status; + snl_free(&ss); + return (0); + +fail_status: + ifconfig_free_vf_status(status); +fail: + ifconfig_error(h, errtype, error); + snl_free(&ss); + return (-1); +}