Bläddra i källkod

Loads of refactoring, integration of new version of Trace.

Adam Ierymenko 5 år sedan
förälder
incheckning
33bb61c63d

+ 0 - 0
include/ZeroTierDebug.h → attic/ZeroTierDebug.h


+ 1 - 1
controller/LFDB.cpp

@@ -44,7 +44,7 @@ LFDB::LFDB(const Identity &myId,const char *path,const char *lfOwnerPrivate,cons
 		// LF record masking key is the first 32 bytes of SHA512(controller private key) in hex,
 		// LF record masking key is the first 32 bytes of SHA512(controller private key) in hex,
 		// hiding record values from anything but the controller or someone who has its key.
 		// hiding record values from anything but the controller or someone who has its key.
 		uint8_t sha384pk[48];
 		uint8_t sha384pk[48];
-		_myId.hash(sha384pk,true);
+		_myId.hashWithPrivate(sha384pk);
 		char maskingKey [128];
 		char maskingKey [128];
 		Utils::hex(sha384pk,32,maskingKey);
 		Utils::hex(sha384pk,32,maskingKey);
 
 

+ 0 - 1
go/native/GoGlue.cpp

@@ -46,7 +46,6 @@
 #include <mutex>
 #include <mutex>
 #include <map>
 #include <map>
 #include <vector>
 #include <vector>
-#include <set>
 #include <memory>
 #include <memory>
 #include <atomic>
 #include <atomic>
 
 

+ 1 - 1
go/pkg/zerotier/api.go

@@ -495,7 +495,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
 			Port: tcpBindAddr.Port,
 			Port: tcpBindAddr.Port,
 		})
 		})
 		if err != nil {
 		if err != nil {
-			node.log.Printf("ERROR: unable to start API HTTP server at TCP bind address %s: %s (continuing anyway)", tcpBindAddr.String(), err.Error())
+			node.infoLog.Printf("ERROR: unable to start API HTTP server at TCP bind address %s: %s (named socket listener startd, continuing anyway)", tcpBindAddr.String(), err.Error())
 		} else {
 		} else {
 			tcpHttpServer = &http.Server{
 			tcpHttpServer = &http.Server{
 				MaxHeaderBytes: 4096,
 				MaxHeaderBytes: 4096,

+ 1 - 1
go/pkg/zerotier/localconfig.go

@@ -57,7 +57,7 @@ type LocalConfigSettings struct {
 	// PortMapping enables uPnP and NAT-PMP support
 	// PortMapping enables uPnP and NAT-PMP support
 	PortMapping bool `json:"portMapping"`
 	PortMapping bool `json:"portMapping"`
 
 
-	// LogSizeMax is the maximum size of the log in kilobytes or 0 for no limit and -1 to disable logging
+	// LogSizeMax is the maximum size of the infoLog in kilobytes or 0 for no limit and -1 to disable logging
 	LogSizeMax int `json:"logSizeMax"`
 	LogSizeMax int `json:"logSizeMax"`
 
 
 	// IP/port to bind for TCP access to control API (disabled if null)
 	// IP/port to bind for TCP access to control API (disabled if null)

+ 6 - 6
go/pkg/zerotier/network.go

@@ -203,7 +203,7 @@ func (n *Network) LocalSettings() NetworkLocalSettings {
 
 
 // MulticastSubscribe subscribes to a multicast group
 // MulticastSubscribe subscribes to a multicast group
 func (n *Network) MulticastSubscribe(mg *MulticastGroup) {
 func (n *Network) MulticastSubscribe(mg *MulticastGroup) {
-	n.node.log.Printf("%.16x joined multicast group %s", uint64(n.id), mg.String())
+	n.node.infoLog.Printf("%.16x joined multicast group %s", uint64(n.id), mg.String())
 	k := mg.key()
 	k := mg.key()
 	n.multicastSubscriptionsLock.Lock()
 	n.multicastSubscriptionsLock.Lock()
 	if _, have := n.multicastSubscriptions[k]; have {
 	if _, have := n.multicastSubscriptions[k]; have {
@@ -217,7 +217,7 @@ func (n *Network) MulticastSubscribe(mg *MulticastGroup) {
 
 
 // MulticastUnsubscribe removes a subscription to a multicast group
 // MulticastUnsubscribe removes a subscription to a multicast group
 func (n *Network) MulticastUnsubscribe(mg *MulticastGroup) {
 func (n *Network) MulticastUnsubscribe(mg *MulticastGroup) {
-	n.node.log.Printf("%.16x left multicast group %s", uint64(n.id), mg.String())
+	n.node.infoLog.Printf("%.16x left multicast group %s", uint64(n.id), mg.String())
 	n.multicastSubscriptionsLock.Lock()
 	n.multicastSubscriptionsLock.Lock()
 	delete(n.multicastSubscriptions, mg.key())
 	delete(n.multicastSubscriptions, mg.key())
 	n.multicastSubscriptionsLock.Unlock()
 	n.multicastSubscriptionsLock.Unlock()
@@ -311,7 +311,7 @@ func (n *Network) updateConfig(nc *NetworkConfig, ls *NetworkLocalSettings) {
 				k := ipNetToKey(&ip)
 				k := ipNetToKey(&ip)
 				wantAssignedIPs[k] = true
 				wantAssignedIPs[k] = true
 				if _, have := haveAssignedIPs[k]; !have {
 				if _, have := haveAssignedIPs[k]; !have {
-					n.node.log.Printf("%.16x adding managed IP %s", uint64(n.id), ip.String())
+					n.node.infoLog.Printf("%.16x adding managed IP %s", uint64(n.id), ip.String())
 					_ = n.tap.AddIP(&ip)
 					_ = n.tap.AddIP(&ip)
 				}
 				}
 			}
 			}
@@ -319,7 +319,7 @@ func (n *Network) updateConfig(nc *NetworkConfig, ls *NetworkLocalSettings) {
 	}
 	}
 	for k, ip := range haveAssignedIPs {
 	for k, ip := range haveAssignedIPs {
 		if _, want := wantAssignedIPs[k]; !want {
 		if _, want := wantAssignedIPs[k]; !want {
-			n.node.log.Printf("%.16x removing managed IP %s", uint64(n.id), ip.String())
+			n.node.infoLog.Printf("%.16x removing managed IP %s", uint64(n.id), ip.String())
 			_ = n.tap.RemoveIP(ip)
 			_ = n.tap.RemoveIP(ip)
 		}
 		}
 	}
 	}
@@ -340,7 +340,7 @@ func (n *Network) updateConfig(nc *NetworkConfig, ls *NetworkLocalSettings) {
 				k := r.key()
 				k := r.key()
 				wantManagedRoutes[k] = true
 				wantManagedRoutes[k] = true
 				if _, have := haveManagedRoutes[k]; !have {
 				if _, have := haveManagedRoutes[k]; !have {
-					n.node.log.Printf("%.16x adding managed route %s", uint64(n.id), r.String())
+					n.node.infoLog.Printf("%.16x adding managed route %s", uint64(n.id), r.String())
 					_ = n.tap.AddRoute(&r)
 					_ = n.tap.AddRoute(&r)
 				}
 				}
 			}
 			}
@@ -348,7 +348,7 @@ func (n *Network) updateConfig(nc *NetworkConfig, ls *NetworkLocalSettings) {
 	}
 	}
 	for k, r := range haveManagedRoutes {
 	for k, r := range haveManagedRoutes {
 		if _, want := wantManagedRoutes[k]; !want {
 		if _, want := wantManagedRoutes[k]; !want {
-			n.node.log.Printf("%.16x removing managed route %s", uint64(n.id), r.String())
+			n.node.infoLog.Printf("%.16x removing managed route %s", uint64(n.id), r.String())
 			_ = n.tap.RemoveRoute(r)
 			_ = n.tap.RemoveRoute(r)
 		}
 		}
 	}
 	}

+ 84 - 52
go/pkg/zerotier/node.go

@@ -24,6 +24,7 @@ import (
 	"encoding/hex"
 	"encoding/hex"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"log"
 	"log"
 	"math/rand"
 	"math/rand"
@@ -82,29 +83,53 @@ var (
 	nodesByUserPtrLock sync.RWMutex
 	nodesByUserPtrLock sync.RWMutex
 )
 )
 
 
-// Node is an instance of the ZeroTier core node and related C++ I/O code
+// Node is an instance of a virtual port on the global switch.
 type Node struct {
 type Node struct {
-	networks               map[NetworkID]*Network
-	networksByMAC          map[MAC]*Network  // locked by networksLock
-	networksLock           sync.RWMutex
-	interfaceAddresses     map[string]net.IP // physical external IPs on the machine
+	// networks contains networks we have joined, and networksByMAC by their local MAC address
+	networks      map[NetworkID]*Network
+	networksByMAC map[MAC]*Network // locked by networksLock
+	networksLock  sync.RWMutex
+
+	// interfaceAddresses are physical IPs assigned to the local machine (detected, not configured)
+	interfaceAddresses     map[string]net.IP
 	interfaceAddressesLock sync.Mutex
 	interfaceAddressesLock sync.Mutex
-	online                 uint32
-	running                uint32
-	basePath               string
-	peersPath              string
-	networksPath           string
-	localConfigPath        string
-	localConfig            LocalConfig
-	localConfigLock        sync.RWMutex
-	logW                   *sizeLimitWriter
-	log                    *log.Logger
-	gn                     *C.ZT_GoNode
-	zn                     *C.ZT_Node
-	id                     *Identity
-	namedSocketApiServer   *http.Server
-	tcpApiServer           *http.Server
-	runWaitGroup           sync.WaitGroup
+
+	// online and running are atomic flags set to control and monitor background tasks
+	online  uint32
+	running uint32
+
+	basePath        string
+	peersPath       string
+	networksPath    string
+	localConfigPath string
+
+	// localConfig is the current state of local.conf
+	localConfig     LocalConfig
+	localConfigLock sync.RWMutex
+
+	// logs for information, errors, and trace output
+	infoLogW  *sizeLimitWriter
+	errLogW   *sizeLimitWriter
+	traceLogW io.Writer
+	infoLog   *log.Logger
+	errLog    *log.Logger
+	traceLog  *log.Logger
+
+	// gn is the instance of GoNode, the Go wrapper and multithreaded I/O engine
+	gn *C.ZT_GoNode
+
+	// zn is the underlying instance of the ZeroTier core
+	zn *C.ZT_Node
+
+	// id is the identity of this node
+	id *Identity
+
+	// HTTP server instances: one for a named socket (Unix domain or Windows named pipe) and one on a local TCP socket
+	namedSocketApiServer *http.Server
+	tcpApiServer         *http.Server
+
+	// runWaitGroup is used to wait for all node goroutines on shutdown
+	runWaitGroup sync.WaitGroup
 }
 }
 
 
 // NewNode creates and initializes a new instance of the ZeroTier node service
 // NewNode creates and initializes a new instance of the ZeroTier node service
@@ -141,13 +166,18 @@ func NewNode(basePath string) (n *Node, err error) {
 	}
 	}
 
 
 	if n.localConfig.Settings.LogSizeMax >= 0 {
 	if n.localConfig.Settings.LogSizeMax >= 0 {
-		n.logW, err = sizeLimitWriterOpen(path.Join(basePath, "node.log"))
+		n.infoLogW, err = sizeLimitWriterOpen(path.Join(basePath, "info.log"))
 		if err != nil {
 		if err != nil {
 			return
 			return
 		}
 		}
-		n.log = log.New(n.logW, "", log.LstdFlags)
+		n.errLogW, err = sizeLimitWriterOpen(path.Join(basePath, "error.log"))
+		if err != nil {
+			return
+		}
+		n.infoLog = log.New(n.infoLogW, "", log.LstdFlags)
+		n.errLog = log.New(n.errLogW, "", log.LstdFlags)
 	} else {
 	} else {
-		n.log = nullLogger
+		n.infoLog = nullLogger
 	}
 	}
 
 
 	if n.localConfig.Settings.PortSearch {
 	if n.localConfig.Settings.PortSearch {
@@ -159,7 +189,7 @@ func NewNode(basePath string) (n *Node, err error) {
 			portCheckCount++
 			portCheckCount++
 			if checkPort(n.localConfig.Settings.PrimaryPort) {
 			if checkPort(n.localConfig.Settings.PrimaryPort) {
 				if n.localConfig.Settings.PrimaryPort != origPort {
 				if n.localConfig.Settings.PrimaryPort != origPort {
-					n.log.Printf("primary port %d unavailable, found port %d and saved in local.conf", origPort, n.localConfig.Settings.PrimaryPort)
+					n.infoLog.Printf("primary port %d unavailable, found port %d and saved in local.conf", origPort, n.localConfig.Settings.PrimaryPort)
 				}
 				}
 				break
 				break
 			}
 			}
@@ -177,11 +207,11 @@ func NewNode(basePath string) (n *Node, err error) {
 				portCheckCount++
 				portCheckCount++
 				if checkPort(n.localConfig.Settings.SecondaryPort) {
 				if checkPort(n.localConfig.Settings.SecondaryPort) {
 					if n.localConfig.Settings.SecondaryPort != origPort {
 					if n.localConfig.Settings.SecondaryPort != origPort {
-						n.log.Printf("secondary port %d unavailable, found port %d (port search enabled)", origPort, n.localConfig.Settings.SecondaryPort)
+						n.infoLog.Printf("secondary port %d unavailable, found port %d (port search enabled)", origPort, n.localConfig.Settings.SecondaryPort)
 					}
 					}
 					break
 					break
 				}
 				}
-				n.log.Printf("secondary port %d unavailable, trying a random port (port search enabled)", n.localConfig.Settings.SecondaryPort)
+				n.infoLog.Printf("secondary port %d unavailable, trying a random port (port search enabled)", n.localConfig.Settings.SecondaryPort)
 				if portCheckCount <= 64 {
 				if portCheckCount <= 64 {
 					n.localConfig.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt()%uint(len(unassignedPrivilegedPorts))]
 					n.localConfig.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt()%uint(len(unassignedPrivilegedPorts))]
 				} else {
 				} else {
@@ -200,14 +230,14 @@ func NewNode(basePath string) (n *Node, err error) {
 		}
 		}
 		if n.localConfig.Settings.SecondaryPort > 0 && n.localConfig.Settings.SecondaryPort < 65536 {
 		if n.localConfig.Settings.SecondaryPort > 0 && n.localConfig.Settings.SecondaryPort < 65536 {
 			if !checkPort(n.localConfig.Settings.SecondaryPort) {
 			if !checkPort(n.localConfig.Settings.SecondaryPort) {
-				n.log.Printf("WARNING: unable to bind secondary port %d",n.localConfig.Settings.SecondaryPort)
+				n.infoLog.Printf("WARNING: unable to bind secondary port %d", n.localConfig.Settings.SecondaryPort)
 			}
 			}
 		}
 		}
 	}
 	}
 
 
 	n.namedSocketApiServer, n.tcpApiServer, err = createAPIServer(basePath, n)
 	n.namedSocketApiServer, n.tcpApiServer, err = createAPIServer(basePath, n)
 	if err != nil {
 	if err != nil {
-		n.log.Printf("FATAL: unable to start API server: %s", err.Error())
+		n.infoLog.Printf("FATAL: unable to start API server: %s", err.Error())
 		return nil, err
 		return nil, err
 	}
 	}
 
 
@@ -215,21 +245,21 @@ func NewNode(basePath string) (n *Node, err error) {
 	nodesByUserPtr[uintptr(unsafe.Pointer(n))] = n
 	nodesByUserPtr[uintptr(unsafe.Pointer(n))] = n
 	nodesByUserPtrLock.Unlock()
 	nodesByUserPtrLock.Unlock()
 
 
+	// Instantiate GoNode and friends from the land of C/C++
 	cPath := C.CString(basePath)
 	cPath := C.CString(basePath)
 	n.gn = C.ZT_GoNode_new(cPath, C.uintptr_t(uintptr(unsafe.Pointer(n))))
 	n.gn = C.ZT_GoNode_new(cPath, C.uintptr_t(uintptr(unsafe.Pointer(n))))
 	C.free(unsafe.Pointer(cPath))
 	C.free(unsafe.Pointer(cPath))
 	if n.gn == nil {
 	if n.gn == nil {
-		n.log.Println("FATAL: node initialization failed")
+		n.infoLog.Println("FATAL: node initialization failed")
 		nodesByUserPtrLock.Lock()
 		nodesByUserPtrLock.Lock()
 		delete(nodesByUserPtr, uintptr(unsafe.Pointer(n)))
 		delete(nodesByUserPtr, uintptr(unsafe.Pointer(n)))
 		nodesByUserPtrLock.Unlock()
 		nodesByUserPtrLock.Unlock()
 		return nil, ErrNodeInitFailed
 		return nil, ErrNodeInitFailed
 	}
 	}
 	n.zn = (*C.ZT_Node)(C.ZT_GoNode_getNode(n.gn))
 	n.zn = (*C.ZT_Node)(C.ZT_GoNode_getNode(n.gn))
-
 	n.id, err = newIdentityFromCIdentity(C.ZT_Node_identity(unsafe.Pointer(n.zn)))
 	n.id, err = newIdentityFromCIdentity(C.ZT_Node_identity(unsafe.Pointer(n.zn)))
 	if err != nil {
 	if err != nil {
-		n.log.Printf("FATAL: error obtaining node's identity")
+		n.infoLog.Printf("FATAL: error obtaining node's identity")
 		nodesByUserPtrLock.Lock()
 		nodesByUserPtrLock.Lock()
 		delete(nodesByUserPtr, uintptr(unsafe.Pointer(n)))
 		delete(nodesByUserPtr, uintptr(unsafe.Pointer(n)))
 		nodesByUserPtrLock.Unlock()
 		nodesByUserPtrLock.Unlock()
@@ -237,6 +267,8 @@ func NewNode(basePath string) (n *Node, err error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	// Background maintenance goroutine that handles polling for local network changes, cleaning internal data
+	// structures, syncing local config changes, and numerous other things that must happen from time to time.
 	n.runWaitGroup.Add(1)
 	n.runWaitGroup.Add(1)
 	go func() {
 	go func() {
 		defer n.runWaitGroup.Done()
 		defer n.runWaitGroup.Done()
@@ -296,7 +328,7 @@ func NewNode(basePath string) (n *Node, err error) {
 						interfaceAddressesChanged = true
 						interfaceAddressesChanged = true
 						ipCstr := C.CString(ipn.String())
 						ipCstr := C.CString(ipn.String())
 						for pn, p := range ports {
 						for pn, p := range ports {
-							n.log.Printf("UDP binding to port %d on interface %s", p, astr)
+							n.infoLog.Printf("UDP binding to port %d on interface %s", p, astr)
 							primary := C.int(0)
 							primary := C.int(0)
 							if pn == 0 {
 							if pn == 0 {
 								primary = 1
 								primary = 1
@@ -311,7 +343,7 @@ func NewNode(basePath string) (n *Node, err error) {
 						interfaceAddressesChanged = true
 						interfaceAddressesChanged = true
 						ipCstr := C.CString(ipn.String())
 						ipCstr := C.CString(ipn.String())
 						for _, p := range ports {
 						for _, p := range ports {
-							n.log.Printf("UDP closing socket bound to port %d on interface %s", p, astr)
+							n.infoLog.Printf("UDP closing socket bound to port %d on interface %s", p, astr)
 							C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(p))
 							C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(p))
 						}
 						}
 						C.free(unsafe.Pointer(ipCstr))
 						C.free(unsafe.Pointer(ipCstr))
@@ -360,9 +392,9 @@ func NewNode(basePath string) (n *Node, err error) {
 					}
 					}
 				}
 				}
 
 
-				// Trim log if it's gone over its size limit
-				if n.localConfig.Settings.LogSizeMax > 0 && n.logW != nil {
-					_ = n.logW.trim(n.localConfig.Settings.LogSizeMax*1024, 0.5, true)
+				// Trim infoLog if it's gone over its size limit
+				if n.localConfig.Settings.LogSizeMax > 0 && n.infoLogW != nil {
+					_ = n.infoLogW.trim(n.localConfig.Settings.LogSizeMax*1024, 0.5, true)
 				}
 				}
 
 
 				n.localConfigLock.RUnlock()
 				n.localConfigLock.RUnlock()
@@ -440,16 +472,16 @@ func (n *Node) SetLocalConfig(lc *LocalConfig) (restartRequired bool, err error)
 		restartRequired = true
 		restartRequired = true
 	}
 	}
 	if lc.Settings.LogSizeMax < 0 {
 	if lc.Settings.LogSizeMax < 0 {
-		n.log = nullLogger
-		_ = n.logW.Close()
-		n.logW = nil
-	} else if n.logW != nil {
-		n.logW, err = sizeLimitWriterOpen(path.Join(n.basePath, "service.log"))
+		n.infoLog = nullLogger
+		_ = n.infoLogW.Close()
+		n.infoLogW = nil
+	} else if n.infoLogW != nil {
+		n.infoLogW, err = sizeLimitWriterOpen(path.Join(n.basePath, "service.infoLog"))
 		if err == nil {
 		if err == nil {
-			n.log = log.New(n.logW, "", log.LstdFlags)
+			n.infoLog = log.New(n.infoLogW, "", log.LstdFlags)
 		} else {
 		} else {
-			n.log = nullLogger
-			n.logW = nil
+			n.infoLog = nullLogger
+			n.infoLogW = nil
 		}
 		}
 	}
 	}
 
 
@@ -458,12 +490,12 @@ func (n *Node) SetLocalConfig(lc *LocalConfig) (restartRequired bool, err error)
 	return
 	return
 }
 }
 
 
-// Join joins a network
+// Join a network.
 // If tap is nil, the default system tap for this OS/platform is used (if available).
 // If tap is nil, the default system tap for this OS/platform is used (if available).
 func (n *Node) Join(nwid NetworkID, settings *NetworkLocalSettings, tap Tap) (*Network, error) {
 func (n *Node) Join(nwid NetworkID, settings *NetworkLocalSettings, tap Tap) (*Network, error) {
 	n.networksLock.RLock()
 	n.networksLock.RLock()
 	if nw, have := n.networks[nwid]; have {
 	if nw, have := n.networks[nwid]; have {
-		n.log.Printf("join network %.16x ignored: already a member", nwid)
+		n.infoLog.Printf("join network %.16x ignored: already a member", nwid)
 		if settings != nil {
 		if settings != nil {
 			nw.SetLocalSettings(settings)
 			nw.SetLocalSettings(settings)
 		}
 		}
@@ -476,13 +508,13 @@ func (n *Node) Join(nwid NetworkID, settings *NetworkLocalSettings, tap Tap) (*N
 	}
 	}
 	ntap := C.ZT_GoNode_join(n.gn, C.uint64_t(nwid))
 	ntap := C.ZT_GoNode_join(n.gn, C.uint64_t(nwid))
 	if ntap == nil {
 	if ntap == nil {
-		n.log.Printf("join network %.16x failed: tap device failed to initialize (check drivers / kernel modules)", uint64(nwid))
+		n.infoLog.Printf("join network %.16x failed: tap device failed to initialize (check drivers / kernel modules)", uint64(nwid))
 		return nil, ErrTapInitFailed
 		return nil, ErrTapInitFailed
 	}
 	}
 
 
 	nw, err := newNetwork(n, nwid, &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1})
 	nw, err := newNetwork(n, nwid, &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1})
 	if err != nil {
 	if err != nil {
-		n.log.Printf("join network %.16x failed: network failed to initialize: %s", nwid, err.Error())
+		n.infoLog.Printf("join network %.16x failed: network failed to initialize: %s", nwid, err.Error())
 		C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid))
 		C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid))
 		return nil, err
 		return nil, err
 	}
 	}
@@ -496,14 +528,14 @@ func (n *Node) Join(nwid NetworkID, settings *NetworkLocalSettings, tap Tap) (*N
 	return nw, nil
 	return nw, nil
 }
 }
 
 
-// Leave leaves a network
+// Leave a network.
 func (n *Node) Leave(nwid NetworkID) error {
 func (n *Node) Leave(nwid NetworkID) error {
-	n.log.Printf("leaving network %.16x", nwid)
 	n.networksLock.Lock()
 	n.networksLock.Lock()
 	nw := n.networks[nwid]
 	nw := n.networks[nwid]
 	delete(n.networks, nwid)
 	delete(n.networks, nwid)
 	n.networksLock.Unlock()
 	n.networksLock.Unlock()
 	if nw != nil {
 	if nw != nil {
+		n.infoLog.Printf("leaving network %.16x", nwid)
 		nw.leaving()
 		nw.leaving()
 	}
 	}
 	C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid))
 	C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid))
@@ -668,7 +700,7 @@ func (n *Node) stateObjectGet(objType int, id [2]uint64) ([]byte, bool) {
 
 
 func (n *Node) handleTrace(traceMessage string) {
 func (n *Node) handleTrace(traceMessage string) {
 	if len(traceMessage) > 0 {
 	if len(traceMessage) > 0 {
-		n.log.Print("TRACE: " + traceMessage)
+		n.infoLog.Print("TRACE: " + traceMessage)
 	}
 	}
 }
 }
 
 

+ 330 - 20
include/ZeroTierCore.h

@@ -19,6 +19,25 @@
 #ifndef ZT_ZEROTIER_API_H
 #ifndef ZT_ZEROTIER_API_H
 #define ZT_ZEROTIER_API_H
 #define ZT_ZEROTIER_API_H
 
 
+/* ZT_PACKED_STRUCT encloses structs whose contents should be bit-packed.
+ * Nearly all compilers support this. These macros detect the compiler and
+ * define it correctly for gcc/icc/clang or MSC. */
+#ifndef ZT_PACKED_STRUCT
+#if defined(__GCC__) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__INTEL_COMPILER) || defined(__clang__)
+#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
+#define ZT_PACKED_STRUCT_START
+#define ZT_PACKED_STRUCT_END __attribute__((packed))
+#endif
+#ifdef _MSC_VER
+#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
+#define ZT_PACKED_STRUCT_START __pragma(pack(push,1))
+#define ZT_PACKED_STRUCT_END __pragma(pack(pop))
+#endif
+#endif
+#ifndef ZT_PACKED_STRUCT
+#error Missing a macro to define ZT_PACKED_STRUCT for your compiler.
+#endif
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 #include <cstdint>
 #include <cstdint>
 extern "C" {
 extern "C" {
@@ -26,26 +45,21 @@ extern "C" {
 #include <stdint.h>
 #include <stdint.h>
 #endif
 #endif
 
 
-/* For struct sockaddr_storage, which is referenced here. */
 #if defined(_WIN32) || defined(_WIN64)
 #if defined(_WIN32) || defined(_WIN64)
 #include <WinSock2.h>
 #include <WinSock2.h>
 #include <WS2tcpip.h>
 #include <WS2tcpip.h>
 #include <Windows.h>
 #include <Windows.h>
-#else /* not Windows */
+#else
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
 #include <sys/types.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/socket.h>
-#endif /* Windows or not */
+#endif
 
 
-/* This symbol may be defined in a build environment or before including this
- * header if you need to prepend something to function specifications. */
 #ifndef ZT_SDK_API
 #ifndef ZT_SDK_API
 #define ZT_SDK_API
 #define ZT_SDK_API
 #endif
 #endif
 
 
-/****************************************************************************/
-/* Core constants                                                           */
 /****************************************************************************/
 /****************************************************************************/
 
 
 /**
 /**
@@ -174,13 +188,6 @@ extern "C" {
  */
  */
 #define ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH 7
 #define ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH 7
 
 
-/**
- * Maximum number of multicast group subscriptions on a local virtual network interface
- *
- * This coincides with many operating systems' maximum values and is rather huge.
- */
-#define ZT_MAX_MULTICAST_SUBSCRIPTIONS 1024
-
 /* Rule specification contants **********************************************/
 /* Rule specification contants **********************************************/
 
 
 /**
 /**
@@ -269,7 +276,310 @@ extern "C" {
 #define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
 #define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
 
 
 /****************************************************************************/
 /****************************************************************************/
-/* Structures and other types                                               */
+
+/**
+ * Credential type IDs
+ *
+ * These are mostly used internally but are declared here so they can be used
+ * in trace messages.
+ */
+enum ZT_CredentialType
+{
+	ZT_CREDENTIAL_TYPE_NULL = 0,
+	ZT_CREDENTIAL_TYPE_COM = 1,
+	ZT_CREDENTIAL_TYPE_CAPABILITY = 2,
+	ZT_CREDENTIAL_TYPE_TAG = 3,
+	ZT_CREDENTIAL_TYPE_COO = 4,
+	ZT_CREDENTIAL_TYPE_REVOCATION = 6
+};
+
+/* Trace events are sent and received as packed structures of a fixed size.
+ * Normally we don't use this form of brittle encoding but in this case the
+ * performance benefit is non-trivial as events are generated in critical
+ * areas of the code.
+ *
+ * NOTE: all integer fields larger than one byte are stored in big-endian
+ * "network" byte order in these structures. */
+
+/**
+ * Flag indicating that VL1 tracing should be generated
+ */
+#define ZT_TRACE_FLAG_VL1           0x01
+
+/**
+ * Flag indicating that VL2 (virtual network) tracing should be generated
+ */
+#define ZT_TRACE_FLAG_VL2           0x02
+
+/**
+ * Flag indicating that VL2 network filter tracing should be generated (separate because this can be very verbose)
+ */
+#define ZT_TRACE_FLAG_VL2_FILTER    0x04
+
+/**
+ * Flag indicating that VL2 multicast propagation should be reported
+ */
+#define ZT_TRACE_FLAG_VL2_MULTICAST 0x08
+
+/**
+ * Trace event types
+ *
+ * All trace event structures start with a size and type.
+ */
+enum ZT_TraceEventType
+{
+	/* VL1 events related to the peer-to-peer layer */
+	ZT_TRACE_VL1_RESETTING_PATHS_IN_SCOPE = 1,
+	ZT_TRACE_VL1_TRYING_NEW_PATH = 2,
+	ZT_TRACE_VL1_LEARNED_NEW_PATH = 3,
+	ZT_TRACE_VL1_INCOMING_PACKET_DROPPED = 4,
+
+	/* VL2 events relate to virtual networks, packet filtering, and authentication */
+	ZT_TRACE_VL2_OUTGOING_FRAME_DROPPED = 100,
+	ZT_TRACE_VL2_INCOMING_FRAME_DROPPED = 101,
+	ZT_TRACE_VL2_NETWORK_CONFIG_REQUESTED = 102,
+	ZT_TRACE_VL2_NETWORK_FILTER = 103
+};
+
+/**
+ * Trace VL1 packet drop reasons
+ */
+enum ZT_TracePacketDropReason
+{
+	ZT_TRACE_PACKET_DROP_REASON_UNSPECIFIED = 0,
+	ZT_TRACE_PACKET_DROP_REASON_PEER_TOO_OLD = 1,
+	ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET = 2,
+	ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED = 3,
+	ZT_TRACE_PACKET_DROP_REASON_RATE_LIMIT_EXCEEDED = 4,
+	ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT = 5,
+	ZT_TRACE_PACKET_DROP_REASON_INVALID_COMPRESSED_DATA = 6,
+	ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB = 7
+};
+
+/**
+ * Trace VL2 frame drop reasons
+ */
+enum ZT_TraceFrameDropReason
+{
+	ZT_TRACE_FRAME_DROP_REASON_UNSPECIFIED = 0,
+	ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_REMOTE = 1,
+	ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_LOCAL = 2,
+	ZT_TRACE_FRAME_DROP_REASON_MULTICAST_DISABLED = 3,
+	ZT_TRACE_FRAME_DROP_REASON_BROADCAST_DISABLED = 4,
+	ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED = 5,
+	ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED_AT_BRIDGE_REPLICATION = 6,
+	ZT_TRACE_FRAME_DROP_REASON_PERMISSION_DENIED = 7
+};
+
+/**
+ * Address types for ZT_TraceEventPathAddress
+ *
+ * These are currently the same as the types in Endpoint.hpp and should remain so
+ * if possible for consistency. Not all of these are used (yet?) but they are defined
+ * for possible future use and the structure is sized to support them.
+ */
+enum ZT_TraceEventPathAddressType
+{
+	ZT_TRACE_EVENT_PATH_TYPE_NIL =          0, /* none/empty */
+	ZT_TRACE_EVENT_PATH_TYPE_INETADDR_V4 =  1, /* 4-byte IPv4 */
+	ZT_TRACE_EVENT_PATH_TYPE_INETADDR_V6 =  2, /* 16-byte IPv6 */
+	ZT_TRACE_EVENT_PATH_TYPE_DNSNAME =      3, /* C string */
+	ZT_TRACE_EVENT_PATH_TYPE_ZEROTIER =     4, /* 5-byte ZeroTier + 48-byte identity hash */
+	ZT_TRACE_EVENT_PATH_TYPE_URL =          5, /* C string */
+	ZT_TRACE_EVENT_PATH_TYPE_ETHERNET =     6  /* 6-byte Ethernet */
+};
+
+/**
+ * Reasons for trying new paths
+ */
+enum ZT_TraceTryingNewPathReason
+{
+	ZT_TRACE_TRYING_NEW_PATH_REASON_PACKET_RECEIVED_FROM_UNKNOWN_PATH = 1,
+	ZT_TRACE_TRYING_NEW_PATH_REASON_RECEIVED_PUSH_DIRECT_PATHS = 2,
+	ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS = 3
+};
+
+/**
+ * Reasons for credential rejection
+ */
+enum ZT_TraceCredentialRejectionReason
+{
+	ZT_TRACE_CREDENTIAL_REJECTION_REASON_SIGNATURE_VERIFICATION_FAILED = 1,
+	ZT_TRACE_CREDENTIAL_REJECTION_REASON_REVOKED = 2,
+	ZT_TRACE_CREDENTIAL_REJECTION_REASON_OLDER_THAN_LATEST = 3,
+	ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID = 4
+};
+
+/**
+ * Physical path address from a trace event
+ *
+ * This is a special packed address format that roughly mirrors Endpoint in the core
+ * and is designed to support both present and future address types.
+ */
+ZT_PACKED_STRUCT(struct ZT_TraceEventPathAddress
+{
+	uint8_t type;        /* ZT_TraceEventPathAddressType */
+	uint8_t address[63]; /* Type-dependent address: 4-byte IPv4, 16-byte IPV6, etc. */
+	uint16_t port;       /* UDP/TCP port for address types for which this is meaningful */
+});
+
+/**
+ * Header for all trace events
+ *
+ * All packet types begin with these fields in this order.
+ */
+ZT_PACKED_STRUCT(struct ZT_TraceEvent
+{
+	uint16_t evSize;     /* sizeof(ZT_TraceEvent_XX structure) (inclusive size in bytes) */
+	uint16_t evType;     /* ZT_TraceEventType */
+});
+
+/* Temporary macros to make it easier to declare all ZT_TraceEvent's sub-types */
+#define _ZT_TRACE_EVENT_STRUCT_START(e) ZT_PACKED_STRUCT_START struct ZT_TraceEvent_##e { \
+	uint16_t evSize; \
+	uint16_t evType;
+#define _ZT_TRACE_EVENT_STRUCT_END() } ZT_PACKED_STRUCT_END;
+
+/**
+ * Node is resetting all paths in a given address scope
+ *
+ * This happens when the node detects and external surface IP addressing change
+ * via a trusted (usually root) peer. It's used to renegotiate links when nodes
+ * move around on the network.
+ */
+_ZT_TRACE_EVENT_STRUCT_START(VL1_RESETTING_PATHS_IN_SCOPE)
+	uint64_t reporter;                               /* ZeroTier address that triggered the reset */
+	uint8_t reporterIdentityHash[48];                /* full identity hash of triggering node's identity */
+	struct ZT_TraceEventPathAddress from;            /* physical origin of triggering packet */
+	struct ZT_TraceEventPathAddress oldExternal;     /* previous detected external address */
+	struct ZT_TraceEventPathAddress newExternal;     /* new detected external address */
+	uint8_t scope;                                   /* IP scope being reset */
+_ZT_TRACE_EVENT_STRUCT_END()
+
+/**
+ * Node is trying a new path
+ *
+ * Paths are tried in response to PUSH_DIRECT_PATHS, RENDEZVOUS, and other places
+ * we might hear of them. A node tries a path by sending a trial message to it.
+ */
+_ZT_TRACE_EVENT_STRUCT_START(VL1_TRYING_NEW_PATH)
+	uint64_t address;                                /* short address of node we're trying to reach */
+	uint8_t identityHash[48];                        /* identity hash of node we're trying to reach */
+	struct ZT_TraceEventPathAddress physicalAddress; /* physical address being tried */
+	struct ZT_TraceEventPathAddress triggerAddress;  /* physical origin of triggering packet */
+	uint64_t triggeringPacketId;                     /* packet ID of triggering packet */
+	uint8_t triggeringPacketVerb;                    /* packet verb of triggering packet */
+	uint64_t triggeredByAddress;                     /* short address of node triggering attempt */
+	uint8_t triggeredByIdentityHash[48];             /* full identity hash of node triggering attempt */
+	uint8_t reason;                                  /* ZT_TraceTryingNewPathReason */
+_ZT_TRACE_EVENT_STRUCT_END()
+
+/**
+ * Node has learned a new path to another node
+ */
+_ZT_TRACE_EVENT_STRUCT_START(VL1_LEARNED_NEW_PATH)
+	uint64_t packetId;                               /* packet ID of confirming packet */
+	uint64_t address;                                /* short address of learned peer */
+	uint8_t identityHash[48];                        /* full identity hash of learned peer */
+	struct ZT_TraceEventPathAddress physicalAddress; /* physical address learned */
+	struct ZT_TraceEventPathAddress replaced;        /* if non-empty, an older address that was replaced */
+_ZT_TRACE_EVENT_STRUCT_END()
+
+/**
+ * An incoming packet was dropped at the VL1 level
+ *
+ * This indicates a packet that passed MAC check but was dropped for some other
+ * reason such as rate limits, being malformed, etc.
+ */
+_ZT_TRACE_EVENT_STRUCT_START(VL1_INCOMING_PACKET_DROPPED)
+	uint64_t packetId;                               /* packet ID of failed packet */
+	uint64_t networkId;                              /* VL2 network ID or 0 if unrelated to a network or unknown */
+	uint64_t address;                                /* short address that sent packet */
+	uint8_t identityHash[48];                        /* full identity hash of sending node */
+	struct ZT_TraceEventPathAddress physicalAddress; /* physical origin address of packet */
+	uint8_t hops;                                    /* hop count of packet */
+	uint8_t verb;                                    /* packet verb */
+	uint8_t reason;                                  /* ZT_TracePacketDropReason */
+_ZT_TRACE_EVENT_STRUCT_END()
+
+/**
+ * Node declined to send a packet read from a local network port/tap
+ */
+_ZT_TRACE_EVENT_STRUCT_START(VL2_OUTGOING_FRAME_DROPPED)
+	uint64_t networkId;                              /* network ID */
+	uint64_t sourceMac;                              /* source MAC address */
+	uint64_t destMac;                                /* destination MAC address */
+	uint16_t etherType;                              /* Ethernet type of frame */
+	uint16_t frameLength;                            /* length of dropped frame */
+	uint8_t frameHead[64];                           /* first up to 64 bytes of dropped frame */
+	uint8_t reason;                                  /* ZT_TraceFrameDropReason */
+_ZT_TRACE_EVENT_STRUCT_END()
+
+/**
+ * An incoming frame was dropped
+ */
+_ZT_TRACE_EVENT_STRUCT_START(VL2_INCOMING_FRAME_DROPPED)
+	uint64_t packetId;                               /* VL1 packet ID */
+	uint64_t networkId;                              /* VL2 network ID */
+	uint64_t sourceMac;                              /* 48-bit source MAC */
+	uint64_t destMac;                                /* 48-bit destination MAC */
+	uint64_t address;                                /* short address of sending peer */
+	struct ZT_TraceEventPathAddress physicalAddress; /* physical source address of packet */
+	uint8_t hops;                                    /* hop count of packet */
+	uint16_t frameLength;                            /* length of frame in bytes */
+	uint8_t frameHead[64];                           /* first up to 64 bytes of dropped frame */
+	uint8_t verb;                                    /* packet verb indicating how frame was sent */
+	uint8_t credentialRequestSent;                   /* if non-zero a request for credentials was sent */
+	uint8_t reason;                                  /* ZT_TraceFrameDropReason */
+_ZT_TRACE_EVENT_STRUCT_END()
+
+/**
+ * Node is requesting a new network config and certificate from a network controller
+ */
+_ZT_TRACE_EVENT_STRUCT_START(VL2_NETWORK_CONFIG_REQUESTED)
+	uint64_t networkId;                              /* VL2 network ID */
+_ZT_TRACE_EVENT_STRUCT_END()
+
+/**
+ * Network filter trace results
+ *
+ * These are generated when filter tracing is enabled to allow filters to be debugged.
+ * Format for rule set logs is documented elsewhere.
+ */
+_ZT_TRACE_EVENT_STRUCT_START(VL2_NETWORK_FILTER)
+	uint64_t networkId;                              /* VL2 network ID */
+	uint8_t primaryRuleSetLog[512];                  /* primary rule set log */
+	uint8_t matchingCapabilityRuleSetLog[512];       /* capability rule set log (if any) */
+	uint32_t matchingCapabilityId;                   /* capability ID or 0 if none */
+	int64_t matchingCapabilityTimestamp;             /* capability timestamp or 0 if none */
+	uint64_t source;                                 /* source ZeroTier address */
+	uint64_t dest;                                   /* destination ZeroTier address */
+	uint64_t sourceMac;                              /* packet source MAC */
+	uint64_t destMac;                                /* packet destination MAC */
+	uint16_t frameLength;                            /* length of filtered frame */
+	uint8_t frameHead[64];                           /* first up to 64 bytes of filtered frame */
+	uint16_t etherType;                              /* frame Ethernet type */
+	uint16_t vlanId;                                 /* frame VLAN ID (currently unused, always 0) */
+	uint8_t noTee;                                   /* if true noTee flag was set in filter */
+	uint8_t inbound;                                 /* direction: 1 for inbound, 0 for outbound */
+	int8_t accept;                                   /* 0: drop, 1: accept, 2: "super-accept" */
+_ZT_TRACE_EVENT_STRUCT_END()
+
+/**
+ * An incoming credential from a peer was rejected
+ */
+_ZT_TRACE_EVENT_STRUCT_START(VL2_CREDENTIAL_REJECTED)
+	uint64_t networkId;                              /* VL2 network ID */
+	uint64_t address;                                /* short address of sender */
+	uint32_t credentialId;                           /* credential ID */
+	int64_t credentialTimestamp;                     /* credential timestamp */
+	uint8_t credentialType;                          /* credential type */
+	uint8_t reason;                                  /* ZT_TraceCredentialRejectionReason */
+_ZT_TRACE_EVENT_STRUCT_END()
+
+#undef _ZT_TRACE_EVENT_STRUCT_START
+#undef _ZT_TRACE_EVENT_STRUCT_END
+
 /****************************************************************************/
 /****************************************************************************/
 
 
 /**
 /**
@@ -390,7 +700,7 @@ enum ZT_Event
 	 * These events are only generated if this is a TRACE-enabled build.
 	 * These events are only generated if this is a TRACE-enabled build.
 	 * This is for local debug traces, not remote trace diagnostics.
 	 * This is for local debug traces, not remote trace diagnostics.
 	 *
 	 *
-	 * Meta-data: C string, TRACE message
+	 * Meta-data: struct of type ZT_Trace_*
 	 */
 	 */
 	ZT_EVENT_TRACE = 5,
 	ZT_EVENT_TRACE = 5,
 
 
@@ -1164,8 +1474,6 @@ enum ZT_StateObjectType
  */
  */
 typedef void ZT_Node;
 typedef void ZT_Node;
 
 
-/****************************************************************************/
-/* Callbacks used by Node API                                               */
 /****************************************************************************/
 /****************************************************************************/
 
 
 /**
 /**
@@ -1357,8 +1665,6 @@ typedef int (*ZT_PathLookupFunction)(
 	struct sockaddr_storage *);       /* Result buffer */
 	struct sockaddr_storage *);       /* Result buffer */
 
 
 /****************************************************************************/
 /****************************************************************************/
-/* C Node API                                                               */
-/****************************************************************************/
 
 
 /**
 /**
  * Structure for configuring ZeroTier core callback functions
  * Structure for configuring ZeroTier core callback functions
@@ -1735,6 +2041,8 @@ ZT_SDK_API void ZT_Node_setController(ZT_Node *node,void *networkConfigMasterIns
  */
  */
 ZT_SDK_API enum ZT_ResultCode ZT_Node_setPhysicalPathConfiguration(ZT_Node *node,const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
 ZT_SDK_API enum ZT_ResultCode ZT_Node_setPhysicalPathConfiguration(ZT_Node *node,const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
 
 
+/****************************************************************************/
+
 /**
 /**
  * Generate a new identity
  * Generate a new identity
  *
  *
@@ -1844,6 +2152,8 @@ ZT_SDK_API void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int include
  */
  */
 ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id);
 ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id);
 
 
+/****************************************************************************/
+
 /**
 /**
  * Get ZeroTier One version
  * Get ZeroTier One version
  *
  *

+ 4 - 4
node/AES.cpp

@@ -20,7 +20,7 @@
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 static inline uint32_t readuint32_t(const void *in)
 static inline uint32_t readuint32_t(const void *in)
 {
 {
 	uint32_t v = ((const uint8_t *)in)[0];
 	uint32_t v = ((const uint8_t *)in)[0];
@@ -161,7 +161,7 @@ void AES::_ctrSW(const uint8_t iv[16],const void *in,unsigned int len,void *out)
 	while (len >= 16) {
 	while (len >= 16) {
 		_encryptSW((const uint8_t *)ctr,(uint8_t *)cenc);
 		_encryptSW((const uint8_t *)ctr,(uint8_t *)cenc);
 		ctr[1] = Utils::hton(++bctr);
 		ctr[1] = Utils::hton(++bctr);
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		for(unsigned int k=0;k<16;++k)
 		for(unsigned int k=0;k<16;++k)
 				*(o++) = *(i++) ^ ((uint8_t *)cenc)[k];
 				*(o++) = *(i++) ^ ((uint8_t *)cenc)[k];
 #else
 #else
@@ -339,7 +339,7 @@ void AES::_gmacSW(const uint8_t iv[12],const uint8_t *in,unsigned int len,uint8_
 	uint64_t y0 = 0,y1 = 0;
 	uint64_t y0 = 0,y1 = 0;
 
 
 	while (len >= 16) {
 	while (len >= 16) {
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		for(unsigned int i=0;i<8;++i) ((uint8_t *)&y0)[i] ^= *(in++);
 		for(unsigned int i=0;i<8;++i) ((uint8_t *)&y0)[i] ^= *(in++);
 		for(unsigned int i=0;i<8;++i) ((uint8_t *)&y1)[i] ^= *(in++);
 		for(unsigned int i=0;i<8;++i) ((uint8_t *)&y1)[i] ^= *(in++);
 #else
 #else
@@ -369,7 +369,7 @@ void AES::_gmacSW(const uint8_t iv[12],const uint8_t *in,unsigned int len,uint8_
 	((uint8_t *)iv2)[14] = 0;
 	((uint8_t *)iv2)[14] = 0;
 	((uint8_t *)iv2)[15] = 1;
 	((uint8_t *)iv2)[15] = 1;
 	_encryptSW((const uint8_t *)iv2,(uint8_t *)iv2);
 	_encryptSW((const uint8_t *)iv2,(uint8_t *)iv2);
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 	for(unsigned int i=0;i<8;++i) out[i] = ((const uint8_t *)&y0)[i] ^ ((const uint8_t *)iv2)[i];
 	for(unsigned int i=0;i<8;++i) out[i] = ((const uint8_t *)&y0)[i] ^ ((const uint8_t *)iv2)[i];
 	for(unsigned int i=8;i<16;++i) out[i] = ((const uint8_t *)&y1)[i-8] ^ ((const uint8_t *)iv2)[i];
 	for(unsigned int i=8;i<16;++i) out[i] = ((const uint8_t *)&y1)[i-8] ^ ((const uint8_t *)iv2)[i];
 #else
 #else

+ 6 - 6
node/Buf.hpp

@@ -145,7 +145,7 @@ public:
 	{
 	{
 		const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
 		const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
 		ii += 2;
 		ii += 2;
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		return (
 		return (
 			((uint16_t)data[s] << 8U) |
 			((uint16_t)data[s] << 8U) |
 			(uint16_t)data[s + 1]);
 			(uint16_t)data[s + 1]);
@@ -164,7 +164,7 @@ public:
 	{
 	{
 		const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
 		const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
 		ii += 4;
 		ii += 4;
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		return (
 		return (
 			((uint32_t)data[s] << 24U) |
 			((uint32_t)data[s] << 24U) |
 			((uint32_t)data[s + 1] << 16U) |
 			((uint32_t)data[s + 1] << 16U) |
@@ -185,7 +185,7 @@ public:
 	{
 	{
 		const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
 		const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
 		ii += 8;
 		ii += 8;
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		return (
 		return (
 			((uint64_t)data[s] << 56U) |
 			((uint64_t)data[s] << 56U) |
 			((uint64_t)data[s + 1] << 48U) |
 			((uint64_t)data[s + 1] << 48U) |
@@ -340,7 +340,7 @@ public:
 	{
 	{
 		const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
 		const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
 		ii += 2;
 		ii += 2;
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		data[s] = (uint8_t)(n >> 8U);
 		data[s] = (uint8_t)(n >> 8U);
 		data[s + 1] = (uint8_t)n;
 		data[s + 1] = (uint8_t)n;
 #else
 #else
@@ -358,7 +358,7 @@ public:
 	{
 	{
 		const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
 		const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
 		ii += 4;
 		ii += 4;
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		data[s] = (uint8_t)(n >> 24U);
 		data[s] = (uint8_t)(n >> 24U);
 		data[s + 1] = (uint8_t)(n >> 16U);
 		data[s + 1] = (uint8_t)(n >> 16U);
 		data[s + 2] = (uint8_t)(n >> 8U);
 		data[s + 2] = (uint8_t)(n >> 8U);
@@ -378,7 +378,7 @@ public:
 	{
 	{
 		const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
 		const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
 		ii += 8;
 		ii += 8;
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		data[s] = (uint8_t)(n >> 56U);
 		data[s] = (uint8_t)(n >> 56U);
 		data[s + 1] = (uint8_t)(n >> 48U);
 		data[s + 1] = (uint8_t)(n >> 48U);
 		data[s + 2] = (uint8_t)(n >> 40U);
 		data[s + 2] = (uint8_t)(n >> 40U);

+ 4 - 4
node/Buffer.hpp

@@ -25,7 +25,7 @@
 #include "Constants.hpp"
 #include "Constants.hpp"
 #include "Utils.hpp"
 #include "Utils.hpp"
 
 
-#if defined(__GNUC__) && (!defined(ZT_NO_TYPE_PUNNING))
+#if defined(__GNUC__) && (!defined(ZT_NO_UNALIGNED_ACCESS))
 #define ZT_VAR_MAY_ALIAS __attribute__((__may_alias__))
 #define ZT_VAR_MAY_ALIAS __attribute__((__may_alias__))
 #else
 #else
 #define ZT_VAR_MAY_ALIAS
 #define ZT_VAR_MAY_ALIAS
@@ -171,7 +171,7 @@ public:
 	{
 	{
 		if (unlikely((i + sizeof(T)) > _l))
 		if (unlikely((i + sizeof(T)) > _l))
 			throw ZT_EXCEPTION_OUT_OF_BOUNDS;
 			throw ZT_EXCEPTION_OUT_OF_BOUNDS;
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		uint8_t *p = reinterpret_cast<uint8_t *>(_b + i);
 		uint8_t *p = reinterpret_cast<uint8_t *>(_b + i);
 		for(unsigned int x=1;x<=sizeof(T);++x)
 		for(unsigned int x=1;x<=sizeof(T);++x)
 			*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
 			*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
@@ -193,7 +193,7 @@ public:
 	{
 	{
 		if (unlikely((i + sizeof(T)) > _l))
 		if (unlikely((i + sizeof(T)) > _l))
 			throw ZT_EXCEPTION_OUT_OF_BOUNDS;
 			throw ZT_EXCEPTION_OUT_OF_BOUNDS;
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		T v = 0;
 		T v = 0;
 		const uint8_t *p = reinterpret_cast<const uint8_t *>(_b + i);
 		const uint8_t *p = reinterpret_cast<const uint8_t *>(_b + i);
 		for(unsigned int x=0;x<sizeof(T);++x) {
 		for(unsigned int x=0;x<sizeof(T);++x) {
@@ -219,7 +219,7 @@ public:
 	{
 	{
 		if (unlikely((_l + sizeof(T)) > C))
 		if (unlikely((_l + sizeof(T)) > C))
 			throw ZT_EXCEPTION_OUT_OF_BOUNDS;
 			throw ZT_EXCEPTION_OUT_OF_BOUNDS;
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		uint8_t *p = reinterpret_cast<uint8_t *>(_b + _l);
 		uint8_t *p = reinterpret_cast<uint8_t *>(_b + _l);
 		for(unsigned int x=1;x<=sizeof(T);++x)
 		for(unsigned int x=1;x<=sizeof(T);++x)
 			*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
 			*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));

+ 1 - 0
node/CMakeLists.txt

@@ -73,6 +73,7 @@ set(core_src
 	SHA512.cpp
 	SHA512.cpp
 	Switch.cpp
 	Switch.cpp
 	Topology.cpp
 	Topology.cpp
+	Trace.cpp
 	Utils.cpp
 	Utils.cpp
 )
 )
 
 

+ 1 - 1
node/Capability.hpp

@@ -58,7 +58,7 @@ class Capability : public Credential
 	friend class Credential;
 	friend class Credential;
 
 
 public:
 public:
-	static ZT_ALWAYS_INLINE Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_CAPABILITY; }
+	static ZT_ALWAYS_INLINE ZT_CredentialType credentialType() { return ZT_CREDENTIAL_TYPE_CAPABILITY; }
 
 
 	ZT_ALWAYS_INLINE Capability() :
 	ZT_ALWAYS_INLINE Capability() :
 		_nwid(0),
 		_nwid(0),

+ 17 - 22
node/CertificateOfMembership.hpp

@@ -69,7 +69,7 @@ class CertificateOfMembership : public Credential
 	friend class Credential;
 	friend class Credential;
 
 
 public:
 public:
-	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COM; }
+	static ZT_ALWAYS_INLINE ZT_CredentialType credentialType() { return ZT_CREDENTIAL_TYPE_COM; }
 
 
 	/**
 	/**
 	 * Reserved qualifier IDs
 	 * Reserved qualifier IDs
@@ -101,7 +101,7 @@ public:
 	/**
 	/**
 	 * Create an empty certificate of membership
 	 * Create an empty certificate of membership
 	 */
 	 */
-	inline CertificateOfMembership() :
+	ZT_ALWAYS_INLINE CertificateOfMembership() :
 		_qualifierCount(0),
 		_qualifierCount(0),
 		_signatureLength(0) {}
 		_signatureLength(0) {}
 
 
@@ -113,7 +113,7 @@ public:
 	 * @param nwid Network ID
 	 * @param nwid Network ID
 	 * @param issuedTo Certificate recipient
 	 * @param issuedTo Certificate recipient
 	 */
 	 */
-	inline CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
+	ZT_ALWAYS_INLINE CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
 	{
 	{
 		_qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP;
 		_qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP;
 		_qualifiers[0].value = timestamp;
 		_qualifiers[0].value = timestamp;
@@ -135,22 +135,22 @@ public:
 	 * @param startAt Position to start in buffer
 	 * @param startAt Position to start in buffer
 	 */
 	 */
 	template<unsigned int C>
 	template<unsigned int C>
-	inline CertificateOfMembership(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
+	ZT_ALWAYS_INLINE CertificateOfMembership(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
 
 
 	/**
 	/**
 	 * @return True if there's something here
 	 * @return True if there's something here
 	 */
 	 */
-	inline operator bool() const { return (_qualifierCount != 0); }
+	ZT_ALWAYS_INLINE operator bool() const { return (_qualifierCount != 0); }
 
 
 	/**
 	/**
 	 * @return Credential ID, always 0 for COMs
 	 * @return Credential ID, always 0 for COMs
 	 */
 	 */
-	inline uint32_t id() const { return 0; }
+	ZT_ALWAYS_INLINE uint32_t id() const { return 0; }
 
 
 	/**
 	/**
 	 * @return Timestamp for this cert and maximum delta for timestamp
 	 * @return Timestamp for this cert and maximum delta for timestamp
 	 */
 	 */
-	inline int64_t timestamp() const
+	ZT_ALWAYS_INLINE int64_t timestamp() const
 	{
 	{
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
 			if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
@@ -162,7 +162,7 @@ public:
 	/**
 	/**
 	 * @return Address to which this cert was issued
 	 * @return Address to which this cert was issued
 	 */
 	 */
-	inline Address issuedTo() const
+	ZT_ALWAYS_INLINE Address issuedTo() const
 	{
 	{
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO)
 			if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO)
@@ -174,7 +174,7 @@ public:
 	/**
 	/**
 	 * @return Network ID for which this cert was issued
 	 * @return Network ID for which this cert was issued
 	 */
 	 */
-	inline uint64_t networkId() const
+	ZT_ALWAYS_INLINE uint64_t networkId() const
 	{
 	{
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			if (_qualifiers[i].id == COM_RESERVED_ID_NETWORK_ID)
 			if (_qualifiers[i].id == COM_RESERVED_ID_NETWORK_ID)
@@ -192,7 +192,7 @@ public:
 	 * @param value Qualifier value
 	 * @param value Qualifier value
 	 * @param maxDelta Qualifier maximum allowed difference (absolute value of difference)
 	 * @param maxDelta Qualifier maximum allowed difference (absolute value of difference)
 	 */
 	 */
-	inline void setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta)
+	ZT_ALWAYS_INLINE void setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta)
 	{
 	{
 		_signedBy.zero();
 		_signedBy.zero();
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 		for(unsigned int i=0;i<_qualifierCount;++i) {
@@ -211,7 +211,7 @@ public:
 		}
 		}
 	}
 	}
 
 
-	inline void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); }
+	ZT_ALWAYS_INLINE void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); }
 
 
 	/**
 	/**
 	 * Compare two certificates for parameter agreement
 	 * Compare two certificates for parameter agreement
@@ -226,7 +226,7 @@ public:
 	 * @param other Cert to compare with
 	 * @param other Cert to compare with
 	 * @return True if certs agree and 'other' may be communicated with
 	 * @return True if certs agree and 'other' may be communicated with
 	 */
 	 */
-	inline bool agreesWith(const CertificateOfMembership &other) const
+	ZT_ALWAYS_INLINE bool agreesWith(const CertificateOfMembership &other) const
 	{
 	{
 		unsigned int myidx = 0;
 		unsigned int myidx = 0;
 		unsigned int otheridx = 0;
 		unsigned int otheridx = 0;
@@ -268,7 +268,7 @@ public:
 	 * @param with Identity to sign with, must include private key
 	 * @param with Identity to sign with, must include private key
 	 * @return True if signature was successful
 	 * @return True if signature was successful
 	 */
 	 */
-	inline bool sign(const Identity &with)
+	ZT_ALWAYS_INLINE bool sign(const Identity &with)
 	{
 	{
 		uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
 		uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
 		unsigned int ptr = 0;
 		unsigned int ptr = 0;
@@ -294,17 +294,12 @@ public:
 	 * @param RR Runtime environment for looking up peers
 	 * @param RR Runtime environment for looking up peers
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 */
 	 */
-	inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
-
-	/**
-	 * @return True if signed
-	 */
-	inline bool isSigned() const { return (_signedBy); }
+	ZT_ALWAYS_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
 
 
 	/**
 	/**
 	 * @return Address that signed this certificate or null address if none
 	 * @return Address that signed this certificate or null address if none
 	 */
 	 */
-	inline const Address &signedBy() const { return _signedBy; }
+	ZT_ALWAYS_INLINE const Address &signedBy() const { return _signedBy; }
 
 
 	template<unsigned int C>
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b) const
 	inline void serialize(Buffer<C> &b) const
@@ -369,7 +364,7 @@ public:
 		return (p - startAt);
 		return (p - startAt);
 	}
 	}
 
 
-	inline bool operator==(const CertificateOfMembership &c) const
+	ZT_ALWAYS_INLINE bool operator==(const CertificateOfMembership &c) const
 	{
 	{
 		if (_signedBy != c._signedBy)
 		if (_signedBy != c._signedBy)
 			return false;
 			return false;
@@ -385,7 +380,7 @@ public:
 		}
 		}
 		return (memcmp(_signature,c._signature,_signatureLength) == 0);
 		return (memcmp(_signature,c._signature,_signatureLength) == 0);
 	}
 	}
-	inline bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
+	ZT_ALWAYS_INLINE bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
 
 
 private:
 private:
 	struct _Qualifier
 	struct _Qualifier

+ 23 - 23
node/CertificateOfOwnership.hpp

@@ -46,7 +46,7 @@ class CertificateOfOwnership : public Credential
 	friend class Credential;
 	friend class Credential;
 
 
 public:
 public:
-	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COO; }
+	static ZT_ALWAYS_INLINE ZT_CredentialType credentialType() { return ZT_CREDENTIAL_TYPE_COO; }
 
 
 	enum Thing
 	enum Thing
 	{
 	{
@@ -56,12 +56,12 @@ public:
 		THING_IPV6_ADDRESS = 3
 		THING_IPV6_ADDRESS = 3
 	};
 	};
 
 
-	inline CertificateOfOwnership()
+	ZT_ALWAYS_INLINE CertificateOfOwnership()
 	{
 	{
 		memset(reinterpret_cast<void *>(this),0,sizeof(CertificateOfOwnership));
 		memset(reinterpret_cast<void *>(this),0,sizeof(CertificateOfOwnership));
 	}
 	}
 
 
-	inline CertificateOfOwnership(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id)
+	ZT_ALWAYS_INLINE CertificateOfOwnership(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id)
 	{
 	{
 		memset(reinterpret_cast<void *>(this),0,sizeof(CertificateOfOwnership));
 		memset(reinterpret_cast<void *>(this),0,sizeof(CertificateOfOwnership));
 		_networkId = nwid;
 		_networkId = nwid;
@@ -70,19 +70,19 @@ public:
 		_issuedTo = issuedTo;
 		_issuedTo = issuedTo;
 	}
 	}
 
 
-	inline uint64_t networkId() const { return _networkId; }
-	inline int64_t timestamp() const { return _ts; }
-	inline uint32_t id() const { return _id; }
-	inline const Address &issuedTo() const { return _issuedTo; }
-	inline const Address &signer() const { return _signedBy; }
-	inline const uint8_t *signature() const { return _signature; }
-	inline unsigned int signatureLength() const { return _signatureLength; }
+	ZT_ALWAYS_INLINE uint64_t networkId() const { return _networkId; }
+	ZT_ALWAYS_INLINE int64_t timestamp() const { return _ts; }
+	ZT_ALWAYS_INLINE uint32_t id() const { return _id; }
+	ZT_ALWAYS_INLINE const Address &issuedTo() const { return _issuedTo; }
+	ZT_ALWAYS_INLINE const Address &signer() const { return _signedBy; }
+	ZT_ALWAYS_INLINE const uint8_t *signature() const { return _signature; }
+	ZT_ALWAYS_INLINE unsigned int signatureLength() const { return _signatureLength; }
 
 
-	inline unsigned int thingCount() const { return (unsigned int)_thingCount; }
-	inline Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; }
-	inline const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; }
+	ZT_ALWAYS_INLINE unsigned int thingCount() const { return (unsigned int)_thingCount; }
+	ZT_ALWAYS_INLINE Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; }
+	ZT_ALWAYS_INLINE const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; }
 
 
-	inline bool owns(const InetAddress &ip) const
+	ZT_ALWAYS_INLINE bool owns(const InetAddress &ip) const
 	{
 	{
 		if (ip.ss_family == AF_INET)
 		if (ip.ss_family == AF_INET)
 			return this->_owns(THING_IPV4_ADDRESS,&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
 			return this->_owns(THING_IPV4_ADDRESS,&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
@@ -91,14 +91,14 @@ public:
 		return false;
 		return false;
 	}
 	}
 
 
-	inline bool owns(const MAC &mac) const
+	ZT_ALWAYS_INLINE bool owns(const MAC &mac) const
 	{
 	{
 		uint8_t tmp[6];
 		uint8_t tmp[6];
 		mac.copyTo(tmp,6);
 		mac.copyTo(tmp,6);
 		return this->_owns(THING_MAC_ADDRESS,tmp,6);
 		return this->_owns(THING_MAC_ADDRESS,tmp,6);
 	}
 	}
 
 
-	inline void addThing(const InetAddress &ip)
+	ZT_ALWAYS_INLINE void addThing(const InetAddress &ip)
 	{
 	{
 		if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
 		if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
 		if (ip.ss_family == AF_INET) {
 		if (ip.ss_family == AF_INET) {
@@ -112,7 +112,7 @@ public:
 		}
 		}
 	}
 	}
 
 
-	inline void addThing(const MAC &mac)
+	ZT_ALWAYS_INLINE void addThing(const MAC &mac)
 	{
 	{
 		if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
 		if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
 		_thingTypes[_thingCount] = THING_MAC_ADDRESS;
 		_thingTypes[_thingCount] = THING_MAC_ADDRESS;
@@ -124,7 +124,7 @@ public:
 	 * @param signer Signing identity, must have private key
 	 * @param signer Signing identity, must have private key
 	 * @return True if signature was successful
 	 * @return True if signature was successful
 	 */
 	 */
-	inline bool sign(const Identity &signer)
+	ZT_ALWAYS_INLINE bool sign(const Identity &signer)
 	{
 	{
 		if (signer.hasPrivate()) {
 		if (signer.hasPrivate()) {
 			Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
 			Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
@@ -136,7 +136,7 @@ public:
 		return false;
 		return false;
 	}
 	}
 
 
-	inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
+	ZT_ALWAYS_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
 
 
 	template<unsigned int C>
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const
@@ -206,13 +206,13 @@ public:
 	}
 	}
 
 
 	// Provides natural sort order by ID
 	// Provides natural sort order by ID
-	inline bool operator<(const CertificateOfOwnership &coo) const { return (_id < coo._id); }
+	ZT_ALWAYS_INLINE bool operator<(const CertificateOfOwnership &coo) const { return (_id < coo._id); }
 
 
-	inline bool operator==(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) == 0); }
-	inline bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); }
+	ZT_ALWAYS_INLINE bool operator==(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) == 0); }
+	ZT_ALWAYS_INLINE bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); }
 
 
 private:
 private:
