git: cb1fc924d2c0 - main - genl: add new command to list genetlink(4)

From: Baptiste Daroussin <bapt_at_FreeBSD.org>
Date: Tue, 30 May 2023 11:37:10 UTC
The branch main has been updated by bapt:

URL: https://cgit.FreeBSD.org/src/commit/?id=cb1fc924d2c0b87ad27e3741aabf641d35797e2e

commit cb1fc924d2c0b87ad27e3741aabf641d35797e2e
Author:     Baptiste Daroussin <bapt@FreeBSD.org>
AuthorDate: 2023-05-30 11:31:04 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2023-05-30 11:36:43 +0000

    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(+)

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 <bsd.prog.mk>
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 <bapt@nours.eu>
+.\"
+.\" 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 <bapt@FreeBSD.org>
+ *
+ * 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 <sys/param.h>
+#include <sys/module.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+
+#include <netlink/netlink.h>
+#include <netlink/netlink_generic.h>
+#include <netlink/netlink_snl.h>
+#include <netlink/netlink_snl_generic.h>
+
+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);
+}