TUN `mixed` stack: TCP never re-injected into tun0 on Linux kernel 7.1 (UDP works, `gvisor` works)
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 45/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- go, linux
- Domain
- networking
Research direction
Start by reproducing the report with the supplied config on Linux using the mixed TUN stack, then compare it with gvisor. The report points to sing-tun's stack_system.go, especially processIPv4TCP and the tun.Write error path; inspect those paths and add focused logging to locate where the SYN stops. Done means TCP connections are reinjected into tun0 and work with the mixed stack.
Written by the indexing model from the issue text.
Description
Operating system
Linux
System version
Kali Linux, kernel 7.1.5+kali-amd64
Installation type
Vendor-bundled binary (sing-box 1.13.8, revision d5adb54, tags below). Cannot test the official release on this machine right now; happy to do so on request.
Version
sing-box version 1.13.8
Environment: go1.25.9 linux/amd64
Tags: with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_naive_outbound,badlinkname,tfogo_checklinkname0,with_purego
Revision: d5adb54bc6c6b2c21ab6f748276c4ec62d9bb650
CGO: disabled
Describe the bug
With "stack": "mixed" the TUN inbound handles UDP fine (DNS hijack and QUIC go through the vless outbound and get replies), but no TCP connection ever reaches the router: inbound connection lines never appear, only inbound packet connection. From the application side every TCP connect times out.
tcpdump on tun0 shows only the kernel's SYNs retransmitting and nothing written back by sing-box: no rewritten 10.0.0.2 → 10.0.0.1:<nat_port> packets at all. The system-stack TCP listener does exist:
$ sudo ss -4tlnp | grep sing-box
LISTEN 0 4096 10.0.0.1:35657 0.0.0.0:* users:(("sing-box",pid=36135,fd=10))
Changing only "stack" to "gvisor" in the same config fixes it immediately (log below).
Config (same file, secrets removed)
{
"log": { "level": "info", "timestamp": true },
"dns": {
"client_subnet": "0.0.0.0/0",
"final": "dns_proxy",
"independent_cache": true,
"rules": [ { "action": "route", "domain": ["hh.ru"], "server": "dns_direct" } ],
"servers": [
{ "detour": "proxy", "server": "1.1.1.1", "tag": "dns_proxy", "type": "tcp" },
{ "server": "1.0.0.1", "tag": "dns_direct", "type": "udp" }
],
"strategy": "ipv4_only"
},
"inbounds": [
{
"address": ["10.0.0.1/30"],
"auto_route": true,
"mtu": 1500,
"stack": "mixed",
"strict_route": true,
"tag": "tun-in",
"type": "tun"
}
],
"route": {
"auto_detect_interface": true,
"default_domain_resolver": { "server": "dns_proxy", "strategy": "ipv4_only" },
"final": "proxy",
"rule_set": [
{ "format": "binary", "path": "/home/user/rule_sets/geoip.srs", "tag": "geoip", "type": "local" },
{ "format": "binary", "path": "/home/user/rule_sets/geosite.srs", "tag": "geosite", "type": "local" }
],
"rules": [
{ "action": "sniff" },
{ "action": "hijack-dns", "mode": "or", "rules": [ { "protocol": "dns" }, { "port": 53 } ], "type": "logical" },
{ "ip_is_private": true, "outbound": "direct" },
{ "ip_cidr": ["217.217.x.x/32"], "outbound": "direct" },
{ "domain": "ip.accessly.app", "outbound": "proxy" },
{ "domain": ["hh.ru"], "outbound": "direct" },
{ "outbound": "direct", "rule_set": "geoip" },
{ "outbound": "direct", "rule_set": "geosite" }
]
},
"outbounds": [
{ "tag": "proxy", "type": "vless", "server": "217.217.x.x", "server_port": 443 },
{ "tag": "direct", "type": "direct" }
]
}
Routing state while connected (mixed)
$ ip route get 1.1.1.1
1.1.1.1 via 10.0.0.2 dev tun0 table 2022 src 10.0.0.1 uid 1000
$ ip rule
0: from all lookup local
9000: from all to 10.0.0.0/30 lookup 2022
9001: from all lookup 2022 suppress_prefixlength 0
9002: not from all dport 53 lookup main suppress_prefixlength 0
9002: from all iif tun0 goto 9010
9003: not from all iif lo lookup 2022
9003: from 0.0.0.0 iif lo lookup 2022
9003: from 10.0.0.0/30 iif lo lookup 2022
9010: from all nop
32766: from all lookup main
32767: from all lookup default
$ ip route show table 2022
default via 10.0.0.2 dev tun0
tcpdump on tun0 during curl https://www.google.com (mixed)
$ sudo tcpdump -ni tun0 -c 30 'tcp port 443 and not host 34.160.111.145'
14:31:39.945205 IP 10.0.0.1.34742 > 104.21.74.183.443: Flags [S], seq 1558434216, win 64240, options [mss 1460,sackOK,TS val 433816482 ecr 0,nop,wscale 10], length 0
14:31:40.966373 IP 10.0.0.1.34742 > 104.21.74.183.443: Flags [S], seq 1558434216, ...
14:31:41.986544 IP 10.0.0.1.34742 > 104.21.74.183.443: Flags [S], seq 1558434216, ...
14:31:44.450464 IP 10.0.0.1.48668 > 142.251.140.106.443: Flags [S], seq 1110277888, ...
14:31:48.540053 IP 10.0.0.1.48110 > 108.177.96.188.443: Flags [S], seq 182472403, ...
14:31:49.570472 IP 10.0.0.1.48110 > 108.177.96.188.443: Flags [S], seq 182472403, ...
14:31:52.642360 IP 10.0.0.1.48110 > 108.177.96.188.443: Flags [S], seq 182472403, ...
14:31:59.814299 IP 10.0.0.1.48110 > 108.177.96.188.443: Flags [S], seq 182472403, ...
14:32:22.092929 IP 10.0.0.1.53822 > 34.117.59.81.443: Flags [S], seq 385209085, ...
30 packets captured, 0 packets dropped by kernel
All 30 packets are outbound SYNs from 10.0.0.1. Not a single packet from sing-box back into tun0 (expected 10.0.0.2.N > 10.0.0.1.35657 [S]).
sing-box log (mixed) during the same test
Only UDP flows are logged; the TCP SYNs above never produce an inbound connection line:
INFO [1388067991 0ms] inbound/tun[tun-in]: inbound packet connection from 10.0.0.1:55084
INFO [1388067991 0ms] inbound/tun[tun-in]: inbound packet connection to 192.168.x.x:53
INFO [1388067991 0ms] outbound/vless[proxy]: outbound connection to 1.1.1.1:53
INFO [1388067991 206ms] dns: exchanged A ip.accessly.app. 197 IN A 104.21.74.183
INFO [2521444636 0ms] inbound/tun[tun-in]: inbound packet connection from 10.0.0.1:57000
INFO [2521444636 0ms] inbound/tun[tun-in]: inbound packet connection to 172.217.171.67:443
INFO [2521444636 0ms] outbound/vless[proxy]: outbound packet connection to 172.217.171.67:443
WARN inbound/tun[tun-in]: link icmp connection from 10.0.0.1 to 1.1.1.1: icmp is not supported by default outbound: proxy
Same config with "stack": "gvisor", debug log
INFO network: updated default interface wlan0, index 2
INFO inbound/tun[tun-in]: started at tun0
INFO sing-box started (0.01s)
INFO [908698353 0ms] inbound/tun[tun-in]: inbound connection from 10.0.0.1:35540
INFO [908698353 0ms] inbound/tun[tun-in]: inbound connection to 74.125.128.188:5228
DEBUG [908698353 0ms] router: match[0] => sniff
DEBUG [908698353 13ms] router: sniffed protocol: tls, domain: mtalk.google.com
INFO [908698353 13ms] outbound/vless[proxy]: outbound connection to 74.125.128.188:5228
INFO [908698353 177ms] outbound/vless[proxy]: outbound connection to 74.125.128.188:5228
INFO [2426132354 0ms] inbound/tun[tun-in]: inbound connection from 10.0.0.1:55000
INFO [2426132354 0ms] inbound/tun[tun-in]: inbound connection to 34.149.226.178:443
DEBUG [2426132354 0ms] router: sniffed protocol: tls, domain: content-signature-2.cdn.mozilla.net
INFO [2426132354 0ms] outbound/vless[proxy]: outbound connection to 34.149.226.178:443
INFO [2426132354 160ms] outbound/vless[proxy]: outbound connection to 34.149.226.178:443
DEBUG [908698353 1m33s] connection: connection upload finished
curl https://ipinfo.io/json then returns the exit server's country (SE), browser works.
Ruled out
- Docker: stopped
docker.serviceanddocker.socket, no change. - firewalld (present,
inet firewalldtable with input hook) and leftover iptables chains from another VPN client: all rule counters stay at 0 during the test. - rp_filter: reverse-path lookup for src 10.0.0.2 arriving on tun0 resolves to tun0 via rule 9000, so strict mode would not drop it.
- Conflicting VPN daemons: a second sing-box-based client was stopped before the capture.
nstatshows no increments inIPReversePathFilter,InAddrErrors,InNoRoutes,InDiscards,ListenDropsduring the test.
Expected behavior
With mixed/system stack, TCP SYNs read from tun should be rewritten and written back to tun so the kernel delivers them to the system-stack listener on 10.0.0.1.
Additional
- Not yet tested on a 6.x kernel; will update.
- Happy to run a debug build or add logging in
sing-tunstack_system.go(processIPv4TCP/tun.Writeerror path) if that helps.
- Dominant language
- Go
- Stars
- 241
- Forks
- 243
- PR merge metrics
- No merged PRs in 30d
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from SagerNet/sing-tun
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
gvisor: LinkEndpointFilter.Attach never forwards nil, so GVisor.Close leaks all inbound dispatcher goroutines and fdsPossibly taken @arpitjain099 claimed this 50 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Darwin: utun process UUID headers are parsed as IP payloadPossibly taken @d3pre5s claimed this 3 days ago. Open
Difficulty 4/5 3-5 days Newbie friendliness 25/100
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
-
Difficulty 3/5 1-2 days Newbie friendliness 70/100
All issues in SagerNet/sing-tun
Similar issues
-
[Bug]: APIGW request ID headers are inconsistent between server and clientPossibly taken @zouxingyuks claimed this today. Openproduct/backlog
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
TencentBlueKing/bk-nodemgr#3782 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 91/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
praetorianer777/Dynamo#53 ·
Maintainers usually reply within 1 day
-
internal test setup
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
SAP/terraform-provider-scc#430 ·
-
internal test setup
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
SAP/terraform-provider-btp#1743 ·
Maintainers usually reply within 1 day