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