-	inline bool _owns(const Thing &t,const void *v,unsigned int l) const
+	ZT_ALWAYS_INLINE bool _owns(const Thing &t,const void *v,unsigned int l) const
 	{
 	{
 		for(unsigned int i=0,j=_thingCount;i<j;++i) {
 		for(unsigned int i=0,j=_thingCount;i<j;++i) {
 			if (_thingTypes[i] == (uint8_t)t) {
 			if (_thingTypes[i] == (uint8_t)t) {

+ 10 - 10
node/Constants.hpp

@@ -118,18 +118,18 @@
 
 
 /**
 /**
  * Period between keepalives sent to paths if no other traffic has been sent
  * Period between keepalives sent to paths if no other traffic has been sent
- *
- * See https://conferences.sigcomm.org/imc/2010/papers/p260.pdf for
- * some real world data on NAT UDP timeouts. From the paper: "the
- * lowest measured timeout when a binding has seen bidirectional
- * traffic is 54 sec." 30 seconds is faster than really necessary.
  */
  */
-#define ZT_PATH_KEEPALIVE_PERIOD 30000
+#define ZT_PATH_KEEPALIVE_PERIOD 20000
+
+/**
+ * Timeout for path alive-ness (measured from last receive)
+ */
+#define ZT_PATH_ALIVE_TIMEOUT ((ZT_PATH_KEEPALIVE_PERIOD * 2) + 5000)
 
 
 /**
 /**
- * Timeout for path aliveness (measured from last receive)
+ * Timeout for path active-ness (measured from last receive)
  */
  */
-#define ZT_PATH_ACTIVITY_TIMEOUT ((ZT_PATH_KEEPALIVE_PERIOD * 2) + 5000)
+#define ZT_PATH_ACTIVITY_TIMEOUT (ZT_PATH_KEEPALIVE_PERIOD + 5000)
 
 
 /**
 /**
  * Delay between full HELLO messages between peers
  * Delay between full HELLO messages between peers
@@ -137,12 +137,12 @@
 #define ZT_PEER_PING_PERIOD 60000
 #define ZT_PEER_PING_PERIOD 60000
 
 
 /**
 /**
- * Timeout for overall peer activity (measured from last receive)
+ * Timeout for peer alive-ness (measured from last receive)
  */
  */
 #define ZT_PEER_ALIVE_TIMEOUT ((ZT_PEER_PING_PERIOD * 2) + 5000)
 #define ZT_PEER_ALIVE_TIMEOUT ((ZT_PEER_PING_PERIOD * 2) + 5000)
 
 
 /**
 /**
- * Timeout for overall peer activity (measured from last receive)
+ * Timeout for peer active-ness (measured from last receive)
  */
  */
 #define ZT_PEER_ACTIVITY_TIMEOUT (ZT_PEER_PING_PERIOD + 5000)
 #define ZT_PEER_ACTIVITY_TIMEOUT (ZT_PEER_PING_PERIOD + 5000)
 
 

+ 0 - 13
node/Credential.hpp

@@ -40,19 +40,6 @@ class RuntimeEnvironment;
 class Credential
 class Credential
 {
 {
 public:
 public:
-	/**
-	 * Do not change type code IDs -- these are used in Revocation objects and elsewhere
-	 */
-	enum Type
-	{
-		CREDENTIAL_TYPE_NULL = 0,
-		CREDENTIAL_TYPE_COM = 1,        // CertificateOfMembership
-		CREDENTIAL_TYPE_CAPABILITY = 2,
-		CREDENTIAL_TYPE_TAG = 3,
-		CREDENTIAL_TYPE_COO = 4,        // CertificateOfOwnership
-		CREDENTIAL_TYPE_REVOCATION = 6
-	};
-
 	/**
 	/**
 	 * Result of verify() operations
 	 * Result of verify() operations
 	 */
 	 */

+ 14 - 9
node/Endpoint.cpp

@@ -19,12 +19,13 @@ bool Endpoint::operator==(const Endpoint &ep) const
 {
 {
 	if (_t == ep._t) {
 	if (_t == ep._t) {
 		switch(_t) {
 		switch(_t) {
-			case INETADDR: return (inetAddr() == ep.inetAddr());
-			case DNSNAME:  return ((_v.dns.port == ep._v.dns.port)&&(strcmp(_v.dns.name,ep._v.dns.name) == 0));
-			case ZEROTIER: return ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) == 0));
-			case URL:      return (strcmp(_v.url,ep._v.url) == 0);
-			case ETHERNET: return (_v.eth == ep._v.eth);
-			default:       return true;
+			default:          return true;
+			case INETADDR_V4:
+			case INETADDR_V6: return (inetAddr() == ep.inetAddr());
+			case DNSNAME:     return ((_v.dns.port == ep._v.dns.port)&&(strcmp(_v.dns.name,ep._v.dns.name) == 0));
+			case ZEROTIER:    return ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) == 0));
+			case URL:         return (strcmp(_v.url,ep._v.url) == 0);
+			case ETHERNET:    return (_v.eth == ep._v.eth);
 		}
 		}
 	}
 	}
 	return false;
 	return false;
@@ -37,7 +38,9 @@ bool Endpoint::operator<(const Endpoint &ep) const
 	} else if (_t == ep._t) {
 	} else if (_t == ep._t) {
 		int ncmp;
 		int ncmp;
 		switch(_t) {
 		switch(_t) {
-			case INETADDR: return (inetAddr() < ep.inetAddr());
+			case INETADDR_V4:
+			case INETADDR_V6:
+				return (inetAddr() < ep.inetAddr());
 			case DNSNAME:
 			case DNSNAME:
 				ncmp = strcmp(_v.dns.name,ep._v.dns.name);
 				ncmp = strcmp(_v.dns.name,ep._v.dns.name);
 				return ((ncmp < 0) ? true : (ncmp == 0)&&(_v.dns.port < ep._v.dns.port));
 				return ((ncmp < 0) ? true : (ncmp == 0)&&(_v.dns.port < ep._v.dns.port));
@@ -58,7 +61,8 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const
 	Utils::storeBigEndian(data + 3,(int16_t)_l[1]);
 	Utils::storeBigEndian(data + 3,(int16_t)_l[1]);
 	Utils::storeBigEndian(data + 5,(int16_t)_l[2]);
 	Utils::storeBigEndian(data + 5,(int16_t)_l[2]);
 	switch(_t) {
 	switch(_t) {
-		case INETADDR:
+		case INETADDR_V4:
+		case INETADDR_V6:
 			return 7 + reinterpret_cast<const InetAddress *>(&_v.sa)->marshal(data+1);
 			return 7 + reinterpret_cast<const InetAddress *>(&_v.sa)->marshal(data+1);
 		case DNSNAME:
 		case DNSNAME:
 			p = 7;
 			p = 7;
@@ -116,7 +120,8 @@ int Endpoint::unmarshal(const uint8_t *restrict data,const int len)
   switch(_t) {
   switch(_t) {
 		case NIL:
 		case NIL:
 			return 7;
 			return 7;
-		case INETADDR:
+		case INETADDR_V4:
+		case INETADDR_V6:
 			return 7 + reinterpret_cast<InetAddress *>(&_v.sa)->unmarshal(data+7,len-7);
 			return 7 + reinterpret_cast<InetAddress *>(&_v.sa)->unmarshal(data+7,len-7);
 		case DNSNAME:
 		case DNSNAME:
 			if (len < 10)
 			if (len < 10)

+ 27 - 17
node/Endpoint.hpp

@@ -40,13 +40,14 @@ class Endpoint
 public:
 public:
 	enum Type
 	enum Type
 	{
 	{
-		NIL =      0,      // NIL value
-		INETADDR = 1,      // InetAddress (v4 or v6)
-		DNSNAME =  2,      // DNS name and port that resolves to InetAddress
-		ZEROTIER = 3,      // ZeroTier Address (for relaying and meshy behavior)
-		URL =      4,      // URL for http/https/ws/etc. (not implemented yet)
-		ETHERNET = 5,      // 48-bit LAN-local Ethernet address
-		UNRECOGNIZED = 255 // Unrecognized endpoint type encountered in stream
+		NIL =          0,   // NIL value
+		INETADDR_V4 =  1,   // IPv4
+		INETADDR_V6 =  2,   // IPv6
+		DNSNAME =      3,   // DNS name and port that resolves to InetAddress
+		ZEROTIER =     4,   // ZeroTier Address (for relaying and meshy behavior)
+		URL =          5,   // URL for http/https/ws/etc. (not implemented yet)
+		ETHERNET =     6,   // 48-bit LAN-local Ethernet address
+		UNRECOGNIZED = 255  // Unrecognized endpoint type encountered in stream
 	};
 	};
 
 
 	ZT_ALWAYS_INLINE Endpoint()
 	ZT_ALWAYS_INLINE Endpoint()
@@ -56,13 +57,12 @@ public:
 
 
 	ZT_ALWAYS_INLINE Endpoint(const Endpoint &ep)
 	ZT_ALWAYS_INLINE Endpoint(const Endpoint &ep)
 	{
 	{
-		memcpy(reinterpret_cast<void *>(this),&ep,sizeof(Endpoint));
+		memcpy(reinterpret_cast<void *>(this),reinterpret_cast<const void *>(&ep),sizeof(Endpoint));
 	}
 	}
 
 
-	explicit ZT_ALWAYS_INLINE Endpoint(const InetAddress &sa) :
-		_t(INETADDR)
+	explicit ZT_ALWAYS_INLINE Endpoint(const InetAddress &sa)
 	{
 	{
-		_v.sa = sa;
+		*this = sa;
 	}
 	}
 
 
 	ZT_ALWAYS_INLINE Endpoint(const Address &zt,const uint8_t identityHash[ZT_IDENTITY_HASH_SIZE]) :
 	ZT_ALWAYS_INLINE Endpoint(const Address &zt,const uint8_t identityHash[ZT_IDENTITY_HASH_SIZE]) :
@@ -93,7 +93,17 @@ public:
 
 
 	ZT_ALWAYS_INLINE Endpoint &operator=(const InetAddress &sa)
 	ZT_ALWAYS_INLINE Endpoint &operator=(const InetAddress &sa)
 	{
 	{
-		_t = INETADDR;
+		switch(sa.ss_family) {
+			case AF_INET:
+				_t = INETADDR_V4;
+				break;
+			case AF_INET6:
+				_t = INETADDR_V6;
+				break;
+			default:
+				_t = NIL;
+				return *this;
+		}
 		_v.sa = sa;
 		_v.sa = sa;
 		return *this;
 		return *this;
 	}
 	}
@@ -101,7 +111,7 @@ public:
 	/**
 	/**
 	 * @return InetAddress or NIL if not of this type
 	 * @return InetAddress or NIL if not of this type
 	 */
 	 */
-	ZT_ALWAYS_INLINE const InetAddress &inetAddr() const { return (_t == INETADDR) ? *reinterpret_cast<const InetAddress *>(&_v.sa) : InetAddress::NIL; }
+	ZT_ALWAYS_INLINE const InetAddress &inetAddr() const { return ((_t == INETADDR_V4)||(_t == INETADDR_V6)) ? *reinterpret_cast<const InetAddress *>(&_v.sa) : InetAddress::NIL; }
 
 
 	/**
 	/**
 	 * @return DNS name or empty string if not of this type
 	 * @return DNS name or empty string if not of this type
@@ -156,14 +166,14 @@ private:
 	int _l[3]; // X,Y,Z location in kilometers from the nearest gravitational center of mass
 	int _l[3]; // X,Y,Z location in kilometers from the nearest gravitational center of mass
 	union {
 	union {
 		struct sockaddr_storage sa;
 		struct sockaddr_storage sa;
-		ZT_PACKED_STRUCT(struct {
+		struct {
 			uint16_t port;
 			uint16_t port;
 			char name[ZT_ENDPOINT_MAX_NAME_SIZE];
 			char name[ZT_ENDPOINT_MAX_NAME_SIZE];
-		}) dns;
-		ZT_PACKED_STRUCT(struct {
+		} dns;
+		struct {
 			uint64_t a;
 			uint64_t a;
 			uint8_t idh[ZT_IDENTITY_HASH_SIZE];
 			uint8_t idh[ZT_IDENTITY_HASH_SIZE];
-		}) zt;
+		} zt;
 		char url[ZT_ENDPOINT_MAX_NAME_SIZE];
 		char url[ZT_ENDPOINT_MAX_NAME_SIZE];
 		uint64_t eth;
 		uint64_t eth;
 	} _v;
 	} _v;

+ 100 - 16
node/Identity.cpp

@@ -89,6 +89,7 @@ void Identity::generate(const Type t)
 
 
 	_type = t;
 	_type = t;
 	_hasPrivate = true;
 	_hasPrivate = true;
+	_hash[0] = 0; // force hash recompute
 
 
 	char *const genmem = new char[ZT_IDENTITY_GEN_MEMORY];
 	char *const genmem = new char[ZT_IDENTITY_GEN_MEMORY];
 	do {
 	do {
@@ -142,24 +143,13 @@ bool Identity::locallyValidate() const
 	return false;
 	return false;
 }
 }
 
 
-bool Identity::hash(uint8_t h[48],const bool includePrivate) const
+void Identity::hashWithPrivate(uint8_t h[48]) const
 {
 {
 	switch(_type) {
 	switch(_type) {
-
-	case C25519:
-		if ((_hasPrivate)&&(includePrivate))
-			SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
-		else SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-		return true;
-
-	case P384:
-		if ((_hasPrivate)&&(includePrivate))
-			SHA384(h,&_pub,sizeof(_pub),&_priv,sizeof(_priv));
-		else SHA384(h,&_pub,sizeof(_pub));
-		return true;
-
+		case C25519: SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); break;
+		case P384: SHA384(h,&_pub,sizeof(_pub),&_priv,sizeof(_priv)); break;
+		default: memset(h,0,48);
 	}
 	}
-	return false;
 }
 }
 
 
 unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
 unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
@@ -293,6 +283,7 @@ char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_
 bool Identity::fromString(const char *str)
 bool Identity::fromString(const char *str)
 {
 {
 	_hasPrivate = false;
 	_hasPrivate = false;
+	_hash[0] = 0; // force hash recompute
 
 
 	if (!str) {
 	if (!str) {
 		_address.zero();
 		_address.zero();
@@ -386,6 +377,97 @@ bool Identity::fromString(const char *str)
 	return true;
 	return true;
 }
 }
 
 
+int Identity::marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],const bool includePrivate) const
+{
+	_address.copyTo(data,ZT_ADDRESS_LENGTH);
+	switch(_type) {
+
+		case C25519:
+			data[ZT_ADDRESS_LENGTH] = (uint8_t)C25519;
+			memcpy(data + ZT_ADDRESS_LENGTH + 1,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+			if ((includePrivate)&&(_hasPrivate)) {
+				data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = ZT_C25519_PRIVATE_KEY_LEN;
+				memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
+				return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN);
+			}
+			data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = 0;
+			return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1);
+
+		case P384:
+			data[ZT_ADDRESS_LENGTH] = (uint8_t)P384;
+			memcpy(data + ZT_ADDRESS_LENGTH + 1,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
+			if ((includePrivate)&&(_hasPrivate)) {
+				data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE;
+				memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,&_priv,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
+				data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE] = 0;
+				return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1);
+			}
+			data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0;
+			data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1] = 0;
+			return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2);
+
+	}
+	return -1;
+}
+
+int Identity::unmarshal(const uint8_t *data,const int len)
+{
+	if (len < (ZT_ADDRESS_LENGTH + 1))
+		return -1;
+	_hash[0] = 0; // force hash recompute
+	unsigned int privlen;
+	switch((_type = (Type)data[ZT_ADDRESS_LENGTH])) {
+
+		case C25519:
+			if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1))
+				return -1;
+			memcpy(_pub.c25519,data + ZT_ADDRESS_LENGTH + 1,ZT_C25519_PUBLIC_KEY_LEN);
+			privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN];
+			if (privlen == ZT_C25519_PRIVATE_KEY_LEN) {
+				if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN))
+					return -1;
+				_hasPrivate = true;
+				memcpy(_priv.c25519,data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,ZT_C25519_PRIVATE_KEY_LEN);
+				return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN);
+			} else if (privlen == 0) {
+				_hasPrivate = false;
+				return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1);
+			}
+			break;
+
+		case P384:
+			if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2))
+				return -1;
+			memcpy(&_pub,data + ZT_ADDRESS_LENGTH + 1,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
+			privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE];
+			if (privlen == ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) {
+				if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1))
+					return -1;
+				_hasPrivate = true;
+				memcpy(&_priv,data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
+				privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE];
+				if (len < (int)(privlen + (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1)))
+					return -1;
+				return (int)(privlen + (unsigned int)(ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1));
+			} else if (privlen == 0) {
+				_hasPrivate = false;
+				return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2);
+			}
+			break;
+
+	}
+	return -1;
+}
+
+void Identity::_computeHash()
+{
+	switch(_type) {
+		case C25519: SHA384(_hash,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); break;
+		case P384: SHA384(_hash,&_pub,sizeof(_pub)); break;
+		default: memset(_hash,0,48);
+	}
+}
+
 } // namespace ZeroTier
 } // namespace ZeroTier
 
 
 extern "C" {
 extern "C" {
@@ -473,7 +555,9 @@ uint64_t ZT_Identity_address(const ZT_Identity *id)
 
 
 void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int includePrivate)
 void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int includePrivate)
 {
 {
-	reinterpret_cast<const ZeroTier::Identity *>(id)->hash(h,includePrivate != 0);
+	if (includePrivate)
+		reinterpret_cast<const ZeroTier::Identity *>(id)->hashWithPrivate(h);
+	else memcpy(h,reinterpret_cast<const ZeroTier::Identity *>(id)->hash(),48);
 }
 }
 
 
 ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id)
 ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id)

+ 18 - 85
node/Identity.hpp

@@ -101,12 +101,21 @@ public:
 	ZT_ALWAYS_INLINE bool hasPrivate() const { return _hasPrivate; }
 	ZT_ALWAYS_INLINE bool hasPrivate() const { return _hasPrivate; }
 
 
 	/**
 	/**
-	 * This generates a SHA384 hash of this identity's keys.
+	 * @return 384-bit/48-byte hash of this identity's public key(s)
+	 */
+	ZT_ALWAYS_INLINE const uint8_t *hash() const
+	{
+		if (_hash[0] == 0)
+			const_cast<Identity *>(this)->_computeHash();
+		return reinterpret_cast<const uint8_t *>(_hash);
+	}
+
+	/**
+	 * Compute a hash of this identity's public and private keys
 	 *
 	 *
-	 * @param h Buffer to receive SHA384 of public key(s)
-	 * @param includePrivate If true, hash private key(s) as well
+	 * @param h Buffer to store SHA384 hash
 	 */
 	 */
-	bool hash(uint8_t h[48],bool includePrivate = false) const;
+	void hashWithPrivate(uint8_t h[48]) const;
 
 
 	/**
 	/**
 	 * Sign a message with this identity (private key required)
 	 * Sign a message with this identity (private key required)
@@ -311,91 +320,15 @@ public:
 
 
 	ZT_ALWAYS_INLINE unsigned long hashCode() const { return ((unsigned long)_address.toInt() + (unsigned long)_pub.c25519[0] + (unsigned long)_pub.c25519[1] + (unsigned long)_pub.c25519[2]); }
 	ZT_ALWAYS_INLINE unsigned long hashCode() const { return ((unsigned long)_address.toInt() + (unsigned long)_pub.c25519[0] + (unsigned long)_pub.c25519[1] + (unsigned long)_pub.c25519[2]); }
 
 
-	// Marshal interface ///////////////////////////////////////////////////////
 	static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_IDENTITY_MARSHAL_SIZE_MAX; }
 	static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_IDENTITY_MARSHAL_SIZE_MAX; }
-	inline int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],const bool includePrivate = false) const
-	{
-		_address.copyTo(data,ZT_ADDRESS_LENGTH);
-		switch(_type) {
-
-			case C25519:
-				data[ZT_ADDRESS_LENGTH] = (uint8_t)C25519;
-				memcpy(data + ZT_ADDRESS_LENGTH + 1,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-				if ((includePrivate)&&(_hasPrivate)) {
-					data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = ZT_C25519_PRIVATE_KEY_LEN;
-					memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
-					return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN);
-				}
-				data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = 0;
-				return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1);
-
-			case P384:
-				data[ZT_ADDRESS_LENGTH] = (uint8_t)P384;
-				memcpy(data + ZT_ADDRESS_LENGTH + 1,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
-				if ((includePrivate)&&(_hasPrivate)) {
-					data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE;
-					memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,&_priv,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
-					data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE] = 0;
-					return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1);
-				}
-				data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0;
-				data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1] = 0;
-				return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2);
-
-		}
-		return -1;
-	}
-	inline int unmarshal(const uint8_t *restrict data,const int len)
-	{
-		if (len < (ZT_ADDRESS_LENGTH + 1))
-			return -1;
-		unsigned int privlen;
-		switch((_type = (Type)data[ZT_ADDRESS_LENGTH])) {
-
-			case C25519:
-				if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1))
-					return -1;
-				memcpy(_pub.c25519,data + ZT_ADDRESS_LENGTH + 1,ZT_C25519_PUBLIC_KEY_LEN);
-				privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN];
-				if (privlen == ZT_C25519_PRIVATE_KEY_LEN) {
-					if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN))
-						return -1;
-					_hasPrivate = true;
-					memcpy(_priv.c25519,data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,ZT_C25519_PRIVATE_KEY_LEN);
-					return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN);
-				} else if (privlen == 0) {
-					_hasPrivate = false;
-					return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1);
-				}
-				break;
-
-			case P384:
-				if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2))
-					return -1;
-				memcpy(&_pub,data + ZT_ADDRESS_LENGTH + 1,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
-				privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE];
-				if (privlen == ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) {
-					if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1))
-						return -1;
-					_hasPrivate = true;
-					memcpy(&_priv,data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
-					privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE];
-					if (len < (int)(privlen + (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1)))
-						return -1;
-					return (int)(privlen + (unsigned int)(ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1));
-				} else if (privlen == 0) {
-					_hasPrivate = false;
-					return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2);
-				}
-				break;
-
-		}
-		return -1;
-	}
-	////////////////////////////////////////////////////////////////////////////
+	int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],bool includePrivate = false) const;
+	int unmarshal(const uint8_t *data,int len);
 
 
 private:
 private:
+	void _computeHash(); // recompute _hash
+
 	Address _address;
 	Address _address;
+	uint64_t _hash[6]; // hash of public key memo-ized for performance, recalculated when _hash[0] == 0
 	Type _type; // _type determines which fields in _priv and _pub are used
 	Type _type; // _type determines which fields in _priv and _pub are used
 	bool _hasPrivate;
 	bool _hasPrivate;
 	ZT_PACKED_STRUCT(struct { // don't re-order these
 	ZT_PACKED_STRUCT(struct { // don't re-order these

+ 64 - 69
node/IncomingPacket.cpp

@@ -35,9 +35,6 @@
 namespace ZeroTier {
 namespace ZeroTier {
 
 
 namespace {
 namespace {
-//////////////////////////////////////////////////////////////////////////////
-// Implementation of each protocol verb                                     //
-//////////////////////////////////////////////////////////////////////////////
 
 
 void _sendErrorNeedCredentials(IncomingPacket &pkt,const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid,const SharedPtr<Path> &path)
 void _sendErrorNeedCredentials(IncomingPacket &pkt,const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid,const SharedPtr<Path> &path)
 {
 {
@@ -65,11 +62,11 @@ ZT_ALWAYS_INLINE bool _doHELLO(IncomingPacket &pkt,const RuntimeEnvironment *con
 	unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(pkt,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
 	unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(pkt,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
 
 
 	if (protoVersion < ZT_PROTO_VERSION_MIN) {
 	if (protoVersion < ZT_PROTO_VERSION_MIN) {
-		RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"protocol version too old");
+		RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_PEER_TOO_OLD);
 		return true;
 		return true;
 	}
 	}
 	if (fromAddress != id.address()) {
 	if (fromAddress != id.address()) {
-		RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"identity/address mismatch");
+		RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
 		return true;
 		return true;
 	}
 	}
 
 
@@ -87,18 +84,13 @@ ZT_ALWAYS_INLINE bool _doHELLO(IncomingPacket &pkt,const RuntimeEnvironment *con
 				uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
 				uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
 				if (RR->identity.agree(id,key)) {
 				if (RR->identity.agree(id,key)) {
 					if (pkt.dearmor(key)) { // ensure packet is authentic, otherwise drop
 					if (pkt.dearmor(key)) { // ensure packet is authentic, otherwise drop
-						RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"address collision");
-						Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
-						outp.append((uint8_t)Packet::VERB_HELLO);
-						outp.append((uint64_t)pid);
-						outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
-						outp.armor(key,true);
-						path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+						RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
+						// TODO: we handle identity collisions differently now
 					} else {
 					} else {
-						RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid MAC");
+						RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
 					}
 					}
 				} else {
 				} else {
-					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid identity");
+					RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
 				}
 				}
 
 
 				return true;
 				return true;
@@ -106,7 +98,7 @@ ZT_ALWAYS_INLINE bool _doHELLO(IncomingPacket &pkt,const RuntimeEnvironment *con
 				// Identity is the same as the one we already have -- check packet integrity
 				// Identity is the same as the one we already have -- check packet integrity
 
 
 				if (!pkt.dearmor(peer->key())) {
 				if (!pkt.dearmor(peer->key())) {
-					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid MAC");
+					RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
 					return true;
 					return true;
 				}
 				}
 
 
@@ -118,30 +110,30 @@ ZT_ALWAYS_INLINE bool _doHELLO(IncomingPacket &pkt,const RuntimeEnvironment *con
 
 
 		// Sanity check: this basically can't happen
 		// Sanity check: this basically can't happen
 		if (alreadyAuthenticated) {
 		if (alreadyAuthenticated) {
-			RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"illegal alreadyAuthenticated state");
+			RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_UNSPECIFIED);
 			return true;
 			return true;
 		}
 		}
 
 
 		// Check rate limits
 		// Check rate limits
 		if (!RR->node->rateGateIdentityVerification(now,path->address())) {
 		if (!RR->node->rateGateIdentityVerification(now,path->address())) {
-			RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"rate limit exceeded");
+			RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_RATE_LIMIT_EXCEEDED);
 			return true;
 			return true;
 		}
 		}
 
 
 		// Check packet integrity and MAC (this is faster than locallyValidate() so do it first to filter out total crap)
 		// Check packet integrity and MAC (this is faster than locallyValidate() so do it first to filter out total crap)
 		SharedPtr<Peer> newPeer(new Peer(RR));
 		SharedPtr<Peer> newPeer(new Peer(RR));
 		if (!newPeer->init(RR->identity,id)) {
 		if (!newPeer->init(RR->identity,id)) {
-			RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"error initializing peer");
+			RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_UNSPECIFIED);
 			return true;
 			return true;
 		}
 		}
 		if (!pkt.dearmor(newPeer->key())) {
 		if (!pkt.dearmor(newPeer->key())) {
-			RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid MAC");
+			RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
 			return true;
 			return true;
 		}
 		}
 
 
 		// Check that identity's address is valid as per the derivation function
 		// Check that identity's address is valid as per the derivation function
 		if (!id.locallyValidate()) {
 		if (!id.locallyValidate()) {
-			RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"invalid identity");
+			RR->t->incomingPacketDropped(tPtr,pid,0,id,path->address(),pkt.hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 			return true;
 			return true;
 		}
 		}
 
 
@@ -158,7 +150,7 @@ ZT_ALWAYS_INLINE bool _doHELLO(IncomingPacket &pkt,const RuntimeEnvironment *con
 		if (ptr < pkt.size()) {
 		if (ptr < pkt.size()) {
 			ptr += externalSurfaceAddress.deserialize(pkt,ptr);
 			ptr += externalSurfaceAddress.deserialize(pkt,ptr);
 			if ((externalSurfaceAddress)&&(pkt.hops() == 0))
 			if ((externalSurfaceAddress)&&(pkt.hops() == 0))
-				RR->sa->iam(tPtr,id.address(),path->localSocket(),path->address(),externalSurfaceAddress,RR->topology->isRoot(id),now);
+				RR->sa->iam(tPtr,id,path->localSocket(),path->address(),externalSurfaceAddress,RR->topology->isRoot(id),now);
 		}
 		}
 	}
 	}
 
 
@@ -270,7 +262,7 @@ ZT_ALWAYS_INLINE bool _doOK(IncomingPacket &pkt,const RuntimeEnvironment *const
 					InetAddress externalSurfaceAddress;
 					InetAddress externalSurfaceAddress;
 					externalSurfaceAddress.deserialize(pkt,ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2);
 					externalSurfaceAddress.deserialize(pkt,ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2);
 					if (externalSurfaceAddress)
 					if (externalSurfaceAddress)
-						RR->sa->iam(tPtr,peer->address(),path->localSocket(),path->address(),externalSurfaceAddress,RR->topology->isRoot(peer->identity()),RR->node->now());
+						RR->sa->iam(tPtr,peer->identity(),path->localSocket(),path->address(),externalSurfaceAddress,RR->topology->isRoot(peer->identity()),RR->node->now());
 				}
 				}
 			}
 			}
 
 
@@ -388,6 +380,7 @@ ZT_ALWAYS_INLINE bool _doFRAME(IncomingPacket &pkt,const RuntimeEnvironment *con
 					RR->node->putFrame(tPtr,nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
 					RR->node->putFrame(tPtr,nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
 			}
 			}
 		} else {
 		} else {
+			RR->t->incomingNetworkFrameDropped(tPtr,nwid,MAC(),MAC(),peer->identity(),path->address(),pkt.hops(),0,nullptr,Packet::VERB_FRAME,true,ZT_TRACE_FRAME_DROP_REASON_PERMISSION_DENIED);
 			_sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path);
 			_sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path);
 			return false;
 			return false;
 		}
 		}
@@ -412,7 +405,7 @@ ZT_ALWAYS_INLINE bool _doEXT_FRAME(IncomingPacket &pkt,const RuntimeEnvironment
 		}
 		}
 
 
 		if (!network->gate(tPtr,peer)) {
 		if (!network->gate(tPtr,peer)) {
-			RR->t->incomingNetworkAccessDenied(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,true);
+			RR->t->incomingNetworkFrameDropped(tPtr,nwid,MAC(),MAC(),peer->identity(),path->address(),pkt.hops(),0,nullptr,Packet::VERB_EXT_FRAME,true,ZT_TRACE_FRAME_DROP_REASON_PERMISSION_DENIED);
 			_sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path);
 			_sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path);
 			return false;
 			return false;
 		}
 		}
@@ -435,19 +428,19 @@ ZT_ALWAYS_INLINE bool _doEXT_FRAME(IncomingPacket &pkt,const RuntimeEnvironment
 						if (network->config().permitsBridging(peer->address())) {
 						if (network->config().permitsBridging(peer->address())) {
 							network->learnBridgeRoute(from,peer->address());
 							network->learnBridgeRoute(from,peer->address());
 						} else {
 						} else {
-							RR->t->incomingNetworkFrameDropped(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (remote)");
+							RR->t->incomingNetworkFrameDropped(tPtr,nwid,from,to,peer->identity(),path->address(),pkt.hops(),(uint16_t)frameLen,frameData,Packet::VERB_EXT_FRAME,true,ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_REMOTE);
 							peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 							peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 							return true;
 							return true;
 						}
 						}
 					} else if (to != network->mac()) {
 					} else if (to != network->mac()) {
 						if (to.isMulticast()) {
 						if (to.isMulticast()) {
 							if (network->config().multicastLimit == 0) {
 							if (network->config().multicastLimit == 0) {
-								RR->t->incomingNetworkFrameDropped(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"multicast disabled");
+								RR->t->incomingNetworkFrameDropped(tPtr,nwid,from,to,peer->identity(),path->address(),pkt.hops(),(uint16_t)frameLen,frameData,Packet::VERB_EXT_FRAME,true,ZT_TRACE_FRAME_DROP_REASON_MULTICAST_DISABLED);
 								peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 								peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 								return true;
 								return true;
 							}
 							}
 						} else if (!network->config().permitsBridging(RR->identity.address())) {
 						} else if (!network->config().permitsBridging(RR->identity.address())) {
-							RR->t->incomingNetworkFrameDropped(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (local)");
+							RR->t->incomingNetworkFrameDropped(tPtr,nwid,from,to,peer->identity(),path->address(),pkt.hops(),(uint16_t)frameLen,frameData,Packet::VERB_EXT_FRAME,true,ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_LOCAL);
 							peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 							peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 							return true;
 							return true;
 						}
 						}
@@ -733,6 +726,7 @@ ZT_ALWAYS_INLINE bool _doUSER_MESSAGE(IncomingPacket &pkt,const RuntimeEnvironme
 bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
 bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
 {
 {
 	const Address sourceAddress(source());
 	const Address sourceAddress(source());
+	const SharedPtr<Peer> peer(RR->topology->get(tPtr,sourceAddress));
 
 
 	try {
 	try {
 		// Check for trusted paths or unencrypted HELLOs (HELLO is the only packet sent in the clear)
 		// Check for trusted paths or unencrypted HELLOs (HELLO is the only packet sent in the clear)
@@ -746,7 +740,8 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
 			if (RR->topology->shouldInboundPathBeTrusted(_path->address(),tpid)) {
 			if (RR->topology->shouldInboundPathBeTrusted(_path->address(),tpid)) {
 				trusted = true;
 				trusted = true;
 			} else {
 			} else {
-				RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"path not trusted");
+				if (peer)
+					RR->t->incomingPacketDropped(tPtr,packetId(),0,peer->identity(),_path->address(),hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
 				return true;
 				return true;
 			}
 			}
 		} else if ((c == ZT_PROTO_CIPHER_SUITE__POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
 		} else if ((c == ZT_PROTO_CIPHER_SUITE__POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
@@ -754,54 +749,54 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
 			return _doHELLO(*this,RR,tPtr,false,_path);
 			return _doHELLO(*this,RR,tPtr,false,_path);
 		}
 		}
 
 
-		const SharedPtr<Peer> peer(RR->topology->get(tPtr,sourceAddress));
-		if (peer) {
-			if (!trusted) {
-				if (!dearmor(peer->key())) {
-					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"invalid MAC");
-					return true;
-				}
-			}
+		if (!peer) {
+			RR->sw->requestWhois(tPtr,RR->node->now(),sourceAddress);
+			return false;
+		}
 
 
-			if (!uncompress()) {
-				RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),Packet::VERB_NOP,"LZ4 decompression failed");
+		if (!trusted) {
+			if (!dearmor(peer->key())) {
+				RR->t->incomingPacketDropped(tPtr,packetId(),0,peer->identity(),_path->address(),hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
 				return true;
 				return true;
 			}
 			}
+		}
 
 
-			const Packet::Verb v = verb();
-			bool r = true;
-			switch(v) {
-				//case Packet::VERB_NOP:
-				default: // ignore unknown verbs, but if they pass auth check they are "received"
-					peer->received(tPtr,_path,hops(),packetId(),payloadLength(),v,0,Packet::VERB_NOP,0);
-					break;
-				case Packet::VERB_HELLO:                      r = _doHELLO(*this,RR,tPtr,true,_path);                  break;
-				case Packet::VERB_ERROR:                      r = _doERROR(*this,RR,tPtr,peer,_path);                  break;
-				case Packet::VERB_OK:                         r = _doOK(*this,RR,tPtr,peer,_path);                     break;
-				case Packet::VERB_WHOIS:                      r = _doWHOIS(*this,RR,tPtr,peer,_path);                  break;
-				case Packet::VERB_RENDEZVOUS:                 r = _doRENDEZVOUS(*this,RR,tPtr,peer,_path);             break;
-				case Packet::VERB_FRAME:                      r = _doFRAME(*this,RR,tPtr,peer,_path);                  break;
-				case Packet::VERB_EXT_FRAME:                  r = _doEXT_FRAME(*this,RR,tPtr,peer,_path);              break;
-				case Packet::VERB_ECHO:                       r = _doECHO(*this,RR,tPtr,peer,_path);                   break;
-				case Packet::VERB_NETWORK_CREDENTIALS:        r = _doNETWORK_CREDENTIALS(*this,RR,tPtr,peer,_path);    break;
-				case Packet::VERB_NETWORK_CONFIG_REQUEST:     r = _doNETWORK_CONFIG_REQUEST(*this,RR,tPtr,peer,_path); break;
-				case Packet::VERB_NETWORK_CONFIG:             r = _doNETWORK_CONFIG(*this,RR,tPtr,peer,_path);         break;
-				case Packet::VERB_MULTICAST_GATHER:           r = _doMULTICAST_GATHER(*this,RR,tPtr,peer,_path);       break;
-				case Packet::VERB_PUSH_DIRECT_PATHS:          r = _doPUSH_DIRECT_PATHS(*this,RR,tPtr,peer,_path);      break;
-				case Packet::VERB_USER_MESSAGE:               r = _doUSER_MESSAGE(*this,RR,tPtr,peer,_path);           break;
-			}
-			return r;
-		} else {
-			RR->sw->requestWhois(tPtr,RR->node->now(),sourceAddress);
-			return false;
+		if (!uncompress()) {
+			RR->t->incomingPacketDropped(tPtr,packetId(),0,peer->identity(),_path->address(),hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_COMPRESSED_DATA);
+			return true;
+		}
+
+		const Packet::Verb v = verb();
+		bool r = true;
+		switch(v) {
+			default: // ignore unknown verbs, but if they pass auth check they are "received" and considered NOPs by peer->receive()
+				RR->t->incomingPacketDropped(tPtr,packetId(),0,peer->identity(),_path->address(),hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB);
+				// fall through
+			case Packet::VERB_NOP:
+				peer->received(tPtr,_path,hops(),packetId(),payloadLength(),v,0,Packet::VERB_NOP,0);
+				break;
+			case Packet::VERB_HELLO:                      r = _doHELLO(*this,RR,tPtr,true,_path);                  break;
+			case Packet::VERB_ERROR:                      r = _doERROR(*this,RR,tPtr,peer,_path);                  break;
+			case Packet::VERB_OK:                         r = _doOK(*this,RR,tPtr,peer,_path);                     break;
+			case Packet::VERB_WHOIS:                      r = _doWHOIS(*this,RR,tPtr,peer,_path);                  break;
+			case Packet::VERB_RENDEZVOUS:                 r = _doRENDEZVOUS(*this,RR,tPtr,peer,_path);             break;
+			case Packet::VERB_FRAME:                      r = _doFRAME(*this,RR,tPtr,peer,_path);                  break;
+			case Packet::VERB_EXT_FRAME:                  r = _doEXT_FRAME(*this,RR,tPtr,peer,_path);              break;
+			case Packet::VERB_ECHO:                       r = _doECHO(*this,RR,tPtr,peer,_path);                   break;
+			case Packet::VERB_NETWORK_CREDENTIALS:        r = _doNETWORK_CREDENTIALS(*this,RR,tPtr,peer,_path);    break;
+			case Packet::VERB_NETWORK_CONFIG_REQUEST:     r = _doNETWORK_CONFIG_REQUEST(*this,RR,tPtr,peer,_path); break;
+			case Packet::VERB_NETWORK_CONFIG:             r = _doNETWORK_CONFIG(*this,RR,tPtr,peer,_path);         break;
+			case Packet::VERB_MULTICAST_GATHER:           r = _doMULTICAST_GATHER(*this,RR,tPtr,peer,_path);       break;
+			case Packet::VERB_PUSH_DIRECT_PATHS:          r = _doPUSH_DIRECT_PATHS(*this,RR,tPtr,peer,_path);      break;
+			case Packet::VERB_USER_MESSAGE:               r = _doUSER_MESSAGE(*this,RR,tPtr,peer,_path);           break;
 		}
 		}
+		return r;
 	} catch (int ztExcCode) {
 	} catch (int ztExcCode) {
-		RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),verb(),"unexpected exception in tryDecode()");
-		return true;
-	} catch ( ... ) {
-		RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),verb(),"unexpected exception in tryDecode()");
-		return true;
-	}
+	} catch ( ... ) {}
+
+	if (peer)
+		RR->t->incomingPacketDropped(tPtr,packetId(),0,peer->identity(),_path->address(),hops(),Packet::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_UNSPECIFIED);
+	return true;
 }
 }
 
 
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 31 - 0
node/InetAddress.hpp

@@ -419,6 +419,37 @@ public:
 		}
 		}
 	}
 	}
 
 
+	/**
+	 * Fill out a ZT_TraceEventPathAddress from this InetAddress
+	 *
+	 * @param ta ZT_TraceEventPathAddress to fill
+	 */
+	ZT_ALWAYS_INLINE void forTrace(ZT_TraceEventPathAddress &ta) const
+	{
+		uint32_t tmp;
+		switch(ss_family) {
+			default:
+				memset(&ta,0,sizeof(ZT_TraceEventPathAddress));
+				break;
+			case AF_INET:
+				ta.type = ZT_TRACE_EVENT_PATH_TYPE_INETADDR_V4;
+				tmp = (uint32_t)(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
+				ta.address[0] = reinterpret_cast<const uint8_t *>(&tmp)[0];
+				ta.address[1] = reinterpret_cast<const uint8_t *>(&tmp)[1];
+				ta.address[2] = reinterpret_cast<const uint8_t *>(&tmp)[2];
+				ta.address[3] = reinterpret_cast<const uint8_t *>(&tmp)[3];
+				memset(ta.address + 4,0,sizeof(ta.address) - 4);
+				ta.port = reinterpret_cast<const struct sockaddr_in *>(this)->sin_port;
+				break;
+			case AF_INET6:
+				ta.type = ZT_TRACE_EVENT_PATH_TYPE_INETADDR_V6;
+				memcpy(ta.address,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
+				memset(ta.address + 16,0,sizeof(ta.address) - 16);
+				ta.port = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
+				break;
+		}
+	}
+
 	/**
 	/**
 	 * Set to null/zero
 	 * Set to null/zero
 	 */
 	 */

+ 1 - 1
node/LZ4.cpp

@@ -101,7 +101,7 @@ union LZ4_streamDecode_u {
 #define HEAPMODE 0
 #define HEAPMODE 0
 #endif
 #endif
 
 
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 #define LZ4_FORCE_MEMORY_ACCESS 0
 #define LZ4_FORCE_MEMORY_ACCESS 0
 #else
 #else
 #define LZ4_FORCE_MEMORY_ACCESS 2
 #define LZ4_FORCE_MEMORY_ACCESS 2

+ 16 - 10
node/Locator.cpp

@@ -46,8 +46,8 @@ int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool exclud
 	int p = 8;
 	int p = 8;
 
 
 	if (_ts > 0) {
 	if (_ts > 0) {
-		data[p++] = (uint8_t)(_endpointCount >> 8U);
-		data[p++] = (uint8_t)_endpointCount;
+		Utils::storeBigEndian(data + p,(uint16_t)_endpointCount);
+		p += 2;
 		for (unsigned int i = 0; i < _endpointCount; ++i) {
 		for (unsigned int i = 0; i < _endpointCount; ++i) {
 			int tmp = _at[i].marshal(data + p);
 			int tmp = _at[i].marshal(data + p);
 			if (tmp < 0)
 			if (tmp < 0)
@@ -56,11 +56,14 @@ int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool exclud
 		}
 		}
 
 
 		if (!excludeSignature) {
 		if (!excludeSignature) {
-			data[p++] = (uint8_t)(_signatureLength >> 8U);
-			data[p++] = (uint8_t)_signatureLength;
+			Utils::storeBigEndian(data + p,(uint16_t)_signatureLength);
+			p += 2;
 			memcpy(data + p,_signature,_signatureLength);
 			memcpy(data + p,_signature,_signatureLength);
 			p += (int)_signatureLength;
 			p += (int)_signatureLength;
 		}
 		}
+
+		Utils::storeBigEndian(data + p,_flags);
+		p += 2;
 	}
 	}
 
 
 	return p;
 	return p;
@@ -75,9 +78,8 @@ int Locator::unmarshal(const uint8_t *restrict data,const int len)
 	int p = 8;
 	int p = 8;
 
 
 	if (_ts > 0) {
 	if (_ts > 0) {
-		unsigned int ec = (int)data[p++];
-		ec <<= 8U;
-		ec |= data[p++];
+		const unsigned int ec = Utils::loadBigEndian<uint16_t>(data + p);
+		p += 2;
 		if (ec > ZT_LOCATOR_MAX_ENDPOINTS)
 		if (ec > ZT_LOCATOR_MAX_ENDPOINTS)
 			return -1;
 			return -1;
 		_endpointCount = ec;
 		_endpointCount = ec;
@@ -90,9 +92,8 @@ int Locator::unmarshal(const uint8_t *restrict data,const int len)
 
 
 		if ((p + 2) > len)
 		if ((p + 2) > len)
 			return -1;
 			return -1;
-		unsigned int sl = data[p++];
-		sl <<= 8U;
-		sl |= data[p++];
+		const unsigned int sl = Utils::loadBigEndian<uint16_t>(data + p);
+		p += 2;
 		if (sl > ZT_SIGNATURE_BUFFER_SIZE)
 		if (sl > ZT_SIGNATURE_BUFFER_SIZE)
 			return -1;
 			return -1;
 		_signatureLength = sl;
 		_signatureLength = sl;
@@ -100,6 +101,11 @@ int Locator::unmarshal(const uint8_t *restrict data,const int len)
 			return -1;
 			return -1;
 		memcpy(_signature,data + p,sl);
 		memcpy(_signature,data + p,sl);
 		p += (int)sl;
 		p += (int)sl;
+
+		if ((p + 2) > len)
+			return -1;
+		_flags = Utils::loadBigEndian<uint16_t>(data + p);
+		p += 2;
 	} else {
 	} else {
 		_ts = 0;
 		_ts = 0;
 	}
 	}

+ 2 - 1
node/Locator.hpp

@@ -23,7 +23,7 @@
 #include "Identity.hpp"
 #include "Identity.hpp"
 
 
 #define ZT_LOCATOR_MAX_ENDPOINTS 8
 #define ZT_LOCATOR_MAX_ENDPOINTS 8
-#define ZT_LOCATOR_MARSHAL_SIZE_MAX (8 + 2 + (ZT_ENDPOINT_MARSHAL_SIZE_MAX * ZT_LOCATOR_MAX_ENDPOINTS) + 2 + ZT_SIGNATURE_BUFFER_SIZE)
+#define ZT_LOCATOR_MARSHAL_SIZE_MAX (8 + 2 + (ZT_ENDPOINT_MARSHAL_SIZE_MAX * ZT_LOCATOR_MAX_ENDPOINTS) + 2 + 2 + ZT_SIGNATURE_BUFFER_SIZE)
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
@@ -120,6 +120,7 @@ private:
 	unsigned int _endpointCount;
 	unsigned int _endpointCount;
 	unsigned int _signatureLength;
 	unsigned int _signatureLength;
 	Endpoint _at[ZT_LOCATOR_MAX_ENDPOINTS];
 	Endpoint _at[ZT_LOCATOR_MAX_ENDPOINTS];
+	uint16_t _flags;
 	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
 	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
 };
 };
 
 

+ 70 - 27
node/Membership.cpp

@@ -20,7 +20,6 @@
 #include "Switch.hpp"
 #include "Switch.hpp"
 #include "Packet.hpp"
 #include "Packet.hpp"
 #include "Node.hpp"
 #include "Node.hpp"
-#include "Trace.hpp"
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
@@ -102,17 +101,24 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const i
 	_lastPushedCredentials = now;
 	_lastPushedCredentials = now;
 }
 }
 
 
-Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com)
+void Membership::clean(const int64_t now,const NetworkConfig &nconf)
+{
+	_cleanCredImpl<Tag>(nconf,_remoteTags);
+	_cleanCredImpl<Capability>(nconf,_remoteCaps);
+	_cleanCredImpl<CertificateOfOwnership>(nconf,_remoteCoos);
+}
+
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const Identity &sourcePeerIdentity,const NetworkConfig &nconf,const CertificateOfMembership &com)
 {
 {
 	const int64_t newts = com.timestamp();
 	const int64_t newts = com.timestamp();
 	if (newts <= _comRevocationThreshold) {
 	if (newts <= _comRevocationThreshold) {
-		RR->t->credentialRejected(tPtr,com,"revoked");
+		RR->t->credentialRejected(tPtr,com.networkId(),sourcePeerIdentity.address(),com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_REVOKED);
 		return ADD_REJECTED;
 		return ADD_REJECTED;
 	}
 	}
 
 
 	const int64_t oldts = _com.timestamp();
 	const int64_t oldts = _com.timestamp();
 	if (newts < oldts) {
 	if (newts < oldts) {
-		RR->t->credentialRejected(tPtr,com,"old");
+		RR->t->credentialRejected(tPtr,com.networkId(),sourcePeerIdentity.address(),com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_OLDER_THAN_LATEST);
 		return ADD_REJECTED;
 		return ADD_REJECTED;
 	}
 	}
 	if ((newts == oldts)&&(_com == com))
 	if ((newts == oldts)&&(_com == com))
@@ -120,27 +126,34 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 
 
 	switch(com.verify(RR,tPtr)) {
 	switch(com.verify(RR,tPtr)) {
 		default:
 		default:
-			RR->t->credentialRejected(tPtr,com,"invalid");
+			RR->t->credentialRejected(tPtr,com.networkId(),sourcePeerIdentity.address(),com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID);
 			return Membership::ADD_REJECTED;
 			return Membership::ADD_REJECTED;
 		case Credential::VERIFY_OK:
 		case Credential::VERIFY_OK:
 			_com = com;
 			_com = com;
 			return ADD_ACCEPTED_NEW;
 			return ADD_ACCEPTED_NEW;
 		case Credential::VERIFY_BAD_SIGNATURE:
 		case Credential::VERIFY_BAD_SIGNATURE:
-			RR->t->credentialRejected(tPtr,com,"invalid");
+			RR->t->credentialRejected(tPtr,com.networkId(),sourcePeerIdentity.address(),com.id(),com.timestamp(),ZT_CREDENTIAL_TYPE_COM,ZT_TRACE_CREDENTIAL_REJECTION_REASON_SIGNATURE_VERIFICATION_FAILED);
 			return ADD_REJECTED;
 			return ADD_REJECTED;
 		case Credential::VERIFY_NEED_IDENTITY:
 		case Credential::VERIFY_NEED_IDENTITY:
 			return ADD_DEFERRED_FOR_WHOIS;
 			return ADD_DEFERRED_FOR_WHOIS;
 	}
 	}
 }
 }
 
 
-// Template out addCredential() for many cred types to avoid copypasta
+// 3/5 of the credential types have identical addCredential() code
 template<typename C>
 template<typename C>
-static Membership::AddCredentialResult _addCredImpl(Hashtable<uint32_t,C> &remoteCreds,const Hashtable<uint64_t,int64_t> &revocations,const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const C &cred)
+static ZT_ALWAYS_INLINE Membership::AddCredentialResult _addCredImpl(
+	Hashtable<uint32_t,C> &remoteCreds,
+	const Hashtable<uint64_t,int64_t> &revocations,
+	const RuntimeEnvironment *const RR,
+	void *const tPtr,
+	const Identity &sourcePeerIdentity,
+	const NetworkConfig &nconf,
+	const C &cred)
 {
 {
 	C *rc = remoteCreds.get(cred.id());
 	C *rc = remoteCreds.get(cred.id());
 	if (rc) {
 	if (rc) {
 		if (rc->timestamp() > cred.timestamp()) {
 		if (rc->timestamp() > cred.timestamp()) {
-			RR->t->credentialRejected(tPtr,cred,"old");
+			RR->t->credentialRejected(tPtr,nconf.networkId,sourcePeerIdentity.address(),cred.id(),cred.timestamp(),C::credentialType(),ZT_TRACE_CREDENTIAL_REJECTION_REASON_OLDER_THAN_LATEST);
 			return Membership::ADD_REJECTED;
 			return Membership::ADD_REJECTED;
 		}
 		}
 		if (*rc == cred)
 		if (*rc == cred)
@@ -149,13 +162,13 @@ static Membership::AddCredentialResult _addCredImpl(Hashtable<uint32_t,C> &remot
 
 
 	const int64_t *const rt = revocations.get(Membership::credentialKey(C::credentialType(),cred.id()));
 	const int64_t *const rt = revocations.get(Membership::credentialKey(C::credentialType(),cred.id()));
 	if ((rt)&&(*rt >= cred.timestamp())) {
 	if ((rt)&&(*rt >= cred.timestamp())) {
-		RR->t->credentialRejected(tPtr,cred,"revoked");
+		RR->t->credentialRejected(tPtr,nconf.networkId,sourcePeerIdentity.address(),cred.id(),cred.timestamp(),C::credentialType(),ZT_TRACE_CREDENTIAL_REJECTION_REASON_REVOKED);
 		return Membership::ADD_REJECTED;
 		return Membership::ADD_REJECTED;
 	}
 	}
 
 
 	switch(cred.verify(RR,tPtr)) {
 	switch(cred.verify(RR,tPtr)) {
 		default:
 		default:
-			RR->t->credentialRejected(tPtr,cred,"invalid");
+			RR->t->credentialRejected(tPtr,nconf.networkId,sourcePeerIdentity.address(),cred.id(),cred.timestamp(),C::credentialType(),ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID);
 			return Membership::ADD_REJECTED;
 			return Membership::ADD_REJECTED;
 		case 0:
 		case 0:
 			if (!rc)
 			if (!rc)
@@ -166,30 +179,29 @@ static Membership::AddCredentialResult _addCredImpl(Hashtable<uint32_t,C> &remot
 			return Membership::ADD_DEFERRED_FOR_WHOIS;
 			return Membership::ADD_DEFERRED_FOR_WHOIS;
 	}
 	}
 }
 }
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const Identity &sourcePeerIdentity,const NetworkConfig &nconf,const Tag &tag) { return _addCredImpl<Tag>(_remoteTags,_revocations,RR,tPtr,sourcePeerIdentity,nconf,tag); }
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const Identity &sourcePeerIdentity,const NetworkConfig &nconf,const Capability &cap) { return _addCredImpl<Capability>(_remoteCaps,_revocations,RR,tPtr,sourcePeerIdentity,nconf,cap); }
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const Identity &sourcePeerIdentity,const NetworkConfig &nconf,const CertificateOfOwnership &coo) { return _addCredImpl<CertificateOfOwnership>(_remoteCoos,_revocations,RR,tPtr,sourcePeerIdentity,nconf,coo); }
 
 
-Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag) { return _addCredImpl<Tag>(_remoteTags,_revocations,RR,tPtr,nconf,tag); }
-Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap) { return _addCredImpl<Capability>(_remoteCaps,_revocations,RR,tPtr,nconf,cap); }
-Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo) { return _addCredImpl<CertificateOfOwnership>(_remoteCoos,_revocations,RR,tPtr,nconf,coo); }
-
-Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev)
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const Identity &sourcePeerIdentity,const NetworkConfig &nconf,const Revocation &rev)
 {
 {
 	int64_t *rt;
 	int64_t *rt;
 	switch(rev.verify(RR,tPtr)) {
 	switch(rev.verify(RR,tPtr)) {
 		default:
 		default:
-			RR->t->credentialRejected(tPtr,rev,"invalid");
+			RR->t->credentialRejected(tPtr,nconf.networkId,sourcePeerIdentity.address(),rev.id(),0,ZT_CREDENTIAL_TYPE_REVOCATION,ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID);
 			return ADD_REJECTED;
 			return ADD_REJECTED;
 		case 0: {
 		case 0: {
-			const Credential::Type ct = rev.type();
+			const ZT_CredentialType ct = rev.typeBeingRevoked();
 			switch(ct) {
 			switch(ct) {
-				case Credential::CREDENTIAL_TYPE_COM:
+				case ZT_CREDENTIAL_TYPE_COM:
 					if (rev.threshold() > _comRevocationThreshold) {
 					if (rev.threshold() > _comRevocationThreshold) {
 						_comRevocationThreshold = rev.threshold();
 						_comRevocationThreshold = rev.threshold();
 						return ADD_ACCEPTED_NEW;
 						return ADD_ACCEPTED_NEW;
 					}
 					}
 					return ADD_ACCEPTED_REDUNDANT;
 					return ADD_ACCEPTED_REDUNDANT;
-				case Credential::CREDENTIAL_TYPE_CAPABILITY:
-				case Credential::CREDENTIAL_TYPE_TAG:
-				case Credential::CREDENTIAL_TYPE_COO:
+				case ZT_CREDENTIAL_TYPE_CAPABILITY:
+				case ZT_CREDENTIAL_TYPE_TAG:
+				case ZT_CREDENTIAL_TYPE_COO:
 					rt = &(_revocations[credentialKey(ct,rev.credentialId())]);
 					rt = &(_revocations[credentialKey(ct,rev.credentialId())]);
 					if (*rt < rev.threshold()) {
 					if (*rt < rev.threshold()) {
 						*rt = rev.threshold();
 						*rt = rev.threshold();
@@ -198,7 +210,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 					}
 					}
 					return ADD_ACCEPTED_REDUNDANT;
 					return ADD_ACCEPTED_REDUNDANT;
 				default:
 				default:
-					RR->t->credentialRejected(tPtr,rev,"invalid");
+					RR->t->credentialRejected(tPtr,nconf.networkId,sourcePeerIdentity.address(),rev.id(),0,ZT_CREDENTIAL_TYPE_REVOCATION,ZT_TRACE_CREDENTIAL_REJECTION_REASON_INVALID);
 					return ADD_REJECTED;
 					return ADD_REJECTED;
 			}
 			}
 		}
 		}
@@ -207,11 +219,42 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 	}
 	}
 }
 }
 
 
-void Membership::clean(const int64_t now,const NetworkConfig &nconf)
+bool Membership::_isUnspoofableAddress(const NetworkConfig &nconf,const InetAddress &ip) const
 {
 {
-	_cleanCredImpl<Tag>(nconf,_remoteTags);
-	_cleanCredImpl<Capability>(nconf,_remoteCaps);
-	_cleanCredImpl<CertificateOfOwnership>(nconf,_remoteCoos);
+	if ((ip.isV6())&&(nconf.ndpEmulation())) {
+		const InetAddress sixpl(InetAddress::makeIpv66plane(nconf.networkId,nconf.issuedTo.toInt()));
+		for(unsigned int i=0;i<nconf.staticIpCount;++i) {
+			if (nconf.staticIps[i].ipsEqual(sixpl)) {
+				bool prefixMatches = true;
+				for(unsigned int j=0;j<5;++j) { // check for match on /40
+					if ((((const struct sockaddr_in6 *)&ip)->sin6_addr.s6_addr)[j] != (((const struct sockaddr_in6 *)&sixpl)->sin6_addr.s6_addr)[j]) {
+						prefixMatches = false;
+						break;
+					}
+				}
+				if (prefixMatches)
+					return true;
+				break;
+			}
+		}
+
+		const InetAddress rfc4193(InetAddress::makeIpv6rfc4193(nconf.networkId,nconf.issuedTo.toInt()));
+		for(unsigned int i=0;i<nconf.staticIpCount;++i) {
+			if (nconf.staticIps[i].ipsEqual(rfc4193)) {
+				bool prefixMatches = true;
+				for(unsigned int j=0;j<11;++j) { // check for match on /88
+					if ((((const struct sockaddr_in6 *)&ip)->sin6_addr.s6_addr)[j] != (((const struct sockaddr_in6 *)&rfc4193)->sin6_addr.s6_addr)[j]) {
+						prefixMatches = false;
+						break;
+					}
+				}
+				if (prefixMatches)
+					return true;
+				break;
+			}
+		}
+	}
+	return false;
 }
 }
 
 
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 16 - 80
node/Membership.hpp

@@ -25,8 +25,6 @@
 #include "Revocation.hpp"
 #include "Revocation.hpp"
 #include "NetworkConfig.hpp"
 #include "NetworkConfig.hpp"
 
 
-#define ZT_MEMBERSHIP_CRED_ID_UNUSED 0xffffffffffffffffULL
-
 namespace ZeroTier {
 namespace ZeroTier {
 
 
 class RuntimeEnvironment;
 class RuntimeEnvironment;
@@ -61,7 +59,7 @@ public:
 	 * @param peerAddress Address of member peer (the one that this Membership describes)
 	 * @param peerAddress Address of member peer (the one that this Membership describes)
 	 * @param nconf My network config
 	 * @param nconf My network config
 	 */
 	 */
-	void pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf);
+	void pushCredentials(const RuntimeEnvironment *RR,void *tPtr,int64_t now,const Address &peerAddress,const NetworkConfig &nconf);
 
 
 	/**
 	/**
 	 * @return Time we last pushed credentials to this member
 	 * @return Time we last pushed credentials to this member
@@ -94,8 +92,8 @@ public:
 	{
 	{
 		if (_isUnspoofableAddress(nconf,r))
 		if (_isUnspoofableAddress(nconf,r))
 			return true;
 			return true;
-		uint32_t *k = (uint32_t *)0;
-		CertificateOfOwnership *v = (CertificateOfOwnership *)0;
+		uint32_t *k = nullptr;
+		CertificateOfOwnership *v = nullptr;
 		Hashtable< uint32_t,CertificateOfOwnership >::Iterator i(*(const_cast< Hashtable< uint32_t,CertificateOfOwnership> *>(&_remoteCoos)));
 		Hashtable< uint32_t,CertificateOfOwnership >::Iterator i(*(const_cast< Hashtable< uint32_t,CertificateOfOwnership> *>(&_remoteCoos)));
 		while (i.next(k,v)) {
 		while (i.next(k,v)) {
 			if (_isCredentialTimestampValid(nconf,*v)&&(v->owns(r)))
 			if (_isCredentialTimestampValid(nconf,*v)&&(v->owns(r)))
@@ -117,12 +115,6 @@ public:
 		return (((t)&&(_isCredentialTimestampValid(nconf,*t))) ? t : (Tag *)0);
 		return (((t)&&(_isCredentialTimestampValid(nconf,*t))) ? t : (Tag *)0);
 	}
 	}
 
 
-	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com);
-	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag);
-	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap);
-	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo);
-	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev);
-
 	/**
 	/**
 	 * Clean internal databases of stale entries
 	 * Clean internal databases of stale entries
 	 *
 	 *
@@ -134,70 +126,20 @@ public:
 	/**
 	/**
 	 * Generates a key for internal use in indexing credentials by type and credential ID
 	 * Generates a key for internal use in indexing credentials by type and credential ID
 	 */
 	 */
-	static uint64_t credentialKey(const Credential::Type &t,const uint32_t i) { return (((uint64_t)t << 32) | (uint64_t)i); }
-
-	/**
-	 * @return Bytes received so far
-	 */
-	ZT_ALWAYS_INLINE uint64_t receivedBytes() const { return _received; }
-
-	/**
-	 * @return Bytes sent so far
-	 */
-	ZT_ALWAYS_INLINE uint64_t sentBytes() const { return _sent; }
-
-	/**
-	 * @param bytes Bytes received
-	 */
-	ZT_ALWAYS_INLINE void logReceivedBytes(const unsigned int bytes) { _received = (uint64_t)bytes; }
+	static ZT_ALWAYS_INLINE uint64_t credentialKey(const ZT_CredentialType &t,const uint32_t i) { return (((uint64_t)t << 32U) | (uint64_t)i); }
 
 
-	/**
-	 * @param bytes Bytes sent
-	 */
-	ZT_ALWAYS_INLINE void logSentBytes(const unsigned int bytes) { _sent = (uint64_t)bytes; }
+	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const Identity &sourcePeerIdentity,const NetworkConfig &nconf,const CertificateOfMembership &com);
+	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const Identity &sourcePeerIdentity,const NetworkConfig &nconf,const Tag &tag);
+	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const Identity &sourcePeerIdentity,const NetworkConfig &nconf,const Capability &cap);
+	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const Identity &sourcePeerIdentity,const NetworkConfig &nconf,const CertificateOfOwnership &coo);
+	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const Identity &sourcePeerIdentity,const NetworkConfig &nconf,const Revocation &rev);
 
 
 private:
 private:
 	// This returns true if a resource is an IPv6 NDP-emulated address. These embed the ZT
 	// This returns true if a resource is an IPv6 NDP-emulated address. These embed the ZT
 	// address of the peer and therefore cannot be spoofed, causing peerOwnsAddress() to
 	// address of the peer and therefore cannot be spoofed, causing peerOwnsAddress() to
 	// always return true for them. A certificate is not required for these.
 	// always return true for them. A certificate is not required for these.
 	ZT_ALWAYS_INLINE bool _isUnspoofableAddress(const NetworkConfig &nconf,const MAC &m) const { return false; }
 	ZT_ALWAYS_INLINE bool _isUnspoofableAddress(const NetworkConfig &nconf,const MAC &m) const { return false; }
