Browse Source

:gear: Save DHCP leases to disk

Ettore Di Giacinto 3 years ago
parent
commit
d837411ccf
3 changed files with 60 additions and 5 deletions
  1. 12 1
      cmd/main.go
  2. 7 1
      pkg/utils/crypto.go
  3. 41 3
      pkg/vpn/dhcp.go

+ 12 - 1
cmd/main.go

@@ -19,6 +19,7 @@ import (
 	"context"
 	"fmt"
 	"os"
+	"path/filepath"
 	"time"
 
 	"github.com/mudler/edgevpn/api"
@@ -34,6 +35,11 @@ This is free software, and you are welcome to redistribute it
 under certain conditions.`
 
 func MainFlags() []cli.Flag {
+	basedir, _ := os.UserHomeDir()
+	if basedir == "" {
+		basedir = os.TempDir()
+	}
+
 	return append([]cli.Flag{
 		&cli.IntFlag{
 			Name:  "key-otp-interval",
@@ -57,6 +63,11 @@ func MainFlags() []cli.Flag {
 			Value: ":8080",
 			Usage: "API listening port",
 		},
+		&cli.StringFlag{
+			Name:  "lease-dir",
+			Value: filepath.Join(basedir, ".edgevpn", "leases"),
+			Usage: "DHCP leases directory",
+		},
 		&cli.StringFlag{
 			Name:   "address",
 			Usage:  "VPN virtual address, e.g. 10.1.0.1/24. No address specified enables p2p ip negotiation (experimental)",
@@ -91,7 +102,7 @@ func Main() func(c *cli.Context) error {
 		o, vpnOpts, ll := cliToOpts(c)
 
 		if c.String("address") == "" {
-			nodeOpts, vO := vpn.DHCP(ll, 10*time.Second)
+			nodeOpts, vO := vpn.DHCP(ll, 10*time.Second, c.String("lease-dir"))
 			o = append(
 				append(
 					o,

+ 7 - 1
pkg/utils/crypto.go

@@ -1,4 +1,4 @@
-// Copyright © 2021 Ettore Di Giacinto <[email protected]>
+// Copyright © 2021-2022 Ettore Di Giacinto <[email protected]>
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@ package utils
 import (
 	"crypto/aes"
 	"crypto/cipher"
+	"crypto/md5"
 	"crypto/rand"
 	"encoding/hex"
 	"errors"
@@ -76,3 +77,8 @@ func AESDecrypt(text string, key *[32]byte) (plaintext string, err error) {
 
 	return string(decodedtext), err
 }
+
+func MD5(text string) string {
+	hash := md5.Sum([]byte(text))
+	return hex.EncodeToString(hash[:])
+}

+ 41 - 3
pkg/vpn/dhcp.go

@@ -18,6 +18,9 @@ package vpn
 import (
 	"context"
 	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
 	"time"
 
 	"github.com/ipfs/go-log/v2"
@@ -30,16 +33,41 @@ import (
 	"github.com/mudler/edgevpn/pkg/blockchain"
 )
 
-func DHCP(l log.StandardLogger, announcetime time.Duration) ([]node.Option, []Option) {
+func checkDHCPLease(c node.Config, leasedir string) string {
+	// retrieve lease if present
+
+	leaseFileName := utils.MD5(fmt.Sprintf("%s-ek", c.ExchangeKey))
+	leaseFile := filepath.Join(leasedir, leaseFileName)
+	if _, err := os.Stat(leaseFile); err == nil {
+		b, _ := ioutil.ReadFile(leaseFile)
+		return string(b)
+	}
+	return ""
+}
+
+func DHCP(l log.StandardLogger, announcetime time.Duration, leasedir string) ([]node.Option, []Option) {
 	ip := make(chan string, 1)
 	return []node.Option{
+			func(cfg *node.Config) error {
+				// retrieve lease if present. consumed by conngater when starting the node
+				lease := checkDHCPLease(*cfg, leasedir)
+				if lease != "" {
+					cfg.InterfaceAddress = fmt.Sprintf("%s/24", lease)
+				}
+				return nil
+			},
 			node.WithNetworkService(
 				func(ctx context.Context, c node.Config, n *node.Node, b *blockchain.Ledger) error {
-					//  whoever wants an IP:
+
+					os.MkdirAll(leasedir, 0600)
+
+					// retrieve lease if present
+					var wantedIP = checkDHCPLease(c, leasedir)
+
+					//  whoever wants a new IP:
 					//  1. Get available nodes. Filter from Machine those that do not have an IP.
 					//  2. Get the leader among them. If we are not, we wait
 					//  3. If we are the leader, pick an IP and start the VPN with that IP
-					var wantedIP string
 					for wantedIP == "" {
 						time.Sleep(5 * time.Second)
 
@@ -85,12 +113,22 @@ func DHCP(l log.StandardLogger, announcetime time.Duration) ([]node.Option, []Op
 						wantedIP = utils.NextIP("10.1.0.1", ips)
 					}
 
+					// Save lease to disk
+					leaseFileName := utils.MD5(fmt.Sprintf("%s-ek", c.ExchangeKey))
+					leaseFile := filepath.Join(leasedir, leaseFileName)
+					l.Debugf("Writing lease to '%s'", leaseFile)
+					if err := ioutil.WriteFile(leaseFile, []byte(wantedIP), 0600); err != nil {
+						l.Warn(err)
+					}
+
+					// propagate ip to channel that is read while starting vpn
 					ip <- wantedIP
 					return nil
 				},
 			),
 		}, []Option{
 			func(cfg *Config) error {
+				// read back IP when starting vpn
 				cfg.InterfaceAddress = fmt.Sprintf("%s/24", <-ip)
 				close(ip)
 				l.Debug("IP Received", cfg.InterfaceAddress)