map_test.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. // Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package controlclient
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "reflect"
  9. "strings"
  10. "testing"
  11. "time"
  12. "tailscale.com/tailcfg"
  13. "tailscale.com/types/key"
  14. "tailscale.com/types/netmap"
  15. )
  16. func TestUndeltaPeers(t *testing.T) {
  17. defer func(old func() time.Time) { clockNow = old }(clockNow)
  18. var curTime time.Time
  19. clockNow = func() time.Time {
  20. return curTime
  21. }
  22. online := func(v bool) func(*tailcfg.Node) {
  23. return func(n *tailcfg.Node) {
  24. n.Online = &v
  25. }
  26. }
  27. seenAt := func(t time.Time) func(*tailcfg.Node) {
  28. return func(n *tailcfg.Node) {
  29. n.LastSeen = &t
  30. }
  31. }
  32. withDERP := func(d string) func(*tailcfg.Node) {
  33. return func(n *tailcfg.Node) {
  34. n.DERP = d
  35. }
  36. }
  37. withEP := func(ep string) func(*tailcfg.Node) {
  38. return func(n *tailcfg.Node) {
  39. n.Endpoints = []string{ep}
  40. }
  41. }
  42. n := func(id tailcfg.NodeID, name string, mod ...func(*tailcfg.Node)) *tailcfg.Node {
  43. n := &tailcfg.Node{ID: id, Name: name}
  44. for _, f := range mod {
  45. f(n)
  46. }
  47. return n
  48. }
  49. peers := func(nv ...*tailcfg.Node) []*tailcfg.Node { return nv }
  50. tests := []struct {
  51. name string
  52. mapRes *tailcfg.MapResponse
  53. curTime time.Time
  54. prev []*tailcfg.Node
  55. want []*tailcfg.Node
  56. }{
  57. {
  58. name: "full_peers",
  59. mapRes: &tailcfg.MapResponse{
  60. Peers: peers(n(1, "foo"), n(2, "bar")),
  61. },
  62. want: peers(n(1, "foo"), n(2, "bar")),
  63. },
  64. {
  65. name: "full_peers_ignores_deltas",
  66. mapRes: &tailcfg.MapResponse{
  67. Peers: peers(n(1, "foo"), n(2, "bar")),
  68. PeersRemoved: []tailcfg.NodeID{2},
  69. },
  70. want: peers(n(1, "foo"), n(2, "bar")),
  71. },
  72. {
  73. name: "add_and_update",
  74. prev: peers(n(1, "foo"), n(2, "bar")),
  75. mapRes: &tailcfg.MapResponse{
  76. PeersChanged: peers(n(0, "zero"), n(2, "bar2"), n(3, "three")),
  77. },
  78. want: peers(n(0, "zero"), n(1, "foo"), n(2, "bar2"), n(3, "three")),
  79. },
  80. {
  81. name: "remove",
  82. prev: peers(n(1, "foo"), n(2, "bar")),
  83. mapRes: &tailcfg.MapResponse{
  84. PeersRemoved: []tailcfg.NodeID{1},
  85. },
  86. want: peers(n(2, "bar")),
  87. },
  88. {
  89. name: "add_and_remove",
  90. prev: peers(n(1, "foo"), n(2, "bar")),
  91. mapRes: &tailcfg.MapResponse{
  92. PeersChanged: peers(n(1, "foo2")),
  93. PeersRemoved: []tailcfg.NodeID{2},
  94. },
  95. want: peers(n(1, "foo2")),
  96. },
  97. {
  98. name: "unchanged",
  99. prev: peers(n(1, "foo"), n(2, "bar")),
  100. mapRes: &tailcfg.MapResponse{},
  101. want: peers(n(1, "foo"), n(2, "bar")),
  102. },
  103. {
  104. name: "online_change",
  105. prev: peers(n(1, "foo"), n(2, "bar")),
  106. mapRes: &tailcfg.MapResponse{
  107. OnlineChange: map[tailcfg.NodeID]bool{
  108. 1: true,
  109. },
  110. },
  111. want: peers(
  112. n(1, "foo", online(true)),
  113. n(2, "bar"),
  114. ),
  115. },
  116. {
  117. name: "online_change_offline",
  118. prev: peers(n(1, "foo"), n(2, "bar")),
  119. mapRes: &tailcfg.MapResponse{
  120. OnlineChange: map[tailcfg.NodeID]bool{
  121. 1: false,
  122. 2: true,
  123. },
  124. },
  125. want: peers(
  126. n(1, "foo", online(false)),
  127. n(2, "bar", online(true)),
  128. ),
  129. },
  130. {
  131. name: "peer_seen_at",
  132. prev: peers(n(1, "foo", seenAt(time.Unix(111, 0))), n(2, "bar")),
  133. curTime: time.Unix(123, 0),
  134. mapRes: &tailcfg.MapResponse{
  135. PeerSeenChange: map[tailcfg.NodeID]bool{
  136. 1: false,
  137. 2: true,
  138. },
  139. },
  140. want: peers(
  141. n(1, "foo"),
  142. n(2, "bar", seenAt(time.Unix(123, 0))),
  143. ),
  144. },
  145. {
  146. name: "ep_change_derp",
  147. prev: peers(n(1, "foo", withDERP("127.3.3.40:3"))),
  148. mapRes: &tailcfg.MapResponse{
  149. PeersChangedPatch: []*tailcfg.PeerChange{{
  150. NodeID: 1,
  151. DERPRegion: 4,
  152. }},
  153. },
  154. want: peers(n(1, "foo", withDERP("127.3.3.40:4"))),
  155. },
  156. {
  157. name: "ep_change_udp",
  158. prev: peers(n(1, "foo", withEP("1.2.3.4:111"))),
  159. mapRes: &tailcfg.MapResponse{
  160. PeersChangedPatch: []*tailcfg.PeerChange{{
  161. NodeID: 1,
  162. Endpoints: []string{"1.2.3.4:56"},
  163. }},
  164. },
  165. want: peers(n(1, "foo", withEP("1.2.3.4:56"))),
  166. },
  167. {
  168. name: "ep_change_udp",
  169. prev: peers(n(1, "foo", withDERP("127.3.3.40:3"), withEP("1.2.3.4:111"))),
  170. mapRes: &tailcfg.MapResponse{
  171. PeersChangedPatch: []*tailcfg.PeerChange{{
  172. NodeID: 1,
  173. Endpoints: []string{"1.2.3.4:56"},
  174. }},
  175. },
  176. want: peers(n(1, "foo", withDERP("127.3.3.40:3"), withEP("1.2.3.4:56"))),
  177. },
  178. {
  179. name: "ep_change_both",
  180. prev: peers(n(1, "foo", withDERP("127.3.3.40:3"), withEP("1.2.3.4:111"))),
  181. mapRes: &tailcfg.MapResponse{
  182. PeersChangedPatch: []*tailcfg.PeerChange{{
  183. NodeID: 1,
  184. DERPRegion: 2,
  185. Endpoints: []string{"1.2.3.4:56"},
  186. }},
  187. },
  188. want: peers(n(1, "foo", withDERP("127.3.3.40:2"), withEP("1.2.3.4:56"))),
  189. },
  190. }
  191. for _, tt := range tests {
  192. t.Run(tt.name, func(t *testing.T) {
  193. if !tt.curTime.IsZero() {
  194. curTime = tt.curTime
  195. }
  196. undeltaPeers(tt.mapRes, tt.prev)
  197. if !reflect.DeepEqual(tt.mapRes.Peers, tt.want) {
  198. t.Errorf("wrong results\n got: %s\nwant: %s", formatNodes(tt.mapRes.Peers), formatNodes(tt.want))
  199. }
  200. })
  201. }
  202. }
  203. func formatNodes(nodes []*tailcfg.Node) string {
  204. var sb strings.Builder
  205. for i, n := range nodes {
  206. if i > 0 {
  207. sb.WriteString(", ")
  208. }
  209. var extra string
  210. if n.Online != nil {
  211. extra += fmt.Sprintf(", online=%v", *n.Online)
  212. }
  213. if n.LastSeen != nil {
  214. extra += fmt.Sprintf(", lastSeen=%v", n.LastSeen.Unix())
  215. }
  216. fmt.Fprintf(&sb, "(%d, %q%s)", n.ID, n.Name, extra)
  217. }
  218. return sb.String()
  219. }
  220. func newTestMapSession(t *testing.T) *mapSession {
  221. return newMapSession(key.NewNode())
  222. }
  223. func TestNetmapForResponse(t *testing.T) {
  224. t.Run("implicit_packetfilter", func(t *testing.T) {
  225. somePacketFilter := []tailcfg.FilterRule{
  226. {
  227. SrcIPs: []string{"*"},
  228. DstPorts: []tailcfg.NetPortRange{
  229. {IP: "10.2.3.4", Ports: tailcfg.PortRange{First: 22, Last: 22}},
  230. },
  231. },
  232. }
  233. ms := newTestMapSession(t)
  234. nm1 := ms.netmapForResponse(&tailcfg.MapResponse{
  235. Node: new(tailcfg.Node),
  236. PacketFilter: somePacketFilter,
  237. })
  238. if len(nm1.PacketFilter) == 0 {
  239. t.Fatalf("zero length PacketFilter")
  240. }
  241. nm2 := ms.netmapForResponse(&tailcfg.MapResponse{
  242. Node: new(tailcfg.Node),
  243. PacketFilter: nil, // testing that the server can omit this.
  244. })
  245. if len(nm1.PacketFilter) == 0 {
  246. t.Fatalf("zero length PacketFilter in 2nd netmap")
  247. }
  248. if !reflect.DeepEqual(nm1.PacketFilter, nm2.PacketFilter) {
  249. t.Error("packet filters differ")
  250. }
  251. })
  252. t.Run("implicit_dnsconfig", func(t *testing.T) {
  253. someDNSConfig := &tailcfg.DNSConfig{Domains: []string{"foo", "bar"}}
  254. ms := newTestMapSession(t)
  255. nm1 := ms.netmapForResponse(&tailcfg.MapResponse{
  256. Node: new(tailcfg.Node),
  257. DNSConfig: someDNSConfig,
  258. })
  259. if !reflect.DeepEqual(nm1.DNS, *someDNSConfig) {
  260. t.Fatalf("1st DNS wrong")
  261. }
  262. nm2 := ms.netmapForResponse(&tailcfg.MapResponse{
  263. Node: new(tailcfg.Node),
  264. DNSConfig: nil, // implicit
  265. })
  266. if !reflect.DeepEqual(nm2.DNS, *someDNSConfig) {
  267. t.Fatalf("2nd DNS wrong")
  268. }
  269. })
  270. t.Run("collect_services", func(t *testing.T) {
  271. ms := newTestMapSession(t)
  272. var nm *netmap.NetworkMap
  273. wantCollect := func(v bool) {
  274. t.Helper()
  275. if nm.CollectServices != v {
  276. t.Errorf("netmap.CollectServices = %v; want %v", nm.CollectServices, v)
  277. }
  278. }
  279. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  280. Node: new(tailcfg.Node),
  281. })
  282. wantCollect(false)
  283. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  284. Node: new(tailcfg.Node),
  285. CollectServices: "false",
  286. })
  287. wantCollect(false)
  288. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  289. Node: new(tailcfg.Node),
  290. CollectServices: "true",
  291. })
  292. wantCollect(true)
  293. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  294. Node: new(tailcfg.Node),
  295. CollectServices: "",
  296. })
  297. wantCollect(true)
  298. })
  299. t.Run("implicit_domain", func(t *testing.T) {
  300. ms := newTestMapSession(t)
  301. var nm *netmap.NetworkMap
  302. want := func(v string) {
  303. t.Helper()
  304. if nm.Domain != v {
  305. t.Errorf("netmap.Domain = %q; want %q", nm.Domain, v)
  306. }
  307. }
  308. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  309. Node: new(tailcfg.Node),
  310. Domain: "foo.com",
  311. })
  312. want("foo.com")
  313. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  314. Node: new(tailcfg.Node),
  315. })
  316. want("foo.com")
  317. })
  318. t.Run("implicit_node", func(t *testing.T) {
  319. someNode := &tailcfg.Node{
  320. Name: "foo",
  321. }
  322. wantNode := &tailcfg.Node{
  323. Name: "foo",
  324. ComputedName: "foo",
  325. ComputedNameWithHost: "foo",
  326. }
  327. ms := newTestMapSession(t)
  328. nm1 := ms.netmapForResponse(&tailcfg.MapResponse{
  329. Node: someNode,
  330. })
  331. if nm1.SelfNode == nil {
  332. t.Fatal("nil Node in 1st netmap")
  333. }
  334. if !reflect.DeepEqual(nm1.SelfNode, wantNode) {
  335. j, _ := json.Marshal(nm1.SelfNode)
  336. t.Errorf("Node mismatch in 1st netmap; got: %s", j)
  337. }
  338. nm2 := ms.netmapForResponse(&tailcfg.MapResponse{})
  339. if nm2.SelfNode == nil {
  340. t.Fatal("nil Node in 1st netmap")
  341. }
  342. if !reflect.DeepEqual(nm2.SelfNode, wantNode) {
  343. j, _ := json.Marshal(nm2.SelfNode)
  344. t.Errorf("Node mismatch in 2nd netmap; got: %s", j)
  345. }
  346. })
  347. }