Skip to content

Commit

Permalink
netfilter: nft_hash: support of symmetric hash
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Laura Garcia Liebana authored and Pablo Neira Ayuso committed Mar 6, 2017
1 parent 511040e commit 3206cad
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 1 deletion.
13 changes: 13 additions & 0 deletions include/uapi/linux/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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,
Expand All @@ -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)
Expand Down
99 changes: 98 additions & 1 deletion net/netfilter/nft_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,33 @@ static void nft_jhash_eval(const struct nft_expr *expr,
regs->data[priv->dreg] = h + priv->offset;
}

struct nft_symhash {
enum nft_registers dreg:8;
u32 modulus;
u32 offset;
};

static void nft_symhash_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_symhash *priv = nft_expr_priv(expr);
struct sk_buff *skb = pkt->skb;
u32 h;

h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus);

regs->data[priv->dreg] = h + priv->offset;
}

static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = {
[NFTA_HASH_SREG] = { .type = NLA_U32 },
[NFTA_HASH_DREG] = { .type = NLA_U32 },
[NFTA_HASH_LEN] = { .type = NLA_U32 },
[NFTA_HASH_MODULUS] = { .type = NLA_U32 },
[NFTA_HASH_SEED] = { .type = NLA_U32 },
[NFTA_HASH_OFFSET] = { .type = NLA_U32 },
[NFTA_HASH_TYPE] = { .type = NLA_U32 },
};

static int nft_jhash_init(const struct nft_ctx *ctx,
Expand Down Expand Up @@ -92,6 +112,32 @@ static int nft_jhash_init(const struct nft_ctx *ctx,
NFT_DATA_VALUE, sizeof(u32));
}

static int nft_symhash_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_symhash *priv = nft_expr_priv(expr);

if (!tb[NFTA_HASH_DREG] ||
!tb[NFTA_HASH_MODULUS])
return -EINVAL;

if (tb[NFTA_HASH_OFFSET])
priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));

priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]);

priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
if (priv->modulus <= 1)
return -ERANGE;

if (priv->offset + priv->modulus - 1 < priv->offset)
return -EOVERFLOW;

return nft_validate_register_store(ctx, priv->dreg, NULL,
NFT_DATA_VALUE, sizeof(u32));
}

static int nft_jhash_dump(struct sk_buff *skb,
const struct nft_expr *expr)
{
Expand All @@ -110,6 +156,28 @@ static int nft_jhash_dump(struct sk_buff *skb,
if (priv->offset != 0)
if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS)))
goto nla_put_failure;
return 0;

nla_put_failure:
return -1;
}

static int nft_symhash_dump(struct sk_buff *skb,
const struct nft_expr *expr)
{
const struct nft_symhash *priv = nft_expr_priv(expr);

if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
goto nla_put_failure;
if (priv->offset != 0)
if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM)))
goto nla_put_failure;
return 0;

nla_put_failure:
Expand All @@ -125,9 +193,38 @@ static const struct nft_expr_ops nft_jhash_ops = {
.dump = nft_jhash_dump,
};

static const struct nft_expr_ops nft_symhash_ops = {
.type = &nft_hash_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_symhash)),
.eval = nft_symhash_eval,
.init = nft_symhash_init,
.dump = nft_symhash_dump,
};

static const struct nft_expr_ops *
nft_hash_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
u32 type;

if (!tb[NFTA_HASH_TYPE])
return &nft_jhash_ops;

type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE]));
switch (type) {
case NFT_HASH_SYM:
return &nft_symhash_ops;
case NFT_HASH_JENKINS:
return &nft_jhash_ops;
default:
break;
}
return ERR_PTR(-EOPNOTSUPP);
}

static struct nft_expr_type nft_hash_type __read_mostly = {
.name = "hash",
.ops = &nft_jhash_ops,
.select_ops = &nft_hash_select_ops,
.policy = nft_hash_policy,
.maxattr = NFTA_HASH_MAX,
.owner = THIS_MODULE,
Expand Down

0 comments on commit 3206cad

Please sign in to comment.