Skip to content

Commit

Permalink
feat: show all attempted hops in api output
Browse files Browse the repository at this point in the history
Signed-off-by: Niklas Treml <[email protected]>
  • Loading branch information
niklastreml committed Jul 26, 2024
1 parent 4ccae9a commit 476f570
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 49 deletions.
15 changes: 9 additions & 6 deletions pkg/checks/traceroute/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ type Traceroute struct {
traceroute tracerouteFactory
}

type tracerouteFactory func(dest string, port, timeout, maxHops int, rc helper.RetryConfig) ([]Hop, error)
type tracerouteFactory func(dest string, port, timeout, maxHops int, rc helper.RetryConfig) (map[int][]Hop, error)

type result struct {
// The minimum number of hops required to reach the target
NumHops int
// The path taken to the destination
Hops []Hop
Hops map[int][]Hop
}

// Run runs the check in a loop sending results to the provided channel
Expand Down Expand Up @@ -120,10 +120,13 @@ func (tr *Traceroute) check(ctx context.Context) map[string]result {
Hops: trace,
}

for i, h := range trace {
if h.Reached {
r.NumHops = i + 1
break
reached:
for i, hops := range trace {
for _, hop := range hops {
if hop.Reached {
r.NumHops = i
break reached
}
}
}

Expand Down
56 changes: 30 additions & 26 deletions pkg/checks/traceroute/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ func TestCheck(t *testing.T) {
want: map[string]result{
"8.8.8.8": {
NumHops: 5,
Hops: []Hop{
{Addr: &net.TCPAddr{IP: net.ParseIP("0.0.0.0")}, Latency: 0 * time.Second, Reached: false, Ttl: 1},
{Addr: &net.TCPAddr{IP: net.ParseIP("0.0.0.1")}, Latency: 1 * time.Second, Reached: false, Ttl: 2},
{Addr: &net.TCPAddr{IP: net.ParseIP("0.0.0.2")}, Latency: 2 * time.Second, Reached: false, Ttl: 3},
{Addr: &net.TCPAddr{IP: net.ParseIP("0.0.0.3")}, Latency: 3 * time.Second, Reached: false, Ttl: 4},
{Addr: &net.TCPAddr{IP: net.ParseIP("123.0.0.123"), Port: 53}, Name: "google-public-dns-a.google.com", Latency: 69 * time.Second, Reached: true, Ttl: 5},
Hops: map[int][]Hop{
1: {{Addr: &net.TCPAddr{IP: net.ParseIP("0.0.0.1")}, Latency: 1 * time.Second, Reached: false, Ttl: 1}},
2: {{Addr: &net.TCPAddr{IP: net.ParseIP("0.0.0.2")}, Latency: 2 * time.Second, Reached: false, Ttl: 2}},
3: {{Addr: &net.TCPAddr{IP: net.ParseIP("0.0.0.3")}, Latency: 3 * time.Second, Reached: false, Ttl: 3}},
4: {{Addr: &net.TCPAddr{IP: net.ParseIP("0.0.0.4")}, Latency: 4 * time.Second, Reached: false, Ttl: 4}},
5: {{Addr: &net.TCPAddr{IP: net.ParseIP("123.0.0.123"), Port: 53}, Name: "google-public-dns-a.google.com", Latency: 69 * time.Second, Reached: true, Ttl: 5}},
},
},
},
Expand All @@ -39,7 +39,7 @@ func TestCheck(t *testing.T) {
name: "Traceroute internal error fails silently",
c: newForTest(returnError(&net.DNSError{Err: "no such host", Name: "google.com", IsNotFound: true}), []string{"google.com"}),
want: map[string]result{
"google.com": {Hops: []Hop{}},
"google.com": {Hops: map[int][]Hop{}},
},
},
}
Expand Down Expand Up @@ -75,35 +75,39 @@ func newForTest(f tracerouteFactory, targets []string) *Traceroute {

// success produces a tracerouteFactory that returns a traceroute result with nHops hops
func success(nHops int) tracerouteFactory {
return func(dest string, port, timeout, maxHops int, _ helper.RetryConfig) ([]Hop, error) {
hops := make([]Hop, nHops)
for i := 0; i < nHops-1; i++ {
hops[i] = Hop{
Latency: time.Second * time.Duration(i),
Addr: &net.TCPAddr{IP: net.ParseIP(ipFromInt(i))},
Name: "",
Ttl: i + 1,
Reached: false,
return func(dest string, port, timeout, maxHops int, _ helper.RetryConfig) (map[int][]Hop, error) {
hops := make(map[int][]Hop)
for i := 1; i < nHops; i++ {
hops[i] = []Hop{
{
Latency: time.Second * time.Duration(i),
Addr: &net.TCPAddr{IP: net.ParseIP(ipFromInt(i))},
Name: "",
Ttl: i,
Reached: false,
},
}
}
hops[nHops-1] = Hop{
Latency: 69 * time.Second,
Addr: &net.TCPAddr{
IP: net.ParseIP("123.0.0.123"),
Port: 53,
hops[nHops] = []Hop{
{
Latency: 69 * time.Second,
Addr: &net.TCPAddr{
IP: net.ParseIP("123.0.0.123"),
Port: 53,
},
Name: "google-public-dns-a.google.com",
Ttl: nHops,
Reached: true,
},
Name: "google-public-dns-a.google.com",
Ttl: nHops,
Reached: true,
}

return hops, nil
}
}

func returnError(err error) tracerouteFactory {
return func(string, int, int, int, helper.RetryConfig) ([]Hop, error) {
return []Hop{}, err
return func(string, int, int, int, helper.RetryConfig) (map[int][]Hop, error) {
return map[int][]Hop{}, err
}
}

Expand Down
33 changes: 16 additions & 17 deletions pkg/checks/traceroute/traceroute.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"
"math/rand"
"net"
"slices"
"sync"
"syscall"
"time"
Expand Down Expand Up @@ -83,10 +82,11 @@ func readIcmpMessage(icmpListener *icmp.PacketConn, timeout time.Duration) (int,
return destPort, routerAddr, nil
}

func TraceRoute(host string, port, timeout, maxHops int, rc helper.RetryConfig) ([]Hop, error) {
// TraceRoute performs a traceroute to the specified host using TCP and listens for ICMP Time Exceeded messages using datagram-oriented ICMP.
// func TraceRoute(host string, port int, maxHops int, timeout time.Duration) ([]Hop, error) {
var hops []Hop
// TraceRoute performs a traceroute to the specified host using TCP and listens for ICMP Time Exceeded messages using ICMP.
func TraceRoute(host string, port, timeout, maxHops int, rc helper.RetryConfig) (map[int][]Hop, error) {
// this could also be a 2d array, but I feel like using an int map here makes the json easier to understand
// as it explicitly shows a mapping of ttl->hops
var hops map[int][]Hop

toDuration := time.Duration(timeout) * time.Second

Expand Down Expand Up @@ -119,14 +119,11 @@ func TraceRoute(host string, port, timeout, maxHops int, rc helper.RetryConfig)
close(results)

for r := range results {
hops = append(hops, r)
hops[r.Ttl] = append(hops[r.Ttl], r)
}

slices.SortFunc(hops, func(a, b Hop) int {
return a.Ttl - b.Ttl
})

PrintHops(hops)
// TODO: log this on debug level
printHops(hops)

return hops, nil
}
Expand Down Expand Up @@ -227,12 +224,14 @@ type Hop struct {
Reached bool
}

func PrintHops(hops []Hop) {
for _, hop := range hops {
fmt.Printf("%d %s %s %v ", hop.Ttl, hop.Addr, hop.Name, hop.Latency)
if hop.Reached {
fmt.Print("( Reached )")
func printHops(mapHops map[int][]Hop) {
for ttl, hops := range mapHops {
for _, hop := range hops {
fmt.Printf("%d %s %s %v ", ttl, hop.Addr, hop.Name, hop.Latency)
if hop.Reached {
fmt.Print("( Reached )")
}
fmt.Println()
}
fmt.Println()
}
}

0 comments on commit 476f570

Please sign in to comment.