From 862e346ac91b3eee7ef94a8d71632d7c17bf2c35 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 18 Dec 2020 00:03:12 +0100 Subject: [PATCH] mptcp: support MPJoin with IPv4 mapped in v6 sk With an IPv4 mapped in v6 socket, we were trying to call inet6_bind() with an IPv4 address resulting in a -EINVAL error because the given addr_len -- size of the address structure -- was too short. We now make sure to use address structures for the same family as the MPTCP socket for both the bind() and the connect(). It means we convert v4 addresses to v4 mapped in v6 or the opposite if needed. Fixes: ec3edaa7ca6c ("mptcp: Add handling of outgoing MP_JOIN requests") Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/122 Reviewed-by: Mat Martineau Co-developed-by: Geliang Tang Signed-off-by: Geliang Tang Signed-off-by: Matthieu Baerts --- net/mptcp/subflow.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 31cc362a46384..7082747b4c0da 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -1081,21 +1081,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 @@ -1139,11 +1149,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; @@ -1159,7 +1169,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);