Skip to content

Commit ec6f9c6

Browse files
hangyanantoninbas
andauthored
Fix packetcapture bpf filter issue (antrea-io#6815) (antrea-io#6821)
In PacketCapture, packets which don’t match the target BPF can be received after the socket is created and before the bpf filter is applied. This patch uses a zero bpf filter (matches no packet), then empties out any packets that arrived before the "zero-BPF" filter was applied. At this point the socket is definitely empty and it can’t fill up with junk because the zero-BPF is in place. Then we replace the zero-BPF with the real BPF we want. Signed-off-by: Hang Yan <[email protected]> Co-authored-by: Antonin Bas <[email protected]>
1 parent 8090c96 commit ec6f9c6

File tree

1 file changed

+41
-3
lines changed

1 file changed

+41
-3
lines changed

pkg/agent/packetcapture/capture/pcap_linux.go

+41-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package capture
1717
import (
1818
"context"
1919
"net"
20+
"time"
2021

2122
"github.com/gopacket/gopacket"
2223
"github.com/gopacket/gopacket/layers"
@@ -39,6 +40,12 @@ func NewPcapCapture() (*pcapCapture, error) {
3940
return &pcapCapture{}, nil
4041
}
4142

43+
// zeroFilter is a filter that will drop all packets.
44+
// see: https://github.com/antrea-io/antrea/issues/6815 for the user case.
45+
func zeroFilter() []bpf.Instruction {
46+
return []bpf.Instruction{returnDrop}
47+
}
48+
4249
func (p *pcapCapture) Capture(ctx context.Context, device string, srcIP, dstIP net.IP, packet *crdv1alpha1.Packet) (chan gopacket.Packet, error) {
4350
// Compile the BPF filter in advance to reduce the time window between starting the capture and applying the filter.
4451
inst := compilePacketFilter(packet, srcIP, dstIP)
@@ -48,21 +55,52 @@ func (p *pcapCapture) Capture(ctx context.Context, device string, srcIP, dstIP n
4855
return nil, err
4956
}
5057

58+
zeroRawInst, err := bpf.Assemble(zeroFilter())
59+
if err != nil {
60+
return nil, err
61+
}
62+
5163
eth, err := pcapgo.NewEthernetHandle(device)
5264
if err != nil {
5365
return nil, err
5466
}
5567
if err = eth.SetPromiscuous(false); err != nil {
5668
return nil, err
5769
}
58-
if err = eth.SetBPF(rawInst); err != nil {
70+
// Install a BPF filter that won't match any packets
71+
// see: https://natanyellin.com/posts/ebpf-filtering-done-right/.
72+
// Packets which don’t match the target BPF can be received after the socket
73+
// is created and before setsockopt is called. Those packets will remain
74+
// in the socket’s buffer even after the BPF is applied and will later
75+
// be transferred to the application via recv. Here we use a zero
76+
// bpf filter(match no packet), then empty out any packets that arrived
77+
// before the “zero-BPF” filter was applied. At this point the socket is
78+
// definitely empty and it can’t fill up with junk because the zero-BPF
79+
// is in place. Then we replace the zero-BPF with the real BPF we want.
80+
if err = eth.SetBPF(zeroRawInst); err != nil {
5981
return nil, err
6082
}
6183
if err = eth.SetCaptureLength(maxSnapshotBytes); err != nil {
6284
return nil, err
6385
}
6486

6587
packetSource := gopacket.NewPacketSource(eth, layers.LinkTypeEthernet, gopacket.WithNoCopy(true))
66-
return packetSource.PacketsCtx(ctx), nil
67-
88+
packetCh := packetSource.PacketsCtx(ctx)
89+
// Drain the channel
90+
for {
91+
select {
92+
case <-ctx.Done():
93+
return nil, ctx.Err()
94+
case <-packetCh:
95+
klog.V(5).InfoS("Found irrelevant packet, discard it", "device", device)
96+
break
97+
case <-time.After(50 * time.Millisecond):
98+
// timeout: channel is drained so socket is drained
99+
// install the correct BPF filter
100+
if err := eth.SetBPF(rawInst); err != nil {
101+
return nil, err
102+
}
103+
return packetCh, nil
104+
}
105+
}
68106
}

0 commit comments

Comments
 (0)