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