* [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
* 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
* [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
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.