package app import ( "fmt" "strings" "time" ) type transportIfaceBinding struct { IfaceID string Mode TransportInterfaceMode RuntimeIface string NetnsName string RoutingTable string } func syncTransportInterfacesWithClientsLocked(clients []TransportClient) (transportInterfacesState, error) { ifaces := loadTransportInterfacesState() norm, changed := normalizeTransportInterfacesState(ifaces, clients) if changed { if err := saveTransportInterfacesState(norm); err != nil { appendTraceLineRateLimited( "transport", fmt.Sprintf("interfaces sync warning: save failed: %v", err), 20*time.Second, ) } } return norm, nil } func findTransportInterfaceByID(items []TransportInterface, ifaceID string) (TransportInterface, bool) { id := normalizeTransportIfaceID(ifaceID) for _, it := range items { if normalizeTransportIfaceID(it.ID) == id { return it, true } } return TransportInterface{}, false } func resolveTransportIfaceBinding(client TransportClient, ifaces transportInterfacesState) transportIfaceBinding { ifaceID := normalizeTransportIfaceID(client.IfaceID) binding := transportIfaceBinding{ IfaceID: ifaceID, Mode: normalizeTransportInterfaceMode("", ifaceID), } if iface, ok := findTransportInterfaceByID(ifaces.Items, ifaceID); ok { binding.Mode = normalizeTransportInterfaceMode(iface.Mode, ifaceID) binding.RuntimeIface = strings.TrimSpace(iface.RuntimeIface) if strings.TrimSpace(iface.NetnsName) != "" { binding.NetnsName = normalizeTransportNetnsName(strings.TrimSpace(iface.NetnsName), client.ID) } if hint := resolveTransportInterfaceRoutingTable(iface, ifaceID); strings.TrimSpace(hint) != "" { binding.RoutingTable = hint } } if strings.TrimSpace(binding.RuntimeIface) == "" { binding.RuntimeIface = strings.TrimSpace(client.Iface) } if strings.TrimSpace(binding.RoutingTable) == "" { if binding.IfaceID != transportDefaultIfaceID { binding.RoutingTable = transportRoutingTableForIfaceID(binding.IfaceID) } else { binding.RoutingTable = normalizeTransportRoutingTable(client.RoutingTable, transportRoutingTableForID(client.ID)) } } if transportNetnsEnabled(client) { explicit := strings.TrimSpace(transportConfigString(client.Config, "netns_name")) if explicit != "" { binding.NetnsName = transportNetnsName(client) } else if strings.TrimSpace(binding.NetnsName) == "" { if binding.IfaceID != transportDefaultIfaceID { binding.NetnsName = normalizeTransportNetnsName("svpn-"+binding.IfaceID, client.ID) } else { binding.NetnsName = normalizeTransportNetnsName("svpn-"+sanitizeID(client.ID), client.ID) } } } return binding } func applyTransportIfaceBinding(client TransportClient, ifaces transportInterfacesState, now time.Time) (TransportClient, bool) { updated := client changed := false binding := resolveTransportIfaceBinding(client, ifaces) if updated.IfaceID != binding.IfaceID { updated.IfaceID = binding.IfaceID changed = true } if strings.TrimSpace(updated.Iface) == "" && strings.TrimSpace(binding.RuntimeIface) != "" { updated.Iface = binding.RuntimeIface changed = true } if strings.TrimSpace(binding.RoutingTable) != "" && strings.TrimSpace(updated.RoutingTable) != strings.TrimSpace(binding.RoutingTable) { updated.RoutingTable = strings.TrimSpace(binding.RoutingTable) changed = true } if transportNetnsEnabled(updated) && strings.TrimSpace(transportConfigString(updated.Config, "netns_name")) == "" && strings.TrimSpace(binding.NetnsName) != "" { cfg := cloneMap(updated.Config) if cfg == nil { cfg = map[string]any{} } cfg["netns_name"] = binding.NetnsName updated.Config = cfg changed = true } if changed { updated.UpdatedAt = now.Format(time.RFC3339) } return updated, changed }