netmap_test.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. // Copyright (c) Tailscale Inc & contributors
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package netmap
  4. import (
  5. "encoding/hex"
  6. "net/netip"
  7. "reflect"
  8. "testing"
  9. "go4.org/mem"
  10. "tailscale.com/net/netaddr"
  11. "tailscale.com/tailcfg"
  12. "tailscale.com/tstest/typewalk"
  13. "tailscale.com/types/key"
  14. )
  15. func testNodeKey(b byte) (ret key.NodePublic) {
  16. var bs [key.NodePublicRawLen]byte
  17. for i := range bs {
  18. bs[i] = b
  19. }
  20. return key.NodePublicFromRaw32(mem.B(bs[:]))
  21. }
  22. func testDiscoKey(hexPrefix string) (ret key.DiscoPublic) {
  23. b, err := hex.DecodeString(hexPrefix)
  24. if err != nil {
  25. panic(err)
  26. }
  27. // this function is used with short hexes, so zero-extend the raw
  28. // value.
  29. var bs [32]byte
  30. copy(bs[:], b)
  31. return key.DiscoPublicFromRaw32(mem.B(bs[:]))
  32. }
  33. func nodeViews(v []*tailcfg.Node) []tailcfg.NodeView {
  34. nv := make([]tailcfg.NodeView, len(v))
  35. for i, n := range v {
  36. nv[i] = n.View()
  37. }
  38. return nv
  39. }
  40. func eps(s ...string) []netip.AddrPort {
  41. var eps []netip.AddrPort
  42. for _, ep := range s {
  43. eps = append(eps, netip.MustParseAddrPort(ep))
  44. }
  45. return eps
  46. }
  47. func TestNetworkMapConcise(t *testing.T) {
  48. for _, tt := range []struct {
  49. name string
  50. nm *NetworkMap
  51. want string
  52. }{
  53. {
  54. name: "basic",
  55. nm: &NetworkMap{
  56. NodeKey: testNodeKey(1),
  57. Peers: nodeViews([]*tailcfg.Node{
  58. {
  59. Key: testNodeKey(2),
  60. HomeDERP: 2,
  61. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  62. },
  63. {
  64. Key: testNodeKey(3),
  65. HomeDERP: 4,
  66. Endpoints: eps("10.2.0.100:12", "10.1.0.100:12345"),
  67. },
  68. }),
  69. },
  70. want: "netmap: self: [AQEBA] auth=machine-unknown u=? []\n [AgICA] D2 : 192.168.0.100:12 192.168.0.100:12354\n [AwMDA] D4 : 10.2.0.100:12 10.1.0.100:12345\n",
  71. },
  72. } {
  73. t.Run(tt.name, func(t *testing.T) {
  74. var got string
  75. n := int(testing.AllocsPerRun(1000, func() {
  76. got = tt.nm.Concise()
  77. }))
  78. t.Logf("Allocs = %d", n)
  79. if got != tt.want {
  80. t.Errorf("Wrong output\n Got: %q\nWant: %q\n## Got (unescaped):\n%s\n## Want (unescaped):\n%s\n", got, tt.want, got, tt.want)
  81. }
  82. })
  83. }
  84. }
  85. func TestConciseDiffFrom(t *testing.T) {
  86. for _, tt := range []struct {
  87. name string
  88. a, b *NetworkMap
  89. want string
  90. }{
  91. {
  92. name: "no_change",
  93. a: &NetworkMap{
  94. NodeKey: testNodeKey(1),
  95. Peers: nodeViews([]*tailcfg.Node{
  96. {
  97. Key: testNodeKey(2),
  98. HomeDERP: 2,
  99. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  100. },
  101. }),
  102. },
  103. b: &NetworkMap{
  104. NodeKey: testNodeKey(1),
  105. Peers: nodeViews([]*tailcfg.Node{
  106. {
  107. Key: testNodeKey(2),
  108. HomeDERP: 2,
  109. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  110. },
  111. }),
  112. },
  113. want: "",
  114. },
  115. {
  116. name: "header_change",
  117. a: &NetworkMap{
  118. NodeKey: testNodeKey(1),
  119. Peers: nodeViews([]*tailcfg.Node{
  120. {
  121. Key: testNodeKey(2),
  122. HomeDERP: 2,
  123. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  124. },
  125. }),
  126. },
  127. b: &NetworkMap{
  128. NodeKey: testNodeKey(2),
  129. Peers: nodeViews([]*tailcfg.Node{
  130. {
  131. Key: testNodeKey(2),
  132. HomeDERP: 2,
  133. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  134. },
  135. }),
  136. },
  137. want: "-netmap: self: [AQEBA] auth=machine-unknown u=? []\n+netmap: self: [AgICA] auth=machine-unknown u=? []\n",
  138. },
  139. {
  140. name: "peer_add",
  141. a: &NetworkMap{
  142. NodeKey: testNodeKey(1),
  143. Peers: nodeViews([]*tailcfg.Node{
  144. {
  145. ID: 2,
  146. Key: testNodeKey(2),
  147. HomeDERP: 2,
  148. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  149. },
  150. }),
  151. },
  152. b: &NetworkMap{
  153. NodeKey: testNodeKey(1),
  154. Peers: nodeViews([]*tailcfg.Node{
  155. {
  156. ID: 1,
  157. Key: testNodeKey(1),
  158. HomeDERP: 1,
  159. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  160. },
  161. {
  162. ID: 2,
  163. Key: testNodeKey(2),
  164. HomeDERP: 2,
  165. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  166. },
  167. {
  168. ID: 3,
  169. Key: testNodeKey(3),
  170. HomeDERP: 3,
  171. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  172. },
  173. }),
  174. },
  175. want: "+ [AQEBA] D1 : 192.168.0.100:12 192.168.0.100:12354\n+ [AwMDA] D3 : 192.168.0.100:12 192.168.0.100:12354\n",
  176. },
  177. {
  178. name: "peer_remove",
  179. a: &NetworkMap{
  180. NodeKey: testNodeKey(1),
  181. Peers: nodeViews([]*tailcfg.Node{
  182. {
  183. ID: 1,
  184. Key: testNodeKey(1),
  185. HomeDERP: 1,
  186. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  187. },
  188. {
  189. ID: 2,
  190. Key: testNodeKey(2),
  191. HomeDERP: 2,
  192. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  193. },
  194. {
  195. ID: 3,
  196. Key: testNodeKey(3),
  197. HomeDERP: 3,
  198. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  199. },
  200. }),
  201. },
  202. b: &NetworkMap{
  203. NodeKey: testNodeKey(1),
  204. Peers: nodeViews([]*tailcfg.Node{
  205. {
  206. ID: 2,
  207. Key: testNodeKey(2),
  208. HomeDERP: 2,
  209. Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
  210. },
  211. }),
  212. },
  213. want: "- [AQEBA] D1 : 192.168.0.100:12 192.168.0.100:12354\n- [AwMDA] D3 : 192.168.0.100:12 192.168.0.100:12354\n",
  214. },
  215. {
  216. name: "peer_port_change",
  217. a: &NetworkMap{
  218. NodeKey: testNodeKey(1),
  219. Peers: nodeViews([]*tailcfg.Node{
  220. {
  221. ID: 2,
  222. Key: testNodeKey(2),
  223. HomeDERP: 2,
  224. Endpoints: eps("192.168.0.100:12", "1.1.1.1:1"),
  225. },
  226. }),
  227. },
  228. b: &NetworkMap{
  229. NodeKey: testNodeKey(1),
  230. Peers: nodeViews([]*tailcfg.Node{
  231. {
  232. ID: 2,
  233. Key: testNodeKey(2),
  234. HomeDERP: 2,
  235. Endpoints: eps("192.168.0.100:12", "1.1.1.1:2"),
  236. },
  237. }),
  238. },
  239. want: "- [AgICA] D2 : 192.168.0.100:12 1.1.1.1:1 \n+ [AgICA] D2 : 192.168.0.100:12 1.1.1.1:2 \n",
  240. },
  241. {
  242. name: "disco_key_only_change",
  243. a: &NetworkMap{
  244. NodeKey: testNodeKey(1),
  245. Peers: nodeViews([]*tailcfg.Node{
  246. {
  247. ID: 2,
  248. Key: testNodeKey(2),
  249. HomeDERP: 2,
  250. Endpoints: eps("192.168.0.100:41641", "1.1.1.1:41641"),
  251. DiscoKey: testDiscoKey("f00f00f00f"),
  252. AllowedIPs: []netip.Prefix{netip.PrefixFrom(netaddr.IPv4(100, 102, 103, 104), 32)},
  253. },
  254. }),
  255. },
  256. b: &NetworkMap{
  257. NodeKey: testNodeKey(1),
  258. Peers: nodeViews([]*tailcfg.Node{
  259. {
  260. ID: 2,
  261. Key: testNodeKey(2),
  262. HomeDERP: 2,
  263. Endpoints: eps("192.168.0.100:41641", "1.1.1.1:41641"),
  264. DiscoKey: testDiscoKey("ba4ba4ba4b"),
  265. AllowedIPs: []netip.Prefix{netip.PrefixFrom(netaddr.IPv4(100, 102, 103, 104), 32)},
  266. },
  267. }),
  268. },
  269. want: "- [AgICA] d:f00f00f00f000000 D2 100.102.103.104 : 192.168.0.100:41641 1.1.1.1:41641\n+ [AgICA] d:ba4ba4ba4b000000 D2 100.102.103.104 : 192.168.0.100:41641 1.1.1.1:41641\n",
  270. },
  271. } {
  272. t.Run(tt.name, func(t *testing.T) {
  273. var got string
  274. n := int(testing.AllocsPerRun(50, func() {
  275. got = tt.b.ConciseDiffFrom(tt.a)
  276. }))
  277. t.Logf("Allocs = %d", n)
  278. if got != tt.want {
  279. t.Errorf("Wrong output\n Got: %q\nWant: %q\n## Got (unescaped):\n%s\n## Want (unescaped):\n%s\n", got, tt.want, got, tt.want)
  280. }
  281. })
  282. }
  283. }
  284. func TestPeerIndexByNodeID(t *testing.T) {
  285. var nilPtr *NetworkMap
  286. if nilPtr.PeerIndexByNodeID(123) != -1 {
  287. t.Errorf("nil PeerIndexByNodeID should return -1")
  288. }
  289. var nm NetworkMap
  290. const min = 2
  291. const max = 10000
  292. const hole = max / 2
  293. for nid := tailcfg.NodeID(2); nid <= max; nid++ {
  294. if nid == hole {
  295. continue
  296. }
  297. nm.Peers = append(nm.Peers, (&tailcfg.Node{ID: nid}).View())
  298. }
  299. for want, nv := range nm.Peers {
  300. got := nm.PeerIndexByNodeID(nv.ID())
  301. if got != want {
  302. t.Errorf("PeerIndexByNodeID(%v) = %v; want %v", nv.ID(), got, want)
  303. }
  304. }
  305. for _, miss := range []tailcfg.NodeID{min - 1, hole, max + 1} {
  306. if got := nm.PeerIndexByNodeID(miss); got != -1 {
  307. t.Errorf("PeerIndexByNodeID(%v) = %v; want -1", miss, got)
  308. }
  309. }
  310. }
  311. func TestNoPrivateKeyMaterial(t *testing.T) {
  312. private := key.PrivateTypesForTest()
  313. for path := range typewalk.MatchingPaths(reflect.TypeFor[NetworkMap](), private.Contains) {
  314. t.Errorf("NetworkMap contains private key material at path: %q", path.Name)
  315. }
  316. }