diag_test.go 4.7 KB

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