tun_tester.go 4.4 KB

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