1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package nettest provides utilities for network testing.
26 unStrmDgramEnabled bool
29 aLongTimeAgo = time.Unix(233431200, 0)
30 neverTimeout = time.Time{}
32 errNoAvailableInterface = errors.New("no available interface")
33 errNoAvailableAddress = errors.New("no available address")
37 if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
41 if ln, err := net.Listen("tcp6", "[::1]:0"); err == nil {
45 rawSocketSess = supportsRawSocket()
48 // Unix network isn't properly working on AIX 7.2 with
49 // Technical Level < 2.
50 out, _ := exec.Command("oslevel", "-s").Output()
51 if len(out) >= len("7200-XX-ZZ-YYMM") { // AIX 7.2, Tech Level XX, Service Pack ZZ, date YYMM
52 ver := string(out[:4])
53 tl, _ := strconv.Atoi(string(out[5:7]))
54 unStrmDgramEnabled = ver > "7200" || (ver == "7200" && tl >= 2)
57 unStrmDgramEnabled = true
61 func unixStrmDgramEnabled() bool {
62 stackOnce.Do(probeStack)
63 return unStrmDgramEnabled
66 // SupportsIPv4 reports whether the platform supports IPv4 networking
68 func SupportsIPv4() bool {
69 stackOnce.Do(probeStack)
73 // SupportsIPv6 reports whether the platform supports IPv6 networking
75 func SupportsIPv6() bool {
76 stackOnce.Do(probeStack)
80 // SupportsRawSocket reports whether the current session is available
81 // to use raw sockets.
82 func SupportsRawSocket() bool {
83 stackOnce.Do(probeStack)
87 // TestableNetwork reports whether network is testable on the current
88 // platform configuration.
90 // See func Dial of the standard library for the supported networks.
91 func TestableNetwork(network string) bool {
92 ss := strings.Split(network, ":")
95 // This is an internal network name for testing on the
96 // package net of the standard library.
98 case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
101 // iOS doesn't support it.
102 if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
106 case "ip", "ip4", "ip6":
107 switch runtime.GOOS {
108 case "fuchsia", "hurd", "js", "nacl", "plan9":
111 if os.Getuid() != 0 {
115 case "unix", "unixgram":
116 switch runtime.GOOS {
117 case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
120 return unixStrmDgramEnabled()
122 // iOS does not support unix, unixgram.
123 if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
128 switch runtime.GOOS {
129 case "aix", "android", "fuchsia", "hurd", "darwin", "js", "nacl", "plan9", "windows":
132 // It passes on amd64 at least. 386 fails
133 // (Issue 22927). arm is unknown.
134 if runtime.GOARCH == "386" {
140 case "tcp4", "udp4", "ip4":
141 return SupportsIPv4()
142 case "tcp6", "udp6", "ip6":
143 return SupportsIPv6()
148 // TestableAddress reports whether address of network is testable on
149 // the current platform configuration.
150 func TestableAddress(network, address string) bool {
151 switch ss := strings.Split(network, ":"); ss[0] {
152 case "unix", "unixgram", "unixpacket":
153 // Abstract unix domain sockets, a Linux-ism.
154 if address[0] == '@' && runtime.GOOS != "linux" {
161 // NewLocalListener returns a listener which listens to a loopback IP
162 // address or local file system path.
164 // The provided network must be "tcp", "tcp4", "tcp6", "unix" or
166 func NewLocalListener(network string) (net.Listener, error) {
170 if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
175 return net.Listen("tcp6", "[::1]:0")
179 return net.Listen("tcp4", "127.0.0.1:0")
183 return net.Listen("tcp6", "[::1]:0")
185 case "unix", "unixpacket":
186 path, err := LocalPath()
190 return net.Listen(network, path)
192 return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH)
195 // NewLocalPacketListener returns a packet listener which listens to a
196 // loopback IP address or local file system path.
198 // The provided network must be "udp", "udp4", "udp6" or "unixgram".
199 func NewLocalPacketListener(network string) (net.PacketConn, error) {
203 if c, err := net.ListenPacket("udp4", "127.0.0.1:0"); err == nil {
208 return net.ListenPacket("udp6", "[::1]:0")
212 return net.ListenPacket("udp4", "127.0.0.1:0")
216 return net.ListenPacket("udp6", "[::1]:0")
219 path, err := LocalPath()
223 return net.ListenPacket(network, path)
225 return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH)
228 // LocalPath returns a local path that can be used for Unix-domain
230 func LocalPath() (string, error) {
231 f, err := ioutil.TempFile("", "go-nettest")
241 // MulticastSource returns a unicast IP address on ifi when ifi is an
242 // IP multicast-capable network interface.
244 // The provided network must be "ip", "ip4" or "ip6".
245 func MulticastSource(network string, ifi *net.Interface) (net.IP, error) {
247 case "ip", "ip4", "ip6":
249 return nil, errNoAvailableAddress
251 if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
252 return nil, errNoAvailableAddress
254 ip, ok := hasRoutableIP(network, ifi)
256 return nil, errNoAvailableAddress
261 // LoopbackInterface returns an available logical network interface
262 // for loopback test.
263 func LoopbackInterface() (*net.Interface, error) {
264 ift, err := net.Interfaces()
266 return nil, errNoAvailableInterface
268 for _, ifi := range ift {
269 if ifi.Flags&net.FlagLoopback != 0 && ifi.Flags&net.FlagUp != 0 {
273 return nil, errNoAvailableInterface
276 // RoutedInterface returns a network interface that can route IP
277 // traffic and satisfies flags.
279 // The provided network must be "ip", "ip4" or "ip6".
280 func RoutedInterface(network string, flags net.Flags) (*net.Interface, error) {
282 case "ip", "ip4", "ip6":
284 return nil, errNoAvailableInterface
286 ift, err := net.Interfaces()
288 return nil, errNoAvailableInterface
290 for _, ifi := range ift {
291 if ifi.Flags&flags != flags {
294 if _, ok := hasRoutableIP(network, &ifi); !ok {
299 return nil, errNoAvailableInterface
302 func hasRoutableIP(network string, ifi *net.Interface) (net.IP, bool) {
303 ifat, err := ifi.Addrs()
307 for _, ifa := range ifat {
308 switch ifa := ifa.(type) {
310 if ip, ok := routableIP(network, ifa.IP); ok {
314 if ip, ok := routableIP(network, ifa.IP); ok {
322 func routableIP(network string, ip net.IP) (net.IP, bool) {
323 if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() {
328 if ip := ip.To4(); ip != nil {
332 if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation
335 if ip := ip.To16(); ip != nil && ip.To4() == nil {
339 if ip := ip.To4(); ip != nil {
342 if ip := ip.To16(); ip != nil {