summaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/bpf/prog_tests/vrf_socket_lookup.c
blob: 2a5e207edad61e1be7dd4375db9397ee47b1c5f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause

/*
 * Topology:
 * ---------
 *     NS0 namespace         |   NS1 namespace
 *			     |
 *     +--------------+      |   +--------------+
 *     |    veth01    |----------|    veth10    |
 *     | 172.16.1.100 |      |   | 172.16.1.200 |
 *     |     bpf      |      |   +--------------+
 *     +--------------+      |
 *      server(UDP/TCP)      |
 *  +-------------------+    |
 *  |        vrf1       |    |
 *  |  +--------------+ |    |   +--------------+
 *  |  |    veth02    |----------|    veth20    |
 *  |  | 172.16.2.100 | |    |   | 172.16.2.200 |
 *  |  |     bpf      | |    |   +--------------+
 *  |  +--------------+ |    |
 *  |   server(UDP/TCP) |    |
 *  +-------------------+    |
 *
 * Test flow
 * -----------
 *  The tests verifies that socket lookup via TC is VRF aware:
 *  1) Creates two veth pairs between NS0 and NS1:
 *     a) veth01 <-> veth10 outside the VRF
 *     b) veth02 <-> veth20 in the VRF
 *  2) Attaches to veth01 and veth02 a program that calls:
 *     a) bpf_skc_lookup_tcp() with TCP and tcp_skc is true
 *     b) bpf_sk_lookup_tcp() with TCP and tcp_skc is false
 *     c) bpf_sk_lookup_udp() with UDP
 *     The program stores the lookup result in bss->lookup_status.
 *  3) Creates a socket TCP/UDP server in/outside the VRF.
 *  4) The test expects lookup_status to be:
 *     a) 0 from device in VRF to server outside VRF
 *     b) 0 from device outside VRF to server in VRF
 *     c) 1 from device in VRF to server in VRF
 *     d) 1 from device outside VRF to server outside VRF
 */

#include <net/if.h>

#include "test_progs.h"
#include "network_helpers.h"
#include "vrf_socket_lookup.skel.h"

#define NS0 "vrf_socket_lookup_0"
#define NS1 "vrf_socket_lookup_1"

#define IP4_ADDR_VETH01 "172.16.1.100"
#define IP4_ADDR_VETH10 "172.16.1.200"
#define IP4_ADDR_VETH02 "172.16.2.100"
#define IP4_ADDR_VETH20 "172.16.2.200"

#define NON_VRF_PORT 5000
#define IN_VRF_PORT 5001

#define TIMEOUT_MS 3000

static int make_socket(int sotype, const char *ip, int port,
		       struct sockaddr_storage *addr)
{
	int err, fd;

	err = make_sockaddr(AF_INET, ip, port, addr, NULL);
	if (!ASSERT_OK(err, "make_address"))
		return -1;

	fd = socket(AF_INET, sotype, 0);
	if (!ASSERT_GE(fd, 0, "socket"))
		return -1;

	if (!ASSERT_OK(settimeo(fd, TIMEOUT_MS), "settimeo"))
		goto fail;

	return fd;
fail:
	close(fd);
	return -1;
}

static int make_server(int sotype, const char *ip, int port, const char *ifname)
{
	int err, fd = -1;

	fd = start_server(AF_INET, sotype, ip, port, TIMEOUT_MS);
	if (!ASSERT_GE(fd, 0, "start_server"))
		return -1;

	if (ifname) {
		err = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
				 ifname, strlen(ifname) + 1);
		if (!ASSERT_OK(err, "setsockopt(SO_BINDTODEVICE)"))
			goto fail;
	}

	return fd;
fail:
	close(fd);
	return -1;
}

