aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/bpf/prog_tests/flow_dissector_reattach.c
blob: 1f51ba66b98bb896c8a035966bcc585cc54d16eb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: GPL-2.0
/*
 * Test that the flow_dissector program can be updated with a single
 * syscall by attaching a new program that replaces the existing one.
 *
 * Corner case - the same program cannot be attached twice.
 */

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdbool.h>
#include <unistd.h>

#include <linux/bpf.h>
#include <bpf/bpf.h>

#include "test_progs.h"

static bool is_attached(int netns)
{
	__u32 cnt;
	int err;

	err = bpf_prog_query(netns, BPF_FLOW_DISSECTOR, 0, NULL, NULL, &cnt);
	if (CHECK_FAIL(err)) {
		perror("bpf_prog_query");
		return true; /* fail-safe */
	}

	return cnt > 0;
}

static int load_prog(void)
{
	struct bpf_insn prog[] = {
		BPF_MOV64_IMM(BPF_REG_0, BPF_OK),
		BPF_EXIT_INSN(),
	};
	int fd;

	fd = bpf_load_program(BPF_PROG_TYPE_FLOW_DISSECTOR, prog,
			      ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
	if (CHECK_FAIL(fd < 0))
		perror("bpf_load_program");

	return fd;
}

static void do_flow_dissector_reattach(void)
{
	int prog_fd[2] = { -1, -1 };
	int err;

	prog_fd[0] = load_prog();
	if (prog_fd[0] < 0)
		return;

	prog_fd[1] = load_prog();
	if (prog_fd[1] < 0)
		goto out_close;

	err = bpf_prog_attach(prog_fd[0], 0, BPF_FLOW_DISSECTOR, 0);
	if (CHECK_FAIL(err)) {
		perror("bpf_prog_attach-0");
		goto out_close;
	}

	/* Expect success when attaching a different program */
	err = bpf_prog_attach(prog_fd[1], 0, BPF_FLOW_DISSECTOR, 0);
	if (CHECK_FAIL(err)) {
		perror("bpf_prog_attach-1");
		goto out_detach;
	}

	/* Expect failure when attaching the same program twice */
	err = bpf_prog_attach(prog_fd[1], 0, BPF_FLOW_DISSECTOR, 0);
	if (CHECK_FAIL(!err || errno != EINVAL))
		perror("bpf_prog_attach-2");

out_detach:
	err = bpf_prog_detach(0, BPF_FLOW_DISSECTOR);
	if (CHECK_FAIL(err))
		perror("bpf_prog_detach");

out_close:
	close(prog_fd[1]);
	close(prog_fd[0]);
}

void test_flow_dissector_reattach(void)
{
	int init_net, self_net, err;

	self_net = open("/proc/self/ns/net", O_RDONLY);
	if (CHECK_FAIL(self_net < 0)) {
		perror("open(/proc/self/ns/net");
		return;
	}

	init_net = open("/proc/1/ns/net", O_RDONLY);
	if (CHECK_FAIL(init_net < 0)) {
		perror("open(/proc/1/ns/net)");
		goto out_close;
	}

	err = setns(init_net, CLONE_NEWNET);
	if (CHECK_FAIL(err)) {
		perror("setns(/proc/1/ns/net)");
		goto out_close;
	}

	if (is_attached(init_net)) {
		test__skip();
		printf("Can't test with flow dissector attached to init_net\n");
		goto out_setns;
	}

	/* First run tests in root network namespace */
	do_flow_dissector_reattach();

	/* Then repeat tests in a non-root namespace */
	err = unshare(CLONE_NEWNET);
	if (CHECK_FAIL(err)) {
		perror("unshare(CLONE_NEWNET)");
		goto out_setns;
	}
	do_flow_dissector_reattach();

out_setns:
	/* Move back to netns we started in. */
	err = setns(self_net, CLONE_NEWNET);
	if (CHECK_FAIL(err))
		perror("setns(/proc/self/ns/net)");

out_close:
	close(init_net);
	close(self_net);
}