diff --git a/go/pkg/zerotier/misc.go b/go/pkg/zerotier/misc.go index 65b6e895a..0af65c2b8 100644 --- a/go/pkg/zerotier/misc.go +++ b/go/pkg/zerotier/misc.go @@ -15,6 +15,7 @@ package zerotier import ( "encoding/base32" + "encoding/binary" "net" "time" "unsafe" @@ -27,10 +28,194 @@ 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) - } + copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], ipn.IP) ones, bits := ipn.Mask.Size() k[2] = (uint64(ones) << 32) | uint64(bits) return } + +func allZero(b []byte) bool { + for _, bb := range b { + if bb != 0 { + return false + } + } + return true +} + +// The ipClassify code below is based on and should produce identical results to +// InetAddress::ipScope() in the C++ code. +/* +InetAddress::IpScope InetAddress::ipScope() const +{ + switch(ss_family) { + + case AF_INET: { + const uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast(this)->sin_addr.s_addr); + switch(ip >> 24) { + case 0x00: return IP_SCOPE_NONE; // 0.0.0.0/8 (reserved, never used) + case 0x06: return IP_SCOPE_PSEUDOPRIVATE; // 6.0.0.0/8 (US Army) + case 0x0a: return IP_SCOPE_PRIVATE; // 10.0.0.0/8 + case 0x0b: return IP_SCOPE_PSEUDOPRIVATE; // 11.0.0.0/8 (US DoD) + case 0x15: return IP_SCOPE_PSEUDOPRIVATE; // 21.0.0.0/8 (US DDN-RVN) + case 0x16: return IP_SCOPE_PSEUDOPRIVATE; // 22.0.0.0/8 (US DISA) + case 0x19: return IP_SCOPE_PSEUDOPRIVATE; // 25.0.0.0/8 (UK Ministry of Defense) + case 0x1a: return IP_SCOPE_PSEUDOPRIVATE; // 26.0.0.0/8 (US DISA) + case 0x1c: return IP_SCOPE_PSEUDOPRIVATE; // 28.0.0.0/8 (US DSI-North) + case 0x1d: return IP_SCOPE_PSEUDOPRIVATE; // 29.0.0.0/8 (US DISA) + case 0x1e: return IP_SCOPE_PSEUDOPRIVATE; // 30.0.0.0/8 (US DISA) + case 0x33: return IP_SCOPE_PSEUDOPRIVATE; // 51.0.0.0/8 (UK Department of Social Security) + case 0x37: return IP_SCOPE_PSEUDOPRIVATE; // 55.0.0.0/8 (US DoD) + case 0x38: return IP_SCOPE_PSEUDOPRIVATE; // 56.0.0.0/8 (US Postal Service) + case 0x64: + if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_PRIVATE; // 100.64.0.0/10 + break; + case 0x7f: return IP_SCOPE_LOOPBACK; // 127.0.0.0/8 + case 0xa9: + if ((ip & 0xffff0000) == 0xa9fe0000) return IP_SCOPE_LINK_LOCAL; // 169.254.0.0/16 + break; + case 0xac: + if ((ip & 0xfff00000) == 0xac100000) return IP_SCOPE_PRIVATE; // 172.16.0.0/12 + break; + case 0xc0: + if ((ip & 0xffff0000) == 0xc0a80000) return IP_SCOPE_PRIVATE; // 192.168.0.0/16 + break; + case 0xff: return IP_SCOPE_NONE; // 255.0.0.0/8 (broadcast, or unused/unusable) + } + switch(ip >> 28) { + case 0xe: return IP_SCOPE_MULTICAST; // 224.0.0.0/4 + case 0xf: return IP_SCOPE_PSEUDOPRIVATE; // 240.0.0.0/4 ("reserved," usually unusable) + } + return IP_SCOPE_GLOBAL; + } break; + + case AF_INET6: { + const unsigned char *ip = reinterpret_cast(reinterpret_cast(this)->sin6_addr.s6_addr); + if ((ip[0] & 0xf0) == 0xf0) { + if (ip[0] == 0xff) return IP_SCOPE_MULTICAST; // ff00::/8 + if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) { + unsigned int k = 2; + while ((!ip[k])&&(k < 15)) ++k; + if ((k == 15)&&(ip[15] == 0x01)) + return IP_SCOPE_LOOPBACK; // fe80::1/128 + else return IP_SCOPE_LINK_LOCAL; // fe80::/10 + } + if ((ip[0] & 0xfe) == 0xfc) return IP_SCOPE_PRIVATE; // fc00::/7 + } + unsigned int k = 0; + while ((!ip[k])&&(k < 15)) ++k; + if (k == 15) { // all 0's except last byte + if (ip[15] == 0x01) return IP_SCOPE_LOOPBACK; // ::1/128 + if (ip[15] == 0x00) return IP_SCOPE_NONE; // ::/128 + } + return IP_SCOPE_GLOBAL; + } break; + + } + + return IP_SCOPE_NONE; +} +*/ + +const ( + ipClassificationNone = -1 + ipClassificationLoopback = 0 + ipClassificationPseudoprivate = 1 + ipClassificationPrivate = 2 + ipClassificationLinkLocal = 3 + ipClassificationMulticast = 4 + ipClassificationGlobal = 5 +) + +var ipv4PseudoprivatePrefixes = []byte{ + 0x06, // 6.0.0.0/8 (US Army) + 0x0b, // 11.0.0.0/8 (US DoD) + 0x15, // 21.0.0.0/8 (US DDN-RVN) + 0x16, // 22.0.0.0/8 (US DISA) + 0x19, // 25.0.0.0/8 (UK Ministry of Defense) + 0x1a, // 26.0.0.0/8 (US DISA) + 0x1c, // 28.0.0.0/8 (US DSI-North) + 0x1d, // 29.0.0.0/8 (US DISA) + 0x1e, // 30.0.0.0/8 (US DISA) + 0x33, // 51.0.0.0/8 (UK Department of Social Security) + 0x37, // 55.0.0.0/8 (US DoD) + 0x38, // 56.0.0.0/8 (US Postal Service) +} + +// ipClassify determines the official or in a few cases unofficial role of an IP address +func ipClassify(ip net.IP) int { + if len(ip) == 16 { + ip4 := ip.To4() + if len(ip4) == 4 { + ip = ip4 + } + } + if len(ip) == 4 { + ip4FirstByte := ip[0] + for _, b := range ipv4PseudoprivatePrefixes { + if ip4FirstByte == b { + return ipClassificationPseudoprivate + } + } + ip4 := binary.BigEndian.Uint32(ip) + switch ip4FirstByte { + case 0x0a: // 10.0.0.0/8 + return ipClassificationPrivate + case 0x64: // 100.64.0.0/10 + if (ip4 & 0xffc00000) == 0x64400000 { + return ipClassificationPrivate + } + case 0x7f: // 127.0.0.1/8 + return ipClassificationLoopback + case 0xa9: // 169.254.0.0/16 + if (ip4 & 0xffff0000) == 0xa9fe0000 { + return ipClassificationLinkLocal + } + case 0xac: // 172.16.0.0/12 + if (ip4 & 0xfff00000) == 0xac100000 { + return ipClassificationPrivate + } + case 0xc0: // 192.168.0.0/16 + if (ip4 & 0xffff0000) == 0xc0a80000 { + return ipClassificationPrivate + } + } + switch ip4 >> 28 { + case 0xe: // 224.0.0.0/4 + return ipClassificationMulticast + case 0xf: // 240.0.0.0/4 ("reserved," usually unusable) + return ipClassificationNone + } + return ipClassificationGlobal + } + + if len(ip) == 16 { + if (ip[0] & 0xf0) == 0xf0 { + if ip[0] == 0xff { // ff00::/8 + return ipClassificationMulticast + } + if ip[0] == 0xfe && (ip[1]&0xc0) == 0x80 { + if allZero(ip[2:15]) { + if ip[15] == 0x01 { // fe80::1/128 + return ipClassificationLoopback + } + return ipClassificationLinkLocal + } + } + if (ip[0] & 0xfe) == 0xfc { // fc00::/7 + return ipClassificationPrivate + } + } + if allZero(ip[0:15]) { + if ip[15] == 0x01 { // ::1/128 + return ipClassificationLoopback + } + if ip[15] == 0x00 { // ::/128 + return ipClassificationNone + } + } + return ipClassificationGlobal + } + + return ipClassificationNone +} diff --git a/go/pkg/zerotier/network.go b/go/pkg/zerotier/network.go index 5009543a3..22a37321c 100644 --- a/go/pkg/zerotier/network.go +++ b/go/pkg/zerotier/network.go @@ -40,7 +40,7 @@ func (n NetworkID) String() string { // MarshalJSON marshals this NetworkID as a string func (n NetworkID) MarshalJSON() ([]byte, error) { - return []byte(n.String()), nil + return []byte("\"" + n.String() + "\""), nil } // UnmarshalJSON unmarshals this NetworkID from a string @@ -75,13 +75,13 @@ type NetworkConfig struct { // MTU is the Ethernet MTU for this network MTU int - // CanBridge is true if this network is allowed to bridge in other devices with different Ethernet addresses + // Bridge is true if this network is allowed to bridge in other devices with different Ethernet addresses Bridge bool // BroadcastEnabled is true if the broadcast (ff:ff:ff:ff:ff:ff) address works (excluding IPv4 ARP which is handled via a special path) BroadcastEnabled bool - // Network configuration revision number according to network controller + // NetconfRevision is the revision number reported by the controller NetconfRevision uint64 // AssignedAddresses are static IPs assigned by the network controller to this device @@ -91,13 +91,50 @@ type NetworkConfig struct { Routes []Route } +// NetworkLocalSettings is settings for this network that can be changed locally +type NetworkLocalSettings struct { + // AllowManagedIPs determines whether managed IP assignment is allowed + AllowManagedIPs bool + + // AllowGlobalIPs determines if managed IPs that overlap with public Internet addresses are allowed + AllowGlobalIPs bool + + // AllowManagedRoutes determines whether managed routes can be set + AllowManagedRoutes bool + + // AllowGlobalRoutes determines if managed routes can overlap with public Internet addresses + AllowGlobalRoutes bool + + // AllowDefaultRouteOverride determines if the default (0.0.0.0 or ::0) route on the system can be overridden ("full tunnel" mode) + AllowDefaultRouteOverride bool +} + // Network is a currently joined network type Network struct { id NetworkID - config NetworkConfig tap Tap + config NetworkConfig + settings NetworkLocalSettings // locked by configLock configLock sync.RWMutex - tapLock sync.RWMutex +} + +// NewNetwork creates a new network with default settings +func NewNetwork(id NetworkID, t Tap) (*Network, error) { + return &Network{ + id: id, + tap: t, + config: NetworkConfig{ + ID: id, + Status: NetworkStatusRequestConfiguration, + }, + settings: NetworkLocalSettings{ + AllowManagedIPs: true, + AllowGlobalIPs: false, + AllowManagedRoutes: true, + AllowGlobalRoutes: false, + AllowDefaultRouteOverride: false, + }, + }, nil } // ID gets this network's unique ID @@ -111,36 +148,84 @@ func (n *Network) Config() NetworkConfig { } // Tap gets this network's tap device -func (n *Network) Tap() Tap { - n.tapLock.RLock() - defer n.tapLock.RUnlock() - return n.tap +func (n *Network) Tap() Tap { return n.tap } + +// SetLocalSettings modifies this network's local settings +func (n *Network) SetLocalSettings(ls *NetworkLocalSettings) { n.updateConfig(nil, ls) } + +func (n *Network) networkConfigRevision() uint64 { + n.configLock.RLock() + defer n.configLock.RUnlock() + return n.config.NetconfRevision } -func (n *Network) handleNetworkConfigUpdate(nc *NetworkConfig) { - n.tapLock.RLock() +func networkManagedIPAllowed(ip net.IP, ls *NetworkLocalSettings) bool { + if !ls.AllowManagedIPs { + return false + } + switch ipClassify(ip) { + case ipClassificationNone, ipClassificationLoopback, ipClassificationLinkLocal, ipClassificationMulticast: + return false + case ipClassificationGlobal: + return ls.AllowGlobalIPs + } + return true +} + +func networkManagedRouteAllowed(r *Route, ls *NetworkLocalSettings) bool { + if !ls.AllowManagedRoutes { + return false + } + bits, _ := r.Target.Mask.Size() + if len(r.Target.IP) > 0 && allZero(r.Target.IP) && bits == 0 { + return ls.AllowDefaultRouteOverride + } + switch ipClassify(r.Target.IP) { + case ipClassificationNone, ipClassificationLoopback, ipClassificationLinkLocal, ipClassificationMulticast: + return false + case ipClassificationGlobal: + return ls.AllowGlobalRoutes + } + return true +} + +func (n *Network) updateConfig(nc *NetworkConfig, ls *NetworkLocalSettings) { n.configLock.Lock() defer n.configLock.Unlock() - defer n.tapLock.RUnlock() - if n.tap == nil { // sanity check + if n.tap == nil { // sanity check, should never happen return } + if nc == nil { + nc = &n.config + } + if ls == nil { + ls = &n.settings + } + // 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) + if n.settings.AllowManagedIPs { + for _, ip := range n.config.AssignedAddresses { + if networkManagedIPAllowed(ip.IP, &n.settings) { // was it allowed? + haveAssignedIPs[ipNetToKey(&ip)] = &ip + } + } + } + if ls.AllowManagedIPs { + for _, ip := range nc.AssignedAddresses { + if networkManagedIPAllowed(ip.IP, ls) { // should it be allowed now? + k := ipNetToKey(&ip) + wantAssignedIPs[k] = true + if _, have := haveAssignedIPs[k]; !have { + n.tap.AddIP(&ip) + } + } } } for k, ip := range haveAssignedIPs { @@ -148,4 +233,38 @@ func (n *Network) handleNetworkConfigUpdate(nc *NetworkConfig) { n.tap.RemoveIP(ip) } } + + // Do the same for managed routes + haveManagedRoutes := make(map[[6]uint64]*Route) + wantManagedRoutes := make(map[[6]uint64]bool) + if n.settings.AllowManagedRoutes { + for _, r := range n.config.Routes { + if networkManagedRouteAllowed(&r, &n.settings) { // was it allowed? + haveManagedRoutes[r.key()] = &r + } + } + } + if ls.AllowManagedRoutes { + for _, r := range nc.Routes { + if networkManagedRouteAllowed(&r, ls) { // should it be allowed now? + k := r.key() + wantManagedRoutes[k] = true + if _, have := haveManagedRoutes[k]; !have { + n.tap.AddRoute(&r) + } + } + } + } + for k, r := range haveManagedRoutes { + if _, want := wantManagedRoutes[k]; !want { + n.tap.RemoveRoute(r) + } + } + + if nc != &n.config { + n.config = *nc + } + if ls != &n.settings { + n.settings = *ls + } } diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 59a38db3f..9016ef2b7 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -43,6 +43,9 @@ const ( NetworkStatusPortError int = C.ZT_NETWORK_STATUS_PORT_ERROR NetworkStatusClientTooOld int = C.ZT_NETWORK_STATUS_CLIENT_TOO_OLD + NetworkTypePrivate int = C.ZT_NETWORK_TYPE_PRIVATE + NetworkTypePublic int = C.ZT_NETWORK_TYPE_PUBLIC + // CoreVersionMajor is the major version of the ZeroTier core CoreVersionMajor int = C.ZEROTIER_ONE_VERSION_MAJOR @@ -136,13 +139,10 @@ func (n *Node) Join(nwid uint64, tap Tap) (*Network, error) { return nil, ErrTapInitFailed } - nw := &Network{ - id: NetworkID(nwid), - config: NetworkConfig{ - ID: NetworkID(nwid), - Status: NetworkStatusRequestConfiguration, - }, - tap: &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1}, + nw, err := NewNetwork(NetworkID(nwid), &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1}) + if err != nil { + C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid)) + return nil, err } n.networksLock.Lock() n.networks[nwid] = nw @@ -368,113 +368,82 @@ func sockaddrStorageToIPNet(ss *C.struct_sockaddr_storage) *net.IPNet { //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 - } - 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) - } + go func() { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return } - 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 + node.networksLock.RLock() + network := node.networks[uint64(nwid)] + node.networksLock.RUnlock() + if network != nil { + ncc := (*C.ZT_VirtualNetworkConfig)(conf) + if network.networkConfigRevision() > uint64(ncc.netconfRevision) { + return } - 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), - }) + 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.updateConfig(&nc, nil) } - network.handleNetworkConfigUpdate(&nc) - } + }() } //export goZtEvent func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return - } - switch eventType { - case C.ZT_EVENT_OFFLINE: - atomic.StoreUint32(&node.online, 0) - case C.ZT_EVENT_ONLINE: - atomic.StoreUint32(&node.online, 1) - case C.ZT_EVENT_TRACE: - node.handleTrace(C.GoString((*C.char)(data))) - case C.ZT_EVENT_USER_MESSAGE: - um := (*C.ZT_UserMessage)(data) - node.handleUserMessage(uint64(um.origin), uint64(um.typeId), C.GoBytes(um.data, C.int(um.length))) - case C.ZT_EVENT_REMOTE_TRACE: - rt := (*C.ZT_RemoteTrace)(data) - node.handleRemoteTrace(uint64(rt.origin), C.GoBytes(unsafe.Pointer(rt.data), C.int(rt.len))) - } -} - -// These are really part of nativeTap - -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) + go func() { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return } - } -} - -//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) + switch eventType { + case C.ZT_EVENT_OFFLINE: + atomic.StoreUint32(&node.online, 0) + case C.ZT_EVENT_ONLINE: + atomic.StoreUint32(&node.online, 1) + case C.ZT_EVENT_TRACE: + node.handleTrace(C.GoString((*C.char)(data))) + case C.ZT_EVENT_USER_MESSAGE: + um := (*C.ZT_UserMessage)(data) + node.handleUserMessage(uint64(um.origin), uint64(um.typeId), C.GoBytes(um.data, C.int(um.length))) + case C.ZT_EVENT_REMOTE_TRACE: + rt := (*C.ZT_RemoteTrace)(data) + node.handleRemoteTrace(uint64(rt.origin), C.GoBytes(unsafe.Pointer(rt.data), C.int(rt.len))) + } + }() } ////////////////////////////////////////////////////////////////////////////// @@ -663,3 +632,40 @@ func (t *nativeTap) SyncRoutes() error { C.ZT_GoTap_syncRoutes(t.tap) return nil } + +////////////////////////////////////////////////////////////////////////////// + +func handleTapMulticastGroupChange(gn unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t, added bool) { + go func() { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return + } + node.networksLock.RLock() + network := node.networks[uint64(nwid)] + node.networksLock.RUnlock() + if network != nil { + tap, _ := network.tap.(*nativeTap) + 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/route.go b/go/pkg/zerotier/route.go index 792392172..1b5afa65b 100644 --- a/go/pkg/zerotier/route.go +++ b/go/pkg/zerotier/route.go @@ -13,7 +13,10 @@ package zerotier -import "net" +import ( + "net" + "unsafe" +) // Route represents a route in a host's routing table type Route struct { @@ -29,3 +32,13 @@ type Route struct { // Metric is an interface metric that can affect route priority (behavior can be OS-specific) Metric uint16 } + +// key generates a key suitable for a map[] from this route +func (r *Route) key() (k [6]uint64) { + copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], r.Target.IP) + ones, bits := r.Target.Mask.Size() + k[2] = (uint64(ones) << 32) | uint64(bits) + copy(((*[16]byte)(unsafe.Pointer(&k[3])))[:], r.Via) + k[5] = (uint64(r.Flags) << 32) | uint64(r.Metric) + return +}