Hi Randy, On Wed, Feb 07, 2018 at 05:28:20PM -0800, Randy Dunlap wrote: [...] > > diff --git a/include/net/nldesc.h b/include/net/nldesc.h > > new file mode 100644 > > index 000000000000..19306a648f10 > > --- /dev/null > > +++ b/include/net/nldesc.h > > @@ -0,0 +1,160 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +#ifndef __NET_NLDESC_H > > +#define __NET_NLDESC_H > > + > > +#include <linux/types.h> > > + > > +struct nl_desc_cmd; > > +struct nl_desc_obj; > > + > > +struct nl_desc_cmds { > > + int max; > > + const struct nl_desc_cmd *table; > > +}; > > + > > +struct nl_desc_objs { > > + int max; > > + const struct nl_desc_obj **table; > > +}; > > + > > +struct nl_desc_req { > > + u32 bus; > > +}; > > + > > +struct net; > > +struct sk_buff; > > +struct nlmsghdr; > > +struct nlattr; > > + > > > + > > +/** > > + * struct nl_desc_obj - netlink object description > > + * @id: unique ID to identify this netlink object > > + * @max: number of attributes to describe this object > > @attr_max: Thanks for spotting this. > > + * @attrs: array of attribute descriptions > > + */ > > +struct nl_desc_obj { > > + u16 id; > > + u16 attr_max; > > + const struct nl_desc_attr *attrs; > > +}; > > > Is there a test program for this? I'm attaching what I have used to test this. These files print the netlink bus description. > Maybe add it to tools/testing/ ? Yes, I can place it there, no problem. This userspace code depends on libmnl though. I was planning to add infrastructure to libmnl to add a couple of helper functions that allows us to populate the nl_desc cache and to look up for presence of commands/attributes. People that don't like libmnl for whatever reason can add similar code to their libraries too, of course. Thanks!
>From 7826d6aa47d20bc09f7c8e33a457a5a338a8db55 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx> Date: Tue, 16 Jan 2018 00:05:37 +0100 Subject: [PATCH libmnl] examples: add netlink bus description Add nft-dump-desc-cmds.c and nft-dump-desc-obj.c to dump command and object descriptions. --- examples/Makefile.am | 11 ++ examples/nft-dump-desc-cmds.c | 177 ++++++++++++++++++++++++++++ examples/nft-dump-desc-objs.c | 263 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 451 insertions(+) create mode 100644 examples/nft-dump-desc-cmds.c create mode 100644 examples/nft-dump-desc-objs.c diff --git a/examples/Makefile.am b/examples/Makefile.am index e5cb052b315c..a8d4ba50f5ad 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1 +1,12 @@ +include $(top_srcdir)/Make_global.am + SUBDIRS = genl kobject netfilter rtnl + +check_PROGRAMS = nft-dump-desc-cmds \ + nft-dump-desc-objs + +nft_dump_desc_cmds_SOURCES = nft-dump-desc-cmds.c +nft_dump_desc_cmds_LDADD = ../src/libmnl.la + +nft_dump_desc_objs_SOURCES = nft-dump-desc-objs.c +nft_dump_desc_objs_LDADD = ../src/libmnl.la diff --git a/examples/nft-dump-desc-cmds.c b/examples/nft-dump-desc-cmds.c new file mode 100644 index 000000000000..cfb5276e911f --- /dev/null +++ b/examples/nft-dump-desc-cmds.c @@ -0,0 +1,177 @@ +#include <endian.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> +#include <inttypes.h> + +#include <linux/netlink.h> +#include <linux/netfilter/nfnetlink.h> + +#include <libmnl/libmnl.h> + +struct nl_desc_cmd; +struct nl_desc_attr; + +struct nl_desc { + uint32_t num_cmds; + struct nl_desc_cmd *cmds; +}; + +struct nl_desc_cmd { + uint32_t id; + uint32_t obj_id; +}; + +static struct nl_desc nl_desc; + +static int nla_desc_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NLA_DESC_CMD_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NLA_DESC_CMD_ID: + case NLA_DESC_CMD_OBJ: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_desc_cmd(const struct nlattr *nest, struct nl_desc_cmd *cmd) +{ + struct nlattr *tb[NLA_DESC_CMD_MAX + 1] = {}; + + mnl_attr_parse_nested(nest, nla_desc_attr_cb, tb); + if (tb[NLA_DESC_CMD_ID]) + cmd->id = mnl_attr_get_u32(tb[NLA_DESC_CMD_ID]); + if (tb[NLA_DESC_CMD_OBJ]) + cmd->obj_id = mnl_attr_get_u32(tb[NLA_DESC_CMD_OBJ]); +} + +static void print_desc_cmds(const struct nlattr *nest, struct nl_desc_cmd *cmds) +{ + struct nlattr *pos; + int j = 1; + + mnl_attr_for_each_nested(pos, nest) + print_desc_cmd(pos, &cmds[j++]); +} + +static int nla_desc_cmds_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NLA_DESC_OBJ_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NLA_DESC_NUM_OBJS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NLA_DESC_OBJS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[NLA_DESC_CMDS_MAX + 1] = {}; + + mnl_attr_parse(nlh, 0, nla_desc_cmds_cb, tb); + if (tb[NLA_DESC_CMDS_NUM]) { + nl_desc.num_cmds = mnl_attr_get_u32(tb[NLA_DESC_CMDS_NUM]); + + nl_desc.cmds = calloc(nl_desc.num_cmds + 1, sizeof(struct nl_desc_cmd)); + if (!nl_desc.cmds) + return MNL_CB_ERROR; + } + + if (tb[NLA_DESC_CMDS]) + print_desc_cmds(tb[NLA_DESC_CMDS], nl_desc.cmds); + + return MNL_CB_OK; +} + +#define NETLINK_DESC 23 +#define NLDESC_GET_CMDS 16 + +int main(void) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + uint32_t seq, portid; + struct nlattr *nest; + int ret, i; + + nl = mnl_socket_open(NETLINK_DESC); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = NLDESC_GET_CMDS; + nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + + mnl_attr_put_u32(nlh, NLA_DESC_REQ_BUS, NETLINK_NETFILTER); + nest = mnl_attr_nest_start(nlh, NLA_DESC_REQ_DATA); + mnl_attr_put_u32(nlh, NFNL_DESC_REQ_SUBSYS, NFNL_SUBSYS_NFTABLES); + mnl_attr_nest_end(nlh, nest); + + ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + if (ret == -1) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + while (1) { + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, &nl_desc); + if (ret == -1) { + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } else if (ret <= MNL_CB_STOP) + break; + } + + mnl_socket_close(nl); + + for (i = 1; nl_desc.cmds[i].obj_id; i++) { + printf("cmd = %d\n", nl_desc.cmds[i].id); + printf("obj_id = %d\n", nl_desc.cmds[i].obj_id); + } + + return 0; +} diff --git a/examples/nft-dump-desc-objs.c b/examples/nft-dump-desc-objs.c new file mode 100644 index 000000000000..8f5b365e3c64 --- /dev/null +++ b/examples/nft-dump-desc-objs.c @@ -0,0 +1,263 @@ +#include <endian.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <arpa/inet.h> +#include <inttypes.h> + +#include <linux/netlink.h> +#include <linux/netfilter/nfnetlink.h> + +#include <libmnl/libmnl.h> + +struct n_desc_obj; +struct n_desc_attr; + +struct nl_desc { + uint32_t num_objs; + struct nl_desc_obj *objs; +}; + +struct nl_desc_obj { + uint16_t id; + uint16_t max; + struct nl_desc_attr *attrs; +}; + +struct nl_desc_attr { + uint16_t nest_id; + uint16_t num; + uint16_t type; + uint16_t len; + uint32_t max; +}; + +static struct nl_desc nl_desc; + +static int nla_desc_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NLA_DESC_ATTR_MAX) < 0) + return MNL_CB_OK; + + switch (type) { + case NLA_DESC_ATTR_NUM: + case NLA_DESC_ATTR_TYPE: + case NLA_DESC_ATTR_LEN: + case NLA_DESC_ATTR_MAXVAL: + case NLA_DESC_ATTR_NEST_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_desc_attr(const struct nlattr *nest, struct nl_desc_attr *attr) +{ + struct nlattr *tb[NLA_DESC_ATTR_MAX + 1] = {}; + + mnl_attr_parse_nested(nest, nla_desc_attr_cb, tb); + if (tb[NLA_DESC_ATTR_NUM]) + attr->num = mnl_attr_get_u32(tb[NLA_DESC_ATTR_NUM]); + if (tb[NLA_DESC_ATTR_TYPE]) + attr->type = mnl_attr_get_u32(tb[NLA_DESC_ATTR_TYPE]); + if (tb[NLA_DESC_ATTR_LEN]) + attr->len = mnl_attr_get_u32(tb[NLA_DESC_ATTR_LEN]); + if (tb[NLA_DESC_ATTR_MAXVAL]) + attr->max = mnl_attr_get_u32(tb[NLA_DESC_ATTR_MAXVAL]); + if (tb[NLA_DESC_ATTR_NEST_ID]) + attr->nest_id = mnl_attr_get_u32(tb[NLA_DESC_ATTR_NEST_ID]); +} + +static void print_desc_attrs(const struct nlattr *nest, struct nl_desc_attr *attrs) +{ + struct nlattr *pos; + int j = 1; + + mnl_attr_for_each_nested(pos, nest) + print_desc_attr(pos, &attrs[j++]); +} + +static int nla_desc_obj_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NLA_DESC_OBJ_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NLA_DESC_OBJ_ID: + case NLA_DESC_OBJ_ATTRS_MAX: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NLA_DESC_OBJ_ATTRS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static void print_desc_obj(const struct nlattr *nest) +{ + struct nlattr *tb[NLA_DESC_OBJ_MAX + 1] = {}; + uint32_t id = 0, attrs_max; + + mnl_attr_parse_nested(nest, nla_desc_obj_attr_cb, tb); + if (tb[NLA_DESC_OBJ_ID]) + id = mnl_attr_get_u32(tb[NLA_DESC_OBJ_ID]); + if (tb[NLA_DESC_OBJ_ATTRS_MAX]) { + attrs_max = mnl_attr_get_u32(tb[NLA_DESC_OBJ_ATTRS_MAX]); + + nl_desc.objs[id].attrs = calloc(attrs_max + 1, sizeof(struct nl_desc_attr)); + if (!nl_desc.objs[id].attrs) + return; + + nl_desc.objs[id].max = attrs_max; + } + if (tb[NLA_DESC_OBJ_ATTRS]) + print_desc_attrs(tb[NLA_DESC_OBJ_ATTRS], nl_desc.objs[id].attrs); +} + +static void print_desc_objs(const struct nlattr *nest) +{ + struct nlattr *pos; + + mnl_attr_for_each_nested(pos, nest) + print_desc_obj(pos); +} + +static int nla_desc_objs_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NLA_DESC_OBJ_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NLA_DESC_NUM_OBJS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case NLA_DESC_OBJS: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[NLA_DESC_MAX + 1] = {}; + + mnl_attr_parse(nlh, 0, nla_desc_objs_cb, tb); + if (tb[NLA_DESC_NUM_OBJS]) { + nl_desc.num_objs = mnl_attr_get_u32(tb[NLA_DESC_NUM_OBJS]); + + nl_desc.objs = calloc(nl_desc.num_objs + 1, sizeof(struct nl_desc_obj)); + if (!nl_desc.objs) + return MNL_CB_ERROR; + } + + if (tb[NLA_DESC_OBJS]) + print_desc_objs(tb[NLA_DESC_OBJS]); + + return MNL_CB_OK; +} + +#define NETLINK_DESC 23 +#define NLDESC_GET_OBJS 18 + +int main(void) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + uint32_t seq, portid; + struct nlattr *nest; + int ret, i, j; + + nl = mnl_socket_open(NETLINK_DESC); + if (nl == NULL) { + perror("mnl_socket_open"); + exit(EXIT_FAILURE); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = NLDESC_GET_OBJS; + nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + + mnl_attr_put_u32(nlh, NLA_DESC_REQ_BUS, NETLINK_NETFILTER); + nest = mnl_attr_nest_start(nlh, NLA_DESC_REQ_DATA); + mnl_attr_put_u32(nlh, NFNL_DESC_REQ_SUBSYS, NFNL_SUBSYS_NFTABLES); + mnl_attr_nest_end(nlh, nest); + + ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); + if (ret == -1) { + perror("mnl_socket_sendto"); + exit(EXIT_FAILURE); + } + portid = mnl_socket_get_portid(nl); + + while (1) { + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) { + perror("mnl_socket_recvfrom"); + exit(EXIT_FAILURE); + } + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, &nl_desc); + if (ret == -1) { + perror("mnl_cb_run"); + exit(EXIT_FAILURE); + } else if (ret <= MNL_CB_STOP) + break; + } + + mnl_socket_close(nl); + + for (i = 1; i <= nl_desc.num_objs; i++) { + printf("id = %d\n", i); + printf("attrs_max = %d\n", nl_desc.objs[i].max); + for (j = 1; j < nl_desc.objs[i].max + 1; j++) { + printf("\t---------\n"); + printf("\tnum = %d\n", nl_desc.objs[i].attrs[j].num); + printf("\ttype = %d\n", nl_desc.objs[i].attrs[j].type); + if (nl_desc.objs[i].attrs[j].nest_id) + printf("\tnest_id = %d\n", nl_desc.objs[i].attrs[j].nest_id); + else { + printf("\tlen = %d\n", nl_desc.objs[i].attrs[j].len); + if (nl_desc.objs[i].attrs[j].max) + printf("\tmax = %d\n", nl_desc.objs[i].attrs[j].max); + } + } + } + + return 0; +} -- 2.11.0