File Index
Files and directories
Sign in
FILE

sockmap_strp.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c C
  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
 13static const char packet[STRP_PKT_FULL_LEN] = "head+body\0";
 14static 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 */
 21static int sockmap_strp_consume_pre_data(int p)
 22{
 23	int recvd;
 24	bool retried = false;
 25	char rcv[10];
 26
 27retry:
 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
 46static 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;
 75err:
 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 */
 90static 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	}
154out_close:
155	close(c0);
156	close(c1);
157	close(p0);
158	close(p1);
159out:
160	test_sockmap_strp__destroy(strp);
161}
162
163/* We have multiple packets in one skb
164 * ------------ ------------ ------------
165 * |  packet1  |   packet2  |  ...
166 * ------------ ------------ ------------
167 */
168static 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
217out_close:
218	close(c);
219	close(p);
220	if (snd)
221		free(snd);
222	if (rcv)
223		free(rcv);
224out:
225	test_sockmap_strp__destroy(strp);
226}
227
228/* Test strparser with partial read */
229static 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
280out_close:
281	close(c);
282	close(p);
283
284out:
285	test_sockmap_strp__destroy(strp);
286}
287
288/* Test simple socket read/write with strparser + FIONREAD */
289static 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
359out_close:
360	close(c);
361	close(p);
362
363out:
364	test_sockmap_strp__destroy(strp);
365}
366
367/* Test strparser with verdict mode */
368static 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
425out_close:
426	close(c0);
427	close(c1);
428	close(p0);
429	close(p1);
430out:
431	test_sockmap_strp__destroy(strp);
432}
433
434void 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}
~/ Read-only file preview.