Skip to contentSkip to conversation

netfilter: meta: add PRANDOM support

PATCH Superseded Florian Westphal 2 participants 5 messages Feb 16, 2016

Subsystems (3)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Florian Westphal

author maintainer
#0 lore raw

PATCH 1/1 netfilter: meta: add PRANDOM support

Can be used to randomly match a packet, e.g. for statistical traffic sampling.

See commit 3ad0040573b0c00f8848
("bpf: split state from prandom_u32() and consolidate {c, e}BPF prngs") for more info why this doesn't use prandom_u32 directly.

Unlike bpf nft_meta can be built as a module, so add an EXPORT_SYMBOL for prandom_seed_full_state too.

---
 include/uapi/linux/netfilter/nf_tables.h |  2 ++
 lib/random32.c                           |  1 +
 net/netfilter/nft_meta.c                 | 15 +++++++++++++++
 3 files changed, 18 insertions(+)
include/uapi/linux/netfilter/nf_tables.h +2 -0
index be41ffc..b19be0a 100644
@@ -681,6 +681,7 @@ enum nft_exthdr_attributes {
  * @NFT_META_IIFGROUP: packet input interface group
  * @NFT_META_OIFGROUP: packet output interface group
  * @NFT_META_CGROUP: socket control group (skb->sk->sk_classid)
+ * @NFT_META_PRANDOM: a 32bit pseudo-random number
  */
 enum nft_meta_keys {
 	NFT_META_LEN,
@@ -707,6 +708,7 @@ enum nft_meta_keys {
 	NFT_META_IIFGROUP,
 	NFT_META_OIFGROUP,
 	NFT_META_CGROUP,
+	NFT_META_PRANDOM,
 };
 
 /**
lib/random32.c +1 -0
index 1211191..510d1ce 100644
@@ -255,6 +255,7 @@ void prandom_seed_full_state(struct rnd_state __percpu *pcpu_state)
 		prandom_warmup(state);
 	}
 }
+EXPORT_SYMBOL(prandom_seed_full_state);
 
 /*
  *	Generate better values after random number generator
net/netfilter/nft_meta.c +15 -0
index fe885bf..6c46671 100644
@@ -28,6 +28,8 @@
 
 #include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */
 
+static DEFINE_PER_CPU(struct rnd_state, nft_prandom_state);
+
 void nft_meta_get_eval(const struct nft_expr *expr,
 		       struct nft_regs *regs,
 		       const struct nft_pktinfo *pkt)
@@ -181,6 +183,11 @@ void nft_meta_get_eval(const struct nft_expr *expr,
 		*dest = sock_cgroup_classid(&sk->sk_cgrp_data);
 		break;
 #endif
+	case NFT_META_PRANDOM: {
+		struct rnd_state *state = this_cpu_ptr(&nft_prandom_state);
+		*dest = prandom_u32_state(state);
+		break;
+	}
 	default:
 		WARN_ON(1);
 		goto err;
@@ -241,6 +248,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
 {
 	struct nft_meta *priv = nft_expr_priv(expr);
 	unsigned int len;
+	static bool prand_inited __read_mostly;
 
 	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
 	switch (priv->key) {
@@ -277,6 +285,13 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
 	case NFT_META_OIFNAME:
 		len = IFNAMSIZ;
 		break;
+	case NFT_META_PRANDOM:
+		if (!prand_inited) {
+			prandom_seed_full_state(&nft_prandom_state);
+			prand_inited = true;
+		}
+		len = sizeof(u32);
+		break;
 	default:
 		return -EOPNOTSUPP;
 	}
Signature · 1 line
2.4.10
#1 lore raw

Hi Florian,

Quoted 12 lines · Florian Westphal

Can be used to randomly match a packet, e.g. for statistical traffic sampling.

See commit 3ad0040573b0c00f8848
("bpf: split state from prandom_u32() and consolidate {c, e}BPF prngs") for more info why this doesn't use prandom_u32 directly.

Unlike bpf nft_meta can be built as a module, so add an EXPORT_SYMBOL for prandom_seed_full_state too.

[...]

Quoted 9 lines
net/netfilter/nft_meta.c +1 -0
@@ -241,6 +248,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
  {
  	struct nft_meta *priv = nft_expr_priv(expr);
  	unsigned int len;
+	static bool prand_inited __read_mostly;
 
  	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
  	switch (priv->key) {

net/netfilter/nft_meta.c int nft_meta_get_init(const struct nft_ctx *ctx, · line 292

Quoted 8 lines
net/netfilter/nft_meta.c +5 -0
@@ -277,6 +285,13 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
  	case NFT_META_OIFNAME:
  		len = IFNAMSIZ;
  		break;
+	case NFT_META_PRANDOM:
+		if (!prand_inited) {
+			prandom_seed_full_state(&nft_prandom_state);
+			prand_inited = true;
+		}

Should this be: prandom_init_once() ?

net/netfilter/nft_meta.c int nft_meta_get_init(const struct nft_ctx *ctx, · line 297

Quoted 6 lines
net/netfilter/nft_meta.c +2 -0
+		len = sizeof(u32);
+		break;
  	default:
  		return -EOPNOTSUPP;
  	}

Thanks,
Daniel

author maintainer
#2 lore raw
Quoted 7 lines · Daniel Borkmann

net/netfilter/nft_meta.c int nft_meta_get_init(const struct nft_ctx *ctx, · line 292

Quoted 5 lines
net/netfilter/nft_meta.c +5 -0
+	case NFT_META_PRANDOM:
+		if (!prand_inited) {
+			prandom_seed_full_state(&nft_prandom_state);
+			prand_inited = true;
+		}

Should this be: prandom_init_once() ?

Thought about that but this is slowpath so I considered the use of static key magic a bit overkill....

I don't mind, if you think prandom_init_once is prefereable I'll respin.

Let me know, thanks!

#3 lore raw
Quoted 9 lines · Florian Westphal
Quoted 7 lines · Daniel Borkmann

net/netfilter/nft_meta.c int nft_meta_get_init(const struct nft_ctx *ctx, · line 292

Quoted 5 lines
net/netfilter/nft_meta.c +5 -0
+	case NFT_META_PRANDOM:
+		if (!prand_inited) {
+			prandom_seed_full_state(&nft_prandom_state);
+			prand_inited = true;
+		}

Should this be: prandom_init_once() ?

Quoted 4 lines

Thought about that but this is slowpath so I considered the use of static key magic a bit overkill....

I don't mind, if you think prandom_init_once is prefereable I'll respin.

You'd have the benefit that the prng init would be race free. nft_meta_get_init() could be called in parallel from multiple CPUs, right?

Quoted 1 line

Let me know, thanks!

author maintainer
#4 lore raw
Quoted 10 lines · Daniel Borkmann
Quoted 9 lines · Florian Westphal
Quoted 7 lines · Daniel Borkmann

net/netfilter/nft_meta.c int nft_meta_get_init(const struct nft_ctx *ctx, · line 292

Quoted 5 lines
net/netfilter/nft_meta.c +5 -0
+	case NFT_META_PRANDOM:
+		if (!prand_inited) {
+			prandom_seed_full_state(&nft_prandom_state);
+			prand_inited = true;
+		}

Should this be: prandom_init_once() ?

Quoted 7 lines
Quoted 4 lines

Thought about that but this is slowpath so I considered the use of static key magic a bit overkill....

I don't mind, if you think prandom_init_once is prefereable I'll respin.

You'd have the benefit that the prng init would be race free. nft_meta_get_init() could be called in parallel from multiple CPUs, right?

We're serialized by nftables' nfnetlink mutex.

I guess I'll just send a V2 and use prandom_init_once after all.

Thanks!