{"record":{"id":"27e01691ef9ee05c","repo":"fatedier/frp","slug":"add-route-to-v-error-v","errorCode":null,"errorMessage":"add route to %v error: %v","messagePattern":"add route to (.+?) error: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/vnet/tun_linux.go","lineNumber":117,"sourceCode":"\t\t\tnumSuffix, err := strconv.Atoi(suffix)\n\t\t\tif err == nil && numSuffix > maxSuffix {\n\t\t\t\tmaxSuffix = numSuffix\n\t\t\t}\n\t\t}\n\t}\n\n\tnextSuffix := maxSuffix + 1\n\tname := fmt.Sprintf(\"%s%d\", basename, nextSuffix)\n\treturn name, nil\n}\n\nfunc addRoutes(ifn *net.Interface, cidr *net.IPNet) error {\n\tr := netlink.Route{\n\t\tDst:       cidr,\n\t\tLinkIndex: ifn.Index,\n\t}\n\tif err := netlink.RouteReplace(&r); err != nil {\n\t\treturn fmt.Errorf(\"add route to %v error: %v\", r.Dst, err)\n\t}\n\treturn nil\n}\n\n// getFallbackTunName generates a deterministic fallback TUN device name\n// based on the base name and the provided address string using a hash.\nfunc getFallbackTunName(baseName, addr string) string {\n\thasher := sha256.New()\n\thasher.Write([]byte(addr))\n\thashBytes := hasher.Sum(nil)\n\t// Use first 4 bytes -> 8 hex chars for brevity, respecting IFNAMSIZ limit.\n\tshortHash := hex.EncodeToString(hashBytes[:4])\n\treturn fmt.Sprintf(\"%s%s\", baseName, shortHash)\n}\n","sourceCodeStart":99,"sourceCodeEnd":132,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/vnet/tun_linux.go#L99-L132","documentation":"Linux vnet route installation: after the TUN device is up, frp uses netlink.RouteReplace to install the configured CIDR route onto that interface; failure is wrapped here. Installing routes requires CAP_NET_ADMIN, so permission problems dominate, but invalid interfaces and netlink socket issues also surface here.","triggerScenarios":"frpc with vnet enabled runs without CAP_NET_ADMIN (container, dropped capabilities, non-root without caps); the interface index is stale; the netlink socket cannot talk to the kernel route table (network namespace restrictions, seccomp filtering netlink).","commonSituations":"Docker/K8s deployment missing --cap-add=NET_ADMIN even though /dev/net/tun works; systemd unit with CapabilityBoundingSet= stripping NET_ADMIN; gVisor/sandboxed runtimes where netlink route modification is unsupported.","solutions":["Grant NET_ADMIN to the frpc process (root, --cap-add=NET_ADMIN, or ambient capabilities in the systemd unit)","Confirm the route CIDR is valid and the TUN device name exists (ip link shows utunX) at failure time","In restrictive runtimes (gVisor, some CI sandboxes), disable vnet or run frpc on the host network namespace","Pair this with the modprobe/dev-tun steps from the TUN creation error — they usually fail together"],"exampleFix":"# before\nsudo -u frpcuser frpc -c frpc.toml  # add route to 10.0.0.0/24 error: permission denied\n\n# after\nsudo setcap cap_net_admin+ep /usr/bin/frpc\nsudo -u frpcuser frpc -c frpc.toml","handlingStrategy":"validation","validationCode":"// verify route-install capability before vnet start on linux\nfunc canManageRoutes() bool {\n    return os.Geteuid() == 0 || hasCapNetAdmin()\n}","typeGuard":null,"tryCatchPattern":"if err := setupVnetRoutes(); err != nil && strings.Contains(err.Error(), \"add route to\") {\n    // missing NET_ADMIN: report and disable vnet rather than retrying\n    log.Printf(\"vnet route install failed (need CAP_NET_ADMIN): %v\", err)\n}","preventionTips":["Grant NET_ADMIN to frpc (container flag, setcap, or systemd unit)","Test in staging with the exact same sandboxing as production","Check for SELinux/AppArmor/seccomp denials when capabilities look correct"],"tags":["vnet","routing","linux","permissions"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}