1ed9d09b3SDmitry Safonov // SPDX-License-Identifier: GPL-2.0
2ed9d09b3SDmitry Safonov /* Author: Dmitry Safonov <dima@arista.com> */
3ed9d09b3SDmitry Safonov #include <inttypes.h>
4ed9d09b3SDmitry Safonov #include "aolib.h"
5ed9d09b3SDmitry Safonov 
6ed9d09b3SDmitry Safonov #define fault(type)	(inj == FAULT_ ## type)
7ed9d09b3SDmitry Safonov 
test_add_key_maclen(int sk,const char * key,uint8_t maclen,union tcp_addr in_addr,uint8_t prefix,uint8_t sndid,uint8_t rcvid)8ed9d09b3SDmitry Safonov static inline int test_add_key_maclen(int sk, const char *key, uint8_t maclen,
9ed9d09b3SDmitry Safonov 				      union tcp_addr in_addr, uint8_t prefix,
10ed9d09b3SDmitry Safonov 				      uint8_t sndid, uint8_t rcvid)
11ed9d09b3SDmitry Safonov {
12ed9d09b3SDmitry Safonov 	struct tcp_ao_add tmp = {};
13ed9d09b3SDmitry Safonov 	int err;
14ed9d09b3SDmitry Safonov 
15ed9d09b3SDmitry Safonov 	if (prefix > DEFAULT_TEST_PREFIX)
16ed9d09b3SDmitry Safonov 		prefix = DEFAULT_TEST_PREFIX;
17ed9d09b3SDmitry Safonov 
18ed9d09b3SDmitry Safonov 	err = test_prepare_key(&tmp, DEFAULT_TEST_ALGO, in_addr, false, false,
19ed9d09b3SDmitry Safonov 			       prefix, 0, sndid, rcvid, maclen,
20ed9d09b3SDmitry Safonov 			       0, strlen(key), key);
21ed9d09b3SDmitry Safonov 	if (err)
22ed9d09b3SDmitry Safonov 		return err;
23ed9d09b3SDmitry Safonov 
24ed9d09b3SDmitry Safonov 	err = setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &tmp, sizeof(tmp));
25ed9d09b3SDmitry Safonov 	if (err < 0)
26ed9d09b3SDmitry Safonov 		return -errno;
27ed9d09b3SDmitry Safonov 
28ed9d09b3SDmitry Safonov 	return test_verify_socket_key(sk, &tmp);
29ed9d09b3SDmitry Safonov }
30ed9d09b3SDmitry Safonov 
try_accept(const char * tst_name,unsigned int port,const char * pwd,union tcp_addr addr,uint8_t prefix,uint8_t sndid,uint8_t rcvid,uint8_t maclen,const char * cnt_name,test_cnt cnt_expected,fault_t inj)31ed9d09b3SDmitry Safonov static void try_accept(const char *tst_name, unsigned int port, const char *pwd,
32ed9d09b3SDmitry Safonov 		       union tcp_addr addr, uint8_t prefix,
33ed9d09b3SDmitry Safonov 		       uint8_t sndid, uint8_t rcvid, uint8_t maclen,
34ed9d09b3SDmitry Safonov 		       const char *cnt_name, test_cnt cnt_expected,
35ed9d09b3SDmitry Safonov 		       fault_t inj)
36ed9d09b3SDmitry Safonov {
37ed9d09b3SDmitry Safonov 	struct tcp_ao_counters ao_cnt1, ao_cnt2;
38ed9d09b3SDmitry Safonov 	uint64_t before_cnt = 0, after_cnt = 0; /* silence GCC */
39ed9d09b3SDmitry Safonov 	int lsk, err, sk = 0;
40ed9d09b3SDmitry Safonov 	time_t timeout;
41ed9d09b3SDmitry Safonov 
42ed9d09b3SDmitry Safonov 	lsk = test_listen_socket(this_ip_addr, port, 1);
43ed9d09b3SDmitry Safonov 
44ed9d09b3SDmitry Safonov 	if (pwd && test_add_key_maclen(lsk, pwd, maclen, addr, prefix, sndid, rcvid))
45ed9d09b3SDmitry Safonov 		test_error("setsockopt(TCP_AO_ADD_KEY)");
46ed9d09b3SDmitry Safonov 
47ed9d09b3SDmitry Safonov 	if (cnt_name)
48ed9d09b3SDmitry Safonov 		before_cnt = netstat_get_one(cnt_name, NULL);
49ed9d09b3SDmitry Safonov 	if (pwd && test_get_tcp_ao_counters(lsk, &ao_cnt1))
50ed9d09b3SDmitry Safonov 		test_error("test_get_tcp_ao_counters()");
51ed9d09b3SDmitry Safonov 
52ed9d09b3SDmitry Safonov 	synchronize_threads(); /* preparations done */
53ed9d09b3SDmitry Safonov 
54ed9d09b3SDmitry Safonov 	timeout = fault(TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC;
55ed9d09b3SDmitry Safonov 	err = test_wait_fd(lsk, timeout, 0);
56ed9d09b3SDmitry Safonov 	if (err == -ETIMEDOUT) {
57ed9d09b3SDmitry Safonov 		if (!fault(TIMEOUT))
58*67f440c0SColin Ian King 			test_fail("timed out for accept()");
59ed9d09b3SDmitry Safonov 	} else if (err < 0) {
60ed9d09b3SDmitry Safonov 		test_error("test_wait_fd()");
61ed9d09b3SDmitry Safonov 	} else {
62ed9d09b3SDmitry Safonov 		if (fault(TIMEOUT))
63ed9d09b3SDmitry Safonov 			test_fail("ready to accept");
64ed9d09b3SDmitry Safonov 
65ed9d09b3SDmitry Safonov 		sk = accept(lsk, NULL, NULL);
66ed9d09b3SDmitry Safonov 		if (sk < 0) {
67ed9d09b3SDmitry Safonov 			test_error("accept()");
68ed9d09b3SDmitry Safonov 		} else {
69ed9d09b3SDmitry Safonov 			if (fault(TIMEOUT))
70ed9d09b3SDmitry Safonov 				test_fail("%s: accepted", tst_name);
71ed9d09b3SDmitry Safonov 		}
72ed9d09b3SDmitry Safonov 	}
73ed9d09b3SDmitry Safonov 
74ed9d09b3SDmitry Safonov 	if (pwd && test_get_tcp_ao_counters(lsk, &ao_cnt2))
75ed9d09b3SDmitry Safonov 		test_error("test_get_tcp_ao_counters()");
76ed9d09b3SDmitry Safonov 
77ed9d09b3SDmitry Safonov 	close(lsk);
78ed9d09b3SDmitry Safonov 	if (pwd)
79ed9d09b3SDmitry Safonov 		test_tcp_ao_counters_cmp(tst_name, &ao_cnt1, &ao_cnt2, cnt_expected);
80ed9d09b3SDmitry Safonov 
81ed9d09b3SDmitry Safonov 	if (!cnt_name)
82ed9d09b3SDmitry Safonov 		goto out;
83ed9d09b3SDmitry Safonov 
84ed9d09b3SDmitry Safonov 	after_cnt = netstat_get_one(cnt_name, NULL);
85ed9d09b3SDmitry Safonov 
86ed9d09b3SDmitry Safonov 	if (after_cnt <= before_cnt) {
87ed9d09b3SDmitry Safonov 		test_fail("%s: %s counter did not increase: %zu <= %zu",
88ed9d09b3SDmitry Safonov 				tst_name, cnt_name, after_cnt, before_cnt);
89ed9d09b3SDmitry Safonov 	} else {
90ed9d09b3SDmitry Safonov 		test_ok("%s: counter %s increased %zu => %zu",
91ed9d09b3SDmitry Safonov 			tst_name, cnt_name, before_cnt, after_cnt);
92ed9d09b3SDmitry Safonov 	}
93ed9d09b3SDmitry Safonov 
94ed9d09b3SDmitry Safonov out:
95ed9d09b3SDmitry Safonov 	synchronize_threads(); /* close() */
96ed9d09b3SDmitry Safonov 	if (sk > 0)
97ed9d09b3SDmitry Safonov 		close(sk);
98ed9d09b3SDmitry Safonov }
99ed9d09b3SDmitry Safonov 
server_fn(void * arg)100ed9d09b3SDmitry Safonov static void *server_fn(void *arg)
101ed9d09b3SDmitry Safonov {
102ed9d09b3SDmitry Safonov 	union tcp_addr wrong_addr, network_addr;
103ed9d09b3SDmitry Safonov 	unsigned int port = test_server_port;
104ed9d09b3SDmitry Safonov 
105ed9d09b3SDmitry Safonov 	if (inet_pton(TEST_FAMILY, TEST_WRONG_IP, &wrong_addr) != 1)
106ed9d09b3SDmitry Safonov 		test_error("Can't convert ip address %s", TEST_WRONG_IP);
107ed9d09b3SDmitry Safonov 
108ed9d09b3SDmitry Safonov 	try_accept("Non-AO server + AO client", port++, NULL,
109ed9d09b3SDmitry Safonov 		   this_ip_dest, -1, 100, 100, 0,
110ed9d09b3SDmitry Safonov 		   "TCPAOKeyNotFound", 0, FAULT_TIMEOUT);
111ed9d09b3SDmitry Safonov 
112ed9d09b3SDmitry Safonov 	try_accept("AO server + Non-AO client", port++, DEFAULT_TEST_PASSWORD,
113ed9d09b3SDmitry Safonov 		   this_ip_dest, -1, 100, 100, 0,
114ed9d09b3SDmitry Safonov 		   "TCPAORequired", TEST_CNT_AO_REQUIRED, FAULT_TIMEOUT);
115ed9d09b3SDmitry Safonov 
116ed9d09b3SDmitry Safonov 	try_accept("Wrong password", port++, "something that is not DEFAULT_TEST_PASSWORD",
117ed9d09b3SDmitry Safonov 		   this_ip_dest, -1, 100, 100, 0,
118ed9d09b3SDmitry Safonov 		   "TCPAOBad", TEST_CNT_BAD, FAULT_TIMEOUT);
119ed9d09b3SDmitry Safonov 
120ed9d09b3SDmitry Safonov 	try_accept("Wrong rcv id", port++, DEFAULT_TEST_PASSWORD,
121ed9d09b3SDmitry Safonov 		   this_ip_dest, -1, 100, 101, 0,
122ed9d09b3SDmitry Safonov 		   "TCPAOKeyNotFound", TEST_CNT_AO_KEY_NOT_FOUND, FAULT_TIMEOUT);
123ed9d09b3SDmitry Safonov 
124ed9d09b3SDmitry Safonov 	try_accept("Wrong snd id", port++, DEFAULT_TEST_PASSWORD,
125ed9d09b3SDmitry Safonov 		   this_ip_dest, -1, 101, 100, 0,
126ed9d09b3SDmitry Safonov 		   "TCPAOGood", TEST_CNT_GOOD, FAULT_TIMEOUT);
127ed9d09b3SDmitry Safonov 
128ed9d09b3SDmitry Safonov 	try_accept("Different maclen", port++, DEFAULT_TEST_PASSWORD,
129ed9d09b3SDmitry Safonov 		   this_ip_dest, -1, 100, 100, 8,
130ed9d09b3SDmitry Safonov 		   "TCPAOBad", TEST_CNT_BAD, FAULT_TIMEOUT);
131ed9d09b3SDmitry Safonov 
132ed9d09b3SDmitry Safonov 	try_accept("Server: Wrong addr", port++, DEFAULT_TEST_PASSWORD,
133ed9d09b3SDmitry Safonov 		   wrong_addr, -1, 100, 100, 0,
134ed9d09b3SDmitry Safonov 		   "TCPAOKeyNotFound", TEST_CNT_AO_KEY_NOT_FOUND, FAULT_TIMEOUT);
135ed9d09b3SDmitry Safonov 
136ed9d09b3SDmitry Safonov 	try_accept("Client: Wrong addr", port++, NULL,
137ed9d09b3SDmitry Safonov 		   this_ip_dest, -1, 100, 100, 0, NULL, 0, FAULT_TIMEOUT);
138ed9d09b3SDmitry Safonov 
139ed9d09b3SDmitry Safonov 	try_accept("rcv id != snd id", port++, DEFAULT_TEST_PASSWORD,
140ed9d09b3SDmitry Safonov 		   this_ip_dest, -1, 200, 100, 0,
141ed9d09b3SDmitry Safonov 		   "TCPAOGood", TEST_CNT_GOOD, 0);
142ed9d09b3SDmitry Safonov 
143ed9d09b3SDmitry Safonov 	if (inet_pton(TEST_FAMILY, TEST_NETWORK, &network_addr) != 1)
144ed9d09b3SDmitry Safonov 		test_error("Can't convert ip address %s", TEST_NETWORK);
145ed9d09b3SDmitry Safonov 
146ed9d09b3SDmitry Safonov 	try_accept("Server: prefix match", port++, DEFAULT_TEST_PASSWORD,
147ed9d09b3SDmitry Safonov 		   network_addr, 16, 100, 100, 0,
148ed9d09b3SDmitry Safonov 		   "TCPAOGood", TEST_CNT_GOOD, 0);
149ed9d09b3SDmitry Safonov 
150ed9d09b3SDmitry Safonov 	try_accept("Client: prefix match", port++, DEFAULT_TEST_PASSWORD,
151ed9d09b3SDmitry Safonov 		   this_ip_dest, -1, 100, 100, 0,
152ed9d09b3SDmitry Safonov 		   "TCPAOGood", TEST_CNT_GOOD, 0);
153ed9d09b3SDmitry Safonov 
154ed9d09b3SDmitry Safonov 	/* client exits */
155ed9d09b3SDmitry Safonov 	synchronize_threads();
156ed9d09b3SDmitry Safonov 	return NULL;
157ed9d09b3SDmitry Safonov }
158ed9d09b3SDmitry Safonov 
try_connect(const char * tst_name,unsigned int port,const char * pwd,union tcp_addr addr,uint8_t prefix,uint8_t sndid,uint8_t rcvid,test_cnt cnt_expected,fault_t inj)159ed9d09b3SDmitry Safonov static void try_connect(const char *tst_name, unsigned int port,
160ed9d09b3SDmitry Safonov 			const char *pwd, union tcp_addr addr, uint8_t prefix,
161ed9d09b3SDmitry Safonov 			uint8_t sndid, uint8_t rcvid,
162ed9d09b3SDmitry Safonov 			test_cnt cnt_expected, fault_t inj)
163ed9d09b3SDmitry Safonov {
164ed9d09b3SDmitry Safonov 	struct tcp_ao_counters ao_cnt1, ao_cnt2;
165ed9d09b3SDmitry Safonov 	time_t timeout;
166ed9d09b3SDmitry Safonov 	int sk, ret;
167ed9d09b3SDmitry Safonov 
168ed9d09b3SDmitry Safonov 	sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
169ed9d09b3SDmitry Safonov 	if (sk < 0)
170ed9d09b3SDmitry Safonov 		test_error("socket()");
171ed9d09b3SDmitry Safonov 
172ed9d09b3SDmitry Safonov 	if (pwd && test_add_key(sk, pwd, addr, prefix, sndid, rcvid))
173ed9d09b3SDmitry Safonov 		test_error("setsockopt(TCP_AO_ADD_KEY)");
174ed9d09b3SDmitry Safonov 
175ed9d09b3SDmitry Safonov 	if (pwd && test_get_tcp_ao_counters(sk, &ao_cnt1))
176ed9d09b3SDmitry Safonov 		test_error("test_get_tcp_ao_counters()");
177ed9d09b3SDmitry Safonov 
178ed9d09b3SDmitry Safonov 	synchronize_threads(); /* preparations done */
179ed9d09b3SDmitry Safonov 
180ed9d09b3SDmitry Safonov 	timeout = fault(TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC;
181ed9d09b3SDmitry Safonov 	ret = _test_connect_socket(sk, this_ip_dest, port, timeout);
182ed9d09b3SDmitry Safonov 
183ed9d09b3SDmitry Safonov 	if (ret < 0) {
184ed9d09b3SDmitry Safonov 		if (fault(KEYREJECT) && ret == -EKEYREJECTED) {
185ed9d09b3SDmitry Safonov 			test_ok("%s: connect() was prevented", tst_name);
186ed9d09b3SDmitry Safonov 		} else if (ret == -ETIMEDOUT && fault(TIMEOUT)) {
187ed9d09b3SDmitry Safonov 			test_ok("%s", tst_name);
188ed9d09b3SDmitry Safonov 		} else if (ret == -ECONNREFUSED &&
189ed9d09b3SDmitry Safonov 				(fault(TIMEOUT) || fault(KEYREJECT))) {
190ed9d09b3SDmitry Safonov 			test_ok("%s: refused to connect", tst_name);
191ed9d09b3SDmitry Safonov 		} else {
192ed9d09b3SDmitry Safonov 			test_error("%s: connect() returned %d", tst_name, ret);
193ed9d09b3SDmitry Safonov 		}
194ed9d09b3SDmitry Safonov 		goto out;
195ed9d09b3SDmitry Safonov 	}
196ed9d09b3SDmitry Safonov 
197ed9d09b3SDmitry Safonov 	if (fault(TIMEOUT) || fault(KEYREJECT))
198ed9d09b3SDmitry Safonov 		test_fail("%s: connected", tst_name);
199ed9d09b3SDmitry Safonov 	else
200ed9d09b3SDmitry Safonov 		test_ok("%s: connected", tst_name);
201ed9d09b3SDmitry Safonov 	if (pwd && ret > 0) {
202ed9d09b3SDmitry Safonov 		if (test_get_tcp_ao_counters(sk, &ao_cnt2))
203ed9d09b3SDmitry Safonov 			test_error("test_get_tcp_ao_counters()");
204ed9d09b3SDmitry Safonov 		test_tcp_ao_counters_cmp(tst_name, &ao_cnt1, &ao_cnt2, cnt_expected);
205ed9d09b3SDmitry Safonov 	}
206ed9d09b3SDmitry Safonov out:
207ed9d09b3SDmitry Safonov 	synchronize_threads(); /* close() */
208ed9d09b3SDmitry Safonov 
209ed9d09b3SDmitry Safonov 	if (ret > 0)
210ed9d09b3SDmitry Safonov 		close(sk);
211ed9d09b3SDmitry Safonov }
212ed9d09b3SDmitry Safonov 
client_fn(void * arg)213ed9d09b3SDmitry Safonov static void *client_fn(void *arg)
214ed9d09b3SDmitry Safonov {
215ed9d09b3SDmitry Safonov 	union tcp_addr wrong_addr, network_addr;
216ed9d09b3SDmitry Safonov 	unsigned int port = test_server_port;
217ed9d09b3SDmitry Safonov 
218ed9d09b3SDmitry Safonov 	if (inet_pton(TEST_FAMILY, TEST_WRONG_IP, &wrong_addr) != 1)
219ed9d09b3SDmitry Safonov 		test_error("Can't convert ip address %s", TEST_WRONG_IP);
220ed9d09b3SDmitry Safonov 
221ed9d09b3SDmitry Safonov 	try_connect("Non-AO server + AO client", port++, DEFAULT_TEST_PASSWORD,
222ed9d09b3SDmitry Safonov 			this_ip_dest, -1, 100, 100, 0, FAULT_TIMEOUT);
223ed9d09b3SDmitry Safonov 
224ed9d09b3SDmitry Safonov 	try_connect("AO server + Non-AO client", port++, NULL,
225ed9d09b3SDmitry Safonov 			this_ip_dest, -1, 100, 100, 0, FAULT_TIMEOUT);
226ed9d09b3SDmitry Safonov 
227ed9d09b3SDmitry Safonov 	try_connect("Wrong password", port++, DEFAULT_TEST_PASSWORD,
228ed9d09b3SDmitry Safonov 			this_ip_dest, -1, 100, 100, 0, FAULT_TIMEOUT);
229ed9d09b3SDmitry Safonov 
230ed9d09b3SDmitry Safonov 	try_connect("Wrong rcv id", port++, DEFAULT_TEST_PASSWORD,
231ed9d09b3SDmitry Safonov 			this_ip_dest, -1, 100, 100, 0, FAULT_TIMEOUT);
232ed9d09b3SDmitry Safonov 
233ed9d09b3SDmitry Safonov 	try_connect("Wrong snd id", port++, DEFAULT_TEST_PASSWORD,
234ed9d09b3SDmitry Safonov 			this_ip_dest, -1, 100, 100, 0, FAULT_TIMEOUT);
235ed9d09b3SDmitry Safonov 
236ed9d09b3SDmitry Safonov 	try_connect("Different maclen", port++, DEFAULT_TEST_PASSWORD,
237ed9d09b3SDmitry Safonov 			this_ip_dest, -1, 100, 100, 0, FAULT_TIMEOUT);
238ed9d09b3SDmitry Safonov 
239ed9d09b3SDmitry Safonov 	try_connect("Server: Wrong addr", port++, DEFAULT_TEST_PASSWORD,
240ed9d09b3SDmitry Safonov 			this_ip_dest, -1, 100, 100, 0, FAULT_TIMEOUT);
241ed9d09b3SDmitry Safonov 
242ed9d09b3SDmitry Safonov 	try_connect("Client: Wrong addr", port++, DEFAULT_TEST_PASSWORD,
243ed9d09b3SDmitry Safonov 			wrong_addr, -1, 100, 100, 0, FAULT_KEYREJECT);
244ed9d09b3SDmitry Safonov 
245ed9d09b3SDmitry Safonov 	try_connect("rcv id != snd id", port++, DEFAULT_TEST_PASSWORD,
246ed9d09b3SDmitry Safonov 			this_ip_dest, -1, 100, 200, TEST_CNT_GOOD, 0);
247ed9d09b3SDmitry Safonov 
248ed9d09b3SDmitry Safonov 	if (inet_pton(TEST_FAMILY, TEST_NETWORK, &network_addr) != 1)
249ed9d09b3SDmitry Safonov 		test_error("Can't convert ip address %s", TEST_NETWORK);
250ed9d09b3SDmitry Safonov 
251ed9d09b3SDmitry Safonov 	try_connect("Server: prefix match", port++, DEFAULT_TEST_PASSWORD,
252ed9d09b3SDmitry Safonov 			this_ip_dest, -1, 100, 100, TEST_CNT_GOOD, 0);
253ed9d09b3SDmitry Safonov 
254ed9d09b3SDmitry Safonov 	try_connect("Client: prefix match", port++, DEFAULT_TEST_PASSWORD,
255ed9d09b3SDmitry Safonov 			network_addr, 16, 100, 100, TEST_CNT_GOOD, 0);
256ed9d09b3SDmitry Safonov 
257ed9d09b3SDmitry Safonov 	return NULL;
258ed9d09b3SDmitry Safonov }
259ed9d09b3SDmitry Safonov 
main(int argc,char * argv[])260ed9d09b3SDmitry Safonov int main(int argc, char *argv[])
261ed9d09b3SDmitry Safonov {
262ed9d09b3SDmitry Safonov 	test_init(21, server_fn, client_fn);
263ed9d09b3SDmitry Safonov 	return 0;
264ed9d09b3SDmitry Safonov }
265