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

     1  //go:build darwin
     2  // +build darwin
     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  	Maxseg               uint64
    31  	TotalRetransmissions uint64
    32  	totalSent            uint64
    33  	TotalReorderings     uint64
    34  	AverageRtt           float64
    35  	rtt_measurements     uint64
    36  	total_rtt            float64
    37  	RetransmitRatio      float64
    38  }
    39  
    40  func ExtendedStatsAvailable() bool {
    41  	return true
    42  }
    43  
    44  type TCPInfo struct {
    45  	unix.TCPConnectionInfo
    46  	Rtt uint32 // Srtt under Darwin
    47  }
    48  
    49  func (es *AggregateExtendedStats) IncorporateConnectionStats(basicConn net.Conn) error {
    50  	if info, err := GetTCPInfo(basicConn); err != nil {
    51  		return fmt.Errorf("OOPS: Could not get the TCP info for the connection: %v", err)
    52  	} else {
    53  		es.Maxseg = utilities.Max(es.Maxseg, uint64(info.Maxseg))
    54  		es.TotalReorderings += info.Rxoutoforderbytes
    55  		es.TotalRetransmissions += info.Txretransmitbytes
    56  		es.totalSent += info.Txbytes
    57  		es.total_rtt += float64(info.Rtt)
    58  		es.rtt_measurements += 1
    59  		es.AverageRtt = es.total_rtt / float64(es.rtt_measurements)
    60  		es.RetransmitRatio = (float64(es.TotalRetransmissions) / float64(es.totalSent)) * 100.0
    61  	}
    62  	return nil
    63  }
    64  
    65  func (es *AggregateExtendedStats) Repr() string {
    66  	return fmt.Sprintf(`Extended Statistics:
    67  	Maximum Segment Size: %v
    68  	Total Bytes Retransmitted: %v
    69  	Retransmission Ratio: %.2f%%
    70  	Total Bytes Reordered: %v
    71  	Average RTT: %v
    72  `, es.Maxseg, es.TotalRetransmissions, es.RetransmitRatio, es.TotalReorderings, es.AverageRtt)
    73  }
    74  
    75  func GetTCPInfo(basicConn net.Conn) (*TCPInfo, error) {
    76  	tlsConn, ok := basicConn.(*tls.Conn)
    77  	if !ok {
    78  		return nil, fmt.Errorf(
    79  			"OOPS: Could not get the TCP info for the connection (not a TLS connection)",
    80  		)
    81  	}
    82  	tcpConn, ok := tlsConn.NetConn().(*net.TCPConn)
    83  	if !ok {
    84  		return nil, fmt.Errorf(
    85  			"OOPS: Could not get the TCP info for the connection (not a TCP connection)",
    86  		)
    87  	}
    88  	rawConn, err := tcpConn.SyscallConn()
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	var rawInfo *unix.TCPConnectionInfo = nil
    94  	var tcpInfo *TCPInfo = nil
    95  	rerr := rawConn.Control(func(fd uintptr) {
    96  		rawInfo, err = unix.GetsockoptTCPConnectionInfo(
    97  			int(fd),
    98  			unix.IPPROTO_TCP,
    99  			unix.TCP_CONNECTION_INFO,
   100  		)
   101  	})
   102  	if rerr != nil {
   103  		return nil, rerr
   104  	}
   105  	if rawInfo != nil && err == nil {
   106  		tcpInfo = &TCPInfo{TCPConnectionInfo: *rawInfo, Rtt: rawInfo.Srtt}
   107  	}
   108  	return tcpInfo, err
   109  }