Skip to content

Commit c30d64f

Browse files
committed
Add tunnel l2 pcap flag for optiona vxlan pcap.
If the flag is enabled, packets that appear to be vxlan encapsulated will have the filtering function applied. Note: Therefore, to avoid getting non-vxlan traffic you will want to apply a general pcap filter on the vxlan udp ports. As well, the flag --output-tunnel will result in output of vxlan header data (i.e. flag/vin) as well as inner address tuple. Signed-off-by: Tom Hadlaw <[email protected]>
1 parent 63396c8 commit c30d64f

File tree

6 files changed

+122
-17
lines changed

6 files changed

+122
-17
lines changed

bpf/kprobe_pwru.c

+57-1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ struct event_t {
8585
u64 print_shinfo_id;
8686
struct skb_meta meta;
8787
struct tuple tuple;
88+
struct tuple tunnel_tuple;
8889
s64 print_stack_id;
8990
u64 param_second;
9091
u64 param_third;
@@ -146,7 +147,7 @@ struct config {
146147
u8 output_stack: 1;
147148
u8 output_caller: 1;
148149
u8 output_cb: 1;
149-
u8 output_unused: 1;
150+
u8 output_tunnel: 1;
150151
u8 is_set: 1;
151152
u8 track_skb: 1;
152153
u8 track_skb_by_stackid: 1;
@@ -259,8 +260,46 @@ filter_pcap_l2(struct sk_buff *skb)
259260
return filter_pcap_ebpf_l2((void *)skb, (void *)skb, (void *)skb, data, data_end);
260261
}
261262

263+
static __noinline bool
264+
filter_pcap_ebpf_tunnel_l2(void *_skb, void *__skb, void *___skb, void *data, void* data_end)
265+
{
266+
return data != data_end && _skb == __skb && __skb == ___skb;
267+
}
268+
269+
static __noinline bool
270+
filter_pcap_ebpf_tunnel_l3(void *_skb, void *__skb, void *___skb, void *data, void* data_end)
271+
{
272+
return data != data_end && _skb == __skb && __skb == ___skb;
273+
}
274+
275+
static __always_inline bool
276+
filter_pcap_tunnel_l2(struct sk_buff *skb)
277+
{
278+
u16 tunnel_l3_off = BPF_CORE_READ(skb, inner_network_header);
279+
// If we don't have a tunnel network header, assume its not tunnel traffic.
280+
// We'll let the injected pcap program handle figuring out the transport
281+
// protocol.
282+
if (tunnel_l3_off == 0)
283+
return false;
284+
285+
void *skb_head = BPF_CORE_READ(skb, head);
286+
void *data = skb_head;
287+
void *data_end = skb_head + BPF_CORE_READ(skb, tail);
288+
289+
if (!filter_pcap_ebpf_tunnel_l2((void *)skb, (void *)skb, (void *)skb,
290+
data + BPF_CORE_READ(skb, inner_mac_header), data_end)) {
291+
return false;
292+
}
293+
294+
return filter_pcap_ebpf_tunnel_l3((void *)skb, (void *)skb, (void *)skb,
295+
data + BPF_CORE_READ(skb, inner_network_header), data_end);
296+
}
297+
262298
static __always_inline bool
263299
filter_pcap(struct sk_buff *skb) {
300+
if (!filter_pcap_tunnel_l2(skb)) {
301+
return false;
302+
}
264303
if (BPF_CORE_READ(skb, mac_len) == 0)
265304
return filter_pcap_l3(skb);
266305
return filter_pcap_l2(skb);
@@ -336,6 +375,19 @@ set_tuple(struct sk_buff *skb, struct tuple *tpl) {
336375
__set_tuple(tpl, skb_head, l3_off, is_ipv4);
337376
}
338377

378+
static __always_inline void
379+
set_tunnel(struct sk_buff *skb, struct tuple *tpl, struct tuple *tunnel_tpl) {
380+
u16 tunnel_l3_off = BPF_CORE_READ(skb, inner_network_header);
381+
// If we don't have a tunnel network header, assume its not tunnel traffic.
382+
if (tunnel_l3_off == 0)
383+
return;
384+
void *skb_head = BPF_CORE_READ(skb, head);
385+
struct iphdr *l3_hdr = (struct iphdr *) (skb_head + tunnel_l3_off);
386+
u8 ip_vsn = BPF_CORE_READ_BITFIELD_PROBED(l3_hdr, version);
387+
bool is_ipv4 = ip_vsn == 4;
388+
__set_tuple(tunnel_tpl, skb_head, tunnel_l3_off, is_ipv4);
389+
}
390+
339391
static __always_inline u64
340392
sync_fetch_and_add(void *id_map) {
341393
u32 *id = bpf_map_lookup_elem(id_map, &ZERO);
@@ -436,6 +488,10 @@ set_output(void *ctx, struct sk_buff *skb, struct event_t *event) {
436488
set_tuple(skb, &event->tuple);
437489
}
438490

491+
if (cfg->output_tunnel) {
492+
set_tunnel(skb, &event->tuple, &event->tunnel_tuple);
493+
}
494+
439495
if (cfg->output_skb) {
440496
set_skb_btf(skb, &event->print_skb_id);
441497
}

internal/libpcap/inject.go

+26-9
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,54 @@ package libpcap
22

33
import (
44
"errors"
5+
"fmt"
56

67
"github.com/cilium/ebpf"
78
"github.com/cilium/ebpf/asm"
89
"github.com/cloudflare/cbpfc"
910
)
1011

12+
func InjectL2TunnelFilter(program *ebpf.ProgramSpec, filterExpr, l2TunnelFilterExpr string) (err error) {
13+
return injectFilter(program, filterExpr, false, true)
14+
}
15+
1116
func InjectL2Filter(program *ebpf.ProgramSpec, filterExpr string) (err error) {
12-
return injectFilter(program, filterExpr, false)
17+
return injectFilter(program, filterExpr, false, false)
1318
}
1419

15-
func InjectFilters(program *ebpf.ProgramSpec, filterExpr string) (err error) {
16-
if err = injectFilter(program, filterExpr, false); err != nil {
20+
func InjectFilters(program *ebpf.ProgramSpec, filterExpr, tunnelFilterExpr string) (err error) {
21+
if err = injectFilter(program, filterExpr, false, false); err != nil {
1722
return
1823
}
19-
if err = injectFilter(program, filterExpr, true); err != nil {
24+
if err = injectFilter(program, filterExpr, true, false); err != nil {
2025
// This could happen for l2 only filters such as "arp". In this
2126
// case we don't want to exit with an error, but instead inject
2227
// a deny-all filter to reject all l3 skbs.
23-
return injectFilter(program, "__pwru_reject_all__", true)
28+
return injectFilter(program, "__pwru_reject_all__", true, false)
29+
}
30+
// Attach any tunnel filters.
31+
if err := injectFilter(program, tunnelFilterExpr, false, true); err != nil {
32+
return fmt.Errorf("l2 tunnel filter: %w", err)
33+
}
34+
if err := injectFilter(program, tunnelFilterExpr, true, true); err != nil {
35+
return fmt.Errorf("l3 tunnel filter: %w", err)
2436
}
25-
return
37+
return nil
2638
}
2739

28-
func injectFilter(program *ebpf.ProgramSpec, filterExpr string, l3 bool) (err error) {
40+
func injectFilter(program *ebpf.ProgramSpec, filterExpr string, l3 bool, tunnel bool) (err error) {
2941
if filterExpr == "" {
3042
return
3143
}
3244

33-
suffix := "_l2"
45+
tunnelSuffix := ""
46+
if tunnel {
47+
tunnelSuffix = "_tunnel"
48+
}
49+
50+
suffix := tunnelSuffix + "_l2"
3451
if l3 {
35-
suffix = "_l3"
52+
suffix = tunnelSuffix + "_l3"
3653
}
3754
injectIdx := -1
3855
for idx, inst := range program.Instructions {

internal/pwru/config.go

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
OutputStackMask
2424
OutputCallerMask
2525
OutputCbMask
26+
OutputTunnelMask
2627
)
2728

2829
const (
@@ -66,6 +67,9 @@ func GetConfig(flags *Flags) (cfg FilterCfg, err error) {
6667
if flags.OutputTuple {
6768
cfg.OutputFlags |= OutputTupleMask
6869
}
70+
if flags.OutputTunnel {
71+
cfg.OutputFlags |= OutputTunnelMask
72+
}
6973
if flags.OutputStack {
7074
cfg.OutputFlags |= OutputStackMask
7175
}

internal/pwru/output.go

+28-6
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ type jsonPrinter struct {
6767
Len uint32 `json:"len,omitempty"`
6868
Cb [5]uint32 `json:"cb,omitempty"`
6969
Tuple *jsonTuple `json:"tuple,omitempty"`
70+
TunnelTuple *jsonTuple `json:"tunnel_tuple,omitempty"`
7071
Stack interface{} `json:"stack,omitempty"`
7172
SkbMetadata interface{} `json:"skb_metadata,omitempty"`
7273
}
@@ -155,6 +156,9 @@ func (o *output) PrintHeader() {
155156
if o.flags.OutputCaller {
156157
fmt.Fprintf(o.writer, " %s", "CALLER")
157158
}
159+
if o.flags.OutputTunnel {
160+
fmt.Fprintf(o.writer, " %s", "TUNNEL")
161+
}
158162
fmt.Fprintf(o.writer, "\n")
159163
}
160164

@@ -209,6 +213,16 @@ func (o *output) PrintJson(event *Event) {
209213
d.Tuple = t
210214
}
211215

216+
if o.flags.OutputTuple {
217+
t := &jsonTuple{}
218+
t.Saddr = addrToStr(event.TunnelTuple.L3Proto, event.TunnelTuple.Saddr)
219+
t.Daddr = addrToStr(event.TunnelTuple.L3Proto, event.TunnelTuple.Daddr)
220+
t.Sport = byteorder.NetworkToHost16(event.TunnelTuple.Sport)
221+
t.Dport = byteorder.NetworkToHost16(event.TunnelTuple.Dport)
222+
t.Proto = event.TunnelTuple.L4Proto
223+
d.TunnelTuple = t
224+
}
225+
212226
if o.flags.OutputStack && event.PrintStackId > 0 {
213227
d.Stack = getStackData(event, o)
214228
}
@@ -273,20 +287,24 @@ func getAddrByArch(event *Event, o *output) (addr uint64) {
273287
return addr
274288
}
275289

276-
func getTupleData(event *Event, outputTCPFlags bool) (tupleData string) {
290+
func getTuple(tpl Tuple, outputTCPFlags bool) (tupleData string) {
277291
var l4Info string
278-
if event.Tuple.L4Proto == syscall.IPPROTO_TCP && event.Tuple.TCPFlag != 0 && outputTCPFlags {
279-
l4Info = fmt.Sprintf("%s:%s", protoToStr(event.Tuple.L4Proto), event.Tuple.TCPFlag)
292+
if tpl.L4Proto == syscall.IPPROTO_TCP && tpl.TCPFlag != 0 && outputTCPFlags {
293+
l4Info = fmt.Sprintf("%s:%s", protoToStr(tpl.L4Proto), tpl.TCPFlag)
280294
} else {
281-
l4Info = protoToStr(event.Tuple.L4Proto)
295+
l4Info = protoToStr(tpl.L4Proto)
282296
}
283297
tupleData = fmt.Sprintf("%s:%d->%s:%d(%s)",
284-
addrToStr(event.Tuple.L3Proto, event.Tuple.Saddr), byteorder.NetworkToHost16(event.Tuple.Sport),
285-
addrToStr(event.Tuple.L3Proto, event.Tuple.Daddr), byteorder.NetworkToHost16(event.Tuple.Dport),
298+
addrToStr(tpl.L3Proto, tpl.Saddr), byteorder.NetworkToHost16(tpl.Sport),
299+
addrToStr(tpl.L3Proto, tpl.Daddr), byteorder.NetworkToHost16(tpl.Dport),
286300
l4Info)
287301
return tupleData
288302
}
289303

304+
func getTupleData(event *Event, outputTCPFlags bool) (tupleData string) {
305+
return getTuple(event.Tuple, outputTCPFlags)
306+
}
307+
290308
func getStackData(event *Event, o *output) (stackData string) {
291309
var stack StackData
292310
id := uint32(event.PrintStackId)
@@ -462,6 +480,10 @@ func (o *output) Print(event *Event) {
462480
fmt.Fprintf(o.writer, "%s", getShinfoData(event, o))
463481
}
464482

483+
if o.flags.OutputTunnel {
484+
fmt.Fprintf(o.writer, " %s", getTuple(event.TunnelTuple, o.flags.OutputTCPFlags))
485+
}
486+
465487
fmt.Fprintln(o.writer)
466488
}
467489

internal/pwru/types.go

+5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Flags struct {
3838
FilterTrackBpfHelpers bool
3939
FilterIfname string
4040
FilterPcap string
41+
FilterTunnelPcap string
4142
FilterKprobeBatch uint
4243

4344
OutputTS string
@@ -52,6 +53,7 @@ type Flags struct {
5253
OutputFile string
5354
OutputJson bool
5455
OutputTCPFlags bool
56+
OutputTunnel bool
5557

5658
KMods []string
5759
AllKMods bool
@@ -74,6 +76,7 @@ func (f *Flags) SetFlags() {
7476
flag.BoolVar(&f.FilterTrackSkb, "filter-track-skb", false, "trace a packet even if it does not match given filters (e.g., after NAT or tunnel decapsulation)")
7577
flag.BoolVar(&f.FilterTrackSkbByStackid, "filter-track-skb-by-stackid", false, "trace a packet even after it is kfreed (e.g., traffic going through bridge)")
7678
flag.BoolVar(&f.FilterTraceTc, "filter-trace-tc", false, "trace TC bpf progs")
79+
flag.StringVar(&f.FilterTunnelPcap, "filter-tunnel-pcap", "", "pcap expression for vxlan/geneve tunnel (l3)")
7780
flag.BoolVar(&f.FilterTraceXdp, "filter-trace-xdp", false, "trace XDP bpf progs")
7881
flag.BoolVar(&f.FilterTrackBpfHelpers, "filter-track-bpf-helpers", false, "trace BPF helper functions")
7982
flag.StringVar(&f.FilterIfname, "filter-ifname", "", "filter skb ifname in --filter-netns (if not specified, use current netns)")
@@ -83,6 +86,7 @@ func (f *Flags) SetFlags() {
8386
flag.BoolVar(&f.OutputTuple, "output-tuple", true, "print L4 tuple")
8487
flag.BoolVar(&f.OutputSkb, "output-skb", false, "print skb")
8588
flag.BoolVar(&f.OutputShinfo, "output-skb-shared-info", false, "print skb shared info")
89+
flag.BoolVar(&f.OutputTunnel, "output-tunnel", false, "print encapsulated tunnel header data")
8690
flag.BoolVar(&f.OutputStack, "output-stack", false, "print stack")
8791
flag.BoolVar(&f.OutputCaller, "output-caller", false, "print caller function name")
8892
flag.Uint64Var(&f.OutputLimitLines, "output-limit-lines", 0, "exit the program after the number of events has been received/printed")
@@ -178,6 +182,7 @@ type Event struct {
178182
PrintShinfoId uint64
179183
Meta Meta
180184
Tuple Tuple
185+
TunnelTuple Tuple
181186
PrintStackId int64
182187
ParamSecond uint64
183188
ParamThird uint64

main.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ func main() {
150150
}
151151
continue
152152
}
153-
if err = libpcap.InjectFilters(program, flags.FilterPcap); err != nil {
153+
if err = libpcap.InjectFilters(program,
154+
flags.FilterPcap, flags.FilterTunnelPcap); err != nil {
154155
log.Fatalf("Failed to inject filter ebpf for %s: %v", name, err)
155156
}
156157
}

0 commit comments

Comments
 (0)