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

[bugfix] Replace custom direction filter for loopback interfaces with BPF filter #76

Merged
merged 2 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
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
15 changes: 0 additions & 15 deletions capture/afpacket/afpacket/afpacket.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,6 @@ retry:
return fmt.Errorf("error receiving next packet from socket: %w", err)
}

// Apply filter (if any)
if filter := s.link.FilterMask(); filter > 0 && filter&pktType != 0 {
goto retry
}

totalLen, err := s.determineTotalPktLen(s.buf)
if err != nil {
return err
Expand Down Expand Up @@ -309,11 +304,6 @@ retry:
if err != nil {
return -1, fmt.Errorf("error receiving next packet from socket: %w", err)
}

// Apply filter (if any)
if filter := s.link.FilterMask(); filter > 0 && filter&pktType != 0 {
goto retry
}
data[0] = pktType

totalLen, err := s.determineTotalPktLen(data[6:])
Expand Down Expand Up @@ -358,11 +348,6 @@ retry:
return -1, capture.PacketUnknown, 0, fmt.Errorf("error receiving next packet from socket: %w", err)
}

// Apply filter (if any)
if filter := s.link.FilterMask(); filter > 0 && filter&pktType != 0 {
goto retry
}

totalLen, err := s.determineTotalPktLen(data)
if err != nil {
return -1, 0, 0, err
Expand Down
12 changes: 1 addition & 11 deletions capture/afpacket/afring/afring.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ type Source struct {
link *link.Link

ipLayerOffsetNum uint32
filter byte

ringBuffer
sync.Mutex
Expand Down Expand Up @@ -76,7 +75,6 @@ func NewSourceFromLink(link *link.Link, options ...Option) (*Source, error) {
nBlocks: tPacketDefaultBlockNr,
ipLayerOffset: link.Type.IPHeaderOffset(),
link: link,
filter: link.FilterMask(),
}
src.ipLayerOffsetNum = uint32(src.ipLayerOffset)

Expand Down Expand Up @@ -336,7 +334,6 @@ func (s *Source) Link() *link.Link {
// one (fetching its first packet).
func (s *Source) nextPacket() error {

retry:
pktHdr := s.curTPacketHeader

// If there is an active block, attempt to simply consume a packet from it
Expand All @@ -351,7 +348,7 @@ retry:

// Update position of next packet and jump to the end
pktHdr.ppos += nextPos
goto finalize
return nil
}

// If there is no next offset, release the TPacketHeader to the kernel and move on to the next block
Expand Down Expand Up @@ -391,13 +388,6 @@ retry:
// Set the position of the first packet in this block and jump to end
pktHdr.ppos = pktHdr.offsetToFirstPkt()

finalize:

// Apply filter (if any)
if s.filter > 0 && s.filter&pktHdr.data[pktHdr.ppos+58] != 0 {
goto retry
}

return nil
}

Expand Down
13 changes: 0 additions & 13 deletions capture/afpacket/afring/afring_zerocopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
// one (fetching / returning its first packet).
func (s *Source) NextPayloadZeroCopy() (payload []byte, pktType capture.PacketType, pktLen uint32, err error) {

retry:
pktHdr := s.curTPacketHeader

// If there is an active block, attempt to simply consume a packet from it
Expand Down Expand Up @@ -72,11 +71,6 @@ retry:

finalize:

// Apply filter (if any)
if s.filter > 0 && s.filter&pktHdr.data[pktHdr.ppos+58] != 0 {
goto retry
}

// Parse the V3 TPacketHeader and the first byte of the payload
hdr := pktHdr.parseHeader()
pos := pktHdr.ppos + uint32(hdr.pktMac)
Expand All @@ -85,7 +79,6 @@ finalize:
return unsafe.Slice(&pktHdr.data[pos], hdr.snaplen),
pktHdr.data[pktHdr.ppos+58],
hdr.pktLen, nil

}

// NextIPPacketZeroCopy receives the IP layer of the next packet from the source and returns it. The operation is blocking.
Expand All @@ -94,7 +87,6 @@ finalize:
// one (fetching / returning its first packet IP layer).
func (s *Source) NextIPPacketZeroCopy() (ipLayer capture.IPLayer, pktType capture.PacketType, pktLen uint32, err error) {

retry:
pktHdr := s.curTPacketHeader

// If there is an active block, attempt to simply consume a packet from it
Expand Down Expand Up @@ -153,11 +145,6 @@ retry:

finalize:

// Apply filter (if any)
if s.filter > 0 && s.filter&pktHdr.data[pktHdr.ppos+58] != 0 {
goto retry
}

// Parse the V3 TPacketHeader and the first byte of the payload
hdr := pktHdr.parseHeader()
pos := pktHdr.ppos + uint32(hdr.pktNet)
Expand Down
18 changes: 18 additions & 0 deletions link/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ import "golang.org/x/net/bpf"

const defaultSnapLen = 262144

var bpfInstructionsLinkTypeLoopback = func(snapLen int) []bpf.RawInstruction {
if snapLen == 0 {
snapLen = defaultSnapLen
}

// LinkTypeLoopback
// not outbound && (ether proto 0x0800 || ether proto 0x86DD)
return []bpf.RawInstruction{
{Op: 0x28, Jt: 0x0, Jf: 0x0, K: 0xfffff004},
{Op: 0x15, Jt: 0x4, Jf: 0x0, K: 0x4},
{Op: 0x28, Jt: 0x0, Jf: 0x0, K: 0xc},
{Op: 0x15, Jt: 0x1, Jf: 0x0, K: 0x800},
{Op: 0x15, Jt: 0x0, Jf: 0x1, K: 0x86dd},
{Op: 0x6, Jt: 0x0, Jf: 0x0, K: uint32(snapLen)},
{Op: 0x6, Jt: 0x0, Jf: 0x0, K: 0x0},
}
}

var bpfInstructionsLinkTypeEther = func(snapLen int) []bpf.RawInstruction {
if snapLen == 0 {
snapLen = defaultSnapLen
Expand Down
35 changes: 4 additions & 31 deletions link/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ func (l Type) IPHeaderOffset() byte {
// BPFFilter returns the link / interface specific raw BPF instructions to filter for valid packets only
func (l Type) BPFFilter() func(snapLen int) []bpf.RawInstruction {
switch l {
case TypeEthernet,
TypeLoopback:
case TypeEthernet:
fako1024 marked this conversation as resolved.
Show resolved Hide resolved
return bpfInstructionsLinkTypeEther
case TypeLoopback:
return bpfInstructionsLinkTypeLoopback
case TypePPP,
TypeIP6IP6,
TypeGRE,
Expand All @@ -110,16 +111,6 @@ func (l Type) BPFFilter() func(snapLen int) []bpf.RawInstruction {
// Link denotes a link, i.e. an interface (wrapped) and its link type
type Link struct {
Interface

filterMask byte
}

// WithPacketFilterMask sets / enables a packet type filter (masking) that will be applied
// during capture
func WithPacketFilterMask(mask byte) func(l *Link) {
return func(l *Link) {
l.filterMask |= mask
}
}

// New instantiates a new link / interface
Expand Down Expand Up @@ -151,8 +142,7 @@ func New(ifName string, opts ...func(*Link)) (link *Link, err error) {
}

link = &Link{
Interface: iface,
filterMask: calculateInitialFilterMask(iface),
Interface: iface,
}

// Apply functional options, if any
Expand All @@ -168,11 +158,6 @@ func (l *Link) IsUp() (bool, error) {
return l.Interface.IsUp()
}

// FilterMask returns the packet type filter for this link / interface
func (l *Link) FilterMask() byte {
return l.filterMask
}

// FindAllLinks retrieves all system network interfaces and their link type
func FindAllLinks() ([]*Link, error) {

Expand All @@ -192,15 +177,3 @@ func FindAllLinks() ([]*Link, error) {

return links, err
}

func calculateInitialFilterMask(i Interface) (mask byte) {

// Loopback devices will show all packets twice (for obvious reasons). In order to avoid
// this duplication, a default filter is applied, rejecting all outbound packets (this is
// in line with best practices, e.g. handling by libpcap).
if i.Type == TypeLoopback {
mask |= 4 // capture.PacketOutgoing
}

return
}
2 changes: 1 addition & 1 deletion link/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func TestLink_BPFFilter(t *testing.T) {
name: "Test Loopback link BPF Filter Function",
l: TypeLoopback,
wantFunc: func(snapLen int) []bpf.RawInstruction {
return bpfInstructionsLinkTypeEther(snapLen)
return bpfInstructionsLinkTypeLoopback(snapLen)
},
},
{
Expand Down