Skip to content
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
20 changes: 12 additions & 8 deletions capture/afpacket/socket/socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"golang.org/x/sys/unix"
)

var setPacketMembership = unix.SetsockoptPacketMreq

const (
TPacketVersion = unix.TPACKET_V3 // TPacketVersion : The TPacket header version to use
)
Expand Down Expand Up @@ -97,14 +99,7 @@ func (sd FileDescriptor) SetSocketOptions(iface *link.Link, snapLen int, options

// If the source is in promiscuous mode, set the required flag
if options.Promiscuous {
mReq := unix.PacketMreq{
Ifindex: int32(iface.Index),
Type: unix.PACKET_MR_PROMISC,
}
// #nosec: G103
reqLen := unsafe.Sizeof(mReq)
// #nosec: G103
if err := setsockopt(sd, unix.SOL_SOCKET, unix.PACKET_ADD_MEMBERSHIP, unsafe.Pointer(&mReq), uintptr(unsafe.Pointer(&reqLen))); err != nil {
if err := setPromiscuousMode(sd, iface); err != nil {
return fmt.Errorf("failed to set promiscuous mode: %w", err)
}
}
Expand Down Expand Up @@ -137,6 +132,15 @@ func (sd FileDescriptor) SetSocketOptions(iface *link.Link, snapLen int, options
return nil
}

func setPromiscuousMode(sd FileDescriptor, iface *link.Link) error {
mReq := unix.PacketMreq{
Ifindex: int32(iface.Index),
Type: unix.PACKET_MR_PROMISC,
}

return setPacketMembership(int(sd), unix.SOL_PACKET, unix.PACKET_ADD_MEMBERSHIP, &mReq)
}

// SetupRingBuffer peforms a call via setsockopt() to prepare a mmmap'ed ring buffer
func (sd FileDescriptor) SetupRingBuffer(val unsafe.Pointer, vallen uintptr) error {
return setsockopt(sd, unix.SOL_PACKET, unix.PACKET_RX_RING, val, vallen)
Expand Down
40 changes: 40 additions & 0 deletions capture/afpacket/socket/socket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
package socket

import (
"errors"
"sync"
"testing"

"github.com/fako1024/gotools/link"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)

const nPolls = 1000000
Expand Down Expand Up @@ -65,3 +68,40 @@ func TestPacketCounter(t *testing.T) {

require.Nil(t, sock.Close())
}

func TestSetPromiscuousModeUsesPacketSocketOptions(t *testing.T) {
origSetPacketMembership := setPacketMembership
t.Cleanup(func() {
setPacketMembership = origSetPacketMembership
})

var called bool
setPacketMembership = func(fd, level, opt int, mreq *unix.PacketMreq) error {
called = true
require.Equal(t, 1234, fd)
require.Equal(t, unix.SOL_PACKET, level)
require.Equal(t, unix.PACKET_ADD_MEMBERSHIP, opt)
require.EqualValues(t, 42, mreq.Ifindex)
require.EqualValues(t, unix.PACKET_MR_PROMISC, mreq.Type)
return nil
}

err := setPromiscuousMode(FileDescriptor(1234), &link.Link{Index: 42})
require.Nil(t, err)
require.True(t, called)
}

func TestSetPromiscuousModePropagatesError(t *testing.T) {
origSetPacketMembership := setPacketMembership
t.Cleanup(func() {
setPacketMembership = origSetPacketMembership
})

expectedErr := errors.New("membership failure")
setPacketMembership = func(fd, level, opt int, mreq *unix.PacketMreq) error {
return expectedErr
}

err := setPromiscuousMode(FileDescriptor(1234), &link.Link{Index: 42})
require.ErrorIs(t, err, expectedErr)
}
3 changes: 2 additions & 1 deletion examples/dump/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ func main() {
ignoreVLANs bool
)

logger, logErr := logging.New(logging.LevelInfo, logging.EncodingPlain)
logger, logShutdownFn, logErr := logging.New(logging.LevelInfo, logging.EncodingPlain)
if logErr != nil {
fmt.Fprintf(os.Stderr, "failed to instantiate CLI logger: %v\n", logErr)
os.Exit(1)
}
defer logShutdownFn()

flag.StringVar(&devName, "d", "", "device / interface to capture on")
flag.IntVar(&maxPkts, "n", 10, "maximum number of packets to capture")
Expand Down
22 changes: 7 additions & 15 deletions examples/pcap/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ package main
import (
"errors"
"flag"
"fmt"
"io"
"os"

"github.com/els0r/telemetry/logging"
"github.com/fako1024/slimcap/capture/pcap"
Expand All @@ -23,44 +21,38 @@ func main() {
raw bool
)

logger, logErr := logging.New(logging.LevelInfo, logging.EncodingPlain)
if logErr != nil {
fmt.Fprintf(os.Stderr, "failed to instantiate CLI logger: %v\n", logErr)
os.Exit(1)
}

flag.StringVar(&fileName, "f", "", "pcap file to read from")
flag.IntVar(&maxPkts, "n", 10, "maximum number of packets to process")
flag.BoolVar(&raw, "r", false, "output raw packet information")
flag.Parse()
if fileName == "" {
logger.Fatalf("no input file specified (-f)")
logging.Logger().Fatalf("no input file specified (-f)")
}

listener, err := pcap.NewSourceFromFile(fileName)
if err != nil {
logger.Fatalf("failed to start pcap reader for `%s`: %s", fileName, err)
logging.Logger().Fatalf("failed to start pcap reader for `%s`: %s", fileName, err)
}

defer func() {
if err := listener.Close(); err != nil {
logger.Fatalf("failed to close listener on `%s`: %s", fileName, err)
logging.Logger().Fatalf("failed to close listener on `%s`: %s", fileName, err)
}
}()

logger.Infof("Reading up to %d packets from `%s` (link type: %d)...", maxPkts, fileName, listener.Link().Type)
logging.Logger().Infof("Reading up to %d packets from `%s` (link type: %d)...", maxPkts, fileName, listener.Link().Type)
for i := 0; i < maxPkts; i++ {
p, err := listener.NextPacket(nil)
if err != nil {
if errors.Is(err, io.EOF) {
return
}
logger.Fatalf("error during packet reading (copy operation) on `%s`: %s", fileName, err)
logging.Logger().Fatalf("error during packet reading (copy operation) on `%s`: %s", fileName, err)
}
if raw {
logger.Infof("Read packet with Payload (total len %d): %v", p.TotalLen(), p.Payload())
logging.Logger().Infof("Read packet with Payload (total len %d): %v", p.TotalLen(), p.Payload())
} else {
logger.Infof("Read packet (total len %d): %v", p.TotalLen(), p.IPLayer().String())
logging.Logger().Infof("Read packet (total len %d): %v", p.TotalLen(), p.IPLayer().String())
}
}
}
11 changes: 0 additions & 11 deletions examples/trace/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ package main

import (
"flag"
"fmt"
"os"
"strings"

"github.com/els0r/telemetry/logging"
)

// Definitions for command line option names
Expand Down Expand Up @@ -63,13 +59,6 @@ func ParseConfig() (cfg Config) {
cfg.Ifaces = parseList(rawIfaces)
cfg.SkipIfaces = parseList(rawSkipIfaces)

var logErr error
logger, logErr = logging.New(logging.LevelFromString(cfg.LogLevel), logging.EncodingPlain)
if logErr != nil {
fmt.Fprintf(os.Stderr, "failed to instantiate CLI logger: %v\n", logErr)
os.Exit(1)
}

return
}

Expand Down
4 changes: 1 addition & 3 deletions examples/trace/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (
"github.com/els0r/telemetry/logging"
)

var logger *logging.L

func main() {

cfg := ParseConfig()
Expand All @@ -25,6 +23,6 @@ func main() {
WithCPUProfiling(cfg.CPUProfileOutput).
WithMemProfiling(cfg.MemProfileOutput).
Run(); err != nil {
logger.Fatalf("critical error during capture: %s", err)
logging.Logger().Fatalf("critical error during capture: %s", err)
}
}
44 changes: 22 additions & 22 deletions examples/trace/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
"os"
"os/signal"
"runtime/pprof"
"slices"
"sort"
"strings"
"sync"

"github.com/els0r/telemetry/logging"
"github.com/fako1024/gotools/link"
"github.com/fako1024/slimcap/capture"
"github.com/fako1024/slimcap/capture/afpacket/afpacket"
Expand Down Expand Up @@ -44,8 +46,8 @@ func (c *Capture) SkipIfaces(ifaces []string) *Capture {
}

// MaxIfaceErrors sets the maximum number of errors allowed to occur before capture is terminated
func (c *Capture) MaxIfaceErrors(max int) *Capture {
c.maxIfaceErrors = max
func (c *Capture) MaxIfaceErrors(maxErrs int) *Capture {
c.maxIfaceErrors = maxErrs
return c
}

Expand Down Expand Up @@ -111,7 +113,7 @@ func (c *Capture) Run() (err error) {
return err
}
for _, iface := range links {
logger.Infof("Found interface `%s` (idx %d), link type %d", iface.Name, iface.Index, iface.Type)
logging.Logger().Infof("Found interface `%s` (idx %d), link type %d", iface.Name, iface.Index, iface.Type)
}

// construct list of skipped interfaces
Expand Down Expand Up @@ -156,13 +158,13 @@ func (c *Capture) Run() (err error) {
return skipped[i] < skipped[j]
})
if len(skipped) > 0 {
logger.Warnf("skipping capture on interfaces [%s]", strings.Join(skipped, ","))
logging.Logger().Warnf("skipping capture on interfaces [%s]", strings.Join(skipped, ","))
}

sort.Slice(capturing, func(i, j int) bool {
return capturing[i] < capturing[j]
})
logger.Infof("attempting capture on interfaces [%s]", strings.Join(capturing, ","))
logging.Logger().Infof("attempting capture on interfaces [%s]", strings.Join(capturing, ","))

sigExitChan := make(chan os.Signal, 1)
signal.Notify(sigExitChan, unix.SIGTERM, os.Interrupt)
Expand All @@ -175,47 +177,45 @@ func (c *Capture) Run() (err error) {
go func(l *link.Link) {
defer wg.Done()

for _, skipLink := range skipped {
if l.Name == skipLink {
return
}
if slices.Contains(skipped, l.Name) {
return
}

if isUp, err := l.IsUp(); err != nil || !isUp {
logger.Warnf("skipping listener on non-up interface `%s`", l.Name)
logging.Logger().Warnf("skipping listener on non-up interface `%s`", l.Name)
return
}

var listener capture.Source
if c.useRingBuffer {
listener, err = afring.NewSourceFromLink(l, afring.CaptureLength(filter.CaptureLengthMinimalIPv6Transport))
if err != nil {
logger.Errorf("error starting listener (with ring buffer) on `%s`: %s", l.Name, err)
logging.Logger().Errorf("error starting listener (with ring buffer) on `%s`: %s", l.Name, err)
}
} else {
listener, err = afpacket.NewSourceFromLink(l)
if err != nil {
logger.Errorf("error starting listener (no ring buffer) on `%s`: %s", l.Name, err)
logging.Logger().Errorf("error starting listener (no ring buffer) on `%s`: %s", l.Name, err)
}
}
listeners = append(listeners, listener)

var nErr int
if c.useZeroCopy {
for {
if err := listener.NextPacketFn(func(payload []byte, totalLen uint32, pktType byte, ipLayerOffset byte) error {
if err := listener.NextPacketFn(func(payload []byte, _ uint32, pktType byte, ipLayerOffset byte) error {
if c.logPacketPayload {
logger.Infof("[%s] Got %v / %d", l.Name, payload[ipLayerOffset:ipLayerOffset+16], pktType)
logging.Logger().Infof("[%s] Got %v / %d", l.Name, payload[ipLayerOffset:ipLayerOffset+16], pktType)
}
return nil
}); err != nil {
if errors.Is(err, capture.ErrCaptureStopped) {
logger.Infof("gracefully stopped capture on `%s`", l.Name)
logging.Logger().Infof("gracefully stopped capture on `%s`", l.Name)
return
}
nErr++
if nErr >= c.maxIfaceErrors {
logger.Errorf("too many errors (%d) on `%s`, stopping capture", nErr, l.Name)
logging.Logger().Errorf("too many errors (%d) on `%s`, stopping capture", nErr, l.Name)
}
}
}
Expand All @@ -224,17 +224,17 @@ func (c *Capture) Run() (err error) {
pkt, err := listener.NextPacket(nil)
if err != nil {
if errors.Is(err, capture.ErrCaptureStopped) {
logger.Infof("gracefully stopped capture on `%s`", l.Name)
logging.Logger().Infof("gracefully stopped capture on `%s`", l.Name)
return
}
nErr++
if nErr >= c.maxIfaceErrors {
logger.Errorf("too many errors (%d) on `%s`, stopping capture", nErr, l.Name)
logging.Logger().Errorf("too many errors (%d) on `%s`, stopping capture", nErr, l.Name)
}
}

if c.logPacketPayload {
logger.Infof("[%s] Got IP layer %v / %d", l.Name, pkt.IPLayer()[:16], pkt.Type())
logging.Logger().Infof("[%s] Got IP layer %v / %d", l.Name, pkt.IPLayer()[:16], pkt.Type())
}
}
}
Expand All @@ -249,12 +249,12 @@ func (c *Capture) Run() (err error) {
for _, listener := range listeners {
stats, err := listener.Stats()
if err != nil {
logger.Errorf("failed to retrieve socket stats: %s", err)
logging.Logger().Errorf("failed to retrieve socket stats: %s", err)
}
logger.Infof("Packet stats for %s: %#v", listener.Link().Name, stats)
logging.Logger().Infof("Packet stats for %s: %#v", listener.Link().Name, stats)

if err := listener.Close(); err != nil {
logger.Errorf("failed to gracefully stop capture: %s", err)
logging.Logger().Errorf("failed to gracefully stop capture: %s", err)
}
}
}()
Expand Down
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ module github.com/fako1024/slimcap
go 1.25.0

require (
github.com/els0r/telemetry/logging v0.0.0-20241007081432-5f966df95bbd
github.com/fako1024/gotools/link v0.0.0-20250828105752-6dffddfeea78
github.com/els0r/telemetry/logging v0.0.0-20260406010724-0c813ed6284d
github.com/fako1024/gotools/link v0.0.0-20260108133916-d42cb4e89f05
github.com/stretchr/testify v1.11.1
golang.org/x/net v0.47.0
golang.org/x/sys v0.38.0
golang.org/x/net v0.53.0
golang.org/x/sys v0.43.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading
Loading