From 65018ae43b14c8157bbe05473d76635626177b87 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Tue, 8 Aug 2006 12:13:34 -0700 Subject: [PATCH] This patch adds a generic netlink controller interface. The controller is the only module using this at the moment. Thomas has a sample user of genetlink that would fit here; bug him for it. Signed-off-by: Stephen Hemminger --- Makefile | 2 +- genl/Makefile | 22 +++ genl/ctrl.c | 284 ++++++++++++++++++++++++++++++++++++++ genl/genl.c | 162 ++++++++++++++++++++++ genl/genl_utils.h | 22 +++ include/linux/genetlink.h | 51 +++++++ 6 files changed, 542 insertions(+), 1 deletion(-) create mode 100644 genl/Makefile create mode 100644 genl/ctrl.c create mode 100644 genl/genl.c create mode 100644 genl/genl_utils.h create mode 100644 include/linux/genetlink.h diff --git a/Makefile b/Makefile index ac58cd97..3fc3a155 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ YACCFLAGS = -d -t -v LDLIBS += -L../lib -lnetlink -lutil -SUBDIRS=lib ip tc misc netem +SUBDIRS=lib ip tc misc netem genl LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a diff --git a/genl/Makefile b/genl/Makefile new file mode 100644 index 00000000..4436508d --- /dev/null +++ b/genl/Makefile @@ -0,0 +1,22 @@ +GENLOBJ=genl.o + +include ../Config + +GENLMODULES := +GENLMODULES += ctrl.o + +GENLOBJ += $(GENLMODULES) + +GENLLIB := + +LDFLAGS += -Wl,-export-dynamic -lm -ldl + +all: genl + +genl: $(GENLOBJ) $(LIBNETLINK) $(LIBUTIL) $(GENLLIB) + +install: all + install -m 0755 -s genl $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(GENLOBJ) $(GENLLIB) genl diff --git a/genl/ctrl.c b/genl/ctrl.c new file mode 100644 index 00000000..0d4750d3 --- /dev/null +++ b/genl/ctrl.c @@ -0,0 +1,284 @@ +/* + * 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) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "genl_utils.h" + +static int usage(void) +{ + fprintf(stderr,"Usage: ctrl \n" \ + "CMD := get | list | monitor\n" \ + "PARMS := name | id \n" \ + "Examples:\n" \ + "\tctrl ls\n" \ + "\tctrl monitor\n" \ + "\tctrl get name foobar\n" \ + "\tctrl get id 0xF\n"); + return -1; +} + +int genl_ctrl_resolve_family(const char *family) +{ + struct rtnl_handle rth; + struct nlmsghdr *nlh; + struct genlmsghdr *ghdr; + int ret = 0; + struct { + struct nlmsghdr n; + char buf[4096]; + } req; + + memset(&req, 0, sizeof(req)); + + nlh = &req.n; + nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_type = GENL_ID_CTRL; + + ghdr = NLMSG_DATA(&req.n); + ghdr->cmd = CTRL_CMD_GETFAMILY; + + if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) { + fprintf(stderr, "Cannot open generic netlink socket\n"); + exit(1); + } + + addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME, family, strlen(family) + 1); + + if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) { + fprintf(stderr, "Error talking to the kernel\n"); + goto errout; + } + + { + struct rtattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *ghdr = NLMSG_DATA(nlh); + int len = nlh->nlmsg_len; + struct rtattr *attrs; + + if (nlh->nlmsg_type != GENL_ID_CTRL) { + fprintf(stderr, "Not a controller message, nlmsg_len=%d " + "nlmsg_type=0x%x\n", nlh->nlmsg_len, nlh->nlmsg_type); + goto errout; + } + + if (ghdr->cmd != CTRL_CMD_NEWFAMILY) { + fprintf(stderr, "Unkown controller command %d\n", ghdr->cmd); + goto errout; + } + + 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(tb, CTRL_ATTR_MAX, attrs, len); + + if (tb[CTRL_ATTR_FAMILY_ID] == NULL) { + fprintf(stderr, "Missing family id TLV\n"); + goto errout; + } + + ret = *(__u16 *) RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]); + } + +errout: + rtnl_close(&rth); + return ret; +} + +static int print_ctrl(const struct sockaddr_nl *who, 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) { + fprintf(stderr, "Unkown 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(tb, CTRL_ATTR_MAX, attrs, len); + + if (tb[CTRL_ATTR_FAMILY_NAME]) { + char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]); + fprintf(fp, "Name: %s\n",name); + } + if (tb[CTRL_ATTR_FAMILY_ID]) { + __u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]); + fprintf(fp, "ID: 0x%x\n",*id); + } + + fflush(fp); + return 0; +} + +static int ctrl_list(int cmd, int argc, char **argv) +{ + struct rtnl_handle rth; + struct nlmsghdr *nlh; + struct genlmsghdr *ghdr; + int ret = -1; + char d[GENL_NAMSIZ]; + struct { + struct nlmsghdr n; + char buf[4096]; + } req; + + memset(&req, 0, sizeof(req)); + + nlh = &req.n; + nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_type = GENL_ID_CTRL; + + ghdr = NLMSG_DATA(&req.n); + ghdr->cmd = CTRL_CMD_GETFAMILY; + + if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) { + fprintf(stderr, "Cannot open generic netlink socket\n"); + exit(1); + } + + if (cmd == CTRL_CMD_GETFAMILY) { + if (argc != 2) { + fprintf(stderr, "Wrong number of params\n"); + return -1; + } + + if (matches(*argv, "name") == 0) { + NEXT_ARG(); + strncpy(d, *argv, sizeof (d) - 1); + 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 (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) { + fprintf(stderr, "Error talking to the kernel\n"); + goto ctrl_done; + } + + if (print_ctrl(NULL, nlh, (void *) stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + goto ctrl_done; + } + + } + + if (cmd == CTRL_CMD_UNSPEC) { + nlh->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + nlh->nlmsg_seq = rth.dump = ++rth.seq; + + if (rtnl_send(&rth, (const char *) nlh, nlh->nlmsg_len) < 0) { + perror("Failed to send dump request\n"); + goto ctrl_done; + } + + rtnl_dump_filter(&rth, print_ctrl, stdout, NULL, NULL); + + } + + ret = 0; +ctrl_done: + 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, "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_ctrl, +}; diff --git a/genl/genl.c b/genl/genl.c new file mode 100644 index 00000000..3ae75aec --- /dev/null +++ b/genl/genl.c @@ -0,0 +1,162 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* until we put our own header */ +#include "SNAPSHOT.h" +#include "utils.h" +#include "genl_utils.h" + +int show_stats = 0; +int show_details = 0; +int show_raw = 0; +int resolve_hosts = 0; + +static void *BODY; +static struct genl_util * genl_list; + + +static int print_nofopt(const struct sockaddr_nl *who, 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(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 = malloc(sizeof(*f)); + if (f) { + memset(f, 0, sizeof(*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] }\n"); + exit(-1); +} + +int main(int argc, char **argv) +{ + char *basename; + + basename = strrchr(argv[0], '/'); + if (basename == NULL) + basename = argv[0]; + else + basename++; + + 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-ss%s\n", SNAPSHOT); + 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) { + int ret; + struct genl_util *a = NULL; + a = get_genl_kind(argv[1]); + if (NULL == a) { + fprintf(stderr,"bad genl %s\n",argv[1]); + } + + ret = a->parse_genlopt(a, argc-1, argv+1); + return ret; + + if (matches(argv[1], "help") == 0) + usage(); + fprintf(stderr, "Object \"%s\" is unknown, try \"genl " + "-help\".\n", argv[1]); + exit(-1); + } + + usage(); +} diff --git a/genl/genl_utils.h b/genl/genl_utils.h new file mode 100644 index 00000000..2f2314b1 --- /dev/null +++ b/genl/genl_utils.h @@ -0,0 +1,22 @@ +#ifndef _TC_UTIL_H_ +#define _TC_UTIL_H_ 1 + +#include "linux/genetlink.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)(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); +}; + +extern int genl_ctrl_resolve_family(const char *family); + +/* seems to have dissapeared from netlink.h */ +static inline __u32 nl_mgrp(__u32 group) +{ + return group ? 1 << group : 0; +} + +#endif diff --git a/include/linux/genetlink.h b/include/linux/genetlink.h new file mode 100644 index 00000000..84f12a41 --- /dev/null +++ b/include/linux/genetlink.h @@ -0,0 +1,51 @@ +#ifndef __LINUX_GENERIC_NETLINK_H +#define __LINUX_GENERIC_NETLINK_H + +#include + +#define GENL_NAMSIZ 16 /* length of family name */ + +#define GENL_MIN_ID NLMSG_MIN_TYPE +#define GENL_MAX_ID 1023 + +struct genlmsghdr { + __u8 cmd; + __u8 version; + __u16 reserved; +}; + +#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr)) + +/* + * List of reserved static generic netlink identifiers: + */ +#define GENL_ID_GENERATE 0 +#define GENL_ID_CTRL NLMSG_MIN_TYPE + +/************************************************************************** + * Controller + **************************************************************************/ + +enum { + CTRL_CMD_UNSPEC, + CTRL_CMD_NEWFAMILY, + CTRL_CMD_DELFAMILY, + CTRL_CMD_GETFAMILY, + CTRL_CMD_NEWOPS, + CTRL_CMD_DELOPS, + CTRL_CMD_GETOPS, + __CTRL_CMD_MAX, +}; + +#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1) + +enum { + CTRL_ATTR_UNSPEC, + CTRL_ATTR_FAMILY_ID, + CTRL_ATTR_FAMILY_NAME, + __CTRL_ATTR_MAX, +}; + +#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1) + +#endif /* __LINUX_GENERIC_NETLINK_H */ -- 2.39.2