Skip to content

Commit

Permalink
move bpf to netutils
Browse files Browse the repository at this point in the history
  • Loading branch information
dmachard committed Apr 21, 2024
1 parent 70aa2dd commit c268659
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 85 deletions.
89 changes: 4 additions & 85 deletions collectors/sniffer_afpacket_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"os"
"syscall"
"time"
"unsafe"

"github.com/dmachard/go-dnscollector/dnsutils"
"github.com/dmachard/go-dnscollector/netutils"
Expand All @@ -21,88 +20,8 @@ import (
"github.com/dmachard/go-logger"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"golang.org/x/net/bpf"
"golang.org/x/sys/unix"
)

// Convert a uint16 to host byte order (big endian)
func Htons(v uint16) int {
return int((v << 8) | (v >> 8))
}

func GetBpfFilter(port int) []bpf.Instruction {
// bpf filter: (ip or ip6 ) and (udp or tcp) and port 53
// fragmented packets are ignored
var filter = []bpf.Instruction{
// Load eth.type (2 bytes at offset 12) and push-it in register A
bpf.LoadAbsolute{Off: 12, Size: 2},
// if eth.type == IPv4 continue with the next instruction
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x0800, SkipTrue: 0, SkipFalse: 10},
// Load ip.proto (1 byte at offset 23) and push-it in register A
bpf.LoadAbsolute{Off: 23, Size: 1},
// ip.proto == UDP ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x11, SkipTrue: 1, SkipFalse: 0},
// ip.proto == TCP ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x6, SkipTrue: 0, SkipFalse: 16},
// load flags and fragment offset (2 bytes at offset 20) to ignore fragmented packet
bpf.LoadAbsolute{Off: 20, Size: 2},
// Only look at the last 13 bits of the data saved in regiter A
// 0x1fff == 0001 1111 1111 1111 (fragment offset)
// If any of the data in fragment offset is true, ignore the packet
bpf.JumpIf{Cond: bpf.JumpBitsSet, Val: 0x1fff, SkipTrue: 14, SkipFalse: 0},
// Load ip.length
// Register X = ip header len * 4
bpf.LoadMemShift{Off: 14},
// Load source port in tcp or udp (2 bytes at offset x+14)
bpf.LoadIndirect{Off: 14, Size: 2},
// source port equal to 53 ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 10, SkipFalse: 0},
// Load destination port in tcp or udp (2 bytes at offset x+16)
bpf.LoadIndirect{Off: 16, Size: 2},
// destination port equal to 53 ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 8, SkipFalse: 9},
// if eth.type == IPv6 continue with the next instruction
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x86dd, SkipTrue: 0, SkipFalse: 8},
// Load ipv6.nxt (2 bytes at offset 12) and push-it in register A
bpf.LoadAbsolute{Off: 20, Size: 1},
// ip.proto == UDP ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x11, SkipTrue: 1, SkipFalse: 0},
// ip.proto == TCP ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x6, SkipTrue: 0, SkipFalse: 5},
// Load source port tcp or udp (2 bytes at offset 54)
bpf.LoadAbsolute{Off: 54, Size: 2},
// source port equal to 53 ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 2, SkipFalse: 0},
// Load destination port tcp or udp (2 bytes at offset 56)
bpf.LoadAbsolute{Off: 56, Size: 2},
// destination port equal to 53 ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 0, SkipFalse: 1},
// Keep the packet and send up to 65k of the packet to userspace
bpf.RetConstant{Val: 0xFFFF},
// Ignore packet
bpf.RetConstant{Val: 0},
}
return filter
}

func ApplyBpfFilter(filter []bpf.Instruction, fd int) (err error) {
var assembled []bpf.RawInstruction
if assembled, err = bpf.Assemble(filter); err != nil {
return err
}

prog := &unix.SockFprog{
Len: uint16(len(assembled)),
Filter: (*unix.SockFilter)(unsafe.Pointer(&assembled[0])),
}

return unix.SetsockoptSockFprog(fd, syscall.SOL_SOCKET, syscall.SO_ATTACH_FILTER, prog)
}

func RemoveBpfFilter(fd int) (err error) {
return syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_DETACH_FILTER, 0)
}

