From cb1fc924d2c0b87ad27e3741aabf641d35797e2e Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Tue, 30 May 2023 13:31:04 +0200 Subject: [PATCH] genl: add new command to list genetlink(4) This commands list genetlink protocols and its operations and capabilities Name: nlctrl ID: 0x10, Version: 00, header size: 2, max attributes: 10 supported operations: - ID: 0x3, Capabilities: 0xe (can modify; can get/dump; has policy) multicast groups: - ID: 0x30, Name: notify Name: carp ID: 0x11, Version: 00, header size: 2, max attributes: 2 supported operations: - ID: 0x1, Capabilities: 0xe (can modify; can get/dump; has policy) - ID: 0x2, Capabilities: 0xb (requires admin permission; can modify; has policy) Reviewed by: melifaro Differential Revision: https://reviews.freebsd.org/D40330 --- share/mk/src.opts.mk | 1 + tools/build/mk/OptionalObsoleteFiles.inc | 5 + tools/build/options/WITHOUT_NETLINK | 4 + tools/build/options/WITH_NETLINK | 4 + usr.bin/Makefile | 1 + usr.bin/genl/Makefile | 3 + usr.bin/genl/genl.1 | 46 ++++++ usr.bin/genl/genl.c | 181 +++++++++++++++++++++++ 8 files changed, 245 insertions(+) create mode 100644 tools/build/options/WITHOUT_NETLINK create mode 100644 tools/build/options/WITH_NETLINK create mode 100644 usr.bin/genl/Makefile create mode 100644 usr.bin/genl/genl.1 create mode 100644 usr.bin/genl/genl.c diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk index f828bdc0151b..7b67a55f34b4 100644 --- a/share/mk/src.opts.mk +++ b/share/mk/src.opts.mk @@ -145,6 +145,7 @@ __DEFAULT_YES_OPTIONS = \ MLX5TOOL \ NETCAT \ NETGRAPH \ + NETLINK \ NETLINK_SUPPORT \ NLS_CATALOGS \ NS_CACHING \ diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc index 9c7e40ee563a..20654515cfd1 100644 --- a/tools/build/mk/OptionalObsoleteFiles.inc +++ b/tools/build/mk/OptionalObsoleteFiles.inc @@ -5639,6 +5639,11 @@ OLD_FILES+=var/yp/Makefile.dist OLD_DIRS+=var/yp .endif +.if ${MK_NETLINK} == no +OLD_FILES+=usr.bin/genl +OLD_FILES+=usr/share/man/man1/genl.1.gz +.endif + .if ${MK_NLS} == no OLD_DIRS+=usr/share/nls/ OLD_DIRS+=usr/share/nls/C diff --git a/tools/build/options/WITHOUT_NETLINK b/tools/build/options/WITHOUT_NETLINK new file mode 100644 index 000000000000..3ff4b66f1900 --- /dev/null +++ b/tools/build/options/WITHOUT_NETLINK @@ -0,0 +1,4 @@ +.\" $FreeBSD$ +Do not build +.Xr genl 1 +utility. diff --git a/tools/build/options/WITH_NETLINK b/tools/build/options/WITH_NETLINK new file mode 100644 index 000000000000..321e9856b2b5 --- /dev/null +++ b/tools/build/options/WITH_NETLINK @@ -0,0 +1,4 @@ +.\" $FreeBSD$ +Build the +.Xr genl 1 +utility. diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 19988d35c7ba..e027eaf81f24 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -235,6 +235,7 @@ SUBDIR.${MK_MAIL}+= msgs SUBDIR.${MK_MAKE}+= bmake SUBDIR.${MK_MAN_UTILS}+= man SUBDIR.${MK_NETCAT}+= nc +SUBDIR.${MK_NETLINK}+= genl SUBDIR.${MK_NIS}+= ypcat SUBDIR.${MK_NIS}+= ypmatch SUBDIR.${MK_NIS}+= ypwhich diff --git a/usr.bin/genl/Makefile b/usr.bin/genl/Makefile new file mode 100644 index 000000000000..15e60300de02 --- /dev/null +++ b/usr.bin/genl/Makefile @@ -0,0 +1,3 @@ +PROG= genl + +.include diff --git a/usr.bin/genl/genl.1 b/usr.bin/genl/genl.1 new file mode 100644 index 000000000000..44ce4feaea48 --- /dev/null +++ b/usr.bin/genl/genl.1 @@ -0,0 +1,46 @@ +.\" +.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD +.\" +.\" Copyright (c) 2023 Baptiste Daroussin +.\" +.\" 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. +.\" +.Dd May 20, 2023 +.Dt GENL 1 +.Os +.Sh NAME +.Nm genl +.Nd "generic netlink list" +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +lists all available generic netlink protocols, and presents its details: +.Bl -tag -width "multicast groups" +.It operations +Id of the operation if any and associated capabilities +.It multicast groups +If of the available multicast group if any and it associated name +.El +.Sh SEE ALSO +.Xr genetlink 4 , +.Xr netlink 4 diff --git a/usr.bin/genl/genl.c b/usr.bin/genl/genl.c new file mode 100644 index 000000000000..8e8e18a7f8e2 --- /dev/null +++ b/usr.bin/genl/genl.c @@ -0,0 +1,181 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright 2023 Baptiste Daroussin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted providing 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 ``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 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. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct genl_ctrl_op { + uint32_t id; + uint32_t flags; +}; + +struct genl_ctrl_ops { + uint32_t num_ops; + struct genl_ctrl_op **ops; +}; + +#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 }, +}; +#undef _OUT +SNL_DECLARE_ATTR_PARSER_EXT(genl_ctrl_op_parser, + sizeof(struct genl_ctrl_op), + _nla_p_getops, NULL); + +struct genl_family { + uint16_t id; + char *name; + uint32_t version; + uint32_t hdrsize; + uint32_t max_attr; + struct snl_genl_ctrl_mcast_groups mcast_groups; + struct genl_ctrl_ops ops; +}; + +#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_OPS, + .off = _OUT(ops), + .cb = snl_attr_get_parray, + .arg = &genl_ctrl_op_parser, + }, + { + .type = CTRL_ATTR_MCAST_GROUPS, + .off = _OUT(mcast_groups), + .cb = snl_attr_get_parray, + .arg = &_genl_ctrl_mc_parser, + }, +}; +#undef _OUT +SNL_DECLARE_GENL_PARSER(genl_family_parser, _nla_p_getfamily); + +static struct op_capability { + uint32_t flag; + const char *str; +} op_caps[] = { + { GENL_ADMIN_PERM, "requires admin permission" }, + { GENL_CMD_CAP_DO, "can modify" }, + { GENL_CMD_CAP_DUMP, "can get/dump" }, + { GENL_CMD_CAP_HASPOL, "has policy" }, +}; + +static void +dump_operations(struct genl_ctrl_ops *ops) +{ + if (ops->num_ops == 0) + return; + printf("\tsupported operations: \n"); + for (uint32_t i = 0; i < ops->num_ops; i++) { + printf("\t - ID: %#02x, Capabilities: %#02x (", + ops->ops[i]->id, + ops->ops[i]->flags); + for (size_t j = 0; j < nitems(op_caps); j++) + if ((ops->ops[i]->flags & op_caps[j].flag) == op_caps[j].flag) + printf("%s; ", op_caps[j].str); + printf("\b\b)\n"); + } +} + +static void +dump_mcast_groups( struct snl_genl_ctrl_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); +} + + +static void +dump_family(struct genl_family *family) +{ + printf("Name: %s\n\tID: %#02hx, Version: %#02x, " + "header size: %d, max attributes: %d\n", + family->name, family->id, family->version, + family->hdrsize, family->max_attr); + dump_operations(&family->ops); + dump_mcast_groups(&family->mcast_groups); +} + +int +main(int argc, char **argv __unused) +{ + struct snl_state ss; + struct snl_writer nw; + struct nlmsghdr *hdr; + struct snl_errmsg_data e = {}; + uint32_t seq_id; + + if (argc > 1) + errx(EXIT_FAILURE, "usage: genl does not accept any argument"); + if (modfind("netlink") == -1) + err(EXIT_FAILURE, "require netlink module to be loaded"); + + if (!snl_init(&ss, NETLINK_GENERIC)) + err(EXIT_FAILURE, "snl_init()"); + + snl_init_writer(&ss, &nw); + hdr = snl_create_genl_msg_request(&nw, GENL_ID_CTRL, CTRL_CMD_GETFAMILY); + if ((hdr = snl_finalize_msg(&nw)) == NULL) + err(EXIT_FAILURE, "snl_finalize_msg"); + seq_id = hdr->nlmsg_seq; + if (!snl_send_message(&ss, hdr)) + err(EXIT_FAILURE, "snl_send_message"); + + while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) { + if (e.error != 0) { + err(EXIT_FAILURE, "Error reading generic netlink"); + } + struct genl_family family = {}; + if (snl_parse_nlmsg(&ss, hdr, &genl_family_parser, &family)) + dump_family(&family); + } + + return (EXIT_SUCCESS); +}