mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-30 14:16:38 +00:00
Address review on relay server IP signaling
Mark relayServerAddress and relayServerIP as optional in the signal proto, return the relay instance address and IP atomically from Manager.RelayInstanceAddress to avoid divergence across reconnections, and split the relay client constructor into NewClient and NewClientWithServerIP. Rename fallback terminology to server IP throughout.
This commit is contained in:
@@ -150,7 +150,7 @@ func (cc *connContainer) close() {
|
||||
type Client struct {
|
||||
log *log.Entry
|
||||
connectionURL string
|
||||
fallbackIP netip.Addr
|
||||
serverIP netip.Addr
|
||||
authTokenStore *auth.TokenStore
|
||||
hashedID messages.PeerID
|
||||
|
||||
@@ -175,16 +175,21 @@ type Client struct {
|
||||
}
|
||||
|
||||
// NewClient creates a new client for the relay server. The client is not connected to the server until the Connect
|
||||
// is called. fallbackIP, when valid, is used as a dial-time fallback if the FQDN-based dial fails. TLS
|
||||
// verification still uses the FQDN from serverURL via SNI.
|
||||
func NewClient(serverURL string, fallbackIP netip.Addr, authTokenStore *auth.TokenStore, peerID string, mtu uint16) *Client {
|
||||
// is called.
|
||||
func NewClient(serverURL string, authTokenStore *auth.TokenStore, peerID string, mtu uint16) *Client {
|
||||
return NewClientWithServerIP(serverURL, netip.Addr{}, authTokenStore, peerID, mtu)
|
||||
}
|
||||
|
||||
// NewClientWithServerIP creates a new client for the relay server with a known server IP. serverIP, when valid, is
|
||||
// used as a dial target if the FQDN-based dial fails. TLS verification still uses the FQDN from serverURL via SNI.
|
||||
func NewClientWithServerIP(serverURL string, serverIP netip.Addr, authTokenStore *auth.TokenStore, peerID string, mtu uint16) *Client {
|
||||
hashedID := messages.HashID(peerID)
|
||||
relayLog := log.WithFields(log.Fields{"relay": serverURL})
|
||||
|
||||
c := &Client{
|
||||
log: relayLog,
|
||||
connectionURL: serverURL,
|
||||
fallbackIP: fallbackIP,
|
||||
serverIP: serverIP,
|
||||
authTokenStore: authTokenStore,
|
||||
hashedID: hashedID,
|
||||
mtu: mtu,
|
||||
@@ -378,11 +383,11 @@ func (c *Client) connect(ctx context.Context) (*RelayAddr, error) {
|
||||
rd := dialer.NewRaceDial(c.log, dialer.DefaultConnectionTimeout, c.connectionURL, dialers...)
|
||||
conn, err := rd.Dial(ctx)
|
||||
if err != nil {
|
||||
fallbackConn, fbErr := c.dialFallback(ctx, dialers)
|
||||
if fbErr != nil {
|
||||
return nil, fmt.Errorf("primary dial: %w; fallback dial: %w", err, fbErr)
|
||||
directConn, dErr := c.dialDirect(ctx, dialers)
|
||||
if dErr != nil {
|
||||
return nil, fmt.Errorf("dial via FQDN: %w; dial via server IP: %w", err, dErr)
|
||||
}
|
||||
conn = fallbackConn
|
||||
conn = directConn
|
||||
}
|
||||
c.relayConn = conn
|
||||
|
||||
@@ -398,22 +403,21 @@ func (c *Client) connect(ctx context.Context) (*RelayAddr, error) {
|
||||
return instanceURL, nil
|
||||
}
|
||||
|
||||
// dialFallback retries the dial against c.fallbackIP, preserving the
|
||||
// original FQDN as the TLS ServerName for SNI. Returns an error if no
|
||||
// fallback IP is configured or if the substituted URL is malformed.
|
||||
func (c *Client) dialFallback(ctx context.Context, dialers []dialer.DialeFn) (net.Conn, error) {
|
||||
if !c.fallbackIP.IsValid() || c.fallbackIP.IsUnspecified() {
|
||||
return nil, errors.New("no usable fallback IP configured")
|
||||
// dialDirect retries the dial against c.serverIP, preserving the original FQDN as the TLS ServerName for SNI.
|
||||
// Returns an error if no usable server IP is configured or if the substituted URL is malformed.
|
||||
func (c *Client) dialDirect(ctx context.Context, dialers []dialer.DialeFn) (net.Conn, error) {
|
||||
if !c.serverIP.IsValid() || c.serverIP.IsUnspecified() {
|
||||
return nil, errors.New("no usable server IP configured")
|
||||
}
|
||||
|
||||
fallbackURL, serverName, err := substituteHost(c.connectionURL, c.fallbackIP)
|
||||
directURL, serverName, err := substituteHost(c.connectionURL, c.serverIP)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("substitute host: %w", err)
|
||||
}
|
||||
|
||||
c.log.Infof("primary dial failed, retrying via fallback IP %s (SNI=%s)", c.fallbackIP, serverName)
|
||||
c.log.Infof("FQDN dial failed, retrying via server IP %s (SNI=%s)", c.serverIP, serverName)
|
||||
|
||||
rd := dialer.NewRaceDial(c.log, dialer.DefaultConnectionTimeout, fallbackURL, dialers...).
|
||||
rd := dialer.NewRaceDial(c.log, dialer.DefaultConnectionTimeout, directURL, dialers...).
|
||||
WithServerName(serverName)
|
||||
return rd.Dial(ctx)
|
||||
}
|
||||
@@ -431,7 +435,7 @@ func substituteHost(serverURL string, ip netip.Addr) (string, string, error) {
|
||||
return "", "", fmt.Errorf("invalid relay URL %q", serverURL)
|
||||
}
|
||||
if !ip.IsValid() {
|
||||
return "", "", errors.New("invalid fallback IP")
|
||||
return "", "", errors.New("invalid server IP")
|
||||
}
|
||||
origHost := u.Hostname()
|
||||
if _, err := netip.ParseAddr(origHost); err == nil {
|
||||
|
||||
@@ -15,10 +15,10 @@ import (
|
||||
"github.com/netbirdio/netbird/shared/relay/auth/allow"
|
||||
)
|
||||
|
||||
// TestClient_FallbackIPRecoversFromUnresolvableFQDN verifies that when the
|
||||
// TestClient_ServerIPRecoversFromUnresolvableFQDN verifies that when the
|
||||
// primary FQDN-based dial fails (unresolvable .invalid host), Connect
|
||||
// recovers via the fallback IP and SNI still uses the FQDN.
|
||||
func TestClient_FallbackIPRecoversFromUnresolvableFQDN(t *testing.T) {
|
||||
// recovers via the server IP and SNI still uses the FQDN.
|
||||
func TestClient_ServerIPRecoversFromUnresolvableFQDN(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -49,19 +49,19 @@ func TestClient_FallbackIPRecoversFromUnresolvableFQDN(t *testing.T) {
|
||||
t.Fatalf("server failed to start: %s", err)
|
||||
}
|
||||
|
||||
t.Run("no fallback IP, primary fails", func(t *testing.T) {
|
||||
c := NewClient(srvCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice-nofallback", iface.DefaultMTU)
|
||||
t.Run("no server IP, primary fails", func(t *testing.T) {
|
||||
c := NewClient(srvCfg.ExposedAddress, hmacTokenStore, "alice-noip", iface.DefaultMTU)
|
||||
err := c.Connect(ctx)
|
||||
if err == nil {
|
||||
_ = c.Close()
|
||||
t.Fatalf("expected connect to fail without fallback IP, got nil")
|
||||
t.Fatalf("expected connect to fail without server IP, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fallback IP recovers", func(t *testing.T) {
|
||||
c := NewClient(srvCfg.ExposedAddress, netip.MustParseAddr("127.0.0.1"), hmacTokenStore, "alice-fallback", iface.DefaultMTU)
|
||||
t.Run("server IP recovers", func(t *testing.T) {
|
||||
c := NewClientWithServerIP(srvCfg.ExposedAddress, netip.MustParseAddr("127.0.0.1"), hmacTokenStore, "alice-with-ip", iface.DefaultMTU)
|
||||
if err := c.Connect(ctx); err != nil {
|
||||
t.Fatalf("connect with fallback IP: %s", err)
|
||||
t.Fatalf("connect with server IP: %s", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = c.Close() })
|
||||
|
||||
@@ -77,7 +77,7 @@ func TestClient_FallbackIPRecoversFromUnresolvableFQDN(t *testing.T) {
|
||||
// TestClient_ConnectedIPAfterFQDNDial verifies ConnectedIP returns the
|
||||
// resolved IP after a successful FQDN-based dial. The underlying socket's
|
||||
// RemoteAddr must be exposed through the dialer wrappers; if it returns
|
||||
// the dial-time URL instead, ConnectedIP returns empty and the fallback
|
||||
// the dial-time URL instead, ConnectedIP returns empty and the dial
|
||||
// IP we advertise to peers is empty too.
|
||||
func TestClient_ConnectedIPAfterFQDNDial(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
@@ -105,7 +105,7 @@ func TestClient_ConnectedIPAfterFQDNDial(t *testing.T) {
|
||||
t.Fatalf("server failed to start: %s", err)
|
||||
}
|
||||
|
||||
c := NewClient(srvCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice-fqdn", iface.DefaultMTU)
|
||||
c := NewClient(srvCfg.ExposedAddress, hmacTokenStore, "alice-fqdn", iface.DefaultMTU)
|
||||
if err := c.Connect(ctx); err != nil {
|
||||
t.Fatalf("connect: %s", err)
|
||||
}
|
||||
@@ -141,7 +141,7 @@ func TestSubstituteHost(t *testing.T) {
|
||||
wantServerName: "relay.example.com",
|
||||
},
|
||||
{
|
||||
name: "ipv6 fallback bracketed",
|
||||
name: "ipv6 server IP bracketed",
|
||||
serverURL: "rels://relay.example.com:443",
|
||||
ip: "2001:db8::1",
|
||||
wantURL: "rels://[2001:db8::1]:443",
|
||||
@@ -169,7 +169,7 @@ func TestSubstituteHost(t *testing.T) {
|
||||
wantServerName: "",
|
||||
},
|
||||
{
|
||||
name: "ipv6 fallback no port",
|
||||
name: "ipv6 server IP no port",
|
||||
serverURL: "rels://relay.example.com",
|
||||
ip: "2001:db8::1",
|
||||
wantURL: "rels://[2001:db8::1]",
|
||||
@@ -215,7 +215,7 @@ func TestSubstituteHost(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClient_ConnectedIPEmptyWhenNotConnected(t *testing.T) {
|
||||
c := NewClient("rel://example.invalid:80", netip.Addr{}, hmacTokenStore, "x", iface.DefaultMTU)
|
||||
c := NewClient("rel://example.invalid:80", hmacTokenStore, "x", iface.DefaultMTU)
|
||||
if got := c.ConnectedIP(); got.IsValid() {
|
||||
t.Fatalf("ConnectedIP on disconnected client = %q, want zero", got)
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package client
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -69,7 +68,7 @@ func TestClient(t *testing.T) {
|
||||
t.Fatalf("failed to start server: %s", err)
|
||||
}
|
||||
t.Log("alice connecting to server")
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
err = clientAlice.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to server: %s", err)
|
||||
@@ -77,7 +76,7 @@ func TestClient(t *testing.T) {
|
||||
defer clientAlice.Close()
|
||||
|
||||
t.Log("placeholder connecting to server")
|
||||
clientPlaceHolder := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "clientPlaceHolder", iface.DefaultMTU)
|
||||
clientPlaceHolder := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "clientPlaceHolder", iface.DefaultMTU)
|
||||
err = clientPlaceHolder.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to server: %s", err)
|
||||
@@ -85,7 +84,7 @@ func TestClient(t *testing.T) {
|
||||
defer clientPlaceHolder.Close()
|
||||
|
||||
t.Log("Bob connecting to server")
|
||||
clientBob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "bob", iface.DefaultMTU)
|
||||
clientBob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "bob", iface.DefaultMTU)
|
||||
err = clientBob.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to server: %s", err)
|
||||
@@ -145,7 +144,7 @@ func TestRegistration(t *testing.T) {
|
||||
t.Fatalf("failed to start server: %s", err)
|
||||
}
|
||||
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
err = clientAlice.Connect(ctx)
|
||||
if err != nil {
|
||||
_ = srv.Shutdown(ctx)
|
||||
@@ -185,7 +184,7 @@ func TestRegistrationTimeout(t *testing.T) {
|
||||
_ = fakeTCPListener.Close()
|
||||
}(fakeTCPListener)
|
||||
|
||||
clientAlice := NewClient("127.0.0.1:50201", netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
clientAlice := NewClient("127.0.0.1:50201", hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
err = clientAlice.Connect(ctx)
|
||||
if err == nil {
|
||||
t.Errorf("failed to connect to server: %s", err)
|
||||
@@ -228,7 +227,7 @@ func TestEcho(t *testing.T) {
|
||||
t.Fatalf("failed to start server: %s", err)
|
||||
}
|
||||
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idAlice, iface.DefaultMTU)
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idAlice, iface.DefaultMTU)
|
||||
err = clientAlice.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to server: %s", err)
|
||||
@@ -240,7 +239,7 @@ func TestEcho(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
clientBob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idBob, iface.DefaultMTU)
|
||||
clientBob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idBob, iface.DefaultMTU)
|
||||
err = clientBob.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to server: %s", err)
|
||||
@@ -320,7 +319,7 @@ func TestBindToUnavailabePeer(t *testing.T) {
|
||||
t.Fatalf("failed to start server: %s", err)
|
||||
}
|
||||
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
err = clientAlice.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("failed to connect to server: %s", err)
|
||||
@@ -368,13 +367,13 @@ func TestBindReconnect(t *testing.T) {
|
||||
t.Fatalf("failed to start server: %s", err)
|
||||
}
|
||||
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
err = clientAlice.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to server: %s", err)
|
||||
}
|
||||
|
||||
clientBob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "bob", iface.DefaultMTU)
|
||||
clientBob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "bob", iface.DefaultMTU)
|
||||
err = clientBob.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("failed to connect to server: %s", err)
|
||||
@@ -396,7 +395,7 @@ func TestBindReconnect(t *testing.T) {
|
||||
t.Errorf("failed to close client: %s", err)
|
||||
}
|
||||
|
||||
clientAlice = NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
clientAlice = NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
err = clientAlice.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("failed to connect to server: %s", err)
|
||||
@@ -471,13 +470,13 @@ func TestCloseConn(t *testing.T) {
|
||||
t.Fatalf("failed to start server: %s", err)
|
||||
}
|
||||
|
||||
bob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "bob", iface.DefaultMTU)
|
||||
bob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "bob", iface.DefaultMTU)
|
||||
err = bob.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("failed to connect to server: %s", err)
|
||||
}
|
||||
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
err = clientAlice.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("failed to connect to server: %s", err)
|
||||
@@ -535,13 +534,13 @@ func TestCloseRelayConn(t *testing.T) {
|
||||
t.Fatalf("failed to start server: %s", err)
|
||||
}
|
||||
|
||||
bob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "bob", iface.DefaultMTU)
|
||||
bob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "bob", iface.DefaultMTU)
|
||||
err = bob.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to server: %s", err)
|
||||
}
|
||||
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, "alice", iface.DefaultMTU)
|
||||
err = clientAlice.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to server: %s", err)
|
||||
@@ -591,7 +590,7 @@ func TestCloseByServer(t *testing.T) {
|
||||
|
||||
idAlice := "alice"
|
||||
log.Debugf("connect by alice")
|
||||
relayClient := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idAlice, iface.DefaultMTU)
|
||||
relayClient := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idAlice, iface.DefaultMTU)
|
||||
if err = relayClient.Connect(ctx); err != nil {
|
||||
log.Fatalf("failed to connect to server: %s", err)
|
||||
}
|
||||
@@ -649,7 +648,7 @@ func TestCloseByClient(t *testing.T) {
|
||||
|
||||
idAlice := "alice"
|
||||
log.Debugf("connect by alice")
|
||||
relayClient := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idAlice, iface.DefaultMTU)
|
||||
relayClient := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idAlice, iface.DefaultMTU)
|
||||
err = relayClient.Connect(ctx)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to connect to server: %s", err)
|
||||
@@ -702,7 +701,7 @@ func TestCloseNotDrainedChannel(t *testing.T) {
|
||||
t.Fatalf("failed to start server: %s", err)
|
||||
}
|
||||
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idAlice, iface.DefaultMTU)
|
||||
clientAlice := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idAlice, iface.DefaultMTU)
|
||||
err = clientAlice.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to server: %s", err)
|
||||
@@ -714,7 +713,7 @@ func TestCloseNotDrainedChannel(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
clientBob := NewClient(serverCfg.ExposedAddress, netip.Addr{}, hmacTokenStore, idBob, iface.DefaultMTU)
|
||||
clientBob := NewClient(serverCfg.ExposedAddress, hmacTokenStore, idBob, iface.DefaultMTU)
|
||||
err = clientBob.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to server: %s", err)
|
||||
|
||||
@@ -132,9 +132,9 @@ func (m *Manager) Serve() error {
|
||||
// established via the relay server. If the peer is on a different relay server, the manager will establish a new
|
||||
// connection to the relay server. It returns back with a net.Conn what represent the remote peer connection.
|
||||
//
|
||||
// fallbackIP, when valid and serverAddress is foreign, is used as a dial-time fallback if the FQDN-based
|
||||
// dial fails. Ignored for the local home-server path. TLS verification still uses the FQDN via SNI.
|
||||
func (m *Manager) OpenConn(ctx context.Context, serverAddress, peerKey string, fallbackIP netip.Addr) (net.Conn, error) {
|
||||
// serverIP, when valid and serverAddress is foreign, is used as a dial target if the FQDN-based dial fails.
|
||||
// Ignored for the local home-server path. TLS verification still uses the FQDN via SNI.
|
||||
func (m *Manager) OpenConn(ctx context.Context, serverAddress, peerKey string, serverIP netip.Addr) (net.Conn, error) {
|
||||
m.relayClientMu.RLock()
|
||||
defer m.relayClientMu.RUnlock()
|
||||
|
||||
@@ -155,7 +155,7 @@ func (m *Manager) OpenConn(ctx context.Context, serverAddress, peerKey string, f
|
||||
netConn, err = m.relayClient.OpenConn(ctx, peerKey)
|
||||
} else {
|
||||
log.Debugf("open peer connection via foreign server: %s", serverAddress)
|
||||
netConn, err = m.openConnVia(ctx, serverAddress, peerKey, fallbackIP)
|
||||
netConn, err = m.openConnVia(ctx, serverAddress, peerKey, serverIP)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -207,29 +207,22 @@ func (m *Manager) AddCloseListener(serverAddress string, onClosedListener OnServ
|
||||
return nil
|
||||
}
|
||||
|
||||
// RelayInstanceAddress returns the address of the permanent relay server. It could change if the network connection is
|
||||
// lost. This address will be sent to the target peer to choose the common relay server for the communication.
|
||||
func (m *Manager) RelayInstanceAddress() (string, error) {
|
||||
// RelayInstanceAddress returns the address and resolved IP of the permanent relay server. It could change if the
|
||||
// network connection is lost. The address is sent to the target peer to choose the common relay server for the
|
||||
// communication; the IP is sent alongside so remote peers can dial directly without their own DNS lookup. Both
|
||||
// values are read under the same lock so they cannot diverge across a reconnection.
|
||||
func (m *Manager) RelayInstanceAddress() (string, netip.Addr, error) {
|
||||
m.relayClientMu.RLock()
|
||||
defer m.relayClientMu.RUnlock()
|
||||
|
||||
if m.relayClient == nil {
|
||||
return "", ErrRelayClientNotConnected
|
||||
return "", netip.Addr{}, ErrRelayClientNotConnected
|
||||
}
|
||||
return m.relayClient.ServerInstanceURL()
|
||||
}
|
||||
|
||||
// RelayInstanceIP returns the IP address of the live home relay connection.
|
||||
// Zero value if not connected. Sent alongside RelayInstanceAddress so remote
|
||||
// peers can dial directly without their own DNS lookup.
|
||||
func (m *Manager) RelayInstanceIP() netip.Addr {
|
||||
m.relayClientMu.RLock()
|
||||
defer m.relayClientMu.RUnlock()
|
||||
|
||||
if m.relayClient == nil {
|
||||
return netip.Addr{}
|
||||
addr, err := m.relayClient.ServerInstanceURL()
|
||||
if err != nil {
|
||||
return "", netip.Addr{}, err
|
||||
}
|
||||
return m.relayClient.ConnectedIP()
|
||||
return addr, m.relayClient.ConnectedIP(), nil
|
||||
}
|
||||
|
||||
// ServerURLs returns the addresses of the relay servers.
|
||||
@@ -253,7 +246,7 @@ func (m *Manager) UpdateToken(token *relayAuth.Token) error {
|
||||
return m.tokenStore.UpdateToken(token)
|
||||
}
|
||||
|
||||
func (m *Manager) openConnVia(ctx context.Context, serverAddress, peerKey string, fallbackIP netip.Addr) (net.Conn, error) {
|
||||
func (m *Manager) openConnVia(ctx context.Context, serverAddress, peerKey string, serverIP netip.Addr) (net.Conn, error) {
|
||||
// check if already has a connection to the desired relay server
|
||||
m.relayClientsMutex.RLock()
|
||||
rt, ok := m.relayClients[serverAddress]
|
||||
@@ -288,7 +281,7 @@ func (m *Manager) openConnVia(ctx context.Context, serverAddress, peerKey string
|
||||
m.relayClients[serverAddress] = rt
|
||||
m.relayClientsMutex.Unlock()
|
||||
|
||||
relayClient := NewClient(serverAddress, fallbackIP, m.tokenStore, m.peerID, m.mtu)
|
||||
relayClient := NewClientWithServerIP(serverAddress, serverIP, m.tokenStore, m.peerID, m.mtu)
|
||||
err := relayClient.Connect(m.ctx)
|
||||
if err != nil {
|
||||
rt.err = err
|
||||
|
||||
@@ -10,12 +10,12 @@ import (
|
||||
"github.com/netbirdio/netbird/relay/server"
|
||||
)
|
||||
|
||||
// TestManager_ForeignRelayFallbackIP exercises the foreign-relay path
|
||||
// TestManager_ForeignRelayServerIP exercises the foreign-relay path
|
||||
// end-to-end through Manager.OpenConn. Alice and Bob register on different
|
||||
// relay servers; Alice dials Bob's foreign relay using an unresolvable
|
||||
// FQDN. Without a fallback IP the dial fails; with Bob's advertised IP it
|
||||
// FQDN. Without a server IP the dial fails; with Bob's advertised IP it
|
||||
// recovers and a payload round-trips between the peers.
|
||||
func TestManager_ForeignRelayFallbackIP(t *testing.T) {
|
||||
func TestManager_ForeignRelayServerIP(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -66,14 +66,11 @@ func TestManager_ForeignRelayFallbackIP(t *testing.T) {
|
||||
t.Fatalf("bob manager serve: %s", err)
|
||||
}
|
||||
|
||||
// Bob's real relay URL (what mgrBob.RelayInstanceAddress returns).
|
||||
bobRealAddr, err := mgrBob.RelayInstanceAddress()
|
||||
// Bob's real relay URL and the IP that would ride along in signal as relayServerIP.
|
||||
bobRealAddr, bobAdvertisedIP, err := mgrBob.RelayInstanceAddress()
|
||||
if err != nil {
|
||||
t.Fatalf("bob relay address: %s", err)
|
||||
}
|
||||
// What Bob's RelayInstanceIP() reports — this is the field that
|
||||
// would ride along in signal as relayServerIP.
|
||||
bobAdvertisedIP := mgrBob.RelayInstanceIP()
|
||||
if !bobAdvertisedIP.IsValid() {
|
||||
t.Fatalf("expected valid RelayInstanceIP for bob, got zero")
|
||||
}
|
||||
@@ -84,16 +81,16 @@ func TestManager_ForeignRelayFallbackIP(t *testing.T) {
|
||||
t.Fatalf("broken FQDN must differ from bob's real address (%s)", bobRealAddr)
|
||||
}
|
||||
|
||||
t.Run("no fallback IP, dial fails", func(t *testing.T) {
|
||||
t.Run("no server IP, dial fails", func(t *testing.T) {
|
||||
dialCtx, dialCancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer dialCancel()
|
||||
_, err := mgrAlice.OpenConn(dialCtx, brokenFQDN, "bob", netip.Addr{})
|
||||
if err == nil {
|
||||
t.Fatalf("expected OpenConn to fail without fallback IP, got success")
|
||||
t.Fatalf("expected OpenConn to fail without server IP, got success")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fallback IP recovers", func(t *testing.T) {
|
||||
t.Run("server IP recovers", func(t *testing.T) {
|
||||
// Bob waits for Alice's incoming peer connection on his side.
|
||||
bobSideCh := make(chan error, 1)
|
||||
go func() {
|
||||
@@ -117,7 +114,7 @@ func TestManager_ForeignRelayFallbackIP(t *testing.T) {
|
||||
|
||||
aliceConn, err := mgrAlice.OpenConn(ctx, brokenFQDN, "bob", bobAdvertisedIP)
|
||||
if err != nil {
|
||||
t.Fatalf("alice OpenConn with fallback IP: %s", err)
|
||||
t.Fatalf("alice OpenConn with server IP: %s", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = aliceConn.Close() })
|
||||
|
||||
@@ -2,8 +2,8 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -102,7 +102,7 @@ func TestForeignConn(t *testing.T) {
|
||||
if err := clientBob.Serve(); err != nil {
|
||||
t.Fatalf("failed to serve manager: %s", err)
|
||||
}
|
||||
bobsSrvAddr, err := clientBob.RelayInstanceAddress()
|
||||
bobsSrvAddr, _, err := clientBob.RelayInstanceAddress()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get relay address: %s", err)
|
||||
}
|
||||
@@ -368,7 +368,7 @@ func TestAutoReconnect(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to serve manager: %s", err)
|
||||
}
|
||||
ra, err := clientAlice.RelayInstanceAddress()
|
||||
ra, _, err := clientAlice.RelayInstanceAddress()
|
||||
if err != nil {
|
||||
t.Errorf("failed to get relay address: %s", err)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -70,7 +69,7 @@ func (sp *ServerPicker) PickServer(parentCtx context.Context) (*Client, error) {
|
||||
|
||||
func (sp *ServerPicker) startConnection(ctx context.Context, resultChan chan connResult, url string) {
|
||||
log.Infof("try to connecting to relay server: %s", url)
|
||||
relayClient := NewClient(url, netip.Addr{}, sp.TokenStore, sp.PeerID, sp.MTU)
|
||||
relayClient := NewClient(url, sp.TokenStore, sp.PeerID, sp.MTU)
|
||||
err := relayClient.Connect(ctx)
|
||||
resultChan <- connResult{
|
||||
RelayClient: relayClient,
|
||||
|
||||
Reference in New Issue
Block a user