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 }