xref: /freebsd/usr.bin/genl/genl.c (revision 535af610)
1 /*
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright 2023 Baptiste Daroussin <bapt@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted providing that the following conditions~
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/module.h>
30 
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <err.h>
35 #include <stdio.h>
36 
37 #include <netlink/netlink.h>
38 #include <netlink/netlink_generic.h>
39 #include <netlink/netlink_snl.h>
40 #include <netlink/netlink_snl_generic.h>
41 
42 struct genl_ctrl_op {
43 	uint32_t id;
44 	uint32_t flags;
45 };
46 
47 struct genl_ctrl_ops {
48 	uint32_t num_ops;
49 	struct genl_ctrl_op **ops;
50 };
51 
52 #define _OUT(_field)	offsetof(struct genl_ctrl_op, _field)
53 static struct snl_attr_parser _nla_p_getops[] = {
54 	{ .type = CTRL_ATTR_OP_ID, .off = _OUT(id), .cb = snl_attr_get_uint32},
55 	{ .type = CTRL_ATTR_OP_FLAGS, .off = _OUT(flags), .cb = snl_attr_get_uint32 },
56 };
57 #undef _OUT
58 SNL_DECLARE_ATTR_PARSER_EXT(genl_ctrl_op_parser,
59 		sizeof(struct genl_ctrl_op),
60 		_nla_p_getops, NULL);
61 
62 struct genl_family {
63 	uint16_t id;
64 	char *name;
65 	uint32_t version;
66 	uint32_t hdrsize;
67 	uint32_t max_attr;
68 	struct snl_genl_ctrl_mcast_groups mcast_groups;
69 	struct genl_ctrl_ops ops;
70 };
71 
72 #define	_OUT(_field)	offsetof(struct genl_family, _field)
73 static struct snl_attr_parser _nla_p_getfamily[] = {
74 	{ .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(id), .cb = snl_attr_get_uint16 },
75 	{ .type = CTRL_ATTR_FAMILY_NAME, .off = _OUT(name), .cb = snl_attr_get_string },
76 	{ .type = CTRL_ATTR_VERSION, .off = _OUT(version), .cb = snl_attr_get_uint32 },
77 	{ .type = CTRL_ATTR_VERSION, .off = _OUT(hdrsize), .cb = snl_attr_get_uint32 },
78 	{ .type = CTRL_ATTR_MAXATTR, .off = _OUT(max_attr), .cb = snl_attr_get_uint32 },
79 	{
80 		.type = CTRL_ATTR_OPS,
81 		.off = _OUT(ops),
82 		.cb = snl_attr_get_parray,
83 		.arg = &genl_ctrl_op_parser,
84 	},
85 	{
86 		.type = CTRL_ATTR_MCAST_GROUPS,
87 		.off = _OUT(mcast_groups),
88 		.cb = snl_attr_get_parray,
89 		.arg = &_genl_ctrl_mc_parser,
90 	},
91 };
92 #undef _OUT
93 SNL_DECLARE_GENL_PARSER(genl_family_parser, _nla_p_getfamily);
94 
95 static struct op_capability {
96 	uint32_t flag;
97 	const char *str;
98 } op_caps[] = {
99 	{ GENL_ADMIN_PERM, "requires admin permission" },
100 	{ GENL_CMD_CAP_DO, "can modify" },
101 	{ GENL_CMD_CAP_DUMP, "can get/dump" },
102 	{ GENL_CMD_CAP_HASPOL, "has policy" },
103 };
104 
105 static void
106 dump_operations(struct genl_ctrl_ops *ops)
107 {
108 	if (ops->num_ops == 0)
109 		return;
110 	printf("\tsupported operations: \n");
111 	for (uint32_t i = 0; i < ops->num_ops; i++) {
112 		printf("\t  - ID: %#02x, Capabilities: %#02x (",
113 		    ops->ops[i]->id,
114 		    ops->ops[i]->flags);
115 		for (size_t j = 0; j < nitems(op_caps); j++)
116 			if ((ops->ops[i]->flags & op_caps[j].flag) == op_caps[j].flag)
117 				printf("%s; ", op_caps[j].str);
118 		printf("\b\b)\n");
119 	}
120 }
121 
122 static void
123 dump_mcast_groups( struct snl_genl_ctrl_mcast_groups *mcast_groups)
124 {
125 	if (mcast_groups->num_groups == 0)
126 		return;
127 	printf("\tmulticast groups: \n");
128 	for (uint32_t i = 0; i < mcast_groups->num_groups; i++)
129 		printf("\t  - ID: %#02x, Name: %s\n",
130 		    mcast_groups->groups[i]->mcast_grp_id,
131 		    mcast_groups->groups[i]->mcast_grp_name);
132 }
133 
134 
135 static void
136 dump_family(struct genl_family *family)
137 {
138 	printf("Name: %s\n\tID: %#02hx, Version: %#02x, "
139 	    "header size: %d, max attributes: %d\n",
140 	    family->name, family->id, family->version,
141 	    family->hdrsize, family->max_attr);
142 	dump_operations(&family->ops);
143 	dump_mcast_groups(&family->mcast_groups);
144 }
145 
146 int
147 main(int argc, char **argv __unused)
148 {
149 	struct snl_state ss;
150 	struct snl_writer nw;
151 	struct nlmsghdr *hdr;
152 	struct snl_errmsg_data e = {};
153 	uint32_t seq_id;
154 
155 	if (argc > 1)
156 		errx(EXIT_FAILURE, "usage: genl does not accept any argument");
157 	if (modfind("netlink") == -1)
158 		err(EXIT_FAILURE, "require netlink module to be loaded");
159 
160 	if (!snl_init(&ss, NETLINK_GENERIC))
161 		err(EXIT_FAILURE, "snl_init()");
162 
163 	snl_init_writer(&ss, &nw);
164 	hdr = snl_create_genl_msg_request(&nw, GENL_ID_CTRL, CTRL_CMD_GETFAMILY);
165 	if ((hdr = snl_finalize_msg(&nw)) == NULL)
166 		err(EXIT_FAILURE, "snl_finalize_msg");
167 	seq_id = hdr->nlmsg_seq;
168 	if (!snl_send_message(&ss, hdr))
169 		err(EXIT_FAILURE, "snl_send_message");
170 
171 	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
172 		if (e.error != 0) {
173 			err(EXIT_FAILURE, "Error reading generic netlink");
174 		}
175 		struct genl_family family = {};
176 		if (snl_parse_nlmsg(&ss, hdr, &genl_family_parser, &family))
177 			dump_family(&family);
178 	}
179 
180 	return (EXIT_SUCCESS);
181 }
182