aboutsummaryrefslogtreecommitdiff
path: root/sys/netlink
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netlink')
-rw-r--r--sys/netlink/netlink_bitset.h57
-rw-r--r--sys/netlink/netlink_route.h1
-rw-r--r--sys/netlink/netlink_snl.h88
-rw-r--r--sys/netlink/netlink_snl_route_parsers.h2
-rw-r--r--sys/netlink/route/iface.c28
-rw-r--r--sys/netlink/route/interface.h1
6 files changed, 176 insertions, 1 deletions
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)