From: Antonio Quartulli <antonio@openvpn.net>
To: netdev@vger.kernel.org
Cc: kuba@kernel.org, ryazanov.s.a@gmail.com, pabeni@redhat.com,
edumazet@google.com, andrew@lunn.ch, sd@queasysnail.net,
Antonio Quartulli <antonio@openvpn.net>
Subject: [PATCH net-next v4 17/25] ovpn: implement keepalive mechanism
Date: Mon, 24 Jun 2024 13:31:14 +0200 [thread overview]
Message-ID: <20240624113122.12732-18-antonio@openvpn.net> (raw)
In-Reply-To: <20240624113122.12732-1-antonio@openvpn.net>
OpenVPN supports configuring a periodic keepalive "ping"
message to allow the remote endpoint detect link failures.
This change implements the ping sending and timer expiring logic.
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
drivers/net/ovpn/io.c | 82 +++++++++++++++++++++++++++++++++++++++++
drivers/net/ovpn/io.h | 1 +
drivers/net/ovpn/peer.c | 72 ++++++++++++++++++++++++++++++++++++
drivers/net/ovpn/peer.h | 45 ++++++++++++++++++++++
4 files changed, 200 insertions(+)
diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
index 764b3df996bc..dedc4d4a1c72 100644
--- a/drivers/net/ovpn/io.c
+++ b/drivers/net/ovpn/io.c
@@ -25,6 +25,31 @@
#include "udp.h"
#include "skb.h"
+static const unsigned char ovpn_keepalive_message[] = {
+ 0x2a, 0x18, 0x7b, 0xf3, 0x64, 0x1e, 0xb4, 0xcb,
+ 0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48
+};
+
+/**
+ * ovpn_is_keepalive - check if skb contains a keepalive message
+ * @skb: packet to check
+ *
+ * Assumes that the first byte of skb->data is defined.
+ *
+ * Return: true if skb contains a keepalive or false otherwise
+ */
+static bool ovpn_is_keepalive(struct sk_buff *skb)
+{
+ if (*skb->data != OVPN_KEEPALIVE_FIRST_BYTE)
+ return false;
+
+ if (!pskb_may_pull(skb, sizeof(ovpn_keepalive_message)))
+ return false;
+
+ return !memcmp(skb->data, ovpn_keepalive_message,
+ sizeof(ovpn_keepalive_message));
+}
+
/* Called after decrypt to write the IP packet to the device.
* This method is expected to manage/free the skb.
*/
@@ -91,6 +116,9 @@ void ovpn_decrypt_post(struct sk_buff *skb, int ret)
goto drop;
}
+ /* note event of authenticated packet received for keepalive */
+ ovpn_peer_keepalive_recv_reset(peer);
+
/* point to encapsulated IP packet */
__skb_pull(skb, ovpn_skb_cb(skb)->payload_offset);
@@ -107,6 +135,12 @@ void ovpn_decrypt_post(struct sk_buff *skb, int ret)
goto drop;
}
+ if (ovpn_is_keepalive(skb)) {
+ netdev_dbg(peer->ovpn->dev,
+ "ping received from peer %u\n", peer->id);
+ goto drop;
+ }
+
net_info_ratelimited("%s: unsupported protocol received from peer %u\n",
peer->ovpn->dev->name, peer->id);
goto drop;
@@ -203,6 +237,7 @@ void ovpn_encrypt_post(struct sk_buff *skb, int ret)
/* no transport configured yet */
goto err;
}
+ ovpn_peer_keepalive_xmit_reset(peer);
/* skb passed down the stack - don't free it */
skb = NULL;
err:
@@ -345,3 +380,50 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
kfree_skb_list(skb);
return NET_XMIT_DROP;
}
+
+/**
+ * ovpn_xmit_special - encrypt and transmit an out-of-band message to peer
+ * @peer: peer to send the message to
+ * @data: message content
+ * @len: message length
+ *
+ * Assumes that caller holds a reference to peer
+ */
+static void ovpn_xmit_special(struct ovpn_peer *peer, const void *data,
+ const unsigned int len)
+{
+ struct ovpn_struct *ovpn;
+ struct sk_buff *skb;
+
+ ovpn = peer->ovpn;
+ if (unlikely(!ovpn))
+ return;
+
+ skb = alloc_skb(256 + len, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return;
+
+ skb_reserve(skb, 128);
+ skb->priority = TC_PRIO_BESTEFFORT;
+ memcpy(__skb_put(skb, len), data, len);
+
+ /* increase reference counter when passing peer to sending queue */
+ if (!ovpn_peer_hold(peer)) {
+ netdev_dbg(ovpn->dev, "%s: cannot hold peer reference for sending special packet\n",
+ __func__);
+ kfree_skb(skb);
+ return;
+ }
+
+ ovpn_send(ovpn, skb, peer);
+}
+
+/**
+ * ovpn_keepalive_xmit - send keepalive message to peer
+ * @peer: the peer to send the message to
+ */
+void ovpn_keepalive_xmit(struct ovpn_peer *peer)
+{
+ ovpn_xmit_special(peer, ovpn_keepalive_message,
+ sizeof(ovpn_keepalive_message));
+}
diff --git a/drivers/net/ovpn/io.h b/drivers/net/ovpn/io.h
index ad741552d742..63fcd576ddaf 100644
--- a/drivers/net/ovpn/io.h
+++ b/drivers/net/ovpn/io.h
@@ -13,6 +13,7 @@
netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev);
void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb);
+void ovpn_keepalive_xmit(struct ovpn_peer *peer);
void ovpn_encrypt_post(struct sk_buff *skb, int ret);
void ovpn_decrypt_post(struct sk_buff *skb, int ret);
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index 8c23268db916..289fe3f84ed4 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -22,6 +22,63 @@
#include "peer.h"
#include "socket.h"
+/**
+ * ovpn_peer_ping - timer task for sending periodic keepalive
+ * @t: timer object that triggered the task
+ */
+static void ovpn_peer_ping(struct timer_list *t)
+{
+ struct ovpn_peer *peer = from_timer(peer, t, keepalive_xmit);
+
+ netdev_dbg(peer->ovpn->dev, "%s: sending ping to peer %u\n", __func__,
+ peer->id);
+ ovpn_keepalive_xmit(peer);
+}
+
+/**
+ * ovpn_peer_expire - timer task for incoming keepialive timeout
+ * @t: the timer that triggered the task
+ */
+static void ovpn_peer_expire(struct timer_list *t)
+{
+ struct ovpn_peer *peer = from_timer(peer, t, keepalive_recv);
+
+ netdev_dbg(peer->ovpn->dev, "%s: peer %u expired\n", __func__,
+ peer->id);
+ ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_EXPIRED);
+}
+
+/**
+ * ovpn_peer_keepalive_set - configure keepalive values for peer
+ * @peer: the peer to configure
+ * @interval: outgoing keepalive interval
+ * @timeout: incoming keepalive timeout
+ */
+void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout)
+{
+ u32 delta;
+
+ netdev_dbg(peer->ovpn->dev,
+ "%s: scheduling keepalive for peer %u: interval=%u timeout=%u\n",
+ __func__, peer->id, interval, timeout);
+
+ peer->keepalive_interval = interval;
+ if (interval > 0) {
+ delta = msecs_to_jiffies(interval * MSEC_PER_SEC);
+ mod_timer(&peer->keepalive_xmit, jiffies + delta);
+ } else {
+ timer_delete(&peer->keepalive_xmit);
+ }
+
+ peer->keepalive_timeout = timeout;
+ if (timeout) {
+ delta = msecs_to_jiffies(timeout * MSEC_PER_SEC);
+ mod_timer(&peer->keepalive_recv, jiffies + delta);
+ } else {
+ timer_delete(&peer->keepalive_recv);
+ }
+}
+
/**
* ovpn_peer_new - allocate and initialize a new peer object
* @ovpn: the openvpn instance inside which the peer should be created
@@ -63,9 +120,22 @@ struct ovpn_peer *ovpn_peer_new(struct ovpn_struct *ovpn, u32 id)
netdev_hold(ovpn->dev, NULL, GFP_KERNEL);
+ timer_setup(&peer->keepalive_xmit, ovpn_peer_ping, 0);
+ timer_setup(&peer->keepalive_recv, ovpn_peer_expire, 0);
+
return peer;
}
+/**
+ * ovpn_peer_timer_delete_all - killall keepalive timers
+ * @peer: peer for which timers should be killed
+ */
+static void ovpn_peer_timer_delete_all(struct ovpn_peer *peer)
+{
+ timer_shutdown_sync(&peer->keepalive_xmit);
+ timer_shutdown_sync(&peer->keepalive_recv);
+}
+
/**
* ovpn_peer_release - release peer private members
* @peer: the peer to release
@@ -77,6 +147,8 @@ static void ovpn_peer_release(struct ovpn_peer *peer)
ovpn_crypto_state_release(&peer->crypto);
ovpn_bind_reset(peer, NULL);
+ ovpn_peer_timer_delete_all(peer);
+
dst_cache_destroy(&peer->dst_cache);
}
diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h
index 6e92e09a3504..fd7e9d57b38a 100644
--- a/drivers/net/ovpn/peer.h
+++ b/drivers/net/ovpn/peer.h
@@ -50,6 +50,10 @@
* @crypto: the crypto configuration (ciphers, keys, etc..)
* @dst_cache: cache for dst_entry used to send to peer
* @bind: remote peer binding
+ * @keepalive_xmit: timer used to send the next keepalive
+ * @keepalive_interval: seconds after which a new keepalive should be sent
+ * @keepalive_recv: timer used to check for received keepalives
+ * @keepalive_timeout: seconds after which an inactive peer is considered dead
* @halt: true if ovpn_peer_mark_delete was called
* @vpn_stats: per-peer in-VPN TX/RX stays
* @link_stats: per-peer link/transport TX/RX stats
@@ -98,6 +102,10 @@ struct ovpn_peer {
struct ovpn_crypto_state crypto;
struct dst_cache dst_cache;
struct ovpn_bind __rcu *bind;
+ struct timer_list keepalive_xmit;
+ unsigned long keepalive_interval;
+ struct timer_list keepalive_recv;
+ unsigned long keepalive_timeout;
bool halt;
struct ovpn_peer_stats vpn_stats;
struct ovpn_peer_stats link_stats;
@@ -144,4 +152,41 @@ struct ovpn_peer *ovpn_peer_get_by_dst(struct ovpn_struct *ovpn,
bool ovpn_peer_check_by_src(struct ovpn_struct *ovpn, struct sk_buff *skb,
struct ovpn_peer *peer);
+/**
+ * ovpn_peer_keepalive_recv_reset - reset keepalive timeout
+ * @peer: peer for which the timeout should be reset
+ *
+ * To be invoked upon reception of an authenticated packet from peer in order
+ * to report valid activity and thus reset the keepalive timeout
+ */
+static inline void ovpn_peer_keepalive_recv_reset(struct ovpn_peer *peer)
+{
+ u32 delta = msecs_to_jiffies(peer->keepalive_timeout * MSEC_PER_SEC);
+
+ if (unlikely(!delta))
+ return;
+
+ mod_timer(&peer->keepalive_recv, jiffies + delta);
+}
+
+/**
+ * ovpn_peer_keepalive_xmit_reset - reset keepalive sending timer
+ * @peer: peer for which the timer should be reset
+ *
+ * To be invoked upon sending of an authenticated packet to peer in order
+ * to report valid outgoing activity and thus reset the keepalive sending
+ * timer
+ */
+static inline void ovpn_peer_keepalive_xmit_reset(struct ovpn_peer *peer)
+{
+ u32 delta = msecs_to_jiffies(peer->keepalive_interval * MSEC_PER_SEC);
+
+ if (unlikely(!delta))
+ return;
+
+ mod_timer(&peer->keepalive_xmit, jiffies + delta);
+}
+
+void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout);
+
#endif /* _NET_OVPN_OVPNPEER_H_ */
--
2.44.2
next prev parent reply other threads:[~2024-06-24 11:30 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-24 11:30 [PATCH net-next v4 00/25] Introducing OpenVPN Data Channel Offload Antonio Quartulli
2024-06-24 11:30 ` [PATCH net-next v4 01/25] netlink: add NLA_POLICY_MAX_LEN macro Antonio Quartulli
2024-06-24 11:30 ` [PATCH net-next v4 02/25] rtnetlink: don't crash on unregister if no dellink exists Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 03/25] net: introduce OpenVPN Data Channel Offload (ovpn) Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 04/25] ovpn: add basic netlink support Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 05/25] ovpn: add basic interface creation/destruction/management routines Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 06/25] ovpn: implement interface creation/destruction via netlink Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 07/25] ovpn: keep carrier always on Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 08/25] ovpn: introduce the ovpn_peer object Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 09/25] ovpn: introduce the ovpn_socket object Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 10/25] ovpn: implement basic TX path (UDP) Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 11/25] ovpn: implement basic RX " Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 12/25] ovpn: implement packet processing Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 13/25] ovpn: store tunnel and transport statistics Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 14/25] ovpn: implement TCP transport Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 15/25] ovpn: implement multi-peer support Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 16/25] ovpn: implement peer lookup logic Antonio Quartulli
2024-06-24 11:31 ` Antonio Quartulli [this message]
2024-06-24 11:31 ` [PATCH net-next v4 18/25] ovpn: add support for updating local UDP endpoint Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 19/25] ovpn: add support for peer floating Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 20/25] ovpn: implement peer add/dump/delete via netlink Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 21/25] ovpn: implement key add/del/swap " Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 22/25] ovpn: kill key and notify userspace in case of IV exhaustion Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 23/25] ovpn: notify userspace when a peer is deleted Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 24/25] ovpn: add basic ethtool support Antonio Quartulli
2024-06-24 11:31 ` [PATCH net-next v4 25/25] testing/selftest: add test tool and scripts for ovpn module Antonio Quartulli
2024-06-25 15:14 ` Jakub Kicinski
2024-06-27 6:46 ` Antonio Quartulli
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240624113122.12732-18-antonio@openvpn.net \
--to=antonio@openvpn.net \
--cc=andrew@lunn.ch \
--cc=edumazet@google.com \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=ryazanov.s.a@gmail.com \
--cc=sd@queasysnail.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).