diag_test.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // Copyright 2014 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 icmp_test
  5. import (
  6. "errors"
  7. "flag"
  8. "fmt"
  9. "net"
  10. "os"
  11. "runtime"
  12. "sync"
  13. "testing"
  14. "time"
  15. "golang.org/x/net/icmp"
  16. "golang.org/x/net/internal/iana"
  17. "golang.org/x/net/internal/nettest"
  18. "golang.org/x/net/ipv4"
  19. "golang.org/x/net/ipv6"
  20. )
  21. var testDiag = flag.Bool("diag", false, "whether to test ICMP message exchange with external network")
  22. type diagTest struct {
  23. network, address string
  24. protocol int
  25. m icmp.Message
  26. }
  27. func TestDiag(t *testing.T) {
  28. if !*testDiag {
  29. t.Skip("avoid external network")
  30. }
  31. t.Run("Ping/NonPrivileged", func(t *testing.T) {
  32. switch runtime.GOOS {
  33. case "darwin":
  34. case "linux":
  35. t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
  36. default:
  37. t.Logf("not supported on %s", runtime.GOOS)
  38. return
  39. }
  40. for i, dt := range []diagTest{
  41. {
  42. "udp4", "0.0.0.0", iana.ProtocolICMP,
  43. icmp.Message{
  44. Type: ipv4.ICMPTypeEcho, Code: 0,
  45. Body: &icmp.Echo{
  46. ID: os.Getpid() & 0xffff,
  47. Data: []byte("HELLO-R-U-THERE"),
  48. },
  49. },
  50. },
  51. {
  52. "udp6", "::", iana.ProtocolIPv6ICMP,
  53. icmp.Message{
  54. Type: ipv6.ICMPTypeEchoRequest, Code: 0,
  55. Body: &icmp.Echo{
  56. ID: os.Getpid() & 0xffff,
  57. Data: []byte("HELLO-R-U-THERE"),
  58. },
  59. },
  60. },
  61. } {
  62. if err := doDiag(dt, i); err != nil {
  63. t.Error(err)
  64. }
  65. }
  66. })
  67. t.Run("Ping/Privileged", func(t *testing.T) {
  68. if m, ok := nettest.SupportsRawIPSocket(); !ok {
  69. t.Skip(m)
  70. }
  71. for i, dt := range []diagTest{
  72. {
  73. "ip4:icmp", "0.0.0.0", iana.ProtocolICMP,
  74. icmp.Message{
  75. Type: ipv4.ICMPTypeEcho, Code: 0,
  76. Body: &icmp.Echo{
  77. ID: os.Getpid() & 0xffff,
  78. Data: []byte("HELLO-R-U-THERE"),
  79. },
  80. },
  81. },
  82. {
  83. "ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP,
  84. icmp.Message{
  85. Type: ipv6.ICMPTypeEchoRequest, Code: 0,
  86. Body: &icmp.Echo{
  87. ID: os.Getpid() & 0xffff,
  88. Data: []byte("HELLO-R-U-THERE"),
  89. },
  90. },
  91. },
  92. } {
  93. if err := doDiag(dt, i); err != nil {
  94. t.Error(err)
  95. }
  96. }
  97. })
  98. t.Run("Probe/Privileged", func(t *testing.T) {
  99. if m, ok := nettest.SupportsRawIPSocket(); !ok {
  100. t.Skip(m)
  101. }
  102. for i, dt := range []diagTest{
  103. {
  104. "ip4:icmp", "0.0.0.0", iana.ProtocolICMP,
  105. icmp.Message{
  106. Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
  107. Body: &icmp.ExtendedEchoRequest{
  108. ID: os.Getpid() & 0xffff,
  109. Local: true,
  110. Extensions: []icmp.Extension{
  111. &icmp.InterfaceIdent{
  112. Class: 3, Type: 1,
  113. Name: "doesnotexist",
  114. },
  115. },
  116. },
  117. },
  118. },
  119. {
  120. "ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP,
  121. icmp.Message{
  122. Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
  123. Body: &icmp.ExtendedEchoRequest{
  124. ID: os.Getpid() & 0xffff,
  125. Local: true,
  126. Extensions: []icmp.Extension{
  127. &icmp.InterfaceIdent{
  128. Class: 3, Type: 1,
  129. Name: "doesnotexist",
  130. },
  131. },
  132. },
  133. },
  134. },
  135. } {
  136. if err := doDiag(dt, i); err != nil {
  137. t.Error(err)
  138. }
  139. }
  140. })
  141. }
  142. func doDiag(dt diagTest, seq int) error {
  143. c, err := icmp.ListenPacket(dt.network, dt.address)
  144. if err != nil {
  145. return err
  146. }
  147. defer c.Close()
  148. dst, err := googleAddr(c, dt.protocol)
  149. if err != nil {
  150. return err
  151. }
  152. if dt.network != "udp6" && dt.protocol == iana.ProtocolIPv6ICMP {
  153. var f ipv6.ICMPFilter
  154. f.SetAll(true)
  155. f.Accept(ipv6.ICMPTypeDestinationUnreachable)
  156. f.Accept(ipv6.ICMPTypePacketTooBig)
  157. f.Accept(ipv6.ICMPTypeTimeExceeded)
  158. f.Accept(ipv6.ICMPTypeParameterProblem)
  159. f.Accept(ipv6.ICMPTypeEchoReply)
  160. f.Accept(ipv6.ICMPTypeExtendedEchoReply)
  161. if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil {
  162. return err
  163. }
  164. }
  165. switch m := dt.m.Body.(type) {
  166. case *icmp.Echo:
  167. m.Seq = 1 << uint(seq)
  168. case *icmp.ExtendedEchoRequest:
  169. m.Seq = 1 << uint(seq)
  170. }
  171. wb, err := dt.m.Marshal(nil)
  172. if err != nil {
  173. return err
  174. }
  175. if n, err := c.WriteTo(wb, dst); err != nil {
  176. return err
  177. } else if n != len(wb) {
  178. return fmt.Errorf("got %v; want %v", n, len(wb))
  179. }
  180. rb := make([]byte, 1500)
  181. if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
  182. return err
  183. }
  184. n, peer, err := c.ReadFrom(rb)
  185. if err != nil {
  186. return err
  187. }
  188. rm, err := icmp.ParseMessage(dt.protocol, rb[:n])
  189. if err != nil {
  190. return err
  191. }
  192. switch {
  193. case dt.m.Type == ipv4.ICMPTypeEcho && rm.Type == ipv4.ICMPTypeEchoReply:
  194. fallthrough
  195. case dt.m.Type == ipv6.ICMPTypeEchoRequest && rm.Type == ipv6.ICMPTypeEchoReply:
  196. fallthrough
  197. case dt.m.Type == ipv4.ICMPTypeExtendedEchoRequest && rm.Type == ipv4.ICMPTypeExtendedEchoReply:
  198. fallthrough
  199. case dt.m.Type == ipv6.ICMPTypeExtendedEchoRequest && rm.Type == ipv6.ICMPTypeExtendedEchoReply:
  200. return nil
  201. default:
  202. return fmt.Errorf("got %+v from %v; want echo reply or extended echo reply", rm, peer)
  203. }
  204. }
  205. func googleAddr(c *icmp.PacketConn, protocol int) (net.Addr, error) {
  206. host := "ipv4.google.com"
  207. if protocol == iana.ProtocolIPv6ICMP {
  208. host = "ipv6.google.com"
  209. }
  210. ips, err := net.LookupIP(host)
  211. if err != nil {
  212. return nil, err
  213. }
  214. netaddr := func(ip net.IP) (net.Addr, error) {
  215. switch c.LocalAddr().(type) {
  216. case *net.UDPAddr:
  217. return &net.UDPAddr{IP: ip}, nil
  218. case *net.IPAddr:
  219. return &net.IPAddr{IP: ip}, nil
  220. default:
  221. return nil, errors.New("neither UDPAddr nor IPAddr")
  222. }
  223. }
  224. if len(ips) > 0 {
  225. return netaddr(ips[0])
  226. }
  227. return nil, errors.New("no A or AAAA record")
  228. }
  229. func TestConcurrentNonPrivilegedListenPacket(t *testing.T) {
  230. if testing.Short() {
  231. t.Skip("avoid external network")
  232. }
  233. switch runtime.GOOS {
  234. case "darwin":
  235. case "linux":
  236. t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
  237. default:
  238. t.Skipf("not supported on %s", runtime.GOOS)
  239. }
  240. network, address := "udp4", "127.0.0.1"
  241. if !nettest.SupportsIPv4() {
  242. network, address = "udp6", "::1"
  243. }
  244. const N = 1000
  245. var wg sync.WaitGroup
  246. wg.Add(N)
  247. for i := 0; i < N; i++ {
  248. go func() {
  249. defer wg.Done()
  250. c, err := icmp.ListenPacket(network, address)
  251. if err != nil {
  252. t.Error(err)
  253. return
  254. }
  255. c.Close()
  256. }()
  257. }
  258. wg.Wait()
  259. }