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 (
|
|
|
|
"container/list"
|
|
|
|
"encoding/gob"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrTestType represents the test type.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrTestType uint32
|
|
|
|
|
|
|
|
const (
|
2019-01-02 13:12:26 +08:00
|
|
|
// All represents all tests - For now only applicable for servers
|
|
|
|
All EthrTestType = iota
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// Bandwidth represents the bandwidth test.
|
2019-01-02 13:12:26 +08:00
|
|
|
Bandwidth
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// Cps represents connections/s test.
|
2018-12-03 04:39:13 +08:00
|
|
|
Cps
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// Pps represents packets/s test.
|
2018-12-03 04:39:13 +08:00
|
|
|
Pps
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// Latency represents the latency test.
|
2018-12-03 04:39:13 +08:00
|
|
|
Latency
|
2019-01-02 13:12:26 +08:00
|
|
|
|
|
|
|
// ConnLatency represents connection setup latency.
|
|
|
|
ConnLatency
|
2018-12-03 04:39:13 +08:00
|
|
|
)
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrProtocol represents the network protocol.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrProtocol uint32
|
|
|
|
|
|
|
|
const (
|
2018-12-11 23:28:33 +08:00
|
|
|
// TCP represents the tcp protocol.
|
|
|
|
TCP EthrProtocol = iota
|
|
|
|
|
|
|
|
// UDP represents the udp protocol.
|
|
|
|
UDP
|
|
|
|
|
|
|
|
// HTTP represents using http protocol.
|
|
|
|
HTTP
|
|
|
|
|
|
|
|
// HTTPS represents using https protocol.
|
|
|
|
HTTPS
|
|
|
|
|
|
|
|
// ICMP represents the icmp protocol.
|
|
|
|
ICMP
|
2018-12-03 04:39:13 +08:00
|
|
|
)
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrTestID represents the test id.
|
|
|
|
type EthrTestID struct {
|
|
|
|
// Protocol represents the protocol this test uses.
|
2018-12-03 04:39:13 +08:00
|
|
|
Protocol EthrProtocol
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// Type represents the test type this test uses.
|
|
|
|
Type EthrTestType
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrMsgType represents the message type.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrMsgType uint32
|
|
|
|
|
|
|
|
const (
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrInv represents the Inv message.
|
2018-12-03 04:39:13 +08:00
|
|
|
EthrInv EthrMsgType = iota
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// EthrSyn represents the Syn message.
|
2018-12-03 04:39:13 +08:00
|
|
|
EthrSyn
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// EthrAck represents the Ack message.
|
2018-12-03 04:39:13 +08:00
|
|
|
EthrAck
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// EthrFin represents the Fin message.
|
2018-12-03 04:39:13 +08:00
|
|
|
EthrFin
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// EthrBgn represents the Bgn message.
|
2018-12-03 04:39:13 +08:00
|
|
|
EthrBgn
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// EthrEnd represents the End message.
|
2018-12-03 04:39:13 +08:00
|
|
|
EthrEnd
|
|
|
|
)
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrMsgVer represents the message version.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrMsgVer uint32
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrMsg represents the message entity.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrMsg struct {
|
2018-12-11 23:28:33 +08:00
|
|
|
// Version represents the message version.
|
2018-12-03 04:39:13 +08:00
|
|
|
Version EthrMsgVer
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// Type represents the message type.
|
|
|
|
Type EthrMsgType
|
|
|
|
|
|
|
|
// Syn represents the Syn value.
|
|
|
|
Syn *EthrMsgSyn
|
|
|
|
|
|
|
|
// Ack represents the Ack value.
|
|
|
|
Ack *EthrMsgAck
|
|
|
|
|
|
|
|
// Fin represents the Fin value.
|
|
|
|
Fin *EthrMsgFin
|
|
|
|
|
|
|
|
// Bgn represents the Bgn value.
|
|
|
|
Bgn *EthrMsgBgn
|
|
|
|
|
|
|
|
// End represents the End value.
|
|
|
|
End *EthrMsgEnd
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrMsgSyn represents the Syn entity.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrMsgSyn struct {
|
2018-12-11 23:28:33 +08:00
|
|
|
// TestParam represents the test parameters.
|
2018-12-03 04:39:13 +08:00
|
|
|
TestParam EthrTestParam
|
|
|
|
}
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrMsgAck represents the Ack entity.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrMsgAck struct {
|
|
|
|
}
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrMsgFin represents the Fin entity.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrMsgFin struct {
|
2018-12-11 23:28:33 +08:00
|
|
|
// Message represents the message body.
|
2018-12-03 04:39:13 +08:00
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrMsgBgn represents the Bgn entity.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrMsgBgn struct {
|
2018-12-11 23:28:33 +08:00
|
|
|
// UDPPort represents the udp port.
|
|
|
|
UDPPort string
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrMsgEnd represents the End entity.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrMsgEnd struct {
|
2018-12-11 23:28:33 +08:00
|
|
|
// Message represents the message body.
|
2018-12-03 04:39:13 +08:00
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
// EthrTestParam represents the parameters used for the test.
|
2018-12-03 04:39:13 +08:00
|
|
|
type EthrTestParam struct {
|
2018-12-11 23:28:33 +08:00
|
|
|
// TestID represents the test id of this test.
|
|
|
|
TestID EthrTestID
|
|
|
|
|
|
|
|
// NumThreads represents how many threads are used for the test.
|
2018-12-03 04:39:13 +08:00
|
|
|
NumThreads uint32
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// BufferSize represents the buffer size.
|
2018-12-03 04:39:13 +08:00
|
|
|
BufferSize uint32
|
2018-12-11 23:28:33 +08:00
|
|
|
|
|
|
|
// RttCount represents the rtt count.
|
|
|
|
RttCount uint32
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
type ethrTestResult struct {
|
|
|
|
data uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
type ethrTest struct {
|
|
|
|
isActive bool
|
|
|
|
session *ethrSession
|
|
|
|
ctrlConn net.Conn
|
|
|
|
enc *gob.Encoder
|
|
|
|
dec *gob.Decoder
|
2018-12-11 00:06:37 +08:00
|
|
|
rcvdMsgs chan *EthrMsg
|
2018-12-03 04:39:13 +08:00
|
|
|
testParam EthrTestParam
|
|
|
|
testResult ethrTestResult
|
|
|
|
done chan struct{}
|
|
|
|
connList *list.List
|
|
|
|
}
|
|
|
|
|
2019-01-02 13:12:26 +08:00
|
|
|
type ethrMode uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
ethrModeInv ethrMode = iota
|
|
|
|
ethrModeServer
|
|
|
|
ethrModeClient
|
|
|
|
ethrModeExtClient
|
|
|
|
)
|
|
|
|
|
2018-12-03 04:39:13 +08:00
|
|
|
type ethrConn struct {
|
|
|
|
test *ethrTest
|
|
|
|
conn net.Conn
|
|
|
|
elem *list.Element
|
|
|
|
fd uintptr
|
|
|
|
data uint64
|
|
|
|
retrans uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
type ethrSession struct {
|
|
|
|
remoteAddr string
|
|
|
|
testCount uint32
|
2018-12-11 23:28:33 +08:00
|
|
|
tests map[EthrTestID]*ethrTest
|
2018-12-03 04:39:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var gSessions = make(map[string]*ethrSession)
|
|
|
|
var gSessionKeys = make([]string, 0)
|
|
|
|
var gSessionLock sync.RWMutex
|
|
|
|
|
|
|
|
func deleteKey(key string) {
|
|
|
|
i := 0
|
|
|
|
for _, x := range gSessionKeys {
|
|
|
|
if x != key {
|
|
|
|
gSessionKeys[i] = x
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gSessionKeys = gSessionKeys[:i]
|
|
|
|
}
|
|
|
|
|
|
|
|
func newTest(remoteAddr string, conn net.Conn, testParam EthrTestParam, enc *gob.Encoder, dec *gob.Decoder) (*ethrTest, error) {
|
|
|
|
gSessionLock.Lock()
|
|
|
|
defer gSessionLock.Unlock()
|
|
|
|
var session *ethrSession
|
|
|
|
session, found := gSessions[remoteAddr]
|
|
|
|
if !found {
|
|
|
|
session = ðrSession{}
|
|
|
|
session.remoteAddr = remoteAddr
|
2018-12-11 23:28:33 +08:00
|
|
|
session.tests = make(map[EthrTestID]*ethrTest)
|
2018-12-03 04:39:13 +08:00
|
|
|
gSessions[remoteAddr] = session
|
|
|
|
gSessionKeys = append(gSessionKeys, remoteAddr)
|
|
|
|
}
|
|
|
|
|
2018-12-11 23:28:33 +08:00
|
|
|
test, found := session.tests[testParam.TestID]
|
2018-12-03 04:39:13 +08:00
|
|
|
if found {
|
|
|
|
return nil, os.ErrExist
|
|
|
|
}
|
|
|
|
session.testCount++
|
|
|
|
test = ðrTest{}
|
|
|
|
test.session = session
|
|
|
|
test.ctrlConn = conn
|
|
|
|
test.enc = enc
|
|
|
|
test.dec = dec
|
2018-12-11 00:06:37 +08:00
|
|
|
test.rcvdMsgs = make(chan *EthrMsg)
|
2018-12-03 04:39:13 +08:00
|
|
|
test.testParam = testParam
|
|
|
|
test.done = make(chan struct{})
|
|
|
|
test.connList = list.New()
|
2018-12-11 23:28:33 +08:00
|
|
|
session.tests[testParam.TestID] = test
|
2018-12-03 04:39:13 +08:00
|
|
|
|
|
|
|
return test, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteTest(test *ethrTest) {
|
|
|
|
gSessionLock.Lock()
|
|
|
|
defer gSessionLock.Unlock()
|
|
|
|
session := test.session
|
2018-12-11 23:28:33 +08:00
|
|
|
testID := test.testParam.TestID
|
2018-12-03 04:39:13 +08:00
|
|
|
//
|
|
|
|
// TODO fix this, we need to decide where to close this, inside this
|
|
|
|
// function or by the caller. The reason we may need it to be done by
|
|
|
|
// the caller is, because done is used for test done notification and
|
|
|
|
// there may be some time after done that consumers are still accessing it
|
|
|
|
//
|
|
|
|
// Since we have not added any refCounting on test object, we are doing
|
|
|
|
// hacky timeout based solution by closing "done" outside and sleeping
|
|
|
|
// for sufficient time. ugh!
|
|
|
|
//
|
|
|
|
// close(test.done)
|
|
|
|
// test.ctrlConn.Close()
|
|
|
|
// test.session = nil
|
|
|
|
// test.connList = test.connList.Init()
|
|
|
|
//
|
2018-12-11 23:28:33 +08:00
|
|
|
delete(session.tests, testID)
|
2018-12-03 04:39:13 +08:00
|
|
|
session.testCount--
|
|
|
|
|
|
|
|
if session.testCount == 0 {
|
|
|
|
deleteKey(session.remoteAddr)
|
|
|
|
delete(gSessions, session.remoteAddr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getTest(remoteAddr string, proto EthrProtocol, testType EthrTestType) (test *ethrTest) {
|
|
|
|
test = nil
|
|
|
|
gSessionLock.RLock()
|
|
|
|
defer gSessionLock.RUnlock()
|
|
|
|
session, found := gSessions[remoteAddr]
|
|
|
|
if !found {
|
|
|
|
return
|
|
|
|
}
|
2018-12-11 23:28:33 +08:00
|
|
|
test, found = session.tests[EthrTestID{proto, testType}]
|
2018-12-03 04:39:13 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (test *ethrTest) newConn(conn net.Conn) (ec *ethrConn) {
|
|
|
|
gSessionLock.Lock()
|
|
|
|
defer gSessionLock.Unlock()
|
|
|
|
ec = ðrConn{}
|
|
|
|
ec.test = test
|
|
|
|
ec.conn = conn
|
|
|
|
ec.fd = getFd(conn)
|
|
|
|
ec.elem = test.connList.PushBack(ec)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (test *ethrTest) delConn(conn net.Conn) {
|
|
|
|
for e := test.connList.Front(); e != nil; e = e.Next() {
|
|
|
|
ec := e.Value.(*ethrConn)
|
|
|
|
if ec.conn == conn {
|
|
|
|
test.connList.Remove(e)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (test *ethrTest) connListDo(f func(*ethrConn)) {
|
|
|
|
gSessionLock.RLock()
|
|
|
|
defer gSessionLock.RUnlock()
|
|
|
|
for e := test.connList.Front(); e != nil; e = e.Next() {
|
|
|
|
ec := e.Value.(*ethrConn)
|
|
|
|
f(ec)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-11 00:06:37 +08:00
|
|
|
func watchControlChannel(test *ethrTest, waitForChannelStop chan bool) {
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
ethrMsg := recvSessionMsg(test.dec)
|
|
|
|
if ethrMsg.Type == EthrInv {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
test.rcvdMsgs <- ethrMsg
|
2018-12-11 01:02:50 +08:00
|
|
|
ui.printDbg("%v", ethrMsg)
|
2018-12-11 00:06:37 +08:00
|
|
|
}
|
|
|
|
waitForChannelStop <- true
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2018-12-03 04:39:13 +08:00
|
|
|
func recvSessionMsg(dec *gob.Decoder) (ethrMsg *EthrMsg) {
|
|
|
|
ethrMsg = &EthrMsg{}
|
|
|
|
err := dec.Decode(ethrMsg)
|
|
|
|
if err != nil {
|
|
|
|
ui.printDbg("Error receiving message on control channel: %v", err)
|
|
|
|
ethrMsg.Type = EthrInv
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func sendSessionMsg(enc *gob.Encoder, ethrMsg *EthrMsg) error {
|
|
|
|
err := enc.Encode(ethrMsg)
|
|
|
|
if err != nil {
|
|
|
|
ui.printDbg("Error sending message on control channel. Message: %v, Error: %v", ethrMsg, err)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func createAckMsg() (ethrMsg *EthrMsg) {
|
|
|
|
ethrMsg = &EthrMsg{Version: 0, Type: EthrAck}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func createFinMsg(message string) (ethrMsg *EthrMsg) {
|
|
|
|
ethrMsg = &EthrMsg{Version: 0, Type: EthrFin}
|
|
|
|
ethrMsg.Fin = &EthrMsgFin{}
|
|
|
|
ethrMsg.Fin.Message = message
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func createSynMsg(testParam EthrTestParam) (ethrMsg *EthrMsg) {
|
|
|
|
ethrMsg = &EthrMsg{Version: 0, Type: EthrSyn}
|
|
|
|
ethrMsg.Syn = &EthrMsgSyn{}
|
|
|
|
ethrMsg.Syn.TestParam = testParam
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func createBgnMsg(port string) (ethrMsg *EthrMsg) {
|
|
|
|
ethrMsg = &EthrMsg{Version: 0, Type: EthrBgn}
|
|
|
|
ethrMsg.Bgn = &EthrMsgBgn{}
|
2018-12-11 23:28:33 +08:00
|
|
|
ethrMsg.Bgn.UDPPort = port
|
2018-12-03 04:39:13 +08:00
|
|
|
return
|
|
|
|
}
|