Skip to content

Commit f126f0c

Browse files
jrifeMartin KaFai Lau
authored andcommitted
selftests/bpf: Add tests for bucket resume logic in established sockets
Replicate the set of test cases used for UDP socket iterators to test similar scenarios for TCP established sockets. Signed-off-by: Jordan Rife <[email protected]> Signed-off-by: Martin KaFai Lau <[email protected]> Acked-by: Stanislav Fomichev <[email protected]>
1 parent 8fc0c5a commit f126f0c

File tree

1 file changed

+293
-0
lines changed

1 file changed

+293
-0
lines changed

tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,45 @@ static int get_nth_socket(int *fds, int fds_len, struct bpf_link *link, int n)
120120
return nth_sock_idx;
121121
}
122122

123+
static void destroy(int fd)
124+
{
125+
struct sock_iter_batch *skel = NULL;
126+
__u64 cookie = socket_cookie(fd);
127+
struct bpf_link *link = NULL;
128+
int iter_fd = -1;
129+
int nread;
130+
__u64 out;
131+
132+
skel = sock_iter_batch__open();
133+
if (!ASSERT_OK_PTR(skel, "sock_iter_batch__open"))
134+
goto done;
135+
136+
skel->rodata->destroy_cookie = cookie;
137+
138+
if (!ASSERT_OK(sock_iter_batch__load(skel), "sock_iter_batch__load"))
139+
goto done;
140+
141+
link = bpf_program__attach_iter(skel->progs.iter_tcp_destroy, NULL);
142+
if (!ASSERT_OK_PTR(link, "bpf_program__attach_iter"))
143+
goto done;
144+
145+
iter_fd = bpf_iter_create(bpf_link__fd(link));
146+
if (!ASSERT_OK_FD(iter_fd, "bpf_iter_create"))
147+
goto done;
148+
149+
/* Delete matching socket. */
150+
nread = read(iter_fd, &out, sizeof(out));
151+
ASSERT_GE(nread, 0, "nread");
152+
if (nread)
153+
ASSERT_EQ(out, cookie, "cookie matches");
154+
done:
155+
if (iter_fd >= 0)
156+
close(iter_fd);
157+
bpf_link__destroy(link);
158+
sock_iter_batch__destroy(skel);
159+
close(fd);
160+
}
161+
123162
static int get_seen_count(int fd, struct sock_count counts[], int n)
124163
{
125164
__u64 cookie = socket_cookie(fd);
@@ -247,6 +286,43 @@ static void remove_seen(int family, int sock_type, const char *addr, __u16 port,
247286
counts_len);
248287
}
249288

289+
static void remove_seen_established(int family, int sock_type, const char *addr,
290+
__u16 port, int *listen_socks,
291+
int listen_socks_len, int *established_socks,
292+
int established_socks_len,
293+
struct sock_count *counts, int counts_len,
294+
struct bpf_link *link, int iter_fd)
295+
{
296+
int close_idx;
297+
298+
/* Iterate through all listening sockets. */
299+
read_n(iter_fd, listen_socks_len, counts, counts_len);
300+
301+
/* Make sure we saw all listening sockets exactly once. */
302+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
303+
counts, counts_len);
304+
305+
/* Leave one established socket. */
306+
read_n(iter_fd, established_socks_len - 1, counts, counts_len);
307+
308+
/* Close a socket we've already seen to remove it from the bucket. */
309+
close_idx = get_nth_socket(established_socks, established_socks_len,
310+
link, listen_socks_len + 1);
311+
if (!ASSERT_GE(close_idx, 0, "close_idx"))
312+
return;
313+
destroy(established_socks[close_idx]);
314+
established_socks[close_idx] = -1;
315+
316+
/* Iterate through the rest of the sockets. */
317+
read_n(iter_fd, -1, counts, counts_len);
318+
319+
/* Make sure the last socket wasn't skipped and that there were no
320+
* repeats.
321+
*/
322+
check_n_were_seen_once(established_socks, established_socks_len,
323+
established_socks_len - 1, counts, counts_len);
324+
}
325+
250326
static void remove_unseen(int family, int sock_type, const char *addr,
251327
__u16 port, int *socks, int socks_len,
252328
int *established_socks, int established_socks_len,
@@ -280,6 +356,51 @@ static void remove_unseen(int family, int sock_type, const char *addr,
280356
counts_len);
281357
}
282358

