calculated_remote.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package nebula
  2. import (
  3. "fmt"
  4. "math"
  5. "net"
  6. "strconv"
  7. "github.com/slackhq/nebula/cidr"
  8. "github.com/slackhq/nebula/config"
  9. "github.com/slackhq/nebula/iputil"
  10. )
  11. // This allows us to "guess" what the remote might be for a host while we wait
  12. // for the lighthouse response. See "lighthouse.calculated_remotes" in the
  13. // example config file.
  14. type calculatedRemote struct {
  15. ipNet net.IPNet
  16. maskIP iputil.VpnIp
  17. mask iputil.VpnIp
  18. port uint32
  19. }
  20. func newCalculatedRemote(ipNet *net.IPNet, port int) (*calculatedRemote, error) {
  21. // Ensure this is an IPv4 mask that we expect
  22. ones, bits := ipNet.Mask.Size()
  23. if ones == 0 || bits != 32 {
  24. return nil, fmt.Errorf("invalid mask: %v", ipNet)
  25. }
  26. if port < 0 || port > math.MaxUint16 {
  27. return nil, fmt.Errorf("invalid port: %d", port)
  28. }
  29. return &calculatedRemote{
  30. ipNet: *ipNet,
  31. maskIP: iputil.Ip2VpnIp(ipNet.IP),
  32. mask: iputil.Ip2VpnIp(ipNet.Mask),
  33. port: uint32(port),
  34. }, nil
  35. }
  36. func (c *calculatedRemote) String() string {
  37. return fmt.Sprintf("CalculatedRemote(mask=%v port=%d)", c.ipNet, c.port)
  38. }
  39. func (c *calculatedRemote) Apply(ip iputil.VpnIp) *Ip4AndPort {
  40. // Combine the masked bytes of the "mask" IP with the unmasked bytes
  41. // of the overlay IP
  42. masked := (c.maskIP & c.mask) | (ip & ^c.mask)
  43. return &Ip4AndPort{Ip: uint32(masked), Port: c.port}
  44. }
  45. func NewCalculatedRemotesFromConfig(c *config.C, k string) (*cidr.Tree4, error) {
  46. value := c.Get(k)
  47. if value == nil {
  48. return nil, nil
  49. }
  50. calculatedRemotes := cidr.NewTree4()
  51. rawMap, ok := value.(map[any]any)
  52. if !ok {
  53. return nil, fmt.Errorf("config `%s` has invalid type: %T", k, value)
  54. }
  55. for rawKey, rawValue := range rawMap {
  56. rawCIDR, ok := rawKey.(string)
  57. if !ok {
  58. return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey)
  59. }
  60. _, ipNet, err := net.ParseCIDR(rawCIDR)
  61. if err != nil {
  62. return nil, fmt.Errorf("config `%s` has invalid CIDR: %s", k, rawCIDR)
  63. }
  64. entry, err := newCalculatedRemotesListFromConfig(rawValue)
  65. if err != nil {
  66. return nil, fmt.Errorf("config '%s.%s': %w", k, rawCIDR, err)
  67. }
  68. calculatedRemotes.AddCIDR(ipNet, entry)
  69. }
  70. return calculatedRemotes, nil
  71. }
  72. func newCalculatedRemotesListFromConfig(raw any) ([]*calculatedRemote, error) {
  73. rawList, ok := raw.([]any)
  74. if !ok {
  75. return nil, fmt.Errorf("calculated_remotes entry has invalid type: %T", raw)
  76. }
  77. var l []*calculatedRemote
  78. for _, e := range rawList {
  79. c, err := newCalculatedRemotesEntryFromConfig(e)
  80. if err != nil {
  81. return nil, fmt.Errorf("calculated_remotes entry: %w", err)
  82. }
  83. l = append(l, c)
  84. }
  85. return l, nil
  86. }
  87. func newCalculatedRemotesEntryFromConfig(raw any) (*calculatedRemote, error) {
  88. rawMap, ok := raw.(map[any]any)
  89. if !ok {
  90. return nil, fmt.Errorf("invalid type: %T", raw)
  91. }
  92. rawValue := rawMap["mask"]
  93. if rawValue == nil {
  94. return nil, fmt.Errorf("missing mask: %v", rawMap)
  95. }
  96. rawMask, ok := rawValue.(string)
  97. if !ok {
  98. return nil, fmt.Errorf("invalid mask (type %T): %v", rawValue, rawValue)
  99. }
  100. _, ipNet, err := net.ParseCIDR(rawMask)
  101. if err != nil {
  102. return nil, fmt.Errorf("invalid mask: %s", rawMask)
  103. }
  104. var port int
  105. rawValue = rawMap["port"]
  106. if rawValue == nil {
  107. return nil, fmt.Errorf("missing port: %v", rawMap)
  108. }
  109. switch v := rawValue.(type) {
  110. case int:
  111. port = v
  112. case string:
  113. port, err = strconv.Atoi(v)
  114. if err != nil {
  115. return nil, fmt.Errorf("invalid port: %s: %w", v, err)
  116. }
  117. default:
  118. return nil, fmt.Errorf("invalid port (type %T): %v", rawValue, rawValue)
  119. }
  120. return newCalculatedRemote(ipNet, port)
  121. }