From 18bcc4fe73bd8245cde001c939b400693a0d9410 Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Wed, 17 Aug 2022 22:07:35 -0400 Subject: [Feature] Add TCP information (RTT and Cwnd) to Logging This patch adds support for logging the underlying RTT and Cwnd of the TCP connection used when doing probes. More work to follow in order to add support for this information on Windows and Darwin. --- datalogger/logger.go | 11 ++++++----- extendedstats/other.go | 6 +++++- extendedstats/unix.go | 29 +++++++++++------------------ go.mod | 2 +- go.sum | 6 ++---- networkQuality.go | 2 +- rpm/rpm.go | 18 ++++++++++++++++-- 7 files changed, 42 insertions(+), 32 deletions(-) diff --git a/datalogger/logger.go b/datalogger/logger.go index 1a4cd20..6236b59 100644 --- a/datalogger/logger.go +++ b/datalogger/logger.go @@ -66,19 +66,20 @@ func doCustomFormatting(value reflect.Value, tag reflect.StructTag) (string, err } formatMethodArgument, success := tag.Lookup("FormatterArgument") if !success { - return "", fmt.Errorf("Could not find the formatter name") + formatMethodArgument = "" } formatMethod := value.MethodByName(formatMethodName) if formatMethod == reflect.ValueOf(0) { return "", fmt.Errorf("Type %v does not support a method named %v", value.Type(), formatMethodName) } - - formatMethodArgumentUsable := make([]reflect.Value, 1) - formatMethodArgumentUsable[0] = reflect.ValueOf(formatMethodArgument) + var formatMethodArgumentUsable []reflect.Value = make([]reflect.Value, 0) + if formatMethodArgument != "" { + formatMethodArgumentUsable = append(formatMethodArgumentUsable, reflect.ValueOf(formatMethodArgument)) + } result := formatMethod.Call(formatMethodArgumentUsable) if len(result) == 1 { - return result[0].String(), nil + return fmt.Sprintf("%v", result[0]), nil } return "", fmt.Errorf("Too many results returned by the format method's invocation.") } diff --git a/extendedstats/other.go b/extendedstats/other.go index b4eae18..50caef6 100644 --- a/extendedstats/other.go +++ b/extendedstats/other.go @@ -11,7 +11,7 @@ import ( type ExtendedStats struct{} func (es *ExtendedStats) IncorporateConnectionStats(conn net.Conn) error { - return fmt.Errorf("OOPS: IncorporateConnectionStats is not supported on this platform") + return fmt.Errorf("IncorporateConnectionStats is not supported on this platform") } func (es *ExtendedStats) Repr() string { @@ -21,3 +21,7 @@ func (es *ExtendedStats) Repr() string { func ExtendedStatsAvailable() bool { return false } + +func GetTCPInfo(basicConn net.Conn) (interface, error) { + return nil, fmt.Errorf("GetTCPInfo is not supported on this platform") +} diff --git a/extendedstats/unix.go b/extendedstats/unix.go index a2d4d30..3db94fc 100644 --- a/extendedstats/unix.go +++ b/extendedstats/unix.go @@ -27,20 +27,8 @@ func ExtendedStatsAvailable() bool { return true } -func (es *ExtendedStats) IncorporateConnectionStats(rawConn net.Conn) error { - tlsConn, ok := rawConn.(*tls.Conn) - if !ok { - return fmt.Errorf( - "OOPS: Could not get the TCP info for the connection (not a TLS connection)", - ) - } - tcpConn, ok := tlsConn.NetConn().(*net.TCPConn) - if !ok { - return fmt.Errorf( - "OOPS: Could not get the TCP info for the connection (not a TCP connection)", - ) - } - if info, err := getTCPInfo(tcpConn); err != nil { +func (es *ExtendedStats) IncorporateConnectionStats(basicConn net.Conn) error { + if info, err := GetTCPInfo(basicConn); err != nil { return fmt.Errorf("OOPS: Could not get the TCP info for the connection: %v", err) } else { es.MaxPathMtu = utilities.Max(es.MaxPathMtu, uint64(info.Pmtu)) @@ -67,16 +55,21 @@ func (es *ExtendedStats) Repr() string { `, es.MaxPathMtu, es.MaxSendMss, es.MaxRecvMss, es.TotalRetransmissions, es.TotalReorderings, es.AverageRtt) } -func getTCPInfo(connection net.Conn) (*unix.TCPInfo, error) { - tcpConn, ok := connection.(*net.TCPConn) +func GetTCPInfo(basicConn net.Conn) (*unix.TCPInfo, error) { + tlsConn, ok := basicConn.(*tls.Conn) if !ok { - return nil, fmt.Errorf("connection is not a net.TCPConn") + return nil, fmt.Errorf("OOPS: Outermost connection is not a TLS connection") + } + tcpConn, ok := tlsConn.NetConn().(*net.TCPConn) + if !ok { + return nil, fmt.Errorf( + "OOPS: Could not get the TCP info for the connection (not a TCP connection)", + ) } rawConn, err := tcpConn.SyscallConn() if err != nil { return nil, err } - var info *unix.TCPInfo = nil rawConn.Control(func(fd uintptr) { info, err = unix.GetsockoptTCPInfo(int(fd), unix.SOL_TCP, unix.TCP_INFO) diff --git a/go.mod b/go.mod index 5fcee76..c017675 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( golang.org/x/net v0.0.0-20220225172249-27dd8689420f - golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 ) require golang.org/x/text v0.3.7 // indirect diff --git a/go.sum b/go.sum index 3fe6d08..75307bd 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,6 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 h1:wEZYwx+kK+KlZ0hpvP2Ls1Xr4+RWnlzGFwPP0aiDjIU= -golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/networkQuality.go b/networkQuality.go index f20ca7d..53b2817 100644 --- a/networkQuality.go +++ b/networkQuality.go @@ -406,7 +406,7 @@ func main() { if err := extendedStats.IncorporateConnectionStats(downloadDataCollectionResult.LGCs[i].Stats().ConnInfo.Conn); err != nil { fmt.Fprintf( os.Stderr, - "Warning: Could not add extended stats for the connection: %v", + "Warning: Could not add extended stats for the connection: %v\n", err, ) } diff --git a/rpm/rpm.go b/rpm/rpm.go index 19da8f5..fe8184e 100644 --- a/rpm/rpm.go +++ b/rpm/rpm.go @@ -27,6 +27,7 @@ import ( "github.com/network-quality/goresponsiveness/constants" "github.com/network-quality/goresponsiveness/datalogger" "github.com/network-quality/goresponsiveness/debug" + "github.com/network-quality/goresponsiveness/extendedstats" "github.com/network-quality/goresponsiveness/lgc" "github.com/network-quality/goresponsiveness/ma" "github.com/network-quality/goresponsiveness/stats" @@ -63,7 +64,9 @@ type ProbeConfiguration struct { 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."` + 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 ThroughputDataPoint struct { @@ -186,7 +189,18 @@ func Probe( ) } }() - dataPoint := ProbeDataPoint{Time: time_before_probe, RoundTripCount: roundTripCount, Duration: totalDelay} + tcpRtt := time.Duration(0 * time.Second) + tcpCwnd := uint32(0) + if extendedstats.ExtendedStatsAvailable() { + tcpInfo, err := extendedstats.GetTCPInfo(probeTracer.stats.ConnInfo.Conn) + if err == nil { + tcpRtt = time.Duration(tcpInfo.Rtt) * time.Microsecond + tcpCwnd = tcpInfo.Snd_cwnd + } else { + fmt.Printf("Warning: Could not fetch the extended stats for a probe: %v\n", err) + } + } + dataPoint := ProbeDataPoint{Time: time_before_probe, RoundTripCount: roundTripCount, Duration: totalDelay, TCPRtt: tcpRtt, TCPCwnd: tcpCwnd} if !utilities.IsInterfaceNil(logger) { logger.LogRecord(dataPoint) } -- cgit v1.2.3