]> Cypherpunks repositories - gostls13.git/commitdiff
net: adjust dual stack support on dragonfly
authorMikio Hara <mikioh.mikioh@gmail.com>
Thu, 14 May 2015 01:18:10 +0000 (10:18 +0900)
committerMikio Hara <mikioh.mikioh@gmail.com>
Sat, 23 May 2015 05:22:57 +0000 (05:22 +0000)
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 <iant@golang.org>
src/net/ipsock_posix.go
src/net/listen_test.go
src/net/main_test.go
src/net/sockopt_bsd.go
src/net/tcp_test.go
src/net/tcpsock_posix.go
src/net/udp_test.go

index 56b9872fd1e6649e5dd26d7c53a3fc31164755ec..83eaf855b4c2d07e125463966f7e792be826e119 100644 (file)
@@ -9,10 +9,18 @@
 package net
 
 import (
+       "runtime"
        "syscall"
        "time"
 )
 
+// BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the
+// "tcp" and "udp" networks does not listen for both IPv4 and IPv6
+// connections. This is due to the fact that IPv4 traffic will not be
+// routed to an IPv6 socket - two separate sockets are required if
+// both address families are to be supported.
+// See inet6(4) for details.
+
 func probeIPv4Stack() bool {
        s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
        switch err {
@@ -41,13 +49,28 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
        var probes = []struct {
                laddr TCPAddr
                value int
-               ok    bool
        }{
                // IPv6 communication capability
                {laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
                // IPv6 IPv4-mapped address communication capability
                {laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
        }
+       var supps [2]bool
+       switch runtime.GOOS {
+       case "dragonfly", "openbsd":
+               // Some released versions of DragonFly BSD pretend to
+               // accept IPV6_V6ONLY=0 successfully, but the state
+               // still stays IPV6_V6ONLY=1. Eventually DragonFly BSD
+               // stops preteding, but the transition period would
+               // cause unpredictable behavior and we need to avoid
+               // it.
+               //
+               // OpenBSD also doesn't support IPV6_V6ONLY=0 but it
+               // never pretends to accept IPV6_V6OLY=0. It always
+               // returns an error and we don't need to probe the
+               // capability.
+               probes = probes[:1]
+       }
 
        for i := range probes {
                s, err := socketFunc(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
@@ -63,10 +86,10 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
                if err := syscall.Bind(s, sa); err != nil {
                        continue
                }
-               probes[i].ok = true
+               supps[i] = true
        }
 
-       return probes[0].ok, probes[1].ok
+       return supps[0], supps[1]
 }
 
 // favoriteAddrFamily returns the appropriate address family to
index 8f43c846d9069d8870a2782045283fba5cd9c74e..89d4d7e0de8f048fc9185bfd2a2c1d44c074bb2b 100644 (file)
@@ -218,8 +218,6 @@ var dualStackTCPListenerTests = []struct {
 // listening address and same port.
 func TestDualStackTCPListener(t *testing.T) {
        switch runtime.GOOS {
-       case "dragonfly":
-               t.Skip("not supported on DragonFly, see golang.org/issue/10729")
        case "nacl", "plan9":
                t.Skipf("not supported on %s", runtime.GOOS)
        }
@@ -233,7 +231,7 @@ func TestDualStackTCPListener(t *testing.T) {
                        continue
                }
 
-               if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
+               if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
                        tt.xerr = nil
                }
                var firstErr, secondErr error
@@ -320,7 +318,7 @@ func TestDualStackUDPListener(t *testing.T) {
                        continue
                }
 
-               if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
+               if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
                        tt.xerr = nil
                }
                var firstErr, secondErr error
index 62b8997091e04eaae3040c594583ff94847ce699..f3f8b1a900282846e24c24a86dd0602bbfb9c8b7 100644 (file)
@@ -59,6 +59,16 @@ func TestMain(m *testing.M) {
        os.Exit(st)
 }
 
+type ipv6LinkLocalUnicastTest struct {
+       network, address string
+       nameLookup       bool
+}
+
+var (
+       ipv6LinkLocalUnicastTCPTests []ipv6LinkLocalUnicastTest
+       ipv6LinkLocalUnicastUDPTests []ipv6LinkLocalUnicastTest
+)
+
 func setupTestData() {
        if supportsIPv4 {
                resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
@@ -81,7 +91,8 @@ func setupTestData() {
                resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil})
        }
 
-       if ifi := loopbackInterface(); ifi != nil {
+       ifi := loopbackInterface()
+       if ifi != nil {
                index := fmt.Sprintf("%v", ifi.Index)
                resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
                        {"tcp6", "[fe80::1%" + ifi.Name + "]:1", &TCPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneToString(ifi.Index)}, nil},
@@ -96,6 +107,44 @@ func setupTestData() {
                        {"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil},
                }...)
        }
+
+       addr := ipv6LinkLocalUnicastAddr(ifi)
+       if addr != "" {
+               if runtime.GOOS != "dragonfly" {
+                       ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+                               {"tcp", "[" + addr + "%" + ifi.Name + "]:0", false},
+                       }...)
+                       ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+                               {"udp", "[" + addr + "%" + ifi.Name + "]:0", false},
+                       }...)
+               }
+               ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+                       {"tcp6", "[" + addr + "%" + ifi.Name + "]:0", false},
+               }...)
+               ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+                       {"udp6", "[" + addr + "%" + ifi.Name + "]:0", false},
+               }...)
+               switch runtime.GOOS {
+               case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
+                       ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+                               {"tcp", "[localhost%" + ifi.Name + "]:0", true},
+                               {"tcp6", "[localhost%" + ifi.Name + "]:0", true},
+                       }...)
+                       ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+                               {"udp", "[localhost%" + ifi.Name + "]:0", true},
+                               {"udp6", "[localhost%" + ifi.Name + "]:0", true},
+                       }...)
+               case "linux":
+                       ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+                               {"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
+                               {"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
+                       }...)
+                       ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+                               {"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
+                               {"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
+                       }...)
+               }
+       }
 }
 
 func printRunningGoroutines() {
index 00e4dbf3761102994b2445ff6a05afa0b41670e7..1b4a586a7eec809d7c9896ebca5b53f5c8b5c0c1 100644 (file)
@@ -25,7 +25,7 @@ func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
                        syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_PORTRANGE, syscall.IPV6_PORTRANGE_HIGH)
                }
        }
