All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC nf-next v2 0/2] netfilter: bpf: support prog update
@ 2023-12-18  4:18 D. Wythe
  2023-12-18  4:18 ` [RFC nf-next v2 1/2] " D. Wythe
  2023-12-18  4:18 ` [RFC nf-next v2 2/2] selftests/bpf: Add netfilter link prog update test D. Wythe
  0 siblings, 2 replies; 7+ messages in thread
From: D. Wythe @ 2023-12-18  4:18 UTC (permalink / raw)
  To: pablo, kadlec, fw
  Cc: bpf, linux-kernel, netdev, coreteam, netfilter-devel, davem,
	edumazet, kuba, pabeni, ast

From: "D. Wythe" <alibuda@linux.alibaba.com>

This patches attempt to implements updating of progs within
bpf netfilter link, allowing user update their ebpf netfilter
prog in hot update manner.

Besides, a corresponding test case has been added to verify
whether the update works.

--
v1:
1. remove unnecessary context, access the prog directly via rcu.
2. remove synchronize_rcu(), dealloc the nf_link via kfree_rcu.
3. check the dead flag during the update.
--
v1->v2:
1. remove unnecessary nf_prog, accessing nf_link->link.prog in direct.

D. Wythe (2):
  netfilter: bpf: support prog update
  selftests/bpf: Add netfilter link prog update test

 net/netfilter/nf_bpf_link.c                        | 63 ++++++++++++----
 .../bpf/prog_tests/netfilter_link_update_prog.c    | 83 ++++++++++++++++++++++
 .../bpf/progs/test_netfilter_link_update_prog.c    | 24 +++++++
 3 files changed, 155 insertions(+), 15 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/netfilter_link_update_prog.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_netfilter_link_update_prog.c

-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC nf-next v2 1/2] netfilter: bpf: support prog update
  2023-12-18  4:18 [RFC nf-next v2 0/2] netfilter: bpf: support prog update D. Wythe
