mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
This batch includes the following fixes:
* set sk_user_data before installing callbacks, to avoid dropping early packets * fix use-after-free in ovpn_net_xmit when accessing shared skbs that got released * fix TX bytes stats by adding-up every positively processed GSO segment -----BEGIN PGP SIGNATURE----- iJEEABYIADkWIQQKU153ubb5unbkl6Gx/ZpNW1HNdwUCaY3n+RsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMSwyLDIACgkQsf2aTVtRzXflQAEAoU7xhL/j06/Ibbs0W34+ vUe6oJgbcY9ubQleW/7pjRMA/3Cj1cR0Ubo9vEPqjd4abPgz8dACLVRsZ7suRoHo /+UO =/iwu -----END PGP SIGNATURE----- Merge tag 'ovpn-net-20260212' of https://github.com/OpenVPN/ovpn-net-next Antonio Quartulli says: ==================== This batch includes the following fixes: * set sk_user_data before installing callbacks, to avoid dropping early packets * fix use-after-free in ovpn_net_xmit when accessing shared skbs that got released * fix TX bytes stats by adding-up every positively processed GSO segment ==================== Link: https://patch.msgid.link/20260212210340.11260-1-antonio@openvpn.net Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
e74ff27fc6
|
|
@ -355,6 +355,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
struct ovpn_priv *ovpn = netdev_priv(dev);
|
||||
struct sk_buff *segments, *curr, *next;
|
||||
struct sk_buff_head skb_list;
|
||||
unsigned int tx_bytes = 0;
|
||||
struct ovpn_peer *peer;
|
||||
__be16 proto;
|
||||
int ret;
|
||||
|
|
@ -365,7 +366,27 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
/* verify IP header size in network packet */
|
||||
proto = ovpn_ip_check_protocol(skb);
|
||||
if (unlikely(!proto || skb->protocol != proto))
|
||||
goto drop;
|
||||
goto drop_no_peer;
|
||||
|
||||
/* retrieve peer serving the destination IP of this packet */
|
||||
peer = ovpn_peer_get_by_dst(ovpn, skb);
|
||||
if (unlikely(!peer)) {
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IP):
|
||||
net_dbg_ratelimited("%s: no peer to send data to dst=%pI4\n",
|
||||
netdev_name(ovpn->dev),
|
||||
&ip_hdr(skb)->daddr);
|
||||
break;
|
||||
case htons(ETH_P_IPV6):
|
||||
net_dbg_ratelimited("%s: no peer to send data to dst=%pI6c\n",
|
||||
netdev_name(ovpn->dev),
|
||||
&ipv6_hdr(skb)->daddr);
|
||||
break;
|
||||
}
|
||||
goto drop_no_peer;
|
||||
}
|
||||
/* dst was needed for peer selection - it can now be dropped */
|
||||
skb_dst_drop(skb);
|
||||
|
||||
if (skb_is_gso(skb)) {
|
||||
segments = skb_gso_segment(skb, 0);
|
||||
|
|
@ -394,36 +415,28 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* only count what we actually send */
|
||||
tx_bytes += curr->len;
|
||||
__skb_queue_tail(&skb_list, curr);
|
||||
}
|
||||
|
||||
/* no segments survived: don't jump to 'drop' because we already
|
||||
* incremented the counter for each failure in the loop
|
||||
*/
|
||||
if (unlikely(skb_queue_empty(&skb_list))) {
|
||||
ovpn_peer_put(peer);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
skb_list.prev->next = NULL;
|
||||
|
||||
/* retrieve peer serving the destination IP of this packet */
|
||||
peer = ovpn_peer_get_by_dst(ovpn, skb);
|
||||
if (unlikely(!peer)) {
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IP):
|
||||
net_dbg_ratelimited("%s: no peer to send data to dst=%pI4\n",
|
||||
netdev_name(ovpn->dev),
|
||||
&ip_hdr(skb)->daddr);
|
||||
break;
|
||||
case htons(ETH_P_IPV6):
|
||||
net_dbg_ratelimited("%s: no peer to send data to dst=%pI6c\n",
|
||||
netdev_name(ovpn->dev),
|
||||
&ipv6_hdr(skb)->daddr);
|
||||
break;
|
||||
}
|
||||
goto drop;
|
||||
}
|
||||
/* dst was needed for peer selection - it can now be dropped */
|
||||
skb_dst_drop(skb);
|
||||
|
||||
ovpn_peer_stats_increment_tx(&peer->vpn_stats, skb->len);
|
||||
ovpn_peer_stats_increment_tx(&peer->vpn_stats, tx_bytes);
|
||||
ovpn_send(ovpn, skb_list.next, peer);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
drop:
|
||||
ovpn_peer_put(peer);
|
||||
drop_no_peer:
|
||||
dev_dstats_tx_dropped(ovpn->dev);
|
||||
skb_tx_error(skb);
|
||||
kfree_skb_list(skb);
|
||||
|
|
|
|||
|
|
@ -200,24 +200,6 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
|
|||
ovpn_sock->sk = sk;
|
||||
kref_init(&ovpn_sock->refcount);
|
||||
|
||||
/* the newly created ovpn_socket is holding reference to sk,
|
||||
* therefore we increase its refcounter.
|
||||
*
|
||||
* This ovpn_socket instance is referenced by all peers
|
||||
* using the same socket.
|
||||
*
|
||||
* ovpn_socket_release() will take care of dropping the reference.
|
||||
*/
|
||||
sock_hold(sk);
|
||||
|
||||
ret = ovpn_socket_attach(ovpn_sock, sock, peer);
|
||||
if (ret < 0) {
|
||||
sock_put(sk);
|
||||
kfree(ovpn_sock);
|
||||
ovpn_sock = ERR_PTR(ret);
|
||||
goto sock_release;
|
||||
}
|
||||
|
||||
/* TCP sockets are per-peer, therefore they are linked to their unique
|
||||
* peer
|
||||
*/
|
||||
|
|
@ -234,7 +216,28 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
|
|||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
rcu_assign_sk_user_data(sk, ovpn_sock);
|
||||
/* the newly created ovpn_socket is holding reference to sk,
|
||||
* therefore we increase its refcounter.
|
||||
*
|
||||
* This ovpn_socket instance is referenced by all peers
|
||||
* using the same socket.
|
||||
*
|
||||
* ovpn_socket_release() will take care of dropping the reference.
|
||||
*/
|
||||
sock_hold(sk);
|
||||
|
||||
ret = ovpn_socket_attach(ovpn_sock, sock, peer);
|
||||
if (ret < 0) {
|
||||
if (sk->sk_protocol == IPPROTO_TCP)
|
||||
ovpn_peer_put(peer);
|
||||
else if (sk->sk_protocol == IPPROTO_UDP)
|
||||
netdev_put(peer->ovpn->dev, &ovpn_sock->dev_tracker);
|
||||
|
||||
sock_put(sk);
|
||||
kfree(ovpn_sock);
|
||||
ovpn_sock = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
sock_release:
|
||||
release_sock(sk);
|
||||
return ovpn_sock;
|
||||
|
|
|
|||
|
|
@ -487,6 +487,7 @@ int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock,
|
|||
/* make sure no pre-existing encapsulation handler exists */
|
||||
if (ovpn_sock->sk->sk_user_data)
|
||||
return -EBUSY;
|
||||
rcu_assign_sk_user_data(ovpn_sock->sk, ovpn_sock);
|
||||
|
||||
/* only a fully connected socket is expected. Connection should be
|
||||
* handled in userspace
|
||||
|
|
@ -495,13 +496,14 @@ int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock,
|
|||
net_err_ratelimited("%s: provided TCP socket is not in ESTABLISHED state: %d\n",
|
||||
netdev_name(peer->ovpn->dev),
|
||||
ovpn_sock->sk->sk_state);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = strp_init(&peer->tcp.strp, ovpn_sock->sk, &cb);
|
||||
if (ret < 0) {
|
||||
DEBUG_NET_WARN_ON_ONCE(1);
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
|
||||
INIT_WORK(&peer->tcp.defer_del_work, ovpn_tcp_peer_del_work);
|
||||
|
|
@ -536,6 +538,9 @@ int ovpn_tcp_socket_attach(struct ovpn_socket *ovpn_sock,
|
|||
strp_check_rcv(&peer->tcp.strp);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
rcu_assign_sk_user_data(ovpn_sock->sk, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ovpn_tcp_close(struct sock *sk, long timeout)
|
||||
|
|
|
|||
|
|
@ -386,6 +386,7 @@ int ovpn_udp_socket_attach(struct ovpn_socket *ovpn_sock, struct socket *sock,
|
|||
struct ovpn_priv *ovpn)
|
||||
{
|
||||
struct udp_tunnel_sock_cfg cfg = {
|
||||
.sk_user_data = ovpn_sock,
|
||||
.encap_type = UDP_ENCAP_OVPNINUDP,
|
||||
.encap_rcv = ovpn_udp_encap_recv,
|
||||
.encap_destroy = ovpn_udp_encap_destroy,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user