Network config stuff in Go

This commit is contained in:
Adam Ierymenko 2019-09-21 20:40:06 -07:00
parent 2eef9d22e6
commit bcb9df9cdf
No known key found for this signature in database
GPG Key ID: C8877CF2D7A5D7F3
4 changed files with 453 additions and 130 deletions

View File

@ -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<const struct sockaddr_in *>(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<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(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
}

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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
}