Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

去掉端口跳跃时的复制包的行为 #911

Open
keakon opened this issue Jan 24, 2024 · 8 comments
Open

去掉端口跳跃时的复制包的行为 #911

keakon opened this issue Jan 24, 2024 · 8 comments
Labels
enhancement New feature or request

Comments

@keakon
Copy link

keakon commented Jan 24, 2024

我在将 hysteria2 与 ClashMetaForAndroid 整合时发现一个问题,发生 hop 时,VPN 就变得无法使用了,虽然读写都仍然有数据,但实际无法使用。

在读代码时发现,现有实现是在端口跳跃时新建一个 UDPConn,把老的关闭,然后通过一个 chan 来缓存收到的包。
我修改了这段逻辑,直接使用原来的 UDPConn,只是在 WriteTo 时选择新的端口,ReadFrom 时无需做任何处理。因为本地端口没变,server:old_port 和 server:new_port 发的包都能收到,并不会造成丢包和连接断开。同时去掉了 chan,也能稍微降低一些开销。

而且这样修改后,与 ClashMetaForAndroid 可以一起正常工作了,原因未知。

#910

@keakon keakon added the enhancement New feature or request label Jan 24, 2024
@tobyxdd
Copy link
Collaborator

tobyxdd commented Jan 24, 2024

目前代码每次新建 UDPConn 就是为了换一个本地端口,来保证 src/dst 都发生变化以绕过 QoS。。

@keakon
Copy link
Author

keakon commented Jan 25, 2024

目前代码每次新建 UDPConn 就是为了换一个本地端口,来保证 src/dst 都发生变化以绕过 QoS。。

难道我被误导了?之前在改 clash 的代码时,是直接重新创建一个 client,这样两边的端口都会换;然后 clash 的开发者告诉我这样不行,会导致源端口被换,与原版实现不一致……
MetaCubeX/mihomo#982 (comment)

我先继续观察吧,目前没有发生 QoS,不知道源端口是不是一个 QoS 因素,毕竟各地的运营商策略不一样。

@haruue
Copy link
Collaborator

haruue commented Feb 27, 2024

发生 hop 时,VPN 就变得无法使用了,虽然读写都仍然有数据,但实际无法使用。

在端口跳跃时新建一个 UDPConn,把老的关闭

能找到这两者之间实际上的因果联系吗?

触发重连的逻辑时, 也是会重建整个客户端(和 UDPConn)的。

@keakon
Copy link
Author

keakon commented Feb 27, 2024

能找到这两者之间实际上的因果联系吗?

无法定位,我不太清楚安卓的运行机制,只是用 Github Actions 来构建,发现去掉复制的代码后就正常:
keakon/ClashMetaForAndroid@0c2c489
日志里确实能看到读写都有数据,但是手机却无法翻墙,而在第一次发生端口跳跃前的时间是能翻墙的。

而且安卓上会出现一些莫名其妙的现象,比如把 ticker 设成 30 秒,它可能 5 秒就触发了,甚至 1 秒内连续触发 2 次。我本以为是时间太短导致跳跃前后连接的状态不一致,于是还检查了上次跳跃的时间,如果太短就不跳跃,但仍然无法解决跳跃后不能翻墙的问题。

@haruue
Copy link
Collaborator

haruue commented Feb 27, 2024

恰好我曾经是搞 Android 的。 我看了一下你的仓库, hysteria2 的接入似乎有点问题。

Hysteria2 Adapter 的 DialContextListenPacketContext 两个方法, 外部都会额外传入一个 opts 参数, 但是在接入 hysteria2 的时候都没有去使用。

你可以对比 Hysteria1 Adapter 相同函数的实现, 这里用 opts 创建了一个 utils.PacketDialer , 并最终把它深入地传入到 Hysteria Client 中, 最终是使用这个 dialer 去创建和服务器的连接的: 1 2 3 4

为什么必须要用这个 dialer 去创建连接呢? 这是因为在开发 Android VPN 的过程中, 需要对连接 VPN 服务器的 socket 调用 VpnService.protect(), 这个系统 API 会将传入的 socket fd 标记为直连, 否则连接 VPN 服务器的流量可能被路由到 VPN 的 tun 中, 导致环路。

连接服务器时最终调用的 dialer.ListenPacket() 实际上在 Android 上的实现是 这个函数, 注意这里的 DefaultSocketHook, 实际上是 在 ClashForAndroid 初始化时被设置上的, 这里的 app.MarkSocket(int(fd)) 最终通过 C 和 JNI 调用了 VpnService.protect()

由于在接入 Hysteria2 时没有进行此项修改, 在 udp hop 发生之后, 新创建的 socket 被路由到了 VPN 的 tun 中。 因此才出现了你提到的那种 hop 之后就连不上的问题。

或许我们应该为 Hysteria2 添加一个 API, 用于设置所有 dial 直连 socket 时应当使用的 dialer。 但是, ClashForAndroid 仍然需要正确接入之后, 才可能正常使用 udp hop。

@keakon
Copy link
Author

keakon commented Feb 27, 2024

好吧,我在 mac 上测试命令行时发现这个参数没传进来,所以没去用它,原来是给其他库的调用者使用的。。

@Sqkam
Copy link

Sqkam commented Mar 6, 2024

目前代码每次新建 UDPConn 就是为了换一个本地端口,来保证 src/dst 都发生变化以绕过 QoS。。

难道我被误导了?之前在改 clash 的代码时,是直接重新创建一个 client,这样两边的端口都会换;然后 clash 的开发者告诉我这样不行,会导致源端口被换,与原版实现不一致…… MetaCubeX/mihomo#982 (comment)

我先继续观察吧,目前没有发生 QoS,不知道源端口是不是一个 QoS 因素,毕竟各地的运营商策略不一样。

没误导你,我试了你这个办法,会导致sing-quic新建一个链接去链接服务器,源端口自然会换

@Sqkam
Copy link

Sqkam commented Mar 6, 2024

看来只能改sing-quic了,我想想办法把quicConn改了😁

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants