From d7b812494e4d551d12545187a1f4d8190abef30c Mon Sep 17 00:00:00 2001 From: khalid aldughayem Date: Sun, 11 Aug 2019 22:07:32 +0200 Subject: [PATCH 1/6] Added find max bandwidth feature. the flag to use it is -findMax and specify a server address, it will run multiple 3 seconds tests to find the maximum bandwidth. --- bwtester/bwtestclient/bwtestclient.go | 463 +++++++++++++++++++++++++- 1 file changed, 462 insertions(+), 1 deletion(-) diff --git a/bwtester/bwtestclient/bwtestclient.go b/bwtester/bwtestclient/bwtestclient.go index 8dcf59a0..6b6cb437 100644 --- a/bwtester/bwtestclient/bwtestclient.go +++ b/bwtester/bwtestclient/bwtestclient.go @@ -1,4 +1,4 @@ -// bwtestserver application +// bwtestclient application // For more documentation on the application see: // https://github.com/netsec-ethz/scion-apps/blob/master/README.md // https://github.com/netsec-ethz/scion-apps/blob/master/bwtester/README.md @@ -32,6 +32,7 @@ const ( DefaultBW = 3000 WildcardChar = "?" GracePeriodSync time.Duration = time.Millisecond * 10 + MinBandwidth = 5000 // 5 kbps is the minimum bandwidth we can test ) var ( @@ -277,6 +278,8 @@ func main() { tzero time.Time // initialized to "zero" time receiveDone sync.Mutex // used to signal when the HandleDCConnReceive goroutine has completed + + maxBandwidth bool ) flag.StringVar(&sciondPath, "sciond", "", "Path to sciond socket") @@ -291,6 +294,7 @@ func main() { flag.BoolVar(&interactive, "i", false, "Interactive mode") flag.StringVar(&pathAlgo, "pathAlgo", "", "Path selection algorithm / metric (\"shortest\", \"mtu\")") flag.BoolVar(&useIPv6, "6", false, "Use IPv6") + flag.BoolVar(&maxBandwidth, "findMax", false, "Find the maximum bandwidth achievable") flag.Parse() flagset := make(map[string]bool) @@ -358,6 +362,10 @@ func main() { scionutil.InitSCION(clientCCAddr) } + if maxBandwidth { + findMaxBandwidth(interactive, pathAlgo, clientCCAddr, serverCCAddr) + } + CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) Check(err) @@ -573,3 +581,456 @@ func main() { fmt.Println("Error, could not fetch server results, MaxTries attempted without success.") } + +func findMaxBandwidth(interactive bool, pathAlgo string, clientCCAddr, serverCCAddr *snet.Addr) { + + var ( + tzero time.Time + receiveDone sync.Mutex + + clientOldBw, serverOldBw, clientThreshold, serverThreshold int64 + serverMax, clientMax bool + finished bool + // run is used to hold the number of the current run. Also to use for the DC Connection port number + // it is added to the CC connection port number on each run. + run uint16 + ) + + clientBw := int64(512000) + serverBw := clientBw + + // Make sure the port number for the client's CC address is zero so a free port is + // picked when the connection fails + clientCCAddr.Host.L4 = addr.NewL4UDPInfo(0) + + // Setup the paths + var pathEntry *sciond.PathReplyEntry + if !serverCCAddr.IA.Eq(clientCCAddr.IA) { + if interactive { + pathEntry = scionutil.ChoosePathInteractive(clientCCAddr, serverCCAddr) + } else { + var metric int + if pathAlgo == "mtu" { + metric = scionutil.MTU + } else if pathAlgo == "shortest" { + metric = scionutil.Shortest + } + pathEntry = scionutil.ChoosePathByMetric(metric, clientCCAddr, serverCCAddr) + } + if pathEntry == nil { + LogFatal("No paths available to remote destination") + } + serverCCAddr.Path = spath.New(pathEntry.Path.FwdPath) + serverCCAddr.Path.InitOffsets() + serverCCAddr.NextHop, _ = pathEntry.HostInfo.Overlay() + } else { + scionutil.InitSCION(clientCCAddr) + } + + CCConn, err := snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) + Check(err) + defer CCConn.Close() + + // get the port used by clientCC after it bound to the dispatcher (because it might be 0) + clientPort := uint((CCConn.LocalAddr()).(*snet.Addr).Host.L4.Port()) + serverPort := serverCCAddr.Host.L4.Port() + + //Address of client data channel (DC) + clientDCAddr := &snet.Addr{IA: clientCCAddr.IA, Host: &addr.AppAddr{ + L3: clientCCAddr.Host.L3, L4: addr.NewL4UDPInfo(uint16(clientPort) + 1)}} + // Address of server data channel (DC) + serverDCAddr := &snet.Addr{IA: serverCCAddr.IA, Host: &addr.AppAddr{ + L3: serverCCAddr.Host.L3, L4: addr.NewL4UDPInfo(uint16(serverPort) + 1)}} + + // Set path on data connection + if !serverDCAddr.IA.Eq(clientDCAddr.IA) { + serverDCAddr.Path = spath.New(pathEntry.Path.FwdPath) + serverDCAddr.Path.InitOffsets() + serverDCAddr.NextHop, _ = pathEntry.HostInfo.Overlay() + fmt.Printf("Client DC \tNext Hop %v\tServer Host %v\n", + serverDCAddr.NextHop, serverDCAddr.Host) + } + + // find the max bandwidth for the client to server connection first + for !finished { + run++ + fmt.Println(strings.Repeat("#", 15)) + fmt.Println("Run: ", run) + + //Data channel connection + DCConn, err := snet.DialSCION(overlayType, clientDCAddr, serverDCAddr) + Check(err) + + // update default packet size to max MTU on the selected path + if pathEntry != nil { + InferedPktSize = int64(pathEntry.Path.Mtu) + } else { + // use default packet size when within same AS and pathEntry is not set + InferedPktSize = DefaultPktSize + } + + // Do the test for 10 seconds (max period), since it makes the results more reliable + clientBwpStr := fmt.Sprintf("3,?,?,%dbps", clientBw) + clientBwp := parseBwtestParameters(clientBwpStr) + clientBwp.Port = clientDCAddr.Host.L4.Port() + serverBwpStr := fmt.Sprintf("3,?,?,%dbps", serverBw) + serverBwp := parseBwtestParameters(serverBwpStr) + serverBwp.Port = serverDCAddr.Host.L4.Port() + + fmt.Println("\nTest parameters:") + fmt.Printf("client->server: %.3f Mbps\n", float64(clientBw)/1000000) + fmt.Printf("server->client: %.3f Mbps\n", float64(serverBw)/1000000) + + t := time.Now() + expFinishTimeSend := t.Add(serverBwp.BwtestDuration + MaxRTT + GracePeriodSend) + expFinishTimeReceive := t.Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) + res := BwtestResult{NumPacketsReceived: -1, + CorrectlyReceived: -1, + IPAvar: -1, + IPAmin: -1, + IPAavg: -1, + IPAmax: -1, + PrgKey: clientBwp.PrgKey, + ExpectedFinishTime: expFinishTimeReceive, + } + + var resLock sync.Mutex + if expFinishTimeReceive.Before(expFinishTimeSend) { + // The receiver will close the DC connection, so it will wait long enough until the + // sender is also done + res.ExpectedFinishTime = expFinishTimeSend + } + + receiveDone.Lock() + go HandleDCConnReceive(&serverBwp, DCConn, &res, &resLock, &receiveDone) + + pktbuf := make([]byte, 2000) + pktbuf[0] = 'N' // Request for new bwtest + n := EncodeBwtestParameters(&clientBwp, pktbuf[1:]) + l := n + 1 + n = EncodeBwtestParameters(&serverBwp, pktbuf[l:]) + l = l + n + + var numtries int64 = 0 + for numtries < MaxTries { + _, err = CCConn.Write(pktbuf[:l]) + Check(err) + + err = CCConn.SetReadDeadline(time.Now().Add(MaxRTT)) + Check(err) + n, err = CCConn.Read(pktbuf) + if err != nil { + // A timeout likely happened, see if we should adjust the expected finishing time + expFinishTimeReceive = time.Now().Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) + resLock.Lock() + if res.ExpectedFinishTime.Before(expFinishTimeReceive) { + res.ExpectedFinishTime = expFinishTimeReceive + } + resLock.Unlock() + + numtries++ + continue + } + // Remove read deadline + err = CCConn.SetReadDeadline(tzero) + Check(err) + + if n != 2 { + fmt.Println("Incorrect server response, trying again") + time.Sleep(Timeout) + numtries++ + continue + } + if pktbuf[0] != 'N' { + fmt.Println("Incorrect server response, trying again") + time.Sleep(Timeout) + numtries++ + continue + } + if pktbuf[1] != 0 { + // The server asks us to wait for some amount of time + time.Sleep(time.Second * time.Duration(int(pktbuf[1]))) + // Don't increase numtries in this case + continue + } + // Everything was successful, exit the loop + break + } + + // Something went wrong, reduce both client and server bandwidth and try again + if numtries == MaxTries { + fmt.Println("[Error] Server -> Client test failed: could not receive a server response," + + " MaxTries attempted without success.") + // if we reached the minimum bandwidth then stop the test because definitely something is wrong. + if serverBw == MinBandwidth { + // We reached the minimum bandwidth for either the client or the server, stop the testing + fmt.Println("[Error] Reached minimum bandwidth (5Kbps) and no response received " + + "from the server.") + break + } + // Decrease both bandwidth since the test failed without testing the client to server bandwidth, so + // reduce both. + if !clientMax { // If we haven't found the client max + // decrease the client bandwidth + clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, + 0, clientOldBw) + clientOldBw = 0 + fmt.Printf("Client's bandwidth might have been too high, decreasing bandwidth"+ + " to %.3f Mbps\n", float64(clientBw)/1000000) + } + if !serverMax { // if we haven't found the server max + // Decrease the server's bandwidth + serverBw, serverThreshold, serverMax = decreaseBandwidth(serverBw, serverThreshold, + 0, serverOldBw) + serverOldBw = 0 + fmt.Printf("Server's bandwidth might have been too high, decreasing bandwidth "+ + "to %.3f Mbps\n", float64(serverBw)/1000000) + } + + CCConn.Close() + CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) + Check(err) + + // Update the client's ports + clientPort = uint((CCConn.LocalAddr()).(*snet.Addr).Host.L4.Port()) + clientDCAddr.Host.L4 = addr.NewL4UDPInfo(uint16(clientPort) + 1) + + DCConn.Close() + + finished = clientMax && serverMax + + // Wait for a second for the connection to come back up if the interface was revoked + if !finished { + time.Sleep(time.Second) + } + continue + } + + // TODO handle the case when SCMP revocation are received + // Start the Client -> Server test + go HandleDCConnSend(&clientBwp, DCConn) + + receiveDone.Lock() + + fmt.Println("\nS->C results") + att := 8 * serverBwp.PacketSize * serverBwp.NumPackets / int64(serverBwp.BwtestDuration/time.Second) + ach := 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) + fmt.Printf("Attempted bandwidth: %d bps / %.3f Mbps\n", att, float64(att)/1000000) + fmt.Printf("Achieved bandwidth: %d bps / %.3f Mbps\n", ach, float64(ach)/1000000) + // check loss S->C loss and modify servers parameters according to that + loss := float32(serverBwp.NumPackets-res.CorrectlyReceived) * 100 / float32(serverBwp.NumPackets) + fmt.Println("Loss rate:", loss, "%") + + if !serverMax { // Check if we have found the server max bandwidth + // Increase or decrease the bandwidth to attempt next based on the achieved bandwidth (ach) and the + // previously achieved bandwidth (serverOldBandwidth). We do not use loss since the link might be lossy + // but still we can achieve a higher bandwidth. + if serverOldBw <= ach { + fmt.Println("Increasing server's bandwidth...") + serverBw = increaseBandwidth(serverBw, serverThreshold) + } else { + fmt.Println("Decreasing server's bandwidth...") + serverBw, serverThreshold, serverMax = decreaseBandwidth(serverBw, serverThreshold, ach, + serverOldBw) + } + serverOldBw = ach + } + + // Fetch results from server + numtries = 0 + for numtries < MaxTries { + pktbuf[0] = 'R' + copy(pktbuf[1:], clientBwp.PrgKey) + _, err = CCConn.Write(pktbuf[:1+len(clientBwp.PrgKey)]) + Check(err) + + err = CCConn.SetReadDeadline(time.Now().Add(MaxRTT)) + Check(err) + n, err = CCConn.Read(pktbuf) + if err != nil { + numtries++ + continue + } + // Remove read deadline + err = CCConn.SetReadDeadline(tzero) + Check(err) + + if n < 2 { + numtries++ + continue + } + if pktbuf[0] != 'R' { + numtries++ + continue + } + if pktbuf[1] != byte(0) { + // Error case + if pktbuf[1] == byte(127) { + // print the results before exiting + fmt.Println("[Error] Results could not be found or PRG key was incorrect, aborting " + + "current test") + // set numtries to the max so the current test does not continue and attempted bandwidth + // is reduced + numtries = MaxTries + break + } + // pktbuf[1] contains number of seconds to wait for results + fmt.Println("We need to sleep for", pktbuf[1], "seconds before we can get the results") + time.Sleep(time.Duration(pktbuf[1]) * time.Second) + // We don't increment numtries as this was not a lost packet or other communication error + continue + } + + sres, n1, err := DecodeBwtestResult(pktbuf[2:]) + if err != nil { + fmt.Println("Decoding error, try again") + numtries++ + continue + } + if n1+2 < n { + fmt.Println("Insufficient number of bytes received, try again") + time.Sleep(Timeout) + numtries++ + continue + } + if !bytes.Equal(clientBwp.PrgKey, sres.PrgKey) { + fmt.Println("PRG Key returned from server incorrect, this should never happen") + numtries++ + continue + } + + fmt.Println("\nC->S results") + att = 8 * clientBwp.PacketSize * clientBwp.NumPackets / int64(clientBwp.BwtestDuration/time.Second) + ach = 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) + fmt.Printf("Attempted bandwidth: %d bps / %.3f Mbps\n", att, float64(att)/1000000) + fmt.Printf("Achieved bandwidth: %d bps / %.3f Mbps\n", ach, float64(ach)/1000000) + loss := float32(clientBwp.NumPackets-sres.CorrectlyReceived) * 100 / float32(clientBwp.NumPackets) + fmt.Println("Loss rate:", loss, "%") + + // check the results and modify the client's parameters same way as we do the server's + if !clientMax { + if clientOldBw <= ach { + fmt.Println("Increasing client's bandwidth..") + clientBw = increaseBandwidth(clientBw, clientThreshold) + } else { + fmt.Println("Decreasing client's bandwidth..") + clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, + ach, clientOldBw) + } + clientOldBw = ach + } + break + } + + if numtries == MaxTries { + fmt.Println("[Error] Client -> Server test failed: could not fetch server results, " + + "MaxTries attempted without success.") + if clientBw == MinBandwidth { + fmt.Println("[Error] Reached minimum bandwidth (5Kbps) and no results received " + + "from the server.") + break + } + // Don't change the server's bandwidth since its test succeeded, just decrease the client's bandwidth + if !clientMax { + clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, + 0, clientOldBw) + clientOldBw = 0 + fmt.Printf("Client's bandwidth might have been too high, decreasing bandwidth to %.3f Mbps\n", + float64(clientBw)/1000000) + } + + CCConn.Close() + CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) + Check(err) + + // Update the client's ports + clientPort = uint((CCConn.LocalAddr()).(*snet.Addr).Host.L4.Port()) + clientDCAddr.Host.L4 = addr.NewL4UDPInfo(uint16(clientPort) + 1) + + DCConn.Close() + + finished = clientMax && serverMax + // If we haven't found the max bandwidth for both the client and the server + if !finished { + receiveDone.Unlock() + // time out for two seconds for the connection to come back up + time.Sleep(time.Second) + } + continue + } + + DCConn.Close() + // Update the client's DC port by adding the run number to it + clientDCAddr.Host.L4 = addr.NewL4UDPInfo(uint16(clientPort) + run) + + // Check if we found the maximum bandwidth for the client and the server + finished = clientMax && serverMax + receiveDone.Unlock() + time.Sleep(time.Second) + } + + fmt.Println("Max client to server available bandwidth: ", float64(clientBw)/1000000, " Mbps") + fmt.Println("Max server to client available bandwidth: ", float64(serverBw)/1000000, " Mbps") + os.Exit(0) +} + +// increaseBandwidth returns a new bandwidth based on threshold and bandwidth values parameters. When the bandwidth is +// lower than threshold, it will be increased by 100% (doubled), otherwise, if the bandwidth is higher than the +// than the threshold then it will only be increased by 10%. +func increaseBandwidth(currentBandwidth, threshold int64) int64 { + var newBandwidth int64 + + if currentBandwidth >= threshold && threshold != 0 { + newBandwidth = int64(float64(currentBandwidth) * 1.1) + } else { + newBandwidth = currentBandwidth * 2 + // Check if threshold is set and that bandwidth does not exceed it if that is the case + // the bandwidth should be set to the threshold + if newBandwidth > threshold && threshold != 0 { + newBandwidth = threshold + } + } + + return newBandwidth +} + +// decreaseBandwidth returns a new decreased bandwidth and a threshold based on the passed threshold and bandwidth +// parameters, and returns true if the returned bandwidth is the maximum achievable bandwidth. +func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwidth int64) (int64, int64, bool) { + var newBandwidth, newThreshold int64 + isMaxBandwidth := false + + // Choose the larger value between them so we don't do unnecessary slow start since we know both bandwidths are + // achievable on that link. + if achievedBandwidth < oldBandwidth { + newBandwidth = oldBandwidth + } else { + // Both achieved bandwidth and oldBw are not set which means an error occurred when using those values + if achievedBandwidth == 0 { + newBandwidth = currentBandwidth / 2 + } else { + newBandwidth = achievedBandwidth + } + } + // Check if the bandwidth did not change for some error then reduce it by half of the current bandwidth + if newBandwidth == currentBandwidth { + newBandwidth = currentBandwidth / 2 + } + // if the threshold is not set then set the threshold and bandwidth + if currentBandwidth <= threshold || threshold == 0 { + newThreshold = newBandwidth + } else if currentBandwidth > threshold { // threshold was set + // threshold is set and we had to decrease the Bandwidth which means we hit the max bandwidth + isMaxBandwidth = true + } + + // Check if we are lower than the lowest bandwidth possible if so that is the maximum achievable bandwidth on + // that link + if newBandwidth < MinBandwidth { + newThreshold = MinBandwidth + newBandwidth = MinBandwidth + isMaxBandwidth = true + } + + return newBandwidth, newThreshold, isMaxBandwidth +} From 15931752da596d284a4ecfb20b2ddf2e3bbdd507 Mon Sep 17 00:00:00 2001 From: khalid aldughayem Date: Tue, 20 Aug 2019 11:25:13 +0200 Subject: [PATCH 2/6] Refactored `bwtestclient` code to reduce duplication with the `findMaxBandwidth` function --- bwtester/bwtestclient/bwtestclient.go | 945 +++++++++++--------------- 1 file changed, 387 insertions(+), 558 deletions(-) diff --git a/bwtester/bwtestclient/bwtestclient.go b/bwtester/bwtestclient/bwtestclient.go index 6b6cb437..94eaf86f 100644 --- a/bwtester/bwtestclient/bwtestclient.go +++ b/bwtester/bwtestclient/bwtestclient.go @@ -25,21 +25,56 @@ import ( ) const ( - DefaultBwtestParameters = "3,1000,30,80kbps" - DefaultDuration = 3 - DefaultPktSize = 1000 - DefaultPktCount = 30 - DefaultBW = 3000 - WildcardChar = "?" - GracePeriodSync time.Duration = time.Millisecond * 10 - MinBandwidth = 5000 // 5 kbps is the minimum bandwidth we can test + DefaultBwtestParameters = "3,1000,30,80kbps" + DefaultDuration = 3 + DefaultPktSize = 1000 + DefaultPktCount = 30 + DefaultBW = 3000 + WildcardChar = "?" + MinBandwidth = 5000 // 5 kbps is the minimum bandwidth we can test ) var ( InferedPktSize int64 - sciondAddr *string - sciondFromIA *bool overlayType string + + sciondPath string + sciondFromIA bool + dispatcherPath string + clientCCAddrStr string + serverCCAddrStr string + clientPort uint + serverPort uint16 + // Address of client control channel (CC) + clientCCAddr *snet.Addr + // Address of server control channel (CC) + serverCCAddr *snet.Addr + // Control channel connection + CCConn snet.Conn + + // Address of client data channel (DC) + clientDCAddr *snet.Addr + // Address of server data channel (DC) + serverDCAddr *snet.Addr + // Data channel connection + DCConn snet.Conn + + clientBwpStr string + clientBwp BwtestParameters + serverBwpStr string + serverBwp BwtestParameters + interactive bool + pathAlgo string + useIPv6 bool + + err error + tzero time.Time // initialized to "zero" time + + receiveDone sync.Mutex // used to signal when the HandleDCConnReceive goroutine has completed + + maxBandwidth bool + + pathEntry *sciond.PathReplyEntry ) func prepareAESKey() []byte { @@ -244,44 +279,6 @@ func getPacketCount(count string) int64 { } func main() { - var ( - sciondPath string - sciondFromIA bool - dispatcherPath string - clientCCAddrStr string - serverCCAddrStr string - clientPort uint - serverPort uint16 - // Address of client control channel (CC) - clientCCAddr *snet.Addr - // Address of server control channel (CC) - serverCCAddr *snet.Addr - // Control channel connection - CCConn snet.Conn - - // Address of client data channel (DC) - clientDCAddr *snet.Addr - // Address of server data channel (DC) - serverDCAddr *snet.Addr - // Data channel connection - DCConn snet.Conn - - clientBwpStr string - clientBwp BwtestParameters - serverBwpStr string - serverBwp BwtestParameters - interactive bool - pathAlgo string - useIPv6 bool - - err error - tzero time.Time // initialized to "zero" time - - receiveDone sync.Mutex // used to signal when the HandleDCConnReceive goroutine has completed - - maxBandwidth bool - ) - flag.StringVar(&sciondPath, "sciond", "", "Path to sciond socket") flag.BoolVar(&sciondFromIA, "sciondFromIA", false, "SCIOND socket path from IA address:ISD-AS") flag.StringVar(&dispatcherPath, "dispatcher", "/run/shm/dispatcher/default.sock", @@ -339,79 +336,18 @@ func main() { sciondPath = sciond.GetDefaultSCIONDPath(nil) } - var pathEntry *sciond.PathReplyEntry - if !serverCCAddr.IA.Eq(clientCCAddr.IA) { - if interactive { - pathEntry = scionutil.ChoosePathInteractive(clientCCAddr, serverCCAddr) - } else { - var metric int - if pathAlgo == "mtu" { - metric = scionutil.MTU - } else if pathAlgo == "shortest" { - metric = scionutil.Shortest - } - pathEntry = scionutil.ChoosePathByMetric(metric, clientCCAddr, serverCCAddr) - } - if pathEntry == nil { - LogFatal("No paths available to remote destination") - } - serverCCAddr.Path = spath.New(pathEntry.Path.FwdPath) - serverCCAddr.Path.InitOffsets() - serverCCAddr.NextHop, _ = pathEntry.HostInfo.Overlay() - } else { - scionutil.InitSCION(clientCCAddr) - } + initConns() if maxBandwidth { - findMaxBandwidth(interactive, pathAlgo, clientCCAddr, serverCCAddr) - } - - CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) - Check(err) - - // get the port used by clientCC after it bound to the dispatcher (because it might be 0) - clientPort = uint((CCConn.LocalAddr()).(*snet.Addr).Host.L4.Port()) - serverPort = serverCCAddr.Host.L4.Port() - - // Address of client data channel (DC) - clientDCAddr = &snet.Addr{IA: clientCCAddr.IA, Host: &addr.AppAddr{ - L3: clientCCAddr.Host.L3, L4: addr.NewL4UDPInfo(uint16(clientPort) + 1)}} - // Address of server data channel (DC) - serverDCAddr = &snet.Addr{IA: serverCCAddr.IA, Host: &addr.AppAddr{ - L3: serverCCAddr.Host.L3, L4: addr.NewL4UDPInfo(uint16(serverPort) + 1)}} - - // Set path on data connection - if !serverDCAddr.IA.Eq(clientDCAddr.IA) { - serverDCAddr.Path = spath.New(pathEntry.Path.FwdPath) - serverDCAddr.Path.InitOffsets() - serverDCAddr.NextHop, _ = pathEntry.HostInfo.Overlay() - fmt.Printf("Client DC \tNext Hop %v\tServer Host %v\n", - serverDCAddr.NextHop, serverDCAddr.Host) + findMaxBandwidth() + } else { + normalRun(flagset) } +} - // Data channel connection - DCConn, err = snet.DialSCION(overlayType, clientDCAddr, serverDCAddr) - Check(err) +func normalRun(flagset map[string]bool) { + setParameters(flagset) - // update default packet size to max MTU on the selected path - if pathEntry != nil { - InferedPktSize = int64(pathEntry.Path.Mtu) - } else { - // use default packet size when within same AS and pathEntry is not set - InferedPktSize = DefaultPktSize - } - if !flagset["cs"] && flagset["sc"] { // Only one direction set, used same for reverse - clientBwpStr = serverBwpStr - fmt.Println("Only sc parameter set, using same values for cs") - } - clientBwp = parseBwtestParameters(clientBwpStr) - clientBwp.Port = uint16(clientPort + 1) - if !flagset["sc"] && flagset["cs"] { // Only one direction set, used same for reverse - serverBwpStr = clientBwpStr - fmt.Println("Only cs parameter set, using same values for sc") - } - serverBwp = parseBwtestParameters(serverBwpStr) - serverBwp.Port = uint16(serverPort + 1) fmt.Println("\nTest parameters:") fmt.Println("clientDCAddr -> serverDCAddr", clientDCAddr, "->", serverDCAddr) fmt.Printf("client->server: %d seconds, %d bytes, %d packets\n", @@ -419,75 +355,9 @@ func main() { fmt.Printf("server->client: %d seconds, %d bytes, %d packets\n", int(serverBwp.BwtestDuration/time.Second), serverBwp.PacketSize, serverBwp.NumPackets) - t := time.Now() - expFinishTimeSend := t.Add(serverBwp.BwtestDuration + MaxRTT + GracePeriodSend) - expFinishTimeReceive := t.Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) - res := BwtestResult{-1, -1, -1, -1, -1, -1, clientBwp.PrgKey, expFinishTimeReceive} - var resLock sync.Mutex - if expFinishTimeReceive.Before(expFinishTimeSend) { - // The receiver will close the DC connection, so it will wait long enough until the - // sender is also done - res.ExpectedFinishTime = expFinishTimeSend - } - - receiveDone.Lock() - go HandleDCConnReceive(&serverBwp, DCConn, &res, &resLock, &receiveDone) - - pktbuf := make([]byte, 2000) - pktbuf[0] = 'N' // Request for new bwtest - n := EncodeBwtestParameters(&clientBwp, pktbuf[1:]) - l := n + 1 - n = EncodeBwtestParameters(&serverBwp, pktbuf[l:]) - l = l + n - - var numtries int64 = 0 - for numtries < MaxTries { - _, err = CCConn.Write(pktbuf[:l]) - Check(err) + res, failed := startSCTest() - err = CCConn.SetReadDeadline(time.Now().Add(MaxRTT)) - Check(err) - n, err = CCConn.Read(pktbuf) - if err != nil { - // A timeout likely happened, see if we should adjust the expected finishing time - expFinishTimeReceive = time.Now().Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) - resLock.Lock() - if res.ExpectedFinishTime.Before(expFinishTimeReceive) { - res.ExpectedFinishTime = expFinishTimeReceive - } - resLock.Unlock() - - numtries++ - continue - } - // Remove read deadline - err = CCConn.SetReadDeadline(tzero) - Check(err) - - if n != 2 { - fmt.Println("Incorrect server response, trying again") - time.Sleep(Timeout) - numtries++ - continue - } - if pktbuf[0] != 'N' { - fmt.Println("Incorrect server response, trying again") - time.Sleep(Timeout) - numtries++ - continue - } - if pktbuf[1] != 0 { - // The server asks us to wait for some amount of time - time.Sleep(time.Second * time.Duration(int(pktbuf[1]))) - // Don't increase numtries in this case - continue - } - - // Everything was successful, exit the loop - break - } - - if numtries == MaxTries { + if failed { Check(fmt.Errorf("Error, could not receive a server response, MaxTries attempted without success.")) } @@ -496,269 +366,55 @@ func main() { receiveDone.Lock() fmt.Println("\nS->C results") - att := 8 * serverBwp.PacketSize * serverBwp.NumPackets / int64(serverBwp.BwtestDuration/time.Second) - ach := 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) - fmt.Printf("Attempted bandwidth: %d bps / %.2f Mbps\n", att, float64(att)/1000000) - fmt.Printf("Achieved bandwidth: %d bps / %.2f Mbps\n", ach, float64(ach)/1000000) - fmt.Println("Loss rate:", (serverBwp.NumPackets-res.CorrectlyReceived)*100/serverBwp.NumPackets, "%") - variance := res.IPAvar - average := res.IPAavg - fmt.Printf("Interarrival time variance: %dms, average interarrival time: %dms\n", - variance/1e6, average/1e6) - fmt.Printf("Interarrival time min: %dms, interarrival time max: %dms\n", - res.IPAmin/1e6, res.IPAmax/1e6) + printResults(res, serverBwp) // Fetch results from server - numtries = 0 - for numtries < MaxTries { - pktbuf[0] = 'R' - copy(pktbuf[1:], clientBwp.PrgKey) - _, err = CCConn.Write(pktbuf[:1+len(clientBwp.PrgKey)]) - Check(err) - - err = CCConn.SetReadDeadline(time.Now().Add(MaxRTT)) - Check(err) - n, err = CCConn.Read(pktbuf) - if err != nil { - numtries++ - continue - } - // Remove read deadline - err = CCConn.SetReadDeadline(tzero) - Check(err) + sres, failed := startCSTest() - if n < 2 { - numtries++ - continue - } - if pktbuf[0] != 'R' { - numtries++ - continue - } - if pktbuf[1] != byte(0) { - // Error case - if pktbuf[1] == byte(127) { - Check(fmt.Errorf("Results could not be found or PRG key was incorrect, abort")) - } - // pktbuf[1] contains number of seconds to wait for results - fmt.Println("We need to sleep for", pktbuf[1], "seconds before we can get the results") - time.Sleep(time.Duration(pktbuf[1]) * time.Second) - // We don't increment numtries as this was not a lost packet or other communication error - continue - } - - sres, n1, err := DecodeBwtestResult(pktbuf[2:]) - if err != nil { - fmt.Println("Decoding error, try again") - numtries++ - continue - } - if n1+2 < n { - fmt.Println("Insufficient number of bytes received, try again") - time.Sleep(Timeout) - numtries++ - continue - } - if !bytes.Equal(clientBwp.PrgKey, sres.PrgKey) { - fmt.Println("PRG Key returned from server incorrect, this should never happen") - numtries++ - continue - } - fmt.Println("\nC->S results") - att = 8 * clientBwp.PacketSize * clientBwp.NumPackets / int64(clientBwp.BwtestDuration/time.Second) - ach = 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) - fmt.Printf("Attempted bandwidth: %d bps / %.2f Mbps\n", att, float64(att)/1000000) - fmt.Printf("Achieved bandwidth: %d bps / %.2f Mbps\n", ach, float64(ach)/1000000) - fmt.Println("Loss rate:", (clientBwp.NumPackets-sres.CorrectlyReceived)*100/clientBwp.NumPackets, "%") - variance := sres.IPAvar - average := sres.IPAavg - fmt.Printf("Interarrival time variance: %dms, average interarrival time: %dms\n", - variance/1e6, average/1e6) - fmt.Printf("Interarrival time min: %dms, interarrival time max: %dms\n", - sres.IPAmin/1e6, sres.IPAmax/1e6) - return + if failed { + Check(fmt.Errorf("Error, could not fetch server results, MaxTries attempted without success.")) } - fmt.Println("Error, could not fetch server results, MaxTries attempted without success.") -} + fmt.Println("\nC->S results") + printResults(sres, clientBwp) -func findMaxBandwidth(interactive bool, pathAlgo string, clientCCAddr, serverCCAddr *snet.Addr) { +} +func findMaxBandwidth() { var ( - tzero time.Time - receiveDone sync.Mutex - clientOldBw, serverOldBw, clientThreshold, serverThreshold int64 serverMax, clientMax bool finished bool - // run is used to hold the number of the current run. Also to use for the DC Connection port number - // it is added to the CC connection port number on each run. + // run is used to hold the number of the current run. run uint16 ) - clientBw := int64(512000) - serverBw := clientBw - - // Make sure the port number for the client's CC address is zero so a free port is - // picked when the connection fails - clientCCAddr.Host.L4 = addr.NewL4UDPInfo(0) - - // Setup the paths - var pathEntry *sciond.PathReplyEntry - if !serverCCAddr.IA.Eq(clientCCAddr.IA) { - if interactive { - pathEntry = scionutil.ChoosePathInteractive(clientCCAddr, serverCCAddr) - } else { - var metric int - if pathAlgo == "mtu" { - metric = scionutil.MTU - } else if pathAlgo == "shortest" { - metric = scionutil.Shortest - } - pathEntry = scionutil.ChoosePathByMetric(metric, clientCCAddr, serverCCAddr) - } - if pathEntry == nil { - LogFatal("No paths available to remote destination") - } - serverCCAddr.Path = spath.New(pathEntry.Path.FwdPath) - serverCCAddr.Path.InitOffsets() - serverCCAddr.NextHop, _ = pathEntry.HostInfo.Overlay() - } else { - scionutil.InitSCION(clientCCAddr) - } - - CCConn, err := snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) - Check(err) - defer CCConn.Close() - - // get the port used by clientCC after it bound to the dispatcher (because it might be 0) - clientPort := uint((CCConn.LocalAddr()).(*snet.Addr).Host.L4.Port()) - serverPort := serverCCAddr.Host.L4.Port() - - //Address of client data channel (DC) - clientDCAddr := &snet.Addr{IA: clientCCAddr.IA, Host: &addr.AppAddr{ - L3: clientCCAddr.Host.L3, L4: addr.NewL4UDPInfo(uint16(clientPort) + 1)}} - // Address of server data channel (DC) - serverDCAddr := &snet.Addr{IA: serverCCAddr.IA, Host: &addr.AppAddr{ - L3: serverCCAddr.Host.L3, L4: addr.NewL4UDPInfo(uint16(serverPort) + 1)}} - - // Set path on data connection - if !serverDCAddr.IA.Eq(clientDCAddr.IA) { - serverDCAddr.Path = spath.New(pathEntry.Path.FwdPath) - serverDCAddr.Path.InitOffsets() - serverDCAddr.NextHop, _ = pathEntry.HostInfo.Overlay() - fmt.Printf("Client DC \tNext Hop %v\tServer Host %v\n", - serverDCAddr.NextHop, serverDCAddr.Host) - } + // set the server and client bandwidth in the beginning to 500kbps + serverBw, clientBw := int64(512*1e3), int64(512*1e3) // find the max bandwidth for the client to server connection first for !finished { run++ - fmt.Println(strings.Repeat("#", 15)) + fmt.Println(strings.Repeat("#", 30)) fmt.Println("Run: ", run) - //Data channel connection - DCConn, err := snet.DialSCION(overlayType, clientDCAddr, serverDCAddr) - Check(err) + resetDCConn() - // update default packet size to max MTU on the selected path - if pathEntry != nil { - InferedPktSize = int64(pathEntry.Path.Mtu) - } else { - // use default packet size when within same AS and pathEntry is not set - InferedPktSize = DefaultPktSize - } + // Set the parameters + clientBwpStr = fmt.Sprintf("3,?,?,%dbps", clientBw) + serverBwpStr = fmt.Sprintf("3,?,?,%dbps", serverBw) + flagset := map[string]bool{"cs": true, "sc": true} - // Do the test for 10 seconds (max period), since it makes the results more reliable - clientBwpStr := fmt.Sprintf("3,?,?,%dbps", clientBw) - clientBwp := parseBwtestParameters(clientBwpStr) - clientBwp.Port = clientDCAddr.Host.L4.Port() - serverBwpStr := fmt.Sprintf("3,?,?,%dbps", serverBw) - serverBwp := parseBwtestParameters(serverBwpStr) - serverBwp.Port = serverDCAddr.Host.L4.Port() + setParameters(flagset) fmt.Println("\nTest parameters:") - fmt.Printf("client->server: %.3f Mbps\n", float64(clientBw)/1000000) - fmt.Printf("server->client: %.3f Mbps\n", float64(serverBw)/1000000) - - t := time.Now() - expFinishTimeSend := t.Add(serverBwp.BwtestDuration + MaxRTT + GracePeriodSend) - expFinishTimeReceive := t.Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) - res := BwtestResult{NumPacketsReceived: -1, - CorrectlyReceived: -1, - IPAvar: -1, - IPAmin: -1, - IPAavg: -1, - IPAmax: -1, - PrgKey: clientBwp.PrgKey, - ExpectedFinishTime: expFinishTimeReceive, - } - - var resLock sync.Mutex - if expFinishTimeReceive.Before(expFinishTimeSend) { - // The receiver will close the DC connection, so it will wait long enough until the - // sender is also done - res.ExpectedFinishTime = expFinishTimeSend - } + fmt.Printf("client->server: %.3f Mbps\n", float64(clientBw)/1e6) + fmt.Printf("server->client: %.3f Mbps\n", float64(serverBw)/1e6) - receiveDone.Lock() - go HandleDCConnReceive(&serverBwp, DCConn, &res, &resLock, &receiveDone) - - pktbuf := make([]byte, 2000) - pktbuf[0] = 'N' // Request for new bwtest - n := EncodeBwtestParameters(&clientBwp, pktbuf[1:]) - l := n + 1 - n = EncodeBwtestParameters(&serverBwp, pktbuf[l:]) - l = l + n - - var numtries int64 = 0 - for numtries < MaxTries { - _, err = CCConn.Write(pktbuf[:l]) - Check(err) - - err = CCConn.SetReadDeadline(time.Now().Add(MaxRTT)) - Check(err) - n, err = CCConn.Read(pktbuf) - if err != nil { - // A timeout likely happened, see if we should adjust the expected finishing time - expFinishTimeReceive = time.Now().Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) - resLock.Lock() - if res.ExpectedFinishTime.Before(expFinishTimeReceive) { - res.ExpectedFinishTime = expFinishTimeReceive - } - resLock.Unlock() - - numtries++ - continue - } - // Remove read deadline - err = CCConn.SetReadDeadline(tzero) - Check(err) - - if n != 2 { - fmt.Println("Incorrect server response, trying again") - time.Sleep(Timeout) - numtries++ - continue - } - if pktbuf[0] != 'N' { - fmt.Println("Incorrect server response, trying again") - time.Sleep(Timeout) - numtries++ - continue - } - if pktbuf[1] != 0 { - // The server asks us to wait for some amount of time - time.Sleep(time.Second * time.Duration(int(pktbuf[1]))) - // Don't increase numtries in this case - continue - } - // Everything was successful, exit the loop - break - } + res, failed := startSCTest() // Something went wrong, reduce both client and server bandwidth and try again - if numtries == MaxTries { + if failed { fmt.Println("[Error] Server -> Client test failed: could not receive a server response," + " MaxTries attempted without success.") // if we reached the minimum bandwidth then stop the test because definitely something is wrong. @@ -768,15 +424,14 @@ func findMaxBandwidth(interactive bool, pathAlgo string, clientCCAddr, serverCCA "from the server.") break } - // Decrease both bandwidth since the test failed without testing the client to server bandwidth, so - // reduce both. + // Decrease both bandwidth since the test failed without testing the client to server bandwidth if !clientMax { // If we haven't found the client max // decrease the client bandwidth clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, 0, clientOldBw) clientOldBw = 0 fmt.Printf("Client's bandwidth might have been too high, decreasing bandwidth"+ - " to %.3f Mbps\n", float64(clientBw)/1000000) + " to %.3f Mbps\n", float64(clientBw)/1e6) } if !serverMax { // if we haven't found the server max // Decrease the server's bandwidth @@ -784,44 +439,26 @@ func findMaxBandwidth(interactive bool, pathAlgo string, clientCCAddr, serverCCA 0, serverOldBw) serverOldBw = 0 fmt.Printf("Server's bandwidth might have been too high, decreasing bandwidth "+ - "to %.3f Mbps\n", float64(serverBw)/1000000) + "to %.3f Mbps\n", float64(serverBw)/1e6) } - CCConn.Close() - CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) - Check(err) - - // Update the client's ports - clientPort = uint((CCConn.LocalAddr()).(*snet.Addr).Host.L4.Port()) - clientDCAddr.Host.L4 = addr.NewL4UDPInfo(uint16(clientPort) + 1) - - DCConn.Close() - + resetCCConn() + // Check if we found the maximum bandwidth for the client and the server finished = clientMax && serverMax - - // Wait for a second for the connection to come back up if the interface was revoked - if !finished { - time.Sleep(time.Second) - } + time.Sleep(time.Second) continue } - // TODO handle the case when SCMP revocation are received - // Start the Client -> Server test go HandleDCConnSend(&clientBwp, DCConn) receiveDone.Lock() + receiveDone.Unlock() fmt.Println("\nS->C results") - att := 8 * serverBwp.PacketSize * serverBwp.NumPackets / int64(serverBwp.BwtestDuration/time.Second) - ach := 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) - fmt.Printf("Attempted bandwidth: %d bps / %.3f Mbps\n", att, float64(att)/1000000) - fmt.Printf("Achieved bandwidth: %d bps / %.3f Mbps\n", ach, float64(ach)/1000000) - // check loss S->C loss and modify servers parameters according to that - loss := float32(serverBwp.NumPackets-res.CorrectlyReceived) * 100 / float32(serverBwp.NumPackets) - fmt.Println("Loss rate:", loss, "%") + printResults(res, serverBwp) if !serverMax { // Check if we have found the server max bandwidth + ach := 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) // Increase or decrease the bandwidth to attempt next based on the achieved bandwidth (ach) and the // previously achieved bandwidth (serverOldBandwidth). We do not use loss since the link might be lossy // but still we can achieve a higher bandwidth. @@ -837,92 +474,9 @@ func findMaxBandwidth(interactive bool, pathAlgo string, clientCCAddr, serverCCA } // Fetch results from server - numtries = 0 - for numtries < MaxTries { - pktbuf[0] = 'R' - copy(pktbuf[1:], clientBwp.PrgKey) - _, err = CCConn.Write(pktbuf[:1+len(clientBwp.PrgKey)]) - Check(err) - - err = CCConn.SetReadDeadline(time.Now().Add(MaxRTT)) - Check(err) - n, err = CCConn.Read(pktbuf) - if err != nil { - numtries++ - continue - } - // Remove read deadline - err = CCConn.SetReadDeadline(tzero) - Check(err) - - if n < 2 { - numtries++ - continue - } - if pktbuf[0] != 'R' { - numtries++ - continue - } - if pktbuf[1] != byte(0) { - // Error case - if pktbuf[1] == byte(127) { - // print the results before exiting - fmt.Println("[Error] Results could not be found or PRG key was incorrect, aborting " + - "current test") - // set numtries to the max so the current test does not continue and attempted bandwidth - // is reduced - numtries = MaxTries - break - } - // pktbuf[1] contains number of seconds to wait for results - fmt.Println("We need to sleep for", pktbuf[1], "seconds before we can get the results") - time.Sleep(time.Duration(pktbuf[1]) * time.Second) - // We don't increment numtries as this was not a lost packet or other communication error - continue - } - - sres, n1, err := DecodeBwtestResult(pktbuf[2:]) - if err != nil { - fmt.Println("Decoding error, try again") - numtries++ - continue - } - if n1+2 < n { - fmt.Println("Insufficient number of bytes received, try again") - time.Sleep(Timeout) - numtries++ - continue - } - if !bytes.Equal(clientBwp.PrgKey, sres.PrgKey) { - fmt.Println("PRG Key returned from server incorrect, this should never happen") - numtries++ - continue - } - - fmt.Println("\nC->S results") - att = 8 * clientBwp.PacketSize * clientBwp.NumPackets / int64(clientBwp.BwtestDuration/time.Second) - ach = 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) - fmt.Printf("Attempted bandwidth: %d bps / %.3f Mbps\n", att, float64(att)/1000000) - fmt.Printf("Achieved bandwidth: %d bps / %.3f Mbps\n", ach, float64(ach)/1000000) - loss := float32(clientBwp.NumPackets-sres.CorrectlyReceived) * 100 / float32(clientBwp.NumPackets) - fmt.Println("Loss rate:", loss, "%") - - // check the results and modify the client's parameters same way as we do the server's - if !clientMax { - if clientOldBw <= ach { - fmt.Println("Increasing client's bandwidth..") - clientBw = increaseBandwidth(clientBw, clientThreshold) - } else { - fmt.Println("Decreasing client's bandwidth..") - clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, - ach, clientOldBw) - } - clientOldBw = ach - } - break - } + sres, failed := startCSTest() - if numtries == MaxTries { + if failed { fmt.Println("[Error] Client -> Server test failed: could not fetch server results, " + "MaxTries attempted without success.") if clientBw == MinBandwidth { @@ -932,45 +486,40 @@ func findMaxBandwidth(interactive bool, pathAlgo string, clientCCAddr, serverCCA } // Don't change the server's bandwidth since its test succeeded, just decrease the client's bandwidth if !clientMax { - clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, - 0, clientOldBw) + clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, 0, clientOldBw) clientOldBw = 0 fmt.Printf("Client's bandwidth might have been too high, decreasing bandwidth to %.3f Mbps\n", - float64(clientBw)/1000000) + float64(clientBw)/1e6) } - CCConn.Close() - CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) - Check(err) - - // Update the client's ports - clientPort = uint((CCConn.LocalAddr()).(*snet.Addr).Host.L4.Port()) - clientDCAddr.Host.L4 = addr.NewL4UDPInfo(uint16(clientPort) + 1) - - DCConn.Close() - + resetCCConn() finished = clientMax && serverMax - // If we haven't found the max bandwidth for both the client and the server - if !finished { - receiveDone.Unlock() - // time out for two seconds for the connection to come back up - time.Sleep(time.Second) - } + time.Sleep(time.Second) continue } - DCConn.Close() - // Update the client's DC port by adding the run number to it - clientDCAddr.Host.L4 = addr.NewL4UDPInfo(uint16(clientPort) + run) + fmt.Println("\nC->S results") + printResults(sres, clientBwp) + if !clientMax { + ach := 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) + // check the results and modify the client's parameters same way as we do the server's + if clientOldBw <= ach { + fmt.Println("Increasing client's bandwidth..") + clientBw = increaseBandwidth(clientBw, clientThreshold) + } else { + fmt.Println("Decreasing client's bandwidth..") + clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, ach, clientOldBw) + } + clientOldBw = ach + } // Check if we found the maximum bandwidth for the client and the server finished = clientMax && serverMax - receiveDone.Unlock() time.Sleep(time.Second) } - fmt.Println("Max client to server available bandwidth: ", float64(clientBw)/1000000, " Mbps") - fmt.Println("Max server to client available bandwidth: ", float64(serverBw)/1000000, " Mbps") + fmt.Println("Max client to server available bandwidth: ", float64(clientBw)/1e6, " Mbps") + fmt.Println("Max server to client available bandwidth: ", float64(serverBw)/1e6, " Mbps") os.Exit(0) } @@ -1034,3 +583,283 @@ func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwi return newBandwidth, newThreshold, isMaxBandwidth } + +// initConns sets up the paths to the server, initializes the Control Channel +// connection, sets up the Data connection addresses, starts the Data Channel +// connection, then it updates packet size. +func initConns() { + // Setup the paths and + if !serverCCAddr.IA.Eq(clientCCAddr.IA) { + if interactive { + pathEntry = scionutil.ChoosePathInteractive(clientCCAddr, serverCCAddr) + } else { + var metric int + if pathAlgo == "mtu" { + metric = scionutil.MTU + } else if pathAlgo == "shortest" { + metric = scionutil.Shortest + } + pathEntry = scionutil.ChoosePathByMetric(metric, clientCCAddr, serverCCAddr) + } + if pathEntry == nil { + LogFatal("No paths available to remote destination") + } + serverCCAddr.Path = spath.New(pathEntry.Path.FwdPath) + _ = serverCCAddr.Path.InitOffsets() + serverCCAddr.NextHop, _ = pathEntry.HostInfo.Overlay() + } else { + _ = scionutil.InitSCION(clientCCAddr) + } + + // Control channel connection + CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) + Check(err) + + // get the port used by clientCC after it bound to the dispatcher (because it might be 0) + clientPort = uint((CCConn.LocalAddr()).(*snet.Addr).Host.L4.Port()) + serverPort = serverCCAddr.Host.L4.Port() + + //Address of client data channel (DC) + clientDCAddr = &snet.Addr{IA: clientCCAddr.IA, Host: &addr.AppAddr{ + L3: clientCCAddr.Host.L3, L4: addr.NewL4UDPInfo(uint16(clientPort) + 1)}} + // Address of server data channel (DC) + serverDCAddr = &snet.Addr{IA: serverCCAddr.IA, Host: &addr.AppAddr{ + L3: serverCCAddr.Host.L3, L4: addr.NewL4UDPInfo(serverPort + 1)}} + + // Set path on data connection + if !serverDCAddr.IA.Eq(clientDCAddr.IA) { + serverDCAddr.Path = spath.New(pathEntry.Path.FwdPath) + _ = serverDCAddr.Path.InitOffsets() + serverDCAddr.NextHop, _ = pathEntry.HostInfo.Overlay() + fmt.Printf("Client DC \tNext Hop %v\tServer Host %v\n", + serverDCAddr.NextHop, serverDCAddr.Host) + } + + //Data channel connection + DCConn, err = snet.DialSCION(overlayType, clientDCAddr, serverDCAddr) + Check(err) + + // update default packet size to max MTU on the selected path + if pathEntry != nil { + InferedPktSize = int64(pathEntry.Path.Mtu) + } else { + // use default packet size when within same AS and pathEntry is not set + InferedPktSize = DefaultPktSize + } +} + +func resetDCConn() { + _ = DCConn.Close() + + DCConn, err = snet.DialSCION(overlayType, clientDCAddr, serverDCAddr) + if err != nil { + LogFatal("Resetting the DCConn", "err", err) + } +} + +func resetCCConn() { + _ = CCConn.Close() + CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) + + if err != nil { + LogFatal("Resetting the CCConn", "err", err) + } +} + +// setParameters set the client and server parameters based on the passed falgset and the client- and serverBwpStr +func setParameters(flagset map[string]bool) { + if !flagset["cs"] && flagset["sc"] { // Only one direction set, used same for reverse + clientBwpStr = serverBwpStr + fmt.Println("Only sc parameter set, using same values for cs") + } + clientBwp = parseBwtestParameters(clientBwpStr) + clientBwp.Port = uint16(clientPort + 1) + if !flagset["sc"] && flagset["cs"] { // Only one direction set, used same for reverse + serverBwpStr = clientBwpStr + fmt.Println("Only cs parameter set, using same values for sc") + } + serverBwp = parseBwtestParameters(serverBwpStr) + serverBwp.Port = serverPort + 1 +} + +// startSCTest runs the server to client test. +// It should be called right after setting up the flags to start the test. +// Returns the bandwidth test results and a boolean to indicate a +//// failure in the measurements (when max tries is reached). +func startSCTest() (*BwtestResult, bool) { + t := time.Now() + expFinishTimeSend := t.Add(serverBwp.BwtestDuration + MaxRTT + GracePeriodSend) + expFinishTimeReceive := t.Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) + res := BwtestResult{NumPacketsReceived: -1, + CorrectlyReceived: -1, + IPAvar: -1, + IPAmin: -1, + IPAavg: -1, + IPAmax: -1, + PrgKey: clientBwp.PrgKey, + ExpectedFinishTime: expFinishTimeReceive, + } + var resLock sync.Mutex + if expFinishTimeReceive.Before(expFinishTimeSend) { + // The receiver will close the DC connection, so it will wait long enough until the + // sender is also done + res.ExpectedFinishTime = expFinishTimeSend + } + + receiveDone.Lock() + go HandleDCConnReceive(&serverBwp, DCConn, &res, &resLock, &receiveDone) + + pktbuf := make([]byte, 2000) + pktbuf[0] = 'N' // Request for new bwtest + n := EncodeBwtestParameters(&clientBwp, pktbuf[1:]) + l := n + 1 + n = EncodeBwtestParameters(&serverBwp, pktbuf[l:]) + l = l + n + + var numtries int64 = 0 + for numtries < MaxTries { + if _, err = CCConn.Write(pktbuf[:l]); err != nil { + LogFatal("[startSCTest] Writing to CCConn", "err", err) + } + + if err = CCConn.SetReadDeadline(time.Now().Add(MaxRTT)); err != nil { + LogFatal("[startSCTest] setting deadline for CCConn", "err", err) + } + + n, err = CCConn.Read(pktbuf) + if err != nil { + // A timeout likely happened, see if we should adjust the expected finishing time + expFinishTimeReceive = time.Now().Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) + resLock.Lock() + if res.ExpectedFinishTime.Before(expFinishTimeReceive) { + res.ExpectedFinishTime = expFinishTimeReceive + } + resLock.Unlock() + + numtries++ + continue + } + // Remove read deadline + if err = CCConn.SetReadDeadline(tzero); err != nil { + LogFatal("[startSCTest] removing deadline for CCConn", "err", err) + } + + if n != 2 { + fmt.Println("Incorrect server response, trying again") + time.Sleep(Timeout) + numtries++ + continue + } + if pktbuf[0] != 'N' { + fmt.Println("Incorrect server response, trying again") + time.Sleep(Timeout) + numtries++ + continue + } + if pktbuf[1] != 0 { + // The server asks us to wait for some amount of time + time.Sleep(time.Second * time.Duration(int(pktbuf[1]))) + // Don't increase numtries in this case + continue + } + // Everything was successful, exit the loop + break + } + + return &res, numtries == MaxTries +} + +// Retrieves the results from the server for the client to server test. +// It should be invoked after calling startSCTest and HandleDCConnSend +// (See normalRun for an example). +// Returns the bandwidth test results and a boolean to indicate a +// failure in the measurements (when max tries is reached). +func startCSTest() (*BwtestResult, bool) { + pktbuf := make([]byte, 2000) + var numtries int64 = 0 + var sres *BwtestResult + var n int + for numtries < MaxTries { + pktbuf[0] = 'R' + copy(pktbuf[1:], clientBwp.PrgKey) + _, err = CCConn.Write(pktbuf[:1+len(clientBwp.PrgKey)]) + Check(err) + + err = CCConn.SetReadDeadline(time.Now().Add(MaxRTT)) + Check(err) + n, err = CCConn.Read(pktbuf) + if err != nil { + numtries++ + continue + } + // Remove read deadline + err = CCConn.SetReadDeadline(tzero) + Check(err) + + if n < 2 { + numtries++ + continue + } + if pktbuf[0] != 'R' { + numtries++ + continue + } + if pktbuf[1] != byte(0) { + // Error case + if pktbuf[1] == byte(127) { + // print the results before exiting + fmt.Println("[Error] Results could not be found or PRG key was incorrect, aborting " + + "current test") + // set numtries to the max so the current test does not continue and attempted bandwidth + // is reduced + numtries = MaxTries + break + } + // pktbuf[1] contains number of seconds to wait for results + fmt.Println("We need to sleep for", pktbuf[1], "seconds before we can get the results") + time.Sleep(time.Duration(pktbuf[1]) * time.Second) + // We don't increment numtries as this was not a lost packet or other communication error + continue + } + + var n1 int + sres, n1, err = DecodeBwtestResult(pktbuf[2:]) + if err != nil { + fmt.Println("Decoding error, try again") + numtries++ + continue + } + if n1+2 < n { + fmt.Println("Insufficient number of bytes received, try again") + time.Sleep(Timeout) + numtries++ + continue + } + if !bytes.Equal(clientBwp.PrgKey, sres.PrgKey) { + fmt.Println("PRG Key returned from server incorrect, this should never happen") + numtries++ + continue + } + + break + } + + return sres, numtries == MaxTries +} + +// printResults prints the attempted and achieved bandwidth, loss, and the +// interarrival time of the specified results res and bandwidth test parameters bwp. +func printResults(res *BwtestResult, bwp BwtestParameters) { + att := 8 * bwp.PacketSize * bwp.NumPackets / int64(bwp.BwtestDuration/time.Second) + ach := 8 * bwp.PacketSize * res.CorrectlyReceived / int64(bwp.BwtestDuration/time.Second) + fmt.Printf("Attempted bandwidth: %d bps / %.3f Mbps\n", att, float64(att)/1e6) + fmt.Printf("Achieved bandwidth: %d bps / %.3f Mbps\n", ach, float64(ach)/1e6) + loss := float32(bwp.NumPackets-res.CorrectlyReceived) * 100 / float32(serverBwp.NumPackets) + fmt.Println("Loss rate:", loss, "%") + variance := res.IPAvar + average := res.IPAavg + fmt.Printf("Interarrival time variance: %dms, average interarrival time: %dms\n", + variance/1e6, average/1e6) + fmt.Printf("Interarrival time min: %dms, interarrival time max: %dms\n", + res.IPAmin/1e6, res.IPAmax/1e6) +} From 8d88460c442e1f8289a939beedda8fd6b138f50e Mon Sep 17 00:00:00 2001 From: khalid aldughayem Date: Tue, 27 Aug 2019 17:36:12 +0200 Subject: [PATCH 3/6] Find maximum bandwidth function finished. `bwtestclient`: - `receiveDone` is a channel now - Refactored code to reduce duplication and make it easier to read - Changed some error logging messages `bwtestlib`: - `HandleDCConnReceive` takes a channel as a parameter instead of Lock, and signals it when it is done --- bwtester/bwtestclient/bwtestclient.go | 373 ++++++++++++-------------- bwtester/bwtestlib/bwtestlib.go | 4 +- 2 files changed, 173 insertions(+), 204 deletions(-) diff --git a/bwtester/bwtestclient/bwtestclient.go b/bwtester/bwtestclient/bwtestclient.go index 94eaf86f..dc903812 100644 --- a/bwtester/bwtestclient/bwtestclient.go +++ b/bwtester/bwtestclient/bwtestclient.go @@ -70,7 +70,7 @@ var ( err error tzero time.Time // initialized to "zero" time - receiveDone sync.Mutex // used to signal when the HandleDCConnReceive goroutine has completed + receiveDone chan struct{} // used to signal when the HandleDCConnReceive goroutine has completed maxBandwidth bool @@ -304,6 +304,8 @@ func main() { os.Exit(0) } + receiveDone = make(chan struct{}) + if useIPv6 { overlayType = "udp6" } else { @@ -340,44 +342,35 @@ func main() { if maxBandwidth { findMaxBandwidth() - } else { - normalRun(flagset) - } -} - -func normalRun(flagset map[string]bool) { - setParameters(flagset) - - fmt.Println("\nTest parameters:") - fmt.Println("clientDCAddr -> serverDCAddr", clientDCAddr, "->", serverDCAddr) - fmt.Printf("client->server: %d seconds, %d bytes, %d packets\n", - int(clientBwp.BwtestDuration/time.Second), clientBwp.PacketSize, clientBwp.NumPackets) - fmt.Printf("server->client: %d seconds, %d bytes, %d packets\n", - int(serverBwp.BwtestDuration/time.Second), serverBwp.PacketSize, serverBwp.NumPackets) - - res, failed := startSCTest() - - if failed { - Check(fmt.Errorf("Error, could not receive a server response, MaxTries attempted without success.")) - } - - go HandleDCConnSend(&clientBwp, DCConn) - - receiveDone.Lock() - fmt.Println("\nS->C results") - printResults(res, serverBwp) - - // Fetch results from server - sres, failed := startCSTest() + } else { + if !flagset["cs"] && flagset["sc"] { // Only one direction set, used same for reverse + clientBwpStr = serverBwpStr + fmt.Println("Only sc parameter set, using same values for cs") + } + clientBwp = parseBwtestParameters(clientBwpStr) + clientBwp.Port = uint16(clientPort + 1) + if !flagset["sc"] && flagset["cs"] { // Only one direction set, used same for reverse + serverBwpStr = clientBwpStr + fmt.Println("Only cs parameter set, using same values for sc") + } + serverBwp = parseBwtestParameters(serverBwpStr) + serverBwp.Port = serverPort + 1 - if failed { - Check(fmt.Errorf("Error, could not fetch server results, MaxTries attempted without success.")) + fmt.Println("\nTest parameters:") + fmt.Println("clientDCAddr -> serverDCAddr", clientDCAddr, "->", serverDCAddr) + fmt.Printf("client->server: %d seconds, %d bytes, %d packets\n", + int(clientBwp.BwtestDuration/time.Second), clientBwp.PacketSize, clientBwp.NumPackets) + fmt.Printf("server->client: %d seconds, %d bytes, %d packets\n", + int(serverBwp.BwtestDuration/time.Second), serverBwp.PacketSize, serverBwp.NumPackets) + + singleRun(func() { + Check(fmt.Errorf("Error, could not receive a server response, MaxTries attempted without success.")) + }, + func() { + Check(fmt.Errorf("Error, could not fetch server results, MaxTries attempted without success.")) + }) } - - fmt.Println("\nC->S results") - printResults(sres, clientBwp) - } func findMaxBandwidth() { @@ -389,130 +382,55 @@ func findMaxBandwidth() { run uint16 ) - // set the server and client bandwidth in the beginning to 500kbps - serverBw, clientBw := int64(512*1e3), int64(512*1e3) + // set the server and client bandwidth in the beginning to 512kbps + serverBw, clientBw := int64(512e3), int64(512e3) - // find the max bandwidth for the client to server connection first for !finished { + run++ - fmt.Println(strings.Repeat("#", 30)) + fmt.Println(strings.Repeat("#", 50)) fmt.Println("Run: ", run) - resetDCConn() - - // Set the parameters - clientBwpStr = fmt.Sprintf("3,?,?,%dbps", clientBw) - serverBwpStr = fmt.Sprintf("3,?,?,%dbps", serverBw) - flagset := map[string]bool{"cs": true, "sc": true} + DCConn = resetConn(DCConn, clientDCAddr, serverDCAddr) - setParameters(flagset) - - fmt.Println("\nTest parameters:") - fmt.Printf("client->server: %.3f Mbps\n", float64(clientBw)/1e6) - fmt.Printf("server->client: %.3f Mbps\n", float64(serverBw)/1e6) - - res, failed := startSCTest() - - // Something went wrong, reduce both client and server bandwidth and try again - if failed { - fmt.Println("[Error] Server -> Client test failed: could not receive a server response," + - " MaxTries attempted without success.") - // if we reached the minimum bandwidth then stop the test because definitely something is wrong. - if serverBw == MinBandwidth { - // We reached the minimum bandwidth for either the client or the server, stop the testing - fmt.Println("[Error] Reached minimum bandwidth (5Kbps) and no response received " + - "from the server.") - break - } - // Decrease both bandwidth since the test failed without testing the client to server bandwidth - if !clientMax { // If we haven't found the client max - // decrease the client bandwidth - clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, - 0, clientOldBw) - clientOldBw = 0 - fmt.Printf("Client's bandwidth might have been too high, decreasing bandwidth"+ - " to %.3f Mbps\n", float64(clientBw)/1e6) - } - if !serverMax { // if we haven't found the server max - // Decrease the server's bandwidth - serverBw, serverThreshold, serverMax = decreaseBandwidth(serverBw, serverThreshold, - 0, serverOldBw) - serverOldBw = 0 - fmt.Printf("Server's bandwidth might have been too high, decreasing bandwidth "+ - "to %.3f Mbps\n", float64(serverBw)/1e6) - } - - resetCCConn() - // Check if we found the maximum bandwidth for the client and the server - finished = clientMax && serverMax - time.Sleep(time.Second) - continue + // Use the maximum duration to get more accurate results + clientBwp = BwtestParameters{ + BwtestDuration: MaxDuration, + PacketSize: InferedPktSize, + NumPackets: (clientBw * 10) / (InferedPktSize * 8), + PrgKey: prepareAESKey(), + Port: uint16(clientPort + 1), } - - go HandleDCConnSend(&clientBwp, DCConn) - - receiveDone.Lock() - receiveDone.Unlock() - - fmt.Println("\nS->C results") - printResults(res, serverBwp) - - if !serverMax { // Check if we have found the server max bandwidth - ach := 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) - // Increase or decrease the bandwidth to attempt next based on the achieved bandwidth (ach) and the - // previously achieved bandwidth (serverOldBandwidth). We do not use loss since the link might be lossy - // but still we can achieve a higher bandwidth. - if serverOldBw <= ach { - fmt.Println("Increasing server's bandwidth...") - serverBw = increaseBandwidth(serverBw, serverThreshold) - } else { - fmt.Println("Decreasing server's bandwidth...") - serverBw, serverThreshold, serverMax = decreaseBandwidth(serverBw, serverThreshold, ach, - serverOldBw) - } - serverOldBw = ach + serverBwp = BwtestParameters{ + BwtestDuration: MaxDuration, + PacketSize: InferedPktSize, + NumPackets: (serverBw * 10) / (InferedPktSize * 8), + PrgKey: prepareAESKey(), + Port: serverPort + 1, } - // Fetch results from server - sres, failed := startCSTest() + fmt.Println("\nTest parameters:") + fmt.Printf("client->server: %.3f Mbps\n", float64(clientBw)/1e6) + fmt.Printf("server->client: %.3f Mbps\n", float64(serverBw)/1e6) + + res, sres, failed := singleRun(func() { + handleSCError(&serverMax, &clientMax, &serverBw, &clientBw, + &serverOldBw, &clientOldBw, &serverThreshold, &clientThreshold) + }, func() { + handleCSError(&clientMax, &clientBw, &clientOldBw, &clientThreshold) + }) if failed { - fmt.Println("[Error] Client -> Server test failed: could not fetch server results, " + - "MaxTries attempted without success.") - if clientBw == MinBandwidth { - fmt.Println("[Error] Reached minimum bandwidth (5Kbps) and no results received " + - "from the server.") - break - } - // Don't change the server's bandwidth since its test succeeded, just decrease the client's bandwidth - if !clientMax { - clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, 0, clientOldBw) - clientOldBw = 0 - fmt.Printf("Client's bandwidth might have been too high, decreasing bandwidth to %.3f Mbps\n", - float64(clientBw)/1e6) - } + //resetCCConn() + CCConn = resetConn(CCConn, clientCCAddr, serverCCAddr) + } else { + ach := 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) + handleBandwidth(&clientMax, &clientBw, &clientOldBw, &clientThreshold, ach, "client -> server") - resetCCConn() - finished = clientMax && serverMax - time.Sleep(time.Second) - continue + ach = 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) + handleBandwidth(&serverMax, &serverBw, &serverOldBw, &serverThreshold, ach, "server -> client") } - fmt.Println("\nC->S results") - printResults(sres, clientBwp) - - if !clientMax { - ach := 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) - // check the results and modify the client's parameters same way as we do the server's - if clientOldBw <= ach { - fmt.Println("Increasing client's bandwidth..") - clientBw = increaseBandwidth(clientBw, clientThreshold) - } else { - fmt.Println("Decreasing client's bandwidth..") - clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, ach, clientOldBw) - } - clientOldBw = ach - } // Check if we found the maximum bandwidth for the client and the server finished = clientMax && serverMax time.Sleep(time.Second) @@ -523,6 +441,56 @@ func findMaxBandwidth() { os.Exit(0) } +// handleSCError is used in findMaxBandwidth to handle the server -> client error in a single run. +// It decreases both the server and client bandwidth, since the test failed without testing the +// client to server bandwidth.Then checks if one of them reached the minimum bandwidth. +func handleSCError(serverMax, clientMax *bool, serverBw, clientBw, serverOldBw, clientOldBw, + serverThreshold, clientThreshold *int64) { + fmt.Println("[Error] Server -> Client test failed: could not receive a server response," + + " MaxTries attempted without success.") + + // if we reached the minimum bandwidth then stop the test because definitely something is wrong. + if *serverBw == MinBandwidth || *clientBw == MinBandwidth { + Check(fmt.Errorf("reached minimum bandwidth (5Kbps) and no response received " + + "from the server")) + } + + handleBandwidth(serverMax, serverBw, serverOldBw, serverThreshold, 0, "server -> client") + handleBandwidth(clientMax, clientBw, clientOldBw, clientThreshold, 0, "client -> server") +} + +// handleCSError is also used in findMaxBandwidth to handle single run error. +// Only modifies the client's bandwidth, since this mean the server to client test succeeded. +func handleCSError(clientMax *bool, clientBw, clientOldBw, clientThreshold *int64) { + fmt.Println("[Error] Client -> Server test failed: could not fetch server results, " + + "MaxTries attempted without success.") + if *clientBw == MinBandwidth { + Check(fmt.Errorf("reached minimum bandwidth (5Kbps) and no results received " + + "from the server")) + } + // Don't change the server's bandwidth since its test succeeded + handleBandwidth(clientMax, clientBw, clientOldBw, clientThreshold, 0, "client -> server") + +} + +// handleBandwidth increases or decreases the bandwidth to try next based on the +// achieved bandwidth (ach) and the previously achieved bandwidth (oldBw). +// We do not use loss since the link might be lossy, then the loss would not be a good metric. +// "name" is just the name of the bandwidth to reduce to print out to the user. +func handleBandwidth(isMax *bool, currentBw, oldBw, threshold *int64, ach int64, name string) { + if *isMax { + return + } + if *oldBw < ach { + fmt.Printf("Increasing %s bandwidth...\n", name) + *currentBw = increaseBandwidth(*currentBw, *threshold) + } else { + fmt.Printf("Decreasing %s bandwidth...\n", name) + *currentBw, *threshold, *isMax = decreaseBandwidth(*currentBw, *threshold, ach, *oldBw) + } + *oldBw = ach +} + // increaseBandwidth returns a new bandwidth based on threshold and bandwidth values parameters. When the bandwidth is // lower than threshold, it will be increased by 100% (doubled), otherwise, if the bandwidth is higher than the // than the threshold then it will only be increased by 10%. @@ -545,9 +513,8 @@ func increaseBandwidth(currentBandwidth, threshold int64) int64 { // decreaseBandwidth returns a new decreased bandwidth and a threshold based on the passed threshold and bandwidth // parameters, and returns true if the returned bandwidth is the maximum achievable bandwidth. -func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwidth int64) (int64, int64, bool) { - var newBandwidth, newThreshold int64 - isMaxBandwidth := false +func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwidth int64) (newBandwidth, + newThreshold int64, isMaxBandwidth bool) { // Choose the larger value between them so we don't do unnecessary slow start since we know both bandwidths are // achievable on that link. @@ -568,7 +535,7 @@ func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwi // if the threshold is not set then set the threshold and bandwidth if currentBandwidth <= threshold || threshold == 0 { newThreshold = newBandwidth - } else if currentBandwidth > threshold { // threshold was set + } else if currentBandwidth > threshold { // threshold is set and we had to decrease the Bandwidth which means we hit the max bandwidth isMaxBandwidth = true } @@ -581,7 +548,7 @@ func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwi isMaxBandwidth = true } - return newBandwidth, newThreshold, isMaxBandwidth + return } // initConns sets up the paths to the server, initializes the Control Channel @@ -648,45 +615,24 @@ func initConns() { } } -func resetDCConn() { - _ = DCConn.Close() - - DCConn, err = snet.DialSCION(overlayType, clientDCAddr, serverDCAddr) - if err != nil { - LogFatal("Resetting the DCConn", "err", err) - } -} +func resetConn(conn snet.Conn, localAddress, remoteAddress *snet.Addr) snet.Conn { + _ = conn.Close() -func resetCCConn() { - _ = CCConn.Close() - CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) + // give it time to close the connection before trying to open it again + time.Sleep(time.Millisecond * 100) + conn, err = snet.DialSCION(overlayType, localAddress, remoteAddress) if err != nil { - LogFatal("Resetting the CCConn", "err", err) - } -} - -// setParameters set the client and server parameters based on the passed falgset and the client- and serverBwpStr -func setParameters(flagset map[string]bool) { - if !flagset["cs"] && flagset["sc"] { // Only one direction set, used same for reverse - clientBwpStr = serverBwpStr - fmt.Println("Only sc parameter set, using same values for cs") - } - clientBwp = parseBwtestParameters(clientBwpStr) - clientBwp.Port = uint16(clientPort + 1) - if !flagset["sc"] && flagset["cs"] { // Only one direction set, used same for reverse - serverBwpStr = clientBwpStr - fmt.Println("Only cs parameter set, using same values for sc") + LogFatal("Resetting connection", "err", err) } - serverBwp = parseBwtestParameters(serverBwpStr) - serverBwp.Port = serverPort + 1 + return conn } -// startSCTest runs the server to client test. +// startTest runs the server to client test. // It should be called right after setting up the flags to start the test. // Returns the bandwidth test results and a boolean to indicate a //// failure in the measurements (when max tries is reached). -func startSCTest() (*BwtestResult, bool) { +func startTest() (*BwtestResult, bool) { t := time.Now() expFinishTimeSend := t.Add(serverBwp.BwtestDuration + MaxRTT + GracePeriodSend) expFinishTimeReceive := t.Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) @@ -706,8 +652,7 @@ func startSCTest() (*BwtestResult, bool) { res.ExpectedFinishTime = expFinishTimeSend } - receiveDone.Lock() - go HandleDCConnReceive(&serverBwp, DCConn, &res, &resLock, &receiveDone) + go HandleDCConnReceive(&serverBwp, DCConn, &res, &resLock, receiveDone) pktbuf := make([]byte, 2000) pktbuf[0] = 'N' // Request for new bwtest @@ -719,11 +664,11 @@ func startSCTest() (*BwtestResult, bool) { var numtries int64 = 0 for numtries < MaxTries { if _, err = CCConn.Write(pktbuf[:l]); err != nil { - LogFatal("[startSCTest] Writing to CCConn", "err", err) + LogFatal("[startTest] Writing to CCConn", "err", err) } if err = CCConn.SetReadDeadline(time.Now().Add(MaxRTT)); err != nil { - LogFatal("[startSCTest] setting deadline for CCConn", "err", err) + LogFatal("[startTest] Setting deadline for CCConn", "err", err) } n, err = CCConn.Read(pktbuf) @@ -741,16 +686,10 @@ func startSCTest() (*BwtestResult, bool) { } // Remove read deadline if err = CCConn.SetReadDeadline(tzero); err != nil { - LogFatal("[startSCTest] removing deadline for CCConn", "err", err) + LogFatal("[startTest] removing deadline for CCConn", "err", err) } - if n != 2 { - fmt.Println("Incorrect server response, trying again") - time.Sleep(Timeout) - numtries++ - continue - } - if pktbuf[0] != 'N' { + if n != 2 || pktbuf[0] != 'N' { fmt.Println("Incorrect server response, trying again") time.Sleep(Timeout) numtries++ @@ -769,15 +708,22 @@ func startSCTest() (*BwtestResult, bool) { return &res, numtries == MaxTries } -// Retrieves the results from the server for the client to server test. -// It should be invoked after calling startSCTest and HandleDCConnSend +// fetchResults gets the results from the server for the client to server test. +// It should be invoked after calling startTest and HandleDCConnSend // (See normalRun for an example). // Returns the bandwidth test results and a boolean to indicate a // failure in the measurements (when max tries is reached). -func startCSTest() (*BwtestResult, bool) { +func fetchResults() (*BwtestResult, bool) { pktbuf := make([]byte, 2000) var numtries int64 = 0 - var sres *BwtestResult + sres := &BwtestResult{NumPacketsReceived: -1, + CorrectlyReceived: -1, + IPAvar: -1, + IPAmin: -1, + IPAavg: -1, + IPAmax: -1, + PrgKey: clientBwp.PrgKey, + } var n int for numtries < MaxTries { pktbuf[0] = 'R' @@ -796,11 +742,7 @@ func startCSTest() (*BwtestResult, bool) { err = CCConn.SetReadDeadline(tzero) Check(err) - if n < 2 { - numtries++ - continue - } - if pktbuf[0] != 'R' { + if n < 2 || pktbuf[0] != 'R' { numtries++ continue } @@ -863,3 +805,30 @@ func printResults(res *BwtestResult, bwp BwtestParameters) { fmt.Printf("Interarrival time min: %dms, interarrival time max: %dms\n", res.IPAmin/1e6, res.IPAmax/1e6) } + +// singleRun runs a single bandwidth test based in the clientBwp and serverBwp. +// The test parameters should be set before using this function. +func singleRun(scError, csError func()) (res, sres *BwtestResult, failed bool) { + res, failed = startTest() + if failed { + scError() + return + } + + go HandleDCConnSend(&clientBwp, DCConn) + + <-receiveDone + fmt.Println("\nS->C results") + printResults(res, serverBwp) + + // Fetch results from server + sres, failed = fetchResults() + if failed { + csError() + return + } + + fmt.Println("\nC->S results") + printResults(sres, clientBwp) + return +} diff --git a/bwtester/bwtestlib/bwtestlib.go b/bwtester/bwtestlib/bwtestlib.go index 0b8e43b3..3e462fe0 100644 --- a/bwtester/bwtestlib/bwtestlib.go +++ b/bwtester/bwtestlib/bwtestlib.go @@ -198,7 +198,7 @@ func HandleDCConnSend(bwp *BwtestParameters, udpConnection snet.Conn) { } } -func HandleDCConnReceive(bwp *BwtestParameters, udpConnection snet.Conn, res *BwtestResult, resLock *sync.Mutex, done *sync.Mutex) { +func HandleDCConnReceive(bwp *BwtestParameters, udpConnection snet.Conn, res *BwtestResult, resLock *sync.Mutex, done chan struct{}) { resLock.Lock() finish := res.ExpectedFinishTime resLock.Unlock() @@ -275,7 +275,7 @@ func HandleDCConnReceive(bwp *BwtestParameters, udpConnection snet.Conn, res *Bw resLock.Unlock() if done != nil { // Signal that we're done - done.Unlock() + done <- struct{}{} } if time.Now().Before(eft) { time.Sleep(eft.Sub(time.Now())) From fc1deeafe9972d540f1f34a1516a24a0ea987344 Mon Sep 17 00:00:00 2001 From: khalid aldughayem Date: Tue, 27 Aug 2019 17:36:12 +0200 Subject: [PATCH 4/6] Find maximum bandwidth function finished. `bwtestclient`: - `receiveDone` is a channel now - Refactored code to reduce duplication and make it easier to read - Changed some error logging messages `bwtestlib`: - `HandleDCConnReceive` takes a channel as a parameter instead of mutex lock, and signals it when it is done --- bwtester/bwtestclient/bwtestclient.go | 373 ++++++++++++-------------- bwtester/bwtestlib/bwtestlib.go | 4 +- 2 files changed, 173 insertions(+), 204 deletions(-) diff --git a/bwtester/bwtestclient/bwtestclient.go b/bwtester/bwtestclient/bwtestclient.go index 94eaf86f..dc903812 100644 --- a/bwtester/bwtestclient/bwtestclient.go +++ b/bwtester/bwtestclient/bwtestclient.go @@ -70,7 +70,7 @@ var ( err error tzero time.Time // initialized to "zero" time - receiveDone sync.Mutex // used to signal when the HandleDCConnReceive goroutine has completed + receiveDone chan struct{} // used to signal when the HandleDCConnReceive goroutine has completed maxBandwidth bool @@ -304,6 +304,8 @@ func main() { os.Exit(0) } + receiveDone = make(chan struct{}) + if useIPv6 { overlayType = "udp6" } else { @@ -340,44 +342,35 @@ func main() { if maxBandwidth { findMaxBandwidth() - } else { - normalRun(flagset) - } -} - -func normalRun(flagset map[string]bool) { - setParameters(flagset) - - fmt.Println("\nTest parameters:") - fmt.Println("clientDCAddr -> serverDCAddr", clientDCAddr, "->", serverDCAddr) - fmt.Printf("client->server: %d seconds, %d bytes, %d packets\n", - int(clientBwp.BwtestDuration/time.Second), clientBwp.PacketSize, clientBwp.NumPackets) - fmt.Printf("server->client: %d seconds, %d bytes, %d packets\n", - int(serverBwp.BwtestDuration/time.Second), serverBwp.PacketSize, serverBwp.NumPackets) - - res, failed := startSCTest() - - if failed { - Check(fmt.Errorf("Error, could not receive a server response, MaxTries attempted without success.")) - } - - go HandleDCConnSend(&clientBwp, DCConn) - - receiveDone.Lock() - fmt.Println("\nS->C results") - printResults(res, serverBwp) - - // Fetch results from server - sres, failed := startCSTest() + } else { + if !flagset["cs"] && flagset["sc"] { // Only one direction set, used same for reverse + clientBwpStr = serverBwpStr + fmt.Println("Only sc parameter set, using same values for cs") + } + clientBwp = parseBwtestParameters(clientBwpStr) + clientBwp.Port = uint16(clientPort + 1) + if !flagset["sc"] && flagset["cs"] { // Only one direction set, used same for reverse + serverBwpStr = clientBwpStr + fmt.Println("Only cs parameter set, using same values for sc") + } + serverBwp = parseBwtestParameters(serverBwpStr) + serverBwp.Port = serverPort + 1 - if failed { - Check(fmt.Errorf("Error, could not fetch server results, MaxTries attempted without success.")) + fmt.Println("\nTest parameters:") + fmt.Println("clientDCAddr -> serverDCAddr", clientDCAddr, "->", serverDCAddr) + fmt.Printf("client->server: %d seconds, %d bytes, %d packets\n", + int(clientBwp.BwtestDuration/time.Second), clientBwp.PacketSize, clientBwp.NumPackets) + fmt.Printf("server->client: %d seconds, %d bytes, %d packets\n", + int(serverBwp.BwtestDuration/time.Second), serverBwp.PacketSize, serverBwp.NumPackets) + + singleRun(func() { + Check(fmt.Errorf("Error, could not receive a server response, MaxTries attempted without success.")) + }, + func() { + Check(fmt.Errorf("Error, could not fetch server results, MaxTries attempted without success.")) + }) } - - fmt.Println("\nC->S results") - printResults(sres, clientBwp) - } func findMaxBandwidth() { @@ -389,130 +382,55 @@ func findMaxBandwidth() { run uint16 ) - // set the server and client bandwidth in the beginning to 500kbps - serverBw, clientBw := int64(512*1e3), int64(512*1e3) + // set the server and client bandwidth in the beginning to 512kbps + serverBw, clientBw := int64(512e3), int64(512e3) - // find the max bandwidth for the client to server connection first for !finished { + run++ - fmt.Println(strings.Repeat("#", 30)) + fmt.Println(strings.Repeat("#", 50)) fmt.Println("Run: ", run) - resetDCConn() - - // Set the parameters - clientBwpStr = fmt.Sprintf("3,?,?,%dbps", clientBw) - serverBwpStr = fmt.Sprintf("3,?,?,%dbps", serverBw) - flagset := map[string]bool{"cs": true, "sc": true} + DCConn = resetConn(DCConn, clientDCAddr, serverDCAddr) - setParameters(flagset) - - fmt.Println("\nTest parameters:") - fmt.Printf("client->server: %.3f Mbps\n", float64(clientBw)/1e6) - fmt.Printf("server->client: %.3f Mbps\n", float64(serverBw)/1e6) - - res, failed := startSCTest() - - // Something went wrong, reduce both client and server bandwidth and try again - if failed { - fmt.Println("[Error] Server -> Client test failed: could not receive a server response," + - " MaxTries attempted without success.") - // if we reached the minimum bandwidth then stop the test because definitely something is wrong. - if serverBw == MinBandwidth { - // We reached the minimum bandwidth for either the client or the server, stop the testing - fmt.Println("[Error] Reached minimum bandwidth (5Kbps) and no response received " + - "from the server.") - break - } - // Decrease both bandwidth since the test failed without testing the client to server bandwidth - if !clientMax { // If we haven't found the client max - // decrease the client bandwidth - clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, - 0, clientOldBw) - clientOldBw = 0 - fmt.Printf("Client's bandwidth might have been too high, decreasing bandwidth"+ - " to %.3f Mbps\n", float64(clientBw)/1e6) - } - if !serverMax { // if we haven't found the server max - // Decrease the server's bandwidth - serverBw, serverThreshold, serverMax = decreaseBandwidth(serverBw, serverThreshold, - 0, serverOldBw) - serverOldBw = 0 - fmt.Printf("Server's bandwidth might have been too high, decreasing bandwidth "+ - "to %.3f Mbps\n", float64(serverBw)/1e6) - } - - resetCCConn() - // Check if we found the maximum bandwidth for the client and the server - finished = clientMax && serverMax - time.Sleep(time.Second) - continue + // Use the maximum duration to get more accurate results + clientBwp = BwtestParameters{ + BwtestDuration: MaxDuration, + PacketSize: InferedPktSize, + NumPackets: (clientBw * 10) / (InferedPktSize * 8), + PrgKey: prepareAESKey(), + Port: uint16(clientPort + 1), } - - go HandleDCConnSend(&clientBwp, DCConn) - - receiveDone.Lock() - receiveDone.Unlock() - - fmt.Println("\nS->C results") - printResults(res, serverBwp) - - if !serverMax { // Check if we have found the server max bandwidth - ach := 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) - // Increase or decrease the bandwidth to attempt next based on the achieved bandwidth (ach) and the - // previously achieved bandwidth (serverOldBandwidth). We do not use loss since the link might be lossy - // but still we can achieve a higher bandwidth. - if serverOldBw <= ach { - fmt.Println("Increasing server's bandwidth...") - serverBw = increaseBandwidth(serverBw, serverThreshold) - } else { - fmt.Println("Decreasing server's bandwidth...") - serverBw, serverThreshold, serverMax = decreaseBandwidth(serverBw, serverThreshold, ach, - serverOldBw) - } - serverOldBw = ach + serverBwp = BwtestParameters{ + BwtestDuration: MaxDuration, + PacketSize: InferedPktSize, + NumPackets: (serverBw * 10) / (InferedPktSize * 8), + PrgKey: prepareAESKey(), + Port: serverPort + 1, } - // Fetch results from server - sres, failed := startCSTest() + fmt.Println("\nTest parameters:") + fmt.Printf("client->server: %.3f Mbps\n", float64(clientBw)/1e6) + fmt.Printf("server->client: %.3f Mbps\n", float64(serverBw)/1e6) + + res, sres, failed := singleRun(func() { + handleSCError(&serverMax, &clientMax, &serverBw, &clientBw, + &serverOldBw, &clientOldBw, &serverThreshold, &clientThreshold) + }, func() { + handleCSError(&clientMax, &clientBw, &clientOldBw, &clientThreshold) + }) if failed { - fmt.Println("[Error] Client -> Server test failed: could not fetch server results, " + - "MaxTries attempted without success.") - if clientBw == MinBandwidth { - fmt.Println("[Error] Reached minimum bandwidth (5Kbps) and no results received " + - "from the server.") - break - } - // Don't change the server's bandwidth since its test succeeded, just decrease the client's bandwidth - if !clientMax { - clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, 0, clientOldBw) - clientOldBw = 0 - fmt.Printf("Client's bandwidth might have been too high, decreasing bandwidth to %.3f Mbps\n", - float64(clientBw)/1e6) - } + //resetCCConn() + CCConn = resetConn(CCConn, clientCCAddr, serverCCAddr) + } else { + ach := 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) + handleBandwidth(&clientMax, &clientBw, &clientOldBw, &clientThreshold, ach, "client -> server") - resetCCConn() - finished = clientMax && serverMax - time.Sleep(time.Second) - continue + ach = 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) + handleBandwidth(&serverMax, &serverBw, &serverOldBw, &serverThreshold, ach, "server -> client") } - fmt.Println("\nC->S results") - printResults(sres, clientBwp) - - if !clientMax { - ach := 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) - // check the results and modify the client's parameters same way as we do the server's - if clientOldBw <= ach { - fmt.Println("Increasing client's bandwidth..") - clientBw = increaseBandwidth(clientBw, clientThreshold) - } else { - fmt.Println("Decreasing client's bandwidth..") - clientBw, clientThreshold, clientMax = decreaseBandwidth(clientBw, clientThreshold, ach, clientOldBw) - } - clientOldBw = ach - } // Check if we found the maximum bandwidth for the client and the server finished = clientMax && serverMax time.Sleep(time.Second) @@ -523,6 +441,56 @@ func findMaxBandwidth() { os.Exit(0) } +// handleSCError is used in findMaxBandwidth to handle the server -> client error in a single run. +// It decreases both the server and client bandwidth, since the test failed without testing the +// client to server bandwidth.Then checks if one of them reached the minimum bandwidth. +func handleSCError(serverMax, clientMax *bool, serverBw, clientBw, serverOldBw, clientOldBw, + serverThreshold, clientThreshold *int64) { + fmt.Println("[Error] Server -> Client test failed: could not receive a server response," + + " MaxTries attempted without success.") + + // if we reached the minimum bandwidth then stop the test because definitely something is wrong. + if *serverBw == MinBandwidth || *clientBw == MinBandwidth { + Check(fmt.Errorf("reached minimum bandwidth (5Kbps) and no response received " + + "from the server")) + } + + handleBandwidth(serverMax, serverBw, serverOldBw, serverThreshold, 0, "server -> client") + handleBandwidth(clientMax, clientBw, clientOldBw, clientThreshold, 0, "client -> server") +} + +// handleCSError is also used in findMaxBandwidth to handle single run error. +// Only modifies the client's bandwidth, since this mean the server to client test succeeded. +func handleCSError(clientMax *bool, clientBw, clientOldBw, clientThreshold *int64) { + fmt.Println("[Error] Client -> Server test failed: could not fetch server results, " + + "MaxTries attempted without success.") + if *clientBw == MinBandwidth { + Check(fmt.Errorf("reached minimum bandwidth (5Kbps) and no results received " + + "from the server")) + } + // Don't change the server's bandwidth since its test succeeded + handleBandwidth(clientMax, clientBw, clientOldBw, clientThreshold, 0, "client -> server") + +} + +// handleBandwidth increases or decreases the bandwidth to try next based on the +// achieved bandwidth (ach) and the previously achieved bandwidth (oldBw). +// We do not use loss since the link might be lossy, then the loss would not be a good metric. +// "name" is just the name of the bandwidth to reduce to print out to the user. +func handleBandwidth(isMax *bool, currentBw, oldBw, threshold *int64, ach int64, name string) { + if *isMax { + return + } + if *oldBw < ach { + fmt.Printf("Increasing %s bandwidth...\n", name) + *currentBw = increaseBandwidth(*currentBw, *threshold) + } else { + fmt.Printf("Decreasing %s bandwidth...\n", name) + *currentBw, *threshold, *isMax = decreaseBandwidth(*currentBw, *threshold, ach, *oldBw) + } + *oldBw = ach +} + // increaseBandwidth returns a new bandwidth based on threshold and bandwidth values parameters. When the bandwidth is // lower than threshold, it will be increased by 100% (doubled), otherwise, if the bandwidth is higher than the // than the threshold then it will only be increased by 10%. @@ -545,9 +513,8 @@ func increaseBandwidth(currentBandwidth, threshold int64) int64 { // decreaseBandwidth returns a new decreased bandwidth and a threshold based on the passed threshold and bandwidth // parameters, and returns true if the returned bandwidth is the maximum achievable bandwidth. -func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwidth int64) (int64, int64, bool) { - var newBandwidth, newThreshold int64 - isMaxBandwidth := false +func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwidth int64) (newBandwidth, + newThreshold int64, isMaxBandwidth bool) { // Choose the larger value between them so we don't do unnecessary slow start since we know both bandwidths are // achievable on that link. @@ -568,7 +535,7 @@ func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwi // if the threshold is not set then set the threshold and bandwidth if currentBandwidth <= threshold || threshold == 0 { newThreshold = newBandwidth - } else if currentBandwidth > threshold { // threshold was set + } else if currentBandwidth > threshold { // threshold is set and we had to decrease the Bandwidth which means we hit the max bandwidth isMaxBandwidth = true } @@ -581,7 +548,7 @@ func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwi isMaxBandwidth = true } - return newBandwidth, newThreshold, isMaxBandwidth + return } // initConns sets up the paths to the server, initializes the Control Channel @@ -648,45 +615,24 @@ func initConns() { } } -func resetDCConn() { - _ = DCConn.Close() - - DCConn, err = snet.DialSCION(overlayType, clientDCAddr, serverDCAddr) - if err != nil { - LogFatal("Resetting the DCConn", "err", err) - } -} +func resetConn(conn snet.Conn, localAddress, remoteAddress *snet.Addr) snet.Conn { + _ = conn.Close() -func resetCCConn() { - _ = CCConn.Close() - CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) + // give it time to close the connection before trying to open it again + time.Sleep(time.Millisecond * 100) + conn, err = snet.DialSCION(overlayType, localAddress, remoteAddress) if err != nil { - LogFatal("Resetting the CCConn", "err", err) - } -} - -// setParameters set the client and server parameters based on the passed falgset and the client- and serverBwpStr -func setParameters(flagset map[string]bool) { - if !flagset["cs"] && flagset["sc"] { // Only one direction set, used same for reverse - clientBwpStr = serverBwpStr - fmt.Println("Only sc parameter set, using same values for cs") - } - clientBwp = parseBwtestParameters(clientBwpStr) - clientBwp.Port = uint16(clientPort + 1) - if !flagset["sc"] && flagset["cs"] { // Only one direction set, used same for reverse - serverBwpStr = clientBwpStr - fmt.Println("Only cs parameter set, using same values for sc") + LogFatal("Resetting connection", "err", err) } - serverBwp = parseBwtestParameters(serverBwpStr) - serverBwp.Port = serverPort + 1 + return conn } -// startSCTest runs the server to client test. +// startTest runs the server to client test. // It should be called right after setting up the flags to start the test. // Returns the bandwidth test results and a boolean to indicate a //// failure in the measurements (when max tries is reached). -func startSCTest() (*BwtestResult, bool) { +func startTest() (*BwtestResult, bool) { t := time.Now() expFinishTimeSend := t.Add(serverBwp.BwtestDuration + MaxRTT + GracePeriodSend) expFinishTimeReceive := t.Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) @@ -706,8 +652,7 @@ func startSCTest() (*BwtestResult, bool) { res.ExpectedFinishTime = expFinishTimeSend } - receiveDone.Lock() - go HandleDCConnReceive(&serverBwp, DCConn, &res, &resLock, &receiveDone) + go HandleDCConnReceive(&serverBwp, DCConn, &res, &resLock, receiveDone) pktbuf := make([]byte, 2000) pktbuf[0] = 'N' // Request for new bwtest @@ -719,11 +664,11 @@ func startSCTest() (*BwtestResult, bool) { var numtries int64 = 0 for numtries < MaxTries { if _, err = CCConn.Write(pktbuf[:l]); err != nil { - LogFatal("[startSCTest] Writing to CCConn", "err", err) + LogFatal("[startTest] Writing to CCConn", "err", err) } if err = CCConn.SetReadDeadline(time.Now().Add(MaxRTT)); err != nil { - LogFatal("[startSCTest] setting deadline for CCConn", "err", err) + LogFatal("[startTest] Setting deadline for CCConn", "err", err) } n, err = CCConn.Read(pktbuf) @@ -741,16 +686,10 @@ func startSCTest() (*BwtestResult, bool) { } // Remove read deadline if err = CCConn.SetReadDeadline(tzero); err != nil { - LogFatal("[startSCTest] removing deadline for CCConn", "err", err) + LogFatal("[startTest] removing deadline for CCConn", "err", err) } - if n != 2 { - fmt.Println("Incorrect server response, trying again") - time.Sleep(Timeout) - numtries++ - continue - } - if pktbuf[0] != 'N' { + if n != 2 || pktbuf[0] != 'N' { fmt.Println("Incorrect server response, trying again") time.Sleep(Timeout) numtries++ @@ -769,15 +708,22 @@ func startSCTest() (*BwtestResult, bool) { return &res, numtries == MaxTries } -// Retrieves the results from the server for the client to server test. -// It should be invoked after calling startSCTest and HandleDCConnSend +// fetchResults gets the results from the server for the client to server test. +// It should be invoked after calling startTest and HandleDCConnSend // (See normalRun for an example). // Returns the bandwidth test results and a boolean to indicate a // failure in the measurements (when max tries is reached). -func startCSTest() (*BwtestResult, bool) { +func fetchResults() (*BwtestResult, bool) { pktbuf := make([]byte, 2000) var numtries int64 = 0 - var sres *BwtestResult + sres := &BwtestResult{NumPacketsReceived: -1, + CorrectlyReceived: -1, + IPAvar: -1, + IPAmin: -1, + IPAavg: -1, + IPAmax: -1, + PrgKey: clientBwp.PrgKey, + } var n int for numtries < MaxTries { pktbuf[0] = 'R' @@ -796,11 +742,7 @@ func startCSTest() (*BwtestResult, bool) { err = CCConn.SetReadDeadline(tzero) Check(err) - if n < 2 { - numtries++ - continue - } - if pktbuf[0] != 'R' { + if n < 2 || pktbuf[0] != 'R' { numtries++ continue } @@ -863,3 +805,30 @@ func printResults(res *BwtestResult, bwp BwtestParameters) { fmt.Printf("Interarrival time min: %dms, interarrival time max: %dms\n", res.IPAmin/1e6, res.IPAmax/1e6) } + +// singleRun runs a single bandwidth test based in the clientBwp and serverBwp. +// The test parameters should be set before using this function. +func singleRun(scError, csError func()) (res, sres *BwtestResult, failed bool) { + res, failed = startTest() + if failed { + scError() + return + } + + go HandleDCConnSend(&clientBwp, DCConn) + + <-receiveDone + fmt.Println("\nS->C results") + printResults(res, serverBwp) + + // Fetch results from server + sres, failed = fetchResults() + if failed { + csError() + return + } + + fmt.Println("\nC->S results") + printResults(sres, clientBwp) + return +} diff --git a/bwtester/bwtestlib/bwtestlib.go b/bwtester/bwtestlib/bwtestlib.go index 0b8e43b3..3e462fe0 100644 --- a/bwtester/bwtestlib/bwtestlib.go +++ b/bwtester/bwtestlib/bwtestlib.go @@ -198,7 +198,7 @@ func HandleDCConnSend(bwp *BwtestParameters, udpConnection snet.Conn) { } } -func HandleDCConnReceive(bwp *BwtestParameters, udpConnection snet.Conn, res *BwtestResult, resLock *sync.Mutex, done *sync.Mutex) { +func HandleDCConnReceive(bwp *BwtestParameters, udpConnection snet.Conn, res *BwtestResult, resLock *sync.Mutex, done chan struct{}) { resLock.Lock() finish := res.ExpectedFinishTime resLock.Unlock() @@ -275,7 +275,7 @@ func HandleDCConnReceive(bwp *BwtestParameters, udpConnection snet.Conn, res *Bw resLock.Unlock() if done != nil { // Signal that we're done - done.Unlock() + done <- struct{}{} } if time.Now().Before(eft) { time.Sleep(eft.Sub(time.Now())) From 8f025a80e0ff399855684d98ef879672e1f15b8d Mon Sep 17 00:00:00 2001 From: khalid aldughayem Date: Thu, 29 Aug 2019 13:09:22 +0200 Subject: [PATCH 5/6] Reduced number of global variables, and added support for cs and sc flags with find max bandwidth option - Reduced global variables to 2 variables, all others are scoped local variables. - initConns function takes client and server addresses as input (along with pathAlgo and interactive for choosing the paths while setting up the addresses), and returns the connections and an error. - find max bandwidth option can be used with the 'cs' and/or the 'sc' flags to set the duration, packet sizes, and starting bandwidth for the test. If the flags are not set, default values are used --- bwtester/bwtestclient/bwtestclient.go | 237 ++++++++++++++------------ 1 file changed, 124 insertions(+), 113 deletions(-) diff --git a/bwtester/bwtestclient/bwtestclient.go b/bwtester/bwtestclient/bwtestclient.go index dc903812..83ed73c2 100644 --- a/bwtester/bwtestclient/bwtestclient.go +++ b/bwtester/bwtestclient/bwtestclient.go @@ -37,44 +37,6 @@ const ( var ( InferedPktSize int64 overlayType string - - sciondPath string - sciondFromIA bool - dispatcherPath string - clientCCAddrStr string - serverCCAddrStr string - clientPort uint - serverPort uint16 - // Address of client control channel (CC) - clientCCAddr *snet.Addr - // Address of server control channel (CC) - serverCCAddr *snet.Addr - // Control channel connection - CCConn snet.Conn - - // Address of client data channel (DC) - clientDCAddr *snet.Addr - // Address of server data channel (DC) - serverDCAddr *snet.Addr - // Data channel connection - DCConn snet.Conn - - clientBwpStr string - clientBwp BwtestParameters - serverBwpStr string - serverBwp BwtestParameters - interactive bool - pathAlgo string - useIPv6 bool - - err error - tzero time.Time // initialized to "zero" time - - receiveDone chan struct{} // used to signal when the HandleDCConnReceive goroutine has completed - - maxBandwidth bool - - pathEntry *sciond.PathReplyEntry ) func prepareAESKey() []byte { @@ -279,6 +241,40 @@ func getPacketCount(count string) int64 { } func main() { + var ( + sciondPath string + sciondFromIA bool + dispatcherPath string + clientCCAddrStr string + serverCCAddrStr string + clientPort uint + + // Control channel connection + CCConn snet.Conn + // Address of client control channel (CC) + clientCCAddr *snet.Addr + // Address of server control channel (CC) + serverCCAddr *snet.Addr + + // Address of client data channel (DC) + clientDCAddr *snet.Addr + // Address of server data channel (DC) + serverDCAddr *snet.Addr + // Data channel connection + DCConn snet.Conn + + clientBwpStr string + clientBwp BwtestParameters + serverBwpStr string + serverBwp BwtestParameters + interactive bool + pathAlgo string + useIPv6 bool + + maxBandwidth bool + + err error + ) flag.StringVar(&sciondPath, "sciond", "", "Path to sciond socket") flag.BoolVar(&sciondFromIA, "sciondFromIA", false, "SCIOND socket path from IA address:ISD-AS") flag.StringVar(&dispatcherPath, "dispatcher", "/run/shm/dispatcher/default.sock", @@ -291,7 +287,11 @@ func main() { flag.BoolVar(&interactive, "i", false, "Interactive mode") flag.StringVar(&pathAlgo, "pathAlgo", "", "Path selection algorithm / metric (\"shortest\", \"mtu\")") flag.BoolVar(&useIPv6, "6", false, "Use IPv6") - flag.BoolVar(&maxBandwidth, "findMax", false, "Find the maximum bandwidth achievable") + flag.BoolVar(&maxBandwidth, "findMax", false, "Find the maximum bandwidth achievable.\nYou can"+ + "use the flags \"cs\" and \"sc\" to set the parameters along with initial bandwidth to test on the link.\n"+ + "The other parameters will be fixed except for the packet count which will change in every run.\nThe higher"+ + " the duration of the test, the more accurate the results, but it will take longer to find the "+ + "maximum bandwidth.") flag.Parse() flagset := make(map[string]bool) @@ -304,8 +304,6 @@ func main() { os.Exit(0) } - receiveDone = make(chan struct{}) - if useIPv6 { overlayType = "udp6" } else { @@ -338,82 +336,78 @@ func main() { sciondPath = sciond.GetDefaultSCIONDPath(nil) } - initConns() + // initalize the addresses before passing them + serverDCAddr, clientDCAddr = &snet.Addr{}, &snet.Addr{} + CCConn, DCConn, err = initConns(serverCCAddr, serverDCAddr, clientCCAddr, clientDCAddr, pathAlgo, interactive) + if err != nil { + Check(err) + } - if maxBandwidth { - findMaxBandwidth() + if !flagset["cs"] && flagset["sc"] { // Only one direction set, used same for reverse + clientBwpStr = serverBwpStr + fmt.Println("Only sc parameter set, using same values for cs") + } + clientBwp = parseBwtestParameters(clientBwpStr) + clientBwp.Port = clientDCAddr.Host.L4.Port() + if !flagset["sc"] && flagset["cs"] { // Only one direction set, used same for reverse + serverBwpStr = clientBwpStr + fmt.Println("Only cs parameter set, using same values for sc") + } + serverBwp = parseBwtestParameters(serverBwpStr) + serverBwp.Port = serverDCAddr.Host.L4.Port() + fmt.Println("\nTest parameters:") + fmt.Println("clientDCAddr -> serverDCAddr", clientDCAddr, "->", serverDCAddr) + fmt.Printf("client->server: %d seconds, %d bytes, %d packets\n", + int(clientBwp.BwtestDuration/time.Second), clientBwp.PacketSize, clientBwp.NumPackets) + fmt.Printf("server->client: %d seconds, %d bytes, %d packets\n", + int(serverBwp.BwtestDuration/time.Second), serverBwp.PacketSize, serverBwp.NumPackets) + + if maxBandwidth { + findMaxBandwidth(CCConn, DCConn, serverCCAddr, serverDCAddr, clientCCAddr, clientDCAddr, serverBwp, clientBwp) } else { - if !flagset["cs"] && flagset["sc"] { // Only one direction set, used same for reverse - clientBwpStr = serverBwpStr - fmt.Println("Only sc parameter set, using same values for cs") - } - clientBwp = parseBwtestParameters(clientBwpStr) - clientBwp.Port = uint16(clientPort + 1) - if !flagset["sc"] && flagset["cs"] { // Only one direction set, used same for reverse - serverBwpStr = clientBwpStr - fmt.Println("Only cs parameter set, using same values for sc") - } - serverBwp = parseBwtestParameters(serverBwpStr) - serverBwp.Port = serverPort + 1 - - fmt.Println("\nTest parameters:") - fmt.Println("clientDCAddr -> serverDCAddr", clientDCAddr, "->", serverDCAddr) - fmt.Printf("client->server: %d seconds, %d bytes, %d packets\n", - int(clientBwp.BwtestDuration/time.Second), clientBwp.PacketSize, clientBwp.NumPackets) - fmt.Printf("server->client: %d seconds, %d bytes, %d packets\n", - int(serverBwp.BwtestDuration/time.Second), serverBwp.PacketSize, serverBwp.NumPackets) - - singleRun(func() { - Check(fmt.Errorf("Error, could not receive a server response, MaxTries attempted without success.")) - }, + + singleRun(CCConn, DCConn, serverBwp, clientBwp, + func() { + Check(fmt.Errorf("Error, could not receive a server response, MaxTries attempted without success.")) + }, func() { Check(fmt.Errorf("Error, could not fetch server results, MaxTries attempted without success.")) }) } } -func findMaxBandwidth() { +func findMaxBandwidth(CCConn, DCConn snet.Conn, serverCCAddr, serverDCAddr, clientCCAddr, clientDCAddr *snet.Addr, + serverBwp, clientBwp BwtestParameters) { var ( - clientOldBw, serverOldBw, clientThreshold, serverThreshold int64 - serverMax, clientMax bool - finished bool + clientOldBw, serverOldBw int64 + clientThreshold, serverThreshold int64 + serverMax, clientMax bool + finished bool // run is used to hold the number of the current run. run uint16 ) - // set the server and client bandwidth in the beginning to 512kbps - serverBw, clientBw := int64(512e3), int64(512e3) + // Calculate from bandwidth parameters from the user + serverBw := (serverBwp.NumPackets * serverBwp.PacketSize * 8) / int64(serverBwp.BwtestDuration/time.Second) + clientBw := (clientBwp.NumPackets * clientBwp.PacketSize * 8) / int64(clientBwp.BwtestDuration/time.Second) for !finished { - run++ fmt.Println(strings.Repeat("#", 50)) fmt.Println("Run: ", run) DCConn = resetConn(DCConn, clientDCAddr, serverDCAddr) - // Use the maximum duration to get more accurate results - clientBwp = BwtestParameters{ - BwtestDuration: MaxDuration, - PacketSize: InferedPktSize, - NumPackets: (clientBw * 10) / (InferedPktSize * 8), - PrgKey: prepareAESKey(), - Port: uint16(clientPort + 1), - } - serverBwp = BwtestParameters{ - BwtestDuration: MaxDuration, - PacketSize: InferedPktSize, - NumPackets: (serverBw * 10) / (InferedPktSize * 8), - PrgKey: prepareAESKey(), - Port: serverPort + 1, - } + // calculate the new number of packets to send based on + serverBwp.NumPackets = (serverBw * int64(serverBwp.BwtestDuration/time.Second)) / (serverBwp.PacketSize * 8) + clientBwp.NumPackets = (clientBw * int64(clientBwp.BwtestDuration/time.Second)) / (clientBwp.PacketSize * 8) - fmt.Println("\nTest parameters:") - fmt.Printf("client->server: %.3f Mbps\n", float64(clientBw)/1e6) + fmt.Println("\nBandwidth values:") fmt.Printf("server->client: %.3f Mbps\n", float64(serverBw)/1e6) + fmt.Printf("client->server: %.3f Mbps\n", float64(clientBw)/1e6) - res, sres, failed := singleRun(func() { + res, sres, failed := singleRun(CCConn, DCConn, serverBwp, clientBwp, func() { handleSCError(&serverMax, &clientMax, &serverBw, &clientBw, &serverOldBw, &clientOldBw, &serverThreshold, &clientThreshold) }, func() { @@ -424,11 +418,11 @@ func findMaxBandwidth() { //resetCCConn() CCConn = resetConn(CCConn, clientCCAddr, serverCCAddr) } else { - ach := 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) - handleBandwidth(&clientMax, &clientBw, &clientOldBw, &clientThreshold, ach, "client -> server") - - ach = 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) + ach := 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) handleBandwidth(&serverMax, &serverBw, &serverOldBw, &serverThreshold, ach, "server -> client") + + ach = 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) + handleBandwidth(&clientMax, &clientBw, &clientOldBw, &clientThreshold, ach, "client -> server") } // Check if we found the maximum bandwidth for the client and the server @@ -436,8 +430,8 @@ func findMaxBandwidth() { time.Sleep(time.Second) } - fmt.Println("Max client to server available bandwidth: ", float64(clientBw)/1e6, " Mbps") - fmt.Println("Max server to client available bandwidth: ", float64(serverBw)/1e6, " Mbps") + fmt.Println("Max server -> client available bandwidth: ", float64(serverBw)/1e6, " Mbps") + fmt.Println("Max client -> server available bandwidth: ", float64(clientBw)/1e6, " Mbps") os.Exit(0) } @@ -554,7 +548,10 @@ func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwi // initConns sets up the paths to the server, initializes the Control Channel // connection, sets up the Data connection addresses, starts the Data Channel // connection, then it updates packet size. -func initConns() { +func initConns(serverCCAddr, serverDCAddr, clientCCAddr, clientDCAddr *snet.Addr, pathAlgo string, interactive bool) (CCConn, + DCConn snet.Conn, err error) { + var pathEntry *sciond.PathReplyEntry + // Setup the paths and if !serverCCAddr.IA.Eq(clientCCAddr.IA) { if interactive { @@ -580,17 +577,18 @@ func initConns() { // Control channel connection CCConn, err = snet.DialSCION(overlayType, clientCCAddr, serverCCAddr) - Check(err) - + if err != nil { + return + } // get the port used by clientCC after it bound to the dispatcher (because it might be 0) - clientPort = uint((CCConn.LocalAddr()).(*snet.Addr).Host.L4.Port()) - serverPort = serverCCAddr.Host.L4.Port() + clientPort := uint((CCConn.LocalAddr()).(*snet.Addr).Host.L4.Port()) + serverPort := serverCCAddr.Host.L4.Port() //Address of client data channel (DC) - clientDCAddr = &snet.Addr{IA: clientCCAddr.IA, Host: &addr.AppAddr{ + *clientDCAddr = snet.Addr{IA: clientCCAddr.IA, Host: &addr.AppAddr{ L3: clientCCAddr.Host.L3, L4: addr.NewL4UDPInfo(uint16(clientPort) + 1)}} // Address of server data channel (DC) - serverDCAddr = &snet.Addr{IA: serverCCAddr.IA, Host: &addr.AppAddr{ + *serverDCAddr = snet.Addr{IA: serverCCAddr.IA, Host: &addr.AppAddr{ L3: serverCCAddr.Host.L3, L4: addr.NewL4UDPInfo(serverPort + 1)}} // Set path on data connection @@ -604,8 +602,9 @@ func initConns() { //Data channel connection DCConn, err = snet.DialSCION(overlayType, clientDCAddr, serverDCAddr) - Check(err) - + if err != nil { + return + } // update default packet size to max MTU on the selected path if pathEntry != nil { InferedPktSize = int64(pathEntry.Path.Mtu) @@ -613,9 +612,12 @@ func initConns() { // use default packet size when within same AS and pathEntry is not set InferedPktSize = DefaultPktSize } + + return } func resetConn(conn snet.Conn, localAddress, remoteAddress *snet.Addr) snet.Conn { + var err error _ = conn.Close() // give it time to close the connection before trying to open it again @@ -632,7 +634,11 @@ func resetConn(conn snet.Conn, localAddress, remoteAddress *snet.Addr) snet.Conn // It should be called right after setting up the flags to start the test. // Returns the bandwidth test results and a boolean to indicate a //// failure in the measurements (when max tries is reached). -func startTest() (*BwtestResult, bool) { +func startTest(CCConn, DCConn snet.Conn, serverBwp, clientBwp BwtestParameters, + receiveDone chan struct{}) (*BwtestResult, bool) { + var tzero time.Time + var err error + t := time.Now() expFinishTimeSend := t.Add(serverBwp.BwtestDuration + MaxRTT + GracePeriodSend) expFinishTimeReceive := t.Add(clientBwp.BwtestDuration + MaxRTT + StragglerWaitPeriod) @@ -713,7 +719,10 @@ func startTest() (*BwtestResult, bool) { // (See normalRun for an example). // Returns the bandwidth test results and a boolean to indicate a // failure in the measurements (when max tries is reached). -func fetchResults() (*BwtestResult, bool) { +func fetchResults(CCConn snet.Conn, clientBwp BwtestParameters) (*BwtestResult, bool) { + var tzero time.Time + var err error + pktbuf := make([]byte, 2000) var numtries int64 = 0 sres := &BwtestResult{NumPacketsReceived: -1, @@ -796,7 +805,7 @@ func printResults(res *BwtestResult, bwp BwtestParameters) { ach := 8 * bwp.PacketSize * res.CorrectlyReceived / int64(bwp.BwtestDuration/time.Second) fmt.Printf("Attempted bandwidth: %d bps / %.3f Mbps\n", att, float64(att)/1e6) fmt.Printf("Achieved bandwidth: %d bps / %.3f Mbps\n", ach, float64(ach)/1e6) - loss := float32(bwp.NumPackets-res.CorrectlyReceived) * 100 / float32(serverBwp.NumPackets) + loss := float32(bwp.NumPackets-res.CorrectlyReceived) * 100 / float32(bwp.NumPackets) fmt.Println("Loss rate:", loss, "%") variance := res.IPAvar average := res.IPAavg @@ -808,8 +817,10 @@ func printResults(res *BwtestResult, bwp BwtestParameters) { // singleRun runs a single bandwidth test based in the clientBwp and serverBwp. // The test parameters should be set before using this function. -func singleRun(scError, csError func()) (res, sres *BwtestResult, failed bool) { - res, failed = startTest() +func singleRun(CCConn, DCConn snet.Conn, serverBwp, clientBwp BwtestParameters, scError, + csError func()) (res, sres *BwtestResult, failed bool) { + receiveDone := make(chan struct{}) + res, failed = startTest(CCConn, DCConn, serverBwp, clientBwp, receiveDone) if failed { scError() return @@ -822,7 +833,7 @@ func singleRun(scError, csError func()) (res, sres *BwtestResult, failed bool) { printResults(res, serverBwp) // Fetch results from server - sres, failed = fetchResults() + sres, failed = fetchResults(CCConn, clientBwp) if failed { csError() return From 94c583638dc6a9ee1355790b76330d1027bf0b4c Mon Sep 17 00:00:00 2001 From: khalid aldughayem Date: Sat, 21 Sep 2019 19:23:08 +0200 Subject: [PATCH 6/6] Renamed some variables to better describe them --- bwtester/bwtestclient/bwtestclient.go | 34 +++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/bwtester/bwtestclient/bwtestclient.go b/bwtester/bwtestclient/bwtestclient.go index 83ed73c2..8dfd7a81 100644 --- a/bwtester/bwtestclient/bwtestclient.go +++ b/bwtester/bwtestclient/bwtestclient.go @@ -380,7 +380,7 @@ func main() { func findMaxBandwidth(CCConn, DCConn snet.Conn, serverCCAddr, serverDCAddr, clientCCAddr, clientDCAddr *snet.Addr, serverBwp, clientBwp BwtestParameters) { var ( - clientOldBw, serverOldBw int64 + clientOldAch, serverOldAch int64 clientThreshold, serverThreshold int64 serverMax, clientMax bool finished bool @@ -409,9 +409,9 @@ func findMaxBandwidth(CCConn, DCConn snet.Conn, serverCCAddr, serverDCAddr, clie res, sres, failed := singleRun(CCConn, DCConn, serverBwp, clientBwp, func() { handleSCError(&serverMax, &clientMax, &serverBw, &clientBw, - &serverOldBw, &clientOldBw, &serverThreshold, &clientThreshold) + &serverOldAch, &clientOldAch, &serverThreshold, &clientThreshold) }, func() { - handleCSError(&clientMax, &clientBw, &clientOldBw, &clientThreshold) + handleCSError(&clientMax, &clientBw, &clientOldAch, &clientThreshold) }) if failed { @@ -419,10 +419,10 @@ func findMaxBandwidth(CCConn, DCConn snet.Conn, serverCCAddr, serverDCAddr, clie CCConn = resetConn(CCConn, clientCCAddr, serverCCAddr) } else { ach := 8 * serverBwp.PacketSize * res.CorrectlyReceived / int64(serverBwp.BwtestDuration/time.Second) - handleBandwidth(&serverMax, &serverBw, &serverOldBw, &serverThreshold, ach, "server -> client") + handleBandwidth(&serverMax, &serverBw, &serverOldAch, &serverThreshold, ach, "server -> client") ach = 8 * clientBwp.PacketSize * sres.CorrectlyReceived / int64(clientBwp.BwtestDuration/time.Second) - handleBandwidth(&clientMax, &clientBw, &clientOldBw, &clientThreshold, ach, "client -> server") + handleBandwidth(&clientMax, &clientBw, &clientOldAch, &clientThreshold, ach, "client -> server") } // Check if we found the maximum bandwidth for the client and the server @@ -438,7 +438,7 @@ func findMaxBandwidth(CCConn, DCConn snet.Conn, serverCCAddr, serverDCAddr, clie // handleSCError is used in findMaxBandwidth to handle the server -> client error in a single run. // It decreases both the server and client bandwidth, since the test failed without testing the // client to server bandwidth.Then checks if one of them reached the minimum bandwidth. -func handleSCError(serverMax, clientMax *bool, serverBw, clientBw, serverOldBw, clientOldBw, +func handleSCError(serverMax, clientMax *bool, serverBw, clientBw, serverOldAch, clientOldAch, serverThreshold, clientThreshold *int64) { fmt.Println("[Error] Server -> Client test failed: could not receive a server response," + " MaxTries attempted without success.") @@ -449,13 +449,13 @@ func handleSCError(serverMax, clientMax *bool, serverBw, clientBw, serverOldBw, "from the server")) } - handleBandwidth(serverMax, serverBw, serverOldBw, serverThreshold, 0, "server -> client") - handleBandwidth(clientMax, clientBw, clientOldBw, clientThreshold, 0, "client -> server") + handleBandwidth(serverMax, serverBw, serverOldAch, serverThreshold, 0, "server -> client") + handleBandwidth(clientMax, clientBw, clientOldAch, clientThreshold, 0, "client -> server") } // handleCSError is also used in findMaxBandwidth to handle single run error. // Only modifies the client's bandwidth, since this mean the server to client test succeeded. -func handleCSError(clientMax *bool, clientBw, clientOldBw, clientThreshold *int64) { +func handleCSError(clientMax *bool, clientBw, clientOldAch, clientThreshold *int64) { fmt.Println("[Error] Client -> Server test failed: could not fetch server results, " + "MaxTries attempted without success.") if *clientBw == MinBandwidth { @@ -463,7 +463,7 @@ func handleCSError(clientMax *bool, clientBw, clientOldBw, clientThreshold *int6 "from the server")) } // Don't change the server's bandwidth since its test succeeded - handleBandwidth(clientMax, clientBw, clientOldBw, clientThreshold, 0, "client -> server") + handleBandwidth(clientMax, clientBw, clientOldAch, clientThreshold, 0, "client -> server") } @@ -471,18 +471,18 @@ func handleCSError(clientMax *bool, clientBw, clientOldBw, clientThreshold *int6 // achieved bandwidth (ach) and the previously achieved bandwidth (oldBw). // We do not use loss since the link might be lossy, then the loss would not be a good metric. // "name" is just the name of the bandwidth to reduce to print out to the user. -func handleBandwidth(isMax *bool, currentBw, oldBw, threshold *int64, ach int64, name string) { +func handleBandwidth(isMax *bool, currentBw, oldAch, threshold *int64, ach int64, name string) { if *isMax { return } - if *oldBw < ach { + if *oldAch < ach { fmt.Printf("Increasing %s bandwidth...\n", name) *currentBw = increaseBandwidth(*currentBw, *threshold) } else { fmt.Printf("Decreasing %s bandwidth...\n", name) - *currentBw, *threshold, *isMax = decreaseBandwidth(*currentBw, *threshold, ach, *oldBw) + *currentBw, *threshold, *isMax = decreaseBandwidth(*currentBw, *threshold, ach, *oldAch) } - *oldBw = ach + *oldAch = ach } // increaseBandwidth returns a new bandwidth based on threshold and bandwidth values parameters. When the bandwidth is @@ -507,13 +507,13 @@ func increaseBandwidth(currentBandwidth, threshold int64) int64 { // decreaseBandwidth returns a new decreased bandwidth and a threshold based on the passed threshold and bandwidth // parameters, and returns true if the returned bandwidth is the maximum achievable bandwidth. -func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldBandwidth int64) (newBandwidth, +func decreaseBandwidth(currentBandwidth, threshold, achievedBandwidth, oldAchieved int64) (newBandwidth, newThreshold int64, isMaxBandwidth bool) { // Choose the larger value between them so we don't do unnecessary slow start since we know both bandwidths are // achievable on that link. - if achievedBandwidth < oldBandwidth { - newBandwidth = oldBandwidth + if achievedBandwidth < oldAchieved { + newBandwidth = oldAchieved } else { // Both achieved bandwidth and oldBw are not set which means an error occurred when using those values if achievedBandwidth == 0 {