1
0

tun_darwin_test.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. //go:build !ios && !e2e_testing
  2. package overlay
  3. import (
  4. "bytes"
  5. "os"
  6. "syscall"
  7. "testing"
  8. "github.com/stretchr/testify/require"
  9. )
  10. func TestTun_WriteFraming(t *testing.T) {
  11. r, w, err := os.Pipe()
  12. require.NoError(t, err)
  13. defer r.Close()
  14. defer w.Close()
  15. tn := &tun{f: w}
  16. for _, tc := range []struct {
  17. name string
  18. pkt []byte
  19. af byte
  20. }{
  21. // Literal families rather than syscall.AF_*, so a wrong constant in Write can't also fix the expectation.
  22. {"ipv4", []byte{0x45, 1, 2, 3, 4, 5}, 2},
  23. {"ipv6", []byte{0x60, 1, 2, 3, 4, 5}, 30},
  24. } {
  25. t.Run(tc.name, func(t *testing.T) {
  26. n, err := tn.Write(tc.pkt)
  27. require.NoError(t, err)
  28. require.Equal(t, len(tc.pkt), n)
  29. got := make([]byte, 64)
  30. n, err = r.Read(got)
  31. require.NoError(t, err)
  32. require.Equal(t, append([]byte{0, 0, 0, tc.af}, tc.pkt...), got[:n])
  33. })
  34. }
  35. _, err = tn.Write([]byte{0x10, 1, 2, 3})
  36. require.Error(t, err)
  37. _, err = tn.Write([]byte{})
  38. require.ErrorIs(t, err, syscall.EIO)
  39. // The rejected writes must not have put anything on the fd, so the next read sees only this packet.
  40. pkt := []byte{0x45, 9, 9}
  41. _, err = tn.Write(pkt)
  42. require.NoError(t, err)
  43. got := make([]byte, 64)
  44. n, err := r.Read(got)
  45. require.NoError(t, err)
  46. require.Equal(t, append([]byte{0, 0, 0, 2}, pkt...), got[:n])
  47. }
  48. func TestTun_ReadStripsHeader(t *testing.T) {
  49. r, w, err := os.Pipe()
  50. require.NoError(t, err)
  51. defer r.Close()
  52. defer w.Close()
  53. tn := &tun{f: r}
  54. pkt := []byte{0x45, 1, 2, 3, 4, 5}
  55. _, err = w.Write(append([]byte{0, 0, 0, syscall.AF_INET}, pkt...))
  56. require.NoError(t, err)
  57. got := make([]byte, 64)
  58. n, err := tn.Read(got)
  59. require.NoError(t, err)
  60. require.Equal(t, pkt, got[:n])
  61. }
  62. func TestTun_ReadShort(t *testing.T) {
  63. r, w, err := os.Pipe()
  64. require.NoError(t, err)
  65. defer r.Close()
  66. defer w.Close()
  67. tn := &tun{f: r}
  68. // Fewer bytes than the protocol header is not a packet.
  69. _, err = w.Write([]byte{0, 0})
  70. require.NoError(t, err)
  71. n, err := tn.Read(make([]byte, 64))
  72. require.NoError(t, err)
  73. require.Zero(t, n)
  74. // The next packet reads cleanly only if the short read consumed exactly what was written.
  75. pkt := []byte{0x45, 1, 2, 3}
  76. _, err = w.Write(append([]byte{0, 0, 0, syscall.AF_INET}, pkt...))
  77. require.NoError(t, err)
  78. got := make([]byte, 64)
  79. n, err = tn.Read(got)
  80. require.NoError(t, err)
  81. require.Equal(t, pkt, got[:n])
  82. }
  83. // A heap allocation per packet rarely shows in a short throughput run, only across GC pauses, so
  84. // this pins Read and Write to zero allocations directly.
  85. func TestTun_ReadWriteDoNotAllocate(t *testing.T) {
  86. r, w, err := os.Pipe()
  87. require.NoError(t, err)
  88. defer r.Close()
  89. defer w.Close()
  90. tw := &tun{f: w}
  91. tr := &tun{f: r}
  92. pkts := [][]byte{bytes.Repeat([]byte{0x45}, 1300), bytes.Repeat([]byte{0x60}, 1300)}
  93. buf := make([]byte, 1500)
  94. i := 0
  95. allocs := testing.AllocsPerRun(1000, func() {
  96. i++
  97. if _, err := tw.Write(pkts[i%len(pkts)]); err != nil {
  98. t.Fatal(err)
  99. }
  100. if _, err := tr.Read(buf); err != nil {
  101. t.Fatal(err)
  102. }
  103. })
  104. require.Zero(t, allocs)
  105. }