flow_rx.go 9.5 KB

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