1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include "cgroup_helpers.h"
4 
5 #define SOL_CUSTOM			0xdeadbeef
6 
7 static int getsetsockopt(void)
8 {
9 	int fd, err;
10 	union {
11 		char u8[4];
12 		__u32 u32;
13 		char cc[16]; /* TCP_CA_NAME_MAX */
14 	} buf = {};
15 	socklen_t optlen;
16 
17 	fd = socket(AF_INET, SOCK_STREAM, 0);
18 	if (fd < 0) {
19 		log_err("Failed to create socket");
20 		return -1;
21 	}
22 
23 	/* IP_TOS - BPF bypass */
24 
25 	buf.u8[0] = 0x08;
26 	err = setsockopt(fd, SOL_IP, IP_TOS, &buf, 1);
27 	if (err) {
28 		log_err("Failed to call setsockopt(IP_TOS)");
29 		goto err;
30 	}
31 
32 	buf.u8[0] = 0x00;
33 	optlen = 1;
34 	err = getsockopt(fd, SOL_IP, IP_TOS, &buf, &optlen);
35 	if (err) {
36 		log_err("Failed to call getsockopt(IP_TOS)");
37 		goto err;
38 	}
39 
40 	if (buf.u8[0] != 0x08) {
41 		log_err("Unexpected getsockopt(IP_TOS) buf[0] 0x%02x != 0x08",
42 			buf.u8[0]);
43 		goto err;
44 	}
45 
46 	/* IP_TTL - EPERM */
47 
48 	buf.u8[0] = 1;
49 	err = setsockopt(fd, SOL_IP, IP_TTL, &buf, 1);
50 	if (!err || errno != EPERM) {
51 		log_err("Unexpected success from setsockopt(IP_TTL)");
52 		goto err;
53 	}
54 
55 	/* SOL_CUSTOM - handled by BPF */
56 
57 	buf.u8[0] = 0x01;
58 	err = setsockopt(fd, SOL_CUSTOM, 0, &buf, 1);
59 	if (err) {
60 		log_err("Failed to call setsockopt");
61 		goto err;
62 	}
63 
64 	buf.u32 = 0x00;
65 	optlen = 4;
66 	err = getsockopt(fd, SOL_CUSTOM, 0, &buf, &optlen);
67 	if (err) {
68 		log_err("Failed to call getsockopt");
69 		goto err;
70 	}
71 
72 	if (optlen != 1) {
73 		log_err("Unexpected optlen %d != 1", optlen);
74 		goto err;
75 	}
76 	if (buf.u8[0] != 0x01) {
77 		log_err("Unexpected buf[0] 0x%02x != 0x01", buf.u8[0]);
78 		goto err;
79 	}
80 
81 	/* SO_SNDBUF is overwritten */
82 
83 	buf.u32 = 0x01010101;
84 	err = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf, 4);
85 	if (err) {
86 		log_err("Failed to call setsockopt(SO_SNDBUF)");
87 		goto err;
88 	}
89 
90 	buf.u32 = 0x00;
91 	optlen = 4;
92 	err = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf, &optlen);
93 	if (err) {
94 		log_err("Failed to call getsockopt(SO_SNDBUF)");
95 		goto err;
96 	}
97 
98 	if (buf.u32 != 0x55AA*2) {
99 		log_err("Unexpected getsockopt(SO_SNDBUF) 0x%x != 0x55AA*2",
100 			buf.u32);
101 		goto err;
102 	}
103 
104 	/* TCP_CONGESTION can extend the string */
105 
106 	strcpy(buf.cc, "nv");
107 	err = setsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, strlen("nv"));
108 	if (err) {
109 		log_err("Failed to call setsockopt(TCP_CONGESTION)");
110 		goto err;
111 	}
112 
113 
114 	optlen = sizeof(buf.cc);
115 	err = getsockopt(fd, SOL_TCP, TCP_CONGESTION, &buf, &optlen);
116 	if (err) {
117 		log_err("Failed to call getsockopt(TCP_CONGESTION)");
118 		goto err;
119 	}
120 
121 	if (strcmp(buf.cc, "cubic") != 0) {
122 		log_err("Unexpected getsockopt(TCP_CONGESTION) %s != %s",
123 			buf.cc, "cubic");
124 		goto err;
125 	}
126 
127 	close(fd);
128 	return 0;
129 err:
130 	close(fd);
131 	return -1;
132 }
133 
134 static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title)
135 {
136 	enum bpf_attach_type attach_type;
137 	enum bpf_prog_type prog_type;
138 	struct bpf_program *prog;
139 	int err;
140 
141 	err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
142 	if (err) {
143 		log_err("Failed to deduct types for %s BPF program", title);
144 		return -1;
145 	}
146 
147 	prog = bpf_object__find_program_by_title(obj, title);
148 	if (!prog) {
149 		log_err("Failed to find %s BPF program", title);
150 		return -1;
151 	}
152 
153 	err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd,
154 			      attach_type, 0);
155 	if (err) {
156 		log_err("Failed to attach %s BPF program", title);
157 		return -1;
158 	}
159 
160 	return 0;
161 }
162 
163 static void run_test(int cgroup_fd)
164 {
165 	struct bpf_prog_load_attr attr = {
166 		.file = "./sockopt_sk.o",
167 	};
168 	struct bpf_object *obj;
169 	int ignored;
170 	int err;
171 
172 	err = bpf_prog_load_xattr(&attr, &obj, &ignored);
173 	if (CHECK_FAIL(err))
174 		return;
175 
176 	err = prog_attach(obj, cgroup_fd, "cgroup/getsockopt");
177 	if (CHECK_FAIL(err))
178 		goto close_bpf_object;
179 
180 	err = prog_attach(obj, cgroup_fd, "cgroup/setsockopt");
181 	if (CHECK_FAIL(err))
182 		goto close_bpf_object;
183 
184 	CHECK_FAIL(getsetsockopt());
185 
186 close_bpf_object:
187 	bpf_object__close(obj);
188 }
189 
190 void test_sockopt_sk(void)
191 {
192 	int cgroup_fd;
193 
194 	cgroup_fd = test__join_cgroup("/sockopt_sk");
195 	if (CHECK_FAIL(cgroup_fd < 0))
196 		return;
197 
198 	run_test(cgroup_fd);
199 	close(cgroup_fd);
200 }
201