mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 23:52:08 +02:00
Included fixes:
* fix TCP selftest failures by reducing number of attempted pings * fix RCU ptr deref outside of RCU read section * fix UAF in case of TCP peer failed to be added to hashtable * fix race condition between iface teardown and new peer being added * ensure dstats are updated with BH disabled to avoid concurrency -----BEGIN PGP SIGNATURE----- iJEEABYIADkWIQQKU153ubb5unbkl6Gx/ZpNW1HNdwUCagZUrRsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDIACgkQsf2aTVtRzXcJ3gEAv0uFO2tjJha8KppS8Bpa 3asGcAcFP686hMYpm5MjrmIA+wcWETrqwtzf1YcfjKyDCVNGVwrHSyQl5dIGNwGD nQgN =WFT+ -----END PGP SIGNATURE----- Merge tag 'ovpn-net-20260514' of https://github.com/OpenVPN/ovpn-net-next Antonio Quartulli says: ==================== Included fixes: * fix TCP selftest failures by reducing number of attempted pings * fix RCU ptr deref outside of RCU read section * fix UAF in case of TCP peer failed to be added to hashtable * fix race condition between iface teardown and new peer being added * ensure dstats are updated with BH disabled to avoid concurrency * tag 'ovpn-net-20260514' of https://github.com/OpenVPN/ovpn-net-next: ovpn: disable BHs when updating device stats ovpn: fix race between deleting interface and adding new peer ovpn: respect peer refcount in CMD_NEW_PEER error path ovpn: tcp - use cached peer pointer in ovpn_tcp_close() selftests: ovpn: reduce remaining ping flood counts ==================== Link: https://patch.msgid.link/20260514231544.795993-1-antonio@openvpn.net Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
956aa1ec97
|
|
@ -201,7 +201,7 @@ void ovpn_decrypt_post(void *data, int ret)
|
|||
skb = NULL;
|
||||
drop:
|
||||
if (unlikely(skb))
|
||||
dev_dstats_rx_dropped(peer->ovpn->dev);
|
||||
ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
|
||||
kfree_skb(skb);
|
||||
drop_nocount:
|
||||
if (likely(peer))
|
||||
|
|
@ -225,7 +225,7 @@ void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb)
|
|||
net_info_ratelimited("%s: no available key for peer %u, key-id: %u\n",
|
||||
netdev_name(peer->ovpn->dev), peer->id,
|
||||
key_id);
|
||||
dev_dstats_rx_dropped(peer->ovpn->dev);
|
||||
ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
|
||||
kfree_skb(skb);
|
||||
ovpn_peer_put(peer);
|
||||
return;
|
||||
|
|
@ -301,7 +301,7 @@ void ovpn_encrypt_post(void *data, int ret)
|
|||
rcu_read_unlock();
|
||||
err:
|
||||
if (unlikely(skb))
|
||||
dev_dstats_tx_dropped(peer->ovpn->dev);
|
||||
ovpn_dev_dstats_tx_dropped(peer->ovpn->dev);
|
||||
if (likely(peer))
|
||||
ovpn_peer_put(peer);
|
||||
if (likely(ks))
|
||||
|
|
@ -343,7 +343,7 @@ static void ovpn_send(struct ovpn_priv *ovpn, struct sk_buff *skb,
|
|||
*/
|
||||
skb_list_walk_safe(skb, curr, next) {
|
||||
if (unlikely(!ovpn_encrypt_one(peer, curr))) {
|
||||
dev_dstats_tx_dropped(ovpn->dev);
|
||||
ovpn_dev_dstats_tx_dropped(ovpn->dev);
|
||||
kfree_skb(curr);
|
||||
}
|
||||
}
|
||||
|
|
@ -414,7 +414,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
if (unlikely(!curr)) {
|
||||
net_err_ratelimited("%s: skb_share_check failed for payload packet\n",
|
||||
netdev_name(dev));
|
||||
dev_dstats_tx_dropped(ovpn->dev);
|
||||
ovpn_dev_dstats_tx_dropped(ovpn->dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -440,7 +440,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
drop:
|
||||
ovpn_peer_put(peer);
|
||||
drop_no_peer:
|
||||
dev_dstats_tx_dropped(ovpn->dev);
|
||||
ovpn_dev_dstats_tx_dropped(ovpn->dev);
|
||||
skb_tx_error(skb);
|
||||
kfree_skb_list(skb);
|
||||
return NETDEV_TX_OK;
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ static void ovpn_net_uninit(struct net_device *dev)
|
|||
{
|
||||
struct ovpn_priv *ovpn = netdev_priv(dev);
|
||||
|
||||
disable_delayed_work_sync(&ovpn->keepalive_work);
|
||||
ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN);
|
||||
gro_cells_destroy(&ovpn->gro_cells);
|
||||
}
|
||||
|
||||
|
|
@ -208,15 +210,6 @@ static int ovpn_newlink(struct net_device *dev,
|
|||
return register_netdevice(dev);
|
||||
}
|
||||
|
||||
static void ovpn_dellink(struct net_device *dev, struct list_head *head)
|
||||
{
|
||||
struct ovpn_priv *ovpn = netdev_priv(dev);
|
||||
|
||||
cancel_delayed_work_sync(&ovpn->keepalive_work);
|
||||
ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN);
|
||||
unregister_netdevice_queue(dev, head);
|
||||
}
|
||||
|
||||
static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||||
{
|
||||
struct ovpn_priv *ovpn = netdev_priv(dev);
|
||||
|
|
@ -235,7 +228,6 @@ static struct rtnl_link_ops ovpn_link_ops = {
|
|||
.policy = ovpn_policy,
|
||||
.maxtype = IFLA_OVPN_MAX,
|
||||
.newlink = ovpn_newlink,
|
||||
.dellink = ovpn_dellink,
|
||||
.fill_info = ovpn_fill_info,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -462,10 +462,12 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
sock_release:
|
||||
ovpn_socket_release(peer);
|
||||
peer_release:
|
||||
/* release right away because peer was not yet hashed, thus it is not
|
||||
* used in any context
|
||||
/* For UDP, the peer is unreachable until added to the hashtables, so
|
||||
* dropping the initial reference is enough. For TCP, the peer may be
|
||||
* concurrently reachable via sk_user_data->peer until
|
||||
* ovpn_socket_release() detaches; rely on the refcount.
|
||||
*/
|
||||
ovpn_peer_release(peer);
|
||||
ovpn_peer_put(peer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ static void ovpn_peer_release_rcu(struct rcu_head *head)
|
|||
* ovpn_peer_release - release peer private members
|
||||
* @peer: the peer to release
|
||||
*/
|
||||
void ovpn_peer_release(struct ovpn_peer *peer)
|
||||
static void ovpn_peer_release(struct ovpn_peer *peer)
|
||||
{
|
||||
ovpn_crypto_state_release(&peer->crypto);
|
||||
spin_lock_bh(&peer->lock);
|
||||
|
|
@ -1034,14 +1034,29 @@ static int ovpn_peer_add_p2p(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
|
|||
*/
|
||||
int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
|
||||
/* Prevent adding new peers while destroying the ovpn interface.
|
||||
* Failing to do so would end up holding the device reference
|
||||
* endlessly hostage of the new peer object with no chance of
|
||||
* release..
|
||||
*/
|
||||
netdev_lock(ovpn->dev);
|
||||
if (ovpn->dev->reg_state != NETREG_REGISTERED)
|
||||
goto out;
|
||||
|
||||
switch (ovpn->mode) {
|
||||
case OVPN_MODE_MP:
|
||||
return ovpn_peer_add_mp(ovpn, peer);
|
||||
ret = ovpn_peer_add_mp(ovpn, peer);
|
||||
break;
|
||||
case OVPN_MODE_P2P:
|
||||
return ovpn_peer_add_p2p(ovpn, peer);
|
||||
ret = ovpn_peer_add_p2p(ovpn, peer);
|
||||
break;
|
||||
}
|
||||
out:
|
||||
netdev_unlock(ovpn->dev);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -127,7 +127,6 @@ static inline bool ovpn_peer_hold(struct ovpn_peer *peer)
|
|||
return kref_get_unless_zero(&peer->refcount);
|
||||
}
|
||||
|
||||
void ovpn_peer_release(struct ovpn_peer *peer);
|
||||
void ovpn_peer_release_kref(struct kref *kref);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
#ifndef _NET_OVPN_OVPNSTATS_H_
|
||||
#define _NET_OVPN_OVPNSTATS_H_
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
/* one stat */
|
||||
struct ovpn_peer_stat {
|
||||
atomic64_t bytes;
|
||||
|
|
@ -44,4 +46,18 @@ static inline void ovpn_peer_stats_increment_tx(struct ovpn_peer_stats *stats,
|
|||
ovpn_peer_stats_increment(&stats->tx, n);
|
||||
}
|
||||
|
||||
static inline void ovpn_dev_dstats_tx_dropped(struct net_device *dev)
|
||||
{
|
||||
local_bh_disable();
|
||||
dev_dstats_tx_dropped(dev);
|
||||
local_bh_enable();
|
||||
}
|
||||
|
||||
static inline void ovpn_dev_dstats_rx_dropped(struct net_device *dev)
|
||||
{
|
||||
local_bh_disable();
|
||||
dev_dstats_rx_dropped(dev);
|
||||
local_bh_enable();
|
||||
}
|
||||
|
||||
#endif /* _NET_OVPN_OVPNSTATS_H_ */
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb)
|
|||
if (WARN_ON(!ovpn_peer_hold(peer)))
|
||||
goto err_nopeer;
|
||||
schedule_work(&peer->tcp.defer_del_work);
|
||||
dev_dstats_rx_dropped(peer->ovpn->dev);
|
||||
ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
|
||||
err_nopeer:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
|
@ -298,9 +298,9 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk)
|
|||
} while (peer->tcp.out_msg.len > 0);
|
||||
|
||||
if (!peer->tcp.out_msg.len) {
|
||||
preempt_disable();
|
||||
local_bh_disable();
|
||||
dev_dstats_tx_add(peer->ovpn->dev, skb->len);
|
||||
preempt_enable();
|
||||
local_bh_enable();
|
||||
}
|
||||
|
||||
kfree_skb(peer->tcp.out_msg.skb);
|
||||
|
|
@ -331,7 +331,7 @@ static void ovpn_tcp_send_sock_skb(struct ovpn_peer *peer, struct sock *sk,
|
|||
ovpn_tcp_send_sock(peer, sk);
|
||||
|
||||
if (peer->tcp.out_msg.skb) {
|
||||
dev_dstats_tx_dropped(peer->ovpn->dev);
|
||||
ovpn_dev_dstats_tx_dropped(peer->ovpn->dev);
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
|
@ -353,7 +353,7 @@ void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk,
|
|||
if (sock_owned_by_user(sk)) {
|
||||
if (skb_queue_len(&peer->tcp.out_queue) >=
|
||||
READ_ONCE(net_hotdata.max_backlog)) {
|
||||
dev_dstats_tx_dropped(peer->ovpn->dev);
|
||||
ovpn_dev_dstats_tx_dropped(peer->ovpn->dev);
|
||||
kfree_skb(skb);
|
||||
goto unlock;
|
||||
}
|
||||
|
|
@ -581,14 +581,19 @@ static void ovpn_tcp_close(struct sock *sk, long timeout)
|
|||
|
||||
rcu_read_lock();
|
||||
sock = rcu_dereference_sk_user_data(sk);
|
||||
if (!sock || !sock->peer || !ovpn_peer_hold(sock->peer)) {
|
||||
if (!sock) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
peer = sock->peer;
|
||||
if (!peer || !ovpn_peer_hold(peer)) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
ovpn_peer_del(sock->peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT);
|
||||
ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT);
|
||||
peer->tcp.sk_cb.prot->close(sk, timeout);
|
||||
ovpn_peer_put(peer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
|
|||
return 0;
|
||||
|
||||
drop:
|
||||
dev_dstats_rx_dropped(ovpn->dev);
|
||||
ovpn_dev_dstats_rx_dropped(ovpn->dev);
|
||||
drop_noovpn:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ ovpn_run_ping_traffic() {
|
|||
|
||||
for p in $(seq 1 ${OVPN_NUM_PEERS}); do
|
||||
ovpn_cmd_ok "send ping traffic to peer ${p}" \
|
||||
ip netns exec ovpn_peer0 ping -qfc 500 -w 3 \
|
||||
ip netns exec ovpn_peer0 ping -qfc 100 -w 3 \
|
||||
5.5.5.$((p + 1))
|
||||
done
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ ovpn_mark_run_baseline_traffic() {
|
|||
|
||||
for p in $(seq 1 3); do
|
||||
ovpn_cmd_ok "send baseline traffic to peer ${p}" \
|
||||
ip netns exec ovpn_peer0 ping -qfc 500 -w 3 \
|
||||
ip netns exec ovpn_peer0 ping -qfc 100 -w 3 \
|
||||
5.5.5.$((p + 1))
|
||||
done
|
||||
}
|
||||
|
|
@ -101,7 +101,7 @@ ovpn_mark_verify_drop_traffic() {
|
|||
local total_count
|
||||
|
||||
for p in $(seq 1 3); do
|
||||
if ping_output=$(ip netns exec ovpn_peer0 ping -qfc 500 -w 1 \
|
||||
if ping_output=$(ip netns exec ovpn_peer0 ping -qfc 100 -w 1 \
|
||||
5.5.5.$((p + 1)) 2>&1); then
|
||||
printf '%s\n' "expected ping to peer ${p} to fail \
|
||||
after nft drop rule"
|
||||
|
|
@ -144,7 +144,7 @@ ovpn_mark_verify_traffic_recovery() {
|
|||
sleep 1
|
||||
for p in $(seq 1 3); do
|
||||
ovpn_cmd_ok "send recovery traffic to peer ${p}" \
|
||||
ip netns exec ovpn_peer0 ping -qfc 500 -w 3 \
|
||||
ip netns exec ovpn_peer0 ping -qfc 100 -w 3 \
|
||||
5.5.5.$((p + 1))
|
||||
done
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ ovpn_run_basic_traffic() {
|
|||
|
||||
ovpn_run_lan_traffic() {
|
||||
ovpn_cmd_ok "ping LAN behind peer1" \
|
||||
ip netns exec ovpn_peer0 ping -qfc 500 -w 3 "${OVPN_LAN_IP}"
|
||||
ip netns exec ovpn_peer0 ping -qfc 100 -w 3 "${OVPN_LAN_IP}"
|
||||
}
|
||||
|
||||
ovpn_run_float_mode() {
|
||||
|
|
@ -127,7 +127,7 @@ ovpn_run_float_mode() {
|
|||
for p in $(seq 1 ${OVPN_NUM_PEERS}); do
|
||||
peer_ns="ovpn_peer${p}"
|
||||
ovpn_cmd_ok "ping tunnel after float peer ${p}" \
|
||||
ip netns exec "${peer_ns}" ping -qfc 500 -w 3 5.5.5.1
|
||||
ip netns exec "${peer_ns}" ping -qfc 100 -w 3 5.5.5.1
|
||||
done
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user