diff mbox

[libnftables,v2,2/2] This patch adds a ruleset object API to libnftables.

Message ID 20130924125421.18650.8806.stgit@Ph0enix
State Superseded
Headers show

Commit Message

Alvaro Neira Sept. 24, 2013, 12:54 p.m. UTC
From: Álvaro Neira Ayuso <alvaroneay@gmail.com>

Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/libnftables/Makefile.am |    3 
 include/libnftables/ruleset.h   |   45 ++
 src/Makefile.am                 |    1 
 src/chain.c                     |    2 
 src/internal.h                  |    8 
 src/libnftables.map             |    9 
 src/rule.c                      |    2 
 src/ruleset.c                   |  759 +++++++++++++++++++++++++++++++++++++++
 src/set.c                       |    2 
 src/table.c                     |    2 
 tests/jsonfiles/64-ruleset.json |    2 
 tests/nft-parsing-test.c        |   40 ++
 tests/xmlfiles/75-ruleset.xml   |    1 
 13 files changed, 871 insertions(+), 5 deletions(-)
 create mode 100644 include/libnftables/ruleset.h
 create mode 100644 src/ruleset.c
 create mode 100644 tests/jsonfiles/64-ruleset.json
 create mode 100644 tests/xmlfiles/75-ruleset.xml


--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Pablo Neira Ayuso Sept. 25, 2013, 8:38 p.m. UTC | #1
@Arturo, could you resolve the following issues in Alvaro's patch and
send a new version? Thanks.

On Tue, Sep 24, 2013 at 02:54:21PM +0200, Alvaro Neira wrote:
> From: Álvaro Neira Ayuso <alvaroneay@gmail.com>
> 
> Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
> Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> ---
>  include/libnftables/Makefile.am |    3 
>  include/libnftables/ruleset.h   |   45 ++
>  src/Makefile.am                 |    1 
>  src/chain.c                     |    2 
>  src/internal.h                  |    8 
>  src/libnftables.map             |    9 
>  src/rule.c                      |    2 
>  src/ruleset.c                   |  759 +++++++++++++++++++++++++++++++++++++++
>  src/set.c                       |    2 
>  src/table.c                     |    2 
>  tests/jsonfiles/64-ruleset.json |    2 
>  tests/nft-parsing-test.c        |   40 ++
>  tests/xmlfiles/75-ruleset.xml   |    1 
>  13 files changed, 871 insertions(+), 5 deletions(-)
>  create mode 100644 include/libnftables/ruleset.h
>  create mode 100644 src/ruleset.c
>  create mode 100644 tests/jsonfiles/64-ruleset.json
>  create mode 100644 tests/xmlfiles/75-ruleset.xml
> 
> diff --git a/include/libnftables/Makefile.am b/include/libnftables/Makefile.am
> index b052992..e243f32 100644
> --- a/include/libnftables/Makefile.am
> +++ b/include/libnftables/Makefile.am
> @@ -2,4 +2,5 @@ pkginclude_HEADERS = table.h		\
>  		     chain.h		\
>  		     rule.h		\
>  		     expr.h		\
> -		     set.h
> +		     set.h		\
> +		     ruleset.h
> diff --git a/include/libnftables/ruleset.h b/include/libnftables/ruleset.h
> new file mode 100644
> index 0000000..a4a1279
> --- /dev/null
> +++ b/include/libnftables/ruleset.h
> @@ -0,0 +1,45 @@
> +#ifndef _RULESET_H_
> +#define _RULESET_H_
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +struct nft_ruleset;
> +
> +struct nft_ruleset *nft_ruleset_alloc(void);
> +void nft_ruleset_free(struct nft_ruleset *r);
> +
> +enum {
> +	NFT_RULESET_ATTR_TABLELIST = 0,
> +	NFT_RULESET_ATTR_CHAINLIST,
> +	NFT_RULESET_ATTR_SETLIST,
> +	NFT_RULESET_ATTR_RULELIST,
> +};
> +
> +bool nft_ruleset_attr_is_set(const struct nft_ruleset *r, uint16_t attr);
> +void nft_ruleset_attr_unset(struct nft_ruleset *r, uint16_t attr);
> +void nft_ruleset_attr_set(struct nft_ruleset *r, uint16_t attr, void *data);
> +const void *nft_ruleset_attr_get(const struct nft_ruleset *r, uint16_t attr);
> +
> +enum {
> +	NFT_RULESET_O_DEFAULT	= 0,
> +	NFT_RULESET_O_XML,
> +	NFT_RULESET_O_JSON,
> +};
> +
> +enum nft_ruleset_parse_type {
> +	NFT_RULESET_PARSE_NONE	= 0,
> +	NFT_RULESET_PARSE_XML,
> +	NFT_RULESET_PARSE_JSON,
> +	NFT_RULESET_PARSE_MAX,
> +};
> +
> +int nft_ruleset_parse(struct nft_ruleset *rs, enum nft_ruleset_parse_type type, const char *data);
> +int nft_ruleset_snprintf(char *buf, size_t size, const struct nft_ruleset *rs, uint32_t type, uint32_t flags);
> +
> +#ifdef __cplusplus
> +} /* extern "C" */
> +#endif
> +
> +#endif /* _RULESET_H_ */
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 51b40a2..474dbf0 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -10,6 +10,7 @@ libnftables_la_SOURCES = utils.c		\
>  			 rule.c			\
>  			 set.c			\
>  			 set_elem.c		\
> +			 ruleset.c		\
>  			 mxml.c			\
>  			 jansson.c		\
>  			 expr.c			\
> diff --git a/src/chain.c b/src/chain.c
> index 8c0d804..e3f70e0 100644
> --- a/src/chain.c
> +++ b/src/chain.c
> @@ -506,7 +506,7 @@ static inline int nft_str2hooknum(int family, const char *hook)
>  }
>  
>  #ifdef JSON_PARSING
> -static int nft_jansson_parse_chain(struct nft_chain *c, json_t *tree)
> +int nft_jansson_parse_chain(struct nft_chain *c, json_t *tree)
>  {
>  	json_t *root;
>  	uint64_t uval64;
> diff --git a/src/internal.h b/src/internal.h
> index df64dd8..b29288a 100644
> --- a/src/internal.h
> +++ b/src/internal.h
> @@ -71,6 +71,14 @@ int nft_jansson_data_reg_parse(json_t *root, const char *tag,
>  			       union nft_data_reg *data_reg);
>  struct nft_set_elem;
>  int nft_set_elem_json_parse(struct nft_set_elem *e, json_t *root);
> +struct nft_table;
> +int nft_jansson_parse_table(struct nft_table *t, json_t *tree);
> +struct nft_chain;
> +int nft_jansson_parse_chain(struct nft_chain *c, json_t *tree);
> +struct nft_rule;
> +int nft_jansson_parse_rule(struct nft_rule *r, json_t *tree);
> +struct nft_set;
> +int nft_jansson_parse_set(struct nft_set *s, json_t *tree);
>  #endif
>  
>  const char *nft_family2str(uint32_t family);
> diff --git a/src/libnftables.map b/src/libnftables.map
> index 963c03e..1223403 100644
> --- a/src/libnftables.map
> +++ b/src/libnftables.map
> @@ -168,5 +168,14 @@ global:
>    nft_set_elems_iter_next;
>    nft_set_elems_iter_destroy;
>  
> +  nft_ruleset_alloc;
> +  nft_ruleset_free;
> +  nft_ruleset_attr_is_set;
> +  nft_ruleset_attr_unset;
> +  nft_ruleset_attr_set;
> +  nft_ruleset_attr_get;
> +  nft_ruleset_parse;
> +  nft_ruleset_snprintf;
> +
>  local: *;
>  };
> diff --git a/src/rule.c b/src/rule.c
> index e593109..c8e1b99 100644
> --- a/src/rule.c
> +++ b/src/rule.c
> @@ -470,7 +470,7 @@ int nft_rule_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_rule *r)
>  EXPORT_SYMBOL(nft_rule_nlmsg_parse);
>  
>  #ifdef JSON_PARSING
> -static int nft_jansson_parse_rule(struct nft_rule *r, json_t *tree)
> +int nft_jansson_parse_rule(struct nft_rule *r, json_t *tree)
>  {
>  	json_t *root, *array;
>  	struct nft_rule_expr *e;
> diff --git a/src/ruleset.c b/src/ruleset.c
> new file mode 100644
> index 0000000..2c30a56
> --- /dev/null
> +++ b/src/ruleset.c
> @@ -0,0 +1,759 @@
> +/*
> + * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
> + * (C) 2013 by Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
> + * (C) 2013 by Alvaro Neira Ayuso <alvaroneay@gmail.com>
> + *
> + * 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.
> + *
> + * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
> + */
> +
> +#include <errno.h>
> +
> +#include "internal.h"
> +
> +#include <libmnl/libmnl.h>
> +#include <libnftables/ruleset.h>
> +#include <libnftables/table.h>
> +#include <libnftables/chain.h>
> +#include <libnftables/set.h>
> +#include <libnftables/rule.h>
> +
> +struct nft_ruleset {
> +	struct nft_table_list	*table_list;
> +	struct nft_chain_list	*chain_list;
> +	struct nft_set_list	*set_list;
> +	struct nft_rule_list	*rule_list;
> +
> +	uint16_t		flags;
> +};
> +
> +struct nft_ruleset *nft_ruleset_alloc(void)
> +{
> +	return calloc(1, sizeof(struct nft_ruleset));
> +}
> +EXPORT_SYMBOL(nft_ruleset_alloc);
> +
> +void nft_ruleset_free(struct nft_ruleset *r)
> +{
> +	if (r->flags & (1 << NFT_RULESET_ATTR_TABLELIST))
> +		nft_table_list_free(r->table_list);
> +

You can remove these empty lines.

> +	if (r->flags & (1 << NFT_RULESET_ATTR_CHAINLIST))
> +		nft_chain_list_free(r->chain_list);
> +
> +	if (r->flags & (1 << NFT_RULESET_ATTR_SETLIST))
> +		nft_set_list_free(r->set_list);
> +
> +	if (r->flags & (1 << NFT_RULESET_ATTR_RULELIST))
> +		nft_rule_list_free(r->rule_list);
> +
> +	xfree(r);
> +}
> +EXPORT_SYMBOL(nft_ruleset_free);
> +
> +bool nft_ruleset_attr_is_set(const struct nft_ruleset *r, uint16_t attr)
> +{
> +	return r->flags & (1 << attr);
> +}
> +EXPORT_SYMBOL(nft_ruleset_attr_is_set);
> +
> +void nft_ruleset_attr_unset(struct nft_ruleset *r, uint16_t attr)
> +{
> +	if (!(r->flags & (1 << attr)))
> +		return;
> +
> +	switch (attr) {
> +	case NFT_RULESET_ATTR_TABLELIST:
> +		nft_table_list_free(r->table_list);
> +		r->table_list = NULL;
> +		break;
> +	case NFT_RULESET_ATTR_CHAINLIST:
> +		nft_chain_list_free(r->chain_list);
> +		r->chain_list = NULL;
> +		break;
> +	case NFT_RULESET_ATTR_SETLIST:
> +		nft_set_list_free(r->set_list);
> +		r->set_list = NULL;
> +		break;
> +	case NFT_RULESET_ATTR_RULELIST:
> +		nft_rule_list_free(r->rule_list);
> +		r->rule_list = NULL;
> +		break;
> +	}
> +	r->flags &= ~(1 << attr);
> +}
> +EXPORT_SYMBOL(nft_ruleset_attr_unset);
> +
> +void nft_ruleset_attr_set(struct nft_ruleset *r, uint16_t attr, void *data)
> +{
> +	switch (attr) {
> +	case NFT_RULESET_ATTR_TABLELIST:
> +		nft_ruleset_attr_unset(r, NFT_RULESET_ATTR_TABLELIST);
> +		r->table_list = data;
> +		break;
> +	case NFT_RULESET_ATTR_CHAINLIST:
> +		nft_ruleset_attr_unset(r, NFT_RULESET_ATTR_CHAINLIST);
> +		r->chain_list = data;
> +		break;
> +	case NFT_RULESET_ATTR_SETLIST:
> +		nft_ruleset_attr_unset(r, NFT_RULESET_ATTR_SETLIST);
> +		r->set_list = data;
> +		break;
> +	case NFT_RULESET_ATTR_RULELIST:
> +		nft_ruleset_attr_unset(r, NFT_RULESET_ATTR_RULELIST);
> +		r->rule_list = data;
> +		break;
> +	default:
> +		return;
> +	}
> +	r->flags |= (1 << attr);
> +}
> +EXPORT_SYMBOL(nft_ruleset_attr_set);
> +
> +const void *nft_ruleset_attr_get(const struct nft_ruleset *r, uint16_t attr)
> +{
> +	if (!(r->flags & (1 << attr)))
> +		return NULL;
> +
> +	switch (attr) {
> +	case NFT_RULESET_ATTR_TABLELIST:
> +		return r->table_list;
> +	case NFT_RULESET_ATTR_CHAINLIST:
> +		return r->chain_list;
> +	case NFT_RULESET_ATTR_SETLIST:
> +		return r->set_list;
> +	case NFT_RULESET_ATTR_RULELIST:
> +		return r->rule_list;
> +	default:
> +		return NULL;
> +	}
> +}
> +EXPORT_SYMBOL(nft_ruleset_attr_get);
> +
> +#ifdef JSON_PARSING
> +static int nft_ruleset_json_parse_table(struct nft_table_list *table_list,
> +					json_t *root)
> +{
> +	struct nft_table *t;
> +
> +	t = nft_table_alloc();
> +	if (t == NULL)
> +		goto err;
> +
> +	if (nft_jansson_parse_table(t, root) < 0)
> +		goto err;
> +
> +	nft_table_list_add_tail(t, table_list);
> +
> +	return 0;
> +err:
> +	nft_table_free(t);
> +	return -1;
> +}
> +
> +static int nft_ruleset_json_parse_chain(struct nft_chain_list *chain_list,
> +					json_t *root)
> +{
> +	struct nft_chain *c;
> +
> +	c = nft_chain_alloc();
> +	if (c == NULL)
> +		goto err;
> +
> +	if (nft_jansson_parse_chain(c, root) < 0)
> +		goto err;
> +
> +	nft_chain_list_add_tail(c, chain_list);
> +
> +	return 0;
> +err:
> +	nft_chain_free(c);
> +	return -1;
> +}
> +
> +static int nft_ruleset_json_parse_rule(struct nft_rule_list *rule_list,
> +				       json_t *root)
> +{
> +	struct nft_rule *r;
> +
> +	r = nft_rule_alloc();
> +	if (r == NULL)
> +		goto err;
> +
> +	if (nft_jansson_parse_rule(r, root) < 0)
> +		goto err;
> +
> +	nft_rule_list_add_tail(r, rule_list);
> +
> +	return 0;
> +err:
> +	nft_rule_free(r);
> +	return -1;
> +}
> +
> +static int nft_ruleset_json_parse_set(struct nft_set_list *set_list,
> +				      json_t *root)
> +{
> +	struct nft_set *s;
> +
> +	s = nft_set_alloc();
> +	if (s == NULL)
> +		goto err;
> +
> +	if (nft_jansson_parse_set(s, root) < 0)
> +		goto err;
> +
> +	nft_set_list_add_tail(s, set_list);
> +
> +	return 0;
> +err:
> +	nft_set_free(s);
> +	return -1;
> +}
> +
> +static int nft_jansson_parse_ruleset(struct nft_ruleset *rs, json_t *tree)
> +{
> +	json_t *node, *array;
> +	int i;
> +	struct nft_table_list *table_list = nft_table_list_alloc();
> +	struct nft_chain_list *chain_list = nft_chain_list_alloc();
> +	struct nft_set_list *set_list = nft_set_list_alloc();
> +	struct nft_rule_list *rule_list = nft_rule_list_alloc();
> +
> +	if (table_list == NULL || chain_list == NULL || set_list == NULL ||
> +	    rule_list == NULL) {
> +		errno = ENOMEM;
> +		goto err;
> +	}
> +
> +	array = nft_jansson_get_node(tree, "nftables");
> +	if (array == NULL)
> +		return -1;
> +
> +	for (i = 0; i < json_array_size(array); ++i) {
> +		node = json_array_get(array, i);
> +		if (nft_jansson_node_exist(node, "table"))
> +			if (nft_ruleset_json_parse_table(table_list, node) < 0)

Better use:
                if (x && y < 0)
                        goto err;

> +				goto err;
> +		if (nft_jansson_node_exist(node, "chain"))
> +			if (nft_ruleset_json_parse_chain(chain_list, node) < 0)
> +				goto err;
> +		if (nft_jansson_node_exist(node, "rule"))
> +			if (nft_ruleset_json_parse_rule(rule_list, node) < 0)
> +				goto err;
> +		if (nft_jansson_node_exist(node, "set"))
> +			if (nft_ruleset_json_parse_set(set_list, node) < 0)
> +				goto err;
> +	}
> +
> +	if (nft_table_list_is_empty(table_list)
> +	    && nft_chain_list_is_empty(chain_list)
> +	    && nft_set_list_is_empty(set_list)
> +	    && nft_rule_list_is_empty(rule_list)) {
> +		errno = EINVAL;
> +		goto err;
> +	}

I don't think we should return an error in this case, remove these
lines above. Same thing in the XML parsing.

BTW, coding style is:

if (aaaaaaaa &&
    bbbbbbbb) {
        ...
}

The ampersand always at the end.

> +	if (!nft_table_list_is_empty(table_list))
> +		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_TABLELIST,
> +				     table_list);
> +	else
> +		nft_table_list_free(table_list);
> +
> +	if (!nft_chain_list_is_empty(chain_list))
> +		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_CHAINLIST,
> +				     chain_list);
> +	else
> +		nft_chain_list_free(chain_list);
> +
> +	if (!nft_set_list_is_empty(set_list))
> +		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_SETLIST, set_list);
> +	else
> +		nft_set_list_free(set_list);
> +
> +	if (!nft_rule_list_is_empty(rule_list))
> +		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_RULELIST, rule_list);
> +	else
> +		nft_rule_list_free(rule_list);
> +
> +	nft_jansson_free_root(tree);
> +	return 0;
> +
> +err:

