diff --git a/internal/dhcp/db.go b/internal/dhcp/db.go new file mode 100644 index 0000000..c93aa1b --- /dev/null +++ b/internal/dhcp/db.go @@ -0,0 +1,19 @@ +package dhcp + +import ( + "git.g3e.fr/syonad/two/pkg/db/kv" + "github.com/dgraph-io/badger/v4" +) + +func StoreDHCPEntries(db *badger.DB, subnetName string, entries map[string]string) error { + for ip, mac := range entries { + if err := kv.AddInDB(db, "subnet/"+subnetName+"/dhcp/"+ip, mac); err != nil { + return err + } + } + return nil +} + +func GetMACForIP(db *badger.DB, subnetName, ip string) (string, error) { + return kv.GetFromDB(db, "subnet/"+subnetName+"/dhcp/"+ip) +} diff --git a/internal/dhcp/dhcp_test.go b/internal/dhcp/dhcp_test.go index c4a659f..1681fec 100644 --- a/internal/dhcp/dhcp_test.go +++ b/internal/dhcp/dhcp_test.go @@ -61,7 +61,7 @@ func newConf(t *testing.T, cidr string) Config { func TestGenerateConfig_CreatesFile(t *testing.T) { conf := newConf(t, "192.168.1.0/29") // 6 hôtes - path, err := GenerateConfig(conf) + path, _, err := GenerateConfig(conf) if err != nil { t.Fatalf("GenerateConfig a échoué : %v", err) } @@ -73,7 +73,7 @@ func TestGenerateConfig_CreatesFile(t *testing.T) { func TestGenerateConfig_FilenameMatchesName(t *testing.T) { conf := newConf(t, "192.168.1.0/29") - path, err := GenerateConfig(conf) + path, _, err := GenerateConfig(conf) if err != nil { t.Fatalf("GenerateConfig a échoué : %v", err) } @@ -86,7 +86,7 @@ func TestGenerateConfig_FilenameMatchesName(t *testing.T) { func TestGenerateConfig_ContainsGateway(t *testing.T) { conf := newConf(t, "192.168.1.0/29") - path, _ := GenerateConfig(conf) + path, _, _ := GenerateConfig(conf) content, _ := os.ReadFile(path) if !strings.Contains(string(content), "dhcp-option=3,192.168.1.1") { @@ -102,7 +102,7 @@ func TestGenerateConfig_ContainsDhcpRange(t *testing.T) { Name: "vpc1", ConfDir: t.TempDir(), } - path, _ := GenerateConfig(conf) + path, _, _ := GenerateConfig(conf) content, _ := os.ReadFile(path) if !strings.Contains(string(content), "dhcp-range=10.10.0.0,static,255.255.255.0,12h") { @@ -113,7 +113,7 @@ func TestGenerateConfig_ContainsDhcpRange(t *testing.T) { func TestGenerateConfig_OneHostEntryPerIP(t *testing.T) { // /29 = réseau + broadcast + 6 hôtes → 8 adresses conf := newConf(t, "10.0.0.0/29") - path, _ := GenerateConfig(conf) + path, _, _ := GenerateConfig(conf) content, _ := os.ReadFile(path) lines := strings.Split(string(content), "\n") @@ -131,7 +131,7 @@ func TestGenerateConfig_OneHostEntryPerIP(t *testing.T) { func TestGenerateConfig_MACPrefix(t *testing.T) { conf := newConf(t, "10.0.0.0/30") // 4 adresses - path, _ := GenerateConfig(conf) + path, _, _ := GenerateConfig(conf) content, _ := os.ReadFile(path) if !strings.Contains(string(content), "00:22:33:") { @@ -148,7 +148,7 @@ func TestGenerateConfig_CreatesConfDir(t *testing.T) { Name: "net", ConfDir: dir, } - if _, err := GenerateConfig(conf); err != nil { + if _, _, err := GenerateConfig(conf); err != nil { t.Fatalf("GenerateConfig devrait créer les répertoires manquants : %v", err) } if _, err := os.Stat(dir); os.IsNotExist(err) { diff --git a/internal/dhcp/generate.go b/internal/dhcp/generate.go index 01bfe20..3c46fe2 100644 --- a/internal/dhcp/generate.go +++ b/internal/dhcp/generate.go @@ -8,7 +8,7 @@ import ( "strings" ) -func GenerateConfig(c Config) (string, error) { +func GenerateConfig(c Config) (string, map[string]string, error) { mask := fmt.Sprintf("%d.%d.%d.%d", c.Network.Mask[0], c.Network.Mask[1], c.Network.Mask[2], c.Network.Mask[3]) var sb strings.Builder @@ -17,18 +17,20 @@ func GenerateConfig(c Config) (string, error) { fmt.Fprintf(&sb, "dhcp-option=3,%s\n", c.Gateway.String()) fmt.Fprintf(&sb, "dhcp-option=6,1.1.1.1,8.8.8.8\n\n") + entries := make(map[string]string) i := 0 for ip := cloneIP(c.Network.IP); c.Network.Contains(ip); incrementIP(ip) { - fmt.Fprintf(&sb, "dhcp-host=00:22:33:%02X:%02X:%02X,%s\n", - (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF, ip) + mac := fmt.Sprintf("00:22:33:%02X:%02X:%02X", (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF) + fmt.Fprintf(&sb, "dhcp-host=%s,%s\n", mac, ip) + entries[ip.String()] = mac i++ } outPath := filepath.Join(c.ConfDir, c.Name+".conf") if err := os.MkdirAll(c.ConfDir, 0755); err != nil { - return "", err + return "", nil, err } - return outPath, os.WriteFile(outPath, []byte(sb.String()), 0644) + return outPath, entries, os.WriteFile(outPath, []byte(sb.String()), 0644) } func incrementIP(ip net.IP) { diff --git a/internal/subnet/create.go b/internal/subnet/create.go index 07658c6..5e80634 100644 --- a/internal/subnet/create.go +++ b/internal/subnet/create.go @@ -101,9 +101,13 @@ func CreateSubnet(db *badger.DB, subnetName string) error { Name: d.vpc + "_" + d.bridge, ConfDir: "/etc/dnsmasq.d", } - if _, err := dhcp.GenerateConfig(conf); err != nil { + _, entries, err := dhcp.GenerateConfig(conf) + if err != nil { return fmt.Errorf("generate dhcp config: %w", err) } + if err := dhcp.StoreDHCPEntries(db, subnetName, entries); err != nil { + return fmt.Errorf("store dhcp entries: %w", err) + } svc, err := systemd.New() if err != nil { diff --git a/internal/subnet/delete.go b/internal/subnet/delete.go index 8606cb9..e7e3929 100644 --- a/internal/subnet/delete.go +++ b/internal/subnet/delete.go @@ -42,6 +42,9 @@ func DeleteSubnet(db *badger.DB, subnetName string) error { if err := os.Remove("/etc/dnsmasq.d/" + d.vpc + "_" + d.bridge + ".conf"); err != nil && !os.IsNotExist(err) { return fmt.Errorf("remove dnsmasq config: %w", err) } + if err := kv.DeleteInDB(db, "subnet/"+subnetName+"/dhcp"); err != nil { + return fmt.Errorf("delete dhcp entries: %w", err) + } if err := ebtables.DeleteARPToGateway(d.bridge, d.gatewayIP.String()); err != nil { return fmt.Errorf("delete ebtables arp rule: %w", err)