* [libnftnl PATCH] set: refactor code in json parse function
@ 2015-01-23 14:49 Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 1/5] src: add command tag in json/xml export support Alvaro Neira Ayuso
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Alvaro Neira Ayuso @ 2015-01-23 14:49 UTC (permalink / raw)
To: netfilter-devel
This patch refactor code to parse the set in two functions
nft_jansson_parse_set_info and nft_jansson_parse_set. Those changes is used
in follow up patches.
Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
---
src/set.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/src/set.c b/src/set.c
index 61e0632..4fd786a 100644
--- a/src/set.c
+++ b/src/set.c
@@ -410,19 +410,15 @@ int nft_set_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_set *s)
EXPORT_SYMBOL(nft_set_nlmsg_parse);
#ifdef JSON_PARSING
-int nft_jansson_parse_set(struct nft_set *s, json_t *tree,
- struct nft_parse_err *err)
+static int nft_jansson_parse_set_info(struct nft_set *s, json_t *tree,
+ struct nft_parse_err *err)
{
- json_t *root, *array, *json_elem;
+ json_t *root = tree, *array, *json_elem;
uint32_t flags, key_type, key_len, data_type, data_len, policy, size;
int family, i;
const char *name, *table;
struct nft_set_elem *elem;
- root = nft_jansson_get_node(tree, "set", err);
- if (root == NULL)
- return -1;
-
name = nft_jansson_parse_str(root, "name", err);
if (name == NULL)
return -1;
@@ -503,6 +499,18 @@ int nft_jansson_parse_set(struct nft_set *s, json_t *tree,
return 0;
}
+
+int nft_jansson_parse_set(struct nft_set *s, json_t *tree,
+ struct nft_parse_err *err)
+{
+ json_t *root;
+
+ root = nft_jansson_get_node(tree, "set", err);
+ if (root == NULL)
+ return -1;
+
+ return nft_jansson_parse_set_info(s, root, err);
+}
#endif
static int nft_set_json_parse(struct nft_set *s, const void *json,
--
1.7.10.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [libnftnl PATCH 1/5] src: add command tag in json/xml export support
2015-01-23 14:49 [libnftnl PATCH] set: refactor code in json parse function Alvaro Neira Ayuso
@ 2015-01-23 14:49 ` Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 2/5] src: add support to import json/xml with the new syntax Alvaro Neira Ayuso
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Alvaro Neira Ayuso @ 2015-01-23 14:49 UTC (permalink / raw)
To: netfilter-devel
Currently, we can't do incremental updates. For example:
If we have a ruleset like:
table ip filter {
chain input {
type filter hook input priority 0;
}
}
The new syntax is:
{"nftables":[{"add":[{"table":{"name":"filter",...}}]}]}
Moreover, this patch adds support to export this new command tag. We support
all the actions that we can do with nft, they are:
- Add, delete and flush tables/chains.
- Add, delete, replace and insert rules.
- Add and delete sets.
- Add and delete set elements.
- Flush ruleset.
You only need to add the command tag:
{"nftables":[{"delete":[{...}, {...},...}]}]}
^^^^^^^^
command tag
The possible fields that we can use are "add", "delete", "flush", "insert",
"replace" and "flush". For example:
- Flush table or chain:
{"nftables":[{"flush":[{"table":{"name":...}}]}]}
To flush a chain. Replace the table with the chain in json format
- Delete table, chain, set or rule:
{"nftables":[{"delete":[{"chain":{"name":...}]}]}
To delete a table, set or rule. Replace chain with the table, set or rule in
json format.
- Replace a rule:
{"nftables":[{"replace":[{"rule":{...}}]}]}
- Insert a rule:
{"nftables":[{"insert":[{"rule":{...}}]}]}
We can export to the new syntax using the event flags and also you can use
the new functions nft_*_cmd_snprintf. In these functions, you can specify the
command that you want to set with your ruleset like a parameter.
Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
---
include/buffer.h | 7 ++++
include/libnftnl/chain.h | 2 +
include/libnftnl/common.h | 9 +++++
include/libnftnl/gen.h | 2 +
include/libnftnl/rule.h | 2 +
include/libnftnl/ruleset.h | 5 +++
include/libnftnl/set.h | 4 ++
include/libnftnl/table.h | 2 +
src/buffer.c | 21 ++++++++++
src/chain.c | 18 +++++++--
src/common.c | 91 +++++++++++++++++++++++---------------------
src/gen.c | 18 +++++++--
src/internal.h | 15 +++++---
src/rule.c | 19 ++++++---
src/ruleset.c | 53 ++++++++++++++++++++------
src/set.c | 18 +++++++--
src/set_elem.c | 18 +++++++--
src/table.c | 18 +++++++--
src/utils.c | 16 ++++++++
19 files changed, 251 insertions(+), 87 deletions(-)
diff --git a/include/buffer.h b/include/buffer.h
index 2b497f2..badffe6 100644
--- a/include/buffer.h
+++ b/include/buffer.h
@@ -24,7 +24,9 @@ int nft_buf_done(struct nft_buf *b);
union nft_data_reg;
int nft_buf_open(struct nft_buf *b, int type, const char *tag);
+int nft_buf_open_array(struct nft_buf *b, int type, const char *tag);
int nft_buf_close(struct nft_buf *b, int type, const char *tag);
+int nft_buf_close_array(struct nft_buf *b, int type, const char *tag);
int nft_buf_u32(struct nft_buf *b, int type, uint32_t value, const char *tag);
int nft_buf_s32(struct nft_buf *b, int type, uint32_t value, const char *tag);
@@ -76,5 +78,10 @@ int nft_buf_reg(struct nft_buf *b, int type, union nft_data_reg *reg,
#define UNIT "unit"
#define USE "use"
#define XOR "xor"
+#define ADD "add"
+#define INSERT "insert"
+#define DELETE "delete"
+#define REPLACE "replace"
+#define FLUSH "flush"
#endif
diff --git a/include/libnftnl/chain.h b/include/libnftnl/chain.h
index c11cb5e..c820a94 100644
--- a/include/libnftnl/chain.h
+++ b/include/libnftnl/chain.h
@@ -61,6 +61,8 @@ int nft_chain_parse(struct nft_chain *c, enum nft_parse_type type,
const char *data, struct nft_parse_err *err);
int nft_chain_parse_file(struct nft_chain *c, enum nft_parse_type type,
FILE *fp, struct nft_parse_err *err);
+int nft_chain_cmd_snprintf(char *buf, size_t size, struct nft_chain *t,
+ uint32_t cmd, uint32_t type, uint32_t flags);
int nft_chain_snprintf(char *buf, size_t size, struct nft_chain *t, uint32_t type, uint32_t flags);
int nft_chain_fprintf(FILE *fp, struct nft_chain *c, uint32_t type, uint32_t flags);
diff --git a/include/libnftnl/common.h b/include/libnftnl/common.h
index fa3ab60..26f15c9 100644
--- a/include/libnftnl/common.h
+++ b/include/libnftnl/common.h
@@ -21,6 +21,15 @@ enum nft_output_flags {
NFT_OF_EVENT_ANY = (NFT_OF_EVENT_NEW | NFT_OF_EVENT_DEL),
};
+enum nft_cmd_type {
+ NFT_CMD_UNSPEC = 0,
+ NFT_CMD_ADD,
+ NFT_CMD_INSERT,
+ NFT_CMD_DELETE,
+ NFT_CMD_REPLACE,
+ NFT_CMD_FLUSH,
+};
+
enum nft_parse_type {
NFT_PARSE_NONE = 0,
NFT_PARSE_XML,
diff --git a/include/libnftnl/gen.h b/include/libnftnl/gen.h
index 00753b0..ad79d92 100644
--- a/include/libnftnl/gen.h
+++ b/include/libnftnl/gen.h
@@ -39,6 +39,8 @@ struct nlmsghdr;
int nft_gen_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_gen *gen);
int nft_gen_snprintf(char *buf, size_t size, struct nft_gen *gen, uint32_t type, uint32_t flags);
+int nft_gen_cmd_snprintf(char *buf, size_t size, struct nft_gen *gen,
+ uint32_t cmd, uint32_t type, uint32_t flags);
int nft_gen_fprintf(FILE *fp, struct nft_gen *gen, uint32_t type, uint32_t flags);
#define nft_gen_nlmsg_build_hdr nft_nlmsg_build_hdr
diff --git a/include/libnftnl/rule.h b/include/libnftnl/rule.h
index 62dba59..d0432e0 100644
--- a/include/libnftnl/rule.h
+++ b/include/libnftnl/rule.h
@@ -58,6 +58,8 @@ int nft_rule_parse(struct nft_rule *r, enum nft_parse_type type,
const char *data, struct nft_parse_err *err);
int nft_rule_parse_file(struct nft_rule *r, enum nft_parse_type type,
FILE *fp, struct nft_parse_err *err);
+int nft_rule_cmd_snprintf(char *buf, size_t size, struct nft_rule *t,
+ uint32_t cmd, uint32_t type, uint32_t flags);
int nft_rule_snprintf(char *buf, size_t size, struct nft_rule *t, uint32_t type, uint32_t flags);
int nft_rule_fprintf(FILE *fp, struct nft_rule *r, uint32_t type, uint32_t flags);
diff --git a/include/libnftnl/ruleset.h b/include/libnftnl/ruleset.h
index 1a3e22f..cec6cd6 100644
--- a/include/libnftnl/ruleset.h
+++ b/include/libnftnl/ruleset.h
@@ -34,7 +34,12 @@ int nft_ruleset_parse(struct nft_ruleset *rs, enum nft_parse_type type,
const char *data, struct nft_parse_err *err);
int nft_ruleset_parse_file(struct nft_ruleset *rs, enum nft_parse_type type,
FILE *fp, struct nft_parse_err *err);
+int nft_ruleset_cmd_snprintf(char *buf, size_t size,
+ const struct nft_ruleset *rs, uint32_t cmd,
+ uint32_t type, uint32_t flags);
int nft_ruleset_snprintf(char *buf, size_t size, const struct nft_ruleset *rs, uint32_t type, uint32_t flags);
+int nft_ruleset_cmd_fprintf(FILE *fp, const struct nft_ruleset *rs,
+ uint32_t cmd, uint32_t type, uint32_t flags);
int nft_ruleset_fprintf(FILE *fp, const struct nft_ruleset *rs, uint32_t type, uint32_t flags);
#ifdef __cplusplus
diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
index 7f3504f..6aedd80 100644
--- a/include/libnftnl/set.h
+++ b/include/libnftnl/set.h
@@ -50,6 +50,8 @@ void nft_set_nlmsg_build_payload(struct nlmsghdr *nlh, struct nft_set *s);
int nft_set_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_set *s);
int nft_set_elems_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_set *s);
+int nft_set_cmd_snprintf(char *buf, size_t size, struct nft_set *s,
+ uint32_t cmd, uint32_t type, uint32_t flags);
int nft_set_snprintf(char *buf, size_t size, struct nft_set *s, uint32_t type, uint32_t flags);
int nft_set_fprintf(FILE *fp, struct nft_set *s, uint32_t type, uint32_t flags);
@@ -112,6 +114,8 @@ int nft_set_elem_parse(struct nft_set_elem *e, enum nft_parse_type type,
const char *data, struct nft_parse_err *err);
int nft_set_elem_parse_file(struct nft_set_elem *e, enum nft_parse_type type,
FILE *fp, struct nft_parse_err *err);
+int nft_set_elem_cmd_snprintf(char *buf, size_t size, struct nft_set_elem *s,
+ uint32_t cmd, uint32_t type, uint32_t flags);
int nft_set_elem_snprintf(char *buf, size_t size, struct nft_set_elem *s, uint32_t type, uint32_t flags);
int nft_set_elem_fprintf(FILE *fp, struct nft_set_elem *se, uint32_t type, uint32_t flags);
diff --git a/include/libnftnl/table.h b/include/libnftnl/table.h
index fac79e7..f20a684 100644
--- a/include/libnftnl/table.h
+++ b/include/libnftnl/table.h
@@ -51,6 +51,8 @@ int nft_table_parse(struct nft_table *t, enum nft_parse_type type,
int nft_table_parse_file(struct nft_table *t, enum nft_parse_type type,
FILE *fp, struct nft_parse_err *err);
int nft_table_snprintf(char *buf, size_t size, struct nft_table *t, uint32_t type, uint32_t flags);
+int nft_table_cmd_snprintf(char *buf, size_t size, struct nft_table *t,
+ uint32_t cmd, uint32_t type, uint32_t flags);
int nft_table_fprintf(FILE *fp, struct nft_table *t, uint32_t type, uint32_t flags);
#define nft_table_nlmsg_build_hdr nft_nlmsg_build_hdr
diff --git a/src/buffer.c b/src/buffer.c
index d64f944..15344ce 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -71,6 +71,17 @@ int nft_buf_open(struct nft_buf *b, int type, const char *tag)
}
}
+int nft_buf_open_array(struct nft_buf *b, int type, const char *tag)
+{
+ switch (type) {
+ case NFT_OUTPUT_JSON:
+ return nft_buf_put(b, "{\"%s\":[", tag);
+ case NFT_OUTPUT_XML:
+ default:
+ return 0;
+ }
+}
+
int nft_buf_close(struct nft_buf *b, int type, const char *tag)
{
switch (type) {
@@ -89,6 +100,16 @@ int nft_buf_close(struct nft_buf *b, int type, const char *tag)
return 0;
}
}
+int nft_buf_close_array(struct nft_buf *b, int type, const char *tag)
+{
+ switch (type) {
+ case NFT_OUTPUT_JSON:
+ return nft_buf_put(b, "]}");
+ case NFT_OUTPUT_XML:
+ default:
+ return 0;
+ }
+}
int nft_buf_u32(struct nft_buf *b, int type, uint32_t value, const char *tag)
{
diff --git a/src/chain.c b/src/chain.c
index 26ad14d..bdd6ec8 100644
--- a/src/chain.c
+++ b/src/chain.c
@@ -847,12 +847,12 @@ static int nft_chain_snprintf_default(char *buf, size_t size,
return offset;
}
-int nft_chain_snprintf(char *buf, size_t size, struct nft_chain *c,
- uint32_t type, uint32_t flags)
+int nft_chain_cmd_snprintf(char *buf, size_t size, struct nft_chain *c,
+ uint32_t cmd, uint32_t type, uint32_t flags)
{
int ret, len = size, offset = 0;
- ret = nft_event_header_snprintf(buf+offset, len, type, flags);
+ ret = nft_cmd_header_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
switch (type) {
@@ -869,13 +869,23 @@ int nft_chain_snprintf(char *buf, size_t size, struct nft_chain *c,
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- ret = nft_event_footer_snprintf(buf+offset, len, type, flags);
+ ret = nft_cmd_footer_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
return offset;
}
EXPORT_SYMBOL(nft_chain_snprintf);
+int nft_chain_snprintf(char *buf, size_t size, struct nft_chain *c,
+ uint32_t type, uint32_t flags)
+{
+ uint32_t cmd;
+
+ nft_flag2cmd(flags, &cmd);
+ return nft_chain_cmd_snprintf(buf, size, c, cmd, type, flags);
+}
+EXPORT_SYMBOL(nft_chain_snprintf);
+
static inline int nft_chain_do_snprintf(char *buf, size_t size, void *c,
uint32_t type, uint32_t flags)
{
diff --git a/src/common.c b/src/common.c
index a6f2508..39bec0d 100644
--- a/src/common.c
+++ b/src/common.c
@@ -16,10 +16,19 @@
#include <libmnl/libmnl.h>
#include <libnftnl/common.h>
#include <libnftnl/set.h>
+#include <buffer.h>
#include <errno.h>
#include "internal.h"
+const char *cmd2tag[] = {
+ [NFT_CMD_ADD] = ADD,
+ [NFT_CMD_INSERT] = INSERT,
+ [NFT_CMD_DELETE] = DELETE,
+ [NFT_CMD_REPLACE] = REPLACE,
+ [NFT_CMD_FLUSH] = FLUSH,
+};
+
struct nlmsghdr *nft_nlmsg_build_hdr(char *buf, uint16_t cmd, uint16_t family,
uint16_t type, uint32_t seq)
{
@@ -70,86 +79,80 @@ int nft_parse_perror(const char *msg, struct nft_parse_err *err)
}
EXPORT_SYMBOL(nft_parse_perror);
-int nft_event_header_snprintf(char *buf, size_t size, uint32_t type,
- uint32_t flags)
+int nft_cmd_header_snprintf(char *buf, size_t size, uint32_t cmd, uint32_t type,
+ uint32_t flags)
{
- int ret = 0;
+ NFT_BUF_INIT(b, buf, size);
- if (!(flags & NFT_OF_EVENT_ANY))
+ if (cmd == NFT_CMD_UNSPEC)
return 0;
switch (type) {
case NFT_OUTPUT_XML:
- if (flags & NFT_OF_EVENT_NEW) {
- ret = snprintf(buf, size, "<event><type>new</type>");
- } else if (flags & NFT_OF_EVENT_DEL) {
- ret = snprintf(buf, size,
- "<event><type>delete</type>");
- } else {
- ret = snprintf(buf, size,
- "<event><type>unknown</type>");
- }
+ nft_buf_open(&b, type, cmd2tag[cmd]);
break;
case NFT_OUTPUT_JSON:
- if (flags & NFT_OF_EVENT_NEW) {
- ret = snprintf(buf, size, "{event:{type:\"new\",{\"");
- } else if (flags & NFT_OF_EVENT_DEL) {
- ret = snprintf(buf, size,
- "{event:{type:\"delete\",{\"");
- } else {
- ret = snprintf(buf, size,
- "{event:{type:\"unknown\",{\"");
- }
+ nft_buf_open_array(&b, type, cmd2tag[cmd]);
break;
default:
- if (flags & NFT_OF_EVENT_NEW) {
- ret = snprintf(buf, size, "%9s", "[NEW] ");
- } else if (flags & NFT_OF_EVENT_DEL) {
- ret = snprintf(buf, size, "%9s", "[DELETE] ");
- } else {
- ret = snprintf(buf, size, "%9s", "[unknown] ");
+ switch (cmd) {
+ case NFT_CMD_ADD:
+ return snprintf(buf, size, "%9s", "[ADD] ");
+ case NFT_CMD_DELETE:
+ return snprintf(buf, size, "%9s", "[DELETE] ");
+ default:
+ return snprintf(buf, size, "%9s", "[unknown] ");
}
break;
}
- return ret;
+ return nft_buf_done(&b);
}
-static int nft_event_header_fprintf_cb(char *buf, size_t size, void *unused,
- uint32_t type, uint32_t flags)
+static int nft_cmd_header_fprintf_cb(char *buf, size_t size, void *cmd,
+ uint32_t type, uint32_t flags)
{
- return nft_event_header_snprintf(buf, size, type, flags);
+ return nft_cmd_header_snprintf(buf, size, *(uint32_t *)cmd, type,
+ flags);
}
-int nft_event_header_fprintf(FILE *fp, uint32_t type, uint32_t flags)
+int nft_cmd_header_fprintf(FILE *fp, uint32_t cmd, uint32_t type,
+ uint32_t flags)
{
- return nft_fprintf(fp, NULL, type, flags, nft_event_header_fprintf_cb);
+ return nft_fprintf(fp, &cmd, type, flags, nft_cmd_header_fprintf_cb);
}
-int nft_event_footer_snprintf(char *buf, size_t size, uint32_t type,
- uint32_t flags)
+int nft_cmd_footer_snprintf(char *buf, size_t size, uint32_t cmd, uint32_t type,
+ uint32_t flags)
{
- if (!(flags & NFT_OF_EVENT_ANY))
+ NFT_BUF_INIT(b, buf, size);
+
+ if (cmd == NFT_CMD_UNSPEC)
return 0;
switch (type) {
case NFT_OUTPUT_XML:
- return snprintf(buf, size, "</event>");
+ nft_buf_close(&b, type, cmd2tag[cmd]);
+ break;
case NFT_OUTPUT_JSON:
- return snprintf(buf, size, "}}}");
+ nft_buf_close_array(&b, type, 0);
+ break;
default:
return 0;
}
+ return nft_buf_done(&b);
}
-static int nft_event_footer_fprintf_cb(char *buf, size_t size, void *unused,
- uint32_t type, uint32_t flags)
+static int nft_cmd_footer_fprintf_cb(char *buf, size_t size, void *cmd,
+ uint32_t type, uint32_t flags)
{
- return nft_event_footer_snprintf(buf, size, type, flags);
+ return nft_cmd_footer_snprintf(buf, size, *(uint32_t *)cmd, type,
+ flags);
}
-int nft_event_footer_fprintf(FILE *fp, uint32_t type, uint32_t flags)
+int nft_cmd_footer_fprintf(FILE *fp, uint32_t cmd, uint32_t type,
+ uint32_t flags)
{
- return nft_fprintf(fp, NULL, type, flags, nft_event_footer_fprintf_cb);
+ return nft_fprintf(fp, &cmd, type, flags, nft_cmd_footer_fprintf_cb);
}
static void nft_batch_build_hdr(char *buf, uint16_t type, uint32_t seq)
diff --git a/src/gen.c b/src/gen.c
index 21d3a49..939a454 100644
--- a/src/gen.c
+++ b/src/gen.c
@@ -161,12 +161,12 @@ static int nft_gen_snprintf_default(char *buf, size_t size, struct nft_gen *gen)
return snprintf(buf, size, "ruleset generation ID %u", gen->id);
}
-int nft_gen_snprintf(char *buf, size_t size, struct nft_gen *gen,
- uint32_t type, uint32_t flags)
+int nft_gen_cmd_snprintf(char *buf, size_t size, struct nft_gen *gen,
+ uint32_t cmd, uint32_t type, uint32_t flags)
{
int ret, len = size, offset = 0;
- ret = nft_event_header_snprintf(buf + offset, len, type, flags);
+ ret = nft_cmd_header_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
switch(type) {
@@ -178,11 +178,21 @@ int nft_gen_snprintf(char *buf, size_t size, struct nft_gen *gen,
}
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- ret = nft_event_footer_snprintf(buf + offset, len, type, flags);
+ ret = nft_cmd_footer_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
return offset;
}
+EXPORT_SYMBOL(nft_gen_cmd_snprintf);
+
+int nft_gen_snprintf(char *buf, size_t size, struct nft_gen *gen,
+ uint32_t type, uint32_t flags)
+{
+ uint32_t cmd;
+
+ nft_flag2cmd(flags, &cmd);
+ return nft_gen_cmd_snprintf(buf, size, gen, cmd, type, flags);
+}
EXPORT_SYMBOL(nft_gen_snprintf);
static inline int nft_gen_do_snprintf(char *buf, size_t size, void *gen,
diff --git a/src/internal.h b/src/internal.h
index db9af11..44cfc5d 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -146,16 +146,19 @@ int nft_str2family(const char *family);
int nft_strtoi(const char *string, int base, void *number, enum nft_type type);
const char *nft_verdict2str(uint32_t verdict);
int nft_str2verdict(const char *verdict, int *verdict_num);
+int nft_flag2cmd(uint32_t flags, uint32_t *cmd);
int nft_get_value(enum nft_type type, void *val, void *out);
#include <stdio.h>
int nft_fprintf(FILE *fp, void *obj, uint32_t type, uint32_t flags, int (*snprintf_cb)(char *buf, size_t bufsiz, void *obj, uint32_t type, uint32_t flags));
-int nft_event_header_snprintf(char *buf, size_t bufsize,
- uint32_t format, uint32_t flags);
-int nft_event_header_fprintf(FILE *fp, uint32_t format, uint32_t flags);
-int nft_event_footer_snprintf(char *buf, size_t bufsize,
- uint32_t format, uint32_t flags);
-int nft_event_footer_fprintf(FILE *fp, uint32_t format, uint32_t flags);
+int nft_cmd_header_snprintf(char *buf, size_t bufsize, uint32_t cmd,
+ uint32_t format, uint32_t flags);
+int nft_cmd_header_fprintf(FILE *fp, uint32_t cmd, uint32_t format,
+ uint32_t flags);
+int nft_cmd_footer_snprintf(char *buf, size_t bufsize, uint32_t cmd,
+ uint32_t format, uint32_t flags);
+int nft_cmd_footer_fprintf(FILE *fp, uint32_t cmd, uint32_t format,
+ uint32_t flags);
struct expr_ops;
diff --git a/src/rule.c b/src/rule.c
index ac5136c..21cd9ad 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -963,15 +963,15 @@ static int nft_rule_snprintf_default(char *buf, size_t size, struct nft_rule *r,
return offset;
}
-int nft_rule_snprintf(char *buf, size_t size, struct nft_rule *r,
- uint32_t type, uint32_t flags)
+int nft_rule_cmd_snprintf(char *buf, size_t size, struct nft_rule *r,
+ uint32_t cmd, uint32_t type, uint32_t flags)
{
int ret, len = size, offset = 0;
uint32_t inner_flags = flags;
inner_flags &= ~NFT_OF_EVENT_ANY;
- ret = nft_event_header_snprintf(buf+offset, len, type, flags);
+ ret = nft_cmd_header_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
switch(type) {
@@ -993,13 +993,22 @@ int nft_rule_snprintf(char *buf, size_t size, struct nft_rule *r,
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- ret = nft_event_footer_snprintf(buf+offset, len, type, flags);
+ ret = nft_cmd_footer_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
return offset;
}
-EXPORT_SYMBOL(nft_rule_snprintf);
+EXPORT_SYMBOL(nft_rule_cmd_snprintf);
+int nft_rule_snprintf(char *buf, size_t size, struct nft_rule *r,
+ uint32_t type, uint32_t flags)
+{
+ uint32_t cmd;
+
+ nft_flag2cmd(flags, &cmd);
+ return nft_rule_cmd_snprintf(buf, size, r, cmd, type, flags);
+}
+EXPORT_SYMBOL(nft_rule_snprintf);
static inline int nft_rule_do_snprintf(char *buf, size_t size, void *r,
uint32_t type, uint32_t flags)
{
diff --git a/src/ruleset.c b/src/ruleset.c
index c29c307..3fb381d 100644
--- a/src/ruleset.c
+++ b/src/ruleset.c
@@ -784,7 +784,7 @@ nft_ruleset_snprintf_rule(char *buf, size_t size,
static int
nft_ruleset_do_snprintf(char *buf, size_t size, const struct nft_ruleset *rs,
- uint32_t type, uint32_t flags)
+ uint32_t cmd, uint32_t type, uint32_t flags)
{
int ret, len = size, offset = 0;
void *prev = NULL;
@@ -793,10 +793,10 @@ nft_ruleset_do_snprintf(char *buf, size_t size, const struct nft_ruleset *rs,
/* dont pass events flags to child calls of _snprintf() */
inner_flags &= ~NFT_OF_EVENT_ANY;
- ret = nft_event_header_snprintf(buf+offset, len, type, flags);
+ ret = snprintf(buf+offset, len, "%s", nft_ruleset_o_opentag(type));
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- ret = snprintf(buf+offset, len, "%s", nft_ruleset_o_opentag(type));
+ ret = nft_cmd_header_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
if (nft_ruleset_attr_is_set(rs, NFT_RULESET_ATTR_TABLELIST) &&
@@ -848,10 +848,10 @@ nft_ruleset_do_snprintf(char *buf, size_t size, const struct nft_ruleset *rs,
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
}
- ret = snprintf(buf+offset, len, "%s", nft_ruleset_o_closetag(type));
+ ret = nft_cmd_footer_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- ret = nft_event_footer_snprintf(buf+offset, len, type, flags);
+ ret = snprintf(buf+offset, len, "%s", nft_ruleset_o_closetag(type));
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
return offset;
@@ -860,11 +860,14 @@ nft_ruleset_do_snprintf(char *buf, size_t size, const struct nft_ruleset *rs,
int nft_ruleset_snprintf(char *buf, size_t size, const struct nft_ruleset *r,
uint32_t type, uint32_t flags)
{
+ uint32_t cmd;
+
+ nft_flag2cmd(flags, &cmd);
switch (type) {
case NFT_OUTPUT_DEFAULT:
case NFT_OUTPUT_XML:
case NFT_OUTPUT_JSON:
- return nft_ruleset_do_snprintf(buf, size, r, type, flags);
+ return nft_ruleset_do_snprintf(buf, size, r, cmd, type, flags);
default:
errno = EOPNOTSUPP;
return -1;
@@ -872,6 +875,22 @@ int nft_ruleset_snprintf(char *buf, size_t size, const struct nft_ruleset *r,
}
EXPORT_SYMBOL(nft_ruleset_snprintf);
+int nft_ruleset_cmd_snprintf(char *buf, size_t size,
+ const struct nft_ruleset *r, uint32_t cmd,
+ uint32_t type, uint32_t flags)
+{
+ switch (type) {
+ case NFT_OUTPUT_DEFAULT:
+ case NFT_OUTPUT_XML:
+ case NFT_OUTPUT_JSON:
+ return nft_ruleset_do_snprintf(buf, size, r, cmd, type, flags);
+ default:
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+}
+EXPORT_SYMBOL(nft_ruleset_cmd_snprintf);
+
static int nft_ruleset_fprintf_tables(FILE *fp, const struct nft_ruleset *rs,
uint32_t type, uint32_t flags)
{
@@ -1017,8 +1036,8 @@ err:
return -1; \
len += ret;
-int nft_ruleset_fprintf(FILE *fp, const struct nft_ruleset *rs, uint32_t type,
- uint32_t flags)
+int nft_ruleset_cmd_fprintf(FILE *fp, const struct nft_ruleset *rs,
+ uint32_t cmd, uint32_t type, uint32_t flags)
{
int len = 0, ret = 0;
void *prev = NULL;
@@ -1027,10 +1046,10 @@ int nft_ruleset_fprintf(FILE *fp, const struct nft_ruleset *rs, uint32_t type,
/* dont pass events flags to child calls of _snprintf() */
inner_flags &= ~NFT_OF_EVENT_ANY;
- ret = nft_event_header_fprintf(fp, type, flags);
+ ret = fprintf(fp, "%s", nft_ruleset_o_opentag(type));
NFT_FPRINTF_RETURN_OR_FIXLEN(ret, len);
- ret = fprintf(fp, "%s", nft_ruleset_o_opentag(type));
+ ret = nft_cmd_header_fprintf(fp, cmd, type, flags);
NFT_FPRINTF_RETURN_OR_FIXLEN(ret, len);
if ((nft_ruleset_attr_is_set(rs, NFT_RULESET_ATTR_TABLELIST)) &&
@@ -1075,12 +1094,22 @@ int nft_ruleset_fprintf(FILE *fp, const struct nft_ruleset *rs, uint32_t type,
NFT_FPRINTF_RETURN_OR_FIXLEN(ret, len);
}
- ret = fprintf(fp, "%s", nft_ruleset_o_closetag(type));
+ ret = nft_cmd_footer_fprintf(fp, cmd, type, flags);
NFT_FPRINTF_RETURN_OR_FIXLEN(ret, len);
- ret = nft_event_footer_fprintf(fp, type, flags);
+ ret = fprintf(fp, "%s", nft_ruleset_o_closetag(type));
NFT_FPRINTF_RETURN_OR_FIXLEN(ret, len);
return len;
}
+EXPORT_SYMBOL(nft_ruleset_cmd_fprintf);
+
+int nft_ruleset_fprintf(FILE *fp, const struct nft_ruleset *rs, uint32_t type,
+ uint32_t flags)
+{
+ uint32_t cmd;
+
+ nft_flag2cmd(flags, &cmd);
+ return nft_ruleset_cmd_fprintf(fp, rs, cmd, type, flags);
+}
EXPORT_SYMBOL(nft_ruleset_fprintf);
diff --git a/src/set.c b/src/set.c
index 4fd786a..ba42b91 100644
--- a/src/set.c
+++ b/src/set.c
@@ -889,8 +889,8 @@ static int nft_set_snprintf_xml(char *buf, size_t size, struct nft_set *s,
return offset;
}
-int nft_set_snprintf(char *buf, size_t size, struct nft_set *s,
- uint32_t type, uint32_t flags)
+int nft_set_cmd_snprintf(char *buf, size_t size, struct nft_set *s,
+ uint32_t cmd, uint32_t type, uint32_t flags)
{
int ret, len = size, offset = 0;
uint32_t inner_flags = flags;
@@ -898,7 +898,7 @@ int nft_set_snprintf(char *buf, size_t size, struct nft_set *s,
/* prevent set_elems to print as events */
inner_flags &= ~NFT_OF_EVENT_ANY;
- ret = nft_event_header_snprintf(buf+offset, len, type, flags);
+ ret = nft_cmd_header_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
switch(type) {
@@ -919,11 +919,21 @@ int nft_set_snprintf(char *buf, size_t size, struct nft_set *s,
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- ret = nft_event_footer_snprintf(buf+offset, len, type, flags);
+ ret = nft_cmd_footer_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
return offset;
}
+EXPORT_SYMBOL(nft_set_cmd_snprintf);
+
+int nft_set_snprintf(char *buf, size_t size, struct nft_set *s,
+ uint32_t type, uint32_t flags)
+{
+ uint32_t cmd;
+
+ nft_flag2cmd(flags, &cmd);
+ return nft_set_cmd_snprintf(buf, size, s, cmd, type, flags);
+}
EXPORT_SYMBOL(nft_set_snprintf);
static inline int nft_set_do_snprintf(char *buf, size_t size, void *s,
diff --git a/src/set_elem.c b/src/set_elem.c
index 4f52b1a..078feeb 100644
--- a/src/set_elem.c
+++ b/src/set_elem.c
@@ -614,12 +614,12 @@ static int nft_set_elem_snprintf_xml(char *buf, size_t size,
return offset;
}
-int nft_set_elem_snprintf(char *buf, size_t size, struct nft_set_elem *e,
- uint32_t type, uint32_t flags)
+int nft_set_elem_cmd_snprintf(char *buf, size_t size, struct nft_set_elem *e,
+ uint32_t cmd, uint32_t type, uint32_t flags)
{
int ret, len = size, offset = 0;
- ret = nft_event_header_snprintf(buf+offset, len, type, flags);
+ ret = nft_cmd_header_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
switch(type) {
@@ -638,11 +638,21 @@ int nft_set_elem_snprintf(char *buf, size_t size, struct nft_set_elem *e,
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- ret = nft_event_footer_snprintf(buf+offset, len, type, flags);
+ ret = nft_cmd_footer_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
return offset;
}
+EXPORT_SYMBOL(nft_set_elem_cmd_snprintf);
+
+int nft_set_elem_snprintf(char *buf, size_t size, struct nft_set_elem *e,
+ uint32_t type, uint32_t flags)
+{
+ uint32_t cmd;
+
+ nft_flag2cmd(flags, &cmd);
+ return nft_set_elem_cmd_snprintf(buf, size, e, cmd, type, flags);
+}
EXPORT_SYMBOL(nft_set_elem_snprintf);
static inline int nft_set_elem_do_snprintf(char *buf, size_t size, void *e,
diff --git a/src/table.c b/src/table.c
index e947394..8868a5a 100644
--- a/src/table.c
+++ b/src/table.c
@@ -419,12 +419,12 @@ static int nft_table_snprintf_default(char *buf, size_t size, struct nft_table *
t->table_flags, t->use);
}
-int nft_table_snprintf(char *buf, size_t size, struct nft_table *t,
- uint32_t type, uint32_t flags)
+int nft_table_cmd_snprintf(char *buf, size_t size, struct nft_table *t,
+ uint32_t cmd, uint32_t type, uint32_t flags)
{
int ret, len = size, offset = 0;
- ret = nft_event_header_snprintf(buf+offset, len, type, flags);
+ ret = nft_cmd_header_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
switch (type) {
@@ -440,11 +440,21 @@ int nft_table_snprintf(char *buf, size_t size, struct nft_table *t,
}
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- ret = nft_event_footer_snprintf(buf+offset, len, type, flags);
+ ret = nft_cmd_footer_snprintf(buf + offset, len, cmd, type, flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
return offset;
}
+EXPORT_SYMBOL(nft_table_cmd_snprintf);
+
+int nft_table_snprintf(char *buf, size_t size, struct nft_table *t,
+ uint32_t type, uint32_t flags)
+{
+ uint32_t cmd;
+
+ nft_flag2cmd(flags, &cmd);
+ return nft_table_cmd_snprintf(buf, size, t, cmd, type, flags);
+}
EXPORT_SYMBOL(nft_table_snprintf);
static inline int nft_table_do_snprintf(char *buf, size_t size, void *t,
diff --git a/src/utils.c b/src/utils.c
index 9013b68..5890115 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -177,6 +177,22 @@ int nft_str2verdict(const char *verdict, int *verdict_num)
return -1;
}
+int nft_flag2cmd(uint32_t flags, uint32_t *cmd)
+{
+ if (!(flags & NFT_OF_EVENT_ANY)) {
+ *cmd = NFT_CMD_UNSPEC;
+ return 0;
+ } else if (flags & NFT_OF_EVENT_NEW) {
+ *cmd = NFT_CMD_ADD;
+ return 0;
+ } else if (flags & NFT_OF_EVENT_DEL) {
+ *cmd = NFT_CMD_DELETE;
+ return 0;
+ }
+
+ return -1;
+}
+
int nft_fprintf(FILE *fp, void *obj, uint32_t type, uint32_t flags,
int (*snprintf_cb)(char *buf, size_t bufsiz, void *obj,
uint32_t type, uint32_t flags))
--
1.7.10.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [libnftnl PATCH 2/5] src: add support to import json/xml with the new syntax
2015-01-23 14:49 [libnftnl PATCH] set: refactor code in json parse function Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 1/5] src: add command tag in json/xml export support Alvaro Neira Ayuso
@ 2015-01-23 14:49 ` Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 3/5] ruleset: consolidate code in json/xml import support Alvaro Neira Ayuso
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Alvaro Neira Ayuso @ 2015-01-23 14:49 UTC (permalink / raw)
To: netfilter-devel
This patch adds support to parse the new syntax in xml/json. This patch adds two
new different functions nft_ruleset_parse_*_cb. With these functions, we can
pass a callback functions like parameter and we can call with each element.
These functions use a new structure like parameter called nft_parse_ctx. This
structure contains the command associated to the ruleset, the type of the
element (table, chain, rule, set or set element) and the object.
Also, with this changes, we have support to parse another new ruleset element:
{"nftables":[{"add":[{"element":{"name":"blackhole","table":"filter"
"family":"ip","key_type":7,"key_len":4,"set_elem":[{"key":{
"reg":{"type":"value","len":4,"data0":"0x0403a8c0"}}}]}}]}]}
With this new element, we can make incremental changes with set elements like
add or delete elements inside a set.
In short, these functions offer us the possibility to make incremental changes
with the ruleset elements.
Another interesting goal is extend (in the future) the ruleset
structure with information like the command associated to each element.
Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
---
include/libnftnl/ruleset.h | 36 ++
src/internal.h | 3 +
src/libnftnl.map | 6 +
src/ruleset.c | 809 +++++++++++++++++++++++++++++---------------
src/set.c | 12 +
src/utils.c | 23 ++
6 files changed, 613 insertions(+), 276 deletions(-)
diff --git a/include/libnftnl/ruleset.h b/include/libnftnl/ruleset.h
index cec6cd6..b240f83 100644
--- a/include/libnftnl/ruleset.h
+++ b/include/libnftnl/ruleset.h
@@ -25,11 +25,47 @@ enum {
NFT_RULESET_ATTR_RULELIST,
};
+enum nft_ruleset_type {
+ NFT_RULESET_TYPE_UNSPEC = 0,
+ NFT_RULESET_TYPE_RULESET,
+ NFT_RULESET_TYPE_TABLE,
+ NFT_RULESET_TYPE_CHAIN,
+ NFT_RULESET_TYPE_RULE,
+ NFT_RULESET_TYPE_SET,
+ NFT_RULESET_TYPE_SET_ELEMS,
+};
+
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);
void *nft_ruleset_attr_get(const struct nft_ruleset *r, uint16_t attr);
+struct nft_parse_ctx;
+
+struct nft_parse_ctx *nft_ruleset_ctx_alloc(void);
+void nft_ruleset_ctx_free(struct nft_parse_ctx *ctx);
+
+enum {
+ NFT_RULESET_CTX_CMD = 0,
+ NFT_RULESET_CTX_TYPE,
+ NFT_RULESET_CTX_TABLE,
+ NFT_RULESET_CTX_CHAIN,
+ NFT_RULESET_CTX_RULE,
+ NFT_RULESET_CTX_SET,
+};
+
+bool nft_ruleset_ctx_attr_is_set(struct nft_parse_ctx *ctx, uint16_t attr);
+void nft_ruleset_ctx_attr_unset(struct nft_parse_ctx *ctx, uint16_t attr);
+void nft_ruleset_ctx_attr_set(struct nft_parse_ctx *ctx, uint16_t attr,
+ void *data);
+void *nft_ruleset_ctx_attr_get(struct nft_parse_ctx *ctx, uint16_t attr);
+
+int nft_ruleset_parse_file_cb(enum nft_parse_type type, FILE *fp,
+ struct nft_parse_err *err, void *data,
+ int (*cb)(struct nft_parse_ctx *ctx));
+int nft_ruleset_parse_buffer_cb(enum nft_parse_type type, const char *buffer,
+ struct nft_parse_err *err, void *data,
+ int (*cb)(struct nft_parse_ctx *ctx));
int nft_ruleset_parse(struct nft_ruleset *rs, enum nft_parse_type type,
const char *data, struct nft_parse_err *err);
int nft_ruleset_parse_file(struct nft_ruleset *rs, enum nft_parse_type type,
diff --git a/src/internal.h b/src/internal.h
index 44cfc5d..0c7f491 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -139,6 +139,8 @@ 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,
struct nft_parse_err *err);
+int nft_jansson_parse_elem(struct nft_set *s, json_t *tree,
+ struct nft_parse_err *err);
#endif
const char *nft_family2str(uint32_t family);
@@ -148,6 +150,7 @@ const char *nft_verdict2str(uint32_t verdict);
int nft_str2verdict(const char *verdict, int *verdict_num);
int nft_flag2cmd(uint32_t flags, uint32_t *cmd);
int nft_get_value(enum nft_type type, void *val, void *out);
+int nft_str2cmd(const char *cmd, uint32_t *cmd_num);
#include <stdio.h>
int nft_fprintf(FILE *fp, void *obj, uint32_t type, uint32_t flags, int (*snprintf_cb)(char *buf, size_t bufsiz, void *obj, uint32_t type, uint32_t flags));
diff --git a/src/libnftnl.map b/src/libnftnl.map
index be7b998..f8abcdb 100644
--- a/src/libnftnl.map
+++ b/src/libnftnl.map
@@ -182,8 +182,14 @@ global:
nft_ruleset_attr_unset;
nft_ruleset_attr_set;
nft_ruleset_attr_get;
+ nft_ruleset_ctx_attr_is_set;
+ nft_ruleset_ctx_attr_unset;
+ nft_ruleset_ctx_attr_set;
+ nft_ruleset_ctx_attr_get;
nft_ruleset_parse;
nft_ruleset_parse_file;
+ nft_ruleset_parse_file_cb;
+ nft_ruleset_parse_buffer_cb;
nft_ruleset_snprintf;
nft_ruleset_fprintf;
diff --git a/src/ruleset.c b/src/ruleset.c
index 3fb381d..a3e4b20 100644
--- a/src/ruleset.c
+++ b/src/ruleset.c
@@ -32,6 +32,30 @@ struct nft_ruleset {
uint16_t flags;
};
+struct nft_parse_ctx {
+ uint32_t cmd;
+ uint32_t type;
+ union {
+ struct nft_table *table;
+ struct nft_chain *chain;
+ struct nft_rule *rule;
+ struct nft_set *set;
+ struct nft_set_elem *set_elem;
+ };
+ union {
+ json_t *json;
+ mxml_node_t *xml;
+ };
+
+ uint32_t set_id;
+ struct nft_set_list *set_list;
+
+ void *data;
+
+ int (*cb)(struct nft_parse_ctx *ctx);
+ uint16_t flags;
+};
+
struct nft_ruleset *nft_ruleset_alloc(void)
{
return calloc(1, sizeof(struct nft_ruleset));
@@ -131,231 +155,347 @@ void *nft_ruleset_attr_get(const struct nft_ruleset *r, uint16_t attr)
}
EXPORT_SYMBOL(nft_ruleset_attr_get);
-#ifdef JSON_PARSING
-static int nft_ruleset_json_parse_tables(struct nft_ruleset *rs, json_t *array,
- struct nft_parse_err *err)
+struct nft_parse_ctx *nft_ruleset_ctx_alloc(void)
{
- int i, len;
- json_t *node;
- struct nft_table *table;
- struct nft_table_list *list = nft_table_list_alloc();
+ return calloc(1, sizeof(struct nft_parse_ctx));
+}
+EXPORT_SYMBOL(nft_ruleset_ctx_alloc);
- if (list == NULL) {
- errno = ENOMEM;
- return -1;
+void nft_ruleset_ctx_free(struct nft_parse_ctx *ctx)
+{
+ xfree(ctx);
+}
+EXPORT_SYMBOL(nft_ruleset_ctx_free);
+
+bool nft_ruleset_ctx_attr_is_set(struct nft_parse_ctx *ctx, uint16_t attr)
+{
+ return ctx->flags & (1 << attr);
+}
+EXPORT_SYMBOL(nft_ruleset_ctx_attr_is_set);
+
+void nft_ruleset_ctx_attr_unset(struct nft_parse_ctx *ctx, uint16_t attr)
+{
+ if (!(ctx->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFT_RULESET_CTX_CMD:
+ case NFT_RULESET_CTX_TYPE:
+ break;
+ case NFT_RULESET_CTX_TABLE:
+ ctx->table = NULL;
+ break;
+ case NFT_RULESET_CTX_CHAIN:
+ ctx->chain = NULL;
+ break;
+ case NFT_RULESET_CTX_RULE:
+ ctx->rule = NULL;
+ break;
+ case NFT_RULESET_CTX_SET:
+ ctx->set = NULL;
+ break;
}
+ ctx->flags &= ~(1 << attr);
+}
+EXPORT_SYMBOL(nft_ruleset_ctx_attr_unset);
- len = json_array_size(array);
- for (i = 0; i < len; i++) {
- node = json_array_get(array, i);
- if (node == NULL) {
- errno = EINVAL;
- goto err;
- }
+void nft_ruleset_ctx_attr_set(struct nft_parse_ctx *ctx, uint16_t attr,
+ void *data)
+{
+ switch (attr) {
+ case NFT_RULESET_CTX_CMD:
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_CMD);
+ ctx->cmd = *((uint32_t *)data);
+ break;
+ case NFT_RULESET_CTX_TYPE:
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_TYPE);
+ ctx->type = *((uint32_t *)data);
+ break;
+ case NFT_RULESET_CTX_TABLE:
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_TABLE);
+ ctx->table = data;
+ break;
+ case NFT_RULESET_CTX_CHAIN:
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_CHAIN);
+ ctx->chain = data;
+ break;
+ case NFT_RULESET_CTX_RULE:
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_RULE);
+ ctx->rule = data;
+ break;
+ case NFT_RULESET_CTX_SET:
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_SET);
+ ctx->set = data;
+ break;
+ }
+ ctx->flags |= (1 << attr);
+}
+EXPORT_SYMBOL(nft_ruleset_ctx_attr_set);
- if (!(nft_jansson_node_exist(node, "table")))
- continue;
+void *nft_ruleset_ctx_attr_get(struct nft_parse_ctx *ctx, uint16_t attr)
+{
+ if (!(ctx->flags & (1 << attr)))
+ return NULL;
- table = nft_table_alloc();
- if (table == NULL) {
- errno = ENOMEM;
- goto err;
- }
+ switch (attr) {
+ case NFT_RULESET_CTX_CMD:
+ return &ctx->cmd;
+ case NFT_RULESET_CTX_TYPE:
+ return &ctx->type;
+ case NFT_RULESET_CTX_TABLE:
+ return ctx->table;
+ case NFT_RULESET_CTX_CHAIN:
+ return ctx->chain;
+ case NFT_RULESET_CTX_RULE:
+ return ctx->rule;
+ case NFT_RULESET_CTX_SET:
+ return ctx->set;
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(nft_ruleset_ctx_attr_get);
- if (nft_jansson_parse_table(table, node, err) < 0) {
- nft_table_free(table);
- goto err;
- }
+#ifdef JSON_PARSING
+static int nft_ruleset_json_parse_tables(struct nft_parse_ctx *ctx,
+ struct nft_parse_err *err)
+{
+ struct nft_table *table;
+ uint32_t type = NFT_RULESET_TYPE_TABLE;
+
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
- nft_table_list_add_tail(table, list);
+ table = nft_table_alloc();
+ if (table == NULL) {
+ errno = ENOMEM;
+ return -1;
}
- if (!nft_table_list_is_empty(list))
- nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_TABLELIST, list);
- else
- nft_table_list_free(list);
+ if (nft_jansson_parse_table(table, ctx->json, err) < 0)
+ goto err;
+
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TABLE, table);
+ if (ctx->cb(ctx) < 0)
+ goto err;
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_TABLE);
return 0;
err:
- nft_table_list_free(list);
+ nft_table_free(table);
return -1;
}
-static int nft_ruleset_json_parse_chains(struct nft_ruleset *rs, json_t *array,
+static int nft_ruleset_json_parse_chains(struct nft_parse_ctx *ctx,
struct nft_parse_err *err)
{
- int i, len;
- json_t *node;
struct nft_chain *chain;
- struct nft_chain_list *list = nft_chain_list_alloc();
+ uint32_t type = NFT_RULESET_TYPE_CHAIN;
- if (list == NULL) {
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
+
+ chain = nft_chain_alloc();
+ if (chain == NULL) {
errno = ENOMEM;
return -1;
}
- len = json_array_size(array);
- for (i = 0; i < len; i++) {
- node = json_array_get(array, i);
- if (node == NULL) {
- errno = EINVAL;
- goto err;
- }
-
- if (!(nft_jansson_node_exist(node, "chain")))
- continue;
+ if (nft_jansson_parse_chain(chain, ctx->json, err) < 0)
+ goto err;
- chain = nft_chain_alloc();
- if (chain == NULL) {
- errno = ENOMEM;
- goto err;
- }
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_CHAIN, chain);
+ if (ctx->cb(ctx) < 0)
+ goto err;
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_CHAIN);
- if (nft_jansson_parse_chain(chain, node, err) < 0) {
- nft_chain_free(chain);
- goto err;
- }
+ return 0;
+err:
+ nft_chain_free(chain);
+ return -1;
+}
- nft_chain_list_add_tail(chain, list);
- }
+static int nft_ruleset_json_parse_set(struct nft_parse_ctx *ctx,
+ struct nft_set *set, int type,
+ struct nft_parse_err *err)
+{
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
- if (!nft_chain_list_is_empty(list))
- nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_CHAINLIST, list);
- else
- nft_chain_list_free(list);
+ nft_set_attr_set_u32(set, NFT_SET_ATTR_ID, ctx->set_id++);
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_SET, set);
+ nft_set_list_add_tail(set, ctx->set_list);
+ if (ctx->cb(ctx) < 0)
+ goto err;
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_SET);
return 0;
err:
- nft_chain_list_free(list);
+ nft_set_free(set);
return -1;
}
-static int nft_ruleset_json_parse_sets(struct nft_ruleset *rs, json_t *array,
- struct nft_parse_err *err)
+static int nft_ruleset_json_parse_set_elems(struct nft_parse_ctx *ctx,
+ struct nft_parse_err *err)
{
- int i, len;
- uint32_t set_id = 0;
- json_t *node;
struct nft_set *set;
- struct nft_set_list *list = nft_set_list_alloc();
- if (list == NULL) {
+ set = nft_set_alloc();
+ if (set == NULL) {
errno = ENOMEM;
return -1;
}
- len = json_array_size(array);
- for (i = 0; i < len; i++) {
- node = json_array_get(array, i);
- if (node == NULL) {
- errno = EINVAL;
- goto err;
- }
- if (!(nft_jansson_node_exist(node, "set")))
- continue;
+ if (nft_jansson_parse_elem(set, ctx->json, err) < 0)
+ goto err;
- set = nft_set_alloc();
- if (set == NULL) {
- errno = ENOMEM;
- goto err;
- }
+ return nft_ruleset_json_parse_set(ctx, set, NFT_RULESET_TYPE_SET_ELEMS,
+ err);
+err:
+ nft_set_free(set);
+ return -1;
+}
- if (nft_jansson_parse_set(set, node, err) < 0) {
- nft_set_free(set);
- goto err;
- }
+static int nft_ruleset_json_parse_sets(struct nft_parse_ctx *ctx,
+ struct nft_parse_err *err)
+{
+ struct nft_set *set;
- nft_set_attr_set_u32(set, NFT_SET_ATTR_ID, set_id++);
- nft_set_list_add_tail(set, list);
+ set = nft_set_alloc();
+ if (set == NULL) {
+ errno = ENOMEM;
+ return -1;
}
- if (!nft_set_list_is_empty(list))
- nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_SETLIST, list);
- else
- nft_set_list_free(list);
+ if (nft_jansson_parse_set(set, ctx->json, err) < 0)
+ goto err;
- return 0;
+ return nft_ruleset_json_parse_set(ctx, set, NFT_RULESET_TYPE_SET, err);
err:
- nft_set_list_free(list);
+ nft_set_free(set);
return -1;
}
-static int nft_ruleset_json_parse_rules(struct nft_ruleset *rs, json_t *array,
+static int nft_ruleset_json_parse_rules(struct nft_parse_ctx *ctx,
struct nft_parse_err *err)
{
- int i, len;
- json_t *node;
- struct nft_rule *rule = NULL;
- struct nft_rule_list *list = nft_rule_list_alloc();
+ struct nft_rule *rule;
+ uint32_t type = NFT_RULESET_TYPE_RULE;
+
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
- if (list == NULL) {
+ rule = nft_rule_alloc();
+ if (rule == NULL) {
errno = ENOMEM;
return -1;
}
+ if (nft_jansson_parse_rule(rule, ctx->json, err, ctx->set_list) < 0)
+ goto err;
+
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_RULE, rule);
+ if (ctx->cb(ctx) < 0)
+ goto err;
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_RULE);
+
+ return 0;
+err:
+ nft_rule_free(rule);
+ return -1;
+}
+
+static int nft_ruleset_json_parse_ruleset(struct nft_parse_ctx *ctx,
+ struct nft_parse_err *err)
+{
+ ctx->set_list = nft_set_list_alloc();
+ json_t *node, *array = ctx->json;
+ int len, i;
+ uint32_t type;
+
+ if (ctx->set_list == NULL)
+ return -1;
+
len = json_array_size(array);
for (i = 0; i < len; i++) {
node = json_array_get(array, i);
if (node == NULL) {
errno = EINVAL;
- goto err;
+ return -1;
}
- if (!(nft_jansson_node_exist(node, "rule")))
- continue;
-
- rule = nft_rule_alloc();
- if (rule == NULL) {
- errno = ENOMEM;
- goto err;
- }
-
- if (nft_jansson_parse_rule(rule, node, err, rs->set_list) < 0) {
- nft_rule_free(rule);
- goto err;
+ ctx->json = node;
+ if (nft_jansson_node_exist(node, "table")) {
+ if (nft_ruleset_json_parse_tables(ctx, err) < 0)
+ return -1;
+ } else if (nft_jansson_node_exist(node, "chain")) {
+ if (nft_ruleset_json_parse_chains(ctx, err) < 0)
+ return -1;
+ } else if (nft_jansson_node_exist(node, "set")) {
+ if (nft_ruleset_json_parse_sets(ctx, err) < 0)
+ return -1;
+ } else if (nft_jansson_node_exist(node, "rule")) {
+ if (nft_ruleset_json_parse_rules(ctx, err) < 0)
+ return -1;
+ } else if (nft_jansson_node_exist(node, "elements")) {
+ if (nft_ruleset_json_parse_set_elems(ctx, err) < 0)
+ return -1;
+ } else {
+ return -1;
}
-
- nft_rule_list_add_tail(rule, list);
}
-
- if (!nft_rule_list_is_empty(list))
- nft_ruleset_attr_set(rs, NFT_RULESET_ATTR_RULELIST, list);
- else
- nft_rule_list_free(list);
+ if (len == 0 && ctx->cmd == NFT_CMD_FLUSH) {
+ type = NFT_RULESET_TYPE_RULESET;
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
+ if (ctx->cb(ctx) < 0)
+ return -1;
+ }
return 0;
-err:
- nft_rule_list_free(list);
- return -1;
}
-static int nft_ruleset_json_parse_ruleset(struct nft_ruleset *rs, json_t *array,
- struct nft_parse_err *err)
+static int nft_ruleset_json_parse_cmd(const char *cmd,
+ struct nft_parse_err *err,
+ struct nft_parse_ctx *ctx)
{
- if (nft_ruleset_json_parse_tables(rs, array, err) != 0)
- return -1;
+ uint32_t cmdnum;
+ int ret;
+ json_t *nodecmd;
- if (nft_ruleset_json_parse_chains(rs, array, err) != 0)
+ ret = nft_str2cmd(cmd, &cmdnum);
+ if (ret < 0)
return -1;
- if (nft_ruleset_json_parse_sets(rs, array, err) != 0)
- return -1;
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_CMD, &cmdnum);
- if (nft_ruleset_json_parse_rules(rs, array, err) != 0)
- return -1;
+ nodecmd = json_object_get(ctx->json, cmd);
+ if (nodecmd == NULL)
+ return 0;
+
+ ctx->json = nodecmd;
+ if (nft_ruleset_json_parse_ruleset(ctx, err) != 0)
+ goto err;
return 0;
+err:
+ return -1;
}
#endif
-static int nft_ruleset_json_parse(struct nft_ruleset *rs, const void *json,
- struct nft_parse_err *err, enum nft_parse_input input)
+static int nft_ruleset_json_parse(const void *json,
+ struct nft_parse_err *err,
+ enum nft_parse_input input, void *arg,
+ int (*cb)(struct nft_parse_ctx *ctx))
{
#ifdef JSON_PARSING
- json_t *root, *array;
+ json_t *root, *array, *node;
json_error_t error;
+ int i, len;
+ const char *key;
+ struct nft_parse_ctx *ctx = nft_ruleset_ctx_alloc();
+ if (ctx == NULL)
+ return -1;
+
+ ctx->cb = cb;
+ ctx->data = arg;
root = nft_jansson_create_root(json, &error, err, input);
if (root == NULL)
return -1;
@@ -366,13 +506,28 @@ static int nft_ruleset_json_parse(struct nft_ruleset *rs, const void *json,
goto err;
}
- if (nft_ruleset_json_parse_ruleset(rs, array, err) != 0)
- goto err;
+ len = json_array_size(array);
+ for (i = 0; i < len; i++) {
+ node = json_array_get(array, i);
+ if (node == NULL) {
+ errno = EINVAL;
+ goto err;
+ }
+ ctx->json = node;
+ key = json_object_iter_key(json_object_iter(node));
+ if (key == NULL)
+ return -1;
+
+ if (nft_ruleset_json_parse_cmd(key, err, ctx) < 0)
+ goto err;
+ }
nft_jansson_free_root(root);
+ nft_ruleset_ctx_free(ctx);
return 0;
err:
nft_jansson_free_root(root);
+ nft_ruleset_ctx_free(ctx);
return -1;
#else
errno = EOPNOTSUPP;
@@ -382,207 +537,237 @@ err:
#ifdef XML_PARSING
static int
-nft_ruleset_xml_parse_tables(struct nft_ruleset *rs, mxml_node_t *tree,
+nft_ruleset_xml_parse_tables(struct nft_parse_ctx *ctx,
struct nft_parse_err *err)
{
- mxml_node_t *node;
struct nft_table *table;
- struct nft_table_list *table_list = nft_table_list_alloc();
- if (table_list == NULL) {
- errno = ENOMEM;
- return -1;
- }
+ uint32_t type = NFT_RULESET_TYPE_TABLE;
- for (node = mxmlFindElement(tree, tree, "table", NULL, NULL,
- MXML_DESCEND_FIRST);
- node != NULL;
- node = mxmlFindElement(node, tree, "table", NULL, NULL,
- MXML_NO_DESCEND)) {
- table = nft_table_alloc();
- if (table == NULL)
- goto err_free;
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
- if (nft_mxml_table_parse(node, table, err) != 0) {
- nft_table_free(table);
- goto err_free;
- }
+ table = nft_table_alloc();
+ if (table == NULL)
+ return -1;
- nft_table_list_add_tail(table, table_list);
- }
+ if (nft_mxml_table_parse(ctx->xml, table, err) != 0)
+ 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);
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TABLE, table);
+ if (ctx->cb(ctx) < 0)
+ goto err;
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_TABLE);
return 0;
-err_free:
- nft_table_list_free(table_list);
+err:
+ nft_table_free(table);
return -1;
}
static int
-nft_ruleset_xml_parse_chains(struct nft_ruleset *rs, mxml_node_t *tree,
+nft_ruleset_xml_parse_chains(struct nft_parse_ctx *ctx,
struct nft_parse_err *err)
{
- mxml_node_t *node;
struct nft_chain *chain;
- struct nft_chain_list *chain_list = nft_chain_list_alloc();
- if (chain_list == NULL) {
- errno = ENOMEM;
- return -1;
- }
+ uint32_t type = NFT_RULESET_TYPE_CHAIN;
- for (node = mxmlFindElement(tree, tree, "chain", NULL, NULL,
- MXML_DESCEND_FIRST);
- node != NULL;
- node = mxmlFindElement(node, tree, "chain", NULL, NULL,
- MXML_NO_DESCEND)) {
- chain = nft_chain_alloc();
- if (chain == NULL)
- goto err_free;
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
- if (nft_mxml_chain_parse(node, chain, err) != 0) {
- nft_chain_free(chain);
- goto err_free;
- }
+ chain = nft_chain_alloc();
+ if (chain == NULL)
+ return -1;
- nft_chain_list_add_tail(chain, chain_list);
- }
+ if (nft_mxml_chain_parse(ctx->xml, chain, err) != 0)
+ goto err;
- 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);
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_CHAIN, chain);
+ if (ctx->cb(ctx) < 0)
+ goto err;
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_CHAIN);
return 0;
-err_free:
- nft_chain_list_free(chain_list);
+err:
+ nft_chain_free(chain);
return -1;
}
static int
-nft_ruleset_xml_parse_sets(struct nft_ruleset *rs, mxml_node_t *tree,
- struct nft_parse_err *err)
+nft_ruleset_xml_parse_set(struct nft_parse_ctx *ctx, int type,
+ struct nft_parse_err *err)
{
- uint32_t set_id = 0;
- mxml_node_t *node;
struct nft_set *set;
- 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)) {
- set = nft_set_alloc();
- if (set == NULL)
- goto err_free;
-
- if (nft_mxml_set_parse(node, set, err) != 0) {
- nft_set_free(set);
- goto err_free;
- }
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
+ set = nft_set_alloc();
+ if (set == NULL)
+ return -1;
- nft_set_attr_set_u32(set, NFT_SET_ATTR_ID, set_id++);
- nft_set_list_add_tail(set, set_list);
- }
+ if (nft_mxml_set_parse(ctx->xml, set, err) != 0)
+ goto err;
- 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);
+ nft_set_attr_set_u32(set, NFT_SET_ATTR_ID, ctx->set_id++);
+ nft_set_list_add_tail(set, ctx->set_list);
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_SET, set);
+ if (ctx->cb(ctx) < 0)
+ goto err;
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_SET);
return 0;
-err_free:
- nft_set_list_free(set_list);
+err:
+ nft_set_free(set);
return -1;
}
static int
-nft_ruleset_xml_parse_rules(struct nft_ruleset *rs, mxml_node_t *tree,
- struct nft_parse_err *err,
- struct nft_set_list *set_list)
+nft_ruleset_xml_parse_set_elems(struct nft_parse_ctx *ctx,
+ struct nft_parse_err *err)
+{
+ return nft_ruleset_xml_parse_set(ctx, NFT_RULESET_TYPE_SET, err);
+}
+
+static int
+nft_ruleset_xml_parse_sets(struct nft_parse_ctx *ctx,
+ struct nft_parse_err *err)
+{
+ return nft_ruleset_xml_parse_set(ctx, NFT_RULESET_TYPE_SET, err);
+}
+
+static int
+nft_ruleset_xml_parse_rules(struct nft_parse_ctx *ctx,
+ struct nft_parse_err *err)
{
- mxml_node_t *node;
struct nft_rule *rule;
- struct nft_rule_list *rule_list = nft_rule_list_alloc();
- if (rule_list == NULL) {
+ uint32_t type = NFT_RULESET_TYPE_RULE;
+
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
+
+ rule = nft_rule_alloc();
+ if (rule == NULL)
+ return -1;
+
+ if (nft_mxml_rule_parse(ctx->xml, rule, err, ctx->set_list) != 0)
+ goto err;
+
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_RULE, rule);
+ if (ctx->cb(ctx) < 0)
+ goto err;
+ nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_RULE);
+
+ return 0;
+err:
+ nft_rule_free(rule);
+ return -1;
+}
+
+static int nft_ruleset_xml_parse_ruleset(struct nft_parse_ctx *ctx,
+ struct nft_parse_err *err)
+{
+ ctx->set_list = nft_set_list_alloc();
+ const char *node_type;
+ mxml_node_t *node, *array = ctx->xml;
+ int len = 0;
+ uint32_t type;
+
+ if (ctx->set_list == NULL) {
errno = ENOMEM;
return -1;
}
- for (node = mxmlFindElement(tree, tree, "rule", NULL, NULL,
+ for (node = mxmlFindElement(array, array, NULL, NULL, NULL,
MXML_DESCEND_FIRST);
node != NULL;
- node = mxmlFindElement(node, tree, "rule", NULL, NULL,
+ node = mxmlFindElement(node, array, NULL, NULL, NULL,
MXML_NO_DESCEND)) {
- rule = nft_rule_alloc();
- if (rule == NULL)
- goto err_free;
-
- if (nft_mxml_rule_parse(node, rule, err, set_list) != 0) {
- nft_rule_free(rule);
- goto err_free;
- }
-
- nft_rule_list_add_tail(rule, rule_list);
+ len++;
+ node_type = node->value.opaque;
+ ctx->xml = node;
+ if (strcmp(node_type, "table") == 0) {
+ if (nft_ruleset_xml_parse_tables(ctx, err) != 0)
+ return -1;
+ } else if (strcmp(node_type, "chain") == 0) {
+ if (nft_ruleset_xml_parse_chains(ctx, err) != 0)
+ return -1;
+ } else if (strcmp(node_type, "set") == 0) {
+ if (nft_ruleset_xml_parse_sets(ctx, err) != 0)
+ return -1;
+ } else if (strcmp(node_type, "rule") == 0) {
+ if (nft_ruleset_xml_parse_rules(ctx, err) != 0)
+ return -1;
+ } else if (strcmp(node_type, "elements") == 0) {
+ if (nft_ruleset_xml_parse_set_elems(ctx, err) != 0)
+ return -1;
+ } else
+ return -1;
+ }
+ if (len == 0 && ctx->cmd == NFT_CMD_FLUSH) {
+ type = NFT_RULESET_TYPE_RULESET;
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
+ if (ctx->cb(ctx) < 0)
+ return -1;
}
-
- 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_ruleset(struct nft_ruleset *rs,
- mxml_node_t *tree,
- struct nft_parse_err *err)
+static int nft_ruleset_xml_parse_cmd(const char *cmd, struct nft_parse_err *err,
+ struct nft_parse_ctx *ctx)
{
- if (nft_ruleset_xml_parse_tables(rs, tree, err) != 0)
- return -1;
+ uint32_t cmdnum;
+ int ret;
+ mxml_node_t *nodecmd;
- if (nft_ruleset_xml_parse_chains(rs, tree, err) != 0)
+ ret = nft_str2cmd(cmd, &cmdnum);
+ if (ret < 0)
return -1;
- if (nft_ruleset_xml_parse_sets(rs, tree, err) != 0)
- return -1;
+ nodecmd = mxmlFindElement(ctx->xml, ctx->xml, cmd, NULL, NULL,
+ MXML_DESCEND_FIRST);
- if (nft_ruleset_xml_parse_rules(rs, tree, err, rs->set_list) != 0)
- return -1;
+ ctx->xml = nodecmd;
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_CMD, &cmdnum);
+
+ if (nft_ruleset_xml_parse_ruleset(ctx, err) != 0)
+ goto err;
return 0;
+err:
+ return -1;
}
#endif
-static int nft_ruleset_xml_parse(struct nft_ruleset *rs, const void *xml,
- struct nft_parse_err *err, enum nft_parse_input input)
+static int nft_ruleset_xml_parse(const void *xml, struct nft_parse_err *err,
+ enum nft_parse_input input, void *arg,
+ int (*cb)(struct nft_parse_ctx *ctx))
{
#ifdef XML_PARSING
- mxml_node_t *tree;
+ mxml_node_t *tree, *nodecmd = NULL;
+ char *cmd;
+ struct nft_parse_ctx *ctx = nft_ruleset_ctx_alloc();
+ if (ctx == NULL)
+ return -1;
+
+ ctx->cb = cb;
+ ctx->data = arg;
tree = nft_mxml_build_tree(xml, "nftables", err, input);
if (tree == NULL)
return -1;
- if (nft_ruleset_xml_parse_ruleset(rs, tree, err) != 0)
- goto err;
+ ctx->xml = tree;
+
+ nodecmd = mxmlWalkNext(tree, tree, MXML_DESCEND_FIRST);
+ while (nodecmd != NULL) {
+ cmd = nodecmd->value.opaque;
+ if (nft_ruleset_xml_parse_cmd(cmd, err, ctx) < 0)
+ goto err;
+ nodecmd = mxmlWalkNext(tree, tree, MXML_NO_DESCEND);
+ }
mxmlDelete(tree);
+ nft_ruleset_ctx_free(ctx);
return 0;
err:
mxmlDelete(tree);
+ nft_ruleset_ctx_free(ctx);
return -1;
#else
errno = EOPNOTSUPP;
@@ -591,18 +776,18 @@ err:
}
static int
-nft_ruleset_do_parse(struct nft_ruleset *r, enum nft_parse_type type,
- const void *data, struct nft_parse_err *err,
- enum nft_parse_input input)
+nft_ruleset_do_parse(enum nft_parse_type type, const void *data,
+ struct nft_parse_err *err, enum nft_parse_input input,
+ void *arg, int (*cb)(struct nft_parse_ctx *ctx))
{
int ret;
switch (type) {
case NFT_PARSE_XML:
- ret = nft_ruleset_xml_parse(r, data, err, input);
+ ret = nft_ruleset_xml_parse(data, err, input, arg, cb);
break;
case NFT_PARSE_JSON:
- ret = nft_ruleset_json_parse(r, data, err, input);
+ ret = nft_ruleset_json_parse(data, err, input, arg, cb);
break;
default:
ret = -1;
@@ -613,17 +798,89 @@ nft_ruleset_do_parse(struct nft_ruleset *r, enum nft_parse_type type,
return ret;
}
+int nft_ruleset_parse_file_cb(enum nft_parse_type type, FILE *fp,
+ struct nft_parse_err *err, void *data,
+ int (*cb)(struct nft_parse_ctx *ctx))
+{
+ return nft_ruleset_do_parse(type, fp, err, NFT_PARSE_FILE, data, cb);
+}
+EXPORT_SYMBOL(nft_ruleset_parse_file_cb);
+
+int nft_ruleset_parse_buffer_cb(enum nft_parse_type type, const char *buffer,
+ struct nft_parse_err *err, void *data,
+ int (*cb)(struct nft_parse_ctx *ctx))
+{
+ return nft_ruleset_do_parse(type, buffer, err, NFT_PARSE_BUFFER, data,
+ cb);
+}
+EXPORT_SYMBOL(nft_ruleset_parse_buffer_cb);
+
+static int nft_ruleset_init_ruleset(struct nft_parse_ctx *ctx)
+{
+ struct nft_ruleset *r = ctx->data;
+ struct nft_table_list *table_list = r->table_list;
+ struct nft_chain_list *chain_list = r->chain_list;
+ struct nft_rule_list *rule_list = r->rule_list;
+ struct nft_set_list *set_list = r->set_list;
+
+ if (ctx->cmd != NFT_CMD_ADD)
+ return -1;
+
+ switch (ctx->type) {
+ case NFT_RULESET_TYPE_TABLE:
+ if (table_list == NULL) {
+ table_list = nft_table_list_alloc();
+ nft_ruleset_attr_set(r, NFT_RULESET_ATTR_TABLELIST,
+ table_list);
+ }
+ nft_table_list_add_tail(ctx->table, table_list);
+ break;
+ case NFT_RULESET_TYPE_CHAIN:
+ if (chain_list == NULL) {
+ chain_list = nft_chain_list_alloc();
+ nft_ruleset_attr_set(r, NFT_RULESET_ATTR_CHAINLIST,
+ chain_list);
+ }
+ nft_chain_list_add_tail(ctx->chain, chain_list);
+ break;
+ case NFT_RULESET_TYPE_SET:
+ if (set_list == NULL) {
+ set_list = nft_set_list_alloc();
+ nft_ruleset_attr_set(r, NFT_RULESET_ATTR_SETLIST,
+ set_list);
+ }
+ nft_set_list_add_tail(ctx->set, set_list);
+ break;
+ case NFT_RULESET_TYPE_RULE:
+ if (rule_list == NULL) {
+ rule_list = nft_rule_list_alloc();
+ nft_ruleset_attr_set(r, NFT_RULESET_ATTR_RULELIST,
+ rule_list);
+ }
+ nft_rule_list_add_tail(ctx->rule, rule_list);
+ break;
+ case NFT_RULESET_TYPE_RULESET:
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
int nft_ruleset_parse(struct nft_ruleset *r, enum nft_parse_type type,
const char *data, struct nft_parse_err *err)
{
- return nft_ruleset_do_parse(r, type, data, err, NFT_PARSE_BUFFER);
+ return nft_ruleset_parse_buffer_cb(type, data, err, r,
+ nft_ruleset_init_ruleset);
}
EXPORT_SYMBOL(nft_ruleset_parse);
int nft_ruleset_parse_file(struct nft_ruleset *rs, enum nft_parse_type type,
FILE *fp, struct nft_parse_err *err)
{
- return nft_ruleset_do_parse(rs, type, fp, err, NFT_PARSE_FILE);
+ return nft_ruleset_parse_file_cb(type, fp, err, rs,
+ nft_ruleset_init_ruleset);
}
EXPORT_SYMBOL(nft_ruleset_parse_file);
diff --git a/src/set.c b/src/set.c
index ba42b91..3fa4794 100644
--- a/src/set.c
+++ b/src/set.c
@@ -511,6 +511,18 @@ int nft_jansson_parse_set(struct nft_set *s, json_t *tree,
return nft_jansson_parse_set_info(s, root, err);
}
+
+int nft_jansson_parse_elem(struct nft_set *s, json_t *tree,
+ struct nft_parse_err *err)
+{
+ json_t *root;
+
+ root = nft_jansson_get_node(tree, "element", err);
+ if (root == NULL)
+ return -1;
+
+ return nft_jansson_parse_set_info(s, root, err);
+}
#endif
static int nft_set_json_parse(struct nft_set *s, const void *json,
diff --git a/src/utils.c b/src/utils.c
index 5890115..87eaa61 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -16,6 +16,7 @@
#include <arpa/inet.h>
#include <errno.h>
#include <inttypes.h>
+#include <buffer.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
@@ -193,6 +194,28 @@ int nft_flag2cmd(uint32_t flags, uint32_t *cmd)
return -1;
}
+int nft_str2cmd(const char *cmd, uint32_t *cmd_num)
+{
+ if (strcmp(cmd, ADD) == 0) {
+ *cmd_num = NFT_CMD_ADD;
+ return 0;
+ } else if (strcmp(cmd, INSERT) == 0) {
+ *cmd_num = NFT_CMD_INSERT;
+ return 0;
+ } else if (strcmp(cmd, DELETE) == 0) {
+ *cmd_num = NFT_CMD_DELETE;
+ return 0;
+ } else if (strcmp(cmd, REPLACE) == 0) {
+ *cmd_num = NFT_CMD_REPLACE;
+ return 0;
+ } else if (strcmp(cmd, FLUSH) == 0) {
+ *cmd_num = NFT_CMD_FLUSH;
+ return 0;
+ }
+
+ return -1;
+}
+
int nft_fprintf(FILE *fp, void *obj, uint32_t type, uint32_t flags,
int (*snprintf_cb)(char *buf, size_t bufsiz, void *obj,
uint32_t type, uint32_t flags))
--
1.7.10.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [libnftnl PATCH 3/5] ruleset: consolidate code in json/xml import support
2015-01-23 14:49 [libnftnl PATCH] set: refactor code in json parse function Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 1/5] src: add command tag in json/xml export support Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 2/5] src: add support to import json/xml with the new syntax Alvaro Neira Ayuso
@ 2015-01-23 14:49 ` Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 4/5] example: Parse and create netlink message using the new parsing functions Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 5/5] test: update json/xml tests to the new syntax Alvaro Neira Ayuso
4 siblings, 0 replies; 6+ messages in thread
From: Alvaro Neira Ayuso @ 2015-01-23 14:49 UTC (permalink / raw)
To: netfilter-devel
Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
---
src/ruleset.c | 266 +++++++++++++++++++++++----------------------------------
1 file changed, 109 insertions(+), 157 deletions(-)
diff --git a/src/ruleset.c b/src/ruleset.c
index a3e4b20..3233e46 100644
--- a/src/ruleset.c
+++ b/src/ruleset.c
@@ -35,6 +35,7 @@ struct nft_ruleset {
struct nft_parse_ctx {
uint32_t cmd;
uint32_t type;
+ uint32_t format;
union {
struct nft_table *table;
struct nft_chain *chain;
@@ -256,8 +257,8 @@ void *nft_ruleset_ctx_attr_get(struct nft_parse_ctx *ctx, uint16_t attr)
}
EXPORT_SYMBOL(nft_ruleset_ctx_attr_get);
-#ifdef JSON_PARSING
-static int nft_ruleset_json_parse_tables(struct nft_parse_ctx *ctx,
+#if defined(JSON_PARSING) || defined(XML_PARSING)
+static int nft_ruleset_parse_tables(struct nft_parse_ctx *ctx,
struct nft_parse_err *err)
{
struct nft_table *table;
@@ -271,8 +272,22 @@ static int nft_ruleset_json_parse_tables(struct nft_parse_ctx *ctx,
return -1;
}
- if (nft_jansson_parse_table(table, ctx->json, err) < 0)
- goto err;
+ switch (ctx->format) {
+ case NFT_OUTPUT_JSON:
+#ifdef JSON_PARSING
+ if (nft_jansson_parse_table(table, ctx->json, err) < 0)
+ goto err;
+#endif
+ break;
+ case NFT_OUTPUT_XML:
+#ifdef XML_PARSING
+ if (nft_mxml_table_parse(ctx->xml, table, err) < 0)
+ goto err;
+#endif
+ break;
+ default:
+ return -1;
+ }
nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TABLE, table);
if (ctx->cb(ctx) < 0)
@@ -285,7 +300,7 @@ err:
return -1;
}
-static int nft_ruleset_json_parse_chains(struct nft_parse_ctx *ctx,
+static int nft_ruleset_parse_chains(struct nft_parse_ctx *ctx,
struct nft_parse_err *err)
{
struct nft_chain *chain;
@@ -299,8 +314,22 @@ static int nft_ruleset_json_parse_chains(struct nft_parse_ctx *ctx,
return -1;
}
- if (nft_jansson_parse_chain(chain, ctx->json, err) < 0)
- goto err;
+ switch (ctx->format) {
+ case NFT_OUTPUT_JSON:
+#ifdef JSON_PARSING
+ if (nft_jansson_parse_chain(chain, ctx->json, err) < 0)
+ goto err;
+#endif
+ break;
+ case NFT_OUTPUT_XML:
+#ifdef XML_PARSING
+ if (nft_mxml_chain_parse(ctx->xml, chain, err) < 0)
+ goto err;
+#endif
+ break;
+ default:
+ return -1;
+ }
nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_CHAIN, chain);
if (ctx->cb(ctx) < 0)
@@ -313,7 +342,7 @@ err:
return -1;
}
-static int nft_ruleset_json_parse_set(struct nft_parse_ctx *ctx,
+static int nft_ruleset_parse_set(struct nft_parse_ctx *ctx,
struct nft_set *set, int type,
struct nft_parse_err *err)
{
@@ -332,7 +361,7 @@ err:
return -1;
}
-static int nft_ruleset_json_parse_set_elems(struct nft_parse_ctx *ctx,
+static int nft_ruleset_parse_set_elems(struct nft_parse_ctx *ctx,
struct nft_parse_err *err)
{
struct nft_set *set;
@@ -343,18 +372,30 @@ static int nft_ruleset_json_parse_set_elems(struct nft_parse_ctx *ctx,
return -1;
}
+ switch (ctx->format) {
+ case NFT_OUTPUT_JSON:
+#ifdef JSON_PARSING
+ if (nft_jansson_parse_elem(set, ctx->json, err) < 0)
+ goto err;
+#endif
+ break;
+ case NFT_OUTPUT_XML:
+#ifdef XML_PARSING
+ if (nft_mxml_set_parse(ctx->xml, set, err) < 0)
+ goto err;
+#endif
+ break;
+ default:
+ return -1;
+ }
- if (nft_jansson_parse_elem(set, ctx->json, err) < 0)
- goto err;
-
- return nft_ruleset_json_parse_set(ctx, set, NFT_RULESET_TYPE_SET_ELEMS,
- err);
+ return nft_ruleset_parse_set(ctx, set, NFT_RULESET_TYPE_SET_ELEMS, err);
err:
nft_set_free(set);
return -1;
}
-static int nft_ruleset_json_parse_sets(struct nft_parse_ctx *ctx,
+static int nft_ruleset_parse_sets(struct nft_parse_ctx *ctx,
struct nft_parse_err *err)
{
struct nft_set *set;
@@ -365,16 +406,30 @@ static int nft_ruleset_json_parse_sets(struct nft_parse_ctx *ctx,
return -1;
}
- if (nft_jansson_parse_set(set, ctx->json, err) < 0)
- goto err;
+ switch (ctx->format) {
+ case NFT_OUTPUT_JSON:
+#ifdef JSON_PARSING
+ if (nft_jansson_parse_set(set, ctx->json, err) < 0)
+ goto err;
+#endif
+ break;
+ case NFT_OUTPUT_XML:
+#ifdef XML_PARSING
+ if (nft_mxml_set_parse(ctx->xml, set, err) < 0)
+ goto err;
+#endif
+ break;
+ default:
+ return -1;
+ }
- return nft_ruleset_json_parse_set(ctx, set, NFT_RULESET_TYPE_SET, err);
+ return nft_ruleset_parse_set(ctx, set, NFT_RULESET_TYPE_SET, err);
err:
nft_set_free(set);
return -1;
}
-static int nft_ruleset_json_parse_rules(struct nft_parse_ctx *ctx,
+static int nft_ruleset_parse_rules(struct nft_parse_ctx *ctx,
struct nft_parse_err *err)
{
struct nft_rule *rule;
@@ -388,8 +443,23 @@ static int nft_ruleset_json_parse_rules(struct nft_parse_ctx *ctx,
return -1;
}
- if (nft_jansson_parse_rule(rule, ctx->json, err, ctx->set_list) < 0)
- goto err;
+ switch (ctx->format) {
+ case NFT_OUTPUT_JSON:
+#ifdef JSON_PARSING
+ if (nft_jansson_parse_rule(rule, ctx->json, err,
+ ctx->set_list) < 0)
+ goto err;
+#endif
+ break;
+ case NFT_OUTPUT_XML:
+#ifdef XML_PARSING
+ if (nft_mxml_rule_parse(ctx->xml, rule, err, ctx->set_list) < 0)
+ goto err;
+#endif
+ break;
+ default:
+ return -1;
+ }
nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_RULE, rule);
if (ctx->cb(ctx) < 0)
@@ -423,19 +493,19 @@ static int nft_ruleset_json_parse_ruleset(struct nft_parse_ctx *ctx,
ctx->json = node;
if (nft_jansson_node_exist(node, "table")) {
- if (nft_ruleset_json_parse_tables(ctx, err) < 0)
+ if (nft_ruleset_parse_tables(ctx, err) < 0)
return -1;
} else if (nft_jansson_node_exist(node, "chain")) {
- if (nft_ruleset_json_parse_chains(ctx, err) < 0)
+ if (nft_ruleset_parse_chains(ctx, err) < 0)
return -1;
} else if (nft_jansson_node_exist(node, "set")) {
- if (nft_ruleset_json_parse_sets(ctx, err) < 0)
+ if (nft_ruleset_parse_sets(ctx, err) < 0)
return -1;
} else if (nft_jansson_node_exist(node, "rule")) {
- if (nft_ruleset_json_parse_rules(ctx, err) < 0)
+ if (nft_ruleset_parse_rules(ctx, err) < 0)
return -1;
} else if (nft_jansson_node_exist(node, "elements")) {
- if (nft_ruleset_json_parse_set_elems(ctx, err) < 0)
+ if (nft_ruleset_parse_set_elems(ctx, err) < 0)
return -1;
} else {
return -1;
@@ -481,7 +551,8 @@ err:
static int nft_ruleset_json_parse(const void *json,
struct nft_parse_err *err,
- enum nft_parse_input input, void *arg,
+ enum nft_parse_input input,
+ enum nft_parse_type type, void *arg,
int (*cb)(struct nft_parse_ctx *ctx))
{
#ifdef JSON_PARSING
@@ -496,6 +567,7 @@ static int nft_ruleset_json_parse(const void *json,
ctx->cb = cb;
ctx->data = arg;
+ ctx->format = type;
root = nft_jansson_create_root(json, &error, err, input);
if (root == NULL)
return -1;
@@ -536,128 +608,6 @@ err:
}
#ifdef XML_PARSING
-static int
-nft_ruleset_xml_parse_tables(struct nft_parse_ctx *ctx,
- struct nft_parse_err *err)
-{
- struct nft_table *table;
- uint32_t type = NFT_RULESET_TYPE_TABLE;
-
- nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
-
- table = nft_table_alloc();
- if (table == NULL)
- return -1;
-
- if (nft_mxml_table_parse(ctx->xml, table, err) != 0)
- goto err;
-
- nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TABLE, table);
- if (ctx->cb(ctx) < 0)
- goto err;
- nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_TABLE);
-
- return 0;
-err:
- nft_table_free(table);
- return -1;
-}
-
-static int
-nft_ruleset_xml_parse_chains(struct nft_parse_ctx *ctx,
- struct nft_parse_err *err)
-{
- struct nft_chain *chain;
- uint32_t type = NFT_RULESET_TYPE_CHAIN;
-
- nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
-
- chain = nft_chain_alloc();
- if (chain == NULL)
- return -1;
-
- if (nft_mxml_chain_parse(ctx->xml, chain, err) != 0)
- goto err;
-
- nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_CHAIN, chain);
- if (ctx->cb(ctx) < 0)
- goto err;
- nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_CHAIN);
-
- return 0;
-err:
- nft_chain_free(chain);
- return -1;
-}
-
-static int
-nft_ruleset_xml_parse_set(struct nft_parse_ctx *ctx, int type,
- struct nft_parse_err *err)
-{
- struct nft_set *set;
-
- nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
- set = nft_set_alloc();
- if (set == NULL)
- return -1;
-
- if (nft_mxml_set_parse(ctx->xml, set, err) != 0)
- goto err;
-
- nft_set_attr_set_u32(set, NFT_SET_ATTR_ID, ctx->set_id++);
- nft_set_list_add_tail(set, ctx->set_list);
- nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_SET, set);
- if (ctx->cb(ctx) < 0)
- goto err;
- nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_SET);
-
- return 0;
-err:
- nft_set_free(set);
- return -1;
-}
-
-static int
-nft_ruleset_xml_parse_set_elems(struct nft_parse_ctx *ctx,
- struct nft_parse_err *err)
-{
- return nft_ruleset_xml_parse_set(ctx, NFT_RULESET_TYPE_SET, err);
-}
-
-static int
-nft_ruleset_xml_parse_sets(struct nft_parse_ctx *ctx,
- struct nft_parse_err *err)
-{
- return nft_ruleset_xml_parse_set(ctx, NFT_RULESET_TYPE_SET, err);
-}
-
-static int
-nft_ruleset_xml_parse_rules(struct nft_parse_ctx *ctx,
- struct nft_parse_err *err)
-{
- struct nft_rule *rule;
- uint32_t type = NFT_RULESET_TYPE_RULE;
-
- nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TYPE, &type);
-
- rule = nft_rule_alloc();
- if (rule == NULL)
- return -1;
-
- if (nft_mxml_rule_parse(ctx->xml, rule, err, ctx->set_list) != 0)
- goto err;
-
- nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_RULE, rule);
- if (ctx->cb(ctx) < 0)
- goto err;
- nft_ruleset_ctx_attr_unset(ctx, NFT_RULESET_CTX_RULE);
-
- return 0;
-err:
- nft_rule_free(rule);
- return -1;
-}
-
static int nft_ruleset_xml_parse_ruleset(struct nft_parse_ctx *ctx,
struct nft_parse_err *err)
{
@@ -681,19 +631,19 @@ static int nft_ruleset_xml_parse_ruleset(struct nft_parse_ctx *ctx,
node_type = node->value.opaque;
ctx->xml = node;
if (strcmp(node_type, "table") == 0) {
- if (nft_ruleset_xml_parse_tables(ctx, err) != 0)
+ if (nft_ruleset_parse_tables(ctx, err) != 0)
return -1;
} else if (strcmp(node_type, "chain") == 0) {
- if (nft_ruleset_xml_parse_chains(ctx, err) != 0)
+ if (nft_ruleset_parse_chains(ctx, err) != 0)
return -1;
} else if (strcmp(node_type, "set") == 0) {
- if (nft_ruleset_xml_parse_sets(ctx, err) != 0)
+ if (nft_ruleset_parse_sets(ctx, err) != 0)
return -1;
} else if (strcmp(node_type, "rule") == 0) {
- if (nft_ruleset_xml_parse_rules(ctx, err) != 0)
+ if (nft_ruleset_parse_rules(ctx, err) != 0)
return -1;
} else if (strcmp(node_type, "elements") == 0) {
- if (nft_ruleset_xml_parse_set_elems(ctx, err) != 0)
+ if (nft_ruleset_parse_set_elems(ctx, err) != 0)
return -1;
} else
return -1;
@@ -735,7 +685,8 @@ err:
#endif
static int nft_ruleset_xml_parse(const void *xml, struct nft_parse_err *err,
- enum nft_parse_input input, void *arg,
+ enum nft_parse_input input,
+ enum nft_parse_type type, void *arg,
int (*cb)(struct nft_parse_ctx *ctx))
{
#ifdef XML_PARSING
@@ -748,6 +699,7 @@ static int nft_ruleset_xml_parse(const void *xml, struct nft_parse_err *err,
ctx->cb = cb;
ctx->data = arg;
+ ctx->format = type;
tree = nft_mxml_build_tree(xml, "nftables", err, input);
if (tree == NULL)
return -1;
@@ -784,10 +736,10 @@ nft_ruleset_do_parse(enum nft_parse_type type, const void *data,
switch (type) {
case NFT_PARSE_XML:
- ret = nft_ruleset_xml_parse(data, err, input, arg, cb);
+ ret = nft_ruleset_xml_parse(data, err, input, type, arg, cb);
break;
case NFT_PARSE_JSON:
- ret = nft_ruleset_json_parse(data, err, input, arg, cb);
+ ret = nft_ruleset_json_parse(data, err, input, type, arg, cb);
break;
default:
ret = -1;
--
1.7.10.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [libnftnl PATCH 4/5] example: Parse and create netlink message using the new parsing functions.
2015-01-23 14:49 [libnftnl PATCH] set: refactor code in json parse function Alvaro Neira Ayuso
` (2 preceding siblings ...)
2015-01-23 14:49 ` [libnftnl PATCH 3/5] ruleset: consolidate code in json/xml import support Alvaro Neira Ayuso
@ 2015-01-23 14:49 ` Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 5/5] test: update json/xml tests to the new syntax Alvaro Neira Ayuso
4 siblings, 0 replies; 6+ messages in thread
From: Alvaro Neira Ayuso @ 2015-01-23 14:49 UTC (permalink / raw)
To: netfilter-devel
With this example, we can parse the elements in the ruleset and create the
netlink message with the action associated. For example:
- Flush ruleset
- Add, delete or flush tables/chains
- Add, delete sets
- Add, delete set elements
- Add, delete, replace or prepend rules
Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
---
examples/Makefile.am | 4 +
examples/nft-ruleset-cmd.c | 437 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 441 insertions(+)
create mode 100644 examples/nft-ruleset-cmd.c
diff --git a/examples/Makefile.am b/examples/Makefile.am
index fafcb76..f2a2740 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -22,6 +22,7 @@ check_PROGRAMS = nft-table-add \
nft-set-elem-get \
nft-set-elem-del \
nft-ruleset-get \
+ nft-ruleset-cmd \
nft-compat-get
nft_table_add_SOURCES = nft-table-add.c
@@ -90,5 +91,8 @@ nft_set_elem_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
nft_ruleset_get_SOURCES = nft-ruleset-get.c
nft_ruleset_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
+nft_ruleset_cmd_SOURCES = nft-ruleset-cmd.c
+nft_ruleset_cmd_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
+
nft_compat_get_SOURCES = nft-compat-get.c
nft_compat_get_LDADD = ../src/libnftnl.la ${LIBMNL_LIBS}
diff --git a/examples/nft-ruleset-cmd.c b/examples/nft-ruleset-cmd.c
new file mode 100644
index 0000000..9a93325
--- /dev/null
+++ b/examples/nft-ruleset-cmd.c
@@ -0,0 +1,437 @@
+/*
+ * (C) 2014 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.
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <stddef.h> /* for offsetof */
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/ruleset.h>
+#include <libnftnl/table.h>
+#include <libnftnl/chain.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/set.h>
+
+struct mnl_nlmsg_batch *batch;
+uint32_t seq;
+
+static int nft_ruleset_flush_rules(struct nft_parse_ctx *ctx);
+
+static int nft_ruleset_set_elems(struct nft_parse_ctx *ctx, uint32_t ctx_cmd)
+{
+ struct nft_set_elems_iter *iter_elems;
+ struct nft_set_elem *elem;
+ struct nft_set *tmp;
+ uint16_t type = 0, cmd = 0;
+ struct nlmsghdr *nlh;
+ struct nft_set *set;
+
+ set = nft_ruleset_ctx_attr_get(ctx, NFT_RULESET_CTX_SET);
+ if (set == NULL)
+ return -1;
+
+ switch (ctx_cmd) {
+ case NFT_CMD_ADD:
+ cmd = NFT_MSG_NEWSETELEM;
+ type = NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK;
+ break;
+ case NFT_CMD_DELETE:
+ cmd = NFT_MSG_DELSETELEM;
+ type = NLM_F_ACK;
+ break;
+ }
+
+ iter_elems = nft_set_elems_iter_create(set);
+ elem = nft_set_elems_iter_next(iter_elems);
+ while (elem != NULL) {
+ tmp = nft_set_alloc();
+ if (tmp == NULL)
+ return -1;
+
+ nft_set_attr_set(tmp, NFT_SET_ATTR_TABLE,
+ nft_set_attr_get_str(set, NFT_SET_ATTR_TABLE));
+ nft_set_attr_set(tmp, NFT_SET_ATTR_NAME,
+ nft_set_attr_get_str(set, NFT_SET_ATTR_NAME));
+
+ nft_set_elem_add(tmp, elem);
+
+ nlh = nft_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd,
+ nft_set_attr_get_u32(set,
+ NFT_SET_ATTR_FAMILY),
+ type, seq++);
+ nft_set_elems_nlmsg_build_payload(nlh, tmp);
+ mnl_nlmsg_batch_next(batch);
+ elem = nft_set_elems_iter_next(iter_elems);
+ }
+
+ nft_set_free(set);
+ return 0;
+}
+
+static int nft_ruleset_set(struct nft_parse_ctx *ctx, uint32_t ctx_cmd)
+{
+
+ struct nlmsghdr *nlh;
+ uint16_t type = 0, cmd = 0;
+ struct nft_set *set;
+
+ set = nft_ruleset_ctx_attr_get(ctx, NFT_RULESET_CTX_SET);
+ if (set == NULL)
+ return -1;
+
+ switch (ctx_cmd) {
+ case NFT_CMD_ADD:
+ cmd = NFT_MSG_NEWSET;
+ type = NLM_F_CREATE|NLM_F_ACK;
+ break;
+ case NFT_CMD_DELETE:
+ cmd = NFT_MSG_DELSET;
+ type = NLM_F_ACK;
+ break;
+ }
+
+ nlh = nft_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd,
+ nft_set_attr_get_u32(set,
+ NFT_SET_ATTR_FAMILY),
+ type,
+ seq++);
+
+ nft_set_nlmsg_build_payload(nlh, set);
+
+ mnl_nlmsg_batch_next(batch);
+
+ if (nft_ruleset_set_elems(ctx, ctx_cmd) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int nft_ruleset_rule(struct nft_parse_ctx *ctx, uint32_t ctx_cmd)
+{
+ struct nlmsghdr *nlh;
+ uint16_t type = 0, cmd = 0;
+ struct nft_rule *rule;
+
+ rule = nft_ruleset_ctx_attr_get(ctx, NFT_RULESET_CTX_RULE);
+ if (rule == NULL)
+ return -1;
+
+ switch (ctx_cmd) {
+ case NFT_CMD_ADD:
+ cmd = NFT_MSG_NEWRULE;
+ type = NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK;
+ break;
+ case NFT_CMD_DELETE:
+ cmd = NFT_MSG_DELRULE;
+ type = NLM_F_ACK;
+ break;
+ case NFT_CMD_REPLACE:
+ cmd = NFT_MSG_NEWRULE;
+ type = NLM_F_REPLACE|NLM_F_ACK;
+ break;
+ case NFT_CMD_INSERT:
+ cmd = NFT_MSG_NEWRULE;
+ type = NLM_F_CREATE|NLM_F_ACK;
+ break;
+ }
+
+ nlh = nft_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd,
+ nft_rule_attr_get_u32(rule,
+ NFT_RULE_ATTR_FAMILY),
+ type,
+ seq++);
+
+ nft_rule_nlmsg_build_payload(nlh, rule);
+
+ mnl_nlmsg_batch_next(batch);
+ nft_rule_free(rule);
+ return 0;
+}
+
+static int nft_ruleset_chain(struct nft_parse_ctx *ctx, uint32_t ctx_cmd)
+{
+ struct nlmsghdr *nlh;
+ uint16_t type = 0, cmd = 0;
+ struct nft_chain *chain;
+
+ chain = nft_ruleset_ctx_attr_get(ctx, NFT_RULESET_CTX_CHAIN);
+ if (chain == NULL)
+ return -1;
+
+ switch (ctx_cmd) {
+ case NFT_CMD_ADD:
+ cmd = NFT_MSG_NEWCHAIN;
+ type = NLM_F_CREATE|NLM_F_ACK;
+ break;
+ case NFT_CMD_DELETE:
+ cmd = NFT_MSG_DELCHAIN;
+ type = NLM_F_ACK;
+ break;
+ case NFT_CMD_FLUSH:
+ return nft_ruleset_flush_rules(ctx);
+ }
+
+ nft_chain_attr_unset(chain, NFT_CHAIN_ATTR_HANDLE);
+ nlh = nft_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd,
+ nft_chain_attr_get_u32(chain,
+ NFT_CHAIN_ATTR_FAMILY),
+ type,
+ seq++);
+
+ nft_chain_nlmsg_build_payload(nlh, chain);
+
+ mnl_nlmsg_batch_next(batch);
+ nft_chain_free(chain);
+ return 0;
+}
+
+static int nft_ruleset_table(struct nft_parse_ctx *ctx, uint32_t ctx_cmd)
+{
+ struct nlmsghdr *nlh;
+ uint16_t type = 0, cmd = 0;
+ struct nft_table *table;
+
+ table = nft_ruleset_ctx_attr_get(ctx, NFT_RULESET_CTX_TABLE);
+ if (table == NULL)
+ return -1;
+
+ switch (ctx_cmd) {
+ case NFT_CMD_ADD:
+ cmd = NFT_MSG_NEWTABLE;
+ type = NLM_F_CREATE|NLM_F_ACK;
+ break;
+ case NFT_CMD_DELETE:
+ cmd = NFT_MSG_DELTABLE;
+ type = NLM_F_ACK;
+ break;
+ case NFT_CMD_FLUSH:
+ return nft_ruleset_flush_rules(ctx);
+ }
+
+ nlh = nft_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
+ cmd,
+ nft_table_attr_get_u32(table,
+ NFT_TABLE_ATTR_FAMILY),
+ type,
+ seq++);
+
+ nft_table_nlmsg_build_payload(nlh, table);
+ mnl_nlmsg_batch_next(batch);
+ nft_table_free(table);
+ return 0;
+}
+
+static int nft_ruleset_flush_rules(struct nft_parse_ctx *ctx)
+{
+ struct nft_rule *nlr;
+ struct nft_table *nlt;
+ struct nft_chain *nlc;
+ uint32_t *type = nft_ruleset_ctx_attr_get(ctx,
+ NFT_RULESET_CTX_TYPE);
+
+ nlr = nft_rule_alloc();
+ if (nlr == NULL)
+ return -1;
+
+ switch (*type) {
+ case NFT_RULESET_TYPE_TABLE:
+ nlt = nft_ruleset_ctx_attr_get(ctx, NFT_RULESET_CTX_TABLE);
+ nft_rule_attr_set(nlr, NFT_RULE_ATTR_TABLE,
+ nft_table_attr_get(nlt, NFT_TABLE_ATTR_NAME));
+ nft_rule_attr_set(nlr, NFT_RULE_ATTR_FAMILY,
+ nft_table_attr_get(nlt, NFT_TABLE_ATTR_FAMILY));
+ break;
+ case NFT_RULESET_TYPE_CHAIN:
+ nlc = nft_ruleset_ctx_attr_get(ctx, NFT_RULESET_CTX_CHAIN);
+ nft_rule_attr_set(nlr, NFT_RULE_ATTR_TABLE,
+ nft_chain_attr_get(nlc,
+ NFT_CHAIN_ATTR_TABLE));
+ nft_rule_attr_set(nlr, NFT_RULE_ATTR_CHAIN,
+ nft_chain_attr_get(nlc,
+ NFT_CHAIN_ATTR_NAME));
+ nft_rule_attr_set(nlr, NFT_RULE_ATTR_FAMILY,
+ nft_chain_attr_get(nlc, NFT_TABLE_ATTR_FAMILY));
+ break;
+ }
+
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_RULE, nlr);
+ return nft_ruleset_rule(ctx, NFT_CMD_DELETE);
+}
+
+static int nft_ruleset_flush_ruleset(struct nft_parse_ctx *ctx)
+{
+ struct nft_table *nlt;
+
+ nlt = nft_table_alloc();
+ if (nlt == NULL)
+ return -1;
+
+ nft_ruleset_ctx_attr_set(ctx, NFT_RULESET_CTX_TABLE, nlt);
+ return nft_ruleset_table(ctx, NFT_CMD_DELETE);
+}
+
+static int ruleset_elems_cb(struct nft_parse_ctx *ctx)
+{
+ uint32_t *ctx_cmd = nft_ruleset_ctx_attr_get(ctx,
+ NFT_RULESET_CTX_CMD);
+ uint32_t *type = nft_ruleset_ctx_attr_get(ctx,
+ NFT_RULESET_CTX_TYPE);
+
+ switch (*type) {
+ case NFT_RULESET_TYPE_TABLE:
+ if (nft_ruleset_table(ctx, *ctx_cmd) < 0)
+ return -1;
+ break;
+ case NFT_RULESET_TYPE_CHAIN:
+ if (nft_ruleset_chain(ctx, *ctx_cmd) < 0)
+ return -1;
+ break;
+ case NFT_RULESET_TYPE_RULE:
+ if (nft_ruleset_rule(ctx, *ctx_cmd) < 0)
+ return -1;
+ break;
+ case NFT_RULESET_TYPE_SET:
+ if (nft_ruleset_set(ctx, *ctx_cmd) < 0)
+ return -1;
+ break;
+ case NFT_RULESET_TYPE_SET_ELEMS:
+ if (nft_ruleset_set_elems(ctx, *ctx_cmd) < 0)
+ return -1;
+ break;
+ case NFT_RULESET_TYPE_RULESET:
+ if (nft_ruleset_flush_ruleset(ctx) < 0)
+ return -1;
+ break;
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct nft_parse_err *err;
+ const char *filename;
+ FILE *fp;
+ int ret = -1, len, batching, portid;
+ uint32_t ruleset_seq;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct mnl_socket *nl;
+
+ if (argc < 2) {
+ printf("Usage: %s <file /.json/.xml>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ fp = fopen(argv[1], "r");
+ if (fp == NULL) {
+ printf("unable to open file %s: %s\n", argv[1],
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ err = nft_parse_err_alloc();
+ if (err == NULL) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ batching = nft_batch_is_supported();
+ if (batching < 0) {
+ perror("Cannot talk to nfnetlink");
+ exit(EXIT_FAILURE);
+ }
+
+ seq = time(NULL);
+ batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
+
+ if (batching) {
+ nft_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
+ mnl_nlmsg_batch_next(batch);
+ }
+ ruleset_seq = seq;
+
+ filename = argv[1];
+ len = strlen(filename);
+ if (strcmp(&filename[len-5], ".json") == 0)
+ ret = nft_ruleset_parse_file_cb(NFT_PARSE_JSON, fp, err, NULL,
+ &ruleset_elems_cb);
+ else if (strcmp(&filename[len-4], ".xml") == 0)
+ ret = nft_ruleset_parse_file_cb(NFT_PARSE_XML, fp, err, NULL,
+ &ruleset_elems_cb);
+ else {
+ printf("the filename %s must to end in .xml or .json\n",
+ filename);
+ exit(EXIT_FAILURE);
+ }
+
+ if (ret < 0) {
+ nft_parse_perror("fail", err);
+ exit(EXIT_FAILURE);
+ }
+
+ fclose(fp);
+
+ if (batching) {
+ nft_batch_end(mnl_nlmsg_batch_current(batch), seq++);
+ mnl_nlmsg_batch_next(batch);
+ }
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ 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);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ mnl_nlmsg_fprintf(stdout, mnl_nlmsg_batch_head(batch),
+ mnl_nlmsg_batch_size(batch), sizeof(struct nfgenmsg));
+
+ if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
+ mnl_nlmsg_batch_size(batch)) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_nlmsg_batch_stop(batch);
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, ruleset_seq, portid, NULL, NULL);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+ return EXIT_SUCCESS;
+}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [libnftnl PATCH 5/5] test: update json/xml tests to the new syntax
2015-01-23 14:49 [libnftnl PATCH] set: refactor code in json parse function Alvaro Neira Ayuso
` (3 preceding siblings ...)
2015-01-23 14:49 ` [libnftnl PATCH 4/5] example: Parse and create netlink message using the new parsing functions Alvaro Neira Ayuso
@ 2015-01-23 14:49 ` Alvaro Neira Ayuso
4 siblings, 0 replies; 6+ messages in thread
From: Alvaro Neira Ayuso @ 2015-01-23 14:49 UTC (permalink / raw)
To: netfilter-devel
Signed-off-by: Alvaro Neira Ayuso <alvaroneay@gmail.com>
---
tests/jsonfiles/01-table.json | 2 +-
tests/jsonfiles/02-table.json | 2 +-
tests/jsonfiles/11-chain.json | 2 +-
tests/jsonfiles/12-chain.json | 2 +-
tests/jsonfiles/13-chain.json | 2 +-
tests/jsonfiles/14-chain.json | 2 +-
tests/jsonfiles/20-rule-bitwise.json | 2 +-
tests/jsonfiles/21-rule-byteorder.json | 2 +-
tests/jsonfiles/22-rule-cmp.json | 2 +-
tests/jsonfiles/23-rule-counter.json | 2 +-
tests/jsonfiles/24-rule-ct.json | 2 +-
tests/jsonfiles/25-rule-exthdr.json | 2 +-
tests/jsonfiles/26-rule-immediate.json | 2 +-
tests/jsonfiles/27-rule-limit.json | 2 +-
tests/jsonfiles/28-rule-log.json | 2 +-
tests/jsonfiles/29-rule-match.json | 2 +-
tests/jsonfiles/30-rule-lookup.json | 2 +-
tests/jsonfiles/31-rule-meta.json | 2 +-
tests/jsonfiles/32-rule-nat4.json | 2 +-
tests/jsonfiles/33-rule-nat6.json | 2 +-
tests/jsonfiles/34-rule-payload.json | 2 +-
tests/jsonfiles/35-rule-target.json | 2 +-
tests/jsonfiles/36-rule-real.json | 2 +-
tests/jsonfiles/37-rule-real.json | 2 +-
tests/jsonfiles/38-rule-real.json | 2 +-
tests/jsonfiles/39-rule-real.json | 2 +-
tests/jsonfiles/40-rule-real.json | 2 +-
tests/jsonfiles/41-rule-real.json | 2 +-
tests/jsonfiles/42-rule-real.json | 2 +-
tests/jsonfiles/43-rule-real.json | 2 +-
tests/jsonfiles/44-rule-real.json | 2 +-
tests/jsonfiles/45-rule-real.json | 2 +-
tests/jsonfiles/46-rule-real.json | 2 +-
tests/jsonfiles/47-rule-real.json | 2 +-
tests/jsonfiles/48-rule-real.json | 2 +-
tests/jsonfiles/49-rule-real.json | 2 +-
tests/jsonfiles/50-rule-real.json | 2 +-
tests/jsonfiles/51-rule-real.json | 2 +-
tests/jsonfiles/52-rule-real.json | 2 +-
tests/jsonfiles/53-rule-real.json | 2 +-
tests/jsonfiles/54-rule-real.json | 2 +-
tests/jsonfiles/55-rule-real.json | 2 +-
tests/jsonfiles/56-rule-real.json | 2 +-
tests/jsonfiles/57-rule-real.json | 2 +-
tests/jsonfiles/58-rule-real.json | 2 +-
tests/jsonfiles/59-rule-real.json | 2 +-
tests/jsonfiles/60-rule-real.json | 2 +-
tests/jsonfiles/61-rule-real.json | 2 +-
tests/jsonfiles/62-set.json | 2 +-
tests/jsonfiles/63-set.json | 2 +-
tests/jsonfiles/64-ruleset.json | 2 +-
tests/jsonfiles/65-rule-meta-target.json | 2 +-
tests/jsonfiles/66-rule-queue.json | 2 +-
tests/jsonfiles/67-rule-queue.json | 2 +-
tests/jsonfiles/68-rule-masq.json | 2 +-
tests/jsonfiles/69-rule-redir.json | 2 +-
tests/jsonfiles/70-rule-real.json | 1 +
tests/nft-parsing-test.c | 4 ++--
tests/xmlfiles/01-table.xml | 2 +-
tests/xmlfiles/02-table.xml | 2 +-
tests/xmlfiles/10-chain.xml | 2 +-
tests/xmlfiles/11-chain.xml | 2 +-
tests/xmlfiles/12-chain.xml | 2 +-
tests/xmlfiles/20-rule-bitwise.xml | 2 +-
tests/xmlfiles/21-rule-byteorder.xml | 2 +-
tests/xmlfiles/22-rule-cmp.xml | 2 +-
tests/xmlfiles/23-rule-counter.xml | 2 +-
tests/xmlfiles/24-rule-ct.xml | 2 +-
tests/xmlfiles/25-rule-exthdr.xml | 2 +-
tests/xmlfiles/26-rule-immediate.xml | 2 +-
tests/xmlfiles/27-rule-limit.xml | 2 +-
tests/xmlfiles/28-rule-log.xml | 2 +-
tests/xmlfiles/29-rule-lookup.xml | 2 +-
tests/xmlfiles/30-rule-match.xml | 2 +-
tests/xmlfiles/31-rule-meta.xml | 2 +-
tests/xmlfiles/32-rule-nat6.xml | 2 +-
tests/xmlfiles/33-rule-nat4.xml | 2 +-
tests/xmlfiles/34-rule-payload.xml | 2 +-
tests/xmlfiles/35-rule-target.xml | 2 +-
tests/xmlfiles/36-rule-real.xml | 2 +-
tests/xmlfiles/37-rule-real.xml | 2 +-
tests/xmlfiles/38-rule-real.xml | 2 +-
tests/xmlfiles/39-rule-real.xml | 2 +-
tests/xmlfiles/40-rule-real.xml | 2 +-
tests/xmlfiles/41-rule-real.xml | 2 +-
tests/xmlfiles/42-rule-real.xml | 2 +-
tests/xmlfiles/43-rule-real.xml | 2 +-
tests/xmlfiles/44-rule-real.xml | 2 +-
tests/xmlfiles/45-rule-real.xml | 2 +-
tests/xmlfiles/46-rule-real.xml | 2 +-
tests/xmlfiles/47-rule-real.xml | 2 +-
tests/xmlfiles/48-rule-real.xml | 2 +-
tests/xmlfiles/49-rule-real.xml | 2 +-
tests/xmlfiles/50-rule-real.xml | 2 +-
tests/xmlfiles/51-rule-real.xml | 2 +-
tests/xmlfiles/52-rule-real.xml | 2 +-
tests/xmlfiles/53-rule-real.xml | 2 +-
tests/xmlfiles/54-rule-real.xml | 2 +-
tests/xmlfiles/55-rule-real.xml | 2 +-
tests/xmlfiles/56-rule-real.xml | 2 +-
tests/xmlfiles/57-rule-real.xml | 2 +-
tests/xmlfiles/58-rule-real.xml | 2 +-
tests/xmlfiles/59-rule-real.xml | 2 +-
tests/xmlfiles/60-rule-real.xml | 2 +-
tests/xmlfiles/61-rule-real.xml | 2 +-
tests/xmlfiles/62-rule-real.xml | 2 +-
tests/xmlfiles/63-rule-real.xml | 2 +-
tests/xmlfiles/64-rule-real.xml | 2 +-
tests/xmlfiles/65-rule-real.xml | 2 +-
tests/xmlfiles/66-rule-real.xml | 2 +-
tests/xmlfiles/67-rule-real.xml | 2 +-
tests/xmlfiles/68-rule-real.xml | 2 +-
tests/xmlfiles/69-rule-real.xml | 2 +-
tests/xmlfiles/70-rule-real.xml | 2 +-
tests/xmlfiles/71-rule-real.xml | 2 +-
tests/xmlfiles/72-rule-real.xml | 2 +-
tests/xmlfiles/73-set.xml | 2 +-
tests/xmlfiles/74-set.xml | 2 +-
tests/xmlfiles/75-ruleset.xml | 2 +-
tests/xmlfiles/76-rule-meta_target.xml | 2 +-
tests/xmlfiles/77-rule-queue.xml | 2 +-
tests/xmlfiles/78-rule-queue.xml | 2 +-
tests/xmlfiles/79-rule-masq.xml | 2 +-
tests/xmlfiles/80-rule-redir.xml | 2 +-
tests/xmlfiles/81-rule-real.xml | 1 +
125 files changed, 126 insertions(+), 124 deletions(-)
create mode 100644 tests/jsonfiles/70-rule-real.json
create mode 100644 tests/xmlfiles/81-rule-real.xml
diff --git a/tests/jsonfiles/01-table.json b/tests/jsonfiles/01-table.json
index 9be7b40..20754f1 100644
--- a/tests/jsonfiles/01-table.json
+++ b/tests/jsonfiles/01-table.json
@@ -1 +1 @@
-{"nftables":[{"table":{"name":"filter","family":"ip","flags":0,"use":0}}]}
+{"nftables":[{"add":[{"table":{"name":"filter","family":"ip","flags":0,"use":0}}]}]}
diff --git a/tests/jsonfiles/02-table.json b/tests/jsonfiles/02-table.json
index 4ff5674..797a00d 100644
--- a/tests/jsonfiles/02-table.json
+++ b/tests/jsonfiles/02-table.json
@@ -1 +1 @@
-{"nftables":[{"table":{"name":"filter2","family":"ip6","flags":0,"use":0}}]}
+{"nftables":[{"add":[{"table":{"name":"filter2","family":"ip6","flags":0,"use":0}}]}]}
diff --git a/tests/jsonfiles/11-chain.json b/tests/jsonfiles/11-chain.json
index 731ed1f..5808819 100644
--- a/tests/jsonfiles/11-chain.json
+++ b/tests/jsonfiles/11-chain.json
@@ -1 +1 @@
-{"nftables":[{"chain":{"name":"input","handle":1,"bytes":1375696,"packets":4136,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"input","prio":0,"policy":"accept"}}]}
+{"nftables":[{"add":[{"chain":{"name":"input","handle":1,"bytes":1375696,"packets":4136,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"input","prio":0,"policy":"accept"}}]}]}
diff --git a/tests/jsonfiles/12-chain.json b/tests/jsonfiles/12-chain.json
index 2d344eb..c1c823c 100644
--- a/tests/jsonfiles/12-chain.json
+++ b/tests/jsonfiles/12-chain.json
@@ -1 +1 @@
-{"nftables":[{"chain":{"name":"forward","handle":2,"bytes":0,"packets":0,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"forward","prio":0,"policy":"accept"}}]}
+{"nftables":[{"add":[{"chain":{"name":"forward","handle":2,"bytes":0,"packets":0,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"forward","prio":0,"policy":"accept"}}]}]}
diff --git a/tests/jsonfiles/13-chain.json b/tests/jsonfiles/13-chain.json
index 5a6ddd1..22940f9 100644
--- a/tests/jsonfiles/13-chain.json
+++ b/tests/jsonfiles/13-chain.json
@@ -1 +1 @@
-{"nftables":[{"chain":{"name":"output","handle":3,"bytes":454786,"packets":2681,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"output","prio":0,"policy":"accept"}}]}
+{"nftables":[{"add":[{"chain":{"name":"output","handle":3,"bytes":454786,"packets":2681,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"output","prio":0,"policy":"accept"}}]}]}
diff --git a/tests/jsonfiles/14-chain.json b/tests/jsonfiles/14-chain.json
index 6642615..0fb1948 100644
--- a/tests/jsonfiles/14-chain.json
+++ b/tests/jsonfiles/14-chain.json
@@ -1 +1 @@
-{"nftables":[{"chain":{"name":"chain1","handle":4,"bytes":0,"packets":0,"table":"filter","family":"ip","use":0}}]}
+{"nftables":[{"add":[{"chain":{"name":"chain1","handle":4,"bytes":0,"packets":0,"table":"filter","family":"ip","use":0}}]}]}
diff --git a/tests/jsonfiles/20-rule-bitwise.json b/tests/jsonfiles/20-rule-bitwise.json
index af53851..0e2e5c6 100644
--- a/tests/jsonfiles/20-rule-bitwise.json
+++ b/tests/jsonfiles/20-rule-bitwise.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":20,"expr":[{"type":"bitwise","sreg":1,"dreg":1,"len":4,"mask":{"reg":{"type":"value","len":4,"data0":"0x0000000a"}},"xor":{"reg":{"type":"value","len":4,"data0":"0x00000000"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":20,"expr":[{"type":"bitwise","sreg":1,"dreg":1,"len":4,"mask":{"reg":{"type":"value","len":4,"data0":"0x0000000a"}},"xor":{"reg":{"type":"value","len":4,"data0":"0x00000000"}}}]}}]}]}
diff --git a/tests/jsonfiles/21-rule-byteorder.json b/tests/jsonfiles/21-rule-byteorder.json
index ae6fb32..18a33f2 100644
--- a/tests/jsonfiles/21-rule-byteorder.json
+++ b/tests/jsonfiles/21-rule-byteorder.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":21,"expr":[{"type":"byteorder","sreg":3,"dreg":4,"op":"hton","len":4,"size":4}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":21,"expr":[{"type":"byteorder","sreg":3,"dreg":4,"op":"hton","len":4,"size":4}]}}]}]}
diff --git a/tests/jsonfiles/22-rule-cmp.json b/tests/jsonfiles/22-rule-cmp.json
index 2a6565a..c8f91ec 100644
--- a/tests/jsonfiles/22-rule-cmp.json
+++ b/tests/jsonfiles/22-rule-cmp.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"forward","handle":22,"expr":[{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":16,"data0":"0x00000000","data1":"0x6e6f6200","data2":"0x2e303164","data3":"0x00393331"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"forward","handle":22,"expr":[{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":16,"data0":"0x00000000","data1":"0x6e6f6200","data2":"0x2e303164","data3":"0x00393331"}}}]}}]}]}
diff --git a/tests/jsonfiles/23-rule-counter.json b/tests/jsonfiles/23-rule-counter.json
index 95d5072..30d7a06 100644
--- a/tests/jsonfiles/23-rule-counter.json
+++ b/tests/jsonfiles/23-rule-counter.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":23,"expr":[{"type":"counter","pkts":135,"bytes":21655}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":23,"expr":[{"type":"counter","pkts":135,"bytes":21655}]}}]}]}
diff --git a/tests/jsonfiles/24-rule-ct.json b/tests/jsonfiles/24-rule-ct.json
index 091d885..71783e8 100644
--- a/tests/jsonfiles/24-rule-ct.json
+++ b/tests/jsonfiles/24-rule-ct.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":24,"expr":[{"type":"ct","dreg":1,"key":"state"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000008"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":24,"expr":[{"type":"ct","dreg":1,"key":"state"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000008"}}}]}}]}]}
diff --git a/tests/jsonfiles/25-rule-exthdr.json b/tests/jsonfiles/25-rule-exthdr.json
index c40a074..0f6988c 100644
--- a/tests/jsonfiles/25-rule-exthdr.json
+++ b/tests/jsonfiles/25-rule-exthdr.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":25,"expr":[{"type":"exthdr","dreg":1,"exthdr_type":"mh","offset":2,"len":16}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":25,"expr":[{"type":"exthdr","dreg":1,"exthdr_type":"mh","offset":2,"len":16}]}}]}]}
diff --git a/tests/jsonfiles/26-rule-immediate.json b/tests/jsonfiles/26-rule-immediate.json
index 767052e..89cd360 100644
--- a/tests/jsonfiles/26-rule-immediate.json
+++ b/tests/jsonfiles/26-rule-immediate.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":26,"expr":[{"type":"immediate","dreg":0,"data":{"reg":{"type":"verdict","verdict":"accept"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":26,"expr":[{"type":"immediate","dreg":0,"data":{"reg":{"type":"verdict","verdict":"accept"}}}]}}]}]}
diff --git a/tests/jsonfiles/27-rule-limit.json b/tests/jsonfiles/27-rule-limit.json
index c268357..fdd5358 100644
--- a/tests/jsonfiles/27-rule-limit.json
+++ b/tests/jsonfiles/27-rule-limit.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":27,"expr":[{"type":"limit","rate":321321,"unit":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":27,"expr":[{"type":"limit","rate":321321,"unit":0}]}}]}]}
diff --git a/tests/jsonfiles/28-rule-log.json b/tests/jsonfiles/28-rule-log.json
index 1e739f1..320bf29 100644
--- a/tests/jsonfiles/28-rule-log.json
+++ b/tests/jsonfiles/28-rule-log.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":28,"expr":[{"type":"log","prefix":"test_chain","group":1,"snaplen":0,"qthreshold":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":28,"expr":[{"type":"log","prefix":"test_chain","group":1,"snaplen":0,"qthreshold":0}]}}]}]}
diff --git a/tests/jsonfiles/29-rule-match.json b/tests/jsonfiles/29-rule-match.json
index b533e77..577c63a 100644
--- a/tests/jsonfiles/29-rule-match.json
+++ b/tests/jsonfiles/29-rule-match.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":9,"expr":[{"type":"match","name":"state"},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":9,"expr":[{"type":"match","name":"state"},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/30-rule-lookup.json b/tests/jsonfiles/30-rule-lookup.json
index 05576b0..3eed063 100644
--- a/tests/jsonfiles/30-rule-lookup.json
+++ b/tests/jsonfiles/30-rule-lookup.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":8,"expr":[{"type":"payload","dreg":1,"offset":12,"len":4,"base":"network"},{"type":"lookup","set":"set0","sreg":1,"dreg":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":8,"expr":[{"type":"payload","dreg":1,"offset":12,"len":4,"base":"network"},{"type":"lookup","set":"set0","sreg":1,"dreg":0}]}}]}]}
diff --git a/tests/jsonfiles/31-rule-meta.json b/tests/jsonfiles/31-rule-meta.json
index d39a6ed..2c2d681 100644
--- a/tests/jsonfiles/31-rule-meta.json
+++ b/tests/jsonfiles/31-rule-meta.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":8,"expr":[{"type":"meta","dreg":1,"key":"protocol"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":8,"expr":[{"type":"meta","dreg":1,"key":"protocol"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/32-rule-nat4.json b/tests/jsonfiles/32-rule-nat4.json
index aa2b50d..4ac8c87 100644
--- a/tests/jsonfiles/32-rule-nat4.json
+++ b/tests/jsonfiles/32-rule-nat4.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":10,"expr":[{"type":"nat","nat_type":"dnat","family":"ip","sreg_addr_min":1,"sreg_addr_max":2,"sreg_proto_min":3,"sreg_proto_max":4}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":10,"expr":[{"type":"nat","nat_type":"dnat","family":"ip","sreg_addr_min":1,"sreg_addr_max":2,"sreg_proto_min":3,"sreg_proto_max":4}]}}]}]}
diff --git a/tests/jsonfiles/33-rule-nat6.json b/tests/jsonfiles/33-rule-nat6.json
index a76eb71..3e4b2e5 100644
--- a/tests/jsonfiles/33-rule-nat6.json
+++ b/tests/jsonfiles/33-rule-nat6.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip6","table":"nat","chain":"output","handle":33,"expr":[{"type":"nat","nat_type":"snat","family":"ip6","sreg_addr_min":1,"sreg_addr_max":2,"sreg_proto_min":3,"sreg_proto_max":4,"flags":12}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip6","table":"nat","chain":"output","handle":33,"expr":[{"type":"nat","nat_type":"snat","family":"ip6","sreg_addr_min":1,"sreg_addr_max":2,"sreg_proto_min":3,"sreg_proto_max":4,"flags":12}]}}]}]}
diff --git a/tests/jsonfiles/34-rule-payload.json b/tests/jsonfiles/34-rule-payload.json
index eec5dc9..127eb0c 100644
--- a/tests/jsonfiles/34-rule-payload.json
+++ b/tests/jsonfiles/34-rule-payload.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":26,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"network"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":2,"len":2,"base":"transport"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":2,"data0":"0x00001600"}}},{"type":"immediate","dreg":0,"data":{"reg":{"type":"verdict","verdict":"accept"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":26,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"network"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":2,"len":2,"base":"transport"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":2,"data0":"0x00001600"}}},{"type":"immediate","dreg":0,"data":{"reg":{"type":"verdict","verdict":"accept"}}}]}}]}]}
diff --git a/tests/jsonfiles/35-rule-target.json b/tests/jsonfiles/35-rule-target.json
index da02921..4ac72a8 100644
--- a/tests/jsonfiles/35-rule-target.json
+++ b/tests/jsonfiles/35-rule-target.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"INPUT","handle":20,"expr":[{"type":"counter","pkts":17,"bytes":4436},{"type":"target","name":"LOG"}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"INPUT","handle":20,"expr":[{"type":"counter","pkts":17,"bytes":4436},{"type":"target","name":"LOG"}]}}]}]}
diff --git a/tests/jsonfiles/36-rule-real.json b/tests/jsonfiles/36-rule-real.json
index 56d6794..65a0d62 100644
--- a/tests/jsonfiles/36-rule-real.json
+++ b/tests/jsonfiles/36-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":36,"expr":[{"type":"payload","dreg":1,"offset":12,"len":8,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":8,"data0":"0x0100a8c0","data1":"0x6400a8c0"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":36,"expr":[{"type":"payload","dreg":1,"offset":12,"len":8,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":8,"data0":"0x0100a8c0","data1":"0x6400a8c0"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/37-rule-real.json b/tests/jsonfiles/37-rule-real.json
index 6eb8c7d..cfd1c13 100644
--- a/tests/jsonfiles/37-rule-real.json
+++ b/tests/jsonfiles/37-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":37,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":37,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}}]}}]}]}
diff --git a/tests/jsonfiles/38-rule-real.json b/tests/jsonfiles/38-rule-real.json
index e0da26f..71123aa 100644
--- a/tests/jsonfiles/38-rule-real.json
+++ b/tests/jsonfiles/38-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":38,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":38,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}}]}}]}]}
diff --git a/tests/jsonfiles/39-rule-real.json b/tests/jsonfiles/39-rule-real.json
index b80bf42..7b05158 100644
--- a/tests/jsonfiles/39-rule-real.json
+++ b/tests/jsonfiles/39-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":39,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"gte","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"cmp","sreg":1,"op":"lte","data":{"reg":{"type":"value","len":4,"data0":"0xfa00a8c0"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":39,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"gte","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"cmp","sreg":1,"op":"lte","data":{"reg":{"type":"value","len":4,"data0":"0xfa00a8c0"}}}]}}]}]}
diff --git a/tests/jsonfiles/40-rule-real.json b/tests/jsonfiles/40-rule-real.json
index 38a8497..8b1259b 100644
--- a/tests/jsonfiles/40-rule-real.json
+++ b/tests/jsonfiles/40-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":40,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":40,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/41-rule-real.json b/tests/jsonfiles/41-rule-real.json
index 58bc9c0..675a95c 100644
--- a/tests/jsonfiles/41-rule-real.json
+++ b/tests/jsonfiles/41-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":41,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0},{"type":"immediate","dreg":0,"data":{"reg":{"type":"verdict","verdict":"drop"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":41,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0},{"type":"immediate","dreg":0,"data":{"reg":{"type":"verdict","verdict":"drop"}}}]}}]}]}
diff --git a/tests/jsonfiles/42-rule-real.json b/tests/jsonfiles/42-rule-real.json
index 025d351..b3d28d4 100644
--- a/tests/jsonfiles/42-rule-real.json
+++ b/tests/jsonfiles/42-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":42,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0},{"type":"log","prefix":"(null)","group":0,"snaplen":0,"qthreshold":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":42,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0},{"type":"log","prefix":"(null)","group":0,"snaplen":0,"qthreshold":0}]}}]}]}
diff --git a/tests/jsonfiles/43-rule-real.json b/tests/jsonfiles/43-rule-real.json
index ff4fedb..e0da9a8 100644
--- a/tests/jsonfiles/43-rule-real.json
+++ b/tests/jsonfiles/43-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":43,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":2,"len":2,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":2,"data0":"0x00001600"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":43,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":2,"len":2,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":2,"data0":"0x00001600"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/44-rule-real.json b/tests/jsonfiles/44-rule-real.json
index 14c6e34..50dd524 100644
--- a/tests/jsonfiles/44-rule-real.json
+++ b/tests/jsonfiles/44-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":44,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":0,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x16000004"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":44,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":0,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x16000004"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/45-rule-real.json b/tests/jsonfiles/45-rule-real.json
index 81841a7..be08598 100644
--- a/tests/jsonfiles/45-rule-real.json
+++ b/tests/jsonfiles/45-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":45,"expr":[{"type":"payload","dreg":1,"offset":12,"len":8,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":8,"data0":"0x0100a8c0","data1":"0x6400a8c0"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":45,"expr":[{"type":"payload","dreg":1,"offset":12,"len":8,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":8,"data0":"0x0100a8c0","data1":"0x6400a8c0"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/46-rule-real.json b/tests/jsonfiles/46-rule-real.json
index 3cfb290..5ea3ca9 100644
--- a/tests/jsonfiles/46-rule-real.json
+++ b/tests/jsonfiles/46-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":46,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":0,"len":8,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":8,"data0":"0x16000004","data1":"0x00000000"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":46,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":0,"len":8,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":8,"data0":"0x16000004","data1":"0x00000000"}}}]}}]}]}
diff --git a/tests/jsonfiles/47-rule-real.json b/tests/jsonfiles/47-rule-real.json
index e4160dd..6e9d084 100644
--- a/tests/jsonfiles/47-rule-real.json
+++ b/tests/jsonfiles/47-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":47,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":0,"len":8,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":8,"data0":"0x16000004","data1":"0x00000000"}}}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":47,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":0,"len":8,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":8,"data0":"0x16000004","data1":"0x00000000"}}}]}}]}]}
diff --git a/tests/jsonfiles/48-rule-real.json b/tests/jsonfiles/48-rule-real.json
index 95e2b38..6387e94 100644
--- a/tests/jsonfiles/48-rule-real.json
+++ b/tests/jsonfiles/48-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":48,"expr":[{"type":"meta","dreg":1,"key":"len"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":48,"expr":[{"type":"meta","dreg":1,"key":"len"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/49-rule-real.json b/tests/jsonfiles/49-rule-real.json
index da0c0c5..e1c84d2 100644
--- a/tests/jsonfiles/49-rule-real.json
+++ b/tests/jsonfiles/49-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":49,"expr":[{"type":"meta","dreg":1,"key":"mark"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000000"}}},{"type":"counter","pkts":55,"bytes":11407}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":49,"expr":[{"type":"meta","dreg":1,"key":"mark"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000000"}}},{"type":"counter","pkts":55,"bytes":11407}]}}]}]}
diff --git a/tests/jsonfiles/50-rule-real.json b/tests/jsonfiles/50-rule-real.json
index 4dbc553..21de049 100644
--- a/tests/jsonfiles/50-rule-real.json
+++ b/tests/jsonfiles/50-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":50,"expr":[{"type":"meta","dreg":1,"key":"iif"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000001"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":50,"expr":[{"type":"meta","dreg":1,"key":"iif"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000001"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/51-rule-real.json b/tests/jsonfiles/51-rule-real.json
index 7d510ab..46afd76 100644
--- a/tests/jsonfiles/51-rule-real.json
+++ b/tests/jsonfiles/51-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":51,"expr":[{"type":"meta","dreg":1,"key":"iifname"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":16,"data0":"0x00000000","data1":"0x00000000","data2":"0x65000000","data3":"0x00306874"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":51,"expr":[{"type":"meta","dreg":1,"key":"iifname"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":16,"data0":"0x00000000","data1":"0x00000000","data2":"0x65000000","data3":"0x00306874"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/52-rule-real.json b/tests/jsonfiles/52-rule-real.json
index 2809478..e64fe80 100644
--- a/tests/jsonfiles/52-rule-real.json
+++ b/tests/jsonfiles/52-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":52,"expr":[{"type":"meta","dreg":1,"key":"oif"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000001"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":52,"expr":[{"type":"meta","dreg":1,"key":"oif"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000001"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/53-rule-real.json b/tests/jsonfiles/53-rule-real.json
index 329945c..910a467 100644
--- a/tests/jsonfiles/53-rule-real.json
+++ b/tests/jsonfiles/53-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":53,"expr":[{"type":"meta","dreg":1,"key":"oifname"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":16,"data0":"0x00000000","data1":"0x00000000","data2":"0x65000000","data3":"0x00306874"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":53,"expr":[{"type":"meta","dreg":1,"key":"oifname"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":16,"data0":"0x00000000","data1":"0x00000000","data2":"0x65000000","data3":"0x00306874"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/54-rule-real.json b/tests/jsonfiles/54-rule-real.json
index b9b2383..f271d83 100644
--- a/tests/jsonfiles/54-rule-real.json
+++ b/tests/jsonfiles/54-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":54,"expr":[{"type":"meta","dreg":1,"key":"skuid"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":54,"expr":[{"type":"meta","dreg":1,"key":"skuid"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/55-rule-real.json b/tests/jsonfiles/55-rule-real.json
index e79552d..f36ebe5 100644
--- a/tests/jsonfiles/55-rule-real.json
+++ b/tests/jsonfiles/55-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":55,"expr":[{"type":"meta","dreg":1,"key":"skgid"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":55,"expr":[{"type":"meta","dreg":1,"key":"skgid"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/56-rule-real.json b/tests/jsonfiles/56-rule-real.json
index a2c4b9c..408b653 100644
--- a/tests/jsonfiles/56-rule-real.json
+++ b/tests/jsonfiles/56-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":56,"expr":[{"type":"meta","dreg":1,"key":"secmark"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000000"}}},{"type":"counter","pkts":55,"bytes":11407}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":56,"expr":[{"type":"meta","dreg":1,"key":"secmark"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000000"}}},{"type":"counter","pkts":55,"bytes":11407}]}}]}]}
diff --git a/tests/jsonfiles/57-rule-real.json b/tests/jsonfiles/57-rule-real.json
index b233f59..fc8a67a 100644
--- a/tests/jsonfiles/57-rule-real.json
+++ b/tests/jsonfiles/57-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":57,"expr":[{"type":"meta","dreg":1,"key":"len"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":57,"expr":[{"type":"meta","dreg":1,"key":"len"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/58-rule-real.json b/tests/jsonfiles/58-rule-real.json
index f98efd3..abb90d5 100644
--- a/tests/jsonfiles/58-rule-real.json
+++ b/tests/jsonfiles/58-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":58,"expr":[{"type":"meta","dreg":1,"key":"protocol"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":2,"data0":"0x00000008"}}},{"type":"counter","pkts":55,"bytes":11407}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":58,"expr":[{"type":"meta","dreg":1,"key":"protocol"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":2,"data0":"0x00000008"}}},{"type":"counter","pkts":55,"bytes":11407}]}}]}]}
diff --git a/tests/jsonfiles/59-rule-real.json b/tests/jsonfiles/59-rule-real.json
index 0f92ade..ffe2288 100644
--- a/tests/jsonfiles/59-rule-real.json
+++ b/tests/jsonfiles/59-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":59,"expr":[{"type":"meta","dreg":1,"key":"mark"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000000"}}},{"type":"counter","pkts":55,"bytes":11407}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":59,"expr":[{"type":"meta","dreg":1,"key":"mark"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000000"}}},{"type":"counter","pkts":55,"bytes":11407}]}}]}]}
diff --git a/tests/jsonfiles/60-rule-real.json b/tests/jsonfiles/60-rule-real.json
index 0a98726..9ec79cf 100644
--- a/tests/jsonfiles/60-rule-real.json
+++ b/tests/jsonfiles/60-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":60,"expr":[{"type":"meta","dreg":1,"key":"iif"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000001"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":60,"expr":[{"type":"meta","dreg":1,"key":"iif"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x00000001"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/61-rule-real.json b/tests/jsonfiles/61-rule-real.json
index 8b57338..fd35d2c 100644
--- a/tests/jsonfiles/61-rule-real.json
+++ b/tests/jsonfiles/61-rule-real.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":61,"expr":[{"type":"meta","dreg":1,"key":"iifname"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":16,"data0":"0x00000000","data1":"0x00000000","data2":"0x65000000","data3":"0x00306874"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":61,"expr":[{"type":"meta","dreg":1,"key":"iifname"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":16,"data0":"0x00000000","data1":"0x00000000","data2":"0x65000000","data3":"0x00306874"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/62-set.json b/tests/jsonfiles/62-set.json
index 58db88b..616eabe 100644
--- a/tests/jsonfiles/62-set.json
+++ b/tests/jsonfiles/62-set.json
@@ -1 +1 @@
-{"nftables":[{"set":{"name":"set0","table":"filter","flags":3,"family":"ip","key_type":12,"key_len":2,"set_elem":[{"flags":0,"key":{"reg":{"type":"value","len":2,"data0":"0x00001700"}}},{"flags":0,"key":{"reg":{"type":"value","len":2,"data0":"0x00001600"}}}]}}]}
+{"nftables":[{"add":[{"set":{"name":"set0","table":"filter","flags":3,"family":"ip","key_type":12,"key_len":2,"set_elem":[{"flags":0,"key":{"reg":{"type":"value","len":2,"data0":"0x00001700"}}},{"flags":0,"key":{"reg":{"type":"value","len":2,"data0":"0x00001600"}}}]}}]}]}
diff --git a/tests/jsonfiles/63-set.json b/tests/jsonfiles/63-set.json
index d63c1cf..02aa5fb 100644
--- a/tests/jsonfiles/63-set.json
+++ b/tests/jsonfiles/63-set.json
@@ -1 +1 @@
-{"nftables":[{"set":{"name":"map0","table":"f","flags":11,"family":"ip","key_type":12,"key_len":2,"data_type":4294967040,"data_len":16,"set_elem":[{"flags":0,"key":{"reg":{"type":"value","len":2,"data0":"0x00001700"}},"data":{"reg":{"type":"verdict","verdict":"goto","chain":"o"}}},{"flags":0,"key":{"reg":{"type":"value","len":2,"data0":"0x00001600"}},"data":{"reg":{"type":"verdict","verdict":"accept"}}}]}}]}
+{"nftables":[{"add":[{"set":{"name":"map0","table":"f","flags":11,"family":"ip","key_type":12,"key_len":2,"data_type":4294967040,"data_len":16,"set_elem":[{"flags":0,"key":{"reg":{"type":"value","len":2,"data0":"0x00001700"}},"data":{"reg":{"type":"verdict","verdict":"goto","chain":"o"}}},{"flags":0,"key":{"reg":{"type":"value","len":2,"data0":"0x00001600"}},"data":{"reg":{"type":"verdict","verdict":"accept"}}}]}}]}]}
diff --git a/tests/jsonfiles/64-ruleset.json b/tests/jsonfiles/64-ruleset.json
index 9b6e611..54e27c0 100644
--- a/tests/jsonfiles/64-ruleset.json
+++ b/tests/jsonfiles/64-ruleset.json
@@ -1 +1 @@
-{"nftables":[{"table":{"name":"filter","family":"ip","flags":0,"use":0}},{"table":{"name":"filter2","family":"ip6","flags":0,"use":0}},{"chain":{"name":"input","handle":1,"bytes":10681449,"packets":16216,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"input","prio":0,"policy":"accept"}},{"chain":{"name":"forward","handle":2,"bytes":0,"packets":0,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"forward","prio":0,"policy":"accept"}},{"chain":{"name":"output","handle":3,"bytes":2375830,"packets":15184,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"output","prio":0,"policy":"accept"}},{"chain":{"name":"chain1","handle":4,"bytes":0,"packets":0,"table":"filter","family":"ip","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,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0},{"type":"immediate","dreg":0,"data":{"reg":{"type":"verdict","verdict":"drop"}}}]}},{"rule":{"family":"ip","table":"filter","chain":"output","handle":9,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":2,"len":2,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":2,"data0":"0x00001600"}}},{"type":"counter","pk
ts":0,"bytes":0}]}},{"rule":{"family":"ip","table":"filter","chain":"output","handle":10,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0}]}},{"rule":{"family":"ip","table":"filter","chain":"output","handle":11,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0},{"type":"immediate","dreg":0,"data":{"reg":{"type":"verdict","verdict":"drop"}}}]}}]}
+{"nftables":[{"add":[{"table":{"name":"filter","family":"ip","flags":0,"use":0}},{"table":{"name":"filter2","family":"ip6","flags":0,"use":0}},{"chain":{"name":"input","handle":1,"bytes":10681449,"packets":16216,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"input","prio":0,"policy":"accept"}},{"chain":{"name":"forward","handle":2,"bytes":0,"packets":0,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"forward","prio":0,"policy":"accept"}},{"chain":{"name":"output","handle":3,"bytes":2375830,"packets":15184,"table":"filter","family":"ip","use":0,"type":"filter","hooknum":"output","prio":0,"policy":"accept"}},{"chain":{"name":"chain1","handle":4,"bytes":0,"packets":0,"table":"filter","family":"ip","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,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0},{"type":"immediate","dreg":0,"data":{"reg":{"type":"verdict","verdict":"drop"}}}]}},{"rule":{"family":"ip","table":"filter","chain":"output","handle":9,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":2,"len":2,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":2,"data0":"0x00001600"}}},{"type":"coun
ter","pkts":0,"bytes":0}]}},{"rule":{"family":"ip","table":"filter","chain":"output","handle":10,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0}]}},{"rule":{"family":"ip","table":"filter","chain":"output","handle":11,"expr":[{"type":"payload","dreg":1,"offset":16,"len":4,"base":"link"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x0100a8c0"}}},{"type":"counter","pkts":0,"bytes":0},{"type":"immediate","dreg":0,"data":{"reg":{"type":"verdict","verdict":"drop"}}}]}}]}]}
diff --git a/tests/jsonfiles/65-rule-meta-target.json b/tests/jsonfiles/65-rule-meta-target.json
index e6180d6..ab4e40c 100644
--- a/tests/jsonfiles/65-rule-meta-target.json
+++ b/tests/jsonfiles/65-rule-meta-target.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":1,"expr":[{"type":"meta","key":"mark","sreg":1},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"output","handle":1,"expr":[{"type":"meta","key":"mark","sreg":1},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":4,"data0":"0x000003e8"}}},{"type":"counter","pkts":0,"bytes":0}]}}]}]}
diff --git a/tests/jsonfiles/66-rule-queue.json b/tests/jsonfiles/66-rule-queue.json
index 016110e..69a2eee 100644
--- a/tests/jsonfiles/66-rule-queue.json
+++ b/tests/jsonfiles/66-rule-queue.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":43,"expr":[{"type":"queue","num":4,"total":2,"flags":0}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":43,"expr":[{"type":"queue","num":4,"total":2,"flags":0}]}}]}]}
diff --git a/tests/jsonfiles/67-rule-queue.json b/tests/jsonfiles/67-rule-queue.json
index 0fefd68..0471a98 100644
--- a/tests/jsonfiles/67-rule-queue.json
+++ b/tests/jsonfiles/67-rule-queue.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":44,"position":43,"expr":[{"type":"queue","num":4,"total":2,"flags":3}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":44,"position":43,"expr":[{"type":"queue","num":4,"total":2,"flags":3}]}}]}]}
diff --git a/tests/jsonfiles/68-rule-masq.json b/tests/jsonfiles/68-rule-masq.json
index 758f5ca..e8f2a58 100644
--- a/tests/jsonfiles/68-rule-masq.json
+++ b/tests/jsonfiles/68-rule-masq.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip6","table":"nat","chain":"postrouting","handle":4,"expr":[{"type":"masq","flags":12}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip6","table":"nat","chain":"postrouting","handle":4,"expr":[{"type":"masq","flags":12}]}}]}]}
diff --git a/tests/jsonfiles/69-rule-redir.json b/tests/jsonfiles/69-rule-redir.json
index 043fff2..d0318f0 100644
--- a/tests/jsonfiles/69-rule-redir.json
+++ b/tests/jsonfiles/69-rule-redir.json
@@ -1 +1 @@
-{"nftables":[{"rule":{"family":"ip6","table":"nat","chain":"prerouting","handle":4,"expr":[{"type":"redir"}]}}]}
+{"nftables":[{"add":[{"rule":{"family":"ip6","table":"nat","chain":"prerouting","handle":4,"expr":[{"type":"redir"}]}}]}]}
diff --git a/tests/jsonfiles/70-rule-real.json b/tests/jsonfiles/70-rule-real.json
new file mode 100644
index 0000000..b38947f
--- /dev/null
+++ b/tests/jsonfiles/70-rule-real.json
@@ -0,0 +1 @@
+{"nftables":[{"add":[{"rule":{"family":"ip","table":"filter","chain":"input","handle":2,"expr":[{"type":"payload","dreg":1,"offset":9,"len":1,"base":"network"},{"type":"cmp","sreg":1,"op":"eq","data":{"reg":{"type":"value","len":1,"data0":"0x00000006"}}},{"type":"payload","dreg":1,"offset":2,"len":2,"base":"transport"},{"type":"cmp","sreg":1,"op":"eq","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 57a5730..76e89ac 100644
--- a/tests/nft-parsing-test.c
+++ b/tests/nft-parsing-test.c
@@ -68,11 +68,11 @@ static int compare_test(uint32_t type, struct nft_ruleset *rs,
switch (type) {
case TEST_XML_RULESET:
nft_ruleset_snprintf(out, sizeof(out), rs,
- NFT_OUTPUT_XML, 0);
+ NFT_OUTPUT_XML, NFT_OF_EVENT_NEW);
break;
case TEST_JSON_RULESET:
nft_ruleset_snprintf(out, sizeof(out), rs,
- NFT_OUTPUT_JSON, 0);
+ NFT_OUTPUT_JSON, NFT_OF_EVENT_NEW);
break;
default:
errno = EINVAL;
diff --git a/tests/xmlfiles/01-table.xml b/tests/xmlfiles/01-table.xml
index 655b544..c83c4e2 100644
--- a/tests/xmlfiles/01-table.xml
+++ b/tests/xmlfiles/01-table.xml
@@ -1 +1 @@
-<nftables><table><name>filter</name><family>ip</family><flags>0</flags><use>0</use></table></nftables>
+<nftables><add><table><name>filter</name><family>ip</family><flags>0</flags><use>0</use></table></add></nftables>
diff --git a/tests/xmlfiles/02-table.xml b/tests/xmlfiles/02-table.xml
index 5de924f..99dc6df 100644
--- a/tests/xmlfiles/02-table.xml
+++ b/tests/xmlfiles/02-table.xml
@@ -1 +1 @@
-<nftables><table><name>nat</name><family>ip6</family><flags>0</flags><use>0</use></table></nftables>
+<nftables><add><table><name>nat</name><family>ip6</family><flags>0</flags><use>0</use></table></add></nftables>
diff --git a/tests/xmlfiles/10-chain.xml b/tests/xmlfiles/10-chain.xml
index 6ac54ab..8ae807b 100644
--- a/tests/xmlfiles/10-chain.xml
+++ b/tests/xmlfiles/10-chain.xml
@@ -1 +1 @@
-<nftables><chain><name>test</name><handle>0</handle><bytes>0</bytes><packets>0</packets><table>filter</table><family>ip</family><type>filter</type><hooknum>input</hooknum><prio>0</prio><policy>accept</policy></chain></nftables>
+<nftables><add><chain><name>test</name><handle>0</handle><bytes>0</bytes><packets>0</packets><table>filter</table><family>ip</family><type>filter</type><hooknum>input</hooknum><prio>0</prio><policy>accept</policy></chain></add></nftables>
diff --git a/tests/xmlfiles/11-chain.xml b/tests/xmlfiles/11-chain.xml
index 34d99dd..d48fa40 100644
--- a/tests/xmlfiles/11-chain.xml
+++ b/tests/xmlfiles/11-chain.xml
@@ -1 +1 @@
-<nftables><chain><name>test</name><handle>0</handle><bytes>59</bytes><packets>1</packets><table>filter</table><family>ip6</family><type>filter</type><hooknum>forward</hooknum><prio>0</prio><policy>drop</policy></chain></nftables>
+<nftables><add><chain><name>test</name><handle>0</handle><bytes>59</bytes><packets>1</packets><table>filter</table><family>ip6</family><type>filter</type><hooknum>forward</hooknum><prio>0</prio><policy>drop</policy></chain></add></nftables>
diff --git a/tests/xmlfiles/12-chain.xml b/tests/xmlfiles/12-chain.xml
index f53f252..448feb2 100644
--- a/tests/xmlfiles/12-chain.xml
+++ b/tests/xmlfiles/12-chain.xml
@@ -1 +1 @@
-<nftables><chain><name>foo</name><handle>100</handle><bytes>59264154979</bytes><packets>2548796325</packets><table>nat</table><family>ip</family><type>nat</type><hooknum>postrouting</hooknum><prio>0</prio><policy>accept</policy></chain></nftables>
+<nftables><add><chain><name>foo</name><handle>100</handle><bytes>59264154979</bytes><packets>2548796325</packets><table>nat</table><family>ip</family><type>nat</type><hooknum>postrouting</hooknum><prio>0</prio><policy>accept</policy></chain></add></nftables>
diff --git a/tests/xmlfiles/20-rule-bitwise.xml b/tests/xmlfiles/20-rule-bitwise.xml
index 9b38437..69ff702 100644
--- a/tests/xmlfiles/20-rule-bitwise.xml
+++ b/tests/xmlfiles/20-rule-bitwise.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="bitwise"><sreg>1</sreg><dreg>1</dreg><len>4</len><mask><reg type="value"><len>4</len><data0>0x0000000a</data0></reg></mask><xor><reg type="value"><len>4</len><data0>0x00000000</data0></reg></xor></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="bitwise"><sreg>1</sreg><dreg>1</dreg><len>4</len><mask><reg type="value"><len>4</len><data0>0x0000000a</data0></reg></mask><xor><reg type="value"><len>4</len><data0>0x00000000</data0></reg></xor></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/21-rule-byteorder.xml b/tests/xmlfiles/21-rule-byteorder.xml
index fce4ed1..2c30ad7 100644
--- a/tests/xmlfiles/21-rule-byteorder.xml
+++ b/tests/xmlfiles/21-rule-byteorder.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>test</table><chain>test</chain><handle>1000</handle><expr type="byteorder"><sreg>3</sreg><dreg>4</dreg><op>hton</op><len>4</len><size>4</size></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>test</table><chain>test</chain><handle>1000</handle><expr type="byteorder"><sreg>3</sreg><dreg>4</dreg><op>hton</op><len>4</len><size>4</size></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/22-rule-cmp.xml b/tests/xmlfiles/22-rule-cmp.xml
index 4e66993..103b7da 100644
--- a/tests/xmlfiles/22-rule-cmp.xml
+++ b/tests/xmlfiles/22-rule-cmp.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>36</handle><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x6e6f6200</data1><data2>0x2e303164</data2><data3>0x00393331</data3></reg></data></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>36</handle><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x6e6f6200</data1><data2>0x2e303164</data2><data3>0x00393331</data3></reg></data></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/23-rule-counter.xml b/tests/xmlfiles/23-rule-counter.xml
index 6299e84..c6708c6 100644
--- a/tests/xmlfiles/23-rule-counter.xml
+++ b/tests/xmlfiles/23-rule-counter.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>39</handle><expr type="counter"><pkts>3</pkts><bytes>177</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>39</handle><expr type="counter"><pkts>3</pkts><bytes>177</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/24-rule-ct.xml b/tests/xmlfiles/24-rule-ct.xml
index 1939e43..832ae2f 100644
--- a/tests/xmlfiles/24-rule-ct.xml
+++ b/tests/xmlfiles/24-rule-ct.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="ct"><dreg>1</dreg><key>state</key><dir>original</dir></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="ct"><dreg>1</dreg><key>state</key><dir>original</dir></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/25-rule-exthdr.xml b/tests/xmlfiles/25-rule-exthdr.xml
index 9e10a88..483d269 100644
--- a/tests/xmlfiles/25-rule-exthdr.xml
+++ b/tests/xmlfiles/25-rule-exthdr.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="exthdr"><dreg>1</dreg><exthdr_type>mh</exthdr_type><offset>2</offset><len>16</len></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="exthdr"><dreg>1</dreg><exthdr_type>mh</exthdr_type><offset>2</offset><len>16</len></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/26-rule-immediate.xml b/tests/xmlfiles/26-rule-immediate.xml
index 3eed652..fa5b3e7 100644
--- a/tests/xmlfiles/26-rule-immediate.xml
+++ b/tests/xmlfiles/26-rule-immediate.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>input</chain><handle>32</handle><expr type="immediate"><dreg>0</dreg><data><reg type="verdict"><verdict>accept</verdict></reg></data></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>input</chain><handle>32</handle><expr type="immediate"><dreg>0</dreg><data><reg type="verdict"><verdict>accept</verdict></reg></data></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/27-rule-limit.xml b/tests/xmlfiles/27-rule-limit.xml
index a4398e8..e186204 100644
--- a/tests/xmlfiles/27-rule-limit.xml
+++ b/tests/xmlfiles/27-rule-limit.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="limit"><rate>123123</rate><unit>321321</unit></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="limit"><rate>123123</rate><unit>321321</unit></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/28-rule-log.xml b/tests/xmlfiles/28-rule-log.xml
index c3e3b11..9a11bbb 100644
--- a/tests/xmlfiles/28-rule-log.xml
+++ b/tests/xmlfiles/28-rule-log.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>96</handle><expr type="log"><prefix>test_chain</prefix><group>1</group></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>96</handle><expr type="log"><prefix>test_chain</prefix><group>1</group></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/29-rule-lookup.xml b/tests/xmlfiles/29-rule-lookup.xml
index 052b008..2074908 100644
--- a/tests/xmlfiles/29-rule-lookup.xml
+++ b/tests/xmlfiles/29-rule-lookup.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>37</handle><expr type="lookup"><set>set0</set><sreg>1</sreg><dreg>0</dreg></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>37</handle><expr type="lookup"><set>set0</set><sreg>1</sreg><dreg>0</dreg></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/30-rule-match.xml b/tests/xmlfiles/30-rule-match.xml
index 4cfe33b..094f913 100644
--- a/tests/xmlfiles/30-rule-match.xml
+++ b/tests/xmlfiles/30-rule-match.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="match"><name>state</name></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="match"><name>state</name></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/31-rule-meta.xml b/tests/xmlfiles/31-rule-meta.xml
index a1c9e8c..1b9e1fa 100644
--- a/tests/xmlfiles/31-rule-meta.xml
+++ b/tests/xmlfiles/31-rule-meta.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>36</handle><expr type="meta"><dreg>1</dreg><key>iifname</key></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>36</handle><expr type="meta"><dreg>1</dreg><key>iifname</key></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/32-rule-nat6.xml b/tests/xmlfiles/32-rule-nat6.xml
index 8fc52e8..5a71148 100644
--- a/tests/xmlfiles/32-rule-nat6.xml
+++ b/tests/xmlfiles/32-rule-nat6.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>nat</table><chain>OUTPUT</chain><handle>100</handle><expr type="nat"><nat_type>snat</nat_type><family>ip6</family><sreg_addr_min>1</sreg_addr_min><sreg_addr_max>2</sreg_addr_max><sreg_proto_min>3</sreg_proto_min><sreg_proto_max>4</sreg_proto_max></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>nat</table><chain>OUTPUT</chain><handle>100</handle><expr type="nat"><nat_type>snat</nat_type><family>ip6</family><sreg_addr_min>1</sreg_addr_min><sreg_addr_max>2</sreg_addr_max><sreg_proto_min>3</sreg_proto_min><sreg_proto_max>4</sreg_proto_max></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/33-rule-nat4.xml b/tests/xmlfiles/33-rule-nat4.xml
index b7b6cb6..c55618b 100644
--- a/tests/xmlfiles/33-rule-nat4.xml
+++ b/tests/xmlfiles/33-rule-nat4.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="nat"><nat_type>dnat</nat_type><family>ip</family><sreg_addr_min>1</sreg_addr_min><sreg_addr_max>2</sreg_addr_max><sreg_proto_min>3</sreg_proto_min><sreg_proto_max>4</sreg_proto_max><flags>12</flags></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="nat"><nat_type>dnat</nat_type><family>ip</family><sreg_addr_min>1</sreg_addr_min><sreg_addr_max>2</sreg_addr_max><sreg_proto_min>3</sreg_proto_min><sreg_proto_max>4</sreg_proto_max><flags>12</flags></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/34-rule-payload.xml b/tests/xmlfiles/34-rule-payload.xml
index a6faca9..34084d0 100644
--- a/tests/xmlfiles/34-rule-payload.xml
+++ b/tests/xmlfiles/34-rule-payload.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>34</handle><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>34</handle><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/35-rule-target.xml b/tests/xmlfiles/35-rule-target.xml
index 5b46350..8bcfeb0 100644
--- a/tests/xmlfiles/35-rule-target.xml
+++ b/tests/xmlfiles/35-rule-target.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="target"><name>LOG</name></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>100</handle><expr type="target"><name>LOG</name></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/36-rule-real.xml b/tests/xmlfiles/36-rule-real.xml
index 6ebc8ea..54db02b 100644
--- a/tests/xmlfiles/36-rule-real.xml
+++ b/tests/xmlfiles/36-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>22</handle><expr type="payload"><dreg>1</dreg><offset>12</offset><len>8</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>8</len><data0>0x0100a8c0</data0><data1>0x6400a8c0</data1></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>22</handle><expr type="payload"><dreg>1</dreg><offset>12</offset><len>8</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>8</len><data0>0x0100a8c0</data0><data1>0x6400a8c0</data1></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/37-rule-real.xml b/tests/xmlfiles/37-rule-real.xml
index 8506f19..97882b9 100644
--- a/tests/xmlfiles/37-rule-real.xml
+++ b/tests/xmlfiles/37-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>25</handle><expr type="meta"><dreg>1</dreg><key>iifname</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x00000000</data1><data2>0x65000000</data2><data3>0x00306874</data3></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x00001600</data0></reg></data></expr><expr type="ct">
<dreg>1</dreg><key>state</key><dir>original</dir></expr><expr type="bitwise"><sreg>1</sreg><dreg>1</dreg><len>4</len><mask><reg type="value"><len>4</len><data0>0x0000000a</data0></reg></mask><xor><reg type="value"><len>4</len><data0>0x00000000</data0></reg></xor></expr><expr type="cmp"><sreg>1</sreg><op>neq</op><data><reg type="value"><len>4</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="log"><prefix>testprefix</prefix><group>1</group></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>25</handle><expr type="meta"><dreg>1</dreg><key>iifname</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x00000000</data1><data2>0x65000000</data2><data3>0x00306874</data3></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x00001600</data0></reg></data></expr><expr type=
"ct"><dreg>1</dreg><key>state</key><dir>original</dir></expr><expr type="bitwise"><sreg>1</sreg><dreg>1</dreg><len>4</len><mask><reg type="value"><len>4</len><data0>0x0000000a</data0></reg></mask><xor><reg type="value"><len>4</len><data0>0x00000000</data0></reg></xor></expr><expr type="cmp"><sreg>1</sreg><op>neq</op><data><reg type="value"><len>4</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="log"><prefix>testprefix</prefix><group>1</group></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/38-rule-real.xml b/tests/xmlfiles/38-rule-real.xml
index 51b1130..49ab7b5 100644
--- a/tests/xmlfiles/38-rule-real.xml
+++ b/tests/xmlfiles/38-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>30</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="lookup"><set>set3</set><sreg>1</sreg><dreg>0</dreg></expr><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x0000bb01</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="immediate"><dreg>0</dreg><data><reg typ
e="verdict"><verdict>accept</verdict></reg></data></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>INPUT</chain><handle>30</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="lookup"><set>set3</set><sreg>1</sreg><dreg>0</dreg></expr><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x0000bb01</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="immediate"><dreg>0</dreg><data><re
g type="verdict"><verdict>accept</verdict></reg></data></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/39-rule-real.xml b/tests/xmlfiles/39-rule-real.xml
index 35b6004..fa63b28 100644
--- a/tests/xmlfiles/39-rule-real.xml
+++ b/tests/xmlfiles/39-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>31</handle><expr type="meta"><dreg>1</dreg><key>iifname</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x00000000</data1><data2>0x6f620000</data2><data3>0x0030646e</data3></reg></data></expr><expr type="meta"><dreg>1</dreg><key>oifname</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x62000000</data1><data2>0x31646e6f</data2><data3>0x0037322e</data3></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>8</offset><len>16</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0xc09a002a</d
ata0><data1>0x2700cac1</data1><data2>0x00000000</data2><data3>0x50010000</data3></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>6</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000011</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x00003500</data0></reg></data></expr><expr type="ct"><dreg>1</dreg><key>status</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000001</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="
log"><prefix>dns_drop</prefix><group>2</group></expr><expr type="immediate"><dreg>0</dreg><data><reg type="verdict"><verdict>drop</verdict></reg></data></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>31</handle><expr type="meta"><dreg>1</dreg><key>iifname</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x00000000</data1><data2>0x6f620000</data2><data3>0x0030646e</data3></reg></data></expr><expr type="meta"><dreg>1</dreg><key>oifname</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x62000000</data1><data2>0x31646e6f</data2><data3>0x0037322e</data3></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>8</offset><len>16</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0xc09a00
2a</data0><data1>0x2700cac1</data1><data2>0x00000000</data2><data3>0x50010000</data3></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>6</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000011</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x00003500</data0></reg></data></expr><expr type="ct"><dreg>1</dreg><key>status</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000001</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr t
ype="log"><prefix>dns_drop</prefix><group>2</group></expr><expr type="immediate"><dreg>0</dreg><data><reg type="verdict"><verdict>drop</verdict></reg></data></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/40-rule-real.xml b/tests/xmlfiles/40-rule-real.xml
index d516164..55f195b 100644
--- a/tests/xmlfiles/40-rule-real.xml
+++ b/tests/xmlfiles/40-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>2</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></data></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>2</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></data></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/41-rule-real.xml b/tests/xmlfiles/41-rule-real.xml
index 3736404..87dc713 100644
--- a/tests/xmlfiles/41-rule-real.xml
+++ b/tests/xmlfiles/41-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>3</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>gte</op><data><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></data></expr><expr type="cmp"><sreg>1</sreg><op>lte</op><data><reg type="value"><len>4</len><data0>0xfa00a8c0</data0></reg></data></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>3</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>gte</op><data><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></data></expr><expr type="cmp"><sreg>1</sreg><op>lte</op><data><reg type="value"><len>4</len><data0>0xfa00a8c0</data0></reg></data></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/42-rule-real.xml b/tests/xmlfiles/42-rule-real.xml
index e88d695..7f66ab8 100644
--- a/tests/xmlfiles/42-rule-real.xml
+++ b/tests/xmlfiles/42-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>4</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>4</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/43-rule-real.xml b/tests/xmlfiles/43-rule-real.xml
index 4f6219f..fd9ac33 100644
--- a/tests/xmlfiles/43-rule-real.xml
+++ b/tests/xmlfiles/43-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>5</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="immediate"><dreg>0</dreg><data><reg type="verdict"><verdict>drop</verdict></reg></data></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>5</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="immediate"><dreg>0</dreg><data><reg type="verdict"><verdict>drop</verdict></reg></data></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/44-rule-real.xml b/tests/xmlfiles/44-rule-real.xml
index 4f12c01..ce026d0 100644
--- a/tests/xmlfiles/44-rule-real.xml
+++ b/tests/xmlfiles/44-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>6</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="log"></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>6</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="log"></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/45-rule-real.xml b/tests/xmlfiles/45-rule-real.xml
index fd2fda2..0448be6 100644
--- a/tests/xmlfiles/45-rule-real.xml
+++ b/tests/xmlfiles/45-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>7</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x00001600</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>7</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x00001600</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/46-rule-real.xml b/tests/xmlfiles/46-rule-real.xml
index 58ebedc..973df55 100644
--- a/tests/xmlfiles/46-rule-real.xml
+++ b/tests/xmlfiles/46-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>8</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>0</offset><len>4</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x16000004</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>8</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>0</offset><len>4</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x16000004</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/47-rule-real.xml b/tests/xmlfiles/47-rule-real.xml
index 44f2a68..0d35fd9 100644
--- a/tests/xmlfiles/47-rule-real.xml
+++ b/tests/xmlfiles/47-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>9</handle><expr type="payload"><dreg>1</dreg><offset>12</offset><len>8</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>8</len><data0>0x0100a8c0</data0><data1>0x6400a8c0</data1></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>9</handle><expr type="payload"><dreg>1</dreg><offset>12</offset><len>8</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>8</len><data0>0x0100a8c0</data0><data1>0x6400a8c0</data1></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/48-rule-real.xml b/tests/xmlfiles/48-rule-real.xml
index 7f8562f..ebcd219 100644
--- a/tests/xmlfiles/48-rule-real.xml
+++ b/tests/xmlfiles/48-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>10</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>0</offset><len>8</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>8</len><data0>0x16000004</data0><data1>0x00000000</data1></reg></data></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>10</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>0</offset><len>8</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>8</len><data0>0x16000004</data0><data1>0x00000000</data1></reg></data></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/49-rule-real.xml b/tests/xmlfiles/49-rule-real.xml
index ce73edc..5fdf0d8 100644
--- a/tests/xmlfiles/49-rule-real.xml
+++ b/tests/xmlfiles/49-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>11</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>0</offset><len>8</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>8</len><data0>0x16000004</data0><data1>0x00000000</data1></reg></data></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>11</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>0</offset><len>8</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>8</len><data0>0x16000004</data0><data1>0x00000000</data1></reg></data></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/50-rule-real.xml b/tests/xmlfiles/50-rule-real.xml
index 0df5270..0ac1ac0 100644
--- a/tests/xmlfiles/50-rule-real.xml
+++ b/tests/xmlfiles/50-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>12</handle><expr type="ct"><dreg>1</dreg><key>state</key><dir>original</dir></expr><expr type="bitwise"><sreg>1</sreg><dreg>1</dreg><len>4</len><mask><reg type="value"><len>4</len><data0>0x0000000a</data0></reg></mask><xor><reg type="value"><len>4</len><data0>0x00000000</data0></reg></xor></expr><expr type="cmp"><sreg>1</sreg><op>neq</op><data><reg type="value"><len>4</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>55</pkts><bytes>11407</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>12</handle><expr type="ct"><dreg>1</dreg><key>state</key><dir>original</dir></expr><expr type="bitwise"><sreg>1</sreg><dreg>1</dreg><len>4</len><mask><reg type="value"><len>4</len><data0>0x0000000a</data0></reg></mask><xor><reg type="value"><len>4</len><data0>0x00000000</data0></reg></xor></expr><expr type="cmp"><sreg>1</sreg><op>neq</op><data><reg type="value"><len>4</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>55</pkts><bytes>11407</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/51-rule-real.xml b/tests/xmlfiles/51-rule-real.xml
index f00ee71..3976636 100644
--- a/tests/xmlfiles/51-rule-real.xml
+++ b/tests/xmlfiles/51-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>13</handle><expr type="ct"><dreg>1</dreg><key>direction</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>5</pkts><bytes>160</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>13</handle><expr type="ct"><dreg>1</dreg><key>direction</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>5</pkts><bytes>160</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/52-rule-real.xml b/tests/xmlfiles/52-rule-real.xml
index 0205214..889415a 100644
--- a/tests/xmlfiles/52-rule-real.xml
+++ b/tests/xmlfiles/52-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>14</handle><expr type="ct"><dreg>1</dreg><key>direction</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000001</data0></reg></data></expr><expr type="counter"><pkts>50</pkts><bytes>11247</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>14</handle><expr type="ct"><dreg>1</dreg><key>direction</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000001</data0></reg></data></expr><expr type="counter"><pkts>50</pkts><bytes>11247</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/53-rule-real.xml b/tests/xmlfiles/53-rule-real.xml
index cfdac8f..a484270 100644
--- a/tests/xmlfiles/53-rule-real.xml
+++ b/tests/xmlfiles/53-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>15</handle><expr type="ct"><dreg>1</dreg><key>status</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000001</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>15</handle><expr type="ct"><dreg>1</dreg><key>status</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000001</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/54-rule-real.xml b/tests/xmlfiles/54-rule-real.xml
index 7451d5f..6859ac2 100644
--- a/tests/xmlfiles/54-rule-real.xml
+++ b/tests/xmlfiles/54-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>16</handle><expr type="ct"><dreg>1</dreg><key>mark</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000064</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>16</handle><expr type="ct"><dreg>1</dreg><key>mark</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000064</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/55-rule-real.xml b/tests/xmlfiles/55-rule-real.xml
index c35eef0..8ee83e4 100644
--- a/tests/xmlfiles/55-rule-real.xml
+++ b/tests/xmlfiles/55-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>17</handle><expr type="ct"><dreg>1</dreg><key>secmark</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>55</pkts><bytes>11407</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>17</handle><expr type="ct"><dreg>1</dreg><key>secmark</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>55</pkts><bytes>11407</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/56-rule-real.xml b/tests/xmlfiles/56-rule-real.xml
index 3f30f5e..be0b868 100644
--- a/tests/xmlfiles/56-rule-real.xml
+++ b/tests/xmlfiles/56-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>18</handle><expr type="ct"><dreg>1</dreg><key>expiration</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x0000001e</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>18</handle><expr type="ct"><dreg>1</dreg><key>expiration</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x0000001e</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/57-rule-real.xml b/tests/xmlfiles/57-rule-real.xml
index 1c5f4c0..7a1d68f 100644
--- a/tests/xmlfiles/57-rule-real.xml
+++ b/tests/xmlfiles/57-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>19</handle><expr type="ct"><dreg>1</dreg><key>helper</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00707466</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>19</handle><expr type="ct"><dreg>1</dreg><key>helper</key><dir>original</dir></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00707466</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/58-rule-real.xml b/tests/xmlfiles/58-rule-real.xml
index 14787f6..d32ed8f 100644
--- a/tests/xmlfiles/58-rule-real.xml
+++ b/tests/xmlfiles/58-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>20</handle><expr type="meta"><dreg>1</dreg><key>len</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x000003e8</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>20</handle><expr type="meta"><dreg>1</dreg><key>len</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x000003e8</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/59-rule-real.xml b/tests/xmlfiles/59-rule-real.xml
index 92ad83f..dc86dfb 100644
--- a/tests/xmlfiles/59-rule-real.xml
+++ b/tests/xmlfiles/59-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>21</handle><expr type="meta"><dreg>1</dreg><key>protocol</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x00000008</data0></reg></data></expr><expr type="counter"><pkts>55</pkts><bytes>11407</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>21</handle><expr type="meta"><dreg>1</dreg><key>protocol</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x00000008</data0></reg></data></expr><expr type="counter"><pkts>55</pkts><bytes>11407</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/60-rule-real.xml b/tests/xmlfiles/60-rule-real.xml
index 70234a6..e5938e3 100644
--- a/tests/xmlfiles/60-rule-real.xml
+++ b/tests/xmlfiles/60-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>22</handle><expr type="meta"><dreg>1</dreg><key>mark</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>55</pkts><bytes>11407</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>22</handle><expr type="meta"><dreg>1</dreg><key>mark</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>55</pkts><bytes>11407</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/61-rule-real.xml b/tests/xmlfiles/61-rule-real.xml
index 786e84a..0c097b9 100644
--- a/tests/xmlfiles/61-rule-real.xml
+++ b/tests/xmlfiles/61-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>23</handle><expr type="meta"><dreg>1</dreg><key>iif</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000001</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>23</handle><expr type="meta"><dreg>1</dreg><key>iif</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000001</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/62-rule-real.xml b/tests/xmlfiles/62-rule-real.xml
index 86d3499..387a4f1 100644
--- a/tests/xmlfiles/62-rule-real.xml
+++ b/tests/xmlfiles/62-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>24</handle><expr type="meta"><dreg>1</dreg><key>iifname</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x00000000</data1><data2>0x65000000</data2><data3>0x00306874</data3></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>24</handle><expr type="meta"><dreg>1</dreg><key>iifname</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x00000000</data1><data2>0x65000000</data2><data3>0x00306874</data3></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/63-rule-real.xml b/tests/xmlfiles/63-rule-real.xml
index af0b87d..1f6aa92 100644
--- a/tests/xmlfiles/63-rule-real.xml
+++ b/tests/xmlfiles/63-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>25</handle><expr type="meta"><dreg>1</dreg><key>oif</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000001</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>25</handle><expr type="meta"><dreg>1</dreg><key>oif</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000001</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/64-rule-real.xml b/tests/xmlfiles/64-rule-real.xml
index f14c95c..68f9d8a 100644
--- a/tests/xmlfiles/64-rule-real.xml
+++ b/tests/xmlfiles/64-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>26</handle><expr type="meta"><dreg>1</dreg><key>oifname</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x00000000</data1><data2>0x65000000</data2><data3>0x00306874</data3></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>26</handle><expr type="meta"><dreg>1</dreg><key>oifname</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>16</len><data0>0x00000000</data0><data1>0x00000000</data1><data2>0x65000000</data2><data3>0x00306874</data3></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/65-rule-real.xml b/tests/xmlfiles/65-rule-real.xml
index b3d37b1..6eeb3d0 100644
--- a/tests/xmlfiles/65-rule-real.xml
+++ b/tests/xmlfiles/65-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>27</handle><expr type="meta"><dreg>1</dreg><key>skuid</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x000003e8</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>27</handle><expr type="meta"><dreg>1</dreg><key>skuid</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x000003e8</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/66-rule-real.xml b/tests/xmlfiles/66-rule-real.xml
index 95ea4cd..1294a85 100644
--- a/tests/xmlfiles/66-rule-real.xml
+++ b/tests/xmlfiles/66-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>28</handle><expr type="meta"><dreg>1</dreg><key>skgid</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x000003e8</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>28</handle><expr type="meta"><dreg>1</dreg><key>skgid</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x000003e8</data0></reg></data></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/67-rule-real.xml b/tests/xmlfiles/67-rule-real.xml
index 197be85..162d084 100644
--- a/tests/xmlfiles/67-rule-real.xml
+++ b/tests/xmlfiles/67-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>29</handle><expr type="meta"><dreg>1</dreg><key>secmark</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>55</pkts><bytes>11407</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>29</handle><expr type="meta"><dreg>1</dreg><key>secmark</key></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>4</len><data0>0x00000000</data0></reg></data></expr><expr type="counter"><pkts>55</pkts><bytes>11407</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/68-rule-real.xml b/tests/xmlfiles/68-rule-real.xml
index a09698d..08371f9 100644
--- a/tests/xmlfiles/68-rule-real.xml
+++ b/tests/xmlfiles/68-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>32</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="lookup"><set>set0</set><sreg>1</sreg><dreg>0</dreg></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>32</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="lookup"><set>set0</set><sreg>1</sreg><dreg>0</dreg></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/69-rule-real.xml b/tests/xmlfiles/69-rule-real.xml
index e39e2cb..62119cd 100644
--- a/tests/xmlfiles/69-rule-real.xml
+++ b/tests/xmlfiles/69-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>33</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="lookup"><set>set1</set><sreg>1</sreg><dreg>0</dreg></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>33</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="lookup"><set>set1</set><sreg>1</sreg><dreg>0</dreg></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/70-rule-real.xml b/tests/xmlfiles/70-rule-real.xml
index d35a252..436c517 100644
--- a/tests/xmlfiles/70-rule-real.xml
+++ b/tests/xmlfiles/70-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>34</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="lookup"><set>map0</set><sreg>1</sreg><dreg>0</dreg></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>34</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="lookup"><set>map0</set><sreg>1</sreg><dreg>0</dreg></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/71-rule-real.xml b/tests/xmlfiles/71-rule-real.xml
index c838a60..bb50afd 100644
--- a/tests/xmlfiles/71-rule-real.xml
+++ b/tests/xmlfiles/71-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>35</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="lookup"><set>map1</set><sreg>1</sreg><dreg>0</dreg></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>35</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="lookup"><set>map1</set><sreg>1</sreg><dreg>0</dreg></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/72-rule-real.xml b/tests/xmlfiles/72-rule-real.xml
index aac8b85..85ce226 100644
--- a/tests/xmlfiles/72-rule-real.xml
+++ b/tests/xmlfiles/72-rule-real.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>output</chain><handle>36</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="lookup"><set>map2</set><sreg>1</sreg><dreg>0</dreg></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>output</chain><handle>36</handle><expr type="payload"><dreg>1</dreg><offset>16</offset><len>4</len><base>network</base></expr><expr type="lookup"><set>map2</set><sreg>1</sreg><dreg>0</dreg></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/73-set.xml b/tests/xmlfiles/73-set.xml
index 856e1f9..17b5995 100644
--- a/tests/xmlfiles/73-set.xml
+++ b/tests/xmlfiles/73-set.xml
@@ -1 +1 @@
-<nftables><set><family>ip</family><table>filter</table><name>set0</name><key_type>0</key_type><key_len>0</key_len><data_type>0</data_type><data_len>0</data_len><set_elem><key><reg type="value"><len>4</len><data0>0x0300a8c0</data0></reg></key></set_elem><set_elem><key><reg type="value"><len>4</len><data0>0x0200a8c0</data0></reg></key></set_elem><set_elem><key><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></key></set_elem></set></nftables>
+<nftables><add><set><family>ip</family><table>filter</table><name>set0</name><key_type>0</key_type><key_len>0</key_len><data_type>0</data_type><data_len>0</data_len><set_elem><key><reg type="value"><len>4</len><data0>0x0300a8c0</data0></reg></key></set_elem><set_elem><key><reg type="value"><len>4</len><data0>0x0200a8c0</data0></reg></key></set_elem><set_elem><key><reg type="value"><len>4</len><data0>0x0100a8c0</data0></reg></key></set_elem></set></add></nftables>
diff --git a/tests/xmlfiles/74-set.xml b/tests/xmlfiles/74-set.xml
index c5be4e5..bf39c46 100644
--- a/tests/xmlfiles/74-set.xml
+++ b/tests/xmlfiles/74-set.xml
@@ -1 +1 @@
-<nftables><set><family>ip6</family><table>filter</table><name>set0</name><key_type>0</key_type><key_len>0</key_len><data_type>0</data_type><data_len>0</data_len><set_elem><key><reg type="value"><len>16</len><data0>0xc09a002a</data0><data1>0x2700cac1</data1><data2>0x00000000</data2><data3>0x70010000</data3></reg></key></set_elem><set_elem><key><reg type="value"><len>16</len><data0>0xc09a002a</data0><data1>0x2700cac1</data1><data2>0x00000000</data2><data3>0x50010000</data3></reg></key></set_elem></set></nftables>
+<nftables><add><set><family>ip6</family><table>filter</table><name>set0</name><key_type>0</key_type><key_len>0</key_len><data_type>0</data_type><data_len>0</data_len><set_elem><key><reg type="value"><len>16</len><data0>0xc09a002a</data0><data1>0x2700cac1</data1><data2>0x00000000</data2><data3>0x70010000</data3></reg></key></set_elem><set_elem><key><reg type="value"><len>16</len><data0>0xc09a002a</data0><data1>0x2700cac1</data1><data2>0x00000000</data2><data3>0x50010000</data3></reg></key></set_elem></set></add></nftables>
diff --git a/tests/xmlfiles/75-ruleset.xml b/tests/xmlfiles/75-ruleset.xml
index 363a567..9c63686 100644
--- a/tests/xmlfiles/75-ruleset.xml
+++ b/tests/xmlfiles/75-ruleset.xml
@@ -1 +1 @@
-<nftables><table><name>filter</name><family>ip</family><flags>0</flags><use>0</use></table><table><name>filter</name><family>ip6</family><flags>0</flags><use>0</use></table><chain><name>input</name><handle>1</handle><bytes>0</bytes><packets>0</packets><table>filter</table><family>ip</family></chain><chain><name>output</name><handle>2</handle><bytes>0</bytes><packets>0</packets><table>filter</table><family>ip</family></chain><chain><name>forward</name><handle>1</handle><bytes>0</bytes><packets>0</packets><table>filter</table><family>ip6</family></chain><set><family>ip6</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><key><reg type="value"><len>2</len><data0>0x00004300</data0></r
eg></key></set_elem><set_elem><key><reg type="value"><len>2</len><data0>0x00003500</data0></reg></key></set_elem></set><set><family>ip</family><table>filter</table><name>map0</name><flags>11</flags><key_type>12</key_type><key_len>2</key_len><data_type>4294967040</data_type><data_len>16</data_len><set_elem><key><reg type="value"><len>2</len><data0>0x00005000</data0></reg></key><data><reg type="verdict"><verdict>drop</verdict></reg></data></set_elem><set_elem><key><reg type="value"><len>2</len><data0>0x00001600</data0></reg></key><data><reg type="verdict"><verdict>accept</verdict></reg></data></set_elem></set><rule><family>ip</family><table>filter</table><chain>input</chain><handle>8</handle><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="immediate"><dreg>0</dreg><dat
a><reg type="verdict"><verdict>accept</verdict></reg></data></expr></rule><rule><family>ip</family><table>filter</table><chain>output</chain><handle>9</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="lookup"><set>map0</set><sreg>1</sreg><dreg>0</dreg></expr></rule><rule><family>ip6</family><table>filter</table><chain>forward</chain><handle>2</handle><expr type="payload"><dreg>1</dreg><offset>6</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</
len><data0>0x00000011</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="lookup"><set>set0</set><sreg>1</sreg><dreg>0</dreg></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="immediate"><dreg>0</dreg><data><reg type="verdict"><verdict>accept</verdict></reg></data></expr></rule></nftables>
+<nftables><add><table><name>filter</name><family>ip</family><flags>0</flags><use>0</use></table><table><name>filter</name><family>ip6</family><flags>0</flags><use>0</use></table><chain><name>input</name><handle>1</handle><bytes>0</bytes><packets>0</packets><table>filter</table><family>ip</family></chain><chain><name>output</name><handle>2</handle><bytes>0</bytes><packets>0</packets><table>filter</table><family>ip</family></chain><chain><name>forward</name><handle>1</handle><bytes>0</bytes><packets>0</packets><table>filter</table><family>ip6</family></chain><set><family>ip6</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><key><reg type="value"><len>2</len><data0>0x00004300</data
0></reg></key></set_elem><set_elem><key><reg type="value"><len>2</len><data0>0x00003500</data0></reg></key></set_elem></set><set><family>ip</family><table>filter</table><name>map0</name><flags>11</flags><key_type>12</key_type><key_len>2</key_len><data_type>4294967040</data_type><data_len>16</data_len><set_elem><key><reg type="value"><len>2</len><data0>0x00005000</data0></reg></key><data><reg type="verdict"><verdict>drop</verdict></reg></data></set_elem><set_elem><key><reg type="value"><len>2</len><data0>0x00001600</data0></reg></key><data><reg type="verdict"><verdict>accept</verdict></reg></data></set_elem></set><rule><family>ip</family><table>filter</table><chain>input</chain><handle>8</handle><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="immediate"><dreg>0</dreg
><data><reg type="verdict"><verdict>accept</verdict></reg></data></expr></rule><rule><family>ip</family><table>filter</table><chain>output</chain><handle>9</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="lookup"><set>map0</set><sreg>1</sreg><dreg>0</dreg></expr></rule><rule><family>ip6</family><table>filter</table><chain>forward</chain><handle>2</handle><expr type="payload"><dreg>1</dreg><offset>6</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><le
n>1</len><data0>0x00000011</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="lookup"><set>set0</set><sreg>1</sreg><dreg>0</dreg></expr><expr type="counter"><pkts>0</pkts><bytes>0</bytes></expr><expr type="immediate"><dreg>0</dreg><data><reg type="verdict"><verdict>accept</verdict></reg></data></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/76-rule-meta_target.xml b/tests/xmlfiles/76-rule-meta_target.xml
index 970de61..beac9f3 100644
--- a/tests/xmlfiles/76-rule-meta_target.xml
+++ b/tests/xmlfiles/76-rule-meta_target.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>129</handle><expr type="meta"><key>mark</key><sreg>1</sreg></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>filter</table><chain>test</chain><handle>129</handle><expr type="meta"><key>mark</key><sreg>1</sreg></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/77-rule-queue.xml b/tests/xmlfiles/77-rule-queue.xml
index 3073b47..b9e9628 100644
--- a/tests/xmlfiles/77-rule-queue.xml
+++ b/tests/xmlfiles/77-rule-queue.xml
@@ -1 +1 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>input</chain><handle>43</handle><expr type="queue"><num>4</num><total>2</total><flags>0</flags></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>input</chain><handle>43</handle><expr type="queue"><num>4</num><total>2</total><flags>0</flags></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/78-rule-queue.xml b/tests/xmlfiles/78-rule-queue.xml
index 1876af7..d192d7d 100644
--- a/tests/xmlfiles/78-rule-queue.xml
+++ b/tests/xmlfiles/78-rule-queue.xml
@@ -1,2 +1,2 @@
-<nftables><rule><family>ip</family><table>filter</table><chain>input</chain><handle>44</handle><position>43</position><expr type="queue"><num>4</num><total>2</total><flags>3</flags></expr></rule></nftables>
+<nftables><add><rule><family>ip</family><table>filter</table><chain>input</chain><handle>44</handle><position>43</position><expr type="queue"><num>4</num><total>2</total><flags>3</flags></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/79-rule-masq.xml b/tests/xmlfiles/79-rule-masq.xml
index 303f2f2..59f9c53 100644
--- a/tests/xmlfiles/79-rule-masq.xml
+++ b/tests/xmlfiles/79-rule-masq.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>nat</table><chain>postrouting</chain><handle>4</handle><expr type="masq"><flags>12</flags></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>nat</table><chain>postrouting</chain><handle>4</handle><expr type="masq"><flags>12</flags></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/80-rule-redir.xml b/tests/xmlfiles/80-rule-redir.xml
index f7dd453..00abc58 100644
--- a/tests/xmlfiles/80-rule-redir.xml
+++ b/tests/xmlfiles/80-rule-redir.xml
@@ -1 +1 @@
-<nftables><rule><family>ip6</family><table>nat</table><chain>prerouting</chain><handle>4</handle><expr type="redir"></expr></rule></nftables>
+<nftables><add><rule><family>ip6</family><table>nat</table><chain>prerouting</chain><handle>4</handle><expr type="redir"></expr></rule></add></nftables>
diff --git a/tests/xmlfiles/81-rule-real.xml b/tests/xmlfiles/81-rule-real.xml
new file mode 100644
index 0000000..11a860e
--- /dev/null
+++ b/tests/xmlfiles/81-rule-real.xml
@@ -0,0 +1 @@
+<nftables><add><rule><family>ip</family><table>filter</table><chain>input</chain><handle>5</handle><expr type="payload"><dreg>1</dreg><offset>9</offset><len>1</len><base>network</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>1</len><data0>0x00000006</data0></reg></data></expr><expr type="payload"><dreg>1</dreg><offset>2</offset><len>2</len><base>transport</base></expr><expr type="cmp"><sreg>1</sreg><op>eq</op><data><reg type="value"><len>2</len><data0>0x00001700</data0></reg></data></expr></rule></add></nftables>
--
1.7.10.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2015-01-23 14:49 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-01-23 14:49 [libnftnl PATCH] set: refactor code in json parse function Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 1/5] src: add command tag in json/xml export support Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 2/5] src: add support to import json/xml with the new syntax Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 3/5] ruleset: consolidate code in json/xml import support Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 4/5] example: Parse and create netlink message using the new parsing functions Alvaro Neira Ayuso
2015-01-23 14:49 ` [libnftnl PATCH 5/5] test: update json/xml tests to the new syntax Alvaro Neira Ayuso
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).