Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nft_hash: support of symmetric hash

This patch provides symmetric hash support according to source
ip address and port, and destination ip address and port.

For this purpose, the __skb_get_hash_symmetric() is used to
identify the flow as it uses FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL
flag by default.

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

By default, jenkins hash will be used if no hash type is
provided for compatibility reasons.

Signed-off-by: Laura Garcia Liebana <laura.garcia@zevenet.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Laura Garcia Liebana and committed by
Pablo Neira Ayuso
3206cade 511040ee

+111 -1
+13
include/uapi/linux/netfilter/nf_tables.h
··· 816 816 }; 817 817 818 818 /** 819 + * enum nft_hash_types - nf_tables hash expression types 820 + * 821 + * @NFT_HASH_JENKINS: Jenkins Hash 822 + * @NFT_HASH_SYM: Symmetric Hash 823 + */ 824 + enum nft_hash_types { 825 + NFT_HASH_JENKINS, 826 + NFT_HASH_SYM, 827 + }; 828 + 829 + /** 819 830 * enum nft_hash_attributes - nf_tables hash expression netlink attributes 820 831 * 821 832 * @NFTA_HASH_SREG: source register (NLA_U32) ··· 835 824 * @NFTA_HASH_MODULUS: modulus value (NLA_U32) 836 825 * @NFTA_HASH_SEED: seed value (NLA_U32) 837 826 * @NFTA_HASH_OFFSET: add this offset value to hash result (NLA_U32) 827 + * @NFTA_HASH_TYPE: hash operation (NLA_U32: nft_hash_types) 838 828 */ 839 829 enum nft_hash_attributes { 840 830 NFTA_HASH_UNSPEC, ··· 845 833 NFTA_HASH_MODULUS, 846 834 NFTA_HASH_SEED, 847 835 NFTA_HASH_OFFSET, 836 + NFTA_HASH_TYPE, 848 837 __NFTA_HASH_MAX, 849 838 }; 850 839 #define NFTA_HASH_MAX (__NFTA_HASH_MAX - 1)
+98 -1
net/netfilter/nft_hash.c
··· 38 38 regs->data[priv->dreg] = h + priv->offset; 39 39 } 40 40 41 + struct nft_symhash { 42 + enum nft_registers dreg:8; 43 + u32 modulus; 44 + u32 offset; 45 + }; 46 + 47 + static void nft_symhash_eval(const struct nft_expr *expr, 48 + struct nft_regs *regs, 49 + const struct nft_pktinfo *pkt) 50 + { 51 + struct nft_symhash *priv = nft_expr_priv(expr); 52 + struct sk_buff *skb = pkt->skb; 53 + u32 h; 54 + 55 + h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus); 56 + 57 + regs->data[priv->dreg] = h + priv->offset; 58 + } 59 + 41 60 static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = { 42 61 [NFTA_HASH_SREG] = { .type = NLA_U32 }, 43 62 [NFTA_HASH_DREG] = { .type = NLA_U32 }, ··· 64 45 [NFTA_HASH_MODULUS] = { .type = NLA_U32 }, 65 46 [NFTA_HASH_SEED] = { .type = NLA_U32 }, 66 47 [NFTA_HASH_OFFSET] = { .type = NLA_U32 }, 48 + [NFTA_HASH_TYPE] = { .type = NLA_U32 }, 67 49 }; 68 50 69 51 static int nft_jhash_init(const struct nft_ctx *ctx, ··· 112 92 NFT_DATA_VALUE, sizeof(u32)); 113 93 } 114 94 95 + static int nft_symhash_init(const struct nft_ctx *ctx, 96 + const struct nft_expr *expr, 97 + const struct nlattr * const tb[]) 98 + { 99 + struct nft_symhash *priv = nft_expr_priv(expr); 100 + 101 + if (!tb[NFTA_HASH_DREG] || 102 + !tb[NFTA_HASH_MODULUS]) 103 + return -EINVAL; 104 + 105 + if (tb[NFTA_HASH_OFFSET]) 106 + priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET])); 107 + 108 + priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]); 109 + 110 + priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS])); 111 + if (priv->modulus <= 1) 112 + return -ERANGE; 113 + 114 + if (priv->offset + priv->modulus - 1 < priv->offset) 115 + return -EOVERFLOW; 116 + 117 + return nft_validate_register_store(ctx, priv->dreg, NULL, 118 + NFT_DATA_VALUE, sizeof(u32)); 119 + } 120 + 115 121 static int nft_jhash_dump(struct sk_buff *skb, 116 122 const struct nft_expr *expr) 117 123 { ··· 156 110 if (priv->offset != 0) 157 111 if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset))) 158 112 goto nla_put_failure; 113 + if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS))) 114 + goto nla_put_failure; 115 + return 0; 116 + 117 + nla_put_failure: 118 + return -1; 119 + } 120 + 121 + static int nft_symhash_dump(struct sk_buff *skb, 122 + const struct nft_expr *expr) 123 + { 124 + const struct nft_symhash *priv = nft_expr_priv(expr); 125 + 126 + if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg)) 127 + goto nla_put_failure; 128 + if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus))) 129 + goto nla_put_failure; 130 + if (priv->offset != 0) 131 + if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset))) 132 + goto nla_put_failure; 133 + if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM))) 134 + goto nla_put_failure; 159 135 return 0; 160 136 161 137 nla_put_failure: ··· 193 125 .dump = nft_jhash_dump, 194 126 }; 195 127 128 + static const struct nft_expr_ops nft_symhash_ops = { 129 + .type = &nft_hash_type, 130 + .size = NFT_EXPR_SIZE(sizeof(struct nft_symhash)), 131 + .eval = nft_symhash_eval, 132 + .init = nft_symhash_init, 133 + .dump = nft_symhash_dump, 134 + }; 135 + 136 + static const struct nft_expr_ops * 137 + nft_hash_select_ops(const struct nft_ctx *ctx, 138 + const struct nlattr * const tb[]) 139 + { 140 + u32 type; 141 + 142 + if (!tb[NFTA_HASH_TYPE]) 143 + return &nft_jhash_ops; 144 + 145 + type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE])); 146 + switch (type) { 147 + case NFT_HASH_SYM: 148 + return &nft_symhash_ops; 149 + case NFT_HASH_JENKINS: 150 + return &nft_jhash_ops; 151 + default: 152 + break; 153 + } 154 + return ERR_PTR(-EOPNOTSUPP); 155 + } 156 + 196 157 static struct nft_expr_type nft_hash_type __read_mostly = { 197 158 .name = "hash", 198 - .ops = &nft_jhash_ops, 159 + .select_ops = &nft_hash_select_ops, 199 160 .policy = nft_hash_policy, 200 161 .maxattr = NFTA_HASH_MAX, 201 162 .owner = THIS_MODULE,