I don't like this error handling, it's sloppy, better add tags for
each step, so we can remove the if checking, ie.

err:
        ...
err_table:
        ...
err_chain:
        ...
err_set:
        ...

> +	if (table_list)
> +		nft_table_list_free(table_list);
> +	if (chain_list)
> +		nft_chain_list_free(chain_list);
> +	if (set_list)
> +		nft_set_list_free(set_list);
> +	if (rule_list)
> +		nft_rule_list_free(rule_list);
> +
> +	nft_jansson_free_root(tree);
> +	return -1;
> +}
> +#endif
> +
> +static int nft_ruleset_json_parse(struct nft_ruleset *rs, const char *json)
> +{
> +#ifdef JSON_PARSING
> +	json_t *tree;
> +	json_error_t error;
> +
> +	tree = nft_jansson_create_root(json, &error);
> +	if (tree == NULL)
> +		return -1;
> +
> +	return nft_jansson_parse_ruleset(rs, tree);
> +#else
> +	errno = EOPNOTSUPP;
> +	return -1;
> +#endif
> +}
> +
> +#ifdef XML_PARSING
> +static int
> +nft_ruleset_xml_parse_tables(struct nft_ruleset *rs, mxml_node_t *tree)
> +{
> +	mxml_node_t *node;
> +	struct nft_table *t;
> +	struct nft_table_list *table_list = nft_table_list_alloc();
> +	if (table_list == NULL) {
> +		errno = ENOMEM;
> +		return -1;
> +	}
> +
> +	for (node = mxmlFindElement(tree, tree, "table", NULL, NULL,
> +				    MXML_DESCEND_FIRST);
> +	     node != NULL;
> +	     node = mxmlFindElement(node, tree, "table", NULL, NULL,
> +				    MXML_NO_DESCEND)) {
> +		t = nft_table_alloc();
> +		if (t == NULL)
> +			goto err_free;
> +
> +		if (nft_mxml_table_parse(node, t) != 0) {
> +			nft_table_free(t);
> +			goto err_free;
> +		}
> +
> +		nft_table_list_add_tail(t, table_list);
> +	}
> +
> +	if (!nft_table_list_is_empty(table_list))
> +		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_TABLELIST,
> +				     table_list);
> +	else
> +		nft_table_list_free(table_list);
> +
> +	return 0;
> +err_free:
> +	nft_table_list_free(table_list);
> +	return -1;
> +}
> +
> +static int
> +nft_ruleset_xml_parse_chains(struct nft_ruleset *rs, mxml_node_t *tree)
> +{
> +	mxml_node_t *node;
> +	struct nft_chain *c;
> +	struct nft_chain_list *chain_list = nft_chain_list_alloc();
> +	if (chain_list == NULL) {
> +		errno = ENOMEM;
> +		return -1;
> +	}
> +
> +	for (node = mxmlFindElement(tree, tree, "chain", NULL, NULL,
> +				    MXML_DESCEND_FIRST);
> +	     node != NULL;
> +	     node = mxmlFindElement(node, tree, "chain", NULL, NULL,
> +				    MXML_NO_DESCEND)) {
> +		c = nft_chain_alloc();
> +		if (c == NULL)
> +			goto err_free;
> +
> +		if (nft_mxml_chain_parse(node, c) != 0) {
> +			nft_chain_free(c);
> +			goto err_free;
> +		}
> +
> +		nft_chain_list_add_tail(c, chain_list);
> +	}
> +
> +	if (!nft_chain_list_is_empty(chain_list))
> +		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_CHAINLIST,
> +				     chain_list);
> +	else
> +		nft_chain_list_free(chain_list);
> +
> +	return 0;
> +err_free:
> +	nft_chain_list_free(chain_list);
> +	return -1;
> +}
> +
> +static int
> +nft_ruleset_xml_parse_rules(struct nft_ruleset *rs, mxml_node_t *tree)
> +{
> +	mxml_node_t *node;
> +	struct nft_rule *r;
> +	struct nft_rule_list *rule_list = nft_rule_list_alloc();
> +	if (rule_list == NULL) {
> +		errno = ENOMEM;
> +		return -1;
> +	}
> +
> +	for (node = mxmlFindElement(tree, tree, "rule", NULL, NULL,
> +				    MXML_DESCEND_FIRST);
> +	     node != NULL;
> +	     node = mxmlFindElement(node, tree, "rule", NULL, NULL,
> +				    MXML_NO_DESCEND)) {
> +		r = nft_rule_alloc();
> +		if (r == NULL)
> +			goto err_free;
> +
> +		if (nft_mxml_rule_parse(node, r) != 0) {
> +			nft_rule_free(r);
> +			goto err_free;
> +		}
> +
> +		nft_rule_list_add_tail(r, rule_list);
> +	}
> +
> +	if (!nft_rule_list_is_empty(rule_list))
> +		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_RULELIST, rule_list);
> +	else
> +		nft_rule_list_free(rule_list);
> +
> +	return 0;
> +err_free:
> +	nft_rule_list_free(rule_list);
> +	return -1;
> +}
> +
> +static int
> +nft_ruleset_xml_parse_sets(struct nft_ruleset *rs, mxml_node_t *tree)
> +{
> +	mxml_node_t *node;
> +	struct nft_set *s;
> +	struct nft_set_list *set_list = nft_set_list_alloc();
> +	if (set_list == NULL) {
> +		errno = ENOMEM;
> +		return -1;
> +	}
> +
> +	for (node = mxmlFindElement(tree, tree, "set", NULL, NULL,
> +				    MXML_DESCEND_FIRST);
> +	     node != NULL;
> +	     node = mxmlFindElement(node, tree, "set", NULL, NULL,
> +				    MXML_NO_DESCEND)) {
> +		s = nft_set_alloc();
> +		if (s == NULL)
> +			goto err_free;
> +
> +		if (nft_mxml_set_parse(node, s) != 0) {
> +			nft_set_free(s);
> +			goto err_free;
> +		}
> +
> +		nft_set_list_add_tail(s, set_list);
> +	}
> +
> +	if (!nft_set_list_is_empty(set_list))
> +		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_SETLIST, set_list);
> +	else
> +		nft_set_list_free(set_list);
> +
> +	return 0;
> +err_free:
> +	nft_set_list_free(set_list);
> +	return -1;
> +}
> +#endif
> +
> +static int nft_ruleset_xml_parse(struct nft_ruleset *rs, const char *xml)
> +{
> +#ifdef XML_PARSING
> +	mxml_node_t *tree;
> +
> +	tree = nft_mxml_build_tree(xml, "nftables");
> +	if (tree == NULL)
> +		return -1;
> +
> +	if (nft_ruleset_xml_parse_tables(rs, tree) != 0)
> +		goto err;
> +
> +	if (nft_ruleset_xml_parse_chains(rs, tree) != 0)
> +		goto err;
> +
> +	if (nft_ruleset_xml_parse_sets(rs, tree) != 0)
> +		goto err;
> +
> +	if (nft_ruleset_xml_parse_rules(rs, tree) != 0)
> +		goto err;
> +
> +	mxmlDelete(tree);
> +
> +	if (!(nft_ruleset_attr_is_set(rs, NFT_RULESET_ATTR_TABLELIST))
> +		&& !(nft_ruleset_attr_is_set(rs, NFT_RULESET_ATTR_CHAINLIST))
> +		&& !(nft_ruleset_attr_is_set(rs, NFT_RULESET_ATTR_SETLIST))
> +		&& !(nft_ruleset_attr_is_set(rs, NFT_RULESET_ATTR_RULELIST))) {
> +		errno = EINVAL;
> +		return -1;
> +	}
> +
> +	return 0;
> +err:
> +	mxmlDelete(tree);
> +	return -1;
> +#else
> +	errno = EOPNOTSUPP;
> +	return -1;
> +#endif
> +}
> +
> +int nft_ruleset_parse(struct nft_ruleset *r, enum nft_ruleset_parse_type type,
> +		      const char *data)
> +{
> +	int ret;
> +
> +	switch (type) {
> +	case NFT_RULESET_PARSE_XML:
> +		ret = nft_ruleset_xml_parse(r, data);
> +		break;
> +	case NFT_RULESET_PARSE_JSON:
> +		ret = nft_ruleset_json_parse(r, data);
> +		break;
> +	default:
> +		ret = -1;
> +		errno = EOPNOTSUPP;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(nft_ruleset_parse);
> +
> +static int separator_snprintf(char *buf, size_t size, void *obj, uint32_t type)
> +{
> +	if (obj == NULL)
> +		return 0;
> +
> +	if (type == NFT_RULESET_O_JSON)
> +		return snprintf(buf, size, ",");
> +
> +	if (type == NFT_RULESET_O_DEFAULT)
> +		return snprintf(buf, size, "\n");

Use switch here instead?

> +	return 0;
> +}
> +
> +static int
> +list_separator_snprintf(char *buf, size_t size, void *prev, void *next,
> +			uint32_t type)
> +{
> +	if (prev == NULL || next == NULL)
> +		return 0;
> +
> +	return separator_snprintf(buf, size, prev, type);
> +}
> +
> +static int
> +nft_ruleset_snprintf_table(char *buf, size_t size,
> +			   const struct nft_ruleset *rs, uint32_t type,
> +			   uint32_t flags)
> +{
> +	struct nft_table *t;
> +	struct nft_table_list_iter *ti;
> +	int ret, len = size, offset = 0;
> +
> +	ti = nft_table_list_iter_create(rs->table_list);
> +	if (ti == NULL)
> +		return 0;
> +
> +	t = nft_table_list_iter_next(ti);
> +	while (t != NULL) {
> +		ret = nft_table_snprintf(buf+offset, size, t, type, flags);
> +		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +		t = nft_table_list_iter_next(ti);
> +		ret = separator_snprintf(buf+offset, size, t, type);
> +		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +	}
> +	nft_table_list_iter_destroy(ti);
> +
> +	return offset;
> +}
> +
> +static int
> +nft_ruleset_snprintf_chain(char *buf, size_t size,
> +			   const struct nft_ruleset *rs, uint32_t type,
> +			   uint32_t flags)
> +{
> +	struct nft_chain *c;
> +	struct nft_chain_list_iter *ci;
> +	int ret, len = size, offset = 0;
> +
> +	ci = nft_chain_list_iter_create(rs->chain_list);
> +	if (ci == NULL)
> +		return 0;
> +
> +	c = nft_chain_list_iter_next(ci);
> +	while (c != NULL) {
> +		ret = nft_chain_snprintf(buf+offset, size, c, type, flags);
> +		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +		c = nft_chain_list_iter_next(ci);
> +		ret = separator_snprintf(buf+offset, size, c, type);
> +		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +	}
> +	nft_chain_list_iter_destroy(ci);
> +
> +	return offset;
> +}
> +
> +static int
> +nft_ruleset_snprintf_set(char *buf, size_t size,
> +			 const struct nft_ruleset *rs, uint32_t type,
> +			 uint32_t flags)
> +{
> +	struct nft_set *s;
> +	struct nft_set_list_iter *si;
> +	int ret, len = size, offset = 0;
> +
> +	si = nft_set_list_iter_create(rs->set_list);
> +	if (si == NULL)
> +		return 0;
> +
> +	s = nft_set_list_iter_next(si);
> +	while (s != NULL) {
> +		ret = nft_set_snprintf(buf+offset, size, s, type, flags);
> +		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +		s = nft_set_list_iter_next(si);
> +		ret = separator_snprintf(buf+offset, size, s, type);
> +		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +	}
> +	nft_set_list_iter_destroy(si);
> +
> +	return offset;
> +}
> +
> +static int
> +nft_ruleset_snprintf_rule(char *buf, size_t size,
> +			  const struct nft_ruleset *rs, uint32_t type,
> +			  uint32_t flags)
> +{
> +	struct nft_rule *r;
> +	struct nft_rule_list_iter *ri;
> +	int ret, len = size, offset = 0;
> +
> +	ri = nft_rule_list_iter_create(rs->rule_list);
> +	if (ri == NULL)
> +		return 0;
> +
> +	r = nft_rule_list_iter_next(ri);
> +	while (r != NULL) {
> +		ret = nft_rule_snprintf(buf+offset, size, r, type, flags);
> +		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +		r = nft_rule_list_iter_next(ri);
> +		ret = separator_snprintf(buf+offset, size, r, type);
> +		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +	}
> +	nft_rule_list_iter_destroy(ri);
> +
> +	return offset;
> +}
> +
> +static int
> +nft_ruleset_do_snprintf(char *buf, size_t size, const struct nft_ruleset *rs,
> +			uint32_t type, uint32_t flags)
> +{
> +	int ret, len = size, offset = 0;
> +
> +	ret = nft_ruleset_snprintf_table(buf+offset, size, rs, type, flags);
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	ret = list_separator_snprintf(buf+offset, size,
> +				      rs->table_list, rs->chain_list, type);
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	ret = nft_ruleset_snprintf_chain(buf+offset, size, rs, type, flags);
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	ret = list_separator_snprintf(buf+offset, size,
> +				      rs->chain_list, rs->set_list, type);
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	ret = nft_ruleset_snprintf_set(buf+offset, size, rs, type, flags);
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	ret = list_separator_snprintf(buf+offset, size,
> +				      rs->set_list, rs->rule_list, type);
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	ret = nft_ruleset_snprintf_rule(buf+offset, size, rs, type, flags);
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	return offset;
> +}
> +
> +static int
> +nft_ruleset_snprintf_xml(char *buf, size_t size, const struct nft_ruleset *rs,
> +			 uint32_t flags)
> +{
> +	int ret, len = size, offset = 0;
> +
> +	ret = snprintf(buf, size, "<nftables>");
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	ret = nft_ruleset_do_snprintf(buf+offset, size, rs, NFT_RULESET_O_XML,
> +				      flags);
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	ret = snprintf(buf+offset, size, "</nftables>");
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	return offset;
> +}
> +
> +static int
> +nft_ruleset_snprintf_json(char *buf, size_t size, const struct nft_ruleset *rs,
> +			  uint32_t flags)
> +{
> +	int ret, len = size, offset = 0;
> +
> +	ret = snprintf(buf, size, "{ \"nftables\": [");
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	ret = nft_ruleset_do_snprintf(buf+offset, size, rs, NFT_RULESET_O_JSON,
> +				      flags);
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	ret = snprintf(buf+offset, size, "]}\n");
> +	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
> +
> +	return offset;
> +}
> +
> +int nft_ruleset_snprintf(char *buf, size_t size, const struct nft_ruleset *r,
> +			 uint32_t type, uint32_t flags)
> +{
> +	switch (type) {
> +	case NFT_RULESET_O_DEFAULT:
> +		return nft_ruleset_do_snprintf(buf, size, r, type, flags);
> +	case NFT_RULESET_O_XML:
> +		return nft_ruleset_snprintf_xml(buf, size, r, flags);
> +	case NFT_RULESET_O_JSON:
> +		return nft_ruleset_snprintf_json(buf, size, r, flags);
> +	default:
> +		break;
> +	}
> +	return -1;
> +}
> +EXPORT_SYMBOL(nft_ruleset_snprintf);
> diff --git a/src/set.c b/src/set.c
> index 530776d..586fae1 100644
> --- a/src/set.c
> +++ b/src/set.c
> @@ -304,7 +304,7 @@ int nft_set_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_set *s)
>  EXPORT_SYMBOL(nft_set_nlmsg_parse);
>  
>  #ifdef JSON_PARSING
> -static int nft_jansson_parse_set(struct nft_set *s, json_t *tree)
> +int nft_jansson_parse_set(struct nft_set *s, json_t *tree)
>  {
>  	json_t *root, *array, *json_elem;
>  	uint32_t uval32;
> diff --git a/src/table.c b/src/table.c
> index c095053..7f14b32 100644
> --- a/src/table.c
> +++ b/src/table.c
> @@ -272,7 +272,7 @@ static int nft_table_xml_parse(struct nft_table *t, const char *xml)
>  }
>  
>  #ifdef JSON_PARSING
> -static int nft_jansson_parse_table(struct nft_table *t, json_t *tree)
> +int nft_jansson_parse_table(struct nft_table *t, json_t *tree)
>  {
>  	json_t *root;
>  	uint32_t flags;
> diff --git a/tests/jsonfiles/64-ruleset.json b/tests/jsonfiles/64-ruleset.json
> new file mode 100644
> index 0000000..39b700c
> --- /dev/null
> +++ b/tests/jsonfiles/64-ruleset.json
> @@ -0,0 +1,2 @@
> +{ "nftables": [{"table" : {"name" : "filter","family" : "ip","flags" : 0}},{"table" : {"name" : "filter2","family" : "ip6","flags" : 0}},{ "chain": {"name": "input","handle": 1,"bytes": 10681449,"packets": 16216,"family": "ip","table": "filter","use": 0,"type": "filter","hooknum": "input","prio": 0,"policy": "accept"}},{ "chain": {"name": "forward","handle": 2,"bytes": 0,"packets": 0,"family": "ip","table": "filter","use": 0,"type": "filter","hooknum": "forward","prio": 0,"policy": "accept"}},{ "chain": {"name": "output","handle": 3,"bytes": 2375830,"packets": 15184,"family": "ip","table": "filter","use": 0,"type": "filter","hooknum": "output","prio": 0,"policy": "accept"}},{ "chain": {"name": "chain1","handle": 4,"bytes": 0,"packets": 0,"family": "ip","table": "filter","use": 0}},{ "set": { "name": "set0","table": "filter","flags": 3,"family": "ip","key_type": 12,"key_len": 2}},{ "rule": { "family" : "ip", "table" : "filter", "chain"  : "output", "handle" : 6,"flags" : 0, "expr" : [ { "type" : "payload", "dreg" : 1, "offset" : 16, "len" : 4, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 4, "data0" : "0x0100a8c0"}}}, { "type" : "counter", "pkts" : 0, "bytes" : 0}, { "type" : "immediate", "dreg" : 0, "immediatedata" : {"data_reg": {"type" : "verdict", "verdict" : "drop"}}}]}},{ "rule": { "family" : "ip", "table" : "filter", "chain"  : "output", "handle" : 9,"flags" : 0, "expr" : [ { "type" : "payload", "dreg" : 1, "offset" : 9, "len" : 1, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 1, "data0" : "0x00000006"}}}, { "type" : "payload", "dreg" : 1, "offset" : 2, "len" : 2, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 2, "data0" : "0x00001600"}}}, { "type" : "counter", "pkts" : 0, "bytes" : 0}]}},{ "rule": { "family" : "ip", "table" : "filter", "chain"  : "output", "handle" : 10,"flags" : 0, "expr" : [ { "type" : "payload", "dreg" : 1, "offset" : 16, "len" : 4, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 4, "data0" : "0x0100a8c0"}}}, { "type" : "counter", "pkts" : 0, "bytes" : 0}]}},{ "rule": { "family" : "ip", "table" : "filter", "chain"  : "output", "handle" : 11,"flags" : 0, "expr" : [ { "type" : "payload", "dreg" : 1, "offset" : 16, "len" : 4, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 4, "data0" : "0x0100a8c0"}}}, { "type" : "counter", "pkts" : 0, "bytes" : 0}, { "type" : "immediate", "dreg" : 0, "immediatedata" : {"data_reg": {"type" : "verdict", "verdict" : "drop"}}}]}},{ "rule": { "family" : "ip", "table" : "filter", "chain"  : "output", "handle" : 13,"flags" : 0, "expr" : [ { "type" : "payload", "dreg" : 1, "offset" : 9, "len" : 1, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 1, "data0" : "0x00000006"}}}, { "type" : "payload", "dreg" : 1, "offset" : 2, "len" : 2, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 2, "data0" : "0x00001600"}}}, { "type" : "counter", "pkts" : 0, "bytes" : 0}]}}]}
> +
> diff --git a/tests/nft-parsing-test.c b/tests/nft-parsing-test.c
> index ecde0e2..bf95205 100644
> --- a/tests/nft-parsing-test.c
> +++ b/tests/nft-parsing-test.c
> @@ -6,6 +6,7 @@
>  #include <errno.h>
>  
>  #include <libmnl/libmnl.h> /*nlmsghdr*/
> +#include <libnftables/ruleset.h>
>  #include <libnftables/table.h>
>  #include <libnftables/chain.h>
>  #include <libnftables/rule.h>
> @@ -24,10 +25,12 @@ enum {
>  	TEST_XML_CHAIN,
>  	TEST_XML_RULE,
>  	TEST_XML_SET,
> +	TEST_XML_RULESET,
>  	TEST_JSON_TABLE,
>  	TEST_JSON_CHAIN,
>  	TEST_JSON_RULE,
>  	TEST_JSON_SET,
> +	TEST_JSON_RULESET,
>  };
>  
>  #if defined(XML_PARSING) || defined(JSON_PARSING)
> @@ -76,6 +79,7 @@ static int compare_test(uint32_t type, void *input, const char *filename)
>  	struct nft_chain *c = NULL;
>  	struct nft_rule *r = NULL;
>  	struct nft_set *s = NULL;
> +	struct nft_ruleset *rs = NULL;
>  	char orig[4096];
>  	char out[4096];
>  	FILE *fp;
> @@ -97,6 +101,10 @@ static int compare_test(uint32_t type, void *input, const char *filename)
>  	case TEST_JSON_SET:
>  		s = (struct nft_set *)input;
>  		break;
> +	case TEST_XML_RULESET:
> +	case TEST_JSON_RULESET:
> +		rs = (struct nft_ruleset *)input;
> +		break;
>  	default:
>  		errno = EINVAL;
>  		return -1;
> @@ -127,6 +135,14 @@ static int compare_test(uint32_t type, void *input, const char *filename)
>  	case TEST_JSON_SET:
>  		nft_set_snprintf(out, sizeof(out), s, NFT_SET_O_JSON, 0);
>  		break;
> +	case TEST_XML_RULESET:
> +		nft_ruleset_snprintf(out, sizeof(out), rs,
> +				     NFT_RULESET_O_XML, 0);
> +		break;
> +	case TEST_JSON_RULESET:
> +		nft_ruleset_snprintf(out, sizeof(out), rs,
> +				     NFT_RULESET_O_JSON, 0);
> +		break;
>  	default:
>  		errno = EINVAL;
>  		return -1;
> @@ -159,6 +175,7 @@ static int test_json(const char *filename)
>  	struct nft_chain *c;
>  	struct nft_rule *r;
>  	struct nft_set *s;
> +	struct nft_ruleset *rs;
>  	json_t *root;
>  	json_error_t error;
>  	char *json;
> @@ -211,6 +228,16 @@ static int test_json(const char *filename)
>  
>  			nft_set_free(s);
>  			}
> +	} else if (json_object_get(root, "nftables") != NULL) {
> +		rs = nft_ruleset_alloc();
> +		if (rs != NULL) {
> +			if (nft_ruleset_parse(rs, NFT_RULESET_PARSE_JSON, json) == 0)
> +				ret = compare_test(TEST_JSON_RULESET, rs, filename);
> +			else
> +				goto failparsing;
> +
> +			nft_ruleset_free(rs);
> +			}
>  	}
>  
>  	free(json);
> @@ -237,6 +264,7 @@ static int test_xml(const char *filename)
>  	struct nft_chain *c;
>  	struct nft_rule *r;
>  	struct nft_set *s;
> +	struct nft_ruleset *rs;
>  	FILE *fp;
>  	mxml_node_t *tree;
>  	char *xml;
> @@ -293,6 +321,18 @@ static int test_xml(const char *filename)
>  
>  			nft_set_free(s);
>  		}
> +	} else if (strcmp(tree->value.opaque, "nftables") == 0) {
> +		rs = nft_ruleset_alloc();
> +		if (rs != NULL) {
> +			if (nft_ruleset_parse(rs, NFT_RULESET_PARSE_XML,
> +					      xml) == 0)
> +				ret = compare_test(TEST_XML_RULESET, rs,
> +						   filename);
> +			else
> +				goto failparsing;
> +
> +			nft_ruleset_free(rs);
> +		}
>  	}
>  
>  	return ret;
> diff --git a/tests/xmlfiles/75-ruleset.xml b/tests/xmlfiles/75-ruleset.xml
> new file mode 100644
> index 0000000..21bb9be
> --- /dev/null
> +++ b/tests/xmlfiles/75-ruleset.xml
> @@ -0,0 +1 @@
> +<nftables><table><name>filter</name><family>ip</family><flags>0</flags></table><table><name>nat</name><family>ip</family><flags>0</flags></table><chain><name>input</name><handle>1</handle><bytes>0</bytes><packets>0</packets><table>filter</table><family>ip</family></chain><chain><name>pre</name><handle>1</handle><bytes>0</bytes><packets>0</packets><table>nat</table><family>ip</family></chain><set><family>ip</family><table>filter</table><name>set0</name><flags>3</flags><key_type>12</key_type><key_len>2</key_len><data_type>0</data_type><data_len>0</data_len><set_elem><flags>0</flags><key><data_reg type="value"><len>2</len><data0>0x000001bb</data0></data_reg></key></set_elem><set_elem><flags>0</flags><key><data_reg type="value"><len>2</len><data0>0x00000019</data0></data_reg></key></set_elem><set_elem><flags>0</flags><key><data_reg type="value"><len>2</len><data0>0x00000016</data0></data_reg></key></set_elem></set><set><family>ip</family><table>nat</table><name>set0</name><flags
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arturo Borrero Sept. 25, 2013, 9:11 p.m. UTC | #2
On 25 September 2013 22:38, Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> @Arturo, could you resolve the following issues in Alvaro's patch and
> send a new version? Thanks.

