package main import ( "flag" "fmt" "html/template" "log" "net/http" "os" "sync" "time" ) type State struct { mu sync.RWMutex GatewayStatus map[string]bool GatewayIPs map[string]string SubnetCount int LastUpdate int64 ControllerUp bool Subnets []string } var state State const indexHTML = ` SRM Daemon

SRM Daemon

{{range $name, $up := .GatewayStatus}} {{end}}
ParameterValue
Gateway "{{html $name}}" ({{index $.GatewayIPs $name}}) {{if $up}}UP{{else}}DOWN{{end}}
Current Routes{{.SubnetCount}}
Last Update {{if eq .LastUpdate 0}}Never{{else}}{{formatTime .LastUpdate}}{{end}}
Controller {{if .ControllerUp}}UP{{else}}DOWN{{end}}
` func formatTime(ts int64) string { t := time.UnixMilli(ts) return t.Format("2006-01-02 15:04:05 MST") } func startWebServer(port int) { funcMap := template.FuncMap{ "formatTime": formatTime, } tmpl, err := template.New("index").Funcs(funcMap).Parse(indexHTML) if err != nil { log.Fatalf("Failed to parse template: %v", err) } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { state.mu.RLock() defer state.mu.RUnlock() w.Header().Set("Content-Type", "text/html; charset=utf-8") tmpl.Execute(w, &state) }) addr := fmt.Sprintf(":%d", port) log.Printf("Web interface listening on %s", addr) if err := http.ListenAndServe(addr, nil); err != nil { log.Fatalf("Web server failed: %v", err) } } func main() { configPath := flag.String("c", "", "Path to config JSON file") flag.Parse() if *configPath == "" { fmt.Fprintln(os.Stderr, "Usage: srmd -c /path/to/config.json") os.Exit(1) } cfg, err := LoadConfig(*configPath) if err != nil { log.Fatalf("Failed to load config: %v", err) } state.GatewayStatus = make(map[string]bool) state.GatewayIPs = make(map[string]string) for _, gw := range cfg.Gateway { state.GatewayStatus[gw.Name] = false state.GatewayIPs[gw.Name] = gw.Address } state.Subnets = []string{} state.LastUpdate = 0 go startWebServer(cfg.Port) var lastUpdate int64 var subnets []string gatewayStates := make(map[string]bool) for _, gw := range cfg.Gateway { gatewayStates[gw.Name] = false } for { log.Println("--- Step 2: Checking gateways ---") prevStates := make(map[string]bool) for k, v := range gatewayStates { prevStates[k] = v } newStates := make(map[string]bool) for _, gw := range cfg.Gateway { up := Ping(gw.Address, cfg.Timeout) newStates[gw.Name] = up log.Printf("Gateway %q (%s): %v", gw.Name, gw.Address, up) } changed := false for _, gw := range cfg.Gateway { if prevStates[gw.Name] != newStates[gw.Name] { changed = true break } } gatewayStates = newStates state.mu.Lock() for k, v := range gatewayStates { state.GatewayStatus[k] = v } state.mu.Unlock() if changed { log.Println("Gateway states changed, updating routes (step 3)") DoRouteUpdate(cfg, gatewayStates, subnets) } else { log.Println("Gateway states unchanged, skipping route update") } log.Println("--- Step 4: Getting update from controller ---") ts, err := GetUpdate(cfg.Controller, cfg.Timeout) if err != nil { log.Printf("Controller update failed: %v", err) state.mu.Lock() state.ControllerUp = false state.mu.Unlock() time.Sleep(time.Duration(cfg.Interval) * time.Second) continue } state.mu.Lock() state.ControllerUp = true state.mu.Unlock() log.Printf("Controller timestamp: %d (lastUpdate: %d)", ts, lastUpdate) if ts == lastUpdate { log.Println("Timestamp unchanged, waiting and retrying") time.Sleep(time.Duration(cfg.Interval) * time.Second) continue } newSubnets, err := GetSubnets(cfg.Controller, cfg.Timeout) if err != nil { log.Printf("Failed to get subnets: %v", err) state.mu.Lock() state.ControllerUp = false state.mu.Unlock() time.Sleep(time.Duration(cfg.Interval) * time.Second) continue } state.mu.Lock() state.ControllerUp = true state.mu.Unlock() subnetsChanged := !stringSliceEqual(subnets, newSubnets) subnets = newSubnets state.mu.Lock() state.Subnets = make([]string, len(subnets)) copy(state.Subnets, subnets) state.SubnetCount = len(subnets) state.mu.Unlock() if subnetsChanged { log.Printf("Subnets changed (%d subnets), updating lastUpdate", len(subnets)) lastUpdate = ts state.mu.Lock() state.LastUpdate = ts state.mu.Unlock() DoRouteUpdate(cfg, gatewayStates, subnets) } else { log.Println("Subnets unchanged, waiting") time.Sleep(time.Duration(cfg.Interval) * time.Second) } } } func stringSliceEqual(a, b []string) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true }