icmpengine

package module
v1.0.2-0...-8684d3e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 23, 2022 License: MIT Imports: 19 Imported by: 0

README

ICMPEngine

ICMPengine is a small library for sending non-privilged ICMP echo requests and recieving replies.

Key features include:

Although this is designed to be used as a library, a basic implmentation is demonstrated here: ./cmd/icmpengine/icmpengine.go

sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"
xtcp diagram

Dependency licenses

Dependancy License Link
Golang BSD https://golang.org/LICENSE
github.com/go-cmd/cmd v1.3.0 MIT https://github.com/go-cmd/cmd/blob/master/LICENSE
github.com/go-kit/kit v0.10.0 MIT https://github.com/go-kit/kit/blob/master/LICENSE
github.com/hashicorp/go-hclog v0.16.2 MIT https://github.com/hashicorp/go-hclog/blob/master/LICENSE
github.com/pkg/profile v1.6.0 BSD https://github.com/pkg/profile/blob/master/LICENSE
github.com/prometheus/client_golang v1.11.0 Apache 2.0 https://github.com/prometheus/client_golang/blob/master/LICENSE
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 BSD https://golang.org/LICENSE
inet.af/netaddr v0.0.0-20210721214506-ce7a8ad02cc1 BSD-3 https://pkg.go.dev/inet.af/netaddr?tab=licenses
das@das-dell5580:~/go/src/gitlab.edgio.net/dseddon/icmpengine$ cat go.mod 
module gitlab.edgio.net/dseddon/icmpengine

go 1.16

require (
	github.com/go-cmd/cmd v1.3.0
	github.com/go-kit/kit v0.10.0 // indirect
	github.com/hashicorp/go-hclog v0.16.2
	github.com/pkg/profile v1.6.0
	github.com/prometheus/client_golang v1.11.0
	golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
	inet.af/netaddr v0.0.0-20210721214506-ce7a8ad02cc1
)

How to tag

git tag
git tag -a v1.0.1 -m "v1.0.1"
git push origin --tags

Documentation

Index

Constants

View Source
const (
	//"golang.org/x/net/internal/iana"
	ProtocolICMP     = 1  // Internet Control Message
	ProtocolIPv6ICMP = 58 // ICMP for IPv6

	Receivers4Cst         = 2
	Receivers6Cst         = 2
	OpenSocketsRetriesCst = 2

	SplayReceiversCst = true

	IEdebugLevel = 111
)
View Source
const (
	PingerFractionModulo = 10

	PdebugLevel = 111
)
View Source
const (
	//ReceiveBufferMax = 1500 // max packet receive size
	ReceiveBufferMax = 200 // max packet receive size

	RdebugLevel = 111
)
View Source
const (
	EdebugLevel = 111
)
View Source
const IsRaceEnabled = false

Enabled reports if the race detector is enabled.

View Source
const (
	SdebugLevel = 111
)

Variables

This section is empty.

Functions

func FakeDrop

func FakeDrop(dropProb float64) (drop bool)

FakeDrop is a simple function to return true based on a probability Looking at this issue, I'm not sure if this is perfect, but should be ok https://github.com/golang/go/issues/12290

func WriteTo

func WriteTo(wb []byte, addr *net.UDPAddr, socket *icmp.PacketConn, debugLevel int, logger hclog.Logger)

WriteTo performs the socket write, and does error handling

Types

type DebugLevelsT

type DebugLevelsT struct {
	IE int
	S  int
	R  int
	E  int
	P  int
}

func GetDebugLevels

func GetDebugLevels(debuglevel int) (debugLevels DebugLevelsT)

GetDebugLevels is a little helper function to return DebugLevelsT filled in with the same debug level for each component

type ExpirersT

type ExpirersT struct {
	WG          sync.WaitGroup
	DoneCh      chan struct{}
	DonesChs    map[Protocol]chan struct{}
	Runnings    map[Protocol]bool
	Running     bool
	DebugLevel  int
	FakeSuccess bool
}

type ICMPEchoReply

type ICMPEchoReply struct {
	Type       uint8
	Code       uint8
	Checksum   uint16
	Identifier uint16
	Seq        uint16
}

ICMPEchoReply represents Echo Reply messages per the IPv4/rfc792 and IPv6/rfc2463 ( see extended comments below )

func ParseICMPEchoReply

func ParseICMPEchoReply(b []byte) (*ICMPEchoReply, error)

