xjasonlyu / tun2socks

tun2socks - powered by gVisor TCP/IP stack
https://github.com/xjasonlyu/tun2socks/wiki
GNU General Public License v3.0
3.14k stars 433 forks source link

[Feature request] direct模式下port forwarding模式 #58

Closed KusakabeShi closed 2 years ago

KusakabeShi commented 3 years ago

可以設置一系列類似這樣的規則,

1314->192.168.11.2:1314/udp 
8080->192.168.22.3:80/tcp
8443->192.168.22.3:443/tcp

就會在主機listen udp 1314 等等port,forwarding到tun端對應ip:port

udp無狀態比較好弄些,tcp感覺上比較麻煩。

只有udp也好,加上這個功能也是挺實用的

xjasonlyu commented 3 years ago

我不知道你在说什么,而且这种奇怪的需求不应该就是自己去解决吗⋯

KusakabeShi commented 3 years ago

就是可以在tun端運行服務,比如http之類 讓外面的人連近來,類似路由器的port forwarding功能

8080->192.168.22.3:80/tcp

這樣設定就會使tun2socks在主機監聽tcp 8080,有連線建立就向tun端的192.168.22.3:80 發送 TCP SYN開始建立連線。 一樣要維護tcp狀態機,有點像是tun2sock原本在做的事情反過來

xjasonlyu commented 3 years ago

直接用iptables就可以做了吧,这种需求比较奇怪而且并不是tun2socks 的行为,所以没必要加吧

KusakabeShi commented 3 years ago

恩... 應該說,我的使用情境比較不一樣。博主設想的情境應該是這樣:

+--------------------------------------------------------+
|                +-----+      +----------+               |
|   user space   | app |      |tun2socks |-------+       |
|                +--|--+      +----^-----+       |       |
|-------------------|--------------|-------------|--------
|                +--v---+          |             |       |
|    kernel      | tun  |----------+      +------v--+    |
|                +------+                 |  eth0   |    |
+-----------------------------------------+---------+----+

然後用iptables呼叫netfilter把eth0上面的端口導向tun

+--------------------------------------------------------+
|                +-----+      +----------+               |
|   user space   | app |      |tun2socks |------+        |
|                +--|--+      +-^--------+      |        |
|-------------------|-----------|---------------|---------
|                +--v---+       |               |        |
|  kernel space  | tun  |-------+               |        |
|                +---^--+                   +---v-----+  |
|                    |    +----------+      |         |  |
|                    +-----port forw <-------  eth0   |  |
|                         +----------+      |         |  |
+-------------------------------------------+---------+--+

但我這邊,是想把這個tun2socks作為這個系統 https://www.kskb.eu.org/2021/06/dn42.html 的一個組件,作為layer3和layer4的轉換工具(下圖右上角的tun2socks) 使VPP網路能連接外網(現有tun2socks已能做到),並且暴露VPP裡面的服務給外網(feature request!!!)

+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                +--------------------------|-----------------------------------+                                                              |
|                                                | VPP                      |          userspace imeplmentation |                                                              |
| +-----------------------+                      |       +----------------->|  Layer 4   of bind() socket()     |                                                              |
| | exabgp                |                      |       |                  |                  function         |                                                              |
| |                       |                      |       |                  |-----------------------------------|                                                              |
| |         +-------------|                      |       |                  |        +--------------+           |                                                              |
| |    +--- |BGP sessions |                      |       |          +--------------> |  Route table |   layer3  |                                                              |
| |+---v----+----------^--|  +--------------+    |       |          |       |        +--------------+           |  +---------------+                     +----------+          |
| ||python3-vpp-api|   |  |  |    nginx     |    |       |          |       |------------------------------------  |wireguard-go*2 |--+                  |tun2socks |--+       |
| +----|------------------+  ----------------    |  +--------+ +--------+   |          +-----+ +-----+ +------+ |  -----------------  |                  ------------  |       |
|      |    |ldpreload.so |  | ldpreload.so |    |  |api.sock| |cli.sock|   |  layer2  |memif| |memif| |memif | |  |  tap2tun*2    |  |                  | tap2tun  |  |       |
|      |    +------|------+  +-------|------+    +--+------|-+-+----^---+---|----------+--|--+-+--|--+-+---|--+-+  +-------^-------+  |                  +--^-------+  |       |
|      |           |                 |                     |        |                     |       |        |               |          |                     |          |       |
|------|-----------|-----------------|---------------------|--------|----------------------------------------------------------------------------------------------------------|
|      |           |                 |                     |        |                     |       |        |               |          |                     |          |       |
|      |           +----------------------unix socket------+        |                     -------------------unix soxket--------------|----------------------          |       |
|      +------unix socket-------------------------------------------|                                                                 |                                v       |
|                                                                                                                                    udp                            direct     |
+-------------+                                                                                                                +------------------------------------------+    |
|kernel space |                                                                                                                |      |            eth0                   |    |
+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
                                                                                                                                      |                                |        
                                                                                                                                      |                                |        
                                                                                                                             +----------------+               +--------v--+     
                                                                                                                             |                |               |           |     
                                                                                                                             |  DN42 Network  |               | Clearnet  |     
                                                                                                                             |                |               |           |     
                                                                                                                             +----------------+               +-----------+     

