V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
heiher
V2EX  ›  分享创造

分享一个 tun2socks 实现

  •  
  •   heiher ·
    heiher · 2023-01-31 20:31:05 +08:00 · 5753 次点击
    这是一个创建于 665 天前的主题,其中的信息可能已经有所发展或是发生改变。

    自用分享,一个 C 语言实现、基于协程和 LwIP 用户态协议栈的 tun2socks 实现,当前支持 Linux 和 Android 系统。

    项目地址: https://github.com/heiher/hev-socks5-tunnel

    功能

    1. IPv4/IPv6 双栈。
    2. 支持重定向 TCP 连接。
    3. 支持重定向 UDP 报文。(UDP over TCP ,需配合hev-socks5-server)

    性能

    详细信息

    速率

    CPU 使用率

    第 1 条附言  ·  2023-02-04 00:24:18 +08:00
    2.4.0 版本增加了 MacOS 系统的支持。
    第 2 条附言  ·  2023-02-07 13:15:55 +08:00

    v2.4.2 版本传输性能翻倍

    内存使用

    第 3 条附言  ·  2023-02-09 23:51:26 +08:00

    在AArch64(RK3399)上的最大传输速率和对应CPU使用率

    • CPU: tun2socks是绑定在两个大核上的(4-5)
    • 网卡: 1Gbps

    最大速率

    speed

    CPU使用率

    cpu

    第 4 条附言  ·  2023-03-04 00:05:08 +08:00
    开始支持 iOS Network Extension
    38 条回复    2023-04-19 22:25:28 +08:00
    missdeer
        1
    missdeer  
       2023-02-01 09:14:17 +08:00
    更想要 windows 版,因为 Linux 、mac 上用 iptables ,pf 就能工作得很好
    heiher
        2
    heiher  
    OP
       2023-02-01 09:17:57 +08:00
    @missdeer 确实,Linux 上我也是使用 tproxy 方案,性能更好,我也主要是用在 Android 系统上的。可惜底层的协程框架目前还不支持 Windows [Doggy]
    nmap
        3
    nmap  
       2023-02-01 11:06:48 +08:00
    请问原理是什么
    heiher
        4
    heiher  
    OP
       2023-02-01 11:31:29 +08:00   ❤️ 2
    @nmap 简述原理:创建 tun 虚拟网卡,配置策略路由使网络应用程序的 TCP 和 UDP 流量经过虚拟网卡,前向链路上 tun2socks 程序从虚拟网卡接收经过操作系统网络协议栈分片的 TCP/IP(UDP/IP)报文,程序再通过用户态网络协议栈 LwIP 将 TCP 分片重组成流,转发至连接着 socks5 服务端的 socket 上。反向链路,程序从 socks5 服务端接收到流数据后,通过 LwIP 的 TCP 写 API 送到用户态网络协议栈进行分片,再将分片后的报文送入虚拟网卡,随后被操作系统重组,最终送到网络应用程序。
    aa51513
        5
    aa51513  
       2023-02-03 09:36:27 +08:00 via Android
    需要预先手动创建 tun 网卡吗,如果是自动创建 tun ,自动修改路由就好了
    heiher
        6
    heiher  
    OP
       2023-02-03 09:53:08 +08:00
    @aa51513 #5 自动创建 tun 网卡设备,但不会自动创建路由规则,主要是考虑到使用需求不同,比如最简单的用法是直接创建一个默认路由,而有些用户可能用策略路由只针对 TCP 、UDP 协议或指定用户下的进程的网络流量。
    TongNianShanHe
        7
    TongNianShanHe  
       2023-02-05 17:08:26 +08:00
    楼主您好,有 android 端的参考例子吗,我这边根据您的代码调用了 TProxyStartService ,然而好像没生效(第二个参数应该填的是 v*nservice 创建后的 fd 吧)
    heiher
        8
    heiher  
    OP
       2023-02-05 17:40:13 +08:00 via Android
    TongNianShanHe
        9
    TongNianShanHe  
       2023-02-05 21:44:18 +08:00
    感谢,找过没找到,应该是被我忽略了。
    但问题似乎还是没有解决,试过 demo 里的写法,没法连通,我的 local 端没有数据(此前用的 xjasonlyu 的 tun2socks 实现),logcat 报“getIfIndex: cannot find interface tun0”,把 demo 的 release 跑了一遍,也不行。
    我先放一下吧,近期没有多余时间去排错了,但是看图感觉性能会比之前好一些,先 star 一下,后续有时间再弄。
    heiher
        10
    heiher  
    OP
       2023-02-05 21:53:48 +08:00
    @TongNianShanHe #9 重点检查配置文件对不对,可以先用生成的配置文件在 Linux 上验证一遍。另外一个需要注意的地方是 JNI 的 PKGNAME ,我不知道你改了没有,可以在 Makefile 中用宏定义覆盖为你的项目包名。
    TongNianShanHe
        11
    TongNianShanHe  
       2023-02-05 22:10:31 +08:00
    @heiher PKGNAME 和 hev-jni.c 的 64 行( class 名)都改过,配置文件我注意到你给的例子里填写 IPv4 地址是没有单引号的,但是安卓 demo 的代码里是加了单引号,两种都试过了,看 logcat 感觉加了单引号是对的,因为改动后 local 端有了 socks5 的握手,但只发出了第一部分,后续都无法接收到。
    heiher
        12
    heiher  
    OP
       2023-02-05 22:24:33 +08:00
    @TongNianShanHe #11 看你描述的现象,我怀疑是没有处理好访问 socks5 server 要加入例外(不通过 VPN)的情况。Android VpnService 给出了 protect(Socket)的[方法]( https://developer.android.com/reference/android/net/VpnService#protect(java.net.Socket)),但 socks5-tunnel 在 jni 里创建 socket ,所以我的 demo 里是把当前应用加入到 disallow 列表里,当处于全局模式时。在 per app 模式是是不允许选择当前应用的。

    https://github.com/heiher/hev-htproxy/blob/master/app/src/main/java/hev/htproxy/TProxyService.java#L99-L105
    TongNianShanHe
        13
    TongNianShanHe  
       2023-02-05 22:56:01 +08:00
    @heiher 这个情况其实考虑过,但我用的是白名单(白名单是硬编码),local 是一个独立进程(通过 sh 运行,写的很糙,因为是自己用)
    我再去看看源码吧,不排除 local 端某些情况下的不兼容的可能性,谢谢你的解答。
    heiher
        14
    heiher  
    OP
       2023-02-05 23:03:33 +08:00 via Android
    @TongNianShanHe #13 好,不客气哈~ 也可以试试在 htproxy 上改改 socks5 server address 来验证。
    TongNianShanHe
        15
    TongNianShanHe  
       2023-02-06 12:02:55 +08:00
    @heiher 我调试了一下,发现是 tun2socks 确实是连通的状态,但是有俩问题:
    1. 握手“黏包”(其实这也不算问题,毕竟 tcp 流,已经自行处理过了)
    2. socks5 里面,通过 ip 地址进行访问是正常的(也就是 command 为 0x01 ),通过域名访问就“死”了,检查了一下发现 DNS 请求头(也就是 udp )的 command 是 0x05 ,我以为是协议做过魔改,所以我把 command=0x05 映射到了 udp 转发上,但也是无法工作。(刚重新看了一遍帖子才发现,是必须要用 UDP over TCP 方式做吗?)
    TongNianShanHe
        16
    TongNianShanHe  
       2023-02-06 12:33:22 +08:00
    @heiher 试着做了个 UDP over TCP 的 demo ,证实了这个想法,确实需要用 UDP over TCP 方式,没有问题了,打扰你了
    heiher
        17
    heiher  
    OP
       2023-02-06 12:37:09 +08:00
    @TongNianShanHe #15 是的,hev-socks5-tunnel 的 udp relay 是采用扩展的 socks5 cmd 实现的 udp over tcp ,不是协议标准的 udp 方式,原因是为了让所有流量都走 tcp ,这样方便过 cdn 。
    heiher
        18
    heiher  
    OP
       2023-02-06 12:37:59 +08:00
    @TongNianShanHe #16 好的,hev-socks5-tunnel 的 udp 功能需要配合 hev-socks5-server 或兼容的服务端使用。
    TongNianShanHe
        19
    TongNianShanHe  
       2023-02-06 18:33:18 +08:00
    @heiher 花了点时间把我那边的适配做好了(我没想到 UDP over TCP 包也做了魔改)

    如果想使用这个库的话这边有几个提示:这个 tun2socks 实现基于 socks5 ,支持两个模式( 0x01 是 CONNECT ,0x05 是 UDP over TCP ),并且 UDP over TCP 包的前两位和 10 ,11 位是当前包的长度,中间的和 socks5 udp relay 转发头是差不多的(也就是说 IPV4 relay 头的长度会从 10 变为 14 ,IPv6 同理加 4 ),其他的都是差不多的,直接转发就行。

    等哪天真闲下来就试着啃啃这个项目吧,感谢分享。
    heiher
        20
    heiher  
    OP
       2023-02-15 13:38:47 +08:00
    @TongNianShanHe Android VPN over socks5 proxy 参考实现: https://github.com/heiher/sockstun
    heiher
        21
    heiher  
    OP
       2023-02-16 21:42:26 +08:00
    @TongNianShanHe 增加了的 udp relay over udp 的方式
    TongNianShanHe
        22
    TongNianShanHe  
       2023-02-17 22:18:32 +08:00   ❤️ 1
    @heiher ( v2 的消息提醒好像有点问题,刚刚才看到)谢谢您,虽然我已经实现了 udp relay over tcp
    testFor
        23
    testFor  
       2023-03-11 23:08:40 +08:00
    从 badvpn 的切换过来,不清楚为什么感觉引入了一个 1s 的延迟,及时很小的网页也是,是为了吞吐量做了缓存么
    heiher
        24
    heiher  
    OP
       2023-03-11 23:41:41 +08:00 via Android
    @testFor 能否提供一个可量化的测试方法?
    testFor
        25
    testFor  
       2023-03-12 00:28:22 +08:00
    没有,刚切换过来,主观感觉延迟 1s,特别是打开 V2EX 这种小网页,表现为前 0.5s 什么也不发生,后 1.0s 集中加载
    testFor
        26
    testFor  
       2023-03-12 00:31:50 +08:00
    @heiher 是否可能因为开了 tcp 延迟
    testFor
        27
    testFor  
       2023-03-12 01:04:15 +08:00
    @heiher 可能也是这个延迟导致楼上说的沾包出现
    heiher
        28
    heiher  
    OP
       2023-03-12 09:30:04 +08:00 via Android
    @testFor 有关 TCP delay 都是系统默认配置,“粘包”效果是因为 socks5 客户端有意实现为预测发送,是有减少延迟作用的。不知道切换前怎样,切换后应该是 dns 经过 socks5 服务器端,不知道是不是因为 dns 的延迟。浏览器的开发者选项中网络传输页面应该可以显示每个资源的加载时间,不妨对比看看,需要注意的是尽可能网络环境的抖动影响,比如本地跑 socks5 服务器,访问本地或国内 http 资源。
    testFor
        29
    testFor  
       2023-03-12 10:27:32 +08:00
    @heiher 所以实现上还是主动延迟了发送时间?
    heiher
        30
    heiher  
    OP
       2023-03-12 10:42:17 +08:00
    @testFor #29 没有主动延迟的意思。

    刚测了访问国内四个网站的传输时间情况,socks5 服务端在局域网另一主机,测试主机配置 tun0 为默认路由(ipv4 & ipv6),数据来自 firefox 开发者选项的 timing:

    testFor
        31
    testFor  
       2023-03-12 10:47:05 +08:00
    @heiher 可以关闭预测么.我的平台是安卓 ,型号为小米 12s.安卓 12. 测试方法可能只有切换 tun2socks 与你的新的 so 进行测试了.
    heiher
        32
    heiher  
    OP
       2023-03-12 10:55:18 +08:00
    @testFor #31 暂不支持,socks5 握手预测的实质是将多轮交互的客户端侧数据一次发送(大概率合并在一个报文中):

    假设客户端与服务端通信的前、反向链路延迟都是 100ms 的话,两种方式的传输模式和延迟情况大致如下:

    经典:
    客户端:Hello ,我支持的鉴权方法有:123 (100ms)
    服务端:Hello ,我选鉴权方法 1 (100ms)
    客户端:请帮我建立与 xxx 的连接 (100ms)
    服务端:好的,连接已经建立好 (100ms)
    合计:400ms

    预测:
    客户端:Hello ,我支持的鉴权方法有:123 ;请帮我建立与 xxx 的连接 (100ms)
    服务端:Hello ,我选鉴权方法 1 ;好的,连接已经建立好 (100ms)
    合计:200ms
    testFor
        33
    testFor  
       2023-03-12 10:59:50 +08:00
    如何是本地连接 1ms 完成交互.会不会你的延迟合并的超时时间都大于连接开销
    heiher
        34
    heiher  
    OP
       2023-03-12 11:04:09 +08:00
    @testFor #31 说明一下没有主动延迟等待收集数据再合并的逻辑,这样理解与事实不符。事实是客户端将 socks5 握手中需多轮交互的多个数据片段一次准备好,并在 TCP 流中一次 send ,仅此而已。
    Cormic
        35
    Cormic  
       2023-04-11 15:53:01 +08:00
    看配置文件似乎是 socks2tun ,名字又是 tun2socks ,是我理解错了吗?
    heiher
        36
    heiher  
    OP
       2023-04-11 16:47:49 +08:00
    @Cormic client -> virtual tunnel -> socks5 server
    TongNianShanHe
        37
    TongNianShanHe  
       2023-04-19 22:20:52 +08:00
    你好作者,我又来了,这次来反馈一个问题,2.5.1 版本,android 端封装后,使用 TProxyStartService 启动没有任何问题,功能也能正常使用,但是通过 TProxyStopService 关闭时,应用会闪退,Logcat 报错如下图,使用您的 android demo 上的代码尝试,问题依旧,但老版本(具体应该是 2.4 左右,时间有点久我也忘记了)没有出现这个问题,如果您方便的话,还是烦请您看一下😂

    ![e6cb550c920dcd75a5923a95d24b4841.png]( https://s2.loli.net/2023/04/19/v3i5RQZFfTwujC8.png)
    heiher
        38
    heiher  
    OP
       2023-04-19 22:25:28 +08:00 via Android
    @TongNianShanHe 好的,我找时间看看,感谢反馈~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3494 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 11:01 · PVG 19:01 · LAX 03:01 · JFK 06:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.