github.com/network-quality/goresponsiveness@v0.0.0-20240129151524-343954285090/extendedstats/unix.go (about)

     1  //go:build dragonfly || freebsd || linux || netbsd || openbsd
     2  // +build dragonfly freebsd linux netbsd openbsd
     3  
     4  /*
     5   * This file is part of Go Responsiveness.
     6   *
     7   * Go Responsiveness is free software: you can redistribute it and/or modify it under
     8   * the terms of the GNU General Public License as published by the Free Software Foundation,
     9   * either version 2 of the License, or (at your option) any later version.
    10   * Go Responsiveness is distributed in the hope that it will be useful, but WITHOUT ANY
    11   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
    12   * PARTICULAR PURPOSE. See the GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License along
    15   * with Go Responsiveness. If not, see <https://www.gnu.org/licenses/>.
    16   */
    17  
    18  package extendedstats
    19  
    20  import (
    21  	"crypto/tls"
    22  	"fmt"
    23  	"net"
    24  
    25  	"github.com/network-quality/goresponsiveness/utilities"
    26  	"golang.org/x/sys/unix"
    27  )
    28  
    29  type AggregateExtendedStats struct {
    30  	MaxPathMtu           uint64
    31  	MaxSendMss           uint64
    32  	MaxRecvMss           uint64
    33  	TotalRetransmissions uint64
    34  	TotalReorderings     uint64
    35  	AverageRtt           float64
    36  	rtt_measurements     uint64
    37  	total_rtt            float64
    38  }
    39  
    40  func ExtendedStatsAvailable() bool {
    41  	return true
    42  }
    43  
    44  func (es *AggregateExtendedStats) IncorporateConnectionStats(basicConn net.Conn) error {
    45  	if info, err := GetTCPInfo(basicConn); err != nil {
    46  		return fmt.Errorf("OOPS: Could not get the TCP info for the connection: %v", err)
    47  	} else {
    48  		es.MaxPathMtu = utilities.Max(es.MaxPathMtu, uint64(info.Pmtu))
    49  		es.MaxRecvMss = utilities.Max(es.MaxRecvMss, uint64(info.Rcv_mss))
    50  		es.MaxSendMss = utilities.Max(es.MaxSendMss, uint64(info.Snd_mss))
    51  		// https://lkml.iu.edu/hypermail/linux/kernel/1705.0/01790.html
    52  		es.TotalRetransmissions += uint64(info.Total_retrans)
    53  		es.TotalReorderings += uint64(info.Reordering)
    54  		es.total_rtt += float64(info.Rtt)
    55  		es.rtt_measurements += 1
    56  		es.AverageRtt = es.total_rtt / float64(es.rtt_measurements)
    57  	}
    58  	return nil
    59  }
    60  
    61  func (es *AggregateExtendedStats) Repr() string {
    62  	return fmt.Sprintf(`Extended Statistics:
    63  	Maximum Path MTU:      %v
    64  	Maximum Send MSS:      %v
    65  	Maximum Recv MSS:      %v
    66  	Total Retransmissions: %v
    67  	Total Reorderings:     %v
    68  	Average RTT:           %v
    69  `, es.MaxPathMtu, es.MaxSendMss, es.MaxRecvMss, es.TotalRetransmissions, es.TotalReorderings, es.AverageRtt)
    70  }
    71  
    72  func GetTCPInfo(basicConn net.Conn) (*unix.TCPInfo, error) {
    73  	tlsConn, ok := basicConn.(*tls.Conn)
    74  	if !ok {
    75  		return nil, fmt.Errorf("OOPS: Outermost connection is not a TLS connection")
    76  	}
    77  	tcpConn, ok := tlsConn.NetConn().(*net.TCPConn)
    78  	if !ok {
    79  		return nil, fmt.Errorf(
    80  			"OOPS: Could not get the TCP info for the connection (not a TCP connection)",
    81  		)
    82  	}
    83  	rawConn, err := tcpConn.SyscallConn()
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	var info *unix.TCPInfo = nil
    88  	rawConn.Control(func(fd uintptr) {
    89  		info, err = unix.GetsockoptTCPInfo(int(fd), unix.SOL_TCP, unix.TCP_INFO)
    90  	})
    91  	return info, err
    92  }