176 lines
3.8 KiB
Go
176 lines
3.8 KiB
Go
|
|
package handlers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"log"
|
||
|
|
"net/http"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"alpine-router/clients"
|
||
|
|
"alpine-router/config"
|
||
|
|
"alpine-router/dhcp"
|
||
|
|
"alpine-router/nat"
|
||
|
|
)
|
||
|
|
|
||
|
|
func HandleClients(w http.ResponseWriter, r *http.Request) {
|
||
|
|
if r.Method != http.MethodGet {
|
||
|
|
fail(w, http.StatusMethodNotAllowed, "method not allowed")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
list, err := clients.GetAll()
|
||
|
|
if err != nil {
|
||
|
|
fail(w, http.StatusInternalServerError, err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ok(w, list)
|
||
|
|
}
|
||
|
|
|
||
|
|
func HandleClientUpdate(w http.ResponseWriter, r *http.Request) {
|
||
|
|
if r.Method != http.MethodPost {
|
||
|
|
fail(w, http.StatusMethodNotAllowed, "method not allowed")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
mac := strings.TrimPrefix(r.URL.Path, "/api/clients/update/")
|
||
|
|
if mac == "" {
|
||
|
|
fail(w, http.StatusBadRequest, "mac address required")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var req struct {
|
||
|
|
Hostname string `json:"hostname"`
|
||
|
|
Blocked bool `json:"blocked"`
|
||
|
|
StaticIP string `json:"static_ip"`
|
||
|
|
}
|
||
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
|
|
fail(w, http.StatusBadRequest, "invalid json: "+err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := updateClient(mac, req.Hostname, req.Blocked, req.StaticIP); err != nil {
|
||
|
|
fail(w, http.StatusInternalServerError, err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
go applyBlockedFirewall()
|
||
|
|
go applyDHCPStaticBindings()
|
||
|
|
ok(w, map[string]string{"message": "updated"})
|
||
|
|
}
|
||
|
|
|
||
|
|
func updateClient(mac, hostname string, blocked bool, staticIP string) error {
|
||
|
|
cfg, err := config.Load()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
found := false
|
||
|
|
for i := range cfg.KnownDevices {
|
||
|
|
if cfg.KnownDevices[i].MAC == mac {
|
||
|
|
cfg.KnownDevices[i].Blocked = blocked
|
||
|
|
cfg.KnownDevices[i].Hostname = hostname
|
||
|
|
cfg.KnownDevices[i].StaticIP = staticIP
|
||
|
|
found = true
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if !found {
|
||
|
|
cfg.KnownDevices = append(cfg.KnownDevices, config.KnownDevice{
|
||
|
|
MAC: mac,
|
||
|
|
Hostname: hostname,
|
||
|
|
Blocked: blocked,
|
||
|
|
StaticIP: staticIP,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
return config.Save(cfg)
|
||
|
|
}
|
||
|
|
|
||
|
|
func applyBlockedFirewall() {
|
||
|
|
if !nat.IsInstalled() {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
cfg, err := config.Load()
|
||
|
|
if err != nil {
|
||
|
|
log.Printf("Warning: load config for blocked firewall: %v", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var blockedIPs []string
|
||
|
|
for _, kd := range cfg.KnownDevices {
|
||
|
|
if kd.Blocked {
|
||
|
|
ip := kd.IP
|
||
|
|
if kd.StaticIP != "" {
|
||
|
|
ip = kd.StaticIP
|
||
|
|
}
|
||
|
|
if ip != "" {
|
||
|
|
blockedIPs = append(blockedIPs, ip)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
natCfg := &nat.Config{Interfaces: cfg.NAT.Interfaces}
|
||
|
|
if err := nat.ApplyRulesWithBlocked(natCfg, blockedIPs); err != nil {
|
||
|
|
log.Printf("Warning: apply blocked firewall rules: %v", err)
|
||
|
|
} else {
|
||
|
|
log.Printf("Applied firewall rules (%d blocked clients)", len(blockedIPs))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func applyDHCPStaticBindings() {
|
||
|
|
if !dhcp.IsInstalled() {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
cfg, err := config.Load()
|
||
|
|
if err != nil {
|
||
|
|
log.Printf("Warning: load config for DHCP static bindings: %v", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var bindings []dhcp.StaticBinding
|
||
|
|
for _, kd := range cfg.KnownDevices {
|
||
|
|
if kd.StaticIP != "" && kd.MAC != "" {
|
||
|
|
bindings = append(bindings, dhcp.StaticBinding{
|
||
|
|
MAC: kd.MAC,
|
||
|
|
Host: kd.Hostname,
|
||
|
|
IP: kd.StaticIP,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
dhcpCfg := &dhcp.Config{
|
||
|
|
Enabled: cfg.DHCP.Enabled,
|
||
|
|
Pools: make([]dhcp.Pool, len(cfg.DHCP.Pools)),
|
||
|
|
}
|
||
|
|
for i, p := range cfg.DHCP.Pools {
|
||
|
|
dhcpCfg.Pools[i] = dhcp.Pool{
|
||
|
|
Interface: p.Interface,
|
||
|
|
Enabled: p.Enabled,
|
||
|
|
Subnet: p.Subnet,
|
||
|
|
Netmask: p.Netmask,
|
||
|
|
RangeStart: p.RangeStart,
|
||
|
|
RangeEnd: p.RangeEnd,
|
||
|
|
Router: p.Router,
|
||
|
|
DNS: p.DNS,
|
||
|
|
LeaseTime: p.LeaseTime,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := dhcp.WriteConfigsWithBindings(dhcpCfg, bindings); err != nil {
|
||
|
|
log.Printf("Warning: write dnsmasq config with static bindings: %v", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if dhcpCfg.Enabled {
|
||
|
|
if err := dhcp.ServiceRestart(); err != nil {
|
||
|
|
log.Printf("Warning: restart dnsmasq after static binding update: %v", err)
|
||
|
|
} else {
|
||
|
|
log.Printf("dnsmasq restarted with %d static bindings", len(bindings))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|