forked from mirrors/linux
		
	bpf, sockmap: Test shutdown() correctly exits epoll and recv()=0
When session gracefully shutdowns epoll needs to wake up and any recv() readers should return 0 not the -EAGAIN they previously returned. Note we use epoll instead of select to test the epoll wake on shutdown event as well. Signed-off-by: John Fastabend <john.fastabend@gmail.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com> Link: https://lore.kernel.org/bpf/20230523025618.113937-12-john.fastabend@gmail.com
This commit is contained in:
		
							parent
							
								
									298970c8af
								
							
						
					
					
						commit
						1fa1fe8ff1
					
				
					 2 changed files with 94 additions and 0 deletions
				
			
		|  | @ -2,6 +2,7 @@ | ||||||
| // Copyright (c) 2020 Cloudflare
 | // Copyright (c) 2020 Cloudflare
 | ||||||
| #include <error.h> | #include <error.h> | ||||||
| #include <netinet/tcp.h> | #include <netinet/tcp.h> | ||||||
|  | #include <sys/epoll.h> | ||||||
| 
 | 
 | ||||||
| #include "test_progs.h" | #include "test_progs.h" | ||||||
| #include "test_skmsg_load_helpers.skel.h" | #include "test_skmsg_load_helpers.skel.h" | ||||||
|  | @ -9,8 +10,11 @@ | ||||||
| #include "test_sockmap_invalid_update.skel.h" | #include "test_sockmap_invalid_update.skel.h" | ||||||
| #include "test_sockmap_skb_verdict_attach.skel.h" | #include "test_sockmap_skb_verdict_attach.skel.h" | ||||||
| #include "test_sockmap_progs_query.skel.h" | #include "test_sockmap_progs_query.skel.h" | ||||||
|  | #include "test_sockmap_pass_prog.skel.h" | ||||||
| #include "bpf_iter_sockmap.skel.h" | #include "bpf_iter_sockmap.skel.h" | ||||||
| 
 | 
 | ||||||
|  | #include "sockmap_helpers.h" | ||||||
|  | 
 | ||||||
| #define TCP_REPAIR		19	/* TCP sock is under repair right now */ | #define TCP_REPAIR		19	/* TCP sock is under repair right now */ | ||||||
| 
 | 
 | ||||||
