diff --git a/sbin/route/route_netlink.c b/sbin/route/route_netlink.c --- a/sbin/route/route_netlink.c +++ b/sbin/route/route_netlink.c @@ -428,11 +428,11 @@ 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 @@ 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 @@ 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 @@ return (e.error); } - diff --git a/sys/netlink/netlink_snl.h b/sys/netlink/netlink_snl.h --- a/sys/netlink/netlink_snl.h +++ b/sys/netlink/netlink_snl.h @@ -724,23 +724,60 @@ 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 @@ 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 @@ 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 @@ 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 --- a/sys/netlink/netlink_snl_generic.h +++ b/sys/netlink/netlink_snl_generic.h @@ -65,11 +65,6 @@ 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 @@ 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 @@ 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 --- a/sys/netlink/netlink_snl_route_parsers.h +++ b/sys/netlink/netlink_snl_route_parsers.h @@ -93,21 +93,18 @@ 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 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 --- a/tests/sys/netlink/test_rtnl_route.c +++ b/tests/sys/netlink/test_rtnl_route.c @@ -129,7 +129,7 @@ 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_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_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_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_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 --- a/tests/sys/netlink/test_snl.c +++ b/tests/sys/netlink/test_snl.c @@ -37,6 +37,70 @@ } +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_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 --- a/tests/sys/netlink/test_snl_generic.c +++ b/tests/sys/netlink/test_snl_generic.c @@ -90,9 +90,10 @@ 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 @@ return (atf_no_error()); } - diff --git a/usr.bin/genl/genl.c b/usr.bin/genl/genl.c --- a/usr.bin/genl/genl.c +++ b/usr.bin/genl/genl.c @@ -98,10 +98,6 @@ 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 @@ 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 @@ 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 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 @@ } 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 @@ 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 @@ 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 --- a/usr.bin/netstat/route_netlink.c +++ b/usr.bin/netstat/route_netlink.c @@ -235,11 +235,12 @@ 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 @@ return (true); } - diff --git a/usr.sbin/arp/arp_netlink.c b/usr.sbin/arp/arp_netlink.c --- a/usr.sbin/arp/arp_netlink.c +++ b/usr.sbin/arp/arp_netlink.c @@ -132,7 +132,7 @@ 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 @@ return (e.error != 0); } - diff --git a/usr.sbin/ndp/ndp_netlink.c b/usr.sbin/ndp/ndp_netlink.c --- a/usr.sbin/ndp/ndp_netlink.c +++ b/usr.sbin/ndp/ndp_netlink.c @@ -164,7 +164,7 @@ 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 @@ return (e.error != 0); } -