]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
netlink: fix network address prefix
authorPablo Neira Ayuso <pablo@netfilter.org>
Sat, 22 Jun 2013 17:12:24 +0000 (19:12 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 24 Jun 2013 12:06:09 +0000 (14:06 +0200)
eg. nft add rule filter output ip daddr 192.168.1.0/24 counter

so far, this operation was only possible using sets.

nft add rule filter output ip daddr \{ 192.168.1.0/24 \} counter

While at it, move all binop postprocess code to a new function that
contains this transformation and the existing bitmask to constant
(as used by eg. ct state new,established).

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
src/netlink.c
src/netlink_delinearize.c
src/netlink_linearize.c

index d835281c30bc5653db52ae559337c1eebbdb1dc9..2a7bdb567fa784d1a64068555e2bc9e55358fed7 100644 (file)
@@ -228,6 +228,28 @@ static void netlink_gen_verdict(const struct expr *expr,
        }
 }
 
+static void netlink_gen_prefix(const struct expr *expr,
+                              struct nft_data_linearize *data)
+{
+       uint32_t i, cidr, idx;
+       uint32_t mask;
+
+       assert(expr->ops->type == EXPR_PREFIX);
+
+       data->len = div_round_up(expr->prefix->len, BITS_PER_BYTE);
+       cidr = expr->prefix_len;
+
+       for (i = 0; i < data->len; i+= 32) {
+               if (cidr - i >= 32)
+                       mask = 0;
+               else
+                       mask = (1 << cidr) - 1;
+
+               idx = i / 32;
+               data->value[idx] = mask;
+       }
+}
+
 void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
 {
        switch (expr->ops->type) {
@@ -237,6 +259,8 @@ void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
                return netlink_gen_concat_data(expr, data);
        case EXPR_VERDICT:
                return netlink_gen_verdict(expr, data);
+       case EXPR_PREFIX:
+               return netlink_gen_prefix(expr, data);
        default:
                BUG("invalid data expression type %s\n", expr->ops->name);
        }
index c24e105ca3cd6c363bc3db9ee4f80f2838bfcb8a..93489138f1851ef06433be79a6fef00917b43490 100644 (file)
@@ -593,11 +593,73 @@ static void meta_match_postprocess(struct payload_ctx *ctx,
        }
 }
 
+static int expr_value2cidr(struct expr *expr)
+{
+       int i, j, k = 0;
+       uint32_t data[4], bits;
+       uint32_t len = div_round_up(expr->len, BITS_PER_BYTE) / sizeof(uint32_t);
+
+       assert(expr->ops->type == EXPR_VALUE);
+
+       mpz_export_data(data, expr->value, expr->byteorder, len);
+
+       for (i = len - 1; i >= 0; i--) {
+               j = 32;
+               bits = UINT32_MAX >> 1;
+
+               if (data[i] == UINT32_MAX)
+                       goto next;
+
+               while (--j >= 0) {
+                       if (data[i] == bits)
+                               break;
+
+                       bits >>=1;
+               }
+next:
+               k += j;
+       }
+       return k;
+}
+
+static void relational_binop_postprocess(struct expr *expr)
+{
+       struct expr *binop = expr->left, *value = expr->right, *i;
+       unsigned long n;
+
+       if (binop->op == OP_AND && expr->op == OP_NEQ &&
+           expr->right->dtype->basetype->type == TYPE_BITMASK) {
+               expr_free(expr->right);
+               expr->right = list_expr_alloc(&binop->left->location);
+               n = 0;
+               while ((n = mpz_scan1(binop->right->value, n)) != ULONG_MAX) {
+                       i = constant_expr_alloc(&binop->right->location,
+                                               binop->left->dtype,
+                                               binop->right->byteorder,
+                                               binop->right->len, NULL);
+                       mpz_set_ui(i->value, 1);
+                       mpz_lshift_ui(i->value, n);
+                       compound_expr_add(expr->right, i);
+                       n++;
+               }
+               expr->left = binop->left;
+               expr->op = OP_FLAGCMP;
+       } else if ((binop->left->dtype->type == TYPE_IPADDR ||
+                   binop->left->dtype->type == TYPE_IP6ADDR) &&
+                   binop->op == OP_AND) {
+               expr->left = expr_clone(binop->left);
+               expr->right = prefix_expr_alloc(&expr->location,
+                                               expr_clone(value),
+                                               expr_value2cidr(binop->right));
+               expr_free(value);
+               expr_free(binop);
+       }
+}
+
 static void expr_postprocess(struct rule_pp_ctx *ctx,
                             struct stmt *stmt, struct expr **exprp)
 {
        struct expr *expr = *exprp, *i;
-       unsigned long n;
 
        //pr_debug("%s len %u\n", expr->ops->name, expr->len);
 
@@ -648,26 +710,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx,
                        meta_match_postprocess(&ctx->pctx, expr);
                        break;
                case EXPR_BINOP:
-                       if (expr->left->op != OP_AND ||
-                           expr->op != OP_NEQ ||
-                           expr->right->dtype->basetype->type != TYPE_BITMASK)
-                               break;
-
-                       expr_free(expr->right);
-                       expr->right = list_expr_alloc(&expr->left->left->location);
-                       n = 0;
-                       while ((n = mpz_scan1(expr->left->right->value, n)) != ULONG_MAX) {
-                               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);
-                               n++;
-                       }
-                       expr->left = expr->left->left;
-                       expr->op = OP_FLAGCMP;
+                       relational_binop_postprocess(expr);
                        break;
                default:
                        break;
index 044815a3ea3b9ba2928647c674379f0410cceae0..e507f9125619ca401111cfa5ad5113070701c78a 100644 (file)
@@ -184,18 +184,36 @@ static void netlink_gen_cmp(struct netlink_linearize_ctx *ctx,
 {
        struct nft_rule_expr *nle;
        enum nft_registers sreg;
-       struct nft_data_linearize nld;
+       struct nft_data_linearize nld, zero = {};
+       struct expr *right;
 
        assert(dreg == NFT_REG_VERDICT);
 
        sreg = get_register(ctx);
        netlink_gen_expr(ctx, expr->left, sreg);
 
+       if (expr->right->ops->type == EXPR_PREFIX) {
+               right = expr->right->prefix;
+
+               netlink_gen_data(expr->right, &nld);
+               zero.len = nld.len;
+
+               nle = alloc_nft_expr("bitwise");
+               nft_rule_expr_set_u32(nle, NFT_EXPR_BITWISE_SREG, sreg);
+               nft_rule_expr_set_u32(nle, NFT_EXPR_BITWISE_DREG, sreg);
+               nft_rule_expr_set_u32(nle, NFT_EXPR_BITWISE_LEN, nld.len);
+               nft_rule_expr_set(nle, NFT_EXPR_BITWISE_MASK, &nld.value, nld.len);
+               nft_rule_expr_set(nle, NFT_EXPR_BITWISE_XOR, &zero.value, zero.len);
+               nft_rule_add_expr(ctx->nlr, nle);
+       } else {
+               right = expr->right;
+       }
+
        nle = alloc_nft_expr("cmp");
        nft_rule_expr_set_u8(nle, NFT_EXPR_CMP_SREG, sreg);
        nft_rule_expr_set_u8(nle, NFT_EXPR_CMP_OP,
-                             netlink_gen_cmp_op(expr->op));
-       netlink_gen_data(expr->right, &nld);
+                            netlink_gen_cmp_op(expr->op));
+       netlink_gen_data(right, &nld);
        nft_rule_expr_set(nle, NFT_EXPR_CMP_DATA, nld.value, nld.len);
        release_register(ctx);