1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
//go:build windows
// +build windows
package extendedstats
import (
"crypto/tls"
"fmt"
"net"
"unsafe"
"github.com/network-quality/goresponsiveness/utilities"
"golang.org/x/sys/windows"
)
type ExtendedStats struct {
MaxMss uint64
TotalBytesSent uint64
TotalBytesReceived uint64
TotalBytesReordered uint64
TotalBytesRetransmitted uint64
RetransmitRatio float64
AverageRtt float64
rtt_measurements uint64
total_rtt float64
}
type TCPINFO_BASE struct {
State uint32
Mss uint32
ConnectionTimeMs uint64
TimestampsEnabled bool
RttUs uint32
MinRttUs uint32
BytesInFlight uint32
Cwnd uint32
SndWnd uint32
RcvWnd uint32
RcvBuf uint32
BytesOut uint64
BytesIn uint64
BytesReordered uint32
BytesRetrans uint32
FastRetrans uint32
DupAcksIn uint32
TimeoutEpisodes uint32
SynRetrans byte // UCHAR
}
// https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/shared/mstcpip.h#L289
type TCPINFO_V0 struct {
TCPINFO_BASE
}
// https://docs.microsoft.com/en-us/windows/win32/api/mstcpip/ns-mstcpip-tcp_info_v1
type TCPINFO_V1 struct {
TCPINFO_BASE
SndLimTransRwin uint32
SndLimTimeRwin uint32
SndLimBytesRwin uint64
SndLimTransCwnd uint32
SndLimTimeCwnd uint32
SndLimBytesCwnd uint64
SndLimTransSnd uint32
SndLimTimeSnd uint32
SndLimBytesSnd uint64
}
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 {
return fmt.Errorf("OOPS: Could not get the TCP info for the connection: %v", err)
} else {
es.MaxMss = utilities.Max(es.MaxMss, uint64(info.Mss))
es.TotalBytesReordered += uint64(info.BytesReordered)
es.TotalBytesRetransmitted += uint64(info.BytesRetrans)
es.TotalBytesSent += info.BytesOut
es.TotalBytesReceived += info.BytesIn
es.total_rtt += float64(info.RttUs)
es.rtt_measurements += 1
es.AverageRtt = es.total_rtt / float64(es.rtt_measurements)
es.RetransmitRatio = (float64(es.TotalBytesRetransmitted) / float64(es.TotalBytesSent)) * 100.0
}
return nil
}
func (es *ExtendedStats) Repr() string {
return fmt.Sprintf(`Extended Statistics:
Maximum Segment Size: %v
Total Bytes Retransmitted: %v
Retransmission Ratio: %.2f%%
Total Bytes Reordered: %v
Average RTT: %v
`, es.MaxMss, es.TotalBytesRetransmitted, es.RetransmitRatio, es.TotalBytesReordered, es.AverageRtt)
}
func ExtendedStatsAvailable() bool {
return true
}
func getTCPInfo(connection net.Conn) (*TCPINFO_V1, error) {
tcpConn, ok := connection.(*net.TCPConn)
if !ok {
return nil, fmt.Errorf("connection is not a net.TCPConn")
}
rawConn, err := tcpConn.SyscallConn()
if err != nil {
return nil, err
}
// SIO_TCP_INFO
// https://docs.microsoft.com/en-us/windows/win32/winsock/sio-tcp-info
// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/mstcpip.h
iocc := uint32(windows.IOC_INOUT | windows.IOC_VENDOR | 39)
// Should be a DWORD, 0 for version 0, 1 for version 1 tcp_info:
// 0: https://docs.microsoft.com/en-us/windows/win32/api/mstcpip/ns-mstcpip-tcp_info_v0
// 1: https://docs.microsoft.com/en-us/windows/win32/api/mstcpip/ns-mstcpip-tcp_info_v1
inbuf := uint32(1)
// Size of the inbuf variable
cbif := uint32(4)
outbuf := TCPINFO_V1{}
cbob := uint32(unsafe.Sizeof(outbuf)) // Size = 136 for V1 and 88 for V0
// Size pointer of return object
cbbr := uint32(0)
overlapped := windows.Overlapped{}
completionRoutine := uintptr(0)
rawConn.Control(func(fd uintptr) {
err = windows.WSAIoctl(
windows.Handle(fd),
iocc,
(*byte)(unsafe.Pointer(&inbuf)),
cbif,
(*byte)(unsafe.Pointer(&outbuf)),
cbob,
&cbbr,
&overlapped,
completionRoutine,
)
})
return &outbuf, err
}
|