git: c344eff91070 - main - netlink: dump interface capabilities with other interface data.

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Fri, 16 Jun 2023 15:34:03 UTC
The branch main has been updated by melifaro:

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

commit c344eff91070ccb15e81baf7897224882b417ae4
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-06-16 14:56:39 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-06-16 15:33:49 +0000

    netlink: dump interface capabilities with other interface data.
    
    This change exports interface capabilities using the standard
    Netlink attribute type, bitset, and switches `ifconfig(8)` to use
    it when displaying interface data.
    Bitset comes in two representations. The first one is "compact",
    where the bits are exported via two arrays - "mask" listing the
    "valid" bits and "values, providing the values for those bits.
    The second one is more verbose, listing each bit as a separate item,
    with its name, id and value. The latter option is handy when submitting
    update requests.
    
    The support for setting capabilities will be added in the upcoming diffs.
    
    Differential Revision: https://reviews.freebsd.org/D40331
---
 sbin/ifconfig/ifconfig.c                |   6 +-
 sbin/ifconfig/ifconfig.h                |   1 -
 sbin/ifconfig/ifconfig_netlink.c        |  26 +++++-
 sys/net/if.c                            |   1 +
 sys/net/if.h                            | 146 +++++++++++++++++---------------
 sys/net/if_strings.h                    | 106 +++++++++++++++++++++++
 sys/netlink/netlink_bitset.h            |  57 +++++++++++++
 sys/netlink/netlink_route.h             |   1 +
 sys/netlink/netlink_snl.h               |  88 ++++++++++++++++++-
 sys/netlink/netlink_snl_route_parsers.h |   2 +
 sys/netlink/route/iface.c               |  28 ++++++
 sys/netlink/route/interface.h           |   1 +
 12 files changed, 390 insertions(+), 73 deletions(-)

diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
index f3d16fc052f6..fa22f09f8100 100644
--- a/sbin/ifconfig/ifconfig.c
+++ b/sbin/ifconfig/ifconfig.c
@@ -55,6 +55,7 @@ static const char rcsid[] =
 #include <net/ethernet.h>
 #include <net/if.h>
 #include <net/if_dl.h>
+#include <net/if_strings.h>
 #include <net/if_types.h>
 #include <net/route.h>
 
@@ -1585,6 +1586,8 @@ unsetifdescr(if_ctx *ctx, const char *val __unused, int value __unused)
 	setifdescr(ctx, "", 0);
 }
 
+#ifdef WITHOUT_NETLINK
+
 #define	IFFBITS \
 "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\7RUNNING" \
 "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \
@@ -1656,7 +1659,7 @@ print_ifcap_nv(if_ctx *ctx)
 		Perror("ioctl (SIOCGIFCAP)");
 }
 
-void
+static void
 print_ifcap(if_ctx *ctx)
 {
 	struct ifreq ifr = {};
@@ -1675,6 +1678,7 @@ print_ifcap(if_ctx *ctx)
 		}
 	}
 }
+#endif
 
 void
 print_ifstatus(if_ctx *ctx)
diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h
index c97ef447a3b3..e33a2c63aec1 100644
--- a/sbin/ifconfig/ifconfig.h
+++ b/sbin/ifconfig/ifconfig.h
@@ -275,7 +275,6 @@ bool	match_ether(const struct sockaddr_dl *sdl);
 bool	match_if_flags(struct ifconfig_args *args, int if_flags);
 int	ifconfig_ioctl(if_ctx *ctx, int iscreate, const struct afswtch *uafp);
 bool	group_member(const char *ifname, const char *match, const char *nomatch);
-void	print_ifcap(if_ctx *ctx);
 void	tunnel_status(if_ctx *ctx);
 struct afswtch	*af_getbyfamily(int af);
 void	af_other_status(if_ctx *ctx);
diff --git a/sbin/ifconfig/ifconfig_netlink.c b/sbin/ifconfig/ifconfig_netlink.c
index 2460d8c60109..f09023c1477c 100644
--- a/sbin/ifconfig/ifconfig_netlink.c
+++ b/sbin/ifconfig/ifconfig_netlink.c
@@ -48,6 +48,7 @@
 #include <net/ethernet.h>
 #include <net/if.h>
 #include <net/if_dl.h>
+#include <net/if_strings.h>
 #include <net/if_types.h>
 #include "ifconfig.h"
 #include "ifconfig_netlink.h"
@@ -343,6 +344,28 @@ sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface)
 	}
 }
 
