summaryrefslogtreecommitdiffstats
path: root/src/expression.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/expression.c')
-rw-r--r--src/expression.c406
1 files changed, 363 insertions, 43 deletions
diff --git a/src/expression.c b/src/expression.c
index cb11cda4..cb2573fe 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -8,16 +8,16 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <limits.h>
#include <expression.h>
#include <statement.h>
#include <datatype.h>
+#include <netlink.h>
#include <rule.h>
#include <gmputil.h>
#include <utils.h>
@@ -28,6 +28,7 @@
extern const struct expr_ops ct_expr_ops;
extern const struct expr_ops fib_expr_ops;
extern const struct expr_ops hash_expr_ops;
+extern const struct expr_ops inner_expr_ops;
extern const struct expr_ops meta_expr_ops;
extern const struct expr_ops numgen_expr_ops;
extern const struct expr_ops osf_expr_ops;
@@ -93,7 +94,7 @@ void expr_free(struct expr *expr)
*/
if (expr->etype != EXPR_INVALID)
expr_destroy(expr);
- xfree(expr);
+ free(expr);
}
void expr_print(const struct expr *expr, struct output_ctx *octx)
@@ -138,6 +139,11 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
} else {
nft_print(octx, "%s expression, datatype %s (%s)",
expr_name(expr), dtype->name, dtype->desc);
+
+ if (dtype == &invalid_type) {
+ nft_print(octx, "\n");
+ return;
+ }
}
if (dtype->basetype != NULL) {
@@ -172,9 +178,20 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
nft_print(octx, "(in hexadecimal):\n");
symbol_table_print(edtype->sym_tbl, edtype,
expr->byteorder, octx);
+ } else if (edtype->describe) {
+ edtype->describe(octx);
}
}
+void expr_to_string(const struct expr *expr, char *string)
+{
+ int len = expr->len / BITS_PER_BYTE;
+
+ assert(expr->dtype == &string_type);
+
+ mpz_export_data(string, expr->value, BYTEORDER_HOST_ENDIAN, len);
+}
+
void expr_set_type(struct expr *expr, const struct datatype *dtype,
enum byteorder byteorder)
{
@@ -243,6 +260,22 @@ static void verdict_expr_destroy(struct expr *expr)
expr_free(expr->chain);
}
+static int verdict_expr_build_udata(struct nftnl_udata_buf *udbuf,
+ const struct expr *expr)
+{
+ return 0;
+}
+
+static struct expr *verdict_expr_parse_udata(const struct nftnl_udata *attr)
+{
+ struct expr *e;
+
+ e = symbol_expr_alloc(&internal_location, SYMBOL_VALUE, NULL, "verdict");
+ e->dtype = &verdict_type;
+ e->len = NFT_REG_SIZE * BITS_PER_BYTE;
+ return e;
+}
+
static const struct expr_ops verdict_expr_ops = {
.type = EXPR_VERDICT,
.name = "verdict",
@@ -251,6 +284,8 @@ static const struct expr_ops verdict_expr_ops = {
.cmp = verdict_expr_cmp,
.clone = verdict_expr_clone,
.destroy = verdict_expr_destroy,
+ .build_udata = verdict_expr_build_udata,
+ .parse_udata = verdict_expr_parse_udata,
};
struct expr *verdict_expr_alloc(const struct location *loc,
@@ -269,8 +304,7 @@ struct expr *verdict_expr_alloc(const struct location *loc,
static void symbol_expr_print(const struct expr *expr, struct output_ctx *octx)
{
- nft_print(octx, "%s%s", expr->scope != NULL ? "$" : "",
- expr->identifier);
+ nft_print(octx, "%s", expr->identifier);
}
static void symbol_expr_clone(struct expr *new, const struct expr *expr)
@@ -282,7 +316,7 @@ static void symbol_expr_clone(struct expr *new, const struct expr *expr)
static void symbol_expr_destroy(struct expr *expr)
{
- xfree(expr->identifier);
+ free_const(expr->identifier);
}
static const struct expr_ops symbol_expr_ops = {
@@ -551,6 +585,7 @@ const char *expr_op_symbols[] = {
[OP_GT] = ">",
[OP_LTE] = "<=",
[OP_GTE] = ">=",
+ [OP_NEG] = "!",
};
static void unary_expr_print(const struct expr *expr, struct output_ctx *octx)
@@ -699,16 +734,26 @@ struct expr *relational_expr_alloc(const struct location *loc, enum ops op,
void relational_expr_pctx_update(struct proto_ctx *ctx,
const struct expr *expr)
{
- const struct expr *left = expr->left;
+ const struct expr *left = expr->left, *right = expr->right;
const struct expr_ops *ops;
+ const struct expr *i;
assert(expr->etype == EXPR_RELATIONAL);
assert(expr->op == OP_EQ || expr->op == OP_IMPLICIT);
ops = expr_ops(left);
if (ops->pctx_update &&
- (left->flags & EXPR_F_PROTOCOL))
- ops->pctx_update(ctx, expr);
+ (left->flags & EXPR_F_PROTOCOL)) {
+ if (expr_is_singleton(right))
+ ops->pctx_update(ctx, &expr->location, left, right);
+ else if (right->etype == EXPR_SET) {
+ list_for_each_entry(i, &right->expressions, list) {
+ if (i->etype == EXPR_SET_ELEM &&
+ i->key->etype == EXPR_VALUE)
+ ops->pctx_update(ctx, &expr->location, left, i->key);
+ }
+ }
+ }
}
static void range_expr_print(const struct expr *expr, struct output_ctx *octx)
@@ -829,6 +874,156 @@ static void concat_expr_print(const struct expr *expr, struct output_ctx *octx)
compound_expr_print(expr, " . ", octx);
}
+#define NFTNL_UDATA_SET_KEY_CONCAT_NEST 0
+#define NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX NFT_REG32_SIZE
+
+#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE 0
+#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA 1
+#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_MAX 2
+
+static struct expr *expr_build_udata_recurse(struct expr *e)
+{
+ switch (e->etype) {
+ case EXPR_BINOP:
+ return e->left;
+ default:
+ break;
+ }
+
+ return e;
+}
+
+static int concat_expr_build_udata(struct nftnl_udata_buf *udbuf,
+ const struct expr *concat_expr)
+{
+ struct nftnl_udata *nest;
+ struct expr *expr, *tmp;
+ unsigned int i = 0;
+
+ list_for_each_entry_safe(expr, tmp, &concat_expr->expressions, list) {
+ struct nftnl_udata *nest_expr;
+ int err;
+
+ expr = expr_build_udata_recurse(expr);
+ if (!expr_ops(expr)->build_udata || i >= NFT_REG32_SIZE)
+ return -1;
+
+ nest = nftnl_udata_nest_start(udbuf, NFTNL_UDATA_SET_KEY_CONCAT_NEST + i);
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE, expr->etype);
+ nest_expr = nftnl_udata_nest_start(udbuf, NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA);
+ err = expr_ops(expr)->build_udata(udbuf, expr);
+ if (err < 0)
+ return err;
+ nftnl_udata_nest_end(udbuf, nest_expr);
+ nftnl_udata_nest_end(udbuf, nest);
+ i++;
+ }
+
+ return 0;
+}
+
+static int concat_parse_udata_nest(const struct nftnl_udata *attr, void *data)
+{
+ const struct nftnl_udata **ud = data;
+ uint8_t type = nftnl_udata_type(attr);
+ uint8_t len = nftnl_udata_len(attr);
+
+ if (type >= NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX)
+ return -1;
+
+ if (len <= sizeof(uint32_t))
+ return -1;
+
+ ud[type] = attr;
+ return 0;
+}
+
+static int concat_parse_udata_nested(const struct nftnl_udata *attr, void *data)
+{
+ const struct nftnl_udata **ud = data;
+ uint8_t type = nftnl_udata_type(attr);
+ uint8_t len = nftnl_udata_len(attr);
+
+ switch (type) {
+ case NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE:
+ if (len != sizeof(uint32_t))
+ return -1;
+ break;
+ case NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA:
+ if (len <= sizeof(uint32_t))
+ return -1;
+ break;
+ default:
+ return 0;
+ }
+
+ ud[type] = attr;
+ return 0;
+}
+
+static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
+{
+ const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX] = {};
+ const struct datatype *dtype;
+ struct expr *concat_expr;
+ uint32_t dt = 0, len = 0;
+ unsigned int i;
+ int err;
+
+ err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr),
+ concat_parse_udata_nest, ud);
+ if (err < 0)
+ return NULL;
+
+ concat_expr = concat_expr_alloc(&internal_location);
+ if (!concat_expr)
+ return NULL;
+
+ for (i = 0; i < array_size(ud); i++) {
+ const struct nftnl_udata *nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_MAX];
+ const struct nftnl_udata *nested, *subdata;
+ const struct expr_ops *ops;
+ struct expr *expr;
+ uint32_t etype;
+
+ if (ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST + i] == NULL)
+ break;
+
+ nested = ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST + i];
+ err = nftnl_udata_parse(nftnl_udata_get(nested), nftnl_udata_len(nested),
+ concat_parse_udata_nested, nest_ud);
+ if (err < 0)
+ goto err_free;
+
+ etype = nftnl_udata_get_u32(nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE]);
+ ops = expr_ops_by_type_u32(etype);
+ if (!ops || !ops->parse_udata)
+ goto err_free;
+
+ subdata = nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA];
+ expr = ops->parse_udata(subdata);
+ if (!expr)
+ goto err_free;
+
+ dt = concat_subtype_add(dt, expr->dtype->type);
+ compound_expr_add(concat_expr, expr);
+ len += netlink_padded_len(expr->len);
+ }
+
+ dtype = concat_type_alloc(dt);
+ if (!dtype)
+ goto err_free;
+
+ __datatype_set(concat_expr, dtype);
+ concat_expr->len = len;
+
+ return concat_expr;
+
+err_free:
+ expr_free(concat_expr);
+ return NULL;
+}
+
static const struct expr_ops concat_expr_ops = {
.type = EXPR_CONCAT,
.name = "concat",
@@ -836,6 +1031,8 @@ static const struct expr_ops concat_expr_ops = {
.json = concat_expr_json,
.clone = compound_expr_clone,
.destroy = concat_expr_destroy,
+ .build_udata = concat_expr_build_udata,
+ .parse_udata = concat_expr_parse_udata,
};
struct expr *concat_expr_alloc(const struct location *loc)
@@ -1006,14 +1203,40 @@ struct expr *mapping_expr_alloc(const struct location *loc,
return expr;
}
+static bool __set_expr_is_vmap(const struct expr *mappings)
+{
+ const struct expr *mapping;
+
+ if (list_empty(&mappings->expressions))
+ return false;
+
+ mapping = list_first_entry(&mappings->expressions, struct expr, list);
+ if (mapping->etype == EXPR_MAPPING &&
+ mapping->right->etype == EXPR_VERDICT)
+ return true;
+
+ return false;
+}
+
+static bool set_expr_is_vmap(const struct expr *expr)
+{
+
+ if (expr->mappings->etype == EXPR_SET)
+ return __set_expr_is_vmap(expr->mappings);
+
+ return false;
+}
+
static void map_expr_print(const struct expr *expr, struct output_ctx *octx)
{
expr_print(expr->map, octx);
- if (expr->mappings->etype == EXPR_SET_REF &&
- expr->mappings->set->data->dtype->type == TYPE_VERDICT)
+ if ((expr->mappings->etype == EXPR_SET_REF &&
+ expr->mappings->set->data->dtype->type == TYPE_VERDICT) ||
+ set_expr_is_vmap(expr))
nft_print(octx, " vmap ");
else
nft_print(octx, " map ");
+
expr_print(expr->mappings, octx);
}
@@ -1091,7 +1314,13 @@ struct expr *set_ref_expr_alloc(const struct location *loc, struct set *set)
static void set_elem_expr_print(const struct expr *expr,
struct output_ctx *octx)
{
+ struct stmt *stmt;
+
expr_print(expr->key, octx);
+ list_for_each_entry(stmt, &expr->stmt_list, list) {
+ nft_print(octx, " ");
+ stmt_print(stmt, octx);
+ }
if (expr->timeout) {
nft_print(octx, " timeout ");
time_print(expr->timeout, octx);
@@ -1100,28 +1329,33 @@ static void set_elem_expr_print(const struct expr *expr,
nft_print(octx, " expires ");
time_print(expr->expiration, octx);
}
- if (expr->stmt) {
- nft_print(octx, " ");
- stmt_print(expr->stmt, octx);
- }
if (expr->comment)
nft_print(octx, " comment \"%s\"", expr->comment);
}
static void set_elem_expr_destroy(struct expr *expr)
{
- xfree(expr->comment);
+ struct stmt *stmt, *next;
+
+ free_const(expr->comment);
expr_free(expr->key);
- stmt_free(expr->stmt);
+ list_for_each_entry_safe(stmt, next, &expr->stmt_list, list)
+ stmt_free(stmt);
}
-static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
+static void __set_elem_expr_clone(struct expr *new, const struct expr *expr)
{
- new->key = expr_clone(expr->key);
new->expiration = expr->expiration;
new->timeout = expr->timeout;
if (expr->comment)
new->comment = xstrdup(expr->comment);
+ init_list_head(&new->stmt_list);
+}
+
+static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
+{
+ new->key = expr_clone(expr->key);
+ __set_elem_expr_clone(new, expr);
}
static const struct expr_ops set_elem_expr_ops = {
@@ -1140,6 +1374,93 @@ struct expr *set_elem_expr_alloc(const struct location *loc, struct expr *key)
expr = expr_alloc(loc, EXPR_SET_ELEM, key->dtype,
key->byteorder, key->len);
expr->key = key;
+ init_list_head(&expr->stmt_list);
+
+ return expr;
+}
+
+static void set_elem_catchall_expr_print(const struct expr *expr,
+ struct output_ctx *octx)
+{
+ nft_print(octx, "*");
+}
+
+static void set_elem_catchall_expr_clone(struct expr *new, const struct expr *expr)
+{
+ __set_elem_expr_clone(new, expr);
+}
+
+static const struct expr_ops set_elem_catchall_expr_ops = {
+ .type = EXPR_SET_ELEM_CATCHALL,
+ .name = "catch-all set element",
+ .print = set_elem_catchall_expr_print,
+ .json = set_elem_catchall_expr_json,
+ .clone = set_elem_catchall_expr_clone,
+};
+
+struct expr *set_elem_catchall_expr_alloc(const struct location *loc)
+{
+ struct expr *expr;
+
+ expr = expr_alloc(loc, EXPR_SET_ELEM_CATCHALL, &invalid_type,
+ BYTEORDER_INVALID, 0);
+ expr->flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+
+ return expr;
+}
+
+static void flagcmp_expr_print(const struct expr *expr, struct output_ctx *octx)
+{
+ expr_print(expr->flagcmp.expr, octx);
+
+ if (expr->op == OP_NEQ)
+ nft_print(octx, " != ");
+ else
+ nft_print(octx, " ");
+
+ expr_print(expr->flagcmp.value, octx);
+ nft_print(octx, " / ");
+ expr_print(expr->flagcmp.mask, octx);
+}
+
+static void flagcmp_expr_clone(struct expr *new, const struct expr *expr)
+{
+ new->flagcmp.expr = expr_clone(expr->flagcmp.expr);
+ new->flagcmp.mask = expr_clone(expr->flagcmp.mask);
+ new->flagcmp.value = expr_clone(expr->flagcmp.value);
+}
+
+static void flagcmp_expr_destroy(struct expr *expr)
+{
+ expr_free(expr->flagcmp.expr);
+ expr_free(expr->flagcmp.mask);
+ expr_free(expr->flagcmp.value);
+}
+
+static const struct expr_ops flagcmp_expr_ops = {
+ .type = EXPR_FLAGCMP,
+ .name = "flags comparison",
+ .print = flagcmp_expr_print,
+ .json = flagcmp_expr_json,
+ .clone = flagcmp_expr_clone,
+ .destroy = flagcmp_expr_destroy,
+};
+
+struct expr *flagcmp_expr_alloc(const struct location *loc, enum ops op,
+ struct expr *match, struct expr *mask,
+ struct expr *value)
+{
+ struct expr *expr;
+
+ expr = expr_alloc(loc, EXPR_FLAGCMP, match->dtype, match->byteorder,
+ match->len);
+ expr->op = op;
+ expr->flagcmp.expr = match;
+ expr->flagcmp.mask = mask;
+ /* json output needs this operation for compatibility */
+ expr->flagcmp.mask->op = OP_OR;
+ expr->flagcmp.value = value;
+
return expr;
}
@@ -1170,6 +1491,7 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
return mpz_set(rop, expr->value);
case EXPR_PREFIX:
range_expr_value_low(rop, expr->prefix);
+ assert(expr->len >= expr->prefix_len);
mpz_init_bitmask(tmp, expr->len - expr->prefix_len);
mpz_add(rop, rop, tmp);
mpz_clear(tmp);
@@ -1185,12 +1507,10 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
}
}
-const struct expr_ops *expr_ops(const struct expr *e)
+static const struct expr_ops *__expr_ops_by_type(enum expr_types etype)
{
- switch (e->etype) {
- case EXPR_INVALID:
- BUG("Invalid expression ops requested");
- break;
+ switch (etype) {
+ case EXPR_INVALID: break;
case EXPR_VERDICT: return &verdict_expr_ops;
case EXPR_SYMBOL: return &symbol_expr_ops;
case EXPR_VARIABLE: return &variable_expr_ops;
@@ -1218,28 +1538,28 @@ const struct expr_ops *expr_ops(const struct expr *e)
case EXPR_RT: return &rt_expr_ops;
case EXPR_FIB: return &fib_expr_ops;
case EXPR_XFRM: return &xfrm_expr_ops;
+ case EXPR_SET_ELEM_CATCHALL: return &set_elem_catchall_expr_ops;
+ case EXPR_FLAGCMP: return &flagcmp_expr_ops;
}
- BUG("Unknown expression type %d\n", e->etype);
+ return NULL;
}
-const struct expr_ops *expr_ops_by_type(enum expr_types etype)
+const struct expr_ops *expr_ops(const struct expr *e)
{
- switch (etype) {
- case EXPR_PAYLOAD: return &payload_expr_ops;
- case EXPR_EXTHDR: return &exthdr_expr_ops;
- case EXPR_META: return &meta_expr_ops;
- case EXPR_SOCKET: return &socket_expr_ops;
- case EXPR_OSF: return &osf_expr_ops;
- case EXPR_CT: return &ct_expr_ops;
- case EXPR_NUMGEN: return &numgen_expr_ops;
- case EXPR_HASH: return &hash_expr_ops;
- case EXPR_RT: return &rt_expr_ops;
- case EXPR_FIB: return &fib_expr_ops;
- case EXPR_XFRM: return &xfrm_expr_ops;
- default:
- break;
- }
+ const struct expr_ops *ops;
+
+ ops = __expr_ops_by_type(e->etype);
+ if (!ops)
+ BUG("Unknown expression type %d\n", e->etype);
+
+ return ops;
+}
+
+const struct expr_ops *expr_ops_by_type_u32(uint32_t value)
+{
+ if (value > EXPR_MAX)
+ return NULL;
- BUG("Unknown expression type %d\n", etype);
+ return __expr_ops_by_type(value);
}