summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--constants/constants.go2
-rw-r--r--l4s/other.go27
-rw-r--r--l4s/unix.go51
-rw-r--r--lgc/download.go27
-rw-r--r--lgc/upload.go27
-rw-r--r--networkQuality.go20
-rw-r--r--probe/probe.go20
-rw-r--r--probe/tracer.go48
-rw-r--r--rpm/rpm.go2
9 files changed, 201 insertions, 23 deletions
diff --git a/constants/constants.go b/constants/constants.go
index fdfd1f2..8621de8 100644
--- a/constants/constants.go
+++ b/constants/constants.go
@@ -36,6 +36,8 @@ var (
DefaultConfigHost string = "networkquality.example.com"
// The default determination of whether to verify server certificates
DefaultInsecureSkipVerify bool = true
+
+ DefaultL4SCongestionControlAlgorithm string = "prague"
)
type SpecParametersCliOptions struct {
diff --git a/l4s/other.go b/l4s/other.go
new file mode 100644
index 0000000..cca6ad6
--- /dev/null
+++ b/l4s/other.go
@@ -0,0 +1,27 @@
+//go:build !linux
+// +build !linux
+
+/*
+ * This file is part of Go Responsiveness.
+ *
+ * Go Responsiveness is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software Foundation,
+ * either version 2 of the License, or (at your option) any later version.
+ * Go Responsiveness is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Go Responsiveness. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package l4s
+
+import (
+ "fmt"
+ "net"
+)
+
+func SetL4S(conn net.Conn, algorithm *string) error {
+ return fmt.Errorf("setting L4S is not supported on this platform")
+}
diff --git a/l4s/unix.go b/l4s/unix.go
new file mode 100644
index 0000000..cfaf43c
--- /dev/null
+++ b/l4s/unix.go
@@ -0,0 +1,51 @@
+//go:build linux
+// +build linux
+
+/*
+ * This file is part of Go Responsiveness.
+ *
+ * Go Responsiveness is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software Foundation,
+ * either version 2 of the License, or (at your option) any later version.
+ * Go Responsiveness is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Go Responsiveness. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package l4s
+
+import (
+ "crypto/tls"
+ "fmt"
+ "net"
+ "syscall"
+)
+
+func SetL4S(conn net.Conn, algorithm *string) error {
+ if algorithm == nil {
+ panic("Attempting to set L4S congestion control without specifying a congestion control algorithm.")
+ }
+ tlsConn, ok := conn.(*tls.Conn)
+ if !ok {
+ return fmt.Errorf("when setting L4S congestion control algorithm, outermost connection is not a TLS connection")
+ }
+ tcpConn, ok := tlsConn.NetConn().(*net.TCPConn)
+ if !ok {
+ return fmt.Errorf("when setting L4S congestion control algorithm, could not get the underlying IP connection")
+ }
+ rawConn, err := tcpConn.SyscallConn()
+ if err != nil {
+ return fmt.Errorf("when setting L4S congestion control algorithm, could not get the underlying raw connection")
+ }
+ var sockoptError error = nil
+ rawConn.Control(func(fd uintptr) {
+ sockoptError = syscall.SetsockoptString(int(fd), syscall.IPPROTO_TCP, syscall.TCP_CONGESTION, *algorithm)
+ })
+ if sockoptError != nil {
+ return fmt.Errorf("when setting L4S congestion control algorithm, could not set the algorithm to %v: %v", *algorithm, sockoptError.Error())
+ }
+ return nil
+}
diff --git a/lgc/download.go b/lgc/download.go
index 3ce4389..38e07cc 100644
--- a/lgc/download.go
+++ b/lgc/download.go
@@ -21,11 +21,13 @@ import (
"io"
"net/http"
"net/http/httptrace"
+ "os"
"sync"
"sync/atomic"
"time"
"github.com/network-quality/goresponsiveness/debug"
+ "github.com/network-quality/goresponsiveness/l4s"
"github.com/network-quality/goresponsiveness/stats"
"github.com/network-quality/goresponsiveness/traceable"
"github.com/network-quality/goresponsiveness/utilities"
@@ -47,16 +49,18 @@ type LoadGeneratingConnectionDownload struct {
tracer *httptrace.ClientTrace
stats stats.TraceStats
status LgcStatus
+ congestionControl *string
statusLock *sync.Mutex
statusWaiter *sync.Cond
}
-func NewLoadGeneratingConnectionDownload(url string, keyLogger io.Writer, connectToAddr string, insecureSkipVerify bool) LoadGeneratingConnectionDownload {
+func NewLoadGeneratingConnectionDownload(url string, keyLogger io.Writer, connectToAddr string, insecureSkipVerify bool, congestionControl *string) LoadGeneratingConnectionDownload {
lgd := LoadGeneratingConnectionDownload{
URL: url,
KeyLogger: keyLogger,
ConnectToAddr: connectToAddr,
InsecureSkipVerify: insecureSkipVerify,
+ congestionControl: congestionControl,
statusLock: &sync.Mutex{},
}
lgd.statusWaiter = sync.NewCond(lgd.statusLock)
@@ -161,6 +165,27 @@ func (lgd *LoadGeneratingConnectionDownload) SetGotConnTimeInfo(
lgd.stats.ConnInfo,
)
}
+
+ if lgd.congestionControl != nil {
+ if debug.IsDebug(lgd.debug) {
+ fmt.Printf(
+ "Attempting to set congestion control algorithm to %v for connection %v at %v with info %v\n",
+ *lgd.congestionControl,
+ lgd.ClientId(),
+ lgd.stats.GetConnectionDoneTime,
+ lgd.stats.ConnInfo,
+ )
+ }
+ if err := l4s.SetL4S(lgd.stats.ConnInfo.Conn, lgd.congestionControl); err != nil {
+ fmt.Fprintf(
+ os.Stderr,
+ "Error setting L4S for %v at %v: %v\n",
+ lgd.ClientId(),
+ lgd.stats.GetConnectionDoneTime,
+ err.Error(),
+ )
+ }
+ }
}
func (lgd *LoadGeneratingConnectionDownload) SetTLSHandshakeStartTime(
diff --git a/lgc/upload.go b/lgc/upload.go
index 32dd86a..2da1782 100644
--- a/lgc/upload.go
+++ b/lgc/upload.go
@@ -21,11 +21,13 @@ import (
"io"
"net/http"
"net/http/httptrace"
+ "os"
"sync"
"sync/atomic"
"time"
"github.com/network-quality/goresponsiveness/debug"
+ "github.com/network-quality/goresponsiveness/l4s"
"github.com/network-quality/goresponsiveness/stats"
"github.com/network-quality/goresponsiveness/traceable"
"github.com/network-quality/goresponsiveness/utilities"
@@ -47,16 +49,18 @@ type LoadGeneratingConnectionUpload struct {
tracer *httptrace.ClientTrace
stats stats.TraceStats
status LgcStatus
+ congestionControl *string
statusLock *sync.Mutex
statusWaiter *sync.Cond
}
-func NewLoadGeneratingConnectionUpload(url string, keyLogger io.Writer, connectToAddr string, insecureSkipVerify bool) LoadGeneratingConnectionUpload {
+func NewLoadGeneratingConnectionUpload(url string, keyLogger io.Writer, connectToAddr string, insecureSkipVerify bool, congestionControl *string) LoadGeneratingConnectionUpload {
lgu := LoadGeneratingConnectionUpload{
URL: url,
KeyLogger: keyLogger,
ConnectToAddr: connectToAddr,
InsecureSkipVerify: insecureSkipVerify,
+ congestionControl: congestionControl,
statusLock: &sync.Mutex{},
}
lgu.status = LGC_STATUS_NOT_STARTED
@@ -158,6 +162,27 @@ func (lgu *LoadGeneratingConnectionUpload) SetGotConnTimeInfo(
lgu.stats.ConnInfo,
)
}
+
+ if lgu.congestionControl != nil {
+ if debug.IsDebug(lgu.debug) {
+ fmt.Printf(
+ "Attempting to set congestion control algorithm to %v for connection %v at %v with info %v\n",
+ *lgu.congestionControl,
+ lgu.ClientId(),
+ lgu.stats.GetConnectionDoneTime,
+ lgu.stats.ConnInfo,
+ )
+ }
+ if err := l4s.SetL4S(lgu.stats.ConnInfo.Conn, lgu.congestionControl); err != nil {
+ fmt.Fprintf(
+ os.Stderr,
+ "Error setting L4S for %v at %v: %v\n",
+ lgu.ClientId(),
+ lgu.stats.GetConnectionDoneTime,
+ err.Error(),
+ )
+ }
+ }
}
func (lgu *LoadGeneratingConnectionUpload) SetTLSHandshakeStartTime(
diff --git a/networkQuality.go b/networkQuality.go
index d551026..67b7ba1 100644
--- a/networkQuality.go
+++ b/networkQuality.go
@@ -165,6 +165,8 @@ var (
false,
"Calculate a relative RPM.",
)
+ withL4S = flag.Bool("with-l4s", false, "Use L4S (with default TCP prague congestion control algorithm.)")
+ withL4SAlgorithm = flag.String("with-l4s-algorithm", "", "Use L4S (with specified congestion control algorithm.)")
)
func main() {
@@ -250,6 +252,18 @@ func main() {
}
}
+ var congestionControlChosen *string = nil
+ if *withL4S || *withL4SAlgorithm != "" {
+ congestionControlChosen = &constants.DefaultL4SCongestionControlAlgorithm
+ if *withL4SAlgorithm != "" {
+ congestionControlChosen = withL4SAlgorithm
+ }
+ }
+
+ if congestionControlChosen != nil && debug.IsDebug(debugLevel) {
+ fmt.Printf("Doing congestion control with the %v algorithm.\n", *congestionControlChosen)
+ }
+
if err := config.Get(configHostPort, *configPath, *insecureSkipVerify,
sslKeyFileConcurrentWriter); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
@@ -414,12 +428,12 @@ func main() {
*/
downloadDirection.CreateLgdc = func() lgc.LoadGeneratingConnection {
lgd := lgc.NewLoadGeneratingConnectionDownload(config.Urls.LargeUrl,
- sslKeyFileConcurrentWriter, config.ConnectToAddr, *insecureSkipVerify)
+ sslKeyFileConcurrentWriter, config.ConnectToAddr, *insecureSkipVerify, congestionControlChosen)
return &lgd
}
uploadDirection.CreateLgdc = func() lgc.LoadGeneratingConnection {
lgu := lgc.NewLoadGeneratingConnectionUpload(config.Urls.UploadUrl,
- sslKeyFileConcurrentWriter, config.ConnectToAddr, *insecureSkipVerify)
+ sslKeyFileConcurrentWriter, config.ConnectToAddr, *insecureSkipVerify, congestionControlChosen)
return &lgu
}
@@ -440,6 +454,7 @@ func main() {
URL: config.Urls.SmallUrl,
ConnectToAddr: config.ConnectToAddr,
InsecureSkipVerify: *insecureSkipVerify,
+ CongestionControl: congestionControlChosen,
}
}
@@ -448,6 +463,7 @@ func main() {
URL: config.Urls.SmallUrl,
ConnectToAddr: config.ConnectToAddr,
InsecureSkipVerify: *insecureSkipVerify,
+ CongestionControl: congestionControlChosen,
}
}
diff --git a/probe/probe.go b/probe/probe.go
index 951e65e..912833f 100644
--- a/probe/probe.go
+++ b/probe/probe.go
@@ -23,6 +23,7 @@ import (
"os"
"time"
+ "github.com/network-quality/goresponsiveness/constants"
"github.com/network-quality/goresponsiveness/debug"
"github.com/network-quality/goresponsiveness/extendedstats"
"github.com/network-quality/goresponsiveness/utilities"
@@ -34,16 +35,18 @@ type ProbeConfiguration struct {
ConnectToAddr string
URL string
Host string
+ CongestionControl *string
InsecureSkipVerify bool
}
type ProbeDataPoint struct {
- Time time.Time `Description:"Time of the generation of the data point." Formatter:"Format" FormatterArgument:"01-02-2006-15-04-05.000"`
- RoundTripCount uint64 `Description:"The number of round trips measured by this data point."`
- Duration time.Duration `Description:"The duration for this measurement." Formatter:"Seconds"`
- TCPRtt time.Duration `Description:"The underlying connection's RTT at probe time." Formatter:"Seconds"`
- TCPCwnd uint32 `Description:"The underlying connection's congestion window at probe time."`
- Type ProbeType `Description:"The type of the probe." Formatter:"Value"`
+ Time time.Time `Description:"Time of the generation of the data point." Formatter:"Format" FormatterArgument:"01-02-2006-15-04-05.000"`
+ RoundTripCount uint64 `Description:"The number of round trips measured by this data point."`
+ Duration time.Duration `Description:"The duration for this measurement." Formatter:"Seconds"`
+ TCPRtt time.Duration `Description:"The underlying connection's RTT at probe time." Formatter:"Seconds"`
+ TCPCwnd uint32 `Description:"The underlying connection's congestion window at probe time."`
+ Type ProbeType `Description:"The type of the probe." Formatter:"Value"`
+ CongestionControl string `Description:"The congestion control algorithm used."`
}
const (
@@ -81,6 +84,7 @@ func Probe(
probeHost string, // optional: for use with a test_endpoint
probeType ProbeType,
probeId uint,
+ congestionControl *string,
captureExtendedStats bool,
debugging *debug.DebugWithPrefix,
) (*ProbeDataPoint, error) {
@@ -88,7 +92,7 @@ func Probe(
return nil, fmt.Errorf("cannot start a probe with a nil client")
}
- probeTracer := NewProbeTracer(client, probeType, probeId, debugging)
+ probeTracer := NewProbeTracer(client, probeType, probeId, congestionControl, debugging)
time_before_probe := time.Now()
probe_req, err := http.NewRequestWithContext(
httptrace.WithClientTrace(managingCtx, probeTracer.trace),
@@ -215,5 +219,7 @@ func Probe(
TCPRtt: tcpRtt,
TCPCwnd: tcpCwnd,
Type: probeType,
+ CongestionControl: *utilities.Conditional(congestionControl == nil,
+ &constants.DefaultL4SCongestionControlAlgorithm, congestionControl),
}, nil
}
diff --git a/probe/tracer.go b/probe/tracer.go
index e59e1aa..b9c4a3f 100644
--- a/probe/tracer.go
+++ b/probe/tracer.go
@@ -23,18 +23,20 @@ import (
"time"
"github.com/network-quality/goresponsiveness/debug"
+ "github.com/network-quality/goresponsiveness/l4s"
"github.com/network-quality/goresponsiveness/stats"
"github.com/network-quality/goresponsiveness/traceable"
"github.com/network-quality/goresponsiveness/utilities"
)
type ProbeTracer struct {
- client *http.Client
- stats *stats.TraceStats
- trace *httptrace.ClientTrace
- debug debug.DebugLevel
- probeid uint
- probeType ProbeType
+ client *http.Client
+ stats *stats.TraceStats
+ trace *httptrace.ClientTrace
+ debug debug.DebugLevel
+ probeid uint
+ probeType ProbeType
+ congestionControl *string
}
func (p *ProbeTracer) String() string {
@@ -226,6 +228,26 @@ func (probe *ProbeTracer) SetGotConnTimeInfo(
)
}
}
+
+ if probe.congestionControl != nil {
+ if debug.IsDebug(probe.debug) {
+ fmt.Printf(
+ "(%s Probe) Setting congestion control for Probe %v to %v.\n",
+ probe.probeType.Value(),
+ probe.ProbeId(),
+ *probe.congestionControl,
+ )
+ }
+ if err := l4s.SetL4S(probe.stats.ConnInfo.Conn, probe.congestionControl); err != nil {
+ fmt.Fprintf(
+ os.Stderr,
+ "(%s Probe) Probe %v could not set L4s because %v.\n",
+ probe.probeType.Value(),
+ probe.ProbeId(),
+ err.Error(),
+ )
+ }
+ }
}
func (probe *ProbeTracer) SetTLSHandshakeStartTime(
@@ -294,15 +316,17 @@ func NewProbeTracer(
client *http.Client,
probeType ProbeType,
probeId uint,
+ congestionControl *string,
debugging *debug.DebugWithPrefix,
) *ProbeTracer {
probe := &ProbeTracer{
- client: client,
- stats: &stats.TraceStats{},
- trace: nil,
- debug: debugging.Level,
- probeid: probeId,
- probeType: probeType,
+ client: client,
+ stats: &stats.TraceStats{},
+ trace: nil,
+ debug: debugging.Level,
+ probeid: probeId,
+ probeType: probeType,
+ congestionControl: congestionControl,
}
trace := traceable.GenerateHttpTimingTracer(probe, debugging.Level)
diff --git a/rpm/rpm.go b/rpm/rpm.go
index 3ebdefd..902e5bb 100644
--- a/rpm/rpm.go
+++ b/rpm/rpm.go
@@ -196,6 +196,7 @@ func ResponsivenessProber[BucketType utilities.Number](
foreignProbeConfiguration.Host,
probe.Foreign,
probeCount,
+ foreignProbeConfiguration.CongestionControl,
captureExtendedStats,
debugging,
)
@@ -258,6 +259,7 @@ func ResponsivenessProber[BucketType utilities.Number](
selfProbeConfiguration.Host,
utilities.Conditional(probeDirection == lgc.LGC_DOWN, probe.SelfDown, probe.SelfUp),
probeCount,
+ selfProbeConfiguration.CongestionControl,
captureExtendedStats,
debugging,
)