tun_file_linux_test.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. //go:build linux && !android && !e2e_testing
  2. // +build linux,!android,!e2e_testing
  3. package tio
  4. import (
  5. "errors"
  6. "os"
  7. "sync"
  8. "testing"
  9. "time"
  10. "github.com/slackhq/nebula/wire"
  11. "github.com/stretchr/testify/require"
  12. "golang.org/x/sys/unix"
  13. )
  14. // newReadPipe returns a read fd. The matching write fd is registered for cleanup.
  15. // The caller takes ownership of the read fd (pass it into a QueueSet).
  16. func newReadPipe(t *testing.T) int {
  17. t.Helper()
  18. var fds [2]int
  19. if err := unix.Pipe2(fds[:], unix.O_CLOEXEC); err != nil {
  20. t.Fatalf("pipe2: %v", err)
  21. }
  22. t.Cleanup(func() { _ = unix.Close(fds[1]) })
  23. return fds[0]
  24. }
  25. func TestPoll_WakeForShutdown_WakesFriends(t *testing.T) {
  26. parent, err := NewPollQueueSet()
  27. require.NoError(t, err)
  28. require.NoError(t, parent.Add(newReadPipe(t)))
  29. require.NoError(t, parent.Add(newReadPipe(t)))
  30. // QueueSet.Close owns the read fds we Added — don't register a separate
  31. // Cleanup to close them or we'll double-close whatever fd the kernel
  32. // has since reused.
  33. readers := parent.Queues()
  34. errs := make([]error, len(readers))
  35. var wg sync.WaitGroup
  36. for i, r := range readers {
  37. wg.Add(1)
  38. go func(i int, r Queue) {
  39. defer wg.Done()
  40. pkts := make([]wire.TunPacket, 1)
  41. _, errs[i] = r.Read(pkts, make([]byte, 64))
  42. }(i, r)
  43. }
  44. time.Sleep(50 * time.Millisecond)
  45. if err := parent.Close(); err != nil {
  46. t.Fatalf("Close: %v", err)
  47. }
  48. done := make(chan struct{})
  49. go func() { wg.Wait(); close(done) }()
  50. select {
  51. case <-done:
  52. case <-time.After(2 * time.Second):
  53. t.Fatal("readers did not wake")
  54. }
  55. for i, err := range errs {
  56. if !errors.Is(err, os.ErrClosed) {
  57. t.Errorf("reader %d: expected os.ErrClosed, got %v", i, err)
  58. }
  59. }
  60. }
  61. func TestPoll_Close_Idempotent(t *testing.T) {
  62. shutdownFd, err := unix.Eventfd(0, unix.EFD_NONBLOCK|unix.EFD_CLOEXEC)
  63. require.NoError(t, err)
  64. t.Cleanup(func() { _ = unix.Close(shutdownFd) })
  65. tf, err := newPoll(newReadPipe(t), shutdownFd)
  66. require.NoError(t, err)
  67. if err := tf.Close(); err != nil {
  68. t.Fatalf("first Close: %v", err)
  69. }
  70. if err := tf.Close(); err != nil {
  71. t.Fatalf("second Close should be a no-op, got %v", err)
  72. }
  73. }
  74. func TestPollQueueSet_Close_ClosesEventfd(t *testing.T) {
  75. qs, err := NewPollQueueSet()
  76. require.NoError(t, err)
  77. require.NoError(t, qs.Add(newReadPipe(t)))
  78. fd := qs.(*pollQueueSet).shutdownFd
  79. require.NoError(t, qs.Close())
  80. // Closing the eventfd again should fail with EBADF, proving Close
  81. // actually released it.
  82. if err := unix.Close(fd); err == nil {
  83. t.Fatalf("eventfd %d still open after QueueSet.Close", fd)
  84. }
  85. // Second Close must be a no-op (and must not double-close the eventfd
  86. // in case the kernel handed it out to another caller in the meantime).
  87. if err := qs.Close(); err != nil {
  88. t.Fatalf("second Close: %v", err)
  89. }
  90. }