type AfpacketSniffer struct {
*pkgutils.Collector
fd int
Expand All @@ -116,7 +35,7 @@ func NewAfpacketSniffer(next []pkgutils.Worker, config *pkgconfig.Config, logger

func (c *AfpacketSniffer) Listen() error {
// raw socket
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, Htons(syscall.ETH_P_ALL))
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, netutils.Htons(syscall.ETH_P_ALL))
if err != nil {
return err
}
Expand Down Expand Up @@ -145,8 +64,8 @@ func (c *AfpacketSniffer) Listen() error {
return err
}

filter := GetBpfFilter(c.GetConfig().Collectors.AfpacketLiveCapture.Port)
err = ApplyBpfFilter(filter, fd)
filter := netutils.GetBpfFilter(c.GetConfig().Collectors.AfpacketLiveCapture.Port)
err = netutils.ApplyBpfFilter(filter, fd)
if err != nil {
return err
}
Expand Down Expand Up @@ -196,7 +115,7 @@ func (c *AfpacketSniffer) Run() {
go func(ctx context.Context) {
defer func() {
dnsProcessor.Stop()
RemoveBpfFilter(c.fd)
netutils.RemoveBpfFilter(c.fd)
syscall.Close(c.fd)
c.LogInfo("read data terminated")
defer close(done)
Expand Down
87 changes: 87 additions & 0 deletions netutils/bpf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package netutils

import (
"syscall"
"unsafe"

"golang.org/x/net/bpf"
"golang.org/x/sys/unix"
)

// Convert a uint16 to host byte order (big endian)
func Htons(v uint16) int {
return int((v << 8) | (v >> 8))
}

func GetBpfFilter(port int) []bpf.Instruction {
// bpf filter: (ip or ip6 ) and (udp or tcp) and port 53
// fragmented packets are ignored
var filter = []bpf.Instruction{
// Load eth.type (2 bytes at offset 12) and push-it in register A
bpf.LoadAbsolute{Off: 12, Size: 2},
// if eth.type == IPv4 continue with the next instruction
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x0800, SkipTrue: 0, SkipFalse: 10},
// Load ip.proto (1 byte at offset 23) and push-it in register A
bpf.LoadAbsolute{Off: 23, Size: 1},
// ip.proto == UDP ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x11, SkipTrue: 1, SkipFalse: 0},
// ip.proto == TCP ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x6, SkipTrue: 0, SkipFalse: 16},
// load flags and fragment offset (2 bytes at offset 20) to ignore fragmented packet
bpf.LoadAbsolute{Off: 20, Size: 2},
// Only look at the last 13 bits of the data saved in regiter A
// 0x1fff == 0001 1111 1111 1111 (fragment offset)
// If any of the data in fragment offset is true, ignore the packet
bpf.JumpIf{Cond: bpf.JumpBitsSet, Val: 0x1fff, SkipTrue: 14, SkipFalse: 0},
// Load ip.length
// Register X = ip header len * 4
bpf.LoadMemShift{Off: 14},
// Load source port in tcp or udp (2 bytes at offset x+14)
bpf.LoadIndirect{Off: 14, Size: 2},
// source port equal to 53 ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 10, SkipFalse: 0},
// Load destination port in tcp or udp (2 bytes at offset x+16)
bpf.LoadIndirect{Off: 16, Size: 2},
// destination port equal to 53 ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 8, SkipFalse: 9},
// if eth.type == IPv6 continue with the next instruction
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x86dd, SkipTrue: 0, SkipFalse: 8},
// Load ipv6.nxt (2 bytes at offset 12) and push-it in register A
bpf.LoadAbsolute{Off: 20, Size: 1},
// ip.proto == UDP ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x11, SkipTrue: 1, SkipFalse: 0},
// ip.proto == TCP ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0x6, SkipTrue: 0, SkipFalse: 5},
// Load source port tcp or udp (2 bytes at offset 54)
bpf.LoadAbsolute{Off: 54, Size: 2},
// source port equal to 53 ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 2, SkipFalse: 0},
// Load destination port tcp or udp (2 bytes at offset 56)
bpf.LoadAbsolute{Off: 56, Size: 2},
// destination port equal to 53 ?
bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(port), SkipTrue: 0, SkipFalse: 1},
// Keep the packet and send up to 65k of the packet to userspace
bpf.RetConstant{Val: 0xFFFF},
// Ignore packet
bpf.RetConstant{Val: 0},
}
return filter
}

func ApplyBpfFilter(filter []bpf.Instruction, fd int) (err error) {
var assembled []bpf.RawInstruction
if assembled, err = bpf.Assemble(filter); err != nil {
return err
}

prog := &unix.SockFprog{

Check failure on line 77 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-freebsd (1.21)

undefined: unix.SockFprog

Check failure on line 77 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-freebsd (1.22)

undefined: unix.SockFprog

Check failure on line 77 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-macos (1.22)

undefined: unix.SockFprog

Check failure on line 77 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-win (1.22)

undefined: unix.SockFprog
Len: uint16(len(assembled)),
Filter: (*unix.SockFilter)(unsafe.Pointer(&assembled[0])),

Check failure on line 79 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-freebsd (1.21)

undefined: unix.SockFilter

Check failure on line 79 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-freebsd (1.22)

undefined: unix.SockFilter

Check failure on line 79 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-macos (1.22)

undefined: unix.SockFilter

Check failure on line 79 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-win (1.22)

undefined: unix.SockFilter
}

return unix.SetsockoptSockFprog(fd, syscall.SOL_SOCKET, syscall.SO_ATTACH_FILTER, prog)

Check failure on line 82 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-freebsd (1.21)

undefined: unix.SetsockoptSockFprog

Check failure on line 82 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-freebsd (1.21)

undefined: syscall.SO_ATTACH_FILTER

Check failure on line 82 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-freebsd (1.22)

undefined: unix.SetsockoptSockFprog

Check failure on line 82 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-freebsd (1.22)

undefined: syscall.SO_ATTACH_FILTER

Check failure on line 82 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-macos (1.22)

undefined: unix.SetsockoptSockFprog

Check failure on line 82 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-macos (1.22)

undefined: syscall.SO_ATTACH_FILTER

Check failure on line 82 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-win (1.22)

undefined: unix.SetsockoptSockFprog

Check failure on line 82 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-win (1.22)

undefined: syscall.SO_ATTACH_FILTER
}

func RemoveBpfFilter(fd int) (err error) {
return syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_DETACH_FILTER, 0)

Check failure on line 86 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-freebsd (1.21)

undefined: syscall.SO_DETACH_FILTER

Check failure on line 86 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-freebsd (1.22)

undefined: syscall.SO_DETACH_FILTER

Check failure on line 86 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-macos (1.22)

undefined: syscall.SO_DETACH_FILTER

Check failure on line 86 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-win (1.22)

cannot use fd (variable of type int) as syscall.Handle value in argument to syscall.SetsockoptInt

Check failure on line 86 in netutils/bpf.go

View workflow job for this annotation

GitHub Actions / go-win (1.22)

undefined: syscall.SO_DETACH_FILTER
}

0 comments on commit c268659

Please sign in to comment.