Skip to content

TUN — mihomo ​

mihomo's tun: is a top-level config block (not a listeners: entry). The schema is nearly identical to sing-box's TUN inbound (mihomo's implementation is derived from sing-box's) with a few mihomo-specific additions: explicit dns-hijack, ICMP-drop toggle, and Darwin recvmsg_x/sendmsg_x optimization flags.

Top-level tun: block ​

FieldTypeDefaultAllowed valuesDescription
enableboolfalsetrue | falseMaster switch. When false the rest of the block is ignored.
devicestring(auto)<interface name>TUN device name.
stackC.TUNStackgvisorsystem | gvisor | mixed | mipsTCP/IP stack. The sing-box values plus mips (mihomo's own userspace stack).
dns-hijack[]string[][<address>]Addresses that should be hijacked as DNS — outgoing UDP to these endpoints is redirected to the internal DNS engine. Standard value: [any:53] (catch all).
auto-routeboolfalsetrue | falseAuto-install OS routes.
auto-detect-interfaceboolfalsetrue | falseAuto-discover the default outbound interface — used for excluding TUN-bound traffic from re-entering TUN.
mtuuint320 (auto)<bytes>Device MTU. 0 picks a sensible default (9000 on most platforms).
gsoboolfalsetrue | falseGeneric Segmentation Offload (Linux). Improves throughput when the kernel supports it.
gso-max-sizeuint3265536<bytes>Maximum segment size when GSO is active.
inet6-address[]netip.Prefix[]<CIDR>IPv6 addresses to assign to the device. IPv4 uses the top-level inet4-address field (currently commented in source — see config/config.go:283).
iproute2-table-indexint2022<int>Linux iproute2 table index.
iproute2-rule-indexint9000<int>Linux iproute2 rule index.
auto-redirectboolfalsetrue | falseLinux NFTables auto-redirect (faster TCP path). Requires auto-route: true — startup fails otherwise.
auto-redirect-input-markuint320<uint32>fwmark on TUN-bound packets.
auto-redirect-output-markuint320<uint32>fwmark on TUN-egress packets.
auto-redirect-iproute2-fallback-rule-indexint0<int>Fallback rule index when auto-redirect can't install.
loopback-address[]netip.Addr[]<IP>Addresses treated as loopback.
strict-routeboolfalsetrue | falseBlock traffic that would otherwise bypass TUN (kill-switch semantics).
route-address[]netip.Prefix[]<CIDR>Route only these CIDRs through TUN when auto-route is on.
route-address-set[]string[]<rule-provider name>Rule-provider-driven inclusion.
route-exclude-address[]netip.Prefix[]<CIDR>CIDRs to keep on the default interface.
route-exclude-address-set[]string[]<rule-provider name>Rule-provider-driven exclusion.
include-interface[]string[]<interface>Only attach to these interfaces.
exclude-interface[]string[]<interface>Exclude these interfaces.
include-uid[]uint32[]<uid>UID inclusion.
include-uid-range[]string[]<from:to>UID range inclusion.
exclude-uid[]uint32[]<uid>UID exclusion.
exclude-uid-range[]string[]<from:to>UID range exclusion.
exclude-src-port[]uint16[]<port>Source-port exclusion.
exclude-src-port-range[]string[]<from:to>Source-port range exclusion.
exclude-dst-port[]uint16[]<port>Destination-port exclusion.
exclude-dst-port-range[]string[]<from:to>Destination-port range exclusion.
include-android-user[]int[]<user id>Android multi-user inclusion.
include-package[]string[]<package name>Android package inclusion.
exclude-package[]string[]<package name>Android package exclusion.
include-mac-address[]string[]<MAC>MAC-based inclusion.
exclude-mac-address[]string[]<MAC>MAC-based exclusion.
endpoint-independent-natboolfalsetrue | falseEnable endpoint-independent NAT for UDP. Required by some games / VoIP apps.
udp-timeoutint64300<seconds>UDP idle timeout in seconds.
icmp-timeoutint640 (10s built-in)<seconds>Idle timeout for forwarded ICMP sessions, in seconds. 0 keeps the built-in 10-second default.
disable-icmp-forwardingboolfalsetrue | falseDrop ICMP packets at the TUN layer (mihomo-specific).
file-descriptorint0<fd>Use a pre-opened file descriptor instead of creating a TUN device. Useful for VPN-service integration where the OS hands you the fd.
inet4-route-address[]netip.Prefix——
inet6-route-address[]netip.Prefix——
inet4-route-exclude-address[]netip.Prefix——
inet6-route-exclude-address[]netip.Prefix——
recvmsgxboolfalsetrue | falseDarwin only — use the recvmsg_x syscall for batching.
sendmsgxboolfalsetrue | falseDarwin only — use sendmsg_x.

Source: config/config.go:273-330 · pinned at v1.19.31 (ab405ba)

Stack choice ​

Same as sing-box, plus mihomo's own mips stack:

  • system — kernel TCP/IP. Fastest, requires kernel TUN support.
  • gvisor — userspace stack. Slower, portable. Default.
  • mixed — system stack for TCP, gVisor for UDP.
  • mips — mihomo's own userspace IP stack (MIPS = mihomo IP stack, not the CPU architecture).

DNS hijack ​

dns-hijack is mihomo's idiomatic way to route DNS queries through the internal resolver:

yaml
tun:
  enable: true
  dns-hijack:
    - any:53          # All UDP/53 traffic
    - tcp://any:53    # And TCP/53
    - 8.8.8.8:53      # Specific server only

This is implemented at the TUN layer — DNS packets are intercepted before they reach the routing rules. It's faster than running a UDP inbound on 53 and routing back through the rules.

Examples ​

Desktop transparent proxy:

yaml
tun:
  enable: true
  device: mihomo-tun
  stack: mixed
  mtu: 9000
  dns-hijack:
    - any:53
    - tcp://any:53
  auto-route: true
  auto-detect-interface: true
  strict-route: true
  route-address:
    - 0.0.0.0/1
    - 128.0.0.0/1
    - ::/1
    - 8000::/1
  route-exclude-address:
    - 192.168.0.0/16
    - 10.0.0.0/8

Linux NFTables auto-redirect:

yaml
tun:
  enable: true
  device: utun0
  stack: system
  dns-hijack: [any:53]
  auto-route: true
  auto-redirect: true
  auto-redirect-input-mark: 0x100
  auto-redirect-output-mark: 0x200

Android app split — proxy only specific apps:

yaml
tun:
  enable: true
  stack: system
  mtu: 9000
  dns-hijack: [any:53]
  auto-route: true
  include-package:
    - com.netflix.mediaclient
    - com.spotify.music
  exclude-package:
    - com.google.android.gms

Notes ​

  • inet4-address is commented out in the source (config/config.go:283) — mihomo currently uses a hard-coded default IPv4 range (198.18.0.1/30). IPv6 still uses the explicit inet6-address field.
  • dns-hijack accepts a couple of special forms:
    • any:53 matches both TCP and UDP port 53.
    • Prefix with tcp:// or udp:// to scope.
    • Bare IP:port for specific upstream resolvers.
  • auto-redirect builds on auto-route rather than replacing it: enabling it without auto-route: true fails at startup (listener/sing_tun/server.go:428-430). On NFTables-capable kernels, add it on top of auto-route for faster TCP redirection.
  • The recvmsgx / sendmsgx flags are macOS-only optimizations that use the per-syscall batching variants. Enable them on macOS for throughput.
  • disable-icmp-forwarding is mihomo-specific — useful when you have a separate ICMP path (e.g. a routing-table-based pinger) and don't want TUN to capture pings.

Cross-core notes ​

  • Xray-core has a minimal TUN inbound. See TUN — Xray-core.
  • sing-box has the same feature set under type: "tun" listed in inbounds[] with snake_case names. See TUN — sing-box.

Source: config/config.go:273-330 · v1.19.31 (ab405ba)

Core Tutorial by Argsment