nettest.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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.
  4. // Package nettest provides utilities for network testing.
  5. package nettest
  6. import (
  7. "errors"
  8. "fmt"
  9. "io/ioutil"
  10. "net"
  11. "os"
  12. "os/exec"
  13. "runtime"
  14. "strconv"
  15. "strings"
  16. "sync"
  17. "time"
  18. )
  19. var (
  20. stackOnce sync.Once
  21. ipv4Enabled bool
  22. ipv6Enabled bool
  23. unStrmDgramEnabled bool
  24. rawSocketSess bool
  25. aLongTimeAgo = time.Unix(233431200, 0)
  26. neverTimeout = time.Time{}
  27. errNoAvailableInterface = errors.New("no available interface")
  28. errNoAvailableAddress = errors.New("no available address")
  29. )
  30. func probeStack() {
  31. if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
  32. ln.Close()
  33. ipv4Enabled = true
  34. }
  35. if ln, err := net.Listen("tcp6", "[::1]:0"); err == nil {
  36. ln.Close()
  37. ipv6Enabled = true
  38. }
  39. rawSocketSess = supportsRawSocket()
  40. switch runtime.GOOS {
  41. case "aix":
  42. // Unix network isn't properly working on AIX 7.2 with
  43. // Technical Level < 2.
  44. out, _ := exec.Command("oslevel", "-s").Output()
  45. if len(out) >= len("7200-XX-ZZ-YYMM") { // AIX 7.2, Tech Level XX, Service Pack ZZ, date YYMM
  46. ver := string(out[:4])
  47. tl, _ := strconv.Atoi(string(out[5:7]))
  48. unStrmDgramEnabled = ver > "7200" || (ver == "7200" && tl >= 2)
  49. }
  50. default:
  51. unStrmDgramEnabled = true
  52. }
  53. }
  54. func unixStrmDgramEnabled() bool {
  55. stackOnce.Do(probeStack)
  56. return unStrmDgramEnabled
  57. }
  58. // SupportsIPv4 reports whether the platform supports IPv4 networking
  59. // functionality.
  60. func SupportsIPv4() bool {
  61. stackOnce.Do(probeStack)
  62. return ipv4Enabled
  63. }
  64. // SupportsIPv6 reports whether the platform supports IPv6 networking
  65. // functionality.
  66. func SupportsIPv6() bool {
  67. stackOnce.Do(probeStack)
  68. return ipv6Enabled
  69. }
  70. // SupportsRawSocket reports whether the current session is available
  71. // to use raw sockets.
  72. func SupportsRawSocket() bool {
  73. stackOnce.Do(probeStack)
  74. return rawSocketSess
  75. }
  76. // TestableNetwork reports whether network is testable on the current
  77. // platform configuration.
  78. //
  79. // See func Dial of the standard library for the supported networks.
  80. func TestableNetwork(network string) bool {
  81. ss := strings.Split(network, ":")
  82. switch ss[0] {
  83. case "ip+nopriv":
  84. // This is an internal network name for testing on the
  85. // package net of the standard library.
  86. switch runtime.GOOS {
  87. case "android", "fuchsia", "hurd", "ios", "js", "nacl", "plan9", "windows":
  88. return false
  89. }
  90. case "ip", "ip4", "ip6":
  91. switch runtime.GOOS {
  92. case "fuchsia", "hurd", "js", "nacl", "plan9":
  93. return false
  94. default:
  95. if os.Getuid() != 0 {
  96. return false
  97. }
  98. }
  99. case "unix", "unixgram":
  100. switch runtime.GOOS {
  101. case "android", "fuchsia", "hurd", "ios", "js", "nacl", "plan9", "windows":
  102. return false
  103. case "aix":
  104. return unixStrmDgramEnabled()
  105. }
  106. case "unixpacket":
  107. switch runtime.GOOS {
  108. case "aix", "android", "fuchsia", "hurd", "darwin", "ios", "js", "nacl", "plan9", "windows", "zos":
  109. return false
  110. case "netbsd":
  111. // It passes on amd64 at least. 386 fails
  112. // (Issue 22927). arm is unknown.
  113. if runtime.GOARCH == "386" {
  114. return false
  115. }
  116. }
  117. }
  118. switch ss[0] {
  119. case "tcp4", "udp4", "ip4":
  120. return SupportsIPv4()
  121. case "tcp6", "udp6", "ip6":
  122. return SupportsIPv6()
  123. }
  124. return true
  125. }
  126. // TestableAddress reports whether address of network is testable on
  127. // the current platform configuration.
  128. func TestableAddress(network, address string) bool {
  129. switch ss := strings.Split(network, ":"); ss[0] {
  130. case "unix", "unixgram", "unixpacket":
  131. // Abstract unix domain sockets, a Linux-ism.
  132. if address[0] == '@' && runtime.GOOS != "linux" {
  133. return false
  134. }
  135. }
  136. return true
  137. }
  138. // NewLocalListener returns a listener which listens to a loopback IP
  139. // address or local file system path.
  140. //
  141. // The provided network must be "tcp", "tcp4", "tcp6", "unix" or
  142. // "unixpacket".
  143. func NewLocalListener(network string) (net.Listener, error) {
  144. switch network {
  145. case "tcp":
  146. if SupportsIPv4() {
  147. if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
  148. return ln, nil
  149. }
  150. }
  151. if SupportsIPv6() {
  152. return net.Listen("tcp6", "[::1]:0")
  153. }
  154. case "tcp4":
  155. if SupportsIPv4() {
  156. return net.Listen("tcp4", "127.0.0.1:0")
  157. }
  158. case "tcp6":
  159. if SupportsIPv6() {
  160. return net.Listen("tcp6", "[::1]:0")
  161. }
  162. case "unix", "unixpacket":
  163. path, err := LocalPath()
  164. if err != nil {
  165. return nil, err
  166. }
  167. return net.Listen(network, path)
  168. }
  169. return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH)
  170. }
  171. // NewLocalPacketListener returns a packet listener which listens to a
  172. // loopback IP address or local file system path.
  173. //
  174. // The provided network must be "udp", "udp4", "udp6" or "unixgram".
  175. func NewLocalPacketListener(network string) (net.PacketConn, error) {
  176. switch network {
  177. case "udp":
  178. if SupportsIPv4() {
  179. if c, err := net.ListenPacket("udp4", "127.0.0.1:0"); err == nil {
  180. return c, nil
  181. }
  182. }
  183. if SupportsIPv6() {
  184. return net.ListenPacket("udp6", "[::1]:0")
  185. }
  186. case "udp4":
  187. if SupportsIPv4() {
  188. return net.ListenPacket("udp4", "127.0.0.1:0")
  189. }
  190. case "udp6":
  191. if SupportsIPv6() {
  192. return net.ListenPacket("udp6", "[::1]:0")
  193. }
  194. case "unixgram":
  195. path, err := LocalPath()
  196. if err != nil {
  197. return nil, err
  198. }
  199. return net.ListenPacket(network, path)
  200. }
  201. return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH)
  202. }
  203. // LocalPath returns a local path that can be used for Unix-domain
  204. // protocol testing.
  205. func LocalPath() (string, error) {
  206. f, err := ioutil.TempFile("", "go-nettest")
  207. if err != nil {
  208. return "", err
  209. }
  210. path := f.Name()
  211. f.Close()
  212. os.Remove(path)
  213. return path, nil
  214. }
  215. // MulticastSource returns a unicast IP address on ifi when ifi is an
  216. // IP multicast-capable network interface.
  217. //
  218. // The provided network must be "ip", "ip4" or "ip6".
  219. func MulticastSource(network string, ifi *net.Interface) (net.IP, error) {
  220. switch network {
  221. case "ip", "ip4", "ip6":
  222. default:
  223. return nil, errNoAvailableAddress
  224. }
  225. if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
  226. return nil, errNoAvailableAddress
  227. }
  228. ip, ok := hasRoutableIP(network, ifi)
  229. if !ok {
  230. return nil, errNoAvailableAddress
  231. }
  232. return ip, nil
  233. }
  234. // LoopbackInterface returns an available logical network interface
  235. // for loopback test.
  236. func LoopbackInterface() (*net.Interface, error) {
  237. ift, err := net.Interfaces()
  238. if err != nil {
  239. return nil, errNoAvailableInterface
  240. }
  241. for _, ifi := range ift {
  242. if ifi.Flags&net.FlagLoopback != 0 && ifi.Flags&net.FlagUp != 0 {
  243. return &ifi, nil
  244. }
  245. }
  246. return nil, errNoAvailableInterface
  247. }
  248. // RoutedInterface returns a network interface that can route IP
  249. // traffic and satisfies flags.
  250. //
  251. // The provided network must be "ip", "ip4" or "ip6".
  252. func RoutedInterface(network string, flags net.Flags) (*net.Interface, error) {
  253. switch network {
  254. case "ip", "ip4", "ip6":
  255. default:
  256. return nil, errNoAvailableInterface
  257. }
  258. ift, err := net.Interfaces()
  259. if err != nil {
  260. return nil, errNoAvailableInterface
  261. }
  262. for _, ifi := range ift {
  263. if ifi.Flags&flags != flags {
  264. continue
  265. }
  266. if _, ok := hasRoutableIP(network, &ifi); !ok {
  267. continue
  268. }
  269. return &ifi, nil
  270. }
  271. return nil, errNoAvailableInterface
  272. }
  273. func hasRoutableIP(network string, ifi *net.Interface) (net.IP, bool) {
  274. ifat, err := ifi.Addrs()
  275. if err != nil {
  276. return nil, false
  277. }
  278. for _, ifa := range ifat {
  279. switch ifa := ifa.(type) {
  280. case *net.IPAddr:
  281. if ip, ok := routableIP(network, ifa.IP); ok {
  282. return ip, true
  283. }
  284. case *net.IPNet:
  285. if ip, ok := routableIP(network, ifa.IP); ok {
  286. return ip, true
  287. }
  288. }
  289. }
  290. return nil, false
  291. }
  292. func routableIP(network string, ip net.IP) (net.IP, bool) {
  293. if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() {
  294. return nil, false
  295. }
  296. switch network {
  297. case "ip4":
  298. if ip := ip.To4(); ip != nil {
  299. return ip, true
  300. }
  301. case "ip6":
  302. if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation
  303. return nil, false
  304. }
  305. if ip := ip.To16(); ip != nil && ip.To4() == nil {
  306. return ip, true
  307. }
  308. default:
  309. if ip := ip.To4(); ip != nil {
  310. return ip, true
  311. }
  312. if ip := ip.To16(); ip != nil {
  313. return ip, true
  314. }
  315. }
  316. return nil, false
  317. }