flow_rx.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. package nebula
  2. import (
  3. "encoding/binary"
  4. "net/netip"
  5. "time"
  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 can hand each received batch over. The callback returns replacement buffers with
  11. // the same count and at least the same lengths.
  12. type handoffConn interface {
  13. ListenOutHandoff(udp.EncReader, func([][]byte) [][]byte) error
  14. }
  15. const rxChunkMin = 16
  16. // rxItem is one received packet. A data packet for an established tunnel has ci set; out and err are written by
  17. // whoever decrypts it.
  18. type rxItem struct {
  19. packet []byte
  20. hostinfo *HostInfo
  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. func (c *rxChunk) run(nb []byte) {
  33. decryptRxItems(c.items, nb)
  34. c.done <- struct{}{}
  35. }
  36. // decryptRxItems decrypts the data packets among items in place. The replay window is left to the ordered stage, as
  37. // are packets without a tunnel and the known replays precheck marked.
  38. func decryptRxItems(items []rxItem, nb []byte) {
  39. for i := range items {
  40. it := &items[i]
  41. if it.ci == nil || it.err != nil {
  42. continue
  43. }
  44. it.out, it.err = it.ci.decryptPacket(it.counter, it.packet, nb)
  45. }
  46. }
  47. // rxBatch is one recvmmsg batch moving through the staged receive pipeline.
  48. type rxBatch struct {
  49. // items is every packet of the batch in arrival order. Data packets are decrypted by a worker; anything else is
  50. // handled whole by the writer, in its place.
  51. items []rxItem
  52. data int
  53. // bufs is the set of recvmmsg buffers the items point into, owned by the batch from handoff until it is reused.
  54. bufs [][]byte
  55. // chunks are the decrypt jobs, nChunks of them in use. With none the writer decrypts the batch itself.
  56. chunks []rxChunk
  57. nChunks int
  58. }
  59. func (b *rxBatch) reset() {
  60. clear(b.items)
  61. b.items = b.items[:0]
  62. b.data = 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. pool *flowPool
  73. // h and hostmapCache are the listener's own. wrxc is the commit context: the writer's, or the listener's while
  74. // nothing is in flight.
  75. h *header.H
  76. hostmapCache map[uint32]*HostInfo
  77. wrxc *rxContext
  78. started time.Time
  79. cur *rxBatch
  80. free []*rxBatch
  81. ordered chan *rxBatch
  82. returned chan *rxBatch
  83. inFlight int
  84. writerDone chan struct{}
  85. readBusy, decryptBusy, writeBusy metrics.Counter
  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, f.staged.rxSlots),
  97. returned: make(chan *rxBatch, f.staged.rxSlots),
  98. writerDone: make(chan struct{}),
  99. readBusy: stageBusy("rx_read", q),
  100. decryptBusy: stageBusy("rx_decrypt", q),
  101. writeBusy: stageBusy("rx_write", q),
  102. metricBatches: metrics.GetOrRegisterCounter("flow.rx.batches", nil),
  103. metricInline: metrics.GetOrRegisterCounter("flow.rx.inline", nil),
  104. }
  105. workers := f.staged.workers
  106. p.pool = newFlowPool(workers, f.staged.rxSlots*2*workers, p.decryptBusy)
  107. for range f.staged.rxSlots {
  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*workers)}
  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. // listen runs the conn's receive loop through the pipeline until it fails, then drains the pipeline. The conn hands
  121. // its buffers over, so a batch moves between stages without a copy.
  122. func (p *stagedRx) listen(li handoffConn) error {
  123. defer p.close()
  124. return li.ListenOutHandoff(p.deliver, p.handoff)
  125. }
  126. // deliver is the listener's per-packet work: classify the packet and add it to the current batch. Nothing here
  127. // decrypts or touches the tun.
  128. func (p *stagedRx) deliver(via netip.AddrPort, packet []byte) {
  129. if p.started.IsZero() {
  130. p.started = time.Now()
  131. }
  132. b := p.cur
  133. it := rxItem{packet: packet, via: via}
  134. p.classify(&it)
  135. if it.ci != nil {
  136. b.data++
  137. }
  138. b.items = append(b.items, it)
  139. }
  140. // classify fills in the tunnel for a plain data packet of an established, non-relayed tunnel, leaving anything else
  141. // for the writer to handle whole. The replay pre-check waits for the end of the batch, see precheck.
  142. func (p *stagedRx) classify(it *rxItem) {
  143. f := p.f
  144. h := p.h
  145. packet := it.packet
  146. if h.Parse(packet) != nil || h.Version != header.Version || h.Type != header.Message || h.Subtype != header.MessageNone {
  147. return
  148. }
  149. if f.myVpnNetworksTable.Contains(it.via.Addr()) {
  150. return
  151. }
  152. hostinfo := f.hostMap.QueryIndexCached(h.RemoteIndex, p.hostmapCache)
  153. if hostinfo == nil || hostinfo.ConnectionState == nil {
  154. return
  155. }
  156. ci := hostinfo.ConnectionState
  157. if len(packet) < header.Len+ci.dKey.Overhead() {
  158. return
  159. }
  160. it.hostinfo, it.ci, it.counter = hostinfo, ci, h.MessageCounter
  161. }
  162. // precheck marks obvious replays before anything decrypts them, like ConnectionState.Decrypt does, taking each
  163. // tunnel's lock once per run of its packets rather than once per packet: the writer takes the same lock for its
  164. // window updates, from another core. The writer checks again when it commits. The window only moves forward in
  165. // between, so a stale check here can only let a replay through to that second check. Marked packets keep their
  166. // place: an earlier RecvError may delete their tunnel, and inline receive then replies with a RecvError rather than
  167. // dropping a replay.
  168. func (p *stagedRx) precheck(b *rxBatch) {
  169. f := p.f
  170. items := b.items
  171. for i := 0; i < len(items); {
  172. ci := items[i].ci
  173. if ci == nil {
  174. i++
  175. continue
  176. }
  177. ci.decryptLock.Lock()
  178. for ; i < len(items) && items[i].ci == ci; i++ {
  179. if ci.window.Check(f.l, items[i].counter) {
  180. continue
  181. }
  182. b.data--
  183. items[i].err = ErrAlreadySeen
  184. }
  185. ci.decryptLock.Unlock()
  186. }
  187. }
  188. // handoff ends a batch: the batch takes bufs, and the conn reads the next batch into the buffers of the next free
  189. // batch.
  190. func (p *stagedRx) handoff(bufs [][]byte) [][]byte {
  191. p.cur.bufs = bufs
  192. p.endBatch()
  193. p.stopClock()
  194. b := p.cur
  195. if len(b.bufs) != len(bufs) || len(b.bufs[0]) < len(bufs[0]) {
  196. b.bufs = make([][]byte, len(bufs))
  197. for i := range b.bufs {
  198. b.bufs[i] = make([]byte, len(bufs[0]))
  199. }
  200. }
  201. return b.bufs
  202. }
  203. // endBatch hands the batch to the workers and the ordered writer, then takes a free batch.
  204. func (p *stagedRx) endBatch() {
  205. clear(p.hostmapCache)
  206. p.reap()
  207. b := p.cur
  208. p.precheck(b)
  209. n := len(b.items)
  210. if n == 0 {
  211. b.reset()
  212. return
  213. }
  214. if p.inFlight == 0 && n < p.f.staged.inlineBelow {
  215. // The writer is idle with nothing in flight, so the listener may commit with its context and flush the tun
  216. p.metricInline.Inc(1)
  217. p.process(b, p.wrxc)
  218. b.reset()
  219. return
  220. }
  221. parts := min(len(b.chunks), b.data/rxChunkMin)
  222. p.metricBatches.Inc(1)
  223. if parts > 0 {
  224. size := (n + parts - 1) / parts
  225. for off := 0; off < n; off += size {
  226. c := &b.chunks[b.nChunks]
  227. c.items = b.items[off:min(off+size, n)]
  228. b.nChunks++
  229. }
  230. }
  231. // The jobs go out before the batch does: once the writer has it, it may finish and reset it at any moment
  232. for i := range b.nChunks {
  233. p.pool.jobs <- &b.chunks[i]
  234. }
  235. p.inFlight++
  236. p.ordered <- b
  237. if len(p.free) == 0 {
  238. // Every batch is in flight, wait for the writer: the socket buffer absorbs the backlog meanwhile. The wait is
  239. // downstream backpressure, so the listener's busy time ends here.
  240. p.stopClock()
  241. p.release(<-p.returned)
  242. }
  243. p.cur = p.free[len(p.free)-1]
  244. p.free = p.free[:len(p.free)-1]
  245. }
  246. // stopClock adds the listener's busy time since the first packet of the batch, once per batch: the clock is read
  247. // when that packet arrives and when the batch is handed off or has to wait.
  248. func (p *stagedRx) stopClock() {
  249. if !p.started.IsZero() {
  250. p.readBusy.Inc(int64(time.Since(p.started)))
  251. p.started = time.Time{}
  252. }
  253. }
  254. // reap takes back every batch the writer has finished with, without waiting.
  255. func (p *stagedRx) reap() {
  256. for {
  257. select {
  258. case b := <-p.returned:
  259. p.release(b)
  260. default:
  261. return
  262. }
  263. }
  264. }
  265. func (p *stagedRx) release(b *rxBatch) {
  266. p.inFlight--
  267. p.free = append(p.free, b)
  268. }
  269. // process is the ordered stage for one batch: decrypt whatever no worker is decrypting, commit every item in arrival
  270. // order, and flush the tun batcher. It reports how long it waited on workers.
  271. func (p *stagedRx) process(b *rxBatch, rxc *rxContext) time.Duration {
  272. var waited time.Duration
  273. if b.nChunks == 0 {
  274. decryptRxItems(b.items, rxc.nb)
  275. p.commit(b.items, rxc)
  276. } else {
  277. // Commit chunk by chunk, so the commit overlaps the workers still on later chunks
  278. for i := range b.nChunks {
  279. c := &b.chunks[i]
  280. select {
  281. case <-c.done:
  282. default:
  283. t := time.Now()
  284. <-c.done
  285. waited += time.Since(t)
  286. }
  287. p.commit(c.items, rxc)
  288. }
  289. }
  290. p.f.flushOutside(rxc)
  291. return waited
  292. }
  293. // commit runs the ordered stage over items in arrival order. A non-data packet goes through readOutsidePackets in
  294. // its place, a run of data packets on one tunnel through commitRun.
  295. func (p *stagedRx) commit(items []rxItem, rxc *rxContext) {
  296. for i := 0; i < len(items); {
  297. ci := items[i].ci
  298. if ci == nil {
  299. p.f.readOutsidePackets(ViaSender{UdpAddr: items[i].via}, items[i].packet, rxc)
  300. i++
  301. continue
  302. }
  303. j := i + 1
  304. for j < len(items) && items[j].ci == ci {
  305. j++
  306. }
  307. p.commitRun(items[i:j], rxc)
  308. i = j
  309. }
  310. }
  311. // commitRun commits a run of decrypted data packets on one tunnel. The window updates go first, under one lock, then
  312. // the rest; handling a data packet never touches the window, so that is the same as one packet at a time.
  313. func (p *stagedRx) commitRun(run []rxItem, rxc *rxContext) {
  314. f := p.f
  315. ci := run[0].ci
  316. // Classification is speculative: earlier control packets may have removed this tunnel. Resolve it through the
  317. // writer's batch-local cache, exactly where inline processing would, before touching replay state.
  318. index := binary.BigEndian.Uint32(run[0].packet[4:8])
  319. hostinfo := f.hostMap.QueryIndexCached(index, rxc.hostmapCache)
  320. if hostinfo != run[0].hostinfo || hostinfo.ConnectionState != ci {
  321. if hostinfo == nil {
  322. for i := range run {
  323. f.maybeSendRecvError(run[i].via, index)
  324. }
  325. }
  326. return
  327. }
  328. ci.decryptLock.Lock()
  329. for i := range run {
  330. // A packet that failed to decrypt does not touch the window, like ConnectionState.Decrypt
  331. if run[i].err == nil && !ci.window.Update(f.l, run[i].counter) {
  332. run[i].err = ErrAlreadySeen
  333. }
  334. }
  335. ci.decryptLock.Unlock()
  336. for i := range run {
  337. it := &run[i]
  338. if f.finishOutsideDecrypt(it.hostinfo, ViaSender{UdpAddr: it.via}, it.counter, it.err) {
  339. f.handleOutsideMessagePacket(it.hostinfo, it.counter, it.out, rxc)
  340. }
  341. }
  342. }
  343. // writer is the ordered receive stage: window, firewall, coalescer and tun write, one batch at a time in arrival
  344. // order.
  345. func (p *stagedRx) writer() {
  346. defer close(p.writerDone)
  347. for b := range p.ordered {
  348. if p.f.closed.Load() {
  349. // The tun is going away, a write would only log errors. Still wait out the workers on the batch.
  350. for i := range b.nChunks {
  351. <-b.chunks[i].done
  352. }
  353. } else {
  354. t := time.Now()
  355. waited := p.process(b, p.wrxc)
  356. p.writeBusy.Inc(int64(time.Since(t) - waited))
  357. }
  358. b.reset()
  359. p.returned <- b
  360. }
  361. }
  362. // close commits anything still in flight, in order, and stops the writer and the workers.
  363. func (p *stagedRx) close() {
  364. if len(p.cur.items) > 0 {
  365. p.endBatch()
  366. }
  367. close(p.ordered)
  368. <-p.writerDone
  369. p.pool.close()
  370. }