ParseICMPEchoReply parses the ICMP echo reply messages This was originally based on on the the golang standard icmp ParseMessage, which for unknown reasons don't parse ICMP echo https://pkg.go.dev/golang.org/x/net/icmp#ParseMessage https://github.com/golang/net/blob/7fd8e65b6420/icmp/message.go#L139

func ParseICMPEchoReplyBB

func ParseICMPEchoReplyBB(b bytes.Buffer) (*ICMPEchoReply, error)

ParseICMPEchoReplyBB is the same as ParseICMPEchoReply, except uses bytes.Buffer, instead of []byte This is mostly to allow use of sync.Pool, which should be faster (maybe?) https://www.akshaydeo.com/blog/2017/12/23/How-did-I-improve-latency-by-700-percent-using-syncPool/

type ICMPEngine

type ICMPEngine struct {
	Log hclog.Logger
	sync.RWMutex
	Timeout      time.Duration
	ReadDeadline time.Duration
	Protocols    []Protocol
	PID          int
	EID          int
	DoneCh       chan struct{}
	Sockets      SocketsT
	Receivers    ReceiversT
	Expirers     ExpirersT
	Pingers      PingersT
	DebugLevel   int
}

ICMPEngine holds the object state Most of this data is for tracking ICMP echo requests sent, and their expiry times The double linked-list (DLL) allows tracking the next Expiry time, while allowing entries to be removed efficently when a ping is recieved. Leveraging https://golang.org/pkg/container/list/ Need to move to https://golang.org/pkg/container/heap/

func New

func New(l hclog.Logger, done chan struct{}, to time.Duration, rd time.Duration, start bool) (icmpEngine *ICMPEngine)

New creates ICMPEngine with default Receivers Per Protocol

func NewFullConfig

func NewFullConfig(logger hclog.Logger, done chan struct{}, timeout time.Duration, deadline time.Duration, start bool, receivers4 int, receivers6 int, SplayReceivers bool, debugLevels DebugLevelsT, fakeSuccess bool) (icmpEngine *ICMPEngine)

NewFullConfig creates ICMPEngine with the full set of configuration options Please note could icmpEngine.Start() It is recommended NOT to actually start until you really need ICMPengine listening for incoming packets e.g. You can defer opening the sockets, and starting the receivers until you actually need them

func (*ICMPEngine) CheckExpirerIsRunning

func (ie *ICMPEngine) CheckExpirerIsRunning() (started bool)

CheckExpirerIsRunning checks Expirers is running, and starts it if required returns if Expirers was started CheckExpirerIsRunning assumes the LOCK is already held by Pinger

func (*ICMPEngine) CloseSockets

func (ie *ICMPEngine) CloseSockets()

CloseSockets() closes the sockets with some assertion checks

func (*ICMPEngine) ExpirerConfig

func (ie *ICMPEngine) ExpirerConfig(FakeSuccess bool)

Expirer tracks the ICMP echo timeouts The idea is to just have the single and nearest timer running at any single moment The "Config" implies that we can configure the FakeSuccess, which is used for testing

func (*ICMPEngine) HackSysctl

func (ie *ICMPEngine) HackSysctl() (success bool)

HackSysctl does sysctl -w net.ipv4.ping_group_range=0 2147483647 This requires root

func (*ICMPEngine) OpenDoneChannels

func (ie *ICMPEngine) OpenDoneChannels(fakeSuccess bool)

OpenDoneChannels opens the main done channel for each worker type

func (*ICMPEngine) OpenSockets

func (ie *ICMPEngine) OpenSockets()

OpenSockets opens non-privleged ICMP sockets for sending echo requests/replies OpenSockets has retry logic, and can use HackSysctl to change the sysctl for the non-privleged ICMP sockets if ICMPEngine is running as root Hopefully ICMPEngine is not running as root, in which case, if it can't open the sockets, it will log fatal

func (*ICMPEngine) Pinger

func (ie *ICMPEngine) Pinger(IP netaddr.IP, packets Sequence, interval time.Duration, sortRTTs bool, DoneCh chan struct{}) (results PingerResults)

Pinger calls PingerConfig with: - zero (0) probability of drop, - no fake success

func (*ICMPEngine) PingerConfig

func (ie *ICMPEngine) PingerConfig(IP netaddr.IP, packets Sequence, interval time.Duration, sortRTTs bool, DoneCh chan struct{}, dropProb float64) (results PingerResults)

