File Index
Files and directories
Sign in
FILE

cls_redirect.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/prog_tests/cls_redirect.c C
  1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  2// Copyright (c) 2020 Cloudflare
  3
  4#define _GNU_SOURCE
  5
  6#include <arpa/inet.h>
  7#include <string.h>
  8
  9#include <linux/pkt_cls.h>
 10#include <netinet/tcp.h>
 11
 12#include <test_progs.h>
 13#include "network_helpers.h"
 14
 15#include "progs/test_cls_redirect.h"
 16#include "test_cls_redirect.skel.h"
 17#include "test_cls_redirect_dynptr.skel.h"
 18#include "test_cls_redirect_subprogs.skel.h"
 19
 20#define ENCAP_IP INADDR_LOOPBACK
 21#define ENCAP_PORT (1234)
 22
 23static int duration = 0;
 24
 25
 26static bool set_up_conn(const struct sockaddr_storage *addr, socklen_t len, int type,
 27			int *server, int *conn,
 28			struct sockaddr_storage *src,
 29			struct sockaddr_storage *dst)
 30{
 31	struct sockaddr_storage ss;
 32	socklen_t slen = sizeof(ss);
 33
 34	*server = start_server_addr(type, addr, len, NULL);
 35	if (*server < 0)
 36		return false;
 37
 38	if (CHECK_FAIL(getsockname(*server, (struct sockaddr *)&ss, &slen)))
 39		goto close_server;
 40
 41	*conn = connect_to_addr(type, &ss, slen, NULL);
 42	if (*conn < 0)
 43		goto close_server;
 44
 45	/* We want to simulate packets arriving at conn, so we have to
 46	 * swap src and dst.
 47	 */
 48	slen = sizeof(*dst);
 49	if (CHECK_FAIL(getsockname(*conn, (struct sockaddr *)dst, &slen)))
 50		goto close_conn;
 51
 52	slen = sizeof(*src);
 53	if (CHECK_FAIL(getpeername(*conn, (struct sockaddr *)src, &slen)))
 54		goto close_conn;
 55
 56	return true;
 57
 58close_conn:
 59	close(*conn);
 60	*conn = -1;
 61close_server:
 62	close(*server);
 63	*server = -1;
 64	return false;
 65}
 66
 67static socklen_t prepare_addr(struct sockaddr_storage *addr, int family)
 68{
 69	struct sockaddr_in *addr4;
 70	struct sockaddr_in6 *addr6;
 71	memset(addr, 0, sizeof(*addr));
 72
 73	switch (family) {
 74	case AF_INET:
 75		addr4 = (struct sockaddr_in *)addr;
 76		addr4->sin_family = family;
 77		addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 78		return sizeof(*addr4);
 79	case AF_INET6:
 80		addr6 = (struct sockaddr_in6 *)addr;
 81		addr6->sin6_family = family;
 82		addr6->sin6_addr = in6addr_loopback;
 83		return sizeof(*addr6);
 84	default:
 85		fprintf(stderr, "Invalid family %d", family);
 86		return 0;
 87	}
 88}
 89
 90static bool was_decapsulated(struct bpf_test_run_opts *tattr)
 91{
 92	return tattr->data_size_out < tattr->data_size_in;
 93}
 94
 95enum type {
 96	UDP,
 97	TCP,
 98	__NR_KIND,
 99};
