flow_rx.go 9.1 KB

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