summaryrefslogtreecommitdiffstats
path: root/genl/ctrl.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--genl/ctrl.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/genl/ctrl.c b/genl/ctrl.c
new file mode 100644
index 0000000..72a9b01
--- /dev/null
+++ b/genl/ctrl.c
@@ -0,0 +1,374 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ctrl.c generic netlink controller
+ *
+ * Authors: J Hadi Salim (hadi@cyberus.ca)
+ * Johannes Berg (johannes@sipsolutions.net)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "utils.h"
+#include "genl_utils.h"
+
+#define GENL_MAX_FAM_OPS 256
+#define GENL_MAX_FAM_GRPS 256
+
+static int usage(void)
+{
+ fprintf(stderr,"Usage: ctrl <CMD>\n" \
+ "CMD := get <PARMS> | list | monitor | policy <PARMS>\n" \
+ "PARMS := name <name> | id <id>\n" \
+ "Examples:\n" \
+ "\tctrl ls\n" \
+ "\tctrl monitor\n" \
+ "\tctrl get name foobar\n" \
+ "\tctrl get id 0xF\n"
+ "\tctrl policy name foobar\n"
+ "\tctrl policy id 0xF\n");
+ return -1;
+}
+
+static void print_ctrl_cmd_flags(FILE *fp, __u32 fl)
+{
+ fprintf(fp, "\n\t\tCapabilities (0x%x):\n ", fl);
+ if (!fl) {
+ fprintf(fp, "\n");
+ return;
+ }
+ fprintf(fp, "\t\t ");
+
+ if (fl & GENL_ADMIN_PERM)
+ fprintf(fp, " requires admin permission;");
+ if (fl & GENL_CMD_CAP_DO)
+ fprintf(fp, " can doit;");
+ if (fl & GENL_CMD_CAP_DUMP)
+ fprintf(fp, " can dumpit;");
+ if (fl & GENL_CMD_CAP_HASPOL)
+ fprintf(fp, " has policy");
+
+ fprintf(fp, "\n");
+}
+
+static int print_ctrl_cmds(FILE *fp, struct rtattr *arg)
+{
+ struct rtattr *tb[CTRL_ATTR_OP_MAX + 1];
+
+ if (arg == NULL)
+ return -1;
+
+ parse_rtattr_nested(tb, CTRL_ATTR_OP_MAX, arg);
+ if (tb[CTRL_ATTR_OP_ID]) {
+ __u32 *id = RTA_DATA(tb[CTRL_ATTR_OP_ID]);
+ fprintf(fp, " ID-0x%x ",*id);
+ }
+ /* we are only gonna do this for newer version of the controller */
+ if (tb[CTRL_ATTR_OP_FLAGS]) {
+ __u32 *fl = RTA_DATA(tb[CTRL_ATTR_OP_FLAGS]);
+ print_ctrl_cmd_flags(fp, *fl);
+ }
+ return 0;
+
+}
+
+static int print_ctrl_grp(FILE *fp, struct rtattr *arg)
+{
+ struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
+
+ if (arg == NULL)
+ return -1;
+
+ parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
+ if (tb[2]) {
+ __u32 *id = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]);
+ fprintf(fp, " ID-0x%x ",*id);
+ }
+ if (tb[1]) {
+ char *name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
+ fprintf(fp, " name: %s ", name);
+ }
+ return 0;
+
+}
+
+/*
+ * The controller sends one nlmsg per family
+*/
+static int print_ctrl(struct rtnl_ctrl_data *ctrl,
+ struct nlmsghdr *n, void *arg)
+{
+ struct rtattr *tb[CTRL_ATTR_MAX + 1];
+ struct genlmsghdr *ghdr = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr *attrs;
+ FILE *fp = (FILE *) arg;
+
+ if (n->nlmsg_type != GENL_ID_CTRL) {
+ fprintf(stderr, "Not a controller message, nlmsg_len=%d "
+ "nlmsg_type=0x%x\n", n->nlmsg_len, n->nlmsg_type);
+ return 0;
+ }
+
+ if (ghdr->cmd != CTRL_CMD_GETFAMILY &&
+ ghdr->cmd != CTRL_CMD_DELFAMILY &&
+ ghdr->cmd != CTRL_CMD_NEWFAMILY &&
+ ghdr->cmd != CTRL_CMD_NEWMCAST_GRP &&
+ ghdr->cmd != CTRL_CMD_DELMCAST_GRP &&
+ ghdr->cmd != CTRL_CMD_GETPOLICY) {
+ fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
+ return 0;
+ }
+
+ len -= NLMSG_LENGTH(GENL_HDRLEN);
+
+ if (len < 0) {
+ fprintf(stderr, "wrong controller message len %d\n", len);
+ return -1;
+ }
+
+ attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
+ parse_rtattr_flags(tb, CTRL_ATTR_MAX, attrs, len, NLA_F_NESTED);
+
+ if (tb[CTRL_ATTR_FAMILY_NAME]) {
+ char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]);
+ fprintf(fp, "\nName: %s\n",name);
+ }
+ if (tb[CTRL_ATTR_FAMILY_ID]) {
+ __u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
+ fprintf(fp, "\tID: 0x%x ",*id);
+ }
+ if (tb[CTRL_ATTR_VERSION]) {
+ __u32 *v = RTA_DATA(tb[CTRL_ATTR_VERSION]);
+ fprintf(fp, " Version: 0x%x ",*v);
+ }
+ if (tb[CTRL_ATTR_HDRSIZE]) {
+ __u32 *h = RTA_DATA(tb[CTRL_ATTR_HDRSIZE]);
+ fprintf(fp, " header size: %d ",*h);
+ }
+ if (tb[CTRL_ATTR_MAXATTR]) {
+ __u32 *ma = RTA_DATA(tb[CTRL_ATTR_MAXATTR]);
+ fprintf(fp, " max attribs: %d ",*ma);
+ }
+ if (tb[CTRL_ATTR_OP_POLICY]) {
+ const struct rtattr *pos;
+
+ rtattr_for_each_nested(pos, tb[CTRL_ATTR_OP_POLICY]) {
+ struct rtattr *ptb[CTRL_ATTR_POLICY_DUMP_MAX + 1];
+ struct rtattr *pattrs = RTA_DATA(pos);
+ int plen = RTA_PAYLOAD(pos);
+
+ parse_rtattr_flags(ptb, CTRL_ATTR_POLICY_DUMP_MAX,
+ pattrs, plen, NLA_F_NESTED);
+
+ fprintf(fp, " op %d policies:",
+ pos->rta_type & ~NLA_F_NESTED);
+
+ if (ptb[CTRL_ATTR_POLICY_DO]) {
+ __u32 *v = RTA_DATA(ptb[CTRL_ATTR_POLICY_DO]);
+
+ fprintf(fp, " do=%d", *v);
+ }
+
+ if (ptb[CTRL_ATTR_POLICY_DUMP]) {
+ __u32 *v = RTA_DATA(ptb[CTRL_ATTR_POLICY_DUMP]);
+
+ fprintf(fp, " dump=%d", *v);
+ }
+ }
+ }
+ if (tb[CTRL_ATTR_POLICY])
+ nl_print_policy(tb[CTRL_ATTR_POLICY], fp);
+
+ /* end of family definitions .. */
+ fprintf(fp,"\n");
+ if (tb[CTRL_ATTR_OPS]) {
+ struct rtattr *tb2[GENL_MAX_FAM_OPS];
+ int i=0;
+ parse_rtattr_nested(tb2, GENL_MAX_FAM_OPS, tb[CTRL_ATTR_OPS]);
+ fprintf(fp, "\tcommands supported: \n");
+ for (i = 0; i < GENL_MAX_FAM_OPS; i++) {
+ if (tb2[i]) {
+ fprintf(fp, "\t\t#%d: ", i);
+ if (0 > print_ctrl_cmds(fp, tb2[i])) {
+ fprintf(fp, "Error printing command\n");
+ }
+ /* for next command */
+ fprintf(fp,"\n");
+ }
+ }
+
+ /* end of family::cmds definitions .. */
+ fprintf(fp,"\n");
+ }
+
+ if (tb[CTRL_ATTR_MCAST_GROUPS]) {
+ struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
+ int i;
+
+ parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
+ tb[CTRL_ATTR_MCAST_GROUPS]);
+ fprintf(fp, "\tmulticast groups:\n");
+
+ for (i = 0; i < GENL_MAX_FAM_GRPS; i++) {
+ if (tb2[i]) {
+ fprintf(fp, "\t\t#%d: ", i);
+ if (0 > print_ctrl_grp(fp, tb2[i]))
+ fprintf(fp, "Error printing group\n");
+ /* for next group */
+ fprintf(fp,"\n");
+ }
+ }
+
+ /* end of family::groups definitions .. */
+ fprintf(fp,"\n");
+ }
+
+ fflush(fp);
+ return 0;
+}
+
+static int print_ctrl2(struct nlmsghdr *n, void *arg)
+{
+ return print_ctrl(NULL, n, arg);
+}
+
+static int ctrl_list(int cmd, int argc, char **argv)
+{
+ struct rtnl_handle rth;
+ int ret = -1;
+ char d[GENL_NAMSIZ];
+ struct {
+ struct nlmsghdr n;
+ struct genlmsghdr g;
+ char buf[4096];
+ } req = {
+ .n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN),
+ .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+ .n.nlmsg_type = GENL_ID_CTRL,
+ .g.cmd = CTRL_CMD_GETFAMILY,
+ };
+ struct nlmsghdr *nlh = &req.n;
+ struct nlmsghdr *answer = NULL;
+
+ if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
+ fprintf(stderr, "Cannot open generic netlink socket\n");
+ exit(1);
+ }
+
+ if (cmd == CTRL_CMD_GETFAMILY || cmd == CTRL_CMD_GETPOLICY) {
+ req.g.cmd = cmd;
+
+ if (argc != 2) {
+ fprintf(stderr, "Wrong number of params\n");
+ goto ctrl_done;
+ }
+
+ if (matches(*argv, "name") == 0) {
+ NEXT_ARG();
+ strlcpy(d, *argv, sizeof(d));
+ addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
+ d, strlen(d) + 1);
+ } else if (matches(*argv, "id") == 0) {
+ __u16 id;
+ NEXT_ARG();
+ if (get_u16(&id, *argv, 0)) {
+ fprintf(stderr, "Illegal \"id\"\n");
+ goto ctrl_done;
+ }
+
+ addattr_l(nlh, 128, CTRL_ATTR_FAMILY_ID, &id, 2);
+
+ } else {
+ fprintf(stderr, "Wrong params\n");
+ goto ctrl_done;
+ }
+ }
+
+ if (cmd == CTRL_CMD_GETFAMILY) {
+ if (rtnl_talk(&rth, nlh, &answer) < 0) {
+ fprintf(stderr, "Error talking to the kernel\n");
+ goto ctrl_done;
+ }
+
+ if (print_ctrl2(answer, (void *) stdout) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ goto ctrl_done;
+ }
+
+ }
+
+ if (cmd == CTRL_CMD_UNSPEC || cmd == CTRL_CMD_GETPOLICY) {
+ nlh->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ nlh->nlmsg_seq = rth.dump = ++rth.seq;
+
+ if (rtnl_send(&rth, nlh, nlh->nlmsg_len) < 0) {
+ perror("Failed to send dump request\n");
+ goto ctrl_done;
+ }
+
+ rtnl_dump_filter(&rth, print_ctrl2, stdout);
+
+ }
+
+ ret = 0;
+ctrl_done:
+ free(answer);
+ rtnl_close(&rth);
+ return ret;
+}
+
+static int ctrl_listen(int argc, char **argv)
+{
+ struct rtnl_handle rth;
+
+ if (rtnl_open_byproto(&rth, nl_mgrp(GENL_ID_CTRL), NETLINK_GENERIC) < 0) {
+ fprintf(stderr, "Cannot open generic netlink socket\n");
+ return -1;
+ }
+
+ if (rtnl_listen(&rth, print_ctrl, (void *) stdout) < 0)
+ exit(2);
+
+ rtnl_close(&rth);
+ return 0;
+}
+
+static int parse_ctrl(struct genl_util *a, int argc, char **argv)
+{
+ argv++;
+ if (--argc <= 0) {
+ fprintf(stderr, "wrong controller params\n");
+ return -1;
+ }
+
+ if (matches(*argv, "monitor") == 0)
+ return ctrl_listen(argc-1, argv+1);
+ if (matches(*argv, "get") == 0)
+ return ctrl_list(CTRL_CMD_GETFAMILY, argc-1, argv+1);
+ if (matches(*argv, "list") == 0 ||
+ matches(*argv, "show") == 0 ||
+ matches(*argv, "lst") == 0)
+ return ctrl_list(CTRL_CMD_UNSPEC, argc-1, argv+1);
+ if (matches(*argv, "policy") == 0)
+ return ctrl_list(CTRL_CMD_GETPOLICY, argc-1, argv+1);
+ if (matches(*argv, "help") == 0)
+ return usage();
+
+ fprintf(stderr, "ctrl command \"%s\" is unknown, try \"ctrl help\".\n",
+ *argv);
+
+ return -1;
+}
+
+struct genl_util ctrl_genl_util = {
+ .name = "ctrl",
+ .parse_genlopt = parse_ctrl,
+ .print_genlopt = print_ctrl2,
+};