1// Copyright 2012 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 ipv4_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/ipv4"
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("udp4")
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("IPv4UDP", func(b *testing.B) {
47		p := ipv4.NewPacketConn(c)
48		cf := ipv4.FlagTTL | ipv4.FlagInterface
49		if err := p.SetControlMessage(cf, true); err != nil {
50			b.Fatal(err)
51		}
52		cm := ipv4.ControlMessage{TTL: 1}
53		ifi, _ := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
54		if ifi != nil {
55			cm.IfIndex = ifi.Index
56		}
57
58		for i := 0; i < b.N; i++ {
59			if _, err := p.WriteTo(wb, &cm, dst); err != nil {
60				b.Fatal(err)
61			}
62			if _, _, _, err := p.ReadFrom(rb); err != nil {
63				b.Fatal(err)
64			}
65		}
66	})
67}
68
69func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {
70	switch runtime.GOOS {
71	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
72		b.Skipf("not supported on %s", runtime.GOOS)
73	}
74
75	payload := []byte("HELLO-R-U-THERE")
76	iph, err := (&ipv4.Header{
77		Version:  ipv4.Version,
78		Len:      ipv4.HeaderLen,
79		TotalLen: ipv4.HeaderLen + len(payload),
80		TTL:      1,
81		Protocol: iana.ProtocolReserved,
82		Src:      net.IPv4(192, 0, 2, 1),
83		Dst:      net.IPv4(192, 0, 2, 254),
84	}).Marshal()
85	if err != nil {
86		b.Fatal(err)
87	}
88	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
89	datagram := append(greh, append(iph, payload...)...)
90	bb := make([]byte, 128)
91	cm := ipv4.ControlMessage{
92		Src: net.IPv4(127, 0, 0, 1),
93	}
94	ifi, _ := nettest.RoutedInterface("ip4", 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("udp4")
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 := ipv4.NewPacketConn(c)
106		dst := c.LocalAddr()
107		cf := ipv4.FlagTTL | ipv4.FlagInterface
108		if err := p.SetControlMessage(cf, true); err != nil {
109			b.Fatal(err)
110		}
111		wms := []ipv4.Message{
112			{
113				Buffers: [][]byte{payload},
114				Addr:    dst,
115				OOB:     cm.Marshal(),
116			},
117		}
118		rms := []ipv4.Message{
119			{
120				Buffers: [][]byte{bb},
121				OOB:     ipv4.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("ip4:%d", iana.ProtocolGRE), "127.0.0.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 := ipv4.NewPacketConn(c)
169		dst := c.LocalAddr()
170		cf := ipv4.FlagTTL | ipv4.FlagInterface
171		if err := p.SetControlMessage(cf, true); err != nil {
172			b.Fatal(err)
173		}
174		wms := []ipv4.Message{
175			{
176				Buffers: [][]byte{datagram},
177				Addr:    dst,
178				OOB:     cm.Marshal(),
179			},
180		}
181		rms := []ipv4.Message{
182			{
183				Buffers: [][]byte{bb},
184				OOB:     ipv4.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
226	c, err := nettest.NewLocalPacketListener("udp4")
227	if err != nil {
228		t.Fatal(err)
229	}
230	defer c.Close()
231	p := ipv4.NewPacketConn(c)
232	defer p.Close()
233
234	dst := c.LocalAddr()
235	ifi, _ := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
236	cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
237	wb := []byte("HELLO-R-U-THERE")
238
239	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
240		if protocolNotSupported(err) {
241			t.Skipf("not supported on %s", runtime.GOOS)
242		}
243		t.Fatal(err)
244	}
245
246	var wg sync.WaitGroup
247	reader := func() {
248		defer wg.Done()
249		rb := make([]byte, 128)
250		if n, cm, _, err := p.ReadFrom(rb); err != nil {
251			t.Error(err)
252			return
253		} else if !bytes.Equal(rb[:n], wb) {
254			t.Errorf("got %v; want %v", rb[:n], wb)
255			return
256		} else {
257			s := cm.String()
258			if strings.Contains(s, ",") {
259				t.Errorf("should be space-separated values: %s", s)
260			}
261		}
262	}
263	writer := func(toggle bool) {
264		defer wg.Done()
265		cm := ipv4.ControlMessage{
266			Src: net.IPv4(127, 0, 0, 1),
267		}
268		if ifi != nil {
269			cm.IfIndex = ifi.Index
270		}
271		if err := p.SetControlMessage(cf, toggle); err != nil {
272			t.Error(err)
273			return
274		}
275		if n, err := p.WriteTo(wb, &cm, dst); err != nil {
276			t.Error(err)
277			return
278		} else if n != len(wb) {
279			t.Errorf("got %d; want %d", n, len(wb))
280			return
281		}
282	}
283
284	const N = 10
285	wg.Add(N)
286	for i := 0; i < N; i++ {
287		go reader()
288	}
289	wg.Add(2 * N)
290	for i := 0; i < 2*N; i++ {
291		go writer(i%2 != 0)
292	}
293	wg.Add(N)
294	for i := 0; i < N; i++ {
295		go reader()
296	}
297	wg.Wait()
298}
299
300func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) {
301	switch runtime.GOOS {
302	case "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
303		t.Skipf("not supported on %s", runtime.GOOS)
304	}
305
306	payload := []byte("HELLO-R-U-THERE")
307	iph, err := (&ipv4.Header{
308		Version:  ipv4.Version,
309		Len:      ipv4.HeaderLen,
310		TotalLen: ipv4.HeaderLen + len(payload),
311		TTL:      1,
312		Protocol: iana.ProtocolReserved,
313		Src:      net.IPv4(192, 0, 2, 1),
314		Dst:      net.IPv4(192, 0, 2, 254),
315	}).Marshal()
316	if err != nil {
317		t.Fatal(err)
318	}
319	greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}
320	datagram := append(greh, append(iph, payload...)...)
321
322	t.Run("UDP", func(t *testing.T) {
323		c, err := nettest.NewLocalPacketListener("udp4")
324		if err != nil {
325			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
326		}
327		defer c.Close()
328		p := ipv4.NewPacketConn(c)
329		t.Run("ToFrom", func(t *testing.T) {
330			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), false)
331		})
332		t.Run("Batch", func(t *testing.T) {
333			testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), true)
334		})
335	})
336	t.Run("IP", func(t *testing.T) {
337		switch runtime.GOOS {
338		case "netbsd":
339			t.Skip("need to configure gre on netbsd")
340		case "openbsd":
341			t.Skip("net.inet.gre.allow=0 by default on openbsd")
342		}
343
344		c, err := net.ListenPacket(fmt.Sprintf("ip4:%d", iana.ProtocolGRE), "127.0.0.1")
345		if err != nil {
346			t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
347		}
348		defer c.Close()
349		p := ipv4.NewPacketConn(c)
350		t.Run("ToFrom", func(t *testing.T) {
351			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), false)
352		})
353		t.Run("Batch", func(t *testing.T) {
354			testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), true)
355		})
356	})
357}
358
359func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv4.PacketConn, data []byte, dst net.Addr, batch bool) {
360	t.Helper()
361
362	ifi, _ := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback)
363	cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface
364
365	if err := p.SetControlMessage(cf, true); err != nil { // probe before test
366		if protocolNotSupported(err) {
367			t.Skipf("not supported on %s", runtime.GOOS)
368		}
369		t.Fatal(err)
370	}
371
372	var wg sync.WaitGroup
373	reader := func() {
374		defer wg.Done()
375		b := make([]byte, 128)
376		n, cm, _, err := p.ReadFrom(b)
377		if err != nil {
378			t.Error(err)
379			return
380		}
381		if !bytes.Equal(b[:n], data) {
382			t.Errorf("got %#v; want %#v", b[:n], data)
383			return
384		}
385		s := cm.String()
386		if strings.Contains(s, ",") {
387			t.Errorf("should be space-separated values: %s", s)
388			return
389		}
390	}
391	batchReader := func() {
392		defer wg.Done()
393		ms := []ipv4.Message{
394			{
395				Buffers: [][]byte{make([]byte, 128)},
396				OOB:     ipv4.NewControlMessage(cf),
397			},
398		}
399		n, err := p.ReadBatch(ms, 0)
400		if err != nil {
401			t.Error(err)
402			return
403		}
404		if n != len(ms) {
405			t.Errorf("got %d; want %d", n, len(ms))
406			return
407		}
408		var cm ipv4.ControlMessage
409		if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil {
410			t.Error(err)
411			return
412		}
413		var b []byte
414		if _, ok := dst.(*net.IPAddr); ok {
415			var h ipv4.Header
416			if err := h.Parse(ms[0].Buffers[0][:ms[0].N]); err != nil {
417				t.Error(err)
418				return
419			}
420			b = ms[0].Buffers[0][h.Len:ms[0].N]
421		} else {
422			b = ms[0].Buffers[0][:ms[0].N]
423		}
424		if !bytes.Equal(b, data) {
425			t.Errorf("got %#v; want %#v", b, data)
426			return
427		}
428		s := cm.String()
429		if strings.Contains(s, ",") {
430			t.Errorf("should be space-separated values: %s", s)
431			return
432		}
433	}
434	writer := func(toggle bool) {
435		defer wg.Done()
436		cm := ipv4.ControlMessage{
437			Src: net.IPv4(127, 0, 0, 1),
438		}
439		if ifi != nil {
440			cm.IfIndex = ifi.Index
441		}
442		if err := p.SetControlMessage(cf, toggle); err != nil {
443			t.Error(err)
444			return
445		}
446		n, err := p.WriteTo(data, &cm, dst)
447		if err != nil {
448			t.Error(err)
449			return
450		}
451		if n != len(data) {
452			t.Errorf("got %d; want %d", n, len(data))
453			return
454		}
455	}
456	batchWriter := func(toggle bool) {
457		defer wg.Done()
458		cm := ipv4.ControlMessage{
459			Src: net.IPv4(127, 0, 0, 1),
460		}
461		if ifi != nil {
462			cm.IfIndex = ifi.Index
463		}
464		if err := p.SetControlMessage(cf, toggle); err != nil {
465			t.Error(err)
466			return
467		}
468		ms := []ipv4.Message{
469			{
470				Buffers: [][]byte{data},
471				OOB:     cm.Marshal(),
472				Addr:    dst,
473			},
474		}
475		n, err := p.WriteBatch(ms, 0)
476		if err != nil {
477			t.Error(err)
478			return
479		}
480		if n != len(ms) {
481			t.Errorf("got %d; want %d", n, len(ms))
482			return
483		}
484		if ms[0].N != len(data) {
485			t.Errorf("got %d; want %d", ms[0].N, len(data))
486			return
487		}
488	}
489
490	const N = 10
491	wg.Add(N)
492	for i := 0; i < N; i++ {
493		if batch {
494			go batchReader()
495		} else {
496			go reader()
497		}
498	}
499	wg.Add(2 * N)
500	for i := 0; i < 2*N; i++ {
501		if batch {
502			go batchWriter(i%2 != 0)
503		} else {
504			go writer(i%2 != 0)
505		}
506
507	}
508	wg.Add(N)
509	for i := 0; i < N; i++ {
510		if batch {
511			go batchReader()
512		} else {
513			go reader()
514		}
515	}
516	wg.Wait()
517}
518