Support no-proxy mode connection mode (#245)

When one of the peers has a static public host IP
or both peers are in the same local network
we establish a direct Wireguard connection
bypassing proxy usage.
This helps reduce FD usage and improves
performance.
This commit is contained in:
Mikhail Bragin 2022-03-01 14:07:33 +01:00 committed by GitHub
parent 69cda73bbb
commit 5d4c2643a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 129 additions and 6 deletions

View File

@ -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 != "" {

View File

@ -31,8 +31,6 @@ const (
PeerConnectionTimeoutMin = 30000 // ms
)
const WgPort = 51820
// EngineConfig is a config for the Engine
type EngineConfig struct {
WgPort int

View File

@ -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

View File

@ -66,3 +66,7 @@ func (p *DummyProxy) Start(remoteConn net.Conn) error {
return nil
}
func (p *DummyProxy) Type() Type {
return TypeDummy
}

View File

@ -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
}

View File

@ -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
}

View File

@ -122,3 +122,7 @@ func (p *WireguardProxy) proxyToLocal() {
}
}
}
func (p *WireguardProxy) Type() Type {
return TypeWireguard
}

View File

@ -9,6 +9,7 @@ import (
const (
DefaultMTU = 1280
DefaultWgPort = 51820
)
// WGIface represents a interface instance