hysteria icon indicating copy to clipboard operation
hysteria copied to clipboard

[BUG] Hysteria未监听所有地址

Open wangxiaoerYah opened this issue 1 year ago • 7 comments

问题详情

我在单网卡上绑定了5个ipv4地址,但是hysteria在使用"listen": ":8443",监听8443端口时并不会使用所有地址,经过测试发现是hysteria的问题,xray或者v2ray它们启动后我可以通过其中任何ip连接vps。

例如: default via 40.207.168.21 dev enp2s0 proto static 40.207.168.20/28 dev enp2s0 proto kernel scope link src 40.207.168.22 上面的ip都做了修改肯定不是真正ip,仅作解释。 hysteria只能通过其中的40.207.168.22连接。但是40.207.168.23,40.207.168.24,40.207.168.25,这些ip地址是和40.207.168.22在同一张网卡上绑定的。

如果我手动将"listen": ":8443",修改为"listen": "40.207.168.24:8443",我就可以通过40.207.168.24连接,不修改就不行。

目前仅测试了ipv4,不知道ipv6有没有这个问题,毕竟很多vps服务商ipv6地址都是免费给,有的一次给很多个,他们都是一个网段甚至一个网关。

服务端安装信息或者一键脚本信息

hysteria 1.3.4

VPS 信息

服务端配置

"listen": ":8443"

服务端日志

客户端安装信息

hysteria 1.3.4

客户端配置

客户端运行环境(操作系统)

客户端日志

wangxiaoerYah avatar Mar 24 '23 21:03 wangxiaoerYah

我查看了同样使用quic-go的v2ray-5项目,发现他们对于监听地址查找本机存在的ip做了专门的处理 address.go

我想应该是个参考文件。

wangxiaoerYah avatar Mar 25 '23 17:03 wangxiaoerYah

如果同一张网卡是有多个同网段的ip地址,那么Linux会默认指定排在最前面的ip作为这个网段的默认ip,也就是 40.207.168.20/28 dev enp2s0 proto kernel scope link src 40.207.168.22

Hysteria 启动时就使用了默认ip作为监听地址? 也就是"listen": ":8443"="listen": "40.207.168.22:8443"

wangxiaoerYah avatar Mar 25 '23 17:03 wangxiaoerYah

@tobyxdd @haruue

wangxiaoerYah avatar Mar 25 '23 17:03 wangxiaoerYah

这种问题应该是因为一个 bind()* 的 UDP socket , 对它调用 sendto() 无法指定源 IP , 网卡会根据路由表的 prefered src 发出去。

也就是说不是服务端没收到, 而是给客户端发响应包的时候, 使用了另一个源地址, 这种情况下, 客户端不会认为这是个有效响应(并且通常会被 IPv4 NAT 丢弃)。

任何基于 TCP 的协议都不存在这个问题, 因为 TCP 在 accpet() 之后, 源地址和目标地址都被固定了。

我们将会研究其他 h3/quic 应用采用的方案。

haruue avatar Mar 26 '23 03:03 haruue

这种问题应该是因为一个 bind()* 的 UDP socket , 对它调用 sendto() 无法指定源 IP , 网卡会根据路由表的 prefered src 发出去。

也就是说不是服务端没收到, 而是给客户端发响应包的时候, 使用了另一个源地址, 这种情况下, 客户端不会认为这是个有效响应(并且通常会被 IPv4 NAT 丢弃)。

任何基于 TCP 的协议都不存在这个问题, 因为 TCP 在 accpet() 之后, 源地址和目标地址都被固定了。

我们将会研究其他 h3/quic 应用采用的方案。

是否可以通过手动调整路由表来修复?

wangxiaoerYah avatar Mar 26 '23 06:03 wangxiaoerYah

是否可以通过手动调整路由表来修复?

我认为可以通过 iptables 的 DNAT 功能来修复, 或者说作为绕过这个问题的一种方案。 利用 NAT 的特性, 把对应的 UDP socket 的响应包的源地址改成正确的源地址。

作为例子, 我开了一台虚拟机, 并给它的默认网络接口添加了 10.11.6.250/24 10.11.6.251/24 10.11.6.252/24 10.11.6.253/24 这 4 个地址。

首先我要确认从这台主机发包默认会使用的地址, 这一步可以使用 ip route get 命令, 像下面这样, 也可以把 1.1.1.1 换成任意外部网络的 IP 地址。

# ip route get 1.1.1.1
1.1.1.1 via 10.11.6.1 dev host0 src 10.11.6.250 uid 0 
    cache 

上面输出中出现的 10.11.6.250 即为这个机器向外发包默认使用的地址。

然后, 就可以利用 iptables 的 DNAT 功能, 把发往 10.11.6.251 10.11.6.252 10.11.6.253 的 Hysteria 端口的 UDP 包都 NAT 到 10.11.6.250 。 在这个例子中, 我把 Hysteria 开在 10443 端口上。

iptables -t nat -N hysteria-dnat
iptables -t nat -A hysteria-dnat -p udp -m udp --dport 10443 -j DNAT --to-destination 10.11.6.250
iptables -t nat -A PREROUTING -d 10.11.6.251 -j hysteria-dnat
iptables -t nat -A PREROUTING -d 10.11.6.252 -j hysteria-dnat
iptables -t nat -A PREROUTING -d 10.11.6.253 -j hysteria-dnat

这样操作之后, 外部的 hysteria 客户端通过 10.11.6.251 连接 hysteria 服务端, 也能收到源地址为 10.11.6.251 的回应了。 其他地址亦然。

另外, 测试过程中还发现, hysteria 客户端实际上并不关心回应包的源地址是否和请求时的相同(客户端也是 bind :: 的), 也就是说, 即使不进行上面这样的操作, 也是有可能连上的, 这种情况下外部的流量看起来就是客户端发送 UDP 请求给 10.11.6.251 , 然后收到了从 10.11.6.250 发来的响应。 当然你的运营商 NAT 仍然可能阻止这种流量(尤其是对于 IPv4)。

haruue avatar Mar 26 '23 07:03 haruue

"server": "[2605:6400:20:1871::2]:123",

如果服务器采用ipv6格式,程序常常闪退, ipv4倒是没发现.

f4nff avatar Apr 07 '23 03:04 f4nff