xref: /linux/tools/testing/selftests/net/tcp_ao/self-connect.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Author: Dmitry Safonov <dima@arista.com> */
3 #include <inttypes.h>
4 #include "aolib.h"
5 
6 static union tcp_addr local_addr;
7 
8 static void __setup_lo_intf(const char *lo_intf,
9 			    const char *addr_str, uint8_t prefix)
10 {
11 	if (inet_pton(TEST_FAMILY, addr_str, &local_addr) != 1)
12 		test_error("Can't convert local ip address");
13 
14 	if (ip_addr_add(lo_intf, TEST_FAMILY, local_addr, prefix))
15 		test_error("Failed to add %s ip address", lo_intf);
16 
17 	if (link_set_up(lo_intf))
18 		test_error("Failed to bring %s up", lo_intf);
19 }
20 
21 static void setup_lo_intf(const char *lo_intf)
22 {
23 #ifdef IPV6_TEST
24 	__setup_lo_intf(lo_intf, "::1", 128);
25 #else
26 	__setup_lo_intf(lo_intf, "127.0.0.1", 8);
27 #endif
28 }
29 
30 static void tcp_self_connect(const char *tst, unsigned int port,
31 			     bool different_keyids, bool check_restore)
32 {
33 	struct tcp_ao_counters before_ao, after_ao;
34 	uint64_t before_aogood, after_aogood;
35 	struct netstat *ns_before, *ns_after;
36 	const size_t nr_packets = 20;
37 	struct tcp_ao_repair ao_img;
38 	struct tcp_sock_state img;
39 	sockaddr_af addr;
40 	int sk;
41 
42 	tcp_addr_to_sockaddr_in(&addr, &local_addr, htons(port));
43 
44 	sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
45 	if (sk < 0)
46 		test_error("socket()");
47 
48 	if (different_keyids) {
49 		if (test_add_key(sk, DEFAULT_TEST_PASSWORD, local_addr, -1, 5, 7))
50 			test_error("setsockopt(TCP_AO_ADD_KEY)");
51 		if (test_add_key(sk, DEFAULT_TEST_PASSWORD, local_addr, -1, 7, 5))
52 			test_error("setsockopt(TCP_AO_ADD_KEY)");
53 	} else {
54 		if (test_add_key(sk, DEFAULT_TEST_PASSWORD, local_addr, -1, 100, 100))
55 			test_error("setsockopt(TCP_AO_ADD_KEY)");
56 	}
57 
58 	if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0)
59 		test_error("bind()");
60 
61 	ns_before = netstat_read();
62 	before_aogood = netstat_get(ns_before, "TCPAOGood", NULL);
63 	if (test_get_tcp_ao_counters(sk, &before_ao))
64 		test_error("test_get_tcp_ao_counters()");
65 
66 	if (__test_connect_socket(sk, "lo", (struct sockaddr *)&addr,
67 				  sizeof(addr), TEST_TIMEOUT_SEC) < 0) {
68 		ns_after = netstat_read();
69 		netstat_print_diff(ns_before, ns_after);
70 		test_error("failed to connect()");
71 	}
72 
73 	if (test_client_verify(sk, 100, nr_packets, TEST_TIMEOUT_SEC)) {
74 		test_fail("%s: tcp connection verify failed", tst);
75 		close(sk);
76 		return;
77 	}
78 
79 	ns_after = netstat_read();
80 	after_aogood = netstat_get(ns_after, "TCPAOGood", NULL);
81 	if (test_get_tcp_ao_counters(sk, &after_ao))
82 		test_error("test_get_tcp_ao_counters()");
83 	if (!check_restore) {
84 		/* to debug: netstat_print_diff(ns_before, ns_after); */
85 		netstat_free(ns_before);
86 	}
87 	netstat_free(ns_after);
88 
89 	if (after_aogood <= before_aogood) {
90 		test_fail("%s: TCPAOGood counter mismatch: %" PRIu64 " <= %" PRIu64,
91 			  tst, after_aogood, before_aogood);
92 		close(sk);
93 		return;
94 	}
95 
96 	if (test_tcp_ao_counters_cmp(tst, &before_ao, &after_ao, TEST_CNT_GOOD)) {
97 		close(sk);
98 		return;
99 	}
100 
101 	if (!check_restore) {
102 		test_ok("%s: connect TCPAOGood %" PRIu64 " => %" PRIu64,
103 				tst, before_aogood, after_aogood);
104 		close(sk);
105 		return;
106 	}
107 
108 	test_enable_repair(sk);
109 	test_sock_checkpoint(sk, &img, &addr);
110 #ifdef IPV6_TEST
111 	addr.sin6_port = htons(port + 1);
112 #else
113 	addr.sin_port = htons(port + 1);
114 #endif
115 	test_ao_checkpoint(sk, &ao_img);
116 	test_kill_sk(sk);
117 
118 	sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
119 	if (sk < 0)
120 		test_error("socket()");
121 
122 	test_enable_repair(sk);
123 	__test_sock_restore(sk, "lo", &img, &addr, &addr, sizeof(addr));
124 	if (different_keyids) {
125 		if (test_add_repaired_key(sk, DEFAULT_TEST_PASSWORD, 0,
126 					  local_addr, -1, 7, 5))
127 			test_error("setsockopt(TCP_AO_ADD_KEY)");
128 		if (test_add_repaired_key(sk, DEFAULT_TEST_PASSWORD, 0,
129 					  local_addr, -1, 5, 7))
130 			test_error("setsockopt(TCP_AO_ADD_KEY)");
131 	} else {
132 		if (test_add_repaired_key(sk, DEFAULT_TEST_PASSWORD, 0,
133 					  local_addr, -1, 100, 100))
134 			test_error("setsockopt(TCP_AO_ADD_KEY)");
135 	}
136 	test_ao_restore(sk, &ao_img);
137 	test_disable_repair(sk);
138 	test_sock_state_free(&img);
139 	if (test_client_verify(sk, 100, nr_packets, TEST_TIMEOUT_SEC)) {
140 		test_fail("%s: tcp connection verify failed", tst);
141 		close(sk);
142 		return;
143 	}
144 	ns_after = netstat_read();
145 	after_aogood = netstat_get(ns_after, "TCPAOGood", NULL);
146 	/* to debug: netstat_print_diff(ns_before, ns_after); */
147 	netstat_free(ns_before);
148 	netstat_free(ns_after);
149 	close(sk);
150 	if (after_aogood <= before_aogood) {
151 		test_fail("%s: TCPAOGood counter mismatch: %" PRIu64 " <= %" PRIu64,
152 			  tst, after_aogood, before_aogood);
153 		return;
154 	}
155 	test_ok("%s: connect TCPAOGood %" PRIu64 " => %" PRIu64,
156 			tst, before_aogood, after_aogood);
157 }
158 
159 static void *client_fn(void *arg)
160 {
161 	unsigned int port = test_server_port;
162 
163 	setup_lo_intf("lo");
164 
165 	tcp_self_connect("self-connect(same keyids)", port++, false, false);
166 
167 	/* expecting rnext to change based on the first segment RNext != Current */
168 	trace_ao_event_expect(TCP_AO_RNEXT_REQUEST, local_addr, local_addr,
169 			      port, port, 0, -1, -1, -1, -1, -1, 7, 5, -1);
170 	tcp_self_connect("self-connect(different keyids)", port++, true, false);
171 	tcp_self_connect("self-connect(restore)", port, false, true);
172 	port += 2; /* restore test restores over different port */
173 	trace_ao_event_expect(TCP_AO_RNEXT_REQUEST, local_addr, local_addr,
174 			      port, port, 0, -1, -1, -1, -1, -1, 7, 5, -1);
175 	/* intentionally on restore they are added to the socket in different order */
176 	trace_ao_event_expect(TCP_AO_RNEXT_REQUEST, local_addr, local_addr,
177 			      port + 1, port + 1, 0, -1, -1, -1, -1, -1, 5, 7, -1);
178 	tcp_self_connect("self-connect(restore, different keyids)", port, true, true);
179 	port += 2; /* restore test restores over different port */
180 
181 	return NULL;
182 }
183 
184 int main(int argc, char *argv[])
185 {
186 	test_init(5, client_fn, NULL);
187 	return 0;
188 }
189