+static void
+print_ifcaps(if_ctx *ctx, if_link_t *link)
+{
+	uint32_t sz_u32 = roundup2(link->iflaf_caps.nla_bitset_size, 32) / 32;
+
+	if (sz_u32 > 0) {
+		uint32_t *caps = link->iflaf_caps.nla_bitset_value;
+
+		printf("\toptions=%x", caps[0]);
+		print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names));
+		putchar('\n');
+	}
+
+	if (ctx->args->supmedia && sz_u32 > 0) {
+		uint32_t *caps = link->iflaf_caps.nla_bitset_mask;
+
+		printf("\tcapabilities=%x", caps[0]);
+		print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names));
+		putchar('\n');
+	}
+}
+
 static void
 status_nl(if_ctx *ctx, struct iface *iface)
 {
@@ -360,8 +383,7 @@ status_nl(if_ctx *ctx, struct iface *iface)
 	if (link->ifla_ifalias != NULL)
 		printf("\tdescription: %s\n", link->ifla_ifalias);
 
-	/* TODO: convert to netlink */
-	print_ifcap(ctx);
+	print_ifcaps(ctx, link);
 	tunnel_status(ctx);
 
 	if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) {
diff --git a/sys/net/if.c b/sys/net/if.c
index 975f4498073e..c5f0a65721fc 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -82,6 +82,7 @@
 #include <net/if_arp.h>
 #include <net/if_clone.h>
 #include <net/if_dl.h>
+#include <net/if_strings.h>
 #include <net/if_types.h>
 #include <net/if_var.h>
 #include <net/if_media.h>
diff --git a/sys/net/if.h b/sys/net/if.h
index 4003b33e5de4..bd2787516f01 100644
--- a/sys/net/if.h
+++ b/sys/net/if.h
@@ -221,42 +221,86 @@ struct if_data {
  * to do the right thing. However, having the filter here
  * avoids replication of the same code in all individual drivers.
  */
-#define	IFCAP_RXCSUM		0x00001  /* can offload checksum on RX */
-#define	IFCAP_TXCSUM		0x00002  /* can offload checksum on TX */
-#define	IFCAP_NETCONS		0x00004  /* can be a network console */
-#define	IFCAP_VLAN_MTU		0x00008	/* VLAN-compatible MTU */
-#define	IFCAP_VLAN_HWTAGGING	0x00010	/* hardware VLAN tag support */
-#define	IFCAP_JUMBO_MTU		0x00020	/* 9000 byte MTU supported */
-#define	IFCAP_POLLING		0x00040	/* driver supports polling */
-#define	IFCAP_VLAN_HWCSUM	0x00080	/* can do IFCAP_HWCSUM on VLANs */
-#define	IFCAP_TSO4		0x00100	/* can do TCP Segmentation Offload */
-#define	IFCAP_TSO6		0x00200	/* can do TCP6 Segmentation Offload */
-#define	IFCAP_LRO		0x00400	/* can do Large Receive Offload */
-#define	IFCAP_WOL_UCAST		0x00800	/* wake on any unicast frame */
-#define	IFCAP_WOL_MCAST		0x01000	/* wake on any multicast frame */
-#define	IFCAP_WOL_MAGIC		0x02000	/* wake on any Magic Packet */
-#define	IFCAP_TOE4		0x04000	/* interface can offload TCP */
-#define	IFCAP_TOE6		0x08000	/* interface can offload TCP6 */
-#define	IFCAP_VLAN_HWFILTER	0x10000 /* interface hw can filter vlan tag */
-#define	IFCAP_NV		0x20000 /* can do SIOCGIFCAPNV/SIOCSIFCAPNV */
-#define	IFCAP_VLAN_HWTSO	0x40000 /* can do IFCAP_TSO on VLANs */
-#define	IFCAP_LINKSTATE		0x80000 /* the runtime link state is dynamic */
-#define	IFCAP_NETMAP		0x100000 /* netmap mode supported/enabled */
-#define	IFCAP_RXCSUM_IPV6	0x200000  /* can offload checksum on IPv6 RX */
-#define	IFCAP_TXCSUM_IPV6	0x400000  /* can offload checksum on IPv6 TX */
-#define	IFCAP_HWSTATS		0x800000 /* manages counters internally */
-#define	IFCAP_TXRTLMT		0x1000000 /* hardware supports TX rate limiting */
-#define	IFCAP_HWRXTSTMP		0x2000000 /* hardware rx timestamping */
-#define	IFCAP_MEXTPG		0x4000000 /* understands M_EXTPG mbufs */
-#define	IFCAP_TXTLS4		0x8000000 /* can do TLS encryption and segmentation for TCP */
-#define	IFCAP_TXTLS6		0x10000000 /* can do TLS encryption and segmentation for TCP6 */
-#define	IFCAP_VXLAN_HWCSUM	0x20000000 /* can do IFCAN_HWCSUM on VXLANs */
-#define	IFCAP_VXLAN_HWTSO	0x40000000 /* can do IFCAP_TSO on VXLANs */
-#define	IFCAP_TXTLS_RTLMT	0x80000000 /* can do TLS with rate limiting */
+
+/* IFCAP values as bit indexes */
+
+#define	IFCAP_B_RXCSUM		0 /* can offload checksum on RX */
+#define	IFCAP_B_TXCSUM		1 /* can offload checksum on TX */
+#define	IFCAP_B_NETCONS		2 /* can be a network console */
+#define	IFCAP_B_VLAN_MTU	3 /* VLAN-compatible MTU */
+#define	IFCAP_B_VLAN_HWTAGGING	4 /* hardware VLAN tag support */
+#define	IFCAP_B_JUMBO_MTU	5 /* 9000 byte MTU supported */
+#define	IFCAP_B_POLLING		6 /* driver supports polling */
+#define	IFCAP_B_VLAN_HWCSUM	7 /* can do IFCAP_HWCSUM on VLANs */
+#define	IFCAP_B_TSO4		8 /* can do TCP Segmentation Offload */
+#define	IFCAP_B_TSO6		9 /* can do TCP6 Segmentation Offload */
+#define	IFCAP_B_LRO		10 /* can do Large Receive Offload */
+#define	IFCAP_B_WOL_UCAST	11 /* wake on any unicast frame */
+#define	IFCAP_B_WOL_MCAST	12 /* wake on any multicast frame */
+#define	IFCAP_B_WOL_MAGIC	13 /* wake on any Magic Packet */
+#define	IFCAP_B_TOE4		14 /* interface can offload TCP */
+#define	IFCAP_B_TOE6		15 /* interface can offload TCP6 */
+#define	IFCAP_B_VLAN_HWFILTER	16 /* interface hw can filter vlan tag */
+#define	IFCAP_B_NV		17 /* can do SIOCGIFCAPNV/SIOCSIFCAPNV */
+#define	IFCAP_B_VLAN_HWTSO	18 /* can do IFCAP_TSO on VLANs */
+#define	IFCAP_B_LINKSTATE	19 /* the runtime link state is dynamic */
+#define	IFCAP_B_NETMAP		20 /* netmap mode supported/enabled */
+#define	IFCAP_B_RXCSUM_IPV6	21 /* can offload checksum on IPv6 RX */
+#define	IFCAP_B_TXCSUM_IPV6	22 /* can offload checksum on IPv6 TX */
+#define	IFCAP_B_HWSTATS		23 /* manages counters internally */
+#define	IFCAP_B_TXRTLMT		24 /* hardware supports TX rate limiting */
+#define	IFCAP_B_HWRXTSTMP	25 /* hardware rx timestamping */
+#define	IFCAP_B_MEXTPG		26 /* understands M_EXTPG mbufs */
+#define	IFCAP_B_TXTLS4		27 /* can do TLS encryption and segmentation for TCP */
+#define	IFCAP_B_TXTLS6		28 /* can do TLS encryption and segmentation for TCP6 */
+#define	IFCAP_B_VXLAN_HWCSUM	29 /* can do IFCAN_HWCSUM on VXLANs */
+#define	IFCAP_B_VXLAN_HWTSO	30 /* can do IFCAP_TSO on VXLANs */
+#define	IFCAP_B_TXTLS_RTLMT	31 /* can do TLS with rate limiting */
+#define	IFCAP_B_RXTLS4		32 /* can to TLS receive for TCP */
+#define	IFCAP_B_RXTLS6		33 /* can to TLS receive for TCP6 */
+#define	__IFCAP_B_SIZE		34
+
+#define	IFCAP_B_MAX	(__IFCAP_B_MAX - 1)
+#define	IFCAP_B_SIZE	(__IFCAP_B_SIZE)
+
+#define	IFCAP_BIT(x)		(1 << (x))
+
+#define	IFCAP_RXCSUM		IFCAP_BIT(IFCAP_B_RXCSUM)
+#define	IFCAP_TXCSUM		IFCAP_BIT(IFCAP_B_TXCSUM)
+#define	IFCAP_NETCONS		IFCAP_BIT(IFCAP_B_NETCONS)
+#define	IFCAP_VLAN_MTU		IFCAP_BIT(IFCAP_B_VLAN_MTU)
+#define	IFCAP_VLAN_HWTAGGING	IFCAP_BIT(IFCAP_B_VLAN_HWTAGGING)
+#define	IFCAP_JUMBO_MTU		IFCAP_BIT(IFCAP_B_JUMBO_MTU)
+#define	IFCAP_POLLING		IFCAP_BIT(IFCAP_B_POLLING)
+#define	IFCAP_VLAN_HWCSUM	IFCAP_BIT(IFCAP_B_VLAN_HWCSUM)
+#define	IFCAP_TSO4		IFCAP_BIT(IFCAP_B_TSO4)
+#define	IFCAP_TSO6		IFCAP_BIT(IFCAP_B_TSO6)
+#define	IFCAP_LRO		IFCAP_BIT(IFCAP_B_LRO)
+#define	IFCAP_WOL_UCAST		IFCAP_BIT(IFCAP_B_WOL_UCAST)
+#define	IFCAP_WOL_MCAST		IFCAP_BIT(IFCAP_B_WOL_MCAST)
+#define	IFCAP_WOL_MAGIC		IFCAP_BIT(IFCAP_B_WOL_MAGIC)
+#define	IFCAP_TOE4		IFCAP_BIT(IFCAP_B_TOE4)
+#define	IFCAP_TOE6		IFCAP_BIT(IFCAP_B_TOE6)
+#define	IFCAP_VLAN_HWFILTER	IFCAP_BIT(IFCAP_B_VLAN_HWFILTER)
+#define	IFCAP_NV		IFCAP_BIT(IFCAP_B_NV)
+#define	IFCAP_VLAN_HWTSO	IFCAP_BIT(IFCAP_B_VLAN_HWTSO)
+#define	IFCAP_LINKSTATE		IFCAP_BIT(IFCAP_B_LINKSTATE)
+#define	IFCAP_NETMAP		IFCAP_BIT(IFCAP_B_NETMAP)
+#define	IFCAP_RXCSUM_IPV6	IFCAP_BIT(IFCAP_B_RXCSUM_IPV6)
+#define	IFCAP_TXCSUM_IPV6	IFCAP_BIT(IFCAP_B_TXCSUM_IPV6)
+#define	IFCAP_HWSTATS		IFCAP_BIT(IFCAP_B_HWSTATS)
+#define	IFCAP_TXRTLMT		IFCAP_BIT(IFCAP_B_TXRTLMT)
+#define	IFCAP_HWRXTSTMP		IFCAP_BIT(IFCAP_B_HWRXTSTMP)
+#define	IFCAP_MEXTPG		IFCAP_BIT(IFCAP_B_MEXTPG)
+#define	IFCAP_TXTLS4		IFCAP_BIT(IFCAP_B_TXTLS4)
+#define	IFCAP_TXTLS6		IFCAP_BIT(IFCAP_B_TXTLS6)
+#define	IFCAP_VXLAN_HWCSUM	IFCAP_BIT(IFCAP_B_VXLAN_HWCSUM)
+#define	IFCAP_VXLAN_HWTSO	IFCAP_BIT(IFCAP_B_VXLAN_HWTSO)
+#define	IFCAP_TXTLS_RTLMT	IFCAP_BIT(IFCAP_B_TXTLS_RTLMT)
 
 /* IFCAP2_* are integers, not bits. */
-#define	IFCAP2_RXTLS4		0
-#define	IFCAP2_RXTLS6		1
+#define	IFCAP2_RXTLS4		(IFCAP_B_RXTLS4 - 32)
+#define	IFCAP2_RXTLS6		(IFCAP_B_RXTLS6 - 32)
 
 #define	IFCAP2_BIT(x)		(1UL << (x))
 
@@ -271,40 +315,6 @@ struct if_data {
 #define	IFCAP_CANTCHANGE	(IFCAP_NETMAP | IFCAP_NV)
 #define	IFCAP_ALLCAPS		0xffffffff
 
-#define	IFCAP_RXCSUM_NAME	"RXCSUM"
-#define	IFCAP_TXCSUM_NAME	"TXCSUM"
-#define	IFCAP_NETCONS_NAME	"NETCONS"
-#define	IFCAP_VLAN_MTU_NAME	"VLAN_MTU"
-#define	IFCAP_VLAN_HWTAGGING_NAME "VLAN_HWTAGGING"
-#define	IFCAP_JUMBO_MTU_NAME	"JUMBO_MTU"
-#define	IFCAP_POLLING_NAME	"POLLING"
-#define	IFCAP_VLAN_HWCSUM_NAME	"VLAN_HWCSUM"
-#define	IFCAP_TSO4_NAME		"TSO4"
-#define	IFCAP_TSO6_NAME		"TSO6"
-#define	IFCAP_LRO_NAME		"LRO"
-#define	IFCAP_WOL_UCAST_NAME	"WOL_UCAST"
-#define	IFCAP_WOL_MCAST_NAME	"WOL_MCAST"
-#define	IFCAP_WOL_MAGIC_NAME	"WOL_MAGIC"
-#define	IFCAP_TOE4_NAME		"TOE4"
-#define	IFCAP_TOE6_NAME		"TOE6"
-#define	IFCAP_VLAN_HWFILTER_NAME "VLAN_HWFILTER"
-#define	IFCAP_VLAN_HWTSO_NAME	"VLAN_HWTSO"
-#define	IFCAP_LINKSTATE_NAME	"LINKSTATE"
-#define	IFCAP_NETMAP_NAME	"NETMAP"
-#define	IFCAP_RXCSUM_IPV6_NAME	"RXCSUM_IPV6"
-#define	IFCAP_TXCSUM_IPV6_NAME	"TXCSUM_IPV6"
-#define	IFCAP_HWSTATS_NAME	"HWSTATS"
-#define	IFCAP_TXRTLMT_NAME	"TXRTLMT"
-#define	IFCAP_HWRXTSTMP_NAME	"HWRXTSTMP"
-#define	IFCAP_MEXTPG_NAME	"MEXTPG"
-#define	IFCAP_TXTLS4_NAME	"TXTLS4"
-#define	IFCAP_TXTLS6_NAME	"TXTLS6"
-#define	IFCAP_VXLAN_HWCSUM_NAME	"VXLAN_HWCSUM"
-#define	IFCAP_VXLAN_HWTSO_NAME	"VXLAN_HWTSO"
-#define	IFCAP_TXTLS_RTLMT_NAME	"TXTLS_RTLMT"
-#define	IFCAP2_RXTLS4_NAME	"RXTLS4"
-#define	IFCAP2_RXTLS6_NAME	"RXTLS6"
-
 #define	IFQ_MAXLEN	50
 #define	IFNET_SLOWHZ	1		/* granularity is 1 second */
 
diff --git a/sys/net/if_strings.h b/sys/net/if_strings.h
new file mode 100644
index 000000000000..95d85f5370ed
--- /dev/null
+++ b/sys/net/if_strings.h
@@ -0,0 +1,106 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NET_IF_STRINGS_H_
+#define _NET_IF_STRINGS_H_
+
+#define	IFCAP_RXCSUM_NAME		"RXCSUM"
+#define	IFCAP_TXCSUM_NAME		"TXCSUM"
+#define	IFCAP_NETCONS_NAME		"NETCONS"
+#define	IFCAP_VLAN_MTU_NAME		"VLAN_MTU"
+#define	IFCAP_VLAN_HWTAGGING_NAME 	"VLAN_HWTAGGING"
+#define	IFCAP_JUMBO_MTU_NAME		"JUMBO_MTU"
+#define	IFCAP_POLLING_NAME		"POLLING"
+#define	IFCAP_VLAN_HWCSUM_NAME		"VLAN_HWCSUM"
+#define	IFCAP_TSO4_NAME			"TSO4"
+#define	IFCAP_TSO6_NAME			"TSO6"
+#define	IFCAP_LRO_NAME			"LRO"
+#define	IFCAP_WOL_UCAST_NAME		"WOL_UCAST"
+#define	IFCAP_WOL_MCAST_NAME		"WOL_MCAST"
+#define	IFCAP_WOL_MAGIC_NAME		"WOL_MAGIC"
+#define	IFCAP_TOE4_NAME			"TOE4"
+#define	IFCAP_TOE6_NAME			"TOE6"
+#define	IFCAP_VLAN_HWFILTER_NAME 	"VLAN_HWFILTER"
+#define	IFCAP_NV_NAME			"NV"
+#define	IFCAP_VLAN_HWTSO_NAME		"VLAN_HWTSO"
+#define	IFCAP_LINKSTATE_NAME		"LINKSTATE"
+#define	IFCAP_NETMAP_NAME		"NETMAP"
+#define	IFCAP_RXCSUM_IPV6_NAME		"RXCSUM_IPV6"
+#define	IFCAP_TXCSUM_IPV6_NAME		"TXCSUM_IPV6"
+#define	IFCAP_HWSTATS_NAME		"HWSTATS"
+#define	IFCAP_TXRTLMT_NAME		"TXRTLMT"
+#define	IFCAP_HWRXTSTMP_NAME		"HWRXTSTMP"
+#define	IFCAP_MEXTPG_NAME		"MEXTPG"
+#define	IFCAP_TXTLS4_NAME		"TXTLS4"
+#define	IFCAP_TXTLS6_NAME		"TXTLS6"
+#define	IFCAP_VXLAN_HWCSUM_NAME		"VXLAN_HWCSUM"
+#define	IFCAP_VXLAN_HWTSO_NAME		"VXLAN_HWTSO"
+#define	IFCAP_TXTLS_RTLMT_NAME		"TXTLS_RTLMT"
+#define	IFCAP_RXTLS4_NAME		"RXTLS4"
+#define	IFCAP_RXTLS6_NAME		"RXTLS6"
+
+#define	IFCAP2_RXTLS4_NAME	IFCAP_RXTLS4_NAME
+#define	IFCAP2_RXTLS6_NAME	IFCAP_RXTLS6_NAME
+
+static const char *ifcap_bit_names[] = {
+	IFCAP_RXCSUM_NAME,
+	IFCAP_TXCSUM_NAME,
+	IFCAP_NETCONS_NAME,
+	IFCAP_VLAN_MTU_NAME,
+	IFCAP_VLAN_HWTAGGING_NAME,
+	IFCAP_JUMBO_MTU_NAME,
+	IFCAP_POLLING_NAME,
+	IFCAP_VLAN_HWCSUM_NAME,
+	IFCAP_TSO4_NAME,
+	IFCAP_TSO6_NAME,
+	IFCAP_LRO_NAME,
+	IFCAP_WOL_UCAST_NAME,
+	IFCAP_WOL_MCAST_NAME,
+	IFCAP_WOL_MAGIC_NAME,
+	IFCAP_TOE4_NAME,
+	IFCAP_TOE6_NAME,
+	IFCAP_VLAN_HWFILTER_NAME,
+	IFCAP_NV_NAME,
+	IFCAP_VLAN_HWTSO_NAME,
+	IFCAP_LINKSTATE_NAME,
+	IFCAP_NETMAP_NAME,
+	IFCAP_RXCSUM_IPV6_NAME,
+	IFCAP_TXCSUM_IPV6_NAME,
+	IFCAP_HWSTATS_NAME,
+	IFCAP_TXRTLMT_NAME,
+	IFCAP_HWRXTSTMP_NAME,
+	IFCAP_MEXTPG_NAME,
+	IFCAP_TXTLS4_NAME,
+	IFCAP_TXTLS6_NAME,
+	IFCAP_VXLAN_HWCSUM_NAME,
+	IFCAP_VXLAN_HWTSO_NAME,
+	IFCAP_TXTLS_RTLMT_NAME,
+	IFCAP_RXTLS4_NAME,
+	IFCAP_RXTLS6_NAME,
+};
+_Static_assert(sizeof(ifcap_bit_names) >= IFCAP_B_SIZE * sizeof(char *),
+    "ifcap bit names missing from ifcap_bit_names");
+
+#endif
diff --git a/sys/netlink/netlink_bitset.h b/sys/netlink/netlink_bitset.h
new file mode 100644
index 000000000000..9a918bd20997
--- /dev/null
+++ b/sys/netlink/netlink_bitset.h
@@ -0,0 +1,57 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Alexander V. Chernikov <melifaro@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Generic netlink message header and attributes
+ */
+#ifndef _NETLINK_NETLINK_BITSET_H_
+#define	_NETLINK_NETLINK_BITSET_H_
+
+#include <netlink/netlink.h>
+
+/* Bitset type nested attributes */
+enum {
+	NLA_BITSET_UNSPEC,
+	NLA_BITSET_NOMASK	= 1, /* flag: mask of valid bits not provided */
+	NLA_BITSET_SIZE		= 2, /* u32: max valid bit # */
+	NLA_BITSET_BITS		= 3, /* nested: array of NLA_BITSET_BIT */
+	NLA_BITSET_VALUE	= 4, /* binary: array of bit values */
+	NLA_BITSET_MASK		= 5, /* binary: array of valid bits */
+	__NLA_BITSET_MAX,
+};
+#define	NLA_BITSET_MAX	(__NLA_BITSET_MAX - 1)
+
+enum {
+	NLA_BITSET_BIT_UNSPEC,
+	NLA_BITSET_BIT_INDEX	= 1, /* u32: index of the bit */
+	NLA_BITSET_BIT_NAME	= 2, /* string: bit description */
+	NLA_BITSET_BIT_VALUE	= 3, /* flag: provided if bit is set */
+	__NLA_BITSET_BIT_MAX,
+};
+#define	NLA_BITSET_BIT_MAX	(__NLA_BITSET_BIT_MAX - 1)
+
+#endif
diff --git a/sys/netlink/netlink_route.h b/sys/netlink/netlink_route.h
index 0c328d941bd5..ecdad83312de 100644
--- a/sys/netlink/netlink_route.h
+++ b/sys/netlink/netlink_route.h
@@ -33,6 +33,7 @@
 #include <net/if_types.h>
 #include <net/if_var.h>
 
+#include <netlink/netlink_bitset.h>
 #include <netlink/route/common.h>
 #include <netlink/route/ifaddrs.h>
 #include <netlink/route/interface.h>
diff --git a/sys/netlink/netlink_snl.h b/sys/netlink/netlink_snl.h
index 6d97c1afd4ee..0292725bd135 100644
--- a/sys/netlink/netlink_snl.h
+++ b/sys/netlink/netlink_snl.h
@@ -43,6 +43,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netlink/netlink.h>
+#include <netlink/netlink_bitset.h>
 
 #define _roundup2(x, y)         (((x)+((y)-1))&(~((y)-1)))
 
@@ -732,7 +733,7 @@ snl_attr_get_nla(struct snl_state *ss __unused, struct nlattr *nla,
 }
 
 static inline bool
-snl_attr_dup_nla(struct snl_state *ss __unused, struct nlattr *nla,
+snl_attr_dup_nla(struct snl_state *ss, struct nlattr *nla,
     const void *arg __unused, void *target)
 {
 	void *ptr = snl_allocz(ss, nla->nla_len);
@@ -773,6 +774,90 @@ snl_attr_dup_struct(struct snl_state *ss, struct nlattr *nla,
 	return (false);
 }
 
+struct snl_attr_bit {
+	uint32_t	bit_index;
+	char		*bit_name;
+	int		bit_value;
+};
+
+struct snl_attr_bits {
+	uint32_t num_bits;
+	struct snl_attr_bit **bits;
+};
+
+#define	_OUT(_field)	offsetof(struct snl_attr_bit, _field)
+static const struct snl_attr_parser _nla_p_bit[] = {
+	{ .type = NLA_BITSET_BIT_INDEX, .off = _OUT(bit_index), .cb = snl_attr_get_uint32 },
+	{ .type = NLA_BITSET_BIT_NAME, .off = _OUT(bit_name), .cb = snl_attr_dup_string },
+	{ .type = NLA_BITSET_BIT_VALUE, .off = _OUT(bit_value), .cb = snl_attr_get_flag },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER_EXT(_nla_bit_parser, sizeof(struct snl_attr_bit), _nla_p_bit, NULL);
+
+struct snl_attr_bitset {
+	uint32_t		nla_bitset_size;
+	uint32_t		*nla_bitset_mask;
+	uint32_t		*nla_bitset_value;
+	struct snl_attr_bits	bits;
+};
+
+#define	_OUT(_field)	offsetof(struct snl_attr_bitset, _field)
+static const struct snl_attr_parser _nla_p_bitset[] = {
+	{ .type = NLA_BITSET_SIZE, .off = _OUT(nla_bitset_size), .cb = snl_attr_get_uint32 },
+	{ .type = NLA_BITSET_BITS, .off = _OUT(bits), .cb = snl_attr_get_parray, .arg = &_nla_bit_parser },
+	{ .type = NLA_BITSET_VALUE, .off = _OUT(nla_bitset_mask), .cb = snl_attr_dup_nla },
+	{ .type = NLA_BITSET_MASK, .off = _OUT(nla_bitset_value), .cb = snl_attr_dup_nla },
+};
+
+static inline bool
+_cb_p_bitset(struct snl_state *ss __unused, void *_target)
+{
+	struct snl_attr_bitset *target = _target;
+
+	uint32_t sz_bytes = _roundup2(target->nla_bitset_size, 32) / 8;
+
+	if (target->nla_bitset_mask != NULL) {
+		struct nlattr *nla = (struct nlattr *)target->nla_bitset_mask;
+		uint32_t data_len = NLA_DATA_LEN(nla);
+
+		if (data_len != sz_bytes || _roundup2(data_len, 4) != data_len)
+			return (false);
+		target->nla_bitset_mask = (uint32_t *)NLA_DATA(nla);
+	}
+
+	if (target->nla_bitset_value != NULL) {
+		struct nlattr *nla = (struct nlattr *)target->nla_bitset_value;
+		uint32_t data_len = NLA_DATA_LEN(nla);
+
+		if (data_len != sz_bytes || _roundup2(data_len, 4) != data_len)
+			return (false);
+		target->nla_bitset_value = (uint32_t *)NLA_DATA(nla);
+	}
+	return (true);
+}
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER_EXT(_nla_bitset_parser,
+		sizeof(struct snl_attr_bitset),
+		_nla_p_bitset, _cb_p_bitset);
+
+/*
+ * Parses the compact bitset representation.
+ */
+static inline bool
+snl_attr_get_bitset_c(struct snl_state *ss, struct nlattr *nla,
+    const void *arg __unused, void *_target)
+{
+	const struct snl_hdr_parser *p = &_nla_bitset_parser;
+	struct snl_attr_bitset *target = _target;
+
+	/* Assumes target points to the beginning of the structure */
+	if (!snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), p, _target))
+		return (false);
+	if (target->nla_bitset_mask == NULL || target->nla_bitset_value == NULL)
+		return (false);
+	return (true);
+}
+
 static inline void
 snl_field_get_uint8(struct snl_state *ss __unused, void *src, void *target)
 {
@@ -1184,6 +1269,7 @@ snl_send_msgs(struct snl_writer *nw)
 
 static const struct snl_hdr_parser *snl_all_core_parsers[] = {
 	&snl_errmsg_parser, &snl_donemsg_parser,
+	&_nla_bit_parser, &_nla_bitset_parser,
 };
 
 #endif
diff --git a/sys/netlink/netlink_snl_route_parsers.h b/sys/netlink/netlink_snl_route_parsers.h
index 1e9320a4559e..7e4bcad4010b 100644
--- a/sys/netlink/netlink_snl_route_parsers.h
+++ b/sys/netlink/netlink_snl_route_parsers.h
@@ -186,12 +186,14 @@ struct snl_parsed_link {
 	uint32_t			ifla_promiscuity;
 	struct rtnl_link_stats64	*ifla_stats64;
 	struct nlattr			*iflaf_orig_hwaddr;
+	struct snl_attr_bitset		iflaf_caps;
 };
 
 #define	_IN(_field)	offsetof(struct ifinfomsg, _field)
 #define	_OUT(_field)	offsetof(struct snl_parsed_link, _field)
 static const struct snl_attr_parser _nla_p_link_fbsd[] = {
 	{ .type = IFLAF_ORIG_HWADDR, .off = _OUT(iflaf_orig_hwaddr), .cb = snl_attr_dup_nla },
+	{ .type = IFLAF_CAPS, .off = _OUT(iflaf_caps), .cb = snl_attr_get_bitset_c },
 };
 SNL_DECLARE_ATTR_PARSER(_link_fbsd_parser, _nla_p_link_fbsd);
 
diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c
index 16bbe4d000cc..3d7f752fa613 100644
--- a/sys/netlink/route/iface.c
+++ b/sys/netlink/route/iface.c
@@ -253,6 +253,33 @@ dump_sa(struct nl_writer *nw, int attr, const struct sockaddr *sa)
         return (nlattr_add(nw, attr, addr_len, addr_data));
 }
 
+static bool
+dump_iface_caps(struct nl_writer *nw, struct ifnet *ifp)
+{
+	int off = nlattr_add_nested(nw, IFLAF_CAPS);
+	uint32_t active_caps[roundup2(IFCAP_B_SIZE, 32) / 32] = {};
+	uint32_t all_caps[roundup2(IFCAP_B_SIZE, 32) / 32] = {};
+
+	MPASS(sizeof(active_caps) >= 8);
+	MPASS(sizeof(all_caps) >= 8);
+
+	if (off == 0)
+		return (false);
+
+	active_caps[0] = (uint32_t)if_getcapabilities(ifp);
+	all_caps[0] = (uint32_t)if_getcapenable(ifp);
+	active_caps[1] = (uint32_t)if_getcapabilities2(ifp);
+	all_caps[1] = (uint32_t)if_getcapenable2(ifp);
+
+	nlattr_add_u32(nw, NLA_BITSET_SIZE, IFCAP_B_SIZE);
+	nlattr_add(nw, NLA_BITSET_MASK, sizeof(all_caps), all_caps);
+	nlattr_add(nw, NLA_BITSET_VALUE, sizeof(active_caps), active_caps);
+
+	nlattr_set_len(nw, off);
+
+	return (true);
+}
+
 /*
  * Dumps interface state, properties and metrics.
  * @nw: message writer
@@ -320,6 +347,7 @@ dump_iface(struct nl_writer *nw, struct ifnet *ifp, const struct nlmsghdr *hdr,
 	int off = nlattr_add_nested(nw, IFLA_FREEBSD);
 	if (off != 0) {
 		get_hwaddr(nw, ifp);
+		dump_iface_caps(nw, ifp);
 
 		nlattr_set_len(nw, off);
 	}
diff --git a/sys/netlink/route/interface.h b/sys/netlink/route/interface.h
index 97270d8234a1..667bf2c96151 100644
--- a/sys/netlink/route/interface.h
+++ b/sys/netlink/route/interface.h
@@ -151,6 +151,7 @@ enum {
 	IFLAF_UNSPEC		= 0,
 	IFLAF_ORIG_IFNAME	= 1,	/* string, original interface name at creation */
 	IFLAF_ORIG_HWADDR	= 2,	/* binary, original hardware address */
+	IFLAF_CAPS		= 3,	/* bitset, interface capabilities */
 	__IFLAF_MAX
 };
 #define IFLAF_MAX (__IFLAF_MAX - 1)