ipv6: IPv4-mapped on wire, :: dst address issue
Subsystems (5)
cover ipv6: IPv4-mapped on wire, :: dst address issue
Under some circumstances IPv6 datagrams are sent with IPv4-mapped IPv6 addresses as the source. Given an IPv6 socket bound to an IPv4-mapped IPv6 address, and an IPv6 destination address, both TCP and UDP will will send packets using the IPv4-mapped IPv6 address as the source. Per RFC 6890 (Table 20), IPv4-mapped IPv6 source addresses are not allowed in an IP datagram. The problem can be observed by attempting to connect() either a TCP or UDP socket, or by using sendmsg() with a UDP socket. The patch is intended to correct this issue for all socket types.
linux follows the BSD convention that an IPv6 destination address specified as in6addr_any is converted to the loopback address. Currently, neither TCP nor UDP consider the possibility that the source address is an IPv4-mapped IPv6 address, and assume that the appropriate loopback address is ::1. The patch adds a check on whether or not the source address is an IPv4-mapped IPv6 address and then sets the destination address to either ::ffff:127.0.0.1 or ::1, as appropriate.
Jon
Jonathan T. Leighton (2):
ipv6: Inhibit IPv4-mapped src address on the wire.
ipv6: Handle IPv4-mapped src to in6addr_any dst.
net/ipv6/datagram.c | 14 +++++++++----- net/ipv6/ip6_output.c | 3 +++ net/ipv6/tcp_ipv6.c | 11 ++++++++--- net/ipv6/udp.c | 4 ++++ 4 files changed, 24 insertions(+), 8 deletions(-)
Signature · 1 line
2.7.4
RFC PATCH 1/2 ipv6: Inhibit IPv4-mapped src address on the wire.
This patch adds a check for the problematic case of an IPv4-mapped IPv6 source address and a destination address that is neither an IPv4-mapped IPv6 address nor in6addr_any, and returns an appropriate error. The check in done before returning from looking up the route.
--- net/ipv6/ip6_output.c | 3 +++ 1 file changed, 3 insertions(+)
net/ipv6/ip6_output.c +3 -0
@@ -1021,6 +1021,9 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk, } }#endif +if (ipv6_addr_v4mapped (&fl6->saddr) && + !(ipv6_addr_v4mapped (&fl6->daddr) ||ipv6_addr_any (&fl6->daddr))) +return -EAFNOSUPPORT ;return 0 ;
Signature · 1 line
2.7.4
RFC PATCH 2/2 ipv6: Handle IPv4-mapped src to in6addr_any dst.
In reply to Jonathan T. LeightonThis 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.
--- net/ipv6/datagram.c | 14 +++++++++----- net/ipv6/tcp_ipv6.c | 11 ++++++++--- net/ipv6/udp.c | 4 ++++ 3 files changed, 21 insertions(+), 8 deletions(-)
net/ipv6/datagram.c +9 -5
@@ -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 (ntohl (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)) {
net/ipv6/tcp_ipv6.c +8 -3
@@ -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 (ntohl (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;
net/ipv6/udp.c +4 -0
@@ -1033,6 +1033,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 (ntohl (INADDR_LOOPBACK ), + daddr);break ;case AF_INET : goto do_udp_sendmsg;
Signature · 1 line
2.7.4