-	ZT_ALWAYS_INLINE bool _isUnspoofableAddress(const NetworkConfig &nconf,const InetAddress &ip) const
-	{
-		if ((ip.isV6())&&(nconf.ndpEmulation())) {
-			const InetAddress sixpl(InetAddress::makeIpv66plane(nconf.networkId,nconf.issuedTo.toInt()));
-			for(unsigned int i=0;i<nconf.staticIpCount;++i) {
-				if (nconf.staticIps[i].ipsEqual(sixpl)) {
-					bool prefixMatches = true;
-					for(unsigned int j=0;j<5;++j) { // check for match on /40
-						if ((((const struct sockaddr_in6 *)&ip)->sin6_addr.s6_addr)[j] != (((const struct sockaddr_in6 *)&sixpl)->sin6_addr.s6_addr)[j]) {
-							prefixMatches = false;
-							break;
-						}
-					}
-					if (prefixMatches)
-						return true;
-					break;
-				}
-			}
-
-			const InetAddress rfc4193(InetAddress::makeIpv6rfc4193(nconf.networkId,nconf.issuedTo.toInt()));
-			for(unsigned int i=0;i<nconf.staticIpCount;++i) {
-				if (nconf.staticIps[i].ipsEqual(rfc4193)) {
-					bool prefixMatches = true;
-					for(unsigned int j=0;j<11;++j) { // check for match on /88
-						if ((((const struct sockaddr_in6 *)&ip)->sin6_addr.s6_addr)[j] != (((const struct sockaddr_in6 *)&rfc4193)->sin6_addr.s6_addr)[j]) {
-							prefixMatches = false;
-							break;
-						}
-					}
-					if (prefixMatches)
-						return true;
-					break;
-				}
-			}
-		}
-		return false;
-	}
+	bool _isUnspoofableAddress(const NetworkConfig &nconf,const InetAddress &ip) const;
 
 
 	// This compares the remote credential's timestamp to the timestamp in our network config
 	// This compares the remote credential's timestamp to the timestamp in our network config
 	// plus or minus the permitted maximum timestamp delta.
 	// plus or minus the permitted maximum timestamp delta.
