diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..546e9c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/nftables-0.9.0.tar.bz2 diff --git a/.nftables.metadata b/.nftables.metadata new file mode 100644 index 0000000..09a6972 --- /dev/null +++ b/.nftables.metadata @@ -0,0 +1 @@ +a3463fc6589c08631ec3f306f6db7f0905249542 SOURCES/nftables-0.9.0.tar.bz2 diff --git a/SOURCES/0001-evaluate-reject-Allow-icmpx-in-inet-bridge-families.patch b/SOURCES/0001-evaluate-reject-Allow-icmpx-in-inet-bridge-families.patch new file mode 100644 index 0000000..d6a76ed --- /dev/null +++ b/SOURCES/0001-evaluate-reject-Allow-icmpx-in-inet-bridge-families.patch @@ -0,0 +1,227 @@ +From f47941faed177fd3943c7eaf9408e9e6481595f6 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Mon, 13 Aug 2018 18:58:57 +0200 +Subject: [PATCH] evaluate: reject: Allow icmpx in inet/bridge families + +Commit 3e6ab2b335142 added restraints on reject types for bridge and +inet families but aparently those were too strict: If a rule in e.g. +inet family contained a match which introduced a protocol dependency, +icmpx type rejects were disallowed for no obvious reason. + +Allow icmpx type rejects in inet family regardless of protocol +dependency since we either have IPv4 or IPv6 traffic in there and for +both icmpx is fine. + +Merge restraints in bridge family with those for TCP reset since it +already does what is needed, namely checking that ether proto is either +IPv4 or IPv6. + +Fixes: 3e6ab2b335142 ("evaluate: reject: check in bridge and inet the network context in reject") +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 8d2c3c72935443228b5e0492c8d3e2e2048c0c5a) +Signed-off-by: Phil Sutter +--- + src/evaluate.c | 7 +---- + tests/py/bridge/reject.t | 5 ++++ + tests/py/bridge/reject.t.json | 44 +++++++++++++++++++++++++++++ + tests/py/bridge/reject.t.payload | 12 ++++++++ + tests/py/inet/reject.t | 3 ++ + tests/py/inet/reject.t.json | 42 +++++++++++++++++++++++++++ + tests/py/inet/reject.t.payload.inet | 12 ++++++++ + 7 files changed, 119 insertions(+), 6 deletions(-) + +diff --git a/src/evaluate.c b/src/evaluate.c +index c4ee3cc94a3db..d18af34341b0d 100644 +--- a/src/evaluate.c ++++ b/src/evaluate.c +@@ -2130,9 +2130,7 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx, + case NFT_REJECT_TCP_RST: + break; + case NFT_REJECT_ICMPX_UNREACH: +- return stmt_binary_error(ctx, stmt->reject.expr, +- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR], +- "conflicting network protocol specified"); ++ break; + case NFT_REJECT_ICMP_UNREACH: + base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc; + protocol = proto_find_num(base, desc); +@@ -2183,9 +2181,6 @@ static int stmt_evaluate_reject_bridge_family(struct eval_ctx *ctx, + + switch (stmt->reject.type) { + case NFT_REJECT_ICMPX_UNREACH: +- return stmt_binary_error(ctx, stmt->reject.expr, +- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR], +- "conflicting network protocol specified"); + case NFT_REJECT_TCP_RST: + base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc; + protocol = proto_find_num(base, desc); +diff --git a/tests/py/bridge/reject.t b/tests/py/bridge/reject.t +index 67deac8d3b5e4..13d65b115c3cb 100644 +--- a/tests/py/bridge/reject.t ++++ b/tests/py/bridge/reject.t +@@ -37,3 +37,8 @@ ether type arp reject;fail + ether type vlan reject with tcp reset;fail + ether type arp reject with tcp reset;fail + ip protocol udp reject with tcp reset;fail ++ ++ether type ip reject with icmpx type admin-prohibited;ok ++ether type ip6 reject with icmpx type admin-prohibited;ok ++ether type vlan reject with icmpx type admin-prohibited;fail ++ether type arp reject with icmpx type admin-prohibited;fail +diff --git a/tests/py/bridge/reject.t.json b/tests/py/bridge/reject.t.json +index aa716f8070666..c0bed56e6ce41 100644 +--- a/tests/py/bridge/reject.t.json ++++ b/tests/py/bridge/reject.t.json +@@ -219,3 +219,47 @@ + } + ] + ++# ether type ip reject with icmpx type admin-prohibited ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "type", ++ "protocol": "ether" ++ } ++ }, ++ "op": "==", ++ "right": "ip" ++ } ++ }, ++ { ++ "reject": { ++ "expr": "admin-prohibited", ++ "type": "icmpx" ++ } ++ } ++] ++ ++# ether type ip6 reject with icmpx type admin-prohibited ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "type", ++ "protocol": "ether" ++ } ++ }, ++ "op": "==", ++ "right": "ip6" ++ } ++ }, ++ { ++ "reject": { ++ "expr": "admin-prohibited", ++ "type": "icmpx" ++ } ++ } ++] ++ +diff --git a/tests/py/bridge/reject.t.payload b/tests/py/bridge/reject.t.payload +index b984f6f8de4d6..888179df9c977 100644 +--- a/tests/py/bridge/reject.t.payload ++++ b/tests/py/bridge/reject.t.payload +@@ -106,3 +106,15 @@ bridge test-bridge input + bridge test-bridge input + [ reject type 2 code 1 ] + ++# ether type ip reject with icmpx type admin-prohibited ++bridge test-bridge input ++ [ payload load 2b @ link header + 12 => reg 1 ] ++ [ cmp eq reg 1 0x00000008 ] ++ [ reject type 2 code 3 ] ++ ++# ether type ip6 reject with icmpx type admin-prohibited ++bridge test-bridge input ++ [ payload load 2b @ link header + 12 => reg 1 ] ++ [ cmp eq reg 1 0x0000dd86 ] ++ [ reject type 2 code 3 ] ++ +diff --git a/tests/py/inet/reject.t b/tests/py/inet/reject.t +index 7679407e6f8d4..a88c5a4afae51 100644 +--- a/tests/py/inet/reject.t ++++ b/tests/py/inet/reject.t +@@ -34,3 +34,6 @@ meta nfproto ipv6 reject with icmp type host-unreachable;fail + meta nfproto ipv4 ip protocol icmp reject with icmpv6 type no-route;fail + meta nfproto ipv6 ip protocol icmp reject with icmp type host-unreachable;fail + meta l4proto udp reject with tcp reset;fail ++ ++meta nfproto ipv4 reject with icmpx type admin-prohibited;ok ++meta nfproto ipv6 reject with icmpx type admin-prohibited;ok +diff --git a/tests/py/inet/reject.t.json b/tests/py/inet/reject.t.json +index 0939f4450509b..46d4857a57c99 100644 +--- a/tests/py/inet/reject.t.json ++++ b/tests/py/inet/reject.t.json +@@ -238,3 +238,45 @@ + } + ] + ++# meta nfproto ipv4 reject with icmpx type admin-prohibited ++[ ++ { ++ "match": { ++ "left": { ++ "meta": { ++ "key": "nfproto" ++ } ++ }, ++ "op": "==", ++ "right": "ipv4" ++ } ++ }, ++ { ++ "reject": { ++ "expr": "admin-prohibited", ++ "type": "icmpx" ++ } ++ } ++] ++ ++# meta nfproto ipv6 reject with icmpx type admin-prohibited ++[ ++ { ++ "match": { ++ "left": { ++ "meta": { ++ "key": "nfproto" ++ } ++ }, ++ "op": "==", ++ "right": "ipv6" ++ } ++ }, ++ { ++ "reject": { ++ "expr": "admin-prohibited", ++ "type": "icmpx" ++ } ++ } ++] ++ +diff --git a/tests/py/inet/reject.t.payload.inet b/tests/py/inet/reject.t.payload.inet +index 7a6468e81f9e7..ee1aae02f1e1d 100644 +--- a/tests/py/inet/reject.t.payload.inet ++++ b/tests/py/inet/reject.t.payload.inet +@@ -220,3 +220,15 @@ inet test-inet input + [ cmp eq reg 1 0x0000000a ] + [ reject type 0 code 0 ] + ++# meta nfproto ipv4 reject with icmpx type admin-prohibited ++inet test-inet input ++ [ meta load nfproto => reg 1 ] ++ [ cmp eq reg 1 0x00000002 ] ++ [ reject type 2 code 3 ] ++ ++# meta nfproto ipv6 reject with icmpx type admin-prohibited ++inet test-inet input ++ [ meta load nfproto => reg 1 ] ++ [ cmp eq reg 1 0x0000000a ] ++ [ reject type 2 code 3 ] ++ +-- +2.19.0 + diff --git a/SOURCES/0002-monitor-Drop-fake-XML-support.patch b/SOURCES/0002-monitor-Drop-fake-XML-support.patch new file mode 100644 index 0000000..aad6e58 --- /dev/null +++ b/SOURCES/0002-monitor-Drop-fake-XML-support.patch @@ -0,0 +1,71 @@ +From 7a2546c151233aceb7cb8628b234e2a7d2d7620f Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Thu, 11 Oct 2018 17:48:57 +0200 +Subject: [PATCH] monitor: Drop fake XML support + +Since libnftnl doesn't support XML formatting, pretending to do so in +nft monitor is pointless. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 2194a76ed46a2f097c5ca5955e44544418866cc2) +Signed-off-by: Phil Sutter +--- + src/monitor.c | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/src/monitor.c b/src/monitor.c +index 4310c3b8dc434..d75410888e3d0 100644 +--- a/src/monitor.c ++++ b/src/monitor.c +@@ -203,7 +203,6 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type, + nftnl_table_get_u64(nlt, NFTNL_TABLE_HANDLE)); + nft_mon_print(monh, "\n"); + break; +- case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_table_fprintf(monh->ctx->octx->output_fp, nlt, + monh->format, netlink_msg2nftnl_of(type)); +@@ -245,7 +244,6 @@ static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type, + break; + } + break; +- case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_chain_fprintf(monh->ctx->octx->output_fp, nlc, + monh->format, netlink_msg2nftnl_of(type)); +@@ -292,7 +290,6 @@ static int netlink_events_set_cb(const struct nlmsghdr *nlh, int type, + break; + } + break; +- case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_set_fprintf(monh->ctx->octx->output_fp, nls, + monh->format, netlink_msg2nftnl_of(type)); +@@ -441,7 +438,6 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type, + + set_free(dummyset); + break; +- case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_set_fprintf(monh->ctx->octx->output_fp, nls, + monh->format, netlink_msg2nftnl_of(type)); +@@ -486,7 +482,6 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type, + break; + } + break; +- case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_obj_fprintf(monh->ctx->octx->output_fp, nlo, + monh->format, netlink_msg2nftnl_of(type)); +@@ -542,7 +537,6 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type, + break; + } + break; +- case NFTNL_OUTPUT_XML: + case NFTNL_OUTPUT_JSON: + nftnl_rule_fprintf(monh->ctx->octx->output_fp, nlr, + monh->format, netlink_msg2nftnl_of(type)); +-- +2.19.0 + diff --git a/SOURCES/0003-monitor-Drop-update-table-and-update-chain-cases.patch b/SOURCES/0003-monitor-Drop-update-table-and-update-chain-cases.patch new file mode 100644 index 0000000..447cb12 --- /dev/null +++ b/SOURCES/0003-monitor-Drop-update-table-and-update-chain-cases.patch @@ -0,0 +1,48 @@ +From 12169cc4179429a88fcc4ffab3e52adb0daf95fc Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Thu, 11 Oct 2018 17:48:58 +0200 +Subject: [PATCH] monitor: Drop 'update table' and 'update chain' cases + +There seems to be no situation where this comes to play. Also, since +there is no 'nft update table/chain' command, this is inconsistent with +input. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 62cea2e4ca9d6bc781ced6518810144a8d697275) +Signed-off-by: Phil Sutter +--- + src/monitor.c | 10 ++-------- + 1 file changed, 2 insertions(+), 8 deletions(-) + +diff --git a/src/monitor.c b/src/monitor.c +index d75410888e3d0..3e70b89f0b2ab 100644 +--- a/src/monitor.c ++++ b/src/monitor.c +@@ -186,10 +186,7 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type, + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + if (type == NFT_MSG_NEWTABLE) { +- if (nlh->nlmsg_flags & NLM_F_EXCL) +- nft_mon_print(monh, "update table "); +- else +- nft_mon_print(monh, "add table "); ++ nft_mon_print(monh, "add table "); + } else { + nft_mon_print(monh, "delete table "); + } +@@ -227,10 +224,7 @@ static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type, + case NFTNL_OUTPUT_DEFAULT: + switch (type) { + case NFT_MSG_NEWCHAIN: +- if (nlh->nlmsg_flags & NLM_F_EXCL) +- nft_mon_print(monh, "update "); +- else +- nft_mon_print(monh, "add "); ++ nft_mon_print(monh, "add "); + + c = netlink_delinearize_chain(monh->ctx, nlc); + chain_print_plain(c, monh->ctx->octx); +-- +2.19.0 + diff --git a/SOURCES/0004-monitor-Fix-printing-of-ct-objects.patch b/SOURCES/0004-monitor-Fix-printing-of-ct-objects.patch new file mode 100644 index 0000000..f54e6f5 --- /dev/null +++ b/SOURCES/0004-monitor-Fix-printing-of-ct-objects.patch @@ -0,0 +1,164 @@ +From 892ec2ca97263ccfffb52f2943c2ab2ac34f476d Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Thu, 11 Oct 2018 17:48:59 +0200 +Subject: [PATCH] monitor: Fix printing of ct objects + +Monitor output is supposed to be single lined without tabs, but ct +object were printed with newlines and tabs hard-coded. Fixing this +wasn't too hard given that there is 'stmt_separator' to also include +semi-colons where required if newline was removed. + +A more obvious mistake was position of object type in monitor output: +Like with other object types, it has to occur between command and table +spec. As a positive side-effect, this aligns ct objects better with +others (see obj_type_name_array for instance). + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 90ed4fb3855f0d9d881b812c75e338e5e93081ba) + +Conflicts: + src/rule.c + tests/shell/testcases/listing/0013objects_0 + tests/shell/testcases/nft-f/0017ct_timeout_obj_0 + tests/shell/testcases/nft-f/dumps/0017ct_timeout_obj_0.nft +-> missing ct timeout support + +Signed-off-by: Phil Sutter +--- + src/json.c | 1 - + src/rule.c | 26 ++++++++++++++++---------- + tests/monitor/testcases/object.t | 27 +++++++++++++++++++++++++++ + 3 files changed, 43 insertions(+), 11 deletions(-) + create mode 100644 tests/monitor/testcases/object.t + +diff --git a/src/json.c b/src/json.c +index b6e6ca9c6c383..af157212c081e 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -264,7 +264,6 @@ static json_t *obj_print_json(struct output_ctx *octx, const struct obj *obj) + json_decref(tmp); + break; + case NFT_OBJECT_CT_HELPER: +- type = "ct helper"; + tmp = json_pack("{s:s, s:o, s:s}", + "type", obj->ct_helper.name, "protocol", + proto_name_json(obj->ct_helper.l4proto), +diff --git a/src/rule.c b/src/rule.c +index 56b956a4f8fec..eb06302d4f223 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -1432,8 +1432,8 @@ static void obj_print_data(const struct obj *obj, + nft_print(octx, "packets 0 bytes 0"); + break; + } +- nft_print(octx, "packets %" PRIu64 " bytes %" PRIu64 "", +- obj->counter.packets, obj->counter.bytes); ++ nft_print(octx, "packets %" PRIu64 " bytes %" PRIu64 "%s", ++ obj->counter.packets, obj->counter.bytes, opts->nl); + break; + case NFT_OBJECT_QUOTA: { + const char *data_unit; +@@ -1452,18 +1452,22 @@ static void obj_print_data(const struct obj *obj, + nft_print(octx, " used %" PRIu64 " %s", + bytes, data_unit); + } ++ nft_print(octx, "%s", opts->nl); + } + break; + case NFT_OBJECT_CT_HELPER: +- nft_print(octx, "ct helper %s {", obj->handle.obj.name); ++ nft_print(octx, " %s {", obj->handle.obj.name); + if (octx->handle > 0) + nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + nft_print(octx, "%s", opts->nl); +- nft_print(octx, "\t\ttype \"%s\" protocol ", +- obj->ct_helper.name); ++ nft_print(octx, "%s%stype \"%s\" protocol ", ++ opts->tab, opts->tab, obj->ct_helper.name); + print_proto_name_proto(obj->ct_helper.l4proto, octx); +- nft_print(octx, "\t\tl3proto %s", +- family2str(obj->ct_helper.l3proto)); ++ nft_print(octx, "%s", opts->stmt_separator); ++ nft_print(octx, "%s%sl3proto %s%s", ++ opts->tab, opts->tab, ++ family2str(obj->ct_helper.l3proto), ++ opts->stmt_separator); + break; + case NFT_OBJECT_LIMIT: { + bool inv = obj->limit.flags & NFT_LIMIT_F_INV; +@@ -1498,10 +1502,11 @@ static void obj_print_data(const struct obj *obj, + } + break; + } ++ nft_print(octx, "%s", opts->nl); + } + break; + default: +- nft_print(octx, "unknown {%s", opts->nl); ++ nft_print(octx, " unknown {%s", opts->nl); + break; + } + } +@@ -1509,7 +1514,7 @@ static void obj_print_data(const struct obj *obj, + static const char * const obj_type_name_array[] = { + [NFT_OBJECT_COUNTER] = "counter", + [NFT_OBJECT_QUOTA] = "quota", +- [NFT_OBJECT_CT_HELPER] = "", ++ [NFT_OBJECT_CT_HELPER] = "ct helper", + [NFT_OBJECT_LIMIT] = "limit", + }; + +@@ -1548,7 +1553,7 @@ static void obj_print_declaration(const struct obj *obj, + + obj_print_data(obj, opts, octx); + +- nft_print(octx, "%s%s}%s", opts->nl, opts->tab, opts->nl); ++ nft_print(octx, "%s}%s", opts->tab, opts->nl); + } + + void obj_print(const struct obj *obj, struct output_ctx *octx) +@@ -1569,6 +1574,7 @@ void obj_print_plain(const struct obj *obj, struct output_ctx *octx) + .nl = " ", + .table = obj->handle.table.name, + .family = family2str(obj->handle.family), ++ .stmt_separator = "; ", + }; + + obj_print_declaration(obj, &opts, octx); +diff --git a/tests/monitor/testcases/object.t b/tests/monitor/testcases/object.t +new file mode 100644 +index 0000000000000..7b88409775796 +--- /dev/null ++++ b/tests/monitor/testcases/object.t +@@ -0,0 +1,27 @@ ++# first the setup ++I add table ip t ++O - ++ ++I add counter ip t c ++O add counter ip t c { packets 0 bytes 0 } ++ ++I delete counter ip t c ++O - ++ ++I add quota ip t q 25 mbytes ++O add quota ip t q { 25 mbytes } ++ ++I delete quota ip t q ++O - ++ ++I add limit ip t l rate 1/second ++O add limit ip t l { rate 1/second } ++ ++I delete limit ip t l ++O - ++ ++I add ct helper ip t cth { type "sip" protocol tcp; l3proto ip; } ++O - ++ ++I delete ct helper ip t cth ++O - +-- +2.19.0 + diff --git a/SOURCES/0005-monitor-Use-libnftables-JSON-output.patch b/SOURCES/0005-monitor-Use-libnftables-JSON-output.patch new file mode 100644 index 0000000..e6e47dd --- /dev/null +++ b/SOURCES/0005-monitor-Use-libnftables-JSON-output.patch @@ -0,0 +1,652 @@ +From 53693c43d94dddf1ae1a0e69bfa953fba2c098e0 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Thu, 11 Oct 2018 17:49:00 +0200 +Subject: [PATCH] monitor: Use libnftables JSON output + +This switches 'nft monitor' JSON output from using libnftnl's to +libnftables' implementation. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 9e88aae28e9f44d010f3ecf7577357f4c0e7d622) +Signed-off-by: Phil Sutter +--- + include/json.h | 51 +++++++++ + src/json.c | 57 ++++++++++ + src/monitor.c | 281 +++++++++++++++++++++++++------------------------ + src/rule.c | 2 - + 4 files changed, 251 insertions(+), 140 deletions(-) + +diff --git a/include/json.h b/include/json.h +index ae3938142aeac..af0f72f13dd60 100644 +--- a/include/json.h ++++ b/include/json.h +@@ -9,9 +9,11 @@ struct expr; + struct netlink_ctx; + struct rule; + struct set; ++struct obj; + struct stmt; + struct symbol_table; + struct table; ++struct netlink_mon_handler; + + #ifdef HAVE_LIBJANSSON + +@@ -81,6 +83,19 @@ int nft_parse_json_buffer(struct nft_ctx *nft, char *buf, size_t buflen, + int nft_parse_json_filename(struct nft_ctx *nft, const char *filename, + struct list_head *msgs, struct list_head *cmds); + ++void monitor_print_table_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct table *t); ++void monitor_print_chain_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct chain *c); ++void monitor_print_set_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct set *s); ++void monitor_print_element_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct set *s); ++void monitor_print_obj_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct obj *o); ++void monitor_print_rule_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct rule *r); ++ + #else /* ! HAVE_LIBJANSSON */ + + typedef void json_t; +@@ -176,6 +191,42 @@ nft_parse_json_filename(struct nft_ctx *nft, const char *filename, + return -EINVAL; + } + ++static inline void monitor_print_table_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct table *t) ++{ ++ /* empty */ ++} ++ ++static inline void monitor_print_chain_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct chain *c) ++{ ++ /* empty */ ++} ++ ++static inline void monitor_print_set_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct set *s) ++{ ++ /* empty */ ++} ++ ++static inline void monitor_print_element_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct set *s) ++{ ++ /* empty */ ++} ++ ++static inline void monitor_print_obj_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct obj *o) ++{ ++ /* empty */ ++} ++ ++static inline void monitor_print_rule_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct rule *r) ++{ ++ /* empty */ ++} ++ + #endif /* HAVE_LIBJANSSON */ + + #endif /* NFTABLES_JSON_H */ +diff --git a/src/json.c b/src/json.c +index af157212c081e..7d89754bd666d 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -147,6 +147,19 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set) + return json_pack("{s:o}", type, root); + } + ++/* XXX: Merge with set_print_json()? */ ++static json_t *element_print_json(struct output_ctx *octx, ++ const struct set *set) ++{ ++ json_t *root = expr_print_json(set->init, octx); ++ ++ return json_pack("{s: {s:s, s:s, s:s, s:o}}", "element", ++ "family", family2str(set->handle.family), ++ "table", set->handle.table.name, ++ "name", set->handle.set.name, ++ "elem", root); ++} ++ + static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx) + { + char buf[1024]; +@@ -1554,3 +1567,47 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd) + json_decref(root); + return 0; + } ++ ++static void monitor_print_json(struct netlink_mon_handler *monh, ++ const char *cmd, json_t *obj) ++{ ++ obj = json_pack("{s:o}", cmd, obj); ++ json_dumpf(obj, monh->ctx->octx->output_fp, 0); ++ json_decref(obj); ++} ++ ++void monitor_print_table_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct table *t) ++{ ++ monitor_print_json(monh, cmd, table_print_json(monh->ctx->octx, t)); ++} ++ ++void monitor_print_chain_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct chain *c) ++{ ++ monitor_print_json(monh, cmd, chain_print_json(monh->ctx->octx, c)); ++} ++ ++void monitor_print_set_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct set *s) ++{ ++ monitor_print_json(monh, cmd, set_print_json(monh->ctx->octx, s)); ++} ++ ++void monitor_print_element_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct set *s) ++{ ++ monitor_print_json(monh, cmd, element_print_json(monh->ctx->octx, s)); ++} ++ ++void monitor_print_obj_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct obj *o) ++{ ++ monitor_print_json(monh, cmd, obj_print_json(monh->ctx->octx, o)); ++} ++ ++void monitor_print_rule_json(struct netlink_mon_handler *monh, ++ const char *cmd, struct rule *r) ++{ ++ monitor_print_json(monh, cmd, rule_print_json(monh->ctx->octx, r)); ++} +diff --git a/src/monitor.c b/src/monitor.c +index 3e70b89f0b2ab..213c40d119b4c 100644 +--- a/src/monitor.c ++++ b/src/monitor.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + + #define nft_mon_print(monh, ...) nft_print(monh->ctx->octx, __VA_ARGS__) + +@@ -127,18 +128,39 @@ static uint32_t netlink_msg2nftnl_of(uint32_t msg) + case NFT_MSG_NEWSET: + case NFT_MSG_NEWSETELEM: + case NFT_MSG_NEWRULE: ++ case NFT_MSG_NEWOBJ: ++ case NFT_MSG_NEWFLOWTABLE: + return NFTNL_OF_EVENT_NEW; + case NFT_MSG_DELTABLE: + case NFT_MSG_DELCHAIN: + case NFT_MSG_DELSET: + case NFT_MSG_DELSETELEM: + case NFT_MSG_DELRULE: ++ case NFT_MSG_DELOBJ: ++ case NFT_MSG_DELFLOWTABLE: + return NFTNL_OF_EVENT_DEL; + } + + return 0; + } + ++static const char *nftnl_of2cmd(uint32_t of) ++{ ++ switch (of) { ++ case NFTNL_OF_EVENT_NEW: ++ return "add"; ++ case NFTNL_OF_EVENT_DEL: ++ return "delete"; ++ default: ++ return "???"; ++ } ++} ++ ++static const char *netlink_msg2cmd(uint32_t msg) ++{ ++ return nftnl_of2cmd(netlink_msg2nftnl_of(msg)); ++} ++ + static void nlr_for_each_set(struct nftnl_rule *nlr, + void (*cb)(struct set *s, void *data), + void *data, struct nft_cache *cache) +@@ -179,34 +201,29 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) + { + struct nftnl_table *nlt; +- uint32_t family; ++ struct table *t; ++ const char *cmd; + + nlt = netlink_table_alloc(nlh); ++ t = netlink_delinearize_table(monh->ctx, nlt); ++ cmd = netlink_msg2cmd(type); + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: +- if (type == NFT_MSG_NEWTABLE) { +- nft_mon_print(monh, "add table "); +- } else { +- nft_mon_print(monh, "delete table "); +- } ++ nft_mon_print(monh, "%s table ", cmd); + +- family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY); +- +- nft_mon_print(monh, "%s %s", family2str(family), +- nftnl_table_get_str(nlt, NFTNL_TABLE_NAME)); ++ nft_mon_print(monh, "%s %s", family2str(t->handle.family), ++ t->handle.table.name); + if (monh->ctx->octx->handle > 0) + nft_mon_print(monh, " # handle %" PRIu64 "", +- nftnl_table_get_u64(nlt, NFTNL_TABLE_HANDLE)); +- nft_mon_print(monh, "\n"); ++ t->handle.handle.id); + break; + case NFTNL_OUTPUT_JSON: +- nftnl_table_fprintf(monh->ctx->octx->output_fp, nlt, +- monh->format, netlink_msg2nftnl_of(type)); +- nft_mon_print(monh, "\n"); ++ monitor_print_table_json(monh, cmd, t); + break; + } +- ++ nft_mon_print(monh, "\n"); ++ table_free(t); + nftnl_table_free(nlt); + return MNL_CB_OK; + } +@@ -216,35 +233,34 @@ static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type, + { + struct nftnl_chain *nlc; + struct chain *c; +- uint32_t family; ++ const char *cmd; + + nlc = netlink_chain_alloc(nlh); ++ c = netlink_delinearize_chain(monh->ctx, nlc); ++ cmd = netlink_msg2cmd(type); + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: ++ nft_mon_print(monh, "%s ", cmd); ++ + switch (type) { + case NFT_MSG_NEWCHAIN: +- nft_mon_print(monh, "add "); +- +- c = netlink_delinearize_chain(monh->ctx, nlc); + chain_print_plain(c, monh->ctx->octx); +- chain_free(c); + break; + case NFT_MSG_DELCHAIN: +- family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY); +- nft_mon_print(monh, "delete chain %s %s %s\n", family2str(family), +- nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE), +- nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME)); ++ nft_mon_print(monh, "chain %s %s %s", ++ family2str(c->handle.family), ++ c->handle.table.name, ++ c->handle.chain.name); + break; + } + break; + case NFTNL_OUTPUT_JSON: +- nftnl_chain_fprintf(monh->ctx->octx->output_fp, nlc, +- monh->format, netlink_msg2nftnl_of(type)); +- nft_mon_print(monh, "\n"); ++ monitor_print_chain_json(monh, cmd, c); + break; + } +- ++ nft_mon_print(monh, "\n"); ++ chain_free(c); + nftnl_chain_free(nlc); + return MNL_CB_OK; + } +@@ -253,43 +269,44 @@ static int netlink_events_set_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) + { + struct nftnl_set *nls; ++ const char *family, *cmd; + struct set *set; +- uint32_t family, flags; ++ uint32_t flags; + + nls = netlink_set_alloc(nlh); + flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS); + if (flags & NFT_SET_ANONYMOUS) + goto out; + ++ set = netlink_delinearize_set(monh->ctx, nls); ++ if (set == NULL) { ++ nftnl_set_free(nls); ++ return MNL_CB_ERROR; ++ } ++ family = family2str(set->handle.family); ++ cmd = netlink_msg2cmd(type); ++ + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: ++ nft_mon_print(monh, "%s ", cmd); ++ + switch (type) { + case NFT_MSG_NEWSET: +- nft_mon_print(monh, "add "); +- set = netlink_delinearize_set(monh->ctx, nls); +- if (set == NULL) { +- nftnl_set_free(nls); +- return MNL_CB_ERROR; +- } + set_print_plain(set, monh->ctx->octx); +- set_free(set); +- nft_mon_print(monh, "\n"); + break; + case NFT_MSG_DELSET: +- family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); +- nft_mon_print(monh, "delete set %s %s %s\n", +- family2str(family), +- nftnl_set_get_str(nls, NFTNL_SET_TABLE), +- nftnl_set_get_str(nls, NFTNL_SET_NAME)); ++ nft_mon_print(monh, "set %s %s %s", family, ++ set->handle.table.name, ++ set->handle.set.name); + break; + } + break; + case NFTNL_OUTPUT_JSON: +- nftnl_set_fprintf(monh->ctx->octx->output_fp, nls, +- monh->format, netlink_msg2nftnl_of(type)); +- nft_mon_print(monh, "\n"); ++ monitor_print_set_json(monh, cmd, set); + break; + } ++ nft_mon_print(monh, "\n"); ++ set_free(set); + out: + nftnl_set_free(nls); + return MNL_CB_OK; +@@ -360,13 +377,14 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type, + struct nftnl_set *nls; + struct set *dummyset; + struct set *set; +- const char *setname, *table; ++ const char *setname, *table, *cmd; + uint32_t family; + + nls = netlink_setelem_alloc(nlh); + table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); + setname = nftnl_set_get_str(nls, NFTNL_SET_NAME); + family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); ++ cmd = netlink_msg2cmd(type); + + set = set_lookup_global(family, table, setname, monh->cache); + if (set == NULL) { +@@ -374,70 +392,63 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type, + goto out; + } + +- switch (monh->format) { +- case NFTNL_OUTPUT_DEFAULT: +- if (set->flags & NFT_SET_ANONYMOUS) +- goto out; +- +- /* we want to 'delinearize' the set_elem, but don't +- * modify the original cached set. This path is only +- * used by named sets, so use a dummy set. +- */ +- dummyset = set_alloc(monh->loc); +- dummyset->key = expr_clone(set->key); +- dummyset->datatype = set->datatype; +- dummyset->flags = set->flags; +- dummyset->init = set_expr_alloc(monh->loc, set); ++ if (set->flags & NFT_SET_ANONYMOUS) ++ goto out; + +- nlsei = nftnl_set_elems_iter_create(nls); +- if (nlsei == NULL) +- memory_allocation_error(); ++ /* we want to 'delinearize' the set_elem, but don't ++ * modify the original cached set. This path is only ++ * used by named sets, so use a dummy set. ++ */ ++ dummyset = set_alloc(monh->loc); ++ dummyset->key = expr_clone(set->key); ++ dummyset->datatype = set->datatype; ++ dummyset->flags = set->flags; ++ dummyset->init = set_expr_alloc(monh->loc, set); + +- nlse = nftnl_set_elems_iter_next(nlsei); +- while (nlse != NULL) { +- if (netlink_event_ignore_range_event(nlse)) { +- set_free(dummyset); +- nftnl_set_elems_iter_destroy(nlsei); +- goto out; +- } +- if (netlink_delinearize_setelem(nlse, dummyset, +- monh->cache) < 0) { +- set_free(dummyset); +- nftnl_set_elems_iter_destroy(nlsei); +- goto out; +- } +- nlse = nftnl_set_elems_iter_next(nlsei); +- } +- nftnl_set_elems_iter_destroy(nlsei); ++ nlsei = nftnl_set_elems_iter_create(nls); ++ if (nlsei == NULL) ++ memory_allocation_error(); + +- if (netlink_event_range_cache(set, dummyset)) { ++ nlse = nftnl_set_elems_iter_next(nlsei); ++ while (nlse != NULL) { ++ if (netlink_event_ignore_range_event(nlse)) { + set_free(dummyset); ++ nftnl_set_elems_iter_destroy(nlsei); + goto out; + } +- +- switch (type) { +- case NFT_MSG_NEWSETELEM: +- nft_mon_print(monh, "add "); +- break; +- case NFT_MSG_DELSETELEM: +- nft_mon_print(monh, "delete "); +- break; +- default: ++ if (netlink_delinearize_setelem(nlse, dummyset, ++ monh->cache) < 0) { + set_free(dummyset); ++ nftnl_set_elems_iter_destroy(nlsei); + goto out; + } +- nft_mon_print(monh, "element %s %s %s ", family2str(family), table, setname); +- expr_print(dummyset->init, monh->ctx->octx); +- nft_mon_print(monh, "\n"); ++ nlse = nftnl_set_elems_iter_next(nlsei); ++ } ++ nftnl_set_elems_iter_destroy(nlsei); + ++ if (netlink_event_range_cache(set, dummyset)) { + set_free(dummyset); ++ goto out; ++ } ++ ++ switch (monh->format) { ++ case NFTNL_OUTPUT_DEFAULT: ++ nft_mon_print(monh, "%s element %s %s %s ", ++ cmd, family2str(family), table, setname); ++ expr_print(dummyset->init, monh->ctx->octx); + break; + case NFTNL_OUTPUT_JSON: +- nftnl_set_fprintf(monh->ctx->octx->output_fp, nls, +- monh->format, netlink_msg2nftnl_of(type)); +- nft_mon_print(monh, "\n"); ++ dummyset->handle.family = family; ++ dummyset->handle.set.name = setname; ++ dummyset->handle.table.name = table; ++ monitor_print_element_json(monh, cmd, dummyset); ++ /* prevent set_free() from trying to free those */ ++ dummyset->handle.set.name = NULL; ++ dummyset->handle.table.name = NULL; + break; + } ++ nft_mon_print(monh, "\n"); ++ set_free(dummyset); + out: + nftnl_set_free(nls); + return MNL_CB_OK; +@@ -446,43 +457,43 @@ out: + static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) + { ++ const char *family, *cmd; + struct nftnl_obj *nlo; +- uint32_t family; + struct obj *obj; + + nlo = netlink_obj_alloc(nlh); + ++ obj = netlink_delinearize_obj(monh->ctx, nlo); ++ if (obj == NULL) { ++ nftnl_obj_free(nlo); ++ return MNL_CB_ERROR; ++ } ++ family = family2str(obj->handle.family); ++ cmd = netlink_msg2cmd(type); ++ + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: ++ nft_mon_print(monh, "%s ", cmd); ++ + switch (type) { + case NFT_MSG_NEWOBJ: +- nft_mon_print(monh, "add "); +- obj = netlink_delinearize_obj(monh->ctx, nlo); +- if (obj == NULL) { +- nftnl_obj_free(nlo); +- return MNL_CB_ERROR; +- } + obj_print_plain(obj, monh->ctx->octx); +- obj_free(obj); +- nft_mon_print(monh, "\n"); + break; + case NFT_MSG_DELOBJ: +- family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY); +- nft_mon_print(monh, "delete %s %s %s %s\n", +- obj_type_name(nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE)), +- family2str(family), +- nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE), +- nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME)); ++ nft_mon_print(monh, "%s %s %s %s", ++ obj_type_name(obj->type), ++ family, ++ obj->handle.table.name, ++ obj->handle.obj.name); + break; + } + break; + case NFTNL_OUTPUT_JSON: +- nftnl_obj_fprintf(monh->ctx->octx->output_fp, nlo, +- monh->format, netlink_msg2nftnl_of(type)); +- nft_mon_print(monh, "\n"); ++ monitor_print_obj_json(monh, cmd, obj); + break; + } +- ++ nft_mon_print(monh, "\n"); ++ obj_free(obj); + nftnl_obj_free(nlo); + return MNL_CB_OK; + } +@@ -496,48 +507,42 @@ static void rule_map_decompose_cb(struct set *s, void *data) + static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) + { ++ const char *family, *cmd; + struct nftnl_rule *nlr; +- const char *family; +- const char *table; +- const char *chain; + struct rule *r; +- uint64_t handle; +- uint32_t fam; + + nlr = netlink_rule_alloc(nlh); ++ r = netlink_delinearize_rule(monh->ctx, nlr); ++ nlr_for_each_set(nlr, rule_map_decompose_cb, NULL, monh->cache); ++ cmd = netlink_msg2cmd(type); ++ + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: +- fam = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); +- family = family2str(fam); +- table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE); +- chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN); +- handle = nftnl_rule_get_u64(nlr, NFTNL_RULE_HANDLE); ++ family = family2str(r->handle.family); ++ ++ nft_mon_print(monh, "%s rule %s %s %s ", ++ cmd, ++ family, ++ r->handle.table.name, ++ r->handle.chain.name); + + switch (type) { + case NFT_MSG_NEWRULE: +- r = netlink_delinearize_rule(monh->ctx, nlr); +- nlr_for_each_set(nlr, rule_map_decompose_cb, NULL, +- monh->cache); +- +- nft_mon_print(monh, "add rule %s %s %s ", family, table, chain); + rule_print(r, monh->ctx->octx); +- nft_mon_print(monh, "\n"); + +- rule_free(r); + break; + case NFT_MSG_DELRULE: +- nft_mon_print(monh, "delete rule %s %s %s handle %u\n", +- family, table, chain, (unsigned int)handle); ++ nft_mon_print(monh, "handle %" PRIu64, ++ r->handle.handle.id); + break; + } + break; + case NFTNL_OUTPUT_JSON: +- nftnl_rule_fprintf(monh->ctx->octx->output_fp, nlr, +- monh->format, netlink_msg2nftnl_of(type)); +- nft_mon_print(monh, "\n"); ++ monitor_print_rule_json(monh, cmd, r); + break; + } +- ++ nft_mon_print(monh, "\n"); ++ rule_free(r); + nftnl_rule_free(nlr); + return MNL_CB_OK; + } +diff --git a/src/rule.c b/src/rule.c +index eb06302d4f223..3065cc5474bbf 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -812,8 +812,6 @@ void chain_print_plain(const struct chain *chain, struct output_ctx *octx) + } + if (octx->handle > 0) + nft_print(octx, " # handle %" PRIu64, chain->handle.handle.id); +- +- nft_print(octx, "\n"); + } + + struct table *table_alloc(void) +-- +2.19.0 + diff --git a/SOURCES/0006-tests-monitor-Test-JSON-output-as-well.patch b/SOURCES/0006-tests-monitor-Test-JSON-output-as-well.patch new file mode 100644 index 0000000..9fa9726 --- /dev/null +++ b/SOURCES/0006-tests-monitor-Test-JSON-output-as-well.patch @@ -0,0 +1,368 @@ +From 24b0395e8d26ed9f7eb1716249a8e07f9f84571d Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Thu, 11 Oct 2018 17:49:01 +0200 +Subject: [PATCH] tests: monitor: Test JSON output as well + +Enhance monitor test suite to test check JSON output as well. Note that +for now there is no support for --echo output testing with JSON. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 16694bdd4c01ae001b60fbc18d45200fffc84de5) + +Conflicts: + tests/monitor/testcases/object.t +-> Missing ct timeout support + +Signed-off-by: Phil Sutter +--- + tests/monitor/README | 27 +++++++++++----- + tests/monitor/run-tests.sh | 45 ++++++++++++++++++++++++-- + tests/monitor/testcases/object.t | 11 +++++++ + tests/monitor/testcases/set-maps.t | 4 +++ + tests/monitor/testcases/set-mixed.t | 7 ++++ + tests/monitor/testcases/set-multiple.t | 5 +++ + tests/monitor/testcases/set-simple.t | 19 +++++++++++ + tests/monitor/testcases/simple.t | 8 +++++ + 8 files changed, 115 insertions(+), 11 deletions(-) + +diff --git a/tests/monitor/README b/tests/monitor/README +index 9c5e37f5c75c9..39096a7fae078 100644 +--- a/tests/monitor/README ++++ b/tests/monitor/README +@@ -15,13 +15,14 @@ to be established manually, i.e. in order to test monitor output when adding a + chain, the table containing it has to be created first. In between each + testcase, rule set is flushed completely. + +-Input and output lines are prefixed by 'I' and 'O', respectively. The prefix has +-to be separated from the rest of the line by whitespace. Consecutive input lines +-are passed to 'nft' together, hence lead to a single transaction. ++Input lines are prefixed by 'I'. Multiple consecutive input lines are passed to ++'nft' together, hence lead to a single transaction. + +-Since in most cases output should be equal to input, there is a shortcut: If a +-line consists of 'O -' only, the test script uses all previous input lines as +-expected output directly. ++There are two types of output lines: Those for standard syntax, prefixed by 'O' ++and those for JSON output, prefixed by 'J'. For standard syntax output lines, ++there is a shortcut: If a line consists of 'O -' only, the test script uses all ++previous input lines as expected output directly. Of course this is not ++available for JSON output lines. + + Empty lines and those starting with '#' are ignored. + +@@ -29,8 +30,8 @@ Test Script Semantics + --------------------- + + The script iterates over all test case files, reading them line by line. It +-assumes that sections of 'I' lines alternate with sections of 'O' lines. After +-stripping the prefix, each line is appended to a temporary file. There are ++assumes that sections of 'I' lines alternate with sections of 'O'/'J' lines. ++After stripping the prefix, each line is appended to a temporary file. There are + separate files for input and output lines. + + If a set of input and output lines is complete (i.e. upon encountering either a +@@ -46,3 +47,13 @@ Note: Running 'nft monitor' in background is prone to race conditions. Hence + an artificial delay is introduced before calling 'nft -f' to allow for 'nft + monitor' to complete initialization and another one before comparing the output + to allow for 'nft monitor' to process the netlink events. ++ ++By default, only standard syntax is being tested for, i.e. 'J'-prefixed lines ++are simply ignored. If JSON testing was requested (by passing '-j' flag to the ++test script), 'O'-prefixed lines in turn are ignored. ++ ++There is one caveat with regards to JSON output: Since it always contains handle ++properties (if the given object possesses such) which is supposed to be ++arbitrary, there is a filter script which normalizes all handle values in ++monitor output to zero before comparison. Therefore expected output must have ++all handle properties present but with a value of zero. +diff --git a/tests/monitor/run-tests.sh b/tests/monitor/run-tests.sh +index 1adabda193949..f4089887b69aa 100755 +--- a/tests/monitor/run-tests.sh ++++ b/tests/monitor/run-tests.sh +@@ -3,6 +3,7 @@ + cd $(dirname $0) + nft=../../src/nft + debug=false ++test_json=false + + mydiff() { + diff -w -I '^# ' "$@" +@@ -47,9 +48,16 @@ echo_output_append() { + } + [[ "$*" =~ ^add|replace|insert ]] && echo "$*" >>$output_file + } ++json_output_filter() { # (filename) ++ # unify handle values ++ sed -i -e 's/\("handle":\) [0-9][0-9]*/\1 0/g' "$1" ++} + monitor_run_test() { + monitor_output=$(mktemp -p $testdir) +- $nft -nn monitor >$monitor_output & ++ monitor_args="" ++ $test_json && monitor_args="vm json" ++ ++ $nft -nn monitor $monitor_args >$monitor_output & + monitor_pid=$! + + sleep 0.5 +@@ -67,6 +75,7 @@ monitor_run_test() { + sleep 0.5 + kill $monitor_pid + wait >/dev/null 2>&1 ++ $test_json && json_output_filter $monitor_output + if ! mydiff -q $monitor_output $output_file >/dev/null 2>&1; then + echo "monitor output differs!" + mydiff -u $output_file $monitor_output +@@ -99,7 +108,33 @@ echo_run_test() { + touch $output_file + } + +-for variant in monitor echo; do ++while [ -n "$1" ]; do ++ case "$1" in ++ -d|--debug) ++ debug=true ++ shift ++ ;; ++ -j|--json) ++ test_json=true ++ shift ++ ;; ++ *) ++ echo "unknown option '$1'" ++ ;& ++ -h|--help) ++ echo "Usage: $(basename $0) [-j|--json] [-d|--debug]" ++ exit 1 ++ ;; ++ esac ++done ++ ++if $test_json; then ++ variants="monitor" ++else ++ variants="monitor echo" ++fi ++ ++for variant in $variants; do + run_test=${variant}_run_test + output_append=${variant}_output_append + +@@ -124,7 +159,11 @@ for variant in monitor echo; do + ;; + O) + input_complete=true +- $output_append "$line" ++ $test_json || $output_append "$line" ++ ;; ++ J) ++ input_complete=true ++ $test_json && $output_append "$line" + ;; + '#'|'') + # ignore comments and empty lines +diff --git a/tests/monitor/testcases/object.t b/tests/monitor/testcases/object.t +index 7b88409775796..e9a6d56ac546c 100644 +--- a/tests/monitor/testcases/object.t ++++ b/tests/monitor/testcases/object.t +@@ -1,27 +1,38 @@ + # first the setup + I add table ip t + O - ++J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} + + I add counter ip t c + O add counter ip t c { packets 0 bytes 0 } ++J {"add": {"counter": {"family": "ip", "name": "c", "table": "t", "handle": 0, "packets": 0, "bytes": 0}}} + + I delete counter ip t c + O - ++J {"delete": {"counter": {"family": "ip", "name": "c", "table": "t", "handle": 0, "packets": 0, "bytes": 0}}} + ++# FIXME: input/output shouldn't be asynchronous here + I add quota ip t q 25 mbytes + O add quota ip t q { 25 mbytes } ++J {"add": {"quota": {"family": "ip", "name": "q", "table": "t", "handle": 0, "bytes": 26214400, "used": 0, "inv": false}}} + + I delete quota ip t q + O - ++J {"delete": {"quota": {"family": "ip", "name": "q", "table": "t", "handle": 0, "bytes": 26214400, "used": 0, "inv": false}}} + ++# FIXME: input/output shouldn't be asynchronous here + I add limit ip t l rate 1/second + O add limit ip t l { rate 1/second } ++J {"add": {"limit": {"family": "ip", "name": "l", "table": "t", "handle": 0, "rate": 1, "per": "second", "burst": 5}}} + + I delete limit ip t l + O - ++J {"delete": {"limit": {"family": "ip", "name": "l", "table": "t", "handle": 0, "rate": 1, "per": "second", "burst": 5}}} + + I add ct helper ip t cth { type "sip" protocol tcp; l3proto ip; } + O - ++J {"add": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle": 0, "type": "sip", "protocol": "tcp", "l3proto": "ip"}}} + + I delete ct helper ip t cth + O - ++J {"delete": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle": 0, "type": "sip", "protocol": "tcp", "l3proto": "ip"}}} +diff --git a/tests/monitor/testcases/set-maps.t b/tests/monitor/testcases/set-maps.t +index 3d86720ec8136..acda480d86dbb 100644 +--- a/tests/monitor/testcases/set-maps.t ++++ b/tests/monitor/testcases/set-maps.t +@@ -2,9 +2,13 @@ + I add table ip t + I add map ip t portip { type inet_service: ipv4_addr; flags interval; } + O - ++J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} ++J {"add": {"map": {"family": "ip", "name": "portip", "table": "t", "type": "inet_service", "handle": 0, "map": "ipv4_addr", "flags": ["interval"]}}} + + I add element ip t portip { 80-100: 10.0.0.1 } + O - ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portip", "elem": {"set": [[{"range": [80, 100]}, "10.0.0.1"]]}}}} + + I add element ip t portip { 1024-65535: 10.0.0.1 } + O - ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portip", "elem": {"set": [[{"range": [1024, 65535]}, "10.0.0.1"]]}}}} +diff --git a/tests/monitor/testcases/set-mixed.t b/tests/monitor/testcases/set-mixed.t +index 9c1c5323f2e4e..08c20116de92f 100644 +--- a/tests/monitor/testcases/set-mixed.t ++++ b/tests/monitor/testcases/set-mixed.t +@@ -3,13 +3,20 @@ I add table ip t + I add set ip t portrange { type inet_service; flags interval; } + I add set ip t ports { type inet_service; } + O - ++J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} ++J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} ++J {"add": {"set": {"family": "ip", "name": "ports", "table": "t", "type": "inet_service", "handle": 0}}} + + # make sure concurrent adds work + I add element ip t portrange { 1024-65535 } + I add element ip t ports { 10 } + O - ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} ++J {"add": {"element": {"family": "ip", "table": "t", "name": "ports", "elem": {"set": [10]}}}} + + # delete items again + I delete element ip t portrange { 1024-65535 } + I delete element ip t ports { 10 } + O - ++J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} ++J {"delete": {"element": {"family": "ip", "table": "t", "name": "ports", "elem": {"set": [10]}}}} +diff --git a/tests/monitor/testcases/set-multiple.t b/tests/monitor/testcases/set-multiple.t +index ad91fac047fe8..bd7a6246a46b4 100644 +--- a/tests/monitor/testcases/set-multiple.t ++++ b/tests/monitor/testcases/set-multiple.t +@@ -3,8 +3,13 @@ I add table ip t + I add set ip t portrange { type inet_service; flags interval; } + I add set ip t portrange2 { type inet_service; flags interval; } + O - ++J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} ++J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} ++J {"add": {"set": {"family": "ip", "name": "portrange2", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} + + # make sure concurrent adds work + I add element ip t portrange { 1024-65535 } + I add element ip t portrange2 { 10-20 } + O - ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange2", "elem": {"set": [{"range": [10, 20]}]}}}} +diff --git a/tests/monitor/testcases/set-simple.t b/tests/monitor/testcases/set-simple.t +index ebff7cbda0c8b..8ca4f32463fd7 100644 +--- a/tests/monitor/testcases/set-simple.t ++++ b/tests/monitor/testcases/set-simple.t +@@ -2,15 +2,21 @@ + I add table ip t + I add set ip t portrange { type inet_service; flags interval; } + O - ++J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} ++J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} + + # adding some ranges + I add element ip t portrange { 1-10 } + O - ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1, 10]}]}}}} + I add element ip t portrange { 1024-65535 } + O - ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} + I add element ip t portrange { 20-30, 40-50 } + O add element ip t portrange { 20-30 } + O add element ip t portrange { 40-50 } ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [20, 30]}]}}}} ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [40, 50]}]}}}} + + # test flushing -> elements are removed in reverse + I flush set ip t portrange +@@ -18,25 +24,38 @@ O delete element ip t portrange { 1024-65535 } + O delete element ip t portrange { 40-50 } + O delete element ip t portrange { 20-30 } + O delete element ip t portrange { 1-10 } ++J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} ++J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [40, 50]}]}}}} ++J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [20, 30]}]}}}} ++J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1, 10]}]}}}} + + # make sure lower scope boundary works + I add element ip t portrange { 0-10 } + O - ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [0, 10]}]}}}} + + # make sure half open before other element works + I add element ip t portrange { 1024-65535 } + I add element ip t portrange { 100-200 } + O - ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [100, 200]}]}}}} + + # make sure deletion of elements works + I delete element ip t portrange { 0-10 } + O - ++J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [0, 10]}]}}}} + I delete element ip t portrange { 100-200 } + I delete element ip t portrange { 1024-65535 } + O - ++J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [100, 200]}]}}}} ++J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} + + # make sure mixed add/delete works + I add element ip t portrange { 10-20 } + I add element ip t portrange { 1024-65535 } + I delete element ip t portrange { 10-20 } + O - ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [10, 20]}]}}}} ++J {"add": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [1024, 65535]}]}}}} ++J {"delete": {"element": {"family": "ip", "table": "t", "name": "portrange", "elem": {"set": [{"range": [10, 20]}]}}}} +diff --git a/tests/monitor/testcases/simple.t b/tests/monitor/testcases/simple.t +index e4dc073e14b65..78d8f8b04bd36 100644 +--- a/tests/monitor/testcases/simple.t ++++ b/tests/monitor/testcases/simple.t +@@ -2,19 +2,27 @@ + I add table ip t + I add chain ip t c + O - ++J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} ++J {"add": {"chain": {"family": "ip", "table": "t", "name": "c", "handle": 0}}} + + I add rule ip t c accept + O - ++J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"accept": null}]}}} + + I add rule ip t c tcp dport { 22, 80, 443 } accept + O - ++J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": {"set": [22, 80, 443]}}}, {"accept": null}]}}} + + I insert rule ip t c counter accept + O add rule ip t c counter packets 0 bytes 0 accept ++J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"counter": {"packets": 0, "bytes": 0}}, {"accept": null}]}}} + + I replace rule ip t c handle 2 accept comment "foo bar" + O delete rule ip t c handle 2 + O add rule ip t c accept comment "foo bar" ++J {"delete": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"accept": null}]}}} ++J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "comment": "foo bar", "expr": [{"accept": null}]}}} + + I add counter ip t cnt + O add counter ip t cnt { packets 0 bytes 0 } ++J {"add": {"counter": {"family": "ip", "name": "cnt", "table": "t", "handle": 0, "packets": 0, "bytes": 0}}} +-- +2.19.0 + diff --git a/SOURCES/0007-segtree-bogus-range-via-get-set-element-on-existing-.patch b/SOURCES/0007-segtree-bogus-range-via-get-set-element-on-existing-.patch new file mode 100644 index 0000000..ab5d9fe --- /dev/null +++ b/SOURCES/0007-segtree-bogus-range-via-get-set-element-on-existing-.patch @@ -0,0 +1,148 @@ +From 0a9ad3152c4e4678c94502441f5b002fef4fbff2 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Mon, 1 Oct 2018 14:51:24 +0200 +Subject: [PATCH] segtree: bogus range via get set element on existing elements + + table ip x { + set y { + type inet_service + flags interval + elements = { 10, 20-30, 40, 50-60 } + } + } + + # nft get element x y { 20-40 } + table ip x { + set y { + type inet_service + flags interval + elements = { 20-40 } + } + } + +20 and 40 exist in the tree, but they are part of different ranges. +This patch adds a new get_set_decompose() function to validate that the +left and the right side of the range. + +Reported-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 95629758a5ec36313d839f8545fef0dc220408d8) +Signed-off-by: Phil Sutter +--- + include/expression.h | 2 +- + src/netlink.c | 5 +++-- + src/segtree.c | 48 ++++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 50 insertions(+), 5 deletions(-) + +diff --git a/include/expression.h b/include/expression.h +index 2bb51e531ecbe..885e9c43bb77f 100644 +--- a/include/expression.h ++++ b/include/expression.h +@@ -442,7 +442,7 @@ extern void interval_map_decompose(struct expr *set); + extern struct expr *get_set_intervals(const struct set *set, + const struct expr *init); + struct table; +-extern void get_set_decompose(struct table *table, struct set *set); ++extern int get_set_decompose(struct table *table, struct set *set); + + extern struct expr *mapping_expr_alloc(const struct location *loc, + struct expr *from, struct expr *to); +diff --git a/src/netlink.c b/src/netlink.c +index 864947b4d2f07..ca5e9b4a0f8a6 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -1353,8 +1353,9 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h, + nftnl_set_free(nls_out); + ctx->set = NULL; + +- if (set->flags & NFT_SET_INTERVAL) +- get_set_decompose(table, set); ++ if (set->flags & NFT_SET_INTERVAL && ++ get_set_decompose(table, set) < 0) ++ return -1; + + return 0; + } +diff --git a/src/segtree.c b/src/segtree.c +index 8a8aa71e8a6e2..288b01f420a48 100644 +--- a/src/segtree.c ++++ b/src/segtree.c +@@ -641,6 +641,42 @@ struct expr *get_set_intervals(const struct set *set, const struct expr *init) + return new_init; + } + ++static struct expr *get_set_interval_find(const struct table *table, ++ const char *set_name, ++ struct expr *left, ++ struct expr *right) ++{ ++ struct expr *range = NULL; ++ struct set *set; ++ mpz_t low, high; ++ struct expr *i; ++ ++ set = set_lookup(table, set_name); ++ mpz_init2(low, set->key->len); ++ mpz_init2(high, set->key->len); ++ ++ list_for_each_entry(i, &set->init->expressions, list) { ++ switch (i->key->ops->type) { ++ case EXPR_RANGE: ++ range_expr_value_low(low, i); ++ range_expr_value_high(high, i); ++ if (mpz_cmp(left->key->value, low) >= 0 && ++ mpz_cmp(right->key->value, high) <= 0) ++ range = range_expr_alloc(&internal_location, ++ expr_clone(left->key), ++ expr_clone(right->key)); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ mpz_clear(low); ++ mpz_clear(high); ++ ++ return range; ++} ++ + static struct expr *get_set_interval_end(const struct table *table, + const char *set_name, + struct expr *left) +@@ -675,7 +711,7 @@ static struct expr *get_set_interval_end(const struct table *table, + return left; + } + +-void get_set_decompose(struct table *table, struct set *set) ++int get_set_decompose(struct table *table, struct set *set) + { + struct expr *i, *next, *new; + struct expr *left = NULL; +@@ -688,7 +724,13 @@ void get_set_decompose(struct table *table, struct set *set) + list_del(&left->list); + list_del(&i->list); + mpz_sub_ui(i->key->value, i->key->value, 1); +- new = range_expr_alloc(&internal_location, left, i); ++ new = get_set_interval_find(table, set->handle.set.name, ++ left, i); ++ if (!new) { ++ errno = ENOENT; ++ return -1; ++ } ++ + compound_expr_add(new_init, new); + left = NULL; + } else { +@@ -707,6 +749,8 @@ void get_set_decompose(struct table *table, struct set *set) + } + + set->init = new_init; ++ ++ return 0; + } + + static bool range_is_prefix(const mpz_t range) +-- +2.19.0 + diff --git a/SOURCES/0008-segtree-disantangle-get_set_interval_end.patch b/SOURCES/0008-segtree-disantangle-get_set_interval_end.patch new file mode 100644 index 0000000..e9a5c04 --- /dev/null +++ b/SOURCES/0008-segtree-disantangle-get_set_interval_end.patch @@ -0,0 +1,109 @@ +From 40edcc35cbfca22d4cb471464eacc12cf7c5c28a Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Wed, 3 Oct 2018 12:09:09 +0200 +Subject: [PATCH] segtree: disantangle get_set_interval_end() + +This function overrides the left pointer. Instead update this function +to return the range that we found to enclose the left element. Note that +we may not find a closing right element - therefore, it is a standalone +element - in that case this function returns NULL. + +Reported-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 33eb4594a1c764776a46b48bc1a7d726b561359d) +Signed-off-by: Phil Sutter +--- + src/segtree.c | 37 ++++++++++++++++++++++--------------- + 1 file changed, 22 insertions(+), 15 deletions(-) + +diff --git a/src/segtree.c b/src/segtree.c +index 288b01f420a48..0d53c83fd837e 100644 +--- a/src/segtree.c ++++ b/src/segtree.c +@@ -681,9 +681,9 @@ static struct expr *get_set_interval_end(const struct table *table, + const char *set_name, + struct expr *left) + { ++ struct expr *i, *range = NULL; + struct set *set; + mpz_t low, high; +- struct expr *i; + + set = set_lookup(table, set_name); + mpz_init2(low, set->key->len); +@@ -694,9 +694,9 @@ static struct expr *get_set_interval_end(const struct table *table, + case EXPR_RANGE: + range_expr_value_low(low, i); + if (mpz_cmp(low, left->key->value) == 0) { +- left = range_expr_alloc(&internal_location, +- expr_clone(left->key), +- expr_clone(i->key->right)); ++ range = range_expr_alloc(&internal_location, ++ expr_clone(left->key), ++ expr_clone(i->key->right)); + break; + } + break; +@@ -708,12 +708,12 @@ static struct expr *get_set_interval_end(const struct table *table, + mpz_clear(low); + mpz_clear(high); + +- return left; ++ return range; + } + + int get_set_decompose(struct table *table, struct set *set) + { +- struct expr *i, *next, *new; ++ struct expr *i, *next, *range; + struct expr *left = NULL; + struct expr *new_init; + +@@ -724,28 +724,35 @@ int get_set_decompose(struct table *table, struct set *set) + list_del(&left->list); + list_del(&i->list); + mpz_sub_ui(i->key->value, i->key->value, 1); +- new = get_set_interval_find(table, set->handle.set.name, ++ range = get_set_interval_find(table, set->handle.set.name, + left, i); +- if (!new) { ++ if (!range) { + errno = ENOENT; + return -1; + } + +- compound_expr_add(new_init, new); ++ compound_expr_add(new_init, range); + left = NULL; + } else { + if (left) { +- left = get_set_interval_end(table, +- set->handle.set.name, +- left); +- compound_expr_add(new_init, left); ++ range = get_set_interval_end(table, ++ set->handle.set.name, ++ left); ++ if (range) ++ compound_expr_add(new_init, range); ++ else ++ compound_expr_add(new_init, ++ expr_clone(left)); + } + left = i; + } + } + if (left) { +- left = get_set_interval_end(table, set->handle.set.name, left); +- compound_expr_add(new_init, left); ++ range = get_set_interval_end(table, set->handle.set.name, left); ++ if (range) ++ compound_expr_add(new_init, left); ++ else ++ compound_expr_add(new_init, expr_clone(left)); + } + + set->init = new_init; +-- +2.19.0 + diff --git a/SOURCES/0009-segtree-memleak-in-get_set_decompose.patch b/SOURCES/0009-segtree-memleak-in-get_set_decompose.patch new file mode 100644 index 0000000..2f72aed --- /dev/null +++ b/SOURCES/0009-segtree-memleak-in-get_set_decompose.patch @@ -0,0 +1,38 @@ +From b18f069971e8e5c161e87ad1742b677ee4185bad Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Wed, 3 Oct 2018 12:16:40 +0200 +Subject: [PATCH] segtree: memleak in get_set_decompose() + +Release set content on error. Moreover, release input set content in +case we finally manage to decompose it. + +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit e08c8bbfeda80f3807ce3784558ce1fb6f0d2bc8) +Signed-off-by: Phil Sutter +--- + src/segtree.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/segtree.c b/src/segtree.c +index 0d53c83fd837e..dc2554b72acf3 100644 +--- a/src/segtree.c ++++ b/src/segtree.c +@@ -727,6 +727,7 @@ int get_set_decompose(struct table *table, struct set *set) + range = get_set_interval_find(table, set->handle.set.name, + left, i); + if (!range) { ++ expr_free(new_init); + errno = ENOENT; + return -1; + } +@@ -755,6 +756,7 @@ int get_set_decompose(struct table *table, struct set *set) + compound_expr_add(new_init, expr_clone(left)); + } + ++ expr_free(set->init); + set->init = new_init; + + return 0; +-- +2.19.0 + diff --git a/SOURCES/0010-segtree-stop-iteration-on-existing-elements-in-case-.patch b/SOURCES/0010-segtree-stop-iteration-on-existing-elements-in-case-.patch new file mode 100644 index 0000000..79bc36d --- /dev/null +++ b/SOURCES/0010-segtree-stop-iteration-on-existing-elements-in-case-.patch @@ -0,0 +1,60 @@ +From d1145542184ec34ce19f383ea3d361d3287651ee Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Wed, 3 Oct 2018 16:19:47 +0200 +Subject: [PATCH] segtree: stop iteration on existing elements in case range is + found + +No need to keep iterating once the range object has been allocated. + +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit a493147e60d350aca4197975281bf2ffe4cd1009) +Signed-off-by: Phil Sutter +--- + src/segtree.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/src/segtree.c b/src/segtree.c +index dc2554b72acf3..dc457d6bc1b7d 100644 +--- a/src/segtree.c ++++ b/src/segtree.c +@@ -661,16 +661,18 @@ static struct expr *get_set_interval_find(const struct table *table, + range_expr_value_low(low, i); + range_expr_value_high(high, i); + if (mpz_cmp(left->key->value, low) >= 0 && +- mpz_cmp(right->key->value, high) <= 0) ++ mpz_cmp(right->key->value, high) <= 0) { + range = range_expr_alloc(&internal_location, + expr_clone(left->key), + expr_clone(right->key)); ++ goto out; ++ } + break; + default: + break; + } + } +- ++out: + mpz_clear(low); + mpz_clear(high); + +@@ -697,14 +699,14 @@ static struct expr *get_set_interval_end(const struct table *table, + range = range_expr_alloc(&internal_location, + expr_clone(left->key), + expr_clone(i->key->right)); +- break; ++ goto out; + } + break; + default: + break; + } + } +- ++out: + mpz_clear(low); + mpz_clear(high); + +-- +2.19.0 + diff --git a/SOURCES/0011-segtree-incorrect-handling-of-last-element-in-get_se.patch b/SOURCES/0011-segtree-incorrect-handling-of-last-element-in-get_se.patch new file mode 100644 index 0000000..dd39952 --- /dev/null +++ b/SOURCES/0011-segtree-incorrect-handling-of-last-element-in-get_se.patch @@ -0,0 +1,32 @@ +From 0a80f39e125ba9bb41386607e0db04b271c0bcea Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Wed, 10 Oct 2018 15:41:04 +0200 +Subject: [PATCH] segtree: incorrect handling of last element in + get_set_decompose() + +Add range to the list of matching elements. + +Fixes: 95629758a5ec ("segtree: bogus range via get set element on existing elements") +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 98b047f231215817d87a49dfffbe7b4c0dc29b57) +Signed-off-by: Phil Sutter +--- + src/segtree.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/segtree.c b/src/segtree.c +index dc457d6bc1b7d..4ee09884cbde6 100644 +--- a/src/segtree.c ++++ b/src/segtree.c +@@ -753,7 +753,7 @@ int get_set_decompose(struct table *table, struct set *set) + if (left) { + range = get_set_interval_end(table, set->handle.set.name, left); + if (range) +- compound_expr_add(new_init, left); ++ compound_expr_add(new_init, range); + else + compound_expr_add(new_init, expr_clone(left)); + } +-- +2.19.0 + diff --git a/SOURCES/0012-segtree-set-proper-error-cause-on-existing-elements.patch b/SOURCES/0012-segtree-set-proper-error-cause-on-existing-elements.patch new file mode 100644 index 0000000..86fbda6 --- /dev/null +++ b/SOURCES/0012-segtree-set-proper-error-cause-on-existing-elements.patch @@ -0,0 +1,49 @@ +From 4c211953b1e6290653fa3bb151b85be29d02288f Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Wed, 10 Oct 2018 19:19:18 +0200 +Subject: [PATCH] segtree: set proper error cause on existing elements + +Adding new elements result in a confusing "Success" error message. + + # nft add element x y { 0-3 } + [...] + Error: Could not process rule: Success + add element x y { 0-3 } + ^^^^^^^^^^^^^^^^^^^^^^^^ + +after this patch, this reports: + + Error: Could not process rule: File exists + add element x y { 0-3 } + ^^^^^^^^^^^^^^^^^^^^^^^^ + +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 8ef0efb97006081e7f6054a950cb3614dd57729f) +Signed-off-by: Phil Sutter +--- + src/segtree.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/segtree.c b/src/segtree.c +index 4ee09884cbde6..5685618b3724a 100644 +--- a/src/segtree.c ++++ b/src/segtree.c +@@ -270,6 +270,7 @@ static int ei_insert(struct list_head *msgs, struct seg_tree *tree, + + return 0; + err: ++ errno = EEXIST; + return expr_binary_error(msgs, lei->expr, new->expr, + "conflicting intervals specified"); + } +@@ -371,6 +372,7 @@ static int set_overlap(struct list_head *msgs, const struct set *set, + + expr_error(msgs, new_intervals[i]->expr, + "interval overlaps with an existing one"); ++ errno = EEXIST; + ret = -1; + goto out; + } +-- +2.19.0 + diff --git a/SOURCES/0013-rule-fix-memleak-in-do_get_setelems.patch b/SOURCES/0013-rule-fix-memleak-in-do_get_setelems.patch new file mode 100644 index 0000000..5ccda60 --- /dev/null +++ b/SOURCES/0013-rule-fix-memleak-in-do_get_setelems.patch @@ -0,0 +1,42 @@ +From 1c305050b37bef63a255570c27f0eead0cb4b582 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Wed, 3 Oct 2018 16:05:32 +0200 +Subject: [PATCH] rule: fix memleak in do_get_setelems() + +Release set and elements in case of error. + +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 406d17db7e02f78d258edb38ac8571112ef8c767) +Signed-off-by: Phil Sutter +--- + src/rule.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/src/rule.c b/src/rule.c +index 3065cc5474bbf..a157ac91683cc 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -1911,17 +1911,15 @@ static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd, + /* Fetch from kernel the elements that have been requested .*/ + err = netlink_get_setelem(ctx, &cmd->handle, &cmd->location, + table, new_set, init); +- if (err < 0) +- return err; +- +- __do_list_set(ctx, cmd, table, new_set); ++ if (err >= 0) ++ __do_list_set(ctx, cmd, table, new_set); + + if (set->flags & NFT_SET_INTERVAL) + expr_free(init); + + set_free(new_set); + +- return 0; ++ return err; + } + + static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd) +-- +2.19.0 + diff --git a/SOURCES/0014-Fix-memleak-in-netlink_parse_fwd-error-path.patch b/SOURCES/0014-Fix-memleak-in-netlink_parse_fwd-error-path.patch new file mode 100644 index 0000000..9e88ef5 --- /dev/null +++ b/SOURCES/0014-Fix-memleak-in-netlink_parse_fwd-error-path.patch @@ -0,0 +1,50 @@ +From 3578231d0a44c4a7617c046d3ef8b3cb1299c05e Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 12 Oct 2018 12:54:09 +0200 +Subject: [PATCH] Fix memleak in netlink_parse_fwd() error path + +Make sure allocated 'stmt' is freed before returning to caller. + +Fixes: 30d45266bf38b ("expr: extend fwd statement to support address and family") +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 30541cb66e2de38eea04ab28cb14f298cce9d99f) +Signed-off-by: Phil Sutter +--- + src/netlink_delinearize.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c +index 31d62420d41c8..ae84512c56f3a 100644 +--- a/src/netlink_delinearize.c ++++ b/src/netlink_delinearize.c +@@ -1152,9 +1152,11 @@ static void netlink_parse_fwd(struct netlink_parse_ctx *ctx, + reg2 = netlink_parse_register(nle, NFTNL_EXPR_FWD_SREG_ADDR); + if (reg2) { + addr = netlink_get_register(ctx, loc, reg2); +- if (addr == NULL) +- return netlink_error(ctx, loc, +- "fwd statement has no output expression"); ++ if (addr == NULL) { ++ netlink_error(ctx, loc, ++ "fwd statement has no output expression"); ++ goto out_err; ++ } + + switch (stmt->fwd.family) { + case AF_INET: +@@ -1166,8 +1168,9 @@ static void netlink_parse_fwd(struct netlink_parse_ctx *ctx, + BYTEORDER_BIG_ENDIAN); + break; + default: +- return netlink_error(ctx, loc, +- "fwd statement has no family"); ++ netlink_error(ctx, loc, ++ "fwd statement has no family"); ++ goto out_err; + } + stmt->fwd.addr = addr; + } +-- +2.19.0 + diff --git a/SOURCES/0015-libnftables-Fix-memleak-in-nft_parse_bison_filename.patch b/SOURCES/0015-libnftables-Fix-memleak-in-nft_parse_bison_filename.patch new file mode 100644 index 0000000..9ecfeba --- /dev/null +++ b/SOURCES/0015-libnftables-Fix-memleak-in-nft_parse_bison_filename.patch @@ -0,0 +1,43 @@ +From eb770d79146941ce975c7b7caa9372b380978f98 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 12 Oct 2018 13:22:55 +0200 +Subject: [PATCH] libnftables: Fix memleak in nft_parse_bison_filename() + +Allocated scanner object leaks when returning to caller. For some odd +reason, this was missed by the commit referenced below. + +Fixes: bd82e03e15df8 ("libnftables: Move scanner object into struct nft_ctx") +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit a56fe55dbd3232e70514610a1c2df1d6b15b931f) +Signed-off-by: Phil Sutter +--- + src/libnftables.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/src/libnftables.c b/src/libnftables.c +index 5bc7ba0d210ab..91af169ca7190 100644 +--- a/src/libnftables.c ++++ b/src/libnftables.c +@@ -418,15 +418,14 @@ static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename, + struct list_head *msgs, struct list_head *cmds) + { + struct cmd *cmd; +- void *scanner; + int ret; + + parser_init(nft, nft->state, msgs, cmds); +- scanner = scanner_init(nft->state); +- if (scanner_read_file(scanner, filename, &internal_location) < 0) ++ nft->scanner = scanner_init(nft->state); ++ if (scanner_read_file(nft->scanner, filename, &internal_location) < 0) + return -1; + +- ret = nft_parse(nft, scanner, nft->state); ++ ret = nft_parse(nft, nft->scanner, nft->state); + if (ret != 0 || nft->state->nerrs > 0) + return -1; + +-- +2.19.0 + diff --git a/SOURCES/0016-src-pass-struct-nft_ctx-through-struct-eval_ctx.patch b/SOURCES/0016-src-pass-struct-nft_ctx-through-struct-eval_ctx.patch new file mode 100644 index 0000000..315511c --- /dev/null +++ b/SOURCES/0016-src-pass-struct-nft_ctx-through-struct-eval_ctx.patch @@ -0,0 +1,441 @@ +From 09a890d3653a87e3752a3ebfc3f5de597a679cc5 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Mon, 22 Oct 2018 12:38:35 +0200 +Subject: [PATCH] src: pass struct nft_ctx through struct eval_ctx + +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 00f777bfc414af1e1384a213adc91ae4ecf6cbdf) + +Conflicts: + src/evaluate.c +-> Missing osf support + +Signed-off-by: Phil Sutter +--- + include/rule.h | 5 +- + src/evaluate.c | 128 ++++++++++++++++++++++----------------------- + src/parser_bison.y | 5 +- + 3 files changed, 66 insertions(+), 72 deletions(-) + +diff --git a/include/rule.h b/include/rule.h +index 909ff36db80c1..b1d15c8725813 100644 +--- a/include/rule.h ++++ b/include/rule.h +@@ -556,16 +556,13 @@ extern void cmd_free(struct cmd *cmd); + * @pctx: payload context + */ + struct eval_ctx { +- struct mnl_socket *nf_sock; ++ struct nft_ctx *nft; + struct list_head *msgs; + struct cmd *cmd; + struct table *table; + struct rule *rule; + struct set *set; + struct stmt *stmt; +- struct nft_cache *cache; +- struct output_ctx *octx; +- unsigned int debug_mask; + struct expr_ctx ectx; + struct proto_ctx pctx; + }; +diff --git a/src/evaluate.c b/src/evaluate.c +index d18af34341b0d..5e9c6328fc692 100644 +--- a/src/evaluate.c ++++ b/src/evaluate.c +@@ -156,7 +156,7 @@ static struct table *table_lookup_global(struct eval_ctx *ctx) + if (ctx->table != NULL) + return ctx->table; + +- table = table_lookup(&ctx->cmd->handle, ctx->cache); ++ table = table_lookup(&ctx->cmd->handle, &ctx->nft->cache); + if (table == NULL) + return NULL; + +@@ -184,8 +184,8 @@ static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr) + } + break; + case SYMBOL_SET: +- ret = cache_update(ctx->nf_sock, ctx->cache, ctx->cmd->op, +- ctx->msgs, ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, ctx->cmd->op, ++ ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +@@ -1731,13 +1731,13 @@ static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp) + + static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr) + { +- if (ctx->debug_mask & NFT_DEBUG_EVALUATION) { ++ if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) { + struct error_record *erec; + erec = erec_create(EREC_INFORMATIONAL, &(*expr)->location, + "Evaluate %s", (*expr)->ops->name); +- erec_print(ctx->octx, erec, ctx->debug_mask); +- expr_print(*expr, ctx->octx); +- nft_print(ctx->octx, "\n\n"); ++ erec_print(&ctx->nft->output, erec, ctx->nft->debug_mask); ++ expr_print(*expr, &ctx->nft->output); ++ nft_print(&ctx->nft->output, "\n\n"); + erec_destroy(erec); + } + +@@ -2717,13 +2717,13 @@ static int stmt_evaluate_objref(struct eval_ctx *ctx, struct stmt *stmt) + + int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt) + { +- if (ctx->debug_mask & NFT_DEBUG_EVALUATION) { ++ if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) { + struct error_record *erec; + erec = erec_create(EREC_INFORMATIONAL, &stmt->location, + "Evaluate %s", stmt->ops->name); +- erec_print(ctx->octx, erec, ctx->debug_mask); +- stmt_print(stmt, ctx->octx); +- nft_print(ctx->octx, "\n\n"); ++ erec_print(&ctx->nft->output, erec, ctx->nft->debug_mask); ++ stmt_print(stmt, &ctx->nft->output); ++ nft_print(&ctx->nft->output, "\n\n"); + erec_destroy(erec); + } + +@@ -2895,12 +2895,12 @@ static int rule_translate_index(struct eval_ctx *ctx, struct rule *rule) + int ret; + + /* update cache with CMD_LIST so that rules are fetched, too */ +- ret = cache_update(ctx->nf_sock, ctx->cache, CMD_LIST, +- ctx->msgs, ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, CMD_LIST, ++ ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +- table = table_lookup(&rule->handle, ctx->cache); ++ table = table_lookup(&rule->handle, &ctx->nft->cache); + if (!table) + return cmd_error(ctx, &rule->handle.table.location, + "Could not process rule: %s", +@@ -2931,7 +2931,7 @@ static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule) + struct stmt *stmt, *tstmt = NULL; + struct error_record *erec; + +- proto_ctx_init(&ctx->pctx, rule->handle.family, ctx->debug_mask); ++ proto_ctx_init(&ctx->pctx, rule->handle.family, ctx->nft->debug_mask); + memset(&ctx->ectx, 0, sizeof(ctx->ectx)); + + ctx->rule = rule; +@@ -3047,13 +3047,13 @@ static int table_evaluate(struct eval_ctx *ctx, struct table *table) + struct chain *chain; + struct set *set; + +- if (table_lookup(&ctx->cmd->handle, ctx->cache) == NULL) { ++ if (table_lookup(&ctx->cmd->handle, &ctx->nft->cache) == NULL) { + if (table == NULL) { + table = table_alloc(); + handle_merge(&table->handle, &ctx->cmd->handle); +- table_add_hash(table, ctx->cache); ++ table_add_hash(table, &ctx->nft->cache); + } else { +- table_add_hash(table_get(table), ctx->cache); ++ table_add_hash(table_get(table), &ctx->nft->cache); + } + } + +@@ -3088,15 +3088,15 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) + + switch (cmd->obj) { + case CMD_OBJ_SETELEM: +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, +- ctx->msgs, ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ++ ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + + return setelem_evaluate(ctx, &cmd->expr); + case CMD_OBJ_SET: +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, +- ctx->msgs, ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ++ ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +@@ -3106,8 +3106,8 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) + handle_merge(&cmd->rule->handle, &cmd->handle); + return rule_evaluate(ctx, cmd->rule); + case CMD_OBJ_CHAIN: +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, +- ctx->msgs, ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ++ ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +@@ -3115,8 +3115,8 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) + case CMD_OBJ_TABLE: + return table_evaluate(ctx, cmd->table); + case CMD_OBJ_FLOWTABLE: +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, +- ctx->msgs, ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ++ ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +@@ -3138,8 +3138,8 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd) + + switch (cmd->obj) { + case CMD_OBJ_SETELEM: +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, +- ctx->msgs, ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ++ ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +@@ -3165,14 +3165,14 @@ static int cmd_evaluate_get(struct eval_ctx *ctx, struct cmd *cmd) + struct set *set; + int ret; + +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, ctx->msgs, +- ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, ++ ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + + switch (cmd->obj) { + case CMD_OBJ_SETELEM: +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &ctx->cmd->handle.table.location, + "Could not process rule: %s", +@@ -3197,7 +3197,7 @@ static int cmd_evaluate_list_obj(struct eval_ctx *ctx, const struct cmd *cmd, + if (obj_type == NFT_OBJECT_UNSPEC) + obj_type = NFT_OBJECT_COUNTER; + +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &cmd->handle.table.location, + "Could not process rule: %s", +@@ -3215,8 +3215,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd) + struct set *set; + int ret; + +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, ctx->msgs, +- ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, ++ ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +@@ -3225,14 +3225,14 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd) + if (cmd->handle.table.name == NULL) + return 0; + +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &cmd->handle.table.location, + "Could not process rule: %s", + strerror(ENOENT)); + return 0; + case CMD_OBJ_SET: +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &cmd->handle.table.location, + "Could not process rule: %s", +@@ -3244,7 +3244,7 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd) + strerror(ENOENT)); + return 0; + case CMD_OBJ_METER: +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &cmd->handle.table.location, + "Could not process rule: %s", +@@ -3256,7 +3256,7 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd) + strerror(ENOENT)); + return 0; + case CMD_OBJ_MAP: +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &cmd->handle.table.location, + "Could not process rule: %s", +@@ -3268,7 +3268,7 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd) + strerror(ENOENT)); + return 0; + case CMD_OBJ_CHAIN: +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &cmd->handle.table.location, + "Could not process rule: %s", +@@ -3294,7 +3294,7 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd) + case CMD_OBJ_FLOWTABLES: + if (cmd->handle.table.name == NULL) + return 0; +- if (table_lookup(&cmd->handle, ctx->cache) == NULL) ++ if (table_lookup(&cmd->handle, &ctx->nft->cache) == NULL) + return cmd_error(ctx, &cmd->handle.table.location, + "Could not process rule: %s", + strerror(ENOENT)); +@@ -3313,8 +3313,8 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd) + { + int ret; + +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, ctx->msgs, +- ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, ++ ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +@@ -3325,7 +3325,7 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd) + case CMD_OBJ_QUOTAS: + if (cmd->handle.table.name == NULL) + return 0; +- if (table_lookup(&cmd->handle, ctx->cache) == NULL) ++ if (table_lookup(&cmd->handle, &ctx->nft->cache) == NULL) + return cmd_error(ctx, &cmd->handle.table.location, + "Could not process rule: %s", + strerror(ENOENT)); +@@ -3343,8 +3343,8 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd) + + switch (cmd->obj) { + case CMD_OBJ_RULESET: +- cache_flush(ctx->nf_sock, ctx->cache, cmd->op, ctx->msgs, +- ctx->debug_mask, ctx->octx); ++ cache_flush(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, ++ ctx->nft->debug_mask, &ctx->nft->output); + break; + case CMD_OBJ_TABLE: + /* Flushing a table does not empty the sets in the table nor remove +@@ -3354,12 +3354,12 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd) + /* Chains don't hold sets */ + break; + case CMD_OBJ_SET: +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, ctx->msgs, +- ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, ++ ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &cmd->handle.table.location, + "Could not process rule: %s", +@@ -3371,12 +3371,12 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd) + strerror(ENOENT)); + return 0; + case CMD_OBJ_MAP: +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, ctx->msgs, +- ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, ++ ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &ctx->cmd->handle.table.location, + "Could not process rule: %s", +@@ -3388,12 +3388,12 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd) + strerror(ENOENT)); + return 0; + case CMD_OBJ_METER: +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, ctx->msgs, +- ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, ++ ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &ctx->cmd->handle.table.location, + "Could not process rule: %s", +@@ -3417,12 +3417,12 @@ static int cmd_evaluate_rename(struct eval_ctx *ctx, struct cmd *cmd) + + switch (cmd->obj) { + case CMD_OBJ_CHAIN: +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, +- ctx->msgs, ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ++ ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +- table = table_lookup(&ctx->cmd->handle, ctx->cache); ++ table = table_lookup(&ctx->cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &ctx->cmd->handle.table.location, + "Could not process rule: %s", +@@ -3517,8 +3517,8 @@ static int cmd_evaluate_monitor(struct eval_ctx *ctx, struct cmd *cmd) + uint32_t event; + int ret; + +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->op, ctx->msgs, +- ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, ++ ctx->nft->debug_mask, &ctx->nft->output); + if (ret < 0) + return ret; + +@@ -3543,8 +3543,8 @@ static int cmd_evaluate_export(struct eval_ctx *ctx, struct cmd *cmd) + return cmd_error(ctx, &cmd->location, + "this output type is not supported"); + +- return cache_update(ctx->nf_sock, ctx->cache, cmd->op, ctx->msgs, +- ctx->debug_mask, ctx->octx); ++ return cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, ++ ctx->nft->debug_mask, &ctx->nft->output); + } + + static int cmd_evaluate_import(struct eval_ctx *ctx, struct cmd *cmd) +@@ -3582,13 +3582,13 @@ static const char *cmd_op_to_name(enum cmd_ops op) + + int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd) + { +- if (ctx->debug_mask & NFT_DEBUG_EVALUATION) { ++ if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) { + struct error_record *erec; + + erec = erec_create(EREC_INFORMATIONAL, &cmd->location, + "Evaluate %s", cmd_op_to_name(cmd->op)); +- erec_print(ctx->octx, erec, ctx->debug_mask); +- nft_print(ctx->octx, "\n\n"); ++ erec_print(&ctx->nft->output, erec, ctx->nft->debug_mask); ++ nft_print(&ctx->nft->output, "\n\n"); + erec_destroy(erec); + } + +diff --git a/src/parser_bison.y b/src/parser_bison.y +index 33915ed8702a6..d75cd50fa29b9 100644 +--- a/src/parser_bison.y ++++ b/src/parser_bison.y +@@ -44,11 +44,8 @@ void parser_init(struct nft_ctx *nft, struct parser_state *state, + state->msgs = msgs; + state->cmds = cmds; + state->scopes[0] = scope_init(&state->top_scope, NULL); +- state->ectx.cache = &nft->cache; ++ state->ectx.nft = nft; + state->ectx.msgs = msgs; +- state->ectx.nf_sock = nft->nf_sock; +- state->ectx.debug_mask = nft->debug_mask; +- state->ectx.octx = &nft->output; + } + + static void yyerror(struct location *loc, struct nft_ctx *nft, void *scanner, +-- +2.19.0 + diff --git a/SOURCES/0017-src-trace-fix-policy-printing.patch b/SOURCES/0017-src-trace-fix-policy-printing.patch new file mode 100644 index 0000000..3a28bd2 --- /dev/null +++ b/SOURCES/0017-src-trace-fix-policy-printing.patch @@ -0,0 +1,80 @@ +From 244a83f9e0809177f07fb7b7474f84a126cf827f Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Wed, 20 Jun 2018 23:06:04 +0200 +Subject: [PATCH] src: trace: fix policy printing + +policy type is erronously handled via verdict, this is wrong. +It is a different event type and needs to be handled as such. + +before: +trace id 42b54e71 inet filter input packet: iif "lo" ip saddr 127.0.0.1 .. +trace id 42b54e71 inet filter input rule ip protocol icmp nftrace set 1 (verdict continue) +trace id 42b54e71 inet filter input verdict continue +trace id 42b54e71 inet filter input + +after: +trace id 9f40c5c7 inet filter input packet: iif "lo" ip saddr 127.0.0.1 .. +trace id 9f40c5c7 inet filter input rule ip protocol icmp nftrace set 1 (verdict continue) +trace id 9f40c5c7 inet filter input verdict continue +trace id 9f40c5c7 inet filter input policy drop + +Reported-by: vtol@gmx.net +Signed-off-by: Florian Westphal +(cherry picked from commit 78ba4ffdeacc9b31f7396d72c98907e861024653) +Signed-off-by: Phil Sutter +--- + src/netlink.c | 31 ++++++++++++++++++++++++++++++- + 1 file changed, 30 insertions(+), 1 deletion(-) + +diff --git a/src/netlink.c b/src/netlink.c +index ca5e9b4a0f8a6..4fd3f2dfefced 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -1705,7 +1705,22 @@ static void trace_print_verdict(const struct nftnl_trace *nlt, + chain = xstrdup(nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET)); + expr = verdict_expr_alloc(&netlink_location, verdict, chain); + +- printf("verdict "); ++ nft_print(octx, "verdict "); ++ expr_print(expr, octx); ++ expr_free(expr); ++} ++ ++static void trace_print_policy(const struct nftnl_trace *nlt, ++ struct output_ctx *octx) ++{ ++ unsigned int policy; ++ struct expr *expr; ++ ++ policy = nftnl_trace_get_u32(nlt, NFTNL_TRACE_POLICY); ++ ++ expr = verdict_expr_alloc(&netlink_location, policy, NULL); ++ ++ nft_print(octx, "policy "); + expr_print(expr, octx); + expr_free(expr); + } +@@ -1921,6 +1936,20 @@ int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, + trace_print_rule(nlt, monh->ctx->octx, monh->cache); + break; + case NFT_TRACETYPE_POLICY: ++ trace_print_hdr(nlt, monh->ctx->octx); ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_POLICY)) { ++ trace_print_policy(nlt, monh->ctx->octx); ++ nft_mon_print(monh, " "); ++ } ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK)) ++ trace_print_expr(nlt, NFTNL_TRACE_MARK, ++ meta_expr_alloc(&netlink_location, ++ NFT_META_MARK), ++ monh->ctx->octx); ++ nft_mon_print(monh, "\n"); ++ break; + case NFT_TRACETYPE_RETURN: + trace_print_hdr(nlt, monh->ctx->octx); + +-- +2.19.0 + diff --git a/SOURCES/0018-rule-list-only-the-table-containing-object.patch b/SOURCES/0018-rule-list-only-the-table-containing-object.patch new file mode 100644 index 0000000..937b57d --- /dev/null +++ b/SOURCES/0018-rule-list-only-the-table-containing-object.patch @@ -0,0 +1,55 @@ +From 48169840569b45e7695b125935bf30967f30b10c Mon Sep 17 00:00:00 2001 +From: Harsha Sharma +Date: Sun, 8 Jul 2018 12:41:03 +0200 +Subject: [PATCH] rule: list only the table containing object + +For e.g. + + % nft list ct helper ip raw cthelp1 + table ip filter { + } + table ip raw { + ct helper cthelp1 { + type "ftp" protocol tcp + l3proto ip + } + } + +With this patch, print only table raw. + +Signed-off-by: Harsha Sharma +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 88456a7ef011728a98c950447630f0a128dcad13) +Signed-off-by: Phil Sutter +--- + src/rule.c | 12 +++++------- + 1 file changed, 5 insertions(+), 7 deletions(-) + +diff --git a/src/rule.c b/src/rule.c +index a157ac91683cc..3b5468d00e79c 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -1593,15 +1593,13 @@ static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type) + cmd->handle.family != table->handle.family) + continue; + +- nft_print(ctx->octx, "table %s %s {\n", +- family2str(table->handle.family), +- table->handle.table.name); +- + if (cmd->handle.table.name != NULL && +- strcmp(cmd->handle.table.name, table->handle.table.name)) { +- nft_print(ctx->octx, "}\n"); ++ !strcmp(cmd->handle.table.name, table->handle.table.name)) { ++ nft_print(ctx->octx, "table %s %s {\n", ++ family2str(table->handle.family), ++ cmd->handle.table.name); ++ } else + continue; +- } + + list_for_each_entry(obj, &table->objs, list) { + if (obj->type != type || +-- +2.19.0 + diff --git a/SOURCES/0019-src-pass-struct-nft_ctx-through-struct-netlink_ctx.patch b/SOURCES/0019-src-pass-struct-nft_ctx-through-struct-netlink_ctx.patch new file mode 100644 index 0000000..b2d401f --- /dev/null +++ b/SOURCES/0019-src-pass-struct-nft_ctx-through-struct-netlink_ctx.patch @@ -0,0 +1,1139 @@ +From da71df5d7e2602279cfe713be01bd402c699cd4e Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Mon, 22 Oct 2018 21:18:19 +0200 +Subject: [PATCH] src: pass struct nft_ctx through struct netlink_ctx + +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 2dc07bcd7eaa56369dff01b596acf010308007d3) + +Conflicts: + src/evaluate.c + src/mnl.c + src/nfnl_osf.c + src/rule.c +-> Missing osf support +-> Missing cleanups + +Signed-off-by: Phil Sutter +--- + include/netlink.h | 9 +-- + include/rule.h | 10 ++- + src/evaluate.c | 51 +++++---------- + src/libnftables.c | 8 +-- + src/mnl.c | 22 +++---- + src/monitor.c | 52 ++++++++-------- + src/netlink.c | 47 +++++++------- + src/netlink_delinearize.c | 4 +- + src/rule.c | 128 ++++++++++++++++++-------------------- + 9 files changed, 147 insertions(+), 184 deletions(-) + +diff --git a/include/netlink.h b/include/netlink.h +index d153e2be03ac5..31465ff16822e 100644 +--- a/include/netlink.h ++++ b/include/netlink.h +@@ -35,26 +35,21 @@ extern const struct location netlink_location; + /** + * struct netlink_ctx + * ++ * @nft: nftables context + * @msgs: message queue + * @list: list of parsed rules/chains/tables + * @set: current set + * @data: pointer to pass data to callback + * @seqnum: sequence number +- * @octx: output context +- * @debug_mask: display debugging information +- * @cache: cache context + */ + struct netlink_ctx { +- struct mnl_socket *nf_sock; ++ struct nft_ctx *nft; + struct list_head *msgs; + struct list_head list; + struct set *set; + const void *data; + uint32_t seqnum; + struct nftnl_batch *batch; +- unsigned int debug_mask; +- struct output_ctx *octx; +- struct nft_cache *cache; + }; + + extern struct nftnl_table *alloc_nftnl_table(const struct handle *h); +diff --git a/include/rule.h b/include/rule.h +index b1d15c8725813..12c2984a14362 100644 +--- a/include/rule.h ++++ b/include/rule.h +@@ -574,12 +574,10 @@ extern struct error_record *rule_postprocess(struct rule *rule); + struct netlink_ctx; + extern int do_command(struct netlink_ctx *ctx, struct cmd *cmd); + +-extern int cache_update(struct mnl_socket *nf_sock, struct nft_cache *cache, +- enum cmd_ops cmd, struct list_head *msgs, unsigned int debug_flag, +- struct output_ctx *octx); +-extern void cache_flush(struct mnl_socket *nf_sock, struct nft_cache *cache, +- enum cmd_ops cmd, struct list_head *msgs, +- unsigned int debug_mask, struct output_ctx *octx); ++extern int cache_update(struct nft_ctx *ctx, enum cmd_ops cmd, ++ struct list_head *msgs); ++extern void cache_flush(struct nft_ctx *ctx, enum cmd_ops cmd, ++ struct list_head *msgs); + extern void cache_release(struct nft_cache *cache); + + enum udata_type { +diff --git a/src/evaluate.c b/src/evaluate.c +index 5e9c6328fc692..809920748c0a9 100644 +--- a/src/evaluate.c ++++ b/src/evaluate.c +@@ -184,8 +184,7 @@ static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr) + } + break; + case SYMBOL_SET: +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, ctx->cmd->op, +- ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, ctx->cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -2895,8 +2894,7 @@ static int rule_translate_index(struct eval_ctx *ctx, struct rule *rule) + int ret; + + /* update cache with CMD_LIST so that rules are fetched, too */ +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, CMD_LIST, +- ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, CMD_LIST, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3088,15 +3086,13 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) + + switch (cmd->obj) { + case CMD_OBJ_SETELEM: +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, +- ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + + return setelem_evaluate(ctx, &cmd->expr); + case CMD_OBJ_SET: +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, +- ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3106,8 +3102,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) + handle_merge(&cmd->rule->handle, &cmd->handle); + return rule_evaluate(ctx, cmd->rule); + case CMD_OBJ_CHAIN: +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, +- ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3115,8 +3110,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) + case CMD_OBJ_TABLE: + return table_evaluate(ctx, cmd->table); + case CMD_OBJ_FLOWTABLE: +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, +- ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3138,8 +3132,7 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd) + + switch (cmd->obj) { + case CMD_OBJ_SETELEM: +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, +- ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3165,8 +3158,7 @@ static int cmd_evaluate_get(struct eval_ctx *ctx, struct cmd *cmd) + struct set *set; + int ret; + +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, +- ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3215,8 +3207,7 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd) + struct set *set; + int ret; + +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, +- ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3313,8 +3304,7 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd) + { + int ret; + +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, +- ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3343,8 +3333,7 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd) + + switch (cmd->obj) { + case CMD_OBJ_RULESET: +- cache_flush(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, +- ctx->nft->debug_mask, &ctx->nft->output); ++ cache_flush(ctx->nft, cmd->op, ctx->msgs); + break; + case CMD_OBJ_TABLE: + /* Flushing a table does not empty the sets in the table nor remove +@@ -3354,8 +3343,7 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd) + /* Chains don't hold sets */ + break; + case CMD_OBJ_SET: +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, +- ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3371,8 +3359,7 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd) + strerror(ENOENT)); + return 0; + case CMD_OBJ_MAP: +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, +- ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3388,8 +3375,7 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd) + strerror(ENOENT)); + return 0; + case CMD_OBJ_METER: +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, +- ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3417,8 +3403,7 @@ static int cmd_evaluate_rename(struct eval_ctx *ctx, struct cmd *cmd) + + switch (cmd->obj) { + case CMD_OBJ_CHAIN: +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, +- ctx->msgs, ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3517,8 +3502,7 @@ static int cmd_evaluate_monitor(struct eval_ctx *ctx, struct cmd *cmd) + uint32_t event; + int ret; + +- ret = cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, +- ctx->nft->debug_mask, &ctx->nft->output); ++ ret = cache_update(ctx->nft, cmd->op, ctx->msgs); + if (ret < 0) + return ret; + +@@ -3543,8 +3527,7 @@ static int cmd_evaluate_export(struct eval_ctx *ctx, struct cmd *cmd) + return cmd_error(ctx, &cmd->location, + "this output type is not supported"); + +- return cache_update(ctx->nft->nf_sock, &ctx->nft->cache, cmd->op, ctx->msgs, +- ctx->nft->debug_mask, &ctx->nft->output); ++ return cache_update(ctx->nft, cmd->op, ctx->msgs); + } + + static int cmd_evaluate_import(struct eval_ctx *ctx, struct cmd *cmd) +diff --git a/src/libnftables.c b/src/libnftables.c +index 91af169ca7190..848c9cba65657 100644 +--- a/src/libnftables.c ++++ b/src/libnftables.c +@@ -40,10 +40,7 @@ static int nft_netlink(struct nft_ctx *nft, + ctx.msgs = msgs; + ctx.seqnum = cmd->seqnum = mnl_seqnum_alloc(&seqnum); + ctx.batch = batch; +- ctx.octx = &nft->output; +- ctx.nf_sock = nf_sock; +- ctx.cache = &nft->cache; +- ctx.debug_mask = nft->debug_mask; ++ ctx.nft = nft; + init_list_head(&ctx.list); + ret = do_command(&ctx, cmd); + if (ret < 0) { +@@ -480,8 +477,7 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename) + LIST_HEAD(cmds); + int rc; + +- rc = cache_update(nft->nf_sock, &nft->cache, CMD_INVALID, &msgs, +- nft->debug_mask, &nft->output); ++ rc = cache_update(nft, CMD_INVALID, &msgs); + if (rc < 0) + return -1; + +diff --git a/src/mnl.c b/src/mnl.c +index 42eacab74e4e0..fd89ee7f28aaf 100644 +--- a/src/mnl.c ++++ b/src/mnl.c +@@ -51,13 +51,13 @@ nft_mnl_recv(struct netlink_ctx *ctx, uint32_t portid, + char buf[NFT_NLMSG_MAXSIZE]; + int ret; + +- ret = mnl_socket_recvfrom(ctx->nf_sock, buf, sizeof(buf)); ++ ret = mnl_socket_recvfrom(ctx->nft->nf_sock, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, ctx->seqnum, portid, cb, cb_data); + if (ret <= 0) + goto out; + +- ret = mnl_socket_recvfrom(ctx->nf_sock, buf, sizeof(buf)); ++ ret = mnl_socket_recvfrom(ctx->nft->nf_sock, buf, sizeof(buf)); + } + out: + if (ret < 0 && errno == EAGAIN) +@@ -70,13 +70,13 @@ static int + nft_mnl_talk(struct netlink_ctx *ctx, const void *data, unsigned int len, + int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data) + { +- uint32_t portid = mnl_socket_get_portid(ctx->nf_sock); ++ uint32_t portid = mnl_socket_get_portid(ctx->nft->nf_sock); + +- if (ctx->debug_mask & NFT_DEBUG_MNL) +- mnl_nlmsg_fprintf(ctx->octx->output_fp, data, len, ++ if (ctx->nft->debug_mask & NFT_DEBUG_MNL) ++ mnl_nlmsg_fprintf(ctx->nft->output.output_fp, data, len, + sizeof(struct nfgenmsg)); + +- if (mnl_socket_sendto(ctx->nf_sock, data, len) < 0) ++ if (mnl_socket_sendto(ctx->nft->nf_sock, data, len) < 0) + return -1; + + return nft_mnl_recv(ctx, portid, cb, cb_data); +@@ -225,23 +225,23 @@ static ssize_t mnl_nft_socket_sendmsg(const struct netlink_ctx *ctx) + }; + uint32_t i; + +- mnl_set_sndbuffer(ctx->nf_sock, ctx->batch); ++ mnl_set_sndbuffer(ctx->nft->nf_sock, ctx->batch); + nftnl_batch_iovec(ctx->batch, iov, iov_len); + + for (i = 0; i < iov_len; i++) { +- if (ctx->debug_mask & NFT_DEBUG_MNL) { +- mnl_nlmsg_fprintf(ctx->octx->output_fp, ++ if (ctx->nft->debug_mask & NFT_DEBUG_MNL) { ++ mnl_nlmsg_fprintf(ctx->nft->output.output_fp, + iov[i].iov_base, iov[i].iov_len, + sizeof(struct nfgenmsg)); + } + } + +- return sendmsg(mnl_socket_get_fd(ctx->nf_sock), &msg, 0); ++ return sendmsg(mnl_socket_get_fd(ctx->nft->nf_sock), &msg, 0); + } + + int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list) + { +- struct mnl_socket *nl = ctx->nf_sock; ++ struct mnl_socket *nl = ctx->nft->nf_sock; + int ret, fd = mnl_socket_get_fd(nl), portid = mnl_socket_get_portid(nl); + char rcv_buf[MNL_SOCKET_BUFFER_SIZE]; + fd_set readfds; +diff --git a/src/monitor.c b/src/monitor.c +index 213c40d119b4c..14ccbc5fe04ca 100644 +--- a/src/monitor.c ++++ b/src/monitor.c +@@ -40,7 +40,7 @@ + #include + #include + +-#define nft_mon_print(monh, ...) nft_print(monh->ctx->octx, __VA_ARGS__) ++#define nft_mon_print(monh, ...) nft_print(&monh->ctx->nft->output, __VA_ARGS__) + + static struct nftnl_table *netlink_table_alloc(const struct nlmsghdr *nlh) + { +@@ -214,7 +214,7 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type, + + nft_mon_print(monh, "%s %s", family2str(t->handle.family), + t->handle.table.name); +- if (monh->ctx->octx->handle > 0) ++ if (monh->ctx->nft->output.handle > 0) + nft_mon_print(monh, " # handle %" PRIu64 "", + t->handle.handle.id); + break; +@@ -245,7 +245,7 @@ static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type, + + switch (type) { + case NFT_MSG_NEWCHAIN: +- chain_print_plain(c, monh->ctx->octx); ++ chain_print_plain(c, &monh->ctx->nft->output); + break; + case NFT_MSG_DELCHAIN: + nft_mon_print(monh, "chain %s %s %s", +@@ -292,7 +292,7 @@ static int netlink_events_set_cb(const struct nlmsghdr *nlh, int type, + + switch (type) { + case NFT_MSG_NEWSET: +- set_print_plain(set, monh->ctx->octx); ++ set_print_plain(set, &monh->ctx->nft->output); + break; + case NFT_MSG_DELSET: + nft_mon_print(monh, "set %s %s %s", family, +@@ -386,7 +386,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type, + family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); + cmd = netlink_msg2cmd(type); + +- set = set_lookup_global(family, table, setname, monh->cache); ++ set = set_lookup_global(family, table, setname, &monh->ctx->nft->cache); + if (set == NULL) { + fprintf(stderr, "W: Received event for an unknown set."); + goto out; +@@ -417,7 +417,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type, + goto out; + } + if (netlink_delinearize_setelem(nlse, dummyset, +- monh->cache) < 0) { ++ &monh->ctx->nft->cache) < 0) { + set_free(dummyset); + nftnl_set_elems_iter_destroy(nlsei); + goto out; +@@ -435,7 +435,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type, + case NFTNL_OUTPUT_DEFAULT: + nft_mon_print(monh, "%s element %s %s %s ", + cmd, family2str(family), table, setname); +- expr_print(dummyset->init, monh->ctx->octx); ++ expr_print(dummyset->init, &monh->ctx->nft->output); + break; + case NFTNL_OUTPUT_JSON: + dummyset->handle.family = family; +@@ -477,7 +477,7 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type, + + switch (type) { + case NFT_MSG_NEWOBJ: +- obj_print_plain(obj, monh->ctx->octx); ++ obj_print_plain(obj, &monh->ctx->nft->output); + break; + case NFT_MSG_DELOBJ: + nft_mon_print(monh, "%s %s %s %s", +@@ -513,7 +513,8 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type, + + nlr = netlink_rule_alloc(nlh); + r = netlink_delinearize_rule(monh->ctx, nlr); +- nlr_for_each_set(nlr, rule_map_decompose_cb, NULL, monh->cache); ++ nlr_for_each_set(nlr, rule_map_decompose_cb, NULL, ++ &monh->ctx->nft->cache); + cmd = netlink_msg2cmd(type); + + switch (monh->format) { +@@ -528,7 +529,7 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type, + + switch (type) { + case NFT_MSG_NEWRULE: +- rule_print(r, monh->ctx->octx); ++ rule_print(r, &monh->ctx->nft->output); + + break; + case NFT_MSG_DELRULE: +@@ -557,7 +558,7 @@ static void netlink_events_cache_addtable(struct netlink_mon_handler *monh, + t = netlink_delinearize_table(monh->ctx, nlt); + nftnl_table_free(nlt); + +- table_add_hash(t, monh->cache); ++ table_add_hash(t, &monh->ctx->nft->cache); + } + + static void netlink_events_cache_deltable(struct netlink_mon_handler *monh, +@@ -571,7 +572,7 @@ static void netlink_events_cache_deltable(struct netlink_mon_handler *monh, + h.family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY); + h.table.name = nftnl_table_get_str(nlt, NFTNL_TABLE_NAME); + +- t = table_lookup(&h, monh->cache); ++ t = table_lookup(&h, &monh->ctx->nft->cache); + if (t == NULL) + goto out; + +@@ -601,7 +602,7 @@ static void netlink_events_cache_addset(struct netlink_mon_handler *monh, + goto out; + s->init = set_expr_alloc(monh->loc, s); + +- t = table_lookup(&s->handle, monh->cache); ++ t = table_lookup(&s->handle, &monh->ctx->nft->cache); + if (t == NULL) { + fprintf(stderr, "W: Unable to cache set: table not found.\n"); + set_free(s); +@@ -628,7 +629,7 @@ static void netlink_events_cache_addsetelem(struct netlink_mon_handler *monh, + table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); + setname = nftnl_set_get_str(nls, NFTNL_SET_NAME); + +- set = set_lookup_global(family, table, setname, monh->cache); ++ set = set_lookup_global(family, table, setname, &monh->ctx->nft->cache); + if (set == NULL) { + fprintf(stderr, + "W: Unable to cache set_elem. Set not found.\n"); +@@ -641,7 +642,8 @@ static void netlink_events_cache_addsetelem(struct netlink_mon_handler *monh, + + nlse = nftnl_set_elems_iter_next(nlsei); + while (nlse != NULL) { +- if (netlink_delinearize_setelem(nlse, set, monh->cache) < 0) { ++ if (netlink_delinearize_setelem(nlse, set, ++ &monh->ctx->nft->cache) < 0) { + fprintf(stderr, + "W: Unable to cache set_elem. " + "Delinearize failed.\n"); +@@ -668,7 +670,7 @@ static void netlink_events_cache_delsets(struct netlink_mon_handler *monh, + struct nftnl_rule *nlr = netlink_rule_alloc(nlh); + + nlr_for_each_set(nlr, netlink_events_cache_delset_cb, NULL, +- monh->cache); ++ &monh->ctx->nft->cache); + nftnl_rule_free(nlr); + } + +@@ -691,7 +693,7 @@ static void netlink_events_cache_addobj(struct netlink_mon_handler *monh, + if (obj == NULL) + goto out; + +- t = table_lookup(&obj->handle, monh->cache); ++ t = table_lookup(&obj->handle, &monh->ctx->nft->cache); + if (t == NULL) { + fprintf(stderr, "W: Unable to cache object: table not found.\n"); + obj_free(obj); +@@ -721,7 +723,7 @@ static void netlink_events_cache_delobj(struct netlink_mon_handler *monh, + type = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE); + h.handle.id = nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE); + +- t = table_lookup(&h, monh->cache); ++ t = table_lookup(&h, &monh->ctx->nft->cache); + if (t == NULL) { + fprintf(stderr, "W: Unable to cache object: table not found.\n"); + goto out; +@@ -835,7 +837,7 @@ static int netlink_events_newgen_cb(const struct nlmsghdr *nlh, int type, + nft_mon_print(monh, "# new generation %d", genid); + if (pid >= 0) { + nft_mon_print(monh, " by process %d", pid); +- if (!monh->ctx->octx->numeric) ++ if (!monh->ctx->nft->output.numeric) + nft_mon_print(monh, " (%s)", name); + } + nft_mon_print(monh, "\n"); +@@ -850,7 +852,7 @@ static int netlink_events_cb(const struct nlmsghdr *nlh, void *data) + uint16_t type = NFNL_MSG_TYPE(nlh->nlmsg_type); + struct netlink_mon_handler *monh = (struct netlink_mon_handler *)data; + +- netlink_events_debug(type, monh->debug_mask); ++ netlink_events_debug(type, monh->ctx->nft->debug_mask); + netlink_events_cache_update(monh, nlh, type); + + if (!(monh->monitor_flags & (1 << type))) +@@ -901,11 +903,9 @@ int netlink_echo_callback(const struct nlmsghdr *nlh, void *data) + .loc = &netlink_location, + .monitor_flags = 0xffffffff, + .cache_needed = true, +- .cache = ctx->cache, +- .debug_mask = ctx->debug_mask, + }; + +- if (!echo_monh.ctx->octx->echo) ++ if (!echo_monh.ctx->nft->output.echo) + return MNL_CB_OK; + + return netlink_events_cb(nlh, &echo_monh); +@@ -929,7 +929,7 @@ int netlink_monitor(struct netlink_mon_handler *monhandler, + return -1; + } + +- return mnl_nft_event_listener(nf_sock, monhandler->debug_mask, +- monhandler->ctx->octx, netlink_events_cb, +- monhandler); ++ return mnl_nft_event_listener(nf_sock, monhandler->ctx->nft->debug_mask, ++ &monhandler->ctx->nft->output, ++ netlink_events_cb, monhandler); + } +diff --git a/src/netlink.c b/src/netlink.c +index 4fd3f2dfefced..e16eb504fdef8 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -42,7 +42,7 @@ + #include + #include + +-#define nft_mon_print(monh, ...) nft_print(monh->ctx->octx, __VA_ARGS__) ++#define nft_mon_print(monh, ...) nft_print(&monh->ctx->nft->output, __VA_ARGS__) + + const struct input_descriptor indesc_netlink = { + .name = "netlink", +@@ -475,10 +475,8 @@ int netlink_replace_rule_batch(struct netlink_ctx *ctx, const struct cmd *cmd) + struct nftnl_rule *nlr; + int err, flags = 0; + +- if (ctx->octx->echo) { +- err = cache_update(ctx->nf_sock, ctx->cache, +- CMD_INVALID, ctx->msgs, +- ctx->debug_mask, ctx->octx); ++ if (ctx->nft->output.echo) { ++ err = cache_update(ctx->nft, CMD_INVALID, ctx->msgs); + if (err < 0) + return err; + +@@ -507,9 +505,9 @@ int netlink_del_rule_batch(struct netlink_ctx *ctx, const struct cmd *cmd) + + void netlink_dump_rule(const struct nftnl_rule *nlr, struct netlink_ctx *ctx) + { +- FILE *fp = ctx->octx->output_fp; ++ FILE *fp = ctx->nft->output.output_fp; + +- if (!(ctx->debug_mask & NFT_DEBUG_NETLINK) || !fp) ++ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_rule_fprintf(fp, nlr, 0, 0); +@@ -575,9 +573,9 @@ static int netlink_flush_rules(struct netlink_ctx *ctx, const struct cmd *cmd) + + void netlink_dump_chain(const struct nftnl_chain *nlc, struct netlink_ctx *ctx) + { +- FILE *fp = ctx->octx->output_fp; ++ FILE *fp = ctx->nft->output.output_fp; + +- if (!(ctx->debug_mask & NFT_DEBUG_NETLINK) || !fp) ++ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_chain_fprintf(fp, nlc, 0, 0); +@@ -837,9 +835,9 @@ static const struct datatype *dtype_map_from_kernel(enum nft_data_types type) + + void netlink_dump_set(const struct nftnl_set *nls, struct netlink_ctx *ctx) + { +- FILE *fp = ctx->octx->output_fp; ++ FILE *fp = ctx->nft->output.output_fp; + +- if (!(ctx->debug_mask & NFT_DEBUG_NETLINK) || !fp) ++ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_set_fprintf(fp, nls, 0, 0); +@@ -1291,7 +1289,7 @@ out: + static int list_setelem_cb(struct nftnl_set_elem *nlse, void *arg) + { + struct netlink_ctx *ctx = arg; +- return netlink_delinearize_setelem(nlse, ctx->set, ctx->cache); ++ return netlink_delinearize_setelem(nlse, ctx->set, &ctx->nft->cache); + } + + int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h, +@@ -1362,9 +1360,9 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h, + + void netlink_dump_obj(struct nftnl_obj *nln, struct netlink_ctx *ctx) + { +- FILE *fp = ctx->octx->output_fp; ++ FILE *fp = ctx->nft->output.output_fp; + +- if (!(ctx->debug_mask & NFT_DEBUG_NETLINK) || !fp) ++ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_obj_fprintf(fp, nln, 0, 0); +@@ -1476,9 +1474,9 @@ static struct nftnl_flowtable *alloc_nftnl_flowtable(const struct handle *h, + static void netlink_dump_flowtable(struct nftnl_flowtable *flo, + struct netlink_ctx *ctx) + { +- FILE *fp = ctx->octx->output_fp; ++ FILE *fp = ctx->nft->output.output_fp; + +- if (!(ctx->debug_mask & NFT_DEBUG_NETLINK) || !fp) ++ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_flowtable_fprintf(fp, flo, 0, 0); +@@ -1930,16 +1928,17 @@ int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, + case NFT_TRACETYPE_RULE: + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER) || + nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER)) +- trace_print_packet(nlt, monh->ctx->octx); ++ trace_print_packet(nlt, &monh->ctx->nft->output); + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_RULE_HANDLE)) +- trace_print_rule(nlt, monh->ctx->octx, monh->cache); ++ trace_print_rule(nlt, &monh->ctx->nft->output, ++ &monh->ctx->nft->cache); + break; + case NFT_TRACETYPE_POLICY: +- trace_print_hdr(nlt, monh->ctx->octx); ++ trace_print_hdr(nlt, &monh->ctx->nft->output); + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_POLICY)) { +- trace_print_policy(nlt, monh->ctx->octx); ++ trace_print_policy(nlt, &monh->ctx->nft->output); + nft_mon_print(monh, " "); + } + +@@ -1947,14 +1946,14 @@ int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, + trace_print_expr(nlt, NFTNL_TRACE_MARK, + meta_expr_alloc(&netlink_location, + NFT_META_MARK), +- monh->ctx->octx); ++ &monh->ctx->nft->output); + nft_mon_print(monh, "\n"); + break; + case NFT_TRACETYPE_RETURN: +- trace_print_hdr(nlt, monh->ctx->octx); ++ trace_print_hdr(nlt, &monh->ctx->nft->output); + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_VERDICT)) { +- trace_print_verdict(nlt, monh->ctx->octx); ++ trace_print_verdict(nlt, &monh->ctx->nft->output); + nft_mon_print(monh, " "); + } + +@@ -1962,7 +1961,7 @@ int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, + trace_print_expr(nlt, NFTNL_TRACE_MARK, + meta_expr_alloc(&netlink_location, + NFT_META_MARK), +- monh->ctx->octx); ++ &monh->ctx->nft->output); + nft_mon_print(monh, "\n"); + break; + } +diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c +index ae84512c56f3a..bf990e9e979d5 100644 +--- a/src/netlink_delinearize.c ++++ b/src/netlink_delinearize.c +@@ -2508,7 +2508,7 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx, + + memset(&_ctx, 0, sizeof(_ctx)); + _ctx.msgs = ctx->msgs; +- _ctx.debug_mask = ctx->debug_mask; ++ _ctx.debug_mask = ctx->nft->debug_mask; + + memset(&h, 0, sizeof(h)); + h.family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); +@@ -2520,7 +2520,7 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx, + h.position.id = nftnl_rule_get_u64(nlr, NFTNL_RULE_POSITION); + + pctx->rule = rule_alloc(&netlink_location, &h); +- pctx->table = table_lookup(&h, ctx->cache); ++ pctx->table = table_lookup(&h, &ctx->nft->cache); + assert(pctx->table != NULL); + + if (nftnl_rule_is_set(nlr, NFTNL_RULE_USERDATA)) { +diff --git a/src/rule.c b/src/rule.c +index 3b5468d00e79c..6acfa3ac1695c 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -86,7 +86,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, enum cmd_ops cmd) + struct set *set; + int ret; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + ret = netlink_list_sets(ctx, &table->handle); + list_splice_tail_init(&ctx->list, &table->sets); + +@@ -141,7 +141,7 @@ static int cache_init(struct netlink_ctx *ctx, enum cmd_ops cmd) + }; + int ret; + +- ret = cache_init_tables(ctx, &handle, ctx->cache); ++ ret = cache_init_tables(ctx, &handle, &ctx->nft->cache); + if (ret < 0) + return ret; + ret = cache_init_objects(ctx, cmd); +@@ -151,20 +151,18 @@ static int cache_init(struct netlink_ctx *ctx, enum cmd_ops cmd) + return 0; + } + +-int cache_update(struct mnl_socket *nf_sock, struct nft_cache *cache, +- enum cmd_ops cmd, struct list_head *msgs, unsigned int debug_mask, +- struct output_ctx *octx) ++int cache_update(struct nft_ctx *nft, enum cmd_ops cmd, struct list_head *msgs) + { + uint16_t genid; + int ret; + struct netlink_ctx ctx = { + .list = LIST_HEAD_INIT(ctx.list), +- .nf_sock = nf_sock, +- .cache = cache, ++ .nft = nft, + .msgs = msgs, +- .debug_mask = debug_mask, +- .octx = octx, ++ .nft = nft, + }; ++ struct mnl_socket *nf_sock = nft->nf_sock; ++ struct nft_cache *cache = &nft->cache; + + replay: + ctx.seqnum = cache->seqnum++; +@@ -197,18 +195,14 @@ static void __cache_flush(struct list_head *table_list) + } + } + +-void cache_flush(struct mnl_socket *nf_sock, struct nft_cache *cache, +- enum cmd_ops cmd, struct list_head *msgs, +- unsigned int debug_mask, struct output_ctx *octx) ++void cache_flush(struct nft_ctx *nft, enum cmd_ops cmd, struct list_head *msgs) + { + struct netlink_ctx ctx = { + .list = LIST_HEAD_INIT(ctx.list), +- .nf_sock = nf_sock, +- .cache = cache, ++ .nft = nft, + .msgs = msgs, +- .debug_mask = debug_mask, +- .octx = octx, + }; ++ struct nft_cache *cache = &nft->cache; + + __cache_flush(&cache->list); + cache->genid = netlink_genid_get(&ctx); +@@ -1121,12 +1115,12 @@ static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd, + struct table *table; + struct set *set; + +- table = table_lookup(h, ctx->cache); ++ table = table_lookup(h, &ctx->nft->cache); + set = set_lookup(table, h->set.name); + + if (set->flags & NFT_SET_INTERVAL && + set_to_intervals(ctx->msgs, set, init, true, +- ctx->debug_mask, set->automerge) < 0) ++ ctx->nft->debug_mask, set->automerge) < 0) + return -1; + + return __do_add_setelems(ctx, h, set, init, flags); +@@ -1140,7 +1134,7 @@ static int do_add_set(struct netlink_ctx *ctx, const struct cmd *cmd, + if (set->init != NULL) { + if (set->flags & NFT_SET_INTERVAL && + set_to_intervals(ctx->msgs, set, set->init, true, +- ctx->debug_mask, set->automerge) < 0) ++ ctx->nft->debug_mask, set->automerge) < 0) + return -1; + } + if (netlink_add_set_batch(ctx, cmd, flags) < 0) +@@ -1156,11 +1150,10 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl) + { + uint32_t flags = excl ? NLM_F_EXCL : 0; + +- if (ctx->octx->echo) { ++ if (ctx->nft->output.echo) { + int ret; + +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->obj, +- ctx->msgs, ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft, cmd->obj, ctx->msgs); + if (ret < 0) + return ret; + +@@ -1206,11 +1199,10 @@ static int do_command_insert(struct netlink_ctx *ctx, struct cmd *cmd) + { + uint32_t flags = 0; + +- if (ctx->octx->echo) { ++ if (ctx->nft->output.echo) { + int ret; + +- ret = cache_update(ctx->nf_sock, ctx->cache, cmd->obj, +- ctx->msgs, ctx->debug_mask, ctx->octx); ++ ret = cache_update(ctx->nft, cmd->obj, ctx->msgs); + if (ret < 0) + return ret; + +@@ -1233,12 +1225,12 @@ static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd) + struct table *table; + struct set *set; + +- table = table_lookup(h, ctx->cache); ++ table = table_lookup(h, &ctx->nft->cache); + set = set_lookup(table, h->set.name); + + if (set->flags & NFT_SET_INTERVAL && + set_to_intervals(ctx->msgs, set, expr, false, +- ctx->debug_mask, set->automerge) < 0) ++ ctx->nft->debug_mask, set->automerge) < 0) + return -1; + + if (netlink_delete_setelems_batch(ctx, cmd) < 0) +@@ -1278,7 +1270,7 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd) + static int do_command_export(struct netlink_ctx *ctx, struct cmd *cmd) + { + struct nftnl_ruleset *rs; +- FILE *fp = ctx->octx->output_fp; ++ FILE *fp = ctx->nft->output.output_fp; + + do { + rs = netlink_dump_ruleset(ctx, &cmd->handle, &cmd->location); +@@ -1288,7 +1280,7 @@ static int do_command_export(struct netlink_ctx *ctx, struct cmd *cmd) + + nftnl_ruleset_fprintf(fp, rs, cmd->markup->format, NFTNL_OF_EVENT_NEW); + +- nft_print(ctx->octx, "\n"); ++ nft_print(&ctx->nft->output, "\n"); + + nftnl_ruleset_free(rs); + return 0; +@@ -1319,7 +1311,7 @@ static int do_command_import(struct netlink_ctx *ctx, struct cmd *cmd) + static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd, + struct table *table) + { +- table_print(table, ctx->octx); ++ table_print(table, &ctx->nft->output); + return 0; + } + +@@ -1333,12 +1325,12 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd) + struct table *table; + struct set *set; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + +- nft_print(ctx->octx, "table %s %s {\n", ++ nft_print(&ctx->nft->output, "table %s %s {\n", + family2str(table->handle.family), + table->handle.table.name); + +@@ -1353,11 +1345,11 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd) + if (cmd->obj == CMD_OBJ_MAPS && + !(set->flags & NFT_SET_MAP)) + continue; +- set_print_declaration(set, &opts, ctx->octx); +- nft_print(ctx->octx, "%s}%s", opts.tab, opts.nl); ++ set_print_declaration(set, &opts, &ctx->nft->output); ++ nft_print(&ctx->nft->output, "%s}%s", opts.tab, opts.nl); + } + +- nft_print(ctx->octx, "}\n"); ++ nft_print(&ctx->nft->output, "}\n"); + } + return 0; + } +@@ -1588,14 +1580,14 @@ static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type) + struct table *table; + struct obj *obj; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + if (cmd->handle.table.name != NULL && + !strcmp(cmd->handle.table.name, table->handle.table.name)) { +- nft_print(ctx->octx, "table %s %s {\n", ++ nft_print(&ctx->nft->output, "table %s %s {\n", + family2str(table->handle.family), + cmd->handle.table.name); + } else +@@ -1607,10 +1599,10 @@ static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type) + strcmp(cmd->handle.obj.name, obj->handle.obj.name))) + continue; + +- obj_print_declaration(obj, &opts, ctx->octx); ++ obj_print_declaration(obj, &opts, &ctx->nft->output); + } + +- nft_print(ctx->octx, "}\n"); ++ nft_print(&ctx->nft->output, "}\n"); + } + return 0; + } +@@ -1705,21 +1697,21 @@ static int do_list_flowtables(struct netlink_ctx *ctx, struct cmd *cmd) + struct flowtable *flowtable; + struct table *table; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + +- nft_print(ctx->octx, "table %s %s {\n", ++ nft_print(&ctx->nft->output, "table %s %s {\n", + family2str(table->handle.family), + table->handle.table.name); + + list_for_each_entry(flowtable, &table->flowtables, list) { +- flowtable_print_declaration(flowtable, &opts, ctx->octx); +- nft_print(ctx->octx, "%s}%s", opts.tab, opts.nl); ++ flowtable_print_declaration(flowtable, &opts, &ctx->nft->output); ++ nft_print(&ctx->nft->output, "%s}%s", opts.tab, opts.nl); + } + +- nft_print(ctx->octx, "}\n"); ++ nft_print(&ctx->nft->output, "}\n"); + } + return 0; + } +@@ -1729,7 +1721,7 @@ static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd) + unsigned int family = cmd->handle.family; + struct table *table; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (family != NFPROTO_UNSPEC && + table->handle.family != family) + continue; +@@ -1750,12 +1742,12 @@ static int do_list_tables(struct netlink_ctx *ctx, struct cmd *cmd) + { + struct table *table; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + +- nft_print(ctx->octx, "table %s %s\n", ++ nft_print(&ctx->nft->output, "table %s %s\n", + family2str(table->handle.family), + table->handle.table.name); + } +@@ -1776,17 +1768,17 @@ static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd, + { + struct chain *chain; + +- table_print_declaration(table, ctx->octx); ++ table_print_declaration(table, &ctx->nft->output); + + list_for_each_entry(chain, &table->chains, list) { + if (chain->handle.family != cmd->handle.family || + strcmp(cmd->handle.chain.name, chain->handle.chain.name) != 0) + continue; + +- chain_print(chain, ctx->octx); ++ chain_print(chain, &ctx->nft->output); + } + +- nft_print(ctx->octx, "}\n"); ++ nft_print(&ctx->nft->output, "}\n"); + + return 0; + } +@@ -1796,18 +1788,18 @@ static int do_list_chains(struct netlink_ctx *ctx, struct cmd *cmd) + struct table *table; + struct chain *chain; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + +- table_print_declaration(table, ctx->octx); ++ table_print_declaration(table, &ctx->nft->output); + + list_for_each_entry(chain, &table->chains, list) { +- chain_print_declaration(chain, ctx->octx); +- nft_print(ctx->octx, "\t}\n"); ++ chain_print_declaration(chain, &ctx->nft->output); ++ nft_print(&ctx->nft->output, "\t}\n"); + } +- nft_print(ctx->octx, "}\n"); ++ nft_print(&ctx->nft->output, "}\n"); + } + + return 0; +@@ -1816,9 +1808,9 @@ static int do_list_chains(struct netlink_ctx *ctx, struct cmd *cmd) + static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, + struct table *table, struct set *set) + { +- table_print_declaration(table, ctx->octx); +- set_print(set, ctx->octx); +- nft_print(ctx->octx, "}\n"); ++ table_print_declaration(table, &ctx->nft->output); ++ set_print(set, &ctx->nft->output); ++ nft_print(&ctx->nft->output, "}\n"); + } + + static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, +@@ -1839,11 +1831,11 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) + { + struct table *table = NULL; + +- if (ctx->octx->json) ++ if (ctx->nft->output.json) + return do_command_list_json(ctx, cmd); + + if (cmd->handle.table.name != NULL) +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + + switch (cmd->obj) { + case CMD_OBJ_TABLE: +@@ -1925,7 +1917,7 @@ static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd) + struct table *table = NULL; + + if (cmd->handle.table.name != NULL) +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + + switch (cmd->obj) { + case CMD_OBJ_SETELEM: +@@ -1964,7 +1956,7 @@ static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd) + + ret = netlink_reset_objs(ctx, cmd, type, dump); + list_for_each_entry_safe(obj, next, &ctx->list, list) { +- table = table_lookup(&obj->handle, ctx->cache); ++ table = table_lookup(&obj->handle, &ctx->nft->cache); + list_move(&obj->list, &table->objs); + } + if (ret < 0) +@@ -1994,7 +1986,7 @@ static int do_command_flush(struct netlink_ctx *ctx, struct cmd *cmd) + + static int do_command_rename(struct netlink_ctx *ctx, struct cmd *cmd) + { +- struct table *table = table_lookup(&cmd->handle, ctx->cache); ++ struct table *table = table_lookup(&cmd->handle, &ctx->nft->cache); + struct chain *chain; + + switch (cmd->obj) { +@@ -2034,8 +2026,8 @@ static int do_command_monitor(struct netlink_ctx *ctx, struct cmd *cmd) + .format = cmd->monitor->format, + .ctx = ctx, + .loc = &cmd->location, +- .cache = ctx->cache, +- .debug_mask = ctx->debug_mask, ++ .cache = &ctx->nft->cache, ++ .debug_mask = ctx->nft->debug_mask, + }; + + monhandler.cache_needed = need_cache(cmd); +@@ -2044,7 +2036,7 @@ static int do_command_monitor(struct netlink_ctx *ctx, struct cmd *cmd) + struct chain *chain; + int ret; + +- list_for_each_entry(t, &ctx->cache->list, list) { ++ list_for_each_entry(t, &ctx->nft->cache.list, list) { + list_for_each_entry(s, &t->sets, list) + s->init = set_expr_alloc(&cmd->location, s); + +@@ -2070,7 +2062,7 @@ static int do_command_monitor(struct netlink_ctx *ctx, struct cmd *cmd) + } + } + +- return netlink_monitor(&monhandler, ctx->nf_sock); ++ return netlink_monitor(&monhandler, ctx->nft->nf_sock); + } + + static int do_command_describe(struct netlink_ctx *ctx, struct cmd *cmd, +@@ -2129,7 +2121,7 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd) + case CMD_MONITOR: + return do_command_monitor(ctx, cmd); + case CMD_DESCRIBE: +- return do_command_describe(ctx, cmd, ctx->octx); ++ return do_command_describe(ctx, cmd, &ctx->nft->output); + default: + BUG("invalid command object type %u\n", cmd->obj); + } +-- +2.19.0 + diff --git a/SOURCES/0020-netlink-reset-mnl_socket-field-in-struct-nft_ctx-on-.patch b/SOURCES/0020-netlink-reset-mnl_socket-field-in-struct-nft_ctx-on-.patch new file mode 100644 index 0000000..b28e35e --- /dev/null +++ b/SOURCES/0020-netlink-reset-mnl_socket-field-in-struct-nft_ctx-on-.patch @@ -0,0 +1,75 @@ +From c8b6092b4fec23b823695c0c9b10325f9c33150c Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Mon, 22 Oct 2018 21:20:44 +0200 +Subject: [PATCH] netlink: reset mnl_socket field in struct nft_ctx on EINTR + +Otherwise we keep using the old netlink socket if we hit EINTR. + +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit d7ef1e206bd9b36607dddcf337fada11d743b61f) + +Conflicts: + include/netlink.h +-> Missing upstream commit 0562beb6544d3 ("src: get rid of netlink_genid_get()") + +Signed-off-by: Phil Sutter +--- + include/netlink.h | 2 +- + src/netlink.c | 4 ++-- + src/rule.c | 3 +-- + 3 files changed, 4 insertions(+), 5 deletions(-) + +diff --git a/include/netlink.h b/include/netlink.h +index 31465ff16822e..894bf6fc98487 100644 +--- a/include/netlink.h ++++ b/include/netlink.h +@@ -191,7 +191,7 @@ extern void netlink_dump_obj(struct nftnl_obj *nlo, struct netlink_ctx *ctx); + extern int netlink_batch_send(struct netlink_ctx *ctx, struct list_head *err_list); + + extern uint16_t netlink_genid_get(struct netlink_ctx *ctx); +-extern void netlink_restart(struct mnl_socket *nf_sock); ++extern struct mnl_socket *netlink_restart(struct mnl_socket *nf_sock); + #define netlink_abi_error() \ + __netlink_abi_error(__FILE__, __LINE__, strerror(errno)); + extern void __noreturn __netlink_abi_error(const char *file, int line, const char *reason); +diff --git a/src/netlink.c b/src/netlink.c +index e16eb504fdef8..c178be3c9ee26 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -73,10 +73,10 @@ void netlink_close_sock(struct mnl_socket *nf_sock) + mnl_socket_close(nf_sock); + } + +-void netlink_restart(struct mnl_socket *nf_sock) ++struct mnl_socket *netlink_restart(struct mnl_socket *nf_sock) + { + netlink_close_sock(nf_sock); +- nf_sock = netlink_open_sock(); ++ return netlink_open_sock(); + } + + uint16_t netlink_genid_get(struct netlink_ctx *ctx) +diff --git a/src/rule.c b/src/rule.c +index 6acfa3ac1695c..47b0d30cbed18 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -161,7 +161,6 @@ int cache_update(struct nft_ctx *nft, enum cmd_ops cmd, struct list_head *msgs) + .msgs = msgs, + .nft = nft, + }; +- struct mnl_socket *nf_sock = nft->nf_sock; + struct nft_cache *cache = &nft->cache; + + replay: +@@ -176,7 +175,7 @@ replay: + if (ret < 0) { + cache_release(cache); + if (errno == EINTR) { +- netlink_restart(nf_sock); ++ nft->nf_sock = netlink_restart(nft->nf_sock); + goto replay; + } + return -1; +-- +2.19.0 + diff --git a/SOURCES/0021-src-remove-opts-field-from-struct-xt_stmt.patch b/SOURCES/0021-src-remove-opts-field-from-struct-xt_stmt.patch new file mode 100644 index 0000000..3856c57 --- /dev/null +++ b/SOURCES/0021-src-remove-opts-field-from-struct-xt_stmt.patch @@ -0,0 +1,71 @@ +From ed4ceb7da31534de89df66d241d449c644fd3ec2 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Tue, 16 Oct 2018 20:56:57 +0200 +Subject: [PATCH] src: remove opts field from struct xt_stmt + +This is never used, ie. always NULL. + +Reported-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +Acked-by: Phil Sutter +(cherry picked from commit b274c169014e71715f9333ee028c5a9304881919) +Signed-off-by: Phil Sutter +--- + include/statement.h | 1 - + src/statement.c | 1 - + src/xt.c | 8 ++------ + 3 files changed, 2 insertions(+), 8 deletions(-) + +diff --git a/include/statement.h b/include/statement.h +index 5a907aa4dee48..0ddbffd24772b 100644 +--- a/include/statement.h ++++ b/include/statement.h +@@ -225,7 +225,6 @@ struct xt_stmt { + struct xtables_match *match; + struct xtables_target *target; + }; +- const char *opts; + void *entry; + }; + +diff --git a/src/statement.c b/src/statement.c +index 58e86f215d5ac..e9c9d648b0092 100644 +--- a/src/statement.c ++++ b/src/statement.c +@@ -767,7 +767,6 @@ static void xt_stmt_print(const struct stmt *stmt, struct output_ctx *octx) + static void xt_stmt_destroy(struct stmt *stmt) + { + xfree(stmt->xt.name); +- xfree(stmt->xt.opts); + xt_stmt_release(stmt); + } + +diff --git a/src/xt.c b/src/xt.c +index 95d0c5f24c07e..74763d58cafd7 100644 +--- a/src/xt.c ++++ b/src/xt.c +@@ -32,9 +32,7 @@ void xt_stmt_xlate(const struct stmt *stmt) + + switch (stmt->xt.type) { + case NFT_XT_MATCH: +- if (stmt->xt.match == NULL && stmt->xt.opts) { +- printf("%s", stmt->xt.opts); +- } else if (stmt->xt.match->xlate) { ++ if (stmt->xt.match->xlate) { + struct xt_xlate_mt_params params = { + .ip = stmt->xt.entry, + .match = stmt->xt.match->m, +@@ -51,9 +49,7 @@ void xt_stmt_xlate(const struct stmt *stmt) + break; + case NFT_XT_WATCHER: + case NFT_XT_TARGET: +- if (stmt->xt.target == NULL && stmt->xt.opts) { +- printf("%s", stmt->xt.opts); +- } else if (stmt->xt.target->xlate) { ++ if (stmt->xt.target->xlate) { + struct xt_xlate_tg_params params = { + .ip = stmt->xt.entry, + .target = stmt->xt.target->t, +-- +2.19.0 + diff --git a/SOURCES/0022-JSON-Support-latest-enhancements-of-fwd-statement.patch b/SOURCES/0022-JSON-Support-latest-enhancements-of-fwd-statement.patch new file mode 100644 index 0000000..6815854 --- /dev/null +++ b/SOURCES/0022-JSON-Support-latest-enhancements-of-fwd-statement.patch @@ -0,0 +1,240 @@ +From 48d688ddb20d4594ee946014a661d30c82c9247d Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 8 Jun 2018 17:27:18 +0200 +Subject: [PATCH] JSON: Support latest enhancements of fwd statement + +JSON equivalent of fwd statement was too primitive to support the added +address and family parameters, so make its value an object and accept +the device expression as value of a "dev" property in there. Then add +optional "addr" and "family" properties to it. + +While being at it, add a testcase to make sure the extended syntax works +right. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit f63b54623fcd1ab7d2f51928571c164409f00175) +Signed-off-by: Phil Sutter +--- + src/json.c | 13 ++++++++-- + src/parser_json.c | 40 ++++++++++++++++++++++++++++-- + tests/py/any/fwd.t | 1 + + tests/py/any/fwd.t.json | 45 ++++++++++++++++++++++------------ + tests/py/any/fwd.t.json.output | 30 ++++++++++++----------- + tests/py/any/fwd.t.payload | 6 +++++ + 6 files changed, 102 insertions(+), 33 deletions(-) + +diff --git a/src/json.c b/src/json.c +index 7d89754bd666d..eeba90e266f75 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -1008,9 +1008,18 @@ json_t *limit_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + + json_t *fwd_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { +- json_t *root; ++ json_t *root, *tmp; ++ ++ root = json_pack("{s:o}", "dev", expr_print_json(stmt->fwd.dev, octx)); ++ ++ if (stmt->fwd.addr) { ++ tmp = json_string(family2str(stmt->fwd.family)); ++ json_object_set_new(root, "family", tmp); ++ ++ tmp = expr_print_json(stmt->fwd.addr, octx); ++ json_object_set_new(root, "addr", tmp); ++ } + +- root = expr_print_json(stmt->fwd.dev, octx); + return json_pack("{s:o}", "fwd", root); + } + +diff --git a/src/parser_json.c b/src/parser_json.c +index 6e14fb7278fb0..af57b3025a104 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -1561,11 +1561,47 @@ static struct stmt *json_parse_limit_stmt(struct json_ctx *ctx, + static struct stmt *json_parse_fwd_stmt(struct json_ctx *ctx, + const char *key, json_t *value) + { +- struct stmt *stmt = fwd_stmt_alloc(int_loc); ++ json_t *jaddr, *jdev; ++ const char *family; ++ struct stmt *stmt; ++ int familyval; ++ ++ if (json_unpack_err(ctx, value, "{s:o}", "dev", &jdev)) ++ return NULL; + +- stmt->fwd.dev = json_parse_expr(ctx, value); ++ stmt = fwd_stmt_alloc(int_loc); ++ ++ stmt->fwd.dev = json_parse_stmt_expr(ctx, jdev); ++ if (!stmt->fwd.dev) { ++ json_error(ctx, "Invalid fwd dev value."); ++ goto out_err; ++ } ++ ++ if (json_unpack(value, "{s:s, s:o}", ++ "family", &family, "addr", &jaddr)) ++ return stmt; ++ ++ familyval = parse_family(family); ++ switch (familyval) { ++ case NFPROTO_IPV4: ++ case NFPROTO_IPV6: ++ stmt->fwd.family = familyval; ++ break; ++ default: ++ json_error(ctx, "Invalid fwd family value '%s'.", family); ++ goto out_err; ++ } ++ ++ stmt->fwd.addr = json_parse_stmt_expr(ctx, jaddr); ++ if (!stmt->fwd.addr) { ++ json_error(ctx, "Invalid fwd addr value."); ++ goto out_err; ++ } + + return stmt; ++out_err: ++ stmt_free(stmt); ++ return NULL; + } + + static struct stmt *json_parse_notrack_stmt(struct json_ctx *ctx, +diff --git a/tests/py/any/fwd.t b/tests/py/any/fwd.t +index d9b4514ee29a1..986a16d9e2c8c 100644 +--- a/tests/py/any/fwd.t ++++ b/tests/py/any/fwd.t +@@ -5,3 +5,4 @@ + fwd to "lo";ok + fwd to mark map { 0x00000001 : "lo", 0x00000002 : "lo"};ok + ++fwd ip to 192.168.2.200 device "lo";ok +diff --git a/tests/py/any/fwd.t.json b/tests/py/any/fwd.t.json +index 644d6d48c2a19..e58a8ad25829b 100644 +--- a/tests/py/any/fwd.t.json ++++ b/tests/py/any/fwd.t.json +@@ -1,7 +1,9 @@ + # fwd to "lo" + [ + { +- "fwd": "lo" ++ "fwd": { ++ "dev": "lo" ++ } + } + ] + +@@ -9,24 +11,37 @@ + [ + { + "fwd": { +- "map": { +- "left": { +- "meta": "mark" +- }, +- "right": { +- "set": [ +- [ +- "0x00000001", +- "lo" +- ], +- [ +- "0x00000002", +- "lo" ++ "dev": { ++ "map": { ++ "left": { ++ "meta": "mark" ++ }, ++ "right": { ++ "set": [ ++ [ ++ "0x00000001", ++ "lo" ++ ], ++ [ ++ "0x00000002", ++ "lo" ++ ] + ] +- ] ++ } + } + } + } + } + ] + ++# fwd ip to 192.168.2.200 device "lo" ++[ ++ { ++ "fwd": { ++ "addr": "192.168.2.200", ++ "dev": "lo", ++ "family": "ip" ++ } ++ } ++] ++ +diff --git a/tests/py/any/fwd.t.json.output b/tests/py/any/fwd.t.json.output +index 5a943567adb0c..e4bad620b22d4 100644 +--- a/tests/py/any/fwd.t.json.output ++++ b/tests/py/any/fwd.t.json.output +@@ -2,21 +2,23 @@ + [ + { + "fwd": { +- "map": { +- "left": { +- "meta": "mark" +- }, +- "right": { +- "set": [ +- [ +- 1, +- "lo" +- ], +- [ +- 2, +- "lo" ++ "dev": { ++ "map": { ++ "left": { ++ "meta": "mark" ++ }, ++ "right": { ++ "set": [ ++ [ ++ 1, ++ "lo" ++ ], ++ [ ++ 2, ++ "lo" ++ ] + ] +- ] ++ } + } + } + } +diff --git a/tests/py/any/fwd.t.payload b/tests/py/any/fwd.t.payload +index 696b55efe8207..966c08b0959c3 100644 +--- a/tests/py/any/fwd.t.payload ++++ b/tests/py/any/fwd.t.payload +@@ -12,3 +12,9 @@ netdev test-netdev ingress + [ lookup reg 1 set __map%d dreg 1 ] + [ fwd sreg_dev 1 ] + ++# fwd ip to 192.168.2.200 device "lo" ++netdev test-netdev ingress ++ [ immediate reg 1 0x00000001 ] ++ [ immediate reg 2 0xc802a8c0 ] ++ [ fwd sreg_dev 1 sreg_addr 2 nfproto 2 ] ++ +-- +2.19.0 + diff --git a/SOURCES/0023-parser_json-Fix-for-ineffective-family-value-checks.patch b/SOURCES/0023-parser_json-Fix-for-ineffective-family-value-checks.patch new file mode 100644 index 0000000..fbb1e2e --- /dev/null +++ b/SOURCES/0023-parser_json-Fix-for-ineffective-family-value-checks.patch @@ -0,0 +1,310 @@ +From c40b8c22beef6011d79c60742bc4043d681198c1 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 12 Oct 2018 17:23:24 +0200 +Subject: [PATCH] parser_json: Fix for ineffective family value checks + +Since handle->family is unsigned, checking for value < 0 never yields +true. Overcome this by changing parse_family() to return an error code +and write the parsed family value into a pointer passed as parameter. + +The above change required a bit more cleanup to avoid passing pointers +to signed variables to the function. Also leverage json_parse_family() a +bit more to reduce code side. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit c7a5401943df8b6b96f6b5eedd9a1e0013e01d86) + +Conflicts: + src/parser_json.c +-> Missing ct timeout support +-> missing tproxy support in JSON +-> Missing ipsec expression + +Signed-off-by: Phil Sutter +--- + src/parser_json.c | 130 ++++++++++++++++++++++------------------------ + 1 file changed, 62 insertions(+), 68 deletions(-) + +diff --git a/src/parser_json.c b/src/parser_json.c +index af57b3025a104..30de17f8a1e26 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -173,7 +173,7 @@ static int json_unpack_stmt(struct json_ctx *ctx, json_t *root, + return 1; + } + +-static int parse_family(const char *name) ++static int parse_family(const char *name, uint32_t *family) + { + unsigned int i; + struct { +@@ -188,13 +188,37 @@ static int parse_family(const char *name) + { "netdev", NFPROTO_NETDEV } + }; + ++ assert(family); ++ + for (i = 0; i < array_size(family_tbl); i++) { +- if (!strcmp(name, family_tbl[i].name)) +- return family_tbl[i].val; ++ if (strcmp(name, family_tbl[i].name)) ++ continue; ++ ++ *family = family_tbl[i].val; ++ return 0; + } + return -1; + } + ++static int json_parse_family(struct json_ctx *ctx, json_t *root) ++{ ++ const char *family; ++ ++ if (!json_unpack(root, "{s:s}", "family", &family)) { ++ uint32_t familyval; ++ ++ if (parse_family(family, &familyval) || ++ (familyval != NFPROTO_IPV6 && ++ familyval != NFPROTO_IPV4)) { ++ json_error(ctx, "Invalid family '%s'.", family); ++ return -1; ++ } ++ return familyval; ++ } ++ ++ return NFPROTO_UNSPEC; ++} ++ + static bool is_keyword(const char *keyword) + { + const char *keywords[] = { +@@ -594,19 +618,15 @@ static struct expr *json_parse_rt_expr(struct json_ctx *ctx, + { "nexthop", NFT_RT_NEXTHOP4 }, + { "mtu", NFT_RT_TCPMSS }, + }; +- unsigned int i, familyval = NFPROTO_UNSPEC; +- const char *key, *family = NULL; ++ const char *key; ++ unsigned int i; ++ int familyval; + + if (json_unpack_err(ctx, root, "{s:s}", "key", &key)) + return NULL; +- if (!json_unpack(root, "{s:s}", "family", &family)) { +- familyval = parse_family(family); +- if (familyval != NFPROTO_IPV4 && +- familyval != NFPROTO_IPV6) { +- json_error(ctx, "Invalid RT family '%s'.", family); +- return NULL; +- } +- } ++ familyval = json_parse_family(ctx, root); ++ if (familyval < 0) ++ return NULL; + + for (i = 0; i < array_size(rt_key_tbl); i++) { + int val = rt_key_tbl[i].val; +@@ -653,9 +673,9 @@ static bool ct_key_is_dir(enum nft_ct_keys key) + static struct expr *json_parse_ct_expr(struct json_ctx *ctx, + const char *type, json_t *root) + { +- const char *key, *dir, *family; ++ const char *key, *dir; + unsigned int i; +- int dirval = -1, familyval = NFPROTO_UNSPEC, keyval = -1; ++ int dirval = -1, familyval, keyval = -1; + + if (json_unpack_err(ctx, root, "{s:s}", "key", &key)) + return NULL; +@@ -672,14 +692,9 @@ static struct expr *json_parse_ct_expr(struct json_ctx *ctx, + return NULL; + } + +- if (!json_unpack(root, "{s:s}", "family", &family)) { +- familyval = parse_family(family); +- if (familyval != NFPROTO_IPV4 && +- familyval != NFPROTO_IPV6) { +- json_error(ctx, "Invalid CT family '%s'.", family); +- return NULL; +- } +- } ++ familyval = json_parse_family(ctx, root); ++ if (familyval < 0) ++ return NULL; + + if (!json_unpack(root, "{s:s}", "dir", &dir)) { + if (!strcmp(dir, "original")) { +@@ -1562,7 +1577,6 @@ static struct stmt *json_parse_fwd_stmt(struct json_ctx *ctx, + const char *key, json_t *value) + { + json_t *jaddr, *jdev; +- const char *family; + struct stmt *stmt; + int familyval; + +@@ -1577,21 +1591,15 @@ static struct stmt *json_parse_fwd_stmt(struct json_ctx *ctx, + goto out_err; + } + +- if (json_unpack(value, "{s:s, s:o}", +- "family", &family, "addr", &jaddr)) +- return stmt; +- +- familyval = parse_family(family); +- switch (familyval) { +- case NFPROTO_IPV4: +- case NFPROTO_IPV6: +- stmt->fwd.family = familyval; +- break; +- default: +- json_error(ctx, "Invalid fwd family value '%s'.", family); ++ familyval = json_parse_family(ctx, value); ++ if (familyval < 0) + goto out_err; +- } + ++ if (familyval == NFPROTO_UNSPEC || ++ json_unpack(value, "{s:o}", "addr", &jaddr)) ++ return stmt; ++ ++ stmt->fwd.family = familyval; + stmt->fwd.addr = json_parse_stmt_expr(ctx, jaddr); + if (!stmt->fwd.addr) { + json_error(ctx, "Invalid fwd addr value."); +@@ -2137,8 +2145,7 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root, + json_error(ctx, "Either name or handle required to delete a table."); + return NULL; + } +- h.family = parse_family(family); +- if (h.family < 0) { ++ if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } +@@ -2178,8 +2185,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, + json_error(ctx, "Either name or handle required to delete a chain."); + return NULL; + } +- h.family = parse_family(family); +- if (h.family < 0) { ++ if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } +@@ -2240,8 +2246,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root, + json_unpack_err(ctx, root, "{s:I}", "handle", &h.handle.id)) + return NULL; + +- h.family = parse_family(family); +- if (h.family < 0) { ++ if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } +@@ -2347,8 +2352,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, + return NULL; + } + +- h.family = parse_family(family); +- if (h.family < 0) { ++ if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } +@@ -2459,8 +2463,7 @@ static struct cmd *json_parse_cmd_add_element(struct json_ctx *ctx, + "elem", &tmp)) + return NULL; + +- h.family = parse_family(family); +- if (h.family < 0) { ++ if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } +@@ -2523,8 +2526,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, + "name", &h.flowtable)) + return NULL; + +- h.family = parse_family(family); +- if (h.family < 0) { ++ if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } +@@ -2568,6 +2570,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + enum cmd_obj cmd_obj) + { + const char *family, *tmp, *rate_unit = "packets", *burst_unit = "bytes"; ++ uint32_t l3proto = NFPROTO_IPV4; + struct handle h = { 0 }; + struct obj *obj; + int inv = 0; +@@ -2588,8 +2591,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + return NULL; + } + +- h.family = parse_family(family); +- if (h.family < 0) { ++ if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } +@@ -2647,18 +2649,13 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + return NULL; + } + } +- if (!json_unpack(root, "{s:s}", "l3proto", &tmp)) { +- int family = parse_family(tmp); +- +- if (family < 0) { +- json_error(ctx, "Invalid ct helper l3proto '%s'.", tmp); +- obj_free(obj); +- return NULL; +- } +- obj->ct_helper.l3proto = family; +- } else { +- obj->ct_helper.l3proto = NFPROTO_IPV4; ++ if (!json_unpack(root, "{s:s}", "l3proto", &tmp) && ++ parse_family(tmp, &l3proto)) { ++ json_error(ctx, "Invalid ct helper l3proto '%s'.", tmp); ++ obj_free(obj); ++ return NULL; + } ++ obj->ct_helper.l3proto = l3proto; + break; + case CMD_OBJ_LIMIT: + obj->type = NFT_OBJECT_LIMIT; +@@ -2770,8 +2767,7 @@ static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx, + h.handle.id = 0; + } + +- h.family = parse_family(family); +- if (h.family < 0) { ++ if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } +@@ -2825,8 +2821,7 @@ static struct cmd *json_parse_cmd_list_multiple(struct json_ctx *ctx, + const char *tmp; + + if (!json_unpack(root, "{s:s}", "family", &tmp)) { +- h.family = parse_family(tmp); +- if (h.family < 0) { ++ if (parse_family(tmp, &h.family)) { + json_error(ctx, "Unknown family '%s'.", tmp); + return NULL; + } +@@ -2981,8 +2976,7 @@ static struct cmd *json_parse_cmd_rename(struct json_ctx *ctx, + "name", &h.chain.name, + "newname", &newname)) + return NULL; +- h.family = parse_family(family); +- if (h.family < 0) { ++ if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } +-- +2.19.0 + diff --git a/SOURCES/0024-json-Fix-memleak-in-dup_stmt_json.patch b/SOURCES/0024-json-Fix-memleak-in-dup_stmt_json.patch new file mode 100644 index 0000000..0528644 --- /dev/null +++ b/SOURCES/0024-json-Fix-memleak-in-dup_stmt_json.patch @@ -0,0 +1,33 @@ +From 5b386b65d61c0f2831ef31a3674225dc21f9e1ff Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 12 Oct 2018 17:50:15 +0200 +Subject: [PATCH] json: Fix memleak in dup_stmt_json() + +The variable 'root' is always assigned to after initialization, so there +is no point in initializing it upon declaration. + +Fixes: e70354f53e9f6 ("libnftables: Implement JSON output support") +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 27d8946db90b79762a36e66647bb8d8fc4c17ce9) +Signed-off-by: Phil Sutter +--- + src/json.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/json.c b/src/json.c +index eeba90e266f75..66b02a934a24b 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -1030,7 +1030,7 @@ json_t *notrack_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + + json_t *dup_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { +- json_t *root = json_object(); ++ json_t *root; + + if (stmt->dup.to) { + root = json_pack("{s:o}", "addr", expr_print_json(stmt->dup.to, octx)); +-- +2.19.0 + diff --git a/SOURCES/0025-json-Fix-for-recent-changes-to-context-structs.patch b/SOURCES/0025-json-Fix-for-recent-changes-to-context-structs.patch new file mode 100644 index 0000000..97c5f9b --- /dev/null +++ b/SOURCES/0025-json-Fix-for-recent-changes-to-context-structs.patch @@ -0,0 +1,287 @@ +From 5eff789ea5d32ad000805c727584ec0d4ee7a392 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Wed, 24 Oct 2018 12:35:04 +0200 +Subject: [PATCH] json: Fix for recent changes to context structs + +Commits introducing nft_ctx pointer to netlink and eval contexts did not +update JSON code accordingly. + +Fixes: 00f777bfc414a ("src: pass struct nft_ctx through struct eval_ctx") +Fixes: 2dc07bcd7eaa5 ("src: pass struct nft_ctx through struct netlink_ctx") +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 841d5f5a3deacfe7b4245df0890849d8e4ad5817) + +Conflicts: +-> Missing ct timeout support + +Signed-off-by: Phil Sutter +--- + src/json.c | 69 ++++++++++++++++++++++++----------------------- + src/parser_json.c | 5 +--- + 2 files changed, 37 insertions(+), 37 deletions(-) + +diff --git a/src/json.c b/src/json.c +index 66b02a934a24b..98581a3c2a3e4 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -211,8 +211,7 @@ static json_t *rule_print_json(struct output_ctx *octx, + return json_pack("{s:o}", "rule", root); + } + +-static json_t *chain_print_json(const struct output_ctx *octx, +- const struct chain *chain) ++static json_t *chain_print_json(const struct chain *chain) + { + json_t *root, *tmp; + +@@ -247,7 +246,7 @@ static json_t *proto_name_json(uint8_t proto) + return json_integer(proto); + } + +-static json_t *obj_print_json(struct output_ctx *octx, const struct obj *obj) ++static json_t *obj_print_json(const struct obj *obj) + { + const char *rate_unit = NULL, *burst_unit = NULL; + const char *type = obj_type_name(obj->type); +@@ -371,8 +370,7 @@ static json_t *table_flags_json(const struct table *table) + return root; + } + +-static json_t *table_print_json(const struct output_ctx *octx, +- const struct table *table) ++static json_t *table_print_json(const struct table *table) + { + json_t *root, *tmp; + +@@ -1295,17 +1293,17 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx, + struct obj *obj; + struct set *set; + +- tmp = table_print_json(ctx->octx, table); ++ tmp = table_print_json(table); + json_array_append_new(root, tmp); + + list_for_each_entry(obj, &table->objs, list) { +- tmp = obj_print_json(ctx->octx, obj); ++ tmp = obj_print_json(obj); + json_array_append_new(root, tmp); + } + list_for_each_entry(set, &table->sets, list) { + if (set->flags & NFT_SET_ANONYMOUS) + continue; +- tmp = set_print_json(ctx->octx, set); ++ tmp = set_print_json(&ctx->nft->output, set); + json_array_append_new(root, tmp); + } + list_for_each_entry(flowtable, &table->flowtables, list) { +@@ -1313,11 +1311,11 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx, + json_array_append_new(root, tmp); + } + list_for_each_entry(chain, &table->chains, list) { +- tmp = chain_print_json(ctx->octx, chain); ++ tmp = chain_print_json(chain); + json_array_append_new(root, tmp); + + list_for_each_entry(rule, &chain->rules, list) { +- tmp = rule_print_json(ctx->octx, rule); ++ tmp = rule_print_json(&ctx->nft->output, rule); + json_array_append_new(root, tmp); + } + } +@@ -1331,7 +1329,7 @@ static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd) + json_t *root = json_array(); + struct table *table; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (family != NFPROTO_UNSPEC && + table->handle.family != family) + continue; +@@ -1348,12 +1346,12 @@ static json_t *do_list_tables_json(struct netlink_ctx *ctx, struct cmd *cmd) + json_t *root = json_array(); + struct table *table; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (family != NFPROTO_UNSPEC && + table->handle.family != family) + continue; + +- json_array_append_new(root, table_print_json(ctx->octx, table)); ++ json_array_append_new(root, table_print_json(table)); + } + + return root; +@@ -1377,10 +1375,10 @@ static json_t *do_list_chain_json(struct netlink_ctx *ctx, + strcmp(cmd->handle.chain.name, chain->handle.chain.name)) + continue; + +- json_array_append_new(root, chain_print_json(ctx->octx, chain)); ++ json_array_append_new(root, chain_print_json(chain)); + + list_for_each_entry(rule, &chain->rules, list) { +- json_t *tmp = rule_print_json(ctx->octx, rule); ++ json_t *tmp = rule_print_json(&ctx->nft->output, rule); + + json_array_append_new(root, tmp); + } +@@ -1395,13 +1393,13 @@ static json_t *do_list_chains_json(struct netlink_ctx *ctx, struct cmd *cmd) + struct table *table; + struct chain *chain; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + list_for_each_entry(chain, &table->chains, list) { +- json_t *tmp = chain_print_json(ctx->octx, chain); ++ json_t *tmp = chain_print_json(chain); + + json_array_append_new(root, tmp); + } +@@ -1418,17 +1416,17 @@ static json_t *do_list_set_json(struct netlink_ctx *ctx, + if (set == NULL) + return json_null(); + +- return json_pack("[o]", set_print_json(ctx->octx, set)); ++ return json_pack("[o]", set_print_json(&ctx->nft->output, set)); + } + + static json_t *do_list_sets_json(struct netlink_ctx *ctx, struct cmd *cmd) + { +- struct output_ctx *octx = ctx->octx; ++ struct output_ctx *octx = &ctx->nft->output; + json_t *root = json_array(); + struct table *table; + struct set *set; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; +@@ -1458,7 +1456,7 @@ static json_t *do_list_obj_json(struct netlink_ctx *ctx, + struct table *table; + struct obj *obj; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; +@@ -1473,8 +1471,7 @@ static json_t *do_list_obj_json(struct netlink_ctx *ctx, + strcmp(cmd->handle.obj.name, obj->handle.obj.name))) + continue; + +- json_array_append_new(root, +- obj_print_json(ctx->octx, obj)); ++ json_array_append_new(root, obj_print_json(obj)); + } + } + +@@ -1487,7 +1484,7 @@ static json_t *do_list_flowtables_json(struct netlink_ctx *ctx, struct cmd *cmd) + struct flowtable *flowtable; + struct table *table; + +- list_for_each_entry(table, &ctx->cache->list, list) { ++ list_for_each_entry(table, &ctx->nft->cache.list, list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; +@@ -1507,7 +1504,7 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd) + json_t *root; + + if (cmd->handle.table.name) +- table = table_lookup(&cmd->handle, ctx->cache); ++ table = table_lookup(&cmd->handle, &ctx->nft->cache); + + switch (cmd->obj) { + case CMD_OBJ_TABLE: +@@ -1572,7 +1569,7 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd) + root = json_null(); + } + root = json_pack("{s:o}", "nftables", root); +- json_dumpf(root, ctx->octx->output_fp, 0); ++ json_dumpf(root, ctx->nft->output.output_fp, 0); + json_decref(root); + return 0; + } +@@ -1581,42 +1578,48 @@ static void monitor_print_json(struct netlink_mon_handler *monh, + const char *cmd, json_t *obj) + { + obj = json_pack("{s:o}", cmd, obj); +- json_dumpf(obj, monh->ctx->octx->output_fp, 0); ++ json_dumpf(obj, monh->ctx->nft->output.output_fp, 0); + json_decref(obj); + } + + void monitor_print_table_json(struct netlink_mon_handler *monh, + const char *cmd, struct table *t) + { +- monitor_print_json(monh, cmd, table_print_json(monh->ctx->octx, t)); ++ monitor_print_json(monh, cmd, table_print_json(t)); + } + + void monitor_print_chain_json(struct netlink_mon_handler *monh, + const char *cmd, struct chain *c) + { +- monitor_print_json(monh, cmd, chain_print_json(monh->ctx->octx, c)); ++ monitor_print_json(monh, cmd, chain_print_json(c)); + } + + void monitor_print_set_json(struct netlink_mon_handler *monh, + const char *cmd, struct set *s) + { +- monitor_print_json(monh, cmd, set_print_json(monh->ctx->octx, s)); ++ struct output_ctx *octx = &monh->ctx->nft->output; ++ ++ monitor_print_json(monh, cmd, set_print_json(octx, s)); + } + + void monitor_print_element_json(struct netlink_mon_handler *monh, + const char *cmd, struct set *s) + { +- monitor_print_json(monh, cmd, element_print_json(monh->ctx->octx, s)); ++ struct output_ctx *octx = &monh->ctx->nft->output; ++ ++ monitor_print_json(monh, cmd, element_print_json(octx, s)); + } + + void monitor_print_obj_json(struct netlink_mon_handler *monh, + const char *cmd, struct obj *o) + { +- monitor_print_json(monh, cmd, obj_print_json(monh->ctx->octx, o)); ++ monitor_print_json(monh, cmd, obj_print_json(o)); + } + + void monitor_print_rule_json(struct netlink_mon_handler *monh, + const char *cmd, struct rule *r) + { +- monitor_print_json(monh, cmd, rule_print_json(monh->ctx->octx, r)); ++ struct output_ctx *octx = &monh->ctx->nft->output; ++ ++ monitor_print_json(monh, cmd, rule_print_json(octx, r)); + } +diff --git a/src/parser_json.c b/src/parser_json.c +index 30de17f8a1e26..817415c15fb89 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -3025,11 +3025,8 @@ static struct cmd *json_parse_cmd(struct json_ctx *ctx, json_t *root) + static int __json_parse(struct json_ctx *ctx, json_t *root) + { + struct eval_ctx ectx = { +- .nf_sock = ctx->nft->nf_sock, ++ .nft = ctx->nft, + .msgs = ctx->msgs, +- .cache = &ctx->nft->cache, +- .octx = &ctx->nft->output, +- .debug_mask = ctx->nft->debug_mask, + }; + json_t *tmp, *value; + size_t index; +-- +2.19.0 + diff --git a/SOURCES/0026-parser_bison-Fix-for-ECN-keyword-in-LHS-of-relationa.patch b/SOURCES/0026-parser_bison-Fix-for-ECN-keyword-in-LHS-of-relationa.patch new file mode 100644 index 0000000..5d164fa --- /dev/null +++ b/SOURCES/0026-parser_bison-Fix-for-ECN-keyword-in-LHS-of-relationa.patch @@ -0,0 +1,185 @@ +From 64b101f4a124f39b494dc5906159a8890568d1f9 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 24 Aug 2018 13:26:57 +0200 +Subject: [PATCH] parser_bison: Fix for ECN keyword in LHS of relational + +Of all possible TCP flags, 'ecn' is special since it is recognized by +lex as a keyword (there is a a field in IPv4 and IPv6 headers with the +same name). Therefore it is listed in keyword_expr, but that was +sufficient for RHS only. The following statement reproduces the issue: + +| tcp flags & (syn | ecn) == (syn | ecn) + +The solution is to limit binop expressions to accept an RHS expression +on RHS ("real" LHS expressions don't make much sense there anyway), +which then allows keyword_expr to occur there. In order to maintain the +recursive behaviour if braces are present, allow primary_rhs_expr to +consist of a basic_rhs_expr enclosed in braces. This in turn requires +for braced RHS part in relational_expr to be dropped, otherwise bison +complains about shift/reduce conflict. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 14a9968a56f8b35138bab172aa7ce796f5d98e03) +Signed-off-by: Phil Sutter +--- + src/parser_bison.y | 15 ++++++--------- + tests/py/inet/tcp.t | 1 + + tests/py/inet/tcp.t.json | 23 +++++++++++++++++++++++ + tests/py/inet/tcp.t.json.output | 16 ++++++++++++++++ + tests/py/inet/tcp.t.payload | 8 ++++++++ + 5 files changed, 54 insertions(+), 9 deletions(-) + +diff --git a/src/parser_bison.y b/src/parser_bison.y +index d75cd50fa29b9..a6b6fc1745a72 100644 +--- a/src/parser_bison.y ++++ b/src/parser_bison.y +@@ -2948,32 +2948,32 @@ fib_tuple : fib_flag DOT fib_tuple + ; + + shift_expr : primary_expr +- | shift_expr LSHIFT primary_expr ++ | shift_expr LSHIFT primary_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_LSHIFT, $1, $3); + } +- | shift_expr RSHIFT primary_expr ++ | shift_expr RSHIFT primary_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_RSHIFT, $1, $3); + } + ; + + and_expr : shift_expr +- | and_expr AMPERSAND shift_expr ++ | and_expr AMPERSAND shift_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_AND, $1, $3); + } + ; + + exclusive_or_expr : and_expr +- | exclusive_or_expr CARET and_expr ++ | exclusive_or_expr CARET and_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_XOR, $1, $3); + } + ; + + inclusive_or_expr : exclusive_or_expr +- | inclusive_or_expr '|' exclusive_or_expr ++ | inclusive_or_expr '|' exclusive_or_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_OR, $1, $3); + } +@@ -3252,10 +3252,6 @@ relational_expr : expr /* implicit */ rhs_expr + { + $$ = relational_expr_alloc(&@2, $2, $1, $3); + } +- | expr relational_op '(' rhs_expr ')' +- { +- $$ = relational_expr_alloc(&@2, $2, $1, $4); +- } + ; + + list_rhs_expr : basic_rhs_expr COMMA basic_rhs_expr +@@ -3439,6 +3435,7 @@ primary_rhs_expr : symbol_expr { $$ = $1; } + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } ++ | '(' basic_rhs_expr ')' { $$ = $2; } + ; + + relational_op : EQ { $$ = OP_EQ; } +diff --git a/tests/py/inet/tcp.t b/tests/py/inet/tcp.t +index d66ba8438a32f..f96e3634f41ed 100644 +--- a/tests/py/inet/tcp.t ++++ b/tests/py/inet/tcp.t +@@ -78,6 +78,7 @@ tcp flags cwr;ok + tcp flags != cwr;ok + tcp flags == syn;ok + tcp flags & (syn|fin) == (syn|fin);ok;tcp flags & (fin | syn) == fin | syn ++tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr;ok;tcp flags == 0xff + + tcp window 22222;ok + tcp window 22;ok +diff --git a/tests/py/inet/tcp.t.json b/tests/py/inet/tcp.t.json +index f5df72aa252a6..9ae51018e93ee 100644 +--- a/tests/py/inet/tcp.t.json ++++ b/tests/py/inet/tcp.t.json +@@ -1068,6 +1068,29 @@ + } + ] + ++# tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr ++[ ++ { ++ "match": { ++ "left": { ++ "&": [ ++ { ++ "payload": { ++ "field": "flags", ++ "protocol": "tcp" ++ } ++ }, ++ { ++ "|": [ "fin", { "|": [ "syn", { "|": [ "rst", { "|": [ "psh", { "|": [ "ack", { "|": [ "urg", { "|": [ "ecn", "cwr" ] } ] } ] } ] } ] } ] } ] ++ } ++ ] ++ }, ++ "op": "==", ++ "right": { "|": [ "fin", { "|": [ "syn", { "|": [ "rst", { "|": [ "psh", { "|": [ "ack", { "|": [ "urg", { "|": [ "ecn", "cwr" ] } ] } ] } ] } ] } ] } ] } ++ } ++ } ++] ++ + # tcp window 22222 + [ + { +diff --git a/tests/py/inet/tcp.t.json.output b/tests/py/inet/tcp.t.json.output +index d099d6febb122..ff58756cf596d 100644 +--- a/tests/py/inet/tcp.t.json.output ++++ b/tests/py/inet/tcp.t.json.output +@@ -132,3 +132,19 @@ + } + ] + ++# tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "flags", ++ "protocol": "tcp" ++ } ++ }, ++ "op": "==", ++ "right": 255 ++ } ++ } ++] ++ +diff --git a/tests/py/inet/tcp.t.payload b/tests/py/inet/tcp.t.payload +index 09538aed746c9..2390a24ead15c 100644 +--- a/tests/py/inet/tcp.t.payload ++++ b/tests/py/inet/tcp.t.payload +@@ -436,6 +436,14 @@ inet test-inet input + [ bitwise reg 1 = (reg=1 & 0x00000003 ) ^ 0x00000000 ] + [ cmp eq reg 1 0x00000003 ] + ++# tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr ++inet test-inet input ++ [ meta load l4proto => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ transport header + 13 => reg 1 ] ++ [ bitwise reg 1 = (reg=1 & 0x000000ff ) ^ 0x00000000 ] ++ [ cmp eq reg 1 0x000000ff ] ++ + # tcp window 22222 + inet test-inet input + [ meta load l4proto => reg 1 ] +-- +2.19.0 + diff --git a/SOURCES/0027-nft.8-Update-meta-pkt_type-value-description.patch b/SOURCES/0027-nft.8-Update-meta-pkt_type-value-description.patch new file mode 100644 index 0000000..1cb85ff --- /dev/null +++ b/SOURCES/0027-nft.8-Update-meta-pkt_type-value-description.patch @@ -0,0 +1,47 @@ +From c2d82484a75c99e079754e75ccc510e91290bb72 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 14 Sep 2018 11:00:14 +0200 +Subject: [PATCH] nft.8: Update meta pkt_type value description + +Commit 8a7f6de536408 ("meta: fix pkttype name and add 'other' symbol") +deprecated pkt_type value 'unicast' (for it being misleading) and +introduced 'host' and 'other' but it did not update documentation +accordingly. Fix this by replacing 'unicast' with 'host' in +documentation and adding 'other'. + +While being at it, make sure these literal values are recognized as +such: Put them in all lower-case (as required by the parser) and in bold +font (to stand out a bit more). + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit d75e9184bb51a1311ac950b13384f329836d597e) + +Conflicts: + doc/primary-expression.txt +-> Patch manually applied to doc/nft.xml, patches converting man page + source to asciidoc are missing entirely. + +Signed-off-by: Phil Sutter +--- + doc/nft.xml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/doc/nft.xml b/doc/nft.xml +index 3a5571b0da57a..0df42810c5e68 100644 +--- a/doc/nft.xml ++++ b/doc/nft.xml +@@ -2991,8 +2991,8 @@ filter output icmpv6 type { echo-request, echo-reply } + + pkt_type + +- Packet type: Unicast (addressed to local host), +- Broadcast (to all), Multicast (to group). ++ Packet type: host (addressed to local host), ++ broadcast (to all), multicast (to group), other (addressed to another host). + + + +-- +2.19.0 + diff --git a/SOURCES/0028-json-Work-around-segfault-when-encountering-xt-stmt.patch b/SOURCES/0028-json-Work-around-segfault-when-encountering-xt-stmt.patch new file mode 100644 index 0000000..bf1a703 --- /dev/null +++ b/SOURCES/0028-json-Work-around-segfault-when-encountering-xt-stmt.patch @@ -0,0 +1,49 @@ +From 48c743edbd41470149a5ac58c350b47cb80fb9eb Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Wed, 24 Oct 2018 21:14:37 +0200 +Subject: [PATCH] json: Work around segfault when encountering xt stmt + +When trying to convert an xt stmt into JSON, print() callback was +called. Though the code in src/xt.c does not respect output_fp, +therefore buffer wasn't filled as expected making libjansson to puke: + +| # nft -j list ruleset +| warning: stmt ops xt have no json callback +| nft: json.c:169: stmt_print_json: Assertion `__out' failed. +| Aborted (core dumped) + +Avoid this by detecting xt stmt ops and returning a stub. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit b849b0dfd9f3aecff5617bc60d5852ef36c3d494) + +Conflicts: + doc/libnftables-json.adoc +-> Patches adding this man page are missing entirely. + +Signed-off-by: Phil Sutter +--- + src/json.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/json.c b/src/json.c +index 98581a3c2a3e4..f74afd5a7292e 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -165,6 +165,12 @@ static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx) + char buf[1024]; + FILE *fp; + ++ /* XXX: Can't be supported at this point: ++ * xt_stmt_xlate() ignores output_fp. ++ */ ++ if (stmt->ops->type == STMT_XT) ++ return json_pack("{s:n}", "xt"); ++ + if (stmt->ops->json) + return stmt->ops->json(stmt, octx); + +-- +2.19.0 + diff --git a/SOURCES/0029-nft.8-Document-log-level-audit.patch b/SOURCES/0029-nft.8-Document-log-level-audit.patch new file mode 100644 index 0000000..9c9a44f --- /dev/null +++ b/SOURCES/0029-nft.8-Document-log-level-audit.patch @@ -0,0 +1,51 @@ +From 109aab644873fcc732c08aea25288b7be1525ed0 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Sat, 27 Oct 2018 12:15:50 +0200 +Subject: [PATCH] nft.8: Document log level audit + +Since this pseudo log level fundamentally changes behaviour of log +statement, dedicate this mode a separate paragraph. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso + +Conflicts: + doc/statements.txt +-> Patch manually applied to doc/nft.xml from asciidoc source. + +Signed-off-by: Phil Sutter +--- + doc/nft.xml | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/doc/nft.xml b/doc/nft.xml +index 0df42810c5e68..22d57ac630037 100644 +--- a/doc/nft.xml ++++ b/doc/nft.xml +@@ -4700,9 +4700,22 @@ tcp flags syn tcp option maxseg size set rt mtu + snaplen + size + ++ ++ log ++ level audit ++ ++ ++ ++ The log statement enables logging of matching packets. When this statement is used from a rule, the Linux kernel will print some information on all matching packets, such as header fields, via the kernel log (where it can be read with dmesg(1) or read in the syslog). ++ ++ ++ In the second form of invocation (if 'nflog_group' is specified), the Linux kernel will pass the packet to nfnetlink_log which will multicast the packet through a netlink socket to the specified multicast group. One or more userspace processes may subscribe to the group to receive the packets, see libnetfilter_queue documentation for details. ++ ++ ++ In the third form of invocation (if level audit is specified), the Linux kernel writes a message into the audit buffer suitably formatted for reading with auditd. Therefore no further formatting options (such as prefix or flags) are allowed in this mode. + + +- The log statement enables logging of matching packets. When this statement is used from a rule, the Linux kernel will print some information on all matching packets, such as header fields, via the kernel log (where it can be read with dmesg(1) or read in the syslog). If the group number is specified, the Linux kernel will pass the packet to nfnetlink_log which will multicast the packet through a netlink socket to the specified multicast group. One or more userspace processes may subscribe to the group to receive the packets, see libnetfilter_queue documentation for details. This is a non-terminating statement, so the rule evaluation continues after the packet is logged. ++ This is a non-terminating statement, so the rule evaluation continues after the packet is logged. + + + +-- +2.19.0 + diff --git a/SOURCES/0030-nft.8-Clarify-index-option-of-add-rule-command.patch b/SOURCES/0030-nft.8-Clarify-index-option-of-add-rule-command.patch new file mode 100644 index 0000000..3d6dad0 --- /dev/null +++ b/SOURCES/0030-nft.8-Clarify-index-option-of-add-rule-command.patch @@ -0,0 +1,68 @@ +From 8d5b7e557b65cd547b55567ac3dfbb2ea3f65418 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Mon, 12 Nov 2018 15:02:52 +0100 +Subject: [PATCH] nft.8: Clarify 'index' option of add rule command + +Documentation for add rule command might trick readers into believing +the optional 'index' argument does not need to be that of an existing +rule. This false assumption is fueled by the fact that iptables allows +to insert with last rule number + 1 to actually append to a chain. +Change the relevant sentence to clarify that. + +While being at it, drop the deprecated 'position' option from +documentation - since this will likely go away at some point, don't +encourage users to use it although they should notice that they +shoudn't. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit f4e40e395af2b73cd72c5454e41fb879da8c5f61) + +Conflicts: + doc/nft.txt +-> Patch applied manually to doc/nft.xml from asciidoc source. + +Signed-off-by: Phil Sutter +--- + doc/nft.xml | 11 +++-------- + 1 file changed, 3 insertions(+), 8 deletions(-) + +diff --git a/doc/nft.xml b/doc/nft.xml +index 22d57ac630037..512724ed8f9a7 100644 +--- a/doc/nft.xml ++++ b/doc/nft.xml +@@ -877,10 +877,7 @@ add table inet mytable + chain + + +- +- handle +- position +- ++ handle + handle + + +@@ -917,8 +914,8 @@ add table inet mytable + + + The add and insert commands support an optional +- location specifier, which is either a handle of an existing +- rule or an index (starting at zero). Internally, ++ location specifier, which is either a handle or the ++ index (starting at zero) of an existing rule. Internally, + rule locations are always identified by handle and the + translation from index happens in userspace. This has two + potential implications in case a concurrent ruleset change happens after the translation +@@ -935,8 +932,6 @@ add table inet mytable + Add a new rule described by the list of statements. The rule is appended to the + given chain unless a handle is specified, in which case the + rule is appended to the rule given by the handle. +- The alternative name position is deprecated and should not be +- used anymore. + + + +-- +2.19.0 + diff --git a/SOURCES/0031-src-Reject-export-vm-json-command.patch b/SOURCES/0031-src-Reject-export-vm-json-command.patch new file mode 100644 index 0000000..e1853dd --- /dev/null +++ b/SOURCES/0031-src-Reject-export-vm-json-command.patch @@ -0,0 +1,152 @@ +From b5fd560fa6b1aa1e112273d9f04281b58eb06dae Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Mon, 17 Dec 2018 16:29:56 +0100 +Subject: [PATCH] src: Reject 'export vm json' command + +Since libnftnl recently dropped JSON output support, this form of JSON +export is not available anymore. Point at 'nft -j list ruleset' command +for a replacement in error message. + +Since 'export' command is not useable anymore, remove it from +documentation. Instead point out that 'list ruleset' command serves well +for dumping and later restoring. + +To not cause pointless inconvenience for users wishing to store their +ruleset in JSON format, make JSON parser fallback to CMD_ADD if no +recognized command property was found. This allows to feed the output of +'nft -j list ruleset' into 'nft -f' without any modification. + +Signed-off-by: Phil Sutter +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 8d51f169e0e832a41d2ed278be903c08bd4fa473) + +Conflicts: +-> Documentation changes applied manually due to missing conversion to + asciidoc. +-> Dropped references to libnftables-json man page from documentation. + (This file has not been backported and doing so is non-trivial due to + asciidoc conversion.) +-> Include libnftnl/common.h in src/evaluate.c to make symbol + NFTNL_OUTPUT_JSON known. + +Signed-off-by: Phil Sutter +--- + doc/nft.xml | 34 +++++++++++++--------------------- + src/evaluate.c | 5 +++++ + src/parser_json.c | 4 ++-- + 3 files changed, 20 insertions(+), 23 deletions(-) + +diff --git a/doc/nft.xml b/doc/nft.xml +index 512724ed8f9a7..1a97d7a169776 100644 +--- a/doc/nft.xml ++++ b/doc/nft.xml +@@ -46,7 +46,7 @@ vi:ts=4 sw=4 + + nft + +- ++ + + -I + directory +@@ -155,6 +155,14 @@ vi:ts=4 sw=4 + + + ++ ++ ++ ++ ++ Format output in JSON. ++ ++ ++ + + + +@@ -480,11 +488,6 @@ filter input iif $int_ifs accept + ruleset + family + +- +- export +- ruleset +- format +- + + + +@@ -514,17 +517,6 @@ filter input iif $int_ifs accept + + + +- +- +- +- +- Print the ruleset in machine readable format. The +- mandatory format parameter +- may be either xml or +- json. +- +- +- + + + +@@ -534,10 +526,10 @@ filter input iif $int_ifs accept + + + +- Note that contrary to what one might assume, the output generated +- by export is not parseable by +- nft -f. Instead, the output of +- list command serves well for that purpose. ++ By design, list ruleset command output may be ++ used as input to nft -f. ++ Effectively, this is the nft-equivalent of iptables-save and ++ iptables-restore. + + + +diff --git a/src/evaluate.c b/src/evaluate.c +index 809920748c0a9..4656c7566db39 100644 +--- a/src/evaluate.c ++++ b/src/evaluate.c +@@ -31,6 +31,8 @@ + #include + #include + ++#include ++ + static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr); + + static const char * const byteorder_names[] = { +@@ -3526,6 +3528,9 @@ static int cmd_evaluate_export(struct eval_ctx *ctx, struct cmd *cmd) + if (cmd->markup->format == __NFT_OUTPUT_NOTSUPP) + return cmd_error(ctx, &cmd->location, + "this output type is not supported"); ++ else if (cmd->markup->format == NFTNL_OUTPUT_JSON) ++ return cmd_error(ctx, &cmd->location, ++ "JSON export is no longer supported, use 'nft -j list ruleset' instead"); + + return cache_update(ctx->nft, cmd->op, ctx->msgs); + } +diff --git a/src/parser_json.c b/src/parser_json.c +index 817415c15fb89..1e3688b2dc1cd 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -3018,8 +3018,8 @@ static struct cmd *json_parse_cmd(struct json_ctx *ctx, json_t *root) + + return parse_cb_table[i].cb(ctx, tmp, parse_cb_table[i].op); + } +- json_error(ctx, "Unknown command object."); +- return NULL; ++ /* to accept 'list ruleset' output 1:1, try add command */ ++ return json_parse_cmd_add(ctx, root, CMD_ADD); + } + + static int __json_parse(struct json_ctx *ctx, json_t *root) +-- +2.19.0 + diff --git a/SOURCES/nftables.conf b/SOURCES/nftables.conf new file mode 100644 index 0000000..5f602ac --- /dev/null +++ b/SOURCES/nftables.conf @@ -0,0 +1,14 @@ +# +# This this will contain your nftables rules and +# is read by the systemd service when restarting +# +# These provide an iptables like set of filters +# (uncomment to include) +# include "/etc/nftables/bridge-filter.nft" +# include "/etc/nftables/inet-filter.nft" +# include "/etc/nftables/ipv4-filter.nft" +# include "/etc/nftables/ipv4-mangle.nft" +# include "/etc/nftables/ipv4-nat.nft" +# include "/etc/nftables/ipv6-filter.nft" +# include "/etc/nftables/ipv6-mangle.nft" +# include "/etc/nftables/ipv6-nat.nft" diff --git a/SOURCES/nftables.service b/SOURCES/nftables.service new file mode 100644 index 0000000..1e8c194 --- /dev/null +++ b/SOURCES/nftables.service @@ -0,0 +1,17 @@ +[Unit] +Description=Netfilter Tables +Documentation=man:nft(8) +Wants=network-pre.target +Before=network-pre.target + +[Service] +Type=oneshot +ProtectSystem=full +ProtectHome=true +ExecStart=/sbin/nft -f /etc/sysconfig/nftables.conf +ExecReload=/sbin/nft 'flush ruleset; include "/etc/sysconfig/nftables.conf";' +ExecStop=/sbin/nft flush ruleset +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/SPECS/nftables.spec b/SPECS/nftables.spec new file mode 100644 index 0000000..da9e66a --- /dev/null +++ b/SPECS/nftables.spec @@ -0,0 +1,268 @@ +Name: nftables +Version: 0.9.0 +Release: 8%{?dist} +# Upstream released a 0.100 version, then 0.4. Need Epoch to get back on track. +Epoch: 1 +Summary: Netfilter Tables userspace utillites + +License: GPLv2 +URL: http://netfilter.org/projects/nftables/ +Source0: http://ftp.netfilter.org/pub/nftables/nftables-%{version}.tar.bz2 +Source1: nftables.service +Source2: nftables.conf + +Patch1: 0001-evaluate-reject-Allow-icmpx-in-inet-bridge-families.patch +Patch2: 0002-monitor-Drop-fake-XML-support.patch +Patch3: 0003-monitor-Drop-update-table-and-update-chain-cases.patch +Patch4: 0004-monitor-Fix-printing-of-ct-objects.patch +Patch5: 0005-monitor-Use-libnftables-JSON-output.patch +Patch6: 0006-tests-monitor-Test-JSON-output-as-well.patch +Patch7: 0007-segtree-bogus-range-via-get-set-element-on-existing-.patch +Patch8: 0008-segtree-disantangle-get_set_interval_end.patch +Patch9: 0009-segtree-memleak-in-get_set_decompose.patch +Patch10: 0010-segtree-stop-iteration-on-existing-elements-in-case-.patch +Patch11: 0011-segtree-incorrect-handling-of-last-element-in-get_se.patch +Patch12: 0012-segtree-set-proper-error-cause-on-existing-elements.patch +Patch13: 0013-rule-fix-memleak-in-do_get_setelems.patch +Patch14: 0014-Fix-memleak-in-netlink_parse_fwd-error-path.patch +Patch15: 0015-libnftables-Fix-memleak-in-nft_parse_bison_filename.patch +Patch16: 0016-src-pass-struct-nft_ctx-through-struct-eval_ctx.patch +Patch17: 0017-src-trace-fix-policy-printing.patch +Patch18: 0018-rule-list-only-the-table-containing-object.patch +Patch19: 0019-src-pass-struct-nft_ctx-through-struct-netlink_ctx.patch +Patch20: 0020-netlink-reset-mnl_socket-field-in-struct-nft_ctx-on-.patch +Patch21: 0021-src-remove-opts-field-from-struct-xt_stmt.patch +Patch22: 0022-JSON-Support-latest-enhancements-of-fwd-statement.patch +Patch23: 0023-parser_json-Fix-for-ineffective-family-value-checks.patch +Patch24: 0024-json-Fix-memleak-in-dup_stmt_json.patch +Patch25: 0025-json-Fix-for-recent-changes-to-context-structs.patch +Patch26: 0026-parser_bison-Fix-for-ECN-keyword-in-LHS-of-relationa.patch +Patch27: 0027-nft.8-Update-meta-pkt_type-value-description.patch +Patch28: 0028-json-Work-around-segfault-when-encountering-xt-stmt.patch +Patch29: 0029-nft.8-Document-log-level-audit.patch +Patch30: 0030-nft.8-Clarify-index-option-of-add-rule-command.patch +Patch31: 0031-src-Reject-export-vm-json-command.patch + + +#BuildRequires: autogen +#BuildRequires: autoconf +#BuildRequires: automake +#BuildRequires: libtool +BuildRequires: gcc +BuildRequires: flex +BuildRequires: bison +BuildRequires: libmnl-devel +BuildRequires: gmp-devel +BuildRequires: readline-devel +BuildRequires: libnftnl-devel +BuildRequires: docbook2X +BuildRequires: systemd +BuildRequires: jansson-devel +BuildRequires: iptables-devel + +%description +Netfilter Tables userspace utilities. + +%package devel +Summary: Development library for nftables / libnftables +Group: Development/Libraries +Requires: %{name} = %{epoch}:%{version}-%{release} +Requires: pkgconfig + +%description devel +Development tools and static libraries and header files for the libnftables library. + +%prep +%autosetup -p1 + +%build +#./autogen.sh +%configure --disable-silent-rules --with-json --with-xtables +make %{?_smp_mflags} + +%install +%make_install +find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' + +# Don't ship static lib (for now at least) +rm -f $RPM_BUILD_ROOT/%{_libdir}/libnftables.a + +chmod 644 $RPM_BUILD_ROOT/%{_mandir}/man8/nft* + +mkdir -p $RPM_BUILD_ROOT/%{_unitdir} +cp -a %{SOURCE1} $RPM_BUILD_ROOT/%{_unitdir}/ + +mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig +cp -a %{SOURCE2} $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/ + +mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/nftables + +%post +%systemd_post nftables.service + +%preun +%systemd_preun nftables.service + +%postun +%systemd_postun_with_restart nftables.service + +%post devel +%ldconfig_post + +%postun devel +%ldconfig_postun + +%files +%license COPYING +%config(noreplace) %{_sysconfdir}/nftables/ +%config(noreplace) %{_sysconfdir}/sysconfig/nftables.conf +%{_sbindir}/nft +%{_libdir}/libnftables.so.* +%{_mandir}/man8/nft* +%{_unitdir}/nftables.service + +%files devel +%{_libdir}/libnftables.so +%{_libdir}/pkgconfig/libnftables.pc +%{_includedir}/nftables/libnftables.h + +%changelog +* Fri Mar 01 2019 Phil Sutter - 1:0.9.0-8 +- Add missing patch to spec file + +* Fri Dec 21 2018 Phil Sutter - 1:0.9.0-7 +- src: Reject 'export vm json' command + +* Tue Dec 18 2018 Phil Sutter - 1:0.9.0-6 +- Rebuild for updated libnftnl + +* Thu Dec 13 2018 Phil Sutter - 1:0.9.0-5 +- nft.8: Document log level audit +- nft.8: Clarify 'index' option of add rule command + +* Thu Oct 25 2018 Phil Sutter - 1:0.9.0-4 +- Add fixes for covscan report +- Fix for ECN keyword in LHS of relational +- Update meta pkt_type value description +- Fix for segfault with JSON output if xt expression is present +- Add missing nft suffix to files included from /etc/sysconfig/nftables.conf +- Use native JSON API in nft monitor + +* Thu Oct 11 2018 Phil Sutter - 1:0.9.0-3 +- Enable xtables support +- Enable JSON support + +* Mon Sep 10 2018 Phil Sutter - 1:0.9.0-2 +- Allow icmpx in inet/bridge families + +* Tue Aug 14 2018 Phil Sutter - 1:0.9.0-1 +- New version 0.9.0 +- Install libnftables +- Add devel sub-package +- Add gcc BuildRequires + +* Sat Mar 03 2018 Kevin Fenzi - 0.8.3-1 +- Update to 0.8.3. Fixes bug #1551207 + +* Thu Feb 08 2018 Fedora Release Engineering - 1:0.8.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Mon Feb 05 2018 Kevin Fenzi - 0.8.2-1 +- Update to 0.8.2. Fixes bug #1541582 + +* Tue Jan 16 2018 Kevin Fenzi - 0.8.1-1 +- Update to 0.8.1. Fixes bug #1534982 + +* Sun Oct 22 2017 Kevin Fenzi - 0.8-1 +- Update to 0.8. + +* Thu Aug 03 2017 Fedora Release Engineering - 1:0.7-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 1:0.7-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Feb 10 2017 Fedora Release Engineering - 1:0.7-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Jan 12 2017 Igor Gnatenko - 1:0.7-2 +- Rebuild for readline 7.x + +* Thu Dec 22 2016 Kevin Fenzi - 0.7-1 +- Update to 0.7 + +* Fri Jul 15 2016 Kevin Fenzi - 0.6-2 +- Rebuild for new glibc symbols + +* Thu Jun 02 2016 Kevin Fenzi - 0.6-1 +- Update to 0.6. + +* Sun Apr 10 2016 Kevin Fenzi - 0.5-4 +- Add example config files and move config to /etc/sysconfig. Fixes bug #1313936 + +* Fri Mar 25 2016 Kevin Fenzi - 0.5-3 +- Add systemd unit file. Fixes bug #1313936 + +* Thu Feb 04 2016 Fedora Release Engineering - 1:0.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Thu Sep 17 2015 Kevin Fenzi 0.5-1 +- Update to 0.5 + +* Wed Jun 17 2015 Fedora Release Engineering - 1:0.4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Sat Jan 10 2015 Kevin Fenzi 0.4-2 +- Add patch to fix nft -f dep gen. + +* Fri Dec 26 2014 Kevin Fenzi 0.4-1 +- Update to 0.4 +- Add Epoch to fix versioning. + +* Wed Sep 03 2014 Kevin Fenzi 0.100-4.20140903git +- Update to 20140903 snapshot + +* Sun Aug 17 2014 Fedora Release Engineering - 0.100-4.20140704git +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Fri Jul 04 2014 Kevin Fenzi 0.100-3.20140704git +- Update to new snapshot + +* Sat Jun 07 2014 Fedora Release Engineering - 0.100-2.20140426git +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Sat Apr 26 2014 Kevin Fenzi 0.100-1.20140426git +- Update t0 20140426 + +* Sun Mar 30 2014 Kevin Fenzi 0.100-1.20140330git +- Update to 20140330 snapshot +- Sync versions to be post 0.100 release. + +* Wed Mar 26 2014 Kevin Fenzi 0-0.7.20140326git +- Update to 20140326 snapshot +- Fix permissions on man pages. + +* Mon Mar 24 2014 Kevin Fenzi 0-0.6.20140324git +- Update to 20140324 snapshot + +* Fri Mar 07 2014 Kevin Fenzi 0-0.5.20140307git +- Update to 20140307 + +* Sat Jan 25 2014 Kevin Fenzi 0-0.4.20140125git +- Update to 20140125 snapshot + +* Sat Jan 18 2014 Kevin Fenzi 0-0.3.20140118git +- Update to 20140118 snapshot +- Fixed License tag to be correct +- Fixed changelog +- nft scripts now use full path for nft +- Fixed man page building +- Dropped unneeded rm in install +- Patched build to not be silent. + +* Tue Dec 03 2013 Kevin Fenzi 0-0.2.20131202git +- Use upstream snapshots for source. +- Use 0 for version. + +* Sat Nov 30 2013 Kevin Fenzi 0-0.1 +- initial version for Fedora review