Skip to content

Commit dc7a699

Browse files
ameryhungKernel Patches Daemon
authored andcommitted
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 7e249c0 commit dc7a699

File tree

1 file changed

+74
-15
lines changed

1 file changed

+74
-15
lines changed

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

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
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) __ksym __weak;
18+
1719
enum {
1820
XDP_MODE = 0,
1921
XDP_PORT = 1,
@@ -68,30 +70,57 @@ static void record_stats(struct xdp_md *ctx, __u32 stat_type)
6870

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

7685
if (data + sizeof(*eth) > data_end)
7786
return NULL;
7887

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

82101
if (iph + 1 > (struct iphdr *)data_end ||
83102
iph->protocol != IPPROTO_UDP)
84103
return NULL;
85104

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);
105+
udph = data + sizeof(*iph) + sizeof(*eth);
106+
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
107+
struct ipv6hdr *ipv6h;
108+
109+
err = bpf_xdp_pull_data(ctx, sizeof(*eth) + sizeof(*ipv6h) +
110+
sizeof(*udph));
111+
if (err)
112+
return NULL;
113+
114+
data_end = (void *)(long)ctx->data_end;
115+
data = (void *)(long)ctx->data;
116+
117+
ipv6h = data + sizeof(*eth);
89118

90119
if (ipv6h + 1 > (struct ipv6hdr *)data_end ||
91120
ipv6h->nexthdr != IPPROTO_UDP)
92121
return NULL;
93122

94-
udph = (void *)eth + sizeof(*ipv6h) + sizeof(*eth);
123+
udph = data + sizeof(*ipv6h) + sizeof(*eth);
95124
} else {
96125
return NULL;
97126
}
@@ -145,17 +174,34 @@ static void swap_machdr(void *data)
145174

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

153189
if (data + sizeof(*eth) > data_end)
154190
return XDP_PASS;
155191

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

160206
if (iph + 1 > (struct iphdr *)data_end ||
161207
iph->protocol != IPPROTO_UDP)
@@ -169,18 +215,30 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
169215
return XDP_PASS;
170216

171217
record_stats(ctx, STATS_RX);
218+
eth = data;
172219
swap_machdr((void *)eth);
173220

221+
tmp_ip = iph->saddr;
174222
iph->saddr = iph->daddr;
175223
iph->daddr = tmp_ip;
176224

177225
record_stats(ctx, STATS_TX);
178226

179227
return XDP_TX;
180228

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

185243
if (ipv6h + 1 > (struct ipv6hdr *)data_end ||
186244
ipv6h->nexthdr != IPPROTO_UDP)
@@ -194,6 +252,7 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
194252
return XDP_PASS;
195253

196254
record_stats(ctx, STATS_RX);
255+
eth = data;
197256
swap_machdr((void *)eth);
198257

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

0 commit comments

Comments
 (0)