Skip to content

Commit 48d6748

Browse files
AnonymousSean-Der
authored andcommitted
Implement retransmit backoff according to 4.2.4.1
1 parent 45e16a0 commit 48d6748

6 files changed

Lines changed: 58 additions & 30 deletions

File tree

config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ type Config struct {
5757
// defaults to time.Second
5858
FlightInterval time.Duration
5959

60+
// DisableRetransmitBackoff can be used to the disable the backoff feature
61+
// when sending outbound messages as specified in RFC 4347 4.2.4.1
62+
DisableRetransmitBackoff bool
63+
6064
// PSK sets the pre-shared key used by this DTLS connection
6165
// If PSK is non-nil only PSK CipherSuites will be used
6266
PSK PSKCallback

conn.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ func handshakeConn(ctx context.Context, conn *Conn, config *Config, isClient boo
202202
rootCAs: config.RootCAs,
203203
clientCAs: config.ClientCAs,
204204
customCipherSuites: config.CustomCipherSuites,
205-
retransmitInterval: workerInterval,
205+
initialRetransmitInterval: workerInterval,
206+
disableRetransmitBackoff: config.DisableRetransmitBackoff,
206207
log: conn.log,
207208
initialEpoch: 0,
208209
keyLogWriter: config.KeyLogWriter,

conn_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3319,7 +3319,6 @@ func TestApplicationDataQueueLimited(t *testing.T) {
33193319
if qlen > maxAppDataPacketQueueSize {
33203320
t.Error("too many encrypted packets enqueued", len(dconn.encryptedPackets))
33213321
}
3322-
t.Log(qlen)
33233322
time.Sleep(1 * time.Second)
33243323
}
33253324
}()

e2e/e2e_lossy_test.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,11 @@ func TestPionE2ELossy(t *testing.T) {
135135

136136
go func() {
137137
cfg := &dtls.Config{
138-
FlightInterval: flightInterval,
139-
CipherSuites: test.CipherSuites,
140-
InsecureSkipVerify: true,
141-
MTU: test.MTU,
138+
FlightInterval: flightInterval,
139+
CipherSuites: test.CipherSuites,
140+
InsecureSkipVerify: true,
141+
MTU: test.MTU,
142+
DisableRetransmitBackoff: true,
142143
}
143144

144145
if test.DoClientAuth {
@@ -151,9 +152,10 @@ func TestPionE2ELossy(t *testing.T) {
151152

152153
go func() {
153154
cfg := &dtls.Config{
154-
Certificates: []tls.Certificate{serverCert},
155-
FlightInterval: flightInterval,
156-
MTU: test.MTU,
155+
Certificates: []tls.Certificate{serverCert},
156+
FlightInterval: flightInterval,
157+
MTU: test.MTU,
158+
DisableRetransmitBackoff: true,
157159
}
158160

159161
if test.DoClientAuth {

handshaker.go

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,14 @@ func (s handshakeState) String() string {
8282
}
8383

8484
type handshakeFSM struct {
85-
currentFlight flightVal
86-
flights []*packet
87-
retransmit bool
88-
state *State
89-
cache *handshakeCache
90-
cfg *handshakeConfig
91-
closed chan struct{}
85+
currentFlight flightVal
86+
flights []*packet
87+
retransmit bool
88+
retransmitInterval time.Duration
89+
state *State
90+
cache *handshakeCache
91+
cfg *handshakeConfig
92+
closed chan struct{}
9293
}
9394

9495
type handshakeConfig struct {
@@ -109,7 +110,8 @@ type handshakeConfig struct {
109110
sessionStore SessionStore
110111
rootCAs *x509.CertPool
111112
clientCAs *x509.CertPool
112-
retransmitInterval time.Duration
113+
initialRetransmitInterval time.Duration
114+
disableRetransmitBackoff bool
113115
customCipherSuites func() []CipherSuite
114116
ellipticCurves []elliptic.Curve
115117
insecureSkipHelloVerify bool
@@ -165,11 +167,12 @@ func newHandshakeFSM(
165167
initialFlight flightVal,
166168
) *handshakeFSM {
167169
return &handshakeFSM{
168-
currentFlight: initialFlight,
169-
state: s,
170-
cache: cache,
171-
cfg: cfg,
172-
closed: make(chan struct{}),
170+
currentFlight: initialFlight,
171+
state: s,
172+
cache: cache,
173+
cfg: cfg,
174+
retransmitInterval: cfg.initialRetransmitInterval,
175+
closed: make(chan struct{}),
173176
}
174177
}
175178

@@ -274,11 +277,12 @@ func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState,
274277
return handshakeErrored, errFlight
275278
}
276279

277-
retransmitTimer := time.NewTimer(s.cfg.retransmitInterval)
280+
retransmitTimer := time.NewTimer(s.retransmitInterval)
278281
for {
279282
select {
280283
case done := <-c.recvHandshake():
281284
nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg)
285+
s.retransmitInterval = s.cfg.initialRetransmitInterval
282286
close(done)
283287
if alert != nil {
284288
if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
@@ -304,8 +308,19 @@ func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState,
304308
if !s.retransmit {
305309
return handshakeWaiting, nil
306310
}
311+
312+
// RFC 4347 4.2.4.1:
313+
// Implementations SHOULD use an initial timer value of 1 second (the minimum defined in RFC 2988 [RFC2988])
314+
// and double the value at each retransmission, up to no less than the RFC 2988 maximum of 60 seconds.
315+
if !s.cfg.disableRetransmitBackoff {
316+
s.retransmitInterval *= 2
317+
}
318+
if s.retransmitInterval > time.Second*60 {
319+
s.retransmitInterval = time.Second * 60
320+
}
307321
return handshakeSending, nil
308322
case <-ctx.Done():
323+
s.retransmitInterval = s.cfg.initialRetransmitInterval
309324
return handshakeErrored, ctx.Err()
310325
}
311326
}
@@ -320,11 +335,12 @@ func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState
320335
return handshakeErrored, errFlight
321336
}
322337

323-
retransmitTimer := time.NewTimer(s.cfg.retransmitInterval)
338+
retransmitTimer := time.NewTimer(s.retransmitInterval)
324339
select {
325340
case done := <-c.recvHandshake():
326341
nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg)
327342
close(done)
343+
s.retransmitInterval = s.cfg.initialRetransmitInterval
328344
if alert != nil {
329345
if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
330346
if err != nil {
@@ -342,10 +358,16 @@ func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState
342358
return handshakeFinished, nil
343359
}
344360
<-retransmitTimer.C
361+
// RFC 4347 4.2.4.1
362+
s.retransmitInterval *= 2
363+
if s.retransmitInterval > time.Second*60 {
364+
s.retransmitInterval = time.Second * 60
365+
}
345366
// Retransmit last flight
346367
return handshakeSending, nil
347368

348369
case <-ctx.Done():
370+
s.retransmitInterval = s.cfg.initialRetransmitInterval
349371
return handshakeErrored, ctx.Err()
350372
}
351373
return handshakeFinished, nil

handshaker_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,10 @@ func TestHandshaker(t *testing.T) {
216216
}
217217

218218
report := func(t *testing.T) {
219-
// with one second server delay and 100 ms retransmit, there should be close to 10 `Finished` from client
220-
// using a range of 9 - 11 for checking
221-
if cntClientFinished < 8 || cntClientFinished > 11 {
222-
t.Errorf("Number of client finished is wrong, expected: %d - %d times, got: %d times", 9, 11, cntClientFinished)
219+
// with one second server delay and 100 ms retransmit (+ exponential backoff), there should be close to 4 `Finished` from client
220+
// using a range of 3 - 5 for checking
221+
if cntClientFinished < 3 || cntClientFinished > 5 {
222+
t.Errorf("Number of client finished is wrong, expected: %d - %d times, got: %d times", 3, 5, cntClientFinished)
223223
}
224224
if !isClientFinished {
225225
t.Errorf("Client is not finished")
@@ -281,7 +281,7 @@ func TestHandshaker(t *testing.T) {
281281
})
282282
}
283283
},
284-
retransmitInterval: nonZeroRetransmitInterval,
284+
initialRetransmitInterval: nonZeroRetransmitInterval,
285285
}
286286

287287
fsm := newHandshakeFSM(&ca.state, ca.handshakeCache, cfg, flight1)
@@ -314,7 +314,7 @@ func TestHandshaker(t *testing.T) {
314314
})
315315
}
316316
},
317-
retransmitInterval: nonZeroRetransmitInterval,
317+
initialRetransmitInterval: nonZeroRetransmitInterval,
318318
}
319319

320320
fsm := newHandshakeFSM(&cb.state, cb.handshakeCache, cfg, flight0)

0 commit comments

Comments
 (0)