Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright (c) 2020 Facebook */
  3
  4#define _GNU_SOURCE
  5#include <netinet/in.h>
  6#include <arpa/inet.h>
  7#include <unistd.h>
  8#include <stdlib.h>
  9#include <string.h>
 10#include <errno.h>
 11#include <sched.h>
 12#include <net/if.h>
 13#include <linux/compiler.h>
 14#include <bpf/libbpf.h>
 15
 16#include "network_helpers.h"
 17#include "test_progs.h"
 18#include "test_btf_skc_cls_ingress.skel.h"
 19
 20#define TEST_NS "skc_cls_ingress"
 21
 22#define BIT(n)		(1 << (n))
 23#define TEST_MODE_IPV4	BIT(0)
 24#define TEST_MODE_IPV6	BIT(1)
 25#define TEST_MODE_DUAL	(TEST_MODE_IPV4 | TEST_MODE_IPV6)
 26
 27#define SERVER_ADDR_IPV4	"127.0.0.1"
 28#define SERVER_ADDR_IPV6	"::1"
 29#define SERVER_ADDR_DUAL	"::0"
 30/* RFC791, 576 for minimal IPv4 datagram, minus 40 bytes of TCP header */
 31#define MIN_IPV4_MSS		536
 32
 33static struct netns_obj *prepare_netns(struct test_btf_skc_cls_ingress *skel)
 34{
 35	LIBBPF_OPTS(bpf_tc_hook, qdisc_lo, .attach_point = BPF_TC_INGRESS);
 36	LIBBPF_OPTS(bpf_tc_opts, tc_attach,
 37		    .prog_fd = bpf_program__fd(skel->progs.cls_ingress));
 38	struct netns_obj *ns = NULL;
 39
 40	ns = netns_new(TEST_NS, true);
 41	if (!ASSERT_OK_PTR(ns, "create and join netns"))
 42		return ns;
 43
 44	qdisc_lo.ifindex = if_nametoindex("lo");
 45	if (!ASSERT_OK(bpf_tc_hook_create(&qdisc_lo), "qdisc add dev lo clsact"))
 46		goto free_ns;
 47
 48	if (!ASSERT_OK(bpf_tc_attach(&qdisc_lo, &tc_attach),
 49		       "filter add dev lo ingress"))
 50		goto free_ns;
 51
 52	/* Ensure 20 bytes options (i.e. in total 40 bytes tcp header) for the
 53	 * bpf_tcp_gen_syncookie() helper.
 54	 */
 55	if (write_sysctl("/proc/sys/net/ipv4/tcp_window_scaling", "1") ||
 56	    write_sysctl("/proc/sys/net/ipv4/tcp_timestamps", "1") ||
 57	    write_sysctl("/proc/sys/net/ipv4/tcp_sack", "1"))
 58		goto free_ns;
 59
 60	return ns;
 61
 62free_ns:
 63	netns_free(ns);
 64	return NULL;
 65}
 66
 67static void reset_test(struct test_btf_skc_cls_ingress *skel)
 68{
 69	memset(&skel->bss->srv_sa4, 0, sizeof(skel->bss->srv_sa4));
 70	memset(&skel->bss->srv_sa6, 0, sizeof(skel->bss->srv_sa6));
 71	skel->bss->listen_tp_sport = 0;
 72	skel->bss->req_sk_sport = 0;
 73	skel->bss->recv_cookie = 0;
 74	skel->bss->gen_cookie = 0;
 75	skel->bss->linum = 0;
 76	skel->bss->mss = 0;
 77}
 78
 79static void print_err_line(struct test_btf_skc_cls_ingress *skel)
 80{
 81	if (skel->bss->linum)
 82		printf("bpf prog error at line %u\n", skel->bss->linum);
 83}
 84
 85static int v6only_true(int fd, void *opts)
 86{
 87	int mode = true;
 88
 89	return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode));
 90}
 91
 92static int v6only_false(int fd, void *opts)
 93{
 94	int mode = false;
 95
 96	return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode));
 97}
 98
 99static void run_test(struct test_btf_skc_cls_ingress *skel, bool gen_cookies,
100		     int ip_mode)
101{
102	const char *tcp_syncookies = gen_cookies ? "2" : "1";
103	int listen_fd = -1, cli_fd = -1, srv_fd = -1, err;
104	struct network_helper_opts opts = { 0 };
105	struct sockaddr_storage *addr;
106	struct sockaddr_in6 srv_sa6;
107	struct sockaddr_in srv_sa4;
108	socklen_t addr_len;
109	int sock_family;
110	char *srv_addr;
111	int srv_port;
112
113	switch (ip_mode) {
114	case TEST_MODE_IPV4:
115		sock_family = AF_INET;
116		srv_addr = SERVER_ADDR_IPV4;
117		addr = (struct sockaddr_storage *)&srv_sa4;
118		addr_len = sizeof(srv_sa4);
119		break;
120	case TEST_MODE_IPV6:
121		opts.post_socket_cb = v6only_true;
122		sock_family = AF_INET6;
123		srv_addr = SERVER_ADDR_IPV6;
124		addr = (struct sockaddr_storage *)&srv_sa6;
125		addr_len = sizeof(srv_sa6);
126		break;
127	case TEST_MODE_DUAL:
128		opts.post_socket_cb = v6only_false;
129		sock_family = AF_INET6;
130		srv_addr = SERVER_ADDR_DUAL;
131		addr = (struct sockaddr_storage *)&srv_sa6;
132		addr_len = sizeof(srv_sa6);
133		break;
134	default:
135		PRINT_FAIL("Unknown IP mode %d", ip_mode);
136		return;
137	}
138
139	if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", tcp_syncookies))
140		return;
141
142	listen_fd = start_server_str(sock_family, SOCK_STREAM, srv_addr,  0,
143				     &opts);
144	if (!ASSERT_OK_FD(listen_fd, "start server"))
145		return;
146
147	err = getsockname(listen_fd, (struct sockaddr *)addr, &addr_len);
148	if (!ASSERT_OK(err, "getsockname(listen_fd)"))
149		goto done;
150
151	switch (ip_mode) {
152	case TEST_MODE_IPV4:
153		memcpy(&skel->bss->srv_sa4, &srv_sa4, sizeof(srv_sa4));
154		srv_port = ntohs(srv_sa4.sin_port);
155		break;
156	case TEST_MODE_IPV6:
157	case TEST_MODE_DUAL:
158		memcpy(&skel->bss->srv_sa6, &srv_sa6, sizeof(srv_sa6));
159		srv_port = ntohs(srv_sa6.sin6_port);
160		break;
161	default:
162		goto done;
163	}
164
165	cli_fd = connect_to_fd(listen_fd, 0);
166	if (!ASSERT_OK_FD(cli_fd, "connect client"))
167		goto done;
168
169	srv_fd = accept(listen_fd, NULL, NULL);
170	if (!ASSERT_OK_FD(srv_fd, "accept connection"))
171		goto done;
172
173	ASSERT_EQ(skel->bss->listen_tp_sport, srv_port, "listen tp src port");
174
175	if (!gen_cookies) {
176		ASSERT_EQ(skel->bss->req_sk_sport, srv_port,
177			  "request socket source port with syncookies disabled");
178		ASSERT_EQ(skel->bss->gen_cookie, 0,
179			  "generated syncookie with syncookies disabled");
180		ASSERT_EQ(skel->bss->recv_cookie, 0,
181			  "received syncookie with syncookies disabled");
182	} else {
183		ASSERT_EQ(skel->bss->req_sk_sport, 0,
184			  "request socket source port with syncookies enabled");
185		ASSERT_NEQ(skel->bss->gen_cookie, 0,
186			   "syncookie properly generated");
187		ASSERT_EQ(skel->bss->gen_cookie, skel->bss->recv_cookie,
188			  "matching syncookies on client and server");
189		ASSERT_GT(skel->bss->mss, MIN_IPV4_MSS,
190			  "MSS in cookie min value");
191		ASSERT_LT(skel->bss->mss, USHRT_MAX,
192			  "MSS in cookie max value");
193	}
194
195done:
196	if (listen_fd != -1)
197		close(listen_fd);
198	if (cli_fd != -1)
199		close(cli_fd);
200	if (srv_fd != -1)
201		close(srv_fd);
202}
203
204static void test_conn_ipv4(struct test_btf_skc_cls_ingress *skel)
205{
206	run_test(skel, false, TEST_MODE_IPV4);
207}
208
209static void test_conn_ipv6(struct test_btf_skc_cls_ingress *skel)
210{
211	run_test(skel, false, TEST_MODE_IPV6);
212}
213
214static void test_conn_dual(struct test_btf_skc_cls_ingress *skel)
215{
216	run_test(skel, false, TEST_MODE_DUAL);
217}
218
219static void test_syncookie_ipv4(struct test_btf_skc_cls_ingress *skel)
220{
221	run_test(skel, true, TEST_MODE_IPV4);
222}
223
224static void test_syncookie_ipv6(struct test_btf_skc_cls_ingress *skel)
225{
226	run_test(skel, true, TEST_MODE_IPV6);
227}
228
229static void test_syncookie_dual(struct test_btf_skc_cls_ingress *skel)
230{
231	run_test(skel, true, TEST_MODE_DUAL);
232}
233
234struct test {
235	const char *desc;
236	void (*run)(struct test_btf_skc_cls_ingress *skel);
237};
238
239#define DEF_TEST(name) { #name, test_##name }
240static struct test tests[] = {
241	DEF_TEST(conn_ipv4),
242	DEF_TEST(conn_ipv6),
243	DEF_TEST(conn_dual),
244	DEF_TEST(syncookie_ipv4),
245	DEF_TEST(syncookie_ipv6),
246	DEF_TEST(syncookie_dual),
247};
248
249void test_btf_skc_cls_ingress(void)
250{
251	struct test_btf_skc_cls_ingress *skel;
252	struct netns_obj *ns;
253	int i;
254
255	skel = test_btf_skc_cls_ingress__open_and_load();
256	if (!ASSERT_OK_PTR(skel, "test_btf_skc_cls_ingress__open_and_load"))
257		return;
258
259	for (i = 0; i < ARRAY_SIZE(tests); i++) {
260		if (!test__start_subtest(tests[i].desc))
261			continue;
262
263		ns = prepare_netns(skel);
264		if (!ns)
265			break;
266
267		tests[i].run(skel);
268
269		print_err_line(skel);
270		reset_test(skel);
271		netns_free(ns);
272	}
273
274	test_btf_skc_cls_ingress__destroy(skel);
275}