-       if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
+       if supportsIPv4map && family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
                // Allow both IP versions even if the OS default
                // is otherwise.  Note that some operating systems
                // never admit this option.
index 64117449bdcf53c057db9a07235377fd9583a991..2191c91fa350ee3139b39d5e94592eba183ba92d 100644 (file)
@@ -367,39 +367,11 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
                t.Skip("avoid external network")
        }
        if !supportsIPv6 {
-               t.Skip("ipv6 is not supported")
-       }
-       ifi := loopbackInterface()
-       if ifi == nil {
-               t.Skip("loopback interface not found")
-       }
-       laddr := ipv6LinkLocalUnicastAddr(ifi)
-       if laddr == "" {
-               t.Skip("ipv6 unicast address on loopback not found")
+               t.Skip("IPv6 is not supported")
        }
 
-       type test struct {
-               net, addr  string
-               nameLookup bool
-       }
-       var tests = []test{
-               {"tcp", "[" + laddr + "%" + ifi.Name + "]:0", false},
-               {"tcp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
-       }
-       switch runtime.GOOS {
-       case "darwin", "freebsd", "openbsd", "netbsd":
-               tests = append(tests, []test{
-                       {"tcp", "[localhost%" + ifi.Name + "]:0", true},
-                       {"tcp6", "[localhost%" + ifi.Name + "]:0", true},
-               }...)
-       case "linux":
-               tests = append(tests, []test{
-                       {"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
-                       {"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
-               }...)
-       }
-       for i, tt := range tests {
-               ln, err := Listen(tt.net, tt.addr)
+       for i, tt := range ipv6LinkLocalUnicastTCPTests {
+               ln, err := Listen(tt.network, tt.address)
                if err != nil {
                        // It might return "LookupHost returned no
                        // suitable address" error on some platforms.
@@ -420,7 +392,7 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
                        t.Fatalf("got %v; expected a proper address with zone identifier", la)
                }
 
-               c, err := Dial(tt.net, ls.Listener.Addr().String())
+               c, err := Dial(tt.network, ls.Listener.Addr().String())
                if err != nil {
                        t.Fatal(err)
                }
index 1f43521a9e70aaf3e931c6e723d5e4b6634225d7..51a8e97915136ad8d3f2d83bea9ed31d953906b3 100644 (file)
@@ -13,11 +13,6 @@ import (
        "time"
 )
 
-// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
-// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
-// will not be routed to an IPv6 socket - two separate sockets are required
-// if both AFs are to be supported. See inet6(4) on OpenBSD for details.
-
 func sockaddrToTCP(sa syscall.Sockaddr) Addr {
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
index 2213468e791e5198a0a926e432f1d8afe0ab6945..b25f96a3fd3a4642d5a7d7b5a1d3b61300cfd32f 100644 (file)
@@ -238,55 +238,32 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
                t.Skip("avoid external network")
        }
        if !supportsIPv6 {
-               t.Skip("ipv6 is not supported")
-       }
-       ifi := loopbackInterface()
-       if ifi == nil {
-               t.Skip("loopback interface not found")
-       }
-       laddr := ipv6LinkLocalUnicastAddr(ifi)
-       if laddr == "" {
-               t.Skip("ipv6 unicast address on loopback not found")
+               t.Skip("IPv6 is not supported")
        }
 
-       type test struct {
-               net, addr  string
-               nameLookup bool
-       }
-       var tests = []test{
-               {"udp", "[" + laddr + "%" + ifi.Name + "]:0", false},
-               {"udp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
-       }
-       // The first udp test fails on DragonFly - see issue 7473.
-       if runtime.GOOS == "dragonfly" {
-               tests = tests[1:]
-       }
-       switch runtime.GOOS {
-       case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
-               tests = append(tests, []test{
-                       {"udp", "[localhost%" + ifi.Name + "]:0", true},
-                       {"udp6", "[localhost%" + ifi.Name + "]:0", true},
-               }...)
-       case "linux":
-               tests = append(tests, []test{
-                       {"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
-                       {"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
-               }...)
-       }
-       for _, tt := range tests {
-               c1, err := ListenPacket(tt.net, tt.addr)
+       for i, tt := range ipv6LinkLocalUnicastUDPTests {
+               c1, err := ListenPacket(tt.network, tt.address)
                if err != nil {
                        // It might return "LookupHost returned no
                        // suitable address" error on some platforms.
                        t.Log(err)
                        continue
                }
-               defer c1.Close()
+               ls, err := (&packetListener{PacketConn: c1}).newLocalServer()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               defer ls.teardown()
+               ch := make(chan error, 1)
+               handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, ch) }
+               if err := ls.buildup(handler); err != nil {
+                       t.Fatal(err)
+               }
                if la, ok := c1.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
                        t.Fatalf("got %v; expected a proper address with zone identifier", la)
                }
 
-               c2, err := Dial(tt.net, c1.LocalAddr().String())
+               c2, err := Dial(tt.network, ls.PacketConn.LocalAddr().String())
                if err != nil {
                        t.Fatal(err)
                }
@@ -302,12 +279,12 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
                        t.Fatal(err)
                }
                b := make([]byte, 32)
-               if _, from, err := c1.ReadFrom(b); err != nil {
+               if _, err := c2.Read(b); err != nil {
                        t.Fatal(err)
-               } else {
-                       if ra, ok := from.(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
-                               t.Fatalf("got %v; expected a proper address with zone identifier", ra)
-                       }
+               }
+
+               for err := range ch {
+                       t.Errorf("#%d: %v", i, err)
                }
        }
 }