flow_tx.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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. readBusy, encryptBusy, sendBusy metrics.Counter
  256. metricBatches metrics.Counter
  257. metricInline metrics.Counter
  258. }
  259. // newStagedTx needs a queue that hands over whole read batches, the offload (TSO/USO) queue on linux. On any other
  260. // queue a read is one packet, and staging it would cost a handoff per packet.
  261. func newStagedTx(f *Interface, queue handoffQueue, q int) *stagedTx {
  262. p := &stagedTx{
  263. txReserver: txReserver{f: f, tunnels: make([]txTunnel, 0, 4), owner: txBudgetOwner(q)},
  264. q: q,
  265. senderCPU: -1,
  266. queue: queue,
  267. sb: batch.NewSendBatch(f.writers[q], batch.SendBatchCap, batch.SendBatchCap*(udp.MTU+32)),
  268. nb: make([]byte, 12),
  269. ordered: make(chan *txSlot, f.staged.txSlots),
  270. returned: make(chan *txSlot, f.staged.txSlots),
  271. senderDone: make(chan struct{}),
  272. readBusy: stageBusy("tx_read", q),
  273. encryptBusy: stageBusy("tx_encrypt", q),
  274. sendBusy: stageBusy("tx_send", q),
  275. metricBatches: metrics.GetOrRegisterCounter("flow.tx.batches", nil),
  276. metricInline: metrics.GetOrRegisterCounter("flow.tx.inline", nil),
  277. }
  278. if f.pinThreads {
  279. p.cpu = f.threadCPU(q)
  280. // Only an explicit list pins the sender, and never onto its reader's CPU, where the two would serialise.
  281. // Automatic placement by queue index can put a busy sender on a slow core; unpinned, the scheduler moves it.
  282. if n := len(f.cpuAffinity); n > 0 && f.cpuAffinity[(q+1)%n] != p.cpu {
  283. p.senderCPU = f.cpuAffinity[(q+1)%n]
  284. }
  285. }
  286. p.pool = newFlowPool(f.staged.workers, f.staged.txSlots, p.encryptBusy)
  287. // Ciphertext adds per-segment headers and tags; the arena can grow for small segments.
  288. buf := queue.SwapRxBuf(nil)
  289. bufSize := len(buf)
  290. for i := range f.staged.txSlots {
  291. if i > 0 {
  292. buf = make([]byte, bufSize)
  293. }
  294. p.free = append(p.free, &txSlot{
  295. f: f,
  296. buf: buf,
  297. sb: batch.NewSendBatch(f.writers[q], 4*batch.SendBatchCap, bufSize+bufSize/4),
  298. done: make(chan struct{}, 1),
  299. })
  300. }
  301. go p.sender()
  302. return p
  303. }
  304. // run is listenIn's loop with the staged pipeline: read, then hand each batch on, until the queue fails.
  305. func (p *stagedTx) run(fwPacket *firewall.ParsedPacket, rejectBuf []byte, conntrackCache *firewall.ConntrackCacheTicker) error {
  306. defer p.close()
  307. if p.f.pinThreads {
  308. p.f.pinThreadToCPU(p.q, p.cpu)
  309. }
  310. for {
  311. pkts, err := p.queue.Read()
  312. if err != nil {
  313. return err
  314. }
  315. t := time.Now()
  316. stall := p.handle(pkts, fwPacket, rejectBuf, conntrackCache.Get())
  317. p.readBusy.Inc(int64(time.Since(t) - stall))
  318. }
  319. }
  320. // handle takes one tun read through the pipeline. It returns once the batch is queued, or sent when the reader sent
  321. // it itself, with how long it waited for the sender.
  322. func (p *stagedTx) handle(pkts []tio.Packet, fwPacket *firewall.ParsedPacket, rejectBuf []byte, localCache firewall.ConntrackCache) time.Duration {
  323. p.reap()
  324. inline := p.inFlight == 0 && p.small(pkts)
  325. if inline && p.f.routines == 1 {
  326. // With only one reader and an empty pipeline, no other staged reservation can be overtaken. Keep this common
  327. // case inexpensive.
  328. p.inlineSoleReader(pkts, fwPacket, rejectBuf, localCache)
  329. return 0
  330. }
  331. slot, stall := p.acquire()
  332. items := p.collect(slot.items[:0], pkts, fwPacket, rejectBuf, p.q, localCache)
  333. if len(items) == 0 {
  334. slot.items = items
  335. p.free = append(p.free, slot)
  336. return stall
  337. }
  338. if p.oversized() {
  339. p.metricInline.Inc(1)
  340. stall += p.sendLarge(items)
  341. slot.items = items[:0]
  342. p.free = append(p.free, slot)
  343. return stall
  344. }
  345. d, ok := p.waitBudget()
  346. stall += d
  347. if !ok {
  348. clear(items)
  349. slot.items = items[:0]
  350. p.free = append(p.free, slot)
  351. return stall
  352. }
  353. slot.reservations = append(slot.reservations[:0], p.tunnels...)
  354. items = p.reserve(items)
  355. slot.items = items
  356. if len(items) == 0 {
  357. slot.releaseBudget()
  358. p.free = append(p.free, slot)
  359. return stall
  360. }
  361. if inline {
  362. p.inline(slot)
  363. p.free = append(p.free, slot)
  364. return stall
  365. }
  366. // The packets now live in the slot, and the queue reads into the slot's old buffer
  367. slot.buf = p.queue.SwapRxBuf(slot.buf)
  368. p.inFlight++
  369. p.metricBatches.Inc(1)
  370. // Counters were reserved above on this goroutine and the slot takes its place in the sender's order right here,
  371. // before any other batch can, so the sender puts them on the wire in counter order.
  372. p.ordered <- slot
  373. p.pool.jobs <- slot
  374. return stall
  375. }
  376. // acquire returns an idle slot, waiting for the sender to give one back when all of them are in flight, and how long
  377. // it waited.
  378. func (p *stagedTx) acquire() (*txSlot, time.Duration) {
  379. var stall time.Duration
  380. if len(p.free) == 0 {
  381. t := time.Now()
  382. p.release(<-p.returned)
  383. stall = time.Since(t)
  384. }
  385. s := p.free[len(p.free)-1]
  386. p.free = p.free[:len(p.free)-1]
  387. return s, stall
  388. }
  389. // reap takes back every slot the sender has finished with, without waiting.
  390. func (p *stagedTx) reap() {
  391. for {
  392. select {
  393. case s := <-p.returned:
  394. p.release(s)
  395. default:
  396. return
  397. }
  398. }
  399. }
  400. // flushOldest waits for the oldest batch in flight to reach the wire and takes its slot back. It reports false when
  401. // nothing is in flight.
  402. func (p *stagedTx) flushOldest() bool {
  403. if p.inFlight == 0 {
  404. return false
  405. }
  406. p.release(<-p.returned)
  407. return true
  408. }
  409. func (p *stagedTx) release(s *txSlot) {
  410. p.inFlight--
  411. p.free = append(p.free, s)
  412. }
  413. // sender is the ordered send stage: it puts each batch on the wire once its worker is done, in dispatch order.
  414. func (p *stagedTx) sender() {
  415. defer close(p.senderDone)
  416. if p.senderCPU >= 0 {
  417. p.f.pinThreadToCPU(p.q, p.senderCPU)
  418. }
  419. for s := range p.ordered {
  420. <-s.done
  421. t := time.Now()
  422. if p.f.closed.Load() {
  423. // The sockets are gone, a flush would only log errors
  424. s.sb.Reset()
  425. } else {
  426. p.f.flushSendBatch(s.sb, p.q)
  427. }
  428. s.releaseBudget()
  429. p.sendBusy.Inc(int64(time.Since(t)))
  430. clear(s.items)
  431. s.items = s.items[:0]
  432. p.returned <- s
  433. }
  434. }
  435. // close sends everything still in flight, in order, and stops the sender and the workers.
  436. func (p *stagedTx) close() {
  437. close(p.ordered)
  438. <-p.senderDone
  439. p.pool.close()
  440. }
  441. // small counts segments against the inline threshold.
  442. func (p *stagedTx) small(pkts []tio.Packet) bool {
  443. below := uint64(p.f.staged.inlineBelow)
  444. if below == 0 {
  445. return false
  446. }
  447. var n uint64
  448. for _, pkt := range pkts {
  449. if n += uint64(pkt.SegmentCount()); n >= below {
  450. return false
  451. }
  452. }
  453. return true
  454. }
  455. // inline encrypts a budgeted batch on the reader, with no worker handoff or buffer swap. The local sender is idle,
  456. // but other readers may hold reservations on these tunnels, so keep our credit until the flush completes.
  457. func (p *stagedTx) inline(slot *txSlot) {
  458. p.metricInline.Inc(1)
  459. for i := range slot.items {
  460. p.f.encryptTxItem(&slot.items[i], p.nb, p.sb)
  461. if p.sb.Len() >= batch.SendBatchCap {
  462. p.f.flushSendBatch(p.sb, p.q)
  463. }
  464. }
  465. p.f.flushSendBatch(p.sb, p.q)
  466. slot.releaseBudget()
  467. clear(slot.items)
  468. slot.items = slot.items[:0]
  469. }
  470. // inlineSoleReader needs no credit: the interface has no other tun reader and this reader has drained every
  471. // reservation before taking the inline path.
  472. func (p *stagedTx) inlineSoleReader(pkts []tio.Packet, fwPacket *firewall.ParsedPacket, rejectBuf []byte, localCache firewall.ConntrackCache) {
  473. p.metricInline.Inc(1)
  474. for _, pkt := range pkts {
  475. p.f.consumeInsidePacket(pkt, fwPacket, p.nb, p.sb, rejectBuf, p.q, localCache)
  476. if p.sb.Len() >= batch.SendBatchCap {
  477. p.f.flushSendBatch(p.sb, p.q)
  478. }
  479. }
  480. p.f.flushSendBatch(p.sb, p.q)
  481. }