Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  3/*
  4 * Copyright 2021 Google LLC.
  5 */
  6
  7#include <test_progs.h>
  8#include <cgroup_helpers.h>
  9#include <network_helpers.h>
 10
 11#include "cgroup_getset_retval_setsockopt.skel.h"
 12#include "cgroup_getset_retval_getsockopt.skel.h"
 13#include "cgroup_getset_retval_hooks.skel.h"
 14
 15#define SOL_CUSTOM	0xdeadbeef
 16
 17static int zero;
 18
 19static void test_setsockopt_set(int cgroup_fd, int sock_fd)
 20{
 21	struct cgroup_getset_retval_setsockopt *obj;
 22	struct bpf_link *link_set_eunatch = NULL;
 23
 24	obj = cgroup_getset_retval_setsockopt__open_and_load();
 25	if (!ASSERT_OK_PTR(obj, "skel-load"))
 26		return;
 27
 28	obj->bss->page_size = sysconf(_SC_PAGESIZE);
 29
 30	/* Attach setsockopt that sets EUNATCH, assert that
 31	 * we actually get that error when we run setsockopt()
 32	 */
 33	link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
 34						      cgroup_fd);
 35	if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
 36		goto close_bpf_object;
 37
 38	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
 39				   &zero, sizeof(int)), "setsockopt"))
 40		goto close_bpf_object;
 41	if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
 42		goto close_bpf_object;
 43
 44	if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
 45		goto close_bpf_object;
 46	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
 47		goto close_bpf_object;
 48
 49close_bpf_object:
 50	bpf_link__destroy(link_set_eunatch);
 51
 52	cgroup_getset_retval_setsockopt__destroy(obj);
 53}
 54
 55static void test_setsockopt_set_and_get(int cgroup_fd, int sock_fd)
 56{
 57	struct cgroup_getset_retval_setsockopt *obj;
 58	struct bpf_link *link_set_eunatch = NULL, *link_get_retval = NULL;
 59
 60	obj = cgroup_getset_retval_setsockopt__open_and_load();
 61	if (!ASSERT_OK_PTR(obj, "skel-load"))
 62		return;
 63
 64	obj->bss->page_size = sysconf(_SC_PAGESIZE);
 65
 66	/* Attach setsockopt that sets EUNATCH, and one that gets the
 67	 * previously set errno. Assert that we get the same errno back.
 68	 */
 69	link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
 70						      cgroup_fd);
 71	if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
 72		goto close_bpf_object;
 73	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
 74						     cgroup_fd);
 75	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
 76		goto close_bpf_object;
 77
 78	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
 79				   &zero, sizeof(int)), "setsockopt"))
 80		goto close_bpf_object;
 81	if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
 82		goto close_bpf_object;
 83
 84	if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
 85		goto close_bpf_object;
 86	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
 87		goto close_bpf_object;
 88	if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value"))
 89		goto close_bpf_object;
 90
 91close_bpf_object:
 92	bpf_link__destroy(link_set_eunatch);
 93	bpf_link__destroy(link_get_retval);
 94
 95	cgroup_getset_retval_setsockopt__destroy(obj);
 96}
 97
 98static void test_setsockopt_default_zero(int cgroup_fd, int sock_fd)
 99{
100	struct cgroup_getset_retval_setsockopt *obj;
101	struct bpf_link *link_get_retval = NULL;
102
103	obj = cgroup_getset_retval_setsockopt__open_and_load();
104	if (!ASSERT_OK_PTR(obj, "skel-load"))
105		return;
106
107	obj->bss->page_size = sysconf(_SC_PAGESIZE);
108
109	/* Attach setsockopt that gets the previously set errno.
110	 * Assert that, without anything setting one, we get 0.
111	 */
112	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
113						     cgroup_fd);
114	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
115		goto close_bpf_object;
116
117	if (!ASSERT_OK(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
118				  &zero, sizeof(int)), "setsockopt"))
119		goto close_bpf_object;
120
121	if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
122		goto close_bpf_object;
123	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
124		goto close_bpf_object;
125	if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
126		goto close_bpf_object;
127
128close_bpf_object:
129	bpf_link__destroy(link_get_retval);
130
131	cgroup_getset_retval_setsockopt__destroy(obj);
132}
133
134static void test_setsockopt_default_zero_and_set(int cgroup_fd, int sock_fd)
135{
136	struct cgroup_getset_retval_setsockopt *obj;
137	struct bpf_link *link_get_retval = NULL, *link_set_eunatch = NULL;
138
139	obj = cgroup_getset_retval_setsockopt__open_and_load();
140	if (!ASSERT_OK_PTR(obj, "skel-load"))
141		return;
142
143	obj->bss->page_size = sysconf(_SC_PAGESIZE);
144
145	/* Attach setsockopt that gets the previously set errno, and then
146	 * one that sets the errno to EUNATCH. Assert that the get does not
147	 * see EUNATCH set later, and does not prevent EUNATCH from being set.
148	 */
149	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
150						     cgroup_fd);
151	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
152		goto close_bpf_object;
153	link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
154						      cgroup_fd);
155	if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
156		goto close_bpf_object;
157
158	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
159				   &zero, sizeof(int)), "setsockopt"))
160		goto close_bpf_object;
161	if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
162		goto close_bpf_object;
163
164	if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
165		goto close_bpf_object;
166	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
167		goto close_bpf_object;
168	if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
169		goto close_bpf_object;
170
171close_bpf_object:
172	bpf_link__destroy(link_get_retval);
173	bpf_link__destroy(link_set_eunatch);
174
175	cgroup_getset_retval_setsockopt__destroy(obj);
176}
177
178static void test_setsockopt_override(int cgroup_fd, int sock_fd)
179{
180	struct cgroup_getset_retval_setsockopt *obj;
181	struct bpf_link *link_set_eunatch = NULL, *link_set_eisconn = NULL;
182	struct bpf_link *link_get_retval = NULL;
183
184	obj = cgroup_getset_retval_setsockopt__open_and_load();
185	if (!ASSERT_OK_PTR(obj, "skel-load"))
186		return;
187
188	obj->bss->page_size = sysconf(_SC_PAGESIZE);
189
190	/* Attach setsockopt that sets EUNATCH, then one that sets EISCONN,
191	 * and then one that gets the exported errno. Assert both the syscall
192	 * and the helper sees the last set errno.
193	 */
194	link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
195						      cgroup_fd);
196	if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
197		goto close_bpf_object;
198	link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
199						      cgroup_fd);
200	if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
201		goto close_bpf_object;
202	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
203						     cgroup_fd);
204	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
205		goto close_bpf_object;
206
207	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
208				   &zero, sizeof(int)), "setsockopt"))
209		goto close_bpf_object;
210	if (!ASSERT_EQ(errno, EISCONN, "setsockopt-errno"))
211		goto close_bpf_object;
212
213	if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
214		goto close_bpf_object;
215	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
216		goto close_bpf_object;
217	if (!ASSERT_EQ(obj->bss->retval_value, -EISCONN, "retval_value"))
218		goto close_bpf_object;
219
220close_bpf_object:
221	bpf_link__destroy(link_set_eunatch);
222	bpf_link__destroy(link_set_eisconn);
223	bpf_link__destroy(link_get_retval);
224
225	cgroup_getset_retval_setsockopt__destroy(obj);
226}
227
228static void test_setsockopt_legacy_eperm(int cgroup_fd, int sock_fd)
229{
230	struct cgroup_getset_retval_setsockopt *obj;
231	struct bpf_link *link_legacy_eperm = NULL, *link_get_retval = NULL;
232
233	obj = cgroup_getset_retval_setsockopt__open_and_load();
234	if (!ASSERT_OK_PTR(obj, "skel-load"))
235		return;
236
237	obj->bss->page_size = sysconf(_SC_PAGESIZE);
238
239	/* Attach setsockopt that return a reject without setting errno
240	 * (legacy reject), and one that gets the errno. Assert that for
241	 * backward compatibility the syscall result in EPERM, and this
242	 * is also visible to the helper.
243	 */
244	link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm,
245						       cgroup_fd);
246	if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm"))
247		goto close_bpf_object;
248	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
249						     cgroup_fd);
250	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
251		goto close_bpf_object;
252
253	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
254				   &zero, sizeof(int)), "setsockopt"))
255		goto close_bpf_object;
256	if (!ASSERT_EQ(errno, EPERM, "setsockopt-errno"))
257		goto close_bpf_object;
258
259	if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
260		goto close_bpf_object;
261	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
262		goto close_bpf_object;
263	if (!ASSERT_EQ(obj->bss->retval_value, -EPERM, "retval_value"))
264		goto close_bpf_object;
265
266close_bpf_object:
267	bpf_link__destroy(link_legacy_eperm);
268	bpf_link__destroy(link_get_retval);
269
270	cgroup_getset_retval_setsockopt__destroy(obj);
271}
272
273static void test_setsockopt_legacy_no_override(int cgroup_fd, int sock_fd)
274{
275	struct cgroup_getset_retval_setsockopt *obj;
276	struct bpf_link *link_set_eunatch = NULL, *link_legacy_eperm = NULL;
277	struct bpf_link *link_get_retval = NULL;
278
279	obj = cgroup_getset_retval_setsockopt__open_and_load();
280	if (!ASSERT_OK_PTR(obj, "skel-load"))
281		return;
282
283	obj->bss->page_size = sysconf(_SC_PAGESIZE);
284
285	/* Attach setsockopt that sets EUNATCH, then one that return a reject
286	 * without setting errno, and then one that gets the exported errno.
287	 * Assert both the syscall and the helper's errno are unaffected by
288	 * the second prog (i.e. legacy rejects does not override the errno
289	 * to EPERM).
290	 */
291	link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
292						      cgroup_fd);
293	if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
294		goto close_bpf_object;
295	link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm,
296						       cgroup_fd);
297	if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm"))
298		goto close_bpf_object;
299	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
300						     cgroup_fd);
301	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
302		goto close_bpf_object;
303
304	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
305				   &zero, sizeof(int)), "setsockopt"))
306		goto close_bpf_object;
307	if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
308		goto close_bpf_object;
309
310	if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
311		goto close_bpf_object;
312	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
313		goto close_bpf_object;
314	if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value"))
315		goto close_bpf_object;
316
317close_bpf_object:
318	bpf_link__destroy(link_set_eunatch);
319	bpf_link__destroy(link_legacy_eperm);
320	bpf_link__destroy(link_get_retval);
321
322	cgroup_getset_retval_setsockopt__destroy(obj);
323}
324
325static void test_getsockopt_get(int cgroup_fd, int sock_fd)
326{
327	struct cgroup_getset_retval_getsockopt *obj;
328	struct bpf_link *link_get_retval = NULL;
329	int buf;
330	socklen_t optlen = sizeof(buf);
331
332	obj = cgroup_getset_retval_getsockopt__open_and_load();
333	if (!ASSERT_OK_PTR(obj, "skel-load"))
334		return;
335
336	obj->bss->page_size = sysconf(_SC_PAGESIZE);
337
338	/* Attach getsockopt that gets previously set errno. Assert that the
339	 * error from kernel is in both ctx_retval_value and retval_value.
340	 */
341	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
342						     cgroup_fd);
343	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
344		goto close_bpf_object;
345
346	if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0,
347				   &buf, &optlen), "getsockopt"))
348		goto close_bpf_object;
349	if (!ASSERT_EQ(errno, EOPNOTSUPP, "getsockopt-errno"))
350		goto close_bpf_object;
351
352	if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
353		goto close_bpf_object;
354	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
355		goto close_bpf_object;
356	if (!ASSERT_EQ(obj->bss->retval_value, -EOPNOTSUPP, "retval_value"))
357		goto close_bpf_object;
358	if (!ASSERT_EQ(obj->bss->ctx_retval_value, -EOPNOTSUPP, "ctx_retval_value"))
359		goto close_bpf_object;
360
361close_bpf_object:
362	bpf_link__destroy(link_get_retval);
363
364	cgroup_getset_retval_getsockopt__destroy(obj);
365}
366
367static void test_getsockopt_override(int cgroup_fd, int sock_fd)
368{
369	struct cgroup_getset_retval_getsockopt *obj;
370	struct bpf_link *link_set_eisconn = NULL;
371	int buf;
372	socklen_t optlen = sizeof(buf);
373
374	obj = cgroup_getset_retval_getsockopt__open_and_load();
375	if (!ASSERT_OK_PTR(obj, "skel-load"))
376		return;
377
378	obj->bss->page_size = sysconf(_SC_PAGESIZE);
379
380	/* Attach getsockopt that sets retval to -EISCONN. Assert that this
381	 * overrides the value from kernel.
382	 */
383	link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
384						      cgroup_fd);
385	if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
386		goto close_bpf_object;
387
388	if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0,
389				   &buf, &optlen), "getsockopt"))
390		goto close_bpf_object;
391	if (!ASSERT_EQ(errno, EISCONN, "getsockopt-errno"))
392		goto close_bpf_object;
393
394	if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
395		goto close_bpf_object;
396	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
397		goto close_bpf_object;
398
399close_bpf_object:
400	bpf_link__destroy(link_set_eisconn);
401
402	cgroup_getset_retval_getsockopt__destroy(obj);
403}
404
405static void test_getsockopt_retval_sync(int cgroup_fd, int sock_fd)
406{
407	struct cgroup_getset_retval_getsockopt *obj;
408	struct bpf_link *link_set_eisconn = NULL, *link_clear_retval = NULL;
409	struct bpf_link *link_get_retval = NULL;
410	int buf;
411	socklen_t optlen = sizeof(buf);
412
413	obj = cgroup_getset_retval_getsockopt__open_and_load();
414	if (!ASSERT_OK_PTR(obj, "skel-load"))
415		return;
416
417	obj->bss->page_size = sysconf(_SC_PAGESIZE);
418
419	/* Attach getsockopt that sets retval to -EISCONN, and one that clears
420	 * ctx retval. Assert that the clearing ctx retval is synced to helper
421	 * and clears any errors both from kernel and BPF..
422	 */
423	link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
424						      cgroup_fd);
425	if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
426		goto close_bpf_object;
427	link_clear_retval = bpf_program__attach_cgroup(obj->progs.clear_retval,
428						       cgroup_fd);
429	if (!ASSERT_OK_PTR(link_clear_retval, "cg-attach-clear_retval"))
430		goto close_bpf_object;
431	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
432						     cgroup_fd);
433	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
434		goto close_bpf_object;
435
436	if (!ASSERT_OK(getsockopt(sock_fd, SOL_CUSTOM, 0,
437				  &buf, &optlen), "getsockopt"))
438		goto close_bpf_object;
439
440	if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
441		goto close_bpf_object;
442	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
443		goto close_bpf_object;
444	if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
445		goto close_bpf_object;
446	if (!ASSERT_EQ(obj->bss->ctx_retval_value, 0, "ctx_retval_value"))
447		goto close_bpf_object;
448
449close_bpf_object:
450	bpf_link__destroy(link_set_eisconn);
451	bpf_link__destroy(link_clear_retval);
452	bpf_link__destroy(link_get_retval);
453
454	cgroup_getset_retval_getsockopt__destroy(obj);
455}
456
457struct exposed_hook {
458	const char *name;
459	int expected_err;
460} exposed_hooks[] = {
461
462#define BPF_RETVAL_HOOK(NAME, SECTION, CTX, EXPECTED_ERR) \
463	{ \
464		.name = #NAME, \
465		.expected_err = EXPECTED_ERR, \
466	},
467
468#include "cgroup_getset_retval_hooks.h"
469
470#undef BPF_RETVAL_HOOK
471};
472
473static void test_exposed_hooks(int cgroup_fd, int sock_fd)
474{
475	struct cgroup_getset_retval_hooks *skel;
476	struct bpf_program *prog;
477	int err;
478	int i;
479
480	for (i = 0; i < ARRAY_SIZE(exposed_hooks); i++) {
481		skel = cgroup_getset_retval_hooks__open();
482		if (!ASSERT_OK_PTR(skel, "cgroup_getset_retval_hooks__open"))
483			continue;
484
485		prog = bpf_object__find_program_by_name(skel->obj, exposed_hooks[i].name);
486		if (!ASSERT_NEQ(prog, NULL, "bpf_object__find_program_by_name"))
487			goto close_skel;
488
489		err = bpf_program__set_autoload(prog, true);
490		if (!ASSERT_OK(err, "bpf_program__set_autoload"))
491			goto close_skel;
492
493		err = cgroup_getset_retval_hooks__load(skel);
494		ASSERT_EQ(err, exposed_hooks[i].expected_err, "expected_err");
495
496close_skel:
497		cgroup_getset_retval_hooks__destroy(skel);
498	}
499}
500
501void test_cgroup_getset_retval(void)
502{
503	int cgroup_fd = -1;
504	int sock_fd = -1;
505
506	cgroup_fd = test__join_cgroup("/cgroup_getset_retval");
507	if (!ASSERT_GE(cgroup_fd, 0, "cg-create"))
508		goto close_fd;
509
510	sock_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0);
511	if (!ASSERT_GE(sock_fd, 0, "start-server"))
512		goto close_fd;
513
514	if (test__start_subtest("setsockopt-set"))
515		test_setsockopt_set(cgroup_fd, sock_fd);
516
517	if (test__start_subtest("setsockopt-set_and_get"))
518		test_setsockopt_set_and_get(cgroup_fd, sock_fd);
519
520	if (test__start_subtest("setsockopt-default_zero"))
521		test_setsockopt_default_zero(cgroup_fd, sock_fd);
522
523	if (test__start_subtest("setsockopt-default_zero_and_set"))
524		test_setsockopt_default_zero_and_set(cgroup_fd, sock_fd);
525
526	if (test__start_subtest("setsockopt-override"))
527		test_setsockopt_override(cgroup_fd, sock_fd);
528
529	if (test__start_subtest("setsockopt-legacy_eperm"))
530		test_setsockopt_legacy_eperm(cgroup_fd, sock_fd);
531
532	if (test__start_subtest("setsockopt-legacy_no_override"))
533		test_setsockopt_legacy_no_override(cgroup_fd, sock_fd);
534
535	if (test__start_subtest("getsockopt-get"))
536		test_getsockopt_get(cgroup_fd, sock_fd);
537
538	if (test__start_subtest("getsockopt-override"))
539		test_getsockopt_override(cgroup_fd, sock_fd);
540
541	if (test__start_subtest("getsockopt-retval_sync"))
542		test_getsockopt_retval_sync(cgroup_fd, sock_fd);
543
544	if (test__start_subtest("exposed_hooks"))
545		test_exposed_hooks(cgroup_fd, sock_fd);
546
547close_fd:
548	close(cgroup_fd);
549}
v6.2
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  3/*
  4 * Copyright 2021 Google LLC.
  5 */
  6
  7#include <test_progs.h>
  8#include <cgroup_helpers.h>
  9#include <network_helpers.h>
 10
 11#include "cgroup_getset_retval_setsockopt.skel.h"
 12#include "cgroup_getset_retval_getsockopt.skel.h"
 13#include "cgroup_getset_retval_hooks.skel.h"
 14
 15#define SOL_CUSTOM	0xdeadbeef
 16
 17static int zero;
 18
 19static void test_setsockopt_set(int cgroup_fd, int sock_fd)
 20{
 21	struct cgroup_getset_retval_setsockopt *obj;
 22	struct bpf_link *link_set_eunatch = NULL;
 23
 24	obj = cgroup_getset_retval_setsockopt__open_and_load();
 25	if (!ASSERT_OK_PTR(obj, "skel-load"))
 26		return;
 27
 
 
 28	/* Attach setsockopt that sets EUNATCH, assert that
 29	 * we actually get that error when we run setsockopt()
 30	 */
 31	link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
 32						      cgroup_fd);
 33	if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
 34		goto close_bpf_object;
 35
 36	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
 37				   &zero, sizeof(int)), "setsockopt"))
 38		goto close_bpf_object;
 39	if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
 40		goto close_bpf_object;
 41
 42	if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
 43		goto close_bpf_object;
 44	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
 45		goto close_bpf_object;
 46
 47close_bpf_object:
 48	bpf_link__destroy(link_set_eunatch);
 49
 50	cgroup_getset_retval_setsockopt__destroy(obj);
 51}
 52
 53static void test_setsockopt_set_and_get(int cgroup_fd, int sock_fd)
 54{
 55	struct cgroup_getset_retval_setsockopt *obj;
 56	struct bpf_link *link_set_eunatch = NULL, *link_get_retval = NULL;
 57
 58	obj = cgroup_getset_retval_setsockopt__open_and_load();
 59	if (!ASSERT_OK_PTR(obj, "skel-load"))
 60		return;
 61
 
 
 62	/* Attach setsockopt that sets EUNATCH, and one that gets the
 63	 * previously set errno. Assert that we get the same errno back.
 64	 */
 65	link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
 66						      cgroup_fd);
 67	if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
 68		goto close_bpf_object;
 69	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
 70						     cgroup_fd);
 71	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
 72		goto close_bpf_object;
 73
 74	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
 75				   &zero, sizeof(int)), "setsockopt"))
 76		goto close_bpf_object;
 77	if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
 78		goto close_bpf_object;
 79
 80	if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
 81		goto close_bpf_object;
 82	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
 83		goto close_bpf_object;
 84	if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value"))
 85		goto close_bpf_object;
 86
 87close_bpf_object:
 88	bpf_link__destroy(link_set_eunatch);
 89	bpf_link__destroy(link_get_retval);
 90
 91	cgroup_getset_retval_setsockopt__destroy(obj);
 92}
 93
 94static void test_setsockopt_default_zero(int cgroup_fd, int sock_fd)
 95{
 96	struct cgroup_getset_retval_setsockopt *obj;
 97	struct bpf_link *link_get_retval = NULL;
 98
 99	obj = cgroup_getset_retval_setsockopt__open_and_load();
100	if (!ASSERT_OK_PTR(obj, "skel-load"))
101		return;
102
 
 
103	/* Attach setsockopt that gets the previously set errno.
104	 * Assert that, without anything setting one, we get 0.
105	 */
106	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
107						     cgroup_fd);
108	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
109		goto close_bpf_object;
110
111	if (!ASSERT_OK(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
112				  &zero, sizeof(int)), "setsockopt"))
113		goto close_bpf_object;
114
115	if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
116		goto close_bpf_object;
117	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
118		goto close_bpf_object;
119	if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
120		goto close_bpf_object;
121
122close_bpf_object:
123	bpf_link__destroy(link_get_retval);
124
125	cgroup_getset_retval_setsockopt__destroy(obj);
126}
127
128static void test_setsockopt_default_zero_and_set(int cgroup_fd, int sock_fd)
129{
130	struct cgroup_getset_retval_setsockopt *obj;
131	struct bpf_link *link_get_retval = NULL, *link_set_eunatch = NULL;
132
133	obj = cgroup_getset_retval_setsockopt__open_and_load();
134	if (!ASSERT_OK_PTR(obj, "skel-load"))
135		return;
136
 
 
137	/* Attach setsockopt that gets the previously set errno, and then
138	 * one that sets the errno to EUNATCH. Assert that the get does not
139	 * see EUNATCH set later, and does not prevent EUNATCH from being set.
140	 */
141	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
142						     cgroup_fd);
143	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
144		goto close_bpf_object;
145	link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
146						      cgroup_fd);
147	if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
148		goto close_bpf_object;
149
150	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
151				   &zero, sizeof(int)), "setsockopt"))
152		goto close_bpf_object;
153	if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
154		goto close_bpf_object;
155
156	if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
157		goto close_bpf_object;
158	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
159		goto close_bpf_object;
160	if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
161		goto close_bpf_object;
162
163close_bpf_object:
164	bpf_link__destroy(link_get_retval);
165	bpf_link__destroy(link_set_eunatch);
166
167	cgroup_getset_retval_setsockopt__destroy(obj);
168}
169
170static void test_setsockopt_override(int cgroup_fd, int sock_fd)
171{
172	struct cgroup_getset_retval_setsockopt *obj;
173	struct bpf_link *link_set_eunatch = NULL, *link_set_eisconn = NULL;
174	struct bpf_link *link_get_retval = NULL;
175
176	obj = cgroup_getset_retval_setsockopt__open_and_load();
177	if (!ASSERT_OK_PTR(obj, "skel-load"))
178		return;
179
 
 
180	/* Attach setsockopt that sets EUNATCH, then one that sets EISCONN,
181	 * and then one that gets the exported errno. Assert both the syscall
182	 * and the helper sees the last set errno.
183	 */
184	link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
185						      cgroup_fd);
186	if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
187		goto close_bpf_object;
188	link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
189						      cgroup_fd);
190	if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
191		goto close_bpf_object;
192	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
193						     cgroup_fd);
194	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
195		goto close_bpf_object;
196
197	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
198				   &zero, sizeof(int)), "setsockopt"))
199		goto close_bpf_object;
200	if (!ASSERT_EQ(errno, EISCONN, "setsockopt-errno"))
201		goto close_bpf_object;
202
203	if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
204		goto close_bpf_object;
205	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
206		goto close_bpf_object;
207	if (!ASSERT_EQ(obj->bss->retval_value, -EISCONN, "retval_value"))
208		goto close_bpf_object;
209
210close_bpf_object:
211	bpf_link__destroy(link_set_eunatch);
212	bpf_link__destroy(link_set_eisconn);
213	bpf_link__destroy(link_get_retval);
214
215	cgroup_getset_retval_setsockopt__destroy(obj);
216}
217
218static void test_setsockopt_legacy_eperm(int cgroup_fd, int sock_fd)
219{
220	struct cgroup_getset_retval_setsockopt *obj;
221	struct bpf_link *link_legacy_eperm = NULL, *link_get_retval = NULL;
222
223	obj = cgroup_getset_retval_setsockopt__open_and_load();
224	if (!ASSERT_OK_PTR(obj, "skel-load"))
225		return;
226
 
 
227	/* Attach setsockopt that return a reject without setting errno
228	 * (legacy reject), and one that gets the errno. Assert that for
229	 * backward compatibility the syscall result in EPERM, and this
230	 * is also visible to the helper.
231	 */
232	link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm,
233						       cgroup_fd);
234	if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm"))
235		goto close_bpf_object;
236	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
237						     cgroup_fd);
238	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
239		goto close_bpf_object;
240
241	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
242				   &zero, sizeof(int)), "setsockopt"))
243		goto close_bpf_object;
244	if (!ASSERT_EQ(errno, EPERM, "setsockopt-errno"))
245		goto close_bpf_object;
246
247	if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
248		goto close_bpf_object;
249	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
250		goto close_bpf_object;
251	if (!ASSERT_EQ(obj->bss->retval_value, -EPERM, "retval_value"))
252		goto close_bpf_object;
253
254close_bpf_object:
255	bpf_link__destroy(link_legacy_eperm);
256	bpf_link__destroy(link_get_retval);
257
258	cgroup_getset_retval_setsockopt__destroy(obj);
259}
260
261static void test_setsockopt_legacy_no_override(int cgroup_fd, int sock_fd)
262{
263	struct cgroup_getset_retval_setsockopt *obj;
264	struct bpf_link *link_set_eunatch = NULL, *link_legacy_eperm = NULL;
265	struct bpf_link *link_get_retval = NULL;
266
267	obj = cgroup_getset_retval_setsockopt__open_and_load();
268	if (!ASSERT_OK_PTR(obj, "skel-load"))
269		return;
270
 
 
271	/* Attach setsockopt that sets EUNATCH, then one that return a reject
272	 * without setting errno, and then one that gets the exported errno.
273	 * Assert both the syscall and the helper's errno are unaffected by
274	 * the second prog (i.e. legacy rejects does not override the errno
275	 * to EPERM).
276	 */
277	link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
278						      cgroup_fd);
279	if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
280		goto close_bpf_object;
281	link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm,
282						       cgroup_fd);
283	if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm"))
284		goto close_bpf_object;
285	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
286						     cgroup_fd);
287	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
288		goto close_bpf_object;
289
290	if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
291				   &zero, sizeof(int)), "setsockopt"))
292		goto close_bpf_object;
293	if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
294		goto close_bpf_object;
295
296	if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
297		goto close_bpf_object;
298	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
299		goto close_bpf_object;
300	if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value"))
301		goto close_bpf_object;
302
303close_bpf_object:
304	bpf_link__destroy(link_set_eunatch);
305	bpf_link__destroy(link_legacy_eperm);
306	bpf_link__destroy(link_get_retval);
307
308	cgroup_getset_retval_setsockopt__destroy(obj);
309}
310
311static void test_getsockopt_get(int cgroup_fd, int sock_fd)
312{
313	struct cgroup_getset_retval_getsockopt *obj;
314	struct bpf_link *link_get_retval = NULL;
315	int buf;
316	socklen_t optlen = sizeof(buf);
317
318	obj = cgroup_getset_retval_getsockopt__open_and_load();
319	if (!ASSERT_OK_PTR(obj, "skel-load"))
320		return;
321
 
 
322	/* Attach getsockopt that gets previously set errno. Assert that the
323	 * error from kernel is in both ctx_retval_value and retval_value.
324	 */
325	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
326						     cgroup_fd);
327	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
328		goto close_bpf_object;
329
330	if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0,
331				   &buf, &optlen), "getsockopt"))
332		goto close_bpf_object;
333	if (!ASSERT_EQ(errno, EOPNOTSUPP, "getsockopt-errno"))
334		goto close_bpf_object;
335
336	if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
337		goto close_bpf_object;
338	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
339		goto close_bpf_object;
340	if (!ASSERT_EQ(obj->bss->retval_value, -EOPNOTSUPP, "retval_value"))
341		goto close_bpf_object;
342	if (!ASSERT_EQ(obj->bss->ctx_retval_value, -EOPNOTSUPP, "ctx_retval_value"))
343		goto close_bpf_object;
344
345close_bpf_object:
346	bpf_link__destroy(link_get_retval);
347
348	cgroup_getset_retval_getsockopt__destroy(obj);
349}
350
351static void test_getsockopt_override(int cgroup_fd, int sock_fd)
352{
353	struct cgroup_getset_retval_getsockopt *obj;
354	struct bpf_link *link_set_eisconn = NULL;
355	int buf;
356	socklen_t optlen = sizeof(buf);
357
358	obj = cgroup_getset_retval_getsockopt__open_and_load();
359	if (!ASSERT_OK_PTR(obj, "skel-load"))
360		return;
361
 
 
362	/* Attach getsockopt that sets retval to -EISCONN. Assert that this
363	 * overrides the value from kernel.
364	 */
365	link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
366						      cgroup_fd);
367	if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
368		goto close_bpf_object;
369
370	if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0,
371				   &buf, &optlen), "getsockopt"))
372		goto close_bpf_object;
373	if (!ASSERT_EQ(errno, EISCONN, "getsockopt-errno"))
374		goto close_bpf_object;
375
376	if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
377		goto close_bpf_object;
378	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
379		goto close_bpf_object;
380
381close_bpf_object:
382	bpf_link__destroy(link_set_eisconn);
383
384	cgroup_getset_retval_getsockopt__destroy(obj);
385}
386
387static void test_getsockopt_retval_sync(int cgroup_fd, int sock_fd)
388{
389	struct cgroup_getset_retval_getsockopt *obj;
390	struct bpf_link *link_set_eisconn = NULL, *link_clear_retval = NULL;
391	struct bpf_link *link_get_retval = NULL;
392	int buf;
393	socklen_t optlen = sizeof(buf);
394
395	obj = cgroup_getset_retval_getsockopt__open_and_load();
396	if (!ASSERT_OK_PTR(obj, "skel-load"))
397		return;
 
 
398
399	/* Attach getsockopt that sets retval to -EISCONN, and one that clears
400	 * ctx retval. Assert that the clearing ctx retval is synced to helper
401	 * and clears any errors both from kernel and BPF..
402	 */
403	link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
404						      cgroup_fd);
405	if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
406		goto close_bpf_object;
407	link_clear_retval = bpf_program__attach_cgroup(obj->progs.clear_retval,
408						       cgroup_fd);
409	if (!ASSERT_OK_PTR(link_clear_retval, "cg-attach-clear_retval"))
410		goto close_bpf_object;
411	link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
412						     cgroup_fd);
413	if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
414		goto close_bpf_object;
415
416	if (!ASSERT_OK(getsockopt(sock_fd, SOL_CUSTOM, 0,
417				  &buf, &optlen), "getsockopt"))
418		goto close_bpf_object;
419
420	if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
421		goto close_bpf_object;
422	if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
423		goto close_bpf_object;
424	if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
425		goto close_bpf_object;
426	if (!ASSERT_EQ(obj->bss->ctx_retval_value, 0, "ctx_retval_value"))
427		goto close_bpf_object;
428
429close_bpf_object:
430	bpf_link__destroy(link_set_eisconn);
431	bpf_link__destroy(link_clear_retval);
432	bpf_link__destroy(link_get_retval);
433
434	cgroup_getset_retval_getsockopt__destroy(obj);
435}
436
437struct exposed_hook {
438	const char *name;
439	int expected_err;
440} exposed_hooks[] = {
441
442#define BPF_RETVAL_HOOK(NAME, SECTION, CTX, EXPECTED_ERR) \
443	{ \
444		.name = #NAME, \
445		.expected_err = EXPECTED_ERR, \
446	},
447
448#include "cgroup_getset_retval_hooks.h"
449
450#undef BPF_RETVAL_HOOK
451};
452
453static void test_exposed_hooks(int cgroup_fd, int sock_fd)
454{
455	struct cgroup_getset_retval_hooks *skel;
456	struct bpf_program *prog;
457	int err;
458	int i;
459
460	for (i = 0; i < ARRAY_SIZE(exposed_hooks); i++) {
461		skel = cgroup_getset_retval_hooks__open();
462		if (!ASSERT_OK_PTR(skel, "cgroup_getset_retval_hooks__open"))
463			continue;
464
465		prog = bpf_object__find_program_by_name(skel->obj, exposed_hooks[i].name);
466		if (!ASSERT_NEQ(prog, NULL, "bpf_object__find_program_by_name"))
467			goto close_skel;
468
469		err = bpf_program__set_autoload(prog, true);
470		if (!ASSERT_OK(err, "bpf_program__set_autoload"))
471			goto close_skel;
472
473		err = cgroup_getset_retval_hooks__load(skel);
474		ASSERT_EQ(err, exposed_hooks[i].expected_err, "expected_err");
475
476close_skel:
477		cgroup_getset_retval_hooks__destroy(skel);
478	}
479}
480
481void test_cgroup_getset_retval(void)
482{
483	int cgroup_fd = -1;
484	int sock_fd = -1;
485
486	cgroup_fd = test__join_cgroup("/cgroup_getset_retval");
487	if (!ASSERT_GE(cgroup_fd, 0, "cg-create"))
488		goto close_fd;
489
490	sock_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0);
491	if (!ASSERT_GE(sock_fd, 0, "start-server"))
492		goto close_fd;
493
494	if (test__start_subtest("setsockopt-set"))
495		test_setsockopt_set(cgroup_fd, sock_fd);
496
497	if (test__start_subtest("setsockopt-set_and_get"))
498		test_setsockopt_set_and_get(cgroup_fd, sock_fd);
499
500	if (test__start_subtest("setsockopt-default_zero"))
501		test_setsockopt_default_zero(cgroup_fd, sock_fd);
502
503	if (test__start_subtest("setsockopt-default_zero_and_set"))
504		test_setsockopt_default_zero_and_set(cgroup_fd, sock_fd);
505
506	if (test__start_subtest("setsockopt-override"))
507		test_setsockopt_override(cgroup_fd, sock_fd);
508
509	if (test__start_subtest("setsockopt-legacy_eperm"))
510		test_setsockopt_legacy_eperm(cgroup_fd, sock_fd);
511
512	if (test__start_subtest("setsockopt-legacy_no_override"))
513		test_setsockopt_legacy_no_override(cgroup_fd, sock_fd);
514
515	if (test__start_subtest("getsockopt-get"))
516		test_getsockopt_get(cgroup_fd, sock_fd);
517
518	if (test__start_subtest("getsockopt-override"))
519		test_getsockopt_override(cgroup_fd, sock_fd);
520
521	if (test__start_subtest("getsockopt-retval_sync"))
522		test_getsockopt_retval_sync(cgroup_fd, sock_fd);
523
524	if (test__start_subtest("exposed_hooks"))
525		test_exposed_hooks(cgroup_fd, sock_fd);
526
527close_fd:
528	close(cgroup_fd);
529}