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.compull/1354/merge
parent
323302f54d
commit
efec2e55bd
|
|
@ -14,6 +14,8 @@
|
|||
#define MAX_PAYLOAD_LEN 5000
|
||||
#define MAX_HDR_LEN 64
|
||||
|
||||
extern int bpf_xdp_pull_data(struct xdp_md *xdp, __u32 len) __ksym __weak;
|
||||
|
||||
enum {
|
||||
XDP_MODE = 0,
|
||||
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)
|
||||
{
|
||||
void *data_end = (void *)(long)ctx->data_end;
|
||||
void *data = (void *)(long)ctx->data;
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
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 ||
|
||||
iph->protocol != IPPROTO_UDP)
|
||||
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)) {
|
||||
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 ||
|
||||
ipv6h->nexthdr != IPPROTO_UDP)
|
||||
return NULL;
|
||||
|
||||
udph = (void *)eth + sizeof(*ipv6h) + sizeof(*eth);
|
||||
udph = data + sizeof(*ipv6h) + sizeof(*eth);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -145,17 +174,34 @@ static void swap_machdr(void *data)
|
|||
|
||||
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 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)
|
||||
return XDP_PASS;
|
||||
|
||||
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
|
||||
struct iphdr *iph = data + sizeof(*eth);
|
||||
__be32 tmp_ip = iph->saddr;
|
||||
struct iphdr *iph;
|
||||
__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 ||
|
||||
iph->protocol != IPPROTO_UDP)
|
||||
|
|
@ -169,8 +215,10 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
|
|||
return XDP_PASS;
|
||||
|
||||
record_stats(ctx, STATS_RX);
|
||||
eth = data;
|
||||
swap_machdr((void *)eth);
|
||||
|
||||
tmp_ip = iph->saddr;
|
||||
iph->saddr = iph->daddr;
|
||||
iph->daddr = tmp_ip;
|
||||
|
||||
|
|
@ -179,8 +227,18 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
|
|||
return XDP_TX;
|
||||
|
||||
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
|
||||
struct ipv6hdr *ipv6h = data + sizeof(*eth);
|
||||
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 ||
|
||||
ipv6h->nexthdr != IPPROTO_UDP)
|
||||
|
|
@ -194,6 +252,7 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
|
|||
return XDP_PASS;
|
||||
|
||||
record_stats(ctx, STATS_RX);
|
||||
eth = data;
|
||||
swap_machdr((void *)eth);
|
||||
|
||||
__builtin_memcpy(&tmp_ipv6, &ipv6h->saddr, sizeof(tmp_ipv6));
|
||||
|
|
|
|||
Loading…
Reference in New Issue