Sure!

best regards.
diff mbox

Patch

diff --git a/include/libnftables/Makefile.am b/include/libnftables/Makefile.am
index b052992..e243f32 100644
--- a/include/libnftables/Makefile.am
+++ b/include/libnftables/Makefile.am
@@ -2,4 +2,5 @@  pkginclude_HEADERS = table.h		\
 		     chain.h		\
 		     rule.h		\
 		     expr.h		\
-		     set.h
+		     set.h		\
+		     ruleset.h
diff --git a/include/libnftables/ruleset.h b/include/libnftables/ruleset.h
new file mode 100644
index 0000000..a4a1279
--- /dev/null
+++ b/include/libnftables/ruleset.h
@@ -0,0 +1,45 @@ 
+#ifndef _RULESET_H_
+#define _RULESET_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nft_ruleset;
+
+struct nft_ruleset *nft_ruleset_alloc(void);
+void nft_ruleset_free(struct nft_ruleset *r);
+
+enum {
+	NFT_RULESET_ATTR_TABLELIST = 0,
+	NFT_RULESET_ATTR_CHAINLIST,
+	NFT_RULESET_ATTR_SETLIST,
+	NFT_RULESET_ATTR_RULELIST,
+};
+
+bool nft_ruleset_attr_is_set(const struct nft_ruleset *r, uint16_t attr);
+void nft_ruleset_attr_unset(struct nft_ruleset *r, uint16_t attr);
+void nft_ruleset_attr_set(struct nft_ruleset *r, uint16_t attr, void *data);
+const void *nft_ruleset_attr_get(const struct nft_ruleset *r, uint16_t attr);
+
+enum {
+	NFT_RULESET_O_DEFAULT	= 0,
+	NFT_RULESET_O_XML,
+	NFT_RULESET_O_JSON,
+};
+
+enum nft_ruleset_parse_type {
+	NFT_RULESET_PARSE_NONE	= 0,
+	NFT_RULESET_PARSE_XML,
+	NFT_RULESET_PARSE_JSON,
+	NFT_RULESET_PARSE_MAX,
+};
+
+int nft_ruleset_parse(struct nft_ruleset *rs, enum nft_ruleset_parse_type type, const char *data);
+int nft_ruleset_snprintf(char *buf, size_t size, const struct nft_ruleset *rs, uint32_t type, uint32_t flags);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* _RULESET_H_ */
diff --git a/src/Makefile.am b/src/Makefile.am
index 51b40a2..474dbf0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -10,6 +10,7 @@  libnftables_la_SOURCES = utils.c		\
 			 rule.c			\
 			 set.c			\
 			 set_elem.c		\
