diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index b94b54578..bd7509237 100644 --- a/go/native/GoGlue.cpp +++ b/go/native/GoGlue.cpp @@ -110,6 +110,8 @@ extern "C" int goStateObjectGetFunc(ZT_GoNode *,int,const uint64_t [2],void *,un extern "C" void goDNSResolverFunc(ZT_GoNode *,const uint8_t *,int,const char *,uintptr_t); extern "C" int goVirtualNetworkConfigFunc(ZT_GoNode *,ZT_GoTap *,uint64_t,int,const ZT_VirtualNetworkConfig *); extern "C" void goZtEvent(ZT_GoNode *,int,const void *); +extern "C" void goHandleTapAddedMulticastGroup(ZT_GoNode *,ZT_GoTap *,uint64_t,uint64_t,uint32_t); +extern "C" void goHandleTapRemovedMulticastGroup(ZT_GoNode *,ZT_GoTap *,uint64_t,uint64_t,uint32_t); static int ZT_GoNode_VirtualNetworkConfigFunction( ZT_Node *node, @@ -334,11 +336,26 @@ extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath) gn->run = true; gn->backgroundTaskThread = std::thread([gn] { + int64_t lastScannedMulticastGroups = 0; while (gn->run) { - std::this_thread::sleep_for(std::chrono::milliseconds(250)); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); const int64_t now = OSUtils::now(); if (now >= gn->nextBackgroundTaskDeadline) gn->node->processBackgroundTasks(nullptr,now,&(gn->nextBackgroundTaskDeadline)); + if ((now - lastScannedMulticastGroups) > 5000) { + lastScannedMulticastGroups = now; + std::vector added,removed; + std::lock_guard tl(gn->taps_l); + for(auto t=gn->taps.begin();t!=gn->taps.end();++t) { + added.clear(); + removed.clear(); + t->second->scanMulticastGroups(added,removed); + for(auto g=added.begin();g!=added.end();++g) + goHandleTapAddedMulticastGroup(gn,(ZT_GoTap *)t->second.get(),t->first,g->mac().toInt(),g->adi()); + for(auto g=removed.begin();g!=removed.end();++g) + goHandleTapRemovedMulticastGroup(gn,(ZT_GoTap *)t->second.get(),t->first,g->mac().toInt(),g->adi()); + } + } } }); diff --git a/go/pkg/zerotier/nativetap.go b/go/pkg/zerotier/nativetap.go index d71086331..1c448236b 100644 --- a/go/pkg/zerotier/nativetap.go +++ b/go/pkg/zerotier/nativetap.go @@ -16,6 +16,7 @@ package zerotier import ( "fmt" "net" + "sync" "sync/atomic" "unsafe" ) @@ -30,9 +31,11 @@ import "C" // nativeTap is a Tap implementation that wraps a native C++ interface to a system tun/tap device type nativeTap struct { - tap unsafe.Pointer - networkStatus uint32 - enabled uint32 + tap unsafe.Pointer + networkStatus uint32 + enabled uint32 + multicastGroupHandlers []func(bool, *MulticastGroup) + multicastGroupHandlersLock sync.Mutex } // SetEnabled sets this tap's enabled state @@ -56,12 +59,12 @@ func (t *nativeTap) AddIP(ip net.IPNet) error { if bits > 128 || bits < 0 { return ErrInvalidParameter } - C.ZT_GoTap_addIp(t.tap, afInet6, unsafe.Pointer(&ip.IP[0]), C.int(bits)) + C.ZT_GoTap_addIp(t.tap, C.int(afInet6), unsafe.Pointer(&ip.IP[0]), C.int(bits)) } else if len(ip.IP) == 4 { if bits > 32 || bits < 0 { return ErrInvalidParameter } - C.ZT_GoTap_addIp(t.tap, afInet, unsafe.Pointer(&ip.IP[0]), C.int(bits)) + C.ZT_GoTap_addIp(t.tap, C.int(afInet), unsafe.Pointer(&ip.IP[0]), C.int(bits)) } return ErrInvalidParameter } @@ -73,14 +76,14 @@ func (t *nativeTap) RemoveIP(ip net.IPNet) error { if bits > 128 || bits < 0 { return ErrInvalidParameter } - C.ZT_GoTap_removeIp(t.tap, afInet6, unsafe.Pointer(&ip.IP[0]), C.int(bits)) + C.ZT_GoTap_removeIp(t.tap, C.int(afInet6), unsafe.Pointer(&ip.IP[0]), C.int(bits)) return nil } if len(ip.IP) == 4 { if bits > 32 || bits < 0 { return ErrInvalidParameter } - C.ZT_GoTap_removeIp(t.tap, afInet, unsafe.Pointer(&ip.IP[0]), C.int(bits)) + C.ZT_GoTap_removeIp(t.tap, C.int(afInet), unsafe.Pointer(&ip.IP[0]), C.int(bits)) return nil } return ErrInvalidParameter @@ -98,7 +101,7 @@ func (t *nativeTap) IPs() (ips []net.IPNet, err error) { count := int(C.ZT_GoTap_ips(t.tap, unsafe.Pointer(&ipbuf[0]), 16384)) ipptr := 0 for i := 0; i < count; i++ { - af := ipbuf[ipptr] + af := int(ipbuf[ipptr]) ipptr++ switch af { case afInet: @@ -135,3 +138,10 @@ func (t *nativeTap) DeviceName() string { } return "" } + +// AddMulticastGroupChangeHandler adds a function to be called when the tap subscribes or unsubscribes to a multicast group. +func (t *nativeTap) AddMulticastGroupChangeHandler(handler func(bool, *MulticastGroup)) { + t.multicastGroupHandlersLock.Lock() + t.multicastGroupHandlers = append(t.multicastGroupHandlers, handler) + t.multicastGroupHandlersLock.Unlock() +} diff --git a/go/pkg/zerotier/network.go b/go/pkg/zerotier/network.go index 3d30c5040..e2a4b5b14 100644 --- a/go/pkg/zerotier/network.go +++ b/go/pkg/zerotier/network.go @@ -106,15 +106,26 @@ type NetworkConfig struct { // Network is a currently joined network type Network struct { + id NetworkID config NetworkConfig configLock sync.RWMutex - tap *Tap + tap Tap tapLock sync.Mutex } +// ID gets this network's unique ID +func (n *Network) ID() NetworkID { return n.id } + // Config returns a copy of this network's current configuration func (n *Network) Config() NetworkConfig { n.configLock.RLock() defer n.configLock.RUnlock() return n.config } + +// Tap gets this network's tap device +func (n *Network) Tap() Tap { + n.tapLock.Lock() + defer n.tapLock.Unlock() + return n.tap +} diff --git a/go/pkg/zerotier/node-callbacks.go b/go/pkg/zerotier/node-callbacks.go index 31e5024e4..98efb9ddc 100644 --- a/go/pkg/zerotier/node-callbacks.go +++ b/go/pkg/zerotier/node-callbacks.go @@ -29,8 +29,8 @@ import ( ) const ( - afInet = C.AF_INET - afInet6 = C.AF_INET6 + afInet int = C.AF_INET + afInet6 int = C.AF_INET6 networkStatusRequestingConfiguration = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION networkStatusOK = C.ZT_NETWORK_STATUS_OK @@ -68,11 +68,11 @@ func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, desiredAddressFam ip, port := node.pathLookup(uint64(ztAddress)) ip4 := ip.To4() if len(ip4) == 4 { - *((*C.int)(familyP)) = afInet + *((*C.int)(familyP)) = C.int(afInet) copy((*[4]byte)(ipP)[:], ip4) *((*C.int)(portP)) = C.int(port) } else if len(ip) == 16 { - *((*C.int)(familyP)) = afInet6 + *((*C.int)(familyP)) = C.int(afInet6) copy((*[16]byte)(ipP)[:], ip) *((*C.int)(portP)) = C.int(port) } @@ -178,3 +178,39 @@ func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) { node.handleRemoteTrace(uint64(rt.origin), C.GoBytes(unsafe.Pointer(rt.data), C.int(rt.len))) } } + +func handleTapMulticastGroupChange(gn unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t, added bool) { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return + } + + node.networksLock.RLock() + network := node.networks[uint64(nwid)] + node.networksLock.RUnlock() + + network.tapLock.Lock() + tap, _ := network.tap.(*nativeTap) + network.tapLock.Unlock() + + if tap != nil { + mg := &MulticastGroup{MAC: MAC(mac), ADI: uint32(adi)} + tap.multicastGroupHandlersLock.Lock() + defer tap.multicastGroupHandlersLock.Unlock() + for _, h := range tap.multicastGroupHandlers { + h(added, mg) + } + } +} + +//export goHandleTapAddedMulticastGroup +func goHandleTapAddedMulticastGroup(gn, tapP unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t) { + handleTapMulticastGroupChange(gn, nwid, mac, adi, true) +} + +//export goHandleTapRemovedMulticastGroup +func goHandleTapRemovedMulticastGroup(gn, tapP unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t) { + handleTapMulticastGroupChange(gn, nwid, mac, adi, false) +} diff --git a/go/pkg/zerotier/tap.go b/go/pkg/zerotier/tap.go index c0dbb8c1b..d6833bedc 100644 --- a/go/pkg/zerotier/tap.go +++ b/go/pkg/zerotier/tap.go @@ -23,4 +23,5 @@ type Tap interface { RemoveIP(ip net.IPNet) error IPs() ([]net.IPNet, error) DeviceName() string + AddMulticastGroupChangeHandler(func(bool, *MulticastGroup)) }