flow_tx.go 17 KB

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