+			 ruleset.c		\
 			 mxml.c			\
 			 jansson.c		\
 			 expr.c			\
diff --git a/src/chain.c b/src/chain.c
index 8c0d804..e3f70e0 100644
--- a/src/chain.c
+++ b/src/chain.c
@@ -506,7 +506,7 @@  static inline int nft_str2hooknum(int family, const char *hook)
 }
 
 #ifdef JSON_PARSING
-static int nft_jansson_parse_chain(struct nft_chain *c, json_t *tree)
+int nft_jansson_parse_chain(struct nft_chain *c, json_t *tree)
 {
 	json_t *root;
 	uint64_t uval64;
diff --git a/src/internal.h b/src/internal.h
index df64dd8..b29288a 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -71,6 +71,14 @@  int nft_jansson_data_reg_parse(json_t *root, const char *tag,
 			       union nft_data_reg *data_reg);
 struct nft_set_elem;
 int nft_set_elem_json_parse(struct nft_set_elem *e, json_t *root);
+struct nft_table;
+int nft_jansson_parse_table(struct nft_table *t, json_t *tree);
+struct nft_chain;
+int nft_jansson_parse_chain(struct nft_chain *c, json_t *tree);
+struct nft_rule;
+int nft_jansson_parse_rule(struct nft_rule *r, json_t *tree);
+struct nft_set;
+int nft_jansson_parse_set(struct nft_set *s, json_t *tree);
 #endif
 
 const char *nft_family2str(uint32_t family);
diff --git a/src/libnftables.map b/src/libnftables.map
index 963c03e..1223403 100644
--- a/src/libnftables.map
+++ b/src/libnftables.map
@@ -168,5 +168,14 @@  global:
   nft_set_elems_iter_next;
   nft_set_elems_iter_destroy;
 
+  nft_ruleset_alloc;
+  nft_ruleset_free;
+  nft_ruleset_attr_is_set;
+  nft_ruleset_attr_unset;
+  nft_ruleset_attr_set;
+  nft_ruleset_attr_get;
+  nft_ruleset_parse;
+  nft_ruleset_snprintf;
+
 local: *;
 };
