Skip to content

Commit 6caf3ad

Browse files
0x7f454c46kuba-moo
authored andcommitted
selftests/net: Repair RST passive reset selftest
Currently, the test is racy and seems to not pass anymore. In order to rectify it, aim on TCP_TW_RST. Doesn't seem way too good with this sleep() part, but it seems as a reasonable compromise for the test. There is a plan in-line comment on how-to improve it, going to do it on the top, at this moment I want it to run on netdev/patchwork selftests dashboard. It also slightly changes tcp_ao-lib in order to get SO_ERROR propagated to test_client_verify() return value. Fixes: c6df7b2 ("selftests/net: Add TCP-AO RST test") Signed-off-by: Dmitry Safonov <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 384aa16 commit 6caf3ad

File tree

2 files changed

+98
-52
lines changed

2 files changed

+98
-52
lines changed

tools/testing/selftests/net/tcp_ao/lib/sock.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ int test_wait_fd(int sk, time_t sec, bool write)
6262
return -ETIMEDOUT;
6363
}
6464

65-
if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &slen) || ret)
65+
if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &slen))
66+
return -errno;
67+
if (ret)
6668
return -ret;
6769
return 0;
6870
}
@@ -584,9 +586,11 @@ int test_client_verify(int sk, const size_t msg_len, const size_t nr,
584586
{
585587
size_t buf_sz = msg_len * nr;
586588
char *buf = alloca(buf_sz);
589+
ssize_t ret;
587590

588591
randomize_buffer(buf, buf_sz);
589-
if (test_client_loop(sk, buf, buf_sz, msg_len, timeout_sec) != buf_sz)
590-
return -1;
591-
return 0;
592+
ret = test_client_loop(sk, buf, buf_sz, msg_len, timeout_sec);
593+
if (ret < 0)
594+
return (int)ret;
595+
return ret != buf_sz ? -1 : 0;
592596
}

tools/testing/selftests/net/tcp_ao/rst.c

Lines changed: 90 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,33 @@
11
// SPDX-License-Identifier: GPL-2.0
2-
/* Author: Dmitry Safonov <[email protected]> */
2+
/*
3+
* The test checks that both active and passive reset have correct TCP-AO
4+
* signature. An "active" reset (abort) here is procured from closing
5+
* listen() socket with non-accepted connections in the queue:
6+
* inet_csk_listen_stop() => inet_child_forget() =>
7+
* => tcp_disconnect() => tcp_send_active_reset()
8+
*
9+
* The passive reset is quite hard to get on established TCP connections.
10+
* It could be procured from non-established states, but the synchronization
11+
* part from userspace in order to reliably get RST seems uneasy.
12+
* So, instead it's procured by corrupting SEQ number on TIMED-WAIT state.
13+
*
14+
* It's important to test both passive and active RST as they go through
15+
* different code-paths:
16+
* - tcp_send_active_reset() makes no-data skb, sends it with tcp_transmit_skb()
17+
* - tcp_v*_send_reset() create their reply skbs and send them with
18+
* ip_send_unicast_reply()
19+
*
20+
* In both cases TCP-AO signatures have to be correct, which is verified by
21+
* (1) checking that the TCP-AO connection was reset and (2) TCP-AO counters.
22+
*
23+
* Author: Dmitry Safonov <[email protected]>
24+
*/
325
#include <inttypes.h>
426
#include "../../../../include/linux/kernel.h"
527
#include "aolib.h"
628

729
const size_t quota = 1000;
30+
const size_t packet_sz = 100;
831
/*
932
* Backlog == 0 means 1 connection in queue, see:
1033
* commit 64a146513f8f ("[NET]: Revert incorrect accept queue...")
@@ -59,26 +82,6 @@ static void close_forced(int sk)
5982
close(sk);
6083
}
6184

62-
static int test_wait_for_exception(int sk, time_t sec)
63-
{
64-
struct timeval tv = { .tv_sec = sec };
65-
struct timeval *ptv = NULL;
66-
fd_set efds;
67-
int ret;
68-
69-
FD_ZERO(&efds);
70-
FD_SET(sk, &efds);
71-
72-
if (sec)
73-
ptv = &tv;
74-
75-
errno = 0;
76-
ret = select(sk + 1, NULL, NULL, &efds, ptv);
77-
if (ret < 0)
78-
return -errno;
79-
return ret ? sk : 0;
80-
}
81-
8285
static void test_server_active_rst(unsigned int port)
8386
{
8487
struct tcp_ao_counters cnt1, cnt2;
@@ -155,17 +158,16 @@ static void test_server_passive_rst(unsigned int port)
155158
test_fail("server returned %zd", bytes);
156159
}
157160

158-
synchronize_threads(); /* 3: chekpoint/restore the connection */
161+
synchronize_threads(); /* 3: checkpoint the client */
162+
synchronize_threads(); /* 4: close the server, creating twsk */
159163
if (test_get_tcp_ao_counters(sk, &ao2))
160164
test_error("test_get_tcp_ao_counters()");
161-
162-
synchronize_threads(); /* 4: terminate server + send more on client */
163-
bytes = test_server_run(sk, quota, TEST_RETRANSMIT_SEC);
164165
close(sk);
166+
167+
synchronize_threads(); /* 5: restore the socket, send more data */
165168
test_tcp_ao_counters_cmp("passive RST server", &ao1, &ao2, TEST_CNT_GOOD);
166169

167-
synchronize_threads(); /* 5: verified => closed */
168-
close(sk);
170+
synchronize_threads(); /* 6: server exits */
169171
}
170172

