Linux Audio

Check our new training course

Linux kernel drivers training

Mar 31-Apr 9, 2025, special US time zones
Register
Loading...
  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
 41static 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;
 71fail:
 72	return -1;
 73}
 74
 75static 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
 81static 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
 92	skel->links.defrag = bpf_program__attach_netfilter(skel->progs.defrag, &opts);
 93	if (!ASSERT_OK_PTR(skel->links.defrag, "program attach"))
 94		goto out;
 95
 96	err = 0;
 97out:
 98	close_netns(nstoken);
 99	return err;
100}
101
102static int send_frags(int client)
103{
104	struct sockaddr_storage saddr;
105	struct sockaddr *saddr_p;
106	socklen_t saddr_len;
107	int err;
108
109	saddr_p = (struct sockaddr *)&saddr;
110	err = make_sockaddr(AF_INET, VETH1_ADDR, SERVER_PORT, &saddr, &saddr_len);
111	if (!ASSERT_OK(err, "make_sockaddr"))
112		return -1;
113
114	err = sendto(client, frag_0, sizeof(frag_0), 0, saddr_p, saddr_len);
115	if (!ASSERT_GE(err, 0, "sendto frag_0"))
116		return -1;
117
118	err = sendto(client, frag_1, sizeof(frag_1), 0, saddr_p, saddr_len);
119	if (!ASSERT_GE(err, 0, "sendto frag_1"))
120		return -1;
121
122	err = sendto(client, frag_2, sizeof(frag_2), 0, saddr_p, saddr_len);
123	if (!ASSERT_GE(err, 0, "sendto frag_2"))
124		return -1;
125
126	return 0;
127}
128
129static int send_frags6(int client)
130{
131	struct sockaddr_storage saddr;
132	struct sockaddr *saddr_p;
133	socklen_t saddr_len;
134	int err;
135
136	saddr_p = (struct sockaddr *)&saddr;
137	/* Port needs to be set to 0 for raw ipv6 socket for some reason */
138	err = make_sockaddr(AF_INET6, VETH1_ADDR6, 0, &saddr, &saddr_len);
139	if (!ASSERT_OK(err, "make_sockaddr"))
140		return -1;
141
142	err = sendto(client, frag6_0, sizeof(frag6_0), 0, saddr_p, saddr_len);
143	if (!ASSERT_GE(err, 0, "sendto frag6_0"))
144		return -1;
145
146	err = sendto(client, frag6_1, sizeof(frag6_1), 0, saddr_p, saddr_len);
147	if (!ASSERT_GE(err, 0, "sendto frag6_1"))
148		return -1;
149
150	err = sendto(client, frag6_2, sizeof(frag6_2), 0, saddr_p, saddr_len);
151	if (!ASSERT_GE(err, 0, "sendto frag6_2"))
152		return -1;
153
154	return 0;
155}
156
157void test_bpf_ip_check_defrag_ok(bool ipv6)
158{
159	struct network_helper_opts rx_opts = {
160		.timeout_ms = 1000,
161		.noconnect = true,
162	};
163	struct network_helper_opts tx_ops = {
164		.timeout_ms = 1000,
165		.type = SOCK_RAW,
166		.proto = IPPROTO_RAW,
167		.noconnect = true,
168	};
169	struct sockaddr_storage caddr;
170	struct ip_check_defrag *skel;
171	struct nstoken *nstoken;
172	int client_tx_fd = -1;
173	int client_rx_fd = -1;
174	socklen_t caddr_len;
175	int srv_fd = -1;
176	char buf[1024];
177	int len, err;
178
179	skel = ip_check_defrag__open_and_load();
180	if (!ASSERT_OK_PTR(skel, "skel_open"))
181		return;
182
183	if (!ASSERT_OK(setup_topology(ipv6), "setup_topology"))
184		goto out;
185
186	if (!ASSERT_OK(attach(skel, ipv6), "attach"))
187		goto out;
188
189	/* Start server in ns1 */
190	nstoken = open_netns(NS1);
191	if (!ASSERT_OK_PTR(nstoken, "setns ns1"))
192		goto out;
193	srv_fd = start_server(ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, NULL, SERVER_PORT, 0);
194	close_netns(nstoken);
195	if (!ASSERT_GE(srv_fd, 0, "start_server"))
196		goto out;
197
198	/* Open tx raw socket in ns0 */
199	nstoken = open_netns(NS0);
200	if (!ASSERT_OK_PTR(nstoken, "setns ns0"))
201		goto out;
202	client_tx_fd = connect_to_fd_opts(srv_fd, &tx_ops);
203	close_netns(nstoken);
204	if (!ASSERT_GE(client_tx_fd, 0, "connect_to_fd_opts"))
205		goto out;
206
207	/* Open rx socket in ns0 */
208	nstoken = open_netns(NS0);
209	if (!ASSERT_OK_PTR(nstoken, "setns ns0"))
210		goto out;
211	client_rx_fd = connect_to_fd_opts(srv_fd, &rx_opts);
212	close_netns(nstoken);
213	if (!ASSERT_GE(client_rx_fd, 0, "connect_to_fd_opts"))
214		goto out;
215
216	/* Bind rx socket to a premeditated port */
217	memset(&caddr, 0, sizeof(caddr));
218	nstoken = open_netns(NS0);
219	if (!ASSERT_OK_PTR(nstoken, "setns ns0"))
220		goto out;
221	if (ipv6) {
222		struct sockaddr_in6 *c = (struct sockaddr_in6 *)&caddr;
223
224		c->sin6_family = AF_INET6;
225		inet_pton(AF_INET6, VETH0_ADDR6, &c->sin6_addr);
226		c->sin6_port = htons(CLIENT_PORT);
227		err = bind(client_rx_fd, (struct sockaddr *)c, sizeof(*c));
228	} else {
229		struct sockaddr_in *c = (struct sockaddr_in *)&caddr;
230
231		c->sin_family = AF_INET;
232		inet_pton(AF_INET, VETH0_ADDR, &c->sin_addr);
233		c->sin_port = htons(CLIENT_PORT);
234		err = bind(client_rx_fd, (struct sockaddr *)c, sizeof(*c));
235	}
236	close_netns(nstoken);
237	if (!ASSERT_OK(err, "bind"))
238		goto out;
239
240	/* Send message in fragments */
241	if (ipv6) {
242		if (!ASSERT_OK(send_frags6(client_tx_fd), "send_frags6"))
243			goto out;
244	} else {
245		if (!ASSERT_OK(send_frags(client_tx_fd), "send_frags"))
246			goto out;
247	}
248
249	if (!ASSERT_EQ(skel->bss->shootdowns, 0, "shootdowns"))
250		goto out;
251
252	/* Receive reassembled msg on server and echo back to client */
253	caddr_len = sizeof(caddr);
254	len = recvfrom(srv_fd, buf, sizeof(buf), 0, (struct sockaddr *)&caddr, &caddr_len);
255	if (!ASSERT_GE(len, 0, "server recvfrom"))
256		goto out;
257	len = sendto(srv_fd, buf, len, 0, (struct sockaddr *)&caddr, caddr_len);
258	if (!ASSERT_GE(len, 0, "server sendto"))
259		goto out;
260
261	/* Expect reassembed message to be echoed back */
262	len = recvfrom(client_rx_fd, buf, sizeof(buf), 0, NULL, NULL);
263	if (!ASSERT_EQ(len, sizeof(MAGIC_MESSAGE) - 1, "client short read"))
264		goto out;
265
266out:
267	if (client_rx_fd != -1)
268		close(client_rx_fd);
269	if (client_tx_fd != -1)
270		close(client_tx_fd);
271	if (srv_fd != -1)
272		close(srv_fd);
273	cleanup_topology();
274	ip_check_defrag__destroy(skel);
275}
276
277void test_bpf_ip_check_defrag(void)
278{
279	if (test__start_subtest("v4"))
280		test_bpf_ip_check_defrag_ok(false);
281	if (test__start_subtest("v6"))
282		test_bpf_ip_check_defrag_ok(true);
283}
1