diff options
Diffstat (limited to '')
-rw-r--r-- | genl/.gitignore | 1 | ||||
-rw-r--r-- | genl/Makefile | 40 | ||||
-rw-r--r-- | genl/ctrl.c | 379 | ||||
-rw-r--r-- | genl/genl.c | 149 | ||||
-rw-r--r-- | genl/genl_utils.h | 15 | ||||
-rw-r--r-- | genl/static-syms.c | 15 |
6 files changed, 599 insertions, 0 deletions
diff --git a/genl/.gitignore b/genl/.gitignore new file mode 100644 index 0000000..383ef04 --- /dev/null +++ b/genl/.gitignore @@ -0,0 +1 @@ +genl diff --git a/genl/Makefile b/genl/Makefile new file mode 100644 index 0000000..0bd27ff --- /dev/null +++ b/genl/Makefile @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0 +GENLOBJ=genl.o + +include ../config.mk +SHARED_LIBS ?= y + +CFLAGS += -fno-strict-aliasing + +GENLMODULES := +GENLMODULES += ctrl.o + +GENLOBJ += $(GENLMODULES) + +ifeq ($(SHARED_LIBS),y) +LDFLAGS += -Wl,-export-dynamic +LDLIBS += -lm -ldl +endif + +all: genl + +genl: $(GENLOBJ) $(LIBNETLINK) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ + +install: all + install -m 0755 genl $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(GENLOBJ) genl + +ifneq ($(SHARED_LIBS),y) + +genl: static-syms.o +static-syms.o: static-syms.h +static-syms.h: $(wildcard *.c) + files="$^" ; \ + for s in `grep -B 3 '\<dlsym' $$files | sed -n '/snprintf/{s:.*"\([^"]*\)".*:\1:;s:%s::;p}'` ; do \ + sed -n '/'$$s'[^ ]* =/{s:.* \([^ ]*'$$s'[^ ]*\) .*:extern char \1[] __attribute__((weak)); if (!strcmp(sym, "\1")) return \1;:;p}' $$files ; \ + done > $@ + +endif diff --git a/genl/ctrl.c b/genl/ctrl.c new file mode 100644 index 0000000..549bc66 --- /dev/null +++ b/genl/ctrl.c @@ -0,0 +1,379 @@ +/* + * ctrl.c generic netlink controller + * + * This program is free software; you can distribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 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, __u32 ctrl_ver) +{ + 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] && ctrl_ver >= 0x2) { + __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, __u32 ctrl_ver) +{ + 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; + __u32 ctrl_v = 0x1; + + 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); + ctrl_v = *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], ctrl_v)) { + 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], ctrl_v)) + 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"); + return -1; + } + + 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, "Canot open generic netlink socket\n"); + return -1; + } + + if (rtnl_listen(&rth, print_ctrl, (void *) stdout) < 0) + return -1; + + 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, +}; diff --git a/genl/genl.c b/genl/genl.c new file mode 100644 index 0000000..6557e6b --- /dev/null +++ b/genl/genl.c @@ -0,0 +1,149 @@ +/* + * genl.c "genl" utility frontend. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Jamal Hadi Salim + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <dlfcn.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <errno.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> /* until we put our own header */ +#include "version.h" +#include "utils.h" +#include "genl_utils.h" + +int show_stats; +int show_details; +int show_raw; + +static void *BODY; +static struct genl_util *genl_list; + + +static int print_nofopt(struct nlmsghdr *n, void *arg) +{ + fprintf((FILE *) arg, "unknown genl type ..\n"); + return 0; +} + +static int parse_nofopt(struct genl_util *f, int argc, char **argv) +{ + if (argc) { + fprintf(stderr, + "Unknown genl \"%s\", hence option \"%s\" is unparsable\n", + f->name, *argv); + return -1; + } + + return 0; +} + +static struct genl_util *get_genl_kind(const char *str) +{ + void *dlh; + char buf[256]; + struct genl_util *f; + + for (f = genl_list; f; f = f->next) + if (strcmp(f->name, str) == 0) + return f; + + snprintf(buf, sizeof(buf), "%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (dlh == NULL) { + dlh = BODY; + if (dlh == NULL) { + dlh = BODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "%s_genl_util", str); + + f = dlsym(dlh, buf); + if (f == NULL) + goto noexist; +reg: + f->next = genl_list; + genl_list = f; + return f; + +noexist: + f = calloc(1, sizeof(*f)); + if (f) { + strncpy(f->name, str, 15); + f->parse_genlopt = parse_nofopt; + f->print_genlopt = print_nofopt; + goto reg; + } + return f; +} + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: genl [ OPTIONS ] OBJECT [help] }\n" + "where OBJECT := { ctrl etc }\n" + " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -V[ersion] | -h[elp] }\n"); + exit(-1); +} + +int main(int argc, char **argv) +{ + while (argc > 1) { + if (argv[1][0] != '-') + break; + if (matches(argv[1], "-stats") == 0 || + matches(argv[1], "-statistics") == 0) { + ++show_stats; + } else if (matches(argv[1], "-details") == 0) { + ++show_details; + } else if (matches(argv[1], "-raw") == 0) { + ++show_raw; + } else if (matches(argv[1], "-Version") == 0) { + printf("genl utility, iproute2-%s\n", version); + exit(0); + } else if (matches(argv[1], "-help") == 0) { + usage(); + } else { + fprintf(stderr, + "Option \"%s\" is unknown, try \"genl -help\".\n", + argv[1]); + exit(-1); + } + argc--; argv++; + } + + if (argc > 1) { + struct genl_util *a; + int ret; + + a = get_genl_kind(argv[1]); + if (!a) { + fprintf(stderr, "bad genl %s\n", argv[1]); + exit(-1); + } + + ret = a->parse_genlopt(a, argc-1, argv+1); + return ret; + } + + usage(); +} diff --git a/genl/genl_utils.h b/genl/genl_utils.h new file mode 100644 index 0000000..9fbeba7 --- /dev/null +++ b/genl/genl_utils.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _GENL_UTILS_H_ +#define _GENL_UTILS_H_ 1 + +#include <linux/genetlink.h> +#include "utils.h" + +struct genl_util { + struct genl_util *next; + char name[16]; + int (*parse_genlopt)(struct genl_util *fu, int argc, char **argv); + int (*print_genlopt)(struct nlmsghdr *n, void *arg); +}; + +#endif diff --git a/genl/static-syms.c b/genl/static-syms.c new file mode 100644 index 0000000..47c4092 --- /dev/null +++ b/genl/static-syms.c @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file creates a dummy version of dynamic loading + * for environments where dynamic linking + * is not used or available. + */ + +#include <string.h> +#include "dlfcn.h" + +void *_dlsym(const char *sym) +{ +#include "static-syms.h" + return NULL; +} |