diag_test.go 6.0 KB

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