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