diff --git a/client/internal/engine.go b/client/internal/engine.go index 02062929b..67a20f69f 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -166,7 +166,7 @@ func (e *Engine) Stop() error { return nil } -// Start creates a new Wireguard tunnel interface and listens to events from Signal and Management services +// Start creates a new WireGuard tunnel interface and listens to events from Signal and Management services // Connections to remote peers are not established here. // However, they will be established once an event with a list of peers to connect to will be received from Management Service func (e *Engine) Start() error { @@ -184,10 +184,11 @@ func (e *Engine) Start() error { return err } - /*transportNet, err := e.newStdNet() + transportNet, err := e.newStdNet() if err != nil { log.Warnf("failed to create pion's stdnet: %s", err) - }*/ + } + fmt.Sprint(transportNet) err = e.wgInterface.Create() if err != nil { diff --git a/client/internal/engine_stdnet.go b/client/internal/engine_stdnet.go index b4e05768c..7a6d9c4e0 100644 --- a/client/internal/engine_stdnet.go +++ b/client/internal/engine_stdnet.go @@ -3,9 +3,9 @@ package internal import ( - "github.com/pion/transport/v2/stdnet" + "github.com/netbirdio/netbird/client/internal/stdnet" ) func (e *Engine) newStdNet() (*stdnet.Net, error) { - return stdnet.NewNet() + return stdnet.NewNet(nil, e.config.IFaceBlackList) } diff --git a/client/internal/engine_stdnet_android.go b/client/internal/engine_stdnet_android.go index 976ffd656..84eefaac1 100644 --- a/client/internal/engine_stdnet_android.go +++ b/client/internal/engine_stdnet_android.go @@ -3,5 +3,5 @@ package internal import "github.com/netbirdio/netbird/client/internal/stdnet" func (e *Engine) newStdNet() (*stdnet.Net, error) { - return stdnet.NewNet(e.config.IFaceDiscover) + return stdnet.NewNet(e.config.IFaceDiscover, e.config.IFaceBlackList) } diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index 18f491ad8..5f250bdf0 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -8,16 +8,14 @@ import ( "sync" "time" - "github.com/pion/ice/v2" - log "github.com/sirupsen/logrus" - "golang.zx2c4.com/wireguard/wgctrl" - "github.com/netbirdio/netbird/client/internal/proxy" "github.com/netbirdio/netbird/client/internal/stdnet" "github.com/netbirdio/netbird/iface" signal "github.com/netbirdio/netbird/signal/client" sProto "github.com/netbirdio/netbird/signal/proto" "github.com/netbirdio/netbird/version" + "github.com/pion/ice/v2" + log "github.com/sirupsen/logrus" ) // ConnConfig is a peer Connection configuration @@ -136,32 +134,6 @@ func NewConn(config ConnConfig, statusRecorder *Status, adapter iface.TunAdapter }, nil } -// interfaceFilter is a function passed to ICE Agent to filter out not allowed interfaces -// to avoid building tunnel over them -func interfaceFilter(blackList []string) func(string) bool { - - return func(iFace string) bool { - for _, s := range blackList { - if strings.HasPrefix(iFace, s) { - log.Debugf("ignoring interface %s - it is not allowed", iFace) - return false - } - } - // look for unlisted WireGuard interfaces - wg, err := wgctrl.New() - if err != nil { - log.Debugf("trying to create a wgctrl client failed with: %v", err) - return true - } - defer func() { - _ = wg.Close() - }() - - _, err = wg.Device(iFace) - return err != nil - } -} - func (conn *Conn) reCreateAgent() error { conn.mu.Lock() defer conn.mu.Unlock() @@ -179,7 +151,7 @@ func (conn *Conn) reCreateAgent() error { Urls: conn.config.StunTurn, CandidateTypes: []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive}, FailedTimeout: &failedTimeout, - InterfaceFilter: interfaceFilter(conn.config.InterfaceBlackList), + InterfaceFilter: stdnet.InterfaceFilter(conn.config.InterfaceBlackList), UDPMux: conn.config.UDPMux, UDPMuxSrflx: conn.config.UDPMuxSrflx, NAT1To1IPs: conn.config.NATExternalIPs, diff --git a/client/internal/peer/conn_test.go b/client/internal/peer/conn_test.go index ddee91800..a1fea337a 100644 --- a/client/internal/peer/conn_test.go +++ b/client/internal/peer/conn_test.go @@ -1,6 +1,7 @@ package peer import ( + "github.com/netbirdio/netbird/client/internal/stdnet" "sync" "testing" "time" @@ -28,7 +29,7 @@ func TestNewConn_interfaceFilter(t *testing.T) { ignore := []string{iface.WgInterfaceDefault, "tun0", "zt", "ZeroTier", "utun", "wg", "ts", "Tailscale", "tailscale"} - filter := interfaceFilter(ignore) + filter := stdnet.InterfaceFilter(ignore) for _, s := range ignore { assert.Equal(t, filter(s), false) diff --git a/client/internal/peer/stdnet.go b/client/internal/peer/stdnet.go index 588aaa929..d5fb715ff 100644 --- a/client/internal/peer/stdnet.go +++ b/client/internal/peer/stdnet.go @@ -3,9 +3,9 @@ package peer import ( - "github.com/pion/transport/v2/stdnet" + "github.com/netbirdio/netbird/client/internal/stdnet" ) func (conn *Conn) newStdNet() (*stdnet.Net, error) { - return stdnet.NewNet() + return stdnet.NewNet(nil, conn.config.InterfaceBlackList) } diff --git a/client/internal/peer/stdnet_android.go b/client/internal/peer/stdnet_android.go index 71a962c21..55c5f3c48 100644 --- a/client/internal/peer/stdnet_android.go +++ b/client/internal/peer/stdnet_android.go @@ -3,5 +3,5 @@ package peer import "github.com/netbirdio/netbird/client/internal/stdnet" func (conn *Conn) newStdNet() (*stdnet.Net, error) { - return stdnet.NewNet(conn.iFaceDiscover) + return stdnet.NewNet(conn.iFaceDiscover, e.config.IFaceBlackList) } diff --git a/client/internal/stdnet/iface_discover.go b/client/internal/stdnet/iface_discover.go index cbe306e2e..50c3916c2 100644 --- a/client/internal/stdnet/iface_discover.go +++ b/client/internal/stdnet/iface_discover.go @@ -4,5 +4,6 @@ package stdnet // to collect network interface information type IFaceDiscover interface { // IFaces return with the description of the interfaces + // todo refactor this to return []*transport.Interface instead to have it generic and independent from the platform IFaces() (string, error) } diff --git a/client/internal/stdnet/stdnet.go b/client/internal/stdnet/stdnet.go index 311306535..c596e105e 100644 --- a/client/internal/stdnet/stdnet.go +++ b/client/internal/stdnet/stdnet.go @@ -5,6 +5,7 @@ package stdnet import ( "fmt" + "golang.zx2c4.com/wireguard/wgctrl" "net" "strings" @@ -18,24 +19,78 @@ import ( type Net struct { stdnet.Net interfaces []*transport.Interface + // interfaceFilter should return true if the given interfaceName is allowed + interfaceFilter func(interfaceName string) bool } // NewNet creates a new StdNet instance. -func NewNet(iFaceDiscover IFaceDiscover) (*Net, error) { - n := &Net{} - +// iFaceDiscover and disallowList can be nil. +// iFaceDiscover +func NewNet(iFaceDiscover IFaceDiscover, disallowList []string) (*Net, error) { + n := &Net{interfaceFilter: InterfaceFilter(disallowList)} return n, n.UpdateInterfaces(iFaceDiscover) } +func (n *Net) filterInterfaces(interfaces []*transport.Interface) []*transport.Interface { + if n.interfaceFilter == nil { + return interfaces + } + result := []*transport.Interface{} + for _, iface := range interfaces { + if n.interfaceFilter(iface.Name) { + result = append(result, iface) + } + } + return result +} + // UpdateInterfaces updates the internal list of network interfaces -// and associated addresses. +// and associated addresses filtering them by name. +// The interfaces are discovered by an external iFaceDiscover function or by a default discoverer if the external one +// wasn't specified. func (n *Net) UpdateInterfaces(iFaceDiscover IFaceDiscover) error { - ifacesString, err := iFaceDiscover.IFaces() + discoveredInterfaces := []*transport.Interface{} + var err error + if iFaceDiscover != nil { + interfacesString := "" + interfacesString, err = iFaceDiscover.IFaces() + discoveredInterfaces = parseInterfacesString(interfacesString) + } else { + // fallback to the default discovering if custom IFaceDiscover wasn't provided + discoveredInterfaces, err = discoverInterfaces() + } if err != nil { return err } - n.interfaces = parseInterfacesString(ifacesString) - return err + + n.interfaces = n.filterInterfaces(discoveredInterfaces) + return nil +} + +func discoverInterfaces() ([]*transport.Interface, error) { + ifs := []*transport.Interface{} + + oifs, err := net.Interfaces() + if err != nil { + return nil, err + } + + for _, oif := range oifs { + ifc := transport.NewInterface(oif) + + addrs, err := oif.Addrs() + if err != nil { + return nil, err + } + + for _, addr := range addrs { + ifc.AddAddress(addr) + } + + ifs = append(ifs, ifc) + } + + return ifs, nil } // Interfaces returns a slice of interfaces which are available on the @@ -135,3 +190,29 @@ func parseInterfacesString(interfaces string) []*transport.Interface { } return ifs } + +// InterfaceFilter is a function passed to ICE Agent to filter out not allowed interfaces +// to avoid building tunnel over them. +func InterfaceFilter(disallowList []string) func(string) bool { + + return func(iFace string) bool { + for _, s := range disallowList { + if strings.HasPrefix(iFace, s) { + log.Debugf("ignoring interface %s - it is not allowed", iFace) + return false + } + } + // look for unlisted WireGuard interfaces + wg, err := wgctrl.New() + if err != nil { + log.Debugf("trying to create a wgctrl client failed with: %v", err) + return true + } + defer func() { + _ = wg.Close() + }() + + _, err = wg.Device(iFace) + return err != nil + } +} diff --git a/iface/bind/bind.go b/iface/bind/bind.go index 442e6ad12..a81a35c84 100644 --- a/iface/bind/bind.go +++ b/iface/bind/bind.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" "github.com/pion/stun" - "github.com/pion/transport/v2/stdnet" + "github.com/pion/transport/v2" log "github.com/sirupsen/logrus" "golang.zx2c4.com/wireguard/conn" "net" @@ -14,10 +14,23 @@ import ( ) type ICEBind struct { + // below fields, initialized on open sharedConn net.PacketConn udpMux *UniversalUDPMuxDefault - mu sync.Mutex // protects following fields + // below are fields initialized on creation + transportNet transport.Net + mu sync.Mutex +} + +// NewICEBind create a new instance of ICEBind with a given transportNet and an interfaceFilter function. +// The interfaceFilter function is used to exclude interfaces from hole punching (the IPs of that interfaces won't be used as connection candidates) +// The transportNet can be nil. +func NewICEBind(transportNet transport.Net, interfaceFilter func(interfaceName string) bool) *ICEBind { + return &ICEBind{ + transportNet: transportNet, + mu: sync.Mutex{}, + } } func (b *ICEBind) GetICEMux() (*UniversalUDPMuxDefault, error) { @@ -44,11 +57,7 @@ func (b *ICEBind) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) { return nil, 0, err } b.sharedConn = ipv4Conn - newNet, err := stdnet.NewNet() - if err != nil { - return nil, 0, err - } - b.udpMux = NewUniversalUDPMuxDefault(UniversalUDPMuxParams{UDPConn: b.sharedConn, Net: newNet}) + b.udpMux = NewUniversalUDPMuxDefault(UniversalUDPMuxParams{UDPConn: b.sharedConn, Net: b.transportNet}) portAddr1, err := netip.ParseAddrPort(ipv4Conn.LocalAddr().String()) if err != nil { diff --git a/iface/bind/udp_mux.go b/iface/bind/udp_mux.go index bcb7b3b7f..22c257e22 100644 --- a/iface/bind/udp_mux.go +++ b/iface/bind/udp_mux.go @@ -211,7 +211,6 @@ func (m *UDPMuxDefault) LocalAddr() net.Addr { // GetListenAddresses returns the list of addresses that this mux is listening on func (m *UDPMuxDefault) GetListenAddresses() []net.Addr { - log.Infof("---------------------------------") if len(m.localAddrsForUnspecified) > 0 { return m.localAddrsForUnspecified } diff --git a/iface/bind/udp_mux_universal.go b/iface/bind/udp_mux_universal.go index 5732019cf..54d95afa1 100644 --- a/iface/bind/udp_mux_universal.go +++ b/iface/bind/udp_mux_universal.go @@ -71,7 +71,6 @@ type udpConn struct { } func (m *UniversalUDPMuxDefault) GetListenAddresses() []net.Addr { - log.Infof("================================") return []net.Addr{m.LocalAddr()} }