diff --git a/src/rule.c b/src/rule.c
index e593109..c8e1b99 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -470,7 +470,7 @@  int nft_rule_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_rule *r)
 EXPORT_SYMBOL(nft_rule_nlmsg_parse);
 
 #ifdef JSON_PARSING
-static int nft_jansson_parse_rule(struct nft_rule *r, json_t *tree)
+int nft_jansson_parse_rule(struct nft_rule *r, json_t *tree)
 {
 	json_t *root, *array;
 	struct nft_rule_expr *e;
diff --git a/src/ruleset.c b/src/ruleset.c
new file mode 100644
index 0000000..2c30a56
--- /dev/null
+++ b/src/ruleset.c
@@ -0,0 +1,759 @@ 
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
+ * (C) 2013 by Alvaro Neira Ayuso <alvaroneay@gmail.com>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <errno.h>
+
+#include "internal.h"
+
+#include <libmnl/libmnl.h>
+#include <libnftables/ruleset.h>
+#include <libnftables/table.h>
+#include <libnftables/chain.h>
+#include <libnftables/set.h>
+#include <libnftables/rule.h>
+
+struct nft_ruleset {
+	struct nft_table_list	*table_list;
+	struct nft_chain_list	*chain_list;
+	struct nft_set_list	*set_list;
+	struct nft_rule_list	*rule_list;
+
+	uint16_t		flags;
+};
+
+struct nft_ruleset *nft_ruleset_alloc(void)
+{
+	return calloc(1, sizeof(struct nft_ruleset));
+}
+EXPORT_SYMBOL(nft_ruleset_alloc);
+
+void nft_ruleset_free(struct nft_ruleset *r)
+{
+	if (r->flags & (1 << NFT_RULESET_ATTR_TABLELIST))
+		nft_table_list_free(r->table_list);
+
+	if (r->flags & (1 << NFT_RULESET_ATTR_CHAINLIST))
+		nft_chain_list_free(r->chain_list);
+
+	if (r->flags & (1 << NFT_RULESET_ATTR_SETLIST))
+		nft_set_list_free(r->set_list);
+
+	if (r->flags & (1 << NFT_RULESET_ATTR_RULELIST))
+		nft_rule_list_free(r->rule_list);
+
+	xfree(r);
+}
+EXPORT_SYMBOL(nft_ruleset_free);
+
+bool nft_ruleset_attr_is_set(const struct nft_ruleset *r, uint16_t attr)
+{
+	return r->flags & (1 << attr);
+}
+EXPORT_SYMBOL(nft_ruleset_attr_is_set);
+
+void nft_ruleset_attr_unset(struct nft_ruleset *r, uint16_t attr)
+{
+	if (!(r->flags & (1 << attr)))
+		return;
+
+	switch (attr) {
+	case NFT_RULESET_ATTR_TABLELIST:
+		nft_table_list_free(r->table_list);
+		r->table_list = NULL;
+		break;
+	case NFT_RULESET_ATTR_CHAINLIST:
+		nft_chain_list_free(r->chain_list);
+		r->chain_list = NULL;
+		break;
+	case NFT_RULESET_ATTR_SETLIST:
+		nft_set_list_free(r->set_list);
+		r->set_list = NULL;
+		break;
+	case NFT_RULESET_ATTR_RULELIST:
+		nft_rule_list_free(r->rule_list);
+		r->rule_list = NULL;
+		break;
+	}
+	r->flags &= ~(1 << attr);
+}
+EXPORT_SYMBOL(nft_ruleset_attr_unset);
+
+void nft_ruleset_attr_set(struct nft_ruleset *r, uint16_t attr, void *data)
+{
+	switch (attr) {
+	case NFT_RULESET_ATTR_TABLELIST:
+		nft_ruleset_attr_unset(r, NFT_RULESET_ATTR_TABLELIST);
+		r->table_list = data;
+		break;
+	case NFT_RULESET_ATTR_CHAINLIST:
+		nft_ruleset_attr_unset(r, NFT_RULESET_ATTR_CHAINLIST);
+		r->chain_list = data;
+		break;
+	case NFT_RULESET_ATTR_SETLIST:
+		nft_ruleset_attr_unset(r, NFT_RULESET_ATTR_SETLIST);
+		r->set_list = data;
+		break;
+	case NFT_RULESET_ATTR_RULELIST:
+		nft_ruleset_attr_unset(r, NFT_RULESET_ATTR_RULELIST);
+		r->rule_list = data;
+		break;
+	default:
+		return;
+	}
+	r->flags |= (1 << attr);
+}
+EXPORT_SYMBOL(nft_ruleset_attr_set);
+
+const void *nft_ruleset_attr_get(const struct nft_ruleset *r, uint16_t attr)
+{
+	if (!(r->flags & (1 << attr)))
+		return NULL;
+
+	switch (attr) {
+	case NFT_RULESET_ATTR_TABLELIST:
+		return r->table_list;
+	case NFT_RULESET_ATTR_CHAINLIST:
+		return r->chain_list;
+	case NFT_RULESET_ATTR_SETLIST:
+		return r->set_list;
+	case NFT_RULESET_ATTR_RULELIST:
+		return r->rule_list;
+	default:
+		return NULL;
+	}
+}
+EXPORT_SYMBOL(nft_ruleset_attr_get);
+
+#ifdef JSON_PARSING
+static int nft_ruleset_json_parse_table(struct nft_table_list *table_list,
+					json_t *root)
+{
+	struct nft_table *t;
+
+	t = nft_table_alloc();
+	if (t == NULL)
+		goto err;
+
+	if (nft_jansson_parse_table(t, root) < 0)
+		goto err;
+
+	nft_table_list_add_tail(t, table_list);
+
+	return 0;
+err:
+	nft_table_free(t);
+	return -1;
+}
+
+static int nft_ruleset_json_parse_chain(struct nft_chain_list *chain_list,
+					json_t *root)
+{
+	struct nft_chain *c;
+
+	c = nft_chain_alloc();
+	if (c == NULL)
+		goto err;
+
+	if (nft_jansson_parse_chain(c, root) < 0)
+		goto err;
+
+	nft_chain_list_add_tail(c, chain_list);
+
+	return 0;
+err:
+	nft_chain_free(c);
+	return -1;
+}
+
+static int nft_ruleset_json_parse_rule(struct nft_rule_list *rule_list,
+				       json_t *root)
+{
+	struct nft_rule *r;
+
+	r = nft_rule_alloc();
+	if (r == NULL)
+		goto err;
+
+	if (nft_jansson_parse_rule(r, root) < 0)
+		goto err;
+
+	nft_rule_list_add_tail(r, rule_list);
+
+	return 0;
+err:
+	nft_rule_free(r);
+	return -1;
+}
+
+static int nft_ruleset_json_parse_set(struct nft_set_list *set_list,
+				      json_t *root)
+{
+	struct nft_set *s;
+
+	s = nft_set_alloc();
+	if (s == NULL)
+		goto err;
+
+	if (nft_jansson_parse_set(s, root) < 0)
+		goto err;
+
+	nft_set_list_add_tail(s, set_list);
+
+	return 0;
+err:
+	nft_set_free(s);
+	return -1;
+}
+
+static int nft_jansson_parse_ruleset(struct nft_ruleset *rs, json_t *tree)
+{
+	json_t *node, *array;
+	int i;
+	struct nft_table_list *table_list = nft_table_list_alloc();
+	struct nft_chain_list *chain_list = nft_chain_list_alloc();
+	struct nft_set_list *set_list = nft_set_list_alloc();
+	struct nft_rule_list *rule_list = nft_rule_list_alloc();
+
+	if (table_list == NULL || chain_list == NULL || set_list == NULL ||
+	    rule_list == NULL) {
+		errno = ENOMEM;
+		goto err;
+	}
+
+	array = nft_jansson_get_node(tree, "nftables");
+	if (array == NULL)
+		return -1;
+
+	for (i = 0; i < json_array_size(array); ++i) {
+		node = json_array_get(array, i);
+		if (nft_jansson_node_exist(node, "table"))
+			if (nft_ruleset_json_parse_table(table_list, node) < 0)
+				goto err;
+		if (nft_jansson_node_exist(node, "chain"))
+			if (nft_ruleset_json_parse_chain(chain_list, node) < 0)
+				goto err;
+		if (nft_jansson_node_exist(node, "rule"))
+			if (nft_ruleset_json_parse_rule(rule_list, node) < 0)
+				goto err;
+		if (nft_jansson_node_exist(node, "set"))
+			if (nft_ruleset_json_parse_set(set_list, node) < 0)
+				goto err;
+	}
+
+	if (nft_table_list_is_empty(table_list)
+	    && nft_chain_list_is_empty(chain_list)
+	    && nft_set_list_is_empty(set_list)
+	    && nft_rule_list_is_empty(rule_list)) {
+		errno = EINVAL;
+		goto err;
+	}
+
+	if (!nft_table_list_is_empty(table_list))
+		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_TABLELIST,
+				     table_list);
+	else
+		nft_table_list_free(table_list);
+
+	if (!nft_chain_list_is_empty(chain_list))
+		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_CHAINLIST,
+				     chain_list);
+	else
+		nft_chain_list_free(chain_list);
+
+	if (!nft_set_list_is_empty(set_list))
+		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_SETLIST, set_list);
+	else
+		nft_set_list_free(set_list);
+
+	if (!nft_rule_list_is_empty(rule_list))
+		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_RULELIST, rule_list);
+	else
+		nft_rule_list_free(rule_list);
+
+	nft_jansson_free_root(tree);
+	return 0;
+
+err:
+	if (table_list)
+		nft_table_list_free(table_list);
+	if (chain_list)
+		nft_chain_list_free(chain_list);
+	if (set_list)
+		nft_set_list_free(set_list);
+	if (rule_list)
+		nft_rule_list_free(rule_list);
+
+	nft_jansson_free_root(tree);
+	return -1;
+}
+#endif
+
+static int nft_ruleset_json_parse(struct nft_ruleset *rs, const char *json)
+{
+#ifdef JSON_PARSING
+	json_t *tree;
+	json_error_t error;
+
+	tree = nft_jansson_create_root(json, &error);
+	if (tree == NULL)
+		return -1;
+
+	return nft_jansson_parse_ruleset(rs, tree);
+#else
+	errno = EOPNOTSUPP;
+	return -1;
+#endif
+}
+
+#ifdef XML_PARSING
+static int
+nft_ruleset_xml_parse_tables(struct nft_ruleset *rs, mxml_node_t *tree)
+{
+	mxml_node_t *node;
+	struct nft_table *t;
+	struct nft_table_list *table_list = nft_table_list_alloc();
+	if (table_list == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	for (node = mxmlFindElement(tree, tree, "table", NULL, NULL,
+				    MXML_DESCEND_FIRST);
+	     node != NULL;
+	     node = mxmlFindElement(node, tree, "table", NULL, NULL,
+				    MXML_NO_DESCEND)) {
+		t = nft_table_alloc();
+		if (t == NULL)
+			goto err_free;
+
+		if (nft_mxml_table_parse(node, t) != 0) {
+			nft_table_free(t);
+			goto err_free;
+		}
+
+		nft_table_list_add_tail(t, table_list);
+	}
+
+	if (!nft_table_list_is_empty(table_list))
+		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_TABLELIST,
+				     table_list);
+	else
+		nft_table_list_free(table_list);
+
+	return 0;
+err_free:
+	nft_table_list_free(table_list);
+	return -1;
+}
+
+static int
+nft_ruleset_xml_parse_chains(struct nft_ruleset *rs, mxml_node_t *tree)
+{
+	mxml_node_t *node;
+	struct nft_chain *c;
+	struct nft_chain_list *chain_list = nft_chain_list_alloc();
+	if (chain_list == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	for (node = mxmlFindElement(tree, tree, "chain", NULL, NULL,
+				    MXML_DESCEND_FIRST);
+	     node != NULL;
+	     node = mxmlFindElement(node, tree, "chain", NULL, NULL,
+				    MXML_NO_DESCEND)) {
+		c = nft_chain_alloc();
+		if (c == NULL)
+			goto err_free;
+
+		if (nft_mxml_chain_parse(node, c) != 0) {
+			nft_chain_free(c);
+			goto err_free;
+		}
+
+		nft_chain_list_add_tail(c, chain_list);
+	}
+
+	if (!nft_chain_list_is_empty(chain_list))
+		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_CHAINLIST,
+				     chain_list);
+	else
+		nft_chain_list_free(chain_list);
+
+	return 0;
+err_free:
+	nft_chain_list_free(chain_list);
+	return -1;
+}
+
+static int
+nft_ruleset_xml_parse_rules(struct nft_ruleset *rs, mxml_node_t *tree)
+{
+	mxml_node_t *node;
+	struct nft_rule *r;
+	struct nft_rule_list *rule_list = nft_rule_list_alloc();
+	if (rule_list == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	for (node = mxmlFindElement(tree, tree, "rule", NULL, NULL,
+				    MXML_DESCEND_FIRST);
+	     node != NULL;
+	     node = mxmlFindElement(node, tree, "rule", NULL, NULL,
+				    MXML_NO_DESCEND)) {
+		r = nft_rule_alloc();
+		if (r == NULL)
+			goto err_free;
+
+		if (nft_mxml_rule_parse(node, r) != 0) {
+			nft_rule_free(r);
+			goto err_free;
+		}
+
+		nft_rule_list_add_tail(r, rule_list);
+	}
+
+	if (!nft_rule_list_is_empty(rule_list))
+		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_RULELIST, rule_list);
+	else
+		nft_rule_list_free(rule_list);
+
+	return 0;
+err_free:
+	nft_rule_list_free(rule_list);
+	return -1;
+}
+
+static int
+nft_ruleset_xml_parse_sets(struct nft_ruleset *rs, mxml_node_t *tree)
+{
+	mxml_node_t *node;
+	struct nft_set *s;
+	struct nft_set_list *set_list = nft_set_list_alloc();
+	if (set_list == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	for (node = mxmlFindElement(tree, tree, "set", NULL, NULL,
+				    MXML_DESCEND_FIRST);
+	     node != NULL;
+	     node = mxmlFindElement(node, tree, "set", NULL, NULL,
+				    MXML_NO_DESCEND)) {
+		s = nft_set_alloc();
+		if (s == NULL)
+			goto err_free;
+
+		if (nft_mxml_set_parse(node, s) != 0) {
+			nft_set_free(s);
+			goto err_free;
+		}
+
+		nft_set_list_add_tail(s, set_list);
+	}
+
+	if (!nft_set_list_is_empty(set_list))
+		nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_SETLIST, set_list);
+	else
+		nft_set_list_free(set_list);
+
+	return 0;
+err_free:
+	nft_set_list_free(set_list);
+	return -1;
+}
+#endif
+
+static int nft_ruleset_xml_parse(struct nft_ruleset *rs, const char *xml)
+{
+#ifdef XML_PARSING
+	mxml_node_t *tree;
+
+	tree = nft_mxml_build_tree(xml, "nftables");
+	if (tree == NULL)
+		return -1;
+
+	if (nft_ruleset_xml_parse_tables(rs, tree) != 0)
+		goto err;
+
+	if (nft_ruleset_xml_parse_chains(rs, tree) != 0)
+		goto err;
+
+	if (nft_ruleset_xml_parse_sets(rs, tree) != 0)
+		goto err;
+
+	if (nft_ruleset_xml_parse_rules(rs, tree) != 0)
+		goto err;
+
+	mxmlDelete(tree);
+
+	if (!(nft_ruleset_attr_is_set(rs, NFT_RULESET_ATTR_TABLELIST))
+		&& !(nft_ruleset_attr_is_set(rs, NFT_RULESET_ATTR_CHAINLIST))
+		&& !(nft_ruleset_attr_is_set(rs, NFT_RULESET_ATTR_SETLIST))
+		&& !(nft_ruleset_attr_is_set(rs, NFT_RULESET_ATTR_RULELIST))) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	return 0;
+err:
+	mxmlDelete(tree);
+	return -1;
+#else
+	errno = EOPNOTSUPP;
+	return -1;
+#endif
+}
+
+int nft_ruleset_parse(struct nft_ruleset *r, enum nft_ruleset_parse_type type,
+		      const char *data)
+{
+	int ret;
+
+	switch (type) {
+	case NFT_RULESET_PARSE_XML:
+		ret = nft_ruleset_xml_parse(r, data);
+		break;
+	case NFT_RULESET_PARSE_JSON:
+		ret = nft_ruleset_json_parse(r, data);
+		break;
+	default:
+		ret = -1;
+		errno = EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(nft_ruleset_parse);
+
+static int separator_snprintf(char *buf, size_t size, void *obj, uint32_t type)
+{
+	if (obj == NULL)
+		return 0;
+
+	if (type == NFT_RULESET_O_JSON)
+		return snprintf(buf, size, ",");
+
+	if (type == NFT_RULESET_O_DEFAULT)
+		return snprintf(buf, size, "\n");
+
+	return 0;
+}
+
+static int
+list_separator_snprintf(char *buf, size_t size, void *prev, void *next,
+			uint32_t type)
+{
+	if (prev == NULL || next == NULL)
+		return 0;
+
+	return separator_snprintf(buf, size, prev, type);
+}
+
+static int
+nft_ruleset_snprintf_table(char *buf, size_t size,
+			   const struct nft_ruleset *rs, uint32_t type,
+			   uint32_t flags)
+{
+	struct nft_table *t;
+	struct nft_table_list_iter *ti;
+	int ret, len = size, offset = 0;
+
+	ti = nft_table_list_iter_create(rs->table_list);
+	if (ti == NULL)
+		return 0;
+
+	t = nft_table_list_iter_next(ti);
+	while (t != NULL) {
+		ret = nft_table_snprintf(buf+offset, size, t, type, flags);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+		t = nft_table_list_iter_next(ti);
+		ret = separator_snprintf(buf+offset, size, t, type);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+	nft_table_list_iter_destroy(ti);
+
+	return offset;
+}
+
+static int
+nft_ruleset_snprintf_chain(char *buf, size_t size,
+			   const struct nft_ruleset *rs, uint32_t type,
+			   uint32_t flags)
+{
+	struct nft_chain *c;
+	struct nft_chain_list_iter *ci;
+	int ret, len = size, offset = 0;
+
+	ci = nft_chain_list_iter_create(rs->chain_list);
+	if (ci == NULL)
+		return 0;
+
+	c = nft_chain_list_iter_next(ci);
+	while (c != NULL) {
+		ret = nft_chain_snprintf(buf+offset, size, c, type, flags);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+		c = nft_chain_list_iter_next(ci);
+		ret = separator_snprintf(buf+offset, size, c, type);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+	nft_chain_list_iter_destroy(ci);
+
+	return offset;
+}
+
+static int
+nft_ruleset_snprintf_set(char *buf, size_t size,
+			 const struct nft_ruleset *rs, uint32_t type,
+			 uint32_t flags)
+{
+	struct nft_set *s;
+	struct nft_set_list_iter *si;
+	int ret, len = size, offset = 0;
+
+	si = nft_set_list_iter_create(rs->set_list);
+	if (si == NULL)
+		return 0;
+
+	s = nft_set_list_iter_next(si);
+	while (s != NULL) {
+		ret = nft_set_snprintf(buf+offset, size, s, type, flags);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+		s = nft_set_list_iter_next(si);
+		ret = separator_snprintf(buf+offset, size, s, type);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+	nft_set_list_iter_destroy(si);
+
+	return offset;
+}
+
+static int
+nft_ruleset_snprintf_rule(char *buf, size_t size,
+			  const struct nft_ruleset *rs, uint32_t type,
+			  uint32_t flags)
+{
+	struct nft_rule *r;
+	struct nft_rule_list_iter *ri;
+	int ret, len = size, offset = 0;
+
+	ri = nft_rule_list_iter_create(rs->rule_list);
+	if (ri == NULL)
+		return 0;
+
+	r = nft_rule_list_iter_next(ri);
+	while (r != NULL) {
+		ret = nft_rule_snprintf(buf+offset, size, r, type, flags);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+		r = nft_rule_list_iter_next(ri);
+		ret = separator_snprintf(buf+offset, size, r, type);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+	nft_rule_list_iter_destroy(ri);
+
+	return offset;
+}
+
+static int
+nft_ruleset_do_snprintf(char *buf, size_t size, const struct nft_ruleset *rs,
+			uint32_t type, uint32_t flags)
+{
+	int ret, len = size, offset = 0;
+
+	ret = nft_ruleset_snprintf_table(buf+offset, size, rs, type, flags);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = list_separator_snprintf(buf+offset, size,
+				      rs->table_list, rs->chain_list, type);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = nft_ruleset_snprintf_chain(buf+offset, size, rs, type, flags);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = list_separator_snprintf(buf+offset, size,
+				      rs->chain_list, rs->set_list, type);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = nft_ruleset_snprintf_set(buf+offset, size, rs, type, flags);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = list_separator_snprintf(buf+offset, size,
+				      rs->set_list, rs->rule_list, type);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = nft_ruleset_snprintf_rule(buf+offset, size, rs, type, flags);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
+static int
+nft_ruleset_snprintf_xml(char *buf, size_t size, const struct nft_ruleset *rs,
+			 uint32_t flags)
+{
+	int ret, len = size, offset = 0;
+
+	ret = snprintf(buf, size, "<nftables>");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = nft_ruleset_do_snprintf(buf+offset, size, rs, NFT_RULESET_O_XML,
+				      flags);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = snprintf(buf+offset, size, "</nftables>");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
+static int
+nft_ruleset_snprintf_json(char *buf, size_t size, const struct nft_ruleset *rs,
+			  uint32_t flags)
+{
+	int ret, len = size, offset = 0;
+
+	ret = snprintf(buf, size, "{ \"nftables\": [");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = nft_ruleset_do_snprintf(buf+offset, size, rs, NFT_RULESET_O_JSON,
+				      flags);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = snprintf(buf+offset, size, "]}\n");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
+int nft_ruleset_snprintf(char *buf, size_t size, const struct nft_ruleset *r,
+			 uint32_t type, uint32_t flags)
+{
+	switch (type) {
+	case NFT_RULESET_O_DEFAULT:
+		return nft_ruleset_do_snprintf(buf, size, r, type, flags);
+	case NFT_RULESET_O_XML:
+		return nft_ruleset_snprintf_xml(buf, size, r, flags);
+	case NFT_RULESET_O_JSON:
+		return nft_ruleset_snprintf_json(buf, size, r, flags);
+	default:
+		break;
+	}
+	return -1;
+}
+EXPORT_SYMBOL(nft_ruleset_snprintf);
diff --git a/src/set.c b/src/set.c
index 530776d..586fae1 100644
--- a/src/set.c
+++ b/src/set.c
@@ -304,7 +304,7 @@  int nft_set_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_set *s)
 EXPORT_SYMBOL(nft_set_nlmsg_parse);
 
 #ifdef JSON_PARSING
-static int nft_jansson_parse_set(struct nft_set *s, json_t *tree)
+int nft_jansson_parse_set(struct nft_set *s, json_t *tree)
 {
 	json_t *root, *array, *json_elem;
 	uint32_t uval32;
diff --git a/src/table.c b/src/table.c
index c095053..7f14b32 100644
--- a/src/table.c
+++ b/src/table.c
@@ -272,7 +272,7 @@  static int nft_table_xml_parse(struct nft_table *t, const char *xml)
 }
 
 #ifdef JSON_PARSING
-static int nft_jansson_parse_table(struct nft_table *t, json_t *tree)
+int nft_jansson_parse_table(struct nft_table *t, json_t *tree)
 {
 	json_t *root;
 	uint32_t flags;
diff --git a/tests/jsonfiles/64-ruleset.json b/tests/jsonfiles/64-ruleset.json
new file mode 100644
index 0000000..39b700c
--- /dev/null
+++ b/tests/jsonfiles/64-ruleset.json
@@ -0,0 +1,2 @@ 
+{ "nftables": [{"table" : {"name" : "filter","family" : "ip","flags" : 0}},{"table" : {"name" : "filter2","family" : "ip6","flags" : 0}},{ "chain": {"name": "input","handle": 1,"bytes": 10681449,"packets": 16216,"family": "ip","table": "filter","use": 0,"type": "filter","hooknum": "input","prio": 0,"policy": "accept"}},{ "chain": {"name": "forward","handle": 2,"bytes": 0,"packets": 0,"family": "ip","table": "filter","use": 0,"type": "filter","hooknum": "forward","prio": 0,"policy": "accept"}},{ "chain": {"name": "output","handle": 3,"bytes": 2375830,"packets": 15184,"family": "ip","table": "filter","use": 0,"type": "filter","hooknum": "output","prio": 0,"policy": "accept"}},{ "chain": {"name": "chain1","handle": 4,"bytes": 0,"packets": 0,"family": "ip","table": "filter","use": 0}},{ "set": { "name": "set0","table": "filter","flags": 3,"family": "ip","key_type": 12,"key_len": 2}},{ "rule": { "family" : "ip", "table" : "filter", "chain"  : "output", "handle" : 6,"flags" : 0, "expr" : [ { "type" : "payload", "dreg" : 1, "offset" : 16, "len" : 4, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 4, "data0" : "0x0100a8c0"}}}, { "type" : "counter", "pkts" : 0, "bytes" : 0}, { "type" : "immediate", "dreg" : 0, "immediatedata" : {"data_reg": {"type" : "verdict", "verdict" : "drop"}}}]}},{ "rule": { "family" : "ip", "table" : "filter", "chain"  : "output", "handle" : 9,"flags" : 0, "expr" : [ { "type" : "payload", "dreg" : 1, "offset" : 9, "len" : 1, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 1, "data0" : "0x00000006"}}}, { "type" : "payload", "dreg" : 1, "offset" : 2, "len" : 2, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 2, "data0" : "0x00001600"}}}, { "type" : "counter", "pkts" : 0, "bytes" : 0}]}},{ "rule": { "family" : "ip", "table" : "filter", "chain"  : "output", "handle" : 10,"flags" : 0, "expr" : [ { "type" : "payload", "dreg" : 1, "offset" : 16, "len" : 4, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 4, "data0" : "0x0100a8c0"}}}, { "type" : "counter", "pkts" : 0, "bytes" : 0}]}},{ "rule": { "family" : "ip", "table" : "filter", "chain"  : "output", "handle" : 11,"flags" : 0, "expr" : [ { "type" : "payload", "dreg" : 1, "offset" : 16, "len" : 4, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 4, "data0" : "0x0100a8c0"}}}, { "type" : "counter", "pkts" : 0, "bytes" : 0}, { "type" : "immediate", "dreg" : 0, "immediatedata" : {"data_reg": {"type" : "verdict", "verdict" : "drop"}}}]}},{ "rule": { "family" : "ip", "table" : "filter", "chain"  : "output", "handle" : 13,"flags" : 0, "expr" : [ { "type" : "payload", "dreg" : 1, "offset" : 9, "len" : 1, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 1, "data0" : "0x00000006"}}}, { "type" : "payload", "dreg" : 1, "offset" : 2, "len" : 2, "base" : "link"}, { "type" : "cmp", "sreg" : 1, "op" : "eq", "cmpdata" : {"data_reg": { "type" : "value", "len" : 2, "data0" : "0x00001600"}}}, { "type" : "counter", "pkts" : 0, "bytes" : 0}]}}]}
+
diff --git a/tests/nft-parsing-test.c b/tests/nft-parsing-test.c
index ecde0e2..bf95205 100644
--- a/tests/nft-parsing-test.c
+++ b/tests/nft-parsing-test.c
@@ -6,6 +6,7 @@ 
 #include <errno.h>
 
 #include <libmnl/libmnl.h> /*nlmsghdr*/
