flow_tx.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. package nebula
  2. import (
  3. "errors"
  4. "net/netip"
  5. "github.com/slackhq/nebula/firewall"
  6. "github.com/slackhq/nebula/header"
  7. "github.com/slackhq/nebula/noiseutil"
  8. "github.com/slackhq/nebula/overlay/batch"
  9. "github.com/slackhq/nebula/overlay/tio"
  10. )
  11. var errFlowSegmentOverrun = errors.New("superpacket has more segments than counters reserved for it")
  12. // txItem is one firewall-approved inside packet (or superpacket) with everything needed to encrypt it. A nonzero
  13. // counter is the first of the segs counters reserved for it on ci, relayCounter the same on the relay tunnel when
  14. // relayHI is set. Zero counters are allocated per segment while encrypting, as inline sends do.
  15. type txItem struct {
  16. pkt tio.Packet
  17. hostinfo *HostInfo
  18. ci *ConnectionState
  19. remote netip.AddrPort
  20. segs uint64
  21. counter uint64
  22. relayHI *HostInfo
  23. relay *Relay
  24. relayCounter uint64
  25. }
  26. // prepareTxItem does the per-packet work sendInsideMessage does before it encrypts, and resolves where the packet goes.
  27. func (f *Interface) prepareTxItem(it *txItem, hostinfo *HostInfo, pkt tio.Packet) bool {
  28. ci := hostinfo.ConnectionState
  29. if ci.eKey == nil {
  30. return false
  31. }
  32. f.recordOutbound(hostinfo)
  33. it.pkt, it.hostinfo, it.ci = pkt, hostinfo, ci
  34. it.remote = hostinfo.GetRemote()
  35. if it.remote.IsValid() {
  36. return true
  37. }
  38. it.relayHI, it.relay = f.findRelay(hostinfo)
  39. if it.relayHI == nil || it.relay == nil {
  40. return false
  41. }
  42. it.remote = it.relayHI.GetRemote()
  43. return true
  44. }
  45. // encryptTxItem segments and encrypts it into sb, for inline and staged sends alike.
  46. func (f *Interface) encryptTxItem(it *txItem, nb []byte, sb *batch.SendBatch) {
  47. reserved := it.counter != 0
  48. c, rc, end := it.counter, it.relayCounter, it.counter+it.segs
  49. err := tio.SegmentSuperpacket(it.pkt, func(seg []byte) error {
  50. if reserved && c >= end {
  51. // More segments than counters reserved for them, never encrypt past the reservation
  52. return errFlowSegmentOverrun
  53. }
  54. if it.relayHI == nil {
  55. // header + plaintext + AEAD tag (16 bytes for both AES-GCM and ChaCha20-Poly1305)
  56. if out := f.encryptTxSegment(it, c, seg, nb, sb.Reserve(header.Len+len(seg)+16)); out != nil {
  57. sb.Commit(out, it.remote)
  58. }
  59. } else {
  60. f.encryptTxSegmentVia(it, c, rc, seg, nb, sb)
  61. }
  62. if reserved {
  63. c++
  64. rc++
  65. }
  66. return nil
  67. })
  68. if err != nil {
  69. it.hostinfo.logger(f.l).Error("Failed to segment superpacket for send", "error", err)
  70. }
  71. }
  72. // encryptTxSegment encrypts one segment into out with counter c, or with the next counter of the tunnel when c is
  73. // zero. It returns nil when encryption failed, which is already logged.
  74. func (f *Interface) encryptTxSegment(it *txItem, c uint64, seg, nb, out []byte) []byte {
  75. if c != 0 {
  76. // Reserved counters are unique, so the encryption needs no lock
  77. return f.encryptMessage(header.Message, 0, it.hostinfo, it.ci, c, seg, nb, out, it.remote)
  78. }
  79. if noiseutil.EncryptLockNeeded {
  80. it.ci.writeLock.Lock()
  81. }
  82. c = it.ci.messageCounter.Add(1)
  83. out = f.encryptMessage(header.Message, 0, it.hostinfo, it.ci, c, seg, nb, out, it.remote)
  84. if noiseutil.EncryptLockNeeded {
  85. it.ci.writeLock.Unlock()
  86. }
  87. return out
  88. }
  89. // encryptTxSegmentVia encrypts one segment for the relayed tunnel, then wraps it for the relay with counter rc, both
  90. // allocated on demand when zero like encryptTxSegment does.
  91. func (f *Interface) encryptTxSegmentVia(it *txItem, c, rc uint64, seg, nb []byte, sb *batch.SendBatch) {
  92. //relay header + header + plaintext + AEAD tag + relay tag
  93. scratch := sb.Reserve(len(seg) + header.MaxOverhead)
  94. out := f.encryptTxSegment(it, c, seg, nb, scratch[header.Len:])
  95. if out == nil {
  96. return
  97. }
  98. toSend, err := f.prepareSendVia(it.relayHI, it.relay, out, nb, scratch, true, rc)
  99. if err != nil {
  100. return
  101. }
  102. remote := it.remote
  103. if rc == 0 {
  104. // Unreserved sends follow the relay's current remote, like sendNoMetrics
  105. remote = it.relayHI.GetRemote()
  106. }
  107. sb.Commit(toSend, remote)
  108. }
  109. // segmentCount reports how many segments tio.SegmentSuperpacket will cut pkt into, without touching its bytes.
  110. func segmentCount(pkt tio.Packet) uint64 {
  111. if !pkt.GSO.IsSuperpacket() {
  112. return 1
  113. }
  114. n := (len(pkt.Bytes) - int(pkt.GSO.HdrLen) + int(pkt.GSO.Size) - 1) / int(pkt.GSO.Size)
  115. return uint64(max(n, 1))
  116. }
  117. // txTunnel accumulates one tunnel's counter needs across a batch so the batch reserves them with one atomic add.
  118. type txTunnel struct {
  119. ci *ConnectionState
  120. hostinfo *HostInfo
  121. n uint64
  122. base uint64
  123. exhausted bool
  124. }
  125. // txReserver turns one tun read into txItems with message counters reserved for them. Only the reader goroutine
  126. // collects and reserves counters.
  127. type txReserver struct {
  128. f *Interface
  129. tunnels []txTunnel
  130. }
  131. // collect runs the filter over pkts and appends the sendable ones to items, tallying each tunnel's counter needs.
  132. func (r *txReserver) collect(items []txItem, pkts []tio.Packet, fwPacket *firewall.ParsedPacket, rejectBuf []byte, q int, localCache firewall.ConntrackCache) []txItem {
  133. f := r.f
  134. r.tunnels = r.tunnels[:0]
  135. for _, pkt := range pkts {
  136. hostinfo := f.filterInsidePacket(pkt, fwPacket, rejectBuf, q, localCache)
  137. if hostinfo == nil {
  138. continue
  139. }
  140. var it txItem
  141. if !f.prepareTxItem(&it, hostinfo, pkt) {
  142. continue
  143. }
  144. it.segs = segmentCount(pkt)
  145. it.counter = r.need(it.hostinfo, it.ci, it.segs)
  146. if it.relayHI != nil {
  147. // The relay bookkeeping inline sends do per segment in prepareSendVia, once per item
  148. f.connectionManager.OutNoRebind(it.relayHI)
  149. f.connectionManager.RelayUsed(it.relay.LocalIndex)
  150. it.relayCounter = r.need(it.relayHI, it.relayHI.ConnectionState, it.segs)
  151. }
  152. items = append(items, it)
  153. }
  154. return items
  155. }
  156. // need adds n counters to ci's tally for this batch and returns the offset of the first one within it.
  157. func (r *txReserver) need(hostinfo *HostInfo, ci *ConnectionState, n uint64) uint64 {
  158. t := r.tunnel(ci)
  159. if t == nil {
  160. r.tunnels = append(r.tunnels, txTunnel{ci: ci, hostinfo: hostinfo})
  161. t = &r.tunnels[len(r.tunnels)-1]
  162. }
  163. off := t.n
  164. t.n += n
  165. return off
  166. }
  167. // tunnel finds ci's tally, a batch rarely touches more than a couple of tunnels so a scan beats a map.
  168. func (r *txReserver) tunnel(ci *ConnectionState) *txTunnel {
  169. for i := range r.tunnels {
  170. if r.tunnels[i].ci == ci {
  171. return &r.tunnels[i]
  172. }
  173. }
  174. return nil
  175. }
  176. // reserve takes the counters collect tallied and stamps them onto items, dropping the items of any tunnel whose
  177. // counter is exhausted. It returns the items that are left.
  178. func (r *txReserver) reserve(items []txItem) []txItem {
  179. f := r.f
  180. // One atomic add per tunnel reserves the whole batch's counters before any worker sees it, so counter order on a
  181. // tunnel is dispatch order, which is the order the batches are flushed in. Reservation and the sender's order are
  182. // both taken on the reader goroutine, so there is no window for another batch to slip in between.
  183. for i := range r.tunnels {
  184. t := &r.tunnels[i]
  185. end := t.ci.messageCounter.Add(t.n)
  186. t.base = end - t.n + 1
  187. if end >= RejectAfterMessages {
  188. t.ci.messageCounter.Store(RejectAfterMessages)
  189. t.exhausted = true
  190. f.messageMetrics.TxExhausted(int64(t.n))
  191. if t.base <= RejectAfterMessages {
  192. t.hostinfo.logger(f.l).Error("Dropping outbound packets, tunnel message counter is exhausted")
  193. }
  194. }
  195. }
  196. n := 0
  197. for _, it := range items {
  198. t := r.tunnel(it.ci)
  199. if t.exhausted {
  200. continue
  201. }
  202. it.counter += t.base
  203. if it.relayHI != nil {
  204. rt := r.tunnel(it.relayHI.ConnectionState)
  205. if rt.exhausted {
  206. continue
  207. }
  208. it.relayCounter += rt.base
  209. }
  210. items[n] = it
  211. n++
  212. }
  213. clear(items[n:])
  214. return items[:n]
  215. }