Skip to content

Commit

Permalink
Add ability to change the ICMP ID and seq counter
Browse files Browse the repository at this point in the history
  • Loading branch information
foogod authored and corny committed Apr 6, 2024
1 parent bc50d4a commit 07811e5
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 28 deletions.
20 changes: 11 additions & 9 deletions pinger.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,20 @@ const (
ProtocolICMPv6 = 58
)

// sequence number for this process
// default sequence counter for this process
var sequence uint32

// Pinger is a instance for ICMP echo requests
type Pinger struct {
LogUnexpectedPackets bool // increases log verbosity
Id uint16
SequenceCounter *uint32

payload Payload
payloadMu sync.RWMutex

requests map[uint16]request // currently running requests
requests map[uint32]request // currently running requests
mtx sync.RWMutex // lock for the requests map
id uint16
conn4 net.PacketConn
conn6 net.PacketConn
write4 sync.Mutex // lock for conn4.WriteTo
Expand Down Expand Up @@ -60,10 +61,11 @@ func New(bind4, bind6 string) (*Pinger, error) {
}

pinger := Pinger{
conn4: conn4,
conn6: conn6,
id: uint16(os.Getpid()),
requests: make(map[uint16]request),
conn4: conn4,
conn6: conn6,
Id: uint16(os.Getpid()),
SequenceCounter: &sequence,
requests: make(map[uint32]request),
}
pinger.SetPayloadSize(56)

Expand Down Expand Up @@ -101,9 +103,9 @@ func (pinger *Pinger) close(conn net.PacketConn) {
}
}

func (pinger *Pinger) removeRequest(seq uint16) {
func (pinger *Pinger) removeRequest(idseq uint32) {
pinger.mtx.Lock()
delete(pinger.requests, seq)
delete(pinger.requests, idseq)
pinger.mtx.Unlock()
}

Expand Down
9 changes: 3 additions & 6 deletions receiving.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,14 @@ func (pinger *Pinger) process(body icmp.MessageBody, result error, addr net.IP,
return
}

// check if we sent this
if uint16(echo.ID) != pinger.id {
return
}
idseq := (uint32(uint16(echo.ID)) << 16) | uint32(uint16(echo.Seq))

// search for existing running echo request
pinger.mtx.Lock()
req := pinger.requests[uint16(echo.Seq)]
req := pinger.requests[idseq]
if _, ok := req.(*simpleRequest); ok {
// a simpleRequest is finished on the first reply
delete(pinger.requests, uint16(echo.Seq))
delete(pinger.requests, idseq)
}
pinger.mtx.Unlock()

Expand Down
29 changes: 16 additions & 13 deletions sending.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (pinger *Pinger) Ping(destination *net.IPAddr, timeout time.Duration) (time
func (pinger *Pinger) PingContext(ctx context.Context, destination *net.IPAddr) (time.Duration, error) {
req := simpleRequest{}

seq, err := pinger.sendRequest(destination, &req)
idseq, err := pinger.sendRequest(destination, &req)
if err != nil {
return 0, err
}
Expand All @@ -54,7 +54,7 @@ func (pinger *Pinger) PingContext(ctx context.Context, destination *net.IPAddr)
err = req.result
case <-ctx.Done():
// dequeue request
pinger.removeRequest(seq)
pinger.removeRequest(idseq)
err = &timeoutError{}
}

Expand All @@ -77,7 +77,7 @@ func (pinger *Pinger) PingMulticast(destination *net.IPAddr, wait time.Duration)
func (pinger *Pinger) PingMulticastContext(ctx context.Context, destination *net.IPAddr) (<-chan Reply, error) {
req := multiRequest{}

seq, err := pinger.sendRequest(destination, &req)
idseq, err := pinger.sendRequest(destination, &req)
if err != nil {
return nil, err
}
Expand All @@ -86,7 +86,7 @@ func (pinger *Pinger) PingMulticastContext(ctx context.Context, destination *net
<-ctx.Done()

// dequeue request
pinger.removeRequest(seq)
pinger.removeRequest(idseq)

req.close()
}()
Expand All @@ -95,9 +95,12 @@ func (pinger *Pinger) PingMulticastContext(ctx context.Context, destination *net
}

// sendRequest marshals the payload and sends the packet.
// It returns the sequence number and an error if the sending failed.
func (pinger *Pinger) sendRequest(destination *net.IPAddr, req request) (uint16, error) {
seq := uint16(atomic.AddUint32(&sequence, 1))
// It returns the combined id+sequence number and an error if the sending failed.
func (pinger *Pinger) sendRequest(destination *net.IPAddr, req request) (uint32, error) {
id := uint16(pinger.Id)
seq := uint16(atomic.AddUint32(pinger.SequenceCounter, 1))

idseq := (uint32(id) << 16) | uint32(seq)

pinger.payloadMu.RLock()
defer pinger.payloadMu.RUnlock()
Expand All @@ -106,7 +109,7 @@ func (pinger *Pinger) sendRequest(destination *net.IPAddr, req request) (uint16,
wm := icmp.Message{
Code: 0,
Body: &icmp.Echo{
ID: int(pinger.id),
ID: int(id),
Seq: int(seq),
Data: pinger.payload,
},
Expand All @@ -128,12 +131,12 @@ func (pinger *Pinger) sendRequest(destination *net.IPAddr, req request) (uint16,
// serialize packet
wb, err := wm.Marshal(nil)
if err != nil {
return seq, err
return idseq, err
}

// enqueue in currently running requests
pinger.mtx.Lock()
pinger.requests[seq] = req
pinger.requests[idseq] = req
pinger.mtx.Unlock()

// start measurement (tStop is set in the receiving end)
Expand All @@ -147,10 +150,10 @@ func (pinger *Pinger) sendRequest(destination *net.IPAddr, req request) (uint16,
// send failed, need to remove request from list
if err != nil {
req.close()
pinger.removeRequest(seq)
pinger.removeRequest(idseq)

return 0, err
return idseq, err
}

return seq, nil
return idseq, nil
}

0 comments on commit 07811e5

Please sign in to comment.