-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests/net: Add SEQ number extension test
Check that on SEQ number wraparound there is no disruption or TCPAOBad segments produced. Sample of expected output: > # ./seq-ext_ipv4 > 1..7 > # 1436[lib/setup.c:254] rand seed 1686611079 > TAP version 13 > ok 1 server alive > ok 2 post-migrate connection alive > ok 3 TCPAOGood counter increased 1002 => 3002 > ok 4 TCPAOGood counter increased 1003 => 3003 > ok 5 TCPAOBad counter didn't increase > ok 6 TCPAOBad counter didn't increase > ok 7 SEQ extension incremented: 1/1999, 1/998999 > # Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Dmitry Safonov <dima@arista.com> Signed-off-by: David S. Miller <davem@davemloft.net>
- Loading branch information
Dmitry Safonov
authored and
David S. Miller
committed
Dec 17, 2023
1 parent
3715d32
commit 0d16eae
Showing
2 changed files
with
246 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Check that after SEQ number wrap-around: | ||
* 1. SEQ-extension has upper bytes set | ||
* 2. TCP conneciton is alive and no TCPAOBad segments | ||
* In order to test (2), the test doesn't just adjust seq number for a queue | ||
* on a connected socket, but migrates it to another sk+port number, so | ||
* that there won't be any delayed packets that will fail to verify | ||
* with the new SEQ numbers. | ||
*/ | ||
#include <inttypes.h> | ||
#include "aolib.h" | ||
|
||
const unsigned int nr_packets = 1000; | ||
const unsigned int msg_len = 1000; | ||
const unsigned int quota = nr_packets * msg_len; | ||
unsigned int client_new_port; | ||
|
||
/* Move them closer to roll-over */ | ||
static void test_adjust_seqs(struct tcp_sock_state *img, | ||
struct tcp_ao_repair *ao_img, | ||
bool server) | ||
{ | ||
uint32_t new_seq1, new_seq2; | ||
|
||
/* make them roll-over during quota, but on different segments */ | ||
if (server) { | ||
new_seq1 = ((uint32_t)-1) - msg_len; | ||
new_seq2 = ((uint32_t)-1) - (quota - 2 * msg_len); | ||
} else { | ||
new_seq1 = ((uint32_t)-1) - (quota - 2 * msg_len); | ||
new_seq2 = ((uint32_t)-1) - msg_len; | ||
} | ||
|
||
img->in.seq = new_seq1; | ||
img->trw.snd_wl1 = img->in.seq - msg_len; | ||
img->out.seq = new_seq2; | ||
img->trw.rcv_wup = img->in.seq; | ||
} | ||
|
||
static int test_sk_restore(struct tcp_sock_state *img, | ||
struct tcp_ao_repair *ao_img, sockaddr_af *saddr, | ||
const union tcp_addr daddr, unsigned int dport, | ||
struct tcp_ao_counters *cnt) | ||
{ | ||
int sk; | ||
|
||
sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); | ||
if (sk < 0) | ||
test_error("socket()"); | ||
|
||
test_enable_repair(sk); | ||
test_sock_restore(sk, img, saddr, daddr, dport); | ||
if (test_add_repaired_key(sk, DEFAULT_TEST_PASSWORD, 0, daddr, -1, 100, 100)) | ||
test_error("setsockopt(TCP_AO_ADD_KEY)"); | ||
test_ao_restore(sk, ao_img); | ||
|
||
if (test_get_tcp_ao_counters(sk, cnt)) | ||
test_error("test_get_tcp_ao_counters()"); | ||
|
||
test_disable_repair(sk); | ||
test_sock_state_free(img); | ||
return sk; | ||
} | ||
|
||
static void *server_fn(void *arg) | ||
{ | ||
uint64_t before_good, after_good, after_bad; | ||
struct tcp_ao_counters ao1, ao2; | ||
struct tcp_sock_state img; | ||
struct tcp_ao_repair ao_img; | ||
sockaddr_af saddr; | ||
ssize_t bytes; | ||
int sk, lsk; | ||
|
||
lsk = test_listen_socket(this_ip_addr, test_server_port, 1); | ||
|
||
if (test_add_key(lsk, DEFAULT_TEST_PASSWORD, this_ip_dest, -1, 100, 100)) | ||
test_error("setsockopt(TCP_AO_ADD_KEY)"); | ||
|
||
synchronize_threads(); /* 1: MKT added => connect() */ | ||
|
||
if (test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0)) | ||
test_error("test_wait_fd()"); | ||
|
||
sk = accept(lsk, NULL, NULL); | ||
if (sk < 0) | ||
test_error("accept()"); | ||
|
||
synchronize_threads(); /* 2: accepted => send data */ | ||
close(lsk); | ||
|
||
bytes = test_server_run(sk, quota, TEST_TIMEOUT_SEC); | ||
if (bytes != quota) { | ||
if (bytes > 0) | ||
test_fail("server served: %zd", bytes); | ||
else | ||
test_fail("server returned: %zd", bytes); | ||
goto out; | ||
} | ||
|
||
before_good = netstat_get_one("TCPAOGood", NULL); | ||
|
||
synchronize_threads(); /* 3: restore the connection on another port */ | ||
|
||
test_enable_repair(sk); | ||
test_sock_checkpoint(sk, &img, &saddr); | ||
test_ao_checkpoint(sk, &ao_img); | ||
test_kill_sk(sk); | ||
#ifdef IPV6_TEST | ||
saddr.sin6_port = htons(ntohs(saddr.sin6_port) + 1); | ||
#else | ||
saddr.sin_port = htons(ntohs(saddr.sin_port) + 1); | ||
#endif | ||
test_adjust_seqs(&img, &ao_img, true); | ||
synchronize_threads(); /* 4: dump finished */ | ||
sk = test_sk_restore(&img, &ao_img, &saddr, this_ip_dest, | ||
client_new_port, &ao1); | ||
|
||
synchronize_threads(); /* 5: verify counters during SEQ-number rollover */ | ||
bytes = test_server_run(sk, quota, TEST_TIMEOUT_SEC); | ||
if (bytes != quota) { | ||
if (bytes > 0) | ||
test_fail("server served: %zd", bytes); | ||
else | ||
test_fail("server returned: %zd", bytes); | ||
} else { | ||
test_ok("server alive"); | ||
} | ||
|
||
if (test_get_tcp_ao_counters(sk, &ao2)) | ||
test_error("test_get_tcp_ao_counters()"); | ||
after_good = netstat_get_one("TCPAOGood", NULL); | ||
|
||
test_tcp_ao_counters_cmp(NULL, &ao1, &ao2, TEST_CNT_GOOD); | ||
|
||
if (after_good <= before_good) { | ||
test_fail("TCPAOGood counter did not increase: %zu <= %zu", | ||
after_good, before_good); | ||
} else { | ||
test_ok("TCPAOGood counter increased %zu => %zu", | ||
before_good, after_good); | ||
} | ||
after_bad = netstat_get_one("TCPAOBad", NULL); | ||
if (after_bad) | ||
test_fail("TCPAOBad counter is non-zero: %zu", after_bad); | ||
else | ||
test_ok("TCPAOBad counter didn't increase"); | ||
test_enable_repair(sk); | ||
test_ao_checkpoint(sk, &ao_img); | ||
if (ao_img.snd_sne && ao_img.rcv_sne) { | ||
test_ok("SEQ extension incremented: %u/%u", | ||
ao_img.snd_sne, ao_img.rcv_sne); | ||
} else { | ||
test_fail("SEQ extension was not incremented: %u/%u", | ||
ao_img.snd_sne, ao_img.rcv_sne); | ||
} | ||
|
||
synchronize_threads(); /* 6: verified => closed */ | ||
out: | ||
close(sk); | ||
return NULL; | ||
} | ||
|
||
static void *client_fn(void *arg) | ||
{ | ||
uint64_t before_good, after_good, after_bad; | ||
struct tcp_ao_counters ao1, ao2; | ||
struct tcp_sock_state img; | ||
struct tcp_ao_repair ao_img; | ||
sockaddr_af saddr; | ||
int sk; | ||
|
||
sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); | ||
if (sk < 0) | ||
test_error("socket()"); | ||
|
||
if (test_add_key(sk, DEFAULT_TEST_PASSWORD, this_ip_dest, -1, 100, 100)) | ||
test_error("setsockopt(TCP_AO_ADD_KEY)"); | ||
|
||
synchronize_threads(); /* 1: MKT added => connect() */ | ||
if (test_connect_socket(sk, this_ip_dest, test_server_port) <= 0) | ||
test_error("failed to connect()"); | ||
|
||
synchronize_threads(); /* 2: accepted => send data */ | ||
if (test_client_verify(sk, msg_len, nr_packets, TEST_TIMEOUT_SEC)) { | ||
test_fail("pre-migrate verify failed"); | ||
return NULL; | ||
} | ||
|
||
before_good = netstat_get_one("TCPAOGood", NULL); | ||
|
||
synchronize_threads(); /* 3: restore the connection on another port */ | ||
test_enable_repair(sk); | ||
test_sock_checkpoint(sk, &img, &saddr); | ||
test_ao_checkpoint(sk, &ao_img); | ||
test_kill_sk(sk); | ||
#ifdef IPV6_TEST | ||
client_new_port = ntohs(saddr.sin6_port) + 1; | ||
saddr.sin6_port = htons(ntohs(saddr.sin6_port) + 1); | ||
#else | ||
client_new_port = ntohs(saddr.sin_port) + 1; | ||
saddr.sin_port = htons(ntohs(saddr.sin_port) + 1); | ||
#endif | ||
test_adjust_seqs(&img, &ao_img, false); | ||
synchronize_threads(); /* 4: dump finished */ | ||
sk = test_sk_restore(&img, &ao_img, &saddr, this_ip_dest, | ||
test_server_port + 1, &ao1); | ||
|
||
synchronize_threads(); /* 5: verify counters during SEQ-number rollover */ | ||
if (test_client_verify(sk, msg_len, nr_packets, TEST_TIMEOUT_SEC)) | ||
test_fail("post-migrate verify failed"); | ||
else | ||
test_ok("post-migrate connection alive"); | ||
|
||
if (test_get_tcp_ao_counters(sk, &ao2)) | ||
test_error("test_get_tcp_ao_counters()"); | ||
after_good = netstat_get_one("TCPAOGood", NULL); | ||
|
||
test_tcp_ao_counters_cmp(NULL, &ao1, &ao2, TEST_CNT_GOOD); | ||
|
||
if (after_good <= before_good) { | ||
test_fail("TCPAOGood counter did not increase: %zu <= %zu", | ||
after_good, before_good); | ||
} else { | ||
test_ok("TCPAOGood counter increased %zu => %zu", | ||
before_good, after_good); | ||
} | ||
after_bad = netstat_get_one("TCPAOBad", NULL); | ||
if (after_bad) | ||
test_fail("TCPAOBad counter is non-zero: %zu", after_bad); | ||
else | ||
test_ok("TCPAOBad counter didn't increase"); | ||
|
||
synchronize_threads(); /* 6: verified => closed */ | ||
close(sk); | ||
|
||
synchronize_threads(); /* don't race to exit: let server exit() */ | ||
return NULL; | ||
} | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
test_init(7, server_fn, client_fn); | ||
return 0; | ||
} |