+#include <libnftables/ruleset.h>
 #include <libnftables/table.h>
 #include <libnftables/chain.h>
 #include <libnftables/rule.h>
@@ -24,10 +25,12 @@  enum {
 	TEST_XML_CHAIN,
 	TEST_XML_RULE,
 	TEST_XML_SET,
+	TEST_XML_RULESET,
 	TEST_JSON_TABLE,
 	TEST_JSON_CHAIN,
 	TEST_JSON_RULE,
 	TEST_JSON_SET,
+	TEST_JSON_RULESET,
 };
 
 #if defined(XML_PARSING) || defined(JSON_PARSING)
@@ -76,6 +79,7 @@  static int compare_test(uint32_t type, void *input, const char *filename)
 	struct nft_chain *c = NULL;
 	struct nft_rule *r = NULL;
 	struct nft_set *s = NULL;
+	struct nft_ruleset *rs = NULL;
 	char orig[4096];
 	char out[4096];
 	FILE *fp;
@@ -97,6 +101,10 @@  static int compare_test(uint32_t type, void *input, const char *filename)
 	case TEST_JSON_SET:
 		s = (struct nft_set *)input;
 		break;
+	case TEST_XML_RULESET:
+	case TEST_JSON_RULESET:
+		rs = (struct nft_ruleset *)input;
+		break;
 	default:
 		errno = EINVAL;
 		return -1;
