Skip to content

Commit adfd272

Browse files
tohojoborkmann
authored andcommitted
bpf, selftests: Extend test_tc_redirect to use modified bpf_redirect_neigh()
This updates the test_tc_neigh prog in selftests to use the new syntax of bpf_redirect_neigh(). To exercise the helper both with and without the optional parameter, add an additional test_tc_neigh_fib test program, which does a bpf_fib_lookup() followed by a call to bpf_redirect_neigh() instead of looking up the ifindex in a map. Update the test_tc_redirect.sh script to run both versions of the test, and while we're add it, fix it to work on systems that have a consolidated dual-stack 'ping' binary instead of separate ping/ping6 versions. Signed-off-by: Toke Høiland-Jørgensen <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent ba452c9 commit adfd272

File tree

3 files changed

+173
-5
lines changed

3 files changed

+173
-5
lines changed

tools/testing/selftests/bpf/progs/test_tc_neigh.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// SPDX-License-Identifier: GPL-2.0
2+
#include <stddef.h>
23
#include <stdint.h>
34
#include <stdbool.h>
45

@@ -118,7 +119,7 @@ SEC("dst_ingress") int tc_dst(struct __sk_buff *skb)
118119
if (bpf_skb_store_bytes(skb, 0, &zero, sizeof(zero), 0) < 0)
119120
return TC_ACT_SHOT;
120121

121-
return bpf_redirect_neigh(get_dev_ifindex(dev_src), 0);
122+
return bpf_redirect_neigh(get_dev_ifindex(dev_src), NULL, 0, 0);
122123
}
123124

124125
SEC("src_ingress") int tc_src(struct __sk_buff *skb)
@@ -142,7 +143,7 @@ SEC("src_ingress") int tc_src(struct __sk_buff *skb)
142143
if (bpf_skb_store_bytes(skb, 0, &zero, sizeof(zero), 0) < 0)
143144
return TC_ACT_SHOT;
144145

145-
return bpf_redirect_neigh(get_dev_ifindex(dev_dst), 0);
146+
return bpf_redirect_neigh(get_dev_ifindex(dev_dst), NULL, 0, 0);
146147
}
147148

