Anpassungen und Persistenz
All checks were successful
build-binaries / build (, amd64, linux) (push) Has been skipped
build-binaries / build (, arm, 7, linux) (push) Has been skipped
build-binaries / build (.exe, amd64, windows) (push) Has been skipped
build-binaries / build (, arm64, linux) (push) Has been skipped
build-binaries / release (push) Has been skipped
release-tag / release-image (push) Successful in 3m28s

This commit is contained in:
2025-10-30 17:17:38 +01:00
parent d700061822
commit 4740c5d6f9
2 changed files with 153 additions and 17 deletions

View File

@@ -45,7 +45,7 @@ jobs:
ext: ".exe"
env:
GO_VERSION: "1.24"
GO_VERSION: "1.25"
BINARY_NAME: ipv6calculator
steps:

168
main.go
View File

@@ -30,6 +30,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
)
// ---------------------------------------------------------------------------
@@ -54,6 +55,11 @@ var (
dhcpScope string
dhcpNamePrefix string
dhcpDomain string
// Persistenter Teil 👇
DuidHostnameList []payload
duidFile string
duidMu sync.RWMutex
)
// ---------------------------------------------------------------------------
@@ -74,8 +80,6 @@ type rangeData struct {
HaveResult bool
}
var DuidHostnameList []payload
type payload struct {
Hostname string `json:"hostname"`
DUIDs []string `json:"duids"`
@@ -94,6 +98,98 @@ type payloadHelper struct {
Dhcp6Scope string
}
func loadDuidList() {
duidMu.Lock()
defer duidMu.Unlock()
f, err := os.Open(duidFile)
if err != nil {
if os.IsNotExist(err) {
DuidHostnameList = nil
return
}
log.Printf("duid-load: %v", err)
return
}
defer f.Close()
var tmp []payload
if err := json.NewDecoder(f).Decode(&tmp); err != nil {
log.Printf("duid-load: decode: %v", err)
return
}
DuidHostnameList = tmp
log.Printf("duid-load: %d Einträge geladen", len(DuidHostnameList))
}
func saveDuidList() {
duidMu.RLock()
data, err := json.MarshalIndent(DuidHostnameList, "", " ")
duidMu.RUnlock()
if err != nil {
log.Printf("duid-save: marshal: %v", err)
return
}
tmpFile := duidFile + ".tmp"
if err := os.WriteFile(tmpFile, data, 0o644); err != nil {
log.Printf("duid-save: write tmp: %v", err)
return
}
if err := os.Rename(tmpFile, duidFile); err != nil {
log.Printf("duid-save: rename: %v", err)
return
}
}
func uniqueStrings(in []string) []string {
m := make(map[string]struct{}, len(in))
var out []string
for _, s := range in {
if s == "" {
continue
}
if _, ok := m[s]; ok {
continue
}
m[s] = struct{}{}
out = append(out, s)
}
return out
}
func uniqueU32(in []uint32) []uint32 {
m := make(map[uint32]struct{}, len(in))
var out []uint32
for _, v := range in {
if _, ok := m[v]; ok {
continue
}
m[v] = struct{}{}
out = append(out, v)
}
return out
}
// fügt neu ein ODER aktualisiert bestehenden Host
func upsertPayload(p payload) {
duidMu.Lock()
defer duidMu.Unlock()
for i := range DuidHostnameList {
if strings.EqualFold(DuidHostnameList[i].Hostname, p.Hostname) {
// merge
DuidHostnameList[i].DUIDs = uniqueStrings(append(DuidHostnameList[i].DUIDs, p.DUIDs...))
DuidHostnameList[i].IAIDs = uniqueU32(append(DuidHostnameList[i].IAIDs, p.IAIDs...))
return
}
}
// neu
p.DUIDs = uniqueStrings(p.DUIDs)
p.IAIDs = uniqueU32(p.IAIDs)
DuidHostnameList = append(DuidHostnameList, p)
}
func octetsRaw(ip string) ([]string, error) {
parts := strings.Split(ip, ".")
if len(parts) != 4 {
@@ -118,19 +214,34 @@ func enabled(k string, def bool) bool {
}
func DhcpHelperFunc(xHostname string, xDUIDs []string, xIAIDs []uint32) []payloadHelper {
/*IPv4*/
Ipv4Octets, _ := octetsRaw(defaultIP)
Ipv4Octets, _ := octetsRaw(defaultIP) // evtl. pageIP nehmen?
rHostname := []rune(xHostname)
qDUID := xDUIDs[0]
if len(rHostname) < 6 { // 2+4
// fallback: nimm letzte zwei Oktette 0.0
qCalculatedIPv4 := Ipv4Octets[0] + "." + Ipv4Octets[1] + ".0.0"
qCalculatedIPv6, _ := embedIPv4(qCalculatedIPv4)
return []payloadHelper{{
Hostname: xHostname,
DUID: firstOrEmpty(xDUIDs),
CalculatedIPv4: qCalculatedIPv4,
CalculatedIPv6: qCalculatedIPv6,
Dhcp4Scope: dhcpScope,
Dhcp6Scope: ulaPrefix,
DomainName: dhcpDomain,
DhcpServer: dhcpServer,
IAID: "0",
}}
}
qDUID := firstOrEmpty(xDUIDs)
qSegment1 := string(rHostname[2:4])
qSegment2 := string(rHostname[4:])
qCalculatedIPv4 := Ipv4Octets[0] + "." + Ipv4Octets[1] + "." + qSegment1 + "." + qSegment2
qCalculatedIPv6, _ := embedIPv4(qCalculatedIPv4)
var res []payloadHelper
for _, t := range xIAIDs {
r := payloadHelper{
res = append(res, payloadHelper{
Hostname: xHostname,
DUID: qDUID,
CalculatedIPv4: qCalculatedIPv4,
@@ -140,17 +251,28 @@ func DhcpHelperFunc(xHostname string, xDUIDs []string, xIAIDs []uint32) []payloa
DomainName: dhcpDomain,
DhcpServer: dhcpServer,
IAID: fmt.Sprintf("%d", t),
})
}
res = append(res, r)
}
return res
}
func firstOrEmpty(xs []string) string {
if len(xs) == 0 {
return ""
}
return xs[0]
}
func getDhcp() []payloadHelper {
duidMu.RLock()
defer duidMu.RUnlock()
cp := make([]payload, len(DuidHostnameList))
copy(cp, DuidHostnameList)
sortByHostname(cp)
var result []payloadHelper
sortByHostname(DuidHostnameList)
for _, b := range DuidHostnameList {
for _, b := range cp {
result = append(result, DhcpHelperFunc(b.Hostname, b.DUIDs, b.IAIDs)...)
}
return result
@@ -166,12 +288,20 @@ func register(w http.ResponseWriter, r *http.Request) {
http.Error(w, "ungültiges JSON", http.StatusBadRequest)
return
}
if p.Hostname == "" {
http.Error(w, "hostname fehlt", http.StatusBadRequest)
return
}
if len(p.DUIDs) == 0 {
// optional: nicht hart abbrechen aber wenigstens loggen
log.Printf("register: Host %s ohne DUIDs", p.Hostname)
}
// --- hier kannst du speichern, weiterverarbeiten, loggen … ---
log.Printf("neuer Client: %s → DUIDs=%v", p.Hostname, p.DUIDs)
DuidHostnameList = append(DuidHostnameList, p)
upsertPayload(p)
saveDuidList() // 👈 direkt nach Update persistieren
w.WriteHeader(http.StatusNoContent) // 204
log.Printf("client registriert/aktualisiert: %s → DUIDs=%v IAIDs=%v", p.Hostname, p.DUIDs, p.IAIDs)
w.WriteHeader(http.StatusNoContent)
}
func getdhcp6(w http.ResponseWriter, r *http.Request) {
@@ -194,6 +324,10 @@ func sortByHostname(p []payload) {
func main() {
initConfigAndTemplates()
// Persistenz initialisieren
duidFile = getenv("DUID_DB_FILE", "duids.json")
loadDuidList()
http.HandleFunc("/", handleSingle)
http.HandleFunc("/convert", handleSingleConvert)
http.HandleFunc("/range", handleRange)
@@ -466,6 +600,7 @@ var rangePageHTML = `<!DOCTYPE html>
<table>
<tr><th>IPv4</th><th>IPv6</th><th>DHCP-IPv4</th><th>DHCP-IPv6</th></tr>
{{range .Rows}}<tr><td>{{.IPv4}}</td><td>{{.IPv6}}</td><td>netsh DHCP Server {{$.DhcpServer}} Scope {{$.DhcpScope}} Add reservedip {{.IPv4}} "{{.Name}}.{{$.DhcpDomain}}" "" "DHCP"</td><td>---</td></tr>{{end}}
//Add-DhcpServerv4Reservation -ComputerName $Srv -ScopeId $Prefix -IPAddress IPv6Address -ClientId $l.ClientDuid -Name $l.HostName -Description "Auto-reserved after rollout" -Type Dhcp
</table>
{{end}}
{{if .Error}}<p style="color:#b00">Fehler: {{.Error}}</p>{{end}}
@@ -492,4 +627,5 @@ var rangeDHCP6HTML = `<!DOCTYPE html>
</table>
</body></html>`
//Add-DhcpServerv4Reservation -ComputerName $Srv -ScopeId $Prefix -IPAddress IPv6Address -ClientId $l.ClientDuid -Name $l.HostName -Description "Auto-reserved after rollout" -Type Dhcp
//Add-DhcpServerv6Reservation -ComputerName $Srv -Prefix $Prefix -IPAddress IPv6Address -ClientDuid $l.ClientDuid -Iaid $l.Iaid -Name $l.HostName -Description "Auto-reserved after rollout"