dhcp.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Copyright © 2021-2022 Ettore Di Giacinto <[email protected]>
  2. //
  3. // This program is free software; you can redistribute it and/or modify
  4. // it under the terms of the GNU General Public License as published by
  5. // the Free Software Foundation; either version 2 of the License, or
  6. // (at your option) any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License along
  14. // with this program; if not, see <http://www.gnu.org/licenses/>.
  15. package vpn
  16. import (
  17. "context"
  18. "fmt"
  19. "io/ioutil"
  20. "os"
  21. "path/filepath"
  22. "time"
  23. "github.com/ipfs/go-log/v2"
  24. "github.com/mudler/edgevpn/pkg/crypto"
  25. "github.com/mudler/edgevpn/pkg/node"
  26. "github.com/mudler/edgevpn/pkg/protocol"
  27. "github.com/mudler/edgevpn/pkg/services"
  28. "github.com/mudler/edgevpn/pkg/types"
  29. "github.com/mudler/edgevpn/pkg/utils"
  30. "github.com/mudler/edgevpn/pkg/blockchain"
  31. )
  32. func checkDHCPLease(c node.Config, leasedir string) string {
  33. // retrieve lease if present
  34. leaseFileName := crypto.MD5(fmt.Sprintf("%s-ek", c.ExchangeKey))
  35. leaseFile := filepath.Join(leasedir, leaseFileName)
  36. if _, err := os.Stat(leaseFile); err == nil {
  37. b, _ := ioutil.ReadFile(leaseFile)
  38. return string(b)
  39. }
  40. return ""
  41. }
  42. func contains(slice []string, elem string) bool {
  43. for _, s := range slice {
  44. if elem == s {
  45. return true
  46. }
  47. }
  48. return false
  49. }
  50. // DHCPNetworkService returns a DHCP network service
  51. func DHCPNetworkService(ip chan string, l log.StandardLogger, maxTime time.Duration, leasedir string, address string) node.NetworkService {
  52. return func(ctx context.Context, c node.Config, n *node.Node, b *blockchain.Ledger) error {
  53. os.MkdirAll(leasedir, 0600)
  54. // retrieve lease if present
  55. var wantedIP = checkDHCPLease(c, leasedir)
  56. // whoever wants a new IP:
  57. // 1. Get available nodes. Filter from Machine those that do not have an IP.
  58. // 2. Get the leader among them. If we are not, we wait
  59. // 3. If we are the leader, pick an IP and start the VPN with that IP
  60. for wantedIP == "" {
  61. time.Sleep(5 * time.Second)
  62. // This network service is blocking and calls in before VPN, hence it needs to registered before VPN
  63. nodes := services.AvailableNodes(b, maxTime)
  64. currentIPs := map[string]string{}
  65. ips := []string{}
  66. for _, t := range b.LastBlock().Storage[protocol.MachinesLedgerKey] {
  67. var m types.Machine
  68. t.Unmarshal(&m)
  69. currentIPs[m.PeerID] = m.Address
  70. l.Debugf("%s uses %s", m.PeerID, m.Address)
  71. ips = append(ips, m.Address)
  72. }
  73. nodesWithNoIP := []string{}
  74. for _, nn := range nodes {
  75. if _, exists := currentIPs[nn]; !exists {
  76. nodesWithNoIP = append(nodesWithNoIP, nn)
  77. }
  78. }
  79. if len(nodes) <= 1 {
  80. l.Debug("not enough nodes to determine an IP, sleeping")
  81. continue
  82. }
  83. if len(nodesWithNoIP) == 0 {
  84. l.Debug("not enough nodes waiting for IP being announced, sleeping")
  85. continue
  86. }
  87. shouldBeLeader := utils.Leader(nodesWithNoIP)
  88. var lead string
  89. v, exists := b.GetKey("dhcp", "leader")
  90. if exists {
  91. v.Unmarshal(&lead)
  92. }
  93. if shouldBeLeader != n.Host().ID().String() && lead != n.Host().ID().String() {
  94. c.Logger.Infof("<%s> not a leader, leader is '%s', sleeping", n.Host().ID().String(), shouldBeLeader)
  95. continue
  96. }
  97. if shouldBeLeader == n.Host().ID().String() && (lead == "" || !contains(nodesWithNoIP, lead)) {
  98. b.Persist(ctx, 5*time.Second, 15*time.Second, "dhcp", "leader", n.Host().ID().String())
  99. c.Logger.Info("Announcing ourselves as leader, backing off")
  100. continue
  101. }
  102. if lead != n.Host().ID().String() {
  103. c.Logger.Info("Backing off, as we are not currently flagged as leader")
  104. time.Sleep(5 * time.Second)
  105. continue
  106. }
  107. l.Debug("Nodes with no ip", nodesWithNoIP)
  108. // We are lead
  109. l.Debug("picking up between", ips)
  110. wantedIP = utils.NextIP(address, ips)
  111. }
  112. // Save lease to disk
  113. leaseFileName := crypto.MD5(fmt.Sprintf("%s-ek", c.ExchangeKey))
  114. leaseFile := filepath.Join(leasedir, leaseFileName)
  115. l.Debugf("Writing lease to '%s'", leaseFile)
  116. if err := ioutil.WriteFile(leaseFile, []byte(wantedIP), 0600); err != nil {
  117. l.Warn(err)
  118. }
  119. // propagate ip to channel that is read while starting vpn
  120. ip <- wantedIP
  121. // Gate connections from VPN
  122. return n.BlockSubnet(fmt.Sprintf("%s/24", wantedIP))
  123. }
  124. }
  125. // DHCP returns a DHCP network service. It requires the Alive Service in order to determine available nodes.
  126. // Nodes available are used to determine which needs an IP and when maxTime expires nodes are marked as offline and
  127. // not considered.
  128. func DHCP(l log.StandardLogger, maxTime time.Duration, leasedir string, address string) ([]node.Option, []Option) {
  129. ip := make(chan string, 1)
  130. return []node.Option{
  131. func(cfg *node.Config) error {
  132. // retrieve lease if present. consumed by conngater when starting the node
  133. lease := checkDHCPLease(*cfg, leasedir)
  134. if lease != "" {
  135. cfg.InterfaceAddress = fmt.Sprintf("%s/24", lease)
  136. }
  137. return nil
  138. },
  139. node.WithNetworkService(DHCPNetworkService(ip, l, maxTime, leasedir, address)),
  140. }, []Option{
  141. func(cfg *Config) error {
  142. // read back IP when starting vpn
  143. cfg.InterfaceAddress = fmt.Sprintf("%s/24", <-ip)
  144. close(ip)
  145. l.Debug("IP Received", cfg.InterfaceAddress)
  146. return nil
  147. },
  148. }
  149. }