1
0

user_test.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package overlay
  2. import (
  3. "fmt"
  4. "net/netip"
  5. "sync"
  6. "testing"
  7. "github.com/slackhq/nebula/overlay/tio"
  8. )
  9. // newTestUserDevice returns the concrete *UserDevice so tests can reach Pipe()
  10. // and the internal queue plumbing.
  11. func newTestUserDevice(t *testing.T) *UserDevice {
  12. t.Helper()
  13. dev, err := NewUserDevice([]netip.Prefix{netip.MustParsePrefix("10.0.0.1/24")})
  14. if err != nil {
  15. t.Fatalf("NewUserDevice: %v", err)
  16. }
  17. ud, ok := dev.(*UserDevice)
  18. if !ok {
  19. t.Fatalf("NewUserDevice returned %T, want *UserDevice", dev)
  20. }
  21. return ud
  22. }
  23. // TestUserDeviceReadersDistinctBuffers ensures each Queue is actually different
  24. func TestUserDeviceReadersDistinctBuffers(t *testing.T) {
  25. d := newTestUserDevice(t)
  26. readers, err := d.Queues(2)
  27. if err != nil {
  28. t.Fatalf("Queues: %v", err)
  29. }
  30. if len(readers) != 2 {
  31. t.Fatalf("Queues(2) returned %d queues, want 2", len(readers))
  32. }
  33. // Distinct queue objects.
  34. if readers[0] == readers[1] {
  35. t.Fatal("Queues(2) returned the same queue object twice")
  36. }
  37. // Drive one packet through each queue and confirm the borrowed bytes from
  38. // the first read are NOT clobbered by the second read. With a shared
  39. // buffer, reading pkt1 into q1 would corrupt q0's still-borrowed slice.
  40. _, ow := d.Pipe()
  41. pkt0 := []byte("packet-zero-aaaaaaaa")
  42. pkt1 := []byte("packet-one-bbbbbbbbb")
  43. // The pipe is unbuffered, so writes block until a reader consumes them.
  44. // Serialize: write pkt0 (read on q0), then write pkt1 (read on q1).
  45. go func() {
  46. if _, err := ow.Write(pkt0); err != nil {
  47. t.Errorf("write pkt0: %v", err)
  48. }
  49. if _, err := ow.Write(pkt1); err != nil {
  50. t.Errorf("write pkt1: %v", err)
  51. }
  52. }()
  53. got0, err := readers[0].Read()
  54. if err != nil {
  55. t.Fatalf("q0.Read: %v", err)
  56. }
  57. if len(got0) != 1 || string(got0[0].Bytes) != string(pkt0) {
  58. t.Fatalf("q0 first read = %q, want %q", firstBytes(got0), pkt0)
  59. }
  60. // Hold onto q0's borrowed slice across q1's read.
  61. borrowed := got0[0].Bytes
  62. got1, err := readers[1].Read()
  63. if err != nil {
  64. t.Fatalf("q1.Read: %v", err)
  65. }
  66. if len(got1) != 1 || string(got1[0].Bytes) != string(pkt1) {
  67. t.Fatalf("q1 read = %q, want %q", firstBytes(got1), pkt1)
  68. }
  69. // q0's borrowed bytes must still hold pkt0 - a shared buffer would now
  70. // show pkt1's contents.
  71. if string(borrowed) != string(pkt0) {
  72. t.Fatalf("q0 borrowed bytes were clobbered by q1's read: got %q, want %q", borrowed, pkt0)
  73. }
  74. }
  75. // TestUserDeviceReadersConcurrentRace exercises two queues reading distinct
  76. // packets concurrently. Run it under `go test -race`: with the old
  77. // shared-buffer implementation the concurrent Reads raced on readBuf/batchRet
  78. // and corrupted each other's returned slices.
  79. func TestUserDeviceReadersConcurrentRace(t *testing.T) {
  80. d := newTestUserDevice(t)
  81. readers, err := d.Queues(2)
  82. if err != nil {
  83. t.Fatalf("Queues: %v", err)
  84. }
  85. _, ow := d.Pipe()
  86. const iterations = 200
  87. errs := make(chan error, 3)
  88. // Each reader parks in Read on the shared outboundReader; io.Pipe hands
  89. // each write to whichever reader is currently waiting. We only care that
  90. // concurrent Reads into distinct buffers are race-free, so any parked
  91. // reader may serve any write.
  92. var wg sync.WaitGroup
  93. run := func(idx int) {
  94. defer wg.Done()
  95. for range iterations {
  96. pkts, err := readers[idx].Read()
  97. if err != nil {
  98. errs <- err
  99. return
  100. }
  101. if len(pkts) != 1 {
  102. errs <- fmt.Errorf("reader %d: got %d packets, want 1", idx, len(pkts))
  103. return
  104. }
  105. // Touch every byte of the borrowed slice while the other reader
  106. // may be mid-Read; a shared buffer would race here.
  107. total := 0
  108. for _, c := range pkts[0].Bytes {
  109. total += int(c)
  110. }
  111. _ = total
  112. }
  113. }
  114. wg.Add(2)
  115. go run(0)
  116. go run(1)
  117. // Feed 2*iterations packets. io.Pipe copies each write straight into the
  118. // waiting reader's private buffer, so reusing buf between writes is safe.
  119. go func() {
  120. buf := make([]byte, 32)
  121. for i := range 2 * iterations {
  122. for j := range buf {
  123. buf[j] = byte(i + j)
  124. }
  125. if _, err := ow.Write(buf); err != nil {
  126. errs <- err
  127. return
  128. }
  129. }
  130. }()
  131. wg.Wait()
  132. select {
  133. case err := <-errs:
  134. t.Fatalf("concurrent reader failed: %v", err)
  135. default:
  136. }
  137. }
  138. func firstBytes(p []tio.Packet) []byte {
  139. if len(p) == 0 {
  140. return nil
  141. }
  142. return p[0].Bytes
  143. }