brintos

brintos / linux-shallow public Read only

0
0
Text · 14.5 KiB · 0ffda96 Raw
596 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <alloca.h>3#include <fcntl.h>4#include <inttypes.h>5#include <string.h>6#include "../../../../../include/linux/kernel.h"7#include "../../../../../include/linux/stringify.h"8#include "aolib.h"9 10const unsigned int test_server_port = 7010;11int __test_listen_socket(int backlog, void *addr, size_t addr_sz)12{13	int err, sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);14	long flags;15 16	if (sk < 0)17		test_error("socket()");18 19	err = setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, veth_name,20			 strlen(veth_name) + 1);21	if (err < 0)22		test_error("setsockopt(SO_BINDTODEVICE)");23 24	if (bind(sk, (struct sockaddr *)addr, addr_sz) < 0)25		test_error("bind()");26 27	flags = fcntl(sk, F_GETFL);28	if ((flags < 0) || (fcntl(sk, F_SETFL, flags | O_NONBLOCK) < 0))29		test_error("fcntl()");30 31	if (listen(sk, backlog))32		test_error("listen()");33 34	return sk;35}36 37int test_wait_fd(int sk, time_t sec, bool write)38{39	struct timeval tv = { .tv_sec = sec };40	struct timeval *ptv = NULL;41	fd_set fds, efds;42	int ret;43	socklen_t slen = sizeof(ret);44 45	FD_ZERO(&fds);46	FD_SET(sk, &fds);47	FD_ZERO(&efds);48	FD_SET(sk, &efds);49 50	if (sec)51		ptv = &tv;52 53	errno = 0;54	if (write)55		ret = select(sk + 1, NULL, &fds, &efds, ptv);56	else57		ret = select(sk + 1, &fds, NULL, &efds, ptv);58	if (ret < 0)59		return -errno;60	if (ret == 0) {61		errno = ETIMEDOUT;62		return -ETIMEDOUT;63	}64 65	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &slen))66		return -errno;67	if (ret)68		return -ret;69	return 0;70}71 72int __test_connect_socket(int sk, const char *device,73			  void *addr, size_t addr_sz, time_t timeout)74{75	long flags;76	int err;77 78	if (device != NULL) {79		err = setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, device,80				 strlen(device) + 1);81		if (err < 0)82			test_error("setsockopt(SO_BINDTODEVICE, %s)", device);83	}84 85	if (!timeout) {86		err = connect(sk, addr, addr_sz);87		if (err) {88			err = -errno;89			goto out;90		}91		return 0;92	}93 94	flags = fcntl(sk, F_GETFL);95	if ((flags < 0) || (fcntl(sk, F_SETFL, flags | O_NONBLOCK) < 0))96		test_error("fcntl()");97 98	if (connect(sk, addr, addr_sz) < 0) {99		if (errno != EINPROGRESS) {100			err = -errno;101			goto out;102		}103		if (timeout < 0)104			return sk;105		err = test_wait_fd(sk, timeout, 1);106		if (err)107			goto out;108	}109	return sk;110 111out:112	close(sk);113	return err;114}115 116int __test_set_md5(int sk, void *addr, size_t addr_sz, uint8_t prefix,117		   int vrf, const char *password)118{119	size_t pwd_len = strlen(password);120	struct tcp_md5sig md5sig = {};121 122	md5sig.tcpm_keylen = pwd_len;123	memcpy(md5sig.tcpm_key, password, pwd_len);124	md5sig.tcpm_flags = TCP_MD5SIG_FLAG_PREFIX;125	md5sig.tcpm_prefixlen = prefix;126	if (vrf >= 0) {127		md5sig.tcpm_flags |= TCP_MD5SIG_FLAG_IFINDEX;128		md5sig.tcpm_ifindex = (uint8_t)vrf;129	}130	memcpy(&md5sig.tcpm_addr, addr, addr_sz);131 132	errno = 0;133	return setsockopt(sk, IPPROTO_TCP, TCP_MD5SIG_EXT,134			&md5sig, sizeof(md5sig));135}136 137 138int test_prepare_key_sockaddr(struct tcp_ao_add *ao, const char *alg,139		void *addr, size_t addr_sz, bool set_current, bool set_rnext,140		uint8_t prefix, uint8_t vrf, uint8_t sndid, uint8_t rcvid,141		uint8_t maclen, uint8_t keyflags,142		uint8_t keylen, const char *key)143{144	memset(ao, 0, sizeof(struct tcp_ao_add));145 146	ao->set_current	= !!set_current;147	ao->set_rnext	= !!set_rnext;148	ao->prefix	= prefix;149	ao->sndid	= sndid;150	ao->rcvid	= rcvid;151	ao->maclen	= maclen;152	ao->keyflags	= keyflags;153	ao->keylen	= keylen;154	ao->ifindex	= vrf;155 156	memcpy(&ao->addr, addr, addr_sz);157 158	if (strlen(alg) > 64)159		return -ENOBUFS;160	strncpy(ao->alg_name, alg, 64);161 162	memcpy(ao->key, key,163	       (keylen > TCP_AO_MAXKEYLEN) ? TCP_AO_MAXKEYLEN : keylen);164	return 0;165}166 167static int test_get_ao_keys_nr(int sk)168{169	struct tcp_ao_getsockopt tmp = {};170	socklen_t tmp_sz = sizeof(tmp);171	int ret;172 173	tmp.nkeys  = 1;174	tmp.get_all = 1;175 176	ret = getsockopt(sk, IPPROTO_TCP, TCP_AO_GET_KEYS, &tmp, &tmp_sz);177	if (ret)178		return -errno;179	return (int)tmp.nkeys;180}181 182int test_get_one_ao(int sk, struct tcp_ao_getsockopt *out,183		void *addr, size_t addr_sz, uint8_t prefix,184		uint8_t sndid, uint8_t rcvid)185{186	struct tcp_ao_getsockopt tmp = {};187	socklen_t tmp_sz = sizeof(tmp);188	int ret;189 190	memcpy(&tmp.addr, addr, addr_sz);191	tmp.prefix = prefix;192	tmp.sndid  = sndid;193	tmp.rcvid  = rcvid;194	tmp.nkeys  = 1;195 196	ret = getsockopt(sk, IPPROTO_TCP, TCP_AO_GET_KEYS, &tmp, &tmp_sz);197	if (ret)198		return ret;199	if (tmp.nkeys != 1)200		return -E2BIG;201	*out = tmp;202	return 0;203}204 205int test_get_ao_info(int sk, struct tcp_ao_info_opt *out)206{207	socklen_t sz = sizeof(*out);208 209	out->reserved = 0;210	out->reserved2 = 0;211	if (getsockopt(sk, IPPROTO_TCP, TCP_AO_INFO, out, &sz))212		return -errno;213	if (sz != sizeof(*out))214		return -EMSGSIZE;215	return 0;216}217 218int test_set_ao_info(int sk, struct tcp_ao_info_opt *in)219{220	socklen_t sz = sizeof(*in);221 222	in->reserved = 0;223	in->reserved2 = 0;224	if (setsockopt(sk, IPPROTO_TCP, TCP_AO_INFO, in, sz))225		return -errno;226	return 0;227}228 229int test_cmp_getsockopt_setsockopt(const struct tcp_ao_add *a,230				   const struct tcp_ao_getsockopt *b)231{232	bool is_kdf_aes_128_cmac = false;233	bool is_cmac_aes = false;234 235	if (!strcmp("cmac(aes128)", a->alg_name)) {236		is_kdf_aes_128_cmac = (a->keylen != 16);237		is_cmac_aes = true;238	}239 240#define __cmp_ao(member)						\241do {									\242	if (b->member != a->member) {					\243		test_fail("getsockopt(): " __stringify(member) " %u != %u",	\244				b->member, a->member);			\245		return -1;						\246	}								\247} while(0)248	__cmp_ao(sndid);249	__cmp_ao(rcvid);250	__cmp_ao(prefix);251	__cmp_ao(keyflags);252	__cmp_ao(ifindex);253	if (a->maclen) {254		__cmp_ao(maclen);255	} else if (b->maclen != 12) {256		test_fail("getsockopt(): expected default maclen 12, but it's %u",257				b->maclen);258		return -1;259	}260	if (!is_kdf_aes_128_cmac) {261		__cmp_ao(keylen);262	} else if (b->keylen != 16) {263		test_fail("getsockopt(): expected keylen 16 for cmac(aes128), but it's %u",264				b->keylen);265		return -1;266	}267#undef __cmp_ao268	if (!is_kdf_aes_128_cmac && memcmp(b->key, a->key, a->keylen)) {269		test_fail("getsockopt(): returned key is different `%s' != `%s'",270				b->key, a->key);271		return -1;272	}273	if (memcmp(&b->addr, &a->addr, sizeof(b->addr))) {274		test_fail("getsockopt(): returned address is different");275		return -1;276	}277	if (!is_cmac_aes && strcmp(b->alg_name, a->alg_name)) {278		test_fail("getsockopt(): returned algorithm %s is different than %s", b->alg_name, a->alg_name);279		return -1;280	}281	if (is_cmac_aes && strcmp(b->alg_name, "cmac(aes)")) {282		test_fail("getsockopt(): returned algorithm %s is different than cmac(aes)", b->alg_name);283		return -1;284	}285	/* For a established key rotation test don't add a key with286	 * set_current = 1, as it's likely to change by peer's request;287	 * rather use setsockopt(TCP_AO_INFO)288	 */289	if (a->set_current != b->is_current) {290		test_fail("getsockopt(): returned key is not Current_key");291		return -1;292	}293	if (a->set_rnext != b->is_rnext) {294		test_fail("getsockopt(): returned key is not RNext_key");295		return -1;296	}297 298	return 0;299}300 301int test_cmp_getsockopt_setsockopt_ao(const struct tcp_ao_info_opt *a,302				      const struct tcp_ao_info_opt *b)303{304	/* No check for ::current_key, as it may change by the peer */305	if (a->ao_required != b->ao_required) {306		test_fail("getsockopt(): returned ao doesn't have ao_required");307		return -1;308	}309	if (a->accept_icmps != b->accept_icmps) {310		test_fail("getsockopt(): returned ao doesn't accept ICMPs");311		return -1;312	}313	if (a->set_rnext && a->rnext != b->rnext) {314		test_fail("getsockopt(): RNext KeyID has changed");315		return -1;316	}317#define __cmp_cnt(member)						\318do {									\319	if (b->member != a->member) {					\320		test_fail("getsockopt(): " __stringify(member) " %llu != %llu",	\321				b->member, a->member);			\322		return -1;						\323	}								\324} while(0)325	if (a->set_counters) {326		__cmp_cnt(pkt_good);327		__cmp_cnt(pkt_bad);328		__cmp_cnt(pkt_key_not_found);329		__cmp_cnt(pkt_ao_required);330		__cmp_cnt(pkt_dropped_icmp);331	}332#undef __cmp_cnt333	return 0;334}335 336int test_get_tcp_ao_counters(int sk, struct tcp_ao_counters *out)337{338	struct tcp_ao_getsockopt *key_dump;339	socklen_t key_dump_sz = sizeof(*key_dump);340	struct tcp_ao_info_opt info = {};341	bool c1, c2, c3, c4, c5;342	struct netstat *ns;343	int err, nr_keys;344 345	memset(out, 0, sizeof(*out));346 347	/* per-netns */348	ns = netstat_read();349	out->netns_ao_good = netstat_get(ns, "TCPAOGood", &c1);350	out->netns_ao_bad = netstat_get(ns, "TCPAOBad", &c2);351	out->netns_ao_key_not_found = netstat_get(ns, "TCPAOKeyNotFound", &c3);352	out->netns_ao_required = netstat_get(ns, "TCPAORequired", &c4);353	out->netns_ao_dropped_icmp = netstat_get(ns, "TCPAODroppedIcmps", &c5);354	netstat_free(ns);355	if (c1 || c2 || c3 || c4 || c5)356		return -EOPNOTSUPP;357 358	err = test_get_ao_info(sk, &info);359	if (err)360		return err;361 362	/* per-socket */363	out->ao_info_pkt_good		= info.pkt_good;364	out->ao_info_pkt_bad		= info.pkt_bad;365	out->ao_info_pkt_key_not_found	= info.pkt_key_not_found;366	out->ao_info_pkt_ao_required	= info.pkt_ao_required;367	out->ao_info_pkt_dropped_icmp	= info.pkt_dropped_icmp;368 369	/* per-key */370	nr_keys = test_get_ao_keys_nr(sk);371	if (nr_keys < 0)372		return nr_keys;373	if (nr_keys == 0)374		test_error("test_get_ao_keys_nr() == 0");375	out->nr_keys = (size_t)nr_keys;376	key_dump = calloc(nr_keys, key_dump_sz);377	if (!key_dump)378		return -errno;379 380	key_dump[0].nkeys = nr_keys;381	key_dump[0].get_all = 1;382	err = getsockopt(sk, IPPROTO_TCP, TCP_AO_GET_KEYS,383			 key_dump, &key_dump_sz);384	if (err) {385		free(key_dump);386		return -errno;387	}388 389	out->key_cnts = calloc(nr_keys, sizeof(out->key_cnts[0]));390	if (!out->key_cnts) {391		free(key_dump);392		return -errno;393	}394 395	while (nr_keys--) {396		out->key_cnts[nr_keys].sndid = key_dump[nr_keys].sndid;397		out->key_cnts[nr_keys].rcvid = key_dump[nr_keys].rcvid;398		out->key_cnts[nr_keys].pkt_good = key_dump[nr_keys].pkt_good;399		out->key_cnts[nr_keys].pkt_bad = key_dump[nr_keys].pkt_bad;400	}401	free(key_dump);402 403	return 0;404}405 406int __test_tcp_ao_counters_cmp(const char *tst_name,407			       struct tcp_ao_counters *before,408			       struct tcp_ao_counters *after,409			       test_cnt expected)410{411#define __cmp_ao(cnt, expecting_inc)					\412do {									\413	if (before->cnt > after->cnt) {					\414		test_fail("%s: Decreased counter " __stringify(cnt) " %" PRIu64 " > %" PRIu64, \415			  tst_name ?: "", before->cnt, after->cnt);		\416		return -1;						\417	}								\418	if ((before->cnt != after->cnt) != (expecting_inc)) {		\419		test_fail("%s: Counter " __stringify(cnt) " was %sexpected to increase %" PRIu64 " => %" PRIu64, \420			  tst_name ?: "", (expecting_inc) ? "" : "not ",	\421			  before->cnt, after->cnt);			\422		return -1;						\423	}								\424} while(0)425 426	errno = 0;427	/* per-netns */428	__cmp_ao(netns_ao_good, !!(expected & TEST_CNT_NS_GOOD));429	__cmp_ao(netns_ao_bad, !!(expected & TEST_CNT_NS_BAD));430	__cmp_ao(netns_ao_key_not_found,431		 !!(expected & TEST_CNT_NS_KEY_NOT_FOUND));432	__cmp_ao(netns_ao_required, !!(expected & TEST_CNT_NS_AO_REQUIRED));433	__cmp_ao(netns_ao_dropped_icmp,434		 !!(expected & TEST_CNT_NS_DROPPED_ICMP));435	/* per-socket */436	__cmp_ao(ao_info_pkt_good, !!(expected & TEST_CNT_SOCK_GOOD));437	__cmp_ao(ao_info_pkt_bad, !!(expected & TEST_CNT_SOCK_BAD));438	__cmp_ao(ao_info_pkt_key_not_found,439		 !!(expected & TEST_CNT_SOCK_KEY_NOT_FOUND));440	__cmp_ao(ao_info_pkt_ao_required, !!(expected & TEST_CNT_SOCK_AO_REQUIRED));441	__cmp_ao(ao_info_pkt_dropped_icmp,442		 !!(expected & TEST_CNT_SOCK_DROPPED_ICMP));443	return 0;444#undef __cmp_ao445}446 447int test_tcp_ao_key_counters_cmp(const char *tst_name,448				 struct tcp_ao_counters *before,449				 struct tcp_ao_counters *after,450				 test_cnt expected,451				 int sndid, int rcvid)452{453	size_t i;454#define __cmp_ao(i, cnt, expecting_inc)					\455do {									\456	if (before->key_cnts[i].cnt > after->key_cnts[i].cnt) {		\457		test_fail("%s: Decreased counter " __stringify(cnt) " %" PRIu64 " > %" PRIu64 " for key %u:%u", \458			  tst_name ?: "", before->key_cnts[i].cnt,	\459			  after->key_cnts[i].cnt,			\460			  before->key_cnts[i].sndid,			\461			  before->key_cnts[i].rcvid);			\462		return -1;						\463	}								\464	if ((before->key_cnts[i].cnt != after->key_cnts[i].cnt) != (expecting_inc)) {		\465		test_fail("%s: Counter " __stringify(cnt) " was %sexpected to increase %" PRIu64 " => %" PRIu64 " for key %u:%u", \466			  tst_name ?: "", (expecting_inc) ? "" : "not ",\467			  before->key_cnts[i].cnt,			\468			  after->key_cnts[i].cnt,			\469			  before->key_cnts[i].sndid,			\470			  before->key_cnts[i].rcvid);			\471		return -1;						\472	}								\473} while(0)474 475	if (before->nr_keys != after->nr_keys) {476		test_fail("%s: Keys changed on the socket %zu != %zu",477			  tst_name, before->nr_keys, after->nr_keys);478		return -1;479	}480 481	/* per-key */482	i = before->nr_keys;483	while (i--) {484		if (sndid >= 0 && before->key_cnts[i].sndid != sndid)485			continue;486		if (rcvid >= 0 && before->key_cnts[i].rcvid != rcvid)487			continue;488		__cmp_ao(i, pkt_good, !!(expected & TEST_CNT_KEY_GOOD));489		__cmp_ao(i, pkt_bad, !!(expected & TEST_CNT_KEY_BAD));490	}491	return 0;492#undef __cmp_ao493}494 495void test_tcp_ao_counters_free(struct tcp_ao_counters *cnts)496{497	free(cnts->key_cnts);498}499 500#define TEST_BUF_SIZE 4096501ssize_t test_server_run(int sk, ssize_t quota, time_t timeout_sec)502{503	ssize_t total = 0;504 505	do {506		char buf[TEST_BUF_SIZE];507		ssize_t bytes, sent;508		int ret;509 510		ret = test_wait_fd(sk, timeout_sec, 0);511		if (ret)512			return ret;513 514		bytes = recv(sk, buf, sizeof(buf), 0);515 516		if (bytes < 0)517			test_error("recv(): %zd", bytes);518		if (bytes == 0)519			break;520 521		ret = test_wait_fd(sk, timeout_sec, 1);522		if (ret)523			return ret;524 525		sent = send(sk, buf, bytes, 0);526		if (sent == 0)527			break;528		if (sent != bytes)529			test_error("send()");530		total += bytes;531	} while (!quota || total < quota);532 533	return total;534}535 536ssize_t test_client_loop(int sk, char *buf, size_t buf_sz,537			 const size_t msg_len, time_t timeout_sec)538{539	char msg[msg_len];540	int nodelay = 1;541	size_t i;542 543	if (setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)))544		test_error("setsockopt(TCP_NODELAY)");545 546	for (i = 0; i < buf_sz; i += min(msg_len, buf_sz - i)) {547		size_t sent, bytes = min(msg_len, buf_sz - i);548		int ret;549 550		ret = test_wait_fd(sk, timeout_sec, 1);551		if (ret)552			return ret;553 554		sent = send(sk, buf + i, bytes, 0);555		if (sent == 0)556			break;557		if (sent != bytes)558			test_error("send()");559 560		bytes = 0;561		do {562			ssize_t got;563 564			ret = test_wait_fd(sk, timeout_sec, 0);565			if (ret)566				return ret;567 568			got = recv(sk, msg + bytes, sizeof(msg) - bytes, 0);569			if (got <= 0)570				return i;571			bytes += got;572		} while (bytes < sent);573		if (bytes > sent)574			test_error("recv(): %zd > %zd", bytes, sent);575		if (memcmp(buf + i, msg, bytes) != 0) {576			test_fail("received message differs");577			return -1;578		}579	}580	return i;581}582 583int test_client_verify(int sk, const size_t msg_len, const size_t nr,584		       time_t timeout_sec)585{586	size_t buf_sz = msg_len * nr;587	char *buf = alloca(buf_sz);588	ssize_t ret;589 590	randomize_buffer(buf, buf_sz);591	ret = test_client_loop(sk, buf, buf_sz, msg_len, timeout_sec);592	if (ret < 0)593		return (int)ret;594	return ret != buf_sz ? -1 : 0;595}596