359+
static void remove_unseen_established(int family, int sock_type,
360+
const char *addr, __u16 port,
361+
int *listen_socks, int listen_socks_len,
362+
int *established_socks,
363+
int established_socks_len,
364+
struct sock_count *counts, int counts_len,
365+
struct bpf_link *link, int iter_fd)
366+
{
367+
int close_idx;
368+
369+
/* Iterate through all listening sockets. */
370+
read_n(iter_fd, listen_socks_len, counts, counts_len);
371+
372+
/* Make sure we saw all listening sockets exactly once. */
373+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
374+
counts, counts_len);
375+
376+
/* Iterate through the first established socket. */
377+
read_n(iter_fd, 1, counts, counts_len);
378+
379+
/* Make sure we saw one established socks. */
380+
check_n_were_seen_once(established_socks, established_socks_len, 1,
381+
counts, counts_len);
382+
383+
/* Close what would be the next socket in the bucket to exercise the
384+
* condition where we need to skip past the first cookie we remembered.
385+
*/
386+
close_idx = get_nth_socket(established_socks, established_socks_len,
387+
link, listen_socks_len + 1);
388+
if (!ASSERT_GE(close_idx, 0, "close_idx"))
389+
return;
390+
391+
destroy(established_socks[close_idx]);
392+
established_socks[close_idx] = -1;
393+
394+
/* Iterate through the rest of the sockets. */
395+
read_n(iter_fd, -1, counts, counts_len);
396+
397+
/* Make sure the remaining sockets were seen exactly once and that we
398+
* didn't repeat the socket that was already seen.
399+
*/
400+
check_n_were_seen_once(established_socks, established_socks_len,
401+
established_socks_len - 1, counts, counts_len);
402+
}
403+
283404
static void remove_all(int family, int sock_type, const char *addr,
284405
__u16 port, int *socks, int socks_len,
285406
int *established_socks, int established_socks_len,
@@ -309,6 +430,54 @@ static void remove_all(int family, int sock_type, const char *addr,
309430
ASSERT_EQ(read_n(iter_fd, -1, counts, counts_len), 0, "read_n");
310431
}
311432

433+
static void remove_all_established(int family, int sock_type, const char *addr,
434+
__u16 port, int *listen_socks,
435+
int listen_socks_len, int *established_socks,
436+
int established_socks_len,
437+
struct sock_count *counts, int counts_len,
438+
struct bpf_link *link, int iter_fd)
439+
{
440+
int *close_idx = NULL;
441+
int i;
442+
443+
/* Iterate through all listening sockets. */
444+
read_n(iter_fd, listen_socks_len, counts, counts_len);
445+
446+
/* Make sure we saw all listening sockets exactly once. */
447+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
448+
counts, counts_len);
449+
450+
/* Iterate through the first established socket. */
451+
read_n(iter_fd, 1, counts, counts_len);
452+
453+
/* Make sure we saw one established socks. */
454+
check_n_were_seen_once(established_socks, established_socks_len, 1,
455+
counts, counts_len);
456+
457+
/* Close all remaining sockets to exhaust the list of saved cookies and
458+
* exit without putting any sockets into the batch on the next read.
459+
*/
460+
close_idx = malloc(sizeof(int) * (established_socks_len - 1));
461+
if (!ASSERT_OK_PTR(close_idx, "close_idx malloc"))
462+
return;
463+
for (i = 0; i < established_socks_len - 1; i++) {
464+
close_idx[i] = get_nth_socket(established_socks,
465+
established_socks_len, link,
466+
listen_socks_len + i);
467+
if (!ASSERT_GE(close_idx[i], 0, "close_idx"))
468+
return;
469+
}
470+
471+
for (i = 0; i < established_socks_len - 1; i++) {
472+
destroy(established_socks[close_idx[i]]);
473+
established_socks[close_idx[i]] = -1;
474+
}
475+
476+
/* Make sure there are no more sockets returned */
477+
ASSERT_EQ(read_n(iter_fd, -1, counts, counts_len), 0, "read_n");
478+
free(close_idx);
479+
}
480+
312481
static void add_some(int family, int sock_type, const char *addr, __u16 port,
313482
int *socks, int socks_len, int *established_socks,
314483
int established_socks_len, struct sock_count *counts,
@@ -339,6 +508,49 @@ static void add_some(int family, int sock_type, const char *addr, __u16 port,
339508
free_fds(new_socks, socks_len);
340509
}
341510

511+
static void add_some_established(int family, int sock_type, const char *addr,
512+
__u16 port, int *listen_socks,
513+
int listen_socks_len, int *established_socks,
514+
int established_socks_len,
515+
struct sock_count *counts,
516+
int counts_len, struct bpf_link *link,
517+
int iter_fd)
518+
{
519+
int *new_socks = NULL;
520+
521+
/* Iterate through all listening sockets. */
522+
read_n(iter_fd, listen_socks_len, counts, counts_len);
523+
524+
/* Make sure we saw all listening sockets exactly once. */
525+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
526+
counts, counts_len);
527+
528+
/* Iterate through the first established_socks_len - 1 sockets. */
529+
read_n(iter_fd, established_socks_len - 1, counts, counts_len);
530+
531+
/* Make sure we saw established_socks_len - 1 sockets exactly once. */
532+
check_n_were_seen_once(established_socks, established_socks_len,
533+
established_socks_len - 1, counts, counts_len);
534+
535+
/* Double the number of established sockets in the bucket. */
536+
new_socks = connect_to_server(family, sock_type, addr, port,
537+
established_socks_len / 2, listen_socks,
538+
listen_socks_len);
539+
if (!ASSERT_OK_PTR(new_socks, "connect_to_server"))
540+
goto done;
541+
542+
/* Iterate through the rest of the sockets. */
543+
read_n(iter_fd, -1, counts, counts_len);
544+
545+
/* Make sure each of the original sockets was seen exactly once. */
546+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
547+
counts, counts_len);
548+
check_n_were_seen_once(established_socks, established_socks_len,
549+
established_socks_len, counts, counts_len);
550+
done:
551+
free_fds(new_socks, established_socks_len);
552+
}
553+
342554
static void force_realloc(int family, int sock_type, const char *addr,
343555
__u16 port, int *socks, int socks_len,
344556
int *established_socks, int established_socks_len,
@@ -368,6 +580,24 @@ static void force_realloc(int family, int sock_type, const char *addr,
368580
free_fds(new_socks, socks_len);
369581
}
370582

583+
static void force_realloc_established(int family, int sock_type,
584+
const char *addr, __u16 port,
585+
int *listen_socks, int listen_socks_len,
586+
int *established_socks,
587+
int established_socks_len,
588+
struct sock_count *counts, int counts_len,
589+
struct bpf_link *link, int iter_fd)
590+
{
591+
/* Iterate through all sockets to trigger a realloc. */
592+
read_n(iter_fd, -1, counts, counts_len);
593+
594+
/* Make sure each socket was seen exactly once. */
595+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
596+
counts, counts_len);
597+
check_n_were_seen_once(established_socks, established_socks_len,
598+
established_socks_len, counts, counts_len);
599+
}
600+
371601
struct test_case {
372602
void (*test)(int family, int sock_type, const char *addr, __u16 port,
373603
int *socks, int socks_len, int *established_socks,
@@ -477,6 +707,69 @@ static struct test_case resume_tests[] = {
477707
.family = AF_INET6,
478708
.test = force_realloc,
479709
},
710+
{
711+
.description = "tcp: resume after removing a seen socket (established)",
712+
/* Force all established sockets into one bucket */
713+
.ehash_buckets = 1,
714+
.connections = nr_soreuse,
715+
.init_socks = nr_soreuse,
716+
/* Room for connect()ed and accept()ed sockets */
717+
.max_socks = nr_soreuse * 3,
718+
.sock_type = SOCK_STREAM,
719+
.family = AF_INET6,
720+
.test = remove_seen_established,
721+
},
722+
{
723+
.description = "tcp: resume after removing one unseen socket (established)",
724+
/* Force all established sockets into one bucket */
725+
.ehash_buckets = 1,
726+
.connections = nr_soreuse,
727+
.init_socks = nr_soreuse,
728+
/* Room for connect()ed and accept()ed sockets */
729+
.max_socks = nr_soreuse * 3,
730+
.sock_type = SOCK_STREAM,
731+
.family = AF_INET6,
732+
.test = remove_unseen_established,
733+
},
734+
{
735+
.description = "tcp: resume after removing all unseen sockets (established)",
736+
/* Force all established sockets into one bucket */
737+
.ehash_buckets = 1,
738+
.connections = nr_soreuse,
739+
.init_socks = nr_soreuse,
740+
/* Room for connect()ed and accept()ed sockets */
741+
.max_socks = nr_soreuse * 3,
742+
.sock_type = SOCK_STREAM,
743+
.family = AF_INET6,
744+
.test = remove_all_established,
745+
},
746+
{
747+
.description = "tcp: resume after adding a few sockets (established)",
748+
/* Force all established sockets into one bucket */
749+
.ehash_buckets = 1,
750+
.connections = nr_soreuse,
751+
.init_socks = nr_soreuse,
752+
/* Room for connect()ed and accept()ed sockets */
753+
.max_socks = nr_soreuse * 3,
754+
.sock_type = SOCK_STREAM,
755+
.family = AF_INET6,
756+
.test = add_some_established,
757+
},
758+
{
759+
.description = "tcp: force a realloc to occur (established)",
760+
/* Force all established sockets into one bucket */
761+
.ehash_buckets = 1,
762+
/* Bucket size will need to double when going from listening to
763+
* established sockets.
764+
*/
765+
.connections = init_batch_size,
766+
.init_socks = nr_soreuse,
767+
/* Room for connect()ed and accept()ed sockets */
768+
.max_socks = nr_soreuse + (init_batch_size * 2),
769+
.sock_type = SOCK_STREAM,
770+
.family = AF_INET6,
771+
.test = force_realloc_established,
772+
},
480773
};
481774

482775
static void do_resume_test(struct test_case *tc)

0 commit comments

Comments
 (0)