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 <[email protected]>
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