100
101enum hops {
102	NO_HOPS,
103	ONE_HOP,
104};
105
106enum flags {
107	NONE,
108	SYN,
109	ACK,
110};
111
112enum conn {
113	KNOWN_CONN,
114	UNKNOWN_CONN,
115};
116
117enum result {
118	ACCEPT,
119	FORWARD,
120};
121
122struct test_cfg {
123	enum type type;
124	enum result result;
125	enum conn conn;
126	enum hops hops;
127	enum flags flags;
128};
129
130static int test_str(void *buf, size_t len, const struct test_cfg *test,
131		    int family)
132{
133	const char *family_str, *type, *conn, *hops, *result, *flags;
134
135	family_str = "IPv4";
136	if (family == AF_INET6)
137		family_str = "IPv6";
138
139	type = "TCP";
140	if (test->type == UDP)
141		type = "UDP";
142
143	conn = "known";
144	if (test->conn == UNKNOWN_CONN)
145		conn = "unknown";
146
147	hops = "no hops";
148	if (test->hops == ONE_HOP)
149		hops = "one hop";
150
151	result = "accept";
152	if (test->result == FORWARD)
153		result = "forward";
154
155	flags = "none";
156	if (test->flags == SYN)
157		flags = "SYN";
158	else if (test->flags == ACK)
159		flags = "ACK";
160
161	return snprintf(buf, len, "%s %s %s %s (%s, flags: %s)", family_str,
162			type, result, conn, hops, flags);
163}
164
165static struct test_cfg tests[] = {
166	{ TCP, ACCEPT, UNKNOWN_CONN, NO_HOPS, SYN },
167	{ TCP, ACCEPT, UNKNOWN_CONN, NO_HOPS, ACK },
168	{ TCP, FORWARD, UNKNOWN_CONN, ONE_HOP, ACK },
169	{ TCP, ACCEPT, KNOWN_CONN, ONE_HOP, ACK },
170	{ UDP, ACCEPT, UNKNOWN_CONN, NO_HOPS, NONE },
171	{ UDP, FORWARD, UNKNOWN_CONN, ONE_HOP, NONE },
172	{ UDP, ACCEPT, KNOWN_CONN, ONE_HOP, NONE },
173};
174
175static void encap_init(encap_headers_t *encap, uint8_t hop_count, uint8_t proto)
176{
177	const uint8_t hlen =
178		(sizeof(struct guehdr) / sizeof(uint32_t)) + hop_count;
179	*encap = (encap_headers_t){
180		.eth = { .h_proto = htons(ETH_P_IP) },
181		.ip = {
182			.ihl = 5,
183			.version = 4,
184			.ttl = IPDEFTTL,
185			.protocol = IPPROTO_UDP,
186			.daddr = htonl(ENCAP_IP)
187		},
188		.udp = {
189			.dest = htons(ENCAP_PORT),
190		},
191		.gue = {
192			.hlen = hlen,
193			.proto_ctype = proto
194		},
195		.unigue = {
196			.hop_count = hop_count
197		},
198	};
199}
200
201static size_t build_input(const struct test_cfg *test, void *const buf,
202			  const struct sockaddr_storage *src,
203			  const struct sockaddr_storage *dst)
204{
205	struct sockaddr_in6 *src_in6 = (struct sockaddr_in6 *)src;
206	struct sockaddr_in6 *dst_in6 = (struct sockaddr_in6 *)dst;
207	struct sockaddr_in *src_in = (struct sockaddr_in *)src;
208	struct sockaddr_in *dst_in = (struct sockaddr_in *)dst;
209	sa_family_t family = src->ss_family;
210	in_port_t sport, dport;
211	encap_headers_t encap;
212	struct iphdr ip;
213	struct ipv6hdr ipv6;
214	struct tcphdr tcp;
215	struct udphdr udp;
216	struct in_addr next_hop;
217	uint8_t *p = buf;
218	int proto;
219
220	sport = (family == AF_INET) ? src_in->sin_port : src_in6->sin6_port;
221	dport = (family == AF_INET) ? dst_in->sin_port : dst_in6->sin6_port;
222
223	proto = IPPROTO_IPIP;
224	if (family == AF_INET6)
225		proto = IPPROTO_IPV6;
226
227	encap_init(&encap, test->hops == ONE_HOP ? 1 : 0, proto);
228	p = mempcpy(p, &encap, sizeof(encap));
229
230	if (test->hops == ONE_HOP) {
231		next_hop = (struct in_addr){ .s_addr = htonl(0x7f000002) };
232		p = mempcpy(p, &next_hop, sizeof(next_hop));
233	}
234
235	proto = IPPROTO_TCP;
236	if (test->type == UDP)
237		proto = IPPROTO_UDP;
238
239	switch (family) {
240	case AF_INET:
241		ip = (struct iphdr){
242			.ihl = 5,
243			.version = 4,
244			.ttl = IPDEFTTL,
245			.protocol = proto,
246			.saddr = src_in->sin_addr.s_addr,
247			.daddr = dst_in->sin_addr.s_addr,
248		};
249		p = mempcpy(p, &ip, sizeof(ip));
250		break;
251	case AF_INET6:
252		ipv6 = (struct ipv6hdr){
253			.version = 6,
254			.hop_limit = IPDEFTTL,
255			.nexthdr = proto,
256			.saddr = src_in6->sin6_addr,
257			.daddr = dst_in6->sin6_addr,
258		};
259		p = mempcpy(p, &ipv6, sizeof(ipv6));
260		break;
261	default:
262		return 0;
263	}
264
265	if (test->conn == UNKNOWN_CONN)
266		sport--;
267
268	switch (test->type) {
269	case TCP:
270		tcp = (struct tcphdr){
271			.source = sport,
272			.dest = dport,
273			.syn = (test->flags == SYN),
274			.ack = (test->flags == ACK),
275		};
276		p = mempcpy(p, &tcp, sizeof(tcp));
277		break;
278	case UDP:
279		udp = (struct udphdr){
280			.source = sport,
281			.dest = dport,
282		};
283		p = mempcpy(p, &udp, sizeof(udp));
284		break;
285	default:
286		return 0;
287	}
288
289	return (void *)p - buf;
290}
291
292static void close_fds(int *fds, int n)
293{
294	int i;
295
296	for (i = 0; i < n; i++)
297		if (fds[i] > 0)
298			close(fds[i]);
299}
300
301static void test_cls_redirect_common(struct bpf_program *prog)
302{
303	LIBBPF_OPTS(bpf_test_run_opts, tattr);
304	int families[] = { AF_INET, AF_INET6 };
305	struct sockaddr_storage ss;
306	socklen_t slen;
307	int i, j, err, prog_fd;
308	int servers[__NR_KIND][ARRAY_SIZE(families)] = {};
309	int conns[__NR_KIND][ARRAY_SIZE(families)] = {};
310	struct sockaddr_storage srcs[__NR_KIND][ARRAY_SIZE(families)];
311	struct sockaddr_storage dsts[__NR_KIND][ARRAY_SIZE(families)];
312
313	for (i = 0; i < ARRAY_SIZE(families); i++) {
314		slen = prepare_addr(&ss, families[i]);
315		if (CHECK_FAIL(!slen))
316			goto cleanup;
317
318		if (CHECK_FAIL(!set_up_conn(&ss, slen, SOCK_DGRAM,
319					    &servers[UDP][i], &conns[UDP][i],
320					    &srcs[UDP][i], &dsts[UDP][i])))
321			goto cleanup;
322
323		if (CHECK_FAIL(!set_up_conn(&ss, slen, SOCK_STREAM,
324					    &servers[TCP][i], &conns[TCP][i],
325					    &srcs[TCP][i], &dsts[TCP][i])))
326			goto cleanup;
327	}
328
329	prog_fd = bpf_program__fd(prog);
330	for (i = 0; i < ARRAY_SIZE(tests); i++) {
331		struct test_cfg *test = &tests[i];
332
333		for (j = 0; j < ARRAY_SIZE(families); j++) {
334			struct sockaddr_storage *src = &srcs[test->type][j];
335			struct sockaddr_storage *dst = &dsts[test->type][j];
336			char input[256];
337			char tmp[256];
338
339			test_str(tmp, sizeof(tmp), test, families[j]);
340			if (!test__start_subtest(tmp))
341				continue;
342
343			tattr.data_out = tmp;
344			tattr.data_size_out = sizeof(tmp);
345
346			tattr.data_in = input;
347			tattr.data_size_in = build_input(test, input, src, dst);
348			if (CHECK_FAIL(!tattr.data_size_in))
349				continue;
350
351			err = bpf_prog_test_run_opts(prog_fd, &tattr);
352			if (CHECK_FAIL(err))
353				continue;
354
355			if (tattr.retval != TC_ACT_REDIRECT) {
356				PRINT_FAIL("expected TC_ACT_REDIRECT, got %d\n",
357					   tattr.retval);
358				continue;
359			}
360
361			switch (test->result) {
362			case ACCEPT:
363				if (CHECK_FAIL(!was_decapsulated(&tattr)))
364					continue;
365				break;
366			case FORWARD:
367				if (CHECK_FAIL(was_decapsulated(&tattr)))
368					continue;
369				break;
370			default:
371				PRINT_FAIL("unknown result %d\n", test->result);
372				continue;
373			}
374		}
375	}
376
377cleanup:
378	close_fds((int *)servers, sizeof(servers) / sizeof(servers[0][0]));
379	close_fds((int *)conns, sizeof(conns) / sizeof(conns[0][0]));
380}
381
382static void test_cls_redirect_dynptr(void)
383{
384	struct test_cls_redirect_dynptr *skel;
385	int err;
386
387	skel = test_cls_redirect_dynptr__open();
388	if (!ASSERT_OK_PTR(skel, "skel_open"))
389		return;
390
391	skel->rodata->ENCAPSULATION_IP = htonl(ENCAP_IP);
392	skel->rodata->ENCAPSULATION_PORT = htons(ENCAP_PORT);
393
394	err = test_cls_redirect_dynptr__load(skel);
395	if (!ASSERT_OK(err, "skel_load"))
396		goto cleanup;
397
398	test_cls_redirect_common(skel->progs.cls_redirect);
399
400cleanup:
401	test_cls_redirect_dynptr__destroy(skel);
402}
403
404static void test_cls_redirect_inlined(void)
405{
406	struct test_cls_redirect *skel;
407	int err;
408
409	skel = test_cls_redirect__open();
410	if (CHECK(!skel, "skel_open", "failed\n"))
411		return;
412
413	skel->rodata->ENCAPSULATION_IP = htonl(ENCAP_IP);
414	skel->rodata->ENCAPSULATION_PORT = htons(ENCAP_PORT);
415
416	err = test_cls_redirect__load(skel);
417	if (CHECK(err, "skel_load", "failed: %d\n", err))
418		goto cleanup;
419
420	test_cls_redirect_common(skel->progs.cls_redirect);
421
422cleanup:
423	test_cls_redirect__destroy(skel);
424}
425
426static void test_cls_redirect_subprogs(void)
427{
428	struct test_cls_redirect_subprogs *skel;
429	int err;
430
431	skel = test_cls_redirect_subprogs__open();
432	if (CHECK(!skel, "skel_open", "failed\n"))
433		return;
434
435	skel->rodata->ENCAPSULATION_IP = htonl(ENCAP_IP);
436	skel->rodata->ENCAPSULATION_PORT = htons(ENCAP_PORT);
437
438	err = test_cls_redirect_subprogs__load(skel);
439	if (CHECK(err, "skel_load", "failed: %d\n", err))
440		goto cleanup;
441
442	test_cls_redirect_common(skel->progs.cls_redirect);
443
444cleanup:
445	test_cls_redirect_subprogs__destroy(skel);
446}
447
448void test_cls_redirect(void)
449{
450	if (test__start_subtest("cls_redirect_inlined"))
451		test_cls_redirect_inlined();
452	if (test__start_subtest("cls_redirect_subprogs"))
453		test_cls_redirect_subprogs();
454	if (test__start_subtest("cls_redirect_dynptr"))
455		test_cls_redirect_dynptr();
456}
~/ Read-only file preview.