Commit fd0e4ec4 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

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.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 17ce76c4 9c2cadef
Loading
Loading
Loading
Loading
+27 −12
Original line number Diff line number Diff line
@@ -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 == 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 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);
+17 −7
Original line number Diff line number Diff line
@@ -1085,20 +1085,30 @@ 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;

		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;

		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;
	}
@@ -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);
+75 −0
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
timeout=450
timeout=600