Skip to content

Commit c117051

Browse files
committed
Add xdp_vlan01_kern.c to investigate compiler optimizations
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
1 parent 29fcd4a commit c117051

4 files changed

Lines changed: 119 additions & 1 deletion

File tree

common/parsing_helpers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ struct icmphdr_common {
5656

5757
/* Allow users of header file to redefine VLAN max depth */
5858
#ifndef VLAN_MAX_DEPTH
59-
#define VLAN_MAX_DEPTH 2
59+
#define VLAN_MAX_DEPTH 4
6060
#endif
6161

6262
static __always_inline int proto_is_vlan(__u16 h_proto)

packet-solutions/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
22

33
XDP_TARGETS := xdp_prog_kern_02 xdp_prog_kern_03
4+
XDP_TARGETS += xdp_vlan01_kern
45
USER_TARGETS := xdp_prog_user
56

67
LIBBPF_DIR = ../libbpf/src/

packet-solutions/xdp_prog_kern_02.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* SPDX-License-Identifier: GPL-2.0 */
22
#include <linux/bpf.h>
33
#include <linux/in.h>
4+
5+
#include <linux/if_ether.h>
46
#include "bpf_helpers.h"
57
#include "bpf_endian.h"
68

packet-solutions/xdp_vlan01_kern.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2+
#include <linux/bpf.h>
3+
#include <linux/if_ether.h>
4+
5+
#include "bpf_helpers.h"
6+
#include "bpf_endian.h"
7+
8+
/* NOTICE: Re-defining VLAN header levels to parse */
9+
#define VLAN_MAX_DEPTH 10
10+
//#include "../common/parsing_helpers.h"
11+
/*
12+
* NOTICE: Copied over parts of ../common/parsing_helpers.h
13+
* to make it easier to point out compiler optimizations
14+
*/
15+
16+
/* Header cursor to keep track of current parsing position */
17+
struct hdr_cursor {
18+
void *pos;
19+
};
20+
21+
static __always_inline int proto_is_vlan(__u16 h_proto)
22+
{
23+
return !!(h_proto == bpf_htons(ETH_P_8021Q) ||
24+
h_proto == bpf_htons(ETH_P_8021AD));
25+
}
26+
27+
/*
28+
* struct vlan_hdr - vlan header
29+
* @h_vlan_TCI: priority and VLAN ID
30+
* @h_vlan_encapsulated_proto: packet type ID or len
31+
*/
32+
struct vlan_hdr {
33+
__be16 h_vlan_TCI;
34+
__be16 h_vlan_encapsulated_proto; /* NOTICE: unsigned type */
35+
};
36+
37+
/* Notice, parse_ethhdr() will skip VLAN tags, by advancing nh->pos and returns
38+
* next header EtherType, BUT the ethhdr pointer supplied still points to the
39+
* Ethernet header. Thus, caller can look at eth->h_proto to see if this was a
40+
* VLAN tagged packet.
41+
*/
42+
static __always_inline int parse_ethhdr(struct hdr_cursor *nh, void *data_end,
43+
struct ethhdr **ethhdr)
44+
{
45+
struct ethhdr *eth = nh->pos;
46+
int hdrsize = sizeof(*eth);
47+
struct vlan_hdr *vlh;
48+
__u16 h_proto;
49+
int i;
50+
51+
/* Byte-count bounds check; check if current pointer + size of header
52+
* is after data_end.
53+
*/
54+
if (nh->pos + hdrsize > data_end)
55+
return -1;
56+
57+
nh->pos += hdrsize;
58+
*ethhdr = eth;
59+
vlh = nh->pos;
60+
h_proto = eth->h_proto;
61+
62+
/* Use loop unrolling to avoid the verifier restriction on loops;
63+
* support up to VLAN_MAX_DEPTH layers of VLAN encapsulation.
64+
*/
65+
#pragma unroll
66+
for (i = 0; i < VLAN_MAX_DEPTH; i++) {
67+
if (!proto_is_vlan(h_proto))
68+
break;
69+
70+
if (vlh + 1 > data_end)
71+
break;
72+
73+
h_proto = vlh->h_vlan_encapsulated_proto;
74+
vlh++;
75+
}
76+
77+
nh->pos = vlh;
78+
return bpf_ntohs(h_proto);
79+
}
80+
81+
SEC("xdp_vlan01")
82+
int xdp_vlan_01(struct xdp_md *ctx)
83+
{
84+
void *data_end = (void *)(long)ctx->data_end;
85+
void *data = (void *)(long)ctx->data;
86+
87+
/* These keep track of the next header type and iterator pointer */
88+
struct hdr_cursor nh;
89+
int nh_type;
90+
nh.pos = data;
91+
92+
struct ethhdr *eth;
93+
nh_type = parse_ethhdr(&nh, data_end, &eth);
94+
if (nh_type < 0)
95+
return XDP_ABORTED;
96+
97+
/* The LLVM compiler is very clever, and will remove above walking of
98+
* VLAN headers (the loop unroll).
99+
*
100+
* The returned value nh_type, variable (__u16) h_proto in
101+
* parse_ethhdr(), is only compared against a negative value (signed).
102+
* The compile see that it can remove the VLAN loop, because:
103+
* 1. h_proto = vlh->h_vlan_encapsulated_proto can only be >= 0
104+
* 2. we never read nh->pos (so it removes nh->pos = vlh;).
105+
*/
106+
107+
/* Accessing eth pointer is still valid after compiler optimization */
108+
if (proto_is_vlan(eth->h_proto))
109+
return XDP_DROP;
110+
111+
/* Hint: to inspect BPF byte-code run:
112+
* llvm-objdump -S xdp_vlan01_kern.o
113+
*/
114+
return XDP_PASS;
115+
}

0 commit comments

Comments
 (0)