This patch adds a check on the type of the source address for the case
where the destination address is in6addr_any. If the source is an
IPv4-mapped IPv6 source address, the destination is changed to
::ffff:127.0.0.1, and otherwise the destination is changed to ::1. This
is done in three locations to handle UDP calls to either connect() or
sendmsg() and TCP calls to connect(). Note that udpv6_sendmsg() delays
handling an in6addr_any destination until very late, so the patch only
needs to handle the case where the source is an IPv4-mapped IPv6
address.

Signed-off-by: Jonathan T. Leighton <jtlei...@udel.edu>
---
 net/ipv6/datagram.c | 14 +++++++++-----
 net/ipv6/tcp_ipv6.c | 11 ++++++++---
 net/ipv6/udp.c      |  4 ++++
 3 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index a3eaafd..eec27f8 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -167,18 +167,22 @@ int __ip6_datagram_connect(struct sock *sk, struct 
sockaddr *uaddr,
        if (np->sndflow)
                fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK;
 
-       addr_type = ipv6_addr_type(&usin->sin6_addr);
-
-       if (addr_type == IPV6_ADDR_ANY) {
+       if (ipv6_addr_any(&usin->sin6_addr)) {
                /*
                 *      connect to self
                 */
-               usin->sin6_addr.s6_addr[15] = 0x01;
+               if (ipv6_addr_v4mapped(&sk->sk_v6_rcv_saddr))
+                       ipv6_addr_set_v4mapped(htonl(INADDR_LOOPBACK),
+                                              &usin->sin6_addr);
+               else
+                       usin->sin6_addr = in6addr_loopback;
        }
 
+       addr_type = ipv6_addr_type(&usin->sin6_addr);
+
        daddr = &usin->sin6_addr;
 
-       if (addr_type == IPV6_ADDR_MAPPED) {
+       if (addr_type & IPV6_ADDR_MAPPED) {
                struct sockaddr_in sin;
 
                if (__ipv6_only_sock(sk)) {
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index b5d2721..21c7199 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -149,8 +149,13 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr 
*uaddr,
         *      connect() to INADDR_ANY means loopback (BSD'ism).
         */
 
-       if (ipv6_addr_any(&usin->sin6_addr))
-               usin->sin6_addr.s6_addr[15] = 0x1;
+       if (ipv6_addr_any(&usin->sin6_addr)) {
+               if (ipv6_addr_v4mapped(&sk->sk_v6_rcv_saddr))
+                       ipv6_addr_set_v4mapped(htonl(INADDR_LOOPBACK),
+                                              &usin->sin6_addr);
+               else
+                       usin->sin6_addr = in6addr_loopback;
+       }
 
        addr_type = ipv6_addr_type(&usin->sin6_addr);
 
@@ -189,7 +194,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr 
*uaddr,
         *      TCP over IPv4
         */
 
-       if (addr_type == IPV6_ADDR_MAPPED) {
+       if (addr_type & IPV6_ADDR_MAPPED) {
                u32 exthdrlen = icsk->icsk_ext_hdr_len;
                struct sockaddr_in sin;
 
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index df71ba0..4e4c401 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1046,6 +1046,10 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, 
size_t len)
                        if (addr_len < SIN6_LEN_RFC2133)
                                return -EINVAL;
                        daddr = &sin6->sin6_addr;
+                       if (ipv6_addr_any(daddr) &&
+                           ipv6_addr_v4mapped(&np->saddr))
+                               ipv6_addr_set_v4mapped(htonl(INADDR_LOOPBACK),
+                                                      daddr);
                        break;
                case AF_INET:
                        goto do_udp_sendmsg;
-- 
2.7.4

Reply via email to