tun_tester.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. //go:build e2e_testing
  2. // +build e2e_testing
  3. package overlay
  4. import (
  5. "context"
  6. "fmt"
  7. "io"
  8. "log/slog"
  9. "net/netip"
  10. "os"
  11. "sync/atomic"
  12. "github.com/gaissmai/bart"
  13. "github.com/slackhq/nebula/config"
  14. "github.com/slackhq/nebula/overlay/tio"
  15. "github.com/slackhq/nebula/routing"
  16. "github.com/slackhq/nebula/udp"
  17. "github.com/slackhq/nebula/wire"
  18. )
  19. type TestTun struct {
  20. Device string
  21. vpnNetworks []netip.Prefix
  22. Routes []Route
  23. routeTree *bart.Table[routing.Gateways]
  24. l *slog.Logger
  25. closed atomic.Bool
  26. rxPackets chan []byte // Packets to receive into nebula
  27. TxPackets chan []byte // Packets transmitted outside by nebula
  28. }
  29. func newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*TestTun, error) {
  30. _, routes, err := getAllRoutesFromConfig(c, vpnNetworks, true)
  31. if err != nil {
  32. return nil, err
  33. }
  34. routeTree, err := makeRouteTree(l, routes, false)
  35. if err != nil {
  36. return nil, err
  37. }
  38. return &TestTun{
  39. Device: c.GetString("tun.dev", ""),
  40. vpnNetworks: vpnNetworks,
  41. Routes: routes,
  42. routeTree: routeTree,
  43. l: l,
  44. rxPackets: make(chan []byte, 10),
  45. TxPackets: make(chan []byte, 10),
  46. }, nil
  47. }
  48. func newTunFromFd(_ *config.C, _ *slog.Logger, _ int, _ []netip.Prefix) (*TestTun, error) {
  49. return nil, fmt.Errorf("newTunFromFd not supported")
  50. }
  51. // Send will place a byte array onto the receive queue for nebula to consume.
  52. // These are unencrypted ip layer frames destined for another nebula node.
  53. // packets should exit the udp side, capture them with udpConn.Get.
  54. //
  55. // Send copies the input via the freelist, so the caller is free to mutate
  56. // or reuse it after the call returns.
  57. func (t *TestTun) Send(packet []byte) {
  58. if t.closed.Load() {
  59. return
  60. }
  61. if t.l.Enabled(context.Background(), slog.LevelDebug) {
  62. t.l.Debug("Tun receiving injected packet", "dataLen", len(packet))
  63. }
  64. buf := acquireTunBuf(len(packet))
  65. copy(buf, packet)
  66. t.rxPackets <- buf
  67. }
  68. // Get will pull an unencrypted ip layer frame from the transmit queue
  69. // nebula meant to send this message to some application on the local system
  70. // packets were ingested from the udp side, you can send them with udpConn.Send
  71. func (t *TestTun) Get(block bool) []byte {
  72. if block {
  73. return <-t.TxPackets
  74. }
  75. select {
  76. case p := <-t.TxPackets:
  77. return p
  78. default:
  79. return nil
  80. }
  81. }
  82. //********************************************************************************************************************//
  83. // Below this is boilerplate implementation to make nebula actually work
  84. //********************************************************************************************************************//
  85. func (t *TestTun) RoutesFor(ip netip.Addr) routing.Gateways {
  86. r, _ := t.routeTree.Lookup(ip)
  87. return r
  88. }
  89. func (t *TestTun) Activate() error {
  90. return nil
  91. }
  92. func (t *TestTun) Networks() []netip.Prefix {
  93. return t.vpnNetworks
  94. }
  95. func (t *TestTun) Name() string {
  96. return t.Device
  97. }
  98. func (t *TestTun) Write(b []byte) (n int, err error) {
  99. if t.closed.Load() {
  100. return 0, io.ErrClosedPipe
  101. }
  102. packet := acquireTunBuf(len(b))
  103. copy(packet, b)
  104. t.TxPackets <- packet
  105. return len(b), nil
  106. }
  107. // ReleaseTunBuf returns a slice from TxPackets to the harness freelist, don't use the bytes after the call.
  108. // Channel-backed instead of sync.Pool because putting a []byte in a sync.Pool escapes the slice header to heap.
  109. func ReleaseTunBuf(b []byte) {
  110. if b == nil {
  111. return
  112. }
  113. select {
  114. case tunBufFreelist <- b:
  115. default:
  116. // Freelist full; drop the buffer for the GC.
  117. }
  118. }
  119. // tunBufFreelist retains the backing arrays for TestTun.Write so steady-state allocation drops to zero once the
  120. // freelist has saturated for the current MTU.
  121. var tunBufFreelist = make(chan []byte, 64)
  122. func acquireTunBuf(n int) []byte {
  123. var b []byte
  124. select {
  125. case b = <-tunBufFreelist:
  126. default:
  127. b = make([]byte, 0, udp.MTU)
  128. }
  129. if cap(b) < n {
  130. b = make([]byte, n)
  131. } else {
  132. b = b[:n]
  133. }
  134. return b
  135. }
  136. func (t *TestTun) Close() error {
  137. if t.closed.CompareAndSwap(false, true) {
  138. close(t.rxPackets)
  139. close(t.TxPackets)
  140. }
  141. return nil
  142. }
  143. func (t *TestTun) Read(p []wire.TunPacket, mem []byte) (int, error) {
  144. if len(p) == 0 || len(mem) == 0 {
  145. return 0, nil //todo should this be an err?
  146. }
  147. p[0].Meta = struct{}{}
  148. n, err := t.read(mem)
  149. if err != nil {
  150. return 0, err
  151. }
  152. p[0].Bytes = mem[:n]
  153. return 1, nil
  154. }
  155. func (t *TestTun) read(b []byte) (int, error) {
  156. p, ok := <-t.rxPackets
  157. if !ok {
  158. return 0, os.ErrClosed
  159. }
  160. n := len(p)
  161. copy(b, p)
  162. // Send always pushes a freelist-acquired slice, return it once we've copied the bytes into the caller's buffer.
  163. select {
  164. case tunBufFreelist <- p:
  165. default:
  166. }
  167. return n, nil
  168. }
  169. func (t *TestTun) Readers() []tio.Queue {
  170. return []tio.Queue{t}
  171. }
  172. func (t *TestTun) Capabilities() tio.Capabilities {
  173. return tio.Capabilities{}
  174. }
  175. func (t *TestTun) SupportsMultiqueue() bool {
  176. return false
  177. }
  178. func (t *TestTun) NewMultiQueueReader() error {
  179. return fmt.Errorf("TODO: multiqueue not implemented")
  180. }