diff options
| author | Ralf Lici <ralf@mandelbit.com> | 2025-10-21 12:09:42 +0200 |
|---|---|---|
| committer | Paolo Abeni <pabeni@redhat.com> | 2025-10-23 15:46:04 +0200 |
| commit | efd729408bc7d57e0c8d027b9ff514187fc1a05b (patch) | |
| tree | 304f33640c98a7fbb3f1b554049e52730816b619 | |
| parent | espintcp: use datagram_poll_queue for socket readiness (diff) | |
| download | linux-efd729408bc7d57e0c8d027b9ff514187fc1a05b.tar.gz linux-efd729408bc7d57e0c8d027b9ff514187fc1a05b.zip | |
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: 11851cbd60ea ("ovpn: implement TCP transport")
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Link: https://patch.msgid.link/20251021100942.195010-4-ralf@mandelbit.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
| -rw-r--r-- | drivers/net/ovpn/tcp.c | 26 |
1 files changed, 22 insertions, 4 deletions
diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 289f62c5d2c7..0d7f30360d87 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -560,16 +560,34 @@ 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; } |
