|
@@ -49,11 +49,124 @@ func NewManager(mm *MetadataManager) *Manager {
|
|
|
prevStats: make(map[string]prevStat),
|
|
prevStats: make(map[string]prevStat),
|
|
|
subscribers: make(map[chan *TrafficSnapshot]bool),
|
|
subscribers: make(map[chan *TrafficSnapshot]bool),
|
|
|
}
|
|
}
|
|
|
|
|
+ mgr.reconcileInterfaceNames()
|
|
|
|
|
+ mgr.recordInterfaceMACs()
|
|
|
mgr.startIPSyncWorker()
|
|
mgr.startIPSyncWorker()
|
|
|
mgr.startTrafficSampler()
|
|
mgr.startTrafficSampler()
|
|
|
return mgr
|
|
return mgr
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// reconcileInterfaceNames ensures kernel interfaces are named according to MAC addresses in metadata.
|
|
|
|
|
+// This is critical for Proxmox LXC containers where interfaces revert to host default names (e.g. eth0, ISP-Router) on boot.
|
|
|
|
|
+func (m *Manager) reconcileInterfaceNames() {
|
|
|
|
|
+ if m.metadata == nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ targetMACs := m.metadata.GetAllMACs()
|
|
|
|
|
+ if len(targetMACs) == 0 {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ links, err := netlink.LinkList()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ log.Printf("[NetSync] Error listing links for name reconciliation: %v", err)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ macToLink := make(map[string]netlink.Link)
|
|
|
|
|
+ nameToLink := make(map[string]netlink.Link)
|
|
|
|
|
+ for _, link := range links {
|
|
|
|
|
+ attrs := link.Attrs()
|
|
|
|
|
+ if attrs == nil {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ nameToLink[attrs.Name] = link
|
|
|
|
|
+ mac := strings.ToLower(attrs.HardwareAddr.String())
|
|
|
|
|
+ if mac != "" && mac != "00:00:00:00:00:00" {
|
|
|
|
|
+ macToLink[mac] = link
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for targetName, mac := range targetMACs {
|
|
|
|
|
+ currentLink, exists := macToLink[mac]
|
|
|
|
|
+ if !exists {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ currentName := currentLink.Attrs().Name
|
|
|
|
|
+ if currentName == targetName {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if conflictingLink, conflict := nameToLink[targetName]; conflict {
|
|
|
|
|
+ conflictMac := strings.ToLower(conflictingLink.Attrs().HardwareAddr.String())
|
|
|
|
|
+ if conflictMac != mac {
|
|
|
|
|
+ tempName := fmt.Sprintf("tmp_%d", time.Now().UnixNano()%10000)
|
|
|
|
|
+ _ = netlink.LinkSetDown(conflictingLink)
|
|
|
|
|
+ if err := netlink.LinkSetName(conflictingLink, tempName); err == nil {
|
|
|
|
|
+ log.Printf("[NetSync] Temporarily renamed conflicting interface %s -> %s", targetName, tempName)
|
|
|
|
|
+ delete(nameToLink, targetName)
|
|
|
|
|
+ nameToLink[tempName] = conflictingLink
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ wasUp := (currentLink.Attrs().Flags & net.FlagUp) != 0
|
|
|
|
|
+ _ = netlink.LinkSetDown(currentLink)
|
|
|
|
|
+ if err := netlink.LinkSetName(currentLink, targetName); err != nil {
|
|
|
|
|
+ log.Printf("[NetSync] Failed to rename %s to %s (MAC %s): %v", currentName, targetName, mac, err)
|
|
|
|
|
+ if wasUp {
|
|
|
|
|
+ _ = netlink.LinkSetUp(currentLink)
|
|
|
|
|
+ }
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.Printf("[NetSync] Successfully reconciled interface %s -> %s (MAC %s)", currentName, targetName, mac)
|
|
|
|
|
+ delete(nameToLink, currentName)
|
|
|
|
|
+ if newLink, err := netlink.LinkByName(targetName); err == nil {
|
|
|
|
|
+ nameToLink[targetName] = newLink
|
|
|
|
|
+ meta := m.metadata.Get(targetName)
|
|
|
|
|
+ if wasUp || meta.AutoUp || len(meta.IPAddresses) > 0 {
|
|
|
|
|
+ _ = netlink.LinkSetUp(newLink)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// recordInterfaceMACs registers MAC addresses for known interfaces in metadata.
|
|
|
|
|
+func (m *Manager) recordInterfaceMACs() {
|
|
|
|
|
+ if m.metadata == nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ links, err := netlink.LinkList()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for _, link := range links {
|
|
|
|
|
+ attrs := link.Attrs()
|
|
|
|
|
+ if attrs == nil {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ name := attrs.Name
|
|
|
|
|
+ mac := strings.ToLower(attrs.HardwareAddr.String())
|
|
|
|
|
+ if mac == "" || mac == "00:00:00:00:00:00" || name == "lo" {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ if isDynamicOrVirtualInterface(name) {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ meta := m.metadata.Get(name)
|
|
|
|
|
+ if meta.Role != "" || len(meta.IPAddresses) > 0 || meta.AutoUp {
|
|
|
|
|
+ if meta.MAC == "" || meta.MAC != mac {
|
|
|
|
|
+ _ = m.metadata.SetMAC(name, mac)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// syncPersistedIPs ensures that all IP addresses saved in metadata are applied to the kernel interfaces
|
|
// syncPersistedIPs ensures that all IP addresses saved in metadata are applied to the kernel interfaces
|
|
|
func (m *Manager) syncPersistedIPs() {
|
|
func (m *Manager) syncPersistedIPs() {
|
|
|
if m.metadata == nil {
|
|
if m.metadata == nil {
|
|
@@ -63,7 +176,14 @@ func (m *Manager) syncPersistedIPs() {
|
|
|
for name, ips := range configured {
|
|
for name, ips := range configured {
|
|
|
link, err := netlink.LinkByName(name)
|
|
link, err := netlink.LinkByName(name)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- continue // Interface might not be attached/created yet by Proxmox host
|
|
|
|
|
|
|
+ meta := m.metadata.Get(name)
|
|
|
|
|
+ if meta.MAC != "" {
|
|
|
|
|
+ m.reconcileInterfaceNames()
|
|
|
|
|
+ link, err = netlink.LinkByName(name)
|
|
|
|
|
+ }
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ continue // Interface might not be attached/created yet by Proxmox host
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Ensure link is UP if it has configured IPs
|
|
// Ensure link is UP if it has configured IPs
|
|
@@ -336,6 +456,11 @@ func (m *Manager) RenameInterface(oldName, newName string) (*InterfaceInfo, erro
|
|
|
return nil, fmt.Errorf("interface %q already exists", newName)
|
|
return nil, fmt.Errorf("interface %q already exists", newName)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ hwAddr := ""
|
|
|
|
|
+ if link.Attrs() != nil && link.Attrs().HardwareAddr != nil {
|
|
|
|
|
+ hwAddr = strings.ToLower(link.Attrs().HardwareAddr.String())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
wasUp := (link.Attrs().Flags & net.FlagUp) != 0
|
|
wasUp := (link.Attrs().Flags & net.FlagUp) != 0
|
|
|
|
|
|
|
|
// Linux kernel requires interface to be DOWN to change dev name
|
|
// Linux kernel requires interface to be DOWN to change dev name
|
|
@@ -361,6 +486,9 @@ func (m *Manager) RenameInterface(oldName, newName string) (*InterfaceInfo, erro
|
|
|
|
|
|
|
|
// Migrate metadata
|
|
// Migrate metadata
|
|
|
_ = m.metadata.Rename(oldName, newName)
|
|
_ = m.metadata.Rename(oldName, newName)
|
|
|
|
|
+ if hwAddr != "" && hwAddr != "00:00:00:00:00:00" {
|
|
|
|
|
+ _ = m.metadata.SetMAC(newName, hwAddr)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Update traffic rate history
|
|
// Update traffic rate history
|
|
|
m.mu.Lock()
|
|
m.mu.Lock()
|