selftests: drv-net: Pull data before parsing headers

It is possible for drivers to generate xdp packets with data residing
entirely in fragments. To keep parsing headers using direct packet
access, call bpf_xdp_pull_data() to pull headers into the linear data
area.

Signed-off-by: Amery Hung <ameryhung@gmail.com>
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Link: https://patch.msgid.link/20250922233356.3356453-9-ameryhung@gmail.com
pull/1354/merge
Amery Hung 2025-09-22 16:33:56 -07:00 committed by Martin KaFai Lau
parent 323302f54d
commit efec2e55bd
1 changed files with 74 additions and 15 deletions

View File

@ -14,6 +14,8 @@
#define MAX_PAYLOAD_LEN 5000 #define MAX_PAYLOAD_LEN 5000
#define MAX_HDR_LEN 64 #define MAX_HDR_LEN 64
extern int bpf_xdp_pull_data(struct xdp_md *xdp, __u32 len) __ksym __weak;
enum { enum {
XDP_MODE = 0, XDP_MODE = 0,
XDP_PORT = 1, XDP_PORT = 1,
@ -68,30 +70,57 @@ static void record_stats(struct xdp_md *ctx, __u32 stat_type)
static struct udphdr *filter_udphdr(struct xdp_md *ctx, __u16 port) static struct udphdr *filter_udphdr(struct xdp_md *ctx, __u16 port)
{ {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct udphdr *udph = NULL; struct udphdr *udph = NULL;
struct ethhdr *eth = data; void *data, *data_end;
struct ethhdr *eth;
int err;
err = bpf_xdp_pull_data(ctx, sizeof(*eth));
if (err)
return NULL;
data_end = (void *)(long)ctx->data_end;
data = eth = (void *)(long)ctx->data;
if (data + sizeof(*eth) > data_end) if (data + sizeof(*eth) > data_end)
return NULL; return NULL;
if (eth->h_proto == bpf_htons(ETH_P_IP)) { if (eth->h_proto == bpf_htons(ETH_P_IP)) {
struct iphdr *iph = data + sizeof(*eth); struct iphdr *iph;
err = bpf_xdp_pull_data(ctx, sizeof(*eth) + sizeof(*iph) +
sizeof(*udph));
if (err)
return NULL;
data_end = (void *)(long)ctx->data_end;
data = (void *)(long)ctx->data;
iph = data + sizeof(*eth);
if (iph + 1 > (struct iphdr *)data_end || if (iph + 1 > (struct iphdr *)data_end ||
iph->protocol != IPPROTO_UDP) iph->protocol != IPPROTO_UDP)
return NULL; return NULL;
udph = (void *)eth + sizeof(*iph) + sizeof(*eth); udph = data + sizeof(*iph) + sizeof(*eth);
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
struct ipv6hdr *ipv6h = data + sizeof(*eth); struct ipv6hdr *ipv6h;
err = bpf_xdp_pull_data(ctx, sizeof(*eth) + sizeof(*ipv6h) +
sizeof(*udph));
if (err)
return NULL;
data_end = (void *)(long)ctx->data_end;
data = (void *)(long)ctx->data;
ipv6h = data + sizeof(*eth);
if (ipv6h + 1 > (struct ipv6hdr *)data_end || if (ipv6h + 1 > (struct ipv6hdr *)data_end ||
ipv6h->nexthdr != IPPROTO_UDP) ipv6h->nexthdr != IPPROTO_UDP)
return NULL; return NULL;
udph = (void *)eth + sizeof(*ipv6h) + sizeof(*eth); udph = data + sizeof(*ipv6h) + sizeof(*eth);
} else { } else {
return NULL; return NULL;
} }
@ -145,17 +174,34 @@ static void swap_machdr(void *data)
static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port) static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
{ {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct udphdr *udph = NULL; struct udphdr *udph = NULL;
struct ethhdr *eth = data; void *data, *data_end;
struct ethhdr *eth;
int err;
err = bpf_xdp_pull_data(ctx, sizeof(*eth));
if (err)
return XDP_PASS;
data_end = (void *)(long)ctx->data_end;
data = eth = (void *)(long)ctx->data;
if (data + sizeof(*eth) > data_end) if (data + sizeof(*eth) > data_end)
return XDP_PASS; return XDP_PASS;
if (eth->h_proto == bpf_htons(ETH_P_IP)) { if (eth->h_proto == bpf_htons(ETH_P_IP)) {
struct iphdr *iph = data + sizeof(*eth); struct iphdr *iph;
__be32 tmp_ip = iph->saddr; __be32 tmp_ip;
err = bpf_xdp_pull_data(ctx, sizeof(*eth) + sizeof(*iph) +
sizeof(*udph));
if (err)
return XDP_PASS;
data_end = (void *)(long)ctx->data_end;
data = (void *)(long)ctx->data;
iph = data + sizeof(*eth);
if (iph + 1 > (struct iphdr *)data_end || if (iph + 1 > (struct iphdr *)data_end ||
iph->protocol != IPPROTO_UDP) iph->protocol != IPPROTO_UDP)
@ -169,8 +215,10 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
return XDP_PASS; return XDP_PASS;
record_stats(ctx, STATS_RX); record_stats(ctx, STATS_RX);
eth = data;
swap_machdr((void *)eth); swap_machdr((void *)eth);
tmp_ip = iph->saddr;
iph->saddr = iph->daddr; iph->saddr = iph->daddr;
iph->daddr = tmp_ip; iph->daddr = tmp_ip;
@ -178,9 +226,19 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
return XDP_TX; return XDP_TX;
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
struct ipv6hdr *ipv6h = data + sizeof(*eth);
struct in6_addr tmp_ipv6; struct in6_addr tmp_ipv6;
struct ipv6hdr *ipv6h;
err = bpf_xdp_pull_data(ctx, sizeof(*eth) + sizeof(*ipv6h) +
sizeof(*udph));
if (err)
return XDP_PASS;
data_end = (void *)(long)ctx->data_end;
data = (void *)(long)ctx->data;
ipv6h = data + sizeof(*eth);
if (ipv6h + 1 > (struct ipv6hdr *)data_end || if (ipv6h + 1 > (struct ipv6hdr *)data_end ||
ipv6h->nexthdr != IPPROTO_UDP) ipv6h->nexthdr != IPPROTO_UDP)
@ -194,6 +252,7 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
return XDP_PASS; return XDP_PASS;
record_stats(ctx, STATS_RX); record_stats(ctx, STATS_RX);
eth = data;
swap_machdr((void *)eth); swap_machdr((void *)eth);
__builtin_memcpy(&tmp_ipv6, &ipv6h->saddr, sizeof(tmp_ipv6)); __builtin_memcpy(&tmp_ipv6, &ipv6h->saddr, sizeof(tmp_ipv6));