Skip to content

Commit eeb64b7

Browse files
committed
net: adjust dual stack support on dragonfly
As mentioned in http://gitweb.dragonflybsd.org/dragonfly.git/commit/727ccde8cce813911d885b7f6ed749dcea68a886, DragonFly BSD is dropping support for IPv6 IPv4-mapped address. Unfortunately, on some released versions we see the kernels pretend to support the feature but actually not (unless tweaking some kernel states via sysctl.) To avoid unpredictable behavior, the net package assumes that all DragonFly BSD kernels don't support IPv6 IPv4-mapped address. Fixes #10764. Change-Id: Ic7af3651e0372ec03774432fbb6b2eb0c455e994 Reviewed-on: https://go-review.googlesource.com/10071 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent cca39ff commit eeb64b7

File tree

7 files changed

+102
-88
lines changed

7 files changed

+102
-88
lines changed

src/net/ipsock_posix.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,18 @@
99
package net
1010

1111
import (
12+
"runtime"
1213
"syscall"
1314
"time"
1415
)
1516

17+
// BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the
18+
// "tcp" and "udp" networks does not listen for both IPv4 and IPv6
19+
// connections. This is due to the fact that IPv4 traffic will not be
20+
// routed to an IPv6 socket - two separate sockets are required if
21+
// both address families are to be supported.
22+
// See inet6(4) for details.
23+
1624
func probeIPv4Stack() bool {
1725
s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
1826
switch err {
@@ -41,13 +49,28 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
4149
var probes = []struct {
4250
laddr TCPAddr
4351
value int
44-
ok bool
4552
}{
4653
// IPv6 communication capability
4754
{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
4855
// IPv6 IPv4-mapped address communication capability
4956
{laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
5057
}
58+
var supps [2]bool
59+
switch runtime.GOOS {
60+
case "dragonfly", "openbsd":
61+
// Some released versions of DragonFly BSD pretend to
62+
// accept IPV6_V6ONLY=0 successfully, but the state
63+
// still stays IPV6_V6ONLY=1. Eventually DragonFly BSD
64+
// stops preteding, but the transition period would
65+
// cause unpredictable behavior and we need to avoid
66+
// it.
67+
//
68+
// OpenBSD also doesn't support IPV6_V6ONLY=0 but it
69+
// never pretends to accept IPV6_V6OLY=0. It always
70+
// returns an error and we don't need to probe the
71+
// capability.
72+
probes = probes[:1]
73+
}
5174

5275
for i := range probes {
5376
s, err := socketFunc(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
@@ -63,10 +86,10 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
6386
if err := syscall.Bind(s, sa); err != nil {
6487
continue
6588
}
66-
probes[i].ok = true
89+
supps[i] = true
6790
}
6891

69-
return probes[0].ok, probes[1].ok
92+
return supps[0], supps[1]
7093
}
7194

7295
// favoriteAddrFamily returns the appropriate address family to

src/net/listen_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,6 @@ var dualStackTCPListenerTests = []struct {
218218
// listening address and same port.
219219
func TestDualStackTCPListener(t *testing.T) {
220220
switch runtime.GOOS {
221-
case "dragonfly":
222-
t.Skip("not supported on DragonFly, see golang.org/issue/10729")
223221
case "nacl", "plan9":
224222
t.Skipf("not supported on %s", runtime.GOOS)
225223
}
@@ -233,7 +231,7 @@ func TestDualStackTCPListener(t *testing.T) {
233231
continue
234232
}
235233

236-
if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
234+
if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
237235
tt.xerr = nil
238236
}
239237
var firstErr, secondErr error
@@ -320,7 +318,7 @@ func TestDualStackUDPListener(t *testing.T) {
320318
continue
321319
}
322320

323-
if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
321+
if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
324322
tt.xerr = nil
325323
}
326324
var firstErr, secondErr error

src/net/main_test.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ func TestMain(m *testing.M) {
5959
os.Exit(st)
6060
}
6161

62+
type ipv6LinkLocalUnicastTest struct {
63+
network, address string
64+
nameLookup bool
65+
}
66+
67+
var (
68+
ipv6LinkLocalUnicastTCPTests []ipv6LinkLocalUnicastTest
69+
ipv6LinkLocalUnicastUDPTests []ipv6LinkLocalUnicastTest
70+
)
71+
6272
func setupTestData() {
6373
if supportsIPv4 {
6474
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
@@ -81,7 +91,8 @@ func setupTestData() {
8191
resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil})
8292
}
8393

84-
if ifi := loopbackInterface(); ifi != nil {
94+
ifi := loopbackInterface()
95+
if ifi != nil {
8596
index := fmt.Sprintf("%v", ifi.Index)
8697
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
8798
{"tcp6", "[fe80::1%" + ifi.Name + "]:1", &TCPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneToString(ifi.Index)}, nil},
@@ -96,6 +107,44 @@ func setupTestData() {
96107
{"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil},
97108
}...)
98109
}
110+
111+
addr := ipv6LinkLocalUnicastAddr(ifi)
112+
if addr != "" {
113+
if runtime.GOOS != "dragonfly" {
114+
ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
115+
{"tcp", "[" + addr + "%" + ifi.Name + "]:0", false},
116+
}...)
117+
ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
118+
{"udp", "[" + addr + "%" + ifi.Name + "]:0", false},
119+
}...)
120+
}
121+
ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
122+
{"tcp6", "[" + addr + "%" + ifi.Name + "]:0", false},
123+
}...)
124+
ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
125+
{"udp6", "[" + addr + "%" + ifi.Name + "]:0", false},
126+
}...)
127+
switch runtime.GOOS {
128+
case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
129+
ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
130+
{"tcp", "[localhost%" + ifi.Name + "]:0", true},
131+
{"tcp6", "[localhost%" + ifi.Name + "]:0", true},
132+
}...)
133+
ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
134+
{"udp", "[localhost%" + ifi.Name + "]:0", true},
135+
{"udp6", "[localhost%" + ifi.Name + "]:0", true},
136+
}...)
137+
case "linux":
138+
ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
139+
{"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
140+
{"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
141+
}...)
142+
ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
143+
{"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
144+
{"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
145+
}...)
146+
}
147+
}
99148
}
100149

