比如使用 ClashX Pro 开启了 Enhanced Mode 后,通过 ifconfig
可以看到有一个 utun10:
utun10: [
{
address: '198.18.0.1',
netmask: '255.255.0.0',
family: 'IPv4',
mac: '00:00:00:00:00:00',
internal: false,
cidr: '198.18.0.1/16'
}
]
但它是如何接管电脑上所有的网络请求的呢? ClashX Pro 是闭源的,没有办法查看源码。
通过命令 route -n get default
查看默认 route 是 en0 接口:
route to: default
destination: default
mask: default
gateway: 192.168.11.1
interface: en0
flags: <UP,GATEWAY,DONE,STATIC,PRCLONING,GLOBAL>
recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire
0 0 0 0 0 0 1500 0
难道使用 Network Extension 中的 Packet Tunnel Provider 不需要手动设置网络的转发吗?
我所理解的过程是这样的:
所以总结下我的问题,第二步是如何接管系统所有请求的?第四步是如何转发到其他接口的?在 Linux 中应该是需要手动写吧?在 macOS 中是用 NE 里哪一些 API ?
另外,在 iOS 端也是和 macOS 一样的吧?开启 QX 应用后,在设置中可以看到有 utun 接口。类似的应用都是用 TUN 模式的( Loon 用 TUN 和 HTTP mode 两种模式)。
1
julyclyde 2023-09-10 19:14:29 +08:00
什么叫“将……接管”?
恕我无法对你的汉语进行语法分析 |
2
MrGba2z 2023-09-10 19:17:59 +08:00
> 接管系统的请求
就好比你装了雷电物理网卡,你在系统里选择优先使用这个网卡,那么系统就会默认使用他。大部分情况下软件都会按照系统的优先级使用第一个网络,但是软件也可以不遵守规则强制使用某一个,这时候 tun 代理就失效了。 |
3
FaiChou OP @julyclyde 哈哈 标题起得草率了,没有回去读一遍。原意是想表达某些应用比如 clashxpro 是如何将它新建的 utun interface 起到接管整个系统网络请求的。
|
4
leonshaw 2023-09-10 19:30:18 +08:00 via Android 1
2 是把流通过操作系统导入 tun ,在桌面端一般是路由和 nf/pf/wfp 等,移动端就是 VPN 相关 API
4 是正常的 socket |
5
lcdtyph 2023-09-10 20:27:50 +08:00 3
@FaiChou #3
就是建立优先级更高的路由表项来把流量路由到 utun 而已 default 相当于 0.0.0.0/0 是优先级最低的匹配项 掩码长度越长优先级越高,netstat -nrf inet 可以看到以下几个路由表项 default via en0 1.0.0.0/8 via utun 2.0.0.0/7 via utun 4.0.0.0/6 via utun 8.0.0.0/5 via utun ... 128.0.0.0/1 via utun 这样可以在不修改 default 路由的情况下把系统流量“劫持”到 utun |
6
fuis 2023-09-10 20:48:40 +08:00
|
7
FaiChou OP @lcdtyph 谢谢,之前一直搞不懂 netstat -rn 给出的这个结果,你一讲我明白了:
default 192.168.11.1 UGScg en0 default link#25 UCSIg utun3 1 198.18.0.1 UGSc utun10 2/7 198.18.0.1 UGSc utun10 4/6 198.18.0.1 UGSc utun10 8/5 198.18.0.1 UGSc utun10 16/4 198.18.0.1 UGSc utun10 32/3 198.18.0.1 UGSc utun10 64/2 198.18.0.1 UGSc utun10 100.64/10 link#25 UCS utun3 |
8
duduke 2023-09-10 20:58:56 +08:00 via iPhone
路由表指定设备,曾经用这个折腾本地开发环境访问线上,还被警告了😂
|
9
FaiChou OP @leonshaw 嗯 谢谢,我大概写一下这个 **4 处理后的请求转发到其他网口** 这个逻辑:
```c // 创建两个套接字 int utun_sock = socket(AF_INET, SOCK_STREAM, 0); int eth0_sock = socket(AF_INET, SOCK_STREAM, 0); // 绑定 utun_sock 到 utun 的 IP 地址 struct sockaddr_in utun_addr; // 初始化 utun_addr bind(utun_sock, (struct sockaddr *)&utun_addr, sizeof(utun_addr)); // 绑定 eth0_sock 到 eth0 接口 setsockopt(eth0_sock, SOL_SOCKET, SO_BINDTODEVICE, "eth0", strlen("eth0")); // 读取数据并转发 char buffer[2048]; while (1) { // 从 utun_sock 读取数据 int n = read(utun_sock, buffer, sizeof(buffer)); if (n <= 0) { // 错误处理 break; } // 将数据写入 eth0_sock int m = write(eth0_sock, buffer, n); if (m <= 0) { // 错误处理 break; } } ``` 大概是这样吧。 |
11
leonshaw 2023-09-10 22:09:14 +08:00 via Android
@FaiChou tun 不是 socket 。linux 下一般是打开 /dev/tun 创建设备,读写这个 fd ,把读到的 IP 包按 VPN 协议封装后发出去。
|
12
FaiChou OP @leonshaw #11 我理解的应该没错吧,在 linux/unix 中一切设备和 I/O 操作都是通过文件描述符抽象的。"把读到的 IP 包按 VPN 协议封装后发出去" 发出去也需要经过物理网口吧,那最终也是需要经过绑定这个物理网口的 "eth0" 网络接口。所以应该是 app network->utun->eth0->network
|
13
FaiChou OP |
14
adoal 2023-09-10 22:57:13 +08:00
并不是 utun“接管”了系统的网络流量再按需转发到物理网卡。
utun 这种虚拟网卡,在操作系统的看来跟物理网卡一样,都是货真价实的网卡。 只不过物理网卡插的是物理线,虚拟网卡插的是虚拟线。 你就理解成在 utun 和远程服务器上的 tun 虚拟网卡之间拉了一根“线”(虽然不存在物理形状),让你的电脑多了一个网卡。只不过这根线的底层是一个通过物理网卡连的 VPN 而已。 那么电脑发出去的包是要走物理网卡还是虚拟网卡,只是正常的路由选择而已,不存在任何接管的手续。 只不过根据路由表确定走虚拟网卡的时候,再由虚拟网卡的驱动封包走虚拟网卡。 重复三遍: 对操作系统来说,虚拟网卡跟物理网卡是一样性质的网卡。 对操作系统来说,虚拟网卡跟物理网卡是一样性质的网卡。 对操作系统来说,虚拟网卡跟物理网卡是一样性质的网卡。 |
15
lcdtyph 2023-09-11 00:13:34 +08:00 via iPhone
|
17
zzzkkk 2023-09-11 00:31:39 +08:00 via Android
我的理解是楼主还没明白 tproxy 和 vpn 区别 对不对
就算 tproxy 也不是这种 socket 拷贝 |
18
zzzkkk 2023-09-11 00:32:35 +08:00 via Android
tproxy 是指 shadowsocks 的 ss-redir
|
20
slowmist 2023-09-11 09:04:39 +08:00
@lcdtyph
所有路由给 utun33 小🐱遇到 ip direct 的时候是怎么处理的 比如让 223.5.5.5 直连 走 default en10 出去 从系统原理上和代码上怎么理解 怎么避免产生回环的? netstat -rnf inet Routing tables Internet: Destination Gateway Flags Netif Expire default 192.168.88.1 UGScg en10 1 198.18.0.1 UGSc utun33 2/7 198.18.0.1 UGSc utun33 4/6 198.18.0.1 UGSc utun33 8/5 198.18.0.1 UGSc utun33 16/4 198.18.0.1 UGSc utun33 32/3 198.18.0.1 UGSc utun33 64/2 198.18.0.1 UGSc utun33 127 127.0.0.1 UCS lo0 127.0.0.1 127.0.0.1 UH lo0 128.0/1 198.18.0.1 UGSc utun33 169.254 link#7 UCS en10 ! 192.168.88 link#7 UCS en10 ! 192.168.88.1/32 link#7 UCS en10 ! 192.168.88.1 cd:cd:cd:cd:cd:cd UHLWIir en10 1179 192.168.88.8/32 link#7 UCS en10 ! 192.168.88.255 ff:ff:ff:ff:ff:ff UHLWbI en10 ! 198.18.0.1 198.18.0.1 UH utun33 Flags:路由的标志位 U:Up: 路由处于活动状态。 H:Host: 路由目标是单个主机。 G:Gateway: 所有发到目的地的网络传到这一远程系统上, 并由它决定最后发到哪里。 S:Static: 这个路由是手工配置的,不是由系统自动生成的。 C:Clone: 生成一个新的路由, 通过这个路由我们可以连接上这些机子。 这种类型的路由通常用于本地网络。 W:WasCloned: 指明一个路由――它是基于本地区域网络 (克隆) 路由自动配置的。 L:Link: 路由涉及到了以太网硬件。 Netif: 网络接口,如 en0 ,是我的机器默认 wifi 接口,而 lo0 表示本机(“回环设备”),也就是这条规则的包不通过 Lan 来发出 差不多是这样:? sudo route add -net 1.0.0.0/8 198.18.0.1 sudo route add -net 2.0.0.0/7 198.18.0.1 sudo route add -net 4.0.0.0/6 198.18.0.1 sudo route add -net 8.0.0.0/5 198.18.0.1 sudo route add -net 16.0.0.0/4 198.18.0.1 sudo route add -net 32.0.0.0/3 198.18.0.1 sudo route add -net 64.0.0.0/2 198.18.0.1 sudo route add -net 128.0.0.0/1 198.18.0.1 sudo route add -net 198.18.0.0/15 198.18.0.1 |
22
lcdtyph 2023-09-11 09:31:33 +08:00 via iPhone
@slowmist
各个平台都有类似的 socket option macos 上是 IP_BOUND_IF ,或者更简单的,直接 bind 你想使用的那个 interface 的地址再进行 connect 就行了 linux 是 SO_BINDTODEVICE 你想让 223.5.5.5 直连,setsockopt(IP_BOUND_IF) 然后 connect(223.5.5.5.5) 就行了 这样可以让 OS 做路由决策的时候忽略掉其他 interface 的路由,以此避免回环 |
23
lcdtyph 2023-09-11 09:40:38 +08:00 via iPhone
@slowmist
比如一些多网口负载均衡的服务就会自己 bind device ,比如 webrtc 好像会多网口同时尝试,quic 草案里也有类似的机制 要想完全避免这种情况只能上透明代理,把 clash 挪到网关上去;或者本机上防火墙规则 |
24
slowmist 2023-09-11 10:17:44 +08:00
@lcdtyph
原来是这样啊 解惑了 https://www.v2ex.com/t/590555 不知道这哥们解决了吗 firewall 不是问题 macos 上有 pf 之前测试 google one vbn 它的 utun 会路由所有的 v4 v6 第一次遇到遇到路由 v6 的 即使本机没有 v6 地址 而且会修改 dns 到 8.8.8.8 4.4 和对应的 v6 地址 cat /etc/resolv.conf 里面是这样显示的 系统设置里面的自定义 dns 已经失效了 google 的 dns 最优先了 不知道用什么方法实现的 |
25
julyclyde 2023-09-11 10:32:41 +08:00
@FaiChou “封装后”再发出去那就是另一次发送了。另一次就独立的进行全套的路由选择
以及,eth0 并没有对应的/dev/文件 |
26
lcdtyph 2023-09-11 10:56:31 +08:00 via iPhone
|