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
729const 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-
8285static 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
171173static 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
411453int 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