Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automated cherry pick of #6821: Fix packetcapture bpf filter issue (#6815) (#6821) #6827

Open
wants to merge 1 commit into
base: release-2.2
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions pkg/agent/packetcapture/capture/pcap_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package capture
import (
"context"
"net"
"time"

"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
Expand All @@ -39,6 +40,12 @@ func NewPcapCapture() (*pcapCapture, error) {
return &pcapCapture{}, nil
}

// zeroFilter is a filter that will drop all packets.
// see: https://github.com/antrea-io/antrea/issues/6815 for the user case.
func zeroFilter() []bpf.Instruction {
return []bpf.Instruction{returnDrop}
}

func (p *pcapCapture) Capture(ctx context.Context, device string, srcIP, dstIP net.IP, packet *crdv1alpha1.Packet) (chan gopacket.Packet, error) {
// Compile the BPF filter in advance to reduce the time window between starting the capture and applying the filter.
inst := compilePacketFilter(packet, srcIP, dstIP)
Expand All @@ -48,21 +55,52 @@ func (p *pcapCapture) Capture(ctx context.Context, device string, srcIP, dstIP n
return nil, err
}

zeroRawInst, err := bpf.Assemble(zeroFilter())
if err != nil {
return nil, err
}

eth, err := pcapgo.NewEthernetHandle(device)
if err != nil {
return nil, err
}
if err = eth.SetPromiscuous(false); err != nil {
return nil, err
}
if err = eth.SetBPF(rawInst); err != nil {
// Install a BPF filter that won't match any packets
// see: https://natanyellin.com/posts/ebpf-filtering-done-right/.
// Packets which don’t match the target BPF can be received after the socket
// is created and before setsockopt is called. Those packets will remain
// in the socket’s buffer even after the BPF is applied and will later
// be transferred to the application via recv. Here we use a zero
// bpf filter(match no packet), then empty 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.
if err = eth.SetBPF(zeroRawInst); err != nil {
return nil, err
}
if err = eth.SetCaptureLength(maxSnapshotBytes); err != nil {
return nil, err
}

packetSource := gopacket.NewPacketSource(eth, layers.LinkTypeEthernet, gopacket.WithNoCopy(true))
return packetSource.PacketsCtx(ctx), nil

packetCh := packetSource.PacketsCtx(ctx)
// Drain the channel
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-packetCh:
klog.V(5).InfoS("Found irrelevant packet, discard it", "device", device)
break
case <-time.After(50 * time.Millisecond):
// timeout: channel is drained so socket is drained
// install the correct BPF filter
if err := eth.SetBPF(rawInst); err != nil {
return nil, err
}
return packetCh, nil
}
}
}
Loading