mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-05 03:53:44 +02:00
Merge branch 'dev' of http://git.int.zerotier.com/ZeroTier/ZeroTierOne into dev
This commit is contained in:
commit
3366b53247
28 changed files with 1133 additions and 700 deletions
|
@ -566,42 +566,69 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest(
|
||||||
|
|
||||||
// Determine whether and how member is authorized
|
// Determine whether and how member is authorized
|
||||||
const char *authorizedBy = (const char *)0;
|
const char *authorizedBy = (const char *)0;
|
||||||
if (!_jB(network["private"],true)) {
|
if (_jB(member["authorized"],false)) {
|
||||||
|
authorizedBy = "memberIsAuthorized";
|
||||||
|
} else if (!_jB(network["private"],true)) {
|
||||||
authorizedBy = "networkIsPublic";
|
authorizedBy = "networkIsPublic";
|
||||||
// If member already has an authorized field, leave it alone. That way its state is
|
|
||||||
// preserved if the user toggles the network back to private. Otherwise set it to
|
|
||||||
// true by default for new members of public nets.
|
|
||||||
if (!member.count("authorized")) {
|
if (!member.count("authorized")) {
|
||||||
member["authorized"] = true;
|
member["authorized"] = true;
|
||||||
member["lastAuthorizedTime"] = now;
|
json ah;
|
||||||
member["lastAuthorizedBy"] = authorizedBy;
|
ah["a"] = true;
|
||||||
|
ah["by"] = authorizedBy;
|
||||||
|
ah["ts"] = now;
|
||||||
|
ah["ct"] = json();
|
||||||
|
ah["c"] = json();
|
||||||
|
member["authHistory"].push_back(ah);
|
||||||
member["lastModified"] = now;
|
member["lastModified"] = now;
|
||||||
auto revj = member["revision"];
|
json &revj = member["revision"];
|
||||||
member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL);
|
member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL);
|
||||||
}
|
}
|
||||||
} else if (_jB(member["authorized"],false)) {
|
|
||||||
authorizedBy = "memberIsAuthorized";
|
|
||||||
} else {
|
} else {
|
||||||
char atok[256];
|
char presentedAuth[512];
|
||||||
if (metaData.get(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH_TOKEN,atok,sizeof(atok)) > 0) {
|
if (metaData.get(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH,presentedAuth,sizeof(presentedAuth)) > 0) {
|
||||||
atok[255] = (char)0; // not necessary but YDIFLO
|
presentedAuth[511] = (char)0; // sanity check
|
||||||
if (strlen(atok) > 0) { // extra sanity check since we never want to compare a null token on either side
|
|
||||||
auto authTokens = network["authTokens"];
|
// Check for bearer token presented by member
|
||||||
|
if ((strlen(presentedAuth) > 6)&&(!strncmp(presentedAuth,"token:",6))) {
|
||||||
|
const char *const presentedToken = presentedAuth + 6;
|
||||||
|
|
||||||
|
json &authTokens = network["authTokens"];
|
||||||
if (authTokens.is_array()) {
|
if (authTokens.is_array()) {
|
||||||
for(unsigned long i=0;i<authTokens.size();++i) {
|
for(unsigned long i=0;i<authTokens.size();++i) {
|
||||||
auto at = authTokens[i];
|
json &token = authTokens[i];
|
||||||
if (at.is_object()) {
|
if (token.is_object()) {
|
||||||
const uint64_t expires = _jI(at["expires"],0ULL);
|
const uint64_t expires = _jI(token["expires"],0ULL);
|
||||||
std::string tok = _jS(at["token"],"");
|
const uint64_t maxUses = _jI(token["maxUsesPerMember"],0ULL);
|
||||||
if ( ((expires == 0ULL)||(expires > now)) && (tok.length() > 0) && (tok == atok) ) {
|
std::string tstr = _jS(token["token"],"");
|
||||||
authorizedBy = "token";
|
|
||||||
member["authorized"] = true; // tokens actually change member authorization state
|
if (((expires == 0ULL)||(expires > now))&&(tstr == presentedToken)) {
|
||||||
member["lastAuthorizedTime"] = now;
|
bool usable = (maxUses == 0);
|
||||||
member["lastAuthorizedBy"] = authorizedBy;
|
if (!usable) {
|
||||||
member["lastModified"] = now;
|
uint64_t useCount = 0;
|
||||||
auto revj = member["revision"];
|
json &ahist = member["authHistory"];
|
||||||
member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL);
|
if (ahist.is_array()) {
|
||||||
break;
|
for(unsigned long j=0;j<ahist.size();++j) {
|
||||||
|
json &ah = ahist[j];
|
||||||
|
if ((_jS(ah["ct"],"") == "token")&&(_jS(ah["c"],"") == tstr)&&(_jB(ah["a"],false)))
|
||||||
|
++useCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usable = (useCount < maxUses);
|
||||||
|
}
|
||||||
|
if (usable) {
|
||||||
|
authorizedBy = "token";
|
||||||
|
member["authorized"] = true;
|
||||||
|
json ah;
|
||||||
|
ah["a"] = true;
|
||||||
|
ah["by"] = authorizedBy;
|
||||||
|
ah["ts"] = now;
|
||||||
|
ah["ct"] = "token";
|
||||||
|
ah["c"] = tstr;
|
||||||
|
member["authHistory"].push_back(ah);
|
||||||
|
member["lastModified"] = now;
|
||||||
|
json &revj = member["revision"];
|
||||||
|
member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -924,13 +951,11 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_jB(network["private"],true)) {
|
CertificateOfMembership com(now,credentialtmd,nwid,identity.address());
|
||||||
CertificateOfMembership com(now,credentialtmd,nwid,identity.address());
|
if (com.sign(signingId)) {
|
||||||
if (com.sign(signingId)) {
|
nc.com = com;
|
||||||
nc.com = com;
|
} else {
|
||||||
} else {
|
return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
|
||||||
return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_writeJson(memberJP,member);
|
_writeJson(memberJP,member);
|
||||||
|
@ -1139,16 +1164,15 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
|
||||||
|
|
||||||
if (b.count("authorized")) {
|
if (b.count("authorized")) {
|
||||||
const bool newAuth = _jB(b["authorized"],false);
|
const bool newAuth = _jB(b["authorized"],false);
|
||||||
const bool oldAuth = _jB(member["authorized"],false);
|
if (newAuth != _jB(member["authorized"],false)) {
|
||||||
if (newAuth != oldAuth) {
|
member["authorized"] = newAuth;
|
||||||
if (newAuth) {
|
json ah;
|
||||||
member["authorized"] = true;
|
ah["a"] = newAuth;
|
||||||
member["lastAuthorizedTime"] = now;
|
ah["by"] = "api";
|
||||||
member["lastAuthorizedBy"] = "user";
|
ah["ts"] = now;
|
||||||
} else {
|
ah["ct"] = json();
|
||||||
member["authorized"] = false;
|
ah["c"] = json();
|
||||||
member["lastDeauthorizedTime"] = now;
|
member["authHistory"].push_back(ah);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1429,13 +1453,14 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
|
||||||
if (authTokens.is_array()) {
|
if (authTokens.is_array()) {
|
||||||
json nat = json::array();
|
json nat = json::array();
|
||||||
for(unsigned long i=0;i<authTokens.size();++i) {
|
for(unsigned long i=0;i<authTokens.size();++i) {
|
||||||
auto token = authTokens[i];
|
json &token = authTokens[i];
|
||||||
if (token.is_object()) {
|
if (token.is_object()) {
|
||||||
std::string tstr = token["token"];
|
std::string tstr = token["token"];
|
||||||
if (tstr.length() > 0) {
|
if (tstr.length() > 0) {
|
||||||
json t = json::object();
|
json t = json::object();
|
||||||
t["token"] = tstr;
|
t["token"] = tstr;
|
||||||
t["expires"] = _jI(token["expires"],0ULL);
|
t["expires"] = _jI(token["expires"],0ULL);
|
||||||
|
t["maxUsesPerMember"] = _jI(token["maxUsesPerMember"],0ULL);
|
||||||
nat.push_back(t);
|
nat.push_back(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1585,7 +1610,6 @@ void EmbeddedNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTes
|
||||||
"\t\"upstream\": \"%.10llx\"," ZT_EOL_S
|
"\t\"upstream\": \"%.10llx\"," ZT_EOL_S
|
||||||
"\t\"current\": \"%.10llx\"," ZT_EOL_S
|
"\t\"current\": \"%.10llx\"," ZT_EOL_S
|
||||||
"\t\"receivedTimestamp\": %llu," ZT_EOL_S
|
"\t\"receivedTimestamp\": %llu," ZT_EOL_S
|
||||||
"\t\"remoteTimestamp\": %llu," ZT_EOL_S
|
|
||||||
"\t\"sourcePacketId\": \"%.16llx\"," ZT_EOL_S
|
"\t\"sourcePacketId\": \"%.16llx\"," ZT_EOL_S
|
||||||
"\t\"flags\": %llu," ZT_EOL_S
|
"\t\"flags\": %llu," ZT_EOL_S
|
||||||
"\t\"sourcePacketHopCount\": %u," ZT_EOL_S
|
"\t\"sourcePacketHopCount\": %u," ZT_EOL_S
|
||||||
|
@ -1606,7 +1630,6 @@ void EmbeddedNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTes
|
||||||
(unsigned long long)report->upstream,
|
(unsigned long long)report->upstream,
|
||||||
(unsigned long long)report->current,
|
(unsigned long long)report->current,
|
||||||
(unsigned long long)OSUtils::now(),
|
(unsigned long long)OSUtils::now(),
|
||||||
(unsigned long long)report->remoteTimestamp,
|
|
||||||
(unsigned long long)report->sourcePacketId,
|
(unsigned long long)report->sourcePacketId,
|
||||||
(unsigned long long)report->flags,
|
(unsigned long long)report->flags,
|
||||||
report->sourcePacketHopCount,
|
report->sourcePacketHopCount,
|
||||||
|
|
|
@ -143,9 +143,7 @@ private:
|
||||||
inline void _initMember(nlohmann::json &member)
|
inline void _initMember(nlohmann::json &member)
|
||||||
{
|
{
|
||||||
if (!member.count("authorized")) member["authorized"] = false;
|
if (!member.count("authorized")) member["authorized"] = false;
|
||||||
if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL;
|
if (!member.count("authHistory")) member["authHistory"] = nlohmann::json::array();
|
||||||
if (!member.count("lastAuthorizedBy")) member["lastAuthorizedBy"] = "";
|
|
||||||
if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL;
|
|
||||||
if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array();
|
if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array();
|
||||||
if (!member.count("recentLog")) member["recentLog"] = nlohmann::json::array();
|
if (!member.count("recentLog")) member["recentLog"] = nlohmann::json::array();
|
||||||
if (!member.count("activeBridge")) member["activeBridge"] = false;
|
if (!member.count("activeBridge")) member["activeBridge"] = false;
|
||||||
|
|
|
@ -229,9 +229,7 @@ This returns an object containing all currently online members and the most rece
|
||||||
| nwid | string | 16-digit network ID | no |
|
| nwid | string | 16-digit network ID | no |
|
||||||
| clock | integer | Current clock, ms since epoch | no |
|
| clock | integer | Current clock, ms since epoch | no |
|
||||||
| authorized | boolean | Is member authorized? (for private networks) | YES |
|
| authorized | boolean | Is member authorized? (for private networks) | YES |
|
||||||
| lastAuthorizedTime | integer | Time 'authorized' was last set to 'true' | no |
|
| authHistory | array[object] | History of auth changes, latest at end | no |
|
||||||
| lastAuthorizedBy | string | What last set 'authorized' to 'true'? | no |
|
|
||||||
| lastDeauthorizedTime | integer | Time 'authorized' was last set to 'false' | no |
|
|
||||||
| activeBridge | boolean | Member is able to bridge to other Ethernet nets | YES |
|
| activeBridge | boolean | Member is able to bridge to other Ethernet nets | YES |
|
||||||
| identity | string | Member's public ZeroTier identity (if known) | no |
|
| identity | string | Member's public ZeroTier identity (if known) | no |
|
||||||
| ipAssignments | array[string] | Managed IP address assignments | YES |
|
| ipAssignments | array[string] | Managed IP address assignments | YES |
|
||||||
|
|
|
@ -154,6 +154,11 @@ extern "C" {
|
||||||
*/
|
*/
|
||||||
#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 8
|
#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Circuit test report flag: upstream peer authorized in path (e.g. by network COM)
|
||||||
|
*/
|
||||||
|
#define ZT_CIRCUIT_TEST_REPORT_FLAGS_UPSTREAM_AUTHORIZED_IN_PATH 0x0000000000000001ULL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of cluster members (and max member ID plus one)
|
* Maximum number of cluster members (and max member ID plus one)
|
||||||
*/
|
*/
|
||||||
|
@ -865,19 +870,28 @@ enum ZT_VirtualNetworkConfigOperation
|
||||||
ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY = 4
|
ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ZT_RelayPolicy
|
||||||
|
{
|
||||||
|
ZT_RELAY_POLICY_NEVER = 0,
|
||||||
|
ZT_RELAY_POLICY_TRUSTED = 1,
|
||||||
|
ZT_RELAY_POLICY_ALWAYS = 2
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What trust hierarchy role does this peer have?
|
* What trust hierarchy role does this peer have?
|
||||||
*/
|
*/
|
||||||
enum ZT_PeerRole {
|
enum ZT_PeerRole
|
||||||
|
{
|
||||||
ZT_PEER_ROLE_LEAF = 0, // ordinary node
|
ZT_PEER_ROLE_LEAF = 0, // ordinary node
|
||||||
ZT_PEER_ROLE_RELAY = 1, // relay node
|
ZT_PEER_ROLE_UPSTREAM = 1, // upstream node
|
||||||
ZT_PEER_ROLE_ROOT = 2 // root server
|
ZT_PEER_ROLE_ROOT = 2 // global root
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vendor ID
|
* Vendor ID
|
||||||
*/
|
*/
|
||||||
enum ZT_Vendor {
|
enum ZT_Vendor
|
||||||
|
{
|
||||||
ZT_VENDOR_UNSPECIFIED = 0,
|
ZT_VENDOR_UNSPECIFIED = 0,
|
||||||
ZT_VENDOR_ZEROTIER = 1
|
ZT_VENDOR_ZEROTIER = 1
|
||||||
};
|
};
|
||||||
|
@ -885,7 +899,8 @@ enum ZT_Vendor {
|
||||||
/**
|
/**
|
||||||
* Platform type
|
* Platform type
|
||||||
*/
|
*/
|
||||||
enum ZT_Platform {
|
enum ZT_Platform
|
||||||
|
{
|
||||||
ZT_PLATFORM_UNSPECIFIED = 0,
|
ZT_PLATFORM_UNSPECIFIED = 0,
|
||||||
ZT_PLATFORM_LINUX = 1,
|
ZT_PLATFORM_LINUX = 1,
|
||||||
ZT_PLATFORM_WINDOWS = 2,
|
ZT_PLATFORM_WINDOWS = 2,
|
||||||
|
@ -900,13 +915,15 @@ enum ZT_Platform {
|
||||||
ZT_PLATFORM_VXWORKS = 11,
|
ZT_PLATFORM_VXWORKS = 11,
|
||||||
ZT_PLATFORM_FREERTOS = 12,
|
ZT_PLATFORM_FREERTOS = 12,
|
||||||
ZT_PLATFORM_SYSBIOS = 13,
|
ZT_PLATFORM_SYSBIOS = 13,
|
||||||
ZT_PLATFORM_HURD = 14
|
ZT_PLATFORM_HURD = 14,
|
||||||
|
ZT_PLATFORM_WEB = 15
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Architecture type
|
* Architecture type
|
||||||
*/
|
*/
|
||||||
enum ZT_Architecture {
|
enum ZT_Architecture
|
||||||
|
{
|
||||||
ZT_ARCHITECTURE_UNSPECIFIED = 0,
|
ZT_ARCHITECTURE_UNSPECIFIED = 0,
|
||||||
ZT_ARCHITECTURE_X86 = 1,
|
ZT_ARCHITECTURE_X86 = 1,
|
||||||
ZT_ARCHITECTURE_X64 = 2,
|
ZT_ARCHITECTURE_X64 = 2,
|
||||||
|
@ -921,7 +938,8 @@ enum ZT_Architecture {
|
||||||
ZT_ARCHITECTURE_SPARC32 = 11,
|
ZT_ARCHITECTURE_SPARC32 = 11,
|
||||||
ZT_ARCHITECTURE_SPARC64 = 12,
|
ZT_ARCHITECTURE_SPARC64 = 12,
|
||||||
ZT_ARCHITECTURE_DOTNET_CLR = 13,
|
ZT_ARCHITECTURE_DOTNET_CLR = 13,
|
||||||
ZT_ARCHITECTURE_JAVA_JVM = 14
|
ZT_ARCHITECTURE_JAVA_JVM = 14,
|
||||||
|
ZT_ARCHITECTURE_WEB = 15
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -959,6 +977,11 @@ typedef struct
|
||||||
*/
|
*/
|
||||||
unsigned int mtu;
|
unsigned int mtu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recommended MTU to avoid fragmentation at the physical layer (hint)
|
||||||
|
*/
|
||||||
|
unsigned int physicalMtu;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If nonzero, the network this port belongs to indicates DHCP availability
|
* If nonzero, the network this port belongs to indicates DHCP availability
|
||||||
*
|
*
|
||||||
|
@ -1218,18 +1241,13 @@ typedef struct {
|
||||||
*/
|
*/
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
|
|
||||||
/**
|
|
||||||
* Timestamp on remote device
|
|
||||||
*/
|
|
||||||
uint64_t remoteTimestamp;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 64-bit packet ID of packet received by the reporting device
|
* 64-bit packet ID of packet received by the reporting device
|
||||||
*/
|
*/
|
||||||
uint64_t sourcePacketId;
|
uint64_t sourcePacketId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flags (currently unused, will be zero)
|
* Flags
|
||||||
*/
|
*/
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
|
|
||||||
|
@ -1591,6 +1609,9 @@ typedef int (*ZT_PathCheckFunction)(
|
||||||
* Note that this can take a few seconds the first time it's called, as it
|
* Note that this can take a few seconds the first time it's called, as it
|
||||||
* will generate an identity.
|
* will generate an identity.
|
||||||
*
|
*
|
||||||
|
* TODO: should consolidate function pointers into versioned structure for
|
||||||
|
* better API stability.
|
||||||
|
*
|
||||||
* @param node Result: pointer is set to new node instance on success
|
* @param node Result: pointer is set to new node instance on success
|
||||||
* @param uptr User pointer to pass to functions/callbacks
|
* @param uptr User pointer to pass to functions/callbacks
|
||||||
* @param now Current clock in milliseconds
|
* @param now Current clock in milliseconds
|
||||||
|
@ -1681,6 +1702,15 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
|
||||||
*/
|
*/
|
||||||
enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
|
enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set node's relay policy
|
||||||
|
*
|
||||||
|
* @param node Node instance
|
||||||
|
* @param rp New relay policy
|
||||||
|
* @return OK(0) or error code
|
||||||
|
*/
|
||||||
|
enum ZT_ResultCode ZT_Node_setRelayPolicy(ZT_Node *node,enum ZT_RelayPolicy rp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join a network
|
* Join a network
|
||||||
*
|
*
|
||||||
|
|
|
@ -236,6 +236,11 @@
|
||||||
*/
|
*/
|
||||||
#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10)
|
#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expiration for credentials presented for MULTICAST_LIKE or MULTICAST_GATHER (for non-network-members)
|
||||||
|
*/
|
||||||
|
#define ZT_MULTICAST_CREDENTIAL_EXPIRATON ZT_MULTICAST_LIKE_EXPIRE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timeout for outgoing multicasts
|
* Timeout for outgoing multicasts
|
||||||
*
|
*
|
||||||
|
@ -263,6 +268,11 @@
|
||||||
*/
|
*/
|
||||||
#define ZT_PATH_MIN_REACTIVATE_INTERVAL 2500
|
#define ZT_PATH_MIN_REACTIVATE_INTERVAL 2500
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not accept HELLOs over a given path more often than this
|
||||||
|
*/
|
||||||
|
#define ZT_PATH_HELLO_RATE_LIMIT 1000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delay between full-fledge pings of directly connected peers
|
* Delay between full-fledge pings of directly connected peers
|
||||||
*/
|
*/
|
||||||
|
@ -283,6 +293,11 @@
|
||||||
*/
|
*/
|
||||||
#define ZT_PEER_ACTIVITY_TIMEOUT 500000
|
#define ZT_PEER_ACTIVITY_TIMEOUT 500000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General rate limit timeout for multiple packet types (HELLO, etc.)
|
||||||
|
*/
|
||||||
|
#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 1000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delay between requests for updated network autoconf information
|
* Delay between requests for updated network autoconf information
|
||||||
*
|
*
|
||||||
|
@ -340,6 +355,26 @@
|
||||||
*/
|
*/
|
||||||
#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4
|
#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time horizon for VERB_NETWORK_CREDENTIALS cutoff
|
||||||
|
*/
|
||||||
|
#define ZT_PEER_CREDENTIALS_CUTOFF_TIME 60000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of VERB_NETWORK_CREDENTIALS within cutoff time
|
||||||
|
*/
|
||||||
|
#define ZT_PEER_CREDEITIALS_CUTOFF_LIMIT 15
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General rate limit for other kinds of rate-limited packets (HELLO, credential request, etc.) both inbound and outbound
|
||||||
|
*/
|
||||||
|
#define ZT_PEER_GENERAL_RATE_LIMIT 1000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long is a path or peer considered to have a trust relationship with us (for e.g. relay policy) since last trusted established packet?
|
||||||
|
*/
|
||||||
|
#define ZT_TRUST_EXPIRATION 600000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable support for older network configurations from older (pre-1.1.6) controllers
|
* Enable support for older network configurations from older (pre-1.1.6) controllers
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -62,11 +62,8 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
|
} else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
|
||||||
// A null pointer for peer to _doHELLO() tells it to run its own
|
// Only HELLO is allowed in the clear, but will still have a MAC
|
||||||
// special internal authentication logic. This is done for unencrypted
|
return _doHELLO(RR,false);
|
||||||
// HELLOs to learn new identities, etc.
|
|
||||||
SharedPtr<Peer> tmp;
|
|
||||||
return _doHELLO(RR,tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
|
SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
|
||||||
|
@ -91,7 +88,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
|
||||||
peer->received(_path,hops(),packetId(),v,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),v,0,Packet::VERB_NOP,false);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Packet::VERB_HELLO: return _doHELLO(RR,peer);
|
case Packet::VERB_HELLO: return _doHELLO(RR,true);
|
||||||
case Packet::VERB_ERROR: return _doERROR(RR,peer);
|
case Packet::VERB_ERROR: return _doERROR(RR,peer);
|
||||||
case Packet::VERB_OK: return _doOK(RR,peer);
|
case Packet::VERB_OK: return _doOK(RR,peer);
|
||||||
case Packet::VERB_WHOIS: return _doWHOIS(RR,peer);
|
case Packet::VERB_WHOIS: return _doWHOIS(RR,peer);
|
||||||
|
@ -136,6 +133,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
|
||||||
switch(errorCode) {
|
switch(errorCode) {
|
||||||
|
|
||||||
case Packet::ERROR_OBJ_NOT_FOUND:
|
case Packet::ERROR_OBJ_NOT_FOUND:
|
||||||
|
// Object not found, currently only meaningful from network controllers.
|
||||||
if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
|
if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
|
||||||
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
||||||
if ((network)&&(network->controller() == peer->address()))
|
if ((network)&&(network->controller() == peer->address()))
|
||||||
|
@ -144,6 +142,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packet::ERROR_UNSUPPORTED_OPERATION:
|
case Packet::ERROR_UNSUPPORTED_OPERATION:
|
||||||
|
// This can be sent in response to any operation, though right now we only
|
||||||
|
// consider it meaningful from network controllers. This would indicate
|
||||||
|
// that the queried node does not support acting as a controller.
|
||||||
if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
|
if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
|
||||||
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
||||||
if ((network)&&(network->controller() == peer->address()))
|
if ((network)&&(network->controller() == peer->address()))
|
||||||
|
@ -152,21 +153,47 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Packet::ERROR_IDENTITY_COLLISION:
|
case Packet::ERROR_IDENTITY_COLLISION:
|
||||||
|
// Roots are the only peers currently permitted to state authoritatively
|
||||||
|
// that an identity has collided. When this occurs the node should be shut
|
||||||
|
// down and a new identity created. The odds of this ever happening are
|
||||||
|
// very low.
|
||||||
if (RR->topology->isRoot(peer->identity()))
|
if (RR->topology->isRoot(peer->identity()))
|
||||||
RR->node->postEvent(ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
|
RR->node->postEvent(ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
|
||||||
|
// This error can be sent in response to any packet that fails network
|
||||||
|
// authorization. We only listen to it if it's from a peer that has recently
|
||||||
|
// been authorized on this network.
|
||||||
|
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
||||||
|
if ((network)&&(network->recentlyAllowedOnNetwork(peer))) {
|
||||||
|
const uint64_t now = RR->node->now();
|
||||||
|
if (peer->rateGateComRequest(now)) {
|
||||||
|
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
||||||
|
network->config().com.serialize(outp);
|
||||||
|
outp.append((uint8_t)0);
|
||||||
|
outp.armor(peer->key(),true);
|
||||||
|
_path->send(RR,outp.data(),outp.size(),now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
|
case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
|
||||||
|
// Network controller: network access denied.
|
||||||
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
||||||
if ((network)&&(network->controller() == peer->address()))
|
if ((network)&&(network->controller() == peer->address()))
|
||||||
network->setAccessDenied();
|
network->setAccessDenied();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Packet::ERROR_UNWANTED_MULTICAST: {
|
case Packet::ERROR_UNWANTED_MULTICAST: {
|
||||||
uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
|
// Members of networks can use this error to indicate that they no longer
|
||||||
MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
|
// want to receive multicasts on a given channel.
|
||||||
TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",nwid,peer->address().toString().c_str(),mg.toString().c_str());
|
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
||||||
RR->mc->remove(nwid,mg,peer->address());
|
if ((network)&&(network->gate(peer,verb(),packetId()))) {
|
||||||
|
MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
|
||||||
|
TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",network->id(),peer->address().toString().c_str(),mg.toString().c_str());
|
||||||
|
RR->mc->remove(network->id(),mg,peer->address());
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
|
@ -179,16 +206,11 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer)
|
bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAuthenticated)
|
||||||
{
|
{
|
||||||
/* Note: this is the only packet ever sent in the clear, and it's also
|
|
||||||
* the only packet that we authenticate via a different path. Authentication
|
|
||||||
* occurs here and is based on the validity of the identity and the
|
|
||||||
* integrity of the packet's MAC, but it must be done after we check
|
|
||||||
* the identity since HELLO is a mechanism for learning new identities
|
|
||||||
* in the first place. */
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const uint64_t now = RR->node->now();
|
||||||
|
|
||||||
const uint64_t pid = packetId();
|
const uint64_t pid = packetId();
|
||||||
const Address fromAddress(source());
|
const Address fromAddress(source());
|
||||||
const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
|
const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
|
||||||
|
@ -215,31 +237,30 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (protoVersion < ZT_PROTO_VERSION_MIN) {
|
|
||||||
TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_path->address().toString().c_str());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (fromAddress != id.address()) {
|
if (fromAddress != id.address()) {
|
||||||
TRACE("dropped HELLO from %s(%s): identity not for sending address",fromAddress.toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped HELLO from %s(%s): identity not for sending address",fromAddress.toString().c_str(),_path->address().toString().c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (protoVersion < ZT_PROTO_VERSION_MIN) {
|
||||||
|
TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_path->address().toString().c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!peer) { // peer == NULL is the normal case here
|
SharedPtr<Peer> peer(RR->topology->getPeer(id.address()));
|
||||||
peer = RR->topology->getPeer(id.address());
|
if (peer) {
|
||||||
if (peer) {
|
// We already have an identity with this address -- check for collisions
|
||||||
// We already have an identity with this address -- check for collisions
|
if (!alreadyAuthenticated) {
|
||||||
|
|
||||||
if (peer->identity() != id) {
|
if (peer->identity() != id) {
|
||||||
// Identity is different from the one we already have -- address collision
|
// Identity is different from the one we already have -- address collision
|
||||||
|
|
||||||
unsigned char key[ZT_PEER_SECRET_KEY_LENGTH];
|
uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
|
||||||
if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
|
if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
|
||||||
if (dearmor(key)) { // ensure packet is authentic, otherwise drop
|
if (dearmor(key)) { // ensure packet is authentic, otherwise drop
|
||||||
TRACE("rejected HELLO from %s(%s): address already claimed",id.address().toString().c_str(),_path->address().toString().c_str());
|
TRACE("rejected HELLO from %s(%s): address already claimed",id.address().toString().c_str(),_path->address().toString().c_str());
|
||||||
Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
|
Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
|
||||||
outp.append((unsigned char)Packet::VERB_HELLO);
|
outp.append((uint8_t)Packet::VERB_HELLO);
|
||||||
outp.append((uint64_t)pid);
|
outp.append((uint64_t)pid);
|
||||||
outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION);
|
outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
|
||||||
outp.armor(key,true);
|
outp.armor(key,true);
|
||||||
_path->send(RR,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,outp.data(),outp.size(),RR->node->now());
|
||||||
} else {
|
} else {
|
||||||
|
@ -260,31 +281,39 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer
|
||||||
|
|
||||||
// Continue at // VALID
|
// Continue at // VALID
|
||||||
}
|
}
|
||||||
} else {
|
} // else continue at // VALID
|
||||||
// We don't already have an identity with this address -- validate and learn it
|
} else {
|
||||||
|
// We don't already have an identity with this address -- validate and learn it
|
||||||
|
|
||||||
// Check identity proof of work
|
// Sanity check: this basically can't happen
|
||||||
if (!id.locallyValidate()) {
|
if (alreadyAuthenticated) {
|
||||||
TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped HELLO from %s(%s): somehow already authenticated with unknown peer?",id.address().toString().c_str(),_path->address().toString().c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
// Check packet integrity and authentication
|
|
||||||
SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
|
|
||||||
if (!dearmor(newPeer->key())) {
|
|
||||||
TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_path->address().toString().c_str());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
peer = RR->topology->addPeer(newPeer);
|
|
||||||
|
|
||||||
// Continue at // VALID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VALID -- if we made it here, packet passed identity and authenticity checks!
|
// Check that identity's address is valid as per the derivation function
|
||||||
|
if (!id.locallyValidate()) {
|
||||||
|
TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_path->address().toString().c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check packet integrity and authentication
|
||||||
|
SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
|
||||||
|
if (!dearmor(newPeer->key())) {
|
||||||
|
TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_path->address().toString().c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
peer = RR->topology->addPeer(newPeer);
|
||||||
|
|
||||||
|
// Continue at // VALID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VALID -- if we made it here, packet passed identity and authenticity checks!
|
||||||
|
|
||||||
|
// Learn our external surface address from other peers to help us negotiate symmetric NATs
|
||||||
|
// and detect changes to our global IP that can trigger path renegotiation.
|
||||||
if ((externalSurfaceAddress)&&(hops() == 0))
|
if ((externalSurfaceAddress)&&(hops() == 0))
|
||||||
RR->sa->iam(id.address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),RR->node->now());
|
RR->sa->iam(id.address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
|
||||||
|
|
||||||
Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);
|
Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);
|
||||||
outp.append((unsigned char)Packet::VERB_HELLO);
|
outp.append((unsigned char)Packet::VERB_HELLO);
|
||||||
|
@ -336,7 +365,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer
|
||||||
}
|
}
|
||||||
|
|
||||||
outp.armor(peer->key(),true);
|
outp.armor(peer->key(),true);
|
||||||
_path->send(RR,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,outp.data(),outp.size(),now);
|
||||||
|
|
||||||
peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
|
peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
|
||||||
peer->received(_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false);
|
||||||
|
@ -351,8 +380,15 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
|
||||||
try {
|
try {
|
||||||
const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
|
const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
|
||||||
const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
|
const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
|
||||||
|
bool trustEstablished = false;
|
||||||
|
|
||||||
//TRACE("%s(%s): OK(%s)",source().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb));
|
// Don't parse OK packets that are not in response to a packet ID we sent
|
||||||
|
if (!RR->node->expectingReplyTo(inRePacketId)) {
|
||||||
|
TRACE("%s(%s): OK(%s) DROPPED: not expecting reply to %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb),packetId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TRACE("%s(%s): OK(%s)",peer->address().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb));
|
||||||
|
|
||||||
switch(inReVerb) {
|
switch(inReVerb) {
|
||||||
|
|
||||||
|
@ -406,6 +442,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
|
||||||
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID);
|
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID);
|
||||||
const SharedPtr<Network> network(RR->node->network(nwid));
|
const SharedPtr<Network> network(RR->node->network(nwid));
|
||||||
if ((network)&&(network->controller() == peer->address())) {
|
if ((network)&&(network->controller() == peer->address())) {
|
||||||
|
trustEstablished = true;
|
||||||
const unsigned int chunkLen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN);
|
const unsigned int chunkLen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN);
|
||||||
const void *chunkData = field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT,chunkLen);
|
const void *chunkData = field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT,chunkLen);
|
||||||
unsigned int chunkIndex = 0;
|
unsigned int chunkIndex = 0;
|
||||||
|
@ -424,10 +461,14 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
|
||||||
|
|
||||||
case Packet::VERB_MULTICAST_GATHER: {
|
case Packet::VERB_MULTICAST_GATHER: {
|
||||||
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
|
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
|
||||||
const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
|
SharedPtr<Network> network(RR->node->network(nwid));
|
||||||
//TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),size());
|
if ((network)&&(network->gateMulticastGatherReply(peer,verb(),packetId()))) {
|
||||||
const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
|
trustEstablished = true;
|
||||||
RR->mc->addMultiple(RR->node->now(),nwid,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
|
const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
|
||||||
|
//TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),size());
|
||||||
|
const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
|
||||||
|
RR->mc->addMultiple(RR->node->now(),nwid,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Packet::VERB_MULTICAST_FRAME: {
|
case Packet::VERB_MULTICAST_FRAME: {
|
||||||
|
@ -437,31 +478,34 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
|
||||||
|
|
||||||
//TRACE("%s(%s): OK(MULTICAST_FRAME) %.16llx/%s flags %.2x",peer->address().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),flags);
|
//TRACE("%s(%s): OK(MULTICAST_FRAME) %.16llx/%s flags %.2x",peer->address().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),flags);
|
||||||
|
|
||||||
unsigned int offset = 0;
|
SharedPtr<Network> network(RR->node->network(nwid));
|
||||||
|
if (network) {
|
||||||
|
unsigned int offset = 0;
|
||||||
|
|
||||||
if ((flags & 0x01) != 0) { // deprecated but still used by older peers
|
if ((flags & 0x01) != 0) { // deprecated but still used by older peers
|
||||||
CertificateOfMembership com;
|
CertificateOfMembership com;
|
||||||
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
|
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
|
||||||
if (com) {
|
if (com)
|
||||||
SharedPtr<Network> network(RR->node->network(com.networkId()));
|
|
||||||
if (network)
|
|
||||||
network->addCredential(com);
|
network->addCredential(com);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ((flags & 0x02) != 0) {
|
if (network->gateMulticastGatherReply(peer,verb(),packetId())) {
|
||||||
// OK(MULTICAST_FRAME) includes implicit gather results
|
trustEstablished = true;
|
||||||
offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
|
if ((flags & 0x02) != 0) {
|
||||||
unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
|
// OK(MULTICAST_FRAME) includes implicit gather results
|
||||||
unsigned int count = at<uint16_t>(offset); offset += 2;
|
offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
|
||||||
RR->mc->addMultiple(RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
|
unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
|
||||||
|
unsigned int count = at<uint16_t>(offset); offset += 2;
|
||||||
|
RR->mc->addMultiple(RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,trustEstablished);
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("dropped OK from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped OK from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
}
|
}
|
||||||
|
@ -471,6 +515,11 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
|
||||||
bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
if (!peer->rateGateInboundWhoisRequest(RR->node->now())) {
|
||||||
|
TRACE("dropped WHOIS from %s(%s): rate limit circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
||||||
outp.append((unsigned char)Packet::VERB_WHOIS);
|
outp.append((unsigned char)Packet::VERB_WHOIS);
|
||||||
outp.append(packetId());
|
outp.append(packetId());
|
||||||
|
@ -515,27 +564,29 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer>
|
||||||
bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
if (!RR->topology->isUpstream(peer->identity())) {
|
||||||
const SharedPtr<Peer> rendezvousWith(RR->topology->getPeer(with));
|
TRACE("RENDEZVOUS from %s ignored since source is not upstream",peer->address().toString().c_str());
|
||||||
if (rendezvousWith) {
|
} else {
|
||||||
const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
|
const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||||
const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
|
const SharedPtr<Peer> rendezvousWith(RR->topology->getPeer(with));
|
||||||
if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
|
if (rendezvousWith) {
|
||||||
const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
|
const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
|
||||||
if (!RR->topology->isUpstream(peer->identity())) {
|
const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
|
||||||
TRACE("RENDEZVOUS from %s says %s might be at %s, ignoring since peer is not upstream",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
|
if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
|
||||||
} else if (RR->node->shouldUsePathForZeroTierTraffic(_path->localAddress(),atAddr)) {
|
const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
|
||||||
RR->node->putPacket(_path->localAddress(),atAddr,"ABRE",4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
|
if (RR->node->shouldUsePathForZeroTierTraffic(_path->localAddress(),atAddr)) {
|
||||||
rendezvousWith->attemptToContactAt(_path->localAddress(),atAddr,RR->node->now());
|
RR->node->putPacket(_path->localAddress(),atAddr,"ABRE",4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
|
||||||
TRACE("RENDEZVOUS from %s says %s might be at %s, sent verification attempt",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
|
rendezvousWith->attemptToContactAt(_path->localAddress(),atAddr,RR->node->now());
|
||||||
|
TRACE("RENDEZVOUS from %s says %s might be at %s, sent verification attempt",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
|
||||||
|
} else {
|
||||||
|
TRACE("RENDEZVOUS from %s says %s might be at %s, ignoring since path is not suitable",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
TRACE("RENDEZVOUS from %s says %s might be at %s, ignoring since path is not suitable",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
|
TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_path->address().toString().c_str());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_path->address().toString().c_str());
|
TRACE("ignored RENDEZVOUS from %s(%s) to meet unknown peer %s",peer->address().toString().c_str(),_path->address().toString().c_str(),with.toString().c_str());
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
TRACE("ignored RENDEZVOUS from %s(%s) to meet unknown peer %s",peer->address().toString().c_str(),_path->address().toString().c_str(),with.toString().c_str());
|
|
||||||
}
|
}
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,false);
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
|
@ -549,25 +600,25 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer>
|
||||||
try {
|
try {
|
||||||
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID);
|
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID);
|
||||||
const SharedPtr<Network> network(RR->node->network(nwid));
|
const SharedPtr<Network> network(RR->node->network(nwid));
|
||||||
bool approved = false;
|
bool trustEstablished = false;
|
||||||
if (network) {
|
if (network) {
|
||||||
if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
|
if (!network->gate(peer,verb(),packetId())) {
|
||||||
if (!network->isAllowed(peer)) {
|
TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
|
||||||
TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
|
} else {
|
||||||
} else {
|
trustEstablished = true;
|
||||||
|
if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
|
||||||
const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
|
const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
|
||||||
const MAC sourceMac(peer->address(),nwid);
|
const MAC sourceMac(peer->address(),nwid);
|
||||||
const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
|
const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
|
||||||
const uint8_t *const frameData = reinterpret_cast<const uint8_t *>(data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
|
const uint8_t *const frameData = reinterpret_cast<const uint8_t *>(data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
|
||||||
if (network->filterIncomingPacket(peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
|
if (network->filterIncomingPacket(peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
|
||||||
RR->node->putFrame(nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
|
RR->node->putFrame(nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
|
||||||
approved = true; // this means approved on the network in general, not this packet per se
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TRACE("dropped FRAME from %s(%s): we are not a member of network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
|
TRACE("dropped FRAME from %s(%s): we are not a member of network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
|
||||||
}
|
}
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,approved);
|
peer->received(_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished);
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("dropped FRAME from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped FRAME from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
}
|
}
|
||||||
|
@ -580,23 +631,23 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
|
||||||
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID);
|
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID);
|
||||||
const SharedPtr<Network> network(RR->node->network(nwid));
|
const SharedPtr<Network> network(RR->node->network(nwid));
|
||||||
if (network) {
|
if (network) {
|
||||||
|
const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
|
||||||
|
|
||||||
|
unsigned int comLen = 0;
|
||||||
|
if ((flags & 0x01) != 0) { // inline COM with EXT_FRAME is deprecated but still used with old peers
|
||||||
|
CertificateOfMembership com;
|
||||||
|
comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
|
||||||
|
if (com)
|
||||||
|
network->addCredential(com);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!network->gate(peer,verb(),packetId())) {
|
||||||
|
TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),network->id());
|
||||||
|
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
|
if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
|
||||||
const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
|
|
||||||
|
|
||||||
unsigned int comLen = 0;
|
|
||||||
if ((flags & 0x01) != 0) { // deprecated but still used by old peers
|
|
||||||
CertificateOfMembership com;
|
|
||||||
comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
|
|
||||||
if (com)
|
|
||||||
network->addCredential(com);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!network->isAllowed(peer)) {
|
|
||||||
TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),network->id());
|
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
|
const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
|
||||||
const MAC to(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
|
const MAC to(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
|
||||||
const MAC from(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
|
const MAC from(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
|
||||||
|
@ -604,7 +655,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
|
||||||
const uint8_t *const frameData = (const uint8_t *)field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen);
|
const uint8_t *const frameData = (const uint8_t *)field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen);
|
||||||
|
|
||||||
if ((!from)||(from.isMulticast())||(from == network->mac())) {
|
if ((!from)||(from.isMulticast())||(from == network->mac())) {
|
||||||
TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: invalid source MAC",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str());
|
TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: invalid source MAC %s",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),from.toString().c_str());
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
|
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -620,7 +671,13 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (to != network->mac()) {
|
} else if (to != network->mac()) {
|
||||||
if (!network->config().permitsBridging(RR->identity.address())) {
|
if (to.isMulticast()) {
|
||||||
|
if (network->config().multicastLimit == 0) {
|
||||||
|
TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: network %.16llx does not allow multicast",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
|
||||||
|
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (!network->config().permitsBridging(RR->identity.address())) {
|
||||||
TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
|
TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
|
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
|
||||||
return true;
|
return true;
|
||||||
|
@ -647,6 +704,11 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
|
||||||
bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
if (!peer->rateGateEchoRequest(RR->node->now())) {
|
||||||
|
TRACE("dropped ECHO from %s(%s): rate limit circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const uint64_t pid = packetId();
|
const uint64_t pid = packetId();
|
||||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
||||||
outp.append((unsigned char)Packet::VERB_ECHO);
|
outp.append((unsigned char)Packet::VERB_ECHO);
|
||||||
|
@ -655,6 +717,7 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer>
|
||||||
outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
|
outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
|
||||||
outp.armor(peer->key(),true);
|
outp.armor(peer->key(),true);
|
||||||
_path->send(RR,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,outp.data(),outp.size(),RR->node->now());
|
||||||
|
|
||||||
peer->received(_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false);
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("dropped ECHO from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped ECHO from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
|
@ -667,14 +730,41 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
|
||||||
try {
|
try {
|
||||||
const uint64_t now = RR->node->now();
|
const uint64_t now = RR->node->now();
|
||||||
|
|
||||||
|
uint64_t authOnNetwork[256];
|
||||||
|
unsigned int authOnNetworkCount = 0;
|
||||||
|
SharedPtr<Network> network;
|
||||||
|
bool trustEstablished = false;
|
||||||
|
|
||||||
// Iterate through 18-byte network,MAC,ADI tuples
|
// Iterate through 18-byte network,MAC,ADI tuples
|
||||||
for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
|
for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
|
||||||
const uint64_t nwid = at<uint64_t>(ptr);
|
const uint64_t nwid = at<uint64_t>(ptr);
|
||||||
const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
|
|
||||||
RR->mc->add(now,nwid,group,peer->address());
|
bool auth = false;
|
||||||
|
for(unsigned int i=0;i<authOnNetworkCount;++i) {
|
||||||
|
if (nwid == authOnNetwork[i]) {
|
||||||
|
auth = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!auth) {
|
||||||
|
if ((!network)||(network->id() != nwid))
|
||||||
|
network = RR->node->network(nwid);
|
||||||
|
const bool authOnNet = ((network)&&(network->gate(peer,verb(),packetId())));
|
||||||
|
trustEstablished |= authOnNet;
|
||||||
|
if (authOnNet||RR->mc->cacheAuthorized(peer->address(),nwid,now)) {
|
||||||
|
auth = true;
|
||||||
|
if (authOnNetworkCount < 256) // sanity check, packets can't really be this big
|
||||||
|
authOnNetwork[authOnNetworkCount++] = nwid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth) {
|
||||||
|
const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
|
||||||
|
RR->mc->add(now,nwid,group,peer->address());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,trustEstablished);
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
}
|
}
|
||||||
|
@ -684,9 +774,15 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
|
||||||
bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
if (!peer->rateGateCredentialsReceived(RR->node->now())) {
|
||||||
|
TRACE("dropped NETWORK_CREDENTIALS from %s(%s): rate limit circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
CertificateOfMembership com;
|
CertificateOfMembership com;
|
||||||
Capability cap;
|
Capability cap;
|
||||||
Tag tag;
|
Tag tag;
|
||||||
|
bool trustEstablished = false;
|
||||||
|
|
||||||
unsigned int p = ZT_PACKET_IDX_PAYLOAD;
|
unsigned int p = ZT_PACKET_IDX_PAYLOAD;
|
||||||
while ((p < size())&&((*this)[p])) {
|
while ((p < size())&&((*this)[p])) {
|
||||||
|
@ -694,9 +790,11 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
|
||||||
if (com) {
|
if (com) {
|
||||||
SharedPtr<Network> network(RR->node->network(com.networkId()));
|
SharedPtr<Network> network(RR->node->network(com.networkId()));
|
||||||
if (network) {
|
if (network) {
|
||||||
if (network->addCredential(com) == 1)
|
switch (network->addCredential(com)) {
|
||||||
return false; // wait for WHOIS
|
case 0: trustEstablished = true; break;
|
||||||
}
|
case 1: return false; // wait for WHOIS
|
||||||
|
}
|
||||||
|
} else RR->mc->addCredential(com,false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++p; // skip trailing 0 after COMs if present
|
++p; // skip trailing 0 after COMs if present
|
||||||
|
@ -707,8 +805,10 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
|
||||||
p += cap.deserialize(*this,p);
|
p += cap.deserialize(*this,p);
|
||||||
SharedPtr<Network> network(RR->node->network(cap.networkId()));
|
SharedPtr<Network> network(RR->node->network(cap.networkId()));
|
||||||
if (network) {
|
if (network) {
|
||||||
if (network->addCredential(cap) == 1)
|
switch (network->addCredential(cap)) {
|
||||||
return false; // wait for WHOIS
|
case 0: trustEstablished = true; break;
|
||||||
|
case 1: return false; // wait for WHOIS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -717,13 +817,15 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
|
||||||
p += tag.deserialize(*this,p);
|
p += tag.deserialize(*this,p);
|
||||||
SharedPtr<Network> network(RR->node->network(tag.networkId()));
|
SharedPtr<Network> network(RR->node->network(tag.networkId()));
|
||||||
if (network) {
|
if (network) {
|
||||||
if (network->addCredential(tag) == 1)
|
switch (network->addCredential(tag)) {
|
||||||
return false; // wait for WHOIS
|
case 0: trustEstablished = true; break;
|
||||||
|
case 1: return false; // wait for WHOIS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished);
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("dropped NETWORK_CREDENTIALS from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped NETWORK_CREDENTIALS from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
}
|
}
|
||||||
|
@ -734,22 +836,21 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
|
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
|
||||||
|
|
||||||
const unsigned int metaDataLength = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);
|
|
||||||
const char *metaDataBytes = (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength);
|
|
||||||
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
|
|
||||||
|
|
||||||
const unsigned int hopCount = hops();
|
const unsigned int hopCount = hops();
|
||||||
const uint64_t requestPacketId = packetId();
|
const uint64_t requestPacketId = packetId();
|
||||||
bool netconfOk = false;
|
bool trustEstablished = false;
|
||||||
|
|
||||||
if (RR->localNetworkController) {
|
if (RR->localNetworkController) {
|
||||||
|
const unsigned int metaDataLength = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);
|
||||||
|
const char *metaDataBytes = (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength);
|
||||||
|
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
|
||||||
|
|
||||||
NetworkConfig *netconf = new NetworkConfig();
|
NetworkConfig *netconf = new NetworkConfig();
|
||||||
try {
|
try {
|
||||||
switch(RR->localNetworkController->doNetworkConfigRequest((hopCount > 0) ? InetAddress() : _path->address(),RR->identity,peer->identity(),nwid,metaData,*netconf)) {
|
switch(RR->localNetworkController->doNetworkConfigRequest((hopCount > 0) ? InetAddress() : _path->address(),RR->identity,peer->identity(),nwid,metaData,*netconf)) {
|
||||||
|
|
||||||
case NetworkController::NETCONF_QUERY_OK: {
|
case NetworkController::NETCONF_QUERY_OK: {
|
||||||
netconfOk = true;
|
trustEstablished = true;
|
||||||
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||||
try {
|
try {
|
||||||
if (netconf->toDictionary(*dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) {
|
if (netconf->toDictionary(*dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) {
|
||||||
|
@ -821,7 +922,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
|
||||||
_path->send(RR,outp.data(),outp.size(),RR->node->now());
|
_path->send(RR,outp.data(),outp.size(),RR->node->now());
|
||||||
}
|
}
|
||||||
|
|
||||||
peer->received(_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,netconfOk);
|
peer->received(_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,trustEstablished);
|
||||||
} catch (std::exception &exc) {
|
} catch (std::exception &exc) {
|
||||||
fprintf(stderr,"WARNING: network config request failed with exception: %s" ZT_EOL_S,exc.what());
|
fprintf(stderr,"WARNING: network config request failed with exception: %s" ZT_EOL_S,exc.what());
|
||||||
TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what());
|
TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what());
|
||||||
|
@ -836,11 +937,13 @@ bool IncomingPacket::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,cons
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
const uint64_t nwid = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
|
const uint64_t nwid = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
|
||||||
|
bool trustEstablished = false;
|
||||||
|
|
||||||
if (Network::controllerFor(nwid) == peer->address()) {
|
if (Network::controllerFor(nwid) == peer->address()) {
|
||||||
SharedPtr<Network> network(RR->node->network(nwid));
|
SharedPtr<Network> network(RR->node->network(nwid));
|
||||||
if (network) {
|
if (network) {
|
||||||
network->requestConfiguration();
|
network->requestConfiguration();
|
||||||
|
trustEstablished = true;
|
||||||
} else {
|
} else {
|
||||||
TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): not a member of %.16llx",source().toString().c_str(),_path->address().toString().c_str(),nwid);
|
TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): not a member of %.16llx",source().toString().c_str(),_path->address().toString().c_str(),nwid);
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP,false);
|
||||||
|
@ -855,7 +958,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP,trustEstablished);
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
}
|
}
|
||||||
|
@ -872,21 +975,24 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
|
||||||
|
|
||||||
//TRACE("<<MC %s(%s) GATHER up to %u in %.16llx/%s",source().toString().c_str(),_path->address().toString().c_str(),gatherLimit,nwid,mg.toString().c_str());
|
//TRACE("<<MC %s(%s) GATHER up to %u in %.16llx/%s",source().toString().c_str(),_path->address().toString().c_str(),gatherLimit,nwid,mg.toString().c_str());
|
||||||
|
|
||||||
|
const SharedPtr<Network> network(RR->node->network(nwid));
|
||||||
|
|
||||||
if ((flags & 0x01) != 0) {
|
if ((flags & 0x01) != 0) {
|
||||||
try {
|
try {
|
||||||
CertificateOfMembership com;
|
CertificateOfMembership com;
|
||||||
com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
|
com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
|
||||||
if (com) {
|
if (com) {
|
||||||
SharedPtr<Network> network(RR->node->network(nwid));
|
|
||||||
if (network)
|
if (network)
|
||||||
network->addCredential(com);
|
network->addCredential(com);
|
||||||
|
else RR->mc->addCredential(com,false);
|
||||||
}
|
}
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("MULTICAST_GATHER from %s(%s): discarded invalid COM",peer->address().toString().c_str(),_path->address().toString().c_str());
|
TRACE("MULTICAST_GATHER from %s(%s): discarded invalid COM",peer->address().toString().c_str(),_path->address().toString().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gatherLimit) {
|
const bool trustEstablished = ((network)&&(network->gate(peer,verb(),packetId())));
|
||||||
|
if ( ( trustEstablished || RR->mc->cacheAuthorized(peer->address(),nwid,RR->node->now()) ) && (gatherLimit > 0) ) {
|
||||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
||||||
outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
|
outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
|
||||||
outp.append(packetId());
|
outp.append(packetId());
|
||||||
|
@ -906,7 +1012,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,trustEstablished);
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("dropped MULTICAST_GATHER from %s(%s): unexpected exception",peer->address().toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped MULTICAST_GATHER from %s(%s): unexpected exception",peer->address().toString().c_str(),_path->address().toString().c_str());
|
||||||
}
|
}
|
||||||
|
@ -932,14 +1038,18 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
|
||||||
network->addCredential(com);
|
network->addCredential(com);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check membership after we've read any included COM, since
|
if (!network->gate(peer,verb(),packetId())) {
|
||||||
// that cert might be what we needed.
|
|
||||||
if (!network->isAllowed(peer)) {
|
|
||||||
TRACE("dropped MULTICAST_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
|
TRACE("dropped MULTICAST_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (network->config().multicastLimit == 0) {
|
||||||
|
TRACE("dropped MULTICAST_FRAME from %s(%s): network %.16llx does not allow multicast",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
|
||||||
|
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int gatherLimit = 0;
|
unsigned int gatherLimit = 0;
|
||||||
if ((flags & 0x02) != 0) {
|
if ((flags & 0x02) != 0) {
|
||||||
gatherLimit = at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT);
|
gatherLimit = at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT);
|
||||||
|
@ -1018,7 +1128,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
|
||||||
const uint64_t now = RR->node->now();
|
const uint64_t now = RR->node->now();
|
||||||
|
|
||||||
// First, subject this to a rate limit
|
// First, subject this to a rate limit
|
||||||
if (!peer->shouldRespondToDirectPathPush(now)) {
|
if (!peer->rateGatePushDirectPaths(now)) {
|
||||||
TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false);
|
||||||
return true;
|
return true;
|
||||||
|
@ -1139,6 +1249,8 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
|
||||||
// Add length of second "additional fields" section.
|
// Add length of second "additional fields" section.
|
||||||
vlf += at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 29 + vlf);
|
vlf += at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 29 + vlf);
|
||||||
|
|
||||||
|
uint64_t reportFlags = 0;
|
||||||
|
|
||||||
// Check credentials (signature already verified)
|
// Check credentials (signature already verified)
|
||||||
if (originatorCredentialNetworkId) {
|
if (originatorCredentialNetworkId) {
|
||||||
SharedPtr<Network> network(RR->node->network(originatorCredentialNetworkId));
|
SharedPtr<Network> network(RR->node->network(originatorCredentialNetworkId));
|
||||||
|
@ -1147,6 +1259,8 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (network->gate(peer,verb(),packetId()))
|
||||||
|
reportFlags |= ZT_CIRCUIT_TEST_REPORT_FLAGS_UPSTREAM_AUTHORIZED_IN_PATH;
|
||||||
} else {
|
} else {
|
||||||
TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_path->address().toString().c_str(),originatorAddress.toString().c_str());
|
TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_path->address().toString().c_str(),originatorAddress.toString().c_str());
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
|
peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
|
||||||
|
@ -1188,7 +1302,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
|
||||||
outp.append((uint16_t)ZT_PLATFORM_UNSPECIFIED);
|
outp.append((uint16_t)ZT_PLATFORM_UNSPECIFIED);
|
||||||
outp.append((uint16_t)ZT_ARCHITECTURE_UNSPECIFIED);
|
outp.append((uint16_t)ZT_ARCHITECTURE_UNSPECIFIED);
|
||||||
outp.append((uint16_t)0); // error code, currently unused
|
outp.append((uint16_t)0); // error code, currently unused
|
||||||
outp.append((uint64_t)0); // flags, currently unused
|
outp.append((uint64_t)reportFlags);
|
||||||
outp.append((uint64_t)packetId());
|
outp.append((uint64_t)packetId());
|
||||||
peer->address().appendTo(outp);
|
peer->address().appendTo(outp);
|
||||||
outp.append((uint8_t)hops());
|
outp.append((uint8_t)hops());
|
||||||
|
@ -1237,7 +1351,6 @@ bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const S
|
||||||
report.upstream = Address(field(ZT_PACKET_IDX_PAYLOAD + 52,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
|
report.upstream = Address(field(ZT_PACKET_IDX_PAYLOAD + 52,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
|
||||||
report.testId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 8);
|
report.testId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 8);
|
||||||
report.timestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
|
report.timestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
|
||||||
report.remoteTimestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 16);
|
|
||||||
report.sourcePacketId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 44);
|
report.sourcePacketId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 44);
|
||||||
report.flags = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 36);
|
report.flags = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 36);
|
||||||
report.sourcePacketHopCount = (*this)[ZT_PACKET_IDX_PAYLOAD + 57]; // end of fixed length headers: 58
|
report.sourcePacketHopCount = (*this)[ZT_PACKET_IDX_PAYLOAD + 57]; // end of fixed length headers: 58
|
||||||
|
|
|
@ -136,7 +136,7 @@ private:
|
||||||
// These are called internally to handle packet contents once it has
|
// These are called internally to handle packet contents once it has
|
||||||
// been authenticated, decrypted, decompressed, and classified.
|
// been authenticated, decrypted, decompressed, and classified.
|
||||||
bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer); // can be called with NULL peer, while all others cannot
|
bool _doHELLO(const RuntimeEnvironment *RR,const bool alreadyAuthenticated);
|
||||||
bool _doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||||
|
|
|
@ -24,13 +24,13 @@
|
||||||
#include "Packet.hpp"
|
#include "Packet.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
|
|
||||||
#define ZT_CREDENTIAL_PUSH_EVERY (ZT_NETWORK_AUTOCONF_DELAY / 4)
|
#define ZT_CREDENTIAL_PUSH_EVERY (ZT_NETWORK_AUTOCONF_DELAY / 3)
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
void Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,const Capability *cap)
|
void Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,const Capability *cap)
|
||||||
{
|
{
|
||||||
if ((now - _lastPushAttempt) < 1000ULL)
|
if ((now - _lastPushAttempt) < 2000ULL)
|
||||||
return;
|
return;
|
||||||
_lastPushAttempt = now;
|
_lastPushAttempt = now;
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ void Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint
|
||||||
}
|
}
|
||||||
capsAndTags.setAt<uint16_t>(tagCountPos,(uint16_t)appendedTags);
|
capsAndTags.setAt<uint16_t>(tagCountPos,(uint16_t)appendedTags);
|
||||||
|
|
||||||
const bool needCom = ((nconf.isPrivate())&&(nconf.com)&&((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY));
|
const bool needCom = ((nconf.com)&&((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY));
|
||||||
if ( (needCom) || (appendedCaps) || (appendedTags) ) {
|
if ( (needCom) || (appendedCaps) || (appendedTags) ) {
|
||||||
Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
||||||
if (needCom) {
|
if (needCom) {
|
||||||
|
@ -99,9 +99,11 @@ int Membership::addCredential(const RuntimeEnvironment *RR,const CertificateOfMe
|
||||||
const int vr = com.verify(RR);
|
const int vr = com.verify(RR);
|
||||||
|
|
||||||
if (vr == 0) {
|
if (vr == 0) {
|
||||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (new)",com.issuedTo().toString().c_str(),com.networkId());
|
if (com.timestamp().first >= _com.timestamp().first) {
|
||||||
if (com.timestamp().first > _com.timestamp().first) {
|
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (new)",com.issuedTo().toString().c_str(),com.networkId());
|
||||||
_com = com;
|
_com = com;
|
||||||
|
} else {
|
||||||
|
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED but not used (OK but older than current)",com.issuedTo().toString().c_str(),com.networkId());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (%d)",com.issuedTo().toString().c_str(),com.networkId(),vr);
|
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (%d)",com.issuedTo().toString().c_str(),com.networkId(),vr);
|
||||||
|
|
|
@ -154,6 +154,23 @@ public:
|
||||||
return nconf.com.agreesWith(_com);
|
return nconf.com.agreesWith(_com);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if this member has been on this network recently (or network is public)
|
||||||
|
*/
|
||||||
|
inline bool recentlyAllowedOnNetwork(const NetworkConfig &nconf) const
|
||||||
|
{
|
||||||
|
if (nconf.isPublic())
|
||||||
|
return true;
|
||||||
|
if (_com) {
|
||||||
|
const uint64_t a = _com.timestamp().first;
|
||||||
|
if ((_blacklistBefore)&&(a <= _blacklistBefore))
|
||||||
|
return false;
|
||||||
|
const uint64_t b = nconf.com.timestamp().first;
|
||||||
|
return ((a <= b) ? ((b - a) <= ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA) : true);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a capability or tag is within its max delta from the timestamp of our network config and newer than any blacklist cutoff time
|
* Check whether a capability or tag is within its max delta from the timestamp of our network config and newer than any blacklist cutoff time
|
||||||
*
|
*
|
||||||
|
|
|
@ -34,8 +34,8 @@ namespace ZeroTier {
|
||||||
|
|
||||||
Multicaster::Multicaster(const RuntimeEnvironment *renv) :
|
Multicaster::Multicaster(const RuntimeEnvironment *renv) :
|
||||||
RR(renv),
|
RR(renv),
|
||||||
_groups(1024),
|
_groups(256),
|
||||||
_groups_m()
|
_gatherAuth(256)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ void Multicaster::send(
|
||||||
}
|
}
|
||||||
|
|
||||||
for(unsigned int k=0;k<numExplicitGatherPeers;++k) {
|
for(unsigned int k=0;k<numExplicitGatherPeers;++k) {
|
||||||
const CertificateOfMembership *com = (network) ? (((network->config())&&(network->config().isPrivate())) ? &(network->config().com) : (const CertificateOfMembership *)0) : (const CertificateOfMembership *)0;
|
const CertificateOfMembership *com = (network) ? ((network->config().com) ? &(network->config().com) : (const CertificateOfMembership *)0) : (const CertificateOfMembership *)0;
|
||||||
Packet outp(explicitGatherPeers[k],RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
|
Packet outp(explicitGatherPeers[k],RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
|
||||||
outp.append(nwid);
|
outp.append(nwid);
|
||||||
outp.append((uint8_t)((com) ? 0x01 : 0x00));
|
outp.append((uint8_t)((com) ? 0x01 : 0x00));
|
||||||
|
@ -253,6 +253,7 @@ void Multicaster::send(
|
||||||
outp.append((uint32_t)gatherLimit);
|
outp.append((uint32_t)gatherLimit);
|
||||||
if (com)
|
if (com)
|
||||||
com->serialize(outp);
|
com->serialize(outp);
|
||||||
|
RR->node->expectReplyTo(outp.packetId());
|
||||||
RR->sw->send(outp,true);
|
RR->sw->send(outp,true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,42 +301,62 @@ void Multicaster::send(
|
||||||
|
|
||||||
void Multicaster::clean(uint64_t now)
|
void Multicaster::clean(uint64_t now)
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_groups_m);
|
{
|
||||||
|
Mutex::Lock _l(_groups_m);
|
||||||
|
Multicaster::Key *k = (Multicaster::Key *)0;
|
||||||
|
MulticastGroupStatus *s = (MulticastGroupStatus *)0;
|
||||||
|
Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
|
||||||
|
while (mm.next(k,s)) {
|
||||||
|
for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
|
||||||
|
if ((tx->expired(now))||(tx->atLimit()))
|
||||||
|
s->txQueue.erase(tx++);
|
||||||
|
else ++tx;
|
||||||
|
}
|
||||||
|
|
||||||
Multicaster::Key *k = (Multicaster::Key *)0;
|
unsigned long count = 0;
|
||||||
MulticastGroupStatus *s = (MulticastGroupStatus *)0;
|
{
|
||||||
Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
|
std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
|
||||||
while (mm.next(k,s)) {
|
std::vector<MulticastGroupMember>::iterator writer(reader);
|
||||||
for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
|
while (reader != s->members.end()) {
|
||||||
if ((tx->expired(now))||(tx->atLimit()))
|
if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
|
||||||
s->txQueue.erase(tx++);
|
*writer = *reader;
|
||||||
else ++tx;
|
++writer;
|
||||||
}
|
++count;
|
||||||
|
}
|
||||||
unsigned long count = 0;
|
++reader;
|
||||||
{
|
|
||||||
std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
|
|
||||||
std::vector<MulticastGroupMember>::iterator writer(reader);
|
|
||||||
while (reader != s->members.end()) {
|
|
||||||
if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
|
|
||||||
*writer = *reader;
|
|
||||||
++writer;
|
|
||||||
++count;
|
|
||||||
}
|
}
|
||||||
++reader;
|
}
|
||||||
|
|
||||||
|
if (count) {
|
||||||
|
s->members.resize(count);
|
||||||
|
} else if (s->txQueue.empty()) {
|
||||||
|
_groups.erase(*k);
|
||||||
|
} else {
|
||||||
|
s->members.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (count) {
|
{
|
||||||
s->members.resize(count);
|
Mutex::Lock _l(_gatherAuth_m);
|
||||||
} else if (s->txQueue.empty()) {
|
_GatherAuthKey *k = (_GatherAuthKey *)0;
|
||||||
_groups.erase(*k);
|
uint64_t *ts = (uint64_t *)ts;
|
||||||
} else {
|
Hashtable<_GatherAuthKey,uint64_t>::Iterator i(_gatherAuth);
|
||||||
s->members.clear();
|
while (i.next(k,ts)) {
|
||||||
|
if ((now - *ts) >= ZT_MULTICAST_CREDENTIAL_EXPIRATON)
|
||||||
|
_gatherAuth.erase(*k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Multicaster::addCredential(const CertificateOfMembership &com,bool alreadyValidated)
|
||||||
|
{
|
||||||
|
if ((alreadyValidated)||(com.verify(RR) == 0)) {
|
||||||
|
Mutex::Lock _l(_gatherAuth_m);
|
||||||
|
_gatherAuth[_GatherAuthKey(com.networkId(),com.issuedTo())] = RR->node->now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
|
void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
|
||||||
{
|
{
|
||||||
// assumes _groups_m is locked
|
// assumes _groups_m is locked
|
||||||
|
|
|
@ -179,12 +179,52 @@ public:
|
||||||
*/
|
*/
|
||||||
void clean(uint64_t now);
|
void clean(uint64_t now);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an authorization credential
|
||||||
|
*
|
||||||
|
* The Multicaster keeps its own track of when valid credentials of network
|
||||||
|
* membership are presented. This allows it to control MULTICAST_LIKE
|
||||||
|
* GATHER authorization for networks this node does not belong to.
|
||||||
|
*
|
||||||
|
* @param com Certificate of membership
|
||||||
|
* @param alreadyValidated If true, COM has already been checked and found to be valid and signed
|
||||||
|
*/
|
||||||
|
void addCredential(const CertificateOfMembership &com,bool alreadyValidated);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check authorization for GATHER and LIKE for non-network-members
|
||||||
|
*
|
||||||
|
* @param a Address of peer
|
||||||
|
* @param nwid Network ID
|
||||||
|
* @param now Current time
|
||||||
|
* @return True if GATHER and LIKE should be allowed
|
||||||
|
*/
|
||||||
|
bool cacheAuthorized(const Address &a,const uint64_t nwid,const uint64_t now) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_gatherAuth_m);
|
||||||
|
const uint64_t *p = _gatherAuth.get(_GatherAuthKey(nwid,a));
|
||||||
|
return ((p)&&((now - *p) < ZT_MULTICAST_CREDENTIAL_EXPIRATON));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
|
void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
|
||||||
|
|
||||||
const RuntimeEnvironment *RR;
|
const RuntimeEnvironment *RR;
|
||||||
|
|
||||||
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
|
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
|
||||||
Mutex _groups_m;
|
Mutex _groups_m;
|
||||||
|
|
||||||
|
struct _GatherAuthKey
|
||||||
|
{
|
||||||
|
_GatherAuthKey() : member(0),networkId(0) {}
|
||||||
|
_GatherAuthKey(const uint64_t nwid,const Address &a) : member(a.toInt()),networkId(nwid) {}
|
||||||
|
inline unsigned long hashCode() const { return (member ^ networkId); }
|
||||||
|
inline bool operator==(const _GatherAuthKey &k) const { return ((member == k.member)&&(networkId == k.networkId)); }
|
||||||
|
uint64_t member;
|
||||||
|
uint64_t networkId;
|
||||||
|
};
|
||||||
|
Hashtable< _GatherAuthKey,uint64_t > _gatherAuth;
|
||||||
|
Mutex _gatherAuth_m;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
171
node/Network.cpp
171
node/Network.cpp
|
@ -656,8 +656,12 @@ bool Network::filterOutgoingPacket(
|
||||||
|
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
|
|
||||||
Membership &m = _memberships[ztDest];
|
Membership *m = (Membership *)0;
|
||||||
const unsigned int remoteTagCount = m.getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS);
|
unsigned int remoteTagCount = 0;
|
||||||
|
if (ztDest) {
|
||||||
|
m = &(_memberships[ztDest]);
|
||||||
|
remoteTagCount = m->getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS);
|
||||||
|
}
|
||||||
|
|
||||||
switch(_doZtFilter(RR,_config,false,ztSource,ztDest2,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc,ccLength)) {
|
switch(_doZtFilter(RR,_config,false,ztSource,ztDest2,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc,ccLength)) {
|
||||||
|
|
||||||
|
@ -737,8 +741,8 @@ bool Network::filterOutgoingPacket(
|
||||||
RR->sw->send(outp,true);
|
RR->sw->send(outp,true);
|
||||||
|
|
||||||
return false; // DROP locally, since we redirected
|
return false; // DROP locally, since we redirected
|
||||||
} else if (ztDest) {
|
} else if (m) {
|
||||||
m.sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config,relevantCap);
|
m->sendCredentialsIfNeeded(RR,RR->node->now(),ztDest,_config,relevantCap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -764,7 +768,7 @@ int Network::filterIncomingPacket(
|
||||||
|
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
|
|
||||||
Membership &m = _membership(ztDest);
|
Membership &m = _membership(sourcePeer->address());
|
||||||
const unsigned int remoteTagCount = m.getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS);
|
const unsigned int remoteTagCount = m.getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS);
|
||||||
|
|
||||||
switch (_doZtFilter(RR,_config,true,sourcePeer->address(),ztDest2,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc,ccLength)) {
|
switch (_doZtFilter(RR,_config,true,sourcePeer->address(),ztDest2,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc,ccLength)) {
|
||||||
|
@ -862,31 +866,24 @@ bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBr
|
||||||
return true;
|
return true;
|
||||||
else if (includeBridgedGroups)
|
else if (includeBridgedGroups)
|
||||||
return _multicastGroupsBehindMe.contains(mg);
|
return _multicastGroupsBehindMe.contains(mg);
|
||||||
else return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::multicastSubscribe(const MulticastGroup &mg)
|
void Network::multicastSubscribe(const MulticastGroup &mg)
|
||||||
{
|
{
|
||||||
{
|
Mutex::Lock _l(_lock);
|
||||||
Mutex::Lock _l(_lock);
|
if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) {
|
||||||
if (std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg))
|
_myMulticastGroups.insert(std::upper_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg),mg);
|
||||||
return;
|
_sendUpdatesToMembers(&mg);
|
||||||
_myMulticastGroups.push_back(mg);
|
|
||||||
std::sort(_myMulticastGroups.begin(),_myMulticastGroups.end());
|
|
||||||
_announceMulticastGroups(&mg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::multicastUnsubscribe(const MulticastGroup &mg)
|
void Network::multicastUnsubscribe(const MulticastGroup &mg)
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
std::vector<MulticastGroup> nmg;
|
std::vector<MulticastGroup>::iterator i(std::lower_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg));
|
||||||
for(std::vector<MulticastGroup>::const_iterator i(_myMulticastGroups.begin());i!=_myMulticastGroups.end();++i) {
|
if ( (i != _myMulticastGroups.end()) && (*i == mg) )
|
||||||
if (*i != mg)
|
_myMulticastGroups.erase(i);
|
||||||
nmg.push_back(*i);
|
|
||||||
}
|
|
||||||
if (nmg.size() != _myMulticastGroups.size())
|
|
||||||
_myMulticastGroups.swap(nmg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Network::applyConfiguration(const NetworkConfig &conf)
|
bool Network::applyConfiguration(const NetworkConfig &conf)
|
||||||
|
@ -1004,6 +1001,7 @@ void Network::requestConfiguration()
|
||||||
|
|
||||||
Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> rmd;
|
Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> rmd;
|
||||||
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION);
|
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION);
|
||||||
|
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_VENDOR,(uint64_t)ZT_VENDOR_ZEROTIER);
|
||||||
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,(uint64_t)ZT_PROTO_VERSION);
|
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,(uint64_t)ZT_PROTO_VERSION);
|
||||||
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,(uint64_t)ZEROTIER_ONE_VERSION_MAJOR);
|
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,(uint64_t)ZEROTIER_ONE_VERSION_MAJOR);
|
||||||
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,(uint64_t)ZEROTIER_ONE_VERSION_MINOR);
|
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,(uint64_t)ZEROTIER_ONE_VERSION_MINOR);
|
||||||
|
@ -1014,6 +1012,7 @@ void Network::requestConfiguration()
|
||||||
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS,(uint64_t)ZT_MAX_NETWORK_TAGS);
|
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS,(uint64_t)ZT_MAX_NETWORK_TAGS);
|
||||||
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);
|
||||||
|
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_RELAY_POLICY,(uint64_t)RR->node->relayPolicy());
|
||||||
|
|
||||||
if (ctrl == RR->identity.address()) {
|
if (ctrl == RR->identity.address()) {
|
||||||
if (RR->localNetworkController) {
|
if (RR->localNetworkController) {
|
||||||
|
@ -1050,12 +1049,56 @@ void Network::requestConfiguration()
|
||||||
} else {
|
} else {
|
||||||
outp.append((unsigned char)0,16);
|
outp.append((unsigned char)0,16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RR->node->expectReplyTo(_inboundConfigPacketId = outp.packetId());
|
||||||
|
_inboundConfigChunks.clear();
|
||||||
|
|
||||||
outp.compress();
|
outp.compress();
|
||||||
RR->sw->send(outp,true);
|
RR->sw->send(outp,true);
|
||||||
|
}
|
||||||
|
|
||||||
// Expect replies with this in-re packet ID
|
bool Network::gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId)
|
||||||
_inboundConfigPacketId = outp.packetId();
|
{
|
||||||
_inboundConfigChunks.clear();
|
const uint64_t now = RR->node->now();
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
try {
|
||||||
|
if (_config) {
|
||||||
|
Membership &m = _membership(peer->address());
|
||||||
|
const bool allow = m.isAllowedOnNetwork(_config);
|
||||||
|
if (allow) {
|
||||||
|
m.sendCredentialsIfNeeded(RR,now,peer->address(),_config,(const Capability *)0);
|
||||||
|
if (m.shouldLikeMulticasts(now)) {
|
||||||
|
_announceMulticastGroupsTo(peer->address(),_allMulticastGroups());
|
||||||
|
m.likingMulticasts(now);
|
||||||
|
}
|
||||||
|
} else if (m.recentlyAllowedOnNetwork(_config)&&peer->rateGateRequestCredentials(now)) {
|
||||||
|
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
|
||||||
|
outp.append((uint8_t)verb);
|
||||||
|
outp.append(packetId);
|
||||||
|
outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
|
||||||
|
outp.append(_id);
|
||||||
|
RR->sw->send(outp,true);
|
||||||
|
}
|
||||||
|
return allow;
|
||||||
|
}
|
||||||
|
} catch ( ... ) {
|
||||||
|
TRACE("gate() check failed for peer %s: unexpected exception",peer->address().toString().c_str());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Network::gateMulticastGatherReply(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId)
|
||||||
|
{
|
||||||
|
return ( (peer->address() == controller()) || RR->topology->isUpstream(peer->identity()) || gate(peer,verb,packetId) || _config.isAnchor(peer->address()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Network::recentlyAllowedOnNetwork(const SharedPtr<Peer> &peer) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
const Membership *m = _memberships.get(peer->address());
|
||||||
|
if (m)
|
||||||
|
return m->recentlyAllowedOnNetwork(_config);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::clean()
|
void Network::clean()
|
||||||
|
@ -1131,7 +1174,22 @@ void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
|
||||||
const unsigned long tmp = (unsigned long)_multicastGroupsBehindMe.size();
|
const unsigned long tmp = (unsigned long)_multicastGroupsBehindMe.size();
|
||||||
_multicastGroupsBehindMe.set(mg,now);
|
_multicastGroupsBehindMe.set(mg,now);
|
||||||
if (tmp != _multicastGroupsBehindMe.size())
|
if (tmp != _multicastGroupsBehindMe.size())
|
||||||
_announceMulticastGroups(&mg);
|
_sendUpdatesToMembers(&mg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Network::addCredential(const CertificateOfMembership &com)
|
||||||
|
{
|
||||||
|
if (com.networkId() != _id)
|
||||||
|
return -1;
|
||||||
|
const Address a(com.issuedTo());
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
Membership &m = _membership(a);
|
||||||
|
const int result = m.addCredential(RR,com);
|
||||||
|
if (result == 0) {
|
||||||
|
m.sendCredentialsIfNeeded(RR,RR->node->now(),a,_config,(const Capability *)0);
|
||||||
|
RR->mc->addCredential(com,true);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::destroy()
|
void Network::destroy()
|
||||||
|
@ -1168,6 +1226,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
|
||||||
ec->status = _status();
|
ec->status = _status();
|
||||||
ec->type = (_config) ? (_config.isPrivate() ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC) : ZT_NETWORK_TYPE_PRIVATE;
|
ec->type = (_config) ? (_config.isPrivate() ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC) : ZT_NETWORK_TYPE_PRIVATE;
|
||||||
ec->mtu = ZT_IF_MTU;
|
ec->mtu = ZT_IF_MTU;
|
||||||
|
ec->physicalMtu = ZT_UDP_DEFAULT_PAYLOAD_MTU - (ZT_PACKET_IDX_PAYLOAD + 16);
|
||||||
ec->dhcp = 0;
|
ec->dhcp = 0;
|
||||||
std::vector<Address> ab(_config.activeBridges());
|
std::vector<Address> ab(_config.activeBridges());
|
||||||
ec->bridge = ((_config.allowPassiveBridging())||(std::find(ab.begin(),ab.end(),RR->identity.address()) != ab.end())) ? 1 : 0;
|
ec->bridge = ((_config.allowPassiveBridging())||(std::find(ab.begin(),ab.end(),RR->identity.address()) != ab.end())) ? 1 : 0;
|
||||||
|
@ -1196,40 +1255,25 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Network::_isAllowed(const SharedPtr<Peer> &peer) const
|
void Network::_sendUpdatesToMembers(const MulticastGroup *const newMulticastGroup)
|
||||||
{
|
|
||||||
// Assumes _lock is locked
|
|
||||||
try {
|
|
||||||
if (_config) {
|
|
||||||
const Membership *const m = _memberships.get(peer->address());
|
|
||||||
if (m)
|
|
||||||
return m->isAllowedOnNetwork(_config);
|
|
||||||
}
|
|
||||||
} catch ( ... ) {
|
|
||||||
TRACE("isAllowed() check failed for peer %s: unexpected exception",peer->address().toString().c_str());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Network::_announceMulticastGroups(const MulticastGroup *const onlyThis)
|
|
||||||
{
|
{
|
||||||
// Assumes _lock is locked
|
// Assumes _lock is locked
|
||||||
const uint64_t now = RR->node->now();
|
const uint64_t now = RR->node->now();
|
||||||
|
|
||||||
std::vector<MulticastGroup> groups;
|
std::vector<MulticastGroup> groups;
|
||||||
if (onlyThis)
|
if (newMulticastGroup)
|
||||||
groups.push_back(*onlyThis);
|
groups.push_back(*newMulticastGroup);
|
||||||
else groups = _allMulticastGroups();
|
else groups = _allMulticastGroups();
|
||||||
|
|
||||||
if ((onlyThis)||((now - _lastAnnouncedMulticastGroupsUpstream) >= ZT_MULTICAST_ANNOUNCE_PERIOD)) {
|
if ((newMulticastGroup)||((now - _lastAnnouncedMulticastGroupsUpstream) >= ZT_MULTICAST_ANNOUNCE_PERIOD)) {
|
||||||
if (!onlyThis)
|
if (!newMulticastGroup)
|
||||||
_lastAnnouncedMulticastGroupsUpstream = now;
|
_lastAnnouncedMulticastGroupsUpstream = now;
|
||||||
|
|
||||||
// Announce multicast groups to upstream peers (roots, etc.) and also send
|
// Announce multicast groups to upstream peers (roots, etc.) and also send
|
||||||
// them our COM so that MULTICAST_GATHER can be authenticated properly.
|
// them our COM so that MULTICAST_GATHER can be authenticated properly.
|
||||||
const std::vector<Address> upstreams(RR->topology->upstreamAddresses());
|
const std::vector<Address> upstreams(RR->topology->upstreamAddresses());
|
||||||
for(std::vector<Address>::const_iterator a(upstreams.begin());a!=upstreams.end();++a) {
|
for(std::vector<Address>::const_iterator a(upstreams.begin());a!=upstreams.end();++a) {
|
||||||
if ((_config.isPrivate())&&(_config.com)) {
|
if (_config.com) {
|
||||||
Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
||||||
_config.com.serialize(outp);
|
_config.com.serialize(outp);
|
||||||
outp.append((uint8_t)0x00);
|
outp.append((uint8_t)0x00);
|
||||||
|
@ -1238,12 +1282,17 @@ void Network::_announceMulticastGroups(const MulticastGroup *const onlyThis)
|
||||||
_announceMulticastGroupsTo(*a,groups);
|
_announceMulticastGroupsTo(*a,groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Announce to controller, which does not need our COM since it obviously
|
// Also announce to controller, and send COM to simplify and generalize behavior even though in theory it does not need it
|
||||||
// knows if we are a member. Of course if we already did or are going to
|
|
||||||
// below then we can skip it here.
|
|
||||||
const Address c(controller());
|
const Address c(controller());
|
||||||
if ( (std::find(upstreams.begin(),upstreams.end(),c) == upstreams.end()) && (!_memberships.contains(c)) )
|
if ( (std::find(upstreams.begin(),upstreams.end(),c) == upstreams.end()) && (!_memberships.contains(c)) ) {
|
||||||
|
if (_config.com) {
|
||||||
|
Packet outp(c,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
||||||
|
_config.com.serialize(outp);
|
||||||
|
outp.append((uint8_t)0x00);
|
||||||
|
RR->sw->send(outp,true);
|
||||||
|
}
|
||||||
_announceMulticastGroupsTo(c,groups);
|
_announceMulticastGroupsTo(c,groups);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that all "network anchors" have Membership records so we will
|
// Make sure that all "network anchors" have Membership records so we will
|
||||||
|
@ -1251,19 +1300,21 @@ void Network::_announceMulticastGroups(const MulticastGroup *const onlyThis)
|
||||||
// piecemeal on-demand fashion.
|
// piecemeal on-demand fashion.
|
||||||
const std::vector<Address> anchors(_config.anchors());
|
const std::vector<Address> anchors(_config.anchors());
|
||||||
for(std::vector<Address>::const_iterator a(anchors.begin());a!=anchors.end();++a)
|
for(std::vector<Address>::const_iterator a(anchors.begin());a!=anchors.end();++a)
|
||||||
_memberships[*a];
|
_membership(*a);
|
||||||
|
|
||||||
// Send MULTICAST_LIKE(s) to all members of this network
|
// Send credentials and multicast LIKEs to members, upstreams, and controller
|
||||||
{
|
{
|
||||||
Address *a = (Address *)0;
|
Address *a = (Address *)0;
|
||||||
Membership *m = (Membership *)0;
|
Membership *m = (Membership *)0;
|
||||||
Hashtable<Address,Membership>::Iterator i(_memberships);
|
Hashtable<Address,Membership>::Iterator i(_memberships);
|
||||||
while (i.next(a,m)) {
|
while (i.next(a,m)) {
|
||||||
if ((onlyThis)||(m->shouldLikeMulticasts(now))) {
|
if ( (m->recentlyAllowedOnNetwork(_config)) || (std::find(anchors.begin(),anchors.end(),*a) != anchors.end()) ) {
|
||||||
if (!onlyThis)
|
|
||||||
m->likingMulticasts(now);
|
|
||||||
m->sendCredentialsIfNeeded(RR,RR->node->now(),*a,_config,(const Capability *)0);
|
m->sendCredentialsIfNeeded(RR,RR->node->now(),*a,_config,(const Capability *)0);
|
||||||
_announceMulticastGroupsTo(*a,groups);
|
if ( ((newMulticastGroup)||(m->shouldLikeMulticasts(now))) && (m->isAllowedOnNetwork(_config)) ) {
|
||||||
|
if (!newMulticastGroup)
|
||||||
|
m->likingMulticasts(now);
|
||||||
|
_announceMulticastGroupsTo(*a,groups);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1310,15 +1361,7 @@ std::vector<MulticastGroup> Network::_allMulticastGroups() const
|
||||||
Membership &Network::_membership(const Address &a)
|
Membership &Network::_membership(const Address &a)
|
||||||
{
|
{
|
||||||
// assumes _lock is locked
|
// assumes _lock is locked
|
||||||
const unsigned long ms = _memberships.size();
|
return _memberships[a];
|
||||||
Membership &m = _memberships[a];
|
|
||||||
if (ms != _memberships.size()) {
|
|
||||||
const uint64_t now = RR->node->now();
|
|
||||||
m.sendCredentialsIfNeeded(RR,now,a,_config,(const Capability *)0);
|
|
||||||
_announceMulticastGroupsTo(a,_allMulticastGroups());
|
|
||||||
m.likingMulticasts(now);
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
|
@ -48,7 +48,6 @@ namespace ZeroTier {
|
||||||
|
|
||||||
class RuntimeEnvironment;
|
class RuntimeEnvironment;
|
||||||
class Peer;
|
class Peer;
|
||||||
class _MulticastAnnounceAll;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A virtual LAN
|
* A virtual LAN
|
||||||
|
@ -56,7 +55,6 @@ class _MulticastAnnounceAll;
|
||||||
class Network : NonCopyable
|
class Network : NonCopyable
|
||||||
{
|
{
|
||||||
friend class SharedPtr<Network>;
|
friend class SharedPtr<Network>;
|
||||||
friend class _MulticastAnnounceAll; // internal function object
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -250,14 +248,25 @@ public:
|
||||||
void requestConfiguration();
|
void requestConfiguration();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Membership check gate for incoming packets related to this network
|
||||||
|
*
|
||||||
* @param peer Peer to check
|
* @param peer Peer to check
|
||||||
|
* @param verb Packet verb
|
||||||
|
* @param packetId Packet ID
|
||||||
* @return True if peer is allowed to communicate on this network
|
* @return True if peer is allowed to communicate on this network
|
||||||
*/
|
*/
|
||||||
inline bool isAllowed(const SharedPtr<Peer> &peer) const
|
bool gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId);
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
/**
|
||||||
return _isAllowed(peer);
|
* Check whether this peer is allowed to provide multicast info for this network
|
||||||
}
|
*/
|
||||||
|
bool gateMulticastGatherReply(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param peer Peer to check
|
||||||
|
* @return True if peer has recently been a valid member of this network
|
||||||
|
*/
|
||||||
|
bool recentlyAllowedOnNetwork(const SharedPtr<Peer> &peer) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform cleanup and possibly save state
|
* Perform cleanup and possibly save state
|
||||||
|
@ -265,12 +274,12 @@ public:
|
||||||
void clean();
|
void clean();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Announce multicast groups to all members, anchors, etc.
|
* Push state to members such as multicast group memberships and latest COM (if needed)
|
||||||
*/
|
*/
|
||||||
inline void announceMulticastGroups()
|
inline void sendUpdatesToMembers()
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
_announceMulticastGroups((const MulticastGroup *)0);
|
_sendUpdatesToMembers((const MulticastGroup *)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -323,9 +332,7 @@ public:
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
const Address *const br = _remoteBridgeRoutes.get(mac);
|
const Address *const br = _remoteBridgeRoutes.get(mac);
|
||||||
if (br)
|
return ((br) ? *br : Address());
|
||||||
return *br;
|
|
||||||
return Address();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -348,13 +355,7 @@ public:
|
||||||
* @param com Certificate of membership
|
* @param com Certificate of membership
|
||||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||||
*/
|
*/
|
||||||
inline int addCredential(const CertificateOfMembership &com)
|
int addCredential(const CertificateOfMembership &com);
|
||||||
{
|
|
||||||
if (com.networkId() != _id)
|
|
||||||
return -1;
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return _membership(com.issuedTo()).addCredential(RR,com);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param cap Capability
|
* @param cap Capability
|
||||||
|
@ -408,11 +409,11 @@ public:
|
||||||
private:
|
private:
|
||||||
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 _isAllowed(const SharedPtr<Peer> &peer) const;
|
bool _gate(const SharedPtr<Peer> &peer);
|
||||||
void _announceMulticastGroups(const MulticastGroup *const onlyThis);
|
void _sendUpdatesToMembers(const MulticastGroup *const newMulticastGroup);
|
||||||
void _announceMulticastGroupsTo(const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
|
void _announceMulticastGroupsTo(const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
|
||||||
std::vector<MulticastGroup> _allMulticastGroups() const;
|
std::vector<MulticastGroup> _allMulticastGroups() const;
|
||||||
Membership &_membership(const Address &a); // also lazily sends COM and MULTICAST_LIKE(s) if this is a new member
|
Membership &_membership(const Address &a);
|
||||||
|
|
||||||
const RuntimeEnvironment *RR;
|
const RuntimeEnvironment *RR;
|
||||||
void *_uPtr;
|
void *_uPtr;
|
||||||
|
|
|
@ -108,9 +108,13 @@ namespace ZeroTier {
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION "v"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION "v"
|
||||||
// Protocol version (see Packet.hpp)
|
// Protocol version (see Packet.hpp)
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION "pv"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION "pv"
|
||||||
// Software major, minor, revision
|
// Software vendor
|
||||||
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_VENDOR "vend"
|
||||||
|
// Software major version
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION "majv"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION "majv"
|
||||||
|
// Software minor version
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION "minv"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION "minv"
|
||||||
|
// Software revision
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION "revv"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION "revv"
|
||||||
// Rules engine revision
|
// Rules engine revision
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV "revr"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV "revr"
|
||||||
|
@ -123,9 +127,11 @@ namespace ZeroTier {
|
||||||
// Maximum number of tags this node can accept
|
// Maximum number of tags this node can accept
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS "mt"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS "mt"
|
||||||
// Network join authorization token (if any)
|
// Network join authorization token (if any)
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH_TOKEN "atok"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH "a"
|
||||||
// Network configuration meta-data flags
|
// Network configuration meta-data flags
|
||||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS "f"
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS "f"
|
||||||
|
// Relay policy for this node
|
||||||
|
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_RELAY_POLICY "rp"
|
||||||
|
|
||||||
// These dictionary keys are short so they don't take up much room.
|
// These dictionary keys are short so they don't take up much room.
|
||||||
// By convention we use upper case for binary blobs, but it doesn't really matter.
|
// By convention we use upper case for binary blobs, but it doesn't really matter.
|
||||||
|
@ -285,6 +291,19 @@ public:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param a Address to check
|
||||||
|
* @return True if address is an anchor
|
||||||
|
*/
|
||||||
|
inline bool isAnchor(const Address &a) const
|
||||||
|
{
|
||||||
|
for(unsigned int i=0;i<specialistCount;++i) {
|
||||||
|
if ((a == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param fromPeer Peer attempting to bridge other Ethernet peers onto network
|
* @param fromPeer Peer attempting to bridge other Ethernet peers onto network
|
||||||
* @return True if this network allows bridging
|
* @return True if this network allows bridging
|
||||||
|
|
|
@ -71,10 +71,14 @@ Node::Node(
|
||||||
_prngStreamPtr(0),
|
_prngStreamPtr(0),
|
||||||
_now(now),
|
_now(now),
|
||||||
_lastPingCheck(0),
|
_lastPingCheck(0),
|
||||||
_lastHousekeepingRun(0)
|
_lastHousekeepingRun(0),
|
||||||
|
_relayPolicy(ZT_RELAY_POLICY_TRUSTED)
|
||||||
{
|
{
|
||||||
_online = false;
|
_online = false;
|
||||||
|
|
||||||
|
memset(_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr));
|
||||||
|
memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo));
|
||||||
|
|
||||||
// Use Salsa20 alone as a high-quality non-crypto PRNG
|
// Use Salsa20 alone as a high-quality non-crypto PRNG
|
||||||
{
|
{
|
||||||
char foo[32];
|
char foo[32];
|
||||||
|
@ -115,6 +119,9 @@ Node::Node(
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RR->topology->amRoot())
|
||||||
|
_relayPolicy = ZT_RELAY_POLICY_ALWAYS;
|
||||||
|
|
||||||
postEvent(ZT_EVENT_UP);
|
postEvent(ZT_EVENT_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +135,7 @@ Node::~Node()
|
||||||
delete RR->topology;
|
delete RR->topology;
|
||||||
delete RR->mc;
|
delete RR->mc;
|
||||||
delete RR->sw;
|
delete RR->sw;
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
delete RR->cluster;
|
delete RR->cluster;
|
||||||
#endif
|
#endif
|
||||||
|
@ -263,7 +271,7 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
|
||||||
for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
|
for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
|
||||||
if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig()))
|
if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig()))
|
||||||
needConfig.push_back(n->second);
|
needConfig.push_back(n->second);
|
||||||
n->second->announceMulticastGroups();
|
n->second->sendUpdatesToMembers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
|
for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
|
||||||
|
@ -316,6 +324,12 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
|
||||||
return ZT_RESULT_OK;
|
return ZT_RESULT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZT_ResultCode Node::setRelayPolicy(enum ZT_RelayPolicy rp)
|
||||||
|
{
|
||||||
|
_relayPolicy = rp;
|
||||||
|
return ZT_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
ZT_ResultCode Node::join(uint64_t nwid,void *uptr)
|
ZT_ResultCode Node::join(uint64_t nwid,void *uptr)
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_networks_m);
|
Mutex::Lock _l(_networks_m);
|
||||||
|
@ -821,6 +835,15 @@ enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,vol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ZT_ResultCode ZT_Node_setRelayPolicy(ZT_Node *node,enum ZT_RelayPolicy rp)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return reinterpret_cast<ZeroTier::Node *>(node)->setRelayPolicy(rp);
|
||||||
|
} catch ( ... ) {
|
||||||
|
return ZT_RESULT_FATAL_ERROR_INTERNAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr)
|
enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -44,6 +44,10 @@
|
||||||
#define TRACE(f,...) {}
|
#define TRACE(f,...) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Bit mask for "expecting reply" hash
|
||||||
|
#define ZT_EXPECTING_REPLIES_BUCKET_MASK1 255
|
||||||
|
#define ZT_EXPECTING_REPLIES_BUCKET_MASK2 31
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,6 +91,7 @@ public:
|
||||||
unsigned int frameLength,
|
unsigned int frameLength,
|
||||||
volatile uint64_t *nextBackgroundTaskDeadline);
|
volatile uint64_t *nextBackgroundTaskDeadline);
|
||||||
ZT_ResultCode processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
|
ZT_ResultCode processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
|
||||||
|
ZT_ResultCode setRelayPolicy(enum ZT_RelayPolicy rp);
|
||||||
ZT_ResultCode join(uint64_t nwid,void *uptr);
|
ZT_ResultCode join(uint64_t nwid,void *uptr);
|
||||||
ZT_ResultCode leave(uint64_t nwid,void **uptr);
|
ZT_ResultCode leave(uint64_t nwid,void **uptr);
|
||||||
ZT_ResultCode multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
ZT_ResultCode multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
||||||
|
@ -241,6 +246,7 @@ public:
|
||||||
inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
|
inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
|
||||||
|
|
||||||
inline bool online() const throw() { return _online; }
|
inline bool online() const throw() { return _online; }
|
||||||
|
inline ZT_RelayPolicy relayPolicy() const { return _relayPolicy; }
|
||||||
|
|
||||||
#ifdef ZT_TRACE
|
#ifdef ZT_TRACE
|
||||||
void postTrace(const char *module,unsigned int line,const char *fmt,...);
|
void postTrace(const char *module,unsigned int line,const char *fmt,...);
|
||||||
|
@ -250,6 +256,33 @@ public:
|
||||||
void postCircuitTestReport(const ZT_CircuitTestReport *report);
|
void postCircuitTestReport(const ZT_CircuitTestReport *report);
|
||||||
void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
|
void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register that we are expecting a reply to a packet ID
|
||||||
|
*
|
||||||
|
* @param packetId Packet ID to expect reply to
|
||||||
|
*/
|
||||||
|
inline void expectReplyTo(const uint64_t packetId)
|
||||||
|
{
|
||||||
|
const unsigned long bucket = (unsigned long)(packetId & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
|
||||||
|
_expectingRepliesTo[bucket][_expectingRepliesToBucketPtr[bucket]++ & ZT_EXPECTING_REPLIES_BUCKET_MASK2] = packetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a given packet ID is something we are expecting a reply to
|
||||||
|
*
|
||||||
|
* @param packetId Packet ID to check
|
||||||
|
* @return True if we're expecting a reply
|
||||||
|
*/
|
||||||
|
inline bool expectingReplyTo(const uint64_t packetId) const
|
||||||
|
{
|
||||||
|
const unsigned long bucket = (unsigned long)(packetId & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
|
||||||
|
for(unsigned long i=0;i<=ZT_EXPECTING_REPLIES_BUCKET_MASK2;++i) {
|
||||||
|
if (_expectingRepliesTo[bucket][i] == packetId)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline SharedPtr<Network> _network(uint64_t nwid) const
|
inline SharedPtr<Network> _network(uint64_t nwid) const
|
||||||
{
|
{
|
||||||
|
@ -266,6 +299,9 @@ private:
|
||||||
|
|
||||||
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
|
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
|
||||||
|
|
||||||
|
uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1];
|
||||||
|
uint64_t _expectingRepliesTo[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1][ZT_EXPECTING_REPLIES_BUCKET_MASK2 + 1];
|
||||||
|
|
||||||
ZT_DataStoreGetFunction _dataStoreGetFunction;
|
ZT_DataStoreGetFunction _dataStoreGetFunction;
|
||||||
ZT_DataStorePutFunction _dataStorePutFunction;
|
ZT_DataStorePutFunction _dataStorePutFunction;
|
||||||
ZT_WirePacketSendFunction _wirePacketSendFunction;
|
ZT_WirePacketSendFunction _wirePacketSendFunction;
|
||||||
|
@ -292,6 +328,7 @@ private:
|
||||||
uint64_t _now;
|
uint64_t _now;
|
||||||
uint64_t _lastPingCheck;
|
uint64_t _lastPingCheck;
|
||||||
uint64_t _lastHousekeepingRun;
|
uint64_t _lastHousekeepingRun;
|
||||||
|
ZT_RelayPolicy _relayPolicy;
|
||||||
bool _online;
|
bool _online;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,7 @@ void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toA
|
||||||
//TRACE(">>MC %.16llx -> %s",(unsigned long long)this,toAddr.toString().c_str());
|
//TRACE(">>MC %.16llx -> %s",(unsigned long long)this,toAddr.toString().c_str());
|
||||||
_packet.newInitializationVector();
|
_packet.newInitializationVector();
|
||||||
_packet.setDestination(toAddr2);
|
_packet.setDestination(toAddr2);
|
||||||
|
RR->node->expectReplyTo(_packet.packetId());
|
||||||
RR->sw->send(_packet,true);
|
RR->sw->send(_packet,true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ const char *Packet::errorString(ErrorCode e)
|
||||||
case ERROR_OBJ_NOT_FOUND: return "OBJECT_NOT_FOUND";
|
case ERROR_OBJ_NOT_FOUND: return "OBJECT_NOT_FOUND";
|
||||||
case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION";
|
case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION";
|
||||||
case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
|
case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
|
||||||
|
case ERROR_NEED_MEMBERSHIP_CERTIFICATE: return "NEED_MEMBERSHIP_CERTIFICATE";
|
||||||
case ERROR_NETWORK_ACCESS_DENIED_: return "NETWORK_ACCESS_DENIED";
|
case ERROR_NETWORK_ACCESS_DENIED_: return "NETWORK_ACCESS_DENIED";
|
||||||
case ERROR_UNWANTED_MULTICAST: return "UNWANTED_MULTICAST";
|
case ERROR_UNWANTED_MULTICAST: return "UNWANTED_MULTICAST";
|
||||||
}
|
}
|
||||||
|
|
|
@ -965,7 +965,7 @@ public:
|
||||||
* <[2] 16-bit reporter OS/platform or 0 if not specified>
|
* <[2] 16-bit reporter OS/platform or 0 if not specified>
|
||||||
* <[2] 16-bit reporter architecture or 0 if not specified>
|
* <[2] 16-bit reporter architecture or 0 if not specified>
|
||||||
* <[2] 16-bit error code (set to 0, currently unused)>
|
* <[2] 16-bit error code (set to 0, currently unused)>
|
||||||
* <[8] 64-bit report flags (set to 0, currently unused)>
|
* <[8] 64-bit report flags>
|
||||||
* <[8] 64-bit packet ID of received CIRCUIT_TEST packet>
|
* <[8] 64-bit packet ID of received CIRCUIT_TEST packet>
|
||||||
* <[5] upstream ZeroTier address from which CIRCUIT_TEST was received>
|
* <[5] upstream ZeroTier address from which CIRCUIT_TEST was received>
|
||||||
* <[1] 8-bit packet hop count of received CIRCUIT_TEST>
|
* <[1] 8-bit packet hop count of received CIRCUIT_TEST>
|
||||||
|
@ -980,6 +980,9 @@ public:
|
||||||
* <[5] ZeroTier address of next hop>
|
* <[5] ZeroTier address of next hop>
|
||||||
* <[...] current best direct path address, if any, 0 if none>
|
* <[...] current best direct path address, if any, 0 if none>
|
||||||
*
|
*
|
||||||
|
* Report flags:
|
||||||
|
* 0x1 - Upstream peer in circuit test path allowed in path (e.g. network COM valid)
|
||||||
|
*
|
||||||
* Circuit test reports can be sent by hops in a circuit test to report
|
* Circuit test reports can be sent by hops in a circuit test to report
|
||||||
* back results. They should include information about the sender as well
|
* back results. They should include information about the sender as well
|
||||||
* as about the paths to which next hops are being sent.
|
* as about the paths to which next hops are being sent.
|
||||||
|
@ -1067,6 +1070,9 @@ public:
|
||||||
/* Verb or use case not supported/enabled by this node */
|
/* Verb or use case not supported/enabled by this node */
|
||||||
ERROR_UNSUPPORTED_OPERATION = 0x05,
|
ERROR_UNSUPPORTED_OPERATION = 0x05,
|
||||||
|
|
||||||
|
/* Network membership certificate update needed */
|
||||||
|
ERROR_NEED_MEMBERSHIP_CERTIFICATE = 0x06,
|
||||||
|
|
||||||
/* Tried to join network, but you're not a member */
|
/* Tried to join network, but you're not a member */
|
||||||
ERROR_NETWORK_ACCESS_DENIED_ = 0x07, /* extra _ at end to avoid Windows name conflict */
|
ERROR_NETWORK_ACCESS_DENIED_ = 0x07, /* extra _ at end to avoid Windows name conflict */
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,7 @@ public:
|
||||||
Path() :
|
Path() :
|
||||||
_lastOut(0),
|
_lastOut(0),
|
||||||
_lastIn(0),
|
_lastIn(0),
|
||||||
|
_lastTrustEstablishedPacketReceived(0),
|
||||||
_addr(),
|
_addr(),
|
||||||
_localAddress(),
|
_localAddress(),
|
||||||
_ipScope(InetAddress::IP_SCOPE_NONE)
|
_ipScope(InetAddress::IP_SCOPE_NONE)
|
||||||
|
@ -113,6 +114,7 @@ public:
|
||||||
Path(const InetAddress &localAddress,const InetAddress &addr) :
|
Path(const InetAddress &localAddress,const InetAddress &addr) :
|
||||||
_lastOut(0),
|
_lastOut(0),
|
||||||
_lastIn(0),
|
_lastIn(0),
|
||||||
|
_lastTrustEstablishedPacketReceived(0),
|
||||||
_addr(addr),
|
_addr(addr),
|
||||||
_localAddress(localAddress),
|
_localAddress(localAddress),
|
||||||
_ipScope(addr.ipScope())
|
_ipScope(addr.ipScope())
|
||||||
|
@ -126,6 +128,11 @@ public:
|
||||||
*/
|
*/
|
||||||
inline void received(const uint64_t t) { _lastIn = t; }
|
inline void received(const uint64_t t) { _lastIn = t; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set time last trusted packet was received (done in Peer::received())
|
||||||
|
*/
|
||||||
|
inline void trustedPacketReceived(const uint64_t t) { _lastTrustEstablishedPacketReceived = t; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a packet via this path (last out time is also updated)
|
* Send a packet via this path (last out time is also updated)
|
||||||
*
|
*
|
||||||
|
@ -159,6 +166,11 @@ public:
|
||||||
*/
|
*/
|
||||||
inline InetAddress::IpScope ipScope() const { return _ipScope; }
|
inline InetAddress::IpScope ipScope() const { return _ipScope; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if path has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
|
||||||
|
*/
|
||||||
|
inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Preference rank, higher == better
|
* @return Preference rank, higher == better
|
||||||
*/
|
*/
|
||||||
|
@ -232,6 +244,7 @@ public:
|
||||||
private:
|
private:
|
||||||
uint64_t _lastOut;
|
uint64_t _lastOut;
|
||||||
uint64_t _lastIn;
|
uint64_t _lastIn;
|
||||||
|
uint64_t _lastTrustEstablishedPacketReceived;
|
||||||
InetAddress _addr;
|
InetAddress _addr;
|
||||||
InetAddress _localAddress;
|
InetAddress _localAddress;
|
||||||
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
|
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
|
||||||
|
|
173
node/Peer.cpp
173
node/Peer.cpp
|
@ -47,6 +47,12 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
|
||||||
_lastMulticastFrame(0),
|
_lastMulticastFrame(0),
|
||||||
_lastDirectPathPushSent(0),
|
_lastDirectPathPushSent(0),
|
||||||
_lastDirectPathPushReceive(0),
|
_lastDirectPathPushReceive(0),
|
||||||
|
_lastCredentialRequestSent(0),
|
||||||
|
_lastWhoisRequestReceived(0),
|
||||||
|
_lastEchoRequestReceived(0),
|
||||||
|
_lastComRequestReceived(0),
|
||||||
|
_lastCredentialsReceived(0),
|
||||||
|
_lastTrustEstablishedPacketReceived(0),
|
||||||
RR(renv),
|
RR(renv),
|
||||||
_remoteClusterOptimal4(0),
|
_remoteClusterOptimal4(0),
|
||||||
_vProto(0),
|
_vProto(0),
|
||||||
|
@ -56,7 +62,8 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
|
||||||
_id(peerIdentity),
|
_id(peerIdentity),
|
||||||
_numPaths(0),
|
_numPaths(0),
|
||||||
_latency(0),
|
_latency(0),
|
||||||
_directPathPushCutoffCount(0)
|
_directPathPushCutoffCount(0),
|
||||||
|
_credentialsCutoffCount(0)
|
||||||
{
|
{
|
||||||
memset(_remoteClusterOptimal6,0,sizeof(_remoteClusterOptimal6));
|
memset(_remoteClusterOptimal6,0,sizeof(_remoteClusterOptimal6));
|
||||||
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
|
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
|
||||||
|
@ -126,6 +133,11 @@ void Peer::received(
|
||||||
else if (verb == Packet::VERB_MULTICAST_FRAME)
|
else if (verb == Packet::VERB_MULTICAST_FRAME)
|
||||||
_lastMulticastFrame = now;
|
_lastMulticastFrame = now;
|
||||||
|
|
||||||
|
if (trustEstablished) {
|
||||||
|
_lastTrustEstablishedPacketReceived = now;
|
||||||
|
path->trustedPacketReceived(now);
|
||||||
|
}
|
||||||
|
|
||||||
if (hops == 0) {
|
if (hops == 0) {
|
||||||
bool pathIsConfirmed = false;
|
bool pathIsConfirmed = false;
|
||||||
{
|
{
|
||||||
|
@ -194,7 +206,80 @@ void Peer::received(
|
||||||
}
|
}
|
||||||
} else if (trustEstablished) {
|
} else if (trustEstablished) {
|
||||||
// Send PUSH_DIRECT_PATHS if hops>0 (relayed) and we have a trust relationship (common network membership)
|
// Send PUSH_DIRECT_PATHS if hops>0 (relayed) and we have a trust relationship (common network membership)
|
||||||
_pushDirectPaths(path,now);
|
#ifdef ZT_ENABLE_CLUSTER
|
||||||
|
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
|
||||||
|
const bool haveCluster = (RR->cluster);
|
||||||
|
#else
|
||||||
|
const bool haveCluster = false;
|
||||||
|
#endif
|
||||||
|
if ( ((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL) && (!haveCluster) ) {
|
||||||
|
_lastDirectPathPushSent = now;
|
||||||
|
|
||||||
|
std::vector<InetAddress> pathsToPush;
|
||||||
|
|
||||||
|
std::vector<InetAddress> dps(RR->node->directPaths());
|
||||||
|
for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i)
|
||||||
|
pathsToPush.push_back(*i);
|
||||||
|
|
||||||
|
std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
|
||||||
|
for(unsigned long i=0,added=0;i<sym.size();++i) {
|
||||||
|
InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
|
||||||
|
if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
|
||||||
|
pathsToPush.push_back(tmp);
|
||||||
|
if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathsToPush.size() > 0) {
|
||||||
|
#ifdef ZT_TRACE
|
||||||
|
std::string ps;
|
||||||
|
for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
|
||||||
|
if (ps.length() > 0)
|
||||||
|
ps.push_back(',');
|
||||||
|
ps.append(p->toString());
|
||||||
|
}
|
||||||
|
TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
|
||||||
|
while (p != pathsToPush.end()) {
|
||||||
|
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
|
||||||
|
outp.addSize(2); // leave room for count
|
||||||
|
|
||||||
|
unsigned int count = 0;
|
||||||
|
while ((p != pathsToPush.end())&&((outp.size() + 24) < 1200)) {
|
||||||
|
uint8_t addressType = 4;
|
||||||
|
switch(p->ss_family) {
|
||||||
|
case AF_INET:
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
addressType = 6;
|
||||||
|
break;
|
||||||
|
default: // we currently only push IP addresses
|
||||||
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
outp.append((uint8_t)0); // no flags
|
||||||
|
outp.append((uint16_t)0); // no extensions
|
||||||
|
outp.append(addressType);
|
||||||
|
outp.append((uint8_t)((addressType == 4) ? 6 : 18));
|
||||||
|
outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
|
||||||
|
outp.append((uint16_t)p->port());
|
||||||
|
|
||||||
|
++count;
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count) {
|
||||||
|
outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
|
||||||
|
outp.armor(_key,true);
|
||||||
|
path->send(RR,outp.data(),outp.size(),now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,6 +351,7 @@ void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,u
|
||||||
atAddress.serialize(outp);
|
atAddress.serialize(outp);
|
||||||
outp.append((uint64_t)RR->topology->worldId());
|
outp.append((uint64_t)RR->topology->worldId());
|
||||||
outp.append((uint64_t)RR->topology->worldTimestamp());
|
outp.append((uint64_t)RR->topology->worldTimestamp());
|
||||||
|
RR->node->expectReplyTo(outp.packetId());
|
||||||
outp.armor(_key,false); // HELLO is sent in the clear
|
outp.armor(_key,false); // HELLO is sent in the clear
|
||||||
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
||||||
}
|
}
|
||||||
|
@ -274,6 +360,7 @@ void Peer::attemptToContactAt(const InetAddress &localAddr,const InetAddress &at
|
||||||
{
|
{
|
||||||
if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
|
if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
|
||||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
|
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
|
||||||
|
RR->node->expectReplyTo(outp.packetId());
|
||||||
outp.armor(_key,true);
|
outp.armor(_key,true);
|
||||||
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
||||||
} else {
|
} else {
|
||||||
|
@ -366,86 +453,4 @@ void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6)
|
||||||
v6 = _paths[bestp6].path->address();
|
v6 = _paths[bestp6].path->address();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Peer::_pushDirectPaths(const SharedPtr<Path> &path,uint64_t now)
|
|
||||||
{
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
|
||||||
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
|
|
||||||
if (RR->cluster)
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((now - _lastDirectPathPushSent) < ZT_DIRECT_PATH_PUSH_INTERVAL)
|
|
||||||
return false;
|
|
||||||
else _lastDirectPathPushSent = now;
|
|
||||||
|
|
||||||
std::vector<InetAddress> pathsToPush;
|
|
||||||
|
|
||||||
std::vector<InetAddress> dps(RR->node->directPaths());
|
|
||||||
for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i)
|
|
||||||
pathsToPush.push_back(*i);
|
|
||||||
|
|
||||||
std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
|
|
||||||
for(unsigned long i=0,added=0;i<sym.size();++i) {
|
|
||||||
InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
|
|
||||||
if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
|
|
||||||
pathsToPush.push_back(tmp);
|
|
||||||
if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pathsToPush.empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#ifdef ZT_TRACE
|
|
||||||
{
|
|
||||||
std::string ps;
|
|
||||||
for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
|
|
||||||
if (ps.length() > 0)
|
|
||||||
ps.push_back(',');
|
|
||||||
ps.append(p->toString());
|
|
||||||
}
|
|
||||||
TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
|
|
||||||
while (p != pathsToPush.end()) {
|
|
||||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
|
|
||||||
outp.addSize(2); // leave room for count
|
|
||||||
|
|
||||||
unsigned int count = 0;
|
|
||||||
while ((p != pathsToPush.end())&&((outp.size() + 24) < 1200)) {
|
|
||||||
uint8_t addressType = 4;
|
|
||||||
switch(p->ss_family) {
|
|
||||||
case AF_INET:
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
addressType = 6;
|
|
||||||
break;
|
|
||||||
default: // we currently only push IP addresses
|
|
||||||
++p;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
outp.append((uint8_t)0); // no flags
|
|
||||||
outp.append((uint16_t)0); // no extensions
|
|
||||||
outp.append(addressType);
|
|
||||||
outp.append((uint8_t)((addressType == 4) ? 6 : 18));
|
|
||||||
outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
|
|
||||||
outp.append((uint16_t)p->port());
|
|
||||||
|
|
||||||
++count;
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count) {
|
|
||||||
outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
|
|
||||||
outp.armor(_key,true);
|
|
||||||
path->send(RR,outp.data(),outp.size(),now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
|
@ -312,7 +312,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* @return 256-bit secret symmetric encryption key
|
* @return 256-bit secret symmetric encryption key
|
||||||
*/
|
*/
|
||||||
inline const unsigned char *key() const throw() { return _key; }
|
inline const unsigned char *key() const { return _key; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the currently known remote version of this peer's client
|
* Set the currently known remote version of this peer's client
|
||||||
|
@ -330,25 +330,22 @@ public:
|
||||||
_vRevision = (uint16_t)vrev;
|
_vRevision = (uint16_t)vrev;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline unsigned int remoteVersionProtocol() const throw() { return _vProto; }
|
inline unsigned int remoteVersionProtocol() const { return _vProto; }
|
||||||
inline unsigned int remoteVersionMajor() const throw() { return _vMajor; }
|
inline unsigned int remoteVersionMajor() const { return _vMajor; }
|
||||||
inline unsigned int remoteVersionMinor() const throw() { return _vMinor; }
|
inline unsigned int remoteVersionMinor() const { return _vMinor; }
|
||||||
inline unsigned int remoteVersionRevision() const throw() { return _vRevision; }
|
inline unsigned int remoteVersionRevision() const { return _vRevision; }
|
||||||
|
|
||||||
inline bool remoteVersionKnown() const throw() { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
|
inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update direct path push stats and return true if we should respond
|
* @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
|
||||||
*
|
|
||||||
* This is a circuit breaker to make VERB_PUSH_DIRECT_PATHS not particularly
|
|
||||||
* useful as a DDOS amplification attack vector. Otherwise a malicious peer
|
|
||||||
* could send loads of these and cause others to bombard arbitrary IPs with
|
|
||||||
* traffic.
|
|
||||||
*
|
|
||||||
* @param now Current time
|
|
||||||
* @return True if we should respond
|
|
||||||
*/
|
*/
|
||||||
inline bool shouldRespondToDirectPathPush(const uint64_t now)
|
inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rate limit gate for VERB_PUSH_DIRECT_PATHS
|
||||||
|
*/
|
||||||
|
inline bool rateGatePushDirectPaths(const uint64_t now)
|
||||||
{
|
{
|
||||||
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
|
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
|
||||||
++_directPathPushCutoffCount;
|
++_directPathPushCutoffCount;
|
||||||
|
@ -357,6 +354,66 @@ public:
|
||||||
return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
|
return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rate limit gate for VERB_NETWORK_CREDENTIALS
|
||||||
|
*/
|
||||||
|
inline bool rateGateCredentialsReceived(const uint64_t now)
|
||||||
|
{
|
||||||
|
if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
|
||||||
|
++_credentialsCutoffCount;
|
||||||
|
else _credentialsCutoffCount = 0;
|
||||||
|
_lastCredentialsReceived = now;
|
||||||
|
return (_directPathPushCutoffCount < ZT_PEER_CREDEITIALS_CUTOFF_LIMIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
|
||||||
|
*/
|
||||||
|
inline bool rateGateRequestCredentials(const uint64_t now)
|
||||||
|
{
|
||||||
|
if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||||
|
_lastCredentialRequestSent = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rate limit gate for inbound WHOIS requests
|
||||||
|
*/
|
||||||
|
inline bool rateGateInboundWhoisRequest(const uint64_t now)
|
||||||
|
{
|
||||||
|
if ((now - _lastWhoisRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||||
|
_lastWhoisRequestReceived = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rate limit gate for inbound ECHO requests
|
||||||
|
*/
|
||||||
|
inline bool rateGateEchoRequest(const uint64_t now)
|
||||||
|
{
|
||||||
|
if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||||
|
_lastEchoRequestReceived = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rate gate requests for network COM
|
||||||
|
*/
|
||||||
|
inline bool rateGateComRequest(const uint64_t now)
|
||||||
|
{
|
||||||
|
if ((now - _lastComRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||||
|
_lastComRequestReceived = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a common set of addresses by which two peers can link, if any
|
* Find a common set of addresses by which two peers can link, if any
|
||||||
*
|
*
|
||||||
|
@ -378,8 +435,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _pushDirectPaths(const SharedPtr<Path> &path,uint64_t now);
|
|
||||||
|
|
||||||
inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const
|
inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const
|
||||||
{
|
{
|
||||||
uint64_t s = ZT_PEER_PING_PERIOD + _paths[p].lastReceive + (uint64_t)(_paths[p].path->preferenceRank() * (ZT_PEER_PING_PERIOD / ZT_PATH_MAX_PREFERENCE_RANK));
|
uint64_t s = ZT_PEER_PING_PERIOD + _paths[p].lastReceive + (uint64_t)(_paths[p].path->preferenceRank() * (ZT_PEER_PING_PERIOD / ZT_PATH_MAX_PREFERENCE_RANK));
|
||||||
|
@ -415,6 +470,12 @@ private:
|
||||||
uint64_t _lastMulticastFrame;
|
uint64_t _lastMulticastFrame;
|
||||||
uint64_t _lastDirectPathPushSent;
|
uint64_t _lastDirectPathPushSent;
|
||||||
uint64_t _lastDirectPathPushReceive;
|
uint64_t _lastDirectPathPushReceive;
|
||||||
|
uint64_t _lastCredentialRequestSent;
|
||||||
|
uint64_t _lastWhoisRequestReceived;
|
||||||
|
uint64_t _lastEchoRequestReceived;
|
||||||
|
uint64_t _lastComRequestReceived;
|
||||||
|
uint64_t _lastCredentialsReceived;
|
||||||
|
uint64_t _lastTrustEstablishedPacketReceived;
|
||||||
const RuntimeEnvironment *RR;
|
const RuntimeEnvironment *RR;
|
||||||
uint32_t _remoteClusterOptimal4;
|
uint32_t _remoteClusterOptimal4;
|
||||||
uint16_t _vProto;
|
uint16_t _vProto;
|
||||||
|
@ -433,6 +494,7 @@ private:
|
||||||
unsigned int _numPaths;
|
unsigned int _numPaths;
|
||||||
unsigned int _latency;
|
unsigned int _latency;
|
||||||
unsigned int _directPathPushCutoffCount;
|
unsigned int _directPathPushCutoffCount;
|
||||||
|
unsigned int _credentialsCutoffCount;
|
||||||
|
|
||||||
AtomicCounter __refCount;
|
AtomicCounter __refCount;
|
||||||
};
|
};
|
||||||
|
|
|
@ -105,7 +105,18 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||||
const Address destination(fragment.destination());
|
const Address destination(fragment.destination());
|
||||||
|
|
||||||
if (destination != RR->identity.address()) {
|
if (destination != RR->identity.address()) {
|
||||||
// Fragment is not for us, so try to relay it
|
switch(RR->node->relayPolicy()) {
|
||||||
|
case ZT_RELAY_POLICY_ALWAYS:
|
||||||
|
break;
|
||||||
|
case ZT_RELAY_POLICY_TRUSTED:
|
||||||
|
if (!path->trustEstablished(now))
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
// case ZT_RELAY_POLICY_NEVER:
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
|
if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
|
||||||
fragment.incrementHops();
|
fragment.incrementHops();
|
||||||
|
|
||||||
|
@ -203,9 +214,20 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||||
//TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size());
|
//TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size());
|
||||||
|
|
||||||
if (destination != RR->identity.address()) {
|
if (destination != RR->identity.address()) {
|
||||||
|
switch(RR->node->relayPolicy()) {
|
||||||
|
case ZT_RELAY_POLICY_ALWAYS:
|
||||||
|
break;
|
||||||
|
case ZT_RELAY_POLICY_TRUSTED:
|
||||||
|
if (!path->trustEstablished(now))
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
// case ZT_RELAY_POLICY_NEVER:
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Packet packet(data,len);
|
Packet packet(data,len);
|
||||||
|
|
||||||
// Packet is not for us, so try to relay it
|
|
||||||
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
|
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
|
||||||
packet.incrementHops();
|
packet.incrementHops();
|
||||||
|
|
||||||
|
@ -327,6 +349,11 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to.isMulticast()) {
|
if (to.isMulticast()) {
|
||||||
|
if (network->config().multicastLimit == 0) {
|
||||||
|
TRACE("%.16llx: dropped multicast: not allowed on network",network->id());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Destination is a multicast address (including broadcast)
|
// Destination is a multicast address (including broadcast)
|
||||||
MulticastGroup mg(to,0);
|
MulticastGroup mg(to,0);
|
||||||
|
|
||||||
|
@ -734,13 +761,12 @@ unsigned long Switch::doTimerTasks(uint64_t now)
|
||||||
|
|
||||||
Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
|
Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
|
||||||
{
|
{
|
||||||
SharedPtr<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
|
SharedPtr<Peer> upstream(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
|
||||||
if (root) {
|
if (upstream) {
|
||||||
Packet outp(root->address(),RR->identity.address(),Packet::VERB_WHOIS);
|
Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS);
|
||||||
addr.appendTo(outp);
|
addr.appendTo(outp);
|
||||||
outp.armor(root->key(),true);
|
RR->node->expectReplyTo(outp.packetId());
|
||||||
if (root->sendDirect(outp.data(),outp.size(),RR->node->now(),true))
|
send(outp,true);
|
||||||
return root->address();
|
|
||||||
}
|
}
|
||||||
return Address();
|
return Address();
|
||||||
}
|
}
|
||||||
|
@ -760,11 +786,8 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
|
||||||
|
|
||||||
SharedPtr<Path> viaPath(peer->getBestPath(now,false));
|
SharedPtr<Path> viaPath(peer->getBestPath(now,false));
|
||||||
if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isRoot(peer->identity())) ) {
|
if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isRoot(peer->identity())) ) {
|
||||||
if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) >> 2,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL)) {
|
if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL))
|
||||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ECHO);
|
peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now);
|
||||||
outp.armor(peer->key(),true);
|
|
||||||
viaPath->send(RR,outp.data(),outp.size(),now);
|
|
||||||
}
|
|
||||||
viaPath.zero();
|
viaPath.zero();
|
||||||
}
|
}
|
||||||
if (!viaPath) {
|
if (!viaPath) {
|
||||||
|
|
|
@ -150,7 +150,7 @@ public:
|
||||||
if (fullSignatureCheck) {
|
if (fullSignatureCheck) {
|
||||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
|
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
|
||||||
update.serialize(tmp,true);
|
update.serialize(tmp,true);
|
||||||
return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature);
|
return C25519::verify(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature);
|
||||||
} else return true;
|
} else return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -169,7 +169,7 @@ public:
|
||||||
b.append((uint8_t)0x01);
|
b.append((uint8_t)0x01);
|
||||||
b.append((uint64_t)_id);
|
b.append((uint64_t)_id);
|
||||||
b.append((uint64_t)_ts);
|
b.append((uint64_t)_ts);
|
||||||
b.append(_updateSigningKey.data,ZT_C25519_PUBLIC_KEY_LEN);
|
b.append(_updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
|
||||||
if (!forSign)
|
if (!forSign)
|
||||||
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||||
b.append((uint8_t)_roots.size());
|
b.append((uint8_t)_roots.size());
|
||||||
|
@ -195,7 +195,7 @@ public:
|
||||||
|
|
||||||
_id = b.template at<uint64_t>(p); p += 8;
|
_id = b.template at<uint64_t>(p); p += 8;
|
||||||
_ts = b.template at<uint64_t>(p); p += 8;
|
_ts = b.template at<uint64_t>(p); p += 8;
|
||||||
memcpy(_updateSigningKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
|
memcpy(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
|
||||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
|
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
|
||||||
unsigned int numRoots = b[p++];
|
unsigned int numRoots = b[p++];
|
||||||
if (numRoots > ZT_WORLD_MAX_ROOTS)
|
if (numRoots > ZT_WORLD_MAX_ROOTS)
|
||||||
|
@ -216,13 +216,13 @@ public:
|
||||||
return (p - startAt);
|
return (p - startAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_updateSigningKey == w._updateSigningKey)&&(_signature == w._signature)&&(_roots == w._roots)); }
|
inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_updatesMustBeSignedBy == w._updatesMustBeSignedBy)&&(_signature == w._signature)&&(_roots == w._roots)); }
|
||||||
inline bool operator!=(const World &w) const throw() { return (!(*this == w)); }
|
inline bool operator!=(const World &w) const throw() { return (!(*this == w)); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint64_t _id;
|
uint64_t _id;
|
||||||
uint64_t _ts;
|
uint64_t _ts;
|
||||||
C25519::Public _updateSigningKey;
|
C25519::Public _updatesMustBeSignedBy;
|
||||||
C25519::Signature _signature;
|
C25519::Signature _signature;
|
||||||
std::vector<Root> _roots;
|
std::vector<Root> _roots;
|
||||||
};
|
};
|
||||||
|
|
|
@ -69,23 +69,28 @@ static void _forkTarget(const InetAddress &t,InetAddress &left,InetAddress &righ
|
||||||
{
|
{
|
||||||
const unsigned int bits = t.netmaskBits() + 1;
|
const unsigned int bits = t.netmaskBits() + 1;
|
||||||
left = t;
|
left = t;
|
||||||
if ((t.ss_family == AF_INET)&&(bits <= 32)) {
|
if (t.ss_family == AF_INET) {
|
||||||
left.setPort(bits);
|
if (bits <= 32) {
|
||||||
right = t;
|
left.setPort(bits);
|
||||||
reinterpret_cast<struct sockaddr_in *>(&right)->sin_addr.s_addr ^= Utils::hton((uint32_t)(1 << (32 - bits)));
|
right = t;
|
||||||
right.setPort(bits);
|
reinterpret_cast<struct sockaddr_in *>(&right)->sin_addr.s_addr ^= Utils::hton((uint32_t)(1 << (32 - bits)));
|
||||||
} else if ((t.ss_family == AF_INET6)&&(bits <= 128)) {
|
right.setPort(bits);
|
||||||
left.setPort(bits);
|
} else {
|
||||||
right = t;
|
right.zero();
|
||||||
uint8_t *b = reinterpret_cast<uint8_t *>(reinterpret_cast<struct sockaddr_in6 *>(&right)->sin6_addr.s6_addr);
|
}
|
||||||
b[bits / 8] ^= 1 << (8 - (bits % 8));
|
} else if (t.ss_family == AF_INET6) {
|
||||||
right.setPort(bits);
|
if (bits <= 128) {
|
||||||
|
left.setPort(bits);
|
||||||
|
right = t;
|
||||||
|
uint8_t *b = reinterpret_cast<uint8_t *>(reinterpret_cast<struct sockaddr_in6 *>(&right)->sin6_addr.s6_addr);
|
||||||
|
b[bits / 8] ^= 1 << (8 - (bits % 8));
|
||||||
|
right.setPort(bits);
|
||||||
|
} else {
|
||||||
|
right.zero();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __BSD__ // ------------------------------------------------------------
|
|
||||||
#define ZT_ROUTING_SUPPORT_FOUND 1
|
|
||||||
|
|
||||||
struct _RTE
|
struct _RTE
|
||||||
{
|
{
|
||||||
InetAddress target;
|
InetAddress target;
|
||||||
|
@ -95,6 +100,9 @@ struct _RTE
|
||||||
bool ifscope;
|
bool ifscope;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __BSD__ // ------------------------------------------------------------
|
||||||
|
#define ZT_ROUTING_SUPPORT_FOUND 1
|
||||||
|
|
||||||
static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains)
|
static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains)
|
||||||
{
|
{
|
||||||
std::vector<_RTE> rtes;
|
std::vector<_RTE> rtes;
|
||||||
|
@ -232,6 +240,7 @@ static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains)
|
||||||
|
|
||||||
static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *ifscope,const char *localInterface)
|
static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *ifscope,const char *localInterface)
|
||||||
{
|
{
|
||||||
|
//printf("route %s %s %s %s %s\n",op,target.toString().c_str(),(via) ? via.toString().c_str() : "(null)",(ifscope) ? ifscope : "(null)",(localInterface) ? localInterface : "(null)");
|
||||||
long p = (long)fork();
|
long p = (long)fork();
|
||||||
if (p > 0) {
|
if (p > 0) {
|
||||||
int exitcode = -1;
|
int exitcode = -1;
|
||||||
|
@ -369,145 +378,123 @@ bool ManagedRoute::sync()
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((_target.isDefaultRoute())||((_target.ss_family == AF_INET)&&(_target.netmaskBits() < 32))) {
|
// Generate two more specific routes than target with one extra bit
|
||||||
/* In ZeroTier we create two more specific routes for every one route. We
|
InetAddress leftt,rightt;
|
||||||
* do this for default routes and IPv4 routes other than /32s. If there
|
_forkTarget(_target,leftt,rightt);
|
||||||
* is a pre-existing system route that this route will override, we create
|
|
||||||
* two more specific interface-bound shadow routes for it.
|
|
||||||
*
|
|
||||||
* This means that ZeroTier can *itself* continue communicating over
|
|
||||||
* whatever physical routes might be present while simultaneously
|
|
||||||
* overriding them for general system traffic. This is mostly for
|
|
||||||
* "full tunnel" VPN modes of operation, but might be useful for
|
|
||||||
* virtualizing physical networks in a hybrid design as well. */
|
|
||||||
|
|
||||||
// Generate two more specific routes than target with one extra bit
|
|
||||||
InetAddress leftt,rightt;
|
|
||||||
_forkTarget(_target,leftt,rightt);
|
|
||||||
|
|
||||||
#ifdef __BSD__ // ------------------------------------------------------------
|
#ifdef __BSD__ // ------------------------------------------------------------
|
||||||
|
|
||||||
// Find lowest metric system route that this route should override (if any)
|
// Find lowest metric system route that this route should override (if any)
|
||||||
InetAddress newSystemVia;
|
InetAddress newSystemVia;
|
||||||
char newSystemDevice[128];
|
char newSystemDevice[128];
|
||||||
newSystemDevice[0] = (char)0;
|
newSystemDevice[0] = (char)0;
|
||||||
int systemMetric = 9999999;
|
int systemMetric = 9999999;
|
||||||
std::vector<_RTE> rtes(_getRTEs(_target,false));
|
std::vector<_RTE> rtes(_getRTEs(_target,false));
|
||||||
|
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
|
||||||
|
if (r->via) {
|
||||||
|
if ( ((!newSystemVia)||(r->metric < systemMetric)) && (strcmp(r->device,_device) != 0) ) {
|
||||||
|
newSystemVia = r->via;
|
||||||
|
Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
|
||||||
|
systemMetric = r->metric;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get device corresponding to route if we don't have that already
|
||||||
|
if ((newSystemVia)&&(!newSystemDevice[0])) {
|
||||||
|
rtes = _getRTEs(newSystemVia,true);
|
||||||
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
|
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
|
||||||
if (r->via) {
|
if ( (r->device[0]) && (strcmp(r->device,_device) != 0) ) {
|
||||||
if ((!newSystemVia)||(r->metric < systemMetric)) {
|
Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
|
||||||
newSystemVia = r->via;
|
break;
|
||||||
Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
|
|
||||||
systemMetric = r->metric;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((newSystemVia)&&(!newSystemDevice[0])) {
|
|
||||||
rtes = _getRTEs(newSystemVia,true);
|
|
||||||
for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
|
|
||||||
if (r->device[0]) {
|
|
||||||
Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (!newSystemDevice[0])
|
||||||
|
newSystemVia.zero();
|
||||||
|
|
||||||
// Shadow system route if it exists, also delete any obsolete shadows
|
// Shadow system route if it exists, also delete any obsolete shadows
|
||||||
// and replace them with the new state. sync() is called periodically to
|
// and replace them with the new state. sync() is called periodically to
|
||||||
// allow us to do that if underlying connectivity changes.
|
// allow us to do that if underlying connectivity changes.
|
||||||
if ( ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice))) && (strcmp(_device,newSystemDevice)) ) {
|
if ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice) != 0)) {
|
||||||
if ((_systemVia)&&(_systemDevice[0])) {
|
if (_systemVia) {
|
||||||
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||||
|
if (rightt)
|
||||||
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_systemVia = newSystemVia;
|
_systemVia = newSystemVia;
|
||||||
Utils::scopy(_systemDevice,sizeof(_systemDevice),newSystemDevice);
|
Utils::scopy(_systemDevice,sizeof(_systemDevice),newSystemDevice);
|
||||||
|
|
||||||
if ((_systemVia)&&(_systemDevice[0])) {
|
if (_systemVia) {
|
||||||
_routeCmd("add",leftt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("add",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||||
_routeCmd("change",leftt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("change",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||||
|
if (rightt) {
|
||||||
_routeCmd("add",rightt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("add",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||||
_routeCmd("change",rightt,_systemVia,_systemDevice,(const char *)0);
|
_routeCmd("change",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply overriding non-device-scoped routes
|
|
||||||
if (!_applied) {
|
|
||||||
if (_via) {
|
|
||||||
_routeCmd("add",leftt,_via,(const char *)0,(const char *)0);
|
|
||||||
_routeCmd("change",leftt,_via,(const char *)0,(const char *)0);
|
|
||||||
_routeCmd("add",rightt,_via,(const char *)0,(const char *)0);
|
|
||||||
_routeCmd("change",rightt,_via,(const char *)0,(const char *)0);
|
|
||||||
} else if (_device[0]) {
|
|
||||||
_routeCmd("add",leftt,_via,(const char *)0,_device);
|
|
||||||
_routeCmd("change",leftt,_via,(const char *)0,_device);
|
|
||||||
_routeCmd("add",rightt,_via,(const char *)0,_device);
|
|
||||||
_routeCmd("change",rightt,_via,(const char *)0,_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
_applied = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __BSD__ ------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
|
||||||
|
|
||||||
if (!_applied) {
|
|
||||||
_routeCmd("replace",leftt,_via,(_via) ? _device : (const char *)0);
|
|
||||||
_routeCmd("replace",rightt,_via,(_via) ? _device : (const char *)0);
|
|
||||||
_applied = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __LINUX__ ----------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
|
||||||
|
|
||||||
if (!_applied) {
|
|
||||||
_winRoute(false,interfaceLuid,interfaceIndex,leftt,_via);
|
|
||||||
_winRoute(false,interfaceLuid,interfaceIndex,rightt,_via);
|
|
||||||
_applied = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __WINDOWS__ --------------------------------------------------------
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
#ifdef __BSD__ // ------------------------------------------------------------
|
|
||||||
|
|
||||||
if (!_applied) {
|
|
||||||
if (_via) {
|
|
||||||
_routeCmd("add",_target,_via,(const char *)0,(const char *)0);
|
|
||||||
_routeCmd("change",_target,_via,(const char *)0,(const char *)0);
|
|
||||||
} else if (_device[0]) {
|
|
||||||
_routeCmd("add",_target,_via,(const char *)0,_device);
|
|
||||||
_routeCmd("change",_target,_via,(const char *)0,_device);
|
|
||||||
}
|
|
||||||
_applied = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __BSD__ ------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
|
||||||
|
|
||||||
if (!_applied) {
|
|
||||||
_routeCmd("replace",_target,_via,(_via) ? _device : (const char *)0);
|
|
||||||
_applied = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __LINUX__ ----------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
|
||||||
|
|
||||||
if (!_applied) {
|
|
||||||
_winRoute(false,interfaceLuid,interfaceIndex,_target,_via);
|
|
||||||
_applied = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __WINDOWS__ --------------------------------------------------------
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_applied.count(leftt)) {
|
||||||
|
_applied[leftt] = false; // not ifscoped
|
||||||
|
_routeCmd("add",leftt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
|
||||||
|
_routeCmd("change",leftt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
|
||||||
|
}
|
||||||
|
if ((rightt)&&(!_applied.count(rightt))) {
|
||||||
|
_applied[rightt] = false; // not ifscoped
|
||||||
|
_routeCmd("add",rightt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
|
||||||
|
_routeCmd("change",rightt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a device-bound default target if there is none in the system. This
|
||||||
|
// is to allow e.g. IPv6 default route to work even if there is no native
|
||||||
|
// IPv6 on your LAN.
|
||||||
|
/*
|
||||||
|
if (_target.isDefaultRoute()) {
|
||||||
|
if (_systemVia) {
|
||||||
|
if (_applied.count(_target)) {
|
||||||
|
_applied.erase(_target);
|
||||||
|
_routeCmd("delete",_target,_via,_device,(_via) ? (const char *)0 : _device);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!_applied.count(_target)) {
|
||||||
|
_applied[_target] = true; // ifscoped
|
||||||
|
_routeCmd("add",_target,_via,_device,(_via) ? (const char *)0 : _device);
|
||||||
|
_routeCmd("change",_target,_via,_device,(_via) ? (const char *)0 : _device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif // __BSD__ ------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __LINUX__ // ----------------------------------------------------------
|
||||||
|
|
||||||
|
if (!_applied.count(leftt)) {
|
||||||
|
_applied[leftt] = false; // boolean unused
|
||||||
|
_routeCmd("replace",leftt,_via,(_via) ? (const char *)0 : _device);
|
||||||
|
}
|
||||||
|
if ((rightt)&&(!_applied.count(rightt))) {
|
||||||
|
_applied[rightt] = false; // boolean unused
|
||||||
|
_routeCmd("replace",rightt,_via,(_via) ? (const char *)0 : _device);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __LINUX__ ----------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __WINDOWS__ // --------------------------------------------------------
|
||||||
|
|
||||||
|
if (!_applied.count(leftt)) {
|
||||||
|
_applied[leftt] = false; // boolean unused
|
||||||
|
_winRoute(false,interfaceLuid,interfaceIndex,leftt,_via);
|
||||||
|
}
|
||||||
|
if ((rightt)&&(!_applied.count(rightt))) {
|
||||||
|
_applied[rightt] = false; // boolean unused
|
||||||
|
_winRoute(false,interfaceLuid,interfaceIndex,rightt,_via);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __WINDOWS__ --------------------------------------------------------
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,66 +508,28 @@ void ManagedRoute::remove()
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (_applied) {
|
#ifdef __BSD__
|
||||||
if ((_target.isDefaultRoute())||((_target.ss_family == AF_INET)&&(_target.netmaskBits() < 32))) {
|
if (_systemVia) {
|
||||||
InetAddress leftt,rightt;
|
InetAddress leftt,rightt;
|
||||||
_forkTarget(_target,leftt,rightt);
|
_forkTarget(_target,leftt,rightt);
|
||||||
|
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
|
||||||
|
if (rightt)
|
||||||
|
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
|
||||||
|
}
|
||||||
|
#endif // __BSD__ ------------------------------------------------------------
|
||||||
|
|
||||||
|
for(std::map<InetAddress,bool>::iterator r(_applied.begin());r!=_applied.end();++r) {
|
||||||
#ifdef __BSD__ // ------------------------------------------------------------
|
#ifdef __BSD__ // ------------------------------------------------------------
|
||||||
|
_routeCmd("delete",r->first,_via,r->second ? _device : (const char *)0,(_via) ? (const char *)0 : _device);
|
||||||
if ((_systemVia)&&(_systemDevice[0])) {
|
|
||||||
_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
|
|
||||||
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
|
|
||||||
}
|
|
||||||
if (_via) {
|
|
||||||
_routeCmd("delete",leftt,_via,(const char *)0,(const char *)0);
|
|
||||||
_routeCmd("delete",rightt,_via,(const char *)0,(const char *)0);
|
|
||||||
} else if (_device[0]) {
|
|
||||||
_routeCmd("delete",leftt,_via,(const char *)0,_device);
|
|
||||||
_routeCmd("delete",rightt,_via,(const char *)0,_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __BSD__ ------------------------------------------------------------
|
#endif // __BSD__ ------------------------------------------------------------
|
||||||
|
|
||||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
#ifdef __LINUX__ // ----------------------------------------------------------
|
||||||
|
_routeCmd("del",*r,_via,(_via) ? (const char *)0 : _device);
|
||||||
_routeCmd("del",leftt,_via,(_via) ? _device : (const char *)0);
|
|
||||||
_routeCmd("del",rightt,_via,(_via) ? _device : (const char *)0);
|
|
||||||
|
|
||||||
#endif // __LINUX__ ----------------------------------------------------------
|
#endif // __LINUX__ ----------------------------------------------------------
|
||||||
|
|
||||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
#ifdef __WINDOWS__ // --------------------------------------------------------
|
||||||
|
_winRoute(true,interfaceLuid,interfaceIndex,*r,_via);
|
||||||
_winRoute(true,interfaceLuid,interfaceIndex,leftt,_via);
|
|
||||||
_winRoute(true,interfaceLuid,interfaceIndex,rightt,_via);
|
|
||||||
|
|
||||||
#endif // __WINDOWS__ --------------------------------------------------------
|
#endif // __WINDOWS__ --------------------------------------------------------
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
#ifdef __BSD__ // ------------------------------------------------------------
|
|
||||||
|
|
||||||
if (_via) {
|
|
||||||
_routeCmd("delete",_target,_via,(const char *)0,(const char *)0);
|
|
||||||
} else if (_device[0]) {
|
|
||||||
_routeCmd("delete",_target,_via,(const char *)0,_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __BSD__ ------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef __LINUX__ // ----------------------------------------------------------
|
|
||||||
|
|
||||||
_routeCmd("del",_target,_via,(_via) ? _device : (const char *)0);
|
|
||||||
|
|
||||||
#endif // __LINUX__ ----------------------------------------------------------
|
|
||||||
|
|
||||||
#ifdef __WINDOWS__ // --------------------------------------------------------
|
|
||||||
|
|
||||||
_winRoute(true,interfaceLuid,interfaceIndex,_target,_via);
|
|
||||||
|
|
||||||
#endif // __WINDOWS__ --------------------------------------------------------
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_target.zero();
|
_target.zero();
|
||||||
|
@ -588,7 +537,7 @@ void ManagedRoute::remove()
|
||||||
_systemVia.zero();
|
_systemVia.zero();
|
||||||
_device[0] = (char)0;
|
_device[0] = (char)0;
|
||||||
_systemDevice[0] = (char)0;
|
_systemDevice[0] = (char)0;
|
||||||
_applied = false;
|
_applied.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
|
@ -6,23 +6,34 @@
|
||||||
|
|
||||||
#include "../node/InetAddress.hpp"
|
#include "../node/InetAddress.hpp"
|
||||||
#include "../node/Utils.hpp"
|
#include "../node/Utils.hpp"
|
||||||
|
#include "../node/SharedPtr.hpp"
|
||||||
|
#include "../node/AtomicCounter.hpp"
|
||||||
|
#include "../node/NonCopyable.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ZT-managed route that used C++ RAII semantics to automatically clean itself up on deallocate
|
* A ZT-managed route that used C++ RAII semantics to automatically clean itself up on deallocate
|
||||||
*/
|
*/
|
||||||
class ManagedRoute
|
class ManagedRoute : NonCopyable
|
||||||
{
|
{
|
||||||
|
friend class SharedPtr<ManagedRoute>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ManagedRoute()
|
ManagedRoute(const InetAddress &target,const InetAddress &via,const char *device)
|
||||||
{
|
{
|
||||||
_device[0] = (char)0;
|
_target = target;
|
||||||
|
_via = via;
|
||||||
|
if (via.ss_family == AF_INET)
|
||||||
|
_via.setPort(32);
|
||||||
|
else if (via.ss_family == AF_INET6)
|
||||||
|
_via.setPort(128);
|
||||||
|
Utils::scopy(_device,sizeof(_device),device);
|
||||||
_systemDevice[0] = (char)0;
|
_systemDevice[0] = (char)0;
|
||||||
_applied = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~ManagedRoute()
|
~ManagedRoute()
|
||||||
|
@ -30,45 +41,6 @@ public:
|
||||||
this->remove();
|
this->remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
ManagedRoute(const ManagedRoute &r)
|
|
||||||
{
|
|
||||||
_applied = false;
|
|
||||||
*this = r;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ManagedRoute &operator=(const ManagedRoute &r)
|
|
||||||
{
|
|
||||||
if ((!_applied)&&(!r._applied)) {
|
|
||||||
memcpy(this,&r,sizeof(ManagedRoute)); // InetAddress is memcpy'able
|
|
||||||
} else {
|
|
||||||
fprintf(stderr,"Applied ManagedRoute isn't copyable!\n");
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize object and set route
|
|
||||||
*
|
|
||||||
* Note: on Windows, use the interface NET_LUID in hexadecimal as the
|
|
||||||
* "device name."
|
|
||||||
*
|
|
||||||
* @param target Route target (e.g. 0.0.0.0/0 for default)
|
|
||||||
* @param via Route next L3 hop or NULL InetAddress if local in which case it will be routed via device
|
|
||||||
* @param device Name or hex LUID of ZeroTier device (e.g. zt#)
|
|
||||||
* @return True if route was successfully set
|
|
||||||
*/
|
|
||||||
inline bool set(const InetAddress &target,const InetAddress &via,const char *device)
|
|
||||||
{
|
|
||||||
if ((!via)&&(!device[0]))
|
|
||||||
return false;
|
|
||||||
this->remove();
|
|
||||||
_target = target;
|
|
||||||
_via = via;
|
|
||||||
Utils::scopy(_device,sizeof(_device),device);
|
|
||||||
return this->sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set or update currently set route
|
* Set or update currently set route
|
||||||
*
|
*
|
||||||
|
@ -93,13 +65,14 @@ public:
|
||||||
inline const char *device() const { return _device; }
|
inline const char *device() const { return _device; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
InetAddress _target;
|
InetAddress _target;
|
||||||
InetAddress _via;
|
InetAddress _via;
|
||||||
InetAddress _systemVia; // for route overrides
|
InetAddress _systemVia; // for route overrides
|
||||||
|
std::map<InetAddress,bool> _applied; // routes currently applied
|
||||||
char _device[128];
|
char _device[128];
|
||||||
char _systemDevice[128]; // for route overrides
|
char _systemDevice[128]; // for route overrides
|
||||||
bool _applied;
|
|
||||||
|
AtomicCounter __refCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
|
@ -215,7 +215,7 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
|
||||||
const char *prole = "";
|
const char *prole = "";
|
||||||
switch(peer->role) {
|
switch(peer->role) {
|
||||||
case ZT_PEER_ROLE_LEAF: prole = "LEAF"; break;
|
case ZT_PEER_ROLE_LEAF: prole = "LEAF"; break;
|
||||||
case ZT_PEER_ROLE_RELAY: prole = "RELAY"; break;
|
case ZT_PEER_ROLE_UPSTREAM: prole = "UPSTREAM"; break;
|
||||||
case ZT_PEER_ROLE_ROOT: prole = "ROOT"; break;
|
case ZT_PEER_ROLE_ROOT: prole = "ROOT"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -537,7 +537,7 @@ public:
|
||||||
EthernetTap *tap;
|
EthernetTap *tap;
|
||||||
ZT_VirtualNetworkConfig config; // memcpy() of raw config from core
|
ZT_VirtualNetworkConfig config; // memcpy() of raw config from core
|
||||||
std::vector<InetAddress> managedIps;
|
std::vector<InetAddress> managedIps;
|
||||||
std::list<ManagedRoute> managedRoutes;
|
std::list< SharedPtr<ManagedRoute> > managedRoutes;
|
||||||
NetworkSettings settings;
|
NetworkSettings settings;
|
||||||
};
|
};
|
||||||
std::map<uint64_t,NetworkState> _nets;
|
std::map<uint64_t,NetworkState> _nets;
|
||||||
|
@ -1128,13 +1128,13 @@ public:
|
||||||
std::vector<InetAddress> myIps(n.tap->ips());
|
std::vector<InetAddress> myIps(n.tap->ips());
|
||||||
|
|
||||||
// Nuke applied routes that are no longer in n.config.routes[] and/or are not allowed
|
// Nuke applied routes that are no longer in n.config.routes[] and/or are not allowed
|
||||||
for(std::list<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();) {
|
for(std::list< SharedPtr<ManagedRoute> >::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();) {
|
||||||
bool haveRoute = false;
|
bool haveRoute = false;
|
||||||
if ( (checkIfManagedIsAllowed(n,mr->target())) && ((mr->via().ss_family != mr->target().ss_family)||(!matchIpOnly(myIps,mr->via()))) ) {
|
if ( (checkIfManagedIsAllowed(n,(*mr)->target())) && (((*mr)->via().ss_family != (*mr)->target().ss_family)||(!matchIpOnly(myIps,(*mr)->via()))) ) {
|
||||||
for(unsigned int i=0;i<n.config.routeCount;++i) {
|
for(unsigned int i=0;i<n.config.routeCount;++i) {
|
||||||
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
|
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
|
||||||
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
|
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
|
||||||
if ( (mr->target() == *target) && ( ((via->ss_family == target->ss_family)&&(mr->via() == *via)) || (tapdev == mr->device()) ) ) {
|
if ( ((*mr)->target() == *target) && ( ((via->ss_family == target->ss_family)&&((*mr)->via().ipsEqual(*via))) || (tapdev == (*mr)->device()) ) ) {
|
||||||
haveRoute = true;
|
haveRoute = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1168,10 +1168,10 @@ public:
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If we've already applied this route, just sync it and continue
|
// If we've already applied this route, just sync it and continue
|
||||||
for(std::list<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();++mr) {
|
for(std::list< SharedPtr<ManagedRoute> >::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();++mr) {
|
||||||
if ( (mr->target() == *target) && ( ((via->ss_family == target->ss_family)&&(mr->via() == *via)) || (tapdev == mr->device()) ) ) {
|
if ( ((*mr)->target() == *target) && ( ((via->ss_family == target->ss_family)&&((*mr)->via().ipsEqual(*via))) || (tapdev == (*mr)->device()) ) ) {
|
||||||
haveRoute = true;
|
haveRoute = true;
|
||||||
mr->sync();
|
(*mr)->sync();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1179,8 +1179,8 @@ public:
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Add and apply new routes
|
// Add and apply new routes
|
||||||
n.managedRoutes.push_back(ManagedRoute());
|
n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,tapdev)));
|
||||||
if (!n.managedRoutes.back().set(*target,*via,tapdev))
|
if (!n.managedRoutes.back()->sync())
|
||||||
n.managedRoutes.pop_back();
|
n.managedRoutes.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue