diff options
Diffstat (limited to 'src/parser_json.c')
-rw-r--r-- | src/parser_json.c | 561 |
1 files changed, 434 insertions, 127 deletions
diff --git a/src/parser_json.c b/src/parser_json.c index 76c268f8..8b7efaf2 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -1,7 +1,14 @@ -#define _GNU_SOURCE +/* + * Copyright (c) Red Hat GmbH. Author: Phil Sutter <phil@nwl.cc> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + #include <errno.h> -#include <stdint.h> /* needed by gmputil.h */ -#include <string.h> #include <syslog.h> #include <erec.h> @@ -533,6 +540,27 @@ static const struct proto_desc *proto_lookup_byname(const char *name) &proto_dccp, &proto_sctp, &proto_th, + &proto_vxlan, + &proto_gre, + &proto_gretap, + &proto_geneve, + }; + unsigned int i; + + for (i = 0; i < array_size(proto_tbl); i++) { + if (!strcmp(proto_tbl[i]->name, name)) + return proto_tbl[i]; + } + return NULL; +} + +static const struct proto_desc *inner_proto_lookup_byname(const char *name) +{ + const struct proto_desc *proto_tbl[] = { + &proto_geneve, + &proto_gre, + &proto_gretap, + &proto_vxlan, }; unsigned int i; @@ -546,7 +574,7 @@ static const struct proto_desc *proto_lookup_byname(const char *name) static struct expr *json_parse_payload_expr(struct json_ctx *ctx, const char *type, json_t *root) { - const char *protocol, *field, *base; + const char *tunnel, *protocol, *field, *base; int offset, len, val; struct expr *expr; @@ -564,11 +592,45 @@ static struct expr *json_parse_payload_expr(struct json_ctx *ctx, json_error(ctx, "Invalid payload base '%s'.", base); return NULL; } + + if (len <= 0 || len > (int)NFT_MAX_EXPR_LEN_BITS) { + json_error(ctx, "Payload length must be between 0 and %lu, got %d", + NFT_MAX_EXPR_LEN_BITS, len); + return NULL; + } + expr = payload_expr_alloc(int_loc, NULL, 0); payload_init_raw(expr, val, offset, len); expr->byteorder = BYTEORDER_BIG_ENDIAN; expr->payload.is_raw = true; return expr; + } else if (!json_unpack(root, "{s:s, s:s, s:s}", + "tunnel", &tunnel, "protocol", &protocol, "field", &field)) { + const struct proto_desc *proto = proto_lookup_byname(protocol); + const struct proto_desc *inner_proto = inner_proto_lookup_byname(tunnel); + + if (!inner_proto) { + json_error(ctx, "Unknown payload tunnel protocol '%s'.", + tunnel); + return NULL; + } + if (!proto) { + json_error(ctx, "Unknown payload protocol '%s'.", + protocol); + return NULL; + } + if (json_parse_payload_field(proto, field, &val)) { + json_error(ctx, "Unknown %s field '%s'.", + protocol, field); + return NULL; + } + expr = payload_expr_alloc(int_loc, proto, val); + expr->payload.inner_desc = inner_proto; + + if (proto == &proto_th) + expr->payload.is_raw = true; + + return expr; } else if (!json_unpack(root, "{s:s, s:s}", "protocol", &protocol, "field", &field)) { const struct proto_desc *proto = proto_lookup_byname(protocol); @@ -602,12 +664,18 @@ static struct expr *json_parse_tcp_option_expr(struct json_ctx *ctx, struct expr *expr; if (!json_unpack(root, "{s:i, s:i, s:i}", - "base", &kind, "offset", &offset, "len", &len)) { + "base", &kind, "offset", &offset, "len", &len)) { uint32_t flag = 0; if (kind < 0 || kind > 255) return NULL; + if (len < 0 || len > (int)NFT_MAX_EXPR_LEN_BITS) { + json_error(ctx, "option length must be between 0 and %lu, got %d", + NFT_MAX_EXPR_LEN_BITS, len); + return NULL; + } + expr = tcpopt_expr_alloc(int_loc, kind, TCPOPT_COMMON_KIND); @@ -673,7 +741,7 @@ static int json_parse_ip_option_field(int type, const char *name, int *val) } static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx, - const char *type, json_t *root) + const char *type, json_t *root) { const char *desc, *field; int descval, fieldval; @@ -689,7 +757,7 @@ static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx, if (json_unpack(root, "{s:s}", "field", &field)) { expr = ipopt_expr_alloc(int_loc, descval, - IPOPT_FIELD_TYPE); + IPOPT_FIELD_TYPE); expr->exthdr.flags = NFT_EXTHDR_F_PRESENT; return expr; @@ -748,6 +816,22 @@ static struct expr *json_parse_sctp_chunk_expr(struct json_ctx *ctx, return sctp_chunk_expr_alloc(int_loc, desc->type, fieldval); } +static struct expr *json_parse_dccp_option_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + int opt_type; + + if (json_unpack_err(ctx, root, "{s:i}", "type", &opt_type)) + return NULL; + + if (opt_type < DCCPOPT_TYPE_MIN || opt_type > DCCPOPT_TYPE_MAX) { + json_error(ctx, "Unknown dccp option type '%d'.", opt_type); + return NULL; + } + + return dccpopt_expr_alloc(int_loc, opt_type); +} + static const struct exthdr_desc *exthdr_lookup_byname(const char *name) { const struct exthdr_desc *exthdr_tbl[] = { @@ -1076,13 +1160,13 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx, } if ((flagval & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == - (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) { + (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) { json_error(ctx, "fib: saddr and daddr are mutually exclusive"); return NULL; } if ((flagval & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) == - (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) { + (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) { json_error(ctx, "fib: iif and oif are mutually exclusive"); return NULL; } @@ -1120,6 +1204,18 @@ static struct expr *json_parse_binop_expr(struct json_ctx *ctx, return NULL; } + if (json_array_size(root) > 2) { + left = json_parse_primary_expr(ctx, json_array_get(root, 0)); + right = json_parse_primary_expr(ctx, json_array_get(root, 1)); + left = binop_expr_alloc(int_loc, thisop, left, right); + for (i = 2; i < json_array_size(root); i++) { + jright = json_array_get(root, i); + right = json_parse_primary_expr(ctx, jright); + left = binop_expr_alloc(int_loc, thisop, left, right); + } + return left; + } + if (json_unpack_err(ctx, root, "[o, o!]", &jleft, &jright)) return NULL; @@ -1454,6 +1550,7 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root) { "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT }, { "ip option", json_parse_ip_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT }, { "sctp chunk", json_parse_sctp_chunk_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT }, + { "dccp option", json_parse_dccp_option_expr, CTX_F_PRIMARY }, { "meta", json_parse_meta_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, { "osf", json_parse_osf_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT }, { "ipsec", json_parse_xfrm_expr, CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT }, @@ -1652,13 +1749,18 @@ static struct stmt *json_parse_match_stmt(struct json_ctx *ctx, !strcmp(opstr, expr_op_symbols[op])) break; } - if (op == __OP_MAX) { + switch (op) { + case OP_EQ ... OP_NEG: + break; + case __OP_MAX: if (!strcmp(opstr, "in")) { op = OP_IMPLICIT; - } else { - json_error(ctx, "Unknown relational op '%s'.", opstr); - return NULL; + break; } + /* fall through */ + default: + json_error(ctx, "Invalid relational op '%s'.", opstr); + return NULL; } left = json_parse_expr(ctx, jleft); @@ -1678,7 +1780,7 @@ static struct stmt *json_parse_match_stmt(struct json_ctx *ctx, } static struct stmt *json_parse_counter_stmt(struct json_ctx *ctx, - const char *key, json_t *value) + const char *key, json_t *value) { uint64_t packets, bytes; struct stmt *stmt; @@ -1687,8 +1789,8 @@ static struct stmt *json_parse_counter_stmt(struct json_ctx *ctx, return counter_stmt_alloc(int_loc); if (!json_unpack(value, "{s:I, s:I}", - "packets", &packets, - "bytes", &bytes)) { + "packets", &packets, + "bytes", &bytes)) { stmt = counter_stmt_alloc(int_loc); stmt->counter.packets = packets; stmt->counter.bytes = bytes; @@ -1706,6 +1808,27 @@ static struct stmt *json_parse_counter_stmt(struct json_ctx *ctx, return stmt; } +static struct stmt *json_parse_last_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt; + int64_t used; + + if (json_is_null(value)) + return last_stmt_alloc(int_loc); + + if (!json_unpack(value, "{s:I}", "used", &used)) { + stmt = last_stmt_alloc(int_loc); + if (used != -1) { + stmt->last.used = used; + stmt->last.set = 1; + } + return stmt; + } + + return NULL; +} + static struct stmt *json_parse_verdict_stmt(struct json_ctx *ctx, const char *key, json_t *value) { @@ -1719,14 +1842,14 @@ static struct stmt *json_parse_verdict_stmt(struct json_ctx *ctx, } static struct stmt *json_parse_mangle_stmt(struct json_ctx *ctx, - const char *type, json_t *root) + const char *type, json_t *root) { json_t *jkey, *jvalue; struct expr *key, *value; struct stmt *stmt; if (json_unpack_err(ctx, root, "{s:o, s:o}", - "key", &jkey, "value", &jvalue)) + "key", &jkey, "value", &jvalue)) return NULL; key = json_parse_mangle_lhs_expr(ctx, jkey); @@ -1779,7 +1902,7 @@ static uint64_t rate_to_bytes(uint64_t val, const char *unit) } static struct stmt *json_parse_quota_stmt(struct json_ctx *ctx, - const char *key, json_t *value) + const char *key, json_t *value) { struct stmt *stmt; int inv = 0; @@ -1929,7 +2052,7 @@ static struct stmt *json_parse_flow_offload_stmt(struct json_ctx *ctx, } static struct stmt *json_parse_notrack_stmt(struct json_ctx *ctx, - const char *key, json_t *value) + const char *key, json_t *value) { return notrack_stmt_alloc(int_loc); } @@ -1967,7 +2090,7 @@ static struct stmt *json_parse_dup_stmt(struct json_ctx *ctx, } static struct stmt *json_parse_secmark_stmt(struct json_ctx *ctx, - const char *key, json_t *value) + const char *key, json_t *value) { struct stmt *stmt; @@ -2039,7 +2162,7 @@ static int json_parse_nat_flags(struct json_ctx *ctx, json_t *root) } static int json_parse_nat_type_flag(struct json_ctx *ctx, - json_t *root, int *flags) + json_t *root, int *flags) { const struct { const char *flag; @@ -2154,7 +2277,6 @@ 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); @@ -2169,7 +2291,7 @@ static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx, } static struct stmt *json_parse_tproxy_stmt(struct json_ctx *ctx, - const char *key, json_t *value) + const char *key, json_t *value) { json_t *jaddr, *tmp; struct stmt *stmt; @@ -2205,7 +2327,7 @@ out_free: } static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx, - const char *key, json_t *value) + const char *key, json_t *value) { struct stmt *stmt = reject_stmt_alloc(int_loc); const struct datatype *dtype = NULL; @@ -2221,17 +2343,17 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx, stmt->reject.icmp_code = 0; } else if (!strcmp(type, "icmpx")) { stmt->reject.type = NFT_REJECT_ICMPX_UNREACH; - dtype = &icmpx_code_type; + dtype = &reject_icmpx_code_type; stmt->reject.icmp_code = 0; } else if (!strcmp(type, "icmp")) { stmt->reject.type = NFT_REJECT_ICMP_UNREACH; stmt->reject.family = NFPROTO_IPV4; - dtype = &icmp_code_type; + dtype = &reject_icmp_code_type; stmt->reject.icmp_code = 0; } else if (!strcmp(type, "icmpv6")) { stmt->reject.type = NFT_REJECT_ICMP_UNREACH; stmt->reject.family = NFPROTO_IPV6; - dtype = &icmpv6_code_type; + dtype = &reject_icmpv6_code_type; stmt->reject.icmp_code = 0; } } @@ -2248,8 +2370,8 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx, } static void json_parse_set_stmt_list(struct json_ctx *ctx, - struct list_head *stmt_list, - json_t *stmt_json) + struct list_head *stmt_list, + json_t *stmt_json) { struct list_head *head; struct stmt *tmp; @@ -2271,7 +2393,7 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx, } static struct stmt *json_parse_set_stmt(struct json_ctx *ctx, - const char *key, json_t *value) + const char *key, json_t *value) { const char *opstr, *set; struct expr *expr, *expr2; @@ -2318,6 +2440,63 @@ static struct stmt *json_parse_set_stmt(struct json_ctx *ctx, return stmt; } +static struct stmt *json_parse_map_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct expr *expr, *expr2, *expr_data; + json_t *elem, *data, *stmt_json; + const char *opstr, *set; + struct stmt *stmt; + int op; + + if (json_unpack_err(ctx, value, "{s:s, s:o, s:o, s:s}", + "op", &opstr, "elem", &elem, "data", &data, "map", &set)) + return NULL; + + if (!strcmp(opstr, "add")) { + op = NFT_DYNSET_OP_ADD; + } else if (!strcmp(opstr, "update")) { + op = NFT_DYNSET_OP_UPDATE; + } else if (!strcmp(opstr, "delete")) { + op = NFT_DYNSET_OP_DELETE; + } else { + json_error(ctx, "Unknown map statement op '%s'.", opstr); + return NULL; + } + + expr = json_parse_set_elem_expr_stmt(ctx, elem); + if (!expr) { + json_error(ctx, "Illegal map statement element."); + return NULL; + } + + expr_data = json_parse_set_elem_expr_stmt(ctx, data); + if (!expr_data) { + json_error(ctx, "Illegal map expression data."); + expr_free(expr); + return NULL; + } + + if (set[0] != '@') { + json_error(ctx, "Illegal map reference in map statement."); + expr_free(expr); + expr_free(expr_data); + return NULL; + } + expr2 = symbol_expr_alloc(int_loc, SYMBOL_SET, NULL, set + 1); + + stmt = map_stmt_alloc(int_loc); + stmt->map.op = op; + stmt->map.key = expr; + stmt->map.data = expr_data; + stmt->map.set = expr2; + + if (!json_unpack(value, "{s:o}", "stmt", &stmt_json)) + json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json); + + return stmt; +} + static int json_parse_log_flag(struct json_ctx *ctx, json_t *root, int *flags) { @@ -2554,7 +2733,7 @@ static struct stmt *json_parse_cthelper_stmt(struct json_ctx *ctx, } static struct stmt *json_parse_cttimeout_stmt(struct json_ctx *ctx, - const char *key, json_t *value) + const char *key, json_t *value) { struct stmt *stmt = objref_stmt_alloc(int_loc); @@ -2589,7 +2768,7 @@ static struct stmt *json_parse_meter_stmt(struct json_ctx *ctx, json_t *jkey, *jstmt; struct stmt *stmt; const char *name; - uint32_t size = 0xffff; + uint32_t size = 0; if (json_unpack_err(ctx, value, "{s:s, s:o, s:o}", "name", &name, "key", &jkey, "stmt", &jstmt)) @@ -2704,7 +2883,14 @@ static struct stmt *json_parse_optstrip_stmt(struct json_ctx *ctx, { struct expr *expr = json_parse_expr(ctx, value); - return expr ? optstrip_stmt_alloc(int_loc, expr) : NULL; + if (!expr || + expr->etype != EXPR_EXTHDR || + expr->exthdr.op != NFT_EXTHDR_OP_TCPOPT) { + json_error(ctx, "Illegal TCP optstrip argument"); + return NULL; + } + + return optstrip_stmt_alloc(int_loc, expr); } static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root) @@ -2723,6 +2909,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root) { "counter", json_parse_counter_stmt }, { "mangle", json_parse_mangle_stmt }, { "quota", json_parse_quota_stmt }, + { "last", json_parse_last_stmt }, { "limit", json_parse_limit_stmt }, { "flow", json_parse_flow_offload_stmt }, { "fwd", json_parse_fwd_stmt }, @@ -2734,6 +2921,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root) { "redirect", json_parse_nat_stmt }, { "reject", json_parse_reject_stmt }, { "set", json_parse_set_stmt }, + { "map", json_parse_map_stmt }, { "log", json_parse_log_stmt }, { "ct helper", json_parse_cthelper_stmt }, { "ct timeout", json_parse_cttimeout_stmt }, @@ -2764,6 +2952,11 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root) return verdict_stmt_alloc(int_loc, expr); } + if (!strcmp(type, "xt")) { + json_error(ctx, "unsupported xtables compat expression, use iptables-nft with this ruleset"); + return NULL; + } + for (i = 0; i < array_size(stmt_parser_tbl); i++) { if (!strcmp(type, stmt_parser_tbl[i].key)) return stmt_parser_tbl[i].cb(ctx, stmt_parser_tbl[i].key, tmp); @@ -2773,20 +2966,67 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root) return NULL; } +static int json_parse_table_flags(struct json_ctx *ctx, json_t *root, + enum table_flags *flags) +{ + json_t *tmp, *tmp2; + size_t index; + int flag; + + if (json_unpack(root, "{s:o}", "flags", &tmp)) + return 0; + + if (json_is_string(tmp)) { + flag = parse_table_flag(json_string_value(tmp)); + if (flag) { + *flags = flag; + return 0; + } + json_error(ctx, "Invalid table flag '%s'.", + json_string_value(tmp)); + return 1; + } + if (!json_is_array(tmp)) { + json_error(ctx, "Unexpected table flags value."); + return 1; + } + json_array_foreach(tmp, index, tmp2) { + if (json_is_string(tmp2)) { + flag = parse_table_flag(json_string_value(tmp2)); + + if (flag) { + *flags |= flag; + continue; + } + } + json_error(ctx, "Invalid table flag at index %zu.", index); + return 1; + } + return 0; +} + static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root, enum cmd_ops op, enum cmd_obj obj) { + const char *family = "", *comment = NULL; struct handle h = { .table.location = *int_loc, }; - const char *family = ""; + struct table *table = NULL; + enum table_flags flags = 0; if (json_unpack_err(ctx, root, "{s:s}", "family", &family)) return NULL; - if (op != CMD_DELETE && - json_unpack_err(ctx, root, "{s:s}", "name", &h.table.name)) { - return NULL; + + if (op != CMD_DELETE) { + if (json_unpack_err(ctx, root, "{s:s}", "name", &h.table.name)) + return NULL; + + json_unpack(root, "{s:s}", "comment", &comment); + if (json_parse_table_flags(ctx, root, &flags)) + return NULL; + } else if (op == CMD_DELETE && json_unpack(root, "{s:s}", "name", &h.table.name) && json_unpack(root, "{s:I}", "handle", &h.handle.id)) { @@ -2800,10 +3040,18 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root, if (h.table.name) h.table.name = xstrdup(h.table.name); + if (comment || flags) { + table = table_alloc(); + handle_merge(&table->handle, &h); + if (comment) + table->comment = xstrdup(comment); + table->flags = flags; + } + if (op == CMD_ADD) json_object_del(root, "handle"); - return cmd_alloc(op, obj, &h, int_loc, NULL); + return cmd_alloc(op, obj, &h, int_loc, table); } static struct expr *parse_policy(const char *policy) @@ -2822,23 +3070,60 @@ static struct expr *parse_policy(const char *policy) sizeof(int) * BITS_PER_BYTE, &policy_num); } +static struct expr *json_parse_devs(struct json_ctx *ctx, json_t *root) +{ + struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST); + const char *dev; + json_t *value; + size_t index; + + if (!json_unpack(root, "s", &dev)) { + tmp = constant_expr_alloc(int_loc, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(dev) * BITS_PER_BYTE, dev); + compound_expr_add(expr, tmp); + return expr; + } + if (!json_is_array(root)) { + expr_free(expr); + return NULL; + } + + json_array_foreach(root, index, value) { + if (json_unpack(value, "s", &dev)) { + json_error(ctx, "Invalid device at index %zu.", + index); + expr_free(expr); + return NULL; + } + tmp = constant_expr_alloc(int_loc, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(dev) * BITS_PER_BYTE, dev); + compound_expr_add(expr, tmp); + } + return expr; +} + static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, enum cmd_ops op, enum cmd_obj obj) { struct handle h = { .table.location = *int_loc, }; - const char *family = "", *policy = "", *type, *hookstr, *name; - struct chain *chain; + const char *family = "", *policy = "", *type, *hookstr, *comment = NULL; + struct chain *chain = NULL; + json_t *devs = NULL; int prio; if (json_unpack_err(ctx, root, "{s:s, s:s}", "family", &family, "table", &h.table.name)) return NULL; - if (op != CMD_DELETE && - json_unpack_err(ctx, root, "{s:s}", "name", &h.chain.name)) { - return NULL; + if (op != CMD_DELETE) { + if (json_unpack_err(ctx, root, "{s:s}", "name", &h.chain.name)) + return NULL; + + json_unpack(root, "{s:s}", "comment", &comment); } else if (op == CMD_DELETE && json_unpack(root, "{s:s}", "name", &h.chain.name) && json_unpack(root, "{s:I}", "handle", &h.handle.id)) { @@ -2853,14 +3138,22 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, if (h.chain.name) h.chain.name = xstrdup(h.chain.name); + if (comment) { + chain = chain_alloc(); + handle_merge(&chain->handle, &h); + chain->comment = xstrdup(comment); + } + if (op == CMD_DELETE || op == CMD_LIST || op == CMD_FLUSH || json_unpack(root, "{s:s, s:s, s:i}", "type", &type, "hook", &hookstr, "prio", &prio)) - return cmd_alloc(op, obj, &h, int_loc, NULL); + return cmd_alloc(op, obj, &h, int_loc, chain); + + if (!chain) + chain = chain_alloc(); - chain = chain_alloc(NULL); chain->flags |= CHAIN_F_BASECHAIN; chain->type.str = xstrdup(type); chain->priority.expr = constant_expr_alloc(int_loc, &integer_type, @@ -2874,16 +3167,15 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, return NULL; } - if (!json_unpack(root, "{s:s}", "dev", &name)) { - struct expr *dev_expr, *expr; + json_unpack(root, "{s:o}", "dev", &devs); - dev_expr = compound_expr_alloc(int_loc, EXPR_LIST); - expr = constant_expr_alloc(int_loc, &integer_type, - BYTEORDER_HOST_ENDIAN, - strlen(name) * BITS_PER_BYTE, - name); - compound_expr_add(dev_expr, expr); - chain->dev_expr = dev_expr; + if (devs) { + chain->dev_expr = json_parse_devs(ctx, devs); + if (!chain->dev_expr) { + json_error(ctx, "Invalid chain dev."); + chain_free(chain); + return NULL; + } } if (!json_unpack(root, "{s:s}", "policy", &policy)) { @@ -2923,7 +3215,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root, if (op != CMD_DELETE && json_unpack_err(ctx, root, "{s:o}", "expr", &tmp)) return NULL; - else if (op == CMD_DELETE && + else if ((op == CMD_DELETE || op == CMD_DESTROY) && json_unpack_err(ctx, root, "{s:I}", "handle", &h.handle.id)) return NULL; @@ -2934,7 +3226,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root, h.table.name = xstrdup(h.table.name); h.chain.name = xstrdup(h.chain.name); - if (op == CMD_DELETE) + if (op == CMD_DELETE || op == CMD_DESTROY) return cmd_alloc(op, obj, &h, int_loc, NULL); if (!json_is_array(tmp)) { @@ -2982,14 +3274,18 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root, static int string_to_nft_object(const char *str) { const char *obj_tbl[__NFT_OBJECT_MAX] = { - [NFT_OBJECT_COUNTER] = "counter", - [NFT_OBJECT_QUOTA] = "quota", - [NFT_OBJECT_LIMIT] = "limit", - [NFT_OBJECT_SECMARK] = "secmark", + [NFT_OBJECT_COUNTER] = "counter", + [NFT_OBJECT_QUOTA] = "quota", + [NFT_OBJECT_CT_HELPER] = "ct helper", + [NFT_OBJECT_LIMIT] = "limit", + [NFT_OBJECT_CT_TIMEOUT] = "ct timeout", + [NFT_OBJECT_SECMARK] = "secmark", + [NFT_OBJECT_CT_EXPECT] = "ct expectation", + [NFT_OBJECT_SYNPROXY] = "synproxy", }; unsigned int i; - for (i = 0; i < NFT_OBJECT_MAX; i++) { + for (i = 0; i <= NFT_OBJECT_MAX; i++) { if (obj_tbl[i] && !strcmp(str, obj_tbl[i])) return i; } @@ -3005,6 +3301,7 @@ static int string_to_set_flag(const char *str) { NFT_SET_CONSTANT, "constant" }, { NFT_SET_INTERVAL, "interval" }, { NFT_SET_TIMEOUT, "timeout" }, + { NFT_SET_EVAL, "dynamic" }, }; unsigned int i; @@ -3019,7 +3316,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, enum cmd_ops op, enum cmd_obj obj) { struct handle h = { 0 }; - const char *family = "", *policy, *dtype_ext = NULL; + const char *family = "", *policy; json_t *tmp, *stmt_json; struct set *set; @@ -3030,7 +3327,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, if (op != CMD_DELETE && json_unpack_err(ctx, root, "{s:s}", "name", &h.set.name)) { return NULL; - } else if (op == CMD_DELETE && + } else if ((op == CMD_DELETE || op == CMD_DESTROY) && json_unpack(root, "{s:s}", "name", &h.set.name) && json_unpack(root, "{s:I}", "handle", &h.handle.id)) { json_error(ctx, "Either name or handle required to delete a set."); @@ -3047,14 +3344,16 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, switch (op) { case CMD_DELETE: + case CMD_DESTROY: case CMD_LIST: case CMD_FLUSH: + case CMD_RESET: return cmd_alloc(op, obj, &h, int_loc, NULL); default: break; } - set = set_alloc(NULL); + set = set_alloc(&internal_location); if (json_unpack(root, "{s:o}", "type", &tmp)) { json_error(ctx, "Invalid set type."); @@ -3070,19 +3369,19 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, return NULL; } - if (!json_unpack(root, "{s:s}", "map", &dtype_ext)) { - const struct datatype *dtype; + if (!json_unpack(root, "{s:o}", "map", &tmp)) { + if (json_is_string(tmp)) { + const char *s = json_string_value(tmp); - set->objtype = string_to_nft_object(dtype_ext); + set->objtype = string_to_nft_object(s); + } if (set->objtype) { set->flags |= NFT_SET_OBJECT; - } else if ((dtype = datatype_lookup_byname(dtype_ext))) { - set->data = constant_expr_alloc(&netlink_location, - dtype, dtype->byteorder, - dtype->size, NULL); + } else if ((set->data = json_parse_dtype_expr(ctx, tmp))) { set->flags |= NFT_SET_MAP; } else { - json_error(ctx, "Invalid map type '%s'.", dtype_ext); + json_error(ctx, "Invalid map type '%s'.", + json_dumps(tmp, 0)); set_free(set); handle_free(&h); return NULL; @@ -3131,6 +3430,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, if (!json_unpack(root, "{s:i}", "gc-interval", &set->gc_int)) set->gc_int *= 1000; json_unpack(root, "{s:i}", "size", &set->desc.size); + json_unpack(root, "{s:b}", "auto-merge", &set->automerge); if (!json_unpack(root, "{s:o}", "stmt", &stmt_json)) json_parse_set_stmt_list(ctx, &set->stmt_list, stmt_json); @@ -3175,41 +3475,6 @@ static struct cmd *json_parse_cmd_add_element(struct json_ctx *ctx, return cmd_alloc(op, cmd_obj, &h, int_loc, expr); } -static struct expr *json_parse_flowtable_devs(struct json_ctx *ctx, - json_t *root) -{ - struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST); - const char *dev; - json_t *value; - size_t index; - - if (!json_unpack(root, "s", &dev)) { - tmp = constant_expr_alloc(int_loc, &string_type, - BYTEORDER_HOST_ENDIAN, - strlen(dev) * BITS_PER_BYTE, dev); - compound_expr_add(expr, tmp); - return expr; - } - if (!json_is_array(root)) { - expr_free(expr); - return NULL; - } - - json_array_foreach(root, index, value) { - if (json_unpack(value, "s", &dev)) { - json_error(ctx, "Invalid flowtable dev at index %zu.", - index); - expr_free(expr); - return NULL; - } - tmp = constant_expr_alloc(int_loc, &string_type, - BYTEORDER_HOST_ENDIAN, - strlen(dev) * BITS_PER_BYTE, dev); - compound_expr_add(expr, tmp); - } - return expr; -} - static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, json_t *root, enum cmd_ops op, enum cmd_obj cmd_obj) @@ -3228,7 +3493,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, if (op != CMD_DELETE && json_unpack_err(ctx, root, "{s:s}", "name", &h.flowtable.name)) { return NULL; - } else if (op == CMD_DELETE && + } else if ((op == CMD_DELETE || op == CMD_DESTROY) && json_unpack(root, "{s:s}", "name", &h.flowtable.name) && json_unpack(root, "{s:I}", "handle", &h.handle.id)) { json_error(ctx, "Either name or handle required to delete a flowtable."); @@ -3243,17 +3508,17 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, if (h.flowtable.name) h.flowtable.name = xstrdup(h.flowtable.name); - if (op == CMD_DELETE || op == CMD_LIST) + if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY) return cmd_alloc(op, cmd_obj, &h, int_loc, NULL); - if (json_unpack_err(ctx, root, "{s:s, s:I}", + if (json_unpack_err(ctx, root, "{s:s, s:i}", "hook", &hook, "prio", &prio)) { handle_free(&h); return NULL; } - json_unpack(root, "{s:o}", &devs); + json_unpack(root, "{s:o}", "dev", &devs); hookstr = chain_hookname_lookup(hook); if (!hookstr) { @@ -3270,7 +3535,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, sizeof(int) * BITS_PER_BYTE, &prio); if (devs) { - flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs); + flowtable->dev_expr = json_parse_devs(ctx, devs); if (!flowtable->dev_expr) { json_error(ctx, "Invalid flowtable dev."); flowtable_free(flowtable); @@ -3287,7 +3552,7 @@ static int json_parse_ct_timeout_policy(struct json_ctx *ctx, json_t *tmp, *val; const char *key; - if (!json_unpack(root, "{s:o}", "policy", &tmp)) + if (json_unpack(root, "{s:o}", "policy", &tmp)) return 0; if (!json_is_object(tmp)) { @@ -3319,8 +3584,8 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, { const char *family, *tmp, *rate_unit = "packets", *burst_unit = "bytes"; uint32_t l3proto = NFPROTO_UNSPEC; + int inv = 0, flags = 0, i, j; struct handle h = { 0 }; - int inv = 0, flags = 0; struct obj *obj; json_t *jflags; @@ -3332,7 +3597,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, cmd_obj == NFT_OBJECT_CT_HELPER) && json_unpack_err(ctx, root, "{s:s}", "name", &h.obj.name)) { return NULL; - } else if (op == CMD_DELETE && + } else if ((op == CMD_DELETE || op == CMD_DESTROY) && cmd_obj != NFT_OBJECT_CT_HELPER && json_unpack(root, "{s:s}", "name", &h.obj.name) && json_unpack(root, "{s:I}", "handle", &h.handle.id)) { @@ -3348,7 +3613,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, if (h.obj.name) h.obj.name = xstrdup(h.obj.name); - if (op == CMD_DELETE || op == CMD_LIST) { + if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY) { if (cmd_obj == NFT_OBJECT_CT_HELPER) return cmd_alloc_obj_ct(op, NFT_OBJECT_CT_HELPER, &h, int_loc, obj_alloc(int_loc)); @@ -3442,7 +3707,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, obj_free(obj); return NULL; } - obj->ct_helper.l3proto = l3proto; + obj->ct_timeout.l3proto = l3proto; init_list_head(&obj->ct_timeout.timeout_list); if (json_parse_ct_timeout_policy(ctx, root, obj)) { @@ -3471,11 +3736,12 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, return NULL; } } - if (!json_unpack(root, "{s:o}", "dport", &tmp)) - obj->ct_expect.dport = atoi(tmp); - json_unpack(root, "{s:I}", "timeout", &obj->ct_expect.timeout); - if (!json_unpack(root, "{s:o}", "size", &tmp)) - obj->ct_expect.size = atoi(tmp); + if (!json_unpack(root, "{s:i}", "dport", &i)) + obj->ct_expect.dport = i; + if (!json_unpack(root, "{s:i}", "timeout", &i)) + obj->ct_expect.timeout = i; + if (!json_unpack(root, "{s:i}", "size", &i)) + obj->ct_expect.size = i; break; case CMD_OBJ_LIMIT: obj->type = NFT_OBJECT_LIMIT; @@ -3487,7 +3753,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, } json_unpack(root, "{s:s}", "rate_unit", &rate_unit); json_unpack(root, "{s:b}", "inv", &inv); - json_unpack(root, "{s:I}", "burst", &obj->limit.burst); + json_unpack(root, "{s:i}", "burst", &obj->limit.burst); json_unpack(root, "{s:s}", "burst_unit", &burst_unit); if (!strcmp(rate_unit, "packets")) { @@ -3505,11 +3771,12 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, case CMD_OBJ_SYNPROXY: obj->type = NFT_OBJECT_SYNPROXY; if (json_unpack_err(ctx, root, "{s:i, s:i}", - "mss", &obj->synproxy.mss, - "wscale", &obj->synproxy.wscale)) { + "mss", &i, "wscale", &j)) { obj_free(obj); return NULL; } + obj->synproxy.mss = i; + obj->synproxy.wscale = j; obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS; obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE; if (!json_unpack(root, "{s:o}", "flags", &jflags)) { @@ -3553,7 +3820,8 @@ static struct cmd *json_parse_cmd_add(struct json_ctx *ctx, { "ct timeout", NFT_OBJECT_CT_TIMEOUT, json_parse_cmd_add_object }, { "ct expectation", NFT_OBJECT_CT_EXPECT, json_parse_cmd_add_object }, { "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object }, - { "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object } + { "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object }, + { "synproxy", CMD_OBJ_SYNPROXY, json_parse_cmd_add_object } }; unsigned int i; json_t *tmp; @@ -3750,6 +4018,39 @@ static struct cmd *json_parse_cmd_list(struct json_ctx *ctx, return NULL; } +static struct cmd *json_parse_cmd_reset_rule(struct json_ctx *ctx, + json_t *root, enum cmd_ops op, + enum cmd_obj obj) +{ + struct handle h = { + .family = NFPROTO_UNSPEC, + }; + const char *family = NULL, *table = NULL, *chain = NULL; + + + if (obj == CMD_OBJ_RULE && + json_unpack_err(ctx, root, "{s:s, s:s, s:s, s:I}", + "family", &family, "table", &table, + "chain", &chain, "handle", &h.handle.id)) + return NULL; + else if (obj == CMD_OBJ_RULES) { + json_unpack(root, "{s:s}", "family", &family); + json_unpack(root, "{s:s}", "table", &table); + json_unpack(root, "{s:s}", "chain", &chain); + } + + if (family && parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + if (table) { + h.table.name = xstrdup(table); + if (chain) + h.chain.name = xstrdup(chain); + } + return cmd_alloc(op, obj, &h, int_loc, NULL); +} + static struct cmd *json_parse_cmd_reset(struct json_ctx *ctx, json_t *root, enum cmd_ops op) { @@ -3763,6 +4064,11 @@ static struct cmd *json_parse_cmd_reset(struct json_ctx *ctx, { "counters", CMD_OBJ_COUNTERS, json_parse_cmd_list_multiple }, { "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object }, { "quotas", CMD_OBJ_QUOTAS, json_parse_cmd_list_multiple }, + { "rule", CMD_OBJ_RULE, json_parse_cmd_reset_rule }, + { "rules", CMD_OBJ_RULES, json_parse_cmd_reset_rule }, + { "element", CMD_OBJ_ELEMENTS, json_parse_cmd_add_element }, + { "set", CMD_OBJ_SET, json_parse_cmd_add_set }, + { "map", CMD_OBJ_MAP, json_parse_cmd_add_set }, }; unsigned int i; json_t *tmp; @@ -3861,6 +4167,7 @@ static struct cmd *json_parse_cmd(struct json_ctx *ctx, json_t *root) { "reset", CMD_RESET, json_parse_cmd_reset }, { "flush", CMD_FLUSH, json_parse_cmd_flush }, { "rename", CMD_RENAME, json_parse_cmd_rename }, + { "destroy", CMD_DESTROY, json_parse_cmd_add }, //{ "export", CMD_EXPORT, json_parse_cmd_export }, //{ "monitor", CMD_MONITOR, json_parse_cmd_monitor }, //{ "describe", CMD_DESCRIBE, json_parse_cmd_describe } |