nettest.go 8.0 KB

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