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