summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2016-10-19 15:46:28 +0900
committerJason A. Donenfeld <Jason@zx2c4.com>2016-10-19 17:22:13 +0900
commit09847c27d4b9af8ce296215ed270ebfb4d08b23c (patch)
tree3465992e6488c7b685a166e08387ddebeb53604d
parenttimers: always delay handshakes for responder (diff)
downloadwireguard-monolithic-historical-09847c27d4b9af8ce296215ed270ebfb4d08b23c.tar.xz
wireguard-monolithic-historical-09847c27d4b9af8ce296215ed270ebfb4d08b23c.zip
timers: only have initiator rekey
If it's time to rekey, and the responder sends a message, the initator will begin the rekeying when sending his response message. In the worst case, this response message will actually just be the keepalive. This generally works well, with the one edge case of the message arriving less than 10 seconds before key expiration, in which the keepalive is not sufficient. In this case, we simply rehandshake immediately.
-rw-r--r--src/packets.h1
-rw-r--r--src/peer.h3
-rw-r--r--src/receive.c29
-rw-r--r--src/send.c42
-rw-r--r--src/timers.c33
-rw-r--r--src/timers.h2
6 files changed, 44 insertions, 66 deletions
diff --git a/src/packets.h b/src/packets.h
index f2ce2b7..035c548 100644
--- a/src/packets.h
+++ b/src/packets.h
@@ -31,6 +31,7 @@ void packet_send_keepalive(struct wireguard_peer *peer);
void packet_send_handshake_initiation(struct wireguard_peer *peer);
void packet_send_handshake_response(struct wireguard_peer *peer);
void packet_send_handshake_cookie(struct wireguard_device *wg, struct sk_buff *initiating_skb, void *data, size_t data_len, __le32 sender_index);
+void packet_send_handshake_initiation_ratelimited(struct wireguard_peer *peer);
void packet_queue_send_handshake_initiation(struct wireguard_peer *peer);
void packet_process_queued_handshake_packets(struct work_struct *work);
diff --git a/src/peer.h b/src/peer.h
index 6eb6224..306f9b3 100644
--- a/src/peer.h
+++ b/src/peer.h
@@ -29,11 +29,12 @@ struct wireguard_peer {
struct cookie latest_cookie;
struct hlist_node pubkey_hash;
uint64_t rx_bytes, tx_bytes;
- struct timer_list timer_retransmit_handshake, timer_delay_handshake, timer_send_keepalive, timer_new_handshake, timer_kill_ephemerals, timer_persistent_keepalive;
+ struct timer_list timer_retransmit_handshake, timer_send_keepalive, timer_new_handshake, timer_kill_ephemerals, timer_persistent_keepalive;
unsigned int timer_handshake_attempts;
unsigned long persistent_keepalive_interval;
bool timer_need_another_keepalive;
bool need_resend_queue;
+ bool sent_lastminute_handshake;
struct timeval walltime_last_handshake;
struct sk_buff_head tx_packet_queue;
struct kref refcount;
diff --git a/src/receive.c b/src/receive.c
index 58a0086..6dda410 100644
--- a/src/receive.c
+++ b/src/receive.c
@@ -123,7 +123,6 @@ static void receive_handshake_packet(struct wireguard_device *wg, void *data, si
return;
}
net_dbg_ratelimited("Receiving handshake initiation from peer %Lu (%pISpfsc)\n", peer->internal_id, &addr);
- timers_handshake_received(peer);
update_latest_addr(peer, skb);
packet_send_handshake_response(peer);
break;
@@ -139,10 +138,10 @@ static void receive_handshake_packet(struct wireguard_device *wg, void *data, si
return;
}
net_dbg_ratelimited("Receiving handshake response from peer %Lu (%pISpfsc)\n", peer->internal_id, &addr);
- timers_handshake_received(peer);
if (noise_handshake_begin_session(&peer->handshake, &peer->keypairs, true)) {
timers_ephemeral_key_created(peer);
timers_handshake_complete(peer);
+ peer->sent_lastminute_handshake = false;
packet_send_queue(peer);
}
break;
@@ -179,6 +178,26 @@ void packet_process_queued_handshake_packets(struct work_struct *work)
}
}
+static void keep_key_fresh(struct wireguard_peer *peer)
+{
+ struct noise_keypair *keypair;
+ bool send = false;
+ if (peer->sent_lastminute_handshake)
+ return;
+
+ rcu_read_lock();
+ keypair = rcu_dereference(peer->keypairs.current_keypair);
+ if (likely(keypair && keypair->sending.is_valid) && keypair->i_am_the_initiator &&
+ unlikely(time_is_before_eq_jiffies64(keypair->sending.birthdate + REJECT_AFTER_TIME - KEEPALIVE_TIMEOUT - REKEY_TIMEOUT)))
+ send = true;
+ rcu_read_unlock();
+
+ if (send) {
+ peer->sent_lastminute_handshake = true;
+ packet_send_handshake_initiation_ratelimited(peer);
+ }
+}
+
struct packet_cb {
u8 ds;
};
@@ -198,8 +217,12 @@ static void receive_data_packet(struct sk_buff *skb, struct wireguard_peer *peer
wg = peer->device;
dev = netdev_pub(wg);
- if (unlikely(used_new_key))
+ if (unlikely(used_new_key)) {
+ peer->sent_lastminute_handshake = false;
packet_send_queue(peer);
+ }
+
+ keep_key_fresh(peer);
/* A packet with length 0 is a keepalive packet */
if (unlikely(!skb->len)) {
diff --git a/src/send.c b/src/send.c
index 90d5d14..a7d505d 100644
--- a/src/send.c
+++ b/src/send.c
@@ -31,6 +31,12 @@ void packet_send_handshake_initiation(struct wireguard_peer *peer)
}
}
+void packet_send_handshake_initiation_ratelimited(struct wireguard_peer *peer)
+{
+ if (time_is_before_jiffies64(peer->last_sent_handshake + REKEY_TIMEOUT))
+ packet_queue_send_handshake_initiation(peer);
+}
+
void packet_send_handshake_response(struct wireguard_peer *peer)
{
struct message_handshake_response packet;
@@ -68,12 +74,6 @@ void packet_queue_send_handshake_initiation(struct wireguard_peer *peer)
peer_put(peer); /* If the work was already queued, we want to drop the extra reference */
}
-static inline void ratelimit_packet_send_handshake_initiation(struct wireguard_peer *peer)
-{
- if (time_is_before_jiffies64(peer->last_sent_handshake + REKEY_TIMEOUT))
- packet_queue_send_handshake_initiation(peer);
-}
-
void packet_send_handshake_cookie(struct wireguard_device *wg, struct sk_buff *initiating_skb, void *data, size_t data_len, __le32 sender_index)
{
struct message_handshake_cookie packet;
@@ -90,30 +90,18 @@ void packet_send_handshake_cookie(struct wireguard_device *wg, struct sk_buff *i
static inline void keep_key_fresh(struct wireguard_peer *peer)
{
struct noise_keypair *keypair;
+ bool send = false;
rcu_read_lock();
keypair = rcu_dereference(peer->keypairs.current_keypair);
- if (unlikely(!keypair || !keypair->sending.is_valid)) {
- rcu_read_unlock();
- return;
- }
+ if (likely(keypair && keypair->sending.is_valid) &&
+ (unlikely(atomic64_read(&keypair->sending.counter.counter) > REKEY_AFTER_MESSAGES) ||
+ (keypair->i_am_the_initiator && unlikely(time_is_before_eq_jiffies64(keypair->sending.birthdate + REKEY_AFTER_TIME)))))
+ send = true;
+ rcu_read_unlock();
- if (atomic64_read(&keypair->sending.counter.counter) > REKEY_AFTER_MESSAGES ||
- time_is_before_eq_jiffies64(keypair->sending.birthdate + REKEY_AFTER_TIME)) {
- rcu_read_unlock();
- /* The initiator can try it immediately, but the responder has to wait a bit,
- * to prevent the thundering herd effect. */
- if (keypair->i_am_the_initiator)
- ratelimit_packet_send_handshake_initiation(peer);
- else {
- /* If it's going to be dead soon, we rekey early. */
- if (time_is_before_eq_jiffies64(keypair->sending.birthdate + REJECT_AFTER_TIME - KEEPALIVE_TIMEOUT - REKEY_TIMEOUT))
- timers_delay_handshake(peer, REKEY_TIMEOUT / 2);
- else /* Otherwise rekey at the usual staggered delay. */
- timers_delay_handshake(peer, REKEY_TIMEOUT / 2 + REKEY_TIMEOUT * 2);
- }
- } else
- rcu_read_unlock();
+ if (send)
+ packet_send_handshake_initiation_ratelimited(peer);
}
void packet_send_keepalive(struct wireguard_peer *peer)
@@ -233,7 +221,7 @@ int packet_send_queue(struct wireguard_peer *peer)
case -ENOKEY:
/* ENOKEY means that we don't have a valid session for the peer, which
* means we should initiate a session, and then requeue everything. */
- ratelimit_packet_send_handshake_initiation(peer);
+ packet_send_handshake_initiation_ratelimited(peer);
goto requeue;
case -EBUSY:
/* EBUSY happens when the parallel workers are all filled up, in which
diff --git a/src/timers.c b/src/timers.c
index 7002e54..300becb 100644
--- a/src/timers.c
+++ b/src/timers.c
@@ -11,7 +11,6 @@
* 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. */
@@ -59,12 +58,6 @@ 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;
@@ -126,38 +119,20 @@ 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)
{
@@ -179,10 +154,6 @@ 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;
@@ -208,10 +179,6 @@ 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;
diff --git a/src/timers.h b/src/timers.h
index 349bdab..b6f80fd 100644
--- a/src/timers.h
+++ b/src/timers.h
@@ -13,9 +13,7 @@ void timers_data_sent(struct wireguard_peer *peer);
void timers_data_received(struct wireguard_peer *peer);
void timers_any_authenticated_packet_received(struct wireguard_peer *peer);
void timers_handshake_initiated(struct wireguard_peer *peer);
-void timers_handshake_received(struct wireguard_peer *peer);
void timers_handshake_complete(struct wireguard_peer *peer);
-void timers_delay_handshake(struct wireguard_peer *peer, unsigned int delay);
void timers_ephemeral_key_created(struct wireguard_peer *peer);
void timers_any_authenticated_packet_traversal(struct wireguard_peer *peer);