lighthouse_test.go 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. package nebula
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "net/netip"
  8. "sync"
  9. "testing"
  10. "time"
  11. "github.com/gaissmai/bart"
  12. "github.com/slackhq/nebula/cert"
  13. "github.com/slackhq/nebula/config"
  14. "github.com/slackhq/nebula/header"
  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. "go.yaml.in/yaml/v3"
  20. )
  21. func TestOldIPv4Only(t *testing.T) {
  22. // This test ensures our new ipv6 enabled LH protobuf IpAndPorts works with the old style to enable backwards compatibility
  23. b := []byte{8, 129, 130, 132, 80, 16, 10}
  24. var m V4AddrPort
  25. err := m.Unmarshal(b)
  26. require.NoError(t, err)
  27. ip := netip.MustParseAddr("10.1.1.1")
  28. bp := ip.As4()
  29. assert.Equal(t, binary.BigEndian.Uint32(bp[:]), m.GetAddr())
  30. }
  31. func testCertState(networks ...netip.Prefix) *CertState {
  32. cs := &CertState{
  33. myVpnNetworks: networks,
  34. myVpnNetworksTable: new(bart.Lite),
  35. myVpnAddrs: make([]netip.Addr, 0, len(networks)),
  36. myVpnAddrsTable: new(bart.Lite),
  37. }
  38. for _, n := range networks {
  39. cs.myVpnNetworksTable.Insert(n)
  40. cs.myVpnAddrs = append(cs.myVpnAddrs, n.Addr())
  41. cs.myVpnAddrsTable.Insert(netip.PrefixFrom(n.Addr(), n.Addr().BitLen()))
  42. }
  43. return cs
  44. }
  45. func Test_lhStaticMapping(t *testing.T) {
  46. l := test.NewLogger()
  47. myVpnNet := netip.MustParsePrefix("10.128.0.1/16")
  48. cs := testCertState(myVpnNet)
  49. lh1 := "10.128.0.2"
  50. c := config.NewC(l)
  51. c.Settings["lighthouse"] = map[string]any{"hosts": []any{lh1}}
  52. c.Settings["static_host_map"] = map[string]any{lh1: []any{"1.1.1.1:4242"}}
  53. _, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  54. require.NoError(t, err)
  55. lh2 := "10.128.0.3"
  56. c = config.NewC(l)
  57. c.Settings["lighthouse"] = map[string]any{"hosts": []any{lh1, lh2}}
  58. c.Settings["static_host_map"] = map[string]any{lh1: []any{"100.1.1.1:4242"}}
  59. _, err = NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  60. require.EqualError(t, err, "lighthouse 10.128.0.3 does not have a static_host_map entry")
  61. }
  62. func TestReloadLighthouseInterval(t *testing.T) {
  63. l := test.NewLogger()
  64. myVpnNet := netip.MustParsePrefix("10.128.0.1/16")
  65. cs := testCertState(myVpnNet)
  66. lh1 := "10.128.0.2"
  67. c := config.NewC(l)
  68. c.Settings["lighthouse"] = map[string]any{
  69. "hosts": []any{lh1},
  70. "interval": "1s",
  71. }
  72. c.Settings["static_host_map"] = map[string]any{lh1: []any{"1.1.1.1:4242"}}
  73. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  74. require.NoError(t, err)
  75. lh.ifce = &mockEncWriter{}
  76. // The first one routine is kicked off by main.go currently, lets make sure that one dies
  77. require.NoError(t, c.ReloadConfigString("lighthouse:\n interval: 5"))
  78. assert.Equal(t, int64(5), lh.interval.Load())
  79. // Subsequent calls are killed off by the LightHouse.Reload function
  80. require.NoError(t, c.ReloadConfigString("lighthouse:\n interval: 10"))
  81. assert.Equal(t, int64(10), lh.interval.Load())
  82. // If this completes then nothing is stealing our reload routine
  83. require.NoError(t, c.ReloadConfigString("lighthouse:\n interval: 11"))
  84. assert.Equal(t, int64(11), lh.interval.Load())
  85. }
  86. func BenchmarkLighthouseHandleRequest(b *testing.B) {
  87. l := test.NewLogger()
  88. myVpnNet := netip.MustParsePrefix("10.128.0.1/0")
  89. cs := testCertState(myVpnNet)
  90. c := config.NewC(l)
  91. lh, err := NewLightHouseFromConfig(b.Context(), l, c, cs, nil, nil)
  92. require.NoError(b, err)
  93. hAddr := netip.MustParseAddrPort("4.5.6.7:12345")
  94. hAddr2 := netip.MustParseAddrPort("4.5.6.7:12346")
  95. vpnIp3 := netip.MustParseAddr("0.0.0.3")
  96. lh.addrMap[vpnIp3] = NewRemoteList([]netip.Addr{vpnIp3}, nil)
  97. lh.addrMap[vpnIp3].unlockedSetV4(
  98. vpnIp3,
  99. vpnIp3,
  100. []*V4AddrPort{
  101. netAddrToProtoV4AddrPort(hAddr.Addr(), hAddr.Port()),
  102. netAddrToProtoV4AddrPort(hAddr2.Addr(), hAddr2.Port()),
  103. },
  104. func(netip.Addr, *V4AddrPort) bool { return true },
  105. )
  106. rAddr := netip.MustParseAddrPort("1.2.2.3:12345")
  107. rAddr2 := netip.MustParseAddrPort("1.2.2.3:12346")
  108. vpnIp2 := netip.MustParseAddr("0.0.0.3")
  109. lh.addrMap[vpnIp2] = NewRemoteList([]netip.Addr{vpnIp2}, nil)
  110. lh.addrMap[vpnIp2].unlockedSetV4(
  111. vpnIp3,
  112. vpnIp3,
  113. []*V4AddrPort{
  114. netAddrToProtoV4AddrPort(rAddr.Addr(), rAddr.Port()),
  115. netAddrToProtoV4AddrPort(rAddr2.Addr(), rAddr2.Port()),
  116. },
  117. func(netip.Addr, *V4AddrPort) bool { return true },
  118. )
  119. mw := &mockEncWriter{}
  120. hi := []netip.Addr{vpnIp2}
  121. b.Run("notfound", func(b *testing.B) {
  122. lhh := lh.NewRequestHandler()
  123. req := &NebulaMeta{
  124. Type: NebulaMeta_HostQuery,
  125. Details: &NebulaMetaDetails{
  126. OldVpnAddr: 4,
  127. V4AddrPorts: nil,
  128. },
  129. }
  130. p, err := req.Marshal()
  131. require.NoError(b, err)
  132. for n := 0; n < b.N; n++ {
  133. lhh.HandleRequest(rAddr, hi, p, mw)
  134. }
  135. })
  136. b.Run("found", func(b *testing.B) {
  137. lhh := lh.NewRequestHandler()
  138. req := &NebulaMeta{
  139. Type: NebulaMeta_HostQuery,
  140. Details: &NebulaMetaDetails{
  141. OldVpnAddr: 3,
  142. V4AddrPorts: nil,
  143. },
  144. }
  145. p, err := req.Marshal()
  146. require.NoError(b, err)
  147. for n := 0; n < b.N; n++ {
  148. lhh.HandleRequest(rAddr, hi, p, mw)
  149. }
  150. })
  151. }
  152. func TestLighthouse_Memory(t *testing.T) {
  153. l := test.NewLogger()
  154. myUdpAddr0 := netip.MustParseAddrPort("10.0.0.2:4242")
  155. myUdpAddr1 := netip.MustParseAddrPort("192.168.0.2:4242")
  156. myUdpAddr2 := netip.MustParseAddrPort("172.16.0.2:4242")
  157. myUdpAddr3 := netip.MustParseAddrPort("100.152.0.2:4242")
  158. myUdpAddr4 := netip.MustParseAddrPort("24.15.0.2:4242")
  159. myUdpAddr5 := netip.MustParseAddrPort("192.168.0.2:4243")
  160. myUdpAddr6 := netip.MustParseAddrPort("192.168.0.2:4244")
  161. myUdpAddr7 := netip.MustParseAddrPort("192.168.0.2:4245")
  162. myUdpAddr8 := netip.MustParseAddrPort("192.168.0.2:4246")
  163. myUdpAddr9 := netip.MustParseAddrPort("192.168.0.2:4247")
  164. myUdpAddr10 := netip.MustParseAddrPort("192.168.0.2:4248")
  165. myUdpAddr11 := netip.MustParseAddrPort("192.168.0.2:4249")
  166. myVpnIp := netip.MustParseAddr("10.128.0.2")
  167. theirUdpAddr0 := netip.MustParseAddrPort("10.0.0.3:4242")
  168. theirUdpAddr1 := netip.MustParseAddrPort("192.168.0.3:4242")
  169. theirUdpAddr2 := netip.MustParseAddrPort("172.16.0.3:4242")
  170. theirUdpAddr3 := netip.MustParseAddrPort("100.152.0.3:4242")
  171. theirUdpAddr4 := netip.MustParseAddrPort("24.15.0.3:4242")
  172. theirVpnIp := netip.MustParseAddr("10.128.0.3")
  173. c := config.NewC(l)
  174. c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
  175. c.Settings["listen"] = map[string]any{"port": 4242}
  176. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  177. cs := testCertState(myVpnNet)
  178. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  179. lh.ifce = &mockEncWriter{}
  180. require.NoError(t, err)
  181. lhh := lh.NewRequestHandler()
  182. // Test that my first update responds with just that
  183. newLHHostUpdate(myUdpAddr0, myVpnIp, []netip.AddrPort{myUdpAddr1, myUdpAddr2}, lhh)
  184. r := newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  185. assertIp4InArray(t, r.msg.Details.V4AddrPorts, myUdpAddr1, myUdpAddr2)
  186. // Ensure we don't accumulate addresses
  187. newLHHostUpdate(myUdpAddr0, myVpnIp, []netip.AddrPort{myUdpAddr3}, lhh)
  188. r = newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  189. assertIp4InArray(t, r.msg.Details.V4AddrPorts, myUdpAddr3)
  190. // Grow it back to 2
  191. newLHHostUpdate(myUdpAddr0, myVpnIp, []netip.AddrPort{myUdpAddr1, myUdpAddr4}, lhh)
  192. r = newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  193. assertIp4InArray(t, r.msg.Details.V4AddrPorts, myUdpAddr1, myUdpAddr4)
  194. // Update a different host and ask about it
  195. newLHHostUpdate(theirUdpAddr0, theirVpnIp, []netip.AddrPort{theirUdpAddr1, theirUdpAddr2, theirUdpAddr3, theirUdpAddr4}, lhh)
  196. r = newLHHostRequest(theirUdpAddr0, theirVpnIp, theirVpnIp, lhh)
  197. assertIp4InArray(t, r.msg.Details.V4AddrPorts, theirUdpAddr1, theirUdpAddr2, theirUdpAddr3, theirUdpAddr4)
  198. // Have both hosts ask about the other
  199. r = newLHHostRequest(theirUdpAddr0, theirVpnIp, myVpnIp, lhh)
  200. assertIp4InArray(t, r.msg.Details.V4AddrPorts, myUdpAddr1, myUdpAddr4)
  201. r = newLHHostRequest(myUdpAddr0, myVpnIp, theirVpnIp, lhh)
  202. assertIp4InArray(t, r.msg.Details.V4AddrPorts, theirUdpAddr1, theirUdpAddr2, theirUdpAddr3, theirUdpAddr4)
  203. // Make sure we didn't get changed
  204. r = newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  205. assertIp4InArray(t, r.msg.Details.V4AddrPorts, myUdpAddr1, myUdpAddr4)
  206. // Ensure proper ordering and limiting
  207. // Send 12 addrs, get 10 back, the last 2 removed, allowing the duplicate to remain (clients dedupe)
  208. newLHHostUpdate(
  209. myUdpAddr0,
  210. myVpnIp,
  211. []netip.AddrPort{
  212. myUdpAddr1,
  213. myUdpAddr2,
  214. myUdpAddr3,
  215. myUdpAddr4,
  216. myUdpAddr5,
  217. myUdpAddr5, //Duplicated on purpose
  218. myUdpAddr6,
  219. myUdpAddr7,
  220. myUdpAddr8,
  221. myUdpAddr9,
  222. myUdpAddr10,
  223. myUdpAddr11, // This should get cut
  224. }, lhh)
  225. r = newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  226. assertIp4InArray(
  227. t,
  228. r.msg.Details.V4AddrPorts,
  229. myUdpAddr1, myUdpAddr2, myUdpAddr3, myUdpAddr4, myUdpAddr5, myUdpAddr5, myUdpAddr6, myUdpAddr7, myUdpAddr8, myUdpAddr9,
  230. )
  231. // Make sure we won't add ips in our vpn network
  232. bad1 := netip.MustParseAddrPort("10.128.0.99:4242")
  233. bad2 := netip.MustParseAddrPort("10.128.0.100:4242")
  234. good := netip.MustParseAddrPort("1.128.0.99:4242")
  235. newLHHostUpdate(myUdpAddr0, myVpnIp, []netip.AddrPort{bad1, bad2, good}, lhh)
  236. r = newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  237. assertIp4InArray(t, r.msg.Details.V4AddrPorts, good)
  238. }
  239. func TestLighthouse_reload(t *testing.T) {
  240. l := test.NewLogger()
  241. c := config.NewC(l)
  242. c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
  243. c.Settings["listen"] = map[string]any{"port": 4242}
  244. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  245. cs := testCertState(myVpnNet)
  246. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  247. require.NoError(t, err)
  248. nc := map[string]any{
  249. "static_host_map": map[string]any{
  250. "10.128.0.2": []any{"1.1.1.1:4242"},
  251. },
  252. }
  253. rc, err := yaml.Marshal(nc)
  254. require.NoError(t, err)
  255. c.ReloadConfigString(string(rc))
  256. err = lh.reload(c, false)
  257. require.NoError(t, err)
  258. }
  259. // TestLighthouse_reloadStaticHostMap verifies that reloading static_host_map applies the new
  260. // config rather than appending to it. See issue #718.
  261. func TestLighthouse_reloadStaticHostMap(t *testing.T) {
  262. l := test.NewLogger()
  263. c := config.NewC(l)
  264. c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
  265. c.Settings["listen"] = map[string]any{"port": 4242}
  266. c.Settings["static_host_map"] = map[string]any{
  267. "10.128.0.2": []any{"1.1.1.1:4242"},
  268. }
  269. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  270. cs := testCertState(myVpnNet)
  271. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  272. require.NoError(t, err)
  273. staticHost := netip.MustParseAddr("10.128.0.2")
  274. otherHost := netip.MustParseAddr("10.128.0.3")
  275. // Capture the RemoteList pointer up front; an in-flight handshake would hold the same one
  276. // on hostinfo.remotes, so it must reflect every reload below.
  277. pinned := lh.Query(staticHost)
  278. require.NotNil(t, pinned)
  279. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("1.1.1.1:4242")}, pinned.CopyAddrs([]netip.Prefix{}))
  280. // Replace the remote address. The new address should be the only entry.
  281. nc := map[string]any{
  282. "static_host_map": map[string]any{
  283. "10.128.0.2": []any{"2.2.2.2:4242"},
  284. },
  285. }
  286. rc, err := yaml.Marshal(nc)
  287. require.NoError(t, err)
  288. require.NoError(t, c.ReloadConfigString(string(rc)))
  289. rl := lh.Query(staticHost)
  290. require.NotNil(t, rl)
  291. assert.Same(t, pinned, rl, "RemoteList pointer must stay stable so in-flight handshakes pick up the change")
  292. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("2.2.2.2:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  293. // Reload back to the original IP. Mirrors the round-trip in issue #718 step 6-8 where
  294. // the buggy reload produced [1.1.1.1, 2.2.2.2, 1.1.1.1] instead of [1.1.1.1].
  295. nc = map[string]any{
  296. "static_host_map": map[string]any{
  297. "10.128.0.2": []any{"1.1.1.1:4242"},
  298. },
  299. }
  300. rc, err = yaml.Marshal(nc)
  301. require.NoError(t, err)
  302. require.NoError(t, c.ReloadConfigString(string(rc)))
  303. rl = lh.Query(staticHost)
  304. require.NotNil(t, rl)
  305. assert.Same(t, pinned, rl)
  306. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("1.1.1.1:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  307. // Reload with the same config. An unchanged entry must not duplicate.
  308. require.NoError(t, c.ReloadConfigString(string(rc)))
  309. rl = lh.Query(staticHost)
  310. require.NotNil(t, rl)
  311. assert.Same(t, pinned, rl)
  312. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("1.1.1.1:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  313. // Switch back to 2.2.2.2 so the rest of the test continues against a known address.
  314. nc = map[string]any{
  315. "static_host_map": map[string]any{
  316. "10.128.0.2": []any{"2.2.2.2:4242"},
  317. },
  318. }
  319. rc, err = yaml.Marshal(nc)
  320. require.NoError(t, err)
  321. require.NoError(t, c.ReloadConfigString(string(rc)))
  322. // Add a second host alongside the first. Both should be present, neither duplicated.
  323. nc = map[string]any{
  324. "static_host_map": map[string]any{
  325. "10.128.0.2": []any{"2.2.2.2:4242"},
  326. "10.128.0.3": []any{"3.3.3.3:4242"},
  327. },
  328. }
  329. rc, err = yaml.Marshal(nc)
  330. require.NoError(t, err)
  331. require.NoError(t, c.ReloadConfigString(string(rc)))
  332. rl = lh.Query(staticHost)
  333. require.NotNil(t, rl)
  334. assert.Same(t, pinned, rl, "adding a sibling entry must not displace the existing RemoteList")
  335. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("2.2.2.2:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  336. rl = lh.Query(otherHost)
  337. require.NotNil(t, rl)
  338. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("3.3.3.3:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  339. // Drop the first host entirely. The vpnAddr is no longer marked static, our owner
  340. // contribution is cleared, but the addrMap entry stays in place so non-static cache
  341. // data (from lighthouse queries) on the same RemoteList isn't lost. In-flight handshakes
  342. // that already had the pointer see an empty address list rather than retrying stale ones.
  343. nc = map[string]any{
  344. "static_host_map": map[string]any{
  345. "10.128.0.3": []any{"3.3.3.3:4242"},
  346. },
  347. }
  348. rc, err = yaml.Marshal(nc)
  349. require.NoError(t, err)
  350. require.NoError(t, c.ReloadConfigString(string(rc)))
  351. _, isStatic := lh.GetStaticHostList()[staticHost]
  352. assert.False(t, isStatic)
  353. rl = lh.Query(staticHost)
  354. require.NotNil(t, rl)
  355. assert.Same(t, pinned, rl)
  356. assert.Empty(t, rl.CopyAddrs([]netip.Prefix{}))
  357. rl = lh.Query(otherHost)
  358. require.NotNil(t, rl)
  359. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("3.3.3.3:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  360. }
  361. // sendLHHostRequest delivers a HostQuery to lhh and hands back the writer that
  362. // captured what it emitted. Pass a nil filter to see every message.
  363. func sendLHHostRequest(fromAddr netip.AddrPort, myVpnIp, queryVpnIp netip.Addr, lhh *LightHouseHandler, filter *NebulaMeta_MessageType) *testEncWriter {
  364. req := &NebulaMeta{
  365. Type: NebulaMeta_HostQuery,
  366. Details: &NebulaMetaDetails{},
  367. }
  368. if queryVpnIp.Is4() {
  369. bip := queryVpnIp.As4()
  370. req.Details.OldVpnAddr = binary.BigEndian.Uint32(bip[:])
  371. } else {
  372. req.Details.VpnAddr = netAddrToProtoAddr(queryVpnIp)
  373. }
  374. b, err := req.Marshal()
  375. if err != nil {
  376. panic(err)
  377. }
  378. w := &testEncWriter{metaFilter: filter}
  379. lhh.HandleRequest(fromAddr, []netip.Addr{myVpnIp}, b, w)
  380. return w
  381. }
  382. func newLHHostRequest(fromAddr netip.AddrPort, myVpnIp, queryVpnIp netip.Addr, lhh *LightHouseHandler) testLhReply {
  383. filter := NebulaMeta_HostQueryReply
  384. return sendLHHostRequest(fromAddr, myVpnIp, queryVpnIp, lhh, &filter).lastReply
  385. }
  386. func TestLighthouse_IgnoresHostQueryForItself(t *testing.T) {
  387. // Validate that we don't answer host queries for our own address.
  388. l := test.NewLogger()
  389. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  390. myVpnIp := myVpnNet.Addr()
  391. c := config.NewC(l)
  392. c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
  393. c.Settings["listen"] = map[string]any{"port": 4242}
  394. // Add a static_host_map entry for ourselves, so our address
  395. // is in the addrMap.
  396. c.Settings["static_host_map"] = map[string]any{
  397. myVpnIp.String(): []any{"192.168.100.1:4242"},
  398. }
  399. lh, err := NewLightHouseFromConfig(t.Context(), l, c, testCertState(myVpnNet), nil, nil)
  400. require.NoError(t, err)
  401. lh.ifce = &mockEncWriter{}
  402. lhh := lh.NewRequestHandler()
  403. peerVpnIp := netip.MustParseAddr("10.128.0.2")
  404. peerUdpAddr := netip.MustParseAddrPort("10.0.0.2:4242")
  405. otherVpnIp := netip.MustParseAddr("10.128.0.3")
  406. otherUdpAddr := netip.MustParseAddrPort("10.0.0.3:4242")
  407. newLHHostUpdate(peerUdpAddr, peerVpnIp, []netip.AddrPort{peerUdpAddr}, lhh)
  408. newLHHostUpdate(otherUdpAddr, otherVpnIp, []netip.AddrPort{otherUdpAddr}, lhh)
  409. // Control: a query about a real peer is still answered, and still ends with
  410. // the punch notification aimed at the host that was asked about.
  411. w := sendLHHostRequest(peerUdpAddr, peerVpnIp, otherVpnIp, lhh, nil)
  412. require.NotNil(t, w.lastReply.msg)
  413. assert.Equal(t, NebulaMeta_HostPunchNotification, w.lastReply.msg.Type)
  414. assert.Equal(t, otherVpnIp, w.lastReply.vpnIp)
  415. // Now validate that we don't send to ourselves.
  416. found, _, err := lh.queryAndPrepMessage(myVpnIp, func(*cache) (int, error) { return 0, nil })
  417. require.NoError(t, err)
  418. require.True(t, found, "the lighthouse should hold a cache entry for its own address")
  419. w = sendLHHostRequest(peerUdpAddr, peerVpnIp, myVpnIp, lhh, nil)
  420. assert.Nil(t, w.lastReply.msg, "a query about our own address must produce no reply and no punch notification")
  421. }
  422. func newLHHostUpdate(fromAddr netip.AddrPort, vpnIp netip.Addr, addrs []netip.AddrPort, lhh *LightHouseHandler) {
  423. req := &NebulaMeta{
  424. Type: NebulaMeta_HostUpdateNotification,
  425. Details: &NebulaMetaDetails{},
  426. }
  427. if vpnIp.Is4() {
  428. bip := vpnIp.As4()
  429. req.Details.OldVpnAddr = binary.BigEndian.Uint32(bip[:])
  430. } else {
  431. req.Details.VpnAddr = netAddrToProtoAddr(vpnIp)
  432. }
  433. for _, v := range addrs {
  434. if v.Addr().Is4() {
  435. req.Details.V4AddrPorts = append(req.Details.V4AddrPorts, netAddrToProtoV4AddrPort(v.Addr(), v.Port()))
  436. } else {
  437. req.Details.V6AddrPorts = append(req.Details.V6AddrPorts, netAddrToProtoV6AddrPort(v.Addr(), v.Port()))
  438. }
  439. }
  440. b, err := req.Marshal()
  441. if err != nil {
  442. panic(err)
  443. }
  444. w := &testEncWriter{}
  445. lhh.HandleRequest(fromAddr, []netip.Addr{vpnIp}, b, w)
  446. }
  447. type testLhReply struct {
  448. nebType header.MessageType
  449. nebSubType header.MessageSubType
  450. vpnIp netip.Addr
  451. msg *NebulaMeta
  452. }
  453. type testEncWriter struct {
  454. lastReply testLhReply
  455. metaFilter *NebulaMeta_MessageType
  456. protocolVersion cert.Version
  457. }
  458. func (tw *testEncWriter) SendVia(via *HostInfo, relay *Relay, ad, nb, out []byte, nocopy bool, q int) {
  459. }
  460. func (tw *testEncWriter) Handshake(vpnIp netip.Addr) {
  461. }
  462. func (tw *testEncWriter) SendMessageToHostInfo(t header.MessageType, st header.MessageSubType, hostinfo *HostInfo, p, _, _ []byte) {
  463. msg := &NebulaMeta{}
  464. err := msg.Unmarshal(p)
  465. if tw.metaFilter == nil || msg.Type == *tw.metaFilter {
  466. tw.lastReply = testLhReply{
  467. nebType: t,
  468. nebSubType: st,
  469. vpnIp: hostinfo.vpnAddrs[0],
  470. msg: msg,
  471. }
  472. }
  473. if err != nil {
  474. panic(err)
  475. }
  476. }
  477. func (tw *testEncWriter) SendMessageToVpnAddr(t header.MessageType, st header.MessageSubType, vpnIp netip.Addr, p, _, _ []byte) {
  478. msg := &NebulaMeta{}
  479. err := msg.Unmarshal(p)
  480. if tw.metaFilter == nil || msg.Type == *tw.metaFilter {
  481. tw.lastReply = testLhReply{
  482. nebType: t,
  483. nebSubType: st,
  484. vpnIp: vpnIp,
  485. msg: msg,
  486. }
  487. }
  488. if err != nil {
  489. panic(err)
  490. }
  491. }
  492. func (tw *testEncWriter) GetHostInfo(vpnIp netip.Addr) *HostInfo {
  493. return nil
  494. }
  495. func (tw *testEncWriter) GetCertState() *CertState {
  496. return &CertState{initiatingVersion: tw.protocolVersion}
  497. }
  498. // assertIp4InArray asserts every address in want is at the same position in have and that the lengths match
  499. func assertIp4InArray(t *testing.T, have []*V4AddrPort, want ...netip.AddrPort) {
  500. if !assert.Len(t, have, len(want)) {
  501. return
  502. }
  503. for k, w := range want {
  504. h := protoV4AddrPortToNetAddrPort(have[k])
  505. if !(h == w) {
  506. assert.Fail(t, fmt.Sprintf("Response did not contain: %v at %v, found %v", w, k, h))
  507. }
  508. }
  509. }
  510. func Test_findNetworkUnion(t *testing.T) {
  511. var out netip.Addr
  512. var ok bool
  513. tenDot := netip.MustParsePrefix("10.0.0.0/8")
  514. oneSevenTwo := netip.MustParsePrefix("172.16.0.0/16")
  515. fe80 := netip.MustParsePrefix("fe80::/8")
  516. fc00 := netip.MustParsePrefix("fc00::/7")
  517. a1 := netip.MustParseAddr("10.0.0.1")
  518. afe81 := netip.MustParseAddr("fe80::1")
  519. //simple
  520. out, ok = findNetworkUnion([]netip.Prefix{tenDot}, []netip.Addr{a1})
  521. assert.True(t, ok)
  522. assert.Equal(t, out, a1)
  523. //mixed lengths
  524. out, ok = findNetworkUnion([]netip.Prefix{tenDot}, []netip.Addr{a1, afe81})
  525. assert.True(t, ok)
  526. assert.Equal(t, out, a1)
  527. out, ok = findNetworkUnion([]netip.Prefix{tenDot, oneSevenTwo}, []netip.Addr{a1})
  528. assert.True(t, ok)
  529. assert.Equal(t, out, a1)
  530. //mixed family
  531. out, ok = findNetworkUnion([]netip.Prefix{tenDot, oneSevenTwo, fe80}, []netip.Addr{a1})
  532. assert.True(t, ok)
  533. assert.Equal(t, out, a1)
  534. out, ok = findNetworkUnion([]netip.Prefix{tenDot, oneSevenTwo, fe80}, []netip.Addr{a1, afe81})
  535. assert.True(t, ok)
  536. assert.Equal(t, out, a1)
  537. //ordering
  538. out, ok = findNetworkUnion([]netip.Prefix{tenDot, oneSevenTwo, fe80}, []netip.Addr{afe81, a1})
  539. assert.True(t, ok)
  540. assert.Equal(t, out, a1)
  541. out, ok = findNetworkUnion([]netip.Prefix{fe80, tenDot, oneSevenTwo}, []netip.Addr{afe81, a1})
  542. assert.True(t, ok)
  543. assert.Equal(t, out, afe81)
  544. //some mismatches
  545. out, ok = findNetworkUnion([]netip.Prefix{tenDot, oneSevenTwo, fe80}, []netip.Addr{afe81})
  546. assert.True(t, ok)
  547. assert.Equal(t, out, afe81)
  548. out, ok = findNetworkUnion([]netip.Prefix{oneSevenTwo, fe80}, []netip.Addr{a1, afe81})
  549. assert.True(t, ok)
  550. assert.Equal(t, out, afe81)
  551. //falsey cases
  552. out, ok = findNetworkUnion([]netip.Prefix{oneSevenTwo, fe80}, []netip.Addr{a1})
  553. assert.False(t, ok)
  554. out, ok = findNetworkUnion([]netip.Prefix{fc00, fe80}, []netip.Addr{a1})
  555. assert.False(t, ok)
  556. out, ok = findNetworkUnion([]netip.Prefix{oneSevenTwo, fc00}, []netip.Addr{a1, afe81})
  557. assert.False(t, ok)
  558. out, ok = findNetworkUnion([]netip.Prefix{fc00}, []netip.Addr{a1, afe81})
  559. assert.False(t, ok)
  560. }
  561. func TestLighthouse_Dont_Delete_Static_Hosts(t *testing.T) {
  562. l := test.NewLogger()
  563. myUdpAddr2 := netip.MustParseAddrPort("1.2.3.4:4242")
  564. testSameHostNotStatic := netip.MustParseAddr("10.128.0.41")
  565. testStaticHost := netip.MustParseAddr("10.128.0.42")
  566. //myVpnIp := netip.MustParseAddr("10.128.0.2")
  567. c := config.NewC(l)
  568. lh1 := "10.128.0.2"
  569. c.Settings["lighthouse"] = map[string]any{
  570. "hosts": []any{lh1},
  571. "interval": "1s",
  572. }
  573. c.Settings["listen"] = map[string]any{"port": 4242}
  574. c.Settings["static_host_map"] = map[string]any{
  575. lh1: []any{"1.1.1.1:4242"},
  576. "10.128.0.42": []any{"1.2.3.4:4242"},
  577. }
  578. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  579. cs := testCertState(myVpnNet)
  580. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  581. require.NoError(t, err)
  582. lh.ifce = &mockEncWriter{}
  583. //test that we actually have the static entry:
  584. out := lh.Query(testStaticHost)
  585. assert.NotNil(t, out)
  586. assert.Equal(t, out.vpnAddrs[0], testStaticHost)
  587. out.Rebuild([]netip.Prefix{}) //why tho
  588. assert.Equal(t, out.addrs[0], myUdpAddr2)
  589. //bolt on a lower numbered primary IP
  590. am := lh.unlockedGetRemoteList([]netip.Addr{testStaticHost})
  591. am.vpnAddrs = []netip.Addr{testSameHostNotStatic, testStaticHost}
  592. lh.addrMap[testSameHostNotStatic] = am
  593. out.Rebuild([]netip.Prefix{}) //???
  594. //test that we actually have the static entry:
  595. out = lh.Query(testStaticHost)
  596. assert.NotNil(t, out)
  597. assert.Equal(t, out.vpnAddrs[0], testSameHostNotStatic)
  598. assert.Equal(t, out.vpnAddrs[1], testStaticHost)
  599. assert.Equal(t, out.addrs[0], myUdpAddr2)
  600. //test that we actually have the static entry for BOTH:
  601. out2 := lh.Query(testSameHostNotStatic)
  602. assert.Same(t, out2, out)
  603. //now do the delete
  604. lh.DeleteVpnAddrs([]netip.Addr{testSameHostNotStatic, testStaticHost})
  605. //verify
  606. out = lh.Query(testSameHostNotStatic)
  607. assert.NotNil(t, out)
  608. if out == nil {
  609. t.Fatal("expected non-nil query for the static host")
  610. }
  611. assert.Equal(t, out.vpnAddrs[0], testSameHostNotStatic)
  612. assert.Equal(t, out.vpnAddrs[1], testStaticHost)
  613. assert.Equal(t, out.addrs[0], myUdpAddr2)
  614. }
  615. func TestLighthouse_DeletesWork(t *testing.T) {
  616. l := test.NewLogger()
  617. myUdpAddr2 := netip.MustParseAddrPort("1.2.3.4:4242")
  618. testHost := netip.MustParseAddr("10.128.0.42")
  619. c := config.NewC(l)
  620. lh1 := "10.128.0.2"
  621. c.Settings["lighthouse"] = map[string]any{
  622. "hosts": []any{lh1},
  623. "interval": "1s",
  624. }
  625. c.Settings["listen"] = map[string]any{"port": 4242}
  626. c.Settings["static_host_map"] = map[string]any{
  627. lh1: []any{"1.1.1.1:4242"},
  628. }
  629. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  630. cs := testCertState(myVpnNet)
  631. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  632. require.NoError(t, err)
  633. lh.ifce = &mockEncWriter{}
  634. //insert the host
  635. am := lh.unlockedGetRemoteList([]netip.Addr{testHost})
  636. am.vpnAddrs = []netip.Addr{testHost}
  637. am.addrs = []netip.AddrPort{myUdpAddr2}
  638. lh.addrMap[testHost] = am
  639. am.Rebuild([]netip.Prefix{}) //???
  640. //test that we actually have the entry:
  641. out := lh.Query(testHost)
  642. assert.NotNil(t, out)
  643. assert.Equal(t, out.vpnAddrs[0], testHost)
  644. out.Rebuild([]netip.Prefix{}) //why tho
  645. assert.Equal(t, out.addrs[0], myUdpAddr2)
  646. //now do the delete
  647. lh.DeleteVpnAddrs([]netip.Addr{testHost})
  648. //verify
  649. out = lh.Query(testHost)
  650. assert.Nil(t, out)
  651. }
  652. func TestLightHouse_logLocalAddrsErr(t *testing.T) {
  653. out := &bytes.Buffer{}
  654. lh := &LightHouse{l: test.NewLoggerWithOutput(out)}
  655. // The first sighting of a failure warns.
  656. lh.logLocalAddrsErr(errors.New("permission denied"))
  657. assert.Contains(t, out.String(), "level=WARN")
  658. assert.Contains(t, out.String(), "permission denied")
  659. // Repeating unchanged does not warn again, which is what keeps a permanent failure from warning
  660. // on every lighthouse.interval for the life of the process.
  661. out.Reset()
  662. lh.logLocalAddrsErr(errors.New("permission denied"))
  663. assert.NotContains(t, out.String(), "level=WARN")
  664. // A different failure is a new event and warns.
  665. out.Reset()
  666. lh.logLocalAddrsErr(errors.New("something else"))
  667. assert.Contains(t, out.String(), "level=WARN")
  668. assert.Contains(t, out.String(), "something else")
  669. // Recovering resets, so the same failure returning later warns again.
  670. out.Reset()
  671. lh.logLocalAddrsErr(nil)
  672. assert.Empty(t, out.String())
  673. lh.logLocalAddrsErr(errors.New("something else"))
  674. assert.Contains(t, out.String(), "level=WARN")
  675. }
  676. // heldEncWriter holds the query worker in its first GetHostInfo until release closes.
  677. type heldEncWriter struct {
  678. mockEncWriter
  679. once sync.Once
  680. held chan struct{}
  681. release chan struct{}
  682. }
  683. func (w *heldEncWriter) GetHostInfo(netip.Addr) *HostInfo {
  684. w.once.Do(func() { close(w.held) })
  685. <-w.release
  686. return nil
  687. }
  688. // QueryServer never waits on the query worker, since the packet readers call it and nothing drains the channel at
  689. // shutdown; a query that finds the channel full is dropped and counted. The worker is held on its first query so the
  690. // channel fills.
  691. func TestLighthouse_QueryServerDoesNotBlock(t *testing.T) {
  692. l := test.NewLogger()
  693. cs := testCertState(netip.MustParsePrefix("10.128.0.1/16"))
  694. lh1 := "10.128.0.2"
  695. c := config.NewC(l)
  696. c.Settings["lighthouse"] = map[string]any{"hosts": []any{lh1}}
  697. c.Settings["static_host_map"] = map[string]any{lh1: []any{"1.1.1.1:4242"}}
  698. c.Settings["handshakes"] = map[string]any{"query_buffer": 1}
  699. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  700. require.NoError(t, err)
  701. w := &heldEncWriter{held: make(chan struct{}), release: make(chan struct{})}
  702. lh.ifce = w
  703. t.Cleanup(func() { close(w.release) })
  704. lh.QueryServer(netip.MustParseAddr("10.128.0.10"))
  705. select {
  706. case <-w.held:
  707. case <-time.After(5 * time.Second):
  708. t.Fatal("the query worker never took the first query")
  709. }
  710. dropped := lh.queryDropped.Count()
  711. done := make(chan struct{})
  712. go func() {
  713. lh.QueryServer(netip.MustParseAddr("10.128.0.11"))
  714. lh.QueryServer(netip.MustParseAddr("10.128.0.12"))
  715. close(done)
  716. }()
  717. select {
  718. case <-done:
  719. case <-time.After(5 * time.Second):
  720. t.Fatal("QueryServer blocked on a full query channel")
  721. }
  722. assert.EqualValues(t, 1, lh.queryDropped.Count()-dropped)
  723. }
  724. // hostmapEncWriter answers GetHostInfo from a real hostmap, the way Interface does.
  725. type hostmapEncWriter struct {
  726. mockEncWriter
  727. hm *HostMap
  728. }
  729. func (w *hostmapEncWriter) GetHostInfo(a netip.Addr) *HostInfo { return w.hm.QueryVpnAddr(a) }
  730. func (w *hostmapEncWriter) GetCertState() *CertState {
  731. return &CertState{initiatingVersion: cert.Version1}
  732. }
  733. // A lighthouse runs four paths that each hold one lock while waiting for the next: CheckAndComplete
  734. // holds the hostmap and takes the handshake manager, StartHandshake holds the handshake manager and
  735. // takes the lighthouse, a host update holds the lighthouse and takes the sender's RemoteList, and a
  736. // punch notification holds the querier's RemoteList while it resolves the target. If that last step
  737. // reaches the hostmap under the RemoteList lock, the four deadlock. Drive them all at once.
  738. func TestLighthouse_PunchNotificationDoesNotCloseLockCycle(t *testing.T) {
  739. l := test.NewLogger()
  740. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  741. c := config.NewC(l)
  742. c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
  743. c.Settings["listen"] = map[string]any{"port": 4242}
  744. lh, err := NewLightHouseFromConfig(t.Context(), l, c, testCertState(myVpnNet), nil, nil)
  745. require.NoError(t, err)
  746. mainHM := newHostMap(l)
  747. pr := []netip.Prefix{}
  748. mainHM.preferredRanges.Store(&pr)
  749. lh.ifce = &hostmapEncWriter{hm: mainHM}
  750. hm := NewHandshakeManager(l, mainHM, lh, &udp.NoopConn{}, defaultHandshakeConfig)
  751. f := &Interface{hostMap: mainHM, lightHouse: lh, handshakeManager: hm, l: l}
  752. hm.f = f
  753. peer := netip.MustParseAddr("10.128.0.2")
  754. peerUdp := netip.MustParseAddrPort("10.0.0.2:4242")
  755. target := netip.MustParseAddr("10.128.0.3")
  756. targetUdp := netip.MustParseAddrPort("10.0.0.3:4242")
  757. outbound := netip.MustParseAddr("10.128.0.4")
  758. inbound := netip.MustParseAddr("10.128.0.5")
  759. seed := lh.NewRequestHandler()
  760. newLHHostUpdate(peerUdp, peer, []netip.AddrPort{peerUdp}, seed)
  761. newLHHostUpdate(targetUdp, target, []netip.AddrPort{targetUdp}, seed)
  762. stop := make(chan struct{})
  763. var wg sync.WaitGroup
  764. loop := func(body func()) {
  765. defer wg.Done()
  766. for {
  767. select {
  768. case <-stop:
  769. return
  770. default:
  771. body()
  772. }
  773. }
  774. }
  775. wg.Add(4)
  776. // Outbound handshakes keep starting and being dropped.
  777. go loop(func() {
  778. hm.DeleteHostInfo(hm.StartHandshake(outbound, nil))
  779. })
  780. // Inbound handshakes keep completing and being torn down.
  781. var idx uint32
  782. go loop(func() {
  783. idx++
  784. hi := &HostInfo{
  785. vpnAddrs: []netip.Addr{inbound},
  786. localIndexId: idx,
  787. remoteIndexId: idx,
  788. remotes: lh.QueryCache([]netip.Addr{inbound}),
  789. HandshakePacket: map[uint8][]byte{handshakePacketStage0: {byte(idx)}},
  790. }
  791. if _, err := hm.CheckAndComplete(hi, handshakePacketStage0, f); err == nil {
  792. mainHM.DeleteHostInfo(hi)
  793. }
  794. })
  795. // The peer keeps sending host updates.
  796. updates := lh.NewRequestHandler()
  797. go loop(func() {
  798. newLHHostUpdate(peerUdp, peer, []netip.AddrPort{peerUdp}, updates)
  799. })
  800. // The same peer keeps querying for the target, which ends in a punch notification.
  801. queries := lh.NewRequestHandler()
  802. go loop(func() {
  803. sendLHHostRequest(peerUdp, peer, target, queries, nil)
  804. })
  805. time.Sleep(time.Second)
  806. close(stop)
  807. done := make(chan struct{})
  808. go func() { wg.Wait(); close(done) }()
  809. select {
  810. case <-done:
  811. case <-time.After(5 * time.Second):
  812. t.Fatal("deadlock: the lighthouse paths never finished after stop was signalled")
  813. }
  814. }
  815. func TestLighthouse_reloadRelaysTriggersUpdate(t *testing.T) {
  816. l := test.NewLogger()
  817. c := config.NewC(l)
  818. base := "lighthouse:\n am_lighthouse: true\nlisten:\n port: 4242\n"
  819. require.NoError(t, c.LoadString(base+"relay:\n relays: [10.128.0.5]\n"))
  820. lh, err := NewLightHouseFromConfig(t.Context(), l, c, testCertState(netip.MustParsePrefix("10.128.0.1/24")), nil, nil)
  821. require.NoError(t, err)
  822. assert.Empty(t, lh.updateTrigger)
  823. require.NoError(t, c.ReloadConfigString(base+"relay:\n relays: [10.128.0.6]\n"))
  824. assert.Equal(t, []netip.Addr{netip.MustParseAddr("10.128.0.6")}, lh.GetRelaysForMe())
  825. require.Len(t, lh.updateTrigger, 1)
  826. <-lh.updateTrigger
  827. // The config changed but what we advertise did not, a bad entry is skipped
  828. require.NoError(t, c.ReloadConfigString(base+"relay:\n relays: [10.128.0.6, nope]\n"))
  829. assert.Equal(t, []netip.Addr{netip.MustParseAddr("10.128.0.6")}, lh.GetRelaysForMe())
  830. assert.Empty(t, lh.updateTrigger)
  831. }
  832. func TestLighthouse_reloadAmRelayAlone(t *testing.T) {
  833. l := test.NewLogger()
  834. c := config.NewC(l)
  835. base := "lighthouse:\n am_lighthouse: true\nlisten:\n port: 4242\nrelay:\n relays: [10.128.0.5]\n"
  836. require.NoError(t, c.LoadString(base+" am_relay: false\n"))
  837. lh, err := NewLightHouseFromConfig(t.Context(), l, c, testCertState(netip.MustParsePrefix("10.128.0.1/24")), nil, nil)
  838. require.NoError(t, err)
  839. assert.Equal(t, []netip.Addr{netip.MustParseAddr("10.128.0.5")}, lh.GetRelaysForMe())
  840. // Relays aren't allowed to have relays, flipping am_relay alone must drop them and tell the lighthouses
  841. require.NoError(t, c.ReloadConfigString(base+" am_relay: true\n"))
  842. assert.Empty(t, lh.GetRelaysForMe())
  843. require.Len(t, lh.updateTrigger, 1)
  844. <-lh.updateTrigger
  845. require.NoError(t, c.ReloadConfigString(base+" am_relay: false\n"))
  846. assert.Equal(t, []netip.Addr{netip.MustParseAddr("10.128.0.5")}, lh.GetRelaysForMe())
  847. require.Len(t, lh.updateTrigger, 1)
  848. }