@@ -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;
@@ -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);
@@ -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;
}
@@ -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);
@@ -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);
}