flow_tx.go 16 KB

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