macOS 中是如何将 utun 网卡接口接管所有网络请求的?

242 天前
 FaiChou

比如使用 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 不需要手动设置网络的转发吗?

我所理解的过程是这样的:

  1. 新建一个 TUN 网络接口
  2. 接管系统的请求
  3. 将请求处理
  4. 处理后的接口转发到其他网络接口
  5. 收到网络请求后其他网络接口转发到 TUN 网络接口中
  6. 处理收到的网络请求
  7. 传递给上层应用

所以总结下我的问题,第二步是如何接管系统所有请求的?第四步是如何转发到其他接口的?在 Linux 中应该是需要手动写吧?在 macOS 中是用 NE 里哪一些 API ?

另外,在 iOS 端也是和 macOS 一样的吧?开启 QX 应用后,在设置中可以看到有 utun 接口。类似的应用都是用 TUN 模式的( Loon 用 TUN 和 HTTP mode 两种模式)。

1915 次点击
所在节点    macOS
28 条回复
julyclyde
242 天前
什么叫“将……接管”?
恕我无法对你的汉语进行语法分析
MrGba2z
242 天前
> 接管系统的请求

就好比你装了雷电物理网卡,你在系统里选择优先使用这个网卡,那么系统就会默认使用他。大部分情况下软件都会按照系统的优先级使用第一个网络,但是软件也可以不遵守规则强制使用某一个,这时候 tun 代理就失效了。
FaiChou
242 天前
@julyclyde 哈哈 标题起得草率了,没有回去读一遍。原意是想表达某些应用比如 clashxpro 是如何将它新建的 utun interface 起到接管整个系统网络请求的。
leonshaw
242 天前
2 是把流通过操作系统导入 tun ,在桌面端一般是路由和 nf/pf/wfp 等,移动端就是 VPN 相关 API
4 是正常的 socket
lcdtyph
242 天前
@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
fuis
242 天前
可以参考这个项目的 README 就可以了解了

https://github.com/songgao/water

赞同 #5
FaiChou
242 天前
@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
duduke
242 天前
路由表指定设备,曾经用这个折腾本地开发环境访问线上,还被警告了😂
FaiChou
242 天前
@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;
}
}

```

大概是这样吧。
julyclyde
242 天前
@lcdtyph 让我想起以前 OpenVPN 的,做了两条就覆盖了所有地址
leonshaw
242 天前
@FaiChou tun 不是 socket 。linux 下一般是打开 /dev/tun 创建设备,读写这个 fd ,把读到的 IP 包按 VPN 协议封装后发出去。
FaiChou
242 天前
@leonshaw #11 我理解的应该没错吧,在 linux/unix 中一切设备和 I/O 操作都是通过文件描述符抽象的。"把读到的 IP 包按 VPN 协议封装后发出去" 发出去也需要经过物理网口吧,那最终也是需要经过绑定这个物理网口的 "eth0" 网络接口。所以应该是 app network->utun->eth0->network
FaiChou
242 天前
@julyclyde #10
@lcdtyph

clashxpro 开增强后,netstat -rn 给出的 `1 2/7 4/6 8/5 16/4 32/3 64/2` 不是标准的 IP 地址或网络地址,那它是怎么展开的,代表哪些地址段?
adoal
242 天前
并不是 utun“接管”了系统的网络流量再按需转发到物理网卡。
utun 这种虚拟网卡,在操作系统的看来跟物理网卡一样,都是货真价实的网卡。
只不过物理网卡插的是物理线,虚拟网卡插的是虚拟线。
你就理解成在 utun 和远程服务器上的 tun 虚拟网卡之间拉了一根“线”(虽然不存在物理形状),让你的电脑多了一个网卡。只不过这根线的底层是一个通过物理网卡连的 VPN 而已。
那么电脑发出去的包是要走物理网卡还是虚拟网卡,只是正常的路由选择而已,不存在任何接管的手续。
只不过根据路由表确定走虚拟网卡的时候,再由虚拟网卡的驱动封包走虚拟网卡。

重复三遍:
对操作系统来说,虚拟网卡跟物理网卡是一样性质的网卡。
对操作系统来说,虚拟网卡跟物理网卡是一样性质的网卡。
对操作系统来说,虚拟网卡跟物理网卡是一样性质的网卡。
lcdtyph
242 天前
@FaiChou
我记得这是 bsd 特有的表达法,就是后缀零可以省略掉
比如说 16.0.0.0/4 可以直接省略成 16/4
lcdtyph
242 天前
@julyclyde
是的,0.0.0.0/1 和 128.0.0.0/1
zzzkkk
242 天前
我的理解是楼主还没明白 tproxy 和 vpn 区别 对不对
就算 tproxy 也不是这种 socket 拷贝
zzzkkk
242 天前
tproxy 是指 shadowsocks 的 ss-redir
FaiChou
241 天前
@lcdtyph 哦原来是这样啊,和 ipv6 一样省略连起来的 0 。
slowmist
241 天前
@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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/972504

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX