From patchwork Tue Jul 28 11:48:49 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 5141 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:fd0b:b0:87d:ab56:3700 with SMTP id cw11csp485501mac; Tue, 28 Jul 2026 04:49:17 -0700 (PDT) X-Forwarded-Encrypted: i=2; AHgh+RoGOGxeM0yncJHVjE84p7ABQRBSEUBpah9Sl+rpfpS22x7e/jclWS+/L/8qZMyAP1C3NBeCbY3NZo0=@openvpn.net X-Received: by 2002:a05:6870:718f:b0:455:ac39:9909 with SMTP id 586e51a60fabf-4586cb4a436mr1192246fac.37.1785239357518; Tue, 28 Jul 2026 04:49:17 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1785239357; cv=none; d=google.com; s=arc-20260327; b=aYg+yPC9pnE6wGzxcC0QYrq3H59Urjf+21ZPCltWf30jIYRfDR0zSgp968NMGjfwFT vUxvOGzd1+VpnP/j9g2Rj3vwsgAyjqC/J3MH5xgR/KGaI0uz1rhPMju0W/4A1+gWi7Qy k+oLeBDuojB4P0TXzSW3Yk5tT4UnV2LWffuI5tE9S48L3qofVXGdfunjJaPKGXENa1M2 nHoDKTDg2f4EqyvWhQgRS4mHrewOlUdj46qjVAiuOc9fOfEq4vAN5/uDRsNlSnQ9ZvmT R0IreTmtFjAAu5EZDsPnCII0qfGAmAej5aNcyqAioK5VQAHvaTpN1Ptw21MAapP5UPSQ s5cw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=errors-to:content-transfer-encoding:cc:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:in-reply-to:message-id:date:to:from :dkim-signature:dkim-signature:dkim-signature:dkim-signature; bh=fU92+gKt4yApHpIwY37R1b5J7gHtzpyeXo1Ak2efZCE=; fh=BsMg/B0Yb/hS/rzP5Npz4luh0IleZm8REk1XWiWRt2A=; b=RduaCgLDkNsTAmK+UQhplOw0O/SdB14v/DReP2YeZb0PjRFgCH6DhZBEdEkUiX8QDA 2vYilc8zp5kg8iCwwm7e6IytVbE4YhZ6lwiWPV7OoKSZWtXEsNrou7VUWPg/58tOovp4 6mPNd06s+1waCqNV/pSaC1qj4pBE9DDHDpeBCuDgx6PdHf2fz/RV3IuqXyw4+rorvOQS lwujYg1afE1jfeQwOsR9EKm89qWKbfWCKFe+p1vYSLSyHDTJHhVOQl9k8uF4WxwKl7LD CkEVeyiFDZgXrUGI32NrD0+tVjbOSWxHukQGzRenR9XVB6v0dqcy/ir+X8P0My+hw1Ur Nhtw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=XbwLAUfs; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b="B/t2AhNx"; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b="Cu/gypxE"; dkim=neutral (body hash did not verify) header.i=@unstable.cc header.s=MBO0001 header.b=qLu2lLQq; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 586e51a60fabf-457aaa0bb80si14550370fac.368.2026.07.28.04.49.17 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 28 Jul 2026 04:49:17 -0700 (PDT) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=XbwLAUfs; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b="B/t2AhNx"; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b="Cu/gypxE"; dkim=neutral (body hash did not verify) header.i=@unstable.cc header.s=MBO0001 header.b=qLu2lLQq; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.sourceforge.net; s=beta; h=Content-Transfer-Encoding:Content-Type:Cc: List-Subscribe:List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id: Subject:MIME-Version:References:In-Reply-To:Message-ID:Date:To:From:Sender: Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender :Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=fU92+gKt4yApHpIwY37R1b5J7gHtzpyeXo1Ak2efZCE=; b=XbwLAUfs7QwdKFYNF29gGflsEF NlhD0tbDXu+gmkhuow14N9MVejLv4q/8gkPsoFWZx6b+Hf1hkNGdNqYcEzX3cwKTTIjEvkQLI/ExO AZWcg24X/euyFJDrsmGQHWGXWzUYtEhgKxdjEyO937rs9QoncNd0g+PdROAfJujqpDFY=; Received: from [127.0.0.1] (helo=sfs-ml-2.v29.lw.sourceforge.com) by sfs-ml-2.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1wogJ7-0003Fx-2k; Tue, 28 Jul 2026 11:49:13 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1wogJ5-0003Fk-S2 for openvpn-devel@lists.sourceforge.net; Tue, 28 Jul 2026 11:49:12 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=Q5s+IliVCAtSxSuO/VpXlFTI87izFQhZPPXqXBnI1qQ=; b=B/t2AhNx4AwZhdD64FAKs0RSYu HUXNmRLvtte4lsImbNwIcRuSuCFgfh3poDYawnDeRKT9FKG/TkqVHO9/A9DqvCH2baEgrkloWWtKe YTe7Jq0JYSxq0f3k1Hs/bb33g9HMhKZlq0MqH11kJugoS0td70eABk3/Auk7BOWrsA44=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID: Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=Q5s+IliVCAtSxSuO/VpXlFTI87izFQhZPPXqXBnI1qQ=; b=Cu/gypxEWnLXy2BJFRIEjrrhf9 xVLMSPnle6vwpF+72upjLT9LCZhG0+zlBxELUOAGp5DyDT2IxzR3deNrGtONTcouYNp7OrozRQdl/ Ox4AQLjzlD1eX/k8m1r2BLmlq5Pqp8X83vqg8isfwTAB7g6R+d6MAw3wOqWXH76C2goA=; Received: from mout-p-101.mailbox.org ([80.241.56.151]) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1wogJ5-0002um-0H for openvpn-devel@lists.sourceforge.net; Tue, 28 Jul 2026 11:49:12 +0000 Received: from smtp1.mailbox.org (unknown [10.196.197.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-101.mailbox.org (Postfix) with ESMTPS id 4h8Ydq1Hr3z8v0H; Tue, 28 Jul 2026 13:49:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=unstable.cc; s=MBO0001; t=1785239343; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Q5s+IliVCAtSxSuO/VpXlFTI87izFQhZPPXqXBnI1qQ=; b=qLu2lLQqcFJMQEcJ4bsza6Ojqrj5Z/lRxWprggB0G58fmNcWIYfIYs4IXiL77VLTfHU/qH VLQeJ/o+I4xwVUnUNaany4+R1Qg/4x/7cHYekbNKImfUSp1q6tO2g+P/YtWQ6LBd+8FRvW 9/8gR/FSCjU8QXQhlB1n1tE4OZAf7NswgEmnnIf1J2i0y7HNpPDCo0PFBBjfHd5RfzfJHk QF8tGN7dZfstYhX1Oc/cDDLTY1pYPqebwzGHgTC80EwDr4s3JoK/XC1N6KYRj/XRxZqx2Z r2A2q/1muYloYqcBA+2/w5SzN0YWmAH8lQfbukN8Q3J/3U6Ebv3+QONxjosQ+w== From: Antonio Quartulli To: openvpn-devel@lists.sourceforge.net Date: Tue, 28 Jul 2026 13:48:49 +0200 Message-ID: <20260728114855.1323861-4-a@unstable.cc> In-Reply-To: <20260728114855.1323861-1-a@unstable.cc> References: <20260728114855.1323861-1-a@unstable.cc> MIME-Version: 1.0 X-Spam-Score: -0.2 (/) X-Spam-Report: Spam detection software, running on the system "sfi-spamd-2.hosts.colo.sdot.me", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Antonio Quartulli The cached local source address bind->local is updated in place on a live, RCU-published ovpn_bind while holding peer->lock: the UDP output error paths reset it (ovpn_udp4_output()/ovpn_udp6_output()) [...] Content analysis details: (-0.2 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.0 RCVD_IN_MSPIKE_H5 RBL: Excellent reputation (+5) [80.241.56.151 listed in wl.mailspike.net] 0.0 RCVD_IN_MSPIKE_WL Mailspike good senders X-Headers-End: 1wogJ5-0002um-0H Subject: [Openvpn-devel] [PATCH ovpn net v4 3/9] ovpn: fix data race reading cached local endpoint on TX path X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Antonio Quartulli Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: 1871959144999625757 X-GMAIL-MSGID: 1871959144999625757 From: Antonio Quartulli The cached local source address bind->local is updated in place on a live, RCU-published ovpn_bind while holding peer->lock: the UDP output error paths reset it (ovpn_udp4_output()/ovpn_udp6_output()) and the RX float path learns it (ovpn_peer_endpoints_update()). The UDP TX fast path and the netlink dump, however, read bind->local holding only rcu_read_lock(), never peer->lock. For bind->local.ipv6 this is a torn read: struct in6_addr is 128 bit and is copied as multiple words, so a concurrent in-place update can make a reader observe a mix of the old and new address. The mangled source address then feeds ip6_dst_lookup_flow() and udp_tunnel6_xmit_skb(). For bind->local.ipv4 (a single aligned word) it is a data race without tearing. A spinlock on the per-packet TX path is not acceptable, and READ_ONCE()/WRITE_ONCE() cannot atomically access the 128-bit IPv6 address (the >8-byte access is rejected at build time and per-word accesses still can't yield a consistent snapshot). Serialize the IPv6 field with a per-peer seqcount_spinlock_t tied to the existing peer->lock: the in-place writers (already under peer->lock) bump it, and readers take a lock-free read_seqcount_begin()/retry() snapshot via the new ovpn_peer_local_ipv6() helper. The single-word IPv4 field is handled with plain READ_ONCE()/WRITE_ONCE(). bind->remote is untouched: it is immutable for a given bind object (only swapped via whole-bind RCU replacement), so reading it locklessly remains safe. Fixes: 08857b5ec5d9 ("ovpn: implement basic TX path (UDP)") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/netlink.c | 13 +++++++++++-- drivers/net/ovpn/peer.c | 26 +++++++++++++++++++++++++- drivers/net/ovpn/peer.h | 6 ++++++ drivers/net/ovpn/udp.c | 17 +++++++++++++---- 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index 4dad85294198..8e21fa3e7822 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -610,14 +610,23 @@ static int ovpn_nl_send_peer(struct sk_buff *skb, const struct genl_info *info, bind = rcu_dereference(peer->bind); if (bind) { if (bind->remote.in4.sin_family == AF_INET) { + /* bind->local is updated in place under peer->lock; + * READ_ONCE() pairs with the WRITE_ONCE() updaters + */ if (nla_put_in_addr(skb, OVPN_A_PEER_REMOTE_IPV4, bind->remote.in4.sin_addr.s_addr) || nla_put_net16(skb, OVPN_A_PEER_REMOTE_PORT, bind->remote.in4.sin_port) || nla_put_in_addr(skb, OVPN_A_PEER_LOCAL_IPV4, - bind->local.ipv4.s_addr)) + READ_ONCE(bind->local.ipv4.s_addr))) goto err_unlock; } else if (bind->remote.in4.sin_family == AF_INET6) { + struct in6_addr local_ipv6; + + /* read the 128-bit local address under the peer + * seqcount to avoid a torn read + */ + ovpn_peer_local_ipv6(peer, bind, &local_ipv6); if (nla_put_in6_addr(skb, OVPN_A_PEER_REMOTE_IPV6, &bind->remote.in6.sin6_addr) || nla_put_u32(skb, OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID, @@ -625,7 +634,7 @@ static int ovpn_nl_send_peer(struct sk_buff *skb, const struct genl_info *info, nla_put_net16(skb, OVPN_A_PEER_REMOTE_PORT, bind->remote.in6.sin6_port) || nla_put_in6_addr(skb, OVPN_A_PEER_LOCAL_IPV6, - &bind->local.ipv6)) + &local_ipv6)) goto err_unlock; } } diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index a330892e82bf..3554da01e406 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -113,6 +113,7 @@ struct ovpn_peer *ovpn_peer_new(struct ovpn_priv *ovpn, u32 id) RCU_INIT_POINTER(peer->bind, NULL); ovpn_crypto_state_init(&peer->crypto); spin_lock_init(&peer->lock); + seqcount_spinlock_init(&peer->bind_local_seq, &peer->lock); kref_init(&peer->refcount); ovpn_peer_stats_init(&peer->vpn_stats); ovpn_peer_stats_init(&peer->link_stats); @@ -176,6 +177,27 @@ int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer, return 0; } +/** + * ovpn_peer_local_ipv6 - read the cached local IPv6 endpoint of a peer + * @peer: the peer owning the binding + * @bind: the binding to read the local address from + * @dst: where the local IPv6 address is copied to + * + * bind->local is updated in place under peer->lock (TX error path and RX + * float path). Read the 128-bit address under the peer seqcount so that + * lockless readers never observe a torn value. + */ +void ovpn_peer_local_ipv6(const struct ovpn_peer *peer, + const struct ovpn_bind *bind, struct in6_addr *dst) +{ + unsigned int seq; + + do { + seq = read_seqcount_begin(&peer->bind_local_seq); + *dst = bind->local.ipv6; + } while (read_seqcount_retry(&peer->bind_local_seq, seq)); +} + /* variable name __tbl2 needs to be different from __tbl1 * in the macro below to avoid confusing clang */ @@ -238,7 +260,7 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) netdev_name(peer->ovpn->dev), peer->id, &bind->local.ipv4.s_addr, &ip_hdr(skb)->daddr); - bind->local.ipv4.s_addr = ip_hdr(skb)->daddr; + WRITE_ONCE(bind->local.ipv4.s_addr, ip_hdr(skb)->daddr); reset_cache = true; } break; @@ -269,7 +291,9 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) netdev_name(peer->ovpn->dev), peer->id, &bind->local.ipv6, &ipv6_hdr(skb)->daddr); + write_seqcount_begin(&peer->bind_local_seq); bind->local.ipv6 = ipv6_hdr(skb)->daddr; + write_seqcount_end(&peer->bind_local_seq); reset_cache = true; } break; diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h index dfa5c0037e02..c0994c606554 100644 --- a/drivers/net/ovpn/peer.h +++ b/drivers/net/ovpn/peer.h @@ -10,6 +10,7 @@ #ifndef _NET_OVPN_OVPNPEER_H_ #define _NET_OVPN_OVPNPEER_H_ +#include #include #include @@ -56,6 +57,8 @@ * @link_stats: per-peer link/transport TX/RX stats * @delete_reason: why peer was deleted (i.e. timeout, transport error, ..) * @lock: protects binding to peer (bind) and keepalive* fields + * @bind_local_seq: seqcount serializing in-place updates of bind->local + * (done under @lock) against lockless readers on the TX path * @refcount: reference counter * @rcu: used to free peer in an RCU safe way * @release_entry: entry for the socket release list @@ -110,6 +113,7 @@ struct ovpn_peer { struct ovpn_peer_stats link_stats; enum ovpn_del_peer_reason delete_reason; spinlock_t lock; /* protects bind and keepalive* */ + seqcount_spinlock_t bind_local_seq; /* protects bind->local */ struct kref refcount; struct rcu_head rcu; struct llist_node release_entry; @@ -151,6 +155,8 @@ struct ovpn_peer *ovpn_peer_get_by_dst(struct ovpn_priv *ovpn, struct sk_buff *skb); void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer); void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer); +void ovpn_peer_local_ipv6(const struct ovpn_peer *peer, + const struct ovpn_bind *bind, struct in6_addr *dst); bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb, struct ovpn_peer *peer); diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c index 7f69e8890b5b..17d65d1595ed 100644 --- a/drivers/net/ovpn/udp.c +++ b/drivers/net/ovpn/udp.c @@ -147,7 +147,10 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind, { struct rtable *rt; struct flowi4 fl = { - .saddr = bind->local.ipv4.s_addr, + /* bind->local is updated in place under peer->lock; a single + * aligned word is read/written atomically via {READ,WRITE}_ONCE + */ + .saddr = READ_ONCE(bind->local.ipv4.s_addr), .daddr = bind->remote.in4.sin_addr.s_addr, .fl4_sport = inet_sk(sk)->inet_sport, .fl4_dport = bind->remote.in4.sin_port, @@ -169,7 +172,7 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind, */ fl.saddr = 0; spin_lock_bh(&peer->lock); - bind->local.ipv4.s_addr = 0; + WRITE_ONCE(bind->local.ipv4.s_addr, 0); spin_unlock_bh(&peer->lock); dst_cache_reset(cache); } @@ -178,7 +181,7 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind, if (IS_ERR(rt) && PTR_ERR(rt) == -EINVAL) { fl.saddr = 0; spin_lock_bh(&peer->lock); - bind->local.ipv4.s_addr = 0; + WRITE_ONCE(bind->local.ipv4.s_addr, 0); spin_unlock_bh(&peer->lock); dst_cache_reset(cache); @@ -224,7 +227,6 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind, int ret; struct flowi6 fl = { - .saddr = bind->local.ipv6, .daddr = bind->remote.in6.sin6_addr, .fl6_sport = inet_sk(sk)->inet_sport, .fl6_dport = bind->remote.in6.sin6_port, @@ -233,6 +235,11 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind, .flowi6_oif = bind->remote.in6.sin6_scope_id, }; + /* bind->local is updated in place under peer->lock; read the 128-bit + * address under the peer seqcount to avoid a torn read + */ + ovpn_peer_local_ipv6(peer, bind, &fl.saddr); + local_bh_disable(); dst = dst_cache_get_ip6(cache, &fl.saddr); if (dst) @@ -245,7 +252,9 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind, */ fl.saddr = in6addr_any; spin_lock_bh(&peer->lock); + write_seqcount_begin(&peer->bind_local_seq); bind->local.ipv6 = in6addr_any; + write_seqcount_end(&peer->bind_local_seq); spin_unlock_bh(&peer->lock); dst_cache_reset(cache); }