171173
static void *server_fn(void *arg)
@@ -284,7 +286,7 @@ static void test_client_active_rst(unsigned int port)
284286
test_error("test_wait_fds(): %d", err);
285287

286288
synchronize_threads(); /* 3: close listen socket */
287-
if (test_client_verify(sk[0], 100, quota / 100, TEST_TIMEOUT_SEC))
289+
if (test_client_verify(sk[0], packet_sz, quota / packet_sz, TEST_TIMEOUT_SEC))
288290
test_fail("Failed to send data on connected socket");
289291
else
290292
test_ok("Verified established tcp connection");
@@ -323,7 +325,6 @@ static void test_client_passive_rst(unsigned int port)
323325
struct tcp_sock_state img;
324326
sockaddr_af saddr;
325327
int sk, err;
326-
socklen_t slen = sizeof(err);
327328

328329
sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
329330
if (sk < 0)
@@ -337,18 +338,51 @@ static void test_client_passive_rst(unsigned int port)
337338
test_error("failed to connect()");
338339

339340
synchronize_threads(); /* 2: accepted => send data */
340-
if (test_client_verify(sk, 100, quota / 100, TEST_TIMEOUT_SEC))
341+
if (test_client_verify(sk, packet_sz, quota / packet_sz, TEST_TIMEOUT_SEC))
341342
test_fail("Failed to send data on connected socket");
342343
else
343344
test_ok("Verified established tcp connection");
344345

345-
synchronize_threads(); /* 3: chekpoint/restore the connection */
346+
synchronize_threads(); /* 3: checkpoint the client */
346347
test_enable_repair(sk);
347348
test_sock_checkpoint(sk, &img, &saddr);
348349
test_ao_checkpoint(sk, &ao_img);
349-
test_kill_sk(sk);
350+
test_disable_repair(sk);
350351

351-
img.out.seq += quota;
352+
synchronize_threads(); /* 4: close the server, creating twsk */
353+
354+
/*
355+
* The "corruption" in SEQ has to be small enough to fit into TCP
356+
* window, see tcp_timewait_state_process() for out-of-window
357+
* segments.
358+
*/
359+
img.out.seq += 5; /* 5 is more noticeable in tcpdump than 1 */
360+
361+
/*
362+
* FIXME: This is kind-of ugly and dirty, but it works.
363+
*
364+
* At this moment, the server has close'ed(sk).
365+
* The passive RST that is being targeted here is new data after
366+
* half-duplex close, see tcp_timewait_state_process() => TCP_TW_RST
367+
*
368+
* What is needed here is:
369+
* (1) wait for FIN from the server
370+
* (2) make sure that the ACK from the client went out
371+
* (3) make sure that the ACK was received and processed by the server
372+
*
373+
* Otherwise, the data that will be sent from "repaired" socket
374+
* post SEQ corruption may get to the server before it's in
375+
* TCP_FIN_WAIT2.
376+
*
377+
* (1) is easy with select()/poll()
378+
* (2) is possible by polling tcpi_state from TCP_INFO
379+
* (3) is quite complex: as server's socket was already closed,
380+
* probably the way to do it would be tcp-diag.
381+
*/
382+
sleep(TEST_RETRANSMIT_SEC);
383+
384+
synchronize_threads(); /* 5: restore the socket, send more data */
385+
test_kill_sk(sk);
352386

