From d6c566c92ac3f21c75301a540da275c87ea4b745 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Fri, 8 Jul 2016 02:29:38 +0200 Subject: persistent keepalive: add kernel mechanism Signed-off-by: Jason A. Donenfeld --- src/config.c | 8 ++++++++ src/peer.h | 3 ++- src/receive.c | 2 +- src/socket.c | 8 ++++++-- src/timers.c | 23 +++++++++++++++++++++++ src/timers.h | 1 + src/uapi.h | 1 + 7 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/config.c b/src/config.c index 3df5d9e..767d31a 100644 --- a/src/config.c +++ b/src/config.c @@ -103,6 +103,13 @@ static int set_peer(struct wireguard_device *wg, void __user *user_peer, size_t break; } + if (in_peer.persistent_keepalive_interval != (uint16_t)-1) { + if (in_peer.persistent_keepalive_interval && (in_peer.persistent_keepalive_interval < 10 || in_peer.persistent_keepalive_interval > 3600)) + ret = -EINVAL; + else + peer->persistent_keepalive_interval = in_peer.persistent_keepalive_interval; + } + if (netdev_pub(wg)->flags & IFF_UP) packet_send_queue(peer); @@ -241,6 +248,7 @@ static int populate_peer(struct wireguard_peer *peer, void *ctx) out_peer.last_handshake_time = peer->walltime_last_handshake; out_peer.tx_bytes = peer->tx_bytes; out_peer.rx_bytes = peer->rx_bytes; + out_peer.persistent_keepalive_interval = peer->persistent_keepalive_interval; ipmasks_data.out_len = data->out_len; ipmasks_data.data = data->data; diff --git a/src/peer.h b/src/peer.h index 05ee7bd..6d74385 100644 --- a/src/peer.h +++ b/src/peer.h @@ -27,8 +27,9 @@ 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_send_keepalive, timer_new_handshake, timer_kill_ephemerals; + struct timer_list timer_retransmit_handshake, timer_send_keepalive, timer_new_handshake, timer_kill_ephemerals, timer_persistent_keepalive; unsigned int timer_handshake_attempts; + uint16_t persistent_keepalive_interval; bool timer_need_another_keepalive; struct timeval walltime_last_handshake; struct sk_buff_head tx_packet_queue; diff --git a/src/receive.c b/src/receive.c index 1b86489..db19527 100644 --- a/src/receive.c +++ b/src/receive.c @@ -273,7 +273,7 @@ void packet_receive(struct wireguard_device *wg, struct sk_buff *skb) static const u8 addr; #endif - if (skb_data_offset(skb, &offset, &len) < 0) + if (unlikely(skb_data_offset(skb, &offset, &len) < 0 || !len)) goto err; switch (message_determine_type(skb->data + offset, len)) { case MESSAGE_HANDSHAKE_INITIATION: diff --git a/src/socket.c b/src/socket.c index ac19a47..5b7bbf8 100644 --- a/src/socket.c +++ b/src/socket.c @@ -4,6 +4,7 @@ #include "socket.h" #include "packets.h" #include "messages.h" +#include "timers.h" #include #include @@ -250,8 +251,10 @@ int socket_send_skb_to_peer(struct wireguard_peer *peer, struct sk_buff *skb, u8 read_lock_bh(&peer->endpoint_lock); ret = send(dev, skb, dst, &peer->endpoint_flow.fl4, &peer->endpoint_flow.fl6, &peer->endpoint_addr, rcu_dereference(peer->device->sock4), rcu_dereference(peer->device->sock6), dscp); - if (!ret) + if (!ret) { + timers_any_packet_sent(peer); peer->tx_bytes += skb_len; + } read_unlock_bh(&peer->endpoint_lock); rcu_read_unlock(); @@ -265,7 +268,8 @@ int socket_send_buffer_to_peer(struct wireguard_peer *peer, void *buffer, size_t if (!skb) return -ENOMEM; skb_reserve(skb, SKB_HEADER_LEN); - memcpy(skb_put(skb, len), buffer, len); + if (likely(buffer)) + memcpy(skb_put(skb, len), buffer, len); return socket_send_skb_to_peer(peer, skb, dscp); } diff --git a/src/timers.c b/src/timers.c index 47d7854..01933a8 100644 --- a/src/timers.c +++ b/src/timers.c @@ -76,6 +76,15 @@ static void queued_expired_kill_ephemerals(struct work_struct *work) peer_put(peer); } +static void expired_send_persistent_keepalive(unsigned long ptr) +{ + struct wireguard_peer *peer = (struct wireguard_peer *)ptr; + + if (unlikely(!peer->persistent_keepalive_interval)) + return; + socket_send_buffer_to_peer(peer, NULL, 0, 0); +} + void timers_data_sent(struct wireguard_peer *peer) { if (likely(peer->timer_send_keepalive.data)) @@ -121,6 +130,12 @@ void timers_ephemeral_key_created(struct wireguard_peer *peer) do_gettimeofday(&peer->walltime_last_handshake); } +void timers_any_packet_sent(struct wireguard_peer *peer) +{ + if (peer->persistent_keepalive_interval && likely(peer->timer_persistent_keepalive.data)) + mod_timer(&peer->timer_persistent_keepalive, jiffies + HZ * peer->persistent_keepalive_interval); +} + void timers_init_peer(struct wireguard_peer *peer) { init_timer(&peer->timer_retransmit_handshake); @@ -139,6 +154,10 @@ void timers_init_peer(struct wireguard_peer *peer) peer->timer_kill_ephemerals.function = expired_kill_ephemerals; peer->timer_kill_ephemerals.data = (unsigned long)peer; + init_timer(&peer->timer_persistent_keepalive); + peer->timer_persistent_keepalive.function = expired_send_persistent_keepalive; + peer->timer_persistent_keepalive.data = (unsigned long)peer; + INIT_WORK(&peer->clear_peer_work, queued_expired_kill_ephemerals); } @@ -160,6 +179,10 @@ void timers_uninit_peer(struct wireguard_peer *peer) del_timer(&peer->timer_kill_ephemerals); peer->timer_kill_ephemerals.data = 0; } + if (peer->timer_persistent_keepalive.data) { + del_timer(&peer->timer_persistent_keepalive); + peer->timer_persistent_keepalive.data = 0; + } } void timers_uninit_peer_wait(struct wireguard_peer *peer) { diff --git a/src/timers.h b/src/timers.h index c5e9678..e8acba3 100644 --- a/src/timers.h +++ b/src/timers.h @@ -15,5 +15,6 @@ void timers_any_authorized_packet_received(struct wireguard_peer *peer); void timers_handshake_initiated(struct wireguard_peer *peer); void timers_handshake_complete(struct wireguard_peer *peer); void timers_ephemeral_key_created(struct wireguard_peer *peer); +void timers_any_packet_sent(struct wireguard_peer *peer); #endif diff --git a/src/uapi.h b/src/uapi.h index 21d4af1..6e3662f 100644 --- a/src/uapi.h +++ b/src/uapi.h @@ -98,6 +98,7 @@ struct wgpeer { __u32 replace_ipmasks : 1; /* Set */ __u16 num_ipmasks; /* Get/Set */ + __u16 persistent_keepalive_interval; /* Get/Set -- 0 = off, 0xffff = unset */ }; struct wgdevice { -- cgit v1.2.3