xref: /linux/tools/testing/selftests/bpf/prog_tests/ip_check_defrag.c (revision 24168c5e6dfbdd5b414f048f47f75d64533296ca)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include <net/if.h>
4 #include <linux/netfilter.h>
5 #include <network_helpers.h>
6 #include "ip_check_defrag.skel.h"
7 #include "ip_check_defrag_frags.h"
8 
9 /*
10  * This selftest spins up a client and an echo server, each in their own
11  * network namespace. The client will send a fragmented message to the server.
12  * The prog attached to the server will shoot down any fragments. Thus, if
13  * the server is able to correctly echo back the message to the client, we will
14  * have verified that netfilter is reassembling packets for us.
15  *
16  * Topology:
17  * =========
18  *           NS0         |         NS1
19  *                       |
20  *         client        |       server
21  *       ----------      |     ----------
22  *       |  veth0  | --------- |  veth1  |
23  *       ----------    peer    ----------
24  *                       |
25  *                       |       with bpf
26  */
27 
28 #define NS0		"defrag_ns0"
29 #define NS1		"defrag_ns1"
30 #define VETH0		"veth0"
31 #define VETH1		"veth1"
32 #define VETH0_ADDR	"172.16.1.100"
33 #define VETH0_ADDR6	"fc00::100"
34 /* The following constants must stay in sync with `generate_udp_fragments.py` */
35 #define VETH1_ADDR	"172.16.1.200"
36 #define VETH1_ADDR6	"fc00::200"
37 #define CLIENT_PORT	48878
38 #define SERVER_PORT	48879
39 #define MAGIC_MESSAGE	"THIS IS THE ORIGINAL MESSAGE, PLEASE REASSEMBLE ME"
40 
41 static int setup_topology(bool ipv6)
42 {
43 	bool up;
44 	int i;
45 
46 	SYS(fail, "ip netns add " NS0);
47 	SYS(fail, "ip netns add " NS1);
48 	SYS(fail, "ip link add " VETH0 " netns " NS0 " type veth peer name " VETH1 " netns " NS1);
49 	if (ipv6) {
50 		SYS(fail, "ip -6 -net " NS0 " addr add " VETH0_ADDR6 "/64 dev " VETH0 " nodad");
51 		SYS(fail, "ip -6 -net " NS1 " addr add " VETH1_ADDR6 "/64 dev " VETH1 " nodad");
52 	} else {
53 		SYS(fail, "ip -net " NS0 " addr add " VETH0_ADDR "/24 dev " VETH0);
54 		SYS(fail, "ip -net " NS1 " addr add " VETH1_ADDR "/24 dev " VETH1);
55 	}
56 	SYS(fail, "ip -net " NS0 " link set dev " VETH0 " up");
57 	SYS(fail, "ip -net " NS1 " link set dev " VETH1 " up");
58 
59 	/* Wait for up to 5s for links to come up */
60 	for (i = 0; i < 5; ++i) {
61 		if (ipv6)
62 			up = !SYS_NOFAIL("ip netns exec " NS0 " ping -6 -c 1 -W 1 " VETH1_ADDR6);
63 		else
64 			up = !SYS_NOFAIL("ip netns exec " NS0 " ping -c 1 -W 1 " VETH1_ADDR);
65 
66 		if (up)
67 			break;
68 	}
69 
70 	return 0;
71 fail:
72 	return -1;
73 }
74 
75 static void cleanup_topology(void)
76 {
77 	SYS_NOFAIL("test -f /var/run/netns/" NS0 " && ip netns delete " NS0);
78 	SYS_NOFAIL("test -f /var/run/netns/" NS1 " && ip netns delete " NS1);
79 }
80 
81 static int attach(struct ip_check_defrag *skel, bool ipv6)
82 {
83 	LIBBPF_OPTS(bpf_netfilter_opts, opts,
84 		    .pf = ipv6 ? NFPROTO_IPV6 : NFPROTO_IPV4,
85 		    .priority = 42,
86 		    .flags = BPF_F_NETFILTER_IP_DEFRAG);
87 	struct nstoken *nstoken;
88 	int err = -1;
89 
90 	nstoken = open_netns(NS1);
91 	if (!ASSERT_OK_PTR(nstoken, "setns"))
92 		goto out;
93 
94 	skel->links.defrag = bpf_program__attach_netfilter(skel->progs.defrag, &opts);
95 	if (!ASSERT_OK_PTR(skel->links.defrag, "program attach"))
96 		goto out;
97 
98 	err = 0;
99 out:
100 	close_netns(nstoken);
101 	return err;
102 }
103 
104 static int send_frags(int client)
105 {
106 	struct sockaddr_storage saddr;
107 	struct sockaddr *saddr_p;
108 	socklen_t saddr_len;
109 	int err;
110 
111 	saddr_p = (struct sockaddr *)&saddr;
112 	err = make_sockaddr(AF_INET, VETH1_ADDR, SERVER_PORT, &saddr, &saddr_len);
113 	if (!ASSERT_OK(err, "make_sockaddr"))
114 		return -1;
115 
116 	err = sendto(client, frag_0, sizeof(frag_0), 0, saddr_p, saddr_len);
117 	if (!ASSERT_GE(err, 0, "sendto frag_0"))
118 		return -1;
119 
120 	err = sendto(client, frag_1, sizeof(frag_1), 0, saddr_p, saddr_len);
121 	if (!ASSERT_GE(err, 0, "sendto frag_1"))
122 		return -1;
123 
124 	err = sendto(client, frag_2, sizeof(frag_2), 0, saddr_p, saddr_len);
125 	if (!ASSERT_GE(err, 0, "sendto frag_2"))
126 		return -1;
127 
128 	return 0;
129 }
130 
131 static int send_frags6(int client)
132 {
133 	struct sockaddr_storage saddr;
134 	struct sockaddr *saddr_p;
135 	socklen_t saddr_len;
136 	int err;
137 
138 	saddr_p = (struct sockaddr *)&saddr;
139 	/* Port needs to be set to 0 for raw ipv6 socket for some reason */
140 	err = make_sockaddr(AF_INET6, VETH1_ADDR6, 0, &saddr, &saddr_len);
141 	if (!ASSERT_OK(err, "make_sockaddr"))
142 		return -1;
143 
144 	err = sendto(client, frag6_0, sizeof(frag6_0), 0, saddr_p, saddr_len);
145 	if (!ASSERT_GE(err, 0, "sendto frag6_0"))
146 		return -1;
147 
148 	err = sendto(client, frag6_1, sizeof(frag6_1), 0, saddr_p, saddr_len);
149 	if (!ASSERT_GE(err, 0, "sendto frag6_1"))
150 		return -1;
151 
152 	err = sendto(client, frag6_2, sizeof(frag6_2), 0, saddr_p, saddr_len);
153 	if (!ASSERT_GE(err, 0, "sendto frag6_2"))
154 		return -1;
155 
156 	return 0;
157 }
158 
159 void test_bpf_ip_check_defrag_ok(bool ipv6)
160 {
161 	struct network_helper_opts rx_opts = {
162 		.timeout_ms = 1000,
163 		.noconnect = true,
164 	};
165 	struct network_helper_opts tx_ops = {
166 		.timeout_ms = 1000,
167 		.type = SOCK_RAW,
168 		.proto = IPPROTO_RAW,
169 		.noconnect = true,
170 	};
171 	struct sockaddr_storage caddr;
172 	struct ip_check_defrag *skel;
173 	struct nstoken *nstoken;
174 	int client_tx_fd = -1;
175 	int client_rx_fd = -1;
176 	socklen_t caddr_len;
177 	int srv_fd = -1;
178 	char buf[1024];
179 	int len, err;
180 
181 	skel = ip_check_defrag__open_and_load();
182 	if (!ASSERT_OK_PTR(skel, "skel_open"))
183 		return;
184 
185 	if (!ASSERT_OK(setup_topology(ipv6), "setup_topology"))
186 		goto out;
187 
188 	if (!ASSERT_OK(attach(skel, ipv6), "attach"))
189 		goto out;
190 
191 	/* Start server in ns1 */
192 	nstoken = open_netns(NS1);
193 	if (!ASSERT_OK_PTR(nstoken, "setns ns1"))
194 		goto out;
195 	srv_fd = start_server(ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, NULL, SERVER_PORT, 0);
196 	close_netns(nstoken);
197 	if (!ASSERT_GE(srv_fd, 0, "start_server"))
198 		goto out;
199 
200 	/* Open tx raw socket in ns0 */
201 	nstoken = open_netns(NS0);
202 	if (!ASSERT_OK_PTR(nstoken, "setns ns0"))
203 		goto out;
204 	client_tx_fd = connect_to_fd_opts(srv_fd, &tx_ops);
205 	close_netns(nstoken);
206 	if (!ASSERT_GE(client_tx_fd, 0, "connect_to_fd_opts"))
207 		goto out;
208 
209 	/* Open rx socket in ns0 */
210 	nstoken = open_netns(NS0);
211 	if (!ASSERT_OK_PTR(nstoken, "setns ns0"))
212 		goto out;
213 	client_rx_fd = connect_to_fd_opts(srv_fd, &rx_opts);
214 	close_netns(nstoken);
215 	if (!ASSERT_GE(client_rx_fd, 0, "connect_to_fd_opts"))
216 		goto out;
217 
218 	/* Bind rx socket to a premeditated port */
219 	memset(&caddr, 0, sizeof(caddr));
220 	nstoken = open_netns(NS0);
221 	if (!ASSERT_OK_PTR(nstoken, "setns ns0"))
222 		goto out;
223 	if (ipv6) {
224 		struct sockaddr_in6 *c = (struct sockaddr_in6 *)&caddr;
225 
226 		c->sin6_family = AF_INET6;
227 		inet_pton(AF_INET6, VETH0_ADDR6, &c->sin6_addr);
228 		c->sin6_port = htons(CLIENT_PORT);
229 		err = bind(client_rx_fd, (struct sockaddr *)c, sizeof(*c));
230 	} else {
231 		struct sockaddr_in *c = (struct sockaddr_in *)&caddr;
232 
233 		c->sin_family = AF_INET;
234 		inet_pton(AF_INET, VETH0_ADDR, &c->sin_addr);
235 		c->sin_port = htons(CLIENT_PORT);
236 		err = bind(client_rx_fd, (struct sockaddr *)c, sizeof(*c));
237 	}
238 	close_netns(nstoken);
239 	if (!ASSERT_OK(err, "bind"))
240 		goto out;
241 
242 	/* Send message in fragments */
243 	if (ipv6) {
244 		if (!ASSERT_OK(send_frags6(client_tx_fd), "send_frags6"))
245 			goto out;
246 	} else {
247 		if (!ASSERT_OK(send_frags(client_tx_fd), "send_frags"))
248 			goto out;
249 	}
250 
251 	if (!ASSERT_EQ(skel->bss->shootdowns, 0, "shootdowns"))
252 		goto out;
253 
254 	/* Receive reassembled msg on server and echo back to client */
255 	caddr_len = sizeof(caddr);
256 	len = recvfrom(srv_fd, buf, sizeof(buf), 0, (struct sockaddr *)&caddr, &caddr_len);
257 	if (!ASSERT_GE(len, 0, "server recvfrom"))
258 		goto out;
259 	len = sendto(srv_fd, buf, len, 0, (struct sockaddr *)&caddr, caddr_len);
260 	if (!ASSERT_GE(len, 0, "server sendto"))
261 		goto out;
262 
263 	/* Expect reassembed message to be echoed back */
264 	len = recvfrom(client_rx_fd, buf, sizeof(buf), 0, NULL, NULL);
265 	if (!ASSERT_EQ(len, sizeof(MAGIC_MESSAGE) - 1, "client short read"))
266 		goto out;
267 
268 out:
269 	if (client_rx_fd != -1)
270 		close(client_rx_fd);
271 	if (client_tx_fd != -1)
272 		close(client_tx_fd);
273 	if (srv_fd != -1)
274 		close(srv_fd);
275 	cleanup_topology();
276 	ip_check_defrag__destroy(skel);
277 }
278 
279 void test_bpf_ip_check_defrag(void)
280 {
281 	if (test__start_subtest("v4"))
282 		test_bpf_ip_check_defrag_ok(false);
283 	if (test__start_subtest("v6"))
284 		test_bpf_ip_check_defrag_ok(true);
285 }
286