static int attach_progs(char *ifname, int tc_prog_fd, int xdp_prog_fd)
{
	LIBBPF_OPTS(bpf_tc_hook, hook, .attach_point = BPF_TC_INGRESS);
	LIBBPF_OPTS(bpf_tc_opts, opts, .handle = 1, .priority = 1,
		    .prog_fd = tc_prog_fd);
	int ret, ifindex;

	ifindex = if_nametoindex(ifname);
	if (!ASSERT_NEQ(ifindex, 0, "if_nametoindex"))
		return -1;
	hook.ifindex = ifindex;

	ret = bpf_tc_hook_create(&hook);
	if (!ASSERT_OK(ret, "bpf_tc_hook_create"))
		return ret;

	ret = bpf_tc_attach(&hook, &opts);
	if (!ASSERT_OK(ret, "bpf_tc_attach")) {
		bpf_tc_hook_destroy(&hook);
		return ret;
	}
	ret = bpf_xdp_attach(ifindex, xdp_prog_fd, 0, NULL);
	if (!ASSERT_OK(ret, "bpf_xdp_attach")) {
		bpf_tc_hook_destroy(&hook);
		return ret;
	}

	return 0;
}

static void cleanup(void)
{
	SYS_NOFAIL("test -f /var/run/netns/" NS0 " && ip netns delete "
		   NS0);
	SYS_NOFAIL("test -f /var/run/netns/" NS1 " && ip netns delete "
		   NS1);
}

static int setup(struct vrf_socket_lookup *skel)
{
	int tc_prog_fd, xdp_prog_fd, ret = 0;
	struct nstoken *nstoken = NULL;

	SYS(fail, "ip netns add " NS0);
	SYS(fail, "ip netns add " NS1);

	/* NS0 <-> NS1 [veth01 <-> veth10] */
	SYS(fail, "ip link add veth01 netns " NS0 " type veth peer name veth10"
	    " netns " NS1);
	SYS(fail, "ip -net " NS0 " addr add " IP4_ADDR_VETH01 "/24 dev veth01");
	SYS(fail, "ip -net " NS0 " link set dev veth01 up");
	SYS(fail, "ip -net " NS1 " addr add " IP4_ADDR_VETH10 "/24 dev veth10");
	SYS(fail, "ip -net " NS1 " link set dev veth10 up");

	/* NS0 <-> NS1 [veth02 <-> veth20] */
	SYS(fail, "ip link add veth02 netns " NS0 " type veth peer name veth20"
	    " netns " NS1);
	SYS(fail, "ip -net " NS0 " addr add " IP4_ADDR_VETH02 "/24 dev veth02");
	SYS(fail, "ip -net " NS0 " link set dev veth02 up");
	SYS(fail, "ip -net " NS1 " addr add " IP4_ADDR_VETH20 "/24 dev veth20");
	SYS(fail, "ip -net " NS1 " link set dev veth20 up");

	/* veth02 -> vrf1  */
	SYS(fail, "ip -net " NS0 " link add vrf1 type vrf table 11");
	SYS(fail, "ip -net " NS0 " route add vrf vrf1 unreachable default"
	    " metric 4278198272");
	SYS(fail, "ip -net " NS0 " link set vrf1 alias vrf");
	SYS(fail, "ip -net " NS0 " link set vrf1 up");
	SYS(fail, "ip -net " NS0 " link set veth02 master vrf1");

	/* Attach TC and XDP progs to veth devices in NS0 */
	nstoken = open_netns(NS0);
	if (!ASSERT_OK_PTR(nstoken, "setns " NS0))
		goto fail;
	tc_prog_fd = bpf_program__fd(skel->progs.tc_socket_lookup);
	if (!ASSERT_GE(tc_prog_fd, 0, "bpf_program__tc_fd"))
		goto fail;
	xdp_prog_fd = bpf_program__fd(skel->progs.xdp_socket_lookup);
	if (!ASSERT_GE(xdp_prog_fd, 0, "bpf_program__xdp_fd"))
		goto fail;

	if (attach_progs("veth01", tc_prog_fd, xdp_prog_fd))
		goto fail;

	if (attach_progs("veth02", tc_prog_fd, xdp_prog_fd))
		goto fail;

	goto close;
fail:
	ret = -1;
close:
	if (nstoken)
		close_netns(nstoken);
	return ret;
}

