Browse Source

getting there...

Adam Ierymenko 5 years ago
parent
commit
2eef9d22e6

+ 1 - 15
attic/service/OneService.cpp

@@ -1458,20 +1458,6 @@ public:
 				const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
 				const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
 
-				const InetAddress *src = NULL;
-				for (unsigned int j=0; j<n.config.assignedAddressCount; ++j) {
-					const InetAddress *const tmp = reinterpret_cast<const InetAddress *>(&(n.config.assignedAddresses[j]));
-					if (target->isV4() && tmp->isV4()) {
-						src = reinterpret_cast<InetAddress *>(&(n.config.assignedAddresses[j]));
-						break;
-					} else if (target->isV6() && tmp->isV6()) {
-						src = reinterpret_cast<InetAddress *>(&(n.config.assignedAddresses[j]));
-						break;
-					}
-				}
-				if (!src)
-					src = &InetAddress::NIL;
-
 				if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(matchIpOnly(myIps,*via))) )
 					continue;
 
@@ -1501,7 +1487,7 @@ public:
 					continue;
 
 				// Add and apply new routes
-				n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,*src,tapdev)));
+				n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,tapdev)));
 				if (!n.managedRoutes.back()->sync())
 					n.managedRoutes.pop_back();
 #endif

+ 49 - 0
go/native/GoGlue.cpp

@@ -661,3 +661,52 @@ extern "C" void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu)
 {
 	reinterpret_cast<EthernetTap *>(tap)->setMtu(mtu);
 }
+
+extern "C" int ZT_GoTap_addRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric)
+{
+	InetAddress target,via;
+	switch(targetAf) {
+		case AF_INET:
+			target.set(targetIp,4,(unsigned int)targetNetmaskBits);
+			break;
+		case AF_INET6:
+			target.set(targetIp,16,(unsigned int)targetNetmaskBits);
+			break;
+	}
+	switch(viaAf) {
+		case AF_INET:
+			via.set(viaIp,4,0);
+			break;
+		case AF_INET6:
+			via.set(viaIp,16,0);
+			break;
+	}
+	return reinterpret_cast<EthernetTap *>(tap)->addRoute(target,via,metric);
+}
+
+extern "C" int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric)
+{
+	InetAddress target,via;
+	switch(targetAf) {
+		case AF_INET:
+			target.set(targetIp,4,(unsigned int)targetNetmaskBits);
+			break;
+		case AF_INET6:
+			target.set(targetIp,16,(unsigned int)targetNetmaskBits);
+			break;
+	}
+	switch(viaAf) {
+		case AF_INET:
+			via.set(viaIp,4,0);
+			break;
+		case AF_INET6:
+			via.set(viaIp,16,0);
+			break;
+	}
+	return reinterpret_cast<EthernetTap *>(tap)->removeRoute(target,via,metric);
+}
+
+extern "C" int ZT_GoTap_syncRoutes(ZT_GoTap *tap)
+{
+	return reinterpret_cast<EthernetTap *>(tap)->syncRoutes();
+}

+ 6 - 0
go/native/GoGlue.h

@@ -91,6 +91,12 @@ void ZT_GoTap_setFriendlyName(ZT_GoTap *tap,const char *friendlyName);
 
 void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu);
 
+int ZT_GoTap_addRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric);
+
+int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric);
+
+int ZT_GoTap_syncRoutes(ZT_GoTap *tap);
+
 /****************************************************************************/
 
 #ifdef __cplusplus

+ 12 - 0
go/pkg/zerotier/misc.go

@@ -15,10 +15,22 @@ package zerotier
 
 import (
 	"encoding/base32"
+	"net"
 	"time"
+	"unsafe"
 )
 
 var base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
 
 // TimeMs returns the time in milliseconds since epoch.
 func TimeMs() int64 { return int64(time.Now().UnixNano()) / int64(1000000) }
