nettest.go 8.2 KB

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