udp_tester.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. //go:build e2e_testing
  2. // +build e2e_testing
  3. package udp
  4. import (
  5. "context"
  6. "io"
  7. "log/slog"
  8. "net/netip"
  9. "os"
  10. "sync"
  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 netip.AddrPort
  59. RxPackets chan *Packet // Packets to receive into nebula
  60. TxPackets chan *Packet // Packets transmitted outside by nebula
  61. // done is closed exactly once by Close. Senders select on it so they
  62. // never race with a channel close; readers exit when it fires. The
  63. // packet channels are intentionally never closed - that was the source
  64. // of `send on closed channel` panics when a WriteTo/Send from another
  65. // goroutine passed the close check and reached the send just after
  66. // Close ran.
  67. done chan struct{}
  68. closeOnce sync.Once
  69. l *slog.Logger
  70. }
  71. func NewListener(l *slog.Logger, ip netip.Addr, port int, _ bool, _ int) (Conn, error) {
  72. return &TesterConn{
  73. Addr: netip.AddrPortFrom(ip, uint16(port)),
  74. RxPackets: make(chan *Packet, 10),
  75. TxPackets: make(chan *Packet, 10),
  76. done: make(chan struct{}),
  77. l: l,
  78. }, nil
  79. }
  80. // Send will place a UdpPacket onto the receive queue for nebula to consume
  81. // this is an encrypted packet or a handshake message in most cases
  82. // packets were transmitted from another nebula node, you can send them with Tun.Send
  83. func (u *TesterConn) Send(packet *Packet) {
  84. if u.l.Enabled(context.Background(), slog.LevelDebug) {
  85. // Parse the header only under debug logging, otherwise the
  86. // allocation would show up in every Send call.
  87. var h header.H
  88. if err := h.Parse(packet.Data); err != nil {
  89. panic(err)
  90. }
  91. u.l.Debug("UDP receiving injected packet",
  92. "header", &h,
  93. "udpAddr", packet.From,
  94. "dataLen", len(packet.Data),
  95. )
  96. }
  97. select {
  98. case <-u.done:
  99. case u.RxPackets <- packet:
  100. }
  101. }
  102. // Get will pull a UdpPacket from the transmit queue
  103. // nebula meant to send this message on the network, it will be encrypted
  104. // packets were ingested from the tun side (in most cases), you can send them with Tun.Send
  105. func (u *TesterConn) Get(block bool) *Packet {
  106. if block {
  107. select {
  108. case <-u.done:
  109. return nil
  110. case p := <-u.TxPackets:
  111. return p
  112. }
  113. }
  114. select {
  115. case p := <-u.TxPackets:
  116. return p
  117. default:
  118. return nil
  119. }
  120. }
  121. //********************************************************************************************************************//
  122. // Below this is boilerplate implementation to make nebula actually work
  123. //********************************************************************************************************************//
  124. func (u *TesterConn) WriteTo(b []byte, addr netip.AddrPort) error {
  125. p := acquirePacket()
  126. if cap(p.Data) < len(b) {
  127. p.Data = make([]byte, len(b))
  128. } else {
  129. p.Data = p.Data[:len(b)]
  130. }
  131. copy(p.Data, b)
  132. p.From = u.Addr
  133. p.To = addr
  134. select {
  135. case <-u.done:
  136. p.Release()
  137. return io.ErrClosedPipe
  138. case u.TxPackets <- p:
  139. return nil
  140. }
  141. }
  142. func (u *TesterConn) ListenOut(r EncReader) error {
  143. for {
  144. select {
  145. case <-u.done:
  146. return os.ErrClosed
  147. case p := <-u.RxPackets:
  148. r(p.From, p.Data)
  149. p.Release()
  150. }
  151. }
  152. }
  153. func (u *TesterConn) ReloadConfig(*config.C) {}
  154. func NewUDPStatsEmitter(_ []Conn) func() {
  155. // No UDP stats for non-linux
  156. return func() {}
  157. }
  158. func (u *TesterConn) LocalAddr() (netip.AddrPort, error) {
  159. return u.Addr, nil
  160. }
  161. func (u *TesterConn) SupportsMultipleReaders() bool {
  162. return false
  163. }
  164. func (u *TesterConn) Rebind() error {
  165. return nil
  166. }
  167. func (u *TesterConn) Close() error {
  168. u.closeOnce.Do(func() {
  169. close(u.done)
  170. })
  171. return nil
  172. }