-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <toke@redhat.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Link: https://lore.kernel.org/bpf/160322915724.32199.17530068594636950447.stgit@toke.dk
- Loading branch information
Showing
3 changed files
with
173 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
#include <stdint.h> | ||
#include <stdbool.h> | ||
#include <stddef.h> | ||
|
||
#include <linux/bpf.h> | ||
#include <linux/stddef.h> | ||
#include <linux/pkt_cls.h> | ||
#include <linux/if_ether.h> | ||
#include <linux/in.h> | ||
#include <linux/ip.h> | ||
#include <linux/ipv6.h> | ||
|
||
#include <bpf/bpf_helpers.h> | ||
#include <bpf/bpf_endian.h> | ||
|
||
#ifndef ctx_ptr | ||
# define ctx_ptr(field) (void *)(long)(field) | ||
#endif | ||
|
||
#define AF_INET 2 | ||
#define AF_INET6 10 | ||
|
||
static __always_inline int fill_fib_params_v4(struct __sk_buff *skb, | ||
struct bpf_fib_lookup *fib_params) | ||
{ | ||
void *data_end = ctx_ptr(skb->data_end); | ||
void *data = ctx_ptr(skb->data); | ||
struct iphdr *ip4h; | ||
|
||
if (data + sizeof(struct ethhdr) > data_end) | ||
return -1; | ||
|
||
ip4h = (struct iphdr *)(data + sizeof(struct ethhdr)); | ||
if ((void *)(ip4h + 1) > data_end) | ||
return -1; | ||
|
||
fib_params->family = AF_INET; | ||
fib_params->tos = ip4h->tos; | ||
fib_params->l4_protocol = ip4h->protocol; | ||
fib_params->sport = 0; | ||
fib_params->dport = 0; | ||
fib_params->tot_len = bpf_ntohs(ip4h->tot_len); | ||
fib_params->ipv4_src = ip4h->saddr; | ||
fib_params->ipv4_dst = ip4h->daddr; | ||
|
||
return 0; | ||
} | ||
|
||
static __always_inline int fill_fib_params_v6(struct __sk_buff *skb, | ||
struct bpf_fib_lookup *fib_params) | ||
{ | ||
struct in6_addr *src = (struct in6_addr *)fib_params->ipv6_src; | ||
struct in6_addr *dst = (struct in6_addr *)fib_params->ipv6_dst; | ||
void *data_end = ctx_ptr(skb->data_end); | ||
void *data = ctx_ptr(skb->data); | ||
struct ipv6hdr *ip6h; | ||
|
||
if (data + sizeof(struct ethhdr) > data_end) | ||
return -1; | ||
|
||
ip6h = (struct ipv6hdr *)(data + sizeof(struct ethhdr)); | ||
if ((void *)(ip6h + 1) > data_end) | ||
return -1; | ||
|
||
fib_params->family = AF_INET6; | ||
fib_params->flowinfo = 0; | ||
fib_params->l4_protocol = ip6h->nexthdr; | ||
fib_params->sport = 0; | ||
fib_params->dport = 0; | ||
fib_params->tot_len = bpf_ntohs(ip6h->payload_len); | ||
*src = ip6h->saddr; | ||
*dst = ip6h->daddr; | ||
|
||
return 0; | ||
} | ||
|
||
SEC("chk_egress") int tc_chk(struct __sk_buff *skb) | ||
{ | ||
void *data_end = ctx_ptr(skb->data_end); | ||
void *data = ctx_ptr(skb->data); | ||
__u32 *raw = data; | ||
|
||
if (data + sizeof(struct ethhdr) > data_end) | ||
return TC_ACT_SHOT; | ||
|
||
return !raw[0] && !raw[1] && !raw[2] ? TC_ACT_SHOT : TC_ACT_OK; | ||
} | ||
|
||
static __always_inline int tc_redir(struct __sk_buff *skb) | ||
{ | ||
struct bpf_fib_lookup fib_params = { .ifindex = skb->ingress_ifindex }; | ||
__u8 zero[ETH_ALEN * 2]; | ||
int ret = -1; | ||
|
||
switch (skb->protocol) { | ||
case __bpf_constant_htons(ETH_P_IP): | ||
ret = fill_fib_params_v4(skb, &fib_params); | ||
break; | ||
case __bpf_constant_htons(ETH_P_IPV6): | ||
ret = fill_fib_params_v6(skb, &fib_params); | ||
break; | ||
} | ||
|
||
if (ret) | ||
return TC_ACT_OK; | ||
|
||
ret = bpf_fib_lookup(skb, &fib_params, sizeof(fib_params), 0); | ||
if (ret == BPF_FIB_LKUP_RET_NOT_FWDED || ret < 0) | ||
return TC_ACT_OK; | ||
|
||
__builtin_memset(&zero, 0, sizeof(zero)); | ||
if (bpf_skb_store_bytes(skb, 0, &zero, sizeof(zero), 0) < 0) | ||
return TC_ACT_SHOT; | ||
|
||
if (ret == BPF_FIB_LKUP_RET_NO_NEIGH) { | ||
struct bpf_redir_neigh nh_params = {}; | ||
|
||
nh_params.nh_family = fib_params.family; | ||
__builtin_memcpy(&nh_params.ipv6_nh, &fib_params.ipv6_dst, | ||
sizeof(nh_params.ipv6_nh)); | ||
|
||
return bpf_redirect_neigh(fib_params.ifindex, &nh_params, | ||
sizeof(nh_params), 0); | ||
|
||
} else if (ret == BPF_FIB_LKUP_RET_SUCCESS) { | ||
void *data_end = ctx_ptr(skb->data_end); | ||
struct ethhdr *eth = ctx_ptr(skb->data); | ||
|
||
if (eth + 1 > data_end) | ||
return TC_ACT_SHOT; | ||
|
||
__builtin_memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN); | ||
__builtin_memcpy(eth->h_source, fib_params.smac, ETH_ALEN); | ||
|
||
return bpf_redirect(fib_params.ifindex, 0); | ||
} | ||
|
||
return TC_ACT_SHOT; | ||
} | ||
|
||
/* these are identical, but keep them separate for compatibility with the | ||
* section names expected by test_tc_redirect.sh | ||
*/ | ||
SEC("dst_ingress") int tc_dst(struct __sk_buff *skb) | ||
{ | ||
return tc_redir(skb); | ||
} | ||
|
||
SEC("src_ingress") int tc_src(struct __sk_buff *skb) | ||
{ | ||
return tc_redir(skb); | ||
} | ||
|
||
char __license[] SEC("license") = "GPL"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters