timers.go raw
1 /* SPDX-License-Identifier: MIT
2 *
3 * Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
4 *
5 * This is based heavily on timers.c from the kernel implementation.
6 */
7
8 package device
9
10 import (
11 "sync"
12 "time"
13 _ "unsafe"
14 )
15
16 //go:linkname fastrandn runtime.fastrandn
17 func fastrandn(n uint32) uint32
18
19 // A Timer manages time-based aspects of the WireGuard protocol.
20 // Timer roughly copies the interface of the Linux kernel's struct timer_list.
21 type Timer struct {
22 *time.Timer
23 modifyingLock sync.RWMutex
24 runningLock sync.Mutex
25 isPending bool
26 }
27
28 func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer {
29 timer := &Timer{}
30 timer.Timer = time.AfterFunc(time.Hour, func() {
31 timer.runningLock.Lock()
32 defer timer.runningLock.Unlock()
33
34 timer.modifyingLock.Lock()
35 if !timer.isPending {
36 timer.modifyingLock.Unlock()
37 return
38 }
39 timer.isPending = false
40 timer.modifyingLock.Unlock()
41
42 expirationFunction(peer)
43 })
44 timer.Stop()
45 return timer
46 }
47
48 func (timer *Timer) Mod(d time.Duration) {
49 timer.modifyingLock.Lock()
50 timer.isPending = true
51 timer.Reset(d)
52 timer.modifyingLock.Unlock()
53 }
54
55 func (timer *Timer) Del() {
56 timer.modifyingLock.Lock()
57 timer.isPending = false
58 timer.Stop()
59 timer.modifyingLock.Unlock()
60 }
61
62 func (timer *Timer) DelSync() {
63 timer.Del()
64 timer.runningLock.Lock()
65 timer.Del()
66 timer.runningLock.Unlock()
67 }
68
69 func (timer *Timer) IsPending() bool {
70 timer.modifyingLock.RLock()
71 defer timer.modifyingLock.RUnlock()
72 return timer.isPending
73 }
74
75 func (peer *Peer) timersActive() bool {
76 return peer.isRunning.Load() && peer.device != nil && peer.device.isUp()
77 }
78
79 func expiredRetransmitHandshake(peer *Peer) {
80 if peer.timers.handshakeAttempts.Load() > MaxTimerHandshakes {
81 peer.device.log.Verbosef("%s - Handshake did not complete after %d attempts, giving up", peer, MaxTimerHandshakes+2)
82
83 if peer.timersActive() {
84 peer.timers.sendKeepalive.Del()
85 }
86
87 /* We drop all packets without a keypair and don't try again,
88 * if we try unsuccessfully for too long to make a handshake.
89 */
90 peer.FlushStagedPackets()
91
92 /* We set a timer for destroying any residue that might be left
93 * of a partial exchange.
94 */
95 if peer.timersActive() && !peer.timers.zeroKeyMaterial.IsPending() {
96 peer.timers.zeroKeyMaterial.Mod(RejectAfterTime * 3)
97 }
98 } else {
99 peer.timers.handshakeAttempts.Add(1)
100 peer.device.log.Verbosef("%s - Handshake did not complete after %d seconds, retrying (try %d)", peer, int(RekeyTimeout.Seconds()), peer.timers.handshakeAttempts.Load()+1)
101
102 /* We clear the endpoint address src address, in case this is the cause of trouble. */
103 peer.markEndpointSrcForClearing()
104
105 peer.SendHandshakeInitiation(true)
106 }
107 }
108
109 func expiredSendKeepalive(peer *Peer) {
110 peer.SendKeepalive()
111 if peer.timers.needAnotherKeepalive.Load() {
112 peer.timers.needAnotherKeepalive.Store(false)
113 if peer.timersActive() {
114 peer.timers.sendKeepalive.Mod(KeepaliveTimeout)
115 }
116 }
117 }
118
119 func expiredNewHandshake(peer *Peer) {
120 peer.device.log.Verbosef("%s - Retrying handshake because we stopped hearing back after %d seconds", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds()))
121 /* We clear the endpoint address src address, in case this is the cause of trouble. */
122 peer.markEndpointSrcForClearing()
123 peer.SendHandshakeInitiation(false)
124 }
125
126 func expiredZeroKeyMaterial(peer *Peer) {
127 peer.device.log.Verbosef("%s - Removing all keys, since we haven't received a new one in %d seconds", peer, int((RejectAfterTime * 3).Seconds()))
128 peer.ZeroAndFlushAll()
129 }
130
131 func expiredPersistentKeepalive(peer *Peer) {
132 if peer.persistentKeepaliveInterval.Load() > 0 {
133 peer.SendKeepalive()
134 }
135 }
136
137 /* Should be called after an authenticated data packet is sent. */
138 func (peer *Peer) timersDataSent() {
139 if peer.timersActive() && !peer.timers.newHandshake.IsPending() {
140 peer.timers.newHandshake.Mod(KeepaliveTimeout + RekeyTimeout + time.Millisecond*time.Duration(fastrandn(RekeyTimeoutJitterMaxMs)))
141 }
142 }
143
144 /* Should be called after an authenticated data packet is received. */
145 func (peer *Peer) timersDataReceived() {
146 if peer.timersActive() {
147 if !peer.timers.sendKeepalive.IsPending() {
148 peer.timers.sendKeepalive.Mod(KeepaliveTimeout)
149 } else {
150 peer.timers.needAnotherKeepalive.Store(true)
151 }
152 }
153 }
154
155 /* Should be called after any type of authenticated packet is sent -- keepalive, data, or handshake. */
156 func (peer *Peer) timersAnyAuthenticatedPacketSent() {
157 if peer.timersActive() {
158 peer.timers.sendKeepalive.Del()
159 }
160 }
161
162 /* Should be called after any type of authenticated packet is received -- keepalive, data, or handshake. */
163 func (peer *Peer) timersAnyAuthenticatedPacketReceived() {
164 if peer.timersActive() {
165 peer.timers.newHandshake.Del()
166 }
167 }
168
169 /* Should be called after a handshake initiation message is sent. */
170 func (peer *Peer) timersHandshakeInitiated() {
171 if peer.timersActive() {
172 peer.timers.retransmitHandshake.Mod(RekeyTimeout + time.Millisecond*time.Duration(fastrandn(RekeyTimeoutJitterMaxMs)))
173 }
174 }
175
176 /* Should be called after a handshake response message is received and processed or when getting key confirmation via the first data message. */
177 func (peer *Peer) timersHandshakeComplete() {
178 if peer.timersActive() {
179 peer.timers.retransmitHandshake.Del()
180 }
181 peer.timers.handshakeAttempts.Store(0)
182 peer.timers.sentLastMinuteHandshake.Store(false)
183 peer.lastHandshakeNano.Store(time.Now().UnixNano())
184 }
185
186 /* Should be called after an ephemeral key is created, which is before sending a handshake response or after receiving a handshake response. */
187 func (peer *Peer) timersSessionDerived() {
188 if peer.timersActive() {
189 peer.timers.zeroKeyMaterial.Mod(RejectAfterTime * 3)
190 }
191 }
192
193 /* Should be called before a packet with authentication -- keepalive, data, or handshake -- is sent, or after one is received. */
194 func (peer *Peer) timersAnyAuthenticatedPacketTraversal() {
195 keepalive := peer.persistentKeepaliveInterval.Load()
196 if keepalive > 0 && peer.timersActive() {
197 peer.timers.persistentKeepalive.Mod(time.Duration(keepalive) * time.Second)
198 }
199 }
200
201 func (peer *Peer) timersInit() {
202 peer.timers.retransmitHandshake = peer.NewTimer(expiredRetransmitHandshake)
203 peer.timers.sendKeepalive = peer.NewTimer(expiredSendKeepalive)
204 peer.timers.newHandshake = peer.NewTimer(expiredNewHandshake)
205 peer.timers.zeroKeyMaterial = peer.NewTimer(expiredZeroKeyMaterial)
206 peer.timers.persistentKeepalive = peer.NewTimer(expiredPersistentKeepalive)
207 }
208
209 func (peer *Peer) timersStart() {
210 peer.timers.handshakeAttempts.Store(0)
211 peer.timers.sentLastMinuteHandshake.Store(false)
212 peer.timers.needAnotherKeepalive.Store(false)
213 }
214
215 func (peer *Peer) timersStop() {
216 peer.timers.retransmitHandshake.DelSync()
217 peer.timers.sendKeepalive.DelSync()
218 peer.timers.newHandshake.DelSync()
219 peer.timers.zeroKeyMaterial.DelSync()
220 peer.timers.persistentKeepalive.DelSync()
221 }
222