@ 2023-12-18  4:18 ` D. Wythe
  2023-12-18 19:06   ` Simon Horman
  2023-12-18  4:18 ` [RFC nf-next v2 2/2] selftests/bpf: Add netfilter link prog update test D. Wythe
  1 sibling, 1 reply; 7+ messages in thread
From: D. Wythe @ 2023-12-18  4:18 UTC (permalink / raw)
  To: pablo, kadlec, fw
  Cc: bpf, linux-kernel, netdev, coreteam, netfilter-devel, davem,
	edumazet, kuba, pabeni, ast

From: "D. Wythe" <alibuda@linux.alibaba.com>

To support the prog update, we need to ensure that the prog seen
within the hook is always valid. Considering that hooks are always
protected by rcu_read_lock(), which provide us the ability to
access the prog under rcu.

Signed-off-by: D. Wythe <alibuda@linux.alibaba.com>
---
 net/netfilter/nf_bpf_link.c | 63 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 48 insertions(+), 15 deletions(-)

diff --git a/net/netfilter/nf_bpf_link.c b/net/netfilter/nf_bpf_link.c
index e502ec0..8eed7cf 100644
--- a/net/netfilter/nf_bpf_link.c
+++ b/net/netfilter/nf_bpf_link.c
@@ -8,17 +8,8 @@
 #include <net/netfilter/nf_bpf_link.h>
 #include <uapi/linux/netfilter_ipv4.h>
 
-static unsigned int nf_hook_run_bpf(void *bpf_prog, struct sk_buff *skb,
-				    const struct nf_hook_state *s)
-{
-	const struct bpf_prog *prog = bpf_prog;
-	struct bpf_nf_ctx ctx = {
-		.state = s,
-		.skb = skb,
-	};
-
-	return bpf_prog_run(prog, &ctx);
-}
+/* protect link update in parallel */
+static DEFINE_MUTEX(bpf_nf_mutex);
 
 struct bpf_nf_link {
 	struct bpf_link link;
@@ -26,8 +17,20 @@ struct bpf_nf_link {
 	struct net *net;
 	u32 dead;
 	const struct nf_defrag_hook *defrag_hook;
+	struct rcu_head head;
 };
 
+static unsigned int nf_hook_run_bpf(void *bpf_link, struct sk_buff *skb,
+				    const struct nf_hook_state *s)
+{
+	const struct bpf_nf_link *nf_link = bpf_link;
+	struct bpf_nf_ctx ctx = {
+		.state = s,
+		.skb = skb,
+	};
+	return bpf_prog_run(rcu_dereference(nf_link->link.prog), &ctx);
+}
+
 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) || IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
 static const struct nf_defrag_hook *
 get_proto_defrag_hook(struct bpf_nf_link *link,
@@ -126,8 +129,7 @@ static void bpf_nf_link_release(struct bpf_link *link)
 static void bpf_nf_link_dealloc(struct bpf_link *link)
 {
 	struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link);
-
-	kfree(nf_link);
+	kfree_rcu(nf_link, head);
 }
 
 static int bpf_nf_link_detach(struct bpf_link *link)
@@ -162,7 +164,34 @@ static int bpf_nf_link_fill_link_info(const struct bpf_link *link,
 static int bpf_nf_link_update(struct bpf_link *link, struct bpf_prog *new_prog,
 			      struct bpf_prog *old_prog)
 {
-	return -EOPNOTSUPP;
+	struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link);
+	int err = 0;
+
+	mutex_lock(&bpf_nf_mutex);
+
+	if (nf_link->dead) {
+		err = -EPERM;
+		goto out;
+	}
+
+	/* target old_prog mismatch */
+	if (old_prog && link->prog != old_prog) {
+		err = -EPERM;
+		goto out;
+	}
+
+	old_prog = link->prog;
+	if (old_prog == new_prog) {
+		/* don't need update */
+		bpf_prog_put(new_prog);
+		goto out;
+	}
+
+	old_prog = xchg(&link->prog, new_prog);
+	bpf_prog_put(old_prog);
+out:
+	mutex_unlock(&bpf_nf_mutex);
+	return err;
 }
 
 static const struct bpf_link_ops bpf_nf_link_lops = {
@@ -226,7 +255,11 @@ int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
 
 	link->hook_ops.hook = nf_hook_run_bpf;
 	link->hook_ops.hook_ops_type = NF_HOOK_OP_BPF;
-	link->hook_ops.priv = prog;
+
+	/* bpf_nf_link_release & bpf_nf_link_dealloc() can ensures that link remains
+	 * valid at all times within nf_hook_run_bpf().
+	 */
+	link->hook_ops.priv = link;
 
 	link->hook_ops.pf = attr->link_create.netfilter.pf;
 	link->hook_ops.priority = attr->link_create.netfilter.priority;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC nf-next v2 2/2] selftests/bpf: Add netfilter link prog update test
  2023-12-18  4:18 [RFC nf-next v2 0/2] netfilter: bpf: support prog update D. Wythe
  2023-12-18  4:18 ` [RFC nf-next v2 1/2] " D. Wythe
