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