]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ovpn: notify userspace on client float event
authorRalf Lici <ralf@mandelbit.com>
Fri, 14 Nov 2025 10:39:40 +0000 (11:39 +0100)
committerAntonio Quartulli <antonio@openvpn.net>
Tue, 17 Mar 2026 10:08:55 +0000 (11:08 +0100)
Send a netlink notification when a client updates its remote UDP
endpoint. The notification includes the new IP address, port, and scope
ID (for IPv6).

Cc: linux-kselftest@vger.kernel.org
Cc: horms@kernel.org
Cc: shuah@kernel.org
Cc: donald.hunter@gmail.com
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Documentation/netlink/specs/ovpn.yaml
drivers/net/ovpn/netlink.c
drivers/net/ovpn/netlink.h
drivers/net/ovpn/peer.c
include/uapi/linux/ovpn.h
tools/testing/selftests/net/ovpn/ovpn-cli.c

index 1b91045cee2e44b1aacc45239aa5269ac913f89b..0d0c028bf96f893849db950f7d1a120126947593 100644 (file)
@@ -502,6 +502,12 @@ operations:
             - ifindex
             - keyconf
 
+    -
+      name: peer-float-ntf
+      doc: Notification about a peer floating (changing its remote UDP endpoint)
+      notify: peer-get
+      mcgrp: peers
+
 mcast-groups:
   list:
     -
index fed0e46b32a33419df3ba0c24c8ce51de7cfe779..e10d7f9a28f50ac17f1413312a509ab1c64460d0 100644 (file)
@@ -1203,6 +1203,88 @@ err_free_msg:
        return ret;
 }
 
+/**
+ * ovpn_nl_peer_float_notify - notify userspace about peer floating
+ * @peer: the floated peer
+ * @ss: sockaddr representing the new remote endpoint
+ *
+ * Return: 0 on success or a negative error code otherwise
+ */
+int ovpn_nl_peer_float_notify(struct ovpn_peer *peer,
+                             const struct sockaddr_storage *ss)
+{
+       struct ovpn_socket *sock;
+       struct sockaddr_in6 *sa6;
+       struct sockaddr_in *sa;
+       struct sk_buff *msg;
+       struct nlattr *attr;
+       int ret = -EMSGSIZE;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &ovpn_nl_family, 0,
+                         OVPN_CMD_PEER_FLOAT_NTF);
+       if (!hdr) {
+               ret = -ENOBUFS;
+               goto err_free_msg;
+       }
+
+       if (nla_put_u32(msg, OVPN_A_IFINDEX, peer->ovpn->dev->ifindex))
+               goto err_cancel_msg;
+
+       attr = nla_nest_start(msg, OVPN_A_PEER);
+       if (!attr)
+               goto err_cancel_msg;
+
+       if (nla_put_u32(msg, OVPN_A_PEER_ID, peer->id))
+               goto err_cancel_msg;
+
+       if (ss->ss_family == AF_INET) {
+               sa = (struct sockaddr_in *)ss;
+               if (nla_put_in_addr(msg, OVPN_A_PEER_REMOTE_IPV4,
+                                   sa->sin_addr.s_addr) ||
+                   nla_put_net16(msg, OVPN_A_PEER_REMOTE_PORT, sa->sin_port))
+                       goto err_cancel_msg;
+       } else if (ss->ss_family == AF_INET6) {
+               sa6 = (struct sockaddr_in6 *)ss;
+               if (nla_put_in6_addr(msg, OVPN_A_PEER_REMOTE_IPV6,
+                                    &sa6->sin6_addr) ||
+                   nla_put_u32(msg, OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID,
+                               sa6->sin6_scope_id) ||
+                   nla_put_net16(msg, OVPN_A_PEER_REMOTE_PORT, sa6->sin6_port))
+                       goto err_cancel_msg;
+       } else {
+               ret = -EAFNOSUPPORT;
+               goto err_cancel_msg;
+       }
+
+       nla_nest_end(msg, attr);
+       genlmsg_end(msg, hdr);
+
+       rcu_read_lock();
+       sock = rcu_dereference(peer->sock);
+       if (!sock) {
+               ret = -EINVAL;
+               goto err_unlock;
+       }
+       genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sk), msg,
+                               0, OVPN_NLGRP_PEERS, GFP_ATOMIC);
+       rcu_read_unlock();
+
+       return 0;
+
+err_unlock:
+       rcu_read_unlock();
+err_cancel_msg:
+       genlmsg_cancel(msg, hdr);
+err_free_msg:
+       nlmsg_free(msg);
+       return ret;
+}
+
 /**
  * ovpn_nl_key_swap_notify - notify userspace peer's key must be renewed
  * @peer: the peer whose key needs to be renewed
index 8615dfc3c4720a2a550b5cd1a8454ccc58a3c6ba..11ee7c681885de2b999fe89956238468af0c86bd 100644 (file)
@@ -13,6 +13,8 @@ int ovpn_nl_register(void);
 void ovpn_nl_unregister(void);
 
 int ovpn_nl_peer_del_notify(struct ovpn_peer *peer);
+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);
 
 #endif /* _NET_OVPN_NETLINK_H_ */
index 3716a1d828015e2f6cdfa75d1a81c89d1f32b4ce..4e145b4497e6658cdfb71c55dca11d447982a03e 100644 (file)
@@ -287,6 +287,8 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
 
        spin_unlock_bh(&peer->lock);
 
+       ovpn_nl_peer_float_notify(peer, &ss);
+
        /* rehashing is required only in MP mode as P2P has one peer
         * only and thus there is no hashtable
         */
index 959b41def61f6db2f373f75bd4956d8afabdacac..0cce0d58b830288cd5e32d2e1cfa3d4f76277041 100644 (file)
@@ -100,6 +100,7 @@ enum {
        OVPN_CMD_KEY_SWAP,
        OVPN_CMD_KEY_SWAP_NTF,
        OVPN_CMD_KEY_DEL,
+       OVPN_CMD_PEER_FLOAT_NTF,
 
        __OVPN_CMD_MAX,
        OVPN_CMD_MAX = (__OVPN_CMD_MAX - 1)
index 0f3babf19fd08463179535184897dd8b63bde502..7178abae1b2ff7e35013c6f2c4653c4f1e066361 100644 (file)
@@ -1516,6 +1516,9 @@ static int ovpn_handle_msg(struct nl_msg *msg, void *arg)
        case OVPN_CMD_PEER_DEL_NTF:
                fprintf(stdout, "received CMD_PEER_DEL_NTF\n");
                break;
+       case OVPN_CMD_PEER_FLOAT_NTF:
+               fprintf(stdout, "received CMD_PEER_FLOAT_NTF\n");
+               break;
        case OVPN_CMD_KEY_SWAP_NTF:
                fprintf(stdout, "received CMD_KEY_SWAP_NTF\n");
                break;