@ 2023-12-18  4:18 ` D. Wythe
  1 sibling, 0 replies; 7+ messages in thread
From: D. Wythe @ 2023-12-18  4:18 UTC (permalink / raw)
  To: pablo, kadlec, fw
  Cc: bpf, linux-kernel, netdev, coreteam, netfilter-devel, davem,
	edumazet, kuba, pabeni, ast

From: "D. Wythe" <alibuda@linux.alibaba.com>

Update prog for active links and verify whether
the prog has been successfully replaced.

Expected output:

./test_progs -t netfilter_link_update_prog
Summary: 1/0 PASSED, 0 SKIPPED, 0 FAILED

Signed-off-by: D. Wythe <alibuda@linux.alibaba.com>
---
 .../bpf/prog_tests/netfilter_link_update_prog.c    | 83 ++++++++++++++++++++++
 .../bpf/progs/test_netfilter_link_update_prog.c    | 24 +++++++
 2 files changed, 107 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/netfilter_link_update_prog.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_netfilter_link_update_prog.c

diff --git a/tools/testing/selftests/bpf/prog_tests/netfilter_link_update_prog.c b/tools/testing/selftests/bpf/prog_tests/netfilter_link_update_prog.c
new file mode 100644
index 00000000..d23b544
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/netfilter_link_update_prog.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <test_progs.h>
+#include <linux/netfilter.h>
+#include <network_helpers.h>
+#include "test_netfilter_link_update_prog.skel.h"
+
+#define SERVER_ADDR "127.0.0.1"
+#define SERVER_PORT 12345
+
+static const char dummy_message[] = "A dummy message";
+
+static int send_dummy(int client_fd)
+{
+	struct sockaddr_storage saddr;
+	struct sockaddr *saddr_p;
+	socklen_t saddr_len;
+	int err;
+
+	saddr_p = (struct sockaddr *)&saddr;
+	err = make_sockaddr(AF_INET, SERVER_ADDR, SERVER_PORT, &saddr, &saddr_len);
+	if (!ASSERT_OK(err, "make_sockaddr"))
+		return -1;
+
+	err = sendto(client_fd, dummy_message, sizeof(dummy_message) - 1, 0, saddr_p, saddr_len);
+	if (!ASSERT_GE(err, 0, "sendto"))
+		return -1;
+
+	return 0;
+}
+
+void test_netfilter_link_update_prog(void)
+{
+	LIBBPF_OPTS(bpf_netfilter_opts, opts,
+		.pf = NFPROTO_IPV4,
+		.hooknum = NF_INET_LOCAL_OUT,
+		.priority = 100);
+	struct test_netfilter_link_update_prog *skel;
+	struct bpf_program *prog;
+	int server_fd, client_fd;
+	int err;
+
+	skel = test_netfilter_link_update_prog__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "test_netfilter_link_update_prog__open_and_load"))
+		goto out;
+
+	prog = skel->progs.nf_link_prog;
+
+	if (!ASSERT_OK_PTR(prog, "load program"))
+		goto out;
+
+	skel->links.nf_link_prog = bpf_program__attach_netfilter(prog, &opts);
+	if (!ASSERT_OK_PTR(skel->links.nf_link_prog, "attach netfilter program"))
+		goto out;
+
+	server_fd = start_server(AF_INET, SOCK_DGRAM, SERVER_ADDR, SERVER_PORT, 0);
+	if (!ASSERT_GE(server_fd, 0, "start_server"))
+		goto out;
+
+	client_fd = connect_to_fd(server_fd, 0);
+	if (!ASSERT_GE(client_fd, 0, "connect_to_fd"))
+		goto out;
+
+	send_dummy(client_fd);
+
+	ASSERT_EQ(skel->bss->counter, 0, "counter should be zero");
+
+	err = bpf_link__update_program(skel->links.nf_link_prog, skel->progs.nf_link_prog_new);
+	if (!ASSERT_OK(err, "bpf_link__update_program"))
+		goto out;
+
+	send_dummy(client_fd);
+	ASSERT_GE(skel->bss->counter, 0, "counter should be greater than zero");
+out:
+	if (client_fd > 0)
+		close(client_fd);
+	if (server_fd > 0)
+		close(server_fd);
+
+	test_netfilter_link_update_prog__destroy(skel);
+}
+
+
diff --git a/tools/testing/selftests/bpf/progs/test_netfilter_link_update_prog.c b/tools/testing/selftests/bpf/progs/test_netfilter_link_update_prog.c
new file mode 100644
index 00000000..42ae332
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_netfilter_link_update_prog.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+
+#define NF_ACCEPT 1
+
+SEC("netfilter")
+int nf_link_prog(struct bpf_nf_ctx *ctx)
+{
+	return NF_ACCEPT;
+}
+
+u64 counter = 0;
+
+SEC("netfilter")
+int nf_link_prog_new(struct bpf_nf_ctx *ctx)
+{
+	counter++;
+	return NF_ACCEPT;
+}
+
+char _license[] SEC("license") = "GPL";
+
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [RFC nf-next v2 1/2] netfilter: bpf: support prog update
  2023-12-18  4:18 ` [RFC nf-next v2 1/2] " D. Wythe
@ 2023-12-18 19:06   ` Simon Horman
  2023-12-19 12:50     ` D. Wythe
  0 siblings, 1 reply; 7+ messages in thread
From: Simon Horman @ 2023-12-18 19:06 UTC (permalink / raw)
  To: D. Wythe
  Cc: pablo, kadlec, fw, bpf, linux-kernel, netdev, coreteam,
	netfilter-devel, davem, edumazet, kuba, pabeni, ast

On Mon, Dec 18, 2023 at 12:18:20PM +0800, D. Wythe wrote:
> From: "D. Wythe" <alibuda@linux.alibaba.com>
> 
> To support the prog update, we need to ensure that the prog seen
> within the hook is always valid. Considering that hooks are always
> protected by rcu_read_lock(), which provide us the ability to
> access the prog under rcu.
> 
> Signed-off-by: D. Wythe <alibuda@linux.alibaba.com>

