WireGuard proxy: Refactor#6287
Conversation
|
@LjhAUMEM 那之前是多peer多socket了? |
是的,之前在每个 conn.Endpoint 上都绑了一个 conn |
|
@LjhAUMEM 你的内置远程解析方法是resolveRemote吗?那之后会加udphop吗?不太懂这个改动,有没有性能测试呀? |
具体来说是 *Net 的 LookupHost,用的 udp 53,失败会 fallback tcp, |
|
有个 bug Xray-core/proxy/wireguard/bind.go Lines 42 to 52 in f66031b 如果读失败了,应该把 conn 丢掉再重新开一个。因为这个 conn 可能是链式代理,tcp 伪装的 udp packetconn 之前是这样写的 Xray-core/proxy/wireguard/bind.go Lines 136 to 142 in fdb9b61 |
我指的是netstack中的LookupHost 然后不检查addr是否为nil就直接parse的在 f66031b |
来源是 netip.Addr.String(),无需检查
所以我为什么说之前是非标的 bind,ReceiveFunc 里当 socket 失效应该返回 net.ErrClosed 告知给调用方,还有链式代理,不然你以为我为什么判断 io.EOF,之前的代码就是屎没想到还有人以这个 review 的
|
客户端配置服务端配置确实有bug,可以复现 |
我来当那个蒙古人 你的意思是 Exclude0122说的tcp伪装的udp packetCon会EOF从而不需要重开?还是我的理解有问题呢? |
|
那看来链式失效了返回的错误不是 io.EOF,或者没有错误?我得搭下环境,稍等 |
因为你后面continue了呀 我觉得 Xray-core/proxy/wireguard/bind.go Lines 42 to 52 in f66031b |
|
链式代理失败不一定返回 EOF,在安卓端不知道为啥会返回 |
|
Xray-core/proxy/wireguard/client.go Line 239 in f66031b 现在 |
那个是写方向的,不影响
不是只调用一次,ReceiveFunc 传了 net.ErrClosed 后上层会重新调用 listenFunc,刚刚出门拿外卖了,我先看看读方向什么情况 |
|
https://gist.github.com/bytecategory/c6d1a8551907a387a55a1ba0c29ccfe6 |
|
@Exclude0122 你说的对,读收到的也是 io.ErrClosedPipe,不过还发现即使返回 net.ErrClosed 也没触发 re bind,似乎只能手动 dev.BindUpdate 或者像之前那些用 chan,但是为了一个链式改回 chan 感觉不值得,我再看看
|
因为core里各种close满天飞的正常关闭和interrupt根本分不清 |
预期行为,在 send 里判断有滞后性,recv 才是第一时间得知关闭的地方,准确来说 recv 和 send 都不应该影响 bind 的生命周期,为了 fake connection 这种非标 conn 加一个判断已经是最大的让步,同时这个判断还要能做到区分来自 bind close 还是仅 fake conn close |
简单加个注释 |
|
你如果观察 wg 的日志在 recv 退出本身就有一条日志... |
|
本身链式也有问题不管成不成功 dial 都会返回一个 fake connection,还有流量计数现在应该是 break,重要你不看喜欢关注一些奇奇怪怪的方面 @bytecategory |
bytecategory
left a comment
There was a problem hiding this comment.
如果能做到同时发送多个UDP packets就好了,减少syscall次数,而不是逐个把bufs发出去
我的确抓不到重点 事已至此 合了吧
|
gso+ReadBatch/WriteBatch,linux 平台特定代码,你从我没分 udp4 udp6 来看就知道不会支持了 |
|
@LjhAUMEM 记得 rebase |
done,应该也 ready 了 |
|
As I read it, recovery is now driven entirely from the receive path: a fatal ReadFrom error → downFunc() (dev.Down) → teardown → re-init on the next Process(). Send only logs the error and breaks, so the write path never triggers a teardown on its own. That works for the chained-proxy / TCP-backed PacketConn case discussed above, because there a broken upstream surfaces as a read error ("closed pipe") and the receive goroutine unblocks. But the original report was about a plain connected UDP socket during a silent uplink outage (host stays up, no ICMP). In that situation ReadFrom may just keep blocking indefinitely and never return an error — so downFunc is never called, the device is never torn down, and writes keep going into a dead socket. From the code path alone it's not obvious what would break that goroutine out and trigger recovery in this case. So the question is: in the pure connected-UDP, no-ICMP outage scenario, what is expected to eventually error on the receive side and kick off the teardown? If nothing does, would it make sense to also trigger teardown from a Send error (or from a WireGuard handshake/keepalive timeout), instead of relying solely on the read path? |
|
@almatv54 可以测试一下,如果断网后再发送确实有 send 的错误而 read 没有需要提供完整日志,另外你有测试过其他实现在你说的断网后能恢复吗 |
|
The technical crux of my worry is exactly the asymmetry you mention. On a connected UDP socket, a send error usually only appears when there's an ICMP signal (e.g. port-unreachable). During a full uplink outage there's typically no ICMP at all, so neither send returns an error nor read unblocks — both sides just go silent. That's the case I'm unsure about: if neither path errors, nothing seems to trigger downFunc/teardown. |
|
@almatv54 Tests and logs are required. |

better wireguard
base #6275
{ "log": { "loglevel": "debug" }, "inbounds": [ { "tag": "wg-in", "listen": "127.0.0.1", "port": 1081, "protocol": "wireguard", "settings": { "secretKey": "QGTBbjHOXH7qPACfhW0a4qViSWz9AzlY4LG+jHaYMFY=", "peers": [ { "publicKey": "HVRq1Lj8MJkTZAI3HFL/ca0E7Dh40b5g8yzIVXQuZhU=" } ] } ,"streamSettings": { "finalmask": { "udp": [ { "type": "salamander", "settings": { "password": "1234" } } ] } } } ], "outbounds": [ { "protocol": "freedom" } ] }{ "log": { "loglevel": "debug" }, "inbounds": [ { "listen": "127.0.0.1", "port": 1080, "protocol": "socks", "settings": { "auth": "noauth", "udp": true } } ], "outbounds": [ { "protocol": "wireguard", "settings": { "secretKey": "8C36wsrFq5MLAZW9Y6l/IEzs362iiRNlm0GOA4S34kc=", "peers": [ { "publicKey": "5kuqMvvPx8NuGMDGJ/naRK/yRjtnmEIMByRTCF8kCXg=", "endpoint": "127.0.0.1:1081" } ] } ,"streamSettings": { "finalmask": { "udp": [ { "type": "salamander", "settings": { "password": "1234" } } ] } } } ] }