353387
sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
354388
if (sk < 0)
@@ -366,25 +400,33 @@ static void test_client_passive_rst(unsigned int port)
366400
test_disable_repair(sk);
367401
test_sock_state_free(&img);
368402

369-
synchronize_threads(); /* 4: terminate server + send more on client */
370-
if (test_client_verify(sk, 100, quota / 100, 2 * TEST_TIMEOUT_SEC))
371-
test_ok("client connection broken post-seq-adjust");
372-
else
373-
test_fail("client connection still works post-seq-adjust");
374-
375-
test_wait_for_exception(sk, TEST_TIMEOUT_SEC);
376-
377-
if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &slen))
378-
test_error("getsockopt()");
379-
if (err != ECONNRESET && err != EPIPE)
380-
test_fail("client connection was not reset: %d", err);
403+
/*
404+
* This is how "passive reset" is acquired in this test from TCP_TW_RST:
405+
*
406+
* IP 10.0.254.1.7011 > 10.0.1.1.59772: Flags [P.], seq 901:1001, ack 1001, win 249,
407+
* options [tcp-ao keyid 100 rnextkeyid 100 mac 0x10217d6c36a22379086ef3b1], length 100
408+
* IP 10.0.254.1.7011 > 10.0.1.1.59772: Flags [F.], seq 1001, ack 1001, win 249,
409+
* options [tcp-ao keyid 100 rnextkeyid 100 mac 0x104ffc99b98c10a5298cc268], length 0
410+
* IP 10.0.1.1.59772 > 10.0.254.1.7011: Flags [.], ack 1002, win 251,
411+
* options [tcp-ao keyid 100 rnextkeyid 100 mac 0xe496dd4f7f5a8a66873c6f93,nop,nop,sack 1 {1001:1002}], length 0
412+
* IP 10.0.1.1.59772 > 10.0.254.1.7011: Flags [P.], seq 1006:1106, ack 1001, win 251,
413+
* options [tcp-ao keyid 100 rnextkeyid 100 mac 0x1b5f3330fb23fbcd0c77d0ca], length 100
414+
* IP 10.0.254.1.7011 > 10.0.1.1.59772: Flags [R], seq 3215596252, win 0,
415+
* options [tcp-ao keyid 100 rnextkeyid 100 mac 0x0bcfbbf497bce844312304b2], length 0
416+
*/
417+
err = test_client_verify(sk, packet_sz, quota / packet_sz, 2 * TEST_TIMEOUT_SEC);
418+
/* Make sure that the connection was reset, not timeouted */
419+
if (err && err == -ECONNRESET)
420+
test_ok("client sock was passively reset post-seq-adjust");
421+
else if (err)
422+
test_fail("client sock was not reset post-seq-adjust: %d", err);
381423
else
382-
test_ok("client connection was reset");
424+
test_fail("client sock is yet connected post-seq-adjust");
383425

384426
if (test_get_tcp_ao_counters(sk, &ao2))
385427
test_error("test_get_tcp_ao_counters()");
386428

387-
synchronize_threads(); /* 5: verified => closed */
429+
synchronize_threads(); /* 6: server exits */
388430
close(sk);
389431
test_tcp_ao_counters_cmp("client passive RST", &ao1, &ao2, TEST_CNT_GOOD);
390432
}
@@ -410,6 +452,6 @@ static void *client_fn(void *arg)
410452

411453
int main(int argc, char *argv[])
412454
{
413-
test_init(15, server_fn, client_fn);
455+
test_init(14, server_fn, client_fn);
414456
return 0;
415457
}

0 commit comments

Comments
 (0)