From 7c9cf9b7912c71d6e6c6adf325452ff2529b569e Mon Sep 17 00:00:00 2001 From: Sergey Galkin Date: Sat, 4 Feb 2023 21:56:32 -0700 Subject: [PATCH 1/3] bsdbpf: new methods SetBPFFilter() to set BPF programm, WritePacketData() to write data, SeeSent configurable option to turn on/off returning local packets. Fixes BPFSniffer creation with bpf device name provided over config options and also fixes fd leak in the case of any subsequent error during BPFSniffer instance creation. --- bsdbpf/bsd_bpf_sniffer.go | 76 +++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/bsdbpf/bsd_bpf_sniffer.go b/bsdbpf/bsd_bpf_sniffer.go index 3e1da0b91..5acc8b8be 100644 --- a/bsdbpf/bsd_bpf_sniffer.go +++ b/bsdbpf/bsd_bpf_sniffer.go @@ -4,6 +4,7 @@ // that can be found in the LICENSE file in the root of the source // tree. +//go:build darwin || dragonfly || freebsd || netbsd || openbsd // +build darwin dragonfly freebsd netbsd openbsd package bsdbpf @@ -11,6 +12,7 @@ package bsdbpf import ( "errors" "fmt" + "github.com/google/gopacket/pcap" "syscall" "time" "unsafe" @@ -22,7 +24,7 @@ import ( const wordSize = int(unsafe.Sizeof(uintptr(0))) func bpfWordAlign(x int) int { - return (((x) + (wordSize - 1)) &^ (wordSize - 1)) + return ((x) + (wordSize - 1)) &^ (wordSize - 1) } // Options is used to configure various properties of the BPF sniffer. @@ -58,6 +60,9 @@ type Options struct { // as provided, to the wire. // The default is true. PreserveLinkAddr bool + // SeeSent is set to false if locally generated packets on the interface should not be returned by BPF. + // The default is true. + SeeSent bool } var defaultOptions = Options{ @@ -67,6 +72,7 @@ var defaultOptions = Options{ Promisc: true, Immediate: true, PreserveLinkAddr: true, + SeeSent: true, } // BPFSniffer is a struct used to track state of a BSD BPF ethernet sniffer @@ -97,34 +103,32 @@ func NewBPFSniffer(iface string, options *Options) (*BPFSniffer, error) { sniffer.options = options } - if sniffer.options.BPFDeviceName == "" { - sniffer.pickBpfDevice() - } + sniffer.pickBpfDevice() // setup our read buffer if sniffer.options.ReadBufLen == 0 { sniffer.options.ReadBufLen, err = syscall.BpfBuflen(sniffer.fd) if err != nil { - return nil, err + goto err } } else { sniffer.options.ReadBufLen, err = syscall.SetBpfBuflen(sniffer.fd, sniffer.options.ReadBufLen) if err != nil { - return nil, err + goto err } } sniffer.readBuffer = make([]byte, sniffer.options.ReadBufLen) err = syscall.SetBpfInterface(sniffer.fd, sniffer.sniffDeviceName) if err != nil { - return nil, err + goto err } if sniffer.options.Immediate { // turn immediate mode on. This makes the snffer non-blocking. err = syscall.SetBpfImmediate(sniffer.fd, enable) if err != nil { - return nil, err + goto err } } @@ -134,7 +138,7 @@ func NewBPFSniffer(iface string, options *Options) (*BPFSniffer, error) { if sniffer.options.Timeout != nil { err = syscall.SetBpfTimeout(sniffer.fd, sniffer.options.Timeout) if err != nil { - return nil, err + goto err } } @@ -143,7 +147,7 @@ func NewBPFSniffer(iface string, options *Options) (*BPFSniffer, error) { // higher level protocol analyzers will not need this err = syscall.SetBpfHeadercmpl(sniffer.fd, enable) if err != nil { - return nil, err + goto err } } @@ -151,11 +155,22 @@ func NewBPFSniffer(iface string, options *Options) (*BPFSniffer, error) { // forces the interface into promiscuous mode err = syscall.SetBpfPromisc(sniffer.fd, enable) if err != nil { - return nil, err + goto err + } + } + + if !sniffer.options.SeeSent { + // See sent is set by default, need to turn it off + err = unix.IoctlSetInt(sniffer.fd, syscall.BIOCSSEESENT, 0) + if err != nil { + goto err } } return &sniffer, nil +err: + syscall.Close(sniffer.fd) + return nil, err } // Close is used to close the file-descriptor of the BPF device file. @@ -165,13 +180,26 @@ func (b *BPFSniffer) Close() error { func (b *BPFSniffer) pickBpfDevice() { var err error - b.options.BPFDeviceName = "" - for i := 0; i < 99; i++ { - b.options.BPFDeviceName = fmt.Sprintf("/dev/bpf%d", i) + if len(b.options.BPFDeviceName) > 0 { b.fd, err = syscall.Open(b.options.BPFDeviceName, syscall.O_RDWR, 0) if err == nil { return } + } else { + b.options.BPFDeviceName = "" + for i := 0; i < 99; i++ { + name := fmt.Sprintf("/dev/bpf%d", i) + b.fd, err = syscall.Open(b.options.BPFDeviceName, syscall.O_RDWR, 0) + if err == nil { + b.options.BPFDeviceName = name + return + } + + if err == syscall.Errno(syscall.ENOENT) { + // No such file, no needs to iterate further + break + } + } } panic("failed to acquire a BPF device for read-write access") } @@ -213,3 +241,23 @@ func (b *BPFSniffer) ReadPacketData() ([]byte, gopacket.CaptureInfo, error) { func (b *BPFSniffer) GetReadBufLen() int { return b.options.ReadBufLen } + +func (s *BPFSniffer) SetBPFFilter(bpfInstructions []pcap.BPFInstruction) error { + bpfIns := make([]syscall.BpfInsn, 0, len(bpfInstructions)) + for _, ins := range bpfInstructions { + sysIns := syscall.BpfInsn{ + Code: ins.Code, + Jt: ins.Jt, + Jf: ins.Jf, + K: ins.K, + } + bpfIns = append(bpfIns, sysIns) + } + + return syscall.SetBpf(s.fd, bpfIns) +} + +func (h *BPFSniffer) WritePacketData(pkt []byte) error { + _, err := unix.Write(h.fd, pkt) + return err +} From 728f71cbfee4b9a69393d444dbe3c3c7614272ef Mon Sep 17 00:00:00 2001 From: srgg Date: Sun, 23 Jul 2023 11:02:18 -0600 Subject: [PATCH 2/3] Update bsdbpf/bsd_bpf_sniffer.go Co-authored-by: Mark Dietzer --- bsdbpf/bsd_bpf_sniffer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bsdbpf/bsd_bpf_sniffer.go b/bsdbpf/bsd_bpf_sniffer.go index 5acc8b8be..54c1836e5 100644 --- a/bsdbpf/bsd_bpf_sniffer.go +++ b/bsdbpf/bsd_bpf_sniffer.go @@ -189,7 +189,7 @@ func (b *BPFSniffer) pickBpfDevice() { b.options.BPFDeviceName = "" for i := 0; i < 99; i++ { name := fmt.Sprintf("/dev/bpf%d", i) - b.fd, err = syscall.Open(b.options.BPFDeviceName, syscall.O_RDWR, 0) + b.fd, err = syscall.Open(name, syscall.O_RDWR, 0) if err == nil { b.options.BPFDeviceName = name return From 6069a8f930a5eb9f773b828050c0378f6ecf4d1e Mon Sep 17 00:00:00 2001 From: srgg Date: Sun, 23 Jul 2023 11:39:02 -0600 Subject: [PATCH 3/3] Update bsdbpf/bsd_bpf_sniffer.go Co-authored-by: Mark Dietzer --- bsdbpf/bsd_bpf_sniffer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bsdbpf/bsd_bpf_sniffer.go b/bsdbpf/bsd_bpf_sniffer.go index 54c1836e5..7ba940586 100644 --- a/bsdbpf/bsd_bpf_sniffer.go +++ b/bsdbpf/bsd_bpf_sniffer.go @@ -161,7 +161,7 @@ func NewBPFSniffer(iface string, options *Options) (*BPFSniffer, error) { if !sniffer.options.SeeSent { // See sent is set by default, need to turn it off - err = unix.IoctlSetInt(sniffer.fd, syscall.BIOCSSEESENT, 0) + err = unix.IoctlSetPointerInt(sniffer.fd, syscall.BIOCSSEESENT, 0) if err != nil { goto err }