From 2eef9d22e65f78357093b08386457bf3c672f0ec Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sat, 21 Sep 2019 18:22:25 -0700 Subject: [PATCH] getting there... --- attic/service/OneService.cpp | 16 +---- go/native/GoGlue.cpp | 49 ++++++++++++++ go/native/GoGlue.h | 6 ++ go/pkg/zerotier/misc.go | 12 ++++ go/pkg/zerotier/network.go | 33 +++++++++ go/pkg/zerotier/node.go | 128 ++++++++++++++++++++++++++++++++--- go/pkg/zerotier/route.go | 5 +- go/pkg/zerotier/tap.go | 7 +- osdep/EthernetTap.cpp | 11 +++ osdep/EthernetTap.hpp | 31 +++++++++ osdep/ManagedRoute.hpp | 25 ++----- 11 files changed, 278 insertions(+), 45 deletions(-) diff --git a/attic/service/OneService.cpp b/attic/service/OneService.cpp index 58fe24cf6..e84da792a 100644 --- a/attic/service/OneService.cpp +++ b/attic/service/OneService.cpp @@ -1458,20 +1458,6 @@ public: const InetAddress *const target = reinterpret_cast(&(n.config.routes[i].target)); const InetAddress *const via = reinterpret_cast(&(n.config.routes[i].via)); - const InetAddress *src = NULL; - for (unsigned int j=0; j(&(n.config.assignedAddresses[j])); - if (target->isV4() && tmp->isV4()) { - src = reinterpret_cast(&(n.config.assignedAddresses[j])); - break; - } else if (target->isV6() && tmp->isV6()) { - src = reinterpret_cast(&(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(new ManagedRoute(*target,*via,*src,tapdev))); + n.managedRoutes.push_back(SharedPtr(new ManagedRoute(*target,*via,tapdev))); if (!n.managedRoutes.back()->sync()) n.managedRoutes.pop_back(); #endif diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index 06ae19336..3f12fbf34 100644 --- a/go/native/GoGlue.cpp +++ b/go/native/GoGlue.cpp @@ -661,3 +661,52 @@ extern "C" void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu) { reinterpret_cast(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(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(tap)->removeRoute(target,via,metric); +} + +extern "C" int ZT_GoTap_syncRoutes(ZT_GoTap *tap) +{ + return reinterpret_cast(tap)->syncRoutes(); +} diff --git a/go/native/GoGlue.h b/go/native/GoGlue.h index 2acfc6299..f6b0c58b5 100644 --- a/go/native/GoGlue.h +++ b/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 diff --git a/go/pkg/zerotier/misc.go b/go/pkg/zerotier/misc.go index 73538c7ee..65b6e895a 100644 --- a/go/pkg/zerotier/misc.go +++ b/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 +} diff --git a/go/pkg/zerotier/network.go b/go/pkg/zerotier/network.go index 132cf63d6..5009543a3 100644 --- a/go/pkg/zerotier/network.go +++ b/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) + } + } +} diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index febbbaa0c..59a38db3f 100644 --- a/go/pkg/zerotier/node.go +++ b/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 +} diff --git a/go/pkg/zerotier/route.go b/go/pkg/zerotier/route.go index 8e68973e1..792392172 100644 --- a/go/pkg/zerotier/route.go +++ b/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 } diff --git a/go/pkg/zerotier/tap.go b/go/pkg/zerotier/tap.go index 310ec2a11..bacf269b5 100644 --- a/go/pkg/zerotier/tap.go +++ b/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 } diff --git a/osdep/EthernetTap.cpp b/osdep/EthernetTap.cpp index 10fe6d895..60e3f70d5 100644 --- a/osdep/EthernetTap.cpp +++ b/osdep/EthernetTap.cpp @@ -128,4 +128,15 @@ bool EthernetTap::addIps(std::vector 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 diff --git a/osdep/EthernetTap.hpp b/osdep/EthernetTap.hpp index db52e6b43..85c600043 100644 --- a/osdep/EthernetTap.hpp +++ b/osdep/EthernetTap.hpp @@ -18,10 +18,13 @@ #include "../node/MAC.hpp" #include "../node/InetAddress.hpp" #include "../node/MulticastGroup.hpp" +#include "ManagedRoute.hpp" #include #include #include +#include +#include namespace ZeroTier { @@ -50,9 +53,37 @@ public: virtual std::vector 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 &added,std::vector &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 l(_managedRoutes_l); + _managedRoutes[std::pair(target,metric)] = std::shared_ptr(new ManagedRoute(target,via,dnp)); + } + + ZT_ALWAYS_INLINE int removeRoute(const InetAddress &target,const InetAddress &via,const unsigned int metric) + { + std::lock_guard l(_managedRoutes_l); + _managedRoutes.erase(std::pair(target,metric)); + } + + ZT_ALWAYS_INLINE int syncRoutes() + { + std::lock_guard l(_managedRoutes_l); + for(auto r=_managedRoutes.begin();r!=_managedRoutes.end();++r) { + r->second->sync(); + } + return 0; + } + +private: + std::map< std::pair,std::shared_ptr > _managedRoutes; + std::mutex _managedRoutes_l; }; } // namespace ZeroTier diff --git a/osdep/ManagedRoute.hpp b/osdep/ManagedRoute.hpp index d1f60d3f0..18a28cd30 100644 --- a/osdep/ManagedRoute.hpp +++ b/osdep/ManagedRoute.hpp @@ -36,28 +36,19 @@ class ManagedRoute friend class SharedPtr; 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 _applied; // routes currently applied char _device[128];