diff options
author | Patrick McHardy <kaber@trash.net> | 2009-03-18 04:55:00 +0100 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2009-03-18 04:55:00 +0100 |
commit | fac10ea799fe9b6158d74f66d6ad46536d38a545 (patch) | |
tree | 8c093bcbb2144aab54c70103e6ed438456ae0d48 /src/netlink_delinearize.c |
Initial commitv0.01-alpha1
Diffstat (limited to 'src/netlink_delinearize.c')
-rw-r--r-- | src/netlink_delinearize.c | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c new file mode 100644 index 00000000..be2271ce --- /dev/null +++ b/src/netlink_delinearize.c @@ -0,0 +1,781 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <linux/netfilter/nf_tables.h> +#include <netlink.h> +#include <rule.h> +#include <statement.h> +#include <expression.h> +#include <gmputil.h> +#include <utils.h> +#include <erec.h> + +struct netlink_parse_ctx { + struct list_head *msgs; + struct rule *rule; + struct expr *registers[NFT_REG_MAX + 1]; +}; + +static void __fmtstring(3, 4) netlink_error(struct netlink_parse_ctx *ctx, + const struct location *loc, + const char *fmt, ...) +{ + struct error_record *erec; + va_list ap; + + va_start(ap, fmt); + erec = erec_vcreate(EREC_ERROR, loc, fmt, ap); + va_end(ap); + erec_queue(erec, ctx->msgs); +} + +static void netlink_set_register(struct netlink_parse_ctx *ctx, + enum nft_registers reg, + struct expr *expr) +{ + if (reg > NFT_REG_MAX) { + netlink_error(ctx, &expr->location, + "Invalid destination register %u", reg); + expr_free(expr); + return; + } + + ctx->registers[reg] = expr; +} + +static struct expr *netlink_get_register(struct netlink_parse_ctx *ctx, + const struct location *loc, + enum nft_registers reg) +{ + struct expr *expr; + + if (reg == NFT_REG_VERDICT || reg > NFT_REG_MAX) { + netlink_error(ctx, loc, "Invalid source register %u", reg); + return NULL; + } + + expr = ctx->registers[reg]; + ctx->registers[reg] = NULL; + return expr; +} + +static struct expr *netlink_alloc_value(const struct location *loc, + const struct nfnl_nft_data *nld) +{ + return constant_expr_alloc(loc, &invalid_type, BYTEORDER_INVALID, + nfnl_nft_data_get_size(nld) * BITS_PER_BYTE, + nfnl_nft_data_get(nld)); +} + +static struct expr *netlink_alloc_verdict(const struct location *loc, + const struct nfnl_nft_data *nld) +{ + unsigned int code; + char *chain; + + code = nfnl_nft_verdict_get_verdict(nld); + switch (code) { + case NFT_JUMP: + case NFT_GOTO: + chain = xstrdup(nfnl_nft_verdict_get_chain(nld)); + break; + default: + chain = NULL; + break; + } + + return verdict_expr_alloc(loc, code, chain); +} + +static struct expr *netlink_alloc_data(const struct location *loc, + const struct nfnl_nft_data *nld, + enum nft_registers dreg) +{ + switch (dreg) { + case NFT_REG_VERDICT: + return netlink_alloc_verdict(loc, nld); + default: + return netlink_alloc_value(loc, nld); + } +} + +static void netlink_parse_immediate(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + const struct nfnl_nft_data *data = nfnl_nft_immediate_get_data(nle); + enum nft_registers dreg = nfnl_nft_immediate_get_dreg(nle); + struct stmt *stmt; + struct expr *expr; + + expr = netlink_alloc_data(loc, data, dreg); + if (dreg == NFT_REG_VERDICT) { + stmt = verdict_stmt_alloc(loc, expr); + list_add_tail(&stmt->list, &ctx->rule->stmts); + } else + netlink_set_register(ctx, dreg, expr); +} + +static enum ops netlink_parse_cmp_op(const struct nfnl_nft_expr *nle) +{ + switch (nfnl_nft_cmp_get_op(nle)) { + case NFT_CMP_EQ: + return OP_EQ; + case NFT_CMP_NEQ: + return OP_NEQ; + case NFT_CMP_LT: + return OP_LT; + case NFT_CMP_LTE: + return OP_LTE; + case NFT_CMP_GT: + return OP_GT; + case NFT_CMP_GTE: + return OP_GTE; + default: + return OP_INVALID; + } +} + +static void netlink_parse_cmp(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + const struct nfnl_nft_data *data = nfnl_nft_cmp_get_data(nle); + struct expr *expr, *left, *right; + struct stmt *stmt; + enum ops op; + + left = netlink_get_register(ctx, loc, nfnl_nft_cmp_get_sreg(nle)); + if (left == NULL) + return netlink_error(ctx, loc, + "Relational expression has no left " + "hand side"); + + op = netlink_parse_cmp_op(nle); + right = netlink_alloc_value(loc, data); + + // FIXME + if (left->len && left->dtype && left->dtype->type != TYPE_STRING && + left->len != right->len) + return netlink_error(ctx, loc, + "Relational expression size mismatch"); + + expr = relational_expr_alloc(loc, op, left, right); + stmt = expr_stmt_alloc(loc, expr); + list_add_tail(&stmt->list, &ctx->rule->stmts); +} + +struct netlink_data_ctx { + const struct location *location; + struct expr *expr; + enum nft_registers dreg; +}; + +static void netlink_data_ctx_init(struct netlink_data_ctx *dctx, + const struct location *loc, + struct expr *expr, enum nft_registers dreg) +{ + dctx->location = loc; + dctx->expr = expr; + dctx->dreg = dreg; +} + +static void netlink_set_parse_data(struct nfnl_nft_data *data, + enum nft_set_elem_flags flags, + void *arg) +{ + struct netlink_data_ctx *dctx = arg; + struct expr *expr; + + assert(dctx->dreg != NFT_REG_VERDICT); + expr = netlink_alloc_value(dctx->location, data); + if (flags & NFT_SE_INTERVAL_END) + expr->flags |= EXPR_F_INTERVAL_END; + compound_expr_add(dctx->expr, expr); +} + +static void netlink_set_parse_mapping(struct nfnl_nft_data *data, + struct nfnl_nft_data *mapping, + enum nft_set_elem_flags flags, + void *arg) +{ + struct netlink_data_ctx *dctx = arg; + struct expr *expr, *left, *right; + + left = netlink_alloc_value(dctx->location, data); + if (mapping != NULL) { + right = netlink_alloc_data(dctx->location, mapping, dctx->dreg); + expr = mapping_expr_alloc(dctx->location, left, right); + } else + expr = left; + + if (flags & NFT_SE_INTERVAL_END) + expr->flags |= EXPR_F_INTERVAL_END; + compound_expr_add(dctx->expr, expr); +} + +extern void interval_map_decompose(struct expr *set); + +static void netlink_parse_set(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct stmt *stmt; + struct expr *expr, *left, *right; + struct netlink_data_ctx dctx; + enum nft_registers dreg; + enum nft_set_flags flags; + + left = netlink_get_register(ctx, loc, nfnl_nft_set_get_sreg(nle)); + if (left == NULL) + return netlink_error(ctx, loc, + "Set expression has no left hand side"); + + right = set_expr_alloc(loc); + + flags = nfnl_nft_set_get_flags(nle); + if (flags & NFT_SET_MAP) { + dreg = nfnl_nft_set_get_dreg(nle); + netlink_data_ctx_init(&dctx, loc, right, dreg); + nfnl_nft_set_foreach_mapping(nle, netlink_set_parse_mapping, + &dctx); + + expr = map_expr_alloc(loc, left, right); + if (dreg != NFT_REG_VERDICT) + return netlink_set_register(ctx, dreg, expr); + } else { + netlink_data_ctx_init(&dctx, loc, right, EXPR_VALUE); + nfnl_nft_set_foreach_elem(nle, netlink_set_parse_data, &dctx); + if (flags & NFT_SET_INTERVAL) { + interval_map_decompose(right); + right->flags |= NFT_SET_INTERVAL; + } + expr = relational_expr_alloc(loc, OP_LOOKUP, left, right); + } + + stmt = expr_stmt_alloc(loc, expr); + list_add_tail(&stmt->list, &ctx->rule->stmts); +} + +static void netlink_parse_bitwise(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct expr *expr, *left, *mask, *xor, *or; + mpz_t m, x, o; + + left = netlink_get_register(ctx, loc, nfnl_nft_bitwise_get_sreg(nle)); + if (left == NULL) + return netlink_error(ctx, loc, + "Bitwise expression has no left " + "hand side"); + + expr = left; + + mask = netlink_alloc_value(loc, nfnl_nft_bitwise_get_mask(nle)); + mpz_init_set(m, mask->value); + + xor = netlink_alloc_value(loc, nfnl_nft_bitwise_get_xor(nle)); + mpz_init_set(x, xor->value); + + mpz_init_set_ui(o, 0); + if (mpz_scan0(m, 0) != mask->len || mpz_cmp_ui(x, 0)) { + /* o = (m & x) ^ x */ + mpz_and(o, m, x); + mpz_xor(o, o, x); + /* x &= m */ + mpz_and(x, x, m); + /* m |= o */ + mpz_ior(m, m, o); + } + + if (mpz_scan0(m, 0) != left->len) { + mpz_set(mask->value, m); + expr = binop_expr_alloc(loc, OP_AND, expr, mask); + expr->len = left->len; + } else + expr_free(mask); + + if (mpz_cmp_ui(x, 0)) { + mpz_set(xor->value, x); + expr = binop_expr_alloc(loc, OP_XOR, expr, xor); + expr->len = left->len; + } else + expr_free(xor); + + if (mpz_cmp_ui(o, 0)) { + or = netlink_alloc_value(loc, nfnl_nft_bitwise_get_xor(nle)); + mpz_set(or->value, o); + expr = binop_expr_alloc(loc, OP_OR, expr, or); + expr->len = left->len; + } + + mpz_clear(m); + mpz_clear(x); + mpz_clear(o); + + netlink_set_register(ctx, nfnl_nft_bitwise_get_dreg(nle), expr); +} + +static void netlink_parse_byteorder(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct expr *expr, *arg; + enum ops op; + + arg = netlink_get_register(ctx, loc, nfnl_nft_byteorder_get_sreg(nle)); + if (arg == NULL) + return netlink_error(ctx, loc, + "Byteorder expression has no left " + "hand side"); + + switch (nfnl_nft_byteorder_get_op(nle)) { + case NFT_BYTEORDER_NTOH: + op = OP_NTOH; + break; + case NFT_BYTEORDER_HTON: + op = OP_HTON; + break; + default: + BUG(); + } + + expr = unary_expr_alloc(loc, op, arg); + expr->len = arg->len; + netlink_set_register(ctx, nfnl_nft_byteorder_get_dreg(nle), expr); +} + +static void netlink_parse_payload(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct expr *expr; + + expr = payload_expr_alloc(loc, NULL, 0); + payload_init_raw(expr, nfnl_nft_payload_get_base(nle) + 1, + nfnl_nft_payload_get_offset(nle) * BITS_PER_BYTE, + nfnl_nft_payload_get_len(nle) * BITS_PER_BYTE); + + netlink_set_register(ctx, nfnl_nft_payload_get_dreg(nle), expr); +} + +static void netlink_parse_exthdr(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct expr *expr; + + expr = exthdr_expr_alloc(loc, NULL, 0); + exthdr_init_raw(expr, nfnl_nft_exthdr_get_type(nle), + nfnl_nft_exthdr_get_offset(nle) * BITS_PER_BYTE, + nfnl_nft_exthdr_get_len(nle) * BITS_PER_BYTE); + + netlink_set_register(ctx, nfnl_nft_exthdr_get_dreg(nle), expr); +} + +static void netlink_parse_meta(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct expr *expr; + + expr = meta_expr_alloc(loc, nfnl_nft_meta_get_key(nle)); + netlink_set_register(ctx, nfnl_nft_meta_get_dreg(nle), expr); +} + +static void netlink_parse_ct(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct expr *expr; + + expr = ct_expr_alloc(loc, nfnl_nft_ct_get_key(nle)); + netlink_set_register(ctx, nfnl_nft_ct_get_dreg(nle), expr); +} + +static void netlink_parse_counter(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct stmt *stmt; + + stmt = counter_stmt_alloc(loc); + stmt->counter.packets = nfnl_nft_counter_get_packets(nle); + stmt->counter.bytes = nfnl_nft_counter_get_bytes(nle); + list_add_tail(&stmt->list, &ctx->rule->stmts); +} + +static void netlink_parse_log(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct stmt *stmt; + const char *prefix; + + stmt = log_stmt_alloc(loc); + prefix = nfnl_nft_log_get_prefix(nle); + if (prefix != NULL) + stmt->log.prefix = xstrdup(prefix); + stmt->log.group = nfnl_nft_log_get_group(nle); + stmt->log.snaplen = nfnl_nft_log_get_snaplen(nle); + stmt->log.qthreshold = nfnl_nft_log_get_qthreshold(nle); + list_add_tail(&stmt->list, &ctx->rule->stmts); +} + +static void netlink_parse_limit(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct stmt *stmt; + + stmt = limit_stmt_alloc(loc); + stmt->limit.rate = nfnl_nft_limit_get_rate(nle); + stmt->limit.depth = nfnl_nft_limit_get_depth(nle); + list_add_tail(&stmt->list, &ctx->rule->stmts); +} + +static void netlink_parse_reject(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *expr) +{ + struct stmt *stmt; + + stmt = reject_stmt_alloc(loc); + list_add_tail(&stmt->list, &ctx->rule->stmts); +} + +static void netlink_parse_nat(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle) +{ + struct stmt *stmt; + struct expr *addr, *proto; + enum nft_registers reg1, reg2; + + stmt = nat_stmt_alloc(loc); + stmt->nat.type = nfnl_nft_nat_get_type(nle); + + reg1 = nfnl_nft_nat_get_sreg_addr_min(nle); + if (reg1) { + addr = netlink_get_register(ctx, loc, reg1); + if (addr == NULL) + return netlink_error(ctx, loc, + "NAT statement has no address " + "expression"); + + expr_set_type(addr, &ipaddr_type, BYTEORDER_BIG_ENDIAN); + stmt->nat.addr = addr; + } + + reg2 = nfnl_nft_nat_get_sreg_addr_max(nle); + if (reg2 && reg2 != reg1) { + addr = netlink_get_register(ctx, loc, reg2); + if (addr == NULL) + return netlink_error(ctx, loc, + "NAT statement has no address " + "expression"); + + expr_set_type(addr, &ipaddr_type, BYTEORDER_BIG_ENDIAN); + if (stmt->nat.addr != NULL) + addr = range_expr_alloc(loc, stmt->nat.addr, addr); + stmt->nat.addr = addr; + } + + reg1 = nfnl_nft_nat_get_sreg_proto_min(nle); + if (reg1) { + proto = netlink_get_register(ctx, loc, reg1); + if (proto == NULL) + return netlink_error(ctx, loc, + "NAT statement has no proto " + "expression"); + + expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN); + stmt->nat.proto = proto; + } + + reg2 = nfnl_nft_nat_get_sreg_proto_max(nle); + if (reg2 && reg2 != reg1) { + proto = netlink_get_register(ctx, loc, reg1); + if (proto == NULL) + return netlink_error(ctx, loc, + "NAT statement has no proto " + "expression"); + + expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN); + stmt->nat.proto = proto; + if (stmt->nat.proto != NULL) + proto = range_expr_alloc(loc, stmt->nat.proto, proto); + stmt->nat.proto = proto; + } + + list_add_tail(&stmt->list, &ctx->rule->stmts); +} + +static const struct { + const char *name; + void (*parse)(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nfnl_nft_expr *nle); +} netlink_parsers[] = { + { .name = "immediate", .parse = netlink_parse_immediate }, + { .name = "cmp", .parse = netlink_parse_cmp }, + { .name = "set", .parse = netlink_parse_set }, + { .name = "bitwise", .parse = netlink_parse_bitwise }, + { .name = "byteorder", .parse = netlink_parse_byteorder }, + { .name = "payload", .parse = netlink_parse_payload }, + { .name = "exthdr", .parse = netlink_parse_exthdr }, + { .name = "meta", .parse = netlink_parse_meta }, + { .name = "ct", .parse = netlink_parse_ct }, + { .name = "counter", .parse = netlink_parse_counter }, + { .name = "log", .parse = netlink_parse_log }, + { .name = "limit", .parse = netlink_parse_limit }, + { .name = "reject", .parse = netlink_parse_reject }, + { .name = "nat", .parse = netlink_parse_nat }, +}; + +static const struct input_descriptor indesc_netlink = { + .name = "netlink", + .type = INDESC_NETLINK, +}; + +static void netlink_parse_expr(struct nl_object *obj, void *arg) +{ + const struct nfnl_nft_expr *nle = (struct nfnl_nft_expr *)obj; + const char *type = nfnl_nft_expr_get_type(nle); + struct netlink_parse_ctx *ctx = arg; + struct location loc; + unsigned int i; + + memset(&loc, 0, sizeof(loc)); + loc.indesc = &indesc_netlink; + loc.nl_obj = obj; + + for (i = 0; i < array_size(netlink_parsers); i++) { + if (strcmp(type, netlink_parsers[i].name)) + continue; + return netlink_parsers[i].parse(ctx, &loc, nle); + } + + netlink_error(ctx, &loc, "unknown expression type '%s'", type); +} + +struct rule_pp_ctx { + struct payload_ctx pctx; +}; + +static void payload_match_postprocess(struct payload_ctx *ctx, + struct stmt *stmt, struct expr *expr) +{ + struct expr *left = expr->left, *right = expr->right, *tmp; + struct list_head list = LIST_HEAD_INIT(list); + struct stmt *nstmt; + struct expr *nexpr; + + switch (expr->op) { + case OP_EQ: + case OP_NEQ: + payload_expr_expand(&list, left, ctx); + list_for_each_entry(left, &list, list) { + tmp = constant_expr_splice(right, left->len); + expr_set_type(tmp, left->dtype, left->byteorder); + nexpr = relational_expr_alloc(&expr->location, expr->op, + left, tmp); + payload_ctx_update(ctx, nexpr); + + nstmt = expr_stmt_alloc(&stmt->location, nexpr); + list_add_tail(&nstmt->list, &stmt->list); + } + list_del(&stmt->list); + stmt_free(stmt); + break; + default: + payload_expr_complete(left, ctx); + expr_set_type(expr->right, expr->left->dtype, + expr->left->byteorder); + break; + } +} + +static void meta_match_postprocess(struct payload_ctx *ctx, + const struct expr *expr) +{ + switch (expr->op) { + case OP_EQ: + payload_ctx_update_meta(ctx, expr); + break; + default: + break; + } +} + +static void expr_postprocess(struct rule_pp_ctx *ctx, + struct stmt *stmt, struct expr **exprp) +{ + struct expr *expr = *exprp, *i; + + //pr_debug("%s len %u\n", expr->ops->name, expr->len); + + switch (expr->ops->type) { + case EXPR_MAP: + expr_postprocess(ctx, stmt, &expr->expr); + list_for_each_entry(i, &expr->mappings->expressions, list) { + if (i->flags & EXPR_F_INTERVAL_END) + continue; + expr_set_type(i->left, expr->expr->dtype, + expr->expr->byteorder); + } + expr_postprocess(ctx, stmt, &expr->mappings); + break; + case EXPR_MAPPING: + expr_postprocess(ctx, stmt, &expr->left); + expr_postprocess(ctx, stmt, &expr->right); + break; + case EXPR_SET: + list_for_each_entry(i, &expr->expressions, list) + expr_postprocess(ctx, stmt, &i); + break; + case EXPR_UNARY: + expr_postprocess(ctx, stmt, &expr->arg); + expr_set_type(expr->arg, expr->arg->dtype, !expr->arg->byteorder); + + *exprp = expr_get(expr->arg); + expr_free(expr); + break; + case EXPR_BINOP: + expr_postprocess(ctx, stmt, &expr->left); + expr_postprocess(ctx, stmt, &expr->right); + expr_set_type(expr->right, expr->left->dtype, + expr->left->byteorder); + expr_set_type(expr, expr->left->dtype, + expr->left->byteorder); + break; + case EXPR_RELATIONAL: + switch (expr->left->ops->type) { + case EXPR_PAYLOAD: + payload_match_postprocess(&ctx->pctx, stmt, expr); + return; + case EXPR_META: + meta_match_postprocess(&ctx->pctx, expr); + break; + default: + expr_postprocess(ctx, stmt, &expr->left); + break; + } + + expr_set_type(expr->right, expr->left->dtype, expr->left->byteorder); + expr_postprocess(ctx, stmt, &expr->right); + + if (expr->left->ops->type == EXPR_BINOP && + expr->left->op == OP_AND && + expr->op == OP_NEQ && + expr->right->dtype->basetype->type == TYPE_BITMASK) { + unsigned int n; + + expr_free(expr->right); + expr->right = list_expr_alloc(&expr->left->left->location); + n = 0; + while ((n = mpz_scan1(expr->left->right->value, n + 1))) { + if (n > expr->left->right->len) + break; + i = constant_expr_alloc(&expr->left->right->location, + expr->left->left->dtype, + expr->left->right->byteorder, + expr->left->right->len, NULL); + mpz_set_ui(i->value, 1); + mpz_lshift_ui(i->value, n); + compound_expr_add(expr->right, i); + } + expr->left = expr->left->left; + expr->op = OP_FLAGCMP; + } + break; + case EXPR_PAYLOAD: + payload_expr_complete(expr, &ctx->pctx); + break; + case EXPR_VALUE: + // FIXME + if (expr->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); + + // Quite a hack :) + if (expr->dtype->type == TYPE_STRING) { + unsigned int len = expr->len; + mpz_t tmp; + mpz_init(tmp); + while (len >= BITS_PER_BYTE) { + mpz_bitmask(tmp, BITS_PER_BYTE); + mpz_lshift_ui(tmp, len - BITS_PER_BYTE); + mpz_and(tmp, tmp, expr->value); + if (mpz_cmp_ui(tmp, 0)) + break; + len -= BITS_PER_BYTE; + } + mpz_clear(tmp); + expr->len = len; + } + break; + case EXPR_EXTHDR: + case EXPR_META: + case EXPR_CT: + case EXPR_VERDICT: + break; + default: + printf("%s\n", expr->ops->name); + BUG(); + } +} + +static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule) +{ + struct rule_pp_ctx rctx; + struct stmt *stmt, *next; + + payload_ctx_init(&rctx.pctx, rule->handle.family); + + list_for_each_entry_safe(stmt, next, &rule->stmts, list) { + switch (stmt->ops->type) { + case STMT_EXPRESSION: + expr_postprocess(&rctx, stmt, &stmt->expr); + break; + case STMT_NAT: + if (stmt->nat.addr != NULL) + expr_postprocess(&rctx, stmt, &stmt->nat.addr); + if (stmt->nat.proto != NULL) + expr_postprocess(&rctx, stmt, &stmt->nat.proto); + break; + default: + break; + } + } +} + +struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx, + const struct nl_object *obj) +{ + const struct nfnl_nft_rule *nlr = (const struct nfnl_nft_rule *)obj; + struct netlink_parse_ctx _ctx, *pctx = &_ctx; + struct handle h; + + memset(&_ctx, 0, sizeof(_ctx)); + _ctx.msgs = ctx->msgs; + + memset(&h, 0, sizeof(h)); + h.family = nfnl_nft_rule_get_family(nlr); + h.table = xstrdup(nfnl_nft_rule_get_table(nlr)); + h.chain = xstrdup(nfnl_nft_rule_get_chain(nlr)); + h.handle = nfnl_nft_rule_get_handle(nlr); + + pctx->rule = rule_alloc(&internal_location, &h); + nfnl_nft_rule_foreach_expr(nlr, netlink_parse_expr, pctx); + + rule_parse_postprocess(pctx, pctx->rule); + return pctx->rule; +} |