14.04.2026 Update
This commit is contained in:
@@ -8,8 +8,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"alpine-router/config"
|
||||
"alpine-router/traffic"
|
||||
"nano-router/config"
|
||||
"nano-router/traffic"
|
||||
)
|
||||
|
||||
const LeasesFile = "/var/lib/misc/dnsmasq.leases"
|
||||
@@ -28,6 +28,7 @@ type Client struct {
|
||||
Known bool `json:"known"`
|
||||
Blocked bool `json:"blocked"`
|
||||
StaticIP string `json:"static_ip"`
|
||||
Policy string `json:"policy"` // "disabled" | "direct" | "vpn" | "" (use default)
|
||||
}
|
||||
|
||||
func GetAll() ([]Client, error) {
|
||||
@@ -78,30 +79,31 @@ func GetAll() ([]Client, error) {
|
||||
found := false
|
||||
|
||||
for ip, c := range byIP {
|
||||
if kd.MAC != "" && c.MAC == kd.MAC {
|
||||
c.Blocked = kd.Blocked
|
||||
matchedMAC := kd.MAC != "" && c.MAC == kd.MAC
|
||||
matchedIP := kd.IP != "" && ip == kd.IP && (kd.MAC == "" || c.MAC == kd.MAC)
|
||||
if !matchedMAC && !matchedIP {
|
||||
continue
|
||||
}
|
||||
|
||||
// Policy, blocked state, and hostname apply to every IP this MAC
|
||||
// has on the network (device connected to multiple interfaces/VLANs).
|
||||
c.Blocked = kd.Blocked
|
||||
c.Policy = kd.Policy
|
||||
if kd.Hostname != "" {
|
||||
c.Hostname = kd.Hostname
|
||||
}
|
||||
|
||||
// Static IP binding (DHCP reservation) and IP override only apply
|
||||
// to the canonical/primary entry for this device.
|
||||
if matchedIP || ip == kd.StaticIP || (!found && matchedMAC) {
|
||||
c.StaticIP = kd.StaticIP
|
||||
if kd.Hostname != "" {
|
||||
c.Hostname = kd.Hostname
|
||||
}
|
||||
if kd.StaticIP != "" {
|
||||
c.IP = kd.StaticIP
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
if kd.IP != "" && ip == kd.IP && (kd.MAC == "" || c.MAC == kd.MAC) {
|
||||
c.Blocked = kd.Blocked
|
||||
c.StaticIP = kd.StaticIP
|
||||
if kd.Hostname != "" {
|
||||
c.Hostname = kd.Hostname
|
||||
}
|
||||
if kd.StaticIP != "" {
|
||||
c.IP = kd.StaticIP
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
found = true
|
||||
// No break — keep iterating so all IPs for this MAC are updated.
|
||||
}
|
||||
|
||||
if !found && key != "" {
|
||||
@@ -120,6 +122,7 @@ func GetAll() ([]Client, error) {
|
||||
Known: true,
|
||||
Blocked: kd.Blocked,
|
||||
StaticIP: kd.StaticIP,
|
||||
Policy: kd.Policy,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,8 +153,22 @@ func GetAll() ([]Client, error) {
|
||||
|
||||
go syncKnownDevices(byIP)
|
||||
|
||||
// Exclude upstream gateways — they appear in the ARP table but are not
|
||||
// LAN clients. Build the exclusion set from configured interface gateways.
|
||||
gatewayIPs := make(map[string]bool)
|
||||
if cfgErr == nil && cfg != nil {
|
||||
for _, iface := range cfg.Interfaces {
|
||||
if iface.Gateway != "" {
|
||||
gatewayIPs[iface.Gateway] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]Client, 0, len(byIP))
|
||||
for _, c := range byIP {
|
||||
if gatewayIPs[c.IP] {
|
||||
continue
|
||||
}
|
||||
result = append(result, *c)
|
||||
}
|
||||
|
||||
@@ -177,6 +194,7 @@ func syncKnownDevices(byIP map[string]*Client) {
|
||||
savedHostnames := make(map[string]string)
|
||||
savedBlocked := make(map[string]bool)
|
||||
savedStaticIPs := make(map[string]string)
|
||||
savedPolicies := make(map[string]string)
|
||||
for _, kd := range cfg.KnownDevices {
|
||||
key := kd.MAC
|
||||
if key == "" {
|
||||
@@ -189,6 +207,9 @@ func syncKnownDevices(byIP map[string]*Client) {
|
||||
if kd.StaticIP != "" {
|
||||
savedStaticIPs[key] = kd.StaticIP
|
||||
}
|
||||
if kd.Policy != "" {
|
||||
savedPolicies[key] = kd.Policy
|
||||
}
|
||||
}
|
||||
|
||||
var seen []config.KnownDevice
|
||||
@@ -210,12 +231,32 @@ func syncKnownDevices(byIP map[string]*Client) {
|
||||
if sip, ok := savedStaticIPs[key]; ok {
|
||||
kd.StaticIP = sip
|
||||
}
|
||||
if pol, ok := savedPolicies[key]; ok {
|
||||
kd.Policy = pol
|
||||
}
|
||||
seen = append(seen, kd)
|
||||
}
|
||||
}
|
||||
_ = config.UpdateKnownDevices(seen)
|
||||
}
|
||||
|
||||
// GetARPIPsByMAC returns a map of MAC address → all IPs currently seen in the
|
||||
// ARP table for that MAC. Used by the firewall to apply per-device policies to
|
||||
// every IP a device has (e.g. multi-interface or dual-stack devices).
|
||||
func GetARPIPsByMAC() map[string][]string {
|
||||
arp, err := parseARPTable()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
result := make(map[string][]string)
|
||||
for ip, c := range arp {
|
||||
if c.MAC != "" {
|
||||
result[c.MAC] = append(result[c.MAC], ip)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func parseDNSMasqLeases() (map[string]*Client, error) {
|
||||
f, err := os.Open(LeasesFile)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user