udp_tester.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. //go:build e2e_testing
  2. package udp
  3. import (
  4. "context"
  5. "io"
  6. "log/slog"
  7. "net/netip"
  8. "os"
  9. "sync"
  10. "sync/atomic"
  11. "github.com/slackhq/nebula/config"
  12. "github.com/slackhq/nebula/header"
  13. )
  14. type Packet struct {
  15. To netip.AddrPort
  16. From netip.AddrPort
  17. Data []byte
  18. }
  19. // Copy returns a fresh *Packet (from the freelist) with a duplicate Data buffer.
  20. func (u *Packet) Copy() *Packet {
  21. n := acquirePacket()
  22. n.To = u.To
  23. n.From = u.From
  24. if cap(n.Data) < len(u.Data) {
  25. n.Data = make([]byte, len(u.Data))
  26. } else {
  27. n.Data = n.Data[:len(u.Data)]
  28. }
  29. copy(n.Data, u.Data)
  30. return n
  31. }
  32. // Release returns p to the harness packet freelist.
  33. // Callers that pull a *Packet from Get / TxPackets must Release when done.
  34. // Channel-backed instead of sync.Pool because sync.Pool's per-P caches drain badly under cross-goroutine Get/Put,
  35. // and putting a []byte in a Pool escapes the slice header to heap.
  36. func (p *Packet) Release() {
  37. if p == nil {
  38. return
  39. }
  40. p.Data = p.Data[:0]
  41. select {
  42. case packetFreelist <- p:
  43. default:
  44. // Freelist full; drop the *Packet for the GC.
  45. }
  46. }
  47. // packetFreelist retains *Packet structs (and their backing Data arrays) so steady-state allocation drops to zero.
  48. var packetFreelist = make(chan *Packet, 64)
  49. func acquirePacket() *Packet {
  50. select {
  51. case p := <-packetFreelist:
  52. return p
  53. default:
  54. return &Packet{}
  55. }
  56. }
  57. type TesterConn struct {
  58. // addr is read by nebula's own goroutines on every send and by the router's flow renderer, and a test can
  59. // move it mid-run to simulate roaming, so it is atomic rather than a plain field.
  60. addr atomic.Pointer[netip.AddrPort]
  61. RxPackets chan *Packet // Packets to receive into nebula
  62. TxPackets chan *Packet // Packets transmitted outside by nebula
  63. // done is closed exactly once by Close. Senders select on it so they
  64. // never race with a channel close; readers exit when it fires. The
  65. // packet channels are intentionally never closed - that was the source
  66. // of `send on closed channel` panics when a WriteTo/Send from another
  67. // goroutine passed the close check and reached the send just after
  68. // Close ran.
  69. done chan struct{}
  70. closeOnce sync.Once
  71. l *slog.Logger
  72. }
  73. func NewListener(l *slog.Logger, s Settings) (Conn, error) {
  74. c := &TesterConn{
  75. RxPackets: make(chan *Packet, 10),
  76. TxPackets: make(chan *Packet, 10),
  77. done: make(chan struct{}),
  78. l: l,
  79. }
  80. c.SetAddr(s.Listen)
  81. return c, nil
  82. }
  83. // GetAddr returns the underlay address this conn currently sends from.
  84. func (u *TesterConn) GetAddr() netip.AddrPort {
  85. return *u.addr.Load()
  86. }
  87. // SetAddr moves this conn to a new underlay address, standing in for a host waking up on a different network.
  88. func (u *TesterConn) SetAddr(addr netip.AddrPort) {
  89. u.addr.Store(&addr)
  90. }
  91. // Send will place a UdpPacket onto the receive queue for nebula to consume
  92. // this is an encrypted packet or a handshake message in most cases
  93. // packets were transmitted from another nebula node, you can send them with Tun.Send
  94. func (u *TesterConn) Send(packet *Packet) {
  95. if u.l.Enabled(context.Background(), slog.LevelDebug) {
  96. // Parse the header only under debug logging, otherwise the
  97. // allocation would show up in every Send call.
  98. var h header.H
  99. if err := h.Parse(packet.Data); err != nil {
  100. panic(err)
  101. }
  102. u.l.Debug("UDP receiving injected packet",
  103. "header", &h,
  104. "udpAddr", packet.From,
  105. "dataLen", len(packet.Data),
  106. )
  107. }
  108. select {
  109. case <-u.done:
  110. case u.RxPackets <- packet:
  111. }
  112. }
  113. // Get will pull a UdpPacket from the transmit queue
  114. // nebula meant to send this message on the network, it will be encrypted
  115. // packets were ingested from the tun side (in most cases), you can send them with Tun.Send
  116. func (u *TesterConn) Get(block bool) *Packet {
  117. if block {
  118. select {
  119. case <-u.done:
  120. return nil
  121. case p := <-u.TxPackets:
  122. return p
  123. }
  124. }
  125. select {
  126. case p := <-u.TxPackets:
  127. return p
  128. default:
  129. return nil
  130. }
  131. }
  132. //********************************************************************************************************************//
  133. // Below this is boilerplate implementation to make nebula actually work
  134. //********************************************************************************************************************//
  135. func (u *TesterConn) WriteTo(b []byte, addr netip.AddrPort) error {
  136. p := acquirePacket()
  137. if cap(p.Data) < len(b) {
  138. p.Data = make([]byte, len(b))
  139. } else {
  140. p.Data = p.Data[:len(b)]
  141. }
  142. copy(p.Data, b)
  143. p.From = u.GetAddr()
  144. p.To = addr
  145. select {
  146. case <-u.done:
  147. p.Release()
  148. return io.ErrClosedPipe
  149. case u.TxPackets <- p:
  150. return nil
  151. }
  152. }
  153. func (u *TesterConn) WriteBatch(bufs [][]byte, addrs []netip.AddrPort) (int, error) {
  154. written := 0
  155. for i, b := range bufs {
  156. if err := u.WriteTo(b, addrs[i]); err == nil {
  157. written++
  158. } else {
  159. u.l.Debug("failed to write packet in batch", "udpAddr", addrs[i], "error", err)
  160. }
  161. }
  162. return written, nil
  163. }
  164. func (u *TesterConn) ListenOut(r EncReader, flush func()) error {
  165. // The batcher borrows plaintext decrypted in place inside p.Data
  166. // until Flush, so the packet must stay alive across flush()
  167. return u.listenOut(r, func(p *Packet) { flush() })
  168. }
  169. // ListenOutHandoff hands each packet's buffer over, the replacement goes back to the pool.
  170. func (u *TesterConn) ListenOutHandoff(r EncReader, handoff func(bufs [][]byte) [][]byte) error {
  171. return u.listenOut(r, func(p *Packet) { p.Data = handoff([][]byte{p.Data})[0] })
  172. }
  173. func (u *TesterConn) listenOut(r EncReader, finish func(*Packet)) error {
  174. for {
  175. select {
  176. case <-u.done:
  177. return os.ErrClosed
  178. case p := <-u.RxPackets:
  179. r(p.From, p.Data[:len(p.Data):len(p.Data)])
  180. finish(p)
  181. p.Release()
  182. }
  183. }
  184. }
  185. func (u *TesterConn) ReloadConfig(*config.C, *config.Reload) {}
  186. func NewUDPStatsEmitter(_ []Conn) func() {
  187. // No UDP stats for non-linux
  188. return func() {}
  189. }
  190. func (u *TesterConn) LocalAddr() (netip.AddrPort, error) {
  191. return u.GetAddr(), nil
  192. }
  193. func (u *TesterConn) SupportsMultipleReaders() bool {
  194. return false
  195. }
  196. func (u *TesterConn) Rebind() error {
  197. return nil
  198. }
  199. func (u *TesterConn) Close() error {
  200. u.closeOnce.Do(func() {
  201. close(u.done)
  202. })
  203. return nil
  204. }