Add the missing bits to JSON parser, printer, man page and testsuite. Fixes: fbe27464dee45 ("src: add nat support for the inet family") Signed-off-by: Phil Sutter <phil@xxxxxx> --- doc/libnftables-json.adoc | 5 ++ src/json.c | 8 ++ src/parser_json.c | 7 +- tests/py/inet/dnat.t.json | 166 ++++++++++++++++++++++++++++++++++++++ tests/py/inet/snat.t.json | 131 ++++++++++++++++++++++++++++++ 5 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 tests/py/inet/dnat.t.json create mode 100644 tests/py/inet/snat.t.json diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc index dbe5ac33d999e..429f530db913c 100644 --- a/doc/libnftables-json.adoc +++ b/doc/libnftables-json.adoc @@ -808,12 +808,14 @@ Duplicate a packet to a different destination. ____ *{ "snat": { "addr":* 'EXPRESSION'*, + "family":* 'STRING'*, "port":* 'EXPRESSION'*, "flags":* 'FLAGS' *}}* *{ "dnat": { "addr":* 'EXPRESSION'*, + "family":* 'STRING'*, "port":* 'EXPRESSION'*, "flags":* 'FLAGS' *}}* @@ -837,6 +839,9 @@ Perform Network Address Translation. *addr*:: Address to translate to. +*family*:: + Family of *addr*, either *ip* or *ip6*. Required in *inet* + table family. *port*:: Port to translate to. *flags*:: diff --git a/src/json.c b/src/json.c index 4900c02336b56..a8538bdca973b 100644 --- a/src/json.c +++ b/src/json.c @@ -1260,6 +1260,14 @@ json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx) json_t *root = json_object(); json_t *array = nat_flags_json(stmt->nat.flags); + switch (stmt->nat.family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + json_object_set_new(root, "family", + json_string(family2str(stmt->nat.family))); + break; + } + if (stmt->nat.addr) json_object_set_new(root, "addr", expr_print_json(stmt->nat.addr, octx)); diff --git a/src/parser_json.c b/src/parser_json.c index 315f247811567..0c4f5d913813a 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -1840,9 +1840,9 @@ static int nat_type_parse(const char *type) static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx, const char *key, json_t *value) { + int type, familyval; struct stmt *stmt; json_t *tmp; - int type; type = nat_type_parse(key); if (type < 0) { @@ -1850,7 +1850,12 @@ static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx, return NULL; } + familyval = json_parse_family(ctx, value); + if (familyval < 0) + return NULL; + stmt = nat_stmt_alloc(int_loc, type); + stmt->nat.family = familyval; if (!json_unpack(value, "{s:o}", "addr", &tmp)) { stmt->nat.addr = json_parse_stmt_expr(ctx, tmp); diff --git a/tests/py/inet/dnat.t.json b/tests/py/inet/dnat.t.json new file mode 100644 index 0000000000000..ac6dac620a85e --- /dev/null +++ b/tests/py/inet/dnat.t.json @@ -0,0 +1,166 @@ +# iifname "foo" tcp dport 80 redirect to :8080 +[ + { + "match": { + "left": { + "meta": { + "key": "iifname" + } + }, + "op": "==", + "right": "foo" + } + }, + { + "match": { + "left": { + "payload": { + "field": "dport", + "protocol": "tcp" + } + }, + "op": "==", + "right": 80 + } + }, + { + "redirect": { + "port": 8080 + } + } +] + +# iifname "eth0" tcp dport 443 dnat ip to 192.168.3.2 +[ + { + "match": { + "left": { + "meta": { + "key": "iifname" + } + }, + "op": "==", + "right": "eth0" + } + }, + { + "match": { + "left": { + "payload": { + "field": "dport", + "protocol": "tcp" + } + }, + "op": "==", + "right": 443 + } + }, + { + "dnat": { + "addr": "192.168.3.2", + "family": "ip" + } + } +] + +# iifname "eth0" tcp dport 443 dnat ip6 to [dead::beef]:4443 +[ + { + "match": { + "left": { + "meta": { + "key": "iifname" + } + }, + "op": "==", + "right": "eth0" + } + }, + { + "match": { + "left": { + "payload": { + "field": "dport", + "protocol": "tcp" + } + }, + "op": "==", + "right": 443 + } + }, + { + "dnat": { + "addr": "dead::beef", + "family": "ip6", + "port": 4443 + } + } +] + +# dnat ip to ct mark map { 0x00000014 : 1.2.3.4} +[ + { + "dnat": { + "addr": { + "map": { + "data": { + "set": [ + [ + 20, + "1.2.3.4" + ] + ] + }, + "key": { + "ct": { + "key": "mark" + } + } + } + }, + "family": "ip" + } + } +] + +# dnat ip to ct mark . ip daddr map { 0x00000014 . 1.1.1.1 : 1.2.3.4} +[ + { + "dnat": { + "addr": { + "map": { + "data": { + "set": [ + [ + { + "concat": [ + 20, + "1.1.1.1" + ] + }, + "1.2.3.4" + ] + ] + }, + "key": { + "concat": [ + { + "ct": { + "key": "mark" + } + }, + { + "payload": { + "field": "daddr", + "protocol": "ip" + } + } + ] + } + } + }, + "family": "ip" + } + } +] + diff --git a/tests/py/inet/snat.t.json b/tests/py/inet/snat.t.json new file mode 100644 index 0000000000000..4671625dc06d9 --- /dev/null +++ b/tests/py/inet/snat.t.json @@ -0,0 +1,131 @@ +# iifname "eth0" tcp dport 81 snat ip to 192.168.3.2 +[ + { + "match": { + "left": { + "meta": { + "key": "iifname" + } + }, + "op": "==", + "right": "eth0" + } + }, + { + "match": { + "left": { + "payload": { + "field": "dport", + "protocol": "tcp" + } + }, + "op": "==", + "right": 81 + } + }, + { + "snat": { + "addr": "192.168.3.2", + "family": "ip" + } + } +] + +# iifname "eth0" tcp dport 81 ip saddr 10.1.1.1 snat to 192.168.3.2 +[ + { + "match": { + "left": { + "meta": { + "key": "iifname" + } + }, + "op": "==", + "right": "eth0" + } + }, + { + "match": { + "left": { + "payload": { + "field": "dport", + "protocol": "tcp" + } + }, + "op": "==", + "right": 81 + } + }, + { + "match": { + "left": { + "payload": { + "field": "saddr", + "protocol": "ip" + } + }, + "op": "==", + "right": "10.1.1.1" + } + }, + { + "snat": { + "addr": "192.168.3.2", + "family": "ip" + } + } +] + +# iifname "eth0" tcp dport 81 snat ip6 to dead::beef +[ + { + "match": { + "left": { + "meta": { + "key": "iifname" + } + }, + "op": "==", + "right": "eth0" + } + }, + { + "match": { + "left": { + "payload": { + "field": "dport", + "protocol": "tcp" + } + }, + "op": "==", + "right": 81 + } + }, + { + "snat": { + "addr": "dead::beef", + "family": "ip6" + } + } +] + +# iifname "foo" masquerade random +[ + { + "match": { + "left": { + "meta": { + "key": "iifname" + } + }, + "op": "==", + "right": "foo" + } + }, + { + "masquerade": { + "flags": "random" + } + } +] + -- 2.21.0