Skip to content

Commit a54bcbe

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 a6e1e8b commit a54bcbe

File tree

6 files changed

+185
-22
lines changed

6 files changed

+185
-22
lines changed

bpf/kprobe_pwru.c

+115-6
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ union addr {
4747
} v6addr;
4848
} __attribute__((packed));
4949

50+
struct vxlanhdr {
51+
u32 flags;
52+
u32 vni;
53+
};
54+
5055
struct skb_meta {
5156
u32 netns;
5257
u32 mark;
@@ -85,6 +90,7 @@ struct event_t {
8590
u64 print_shinfo_id;
8691
struct skb_meta meta;
8792
struct tuple tuple;
93+
struct tuple tunnel_tuple;
8894
s64 print_stack_id;
8995
u64 param_second;
9096
u64 param_third;
@@ -146,7 +152,7 @@ struct config {
146152
u8 output_stack: 1;
147153
u8 output_caller: 1;
148154
u8 output_cb: 1;
149-
u8 output_unused: 1;
155+
u8 output_tunnel: 1;
150156
u8 is_set: 1;
151157
u8 track_skb: 1;
152158
u8 track_skb_by_stackid: 1;
@@ -259,8 +265,59 @@ filter_pcap_l2(struct sk_buff *skb)
259265
return filter_pcap_ebpf_l2((void *)skb, (void *)skb, (void *)skb, data, data_end);
260266
}
261267

268+
static __noinline bool
269+
filter_pcap_ebpf_tunnel_l2(void *_skb, void *__skb, void *___skb, void *data, void* data_end)
270+
{
271+
return data != data_end && _skb == __skb && __skb == ___skb;
272+
}
273+
274+
static __noinline bool
275+
filter_pcap_ebpf_tunnel_l3(void *_skb, void *__skb, void *___skb, void *data, void* data_end)
276+
{
277+
return data != data_end && _skb == __skb && __skb == ___skb;
278+
}
279+
280+
static __always_inline bool
281+
filter_pcap_tunnel_l2(struct sk_buff *skb)
282+
{
283+
void *skb_head = BPF_CORE_READ(skb, head);
284+
void *data = skb_head;
285+
void *data_end = skb_head + BPF_CORE_READ(skb, tail);
286+
u16 l3_off = BPF_CORE_READ(skb, network_header);
287+
struct iphdr *ip4 = (struct iphdr *) (data + l3_off);
288+
u16 l4_off = l3_off + BPF_CORE_READ_BITFIELD_PROBED(ip4, ihl) * 4;
289+
// For VXLAN, we only care about udp packets.
290+
if (BPF_CORE_READ(ip4, protocol) != IPPROTO_UDP) {
291+
return true;
292+
}
293+
294+
struct vxlanhdr vxh = {};
295+
if (bpf_probe_read_kernel(&vxh, sizeof(struct vxlanhdr), data + l4_off + 8) != 0) {
296+
return true;
297+
}
298+
299+
if (vxh.flags != 8) {
300+
return true;
301+
}
302+
303+
data = (void*) (data + l4_off + 8 + 8);
304+
struct ethhdr *eth = (struct ethhdr*) data;
305+
if (BPF_CORE_READ(eth, h_proto) != bpf_htons(ETH_P_IP))
306+
return false;
307+
if (!filter_pcap_ebpf_tunnel_l2((void *)skb, (void *)skb, (void *)skb, data, data_end)) {
308+
return false;
309+
}
310+
struct iphdr *iph = (struct iphdr *) (data + sizeof(struct ethhdr));
311+
u32 saddr = BPF_CORE_READ(iph, saddr);
312+
data = (void*) (data + sizeof(struct ethhdr));
313+
return filter_pcap_ebpf_tunnel_l3((void *)skb, (void *)skb, (void *)skb, data, data_end);
314+
}
315+
262316
static __always_inline bool
263317
filter_pcap(struct sk_buff *skb) {
318+
if (!filter_pcap_tunnel_l2(skb)) {
319+
return false;
320+
}
264321
if (BPF_CORE_READ(skb, mac_len) == 0)
265322
return filter_pcap_l3(skb);
266323
return filter_pcap_l2(skb);
@@ -288,7 +345,31 @@ set_meta(struct sk_buff *skb, struct skb_meta *meta) {
288345
}
289346
}
290347

291-
static __always_inline void
348+
// Returns l4 offset
349+
static __always_inline u16
350+
__set_l3_tuple(void *data, u16 l3_off, bool is_ipv4, union addr *saddr, union addr *daddr, u16 *l3_proto, u8 *l4_proto)
351+
{
352+
u16 l4_off;
353+
if (is_ipv4) {
354+
struct iphdr *ip4 = (struct iphdr *) (data + l3_off);
355+
BPF_CORE_READ_INTO(saddr, ip4, saddr);
356+
BPF_CORE_READ_INTO(daddr, ip4, daddr);
357+
BPF_CORE_READ_INTO(l4_proto, ip4, protocol);
358+
*l3_proto = ETH_P_IP;
359+
l4_off = l3_off + BPF_CORE_READ_BITFIELD_PROBED(ip4, ihl) * 4;
360+
361+
} else {
362+
struct ipv6hdr *ip6 = (struct ipv6hdr *) (data + l3_off);
363+
BPF_CORE_READ_INTO(saddr, ip6, saddr);
364+
BPF_CORE_READ_INTO(daddr, ip6, daddr);
365+
BPF_CORE_READ_INTO(l4_proto, ip6, nexthdr);
366+
*l3_proto = ETH_P_IPV6;
367+
l4_off = l3_off + ipv6_hdrlen(ip6);
368+
}
369+
return l4_off;
370+
}
371+
372+
static __always_inline u16
292373
__set_tuple(struct tuple *tpl, void *data, u16 l3_off, bool is_ipv4) {
293374
u16 l4_off;
294375

@@ -314,26 +395,51 @@ __set_tuple(struct tuple *tpl, void *data, u16 l3_off, bool is_ipv4) {
314395
tpl->sport= BPF_CORE_READ(tcp, source);
315396
tpl->dport= BPF_CORE_READ(tcp, dest);
316397
bpf_probe_read_kernel(&tpl->tcp_flags, sizeof(tpl->tcp_flags), (void *)tcp + offsetof(struct tcphdr, window) - 1);
398+
return l4_off + sizeof(*tcp);
317399
} else if (tpl->l4_proto == IPPROTO_UDP) {
318400
struct udphdr *udp = (struct udphdr *) (data + l4_off);
319401
tpl->sport= BPF_CORE_READ(udp, source);
320402
tpl->dport= BPF_CORE_READ(udp, dest);
403+
return l4_off + sizeof(*udp);
321404
}
405+
return l4_off;
322406
}
323407

324-
static __always_inline void
325-
set_tuple(struct sk_buff *skb, struct tuple *tpl) {
408+
static __always_inline s16
409+
set_tuple(struct sk_buff *skb, struct tuple *tpl, struct tuple *tunnel_tpl) {
326410
void *skb_head = BPF_CORE_READ(skb, head);
327411
u16 l3_off = BPF_CORE_READ(skb, network_header);
328412

329413
struct iphdr *l3_hdr = (struct iphdr *) (skb_head + l3_off);
330414
u8 ip_vsn = BPF_CORE_READ_BITFIELD_PROBED(l3_hdr, version);
331415

332416
if (ip_vsn !=4 && ip_vsn != 6)
417+
return -1;
418+
419+
bool is_ipv4 = ip_vsn == 4;
420+
return __set_tuple(tpl, skb_head, l3_off, is_ipv4);
421+
}
422+
423+
static __always_inline void
424+
set_tunnel(struct sk_buff *skb, struct tuple *tpl, struct tuple *tunnel_tpl, u16 l4_data_off) {
425+
void *skb_head = BPF_CORE_READ(skb, head);
426+
struct vxlanhdr vxh = {};
427+
if (bpf_probe_read_kernel(&vxh, sizeof(struct vxlanhdr), skb_head + l4_data_off) != 0) {
428+
return;
429+
}
430+
431+
if (vxh.flags != 0x8)
333432
return;
334433

434+
struct ethhdr *inner = (struct ethhdr*) (skb_head + l4_data_off + sizeof(struct vxlanhdr));
435+
if (BPF_CORE_READ(inner, h_proto) != bpf_htons(ETH_P_IP))
436+
return;
437+
438+
struct iphdr *l3_hdr = (struct iphdr *) (skb_head + l4_data_off + sizeof(struct vxlanhdr) + sizeof(struct ethhdr));
439+
u8 ip_vsn = BPF_CORE_READ_BITFIELD_PROBED(l3_hdr, version);
335440
bool is_ipv4 = ip_vsn == 4;
336-
__set_tuple(tpl, skb_head, l3_off, is_ipv4);
441+
u16 l3_off = l4_data_off + sizeof(struct vxlanhdr) + sizeof(struct ethhdr);
442+
__set_tuple(tunnel_tpl, skb_head, l3_off, is_ipv4);
337443
}
338444

339445
static __always_inline u64
@@ -433,7 +539,10 @@ set_output(void *ctx, struct sk_buff *skb, struct event_t *event) {
433539
}
434540

435541
if (cfg->output_tuple) {
436-
set_tuple(skb, &event->tuple);
542+
s16 l4_off = set_tuple(skb, &event->tuple, &event->tunnel_tuple);
543+
if (cfg->output_tunnel && l4_off != -1 && event->tuple.l4_proto == IPPROTO_UDP) {
544+
set_tunnel(skb, &event->tuple, &event->tunnel_tuple, l4_off);
545+
}
437546
}
438547

439548
if (cfg->output_skb) {

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

+10
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 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")
@@ -143,6 +147,11 @@ func (f tcpFlag) String() string {
143147
return strings.Join(flags, "|")
144148
}
145149

150+
type L2Tuple struct {
151+
Dest [6]byte
152+
Src [6]byte
153+
}
154+
146155
type Tuple struct {
147156
Saddr [16]byte
148157
Daddr [16]byte
@@ -178,6 +187,7 @@ type Event struct {
178187
PrintShinfoId uint64
179188
Meta Meta
180189
Tuple Tuple
190+
TunnelTuple Tuple
181191
PrintStackId int64
182192
ParamSecond uint64
183193
ParamThird uint64

0 commit comments

Comments
 (0)