2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright 2023 Baptiste Daroussin <bapt@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted providing that the following conditions~
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.
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.
28 #include <sys/param.h>
29 #include <sys/module.h>
30 #include <sys/socket.h>
39 #include <netlink/netlink.h>
40 #include <netlink/netlink_generic.h>
41 #include <netlink/netlink_snl.h>
42 #include <netlink/netlink_snl_generic.h>
44 static int monitor_mcast(int argc, char **argv);
45 static int list_families(int argc, char **argv);
46 static void parser_nlctrl_notify(struct snl_state *ss, struct nlmsghdr *hdr);
47 static void parser_fallback(struct snl_state *ss, struct nlmsghdr *hdr);
49 static struct commands {
52 int (*cmd)(int argc, char **argv);
54 { "monitor", "monitor <family> <multicast group>", monitor_mcast },
55 { "list", "list", list_families },
58 static struct mcast_parsers {
60 void (*parser)(struct snl_state *ss, struct nlmsghdr *hdr);
61 } mcast_parsers [] = {
62 { "nlctrl", parser_nlctrl_notify },
70 struct genl_ctrl_ops {
72 struct genl_ctrl_op **ops;
75 #define _OUT(_field) offsetof(struct genl_ctrl_op, _field)
76 static struct snl_attr_parser _nla_p_getops[] = {
77 { .type = CTRL_ATTR_OP_ID, .off = _OUT(id), .cb = snl_attr_get_uint32},
78 { .type = CTRL_ATTR_OP_FLAGS, .off = _OUT(flags), .cb = snl_attr_get_uint32 },
81 SNL_DECLARE_ATTR_PARSER_EXT(genl_ctrl_op_parser,
82 sizeof(struct genl_ctrl_op),
91 struct snl_genl_ctrl_mcast_groups mcast_groups;
92 struct genl_ctrl_ops ops;
95 #define _OUT(_field) offsetof(struct genl_family, _field)
96 static struct snl_attr_parser _nla_p_getfamily[] = {
97 { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(id), .cb = snl_attr_get_uint16 },
98 { .type = CTRL_ATTR_FAMILY_NAME, .off = _OUT(name), .cb = snl_attr_get_string },
99 { .type = CTRL_ATTR_VERSION, .off = _OUT(version), .cb = snl_attr_get_uint32 },
100 { .type = CTRL_ATTR_VERSION, .off = _OUT(hdrsize), .cb = snl_attr_get_uint32 },
101 { .type = CTRL_ATTR_MAXATTR, .off = _OUT(max_attr), .cb = snl_attr_get_uint32 },
103 .type = CTRL_ATTR_OPS,
105 .cb = snl_attr_get_parray,
106 .arg = &genl_ctrl_op_parser,
109 .type = CTRL_ATTR_MCAST_GROUPS,
110 .off = _OUT(mcast_groups),
111 .cb = snl_attr_get_parray,
112 .arg = &_genl_ctrl_mc_parser,
116 SNL_DECLARE_GENL_PARSER(genl_family_parser, _nla_p_getfamily);
118 static struct op_capability {
122 { GENL_ADMIN_PERM, "requires admin permission" },
123 { GENL_CMD_CAP_DO, "can modify" },
124 { GENL_CMD_CAP_DUMP, "can get/dump" },
125 { GENL_CMD_CAP_HASPOL, "has policy" },
129 dump_operations(struct genl_ctrl_ops *ops)
131 if (ops->num_ops == 0)
133 printf("\tsupported operations: \n");
134 for (uint32_t i = 0; i < ops->num_ops; i++) {
135 printf("\t - ID: %#02x, Capabilities: %#02x (",
138 for (size_t j = 0; j < nitems(op_caps); j++)
139 if ((ops->ops[i]->flags & op_caps[j].flag) == op_caps[j].flag)
140 printf("%s; ", op_caps[j].str);
146 dump_mcast_groups( struct snl_genl_ctrl_mcast_groups *mcast_groups)
148 if (mcast_groups->num_groups == 0)
150 printf("\tmulticast groups: \n");
151 for (uint32_t i = 0; i < mcast_groups->num_groups; i++)
152 printf("\t - ID: %#02x, Name: %s\n",
153 mcast_groups->groups[i]->mcast_grp_id,
154 mcast_groups->groups[i]->mcast_grp_name);
160 fprintf(stderr, "Usage: %s\n", getprogname());
161 for (size_t i = 0; i < nitems(cmds); i++)
162 fprintf(stderr, " %s %s\n", getprogname(), cmds[i].usage);
166 dump_family(struct genl_family *family)
168 printf("Name: %s\n\tID: %#02hx, Version: %#02x, "
169 "header size: %d, max attributes: %d\n",
170 family->name, family->id, family->version,
171 family->hdrsize, family->max_attr);
172 dump_operations(&family->ops);
173 dump_mcast_groups(&family->mcast_groups);
177 parser_nlctrl_notify(struct snl_state *ss, struct nlmsghdr *hdr)
179 struct genl_family family = {};
181 if (snl_parse_nlmsg(ss, hdr, &genl_family_parser,
183 dump_family(&family);
187 parser_fallback(struct snl_state *ss __unused, struct nlmsghdr *hdr __unused)
189 printf("New unknown message\n");
193 monitor_mcast(int argc __unused, char **argv)
196 struct nlmsghdr *hdr;
197 struct _getfamily_attrs attrs;
200 void (*parser)(struct snl_state *ss, struct nlmsghdr *hdr);
202 parser = parser_fallback;
204 if (!snl_init(&ss, NETLINK_GENERIC))
205 err(EXIT_FAILURE, "snl_init()");
209 return (EXIT_FAILURE);
211 if (!snl_get_genl_family_info(&ss, argv[0], &attrs))
212 errx(EXIT_FAILURE, "Unknown family '%s'", argv[0]);
213 for (uint32_t i = 0; i < attrs.mcast_groups.num_groups; i++) {
214 if (strcmp(attrs.mcast_groups.groups[i]->mcast_grp_name,
217 if (setsockopt(ss.fd, SOL_NETLINK,
218 NETLINK_ADD_MEMBERSHIP,
219 (void *)&attrs.mcast_groups.groups[i]->mcast_grp_id,
220 sizeof(attrs.mcast_groups.groups[i]->mcast_grp_id))
222 err(EXIT_FAILURE, "Cannot subscribe to command "
228 errx(EXIT_FAILURE, "No such multicat group '%s'"
229 " in family '%s'", argv[1], argv[0]);
230 for (size_t i= 0; i < nitems(mcast_parsers); i++) {
231 if (strcmp(mcast_parsers[i].family, argv[0]) == 0) {
232 parser = mcast_parsers[i].parser;
236 memset(&pfd, 0, sizeof(pfd));
238 pfd.events = POLLIN | POLLERR;
241 if (poll(&pfd, 1, -1) == -1) {
244 err(EXIT_FAILURE, "poll()");
246 hdr = snl_read_message(&ss);
247 if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR)
252 return (EXIT_SUCCESS);
256 list_families(int argc, char **argv __unused)
259 struct snl_writer nw;
260 struct nlmsghdr *hdr;
261 struct snl_errmsg_data e = {};
266 return (EXIT_FAILURE);
268 if (!snl_init(&ss, NETLINK_GENERIC))
269 err(EXIT_FAILURE, "snl_init()");
271 snl_init_writer(&ss, &nw);
272 hdr = snl_create_genl_msg_request(&nw, GENL_ID_CTRL,
274 if ((hdr = snl_finalize_msg(&nw)) == NULL)
275 err(EXIT_FAILURE, "snl_finalize_msg");
276 seq_id = hdr->nlmsg_seq;
277 if (!snl_send_message(&ss, hdr))
278 err(EXIT_FAILURE, "snl_send_message");
280 while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
282 err(EXIT_FAILURE, "Error reading generic netlink");
284 struct genl_family family = {};
285 if (snl_parse_nlmsg(&ss, hdr, &genl_family_parser, &family))
286 dump_family(&family);
289 return (EXIT_SUCCESS);
293 main(int argc, char **argv)
295 if (modfind("netlink") == -1)
296 err(EXIT_FAILURE, "require netlink module to be loaded");
299 return (list_families(0, NULL));
301 for (size_t i = 0; i < nitems(cmds); i++) {
302 if (strcmp(argv[1], cmds[i].name) == 0)
303 return (cmds[i].cmd(argc - 2, argv + 2));
307 return (EXIT_FAILURE);