148149
char __license[] SEC("license") = "GPL";
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <stdint.h>
3+
#include <stdbool.h>
4+
#include <stddef.h>
5+
6+
#include <linux/bpf.h>
7+
#include <linux/stddef.h>
8+
#include <linux/pkt_cls.h>
9+
#include <linux/if_ether.h>
10+
#include <linux/in.h>
11+
#include <linux/ip.h>
12+
#include <linux/ipv6.h>
13+
14+
#include <bpf/bpf_helpers.h>
15+
#include <bpf/bpf_endian.h>
16+
17+
#ifndef ctx_ptr
18+
# define ctx_ptr(field) (void *)(long)(field)
19+
#endif
20+
21+
#define AF_INET 2
22+
#define AF_INET6 10
23+
24+
static __always_inline int fill_fib_params_v4(struct __sk_buff *skb,
25+
struct bpf_fib_lookup *fib_params)
26+
{
27+
void *data_end = ctx_ptr(skb->data_end);
28+
void *data = ctx_ptr(skb->data);
29+
struct iphdr *ip4h;
30+
31+
if (data + sizeof(struct ethhdr) > data_end)
32+
return -1;
33+
34+
ip4h = (struct iphdr *)(data + sizeof(struct ethhdr));
35+
if ((void *)(ip4h + 1) > data_end)
36+
return -1;
37+
38+
fib_params->family = AF_INET;
39+
fib_params->tos = ip4h->tos;
40+
fib_params->l4_protocol = ip4h->protocol;
41+
fib_params->sport = 0;
42+
fib_params->dport = 0;
43+
fib_params->tot_len = bpf_ntohs(ip4h->tot_len);
44+
fib_params->ipv4_src = ip4h->saddr;
45+
fib_params->ipv4_dst = ip4h->daddr;
46+
47+
return 0;
48+
}
49+
50+
static __always_inline int fill_fib_params_v6(struct __sk_buff *skb,
51+
struct bpf_fib_lookup *fib_params)
52+
{
53+
struct in6_addr *src = (struct in6_addr *)fib_params->ipv6_src;
54+
struct in6_addr *dst = (struct in6_addr *)fib_params->ipv6_dst;
55+
void *data_end = ctx_ptr(skb->data_end);
56+
void *data = ctx_ptr(skb->data);
57+
struct ipv6hdr *ip6h;
58+
59+
if (data + sizeof(struct ethhdr) > data_end)
60+
return -1;
61+
62+
ip6h = (struct ipv6hdr *)(data + sizeof(struct ethhdr));
63+
if ((void *)(ip6h + 1) > data_end)
64+
return -1;
65+
66+
fib_params->family = AF_INET6;
67+
fib_params->flowinfo = 0;
68+
fib_params->l4_protocol = ip6h->nexthdr;
69+
fib_params->sport = 0;
70+
fib_params->dport = 0;
71+
fib_params->tot_len = bpf_ntohs(ip6h->payload_len);
72+
*src = ip6h->saddr;
73+
*dst = ip6h->daddr;
74+
75+
return 0;
76+
}
77+
78+
SEC("chk_egress") int tc_chk(struct __sk_buff *skb)
79+
{
80+
void *data_end = ctx_ptr(skb->data_end);
81+
void *data = ctx_ptr(skb->data);
82+
__u32 *raw = data;
83+
84+
if (data + sizeof(struct ethhdr) > data_end)
85+
return TC_ACT_SHOT;
86+
87+
return !raw[0] && !raw[1] && !raw[2] ? TC_ACT_SHOT : TC_ACT_OK;
88+
}
89+
90+
static __always_inline int tc_redir(struct __sk_buff *skb)
91+
{
92+
struct bpf_fib_lookup fib_params = { .ifindex = skb->ingress_ifindex };
93+
__u8 zero[ETH_ALEN * 2];
94+
int ret = -1;
95+
96+
switch (skb->protocol) {
97+
case __bpf_constant_htons(ETH_P_IP):
98+
ret = fill_fib_params_v4(skb, &fib_params);
99+
break;
100+
case __bpf_constant_htons(ETH_P_IPV6):
101+
ret = fill_fib_params_v6(skb, &fib_params);
102+
break;
103+
}
104+
105+
if (ret)
106+
return TC_ACT_OK;
107+
108+
ret = bpf_fib_lookup(skb, &fib_params, sizeof(fib_params), 0);
109+
if (ret == BPF_FIB_LKUP_RET_NOT_FWDED || ret < 0)
110+
return TC_ACT_OK;
111+
112+
__builtin_memset(&zero, 0, sizeof(zero));
113+
if (bpf_skb_store_bytes(skb, 0, &zero, sizeof(zero), 0) < 0)
114+
return TC_ACT_SHOT;
115+
116+
if (ret == BPF_FIB_LKUP_RET_NO_NEIGH) {
117+
struct bpf_redir_neigh nh_params = {};
118+
119+
nh_params.nh_family = fib_params.family;
120+
__builtin_memcpy(&nh_params.ipv6_nh, &fib_params.ipv6_dst,
121+
sizeof(nh_params.ipv6_nh));
122+
123+
return bpf_redirect_neigh(fib_params.ifindex, &nh_params,
124+
sizeof(nh_params), 0);
125+
126+
} else if (ret == BPF_FIB_LKUP_RET_SUCCESS) {
127+
void *data_end = ctx_ptr(skb->data_end);
128+
struct ethhdr *eth = ctx_ptr(skb->data);
129+
130+
if (eth + 1 > data_end)
131+
return TC_ACT_SHOT;
132+
133+
__builtin_memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
134+
__builtin_memcpy(eth->h_source, fib_params.smac, ETH_ALEN);
135+
136+
return bpf_redirect(fib_params.ifindex, 0);
137+
}
138+
139+
return TC_ACT_SHOT;
140+
}
141+
142+
/* these are identical, but keep them separate for compatibility with the
143+
* section names expected by test_tc_redirect.sh
144+
*/
145+
SEC("dst_ingress") int tc_dst(struct __sk_buff *skb)
146+
{
147+
return tc_redir(skb);
148+
}
149+
150+
SEC("src_ingress") int tc_src(struct __sk_buff *skb)
151+
{
152+
return tc_redir(skb);
153+
}
154+
155+
char __license[] SEC("license") = "GPL";

