{"record":{"id":"a9f2f8fbcec41720","repo":"XTLS/Xray-core","slug":"invalid-xray-tun-fd-tun-device-name-actualname","errorCode":null,"errorMessage":"invalid xray.tun.fd: TUN device name {actualName} does not match configured name {expectedName}","messagePattern":"invalid xray\\.tun\\.fd: TUN device name (.+?) does not match configured name (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/tun/tun_linux.go","lineNumber":105,"sourceCode":"\tifr, err := unix.NewIfreq(\"\")\n\tif err != nil {\n\t\treturn -1, nil, true, err\n\t}\n\tif err = unix.IoctlIfreq(fd, unix.TUNGETIFF, ifr); err != nil {\n\t\treturn -1, nil, true, err\n\t}\n\n\tflags := ifr.Uint16()\n\tif flags&unix.IFF_TUN == 0 {\n\t\treturn -1, nil, true, errors.New(\"invalid \", platform.TunFdKey, \": file descriptor is not a TUN device\")\n\t}\n\tif flags&unix.IFF_NO_PI == 0 {\n\t\treturn -1, nil, true, errors.New(\"invalid \", platform.TunFdKey, \": TUN device must use IFF_NO_PI\")\n\t}\n\n\tactualName := ifr.Name()\n\tif expectedName != \"\" && actualName != expectedName {\n\t\treturn -1, nil, true, errors.New(\"invalid \", platform.TunFdKey, \": TUN device name \", actualName, \" does not match configured name \", expectedName)\n\t}\n\n\ttunLink, err := netlink.LinkByName(actualName)\n\tif err != nil {\n\t\treturn -1, nil, true, err\n\t}\n\n\tif err = unix.SetNonblock(fd, true); err != nil {\n\t\treturn -1, nil, true, err\n\t}\n\n\treturn fd, tunLink, true, nil\n}\n\n// open the file that implements tun interface in the OS\nfunc open(name string) (int, error) {\n\tfd, err := unix.Open(\"/dev/net/tun\", unix.O_RDWR, 0)\n\tif err != nil {","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/tun/tun_linux.go#L87-L123","documentation":"When a TUN device name is configured, Xray compares it against the actual name reported by TUNGETIFF (ifr.Name()). A mismatch means the fd belongs to a different TUN interface than the one named in the inbound config, which would make the subsequent netlink configuration (addresses, routes) target the wrong device.","triggerScenarios":"Configuring name \"tun0\" in the tun inbound while xray.tun.fd refers to tun1; a TUN manager that renames or numbers devices dynamically; reusing a config after the OS assigned a different free tunN name.","commonSituations":"Persisted configs paired with an external fd provider that opens whichever tunN is free; Android VpnService where the interface name is chosen by the framework; typo in the configured name.","solutions":["Align the configured TUN name with the actual interface name (check `ip link` / TUNGETIFF result)","Pin the name when creating the device externally so it is deterministic (TUNSETIFF with the exact name)","Omit the expected name in config if the actual name is not known ahead of time"],"exampleFix":"// before: config names tun0, fd is tun1\n{ \"name\": \"tun0\", ... }  // fd actually belongs to tun1\n\n// after\nip tuntap add mode tun name tun0  # create with fixed name, pass its fd\n{ \"name\": \"tun0\", ... }","handlingStrategy":"validation","validationCode":"// before handing over the fd, read its real name and reconcile with config\nifr, _ := unix.NewIfreq(\"\")\n_ = unix.IoctlIfreq(fd, unix.TUNGETIFF, ifr)\nactual := ifr.Name()\nif cfgName != \"\" && cfgName != actual {\n\tlog.Fatalf(\"config name %q != actual tun name %q\", cfgName, actual)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Create persistent TUN devices with fixed names (ip tuntap add name X)","Derive the configured name from TUNGETIFF instead of hardcoding","Leave the name unset when the environment assigns names dynamically"],"tags":["tun","linux","config","interface-name"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}