2018-12-03 04:39:13 +08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Copyright (C) Microsoft. All rights reserved.
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
// See LICENSE.txt file in the project root for full license information.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-11-10 21:37:52 +08:00
|
|
|
// "bytes"
|
|
|
|
// "crypto/tls"
|
|
|
|
// "crypto/x509"
|
|
|
|
|
2018-12-03 04:39:13 +08:00
|
|
|
"bytes"
|
2020-11-24 10:55:20 +08:00
|
|
|
"encoding/binary"
|
2018-12-03 04:39:13 +08:00
|
|
|
"encoding/gob"
|
2020-11-24 10:55:20 +08:00
|
|
|
"encoding/hex"
|
2019-01-08 01:15:09 +08:00
|
|
|
"fmt"
|
2018-12-03 04:39:13 +08:00
|
|
|
"io"
|
2020-11-24 10:55:20 +08:00
|
|
|
"net/url"
|
2020-11-10 21:37:52 +08:00
|
|
|
"sort"
|
2020-11-24 10:55:20 +08:00
|
|
|
"strconv"
|
2020-11-10 21:37:52 +08:00
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
|
|
|
|
// "io"
|
|
|
|
// "io/ioutil"
|
2018-12-03 04:39:13 +08:00
|
|
|
"net"
|
2020-11-10 21:37:52 +08:00
|
|
|
// "net/http"
|
2018-12-03 04:39:13 +08:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
2020-11-10 21:37:52 +08:00
|
|
|
|
|
|
|
// "sort"
|
|
|
|
// "sync/atomic"
|
|
|
|
"syscall"
|
2018-12-03 04:39:13 +08:00
|
|
|
"time"
|
2020-11-10 21:37:52 +08:00
|
|
|
|
|
|
|
"golang.org/x/net/icmp"
|
|
|
|
"golang.org/x/net/ipv4"
|
2018-12-03 04:39:13 +08:00
|
|
|
)
|
|
|
|
|
2019-02-03 23:53:54 +08:00
|
|
|
var gIgnoreCert bool
|
|
|
|
|
2018-12-03 04:39:13 +08:00
|
|
|
const (
|
2020-11-24 10:55:20 +08:00
|
|
|
done = 0
|
|
|
|
timeout = 1
|
|
|
|
interrupt = 2
|
|
|
|
disconnect = 3
|
2018-12-03 04:39:13 +08:00
|
|
|
)
|
|
|
|
|
2020-09-23 07:28:41 +08:00
|
|
|
func handleInterrupt(toStop chan<- int) {
|
2018-12-03 04:39:13 +08:00
|
|
|
sigChan := make(chan os.Signal)
|
2020-11-10 21:37:52 +08:00
|
|
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
2018-12-03 04:39:13 +08:00
|
|
|
go func() {
|
2020-09-23 07:28:41 +08:00
|
|
|
<-sigChan
|
|
|
|
toStop <- interrupt
|
2018-12-03 04:39:13 +08:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
func runDurationTimer(d time.Duration, toStop chan int) {
|
|
|
|
go func() {
|
|
|
|
dSeconds := uint64(d.Seconds())
|
|
|
|
if dSeconds == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
time.Sleep(d)
|
2020-11-10 21:37:52 +08:00
|
|
|
// Sleep extra 200ms to ensure stats print for correct number of seconds.
|
|
|
|
time.Sleep(200 * time.Millisecond)
|
2018-12-03 04:39:13 +08:00
|
|
|
toStop <- timeout
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2020-11-10 21:37:52 +08:00
|
|
|
func initClient() {
|
|
|
|
initClientUI()
|
|
|
|
}
|
|
|
|
|
|
|
|
func handshakeWithServer(test *ethrTest, conn net.Conn) {
|
|
|
|
dec := gob.NewDecoder(conn)
|
|
|
|
enc := gob.NewEncoder(conn)
|
|
|
|
ethrMsg := createSynMsg(test.testParam)
|
|
|
|
err := sendSessionMsg(enc, ethrMsg)
|
|
|
|
if err != nil {
|
|
|
|
ui.printErr("Failed to send session message: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ethrMsg = recvSessionMsg(dec)
|
|
|
|
if ethrMsg.Type != EthrAck {
|
|
|
|
if ethrMsg.Type == EthrFin {
|
|
|
|
err = fmt.Errorf("%s", ethrMsg.Fin.Message)
|
|
|
|
} else {
|
|
|
|
err = fmt.Errorf("Unexpected control message received. %v", ethrMsg)
|
|
|
|
}
|
|
|
|
}
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func getServerIPandPort(server string) (string, string, error) {
|
|
|
|
hostName := ""
|
|
|
|
hostIP := ""
|
|
|
|
port := ""
|
|
|
|
u, err := url.Parse(server)
|
|
|
|
if err == nil && u.Hostname() != "" {
|
|
|
|
hostName = u.Hostname()
|
|
|
|
if u.Port() != "" {
|
|
|
|
port = u.Port()
|
|
|
|
} else {
|
|
|
|
if u.Scheme == "http" {
|
|
|
|
port = "80"
|
|
|
|
} else if u.Scheme == "https" {
|
|
|
|
port = "443"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
hostName, port, err = net.SplitHostPort(server)
|
|
|
|
if err != nil {
|
|
|
|
hostName = server
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_, hostIP, err = ethrLookupIP(hostName)
|
|
|
|
return hostIP, port, err
|
|
|
|
}
|
|
|
|
|
2020-11-10 21:37:52 +08:00
|
|
|
func runClient(testParam EthrTestParam, clientParam ethrClientParam, server string) {
|
|
|
|
initClient()
|
2020-11-24 10:55:20 +08:00
|
|
|
hostIP, port, err := getServerIPandPort(server)
|
|
|
|
if err != nil {
|
|
|
|
return
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
if !xMode && port != "" {
|
|
|
|
// For Ethr to Ethr tests, override the port supplied as part
|
|
|
|
// of the server name/url.
|
|
|
|
port = gEthrPortStr
|
|
|
|
}
|
|
|
|
test, err := newTest(hostIP, nil, testParam)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
|
|
|
ui.printErr("Failed to create the new test.")
|
|
|
|
return
|
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
test.remoteAddr = server
|
|
|
|
test.remoteIP = hostIP
|
|
|
|
test.remotePort = port
|
|
|
|
if testParam.TestID.Protocol == ICMP {
|
|
|
|
test.dialAddr = hostIP
|
|
|
|
} else {
|
|
|
|
test.dialAddr = fmt.Sprintf("[%s]:%s", hostIP, port)
|
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
runTest(test, clientParam.duration, clientParam.gap, clientParam.warmupCount)
|
|
|
|
}
|
|
|
|
|
|
|
|
func runTest(test *ethrTest, d, g time.Duration, warmupCount int) {
|
|
|
|
toStop := make(chan int, 1)
|
2018-12-03 04:39:13 +08:00
|
|
|
startStatsTimer()
|
2020-11-10 21:37:52 +08:00
|
|
|
runDurationTimer(d, toStop)
|
|
|
|
test.isActive = true
|
2018-12-11 23:28:33 +08:00
|
|
|
if test.testParam.TestID.Protocol == TCP {
|
|
|
|
if test.testParam.TestID.Type == Bandwidth {
|
2020-11-24 10:55:20 +08:00
|
|
|
tcpRunBandwidthTest(test, toStop)
|
2018-12-11 23:28:33 +08:00
|
|
|
} else if test.testParam.TestID.Type == Latency {
|
2020-11-10 21:37:52 +08:00
|
|
|
go runTCPLatencyTest(test, g, toStop)
|
|
|
|
} else if test.testParam.TestID.Type == Cps {
|
|
|
|
go tcpRunCpsTest(test)
|
|
|
|
} else if test.testParam.TestID.Type == Ping {
|
|
|
|
go clientRunPingTest(test, g, warmupCount)
|
2020-11-24 10:55:20 +08:00
|
|
|
} else if test.testParam.TestID.Type == TraceRoute {
|
|
|
|
tcpRunTraceRoute(test, g, toStop)
|
|
|
|
} else if test.testParam.TestID.Type == MyTraceRoute {
|
|
|
|
tcpRunMyTraceRoute(test, g, toStop)
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
2018-12-11 23:28:33 +08:00
|
|
|
} else if test.testParam.TestID.Protocol == UDP {
|
2020-11-10 21:37:52 +08:00
|
|
|
if test.testParam.TestID.Type == Bandwidth ||
|
|
|
|
test.testParam.TestID.Type == Pps {
|
|
|
|
runUDPBandwidthAndPpsTest(test)
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
} else if test.testParam.TestID.Protocol == ICMP {
|
2020-11-24 10:55:20 +08:00
|
|
|
if test.testParam.TestID.Type == Ping {
|
2020-11-10 21:37:52 +08:00
|
|
|
go clientRunPingTest(test, g, warmupCount)
|
2020-11-24 10:55:20 +08:00
|
|
|
} else if test.testParam.TestID.Type == TraceRoute {
|
|
|
|
icmpRunTraceRoute(test, g, toStop)
|
|
|
|
} else if test.testParam.TestID.Type == MyTraceRoute {
|
|
|
|
icmpRunMyTraceRoute(test, g, toStop)
|
2019-01-08 01:15:09 +08:00
|
|
|
}
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
|
2020-09-23 07:28:41 +08:00
|
|
|
handleInterrupt(toStop)
|
2018-12-03 04:39:13 +08:00
|
|
|
reason := <-toStop
|
|
|
|
stopStatsTimer()
|
2020-11-10 21:37:52 +08:00
|
|
|
close(test.done)
|
|
|
|
if test.testParam.TestID.Type == Ping {
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
}
|
2018-12-03 04:39:13 +08:00
|
|
|
switch reason {
|
2020-11-24 10:55:20 +08:00
|
|
|
case done:
|
|
|
|
ui.printMsg("Ethr done, measurement complete.")
|
2018-12-03 04:39:13 +08:00
|
|
|
case timeout:
|
|
|
|
ui.printMsg("Ethr done, duration: " + d.String() + ".")
|
|
|
|
case interrupt:
|
|
|
|
ui.printMsg("Ethr done, received interrupt signal.")
|
2020-11-10 21:37:52 +08:00
|
|
|
case disconnect:
|
|
|
|
ui.printMsg("Ethr done, connection terminated.")
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
return
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func tcpRunBandwidthTest(test *ethrTest, toStop chan int) {
|
2020-11-10 21:37:52 +08:00
|
|
|
var wg sync.WaitGroup
|
2020-11-24 10:55:20 +08:00
|
|
|
tcpRunBanwidthTestThreads(test, &wg)
|
2020-11-10 21:37:52 +08:00
|
|
|
go func(wg *sync.WaitGroup) {
|
|
|
|
wg.Wait()
|
|
|
|
toStop <- disconnect
|
|
|
|
}(&wg)
|
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func tcpRunBanwidthTestThreads(test *ethrTest, wg *sync.WaitGroup) {
|
2018-12-03 04:39:13 +08:00
|
|
|
for th := uint32(0); th < test.testParam.NumThreads; th++ {
|
2020-11-24 10:55:20 +08:00
|
|
|
conn, err := net.Dial(tcp(ipVer), test.dialAddr)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
|
|
|
ui.printErr("Error dialing connection: %v", err)
|
|
|
|
return
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
handshakeWithServer(test, conn)
|
|
|
|
wg.Add(1)
|
|
|
|
go runTCPBandwidthTestHandler(test, conn, wg)
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 21:37:52 +08:00
|
|
|
func runTCPBandwidthTestHandler(test *ethrTest, conn net.Conn, wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
|
|
|
defer conn.Close()
|
|
|
|
ec := test.newConn(conn)
|
|
|
|
rserver, rport, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
|
|
|
lserver, lport, _ := net.SplitHostPort(conn.LocalAddr().String())
|
|
|
|
ui.printMsg("[%3d] local %s port %s connected to %s port %s",
|
|
|
|
ec.fd, lserver, lport, rserver, rport)
|
|
|
|
buff := make([]byte, test.testParam.BufferSize)
|
|
|
|
for i := uint32(0); i < test.testParam.BufferSize; i++ {
|
|
|
|
buff[i] = byte(i)
|
|
|
|
}
|
|
|
|
blen := len(buff)
|
|
|
|
ExitForLoop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-test.done:
|
|
|
|
break ExitForLoop
|
|
|
|
default:
|
|
|
|
n := 0
|
|
|
|
var err error = nil
|
|
|
|
if test.testParam.Reverse {
|
|
|
|
n, err = io.ReadFull(conn, buff)
|
|
|
|
} else {
|
|
|
|
n, err = conn.Write(buff)
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil || n < blen {
|
|
|
|
ui.printDbg("Error sending/receiving data on a connection for bandwidth test: %v", err)
|
|
|
|
break ExitForLoop
|
|
|
|
}
|
|
|
|
atomic.AddUint64(&ec.bw, uint64(blen))
|
|
|
|
atomic.AddUint64(&test.testResult.bw, uint64(blen))
|
|
|
|
}
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 21:37:52 +08:00
|
|
|
func runTCPLatencyTest(test *ethrTest, g time.Duration, toStop chan int) {
|
2020-11-24 10:55:20 +08:00
|
|
|
conn, err := net.Dial(tcp(ipVer), test.dialAddr)
|
2018-12-03 04:39:13 +08:00
|
|
|
if err != nil {
|
2020-11-10 21:37:52 +08:00
|
|
|
ui.printErr("Error dialing the latency connection: %v", err)
|
2018-12-03 04:39:13 +08:00
|
|
|
os.Exit(1)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
2020-11-10 21:37:52 +08:00
|
|
|
handshakeWithServer(test, conn)
|
|
|
|
ui.emitLatencyHdr()
|
2018-12-03 04:39:13 +08:00
|
|
|
buffSize := test.testParam.BufferSize
|
|
|
|
buff := make([]byte, buffSize)
|
|
|
|
for i := uint32(0); i < buffSize; i++ {
|
|
|
|
buff[i] = byte(i)
|
|
|
|
}
|
|
|
|
blen := len(buff)
|
|
|
|
rttCount := test.testParam.RttCount
|
|
|
|
latencyNumbers := make([]time.Duration, rttCount)
|
2018-12-04 22:59:49 +08:00
|
|
|
ExitForLoop:
|
2018-12-03 04:39:13 +08:00
|
|
|
for {
|
2018-12-04 22:59:49 +08:00
|
|
|
ExitSelect:
|
2018-12-03 04:39:13 +08:00
|
|
|
select {
|
|
|
|
case <-test.done:
|
2018-12-04 22:59:49 +08:00
|
|
|
break ExitForLoop
|
2018-12-03 04:39:13 +08:00
|
|
|
default:
|
2020-11-10 21:37:52 +08:00
|
|
|
t0 := time.Now()
|
2018-12-03 04:39:13 +08:00
|
|
|
for i := uint32(0); i < rttCount; i++ {
|
|
|
|
s1 := time.Now()
|
|
|
|
n, err := conn.Write(buff)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil || n < blen {
|
|
|
|
ui.printDbg("Error sending/receiving data on a connection for latency test: %v", err)
|
|
|
|
toStop <- disconnect
|
2018-12-04 22:59:49 +08:00
|
|
|
break ExitSelect
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
_, err = io.ReadFull(conn, buff)
|
|
|
|
if err != nil {
|
2020-11-10 21:37:52 +08:00
|
|
|
ui.printDbg("Error sending/receiving data on a connection for latency test: %v", err)
|
|
|
|
toStop <- disconnect
|
2018-12-04 22:59:49 +08:00
|
|
|
break ExitSelect
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
e2 := time.Since(s1)
|
|
|
|
latencyNumbers[i] = e2
|
|
|
|
}
|
|
|
|
// TODO temp code, fix it better, this is to allow server to do
|
|
|
|
// server side latency measurements as well.
|
|
|
|
_, _ = conn.Write(buff)
|
2020-11-10 21:37:52 +08:00
|
|
|
calcAndPrintLatency(test, rttCount, latencyNumbers)
|
|
|
|
t1 := time.Since(t0)
|
|
|
|
if t1 < g {
|
|
|
|
time.Sleep(g - t1)
|
|
|
|
}
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 21:37:52 +08:00
|
|
|
func calcAndPrintLatency(test *ethrTest, rttCount uint32, latencyNumbers []time.Duration) {
|
|
|
|
sum := int64(0)
|
|
|
|
for _, d := range latencyNumbers {
|
|
|
|
sum += d.Nanoseconds()
|
|
|
|
}
|
|
|
|
elapsed := time.Duration(sum / int64(rttCount))
|
|
|
|
sort.SliceStable(latencyNumbers, func(i, j int) bool {
|
|
|
|
return latencyNumbers[i] < latencyNumbers[j]
|
|
|
|
})
|
|
|
|
//
|
|
|
|
// Special handling for rttCount == 1. This prevents negative index
|
|
|
|
// in the latencyNumber index. The other option is to use
|
|
|
|
// roundUpToZero() but that is more expensive.
|
|
|
|
//
|
|
|
|
rttCountFixed := rttCount
|
|
|
|
if rttCountFixed == 1 {
|
|
|
|
rttCountFixed = 2
|
|
|
|
}
|
|
|
|
avg := elapsed
|
|
|
|
min := latencyNumbers[0]
|
|
|
|
max := latencyNumbers[rttCount-1]
|
|
|
|
p50 := latencyNumbers[((rttCountFixed*50)/100)-1]
|
|
|
|
p90 := latencyNumbers[((rttCountFixed*90)/100)-1]
|
|
|
|
p95 := latencyNumbers[((rttCountFixed*95)/100)-1]
|
|
|
|
p99 := latencyNumbers[((rttCountFixed*99)/100)-1]
|
|
|
|
p999 := latencyNumbers[uint64(((float64(rttCountFixed)*99.9)/100)-1)]
|
|
|
|
p9999 := latencyNumbers[uint64(((float64(rttCountFixed)*99.99)/100)-1)]
|
|
|
|
ui.emitLatencyResults(
|
2020-11-24 10:55:20 +08:00
|
|
|
test.session.remoteIP,
|
2020-11-10 21:37:52 +08:00
|
|
|
protoToString(test.testParam.TestID.Protocol),
|
|
|
|
avg, min, max, p50, p90, p95, p99, p999, p9999)
|
|
|
|
}
|
|
|
|
|
|
|
|
func tcpRunCpsTest(test *ethrTest) {
|
2019-01-02 13:12:26 +08:00
|
|
|
for th := uint32(0); th < test.testParam.NumThreads; th++ {
|
|
|
|
go func() {
|
2020-11-10 21:37:52 +08:00
|
|
|
ExitForLoop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-test.done:
|
|
|
|
break ExitForLoop
|
|
|
|
default:
|
2020-11-24 10:55:20 +08:00
|
|
|
conn, err := net.Dial(tcp(ipVer), test.dialAddr)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err == nil {
|
|
|
|
atomic.AddUint64(&test.testResult.cps, 1)
|
|
|
|
tcpconn, ok := conn.(*net.TCPConn)
|
|
|
|
if ok {
|
|
|
|
tcpconn.SetLinger(0)
|
|
|
|
}
|
|
|
|
conn.Close()
|
|
|
|
} else {
|
2020-11-24 10:55:20 +08:00
|
|
|
ui.printDbg("Unable to dial TCP connection to %s, error: %v", test.dialAddr, err)
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
}
|
2019-01-02 13:12:26 +08:00
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func clientRunPingTest(test *ethrTest, g time.Duration, warmupCount int) {
|
|
|
|
// TODO: Override NumThreads for now, fix it later to support parallel
|
|
|
|
// threads.
|
|
|
|
test.testParam.NumThreads = 1
|
|
|
|
for th := uint32(0); th < test.testParam.NumThreads; th++ {
|
|
|
|
go func() {
|
|
|
|
var sent, rcvd, lost uint32
|
|
|
|
warmupText := "[warmup] "
|
|
|
|
latencyNumbers := make([]time.Duration, 0)
|
2019-01-02 13:12:26 +08:00
|
|
|
ExitForLoop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-test.done:
|
2020-11-24 10:55:20 +08:00
|
|
|
printConnectionLatencyResults(test.dialAddr, test, sent, rcvd, lost, latencyNumbers)
|
2019-01-02 13:12:26 +08:00
|
|
|
break ExitForLoop
|
|
|
|
default:
|
2020-11-10 21:37:52 +08:00
|
|
|
t0 := time.Now()
|
|
|
|
if warmupCount > 0 {
|
|
|
|
warmupCount--
|
2020-11-24 10:55:20 +08:00
|
|
|
clientRunPing(test, warmupText)
|
2020-11-10 21:37:52 +08:00
|
|
|
} else {
|
|
|
|
sent++
|
2020-11-24 10:55:20 +08:00
|
|
|
latency, err := clientRunPing(test, "")
|
2020-11-10 21:37:52 +08:00
|
|
|
if err == nil {
|
|
|
|
rcvd++
|
|
|
|
latencyNumbers = append(latencyNumbers, latency)
|
|
|
|
} else {
|
|
|
|
lost++
|
|
|
|
}
|
2019-01-02 13:12:26 +08:00
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
if rcvd >= 1000 {
|
2020-11-24 10:55:20 +08:00
|
|
|
printConnectionLatencyResults(test.dialAddr, test, sent, rcvd, lost, latencyNumbers)
|
2020-11-10 21:37:52 +08:00
|
|
|
latencyNumbers = make([]time.Duration, 0)
|
|
|
|
sent, rcvd, lost = 0, 0, 0
|
|
|
|
}
|
|
|
|
t1 := time.Since(t0)
|
|
|
|
if t1 < g {
|
|
|
|
time.Sleep(g - t1)
|
2019-01-02 13:12:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func clientRunPing(test *ethrTest, prefix string) (time.Duration, error) {
|
2020-11-10 21:37:52 +08:00
|
|
|
if test.testParam.TestID.Protocol == TCP {
|
2020-11-24 10:55:20 +08:00
|
|
|
return tcpRunPing(test, prefix)
|
2020-11-10 21:37:52 +08:00
|
|
|
} else {
|
2020-11-24 10:55:20 +08:00
|
|
|
return icmpRunPing(test, prefix)
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func tcpRunPing(test *ethrTest, prefix string) (timeTaken time.Duration, err error) {
|
2020-11-10 21:37:52 +08:00
|
|
|
t0 := time.Now()
|
|
|
|
// conn, err := net.DialTimeout(tcp(ipVer), *server, time.Second)
|
2020-11-24 10:55:20 +08:00
|
|
|
conn, err := net.Dial(tcp(ipVer), test.dialAddr)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
2020-11-24 10:55:20 +08:00
|
|
|
ui.printMsg("[tcp] %sConnection to %s: Timed out (%v)", prefix, test.dialAddr, err)
|
2020-11-10 21:37:52 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
timeTaken = time.Since(t0)
|
|
|
|
rserver, rport, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
|
|
|
lserver, lport, _ := net.SplitHostPort(conn.LocalAddr().String())
|
|
|
|
ui.printMsg("[tcp] %sConnection from [%s]:%s to [%s]:%s: %s",
|
|
|
|
prefix, lserver, lport, rserver, rport, durationToString(timeTaken))
|
|
|
|
tcpconn, ok := conn.(*net.TCPConn)
|
|
|
|
if ok {
|
|
|
|
tcpconn.SetLinger(0)
|
|
|
|
}
|
|
|
|
conn.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func printConnectionLatencyResults(server string, test *ethrTest, sent, rcvd, lost uint32, latencyNumbers []time.Duration) {
|
|
|
|
fmt.Println("-----------------------------------------------------------------------------------------")
|
|
|
|
ui.printMsg("TCP connect statistics for %s:", server)
|
|
|
|
ui.printMsg(" Sent = %d, Received = %d, Lost = %d", sent, rcvd, lost)
|
|
|
|
if rcvd > 0 {
|
|
|
|
ui.emitLatencyHdr()
|
|
|
|
calcAndPrintLatency(test, rcvd, latencyNumbers)
|
|
|
|
fmt.Println("-----------------------------------------------------------------------------------------")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func tcpRunTraceRoute(test *ethrTest, gap time.Duration, toStop chan int) {
|
|
|
|
tcpRunTraceRouteInternal(test, gap, toStop, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func tcpRunMyTraceRoute(test *ethrTest, gap time.Duration, toStop chan int) {
|
|
|
|
tcpRunTraceRouteInternal(test, gap, toStop, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func tcpRunTraceRouteInternal(test *ethrTest, gap time.Duration, toStop chan int, mtrMode bool) {
|
|
|
|
if !IsAdmin() {
|
|
|
|
ui.printErr("For TCP based TraceRoute, running as administrator is required.\n")
|
|
|
|
toStop <- interrupt
|
|
|
|
return
|
|
|
|
}
|
|
|
|
gHop = make([]ethrHopData, gMaxHops)
|
|
|
|
err := tcpDiscoverHops(test, mtrMode)
|
|
|
|
if err != nil {
|
|
|
|
ui.printErr("Destination %s is not responding to TCP connection.", test.session.remoteIP)
|
|
|
|
ui.printErr("Terminating tracing...")
|
|
|
|
toStop <- interrupt
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !mtrMode {
|
|
|
|
toStop <- done
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for i := 0; i < gCurHops; i++ {
|
|
|
|
if gHop[i].addr != "" {
|
|
|
|
go tcpProbeHop(test, gap, i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func tcpProbeHop(test *ethrTest, gap time.Duration, hop int) {
|
|
|
|
seq := 0
|
|
|
|
ExitForLoop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-test.done:
|
|
|
|
break ExitForLoop
|
|
|
|
default:
|
|
|
|
t0 := time.Now()
|
|
|
|
err, _ := tcpProbe(test, hop+1, gHop[hop].addr, &gHop[hop])
|
|
|
|
if err == nil {
|
2019-01-02 13:12:26 +08:00
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
seq++
|
|
|
|
t1 := time.Since(t0)
|
|
|
|
if t1 < gap {
|
|
|
|
time.Sleep(gap - t1)
|
2019-01-02 13:12:26 +08:00
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
}
|
2019-01-02 13:12:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func tcpDiscoverHops(test *ethrTest, mtrMode bool) error {
|
|
|
|
ui.printMsg("Tracing route to %s over %d hops:", test.session.remoteIP, gMaxHops)
|
|
|
|
for i := 0; i < gMaxHops; i++ {
|
|
|
|
var hopData ethrHopData
|
|
|
|
err, isLast := tcpProbe(test, i+1, "", &hopData)
|
|
|
|
if err == nil {
|
|
|
|
hopData.name, hopData.fullName = lookupHopName(hopData.addr)
|
|
|
|
}
|
|
|
|
if hopData.addr != "" {
|
|
|
|
if mtrMode {
|
|
|
|
ui.printMsg("%2d.|--%s", i+1, hopData.addr+" ["+hopData.fullName+"]")
|
|
|
|
} else {
|
|
|
|
ui.printMsg("%2d.|--%-70s %s", i+1, hopData.addr+" ["+hopData.fullName+"]", durationToString(hopData.last))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ui.printMsg("%2d.|--%s", i+1, "???")
|
|
|
|
}
|
|
|
|
copyInitialHopData(i, hopData)
|
|
|
|
if isLast {
|
|
|
|
gCurHops = i + 1
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return os.ErrNotExist
|
|
|
|
}
|
|
|
|
|
|
|
|
func tcpProbe(test *ethrTest, hop int, hopIP string, hopData *ethrHopData) (error, bool) {
|
|
|
|
isLast := false
|
|
|
|
c, err := IcmpNewConn(test.remoteIP)
|
|
|
|
if err != nil {
|
|
|
|
ui.printErr("Failed to create ICMP connection. Error: %v", err)
|
|
|
|
return err, isLast
|
|
|
|
}
|
|
|
|
defer c.Close()
|
|
|
|
localPortNum := uint16(8888 + hop)
|
|
|
|
b := make([]byte, 4)
|
|
|
|
binary.BigEndian.PutUint16(b[0:], localPortNum)
|
|
|
|
remotePortNum, err := strconv.ParseUint(test.remotePort, 10, 16)
|
|
|
|
binary.BigEndian.PutUint16(b[2:], uint16(remotePortNum))
|
|
|
|
peerAddrChan := make(chan string)
|
|
|
|
endTimeChan := make(chan time.Time)
|
|
|
|
go func() {
|
|
|
|
peerAddr, _, _ := icmpListenForSpecific4(c, TCP, time.Second*2, hopIP, b, nil, 0)
|
|
|
|
endTimeChan <- time.Now()
|
|
|
|
peerAddrChan <- peerAddr
|
|
|
|
}()
|
|
|
|
startTime := time.Now()
|
|
|
|
_, err = ethrDialForTraceRoute(tcp(ipVer), test.dialAddr, localPortNum, hop)
|
|
|
|
hopData.sent++
|
|
|
|
peerAddr := ""
|
|
|
|
endTime := time.Now()
|
|
|
|
if err == nil {
|
|
|
|
isLast = true
|
|
|
|
peerAddr = test.remoteIP
|
|
|
|
} else {
|
|
|
|
endTime = <-endTimeChan
|
|
|
|
peerAddr = <-peerAddrChan
|
|
|
|
}
|
|
|
|
elapsed := endTime.Sub(startTime)
|
|
|
|
if peerAddr == "" || (hopIP != "" && peerAddr != hopIP) {
|
|
|
|
hopData.lost++
|
|
|
|
ui.printDbg("Neither connection completed, nor ICMP TTL exceeded received.")
|
|
|
|
return os.ErrNotExist, isLast
|
|
|
|
}
|
|
|
|
genHopData(hopData, peerAddr, elapsed)
|
|
|
|
return nil, isLast
|
|
|
|
}
|
|
|
|
|
2020-11-10 21:37:52 +08:00
|
|
|
type ethrHopData struct {
|
2020-11-24 10:55:20 +08:00
|
|
|
addr string
|
|
|
|
sent uint32
|
|
|
|
rcvd uint32
|
|
|
|
lost uint32
|
|
|
|
last time.Duration
|
|
|
|
best time.Duration
|
|
|
|
worst time.Duration
|
|
|
|
total time.Duration
|
|
|
|
name string
|
|
|
|
fullName string
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var gMaxHops int = 30
|
|
|
|
var gCurHops int
|
|
|
|
var gHop []ethrHopData
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func icmpRunPing(test *ethrTest, prefix string) (time.Duration, error) {
|
|
|
|
dstIPAddr, _, err := ethrLookupIP(test.dialAddr)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
|
|
|
return time.Second, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var hopData ethrHopData
|
2020-11-24 10:55:20 +08:00
|
|
|
err, isLast := icmpProbe(test, dstIPAddr, time.Second, "", &hopData, 254, 255)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
2020-11-24 10:55:20 +08:00
|
|
|
ui.printMsg("[icmp] %sPing to %s: %v", prefix, test.dialAddr, err)
|
2020-11-10 21:37:52 +08:00
|
|
|
return time.Second, err
|
|
|
|
}
|
|
|
|
if !isLast {
|
|
|
|
ui.printMsg("[icmp] %sPing to %s: %s",
|
2020-11-24 10:55:20 +08:00
|
|
|
prefix, test.dialAddr, "Non-EchoReply Received.")
|
2020-11-10 21:37:52 +08:00
|
|
|
return time.Second, os.ErrNotExist
|
|
|
|
}
|
|
|
|
ui.printMsg("[icmp] %sPing to %s: %s",
|
2020-11-24 10:55:20 +08:00
|
|
|
prefix, test.dialAddr, durationToString(hopData.last))
|
2020-11-10 21:37:52 +08:00
|
|
|
return hopData.last, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func icmpRunTraceRoute(test *ethrTest, gap time.Duration, toStop chan int) {
|
2020-11-24 10:55:20 +08:00
|
|
|
icmpRunTraceRouteInternal(test, gap, toStop, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func icmpRunMyTraceRoute(test *ethrTest, gap time.Duration, toStop chan int) {
|
|
|
|
icmpRunTraceRouteInternal(test, gap, toStop, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func icmpRunTraceRouteInternal(test *ethrTest, gap time.Duration, toStop chan int, mtrMode bool) {
|
2020-11-10 21:37:52 +08:00
|
|
|
gHop = make([]ethrHopData, gMaxHops)
|
2020-11-24 10:55:20 +08:00
|
|
|
dstIPAddr, _, err := ethrLookupIP(test.session.remoteIP)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
|
|
|
toStop <- interrupt
|
|
|
|
return
|
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
err = icmpDiscoverHops(test, dstIPAddr, mtrMode)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
2020-11-24 10:55:20 +08:00
|
|
|
ui.printErr("Destination %s is not responding to ICMP Echo.", test.session.remoteIP)
|
2020-11-10 21:37:52 +08:00
|
|
|
ui.printErr("Terminating tracing...")
|
|
|
|
toStop <- interrupt
|
|
|
|
return
|
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
if !mtrMode {
|
|
|
|
toStop <- done
|
|
|
|
return
|
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
for i := 0; i < gCurHops; i++ {
|
2020-11-24 10:55:20 +08:00
|
|
|
if gHop[i].addr != "" {
|
2020-11-10 21:37:52 +08:00
|
|
|
go icmpProbeHop(test, gap, i, dstIPAddr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func copyInitialHopData(hop int, hopData ethrHopData) {
|
|
|
|
gHop[hop].addr = hopData.addr
|
|
|
|
gHop[hop].best = hopData.last
|
|
|
|
gHop[hop].name = hopData.name
|
2020-11-24 10:55:20 +08:00
|
|
|
gHop[hop].fullName = hopData.fullName
|
|
|
|
}
|
|
|
|
|
|
|
|
func genHopData(hopData *ethrHopData, peerAddr string, elapsed time.Duration) {
|
|
|
|
hopData.addr = peerAddr
|
|
|
|
hopData.last = elapsed
|
|
|
|
if hopData.best > elapsed {
|
|
|
|
hopData.best = elapsed
|
|
|
|
}
|
|
|
|
if hopData.worst < elapsed {
|
|
|
|
hopData.worst = elapsed
|
|
|
|
}
|
|
|
|
hopData.total += elapsed
|
|
|
|
hopData.rcvd++
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func lookupHopName(addr string) (string, string) {
|
2020-11-10 21:37:52 +08:00
|
|
|
name := ""
|
2020-11-24 10:55:20 +08:00
|
|
|
tname := ""
|
|
|
|
if addr == "" {
|
|
|
|
return tname, name
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
names, err := net.LookupAddr(addr)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err == nil && len(names) > 0 {
|
|
|
|
name = names[0]
|
|
|
|
sz := len(name)
|
|
|
|
|
|
|
|
if sz > 0 && name[sz-1] == '.' {
|
|
|
|
name = name[:sz-1]
|
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
tname = truncateStringFromEnd(name, 16)
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
return tname, name
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func icmpDiscoverHops(test *ethrTest, dstIPAddr net.IPAddr, mtrMode bool) error {
|
|
|
|
if test.session.remoteIP == dstIPAddr.String() {
|
|
|
|
ui.printMsg("Tracing route to %s over %d hops:", test.session.remoteIP, gMaxHops)
|
2020-11-10 21:37:52 +08:00
|
|
|
} else {
|
2020-11-24 10:55:20 +08:00
|
|
|
ui.printMsg("Tracing route to %s (%s) over %d hops:", test.session.remoteIP, dstIPAddr.String(), gMaxHops)
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
for i := 0; i < gMaxHops; i++ {
|
|
|
|
var hopData ethrHopData
|
2020-11-24 10:55:20 +08:00
|
|
|
err, isLast := icmpProbe(test, dstIPAddr, time.Second*2, "", &hopData, i, 1)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err == nil {
|
2020-11-24 10:55:20 +08:00
|
|
|
hopData.name, hopData.fullName = lookupHopName(hopData.addr)
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
if hopData.addr != "" {
|
|
|
|
if mtrMode {
|
|
|
|
ui.printMsg("%2d.|--%s", i+1, hopData.addr+" ["+hopData.fullName+"]")
|
|
|
|
} else {
|
|
|
|
ui.printMsg("%2d.|--%-70s %s", i+1, hopData.addr+" ["+hopData.fullName+"]", durationToString(hopData.last))
|
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
} else {
|
2020-11-24 10:55:20 +08:00
|
|
|
ui.printMsg("%2d.|--%s", i+1, "???")
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
copyInitialHopData(i, hopData)
|
|
|
|
if isLast {
|
|
|
|
gCurHops = i + 1
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return os.ErrNotExist
|
|
|
|
}
|
|
|
|
|
|
|
|
func icmpProbeHop(test *ethrTest, gap time.Duration, hop int, dstIPAddr net.IPAddr) {
|
|
|
|
seq := 0
|
|
|
|
ExitForLoop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-test.done:
|
|
|
|
break ExitForLoop
|
|
|
|
default:
|
|
|
|
t0 := time.Now()
|
2020-11-24 10:55:20 +08:00
|
|
|
err, _ := icmpProbe(test, dstIPAddr, time.Second, gHop[hop].addr, &gHop[hop], hop, seq)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err == nil {
|
|
|
|
}
|
|
|
|
seq++
|
|
|
|
t1 := time.Since(t0)
|
|
|
|
if t1 < gap {
|
|
|
|
time.Sleep(gap - t1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
/*
|
|
|
|
func icmpNewConn(localAddr string) (*icmp.PacketConn, error) {
|
|
|
|
c, err := icmp.ListenPacket("ip4:icmp", localAddr)
|
|
|
|
if err != nil {
|
|
|
|
ui.printErr("Failed to listen for ICMP on local address %v. Msg: %v.", localAddr, err.Error())
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
func icmpProbe(test *ethrTest, dstIPAddr net.IPAddr, icmpTimeout time.Duration, hopIP string, hopData *ethrHopData, hop, seq int) (error, bool) {
|
2020-11-10 21:37:52 +08:00
|
|
|
isLast := false
|
2020-11-24 10:55:20 +08:00
|
|
|
echoMsg := fmt.Sprintf("Hello: Ethr - %v", hop)
|
2020-11-10 21:37:52 +08:00
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
c, err := IcmpNewConn(test.remoteIP)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
2020-11-24 10:55:20 +08:00
|
|
|
ui.printErr("Failed to create ICMP connection. Error: %v", err)
|
2020-11-10 21:37:52 +08:00
|
|
|
return err, isLast
|
|
|
|
}
|
|
|
|
defer c.Close()
|
2020-11-24 10:55:20 +08:00
|
|
|
start, wb, err := icmpSendMsg(c, dstIPAddr, hop, seq, echoMsg, icmpTimeout)
|
|
|
|
if err != nil {
|
|
|
|
return err, isLast
|
|
|
|
}
|
|
|
|
hopData.sent++
|
|
|
|
neededSeq := hop<<8 | seq
|
|
|
|
peerAddr, isLast, err := icmpListenForSpecific4(c, ICMP, icmpTimeout, hopIP, wb[4:8], []byte(echoMsg), neededSeq)
|
|
|
|
if err != nil {
|
|
|
|
hopData.lost++
|
|
|
|
ui.printDbg("Failed to receive ICMP reply packet. Error: %v", err)
|
|
|
|
return err, isLast
|
|
|
|
}
|
|
|
|
elapsed := time.Since(start)
|
|
|
|
genHopData(hopData, peerAddr, elapsed)
|
|
|
|
return nil, isLast
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
ProtocolICMP = 1 // ICMP for IPv4
|
|
|
|
ProtocolIPv6ICMP = 58 // ICMP for IPv6
|
|
|
|
)
|
2020-11-10 21:37:52 +08:00
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func icmpSendMsg(c net.PacketConn, dstIPAddr net.IPAddr, hop, seq int, body string, timeout time.Duration) (time.Time, []byte, error) {
|
|
|
|
cIPv4 := ipv4.NewPacketConn(c)
|
|
|
|
start := time.Now()
|
|
|
|
err := cIPv4.SetTTL(hop + 1)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
|
|
|
ui.printErr("Failed to set TTL. Error: %v", err)
|
2020-11-24 10:55:20 +08:00
|
|
|
return start, nil, err
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
err = c.SetDeadline(time.Now().Add(timeout))
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
|
|
|
ui.printErr("Failed to set Deadline. Error: %v", err)
|
2020-11-24 10:55:20 +08:00
|
|
|
return start, nil, err
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pid := os.Getpid() & 0xffff
|
2020-11-24 10:55:20 +08:00
|
|
|
pid = 9999
|
2020-11-10 21:37:52 +08:00
|
|
|
wm := icmp.Message{
|
|
|
|
Type: ipv4.ICMPTypeEcho, Code: 0,
|
|
|
|
Body: &icmp.Echo{
|
2020-11-24 10:55:20 +08:00
|
|
|
ID: pid, Seq: hop<<8 | seq,
|
|
|
|
Data: []byte(body),
|
2020-11-10 21:37:52 +08:00
|
|
|
},
|
|
|
|
}
|
|
|
|
wb, err := wm.Marshal(nil)
|
|
|
|
if err != nil {
|
|
|
|
ui.printErr("Failed to Marshal data. Error: %v", err)
|
2020-11-24 10:55:20 +08:00
|
|
|
return start, nil, err
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
start = time.Now()
|
2020-11-10 21:37:52 +08:00
|
|
|
if _, err := c.WriteTo(wb, &dstIPAddr); err != nil {
|
|
|
|
ui.printErr("Failed to send ICMP data. Error: %v", err)
|
2020-11-24 10:55:20 +08:00
|
|
|
return start, nil, err
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
return start, wb, nil
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func icmpListenForSpecific4(c net.PacketConn, proto EthrProtocol, timeout time.Duration, neededPeer string, neededSig []byte, neededIcmpBody []byte, neededIcmpSeq int) (string, bool, error) {
|
|
|
|
cIPv4 := ipv4.NewPacketConn(c)
|
|
|
|
peerAddr := ""
|
2020-11-10 21:37:52 +08:00
|
|
|
isLast := false
|
2020-11-24 10:55:20 +08:00
|
|
|
err := cIPv4.SetDeadline(time.Now().Add(timeout))
|
|
|
|
if err != nil {
|
|
|
|
ui.printErr("Failed to set Deadline. Error: %v", err)
|
|
|
|
return peerAddr, isLast, err
|
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
for {
|
|
|
|
b := make([]byte, 1500)
|
2020-11-24 10:55:20 +08:00
|
|
|
n, peer, err := c.ReadFrom(b)
|
2020-11-10 21:37:52 +08:00
|
|
|
if err != nil {
|
2020-11-24 10:55:20 +08:00
|
|
|
if proto == ICMP {
|
|
|
|
// In case of non-ICMP TraceRoute, it is expected that no packet is received
|
|
|
|
// in some case, e.g. when packet reach final hop and TCP connection establishes.
|
|
|
|
ui.printDbg("Failed to receive ICMP packet. Error: %v", err)
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
return peerAddr, isLast, err
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
if n == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
ui.printDbg("Packet:\n%s", hex.Dump(b[:n]))
|
|
|
|
ui.printDbg("Finding Pattern\n%v", hex.Dump(neededSig[:4]))
|
|
|
|
peerAddr = peer.String()
|
|
|
|
if neededPeer != "" && peerAddr != neededPeer {
|
2020-11-10 21:37:52 +08:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
icmpMsg, err := icmp.ParseMessage(ProtocolICMP, b[:n])
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if icmpMsg.Type == ipv4.ICMPTypeTimeExceeded {
|
|
|
|
body := icmpMsg.Body.(*icmp.TimeExceeded).Data
|
2020-11-24 10:55:20 +08:00
|
|
|
index := bytes.Index(body, neededSig[:4])
|
2020-11-10 21:37:52 +08:00
|
|
|
if index > 0 {
|
2020-11-24 10:55:20 +08:00
|
|
|
if proto == TCP {
|
|
|
|
return peerAddr, isLast, nil
|
|
|
|
} else if proto == ICMP {
|
|
|
|
if index < 4 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
innerIcmpMsg, _ := icmp.ParseMessage(ProtocolICMP, body[index-4:])
|
|
|
|
switch innerIcmpMsg.Body.(type) {
|
|
|
|
case *icmp.Echo:
|
|
|
|
seq := innerIcmpMsg.Body.(*icmp.Echo).Seq
|
|
|
|
if seq == neededIcmpSeq {
|
|
|
|
return peerAddr, isLast, nil
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// Ignore as this is not the right ICMP packet.
|
|
|
|
ui.printDbg("Unable to recognize packet.")
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
}
|
2020-11-24 10:55:20 +08:00
|
|
|
} else {
|
|
|
|
ui.printDbg("Pattern %v not found.", hex.Dump(neededSig[:4]))
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
if proto == ICMP && icmpMsg.Type == ipv4.ICMPTypeEchoReply {
|
2020-11-10 21:37:52 +08:00
|
|
|
echo := icmpMsg.Body.(*icmp.Echo)
|
|
|
|
ethrUnused(echo)
|
|
|
|
b, _ := icmpMsg.Body.Marshal(1)
|
2020-11-24 10:55:20 +08:00
|
|
|
if string(b[4:]) != string(neededIcmpBody) {
|
2020-11-10 21:37:52 +08:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
isLast = true
|
2020-11-24 10:55:20 +08:00
|
|
|
return peerAddr, isLast, nil
|
2020-11-10 21:37:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-24 10:55:20 +08:00
|
|
|
func runUDPBandwidthAndPpsTest(test *ethrTest) {
|
|
|
|
for th := uint32(0); th < test.testParam.NumThreads; th++ {
|
|
|
|
go func() {
|
|
|
|
buff := make([]byte, test.testParam.BufferSize)
|
|
|
|
conn, err := net.Dial(udp(ipVer), test.dialAddr)
|
|
|
|
if err != nil {
|
|
|
|
ui.printDbg("Unable to dial UDP, error: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
ec := test.newConn(conn)
|
|
|
|
rserver, rport, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
|
|
|
lserver, lport, _ := net.SplitHostPort(conn.LocalAddr().String())
|
|
|
|
ui.printMsg("[%3d] local %s port %s connected to %s port %s",
|
|
|
|
ec.fd, lserver, lport, rserver, rport)
|
|
|
|
blen := len(buff)
|
|
|
|
ExitForLoop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-test.done:
|
|
|
|
break ExitForLoop
|
|
|
|
default:
|
|
|
|
n, err := conn.Write(buff)
|
|
|
|
if err != nil {
|
|
|
|
ui.printDbg("%v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if n < blen {
|
|
|
|
ui.printDbg("Partial write: %d", n)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
atomic.AddUint64(&ec.bw, uint64(n))
|
|
|
|
atomic.AddUint64(&ec.pps, 1)
|
|
|
|
atomic.AddUint64(&test.testResult.bw, uint64(n))
|
|
|
|
atomic.AddUint64(&test.testResult.pps, 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 21:37:52 +08:00
|
|
|
/*
|
2018-12-12 00:35:20 +08:00
|
|
|
func runHTTPBandwidthTest(test *ethrTest) {
|
2020-11-24 10:55:20 +08:00
|
|
|
uri := test.session.remoteIP
|
2019-01-08 01:15:09 +08:00
|
|
|
ui.printMsg("uri=%s", uri)
|
2018-12-03 04:39:13 +08:00
|
|
|
uri = "http://" + uri + ":" + httpBandwidthPort
|
|
|
|
for th := uint32(0); th < test.testParam.NumThreads; th++ {
|
|
|
|
buff := make([]byte, test.testParam.BufferSize)
|
|
|
|
for i := uint32(0); i < test.testParam.BufferSize; i++ {
|
|
|
|
buff[i] = 'x'
|
|
|
|
}
|
|
|
|
tr := &http.Transport{DisableCompression: true}
|
|
|
|
client := &http.Client{Transport: tr}
|
2019-01-08 01:15:09 +08:00
|
|
|
go runHTTPandHTTPSBandwidthTest(test, client, uri, buff)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func runHTTPSBandwidthTest(test *ethrTest) {
|
2020-11-24 10:55:20 +08:00
|
|
|
uri := test.session.remoteIP
|
2019-01-08 01:15:09 +08:00
|
|
|
uri = "https://" + uri + ":" + httpsBandwidthPort
|
|
|
|
for th := uint32(0); th < test.testParam.NumThreads; th++ {
|
|
|
|
buff := make([]byte, test.testParam.BufferSize)
|
|
|
|
for i := uint32(0); i < test.testParam.BufferSize; i++ {
|
|
|
|
buff[i] = 'x'
|
|
|
|
}
|
|
|
|
c, err := x509.ParseCertificate(gCert)
|
|
|
|
if err != nil {
|
2019-09-30 20:43:51 +08:00
|
|
|
ui.printErr("runHTTPSBandwidthTest: failed to parse certificate: %v", err)
|
2019-01-08 01:15:09 +08:00
|
|
|
}
|
|
|
|
clientCertPool := x509.NewCertPool()
|
|
|
|
clientCertPool.AddCert(c)
|
|
|
|
|
|
|
|
tlsConfig := &tls.Config{
|
2019-02-03 23:53:54 +08:00
|
|
|
InsecureSkipVerify: gIgnoreCert,
|
2019-01-08 01:15:09 +08:00
|
|
|
// Certificates: []tls.Certificate{cert},
|
|
|
|
RootCAs: clientCertPool,
|
|
|
|
}
|
|
|
|
//tlsConfig.BuildNameToCertificate()
|
|
|
|
tr := &http.Transport{DisableCompression: true, TLSClientConfig: tlsConfig}
|
|
|
|
client := &http.Client{Transport: tr}
|
|
|
|
go runHTTPandHTTPSBandwidthTest(test, client, uri, buff)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func runHTTPandHTTPSBandwidthTest(test *ethrTest, client *http.Client, uri string, buff []byte) {
|
|
|
|
ExitForLoop:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-test.done:
|
|
|
|
break ExitForLoop
|
|
|
|
default:
|
|
|
|
// response, err := http.Get(uri)
|
|
|
|
response, err := client.Post(uri, "text/plain", bytes.NewBuffer(buff))
|
|
|
|
if err != nil {
|
|
|
|
ui.printDbg("Error in HTTP request: %v", err)
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
ui.printDbg("Status received: %v", response.StatusCode)
|
|
|
|
if response.StatusCode != http.StatusOK {
|
|
|
|
ui.printDbg("Error in HTTP request, received status: %v", response.StatusCode)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
contents, err := ioutil.ReadAll(response.Body)
|
|
|
|
response.Body.Close()
|
|
|
|
if err != nil {
|
|
|
|
ui.printDbg("Error in receving HTTP response: %v", err)
|
|
|
|
continue
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
2019-01-08 01:15:09 +08:00
|
|
|
ethrUnused(contents)
|
|
|
|
// ui.printDbg("%s", string(contents))
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
2019-01-08 01:15:09 +08:00
|
|
|
atomic.AddUint64(&test.testResult.data, uint64(test.testParam.BufferSize))
|
|
|
|
}
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
}
|
2020-11-10 21:37:52 +08:00
|
|
|
*/
|