summaryrefslogtreecommitdiffstats
path: root/src/netlink_delinearize.c
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2015-10-18 20:02:16 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2015-11-02 12:51:36 +0100
commitb851ba4731d9f7c5e38889875a83173fcc4d3f16 (patch)
tree3ca89f5c184998ece7216eae4d9095807cb7ef0f /src/netlink_delinearize.c
parent0721fbbe7a951a1e879d120c7a722012c38af9a6 (diff)
src: add interface wildcard matching
Contrary to iptables, we use the asterisk character '*' as wildcard. # nft --debug=netlink add rule test test iifname eth\* ip test test [ meta load iifname => reg 1 ] [ cmp eq reg 1 0x00687465 ] Note that this generates an optimized comparison without bitwise. In case you want to match a device that contains an asterisk, you have to escape the asterisk, ie. # nft add rule test test iifname eth\\* The wildcard string handling occurs from the evaluation step, where we convert from: relational / \ / \ meta value oifname eth* to: relational / \ / \ meta prefix ofiname As Patrick suggested, this not actually a wildcard but a prefix since it only applies to the string when placed at the end. More comments: * This relaxes the left->size > right->size from netlink_parse_cmp() for strings since the optimization that this patch applies may now result in bogus errors. * This patch can be later on extended to apply a similar optimization to payload expressions when: expr->len % BITS_PER_BYTE == 0 For meta and ct, the kernel checks for the exact length of the attributes (it expects integer 32 bits) so we can't do it unless we relax that. * Wildcard strings are not supported from sets and maps yet. Error reporting is not very good at this stage since expr_evaluate_prefix() doesn't have enough context (ctx->set is NULL, the set object is currently created later after evaluating the lhs and rhs of the relational). I'll be following up on this later. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src/netlink_delinearize.c')
-rw-r--r--src/netlink_delinearize.c115
1 files changed, 93 insertions, 22 deletions
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 09f5932a..3584de78 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -232,11 +232,11 @@ static void netlink_parse_cmp(struct netlink_parse_ctx *ctx,
nld.value = nftnl_expr_get(nle, NFTNL_EXPR_CMP_DATA, &nld.len);
right = netlink_alloc_value(loc, &nld);
- if (left->len != right->len) {
- if (left->len > right->len)
- return netlink_error(ctx, loc,
- "Relational expression size "
- "mismatch");
+ if (left->len > right->len &&
+ left->dtype != &string_type) {
+ return netlink_error(ctx, loc,
+ "Relational expression size mismatch");
+ } else if (left->len < right->len) {
left = netlink_parse_concat_expr(ctx, loc, sreg, right->len);
if (left == NULL)
return;
@@ -1088,7 +1088,7 @@ static void meta_match_postprocess(struct rule_pp_ctx *ctx,
}
/* Convert a bitmask to a prefix length */
-static unsigned int expr_mask_to_prefix(struct expr *expr)
+static unsigned int expr_mask_to_prefix(const struct expr *expr)
{
unsigned long n;
@@ -1214,6 +1214,91 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *e
}
}
+static struct expr *string_wildcard_expr_alloc(struct location *loc,
+ const struct expr *mask,
+ const struct expr *expr)
+{
+ unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+ char data[len + 2];
+ int pos;
+
+ mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len);
+ pos = div_round_up(expr_mask_to_prefix(mask), BITS_PER_BYTE);
+ data[pos] = '*';
+ data[pos + 1] = '\0';
+
+ return constant_expr_alloc(loc, &string_type, BYTEORDER_HOST_ENDIAN,
+ expr->len + BITS_PER_BYTE, data);
+}
+
+static void escaped_string_wildcard_expr_alloc(struct expr **exprp,
+ unsigned int len)
+{
+ struct expr *expr = *exprp, *tmp;
+ char data[len + 3];
+ int pos;
+
+ mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len);
+ pos = div_round_up(len, BITS_PER_BYTE);
+ data[pos - 1] = '\\';
+ data[pos] = '*';
+
+ tmp = constant_expr_alloc(&expr->location, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ expr->len + BITS_PER_BYTE, data);
+ expr_free(expr);
+ *exprp = tmp;
+}
+
+/* This calculates the string length and checks if it is nul-terminated, this
+ * function is quite a hack :)
+ */
+static bool __expr_postprocess_string(struct expr **exprp)
+{
+ struct expr *expr = *exprp;
+ unsigned int len = expr->len;
+ bool nulterminated = false;
+ 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;
+ else
+ nulterminated = true;
+ len -= BITS_PER_BYTE;
+ }
+
+ mpz_rshift_ui(tmp, len - BITS_PER_BYTE);
+
+ if (nulterminated &&
+ mpz_cmp_ui(tmp, '*') == 0)
+ escaped_string_wildcard_expr_alloc(exprp, len);
+
+ mpz_clear(tmp);
+ expr->len = len;
+
+ return nulterminated;
+}
+
+static struct expr *expr_postprocess_string(struct expr *expr)
+{
+ struct expr *mask;
+
+ assert(expr->dtype->type == TYPE_STRING);
+ if (__expr_postprocess_string(&expr))
+ return expr;
+
+ mask = constant_expr_alloc(&expr->location, &integer_type,
+ BYTEORDER_HOST_ENDIAN,
+ expr->len + BITS_PER_BYTE, NULL);
+ mpz_init_bitmask(mask->value, expr->len);
+ return string_wildcard_expr_alloc(&expr->location, mask, expr);
+}
+
static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp, *i;
@@ -1299,22 +1384,8 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
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;
- }
+ if (expr->dtype->type == TYPE_STRING)
+ *exprp = expr_postprocess_string(expr);
if (expr->dtype->basetype != NULL &&
expr->dtype->basetype->type == TYPE_BITMASK)