diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c
index 5c94d34219c73..c8c471a2959fe 100644
--- a/drivers/net/ovpn/netlink.c
+++ b/drivers/net/ovpn/netlink.c
@@ -370,6 +370,7 @@ static int ovpn_nl_peer_modify(struct ovpn_peer *peer, struct genl_info *info,
 
 int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
 {
+	struct ovpn_nl_request req = { .info = info, .sk = NETLINK_CB(skb).sk };
 	struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
 	struct ovpn_priv *ovpn = info->user_ptr[0];
 	struct ovpn_socket *ovpn_sock;
@@ -473,7 +474,7 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
 	if (ret < 0)
 		goto sock_release;
 
-	ret = ovpn_peer_add(ovpn, peer);
+	ret = ovpn_peer_add(ovpn, peer, &req);
 	if (ret < 0) {
 		NL_SET_ERR_MSG_FMT_MOD(info->extack,
 				       "cannot add new peer (id=%u) to hashtable: %d",
@@ -817,6 +818,7 @@ int ovpn_nl_peer_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 
 int ovpn_nl_peer_del_doit(struct sk_buff *skb, struct genl_info *info)
 {
+	struct ovpn_nl_request req = { .info = info, .sk = NETLINK_CB(skb).sk };
 	struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
 	struct ovpn_priv *ovpn = info->user_ptr[0];
 	struct ovpn_peer *peer;
@@ -844,7 +846,7 @@ int ovpn_nl_peer_del_doit(struct sk_buff *skb, struct genl_info *info)
 	}
 
 	netdev_dbg(ovpn->dev, "del peer %u\n", peer->id);
-	ret = ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_USERSPACE);
+	ret = ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_USERSPACE, &req);
 	ovpn_peer_put(peer);
 
 	return ret;
@@ -1180,17 +1182,27 @@ int ovpn_nl_key_del_doit(struct sk_buff *skb, struct genl_info *info)
 	return 0;
 }
 
+static int ovpn_nl_echo_filter(struct sock *sk, struct sk_buff *skb, void *data)
+{
+	return sk == data;
+}
+
 /**
  * ovpn_nl_peer_del_notify - notify userspace about peer being deleted
  * @peer: the peer being deleted
+ * @req: request context, or NULL for unsolicited notifications
  *
  * Return: 0 on success or a negative error code otherwise
  */
-int ovpn_nl_peer_del_notify(struct ovpn_peer *peer)
+int ovpn_nl_peer_del_notify(struct ovpn_peer *peer,
+			    struct ovpn_nl_request *req)
 {
+	struct genl_info *info = req ? req->info : NULL;
+	struct net *net = dev_net(peer->ovpn->dev);
 	struct sk_buff *msg;
 	struct nlattr *attr;
 	int ret = -EMSGSIZE;
+	bool report;
 	void *hdr;
 
 	netdev_info(peer->ovpn->dev, "deleting peer with id %u, reason %d\n",
@@ -1221,8 +1233,20 @@ int ovpn_nl_peer_del_notify(struct ovpn_peer *peer)
 
 	nla_nest_end(msg, attr);
 	genlmsg_end(msg, hdr);
-	genlmsg_multicast_netns(&ovpn_nl_family, dev_net(peer->ovpn->dev), msg,
-				0, OVPN_NLGRP_PEERS, GFP_ATOMIC);
+	report = info && nlmsg_report(info->nlhdr);
+
+	if (report) {
+		nlmsg_hdr(msg)->nlmsg_pid = info->snd_portid;
+		nlmsg_hdr(msg)->nlmsg_seq = info->snd_seq;
+		/* the echo goes to the requester, the multicast to the device */
+		skb_get(msg);
+	}
+	genlmsg_multicast_netns_filtered(&ovpn_nl_family, net, msg, 0,
+					 OVPN_NLGRP_PEERS, GFP_ATOMIC,
+					 ovpn_nl_echo_filter,
+					 report ? req->sk : NULL);
+	if (report)
+		genlmsg_reply(msg, info);
 
 	return 0;
 
diff --git a/drivers/net/ovpn/netlink.h b/drivers/net/ovpn/netlink.h
index 11ee7c681885d..752dd96c48932 100644
--- a/drivers/net/ovpn/netlink.h
+++ b/drivers/net/ovpn/netlink.h
@@ -9,10 +9,21 @@
 #ifndef _NET_OVPN_NETLINK_H_
 #define _NET_OVPN_NETLINK_H_
 
+/**
+ * struct ovpn_nl_request - context for request-triggered notifications
+ * @info: generic netlink request information
+ * @sk: requesting netlink socket
+ */
+struct ovpn_nl_request {
+	struct genl_info *info;
+	struct sock *sk;
+};
+
 int ovpn_nl_register(void);
 void ovpn_nl_unregister(void);
 
-int ovpn_nl_peer_del_notify(struct ovpn_peer *peer);
+int ovpn_nl_peer_del_notify(struct ovpn_peer *peer,
+			    struct ovpn_nl_request *req);
 int ovpn_nl_peer_float_notify(struct ovpn_peer *peer,
 			      const struct sockaddr_storage *ss);
 int ovpn_nl_key_swap_notify(struct ovpn_peer *peer, u8 key_id);
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index c95656ca7c357..c610d9d00854c 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -684,7 +684,8 @@ struct ovpn_peer *ovpn_peer_get_by_id(struct ovpn_priv *ovpn, u32 peer_id)
 
 static void ovpn_peer_remove(struct ovpn_peer *peer,
 			     enum ovpn_del_peer_reason reason,
-			     struct llist_head *release_list)
+			     struct llist_head *release_list,
+			     struct ovpn_nl_request *req)
 {
 	lockdep_assert_held(&peer->ovpn->lock);
 
@@ -713,7 +714,7 @@ static void ovpn_peer_remove(struct ovpn_peer *peer,
 	}
 
 	peer->delete_reason = reason;
-	ovpn_nl_peer_del_notify(peer);
+	ovpn_nl_peer_del_notify(peer, req);
 
 	/* append to provided list for later socket release and ref drop */
 	llist_add(&peer->release_entry, release_list);
@@ -1082,10 +1083,12 @@ static int ovpn_peer_add_mp(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
  * ovpn_peer_add_p2p - add peer to related tables in a P2P instance
  * @ovpn: the instance to add the peer to
  * @peer: the peer to add
+ * @req: request context
  *
  * Return: 0 on success or a negative error code otherwise
  */
-static int ovpn_peer_add_p2p(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
+static int ovpn_peer_add_p2p(struct ovpn_priv *ovpn, struct ovpn_peer *peer,
+			     struct ovpn_nl_request *req)
 {
 	LLIST_HEAD(release_list);
 	struct ovpn_peer *tmp;
@@ -1098,7 +1101,7 @@ static int ovpn_peer_add_p2p(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
 					lockdep_is_held(&ovpn->lock));
 	if (tmp)
 		ovpn_peer_remove(tmp, OVPN_DEL_PEER_REASON_TEARDOWN,
-				 &release_list);
+				 &release_list, req);
 
 	rcu_assign_pointer(ovpn->peer, peer);
 	/* in P2P mode the carrier is switched on when the peer is added */
@@ -1112,12 +1115,14 @@ static int ovpn_peer_add_p2p(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
  * ovpn_peer_add - add peer to the related tables
  * @ovpn: the openvpn instance the peer belongs to
  * @peer: the peer object to add
+ * @req: request context
  *
  * Assume refcounter was increased by caller
  *
  * Return: 0 on success or a negative error code otherwise
  */
-int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
+int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer,
+		  struct ovpn_nl_request *req)
 {
 	int ret = -ENODEV;
 
@@ -1135,7 +1140,7 @@ int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
 		ret = ovpn_peer_add_mp(ovpn, peer);
 		break;
 	case OVPN_MODE_P2P:
-		ret = ovpn_peer_add_p2p(ovpn, peer);
+		ret = ovpn_peer_add_p2p(ovpn, peer, req);
 		break;
 	}
 out:
@@ -1149,12 +1154,14 @@ int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
  * @peer: the peer to delete
  * @reason: reason why the peer was deleted (sent to userspace)
  * @release_list: list where delete peer should be appended
+ * @req: request context, or NULL for unsolicited deletion
  *
  * Return: 0 on success or a negative error code otherwise
  */
 static int ovpn_peer_del_mp(struct ovpn_peer *peer,
 			    enum ovpn_del_peer_reason reason,
-			    struct llist_head *release_list)
+			    struct llist_head *release_list,
+			    struct ovpn_nl_request *req)
 {
 	struct ovpn_peer *tmp;
 	int ret = -ENOENT;
@@ -1163,7 +1170,7 @@ static int ovpn_peer_del_mp(struct ovpn_peer *peer,
 
 	tmp = ovpn_peer_get_by_id(peer->ovpn, peer->id);
 	if (tmp == peer) {
-		ovpn_peer_remove(peer, reason, release_list);
+		ovpn_peer_remove(peer, reason, release_list, req);
 		ret = 0;
 	}
 
@@ -1178,12 +1185,14 @@ static int ovpn_peer_del_mp(struct ovpn_peer *peer,
  * @peer: the peer to delete
  * @reason: reason why the peer was deleted (sent to userspace)
  * @release_list: list where delete peer should be appended
+ * @req: request context, or NULL for unsolicited deletion
  *
  * Return: 0 on success or a negative error code otherwise
  */
 static int ovpn_peer_del_p2p(struct ovpn_peer *peer,
 			     enum ovpn_del_peer_reason reason,
-			     struct llist_head *release_list)
+			     struct llist_head *release_list,
+			     struct ovpn_nl_request *req)
 {
 	struct ovpn_peer *tmp;
 
@@ -1194,7 +1203,7 @@ static int ovpn_peer_del_p2p(struct ovpn_peer *peer,
 	if (tmp != peer)
 		return -ENOENT;
 
-	ovpn_peer_remove(peer, reason, release_list);
+	ovpn_peer_remove(peer, reason, release_list, req);
 
 	return 0;
 }
@@ -1203,10 +1212,12 @@ static int ovpn_peer_del_p2p(struct ovpn_peer *peer,
  * ovpn_peer_del - delete peer from related tables
  * @peer: the peer object to delete
  * @reason: reason for deleting peer (will be sent to userspace)
+ * @req: request context, or NULL for unsolicited deletion
  *
  * Return: 0 on success or a negative error code otherwise
  */
-int ovpn_peer_del(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason)
+int ovpn_peer_del(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason,
+		  struct ovpn_nl_request *req)
 {
 	LLIST_HEAD(release_list);
 	int ret = -EOPNOTSUPP;
@@ -1214,10 +1225,10 @@ int ovpn_peer_del(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason)
 	spin_lock_bh(&peer->ovpn->lock);
 	switch (peer->ovpn->mode) {
 	case OVPN_MODE_MP:
-		ret = ovpn_peer_del_mp(peer, reason, &release_list);
+		ret = ovpn_peer_del_mp(peer, reason, &release_list, req);
 		break;
 	case OVPN_MODE_P2P:
-		ret = ovpn_peer_del_p2p(peer, reason, &release_list);
+		ret = ovpn_peer_del_p2p(peer, reason, &release_list, req);
 		break;
 	default:
 		break;
@@ -1256,7 +1267,7 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
 		}
 	}
 
-	ovpn_peer_remove(peer, reason, &release_list);
+	ovpn_peer_remove(peer, reason, &release_list, NULL);
 	unlock_ovpn(ovpn, &release_list);
 }
 
@@ -1284,7 +1295,7 @@ static void ovpn_peers_release_mp(struct ovpn_priv *ovpn, struct sock *sk,
 		}
 
 		if (remove)
-			ovpn_peer_remove(peer, reason, &release_list);
+			ovpn_peer_remove(peer, reason, &release_list, NULL);
 	}
 	unlock_ovpn(ovpn, &release_list);
 }
@@ -1345,7 +1356,7 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer,
 		netdev_dbg(peer->ovpn->dev, "peer %u expired\n",
 			   peer->id);
 		ovpn_peer_remove(peer, OVPN_DEL_PEER_REASON_EXPIRED,
-				 release_list);
+				 release_list, NULL);
 		return 0;
 	}
 
diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h
index dfa5c0037e02b..4957c09aa0668 100644
--- a/drivers/net/ovpn/peer.h
+++ b/drivers/net/ovpn/peer.h
@@ -17,6 +17,8 @@
 #include "socket.h"
 #include "stats.h"
 
+struct ovpn_nl_request;
+
 /**
  * struct ovpn_peer - the main remote peer object
  * @ovpn: main openvpn instance this peer belongs to
@@ -139,8 +141,10 @@ static inline void ovpn_peer_put(struct ovpn_peer *peer)
 }
 
 struct ovpn_peer *ovpn_peer_new(struct ovpn_priv *ovpn, u32 id);
-int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer);
-int ovpn_peer_del(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason);
+int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer,
+		  struct ovpn_nl_request *req);
+int ovpn_peer_del(struct ovpn_peer *peer, enum ovpn_del_peer_reason reason,
+		  struct ovpn_nl_request *req);
 void ovpn_peers_free(struct ovpn_priv *ovpn, struct sock *sock,
 		     enum ovpn_del_peer_reason reason);
 
diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c
index 8fe8a8e750a40..c74f11af44074 100644
--- a/drivers/net/ovpn/tcp.c
+++ b/drivers/net/ovpn/tcp.c
@@ -502,7 +502,7 @@ static void ovpn_tcp_peer_del_work(struct work_struct *work)
 	struct ovpn_peer *peer = container_of(work, struct ovpn_peer,
 					      tcp.defer_del_work);
 
-	ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_ERROR);
+	ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_ERROR, NULL);
 	ovpn_peer_put(peer);
 }
 
@@ -594,7 +594,7 @@ static void ovpn_tcp_close(struct sock *sk, long timeout)
 	}
 	rcu_read_unlock();
 
-	ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT);
+	ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT, NULL);
 	peer->tcp.sk_cb.prot->close(sk, timeout);
 	ovpn_peer_put(peer);
 }
