Skip to content

Commit

Permalink
Drastically improve performance
Browse files Browse the repository at this point in the history
  • Loading branch information
robgonnella committed Dec 26, 2023
1 parent f8fd2f0 commit d6796df
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 57 deletions.
2 changes: 0 additions & 2 deletions internal/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,6 @@ OUTER:
}
}

c.scanner.Stop()

c.log.Info().Str("duration", time.Since(start).String()).Msg("go-lanscan complete")

return nil
Expand Down
14 changes: 0 additions & 14 deletions internal/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,6 @@ func TestCore(t *testing.T) {
return nil
})

mockScanner.EXPECT().Stop()

runner.Initialize(
mockScanner,
1,
Expand Down Expand Up @@ -294,8 +292,6 @@ func TestCore(t *testing.T) {
return nil
})

mockScanner.EXPECT().Stop()

runner.Initialize(
mockScanner,
1,
Expand Down Expand Up @@ -346,8 +342,6 @@ func TestCore(t *testing.T) {
return nil
})

mockScanner.EXPECT().Stop()

runner.Initialize(
mockScanner,
1,
Expand Down Expand Up @@ -441,8 +435,6 @@ func TestCore(t *testing.T) {
return nil
})

mockScanner.EXPECT().Stop()

runner.Initialize(
mockScanner,
1,
Expand Down Expand Up @@ -537,8 +529,6 @@ func TestCore(t *testing.T) {
return nil
})

mockScanner.EXPECT().Stop()

runner.Initialize(
mockScanner,
1,
Expand Down Expand Up @@ -611,8 +601,6 @@ func TestCore(t *testing.T) {
return nil
})

mockScanner.EXPECT().Stop()

runner.Initialize(
mockScanner,
1,
Expand Down Expand Up @@ -646,8 +634,6 @@ func TestCore(t *testing.T) {
return nil
})

mockScanner.EXPECT().Stop()

runner.Initialize(
mockScanner,
10,
Expand Down
16 changes: 16 additions & 0 deletions mock/scanner/scanner.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions pkg/internal/test-helper/test-helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func NewArpReplyReadResult(srcIP net.IP, srcHwAddr net.HardwareAddr) (data []byt
DstProtAddress: []byte{192, 168, 1, 1}, // Target IP address
}

var payload gopacket.Payload

buf := gopacket.NewSerializeBuffer()

opts := gopacket.SerializeOptions{
Expand All @@ -36,7 +38,7 @@ func NewArpReplyReadResult(srcIP net.IP, srcHwAddr net.HardwareAddr) (data []byt
}

// Serialize the ARP packet.
gopacket.SerializeLayers(buf, opts, &eth, &arp)
gopacket.SerializeLayers(buf, opts, &eth, &arp, &payload)

return buf.Bytes(), gopacket.CaptureInfo{}, nil
}
Expand All @@ -60,6 +62,8 @@ func NewArpRequestReadResult() (data []byte, ci gopacket.CaptureInfo, err error)
DstProtAddress: []byte{192, 168, 1, 1}, // Target IP address
}

var payload gopacket.Payload

buf := gopacket.NewSerializeBuffer()

opts := gopacket.SerializeOptions{
Expand All @@ -68,7 +72,7 @@ func NewArpRequestReadResult() (data []byte, ci gopacket.CaptureInfo, err error)
}

// Serialize the ARP packet.
gopacket.SerializeLayers(buf, opts, &eth, &arp)
gopacket.SerializeLayers(buf, opts, &eth, &arp, &payload)

return buf.Bytes(), gopacket.CaptureInfo{}, nil
}
Expand Down
56 changes: 45 additions & 11 deletions pkg/scanner/arpscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"

"github.com/robgonnella/go-lanscan/internal/logger"
"github.com/robgonnella/go-lanscan/internal/util"
Expand Down Expand Up @@ -93,7 +94,7 @@ func (s *ArpScanner) Scan() error {
s.networkInfo.Interface().Name,
65536,
true,
s.idleTimeout,
pcap.BlockForever,
)

