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 }