1
2
3
4
5 package ipv6_test
6
7 import (
8 "bytes"
9 "fmt"
10 "net"
11 "runtime"
12 "strings"
13 "sync"
14 "testing"
15 "time"
16
17 "golang.org/x/net/internal/iana"
18 "golang.org/x/net/ipv6"
19 "golang.org/x/net/nettest"
20 )
21
22 func BenchmarkReadWriteUnicast(b *testing.B) {
23 switch runtime.GOOS {
24 case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
25 b.Skipf("not supported on %s", runtime.GOOS)
26 }
27
28 c, err := nettest.NewLocalPacketListener("udp6")
29 if err != nil {
30 b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
31 }
32 defer c.Close()
33
34 dst := c.LocalAddr()
35 wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128)
36
37 b.Run("NetUDP", func(b *testing.B) {
38 for i := 0; i < b.N; i++ {
39 if _, err := c.WriteTo(wb, dst); err != nil {
40 b.Fatal(err)
41 }
42 if _, _, err := c.ReadFrom(rb); err != nil {
43 b.Fatal(err)
44 }
45 }
46 })
47 b.Run("IPv6UDP", func(b *testing.B) {
48 p := ipv6.NewPacketConn(c)
49 cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
50 if err := p.SetControlMessage(cf, true); err != nil {
51 b.Fatal(err)
52 }
53 cm := ipv6.ControlMessage{
54 TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
55 HopLimit: 1,
56 }
57 ifi, _ := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
58 if ifi != nil {
59 cm.IfIndex = ifi.Index
60 }
61
62 for i := 0; i < b.N; i++ {
63 if _, err := p.WriteTo(wb, &cm, dst); err != nil {
64 b.Fatal(err)
65 }
66 if _, _, _, err := p.ReadFrom(rb); err != nil {
67 b.Fatal(err)
68 }
69 }
70 })
71 }
72
73 func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {
74 switch runtime.GOOS {
75 case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
76 b.Skipf("not supported on %s", runtime.GOOS)
77 }
78
79 payload := []byte("HELLO-R-U-THERE")
80 iph := []byte{
81 0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01,
82 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
84 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
86 }
87 greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00}
88 datagram := append(greh, append(iph, payload...)...)
89 bb := make([]byte, 128)
90 cm := ipv6.ControlMessage{
91 TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
92 HopLimit: 1,
93 Src: net.IPv6loopback,
94 }
95 ifi, _ := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
96 if ifi != nil {
97 cm.IfIndex = ifi.Index
98 }
99
100 b.Run("UDP", func(b *testing.B) {
101 c, err := nettest.NewLocalPacketListener("udp6")
102 if err != nil {
103 b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
104 }
105 defer c.Close()
106 p := ipv6.NewPacketConn(c)
107 dst := c.LocalAddr()
108 cf := ipv6.FlagHopLimit | ipv6.FlagInterface
109 if err := p.SetControlMessage(cf, true); err != nil {
110 b.Fatal(err)
111 }
112 wms := []ipv6.Message{
113 {
114 Buffers: [][]byte{payload},
115 Addr: dst,
116 OOB: cm.Marshal(),
117 },
118 }
119 rms := []ipv6.Message{
120 {
121 Buffers: [][]byte{bb},
122 OOB: ipv6.NewControlMessage(cf),
123 },
124 }
125 b.Run("Net", func(b *testing.B) {
126 for i := 0; i < b.N; i++ {
127 if _, err := c.WriteTo(payload, dst); err != nil {
128 b.Fatal(err)
129 }
130 if _, _, err := c.ReadFrom(bb); err != nil {
131 b.Fatal(err)
132 }
133 }
134 })
135 b.Run("ToFrom", func(b *testing.B) {
136 for i := 0; i < b.N; i++ {
137 if _, err := p.WriteTo(payload, &cm, dst); err != nil {
138 b.Fatal(err)
139 }
140 if _, _, _, err := p.ReadFrom(bb); err != nil {
141 b.Fatal(err)
142 }
143 }
144 })
145 b.Run("Batch", func(b *testing.B) {
146 for i := 0; i < b.N; i++ {
147 if _, err := p.WriteBatch(wms, 0); err != nil {
148 b.Fatal(err)
149 }
150 if _, err := p.ReadBatch(rms, 0); err != nil {
151 b.Fatal(err)
152 }
153 }
154 })
155 })
156 b.Run("IP", func(b *testing.B) {
157 switch runtime.GOOS {
158 case "netbsd":
159 b.Skip("need to configure gre on netbsd")
160 case "openbsd":
161 b.Skip("net.inet.gre.allow=0 by default on openbsd")
162 }
163
164 c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1")
165 if err != nil {
166 b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
167 }
168 defer c.Close()
169 p := ipv6.NewPacketConn(c)
170 dst := c.LocalAddr()
171 cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
172 if err := p.SetControlMessage(cf, true); err != nil {
173 b.Fatal(err)
174 }
175 wms := []ipv6.Message{
176 {
177 Buffers: [][]byte{datagram},
178 Addr: dst,
179 OOB: cm.Marshal(),
180 },
181 }
182 rms := []ipv6.Message{
183 {
184 Buffers: [][]byte{bb},
185 OOB: ipv6.NewControlMessage(cf),
186 },
187 }
188 b.Run("Net", func(b *testing.B) {
189 for i := 0; i < b.N; i++ {
190 if _, err := c.WriteTo(datagram, dst); err != nil {
191 b.Fatal(err)
192 }
193 if _, _, err := c.ReadFrom(bb); err != nil {
194 b.Fatal(err)
195 }
196 }
197 })
198 b.Run("ToFrom", func(b *testing.B) {
199 for i := 0; i < b.N; i++ {
200 if _, err := p.WriteTo(datagram, &cm, dst); err != nil {
201 b.Fatal(err)
202 }
203 if _, _, _, err := p.ReadFrom(bb); err != nil {
204 b.Fatal(err)
205 }
206 }
207 })
208 b.Run("Batch", func(b *testing.B) {
209 for i := 0; i < b.N; i++ {
210 if _, err := p.WriteBatch(wms, 0); err != nil {
211 b.Fatal(err)
212 }
213 if _, err := p.ReadBatch(rms, 0); err != nil {
214 b.Fatal(err)
215 }
216 }
217 })
218 })
219 }
220
221 func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
222 switch runtime.GOOS {
223 case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
224 t.Skipf("not supported on %s", runtime.GOOS)
225 }
226 ifi, err := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
227 if err != nil {
228 t.Skip("ipv6 is not enabled for loopback interface")
229 }
230 c, err := nettest.NewLocalPacketListener("udp6")
231 if err != nil {
232 t.Fatal(err)
233 }
234 defer c.Close()
235 p := ipv6.NewPacketConn(c)
236 defer p.Close()
237
238 dst := c.LocalAddr()
239 cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
240 wb := []byte("HELLO-R-U-THERE")
241
242 if err := p.SetControlMessage(cf, true); err != nil {
243 if protocolNotSupported(err) {
244 t.Skipf("not supported on %s", runtime.GOOS)
245 }
246 t.Fatal(err)
247 }
248
249 var firstError sync.Once
250 fatalf := func(format string, args ...interface{}) {
251
252
253
254 first := false
255 firstError.Do(func() {
256 first = true
257 p.Close()
258 })
259 if first {
260 t.Helper()
261 t.Errorf(format, args...)
262 }
263 runtime.Goexit()
264 }
265
266 var wg sync.WaitGroup
267 reader := func() {
268 defer wg.Done()
269 rb := make([]byte, 128)
270 if n, cm, _, err := p.ReadFrom(rb); err != nil {
271 fatalf("%v", err)
272 } else if !bytes.Equal(rb[:n], wb) {
273 fatalf("got %v; want %v", rb[:n], wb)
274 } else {
275 s := cm.String()
276 if strings.Contains(s, ",") {
277 t.Errorf("should be space-separated values: %s", s)
278 }
279 }
280 }
281 writer := func(toggle bool) {
282 defer wg.Done()
283 cm := ipv6.ControlMessage{
284 TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
285 Src: net.IPv6loopback,
286 }
287 if ifi != nil {
288 cm.IfIndex = ifi.Index
289 }
290 if err := p.SetControlMessage(cf, toggle); err != nil {
291 fatalf("%v", err)
292 }
293
294 backoff := time.Millisecond
295 for {
296 n, err := p.WriteTo(wb, &cm, dst)
297 if err != nil {
298 if n == 0 && isENOBUFS(err) {
299 time.Sleep(backoff)
300 backoff *= 2
301 continue
302 }
303 fatalf("%v", err)
304 }
305 if n != len(wb) {
306 fatalf("got %d; want %d", n, len(wb))
307 }
308 break
309 }
310 }
311
312 const N = 10
313 wg.Add(N)
314 for i := 0; i < N; i++ {
315 go reader()
316 }
317 wg.Add(2 * N)
318 for i := 0; i < 2*N; i++ {
319 go writer(i%2 != 0)
320 }
321 wg.Add(N)
322 for i := 0; i < N; i++ {
323 go reader()
324 }
325 wg.Wait()
326 }
327
328 func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) {
329 switch runtime.GOOS {
330 case "fuchsia", "hurd", "js", "nacl", "plan9", "wasip1", "windows":
331 t.Skipf("not supported on %s", runtime.GOOS)
332 }
333
334 payload := []byte("HELLO-R-U-THERE")
335 iph := []byte{
336 0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01,
337 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
338 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
339 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
340 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
341 }
342 greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00}
343 datagram := append(greh, append(iph, payload...)...)
344
345 t.Run("UDP", func(t *testing.T) {
346 c, err := nettest.NewLocalPacketListener("udp6")
347 if err != nil {
348 t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
349 }
350 defer c.Close()
351 p := ipv6.NewPacketConn(c)
352 t.Run("ToFrom", func(t *testing.T) {
353 testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), false)
354 })
355 t.Run("Batch", func(t *testing.T) {
356 testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), true)
357 })
358 })
359 t.Run("IP", func(t *testing.T) {
360 switch runtime.GOOS {
361 case "netbsd":
362 t.Skip("need to configure gre on netbsd")
363 case "openbsd":
364 t.Skip("net.inet.gre.allow=0 by default on openbsd")
365 }
366
367 c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1")
368 if err != nil {
369 t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
370 }
371 defer c.Close()
372 p := ipv6.NewPacketConn(c)
373 t.Run("ToFrom", func(t *testing.T) {
374 testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), false)
375 })
376 t.Run("Batch", func(t *testing.T) {
377 testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), true)
378 })
379 })
380 }
381
382 func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv6.PacketConn, data []byte, dst net.Addr, batch bool) {
383 ifi, _ := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback)
384 cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU
385
386 if err := p.SetControlMessage(cf, true); err != nil {
387 if protocolNotSupported(err) {
388 t.Skipf("not supported on %s", runtime.GOOS)
389 }
390 t.Fatal(err)
391 }
392
393 var firstError sync.Once
394 fatalf := func(format string, args ...interface{}) {
395
396
397
398 first := false
399 firstError.Do(func() {
400 first = true
401 p.Close()
402 })
403 if first {
404 t.Helper()
405 t.Errorf(format, args...)
406 }
407 runtime.Goexit()
408 }
409
410 var wg sync.WaitGroup
411 reader := func() {
412 defer wg.Done()
413 b := make([]byte, 128)
414 n, cm, _, err := p.ReadFrom(b)
415 if err != nil {
416 fatalf("%v", err)
417 }
418 if !bytes.Equal(b[:n], data) {
419 fatalf("got %#v; want %#v", b[:n], data)
420 }
421 s := cm.String()
422 if strings.Contains(s, ",") {
423 fatalf("should be space-separated values: %s", s)
424 }
425 }
426 batchReader := func() {
427 defer wg.Done()
428 ms := []ipv6.Message{
429 {
430 Buffers: [][]byte{make([]byte, 128)},
431 OOB: ipv6.NewControlMessage(cf),
432 },
433 }
434 n, err := p.ReadBatch(ms, 0)
435 if err != nil {
436 fatalf("%v", err)
437 }
438 if n != len(ms) {
439 fatalf("got %d; want %d", n, len(ms))
440 }
441 var cm ipv6.ControlMessage
442 if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil {
443 fatalf("%v", err)
444 }
445 b := ms[0].Buffers[0][:ms[0].N]
446 if !bytes.Equal(b, data) {
447 fatalf("got %#v; want %#v", b, data)
448 }
449 s := cm.String()
450 if strings.Contains(s, ",") {
451 fatalf("should be space-separated values: %s", s)
452 }
453 }
454 writer := func(toggle bool) {
455 defer wg.Done()
456 cm := ipv6.ControlMessage{
457 TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
458 HopLimit: 1,
459 Src: net.IPv6loopback,
460 }
461 if ifi != nil {
462 cm.IfIndex = ifi.Index
463 }
464 if err := p.SetControlMessage(cf, toggle); err != nil {
465 fatalf("%v", err)
466 }
467
468 backoff := time.Millisecond
469 for {
470 n, err := p.WriteTo(data, &cm, dst)
471 if err != nil {
472 if n == 0 && isENOBUFS(err) {
473 time.Sleep(backoff)
474 backoff *= 2
475 continue
476 }
477 fatalf("%v", err)
478 }
479 if n != len(data) {
480 fatalf("got %d; want %d", n, len(data))
481 }
482 break
483 }
484 }
485 batchWriter := func(toggle bool) {
486 defer wg.Done()
487 cm := ipv6.ControlMessage{
488 TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced,
489 HopLimit: 1,
490 Src: net.IPv6loopback,
491 }
492 if ifi != nil {
493 cm.IfIndex = ifi.Index
494 }
495 if err := p.SetControlMessage(cf, toggle); err != nil {
496 fatalf("%v", err)
497 }
498 ms := []ipv6.Message{
499 {
500 Buffers: [][]byte{data},
501 OOB: cm.Marshal(),
502 Addr: dst,
503 },
504 }
505
506 backoff := time.Millisecond
507 for {
508 n, err := p.WriteBatch(ms, 0)
509 if err != nil {
510 if n == 0 && isENOBUFS(err) {
511 time.Sleep(backoff)
512 backoff *= 2
513 continue
514 }
515 fatalf("%v", err)
516 }
517 if n != len(ms) {
518 fatalf("got %d; want %d", n, len(ms))
519 }
520 if ms[0].N != len(data) {
521 fatalf("got %d; want %d", ms[0].N, len(data))
522 }
523 break
524 }
525 }
526
527 const N = 10
528 wg.Add(N)
529 for i := 0; i < N; i++ {
530 if batch {
531 go batchReader()
532 } else {
533 go reader()
534 }
535 }
536 wg.Add(2 * N)
537 for i := 0; i < 2*N; i++ {
538 if batch {
539 go batchWriter(i%2 != 0)
540 } else {
541 go writer(i%2 != 0)
542 }
543 }
544 wg.Add(N)
545 for i := 0; i < N; i++ {
546 if batch {
547 go batchReader()
548 } else {
549 go reader()
550 }
551 }
552 wg.Wait()
553 }
554
View as plain text