if err != nil {
Expand Down Expand Up @@ -161,22 +162,55 @@ func (s *ArpScanner) SetPacketCapture(cap PacketCapture) {
}

func (s *ArpScanner) readPackets() {
packetSource := gopacket.NewPacketSource(s.handle, layers.LayerTypeEthernet)
packetSource.DecodeOptions.NoCopy = true
packetSource.DecodeOptions.Lazy = true
stopChan := make(chan struct{})

defer s.reset()
go func() {
for {
select {
case <-stopChan:
return
default:
var eth layers.Ethernet
var arp layers.ARP
var payload gopacket.Payload

parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &arp, &payload)
decoded := []gopacket.LayerType{}
packetData, _, err := s.handle.ReadPacketData()

if err != nil {
s.debug.Error().Err(err).Msg("arp: error reading packet")
continue
}

err = parser.DecodeLayers(packetData, &decoded)

if err != nil {
s.debug.Error().Err(err).Msg("arp: error decoding packet")
continue
}

OUTER:
for _, layerType := range decoded {
switch layerType {
case layers.LayerTypeARP:
go s.handleARPLayer(&arp)
break OUTER
}
}
}
}
}()

defer func() {
stopChan <- struct{}{}
s.reset()
}()

for {
select {
case <-s.ctx.Done():
return
case packet := <-packetSource.Packets():
arpLayer := packet.Layer(layers.LayerTypeARP)

if arpLayer != nil {
go s.handleARPLayer(arpLayer.(*layers.ARP))
}
default:
s.packetSentAtMux.RLock()
packetSentAt := s.lastPacketSentAt
Expand Down
56 changes: 56 additions & 0 deletions pkg/scanner/arpscan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,62 @@ func TestArpScanner(t *testing.T) {
assert.ErrorIs(st, mockErr, err)
})

t.Run("prints debug message if reading packets returns error", func(st *testing.T) {
cap := mock_scanner.NewMockPacketCapture(ctrl)
handle := mock_scanner.NewMockPacketCaptureHandle(ctrl)
mockNetInfo := mock_network.NewMockNetwork(ctrl)

arpScanner := scanner.NewArpScanner(
[]string{},
mockNetInfo,
scanner.WithPacketCapture(cap),
)

wg := sync.WaitGroup{}
wg.Add(1)

mockNetInfo.EXPECT().Interface().AnyTimes().Return(mockInterface)
mockNetInfo.EXPECT().IPNet().Return(mockIPNet).AnyTimes()
mockNetInfo.EXPECT().UserIP().Return(mockUserIP)
mockNetInfo.EXPECT().Cidr().AnyTimes().Return(cidr)

cap.EXPECT().OpenLive(
gomock.Any(),
gomock.Any(),
gomock.Any(),
gomock.Any()).Return(handle, nil)

cap.EXPECT().SerializeLayers(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()

handle.EXPECT().Close().AnyTimes()

handle.EXPECT().WritePacketData(gomock.Any()).DoAndReturn(func(data []byte) (err error) {
defer func() {
arpScanner.Stop()
wg.Done()
}()
return nil
})

firstCall := true
handle.EXPECT().ReadPacketData().AnyTimes().DoAndReturn(func() (data []byte, ci gopacket.CaptureInfo, err error) {
if firstCall {
firstCall = false
return nil, gopacket.CaptureInfo{}, errors.New("mock ReadPacketData error")
}
return test_helper.NewArpReplyReadResult(
mockNonIncludedArpSrcIP,
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
)
})

err := arpScanner.Scan()

wg.Wait()

assert.NoError(st, err)
})

t.Run("performs arp scan on default network info", func(st *testing.T) {
cap := mock_scanner.NewMockPacketCapture(ctrl)
handle := mock_scanner.NewMockPacketCaptureHandle(ctrl)
Expand Down
4 changes: 4 additions & 0 deletions pkg/scanner/fullscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"sync"
"time"

"github.com/robgonnella/go-lanscan/internal/logger"
"github.com/robgonnella/go-lanscan/internal/util"
"github.com/robgonnella/go-lanscan/pkg/network"
"github.com/robgonnella/go-lanscan/pkg/oui"
Expand All @@ -30,6 +31,7 @@ type FullScanner struct {
scanning bool
scanningMux *sync.RWMutex
deviceMux *sync.RWMutex
debug logger.DebugLogger
}

func NewFullScanner(
Expand Down Expand Up @@ -68,6 +70,7 @@ func NewFullScanner(
scanning: false,
scanningMux: &sync.RWMutex{},
deviceMux: &sync.RWMutex{},
debug: logger.NewDebugLogger(),
}

for _, o := range options {
Expand Down Expand Up @@ -142,6 +145,7 @@ func (s *FullScanner) Stop() {
if s.synScanner != nil {
s.synScanner.Stop()
}
s.debug.Info().Msg("all scanners stopped")
}

func (s *FullScanner) SetRequestNotifications(cb func(req *Request)) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/scanner/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
// How long to wait before sending next packet
// the faster you send packets the more packets
// will be missed when reading
const defaultAccuracy = time.Millisecond * 100
const defaultAccuracy = time.Microsecond * 100

type ScannerOption = func(s Scanner)

Expand Down
Loading

0 comments on commit d6796df

Please sign in to comment.