hostmap.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. //go:build e2e_testing
  2. package router
  3. import (
  4. "fmt"
  5. "net/netip"
  6. "sort"
  7. "strings"
  8. "github.com/slackhq/nebula"
  9. )
  10. type edge struct {
  11. from string
  12. to string
  13. dual bool
  14. }
  15. func renderHostmaps(controls ...*nebula.Control) string {
  16. var lines []*edge
  17. r := "graph TB\n"
  18. for _, c := range controls {
  19. sr, se := renderHostmap(c)
  20. r += sr
  21. for _, e := range se {
  22. add := true
  23. // Collapse duplicate edges into a bi-directionally connected edge
  24. for _, ge := range lines {
  25. if e.to == ge.from && e.from == ge.to {
  26. add = false
  27. ge.dual = true
  28. break
  29. }
  30. }
  31. if add {
  32. lines = append(lines, e)
  33. }
  34. }
  35. }
  36. for _, line := range lines {
  37. if line.dual {
  38. r += fmt.Sprintf("\t%v <--> %v\n", line.from, line.to)
  39. } else {
  40. r += fmt.Sprintf("\t%v --> %v\n", line.from, line.to)
  41. }
  42. }
  43. return r
  44. }
  45. func renderHostmap(c *nebula.Control) (string, []*edge) {
  46. var lines []string
  47. var globalLines []*edge
  48. crt := c.GetCertState().GetDefaultCertificate()
  49. clusterName := strings.Trim(crt.Name(), " ")
  50. clusterVpnIp := crt.Networks()[0].Addr()
  51. r := fmt.Sprintf("\tsubgraph %s[\"%s (%s)\"]\n", clusterName, clusterName, clusterVpnIp)
  52. hm := c.GetHostmap()
  53. hm.RLock()
  54. defer hm.RUnlock()
  55. // Draw the vpn to index nodes
  56. r += fmt.Sprintf("\t\tsubgraph %s.hosts[\"Hosts (vpn ip to index)\"]\n", clusterName)
  57. hosts := sortedHosts(hm.Hosts)
  58. for _, vpnIp := range hosts {
  59. hi := hm.Hosts[vpnIp]
  60. r += fmt.Sprintf("\t\t\t%v.%v[\"%v\"]\n", clusterName, vpnIp, vpnIp)
  61. lines = append(lines, fmt.Sprintf("%v.%v --> %v.%v", clusterName, vpnIp, clusterName, hi.GetLocalIndex()))
  62. rs := hi.GetRelayState()
  63. for _, relayIp := range rs.CopyRelayIps() {
  64. lines = append(lines, fmt.Sprintf("%v.%v --> %v.%v", clusterName, vpnIp, clusterName, relayIp))
  65. }
  66. for _, relayIp := range rs.CopyRelayForIdxs() {
  67. lines = append(lines, fmt.Sprintf("%v.%v --> %v.%v", clusterName, vpnIp, clusterName, relayIp))
  68. }
  69. }
  70. r += "\t\tend\n"
  71. // Draw the relay hostinfos
  72. if len(hm.Relays) > 0 {
  73. r += fmt.Sprintf("\t\tsubgraph %s.relays[\"Relays (relay index to hostinfo)\"]\n", clusterName)
  74. for relayIndex, hi := range hm.Relays {
  75. r += fmt.Sprintf("\t\t\t%v.%v[\"%v\"]\n", clusterName, relayIndex, relayIndex)
  76. lines = append(lines, fmt.Sprintf("%v.%v --> %v.%v", clusterName, relayIndex, clusterName, hi.GetLocalIndex()))
  77. }
  78. r += "\t\tend\n"
  79. }
  80. // Draw the local index to relay or remote index nodes
  81. r += fmt.Sprintf("\t\tsubgraph indexes.%s[\"Indexes (index to hostinfo)\"]\n", clusterName)
  82. indexes := sortedIndexes(hm.Indexes)
  83. for _, idx := range indexes {
  84. hi, ok := hm.Indexes[idx]
  85. if ok {
  86. r += fmt.Sprintf("\t\t\t%v.%v[\"%v (%v)\"]\n", clusterName, idx, idx, hi.GetVpnAddrs())
  87. remoteClusterName := strings.Trim(hi.GetCert().Certificate.Name(), " ")
  88. globalLines = append(globalLines, &edge{from: fmt.Sprintf("%v.%v", clusterName, idx), to: fmt.Sprintf("%v.%v", remoteClusterName, hi.GetRemoteIndex())})
  89. _ = hi
  90. }
  91. }
  92. r += "\t\tend\n"
  93. // Add the edges inside this host
  94. for _, line := range lines {
  95. r += fmt.Sprintf("\t\t%v\n", line)
  96. }
  97. r += "\tend\n"
  98. return r, globalLines
  99. }
  100. func sortedHosts(hosts map[netip.Addr]*nebula.HostInfo) []netip.Addr {
  101. keys := make([]netip.Addr, 0, len(hosts))
  102. for key := range hosts {
  103. keys = append(keys, key)
  104. }
  105. sort.SliceStable(keys, func(i, j int) bool {
  106. return keys[i].Compare(keys[j]) > 0
  107. })
  108. return keys
  109. }
  110. func sortedIndexes(indexes map[uint32]*nebula.HostInfo) []uint32 {
  111. keys := make([]uint32, 0, len(indexes))
  112. for key := range indexes {
  113. keys = append(keys, key)
  114. }
  115. sort.SliceStable(keys, func(i, j int) bool {
  116. return keys[i] > keys[j]
  117. })
  118. return keys
  119. }