1// SPDX-License-Identifier: GPL-2.0
2#include <test_progs.h>
3#include "cgroup_helpers.h"
4
5#include "sockopt_multi.skel.h"
6
7static int run_getsockopt_test(struct sockopt_multi *obj, int cg_parent,
8			       int cg_child, int sock_fd)
9{
10	struct bpf_link *link_parent = NULL;
11	struct bpf_link *link_child = NULL;
12	socklen_t optlen;
13	__u8 buf;
14	int err;
15
16	/* Set IP_TOS to the expected value (0x80). */
17
18	buf = 0x80;
19	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
20	if (err < 0) {
21		log_err("Failed to call setsockopt(IP_TOS)");
22		goto detach;
23	}
24
25	buf = 0x00;
26	optlen = 1;
27	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
28	if (err) {
29		log_err("Failed to call getsockopt(IP_TOS)");
30		goto detach;
31	}
32
33	if (buf != 0x80) {
34		log_err("Unexpected getsockopt 0x%x != 0x80 without BPF", buf);
35		err = -1;
36		goto detach;
37	}
38
39	/* Attach child program and make sure it returns new value:
40	 * - kernel:      -> 0x80
41	 * - child:  0x80 -> 0x90
42	 */
43
44	link_child = bpf_program__attach_cgroup(obj->progs._getsockopt_child,
45						cg_child);
46	if (!ASSERT_OK_PTR(link_child, "cg-attach-getsockopt_child"))
47		goto detach;
48
49	buf = 0x00;
50	optlen = 1;
51	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
52	if (err) {
53		log_err("Failed to call getsockopt(IP_TOS)");
54		goto detach;
55	}
56
57	if (buf != 0x90) {
58		log_err("Unexpected getsockopt 0x%x != 0x90", buf);
59		err = -1;
60		goto detach;
61	}
62
63	/* Attach parent program and make sure it returns new value:
64	 * - kernel:      -> 0x80
65	 * - child:  0x80 -> 0x90
66	 * - parent: 0x90 -> 0xA0
67	 */
68
69	link_parent = bpf_program__attach_cgroup(obj->progs._getsockopt_parent,
70						 cg_parent);
71	if (!ASSERT_OK_PTR(link_parent, "cg-attach-getsockopt_parent"))
72		goto detach;
73
74	buf = 0x00;
75	optlen = 1;
76	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
77	if (err) {
78		log_err("Failed to call getsockopt(IP_TOS)");
79		goto detach;
80	}
81
82	if (buf != 0xA0) {
83		log_err("Unexpected getsockopt 0x%x != 0xA0", buf);
84		err = -1;
85		goto detach;
86	}
87
88	/* Setting unexpected initial sockopt should return EPERM:
89	 * - kernel: -> 0x40
90	 * - child:  unexpected 0x40, EPERM
91	 * - parent: unexpected 0x40, EPERM
92	 */
93
94	buf = 0x40;
95	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
96	if (err < 0) {
97		log_err("Failed to call setsockopt(IP_TOS)");
98		goto detach;
99	}
100
101	buf = 0x00;
102	optlen = 1;
103	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
104	if (!err) {
105		log_err("Unexpected success from getsockopt(IP_TOS)");
106		goto detach;
107	}
108
109	/* Detach child program and make sure we still get EPERM:
110	 * - kernel: -> 0x40
111	 * - parent: unexpected 0x40, EPERM
112	 */
113
114	bpf_link__destroy(link_child);
115	link_child = NULL;
116
117	buf = 0x00;
118	optlen = 1;
119	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
120	if (!err) {
121		log_err("Unexpected success from getsockopt(IP_TOS)");
122		goto detach;
123	}
124
125	/* Set initial value to the one the parent program expects:
126	 * - kernel:      -> 0x90
127	 * - parent: 0x90 -> 0xA0
128	 */
129
130	buf = 0x90;
131	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
132	if (err < 0) {
133		log_err("Failed to call setsockopt(IP_TOS)");
134		goto detach;
135	}
136
137	buf = 0x00;
138	optlen = 1;
139	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
140	if (err) {
141		log_err("Failed to call getsockopt(IP_TOS)");
142		goto detach;
143	}
144
145	if (buf != 0xA0) {
146		log_err("Unexpected getsockopt 0x%x != 0xA0", buf);
147		err = -1;
148		goto detach;
149	}
150
151detach:
152	bpf_link__destroy(link_child);
153	bpf_link__destroy(link_parent);
154
155	return err;
156}
157
158static int run_setsockopt_test(struct sockopt_multi *obj, int cg_parent,
159			       int cg_child, int sock_fd)
160{
161	struct bpf_link *link_parent = NULL;
162	struct bpf_link *link_child = NULL;
163	socklen_t optlen;
164	__u8 buf;
165	int err;
166
167	/* Set IP_TOS to the expected value (0x80). */
168
169	buf = 0x80;
170	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
171	if (err < 0) {
172		log_err("Failed to call setsockopt(IP_TOS)");
173		goto detach;
174	}
175
176	buf = 0x00;
177	optlen = 1;
178	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
179	if (err) {
180		log_err("Failed to call getsockopt(IP_TOS)");
181		goto detach;
182	}
183
184	if (buf != 0x80) {
185		log_err("Unexpected getsockopt 0x%x != 0x80 without BPF", buf);
186		err = -1;
187		goto detach;
188	}
189
190	/* Attach child program and make sure it adds 0x10. */
191
192	link_child = bpf_program__attach_cgroup(obj->progs._setsockopt,
193						cg_child);
194	if (!ASSERT_OK_PTR(link_child, "cg-attach-setsockopt_child"))
195		goto detach;
196
197	buf = 0x80;
198	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
199	if (err < 0) {
200		log_err("Failed to call setsockopt(IP_TOS)");
201		goto detach;
202	}
203
204	buf = 0x00;
205	optlen = 1;
206	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
207	if (err) {
208		log_err("Failed to call getsockopt(IP_TOS)");
209		goto detach;
210	}
211
212	if (buf != 0x80 + 0x10) {
213		log_err("Unexpected getsockopt 0x%x != 0x80 + 0x10", buf);
214		err = -1;
215		goto detach;
216	}
217
218	/* Attach parent program and make sure it adds another 0x10. */
219
220	link_parent = bpf_program__attach_cgroup(obj->progs._setsockopt,
221						 cg_parent);
222	if (!ASSERT_OK_PTR(link_parent, "cg-attach-setsockopt_parent"))
223		goto detach;
224
225	buf = 0x80;
226	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
227	if (err < 0) {
228		log_err("Failed to call setsockopt(IP_TOS)");
229		goto detach;
230	}
231
232	buf = 0x00;
233	optlen = 1;
234	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
235	if (err) {
236		log_err("Failed to call getsockopt(IP_TOS)");
237		goto detach;
238	}
239
240	if (buf != 0x80 + 2 * 0x10) {
241		log_err("Unexpected getsockopt 0x%x != 0x80 + 2 * 0x10", buf);
242		err = -1;
243		goto detach;
244	}
245
246detach:
247	bpf_link__destroy(link_child);
248	bpf_link__destroy(link_parent);
249
250	return err;
251}
252
253void test_sockopt_multi(void)
254{
255	int cg_parent = -1, cg_child = -1;
256	struct sockopt_multi *obj = NULL;
257	int sock_fd = -1;
258
259	cg_parent = test__join_cgroup("/parent");
260	if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent"))
261		goto out;
262
263	cg_child = test__join_cgroup("/parent/child");
264	if (!ASSERT_GE(cg_child, 0, "join_cgroup /parent/child"))
265		goto out;
266
267	obj = sockopt_multi__open_and_load();
268	if (!ASSERT_OK_PTR(obj, "skel-load"))
269		goto out;
270
271	obj->bss->page_size = sysconf(_SC_PAGESIZE);
272
273	sock_fd = socket(AF_INET, SOCK_STREAM, 0);
274	if (!ASSERT_GE(sock_fd, 0, "socket"))
275		goto out;
276
277	ASSERT_OK(run_getsockopt_test(obj, cg_parent, cg_child, sock_fd), "getsockopt_test");
278	ASSERT_OK(run_setsockopt_test(obj, cg_parent, cg_child, sock_fd), "setsockopt_test");
279
280out:
281	close(sock_fd);
282	sockopt_multi__destroy(obj);
283	close(cg_child);
284	close(cg_parent);
285}
286