122 lines
3.6 KiB
Go
122 lines
3.6 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func restoreRoutesFromCache() cmdResult {
|
|
return withRoutesOpLock("routes restore", restoreRoutesFromCacheUnlocked)
|
|
}
|
|
|
|
func restoreRoutesFromCacheUnlocked() cmdResult {
|
|
meta, err := loadRoutesClearCacheMeta()
|
|
if err != nil {
|
|
return cmdResult{
|
|
OK: false,
|
|
Message: fmt.Sprintf("routes cache missing: %v", err),
|
|
}
|
|
}
|
|
|
|
ips := readNonEmptyLines(routesCacheIPs)
|
|
dynIPs := readNonEmptyLines(routesCacheDyn)
|
|
routeLines, _ := readLinesFile(routesCacheRT)
|
|
|
|
ensureRoutesTableEntry()
|
|
removeTrafficRulesForTable()
|
|
_, _, _, _ = runCommandTimeout(5*time.Second, "ip", "route", "flush", "table", routesTableName())
|
|
|
|
ignoredRoutes := 0
|
|
for _, ln := range routeLines {
|
|
if err := restoreRouteLine(ln); err != nil {
|
|
if shouldIgnoreRestoreRouteError(ln, err) {
|
|
ignoredRoutes++
|
|
appendTraceLine("routes", fmt.Sprintf("restore route skipped (%q): %v", ln, err))
|
|
continue
|
|
}
|
|
return cmdResult{
|
|
OK: false,
|
|
Message: fmt.Sprintf("restore route failed (%q): %v", ln, err),
|
|
}
|
|
}
|
|
}
|
|
if ignoredRoutes > 0 {
|
|
appendTraceLine("routes", fmt.Sprintf("restore route: skipped non-critical routes=%d", ignoredRoutes))
|
|
}
|
|
|
|
if len(routeLines) == 0 && strings.TrimSpace(meta.Iface) != "" {
|
|
_, _, _, _ = runCommandTimeout(
|
|
5*time.Second,
|
|
"ip", "-4", "route", "replace",
|
|
"default", "dev", meta.Iface,
|
|
"table", routesTableName(),
|
|
"mtu", policyRouteMTU,
|
|
)
|
|
}
|
|
|
|
_, _, _, _ = runCommandTimeout(5*time.Second, "nft", "add", "table", "inet", "agvpn")
|
|
_, _, _, _ = runCommandTimeout(5*time.Second, "nft", "add", "set", "inet", "agvpn", "agvpn4", "{", "type", "ipv4_addr", ";", "flags", "interval", ";", "}")
|
|
_, _, _, _ = runCommandTimeout(5*time.Second, "nft", "add", "set", "inet", "agvpn", "agvpn_dyn4", "{", "type", "ipv4_addr", ";", "flags", "interval", ";", "}")
|
|
_, _, _, _ = runCommandTimeout(5*time.Second, "nft", "flush", "set", "inet", "agvpn", "agvpn4")
|
|
_, _, _, _ = runCommandTimeout(5*time.Second, "nft", "flush", "set", "inet", "agvpn", "agvpn_dyn4")
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
|
|
defer cancel()
|
|
|
|
if len(ips) > 0 {
|
|
if err := nftUpdateSetIPsSmart(ctx, "agvpn4", ips, nil); err != nil {
|
|
return cmdResult{
|
|
OK: false,
|
|
Message: fmt.Sprintf("restore nft cache failed for agvpn4: %v", err),
|
|
}
|
|
}
|
|
}
|
|
if len(dynIPs) > 0 {
|
|
if err := nftUpdateSetIPsSmart(ctx, "agvpn_dyn4", dynIPs, nil); err != nil {
|
|
return cmdResult{
|
|
OK: false,
|
|
Message: fmt.Sprintf("restore nft cache failed for agvpn_dyn4: %v", err),
|
|
}
|
|
}
|
|
}
|
|
|
|
traffic := loadTrafficModeState()
|
|
iface := strings.TrimSpace(meta.Iface)
|
|
if iface == "" {
|
|
iface = detectIfaceFromRoutes(routeLines)
|
|
}
|
|
if iface == "" {
|
|
iface, _ = resolveTrafficIface(traffic.PreferredIface)
|
|
}
|
|
if iface != "" {
|
|
if err := applyTrafficMode(traffic, iface); err != nil {
|
|
return cmdResult{
|
|
OK: false,
|
|
Message: fmt.Sprintf("cache restored, but traffic mode apply failed: %v", err),
|
|
}
|
|
}
|
|
}
|
|
|
|
_ = cacheCopyOrEmpty(routesCacheIPs, stateDir+"/last-ips.txt")
|
|
if fileExists(routesCacheMap) {
|
|
_ = cacheCopyOrEmpty(routesCacheMap, stateDir+"/last-ips-map.txt")
|
|
}
|
|
if fileExists(routesCacheMapD) {
|
|
_ = cacheCopyOrEmpty(routesCacheMapD, lastIPsMapDirect)
|
|
}
|
|
if fileExists(routesCacheMapW) {
|
|
_ = cacheCopyOrEmpty(routesCacheMapW, lastIPsMapDyn)
|
|
}
|
|
_ = writeStatusSnapshot(len(ips)+len(dynIPs), iface)
|
|
|
|
return cmdResult{
|
|
OK: true,
|
|
Message: fmt.Sprintf(
|
|
"routes restored from cache: agvpn4=%d agvpn_dyn4=%d routes=%d iface=%s",
|
|
len(ips), len(dynIPs), len(routeLines), ifaceOrDash(iface),
|
|
),
|
|
}
|
|
}
|