...

> @@ -26,8 +17,20 @@ struct bpf_nf_link {
>  	struct net *net;
>  	u32 dead;
>  	const struct nf_defrag_hook *defrag_hook;
> +	struct rcu_head head;
>  };
>  
> +static unsigned int nf_hook_run_bpf(void *bpf_link, struct sk_buff *skb,
> +				    const struct nf_hook_state *s)
> +{
> +	const struct bpf_nf_link *nf_link = bpf_link;
> +	struct bpf_nf_ctx ctx = {
> +		.state = s,
> +		.skb = skb,
> +	};
> +	return bpf_prog_run(rcu_dereference(nf_link->link.prog), &ctx);

Hi,

AFAICT nf_link->link.prog isn't annotated as __rcu,
so perhaps rcu_dereference() is not correct here?

In any case, sparse seems a bit unhappy:

  .../nf_bpf_link.c:31:29: error: incompatible types in comparison expression (different address spaces):
  .../nf_bpf_link.c:31:29:    struct bpf_prog [noderef] __rcu *
  .../nf_bpf_link.c:31:29:    struct bpf_prog *

> +}
> +
>  #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) || IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
>  static const struct nf_defrag_hook *
>  get_proto_defrag_hook(struct bpf_nf_link *link,

...

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC nf-next v2 1/2] netfilter: bpf: support prog update
  2023-12-18 19:06   ` Simon Horman
@ 2023-12-19 12:50     ` D. Wythe
  2023-12-19 14:58       ` Florian Westphal
  0 siblings, 1 reply; 7+ messages in thread
From: D. Wythe @ 2023-12-19 12:50 UTC (permalink / raw)
  To: Simon Horman
  Cc: pablo, kadlec, fw, bpf, linux-kernel, netdev, coreteam,
	netfilter-devel, davem, edumazet, kuba, pabeni, ast



On 12/19/23 3:06 AM, Simon Horman wrote:
> On Mon, Dec 18, 2023 at 12:18:20PM +0800, D. Wythe wrote:
>> From: "D. Wythe" <alibuda@linux.alibaba.com>
>>
>> To support the prog update, we need to ensure that the prog seen
>> within the hook is always valid. Considering that hooks are always
>> protected by rcu_read_lock(), which provide us the ability to
>> access the prog under rcu.
>>
>> Signed-off-by: D. Wythe <alibuda@linux.alibaba.com>
> ...
>
>> @@ -26,8 +17,20 @@ struct bpf_nf_link {
>>   	struct net *net;
>>   	u32 dead;
>>   	const struct nf_defrag_hook *defrag_hook;
>> +	struct rcu_head head;
>>   };
>>   
>> +static unsigned int nf_hook_run_bpf(void *bpf_link, struct sk_buff *skb,
>> +				    const struct nf_hook_state *s)
>> +{
>> +	const struct bpf_nf_link *nf_link = bpf_link;
>> +	struct bpf_nf_ctx ctx = {
>> +		.state = s,
>> +		.skb = skb,
>> +	};
>> +	return bpf_prog_run(rcu_dereference(nf_link->link.prog), &ctx);
> Hi,
>
> AFAICT nf_link->link.prog isn't annotated as __rcu,
> so perhaps rcu_dereference() is not correct here?
>
> In any case, sparse seems a bit unhappy:
>
>    .../nf_bpf_link.c:31:29: error: incompatible types in comparison expression (different address spaces):
>    .../nf_bpf_link.c:31:29:    struct bpf_prog [noderef] __rcu *
>    .../nf_bpf_link.c:31:29:    struct bpf_prog *

Hi Simon,

thanks for the reporting.

Yes, I had anticipated that sparse would report an error. I tried to 
cast the type,
but it would compile an error likes that:


net/netfilter/nf_bpf_link.c: In function ‘nf_hook_run_bpf’:
./include/asm-generic/rwonce.h:44:70: error: lvalue required as unary 
‘&’ operand
    44 | #define __READ_ONCE(x) (*(const volatile 
__unqual_scalar_typeof(x) *)&(x))
| ^
./include/asm-generic/rwonce.h:50:2: note: in expansion of macro 
‘__READ_ONCE’
    50 |  __READ_ONCE(x);       \
       |  ^~~~~~~~~~~
./include/linux/rcupdate.h:436:43: note: in expansion of macro ‘READ_ONCE’
   436 |  typeof(*p) *local = (typeof(*p) *__force)READ_ONCE(p); \
       |                                           ^~~~~~~~~
./include/linux/rcupdate.h:584:2: note: in expansion of macro 
‘__rcu_dereference_check’
   584 |  __rcu_dereference_check((p), __UNIQUE_ID(rcu), \
       |  ^~~~~~~~~~~~~~~~~~~~~~~
./include/linux/rcupdate.h:656:28: note: in expansion of macro 
‘rcu_dereference_check’
   656 | #define rcu_dereference(p) rcu_dereference_check(p, 0)
       |                            ^~~~~~~~~~~~~~~~~~~~~
net/netfilter/nf_bpf_link.c:31:22: note: in expansion of macro 
‘rcu_dereference’
    31 |  return bpf_prog_run(rcu_dereference((const struct bpf_prog 
__rcu *)nf_link->link.prog), &ctx);
       |                      ^~~~~~~~~~~~~~~

So, I think we might need to go back to version 1.

@ Florian , what do you think ?

D. Wythe

>> +}
>> +
>>   #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) || IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
>>   static const struct nf_defrag_hook *
>>   get_proto_defrag_hook(struct bpf_nf_link *link,
> ...


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC nf-next v2 1/2] netfilter: bpf: support prog update
  2023-12-19 12:50     ` D. Wythe
