1 // SPDX-License-Identifier: GPL-2.0
2 #include <error.h>
3 #include <netinet/tcp.h>
4 #include <test_progs.h>
5 #include "sockmap_helpers.h"
6 #include "test_skmsg_load_helpers.skel.h"
7 #include "test_sockmap_strp.skel.h"
8 
9 #define STRP_PKT_HEAD_LEN 4
10 #define STRP_PKT_BODY_LEN 6
11 #define STRP_PKT_FULL_LEN (STRP_PKT_HEAD_LEN + STRP_PKT_BODY_LEN)
12 
13 static const char packet[STRP_PKT_FULL_LEN] = "head+body\0";
14 static const int test_packet_num = 100;
15 
16 /* Current implementation of tcp_bpf_recvmsg_parser() invokes data_ready
17  * with sk held if an skb exists in sk_receive_queue. Then for the
18  * data_ready implementation of strparser, it will delay the read
19  * operation if sk is held and EAGAIN is returned.
20  */
sockmap_strp_consume_pre_data(int p)21 static int sockmap_strp_consume_pre_data(int p)
22 {
23 	int recvd;
24 	bool retried = false;
25 	char rcv[10];
26 
27 retry:
28 	errno = 0;
29 	recvd = recv_timeout(p, rcv, sizeof(rcv), 0, 1);
30 	if (recvd < 0 && errno == EAGAIN && retried == false) {
31 		/* On the first call, EAGAIN will certainly be returned.
32 		 * A 1-second wait is enough for the workqueue to finish.
33 		 */
34 		sleep(1);
35 		retried = true;
36 		goto retry;
37 	}
38 
39 	if (!ASSERT_EQ(recvd, STRP_PKT_FULL_LEN, "recv error or truncated data") ||
40 	    !ASSERT_OK(memcmp(packet, rcv, STRP_PKT_FULL_LEN),
41 				"data mismatch"))
42 		return -1;
43 	return 0;
44 }
45 
sockmap_strp_init(int * out_map,bool pass,bool need_parser)46 static struct test_sockmap_strp *sockmap_strp_init(int *out_map, bool pass,
47 						   bool need_parser)
48 {
49 	struct test_sockmap_strp *strp = NULL;
50 	int verdict, parser;
51 	int err;
52 
53 	strp = test_sockmap_strp__open_and_load();
54 	*out_map = bpf_map__fd(strp->maps.sock_map);
55 
56 	if (need_parser)
57 		parser = bpf_program__fd(strp->progs.prog_skb_parser_partial);
58 	else
59 		parser = bpf_program__fd(strp->progs.prog_skb_parser);
60 
61 	if (pass)
62 		verdict = bpf_program__fd(strp->progs.prog_skb_verdict_pass);
63 	else
64 		verdict = bpf_program__fd(strp->progs.prog_skb_verdict);
65 
66 	err = bpf_prog_attach(parser, *out_map, BPF_SK_SKB_STREAM_PARSER, 0);
67 	if (!ASSERT_OK(err, "bpf_prog_attach stream parser"))
68 		goto err;
69 
70 	err = bpf_prog_attach(verdict, *out_map, BPF_SK_SKB_STREAM_VERDICT, 0);
71 	if (!ASSERT_OK(err, "bpf_prog_attach stream verdict"))
72 		goto err;
73 
74 	return strp;
75 err:
76 	test_sockmap_strp__destroy(strp);
77 	return NULL;
78 }
79 
80 /* Dispatch packets to different socket by packet size:
81  *
82  *                      ------  ------
83  *                     | pkt4 || pkt1 |... > remote socket
84  *  ------ ------     / ------  ------
85  * | pkt8 | pkt7 |...
86  *  ------ ------     \ ------  ------
87  *                     | pkt3 || pkt2 |... > local socket
88  *                      ------  ------
89  */
test_sockmap_strp_dispatch_pkt(int family,int sotype)90 static void test_sockmap_strp_dispatch_pkt(int family, int sotype)
91 {
92 	int i, j, zero = 0, one = 1, recvd;
93 	int err, map;
94 	int c0 = -1, p0 = -1, c1 = -1, p1 = -1;
95 	struct test_sockmap_strp *strp = NULL;
96 	int test_cnt = 6;
97 	char rcv[10];
98 	struct {
99 		char	data[7];
100 		int	data_len;
101 		int	send_cnt;
102 		int	*receiver;
103 	} send_dir[2] = {
104 		/* data expected to deliver to local */
105 		{"llllll", 6, 0, &p0},
106 		/* data expected to deliver to remote */
107 		{"rrrrr",  5, 0, &c1}
108 	};
109 
110 	strp = sockmap_strp_init(&map, false, false);
111 	if (!ASSERT_TRUE(strp, "sockmap_strp_init"))
112 		return;
113 
114 	err = create_socket_pairs(family, sotype, &c0, &c1, &p0, &p1);
115 	if (!ASSERT_OK(err, "create_socket_pairs()"))
116 		goto out;
117 
118 	err = bpf_map_update_elem(map, &zero, &p0, BPF_NOEXIST);
119 	if (!ASSERT_OK(err, "bpf_map_update_elem(p0)"))
120 		goto out_close;
121 
122 	err = bpf_map_update_elem(map, &one, &p1, BPF_NOEXIST);
123 	if (!ASSERT_OK(err, "bpf_map_update_elem(p1)"))
124 		goto out_close;
125 
126 	err = setsockopt(c1, IPPROTO_TCP, TCP_NODELAY, &zero, sizeof(zero));
127 	if (!ASSERT_OK(err, "setsockopt(TCP_NODELAY)"))
128 		goto out_close;
129 
130 	/* deliver data with data size greater than 5 to local */
131 	strp->data->verdict_max_size = 5;
132 
133 	for (i = 0; i < test_cnt; i++) {
134 		int d = i % 2;
135 
136 		xsend(c0, send_dir[d].data, send_dir[d].data_len, 0);
137 		send_dir[d].send_cnt++;
138 	}
139 
140 	for (i = 0; i < 2; i++) {
141 		for (j = 0; j < send_dir[i].send_cnt; j++) {
142 			int expected = send_dir[i].data_len;
143 
144 			recvd = recv_timeout(*send_dir[i].receiver, rcv,
145 					     expected, MSG_DONTWAIT,
146 					     IO_TIMEOUT_SEC);
147 			if (!ASSERT_EQ(recvd, expected, "recv_timeout()"))
148 				goto out_close;
149 			if (!ASSERT_OK(memcmp(send_dir[i].data, rcv, recvd),
150 				       "data mismatch"))
151 				goto out_close;
152 		}
153 	}
154 out_close:
155 	close(c0);
156 	close(c1);
157 	close(p0);
158 	close(p1);
159 out:
160 	test_sockmap_strp__destroy(strp);
161 }
162 
163 /* We have multiple packets in one skb
164  * ------------ ------------ ------------
165  * |  packet1  |   packet2  |  ...
166  * ------------ ------------ ------------
167  */
test_sockmap_strp_multiple_pkt(int family,int sotype)168 static void test_sockmap_strp_multiple_pkt(int family, int sotype)
169 {
170 	int i, zero = 0;
171 	int sent, recvd, total;
172 	int err, map;
173 	int c = -1, p = -1;
174 	struct test_sockmap_strp *strp = NULL;
175 	char *snd = NULL, *rcv = NULL;
176 
177 	strp = sockmap_strp_init(&map, true, true);
178 	if (!ASSERT_TRUE(strp, "sockmap_strp_init"))
179 		return;
180 
181 	err = create_pair(family, sotype, &c, &p);
182 	if (err)
183 		goto out;
184 
185 	err = bpf_map_update_elem(map, &zero, &p, BPF_NOEXIST);
186 	if (!ASSERT_OK(err, "bpf_map_update_elem(zero, p)"))
187 		goto out_close;
188 
189 	/* construct multiple packets in one buffer */
190 	total = test_packet_num * STRP_PKT_FULL_LEN;
191 	snd = malloc(total);
192 	rcv = malloc(total + 1);
193 	if (!ASSERT_TRUE(snd, "malloc(snd)") ||
194 	    !ASSERT_TRUE(rcv, "malloc(rcv)"))
195 		goto out_close;
196 
197 	for (i = 0; i < test_packet_num; i++) {
198 		memcpy(snd + i * STRP_PKT_FULL_LEN,
199 		       packet, STRP_PKT_FULL_LEN);
200 	}
201 
202 	sent = xsend(c, snd, total, 0);
203 	if (!ASSERT_EQ(sent, total, "xsend(c)"))
204 		goto out_close;
205 
206 	/* try to recv one more byte to avoid truncation check */
207 	recvd = recv_timeout(p, rcv, total + 1, MSG_DONTWAIT, IO_TIMEOUT_SEC);
208 	if (!ASSERT_EQ(recvd, total, "recv(rcv)"))
209 		goto out_close;
210 
211 	/* we sent TCP segment with multiple encapsulation
212 	 * then check whether packets are handled correctly
213 	 */
214 	if (!ASSERT_OK(memcmp(snd, rcv, total), "data mismatch"))
215 		goto out_close;
216 
217 out_close:
218 	close(c);
219 	close(p);
220 	if (snd)
221 		free(snd);
222 	if (rcv)
223 		free(rcv);
224 out:
225 	test_sockmap_strp__destroy(strp);
226 }
227 
228 /* Test strparser with partial read */
test_sockmap_strp_partial_read(int family,int sotype)229 static void test_sockmap_strp_partial_read(int family, int sotype)
230 {
231 	int zero = 0, recvd, off;
232 	int err, map;
233 	int c = -1, p = -1;
234 	struct test_sockmap_strp *strp = NULL;
235 	char rcv[STRP_PKT_FULL_LEN + 1] = "0";
236 
237 	strp = sockmap_strp_init(&map, true, true);
238 	if (!ASSERT_TRUE(strp, "sockmap_strp_init"))
239 		return;
240 
241 	err = create_pair(family, sotype, &c, &p);
242 	if (err)
243 		goto out;
244 
245 	/* sk_data_ready of 'p' will be replaced by strparser handler */
246 	err = bpf_map_update_elem(map, &zero, &p, BPF_NOEXIST);
247 	if (!ASSERT_OK(err, "bpf_map_update_elem(zero, p)"))
248 		goto out_close;
249 
250 	/* 1.1 send partial head, 1 byte header left */
251 	off = STRP_PKT_HEAD_LEN - 1;
252 	xsend(c, packet, off, 0);
253 	recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1);
254 	if (!ASSERT_EQ(-1, recvd, "partial head sent, expected no data"))
255 		goto out_close;
256 
257 	/* 1.2 send remaining head and body */
258 	xsend(c, packet + off, STRP_PKT_FULL_LEN - off, 0);
259 	recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, IO_TIMEOUT_SEC);
260 	if (!ASSERT_EQ(recvd, STRP_PKT_FULL_LEN, "expected full data"))
261 		goto out_close;
262 
263 	/* 2.1 send partial head, 1 byte header left */
264 	off = STRP_PKT_HEAD_LEN - 1;
265 	xsend(c, packet, off, 0);
266 
267 	/* 2.2 send remaining head and partial body, 1 byte body left */
268 	xsend(c, packet + off, STRP_PKT_FULL_LEN - off - 1, 0);
269 	off = STRP_PKT_FULL_LEN - 1;
270 	recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1);
271 	if (!ASSERT_EQ(-1, recvd, "partial body sent, expected no data"))
272 		goto out_close;
273 
274 	/* 2.3 send remaining body */
275 	xsend(c, packet + off, STRP_PKT_FULL_LEN - off, 0);
276 	recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, IO_TIMEOUT_SEC);
277 	if (!ASSERT_EQ(recvd, STRP_PKT_FULL_LEN, "expected full data"))
278 		goto out_close;
279 
280 out_close:
281 	close(c);
282 	close(p);
283 
284 out:
285 	test_sockmap_strp__destroy(strp);
286 }
287 
288 /* Test simple socket read/write with strparser + FIONREAD */
test_sockmap_strp_pass(int family,int sotype,bool fionread)289 static void test_sockmap_strp_pass(int family, int sotype, bool fionread)
290 {
291 	int zero = 0, pkt_size = STRP_PKT_FULL_LEN, sent, recvd, avail;
292 	int err, map;
293 	int c = -1, p = -1;
294 	int test_cnt = 10, i;
295 	struct test_sockmap_strp *strp = NULL;
296 	char rcv[STRP_PKT_FULL_LEN + 1] = "0";
297 
298 	strp = sockmap_strp_init(&map, true, true);
299 	if (!ASSERT_TRUE(strp, "sockmap_strp_init"))
300 		return;
301 
302 	err = create_pair(family, sotype, &c, &p);
303 	if (err)
304 		goto out;
305 
306 	/* inject some data before bpf process, it should be read
307 	 * correctly because we check sk_receive_queue in
308 	 * tcp_bpf_recvmsg_parser().
309 	 */
310 	sent = xsend(c, packet, pkt_size, 0);
311 	if (!ASSERT_EQ(sent, pkt_size, "xsend(pre-data)"))
312 		goto out_close;
313 
314 	/* sk_data_ready of 'p' will be replaced by strparser handler */
315 	err = bpf_map_update_elem(map, &zero, &p, BPF_NOEXIST);
316 	if (!ASSERT_OK(err, "bpf_map_update_elem(p)"))
317 		goto out_close;
318 
319 	/* consume previous data we injected */
320 	if (sockmap_strp_consume_pre_data(p))
321 		goto out_close;
322 
323 	/* Previously, we encountered issues such as deadlocks and
324 	 * sequence errors that resulted in the inability to read
325 	 * continuously. Therefore, we perform multiple iterations
326 	 * of testing here.
327 	 */
328 	for (i = 0; i < test_cnt; i++) {
329 		sent = xsend(c, packet, pkt_size, 0);
330 		if (!ASSERT_EQ(sent, pkt_size, "xsend(c)"))
331 			goto out_close;
332 
333 		recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT,
334 				     IO_TIMEOUT_SEC);
335 		if (!ASSERT_EQ(recvd, pkt_size, "recv_timeout(p)") ||
336 		    !ASSERT_OK(memcmp(packet, rcv, pkt_size),
337 				  "memcmp, data mismatch"))
338 			goto out_close;
339 	}
340 
341 	if (fionread) {
342 		sent = xsend(c, packet, pkt_size, 0);
343 		if (!ASSERT_EQ(sent, pkt_size, "second xsend(c)"))
344 			goto out_close;
345 
346 		err = ioctl(p, FIONREAD, &avail);
347 		if (!ASSERT_OK(err, "ioctl(FIONREAD) error") ||
348 		    !ASSERT_EQ(avail, pkt_size, "ioctl(FIONREAD)"))
349 			goto out_close;
350 
351 		recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT,
352 				     IO_TIMEOUT_SEC);
353 		if (!ASSERT_EQ(recvd, pkt_size, "second recv_timeout(p)") ||
354 		    !ASSERT_OK(memcmp(packet, rcv, pkt_size),
355 			      "second memcmp, data mismatch"))
356 			goto out_close;
357 	}
358 
359 out_close:
360 	close(c);
361 	close(p);
362 
363 out:
364 	test_sockmap_strp__destroy(strp);
365 }
366 
367 /* Test strparser with verdict mode */
test_sockmap_strp_verdict(int family,int sotype)368 static void test_sockmap_strp_verdict(int family, int sotype)
369 {
370 	int zero = 0, one = 1, sent, recvd, off;
371 	int err, map;
372 	int c0 = -1, p0 = -1, c1 = -1, p1 = -1;
373 	struct test_sockmap_strp *strp = NULL;
374 	char rcv[STRP_PKT_FULL_LEN + 1] = "0";
375 
376 	strp = sockmap_strp_init(&map, false, true);
377 	if (!ASSERT_TRUE(strp, "sockmap_strp_init"))
378 		return;
379 
380 	/* We simulate a reverse proxy server.
381 	 * When p0 receives data from c0, we forward it to c1.
382 	 * From c1's perspective, it will consider this data
383 	 * as being sent by p1.
384 	 */
385 	err = create_socket_pairs(family, sotype, &c0, &c1, &p0, &p1);
386 	if (!ASSERT_OK(err, "create_socket_pairs()"))
387 		goto out;
388 
389 	err = bpf_map_update_elem(map, &zero, &p0, BPF_NOEXIST);
390 	if (!ASSERT_OK(err, "bpf_map_update_elem(p0)"))
391 		goto out_close;
392 
393 	err = bpf_map_update_elem(map, &one, &p1, BPF_NOEXIST);
394 	if (!ASSERT_OK(err, "bpf_map_update_elem(p1)"))
395 		goto out_close;
396 
397 	sent = xsend(c0, packet, STRP_PKT_FULL_LEN, 0);
398 	if (!ASSERT_EQ(sent, STRP_PKT_FULL_LEN, "xsend(c0)"))
399 		goto out_close;
400 
401 	recvd = recv_timeout(c1, rcv, sizeof(rcv), MSG_DONTWAIT,
402 			     IO_TIMEOUT_SEC);
403 	if (!ASSERT_EQ(recvd, STRP_PKT_FULL_LEN, "recv_timeout(c1)") ||
404 	    !ASSERT_OK(memcmp(packet, rcv, STRP_PKT_FULL_LEN),
405 			  "received data does not match the sent data"))
406 		goto out_close;
407 
408 	/* send again to ensure the stream is functioning correctly. */
409 	sent = xsend(c0, packet, STRP_PKT_FULL_LEN, 0);
410 	if (!ASSERT_EQ(sent, STRP_PKT_FULL_LEN, "second xsend(c0)"))
411 		goto out_close;
412 
413 	/* partial read */
414 	off = STRP_PKT_FULL_LEN / 2;
415 	recvd = recv_timeout(c1, rcv, off, MSG_DONTWAIT,
416 			     IO_TIMEOUT_SEC);
417 	recvd += recv_timeout(c1, rcv + off, sizeof(rcv) - off, MSG_DONTWAIT,
418 			      IO_TIMEOUT_SEC);
419 
420 	if (!ASSERT_EQ(recvd, STRP_PKT_FULL_LEN, "partial recv_timeout(c1)") ||
421 	    !ASSERT_OK(memcmp(packet, rcv, STRP_PKT_FULL_LEN),
422 			  "partial received data does not match the sent data"))
423 		goto out_close;
424 
425 out_close:
426 	close(c0);
427 	close(c1);
428 	close(p0);
429 	close(p1);
430 out:
431 	test_sockmap_strp__destroy(strp);
432 }
433 
test_sockmap_strp(void)434 void test_sockmap_strp(void)
435 {
436 	if (test__start_subtest("sockmap strp tcp pass"))
437 		test_sockmap_strp_pass(AF_INET, SOCK_STREAM, false);
438 	if (test__start_subtest("sockmap strp tcp v6 pass"))
439 		test_sockmap_strp_pass(AF_INET6, SOCK_STREAM, false);
440 	if (test__start_subtest("sockmap strp tcp pass fionread"))
441 		test_sockmap_strp_pass(AF_INET, SOCK_STREAM, true);
442 	if (test__start_subtest("sockmap strp tcp v6 pass fionread"))
443 		test_sockmap_strp_pass(AF_INET6, SOCK_STREAM, true);
444 	if (test__start_subtest("sockmap strp tcp verdict"))
445 		test_sockmap_strp_verdict(AF_INET, SOCK_STREAM);
446 	if (test__start_subtest("sockmap strp tcp v6 verdict"))
447 		test_sockmap_strp_verdict(AF_INET6, SOCK_STREAM);
448 	if (test__start_subtest("sockmap strp tcp partial read"))
449 		test_sockmap_strp_partial_read(AF_INET, SOCK_STREAM);
450 	if (test__start_subtest("sockmap strp tcp multiple packets"))
451 		test_sockmap_strp_multiple_pkt(AF_INET, SOCK_STREAM);
452 	if (test__start_subtest("sockmap strp tcp dispatch"))
453 		test_sockmap_strp_dispatch_pkt(AF_INET, SOCK_STREAM);
454 }
455