TUN
Creates a TUN interface; traffic sent to this interface will be processed by Xray. Currently, Windows, Linux, macOS, and FreeBSD are supported.
On Android and iOS, the TUN FD must be passed in from an external app via the XRAY_TUN_FD environment variable, which redirects traffic through the interface designated by the system. It cannot run standalone and only serves as a way for an app to feed traffic into Xray.
On Linux, this environment variable can optionally be used to pass in the TUN FD for certain lightweight or unprivileged implementations.
InboundConfigurationObject
InboundConfigurationObject corresponds to the settings item in InboundObject.
{
"inbounds": [
{
// ...
"protocol": "tun",
"settings": {
"name": "utun10",
"desc": "Wintun",
"mtu": 1500,
"gateway": ["10.0.0.1/16", "fc00::1/64"],
"dns": ["1.1.1.1", "8.8.8.8"],
"userLevel": 0,
"autoSystemRoutingTable": ["0.0.0.0/0", "::/0"],
"autoOutboundsInterface": "auto",
"autoSystemDnsToGateway": false,
"autoSystemWfpBlockLeak": ["dns", "misconfigtun"]
}
}
]
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
name: string
The name of the created TUN interface. Default is "utunN".
Where N is a random number between 10 and 1024.
On macOS, the name has to be utunN, and on FreeBSD tunN. On Windows, an existing Wintun adapter with that name is reused.
desc: string
The description of the network interface name on Windows. Default is Wintun. This string is concatenated with "Tunnel" to form "xxx Tunnel".
On Windows, you can use route print to view the details.
mtu: number
The MTU of the interface. The default is 1500.
gateway: [string]
The list of address prefixes assigned to the TUN interface, usually one for IPv4 and one for IPv6, such as "10.0.0.1/16" and "fc00::1/64".
If it is not set, the result depends on the system: on Linux, Xray assigns no address; on Windows, the system gives the TUN interface link-local addresses itself (IPv6 at once, IPv4 from 169.254.0.0/16 after a few seconds); on macOS and FreeBSD, 169.254.10.1/30 is used. macOS and FreeBSD only use the first IPv4 prefix.
On Windows with IPv6 turned off by DisabledComponents (0x10 set, such as 0xFF), errors setting up the IPv6 entries here, in dns and in autoSystemRoutingTable, or clearing the IPv6 DNS servers when dns has none, are only logged. Otherwise, they keep the TUN interface from starting.
dns: [string]
The list of DNS servers assigned to the TUN interface, such as "1.1.1.1" and "8.8.8.8".
This option only takes effect on Windows, where the servers are set on the TUN interface. Windows sends name queries to them as well as to the DNS servers of the other interfaces; "dns" in autoSystemWfpBlockLeak blocks the latter. Queries to these servers only go through the TUN interface when the servers are covered by gateway or autoSystemRoutingTable. In Xray they are ordinary traffic to port 53: a routing rule can hand them to a DNS outbound, otherwise they are forwarded to the servers like other traffic. The addresses of the TUN interface are not registered in DNS, and the DNS cache is flushed when the TUN interface starts and stops.
When autoOutboundsInterface is in use, the localhost DNS server of Xray skips these servers (unless another interface uses them too), as its queries to them would lead back into the TUN interface.
On Linux and FreeBSD, the system DNS is not taken from this option; see autoSystemDnsToGateway. On macOS, the system DNS is not configured.
userLevel: number
User level. The connection will use the local policy corresponding to this user level.
The value of userLevel corresponds to the level value in policy. If not specified, the default is 0.
autoSystemRoutingTable: [string]
The list of destination prefixes that Xray should add to the system routing table automatically so that the traffic is directed into this TUN interface. Each item is a CIDR. For example, "0.0.0.0/0" means all IPv4 traffic, and "::/0" means all IPv6 traffic.
Currently supported on Windows, Linux, macOS and FreeBSD.
autoOutboundsInterface: string
Automatically binds Xray outbounds to a physical network interface, so that traffic generated by Xray itself is not sent back into the TUN interface and looped.
If a connection cannot be bound to that interface, it fails instead of leaving unbound, where it could loop back into the TUN interface. On Windows, an interface with only IPv4 or only IPv6 works as well: connections of the missing IP version fail, rather than leaving through another interface.
Connections to loopback addresses are not bound. On FreeBSD, Xray's connections are switched to a second routing table (FIB 1) that mirrors the physical default route instead, which needs the boot tunable net.fibs set to at least 2, otherwise Xray does not start.
On Windows, this binding only holds while that interface has weak host send and forwarding off for the IP versions routed to the TUN interface; otherwise Windows sends Xray's own connections into the TUN interface, where they stall. Xray therefore turns weak host send off on that interface while the TUN interface runs, and on again when it stops. Forwarding, which Mobile Hotspot and Internet Connection Sharing need, is left alone, and a warning is logged instead: have the hotspot share the TUN interface (Settings → Network & internet → Mobile hotspot → Share my internet connection from), which also sends the hotspot's devices through Xray. The TUN interface only appears in that list while Xray is running.
Equivalent to automatically setting sockopt.interface for all outbounds (it also additionally covers some requests that cannot have outbound settings configured, such as the various local modes of the built-in DNS), which can be overridden by manually set sockopt.
The default value is null, which means not configured. You can specify an interface name explicitly, or use "auto" to let Xray choose one automatically. If autoSystemRoutingTable is configured but this field is omitted, Xray treats it as "auto".
autoSystemDnsToGateway: true | false
This option only takes effect on Linux and FreeBSD. The default is false.
When it is enabled, Xray points the system resolver at the TUN interface, so that the name lookups of the system go through Xray: on Linux, systemd-resolved (through resolvectl) at the first IPv4 address in gateway plus one (e.g. 10.0.0.1/16 → 10.0.0.2), or without an IPv4 one, the first IPv6 address plus one (e.g. fc00::1/64 → fc00::2); on FreeBSD, through resolvconf(8), at the first IPv4 address in gateway itself (e.g. 10.0.0.1/16 → 10.0.0.1), as the TUN interface has the next one there. Without gateway, Xray reports an error and does not start. It is not taken from dns, as the system would then query those servers directly, outside the TUN interface.
Queries to that address have to be answered by Xray, so a routing rule has to send port 53 of this inbound to a DNS outbound. Xray checks this before changing the system DNS, and does not start if such a query would not reach a DNS outbound, or if Xray's own DNS could resolve through the system resolver (no name servers, or a localhost one), which would loop.
On Linux, it needs systemd-resolved 240 or later to be running. On FreeBSD, resolvconf has to write the resolver files (no resolvconf=NO, nor inclusive_interfaces covering the TUN interface, in resolvconf.conf); with local_unbound, unbound's DNSSEC validation has to be turned off, as the extra queries of validation get no answer through the DNS outbound. Whenever the system DNS cannot be changed, Xray does not start. The setting is reverted when Xray exits normally; if Xray is killed (e.g. with SIGKILL), run resolvectl revert <interface> on Linux or resolvconf -d <interface> on FreeBSD to clean up.
autoSystemWfpBlockLeak: [string]
It is recommended to set at least ["dns", "misconfigtun"], to block DNS leaks and the traffic of an IP version without routes.
This option only takes effect on Windows, and needs autoSystemRoutingTable, otherwise Xray reports an error and does not start. The default is empty, i.e. no filters. Xray adds Windows Filtering Platform (WFP) filters that let the traffic of other programs leave only through the TUN interface, each kind chosen by a value in the list:
"dns": stands for"53","853"(DNS over TLS and QUIC) and"windnsclient". Windows sends name queries to the DNS servers of all interfaces, each through its own interface whatever the routes say, and other programs reach a DNS server on the local network (e.g.192.168.1.1handed out by DHCP) through its more specific route, so without this, such servers would still be queried outside the TUN interface."windnsclient": the Windows DNS Client service (Dnscache), except for name resolution on the local network (LLMNR, mDNS). On Windows 11, Windows Server 2022 and later, it can send name queries over DNS over HTTPS or TLS, to any port; earlier versions only query port 53, so there this does nothing."tcp","udp": that protocol, to all ports, or with/<port>or/<low>-<high>(1 to 65535) to some, e.g."tcp/853"or"udp/10-20". The ports alone stand for both, e.g."53"or"10-20"."icmp": ICMP and ICMPv6, e.g.ping."misconfigtun": an IP version (IPv4 or IPv6) without routes inautoSystemRoutingTable, whose traffic would bypass the TUN interface. With IPv6 turned off byDisabledComponents, it covers IPv6 whatever the routes, as IPv6 setup errors are then only logged, and IPv6 still works until the reboot that applies that setting."all": everything, in both directions. Access to the local network then breaks unless it is routed into the TUN interface as well, and so do incoming connections to other programs, other VPNs, and virtual machines whose NAT runs as a program on the host."allexceptlan": like"all", but traffic to and from the local network stays allowed, e.g. for printers or casting to a TV: private, link-local, multicast and broadcast addresses (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,224.0.0.0/4,255.255.255.255,fc00::/7,fe80::/10,ff00::/8), not global IPv6 ones. It includes"dns", and the other values apply there as well."all"overrides it.
A CIDR in front limits a value to its addresses, an IPv6 one in brackets, e.g. "8.8.8.8/32:all", "1.1.1.0/24:53" or "[::/0]:all" (IPv6 only). It limits what the value stands for or includes as well: "0.0.0.0/0:allexceptlan", for instance, leaves DNS over IPv6 open.
Loopback, DHCP, DHCPv6 and IPv6 neighbor discovery always stay allowed. While the filters are in place, Xray's own outgoing connections also get past the block rules of Windows Firewall. If the filters cannot be added, Xray does not start; they are removed when Xray exits, even if it crashes.
Keeping the system's DNS inside the TUN interface, with "all", "allexceptlan", "dns", "windnsclient", or anything covering TCP or UDP port 53, even to some addresses only, needs dns, otherwise Xray reports an error and does not start. The servers in dns then have to be covered by gateway or autoSystemRoutingTable, or be loopback addresses like 127.0.0.1, and DNS servers that should be reached directly belong in Xray's own DNS settings.
The filters break some setups: with "dns", a local DNS resolver that sends its own queries through another interface, the DNS of another VPN, virtual machines whose NAT resolves names on the host, or signing in to a captive portal; with "misconfigtun", IPv4 or IPv6 on the local network while no route of that version leads to the TUN interface.
Usage Tips
If autoSystemRoutingTable is not configured, you still need to add routes manually to direct traffic to the created TUN interface; otherwise, it remains just an interface. For more fine-grained policy routing, you still need to configure the OS manually as well.
Even with routes, the system's DNS may still leak outside the TUN interface: on Windows, set dns and at least ["dns"] in autoSystemWfpBlockLeak; on Linux and FreeBSD, use autoSystemDnsToGateway; on macOS, configure the system DNS yourself, e.g. with a server that is routed into the TUN interface.
Replies to ping (ICMP Echo) are generated by Xray itself, so they do not show whether the destination is reachable.
If you only want to proxy specific process(es), the process name routing in the Xray routing system will be very useful.
WARNING
Be aware of potential traffic loop issues. After setting routes, requests initiated by Xray might be sent back to Xray, causing a loop! Prefer autoOutboundsInterface to avoid this problem. If you need manual control, you can still use interface in sockopt to bind to the actual physical network interface. ipconfig (Windows) or ip a (Linux) will help you find the interface name you need. Alternatively, use the outbound sendThrough setting. It is available directly in OutboundObject without the deep nesting level of sockOpt.interface. Here you need to use the IP address on the network card, such as 192.168.1.2 (As you can see, its disadvantage is that it cannot automatically support dual-stack; please choose according to the IP actually used for your outbound connection).