aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/timers.c
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2016-10-19 01:08:08 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2016-10-19 17:22:13 +0900
commit87427a7ec866c7f8fb829fa07340b3a9b2d7f66f (patch)
treefd6d181b9e4fd82b62ea45377b8af79e1bbbdc12 /src/timers.c
parenttimers: move constants to header (diff)
downloadwireguard-monolithic-historical-87427a7ec866c7f8fb829fa07340b3a9b2d7f66f.tar.xz
wireguard-monolithic-historical-87427a7ec866c7f8fb829fa07340b3a9b2d7f66f.zip
timers: always delay handshakes for responder
With the prior behavior, when sending a packet, we checked to see if it was about time to start a new handshake, and if we were past a certain time, we started it. For the responder, we made that time a bit further in the future than for the initiator, to prevent the thundering herd problem of them both starting at the same time. However, this was flawed. If both parties stopped communicating after 2.2 minutes, and then one party decided to initiate a TCP connection before the 3 minute mark, the currently open session would be used. However, because it was after the 2.2 minute mark, both peers would try to initiate a handshake upon sending their first packet. The errant flow was as follows: 1. Peer A sends SYN. 2. Peer A sees that his key is getting old and initiates new handshake. 3. Peer B receives SYN and sends ACK. 4. Peer B sees that his key is getting old and initiates new handshake. Since these events happened after the 2.2 minute mark, there's no delay between handshake initiations, and problems begin. The new behavior is changed to: 1. Peer A sends SYN. 2. Peer A sees that his key is getting old and initiates new handshake. 3. Peer B receives SYN and sends ACK. 4. Peer B sees that his key is getting old and schedules a delayed handshake for 12.5 seconds in the future. 5. Peer B receives handshake initiation and cancels scheduled handshake.
Diffstat (limited to 'src/timers.c')
-rw-r--r--src/timers.c33
1 files changed, 33 insertions, 0 deletions
diff --git a/src/timers.c b/src/timers.c
index 300becb..7002e54 100644
--- a/src/timers.c
+++ b/src/timers.c
@@ -11,6 +11,7 @@
* Timer for initiating new handshake if we have sent a packet but after have not received one (even empty) for `(KEEPALIVE_TIMEOUT + REKEY_TIMEOUT)` ms
* Timer for zeroing out all ephemeral keys after `(REJECT_AFTER_TIME * 3)` ms if no new keys have been received
* Timer for, if enabled, sending an empty authenticated packet every user-specified seconds
+ * Timer for starting a new handshake based on a delay
*/
/* This rounds the time down to the closest power of two of the closest quarter second. */
@@ -58,6 +59,12 @@ static void expired_new_handshake(unsigned long ptr)
packet_queue_send_handshake_initiation(peer);
}
+static void expired_delay_handshake(unsigned long ptr)
+{
+ struct wireguard_peer *peer = (struct wireguard_peer *)ptr;
+ packet_queue_send_handshake_initiation(peer);
+}
+
static void expired_kill_ephemerals(unsigned long ptr)
{
struct wireguard_peer *peer = (struct wireguard_peer *)ptr;
@@ -119,20 +126,38 @@ void timers_any_authenticated_packet_received(struct wireguard_peer *peer)
/* Should be called after a handshake initiation message is sent. */
void timers_handshake_initiated(struct wireguard_peer *peer)
{
+ if (likely(peer->timer_delay_handshake.data))
+ del_timer(&peer->timer_delay_handshake);
if (likely(peer->timer_send_keepalive.data))
del_timer(&peer->timer_send_keepalive);
if (likely(peer->timer_retransmit_handshake.data))
mod_timer(&peer->timer_retransmit_handshake, slack_time(jiffies + REKEY_TIMEOUT + HZ / 4));
}
+/* Should be called after a handshake message of any kind is received. */
+void timers_handshake_received(struct wireguard_peer *peer)
+{
+ if (likely(peer->timer_delay_handshake.data))
+ del_timer(&peer->timer_delay_handshake);
+}
+
/* Should be called after a handshake response message is received and processed. */
void timers_handshake_complete(struct wireguard_peer *peer)
{
+ if (likely(peer->timer_delay_handshake.data))
+ del_timer(&peer->timer_delay_handshake);
if (likely(peer->timer_retransmit_handshake.data))
del_timer(&peer->timer_retransmit_handshake);
peer->timer_handshake_attempts = 0;
}
+/* Should be called in order to initiate a handshake a little bit in the future. */
+void timers_delay_handshake(struct wireguard_peer *peer, unsigned int delay)
+{
+ if (likely(peer->timer_delay_handshake.data) && !timer_pending(&peer->timer_delay_handshake))
+ mod_timer(&peer->timer_delay_handshake, jiffies + delay);
+}
+
/* Should be called after an ephemeral key is created, which is before sending a handshake response or after receiving a handshake response. */
void timers_ephemeral_key_created(struct wireguard_peer *peer)
{
@@ -154,6 +179,10 @@ void timers_init_peer(struct wireguard_peer *peer)
peer->timer_retransmit_handshake.function = expired_retransmit_handshake;
peer->timer_retransmit_handshake.data = (unsigned long)peer;
+ init_timer(&peer->timer_delay_handshake);
+ peer->timer_delay_handshake.function = expired_delay_handshake;
+ peer->timer_delay_handshake.data = (unsigned long)peer;
+
init_timer(&peer->timer_send_keepalive);
peer->timer_send_keepalive.function = expired_send_keepalive;
peer->timer_send_keepalive.data = (unsigned long)peer;
@@ -179,6 +208,10 @@ void timers_uninit_peer(struct wireguard_peer *peer)
del_timer(&peer->timer_retransmit_handshake);
peer->timer_retransmit_handshake.data = 0;
}
+ if (peer->timer_delay_handshake.data) {
+ del_timer(&peer->timer_delay_handshake);
+ peer->timer_delay_handshake.data = 0;
+ }
if (peer->timer_send_keepalive.data) {
del_timer(&peer->timer_send_keepalive);
peer->timer_send_keepalive.data = 0;