@@ -213,10 +155,10 @@ private:
 	}
 	}
 
 
 	template<typename C>
 	template<typename C>
-	inline void _cleanCredImpl(const NetworkConfig &nconf,Hashtable<uint32_t,C> &remoteCreds)
+	ZT_ALWAYS_INLINE void _cleanCredImpl(const NetworkConfig &nconf,Hashtable<uint32_t,C> &remoteCreds)
 	{
 	{
-		uint32_t *k = (uint32_t *)0;
-		C *v = (C *)0;
+		uint32_t *k = nullptr;
+		C *v = nullptr;
 		typename Hashtable<uint32_t,C>::Iterator i(remoteCreds);
 		typename Hashtable<uint32_t,C>::Iterator i(remoteCreds);
 		while (i.next(k,v)) {
 		while (i.next(k,v)) {
 			if (!_isCredentialTimestampValid(nconf,*v))
 			if (!_isCredentialTimestampValid(nconf,*v))
@@ -233,12 +175,6 @@ private:
 	// Time we last pushed credentials
 	// Time we last pushed credentials
 	int64_t _lastPushedCredentials;
 	int64_t _lastPushedCredentials;
 
 
-	// Number of Ethernet frame bytes received
-	uint64_t _received;
-
-	// Number of Ethernet frame bytes sent
-	uint64_t _sent;
-
 	// Remote member's latest network COM
 	// Remote member's latest network COM
 	CertificateOfMembership _com;
 	CertificateOfMembership _com;
 
 
@@ -256,8 +192,8 @@ public:
 	public:
 	public:
 		ZT_ALWAYS_INLINE CapabilityIterator(Membership &m,const NetworkConfig &nconf) :
 		ZT_ALWAYS_INLINE CapabilityIterator(Membership &m,const NetworkConfig &nconf) :
 			_hti(m._remoteCaps),
 			_hti(m._remoteCaps),
-			_k((uint32_t *)0),
-			_c((Capability *)0),
+			_k(nullptr),
+			_c(nullptr),
 			_m(m),
 			_m(m),
 			_nconf(nconf)
 			_nconf(nconf)
 		{
 		{
@@ -269,7 +205,7 @@ public:
 				if (_m._isCredentialTimestampValid(_nconf,*_c))
 				if (_m._isCredentialTimestampValid(_nconf,*_c))
 					return _c;
 					return _c;
 			}
 			}
-			return (Capability *)0;
+			return nullptr;
 		}
 		}
 
 
 	private:
 	private:

+ 128 - 68
node/Network.cpp

@@ -11,12 +11,8 @@
  */
  */
 /****/
 /****/
 
 
-#include <cstdio>
 #include <cstring>
 #include <cstring>
 #include <cstdlib>
 #include <cstdlib>
-#include <cmath>
-
-#include "../include/ZeroTierDebug.h"
 
 
 #include "Constants.hpp"
 #include "Constants.hpp"
 #include "Network.hpp"
 #include "Network.hpp"
@@ -28,7 +24,6 @@
 #include "Buffer.hpp"
 #include "Buffer.hpp"
 #include "Packet.hpp"
 #include "Packet.hpp"
 #include "NetworkController.hpp"
 #include "NetworkController.hpp"
-#include "Node.hpp"
 #include "Peer.hpp"
 #include "Peer.hpp"
 #include "Trace.hpp"
 #include "Trace.hpp"
 #include "ScopedPtr.hpp"
 #include "ScopedPtr.hpp"
@@ -40,7 +35,7 @@ namespace ZeroTier {
 namespace {
 namespace {
 
 
 // Returns true if packet appears valid; pos and proto will be set
 // Returns true if packet appears valid; pos and proto will be set
-static bool _ipv6GetPayload(const uint8_t *frameData,unsigned int frameLen,unsigned int &pos,unsigned int &proto)
+bool _ipv6GetPayload(const uint8_t *frameData,unsigned int frameLen,unsigned int &pos,unsigned int &proto)
 {
 {
 	if (frameLen < 40)
 	if (frameLen < 40)
 		return false;
 		return false;
@@ -77,7 +72,7 @@ enum _doZtFilterResult
 	DOZTFILTER_SUPER_ACCEPT
 	DOZTFILTER_SUPER_ACCEPT
 };
 };
 
 
-static _doZtFilterResult _doZtFilter(
+_doZtFilterResult _doZtFilter(
 	const RuntimeEnvironment *RR,
 	const RuntimeEnvironment *RR,
 	Trace::RuleResultLog &rrl,
 	Trace::RuleResultLog &rrl,
 	const NetworkConfig &nconf,
 	const NetworkConfig &nconf,
@@ -108,14 +103,14 @@ static _doZtFilterResult _doZtFilter(
 	rrl.clear();
 	rrl.clear();
 
 
 	for(unsigned int rn=0;rn<ruleCount;++rn) {
 	for(unsigned int rn=0;rn<ruleCount;++rn) {
-		const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[rn].t & 0x3f);
+		const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[rn].t & 0x3fU);
 
 
 		// First check if this is an ACTION
 		// First check if this is an ACTION
 		if ((unsigned int)rt <= (unsigned int)ZT_NETWORK_RULE_ACTION__MAX_ID) {
 		if ((unsigned int)rt <= (unsigned int)ZT_NETWORK_RULE_ACTION__MAX_ID) {
 			if (thisSetMatches) {
 			if (thisSetMatches) {
 				switch(rt) {
 				switch(rt) {
 					case ZT_NETWORK_RULE_ACTION_PRIORITY:
 					case ZT_NETWORK_RULE_ACTION_PRIORITY:
-						qosBucket = (rules[rn].v.qosBucket >= 0 || rules[rn].v.qosBucket <= 8) ? rules[rn].v.qosBucket : 4; // 4 = default bucket (no priority)
+						qosBucket = (rules[rn].v.qosBucket >= 0 && rules[rn].v.qosBucket <= 8) ? rules[rn].v.qosBucket : 4; // 4 = default bucket (no priority)
 						return DOZTFILTER_ACCEPT;
 						return DOZTFILTER_ACCEPT;
 
 
 					case ZT_NETWORK_RULE_ACTION_DROP:
 					case ZT_NETWORK_RULE_ACTION_DROP:
@@ -180,7 +175,7 @@ static _doZtFilterResult _doZtFilter(
 
 
 		// Circuit breaker: no need to evaluate an AND if the set's match state
 		// Circuit breaker: no need to evaluate an AND if the set's match state
 		// is currently false since anything AND false is false.
 		// is currently false since anything AND false is false.
-		if ((!thisSetMatches)&&(!(rules[rn].t & 0x40))) {
+		if ((!thisSetMatches)&&(!(rules[rn].t & 0x40U))) {
 			rrl.logSkipped(rn,thisSetMatches);
 			rrl.logSkipped(rn,thisSetMatches);
 			continue;
 			continue;
 		}
 		}
@@ -245,7 +240,7 @@ static _doZtFilterResult _doZtFilter(
 					const uint8_t tosMasked = frameData[1] & rules[rn].v.ipTos.mask;
 					const uint8_t tosMasked = frameData[1] & rules[rn].v.ipTos.mask;
 					thisRuleMatches = (uint8_t)((tosMasked >= rules[rn].v.ipTos.value[0])&&(tosMasked <= rules[rn].v.ipTos.value[1]));
 					thisRuleMatches = (uint8_t)((tosMasked >= rules[rn].v.ipTos.value[0])&&(tosMasked <= rules[rn].v.ipTos.value[1]));
 				} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
 				} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
-					const uint8_t tosMasked = (((frameData[0] << 4) & 0xf0) | ((frameData[1] >> 4) & 0x0f)) & rules[rn].v.ipTos.mask;
+					const uint8_t tosMasked = (((frameData[0] << 4U) & 0xf0U) | ((frameData[1] >> 4U) & 0x0fU)) & rules[rn].v.ipTos.mask;
 					thisRuleMatches = (uint8_t)((tosMasked >= rules[rn].v.ipTos.value[0])&&(tosMasked <= rules[rn].v.ipTos.value[1]));
 					thisRuleMatches = (uint8_t)((tosMasked >= rules[rn].v.ipTos.value[0])&&(tosMasked <= rules[rn].v.ipTos.value[1]));
 				} else {
 				} else {
 					thisRuleMatches = 0;
 					thisRuleMatches = 0;
@@ -271,7 +266,7 @@ static _doZtFilterResult _doZtFilter(
 			case ZT_NETWORK_RULE_MATCH_ICMP:
 			case ZT_NETWORK_RULE_MATCH_ICMP:
 				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
 				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
 					if (frameData[9] == 0x01) { // IP protocol == ICMP
 					if (frameData[9] == 0x01) { // IP protocol == ICMP
-						const unsigned int ihl = (frameData[0] & 0xf) * 4;
+						const unsigned int ihl = (frameData[0] & 0xfU) * 4;
 						if (frameLen >= (ihl + 2)) {
 						if (frameLen >= (ihl + 2)) {
 							if (rules[rn].v.icmp.type == frameData[ihl]) {
 							if (rules[rn].v.icmp.type == frameData[ihl]) {
 								if ((rules[rn].v.icmp.flags & 0x01) != 0) {
 								if ((rules[rn].v.icmp.flags & 0x01) != 0) {
@@ -314,7 +309,7 @@ static _doZtFilterResult _doZtFilter(
 			case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
 			case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
 			case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
 			case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
 				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
 				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
-					const unsigned int headerLen = 4 * (frameData[0] & 0xf);
+					const unsigned int headerLen = 4 * (frameData[0] & 0xfU);
 					int p = -1;
 					int p = -1;
 					switch(frameData[9]) { // IP protocol number
 					switch(frameData[9]) { // IP protocol number
 						// All these start with 16-bit source and destination port in that order
 						// All these start with 16-bit source and destination port in that order
@@ -324,7 +319,7 @@ static _doZtFilterResult _doZtFilter(
 						case 0x88: // UDPLite
 						case 0x88: // UDPLite
 							if (frameLen > (headerLen + 4)) {
 							if (frameLen > (headerLen + 4)) {
 								unsigned int pos = headerLen + ((rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE) ? 2 : 0);
 								unsigned int pos = headerLen + ((rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE) ? 2 : 0);
-								p = (int)frameData[pos++] << 8;
+								p = (int)(frameData[pos++] << 8U);
 								p |= (int)frameData[pos];
 								p |= (int)frameData[pos];
 							}
 							}
 							break;
 							break;
@@ -343,7 +338,7 @@ static _doZtFilterResult _doZtFilter(
 							case 0x88: // UDPLite
 							case 0x88: // UDPLite
 								if (frameLen > (pos + 4)) {
 								if (frameLen > (pos + 4)) {
 									if (rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE) pos += 2;
 									if (rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE) pos += 2;
-									p = (int)frameData[pos++] << 8;
+									p = (int)(frameData[pos++] << 8U);
 									p |= (int)frameData[pos];
 									p |= (int)frameData[pos];
 								}
 								}
 								break;
 								break;
@@ -402,15 +397,15 @@ static _doZtFilterResult _doZtFilter(
 				}
 				}
 				cf |= ownershipVerificationMask;
 				cf |= ownershipVerificationMask;
 				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)&&(frameData[9] == 0x06)) {
 				if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)&&(frameData[9] == 0x06)) {
-					const unsigned int headerLen = 4 * (frameData[0] & 0xf);
+					const unsigned int headerLen = 4 * (frameData[0] & 0xfU);
 					cf |= (uint64_t)frameData[headerLen + 13];
 					cf |= (uint64_t)frameData[headerLen + 13];
-					cf |= (((uint64_t)(frameData[headerLen + 12] & 0x0f)) << 8);
+					cf |= (((uint64_t)(frameData[headerLen + 12] & 0x0fU)) << 8U);
 				} else if (etherType == ZT_ETHERTYPE_IPV6) {
 				} else if (etherType == ZT_ETHERTYPE_IPV6) {
 					unsigned int pos = 0,proto = 0;
 					unsigned int pos = 0,proto = 0;
 					if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
 					if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
 						if ((proto == 0x06)&&(frameLen > (pos + 14))) {
 						if ((proto == 0x06)&&(frameLen > (pos + 14))) {
 							cf |= (uint64_t)frameData[pos + 13];
 							cf |= (uint64_t)frameData[pos + 13];
-							cf |= (((uint64_t)(frameData[pos + 12] & 0x0f)) << 8);
+							cf |= (((uint64_t)(frameData[pos + 12] & 0x0fU)) << 8U);
 						}
 						}
 					}
 					}
 				}
 				}
@@ -491,15 +486,15 @@ static _doZtFilterResult _doZtFilter(
 			}	break;
 			}	break;
 			case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: {
 			case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: {
 				uint64_t integer = 0;
 				uint64_t integer = 0;
-				const unsigned int bits = (rules[rn].v.intRange.format & 63) + 1;
+				const unsigned int bits = (rules[rn].v.intRange.format & 63U) + 1;
 				const unsigned int bytes = ((bits + 8 - 1) / 8); // integer ceiling of division by 8
 				const unsigned int bytes = ((bits + 8 - 1) / 8); // integer ceiling of division by 8
-				if ((rules[rn].v.intRange.format & 0x80) == 0) {
+				if ((rules[rn].v.intRange.format & 0x80U) == 0) {
 					// Big-endian
 					// Big-endian
 					unsigned int idx = rules[rn].v.intRange.idx + (8 - bytes);
 					unsigned int idx = rules[rn].v.intRange.idx + (8 - bytes);
 					const unsigned int eof = idx + bytes;
 					const unsigned int eof = idx + bytes;
 					if (eof <= frameLen) {
 					if (eof <= frameLen) {
 						while (idx < eof) {
 						while (idx < eof) {
-							integer <<= 8;
+							integer <<= 8U;
 							integer |= frameData[idx++];
 							integer |= frameData[idx++];
 						}
 						}
 					}
 					}
@@ -510,8 +505,8 @@ static _doZtFilterResult _doZtFilter(
 					const unsigned int eof = idx + bytes;
 					const unsigned int eof = idx + bytes;
 					if (eof <= frameLen) {
 					if (eof <= frameLen) {
 						while (idx < eof) {
 						while (idx < eof) {
-							integer >>= 8;
-							integer |= ((uint64_t)frameData[idx++]) << 56;
+							integer >>= 8U;
+							integer |= ((uint64_t)frameData[idx++]) << 56U;
 						}
 						}
 					}
 					}
 					integer >>= (64 - bits);
 					integer >>= (64 - bits);
@@ -528,9 +523,9 @@ static _doZtFilterResult _doZtFilter(
 
 
 		rrl.log(rn,thisRuleMatches,thisSetMatches);
 		rrl.log(rn,thisRuleMatches,thisSetMatches);
 
 
-		if ((rules[rn].t & 0x40))
-			thisSetMatches |= (thisRuleMatches ^ ((rules[rn].t >> 7) & 1));
-		else thisSetMatches &= (thisRuleMatches ^ ((rules[rn].t >> 7) & 1));
+		if ((rules[rn].t & 0x40U))
+			thisSetMatches |= (thisRuleMatches ^ ((rules[rn].t >> 7U) & 1U));
+		else thisSetMatches &= (thisRuleMatches ^ ((rules[rn].t >> 7U) & 1U));
 	}
 	}
 
 
 	return DOZTFILTER_NO_MATCH;
 	return DOZTFILTER_NO_MATCH;
@@ -622,10 +617,10 @@ bool Network::filterOutgoingPacket(
 	const unsigned int vlanId,
 	const unsigned int vlanId,
 	uint8_t &qosBucket)
 	uint8_t &qosBucket)
 {
 {
+	Trace::RuleResultLog rrl,crrl;
 	Address ztFinalDest(ztDest);
 	Address ztFinalDest(ztDest);
 	int localCapabilityIndex = -1;
 	int localCapabilityIndex = -1;
 	int accept = 0;
 	int accept = 0;
-	Trace::RuleResultLog rrl,crrl;
 	Address cc;
 	Address cc;
 	unsigned int ccLength = 0;
 	unsigned int ccLength = 0;
 	bool ccWatch = false;
 	bool ccWatch = false;
@@ -674,8 +669,7 @@ bool Network::filterOutgoingPacket(
 		}	break;
 		}	break;
 
 
 		case DOZTFILTER_DROP:
 		case DOZTFILTER_DROP:
-			//if (_config.remoteTraceTarget)
-			//	RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
+			RR->t->networkFilter(tPtr,_id,rrl.l,nullptr,0,0,ztSource,ztDest,macSource,macDest,(uint16_t)frameLen,frameData,(uint16_t)etherType,(uint16_t)vlanId,noTee,false,0);
 			return false;
 			return false;
 
 
 		case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
 		case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
@@ -688,10 +682,7 @@ bool Network::filterOutgoingPacket(
 			break;
 			break;
 	}
 	}
 
 
-	if (accept) {
-		if (membership)
-			membership->logSentBytes(frameLen);
-
+	if (accept != 0) {
 		if ((!noTee)&&(cc)) {
 		if ((!noTee)&&(cc)) {
 			Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
 			Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
 			outp.append(_id);
 			outp.append(_id);
@@ -715,19 +706,19 @@ bool Network::filterOutgoingPacket(
 			outp.compress();
 			outp.compress();
 			RR->sw->send(tPtr,outp,true);
 			RR->sw->send(tPtr,outp,true);
 
 
-			//if (_config.remoteTraceTarget)
-			//	RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
-			return false; // DROP locally, since we redirected
-		} else {
-			//if (_config.remoteTraceTarget)
-			//	RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,1);
-			return true;
+			// DROP locally since we redirected
+			accept = 0;
 		}
 		}
+	}
+
+	if (localCapabilityIndex >= 0) {
+		const Capability &cap = _config.capabilities[localCapabilityIndex];
+		RR->t->networkFilter(tPtr,_id,rrl.l,crrl.l,cap.id(),cap.timestamp(),ztSource,ztDest,macSource,macDest,(uint16_t)frameLen,frameData,(uint16_t)etherType,(uint16_t)vlanId,noTee,false,accept);
 	} else {
 	} else {
-		//if (_config.remoteTraceTarget)
-		//	RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
-		return false;
+		RR->t->networkFilter(tPtr,_id,rrl.l,nullptr,0,0,ztSource,ztDest,macSource,macDest,(uint16_t)frameLen,frameData,(uint16_t)etherType,(uint16_t)vlanId,noTee,false,accept);
 	}
 	}
+
+	return (accept != 0);
 }
 }
 
 
 int Network::filterIncomingPacket(
 int Network::filterIncomingPacket(
@@ -810,8 +801,6 @@ int Network::filterIncomingPacket(
 	}
 	}
 
 
 	if (accept) {
 	if (accept) {
-		membership.logReceivedBytes(frameLen);
-
 		if (cc) {
 		if (cc) {
 			Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
 			Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
 			outp.append(_id);
 			outp.append(_id);
@@ -846,6 +835,24 @@ int Network::filterIncomingPacket(
 	return accept;
 	return accept;
 }
 }
 
 
+void Network::multicastSubscribe(void *tPtr,const MulticastGroup &mg)
+{
+	Mutex::Lock l(_myMulticastGroups_l);
+	if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) {
+		_myMulticastGroups.insert(std::upper_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg),mg);
+		Mutex::Lock l2(_memberships_l);
+		_announceMulticastGroups(tPtr,true);
+	}
+}
+
+void Network::multicastUnsubscribe(const MulticastGroup &mg)
+{
+	Mutex::Lock l(_myMulticastGroups_l);
+	std::vector<MulticastGroup>::iterator i(std::lower_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg));
+	if ( (i != _myMulticastGroups.end()) && (*i == mg) )
+		_myMulticastGroups.erase(i);
+}
+
 uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr)
 uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr)
 {
 {
 	if (_destroyed)
 	if (_destroyed)
@@ -861,11 +868,11 @@ uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Add
 	{
 	{
 		Mutex::Lock l1(_config_l);
 		Mutex::Lock l1(_config_l);
 
 
-		_IncomingConfigChunk *c = (_IncomingConfigChunk *)0;
+		_IncomingConfigChunk *c = nullptr;
 		uint64_t chunkId = 0;
 		uint64_t chunkId = 0;
 		unsigned long totalLength,chunkIndex;
 		unsigned long totalLength,chunkIndex;
 		if (ptr < chunk.size()) {
 		if (ptr < chunk.size()) {
-			const bool fastPropagate = ((chunk[ptr++] & 0x01) != 0);
+			const bool fastPropagate = ((chunk[ptr++] & 0x01U) != 0);
 			configUpdateId = chunk.at<uint64_t>(ptr); ptr += 8;
 			configUpdateId = chunk.at<uint64_t>(ptr); ptr += 8;
 			totalLength = chunk.at<uint32_t>(ptr); ptr += 4;
 			totalLength = chunk.at<uint32_t>(ptr); ptr += 4;
 			chunkIndex = chunk.at<uint32_t>(ptr); ptr += 4;
 			chunkIndex = chunk.at<uint32_t>(ptr); ptr += 4;
@@ -906,8 +913,8 @@ uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Add
 			// New properly verified chunks can be flooded "virally" through the network
 			// New properly verified chunks can be flooded "virally" through the network
 			if (fastPropagate) {
 			if (fastPropagate) {
 				Mutex::Lock l2(_memberships_l);
 				Mutex::Lock l2(_memberships_l);
-				Address *a = (Address *)0;
-				Membership *m = (Membership *)0;
+				Address *a = nullptr;
+				Membership *m = nullptr;
 				Hashtable<Address,Membership>::Iterator i(_memberships);
 				Hashtable<Address,Membership>::Iterator i(_memberships);
 				while (i.next(a,m)) {
 				while (i.next(a,m)) {
 					if ((*a != source)&&(*a != controller())) {
 					if ((*a != source)&&(*a != controller())) {
@@ -1102,7 +1109,31 @@ void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
 	}
 	}
 }
 }
 
 
-Membership::AddCredentialResult Network::addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev)
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const Identity &sourcePeerIdentity,const CertificateOfMembership &com)
+{
+	if (com.networkId() != _id)
+		return Membership::ADD_REJECTED;
+	Mutex::Lock _l(_memberships_l);
+	return _memberships[com.issuedTo()].addCredential(RR,tPtr,sourcePeerIdentity,_config,com);
+}
+
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const Identity &sourcePeerIdentity,const Capability &cap)
+{
+	if (cap.networkId() != _id)
+		return Membership::ADD_REJECTED;
+	Mutex::Lock _l(_memberships_l);
+	return _memberships[cap.issuedTo()].addCredential(RR,tPtr,sourcePeerIdentity,_config,cap);
+}
+
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const Identity &sourcePeerIdentity,const Tag &tag)
+{
+	if (tag.networkId() != _id)
+		return Membership::ADD_REJECTED;
+	Mutex::Lock _l(_memberships_l);
+	return _memberships[tag.issuedTo()].addCredential(RR,tPtr,sourcePeerIdentity,_config,tag);
+}
+
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const Identity &sourcePeerIdentity,const Revocation &rev)
 {
 {
 	if (rev.networkId() != _id)
 	if (rev.networkId() != _id)
 		return Membership::ADD_REJECTED;
 		return Membership::ADD_REJECTED;
@@ -1110,14 +1141,14 @@ Membership::AddCredentialResult Network::addCredential(void *tPtr,const Address
 	Mutex::Lock l1(_memberships_l);
 	Mutex::Lock l1(_memberships_l);
 	Membership &m = _memberships[rev.target()];
 	Membership &m = _memberships[rev.target()];
 
 
-	const Membership::AddCredentialResult result = m.addCredential(RR,tPtr,_config,rev);
+	const Membership::AddCredentialResult result = m.addCredential(RR,tPtr,sourcePeerIdentity,_config,rev);
 
 
 	if ((result == Membership::ADD_ACCEPTED_NEW)&&(rev.fastPropagate())) {
 	if ((result == Membership::ADD_ACCEPTED_NEW)&&(rev.fastPropagate())) {
-		Address *a = (Address *)0;
-		Membership *m = (Membership *)0;
+		Address *a = nullptr;
+		Membership *m = nullptr;
 		Hashtable<Address,Membership>::Iterator i(_memberships);
 		Hashtable<Address,Membership>::Iterator i(_memberships);
 		while (i.next(a,m)) {
 		while (i.next(a,m)) {
-			if ((*a != sentFrom)&&(*a != rev.signer())) {
+			if ((*a != sourcePeerIdentity.address())&&(*a != rev.signer())) {
 				Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
 				Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
 				outp.append((uint8_t)0x00); // no COM
 				outp.append((uint8_t)0x00); // no COM
 				outp.append((uint16_t)0); // no capabilities
 				outp.append((uint16_t)0); // no capabilities
@@ -1133,15 +1164,44 @@ Membership::AddCredentialResult Network::addCredential(void *tPtr,const Address
 	return result;
 	return result;
 }
 }
 
 
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const Identity &sourcePeerIdentity,const CertificateOfOwnership &coo)
+{
+	if (coo.networkId() != _id)
+		return Membership::ADD_REJECTED;
+	Mutex::Lock _l(_memberships_l);
+	return _memberships[coo.issuedTo()].addCredential(RR,tPtr,sourcePeerIdentity,_config,coo);
+}
+
+void Network::pushCredentialsNow(void *tPtr,const Address &to,const int64_t now)
+{
+	Mutex::Lock _l(_memberships_l);
+	_memberships[to].pushCredentials(RR,tPtr,now,to,_config);
+}
+
+void Network::destroy()
+{
+	_memberships_l.lock();
+	_config_l.lock();
+	_destroyed = true;
+	_config_l.unlock();
+	_memberships_l.unlock();
+}
+
+void Network::externalConfig(ZT_VirtualNetworkConfig *ec) const
+{
+	Mutex::Lock _l(_config_l);
+	_externalConfig(ec);
+}
+
 void Network::_requestConfiguration(void *tPtr)
 void Network::_requestConfiguration(void *tPtr)
 {
 {
 	if (_destroyed)
 	if (_destroyed)
 		return;
 		return;
 
 
-	if ((_id >> 56) == 0xff) {
-		if ((_id & 0xffffff) == 0) {
-			const uint16_t startPortRange = (uint16_t)((_id >> 40) & 0xffff);
-			const uint16_t endPortRange = (uint16_t)((_id >> 24) & 0xffff);
+	if ((_id >> 56U) == 0xff) {
+		if ((_id & 0xffffffU) == 0) {
+			const uint16_t startPortRange = (uint16_t)((_id >> 40U) & 0xffff);
+			const uint16_t endPortRange = (uint16_t)((_id >> 24U) & 0xffff);
 			if (endPortRange >= startPortRange) {
 			if (endPortRange >= startPortRange) {
 				ScopedPtr<NetworkConfig> nconf(new NetworkConfig());
 				ScopedPtr<NetworkConfig> nconf(new NetworkConfig());
 
 
@@ -1158,7 +1218,7 @@ void Network::_requestConfiguration(void *tPtr)
 				nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,RR->identity.address().toInt());
 				nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,RR->identity.address().toInt());
 
 
 				// Drop everything but IPv6
 				// Drop everything but IPv6
-				nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE | 0x80; // NOT
+				nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE | 0x80U; // NOT
 				nconf->rules[0].v.etherType = 0x86dd; // IPv6
 				nconf->rules[0].v.etherType = 0x86dd; // IPv6
 				nconf->rules[1].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
 				nconf->rules[1].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
 
 
@@ -1170,7 +1230,7 @@ void Network::_requestConfiguration(void *tPtr)
 				// Allow destination ports within range
 				// Allow destination ports within range
 				nconf->rules[4].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
 				nconf->rules[4].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
 				nconf->rules[4].v.ipProtocol = 0x11; // UDP
 				nconf->rules[4].v.ipProtocol = 0x11; // UDP
-				nconf->rules[5].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL | 0x40; // OR
+				nconf->rules[5].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL | 0x40U; // OR
 				nconf->rules[5].v.ipProtocol = 0x06; // TCP
 				nconf->rules[5].v.ipProtocol = 0x06; // TCP
 				nconf->rules[6].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE;
 				nconf->rules[6].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE;
 				nconf->rules[6].v.port[0] = startPortRange;
 				nconf->rules[6].v.port[0] = startPortRange;
@@ -1178,7 +1238,7 @@ void Network::_requestConfiguration(void *tPtr)
 				nconf->rules[7].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
 				nconf->rules[7].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
 
 
 				// Allow non-SYN TCP packets to permit non-connection-initiating traffic
 				// Allow non-SYN TCP packets to permit non-connection-initiating traffic
-				nconf->rules[8].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS | 0x80; // NOT
+				nconf->rules[8].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS | 0x80U; // NOT
 				nconf->rules[8].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN;
 				nconf->rules[8].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN;
 				nconf->rules[9].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
 				nconf->rules[9].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
 
 
@@ -1208,16 +1268,16 @@ void Network::_requestConfiguration(void *tPtr)
 			} else {
 			} else {
 				this->setNotFound();
 				this->setNotFound();
 			}
 			}
-		} else if ((_id & 0xff) == 0x01) {
+		} else if ((_id & 0xffU) == 0x01) {
 			// ffAAaaaaaaaaaa01 -- where AA is the IPv4 /8 to use and aaaaaaaaaa is the anchor node for multicast gather and replication
 			// ffAAaaaaaaaaaa01 -- where AA is the IPv4 /8 to use and aaaaaaaaaa is the anchor node for multicast gather and replication
 			const uint64_t myAddress = RR->identity.address().toInt();
 			const uint64_t myAddress = RR->identity.address().toInt();
-			const uint64_t networkHub = (_id >> 8) & 0xffffffffffULL;
+			const uint64_t networkHub = (_id >> 8U) & 0xffffffffffULL;
 
 
 			uint8_t ipv4[4];
 			uint8_t ipv4[4];
-			ipv4[0] = (uint8_t)((_id >> 48) & 0xff);
-			ipv4[1] = (uint8_t)((myAddress >> 16) & 0xff);
-			ipv4[2] = (uint8_t)((myAddress >> 8) & 0xff);
-			ipv4[3] = (uint8_t)(myAddress & 0xff);
+			ipv4[0] = (uint8_t)(_id >> 48U);
+			ipv4[1] = (uint8_t)(myAddress >> 16U);
+			ipv4[2] = (uint8_t)(myAddress >> 8U);
+			ipv4[3] = (uint8_t)myAddress;
 
 
 			char v4ascii[24];
 			char v4ascii[24];
 			Utils::decimal(ipv4[0],v4ascii);
 			Utils::decimal(ipv4[0],v4ascii);
@@ -1282,7 +1342,7 @@ void Network::_requestConfiguration(void *tPtr)
 	rmd->add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS,(uint64_t)0);
 	rmd->add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS,(uint64_t)0);
 	rmd->add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,(uint64_t)ZT_RULES_ENGINE_REVISION);
 	rmd->add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,(uint64_t)ZT_RULES_ENGINE_REVISION);
 
 
-	RR->t->networkConfigRequestSent(tPtr,*this,ctrl);
+	RR->t->networkConfigRequestSent(tPtr,_id);
 
 
 	if (ctrl == RR->identity.address()) {
 	if (ctrl == RR->identity.address()) {
 		if (RR->localNetworkController) {
 		if (RR->localNetworkController) {

+ 38 - 97
node/Network.hpp

@@ -58,7 +58,7 @@ public:
 	/**
 	/**
 	 * Compute primary controller device ID from network ID
 	 * Compute primary controller device ID from network ID
 	 */
 	 */
-	static inline Address controllerFor(uint64_t nwid) { return Address(nwid >> 24); }
+	static ZT_ALWAYS_INLINE Address controllerFor(uint64_t nwid) { return Address(nwid >> 24U); }
 
 
 	/**
 	/**
 	 * Construct a new network
 	 * Construct a new network
@@ -76,14 +76,14 @@ public:
 
 
 	~Network();
 	~Network();
 
 
-	inline uint64_t id() const { return _id; }
-	inline Address controller() const { return Address(_id >> 24); }
-	inline bool multicastEnabled() const { return (_config.multicastLimit > 0); }
-	inline bool hasConfig() const { return (_config); }
-	inline uint64_t lastConfigUpdate() const { return _lastConfigUpdate; }
-	inline ZT_VirtualNetworkStatus status() const { return _status(); }
-	inline const NetworkConfig &config() const { return _config; }
-	inline const MAC &mac() const { return _mac; }
+	ZT_ALWAYS_INLINE uint64_t id() const { return _id; }
+	ZT_ALWAYS_INLINE Address controller() const { return Address(_id >> 24); }
+	ZT_ALWAYS_INLINE bool multicastEnabled() const { return (_config.multicastLimit > 0); }
+	ZT_ALWAYS_INLINE bool hasConfig() const { return (_config); }
+	ZT_ALWAYS_INLINE uint64_t lastConfigUpdate() const { return _lastConfigUpdate; }
+	ZT_ALWAYS_INLINE ZT_VirtualNetworkStatus status() const { return _status(); }
+	ZT_ALWAYS_INLINE const NetworkConfig &config() const { return _config; }
+	ZT_ALWAYS_INLINE const MAC &mac() const { return _mac; }
 
 
 	/**
 	/**
 	 * Apply filters to an outgoing packet
 	 * Apply filters to an outgoing packet
@@ -107,15 +107,15 @@ public:
 	 */
 	 */
 	bool filterOutgoingPacket(
 	bool filterOutgoingPacket(
 		void *tPtr,
 		void *tPtr,
-		const bool noTee,
+		bool noTee,
 		const Address &ztSource,
 		const Address &ztSource,
 		const Address &ztDest,
 		const Address &ztDest,
 		const MAC &macSource,
 		const MAC &macSource,
 		const MAC &macDest,
 		const MAC &macDest,
 		const uint8_t *frameData,
 		const uint8_t *frameData,
-		const unsigned int frameLen,
-		const unsigned int etherType,
-		const unsigned int vlanId,
+		unsigned int frameLen,
+		unsigned int etherType,
+		unsigned int vlanId,
 		uint8_t &qosBucket);
 		uint8_t &qosBucket);
 
 
 	/**
 	/**
@@ -144,9 +144,9 @@ public:
 		const MAC &macSource,
 		const MAC &macSource,
 		const MAC &macDest,
 		const MAC &macDest,
 		const uint8_t *frameData,
 		const uint8_t *frameData,
-		const unsigned int frameLen,
-		const unsigned int etherType,
-		const unsigned int vlanId);
+		unsigned int frameLen,
+		unsigned int etherType,
+		unsigned int vlanId);
 
 
 	/**
 	/**
 	 * Check whether we are subscribed to a multicast group
 	 * Check whether we are subscribed to a multicast group
@@ -155,7 +155,7 @@ public:
 	 * @param includeBridgedGroups If true, also check groups we've learned via bridging
 	 * @param includeBridgedGroups If true, also check groups we've learned via bridging
 	 * @return True if this network endpoint / peer is a member
 	 * @return True if this network endpoint / peer is a member
 	 */
 	 */
-	inline bool subscribedToMulticastGroup(const MulticastGroup &mg,const bool includeBridgedGroups) const
+	ZT_ALWAYS_INLINE bool subscribedToMulticastGroup(const MulticastGroup &mg,const bool includeBridgedGroups) const
 	{
 	{
 		Mutex::Lock l(_myMulticastGroups_l);
 		Mutex::Lock l(_myMulticastGroups_l);
 		if (std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg))
 		if (std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg))
@@ -171,28 +171,14 @@ public:
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param mg New multicast group
 	 * @param mg New multicast group
 	 */
 	 */
-	inline void multicastSubscribe(void *tPtr,const MulticastGroup &mg)
-	{
-		Mutex::Lock l(_myMulticastGroups_l);
-		if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) {
-			_myMulticastGroups.insert(std::upper_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg),mg);
-			Mutex::Lock l2(_memberships_l);
-			_announceMulticastGroups(tPtr,true);
-		}
-	}
+	void multicastSubscribe(void *tPtr,const MulticastGroup &mg);
 
 
 	/**
 	/**
 	 * Unsubscribe from a multicast group
 	 * Unsubscribe from a multicast group
 	 *
 	 *
 	 * @param mg Multicast group
 	 * @param mg Multicast group
 	 */
 	 */
-	inline void multicastUnsubscribe(const MulticastGroup &mg)
-	{
-		Mutex::Lock l(_myMulticastGroups_l);
-		std::vector<MulticastGroup>::iterator i(std::lower_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg));
-		if ( (i != _myMulticastGroups.end()) && (*i == mg) )
-			_myMulticastGroups.erase(i);
-	}
+	void multicastUnsubscribe(const MulticastGroup &mg);
 
 
 	/**
 	/**
 	 * Handle an inbound network config chunk
 	 * Handle an inbound network config chunk
@@ -208,7 +194,7 @@ public:
 	 * @param ptr Index of chunk and related fields in packet
 	 * @param ptr Index of chunk and related fields in packet
 	 * @return Update ID if update was fully assembled and accepted or 0 otherwise
 	 * @return Update ID if update was fully assembled and accepted or 0 otherwise
 	 */
 	 */
-	uint64_t handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
+	uint64_t handleConfigChunk(void *tPtr,uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
 
 
 	/**
 	/**
 	 * Set network configuration
 	 * Set network configuration
@@ -227,12 +213,12 @@ public:
 	/**
 	/**
 	 * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
 	 * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
 	 */
 	 */
-	inline void setAccessDenied() { _netconfFailure = NETCONF_FAILURE_ACCESS_DENIED; }
+	ZT_ALWAYS_INLINE void setAccessDenied() { _netconfFailure = NETCONF_FAILURE_ACCESS_DENIED; }
 
 
 	/**
 	/**
 	 * Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this
 	 * Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this
 	 */
 	 */
-	inline void setNotFound() { _netconfFailure = NETCONF_FAILURE_NOT_FOUND; }
+	ZT_ALWAYS_INLINE void setNotFound() { _netconfFailure = NETCONF_FAILURE_NOT_FOUND; }
 
 
 	/**
 	/**
 	 * Determine whether this peer is permitted to communicate on this network
 	 * Determine whether this peer is permitted to communicate on this network
@@ -245,7 +231,7 @@ public:
 	/**
 	/**
 	 * Do periodic cleanup and housekeeping tasks
 	 * Do periodic cleanup and housekeeping tasks
 	 */
 	 */
-	void doPeriodicTasks(void *tPtr,const int64_t now);
+	void doPeriodicTasks(void *tPtr,int64_t now);
 
 
 	/**
 	/**
 	 * Find the node on this network that has this MAC behind it (if any)
 	 * Find the node on this network that has this MAC behind it (if any)
@@ -253,18 +239,13 @@ public:
 	 * @param mac MAC address
 	 * @param mac MAC address
 	 * @return ZeroTier address of bridge to this MAC
 	 * @return ZeroTier address of bridge to this MAC
 	 */
 	 */
-	inline Address findBridgeTo(const MAC &mac) const
+	ZT_ALWAYS_INLINE Address findBridgeTo(const MAC &mac) const
 	{
 	{
 		Mutex::Lock _l(_remoteBridgeRoutes_l);
 		Mutex::Lock _l(_remoteBridgeRoutes_l);
 		const Address *const br = _remoteBridgeRoutes.get(mac);
 		const Address *const br = _remoteBridgeRoutes.get(mac);
 		return ((br) ? *br : Address());
 		return ((br) ? *br : Address());
 	}
 	}
 
 
-	/**
-	 * @return True if QoS is in effect for this network
-	 */
-	inline bool qosEnabled() { return false; }
-
 	/**
 	/**
 	 * Set a bridge route
 	 * Set a bridge route
 	 *
 	 *
@@ -280,7 +261,7 @@ public:
 	 * @param mg Multicast group
 	 * @param mg Multicast group
 	 * @param now Current time
 	 * @param now Current time
 	 */
 	 */
-	inline void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now)
+	ZT_ALWAYS_INLINE void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now)
 	{
 	{
 		Mutex::Lock l(_myMulticastGroups_l);
 		Mutex::Lock l(_myMulticastGroups_l);
 		_multicastGroupsBehindMe.set(mg,now);
 		_multicastGroupsBehindMe.set(mg,now);
@@ -289,51 +270,27 @@ public:
 	/**
 	/**
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 */
 	 */
-	inline Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfMembership &com)
-	{
-		if (com.networkId() != _id)
-			return Membership::ADD_REJECTED;
-		Mutex::Lock _l(_memberships_l);
-		return _memberships[com.issuedTo()].addCredential(RR,tPtr,_config,com);
-	}
+	Membership::AddCredentialResult addCredential(void *tPtr,const Identity &sourcePeerIdentity,const CertificateOfMembership &com);
 
 
 	/**
 	/**
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 */
 	 */
-	inline Membership::AddCredentialResult addCredential(void *tPtr,const Capability &cap)
-	{
-		if (cap.networkId() != _id)
-			return Membership::ADD_REJECTED;
-		Mutex::Lock _l(_memberships_l);
-		return _memberships[cap.issuedTo()].addCredential(RR,tPtr,_config,cap);
-	}
+	Membership::AddCredentialResult addCredential(void *tPtr,const Identity &sourcePeerIdentity,const Capability &cap);
 
 
 	/**
 	/**
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 */
 	 */
-	inline Membership::AddCredentialResult addCredential(void *tPtr,const Tag &tag)
-	{
-		if (tag.networkId() != _id)
-			return Membership::ADD_REJECTED;
-		Mutex::Lock _l(_memberships_l);
-		return _memberships[tag.issuedTo()].addCredential(RR,tPtr,_config,tag);
-	}
+	Membership::AddCredentialResult addCredential(void *tPtr,const Identity &sourcePeerIdentity,const Tag &tag);
 
 
 	/**
 	/**
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 */
 	 */
-	Membership::AddCredentialResult addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev);
+	Membership::AddCredentialResult addCredential(void *tPtr,const Identity &sourcePeerIdentity,const Revocation &rev);
 
 
 	/**
 	/**
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 */
 	 */
-	inline Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfOwnership &coo)
-	{
-		if (coo.networkId() != _id)
-			return Membership::ADD_REJECTED;
-		Mutex::Lock _l(_memberships_l);
-		return _memberships[coo.issuedTo()].addCredential(RR,tPtr,_config,coo);
-	}
+	Membership::AddCredentialResult addCredential(void *tPtr,const Identity &sourcePeerIdentity,const CertificateOfOwnership &coo);
 
 
 	/**
 	/**
 	 * Force push credentials (COM, etc.) to a peer now
 	 * Force push credentials (COM, etc.) to a peer now
@@ -342,11 +299,7 @@ public:
 	 * @param to Destination peer address
 	 * @param to Destination peer address
 	 * @param now Current time
 	 * @param now Current time
 	 */
 	 */
-	inline void pushCredentialsNow(void *tPtr,const Address &to,const int64_t now)
-	{
-		Mutex::Lock _l(_memberships_l);
-		_memberships[to].pushCredentials(RR,tPtr,now,to,_config);
-	}
+	void pushCredentialsNow(void *tPtr,const Address &to,int64_t now);
 
 
 	/**
 	/**
 	 * Push credentials if we haven't done so in a long time
 	 * Push credentials if we haven't done so in a long time
@@ -355,7 +308,7 @@ public:
 	 * @param to Destination peer address
 	 * @param to Destination peer address
 	 * @param now Current time
 	 * @param now Current time
 	 */
 	 */
-	inline void pushCredentialsIfNeeded(void *tPtr,const Address &to,const int64_t now)
+	ZT_ALWAYS_INLINE void pushCredentialsIfNeeded(void *tPtr,const Address &to,const int64_t now)
 	{
 	{
 		const int64_t tout = std::min(_config.credentialTimeMaxDelta,(int64_t)ZT_PEER_ACTIVITY_TIMEOUT);
 		const int64_t tout = std::min(_config.credentialTimeMaxDelta,(int64_t)ZT_PEER_ACTIVITY_TIMEOUT);
 		Mutex::Lock _l(_memberships_l);
 		Mutex::Lock _l(_memberships_l);
@@ -370,25 +323,14 @@ public:
 	 * This sets the network to completely remove itself on delete. This also prevents the
 	 * This sets the network to completely remove itself on delete. This also prevents the
 	 * call of the normal port shutdown event on delete.
 	 * call of the normal port shutdown event on delete.
 	 */
 	 */
-	inline void destroy()
-	{
-		_memberships_l.lock();
-		_config_l.lock();
-		_destroyed = true;
-		_config_l.unlock();
-		_memberships_l.unlock();
-	}
+	void destroy();
 
 
 	/**
 	/**
 	 * Get this network's config for export via the ZT core API
 	 * Get this network's config for export via the ZT core API
 	 *
 	 *
 	 * @param ec Buffer to fill with externally-visible network configuration
 	 * @param ec Buffer to fill with externally-visible network configuration
 	 */
 	 */
-	inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
-	{
-		Mutex::Lock _l(_config_l);
-		_externalConfig(ec);
-	}
+	void externalConfig(ZT_VirtualNetworkConfig *ec) const;
 
 
 	/**
 	/**
 	 * Iterate through memberships
 	 * Iterate through memberships
@@ -396,7 +338,7 @@ public:
 	 * @param f Function of (const Address,const Membership)
 	 * @param f Function of (const Address,const Membership)
 	 */
 	 */
 	template<typename F>
 	template<typename F>
-	inline void eachMember(F f)
+	ZT_ALWAYS_INLINE void eachMember(F f)
 	{
 	{
 		Mutex::Lock ml(_memberships_l);
 		Mutex::Lock ml(_memberships_l);
 		Hashtable<Address,Membership>::Iterator i(_memberships);
 		Hashtable<Address,Membership>::Iterator i(_memberships);
@@ -411,13 +353,12 @@ public:
 	/**
 	/**
 	 * @return Externally usable pointer-to-pointer exported via the core API
 	 * @return Externally usable pointer-to-pointer exported via the core API
 	 */
 	 */
-	inline void **userPtr() { return &_uPtr; }
+	ZT_ALWAYS_INLINE void **userPtr() { return &_uPtr; }
 
 
 private:
 private:
 	void _requestConfiguration(void *tPtr);
 	void _requestConfiguration(void *tPtr);
 	ZT_VirtualNetworkStatus _status() const;
 	ZT_VirtualNetworkStatus _status() const;
 	void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
 	void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
-	bool _gate(const SharedPtr<Peer> &peer);
 	void _announceMulticastGroups(void *tPtr,bool force);
 	void _announceMulticastGroups(void *tPtr,bool force);
 	void _announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
 	void _announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
 	std::vector<MulticastGroup> _allMulticastGroups() const;
 	std::vector<MulticastGroup> _allMulticastGroups() const;
@@ -433,11 +374,11 @@ private:
 	Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
 	Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
 
 
 	NetworkConfig _config;
 	NetworkConfig _config;
-	uint64_t _lastConfigUpdate;
+	volatile uint64_t _lastConfigUpdate;
 
 
 	struct _IncomingConfigChunk
 	struct _IncomingConfigChunk
 	{
 	{
-		inline _IncomingConfigChunk() : ts(0),updateId(0),haveChunks(0),haveBytes(0),data() {}
+		ZT_ALWAYS_INLINE _IncomingConfigChunk() : ts(0),updateId(0),haveChunks(0),haveBytes(0),data() {}
 		uint64_t ts;
 		uint64_t ts;
 		uint64_t updateId;
 		uint64_t updateId;
 		uint64_t haveChunkIds[ZT_NETWORK_MAX_UPDATE_CHUNKS];
 		uint64_t haveChunkIds[ZT_NETWORK_MAX_UPDATE_CHUNKS];

+ 1 - 5
node/Node.cpp

@@ -33,10 +33,6 @@
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
-/****************************************************************************/
-/* Public Node interface (C++, exposed via CAPI bindings)                   */
-/****************************************************************************/
-
 Node::Node(void *uPtr,void *tPtr,const struct ZT_Node_Callbacks *callbacks,int64_t now) :
 Node::Node(void *uPtr,void *tPtr,const struct ZT_Node_Callbacks *callbacks,int64_t now) :
 	_RR(this),
 	_RR(this),
 	RR(&_RR),
 	RR(&_RR),
@@ -437,7 +433,7 @@ ZT_PeerList *Node::peers() const
 		p->address = (*pi)->address().toInt();
 		p->address = (*pi)->address().toInt();
 		identities[pl->peerCount] = (*pi)->identity(); // need to make a copy in case peer gets deleted
 		identities[pl->peerCount] = (*pi)->identity(); // need to make a copy in case peer gets deleted
 		p->identity = &identities[pl->peerCount];
 		p->identity = &identities[pl->peerCount];
-		(*pi)->identity().hash(p->identityHash,false);
+		memcpy(p->identityHash,(*pi)->identity().hash(),sizeof(p->identityHash));
 		if ((*pi)->remoteVersionKnown()) {
 		if ((*pi)->remoteVersionKnown()) {
 			p->versionMajor = (int)(*pi)->remoteVersionMajor();
 			p->versionMajor = (int)(*pi)->remoteVersionMajor();
 			p->versionMinor = (int)(*pi)->remoteVersionMinor();
 			p->versionMinor = (int)(*pi)->remoteVersionMinor();

+ 1 - 1
node/Node.hpp

@@ -264,7 +264,7 @@ public:
 	 * @param remoteAddress Remote address
 	 * @param remoteAddress Remote address
 	 * @return True if path should be used
 	 * @return True if path should be used
 	 */
 	 */
-	bool shouldUsePathForZeroTierTraffic(void *tPtr,const Identity &id,const int64_t localSocket,const InetAddress &remoteAddress);
+	bool shouldUsePathForZeroTierTraffic(void *tPtr,const Identity &id,int64_t localSocket,const InetAddress &remoteAddress);
 
 
 	/**
 	/**
 	 * Query callback for a physical address for a peer
 	 * Query callback for a physical address for a peer

+ 4 - 7
node/OS.hpp

@@ -28,18 +28,15 @@
 //
 //
 
 
 #ifndef __GCC__
 #ifndef __GCC__
-#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__INTEL_COMPILER) || defined(__clang__)
 #define __GCC__
 #define __GCC__
 #endif
 #endif
 #endif
 #endif
 
 
-#if !defined(__GCC__) && !defined (__clang__) && !defined(__INTEL_COMPILER)
-#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
+#ifdef _MSC_VER
 #pragma warning(disable : 4290)
 #pragma warning(disable : 4290)
 #pragma warning(disable : 4996)
 #pragma warning(disable : 4996)
 #pragma warning(disable : 4101)
 #pragma warning(disable : 4101)
-#else
-#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
 #endif
 #endif
 
 
 #if defined(_WIN32) || defined(_WIN64)
 #if defined(_WIN32) || defined(_WIN64)
@@ -98,8 +95,8 @@
 
 
 // Avoid unaligned type casts on all but x86/x64 architecture.
 // Avoid unaligned type casts on all but x86/x64 architecture.
 #if (!(defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64) || defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || defined(_X86_) || defined(__I86__) || defined(__INTEL__) || defined(__386)))
 #if (!(defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64) || defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || defined(_X86_) || defined(__I86__) || defined(__INTEL__) || defined(__386)))
-#ifndef ZT_NO_TYPE_PUNNING
-#define ZT_NO_TYPE_PUNNING
+#ifndef ZT_NO_UNALIGNED_ACCESS
+#define ZT_NO_UNALIGNED_ACCESS
 #endif
 #endif
 #endif
 #endif
 
 

+ 1 - 1
node/Packet.cpp

@@ -66,7 +66,7 @@ bool Packet::dearmor(const void *key)
 		s20.crypt12(ZERO_KEY,macKey,sizeof(macKey));
 		s20.crypt12(ZERO_KEY,macKey,sizeof(macKey));
 		uint64_t mac[2];
 		uint64_t mac[2];
 		poly1305(mac,payload,payloadLen,macKey);
 		poly1305(mac,payload,payloadLen,macKey);
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 		if (!Utils::secureEq(mac,data + ZT_PACKET_IDX_MAC,8))
 		if (!Utils::secureEq(mac,data + ZT_PACKET_IDX_MAC,8))
 			return false;
 			return false;
 #else
 #else

+ 8 - 1
node/Path.hpp

@@ -111,7 +111,14 @@ public:
 	 *
 	 *
 	 * @param now Current time
 	 * @param now Current time
 	 */
 	 */
-	ZT_ALWAYS_INLINE bool alive(const int64_t now) const { return ((now - _lastIn) < ZT_PATH_ACTIVITY_TIMEOUT); }
+	ZT_ALWAYS_INLINE bool alive(const int64_t now) const { return ((now - _lastIn) < ZT_PATH_ALIVE_TIMEOUT); }
+
+	/**
+	 * Check if path is considered active
+	 *
+	 * @param now Current time
+	 */
+	ZT_ALWAYS_INLINE bool active(const int64_t now) const { return ((now - _lastIn) < ZT_PATH_ACTIVITY_TIMEOUT); }
 
 
 	/**
 	/**
 	 * @return Physical address
 	 * @return Physical address

+ 3 - 5
node/Peer.cpp

@@ -92,9 +92,7 @@ void Peer::received(
 				if ((_paths[i]->address().ss_family == path->address().ss_family) &&
 				if ((_paths[i]->address().ss_family == path->address().ss_family) &&
 				    (_paths[i]->localSocket() == path->localSocket()) && // TODO: should be localInterface when multipath is integrated
 				    (_paths[i]->localSocket() == path->localSocket()) && // TODO: should be localInterface when multipath is integrated
 				    (_paths[i]->address().ipsEqual2(path->address()))) {
 				    (_paths[i]->address().ipsEqual2(path->address()))) {
-					// If this is another path to the same place, swap it out as the
-					// one we just received from may replace an old one but don't
-					// learn it as a new path.
+					// Replace older path if everything is the same except the port number.
 					_paths[i] = path;
 					_paths[i] = path;
 					goto path_check_done;
 					goto path_check_done;
 				} else {
 				} else {
@@ -130,7 +128,7 @@ path_check_done:
 		_lastAttemptedP2PInit = now;
 		_lastAttemptedP2PInit = now;
 
 
 		InetAddress addr;
 		InetAddress addr;
-		if (_bootstrap.type() == Endpoint::INETADDR)
+		if ((_bootstrap.type() == Endpoint::INETADDR_V4)||(_bootstrap.type() == Endpoint::INETADDR_V6))
 			sendHELLO(tPtr,-1,_bootstrap.inetAddr(),now);
 			sendHELLO(tPtr,-1,_bootstrap.inetAddr(),now);
 		if (RR->node->externalPathLookup(tPtr,_id,-1,addr)) {
 		if (RR->node->externalPathLookup(tPtr,_id,-1,addr)) {
 			if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id,-1,addr))
 			if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id,-1,addr))
@@ -256,7 +254,7 @@ void Peer::ping(void *tPtr,int64_t now,const bool pingAllAddressTypes)
 		return;
 		return;
 	}
 	}
 
 
-	if (_bootstrap.type() == Endpoint::INETADDR)
+	if ((_bootstrap.type() == Endpoint::INETADDR_V4)||(_bootstrap.type() == Endpoint::INETADDR_V6))
 		sendHELLO(tPtr,-1,_bootstrap.inetAddr(),now);
 		sendHELLO(tPtr,-1,_bootstrap.inetAddr(),now);
 
 
 	SharedPtr<Peer> r(RR->topology->root());
 	SharedPtr<Peer> r(RR->topology->root());

+ 2 - 2
node/Poly1305.cpp

@@ -70,7 +70,7 @@ typedef struct poly1305_state_internal_t {
   unsigned char final;
   unsigned char final;
 } poly1305_state_internal_t;
 } poly1305_state_internal_t;
 
 
-#if defined(ZT_NO_TYPE_PUNNING) || (__BYTE_ORDER != __LITTLE_ENDIAN)
+#if defined(ZT_NO_UNALIGNED_ACCESS) || (__BYTE_ORDER != __LITTLE_ENDIAN)
 static inline unsigned long long U8TO64(const unsigned char *p)
 static inline unsigned long long U8TO64(const unsigned char *p)
 {
 {
   return
   return
@@ -87,7 +87,7 @@ static inline unsigned long long U8TO64(const unsigned char *p)
 #define U8TO64(p) (*reinterpret_cast<const unsigned long long *>(p))
 #define U8TO64(p) (*reinterpret_cast<const unsigned long long *>(p))
 #endif
 #endif
 
 
-#if defined(ZT_NO_TYPE_PUNNING) || (__BYTE_ORDER != __LITTLE_ENDIAN)
+#if defined(ZT_NO_UNALIGNED_ACCESS) || (__BYTE_ORDER != __LITTLE_ENDIAN)
 static inline void U64TO8(unsigned char *p, unsigned long long v)
 static inline void U64TO8(unsigned char *p, unsigned long long v)
 {
 {
   p[0] = (v      ) & 0xff;
   p[0] = (v      ) & 0xff;

+ 6 - 6
node/Revocation.hpp

@@ -44,7 +44,7 @@ class Revocation : public Credential
 	friend class Credential;
 	friend class Credential;
 
 
 public:
 public:
-	static ZT_ALWAYS_INLINE Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_REVOCATION; }
+	static ZT_ALWAYS_INLINE ZT_CredentialType credentialType() { return ZT_CREDENTIAL_TYPE_REVOCATION; }
 
 
 	ZT_ALWAYS_INLINE Revocation() :
 	ZT_ALWAYS_INLINE Revocation() :
 		_id(0),
 		_id(0),
@@ -54,7 +54,7 @@ public:
 		_flags(0),
 		_flags(0),
 		_target(),
 		_target(),
 		_signedBy(),
 		_signedBy(),
-		_type(Credential::CREDENTIAL_TYPE_NULL),
+		_type(ZT_CREDENTIAL_TYPE_NULL),
 		_signatureLength(0)
 		_signatureLength(0)
 	{
 	{
 	}
 	}
@@ -68,7 +68,7 @@ public:
 	 * @param tgt Target node whose credential(s) are being revoked
 	 * @param tgt Target node whose credential(s) are being revoked
 	 * @param ct Credential type being revoked
 	 * @param ct Credential type being revoked
 	 */
 	 */
-	ZT_ALWAYS_INLINE Revocation(const uint32_t i,const uint64_t nwid,const uint32_t cid,const uint64_t thr,const uint64_t fl,const Address &tgt,const Credential::Type ct) :
+	ZT_ALWAYS_INLINE Revocation(const uint32_t i,const uint64_t nwid,const uint32_t cid,const uint64_t thr,const uint64_t fl,const Address &tgt,const ZT_CredentialType ct) :
 		_id(i),
 		_id(i),
 		_credentialId(cid),
 		_credentialId(cid),
 		_networkId(nwid),
 		_networkId(nwid),
@@ -87,7 +87,7 @@ public:
 	ZT_ALWAYS_INLINE int64_t threshold() const { return _threshold; }
 	ZT_ALWAYS_INLINE int64_t threshold() const { return _threshold; }
 	ZT_ALWAYS_INLINE const Address &target() const { return _target; }
 	ZT_ALWAYS_INLINE const Address &target() const { return _target; }
 	ZT_ALWAYS_INLINE const Address &signer() const { return _signedBy; }
 	ZT_ALWAYS_INLINE const Address &signer() const { return _signedBy; }
-	ZT_ALWAYS_INLINE Credential::Type type() const { return _type; }
+	ZT_ALWAYS_INLINE ZT_CredentialType typeBeingRevoked() const { return _type; }
 	ZT_ALWAYS_INLINE const uint8_t *signature() const { return _signature; }
 	ZT_ALWAYS_INLINE const uint8_t *signature() const { return _signature; }
 	ZT_ALWAYS_INLINE unsigned int signatureLength() const { return _signatureLength; }
 	ZT_ALWAYS_INLINE unsigned int signatureLength() const { return _signatureLength; }
 	ZT_ALWAYS_INLINE bool fastPropagate() const { return ((_flags & ZT_REVOCATION_FLAG_FAST_PROPAGATE) != 0); }
 	ZT_ALWAYS_INLINE bool fastPropagate() const { return ((_flags & ZT_REVOCATION_FLAG_FAST_PROPAGATE) != 0); }
@@ -160,7 +160,7 @@ public:
 		_flags = b.template at<uint64_t>(p); p += 8;
 		_flags = b.template at<uint64_t>(p); p += 8;
 		_target.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
 		_target.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
 		_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
 		_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
-		_type = (Credential::Type)b[p++];
+		_type = (ZT_CredentialType)b[p++];
 
 
 		if (b[p++] == 1) {
 		if (b[p++] == 1) {
 			_signatureLength = b.template at<uint16_t>(p);
 			_signatureLength = b.template at<uint16_t>(p);
@@ -186,7 +186,7 @@ private:
 	uint64_t _flags;
 	uint64_t _flags;
 	Address _target;
 	Address _target;
 	Address _signedBy;
 	Address _signedBy;
-	Credential::Type _type;
+	ZT_CredentialType _type;
 	unsigned int _signatureLength;
 	unsigned int _signatureLength;
 	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
 	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
 };
 };

+ 0 - 58
node/RingBuffer.hpp

@@ -235,64 +235,6 @@ public:
 		}
 		}
 		return curr_cnt ? subtotal / (float)curr_cnt : 0;
 		return curr_cnt ? subtotal / (float)curr_cnt : 0;
 	}
 	}
-
-	/**
-	 * @return The sample standard deviation of element values
-	 */
-	inline float stddev() { return sqrt(variance()); }
-
-	/**
-	 * @return The variance of element values
-	 */
-	inline float variance()
-	{
-		size_t iterator = begin;
-		float cached_mean = mean();
-		size_t curr_cnt = count();
-		T sum_of_squared_deviations = 0;
-		for (size_t i=0; i<curr_cnt; i++) {
-			iterator = (iterator + S - 1) % curr_cnt;
-			float deviation = (buf[i] - cached_mean);
-			sum_of_squared_deviations += (T)(deviation*deviation);
-		}
-		float variance = (float)sum_of_squared_deviations / (float)(S - 1);
-		return variance;
-	}
-
-	/**
-	 * @return The number of elements of zero value
-	 */
-	inline size_t zeroCount()
-	{
-		size_t iterator = begin;
-		size_t zeros = 0;
-		size_t curr_cnt = count();
-		for (size_t i=0; i<curr_cnt; i++) {
-			iterator = (iterator + S - 1) % curr_cnt;
-			if (*(buf + iterator) == 0) {
-				zeros++;
-			}
-		}
-		return zeros;
-	}
-
-	/**
-	 * @param value Value to match against in buffer
-	 * @return The number of values held in the ring buffer which match a given value
-	 */
-	inline size_t countValue(T value)
-	{
-		size_t iterator = begin;
-		size_t cnt = 0;
-		size_t curr_cnt = count();
-		for (size_t i=0; i<curr_cnt; i++) {
-			iterator = (iterator + S - 1) % curr_cnt;
-			if (*(buf + iterator) == value) {
-				cnt++;
-			}
-		}
-		return cnt;
-	}
 };
 };
 
 
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 1 - 1
node/SHA512.cpp

@@ -218,7 +218,7 @@ void HMACSHA384(const uint8_t key[32],const void *msg,const unsigned int msglen,
 	uint64_t kInPadded[16]; // input padded key
 	uint64_t kInPadded[16]; // input padded key
 	uint64_t outer[22]; // output padded key | H(input padded key | msg)
 	uint64_t outer[22]; // output padded key | H(input padded key | msg)
 
 
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 	for(int i=0;i<32;++i) ((uint8_t *)kInPadded)[i] = key[i] ^ 0x36;
 	for(int i=0;i<32;++i) ((uint8_t *)kInPadded)[i] = key[i] ^ 0x36;
 	for(int i=4;i<16;++i) kInPadded[i] = 0x3636363636363636ULL;
 	for(int i=4;i<16;++i) kInPadded[i] = 0x3636363636363636ULL;
 	for(int i=0;i<32;++i) ((uint8_t *)outer)[i] = key[i] ^ 0x5c;
 	for(int i=0;i<32;++i) ((uint8_t *)outer)[i] = key[i] ^ 0x5c;

+ 2 - 8
node/SHA512.hpp

@@ -32,12 +32,6 @@
 
 
 #define ZT_HMACSHA384_LEN 48
 #define ZT_HMACSHA384_LEN 48
 
 
-#define ZT_PROTO_KBKDF_LABEL_KEY_USE_HMAC_SHA_384 'H'
-#define ZT_PROTO_KBKDF_LABEL_KEY_USE_AES_GMAC_SIV_K1 '1'
-#define ZT_PROTO_KBKDF_LABEL_KEY_USE_AES_GMAC_SIV_K2 '2'
-#define ZT_PROTO_KBKDF_LABEL_KEY_USE_AES_GMAC_SIV_K3 '3'
-#define ZT_PROTO_KBKDF_LABEL_KEY_USE_AES_GMAC_SIV_K4 '4'
-
 namespace ZeroTier {
 namespace ZeroTier {
 
 
 #ifdef __APPLE__
 #ifdef __APPLE__
@@ -108,7 +102,7 @@ void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,u
  * @param msglen Length of message
  * @param msglen Length of message
  * @param mac Buffer to fill with result
  * @param mac Buffer to fill with result
  */
  */
-void HMACSHA384(const uint8_t key[32],const void *msg,const unsigned int msglen,uint8_t mac[48]);
+void HMACSHA384(const uint8_t key[32],const void *msg,unsigned int msglen,uint8_t mac[48]);
 
 
 /**
 /**
  * Compute KBKDF (key-based key derivation function) using HMAC-SHA-384 as a PRF
  * Compute KBKDF (key-based key derivation function) using HMAC-SHA-384 as a PRF
@@ -119,7 +113,7 @@ void HMACSHA384(const uint8_t key[32],const void *msg,const unsigned int msglen,
  * @param iter Key iteration for generation of multiple keys for the same label/context
  * @param iter Key iteration for generation of multiple keys for the same label/context
  * @param out Output to receive derived key
  * @param out Output to receive derived key
  */
  */
-void KBKDFHMACSHA384(const uint8_t key[32],const char label,const char context,const uint32_t iter,uint8_t out[32]);
+void KBKDFHMACSHA384(const uint8_t key[32],char label,char context,uint32_t iter,uint8_t out[32]);
 
 
 } // namespace ZeroTier
 } // namespace ZeroTier
 
 

+ 2 - 2
node/Salsa20.cpp

@@ -19,7 +19,7 @@
 
 
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 
 
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 // Slower version that does not use type punning
 // Slower version that does not use type punning
 #define U8TO32_LITTLE(p) ( ((uint32_t)(p)[0]) | ((uint32_t)(p)[1] << 8) | ((uint32_t)(p)[2] << 16) | ((uint32_t)(p)[3] << 24) )
 #define U8TO32_LITTLE(p) ( ((uint32_t)(p)[0]) | ((uint32_t)(p)[1] << 8) | ((uint32_t)(p)[2] << 16) | ((uint32_t)(p)[3] << 24) )
 static inline void U32TO8_LITTLE(uint8_t *const c,const uint32_t v) { c[0] = (uint8_t)v; c[1] = (uint8_t)(v >> 8); c[2] = (uint8_t)(v >> 16); c[3] = (uint8_t)(v >> 24); }
 static inline void U32TO8_LITTLE(uint8_t *const c,const uint32_t v) { c[0] = (uint8_t)v; c[1] = (uint8_t)(v >> 8); c[2] = (uint8_t)(v >> 16); c[3] = (uint8_t)(v >> 24); }
@@ -27,7 +27,7 @@ static inline void U32TO8_LITTLE(uint8_t *const c,const uint32_t v) { c[0] = (ui
 // Fast version that just does 32-bit load/store
 // Fast version that just does 32-bit load/store
 #define U8TO32_LITTLE(p) (*((const uint32_t *)((const void *)(p))))
 #define U8TO32_LITTLE(p) (*((const uint32_t *)((const void *)(p))))
 #define U32TO8_LITTLE(c,v) *((uint32_t *)((void *)(c))) = (v)
 #define U32TO8_LITTLE(c,v) *((uint32_t *)((void *)(c))) = (v)
-#endif // ZT_NO_TYPE_PUNNING
+#endif // ZT_NO_UNALIGNED_ACCESS
 
 
 #else // __BYTE_ORDER == __BIG_ENDIAN (we don't support anything else... does MIDDLE_ENDIAN even still exist?)
 #else // __BYTE_ORDER == __BIG_ENDIAN (we don't support anything else... does MIDDLE_ENDIAN even still exist?)
 
 

+ 8 - 41
node/SelfAwareness.cpp

@@ -25,7 +25,7 @@
 #include "Trace.hpp"
 #include "Trace.hpp"
 
 
 // Entry timeout -- make it fairly long since this is just to prevent stale buildup
 // Entry timeout -- make it fairly long since this is just to prevent stale buildup
-#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 600000
+#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 300000
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
@@ -53,11 +53,7 @@ SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
 {
 {
 }
 }
 
 
-SelfAwareness::~SelfAwareness()
-{
-}
-
-void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now)
+void SelfAwareness::iam(void *tPtr,const Identity &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now)
 {
 {
 	const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
 	const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
 
 
@@ -65,12 +61,10 @@ void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receive
 		return;
 		return;
 
 
 	Mutex::Lock l(_phy_l);
 	Mutex::Lock l(_phy_l);
-	PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalSocket,reporterPhysicalAddress,scope)];
+	PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter.address(),receivedOnLocalSocket,reporterPhysicalAddress,scope)];
 
 
 	if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
 	if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
 		// Changes to external surface reported by trusted peers causes path reset in this scope
 		// Changes to external surface reported by trusted peers causes path reset in this scope
-		RR->t->resettingPathsInScope(tPtr,reporter,reporterPhysicalAddress,myPhysicalAddress,scope);
-
 		entry.mySurface = myPhysicalAddress;
 		entry.mySurface = myPhysicalAddress;
 		entry.ts = now;
 		entry.ts = now;
 		entry.trusted = trusted;
 		entry.trusted = trusted;
@@ -80,10 +74,10 @@ void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receive
 		// Don't use 'entry' after this since hash table gets modified.
 		// Don't use 'entry' after this since hash table gets modified.
 		{
 		{
 			Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
 			Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
-			PhySurfaceKey *k = (PhySurfaceKey *)0;
-			PhySurfaceEntry *e = (PhySurfaceEntry *)0;
+			PhySurfaceKey *k = nullptr;
+			PhySurfaceEntry *e = nullptr;
 			while (i.next(k,e)) {
 			while (i.next(k,e)) {
-				if ((k->reporterPhysicalAddress != reporterPhysicalAddress)&&(k->scope == scope))
+				if ((k->scope == scope)&&(k->reporterPhysicalAddress != reporterPhysicalAddress))
 					_phy.erase(*k);
 					_phy.erase(*k);
 			}
 			}
 		}
 		}
@@ -91,6 +85,8 @@ void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receive
 		// Reset all paths within this scope and address family
 		// Reset all paths within this scope and address family
 		_ResetWithinScope rset(tPtr,now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
 		_ResetWithinScope rset(tPtr,now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
 		RR->topology->eachPeer<_ResetWithinScope &>(rset);
 		RR->topology->eachPeer<_ResetWithinScope &>(rset);
+
+		RR->t->resettingPathsInScope(tPtr,reporter,reporterPhysicalAddress,entry.mySurface,myPhysicalAddress,scope);
 	} else {
 	} else {
 		// Otherwise just update DB to use to determine external surface info
 		// Otherwise just update DB to use to determine external surface info
 		entry.mySurface = myPhysicalAddress;
 		entry.mySurface = myPhysicalAddress;
@@ -111,35 +107,6 @@ void SelfAwareness::clean(int64_t now)
 	}
 	}
 }
 }
 
 
-bool SelfAwareness::symmetricNat(const int64_t now) const
-{
-	Hashtable< InetAddress,std::pair< std::set<int>,std::set<int64_t> > > ipToPortsAndLocalSockets(16);
-
-	{
-		Mutex::Lock l(_phy_l);
-		Hashtable<PhySurfaceKey,PhySurfaceEntry>::Iterator i(const_cast<SelfAwareness *>(this)->_phy);
-		PhySurfaceKey *k = nullptr;
-		PhySurfaceEntry *e = nullptr;
-		while (i.next(k,e)) {
-			if ((now - e->ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) {
-				std::pair< std::set<int>,std::set<int64_t> > &ii = ipToPortsAndLocalSockets[e->mySurface.ipOnly()];
-				ii.first.insert(e->mySurface.port());
-				if (k->receivedOnLocalSocket != -1)
-					ii.second.insert(k->receivedOnLocalSocket);
-			}
-		}
-	}
-
-	Hashtable< InetAddress,std::pair< std::set<int>,std::set<int64_t> > >::Iterator i(ipToPortsAndLocalSockets);
-	InetAddress *k = nullptr;
-	std::pair< std::set<int>,std::set<int64_t> > *v = nullptr;
-	while (i.next(k,v)) {
-		if (v->first.size() > v->second.size()) // more external ports than local sockets for a given external IP
-			return true;
-	}
-	return false;
-}
-
 std::multimap<unsigned long,InetAddress> SelfAwareness::externalAddresses(const int64_t now) const
 std::multimap<unsigned long,InetAddress> SelfAwareness::externalAddresses(const int64_t now) const
 {
 {
 	std::multimap<unsigned long,InetAddress> r;
 	std::multimap<unsigned long,InetAddress> r;

+ 3 - 11
node/SelfAwareness.hpp

@@ -24,6 +24,7 @@
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
+class Identity;
 class RuntimeEnvironment;
 class RuntimeEnvironment;
 
 
 /**
 /**
@@ -35,19 +36,18 @@ class SelfAwareness
 {
 {
 public:
 public:
 	explicit SelfAwareness(const RuntimeEnvironment *renv);
 	explicit SelfAwareness(const RuntimeEnvironment *renv);
-	~SelfAwareness();
 
 
 	/**
 	/**
 	 * Called when a remote peer informs us of our external network address
 	 * Called when a remote peer informs us of our external network address
 	 *
 	 *
-	 * @param reporter ZeroTier address of reporting peer
+	 * @param reporter Identity of reporting peer
 	 * @param receivedOnLocalAddress Local address on which report was received
 	 * @param receivedOnLocalAddress Local address on which report was received
 	 * @param reporterPhysicalAddress Physical address that reporting peer seems to have
 	 * @param reporterPhysicalAddress Physical address that reporting peer seems to have
 	 * @param myPhysicalAddress Physical address that peer says we have
 	 * @param myPhysicalAddress Physical address that peer says we have
 	 * @param trusted True if this peer is trusted as an authority to inform us of external address changes
 	 * @param trusted True if this peer is trusted as an authority to inform us of external address changes
 	 * @param now Current time
 	 * @param now Current time
 	 */
 	 */
-	void iam(void *tPtr,const Address &reporter,int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now);
+	void iam(void *tPtr,const Identity &reporter,int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now);
 
 
 	/**
 	/**
 	 * Clean up database periodically
 	 * Clean up database periodically
@@ -56,14 +56,6 @@ public:
 	 */
 	 */
 	void clean(int64_t now);
 	void clean(int64_t now);
 
 
-	/**
-	 * Check whether this node appears to be behind a symmetric NAT
-	 *
-	 * @param now Current time
-	 * @return True if it looks like we're behind a symmetric NAT
-	 */
-	bool symmetricNat(int64_t now) const;
-
 	/**
 	/**
 	 * Get external address consensus, which is the statistical "mode" of external addresses.
 	 * Get external address consensus, which is the statistical "mode" of external addresses.
 	 *
 	 *

+ 7 - 7
node/Switch.cpp

@@ -211,7 +211,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 	bool fromBridged;
 	bool fromBridged;
 	if ((fromBridged = (from != network->mac()))) {
 	if ((fromBridged = (from != network->mac()))) {
 		if (!network->config().permitsBridging(RR->identity.address())) {
 		if (!network->config().permitsBridging(RR->identity.address())) {
-			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"not a bridge");
+			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,ZT_TRACE_FRAME_DROP_REASON_BRIDGING_NOT_ALLOWED_LOCAL);
 			return;
 			return;
 		}
 		}
 	}
 	}
@@ -235,7 +235,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 				multicastGroup = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
 				multicastGroup = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
 			} else if (!network->config().enableBroadcast()) {
 			} else if (!network->config().enableBroadcast()) {
 				// Don't transmit broadcasts if this network doesn't want them
 				// Don't transmit broadcasts if this network doesn't want them
-				RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"broadcast disabled");
+				RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,ZT_TRACE_FRAME_DROP_REASON_BROADCAST_DISABLED);
 				return;
 				return;
 			}
 			}
 		} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) {
 		} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) {
@@ -323,7 +323,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 
 
 		// Check this after NDP emulation, since that has to be allowed in exactly this case
 		// Check this after NDP emulation, since that has to be allowed in exactly this case
 		if (network->config().multicastLimit == 0) {
 		if (network->config().multicastLimit == 0) {
-			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"multicast disabled");
+			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,ZT_TRACE_FRAME_DROP_REASON_MULTICAST_DISABLED);
 			return;
 			return;
 		}
 		}
 
 
@@ -336,7 +336,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 
 
 		// First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates.
 		// First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates.
 		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) {
 		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) {
-			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
+			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED);
 			return;
 			return;
 		}
 		}
 
 
@@ -365,7 +365,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 		SharedPtr<Peer> toPeer(RR->topology->get(tPtr,toZT));
 		SharedPtr<Peer> toPeer(RR->topology->get(tPtr,toZT));
 
 
 		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) {
 		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) {
-			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
+			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED);
 			return;
 			return;
 		}
 		}
 
 
@@ -392,7 +392,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 		// for each ZT destination are also done below. This is the same rationale
 		// for each ZT destination are also done below. This is the same rationale
 		// and design as for multicast.
 		// and design as for multicast.
 		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) {
 		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) {
-			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
+			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED);
 			return;
 			return;
 		}
 		}
 
 
@@ -442,7 +442,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 				outp.append((uint16_t)etherType);
 				outp.append((uint16_t)etherType);
 				outp.append(data,len);
 				outp.append(data,len);
 			} else {
 			} else {
-				RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked (bridge replication)");
+				RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,ZT_TRACE_FRAME_DROP_REASON_FILTER_BLOCKED_AT_BRIDGE_REPLICATION);
 			}
 			}
 		}
 		}
 	}
 	}

+ 1 - 1
node/Tag.hpp

@@ -52,7 +52,7 @@ class Tag : public Credential
 	friend class Credential;
 	friend class Credential;
 
 
 public:
 public:
-	static ZT_ALWAYS_INLINE Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_TAG; }
+	static ZT_ALWAYS_INLINE ZT_CredentialType credentialType() { return ZT_CREDENTIAL_TYPE_TAG; }
 
 
 	ZT_ALWAYS_INLINE Tag() :
 	ZT_ALWAYS_INLINE Tag() :
 		_id(0),
 		_id(0),

+ 282 - 0
node/Trace.cpp

@@ -0,0 +1,282 @@
+/*
+ * Copyright (c)2013-2020 ZeroTier, Inc.
+ *
+ * Use of this software is governed by the Business Source License included
+ * in the LICENSE.TXT file in the project's root directory.
+ *
+ * Change Date: 2024-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by version 2.0 of the Apache License.
+ */
+/****/
+
+#include "Trace.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Node.hpp"
+#include "Peer.hpp"
+
+// Macro to avoid calling hton() on values known at compile time.
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define CONST_TO_BE_UINT16(x) ((uint16_t)((uint16_t)((uint16_t)(x) << 8U) | (uint16_t)((uint16_t)(x) >> 8U)))
+#else
+#define CONST_TO_BE_UINT16(x) ((uint16_t)(x))
+#endif
+
+namespace ZeroTier {
+
+Trace::Trace(const RuntimeEnvironment *renv) :
+	RR(renv),
+	_vl1(false),
+	_vl2(false),
+	_vl2Filter(false),
+	_vl2Multicast(false),
+	_eventBufSize(0)
+{
+}
+
+void Trace::_resettingPathsInScope(
+	void *const tPtr,
+	const Identity &reporter,
+	const InetAddress &from,
+	const InetAddress &oldExternal,
+	const InetAddress &newExternal,
+	const InetAddress::IpScope scope)
+{
+	ZT_TraceEvent_VL1_RESETTING_PATHS_IN_SCOPE ev;
+	ev.evSize = CONST_TO_BE_UINT16(sizeof(ev));
+	ev.evType = CONST_TO_BE_UINT16(ZT_TRACE_VL1_RESETTING_PATHS_IN_SCOPE);
+	from.forTrace(ev.from);
+	oldExternal.forTrace(ev.oldExternal);
+	newExternal.forTrace(ev.newExternal);
+	ev.scope = (uint8_t)scope;
+
+	RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev);
+}
+
+void Trace::_tryingNewPath(
+	void *const tPtr,
+	const Identity &trying,
+	const InetAddress &physicalAddress,
+	const InetAddress &triggerAddress,
+	uint64_t triggeringPacketId,
+	uint8_t triggeringPacketVerb,
+	uint64_t triggeredByAddress,
+	const uint8_t *triggeredByIdentityHash,
+	ZT_TraceTryingNewPathReason reason)
+{
+	ZT_TraceEvent_VL1_TRYING_NEW_PATH ev;
+	ev.evSize = CONST_TO_BE_UINT16(sizeof(ev));
+	ev.evType = CONST_TO_BE_UINT16(ZT_TRACE_VL1_TRYING_NEW_PATH);
+	ev.address = Utils::hton(trying.address().toInt());
+	memcpy(ev.identityHash,trying.hash(),48);
+	physicalAddress.forTrace(ev.physicalAddress);
+	triggerAddress.forTrace(ev.triggerAddress);
+	ev.triggeringPacketId = Utils::hton(triggeringPacketId);
+	ev.triggeringPacketVerb = triggeringPacketVerb;
+	ev.triggeredByAddress = Utils::hton(triggeredByAddress);
+	if (triggeredByIdentityHash)
+		memcpy(ev.triggeredByIdentityHash,triggeredByIdentityHash,48);
+	else memset(ev.triggeredByIdentityHash,0,48);
+	ev.reason = (uint8_t)reason;
+
+	RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev);
+}
+
+void Trace::_learnedNewPath(
+	void *const tPtr,
+	uint64_t packetId,
+	const Identity &peerIdentity,
+	const InetAddress &physicalAddress,
+	const InetAddress &replaced)
+{
+	ZT_TraceEvent_VL1_LEARNED_NEW_PATH ev;
+	ev.evSize = CONST_TO_BE_UINT16(sizeof(ev));
+	ev.evType = CONST_TO_BE_UINT16(ZT_TRACE_VL1_LEARNED_NEW_PATH);
+	ev.packetId = Utils::hton(packetId);
+	ev.address = Utils::hton(peerIdentity.address().toInt());
+	memcpy(ev.identityHash,peerIdentity.hash(),48);
+	physicalAddress.forTrace(ev.physicalAddress);
+	replaced.forTrace(ev.replaced);
+
+	RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev);
+}
+
+void Trace::_incomingPacketDropped(
+	void *const tPtr,
+	uint64_t packetId,
+	uint64_t networkId,
+	const Identity &peerIdentity,
+	const InetAddress &physicalAddress,
+	uint8_t hops,
+	uint8_t verb,
+	ZT_TracePacketDropReason reason)
+{
+	ZT_TraceEvent_VL1_INCOMING_PACKET_DROPPED ev;
+	ev.evSize = CONST_TO_BE_UINT16(sizeof(ev));
+	ev.evType = CONST_TO_BE_UINT16(ZT_TRACE_VL1_INCOMING_PACKET_DROPPED);
+	ev.packetId = Utils::hton(packetId);
+	ev.networkId = Utils::hton(networkId);
+	ev.address = Utils::hton(peerIdentity.address().toInt());
+	memcpy(ev.identityHash,peerIdentity.hash(),48);
+	physicalAddress.forTrace(ev.physicalAddress);
+	ev.hops = hops;
+	ev.verb = verb;
+	ev.reason = (uint8_t)reason;
+
+	RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev);
+}
+
+void Trace::_outgoingNetworkFrameDropped(
+	void *const tPtr,
+	uint64_t networkId,
+	const MAC &sourceMac,
+	const MAC &destMac,
+	uint16_t etherType,
+	uint16_t frameLength,
+	const uint8_t *frameData,
+	ZT_TraceFrameDropReason reason)
+{
+	ZT_TraceEvent_VL2_OUTGOING_FRAME_DROPPED ev;
+	ev.evSize = CONST_TO_BE_UINT16(sizeof(ev));
+	ev.evType = CONST_TO_BE_UINT16(ZT_TRACE_VL2_OUTGOING_FRAME_DROPPED);
+	ev.networkId = Utils::hton(networkId);
+	ev.sourceMac = Utils::hton(sourceMac.toInt());
+	ev.destMac = Utils::hton(destMac.toInt());
+	ev.etherType = Utils::hton(etherType);
+	ev.frameLength = Utils::hton(frameLength);
+	if (frameData) {
+		unsigned int l = frameLength;
+		if (l > sizeof(ev.frameHead))
+			l = sizeof(ev.frameHead);
+		memcpy(ev.frameHead,frameData,l);
+		memset(ev.frameHead + l,0,sizeof(ev.frameHead) - l);
+	}
+	ev.reason = (uint8_t)reason;
+
+	RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev);
+}
+
+void Trace::_incomingNetworkFrameDropped(
+	void *const tPtr,
+	uint64_t networkId,
+	const MAC &sourceMac,
+	const MAC &destMac,
+	const Identity &peerIdentity,
+	const InetAddress &physicalAddress,
+	uint8_t hops,
+	uint16_t frameLength,
+	const uint8_t *frameData,
+	uint8_t verb,
+	bool credentialRequestSent,
+	ZT_TraceFrameDropReason reason)
+{
+	ZT_TraceEvent_VL2_INCOMING_FRAME_DROPPED ev;
+	ev.evSize = CONST_TO_BE_UINT16(sizeof(ev));
+	ev.evType = CONST_TO_BE_UINT16(ZT_TRACE_VL2_INCOMING_FRAME_DROPPED);
+	ev.networkId = Utils::hton(networkId);
+	ev.sourceMac = Utils::hton(sourceMac.toInt());
+	ev.destMac = Utils::hton(destMac.toInt());
+	ev.address = Utils::hton(peerIdentity.address().toInt());
+	physicalAddress.forTrace(ev.physicalAddress);
+	ev.hops = hops;
+	ev.frameLength = Utils::hton(frameLength);
+	if (frameData) {
+		unsigned int l = frameLength;
+		if (l > sizeof(ev.frameHead))
+			l = sizeof(ev.frameHead);
+		memcpy(ev.frameHead,frameData,l);
+		memset(ev.frameHead + l,0,sizeof(ev.frameHead) - l);
+	}
+	ev.verb = verb;
+	ev.credentialRequestSent = (uint8_t)credentialRequestSent;
+	ev.reason = (uint8_t)reason;
+
+	RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev);
+}
+
+void Trace::_networkConfigRequestSent(
+	void *const tPtr,
+	uint64_t networkId)
+{
+	ZT_TraceEvent_VL2_NETWORK_CONFIG_REQUESTED ev;
+	ev.evSize = CONST_TO_BE_UINT16(sizeof(ev));
+	ev.evType = CONST_TO_BE_UINT16(ZT_TRACE_VL2_NETWORK_CONFIG_REQUESTED);
+	ev.networkId = Utils::hton(networkId);
+
+	RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev);
+}
+
+void Trace::_networkFilter(
+	void *const tPtr,
+	uint64_t networkId,
+	const uint8_t primaryRuleSetLog[512],
+	const uint8_t matchingCapabilityRuleSetLog[512],
+	uint32_t matchingCapabilityId,
+	int64_t matchingCapabilityTimestamp,
+	const Address &source,
+	const Address &dest,
+	const MAC &sourceMac,
+	const MAC &destMac,
+	uint16_t frameLength,
+	const uint8_t *frameData,
+	uint16_t etherType,
+	uint16_t vlanId,
+	bool noTee,
+	bool inbound,
+	int accept)
+{
+	ZT_TraceEvent_VL2_NETWORK_FILTER ev;
+	ev.evSize = CONST_TO_BE_UINT16(sizeof(ev));
+	ev.evType = CONST_TO_BE_UINT16(ZT_TRACE_VL2_NETWORK_FILTER);
+	ev.networkId = Utils::hton(networkId);
+	memcpy(ev.primaryRuleSetLog,primaryRuleSetLog,sizeof(ev.primaryRuleSetLog));
+	if (matchingCapabilityRuleSetLog)
+		memcpy(ev.matchingCapabilityRuleSetLog,matchingCapabilityRuleSetLog,sizeof(ev.matchingCapabilityRuleSetLog));
+	else memset(ev.matchingCapabilityRuleSetLog,0,sizeof(ev.matchingCapabilityRuleSetLog));
+	ev.matchingCapabilityId = Utils::hton(matchingCapabilityId);
+	ev.matchingCapabilityTimestamp = Utils::hton(matchingCapabilityTimestamp);
+	ev.source = Utils::hton(source.toInt());
+	ev.dest = Utils::hton(dest.toInt());
+	ev.sourceMac = Utils::hton(sourceMac.toInt());
+	ev.destMac = Utils::hton(destMac.toInt());
+	ev.frameLength = Utils::hton(frameLength);
+	if (frameData) {
+		unsigned int l = frameLength;
+		if (l > sizeof(ev.frameHead))
+			l = sizeof(ev.frameHead);
+		memcpy(ev.frameHead,frameData,l);
+		memset(ev.frameHead + l,0,sizeof(ev.frameHead) - l);
+	}
+	ev.etherType = Utils::hton(etherType);
+	ev.vlanId = Utils::hton(vlanId);
+	ev.noTee = (uint8_t)noTee;
+	ev.inbound = (uint8_t)inbound;
+	ev.accept = (int8_t)accept;
+
+	RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev);
+}
+
+void Trace::_credentialRejected(
+	void *const tPtr,
+	uint64_t networkId,
+	const Address &address,
+	uint32_t credentialId,
+	int64_t credentialTimestamp,
+	uint8_t credentialType,
+	ZT_TraceCredentialRejectionReason reason)
+{
+	ZT_TraceEvent_VL2_CREDENTIAL_REJECTED ev;
+	ev.evSize = CONST_TO_BE_UINT16(sizeof(ev));
+	ev.evType = CONST_TO_BE_UINT16(ZT_TRACE_VL2_NETWORK_FILTER);
+	ev.networkId = Utils::hton(networkId);
+	ev.address = Utils::hton(address.toInt());
+	ev.credentialId = Utils::hton(credentialId);
+	ev.credentialTimestamp = Utils::hton(credentialTimestamp);
+	ev.credentialType = credentialType;
+	ev.reason = (uint8_t)reason;
+
+	RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev);
+}
+
+} // namespace ZeroTier

+ 225 - 80
node/Trace.hpp

@@ -14,29 +14,26 @@
 #ifndef ZT_TRACE_HPP
 #ifndef ZT_TRACE_HPP
 #define ZT_TRACE_HPP
 #define ZT_TRACE_HPP
 
 
-#include <cstdio>
 #include <cstdint>
 #include <cstdint>
 #include <cstring>
 #include <cstring>
 #include <cstdlib>
 #include <cstdlib>
+#include <vector>
 
 
 #include "Constants.hpp"
 #include "Constants.hpp"
 #include "SharedPtr.hpp"
 #include "SharedPtr.hpp"
+#include "Mutex.hpp"
 #include "Packet.hpp"
 #include "Packet.hpp"
-#include "Credential.hpp"
 #include "InetAddress.hpp"
 #include "InetAddress.hpp"
-#include "Dictionary.hpp"
-#include "Mutex.hpp"
-#include "Hashtable.hpp"
+#include "Address.hpp"
+#include "MAC.hpp"
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
 class RuntimeEnvironment;
 class RuntimeEnvironment;
-class Address;
 class Identity;
 class Identity;
 class Peer;
 class Peer;
 class Path;
 class Path;
 class Network;
 class Network;
-class MAC;
 class CertificateOfMembership;
 class CertificateOfMembership;
 class CertificateOfOwnership;
 class CertificateOfOwnership;
 class Revocation;
 class Revocation;
@@ -50,117 +47,265 @@ struct NetworkConfig;
 class Trace
 class Trace
 {
 {
 public:
 public:
-	class RuleResultLog
+	struct RuleResultLog
 	{
 	{
-	public:
-		ZT_ALWAYS_INLINE RuleResultLog() { this->clear(); }
-
-		ZT_ALWAYS_INLINE void log(const unsigned int rn,const uint8_t thisRuleMatches,const uint8_t thisSetMatches) { _l[rn >> 1U] |= ( ((thisRuleMatches + 1U) << 2U) | (thisSetMatches + 1U) ) << ((rn & 1U) << 2U); }
-		ZT_ALWAYS_INLINE void logSkipped(const unsigned int rn,const uint8_t thisSetMatches) { _l[rn >> 1U] |= (thisSetMatches + 1U) << ((rn & 1U) << 2U); }
-		ZT_ALWAYS_INLINE void clear() { memset(_l,0,sizeof(_l)); }
+		uint8_t l[ZT_MAX_NETWORK_RULES / 2]; // ZT_MAX_NETWORK_RULES 4-bit fields
 
 
-		ZT_ALWAYS_INLINE const uint8_t *data() const { return _l; }
-		ZT_ALWAYS_INLINE unsigned int sizeBytes() const { return (ZT_MAX_NETWORK_RULES / 2); }
-
-	private:
-		uint8_t _l[ZT_MAX_NETWORK_RULES / 2];
+		ZT_ALWAYS_INLINE void log(const unsigned int rn,const uint8_t thisRuleMatches,const uint8_t thisSetMatches)
+		{
+			l[rn >> 1U] |= ( ((thisRuleMatches + 1U) << 2U) | (thisSetMatches + 1U) ) << ((rn & 1U) << 2U);
+		}
+		ZT_ALWAYS_INLINE void logSkipped(const unsigned int rn,const uint8_t thisSetMatches)
+		{
+			l[rn >> 1U] |= (thisSetMatches + 1U) << ((rn & 1U) << 2U);
+		}
+		ZT_ALWAYS_INLINE void clear()
+		{
+			memset(l,0,sizeof(l));
+		}
 	};
 	};
 
 
-	inline Trace(const RuntimeEnvironment *renv)
-	{
-	}
-
-	inline void resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope)
-	{
-	}
-
-	inline void peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &path,const uint64_t packetId,const Packet::Verb verb)
-	{
-	}
-
-	inline void peerLinkNowRedundant(void *const tPtr,Peer &peer)
-	{
-	}
-
-	inline void peerLinkNoLongerRedundant(void *const tPtr,Peer &peer)
-	{
-	}
-
-	inline void peerLinkAggregateStatistics(void *const tPtr,Peer &peer)
-	{
-	}
+	explicit Trace(const RuntimeEnvironment *renv);
 
 
-	inline void peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId)
+	ZT_ALWAYS_INLINE void resettingPathsInScope(
+		void *const tPtr,
+		const Identity &reporter,
+		const InetAddress &from,
+		const InetAddress &oldExternal,
+		const InetAddress &newExternal,
+		const InetAddress::IpScope scope)
 	{
 	{
+		if (_vl1) _resettingPathsInScope(tPtr,reporter,from,oldExternal,newExternal,scope);
 	}
 	}
 
 
-	inline void incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason)
+	ZT_ALWAYS_INLINE void tryingNewPath(
+		void *const tPtr,
+		const Identity &trying,
+		const InetAddress &physicalAddress,
+		const InetAddress &triggerAddress,
+		uint64_t triggeringPacketId,
+		uint8_t triggeringPacketVerb,
+		uint64_t triggeredByAddress,
+		const uint8_t *triggeredByIdentityHash,
+		ZT_TraceTryingNewPathReason reason)
 	{
 	{
+		if (_vl1) _tryingNewPath(tPtr,trying,physicalAddress,triggerAddress,triggeringPacketId,triggeringPacketVerb,triggeredByAddress,triggeredByIdentityHash,reason);
 	}
 	}
 
 
-	inline void incomingPacketInvalid(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason)
+	ZT_ALWAYS_INLINE void learnedNewPath(
+		void *const tPtr,
+		uint64_t packetId,
+		const Identity &peerIdentity,
+		const InetAddress &physicalAddress,
+		const InetAddress &replaced)
 	{
 	{
+		if (_vl1) _learnedNewPath(tPtr,packetId,peerIdentity,physicalAddress,replaced);
 	}
 	}
 
 
-	inline void incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const char *reason)
+	ZT_ALWAYS_INLINE void incomingPacketDropped(
+		void *const tPtr,
+		uint64_t packetId,
+		uint64_t networkId,
+		const Identity &peerIdentity,
+		const InetAddress &physicalAddress,
+		uint8_t hops,
+		uint8_t verb,
+		const ZT_TracePacketDropReason reason)
 	{
 	{
+		if (_vl1) _incomingPacketDropped(tPtr,packetId,networkId,peerIdentity,physicalAddress,hops,verb,reason);
 	}
 	}
 
 
-	inline void outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason)
+	ZT_ALWAYS_INLINE void outgoingNetworkFrameDropped(
+		void *const tPtr,
+		uint64_t networkId,
+		const MAC &sourceMac,
+		const MAC &destMac,
+		uint16_t etherType,
+		uint16_t frameLength,
+		const uint8_t *frameData,
+		ZT_TraceFrameDropReason reason)
 	{
 	{
+		if (_vl2) _outgoingNetworkFrameDropped(tPtr,networkId,sourceMac,destMac,etherType,frameLength,frameData,reason);
 	}
 	}
 
 
-	inline void incomingNetworkAccessDenied(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested)
+	ZT_ALWAYS_INLINE void incomingNetworkFrameDropped(
+		void *const tPtr,
+		uint64_t networkId,
+		const MAC &sourceMac,
+		const MAC &destMac,
+		const Identity &peerIdentity,
+		const InetAddress &physicalAddress,
+		uint8_t hops,
+		uint16_t frameLength,
+		const uint8_t *frameData,
+		uint8_t verb,
+		bool credentialRequestSent,
+		ZT_TraceFrameDropReason reason)
 	{
 	{
+		if (_vl2) _incomingNetworkFrameDropped(tPtr,networkId,sourceMac,destMac,peerIdentity,physicalAddress,hops,frameLength,frameData,verb,credentialRequestSent,reason);
 	}
 	}
 
 
-	inline void incomingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason)
+	ZT_ALWAYS_INLINE void networkConfigRequestSent(
+		void *const tPtr,
+		uint64_t networkId)
 	{
 	{
+		if (_vl2) _networkConfigRequestSent(tPtr,networkId);
 	}
 	}
 
 
-	inline void networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller)
+	ZT_ALWAYS_INLINE void networkFilter(
+		void *const tPtr,
+		uint64_t networkId,
+		const uint8_t primaryRuleSetLog[512],
+		const uint8_t matchingCapabilityRuleSetLog[512],
+		uint32_t matchingCapabilityId,
+		int64_t matchingCapabilityTimestamp,
+		const Address &source,
+		const Address &dest,
+		const MAC &sourceMac,
+		const MAC &destMac,
+		uint16_t frameLength,
+		const uint8_t *frameData,
+		uint16_t etherType,
+		uint16_t vlanId,
+		bool noTee,
+		bool inbound,
+		int accept)
 	{
 	{
+		if (_vl2Filter) {
+			_networkFilter(
+				tPtr,
+				networkId,
+				primaryRuleSetLog,
+				matchingCapabilityRuleSetLog,
+				matchingCapabilityId,
+				matchingCapabilityTimestamp,
+				source,
+				dest,
+				sourceMac,
+				destMac,
+				frameLength,
+				frameData,
+				etherType,
+				vlanId,
+				noTee,
+				inbound,
+				accept);
+		}
 	}
 	}
 
 
-	inline void networkFilter(
+	ZT_ALWAYS_INLINE void credentialRejected(
 		void *const tPtr,
 		void *const tPtr,
-		const Network &network,
-		const RuleResultLog &primaryRuleSetLog,
-		const RuleResultLog *const matchingCapabilityRuleSetLog,
-		const Capability *const matchingCapability,
-		const Address &ztSource,
-		const Address &ztDest,
-		const MAC &macSource,
-		const MAC &macDest,
-		const uint8_t *const frameData,
-		const unsigned int frameLen,
-		const unsigned int etherType,
-		const unsigned int vlanId,
-		const bool noTee,
-		const bool inbound,
-		const int accept)
+		uint64_t networkId,
+		const Address &address,
+		uint32_t credentialId,
+		int64_t credentialTimestamp,
+		uint8_t credentialType,
+		ZT_TraceCredentialRejectionReason reason)
 	{
 	{
+		if (_vl2) _credentialRejected(tPtr,networkId,address,credentialId,credentialTimestamp,credentialType,reason);
 	}
 	}
 
 
-	inline void credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason)
-	{
-	}
+private:
+	void _resettingPathsInScope(
+		void *tPtr,
+		const Identity &reporter,
+		const InetAddress &from,
+		const InetAddress &oldExternal,
+		const InetAddress &newExternal,
+		InetAddress::IpScope scope);
+	void _tryingNewPath(
+		void *tPtr,
+		const Identity &trying,
+		const InetAddress &physicalAddress,
+		const InetAddress &triggerAddress,
+		uint64_t triggeringPacketId,
+		uint8_t triggeringPacketVerb,
+		uint64_t triggeredByAddress,
+		const uint8_t *triggeredByIdentityHash,
+		ZT_TraceTryingNewPathReason reason);
+	void _learnedNewPath(
+		void *tPtr,
+		uint64_t packetId,
+		const Identity &peerIdentity,
+		const InetAddress &physicalAddress,
+		const InetAddress &replaced);
+	void _incomingPacketDropped(
+		void *tPtr,
+		uint64_t packetId,
+		uint64_t networkId,
+		const Identity &peerIdentity,
+		const InetAddress &physicalAddress,
+		uint8_t hops,
+		uint8_t verb,
+		ZT_TracePacketDropReason reason);
+	void _outgoingNetworkFrameDropped(
+		void *tPtr,
+		uint64_t networkId,
+		const MAC &sourceMac,
+		const MAC &destMac,
+		uint16_t etherType,
+		uint16_t frameLength,
+		const uint8_t *frameData,
+		ZT_TraceFrameDropReason reason);
+	void _incomingNetworkFrameDropped(
+		void *const tPtr,
+		uint64_t networkId,
+		const MAC &sourceMac,
+		const MAC &destMac,
+		const Identity &peerIdentity,
+		const InetAddress &physicalAddress,
+		uint8_t hops,
+		uint16_t frameLength,
+		const uint8_t *frameData,
+		uint8_t verb,
+		bool credentialRequestSent,
+		ZT_TraceFrameDropReason reason);
+	void _networkConfigRequestSent(
+		void *tPtr,
+		uint64_t networkId);
+	void _networkFilter(
+		void *tPtr,
+		uint64_t networkId,
+		const uint8_t primaryRuleSetLog[512],
+		const uint8_t matchingCapabilityRuleSetLog[512],
+		uint32_t matchingCapabilityId,
+		int64_t matchingCapabilityTimestamp,
+		const Address &source,
+		const Address &dest,
+		const MAC &sourceMac,
+		const MAC &destMac,
+		uint16_t frameLength,
+		const uint8_t *frameData,
+		uint16_t etherType,
+		uint16_t vlanId,
+		bool noTee,
+		bool inbound,
+		int accept);
+	void _credentialRejected(
+		void *tPtr,
+		uint64_t networkId,
+		const Address &address,
+		uint32_t credentialId,
+		int64_t credentialTimestamp,
+		uint8_t credentialType,
+		ZT_TraceCredentialRejectionReason reason);
 
 
-	inline void credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason)
-	{
-	}
+	const RuntimeEnvironment *const RR;
+	volatile bool _vl1,_vl2,_vl2Filter,_vl2Multicast;
 
 
-	inline void credentialRejected(void *const tPtr,const Capability &c,const char *reason)
+	struct _MonitoringPeer
 	{
 	{
-	}
+		int64_t _timeSet;
+		unsigned int _traceTypes;
+		SharedPtr<Peer> peer;
+		Mutex lock;
+	};
 
 
-	inline void credentialRejected(void *const tPtr,const Tag &c,const char *reason)
-	{
-	}
+	uint8_t _eventBuf[8192]; // must be less than ZT_PROTO_MAX_PACKET_LENGTH
+	unsigned int _eventBufSize;
 
 
-	inline void credentialRejected(void *const tPtr,const Revocation &c,const char *reason)
-	{
-	}
+	std::vector<_MonitoringPeer> _monitoringPeers;
+	RWMutex _monitoringPeers_l;
 };
 };
 
 
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 1 - 1
node/Utils.cpp

@@ -331,7 +331,7 @@ uint64_t random()
 
 
 	l.lock();
 	l.lock();
 	const uint64_t result = ROL64(s1 * 5,7) * 9;
 	const uint64_t result = ROL64(s1 * 5,7) * 9;
-	const uint64_t t = s1 << 17;
+	const uint64_t t = s1 << 17U;
 	s2 ^= s0;
 	s2 ^= s0;
 	s3 ^= s1;
 	s3 ^= s1;
 	s1 ^= s2;
 	s1 ^= s2;

+ 3 - 3
node/Utils.hpp

@@ -395,7 +395,7 @@ static ZT_ALWAYS_INLINE T ntoh(T n) { return n; }
 template<typename I>
 template<typename I>
 static ZT_ALWAYS_INLINE I loadBigEndian(const void *const p)
 static ZT_ALWAYS_INLINE I loadBigEndian(const void *const p)
 {
 {
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 	I x = (I)0;
 	I x = (I)0;
 	for(unsigned int k=0;k<sizeof(I);++k) {
 	for(unsigned int k=0;k<sizeof(I);++k) {
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 #if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -413,7 +413,7 @@ static ZT_ALWAYS_INLINE I loadBigEndian(const void *const p)
 template<typename I>
 template<typename I>
 static ZT_ALWAYS_INLINE void storeBigEndian(void *const p,const I i)
 static ZT_ALWAYS_INLINE void storeBigEndian(void *const p,const I i)
 {
 {
-#ifdef ZT_NO_TYPE_PUNNING
+#ifdef ZT_NO_UNALIGNED_ACCESS
 	for(unsigned int k=0;k<sizeof(I);++k) {
 	for(unsigned int k=0;k<sizeof(I);++k) {
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 		reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[(sizeof(I)-1)-k];
 		reinterpret_cast<uint8_t *>(p)[k] = reinterpret_cast<const uint8_t *>(&i)[(sizeof(I)-1)-k];
@@ -422,7 +422,7 @@ static ZT_ALWAYS_INLINE void storeBigEndian(void *const p,const I i)
 #endif
 #endif
 	}
 	}
 #else
 #else
-	*reinterpret_cast<I *>(p) = Utils::hton(i);
+	*reinterpret_cast<I *>(p) = hton(i);
 #endif
 #endif
 }
 }
 
 

+ 4 - 0
osdep/OSUtils.cpp

@@ -24,6 +24,10 @@
 #include <fcntl.h>
 #include <fcntl.h>
 #endif
 #endif
 
 
+#ifdef __GCC__
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
 namespace ZeroTier {
 namespace ZeroTier {
 
 
 unsigned int OSUtils::ztsnprintf(char *buf,unsigned int len,const char *fmt,...)
 unsigned int OSUtils::ztsnprintf(char *buf,unsigned int len,const char *fmt,...)

+ 4 - 4
osdep/OSUtils.hpp

@@ -203,7 +203,7 @@ public:
 	 * @param quot Zero or more quote characters
 	 * @param quot Zero or more quote characters
 	 * @return Vector of tokens
 	 * @return Vector of tokens
 	 */
 	 */
-	static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
+	static std::vector<std::string> split(const char *s,const char *sep,const char *esc,const char *quot);
 
 
 	/**
 	/**
 	 * Write a block of data to disk, replacing any current file contents
 	 * Write a block of data to disk, replacing any current file contents
@@ -222,9 +222,9 @@ public:
 #ifndef OMIT_JSON_SUPPORT
 #ifndef OMIT_JSON_SUPPORT
 	static nlohmann::json jsonParse(const std::string &buf);
 	static nlohmann::json jsonParse(const std::string &buf);
 	static std::string jsonDump(const nlohmann::json &j,int indentation = 1);
 	static std::string jsonDump(const nlohmann::json &j,int indentation = 1);
-	static uint64_t jsonInt(const nlohmann::json &jv,const uint64_t dfl);
-	static uint64_t jsonIntHex(const nlohmann::json &jv,const uint64_t dfl);
-	static bool jsonBool(const nlohmann::json &jv,const bool dfl);
+	static uint64_t jsonInt(const nlohmann::json &jv,uint64_t dfl);
+	static uint64_t jsonIntHex(const nlohmann::json &jv,uint64_t dfl);
+	static bool jsonBool(const nlohmann::json &jv,bool dfl);
 	static std::string jsonString(const nlohmann::json &jv,const char *dfl);
 	static std::string jsonString(const nlohmann::json &jv,const char *dfl);
 #endif // OMIT_JSON_SUPPORT
 #endif // OMIT_JSON_SUPPORT
 };
 };