1
0

lanes_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. package nebula
  2. import (
  3. "net/netip"
  4. "testing"
  5. "time"
  6. "github.com/gaissmai/bart"
  7. "github.com/slackhq/nebula/cert"
  8. "github.com/slackhq/nebula/config"
  9. "github.com/slackhq/nebula/header"
  10. "github.com/slackhq/nebula/overlay/batch"
  11. "github.com/slackhq/nebula/overlay/overlaytest"
  12. "github.com/slackhq/nebula/overlay/tio"
  13. "github.com/slackhq/nebula/test"
  14. "github.com/slackhq/nebula/udp"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/require"
  17. )
  18. func newTestBaseHostInfo(vpnIp netip.Addr, localIdx, remoteIdx uint32, laneCount int) *HostInfo {
  19. base := &HostInfo{
  20. vpnAddrs: []netip.Addr{vpnIp},
  21. localIndexId: localIdx,
  22. remoteIndexId: remoteIdx,
  23. remotes: NewRemoteList([]netip.Addr{vpnIp}, nil),
  24. HandshakePacket: map[uint8][]byte{},
  25. }
  26. base.SetRemote(netip.MustParseAddrPort("192.0.2.1:4242"))
  27. base.lanes = newLaneState(laneCount, uint16(laneCount), 4242, 0)
  28. return base
  29. }
  30. func newTestLaneHostInfo(base *HostInfo, laneIndex uint16, localIdx, remoteIdx uint32, owned bool) *HostInfo {
  31. lane := &HostInfo{
  32. vpnAddrs: base.vpnAddrs,
  33. localIndexId: localIdx,
  34. remoteIndexId: remoteIdx,
  35. remotes: NewRemoteList(base.vpnAddrs, nil),
  36. HandshakePacket: map[uint8][]byte{},
  37. sockIdx: int(laneIndex),
  38. laneIndex: laneIndex,
  39. laneOwned: owned,
  40. parent: base,
  41. }
  42. lane.SetRemote(netip.MustParseAddrPort("192.0.2.1:4243"))
  43. return lane
  44. }
  45. func TestLanePortOffset(t *testing.T) {
  46. a := netip.MustParseAddr("10.0.0.1")
  47. b := netip.MustParseAddr("10.0.0.2")
  48. // Deterministic and in range.
  49. for _, count := range []uint16{1, 2, 3, 4, 16, 256} {
  50. o := lanePortOffset(a, b, count)
  51. assert.Equal(t, o, lanePortOffset(a, b, count), "count %d not deterministic", count)
  52. assert.Less(t, o, count, "count %d out of range", count)
  53. }
  54. assert.Equal(t, uint16(0), lanePortOffset(a, b, 0), "zero port count")
  55. // The two sides' rotations cancel when port counts match, preserving the
  56. // lane-i-reverses-lane-j conntrack pairing.
  57. for _, count := range []uint16{2, 3, 4, 7, 16} {
  58. for i := range 32 {
  59. peer := netip.AddrFrom4([4]byte{192, 0, 2, byte(i)})
  60. oA := lanePortOffset(a, peer, count)
  61. oB := lanePortOffset(peer, a, count)
  62. assert.Equal(t, uint16(0), (oA+oB)%count,
  63. "offsets don't cancel for peer %s count %d", peer, count)
  64. }
  65. }
  66. // Distinct small peers land on distinct rotations of a big peer's range,
  67. // not all on the same first ports.
  68. const bigPeerPorts = 16
  69. distinct := map[uint16]struct{}{}
  70. for i := range 64 {
  71. client := netip.AddrFrom4([4]byte{192, 0, 2, byte(i)})
  72. distinct[lanePortOffset(client, a, bigPeerPorts)] = struct{}{}
  73. }
  74. assert.GreaterOrEqual(t, len(distinct), 8, "64 clients only produced %d distinct offsets", len(distinct))
  75. }
  76. func TestLaneTargetPort(t *testing.T) {
  77. // No rotation: lane i targets base+i, wrapping past the peer's range.
  78. ls := newLaneState(4, 4, 4242, 0)
  79. for i, want := range map[int]uint16{1: 4243, 2: 4244, 3: 4245, 5: 4243} {
  80. assert.Equal(t, want, ls.laneTargetPort(i), "lane %d", i)
  81. }
  82. // Rotation shifts the whole mapping; the wrapped lane lands on the base
  83. // port itself, which is a valid distinct 4-tuple (our source port differs).
  84. ls = newLaneState(4, 4, 4242, 3)
  85. for i, want := range map[int]uint16{1: 4242, 2: 4243, 3: 4244} {
  86. assert.Equal(t, want, ls.laneTargetPort(i), "rotated lane %d", i)
  87. }
  88. // Fewer peer ports than local lanes: rotation still spreads across all of
  89. // the peer's ports.
  90. ls = newLaneState(16, 2, 4242, 1)
  91. assert.Equal(t, uint16(4242), ls.laneTargetPort(1))
  92. assert.Equal(t, uint16(4243), ls.laneTargetPort(2))
  93. }
  94. func TestLaneHostmapLifecycle(t *testing.T) {
  95. l := test.NewLogger()
  96. hostMap := newHostMap(l)
  97. ifce := &Interface{l: l} // connectionManager nil is tolerated by unlockedAddLane
  98. vpnIp := netip.MustParseAddr("172.1.1.2")
  99. base := newTestBaseHostInfo(vpnIp, 100, 200, 4)
  100. hostMap.Lock()
  101. hostMap.unlockedAddHostInfo(base, ifce)
  102. hostMap.Unlock()
  103. lane := newTestLaneHostInfo(base, 1, 101, 201, true)
  104. hostMap.Lock()
  105. hostMap.unlockedAddLane(lane, ifce)
  106. hostMap.Unlock()
  107. base.lanes.txLanes[1].Store(lane)
  108. // The lane is reachable by index (RX demux, recv_error) but never a Hosts primary.
  109. assert.Equal(t, lane, hostMap.QueryIndex(101))
  110. assert.Equal(t, lane, hostMap.QueryReverseIndex(201))
  111. assert.Equal(t, base, hostMap.Hosts[vpnIp])
  112. // A lane can never be promoted to primary.
  113. hostMap.Lock()
  114. assert.False(t, hostMap.unlockedMakePrimary(lane))
  115. hostMap.Unlock()
  116. assert.Equal(t, base, hostMap.Hosts[vpnIp])
  117. // Deleting the lane clears only its slot, applies backoff, and never
  118. // reports "no more tunnels to peer" (final).
  119. final := hostMap.DeleteHostInfo(lane)
  120. assert.False(t, final)
  121. assert.Nil(t, hostMap.QueryIndex(101))
  122. assert.Equal(t, base, hostMap.Hosts[vpnIp])
  123. assert.Nil(t, base.lanes.txLanes[1].Load())
  124. base.lanes.Lock()
  125. assert.Equal(t, uint8(1), base.lanes.txFails[1])
  126. assert.False(t, base.lanes.txPending[1])
  127. assert.True(t, base.lanes.txRetryAt[1].After(time.Now()))
  128. base.lanes.Unlock()
  129. // Idempotent: deleting again must not bump the backoff further.
  130. hostMap.DeleteHostInfo(lane)
  131. base.lanes.Lock()
  132. assert.Equal(t, uint8(2), base.lanes.txFails[1]) // noteLaneFailure still runs, but slot CAS is a no-op
  133. base.lanes.Unlock()
  134. }
  135. func TestLaneHostmapCascadeDelete(t *testing.T) {
  136. l := test.NewLogger()
  137. hostMap := newHostMap(l)
  138. ifce := &Interface{l: l}
  139. vpnIp := netip.MustParseAddr("172.1.1.3")
  140. base := newTestBaseHostInfo(vpnIp, 300, 400, 4)
  141. hostMap.Lock()
  142. hostMap.unlockedAddHostInfo(base, ifce)
  143. hostMap.Unlock()
  144. owned := newTestLaneHostInfo(base, 1, 301, 401, true)
  145. peer := newTestLaneHostInfo(base, 2, 302, 402, false)
  146. hostMap.Lock()
  147. hostMap.unlockedAddLane(owned, ifce)
  148. hostMap.unlockedAddLane(peer, ifce)
  149. hostMap.Unlock()
  150. base.lanes.txLanes[1].Store(owned)
  151. base.lanes.Lock()
  152. base.lanes.peerLanes = append(base.lanes.peerLanes, peer)
  153. base.lanes.Unlock()
  154. // Deleting the base takes the whole lane family with it.
  155. final := hostMap.DeleteHostInfo(base)
  156. assert.True(t, final)
  157. assert.Nil(t, hostMap.QueryIndex(300))
  158. assert.Nil(t, hostMap.QueryIndex(301))
  159. assert.Nil(t, hostMap.QueryIndex(302))
  160. assert.Nil(t, hostMap.Hosts[vpnIp])
  161. }
  162. // Regression: deleting a hostinfo whose pending entry is NOT the one recorded
  163. // in vpnIps (e.g. a lane, whose vpnAddrs alias the base's) must not evict a
  164. // concurrently pending base handshake for the same address.
  165. func TestHandshakeManagerVpnIpsIdentityDelete(t *testing.T) {
  166. l := test.NewLogger()
  167. hostMap := newHostMap(l)
  168. lh := newTestLighthouse()
  169. hm := NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig)
  170. vpnIp := netip.MustParseAddr("172.1.1.4")
  171. pendingBase := hm.StartHandshake(vpnIp, nil)
  172. require.NotNil(t, pendingBase)
  173. other := &HostInfo{vpnAddrs: []netip.Addr{vpnIp}, localIndexId: 999}
  174. hm.DeleteHostInfo(other)
  175. // The pending base handshake must still be tracked.
  176. assert.Equal(t, pendingBase, hm.QueryVpnAddr(vpnIp))
  177. // And deleting the actual owner still works.
  178. hm.DeleteHostInfo(pendingBase)
  179. assert.Nil(t, hm.QueryVpnAddr(vpnIp))
  180. }
  181. func newLaneTestConnectionManager(hostMap *HostMap) (*connectionManager, *Interface) {
  182. l := test.NewLogger()
  183. lh := newTestLighthouse()
  184. cs := &CertState{
  185. initiatingVersion: cert.Version1,
  186. privateKey: []byte{},
  187. v1Cert: &dummyCert{version: cert.Version1},
  188. v1Credential: nil,
  189. }
  190. ifce := &Interface{
  191. hostMap: hostMap,
  192. inside: &overlaytest.NoopTun{},
  193. outside: &udp.NoopConn{},
  194. firewall: &Firewall{},
  195. lightHouse: lh,
  196. pki: &PKI{},
  197. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  198. myVpnNetworksTable: new(bart.Lite),
  199. l: l,
  200. }
  201. ifce.pki.cs.Store(cs)
  202. conf := config.NewC(test.NewLogger())
  203. punchy := NewPunchyFromConfig(test.NewLogger(), conf, nil)
  204. cm := newConnectionManagerFromConfig(test.NewLogger(), conf, hostMap, punchy)
  205. cm.intf = ifce
  206. ifce.connectionManager = cm
  207. ifce.handshakeManager.f = ifce
  208. return cm, ifce
  209. }
  210. func TestLaneTrafficDecision(t *testing.T) {
  211. hostMap := newHostMap(test.NewLogger())
  212. cm, ifce := newLaneTestConnectionManager(hostMap)
  213. vpnIp := netip.MustParseAddr("172.1.1.5")
  214. base := newTestBaseHostInfo(vpnIp, 500, 600, 4)
  215. base.ConnectionState = &ConnectionState{}
  216. hostMap.Lock()
  217. hostMap.unlockedAddHostInfo(base, ifce)
  218. hostMap.Unlock()
  219. lane := newTestLaneHostInfo(base, 1, 501, 601, true)
  220. lane.ConnectionState = &ConnectionState{}
  221. hostMap.Lock()
  222. hostMap.unlockedAddLane(lane, ifce)
  223. hostMap.Unlock()
  224. base.lanes.txLanes[1].Store(lane)
  225. now := time.Now()
  226. // A lane with inbound traffic is alive and never swaps primary or
  227. // migrates relays.
  228. lane.in.Store(true)
  229. decision, resolved, _ := cm.makeTrafficDecision(lane.localIndexId, now)
  230. assert.Equal(t, doNothing, decision)
  231. assert.Equal(t, lane, resolved)
  232. assert.False(t, lane.pendingDeletion.Load())
  233. // An idle lane gets an active keepalive test...
  234. decision, _, _ = cm.makeTrafficDecision(lane.localIndexId, now)
  235. assert.Equal(t, sendTestPacket, decision)
  236. assert.True(t, lane.pendingDeletion.Load())
  237. // ...and is declared dead when the test goes unanswered.
  238. decision, _, _ = cm.makeTrafficDecision(lane.localIndexId, now)
  239. assert.Equal(t, deleteTunnel, decision)
  240. }
  241. func TestBaseInactiveConsidersLanes(t *testing.T) {
  242. hostMap := newHostMap(test.NewLogger())
  243. cm, _ := newLaneTestConnectionManager(hostMap)
  244. cm.dropInactive.Store(true)
  245. cm.inactivityTimeout.Store(int64(10 * time.Minute))
  246. now := time.Now()
  247. vpnIp := netip.MustParseAddr("172.1.1.6")
  248. base := newTestBaseHostInfo(vpnIp, 700, 800, 4)
  249. base.lastUsed = now.Add(-time.Hour)
  250. // Base alone: inactive.
  251. _, inactive := cm.isInactive(base, now)
  252. assert.True(t, inactive)
  253. // A recently used lane keeps the base alive.
  254. lane := newTestLaneHostInfo(base, 1, 701, 801, true)
  255. lane.lastUsed = now.Add(-time.Minute)
  256. base.lanes.txLanes[1].Store(lane)
  257. _, inactive = cm.isInactive(base, now)
  258. assert.False(t, inactive)
  259. // Peer-owned lanes count too.
  260. base.lanes.txLanes[1].Store(nil)
  261. base.lanes.Lock()
  262. base.lanes.peerLanes = append(base.lanes.peerLanes, lane)
  263. base.lanes.Unlock()
  264. _, inactive = cm.isInactive(base, now)
  265. assert.False(t, inactive)
  266. }
  267. // recordingBatchWriter satisfies batch's writer interface and records what
  268. // was flushed to it.
  269. type recordingBatchWriter struct {
  270. bufs [][]byte
  271. dsts []netip.AddrPort
  272. }
  273. func (w *recordingBatchWriter) WriteBatch(bufs [][]byte, addrs []netip.AddrPort, outerECNs []byte) error {
  274. for i := range bufs {
  275. w.bufs = append(w.bufs, append([]byte(nil), bufs[i]...))
  276. w.dsts = append(w.dsts, addrs[i])
  277. }
  278. return nil
  279. }
  280. func TestSendInsideMessageLaneSwap(t *testing.T) {
  281. hostMap := newHostMap(test.NewLogger())
  282. cm, ifce := newLaneTestConnectionManager(hostMap)
  283. _ = cm
  284. vpnIp := netip.MustParseAddr("172.1.1.7")
  285. base := newTestBaseHostInfo(vpnIp, 900, 1000, 4)
  286. lane := newTestLaneHostInfo(base, 1, 901, 1001, true)
  287. // Real cipher states from a real handshake so encryption works.
  288. baseInit, _ := runTestHandshake(t)
  289. laneInit, _ := runTestHandshake(t)
  290. base.ConnectionState = newConnectionStateFromResult(baseInit)
  291. lane.ConnectionState = newConnectionStateFromResult(laneInit)
  292. baseWriter := &recordingBatchWriter{}
  293. laneWriter := &recordingBatchWriter{}
  294. newTx := func(laneSlot int) *txQueue {
  295. return &txQueue{
  296. laneSlot: laneSlot,
  297. base: batch.NewSendBatch(baseWriter, batch.SendBatchCap, 1<<16),
  298. lane: batch.NewSendBatch(laneWriter, batch.SendBatchCap, 1<<16),
  299. }
  300. }
  301. tx1 := newTx(1)
  302. tx2 := newTx(2)
  303. pkt := tio.Packet{Bytes: []byte{0x45, 0, 0, 4, 1, 2, 3, 4}}
  304. nb := make([]byte, 12)
  305. // With the lane published, slot-1 traffic uses the lane session and the
  306. // lane batch.
  307. base.lanes.txLanes[1].Store(lane)
  308. ifce.sendInsideMessage(base, pkt, nb, tx1)
  309. tx1.flush(ifce.l)
  310. require.Len(t, laneWriter.bufs, 1)
  311. require.Empty(t, baseWriter.bufs)
  312. assert.Equal(t, lane.GetRemote(), laneWriter.dsts[0])
  313. h := &header.H{}
  314. require.NoError(t, h.Parse(laneWriter.bufs[0]))
  315. assert.Equal(t, lane.remoteIndexId, h.RemoteIndex)
  316. // An overflow routine sharing slot 1 (multiport.lanes < routines) rides
  317. // the same lane session.
  318. tx1b := newTx(1)
  319. ifce.sendInsideMessage(base, pkt, nb, tx1b)
  320. tx1b.flush(ifce.l)
  321. require.Len(t, laneWriter.bufs, 2)
  322. require.NoError(t, h.Parse(laneWriter.bufs[1]))
  323. assert.Equal(t, lane.remoteIndexId, h.RemoteIndex)
  324. // Slot 2 has no lane: base tunnel, base batch.
  325. ifce.sendInsideMessage(base, pkt, nb, tx2)
  326. tx2.flush(ifce.l)
  327. require.Len(t, baseWriter.bufs, 1)
  328. assert.Equal(t, base.GetRemote(), baseWriter.dsts[0])
  329. require.NoError(t, h.Parse(baseWriter.bufs[0]))
  330. assert.Equal(t, base.remoteIndexId, h.RemoteIndex)
  331. // Lane death: slot cleared, instant fallback to base.
  332. base.lanes.txLanes[1].Store(nil)
  333. ifce.sendInsideMessage(base, pkt, nb, tx1)
  334. tx1.flush(ifce.l)
  335. require.Len(t, baseWriter.bufs, 2)
  336. require.Len(t, laneWriter.bufs, 2)
  337. }
  338. func TestLaneSlotFor(t *testing.T) {
  339. // Overflow routines wrap onto the configured lanes round-robin.
  340. f := &Interface{multiport: true, laneCount: 2}
  341. for i, want := range []int{0, 1, 0, 1, 0, 1} {
  342. assert.Equal(t, want, f.laneSlotFor(i), "routine %d", i)
  343. }
  344. // Full lane count: identity mapping, one lane per routine.
  345. f = &Interface{multiport: true, laneCount: 4}
  346. for i := range 4 {
  347. assert.Equal(t, i, f.laneSlotFor(i), "routine %d", i)
  348. }
  349. // Multiport off: identity, each routine keeps its own writer.
  350. f = &Interface{multiport: false, laneCount: 0}
  351. for i := range 4 {
  352. assert.Equal(t, i, f.laneSlotFor(i), "routine %d", i)
  353. }
  354. }
  355. func TestCompleteLaneResponder(t *testing.T) {
  356. hostMap := newHostMap(test.NewLogger())
  357. _, ifce := newLaneTestConnectionManager(hostMap)
  358. ifce.writers = []udp.Conn{&udp.NoopConn{}, &udp.NoopConn{}, &udp.NoopConn{}, &udp.NoopConn{}}
  359. ifce.messageMetrics = newMessageMetricsOnlyRecvError()
  360. hm := ifce.handshakeManager
  361. hm.config.laneCount = 4
  362. hm.config.lanePortCount = 4
  363. hm.config.laneBasePort = 4242
  364. // A real handshake supplies usable keys and a peer cert.
  365. _, respR := runTestHandshake(t)
  366. respR.PeerLaneIndex = 2
  367. respR.PeerPortCount = 4
  368. respR.PeerBasePort = 5353
  369. via := ViaSender{UdpAddr: netip.MustParseAddrPort("192.0.2.9:5355"), SockIdx: 2}
  370. packet := make([]byte, header.Len+8)
  371. copy(packet[header.Len:], []byte("stage0!!"))
  372. vpnAddrs := []netip.Addr{netip.MustParseAddr("172.1.1.9")}
  373. // No base tunnel: the lane handshake is dropped, nothing registered.
  374. hm.completeLaneResponder(via, packet, []byte("resp"), respR, vpnAddrs)
  375. assert.Nil(t, hostMap.QueryIndex(respR.LocalIndex))
  376. // With a live base the lane attaches to it.
  377. base := newTestBaseHostInfo(vpnAddrs[0], 1300, 1400, 4)
  378. base.ConnectionState = &ConnectionState{}
  379. hostMap.Lock()
  380. hostMap.unlockedAddHostInfo(base, ifce)
  381. hostMap.Unlock()
  382. hm.completeLaneResponder(via, packet, []byte("resp"), respR, vpnAddrs)
  383. lane := hostMap.QueryIndex(respR.LocalIndex)
  384. require.NotNil(t, lane)
  385. assert.True(t, lane.isLane())
  386. assert.False(t, lane.laneOwned)
  387. assert.Equal(t, uint16(2), lane.laneIndex)
  388. assert.Equal(t, 2, lane.sockIdx)
  389. assert.Equal(t, via.UdpAddr, lane.GetRemote())
  390. assert.Equal(t, base, hostMap.Hosts[vpnAddrs[0]], "lane must not displace the base as primary")
  391. base.lanes.Lock()
  392. assert.Len(t, base.lanes.peerLanes, 1)
  393. base.lanes.Unlock()
  394. // A byte-identical stage-0 retransmit resends the cached response and
  395. // must not register a second lane.
  396. hm.completeLaneResponder(via, packet, []byte("resp"), respR, vpnAddrs)
  397. base.lanes.Lock()
  398. assert.Len(t, base.lanes.peerLanes, 1)
  399. base.lanes.Unlock()
  400. // An out-of-range lane index is refused.
  401. respR2 := *respR
  402. respR2.PeerLaneIndex = 9
  403. respR2.LocalIndex = respR.LocalIndex + 1
  404. hm.completeLaneResponder(via, packet, []byte("resp"), &respR2, vpnAddrs)
  405. assert.Nil(t, hostMap.QueryIndex(respR2.LocalIndex))
  406. }
  407. func TestEnsureLanesBackoffOnStage0Failure(t *testing.T) {
  408. hostMap := newHostMap(test.NewLogger())
  409. _, ifce := newLaneTestConnectionManager(hostMap)
  410. // laneCount enables multiport in the manager; the dummy CertState has no
  411. // credential, so stage-0 construction must fail and release the slot with
  412. // backoff rather than leaving it claimed forever.
  413. hm := ifce.handshakeManager
  414. hm.config.laneCount = 4
  415. hm.config.lanePortCount = 4
  416. hm.config.laneBasePort = 4242
  417. vpnIp := netip.MustParseAddr("172.1.1.8")
  418. base := newTestBaseHostInfo(vpnIp, 1100, 1200, 4)
  419. hm.EnsureLanes(base)
  420. base.lanes.Lock()
  421. defer base.lanes.Unlock()
  422. for i := 1; i < 4; i++ {
  423. assert.False(t, base.lanes.txPending[i], "slot %d still pending", i)
  424. assert.Equal(t, uint8(1), base.lanes.txFails[i], "slot %d fails", i)
  425. assert.True(t, base.lanes.txRetryAt[i].After(time.Now()), "slot %d retryAt", i)
  426. }
  427. }