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
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:
@@ -45,7 +45,7 @@ jobs:
|
|||||||
ext: ".exe"
|
ext: ".exe"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: "1.24"
|
GO_VERSION: "1.25"
|
||||||
BINARY_NAME: ipv6calculator
|
BINARY_NAME: ipv6calculator
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
168
main.go
168
main.go
@@ -30,6 +30,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -54,6 +55,11 @@ var (
|
|||||||
dhcpScope string
|
dhcpScope string
|
||||||
dhcpNamePrefix string
|
dhcpNamePrefix string
|
||||||
dhcpDomain string
|
dhcpDomain string
|
||||||
|
|
||||||
|
// Persistenter Teil 👇
|
||||||
|
DuidHostnameList []payload
|
||||||
|
duidFile string
|
||||||
|
duidMu sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -74,8 +80,6 @@ type rangeData struct {
|
|||||||
HaveResult bool
|
HaveResult bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var DuidHostnameList []payload
|
|
||||||
|
|
||||||
type payload struct {
|
type payload struct {
|
||||||
Hostname string `json:"hostname"`
|
Hostname string `json:"hostname"`
|
||||||
DUIDs []string `json:"duids"`
|
DUIDs []string `json:"duids"`
|
||||||
@@ -94,6 +98,98 @@ type payloadHelper struct {
|
|||||||
Dhcp6Scope string
|
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) {
|
func octetsRaw(ip string) ([]string, error) {
|
||||||
parts := strings.Split(ip, ".")
|
parts := strings.Split(ip, ".")
|
||||||
if len(parts) != 4 {
|
if len(parts) != 4 {
|
||||||
@@ -118,19 +214,34 @@ func enabled(k string, def bool) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DhcpHelperFunc(xHostname string, xDUIDs []string, xIAIDs []uint32) []payloadHelper {
|
func DhcpHelperFunc(xHostname string, xDUIDs []string, xIAIDs []uint32) []payloadHelper {
|
||||||
/*IPv4*/
|
Ipv4Octets, _ := octetsRaw(defaultIP) // evtl. pageIP nehmen?
|
||||||
Ipv4Octets, _ := octetsRaw(defaultIP)
|
|
||||||
|
|
||||||
rHostname := []rune(xHostname)
|
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])
|
qSegment1 := string(rHostname[2:4])
|
||||||
qSegment2 := string(rHostname[4:])
|
qSegment2 := string(rHostname[4:])
|
||||||
|
|
||||||
qCalculatedIPv4 := Ipv4Octets[0] + "." + Ipv4Octets[1] + "." + qSegment1 + "." + qSegment2
|
qCalculatedIPv4 := Ipv4Octets[0] + "." + Ipv4Octets[1] + "." + qSegment1 + "." + qSegment2
|
||||||
qCalculatedIPv6, _ := embedIPv4(qCalculatedIPv4)
|
qCalculatedIPv6, _ := embedIPv4(qCalculatedIPv4)
|
||||||
var res []payloadHelper
|
var res []payloadHelper
|
||||||
for _, t := range xIAIDs {
|
for _, t := range xIAIDs {
|
||||||
r := payloadHelper{
|
res = append(res, payloadHelper{
|
||||||
Hostname: xHostname,
|
Hostname: xHostname,
|
||||||
DUID: qDUID,
|
DUID: qDUID,
|
||||||
CalculatedIPv4: qCalculatedIPv4,
|
CalculatedIPv4: qCalculatedIPv4,
|
||||||
@@ -140,17 +251,28 @@ func DhcpHelperFunc(xHostname string, xDUIDs []string, xIAIDs []uint32) []payloa
|
|||||||
DomainName: dhcpDomain,
|
DomainName: dhcpDomain,
|
||||||
DhcpServer: dhcpServer,
|
DhcpServer: dhcpServer,
|
||||||
IAID: fmt.Sprintf("%d", t),
|
IAID: fmt.Sprintf("%d", t),
|
||||||
}
|
})
|
||||||
res = append(res, r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func firstOrEmpty(xs []string) string {
|
||||||
|
if len(xs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return xs[0]
|
||||||
|
}
|
||||||
|
|
||||||
func getDhcp() []payloadHelper {
|
func getDhcp() []payloadHelper {
|
||||||
|
duidMu.RLock()
|
||||||
|
defer duidMu.RUnlock()
|
||||||
|
|
||||||
|
cp := make([]payload, len(DuidHostnameList))
|
||||||
|
copy(cp, DuidHostnameList)
|
||||||
|
sortByHostname(cp)
|
||||||
|
|
||||||
var result []payloadHelper
|
var result []payloadHelper
|
||||||
sortByHostname(DuidHostnameList)
|
for _, b := range cp {
|
||||||
for _, b := range DuidHostnameList {
|
|
||||||
result = append(result, DhcpHelperFunc(b.Hostname, b.DUIDs, b.IAIDs)...)
|
result = append(result, DhcpHelperFunc(b.Hostname, b.DUIDs, b.IAIDs)...)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@@ -166,12 +288,20 @@ func register(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "ungültiges JSON", http.StatusBadRequest)
|
http.Error(w, "ungültiges JSON", http.StatusBadRequest)
|
||||||
return
|
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 … ---
|
upsertPayload(p)
|
||||||
log.Printf("neuer Client: %s → DUIDs=%v", p.Hostname, p.DUIDs)
|
saveDuidList() // 👈 direkt nach Update persistieren
|
||||||
DuidHostnameList = append(DuidHostnameList, p)
|
|
||||||
|
|
||||||
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) {
|
func getdhcp6(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -194,6 +324,10 @@ func sortByHostname(p []payload) {
|
|||||||
func main() {
|
func main() {
|
||||||
initConfigAndTemplates()
|
initConfigAndTemplates()
|
||||||
|
|
||||||
|
// Persistenz initialisieren
|
||||||
|
duidFile = getenv("DUID_DB_FILE", "duids.json")
|
||||||
|
loadDuidList()
|
||||||
|
|
||||||
http.HandleFunc("/", handleSingle)
|
http.HandleFunc("/", handleSingle)
|
||||||
http.HandleFunc("/convert", handleSingleConvert)
|
http.HandleFunc("/convert", handleSingleConvert)
|
||||||
http.HandleFunc("/range", handleRange)
|
http.HandleFunc("/range", handleRange)
|
||||||
@@ -466,6 +600,7 @@ var rangePageHTML = `<!DOCTYPE html>
|
|||||||
<table>
|
<table>
|
||||||
<tr><th>IPv4</th><th>IPv6</th><th>DHCP-IPv4</th><th>DHCP-IPv6</th></tr>
|
<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}}
|
{{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>
|
</table>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .Error}}<p style="color:#b00">Fehler: {{.Error}}</p>{{end}}
|
{{if .Error}}<p style="color:#b00">Fehler: {{.Error}}</p>{{end}}
|
||||||
@@ -492,4 +627,5 @@ var rangeDHCP6HTML = `<!DOCTYPE html>
|
|||||||
</table>
|
</table>
|
||||||
</body></html>`
|
</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"
|
//Add-DhcpServerv6Reservation -ComputerName $Srv -Prefix $Prefix -IPAddress IPv6Address -ClientDuid $l.ClientDuid -Iaid $l.Iaid -Name $l.HostName -Description "Auto-reserved after rollout"
|
||||||
|
|||||||
Reference in New Issue
Block a user