From a73d94c179cea58b993df23b5f25f08c1778a52a Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 24 Aug 2018 17:11:48 +0200 Subject: [PATCH 01/10] Make ping after discovery concurrent --- hop/hop.go | 19 +++++++++--- imcp/icmp.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++-- mtr/mtr.go | 2 +- 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/hop/hop.go b/hop/hop.go index 9568079..d8d3acf 100644 --- a/hop/hop.go +++ b/hop/hop.go @@ -18,6 +18,7 @@ type HopStatistic struct { Sent int TTL int Target string + TargetIP *net.IPAddr Last imcp.ICMPReturn Best imcp.ICMPReturn Worst imcp.ICMPReturn @@ -25,6 +26,7 @@ type HopStatistic struct { Lost int Packets *ring.Ring RingBufferSize int + pingSeq int } type packet struct { @@ -33,12 +35,21 @@ type packet struct { } func (s *HopStatistic) Next() { - r, _ := imcp.SendIMCP("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 + return + } + if s.TargetIP == nil { + addr, err := net.ResolveIPAddr("ip4", s.Target) + if err != nil { + return + } + s.TargetIP = addr } + s.pingSeq++ + r, _ := imcp.SendIMCP("0.0.0.0", s.TargetIP, s.TTL, s.PID, s.Timeout, s.pingSeq) + s.Packets = s.Packets.Prev() + s.Packets.Value = r + s.Sent++ s.SumElapsed = r.Elapsed + s.SumElapsed if !r.Success { diff --git a/imcp/icmp.go b/imcp/icmp.go index 5a3b936..ef44138 100644 --- a/imcp/icmp.go +++ b/imcp/icmp.go @@ -15,8 +15,8 @@ type ICMPReturn struct { Elapsed time.Duration } -// SendIMCP sends a IMCP to a given destination -func SendIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time.Duration) (hop ICMPReturn, err error) { +// SendIMCP sends a IMCP to a given destination but does allow an IMCP timeout +func SendDiscoverIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time.Duration, seq int) (hop ICMPReturn, err error) { hop.Success = false start := time.Now() c, err := icmp.ListenPacket("ip4:icmp", localAddr) @@ -53,3 +53,84 @@ func SendIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time.Duratio hop.Success = true return hop, err } + +// SendIMCP sends a IMCP to a given destination +func SendIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time.Duration, seq int) (hop ICMPReturn, err error) { + hop.Success = false + start := time.Now() + c, err := icmp.ListenPacket("ip4:icmp", localAddr) + if err != nil { + return hop, err + } + defer c.Close() + c.IPv4PacketConn().SetTTL(ttl) + c.SetDeadline(time.Now().Add(timeout)) + wm := icmp.Message{ + Type: ipv4.ICMPTypeEcho, Code: 0, + Body: &icmp.Echo{ + ID: pid, Seq: 0, + Data: []byte("hello"), + }, + } + wb, err := wm.Marshal(nil) + if err != nil { + return hop, err + } + + if _, err := c.WriteTo(wb, dst); err != nil { + return hop, err + } + + _, err = listenForSpecific(time.Now().Add(timeout), dst.String()) + if err != nil { + return hop, err + } + + elapsed := time.Since(start) + hop.Elapsed = elapsed + hop.Addr = dst.String() + hop.Success = true + return hop, err +} + +// listenForSpecific listens for a reply from a specific destination and returns the body if returned +func listenForSpecific(deadline time.Time, neededPeer string) (string, error) { + conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") + if err != nil { + return "", err + } + conn.SetDeadline(deadline) + defer conn.Close() + for { + bytes := make([]byte, 1500) + n, peer, err := conn.ReadFrom(bytes) + if err != nil { + if neterr, ok := err.(*net.OpError); ok { + return "", neterr + } + } + if n == 0 { + continue + } + + if peer.String() != neededPeer { + continue + } + + x, err := icmp.ParseMessage(1, bytes[:n]) + if err != nil { + continue + } + + if x.Type.(ipv4.ICMPType).String() == "time exceeded" { + return "", nil + } + + if x.Type.(ipv4.ICMPType).String() == "echo reply" { + b, _ := x.Body.Marshal(1) + return string(b[:2]), nil + } + + panic(x.Type.(ipv4.ICMPType).String()) + } +} diff --git a/mtr/mtr.go b/mtr/mtr.go index e80cc8f..eac373c 100644 --- a/mtr/mtr.go +++ b/mtr/mtr.go @@ -97,7 +97,7 @@ func (m *MTR) discover(ch chan struct{}) { unknownHopsCount := 0 for ttl := 1; ttl < m.maxHops; ttl++ { time.Sleep(m.hopsleep) - hopReturn, err := imcp.SendIMCP("0.0.0.0", &ipAddr, ttl, pid, m.timeout) + hopReturn, err := imcp.SendDiscoverIMCP("0.0.0.0", &ipAddr, ttl, pid, m.timeout, 1) m.mutex.Lock() s := m.registerStatistic(ttl, hopReturn) From bc70126eafe116783a294593d3909e2bdb3926fe Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 24 Aug 2018 17:33:50 +0200 Subject: [PATCH 02/10] check if echos return the same body --- imcp/icmp.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/imcp/icmp.go b/imcp/icmp.go index ef44138..3c0ecbd 100644 --- a/imcp/icmp.go +++ b/imcp/icmp.go @@ -1,6 +1,7 @@ package imcp import ( + "fmt" "net" "time" @@ -65,11 +66,12 @@ func SendIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time.Duratio defer c.Close() c.IPv4PacketConn().SetTTL(ttl) c.SetDeadline(time.Now().Add(timeout)) + body := fmt.Sprintf("ping%d", seq) wm := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: pid, Seq: 0, - Data: []byte("hello"), + Data: []byte(body), }, } wb, err := wm.Marshal(nil) @@ -81,7 +83,7 @@ func SendIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time.Duratio return hop, err } - _, err = listenForSpecific(time.Now().Add(timeout), dst.String()) + _, err = listenForSpecific(time.Now().Add(timeout), dst.String(), body) if err != nil { return hop, err } @@ -94,7 +96,7 @@ func SendIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time.Duratio } // listenForSpecific listens for a reply from a specific destination and returns the body if returned -func listenForSpecific(deadline time.Time, neededPeer string) (string, error) { +func listenForSpecific(deadline time.Time, neededPeer, neededBody string) (string, error) { conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { return "", err @@ -128,7 +130,10 @@ func listenForSpecific(deadline time.Time, neededPeer string) (string, error) { if x.Type.(ipv4.ICMPType).String() == "echo reply" { b, _ := x.Body.Marshal(1) - return string(b[:2]), nil + if string(b[4:]) != neededBody { + continue + } + return string(b[4:]), nil } panic(x.Type.(ipv4.ICMPType).String()) From d7b025b177df3784765b64380d9ebec622423ec9 Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 24 Aug 2018 17:36:02 +0200 Subject: [PATCH 03/10] add todo item --- imcp/icmp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imcp/icmp.go b/imcp/icmp.go index 3c0ecbd..22e1ef3 100644 --- a/imcp/icmp.go +++ b/imcp/icmp.go @@ -125,7 +125,7 @@ func listenForSpecific(deadline time.Time, neededPeer, neededBody string) (strin } if x.Type.(ipv4.ICMPType).String() == "time exceeded" { - return "", nil + return "", nil //TODO: add a check for sequences here } if x.Type.(ipv4.ICMPType).String() == "echo reply" { From 112a1934104db965ae8da7ad8e94d48657f71b34 Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 24 Aug 2018 17:48:00 +0200 Subject: [PATCH 04/10] remove TTL from normal pings --- hop/hop.go | 2 +- imcp/icmp.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hop/hop.go b/hop/hop.go index d8d3acf..ea86aa4 100644 --- a/hop/hop.go +++ b/hop/hop.go @@ -46,7 +46,7 @@ func (s *HopStatistic) Next() { s.TargetIP = addr } s.pingSeq++ - r, _ := imcp.SendIMCP("0.0.0.0", s.TargetIP, s.TTL, s.PID, s.Timeout, s.pingSeq) + r, _ := imcp.SendIMCP("0.0.0.0", s.TargetIP, s.PID, s.Timeout, s.pingSeq) s.Packets = s.Packets.Prev() s.Packets.Value = r diff --git a/imcp/icmp.go b/imcp/icmp.go index 22e1ef3..f6f0d22 100644 --- a/imcp/icmp.go +++ b/imcp/icmp.go @@ -1,6 +1,7 @@ package imcp import ( + "errors" "fmt" "net" "time" @@ -56,7 +57,7 @@ func SendDiscoverIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time } // SendIMCP sends a IMCP to a given destination -func SendIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time.Duration, seq int) (hop ICMPReturn, err error) { +func SendIMCP(localAddr string, dst net.Addr, pid int, timeout time.Duration, seq int) (hop ICMPReturn, err error) { hop.Success = false start := time.Now() c, err := icmp.ListenPacket("ip4:icmp", localAddr) @@ -64,7 +65,6 @@ func SendIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time.Duratio return hop, err } defer c.Close() - c.IPv4PacketConn().SetTTL(ttl) c.SetDeadline(time.Now().Add(timeout)) body := fmt.Sprintf("ping%d", seq) wm := icmp.Message{ @@ -125,7 +125,7 @@ func listenForSpecific(deadline time.Time, neededPeer, neededBody string) (strin } if x.Type.(ipv4.ICMPType).String() == "time exceeded" { - return "", nil //TODO: add a check for sequences here + return "", errors.New("time exceeded") } if x.Type.(ipv4.ICMPType).String() == "echo reply" { From c6f9f56d2a9fde414949a9796964e5b1331c1367 Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 24 Aug 2018 17:57:16 +0200 Subject: [PATCH 05/10] re-use the listener --- imcp/icmp.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/imcp/icmp.go b/imcp/icmp.go index f6f0d22..905f74e 100644 --- a/imcp/icmp.go +++ b/imcp/icmp.go @@ -27,7 +27,11 @@ func SendDiscoverIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time } defer c.Close() c.IPv4PacketConn().SetTTL(ttl) - c.SetDeadline(time.Now().Add(timeout)) + err = c.SetDeadline(time.Now().Add(timeout)) + if err != nil { + return hop, err + } + wm := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ @@ -65,7 +69,11 @@ func SendIMCP(localAddr string, dst net.Addr, pid int, timeout time.Duration, se return hop, err } defer c.Close() - c.SetDeadline(time.Now().Add(timeout)) + err = c.SetDeadline(time.Now().Add(timeout)) + if err != nil { + return hop, err + } + body := fmt.Sprintf("ping%d", seq) wm := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, @@ -83,7 +91,7 @@ func SendIMCP(localAddr string, dst net.Addr, pid int, timeout time.Duration, se return hop, err } - _, err = listenForSpecific(time.Now().Add(timeout), dst.String(), body) + _, err = listenForSpecific(c, time.Now().Add(timeout), dst.String(), body) if err != nil { return hop, err } @@ -95,14 +103,8 @@ func SendIMCP(localAddr string, dst net.Addr, pid int, timeout time.Duration, se return hop, err } -// listenForSpecific listens for a reply from a specific destination and returns the body if returned -func listenForSpecific(deadline time.Time, neededPeer, neededBody string) (string, error) { - conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") - if err != nil { - return "", err - } - conn.SetDeadline(deadline) - defer conn.Close() +// listenForSpecific listens for a reply from a specific destination with a specifi body and returns the body if returned +func listenForSpecific(conn *icmp.PacketConn, deadline time.Time, neededPeer, neededBody string) (string, error) { for { bytes := make([]byte, 1500) n, peer, err := conn.ReadFrom(bytes) From 21fddbcf76ebbd45a719081c844f56df0d90e01e Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 24 Aug 2018 21:42:13 +0200 Subject: [PATCH 06/10] do not count failed as best or worst time --- hop/hop.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hop/hop.go b/hop/hop.go index ea86aa4..33df67c 100644 --- a/hop/hop.go +++ b/hop/hop.go @@ -51,11 +51,14 @@ func (s *HopStatistic) Next() { s.Packets.Value = r s.Sent++ - s.SumElapsed = r.Elapsed + s.SumElapsed + if !r.Success { - s.Lost++ + return // do not count failed into statistics } + + s.SumElapsed = r.Elapsed + s.SumElapsed s.Last = r + if s.Best.Elapsed > r.Elapsed { s.Best = r } From dd39cdc208732a4be537bb9a7e041afe5de2bbe3 Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 24 Aug 2018 22:51:04 +0200 Subject: [PATCH 07/10] Make use of returned imcp header in time exeeded --- hop/hop.go | 13 +++---------- imcp/icmp.go | 38 +++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/hop/hop.go b/hop/hop.go index 33df67c..8e1a20c 100644 --- a/hop/hop.go +++ b/hop/hop.go @@ -18,7 +18,6 @@ type HopStatistic struct { Sent int TTL int Target string - TargetIP *net.IPAddr Last imcp.ICMPReturn Best imcp.ICMPReturn Worst imcp.ICMPReturn @@ -38,26 +37,20 @@ func (s *HopStatistic) Next() { if s.Target == "" { return } - if s.TargetIP == nil { - addr, err := net.ResolveIPAddr("ip4", s.Target) - if err != nil { - return - } - s.TargetIP = addr - } s.pingSeq++ - r, _ := imcp.SendIMCP("0.0.0.0", s.TargetIP, s.PID, s.Timeout, s.pingSeq) + r, _ := imcp.SendIMCP("0.0.0.0", s.Dest, s.Target, s.TTL, s.PID, s.Timeout, s.pingSeq) s.Packets = s.Packets.Prev() s.Packets.Value = r s.Sent++ + s.Last = r if !r.Success { + s.Lost++ return // do not count failed into statistics } s.SumElapsed = r.Elapsed + s.SumElapsed - s.Last = r if s.Best.Elapsed > r.Elapsed { s.Best = r diff --git a/imcp/icmp.go b/imcp/icmp.go index 905f74e..e2e1ebf 100644 --- a/imcp/icmp.go +++ b/imcp/icmp.go @@ -1,7 +1,7 @@ package imcp import ( - "errors" + "bytes" "fmt" "net" "time" @@ -35,7 +35,7 @@ func SendDiscoverIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time wm := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ - ID: pid, Seq: 1, + ID: pid, Seq: seq, Data: []byte(""), }, } @@ -61,7 +61,7 @@ func SendDiscoverIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time } // SendIMCP sends a IMCP to a given destination -func SendIMCP(localAddr string, dst net.Addr, pid int, timeout time.Duration, seq int) (hop ICMPReturn, err error) { +func SendIMCP(localAddr string, dst net.Addr, target string, ttl, pid int, timeout time.Duration, seq int) (hop ICMPReturn, err error) { hop.Success = false start := time.Now() c, err := icmp.ListenPacket("ip4:icmp", localAddr) @@ -69,6 +69,7 @@ func SendIMCP(localAddr string, dst net.Addr, pid int, timeout time.Duration, se return hop, err } defer c.Close() + c.IPv4PacketConn().SetTTL(ttl) err = c.SetDeadline(time.Now().Add(timeout)) if err != nil { return hop, err @@ -78,7 +79,7 @@ func SendIMCP(localAddr string, dst net.Addr, pid int, timeout time.Duration, se wm := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ - ID: pid, Seq: 0, + ID: pid, Seq: seq, Data: []byte(body), }, } @@ -91,26 +92,26 @@ func SendIMCP(localAddr string, dst net.Addr, pid int, timeout time.Duration, se return hop, err } - _, err = listenForSpecific(c, time.Now().Add(timeout), dst.String(), body) + peer, _, err := listenForSpecific(c, time.Now().Add(timeout), target, body, seq, wb) if err != nil { return hop, err } elapsed := time.Since(start) hop.Elapsed = elapsed - hop.Addr = dst.String() + hop.Addr = peer hop.Success = true return hop, err } // listenForSpecific listens for a reply from a specific destination with a specifi body and returns the body if returned -func listenForSpecific(conn *icmp.PacketConn, deadline time.Time, neededPeer, neededBody string) (string, error) { +func listenForSpecific(conn *icmp.PacketConn, deadline time.Time, neededPeer, neededBody string, needSeq int, sent []byte) (string, string, error) { for { - bytes := make([]byte, 1500) - n, peer, err := conn.ReadFrom(bytes) + b := make([]byte, 1500) + n, peer, err := conn.ReadFrom(b) if err != nil { if neterr, ok := err.(*net.OpError); ok { - return "", neterr + return "", "", neterr } } if n == 0 { @@ -121,13 +122,22 @@ func listenForSpecific(conn *icmp.PacketConn, deadline time.Time, neededPeer, ne continue } - x, err := icmp.ParseMessage(1, bytes[:n]) + x, err := icmp.ParseMessage(1, b[:n]) if err != nil { continue } if x.Type.(ipv4.ICMPType).String() == "time exceeded" { - return "", errors.New("time exceeded") + body := x.Body.(*icmp.TimeExceeded).Data + + index := bytes.Index(body, sent[:4]) + if index > 0 { + x, _ := icmp.ParseMessage(1, body[index:]) + seq := x.Body.(*icmp.Echo).Seq + if seq == needSeq { + return peer.String(), "", nil + } + } } if x.Type.(ipv4.ICMPType).String() == "echo reply" { @@ -135,9 +145,7 @@ func listenForSpecific(conn *icmp.PacketConn, deadline time.Time, neededPeer, ne if string(b[4:]) != neededBody { continue } - return string(b[4:]), nil + return peer.String(), string(b[4:]), nil } - - panic(x.Type.(ipv4.ICMPType).String()) } } From 8910bb0996b60019a6f8c393b25acb6ad2c9599a Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 24 Aug 2018 22:55:57 +0200 Subject: [PATCH 08/10] prevent panic from type assertions --- imcp/icmp.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/imcp/icmp.go b/imcp/icmp.go index e2e1ebf..f0485a4 100644 --- a/imcp/icmp.go +++ b/imcp/icmp.go @@ -133,9 +133,14 @@ func listenForSpecific(conn *icmp.PacketConn, deadline time.Time, neededPeer, ne index := bytes.Index(body, sent[:4]) if index > 0 { x, _ := icmp.ParseMessage(1, body[index:]) - seq := x.Body.(*icmp.Echo).Seq - if seq == needSeq { - return peer.String(), "", nil + switch x.Body.(type) { + case *icmp.Echo: + seq := x.Body.(*icmp.Echo).Seq + if seq == needSeq { + return peer.String(), "", nil + } + default: + // ignore } } } From ed39df7e6f488a26ee68cb194f2994a9bc2dda74 Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 24 Aug 2018 22:57:39 +0200 Subject: [PATCH 09/10] Check more errors --- imcp/icmp.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/imcp/icmp.go b/imcp/icmp.go index f0485a4..5c3023b 100644 --- a/imcp/icmp.go +++ b/imcp/icmp.go @@ -26,7 +26,11 @@ func SendDiscoverIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time return hop, err } defer c.Close() - c.IPv4PacketConn().SetTTL(ttl) + + err = c.IPv4PacketConn().SetTTL(ttl) + if err != nil { + return hop, err + } err = c.SetDeadline(time.Now().Add(timeout)) if err != nil { return hop, err @@ -69,7 +73,10 @@ func SendIMCP(localAddr string, dst net.Addr, target string, ttl, pid int, timeo return hop, err } defer c.Close() - c.IPv4PacketConn().SetTTL(ttl) + err = c.IPv4PacketConn().SetTTL(ttl) + if err != nil { + return hop, err + } err = c.SetDeadline(time.Now().Add(timeout)) if err != nil { return hop, err From 514f020a95f0a09036b843fbf9666afdab487376 Mon Sep 17 00:00:00 2001 From: Maartje Eyskens Date: Fri, 24 Aug 2018 23:01:48 +0200 Subject: [PATCH 10/10] update comments --- imcp/icmp.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imcp/icmp.go b/imcp/icmp.go index 5c3023b..7d72114 100644 --- a/imcp/icmp.go +++ b/imcp/icmp.go @@ -17,7 +17,7 @@ type ICMPReturn struct { Elapsed time.Duration } -// SendIMCP sends a IMCP to a given destination but does allow an IMCP timeout +// SendDiscoverIMCP sends a IMCP to a given destination with a TTL to discover hops func SendDiscoverIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time.Duration, seq int) (hop ICMPReturn, err error) { hop.Success = false start := time.Now() @@ -64,7 +64,7 @@ func SendDiscoverIMCP(localAddr string, dst net.Addr, ttl, pid int, timeout time return hop, err } -// SendIMCP sends a IMCP to a given destination +// SendIMCP sends a IMCP to a given destination which requires a reply from that specific destination func SendIMCP(localAddr string, dst net.Addr, target string, ttl, pid int, timeout time.Duration, seq int) (hop ICMPReturn, err error) { hop.Success = false start := time.Now()