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/evaluate.c |
Initial commitv0.01-alpha1
Diffstat (limited to 'src/evaluate.c')
-rw-r--r-- | src/evaluate.c | 1031 |
1 files changed, 1031 insertions, 0 deletions
diff --git a/src/evaluate.c b/src/evaluate.c new file mode 100644 index 00000000..0deff9ad --- /dev/null +++ b/src/evaluate.c @@ -0,0 +1,1031 @@ +/* + * 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 <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <arpa/inet.h> +#include <linux/netfilter/nf_tables.h> + +#include <expression.h> +#include <statement.h> +#include <rule.h> +#include <erec.h> +#include <gmputil.h> +#include <utils.h> + +#define TRACE 0 + +static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr); +static int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt); + +static const char *byteorder_names[] = { + [BYTEORDER_INVALID] = "invalid", + [BYTEORDER_HOST_ENDIAN] = "host endian", + [BYTEORDER_BIG_ENDIAN] = "big endian", +}; + + +static int __fmtstring(4, 5) stmt_binary_error(struct eval_ctx *ctx, + const struct stmt *s1, + const struct stmt *s2, + const char *fmt, ...) +{ + struct error_record *erec; + va_list ap; + + va_start(ap, fmt); + erec = erec_vcreate(EREC_ERROR, &s1->location, fmt, ap); + if (s2 != NULL) + erec_add_location(erec, &s2->location); + va_end(ap); + erec_queue(erec, ctx->msgs); + return -1; +} + +static enum ops byteorder_conversion_op(struct expr *expr, + enum byteorder byteorder) +{ + switch (expr->byteorder) { + case BYTEORDER_HOST_ENDIAN: + if (byteorder == BYTEORDER_BIG_ENDIAN) + return OP_HTON; + break; + case BYTEORDER_BIG_ENDIAN: + if (byteorder == BYTEORDER_HOST_ENDIAN) + return OP_NTOH; + break; + default: + break; + } + BUG(); +} + +static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr, + enum byteorder byteorder) +{ + enum ops op; + + assert(!expr_is_constant(*expr) || expr_is_singleton(*expr)); + + if ((*expr)->byteorder == byteorder) + return 0; + if (expr_basetype(*expr)->type != TYPE_INTEGER) + return expr_error(ctx, *expr, + "Byteorder mismatch: expected %s, got %s", + byteorder_names[byteorder], + byteorder_names[(*expr)->byteorder]); + + if (expr_is_constant(*expr)) + (*expr)->byteorder = byteorder; + else { + op = byteorder_conversion_op(*expr, byteorder); + *expr = unary_expr_alloc(&(*expr)->location, op, *expr); + if (expr_evaluate(ctx, expr) < 0) + return -1; + } + return 0; +} + +/* + * Symbol expression: parse symbol and evaluate resulting expression. + */ +static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr) +{ + struct error_record *erec; + struct expr *new; + + (*expr)->sym_type = ctx->ectx.dtype; + erec = symbol_parse(*expr, &new); + if (erec != NULL) { + erec_queue(erec, ctx->msgs); + return -1; + } + + expr_free(*expr); + *expr = new; + + return expr_evaluate(ctx, expr); +} + +static int expr_evaluate_value(struct eval_ctx *ctx, struct expr **expr) +{ + mpz_t mask; + + switch (expr_basetype(*expr)->type) { + case TYPE_INTEGER: + mpz_init_bitmask(mask, ctx->ectx.len); + if (mpz_cmp((*expr)->value, mask) > 0) { + expr_error(ctx, *expr, + "Value %Zu exceeds valid range 0-%Zu", + (*expr)->value, mask); + mpz_clear(mask); + return -1; + } + (*expr)->len = ctx->ectx.len; + mpz_clear(mask); + break; + case TYPE_STRING: + if (ctx->ectx.len > 0) { + if ((*expr)->len > ctx->ectx.len) + return expr_error(ctx, *expr, + "String exceeds maximum length of %u", + ctx->ectx.len / BITS_PER_BYTE); + (*expr)->len = ctx->ectx.len; + } + break; + default: + BUG(); + } + return 0; +} + +/* + * Primary expressions determine the datatype context. + */ +static int expr_evaluate_primary(struct eval_ctx *ctx, struct expr **expr) +{ + ctx->ectx.dtype = (*expr)->dtype; + ctx->ectx.len = (*expr)->len; + (*expr)->flags |= EXPR_F_PRIMARY; + return 0; +} + +/* + * Payload expression: check whether dependencies are fulfilled, otherwise + * generate the necessary relational expression and prepend it to the current + * statement. + */ +static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *payload = *expr; + enum payload_bases base = payload->payload.base; + struct stmt *nstmt; + struct expr *nexpr; + + if (ctx->pctx.protocol[base].desc == NULL) { + if (payload_gen_dependency(ctx, payload, &nexpr) < 0) + return -1; + nstmt = expr_stmt_alloc(&nexpr->location, nexpr); + if (stmt_evaluate(ctx, nstmt) < 0) + return -1; + list_add_tail(&nstmt->list, &ctx->stmt->list); + } else if (ctx->pctx.protocol[base].desc != payload->payload.desc) + return expr_error(ctx, payload, + "conflicting protocols specified: %s vs. %s", + ctx->pctx.protocol[base].desc->name, + payload->payload.desc->name); + + return expr_evaluate_primary(ctx, expr); +} + +/* + * Prefix expression: the argument must be a constant value of integer base + * type; the prefix length must be less than or equal to the type width. + */ +static int expr_evaluate_prefix(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *prefix = *expr, *base, *and, *mask; + + if (expr_evaluate(ctx, &prefix->expr) < 0) + return -1; + base = prefix->expr; + + if (!expr_is_constant(base)) + return expr_error(ctx, prefix, + "Prefix expression is undefined for " + "non-constant expressions"); + + if (expr_basetype(base)->type != TYPE_INTEGER) + return expr_error(ctx, prefix, + "Prefix expression is undefined for " + "%s types", base->dtype->name); + + if (prefix->prefix_len > base->len) + return expr_error(ctx, prefix, + "Prefix length %u is invalid for type " + "of %u bits width", + prefix->prefix_len, base->len); + + /* Clear the uncovered bits of the base value */ + mask = constant_expr_alloc(&prefix->location, &integer_type, + BYTEORDER_HOST_ENDIAN, base->len, NULL); + mpz_prefixmask(mask->value, base->len, prefix->prefix_len); + and = binop_expr_alloc(&prefix->location, OP_AND, base, mask); + + prefix->expr = and; + if (expr_evaluate(ctx, &prefix->expr) < 0) + return -1; + base = prefix->expr; + assert(expr_is_constant(base)); + + prefix->dtype = base->dtype; + prefix->byteorder = base->byteorder; + prefix->len = base->len; + prefix->flags |= EXPR_F_CONSTANT; + return 0; +} + +/* + * Range expression: both sides must be constants of integer base type. + */ +static int expr_evaluate_range_expr(struct eval_ctx *ctx, + const struct expr *range, + struct expr **expr) +{ + if (expr_evaluate(ctx, expr) < 0) + return -1; + + if (expr_basetype(*expr)->type != TYPE_INTEGER) + return expr_binary_error(ctx, *expr, range, + "Range expression is undefined for " + "%s types", (*expr)->dtype->name); + if (!expr_is_constant(*expr)) + return expr_binary_error(ctx, *expr, range, + "Range is not constant"); + return 0; +} + +static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *range = *expr, *left, *right; + + if (expr_evaluate_range_expr(ctx, range, &range->left) < 0) + return -1; + left = range->left; + + if (expr_evaluate_range_expr(ctx, range, &range->right) < 0) + return -1; + right = range->right; + + if (mpz_cmp(left->value, right->value) >= 0) + return expr_error(ctx, range, "Range has zero or negative size"); + + range->dtype = left->dtype; + range->flags |= EXPR_F_CONSTANT; + return 0; +} + +/* + * Unary expressions: unary expressions are only generated internally for + * byteorder conversion of non-constant numerical expressions. + */ +static int expr_evaluate_unary(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *unary = *expr, *arg; + enum byteorder byteorder; + + if (expr_evaluate(ctx, &unary->arg) < 0) + return -1; + arg = unary->arg; + + assert(!expr_is_constant(arg)); + assert(expr_basetype(arg)->type == TYPE_INTEGER); + assert(arg->ops->type != EXPR_UNARY); + + switch (unary->op) { + case OP_HTON: + assert(arg->byteorder == BYTEORDER_HOST_ENDIAN); + byteorder = BYTEORDER_BIG_ENDIAN; + break; + case OP_NTOH: + assert(arg->byteorder == BYTEORDER_BIG_ENDIAN); + byteorder = BYTEORDER_HOST_ENDIAN; + break; + default: + BUG(); + } + + unary->dtype = arg->dtype; + unary->byteorder = byteorder; + unary->len = arg->len; + return 0; +} + +/* + * Binops + */ +static int constant_binop_simplify(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *op = *expr, *left = (*expr)->left, *right = (*expr)->right; + struct expr *new; + mpz_t val, mask; + + assert(left->ops->type == EXPR_VALUE); + assert(right->ops->type == EXPR_VALUE); + assert(left->byteorder == right->byteorder); + + mpz_init2(val, op->len); + mpz_init_bitmask(mask, op->len); + + switch (op->op) { + case OP_AND: + mpz_and(val, left->value, right->value); + mpz_and(val, val, mask); + break; + case OP_XOR: + mpz_xor(val, left->value, right->value); + mpz_and(val, val, mask); + break; + case OP_OR: + mpz_ior(val, left->value, right->value); + mpz_and(val, val, mask); + break; + case OP_LSHIFT: + assert(left->byteorder == BYTEORDER_HOST_ENDIAN); + mpz_lshift_ui(val, mpz_get_uint32(right->value)); + mpz_and(val, val, mask); + break; + case OP_RSHIFT: + assert(left->byteorder == BYTEORDER_HOST_ENDIAN); + mpz_set(val, left->value); + mpz_and(val, val, mask); + mpz_rshift_ui(val, mpz_get_uint32(right->value)); + break; + default: + BUG(); + } + + new = constant_expr_alloc(&op->location, op->dtype, op->byteorder, + op->len, NULL); + mpz_set(new->value, val); + + expr_free(*expr); + *expr = new; + + mpz_clear(mask); + mpz_clear(val); + + return expr_evaluate(ctx, expr); +} + +static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *op = *expr, *left = op->left, *right = op->right; + + if (mpz_get_uint32(right->value) >= left->len) + return expr_binary_error(ctx, right, left, + "%s shift of %u bits is undefined " + "for type of %u bits width", + op->op == OP_LSHIFT ? "Left" : "Right", + mpz_get_uint32(right->value), + left->len); + + /* Both sides need to be in host byte order */ + if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0) + return -1; + left = op->left; + if (byteorder_conversion(ctx, &op->right, BYTEORDER_HOST_ENDIAN) < 0) + return -1; + + op->dtype = &integer_type; + op->byteorder = BYTEORDER_HOST_ENDIAN; + op->len = left->len; + + if (expr_is_constant(left)) + return constant_binop_simplify(ctx, expr); + return 0; +} + +static int expr_evaluate_bitwise(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *op = *expr, *left = op->left, *right = op->right; + + if (byteorder_conversion(ctx, &op->right, left->byteorder) < 0) + return -1; + right = op->right; + + op->dtype = left->dtype; + op->byteorder = left->byteorder; + op->len = left->len; + + if (expr_is_constant(left)) + return constant_binop_simplify(ctx, expr); + return 0; +} + +/* + * Binop expression: both sides must be of integer base type. The left + * hand side may be either constant or non-constant; in case its constant + * it must be a singleton. The ride hand side must always be a constant + * singleton. + */ +static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *op = *expr, *left, *right; + const char *sym = expr_op_symbols[op->op]; + + if (expr_evaluate(ctx, &op->left) < 0) + return -1; + left = op->left; + + if (op->op == OP_LSHIFT || op->op == OP_RSHIFT) + expr_set_context(&ctx->ectx, &integer_type, ctx->ectx.len); + if (expr_evaluate(ctx, &op->right) < 0) + return -1; + right = op->right; + + if (expr_basetype(left)->type != TYPE_INTEGER) + return expr_binary_error(ctx, left, op, + "Binary operation (%s) is undefined " + "for %s types", + sym, left->dtype->name); + + if (expr_is_constant(left) && !expr_is_singleton(left)) + return expr_binary_error(ctx, left, op, + "Binary operation (%s) is undefined " + "for %s expressions", + sym, left->ops->name); + + if (!expr_is_constant(right)) + return expr_binary_error(ctx, right, op, + "Right hand side of binary operation " + "(%s) must be constant", sym); + + if (!expr_is_singleton(right)) + return expr_binary_error(ctx, left, op, + "Binary operation (%s) is undefined " + "for %s expressions", + sym, right->ops->name); + + /* The grammar guarantees this */ + assert(expr_basetype(left) == expr_basetype(right)); + + switch (op->op) { + case OP_LSHIFT: + case OP_RSHIFT: + return expr_evaluate_shift(ctx, expr); + case OP_AND: + case OP_XOR: + case OP_OR: + return expr_evaluate_bitwise(ctx, expr); + default: + BUG(); + } +} + +static int list_member_evaluate(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *next = list_entry((*expr)->list.next, struct expr, list); + int err; + + assert(*expr != next); + list_del(&(*expr)->list); + err = expr_evaluate(ctx, expr); + list_add_tail(&(*expr)->list, &next->list); + return err; +} + +static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr) +{ + unsigned int flags = EXPR_F_CONSTANT; + struct expr *i, *next; + + list_for_each_entry_safe(i, next, &(*expr)->expressions, list) { + if (list_member_evaluate(ctx, &i) < 0) + return -1; + flags &= i->flags; + } + + (*expr)->flags |= flags; + return 0; +} + +static int expr_evaluate_list(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *list = *expr, *new, *i, *next; + mpz_t val; + + mpz_init_set_ui(val, 0); + list_for_each_entry_safe(i, next, &list->expressions, list) { + if (list_member_evaluate(ctx, &i) < 0) + return -1; + if (i->ops->type != EXPR_VALUE) + return expr_error(ctx, i, + "List member must be a constant " + "value"); + if (i->dtype->basetype->type != TYPE_BITMASK) + return expr_error(ctx, i, + "Basetype of type %s is not bitmask", + i->dtype->name); + mpz_ior(val, val, i->value); + } + + new = constant_expr_alloc(&list->location, ctx->ectx.dtype, + BYTEORDER_HOST_ENDIAN, ctx->ectx.len, NULL); + mpz_set(new->value, val); + mpz_clear(val); + + expr_free(*expr); + *expr = new; + return 0; +} + +static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *set = *expr, *i, *next; + + list_for_each_entry_safe(i, next, &set->expressions, list) { + if (list_member_evaluate(ctx, &i) < 0) + return -1; + if (!expr_is_constant(i)) + return expr_error(ctx, i, "Set member is not constant"); + if (!expr_is_singleton(i)) + set->flags |= SET_F_INTERVAL; + } + + set->dtype = ctx->ectx.dtype; + set->len = ctx->ectx.len; + set->flags |= EXPR_F_CONSTANT; + return 0; +} + +static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr_ctx ectx = ctx->ectx; + struct expr *map = *expr, *i; + + if (expr_evaluate(ctx, &map->expr) < 0) + return -1; + if (expr_is_constant(map->expr)) + return expr_error(ctx, map->expr, + "Map expression can not be constant"); + + /* FIXME: segtree needs to know the dimension of the *key*. + * The len should actually be the value of the mapping. */ + map->mappings->dtype = ctx->ectx.dtype; + map->mappings->len = ctx->ectx.len; + + list_for_each_entry(i, &map->mappings->expressions, list) { + expr_set_context(&ctx->ectx, map->expr->dtype, map->expr->len); + if (expr_evaluate(ctx, &i->left) < 0) + return -1; + if (!expr_is_constant(i->left)) + return expr_error(ctx, i->left, + "Key must be a constant"); + if (!expr_is_singleton(i->left)) + map->mappings->flags |= SET_F_INTERVAL; + + expr_set_context(&ctx->ectx, ectx.dtype, ectx.len); + if (expr_evaluate(ctx, &i->right) < 0) + return -1; + if (!expr_is_constant(i->right)) + return expr_error(ctx, i->right, + "Mapping must be a constant"); + if (!expr_is_singleton(i->right)) + return expr_error(ctx, i->right, + "Mapping must be a singleton"); + } + + map->dtype = ctx->ectx.dtype; + map->flags |= EXPR_F_CONSTANT; + + /* Data for range lookups needs to be in big endian order */ + if (map->mappings->flags & SET_F_INTERVAL && + byteorder_conversion(ctx, &map->expr, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + return 0; +} + +/* + * Transfer the invertible binops to the constant side of an equality + * expression. A left shift is only invertible if the low n bits are + * zero. + */ +static int binop_can_transfer(struct eval_ctx *ctx, + struct expr *left, struct expr *right) +{ + switch (left->op) { + case OP_LSHIFT: + if (mpz_scan1(right->value, 0) < mpz_get_uint32(left->right->value)) + return expr_binary_error(ctx, right, left, + "Comparison is always false"); + return 1; + case OP_XOR: + return 1; + default: + return 0; + } +} + +static int binop_transfer_one(struct eval_ctx *ctx, + const struct expr *left, struct expr **right) +{ + expr_get(*right); + + switch (left->op) { + case OP_LSHIFT: + (*right) = binop_expr_alloc(&(*right)->location, OP_RSHIFT, + *right, expr_get(left->right)); + break; + case OP_XOR: + (*right) = binop_expr_alloc(&(*right)->location, OP_XOR, + *right, expr_get(left->right)); + break; + default: + BUG(); + } + + return expr_evaluate(ctx, right); +} + +static int binop_transfer(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *left = (*expr)->left, *i, *next; + int err; + + if (left->ops->type != EXPR_BINOP) + return 0; + + switch ((*expr)->right->ops->type) { + case EXPR_VALUE: + err = binop_can_transfer(ctx, left, (*expr)->right); + if (err <= 0) + return err; + if (binop_transfer_one(ctx, left, &(*expr)->right) < 0) + return -1; + break; + case EXPR_SET: + list_for_each_entry(i, &(*expr)->right->expressions, list) { + err = binop_can_transfer(ctx, left, i); + if (err <= 0) + return err; + } + list_for_each_entry_safe(i, next, &(*expr)->right->expressions, + list) { + list_del(&i->list); + if (binop_transfer_one(ctx, left, &i) < 0) + return -1; + list_add_tail(&i->list, &next->list); + } + break; + default: + return 0; + } + + left = expr_get((*expr)->left->left); + left->dtype = (*expr)->left->dtype; + expr_free((*expr)->left); + (*expr)->left = left; + return 0; +} + +static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *rel = *expr, *left, *right; + + if (expr_evaluate(ctx, &rel->left) < 0) + return -1; + left = rel->left; + + if (expr_evaluate(ctx, &rel->right) < 0) + return -1; + right = rel->right; + + if (!expr_is_constant(right)) + return expr_binary_error(ctx, right, rel, + "Right hand side of relational " + "expression (%s) must be constant", + expr_op_symbols[rel->op]); + if (expr_is_constant(left)) + return expr_binary_error(ctx, left, right, + "Relational expression (%s) has " + "constant value", + expr_op_symbols[rel->op]); + + switch (rel->op) { + case OP_LOOKUP: + /* Data for range lookups needs to be in big endian order */ + if (right->flags & SET_F_INTERVAL && + byteorder_conversion(ctx, &rel->left, + BYTEORDER_BIG_ENDIAN) < 0) + return -1; + left = rel->left; + break; + case OP_EQ: + /* + * Update payload context for payload and meta iiftype equality + * expressions. + */ + switch (left->ops->type) { + case EXPR_PAYLOAD: + payload_ctx_update(&ctx->pctx, rel); + break; + case EXPR_META: + payload_ctx_update_meta(&ctx->pctx, rel); + break; + default: + break; + } + case OP_NEQ: + case OP_FLAGCMP: + switch (right->ops->type) { + case EXPR_RANGE: + goto range; + case EXPR_PREFIX: + if (byteorder_conversion(ctx, &right->expr, left->byteorder) < 0) + return -1; + break; + case EXPR_VALUE: + if (byteorder_conversion(ctx, &rel->right, left->byteorder) < 0) + return -1; + break; + default: + BUG(); + } + break; + case OP_LT: + case OP_GT: + case OP_LTE: + case OP_GTE: + switch (left->ops->type) { + case EXPR_CONCAT: + return expr_binary_error(ctx, left, rel, + "Relational expression (%s) is undefined " + "for %s expressions", + expr_op_symbols[rel->op], + left->ops->name); + default: + break; + } + + if (!expr_is_singleton(right)) + return expr_binary_error(ctx, right, rel, + "Relational expression (%s) is undefined " + "for %s expressions", + expr_op_symbols[rel->op], + right->ops->name); + + if (byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + if (byteorder_conversion(ctx, &rel->right, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + break; + case OP_RANGE: +range: + switch (left->ops->type) { + case EXPR_CONCAT: + return expr_binary_error(ctx, left, rel, + "Relational expression (%s) is undefined" + "for %s expressions", + expr_op_symbols[rel->op], + left->ops->name); + default: + break; + } + + if (byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + if (byteorder_conversion(ctx, &right->left, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + if (byteorder_conversion(ctx, &right->right, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + break; + default: + BUG(); + } + + if (binop_transfer(ctx, expr) < 0) + return -1; + + return 0; +} + +static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr) +{ +#if TRACE + struct error_record *erec; + erec = erec_create(EREC_INFORMATIONAL, &(*expr)->location, "Evaluate"); + erec_print(stdout, erec); expr_print(*expr); printf("\n\n"); +#endif + + switch ((*expr)->ops->type) { + case EXPR_SYMBOL: + return expr_evaluate_symbol(ctx, expr); + case EXPR_VALUE: + return expr_evaluate_value(ctx, expr); + case EXPR_VERDICT: + case EXPR_EXTHDR: + case EXPR_META: + case EXPR_CT: + return expr_evaluate_primary(ctx, expr); + case EXPR_PAYLOAD: + return expr_evaluate_payload(ctx, expr); + case EXPR_PREFIX: + return expr_evaluate_prefix(ctx, expr); + case EXPR_RANGE: + return expr_evaluate_range(ctx, expr); + case EXPR_UNARY: + return expr_evaluate_unary(ctx, expr); + case EXPR_BINOP: + return expr_evaluate_binop(ctx, expr); + case EXPR_CONCAT: + return expr_evaluate_concat(ctx, expr); + case EXPR_LIST: + return expr_evaluate_list(ctx, expr); + case EXPR_SET: + return expr_evaluate_set(ctx, expr); + case EXPR_MAP: + return expr_evaluate_map(ctx, expr); + case EXPR_RELATIONAL: + return expr_evaluate_relational(ctx, expr); + default: + BUG(); + } +} + +static int stmt_evaluate_expr(struct eval_ctx *ctx, struct stmt *stmt) +{ + memset(&ctx->ectx, 0, sizeof(ctx->ectx)); + return expr_evaluate(ctx, &stmt->expr); +} + +static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt) +{ + if (expr_evaluate(ctx, &stmt->expr) < 0) + return -1; + + switch (stmt->expr->ops->type) { + case EXPR_VERDICT: + if (stmt->expr->verdict != NFT_CONTINUE) + stmt->flags |= STMT_F_TERMINAL; + break; + case EXPR_MAP: + break; + default: + BUG(); + } + return 0; +} + +static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt) +{ + expr_set_context(&ctx->ectx, stmt->meta.tmpl->dtype, + stmt->meta.tmpl->len); + if (expr_evaluate(ctx, &stmt->meta.expr) < 0) + return -1; + return 0; +} + +static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt) +{ + stmt->flags |= STMT_F_TERMINAL; + return 0; +} + +static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt) +{ + int err; + + if (stmt->nat.addr != NULL) { + expr_set_context(&ctx->ectx, &ipaddr_type, + 4 * BITS_PER_BYTE); + err = expr_evaluate(ctx, &stmt->nat.addr); + if (err < 0) + return err; + } + + if (stmt->nat.proto != NULL) { + expr_set_context(&ctx->ectx, &inet_service_type, + 2 * BITS_PER_BYTE); + err = expr_evaluate(ctx, &stmt->nat.proto); + if (err < 0) + return err; + } + + stmt->flags |= STMT_F_TERMINAL; + return 0; +} + +static int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt) +{ +#if TRACE + struct error_record *erec; + erec = erec_create(EREC_INFORMATIONAL, &stmt->location, "Evaluate"); + erec_print(stdout, erec); stmt_print(stmt); printf("\n\n"); +#endif + + switch (stmt->ops->type) { + case STMT_COUNTER: + case STMT_LIMIT: + case STMT_LOG: + return 0; + case STMT_EXPRESSION: + return stmt_evaluate_expr(ctx, stmt); + case STMT_VERDICT: + return stmt_evaluate_verdict(ctx, stmt); + case STMT_META: + return stmt_evaluate_meta(ctx, stmt); + case STMT_REJECT: + return stmt_evaluate_reject(ctx, stmt); + case STMT_NAT: + return stmt_evaluate_nat(ctx, stmt); + default: + BUG(); + } +} + +static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule) +{ + struct stmt *stmt, *tstmt = NULL; + + payload_ctx_init(&ctx->pctx, rule->handle.family); + memset(&ctx->ectx, 0, sizeof(ctx->ectx)); + + list_for_each_entry(stmt, &rule->stmts, list) { + if (tstmt != NULL) + return stmt_binary_error(ctx, stmt, tstmt, + "Statement after terminal " + "statement has no effect"); + + ctx->stmt = stmt; + if (stmt_evaluate(ctx, stmt) < 0) + return -1; + if (stmt->flags & STMT_F_TERMINAL) + tstmt = stmt; + } + return 0; +} + +static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain) +{ + struct rule *rule; + + list_for_each_entry(rule, &chain->rules, list) { + handle_merge(&rule->handle, &chain->handle); + if (rule_evaluate(ctx, rule) < 0) + return -1; + } + return 0; +} + +static int table_evaluate(struct eval_ctx *ctx, struct table *table) +{ + struct chain *chain; + + list_for_each_entry(chain, &table->chains, list) { + handle_merge(&chain->handle, &table->handle); + if (chain_evaluate(ctx, chain) < 0) + return -1; + } + return 0; +} + +static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) +{ + switch (cmd->obj) { + case CMD_OBJ_RULE: + handle_merge(&cmd->rule->handle, &cmd->handle); + return rule_evaluate(ctx, cmd->rule); + case CMD_OBJ_CHAIN: + if (cmd->data == NULL) + return 0; + return chain_evaluate(ctx, cmd->chain); + case CMD_OBJ_TABLE: + if (cmd->data == NULL) + return 0; + return table_evaluate(ctx, cmd->table); + default: + BUG(); + } +} + +static int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd) +{ +#if TRACE + struct error_record *erec; + erec = erec_create(EREC_INFORMATIONAL, &cmd->location, "Evaluate"); + erec_print(stdout, erec); printf("\n\n"); +#endif + + switch (cmd->op) { + case CMD_ADD: + return cmd_evaluate_add(ctx, cmd); + case CMD_DELETE: + case CMD_LIST: + case CMD_FLUSH: + return 0; + default: + BUG(); + }; +} + +int evaluate(struct eval_ctx *ctx, struct list_head *commands) +{ + struct cmd *cmd; + + list_for_each_entry(cmd, commands, list) { + if (cmd_evaluate(ctx, cmd) < 0) + return -1; + } + return 0; +} |