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