tools/testing/selftests/bpf/test_tc_redirect.sh

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ command -v timeout >/dev/null 2>&1 || \
2424
{ echo >&2 "timeout is not available"; exit 1; }
2525
command -v ping >/dev/null 2>&1 || \
2626
{ echo >&2 "ping is not available"; exit 1; }
27-
command -v ping6 >/dev/null 2>&1 || \
28-
{ echo >&2 "ping6 is not available"; exit 1; }
27+
if command -v ping6 >/dev/null 2>&1; then PING6=ping6; else PING6=ping; fi
2928
command -v perl >/dev/null 2>&1 || \
3029
{ echo >&2 "perl is not available"; exit 1; }
3130
command -v jq >/dev/null 2>&1 || \
@@ -152,7 +151,7 @@ netns_test_connectivity()
152151
echo -e "${TEST}: ${GREEN}PASS${NC}"
153152

154153
TEST="ICMPv6 connectivity test"
155-
ip netns exec ${NS_SRC} ping6 $PING_ARG ${IP6_DST}
154+
ip netns exec ${NS_SRC} $PING6 $PING_ARG ${IP6_DST}
156155
if [ $? -ne 0 ]; then
157156
echo -e "${TEST}: ${RED}FAIL${NC}"
158157
exit 1
@@ -170,6 +169,7 @@ hex_mem_str()
170169
netns_setup_bpf()
171170
{
172171
local obj=$1
172+
local use_forwarding=${2:-0}
173173

174174
ip netns exec ${NS_FWD} tc qdisc add dev veth_src_fwd clsact
175175
ip netns exec ${NS_FWD} tc filter add dev veth_src_fwd ingress bpf da obj $obj sec src_ingress
@@ -179,6 +179,14 @@ netns_setup_bpf()
179179
ip netns exec ${NS_FWD} tc filter add dev veth_dst_fwd ingress bpf da obj $obj sec dst_ingress
180180
ip netns exec ${NS_FWD} tc filter add dev veth_dst_fwd egress bpf da obj $obj sec chk_egress
181181

182+
if [ "$use_forwarding" -eq "1" ]; then
183+
# bpf_fib_lookup() checks if forwarding is enabled
184+
ip netns exec ${NS_FWD} sysctl -w net.ipv4.ip_forward=1
185+
ip netns exec ${NS_FWD} sysctl -w net.ipv6.conf.veth_dst_fwd.forwarding=1
186+
ip netns exec ${NS_FWD} sysctl -w net.ipv6.conf.veth_src_fwd.forwarding=1
187+
return 0
188+
fi
189+
182190
veth_src=$(ip netns exec ${NS_FWD} cat /sys/class/net/veth_src_fwd/ifindex)
183191
veth_dst=$(ip netns exec ${NS_FWD} cat /sys/class/net/veth_dst_fwd/ifindex)
184192

@@ -200,5 +208,9 @@ netns_setup_bpf test_tc_neigh.o
200208
netns_test_connectivity
201209
netns_cleanup
202210
netns_setup
211+
netns_setup_bpf test_tc_neigh_fib.o 1
212+
netns_test_connectivity
213+
netns_cleanup
214+
netns_setup
203215
netns_setup_bpf test_tc_peer.o
204216
netns_test_connectivity

0 commit comments

Comments
 (0)