git: 40462a376a32 - main - genl: stop using struct _getfamily_attrs, snl_genl_ctrl_mcast_group
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 17 Jan 2025 02:25:03 UTC
The branch main has been updated by glebius: URL: https://cgit.FreeBSD.org/src/commit/?id=40462a376a324c19845a5f696c565069142ef326 commit 40462a376a324c19845a5f696c565069142ef326 Author: Gleb Smirnoff <glebius@FreeBSD.org> AuthorDate: 2025-01-17 02:20:13 +0000 Commit: Gleb Smirnoff <glebius@FreeBSD.org> CommitDate: 2025-01-17 02:24:32 +0000 genl: stop using struct _getfamily_attrs, snl_genl_ctrl_mcast_group This program has two modes: monitor a single family and dump known families. The former uses directly snl_get_genl_family_info() which uses supposedly internal struct _getfamily_attrs as the argument. The latter uses a parser named genl_family_parser that makes a mixture of local definitions and definitions from <netlink_snl_generic.h>. While the struct genl_family_parser is local, it points at struct snl_genl_ctrl_mcast_groups and struct snl_genl_ctrl_mcast_group that are supposedly private to netlink_snl_generic.h, as are hanging off the underscored _getfamily_attrs. Rewrite this mess by using same parser for both modes, that is fully implemented locally. This parser has another very important difference to the one declared in the header library. It will copy strings out of the message into memory allocated within the snl_state. With the header library parser strings point into received packet and contents will be overwritten on next netlink message. This is not a bug with existing genl(1) program, but it would be with future changes. Reviewed by: melifaro Differential Revision: https://reviews.freebsd.org/D48479 --- usr.bin/genl/genl.c | 149 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 112 insertions(+), 37 deletions(-) diff --git a/usr.bin/genl/genl.c b/usr.bin/genl/genl.c index dd0c3af30c9c..cb01c6bcdd26 100644 --- a/usr.bin/genl/genl.c +++ b/usr.bin/genl/genl.c @@ -81,43 +81,103 @@ static struct snl_attr_parser ap_nlevent_get[] = { #undef _OUT SNL_DECLARE_GENL_PARSER(nlevent_get_parser, ap_nlevent_get); +/* + * We run our own parser(s) for CTRL_CMD_GETFAMILY instead of interfaces + * provided by <netlink_snl_generic.h>. One reason is that we want the parsed + * string attributes to point into long living memory, instead of inside of + * just received message, hence we use snl_attr_get_stringn() instead of the + * snl_attr_get_string(). The shared library uses the latter to avoid creating + * ambiguous memory leaks. Second reason is that genl(1) usage of the data is + * more extended than typical application that cares only of its own family and + * usually one group. We are going to cycle around all available. + */ 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) -static struct snl_attr_parser _nla_p_getops[] = { - { .type = CTRL_ATTR_OP_ID, .off = _OUT(id), .cb = snl_attr_get_uint32}, - { .type = CTRL_ATTR_OP_FLAGS, .off = _OUT(flags), .cb = snl_attr_get_uint32 }, + { + .type = CTRL_ATTR_OP_ID, + .off = _OUT(id), + .cb = snl_attr_get_uint32 + }, + { + .type = CTRL_ATTR_OP_FLAGS, + .off = _OUT(flags), + .cb = snl_attr_get_uint32 + }, +#undef _OUT +}; +SNL_DECLARE_ATTR_PARSER_EXT(genl_ctrl_op_parser, sizeof(struct genl_ctrl_op), + nla_p_getops, NULL); + +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) + { + .type = CTRL_ATTR_MCAST_GRP_NAME, + .off = _OUT(name), + .cb = snl_attr_get_stringn, + }, + { + .type = CTRL_ATTR_MCAST_GRP_ID, + .off = _OUT(id), + .cb = snl_attr_get_uint32, + }, #undef _OUT -SNL_DECLARE_ATTR_PARSER_EXT(genl_ctrl_op_parser, - sizeof(struct genl_ctrl_op), - _nla_p_getops, NULL); +}; +SNL_DECLARE_ATTR_PARSER_EXT(genl_mc_parser, sizeof(struct genl_mcast_group), + nla_p_getmc, NULL); struct genl_family { uint16_t id; - char *name; + const char *name; uint32_t version; uint32_t hdrsize; uint32_t max_attr; - struct snl_genl_ctrl_mcast_groups mcast_groups; + struct genl_mcast_groups mcast_groups; struct genl_ctrl_ops ops; }; +static struct snl_attr_parser nla_p_getfamily[] = { #define _OUT(_field) offsetof(struct genl_family, _field) -static struct snl_attr_parser _nla_p_getfamily[] = { - { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(id), .cb = snl_attr_get_uint16 }, - { .type = CTRL_ATTR_FAMILY_NAME, .off = _OUT(name), .cb = snl_attr_get_string }, - { .type = CTRL_ATTR_VERSION, .off = _OUT(version), .cb = snl_attr_get_uint32 }, - { .type = CTRL_ATTR_VERSION, .off = _OUT(hdrsize), .cb = snl_attr_get_uint32 }, - { .type = CTRL_ATTR_MAXATTR, .off = _OUT(max_attr), .cb = snl_attr_get_uint32 }, + { + .type = CTRL_ATTR_FAMILY_ID, + .off = _OUT(id), + .cb = snl_attr_get_uint16, + }, + { + .type = CTRL_ATTR_FAMILY_NAME, + .off = _OUT(name), + .cb = snl_attr_get_stringn, + }, + { + .type = CTRL_ATTR_VERSION, + .off = _OUT(version), + .cb = snl_attr_get_uint32, + }, + { + .type = CTRL_ATTR_VERSION, + .off = _OUT(hdrsize), + .cb = snl_attr_get_uint32, + }, + { + .type = CTRL_ATTR_MAXATTR, + .off = _OUT(max_attr), + .cb = snl_attr_get_uint32, + }, { .type = CTRL_ATTR_OPS, .off = _OUT(ops), @@ -128,11 +188,11 @@ static struct snl_attr_parser _nla_p_getfamily[] = { .type = CTRL_ATTR_MCAST_GROUPS, .off = _OUT(mcast_groups), .cb = snl_attr_get_parray, - .arg = &_genl_ctrl_mc_parser, + .arg = &genl_mc_parser, }, -}; #undef _OUT -SNL_DECLARE_GENL_PARSER(genl_family_parser, _nla_p_getfamily); +}; +SNL_DECLARE_GENL_PARSER(genl_family_parser, nla_p_getfamily); static struct op_capability { uint32_t flag; @@ -162,15 +222,15 @@ dump_operations(struct genl_ctrl_ops *ops) } static void -dump_mcast_groups( struct snl_genl_ctrl_mcast_groups *mcast_groups) +dump_mcast_groups(struct genl_mcast_groups *mcast_groups) { if (mcast_groups->num_groups == 0) return; printf("\tmulticast groups: \n"); for (uint32_t i = 0; i < mcast_groups->num_groups; i++) printf("\t - ID: %#02x, Name: %s\n", - mcast_groups->groups[i]->mcast_grp_id, - mcast_groups->groups[i]->mcast_grp_name); + mcast_groups->groups[i]->id, + mcast_groups->groups[i]->name); } static void @@ -222,39 +282,53 @@ parser_fallback(struct snl_state *ss __unused, struct nlmsghdr *hdr __unused) printf("New unknown message\n"); } -int -monitor_mcast(int argc __unused, char **argv) +/* Populated by monitor_mcast() and may be used by protocol parser callbacks. */ +static struct genl_family attrs; + +static int +monitor_mcast(int argc, char **argv) { struct snl_state ss; + struct snl_writer nw; struct nlmsghdr *hdr; - struct _getfamily_attrs attrs; struct pollfd pfd; bool found = false; bool all = false; void (*parser)(struct snl_state *ss, struct nlmsghdr *hdr); - parser = parser_fallback; - - if (!snl_init(&ss, NETLINK_GENERIC)) - err(EXIT_FAILURE, "snl_init()"); - if (argc < 1 || argc > 2) { usage(); return (EXIT_FAILURE); } - if (!snl_get_genl_family_info(&ss, argv[0], &attrs)) - errx(EXIT_FAILURE, "Unknown family '%s'", argv[0]); + if (!snl_init(&ss, NETLINK_GENERIC)) + err(EXIT_FAILURE, "snl_init()"); + snl_init_writer(&ss, &nw); + snl_create_genl_msg_request(&nw, GENL_ID_CTRL, CTRL_CMD_GETFAMILY); + snl_add_msg_attr_string(&nw, CTRL_ATTR_FAMILY_NAME, argv[0]); + if ((hdr = snl_finalize_msg(&nw)) == NULL) + err(EXIT_FAILURE, "snl_finalize_msg"); + if (!snl_send_message(&ss, hdr)) + err(EXIT_FAILURE, "snl_send_message"); + hdr = snl_read_reply(&ss, hdr->nlmsg_seq); + if (hdr == NULL) + err(EXIT_FAILURE, "snl_read_reply"); + if (hdr->nlmsg_type == NLMSG_ERROR) + err(EXIT_FAILURE, "netlink(4) returned error"); + memset(&attrs, 0, sizeof(attrs)); + if (!snl_parse_nlmsg(&ss, hdr, &genl_family_parser, &attrs)) + err(EXIT_FAILURE, "snl_parse_nlmsg CTRL_CMD_GETFAMILY"); + if (argc == 1) all = true; - for (unsigned int i = 0; i < attrs.mcast_groups.num_groups; i++) { - if (all || strcmp(attrs.mcast_groups.groups[i]->mcast_grp_name, - argv[1]) == 0) { + for (u_int i = 0; i < attrs.mcast_groups.num_groups; i++) { + if (all || + strcmp(attrs.mcast_groups.groups[i]->name, argv[1]) == 0) { found = true; if (setsockopt(ss.fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, - &attrs.mcast_groups.groups[i]->mcast_grp_id, - sizeof(attrs.mcast_groups.groups[i]->mcast_grp_id)) + &attrs.mcast_groups.groups[i]->id, + sizeof(attrs.mcast_groups.groups[i]->id)) == -1) err(EXIT_FAILURE, "Cannot subscribe to command " "notify"); @@ -265,6 +339,7 @@ monitor_mcast(int argc __unused, char **argv) if (!found) errx(EXIT_FAILURE, "No such multicast group '%s'" " in family '%s'", argv[1], argv[0]); + parser = parser_fallback; for (size_t i= 0; i < nitems(mcast_parsers); i++) { if (strcmp(mcast_parsers[i].family, argv[0]) == 0) { parser = mcast_parsers[i].parser;