1 // SPDX-License-Identifier: GPL-2.0 2 /* Check what features does the kernel support (where the selftest is running). 3 * Somewhat inspired by CRIU kerndat/kdat kernel features detector. 4 */ 5 #include <pthread.h> 6 #include "aolib.h" 7 8 struct kconfig_t { 9 int _error; /* negative errno if not supported */ 10 int (*check_kconfig)(int *error); 11 }; 12 13 static int has_net_ns(int *err) 14 { 15 if (access("/proc/self/ns/net", F_OK) < 0) { 16 *err = errno; 17 if (errno == ENOENT) 18 return 0; 19 test_print("Unable to access /proc/self/ns/net: %m"); 20 return -errno; 21 } 22 return *err = errno = 0; 23 } 24 25 static int has_veth(int *err) 26 { 27 int orig_netns, ns_a, ns_b; 28 29 orig_netns = open_netns(); 30 ns_a = unshare_open_netns(); 31 ns_b = unshare_open_netns(); 32 33 *err = add_veth("check_veth", ns_a, ns_b); 34 35 switch_ns(orig_netns); 36 close(orig_netns); 37 close(ns_a); 38 close(ns_b); 39 return 0; 40 } 41 42 static int has_tcp_ao(int *err) 43 { 44 struct sockaddr_in addr = { 45 .sin_family = test_family, 46 }; 47 struct tcp_ao_add tmp = {}; 48 const char *password = DEFAULT_TEST_PASSWORD; 49 int sk, ret = 0; 50 51 sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); 52 if (sk < 0) { 53 test_print("socket(): %m"); 54 return -errno; 55 } 56 57 tmp.sndid = 100; 58 tmp.rcvid = 100; 59 tmp.keylen = strlen(password); 60 memcpy(tmp.key, password, strlen(password)); 61 strcpy(tmp.alg_name, "hmac(sha1)"); 62 memcpy(&tmp.addr, &addr, sizeof(addr)); 63 *err = 0; 64 if (setsockopt(sk, IPPROTO_TCP, TCP_AO_ADD_KEY, &tmp, sizeof(tmp)) < 0) { 65 *err = -errno; 66 if (errno != ENOPROTOOPT) 67 ret = -errno; 68 } 69 close(sk); 70 return ret; 71 } 72 73 static int has_tcp_md5(int *err) 74 { 75 union tcp_addr addr_any = {}; 76 int sk, ret = 0; 77 78 sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 79 if (sk < 0) { 80 test_print("socket(): %m"); 81 return -errno; 82 } 83 84 /* 85 * Under CONFIG_CRYPTO_FIPS=y it fails with ENOMEM, rather with 86 * anything more descriptive. Oh well. 87 */ 88 *err = 0; 89 if (test_set_md5(sk, addr_any, 0, -1, DEFAULT_TEST_PASSWORD)) { 90 *err = -errno; 91 if (errno != ENOPROTOOPT && errno == ENOMEM) { 92 test_print("setsockopt(TCP_MD5SIG_EXT): %m"); 93 ret = -errno; 94 } 95 } 96 close(sk); 97 return ret; 98 } 99 100 static int has_vrfs(int *err) 101 { 102 int orig_netns, ns_test, ret = 0; 103 104 orig_netns = open_netns(); 105 ns_test = unshare_open_netns(); 106 107 *err = add_vrf("ksft-check", 55, 101, ns_test); 108 if (*err && *err != -EOPNOTSUPP) { 109 test_print("Failed to add a VRF: %d", *err); 110 ret = *err; 111 } 112 113 switch_ns(orig_netns); 114 close(orig_netns); 115 close(ns_test); 116 return ret; 117 } 118 119 static int has_ftrace(int *err) 120 { 121 *err = test_setup_tracing(); 122 return 0; 123 } 124 125 #define KCONFIG_UNKNOWN 1 126 static pthread_mutex_t kconfig_lock = PTHREAD_MUTEX_INITIALIZER; 127 static struct kconfig_t kconfig[__KCONFIG_LAST__] = { 128 { KCONFIG_UNKNOWN, has_net_ns }, 129 { KCONFIG_UNKNOWN, has_veth }, 130 { KCONFIG_UNKNOWN, has_tcp_ao }, 131 { KCONFIG_UNKNOWN, has_tcp_md5 }, 132 { KCONFIG_UNKNOWN, has_vrfs }, 133 { KCONFIG_UNKNOWN, has_ftrace }, 134 }; 135 136 const char *tests_skip_reason[__KCONFIG_LAST__] = { 137 "Tests require network namespaces support (CONFIG_NET_NS)", 138 "Tests require veth support (CONFIG_VETH)", 139 "Tests require TCP-AO support (CONFIG_TCP_AO)", 140 "setsockopt(TCP_MD5SIG_EXT) is not supported (CONFIG_TCP_MD5)", 141 "VRFs are not supported (CONFIG_NET_VRF)", 142 "Ftrace points are not supported (CONFIG_TRACEPOINTS)", 143 }; 144 145 bool kernel_config_has(enum test_needs_kconfig k) 146 { 147 bool ret; 148 149 pthread_mutex_lock(&kconfig_lock); 150 if (kconfig[k]._error == KCONFIG_UNKNOWN) { 151 if (kconfig[k].check_kconfig(&kconfig[k]._error)) 152 test_error("Failed to initialize kconfig %u", k); 153 } 154 ret = kconfig[k]._error == 0; 155 pthread_mutex_unlock(&kconfig_lock); 156 return ret; 157 } 158