topology.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright 2019 the Kilo authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package mesh
  15. import (
  16. "errors"
  17. "net"
  18. "sort"
  19. "github.com/squat/kilo/pkg/wireguard"
  20. )
  21. // Topology represents the logical structure of the overlay network.
  22. type Topology struct {
  23. // key is the private key of the node creating the topology.
  24. key []byte
  25. port uint32
  26. // Location is the logical location of the local host.
  27. location string
  28. segments []*segment
  29. peers []*Peer
  30. // hostname is the hostname of the local host.
  31. hostname string
  32. // leader represents whether or not the local host
  33. // is the segment leader.
  34. leader bool
  35. // persistentKeepalive is the interval in seconds of the emission
  36. // of keepalive packets by the local node to its peers.
  37. persistentKeepalive int
  38. // privateIP is the private IP address of the local node.
  39. privateIP *net.IPNet
  40. // subnet is the Pod subnet of the local node.
  41. subnet *net.IPNet
  42. // wireGuardCIDR is the allocated CIDR of the WireGuard
  43. // interface of the local node. If the local node is not
  44. // the leader, then it is nil.
  45. wireGuardCIDR *net.IPNet
  46. }
  47. type segment struct {
  48. allowedIPs []*net.IPNet
  49. endpoint *wireguard.Endpoint
  50. key []byte
  51. // Location is the logical location of this segment.
  52. location string
  53. // cidrs is a slice of subnets of all peers in the segment.
  54. cidrs []*net.IPNet
  55. // hostnames is a slice of the hostnames of the peers in the segment.
  56. hostnames []string
  57. // leader is the index of the leader of the segment.
  58. leader int
  59. // privateIPs is a slice of private IPs of all peers in the segment.
  60. privateIPs []net.IP
  61. // wireGuardIP is the allocated IP address of the WireGuard
  62. // interface on the leader of the segment.
  63. wireGuardIP net.IP
  64. }
  65. // NewTopology creates a new Topology struct from a given set of nodes and peers.
  66. func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port uint32, key []byte, subnet *net.IPNet, persistentKeepalive int) (*Topology, error) {
  67. topoMap := make(map[string][]*Node)
  68. for _, node := range nodes {
  69. var location string
  70. switch granularity {
  71. case LogicalGranularity:
  72. location = node.Location
  73. case FullGranularity:
  74. location = node.Name
  75. }
  76. topoMap[location] = append(topoMap[location], node)
  77. }
  78. var localLocation string
  79. switch granularity {
  80. case LogicalGranularity:
  81. localLocation = nodes[hostname].Location
  82. case FullGranularity:
  83. localLocation = hostname
  84. }
  85. t := Topology{key: key, port: port, hostname: hostname, location: localLocation, persistentKeepalive: persistentKeepalive, privateIP: nodes[hostname].InternalIP, subnet: nodes[hostname].Subnet}
  86. for location := range topoMap {
  87. // Sort the location so the result is stable.
  88. sort.Slice(topoMap[location], func(i, j int) bool {
  89. return topoMap[location][i].Name < topoMap[location][j].Name
  90. })
  91. leader := findLeader(topoMap[location])
  92. if location == localLocation && topoMap[location][leader].Name == hostname {
  93. t.leader = true
  94. }
  95. var allowedIPs []*net.IPNet
  96. var cidrs []*net.IPNet
  97. var hostnames []string
  98. var privateIPs []net.IP
  99. for _, node := range topoMap[location] {
  100. // Allowed IPs should include:
  101. // - the node's allocated subnet
  102. // - the node's WireGuard IP
  103. // - the node's internal IP
  104. allowedIPs = append(allowedIPs, node.Subnet, oneAddressCIDR(node.InternalIP.IP))
  105. cidrs = append(cidrs, node.Subnet)
  106. hostnames = append(hostnames, node.Name)
  107. privateIPs = append(privateIPs, node.InternalIP.IP)
  108. }
  109. t.segments = append(t.segments, &segment{
  110. allowedIPs: allowedIPs,
  111. endpoint: topoMap[location][leader].Endpoint,
  112. key: topoMap[location][leader].Key,
  113. location: location,
  114. cidrs: cidrs,
  115. hostnames: hostnames,
  116. leader: leader,
  117. privateIPs: privateIPs,
  118. })
  119. }
  120. // Sort the Topology segments so the result is stable.
  121. sort.Slice(t.segments, func(i, j int) bool {
  122. return t.segments[i].location < t.segments[j].location
  123. })
  124. for _, peer := range peers {
  125. t.peers = append(t.peers, peer)
  126. }
  127. // Sort the Topology peers so the result is stable.
  128. sort.Slice(t.peers, func(i, j int) bool {
  129. return t.peers[i].Name < t.peers[j].Name
  130. })
  131. // We need to defensively deduplicate peer allowed IPs. If two peers claim the same IP,
  132. // the WireGuard configuration could flap, causing the interface to churn.
  133. t.peers = deduplicatePeerIPs(t.peers)
  134. // Allocate IPs to the segment leaders in a stable, coordination-free manner.
  135. a := newAllocator(*subnet)
  136. for _, segment := range t.segments {
  137. ipNet := a.next()
  138. if ipNet == nil {
  139. return nil, errors.New("failed to allocate an IP address; ran out of IP addresses")
  140. }
  141. segment.wireGuardIP = ipNet.IP
  142. segment.allowedIPs = append(segment.allowedIPs, oneAddressCIDR(ipNet.IP))
  143. if t.leader && segment.location == t.location {
  144. t.wireGuardCIDR = &net.IPNet{IP: ipNet.IP, Mask: subnet.Mask}
  145. }
  146. }
  147. return &t, nil
  148. }
  149. // Conf generates a WireGuard configuration file for a given Topology.
  150. func (t *Topology) Conf() *wireguard.Conf {
  151. c := &wireguard.Conf{
  152. Interface: &wireguard.Interface{
  153. PrivateKey: t.key,
  154. ListenPort: t.port,
  155. },
  156. }
  157. for _, s := range t.segments {
  158. if s.location == t.location {
  159. continue
  160. }
  161. peer := &wireguard.Peer{
  162. AllowedIPs: s.allowedIPs,
  163. Endpoint: s.endpoint,
  164. PersistentKeepalive: t.persistentKeepalive,
  165. PublicKey: s.key,
  166. }
  167. c.Peers = append(c.Peers, peer)
  168. }
  169. for _, p := range t.peers {
  170. peer := &wireguard.Peer{
  171. AllowedIPs: p.AllowedIPs,
  172. Endpoint: p.Endpoint,
  173. PersistentKeepalive: t.persistentKeepalive,
  174. PresharedKey: p.PresharedKey,
  175. PublicKey: p.PublicKey,
  176. }
  177. c.Peers = append(c.Peers, peer)
  178. }
  179. return c
  180. }
  181. // AsPeer generates the WireGuard peer configuration for the local location of the given Topology.
  182. // This configuration can be used to configure this location as a peer of another WireGuard interface.
  183. func (t *Topology) AsPeer() *wireguard.Peer {
  184. for _, s := range t.segments {
  185. if s.location != t.location {
  186. continue
  187. }
  188. return &wireguard.Peer{
  189. AllowedIPs: s.allowedIPs,
  190. Endpoint: s.endpoint,
  191. PublicKey: s.key,
  192. }
  193. }
  194. return nil
  195. }
  196. // PeerConf generates a WireGuard configuration file for a given peer in a Topology.
  197. func (t *Topology) PeerConf(name string) *wireguard.Conf {
  198. var pka int
  199. var psk []byte
  200. for i := range t.peers {
  201. if t.peers[i].Name == name {
  202. pka = t.peers[i].PersistentKeepalive
  203. psk = t.peers[i].PresharedKey
  204. break
  205. }
  206. }
  207. c := &wireguard.Conf{}
  208. for _, s := range t.segments {
  209. peer := &wireguard.Peer{
  210. AllowedIPs: s.allowedIPs,
  211. Endpoint: s.endpoint,
  212. PersistentKeepalive: pka,
  213. PresharedKey: psk,
  214. PublicKey: s.key,
  215. }
  216. c.Peers = append(c.Peers, peer)
  217. }
  218. for i := range t.peers {
  219. if t.peers[i].Name == name {
  220. continue
  221. }
  222. peer := &wireguard.Peer{
  223. AllowedIPs: t.peers[i].AllowedIPs,
  224. PersistentKeepalive: pka,
  225. PublicKey: t.peers[i].PublicKey,
  226. Endpoint: t.peers[i].Endpoint,
  227. }
  228. c.Peers = append(c.Peers, peer)
  229. }
  230. return c
  231. }
  232. // oneAddressCIDR takes an IP address and returns a CIDR
  233. // that contains only that address.
  234. func oneAddressCIDR(ip net.IP) *net.IPNet {
  235. return &net.IPNet{IP: ip, Mask: net.CIDRMask(len(ip)*8, len(ip)*8)}
  236. }
  237. // findLeader selects a leader for the nodes in a segment;
  238. // it will select the first node that says it should lead
  239. // or the first node in the segment if none have volunteered,
  240. // always preferring those with a public external IP address,
  241. func findLeader(nodes []*Node) int {
  242. var leaders, public []int
  243. for i := range nodes {
  244. if nodes[i].Leader {
  245. if isPublic(nodes[i].Endpoint.IP) {
  246. return i
  247. }
  248. leaders = append(leaders, i)
  249. }
  250. if isPublic(nodes[i].Endpoint.IP) {
  251. public = append(public, i)
  252. }
  253. }
  254. if len(leaders) != 0 {
  255. return leaders[0]
  256. }
  257. if len(public) != 0 {
  258. return public[0]
  259. }
  260. return 0
  261. }
  262. func deduplicatePeerIPs(peers []*Peer) []*Peer {
  263. ps := make([]*Peer, len(peers))
  264. ips := make(map[string]struct{})
  265. for i, peer := range peers {
  266. p := Peer{
  267. Name: peer.Name,
  268. Peer: wireguard.Peer{
  269. Endpoint: peer.Endpoint,
  270. PersistentKeepalive: peer.PersistentKeepalive,
  271. PresharedKey: peer.PresharedKey,
  272. PublicKey: peer.PublicKey,
  273. },
  274. }
  275. for _, ip := range peer.AllowedIPs {
  276. if _, ok := ips[ip.String()]; ok {
  277. continue
  278. }
  279. p.AllowedIPs = append(p.AllowedIPs, ip)
  280. ips[ip.String()] = struct{}{}
  281. }
  282. ps[i] = &p
  283. }
  284. return ps
  285. }