Commit fd0e4ec4 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'mptcp-ipv4-mapped-ipv6-addressing-for-subflows'

Mat Martineau says:

====================
MPTCP: IPv4-mapped IPv6 addressing for subflows

This patch series from the MPTCP tree adds support for IPv4-mapped IPv6
addressing that was missing when multiple subflows were first
implemented.

Patches 1 and 2 handle the conversion and comparison of the mapped
addresses.

Patch 3 contains a minor refactor in the path manager's handling of
addresses.

Patches 4 and 5 add selftests for the new functionality and adjust the
selftest timeout.
====================

Link: https://lore.kernel.org/r/20210125185904.6997-1-mathew.j.martineau@linux.intel.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 17ce76c4 9c2cadef
......@@ -60,15 +60,20 @@ static bool addresses_equal(const struct mptcp_addr_info *a,
{
bool addr_equals = false;
if (a->family != b->family)
return false;
if (a->family == AF_INET)
addr_equals = a->addr.s_addr == b->addr.s_addr;
if (a->family == b->family) {
if (a->family == AF_INET)
addr_equals = a->addr.s_addr == b->addr.s_addr;
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
else
addr_equals = !ipv6_addr_cmp(&a->addr6, &b->addr6);
else
addr_equals = !ipv6_addr_cmp(&a->addr6, &b->addr6);
} else if (a->family == AF_INET) {
if (ipv6_addr_v4mapped(&b->addr6))
addr_equals = a->addr.s_addr == b->addr6.s6_addr32[3];
} else if (b->family == AF_INET) {
if (ipv6_addr_v4mapped(&a->addr6))
addr_equals = a->addr6.s6_addr32[3] == b->addr.s_addr;
#endif
}
if (!addr_equals)
return false;
......@@ -137,6 +142,7 @@ select_local_address(const struct pm_nl_pernet *pernet,
struct mptcp_sock *msk)
{
struct mptcp_pm_addr_entry *entry, *ret = NULL;
struct sock *sk = (struct sock *)msk;
rcu_read_lock();
__mptcp_flush_join_list(msk);
......@@ -144,11 +150,20 @@ select_local_address(const struct pm_nl_pernet *pernet,
if (!(entry->addr.flags & MPTCP_PM_ADDR_FLAG_SUBFLOW))
continue;
if (entry->addr.family != sk->sk_family) {
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
if ((entry->addr.family == AF_INET &&
!ipv6_addr_v4mapped(&sk->sk_v6_daddr)) ||
(sk->sk_family == AF_INET &&
!ipv6_addr_v4mapped(&entry->addr.addr6)))
#endif
continue;
}
/* avoid any address already in use by subflows and
* pending join
*/
if (entry->addr.family == ((struct sock *)msk)->sk_family &&
!lookup_subflow_by_saddr(&msk->conn_list, &entry->addr)) {
if (!lookup_subflow_by_saddr(&msk->conn_list, &entry->addr)) {
ret = entry;
break;
}
......@@ -310,7 +325,6 @@ void mptcp_pm_free_anno_list(struct mptcp_sock *msk)
static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk)
{
struct mptcp_addr_info remote = { 0 };
struct sock *sk = (struct sock *)msk;
struct mptcp_pm_addr_entry *local;
struct pm_nl_pernet *pernet;
......@@ -344,13 +358,14 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk)
/* check if should create a new subflow */
if (msk->pm.local_addr_used < msk->pm.local_addr_max &&
msk->pm.subflows < msk->pm.subflows_max) {
remote_address((struct sock_common *)sk, &remote);
local = select_local_address(pernet, msk);
if (local) {
struct mptcp_addr_info remote = { 0 };
msk->pm.local_addr_used++;
msk->pm.subflows++;
check_work_pending(msk);
remote_address((struct sock_common *)sk, &remote);
spin_unlock_bh(&msk->pm.lock);
__mptcp_subflow_connect(sk, &local->addr, &remote);
spin_lock_bh(&msk->pm.lock);
......
......@@ -1085,21 +1085,31 @@ void mptcpv6_handle_mapped(struct sock *sk, bool mapped)
#endif
static void mptcp_info2sockaddr(const struct mptcp_addr_info *info,
struct sockaddr_storage *addr)
struct sockaddr_storage *addr,
unsigned short family)
{
memset(addr, 0, sizeof(*addr));
addr->ss_family = info->family;
addr->ss_family = family;
if (addr->ss_family == AF_INET) {
struct sockaddr_in *in_addr = (struct sockaddr_in *)addr;
in_addr->sin_addr = info->addr;
if (info->family == AF_INET)
in_addr->sin_addr = info->addr;
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
else if (ipv6_addr_v4mapped(&info->addr6))
in_addr->sin_addr.s_addr = info->addr6.s6_addr32[3];
#endif
in_addr->sin_port = info->port;
}
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
else if (addr->ss_family == AF_INET6) {
struct sockaddr_in6 *in6_addr = (struct sockaddr_in6 *)addr;
in6_addr->sin6_addr = info->addr6;
if (info->family == AF_INET)
ipv6_addr_set_v4mapped(info->addr.s_addr,
&in6_addr->sin6_addr);
else
in6_addr->sin6_addr = info->addr6;
in6_addr->sin6_port = info->port;
}
#endif
......@@ -1143,11 +1153,11 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
subflow->remote_key = msk->remote_key;
subflow->local_key = msk->local_key;
subflow->token = msk->token;
mptcp_info2sockaddr(loc, &addr);
mptcp_info2sockaddr(loc, &addr, ssk->sk_family);
addrlen = sizeof(struct sockaddr_in);
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
if (loc->family == AF_INET6)
if (addr.ss_family == AF_INET6)
addrlen = sizeof(struct sockaddr_in6);
#endif
ssk->sk_bound_dev_if = loc->ifindex;
......@@ -1163,7 +1173,7 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
subflow->remote_id = remote_id;
subflow->request_join = 1;
subflow->request_bkup = !!(loc->flags & MPTCP_PM_ADDR_FLAG_BACKUP);
mptcp_info2sockaddr(remote, &addr);
mptcp_info2sockaddr(remote, &addr, ssk->sk_family);
mptcp_add_pending_subflow(msk, subflow);
err = kernel_connect(sf, (struct sockaddr *)&addr, addrlen, O_NONBLOCK);
......
......@@ -790,6 +790,81 @@ chk_join_nr "remove subflow and signal IPv6" 2 2 2
chk_add_nr 1 1
chk_rm_nr 1 1
# subflow IPv4-mapped to IPv4-mapped
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl add "::ffff:10.0.3.2" flags subflow
run_tests $ns1 $ns2 "::ffff:10.0.1.1"
chk_join_nr "single subflow IPv4-mapped" 1 1 1
# signal address IPv4-mapped with IPv4-mapped sk
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl limits 1 1
ip netns exec $ns1 ./pm_nl_ctl add "::ffff:10.0.2.1" flags signal
run_tests $ns1 $ns2 "::ffff:10.0.1.1"
chk_join_nr "signal address IPv4-mapped" 1 1 1
chk_add_nr 1 1
# subflow v4-map-v6
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
run_tests $ns1 $ns2 "::ffff:10.0.1.1"
chk_join_nr "single subflow v4-map-v6" 1 1 1
# signal address v4-map-v6
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl limits 1 1
ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal
run_tests $ns1 $ns2 "::ffff:10.0.1.1"
chk_join_nr "signal address v4-map-v6" 1 1 1
chk_add_nr 1 1
# subflow v6-map-v4
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl add "::ffff:10.0.3.2" flags subflow
run_tests $ns1 $ns2 10.0.1.1
chk_join_nr "single subflow v6-map-v4" 1 1 1
# signal address v6-map-v4
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl limits 1 1
ip netns exec $ns1 ./pm_nl_ctl add "::ffff:10.0.2.1" flags signal
run_tests $ns1 $ns2 10.0.1.1
chk_join_nr "signal address v6-map-v4" 1 1 1
chk_add_nr 1 1
# no subflow IPv6 to v4 address
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl add dead:beef:2::2 flags subflow
run_tests $ns1 $ns2 10.0.1.1
chk_join_nr "no JOIN with diff families v4-v6" 0 0 0
# no subflow IPv6 to v4 address even if v6 has a valid v4 at the end
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl add dead:beef:2::10.0.3.2 flags subflow
run_tests $ns1 $ns2 10.0.1.1
chk_join_nr "no JOIN with diff families v4-v6-2" 0 0 0
# no subflow IPv4 to v6 address, no need to slow down too then
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl limits 0 1
ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
run_tests $ns1 $ns2 dead:beef:1::1
chk_join_nr "no JOIN with diff families v6-v4" 0 0 0
# single subflow, backup
reset
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment