diff options
| -rw-r--r-- | constants/constants.go | 2 | ||||
| -rw-r--r-- | l4s/other.go | 27 | ||||
| -rw-r--r-- | l4s/unix.go | 51 | ||||
| -rw-r--r-- | lgc/download.go | 27 | ||||
| -rw-r--r-- | lgc/upload.go | 27 | ||||
| -rw-r--r-- | networkQuality.go | 20 | ||||
| -rw-r--r-- | probe/probe.go | 20 | ||||
| -rw-r--r-- | probe/tracer.go | 48 | ||||
| -rw-r--r-- | rpm/rpm.go | 2 |
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) @@ -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, ) |
