tun_darwin_test.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. //go:build !ios && !e2e_testing
  2. package overlay
  3. import (
  4. "bytes"
  5. "os"
  6. "syscall"
  7. "testing"
  8. "github.com/slackhq/nebula/test"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/require"
  11. "golang.org/x/sys/unix"
  12. )
  13. // newSocketpairTun returns a tun writing to one end of a connected AF_UNIX datagram socketpair, which
  14. // takes sendmsg_x the way a utun control socket does, and the other end's fd for reading back.
  15. // sndbuf is the writer's send buffer, which caps the largest datagram it accepts.
  16. func newSocketpairTun(t *testing.T, sndbuf int) (*tun, int) {
  17. t.Helper()
  18. fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_DGRAM, 0)
  19. require.NoError(t, err)
  20. require.NoError(t, unix.SetsockoptInt(fds[0], unix.SOL_SOCKET, unix.SO_SNDBUF, sndbuf))
  21. require.NoError(t, unix.SetsockoptInt(fds[1], unix.SOL_SOCKET, unix.SO_RCVBUF, 1<<20))
  22. // A dropped datagram fails the read-back instead of hanging it.
  23. require.NoError(t, unix.SetsockoptTimeval(fds[1], unix.SOL_SOCKET, unix.SO_RCVTIMEO, &unix.Timeval{Sec: 2}))
  24. require.NoError(t, unix.SetNonblock(fds[0], true))
  25. f := os.NewFile(uintptr(fds[0]), "socketpair")
  26. t.Cleanup(func() {
  27. _ = f.Close()
  28. _ = unix.Close(fds[1])
  29. })
  30. return &tun{f: f, l: test.NewLogger()}, fds[1]
  31. }
  32. // testTunPkt returns packet i of size bytes, alternating IPv4 and IPv6, and the datagram the utun
  33. // device should receive for it.
  34. func testTunPkt(i, size int) (pkt, wire []byte) {
  35. pkt = make([]byte, size)
  36. pkt[0] = 0x45
  37. af := byte(syscall.AF_INET)
  38. if i%2 == 1 {
  39. pkt[0] = 0x60
  40. af = syscall.AF_INET6
  41. }
  42. pkt[1] = byte(i)
  43. pkt[2] = byte(i >> 8)
  44. return pkt, append([]byte{0, 0, 0, af}, pkt...)
  45. }
  46. // readTunBack asserts r receives exactly want, in order.
  47. func readTunBack(t *testing.T, r int, want [][]byte) {
  48. t.Helper()
  49. buf := make([]byte, 4096)
  50. for i, w := range want {
  51. n, _, err := unix.Recvfrom(r, buf, 0)
  52. require.NoError(t, err, "datagram %d", i)
  53. if !bytes.Equal(w, buf[:n]) {
  54. t.Fatalf("datagram %d: got % x, want % x", i, buf[:n], w)
  55. }
  56. }
  57. _, _, err := unix.Recvfrom(r, buf, unix.MSG_DONTWAIT)
  58. assert.ErrorIs(t, err, unix.EAGAIN, "extra datagrams delivered")
  59. }
  60. // forEachTunWritePath runs fn once over sendmsg_x and once over the per-packet writev fallback.
  61. func forEachTunWritePath(t *testing.T, fn func(t *testing.T, fallback bool)) {
  62. for _, fallback := range []bool{false, true} {
  63. name := "sendmsg_x"
  64. if fallback {
  65. name = "writev"
  66. }
  67. t.Run(name, func(t *testing.T) { fn(t, fallback) })
  68. }
  69. }
  70. // TestTunWriteBatch pins that WriteBatch delivers every packet whole, in order and behind the right
  71. // utun AF prefix, across several sendmsg_x calls, and returns nil.
  72. func TestTunWriteBatch(t *testing.T) {
  73. forEachTunWritePath(t, func(t *testing.T, fallback bool) {
  74. tn, r := newSocketpairTun(t, 1<<20)
  75. tn.noSendmsgX.Store(fallback)
  76. var pkts, want [][]byte
  77. for i := range 3*tunWriteBatch + 5 {
  78. p, w := testTunPkt(i, 60+i)
  79. pkts = append(pkts, p)
  80. want = append(want, w)
  81. }
  82. require.NoError(t, tn.WriteBatch(pkts))
  83. assert.Equal(t, fallback, tn.noSendmsgX.Load())
  84. readTunBack(t, r, want)
  85. })
  86. }
  87. // TestTunWriteBatchSkipsInvalid pins that an empty packet or one with no IP version is skipped
  88. // without shifting the packets after it, and that the first such error is the one reported.
  89. func TestTunWriteBatchSkipsInvalid(t *testing.T) {
  90. forEachTunWritePath(t, func(t *testing.T, fallback bool) {
  91. tn, r := newSocketpairTun(t, 1<<20)
  92. tn.noSendmsgX.Store(fallback)
  93. var pkts, want [][]byte
  94. for i := range 20 {
  95. p, w := testTunPkt(i, 60)
  96. pkts = append(pkts, p)
  97. want = append(want, w)
  98. }
  99. pkts = append(pkts[:10], append([][]byte{{0x10, 1, 2}, {}}, pkts[10:]...)...)
  100. err := tn.WriteBatch(pkts)
  101. assert.ErrorContains(t, err, "IP version")
  102. readTunBack(t, r, want)
  103. })
  104. }
  105. // TestTunWriteBatchOversize pins that a packet larger than the socket's send buffer drops only
  106. // itself and is reported, whether sendmsg_x reaches it mid-call (a short count) or first (EMSGSIZE
  107. // for the whole call). The send buffer is 1024 bytes and the oversize packet 2000.
  108. func TestTunWriteBatchOversize(t *testing.T) {
  109. for _, tc := range []struct {
  110. name string
  111. at int
  112. }{
  113. {"mid-call", tunWriteBatch + 6},
  114. {"first-in-call", tunWriteBatch},
  115. } {
  116. t.Run(tc.name, func(t *testing.T) {
  117. forEachTunWritePath(t, func(t *testing.T, fallback bool) {
  118. tn, r := newSocketpairTun(t, 1024)
  119. tn.noSendmsgX.Store(fallback)
  120. var pkts, want [][]byte
  121. for i := range 2*tunWriteBatch + 10 {
  122. size := 100
  123. if i == tc.at {
  124. size = 2000
  125. }
  126. p, w := testTunPkt(i, size)
  127. pkts = append(pkts, p)
  128. if i != tc.at {
  129. want = append(want, w)
  130. }
  131. }
  132. err := tn.WriteBatch(pkts)
  133. assert.ErrorIs(t, err, unix.EMSGSIZE)
  134. assert.Equal(t, fallback, tn.noSendmsgX.Load())
  135. readTunBack(t, r, want)
  136. })
  137. })
  138. }
  139. }
  140. // TestTunQueueReadDrains pins that Read hands back every queued packet, AF prefix stripped and in
  141. // order, capped at tunReadBatch per call, and doesn't wait for more once the queue is empty.
  142. func TestTunQueueReadDrains(t *testing.T) {
  143. tn, w := newSocketpairTun(t, 1<<20)
  144. // An AF_UNIX datagram send is bounded by the receiver's buffer, here the tun's end.
  145. rc, err := tn.f.SyscallConn()
  146. require.NoError(t, err)
  147. require.NoError(t, rc.Control(func(fd uintptr) {
  148. require.NoError(t, unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF, 1<<20))
  149. }))
  150. q := &tunQueue{t: tn, buf: make([]byte, tunReadArena)}
  151. total := tunReadBatch + 3
  152. var want [][]byte
  153. for i := range total {
  154. pkt, wire := testTunPkt(i, 60+i)
  155. _, err := unix.Write(w, wire)
  156. require.NoError(t, err)
  157. want = append(want, pkt)
  158. }
  159. var got [][]byte
  160. for _, size := range []int{tunReadBatch, 3} {
  161. pkts, err := q.Read()
  162. require.NoError(t, err)
  163. require.Len(t, pkts, size)
  164. for _, p := range pkts {
  165. got = append(got, p.Clone().Bytes)
  166. }
  167. }
  168. assert.Equal(t, want, got)
  169. }
  170. // TestTunQueueReadClipsPackets pins that each returned packet's capacity ends at its own bytes, so
  171. // appending to one can't overwrite the next.
  172. func TestTunQueueReadClipsPackets(t *testing.T) {
  173. tn, w := newSocketpairTun(t, 1<<20)
  174. q := &tunQueue{t: tn, buf: make([]byte, tunReadArena)}
  175. for i := range 2 {
  176. _, wire := testTunPkt(i, 100)
  177. _, err := unix.Write(w, wire)
  178. require.NoError(t, err)
  179. }
  180. pkts, err := q.Read()
  181. require.NoError(t, err)
  182. require.Len(t, pkts, 2)
  183. assert.Equal(t, len(pkts[0].Bytes), cap(pkts[0].Bytes))
  184. }