Skip to content

Conversation

@Owersun
Copy link

@Owersun Owersun commented Dec 22, 2025

This is implementation of tun network L3 interface as input to the app.

There is README.md in the folder, explaining how the feature works.

Worth to mention on the implementation itself:
This is extremely oversimplified (not in functionality, rather intentionally without excessive complexity) implementation.
There is Linux support only (but the implementation allow to add support for other OS later, if needed). Most probable use case of this feature are router boxes, which mostly are linux based devices.
There are no internal app configuration ways to manage the interface, as network interface is OS level entity. The complication of double routing table or ip rules, or other ways this should be enhanced to work properly, should be managed by OS, to ensure proper integrity with network state of the system. This is explicit decision based on how many different things there are you could do with a network interface in Linux, and adding all of that to be configurable through the app is excessive.
No external additional libraries used, the whole ip stack is gvisor lib, already existing in the app. Tun interface itself is just a file in the system.
OS Level optimisations like GRO/GSO are intentionally disabled, as passing through traffic (forwarded through the interface) anyway is not subject for it. Implementing and always checking and accounting for possible GRO/GSO tables affect performance, not enhance it, in the configuration as a router device. There is very-very-very slim possible advantage for traffic originating from the router itself, which will gain like 0.1% real life performance, but will need like 80% more code to support it.

There were several tests done with different scenarios, all of them used VRAY-XTLS-Reality as uplink.
Normal browsing works just fine, TLS sniffing also works, no issues with that.
Ssh through the interface also worked without any issues, no delays, not lag.
Torrents work just fine.
In one case, test subject managed to run another IPSec (UDP) based VPN on top of that, connecting through Xray, through IPSec commercial VPN to different locations, and then using VoIP and video conferencing apps on top of that, joining several meetings. All that from the country where IPSec VPN is under restricted ban.
I honestly didn't come up with more cases I wanna try after that worked.

With my router based on mediatek mt7986a (banana-pi-r3) I was not able to find traffic top with my 100Mb uplink connection, services like speedtest always load it up to the top.
Although I expect the numbers will not be so extreme when many connections open and close. The cpu profile shows that cpu spikes on connection establishing (routing through the app, forward to uplink and so on), and then it has no problem for traffic flow through same running connection.

All in all, this is very similar implementation to any standalone tun-socks proxy there is, just without excessive complexity, and without necessary app-to-app connection in between, passing packets, converted to connection streams, from the network directly to the app core.

@Fangliding
Copy link
Member

明明有文档站为什么要单独写个README.md

@Owersun
Copy link
Author

Owersun commented Dec 22, 2025

Current README explains how the feature is intended to be used.
Sure best place for it would be part of wiki page for Xray-core, it’s just not the part of this repo. It felt a little wrong to open a pull request without explanation of how this intended to be used.

@Fangliding
Copy link
Member

它那么长似乎有的地方不是面向开发而是像文档面向最终用户的?

@Owersun
Copy link
Author

Owersun commented Dec 22, 2025

Yes, README included is more an explanation for a user how to utilise the feature.
Although it should be helpful for anyone testing it.
If there is a better place to add user targeted description, tell me where should I move it.
I felt like this README is required to explain why there are no options that any tun-socks proxy is full of

@Fangliding
Copy link
Member

可以解释一下 ConnectionHandler 是做什么的吗 我好像没有看到有关它的逻辑

@Owersun
Copy link
Author

Owersun commented Dec 22, 2025

"ConnectionHandler" is an interface with only one function "HandleConnection".
The function is passed as callback to stack implementation, and intended to be called every time stack receive new connection.
In current implementation (gvisor) this function is wrapped in a small function that is passed to gvisor as callback.