@@ -127,6 +135,14 @@  static int compare_test(uint32_t type, void *input, const char *filename)
 	case TEST_JSON_SET:
 		nft_set_snprintf(out, sizeof(out), s, NFT_SET_O_JSON, 0);
 		break;
+	case TEST_XML_RULESET:
+		nft_ruleset_snprintf(out, sizeof(out), rs,
+				     NFT_RULESET_O_XML, 0);
+		break;
+	case TEST_JSON_RULESET:
+		nft_ruleset_snprintf(out, sizeof(out), rs,
+				     NFT_RULESET_O_JSON, 0);
+		break;
 	default:
 		errno = EINVAL;
 		return -1;
@@ -159,6 +175,7 @@  static int test_json(const char *filename)
 	struct nft_chain *c;
 	struct nft_rule *r;
 	struct nft_set *s;
+	struct nft_ruleset *rs;
 	json_t *root;
 	json_error_t error;
 	char *json;
@@ -211,6 +228,16 @@  static int test_json(const char *filename)
 
 			nft_set_free(s);
 			}
+	} else if (json_object_get(root, "nftables") != NULL) {
+		rs = nft_ruleset_alloc();
+		if (rs != NULL) {
+			if (nft_ruleset_parse(rs, NFT_RULESET_PARSE_JSON, json) == 0)
+				ret = compare_test(TEST_JSON_RULESET, rs, filename);
+			else
+				goto failparsing;
+
+			nft_ruleset_free(rs);
+			}
 	}
 
 	free(json);
