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: Listen is unfriendly to multiple address families, endpoints and subflows #9334
Comments
/cc @mikioh |
The correct way to listen for both tcp4 and tcp6 is to leave out the Will listen on tcp6 if it's dual-stack system. I don't think net.Listen should automatically create multiple listener for For example, what if the machine added another network interface and another |
That's not entirely accurate. There do exist dual-stack systems which don't support dual-stack sockets, so AF_INET and AF_INET6 must be kept separate. In that case,
If |
What should happen if more IPs are added to a hostname? Should No matter what we do, I think the behavior will surprise some users. Also, what do you think should the Listener's Addr() method return IMHO, the existing documentation actually precludes listening on I always think supporting hostname in Listen is just a convenience, |
I don't have a good answer, and that's the fundamental flaw I was referring to. Since there's nothing sane for
Perhaps have a type that stores a resolved AddressSet, and another type that holds the listening sockets, where you can Read the AddressSet (to see which sockets exist) or Write it (to open new sockets and close obsolete ones). Then sufficiently-crazy users could resolve multiple hostnames, merge the results together, and dynamically update the pool of sockets without interrupting existing listeners. But I don't care strongly about the dynamic stuff; I just think listening on localhost or ::+0.0.0.0 should be easy. The problem is that net.Listen() is easy, popular, and wrong. The alternative (keeping track of multiple listening sockets, with dual-stack behavior that varies by OS) is so much more involved that people avoid doing the right thing, and IPv6 compatibility suffers as a result. |
I wouldn't call the current net.Listen wrong. It just can't handle every See also #8124, which has more in-depth discussion of the prefer-IPv4 issue. |
I encountered the same question when working on a C networking library, and the solution was to funnel the IPV6_V6ONLY calls through a function with a test-only flag that emulates an OS without dualstack sockets: /* Returns 1 on success (dual-stack), 0 on failure (single-stack). */
static int set_socket_dualstack(int fd) {
if (!forbid_dualstack_sockets_for_testing) {
const int off = 0;
return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
} else {
/* Force an IPv6-only socket, for testing purposes. */
const int on = 1;
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
return 0;
}
} Granted, a flag is a bit unsightly, but it's an easy way to ensure coverage of the
I don't agree with that defeatist position; When a standard library exposes hostname-based socket interfaces, it's the library's responsibility to follow best practices, instead of taking shortcuts. "one hostname ⇒ one socket" is a shortcut that's not obvious to users of the library, but which seems harmful to the networking ecosystem. Pretending that no code had yet been written, I don't think a |
We perhaps need to support this eventually. In that case the new stuff should support not only for dual-stack TCP listeners but for SCTP, MPTCP listeners. Random thoughts on API:
Random thoughts on testing:
Random thoughts on roadmap:
References: PS: Moreover, I'd want to see what happens with the IP Stack Evolution Program: http://www.ietf.org/proceedings/91/slides/slides-91-iab-techplenary-6.pdf |
Is this related issue? #7598 |
No, #7598 is a simple, Windows-specific investigation; how Windows dual IP stacks behave when we use it. |
CL https://golang.org/cl/31931 mentions this issue. |
In general, these functions cannot behave correctly when given a hostname, because a hostname may represent multiple IP addresses, and first(isIPv4) chooses at most one. Updates #9334 Change-Id: Icfb629f84af4d976476385a3071270253c0000b1 Reviewed-on: https://go-review.googlesource.com/31931 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
As noted [here](golang/go#9334 (comment)), the "correct way" to listen on both IPv4 and IPv6 loopback interfaces is to omit the address part. Closes: #2 And will also close: wincent/vim-clipper#1
Today, net.Listen will only listen on 127.0.0.1 if localhost is passed [0]. Listening on `:8080` will open a dualstack socket on OSs that support it. [0] golang/go#9334
A counterpoint to this, as it listens to all interfaces. I have a system which has multiple interfaces, each is dual stack and I need my application to listen to only a single interface. I got caught by issue as I used hostname:9100 (which resolves to IPv4 and IPv6 on the interface) as the most common behaviour for a networking stack which accepts a hostname is to listen on all addresses that the hostname references at application initialization. Another approach would be to accept the interface name and port (e.g. bge0:9100) and listen to all IPs allocated to the interface at the time of initialization. It's rare to see software which supports the IPs changing after application start, so no matter whether the name resolution or interface approach is taken, after initial resolution I would expect that it's up to the admin to either reconfigure or restart the application if those changes are required. Obviously, having a way of telling the system to re-check every X seconds would be a nice to have, but that's far from expected behaviour, whereas resolving all IPs at start is expected behaviour (as can be seen by the large number of references to this issue). |
Today, net.Listen will only listen on 127.0.0.1 if localhost is passed [0]. Listening on `:8080` will open a dualstack socket on OSs that support it. [0] golang/go#9334
Has anyone written a sane example of multi-listen? |
I wrote https://github.com/tsavola/listen, but I'm not too happy about the implementation. |
@tsavola With your implementation, what would need to be changed for you to be happy with it? |
Each acceptLoop goroutine accepts underlying connections before they are requested by the application; one or more TCP connections may be established before the application calls Accept. If the listener is wrapped by a connection limiter or such, there might be a long delay before the connection is actually passed to the application--or the connection might never be seen by the application. I would be happy if the application's Accept call would directly cause the establishment of at most one TCP connection. |
I don't expect to tilt the scales much compared to the very impressive selection of open source projects that appear to have run into this, but add my vote to the pile in favor of allowing binding on a hostname or on a network interface name to listen to all associated IPs. It shouldn't be so hard to listen on both an IPv4 and IPv6 address associated with a given hostname (without listening on all interfaces). |
are there any updates on this issue? 8 years now. |
The following example program:
Currently yields this result:
While the following result would be optimal:
(Note that the first socket is actually dualstack, and bound to ::ffff:127.0.0.1, but that's less critical than adding the second socket bound to ::1.)
More generally, when you call
net.Listen()
on a hostname which resolves to multiple IPv4/IPv6 addresses, only the first IPv4 address is selected. An analogous problem occurs if youListen("tcp", ":8080")
on an operating system that doesn't support dualstack sockets: instead of returning a pair of sockets bound to[::]:8080
and0.0.0.0:80
, you only get IPv4.The fundamental flaw is that
Listen()
assumes a single socket, which is a leaky abstraction that's inappropriate for high-level things like example servers, e.g.:http://golang.org/pkg/net/#pkg-overview
Go should either adapt the
Listen()
API to support multiple sockets, or if that's not feasible, a new multi-socket API should be introduced, which deprecatesListen()
for all cases except simple non-wildcard addresses.The text was updated successfully, but these errors were encountered: