mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-02 06:56:36 +08:00
fix(relay): prioritize IPv4 in dual-stack outbound IP detection
修复 Relay 节点在双栈网络环境下可能上报 IPv6 地址,导致 Tunnel frpc 客户端无法连接 frps 的问题。 根因: - HTTPOutboundIPStrategy 首次尝试使用 tcp4 强制 IPv4 连接 - 失败时回退到双栈 tcp 客户端,此时可能通过 IPv6 连接并返回 IPv6 地址 - Relay 心跳将 IPv6 地址上报给 Server - Tunnel frpc 尝试连接该 IPv6 地址失败 修复: - 在回退到双栈客户端后,仍优先返回 IPv4 地址(通过 ip.To4() 转换) - 仅在完全无 IPv4 路由时才返回 IPv6 - 确保 Relay 心跳上报的 IP 与 frpc 连接兼容 影响范围: - pkg/geoip.HTTPOutboundIPStrategy.GetOutboundIP - internal/apps/relay/heartbeat.Service(通过 nodeip.DetectWithContext 调用) 测试: - 新增 TestHTTPOutboundIPStrategyPrioritizesIPv4 验证 IPv4 优先逻辑 - 所有现有测试保持通过
This commit is contained in:
+10
-1
@@ -109,7 +109,16 @@ func (s *HTTPOutboundIPStrategy) GetOutboundIP(ctx context.Context) (net.IP, err
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
return s.query(ctx, fallbackClient)
|
||||
ip, err = s.query(ctx, fallbackClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Always prioritize IPv4 for relay compatibility
|
||||
if ipv4 := ip.To4(); ipv4 != nil {
|
||||
return ipv4, nil
|
||||
}
|
||||
// Return IPv6 only if no IPv4 is available
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func (s *HTTPOutboundIPStrategy) query(ctx context.Context, client *http.Client) (net.IP, error) {
|
||||
|
||||
@@ -80,3 +80,45 @@ func TestHTTPOutboundIPStrategyRejectsPrivateIP(t *testing.T) {
|
||||
t.Fatal("expected private IP to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPOutboundIPStrategyPrioritizesIPv4(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
response string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "IPv4 address",
|
||||
response: `{"ip":"8.8.8.8"}`,
|
||||
want: "8.8.8.8",
|
||||
},
|
||||
{
|
||||
name: "IPv6 address normalized to IPv4",
|
||||
response: `{"ip":"::ffff:8.8.8.8"}`,
|
||||
want: "8.8.8.8",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(tt.response))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
strategy := NewHTTPOutboundIPStrategy(RealIPCCAdapter{URL: server.URL}, server.Client())
|
||||
ip, err := strategy.GetOutboundIP(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("GetOutboundIP failed: %v", err)
|
||||
}
|
||||
if ip.String() != tt.want {
|
||||
t.Errorf("GetOutboundIP() = %v, want %v", ip.String(), tt.want)
|
||||
}
|
||||
// Ensure it's an IPv4 address (4 bytes)
|
||||
if ipv4 := ip.To4(); ipv4 == nil {
|
||||
t.Errorf("expected IPv4 address, got %v", ip)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user