return root ? : json_null();
}
-static json_t *obj_print_json(const struct obj *obj)
+static json_t *tunnel_erspan_print_json(const struct obj *obj)
+{
+ json_t *tunnel;
+
+ switch (obj->tunnel.erspan.version) {
+ case 1:
+ tunnel = json_pack("{s:i, s:i}",
+ "version", obj->tunnel.erspan.version,
+ "index", obj->tunnel.erspan.v1.index);
+ break;
+ case 2:
+ tunnel = json_pack("{s:i, s:s, s:i}",
+ "version", obj->tunnel.erspan.version,
+ "dir", obj->tunnel.erspan.v2.direction ?
+ "egress" : "ingress",
+ "hwid", obj->tunnel.erspan.v2.hwid);
+ break;
+ default:
+ BUG("Unknown tunnel erspan version %d", obj->tunnel.erspan.version);
+ }
+
+ return tunnel;
+}
+
+static json_t *obj_print_json(struct output_ctx *octx, const struct obj *obj)
{
const char *rate_unit = NULL, *burst_unit = NULL;
const char *type = obj_type_name(obj->type);
json_decref(tmp);
break;
case NFT_OBJECT_TUNNEL:
- /* TODO */
+ tmp = json_pack("{s:i, s:o, s:o, s:i, s:i, s:i, s:i}",
+ "id", obj->tunnel.id,
+ obj->tunnel.src->dtype->type == TYPE_IPADDR ? "src-ipv4" : "src-ipv6",
+ expr_print_json(obj->tunnel.src, octx),
+ obj->tunnel.dst->dtype->type == TYPE_IPADDR ? "dst-ipv4" : "dst-ipv6",
+ expr_print_json(obj->tunnel.dst, octx),
+ "sport", obj->tunnel.sport,
+ "dport", obj->tunnel.dport,
+ "tos", obj->tunnel.tos,
+ "ttl", obj->tunnel.ttl);
+
+ switch (obj->tunnel.type) {
+ case TUNNEL_UNSPEC:
+ break;
+ case TUNNEL_ERSPAN:
+ json_object_set_new(tmp, "type", json_string("erspan"));
+ json_object_set_new(tmp, "tunnel",
+ tunnel_erspan_print_json(obj));
+ break;
+ case TUNNEL_VXLAN:
+ json_object_set_new(tmp, "type", json_string("vxlan"));
+ json_object_set_new(tmp, "tunnel",
+ json_pack("{s:i}",
+ "gbp",
+ obj->tunnel.vxlan.gbp));
+ break;
+ case TUNNEL_GENEVE:
+ struct tunnel_geneve *geneve;
+ json_t *opts = json_array();
+
+ list_for_each_entry(geneve, &obj->tunnel.geneve_opts, list) {
+ char data_str[256];
+ json_t *opt;
+ int offset;
+
+ data_str[0] = '0';
+ data_str[1] = 'x';
+ offset = 2;
+ for (uint32_t i = 0; i < geneve->data_len; i++)
+ offset += snprintf(data_str + offset,
+ 3, "%x", geneve->data[i]);
+
+ opt = json_pack("{s:i, s:i, s:s}",
+ "class", geneve->geneve_class,
+ "opt-type", geneve->type,
+ "data", data_str);
+ json_array_append_new(opts, opt);
+ }
+
+ json_object_set_new(tmp, "type", json_string("geneve"));
+ json_object_set_new(tmp, "tunnel", opts);
+ break;
+ }
+ json_object_update(root, tmp);
+ json_decref(tmp);
break;
}
return nft_json_pack("{s:o}", "ipsec", root);
}
+json_t *tunnel_expr_json(const struct expr *expr, struct output_ctx *octx)
+{
+ return json_pack("{s:{s:s}}", "tunnel",
+ "key", tunnel_templates[expr->tunnel.key].token);
+}
+
json_t *integer_type_json(const struct expr *expr, struct output_ctx *octx)
{
char buf[1024] = "0x";
json_array_append_new(root, tmp);
}
list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
- tmp = obj_print_json(obj);
+ tmp = obj_print_json(&ctx->nft->output, obj);
json_array_append_new(root, tmp);
}
list_for_each_entry(set, &table->set_cache.list, cache.list) {
strcmp(cmd->handle.obj.name, obj->handle.obj.name)))
continue;
- json_array_append_new(root, obj_print_json(obj));
+ json_array_append_new(root, obj_print_json(&ctx->nft->output, obj));
}
}
void monitor_print_obj_json(struct netlink_mon_handler *monh,
const char *cmd, struct obj *o)
{
- monitor_print_json(monh, cmd, obj_print_json(o));
+ struct output_ctx *octx = &monh->ctx->nft->output;
+
+ monitor_print_json(monh, cmd, obj_print_json(octx, o));
}
void monitor_print_flowtable_json(struct netlink_mon_handler *monh,
return meta_expr_alloc(int_loc, key);
}
+static struct expr *json_parse_tunnel_expr(struct json_ctx *ctx,
+ const char *type, json_t *root)
+{
+ struct error_record *erec;
+ unsigned int key;
+ const char *name;
+
+ if (json_unpack_err(ctx, root, "{s:s}", "key", &name))
+ return NULL;
+ erec = tunnel_key_parse(int_loc, name, &key);
+ if (erec) {
+ erec_queue(erec, ctx->msgs);
+ return NULL;
+ }
+ return tunnel_expr_alloc(int_loc, key);
+}
+
static struct expr *json_parse_osf_expr(struct json_ctx *ctx,
const char *type, json_t *root)
{
{ "rt", json_parse_rt_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "ct", json_parse_ct_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "numgen", json_parse_numgen_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
+ { "tunnel", json_parse_tunnel_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SES | CTX_F_MAP },
/* below two are hash expr */
{ "jhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "symhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
return stmt;
}
+static struct stmt *json_parse_tunnel_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct stmt *stmt;
+
+ stmt = objref_stmt_alloc(int_loc);
+ stmt->objref.type = NFT_OBJECT_TUNNEL;
+ stmt->objref.expr = json_parse_stmt_expr(ctx, value);
+ if (!stmt->objref.expr) {
+ json_error(ctx, "Invalid tunnel reference.");
+ stmt_free(stmt);
+ return NULL;
+ }
+
+ return stmt;
+}
+
static unsigned int json_parse_nat_flag(const char *flag)
{
const struct {
{ "synproxy", json_parse_synproxy_stmt },
{ "reset", json_parse_optstrip_stmt },
{ "secmark", json_parse_secmark_stmt },
+ { "tunnel", json_parse_tunnel_stmt },
};
const char *type;
unsigned int i;
return 0;
}
+static int json_parse_tunnel_erspan(struct json_ctx *ctx,
+ json_t *root, struct obj *obj)
+{
+ const char *dir;
+ json_t *tmp;
+ int i;
+
+ if (json_unpack_err(ctx, root, "{s:o}", "tunnel", &tmp))
+ return 1;
+
+ if (json_unpack_err(ctx, tmp, "{s:i}", "version", &obj->tunnel.erspan.version))
+ return 1;
+
+ switch (obj->tunnel.erspan.version) {
+ case 1:
+ if (json_unpack_err(ctx, tmp, "{s:i}",
+ "index", &obj->tunnel.erspan.v1.index))
+ return 1;
+ break;
+ case 2:
+ if (json_unpack_err(ctx, tmp, "{s:s, s:i}",
+ "dir", &dir,
+ "hwid", &i))
+ return 1;
+ obj->tunnel.erspan.v2.hwid = i;
+
+ if (!strcmp(dir, "ingress")) {
+ obj->tunnel.erspan.v2.direction = 0;
+ } else if (!strcmp(dir, "egress")) {
+ obj->tunnel.erspan.v2.direction = 1;
+ } else {
+ json_error(ctx, "Invalid direction '%s'.", dir);
+ return 1;
+ }
+ break;
+ default:
+ json_error(ctx, "Invalid erspan version %u" , obj->tunnel.erspan.version);
+ return 1;
+ }
+
+ return 0;
+}
+
+static enum tunnel_type json_parse_tunnel_type(struct json_ctx *ctx,
+ const char *type)
+{
+ const struct {
+ const char *type;
+ int val;
+ } type_tbl[] = {
+ { "erspan", TUNNEL_ERSPAN },
+ { "vxlan", TUNNEL_VXLAN },
+ { "geneve", TUNNEL_GENEVE },
+ };
+ unsigned int i;
+
+ if (!type)
+ return TUNNEL_UNSPEC;
+
+ for (i = 0; i < array_size(type_tbl); i++) {
+ if (!strcmp(type, type_tbl[i].type))
+ return type_tbl[i].val;
+ }
+
+ return TUNNEL_UNSPEC;
+}
+
+static int json_parse_tunnel_src_and_dst(struct json_ctx *ctx,
+ json_t *root,
+ struct obj *obj)
+{
+ bool is_ipv4 = false, src_set = false, dst_set = false;
+ struct expr *expr;
+ json_t *tmp;
+
+ if (!json_unpack(root, "{s:o}", "src-ipv4", &tmp)) {
+ is_ipv4 = true;
+ src_set = true;
+ expr = json_parse_expr(ctx, tmp);
+ if (!expr)
+ return -1;
+ datatype_set(expr, &ipaddr_type);
+ obj->tunnel.src = expr;
+ }
+
+ if (!json_unpack(root, "{s:o}", "src-ipv6", &tmp)) {
+ if (is_ipv4 || src_set)
+ return -1;
+ src_set = true;
+ expr = json_parse_expr(ctx, tmp);
+ if (!expr)
+ return -1;
+ datatype_set(expr, &ip6addr_type);
+ obj->tunnel.src = expr;
+ }
+
+ if (!json_unpack(root, "{s:o}", "dst-ipv4", &tmp)) {
+ dst_set = true;
+ if (!is_ipv4)
+ return -1;
+ expr = json_parse_expr(ctx, tmp);
+ if (!expr)
+ return -1;
+ datatype_set(expr, &ipaddr_type);
+ obj->tunnel.dst = expr;
+ }
+
+ if (!json_unpack(root, "{s:o}", "dst-ipv6", &tmp)) {
+ if (is_ipv4 || dst_set)
+ return -1;
+ dst_set = true;
+ expr = json_parse_expr(ctx, tmp);
+ if (!expr)
+ return -1;
+ datatype_set(expr, &ip6addr_type);
+ obj->tunnel.dst = expr;
+ }
+
+ if (!dst_set || !src_set)
+ return -1;
+
+ return 0;
+}
+
static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
json_t *root, enum cmd_ops op,
enum cmd_obj cmd_obj)
{
- const char *family, *tmp, *rate_unit = "packets", *burst_unit = "bytes";
+ const char *family, *tmp = NULL, *rate_unit = "packets", *burst_unit = "bytes";
uint32_t l3proto = NFPROTO_UNSPEC;
int inv = 0, flags = 0, i, j;
struct handle h = { 0 };
+ json_t *tmp_json;
struct obj *obj;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
obj->synproxy.flags |= flags;
break;
- case CMD_OBJ_TUNNEL:
- /* TODO */
+ case NFT_OBJECT_TUNNEL:
+ cmd_obj = CMD_OBJ_TUNNEL;
+ obj->type = NFT_OBJECT_TUNNEL;
+
+ if (json_parse_tunnel_src_and_dst(ctx, root, obj))
+ goto err_free_obj;
+
+ json_unpack(root, "{s:i}", "id", &obj->tunnel.id);
+ json_unpack(root, "{s:i}", "sport", &i);
+ obj->tunnel.sport = i;
+ json_unpack(root, "{s:i}", "dport", &i);
+ obj->tunnel.sport = i;
+ json_unpack(root, "{s:i}", "ttl", &i);
+ obj->tunnel.ttl = i;
+ json_unpack(root, "{s:i}", "tos", &i);
+ obj->tunnel.tos = i;
+ json_unpack(root, "{s:s}", "type", &tmp);
+
+ obj->tunnel.type = json_parse_tunnel_type(ctx, tmp);
+ switch (obj->tunnel.type) {
+ case TUNNEL_UNSPEC:
+ break;
+ case TUNNEL_ERSPAN:
+ if (json_parse_tunnel_erspan(ctx, root, obj))
+ goto err_free_obj;
+ break;
+ case TUNNEL_VXLAN:
+ if (json_unpack_err(ctx, root,
+ "{s:o}", "tunnel", &tmp_json))
+ goto err_free_obj;
+
+ json_unpack(tmp_json, "{s:i}",
+ "gbp", &obj->tunnel.vxlan.gbp);
+ break;
+ case TUNNEL_GENEVE:
+ json_t *value;
+ size_t index;
+
+ if (json_unpack_err(ctx, root,
+ "{s:o}", "tunnel", &tmp_json))
+ goto err_free_obj;
+
+ json_array_foreach(tmp_json, index, value) {
+ struct tunnel_geneve *geneve = xmalloc(sizeof(struct tunnel_geneve));
+ if (!geneve)
+ memory_allocation_error();
+
+ if (json_unpack_err(ctx, value, "{s:i, s:i, s:s}",
+ "class", &i,
+ "opt-type", &j,
+ "data", &tmp)) {
+ free(geneve);
+ goto err_free_obj;
+ }
+ geneve->geneve_class = i;
+ geneve->type = j;
+
+ if (tunnel_geneve_data_str2array(tmp,
+ geneve->data,
+ &geneve->data_len)) {
+ free(geneve);
+ goto err_free_obj;
+ }
+
+ if (index == 0)
+ init_list_head(&obj->tunnel.geneve_opts);
+
+ list_add_tail(&geneve->list, &obj->tunnel.geneve_opts);
+ }
+ break;
+ }
break;
default:
BUG("Invalid CMD '%d'", cmd_obj);