JSON in/output doesn't know about nat_type and thus cannot save/restore nat mappings involving prefixes or concatenations because the snat statement lacks the prefix/concat/interval type flags. Furthermore, bison parser was extended to support netmap. This is done with an internal 'netmap' flag that is passed to the kernel. We need to dump/restore that as well. Also make sure ip/snat.t passes in json mode. Fixes: 35a6b10c1bc4 ("src: add netmap support") Fixes: 9599d9d25a6b ("src: NAT support for intervals in maps") Signed-off-by: Florian Westphal <fw@xxxxxxxxx> --- src/json.c | 43 +++++++++++++--- src/parser_json.c | 70 +++++++++++++++++++++++++- tests/py/ip/snat.t.json | 108 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 10 deletions(-) diff --git a/src/json.c b/src/json.c index a8824d3fc05a..3c4654d6dada 100644 --- a/src/json.c +++ b/src/json.c @@ -1288,7 +1288,7 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx) return json_pack("{s:o}", "log", root); } -static json_t *nat_flags_json(int flags) +static json_t *nat_flags_json(uint32_t flags) { json_t *array = json_array(); @@ -1298,9 +1298,37 @@ static json_t *nat_flags_json(int flags) json_array_append_new(array, json_string("fully-random")); if (flags & NF_NAT_RANGE_PERSISTENT) json_array_append_new(array, json_string("persistent")); + if (flags & NF_NAT_RANGE_NETMAP) + json_array_append_new(array, json_string("netmap")); return array; } +static json_t *nat_type_flags_json(uint32_t type_flags) +{ + json_t *array = json_array(); + + if (type_flags & STMT_NAT_F_INTERVAL) + json_array_append_new(array, json_string("interval")); + if (type_flags & STMT_NAT_F_PREFIX) + json_array_append_new(array, json_string("prefix")); + if (type_flags & STMT_NAT_F_CONCAT) + json_array_append_new(array, json_string("concat")); + + return array; +} + +static void nat_stmt_add_array(json_t *root, const char *name, json_t *array) +{ + if (json_array_size(array) > 1) { + json_object_set_new(root, name, array); + } else { + if (json_array_size(array)) + json_object_set(root, name, + json_array_get(array, 0)); + json_decref(array); + } +} + json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx) { json_t *root = json_object(); @@ -1322,13 +1350,12 @@ json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx) json_object_set_new(root, "port", expr_print_json(stmt->nat.proto, octx)); - if (json_array_size(array) > 1) { - json_object_set_new(root, "flags", array); - } else { - if (json_array_size(array)) - json_object_set(root, "flags", - json_array_get(array, 0)); - json_decref(array); + nat_stmt_add_array(root, "flags", array); + + if (stmt->nat.type_flags) { + array = nat_type_flags_json(stmt->nat.type_flags); + + nat_stmt_add_array(root, "type_flags", array); } if (!json_object_size(root)) { diff --git a/src/parser_json.c b/src/parser_json.c index ac89166ec8a9..136239121427 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -1358,8 +1358,8 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root) { "set", json_parse_set_expr, CTX_F_RHS | CTX_F_STMT }, /* allow this as stmt expr because that allows set references */ { "map", json_parse_map_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS }, /* below three are multiton_rhs_expr */ - { "prefix", json_parse_prefix_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_CONCAT }, - { "range", json_parse_range_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_CONCAT }, + { "prefix", json_parse_prefix_expr, CTX_F_RHS | CTX_F_SET_RHS | CTX_F_STMT | CTX_F_CONCAT }, + { "range", json_parse_range_expr, CTX_F_RHS | CTX_F_SET_RHS | CTX_F_STMT | CTX_F_CONCAT }, { "payload", json_parse_payload_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, { "exthdr", json_parse_exthdr_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, { "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT }, @@ -1861,6 +1861,7 @@ static int json_parse_nat_flag(struct json_ctx *ctx, { "random", NF_NAT_RANGE_PROTO_RANDOM }, { "fully-random", NF_NAT_RANGE_PROTO_RANDOM_FULLY }, { "persistent", NF_NAT_RANGE_PERSISTENT }, + { "netmap", NF_NAT_RANGE_NETMAP }, }; const char *flag; unsigned int i; @@ -1905,6 +1906,60 @@ static int json_parse_nat_flags(struct json_ctx *ctx, json_t *root) return flags; } +static int json_parse_nat_type_flag(struct json_ctx *ctx, + json_t *root, int *flags) +{ + const struct { + const char *flag; + int val; + } flag_tbl[] = { + { "interval", STMT_NAT_F_INTERVAL }, + { "prefix", STMT_NAT_F_PREFIX }, + { "concat", STMT_NAT_F_CONCAT }, + }; + const char *flag; + unsigned int i; + + assert(flags); + + if (!json_is_string(root)) { + json_error(ctx, "Invalid nat type flag type %s, expected string.", + json_typename(root)); + return 1; + } + flag = json_string_value(root); + for (i = 0; i < array_size(flag_tbl); i++) { + if (!strcmp(flag, flag_tbl[i].flag)) { + *flags |= flag_tbl[i].val; + return 0; + } + } + json_error(ctx, "Unknown nat type flag '%s'.", flag); + return 1; +} + +static int json_parse_nat_type_flags(struct json_ctx *ctx, json_t *root) +{ + int flags = 0; + json_t *value; + size_t index; + + if (json_is_string(root)) { + json_parse_nat_type_flag(ctx, root, &flags); + return flags; + } else if (!json_is_array(root)) { + json_error(ctx, "Invalid nat flags type %s.", + json_typename(root)); + return -1; + } + json_array_foreach(root, index, value) { + if (json_parse_nat_type_flag(ctx, value, &flags)) + json_error(ctx, "Parsing nat type flag at index %zu failed.", + index); + } + return flags; +} + static int nat_type_parse(const char *type) { const char * const nat_etypes[] = { @@ -1967,6 +2022,17 @@ static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx, } stmt->nat.flags = flags; } + + if (!json_unpack(value, "{s:o}", "type_flags", &tmp)) { + int flags = json_parse_nat_type_flags(ctx, tmp); + + if (flags < 0) { + stmt_free(stmt); + return NULL; + } + stmt->nat.type_flags = flags; + } + return stmt; } diff --git a/tests/py/ip/snat.t.json b/tests/py/ip/snat.t.json index e87b524ee667..62c6e61bea7c 100644 --- a/tests/py/ip/snat.t.json +++ b/tests/py/ip/snat.t.json @@ -166,3 +166,111 @@ } ] +# snat ip addr . port to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 } +[ + { + "snat": { + "addr": { + "map": { + "data": { + "set": [ + [ + "10.141.11.4", + { + "concat": [ + "192.168.2.3", + 80 + ] + } + ] + ] + }, + "key": { + "payload": { + "field": "saddr", + "protocol": "ip" + } + } + } + }, + "family": "ip", + "type_flags": "concat" + } + } +] + +# snat ip interval to ip saddr map { 10.141.11.4 : 192.168.2.2-192.168.2.4 } +[ + { + "snat": { + "addr": { + "map": { + "data": { + "set": [ + [ + "10.141.11.4", + { + "range": [ + "192.168.2.2", + "192.168.2.4" + ] + } + ] + ] + }, + "key": { + "payload": { + "field": "saddr", + "protocol": "ip" + } + } + } + }, + "family": "ip", + "type_flags": "interval" + } + } +] + +# snat ip prefix to ip saddr map { 10.141.11.0/24 : 192.168.2.0/24 } +[ + { + "snat": { + "addr": { + "map": { + "data": { + "set": [ + [ + { + "prefix": { + "addr": "10.141.11.0", + "len": 24 + } + }, + { + "prefix": { + "addr": "192.168.2.0", + "len": 24 + } + } + ] + ] + }, + "key": { + "payload": { + "field": "saddr", + "protocol": "ip" + } + } + } + }, + "family": "ip", + "flags": "netmap", + "type_flags": [ + "interval", + "prefix" + ] + } + } +] + -- 2.26.2