Skip to content

Commit

Permalink
Introduce per hop sleep
Browse files Browse the repository at this point in the history
Signed-off-by: Tim Foerster <tim.foerster@hetzner.de>
  • Loading branch information
tonobo committed Dec 19, 2017
1 parent 2306e81 commit 99845c4
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 49 deletions.
12 changes: 5 additions & 7 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var (
COUNT = 5
TIMEOUT = 800 * time.Millisecond
INTERVAL = 100 * time.Millisecond
HOP_SLEEP = time.Nanosecond
MAX_HOPS = 64
RING_BUFFER_SIZE = 50
jsonFmt = false
Expand All @@ -27,16 +28,14 @@ var RootCmd = &cobra.Command{
if len(args) != 1 {
return errors.New("No target provided")
}
m, ch := NewMTR(args[0], TIMEOUT, INTERVAL)
m, ch := NewMTR(args[0], TIMEOUT, INTERVAL, HOP_SLEEP)
if jsonFmt {
go func(ch chan struct{}) {
for {
<-ch
}
}(ch)
for i := 0; i < COUNT; i++ {
m.Run(ch)
}
m.Run(ch, COUNT)
s, _ := pj.Marshal(m)
fmt.Println(string(s))
return nil
Expand All @@ -52,9 +51,7 @@ var RootCmd = &cobra.Command{
mu.Unlock()
}
}(ch)
for i := 0; i < COUNT; i++ {
m.Run(ch)
}
m.Run(ch, COUNT)
close(ch)
mu.Lock()
render(m)
Expand All @@ -73,6 +70,7 @@ func init() {
RootCmd.Flags().IntVarP(&COUNT, "count", "c", COUNT, "Amount of pings per target")
RootCmd.Flags().DurationVarP(&TIMEOUT, "timeout", "t", TIMEOUT, "ICMP reply timeout")
RootCmd.Flags().DurationVarP(&INTERVAL, "interval", "i", INTERVAL, "Wait time between icmp packets before sending new one")
RootCmd.Flags().DurationVar(&HOP_SLEEP, "hop-sleep", HOP_SLEEP, "Wait time between pinging next hop")
RootCmd.Flags().IntVar(&MAX_HOPS, "max-hops", MAX_HOPS, "Maximal TTL count")
RootCmd.Flags().IntVar(&RING_BUFFER_SIZE, "buffer-size", RING_BUFFER_SIZE, "Cached packet buffer size")
RootCmd.Flags().BoolVar(&jsonFmt, "json", jsonFmt, "Print json results")
Expand Down
26 changes: 25 additions & 1 deletion hop.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import (
"container/ring"
"encoding/json"
"fmt"
"net"
"time"

gm "github.com/buger/goterm"
)

type HopStatistic struct {
dest *net.IPAddr
timeout time.Duration
pid int
Sent int
TTL int
Target string
Expand All @@ -26,8 +30,28 @@ type packet struct {
ResponseTime float64 `json:"respond_ms"`
}

func (h *HopStatistic) MarshalJSON() ([]byte, error) {
func (s *HopStatistic) Next() {
r, _ := Icmp("0.0.0.0", s.dest, s.TTL, s.pid, s.timeout)
s.Packets = s.Packets.Prev()
s.Packets.Value = r
if s.Target == "" {
s.Target = r.Addr
}
s.Sent++
s.SumElapsed = r.Elapsed + s.SumElapsed
if !r.Success {
s.Lost++
}
s.Last = r
if s.Best.Elapsed > r.Elapsed {
s.Best = r
}
if s.Worst.Elapsed < r.Elapsed {
s.Worst = r
}
}

func (h *HopStatistic) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Sent int `json:"sent"`
Target string `json:"target"`
Expand Down
86 changes: 45 additions & 41 deletions mtr.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,57 +15,40 @@ type MTR struct {
mutex *sync.RWMutex
timeout time.Duration
interval time.Duration
Address string `json:"destination"`
Address string `json:"destination"`
hopsleep time.Duration
Statistic map[int]*HopStatistic `json:"statistic"`
}

func NewMTR(addr string, timeout time.Duration, interval time.Duration) (*MTR, chan struct{}) {
func NewMTR(addr string, timeout time.Duration, interval time.Duration, hopsleep time.Duration) (*MTR, chan struct{}) {
return &MTR{
interval: interval,
timeout: timeout,
hopsleep: hopsleep,
Address: addr,
mutex: &sync.RWMutex{},
Statistic: map[int]*HopStatistic{},
}, make(chan struct{})
}

func (m *MTR) registerStatistic(ttl int, r ICMPReturn) {
if m.Statistic[ttl] == nil {
m.Statistic[ttl] = &HopStatistic{
Sent: 1,
TTL: ttl,
Target: r.Addr,
Last: r,
Best: r,
Worst: r,
Lost: 0,
SumElapsed: r.Elapsed,
Packets: ring.New(RING_BUFFER_SIZE),
}
if !r.Success {
m.Statistic[ttl].Lost++
}
m.Statistic[ttl].Packets.Value = r
return
func (m *MTR) registerStatistic(ttl int, r ICMPReturn) *HopStatistic {
m.Statistic[ttl] = &HopStatistic{
Sent: 1,
TTL: ttl,
Target: r.Addr,
timeout: m.timeout,
Last: r,
Best: r,
Worst: r,
Lost: 0,
SumElapsed: r.Elapsed,
Packets: ring.New(RING_BUFFER_SIZE),
}
s := m.Statistic[ttl]
s.Packets = s.Packets.Prev()
s.Packets.Value = r
if s.Target == "" {
s.Target = r.Addr
}
s.Sent++
s.SumElapsed = r.Elapsed + s.SumElapsed
if !r.Success {
m.Statistic[ttl].Lost++
}
s.Last = r
if s.Best.Elapsed > r.Elapsed {
s.Best = r
}
if s.Worst.Elapsed < r.Elapsed {
s.Worst = r
}
m.Statistic[ttl].Packets.Value = r
return m.Statistic[ttl]
}

func (m *MTR) Render(offset int) {
Expand All @@ -81,28 +64,49 @@ func (m *MTR) Render(offset int) {
return
}

func (m *MTR) Run(ch chan struct{}) {
func (m *MTR) ping(ch chan struct{}, count int) {
for i := 0; i < count; i++ {
time.Sleep(m.interval)
for i := 1; i <= len(m.Statistic); i++ {
time.Sleep(m.hopsleep)
m.mutex.RLock()
m.Statistic[i].Next()
m.mutex.RUnlock()
ch <- struct{}{}
}
}
}

func (m *MTR) Run(ch chan struct{}, count int) {
m.discover(ch)
m.ping(ch, count-1)
}

func (m *MTR) discover(ch chan struct{}) {
ipAddr := net.IPAddr{IP: net.ParseIP(m.Address)}
pid := os.Getpid() & 0xffff
ttlDoubleBump := false

for ttl := 1; ttl < 64; ttl++ {
time.Sleep(m.interval)
for ttl := 1; ttl < MAX_HOPS; ttl++ {
time.Sleep(m.hopsleep)
hopReturn, err := Icmp("0.0.0.0", &ipAddr, ttl, pid, m.timeout)
if err != nil || !hopReturn.Success {
if ttlDoubleBump {
break
}
m.mutex.Lock()
m.registerStatistic(ttl, hopReturn)
s := m.registerStatistic(ttl, hopReturn)
s.dest = &ipAddr
s.pid = pid
m.mutex.Unlock()
ch <- struct{}{}
ttlDoubleBump = true
continue
}
ttlDoubleBump = false
m.mutex.Lock()
m.registerStatistic(ttl, hopReturn)
s := m.registerStatistic(ttl, hopReturn)
s.dest = &ipAddr
s.pid = pid
m.mutex.Unlock()
ch <- struct{}{}
if hopReturn.Addr == m.Address {
Expand Down

0 comments on commit 99845c4

Please sign in to comment.