static int test_lookup(struct vrf_socket_lookup *skel, int sotype,
		       const char *ip, int port, bool test_xdp, bool tcp_skc,
		       int lookup_status_exp)
{
	static const char msg[] = "Hello Server";
	struct sockaddr_storage addr = {};
	int fd, ret = 0;

	fd = make_socket(sotype, ip, port, &addr);
	if (fd < 0)
		return -1;

	skel->bss->test_xdp = test_xdp;
	skel->bss->tcp_skc = tcp_skc;
	skel->bss->lookup_status = -1;

	if (sotype == SOCK_STREAM)
		connect(fd, (void *)&addr, sizeof(struct sockaddr_in));
	else
		sendto(fd, msg, sizeof(msg), 0, (void *)&addr,
		       sizeof(struct sockaddr_in));

	if (!ASSERT_EQ(skel->bss->lookup_status, lookup_status_exp,
		       "lookup_status"))
		goto fail;

	goto close;

fail:
	ret = -1;
close:
	close(fd);
	return ret;
}

static void _test_vrf_socket_lookup(struct vrf_socket_lookup *skel, int sotype,
				    bool test_xdp, bool tcp_skc)
{
	int in_vrf_server = -1, non_vrf_server = -1;
	struct nstoken *nstoken = NULL;

	nstoken = open_netns(NS0);
	if (!ASSERT_OK_PTR(nstoken, "setns " NS0))
		goto done;

	/* Open sockets in and outside VRF */
	non_vrf_server = make_server(sotype, "0.0.0.0", NON_VRF_PORT, NULL);
	if (!ASSERT_GE(non_vrf_server, 0, "make_server__outside_vrf_fd"))
		goto done;

	in_vrf_server = make_server(sotype, "0.0.0.0", IN_VRF_PORT, "veth02");
	if (!ASSERT_GE(in_vrf_server, 0, "make_server__in_vrf_fd"))
		goto done;

	/* Perform test from NS1 */
	close_netns(nstoken);
	nstoken = open_netns(NS1);
	if (!ASSERT_OK_PTR(nstoken, "setns " NS1))
		goto done;

	if (!ASSERT_OK(test_lookup(skel, sotype, IP4_ADDR_VETH02, NON_VRF_PORT,
				   test_xdp, tcp_skc, 0), "in_to_out"))
		goto done;
	if (!ASSERT_OK(test_lookup(skel, sotype, IP4_ADDR_VETH02, IN_VRF_PORT,
				   test_xdp, tcp_skc, 1), "in_to_in"))
		goto done;
	if (!ASSERT_OK(test_lookup(skel, sotype, IP4_ADDR_VETH01, NON_VRF_PORT,
				   test_xdp, tcp_skc, 1), "out_to_out"))
		goto done;
	if (!ASSERT_OK(test_lookup(skel, sotype, IP4_ADDR_VETH01, IN_VRF_PORT,
				   test_xdp, tcp_skc, 0), "out_to_in"))
		goto done;

done:
	if (non_vrf_server >= 0)
		close(non_vrf_server);
	if (in_vrf_server >= 0)
		close(in_vrf_server);
	if (nstoken)
		close_netns(nstoken);
}

void test_vrf_socket_lookup(void)
{
	struct vrf_socket_lookup *skel;

	cleanup();

	skel = vrf_socket_lookup__open_and_load();
	if (!ASSERT_OK_PTR(skel, "vrf_socket_lookup__open_and_load"))
		return;

	if (!ASSERT_OK(setup(skel), "setup"))
		goto done;

	if (test__start_subtest("tc_socket_lookup_tcp"))
		_test_vrf_socket_lookup(skel, SOCK_STREAM, false, false);
	if (test__start_subtest("tc_socket_lookup_tcp_skc"))
		_test_vrf_socket_lookup(skel, SOCK_STREAM, false, false);
	if (test__start_subtest("tc_socket_lookup_udp"))
		_test_vrf_socket_lookup(skel, SOCK_STREAM, false, false);
	if (test__start_subtest("xdp_socket_lookup_tcp"))
		_test_vrf_socket_lookup(skel, SOCK_STREAM, true, false);
	if (test__start_subtest("xdp_socket_lookup_tcp_skc"))
		_test_vrf_socket_lookup(skel, SOCK_STREAM, true, false);
	if (test__start_subtest("xdp_socket_lookup_udp"))
		_test_vrf_socket_lookup(skel, SOCK_STREAM, true, false);

done:
	vrf_socket_lookup__destroy(skel);
	cleanup();
}