xref: /freebsd/usr.bin/genl/genl.c (revision d2e6eb6046560a848cf5627b4353a6fef1421ed4)
1cb1fc924SBaptiste Daroussin /*
2cb1fc924SBaptiste Daroussin  * SPDX-License-Identifier: BSD-2-Clause
3cb1fc924SBaptiste Daroussin  *
4cb1fc924SBaptiste Daroussin  * Copyright 2023 Baptiste Daroussin <bapt@FreeBSD.org>
5cb1fc924SBaptiste Daroussin  *
6cb1fc924SBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
7cb1fc924SBaptiste Daroussin  * modification, are permitted providing that the following conditions~
8cb1fc924SBaptiste Daroussin  * are met:
9cb1fc924SBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
10cb1fc924SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
11cb1fc924SBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
12cb1fc924SBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in the
13cb1fc924SBaptiste Daroussin  *    documentation and/or other materials provided with the distribution.
14cb1fc924SBaptiste Daroussin  *
15cb1fc924SBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16cb1fc924SBaptiste Daroussin  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17cb1fc924SBaptiste Daroussin  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18cb1fc924SBaptiste Daroussin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19cb1fc924SBaptiste Daroussin  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20cb1fc924SBaptiste Daroussin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21cb1fc924SBaptiste Daroussin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22cb1fc924SBaptiste Daroussin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23cb1fc924SBaptiste Daroussin  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24cb1fc924SBaptiste Daroussin  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25cb1fc924SBaptiste Daroussin  * POSSIBILITY OF SUCH DAMAGE.
26cb1fc924SBaptiste Daroussin  */
27cb1fc924SBaptiste Daroussin 
28cb1fc924SBaptiste Daroussin #include <sys/param.h>
29cb1fc924SBaptiste Daroussin #include <sys/module.h>
30e19b2ef9SBaptiste Daroussin #include <sys/socket.h>
31cb1fc924SBaptiste Daroussin 
32cb1fc924SBaptiste Daroussin #include <stdint.h>
33cb1fc924SBaptiste Daroussin #include <stdlib.h>
34cb1fc924SBaptiste Daroussin #include <unistd.h>
35cb1fc924SBaptiste Daroussin #include <err.h>
36cb1fc924SBaptiste Daroussin #include <stdio.h>
37e19b2ef9SBaptiste Daroussin #include <poll.h>
38cb1fc924SBaptiste Daroussin 
39cb1fc924SBaptiste Daroussin #include <netlink/netlink.h>
40cb1fc924SBaptiste Daroussin #include <netlink/netlink_generic.h>
41cb1fc924SBaptiste Daroussin #include <netlink/netlink_snl.h>
42cb1fc924SBaptiste Daroussin #include <netlink/netlink_snl_generic.h>
4388372289SBaptiste Daroussin #include <netlink/netlink_sysevent.h>
44cb1fc924SBaptiste Daroussin 
4588cd1e17SGleb Smirnoff #include "genl.h"
4688cd1e17SGleb Smirnoff 
47e19b2ef9SBaptiste Daroussin static int monitor_mcast(int argc, char **argv);
48e19b2ef9SBaptiste Daroussin static int list_families(int argc, char **argv);
49e19b2ef9SBaptiste Daroussin 
50e19b2ef9SBaptiste Daroussin static struct commands {
51e19b2ef9SBaptiste Daroussin 	const char *name;
52e19b2ef9SBaptiste Daroussin 	const char *usage;
53e19b2ef9SBaptiste Daroussin 	int (*cmd)(int argc, char **argv);
54e19b2ef9SBaptiste Daroussin } cmds[] = {
55f45132dbSBaptiste Daroussin 	{ "monitor", "monitor <family> [multicast group]", monitor_mcast },
56e19b2ef9SBaptiste Daroussin 	{ "list", "list", list_families },
57e19b2ef9SBaptiste Daroussin };
58e19b2ef9SBaptiste Daroussin 
5988cd1e17SGleb Smirnoff static monitor_parser_t parser_nlctrl_notify;
6088cd1e17SGleb Smirnoff static monitor_parser_t parser_nlsysevent;
6188cd1e17SGleb Smirnoff 
62e19b2ef9SBaptiste Daroussin static struct mcast_parsers {
63e19b2ef9SBaptiste Daroussin 	const char *family;
6488cd1e17SGleb Smirnoff 	monitor_parser_t *parser;
65e19b2ef9SBaptiste Daroussin } mcast_parsers [] = {
66e19b2ef9SBaptiste Daroussin 	{ "nlctrl", parser_nlctrl_notify },
6788372289SBaptiste Daroussin 	{ "nlsysevent", parser_nlsysevent },
6888cd1e17SGleb Smirnoff 	{ "rpc", parser_rpc },
69e19b2ef9SBaptiste Daroussin };
70e19b2ef9SBaptiste Daroussin 
7188372289SBaptiste Daroussin struct nlevent {
7288372289SBaptiste Daroussin 	const char *name;
7388372289SBaptiste Daroussin 	const char *subsystem;
7488372289SBaptiste Daroussin 	const char *type;
7588372289SBaptiste Daroussin 	const char *data;
7688372289SBaptiste Daroussin };
7788372289SBaptiste Daroussin #define _OUT(_field) offsetof(struct nlevent, _field)
7888372289SBaptiste Daroussin static struct snl_attr_parser ap_nlevent_get[] = {
7988372289SBaptiste Daroussin 	{ .type = NLSE_ATTR_SYSTEM, .off = _OUT(name), .cb = snl_attr_get_string },
8088372289SBaptiste Daroussin 	{ .type = NLSE_ATTR_SUBSYSTEM, .off = _OUT(subsystem), .cb = snl_attr_get_string },
8188372289SBaptiste Daroussin 	{ .type = NLSE_ATTR_TYPE, .off = _OUT(type), .cb = snl_attr_get_string },
8288372289SBaptiste Daroussin 	{ .type = NLSE_ATTR_DATA, .off = _OUT(data), .cb = snl_attr_get_string },
8388372289SBaptiste Daroussin };
8488372289SBaptiste Daroussin #undef _OUT
8588372289SBaptiste Daroussin SNL_DECLARE_GENL_PARSER(nlevent_get_parser, ap_nlevent_get);
8688372289SBaptiste Daroussin 
8740462a37SGleb Smirnoff /*
8840462a37SGleb Smirnoff  * We run our own parser(s) for CTRL_CMD_GETFAMILY instead of interfaces
8940462a37SGleb Smirnoff  * provided by <netlink_snl_generic.h>.  One reason is that we want the parsed
9040462a37SGleb Smirnoff  * string attributes to point into long living memory, instead of inside of
9140462a37SGleb Smirnoff  * just received message, hence we use snl_attr_get_stringn() instead of the
9240462a37SGleb Smirnoff  * snl_attr_get_string().  The shared library uses the latter to avoid creating
9340462a37SGleb Smirnoff  * ambiguous memory leaks.  Second reason is that genl(1) usage of the data is
9440462a37SGleb Smirnoff  * more extended than typical application that cares only of its own family and
9540462a37SGleb Smirnoff  * usually one group.  We are going to cycle around all available.
9640462a37SGleb Smirnoff  */
97cb1fc924SBaptiste Daroussin struct genl_ctrl_op {
98cb1fc924SBaptiste Daroussin 	uint32_t id;
99cb1fc924SBaptiste Daroussin 	uint32_t flags;
100cb1fc924SBaptiste Daroussin };
101cb1fc924SBaptiste Daroussin struct genl_ctrl_ops {
102cb1fc924SBaptiste Daroussin 	uint32_t num_ops;
103cb1fc924SBaptiste Daroussin 	struct genl_ctrl_op **ops;
104cb1fc924SBaptiste Daroussin };
10540462a37SGleb Smirnoff static struct snl_attr_parser nla_p_getops[] = {
106cb1fc924SBaptiste Daroussin #define _OUT(_field)	offsetof(struct genl_ctrl_op, _field)
10740462a37SGleb Smirnoff 	{
10840462a37SGleb Smirnoff 		.type = CTRL_ATTR_OP_ID,
10940462a37SGleb Smirnoff 		.off = _OUT(id),
11040462a37SGleb Smirnoff 		.cb = snl_attr_get_uint32
11140462a37SGleb Smirnoff 	},
11240462a37SGleb Smirnoff 	{
11340462a37SGleb Smirnoff 		.type = CTRL_ATTR_OP_FLAGS,
11440462a37SGleb Smirnoff 		.off = _OUT(flags),
11540462a37SGleb Smirnoff 		.cb = snl_attr_get_uint32
11640462a37SGleb Smirnoff 	},
117cb1fc924SBaptiste Daroussin #undef _OUT
11840462a37SGleb Smirnoff };
11940462a37SGleb Smirnoff SNL_DECLARE_ATTR_PARSER_EXT(genl_ctrl_op_parser, sizeof(struct genl_ctrl_op),
12040462a37SGleb Smirnoff     nla_p_getops, NULL);
12140462a37SGleb Smirnoff 
12240462a37SGleb Smirnoff struct genl_mcast_group {
12340462a37SGleb Smirnoff 	uint32_t id;
12440462a37SGleb Smirnoff 	const char *name;
12540462a37SGleb Smirnoff };
12640462a37SGleb Smirnoff struct genl_mcast_groups {
12740462a37SGleb Smirnoff 	uint32_t num_groups;
12840462a37SGleb Smirnoff 	struct genl_mcast_group **groups;
12940462a37SGleb Smirnoff };
13040462a37SGleb Smirnoff static struct snl_attr_parser nla_p_getmc[] = {
13140462a37SGleb Smirnoff #define	_OUT(_field)	offsetof(struct genl_mcast_group, _field)
13240462a37SGleb Smirnoff 	{
13340462a37SGleb Smirnoff 		.type = CTRL_ATTR_MCAST_GRP_NAME,
13440462a37SGleb Smirnoff 		.off = _OUT(name),
13540462a37SGleb Smirnoff 		.cb = snl_attr_get_stringn,
13640462a37SGleb Smirnoff 	},
13740462a37SGleb Smirnoff 	{
13840462a37SGleb Smirnoff 		.type = CTRL_ATTR_MCAST_GRP_ID,
13940462a37SGleb Smirnoff 		.off = _OUT(id),
14040462a37SGleb Smirnoff 		.cb = snl_attr_get_uint32,
14140462a37SGleb Smirnoff 	},
14240462a37SGleb Smirnoff #undef _OUT
14340462a37SGleb Smirnoff };
14440462a37SGleb Smirnoff SNL_DECLARE_ATTR_PARSER_EXT(genl_mc_parser, sizeof(struct genl_mcast_group),
14540462a37SGleb Smirnoff     nla_p_getmc, NULL);
146cb1fc924SBaptiste Daroussin 
147cb1fc924SBaptiste Daroussin struct genl_family {
148cb1fc924SBaptiste Daroussin 	uint16_t id;
14940462a37SGleb Smirnoff 	const char *name;
150cb1fc924SBaptiste Daroussin 	uint32_t version;
151cb1fc924SBaptiste Daroussin 	uint32_t hdrsize;
152cb1fc924SBaptiste Daroussin 	uint32_t max_attr;
15340462a37SGleb Smirnoff 	struct genl_mcast_groups mcast_groups;
154cb1fc924SBaptiste Daroussin 	struct genl_ctrl_ops ops;
155cb1fc924SBaptiste Daroussin };
156cb1fc924SBaptiste Daroussin 
15740462a37SGleb Smirnoff static struct snl_attr_parser nla_p_getfamily[] = {
158cb1fc924SBaptiste Daroussin #define	_OUT(_field)	offsetof(struct genl_family, _field)
15940462a37SGleb Smirnoff 	{
16040462a37SGleb Smirnoff 		.type = CTRL_ATTR_FAMILY_ID,
16140462a37SGleb Smirnoff 		.off = _OUT(id),
16240462a37SGleb Smirnoff 		.cb = snl_attr_get_uint16,
16340462a37SGleb Smirnoff 	},
16440462a37SGleb Smirnoff 	{
16540462a37SGleb Smirnoff 		.type = CTRL_ATTR_FAMILY_NAME,
16640462a37SGleb Smirnoff 		.off = _OUT(name),
16740462a37SGleb Smirnoff 		.cb = snl_attr_get_stringn,
16840462a37SGleb Smirnoff 	},
16940462a37SGleb Smirnoff 	{
17040462a37SGleb Smirnoff 		.type = CTRL_ATTR_VERSION,
17140462a37SGleb Smirnoff 		.off = _OUT(version),
17240462a37SGleb Smirnoff 		.cb = snl_attr_get_uint32,
17340462a37SGleb Smirnoff 	},
17440462a37SGleb Smirnoff 	{
17540462a37SGleb Smirnoff 		.type = CTRL_ATTR_VERSION,
17640462a37SGleb Smirnoff 		.off = _OUT(hdrsize),
17740462a37SGleb Smirnoff 		.cb = snl_attr_get_uint32,
17840462a37SGleb Smirnoff 	},
17940462a37SGleb Smirnoff 	{
18040462a37SGleb Smirnoff 		.type = CTRL_ATTR_MAXATTR,
18140462a37SGleb Smirnoff 		.off = _OUT(max_attr),
18240462a37SGleb Smirnoff 		.cb = snl_attr_get_uint32,
18340462a37SGleb Smirnoff 	},
184cb1fc924SBaptiste Daroussin 	{
185cb1fc924SBaptiste Daroussin 		.type = CTRL_ATTR_OPS,
186cb1fc924SBaptiste Daroussin 		.off = _OUT(ops),
187cb1fc924SBaptiste Daroussin 		.cb = snl_attr_get_parray,
188cb1fc924SBaptiste Daroussin 		.arg = &genl_ctrl_op_parser,
189cb1fc924SBaptiste Daroussin 	},
190cb1fc924SBaptiste Daroussin 	{
191cb1fc924SBaptiste Daroussin 		.type = CTRL_ATTR_MCAST_GROUPS,
192cb1fc924SBaptiste Daroussin 		.off = _OUT(mcast_groups),
193cb1fc924SBaptiste Daroussin 		.cb = snl_attr_get_parray,
19440462a37SGleb Smirnoff 		.arg = &genl_mc_parser,
195cb1fc924SBaptiste Daroussin 	},
196cb1fc924SBaptiste Daroussin #undef _OUT
19740462a37SGleb Smirnoff };
19840462a37SGleb Smirnoff SNL_DECLARE_GENL_PARSER(genl_family_parser, nla_p_getfamily);
199cb1fc924SBaptiste Daroussin 
200cb1fc924SBaptiste Daroussin static struct op_capability {
201cb1fc924SBaptiste Daroussin 	uint32_t flag;
202cb1fc924SBaptiste Daroussin 	const char *str;
203cb1fc924SBaptiste Daroussin } op_caps[] = {
204cb1fc924SBaptiste Daroussin 	{ GENL_ADMIN_PERM, "requires admin permission" },
205cb1fc924SBaptiste Daroussin 	{ GENL_CMD_CAP_DO, "can modify" },
206cb1fc924SBaptiste Daroussin 	{ GENL_CMD_CAP_DUMP, "can get/dump" },
207cb1fc924SBaptiste Daroussin 	{ GENL_CMD_CAP_HASPOL, "has policy" },
208cb1fc924SBaptiste Daroussin };
209cb1fc924SBaptiste Daroussin 
210cb1fc924SBaptiste Daroussin static void
dump_operations(struct genl_ctrl_ops * ops)211cb1fc924SBaptiste Daroussin dump_operations(struct genl_ctrl_ops *ops)
212cb1fc924SBaptiste Daroussin {
213cb1fc924SBaptiste Daroussin 	if (ops->num_ops == 0)
214cb1fc924SBaptiste Daroussin 		return;
215cb1fc924SBaptiste Daroussin 	printf("\tsupported operations: \n");
216cb1fc924SBaptiste Daroussin 	for (uint32_t i = 0; i < ops->num_ops; i++) {
217*d2e6eb60SGleb Smirnoff 		bool p = true;
218*d2e6eb60SGleb Smirnoff 
219*d2e6eb60SGleb Smirnoff 		printf("\t  - ID: %#02x, Capabilities: %#02x",
220cb1fc924SBaptiste Daroussin 		    ops->ops[i]->id,
221cb1fc924SBaptiste Daroussin 		    ops->ops[i]->flags);
222cb1fc924SBaptiste Daroussin 		for (size_t j = 0; j < nitems(op_caps); j++)
223*d2e6eb60SGleb Smirnoff 			if ((ops->ops[i]->flags & op_caps[j].flag) ==
224*d2e6eb60SGleb Smirnoff 			    op_caps[j].flag) {
225*d2e6eb60SGleb Smirnoff 				printf("%s%s", p ? " (" : "; ",
226*d2e6eb60SGleb Smirnoff 				    op_caps[j].str);
227*d2e6eb60SGleb Smirnoff 				p = false;
228*d2e6eb60SGleb Smirnoff 			}
229*d2e6eb60SGleb Smirnoff 		printf("%s\n", p ? "" : ")");
230cb1fc924SBaptiste Daroussin 	}
231cb1fc924SBaptiste Daroussin }
232cb1fc924SBaptiste Daroussin 
233cb1fc924SBaptiste Daroussin static void
dump_mcast_groups(struct genl_mcast_groups * mcast_groups)23440462a37SGleb Smirnoff dump_mcast_groups(struct genl_mcast_groups *mcast_groups)
235cb1fc924SBaptiste Daroussin {
236cb1fc924SBaptiste Daroussin 	if (mcast_groups->num_groups == 0)
237cb1fc924SBaptiste Daroussin 		return;
238cb1fc924SBaptiste Daroussin 	printf("\tmulticast groups: \n");
239cb1fc924SBaptiste Daroussin 	for (uint32_t i = 0; i < mcast_groups->num_groups; i++)
240cb1fc924SBaptiste Daroussin 		printf("\t  - ID: %#02x, Name: %s\n",
24140462a37SGleb Smirnoff 		    mcast_groups->groups[i]->id,
24240462a37SGleb Smirnoff 		    mcast_groups->groups[i]->name);
243cb1fc924SBaptiste Daroussin }
244cb1fc924SBaptiste Daroussin 
245e19b2ef9SBaptiste Daroussin static void
usage(void)246e19b2ef9SBaptiste Daroussin usage(void)
247e19b2ef9SBaptiste Daroussin {
248e19b2ef9SBaptiste Daroussin 	fprintf(stderr, "Usage: %s\n", getprogname());
249e19b2ef9SBaptiste Daroussin 	for (size_t i = 0; i < nitems(cmds); i++)
250e19b2ef9SBaptiste Daroussin 		fprintf(stderr, "       %s %s\n", getprogname(), cmds[i].usage);
251e19b2ef9SBaptiste Daroussin }
252cb1fc924SBaptiste Daroussin 
253cb1fc924SBaptiste Daroussin static void
dump_family(struct genl_family * family)254cb1fc924SBaptiste Daroussin dump_family(struct genl_family *family)
255cb1fc924SBaptiste Daroussin {
256cb1fc924SBaptiste Daroussin 	printf("Name: %s\n\tID: %#02hx, Version: %#02x, "
257cb1fc924SBaptiste Daroussin 	    "header size: %d, max attributes: %d\n",
258cb1fc924SBaptiste Daroussin 	    family->name, family->id, family->version,
259cb1fc924SBaptiste Daroussin 	    family->hdrsize, family->max_attr);
260cb1fc924SBaptiste Daroussin 	dump_operations(&family->ops);
261cb1fc924SBaptiste Daroussin 	dump_mcast_groups(&family->mcast_groups);
262cb1fc924SBaptiste Daroussin }
263cb1fc924SBaptiste Daroussin 
26488cd1e17SGleb Smirnoff static void
parser_nlctrl_notify(struct snl_state * ss,struct nlmsghdr * hdr)265e19b2ef9SBaptiste Daroussin parser_nlctrl_notify(struct snl_state *ss, struct nlmsghdr *hdr)
266e19b2ef9SBaptiste Daroussin {
267e19b2ef9SBaptiste Daroussin 	struct genl_family family = {};
268e19b2ef9SBaptiste Daroussin 
269e19b2ef9SBaptiste Daroussin 	if (snl_parse_nlmsg(ss, hdr, &genl_family_parser,
270e19b2ef9SBaptiste Daroussin 				&family))
271e19b2ef9SBaptiste Daroussin 		dump_family(&family);
272e19b2ef9SBaptiste Daroussin }
273e19b2ef9SBaptiste Daroussin 
27488cd1e17SGleb Smirnoff static void
parser_nlsysevent(struct snl_state * ss,struct nlmsghdr * hdr)27588372289SBaptiste Daroussin parser_nlsysevent(struct snl_state *ss, struct nlmsghdr *hdr)
27688372289SBaptiste Daroussin {
27788372289SBaptiste Daroussin 	struct nlevent ne = {};
27888372289SBaptiste Daroussin 	if (snl_parse_nlmsg(ss, hdr, &nlevent_get_parser, &ne)) {
27988372289SBaptiste Daroussin 		printf("system=%s subsystem=%s type=%s", ne.name, ne.subsystem, ne.type);
28088372289SBaptiste Daroussin 		if (ne.data) {
28188372289SBaptiste Daroussin 			printf(" %s", ne.data);
28288372289SBaptiste Daroussin 			if (ne.data[strlen(ne.data) -1] != '\n')
28388372289SBaptiste Daroussin 				printf("\n");
28488372289SBaptiste Daroussin 		}
28588372289SBaptiste Daroussin 	}
28688372289SBaptiste Daroussin }
28788372289SBaptiste Daroussin 
28888cd1e17SGleb Smirnoff static void
parser_fallback(struct snl_state * ss __unused,struct nlmsghdr * hdr)28988cd1e17SGleb Smirnoff parser_fallback(struct snl_state *ss __unused, struct nlmsghdr *hdr)
290e19b2ef9SBaptiste Daroussin {
29188cd1e17SGleb Smirnoff 	printf("Unknown message: type 0x%x, length %u\n",
29288cd1e17SGleb Smirnoff 	    hdr->nlmsg_type, hdr->nlmsg_len);
293e19b2ef9SBaptiste Daroussin }
294e19b2ef9SBaptiste Daroussin 
29540462a37SGleb Smirnoff /* Populated by monitor_mcast() and may be used by protocol parser callbacks. */
29640462a37SGleb Smirnoff static struct genl_family attrs;
29740462a37SGleb Smirnoff 
29888cd1e17SGleb Smirnoff const char *
group_name(uint32_t id)29988cd1e17SGleb Smirnoff group_name(uint32_t id)
30088cd1e17SGleb Smirnoff {
30188cd1e17SGleb Smirnoff 	for (u_int i = 0; i < attrs.mcast_groups.num_groups; i++)
30288cd1e17SGleb Smirnoff 		if (attrs.mcast_groups.groups[i]->id == id)
30388cd1e17SGleb Smirnoff 			return (attrs.mcast_groups.groups[i]->name);
30488cd1e17SGleb Smirnoff 	return ("???");
30588cd1e17SGleb Smirnoff }
30688cd1e17SGleb Smirnoff 
30740462a37SGleb Smirnoff static int
monitor_mcast(int argc,char ** argv)30840462a37SGleb Smirnoff monitor_mcast(int argc, char **argv)
309e19b2ef9SBaptiste Daroussin {
310e19b2ef9SBaptiste Daroussin 	struct snl_state ss;
31140462a37SGleb Smirnoff 	struct snl_writer nw;
312e19b2ef9SBaptiste Daroussin 	struct nlmsghdr *hdr;
313e19b2ef9SBaptiste Daroussin 	struct pollfd pfd;
314e19b2ef9SBaptiste Daroussin 	bool found = false;
31565e7a648SBaptiste Daroussin 	bool all = false;
31688cd1e17SGleb Smirnoff 	monitor_parser_t *parser;
317e19b2ef9SBaptiste Daroussin 
318f45132dbSBaptiste Daroussin 	if (argc < 1 || argc > 2) {
319e19b2ef9SBaptiste Daroussin 		usage();
320e19b2ef9SBaptiste Daroussin 		return (EXIT_FAILURE);
321e19b2ef9SBaptiste Daroussin 	}
32288372289SBaptiste Daroussin 
32340462a37SGleb Smirnoff 	if (!snl_init(&ss, NETLINK_GENERIC))
32440462a37SGleb Smirnoff 		err(EXIT_FAILURE, "snl_init()");
32540462a37SGleb Smirnoff 	snl_init_writer(&ss, &nw);
32640462a37SGleb Smirnoff 	snl_create_genl_msg_request(&nw, GENL_ID_CTRL, CTRL_CMD_GETFAMILY);
32740462a37SGleb Smirnoff 	snl_add_msg_attr_string(&nw, CTRL_ATTR_FAMILY_NAME, argv[0]);
32840462a37SGleb Smirnoff 	if ((hdr = snl_finalize_msg(&nw)) == NULL)
32940462a37SGleb Smirnoff 		err(EXIT_FAILURE, "snl_finalize_msg");
33040462a37SGleb Smirnoff 	if (!snl_send_message(&ss, hdr))
33140462a37SGleb Smirnoff 		err(EXIT_FAILURE, "snl_send_message");
33240462a37SGleb Smirnoff 	hdr = snl_read_reply(&ss, hdr->nlmsg_seq);
33340462a37SGleb Smirnoff 	if (hdr == NULL)
33440462a37SGleb Smirnoff 		err(EXIT_FAILURE, "snl_read_reply");
33540462a37SGleb Smirnoff 	if (hdr->nlmsg_type == NLMSG_ERROR)
33640462a37SGleb Smirnoff 		err(EXIT_FAILURE, "netlink(4) returned error");
33740462a37SGleb Smirnoff 	memset(&attrs, 0, sizeof(attrs));
33840462a37SGleb Smirnoff 	if (!snl_parse_nlmsg(&ss, hdr, &genl_family_parser, &attrs))
33940462a37SGleb Smirnoff 		err(EXIT_FAILURE, "snl_parse_nlmsg CTRL_CMD_GETFAMILY");
34040462a37SGleb Smirnoff 
341f45132dbSBaptiste Daroussin 	if (argc == 1)
34265e7a648SBaptiste Daroussin 		all = true;
34340462a37SGleb Smirnoff 	for (u_int i = 0; i < attrs.mcast_groups.num_groups; i++) {
34440462a37SGleb Smirnoff 		if (all ||
34540462a37SGleb Smirnoff 		    strcmp(attrs.mcast_groups.groups[i]->name, argv[1]) == 0) {
346e19b2ef9SBaptiste Daroussin 			found = true;
347e19b2ef9SBaptiste Daroussin 			if (setsockopt(ss.fd, SOL_NETLINK,
348e19b2ef9SBaptiste Daroussin 			    NETLINK_ADD_MEMBERSHIP,
34940462a37SGleb Smirnoff 			    &attrs.mcast_groups.groups[i]->id,
35040462a37SGleb Smirnoff 			    sizeof(attrs.mcast_groups.groups[i]->id))
351e19b2ef9SBaptiste Daroussin 			    == -1)
352e19b2ef9SBaptiste Daroussin 				err(EXIT_FAILURE, "Cannot subscribe to command "
353e19b2ef9SBaptiste Daroussin 				    "notify");
35465e7a648SBaptiste Daroussin 			if (!all)
355e19b2ef9SBaptiste Daroussin 				break;
356e19b2ef9SBaptiste Daroussin 		}
357e19b2ef9SBaptiste Daroussin 	}
358e19b2ef9SBaptiste Daroussin 	if (!found)
3595fa2e50eSGleb Smirnoff 		errx(EXIT_FAILURE, "No such multicast group '%s'"
360e19b2ef9SBaptiste Daroussin 		    " in family '%s'", argv[1], argv[0]);
36140462a37SGleb Smirnoff 	parser = parser_fallback;
362e19b2ef9SBaptiste Daroussin 	for (size_t i= 0; i < nitems(mcast_parsers); i++) {
363e19b2ef9SBaptiste Daroussin 		if (strcmp(mcast_parsers[i].family, argv[0]) == 0) {
364e19b2ef9SBaptiste Daroussin 			parser = mcast_parsers[i].parser;
365e19b2ef9SBaptiste Daroussin 			break;
366e19b2ef9SBaptiste Daroussin 		}
367e19b2ef9SBaptiste Daroussin 	}
368e19b2ef9SBaptiste Daroussin 	memset(&pfd, 0, sizeof(pfd));
369e19b2ef9SBaptiste Daroussin 	pfd.fd = ss.fd;
370e19b2ef9SBaptiste Daroussin 	pfd.events = POLLIN | POLLERR;
371e19b2ef9SBaptiste Daroussin 	while (true) {
372e19b2ef9SBaptiste Daroussin 		pfd.revents = 0;
373e19b2ef9SBaptiste Daroussin 		if (poll(&pfd, 1, -1) == -1) {
374e19b2ef9SBaptiste Daroussin 			if (errno == EINTR)
375e19b2ef9SBaptiste Daroussin 				continue;
376e19b2ef9SBaptiste Daroussin 			err(EXIT_FAILURE, "poll()");
377e19b2ef9SBaptiste Daroussin 		}
378e19b2ef9SBaptiste Daroussin 		hdr = snl_read_message(&ss);
379e19b2ef9SBaptiste Daroussin 		if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR)
380e19b2ef9SBaptiste Daroussin 			parser(&ss, hdr);
381e19b2ef9SBaptiste Daroussin 
382e19b2ef9SBaptiste Daroussin 	}
383e19b2ef9SBaptiste Daroussin 
384e19b2ef9SBaptiste Daroussin 	return (EXIT_SUCCESS);
385e19b2ef9SBaptiste Daroussin }
386e19b2ef9SBaptiste Daroussin 
387e19b2ef9SBaptiste Daroussin int
list_families(int argc,char ** argv __unused)388e19b2ef9SBaptiste Daroussin list_families(int argc, char **argv __unused)
389cb1fc924SBaptiste Daroussin {
390cb1fc924SBaptiste Daroussin 	struct snl_state ss;
391cb1fc924SBaptiste Daroussin 	struct snl_writer nw;
392cb1fc924SBaptiste Daroussin 	struct nlmsghdr *hdr;
393cb1fc924SBaptiste Daroussin 	struct snl_errmsg_data e = {};
394cb1fc924SBaptiste Daroussin 	uint32_t seq_id;
395cb1fc924SBaptiste Daroussin 
396e19b2ef9SBaptiste Daroussin 	if (argc != 0) {
397e19b2ef9SBaptiste Daroussin 		usage();
398e19b2ef9SBaptiste Daroussin 		return (EXIT_FAILURE);
399e19b2ef9SBaptiste Daroussin 	}
400cb1fc924SBaptiste Daroussin 	if (!snl_init(&ss, NETLINK_GENERIC))
401cb1fc924SBaptiste Daroussin 		err(EXIT_FAILURE, "snl_init()");
402cb1fc924SBaptiste Daroussin 
403cb1fc924SBaptiste Daroussin 	snl_init_writer(&ss, &nw);
404e19b2ef9SBaptiste Daroussin 	hdr = snl_create_genl_msg_request(&nw, GENL_ID_CTRL,
405e19b2ef9SBaptiste Daroussin 	    CTRL_CMD_GETFAMILY);
406cb1fc924SBaptiste Daroussin 	if ((hdr = snl_finalize_msg(&nw)) == NULL)
407cb1fc924SBaptiste Daroussin 		err(EXIT_FAILURE, "snl_finalize_msg");
408cb1fc924SBaptiste Daroussin 	seq_id = hdr->nlmsg_seq;
409cb1fc924SBaptiste Daroussin 	if (!snl_send_message(&ss, hdr))
410cb1fc924SBaptiste Daroussin 		err(EXIT_FAILURE, "snl_send_message");
411cb1fc924SBaptiste Daroussin 
412cb1fc924SBaptiste Daroussin 	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
413cb1fc924SBaptiste Daroussin 		if (e.error != 0) {
414cb1fc924SBaptiste Daroussin 			err(EXIT_FAILURE, "Error reading generic netlink");
415cb1fc924SBaptiste Daroussin 		}
416cb1fc924SBaptiste Daroussin 		struct genl_family family = {};
417cb1fc924SBaptiste Daroussin 		if (snl_parse_nlmsg(&ss, hdr, &genl_family_parser, &family))
418cb1fc924SBaptiste Daroussin 			dump_family(&family);
419cb1fc924SBaptiste Daroussin 	}
420cb1fc924SBaptiste Daroussin 
421cb1fc924SBaptiste Daroussin 	return (EXIT_SUCCESS);
422cb1fc924SBaptiste Daroussin }
423e19b2ef9SBaptiste Daroussin 
424e19b2ef9SBaptiste Daroussin int
main(int argc,char ** argv)425e19b2ef9SBaptiste Daroussin main(int argc, char **argv)
426e19b2ef9SBaptiste Daroussin {
427e19b2ef9SBaptiste Daroussin 	if (modfind("netlink") == -1)
428e19b2ef9SBaptiste Daroussin 		err(EXIT_FAILURE, "require netlink module to be loaded");
429e19b2ef9SBaptiste Daroussin 
430e19b2ef9SBaptiste Daroussin 	if (argc == 1)
431e19b2ef9SBaptiste Daroussin 		return (list_families(0, NULL));
432e19b2ef9SBaptiste Daroussin 
433e19b2ef9SBaptiste Daroussin 	for (size_t i = 0; i < nitems(cmds); i++) {
434e19b2ef9SBaptiste Daroussin 		if (strcmp(argv[1], cmds[i].name) == 0)
435e19b2ef9SBaptiste Daroussin 			return (cmds[i].cmd(argc - 2, argv + 2));
436e19b2ef9SBaptiste Daroussin 	}
437e19b2ef9SBaptiste Daroussin 	usage();
438e19b2ef9SBaptiste Daroussin 
439e19b2ef9SBaptiste Daroussin 	return (EXIT_FAILURE);
440e19b2ef9SBaptiste Daroussin }
441