diff --git a/client/cmd/up.go b/client/cmd/up.go index e03b7e4dc..5f7b049eb 100644 --- a/client/cmd/up.go +++ b/client/cmd/up.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "github.com/wiretrustee/wiretrustee/iface" "time" "github.com/cenkalti/backoff/v4" @@ -71,7 +72,7 @@ func createEngineConfig(key wgtypes.Key, config *internal.Config, peerConfig *mg WgAddr: peerConfig.Address, IFaceBlackList: iFaceBlackList, WgPrivateKey: key, - WgPort: internal.WgPort, + WgPort: iface.DefaultWgPort, } if config.PreSharedKey != "" { diff --git a/client/internal/engine.go b/client/internal/engine.go index f68e01d02..19f79b8fc 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -31,8 +31,6 @@ const ( PeerConnectionTimeoutMin = 30000 // ms ) -const WgPort = 51820 - // EngineConfig is a config for the Engine type EngineConfig struct { WgPort int diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index 6fed5b0ae..f75a612f5 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -2,6 +2,7 @@ package peer import ( "context" + "github.com/wiretrustee/wiretrustee/iface" "golang.zx2c4.com/wireguard/wgctrl" "net" "sync" @@ -222,7 +223,14 @@ func (conn *Conn) Open() error { return err } - log.Infof("connected to peer %s [laddr <-> raddr] [%s <-> %s]", conn.config.Key, remoteConn.LocalAddr().String(), remoteConn.RemoteAddr().String()) + if conn.proxy.Type() == proxy.TypeNoProxy { + host, _, _ := net.SplitHostPort(remoteConn.LocalAddr().String()) + rhost, _, _ := net.SplitHostPort(remoteConn.RemoteAddr().String()) + // direct Wireguard connection + log.Infof("directly connected to peer %s [laddr <-> raddr] [%s:%d <-> %s:%d]", conn.config.Key, host, iface.DefaultWgPort, rhost, iface.DefaultWgPort) + } else { + log.Infof("connected to peer %s [laddr <-> raddr] [%s <-> %s]", conn.config.Key, remoteConn.LocalAddr().String(), remoteConn.RemoteAddr().String()) + } // wait until connection disconnected or has been closed externally (upper layer, e.g. engine) select { @@ -235,16 +243,65 @@ func (conn *Conn) Open() error { } } +// useProxy determines whether a direct connection (without a go proxy) is possible +// There are 3 cases: one of the peers has a public IP or both peers are in the same private network +// Please note, that this check happens when peers were already able to ping each other using ICE layer. +func shouldUseProxy(pair *ice.CandidatePair) bool { + remoteIP := net.ParseIP(pair.Remote.Address()) + myIp := net.ParseIP(pair.Local.Address()) + remoteIsPublic := IsPublicIP(remoteIP) + myIsPublic := IsPublicIP(myIp) + + //one of the hosts has a public IP + if remoteIsPublic && pair.Remote.Type() == ice.CandidateTypeHost { + return false + } + if myIsPublic && pair.Local.Type() == ice.CandidateTypeHost { + return false + } + + if pair.Local.Type() == ice.CandidateTypeHost && pair.Remote.Type() == ice.CandidateTypeHost { + if !remoteIsPublic && !myIsPublic { + //both hosts are in the same private network + return false + } + } + + return true +} + +// IsPublicIP indicates whether IP is public or not. +func IsPublicIP(ip net.IP) bool { + if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsPrivate() { + return false + } + return true +} + // startProxy starts proxying traffic from/to local Wireguard and sets connection status to StatusConnected func (conn *Conn) startProxy(remoteConn net.Conn) error { conn.mu.Lock() defer conn.mu.Unlock() - conn.proxy = proxy.NewWireguardProxy(conn.config.ProxyConfig) - err := conn.proxy.Start(remoteConn) + var pair *ice.CandidatePair + pair, err := conn.agent.GetSelectedCandidatePair() if err != nil { return err } + + useProxy := shouldUseProxy(pair) + var p proxy.Proxy + if useProxy { + p = proxy.NewWireguardProxy(conn.config.ProxyConfig) + } else { + p = proxy.NewNoProxy(conn.config.ProxyConfig) + } + conn.proxy = p + err = p.Start(remoteConn) + if err != nil { + return err + } + conn.status = StatusConnected return nil diff --git a/client/internal/proxy/dummy.go b/client/internal/proxy/dummy.go index bce657d2a..ebd7cd68f 100644 --- a/client/internal/proxy/dummy.go +++ b/client/internal/proxy/dummy.go @@ -66,3 +66,7 @@ func (p *DummyProxy) Start(remoteConn net.Conn) error { return nil } + +func (p *DummyProxy) Type() Type { + return TypeDummy +} diff --git a/client/internal/proxy/noproxy.go b/client/internal/proxy/noproxy.go new file mode 100644 index 000000000..7f9cae600 --- /dev/null +++ b/client/internal/proxy/noproxy.go @@ -0,0 +1,49 @@ +package proxy + +import ( + log "github.com/sirupsen/logrus" + "github.com/wiretrustee/wiretrustee/iface" + "net" +) + +// NoProxy is used when there is no need for a proxy between ICE and Wireguard. +// This is possible in either of these cases: +// - peers are in the same local network +// - one of the peers has a public static IP (host) +// NoProxy will just update remote peer with a remote host and fixed Wireguard port (r.g. 51820). +// In order NoProxy to work, Wireguard port has to be fixed for the time being. +type NoProxy struct { + config Config +} + +func NewNoProxy(config Config) *NoProxy { + return &NoProxy{config: config} +} + +func (p *NoProxy) Close() error { + // noop + return nil +} + +// Start just updates Wireguard peer with the remote IP and default Wireguard port +func (p *NoProxy) Start(remoteConn net.Conn) error { + + log.Debugf("using NoProxy while connecting to peer %s", p.config.RemoteKey) + addr, err := net.ResolveUDPAddr("udp", remoteConn.RemoteAddr().String()) + if err != nil { + return err + } + addr.Port = iface.DefaultWgPort + err = p.config.WgInterface.UpdatePeer(p.config.RemoteKey, p.config.AllowedIps, DefaultWgKeepAlive, + addr, p.config.PreSharedKey) + + if err != nil { + return err + } + + return nil +} + +func (p *NoProxy) Type() Type { + return TypeNoProxy +} diff --git a/client/internal/proxy/proxy.go b/client/internal/proxy/proxy.go index 5bf07927d..a856ab1e6 100644 --- a/client/internal/proxy/proxy.go +++ b/client/internal/proxy/proxy.go @@ -10,6 +10,14 @@ import ( const DefaultWgKeepAlive = 25 * time.Second +type Type string + +const ( + TypeNoProxy Type = "NoProxy" + TypeWireguard Type = "Wireguard" + TypeDummy Type = "Dummy" +) + type Config struct { WgListenAddr string RemoteKey string @@ -22,4 +30,5 @@ type Proxy interface { io.Closer // Start creates a local remoteConn and starts proxying data from/to remoteConn Start(remoteConn net.Conn) error + Type() Type } diff --git a/client/internal/proxy/wireguard.go b/client/internal/proxy/wireguard.go index c7325a71f..a8609cb18 100644 --- a/client/internal/proxy/wireguard.go +++ b/client/internal/proxy/wireguard.go @@ -122,3 +122,7 @@ func (p *WireguardProxy) proxyToLocal() { } } } + +func (p *WireguardProxy) Type() Type { + return TypeWireguard +} diff --git a/iface/iface.go b/iface/iface.go index 1ba5b1813..4eb40a026 100644 --- a/iface/iface.go +++ b/iface/iface.go @@ -9,6 +9,7 @@ import ( const ( DefaultMTU = 1280 + DefaultWgPort = 51820 ) // WGIface represents a interface instance