Mihomo TUN Mode Breaks Networking: Ip_forward, UFW, and Auto-Redirect

Contents

When using mihomo in TUN mode (stack: system, auto-route: true), enabling it causes a complete network outage — SSH drops too. The problem isn’t in the mihomo config itself, but in system network forwarding settings that aren’t configured.

Cause

stack: system makes mihomo rely on the kernel networking stack to forward TUN traffic. auto-route: true takes over the default route and funnels all traffic into the TUN interface. But the kernel needs three conditions to forward:

  1. net.ipv4.ip_forward must be 1
  2. The firewall forward chain must not DROP
  3. The kernel must not drop TCP packets returning from the TUN interface due to rp_filter

Arch Linux defaults to ip_forward=0, and UFW defaults to DEFAULT_FORWARD_POLICY="DROP". Both block forwarding. Even after fixing those two, on kernel 6.x ping works but curl times out — TCP handshake packets returning from the TUN interface are dropped by rp_filter=1. The mihomo log shows [TCP] ... using DIRECT, but the connection just times out.

Fix

1. Enable ip_forward

echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-mihomo.conf
sudo sysctl -p /etc/sysctl.d/99-mihomo.conf

2. Change UFW forward policy to ACCEPT

sudo sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw
sudo ufw reload

3. Enable auto-redirect

Add auto-redirect: true to the tun section in config.yaml:

tun:
  enable: true
  stack: system
  auto-route: true
  auto-redirect: true
  auto-detect-interface: true

auto-redirect automatically configures nftables rules to redirect TCP connections, bypassing rp_filter’s filtering of return packets from the TUN interface. Restart mihomo after changing.

Alternative

If you don’t want to change system forwarding settings, switch the TUN stack:

  • mixed: TCP uses the system stack, UDP uses gvisor — better compatibility than system, better performance than pure gvisor
  • gvisor: handles TCP/IP entirely in userspace without relying on kernel forwarding — best compatibility but slightly lower performance

Change tun.stack in config.yaml from system to mixed or gvisor. No system configuration changes needed.

Contents