/* SPDX-License-Identifier: GPL-2.0 */ /* * TCP-AO selftest library. Provides helpers to unshare network * namespaces, create veth, assign ip addresses, set routes, * manipulate socket options, read network counter and etc. * Author: Dmitry Safonov */ #ifndef _AOLIB_H_ #define _AOLIB_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include "../../../../../include/linux/stringify.h" #include "../../../../../include/linux/bits.h" #ifndef SOL_TCP /* can't include as including */ # define SOL_TCP 6 /* TCP level */ #endif /* Working around ksft, see the comment in lib/setup.c */ extern void __test_msg(const char *buf); extern void __test_ok(const char *buf); extern void __test_fail(const char *buf); extern void __test_xfail(const char *buf); extern void __test_error(const char *buf); extern void __test_skip(const char *buf); __attribute__((__format__(__printf__, 2, 3))) static inline void __test_print(void (*fn)(const char *), const char *fmt, ...) { #define TEST_MSG_BUFFER_SIZE 4096 char buf[TEST_MSG_BUFFER_SIZE]; va_list arg; va_start(arg, fmt); vsnprintf(buf, sizeof(buf), fmt, arg); va_end(arg); fn(buf); } #define test_print(fmt, ...) \ __test_print(__test_msg, "%ld[%s:%u] " fmt "\n", \ syscall(SYS_gettid), \ __FILE__, __LINE__, ##__VA_ARGS__) #define test_ok(fmt, ...) \ __test_print(__test_ok, fmt "\n", ##__VA_ARGS__) #define test_skip(fmt, ...) \ __test_print(__test_skip, fmt "\n", ##__VA_ARGS__) #define test_xfail(fmt, ...) \ __test_print(__test_xfail, fmt "\n", ##__VA_ARGS__) #define test_fail(fmt, ...) \ do { \ if (errno) \ __test_print(__test_fail, fmt ": %m\n", ##__VA_ARGS__); \ else \ __test_print(__test_fail, fmt "\n", ##__VA_ARGS__); \ test_failed(); \ } while (0) #define KSFT_FAIL 1 #define test_error(fmt, ...) \ do { \ if (errno) \ __test_print(__test_error, "%ld[%s:%u] " fmt ": %m\n", \ syscall(SYS_gettid), __FILE__, __LINE__, \ ##__VA_ARGS__); \ else \ __test_print(__test_error, "%ld[%s:%u] " fmt "\n", \ syscall(SYS_gettid), __FILE__, __LINE__, \ ##__VA_ARGS__); \ exit(KSFT_FAIL); \ } while (0) enum test_fault { FAULT_TIMEOUT = 1, FAULT_KEYREJECT, FAULT_PREINSTALL_AO, FAULT_PREINSTALL_MD5, FAULT_POSTINSTALL, FAULT_BUSY, FAULT_CURRNEXT, FAULT_FIXME, }; typedef enum test_fault fault_t; enum test_needs_kconfig { KCONFIG_NET_NS = 0, /* required */ KCONFIG_VETH, /* required */ KCONFIG_TCP_AO, /* required */ KCONFIG_TCP_MD5, /* optional, for TCP-MD5 features */ KCONFIG_NET_VRF, /* optional, for L3/VRF testing */ __KCONFIG_LAST__ }; extern bool kernel_config_has(enum test_needs_kconfig k); extern const char *tests_skip_reason[__KCONFIG_LAST__]; static inline bool should_skip_test(const char *tst_name, enum test_needs_kconfig k) { if (kernel_config_has(k)) return false; test_skip("%s: %s", tst_name, tests_skip_reason[k]); return true; } union tcp_addr { struct in_addr a4; struct in6_addr a6; }; typedef void *(*thread_fn)(void *); extern void test_failed(void); extern void __test_init(unsigned int ntests, int family, unsigned int prefix, union tcp_addr addr1, union tcp_addr addr2, thread_fn peer1, thread_fn peer2); static inline void test_init2(unsigned int ntests, thread_fn peer1, thread_fn peer2, int family, unsigned int prefix, const char *addr1, const char *addr2) { union tcp_addr taddr1, taddr2; if (inet_pton(family, addr1, &taddr1) != 1) test_error("Can't convert ip address %s", addr1); if (inet_pton(family, addr2, &taddr2) != 1) test_error("Can't convert ip address %s", addr2); __test_init(ntests, family, prefix, taddr1, taddr2, peer1, peer2); } extern void test_add_destructor(void (*d)(void)); /* To adjust optmem socket limit, approximately estimate a number, * that is bigger than sizeof(struct tcp_ao_key). */ #define KERNEL_TCP_AO_KEY_SZ_ROUND_UP 300 extern void test_set_optmem(size_t value); extern size_t test_get_optmem(void); extern const struct sockaddr_in6 addr_any6; extern const struct sockaddr_in addr_any4; #ifdef IPV6_TEST # define __TEST_CLIENT_IP(n) ("2001:db8:" __stringify(n) "::1") # define TEST_CLIENT_IP __TEST_CLIENT_IP(1) # define TEST_WRONG_IP "2001:db8:253::1" # define TEST_SERVER_IP "2001:db8:254::1" # define TEST_NETWORK "2001::" # define TEST_PREFIX 128 # define TEST_FAMILY AF_INET6 # define SOCKADDR_ANY addr_any6 # define sockaddr_af struct sockaddr_in6 #else # define __TEST_CLIENT_IP(n) ("10.0." __stringify(n) ".1") # define TEST_CLIENT_IP __TEST_CLIENT_IP(1) # define TEST_WRONG_IP "10.0.253.1" # define TEST_SERVER_IP "10.0.254.1" # define TEST_NETWORK "10.0.0.0" # define TEST_PREFIX 32 # define TEST_FAMILY AF_INET # define SOCKADDR_ANY addr_any4 # define sockaddr_af struct sockaddr_in #endif static inline union tcp_addr gen_tcp_addr(union tcp_addr net, size_t n) { union tcp_addr ret = net; #ifdef IPV6_TEST ret.a6.s6_addr32[3] = htonl(n & (BIT(32) - 1)); ret.a6.s6_addr32[2] = htonl((n >> 32) & (BIT(32) - 1)); #else ret.a4.s_addr = htonl(ntohl(net.a4.s_addr) + n); #endif return ret; } static inline void tcp_addr_to_sockaddr_in(void *dest, const union tcp_addr *src, unsigned int port) { sockaddr_af *out = dest; memset(out, 0, sizeof(*out)); #ifdef IPV6_TEST out->sin6_family = AF_INET6; out->sin6_port = port; out->sin6_addr = src->a6; #else out->sin_family = AF_INET; out->sin_port = port; out->sin_addr = src->a4; #endif } static inline void test_init(unsigned int ntests, thread_fn peer1, thread_fn peer2) { test_init2(ntests, peer1, peer2, TEST_FAMILY, TEST_PREFIX, TEST_SERVER_IP, TEST_CLIENT_IP); } extern void synchronize_threads(void); extern void switch_ns(int fd); extern __thread union tcp_addr this_ip_addr; extern __thread union tcp_addr this_ip_dest; extern int test_family; extern void randomize_buffer(void *buf, size_t buflen); extern int open_netns(void); extern int unshare_open_netns(void); extern const char veth_name[]; extern int add_veth(const char *name, int nsfda, int nsfdb); extern int add_vrf(const char *name, uint32_t tabid, int ifindex, int nsfd); extern int ip_addr_add(const char *intf, int family, union tcp_addr addr, uint8_t prefix); extern int ip_route_add(const char *intf, int family, union tcp_addr src, union tcp_addr dst); extern int ip_route_add_vrf(const char *intf, int family, union tcp_addr src, union tcp_addr dst, uint8_t vrf); extern int link_set_up(const char *intf); extern const unsigned int test_server_port; extern int test_wait_fd(int sk, time_t sec, bool write); extern int __test_connect_socket(int sk, const char *device, void *addr, size_t addr_sz, time_t timeout); extern int __test_listen_socket(int backlog, void *addr, size_t addr_sz); static inline int test_listen_socket(const union tcp_addr taddr, unsigned int port, int backlog) { sockaddr_af addr; tcp_addr_to_sockaddr_in(&addr, &taddr, htons(port)); return __test_listen_socket(backlog, (void *)&addr, sizeof(addr)); } /* * In order for selftests to work under CONFIG_CRYPTO_FIPS=y, * the password should be loger than 14 bytes, see hmac_setkey() */ #define TEST_TCP_AO_MINKEYLEN 14 #define DEFAULT_TEST_PASSWORD "In this hour, I do not believe that any darkness will endure." #ifndef DEFAULT_TEST_ALGO #define DEFAULT_TEST_ALGO "cmac(aes128)" #endif #ifdef IPV6_TEST #define DEFAULT_TEST_PREFIX 128 #else #define DEFAULT_TEST_PREFIX 32 #endif /* * Timeout on syscalls where failure is not expected. * You may want to rise it if the test machine is very busy. */ #ifndef TEST_TIMEOUT_SEC #define TEST_TIMEOUT_SEC 5 #endif /* * Timeout on connect() where a failure is expected. * If set to 0 - kernel will try to retransmit SYN number of times, set in * /proc/sys/net/ipv4/tcp_syn_retries * By default set to 1 to make tests pass faster on non-busy machine. */ #ifndef TEST_RETRANSMIT_SEC #define TEST_RETRANSMIT_SEC 1 #endif static inline int _test_connect_socket(int sk, const union tcp_addr taddr, unsigned int port, time_t timeout) { sockaddr_af addr; tcp_addr_to_sockaddr_in(&addr, &taddr, htons(port)); return __test_connect_socket(sk, veth_name, (void *)&addr, sizeof(addr), timeout); } static inline int test_connect_socket(int sk, const union tcp_addr taddr, unsigned int port) { return _test_connect_socket(sk, taddr, port, TEST_TIMEOUT_SEC); } extern int __test_set_md5(int sk, void *addr, size_t addr_sz, uint8_t prefix, int vrf, const char *password); static inline int test_set_md5(int sk, const union tcp_addr in_addr, uint8_t prefix, int vrf, const char *password) { sockaddr_af addr; if (prefix > DEFAULT_TEST_PREFIX) prefix = DEFAULT_TEST_PREFIX; tcp_addr_to_sockaddr_in(&addr, &in_addr, 0); return __test_set_md5(sk, (void *)&addr, sizeof(addr), prefix, vrf, password); } extern int test_prepare_key_sockaddr(struct tcp_ao_add *ao, const char *alg, void *addr, size_t addr_sz, bool set_current, bool set_rnext, uint8_t prefix, uint8_t vrf, uint8_t sndid, uint8_t rcvid, uint8_t maclen, uint8_t keyflags, uint8_t keylen, const char *key); static inline int test_prepare_key(struct tcp_ao_add *ao, const char *alg, union tcp_addr taddr, bool set_current, bool set_rnext, uint8_t prefix, uint8_t vrf, uint8_t sndid, uint8_t rcvid, uint8_t maclen, uint8_t keyflags, uint8_t keylen, const char *key) { sockaddr_af addr; tcp_addr_to_sockaddr_in(&addr, &taddr, 0); return test_prepare_key_sockaddr(ao, alg, (void *)&addr, sizeof(addr), set_current, set_rnext, prefix, vrf, sndid, rcvid, maclen, keyflags, keylen, key); } static inline int test_prepare_def_key(struct tcp_ao_add *ao, const char *key, uint8_t keyflags, union tcp_addr in_addr, uint8_t prefix, uint8_t vrf, uint8_t sndid, uint8_t rcvid) { if (prefix > DEFAULT_TEST_PREFIX) prefix = DEFAULT_TEST_PREFIX; return test_prepare_key(ao, DEFAULT_TEST_ALGO, in_addr, false, false, prefix, vrf, sndid, rcvid, 0, keyflags, strlen(key), key); } extern int test_get_one_ao(int sk, struct tcp_ao_getsockopt *out, void *addr, size_t addr_sz, uint8_t prefix, uint8_t sndid, uint8_t rcvid); extern int test_get_ao_info(int sk, struct tcp_ao_info_opt *out); extern int test_set_ao_info(int sk, struct tcp_ao_info_opt *in); extern int test_cmp_getsockopt_setsockopt(const struct tcp_ao_add *a, const struct tcp_ao_getsockopt *b); extern int test_cmp_getsockopt_setsockopt_ao(const struct tcp_ao_info_opt *a, const struct tcp_ao_info_opt *b); static inline int test_verify_socket_key(int sk, struct tcp_ao_add *key) { struct tcp_ao_getsockopt key2 = {}; int err; err = test_get_one_ao(sk, &key2, &key->addr, sizeof(key->addr), key->prefix, key->sndid, key->rcvid); if (err) return err; return test_cmp_getsockopt_setsockopt(key, &key2); } static inline int test_add_key_vrf(int sk, const char *key, uint8_t keyflags, union tcp_addr in_addr, uint8_t prefix, uint8_t vrf, uint8_t sndid, uint8_t rcvid) { struct tcp_ao_add tmp = {}; int err; err = test_prepare_def_key(&tmp, key, keyflags, in_addr, prefix, vrf, sndid, rcvid); if (err) return err; err = setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &tmp, sizeof(tmp)); if (err < 0) return -errno; return test_verify_socket_key(sk, &tmp); } static inline int test_add_key(int sk, const char *key, union tcp_addr in_addr, uint8_t prefix, uint8_t sndid, uint8_t rcvid) { return test_add_key_vrf(sk, key, 0, in_addr, prefix, 0, sndid, rcvid); } static inline int test_verify_socket_ao(int sk, struct tcp_ao_info_opt *ao) { struct tcp_ao_info_opt ao2 = {}; int err; err = test_get_ao_info(sk, &ao2); if (err) return err; return test_cmp_getsockopt_setsockopt_ao(ao, &ao2); } static inline int test_set_ao_flags(int sk, bool ao_required, bool accept_icmps) { struct tcp_ao_info_opt ao = {}; int err; err = test_get_ao_info(sk, &ao); /* Maybe ao_info wasn't allocated yet */ if (err && err != -ENOENT) return err; ao.ao_required = !!ao_required; ao.accept_icmps = !!accept_icmps; err = test_set_ao_info(sk, &ao); if (err) return err; return test_verify_socket_ao(sk, &ao); } extern ssize_t test_server_run(int sk, ssize_t quota, time_t timeout_sec); extern ssize_t test_client_loop(int sk, char *buf, size_t buf_sz, const size_t msg_len, time_t timeout_sec); extern int test_client_verify(int sk, const size_t msg_len, const size_t nr, time_t timeout_sec); struct tcp_ao_key_counters { uint8_t sndid; uint8_t rcvid; uint64_t pkt_good; uint64_t pkt_bad; }; struct tcp_ao_counters { /* per-netns */ uint64_t netns_ao_good; uint64_t netns_ao_bad; uint64_t netns_ao_key_not_found; uint64_t netns_ao_required; uint64_t netns_ao_dropped_icmp; /* per-socket */ uint64_t ao_info_pkt_good; uint64_t ao_info_pkt_bad; uint64_t ao_info_pkt_key_not_found; uint64_t ao_info_pkt_ao_required; uint64_t ao_info_pkt_dropped_icmp; /* per-key */ size_t nr_keys; struct tcp_ao_key_counters *key_cnts; }; extern int test_get_tcp_ao_counters(int sk, struct tcp_ao_counters *out); #define TEST_CNT_KEY_GOOD BIT(0) #define TEST_CNT_KEY_BAD BIT(1) #define TEST_CNT_SOCK_GOOD BIT(2) #define TEST_CNT_SOCK_BAD BIT(3) #define TEST_CNT_SOCK_KEY_NOT_FOUND BIT(4) #define TEST_CNT_SOCK_AO_REQUIRED BIT(5) #define TEST_CNT_SOCK_DROPPED_ICMP BIT(6) #define TEST_CNT_NS_GOOD BIT(7) #define TEST_CNT_NS_BAD BIT(8) #define TEST_CNT_NS_KEY_NOT_FOUND BIT(9) #define TEST_CNT_NS_AO_REQUIRED BIT(10) #define TEST_CNT_NS_DROPPED_ICMP BIT(11) typedef uint16_t test_cnt; #define TEST_CNT_AO_GOOD (TEST_CNT_SOCK_GOOD | TEST_CNT_NS_GOOD) #define TEST_CNT_AO_BAD (TEST_CNT_SOCK_BAD | TEST_CNT_NS_BAD) #define TEST_CNT_AO_KEY_NOT_FOUND (TEST_CNT_SOCK_KEY_NOT_FOUND | \ TEST_CNT_NS_KEY_NOT_FOUND) #define TEST_CNT_AO_REQUIRED (TEST_CNT_SOCK_AO_REQUIRED | \ TEST_CNT_NS_AO_REQUIRED) #define TEST_CNT_AO_DROPPED_ICMP (TEST_CNT_SOCK_DROPPED_ICMP | \ TEST_CNT_NS_DROPPED_ICMP) #define TEST_CNT_GOOD (TEST_CNT_KEY_GOOD | TEST_CNT_AO_GOOD) #define TEST_CNT_BAD (TEST_CNT_KEY_BAD | TEST_CNT_AO_BAD) extern int __test_tcp_ao_counters_cmp(const char *tst_name, struct tcp_ao_counters *before, struct tcp_ao_counters *after, test_cnt expected); extern int test_tcp_ao_key_counters_cmp(const char *tst_name, struct tcp_ao_counters *before, struct tcp_ao_counters *after, test_cnt expected, int sndid, int rcvid); extern void test_tcp_ao_counters_free(struct tcp_ao_counters *cnts); /* * Frees buffers allocated in test_get_tcp_ao_counters(). * The function doesn't expect new keys or keys removed between calls * to test_get_tcp_ao_counters(). Check key counters manually if they * may change. */ static inline int test_tcp_ao_counters_cmp(const char *tst_name, struct tcp_ao_counters *before, struct tcp_ao_counters *after, test_cnt expected) { int ret; ret = __test_tcp_ao_counters_cmp(tst_name, before, after, expected); if (ret) goto out; ret = test_tcp_ao_key_counters_cmp(tst_name, before, after, expected, -1, -1); out: test_tcp_ao_counters_free(before); test_tcp_ao_counters_free(after); return ret; } struct netstat; extern struct netstat *netstat_read(void); extern void netstat_free(struct netstat *ns); extern void netstat_print_diff(struct netstat *nsa, struct netstat *nsb); extern uint64_t netstat_get(struct netstat *ns, const char *name, bool *not_found); static inline uint64_t netstat_get_one(const char *name, bool *not_found) { struct netstat *ns = netstat_read(); uint64_t ret; ret = netstat_get(ns, name, not_found); netstat_free(ns); return ret; } struct tcp_sock_queue { uint32_t seq; void *buf; }; struct tcp_sock_state { struct tcp_info info; struct tcp_repair_window trw; struct tcp_sock_queue out; int outq_len; /* output queue size (not sent + not acked) */ int outq_nsd_len; /* output queue size (not sent only) */ struct tcp_sock_queue in; int inq_len; int mss; int timestamp; }; extern void __test_sock_checkpoint(int sk, struct tcp_sock_state *state, void *addr, size_t addr_size); static inline void test_sock_checkpoint(int sk, struct tcp_sock_state *state, sockaddr_af *saddr) { __test_sock_checkpoint(sk, state, saddr, sizeof(*saddr)); } extern void test_ao_checkpoint(int sk, struct tcp_ao_repair *state); extern void __test_sock_restore(int sk, const char *device, struct tcp_sock_state *state, void *saddr, void *daddr, size_t addr_size); static inline void test_sock_restore(int sk, struct tcp_sock_state *state, sockaddr_af *saddr, const union tcp_addr daddr, unsigned int dport) { sockaddr_af addr; tcp_addr_to_sockaddr_in(&addr, &daddr, htons(dport)); __test_sock_restore(sk, veth_name, state, saddr, &addr, sizeof(addr)); } extern void test_ao_restore(int sk, struct tcp_ao_repair *state); extern void test_sock_state_free(struct tcp_sock_state *state); extern void test_enable_repair(int sk); extern void test_disable_repair(int sk); extern void test_kill_sk(int sk); static inline int test_add_repaired_key(int sk, const char *key, uint8_t keyflags, union tcp_addr in_addr, uint8_t prefix, uint8_t sndid, uint8_t rcvid) { struct tcp_ao_add tmp = {}; int err; err = test_prepare_def_key(&tmp, key, keyflags, in_addr, prefix, 0, sndid, rcvid); if (err) return err; tmp.set_current = 1; tmp.set_rnext = 1; if (setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &tmp, sizeof(tmp)) < 0) return -errno; return test_verify_socket_key(sk, &tmp); } #endif /* _AOLIB_H_ */