1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include "cgroup_helpers.h"
4 
5 #include <linux/tcp.h>
6 #include "sockopt_sk.skel.h"
7 
8 #ifndef SOL_TCP
9 #define SOL_TCP IPPROTO_TCP
10 #endif
11 
12 #define SOL_CUSTOM			0xdeadbeef
13 
14 static int getsetsockopt(void)
15 {
16 	int fd, err;
17 	union {
18 		char u8[4];
19 		__u32 u32;
20 		char cc[16]; /* TCP_CA_NAME_MAX */
21 		struct tcp_zerocopy_receive zc;
22 	} buf = {};
23 	socklen_t optlen;
24 	char *big_buf = NULL;
25 
26 	fd = socket(AF_INET, SOCK_STREAM, 0);
27 	if (fd < 0) {
28 		log_err("Failed to create socket");
29 		return -1;
30 	}
31 
32 	/* IP_TOS - BPF bypass */
33 
34 	optlen = getpagesize() * 2;
35 	big_buf = calloc(1, optlen);
36 	if (!big_buf) {
37 		log_err("Couldn't allocate two pages");
38 		goto err;
39 	}
40 
41 	*(int *)big_buf = 0x08;
42 	err = setsockopt(fd, SOL_IP, IP_TOS, big_buf, optlen);
43 	if (err) {
44 		log_err("Failed to call setsockopt(IP_TOS)");
45 		goto err;
46 	}
47 
48 	memset(big_buf, 0, optlen);
49 	optlen = 1;
50 	err = getsockopt(fd, SOL_IP, IP_TOS, big_buf, &optlen);
51 	if (err) {
52 		log_err("Failed to call getsockopt(IP_TOS)");
53 		goto err;
54 	}
55 
56 	if (*big_buf != 0x08) {
57 		log_err("Unexpected getsockopt(IP_TOS) optval 0x%x != 0x08",
58 			(int)*big_buf);
59 		goto err;
60 	}
61 
62 	/* IP_TTL - EPERM */
63 
64 	buf.u8[0] = 1;
65 	err = setsockopt(fd, SOL_IP, IP_TTL, &buf, 1);
66 	if (!err || errno != EPERM) {
67 		log_err("Unexpected success from setsockopt(IP_TTL)");
68 		goto err;
69 	}
70 
71 	/* SOL_CUSTOM - handled by BPF */
72 
73 	buf.u8[0] = 0x01;
74 	err = setsockopt(fd, SOL_CUSTOM, 0, &buf, 1);
75 	if (err) {
76 		log_err("Failed to call setsockopt");
77 		goto err;
78 	}
79 
80 	buf.u32 = 0x00;
81 	optlen = 4;
82 	err = getsockopt(fd, SOL_CUSTOM, 0, &buf, &optlen);
83 	if (err) {
84 		log_err("Failed to call getsockopt");
85 		goto err;
86 	}
87 
88 	if (optlen != 1) {
89 		log_err("Unexpected optlen %d != 1", optlen);
90 		goto err;
91 	}
92 	if (buf.u8[0] != 0x01) {
93 		log_err("Unexpected buf[0] 0x%02x != 0x01", buf.u8[0]);
94 		goto err;
95 	}
96 
97 	/* IP_FREEBIND - BPF can't access optval past PAGE_SIZE */
98 
99 	optlen = getpagesize() * 2;
100 	memset(big_buf, 0, optlen);
101 
102 	err = setsockopt(fd, SOL_IP, IP_FREEBIND, big_buf, optlen);
103 	if (err != 0) {
104 		log_err("Failed to call setsockopt, ret=%d", err);
105 		goto err;
106 	}
107 
108 	err = getsockopt(fd, SOL_IP, IP_FREEBIND, big_buf, &optlen);
109 	if (err != 0) {
110 		log_err("Failed to call getsockopt, ret=%d", err);
111 		goto err;
112 	}
113 
114 	if (optlen != 1 || *(__u8 *)big_buf != 0x55) {
115 		log_err("Unexpected IP_FREEBIND getsockopt, optlen=%d, optval=0x%x",
116 			optlen, *(__u8 *)big_buf);
117 	}
118 
119 	/* SO_SNDBUF is overwritten */
120 
121 	buf.u32 = 0x01010101;
122 	err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf, 4);
123 	if (err) {
124 		log_err("Failed to call setsockopt(SO_SNDBUF)");
125 		goto err;
126 	}
127 
128 	buf.u32 = 0x00;
129 	optlen = 4;
130 	err = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf, &optlen);
131 	if (err) {
132 		log_err("Failed to call getsockopt(SO_SNDBUF)");
133 		goto err;
134 	}
135 
136 	if (buf.u32 != 0x55AA*2) {
137 		log_err("Unexpected getsockopt(SO_SNDBUF) 0x%x != 0x55AA*2",
138 			buf.u32);
139 		goto err;
140 	}
141 
142 	/* TCP_CONGESTION can extend the string */
143 
144 	strcpy(buf.cc, "nv");
145 	err = setsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, strlen("nv"));
146 	if (err) {
147 		log_err("Failed to call setsockopt(TCP_CONGESTION)");
148 		goto err;
149 	}
150 
151 
152 	optlen = sizeof(buf.cc);
153 	err = getsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, &optlen);
154 	if (err) {
155 		log_err("Failed to call getsockopt(TCP_CONGESTION)");
156 		goto err;
157 	}
158 
159 	if (strcmp(buf.cc, "cubic") != 0) {
160 		log_err("Unexpected getsockopt(TCP_CONGESTION) %s != %s",
161 			buf.cc, "cubic");
162 		goto err;
163 	}
164 
165 	/* TCP_ZEROCOPY_RECEIVE triggers */
166 	memset(&buf, 0, sizeof(buf));
167 	optlen = sizeof(buf.zc);
168 	err = getsockopt(fd, SOL_TCP, TCP_ZEROCOPY_RECEIVE, &buf, &optlen);
169 	if (err) {
170 		log_err("Unexpected getsockopt(TCP_ZEROCOPY_RECEIVE) err=%d errno=%d",
171 			err, errno);
172 		goto err;
173 	}
174 
175 	memset(&buf, 0, sizeof(buf));
176 	buf.zc.address = 12345; /* Not page aligned. Rejected by tcp_zerocopy_receive() */
177 	optlen = sizeof(buf.zc);
178 	errno = 0;
179 	err = getsockopt(fd, SOL_TCP, TCP_ZEROCOPY_RECEIVE, &buf, &optlen);
180 	if (errno != EINVAL) {
181 		log_err("Unexpected getsockopt(TCP_ZEROCOPY_RECEIVE) err=%d errno=%d",
182 			err, errno);
183 		goto err;
184 	}
185 
186 	free(big_buf);
187 	close(fd);
188 	return 0;
189 err:
190 	free(big_buf);
191 	close(fd);
192 	return -1;
193 }
194 
195 static void run_test(int cgroup_fd)
196 {
197 	struct sockopt_sk *skel;
198 
199 	skel = sockopt_sk__open_and_load();
200 	if (!ASSERT_OK_PTR(skel, "skel_load"))
201 		goto cleanup;
202 
203 	skel->bss->page_size = getpagesize();
204 
205 	skel->links._setsockopt =
206 		bpf_program__attach_cgroup(skel->progs._setsockopt, cgroup_fd);
207 	if (!ASSERT_OK_PTR(skel->links._setsockopt, "setsockopt_link"))
208 		goto cleanup;
209 
210 	skel->links._getsockopt =
211 		bpf_program__attach_cgroup(skel->progs._getsockopt, cgroup_fd);
212 	if (!ASSERT_OK_PTR(skel->links._getsockopt, "getsockopt_link"))
213 		goto cleanup;
214 
215 	ASSERT_OK(getsetsockopt(), "getsetsockopt");
216 
217 cleanup:
218 	sockopt_sk__destroy(skel);
219 }
220 
221 void test_sockopt_sk(void)
222 {
223 	int cgroup_fd;
224 
225 	cgroup_fd = test__join_cgroup("/sockopt_sk");
226 	if (CHECK_FAIL(cgroup_fd < 0))
227 		return;
228 
229 	run_test(cgroup_fd);
230 	close(cgroup_fd);
231 }
232