+
+// ipNetToKey creates a key that can be used in a map[] from a net.IPNet
+func ipNetToKey(ipn *net.IPNet) (k [3]uint64) {
+	if len(ipn.IP) > 0 {
+		copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], ipn.IP)
+	}
+	ones, bits := ipn.Mask.Size()
+	k[2] = (uint64(ones) << 32) | uint64(bits)
+	return
+}

+ 33 - 0
go/pkg/zerotier/network.go

@@ -116,3 +116,36 @@ func (n *Network) Tap() Tap {
 	defer n.tapLock.RUnlock()
 	return n.tap
 }
+
+func (n *Network) handleNetworkConfigUpdate(nc *NetworkConfig) {
+	n.tapLock.RLock()
+	n.configLock.Lock()
+	defer n.configLock.Unlock()
+	defer n.tapLock.RUnlock()
+
+	if n.tap == nil { // sanity check
+		return
+	}
+
+	// Add IPs to tap that are newly assigned in this config update,
+	// and remove any IPs from the tap that were assigned that are no
+	// longer wanted. IPs assigned to the tap externally (e.g. by an
+	// "ifconfig" command) are left alone.
+	haveAssignedIPs := make(map[[3]uint64]*net.IPNet)
+	for _, ip := range n.config.AssignedAddresses {
+		haveAssignedIPs[ipNetToKey(&ip)] = &ip
+	}
+	wantAssignedIPs := make(map[[3]uint64]bool)
+	for _, ip := range nc.AssignedAddresses {
+		k := ipNetToKey(&ip)
+		wantAssignedIPs[k] = true
+		if _, have := haveAssignedIPs[k]; !have {
+			n.tap.AddIP(&ip)
+		}
+	}
+	for k, ip := range haveAssignedIPs {
+		if _, want := wantAssignedIPs[k]; !want {
+			n.tap.RemoveIP(ip)
+		}
+	}
+}

+ 119 - 9
go/pkg/zerotier/node.go

@@ -20,6 +20,7 @@ package zerotier
 import "C"
 
 import (
+	"encoding/binary"
 	"errors"
 	"fmt"
 	"io/ioutil"
@@ -28,7 +29,6 @@ import (
 	"path"
 	"sync"
 	"sync/atomic"
-	"time"
 	"unsafe"
 
 	acl "github.com/hectane/go-acl"
@@ -139,10 +139,8 @@ func (n *Node) Join(nwid uint64, tap Tap) (*Network, error) {
 	nw := &Network{
 		id: NetworkID(nwid),
 		config: NetworkConfig{
-			ID:          NetworkID(nwid),
-			Status:      NetworkStatusRequestConfiguration,
-			LastUpdated: time.Now(),
-			Enabled:     true,
+			ID:     NetworkID(nwid),
+			Status: NetworkStatusRequestConfiguration,
 		},
 		tap: &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1},
 	}
@@ -347,20 +345,74 @@ func goDNSResolverFunc(gn unsafe.Pointer, dnsRecordTypes unsafe.Pointer, numDNSR
 	}()
 }
 
+func sockaddrStorageToIPNet(ss *C.struct_sockaddr_storage) *net.IPNet {
+	var a net.IPNet
+	switch ss.ss_family {
+	case afInet:
+		sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss))
+		var ip4 [4]byte
+		copy(ip4[:], (*[4]byte)(unsafe.Pointer(&sa4.sin_addr))[:])
+		a.IP = net.IP(ip4[:])
+		a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:])), 32)
+		return &a
+	case afInet6:
+		sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss))
+		var ip6 [16]byte
+		copy(ip6[:], (*[16]byte)(unsafe.Pointer(&sa6.sin6_addr))[:])
+		a.IP = net.IP(ip6[:])
+		a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:])), 128)
+		return &a
+	}
+	return nil
+}
+
 //export goVirtualNetworkConfigFunc
 func goVirtualNetworkConfigFunc(gn, tapP unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) {
 	nodesByUserPtrLock.RLock()
 	node := nodesByUserPtr[uintptr(gn)]
 	nodesByUserPtrLock.RUnlock()
 	if node == nil {
-		return 255
+		return
 	}
 	node.networksLock.RLock()
 	network := node.networks[uint64(nwid)]
 	node.networksLock.RUnlock()
 	if network != nil {
+		ncc := (*C.ZT_VirtualNetworkConfig)(conf)
+		var nc NetworkConfig
+		nc.ID = uint64(ncc.nwid)
+		nc.MAC = MAC(ncc.mac)
+		nc.Name = C.GoString(ncc.name)
+		nc.Status = int(ncc.status)
+		nc.Type = int(ncc._type)
+		nc.MTU = int(ncc.mtu)
+		nc.Bridge = (ncc.bridge != 0)
+		nc.BroadcastEnabled = (ncc.broadcastEnabled != 0)
+		nc.NetconfRevision = uint64(ncc.netconfRevision)
+		for i := 0; i < int(ncc.assignedAddressCount); i++ {
+			a := sockaddrStorageToIPNet(&ncc.assignedAddresses[i])
+			if a != nil {
+				nc.AssignedAddresses = append(nc.AssignedAddresses, *a)
+			}
+		}
+		for i := 0; i < int(ncc.routeCount); i++ {
+			tgt := sockaddrStorageToIPNet(&ncc.routes[i].target)
+			viaN := sockaddrStorageToIPNet(&ncc.routes[i].via)
+			var via net.IP
+			if viaN != nil {
+				via = viaN.IP
+			}
+			if tgt != nil {
+				nc.Routes = append(nc.Routes, Route{
+					Target: *tgt,
+					Via:    via,
+					Flags:  uint16(ncc.routes[i].flags),
+					Metric: uint16(ncc.routes[i].metric),
+				})
+			}
+		}
+		network.handleNetworkConfigUpdate(&nc)
 	}
-	//return C.int(node.handleNetworkConfigUpdate(uint64(nwid), int(op), (*C.ZT_VirtualNetworkConfig)(conf)))
 }
 
 //export goZtEvent
