Skip to content

Commit

Permalink
Merge pull request #23 from middleware-labs/kartik-SYN-10
Browse files Browse the repository at this point in the history
SYN-10: added hops in response
  • Loading branch information
tejaskokje-mw authored Aug 9, 2024
2 parents e4bdf0d + 61f1113 commit c70e54c
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 52 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@

# Go workspace file
go.work

# ide
.vscode
.idea
105 changes: 93 additions & 12 deletions pkg/worker/check_icmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package worker
import (
"encoding/json"
"fmt"
"net"
"strings"
"time"

Expand All @@ -16,6 +17,12 @@ const (
assertTypeICMPPacketRecv string = "packet_received"
)

var (
icmpStatusEstablished = "established"
icmpStatusRefused = "refused"
icmpStatusTimeout = "timeout"
)

type pinger interface {
Run() error
Statistics() *probing.Statistics
Expand Down Expand Up @@ -66,10 +73,10 @@ type icmpChecker struct {
c SyntheticCheck
details map[string]float64
timers map[string]float64
testBody map[string]interface{}
assertions []map[string]string
attrs pcommon.Map
pinger pinger
netter Netter
BaseCheckerForTTL
}

func getDefaultPinger(c SyntheticCheck) (*icmpPinger, error) {
Expand Down Expand Up @@ -102,15 +109,18 @@ func newICMPChecker(c SyntheticCheck, pinger pinger) protocolChecker {
timers: map[string]float64{
"duration": 0,
},
testBody: map[string]interface{}{
"rcmp_status": "FAILED",
"packet_size": "0 bytes",
"packet": "N/A",
"latency": "N/A",
BaseCheckerForTTL: BaseCheckerForTTL{
testBody: map[string]interface{}{
"rcmp_status": "FAILED",
"packet_size": "0 bytes",
"packet": "N/A",
"latency": "N/A",
},
attrs: pcommon.NewMap(),
},
assertions: make([]map[string]string, 0),
attrs: pcommon.NewMap(),
pinger: pinger,
netter: &DefaultNetter{},
}
}

Expand Down Expand Up @@ -174,6 +184,7 @@ func (checker *icmpChecker) check() testStatus {
status: testStatusOK,
}

icmpStatus := icmpStatusEstablished
err := checker.pinger.Run() // Blocks until finished.
if err != nil {
testStatus.status = testStatusError
Expand All @@ -183,6 +194,58 @@ func (checker *icmpChecker) check() testStatus {
return testStatus
}

start := time.Now()
addr, lcErr := checker.netter.LookupIP(checker.c.Endpoint)
if lcErr != nil {
checker.timers["duration"] = timeInMs(time.Since(start))
testStatus.status = testStatusError
testStatus.msg = fmt.Sprintf("error resolving dns: %v", lcErr)

for _, assert := range checker.c.Request.Assertions.TCP.Cases {
checker.assertions = append(checker.assertions,
map[string]string{
"type": assert.Type,
"reason": "should be " +
strings.ReplaceAll(assert.Config.Operator, "_", " ") +
" " + assert.Config.Value,
"status": "FAIL",
"actual": "DNS resolution failed",
})
}
checker.processICMPResponse(testStatus)
return testStatus
}

checker.timers["dns"] = timeInMs(time.Since(start))
cnTime := time.Now()

conn, tmErr := checker.netter.DialTimeout("icmp", addr[0].String()+
":"+checker.c.Request.Port,
time.Duration(checker.c.Expect.ResponseTimeLessThen)*time.Second)
if tmErr != nil {
checker.timers["connection"] = timeInMs(time.Since(cnTime))
testStatus.status = testStatusError
testStatus.msg = fmt.Sprintf("error connecting icmp %v", tmErr)

checker.attrs.PutStr("connection.error", tmErr.Error())
icmpStatus = icmpStatusRefused
checker.testBody["connection_status"] = icmpStatusRefused
if strings.Contains(tmErr.Error(), icmpStatusTimeout) {
icmpStatus = tcpStatusTimeout
checker.testBody["connection_status"] = icmpStatusTimeout
}
} else {
defer checker.netter.ConnClose(conn)
checker.timers["connection"] = timeInMs(time.Since(cnTime))
checker.testBody["connection_status"] = icmpStatusEstablished
}

checker.attrs.PutStr("connection.status", icmpStatus)
checker.timers["duration"] = timeInMs(time.Since(start))

// process ttl
checker.processICMPTTL(addr, lcErr)

stats := checker.pinger.Statistics()

checker.timers["duration"] = timeInMs(stats.AvgRtt)
Expand Down Expand Up @@ -264,14 +327,32 @@ func (checker *icmpChecker) check() testStatus {
return testStatus
}

func (checker *icmpChecker) processICMPTTL(addr []net.IP, lcErr error) testStatus {
testStatus := testStatus{
status: testStatusOK,
}

if checker.c.Request.TTL {
if len(addr) > 0 {
traceRouter := newTraceRouteChecker(addr[0],
checker.c.Expect.ResponseTimeLessThen, checker.timers, checker.attrs)
tStatus := traceRouter.check()
traceRouter.getAttrs().CopyTo(checker.attrs)

testStatus.status = tStatus.status
testStatus.msg = fmt.Sprintf("error resolving dns %v", tStatus)
} else {
testStatus.status = testStatusError
testStatus.msg = fmt.Sprintf("error resolving dns %v", lcErr)
}
}
return testStatus
}

func (checker *icmpChecker) getTimers() map[string]float64 {
return checker.timers
}

func (checker *icmpChecker) getAttrs() pcommon.Map {
return checker.attrs
}

func (checker *icmpChecker) getTestResponseBody() map[string]interface{} {
return checker.testBody
}
145 changes: 109 additions & 36 deletions pkg/worker/check_tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"net"
"regexp"
"strconv"
"strings"
"time"

Expand All @@ -16,34 +18,105 @@ const (
assertTypeTCPConnection string = "connection"
)

type netter interface {
lookupIP(host string) ([]net.IP, error)
dialTimeout(network, address string,
timeout time.Duration) (net.Conn, error)
connClose(conn net.Conn) error
type tcpChecker struct {
c SyntheticCheck
timers map[string]float64
assertions []map[string]string
netter Netter
BaseCheckerForTTL
}

type defaultNetter struct{}

func (d *defaultNetter) lookupIP(host string) ([]net.IP, error) {
return net.LookupIP(host)
type BaseCheckerForTTL struct {
testBody map[string]interface{}
attrs pcommon.Map
}

func (d *defaultNetter) dialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
return net.DialTimeout(network, address, timeout)
}
func (bc *BaseCheckerForTTL) getTestResponseBody() map[string]interface{} {
var traceroute []map[string]interface{}
hopData := make(map[int]map[string]interface{})
maxHopNum := 0

func (d *defaultNetter) connClose(conn net.Conn) error {
return conn.Close()
}
bc.attrs.Range(func(k string, v pcommon.Value) bool {
if k == "hops.count" {
bc.testBody["hops_count"] = v.AsRaw()
}

type tcpChecker struct {
c SyntheticCheck
timers map[string]float64
testBody map[string]interface{}
assertions []map[string]string
attrs pcommon.Map
netter netter
if k == "hops" {
hopsStr := v.AsString()
lines := strings.Split(hopsStr, "\n")
hopRegex := regexp.MustCompile(`hop (\d+)\. ([\d.]+) ([\d.]+)ms`)

for _, line := range lines {
matches := hopRegex.FindStringSubmatch(line)
if matches == nil {
continue
}

hopNum, _ := strconv.Atoi(matches[1])
ip := matches[2]
latency, _ := strconv.ParseFloat(matches[3], 64)

if _, exists := hopData[hopNum]; !exists {
hopData[hopNum] = map[string]interface{}{
"latency": map[string]interface{}{
"min": latency,
"max": latency,
"avg": latency,
"values": []float64{latency},
},
"routers": []map[string]string{{"ip": ip}},
}
} else {
latencyData := hopData[hopNum]["latency"].(map[string]interface{})
values := latencyData["values"].([]float64)
values = append(values, latency)

minLatency := latencyData["min"].(float64)
maxLatency := latencyData["max"].(float64)

if latency < minLatency {
latencyData["min"] = latency
}
if latency > maxLatency {
latencyData["max"] = latency
}
latencyData["values"] = values
latencyData["avg"] = (latencyData["min"].(float64) + latencyData["max"].(float64)) / 2

routers := hopData[hopNum]["routers"].([]map[string]string)
routers = append(routers, map[string]string{"ip": ip})
hopData[hopNum]["routers"] = routers
}

if hopNum > maxHopNum {
maxHopNum = hopNum
}
}

// Converting the map to a slice, filling gaps with default values
for i := 1; i <= maxHopNum; i++ {
if data, exists := hopData[i]; exists {
traceroute = append(traceroute, data)
} else {
// Adding default values for missing hop numbers
traceroute = append(traceroute, map[string]interface{}{
"latency": map[string]interface{}{
"min": nil,
"max": nil,
"avg": nil,
"values": nil,
},
"routers": []map[string]string{{"ip": "???"}},
})
}
}
}

return true
})

bc.testBody["traceroute"] = traceroute
return bc.testBody
}

func newTCPChecker(c SyntheticCheck) protocolChecker {
Expand All @@ -54,14 +127,16 @@ func newTCPChecker(c SyntheticCheck) protocolChecker {
"dns": 0,
"connection": 0,
},
testBody: map[string]interface{}{
"assertions": make([]map[string]interface{}, 0),
"tookMs": "0 ms",
"connection_status": tcpStatusEstablished,
},
assertions: make([]map[string]string, 0),
attrs: pcommon.NewMap(),
netter: &defaultNetter{},
netter: &DefaultNetter{},
BaseCheckerForTTL: BaseCheckerForTTL{
testBody: map[string]interface{}{
"assertions": make([]map[string]interface{}, 0),
"tookMs": "0 ms",
"connection_status": tcpStatusEstablished,
},
attrs: pcommon.NewMap(),
},
}
}

Expand Down Expand Up @@ -149,6 +224,8 @@ func (checker *tcpChecker) processTCPTTL(addr []net.IP, lcErr error) testStatus
traceRouter := newTraceRouteChecker(addr[0],
checker.c.Expect.ResponseTimeLessThen, checker.timers, checker.attrs)
tStatus := traceRouter.check()
traceRouter.getAttrs().CopyTo(checker.attrs)

testStatus.status = tStatus.status
testStatus.msg = fmt.Sprintf("error resolving dns %v", tStatus)
} else {
Expand All @@ -167,7 +244,7 @@ func (checker *tcpChecker) check() testStatus {
tcpStatus := tcpStatusEstablished
start := time.Now()

addr, lcErr := checker.netter.lookupIP(checker.c.Endpoint)
addr, lcErr := checker.netter.LookupIP(checker.c.Endpoint)
if lcErr != nil {
checker.timers["duration"] = timeInMs(time.Since(start))
testStatus.status = testStatusError
Expand All @@ -191,7 +268,7 @@ func (checker *tcpChecker) check() testStatus {
checker.timers["dns"] = timeInMs(time.Since(start))
cnTime := time.Now()

conn, tmErr := checker.netter.dialTimeout("tcp", addr[0].String()+
conn, tmErr := checker.netter.DialTimeout("tcp", addr[0].String()+
":"+checker.c.Request.Port,
time.Duration(checker.c.Expect.ResponseTimeLessThen)*time.Second)
if tmErr != nil {
Expand All @@ -207,7 +284,7 @@ func (checker *tcpChecker) check() testStatus {
checker.testBody["connection_status"] = tcpStatusTimeout
}
} else {
defer checker.netter.connClose(conn)
defer checker.netter.ConnClose(conn)
checker.timers["connection"] = timeInMs(time.Since(cnTime))
checker.testBody["connection_status"] = tcpStatusEstablished
}
Expand All @@ -232,10 +309,6 @@ func (checker *tcpChecker) getAttrs() pcommon.Map {
return checker.attrs
}

func (checker *tcpChecker) getTestResponseBody() map[string]interface{} {
return checker.testBody
}

func (checker *tcpChecker) getDetails() map[string]float64 {
return nil
}
Loading

0 comments on commit c70e54c

Please sign in to comment.