Commit efd72940 authored by Ralf Lici's avatar Ralf Lici Committed by Paolo Abeni
Browse files

ovpn: use datagram_poll_queue for socket readiness in TCP



openvpn TCP encapsulation uses a custom queue to deliver packets to
userspace. Currently it relies on datagram_poll, which checks
sk_receive_queue, leading to false readiness signals when that queue
contains non-userspace packets.

Switch ovpn_tcp_poll to use datagram_poll_queue with the peer's
user_queue, ensuring poll only signals readiness when userspace data is
actually available. Also refactor ovpn_tcp_poll in order to enforce the
assumption we can make on the lifetime of ovpn_sock and peer.

Fixes: 11851cbd ("ovpn: implement TCP transport")
Signed-off-by: default avatarAntonio Quartulli <antonio@openvpn.net>
Signed-off-by: default avatarRalf Lici <ralf@mandelbit.com>
Reviewed-by: default avatarSabrina Dubroca <sd@queasysnail.net>
Link: https://patch.msgid.link/20251021100942.195010-4-ralf@mandelbit.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 0fc3e32c
Loading
Loading
Loading
Loading
+22 −4
Original line number Diff line number Diff line
@@ -560,15 +560,33 @@ static void ovpn_tcp_close(struct sock *sk, long timeout)
static __poll_t ovpn_tcp_poll(struct file *file, struct socket *sock,
			      poll_table *wait)
{
	__poll_t mask = datagram_poll(file, sock, wait);
	struct sk_buff_head *queue = &sock->sk->sk_receive_queue;
	struct ovpn_socket *ovpn_sock;
	struct ovpn_peer *peer = NULL;
	__poll_t mask;

	rcu_read_lock();
	ovpn_sock = rcu_dereference_sk_user_data(sock->sk);
	if (ovpn_sock && ovpn_sock->peer &&
	    !skb_queue_empty(&ovpn_sock->peer->tcp.user_queue))
		mask |= EPOLLIN | EPOLLRDNORM;
	/* if we landed in this callback, we expect to have a
	 * meaningful state. The ovpn_socket lifecycle would
	 * prevent it otherwise.
	 */
	if (WARN(!ovpn_sock || !ovpn_sock->peer,
		 "ovpn: null state in ovpn_tcp_poll!")) {
		rcu_read_unlock();
		return 0;
	}

	if (ovpn_peer_hold(ovpn_sock->peer)) {
		peer = ovpn_sock->peer;
		queue = &peer->tcp.user_queue;
	}
	rcu_read_unlock();

	mask = datagram_poll_queue(file, sock, wait, queue);

	if (peer)
		ovpn_peer_put(peer);

	return mask;
}