golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/ipv6/readwrite_test.go (about) 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 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 { // 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 firstError sync.Once 250 fatalf := func(format string, args ...interface{}) { 251 // On the first error, close the PacketConn to unblock the remaining 252 // goroutines. Suppress any further errors, which may occur simply due to 253 // closing the PacketConn. 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 { // probe before test 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 // On the first error, close the PacketConn to unblock the remaining 396 // goroutines. Suppress any further errors, which may occur simply due to 397 // closing the PacketConn. 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 }