@@ -237,6 +264,7 @@  static int test_xml(const char *filename)
 	struct nft_chain *c;
 	struct nft_rule *r;
 	struct nft_set *s;
+	struct nft_ruleset *rs;
 	FILE *fp;
 	mxml_node_t *tree;
 	char *xml;
@@ -293,6 +321,18 @@  static int test_xml(const char *filename)
 
 			nft_set_free(s);
 		}
+	} else if (strcmp(tree->value.opaque, "nftables") == 0) {
+		rs = nft_ruleset_alloc();
+		if (rs != NULL) {
+			if (nft_ruleset_parse(rs, NFT_RULESET_PARSE_XML,
+					      xml) == 0)
+				ret = compare_test(TEST_XML_RULESET, rs,
+						   filename);
+			else
+				goto failparsing;
+
+			nft_ruleset_free(rs);
+		}
 	}
 
 	return ret;
diff --git a/tests/xmlfiles/75-ruleset.xml b/tests/xmlfiles/75-ruleset.xml
new file mode 100644
index 0000000..21bb9be
--- /dev/null
+++ b/tests/xmlfiles/75-ruleset.xml
@@ -0,0 +1 @@ 
+<nftables><table><name>filter</name><family>ip</family><flags>0</flags></table><table><name>nat</name><family>ip</family><flags>0</flags></table><chain><name>input</name><handle>1</handle><bytes>0</bytes><packets>0</packets><table>filter</table><family>ip</family></chain><chain><name>pre</name><handle>1</handle><bytes>0</bytes><packets>0</packets><table>nat</table><family>ip</family></chain><set><family>ip</family><table>filter</table><name>set0</name><flags>3</flags><key_type>12</key_type><key_len>2</key_len><data_type>0</data_type><data_len>0</data_len><set_elem><flags>0</flags><key><data_reg type="value"><len>2</len><data0>0x000001bb</data0></data_reg></key></set_elem><set_elem><flags>0</flags><key><data_reg type="value"><len>2</len><data0>0x00000019</data0></data_reg></key></set_elem><set_elem><flags>0</flags><key><data_reg type="value"><len>2</len><data0>0x00000016</data0></data_reg></key></set_elem></set><set><family>ip</family><table>nat</table><name>set0</name><flags