mirror of
https://github.com/torvalds/linux.git
synced 2026-09-26 01:52:03 +02:00
Included fixes:
* add selftest coverage for peer VPN address validation * reject multicast, broadcast and loopback peer VPN addresses, which can never identify a peer * reject MP peers left with no usable VPN address, as they can never be selected for TX * reject duplicate peer VPN addresses, which made peer lookup return an arbitrary peer * fix stale entry left in the VPN address hashtable when an address is cleared * fix torn IPv6 address read on lockless TX when the unusable local source is cleared in place * fix torn IPv6 address read on lockless TX when a new local endpoint is learned in place * fix dst cache being populated with a route resolved from an already replaced bind * fix stale route being reused after the socket mark or UDP source port changed * fix bogus validation of an unspecified local source address, which must instead be left to route source autoselection * fix IPv6 link-local peer endpoints losing their scope id when configured via netlink, breaking route lookup -----BEGIN PGP SIGNATURE----- iJEEABYIADkWIQQr0db7q+Rc7Zog28Fc8QQzwdnOtwUCarECxxsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDIACgkQXPEEM8HZzrcvTAEA/r3oFnZ4EOtiOyA2OpZ/ Iya+WAJ7LShj7tLymzujMe8BAIH6J2RyrWXnfDFvtSG8HGDEe4EBzVpP56tX0ZVn xc0N =fg0G -----END PGP SIGNATURE----- Merge tag 'ovpn-net-20260921' of https://github.com/OpenVPN/ovpn-net-next Antonio Quartulli says: ==================== Included fixes: * add selftest coverage for peer VPN address validation * reject multicast, broadcast and loopback peer VPN addresses, which can never identify a peer * reject MP peers left with no usable VPN address, as they can never be selected for TX * reject duplicate peer VPN addresses, which made peer lookup return an arbitrary peer * fix stale entry left in the VPN address hashtable when an address is cleared * fix torn IPv6 address read on lockless TX when the unusable local source is cleared in place * fix torn IPv6 address read on lockless TX when a new local endpoint is learned in place * fix dst cache being populated with a route resolved from an already replaced bind * fix stale route being reused after the socket mark or UDP source port changed * fix bogus validation of an unspecified local source address, which must instead be left to route source autoselection * fix IPv6 link-local peer endpoints losing their scope id when configured via netlink, breaking route lookup * tag 'ovpn-net-20260921' of https://github.com/OpenVPN/ovpn-net-next: selftests: ovpn: validate peer VPN addresses ovpn: reject invalid peer VPN addresses ovpn: reject multipeer peers without VPN addresses ovpn: reject duplicate peer VPN addresses ovpn: always unhash old VPN addresses before rehashing ovpn: replace bind when clearing stale local source ovpn: replace bind when learning local endpoint ovpn: validate peer state before caching UDP dst ovpn: track UDP socket route key for peer dst cache ovpn: skip UDP source validation for unspecified addresses ovpn: preserve IPv6 scope id for netlink peer endpoints ==================== Link: https://patch.msgid.link/20260921102215.3599702-1-antonio@openvpn.net Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
33a61f0923
|
|
@ -100,6 +100,8 @@ static bool ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs,
|
|||
struct sockaddr_in6 *sin6;
|
||||
struct sockaddr_in *sin;
|
||||
struct in6_addr *in6;
|
||||
struct nlattr *scope;
|
||||
u32 scope_id = 0;
|
||||
__be16 port = 0;
|
||||
__be32 *in;
|
||||
|
||||
|
|
@ -114,6 +116,9 @@ static bool ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs,
|
|||
} else if (attrs[OVPN_A_PEER_REMOTE_IPV6]) {
|
||||
ss->ss_family = AF_INET6;
|
||||
in6 = nla_data(attrs[OVPN_A_PEER_REMOTE_IPV6]);
|
||||
scope = attrs[OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID];
|
||||
if (scope)
|
||||
scope_id = nla_get_u32(scope);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -126,6 +131,7 @@ static bool ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs,
|
|||
if (!ipv6_addr_v4mapped(in6)) {
|
||||
sin6 = (struct sockaddr_in6 *)ss;
|
||||
sin6->sin6_port = port;
|
||||
sin6->sin6_scope_id = scope_id;
|
||||
memcpy(&sin6->sin6_addr, in6, sizeof(*in6));
|
||||
break;
|
||||
}
|
||||
|
|
@ -179,6 +185,39 @@ static sa_family_t ovpn_nl_family_get(struct nlattr *addr4,
|
|||
return AF_UNSPEC;
|
||||
}
|
||||
|
||||
static int ovpn_nl_peer_check_vpn_addrs(const struct in_addr *addr4,
|
||||
const struct in6_addr *addr6,
|
||||
struct genl_info *info)
|
||||
{
|
||||
int addr6_type;
|
||||
|
||||
if (addr4->s_addr == htonl(INADDR_ANY) && ipv6_addr_any(addr6)) {
|
||||
NL_SET_ERR_MSG_MOD(info->extack,
|
||||
"at least one VPN IP must be configured in MP mode");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ipv4_is_multicast(addr4->s_addr) || ipv4_is_lbcast(addr4->s_addr) ||
|
||||
ipv4_is_loopback(addr4->s_addr)) {
|
||||
NL_SET_ERR_MSG_MOD(info->extack,
|
||||
"VPN IPv4 address must be valid unicast or any");
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
if (!ipv6_addr_any(addr6)) {
|
||||
addr6_type = ipv6_addr_type(addr6);
|
||||
|
||||
if (!(addr6_type & IPV6_ADDR_UNICAST) ||
|
||||
(addr6_type & (IPV6_ADDR_LOOPBACK | IPV6_ADDR_COMPATv4))) {
|
||||
NL_SET_ERR_MSG_MOD(info->extack,
|
||||
"VPN IPv6 address must be valid unicast or any");
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ovpn_nl_peer_precheck(struct ovpn_priv *ovpn,
|
||||
struct genl_info *info,
|
||||
struct nlattr **attrs)
|
||||
|
|
@ -346,8 +385,10 @@ 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 nlattr *attrs[OVPN_A_PEER_MAX + 1];
|
||||
struct in_addr vpn_addr4 = { .s_addr = htonl(INADDR_ANY) };
|
||||
struct in6_addr vpn_addr6 = IN6ADDR_ANY_INIT;
|
||||
struct ovpn_priv *ovpn = info->user_ptr[0];
|
||||
struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
|
||||
struct ovpn_socket *ovpn_sock;
|
||||
struct socket *sock = NULL;
|
||||
struct ovpn_peer *peer;
|
||||
|
|
@ -371,11 +412,18 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
return -EINVAL;
|
||||
|
||||
/* in MP mode VPN IPs are required for selecting the right peer */
|
||||
if (ovpn->mode == OVPN_MODE_MP && !attrs[OVPN_A_PEER_VPN_IPV4] &&
|
||||
!attrs[OVPN_A_PEER_VPN_IPV6]) {
|
||||
NL_SET_ERR_MSG_FMT_MOD(info->extack,
|
||||
"VPN IP must be provided in MP mode");
|
||||
return -EINVAL;
|
||||
if (ovpn->mode == OVPN_MODE_MP) {
|
||||
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]);
|
||||
|
||||
ret = ovpn_nl_peer_check_vpn_addrs(&vpn_addr4, &vpn_addr6,
|
||||
info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
peer_id = nla_get_u32(attrs[OVPN_A_PEER_ID]);
|
||||
|
|
@ -474,8 +522,10 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
|
||||
struct ovpn_priv *ovpn = info->user_ptr[0];
|
||||
struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
|
||||
struct in6_addr vpn_addr6;
|
||||
struct in_addr vpn_addr4;
|
||||
struct ovpn_socket *sock;
|
||||
struct ovpn_peer *peer;
|
||||
u32 peer_id;
|
||||
|
|
@ -522,28 +572,58 @@ 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;
|
||||
|
||||
/* reject peer with conflicting VPN address */
|
||||
if (attrs[OVPN_A_PEER_VPN_IPV4]) {
|
||||
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;
|
||||
}
|
||||
if (attrs[OVPN_A_PEER_VPN_IPV6]) {
|
||||
vpn_addr6 = nla_get_in6_addr(attrs[OVPN_A_PEER_VPN_IPV6]);
|
||||
if (ovpn_peer_vpn_addr_conflict6(ovpn, peer, &vpn_addr6))
|
||||
goto addr_conflict;
|
||||
}
|
||||
|
||||
/* in MP mode VPN IPs are required for selecting the right peer */
|
||||
if (ovpn->mode == OVPN_MODE_MP) {
|
||||
ret = ovpn_nl_peer_check_vpn_addrs(&vpn_addr4, &vpn_addr6,
|
||||
info);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = ovpn_nl_peer_modify(peer, info, attrs);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
/* ret == 1 means that VPN IPv4/6 has been modified and rehashing
|
||||
* is required
|
||||
*/
|
||||
if (ret > 0)
|
||||
if (ret > 0) {
|
||||
ovpn_peer_hash_vpn_ip(peer);
|
||||
ret = 0;
|
||||
}
|
||||
/* if the remote endpoint was updated, the by_transp_addr hash bucket
|
||||
* also needs to be refreshed, otherwise incoming packets from the new
|
||||
* remote address would fail the lockless lookup
|
||||
*/
|
||||
if (attrs[OVPN_A_PEER_REMOTE_IPV4] || attrs[OVPN_A_PEER_REMOTE_IPV6])
|
||||
ovpn_peer_hash_transp_addr(peer);
|
||||
|
||||
unlock:
|
||||
spin_unlock_bh(&ovpn->lock);
|
||||
ovpn_peer_put(peer);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
addr_conflict:
|
||||
NL_SET_ERR_MSG_FMT_MOD(info->extack,
|
||||
"VPN IP is already assigned to another peer");
|
||||
ret = -EADDRINUSE;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
static int ovpn_nl_send_peer(struct sk_buff *skb, const struct genl_info *info,
|
||||
|
|
|
|||
|
|
@ -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->route_key_seq, &peer->lock);
|
||||
kref_init(&peer->refcount);
|
||||
ovpn_peer_stats_init(&peer->vpn_stats);
|
||||
ovpn_peer_stats_init(&peer->link_stats);
|
||||
|
|
@ -199,13 +200,12 @@ static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer,
|
|||
*/
|
||||
void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
|
||||
{
|
||||
const void *local_ip = NULL;
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr_in6 *sa6;
|
||||
bool reset_cache = false;
|
||||
struct sockaddr_in *sa;
|
||||
struct ovpn_bind *bind;
|
||||
const void *local_ip;
|
||||
size_t salen = 0;
|
||||
bool floated = false;
|
||||
|
||||
spin_lock_bh(&peer->lock);
|
||||
bind = rcu_dereference_protected(peer->bind,
|
||||
|
|
@ -232,8 +232,7 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
|
|||
.sin_addr.s_addr = ip_hdr(skb)->saddr,
|
||||
.sin_port = udp_hdr(skb)->source,
|
||||
};
|
||||
salen = sizeof(*sa);
|
||||
reset_cache = true;
|
||||
floated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -245,10 +244,12 @@ 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;
|
||||
reset_cache = true;
|
||||
local_ip = &ip_hdr(skb)->daddr;
|
||||
memcpy(&ss, &bind->remote, sizeof(struct sockaddr_in));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* nothing changed */
|
||||
goto unlock;
|
||||
case htons(ETH_P_IPV6):
|
||||
/* float check */
|
||||
if (unlikely(!ovpn_bind_skb_src_match(bind, skb))) {
|
||||
|
|
@ -270,8 +271,7 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
|
|||
ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr,
|
||||
skb->skb_iif),
|
||||
};
|
||||
salen = sizeof(*sa6);
|
||||
reset_cache = true;
|
||||
floated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -284,26 +284,30 @@ 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);
|
||||
bind->local.ipv6 = ipv6_hdr(skb)->daddr;
|
||||
reset_cache = true;
|
||||
local_ip = &ipv6_hdr(skb)->daddr;
|
||||
memcpy(&ss, &bind->remote, sizeof(struct sockaddr_in6));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* nothing changed */
|
||||
goto unlock;
|
||||
default:
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (unlikely(reset_cache))
|
||||
dst_cache_reset(&peer->dst_cache);
|
||||
|
||||
/* if the peer did not float, we can bail out now */
|
||||
if (likely(!salen))
|
||||
goto unlock;
|
||||
|
||||
if (unlikely(ovpn_peer_reset_sockaddr(peer,
|
||||
(struct sockaddr_storage *)&ss,
|
||||
local_ip) < 0))
|
||||
goto unlock;
|
||||
|
||||
/* reset the cache only after a successful bind update to avoid useless
|
||||
* cache misses on concurrent TX
|
||||
*/
|
||||
dst_cache_reset(&peer->dst_cache);
|
||||
|
||||
/* if only the local address changed, bail out now */
|
||||
if (!floated)
|
||||
goto unlock;
|
||||
|
||||
net_dbg_ratelimited("%s: peer %d floated to %pIScp",
|
||||
netdev_name(peer->ovpn->dev), peer->id, &ss);
|
||||
|
||||
|
|
@ -484,7 +488,7 @@ static struct ovpn_peer *ovpn_peer_get_by_vpn_addr4(struct ovpn_priv *ovpn,
|
|||
* Return: the peer if found or NULL otherwise
|
||||
*/
|
||||
static struct ovpn_peer *ovpn_peer_get_by_vpn_addr6(struct ovpn_priv *ovpn,
|
||||
struct in6_addr *addr)
|
||||
const struct in6_addr *addr)
|
||||
{
|
||||
struct hlist_nulls_head *nhead;
|
||||
struct hlist_nulls_node *ntmp;
|
||||
|
|
@ -509,6 +513,64 @@ static struct ovpn_peer *ovpn_peer_get_by_vpn_addr6(struct ovpn_priv *ovpn,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_peer_vpn_addr_conflict4 - check if the VPN v4 address is already in use
|
||||
* @ovpn: the openvpn instance to search
|
||||
* @peer: peer being added or updated, or NULL
|
||||
* @addr: VPN IPv4 address to check
|
||||
*
|
||||
* Check whether @addr is already assigned to another peer. @peer is ignored
|
||||
* when found, allowing peer updates that keep an existing address.
|
||||
* Unspecified addresses are ignored.
|
||||
*
|
||||
* Note: the caller must hold @ovpn->lock.
|
||||
*
|
||||
* Return: true on conflict, false otherwise.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_peer_vpn_addr_conflict6 - check if the VPN v6 address is already in use
|
||||
* @ovpn: the openvpn instance to search
|
||||
* @peer: peer being added or updated, or NULL
|
||||
* @addr: VPN IPv6 address to check
|
||||
*
|
||||
* Check whether @addr is already assigned to another peer. @peer is ignored
|
||||
* when found, allowing peer updates that keep an existing address.
|
||||
* Unspecified addresses are ignored.
|
||||
*
|
||||
* Note: the caller must hold @ovpn->lock.
|
||||
*
|
||||
* Return: true on conflict, false otherwise.
|
||||
*/
|
||||
bool ovpn_peer_vpn_addr_conflict6(struct ovpn_priv *ovpn,
|
||||
const struct ovpn_peer *peer,
|
||||
const struct in6_addr *addr)
|
||||
{
|
||||
struct ovpn_peer *tmp = NULL;
|
||||
|
||||
lockdep_assert_held(&ovpn->lock);
|
||||
|
||||
/* we don't hash ::, no conflict in that case */
|
||||
if (!ipv6_addr_any(addr))
|
||||
tmp = ovpn_peer_get_by_vpn_addr6(ovpn, addr);
|
||||
|
||||
return tmp && tmp != peer;
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_peer_transp_match - check if sockaddr and peer binding match
|
||||
* @peer: the peer to get the binding from
|
||||
|
|
@ -990,10 +1052,11 @@ void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer)
|
|||
if (hlist_unhashed(&peer->hash_entry_id))
|
||||
return;
|
||||
|
||||
if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY)) {
|
||||
/* remove potential old hashing */
|
||||
hlist_nulls_del_init_rcu(&peer->hash_entry_addr4);
|
||||
/* remove potential old hashing */
|
||||
hlist_nulls_del_init_rcu(&peer->hash_entry_addr4);
|
||||
hlist_nulls_del_init_rcu(&peer->hash_entry_addr6);
|
||||
|
||||
if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY)) {
|
||||
nhead = ovpn_get_hash_head(peer->ovpn->peers->by_vpn_addr4,
|
||||
&peer->vpn_addrs.ipv4,
|
||||
sizeof(peer->vpn_addrs.ipv4));
|
||||
|
|
@ -1001,9 +1064,6 @@ void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer)
|
|||
}
|
||||
|
||||
if (!ipv6_addr_any(&peer->vpn_addrs.ipv6)) {
|
||||
/* remove potential old hashing */
|
||||
hlist_nulls_del_init_rcu(&peer->hash_entry_addr6);
|
||||
|
||||
nhead = ovpn_get_hash_head(peer->ovpn->peers->by_vpn_addr6,
|
||||
&peer->vpn_addrs.ipv6,
|
||||
sizeof(peer->vpn_addrs.ipv6));
|
||||
|
|
@ -1038,6 +1098,13 @@ static int ovpn_peer_add_mp(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* reject peer with conflicting VPN address */
|
||||
if (ovpn_peer_vpn_addr_conflict4(ovpn, NULL, &peer->vpn_addrs.ipv4) ||
|
||||
ovpn_peer_vpn_addr_conflict6(ovpn, NULL, &peer->vpn_addrs.ipv6)) {
|
||||
ret = -EADDRINUSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bind = rcu_dereference_protected(peer->bind, true);
|
||||
/* peers connected via TCP have bind == NULL */
|
||||
if (bind) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#ifndef _NET_OVPN_OVPNPEER_H_
|
||||
#define _NET_OVPN_OVPNPEER_H_
|
||||
|
||||
#include <linux/seqlock.h>
|
||||
#include <net/dst_cache.h>
|
||||
#include <net/strparser.h>
|
||||
|
||||
|
|
@ -17,6 +18,16 @@
|
|||
#include "socket.h"
|
||||
#include "stats.h"
|
||||
|
||||
/**
|
||||
* struct ovpn_route_key - route key used for the peer dst cache
|
||||
* @mark: fwmark used for route lookup
|
||||
* @sport: UDP source port used for route lookup
|
||||
*/
|
||||
struct ovpn_route_key {
|
||||
u32 mark;
|
||||
__be16 sport;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ovpn_peer - the main remote peer object
|
||||
* @ovpn: main openvpn instance this peer belongs to
|
||||
|
|
@ -45,6 +56,8 @@
|
|||
* @tcp.sk_cb.ops: pointer to the original prot_ops object (TCP only)
|
||||
* @crypto: the crypto configuration (ciphers, keys, etc..)
|
||||
* @dst_cache: cache for dst_entry used to send to peer
|
||||
* @route_key: route key matching the current dst cache contents
|
||||
* @route_key_seq: seqcount protecting lockless route_key reads
|
||||
* @bind: remote peer binding
|
||||
* @keepalive_interval: seconds after which a new keepalive should be sent
|
||||
* @keepalive_xmit_exp: future timestamp when next keepalive should be sent
|
||||
|
|
@ -55,7 +68,7 @@
|
|||
* @vpn_stats: per-peer in-VPN TX/RX stats
|
||||
* @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
|
||||
* @lock: protects binding to peer (bind), route_key and keepalive* fields
|
||||
* @refcount: reference counter
|
||||
* @rcu: used to free peer in an RCU safe way
|
||||
* @release_entry: entry for the socket release list
|
||||
|
|
@ -99,6 +112,8 @@ struct ovpn_peer {
|
|||
} tcp;
|
||||
struct ovpn_crypto_state crypto;
|
||||
struct dst_cache dst_cache;
|
||||
struct ovpn_route_key route_key;
|
||||
seqcount_spinlock_t route_key_seq;
|
||||
struct ovpn_bind __rcu *bind;
|
||||
unsigned long keepalive_interval;
|
||||
unsigned long keepalive_xmit_exp;
|
||||
|
|
@ -109,7 +124,7 @@ struct ovpn_peer {
|
|||
struct ovpn_peer_stats vpn_stats;
|
||||
struct ovpn_peer_stats link_stats;
|
||||
enum ovpn_del_peer_reason delete_reason;
|
||||
spinlock_t lock; /* protects bind and keepalive* */
|
||||
spinlock_t lock; /* protects bind, route_key and keepalive* */
|
||||
struct kref refcount;
|
||||
struct rcu_head rcu;
|
||||
struct llist_node release_entry;
|
||||
|
|
@ -149,6 +164,12 @@ struct ovpn_peer *ovpn_peer_get_by_transp_addr(struct ovpn_priv *ovpn,
|
|||
struct ovpn_peer *ovpn_peer_get_by_id(struct ovpn_priv *ovpn, u32 peer_id);
|
||||
struct ovpn_peer *ovpn_peer_get_by_dst(struct ovpn_priv *ovpn,
|
||||
struct sk_buff *skb);
|
||||
bool ovpn_peer_vpn_addr_conflict4(struct ovpn_priv *ovpn,
|
||||
const struct ovpn_peer *peer,
|
||||
const struct in_addr *addr);
|
||||
bool ovpn_peer_vpn_addr_conflict6(struct ovpn_priv *ovpn,
|
||||
const struct ovpn_peer *peer,
|
||||
const struct in6_addr *addr);
|
||||
void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer);
|
||||
void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer);
|
||||
bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb,
|
||||
|
|
|
|||
|
|
@ -131,6 +131,77 @@ static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool ovpn_route_key_equal(const struct ovpn_route_key *a,
|
||||
const struct ovpn_route_key *b)
|
||||
{
|
||||
return a->mark == b->mark && a->sport == b->sport;
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_dst_cache_check_key - reset peer dst cache after key changes
|
||||
* @peer: the peer owning the dst cache
|
||||
* @cache: the cache that might need to be reset
|
||||
* @key: the route key for the packet being transmitted
|
||||
*
|
||||
* Reset the peer dst cache if it was populated for a different route key.
|
||||
*/
|
||||
static void ovpn_dst_cache_check_key(struct ovpn_peer *peer,
|
||||
struct dst_cache *cache,
|
||||
const struct ovpn_route_key *key)
|
||||
{
|
||||
struct ovpn_route_key old_key;
|
||||
unsigned int seq;
|
||||
|
||||
/* snapshot the saved key before deciding whether the cache matches */
|
||||
do {
|
||||
seq = read_seqcount_begin(&peer->route_key_seq);
|
||||
old_key = peer->route_key;
|
||||
} while (read_seqcount_retry(&peer->route_key_seq, seq));
|
||||
|
||||
/* nothing changed: the current cache can be reused */
|
||||
if (likely(ovpn_route_key_equal(&old_key, key)))
|
||||
return;
|
||||
|
||||
/* recheck under lock because another path may have updated the key */
|
||||
spin_lock_bh(&peer->lock);
|
||||
if (!ovpn_route_key_equal(&peer->route_key, key)) {
|
||||
write_seqcount_begin(&peer->route_key_seq);
|
||||
peer->route_key = *key;
|
||||
dst_cache_reset(cache);
|
||||
write_seqcount_end(&peer->route_key_seq);
|
||||
}
|
||||
spin_unlock_bh(&peer->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_dst_cache_current - check whether a route lookup matches peer state
|
||||
* @peer: the peer owning the bind and dst cache
|
||||
* @bind: the RCU bind used for the route lookup
|
||||
* @key: the route key used for the route lookup
|
||||
*
|
||||
* Check that @bind is still the current peer bind and that @key still matches
|
||||
* the peer route key. The caller must hold @peer->lock. The TX path keeps
|
||||
* @bind inside an RCU read-side critical section, so pointer identity is enough
|
||||
* to detect whether the bind was replaced while the route lookup was running.
|
||||
*
|
||||
* Return: true if the lookup result still matches the current peer state and
|
||||
* may update the dst cache or replace the bind.
|
||||
*/
|
||||
static bool ovpn_dst_cache_current(const struct ovpn_peer *peer,
|
||||
const struct ovpn_bind *bind,
|
||||
const struct ovpn_route_key *key)
|
||||
{
|
||||
const struct ovpn_bind *curr_bind;
|
||||
|
||||
lockdep_assert_held(&peer->lock);
|
||||
|
||||
curr_bind = rcu_dereference_protected(peer->bind,
|
||||
lockdep_is_held(&peer->lock));
|
||||
|
||||
return curr_bind == bind &&
|
||||
ovpn_route_key_equal(key, &peer->route_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* ovpn_udp4_output - send IPv4 packet over udp socket
|
||||
* @peer: the destination peer
|
||||
|
|
@ -138,21 +209,26 @@ static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
|
|||
* @cache: dst cache
|
||||
* @sk: the socket to send the packet over
|
||||
* @skb: the packet to send
|
||||
* @key: the route key snapshot used for cache validation and flow lookup
|
||||
*
|
||||
* Return: 0 on success or a negative error code otherwise
|
||||
*/
|
||||
static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
||||
struct dst_cache *cache, struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
struct sk_buff *skb,
|
||||
const struct ovpn_route_key *key)
|
||||
{
|
||||
struct sockaddr_storage remote;
|
||||
struct in_addr local = {};
|
||||
bool reset_local = false;
|
||||
struct rtable *rt;
|
||||
struct flowi4 fl = {
|
||||
.saddr = bind->local.ipv4.s_addr,
|
||||
.daddr = bind->remote.in4.sin_addr.s_addr,
|
||||
.fl4_sport = inet_sk(sk)->inet_sport,
|
||||
.fl4_sport = key->sport,
|
||||
.fl4_dport = bind->remote.in4.sin_port,
|
||||
.flowi4_proto = sk->sk_protocol,
|
||||
.flowi4_mark = sk->sk_mark,
|
||||
.flowi4_mark = key->mark,
|
||||
};
|
||||
int ret;
|
||||
|
||||
|
|
@ -161,26 +237,19 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
if (rt)
|
||||
goto transmit;
|
||||
|
||||
if (unlikely(!inet_confirm_addr(sock_net(sk), NULL, 0, fl.saddr,
|
||||
RT_SCOPE_HOST))) {
|
||||
/* we may end up here when the cached address is not usable
|
||||
* anymore. In this case we reset address/cache and perform a
|
||||
* new look up
|
||||
if (fl.saddr && unlikely(!inet_confirm_addr(sock_net(sk), NULL, 0,
|
||||
fl.saddr, RT_SCOPE_HOST))) {
|
||||
/* The learned local address is not usable anymore.
|
||||
* Retry with source address autoselection.
|
||||
*/
|
||||
fl.saddr = 0;
|
||||
spin_lock_bh(&peer->lock);
|
||||
bind->local.ipv4.s_addr = 0;
|
||||
spin_unlock_bh(&peer->lock);
|
||||
dst_cache_reset(cache);
|
||||
reset_local = true;
|
||||
}
|
||||
|
||||
rt = ip_route_output_flow(sock_net(sk), &fl, sk);
|
||||
if (IS_ERR(rt) && PTR_ERR(rt) == -EINVAL) {
|
||||
fl.saddr = 0;
|
||||
spin_lock_bh(&peer->lock);
|
||||
bind->local.ipv4.s_addr = 0;
|
||||
spin_unlock_bh(&peer->lock);
|
||||
dst_cache_reset(cache);
|
||||
reset_local = true;
|
||||
|
||||
rt = ip_route_output_flow(sock_net(sk), &fl, sk);
|
||||
}
|
||||
|
|
@ -193,7 +262,30 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
ret);
|
||||
goto err;
|
||||
}
|
||||
dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
|
||||
|
||||
/* avoid storing a stale cache or local address */
|
||||
spin_lock_bh(&peer->lock);
|
||||
if (likely(ovpn_dst_cache_current(peer, bind, key))) {
|
||||
if (!reset_local) {
|
||||
dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
|
||||
spin_unlock_bh(&peer->lock);
|
||||
goto transmit;
|
||||
}
|
||||
|
||||
/* invalidate per-CPU dst entries that may still carry
|
||||
* the stale source
|
||||
*/
|
||||
dst_cache_reset(cache);
|
||||
|
||||
/* preserve the current remote */
|
||||
memcpy(&remote, &bind->remote, sizeof(struct sockaddr_in));
|
||||
/* The current packet already has a valid wildcard-source route.
|
||||
* If replacing the bind fails, leave the stale local in place;
|
||||
* a later cache miss will retry the repair.
|
||||
*/
|
||||
ovpn_peer_reset_sockaddr(peer, &remote, &local);
|
||||
}
|
||||
spin_unlock_bh(&peer->lock);
|
||||
|
||||
transmit:
|
||||
udp_tunnel_xmit_skb(rt, sk, skb, fl.saddr, fl.daddr, 0,
|
||||
|
|
@ -213,23 +305,28 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
* @cache: dst cache
|
||||
* @sk: the socket to send the packet over
|
||||
* @skb: the packet to send
|
||||
* @key: the route key snapshot used for cache validation and flow lookup
|
||||
*
|
||||
* Return: 0 on success or a negative error code otherwise
|
||||
*/
|
||||
static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
||||
struct dst_cache *cache, struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
struct sk_buff *skb,
|
||||
const struct ovpn_route_key *key)
|
||||
{
|
||||
struct in6_addr local = in6addr_any;
|
||||
struct sockaddr_storage remote;
|
||||
bool reset_local = false;
|
||||
struct dst_entry *dst;
|
||||
int ret;
|
||||
|
||||
struct flowi6 fl = {
|
||||
.saddr = bind->local.ipv6,
|
||||
.daddr = bind->remote.in6.sin6_addr,
|
||||
.fl6_sport = inet_sk(sk)->inet_sport,
|
||||
.fl6_sport = key->sport,
|
||||
.fl6_dport = bind->remote.in6.sin6_port,
|
||||
.flowi6_proto = sk->sk_protocol,
|
||||
.flowi6_mark = sk->sk_mark,
|
||||
.flowi6_mark = key->mark,
|
||||
.flowi6_oif = bind->remote.in6.sin6_scope_id,
|
||||
};
|
||||
|
||||
|
|
@ -238,16 +335,13 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
if (dst)
|
||||
goto transmit;
|
||||
|
||||
if (unlikely(!ipv6_chk_addr(sock_net(sk), &fl.saddr, NULL, 0))) {
|
||||
/* we may end up here when the cached address is not usable
|
||||
* anymore. In this case we reset address/cache and perform a
|
||||
* new look up
|
||||
if (!ipv6_addr_any(&fl.saddr) &&
|
||||
unlikely(!ipv6_chk_addr(sock_net(sk), &fl.saddr, NULL, 0))) {
|
||||
/* The learned local address is not usable anymore.
|
||||
* Retry with source address autoselection.
|
||||
*/
|
||||
fl.saddr = in6addr_any;
|
||||
spin_lock_bh(&peer->lock);
|
||||
bind->local.ipv6 = in6addr_any;
|
||||
spin_unlock_bh(&peer->lock);
|
||||
dst_cache_reset(cache);
|
||||
reset_local = true;
|
||||
}
|
||||
|
||||
dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl, NULL);
|
||||
|
|
@ -258,7 +352,30 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
&bind->remote.in6, ret);
|
||||
goto err;
|
||||
}
|
||||
dst_cache_set_ip6(cache, dst, &fl.saddr);
|
||||
|
||||
/* avoid storing a stale cache or local address */
|
||||
spin_lock_bh(&peer->lock);
|
||||
if (likely(ovpn_dst_cache_current(peer, bind, key))) {
|
||||
if (!reset_local) {
|
||||
dst_cache_set_ip6(cache, dst, &fl.saddr);
|
||||
spin_unlock_bh(&peer->lock);
|
||||
goto transmit;
|
||||
}
|
||||
|
||||
/* invalidate per-CPU dst entries that may still carry
|
||||
* the stale source
|
||||
*/
|
||||
dst_cache_reset(cache);
|
||||
|
||||
/* preserve the current remote */
|
||||
memcpy(&remote, &bind->remote, sizeof(struct sockaddr_in6));
|
||||
/* The current packet already has a valid wildcard-source route.
|
||||
* If replacing the bind fails, leave the stale local in place;
|
||||
* a later cache miss will retry the repair.
|
||||
*/
|
||||
ovpn_peer_reset_sockaddr(peer, &remote, &local);
|
||||
}
|
||||
spin_unlock_bh(&peer->lock);
|
||||
|
||||
transmit:
|
||||
/* user IPv6 packets may be larger than the transport interface
|
||||
|
|
@ -287,6 +404,7 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
* @cache: dst cache
|
||||
* @sk: the socket to send the packet over
|
||||
* @skb: the packet to send
|
||||
* @key: route key snapshot used for cache validation and flow lookup
|
||||
*
|
||||
* rcu_read_lock should be held on entry.
|
||||
* On return, the skb is consumed.
|
||||
|
|
@ -294,7 +412,8 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
|
|||
* Return: 0 on success or a negative error code otherwise
|
||||
*/
|
||||
static int ovpn_udp_output(struct ovpn_peer *peer, struct dst_cache *cache,
|
||||
struct sock *sk, struct sk_buff *skb)
|
||||
struct sock *sk, struct sk_buff *skb,
|
||||
struct ovpn_route_key *key)
|
||||
{
|
||||
struct ovpn_bind *bind;
|
||||
int ret;
|
||||
|
|
@ -314,11 +433,11 @@ static int ovpn_udp_output(struct ovpn_peer *peer, struct dst_cache *cache,
|
|||
|
||||
switch (bind->remote.in4.sin_family) {
|
||||
case AF_INET:
|
||||
ret = ovpn_udp4_output(peer, bind, cache, sk, skb);
|
||||
ret = ovpn_udp4_output(peer, bind, cache, sk, skb, key);
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case AF_INET6:
|
||||
ret = ovpn_udp6_output(peer, bind, cache, sk, skb);
|
||||
ret = ovpn_udp6_output(peer, bind, cache, sk, skb, key);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
|
|
@ -340,15 +459,21 @@ static int ovpn_udp_output(struct ovpn_peer *peer, struct dst_cache *cache,
|
|||
void ovpn_udp_send_skb(struct ovpn_peer *peer, struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ovpn_route_key key = {
|
||||
.mark = READ_ONCE(sk->sk_mark),
|
||||
.sport = READ_ONCE(inet_sk(sk)->inet_sport),
|
||||
};
|
||||
int ret;
|
||||
|
||||
skb->dev = peer->ovpn->dev;
|
||||
skb->mark = READ_ONCE(sk->sk_mark);
|
||||
skb->mark = key.mark;
|
||||
/* no checksum performed at this layer */
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
ovpn_dst_cache_check_key(peer, &peer->dst_cache, &key);
|
||||
|
||||
/* crypto layer -> transport (UDP) */
|
||||
ret = ovpn_udp_output(peer, &peer->dst_cache, sk, skb);
|
||||
ret = ovpn_udp_output(peer, &peer->dst_cache, sk, skb, &key);
|
||||
if (unlikely(ret < 0))
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,19 @@ ovpn_create_ns() {
|
|||
ip netns add "ovpn_peer${1}"
|
||||
}
|
||||
|
||||
ovpn_peer_vpn_addr() {
|
||||
local peer="$1"
|
||||
local file
|
||||
|
||||
if [ "${OVPN_PROTO}" == "UDP" ]; then
|
||||
file="${OVPN_UDP_PEERS_FILE}"
|
||||
else
|
||||
file="${OVPN_TCP_PEERS_FILE}"
|
||||
fi
|
||||
|
||||
awk -v peer="${peer}" '$1 == peer {print $NF; exit}' "${file}"
|
||||
}
|
||||
|
||||
ovpn_setup_ns() {
|
||||
local peer="ovpn_peer${1}"
|
||||
local server_ns="ovpn_peer0"
|
||||
|
|
|
|||
|
|
@ -650,6 +650,26 @@ static int ovpn_connect(struct ovpn_ctx *ovpn)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ovpn_nl_put_vpn_addr(struct nl_msg *msg,
|
||||
const struct ovpn_ctx *ovpn)
|
||||
{
|
||||
if (!ovpn->peer_ip_set)
|
||||
return 0;
|
||||
|
||||
switch (ovpn->peer_ip.in4.sin_family) {
|
||||
case AF_INET:
|
||||
return nla_put_u32(msg, OVPN_A_PEER_VPN_IPV4,
|
||||
ovpn->peer_ip.in4.sin_addr.s_addr);
|
||||
case AF_INET6:
|
||||
return nla_put(msg, OVPN_A_PEER_VPN_IPV6,
|
||||
sizeof(struct in6_addr),
|
||||
&ovpn->peer_ip.in6.sin6_addr);
|
||||
default:
|
||||
fprintf(stderr, "Invalid family for peer address\n");
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
}
|
||||
|
||||
static int ovpn_new_peer(struct ovpn_ctx *ovpn, bool is_tcp)
|
||||
{
|
||||
struct nlattr *attr;
|
||||
|
|
@ -691,22 +711,9 @@ static int ovpn_new_peer(struct ovpn_ctx *ovpn, bool is_tcp)
|
|||
}
|
||||
}
|
||||
|
||||
if (ovpn->peer_ip_set) {
|
||||
switch (ovpn->peer_ip.in4.sin_family) {
|
||||
case AF_INET:
|
||||
NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_VPN_IPV4,
|
||||
ovpn->peer_ip.in4.sin_addr.s_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
NLA_PUT(ctx->nl_msg, OVPN_A_PEER_VPN_IPV6,
|
||||
sizeof(struct in6_addr),
|
||||
&ovpn->peer_ip.in6.sin6_addr);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid family for peer address\n");
|
||||
goto nla_put_failure;
|
||||
}
|
||||
}
|
||||
ret = ovpn_nl_put_vpn_addr(ctx->nl_msg, ovpn);
|
||||
if (ret)
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest_end(ctx->nl_msg, attr);
|
||||
|
||||
|
|
@ -732,6 +739,10 @@ static int ovpn_set_peer(struct ovpn_ctx *ovpn)
|
|||
ovpn->keepalive_interval);
|
||||
NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_KEEPALIVE_TIMEOUT,
|
||||
ovpn->keepalive_timeout);
|
||||
|
||||
ret = ovpn_nl_put_vpn_addr(ctx->nl_msg, ovpn);
|
||||
if (ret)
|
||||
goto nla_put_failure;
|
||||
nla_nest_end(ctx->nl_msg, attr);
|
||||
|
||||
ret = ovpn_nl_msg_send(ctx, NULL);
|
||||
|
|
@ -1730,13 +1741,14 @@ static void usage(const char *cmd)
|
|||
fprintf(stderr, "\tmark: socket FW mark value\n");
|
||||
|
||||
fprintf(stderr,
|
||||
"* set_peer <iface> <peer_id> <keepalive_interval> <keepalive_timeout>: set peer attributes\n");
|
||||
"* set_peer <iface> <peer_id> <keepalive_interval> <keepalive_timeout> [vpnaddr]: set peer attributes\n");
|
||||
fprintf(stderr, "\tiface: ovpn interface name\n");
|
||||
fprintf(stderr, "\tpeer_id: peer ID of the peer to modify\n");
|
||||
fprintf(stderr,
|
||||
"\tkeepalive_interval: interval for sending ping messages\n");
|
||||
fprintf(stderr,
|
||||
"\tkeepalive_timeout: time after which a peer is timed out\n");
|
||||
fprintf(stderr, "\tvpnaddr: peer VPN IP\n");
|
||||
|
||||
fprintf(stderr, "* del_peer <iface> <peer_id>: delete peer\n");
|
||||
fprintf(stderr, "\tiface: ovpn interface name\n");
|
||||
|
|
@ -2090,6 +2102,8 @@ static int ovpn_run_cmd(struct ovpn_ctx *ovpn)
|
|||
return ret;
|
||||
|
||||
ret = ovpn_new_peer(ovpn, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ovpn_waitbg();
|
||||
break;
|
||||
case CMD_NEW_MULTI_PEER:
|
||||
|
|
@ -2331,6 +2345,12 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
|
|||
"keepalive interval value out of range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc > 6) {
|
||||
ret = ovpn_parse_remote(ovpn, NULL, NULL, argv[6]);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case CMD_DEL_PEER:
|
||||
if (argc < 4)
|
||||
|
|
|
|||
|
|
@ -56,6 +56,76 @@ ovpn_prepare_network() {
|
|||
done
|
||||
}
|
||||
|
||||
ovpn_new_test_peer() {
|
||||
local peer_id="$1"
|
||||
|
||||
shift
|
||||
ip netns exec ovpn_peer0 "${OVPN_CLI}" new_peer tun0 \
|
||||
"${peer_id}" none 65000 10.10.1.2 1 "$@"
|
||||
}
|
||||
|
||||
ovpn_set_peer_vpn_addr() {
|
||||
ip netns exec ovpn_peer0 "${OVPN_CLI}" set_peer tun0 \
|
||||
"$1" 60 120 "$2"
|
||||
}
|
||||
|
||||
ovpn_run_vpn_addr_validation() {
|
||||
local addr
|
||||
local peer1_addr4
|
||||
local test_peer_id=$((OVPN_NUM_PEERS + 1))
|
||||
local test_peer_addr6="2001:db8::2"
|
||||
# Do not include 0.0.0.0 or :: here. They are invalid on creation, but
|
||||
# clear one address family on update and are valid if the other remains.
|
||||
local -a invalid_addrs=(
|
||||
"127.0.0.1"
|
||||
"224.0.0.1"
|
||||
"255.255.255.255"
|
||||
"::1"
|
||||
"::192.0.2.1"
|
||||
"::ffff:192.0.2.1"
|
||||
"ff02::1"
|
||||
)
|
||||
|
||||
peer1_addr4=$(ovpn_peer_vpn_addr 1)
|
||||
|
||||
ovpn_cmd_fail "reject peer without VPN address" \
|
||||
ovpn_new_test_peer "${test_peer_id}"
|
||||
|
||||
for addr in "0.0.0.0" "::" "${invalid_addrs[@]}"; do
|
||||
ovpn_cmd_fail "reject new peer VPN address ${addr}" \
|
||||
ovpn_new_test_peer "${test_peer_id}" "${addr}"
|
||||
done
|
||||
|
||||
ovpn_cmd_fail "reject duplicate IPv4 address on peer creation" \
|
||||
ovpn_new_test_peer "${test_peer_id}" "${peer1_addr4}"
|
||||
ovpn_cmd_fail "reject clearing the last peer VPN address" \
|
||||
ovpn_set_peer_vpn_addr 1 0.0.0.0
|
||||
|
||||
for addr in "${invalid_addrs[@]}"; do
|
||||
ovpn_cmd_fail "reject updated peer VPN address ${addr}" \
|
||||
ovpn_set_peer_vpn_addr 1 "${addr}"
|
||||
done
|
||||
|
||||
ovpn_cmd_fail "reject duplicate IPv4 address on peer update" \
|
||||
ovpn_set_peer_vpn_addr 2 "${peer1_addr4}"
|
||||
|
||||
ovpn_cmd_ok "add peer IPv6 address" \
|
||||
ovpn_set_peer_vpn_addr 1 "${test_peer_addr6}"
|
||||
ovpn_cmd_fail "reject duplicate IPv6 address on peer creation" \
|
||||
ovpn_new_test_peer "${test_peer_id}" "${test_peer_addr6}"
|
||||
ovpn_cmd_fail "reject duplicate IPv6 address on peer update" \
|
||||
ovpn_set_peer_vpn_addr 2 "${test_peer_addr6}"
|
||||
|
||||
ovpn_cmd_ok "clear peer IPv4 address" \
|
||||
ovpn_set_peer_vpn_addr 1 0.0.0.0
|
||||
ovpn_cmd_fail "reject clearing the remaining peer IPv6 address" \
|
||||
ovpn_set_peer_vpn_addr 1 ::
|
||||
ovpn_cmd_ok "restore peer IPv4 address" \
|
||||
ovpn_set_peer_vpn_addr 1 "${peer1_addr4}"
|
||||
ovpn_cmd_ok "clear peer IPv6 address" \
|
||||
ovpn_set_peer_vpn_addr 1 ::
|
||||
}
|
||||
|
||||
ovpn_run_basic_traffic() {
|
||||
local p
|
||||
local header1
|
||||
|
|
@ -293,15 +363,16 @@ trap ovpn_stage_err ERR
|
|||
|
||||
ktap_print_header
|
||||
if [ "${OVPN_FLOAT}" == "1" ]; then
|
||||
ktap_set_plan 13
|
||||
ktap_set_plan 14
|
||||
else
|
||||
ktap_set_plan 12
|
||||
ktap_set_plan 13
|
||||
fi
|
||||
|
||||
ovpn_cleanup
|
||||
modprobe -q ovpn || true
|
||||
|
||||
ovpn_run_stage "setup network topology" ovpn_prepare_network
|
||||
ovpn_run_stage "validate peer VPN addresses" ovpn_run_vpn_addr_validation
|
||||
ovpn_run_stage "run baseline data traffic" ovpn_run_basic_traffic
|
||||
ovpn_run_stage "run LAN traffic behind peer1" ovpn_run_lan_traffic
|
||||
[ "${OVPN_FLOAT}" == "1" ] && ovpn_run_stage "run floating peer checks" \
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user