map_test.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. "fmt"
  7. "reflect"
  8. "strings"
  9. "testing"
  10. "time"
  11. "tailscale.com/tailcfg"
  12. "tailscale.com/types/netmap"
  13. "tailscale.com/types/wgkey"
  14. )
  15. func TestUndeltaPeers(t *testing.T) {
  16. defer func(old func() time.Time) { clockNow = old }(clockNow)
  17. var curTime time.Time
  18. clockNow = func() time.Time {
  19. return curTime
  20. }
  21. online := func(v bool) func(*tailcfg.Node) {
  22. return func(n *tailcfg.Node) {
  23. n.Online = &v
  24. }
  25. }
  26. seenAt := func(t time.Time) func(*tailcfg.Node) {
  27. return func(n *tailcfg.Node) {
  28. n.LastSeen = &t
  29. }
  30. }
  31. n := func(id tailcfg.NodeID, name string, mod ...func(*tailcfg.Node)) *tailcfg.Node {
  32. n := &tailcfg.Node{ID: id, Name: name}
  33. for _, f := range mod {
  34. f(n)
  35. }
  36. return n
  37. }
  38. peers := func(nv ...*tailcfg.Node) []*tailcfg.Node { return nv }
  39. tests := []struct {
  40. name string
  41. mapRes *tailcfg.MapResponse
  42. curTime time.Time
  43. prev []*tailcfg.Node
  44. want []*tailcfg.Node
  45. }{
  46. {
  47. name: "full_peers",
  48. mapRes: &tailcfg.MapResponse{
  49. Peers: peers(n(1, "foo"), n(2, "bar")),
  50. },
  51. want: peers(n(1, "foo"), n(2, "bar")),
  52. },
  53. {
  54. name: "full_peers_ignores_deltas",
  55. mapRes: &tailcfg.MapResponse{
  56. Peers: peers(n(1, "foo"), n(2, "bar")),
  57. PeersRemoved: []tailcfg.NodeID{2},
  58. },
  59. want: peers(n(1, "foo"), n(2, "bar")),
  60. },
  61. {
  62. name: "add_and_update",
  63. prev: peers(n(1, "foo"), n(2, "bar")),
  64. mapRes: &tailcfg.MapResponse{
  65. PeersChanged: peers(n(0, "zero"), n(2, "bar2"), n(3, "three")),
  66. },
  67. want: peers(n(0, "zero"), n(1, "foo"), n(2, "bar2"), n(3, "three")),
  68. },
  69. {
  70. name: "remove",
  71. prev: peers(n(1, "foo"), n(2, "bar")),
  72. mapRes: &tailcfg.MapResponse{
  73. PeersRemoved: []tailcfg.NodeID{1},
  74. },
  75. want: peers(n(2, "bar")),
  76. },
  77. {
  78. name: "add_and_remove",
  79. prev: peers(n(1, "foo"), n(2, "bar")),
  80. mapRes: &tailcfg.MapResponse{
  81. PeersChanged: peers(n(1, "foo2")),
  82. PeersRemoved: []tailcfg.NodeID{2},
  83. },
  84. want: peers(n(1, "foo2")),
  85. },
  86. {
  87. name: "unchanged",
  88. prev: peers(n(1, "foo"), n(2, "bar")),
  89. mapRes: &tailcfg.MapResponse{},
  90. want: peers(n(1, "foo"), n(2, "bar")),
  91. },
  92. {
  93. name: "online_change",
  94. prev: peers(n(1, "foo"), n(2, "bar")),
  95. mapRes: &tailcfg.MapResponse{
  96. OnlineChange: map[tailcfg.NodeID]bool{
  97. 1: true,
  98. },
  99. },
  100. want: peers(
  101. n(1, "foo", online(true)),
  102. n(2, "bar"),
  103. ),
  104. },
  105. {
  106. name: "online_change_offline",
  107. prev: peers(n(1, "foo"), n(2, "bar")),
  108. mapRes: &tailcfg.MapResponse{
  109. OnlineChange: map[tailcfg.NodeID]bool{
  110. 1: false,
  111. 2: true,
  112. },
  113. },
  114. want: peers(
  115. n(1, "foo", online(false)),
  116. n(2, "bar", online(true)),
  117. ),
  118. },
  119. {
  120. name: "peer_seen_at",
  121. prev: peers(n(1, "foo", seenAt(time.Unix(111, 0))), n(2, "bar")),
  122. curTime: time.Unix(123, 0),
  123. mapRes: &tailcfg.MapResponse{
  124. PeerSeenChange: map[tailcfg.NodeID]bool{
  125. 1: false,
  126. 2: true,
  127. },
  128. },
  129. want: peers(
  130. n(1, "foo"),
  131. n(2, "bar", seenAt(time.Unix(123, 0))),
  132. ),
  133. },
  134. }
  135. for _, tt := range tests {
  136. t.Run(tt.name, func(t *testing.T) {
  137. if !tt.curTime.IsZero() {
  138. curTime = tt.curTime
  139. }
  140. undeltaPeers(tt.mapRes, tt.prev)
  141. if !reflect.DeepEqual(tt.mapRes.Peers, tt.want) {
  142. t.Errorf("wrong results\n got: %s\nwant: %s", formatNodes(tt.mapRes.Peers), formatNodes(tt.want))
  143. }
  144. })
  145. }
  146. }
  147. func formatNodes(nodes []*tailcfg.Node) string {
  148. var sb strings.Builder
  149. for i, n := range nodes {
  150. if i > 0 {
  151. sb.WriteString(", ")
  152. }
  153. var extra string
  154. if n.Online != nil {
  155. extra += fmt.Sprintf(", online=%v", *n.Online)
  156. }
  157. if n.LastSeen != nil {
  158. extra += fmt.Sprintf(", lastSeen=%v", n.LastSeen.Unix())
  159. }
  160. fmt.Fprintf(&sb, "(%d, %q%s)", n.ID, n.Name, extra)
  161. }
  162. return sb.String()
  163. }
  164. func newTestMapSession(t *testing.T) *mapSession {
  165. k, err := wgkey.NewPrivate()
  166. if err != nil {
  167. t.Fatal(err)
  168. }
  169. return newMapSession(k)
  170. }
  171. func TestNetmapForResponse(t *testing.T) {
  172. t.Run("implicit_packetfilter", func(t *testing.T) {
  173. somePacketFilter := []tailcfg.FilterRule{
  174. {
  175. SrcIPs: []string{"*"},
  176. DstPorts: []tailcfg.NetPortRange{
  177. {IP: "10.2.3.4", Ports: tailcfg.PortRange{First: 22, Last: 22}},
  178. },
  179. },
  180. }
  181. ms := newTestMapSession(t)
  182. nm1 := ms.netmapForResponse(&tailcfg.MapResponse{
  183. Node: new(tailcfg.Node),
  184. PacketFilter: somePacketFilter,
  185. })
  186. if len(nm1.PacketFilter) == 0 {
  187. t.Fatalf("zero length PacketFilter")
  188. }
  189. nm2 := ms.netmapForResponse(&tailcfg.MapResponse{
  190. Node: new(tailcfg.Node),
  191. PacketFilter: nil, // testing that the server can omit this.
  192. })
  193. if len(nm1.PacketFilter) == 0 {
  194. t.Fatalf("zero length PacketFilter in 2nd netmap")
  195. }
  196. if !reflect.DeepEqual(nm1.PacketFilter, nm2.PacketFilter) {
  197. t.Error("packet filters differ")
  198. }
  199. })
  200. t.Run("implicit_dnsconfig", func(t *testing.T) {
  201. someDNSConfig := &tailcfg.DNSConfig{Domains: []string{"foo", "bar"}}
  202. ms := newTestMapSession(t)
  203. nm1 := ms.netmapForResponse(&tailcfg.MapResponse{
  204. Node: new(tailcfg.Node),
  205. DNSConfig: someDNSConfig,
  206. })
  207. if !reflect.DeepEqual(nm1.DNS, *someDNSConfig) {
  208. t.Fatalf("1st DNS wrong")
  209. }
  210. nm2 := ms.netmapForResponse(&tailcfg.MapResponse{
  211. Node: new(tailcfg.Node),
  212. DNSConfig: nil, // implict
  213. })
  214. if !reflect.DeepEqual(nm2.DNS, *someDNSConfig) {
  215. t.Fatalf("2nd DNS wrong")
  216. }
  217. })
  218. t.Run("collect_services", func(t *testing.T) {
  219. ms := newTestMapSession(t)
  220. var nm *netmap.NetworkMap
  221. wantCollect := func(v bool) {
  222. t.Helper()
  223. if nm.CollectServices != v {
  224. t.Errorf("netmap.CollectServices = %v; want %v", nm.CollectServices, v)
  225. }
  226. }
  227. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  228. Node: new(tailcfg.Node),
  229. })
  230. wantCollect(false)
  231. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  232. Node: new(tailcfg.Node),
  233. CollectServices: "false",
  234. })
  235. wantCollect(false)
  236. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  237. Node: new(tailcfg.Node),
  238. CollectServices: "true",
  239. })
  240. wantCollect(true)
  241. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  242. Node: new(tailcfg.Node),
  243. CollectServices: "",
  244. })
  245. wantCollect(true)
  246. })
  247. t.Run("implicit_domain", func(t *testing.T) {
  248. ms := newTestMapSession(t)
  249. var nm *netmap.NetworkMap
  250. want := func(v string) {
  251. t.Helper()
  252. if nm.Domain != v {
  253. t.Errorf("netmap.Domain = %q; want %q", nm.Domain, v)
  254. }
  255. }
  256. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  257. Node: new(tailcfg.Node),
  258. Domain: "foo.com",
  259. })
  260. want("foo.com")
  261. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  262. Node: new(tailcfg.Node),
  263. })
  264. want("foo.com")
  265. })
  266. }