connection_state_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package nebula
  2. import (
  3. "net/netip"
  4. "strconv"
  5. "sync"
  6. "testing"
  7. "time"
  8. "github.com/flynn/noise"
  9. "github.com/rcrowley/go-metrics"
  10. "github.com/slackhq/nebula/cert"
  11. ct "github.com/slackhq/nebula/cert_test"
  12. "github.com/slackhq/nebula/handshake"
  13. "github.com/slackhq/nebula/header"
  14. "github.com/slackhq/nebula/overlay"
  15. "github.com/slackhq/nebula/test"
  16. "github.com/slackhq/nebula/udp"
  17. "github.com/stretchr/testify/assert"
  18. "github.com/stretchr/testify/require"
  19. )
  20. // runTestHandshake runs a complete IX handshake between two freshly-built
  21. // peers and returns the initiator and responder Results. Used to produce
  22. // real cipher states for tests that need to exercise post-handshake glue.
  23. func runTestHandshake(t *testing.T) (initR, respR *handshake.Result) {
  24. t.Helper()
  25. ca, _, caKey, _ := ct.NewTestCaCert(
  26. cert.Version2, cert.Curve_CURVE25519, time.Time{}, time.Time{}, nil, nil, nil,
  27. )
  28. caPool := ct.NewTestCAPool(ca)
  29. makeCreds := func(name string, networks []netip.Prefix) handshake.GetCredentialFunc {
  30. c, _, rawKey, _ := ct.NewTestCert(
  31. cert.Version2, cert.Curve_CURVE25519, ca, caKey,
  32. name, ca.NotBefore(), ca.NotAfter(), networks, nil, nil,
  33. )
  34. priv, _, _, err := cert.UnmarshalPrivateKeyFromPEM(rawKey)
  35. require.NoError(t, err)
  36. hsBytes, err := c.MarshalForHandshakes()
  37. require.NoError(t, err)
  38. ncs := noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256)
  39. cred := handshake.NewCredential(c, hsBytes, priv, ncs)
  40. return func(v cert.Version) *handshake.Credential {
  41. if v == cert.Version2 {
  42. return cred
  43. }
  44. return nil
  45. }
  46. }
  47. verifier := func(c cert.Certificate) (*cert.CachedCertificate, error) {
  48. return caPool.VerifyCertificate(time.Now(), c)
  49. }
  50. initCreds := makeCreds("initiator", []netip.Prefix{netip.MustParsePrefix("10.0.0.1/24")})
  51. respCreds := makeCreds("responder", []netip.Prefix{netip.MustParsePrefix("10.0.0.2/24")})
  52. initM, err := handshake.NewMachine(
  53. cert.Version2, initCreds, verifier,
  54. func() (uint32, error) { return 1000, nil },
  55. true, header.HandshakeIXPSK0,
  56. )
  57. require.NoError(t, err)
  58. respM, err := handshake.NewMachine(
  59. cert.Version2, respCreds, verifier,
  60. func() (uint32, error) { return 2000, nil },
  61. false, header.HandshakeIXPSK0,
  62. )
  63. require.NoError(t, err)
  64. msg1, err := initM.Initiate(nil)
  65. require.NoError(t, err)
  66. resp, respR, err := respM.ProcessPacket(nil, msg1)
  67. require.NoError(t, err)
  68. require.NotNil(t, respR)
  69. _, initR, err = initM.ProcessPacket(nil, resp)
  70. require.NoError(t, err)
  71. require.NotNil(t, initR)
  72. return initR, respR
  73. }
  74. func TestConnectionState_NextMessageCounter(t *testing.T) {
  75. cs := &ConnectionState{}
  76. cs.messageCounter.Store(RejectAfterMessages - 2)
  77. c, ok := cs.NextMessageCounter()
  78. assert.True(t, ok)
  79. assert.Equal(t, RejectAfterMessages-1, c)
  80. // Hitting the limit refuses and pins the counter there
  81. c, ok = cs.NextMessageCounter()
  82. assert.False(t, ok)
  83. assert.Equal(t, RejectAfterMessages, c)
  84. assert.Equal(t, RejectAfterMessages, cs.messageCounter.Load())
  85. // Continued send attempts stay refused and the counter never wraps
  86. for range 10 {
  87. _, ok = cs.NextMessageCounter()
  88. assert.False(t, ok)
  89. }
  90. assert.Equal(t, RejectAfterMessages, cs.messageCounter.Load())
  91. }
  92. // TestSendNoMetricsDropsExhausted drives the send path to the exhausted drop; metric and out flag prove it.
  93. func TestSendNoMetricsDropsExhausted(t *testing.T) {
  94. initR, _ := runTestHandshake(t)
  95. ci, err := newConnectionStateFromResult(initR)
  96. require.NoError(t, err)
  97. ci.messageCounter.Store(RejectAfterMessages - 1)
  98. f := &Interface{l: test.NewLogger(), messageMetrics: &MessageMetrics{txExhausted: metrics.NewCounter()}}
  99. hostinfo := &HostInfo{vpnAddrs: []netip.Addr{netip.MustParseAddr("10.0.0.1")}, ConnectionState: ci}
  100. f.sendNoMetrics(header.Message, 0, ci, hostinfo, netip.AddrPort{}, []byte{}, make([]byte, 12), make([]byte, mtu), 0)
  101. // The crossing send is refused: it records an exhaustion drop and never reaches connectionManager.Out.
  102. assert.Equal(t, int64(1), f.messageMetrics.txExhausted.Count())
  103. assert.False(t, hostinfo.sentSinceCheck())
  104. }
  105. // TestSendNoMetricsCloseTunnelKeepsRebindEpoch pins that a closing tunnel does not consume a rebind, a later
  106. // packet on a re-established tunnel still needs that edge to trigger the far-side punch.
  107. func TestSendNoMetricsCloseTunnelKeepsRebindEpoch(t *testing.T) {
  108. initR, _ := runTestHandshake(t)
  109. ci, err := newConnectionStateFromResult(initR)
  110. require.NoError(t, err)
  111. f := &Interface{
  112. l: test.NewLogger(),
  113. messageMetrics: &MessageMetrics{txExhausted: metrics.NewCounter()},
  114. writers: []udp.Conn{udp.NoopConn{}},
  115. connectionManager: &connectionManager{},
  116. }
  117. hostinfo := &HostInfo{vpnAddrs: []netip.Addr{netip.MustParseAddr("10.0.0.1")}, ConnectionState: ci}
  118. // Tunnel is on epoch 0, then we rebind.
  119. hostinfo.markOut(0)
  120. f.rebindEpoch.Add(1)
  121. remote := netip.MustParseAddrPort("10.0.0.2:4242")
  122. f.sendNoMetrics(header.CloseTunnel, 0, ci, hostinfo, remote, []byte{}, make([]byte, 12), make([]byte, mtu), 0)
  123. // markOut at the new epoch still reports the move, so the edge was preserved.
  124. assert.True(t, hostinfo.markOut(1), "a CloseTunnel send must not consume the rebind epoch")
  125. }
  126. // capturingConn is a udp.Conn that records every WriteTo.
  127. type capturingConn struct {
  128. udp.NoopConn
  129. writes [][]byte
  130. addrs []netip.AddrPort
  131. }
  132. func (c *capturingConn) WriteTo(b []byte, addr netip.AddrPort) error {
  133. c.writes = append(c.writes, append([]byte(nil), b...))
  134. c.addrs = append(c.addrs, addr)
  135. return nil
  136. }
  137. // TestSendNoMetricsViaRelay sends through a relay from the mtu sized buffer the cached packet flush uses. Payloads up
  138. // to overlay.MaxMTU go out intact, anything bigger is dropped rather than outgrowing the buffer, which once panicked.
  139. func TestSendNoMetricsViaRelay(t *testing.T) {
  140. tests := []struct {
  141. n int
  142. sent bool
  143. }{
  144. {0, true},
  145. {1300, true},
  146. {overlay.MaxMTU, true},
  147. {overlay.MaxMTU + 1, false}, // too big for the relay's tag
  148. {mtu - 47, false}, // too big for the inner tag, this panicked
  149. {9000, false}, // a full packet at tun.mtu 9000
  150. }
  151. for _, tt := range tests {
  152. t.Run(strconv.Itoa(tt.n), func(t *testing.T) {
  153. // One tunnel to the target, carried end to end, and one to the relay that wraps it
  154. targetInit, targetResp := runTestHandshake(t)
  155. relayInit, relayResp := runTestHandshake(t)
  156. ci, err := newConnectionStateFromResult(targetInit)
  157. require.NoError(t, err)
  158. relayCI, err := newConnectionStateFromResult(relayInit)
  159. require.NoError(t, err)
  160. targetAddr := netip.MustParseAddr("10.0.0.2")
  161. relayAddr := netip.MustParseAddr("10.0.0.3")
  162. relayRemote := netip.MustParseAddrPort("192.0.2.1:4242")
  163. relay := &Relay{Type: TerminalType, State: Established, LocalIndex: 5, RemoteIndex: 6, PeerAddr: targetAddr}
  164. relayHI := &HostInfo{
  165. vpnAddrs: []netip.Addr{relayAddr},
  166. ConnectionState: relayCI,
  167. relayState: RelayState{
  168. relayForByAddr: map[netip.Addr]*Relay{targetAddr: relay},
  169. relayForByIdx: map[uint32]*Relay{relay.LocalIndex: relay},
  170. },
  171. }
  172. relayHI.remote.Store(&relayRemote)
  173. // No remote of its own, so the send has to go via the relay
  174. hostinfo := &HostInfo{
  175. vpnAddrs: []netip.Addr{targetAddr},
  176. ConnectionState: ci,
  177. remoteIndexId: 7,
  178. relayState: RelayState{
  179. relays: []netip.Addr{relayAddr},
  180. relayForByAddr: map[netip.Addr]*Relay{},
  181. relayForByIdx: map[uint32]*Relay{},
  182. },
  183. }
  184. l := test.NewLogger()
  185. hm := newHostMap(l)
  186. hm.Hosts[relayAddr] = relayHI
  187. conn := &capturingConn{}
  188. f := &Interface{
  189. l: l,
  190. hostMap: hm,
  191. messageMetrics: &MessageMetrics{txExhausted: metrics.NewCounter()},
  192. writers: []udp.Conn{conn},
  193. }
  194. f.connectionManager = &connectionManager{intf: f, relayUsed: map[uint32]struct{}{}, relayUsedLock: &sync.RWMutex{}}
  195. payload := make([]byte, tt.n)
  196. for i := range payload {
  197. payload[i] = byte(i)
  198. }
  199. counter := ci.messageCounter.Load()
  200. f.sendNoMetrics(header.Message, 0, ci, hostinfo, netip.AddrPort{}, payload, make([]byte, 12), make([]byte, mtu), 0)
  201. if !tt.sent {
  202. assert.Empty(t, conn.writes)
  203. assert.Equal(t, counter, ci.messageCounter.Load(), "a dropped packet must not spend a counter")
  204. return
  205. }
  206. require.Len(t, conn.writes, 1)
  207. assert.Equal(t, relayRemote, conn.addrs[0])
  208. pkt := conn.writes[0]
  209. // The relay authenticates the whole packet under its own header
  210. outer := &header.H{}
  211. require.NoError(t, outer.Parse(pkt))
  212. assert.Equal(t, header.MessageRelay, outer.Subtype)
  213. assert.Equal(t, relay.RemoteIndex, outer.RemoteIndex)
  214. relayPeer, err := newConnectionStateFromResult(relayResp)
  215. require.NoError(t, err)
  216. require.NoError(t, relayPeer.VerifyRelay(l, outer.MessageCounter, pkt, make([]byte, 12)))
  217. // Inside it, the target decrypts the end to end packet back to the payload
  218. inner := pkt[header.Len : len(pkt)-relayPeer.dKey.Overhead()]
  219. h := &header.H{}
  220. require.NoError(t, h.Parse(inner))
  221. assert.Equal(t, header.Message, h.Type)
  222. assert.Equal(t, hostinfo.remoteIndexId, h.RemoteIndex)
  223. targetPeer, err := newConnectionStateFromResult(targetResp)
  224. require.NoError(t, err)
  225. got, err := targetPeer.Decrypt(l, h.MessageCounter, inner, make([]byte, 12))
  226. require.NoError(t, err)
  227. assert.Equal(t, payload, got)
  228. })
  229. }
  230. }
  231. func TestNewConnectionStateFromResult(t *testing.T) {
  232. initR, respR := runTestHandshake(t)
  233. t.Run("initiator", func(t *testing.T) {
  234. ci, err := newConnectionStateFromResult(initR)
  235. require.NoError(t, err)
  236. assert.True(t, ci.initiator)
  237. assert.Equal(t, initR.MyCert, ci.myCert)
  238. assert.Equal(t, initR.RemoteCert, ci.peerCert)
  239. assert.NotNil(t, ci.eKey)
  240. assert.NotNil(t, ci.dKey)
  241. // IX has 2 handshake messages; the next data-plane send is counter=3.
  242. assert.Equal(t, uint64(2), ci.messageCounter.Load(),
  243. "messageCounter must equal Result.MessageIndex so the next send is N+1")
  244. // Both handshake counters must be marked seen so they don't appear lost.
  245. // Check returns false if an index has already been recorded.
  246. assert.False(t, ci.window.Check(nil, 1), "counter 1 must already be seen")
  247. assert.False(t, ci.window.Check(nil, 2), "counter 2 must already be seen")
  248. // Counter 3 is the next data-plane message and must NOT be pre-marked.
  249. assert.True(t, ci.window.Check(nil, 3), "counter 3 must not be pre-seeded")
  250. })
  251. t.Run("message index too large is refused", func(t *testing.T) {
  252. bad := *initR
  253. bad.MessageIndex = ReplayWindow
  254. ci, err := newConnectionStateFromResult(&bad)
  255. require.Error(t, err)
  256. assert.Nil(t, ci)
  257. })
  258. t.Run("responder", func(t *testing.T) {
  259. ci, err := newConnectionStateFromResult(respR)
  260. require.NoError(t, err)
  261. assert.False(t, ci.initiator)
  262. assert.Equal(t, respR.MyCert, ci.myCert)
  263. assert.Equal(t, respR.RemoteCert, ci.peerCert)
  264. assert.NotNil(t, ci.eKey)
  265. assert.NotNil(t, ci.dKey)
  266. assert.Equal(t, uint64(2), ci.messageCounter.Load())
  267. })
  268. }