Commit e74ff27f authored by Paolo Abeni's avatar Paolo Abeni
Browse files

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: default avatarPaolo Abeni <pabeni@redhat.com>
parents 22069735 b660b13d
Loading
Loading
Loading
Loading
+34 −21
Original line number Diff line number Diff line
@@ -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);
	}
	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;
	/* 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;
	}
	/* dst was needed for peer selection - it can now be dropped */
	skb_dst_drop(skb);
	skb_list.prev->next = NULL;

	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);
+21 −18
Original line number Diff line number Diff line
@@ -200,6 +200,22 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)
	ovpn_sock->sk = sk;
	kref_init(&ovpn_sock->refcount);

	/* TCP sockets are per-peer, therefore they are linked to their unique
	 * peer
	 */
	if (sk->sk_protocol == IPPROTO_TCP) {
		INIT_WORK(&ovpn_sock->tcp_tx_work, ovpn_tcp_tx_work);
		ovpn_sock->peer = peer;
		ovpn_peer_hold(peer);
	} else if (sk->sk_protocol == IPPROTO_UDP) {
		/* in UDP we only link the ovpn instance since the socket is
		 * shared among multiple peers
		 */
		ovpn_sock->ovpn = peer->ovpn;
		netdev_hold(peer->ovpn->dev, &ovpn_sock->dev_tracker,
			    GFP_KERNEL);
	}

	/* the newly created ovpn_socket is holding reference to sk,
	 * therefore we increase its refcounter.
	 *
@@ -212,29 +228,16 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer)

	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);
		goto sock_release;
	}

	/* TCP sockets are per-peer, therefore they are linked to their unique
	 * peer
	 */
	if (sk->sk_protocol == IPPROTO_TCP) {
		INIT_WORK(&ovpn_sock->tcp_tx_work, ovpn_tcp_tx_work);
		ovpn_sock->peer = peer;
		ovpn_peer_hold(peer);
	} else if (sk->sk_protocol == IPPROTO_UDP) {
		/* in UDP we only link the ovpn instance since the socket is
		 * shared among multiple peers
		 */
		ovpn_sock->ovpn = peer->ovpn;
		netdev_hold(peer->ovpn->dev, &ovpn_sock->dev_tracker,
			    GFP_KERNEL);
	}

	rcu_assign_sk_user_data(sk, ovpn_sock);
sock_release:
	release_sock(sk);
	return ovpn_sock;
+7 −2
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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,