Skip to content

Commit 45d31e2

Browse files
committed
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 direcy packet access, call bpf_xdp_pull_data() to pull headers into the linear data area. Signed-off-by: Amery Hung <[email protected]>
1 parent 9b1f62f commit 45d31e2

File tree

1 file changed

+75
-15
lines changed

1 file changed

+75
-15
lines changed

tools/testing/selftests/net/lib/xdp_native.bpf.c

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
#define MAX_PAYLOAD_LEN 5000
1515
#define MAX_HDR_LEN 64
1616

17+
extern int bpf_xdp_pull_data(struct xdp_md *xdp, __u32 len,
18+
__u64 flags) __ksym __weak;
19+
1720
enum {
1821
XDP_MODE = 0,
1922
XDP_PORT = 1,
@@ -68,30 +71,57 @@ static void record_stats(struct xdp_md *ctx, __u32 stat_type)
6871

6972
static struct udphdr *filter_udphdr(struct xdp_md *ctx, __u16 port)
7073
{
71-
void *data_end = (void *)(long)ctx->data_end;
72-
void *data = (void *)(long)ctx->data;
7374
struct udphdr *udph = NULL;
74-
struct ethhdr *eth = data;
75+
void *data, *data_end;
76+
struct ethhdr *eth;
77+
int err;
78+
79+
err = bpf_xdp_pull_data(ctx, sizeof(*eth), 0);
80+
if (err)
81+
return NULL;
82+
83+
data_end = (void *)(long)ctx->data_end;
84+
data = eth = (void *)(long)ctx->data;
7585

7686
if (data + sizeof(*eth) > data_end)
7787
return NULL;
7888

7989
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
80-
struct iphdr *iph = data + sizeof(*eth);
90+
struct iphdr *iph;
91+
92+
err = bpf_xdp_pull_data(ctx, sizeof(*eth) + sizeof(*iph) +
93+
sizeof(*udph), 0);
94+
if (err)
95+
return NULL;
96+
97+
data_end = (void *)(long)ctx->data_end;
98+
data = (void *)(long)ctx->data;
99+
100+
iph = data + sizeof(*eth);
81101

82102
if (iph + 1 > (struct iphdr *)data_end ||
83103
iph->protocol != IPPROTO_UDP)
84104
return NULL;
85105

86-
udph = (void *)eth + sizeof(*iph) + sizeof(*eth);
87-
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
88-
struct ipv6hdr *ipv6h = data + sizeof(*eth);
106+
udph = data + sizeof(*iph) + sizeof(*eth);
107+
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
108+
struct ipv6hdr *ipv6h;
109+
110+
err = bpf_xdp_pull_data(ctx, sizeof(*eth) + sizeof(*ipv6h) +
111+
sizeof(*udph), 0);
112+
if (err)
113+
return NULL;
114+
115+
data_end = (void *)(long)ctx->data_end;
116+
data = (void *)(long)ctx->data;
117+
118+
ipv6h = data + sizeof(*eth);
89119

90120
if (ipv6h + 1 > (struct ipv6hdr *)data_end ||
91121
ipv6h->nexthdr != IPPROTO_UDP)
92122
return NULL;
93123

94-
udph = (void *)eth + sizeof(*ipv6h) + sizeof(*eth);
124+
udph = data + sizeof(*ipv6h) + sizeof(*eth);
95125
} else {
96126
return NULL;
97127
}
@@ -145,17 +175,34 @@ static void swap_machdr(void *data)
145175

146176
static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
147177
{
148-
void *data_end = (void *)(long)ctx->data_end;
149-
void *data = (void *)(long)ctx->data;
150178
struct udphdr *udph = NULL;
151-
struct ethhdr *eth = data;
179+
void *data, *data_end;
180+
struct ethhdr *eth;
181+
int err;
182+
183+
err = bpf_xdp_pull_data(ctx, sizeof(*eth), 0);
184+
if (err)
185+
return XDP_PASS;
186+
187+
data_end = (void *)(long)ctx->data_end;
188+
data = eth = (void *)(long)ctx->data;
152189

153190
if (data + sizeof(*eth) > data_end)
154191
return XDP_PASS;
155192

156193
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
157-
struct iphdr *iph = data + sizeof(*eth);
158-
__be32 tmp_ip = iph->saddr;
194+
struct iphdr *iph;
195+
__be32 tmp_ip;
196+
197+
err = bpf_xdp_pull_data(ctx, sizeof(*eth) + sizeof(*iph) +
198+
sizeof(*udph), 0);
199+
if (err)
200+
return XDP_PASS;
201+
202+
data_end = (void *)(long)ctx->data_end;
203+
data = (void *)(long)ctx->data;
204+
205+
iph = data + sizeof(*eth);
159206

160207
if (iph + 1 > (struct iphdr *)data_end ||
161208
iph->protocol != IPPROTO_UDP)
@@ -169,18 +216,30 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
169216
return XDP_PASS;
170217

171218
record_stats(ctx, STATS_RX);
219+
eth = data;
172220
swap_machdr((void *)eth);
173221

222+
tmp_ip = iph->saddr;
174223
iph->saddr = iph->daddr;
175224
iph->daddr = tmp_ip;
176225

177226
record_stats(ctx, STATS_TX);
178227

179228
return XDP_TX;
180229

181-
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
182-
struct ipv6hdr *ipv6h = data + sizeof(*eth);
230+
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
183231
struct in6_addr tmp_ipv6;
232+
struct ipv6hdr *ipv6h;
233+
234+
err = bpf_xdp_pull_data(ctx, sizeof(*eth) + sizeof(*ipv6h) +
235+
sizeof(*udph), 0);
236+
if (err)
237+
return XDP_PASS;
238+
239+
data_end = (void *)(long)ctx->data_end;
240+
data = (void *)(long)ctx->data;
241+
242+
ipv6h = data + sizeof(*eth);
184243

185244
if (ipv6h + 1 > (struct ipv6hdr *)data_end ||
186245
ipv6h->nexthdr != IPPROTO_UDP)
@@ -194,6 +253,7 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
194253
return XDP_PASS;
195254

196255
record_stats(ctx, STATS_RX);
256+
eth = data;
197257
swap_machdr((void *)eth);
198258

199259
__builtin_memcpy(&tmp_ipv6, &ipv6h->saddr, sizeof(tmp_ipv6));

0 commit comments

Comments
 (0)