mirror of
https://github.com/netbirdio/netbird.git
synced 2025-06-20 17:58:02 +02:00
Merge pull request #36 from wiretrustee/avoid-proxy-when-local-net
feature: initial implementation of avoiding local proxy if peers are …
This commit is contained in:
commit
3041ff4ef7
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
ice "github.com/pion/ice/v2"
|
ice "github.com/pion/ice/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/wiretrustee/wiretrustee/iface"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -93,6 +94,7 @@ func (conn *Connection) Open(timeout time.Duration) error {
|
|||||||
|
|
||||||
// create an ice.Agent that will be responsible for negotiating and establishing actual peer-to-peer connection
|
// create an ice.Agent that will be responsible for negotiating and establishing actual peer-to-peer connection
|
||||||
a, err := ice.NewAgent(&ice.AgentConfig{
|
a, err := ice.NewAgent(&ice.AgentConfig{
|
||||||
|
// MulticastDNSMode: ice.MulticastDNSModeQueryAndGather,
|
||||||
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4},
|
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4},
|
||||||
Urls: conn.Config.StunTurnURLS,
|
Urls: conn.Config.StunTurnURLS,
|
||||||
InterfaceFilter: func(s string) bool {
|
InterfaceFilter: func(s string) bool {
|
||||||
@ -144,10 +146,23 @@ func (conn *Connection) Open(timeout time.Duration) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = conn.wgProxy.Start(remoteConn)
|
pair, err := conn.agent.GetSelectedCandidatePair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// in case the remote peer is in the local network we don't need a Wireguard proxy, direct communication is possible.
|
||||||
|
if pair.Local.Type() == ice.CandidateTypeHost && pair.Remote.Type() == ice.CandidateTypeHost {
|
||||||
|
log.Debugf("remote peer %s is in the local network with an address %s", conn.Config.RemoteWgKey.String(), pair.Remote.Address())
|
||||||
|
err = conn.wgProxy.StartLocal(fmt.Sprintf("%s:%d", pair.Remote.Address(), iface.WgPort))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = conn.wgProxy.Start(remoteConn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Infof("opened connection to peer %s", conn.Config.RemoteWgKey.String())
|
log.Infof("opened connection to peer %s", conn.Config.RemoteWgKey.String())
|
||||||
case <-time.After(timeout):
|
case <-time.After(timeout):
|
||||||
@ -298,7 +313,6 @@ func (conn *Connection) listenOnConnectionStateChanges() error {
|
|||||||
}
|
}
|
||||||
log.Infof("will connect to peer %s via a selected connnection candidate pair %s", conn.Config.RemoteWgKey.String(), pair)
|
log.Infof("will connect to peer %s via a selected connnection candidate pair %s", conn.Config.RemoteWgKey.String(), pair)
|
||||||
} else if state == ice.ConnectionStateDisconnected || state == ice.ConnectionStateFailed {
|
} else if state == ice.ConnectionStateDisconnected || state == ice.ConnectionStateFailed {
|
||||||
// todo do we really wanna have a connection restart within connection itself? Think of moving it outside
|
|
||||||
err := conn.Close()
|
err := conn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("error while closing connection to peer %s -> %s", conn.Config.RemoteWgKey.String(), err.Error())
|
log.Warnf("error while closing connection to peer %s -> %s", conn.Config.RemoteWgKey.String(), err.Error())
|
||||||
|
@ -42,6 +42,15 @@ func (p *WgProxy) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *WgProxy) StartLocal(host string) error {
|
||||||
|
err := iface.UpdatePeer(p.iface, p.remoteKey, p.allowedIps, DefaultWgKeepAlive, host)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error while configuring Wireguard peer [%s] %s", p.remoteKey, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Start starts a new proxy using the ICE connection
|
// Start starts a new proxy using the ICE connection
|
||||||
func (p *WgProxy) Start(remoteConn *ice.Conn) error {
|
func (p *WgProxy) Start(remoteConn *ice.Conn) error {
|
||||||
|
|
||||||
|
1
go.mod
1
go.mod
@ -14,6 +14,7 @@ require (
|
|||||||
github.com/vishvananda/netlink v1.1.0
|
github.com/vishvananda/netlink v1.1.0
|
||||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df
|
||||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf
|
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20210604143328-f9b48a961cd2
|
golang.zx2c4.com/wireguard v0.0.0-20210604143328-f9b48a961cd2
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5
|
||||||
golang.zx2c4.com/wireguard/windows v0.3.14
|
golang.zx2c4.com/wireguard/windows v0.3.14
|
||||||
|
@ -1,27 +1,25 @@
|
|||||||
package iface
|
package iface
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/conn"
|
"golang.zx2c4.com/wireguard/conn"
|
||||||
"golang.zx2c4.com/wireguard/device"
|
"golang.zx2c4.com/wireguard/device"
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultMTU = 1280
|
defaultMTU = 1280
|
||||||
|
WgPort = 51820
|
||||||
)
|
)
|
||||||
|
|
||||||
// Saves tun device object - is it required?
|
|
||||||
var tunIface tun.Device
|
var tunIface tun.Device
|
||||||
|
|
||||||
// Create Creates a new Wireguard interface, sets a given IP and brings it up.
|
// CreateWithUserspace Creates a new Wireguard interface, using wireguard-go userspace implementation
|
||||||
// Will reuse an existing one.
|
func CreateWithUserspace(iface string, address string) error {
|
||||||
func Create(iface string, address string) error {
|
|
||||||
var err error
|
var err error
|
||||||
tunIface, err = tun.CreateTUN(iface, defaultMTU)
|
tunIface, err = tun.CreateTUN(iface, defaultMTU)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -52,7 +50,7 @@ func Create(iface string, address string) error {
|
|||||||
|
|
||||||
log.Debugln("UAPI listener started")
|
log.Debugln("UAPI listener started")
|
||||||
|
|
||||||
err = assignAddr(address, tunIface)
|
err = assignAddr(address, iface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -85,10 +83,12 @@ func Configure(iface string, privateKey string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fwmark := 0
|
fwmark := 0
|
||||||
|
p := WgPort
|
||||||
cfg := wgtypes.Config{
|
cfg := wgtypes.Config{
|
||||||
PrivateKey: &key,
|
PrivateKey: &key,
|
||||||
ReplacePeers: false,
|
ReplacePeers: false,
|
||||||
FirewallMark: &fwmark,
|
FirewallMark: &fwmark,
|
||||||
|
ListenPort: &p,
|
||||||
}
|
}
|
||||||
err = wg.ConfigureDevice(iface, cfg)
|
err = wg.ConfigureDevice(iface, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,19 +2,18 @@ package iface
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
|
||||||
"net"
|
"net"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
//const (
|
// Create Creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
// interfacePrefix = "utun"
|
func Create(iface string, address string) error {
|
||||||
//)
|
return CreateWithUserspace(iface, address)
|
||||||
|
}
|
||||||
|
|
||||||
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
||||||
func assignAddr(address string, tunDevice tun.Device) error {
|
func assignAddr(address string, ifaceName string) error {
|
||||||
ifaceName, err := tunDevice.Name()
|
|
||||||
ip := strings.Split(address, "/")
|
ip := strings.Split(address, "/")
|
||||||
cmd := exec.Command("ifconfig", ifaceName, "inet", address, ip[0])
|
cmd := exec.Command("ifconfig", ifaceName, "inet", address, ip[0])
|
||||||
if out, err := cmd.CombinedOutput(); err != nil {
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
@ -3,24 +3,76 @@ package iface
|
|||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
|
||||||
|
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
//const (
|
// Create Creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
// interfacePrefix = "wg"
|
// Will reuse an existing one.
|
||||||
//)
|
func Create(iface string, address string) error {
|
||||||
|
|
||||||
// assignAddr Adds IP address to the tunnel interface
|
if WireguardModExists() {
|
||||||
func assignAddr(address string, tunDevice tun.Device) error {
|
log.Debug("using kernel Wireguard module")
|
||||||
var err error
|
return CreateWithKernel(iface, address)
|
||||||
|
} else {
|
||||||
|
return CreateWithUserspace(iface, address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateWithKernel Creates a new Wireguard interface using kernel Wireguard module.
|
||||||
|
// Works for Linux and offers much better network performance
|
||||||
|
func CreateWithKernel(iface string, address string) error {
|
||||||
attrs := netlink.NewLinkAttrs()
|
attrs := netlink.NewLinkAttrs()
|
||||||
attrs.Name, err = tunDevice.Name()
|
attrs.Name = iface
|
||||||
|
|
||||||
|
link := wgLink{
|
||||||
|
attrs: &attrs,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("adding device: %s", iface)
|
||||||
|
err := netlink.LinkAdd(&link)
|
||||||
|
if os.IsExist(err) {
|
||||||
|
log.Infof("interface %s already exists. Will reuse.", iface)
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("adding address %s to interface: %s", address, iface)
|
||||||
|
addr, _ := netlink.ParseAddr(address)
|
||||||
|
err = netlink.AddrAdd(&link, addr)
|
||||||
|
if os.IsExist(err) {
|
||||||
|
log.Infof("interface %s already has the address: %s", iface, address)
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = assignAddr(address, iface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo do a discovery
|
||||||
|
log.Debugf("setting MTU: %s", iface)
|
||||||
|
err = netlink.LinkSetMTU(&link, defaultMTU)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error setting MTU on interface: %s", iface)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("bringing up interface: %s", iface)
|
||||||
|
err = netlink.LinkSetUp(&link)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error bringing up interface: %s", iface)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignAddr Adds IP address to the tunnel interface
|
||||||
|
func assignAddr(address, name string) error {
|
||||||
|
var err error
|
||||||
|
attrs := netlink.NewLinkAttrs()
|
||||||
|
attrs.Name = name
|
||||||
|
|
||||||
link := wgLink{
|
link := wgLink{
|
||||||
attrs: &attrs,
|
attrs: &attrs,
|
||||||
}
|
}
|
||||||
|
@ -8,16 +8,21 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Create Creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
|
func Create(iface string, address string) error {
|
||||||
|
return CreateWithUserspace(iface, address)
|
||||||
|
}
|
||||||
|
|
||||||
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
||||||
func assignAddr(address string, tunDevice tun.Device) error {
|
func assignAddr(address string, ifaceName string) error {
|
||||||
ifaceName, err := tunDevice.Name()
|
|
||||||
nativeTunDevice := tunDevice.(*tun.NativeTun)
|
nativeTunDevice := tunIface.(*tun.NativeTun)
|
||||||
luid := winipcfg.LUID(nativeTunDevice.LUID())
|
luid := winipcfg.LUID(nativeTunDevice.LUID())
|
||||||
|
|
||||||
ip, ipnet, _ := net.ParseCIDR(address)
|
ip, ipnet, _ := net.ParseCIDR(address)
|
||||||
|
|
||||||
log.Debugf("adding address %s to interface: %s", address, ifaceName)
|
log.Debugf("adding address %s to interface: %s", address, ifaceName)
|
||||||
err = luid.SetIPAddresses([]net.IPNet{{ip, ipnet.Mask}})
|
err := luid.SetIPAddresses([]net.IPNet{{ip, ipnet.Mask}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
144
iface/mod.go
Normal file
144
iface/mod.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
// Holds logic to check existence of Wireguard kernel module
|
||||||
|
// Copied from https://github.com/paultag/go-modprobe
|
||||||
|
|
||||||
|
import (
|
||||||
|
"debug/elf"
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// get the root directory for the kernel modules. If this line panics,
|
||||||
|
// it's because getModuleRoot has failed to get the uname of the running
|
||||||
|
// kernel (likely a non-POSIX system, but maybe a broken kernel?)
|
||||||
|
moduleRoot = getModuleRoot()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get the module root (/lib/modules/$(uname -r)/)
|
||||||
|
func getModuleRoot() string {
|
||||||
|
uname := unix.Utsname{}
|
||||||
|
if err := unix.Uname(&uname); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for ; uname.Release[i] != 0; i++ {
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(
|
||||||
|
"/lib/modules",
|
||||||
|
string(uname.Release[:i]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// modName will, given a file descriptor to a Kernel Module (.ko file), parse the
|
||||||
|
// binary to get the module name. For instance, given a handle to the file at
|
||||||
|
// `kernel/drivers/usb/gadget/legacy/g_ether.ko`, return `g_ether`.
|
||||||
|
func modName(file *os.File) (string, error) {
|
||||||
|
f, err := elf.NewFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
syms, err := f.Symbols()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sym := range syms {
|
||||||
|
if strings.Compare(sym.Name, "__this_module") == 0 {
|
||||||
|
section := f.Sections[sym.Section]
|
||||||
|
data, err := section.Data()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) < 25 {
|
||||||
|
return "", fmt.Errorf("modprobe: data is short, __this_module is '%s'", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
data = data[24:]
|
||||||
|
i := 0
|
||||||
|
for ; data[i] != 0x00; i++ {
|
||||||
|
}
|
||||||
|
return string(data[:i]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("No name found. Is this a .ko or just an ELF?")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open every single kernel module under the root, and parse the ELF headers to
|
||||||
|
// extract the module name.
|
||||||
|
func elfMap(root string) (map[string]string, error) {
|
||||||
|
ret := map[string]string{}
|
||||||
|
|
||||||
|
err := filepath.Walk(
|
||||||
|
root,
|
||||||
|
func(path string, info os.FileInfo, err error) error {
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// skip broken files
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.Mode().IsRegular() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fd, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
name, err := modName(fd)
|
||||||
|
if err != nil {
|
||||||
|
/* For now, let's just ignore that and avoid adding to it */
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ret[name] = path
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open every single kernel module under the kernel module directory
|
||||||
|
// (/lib/modules/$(uname -r)/), and parse the ELF headers to extract the
|
||||||
|
// module name.
|
||||||
|
func generateMap() (map[string]string, error) {
|
||||||
|
return elfMap(moduleRoot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WireguardModExists returns true if Wireguard kernel module exists.
|
||||||
|
func WireguardModExists() bool {
|
||||||
|
_, err := resolveModName("wireguard")
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveModName will, given a module name (such as `wireguard`) return an absolute
|
||||||
|
// path to the .ko that provides that module.
|
||||||
|
func resolveModName(name string) (string, error) {
|
||||||
|
paths, err := generateMap()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
fsPath := paths[name]
|
||||||
|
if !strings.HasPrefix(fsPath, moduleRoot) {
|
||||||
|
return "", fmt.Errorf("module isn't in the module directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
return fsPath, nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user