@@ -271,15 +271,17 @@ static int ovpn_nl_peer_precheck(struct ovpn_priv *ovpn,
* @peer: the peer to modify
* @info: generic netlink info from the user request
* @attrs: the attributes from the user request
+ * @remote: remote address, if provided
*
* Return: a negative error code in case of failure, 0 on success or 1 on
* success and the VPN IPs have been modified (requires rehashing in MP
* mode)
*/
static int ovpn_nl_peer_modify(struct ovpn_peer *peer, struct genl_info *info,
- struct nlattr **attrs)
+ struct nlattr **attrs,
+ const struct sockaddr_storage *remote)
{
- struct sockaddr_storage ss = {};
+ struct sockaddr_storage empty_remote = {};
void *local_ip = NULL;
u32 interv, timeout;
bool rehash = false;
@@ -287,15 +289,15 @@ static int ovpn_nl_peer_modify(struct ovpn_peer *peer, struct genl_info *info,
spin_lock_bh(&peer->lock);
- if (ovpn_nl_attr_sockaddr_remote(attrs, &ss)) {
+ if (remote) {
/* we carry the local IP in a generic container.
* ovpn_peer_reset_sockaddr() will properly interpret it
- * based on ss.ss_family
+ * based on remote->ss_family
*/
local_ip = ovpn_nl_attr_local_ip(attrs);
/* set peer sockaddr */
- ret = ovpn_peer_reset_sockaddr(peer, &ss, local_ip);
+ ret = ovpn_peer_reset_sockaddr(peer, remote, local_ip);
if (ret < 0) {
NL_SET_ERR_MSG_FMT_MOD(info->extack,
"cannot set peer sockaddr: %d",
@@ -333,7 +335,7 @@ static int ovpn_nl_peer_modify(struct ovpn_peer *peer, struct genl_info *info,
netdev_dbg(peer->ovpn->dev,
"modify peer id=%u tx_id=%u endpoint=%pIScp VPN-IPv4=%pI4 VPN-IPv6=%pI6c\n",
- peer->id, peer->tx_id, &ss,
+ peer->id, peer->tx_id, remote ?: &empty_remote,
&peer->vpn_addrs.ipv4.s_addr, &peer->vpn_addrs.ipv6);
spin_unlock_bh(&peer->lock);
@@ -344,10 +346,43 @@ static int ovpn_nl_peer_modify(struct ovpn_peer *peer, struct genl_info *info,
return ret;
}
+/**
+ * ovpn_nl_udp_remote_compatible - check if a UDP socket can use a remote
+ * @sk: UDP socket to validate
+ * @remote: remote endpoint to validate against the socket
+ * @extack: netlink extended ACK for reporting validation errors
+ *
+ * Return: 0 if the remote endpoint is compatible with the socket or a negative
+ * error code otherwise.
+ */
+static int ovpn_nl_udp_remote_compatible(const struct sock *sk,
+ const struct sockaddr_storage *remote,
+ struct netlink_ext_ack *extack)
+{
+ const unsigned short sock_family = READ_ONCE(sk->sk_family);
+
+ if (sock_family == AF_INET && remote->ss_family == AF_INET6) {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "UDP socket is IPv4 but remote is IPv6");
+ return -EAFNOSUPPORT;
+ }
+
+ if (sock_family == AF_INET6 && ipv6_only_sock(sk) &&
+ remote->ss_family == AF_INET) {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "UDP socket is IPv6-only but remote is IPv4");
+ return -EAFNOSUPPORT;
+ }
+
+ return 0;
+}
+
int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
{
- struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
+ const struct sockaddr_storage *remote = NULL;
struct ovpn_priv *ovpn = info->user_ptr[0];
+ struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
+ struct sockaddr_storage ss = {};
struct ovpn_socket *ovpn_sock;
struct socket *sock = NULL;
struct ovpn_peer *peer;
@@ -411,26 +446,34 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
goto peer_release;
}
- /* Only when using UDP as transport protocol the remote endpoint
- * can be configured so that ovpn knows where to send packets to.
- */
- if (sk_is_udp(sock->sk) &&
- !attrs[OVPN_A_PEER_REMOTE_IPV4] &&
- !attrs[OVPN_A_PEER_REMOTE_IPV6]) {
- NL_SET_ERR_MSG_FMT_MOD(info->extack,
- "missing remote IP address for UDP socket");
- sockfd_put(sock);
- ret = -EINVAL;
- goto peer_release;
- }
+ if (ovpn_nl_attr_sockaddr_remote(attrs, &ss))
+ remote = &ss;
- /* In case of TCP, the socket is connected to the peer and ovpn
- * will just send bytes over it, without the need to specify a
- * destination.
- */
- if (sk_is_tcp(sock->sk) &&
- (attrs[OVPN_A_PEER_REMOTE_IPV4] ||
- attrs[OVPN_A_PEER_REMOTE_IPV6])) {
+ if (sk_is_udp(sock->sk)) {
+ /* Only when using UDP as transport protocol the remote
+ * endpoint can be configured so that ovpn knows where to send
+ * packets to.
+ */
+ if (!remote) {
+ NL_SET_ERR_MSG_FMT_MOD(info->extack,
+ "missing remote IP address for UDP socket");
+ sockfd_put(sock);
+ ret = -EINVAL;
+ goto peer_release;
+ }
+
+ /* can the socket be used with this remote? */
+ ret = ovpn_nl_udp_remote_compatible(sock->sk, remote,
+ info->extack);
+ if (ret < 0) {
+ sockfd_put(sock);
+ goto peer_release;
+ }
+ } else if (remote) {
+ /* In case of TCP, the socket is connected to the peer and ovpn
+ * will just send bytes over it, without the need to specify a
+ * destination.
+ */
NL_SET_ERR_MSG_FMT_MOD(info->extack,
"unexpected remote IP address with TCP socket");
sockfd_put(sock);
@@ -456,7 +499,7 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
rcu_assign_pointer(peer->sock, ovpn_sock);
- ret = ovpn_nl_peer_modify(peer, info, attrs);
+ ret = ovpn_nl_peer_modify(peer, info, attrs, remote);
if (ret < 0)
goto sock_release;
@@ -483,8 +526,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];
+ const struct sockaddr_storage *remote = NULL;
struct ovpn_priv *ovpn = info->user_ptr[0];
+ struct nlattr *attrs[OVPN_A_PEER_MAX + 1];
+ struct sockaddr_storage ss = {};
struct ovpn_socket *sock;
struct ovpn_peer *peer;
u32 peer_id;
@@ -516,22 +561,33 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info)
return -ENOENT;
}
- /* when using a TCP socket the remote IP is not expected */
+ if (ovpn_nl_attr_sockaddr_remote(attrs, &ss))
+ remote = &ss;
+
rcu_read_lock();
sock = rcu_dereference(peer->sock);
- if (sock && sock->sk->sk_protocol == IPPROTO_TCP &&
- (attrs[OVPN_A_PEER_REMOTE_IPV4] ||
- attrs[OVPN_A_PEER_REMOTE_IPV6])) {
- rcu_read_unlock();
- NL_SET_ERR_MSG_FMT_MOD(info->extack,
- "unexpected remote IP address with TCP socket");
- ovpn_peer_put(peer);
- return -EINVAL;
+ if (sock && remote) {
+ if (sk_is_udp(sock->sk)) {
+ ret = ovpn_nl_udp_remote_compatible(sock->sk, remote,
+ info->extack);
+ if (ret < 0) {
+ rcu_read_unlock();
+ ovpn_peer_put(peer);
+ return ret;
+ }
+ } else if (sk_is_tcp(sock->sk)) {
+ /* when using a TCP socket remote IP is not expected */
+ NL_SET_ERR_MSG_FMT_MOD(info->extack,
+ "unexpected remote IP address with TCP socket");
+ rcu_read_unlock();
+ ovpn_peer_put(peer);
+ return -EINVAL;
+ }
}
rcu_read_unlock();
spin_lock_bh(&ovpn->lock);
- ret = ovpn_nl_peer_modify(peer, info, attrs);
+ ret = ovpn_nl_peer_modify(peer, info, attrs, remote);
if (ret < 0) {
spin_unlock_bh(&ovpn->lock);
ovpn_peer_put(peer);