Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: Resolve{TCP,UDP,IP}Addr returns IPv4 address on host without IPv4 connectivity #28666

Open
bluecmd opened this issue Nov 8, 2018 · 9 comments
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@bluecmd
Copy link

bluecmd commented Nov 8, 2018

What version of Go are you using (go version)?

$ go version
go version go1.11rc2 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

Linux. The issue doesn't depend on CPU arch and has been reproduced on
ARMv5 and x86-64.

What did you do?

A minimal repro is this program:

package main

import (
	"log"
	"net"
)

func main() {
	r, _ := net.ResolveUDPAddr("udp", "ping.sunet.se:0") // IPv4 and IPv6 records
	log.Printf("Result of ResolveUDPAddr: %v", r)

	r2, _ := net.ResolveIPAddr("ip", "ping.sunet.se") // IPv4 and IPv6 records
	log.Printf("Result of ResolveIPAddr: %v", r2)

	r3, _ := net.ResolveIPAddr("ip", "ipv6.sunet.se") // Only IPv6 record
	log.Printf("Result of ResolveIPAddr: %v", r3)
}

This shows the behavior of ResolveUDPAddr and ResolveIPAddr with dual-stacked
hosts and a IPv6 only host as the last entry. This also applies to ResolveTCPAddr.

What did you expect to see?

On hosts that have an IPv6 and an IPv4 address, the IPv6 address is preferred
on hosts that has IPv6 only connectivity.

In the program below, the output should be something like this on an IPv6-only system:

[bluecmd]$ ./ipv6_dial
2018/11/08 16:22:56 Result of ResolveUDPAddr: 2001:6b0:7::14
2018/11/08 16:22:56 Result of ResolveIPAddr: 2001:6b0:7::14
2018/11/08 16:22:56 Result of ResolveIPAddr: 2001:6b0:7::14

What did you see instead?

This is the outcome on an IPv6 only system:

[bluecmd]$ sudo ip ro
[bluecmd]$ sudo ip -6 ro
[..]
default via fe80::200:5eff:fe00:265 dev eth0 proto ra metric 100 pref medium

Yet ResolveIPAddr prefers IPv4:

[bluecmd]$ ./ipv6_dial
2018/11/08 16:22:56 Result of ResolveUDPAddr: 192.36.125.18:0
2018/11/08 16:22:56 Result of ResolveIPAddr: 192.36.125.18
2018/11/08 16:22:56 Result of ResolveIPAddr: 2001:6b0:7::14

This is also not dependent on CGO.

For more logs, see here.

Extra information

Problematic code is most likely this part of ipsock.go:

go/src/net/ipsock.go

Lines 73 to 98 in 7da1f7a

func (addrs addrList) forResolve(network, addr string) Addr {
var want6 bool
switch network {
case "ip":
// IPv6 literal (addr does NOT contain a port)
want6 = count(addr, ':') > 0
case "tcp", "udp":
// IPv6 literal. (addr contains a port, so look for '[')
want6 = count(addr, '[') > 0
}
if want6 {
return addrs.first(isNotIPv4)
}
return addrs.first(isIPv4)
}
// first returns the first address which satisfies strategy, or if
// none do, then the first address of any kind.
func (addrs addrList) first(strategy func(Addr) bool) Addr {
for _, addr := range addrs {
if strategy(addr) {
return addr
}
}
return addrs[0]
}

My reading is that the code returns IPv4 unless IPv6 was explicitly asked for.
If the host is IPv6 only, then that host is picked on the fallback return in first().

I would suggest changing the code to not prefer IPv4 addresses if there is no IPv4 connectivity.

@ianlancetaylor ianlancetaylor added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Nov 9, 2018
@ianlancetaylor ianlancetaylor added this to the Go1.13 milestone Nov 9, 2018
@ianlancetaylor
Copy link
Contributor

CC @mikioh @iangudger

@mchtech
Copy link

mchtech commented Jan 10, 2019

Why not prefer to return IPv6 address?

@vlcty
Copy link

vlcty commented Oct 11, 2020

I'm experiencing the same issue. Any updates on this? I mean IPv6 is the current gen protocol.

@saudiqbal
Copy link

Same problem with Wireguard with dual stack hostname. IPv6 should be preferred over IPv4, hope it should be fixed soon.

https://www.reddit.com/r/WireGuard/comments/e05ef1/hostname_in_endpoint_wireguard_uses_ipv4_instead/f8d5fea/

@vlcty
Copy link

vlcty commented Nov 26, 2020

What needs to be done to re-prioritize this issue? @ianlancetaylor @andybons @rsc

@ianlancetaylor
Copy link
Contributor

@vlcty Someone needs to look at the code and send a fix. I don't personally know offhand how to fix this.

@stapelberg
Copy link
Contributor

AFAICT, Go currently knows about the IP stack’s capabilities (supports IPv4/IPv6 in principle), but not about the actual connectivity (an IPv4/IPv6 address is configured).

In i3status, we determine IPv6 connectivity by creating a UDP socket to a global IPv6 address: https://github.com/i3/i3status/blob/3f27399d730bb9a66bebfed6aff2660828687ca5/src/print_ipv6_addr.c#L82-L93

As UDP sockets are connectionless, this approach does not send anything onto the network.

So, my suggestion for a fix would be to check for IPv6 connectivity, and short-circuit the IPv4 preference in ipsock.go that @bluecmd mentioned.

If we wanted to play it safer, instead of checking for IPv6 connectivity, we could check if IPv4 connectivity is missing.


By the way, RFC 6724 (Default Address Selection for Internet Protocol Version 6 (IPv6)) covers this topic in a great amount of detail if anyone wants to read up more. We’re running into this problem mentioned in the RFC’s introduction section:

Furthermore, dual- or hybrid-stack implementations, which support
both IPv6 and IPv4, will very often need to choose between IPv6 and
IPv4 when initiating communication, for example, when DNS name
resolution yields both IPv6 and IPv4 addresses and the network
protocol stack has available both IPv6 and IPv4 source addresses. In
such cases, a simple policy to always prefer IPv6 or always prefer
IPv4 can produce poor behavior.

@miyurusankalpa
Copy link

The commit for change that added the code is here a5179bd and it looks like the author had IPv6 disabled in the kernel as per the comments in the issue linked with it.

The culprit for this issue is in the return function.

return addrs.first(isIPv4)

I think it should not return a list IPv4 address by default. It should return the address list as is.

This also looks like the same issue as nodejs/node#6307

@DaveB91
Copy link

DaveB91 commented Jun 23, 2022

When will WG prefer IPv6 over IPv4 for the peer endpoint? This hurts as mobile operators move to single-stack IPv6 and 6to4 gateways.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

10 participants