interface_pipeline.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. //go:build darwin || send_pipeline
  2. package nebula
  3. import (
  4. "github.com/slackhq/nebula/firewall"
  5. "github.com/slackhq/nebula/overlay/batch"
  6. "github.com/slackhq/nebula/overlay/tio"
  7. "github.com/slackhq/nebula/udp"
  8. )
  9. // sendPipelinePlatform lets listenIn take the pipelined send path. It is on for darwin, and for any platform built
  10. // with -tags send_pipeline so the pipeline's tests (and the e2e suite through it) run on linux too.
  11. const sendPipelinePlatform = true
  12. // listenInPipelined is f.listenIn with the underlay writes moved to a sender goroutine (batch.SendPipeline). This
  13. // goroutine still reads the tun, runs the firewall and encrypts, straight into the pipeline's batch. The sender
  14. // writes whatever has built up each time its previous write returns, so under load the packets read while one
  15. // write is in the kernel leave together in the next one, and the next tun read overlaps the write.
  16. func listenInPipelined(f *Interface, queue tio.Queue, i int) {
  17. if f.pinThreads {
  18. f.pinThisThread(i)
  19. }
  20. rejectBuf := make([]byte, mtu)
  21. arenaSize := batch.SendBatchCap * (udp.MTU + 32)
  22. onWrite := func(queued, written int, err error) {
  23. f.accountSendBatch(queued, written, err, i)
  24. }
  25. p := batch.NewSendPipeline(f.writers[i], batch.SendBatchCap, arenaSize, udp.MaxWriteBatch, onWrite)
  26. defer p.Close()
  27. fwPacket := &firewall.ParsedPacket{}
  28. nb := make([]byte, 12, 12)
  29. conntrackCache := firewall.NewConntrackCacheTicker(f.ctx, f.l, f.conntrackCacheTimeout)
  30. for {
  31. pkts, err := queue.Read()
  32. if err != nil {
  33. // Same shutdown noise handling as listenOut
  34. if !f.closed.Load() && f.ctx.Err() == nil {
  35. f.l.Error("Error while reading outbound packet, closing", "error", err, "reader", i)
  36. f.onFatal(err)
  37. }
  38. break
  39. }
  40. for j, pkt := range pkts {
  41. // One Begin/End per packet holds the batch only briefly, so a sender that finishes a write mid-read
  42. // takes what is ready then. An idle sender is woken once, at the end of the read, to take it whole.
  43. sb := p.Begin()
  44. f.consumeInsidePacket(pkt, fwPacket, nb, sb, rejectBuf, i, conntrackCache.Get())
  45. p.End(j == len(pkts)-1)
  46. }
  47. }
  48. f.l.Debug("overlay reader is done", "reader", i)
  49. }