summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests/empty_skb.c
blob: 261228eb68e810ce096407c1d80dde41ca398c37 (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
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include <network_helpers.h>
#include <net/if.h>
#include "empty_skb.skel.h"

void test_empty_skb(void)
{
	LIBBPF_OPTS(bpf_test_run_opts, tattr);
	struct empty_skb *bpf_obj = NULL;
	struct nstoken *tok = NULL;
	struct bpf_program *prog;
	char eth_hlen_pp[15];
	char eth_hlen[14];
	int veth_ifindex;
	int ipip_ifindex;
	int err;
	int i;

	struct {
		const char *msg;
		const void *data_in;
		__u32 data_size_in;
		int *ifindex;
		int err;
		int ret;
		int lwt_egress_ret; /* expected retval at lwt/egress */
		bool success_on_tc;
	} tests[] = {
		/* Empty packets are always rejected. */

		{
			/* BPF_PROG_RUN ETH_HLEN size check */
			.msg = "veth empty ingress packet",
			.data_in = NULL,
			.data_size_in = 0,
			.ifindex = &veth_ifindex,
			.err = -EINVAL,
		},
		{
			/* BPF_PROG_RUN ETH_HLEN size check */
			.msg = "ipip empty ingress packet",
			.data_in = NULL,
			.data_size_in = 0,
			.ifindex = &ipip_ifindex,
			.err = -EINVAL,
		},

		/* ETH_HLEN-sized packets:
		 * - can not be redirected at LWT_XMIT
		 * - can be redirected at TC to non-tunneling dest
		 */

		{
			/* __bpf_redirect_common */
			.msg = "veth ETH_HLEN packet ingress",
			.data_in = eth_hlen,
			.data_size_in = sizeof(eth_hlen),
			.ifindex = &veth_ifindex,
			.ret = -ERANGE,
			.lwt_egress_ret = -ERANGE,
			.success_on_tc = true,
		},
		{
			/* __bpf_redirect_no_mac
			 *
			 * lwt: skb->len=0 <= skb_network_offset=0
			 * tc: skb->len=14 <= skb_network_offset=14
			 */
			.msg = "ipip ETH_HLEN packet ingress",
			.data_in = eth_hlen,
			.data_size_in = sizeof(eth_hlen),
			.ifindex = &ipip_ifindex,
			.ret = -ERANGE,
			.lwt_egress_ret = -ERANGE,
		},

		/* ETH_HLEN+1-sized packet should be redirected. */

		{
			.msg = "veth ETH_HLEN+1 packet ingress",
			.data_in = eth_hlen_pp,
			.data_size_in = sizeof(eth_hlen_pp),
			.ifindex = &veth_ifindex,
			.lwt_egress_ret = 1, /* veth_xmit NET_XMIT_DROP */
		},
		{
			.msg = "ipip ETH_HLEN+1 packet ingress",
			.data_in = eth_hlen_pp,
			.data_size_in = sizeof(eth_hlen_pp),
			.ifindex = &ipip_ifindex,
		},
	};

	SYS(out, "ip netns add empty_skb");
	tok = open_netns("empty_skb");
	SYS(out, "ip link add veth0 type veth peer veth1");
	SYS(out, "ip link set dev veth0 up");
	SYS(out, "ip link set dev veth1 up");
	SYS(out, "ip addr add 10.0.0.1/8 dev veth0");
	SYS(out, "ip addr add 10.0.0.2/8 dev veth1");
	veth_ifindex = if_nametoindex("veth0");

	SYS(out, "ip link add ipip0 type ipip local 10.0.0.1 remote 10.0.0.2");
	SYS(out, "ip link set ipip0 up");
	SYS(out, "ip addr add 192.168.1.1/16 dev ipip0");
	ipip_ifindex = if_nametoindex("ipip0");

	bpf_obj = empty_skb__open_and_load();
	if (!ASSERT_OK_PTR(bpf_obj, "open skeleton"))
		goto out;

	for (i = 0; i < ARRAY_SIZE(tests); i++) {
		bpf_object__for_each_program(prog, bpf_obj->obj) {
			bool at_egress = strstr(bpf_program__name(prog), "egress") != NULL;
			bool at_tc = !strncmp(bpf_program__section_name(prog), "tc", 2);
			int expected_ret;
			char buf[128];

			expected_ret = at_egress && !at_tc ? tests[i].lwt_egress_ret : tests[i].ret;

			tattr.data_in = tests[i].data_in;
			tattr.data_size_in = tests[i].data_size_in;

			tattr.data_size_out = 0;
			bpf_obj->bss->ifindex = *tests[i].ifindex;
			bpf_obj->bss->ret = 0;
			err = bpf_prog_test_run_opts(bpf_program__fd(prog), &tattr);
			sprintf(buf, "err: %s [%s]", tests[i].msg, bpf_program__name(prog));

			if (at_tc && tests[i].success_on_tc)
				ASSERT_GE(err, 0, buf);
			else
				ASSERT_EQ(err, tests[i].err, buf);
			sprintf(buf, "ret: %s [%s]", tests[i].msg, bpf_program__name(prog));
			if (at_tc && tests[i].success_on_tc)
				ASSERT_GE(bpf_obj->bss->ret, 0, buf);
			else
				ASSERT_EQ(bpf_obj->bss->ret, expected_ret, buf);
		}
	}

out:
	if (bpf_obj)
		empty_skb__destroy(bpf_obj);
	if (tok)
		close_netns(tok);
	SYS_NOFAIL("ip netns del empty_skb");
}