@@ -461,7 +513,7 @@ func (t *nativeTap) Enabled() bool {
 }
 
 // AddIP adds an IP address (with netmask) to this tap
-func (t *nativeTap) AddIP(ip net.IPNet) error {
+func (t *nativeTap) AddIP(ip *net.IPNet) error {
 	bits, _ := ip.Mask.Size()
 	if len(ip.IP) == 16 {
 		if bits > 128 || bits < 0 {
@@ -478,7 +530,7 @@ func (t *nativeTap) AddIP(ip net.IPNet) error {
 }
 
 // RemoveIP removes this IP address (with netmask) from this tap
-func (t *nativeTap) RemoveIP(ip net.IPNet) error {
+func (t *nativeTap) RemoveIP(ip *net.IPNet) error {
 	bits, _ := ip.Mask.Size()
 	if len(ip.IP) == 16 {
 		if bits > 128 || bits < 0 {
@@ -553,3 +605,61 @@ func (t *nativeTap) AddMulticastGroupChangeHandler(handler func(bool, *Multicast
 	t.multicastGroupHandlers = append(t.multicastGroupHandlers, handler)
 	t.multicastGroupHandlersLock.Unlock()
 }
+
+// AddRoute adds or updates a managed route on this tap's interface
+func (t *nativeTap) AddRoute(r *Route) error {
+	rc := 0
+	if r != nil {
+		if len(r.Target.IP) == 4 {
+			mask, _ := r.Target.Mask.Size()
+			if len(r.Via) == 4 {
+				rc = int(C.ZT_GoTap_addRoute(t.tap, afInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), afInet, unsafe.Pointer(&r.Via[0]), C.int(r.Metric)))
+			} else {
+				rc = int(C.ZT_GoTap_addRoute(t.tap, afInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.int(r.Metric)))
+			}
+		} else if len(r.Target.IP) == 16 {
+			mask, _ := r.Target.Mask.Size()
+			if len(r.Via) == 4 {
+				rc = int(C.ZT_GoTap_addRoute(t.tap, afInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), afInet6, unsafe.Pointer(&r.Via[0]), C.int(r.Metric)))
+			} else {
+				rc = int(C.ZT_GoTap_addRoute(t.tap, afInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.int(r.Metric)))
+			}
+		}
+	}
+	if rc != 0 {
+		return fmt.Errorf("tap device error adding route: %d", rc)
+	}
+	return nil
+}
+
+// RemoveRoute removes a managed route on this tap's interface
+func (t *nativeTap) RemoveRoute(r *Route) error {
+	rc := 0
+	if r != nil {
+		if len(r.Target.IP) == 4 {
+			mask, _ := r.Target.Mask.Size()
+			if len(r.Via) == 4 {
+				rc = int(C.ZT_GoTap_removeRoute(t.tap, afInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), afInet, unsafe.Pointer(&r.Via[0]), C.int(r.Metric)))
+			} else {
+				rc = int(C.ZT_GoTap_removeRoute(t.tap, afInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.int(r.Metric)))
+			}
+		} else if len(r.Target.IP) == 16 {
+			mask, _ := r.Target.Mask.Size()
+			if len(r.Via) == 4 {
+				rc = int(C.ZT_GoTap_removeRoute(t.tap, afInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), afInet6, unsafe.Pointer(&r.Via[0]), C.int(r.Metric)))
+			} else {
+				rc = int(C.ZT_GoTap_removeRoute(t.tap, afInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.int(r.Metric)))
+			}
+		}
+	}
+	if rc != 0 {
+		return fmt.Errorf("tap device error removing route: %d", rc)
+	}
+	return nil
+}
+
+// SyncRoutes synchronizes managed routes
+func (t *nativeTap) SyncRoutes() error {
+	C.ZT_GoTap_syncRoutes(t.tap)
+	return nil
+}

+ 4 - 1
go/pkg/zerotier/route.go

@@ -23,6 +23,9 @@ type Route struct {
 	// Via is how to reach this target (null/empty if the target IP range is local to this virtual LAN)
 	Via net.IP
 
+	// Route flags (currently unused, always 0)
+	Flags uint16
+
 	// Metric is an interface metric that can affect route priority (behavior can be OS-specific)
-	Metric int
+	Metric uint16
 }

+ 5 - 2
go/pkg/zerotier/tap.go

@@ -21,9 +21,12 @@ type Tap interface {
 	Error() (int, string)
 	SetEnabled(enabled bool)
 	Enabled() bool
-	AddIP(ip net.IPNet) error
-	RemoveIP(ip net.IPNet) error
+	AddIP(ip *net.IPNet) error
+	RemoveIP(ip *net.IPNet) error
 	IPs() ([]net.IPNet, error)
 	DeviceName() string
 	AddMulticastGroupChangeHandler(func(bool, *MulticastGroup))
+	AddRoute(r *Route) error
+	RemoveRoute(r *Route) error
+	SyncRoutes() error
 }

+ 11 - 0
osdep/EthernetTap.cpp

@@ -128,4 +128,15 @@ bool EthernetTap::addIps(std::vector<InetAddress> ips)
 	return true;
 }
 
+std::string EthernetTap::routingDeviceName() const
+{
+#ifdef __WINDOWS__
+	char tapdev[64];
+	OSUtils::ztsnprintf(tapdev,sizeof(tapdev),"%.16llx",(unsigned long long)(((const WindowsEthernetTap *)(this))->luid().Value));
+	return std::string(tapdev);
+#else
+	return this->deviceName();
+#endif
+}
+
 } // namespace ZeroTier

+ 31 - 0
osdep/EthernetTap.hpp

@@ -18,10 +18,13 @@
 #include "../node/MAC.hpp"
 #include "../node/InetAddress.hpp"
 #include "../node/MulticastGroup.hpp"
+#include "ManagedRoute.hpp"
 
 #include <string>
 #include <memory>
 #include <vector>
+#include <mutex>
+#include <map>
 
 namespace ZeroTier {
 
@@ -50,9 +53,37 @@ public:
 	virtual std::vector<InetAddress> ips() const = 0;
 	virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len) = 0;
 	virtual std::string deviceName() const = 0;
+	virtual std::string routingDeviceName() const;
 	virtual void setFriendlyName(const char *friendlyName) = 0;
 	virtual void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed) = 0;
 	virtual void setMtu(unsigned int mtu) = 0;
+
+	ZT_ALWAYS_INLINE int addRoute(const InetAddress &target,const InetAddress &via,const unsigned int metric)
+	{
+		const std::string dn(this->routingDeviceName());
+		const char *const dnp = (dn.length() > 0) ? dn.c_str() : (const char *)0;
+		std::lock_guard<std::mutex> l(_managedRoutes_l);
+		_managedRoutes[std::pair<InetAddress,unsigned int>(target,metric)] = std::shared_ptr<ManagedRoute>(new ManagedRoute(target,via,dnp));
+	}
+
+	ZT_ALWAYS_INLINE int removeRoute(const InetAddress &target,const InetAddress &via,const unsigned int metric)
+	{
+		std::lock_guard<std::mutex> l(_managedRoutes_l);
+		_managedRoutes.erase(std::pair<InetAddress,unsigned int>(target,metric));
+	}
+
+	ZT_ALWAYS_INLINE int syncRoutes()
+	{
+		std::lock_guard<std::mutex> l(_managedRoutes_l);
+		for(auto r=_managedRoutes.begin();r!=_managedRoutes.end();++r) {
+			r->second->sync();
+		}
+		return 0;
+	}
+
+private:
+	std::map< std::pair<InetAddress,unsigned int>,std::shared_ptr<ManagedRoute> > _managedRoutes;
+	std::mutex _managedRoutes_l;
 };
 
 } // namespace ZeroTier

