mptcp: pm nl: support IPv4 mapped in v6 addresses
authorMatthieu Baerts <matthieu.baerts@tessares.net>
Mon, 25 Jan 2021 18:59:01 +0000 (10:59 -0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 28 Jan 2021 00:53:52 +0000 (16:53 -0800)
On one side, we can allow the creation of subflows between v4 mapped in
v6 and v4 addresses. For that we look for v4mapped addresses between the
local address we want to select and the remote one.

On the other side, we also properly deal with received v4mapped
addresses, either announced ones or set via Netlink.

Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/122
Suggested-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Co-developed-by: Geliang Tang <geliangtang@gmail.com>
Signed-off-by: Geliang Tang <geliangtang@gmail.com>
Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/mptcp/pm_netlink.c

index 9b1f6298bbdba9dbaca0dbbe7b4064a5af580e07..f0afff6ba015fc65312084b7d00f7fabf94a3671 100644 (file)
@@ -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;
                }