Welford's math stolen from https://pkg.go.dev/github.com/eclesh/welford Welford's one-pass algorithm for computing the mean and variance of a set of numbers. For more information see Knuth (TAOCP Vol 2, 3rd ed, pg 232)

func (*ICMPEngine) PingerWithStatsChannel

func (ie *ICMPEngine) PingerWithStatsChannel(IP netaddr.IP, packets Sequence, interval time.Duration, sortRTTs bool, DoneCh chan struct{}, wg *sync.WaitGroup, pingerResultsCh chan<- PingerResults)

PingerWithStatsChannel is the Pinger which sends stats on the output channel, rather than returning the values

func (*ICMPEngine) Receiver

func (ie *ICMPEngine) Receiver(proto Protocol, index int, allDone <-chan struct{}, done <-chan struct{})

Receiver receives ICMP messages, calculates the round-trip-time(RTT) and then send the response to the requesting Pinger Receiver is also responsible for tracking the timeouts, using the double-linked-list and map ie.ReadDeadline is used to not just block forever on the read call, so we can check the Done channel has been called When choosing the ReadDeadline, it's just changing how quickly the Receiver might detect the Done signal

Because ReadFrom syscall is blocking, a SetReadDeadline is used to allow ReadFrom to finish, this is mostly to allow checking for the done signal, and therefore allow closing down the Receivers gracefully. There is [Timeouts In A Row] code that increases these timeouts gradually, to decrease the ReadFrom thrashing

func (*ICMPEngine) Run

func (ie *ICMPEngine) Run(wg *sync.WaitGroup)

func (*ICMPEngine) Start

func (ie *ICMPEngine) Start()

Start OpenSockets and starts the Receivers, with default to splay the receiver start times This isn't done on New or NewRPP, to avoid opening the sockets and having the Receivers busy making recieve syscalls until ICMPEngine really needs to be running. e.g. ICMPEngine "object" can be created once, but not actually running much until Start() is called This is possibly an premature optimization.

func (*ICMPEngine) StartReceiversSplay

func (ie *ICMPEngine) StartReceiversSplay()

StartReceiversSplay starts the receivers, with some sanity checking Splay the receiver start times, means this will essentailly offset the start time of the receivers, but this slows down the startup time

func (*ICMPEngine) Stop

func (ie *ICMPEngine) Stop(fakeSuccess bool)

Stop gracefully stops the workers

type PingExpired

type PingExpired struct {
	Seq  Sequence
	Send time.Time
}

PingExpired is passed from the Expirer to the Pingers This only happens when there is a timeout (obviously)

type PingSuccess

type PingSuccess struct {
	Seq      Sequence
	Send     time.Time
	Received time.Time
	RTT      time.Duration
}

PingSuccess is passed from the Receivers to the Pingers

type PingerResults

type PingerResults struct {
	IP             netaddr.IP
	Successes      int
	Failures       int
	OutOfOrder     int
	RTTs           []time.Duration
	Count          int
	Min            time.Duration
	Max            time.Duration
	Mean           time.Duration
	Variance       time.Duration
	Sum            time.Duration
	PingerDuration time.Duration
}

type PingersT

type PingersT struct {
	WG         sync.WaitGroup
	DoneCh     chan struct{}
	Pings      map[netaddr.IP]map[Sequence]*list.Element
	ExpiresDLL *list.List
	SuccessChs map[netaddr.IP]chan PingSuccess
	ExpiredChs map[netaddr.IP]chan PingExpired
	DonesChs   map[netaddr.IP]chan struct{}
	DebugLevel int
}

type Pings

type Pings struct {
	NetaddrIP netaddr.IP
	Seq       Sequence
	Send      time.Time
	Expiry    time.Time
	FakeDrop  bool
}

type Protocol

type Protocol uint8

type ReceiversT

type ReceiversT struct {
	WG         sync.WaitGroup
	DoneCh     chan struct{}
	DoneChs    map[Protocol]chan struct{}
	Counts     map[Protocol]int
	Splay      bool
	Runnings   map[Protocol]bool
	Running    bool
	DebugLevel int
}

type Sequence

type Sequence uint16

type SocketsT

type SocketsT struct {
	Open       bool
	Opens      map[Protocol]bool
	Networks   map[Protocol]string
	Addresses  map[Protocol]string
	Sockets    map[Protocol]*icmp.PacketConn
	DebugLevel int
}

type WorkerType

type WorkerType rune

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL