Skip to content

Commit 2b9823d

Browse files
committed
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]>
1 parent bb3f120 commit 2b9823d

File tree

1 file changed

+292
-0
lines changed

1 file changed

+292
-0
lines changed

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

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

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

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

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

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

504+
static void add_some_established(int family, int sock_type, const char *addr,
505+
__u16 port, int *listen_socks,
506+
int listen_socks_len, int *established_socks,
507+
int established_socks_len,
508+
struct sock_count *counts,
509+
int counts_len, struct bpf_link *link,
510+
int iter_fd)
511+
{
512+
int *new_socks = NULL;
513+
514+
/* Iterate through all listening sockets. */
515+
read_n(iter_fd, listen_socks_len, counts, counts_len);
516+
517+
/* Make sure we saw all listening sockets exactly once. */
518+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
519+
counts, counts_len);
520+
521+
/* Iterate through the first established_socks_len - 1 sockets. */
522+
read_n(iter_fd, established_socks_len - 1, counts, counts_len);
523+
524+
/* Make sure we saw established_socks_len - 1 sockets exactly once. */
525+
check_n_were_seen_once(established_socks, established_socks_len,
526+
established_socks_len - 1, counts, counts_len);
527+
528+
/* Double the number of established sockets in the bucket. */
529+
new_socks = connect_to_server(family, sock_type, addr, port,
530+
established_socks_len / 2, listen_socks,
531+
listen_socks_len);
532+
if (!ASSERT_OK_PTR(new_socks, "connect_to_server"))
533+
goto done;
534+
535+
/* Iterate through the rest of the sockets. */
536+
read_n(iter_fd, -1, counts, counts_len);
537+
538+
/* Make sure each of the original sockets was seen exactly once. */
539+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
540+
counts, counts_len);
541+
check_n_were_seen_once(established_socks, established_socks_len,
542+
established_socks_len, counts, counts_len);
543+
done:
544+
free_fds(new_socks, established_socks_len);
545+
}
546+
336547
static void force_realloc(int family, int sock_type, const char *addr,
337548
__u16 port, int *socks, int socks_len,
338549
int *established_socks, int established_socks_len,
@@ -362,6 +573,24 @@ static void force_realloc(int family, int sock_type, const char *addr,
362573
free_fds(new_socks, socks_len);
363574
}
364575

576+
static void force_realloc_established(int family, int sock_type,
577+
const char *addr, __u16 port,
578+
int *listen_socks, int listen_socks_len,
579+
int *established_socks,
580+
int established_socks_len,
581+
struct sock_count *counts, int counts_len,
582+
struct bpf_link *link, int iter_fd)
583+
{
584+
/* Iterate through all sockets to trigger a realloc. */
585+
read_n(iter_fd, -1, counts, counts_len);
586+
587+
/* Make sure each socket was seen exactly once. */
588+
check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len,
589+
counts, counts_len);
590+
check_n_were_seen_once(established_socks, established_socks_len,
591+
established_socks_len, counts, counts_len);
592+
}
593+
365594
struct test_case {
366595
void (*test)(int family, int sock_type, const char *addr, __u16 port,
367596
int *socks, int socks_len, int *established_socks,
@@ -471,6 +700,69 @@ static struct test_case resume_tests[] = {
471700
.family = AF_INET6,
472701
.test = force_realloc,
473702
},
703+
{
704+
.description = "tcp: resume after removing a seen socket (established)",
705+
/* Force all established sockets into one bucket */
706+
.ehash_buckets = 1,
707+
.connections = nr_soreuse,
708+
.init_socks = nr_soreuse,
709+
/* Room for connect()ed and accept()ed sockets */
710+
.max_socks = nr_soreuse * 3,
711+
.sock_type = SOCK_STREAM,
712+
.family = AF_INET6,
713+
.test = remove_seen_established,
714+
},
715+
{
716+
.description = "tcp: resume after removing one unseen socket (established)",
717+
/* Force all established sockets into one bucket */
718+
.ehash_buckets = 1,
719+
.connections = nr_soreuse,
720+
.init_socks = nr_soreuse,
721+
/* Room for connect()ed and accept()ed sockets */
722+
.max_socks = nr_soreuse * 3,
723+
.sock_type = SOCK_STREAM,
724+
.family = AF_INET6,
725+
.test = remove_unseen_established,
726+
},
727+
{
728+
.description = "tcp: resume after removing all unseen sockets (established)",
729+
/* Force all established sockets into one bucket */
730+
.ehash_buckets = 1,
731+
.connections = nr_soreuse,
732+
.init_socks = nr_soreuse,
733+
/* Room for connect()ed and accept()ed sockets */
734+
.max_socks = nr_soreuse * 3,
735+
.sock_type = SOCK_STREAM,
736+
.family = AF_INET6,
737+
.test = remove_all_established,
738+
},
739+
{
740+
.description = "tcp: resume after adding a few sockets (established)",
741+
/* Force all established sockets into one bucket */
742+
.ehash_buckets = 1,
743+
.connections = nr_soreuse,
744+
.init_socks = nr_soreuse,
745+
/* Room for connect()ed and accept()ed sockets */
746+
.max_socks = nr_soreuse * 3,
747+
.sock_type = SOCK_STREAM,
748+
.family = AF_INET6,
749+
.test = add_some_established,
750+
},
751+
{
752+
.description = "tcp: force a realloc to occur (established)",
753+
/* Force all established sockets into one bucket */
754+
.ehash_buckets = 1,
755+
/* Bucket size will need to double when going from listening to
756+
* established sockets.
757+
*/
758+
.connections = init_batch_size,
759+
.init_socks = nr_soreuse,
760+
/* Room for connect()ed and accept()ed sockets */
761+
.max_socks = nr_soreuse + (init_batch_size * 2),
762+
.sock_type = SOCK_STREAM,
763+
.family = AF_INET6,
764+
.test = force_realloc_established,
765+
},
474766
};
475767

476768
static void do_resume_test(struct test_case *tc)

0 commit comments

Comments
 (0)