forked from mkieweg/minq
-
Notifications
You must be signed in to change notification settings - Fork 0
/
congestion.go
280 lines (236 loc) · 8.32 KB
/
congestion.go
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/*
Package minq is a minimal implementation of QUIC, as documented at
https://quicwg.github.io/. Minq partly implements draft-04.
*/
package minq
import (
"math"
"time"
// "fmt"
)
// congestion control related constants
const (
kDefaultMss = 1460 // bytes
kInitalWindow = 10 * kDefaultMss
kMinimumWindow = 2 * kDefaultMss
kMaximumWindow = kInitalWindow
kLossReductionFactor = 0.5
)
// loss dectection related constants
const (
kMaxTLPs = 2
kReorderingThreshold = 3
kTimeReorderingFraction = 0.125
kMinTLPTimeout = 10 // ms
kMinRTOTimeout = 200 // ms
kDelayedAckTimeout = 25 // ms
// kDefaultInitialRtt = 100 // ms // already in connection.go
)
type CongestionController interface {
onPacketSent(pn uint64, isAckOnly bool, sentBytes int)
onAckReceived(acks ackRanges, delay time.Duration)
bytesAllowedToSend() int
setLostPacketHandler(handler func(pn uint64))
}
/*
* DUMMY congestion controller
*/
type CongestionControllerDummy struct{
}
func (cc *CongestionControllerDummy) onPacketSent(pn uint64, isAckOnly bool, sentBytes int){
}
func (cc *CongestionControllerDummy) onAckReceived(acks ackRanges, delay time.Duration){
}
func (cc *CongestionControllerDummy) bytesAllowedToSend() int{
/* return the the maximum int value */
return int(^uint(0) >> 1)
}
func (cc *CongestionControllerDummy) setLostPacketHandler(handler func(pn uint64)){
}
/*
* draft-ietf-quic-recovery congestion controller
*/
type CongestionControllerIetf struct {
// Congestion control related
bytesInFlight int
congestionWindow int
endOfRecovery uint64
sstresh int
// Loss detection related
lossDetectionAlarm int //TODO([email protected]) set this to the right type
handshakeCount int
tlpCount int
rtoCount int
largestSendBeforeRto uint64
timeOfLastSentPacket time.Time
largestSendPacket uint64
largestAckedPacket uint64
// largestRtt time.Duration
smoothedRtt time.Duration
rttVar float32
reorderingThreshold int
timeReorderingFraction float32
lossTime time.Time
sentPackets map[uint64]packetEntry
// others
lostPacketHandler func(pn uint64)
conn *Connection
}
type packetEntry struct{
pn uint64
txTime time.Time
bytes int
}
func (cc *CongestionControllerIetf) onPacketSent(pn uint64, isAckOnly bool, sentBytes int){
cc.timeOfLastSentPacket = time.Now()
cc.largestSendPacket = pn
packetData := packetEntry{pn, time.Now(), 0}
cc.conn.log(logTypeCongestion, "Packet send pn: %d len:%d ackonly: %v\n", pn, sentBytes, isAckOnly)
if !isAckOnly{
cc.onPacketSentCC(sentBytes)
packetData.bytes = sentBytes
cc.setLossDetectionAlarm()
}
cc.sentPackets[pn] = packetData
}
// acks is received to be a sorted list, where the largest packet numbers are at the beginning
func(cc *CongestionControllerIetf) onAckReceived(acks ackRanges, delay time.Duration){
// keep track of largest packet acked overall
if acks[0].lastPacket > cc.largestAckedPacket {
cc.largestAckedPacket = acks[0].lastPacket
}
// If the largest acked is newly acked update rtt
_, present := cc.sentPackets[acks[0].lastPacket]
if present {
//TODO([email protected]) RTT stuff
//largestRtt = time.Now - cc.sentPackets[acks[0].lastPacket].txTime
//if (latestRtt > delay){
// latestRtt -= delay
// cc.updateRtt(latestRtt)
}
// find and proccess newly acked packets
for _, ackBlock := range acks {
for pn := ackBlock.lastPacket; pn > (ackBlock.lastPacket - ackBlock.count); pn-- {
cc.conn.log(logTypeCongestion, "Ack for pn %d received", pn)
_, present := cc.sentPackets[pn]
if present {
cc.conn.log(logTypeCongestion, "First ack for pn %d received", pn)
cc.onPacketAcked(pn)
}
}
}
cc.detectLostPackets()
cc.setLossDetectionAlarm()
}
func (cc *CongestionControllerIetf) setLostPacketHandler(handler func(pn uint64)){
cc.lostPacketHandler = handler
}
func(cc *CongestionControllerIetf) updateRtt(latestRtt time.Duration){
//TODO([email protected])
}
func(cc *CongestionControllerIetf) onPacketAcked(pn uint64){
cc.onPacketAckedCC(pn)
//TODO([email protected]) some RTO stuff here
delete(cc.sentPackets, pn)
}
func(cc *CongestionControllerIetf) setLossDetectionAlarm(){
//TODO([email protected])
}
func(cc *CongestionControllerIetf) onLossDetectionAlarm(){
//TODO([email protected])
}
func(cc *CongestionControllerIetf) detectLostPackets(){
var lostPackets []packetEntry
//TODO([email protected]) implement loss detection different from reorderingThreshold
for _, packet := range cc.sentPackets {
if (cc.largestAckedPacket > packet.pn) &&
(cc.largestAckedPacket - packet.pn > uint64(cc.reorderingThreshold)) {
lostPackets = append(lostPackets, packet)
}
}
if len(lostPackets) > 0{
cc.onPacketsLost(lostPackets)
}
for _, packet := range lostPackets {
delete(cc.sentPackets, packet.pn)
}
}
func (cc *CongestionControllerIetf) onPacketSentCC(bytes_sent int){
cc.bytesInFlight += bytes_sent
cc.conn.log(logTypeCongestion, "%d bytes added to bytesInFlight", bytes_sent)
}
func (cc *CongestionControllerIetf) onPacketAckedCC(pn uint64){
cc.bytesInFlight -= cc.sentPackets[pn].bytes
cc.conn.log(logTypeCongestion, "%d bytes from packet %d removed from bytesInFlight", cc.sentPackets[pn].bytes, pn)
if pn < cc.endOfRecovery {
// Do not increase window size during recovery
return
}
if cc.congestionWindow < cc.sstresh {
// Slow start
cc.congestionWindow += cc.sentPackets[pn].bytes
cc.conn.log(logTypeCongestion, "PDV Slow Start: increasing window size with %d bytes to %d",
cc.sentPackets[pn].bytes, cc.congestionWindow)
} else {
// Congestion avoidance
cc.congestionWindow += kDefaultMss * cc.sentPackets[pn].bytes / cc.congestionWindow
cc.conn.log(logTypeCongestion, "PDV Congestion Avoidance: increasing window size to %d",
cc.congestionWindow)
}
}
func (cc *CongestionControllerIetf) onPacketsLost(packets []packetEntry){
var largestLostPn uint64 = 0
for _, packet := range packets {
// First remove lost packets from bytesInFlight and inform the connection
// of the loss
cc.conn.log(logTypeCongestion, "Packet pn: %d len: %d is lost", packet.pn, packet.bytes)
cc.bytesInFlight -= packet.bytes
if cc.lostPacketHandler != nil {
cc.lostPacketHandler(packet.pn)
}
// and keep track of the largest lost packet
if packet.pn > largestLostPn {
largestLostPn = packet.pn
}
}
// Now start a new recovery epoch if the largest lost packet is larger than the
// end of the previous recovery epoch
if cc.endOfRecovery < largestLostPn {
cc.endOfRecovery = cc.largestSendPacket
cc.congestionWindow = int(float32(cc.congestionWindow) * kLossReductionFactor)
if kMinimumWindow > cc.congestionWindow {
cc.congestionWindow = kMinimumWindow
}
cc.sstresh = cc.congestionWindow
cc.conn.log(logTypeCongestion, "PDV Recovery started. Window size: %d, sstresh: %d, endOfRecovery %d",
cc.congestionWindow, cc.sstresh, cc.endOfRecovery)
}
}
func (cc *CongestionControllerIetf) bytesAllowedToSend() int {
cc.conn.log(logTypeCongestion, "Remaining congestion window size: %d", cc.congestionWindow - cc.bytesInFlight)
return cc.congestionWindow - cc.bytesInFlight
}
func newCongestionControllerIetf(conn *Connection) *CongestionControllerIetf{
return &CongestionControllerIetf{
0, // bytesInFlight
kInitalWindow, // congestionWindow
0, // endOfRecovery
int(^uint(0) >> 1), // sstresh
0, // lossDetectionAlarm
0, // handshakeCount
0, // tlpCount
0, // rtoCount
0, // largestSendBeforeRto
time.Unix(0,0), // timeOfLastSentPacket
0, // largestSendPacket
0, // largestAckedPacket
0, // smoothedRtt
0, // rttVar
kReorderingThreshold, // reorderingThreshold
math.MaxFloat32, // timeReorderingFraction
time.Unix(0,0), // lossTime
make(map[uint64]packetEntry), // sentPackets
nil, // lostPacketHandler
conn, // conn
}
}