golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/ipv4/readwrite_test.go (about) 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 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 { // probe before test 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 // On the first error, close the PacketConn to unblock the remaining 250 // goroutines. Suppress any further errors, which may occur simply due to 251 // closing the PacketConn. 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 { // probe before test 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 // On the first error, close the PacketConn to unblock the remaining 400 // goroutines. Suppress any further errors, which may occur simply due to 401 // closing the PacketConn. 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 }