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
has_net_ns(int * err)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
has_veth(int * err)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
has_tcp_ao(int * err)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
has_tcp_md5(int * err)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
has_vrfs(int * err)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
has_ftrace(int * err)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
kernel_config_has(enum test_needs_kconfig k)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