2026-07-29, 17:37:40 +0200, Ralf Lici wrote:
> In MP mode, ovpn uses the peer VPN addresses as lookup keys for
> selecting the peer that should receive an outgoing tunnel packet.
> However, the netlink peer configuration path does not currently reject
> duplicate VPN addresses.
> 
> If two peers are configured with the same VPN address, both can be
> inserted in the VPN address hash table and lookups return whichever peer
> is found first. This makes peer selection ambiguous and dependent on
> hash insertion order.
> 
> Reject peer creation or update when the resulting VPN address is already
> assigned to another peer. Ignore unspecified addresses because those are
> not inserted in the VPN address hash tables.

Maybe mention that it's a user-visible change of behavior, but it
doesn't matter since that setup has never worked.


> @@ -522,22 +524,41 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct 
> genl_info *info)
>       rcu_read_unlock();
>  
>       spin_lock_bh(&ovpn->lock);
> -     ret = ovpn_nl_peer_modify(peer, info, attrs);
> -     if (ret < 0) {
> -             spin_unlock_bh(&ovpn->lock);
> -             ovpn_peer_put(peer);
> -             return ret;
> +
> +     vpn_addr4 = peer->vpn_addrs.ipv4;
> +     vpn_addr6 = peer->vpn_addrs.ipv6;
> +
> +     if (attrs[OVPN_A_PEER_VPN_IPV4])
> +             vpn_addr4.s_addr = nla_get_in_addr(attrs[OVPN_A_PEER_VPN_IPV4]);
> +
> +     if (attrs[OVPN_A_PEER_VPN_IPV6])
> +             vpn_addr6 = nla_get_in6_addr(attrs[OVPN_A_PEER_VPN_IPV6]);
> +
> +     /* reject peer with conflicting VPN address */
> +     if ((attrs[OVPN_A_PEER_VPN_IPV4] || attrs[OVPN_A_PEER_VPN_IPV6]) &&
> +         ovpn_peer_vpn_addr_conflict(ovpn, peer, &vpn_addr4, &vpn_addr6)) {
> +             NL_SET_ERR_MSG_FMT_MOD(info->extack,
> +                                    "VPN IP is already assigned to another 
> peer");
> +             ret = -EADDRINUSE;
> +             goto unlock;
>       }

nit: maybe splitting conflict() into conflict{4,6}() would be a bit
cleaner? passing addresses of 2 families feels kind of strange.

        if (attrs[OVPN_A_PEER_VPN_IPV4]) {
                struct in_addr vpn_addr4 = {
                        .s_addr = nla_get_in_addr(attrs[OVPN_A_PEER_VPN_IPV4]),
                };
                if (ovpn_peer_vpn_addr_conflict4(ovpn, peer, &vpn_addr4))
                        goto addr_conflict;
        }


And then conflict* looks slightly nicer too:

bool ovpn_peer_vpn_addr_conflict4(struct ovpn_priv *ovpn,
                                  const struct ovpn_peer *peer,
                                  const struct in_addr *addr)
{
        struct ovpn_peer *tmp = NULL;

        lockdep_assert_held(&ovpn->lock);

        /* we don't hash INADDR_ANY, no conflict in that case */
        if (addr->s_addr != htonl(INADDR_ANY))
                tmp = ovpn_peer_get_by_vpn_addr4(ovpn, addr->s_addr);

        return tmp && tmp != peer;
}

-- 
Sabrina


_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to