Commit 6694a688 authored by Mikio Hara's avatar Mikio Hara

net: delay IP protocol stack-snooping system calls

This change delays IP protocol stack-snooping system calls until the
start of connection setup for the better experience with some system
call auditing, such as seccomp on Linux. See #16789 for examples.

Also updates the documentation on favoriteAddrFamily, which is the
owner of stack-snooping system calls.

Fixes #16789.

Change-Id: I4af27bc1ed06ffb1f657b6f6381c328c1f41c66c
Reviewed-on: https://go-review.googlesource.com/40750
Run-TryBot: Mikio Hara <mikioh.mikioh@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent bc293137
...@@ -31,7 +31,7 @@ func TestProhibitionaryDialArg(t *testing.T) { ...@@ -31,7 +31,7 @@ func TestProhibitionaryDialArg(t *testing.T) {
case "plan9": case "plan9":
t.Skipf("not supported on %s", runtime.GOOS) t.Skipf("not supported on %s", runtime.GOOS)
} }
if !supportsIPv4map { if !supportsIPv4map() {
t.Skip("mapping ipv4 address inside ipv6 address not supported") t.Skip("mapping ipv4 address inside ipv6 address not supported")
} }
...@@ -81,7 +81,7 @@ func TestDialerDualStackFDLeak(t *testing.T) { ...@@ -81,7 +81,7 @@ func TestDialerDualStackFDLeak(t *testing.T) {
case "openbsd": case "openbsd":
testenv.SkipFlaky(t, 15157) testenv.SkipFlaky(t, 15157)
} }
if !supportsIPv4 || !supportsIPv6 { if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
...@@ -190,7 +190,7 @@ func dialClosedPort() (actual, expected time.Duration) { ...@@ -190,7 +190,7 @@ func dialClosedPort() (actual, expected time.Duration) {
func TestDialParallel(t *testing.T) { func TestDialParallel(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if !supportsIPv4 || !supportsIPv6 { if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
...@@ -361,7 +361,7 @@ func lookupSlowFast(ctx context.Context, fn func(context.Context, string) ([]IPA ...@@ -361,7 +361,7 @@ func lookupSlowFast(ctx context.Context, fn func(context.Context, string) ([]IPA
func TestDialerFallbackDelay(t *testing.T) { func TestDialerFallbackDelay(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if !supportsIPv4 || !supportsIPv6 { if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
...@@ -427,7 +427,7 @@ func TestDialerFallbackDelay(t *testing.T) { ...@@ -427,7 +427,7 @@ func TestDialerFallbackDelay(t *testing.T) {
} }
func TestDialParallelSpuriousConnection(t *testing.T) { func TestDialParallelSpuriousConnection(t *testing.T) {
if !supportsIPv4 || !supportsIPv6 { if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
...@@ -536,7 +536,7 @@ func TestDialerPartialDeadline(t *testing.T) { ...@@ -536,7 +536,7 @@ func TestDialerPartialDeadline(t *testing.T) {
} }
func TestDialerLocalAddr(t *testing.T) { func TestDialerLocalAddr(t *testing.T) {
if !supportsIPv4 || !supportsIPv6 { if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
...@@ -590,7 +590,7 @@ func TestDialerLocalAddr(t *testing.T) { ...@@ -590,7 +590,7 @@ func TestDialerLocalAddr(t *testing.T) {
{"tcp", "::1", &UnixAddr{}, &AddrError{Err: "some error"}}, {"tcp", "::1", &UnixAddr{}, &AddrError{Err: "some error"}},
} }
if supportsIPv4map { if supportsIPv4map() {
tests = append(tests, test{ tests = append(tests, test{
"tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, nil, "tcp", "127.0.0.1", &TCPAddr{IP: ParseIP("::")}, nil,
}) })
...@@ -652,7 +652,7 @@ func TestDialerLocalAddr(t *testing.T) { ...@@ -652,7 +652,7 @@ func TestDialerLocalAddr(t *testing.T) {
func TestDialerDualStack(t *testing.T) { func TestDialerDualStack(t *testing.T) {
testenv.SkipFlaky(t, 13324) testenv.SkipFlaky(t, 13324)
if !supportsIPv4 || !supportsIPv6 { if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
...@@ -755,7 +755,7 @@ func TestDialCancel(t *testing.T) { ...@@ -755,7 +755,7 @@ func TestDialCancel(t *testing.T) {
} }
blackholeIPPort := JoinHostPort(slowDst4, "1234") blackholeIPPort := JoinHostPort(slowDst4, "1234")
if !supportsIPv4 { if !supportsIPv4() {
blackholeIPPort = JoinHostPort(slowDst6, "1234") blackholeIPPort = JoinHostPort(slowDst6, "1234")
} }
......
...@@ -214,7 +214,7 @@ func TestDialAddrError(t *testing.T) { ...@@ -214,7 +214,7 @@ func TestDialAddrError(t *testing.T) {
case "nacl", "plan9": case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS) t.Skipf("not supported on %s", runtime.GOOS)
} }
if !supportsIPv4 || !supportsIPv6 { if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
func TestResolveGoogle(t *testing.T) { func TestResolveGoogle(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 { if !supportsIPv4() || !supportsIPv6() || !*testIPv4 || !*testIPv6 {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
...@@ -62,7 +62,7 @@ var dialGoogleTests = []struct { ...@@ -62,7 +62,7 @@ var dialGoogleTests = []struct {
func TestDialGoogle(t *testing.T) { func TestDialGoogle(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 { if !supportsIPv4() || !supportsIPv6() || !*testIPv4 || !*testIPv6 {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
......
...@@ -24,13 +24,7 @@ type netFD struct { ...@@ -24,13 +24,7 @@ type netFD struct {
isStream bool isStream bool
} }
var ( var netdir = "/net" // default network
netdir string // default network
)
func sysInit() {
netdir = "/net"
}
func newFD(net, name string, listen, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) { func newFD(net, name string, listen, ctl, data *os.File, laddr, raddr Addr) (*netFD, error) {
ret := &netFD{ ret := &netFD{
......
...@@ -28,9 +28,6 @@ type netFD struct { ...@@ -28,9 +28,6 @@ type netFD struct {
raddr Addr raddr Addr
} }
func sysInit() {
}
func newFD(sysfd, family, sotype int, net string) (*netFD, error) { func newFD(sysfd, family, sotype int, net string) (*netFD, error) {
ret := &netFD{ ret := &netFD{
pfd: poll.FD{ pfd: poll.FD{
......
...@@ -13,9 +13,6 @@ import ( ...@@ -13,9 +13,6 @@ import (
"unsafe" "unsafe"
) )
func sysInit() {
}
// canUseConnectEx reports whether we can use the ConnectEx Windows API call // canUseConnectEx reports whether we can use the ConnectEx Windows API call
// for the given network type. // for the given network type.
func canUseConnectEx(net string) bool { func canUseConnectEx(net string) bool {
......
...@@ -262,13 +262,13 @@ func validateInterfaceMulticastAddrs(ifat []Addr) (*routeStats, error) { ...@@ -262,13 +262,13 @@ func validateInterfaceMulticastAddrs(ifat []Addr) (*routeStats, error) {
func checkUnicastStats(ifStats *ifStats, uniStats *routeStats) error { func checkUnicastStats(ifStats *ifStats, uniStats *routeStats) error {
// Test the existence of connected unicast routes for IPv4. // Test the existence of connected unicast routes for IPv4.
if supportsIPv4 && ifStats.loop+ifStats.other > 0 && uniStats.ipv4 == 0 { if supportsIPv4() && ifStats.loop+ifStats.other > 0 && uniStats.ipv4 == 0 {
return fmt.Errorf("num IPv4 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats) return fmt.Errorf("num IPv4 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats)
} }
// Test the existence of connected unicast routes for IPv6. // Test the existence of connected unicast routes for IPv6.
// We can assume the existence of ::1/128 when at least one // We can assume the existence of ::1/128 when at least one
// loopback interface is installed. // loopback interface is installed.
if supportsIPv6 && ifStats.loop > 0 && uniStats.ipv6 == 0 { if supportsIPv6() && ifStats.loop > 0 && uniStats.ipv6 == 0 {
return fmt.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats) return fmt.Errorf("num IPv6 unicast routes = 0; want >0; summary: %+v, %+v", ifStats, uniStats)
} }
return nil return nil
...@@ -290,7 +290,7 @@ func checkMulticastStats(ifStats *ifStats, uniStats, multiStats *routeStats) err ...@@ -290,7 +290,7 @@ func checkMulticastStats(ifStats *ifStats, uniStats, multiStats *routeStats) err
// We can assume the existence of connected multicast // We can assume the existence of connected multicast
// route clones when at least two connected unicast // route clones when at least two connected unicast
// routes, ::1/128 and other, are installed. // routes, ::1/128 and other, are installed.
if supportsIPv6 && ifStats.loop > 0 && uniStats.ipv6 > 1 && multiStats.ipv6 == 0 { if supportsIPv6() && ifStats.loop > 0 && uniStats.ipv6 > 1 && multiStats.ipv6 == 0 {
return fmt.Errorf("num IPv6 multicast route clones = 0; want >0; summary: %+v, %+v, %+v", ifStats, uniStats, multiStats) return fmt.Errorf("num IPv6 multicast route clones = 0; want >0; summary: %+v, %+v, %+v", ifStats, uniStats, multiStats)
} }
} }
......
...@@ -2,12 +2,11 @@ ...@@ -2,12 +2,11 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Internet protocol family sockets
package net package net
import ( import (
"context" "context"
"sync"
) )
// BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the // BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the
...@@ -17,20 +16,36 @@ import ( ...@@ -17,20 +16,36 @@ import (
// both address families are to be supported. // both address families are to be supported.
// See inet6(4) for details. // See inet6(4) for details.
var ( type ipStackCapabilities struct {
// supportsIPv4 reports whether the platform supports IPv4 sync.Once // guards following
// networking functionality. ipv4Enabled bool
supportsIPv4 bool ipv6Enabled bool
ipv4MappedIPv6Enabled bool
}
// supportsIPv6 reports whether the platform supports IPv6 var ipStackCaps ipStackCapabilities
// networking functionality.
supportsIPv6 bool
// supportsIPv4map reports whether the platform supports // supportsIPv4 reports whether the platform supports IPv4 networking
// mapping an IPv4 address inside an IPv6 address at transport // functionality.
// layer protocols. See RFC 4291, RFC 4038 and RFC 3493. func supportsIPv4() bool {
supportsIPv4map bool ipStackCaps.Once.Do(ipStackCaps.probe)
) return ipStackCaps.ipv4Enabled
}
// supportsIPv6 reports whether the platform supports IPv6 networking
// functionality.
func supportsIPv6() bool {
ipStackCaps.Once.Do(ipStackCaps.probe)
return ipStackCaps.ipv6Enabled
}
// supportsIPv4map reports whether the platform supports mapping an
// IPv4 address inside an IPv6 address at transport layer
// protocols. See RFC 4291, RFC 4038 and RFC 3493.
func supportsIPv4map() bool {
ipStackCaps.Once.Do(ipStackCaps.probe)
return ipStackCaps.ipv4MappedIPv6Enabled
}
// An addrList represents a list of network endpoint addresses. // An addrList represents a list of network endpoint addresses.
type addrList []Addr type addrList []Addr
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Internet protocol family sockets for Plan 9
package net package net
import ( import (
...@@ -12,6 +10,18 @@ import ( ...@@ -12,6 +10,18 @@ import (
"syscall" "syscall"
) )
// Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
// capabilities.
//
// Plan 9 uses IPv6 natively, see ip(3).
func (p *ipStackCapabilities) probe() {
p.ipv4Enabled = probe(netdir+"/iproute", "4i")
p.ipv6Enabled = probe(netdir+"/iproute", "6i")
if p.ipv4Enabled && p.ipv6Enabled {
p.ipv4MappedIPv6Enabled = true
}
}
func probe(filename, query string) bool { func probe(filename, query string) bool {
var file *file var file *file
var err error var err error
...@@ -36,23 +46,6 @@ func probe(filename, query string) bool { ...@@ -36,23 +46,6 @@ func probe(filename, query string) bool {
return r return r
} }
func probeIPv4Stack() bool {
return probe(netdir+"/iproute", "4i")
}
// probeIPv6Stack returns two boolean values. If the first boolean
// value is true, kernel supports basic IPv6 functionality. If the
// second boolean value is true, kernel supports IPv6 IPv4-mapping.
func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
// Plan 9 uses IPv6 natively, see ip(3).
r := probe(netdir+"/iproute", "6i")
v := false
if r {
v = probe(netdir+"/iproute", "4i")
}
return r, v
}
// parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80). // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
func parsePlan9Addr(s string) (ip IP, iport int, err error) { func parsePlan9Addr(s string) (ip IP, iport int, err error) {
addr := IPv4zero // address contains port only addr := IPv4zero // address contains port only
......
...@@ -13,31 +13,24 @@ import ( ...@@ -13,31 +13,24 @@ import (
"syscall" "syscall"
) )
func probeIPv4Stack() bool { // Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
// capabilities which are controlled by the IPV6_V6ONLY socket option
// and kernel configuration.
//
// Should we try to use the IPv4 socket interface if we're only
// dealing with IPv4 sockets? As long as the host system understands
// IPv4-mapped IPv6, it's okay to pass IPv4-mapeed IPv6 addresses to
// the IPv6 interface. That simplifies our code and is most
// general. Unfortunately, we need to run on kernels built without
// IPv6 support too. So probe the kernel to figure it out.
func (p *ipStackCapabilities) probe() {
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 {
case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT: case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
return false
case nil: case nil:
poll.CloseFunc(s) poll.CloseFunc(s)
p.ipv4Enabled = true
} }
return true
}
// Should we try to use the IPv4 socket interface if we're
// only dealing with IPv4 sockets? As long as the host system
// understands IPv6, it's okay to pass IPv4 addresses to the IPv6
// interface. That simplifies our code and is most general.
// Unfortunately, we need to run on kernels built without IPv6
// support too. So probe the kernel to figure it out.
//
// probeIPv6Stack probes both basic IPv6 capability and IPv6 IPv4-
// mapping capability which is controlled by IPV6_V6ONLY socket
// option and/or kernel state "net.inet6.ip6.v6only".
// It returns two boolean values. If the first boolean value is
// true, kernel supports basic IPv6 functionality. If the second
// boolean value is true, kernel supports IPv6 IPv4-mapping.
func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
var probes = []struct { var probes = []struct {
laddr TCPAddr laddr TCPAddr
value int value int
...@@ -47,23 +40,13 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { ...@@ -47,23 +40,13 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
// IPv4-mapped IPv6 address communication capability // IPv4-mapped IPv6 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 { switch runtime.GOOS {
case "dragonfly", "openbsd": case "dragonfly", "openbsd":
// Some released versions of DragonFly BSD pretend to // The latest DragonFly BSD and OpenBSD kernels don't
// accept IPV6_V6ONLY=0 successfully, but the state // support IPV6_V6ONLY=0. They always return an error
// still stays IPV6_V6ONLY=1. Eventually DragonFly BSD // and we don't need to probe the capability.
// stops pretending, 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] 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)
if err != nil { if err != nil {
...@@ -78,51 +61,55 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { ...@@ -78,51 +61,55 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
if err := syscall.Bind(s, sa); err != nil { if err := syscall.Bind(s, sa); err != nil {
continue continue
} }
supps[i] = true if i == 0 {
p.ipv6Enabled = true
} else {
p.ipv4MappedIPv6Enabled = true
}
} }
return supps[0], supps[1]
} }
// favoriteAddrFamily returns the appropriate address family to // favoriteAddrFamily returns the appropriate address family for the
// the given net, laddr, raddr and mode. At first it figures // given network, laddr, raddr and mode.
// address family out from the net. If mode indicates "listen"
// and laddr is a wildcard, it assumes that the user wants to
// make a passive connection with a wildcard address family, both
// AF_INET and AF_INET6, and a wildcard address like following:
// //
// 1. A wild-wild listen, "tcp" + "" // If mode indicates "listen" and laddr is a wildcard, we assume that
// If the platform supports both IPv6 and IPv6 IPv4-mapping // the user wants to make a passive-open connection with a wildcard
// capabilities, or does not support IPv4, we assume that // address family, both AF_INET and AF_INET6, and a wildcard address
// the user wants to listen on both IPv4 and IPv6 wildcard // like the following:
// addresses over an AF_INET6 socket with IPV6_V6ONLY=0.
// Otherwise we prefer an IPv4 wildcard address listen over
// an AF_INET socket.
// //
// 2. A wild-ipv4wild listen, "tcp" + "0.0.0.0" // - A listen for a wildcard communication domain, "tcp" or
// Same as 1. // "udp", with a wildcard address: If the platform supports
// both IPv6 and IPv4-mapped IPv6 communication capabilities,
// or does not support IPv4, we use a dual stack, AF_INET6 and
// IPV6_V6ONLY=0, wildcard address listen. The dual stack
// wildcard address listen may fall back to an IPv6-only,
// AF_INET6 and IPV6_V6ONLY=1, wildcard address listen.
// Otherwise we prefer an IPv4-only, AF_INET, wildcard address
// listen.
// //
// 3. A wild-ipv6wild listen, "tcp" + "[::]" // - A listen for a wildcard communication domain, "tcp" or
// Almost same as 1 but we prefer an IPv6 wildcard address // "udp", with an IPv4 wildcard address: same as above.
// listen over an AF_INET6 socket with IPV6_V6ONLY=0 when
// the platform supports IPv6 capability but not IPv6 IPv4-
// mapping capability.
// //
// 4. A ipv4-ipv4wild listen, "tcp4" + "" or "0.0.0.0" // - A listen for a wildcard communication domain, "tcp" or
// We use an IPv4 (AF_INET) wildcard address listen. // "udp", with an IPv6 wildcard address: same as above.
// //
// 5. A ipv6-ipv6wild listen, "tcp6" + "" or "[::]" // - A listen for an IPv4 communication domain, "tcp4" or "udp4",
// We use an IPv6 (AF_INET6, IPV6_V6ONLY=1) wildcard address // with an IPv4 wildcard address: We use an IPv4-only, AF_INET,
// listen. // wildcard address listen.
//
// - A listen for an IPv6 communication domain, "tcp6" or "udp6",
// with an IPv6 wildcard address: We use an IPv6-only, AF_INET6
// and IPV6_V6ONLY=1, wildcard address listen.
// //
// Otherwise guess: if the addresses are IPv4 then returns AF_INET, // Otherwise guess: If the addresses are IPv4 then returns AF_INET,
// or else returns AF_INET6. It also returns a boolean value what // or else returns AF_INET6. It also returns a boolean value what
// designates IPV6_V6ONLY option. // designates IPV6_V6ONLY option.
// //
// Note that OpenBSD allows neither "net.inet6.ip6.v6only=1" change // Note that the latest DragonFly BSD and OpenBSD kernels allow
// nor IPPROTO_IPV6 level IPV6_V6ONLY socket option setting. // neither "net.inet6.ip6.v6only=1" change nor IPPROTO_IPV6 level
func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family int, ipv6only bool) { // IPV6_V6ONLY socket option setting.
switch net[len(net)-1] { func favoriteAddrFamily(network string, laddr, raddr sockaddr, mode string) (family int, ipv6only bool) {
switch network[len(network)-1] {
case '4': case '4':
return syscall.AF_INET, false return syscall.AF_INET, false
case '6': case '6':
...@@ -130,7 +117,7 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family ...@@ -130,7 +117,7 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family
} }
if mode == "listen" && (laddr == nil || laddr.isWildcard()) { if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
if supportsIPv4map || !supportsIPv4 { if supportsIPv4map() || !supportsIPv4() {
return syscall.AF_INET6, false return syscall.AF_INET6, false
} }
if laddr == nil { if laddr == nil {
...@@ -146,7 +133,6 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family ...@@ -146,7 +133,6 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family
return syscall.AF_INET6, false return syscall.AF_INET6, false
} }
// Internet sockets (TCP, UDP, IP)
func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string) (fd *netFD, err error) { func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string) (fd *netFD, err error) {
if (runtime.GOOS == "windows" || runtime.GOOS == "openbsd" || runtime.GOOS == "nacl") && mode == "dial" && raddr.isWildcard() { if (runtime.GOOS == "windows" || runtime.GOOS == "openbsd" || runtime.GOOS == "nacl") && mode == "dial" && raddr.isWildcard() {
raddr = raddr.toLocal(net) raddr = raddr.toLocal(net)
......
...@@ -215,7 +215,7 @@ var addrListTests = []struct { ...@@ -215,7 +215,7 @@ var addrListTests = []struct {
} }
func TestAddrList(t *testing.T) { func TestAddrList(t *testing.T) {
if !supportsIPv4 || !supportsIPv6 { if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
......
...@@ -225,7 +225,7 @@ func TestDualStackTCPListener(t *testing.T) { ...@@ -225,7 +225,7 @@ func TestDualStackTCPListener(t *testing.T) {
case "nacl", "plan9": case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS) t.Skipf("not supported on %s", runtime.GOOS)
} }
if !supportsIPv4 || !supportsIPv6 { if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
...@@ -235,7 +235,7 @@ func TestDualStackTCPListener(t *testing.T) { ...@@ -235,7 +235,7 @@ func TestDualStackTCPListener(t *testing.T) {
continue continue
} }
if !supportsIPv4map && 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
...@@ -315,7 +315,7 @@ func TestDualStackUDPListener(t *testing.T) { ...@@ -315,7 +315,7 @@ func TestDualStackUDPListener(t *testing.T) {
case "nacl", "plan9": case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS) t.Skipf("not supported on %s", runtime.GOOS)
} }
if !supportsIPv4 || !supportsIPv6 { if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
...@@ -325,7 +325,7 @@ func TestDualStackUDPListener(t *testing.T) { ...@@ -325,7 +325,7 @@ func TestDualStackUDPListener(t *testing.T) {
continue continue
} }
if !supportsIPv4map && 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
...@@ -454,7 +454,7 @@ func checkDualStackAddrFamily(fd *netFD) error { ...@@ -454,7 +454,7 @@ func checkDualStackAddrFamily(fd *netFD) error {
// and IPv6 IPv4-mapping capability, we can assume // and IPv6 IPv4-mapping capability, we can assume
// that the node listens on a wildcard address with an // that the node listens on a wildcard address with an
// AF_INET6 socket. // AF_INET6 socket.
if supportsIPv4map && fd.laddr.(*TCPAddr).isWildcard() { if supportsIPv4map() && fd.laddr.(*TCPAddr).isWildcard() {
if fd.family != syscall.AF_INET6 { if fd.family != syscall.AF_INET6 {
return fmt.Errorf("Listen(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, syscall.AF_INET6) return fmt.Errorf("Listen(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, syscall.AF_INET6)
} }
...@@ -468,7 +468,7 @@ func checkDualStackAddrFamily(fd *netFD) error { ...@@ -468,7 +468,7 @@ func checkDualStackAddrFamily(fd *netFD) error {
// and IPv6 IPv4-mapping capability, we can assume // and IPv6 IPv4-mapping capability, we can assume
// that the node listens on a wildcard address with an // that the node listens on a wildcard address with an
// AF_INET6 socket. // AF_INET6 socket.
if supportsIPv4map && fd.laddr.(*UDPAddr).isWildcard() { if supportsIPv4map() && fd.laddr.(*UDPAddr).isWildcard() {
if fd.family != syscall.AF_INET6 { if fd.family != syscall.AF_INET6 {
return fmt.Errorf("ListenPacket(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, syscall.AF_INET6) return fmt.Errorf("ListenPacket(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, syscall.AF_INET6)
} }
...@@ -535,7 +535,7 @@ func TestIPv4MulticastListener(t *testing.T) { ...@@ -535,7 +535,7 @@ func TestIPv4MulticastListener(t *testing.T) {
case "solaris": case "solaris":
t.Skipf("not supported on solaris, see golang.org/issue/7399") t.Skipf("not supported on solaris, see golang.org/issue/7399")
} }
if !supportsIPv4 { if !supportsIPv4() {
t.Skip("IPv4 is not supported") t.Skip("IPv4 is not supported")
} }
...@@ -610,7 +610,7 @@ func TestIPv6MulticastListener(t *testing.T) { ...@@ -610,7 +610,7 @@ func TestIPv6MulticastListener(t *testing.T) {
case "solaris": case "solaris":
t.Skipf("not supported on solaris, see issue 7399") t.Skipf("not supported on solaris, see issue 7399")
} }
if !supportsIPv6 { if !supportsIPv6() {
t.Skip("IPv6 is not supported") t.Skip("IPv6 is not supported")
} }
if os.Getuid() != 0 { if os.Getuid() != 0 {
......
...@@ -63,7 +63,7 @@ func TestLookupGoogleSRV(t *testing.T) { ...@@ -63,7 +63,7 @@ func TestLookupGoogleSRV(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
} }
if !supportsIPv4 || !*testIPv4 { if !supportsIPv4() || !*testIPv4 {
t.Skip("IPv4 is required") t.Skip("IPv4 is required")
} }
...@@ -99,7 +99,7 @@ func TestLookupGmailMX(t *testing.T) { ...@@ -99,7 +99,7 @@ func TestLookupGmailMX(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
} }
if !supportsIPv4 || !*testIPv4 { if !supportsIPv4() || !*testIPv4 {
t.Skip("IPv4 is required") t.Skip("IPv4 is required")
} }
...@@ -131,7 +131,7 @@ func TestLookupGmailNS(t *testing.T) { ...@@ -131,7 +131,7 @@ func TestLookupGmailNS(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
} }
if !supportsIPv4 || !*testIPv4 { if !supportsIPv4() || !*testIPv4 {
t.Skip("IPv4 is required") t.Skip("IPv4 is required")
} }
...@@ -164,7 +164,7 @@ func TestLookupGmailTXT(t *testing.T) { ...@@ -164,7 +164,7 @@ func TestLookupGmailTXT(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
} }
if !supportsIPv4 || !*testIPv4 { if !supportsIPv4() || !*testIPv4 {
t.Skip("IPv4 is required") t.Skip("IPv4 is required")
} }
...@@ -199,7 +199,7 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) { ...@@ -199,7 +199,7 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
} }
if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 { if !supportsIPv4() || !supportsIPv6() || !*testIPv4 || !*testIPv6 {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
...@@ -220,7 +220,7 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) { ...@@ -220,7 +220,7 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) {
} }
func TestLookupIPv6LinkLocalAddr(t *testing.T) { func TestLookupIPv6LinkLocalAddr(t *testing.T) {
if !supportsIPv6 || !*testIPv6 { if !supportsIPv6() || !*testIPv6 {
t.Skip("IPv6 is required") t.Skip("IPv6 is required")
} }
...@@ -256,7 +256,7 @@ func TestLookupCNAME(t *testing.T) { ...@@ -256,7 +256,7 @@ func TestLookupCNAME(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
} }
if !supportsIPv4 || !*testIPv4 { if !supportsIPv4() || !*testIPv4 {
t.Skip("IPv4 is required") t.Skip("IPv4 is required")
} }
...@@ -283,7 +283,7 @@ func TestLookupGoogleHost(t *testing.T) { ...@@ -283,7 +283,7 @@ func TestLookupGoogleHost(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
} }
if !supportsIPv4 || !*testIPv4 { if !supportsIPv4() || !*testIPv4 {
t.Skip("IPv4 is required") t.Skip("IPv4 is required")
} }
...@@ -315,7 +315,7 @@ func TestLookupGoogleIP(t *testing.T) { ...@@ -315,7 +315,7 @@ func TestLookupGoogleIP(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
} }
if !supportsIPv4 || !*testIPv4 { if !supportsIPv4() || !*testIPv4 {
t.Skip("IPv4 is required") t.Skip("IPv4 is required")
} }
...@@ -450,7 +450,7 @@ func TestDNSFlood(t *testing.T) { ...@@ -450,7 +450,7 @@ func TestDNSFlood(t *testing.T) {
} }
func TestLookupDotsWithLocalSource(t *testing.T) { func TestLookupDotsWithLocalSource(t *testing.T) {
if !supportsIPv4 || !*testIPv4 { if !supportsIPv4() || !*testIPv4 {
t.Skip("IPv4 is required") t.Skip("IPv4 is required")
} }
...@@ -499,7 +499,7 @@ func TestLookupDotsWithRemoteSource(t *testing.T) { ...@@ -499,7 +499,7 @@ func TestLookupDotsWithRemoteSource(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
} }
if !supportsIPv4 || !*testIPv4 { if !supportsIPv4() || !*testIPv4 {
t.Skip("IPv4 is required") t.Skip("IPv4 is required")
} }
......
...@@ -70,7 +70,7 @@ var ( ...@@ -70,7 +70,7 @@ var (
) )
func setupTestData() { func setupTestData() {
if supportsIPv4 { if supportsIPv4() {
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{ resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
{"tcp", "localhost:1", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 1}, nil}, {"tcp", "localhost:1", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 1}, nil},
{"tcp4", "localhost:2", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 2}, nil}, {"tcp4", "localhost:2", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 2}, nil},
...@@ -85,7 +85,7 @@ func setupTestData() { ...@@ -85,7 +85,7 @@ func setupTestData() {
}...) }...)
} }
if supportsIPv6 { if supportsIPv6() {
resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp6", "localhost:3", &TCPAddr{IP: IPv6loopback, Port: 3}, nil}) resolveTCPAddrTests = append(resolveTCPAddrTests, resolveTCPAddrTest{"tcp6", "localhost:3", &TCPAddr{IP: IPv6loopback, Port: 3}, nil})
resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp6", "localhost:3", &UDPAddr{IP: IPv6loopback, Port: 3}, nil}) resolveUDPAddrTests = append(resolveUDPAddrTests, resolveUDPAddrTest{"udp6", "localhost:3", &UDPAddr{IP: IPv6loopback, Port: 3}, nil})
resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil}) resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil})
......
...@@ -31,20 +31,20 @@ func testUnixAddr() string { ...@@ -31,20 +31,20 @@ func testUnixAddr() string {
func newLocalListener(network string) (Listener, error) { func newLocalListener(network string) (Listener, error) {
switch network { switch network {
case "tcp": case "tcp":
if supportsIPv4 { if supportsIPv4() {
if ln, err := Listen("tcp4", "127.0.0.1:0"); err == nil { if ln, err := Listen("tcp4", "127.0.0.1:0"); err == nil {
return ln, nil return ln, nil
} }
} }
if supportsIPv6 { if supportsIPv6() {
return Listen("tcp6", "[::1]:0") return Listen("tcp6", "[::1]:0")
} }
case "tcp4": case "tcp4":
if supportsIPv4 { if supportsIPv4() {
return Listen("tcp4", "127.0.0.1:0") return Listen("tcp4", "127.0.0.1:0")
} }
case "tcp6": case "tcp6":
if supportsIPv6 { if supportsIPv6() {
return Listen("tcp6", "[::1]:0") return Listen("tcp6", "[::1]:0")
} }
case "unix", "unixpacket": case "unix", "unixpacket":
...@@ -333,18 +333,18 @@ func timeoutTransmitter(c Conn, d, min, max time.Duration, ch chan<- error) { ...@@ -333,18 +333,18 @@ func timeoutTransmitter(c Conn, d, min, max time.Duration, ch chan<- error) {
func newLocalPacketListener(network string) (PacketConn, error) { func newLocalPacketListener(network string) (PacketConn, error) {
switch network { switch network {
case "udp": case "udp":
if supportsIPv4 { if supportsIPv4() {
return ListenPacket("udp4", "127.0.0.1:0") return ListenPacket("udp4", "127.0.0.1:0")
} }
if supportsIPv6 { if supportsIPv6() {
return ListenPacket("udp6", "[::1]:0") return ListenPacket("udp6", "[::1]:0")
} }
case "udp4": case "udp4":
if supportsIPv4 { if supportsIPv4() {
return ListenPacket("udp4", "127.0.0.1:0") return ListenPacket("udp4", "127.0.0.1:0")
} }
case "udp6": case "udp6":
if supportsIPv6 { if supportsIPv6() {
return ListenPacket("udp6", "[::1]:0") return ListenPacket("udp6", "[::1]:0")
} }
case "unixgram": case "unixgram":
......
...@@ -96,12 +96,6 @@ var ( ...@@ -96,12 +96,6 @@ var (
netCgo bool // set true in conf_netcgo.go for build tag "netcgo" netCgo bool // set true in conf_netcgo.go for build tag "netcgo"
) )
func init() {
sysInit()
supportsIPv4 = probeIPv4Stack()
supportsIPv6, supportsIPv4map = probeIPv6Stack()
}
// Addr represents a network end point address. // Addr represents a network end point address.
// //
// The two methods Network and String conventionally return strings // The two methods Network and String conventionally return strings
......
...@@ -50,11 +50,11 @@ func testableNetwork(network string) bool { ...@@ -50,11 +50,11 @@ func testableNetwork(network string) bool {
} }
switch ss[0] { switch ss[0] {
case "tcp4", "udp4", "ip4": case "tcp4", "udp4", "ip4":
if !supportsIPv4 { if !supportsIPv4() {
return false return false
} }
case "tcp6", "udp6", "ip6": case "tcp6", "udp6", "ip6":
if !supportsIPv6 { if !supportsIPv6() {
return false return false
} }
} }
...@@ -117,25 +117,25 @@ func testableListenArgs(network, address, client string) bool { ...@@ -117,25 +117,25 @@ func testableListenArgs(network, address, client string) bool {
// Test functionality of IPv4 communication using AF_INET and // Test functionality of IPv4 communication using AF_INET and
// IPv6 communication using AF_INET6 sockets. // IPv6 communication using AF_INET6 sockets.
if !supportsIPv4 && ip.To4() != nil { if !supportsIPv4() && ip.To4() != nil {
return false return false
} }
if !supportsIPv6 && ip.To16() != nil && ip.To4() == nil { if !supportsIPv6() && ip.To16() != nil && ip.To4() == nil {
return false return false
} }
cip := ParseIP(client) cip := ParseIP(client)
if cip != nil { if cip != nil {
if !supportsIPv4 && cip.To4() != nil { if !supportsIPv4() && cip.To4() != nil {
return false return false
} }
if !supportsIPv6 && cip.To16() != nil && cip.To4() == nil { if !supportsIPv6() && cip.To16() != nil && cip.To4() == nil {
return false return false
} }
} }
// Test functionality of IPv4 communication using AF_INET6 // Test functionality of IPv4 communication using AF_INET6
// sockets. // sockets.
if !supportsIPv4map && supportsIPv4 && (network == "tcp" || network == "udp" || network == "ip") && wildcard { if !supportsIPv4map() && supportsIPv4() && (network == "tcp" || network == "udp" || network == "ip") && wildcard {
// At this point, we prefer IPv4 when ip is nil. // At this point, we prefer IPv4 when ip is nil.
// See favoriteAddrFamily for further information. // See favoriteAddrFamily for further information.
if ip.To16() != nil && ip.To4() == nil && cip.To4() != nil { // a pair of IPv6 server and IPv4 client if ip.To16() != nil && ip.To4() == nil && cip.To4() != nil { // a pair of IPv6 server and IPv4 client
......
...@@ -25,7 +25,7 @@ func setDefaultSockopts(s, family, sotype int, ipv6only bool) error { ...@@ -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 supportsIPv4map && 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.
......
...@@ -32,28 +32,28 @@ func BenchmarkTCP4PersistentTimeout(b *testing.B) { ...@@ -32,28 +32,28 @@ func BenchmarkTCP4PersistentTimeout(b *testing.B) {
} }
func BenchmarkTCP6OneShot(b *testing.B) { func BenchmarkTCP6OneShot(b *testing.B) {
if !supportsIPv6 { if !supportsIPv6() {
b.Skip("ipv6 is not supported") b.Skip("ipv6 is not supported")
} }
benchmarkTCP(b, false, false, "[::1]:0") benchmarkTCP(b, false, false, "[::1]:0")
} }
func BenchmarkTCP6OneShotTimeout(b *testing.B) { func BenchmarkTCP6OneShotTimeout(b *testing.B) {
if !supportsIPv6 { if !supportsIPv6() {
b.Skip("ipv6 is not supported") b.Skip("ipv6 is not supported")
} }
benchmarkTCP(b, false, true, "[::1]:0") benchmarkTCP(b, false, true, "[::1]:0")
} }
func BenchmarkTCP6Persistent(b *testing.B) { func BenchmarkTCP6Persistent(b *testing.B) {
if !supportsIPv6 { if !supportsIPv6() {
b.Skip("ipv6 is not supported") b.Skip("ipv6 is not supported")
} }
benchmarkTCP(b, true, false, "[::1]:0") benchmarkTCP(b, true, false, "[::1]:0")
} }
func BenchmarkTCP6PersistentTimeout(b *testing.B) { func BenchmarkTCP6PersistentTimeout(b *testing.B) {
if !supportsIPv6 { if !supportsIPv6() {
b.Skip("ipv6 is not supported") b.Skip("ipv6 is not supported")
} }
benchmarkTCP(b, true, true, "[::1]:0") benchmarkTCP(b, true, true, "[::1]:0")
...@@ -163,7 +163,7 @@ func BenchmarkTCP4ConcurrentReadWrite(b *testing.B) { ...@@ -163,7 +163,7 @@ func BenchmarkTCP4ConcurrentReadWrite(b *testing.B) {
} }
func BenchmarkTCP6ConcurrentReadWrite(b *testing.B) { func BenchmarkTCP6ConcurrentReadWrite(b *testing.B) {
if !supportsIPv6 { if !supportsIPv6() {
b.Skip("ipv6 is not supported") b.Skip("ipv6 is not supported")
} }
benchmarkTCPConcurrentReadWrite(b, "[::1]:0") benchmarkTCPConcurrentReadWrite(b, "[::1]:0")
...@@ -372,7 +372,7 @@ func TestTCPListenerName(t *testing.T) { ...@@ -372,7 +372,7 @@ func TestTCPListenerName(t *testing.T) {
func TestIPv6LinkLocalUnicastTCP(t *testing.T) { func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if !supportsIPv6 { if !supportsIPv6() {
t.Skip("IPv6 is not supported") t.Skip("IPv6 is not supported")
} }
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
func BenchmarkUDP6LinkLocalUnicast(b *testing.B) { func BenchmarkUDP6LinkLocalUnicast(b *testing.B) {
testHookUninstaller.Do(uninstallTestHooks) testHookUninstaller.Do(uninstallTestHooks)
if !supportsIPv6 { if !supportsIPv6() {
b.Skip("IPv6 is not supported") b.Skip("IPv6 is not supported")
} }
ifi := loopbackInterface() ifi := loopbackInterface()
...@@ -279,7 +279,7 @@ func TestUDPConnLocalAndRemoteNames(t *testing.T) { ...@@ -279,7 +279,7 @@ func TestUDPConnLocalAndRemoteNames(t *testing.T) {
func TestIPv6LinkLocalUnicastUDP(t *testing.T) { func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if !supportsIPv6 { if !supportsIPv6() {
t.Skip("IPv6 is not supported") t.Skip("IPv6 is not supported")
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment