flow_tx.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. package nebula
  2. import (
  3. "errors"
  4. "net/netip"
  5. "time"
  6. "github.com/rcrowley/go-metrics"
  7. "github.com/slackhq/nebula/firewall"
  8. "github.com/slackhq/nebula/header"
  9. "github.com/slackhq/nebula/noiseutil"
  10. "github.com/slackhq/nebula/overlay/batch"
  11. "github.com/slackhq/nebula/overlay/tio"
  12. "github.com/slackhq/nebula/udp"
  13. )
  14. var errFlowSegmentOverrun = errors.New("superpacket has more segments than counters reserved for it")
  15. // txItem is one firewall-approved inside packet (or superpacket) with everything needed to encrypt it. A nonzero
  16. // counter is the first of the segs counters reserved for it on ci, relayCounter the same on the relay tunnel when
  17. // relayHI is set. Zero counters are allocated per segment while encrypting, as inline sends do.
  18. type txItem struct {
  19. pkt tio.Packet
  20. hostinfo *HostInfo
  21. ci *ConnectionState
  22. remote netip.AddrPort
  23. segs uint64
  24. counter uint64
  25. relayHI *HostInfo
  26. relay *Relay
  27. relayCounter uint64
  28. }
  29. // prepareTxItem does the per-packet work sendInsideMessage does before it encrypts, and resolves where the packet goes.
  30. func (f *Interface) prepareTxItem(it *txItem, hostinfo *HostInfo, pkt tio.Packet) bool {
  31. ci := hostinfo.ConnectionState
  32. if ci.eKey == nil {
  33. return false
  34. }
  35. f.recordOutbound(hostinfo)
  36. it.pkt, it.hostinfo, it.ci = pkt, hostinfo, ci
  37. it.remote = hostinfo.GetRemote()
  38. if it.remote.IsValid() {
  39. return true
  40. }
  41. it.relayHI, it.relay = f.findRelay(hostinfo)
  42. if it.relayHI == nil || it.relay == nil {
  43. return false
  44. }
  45. it.remote = it.relayHI.GetRemote()
  46. return true
  47. }
  48. // encryptTxItem segments and encrypts it into sb, for inline and staged sends alike.
  49. func (f *Interface) encryptTxItem(it *txItem, nb []byte, sb *batch.SendBatch) {
  50. reserved := it.counter != 0
  51. c, rc, end := it.counter, it.relayCounter, it.counter+it.segs
  52. err := tio.SegmentSuperpacket(it.pkt, func(seg []byte) error {
  53. if reserved && c >= end {
  54. // More segments than counters reserved for them, never encrypt past the reservation
  55. return errFlowSegmentOverrun
  56. }
  57. if it.relayHI == nil {
  58. // header + plaintext + AEAD tag (16 bytes for both AES-GCM and ChaCha20-Poly1305)
  59. if out := f.encryptTxSegment(it, c, seg, nb, sb.Reserve(header.Len+len(seg)+16)); out != nil {
  60. sb.Commit(out, it.remote)
  61. }
  62. } else {
  63. f.encryptTxSegmentVia(it, c, rc, seg, nb, sb)
  64. }
  65. if reserved {
  66. c++
  67. rc++
  68. }
  69. return nil
  70. })
  71. if err != nil {
  72. it.hostinfo.logger(f.l).Error("Failed to segment superpacket for send", "error", err)
  73. }
  74. }
  75. // encryptTxSegment encrypts one segment into out with counter c, or with the next counter of the tunnel when c is
  76. // zero. It returns nil when encryption failed, which is already logged.
  77. func (f *Interface) encryptTxSegment(it *txItem, c uint64, seg, nb, out []byte) []byte {
  78. if c != 0 {
  79. // Reserved counters are unique, so the encryption needs no lock
  80. return f.encryptMessage(header.Message, 0, it.hostinfo, it.ci, c, seg, nb, out, it.remote)
  81. }
  82. if noiseutil.EncryptLockNeeded {
  83. it.ci.writeLock.Lock()
  84. }
  85. c = it.ci.messageCounter.Add(1)
  86. out = f.encryptMessage(header.Message, 0, it.hostinfo, it.ci, c, seg, nb, out, it.remote)
  87. if noiseutil.EncryptLockNeeded {
  88. it.ci.writeLock.Unlock()
  89. }
  90. return out
  91. }
  92. // encryptTxSegmentVia encrypts one segment for the relayed tunnel, then wraps it for the relay with counter rc, both
  93. // allocated on demand when zero like encryptTxSegment does.
  94. func (f *Interface) encryptTxSegmentVia(it *txItem, c, rc uint64, seg, nb []byte, sb *batch.SendBatch) {
  95. //relay header + header + plaintext + AEAD tag + relay tag
  96. scratch := sb.Reserve(len(seg) + header.MaxOverhead)
  97. out := f.encryptTxSegment(it, c, seg, nb, scratch[header.Len:])
  98. if out == nil {
  99. return
  100. }
  101. toSend, err := f.prepareSendVia(it.relayHI, it.relay, out, nb, scratch, true, rc)
  102. if err != nil {
  103. return
  104. }
  105. remote := it.remote
  106. if rc == 0 {
  107. // Unreserved sends follow the relay's current remote, like sendNoMetrics
  108. remote = it.relayHI.GetRemote()
  109. }
  110. sb.Commit(toSend, remote)
  111. }
  112. // txTunnel accumulates one tunnel's counter needs across a batch so the batch reserves them with one atomic add.
  113. type txTunnel struct {
  114. ci *ConnectionState
  115. hostinfo *HostInfo
  116. n uint64
  117. base uint64
  118. exhausted bool
  119. }
  120. // txReserver turns one tun read into txItems with message counters reserved for them. Only the reader goroutine
  121. // collects and reserves counters.
  122. type txReserver struct {
  123. f *Interface
  124. tunnels []txTunnel
  125. owner uint64
  126. }
  127. // collect runs the filter over pkts and appends the sendable ones to items, tallying each tunnel's counter needs.
  128. func (r *txReserver) collect(items []txItem, pkts []tio.Packet, fwPacket *firewall.ParsedPacket, rejectBuf []byte, q int, localCache firewall.ConntrackCache) []txItem {
  129. f := r.f
  130. r.tunnels = r.tunnels[:0]
  131. for _, pkt := range pkts {
  132. hostinfo := f.filterInsidePacket(pkt, fwPacket, rejectBuf, q, localCache)
  133. if hostinfo == nil {
  134. continue
  135. }
  136. var it txItem
  137. if !f.prepareTxItem(&it, hostinfo, pkt) {
  138. continue
  139. }
  140. it.segs = uint64(pkt.SegmentCount())
  141. it.counter = r.need(it.hostinfo, it.ci, it.segs)
  142. if it.relayHI != nil {
  143. // The relay bookkeeping inline sends do per segment in prepareSendVia, once per item
  144. f.connectionManager.OutNoRebind(it.relayHI)
  145. f.connectionManager.RelayUsed(it.relay.LocalIndex)
  146. it.relayCounter = r.need(it.relayHI, it.relayHI.ConnectionState, it.segs)
  147. }
  148. items = append(items, it)
  149. }
  150. return items
  151. }
  152. // need adds n counters to ci's tally for this batch and returns the offset of the first one within it.
  153. func (r *txReserver) need(hostinfo *HostInfo, ci *ConnectionState, n uint64) uint64 {
  154. t := r.tunnel(ci)
  155. if t == nil {
  156. r.tunnels = append(r.tunnels, txTunnel{ci: ci, hostinfo: hostinfo})
  157. t = &r.tunnels[len(r.tunnels)-1]
  158. }
  159. off := t.n
  160. t.n += n
  161. return off
  162. }
  163. // tunnel finds ci's tally, a batch rarely touches more than a couple of tunnels so a scan beats a map.
  164. func (r *txReserver) tunnel(ci *ConnectionState) *txTunnel {
  165. for i := range r.tunnels {
  166. if r.tunnels[i].ci == ci {
  167. return &r.tunnels[i]
  168. }
  169. }
  170. return nil
  171. }
  172. // reserve takes the counters collect tallied and stamps them onto items, dropping the items of any tunnel whose
  173. // counter is exhausted. It returns the items that are left.
  174. func (r *txReserver) reserve(items []txItem) []txItem {
  175. f := r.f
  176. // One atomic add per tunnel reserves the whole batch's counters before any worker sees it, so counter order on a
  177. // tunnel is dispatch order, which is the order the batches are flushed in. Reservation and the sender's order are
  178. // both taken on the reader goroutine, so there is no window for another batch to slip in between.
  179. for i := range r.tunnels {
  180. t := &r.tunnels[i]
  181. end := t.ci.messageCounter.Add(t.n)
  182. t.base = end - t.n + 1
  183. if end >= RejectAfterMessages {
  184. t.ci.messageCounter.Store(RejectAfterMessages)
  185. t.exhausted = true
  186. f.messageMetrics.TxExhausted(int64(t.n))
  187. if t.base <= RejectAfterMessages {
  188. t.hostinfo.logger(f.l).Error("Dropping outbound packets, tunnel message counter is exhausted")
  189. }
  190. }
  191. }
  192. n := 0
  193. for _, it := range items {
  194. t := r.tunnel(it.ci)
  195. if t.exhausted {
  196. continue
  197. }
  198. it.counter += t.base
  199. if it.relayHI != nil {
  200. rt := r.tunnel(it.relayHI.ConnectionState)
  201. if rt.exhausted {
  202. continue
  203. }
  204. it.relayCounter += rt.base
  205. }
  206. items[n] = it
  207. n++
  208. }
  209. clear(items[n:])
  210. return items[:n]
  211. }
  212. // handoffQueue is a tun queue that can give up the buffer its last Read filled. SwapRxBuf(nil) allocates a
  213. // correctly sized replacement; later swaps recycle the buffers handed back.
  214. type handoffQueue interface {
  215. tio.Queue
  216. SwapRxBuf([]byte) []byte
  217. }
  218. // txSlot is one staged batch. The reader fills items, a worker encrypts them into sb and signals done, and the
  219. // ordered sender flushes sb.
  220. type txSlot struct {
  221. f *Interface
  222. items []txItem
  223. // buf owns the queue receive buffer the items point into, swapped out at dispatch
  224. buf []byte
  225. sb *batch.SendBatch
  226. done chan struct{}
  227. // reservations hold shared tunnel credit until the flush, including for dropped counters
  228. reservations []txTunnel
  229. }
  230. func (s *txSlot) run(nb []byte) {
  231. for i := range s.items {
  232. s.f.encryptTxItem(&s.items[i], nb, s.sb)
  233. }
  234. s.done <- struct{}{}
  235. }
  236. // stagedTx is the staged transmit pipeline of one tun reader. Everything but the workers and the sender runs on the
  237. // reader goroutine.
  238. type stagedTx struct {
  239. txReserver
  240. q int
  241. cpu int // reader CPU, inline sends stay here
  242. senderCPU int // -1 leaves the sender within its inherited affinity
  243. queue handoffQueue
  244. pool *flowPool
  245. // sb and nb are the reader's own, for the sends it does itself
  246. sb *batch.SendBatch
  247. nb []byte
  248. // free holds the reader's idle slots. A dispatched slot goes to the workers and, in the same order, to the
  249. // sender on ordered; the sender gives it back on returned once it is on the wire.
  250. free []*txSlot
  251. ordered chan *txSlot
  252. returned chan *txSlot
  253. inFlight int
  254. senderDone chan struct{}
  255. // gateInline is the routine's transmit mode, gateStreak how many reads in a row wanted the other one
  256. gateInline bool
  257. gateStreak int
  258. metricGateSwitches metrics.Counter
  259. readBusy, encryptBusy, sendBusy metrics.Counter
  260. metricBatches metrics.Counter
  261. metricInline metrics.Counter
  262. }
  263. // newStagedTx needs a queue that hands over whole read batches, the offload (TSO/USO) queue on linux. On any other
  264. // queue a read is one packet, and staging it would cost a handoff per packet.
  265. func newStagedTx(f *Interface, queue handoffQueue, q int) *stagedTx {
  266. p := &stagedTx{
  267. txReserver: txReserver{f: f, tunnels: make([]txTunnel, 0, 4), owner: txBudgetOwner(q)},
  268. q: q,
  269. senderCPU: -1,
  270. queue: queue,
  271. sb: batch.NewSendBatch(f.writers[q], batch.SendBatchCap, batch.SendBatchCap*(udp.MTU+32)),
  272. nb: make([]byte, 12),
  273. ordered: make(chan *txSlot, f.staged.txSlots),
  274. returned: make(chan *txSlot, f.staged.txSlots),
  275. senderDone: make(chan struct{}),
  276. readBusy: stageBusy("tx_read", q),
  277. encryptBusy: stageBusy("tx_encrypt", q),
  278. sendBusy: stageBusy("tx_send", q),
  279. metricBatches: metrics.GetOrRegisterCounter("flow.tx.batches", nil),
  280. metricInline: metrics.GetOrRegisterCounter("flow.tx.inline", nil),
  281. // Starts inline, the pipeline is empty, unless the threshold never allows it
  282. gateInline: f.staged.inlineBelow > 0,
  283. metricGateSwitches: metrics.GetOrRegisterCounter("flow.tx.gate_switches", nil),
  284. }
  285. if f.pinThreads {
  286. p.cpu = f.threadCPU(q)
  287. // Only an explicit list pins the sender, and never onto its reader's CPU, where the two would serialise.
  288. // Automatic placement by queue index can put a busy sender on a slow core; unpinned, the scheduler moves it.
  289. if n := len(f.cpuAffinity); n > 0 && f.cpuAffinity[(q+1)%n] != p.cpu {
  290. p.senderCPU = f.cpuAffinity[(q+1)%n]
  291. }
  292. }
  293. p.pool = newFlowPool(f.staged.workers, f.staged.txSlots, p.encryptBusy)
  294. // Ciphertext adds per-segment headers and tags; the arena can grow for small segments.
  295. buf := queue.SwapRxBuf(nil)
  296. bufSize := len(buf)
  297. for i := range f.staged.txSlots {
  298. if i > 0 {
  299. buf = make([]byte, bufSize)
  300. }
  301. p.free = append(p.free, &txSlot{
  302. f: f,
  303. buf: buf,
  304. sb: batch.NewSendBatch(f.writers[q], 4*batch.SendBatchCap, bufSize+bufSize/4),
  305. done: make(chan struct{}, 1),
  306. })
  307. }
  308. go p.sender()
  309. return p
  310. }
  311. // run is listenIn's loop with the staged pipeline: read, then hand each batch on, until the queue fails.
  312. func (p *stagedTx) run(fwPacket *firewall.ParsedPacket, rejectBuf []byte, conntrackCache *firewall.ConntrackCacheTicker) error {
  313. defer p.close()
  314. if p.f.pinThreads {
  315. p.f.pinThreadToCPU(p.q, p.cpu)
  316. }
  317. for {
  318. pkts, err := p.queue.Read()
  319. if err != nil {
  320. return err
  321. }
  322. // Waiting for a slot is backpressure, not reader work, so it comes before the clock read. The loop reads the
  323. // clock twice, more only when it waits for tunnel credit or drains, for an oversized read or a switch to inline.
  324. p.ready()
  325. t := time.Now()
  326. stall := p.handle(pkts, fwPacket, rejectBuf, conntrackCache.Get())
  327. p.readBusy.Inc(int64(time.Since(t) - stall))
  328. }
  329. }
  330. // handle takes one tun read through the pipeline. It returns once the batch is queued, or sent when the reader sent
  331. // it itself, with how long it waited for tunnel credit.
  332. func (p *stagedTx) handle(pkts []tio.Packet, fwPacket *firewall.ParsedPacket, rejectBuf []byte, localCache firewall.ConntrackCache) time.Duration {
  333. p.ready()
  334. inline, stall := p.gate(p.small(pkts))
  335. if inline && p.f.routines == 1 {
  336. // With only one reader and an empty pipeline, no other staged reservation can be overtaken. Keep this common
  337. // case inexpensive.
  338. p.inlineSoleReader(pkts, fwPacket, rejectBuf, localCache)
  339. return stall
  340. }
  341. slot := p.free[len(p.free)-1]
  342. p.free = p.free[:len(p.free)-1]
  343. items := p.collect(slot.items[:0], pkts, fwPacket, rejectBuf, p.q, localCache)
  344. if len(items) == 0 {
  345. slot.items = items
  346. p.free = append(p.free, slot)
  347. return stall
  348. }
  349. if p.oversized() {
  350. p.metricInline.Inc(1)
  351. stall += p.sendLarge(items)
  352. slot.items = items[:0]
  353. p.free = append(p.free, slot)
  354. return stall
  355. }
  356. d, ok := p.waitBudget()
  357. stall += d
  358. if !ok {
  359. clear(items)
  360. slot.items = items[:0]
  361. p.free = append(p.free, slot)
  362. return stall
  363. }
  364. slot.reservations = append(slot.reservations[:0], p.tunnels...)
  365. items = p.reserve(items)
  366. slot.items = items
  367. if len(items) == 0 {
  368. slot.releaseBudget()
  369. p.free = append(p.free, slot)
  370. return stall
  371. }
  372. if inline {
  373. p.inline(slot)
  374. p.free = append(p.free, slot)
  375. return stall
  376. }
  377. // The packets now live in the slot, and the queue reads into the slot's old buffer
  378. slot.buf = p.queue.SwapRxBuf(slot.buf)
  379. p.inFlight++
  380. p.metricBatches.Inc(1)
  381. // Counters were reserved above on this goroutine and the slot takes its place in the sender's order right here,
  382. // before any other batch can, so the sender puts them on the wire in counter order.
  383. p.ordered <- slot
  384. p.pool.jobs <- slot
  385. return stall
  386. }
  387. // gate reports whether a read goes inline, and how long it waited for the pipeline to drain. Each routine holds one
  388. // transmit mode, inline on the reader or fanout to the workers and the sender, until gateHold reads in a row want the
  389. // other one, the last of which goes the new way. Choosing per read let small inline sends leave from the reader's CPU
  390. // between staged ones leaving from the sender's, and the receiver's TCP saw them reordered. Going inline the reader
  391. // first waits until everything in flight is on the wire and the sender is idle, so no inline send overtakes a staged
  392. // one. Going to fanout nothing is in flight: inline sends finish before the reader moves on.
  393. func (p *stagedTx) gate(small bool) (bool, time.Duration) {
  394. if small == p.gateInline {
  395. p.gateStreak = 0
  396. return small, 0
  397. }
  398. if p.gateStreak++; p.gateStreak < p.f.staged.gateHold {
  399. return p.gateInline, 0
  400. }
  401. p.gateInline, p.gateStreak = small, 0
  402. p.metricGateSwitches.Inc(1)
  403. if !small {
  404. return false, 0
  405. }
  406. t := time.Now()
  407. for p.flushOldest() {
  408. }
  409. return true, time.Since(t)
  410. }
  411. // ready takes back every slot the sender has finished with, and waits for one when all of them are in flight.
  412. func (p *stagedTx) ready() {
  413. p.reap()
  414. if len(p.free) == 0 {
  415. p.release(<-p.returned)
  416. }
  417. }
  418. // reap takes back every slot the sender has finished with, without waiting.
  419. func (p *stagedTx) reap() {
  420. for {
  421. select {
  422. case s := <-p.returned:
  423. p.release(s)
  424. default:
  425. return
  426. }
  427. }
  428. }
  429. // flushOldest waits for the oldest batch in flight to reach the wire and takes its slot back. It reports false when
  430. // nothing is in flight.
  431. func (p *stagedTx) flushOldest() bool {
  432. if p.inFlight == 0 {
  433. return false
  434. }
  435. p.release(<-p.returned)
  436. return true
  437. }
  438. func (p *stagedTx) release(s *txSlot) {
  439. p.inFlight--
  440. p.free = append(p.free, s)
  441. }
  442. // sender is the ordered send stage: it puts each batch on the wire once its worker is done, in dispatch order.
  443. func (p *stagedTx) sender() {
  444. defer close(p.senderDone)
  445. if p.senderCPU >= 0 {
  446. p.f.pinThreadToCPU(p.q, p.senderCPU)
  447. }
  448. for s := range p.ordered {
  449. <-s.done
  450. t := time.Now()
  451. if p.f.closed.Load() {
  452. // The sockets are gone, a flush would only log errors
  453. s.sb.Reset()
  454. } else {
  455. p.f.flushSendBatch(s.sb, p.q)
  456. }
  457. s.releaseBudget()
  458. p.sendBusy.Inc(int64(time.Since(t)))
  459. clear(s.items)
  460. s.items = s.items[:0]
  461. p.returned <- s
  462. }
  463. }
  464. // close sends everything still in flight, in order, and stops the sender and the workers.
  465. func (p *stagedTx) close() {
  466. close(p.ordered)
  467. <-p.senderDone
  468. p.pool.close()
  469. }
  470. // small counts segments against the inline threshold.
  471. func (p *stagedTx) small(pkts []tio.Packet) bool {
  472. below := uint64(p.f.staged.inlineBelow)
  473. if below == 0 {
  474. return false
  475. }
  476. var n uint64
  477. for _, pkt := range pkts {
  478. if n += uint64(pkt.SegmentCount()); n >= below {
  479. return false
  480. }
  481. }
  482. return true
  483. }
  484. // inline encrypts a budgeted batch on the reader, with no worker handoff or buffer swap. The local sender is idle,
  485. // but other readers may hold reservations on these tunnels, so keep our credit until the flush completes.
  486. func (p *stagedTx) inline(slot *txSlot) {
  487. p.metricInline.Inc(1)
  488. for i := range slot.items {
  489. p.f.encryptTxItem(&slot.items[i], p.nb, p.sb)
  490. if p.sb.Len() >= batch.SendBatchCap {
  491. p.f.flushSendBatch(p.sb, p.q)
  492. }
  493. }
  494. p.f.flushSendBatch(p.sb, p.q)
  495. slot.releaseBudget()
  496. clear(slot.items)
  497. slot.items = slot.items[:0]
  498. }
  499. // inlineSoleReader needs no credit: the interface has no other tun reader and this reader has drained every
  500. // reservation before taking the inline path.
  501. func (p *stagedTx) inlineSoleReader(pkts []tio.Packet, fwPacket *firewall.ParsedPacket, rejectBuf []byte, localCache firewall.ConntrackCache) {
  502. p.metricInline.Inc(1)
  503. for _, pkt := range pkts {
  504. p.f.consumeInsidePacket(pkt, fwPacket, p.nb, p.sb, rejectBuf, p.q, localCache)
  505. if p.sb.Len() >= batch.SendBatchCap {
  506. p.f.flushSendBatch(p.sb, p.q)
  507. }
  508. }
  509. p.f.flushSendBatch(p.sb, p.q)
  510. }