]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: hash: support of symmetric hash
authorLaura Garcia Liebana <nevola@gmail.com>
Tue, 28 Feb 2017 17:42:50 +0000 (18:42 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 6 Mar 2017 17:25:05 +0000 (18:25 +0100)
This patch provides symmetric hash support according to source
ip address and port, and destination ip address and port.

The new attribute NFTA_HASH_TYPE has been included to support
different types of hashing functions. Currently supported
NFT_HASH_JENKINS through jhash and NFT_HASH_SYM through symhash.

The main difference between both types are:
 - jhash requires an expression with sreg, symhash doesn't.
 - symhash supports modulus and offset, but not seed.

Examples:

 nft add rule ip nat prerouting ct mark set jhash ip saddr mod 2
 nft add rule ip nat prerouting ct mark set symhash mod 2

Signed-off-by: Laura Garcia Liebana <laura.garcia@zevenet.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/expression.h
include/hash.h
include/linux/netfilter/nf_tables.h
src/evaluate.c
src/hash.c
src/netlink_delinearize.c
src/netlink_linearize.c
src/parser_bison.y
src/scanner.l
tests/py/ip/hash.t
tests/py/ip/hash.t.payload

index 83ecf1114bf14c472895985d9a58cf1c47133182..4f2948cc002be767a4fcdb40addecc94ae837c20 100644 (file)
@@ -309,6 +309,7 @@ struct expr {
                        uint32_t                mod;
                        uint32_t                seed;
                        uint32_t                offset;
+                       enum nft_hash_types     type;
                } hash;
                struct {
                        /* EXPR_FIB */
index 8bf53e2e2bfb2f20cdcdc317c71ed3960439f1c3..7f9c6f13d45484adbadb25cf99e5b615dbc96f69 100644 (file)
@@ -3,6 +3,6 @@
 
 extern struct expr *hash_expr_alloc(const struct location *loc,
                                    uint32_t modulus, uint32_t seed,
-                                   uint32_t offset);
+                                   uint32_t offset, enum nft_hash_types type);
 
 #endif /* NFTABLES_HASH_H */
index 05215d30fe5c9853b7871e799ccdce4878a04ef1..4f7d75682c5917cf9b26e70d9ac09b2e02d8ed7c 100644 (file)
@@ -815,6 +815,17 @@ enum nft_rt_keys {
        NFT_RT_NEXTHOP6,
 };
 
+/**
+ * enum nft_hash_types - nf_tables hash expression types
+ *
+ * @NFT_HASH_JENKINS: Jenkins Hash
+ * @NFT_HASH_SYM: Symmetric Hash
+ */
+enum nft_hash_types {
+       NFT_HASH_JENKINS,
+       NFT_HASH_SYM,
+};
+
 /**
  * enum nft_hash_attributes - nf_tables hash expression netlink attributes
  *
@@ -824,6 +835,7 @@ enum nft_rt_keys {
  * @NFTA_HASH_MODULUS: modulus value (NLA_U32)
  * @NFTA_HASH_SEED: seed value (NLA_U32)
  * @NFTA_HASH_OFFSET: add this offset value to hash result (NLA_U32)
+ * @NFTA_HASH_TYPE: hash operation (NLA_U32: nft_hash_types)
  */
 enum nft_hash_attributes {
        NFTA_HASH_UNSPEC,
@@ -833,6 +845,7 @@ enum nft_hash_attributes {
        NFTA_HASH_MODULUS,
        NFTA_HASH_SEED,
        NFTA_HASH_OFFSET,
+       NFTA_HASH_TYPE,
        __NFTA_HASH_MAX,
 };
 #define NFTA_HASH_MAX  (__NFTA_HASH_MAX - 1)
index 5498516686ad89b49f4472c6f3ccb46039ca18b5..efcafc7249bfcc2ea168dcfd8f74cf76d1a01e3b 100644 (file)
@@ -1288,7 +1288,8 @@ static int expr_evaluate_hash(struct eval_ctx *ctx, struct expr **exprp)
        expr_dtype_integer_compatible(ctx, expr);
 
        expr_set_context(&ctx->ectx, NULL, 0);
-       if (expr_evaluate(ctx, &expr->hash.expr) < 0)
+       if (expr->hash.expr &&
+           expr_evaluate(ctx, &expr->hash.expr) < 0)
                return -1;
 
        /* expr_evaluate_primary() sets the context to what to the input
index d26b2eda84e9058ca3fedd51b5411c8e4304934c..a7a9612271419801e582c08b3f5b6829ea4eab88 100644 (file)
 
 static void hash_expr_print(const struct expr *expr)
 {
-       printf("jhash ");
-       expr_print(expr->hash.expr);
+       switch (expr->hash.type) {
+       case NFT_HASH_SYM:
+               printf("symhash");
+       break;
+       case NFT_HASH_JENKINS:
+       default:
+               printf("jhash ");
+               expr_print(expr->hash.expr);
+       }
+
        printf(" mod %u", expr->hash.mod);
-       if (expr->hash.seed)
+       if (expr->hash.type & NFT_HASH_JENKINS && expr->hash.seed)
                printf(" seed 0x%x", expr->hash.seed);
        if (expr->hash.offset)
                printf(" offset %u", expr->hash.offset);
@@ -28,18 +36,22 @@ static void hash_expr_print(const struct expr *expr)
 
 static bool hash_expr_cmp(const struct expr *e1, const struct expr *e2)
 {
-       return expr_cmp(e1->hash.expr, e2->hash.expr) &&
+       return (e1->hash.expr ||
+               expr_cmp(e1->hash.expr, e2->hash.expr)) &&
               e1->hash.mod == e2->hash.mod &&
               e1->hash.seed == e2->hash.seed &&
-              e1->hash.offset == e2->hash.offset;
+              e1->hash.offset == e2->hash.offset &&
+              e1->hash.type == e2->hash.type;
 }
 
 static void hash_expr_clone(struct expr *new, const struct expr *expr)
 {
-       new->hash.expr = expr_clone(expr->hash.expr);
+       if (expr->hash.expr)
+               new->hash.expr = expr_clone(expr->hash.expr);
        new->hash.mod = expr->hash.mod;
        new->hash.seed = expr->hash.seed;
        new->hash.offset = expr->hash.offset;
+       new->hash.type = expr->hash.type;
 }
 
 static const struct expr_ops hash_expr_ops = {
@@ -51,7 +63,8 @@ static const struct expr_ops hash_expr_ops = {
 };
 
 struct expr *hash_expr_alloc(const struct location *loc, uint32_t mod,
-                            uint32_t seed, uint32_t offset)
+                            uint32_t seed, uint32_t offset,
+                            enum nft_hash_types type)
 {
        struct expr *expr;
 
@@ -60,6 +73,7 @@ struct expr *hash_expr_alloc(const struct location *loc, uint32_t mod,
        expr->hash.mod  = mod;
        expr->hash.seed = seed;
        expr->hash.offset = offset;
+       expr->hash.type = type;
 
        return expr;
 }
index 39347e01ed1c22ec23141185fbc8bd79dad3aed3..36e8fe3c4337b247bd98699dd255e0d30362094e 100644 (file)
@@ -523,27 +523,33 @@ static void netlink_parse_hash(struct netlink_parse_ctx *ctx,
        enum nft_registers sreg, dreg;
        struct expr *expr, *hexpr;
        uint32_t mod, seed, len, offset;
+       enum nft_hash_types type;
 
-       sreg = netlink_parse_register(nle, NFTNL_EXPR_HASH_SREG);
-       hexpr = netlink_get_register(ctx, loc, sreg);
-       if (hexpr == NULL)
-               return netlink_error(ctx, loc,
-                                    "hash statement has no expression");
-
+       type = nftnl_expr_get_u32(nle, NFTNL_EXPR_HASH_TYPE);
        offset = nftnl_expr_get_u32(nle, NFTNL_EXPR_HASH_OFFSET);
        seed = nftnl_expr_get_u32(nle, NFTNL_EXPR_HASH_SEED);
        mod  = nftnl_expr_get_u32(nle, NFTNL_EXPR_HASH_MODULUS);
-       len = nftnl_expr_get_u32(nle, NFTNL_EXPR_HASH_LEN) * BITS_PER_BYTE;
 
-       if (hexpr->len < len) {
-               hexpr = netlink_parse_concat_expr(ctx, loc, sreg, len);
+       expr = hash_expr_alloc(loc, mod, seed, offset, type);
+
+       if (type != NFT_HASH_SYM) {
+               sreg = netlink_parse_register(nle, NFTNL_EXPR_HASH_SREG);
+               hexpr = netlink_get_register(ctx, loc, sreg);
+
                if (hexpr == NULL)
-                       return;
+                       return
+                       netlink_error(ctx, loc,
+                                     "hash statement has no expression");
+               len = nftnl_expr_get_u32(nle,
+                                        NFTNL_EXPR_HASH_LEN) * BITS_PER_BYTE;
+               if (hexpr->len < len) {
+                       hexpr = netlink_parse_concat_expr(ctx, loc, sreg, len);
+                       if (hexpr == NULL)
+                               return;
+               }
+               expr->hash.expr = hexpr;
        }
 
-       expr = hash_expr_alloc(loc, mod, seed, offset);
-       expr->hash.expr = hexpr;
-
        dreg = netlink_parse_register(nle, NFTNL_EXPR_HASH_DREG);
        netlink_set_register(ctx, dreg, expr);
 }
@@ -1826,7 +1832,8 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
        case EXPR_FIB:
                break;
        case EXPR_HASH:
-               expr_postprocess(ctx, &expr->hash.expr);
+               if (expr->hash.expr)
+                       expr_postprocess(ctx, &expr->hash.expr);
                break;
        case EXPR_CT:
                ct_expr_update_type(&ctx->pctx, expr);
index 48f34c25acdaff6b70277f957f68ffac782e8caf..293150e2d01556067c90e584a12d070e91e5db81 100644 (file)
@@ -125,18 +125,23 @@ static void netlink_gen_hash(struct netlink_linearize_ctx *ctx,
        enum nft_registers sreg;
        struct nftnl_expr *nle;
 
-       sreg = get_register(ctx, expr->hash.expr);
-       netlink_gen_expr(ctx, expr->hash.expr, sreg);
-       release_register(ctx, expr->hash.expr);
-
        nle = alloc_nft_expr("hash");
-       netlink_put_register(nle, NFTNL_EXPR_HASH_SREG, sreg);
+
+       if (expr->hash.expr) {
+               sreg = get_register(ctx, expr->hash.expr);
+               netlink_gen_expr(ctx, expr->hash.expr, sreg);
+               release_register(ctx, expr->hash.expr);
+               netlink_put_register(nle, NFTNL_EXPR_HASH_SREG, sreg);
+
+               nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_LEN,
+                                  div_round_up(expr->hash.expr->len,
+                                               BITS_PER_BYTE));
+       }
        netlink_put_register(nle, NFTNL_EXPR_HASH_DREG, dreg);
-       nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_LEN,
-                          div_round_up(expr->hash.expr->len, BITS_PER_BYTE));
        nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_MODULUS, expr->hash.mod);
        nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_SEED, expr->hash.seed);
        nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_OFFSET, expr->hash.offset);
+       nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_TYPE, expr->hash.type);
        nftnl_rule_add_expr(ctx->nlr, nle);
 }
 
index 15931e9610ce1b29805461b5609a9dec4872a80d..dff8a5ab90aceb3ece4ff12666883d7f8a35f787 100644 (file)
@@ -437,6 +437,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %token OFFSET                  "offset"
 
 %token JHASH                   "jhash"
+%token SYMHASH                 "symhash"
 %token SEED                    "seed"
 
 %token POSITION                        "position"
@@ -512,7 +513,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %destructor { stmt_free($$); } reject_stmt reject_stmt_alloc
 %type <stmt>                   nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
 %destructor { stmt_free($$); } nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
-%type <val>                    nf_nat_flags nf_nat_flag offset_opt
+%type <val>                    nf_nat_flags nf_nat_flag offset_opt seed_opt
 %type <stmt>                   queue_stmt queue_stmt_alloc
 %destructor { stmt_free($$); } queue_stmt queue_stmt_alloc
 %type <val>                    queue_stmt_flags queue_stmt_flag
@@ -2916,15 +2917,18 @@ numgen_expr             :       NUMGEN  numgen_type     MOD     NUM     offset_opt
                        }
                        ;
 
-hash_expr              :       JHASH   expr    MOD     NUM     SEED    NUM     offset_opt
+seed_opt               :       /* empty */     { $$ = 0; }
+                       |       SEED    NUM     { $$ = $2; }
+                       ;
+
+hash_expr              :       JHASH           expr    MOD     NUM     seed_opt        offset_opt
                        {
-                               $$ = hash_expr_alloc(&@$, $4, $6, $7);
+                               $$ = hash_expr_alloc(&@$, $4, $5, $6, NFT_HASH_JENKINS);
                                $$->hash.expr = $2;
                        }
-                       |       JHASH   expr    MOD     NUM     offset_opt
+                       |       SYMHASH         MOD     NUM     offset_opt
                        {
-                               $$ = hash_expr_alloc(&@$, $4, 0, $5);
-                               $$->hash.expr = $2;
+                               $$ = hash_expr_alloc(&@$, $3, 0, $4, NFT_HASH_SYM);
                        }
                        ;
 
index b70e1a80712de864604deaa4bb78f133d1a744ec..003f15eb1c3c27e1e673a0c862e21e33cbcf4af6 100644 (file)
@@ -488,6 +488,7 @@ addrstring  ({macaddr}|{ip4addr}|{ip6addr})
 "offset"               { return OFFSET; }
 
 "jhash"                        { return JHASH; }
+"symhash"              { return SYMHASH; }
 "seed"                 { return SEED; }
 
 "dup"                  { return DUP; }
index 0d01a11dcedefcb935675c1fee4da9b56c416971..2becef6214e170769ac697ce5ab9c37aa32f6b88 100644 (file)
@@ -6,3 +6,4 @@ ct mark set jhash ip saddr . ip daddr mod 2;ok
 ct mark set jhash ip saddr . ip daddr mod 2 seed 0xdeadbeef offset 100;ok
 ct mark set jhash ip saddr . ip daddr mod 2 offset 100;ok
 dnat to jhash ip saddr mod 2 seed 0xdeadbeef map { 0 : 192.168.20.100, 1 : 192.168.30.100 };ok
+ct mark set symhash mod 2 offset 100;ok
index 8c28ad98987e26d1ba327b5f1f0df8eff9815b55..21227e913a5cb3224cb4dd31f5c1dc9a83a7e80a 100644 (file)
@@ -36,3 +36,7 @@ ip test-ip4 pre
   [ lookup reg 1 set __map%d dreg 1 ]
   [ nat dnat ip addr_min reg 1 addr_max reg 0 ]
 
+# ct mark set symhash mod 2 offset 100
+ip test-ip4 pre
+  [ hash reg 1 = symhash() % mod 2 offset 100 ]
+  [ ct set mark with reg 1 ]