nettest.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  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", "js", "nacl", "plan9", "windows":
  88. return false
  89. case "darwin":
  90. // iOS doesn't support it.
  91. if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
  92. return false
  93. }
  94. }
  95. case "ip", "ip4", "ip6":
  96. switch runtime.GOOS {
  97. case "fuchsia", "hurd", "js", "nacl", "plan9":
  98. return false
  99. default:
  100. if os.Getuid() != 0 {
  101. return false
  102. }
  103. }
  104. case "unix", "unixgram":
  105. switch runtime.GOOS {
  106. case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
  107. return false
  108. case "aix":
  109. return unixStrmDgramEnabled()
  110. case "darwin":
  111. // iOS does not support unix, unixgram.
  112. if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
  113. return false
  114. }
  115. }
  116. case "unixpacket":
  117. switch runtime.GOOS {
  118. case "aix", "android", "fuchsia", "hurd", "darwin", "js", "nacl", "plan9", "windows":
  119. return false
  120. case "netbsd":
  121. // It passes on amd64 at least. 386 fails
  122. // (Issue 22927). arm is unknown.
  123. if runtime.GOARCH == "386" {
  124. return false
  125. }
  126. }
  127. }
  128. switch ss[0] {
  129. case "tcp4", "udp4", "ip4":
  130. return SupportsIPv4()
  131. case "tcp6", "udp6", "ip6":
  132. return SupportsIPv6()
  133. }
  134. return true
  135. }
  136. // TestableAddress reports whether address of network is testable on
  137. // the current platform configuration.
  138. func TestableAddress(network, address string) bool {
  139. switch ss := strings.Split(network, ":"); ss[0] {
  140. case "unix", "unixgram", "unixpacket":
  141. // Abstract unix domain sockets, a Linux-ism.
  142. if address[0] == '@' && runtime.GOOS != "linux" {
  143. return false
  144. }
  145. }
  146. return true
  147. }
  148. // NewLocalListener returns a listener which listens to a loopback IP
  149. // address or local file system path.
  150. //
  151. // The provided network must be "tcp", "tcp4", "tcp6", "unix" or
  152. // "unixpacket".
  153. func NewLocalListener(network string) (net.Listener, error) {
  154. switch network {
  155. case "tcp":
  156. if SupportsIPv4() {
  157. if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
  158. return ln, nil
  159. }
  160. }
  161. if SupportsIPv6() {
  162. return net.Listen("tcp6", "[::1]:0")
  163. }
  164. case "tcp4":
  165. if SupportsIPv4() {
  166. return net.Listen("tcp4", "127.0.0.1:0")
  167. }
  168. case "tcp6":
  169. if SupportsIPv6() {
  170. return net.Listen("tcp6", "[::1]:0")
  171. }
  172. case "unix", "unixpacket":
  173. path, err := LocalPath()
  174. if err != nil {
  175. return nil, err
  176. }
  177. return net.Listen(network, path)
  178. }
  179. return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH)
  180. }
  181. // NewLocalPacketListener returns a packet listener which listens to a
  182. // loopback IP address or local file system path.
  183. //
  184. // The provided network must be "udp", "udp4", "udp6" or "unixgram".
  185. func NewLocalPacketListener(network string) (net.PacketConn, error) {
  186. switch network {
  187. case "udp":
  188. if SupportsIPv4() {
  189. if c, err := net.ListenPacket("udp4", "127.0.0.1:0"); err == nil {
  190. return c, nil
  191. }
  192. }
  193. if SupportsIPv6() {
  194. return net.ListenPacket("udp6", "[::1]:0")
  195. }
  196. case "udp4":
  197. if SupportsIPv4() {
  198. return net.ListenPacket("udp4", "127.0.0.1:0")
  199. }
  200. case "udp6":
  201. if SupportsIPv6() {
  202. return net.ListenPacket("udp6", "[::1]:0")
  203. }
  204. case "unixgram":
  205. path, err := LocalPath()
  206. if err != nil {
  207. return nil, err
  208. }
  209. return net.ListenPacket(network, path)
  210. }
  211. return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH)
  212. }
  213. // LocalPath returns a local path that can be used for Unix-domain
  214. // protocol testing.
  215. func LocalPath() (string, error) {
  216. f, err := ioutil.TempFile("", "go-nettest")
  217. if err != nil {
  218. return "", err
  219. }
  220. path := f.Name()
  221. f.Close()
  222. os.Remove(path)
  223. return path, nil
  224. }
  225. // MulticastSource returns a unicast IP address on ifi when ifi is an
  226. // IP multicast-capable network interface.
  227. //
  228. // The provided network must be "ip", "ip4" or "ip6".
  229. func MulticastSource(network string, ifi *net.Interface) (net.IP, error) {
  230. switch network {
  231. case "ip", "ip4", "ip6":
  232. default:
  233. return nil, errNoAvailableAddress
  234. }
  235. if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
  236. return nil, errNoAvailableAddress
  237. }
  238. ip, ok := hasRoutableIP(network, ifi)
  239. if !ok {
  240. return nil, errNoAvailableAddress
  241. }
  242. return ip, nil
  243. }
  244. // LoopbackInterface returns an available logical network interface
  245. // for loopback test.
  246. func LoopbackInterface() (*net.Interface, error) {
  247. ift, err := net.Interfaces()
  248. if err != nil {
  249. return nil, errNoAvailableInterface
  250. }
  251. for _, ifi := range ift {
  252. if ifi.Flags&net.FlagLoopback != 0 && ifi.Flags&net.FlagUp != 0 {
  253. return &ifi, nil
  254. }
  255. }
  256. return nil, errNoAvailableInterface
  257. }
  258. // RoutedInterface returns a network interface that can route IP
  259. // traffic and satisfies flags.
  260. //
  261. // The provided network must be "ip", "ip4" or "ip6".
  262. func RoutedInterface(network string, flags net.Flags) (*net.Interface, error) {
  263. switch network {
  264. case "ip", "ip4", "ip6":
  265. default:
  266. return nil, errNoAvailableInterface
  267. }
  268. ift, err := net.Interfaces()
  269. if err != nil {
  270. return nil, errNoAvailableInterface
  271. }
  272. for _, ifi := range ift {
  273. if ifi.Flags&flags != flags {
  274. continue
  275. }
  276. if _, ok := hasRoutableIP(network, &ifi); !ok {
  277. continue
  278. }
  279. return &ifi, nil
  280. }
  281. return nil, errNoAvailableInterface
  282. }
  283. func hasRoutableIP(network string, ifi *net.Interface) (net.IP, bool) {
  284. ifat, err := ifi.Addrs()
  285. if err != nil {
  286. return nil, false
  287. }
  288. for _, ifa := range ifat {
  289. switch ifa := ifa.(type) {
  290. case *net.IPAddr:
  291. if ip, ok := routableIP(network, ifa.IP); ok {
  292. return ip, true
  293. }
  294. case *net.IPNet:
  295. if ip, ok := routableIP(network, ifa.IP); ok {
  296. return ip, true
  297. }
  298. }
  299. }
  300. return nil, false
  301. }
  302. func routableIP(network string, ip net.IP) (net.IP, bool) {
  303. if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() {
  304. return nil, false
  305. }
  306. switch network {
  307. case "ip4":
  308. if ip := ip.To4(); ip != nil {
  309. return ip, true
  310. }
  311. case "ip6":
  312. if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation
  313. return nil, false
  314. }
  315. if ip := ip.To16(); ip != nil && ip.To4() == nil {
  316. return ip, true
  317. }
  318. default:
  319. if ip := ip.To4(); ip != nil {
  320. return ip, true
  321. }
  322. if ip := ip.To16(); ip != nil {
  323. return ip, true
  324. }
  325. }
  326. return nil, false
  327. }