TUN — sing-box
sing-box's TUN inbound is the canonical implementation of a Go-land TUN — most other implementations (mihomo's, Clash-Meta, others) are derived from it. The inbound creates a TUN device, parses raw packets in either the system or gVisor stack, and dispatches resulting connections to the routing engine.
Inbound
type: "tun":
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
interface_name | string | (auto) | <interface name> | TUN device name. Empty picks an OS-default (tun0, utun5, etc.). |
netns | string | (unset) | <network namespace tag / name / path> | Linux only. Create the TUN interface inside this network namespace — a tag from the top-level network_namespaces, or a namespace name or path. auto_route / auto_redirect then operate inside the namespace, and no root is needed if the current user owns it. Conflicts with platform. |
mtu | uint32 | 65535 | <bytes> | MTU for the device. When unset: 65535, or 9000 on Android and 4064 inside an Apple Network Extension. |
address | badoption.Listable[netip.Prefix] | [] | <CIDR> | Interface addresses (one IPv4 + one IPv6 typical). The old inet4_address / inet6_address fields are rejected at startup. |
dns_mode | string | hijack | disabled | native | hijack | How DNS is handled on the TUN interface. native sets the platform's interface DNS (systemd-resolved on Linux, per-interface DNS on Windows / Apple); hijack (default) additionally intercepts port-53 traffic — an iproute2 rule on Linux, an nftables DNAT to dns_address with auto_redirect, a WFP filter on Windows with strict_route; disabled does neither. |
dns_address | badoption.Listable[netip.Addr] | (derived from address) | <IP> | DNS server address(es) used by dns_mode. When unset, one per family is derived as the next IP after the first address entry, and connections to it are hijacked into the DNS module automatically (like a hijack-dns rule). Once set, that auto-hijack is off — add an explicit hijack-dns route rule if you still need it. |
auto_route | bool | false | true | false | Auto-install OS routes that direct all traffic to the TUN device. |
iproute2_table_index | int | 2022 | <int> | Linux iproute2 table index used by auto_route. |
iproute2_rule_index | int | 9000 | <int> | Linux iproute2 rule index. |
auto_redirect | bool | false | true | false | Linux NFTables auto-redirect — splice traffic into TUN without changing routes. Faster than auto_route on hot loopback paths. |
auto_redirect_input_mark | FwMark | 0 | <uint32> | fwmark applied to TUN-bound packets. |
auto_redirect_output_mark | FwMark | 0 | <uint32> | fwmark applied to TUN-egress packets. |
auto_redirect_reset_mark | FwMark | 0 | <uint32> | fwmark to remove after processing. |
auto_redirect_nfqueue | uint16 | 0 | <uint16> | NFQUEUE number for the auto-redirect path. |
auto_redirect_iproute2_fallback_rule_index | int | 0 | <int> | Fallback rule index when auto-redirect can't be installed. |
exclude_mptcp | bool | false | true | false | Skip MPTCP flows (let them use the normal kernel path). |
loopback_address | badoption.Listable[netip.Addr] | [] | <IP> | Addresses treated as loopback (don't route through TUN). |
strict_route | bool | false | true | false | Block traffic from leaking around the TUN device (DROP rules on the default-route boundary). |
route_address | badoption.Listable[netip.Prefix] | [] | <CIDR> | When auto_route is on, route only these CIDRs through TUN. Default routes everything. |
route_address_set | badoption.Listable[string] | [] | <rule-set tag> | Route based on rule-set IP-CIDR entries instead of an explicit list. |
route_exclude_address | badoption.Listable[netip.Prefix] | [] | <CIDR> | CIDRs to keep on the default interface (escape hatch). |
route_exclude_address_set | badoption.Listable[string] | [] | <rule-set tag> | Rule-set-driven exclusion. |
include_interface | badoption.Listable[string] | [] | <interface> | Only attach to these interfaces (mobile multi-NIC). |
exclude_interface | badoption.Listable[string] | [] | <interface> | Exclude these interfaces. |
include_uid | badoption.Listable[uint32] | [] | <uid> | Linux/macOS UID inclusion. |
include_uid_range | badoption.Listable[string] | [] | <from:to> | UID range inclusion. |
exclude_uid | badoption.Listable[uint32] | [] | <uid> | UID exclusion. |
exclude_uid_range | badoption.Listable[string] | [] | <from:to> | UID range exclusion. |
include_android_user | badoption.Listable[int] | [] | <user id> | Android multi-user inclusion. |
include_package | badoption.Listable[string] | [] | <package name> | Android package inclusion. |
exclude_package | badoption.Listable[string] | [] | <package name> | Android package exclusion. |
include_mac_address | badoption.Listable[string] | [] | <MAC address> | Linux with auto_route and auto_redirect only. Route only traffic from these source MAC addresses (e.g. LAN clients on a router). Conflicts with exclude_mac_address. |
exclude_mac_address | badoption.Listable[string] | [] | <MAC address> | Linux with auto_route and auto_redirect only. Exclude these source MAC addresses. Conflicts with include_mac_address. |
udp_timeout | UDPTimeoutCompat | 5m | <duration or seconds> | Idle timeout for UDP flows. |
udp_mapping | UDPNATBehavior | endpoint_independent | endpoint_independent | address_dependent | address_and_port_dependent | UDP NAT mapping: reuse one mapping per source address and port for all destinations (default), or a separate mapping per destination address / address and port. |
udp_filtering | UDPNATBehavior | endpoint_independent | endpoint_independent | address_dependent | address_and_port_dependent | UDP NAT filtering: accept packets from any remote endpoint (default), or only from addresses / address-and-port pairs already sent to. |
udp_nat_max | uint32 | 0 (auto) | <uint32> | Maximum number of UDP NAT sessions; the least recently used one is closed when full. 0 means 4096 on iOS, otherwise 4096–16384 based on total memory (16384 if it can't be detected). |
stack | string | (mixed / system) | system | gvisor | mixed | TCP/IP stack implementation. system uses the kernel's network stack; gvisor runs a userspace stack; mixed uses the system stack for TCP and gVisor for UDP. Defaults to mixed when built with gVisor, otherwise system. |
platform | *TunPlatformOptions | (unset) | TunPlatformOptions | Platform-specific overrides (currently just http_proxy). |
Source: option/tun.go:14-72 · pinned at v1.14.2 (af6e64c)
The struct also embeds InboundOptions (sniff, sniff_override_dest, domain_strategy, …), but only to reject them at startup — see Notes.
Stack choice
system— kernel's native TCP/IP. Fastest. Requires the OS to expose the right TUN ioctls (Linux: yes; macOS: utun yes; Windows: wintun via DLL).gvisor— Google's userspace TCP/IP stack. Slower but portable; the only choice on platforms without good kernel TUN support.mixed— system for TCP (kernel-grade throughput on the connection-heavy path), gVisor for UDP. The default in builds with gVisor (otherwisesystemis the default) and what most users want.
Routing models
sing-box's TUN supports two routing approaches:
auto_route: true(cross-platform). Installs OS routes directing all traffic to the TUN. On Linux, uses iproute2 table + rule indices. On macOS, programsroute add. On Windows, programs the routing table directly.auto_redirect: true(Linux only, on top ofauto_route). Adds nftables rules that redirect traffic into sing-box — better routing and performance than plainauto_routeor tproxy, no conflicts with Docker bridge networks, and automatic OpenWrt fw4 integration. Requiresnftand matching kernel modules.
strict_route: true adds DROP rules on the surrounding interface to prevent traffic leaking around the TUN — important for kill-switch semantics.
Examples
Standard desktop setup (Linux/macOS):
{
"inbounds": [{
"type": "tun",
"tag": "tun-in",
"interface_name": "sing-tun",
"mtu": 9000,
"address": ["172.16.0.1/30", "fdfe:dcba:9876::1/126"],
"auto_route": true,
"strict_route": true,
"stack": "mixed"
}],
"route": {
"auto_detect_interface": true,
"rules": [
{ "action": "sniff" },
{ "protocol": "dns", "action": "hijack-dns" },
{ "ip_is_private": true, "outbound": "direct" }
],
"final": "proxy"
}
}Android app filtering — proxy only the listed packages:
{
"inbounds": [{
"type": "tun",
"interface_name": "tun0",
"mtu": 9000,
"address": ["172.16.0.1/30"],
"auto_route": true,
"stack": "system",
"include_package": ["com.netflix.mediaclient", "com.spotify.music"]
}]
}Linux with NFTables auto-redirect (preferred over auto-route on busy hosts):
{
"inbounds": [{
"type": "tun",
"auto_route": true,
"auto_redirect": true,
"auto_redirect_input_mark": "0x100",
"auto_redirect_output_mark": "0x200",
"address": ["172.16.0.1/30"]
}]
}Notes
- The default
dns_mode(hijack) changes system state: sing-box sets the TUN interface's native DNS and installs port-53 hijacking. Set"dns_mode": "disabled"to leave interface DNS and the firewall untouched. - The
inet4_address/inet6_addressfields, theirinet4_*/inet6_*route variants andgsoparse but are rejected at startup; they are hidden from the table above. Useaddress,route_addressandroute_exclude_address. - The inbound fields embedded in this struct (
sniff,sniff_override_dest,domain_strategy, …) are rejected at startup — usesniff/resolveroute rule actions instead. endpoint_independent_natis parsed but ignored. UDP NAT behaviour is set withudp_mapping/udp_filtering(endpoint-independent by default) and capped byudp_nat_max.auto_redirectrequiresauto_route(Linux only) and conflicts withroute.default_markand dial-levelrouting_mark.- With
auto_route,strict_routemakes unsupported networks unreachable; on Windows it also prevents DNS leaks from multihomed resolution. On Linux withauto_redirectit additionally sendsSO_BINDTODEVICEtraffic through sing-box.
Cross-core notes
- Xray-core has a minimal TUN inbound — it can install routes via
autoSystemRoutingTable, but has no DNS hijack and no app filtering. See TUN — Xray-core. - mihomo has a nearly identical feature set under the top-level
tun:block, with kebab-case field names and an extradns-hijacklist. See TUN — mihomo.
Source: option/tun.go:14-72 · v1.14.2 (af6e64c)