gvisor call stack function every time there is new connection (TCP SYN packet or UDP packet that doesn't belong to any stream), stack function map gvisor connection to simple golang net.Conn and pass it to the HandlerConnection. HandleConnection receive net.Conn and destination as input, and simply pass it to the app dispatcher as new connection with the destination. At this stage the net.Conn stream is no different to stream received by any other proxy implementation as result of connect to the proxy. Gvisor is the connection that bridges network packets and net.Conn streams

the usage of this function is on stack_gvisor.go#89

@Fangliding
Copy link
Member

我的意思是 没有其他抽象逻辑 为什么要声明一个接口

@Owersun
Copy link
Author

Owersun commented Dec 22, 2025

In case there could be other implementations of the stack, than gvisor.
Yes, there is barely one now, but if there is going to be, it is just going to be stack_whatever.go, that consume same interface "ConnectionHandler" as callback, and bridge it to the different ip stack implementation.
For sure this can be squashed to not exist, but it will save like 10 lines of code?.. In exchange of requiring to put it all back, if there is other implementation than gvisor.
Besides that I tried as I could to replicate how things are done in other parts of the app, I think this approach was taken in how it happens in wireguard. Or maybe how it was in sing-tun, which I researched.

@Fangliding
Copy link
Member

tun.LinuxTun 最后可以被正确调用Close()吗

@Owersun
Copy link
Author

Owersun commented Dec 22, 2025

That's a legit thought. Thank you for that.
It will take some time from me to find place to put it, currently all construct functions actually complete, leaving only stack threads running.

@Owersun
Copy link
Author

Owersun commented Dec 22, 2025

I reviewed the flow, and it matches other input implementations: there is no "close" that signal proxy input/output handler to finish, so after it is initialised it is never going to shutdown. There is no closing of tun device in wireguard implementation, or any socket cleanup in other implementations.
I've added .Close() calls to cleanup the interface/stack in case of errors returned.
In general the device is properly deregistered from the system by OS when app binary finishes, there is not going to be a dangling tun interface in the system, Linux knows its job related to that.
If there would be any way to hook to core shutting down and register a callback of that event, I can insert cleanup there, but as I wrote, I didn't find any input/output implementation doing that.

@RPRX
Copy link
Member

RPRX commented Dec 23, 2025

This is extremely oversimplified (not in functionality, rather intentionally without excessive complexity) implementation.

这就是 Xray-core 需要的 TUN

Linux 上其实 TPROXY 性能比 TUN 更好,所以如果能把 Windows TUN 一起实现就更好了,今年的 Project X NFT 大奖就是你的了

其实为啥我迟迟不动工 Windows TUN,一是因为麻烦二是因为我不太想让 core 处理 tcp/ip,总是在调研类似于 TRPOXY 的方案,不过 WPF 没有 wintun.dll 这种现成的免费驱动,而如果搞 API HOOK 或 DLL 注入则更麻烦,权限不一定够还可能被判定为外挂

最有希望的是 eBPF on Windows 不过还在测试版又要钱去实名签名,所以我决定两个都要,先整个 wintun.dll 吧 @Owersun

@Owersun
Copy link
Author

Owersun commented Dec 23, 2025

Thank you for kind words.

I did omit windows implementation for the same reason you have mentioned. It's complicated, it will require external wuntun.dll, and with all that complexity it barely is going to be used. tun really shines for forwarded traffic, which most of the time is router setup, 99% of which is a linux box. Windows tun implementation will be used by like 1% of enthusiasts and will add 80% of the code to implement. Really bad trade off. Although I made the code extendable enough that it can be added later if there are really going to be people asking. Just don't think initial version must have it.
I did investigate TPROXY before starting with the implementation. It is nice, it is performant, but it doesn't cover everything. e.g. will require quirks to do things like ICMP or multicast. Or encapsulate other protocols like GRE. All in all TPROXY is tcp/udp layer on the level of ip/nf tables where kernel is trying to stitch packets into streams. tun is raw network input that directly pass packets into the app, where gvisor tries to stitch them into streams. Same things, different application levels.
I would say tun is not "next TPROXY", it is additional input type that can be used when needed. Same way e.g. VLESS and xHTTP exist together in the core. They both are for the same thing - to masque the traffic. And one can be used in one case, the other in the other. Same idea here. TPROXY with dokodemo can be great choice on a router with sub 300Hz cpu. Tun can be chosen when a user requires really strange setups.
Once more, the simplification of this implementation (no ways to configure the interface, no support for anything except linux) is not because that's as good as I can do, it's because this should cover 80% of use cases with really clean feature. And then always can be further enhanced to support android/macos/windows(maybe)/... and so on.

@Fangliding
Copy link
Member

Windows TUN是无和有的区别 而Linux TUN vs Linux tproxy 是性能和配置是否麻烦的事(

@Owersun
Copy link
Author

Owersun commented Dec 23, 2025

Sure, I'll have a look how other apps does that for Windows.
In my initial research it was all looking as "no thank you".
It will take some time, since I really have not a single windows machine, I'll need to roll a virtual machine and take a look how windows look in 2025. It's been a long time.
by the way, the wireguard implementation doesn't have windows tun, probably for the same reason.

@Zerogoki00
Copy link

Zerogoki00 commented Dec 23, 2025

@Owersun

Windows tun implementation will be used by like 1% of enthusiasts and will add 80% of the code to implement

not really, for example sing-box also has a tun interface and it's actively used on Windows in many GUI proxy clients. Having a cross-platform tun interface would be very nice for xray, because there will be no need for double setup of xray <--> sing-box or tun2proxy for system-wide tunneling on windows

@Owersun
Copy link
Author

Owersun commented Dec 23, 2025

Sure thing. As I said - this is initial implementation I made as MVP (minimal viable product). I tried to do it as clean as possible, so that the idea is clear. And extendable at the same time.
The plan was that windows support can be added later if needed.
I already see in this topic that windows tun support is demanded feature, so I did agree I'll add it in. I'm already looking at it (although it will take some time from me because of holiday season).
I just want to warn everyone that windows support will add like three times more code that there is now in the pull request. It will be really more complicated to review.

@yuhan6665
Copy link
Member

Thanks @Owersun for your great work!
I have a question for Android, I see you define a tun interface and currently Android is excluded. I'm one of the author of Android app v2rayNG, I wonder if the tun inbound can work with the Android VPN service and what work would be needed to support it.

Currently, we need to spin off a separate tun2socks process like the following https://github.com/2dust/v2rayNG/blob/master/V2rayNG/app/src/main/java/com/v2ray/ang/service/Tun2SocksService.kt#L35

@Owersun
Copy link
Author

Owersun commented Dec 27, 2025

To make it work on Android it require literally 1 line change.
Android is Linux, only the device to clone to make new interface is at /dev/tun instead of /dev/net/tun.
As I was saying, my goal was to introduce base, most useful system, and then start adding more support.
Android is the easiest to add, I can take a look at it (what happens when it is enabled, how the interface behave), after I'm done with Windows.

@yuhan6665
Copy link
Member

Guys, I have some good news. I try to pass the Android VPN service fd to the core and it is working with initial testing on v2rayNG!
That means we will be able to use Core's Tun soon, instead of badvpn-tun2socks or hev-socks5. cc @2dust

This is an important step towards one-core-fits-all and it will simplify many use cases. My suggestion is that we accept the PR now and work on Windows and other improvements later. @RPRX @Fangliding

@Fangliding
Copy link
Member

它的效率能比hev高吗(
我之前没有在意Android平台 因为觉得Android平台上还是专门去弄可能好点
以及可能的udp问题(

@Owersun
Copy link
Author

Owersun commented Dec 28, 2025

I'm glad that it turned out to be easily usable, that was the whole idea.

With Windows I honestly done with the implementation, but it just look horrible... With external wintun.dll required (all other apps wireguard-go/sing-tun/etc. do that the same way), with problems with ipv6, and with a lot internal memory allocation (although this doesn't affect speed much).
I am at the process of making it not horrible as minimal version, but windows is really sad OS for network handling.

@yuhan6665
Copy link
Member

它的效率能比hev高吗( 我之前没有在意Android平台 因为觉得Android平台上还是专门去弄可能好点 以及可能的udp问题(

效率是一码事 全 go stack 是另一码事 所以 v2rayNG 搞了可选的 tun (现在是俩选项 我准备加第三个)
将来甚至可能搞 process 分流啥的

@Fangliding
Copy link
Member

Fangliding commented Dec 28, 2025

Android编译是后来加的 其他安卓客户端用的是 Linux arm64编译吧 不知道这样会不会有问题
还有那个Android编译还有小问题 #4772

@2dust
Copy link

2dust commented Dec 29, 2025

Guys, I have some good news. I try to pass the Android VPN service fd to the core and it is working with initial testing on v2rayNG! That means we will be able to use Core's Tun soon, instead of badvpn-tun2socks or hev-socks5. cc @2dust

This is an important step towards one-core-fits-all and it will simplify many use cases. My suggestion is that we accept the PR now and work on Windows and other improvements later. @RPRX @Fangliding

如果能实现,v2rayNG 将移除 badvpn-tun2socks ,只保留 xray tun 和 hev tun 即可

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants