diff --git a/usr.bin/genl/genl.1 b/usr.bin/genl/genl.1 --- a/usr.bin/genl/genl.1 +++ b/usr.bin/genl/genl.1 @@ -24,23 +24,44 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 20, 2023 +.Dd Oct 5, 2023 .Dt GENL 1 .Os .Sh NAME .Nm genl -.Nd "generic netlink list" +.Nd "generic netlink" .Sh SYNOPSIS .Nm +.Pp +.Nm Cm list +.Pp +.Nm Cm monitor Ao family Ac Ao multicast group Ac .Sh DESCRIPTION +The .Nm -lists all available generic netlink protocols, and presents its details: +utility is design to provide access to the user to generic netlink +protocols. +.Pp +The following commands are available: +.Bl -tag -ident +.It Cm list Po default Pc +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 +.It Cm monitor Ao family Ac Ao multicast group Ac +Connect to the +.Ar family +protocol and subscribe to the +.Ar mulicast group +then print the received messages in a readable format if the protocol is known. +So far only +.Qq nlctrl +is known. +.El .Sh SEE ALSO .Xr genetlink 4 , .Xr netlink 4 diff --git a/usr.bin/genl/genl.c b/usr.bin/genl/genl.c --- a/usr.bin/genl/genl.c +++ b/usr.bin/genl/genl.c @@ -27,18 +27,41 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include #include +static int monitor_mcast(int argc, char **argv); +static int list_families(int argc, char **argv); +static void parser_nlctrl_notify(struct snl_state *ss, struct nlmsghdr *hdr); +static void parser_fallback(struct snl_state *ss, struct nlmsghdr *hdr); + +static struct commands { + const char *name; + const char *usage; + int (*cmd)(int argc, char **argv); +} cmds[] = { + { "monitor", "monitor ", monitor_mcast }, + { "list", "list", list_families }, +}; + +static struct mcast_parsers { + const char *family; + void (*parser)(struct snl_state *ss, struct nlmsghdr *hdr); +} mcast_parsers [] = { + { "nlctrl", parser_nlctrl_notify }, +}; + struct genl_ctrl_op { uint32_t id; uint32_t flags; @@ -131,6 +154,13 @@ mcast_groups->groups[i]->mcast_grp_name); } +static void +usage(void) +{ + fprintf(stderr, "Usage: %s\n", getprogname()); + for (size_t i = 0; i < nitems(cmds); i++) + fprintf(stderr, " %s %s\n", getprogname(), cmds[i].usage); +} static void dump_family(struct genl_family *family) @@ -143,8 +173,87 @@ dump_mcast_groups(&family->mcast_groups); } +void +parser_nlctrl_notify(struct snl_state *ss, struct nlmsghdr *hdr) +{ + struct genl_family family = {}; + + if (snl_parse_nlmsg(ss, hdr, &genl_family_parser, + &family)) + dump_family(&family); +} + +void +parser_fallback(struct snl_state *ss __unused, struct nlmsghdr *hdr __unused) +{ + printf("New unknown message\n"); +} + +int +monitor_mcast(int argc __unused, char **argv) +{ + struct snl_state ss; + struct nlmsghdr *hdr; + struct _getfamily_attrs attrs; + struct pollfd pfd; + bool found = false; + void (*parser)(struct snl_state *ss, struct nlmsghdr *hdr); + + parser = parser_fallback; + + if (!snl_init(&ss, NETLINK_GENERIC)) + err(EXIT_FAILURE, "snl_init()"); + + if (argc != 2) { + usage(); + return (EXIT_FAILURE); + } + if (!snl_get_genl_family_info(&ss, argv[0], &attrs)) + errx(EXIT_FAILURE, "Unknown family '%s'", argv[0]); + for (uint32_t i = 0; i < attrs.mcast_groups.num_groups; i++) { + if (strcmp(attrs.mcast_groups.groups[i]->mcast_grp_name, + argv[1]) == 0) { + found = true; + if (setsockopt(ss.fd, SOL_NETLINK, + NETLINK_ADD_MEMBERSHIP, + (void *)&attrs.mcast_groups.groups[i]->mcast_grp_id, + sizeof(attrs.mcast_groups.groups[i]->mcast_grp_id)) + == -1) + err(EXIT_FAILURE, "Cannot subscribe to command " + "notify"); + break; + } + } + if (!found) + errx(EXIT_FAILURE, "No such multicat group '%s'" + " in family '%s'", argv[1], argv[0]); + for (size_t i= 0; i < nitems(mcast_parsers); i++) { + if (strcmp(mcast_parsers[i].family, argv[0]) == 0) { + parser = mcast_parsers[i].parser; + break; + } + } + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = ss.fd; + pfd.events = POLLIN | POLLERR; + while (true) { + pfd.revents = 0; + if (poll(&pfd, 1, -1) == -1) { + if (errno == EINTR) + continue; + err(EXIT_FAILURE, "poll()"); + } + hdr = snl_read_message(&ss); + if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR) + parser(&ss, hdr); + + } + + return (EXIT_SUCCESS); +} + int -main(int argc, char **argv __unused) +list_families(int argc, char **argv __unused) { struct snl_state ss; struct snl_writer nw; @@ -152,16 +261,16 @@ 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 (argc != 0) { + usage(); + return (EXIT_FAILURE); + } 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); + 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; @@ -179,3 +288,21 @@ return (EXIT_SUCCESS); } + +int +main(int argc, char **argv) +{ + if (modfind("netlink") == -1) + err(EXIT_FAILURE, "require netlink module to be loaded"); + + if (argc == 1) + return (list_families(0, NULL)); + + for (size_t i = 0; i < nitems(cmds); i++) { + if (strcmp(argv[1], cmds[i].name) == 0) + return (cmds[i].cmd(argc - 2, argv + 2)); + } + usage(); + + return (EXIT_FAILURE); +}