mirror of
https://github.com/golang/go
synced 2024-11-05 16:56:16 -07:00
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 <iant@golang.org>
This commit is contained in:
parent
cca39ff3b1
commit
eeb64b7fef
@ -9,10 +9,18 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"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 {
|
func probeIPv4Stack() bool {
|
||||||
s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
|
s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
|
||||||
switch err {
|
switch err {
|
||||||
@ -41,13 +49,28 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
|
|||||||
var probes = []struct {
|
var probes = []struct {
|
||||||
laddr TCPAddr
|
laddr TCPAddr
|
||||||
value int
|
value int
|
||||||
ok bool
|
|
||||||
}{
|
}{
|
||||||
// IPv6 communication capability
|
// IPv6 communication capability
|
||||||
{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
|
{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
|
||||||
// IPv6 IPv4-mapped address communication capability
|
// IPv6 IPv4-mapped address communication capability
|
||||||
{laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
|
{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 {
|
for i := range probes {
|
||||||
s, err := socketFunc(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
|
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 {
|
if err := syscall.Bind(s, sa); err != nil {
|
||||||
continue
|
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
|
// favoriteAddrFamily returns the appropriate address family to
|
||||||
|
@ -218,8 +218,6 @@ var dualStackTCPListenerTests = []struct {
|
|||||||
// listening address and same port.
|
// listening address and same port.
|
||||||
func TestDualStackTCPListener(t *testing.T) {
|
func TestDualStackTCPListener(t *testing.T) {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "dragonfly":
|
|
||||||
t.Skip("not supported on DragonFly, see golang.org/issue/10729")
|
|
||||||
case "nacl", "plan9":
|
case "nacl", "plan9":
|
||||||
t.Skipf("not supported on %s", runtime.GOOS)
|
t.Skipf("not supported on %s", runtime.GOOS)
|
||||||
}
|
}
|
||||||
@ -233,7 +231,7 @@ func TestDualStackTCPListener(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
|
if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
|
||||||
tt.xerr = nil
|
tt.xerr = nil
|
||||||
}
|
}
|
||||||
var firstErr, secondErr error
|
var firstErr, secondErr error
|
||||||
@ -320,7 +318,7 @@ func TestDualStackUDPListener(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
|
if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
|
||||||
tt.xerr = nil
|
tt.xerr = nil
|
||||||
}
|
}
|
||||||
var firstErr, secondErr error
|
var firstErr, secondErr error
|
||||||
|
@ -59,6 +59,16 @@ func TestMain(m *testing.M) {
|
|||||||
os.Exit(st)
|
os.Exit(st)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ipv6LinkLocalUnicastTest struct {
|
||||||
|
network, address string
|
||||||
|
nameLookup bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ipv6LinkLocalUnicastTCPTests []ipv6LinkLocalUnicastTest
|
||||||
|
ipv6LinkLocalUnicastUDPTests []ipv6LinkLocalUnicastTest
|
||||||
|
)
|
||||||
|
|
||||||
func setupTestData() {
|
func setupTestData() {
|
||||||
if supportsIPv4 {
|
if supportsIPv4 {
|
||||||
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
|
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
|
||||||
@ -81,7 +91,8 @@ func setupTestData() {
|
|||||||
resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil})
|
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)
|
index := fmt.Sprintf("%v", ifi.Index)
|
||||||
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
|
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
|
||||||
{"tcp6", "[fe80::1%" + ifi.Name + "]:1", &TCPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneToString(ifi.Index)}, nil},
|
{"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},
|
{"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() {
|
func printRunningGoroutines() {
|
||||||
|
@ -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)
|
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
|
// Allow both IP versions even if the OS default
|
||||||
// is otherwise. Note that some operating systems
|
// is otherwise. Note that some operating systems
|
||||||
// never admit this option.
|
// never admit this option.
|
||||||
|
@ -367,39 +367,11 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
|
|||||||
t.Skip("avoid external network")
|
t.Skip("avoid external network")
|
||||||
}
|
}
|
||||||
if !supportsIPv6 {
|
if !supportsIPv6 {
|
||||||
t.Skip("ipv6 is not supported")
|
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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type test struct {
|
for i, tt := range ipv6LinkLocalUnicastTCPTests {
|
||||||
net, addr string
|
ln, err := Listen(tt.network, tt.address)
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// It might return "LookupHost returned no
|
// It might return "LookupHost returned no
|
||||||
// suitable address" error on some platforms.
|
// 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)
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,6 @@ import (
|
|||||||
"time"
|
"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 {
|
func sockaddrToTCP(sa syscall.Sockaddr) Addr {
|
||||||
switch sa := sa.(type) {
|
switch sa := sa.(type) {
|
||||||
case *syscall.SockaddrInet4:
|
case *syscall.SockaddrInet4:
|
||||||
|
@ -238,55 +238,32 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
|
|||||||
t.Skip("avoid external network")
|
t.Skip("avoid external network")
|
||||||
}
|
}
|
||||||
if !supportsIPv6 {
|
if !supportsIPv6 {
|
||||||
t.Skip("ipv6 is not supported")
|
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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type test struct {
|
for i, tt := range ipv6LinkLocalUnicastUDPTests {
|
||||||
net, addr string
|
c1, err := ListenPacket(tt.network, tt.address)
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// It might return "LookupHost returned no
|
// It might return "LookupHost returned no
|
||||||
// suitable address" error on some platforms.
|
// suitable address" error on some platforms.
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
continue
|
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 == "" {
|
if la, ok := c1.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
|
||||||
t.Fatalf("got %v; expected a proper address with zone identifier", la)
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -302,12 +279,12 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
b := make([]byte, 32)
|
b := make([]byte, 32)
|
||||||
if _, from, err := c1.ReadFrom(b); err != nil {
|
if _, err := c2.Read(b); err != nil {
|
||||||
t.Fatal(err)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user