| #define TCP_REPAIR_ON		1 | #define TCP_REPAIR_ON		1 | ||||||
|  | @ -350,6 +354,62 @@ static void test_sockmap_progs_query(enum bpf_attach_type attach_type) | ||||||
| 	test_sockmap_progs_query__destroy(skel); | 	test_sockmap_progs_query__destroy(skel); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define MAX_EVENTS 10 | ||||||
|  | static void test_sockmap_skb_verdict_shutdown(void) | ||||||
|  | { | ||||||
|  | 	struct epoll_event ev, events[MAX_EVENTS]; | ||||||
|  | 	int n, err, map, verdict, s, c1, p1; | ||||||
|  | 	struct test_sockmap_pass_prog *skel; | ||||||
|  | 	int epollfd; | ||||||
|  | 	int zero = 0; | ||||||
|  | 	char b; | ||||||
|  | 
 | ||||||
|  | 	skel = test_sockmap_pass_prog__open_and_load(); | ||||||
|  | 	if (!ASSERT_OK_PTR(skel, "open_and_load")) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	verdict = bpf_program__fd(skel->progs.prog_skb_verdict); | ||||||
|  | 	map = bpf_map__fd(skel->maps.sock_map_rx); | ||||||
|  | 
 | ||||||
|  | 	err = bpf_prog_attach(verdict, map, BPF_SK_SKB_STREAM_VERDICT, 0); | ||||||
|  | 	if (!ASSERT_OK(err, "bpf_prog_attach")) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	s = socket_loopback(AF_INET, SOCK_STREAM); | ||||||
|  | 	if (s < 0) | ||||||
|  | 		goto out; | ||||||
|  | 	err = create_pair(s, AF_INET, SOCK_STREAM, &c1, &p1); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		goto out_close; | ||||||
|  | 
 | ||||||
|  | 	shutdown(p1, SHUT_WR); | ||||||
|  | 
 | ||||||
|  | 	ev.events = EPOLLIN; | ||||||
|  | 	ev.data.fd = c1; | ||||||
|  | 
 | ||||||
|  | 	epollfd = epoll_create1(0); | ||||||
|  | 	if (!ASSERT_GT(epollfd, -1, "epoll_create(0)")) | ||||||
|  | 		goto out_close; | ||||||
|  | 	err = epoll_ctl(epollfd, EPOLL_CTL_ADD, c1, &ev); | ||||||
|  | 	if (!ASSERT_OK(err, "epoll_ctl(EPOLL_CTL_ADD)")) | ||||||
|  | 		goto out_close; | ||||||
|  | 	err = epoll_wait(epollfd, events, MAX_EVENTS, -1); | ||||||
|  | 	if (!ASSERT_EQ(err, 1, "epoll_wait(fd)")) | ||||||
|  | 		goto out_close; | ||||||
|  | 
 | ||||||
|  | 	n = recv(c1, &b, 1, SOCK_NONBLOCK); | ||||||
|  | 	ASSERT_EQ(n, 0, "recv_timeout(fin)"); | ||||||
|  | out_close: | ||||||
|  | 	close(c1); | ||||||
|  | 	close(p1); | ||||||
|  | out: | ||||||
|  | 	test_sockmap_pass_prog__destroy(skel); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void test_sockmap_basic(void) | void test_sockmap_basic(void) | ||||||
| { | { | ||||||
| 	if (test__start_subtest("sockmap create_update_free")) | 	if (test__start_subtest("sockmap create_update_free")) | ||||||
|  | @ -384,4 +444,6 @@ void test_sockmap_basic(void) | ||||||
| 		test_sockmap_progs_query(BPF_SK_SKB_STREAM_VERDICT); | 		test_sockmap_progs_query(BPF_SK_SKB_STREAM_VERDICT); | ||||||
| 	if (test__start_subtest("sockmap skb_verdict progs query")) | 	if (test__start_subtest("sockmap skb_verdict progs query")) | ||||||
| 		test_sockmap_progs_query(BPF_SK_SKB_VERDICT); | 		test_sockmap_progs_query(BPF_SK_SKB_VERDICT); | ||||||
|  | 	if (test__start_subtest("sockmap skb_verdict shutdown")) | ||||||
|  | 		test_sockmap_skb_verdict_shutdown(); | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										32
									
								
								tools/testing/selftests/bpf/progs/test_sockmap_pass_prog.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								tools/testing/selftests/bpf/progs/test_sockmap_pass_prog.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | #include <linux/bpf.h> | ||||||
|  | #include <bpf/bpf_helpers.h> | ||||||
|  | #include <bpf/bpf_endian.h> | ||||||
|  | 
 | ||||||
|  | struct { | ||||||
|  | 	__uint(type, BPF_MAP_TYPE_SOCKMAP); | ||||||
|  | 	__uint(max_entries, 20); | ||||||
|  | 	__type(key, int); | ||||||
|  | 	__type(value, int); | ||||||
|  | } sock_map_rx SEC(".maps"); | ||||||
|  | 
 | ||||||
|  | struct { | ||||||
|  | 	__uint(type, BPF_MAP_TYPE_SOCKMAP); | ||||||
|  | 	__uint(max_entries, 20); | ||||||
|  | 	__type(key, int); | ||||||
|  | 	__type(value, int); | ||||||
|  | } sock_map_tx SEC(".maps"); | ||||||
|  | 
 | ||||||
|  | struct { | ||||||
|  | 	__uint(type, BPF_MAP_TYPE_SOCKMAP); | ||||||
|  | 	__uint(max_entries, 20); | ||||||
|  | 	__type(key, int); | ||||||
|  | 	__type(value, int); | ||||||
|  | } sock_map_msg SEC(".maps"); | ||||||
|  | 
 | ||||||
|  | SEC("sk_skb") | ||||||
|  | int prog_skb_verdict(struct __sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	return SK_PASS; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | char _license[] SEC("license") = "GPL"; | ||||||
		Loading…
	
		Reference in a new issue
	
	 John Fastabend
						John Fastabend