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
getsetsockopt(void)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; /* rejected by BPF */
177 optlen = sizeof(buf.zc);
178 errno = 0;
179 err = getsockopt(fd, SOL_TCP, TCP_ZEROCOPY_RECEIVE, &buf, &optlen);
180 if (errno != EPERM) {
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
run_test(int cgroup_fd)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
test_sockopt_sk(void)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