@ 2023-12-19 14:58       ` Florian Westphal
  2023-12-20 12:40         ` D. Wythe
  0 siblings, 1 reply; 7+ messages in thread
From: Florian Westphal @ 2023-12-19 14:58 UTC (permalink / raw)
  To: D. Wythe
  Cc: Simon Horman, pablo, kadlec, fw, bpf, linux-kernel, netdev,
	coreteam, netfilter-devel, davem, edumazet, kuba, pabeni, ast

D. Wythe <alibuda@linux.alibaba.com> wrote:
> net/netfilter/nf_bpf_link.c:31:22: note: in expansion of macro
> ‘rcu_dereference’
>    31 |  return bpf_prog_run(rcu_dereference((const struct bpf_prog __rcu
> *)nf_link->link.prog), &ctx);
>       |                      ^~~~~~~~~~~~~~~
> 
> So, I think we might need to go back to version 1.
> 
> @ Florian , what do you think ?

Use rcu_dereference_raw().

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC nf-next v2 1/2] netfilter: bpf: support prog update
  2023-12-19 14:58       ` Florian Westphal
@ 2023-12-20 12:40         ` D. Wythe
  0 siblings, 0 replies; 7+ messages in thread
From: D. Wythe @ 2023-12-20 12:40 UTC (permalink / raw)
  To: Florian Westphal
  Cc: Simon Horman, pablo, kadlec, bpf, linux-kernel, netdev, coreteam,
	netfilter-devel, davem, edumazet, kuba, pabeni, ast



On 12/19/23 10:58 PM, Florian Westphal wrote:
> D. Wythe <alibuda@linux.alibaba.com> wrote:
>> net/netfilter/nf_bpf_link.c:31:22: note: in expansion of macro
>> ‘rcu_dereference’
>>     31 |  return bpf_prog_run(rcu_dereference((const struct bpf_prog __rcu
>> *)nf_link->link.prog), &ctx);
>>        |                      ^~~~~~~~~~~~~~~
>>
>> So, I think we might need to go back to version 1.
>>
>> @ Florian , what do you think ?
> Use rcu_dereference_raw().

Got it. I'm also good with that.

D. Wythe

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-12-20 12:40 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-12-18  4:18 [RFC nf-next v2 0/2] netfilter: bpf: support prog update D. Wythe
2023-12-18  4:18 ` [RFC nf-next v2 1/2] " D. Wythe
2023-12-18 19:06   ` Simon Horman
2023-12-19 12:50     ` D. Wythe
2023-12-19 14:58       ` Florian Westphal
2023-12-20 12:40         ` D. Wythe
2023-12-18  4:18 ` [RFC nf-next v2 2/2] selftests/bpf: Add netfilter link prog update test D. Wythe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.