outside_relay_test.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. package nebula
  2. import (
  3. "bytes"
  4. "net/netip"
  5. "sync"
  6. "testing"
  7. "github.com/flynn/noise"
  8. "github.com/gaissmai/bart"
  9. "github.com/rcrowley/go-metrics"
  10. "github.com/slackhq/nebula/header"
  11. "github.com/slackhq/nebula/noiseutil"
  12. "github.com/slackhq/nebula/overlay/batch"
  13. "github.com/slackhq/nebula/test"
  14. "github.com/slackhq/nebula/udp"
  15. "github.com/stretchr/testify/require"
  16. )
  17. type relayBatchWrite struct {
  18. packets [][]byte
  19. remotes []netip.AddrPort
  20. batched bool
  21. }
  22. type relayBatchConn struct {
  23. udp.NoopConn
  24. read func(udp.EncReader, func())
  25. writes []relayBatchWrite
  26. }
  27. func (c *relayBatchConn) ListenOut(r udp.EncReader, flush func()) error {
  28. c.read(r, flush)
  29. return nil
  30. }
  31. func (c *relayBatchConn) WriteBatch(bufs [][]byte, addrs []netip.AddrPort) (int, error) {
  32. w := relayBatchWrite{remotes: append([]netip.AddrPort(nil), addrs...), batched: true}
  33. for _, b := range bufs {
  34. w.packets = append(w.packets, bytes.Clone(b))
  35. }
  36. c.writes = append(c.writes, w)
  37. return len(bufs), nil
  38. }
  39. func (c *relayBatchConn) WriteTo(b []byte, addr netip.AddrPort) error {
  40. c.writes = append(c.writes, relayBatchWrite{packets: [][]byte{bytes.Clone(b)}, remotes: []netip.AddrPort{addr}})
  41. return nil
  42. }
  43. func (c *relayBatchConn) packets() [][]byte {
  44. var packets [][]byte
  45. for _, w := range c.writes {
  46. packets = append(packets, w.packets...)
  47. }
  48. return packets
  49. }
  50. type relayBatchPath struct {
  51. f *Interface
  52. conn *relayBatchConn
  53. source, target *HostInfo
  54. incoming, outgoing *Relay
  55. sourceKey noiseutil.CipherState
  56. cipher noise.CipherFunc
  57. counter uint64
  58. }
  59. // Separate cipher instances let the reference SendVia path use the same keys and
  60. // counters without rewinding an AEAD instance (including FIPS nonce checks).
  61. func relayBatchKey(cipher noise.CipherFunc, id byte) noiseutil.CipherState {
  62. suite := noise.NewCipherSuite(noise.DH25519, cipher, noise.HashSHA256)
  63. return noiseutil.NewCipherState(noise.UnsafeNewCipherState(suite, [32]byte{id}, 0), cipher)
  64. }
  65. func newRelayBatchPath(t *testing.T, cipher noise.CipherFunc) *relayBatchPath {
  66. t.Helper()
  67. l := test.NewLogger()
  68. conn := &relayBatchConn{}
  69. f := &Interface{
  70. ctx: t.Context(), l: l, hostMap: newHostMap(l), outside: conn,
  71. writers: []udp.Conn{conn}, lightHouse: &LightHouse{l: l},
  72. myVpnNetworksTable: &bart.Lite{}, metricTxDropped: metrics.NewCounter(),
  73. batchers: []*batch.MultiCoalescer{batch.NewMultiCoalescer(&capturingTun{}, l)},
  74. }
  75. f.connectionManager = &connectionManager{
  76. intf: f, relayUsed: map[uint32]struct{}{}, relayUsedLock: &sync.RWMutex{},
  77. }
  78. newHost := func(addr string, remote string, id byte) *HostInfo {
  79. h := &HostInfo{
  80. vpnAddrs: []netip.Addr{netip.MustParseAddr(addr)},
  81. ConnectionState: &ConnectionState{
  82. eKey: relayBatchKey(cipher, id), dKey: relayBatchKey(cipher, id), window: NewBits(ReplayWindow),
  83. },
  84. relayState: RelayState{relayForByAddr: map[netip.Addr]*Relay{}, relayForByIdx: map[uint32]*Relay{}},
  85. }
  86. u := netip.MustParseAddrPort(remote)
  87. h.remote.Store(&u)
  88. f.hostMap.Hosts[h.vpnAddrs[0]] = h
  89. return h
  90. }
  91. source := newHost("192.0.2.1", "198.51.100.1:4242", 1)
  92. target := newHost("192.0.2.2", "198.51.100.2:4242", 2)
  93. incoming := &Relay{Type: ForwardingType, State: Established, LocalIndex: 301, RemoteIndex: 302, PeerAddr: target.vpnAddrs[0]}
  94. outgoing := &Relay{Type: ForwardingType, State: Established, LocalIndex: 303, RemoteIndex: 304, PeerAddr: source.vpnAddrs[0]}
  95. source.relayState.InsertRelay(incoming.PeerAddr, incoming.LocalIndex, incoming)
  96. target.relayState.InsertRelay(outgoing.PeerAddr, outgoing.LocalIndex, outgoing)
  97. f.hostMap.Relays[incoming.LocalIndex] = source
  98. f.hostMap.Indexes[100] = source
  99. return &relayBatchPath{f: f, conn: conn, source: source, target: target, incoming: incoming, outgoing: outgoing, sourceKey: relayBatchKey(cipher, 1), cipher: cipher}
  100. }
  101. func (p *relayBatchPath) wrap(t *testing.T, payload []byte) []byte {
  102. t.Helper()
  103. p.counter++
  104. b := header.Encode(make([]byte, header.Len), header.Version, header.Message, header.MessageRelay, p.incoming.LocalIndex, p.counter)
  105. b = append(b, payload...)
  106. b, err := p.sourceKey.EncryptDanger(b, b, nil, p.counter, make([]byte, 12))
  107. require.NoError(t, err)
  108. return b
  109. }
  110. func (p *relayBatchPath) reference(payloads [][]byte) [][]byte {
  111. conn := &relayBatchConn{}
  112. f := &Interface{writers: []udp.Conn{conn}, connectionManager: p.f.connectionManager, l: p.f.l}
  113. h := &HostInfo{ConnectionState: &ConnectionState{eKey: relayBatchKey(p.cipher, 2)}}
  114. u := p.target.GetRemote()
  115. h.remote.Store(&u)
  116. for _, payload := range payloads {
  117. f.SendVia(h, p.outgoing, payload, make([]byte, 12), make([]byte, 0, len(payload)+header.MaxOverhead), false, 0)
  118. }
  119. return conn.packets()
  120. }
  121. func TestRelayReceiveBatch(t *testing.T) {
  122. for _, cipher := range []noise.CipherFunc{noiseutil.CipherAESGCM, noise.CipherChaChaPoly} {
  123. t.Run(cipher.CipherName(), func(t *testing.T) {
  124. p := newRelayBatchPath(t, cipher)
  125. const count = 8
  126. var payloads, wire [][]byte
  127. for i := range count * 2 {
  128. payload := bytes.Repeat([]byte{byte(i)}, 64)
  129. payloads = append(payloads, payload)
  130. wire = append(wire, p.wrap(t, payload))
  131. }
  132. want := p.reference(payloads)
  133. // Model a listener reusing the same slots for the next receive batch.
  134. buffers := make([][]byte, count)
  135. for i := range buffers {
  136. buffers[i] = make([]byte, len(wire[i]))
  137. }
  138. p.conn.read = func(r udp.EncReader, flush func()) {
  139. for b := range 2 {
  140. for i := range count {
  141. copy(buffers[i], wire[b*count+i])
  142. r(p.source.GetRemote(), buffers[i])
  143. require.Len(t, p.conn.writes, b, "forwarding waits for the batch boundary")
  144. }
  145. flush()
  146. require.Len(t, p.conn.writes, b+1, "flush must send before receive slots are reused")
  147. for _, buffer := range buffers {
  148. clear(buffer)
  149. }
  150. }
  151. }
  152. p.f.listenOut(0)
  153. require.Equal(t, want, p.conn.packets())
  154. for _, w := range p.conn.writes {
  155. require.True(t, w.batched)
  156. require.Len(t, w.packets, count)
  157. for _, remote := range w.remotes {
  158. require.Equal(t, p.target.GetRemote(), remote)
  159. }
  160. }
  161. })
  162. }
  163. }
  164. func TestRelayBatchMixedDestinations(t *testing.T) {
  165. p := newRelayBatchPath(t, noise.CipherChaChaPoly)
  166. first, second := p.target.GetRemote(), netip.MustParseAddrPort("198.51.100.3:4242")
  167. remotes := []netip.AddrPort{first, first, second, second, first}
  168. var payloads, wire [][]byte
  169. for i := range remotes {
  170. payload := []byte{byte(i)}
  171. payloads = append(payloads, payload)
  172. wire = append(wire, p.wrap(t, payload))
  173. }
  174. want := p.reference(payloads)
  175. p.conn.read = func(r udp.EncReader, flush func()) {
  176. for i, remote := range remotes {
  177. p.target.remote.Store(&remote)
  178. r(p.source.GetRemote(), wire[i])
  179. }
  180. flush()
  181. }
  182. p.f.listenOut(0)
  183. require.Equal(t, want, p.conn.packets())
  184. require.Len(t, p.conn.writes, 1, "WriteBatch splits runs by destination, the batch does not need to")
  185. require.Equal(t, remotes, p.conn.writes[0].remotes)
  186. }
  187. func TestRelayBatchCapacity(t *testing.T) {
  188. p := newRelayBatchPath(t, noise.CipherChaChaPoly)
  189. p.conn.read = func(r udp.EncReader, flush func()) {
  190. for i := range batch.SendBatchCap + 1 {
  191. r(p.source.GetRemote(), p.wrap(t, []byte{byte(i)}))
  192. }
  193. require.Len(t, p.conn.writes, 1)
  194. require.Len(t, p.conn.writes[0].packets, batch.SendBatchCap)
  195. flush()
  196. }
  197. p.f.listenOut(0)
  198. require.Len(t, p.conn.writes, 2)
  199. require.Len(t, p.conn.writes[1].packets, 1)
  200. }
  201. func TestRelayBatchRemovedTarget(t *testing.T) {
  202. for _, remove := range []string{"host", "relay", "disestablished"} {
  203. t.Run(remove, func(t *testing.T) {
  204. p := newRelayBatchPath(t, noise.CipherChaChaPoly)
  205. first, removed := p.wrap(t, []byte("first")), p.wrap(t, []byte("removed"))
  206. want := p.reference([][]byte{[]byte("first")})
  207. p.conn.read = func(r udp.EncReader, flush func()) {
  208. r(p.source.GetRemote(), first)
  209. switch remove {
  210. case "host":
  211. delete(p.f.hostMap.Hosts, p.target.vpnAddrs[0])
  212. case "relay":
  213. delete(p.target.relayState.relayForByAddr, p.source.vpnAddrs[0])
  214. case "disestablished":
  215. p.target.relayState.UpdateRelayForByIpState(p.source.vpnAddrs[0], Disestablished)
  216. }
  217. r(p.source.GetRemote(), removed)
  218. flush()
  219. }
  220. p.f.listenOut(0)
  221. require.Len(t, p.conn.writes, 1)
  222. require.Equal(t, want, p.conn.packets(), "only the packet accepted before removal is sent")
  223. })
  224. }
  225. }