xref: /freebsd/usr.bin/genl/genl.c (revision e19b2ef9)
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>
43cb1fc924SBaptiste Daroussin 
44e19b2ef9SBaptiste Daroussin static int monitor_mcast(int argc, char **argv);
45e19b2ef9SBaptiste Daroussin static int list_families(int argc, char **argv);
46e19b2ef9SBaptiste Daroussin static void parser_nlctrl_notify(struct snl_state *ss, struct nlmsghdr *hdr);
47e19b2ef9SBaptiste Daroussin static void parser_fallback(struct snl_state *ss, struct nlmsghdr *hdr);
48e19b2ef9SBaptiste Daroussin 
49e19b2ef9SBaptiste Daroussin static struct commands {
50e19b2ef9SBaptiste Daroussin 	const char *name;
51e19b2ef9SBaptiste Daroussin 	const char *usage;
52e19b2ef9SBaptiste Daroussin 	int (*cmd)(int argc, char **argv);
53e19b2ef9SBaptiste Daroussin } cmds[] = {
54e19b2ef9SBaptiste Daroussin 	{ "monitor", "monitor <family> <multicast group>", monitor_mcast },
55e19b2ef9SBaptiste Daroussin 	{ "list", "list", list_families },
56e19b2ef9SBaptiste Daroussin };
57e19b2ef9SBaptiste Daroussin 
58e19b2ef9SBaptiste Daroussin static struct mcast_parsers {
59e19b2ef9SBaptiste Daroussin 	const char *family;
60e19b2ef9SBaptiste Daroussin 	void (*parser)(struct snl_state *ss, struct nlmsghdr *hdr);
61e19b2ef9SBaptiste Daroussin } mcast_parsers [] = {
62e19b2ef9SBaptiste Daroussin 	{ "nlctrl", parser_nlctrl_notify },
63e19b2ef9SBaptiste Daroussin };
64e19b2ef9SBaptiste Daroussin 
65cb1fc924SBaptiste Daroussin struct genl_ctrl_op {
66cb1fc924SBaptiste Daroussin 	uint32_t id;
67cb1fc924SBaptiste Daroussin 	uint32_t flags;
68cb1fc924SBaptiste Daroussin };
69cb1fc924SBaptiste Daroussin 
70cb1fc924SBaptiste Daroussin struct genl_ctrl_ops {
71cb1fc924SBaptiste Daroussin 	uint32_t num_ops;
72cb1fc924SBaptiste Daroussin 	struct genl_ctrl_op **ops;
73cb1fc924SBaptiste Daroussin };
74cb1fc924SBaptiste Daroussin 
75cb1fc924SBaptiste Daroussin #define _OUT(_field)	offsetof(struct genl_ctrl_op, _field)
76cb1fc924SBaptiste Daroussin static struct snl_attr_parser _nla_p_getops[] = {
77cb1fc924SBaptiste Daroussin 	{ .type = CTRL_ATTR_OP_ID, .off = _OUT(id), .cb = snl_attr_get_uint32},
78cb1fc924SBaptiste Daroussin 	{ .type = CTRL_ATTR_OP_FLAGS, .off = _OUT(flags), .cb = snl_attr_get_uint32 },
79cb1fc924SBaptiste Daroussin };
80cb1fc924SBaptiste Daroussin #undef _OUT
81cb1fc924SBaptiste Daroussin SNL_DECLARE_ATTR_PARSER_EXT(genl_ctrl_op_parser,
82cb1fc924SBaptiste Daroussin 		sizeof(struct genl_ctrl_op),
83cb1fc924SBaptiste Daroussin 		_nla_p_getops, NULL);
84cb1fc924SBaptiste Daroussin 
85cb1fc924SBaptiste Daroussin struct genl_family {
86cb1fc924SBaptiste Daroussin 	uint16_t id;
87cb1fc924SBaptiste Daroussin 	char *name;
88cb1fc924SBaptiste Daroussin 	uint32_t version;
89cb1fc924SBaptiste Daroussin 	uint32_t hdrsize;
90cb1fc924SBaptiste Daroussin 	uint32_t max_attr;
91cb1fc924SBaptiste Daroussin 	struct snl_genl_ctrl_mcast_groups mcast_groups;
92cb1fc924SBaptiste Daroussin 	struct genl_ctrl_ops ops;
93cb1fc924SBaptiste Daroussin };
94cb1fc924SBaptiste Daroussin 
95cb1fc924SBaptiste Daroussin #define	_OUT(_field)	offsetof(struct genl_family, _field)
96cb1fc924SBaptiste Daroussin static struct snl_attr_parser _nla_p_getfamily[] = {
97cb1fc924SBaptiste Daroussin 	{ .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(id), .cb = snl_attr_get_uint16 },
98cb1fc924SBaptiste Daroussin 	{ .type = CTRL_ATTR_FAMILY_NAME, .off = _OUT(name), .cb = snl_attr_get_string },
99cb1fc924SBaptiste Daroussin 	{ .type = CTRL_ATTR_VERSION, .off = _OUT(version), .cb = snl_attr_get_uint32 },
100cb1fc924SBaptiste Daroussin 	{ .type = CTRL_ATTR_VERSION, .off = _OUT(hdrsize), .cb = snl_attr_get_uint32 },
101cb1fc924SBaptiste Daroussin 	{ .type = CTRL_ATTR_MAXATTR, .off = _OUT(max_attr), .cb = snl_attr_get_uint32 },
102cb1fc924SBaptiste Daroussin 	{
103cb1fc924SBaptiste Daroussin 		.type = CTRL_ATTR_OPS,
104cb1fc924SBaptiste Daroussin 		.off = _OUT(ops),
105cb1fc924SBaptiste Daroussin 		.cb = snl_attr_get_parray,
106cb1fc924SBaptiste Daroussin 		.arg = &genl_ctrl_op_parser,
107cb1fc924SBaptiste Daroussin 	},
108cb1fc924SBaptiste Daroussin 	{
109cb1fc924SBaptiste Daroussin 		.type = CTRL_ATTR_MCAST_GROUPS,
110cb1fc924SBaptiste Daroussin 		.off = _OUT(mcast_groups),
111cb1fc924SBaptiste Daroussin 		.cb = snl_attr_get_parray,
112cb1fc924SBaptiste Daroussin 		.arg = &_genl_ctrl_mc_parser,
113cb1fc924SBaptiste Daroussin 	},
114cb1fc924SBaptiste Daroussin };
115cb1fc924SBaptiste Daroussin #undef _OUT
116cb1fc924SBaptiste Daroussin SNL_DECLARE_GENL_PARSER(genl_family_parser, _nla_p_getfamily);
117cb1fc924SBaptiste Daroussin 
118cb1fc924SBaptiste Daroussin static struct op_capability {
119cb1fc924SBaptiste Daroussin 	uint32_t flag;
120cb1fc924SBaptiste Daroussin 	const char *str;
121cb1fc924SBaptiste Daroussin } op_caps[] = {
122cb1fc924SBaptiste Daroussin 	{ GENL_ADMIN_PERM, "requires admin permission" },
123cb1fc924SBaptiste Daroussin 	{ GENL_CMD_CAP_DO, "can modify" },
124cb1fc924SBaptiste Daroussin 	{ GENL_CMD_CAP_DUMP, "can get/dump" },
125cb1fc924SBaptiste Daroussin 	{ GENL_CMD_CAP_HASPOL, "has policy" },
126cb1fc924SBaptiste Daroussin };
127cb1fc924SBaptiste Daroussin 
128cb1fc924SBaptiste Daroussin static void
dump_operations(struct genl_ctrl_ops * ops)129cb1fc924SBaptiste Daroussin dump_operations(struct genl_ctrl_ops *ops)
130cb1fc924SBaptiste Daroussin {
131cb1fc924SBaptiste Daroussin 	if (ops->num_ops == 0)
132cb1fc924SBaptiste Daroussin 		return;
133cb1fc924SBaptiste Daroussin 	printf("\tsupported operations: \n");
134cb1fc924SBaptiste Daroussin 	for (uint32_t i = 0; i < ops->num_ops; i++) {
135cb1fc924SBaptiste Daroussin 		printf("\t  - ID: %#02x, Capabilities: %#02x (",
136cb1fc924SBaptiste Daroussin 		    ops->ops[i]->id,
137cb1fc924SBaptiste Daroussin 		    ops->ops[i]->flags);
138cb1fc924SBaptiste Daroussin 		for (size_t j = 0; j < nitems(op_caps); j++)
139cb1fc924SBaptiste Daroussin 			if ((ops->ops[i]->flags & op_caps[j].flag) == op_caps[j].flag)
140cb1fc924SBaptiste Daroussin 				printf("%s; ", op_caps[j].str);
141cb1fc924SBaptiste Daroussin 		printf("\b\b)\n");
142cb1fc924SBaptiste Daroussin 	}
143cb1fc924SBaptiste Daroussin }
144cb1fc924SBaptiste Daroussin 
145cb1fc924SBaptiste Daroussin static void
dump_mcast_groups(struct snl_genl_ctrl_mcast_groups * mcast_groups)146cb1fc924SBaptiste Daroussin dump_mcast_groups( struct snl_genl_ctrl_mcast_groups *mcast_groups)
147cb1fc924SBaptiste Daroussin {
148cb1fc924SBaptiste Daroussin 	if (mcast_groups->num_groups == 0)
149cb1fc924SBaptiste Daroussin 		return;
150cb1fc924SBaptiste Daroussin 	printf("\tmulticast groups: \n");
151cb1fc924SBaptiste Daroussin 	for (uint32_t i = 0; i < mcast_groups->num_groups; i++)
152cb1fc924SBaptiste Daroussin 		printf("\t  - ID: %#02x, Name: %s\n",
153cb1fc924SBaptiste Daroussin 		    mcast_groups->groups[i]->mcast_grp_id,
154cb1fc924SBaptiste Daroussin 		    mcast_groups->groups[i]->mcast_grp_name);
155cb1fc924SBaptiste Daroussin }
156cb1fc924SBaptiste Daroussin 
157e19b2ef9SBaptiste Daroussin static void
usage(void)158e19b2ef9SBaptiste Daroussin usage(void)
159e19b2ef9SBaptiste Daroussin {
160e19b2ef9SBaptiste Daroussin 	fprintf(stderr, "Usage: %s\n", getprogname());
161e19b2ef9SBaptiste Daroussin 	for (size_t i = 0; i < nitems(cmds); i++)
162e19b2ef9SBaptiste Daroussin 		fprintf(stderr, "       %s %s\n", getprogname(), cmds[i].usage);
163e19b2ef9SBaptiste Daroussin }
164cb1fc924SBaptiste Daroussin 
165cb1fc924SBaptiste Daroussin static void
dump_family(struct genl_family * family)166cb1fc924SBaptiste Daroussin dump_family(struct genl_family *family)
167cb1fc924SBaptiste Daroussin {
168cb1fc924SBaptiste Daroussin 	printf("Name: %s\n\tID: %#02hx, Version: %#02x, "
169cb1fc924SBaptiste Daroussin 	    "header size: %d, max attributes: %d\n",
170cb1fc924SBaptiste Daroussin 	    family->name, family->id, family->version,
171cb1fc924SBaptiste Daroussin 	    family->hdrsize, family->max_attr);
172cb1fc924SBaptiste Daroussin 	dump_operations(&family->ops);
173cb1fc924SBaptiste Daroussin 	dump_mcast_groups(&family->mcast_groups);
174cb1fc924SBaptiste Daroussin }
175cb1fc924SBaptiste Daroussin 
176e19b2ef9SBaptiste Daroussin void
parser_nlctrl_notify(struct snl_state * ss,struct nlmsghdr * hdr)177e19b2ef9SBaptiste Daroussin parser_nlctrl_notify(struct snl_state *ss, struct nlmsghdr *hdr)
178e19b2ef9SBaptiste Daroussin {
179e19b2ef9SBaptiste Daroussin 	struct genl_family family = {};
180e19b2ef9SBaptiste Daroussin 
181e19b2ef9SBaptiste Daroussin 	if (snl_parse_nlmsg(ss, hdr, &genl_family_parser,
182e19b2ef9SBaptiste Daroussin 				&family))
183e19b2ef9SBaptiste Daroussin 		dump_family(&family);
184e19b2ef9SBaptiste Daroussin }
185e19b2ef9SBaptiste Daroussin 
186e19b2ef9SBaptiste Daroussin void
parser_fallback(struct snl_state * ss __unused,struct nlmsghdr * hdr __unused)187e19b2ef9SBaptiste Daroussin parser_fallback(struct snl_state *ss __unused, struct nlmsghdr *hdr __unused)
188e19b2ef9SBaptiste Daroussin {
189e19b2ef9SBaptiste Daroussin 	printf("New unknown message\n");
190e19b2ef9SBaptiste Daroussin }
191e19b2ef9SBaptiste Daroussin 
192cb1fc924SBaptiste Daroussin int
monitor_mcast(int argc __unused,char ** argv)193e19b2ef9SBaptiste Daroussin monitor_mcast(int argc __unused, char **argv)
194e19b2ef9SBaptiste Daroussin {
195e19b2ef9SBaptiste Daroussin 	struct snl_state ss;
196e19b2ef9SBaptiste Daroussin 	struct nlmsghdr *hdr;
197e19b2ef9SBaptiste Daroussin 	struct _getfamily_attrs attrs;
198e19b2ef9SBaptiste Daroussin 	struct pollfd pfd;
199e19b2ef9SBaptiste Daroussin 	bool found = false;
200e19b2ef9SBaptiste Daroussin 	void (*parser)(struct snl_state *ss, struct nlmsghdr *hdr);
201e19b2ef9SBaptiste Daroussin 
202e19b2ef9SBaptiste Daroussin 	parser = parser_fallback;
203e19b2ef9SBaptiste Daroussin 
204e19b2ef9SBaptiste Daroussin 	if (!snl_init(&ss, NETLINK_GENERIC))
205e19b2ef9SBaptiste Daroussin 		err(EXIT_FAILURE, "snl_init()");
206e19b2ef9SBaptiste Daroussin 
207e19b2ef9SBaptiste Daroussin 	if (argc != 2) {
208e19b2ef9SBaptiste Daroussin 		usage();
209e19b2ef9SBaptiste Daroussin 		return (EXIT_FAILURE);
210e19b2ef9SBaptiste Daroussin 	}
211e19b2ef9SBaptiste Daroussin 	if (!snl_get_genl_family_info(&ss, argv[0], &attrs))
212e19b2ef9SBaptiste Daroussin 		errx(EXIT_FAILURE, "Unknown family '%s'", argv[0]);
213e19b2ef9SBaptiste Daroussin 	for (uint32_t i = 0; i < attrs.mcast_groups.num_groups; i++) {
214e19b2ef9SBaptiste Daroussin 		if (strcmp(attrs.mcast_groups.groups[i]->mcast_grp_name,
215e19b2ef9SBaptiste Daroussin 		    argv[1]) == 0) {
216e19b2ef9SBaptiste Daroussin 			found = true;
217e19b2ef9SBaptiste Daroussin 			if (setsockopt(ss.fd, SOL_NETLINK,
218e19b2ef9SBaptiste Daroussin 			    NETLINK_ADD_MEMBERSHIP,
219e19b2ef9SBaptiste Daroussin 			    (void *)&attrs.mcast_groups.groups[i]->mcast_grp_id,
220e19b2ef9SBaptiste Daroussin 			    sizeof(attrs.mcast_groups.groups[i]->mcast_grp_id))
221e19b2ef9SBaptiste Daroussin 			    == -1)
222e19b2ef9SBaptiste Daroussin 				err(EXIT_FAILURE, "Cannot subscribe to command "
223e19b2ef9SBaptiste Daroussin 				    "notify");
224e19b2ef9SBaptiste Daroussin 			break;
225e19b2ef9SBaptiste Daroussin 		}
226e19b2ef9SBaptiste Daroussin 	}
227e19b2ef9SBaptiste Daroussin 	if (!found)
228e19b2ef9SBaptiste Daroussin 		errx(EXIT_FAILURE, "No such multicat group '%s'"
229e19b2ef9SBaptiste Daroussin 		    " in family '%s'", argv[1], argv[0]);
230e19b2ef9SBaptiste Daroussin 	for (size_t i= 0; i < nitems(mcast_parsers); i++) {
231e19b2ef9SBaptiste Daroussin 		if (strcmp(mcast_parsers[i].family, argv[0]) == 0) {
232e19b2ef9SBaptiste Daroussin 			parser = mcast_parsers[i].parser;
233e19b2ef9SBaptiste Daroussin 			break;
234e19b2ef9SBaptiste Daroussin 		}
235e19b2ef9SBaptiste Daroussin 	}
236e19b2ef9SBaptiste Daroussin 	memset(&pfd, 0, sizeof(pfd));
237e19b2ef9SBaptiste Daroussin 	pfd.fd = ss.fd;
238e19b2ef9SBaptiste Daroussin 	pfd.events = POLLIN | POLLERR;
239e19b2ef9SBaptiste Daroussin 	while (true) {
240e19b2ef9SBaptiste Daroussin 		pfd.revents = 0;
241e19b2ef9SBaptiste Daroussin 		if (poll(&pfd, 1, -1) == -1) {
242e19b2ef9SBaptiste Daroussin 			if (errno == EINTR)
243e19b2ef9SBaptiste Daroussin 				continue;
244e19b2ef9SBaptiste Daroussin 			err(EXIT_FAILURE, "poll()");
245e19b2ef9SBaptiste Daroussin 		}
246e19b2ef9SBaptiste Daroussin 		hdr = snl_read_message(&ss);
247e19b2ef9SBaptiste Daroussin 		if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR)
248e19b2ef9SBaptiste Daroussin 			parser(&ss, hdr);
249e19b2ef9SBaptiste Daroussin 
250e19b2ef9SBaptiste Daroussin 	}
251e19b2ef9SBaptiste Daroussin 
252e19b2ef9SBaptiste Daroussin 	return (EXIT_SUCCESS);
253e19b2ef9SBaptiste Daroussin }
254e19b2ef9SBaptiste Daroussin 
255e19b2ef9SBaptiste Daroussin int
list_families(int argc,char ** argv __unused)256e19b2ef9SBaptiste Daroussin list_families(int argc, char **argv __unused)
257cb1fc924SBaptiste Daroussin {
258cb1fc924SBaptiste Daroussin 	struct snl_state ss;
259cb1fc924SBaptiste Daroussin 	struct snl_writer nw;
260cb1fc924SBaptiste Daroussin 	struct nlmsghdr *hdr;
261cb1fc924SBaptiste Daroussin 	struct snl_errmsg_data e = {};
262cb1fc924SBaptiste Daroussin 	uint32_t seq_id;
263cb1fc924SBaptiste Daroussin 
264e19b2ef9SBaptiste Daroussin 	if (argc != 0) {
265e19b2ef9SBaptiste Daroussin 		usage();
266e19b2ef9SBaptiste Daroussin 		return (EXIT_FAILURE);
267e19b2ef9SBaptiste Daroussin 	}
268cb1fc924SBaptiste Daroussin 	if (!snl_init(&ss, NETLINK_GENERIC))
269cb1fc924SBaptiste Daroussin 		err(EXIT_FAILURE, "snl_init()");
270cb1fc924SBaptiste Daroussin 
271cb1fc924SBaptiste Daroussin 	snl_init_writer(&ss, &nw);
272e19b2ef9SBaptiste Daroussin 	hdr = snl_create_genl_msg_request(&nw, GENL_ID_CTRL,
273e19b2ef9SBaptiste Daroussin 	    CTRL_CMD_GETFAMILY);
274cb1fc924SBaptiste Daroussin 	if ((hdr = snl_finalize_msg(&nw)) == NULL)
275cb1fc924SBaptiste Daroussin 		err(EXIT_FAILURE, "snl_finalize_msg");
276cb1fc924SBaptiste Daroussin 	seq_id = hdr->nlmsg_seq;
277cb1fc924SBaptiste Daroussin 	if (!snl_send_message(&ss, hdr))
278cb1fc924SBaptiste Daroussin 		err(EXIT_FAILURE, "snl_send_message");
279cb1fc924SBaptiste Daroussin 
280cb1fc924SBaptiste Daroussin 	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
281cb1fc924SBaptiste Daroussin 		if (e.error != 0) {
282cb1fc924SBaptiste Daroussin 			err(EXIT_FAILURE, "Error reading generic netlink");
283cb1fc924SBaptiste Daroussin 		}
284cb1fc924SBaptiste Daroussin 		struct genl_family family = {};
285cb1fc924SBaptiste Daroussin 		if (snl_parse_nlmsg(&ss, hdr, &genl_family_parser, &family))
286cb1fc924SBaptiste Daroussin 			dump_family(&family);
287cb1fc924SBaptiste Daroussin 	}
288cb1fc924SBaptiste Daroussin 
289cb1fc924SBaptiste Daroussin 	return (EXIT_SUCCESS);
290cb1fc924SBaptiste Daroussin }
291e19b2ef9SBaptiste Daroussin 
292e19b2ef9SBaptiste Daroussin int
main(int argc,char ** argv)293e19b2ef9SBaptiste Daroussin main(int argc, char **argv)
294e19b2ef9SBaptiste Daroussin {
295e19b2ef9SBaptiste Daroussin 	if (modfind("netlink") == -1)
296e19b2ef9SBaptiste Daroussin 		err(EXIT_FAILURE, "require netlink module to be loaded");
297e19b2ef9SBaptiste Daroussin 
298e19b2ef9SBaptiste Daroussin 	if (argc == 1)
299e19b2ef9SBaptiste Daroussin 		return (list_families(0, NULL));
300e19b2ef9SBaptiste Daroussin 
301e19b2ef9SBaptiste Daroussin 	for (size_t i = 0; i < nitems(cmds); i++) {
302e19b2ef9SBaptiste Daroussin 		if (strcmp(argv[1], cmds[i].name) == 0)
303e19b2ef9SBaptiste Daroussin 			return (cmds[i].cmd(argc - 2, argv + 2));
304e19b2ef9SBaptiste Daroussin 	}
305e19b2ef9SBaptiste Daroussin 	usage();
306e19b2ef9SBaptiste Daroussin 
307e19b2ef9SBaptiste Daroussin 	return (EXIT_FAILURE);
308e19b2ef9SBaptiste Daroussin }
309