+ 7 - 18
osdep/ManagedRoute.hpp

@@ -36,28 +36,19 @@ class ManagedRoute
 	friend class SharedPtr<ManagedRoute>;
 
 public:
-	ManagedRoute(const InetAddress &target,const InetAddress &via,const InetAddress &src,const char *device)
+	ZT_ALWAYS_INLINE ManagedRoute(const InetAddress &target,const InetAddress &via,const char *device)
 	{
 		_target = target;
 		_via = via;
-		_src = src;
 		if (via.ss_family == AF_INET)
 			_via.setPort(32);
 		else if (via.ss_family == AF_INET6)
 			_via.setPort(128);
-		if (src.ss_family == AF_INET) {
-			_src.setPort(32);
-		} else if (src.ss_family == AF_INET6) {
-			_src.setPort(128);
-		}
 		Utils::scopy(_device,sizeof(_device),device);
 		_systemDevice[0] = (char)0;
 	}
 
-	~ManagedRoute()
-	{
-		this->remove();
-	}
+	ZT_ALWAYS_INLINE ~ManagedRoute() { this->remove(); }
 
 	/**
 	 * Set or update currently set route
@@ -78,18 +69,16 @@ public:
 	 */
 	void remove();
 
-	inline const InetAddress &target() const { return _target; }
-	inline const InetAddress &via() const { return _via; }
-	inline const InetAddress &src() const { return _src; }
-	inline const char *device() const { return _device; }
+	ZT_ALWAYS_INLINE const InetAddress &target() const { return _target; }
+	ZT_ALWAYS_INLINE const InetAddress &via() const { return _via; }
+	ZT_ALWAYS_INLINE const char *device() const { return _device; }
 
 private:
-	ManagedRoute(const ManagedRoute &) {}
-	inline ManagedRoute &operator=(const ManagedRoute &) { return *this; }
+	ZT_ALWAYS_INLINE ManagedRoute(const ManagedRoute &) {}
+	ZT_ALWAYS_INLINE ManagedRoute &operator=(const ManagedRoute &) { return *this; }
 
 	InetAddress _target;
 	InetAddress _via;
-	InetAddress _src;
 	InetAddress _systemVia; // for route overrides
 	std::map<InetAddress,bool> _applied; // routes currently applied
 	char _device[128];