101150
func printRunningGoroutines() {

src/net/sockopt_bsd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
2525
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_PORTRANGE, syscall.IPV6_PORTRANGE_HIGH)
2626
}
2727
}
28-
if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
28+
if supportsIPv4map && family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
2929
// Allow both IP versions even if the OS default
3030
// is otherwise. Note that some operating systems
3131
// never admit this option.

src/net/tcp_test.go

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -367,39 +367,11 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
367367
t.Skip("avoid external network")
368368
}
369369
if !supportsIPv6 {
370-
t.Skip("ipv6 is not supported")
371-
}
372-
ifi := loopbackInterface()
373-
if ifi == nil {
374-
t.Skip("loopback interface not found")
375-
}
376-
laddr := ipv6LinkLocalUnicastAddr(ifi)
377-
if laddr == "" {
378-
t.Skip("ipv6 unicast address on loopback not found")
370+
t.Skip("IPv6 is not supported")
379371
}
380372

381-
type test struct {
382-
net, addr string
383-
nameLookup bool
384-
}
385-
var tests = []test{
386-
{"tcp", "[" + laddr + "%" + ifi.Name + "]:0", false},
387-
{"tcp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
388-
}
389-
switch runtime.GOOS {
390-
case "darwin", "freebsd", "openbsd", "netbsd":
391-
tests = append(tests, []test{
392-
{"tcp", "[localhost%" + ifi.Name + "]:0", true},
393-
{"tcp6", "[localhost%" + ifi.Name + "]:0", true},
394-
}...)
395-
case "linux":
396-
tests = append(tests, []test{
397-
{"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
398-
{"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
399-
}...)
400-
}
401-
for i, tt := range tests {
402-
ln, err := Listen(tt.net, tt.addr)
373+
for i, tt := range ipv6LinkLocalUnicastTCPTests {
374+
ln, err := Listen(tt.network, tt.address)
403375
if err != nil {
404376
// It might return "LookupHost returned no
405377
// suitable address" error on some platforms.
@@ -420,7 +392,7 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
420392
t.Fatalf("got %v; expected a proper address with zone identifier", la)
421393
}
422394

423-
c, err := Dial(tt.net, ls.Listener.Addr().String())
395+
c, err := Dial(tt.network, ls.Listener.Addr().String())
424396
if err != nil {
425397
t.Fatal(err)
426398
}

src/net/tcpsock_posix.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@ import (
1313
"time"
1414
)
1515

16-
// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
17-
// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
18-
// will not be routed to an IPv6 socket - two separate sockets are required
19-
// if both AFs are to be supported. See inet6(4) on OpenBSD for details.
20-
2116
func sockaddrToTCP(sa syscall.Sockaddr) Addr {
2217
switch sa := sa.(type) {
2318
case *syscall.SockaddrInet4:

src/net/udp_test.go

Lines changed: 19 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -238,55 +238,32 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
238238
t.Skip("avoid external network")
239239
}
240240
if !supportsIPv6 {
241-
t.Skip("ipv6 is not supported")
242-
}
243-
ifi := loopbackInterface()
244-
if ifi == nil {
245-
t.Skip("loopback interface not found")
246-
}
247-
laddr := ipv6LinkLocalUnicastAddr(ifi)
248-
if laddr == "" {
249-
t.Skip("ipv6 unicast address on loopback not found")
241+
t.Skip("IPv6 is not supported")
250242
}
251243

252-
type test struct {
253-
net, addr string
254-
nameLookup bool
255-
}
256-
var tests = []test{
257-
{"udp", "[" + laddr + "%" + ifi.Name + "]:0", false},
258-
{"udp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
259-
}
260-
// The first udp test fails on DragonFly - see issue 7473.
261-
if runtime.GOOS == "dragonfly" {
262-
tests = tests[1:]
263-
}
264-
switch runtime.GOOS {
265-
case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
266-
tests = append(tests, []test{
267-
{"udp", "[localhost%" + ifi.Name + "]:0", true},
268-
{"udp6", "[localhost%" + ifi.Name + "]:0", true},
269-
}...)
270-
case "linux":
271-
tests = append(tests, []test{
272-
{"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
273-
{"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
274-
}...)
275-
}
276-
for _, tt := range tests {
277-
c1, err := ListenPacket(tt.net, tt.addr)
244+
for i, tt := range ipv6LinkLocalUnicastUDPTests {
245+
c1, err := ListenPacket(tt.network, tt.address)
278246
if err != nil {
279247
// It might return "LookupHost returned no
280248
// suitable address" error on some platforms.
281249
t.Log(err)
282250
continue
283251
}
284-
defer c1.Close()
252+
ls, err := (&packetListener{PacketConn: c1}).newLocalServer()
253+
if err != nil {
254+
t.Fatal(err)
255+
}
256+
defer ls.teardown()
257+
ch := make(chan error, 1)
258+
handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, ch) }
259+
if err := ls.buildup(handler); err != nil {
260+
t.Fatal(err)
261+
}
285262
if la, ok := c1.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
286263
t.Fatalf("got %v; expected a proper address with zone identifier", la)
287264
}
288265

289-
c2, err := Dial(tt.net, c1.LocalAddr().String())
266+
c2, err := Dial(tt.network, ls.PacketConn.LocalAddr().String())
290267
if err != nil {
291268
t.Fatal(err)
292269
}
@@ -302,12 +279,12 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
302279
t.Fatal(err)
303280
}
304281
b := make([]byte, 32)
305-
if _, from, err := c1.ReadFrom(b); err != nil {
282+
if _, err := c2.Read(b); err != nil {
306283
t.Fatal(err)
307-
} else {
308-
if ra, ok := from.(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
309-
t.Fatalf("got %v; expected a proper address with zone identifier", ra)
310-
}
284+
}
285+
286+
for err := range ch {
287+
t.Errorf("#%d: %v", i, err)
311288
}
312289
}
313290
}

0 commit comments

Comments
 (0)