You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
4.2 KiB

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