-
Notifications
You must be signed in to change notification settings - Fork 128
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests/bpf: Test bpf_skb_change_tail() in TC ingress
Similarly to the previous test, we also need a test case to cover positive offsets as well, TC is an excellent hook for this. Cc: John Fastabend <[email protected]> Cc: Daniel Borkmann <[email protected]> Tested-by: Zijian Zhang <[email protected]> Signed-off-by: Cong Wang <[email protected]>
- Loading branch information
Cong Wang
authored and
Kernel Patches Daemon
committed
Dec 19, 2024
1 parent
adc3bc9
commit d5499fd
Showing
2 changed files
with
168 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
#include <error.h> | ||
#include <test_progs.h> | ||
#include <linux/pkt_cls.h> | ||
|
||
#include "test_tc_change_tail.skel.h" | ||
#include "socket_helpers.h" | ||
|
||
#define LO_IFINDEX 1 | ||
|
||
void test_tc_change_tail(void) | ||
{ | ||
LIBBPF_OPTS(bpf_tcx_opts, tcx_opts); | ||
struct test_tc_change_tail *skel = NULL; | ||
struct bpf_link *link; | ||
int c1, p1; | ||
char buf[2]; | ||
int ret; | ||
|
||
skel = test_tc_change_tail__open_and_load(); | ||
if (!ASSERT_OK_PTR(skel, "test_tc_change_tail__open_and_load")) | ||
return; | ||
|
||
link = bpf_program__attach_tcx(skel->progs.change_tail, LO_IFINDEX, | ||
&tcx_opts); | ||
if (!ASSERT_OK_PTR(link, "bpf_program__attach_tcx")) | ||
goto destroy; | ||
|
||
skel->links.change_tail = link; | ||
ret = create_pair(AF_INET, SOCK_DGRAM, &c1, &p1); | ||
if (!ASSERT_OK(ret, "create_pair")) | ||
goto destroy; | ||
|
||
ret = xsend(p1, "Tr", 2, 0); | ||
ASSERT_EQ(ret, 2, "xsend(p1)"); | ||
ret = recv(c1, buf, 2, 0); | ||
ASSERT_EQ(ret, 2, "recv(c1)"); | ||
ASSERT_EQ(skel->data->change_tail_ret, 0, "change_tail_ret"); | ||
|
||
ret = xsend(p1, "G", 1, 0); | ||
ASSERT_EQ(ret, 1, "xsend(p1)"); | ||
ret = recv(c1, buf, 2, 0); | ||
ASSERT_EQ(ret, 1, "recv(c1)"); | ||
ASSERT_EQ(skel->data->change_tail_ret, 0, "change_tail_ret"); | ||
|
||
ret = xsend(p1, "E", 1, 0); | ||
ASSERT_EQ(ret, 1, "xsend(p1)"); | ||
ret = recv(c1, buf, 1, 0); | ||
ASSERT_EQ(ret, 1, "recv(c1)"); | ||
ASSERT_EQ(skel->data->change_tail_ret, -EINVAL, "change_tail_ret"); | ||
|
||
ret = xsend(p1, "Z", 1, 0); | ||
ASSERT_EQ(ret, 1, "xsend(p1)"); | ||
ret = recv(c1, buf, 1, 0); | ||
ASSERT_EQ(ret, 1, "recv(c1)"); | ||
ASSERT_EQ(skel->data->change_tail_ret, -EINVAL, "change_tail_ret"); | ||
|
||
close(c1); | ||
close(p1); | ||
destroy: | ||
test_tc_change_tail__destroy(skel); | ||
} |
106 changes: 106 additions & 0 deletions
106
tools/testing/selftests/bpf/progs/test_tc_change_tail.c
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,106 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
#include <linux/bpf.h> | ||
#include <bpf/bpf_helpers.h> | ||
#include <linux/if_ether.h> | ||
#include <linux/in.h> | ||
#include <linux/ip.h> | ||
#include <linux/udp.h> | ||
#include <linux/pkt_cls.h> | ||
|
||
long change_tail_ret = 1; | ||
|
||
static __always_inline struct iphdr *parse_ip_header(struct __sk_buff *skb, int *ip_proto) | ||
{ | ||
void *data_end = (void *)(long)skb->data_end; | ||
void *data = (void *)(long)skb->data; | ||
struct ethhdr *eth = data; | ||
struct iphdr *iph; | ||
|
||
/* Verify Ethernet header */ | ||
if ((void *)(data + sizeof(*eth)) > data_end) | ||
return NULL; | ||
|
||
/* Skip Ethernet header to get to IP header */ | ||
iph = (void *)(data + sizeof(struct ethhdr)); | ||
|
||
/* Verify IP header */ | ||
if ((void *)(data + sizeof(struct ethhdr) + sizeof(*iph)) > data_end) | ||
return NULL; | ||
|
||
/* Basic IP header validation */ | ||
if (iph->version != 4) /* Only support IPv4 */ | ||
return NULL; | ||
|
||
if (iph->ihl < 5) /* Minimum IP header length */ | ||
return NULL; | ||
|
||
*ip_proto = iph->protocol; | ||
return iph; | ||
} | ||
|
||
static __always_inline struct udphdr *parse_udp_header(struct __sk_buff *skb, struct iphdr *iph) | ||
{ | ||
void *data_end = (void *)(long)skb->data_end; | ||
void *hdr = (void *)iph; | ||
struct udphdr *udp; | ||
|
||
/* Calculate UDP header position */ | ||
udp = hdr + (iph->ihl * 4); | ||
hdr = (void *)udp; | ||
|
||
/* Verify UDP header bounds */ | ||
if ((void *)(hdr + sizeof(*udp)) > data_end) | ||
return NULL; | ||
|
||
return udp; | ||
} | ||
|
||
SEC("tc/ingress") | ||
int change_tail(struct __sk_buff *skb) | ||
{ | ||
int len = skb->len; | ||
struct udphdr *udp; | ||
struct iphdr *iph; | ||
void *data_end; | ||
char *payload; | ||
int ip_proto; | ||
|
||
bpf_skb_pull_data(skb, len); | ||
|
||
data_end = (void *)(long)skb->data_end; | ||
iph = parse_ip_header(skb, &ip_proto); | ||
if (!iph) | ||
return TCX_PASS; | ||
|
||
if (ip_proto != IPPROTO_UDP) | ||
return TCX_PASS; | ||
|
||
udp = parse_udp_header(skb, iph); | ||
if (!udp) | ||
return TCX_PASS; | ||
|
||
payload = (char *)udp + (sizeof(struct udphdr)); | ||
if (payload + 1 > (char *)data_end) | ||
return TCX_PASS; | ||
|
||
if (payload[0] == 'T') { /* Trim the packet */ | ||
change_tail_ret = bpf_skb_change_tail(skb, len - 1, 0); | ||
if (!change_tail_ret) | ||
bpf_skb_change_tail(skb, len, 0); | ||
return TCX_PASS; | ||
} else if (payload[0] == 'G') { /* Grow the packet */ | ||
change_tail_ret = bpf_skb_change_tail(skb, len + 1, 0); | ||
if (!change_tail_ret) | ||
bpf_skb_change_tail(skb, len, 0); | ||
return TCX_PASS; | ||
} else if (payload[0] == 'E') { /* Error */ | ||
change_tail_ret = bpf_skb_change_tail(skb, 65535, 0); | ||
return TCX_PASS; | ||
} else if (payload[0] == 'Z') { /* Zero */ | ||
change_tail_ret = bpf_skb_change_tail(skb, 0, 0); | ||
return TCX_PASS; | ||
} | ||
return TCX_DROP; | ||
} | ||
|
||
char _license[] SEC("license") = "GPL"; |