1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Test that the flow_dissector program can be updated with a single
4  * syscall by attaching a new program that replaces the existing one.
5  *
6  * Corner case - the same program cannot be attached twice.
7  */
8 
9 #define _GNU_SOURCE
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <sched.h>
13 #include <stdbool.h>
14 #include <unistd.h>
15 
16 #include <linux/bpf.h>
17 #include <bpf/bpf.h>
18 
19 #include "test_progs.h"
20 
21 static bool is_attached(int netns)
22 {
23 	__u32 cnt;
24 	int err;
25 
26 	err = bpf_prog_query(netns, BPF_FLOW_DISSECTOR, 0, NULL, NULL, &cnt);
27 	if (CHECK_FAIL(err)) {
28 		perror("bpf_prog_query");
29 		return true; /* fail-safe */
30 	}
31 
32 	return cnt > 0;
33 }
34 
35 static int load_prog(void)
36 {
37 	struct bpf_insn prog[] = {
38 		BPF_MOV64_IMM(BPF_REG_0, BPF_OK),
39 		BPF_EXIT_INSN(),
40 	};
41 	int fd;
42 
43 	fd = bpf_load_program(BPF_PROG_TYPE_FLOW_DISSECTOR, prog,
44 			      ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
45 	if (CHECK_FAIL(fd < 0))
46 		perror("bpf_load_program");
47 
48 	return fd;
49 }
50 
51 static void do_flow_dissector_reattach(void)
52 {
53 	int prog_fd[2] = { -1, -1 };
54 	int err;
55 
56 	prog_fd[0] = load_prog();
57 	if (prog_fd[0] < 0)
58 		return;
59 
60 	prog_fd[1] = load_prog();
61 	if (prog_fd[1] < 0)
62 		goto out_close;
63 
64 	err = bpf_prog_attach(prog_fd[0], 0, BPF_FLOW_DISSECTOR, 0);
65 	if (CHECK_FAIL(err)) {
66 		perror("bpf_prog_attach-0");
67 		goto out_close;
68 	}
69 
70 	/* Expect success when attaching a different program */
71 	err = bpf_prog_attach(prog_fd[1], 0, BPF_FLOW_DISSECTOR, 0);
72 	if (CHECK_FAIL(err)) {
73 		perror("bpf_prog_attach-1");
74 		goto out_detach;
75 	}
76 
77 	/* Expect failure when attaching the same program twice */
78 	err = bpf_prog_attach(prog_fd[1], 0, BPF_FLOW_DISSECTOR, 0);
79 	if (CHECK_FAIL(!err || errno != EINVAL))
80 		perror("bpf_prog_attach-2");
81 
82 out_detach:
83 	err = bpf_prog_detach(0, BPF_FLOW_DISSECTOR);
84 	if (CHECK_FAIL(err))
85 		perror("bpf_prog_detach");
86 
87 out_close:
88 	close(prog_fd[1]);
89 	close(prog_fd[0]);
90 }
91 
92 void test_flow_dissector_reattach(void)
93 {
94 	int init_net, self_net, err;
95 
96 	self_net = open("/proc/self/ns/net", O_RDONLY);
97 	if (CHECK_FAIL(self_net < 0)) {
98 		perror("open(/proc/self/ns/net");
99 		return;
100 	}
101 
102 	init_net = open("/proc/1/ns/net", O_RDONLY);
103 	if (CHECK_FAIL(init_net < 0)) {
104 		perror("open(/proc/1/ns/net)");
105 		goto out_close;
106 	}
107 
108 	err = setns(init_net, CLONE_NEWNET);
109 	if (CHECK_FAIL(err)) {
110 		perror("setns(/proc/1/ns/net)");
111 		goto out_close;
112 	}
113 
114 	if (is_attached(init_net)) {
115 		test__skip();
116 		printf("Can't test with flow dissector attached to init_net\n");
117 		goto out_setns;
118 	}
119 
120 	/* First run tests in root network namespace */
121 	do_flow_dissector_reattach();
122 
123 	/* Then repeat tests in a non-root namespace */
124 	err = unshare(CLONE_NEWNET);
125 	if (CHECK_FAIL(err)) {
126 		perror("unshare(CLONE_NEWNET)");
127 		goto out_setns;
128 	}
129 	do_flow_dissector_reattach();
130 
131 out_setns:
132 	/* Move back to netns we started in. */
133 	err = setns(self_net, CLONE_NEWNET);
134 	if (CHECK_FAIL(err))
135 		perror("setns(/proc/self/ns/net)");
136 
137 out_close:
138 	close(init_net);
139 	close(self_net);
140 }
141