flow_rx.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package nebula
  2. import (
  3. "log/slog"
  4. "net/netip"
  5. "sync"
  6. "github.com/rcrowley/go-metrics"
  7. "github.com/slackhq/nebula/header"
  8. "github.com/slackhq/nebula/udp"
  9. )
  10. // handoffConn is a udp conn that receives in batches of a fixed shape, so staged receive can have replacement buffers
  11. // ready for every batch it keeps.
  12. type handoffConn interface {
  13. ListenOut(udp.EncReader, func([][]byte) [][]byte) error
  14. HandoffShape() (n, size int)
  15. }
  16. const stagedRxSlots = 6
  17. const rxChunkMin = 16
  18. // rxItem is one received packet. A data packet for an established tunnel has ci set; out and err are written by
  19. // whoever decrypts it.
  20. type rxItem struct {
  21. packet []byte
  22. ci *ConnectionState
  23. counter uint64
  24. via netip.AddrPort
  25. out []byte
  26. err error
  27. }
  28. // rxChunk is a contiguous run of a batch's items for one worker to decrypt.
  29. type rxChunk struct {
  30. items []rxItem
  31. done chan struct{}
  32. }
  33. // decryptRxItems checks the data packets among items against the replay window and decrypts the ones it accepts in
  34. // place, as Decrypt does. Recording the counters is left to readOutsidePackets on the writer, which also handles
  35. // the packets without a tunnel.
  36. func decryptRxItems(l *slog.Logger, items []rxItem, nb []byte) {
  37. for i := range items {
  38. it := &items[i]
  39. if it.ci == nil {
  40. continue
  41. }
  42. if !it.ci.checkReplay(l, it.counter) {
  43. it.err = ErrAlreadySeen
  44. continue
  45. }
  46. it.out, it.err = it.ci.decryptPacket(it.counter, it.packet, nb)
  47. }
  48. }
  49. // rxBatch is one recvmmsg batch moving through the staged receive pipeline.
  50. type rxBatch struct {
  51. // items is every packet of the batch in arrival order. Data packets are decrypted by a worker; anything else is
  52. // handled whole by the writer, in its place.
  53. items []rxItem
  54. // bufs is the set of recvmmsg buffers the items point into, owned by the batch from handoff until it is reused.
  55. bufs [][]byte
  56. // chunks are the decrypt jobs, nChunks of them in use. With none the writer decrypts the batch itself.
  57. chunks []rxChunk
  58. nChunks int
  59. }
  60. func (b *rxBatch) reset() {
  61. clear(b.items)
  62. b.items = b.items[:0]
  63. for i := range b.nChunks {
  64. b.chunks[i].items = nil
  65. }
  66. b.nChunks = 0
  67. }
  68. // stagedRx is the staged receive pipeline of one udp listener. deliver and handoff run on the listener goroutine.
  69. type stagedRx struct {
  70. f *Interface
  71. q int
  72. // jobs feeds the decrypt workers. The listener is the only producer and closes it once the writer has drained.
  73. jobs chan *rxChunk
  74. workers sync.WaitGroup
  75. // h and hostmapCache are the listener's own. wrxc is the commit context: the writer's, or the listener's while
  76. // nothing is in flight.
  77. h *header.H
  78. hostmapCache map[uint32]*HostInfo
  79. wrxc *rxContext
  80. cur *rxBatch
  81. free []*rxBatch
  82. ordered chan *rxBatch
  83. returned chan *rxBatch
  84. inFlight int
  85. writerDone chan struct{}
  86. metricBatches metrics.Counter
  87. metricInline metrics.Counter
  88. }
  89. func newStagedRx(f *Interface, q int, wrxc *rxContext) *stagedRx {
  90. p := &stagedRx{
  91. f: f,
  92. q: q,
  93. h: &header.H{},
  94. hostmapCache: map[uint32]*HostInfo{},
  95. wrxc: wrxc,
  96. ordered: make(chan *rxBatch, stagedRxSlots),
  97. returned: make(chan *rxBatch, stagedRxSlots),
  98. writerDone: make(chan struct{}),
  99. metricBatches: metrics.GetOrRegisterCounter("flow.rx.batches", nil),
  100. metricInline: metrics.GetOrRegisterCounter("flow.rx.inline", nil),
  101. }
  102. n := f.staged.workers
  103. p.jobs = make(chan *rxChunk, stagedRxSlots*2*n)
  104. for range n {
  105. p.workers.Go(p.decrypt)
  106. }
  107. for range stagedRxSlots {
  108. // Two chunks per worker, so the writer can start committing while the workers are still on the rest
  109. b := &rxBatch{items: make([]rxItem, 0, 256), chunks: make([]rxChunk, 2*n)}
  110. for i := range b.chunks {
  111. b.chunks[i].done = make(chan struct{}, 1)
  112. }
  113. p.free = append(p.free, b)
  114. }
  115. p.cur = p.free[len(p.free)-1]
  116. p.free = p.free[:len(p.free)-1]
  117. go p.writer()
  118. return p
  119. }
  120. // decrypt is a worker: it decrypts the chunks it is handed and signals each one done.
  121. func (p *stagedRx) decrypt() {
  122. nb := make([]byte, 12)
  123. for c := range p.jobs {
  124. decryptRxItems(p.f.l, c.items, nb)
  125. c.done <- struct{}{}
  126. }
  127. }
  128. // listen runs the conn's receive loop through the pipeline until it fails, then drains the pipeline. The conn hands
  129. // its buffers over, so a batch moves between stages without a copy.
  130. func (p *stagedRx) listen(li handoffConn) error {
  131. // The first batch reads into the conn's own buffers; every other one brings its own
  132. n, size := li.HandoffShape()
  133. for _, b := range p.free {
  134. b.bufs = make([][]byte, n)
  135. for i := range b.bufs {
  136. b.bufs[i] = make([]byte, size)
  137. }
  138. }
  139. defer p.close()
  140. return li.ListenOut(p.deliver, p.handoff)
  141. }
  142. // deliver is the listener's per-packet work: classify the packet and add it to the current batch. Nothing here
  143. // decrypts or touches the tun.
  144. func (p *stagedRx) deliver(via netip.AddrPort, packet []byte) {
  145. it := rxItem{packet: packet, via: via}
  146. p.classify(&it)
  147. p.cur.items = append(p.cur.items, it)
  148. }
  149. // classify fills in the tunnel for a plain data packet of an established, non-relayed tunnel, leaving anything else
  150. // for the writer to handle whole. It is speculative: readOutsidePackets resolves the tunnel again on the writer and
  151. // uses the worker's result only when it is still the same one.
  152. func (p *stagedRx) classify(it *rxItem) {
  153. f := p.f
  154. h := p.h
  155. packet := it.packet
  156. if h.Parse(packet) != nil || h.Version != header.Version || h.Type != header.Message || h.Subtype != header.MessageNone {
  157. return
  158. }
  159. if f.myVpnNetworksTable.Contains(it.via.Addr()) {
  160. return
  161. }
  162. hostinfo := f.hostMap.QueryIndexCached(h.RemoteIndex, p.hostmapCache)
  163. if hostinfo == nil || hostinfo.ConnectionState == nil {
  164. return
  165. }
  166. ci := hostinfo.ConnectionState
  167. if len(packet) < header.Len+ci.dKey.Overhead() {
  168. return
  169. }
  170. it.ci, it.counter = ci, h.MessageCounter
  171. }
  172. // handoff ends a batch: the batch takes bufs, and the conn reads the next batch into the buffers of the next free
  173. // batch.
  174. func (p *stagedRx) handoff(bufs [][]byte) [][]byte {
  175. p.cur.bufs = bufs
  176. p.endBatch()
  177. return p.cur.bufs
  178. }
  179. // endBatch hands the batch to the workers and the ordered writer, then takes a free batch.
  180. func (p *stagedRx) endBatch() {
  181. clear(p.hostmapCache)
  182. p.reap()
  183. b := p.cur
  184. n := len(b.items)
  185. if n == 0 {
  186. b.reset()
  187. return
  188. }
  189. if p.inFlight == 0 && n < p.f.staged.inlineBelow {
  190. // The writer is idle with nothing in flight, so the listener may commit with its context and flush the tun
  191. p.metricInline.Inc(1)
  192. p.process(b, p.wrxc)
  193. b.reset()
  194. return
  195. }
  196. parts := min(len(b.chunks), n/rxChunkMin)
  197. p.metricBatches.Inc(1)
  198. if parts > 0 {
  199. size := (n + parts - 1) / parts
  200. for off := 0; off < n; off += size {
  201. c := &b.chunks[b.nChunks]
  202. c.items = b.items[off:min(off+size, n)]
  203. b.nChunks++
  204. }
  205. }
  206. // The jobs go out before the batch does: once the writer has it, it may finish and reset it at any moment
  207. for i := range b.nChunks {
  208. p.jobs <- &b.chunks[i]
  209. }
  210. p.inFlight++
  211. p.ordered <- b
  212. if len(p.free) == 0 {
  213. // Every batch is in flight, wait for the writer: the socket buffer absorbs the backlog meanwhile
  214. p.release(<-p.returned)
  215. }
  216. p.cur = p.free[len(p.free)-1]
  217. p.free = p.free[:len(p.free)-1]
  218. }
  219. // reap takes back every batch the writer has finished with, without waiting.
  220. func (p *stagedRx) reap() {
  221. for {
  222. select {
  223. case b := <-p.returned:
  224. p.release(b)
  225. default:
  226. return
  227. }
  228. }
  229. }
  230. func (p *stagedRx) release(b *rxBatch) {
  231. p.inFlight--
  232. p.free = append(p.free, b)
  233. }
  234. // process is the ordered stage for one batch: decrypt whatever no worker is decrypting, commit every item in arrival
  235. // order, and flush the tun batcher.
  236. func (p *stagedRx) process(b *rxBatch, rxc *rxContext) {
  237. if b.nChunks == 0 {
  238. decryptRxItems(p.f.l, b.items, rxc.nb)
  239. p.commit(b.items, rxc)
  240. } else {
  241. // Commit chunk by chunk, so the commit overlaps the workers still on later chunks
  242. for i := range b.nChunks {
  243. c := &b.chunks[i]
  244. <-c.done
  245. p.commit(c.items, rxc)
  246. }
  247. }
  248. p.f.flushOutside(rxc)
  249. }
  250. // commit runs every item through readOutsidePackets in arrival order, handing it the worker's result for a data
  251. // packet, exactly as inline receive handles them.
  252. func (p *stagedRx) commit(items []rxItem, rxc *rxContext) {
  253. for i := range items {
  254. it := &items[i]
  255. if it.ci != nil {
  256. rxc.decrypted = it
  257. }
  258. p.f.readOutsidePackets(ViaSender{UdpAddr: it.via}, it.packet, rxc)
  259. rxc.decrypted = nil
  260. }
  261. }
  262. // writer is the ordered receive stage: window, firewall, coalescer and tun write, one batch at a time in arrival
  263. // order.
  264. func (p *stagedRx) writer() {
  265. defer close(p.writerDone)
  266. for b := range p.ordered {
  267. if p.f.closed.Load() {
  268. // The tun is going away, a write would only log errors. Still wait out the workers on the batch.
  269. for i := range b.nChunks {
  270. <-b.chunks[i].done
  271. }
  272. } else {
  273. p.process(b, p.wrxc)
  274. }
  275. b.reset()
  276. p.returned <- b
  277. }
  278. }
  279. // close commits anything still in flight, in order, and stops the writer and the workers.
  280. func (p *stagedRx) close() {
  281. if len(p.cur.items) > 0 {
  282. p.endBatch()
  283. }
  284. close(p.ordered)
  285. <-p.writerDone
  286. close(p.jobs)
  287. p.workers.Wait()
  288. }