From nobody Wed Aug 19 23:46:29 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 4hQNWb6CpLz6nf3c for ; Wed, 19 Aug 2026 23:46:35 +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 4hQNWb49Mrz43pC for ; Wed, 19 Aug 2026 23:46:35 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1787183195; 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=L8gKdWh5p3DkNT19SrXiKmF3U9HB5yE5ZuI60tszrRE=; b=TvonkLIgJniNZATw5yEupQAy6xZchu3dzvfCZAU7gPIiE8R/2vw4D5GulpBSbDxhR4zSGv V/tffRZwtnrjUJ5HQ+lL/0C1CuSHF5j4X6p3JuaM0m8WaclmYIXnIlZVFMxizNFxNzMO7w +OIYz+YNhYU1sz4hZn4HqZJSkzuQJNWsKT+bQscOcb3W/uRgrdB96v3Uo2eDi9MtjDkWXR xeyKC1Bwr2SmKA8QAsACKSR7kP1eBnPyky4CeSxxdvtJfZDJvFrEOcWVnVLiJJTzRj18E7 4yGh+DSJVyipBHZVa9MGKUwS2EjPmQdCLgoe50iXUvSbSzlLWrVjF5xnEnKuuA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1787183195; a=rsa-sha256; cv=none; b=cojGeYaKo93uarWwN9xjp1v75TIhd7J8Igdt8+iu9u0xlPFDHOLKqBBjprGoqrbAS7wiM9 JalXCq5ddAxU3TfXNI+Zn//MtQAgs0qM07qlzb/4D9C96woUtwGbAevp4dvLTx6eBz5jVc wGwQY6K/SnvvtZrqXxGG9nwGy9UsA+G7dGcZYg4SBxFoCpq/dBqVstVtarr5NmLRjAHE78 0loYSAgDMJRiMQSA6Ulc8j3gRHtxGdEzZH+U/ilBbxt6RzCtZAUzwm4ffTf3nQS16YS9RP diFAG6V9DSlUq89T2SMCUmXj6kQLEYgDcISiHlSJU/WLWAvz05ZEjYxKWC5Elw== 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=1787183195; 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=L8gKdWh5p3DkNT19SrXiKmF3U9HB5yE5ZuI60tszrRE=; b=rR64gzLsWnzcpFLsLgS+C/IwdgU4A88oDOJ+b+gsBe7O8eYiOjXpzPv0EkyL870zoaEcyi NzENrA87mCJJFhbjBF8AsmkHiaV7K7u1jdQCnbV10NRF7jbmjGDkp9X9IMQg1gFeqwim0v ZzUgGnqQ+UNY8SzQAa7aCBeR8t7eHzYu4T9FSICXa16cl44So8MX+UkHYSS9KzCDfflSy8 EtXgy89DGbaJ1LM4sYkf2rMssur87+kTyOMDkD3RaExFrNfKMJLQ+MVw6Djn8WOSdfY5Yq 7gkW3gpVLIaKPsYZJUlkXqB3DagYC4ezySPNeaV2yBQyP8mNUzoIGqnY8vDFkg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hQNWZ5JbKzslh for ; Wed, 19 Aug 2026 23:46:34 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 419c1 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Wed, 19 Aug 2026 23:46:29 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kevin Bowling Subject: git: 60c0a075472c - main - netlink: Parse repeated nested attributes 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: 60c0a075472cabd0ac7f54411f6a65626169984a Auto-Submitted: auto-generated Date: Wed, 19 Aug 2026 23:46:29 +0000 Message-Id: <6a864055.419c1.41f86686@gitrepo.freebsd.org> The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=60c0a075472cabd0ac7f54411f6a65626169984a commit 60c0a075472cabd0ac7f54411f6a65626169984a Author: Kevin Bowling AuthorDate: 2026-08-10 21:34:12 +0000 Commit: Kevin Bowling CommitDate: 2026-08-19 23:44:55 +0000 netlink: Parse repeated nested attributes Modern Netlink arrays encode their elements as repeated attributes of the same type rather than as children of an additional array container. Add an SNL callback that parses one nested element for each occurrence and appends it to a geometrically grown parser array. Retain the existing parray callback for protocols that use the legacy container form. Store the growth capacity in struct snl_parray, appended after its existing public count and items fields so their offsets remain stable on LP64 and ILP32. Require parser targets to be real snl_parray objects, and convert bitset, generic Netlink, and route multipath arrays accordingly. This avoids relying on layout aliases for private growth state. Add regression coverage for a nested bit array that grows beyond its initial allocation, while preserving replacement semantics when a legacy array target is reused. Reviewed by: melifaro, pouria Sponsored by: BBOX.io Differential Revision: https://reviews.freebsd.org/D58775 --- sbin/route/route_netlink.c | 22 +++---- sys/netlink/netlink_snl.h | 106 +++++++++++++++++++++++--------- sys/netlink/netlink_snl_generic.h | 18 +++--- sys/netlink/netlink_snl_route_parsers.h | 11 ++-- tests/sys/netlink/test_rtnl_route.c | 10 +-- tests/sys/netlink/test_snl.c | 65 ++++++++++++++++++++ tests/sys/netlink/test_snl_generic.c | 6 +- usr.bin/genl/genl.c | 59 +++++++++--------- usr.bin/netstat/route_netlink.c | 8 +-- usr.sbin/arp/arp_netlink.c | 3 +- usr.sbin/ndp/ndp_netlink.c | 3 +- 11 files changed, 211 insertions(+), 100 deletions(-) diff --git a/sbin/route/route_netlink.c b/sbin/route/route_netlink.c index f145771ebb12..ce67cc71c3e8 100644 --- a/sbin/route/route_netlink.c +++ b/sbin/route/route_netlink.c @@ -428,11 +428,11 @@ print_nhop_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct sockaddr *ds printf(" fib: %u\n", (unsigned int)r.rta_table); printf(" flags: "); printb(r.rta_rtflags, routeflags); - printf("\n nhops: %u\n", r.rta_multipath.num_nhops); - if (r.rta_multipath.num_nhops != 0) { + printf("\n nhops: %u\n", r.rta_multipath.count); + if (r.rta_multipath.count != 0) { bool first = true; - for (uint32_t i = 0; i < r.rta_multipath.num_nhops; i++) { - struct rta_mpath_nh *nh = r.rta_multipath.nhops[i]; + for (uint32_t i = 0; i < r.rta_multipath.count; i++) { + struct rta_mpath_nh *nh = r.rta_multipath.items[i]; printf("\tvia "); print_nlmsg_route_nhop(h, &r, nh, first); @@ -601,14 +601,14 @@ print_nlmsg_route(struct nl_helper *h, struct nlmsghdr *hdr, return; } - if (r.rta_multipath.num_nhops != 0) { + if (r.rta_multipath.count != 0) { bool first = true; memset(buf, ' ', sizeof(buf)); buf[len] = '\0'; - for (uint32_t i = 0; i < r.rta_multipath.num_nhops; i++) { - struct rta_mpath_nh *nh = r.rta_multipath.nhops[i]; + for (uint32_t i = 0; i < r.rta_multipath.count; i++) { + struct rta_mpath_nh *nh = r.rta_multipath.items[i]; if (!first) printf("%s", buf); @@ -903,9 +903,10 @@ flushroute_one(struct nl_helper *h, struct snl_parsed_route *r) print_nlmsg(h, hdr, &attrs); } else { - if (r->rta_multipath.num_nhops != 0) { - for (uint32_t i = 0; i < r->rta_multipath.num_nhops; i++) { - struct rta_mpath_nh *nh = r->rta_multipath.nhops[i]; + if (r->rta_multipath.count != 0) { + for (uint32_t i = 0; i < r->rta_multipath.count; i++) { + struct rta_mpath_nh *nh = + r->rta_multipath.items[i]; print_flushed_route(r, nh->gw); } @@ -975,4 +976,3 @@ flushroutes_fib_nl(int fib, int af) return (e.error); } - diff --git a/sys/netlink/netlink_snl.h b/sys/netlink/netlink_snl.h index 34a0f3791af2..ecc980387fa8 100644 --- a/sys/netlink/netlink_snl.h +++ b/sys/netlink/netlink_snl.h @@ -724,23 +724,60 @@ snl_attr_get_nested(struct snl_state *ss, struct nlattr *nla, const void *arg, v struct snl_parray { uint32_t count; void **items; + uint32_t _capacity; }; +static inline bool +snl_parray_append(struct snl_state *ss, struct snl_parray *array, void *item, + uint32_t start_size) +{ + void **new_items; + size_t alloc_size; + uint32_t new_size; + + if (start_size == 0) + return (false); + if (array->_capacity == 0) { + new_size = start_size; + if (__builtin_mul_overflow((size_t)new_size, + sizeof(*new_items), &alloc_size)) + return (false); + array->items = (void **)snl_allocz(ss, alloc_size); + if (array->items == NULL) + return (false); + array->_capacity = new_size; + } else if (array->count == array->_capacity) { + if (array->_capacity > UINT32_MAX / 2) + return (false); + new_size = array->_capacity * 2; + if (__builtin_mul_overflow((size_t)new_size, + sizeof(*new_items), &alloc_size)) + return (false); + new_items = (void **)snl_allocz(ss, alloc_size); + if (new_items == NULL) + return (false); + memcpy(new_items, array->items, + array->count * sizeof(*new_items)); + array->items = new_items; + array->_capacity = new_size; + } + array->items[array->count++] = item; + return (true); +} + static inline bool snl_attr_get_parray_sz(struct snl_state *ss, struct nlattr *container_nla, - uint32_t start_size, const void *arg, void *target) + uint32_t start_size, const void *arg, struct snl_parray *array) { const struct snl_hdr_parser *p = (const struct snl_hdr_parser *)arg; - struct snl_parray *array = (struct snl_parray *)target; struct nlattr *nla; - uint32_t count = 0, size = start_size; - if (p->out_size == 0) - return (false); - - array->items = (void **)snl_allocz(ss, size * sizeof(void *)); - if (array->items == NULL) + if (p->out_size == 0 || start_size == 0) return (false); + /* A container attribute replaces, rather than extends, its output. */ + array->count = 0; + array->items = NULL; + array->_capacity = 0; /* * If the provided parser is an attribute parser, assume that each @@ -766,31 +803,49 @@ snl_attr_get_parray_sz(struct snl_state *ss, struct nlattr *container_nla, if (!(snl_parse_header(ss, data, data_len, p, item))) return (false); - if (count == size) { - uint32_t new_size = size * 2; - void **new_array = (void **)snl_allocz(ss, new_size *sizeof(void *)); - - memcpy(new_array, array->items, size * sizeof(void *)); - array->items = new_array; - size = new_size; - } - array->items[count++] = item; + if (!snl_parray_append(ss, array, item, start_size)) + return (false); } - array->count = count; return (true); } /* * Parses and stores the unknown-size array. - * Assumes each array item is a container and the NLAs in the container are parsable - * by the parser provided in @arg. - * Assumes @target is struct snl_parray + * Assumes each array item is a container and the NLAs in the container are + * parsable by the parser provided in @arg. + * @target must point to an actual struct snl_parray. Do not substitute a + * layout-compatible structure: the parser array includes private growth state. */ static inline bool snl_attr_get_parray(struct snl_state *ss, struct nlattr *nla, const void *arg, void *target) { - return (snl_attr_get_parray_sz(ss, nla, 8, arg, target)); + return (snl_attr_get_parray_sz(ss, nla, 8, arg, + (struct snl_parray *)target)); +} + +/* + * Append one repeated nested attribute to an array. Unlike + * snl_attr_get_parray(), the attribute itself is one array member rather + * than a container holding an entire array. Use this for modern Netlink + * multi-attributes. @target must point to an actual struct snl_parray. + */ +static inline bool +snl_attr_get_multi(struct snl_state *ss, struct nlattr *nla, const void *arg, + void *target) +{ + const struct snl_hdr_parser *p = arg; + struct snl_parray *array = target; + void *item; + + if (p->out_size == 0) + return (false); + item = snl_allocz(ss, p->out_size); + if (item == NULL) + return (false); + if (!snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), p, item)) + return (false); + return (snl_parray_append(ss, array, item, 8)); } static inline bool @@ -849,11 +904,6 @@ struct snl_attr_bit { 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 }, @@ -867,7 +917,7 @@ struct snl_attr_bitset { uint32_t nla_bitset_size; uint32_t *nla_bitset_mask; uint32_t *nla_bitset_value; - struct snl_attr_bits bits; + struct snl_parray bits; }; #define _OUT(_field) offsetof(struct snl_attr_bitset, _field) diff --git a/sys/netlink/netlink_snl_generic.h b/sys/netlink/netlink_snl_generic.h index 10e98a0266e0..8a8e99813086 100644 --- a/sys/netlink/netlink_snl_generic.h +++ b/sys/netlink/netlink_snl_generic.h @@ -65,11 +65,6 @@ struct _snl_genl_ctrl_mcast_group { const char *mcast_grp_name; }; -struct _snl_genl_ctrl_mcast_groups { - uint32_t num_groups; - struct _snl_genl_ctrl_mcast_group **groups; -}; - #define _OUT(_field) offsetof(struct _snl_genl_ctrl_mcast_group, _field) static struct snl_attr_parser _nla_p_getmc[] = { { @@ -90,7 +85,7 @@ SNL_DECLARE_ATTR_PARSER_EXT(_genl_ctrl_mc_parser, struct _getfamily_attrs { uint16_t family_id; const char *family_name; - struct _snl_genl_ctrl_mcast_groups mcast_groups; + struct snl_parray mcast_groups; }; #define _IN(_field) offsetof(struct genlmsghdr, _field) @@ -165,10 +160,13 @@ snl_get_genl_mcast_group(struct snl_state *ss, const char *family_name, return (0); if (family_id != NULL) *family_id = attrs.family_id; - for (u_int i = 0; i < attrs.mcast_groups.num_groups; i++) - if (strcmp(attrs.mcast_groups.groups[i]->mcast_grp_name, - group_name) == 0) - return (attrs.mcast_groups.groups[i]->mcast_grp_id); + for (u_int i = 0; i < attrs.mcast_groups.count; i++) { + struct _snl_genl_ctrl_mcast_group *group; + + group = attrs.mcast_groups.items[i]; + if (strcmp(group->mcast_grp_name, group_name) == 0) + return (group->mcast_grp_id); + } return (0); } diff --git a/sys/netlink/netlink_snl_route_parsers.h b/sys/netlink/netlink_snl_route_parsers.h index f0849884ec97..10698ca987f3 100644 --- a/sys/netlink/netlink_snl_route_parsers.h +++ b/sys/netlink/netlink_snl_route_parsers.h @@ -93,21 +93,18 @@ SNL_DECLARE_PARSER_EXT(_mpath_nh_parser, sizeof(struct rtnexthop), sizeof(struct rta_mpath_nh), _fp_p_mp_nh, _nla_p_mp_nh, _cb_p_mp_nh); -struct rta_mpath { - uint32_t num_nhops; - struct rta_mpath_nh **nhops; -}; - static bool nlattr_get_multipath(struct snl_state *ss, struct nlattr *nla, const void *arg __unused, void *target) { + struct snl_parray *mpath = target; uint32_t start_size = 4; while (start_size < NLA_DATA_LEN(nla) / sizeof(struct rtnexthop)) start_size *= 2; - return (snl_attr_get_parray_sz(ss, nla, start_size, &_mpath_nh_parser, target)); + return (snl_attr_get_parray_sz(ss, nla, start_size, + &_mpath_nh_parser, mpath)); } struct snl_parsed_route { @@ -115,7 +112,7 @@ struct snl_parsed_route { struct sockaddr *rta_gw; struct sockaddr *rta_pref_src; struct nlattr *rta_metrics; - struct rta_mpath rta_multipath; + struct snl_parray rta_multipath; uint32_t rta_oif; uint32_t rta_expire; uint32_t rta_table; diff --git a/tests/sys/netlink/test_rtnl_route.c b/tests/sys/netlink/test_rtnl_route.c index 334d1fea9fe9..9a1bf0d3b117 100644 --- a/tests/sys/netlink/test_rtnl_route.c +++ b/tests/sys/netlink/test_rtnl_route.c @@ -129,7 +129,7 @@ ATF_TC_BODY(rtnl_nhgrp, tc) ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL); ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r)); ATF_CHECK(r.rta_knh_id != 0); - ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 2); + ATF_CHECK_INTEQ(r.rta_multipath.count, 2); cleanup_route_by_dst(&ss, &nw, "192.0.2.0"); } @@ -179,7 +179,7 @@ ATF_TC_BODY(rtnl_nhop_merge, tc) ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL); ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r)); ATF_CHECK(r.rta_knh_id != 0); - ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 0); + ATF_CHECK_INTEQ(r.rta_multipath.count, 0); /* Append anoher nhop */ snl_init_writer(&ss, &nw); @@ -202,7 +202,7 @@ ATF_TC_BODY(rtnl_nhop_merge, tc) ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL); ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r)); ATF_CHECK(r.rta_knh_id != 0); - ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 2); + ATF_CHECK_INTEQ(r.rta_multipath.count, 2); cleanup_route_by_dst(&ss, &nw, "198.51.100.0"); } @@ -293,7 +293,7 @@ ATF_TC_BODY(rtnl_nhgrp_expire, tc) ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL); ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r)); ATF_CHECK(r.rta_knh_id != 0); - ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 3); + ATF_CHECK_INTEQ(r.rta_multipath.count, 3); /* wait for 2 seconds and try again */ sleep(2); @@ -306,7 +306,7 @@ ATF_TC_BODY(rtnl_nhgrp_expire, tc) ATF_REQUIRE(snl_send_message(&ss, hdr)); ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL); ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &snl_rtm_route_parser, &r)); - ATF_CHECK_INTEQ(r.rta_multipath.num_nhops, 2); + ATF_CHECK_INTEQ(r.rta_multipath.count, 2); cleanup_route_by_dst(&ss, &nw, "203.0.113.0"); } diff --git a/tests/sys/netlink/test_snl.c b/tests/sys/netlink/test_snl.c index c2bbc4aff949..15160367299a 100644 --- a/tests/sys/netlink/test_snl.c +++ b/tests/sys/netlink/test_snl.c @@ -37,6 +37,70 @@ ATF_TC_BODY(snl_verify_core_parsers, tc) } +ATF_TC(snl_parse_bitset_array); +ATF_TC_HEAD(snl_parse_bitset_array, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Tests snl(3) parsing a growing nested bit array"); + atf_tc_set_md_var(tc, "require.kmods", "netlink"); +} + +ATF_TC_BODY(snl_parse_bitset_array, tc) +{ + struct snl_attr_bit *bit; + struct snl_parsed_link link = {}; + struct snl_state ss; + struct snl_writer nw; + struct nlmsghdr *hdr; + uint32_t mask, value; + char name[16]; + int bits_off, entry_off, fbsd_off, caps_off; + + ATF_REQUIRE(snl_init(&ss, NETLINK_ROUTE)); + snl_init_writer(&ss, &nw); + hdr = snl_create_msg_request(&nw, RTM_NEWLINK); + ATF_REQUIRE(hdr != NULL); + ATF_REQUIRE(snl_reserve_msg_object(&nw, struct ifinfomsg) != NULL); + + fbsd_off = snl_add_msg_attr_nested(&nw, IFLA_FREEBSD); + ATF_REQUIRE(fbsd_off != 0); + caps_off = snl_add_msg_attr_nested(&nw, IFLAF_CAPS); + ATF_REQUIRE(caps_off != 0); + ATF_REQUIRE(snl_add_msg_attr_u32(&nw, NLA_BITSET_SIZE, 32)); + mask = 0x1ff; + value = 0x155; + ATF_REQUIRE(snl_add_msg_attr(&nw, NLA_BITSET_MASK, sizeof(mask), + &mask)); + ATF_REQUIRE(snl_add_msg_attr(&nw, NLA_BITSET_VALUE, sizeof(value), + &value)); + bits_off = snl_add_msg_attr_nested(&nw, NLA_BITSET_BITS); + ATF_REQUIRE(bits_off != 0); + for (uint32_t i = 0; i < 9; i++) { + entry_off = snl_add_msg_attr_nested(&nw, i + 1); + ATF_REQUIRE(entry_off != 0); + ATF_REQUIRE(snl_add_msg_attr_u32(&nw, + NLA_BITSET_BIT_INDEX, i)); + snprintf(name, sizeof(name), "bit-%u", i); + ATF_REQUIRE(snl_add_msg_attr_string(&nw, + NLA_BITSET_BIT_NAME, name)); + if ((i & 1) == 0) + ATF_REQUIRE(snl_add_msg_attr_flag(&nw, + NLA_BITSET_BIT_VALUE)); + snl_end_attr_nested(&nw, entry_off); + } + snl_end_attr_nested(&nw, bits_off); + snl_end_attr_nested(&nw, caps_off); + snl_end_attr_nested(&nw, fbsd_off); + hdr = snl_finalize_msg(&nw); + ATF_REQUIRE(hdr != NULL); + + ATF_REQUIRE(snl_parse_nlmsg(&ss, hdr, &snl_rtm_link_parser, &link)); + ATF_REQUIRE_EQ(link.iflaf_caps.bits.count, 9); + bit = link.iflaf_caps.bits.items[8]; + ATF_CHECK_EQ(bit->bit_index, 8); + ATF_CHECK_STREQ(bit->bit_name, "bit-8"); + ATF_CHECK_EQ(bit->bit_value, 1); +} ATF_TC(snl_verify_route_parsers); ATF_TC_HEAD(snl_verify_route_parsers, tc) { @@ -230,6 +294,7 @@ ATF_TC_BODY(snl_list_ifaces, tc) ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, snl_verify_core_parsers); + ATF_TP_ADD_TC(tp, snl_parse_bitset_array); ATF_TP_ADD_TC(tp, snl_verify_route_parsers); ATF_TP_ADD_TC(tp, snl_parse_errmsg_capped); ATF_TP_ADD_TC(tp, snl_parse_errmsg_capped_extack); diff --git a/tests/sys/netlink/test_snl_generic.c b/tests/sys/netlink/test_snl_generic.c index 8613bf04a45c..67515c006d37 100644 --- a/tests/sys/netlink/test_snl_generic.c +++ b/tests/sys/netlink/test_snl_generic.c @@ -90,9 +90,10 @@ ATF_TC_BODY(test_snl_get_genl_family_groups, tc) struct _getfamily_attrs attrs = {}; ATF_CHECK(snl_parse_nlmsg(&ss, hdr, &_genl_ctrl_getfam_parser, &attrs)); - ATF_CHECK_EQ(attrs.mcast_groups.num_groups, 1); + ATF_CHECK_EQ(attrs.mcast_groups.count, 1); - struct _snl_genl_ctrl_mcast_group *group = attrs.mcast_groups.groups[0]; + struct _snl_genl_ctrl_mcast_group *group = + attrs.mcast_groups.items[0]; ATF_CHECK(group->mcast_grp_id > 0); ATF_CHECK(!strcmp(group->mcast_grp_name, "notify")); @@ -107,4 +108,3 @@ ATF_TP_ADD_TCS(tp) return (atf_no_error()); } - diff --git a/usr.bin/genl/genl.c b/usr.bin/genl/genl.c index 4d2c252dab98..2107c154ee78 100644 --- a/usr.bin/genl/genl.c +++ b/usr.bin/genl/genl.c @@ -98,10 +98,6 @@ struct genl_ctrl_op { uint32_t id; uint32_t flags; }; -struct genl_ctrl_ops { - uint32_t num_ops; - struct genl_ctrl_op **ops; -}; static struct snl_attr_parser nla_p_getops[] = { #define _OUT(_field) offsetof(struct genl_ctrl_op, _field) { @@ -123,10 +119,6 @@ struct genl_mcast_group { uint32_t id; const char *name; }; -struct genl_mcast_groups { - uint32_t num_groups; - struct genl_mcast_group **groups; -}; static struct snl_attr_parser nla_p_getmc[] = { #define _OUT(_field) offsetof(struct genl_mcast_group, _field) { @@ -150,8 +142,8 @@ struct genl_family { uint32_t version; uint32_t hdrsize; uint32_t max_attr; - struct genl_mcast_groups mcast_groups; - struct genl_ctrl_ops ops; + struct snl_parray mcast_groups; + struct snl_parray ops; }; static struct snl_attr_parser nla_p_getfamily[] = { @@ -208,19 +200,21 @@ static struct op_capability { }; static void -dump_operations(struct genl_ctrl_ops *ops) +dump_operations(struct snl_parray *ops) { - if (ops->num_ops == 0) + struct genl_ctrl_op *op; + + if (ops->count == 0) return; printf("\tsupported operations: \n"); - for (uint32_t i = 0; i < ops->num_ops; i++) { + for (uint32_t i = 0; i < ops->count; i++) { bool p = true; + op = ops->items[i]; printf("\t - ID: %#02x, Capabilities: %#02x", - ops->ops[i]->id, - ops->ops[i]->flags); + op->id, op->flags); for (size_t j = 0; j < nitems(op_caps); j++) - if ((ops->ops[i]->flags & op_caps[j].flag) == + if ((op->flags & op_caps[j].flag) == op_caps[j].flag) { printf("%s%s", p ? " (" : "; ", op_caps[j].str); @@ -231,15 +225,18 @@ dump_operations(struct genl_ctrl_ops *ops) } static void -dump_mcast_groups(struct genl_mcast_groups *mcast_groups) +dump_mcast_groups(struct snl_parray *mcast_groups) { - if (mcast_groups->num_groups == 0) + struct genl_mcast_group *group; + + if (mcast_groups->count == 0) return; printf("\tmulticast groups: \n"); - for (uint32_t i = 0; i < mcast_groups->num_groups; i++) + for (uint32_t i = 0; i < mcast_groups->count; i++) { + group = mcast_groups->items[i]; printf("\t - ID: %#02x, Name: %s\n", - mcast_groups->groups[i]->id, - mcast_groups->groups[i]->name); + group->id, group->name); + } } static void @@ -298,9 +295,13 @@ static struct genl_family attrs; const char * group_name(uint32_t id) { - for (u_int i = 0; i < attrs.mcast_groups.num_groups; i++) - if (attrs.mcast_groups.groups[i]->id == id) - return (attrs.mcast_groups.groups[i]->name); + struct genl_mcast_group *group; + + for (u_int i = 0; i < attrs.mcast_groups.count; i++) { + group = attrs.mcast_groups.items[i]; + if (group->id == id) + return (group->name); + } return ("???"); } @@ -340,14 +341,16 @@ monitor_mcast(int argc, char **argv) if (argc == 1) all = true; - for (u_int i = 0; i < attrs.mcast_groups.num_groups; i++) { + for (u_int i = 0; i < attrs.mcast_groups.count; i++) { + struct genl_mcast_group *group; + + group = attrs.mcast_groups.items[i]; if (all || - strcmp(attrs.mcast_groups.groups[i]->name, argv[1]) == 0) { + strcmp(group->name, argv[1]) == 0) { found = true; if (setsockopt(ss.fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, - &attrs.mcast_groups.groups[i]->id, - sizeof(attrs.mcast_groups.groups[i]->id)) + &group->id, sizeof(group->id)) == -1) err(EXIT_FAILURE, "Cannot subscribe to command " "notify"); diff --git a/usr.bin/netstat/route_netlink.c b/usr.bin/netstat/route_netlink.c index f7349650f4c6..a7b1a546e285 100644 --- a/usr.bin/netstat/route_netlink.c +++ b/usr.bin/netstat/route_netlink.c @@ -235,11 +235,12 @@ p_rtentry_netlink(struct snl_state *ss, const char *name, struct nlmsghdr *hdr) if (rt.rtax_weight == 0) rt.rtax_weight = rt_default_weight; - if (rt.rta_multipath.num_nhops != 0) { + if (rt.rta_multipath.count != 0) { uint32_t orig_rtflags = rt.rta_rtflags; uint32_t orig_mtu = rt.rtax_mtu; - for (uint32_t i = 0; i < rt.rta_multipath.num_nhops; i++) { - struct rta_mpath_nh *nhop = rt.rta_multipath.nhops[i]; + for (uint32_t i = 0; i < rt.rta_multipath.count; i++) { + struct rta_mpath_nh *nhop = + rt.rta_multipath.items[i]; rt.rta_gw = nhop->gw; rt.rta_oif = nhop->ifindex; @@ -341,4 +342,3 @@ p_rtable_netlink(int fibnum, int af) return (true); } - diff --git a/usr.sbin/arp/arp_netlink.c b/usr.sbin/arp/arp_netlink.c index 34f21cf96f4f..e998579d78cb 100644 --- a/usr.sbin/arp/arp_netlink.c +++ b/usr.sbin/arp/arp_netlink.c @@ -132,7 +132,7 @@ guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr) if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) return (0); - if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY)) + if (r.rta_multipath.count > 0 || (r.rta_rtflags & RTF_GATEWAY)) return (0); /* Check if the interface is of supported type */ @@ -448,4 +448,3 @@ set_nl(struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host) return (e.error != 0); } - diff --git a/usr.sbin/ndp/ndp_netlink.c b/usr.sbin/ndp/ndp_netlink.c index 7bd3252f0943..cd443bda635a 100644 --- a/usr.sbin/ndp/ndp_netlink.c +++ b/usr.sbin/ndp/ndp_netlink.c @@ -164,7 +164,7 @@ guess_ifindex(struct snl_state *ss, uint32_t fibnum, const struct sockaddr_in6 * if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) return (0); - if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY)) + if (r.rta_multipath.count > 0 || (r.rta_rtflags & RTF_GATEWAY)) return (0); /* Check if the interface is of supported type */ @@ -521,4 +521,3 @@ set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, char return (e.error != 0); } -