在這個場景中,我沒有kernel的任何權限,tun0之類的虛擬網卡也沒有註冊在kernel裡面。 唯一用的kernel功能只有unix socket和普通的tcp/udp socket。幾乎所有東西都在userspace完成

tun2socks的tun端被我用libmemif經由tap2tun註冊在VPP裡面,所以也不可能用iptables直接把封包轉送到tun端。而且要捕獲網卡的tcp封包也是要root權限。所以還是只能在tun2socks這端用 bind() 去listen然後送進tun端

qemu在沒有root的情況下,是用SLIRP這個工具來做layer2-layer4的轉換的。layer2端對應VM的虛擬網卡,layer4端則是tun2socks的direct模式,以在沒有root的情況下和使VM上網(vm的kernel跑在主系統的user space,vm裡的網卡也沒註冊進主系統)。slirp也有提供port forward的功能,讓外界可以連線到vm裡面的服務

為甚麼我不用SLIRP而是在這邊提交feature request呢... 是希望有一個go版本的SLIRP可以用,各種編譯問題比C++好處理多了。 還有一個因素是我對layer4-layer3轉換的知識比較不足,一個linux tcp socket的狀態和tcp狀態機的對應和轉換,還有gvisor裡面的處裡邏輯。我可能要重頭開始看。想說博主比我還要更熟悉這塊

如果博主暫時不打算加入此功能,想問問博主有沒有知道go語言實現的其他類似的工具,要搞C++(SLIRP)太痛苦了...

xjasonlyu commented 3 years ago

啊我好像明白你的意思了,有点意思🤨

所以如果我没有理解错的话,你FR是不是想增加一个类似-device udp://1270.0.0.1:1234的UDP套接字来模拟tun设备,作为L3-L4的入口?

KusakabeShi commented 3 years ago

不太一樣, 是L4->L3的入口 -p 9000:192.168.0.1:9000/tcp L3->L4的部分比較好弄,我去改 https://github.com/xjasonlyu/tun2socks/blob/main/core/device/tun/tun.go ,弄一個tun_vpp之類的就行了,改寫他的Read() Write() function

我目前的理解是這樣(來源網路),L3->L4的原理是中继程式运行一个独立TCP/IP协议栈,並建立一个侦听 “TCP_SYN” 任何地址与端口的虚拟TCP服务器。每當建立一個虛擬服務器,就開一個對外的tcp連接,與之互相通信。也就是博主已經完成的部分

但是L4->L3就觸及我的知識盲區了。我想到可能的工作原理大概是這樣,一樣有userspace的TCP/IP协议栈,並且 listen port 8080。當外部連建立,則運行一個虛擬TCP客戶端,和L3內部這邊的服務器交互

xjasonlyu commented 3 years ago

你是想绕过kernel,自己在userspace网络栈上实现tcp状态机?即tcp2raw这样?

xjasonlyu commented 3 years ago

假设有这么个东西,那你的SYN包要以什么形式出来,直接发到tun接口上还是以UDP形式封装发给其他端口?

KusakabeShi commented 3 years ago

你是想绕过kernel,自己在userspace网络栈上实现tcp状态机?即tcp2raw这样?

是阿,博主的這個專案不是已經用gVisor的TCP/IP协议栈實現了L3->L4的部分了嗎?

假设有这么个东西,那你的SYN包要以什么形式出来,直接发到tun接口上还是以UDP形式封装发给其他端口?

直接发到tun接口。 這個專案的tun接口看上去有在 core/device/tun 作抽象。之後要轉發到UDP還是轉去其他地方,只要改寫 core/device/tun/io_unix.go 裡面的Read和Write就行了對吧?

xjasonlyu commented 3 years ago

直接发到tun接口。 這個專案的tun接口看上去有在 core/device/tun 作抽象。之後要轉發到UDP還是轉去其他地方,只要改寫 core/device/tun/io_unix.go 裡面的Read和Write就行了對吧?

不是哦,device抽象的是入口,默认出口是tunnel,即将所有的包重组成至L4然后通过proxy转发。

那你需要的应该是tcp2tun吧🌚,实现不复杂,只是似乎和这个项目没有太大关系了。这样的话就是要么iptables,要么自己造轮子了。

KusakabeShi commented 3 years ago

sorry之後忙別的忘記回復

我想說這個專案已經實現了L3->L4的部分,再多實現一個L4->L3,變成一個全功能的L3<->L4轉換器,我就可以直接拿來用

自己實現也可以,我再看看相關資料好了

KusakabeShi commented 2 years ago

我說的功能在這邊找到了! https://github.com/KusakabeSi/slirpnetstack

有兩個端,一邊是L2/L3 raw packet,另一邊則是linux tcp/udp session。這隻程式可以做轉換

當Layer 3端收到TCP SYN的時候,就在Layer 4呼叫connect() 系統呼叫 當Layer 4端監聽端口的 accept() 被觸發,就對Layer 3那邊發出TCP SYN 觸發遠端系統的accept()

剛好對應這個的 "local forward" 和 "remote forward" 模式!