diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index a1905221f..35666fdba 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -167,8 +167,8 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) : ||(sqlite3_prepare_v2(_db,"SELECT n.id FROM Member AS m,Node AS n WHERE m.networkId = ? AND n.id = m.nodeId ORDER BY n.id ASC",-1,&_sListNetworkMembers,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"SELECT m.authorized,m.activeBridge,n.identity,n.lastAt,n.lastSeen,n.firstSeen FROM Member AS m,Node AS n WHERE m.networkId = ? AND m.nodeId = ?",-1,&_sGetMember2,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"SELECT ipNetwork,ipNetmaskBits,ipVersion FROM IpAssignmentPool WHERE networkId = ? ORDER BY ipNetwork ASC",-1,&_sGetIpAssignmentPools2,(const char **)0) != SQLITE_OK) - ||(sqlite3_prepare_v2(_db,"SELECT ruleId,nodeId,vlanId,vlanPcp,etherType,macSource,macDest,ipSource,ipDest,ipTos,ipProtocol,ipSourcePort,ipDestPort,\"flags\",invFlags,\"action\" FROM Rule WHERE networkId = ? ORDER BY ruleId ASC",-1,&_sListRules,(const char **)0) != SQLITE_OK) - ||(sqlite3_prepare_v2(_db,"INSERT INTO Rule (networkId,ruleId,nodeId,vlanId,vlanPcP,etherType,macSource,macDest,ipSource,ipDest,ipTos,ipProtocol,ipSourcePort,ipDestPort,\"action\") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",-1,&_sCreateRule,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"SELECT ruleNo,nodeId,vlanId,vlanPcp,etherType,macSource,macDest,ipSource,ipDest,ipTos,ipProtocol,ipSourcePort,ipDestPort,\"flags\",invFlags,\"action\" FROM Rule WHERE networkId = ? ORDER BY ruleNo ASC",-1,&_sListRules,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"INSERT INTO Rule (networkId,ruleNo,nodeId,vlanId,vlanPcP,etherType,macSource,macDest,ipSource,ipDest,ipTos,ipProtocol,ipSourcePort,ipDestPort,\"action\") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",-1,&_sCreateRule,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"INSERT INTO Network (id,name,creationTime,revision) VALUES (?,?,?,1)",-1,&_sCreateNetwork,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"SELECT revision FROM Network WHERE id = ?",-1,&_sGetNetworkRevision,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"UPDATE Network SET revision = ? WHERE id = ?",-1,&_sSetNetworkRevision,(const char **)0) != SQLITE_OK) @@ -179,7 +179,10 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) : ||(sqlite3_prepare_v2(_db,"DELETE FROM Rule WHERE networkId = ?",-1,&_sDeleteRulesForNetwork,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"INSERT INTO IpAssignmentPool (networkId,ipNetwork,ipNetmaskBits,ipVersion) VALUES (?,?,?,?)",-1,&_sCreateIpAssignmentPool,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"DELETE FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sDeleteMember,(const char **)0) != SQLITE_OK) - ||(sqlite3_prepare_v2(_db,"DELETE FROM IpAssignment WHERE networkId = ?; DELETE FROM IpAssignmentPool WHERE networkId = ?; DELETE FROM Member WHERE networkId = ?; DELETE FROM MulticastRate WHERE networkId = ?; DELETE FROM Relay WHERE networkId = ?; DELETE FROM Rule WHERE networkId = ?; DELETE FROM Network WHERE id = ?;",-1,&_sDeleteNetworkAndRelated,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"DELETE FROM Network WHERE id = ?",-1,&_sDeleteNetwork,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"SELECT ip,ipVersion,metric FROM Gateway WHERE networkId = ? ORDER BY metric ASC",-1,&_sGetGateways,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"DELETE FROM Gateway WHERE networkId = ?",-1,&_sDeleteGateways,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"INSERT INTO Gateway (networkId,ip,ipVersion,metric) VALUES (?,?,?,?)",-1,&_sCreateGateway,(const char **)0) != SQLITE_OK) ) { //printf("!!! %s\n",sqlite3_errmsg(_db)); sqlite3_close(_db); @@ -222,7 +225,10 @@ SqliteNetworkController::~SqliteNetworkController() sqlite3_finalize(_sDeleteIpAssignmentPoolsForNetwork); sqlite3_finalize(_sDeleteRulesForNetwork); sqlite3_finalize(_sCreateIpAssignmentPool); - sqlite3_finalize(_sDeleteNetworkAndRelated); + sqlite3_finalize(_sDeleteNetwork); + sqlite3_finalize(_sGetGateways); + sqlite3_finalize(_sDeleteGateways); + sqlite3_finalize(_sCreateGateway); sqlite3_close(_db); } } @@ -455,6 +461,52 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co netconf[ZT_NETWORKCONFIG_DICT_KEY_RELAYS] = relays; } + { + char tmp[128]; + std::string gateways; + sqlite3_reset(_sGetGateways); + sqlite3_bind_text(_sGetGateways,1,network.id,16,SQLITE_STATIC); + while (sqlite3_step(_sGetGateways) == SQLITE_ROW) { + const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetGateways,0); + switch(sqlite3_column_int(_sGetGateways,1)) { // ipVersion + case 4: + Utils::snprintf(tmp,sizeof(tmp),"%s%d.%d.%d.%d/%d", + (gateways.length() > 0) ? "," : "", + (int)ip[0], + (int)ip[1], + (int)ip[2], + (int)ip[3], + (int)sqlite3_column_int(_sGetGateways,2)); // metric + gateways.append(tmp); + break; + case 6: + Utils::snprintf(tmp,sizeof(tmp),"%s%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d", + (gateways.length() > 0) ? "," : "", + (int)ip[0], + (int)ip[1], + (int)ip[2], + (int)ip[3], + (int)ip[4], + (int)ip[5], + (int)ip[6], + (int)ip[7], + (int)ip[8], + (int)ip[9], + (int)ip[10], + (int)ip[11], + (int)ip[12], + (int)ip[13], + (int)ip[14], + (int)ip[15], + (int)sqlite3_column_int(_sGetGateways,2)); // metric + gateways.append(tmp); + break; + } + } + if (gateways.length()) + netconf[ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS] = gateways; + } + if ((network.v4AssignMode)&&(!strcmp(network.v4AssignMode,"zt"))) { std::string v4s; @@ -808,6 +860,31 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( sqlite3_step(_sCreateRelay); } } + } else if (!strcmp(j->u.object.values[k].name,"gateways")) { + sqlite3_reset(_sDeleteGateways); + sqlite3_bind_text(_sDeleteGateways,1,nwids,16,SQLITE_STATIC); + sqlite3_step(_sDeleteGateways); + if (j->u.object.values[k].value->type == json_array) { + for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { + json_value *gateway = j->u.object.values[k].value->u.array.values[kk]; + if ((gateway)&&(gateway->type == json_string)) { + InetAddress gwip(gateway->u.string.ptr); + int ipVersion = 0; + if (gwip.ss_family == AF_INET) + ipVersion = 4; + else if (gwip.ss_family == AF_INET6) + ipVersion = 6; + if (ipVersion) { + sqlite3_reset(_sCreateGateway); + sqlite3_bind_text(_sCreateGateway,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_blob(_sCreateGateway,2,gwip.rawIpData(),(gwip.ss_family == AF_INET6) ? 16 : 4,SQLITE_STATIC); + sqlite3_bind_int(_sCreateGateway,3,ipVersion); + sqlite3_bind_int(_sCreateGateway,4,(int)gwip.metric()); + sqlite3_step(_sCreateGateway); + } + } + } + } } else if (!strcmp(j->u.object.values[k].name,"ipAssignmentPools")) { if (j->u.object.values[k].value->type == json_array) { std::set pools; @@ -855,7 +932,7 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( json_value *rj = j->u.object.values[k].value->u.array.values[kk]; if ((rj)&&(rj->type == json_object)) { struct { // NULL pointers indicate missing or NULL -- wildcards - const json_int_t *ruleId; + const json_int_t *ruleNo; const char *nodeId; const json_int_t *vlanId; const json_int_t *vlanPcp; @@ -875,8 +952,8 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( memset(&rule,0,sizeof(rule)); for(unsigned int rk=0;rku.object.length;++rk) { - if ((!strcmp(rj->u.object.values[rk].name,"ruleId"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.ruleId = &(rj->u.object.values[rk].value->u.integer); + if ((!strcmp(rj->u.object.values[rk].name,"ruleNo"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.ruleNo = &(rj->u.object.values[rk].value->u.integer); else if ((!strcmp(rj->u.object.values[rk].name,"nodeId"))&&(rj->u.object.values[rk].value->type == json_string)) rule.nodeId = rj->u.object.values[rk].value->u.string.ptr; else if ((!strcmp(rj->u.object.values[rk].name,"vlanId"))&&(rj->u.object.values[rk].value->type == json_integer)) @@ -909,11 +986,11 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( rule.action = rj->u.object.values[rk].value->u.string.ptr; } - if ((rule.ruleId)&&(rule.action)&&(rule.action[0])) { + if ((rule.ruleNo)&&(rule.action)&&(rule.action[0])) { char mactmp1[16],mactmp2[16]; sqlite3_reset(_sCreateRule); sqlite3_bind_text(_sCreateRule,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_int64(_sCreateRule,2,*rule.ruleId); + sqlite3_bind_int64(_sCreateRule,2,*rule.ruleNo); // Optional values: null by default for(int i=3;i<=16;++i) @@ -993,6 +1070,11 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE( char nwids[24]; Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); + sqlite3_reset(_sGetNetworkById); + sqlite3_bind_text(_sGetNetworkById,1,nwids,16,SQLITE_STATIC); + if (sqlite3_step(_sGetNetworkById) != SQLITE_ROW) + return 404; + if (path.size() >= 3) { if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { @@ -1000,6 +1082,12 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE( char addrs[24]; Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address); + sqlite3_reset(_sGetMember); + sqlite3_bind_text(_sGetMember,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sGetMember,2,addrs,10,SQLITE_STATIC); + if (sqlite3_step(_sGetMember) != SQLITE_ROW) + return 404; + sqlite3_reset(_sDeleteIpAllocations); sqlite3_bind_text(_sDeleteIpAllocations,1,nwids,16,SQLITE_STATIC); sqlite3_bind_text(_sDeleteIpAllocations,2,addrs,10,SQLITE_STATIC); @@ -1016,10 +1104,9 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE( } else { - sqlite3_reset(_sDeleteNetworkAndRelated); - for(int i=1;i<=7;++i) - sqlite3_bind_text(_sDeleteNetworkAndRelated,i,nwids,16,SQLITE_STATIC); - return ((sqlite3_step(_sDeleteNetworkAndRelated) == SQLITE_DONE) ? 200 : 500); + sqlite3_reset(_sDeleteNetwork); + sqlite3_bind_text(_sDeleteNetwork,1,nwids,16,SQLITE_STATIC); + return ((sqlite3_step(_sDeleteNetwork) == SQLITE_DONE) ? 200 : 500); } } // else 404 @@ -1202,6 +1289,49 @@ unsigned int SqliteNetworkController::_doCPGet( responseBody.append(_jsonEscape((const char *)sqlite3_column_text(_sGetRelays,1))); responseBody.append("\"}"); } + responseBody.append("],\n\t\"gateways\": ["); + + sqlite3_reset(_sGetGateways); + sqlite3_bind_text(_sGetGateways,1,nwids,16,SQLITE_STATIC); + bool firstGateway = true; + while (sqlite3_step(_sGetGateways) == SQLITE_ROW) { + char tmp[128]; + const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetGateways,0); + switch(sqlite3_column_int(_sGetGateways,1)) { // ipVersion + case 4: + Utils::snprintf(tmp,sizeof(tmp),"%s%d.%d.%d.%d/%d\"", + (firstGateway) ? "\"" : ",\"", + (int)ip[0], + (int)ip[1], + (int)ip[2], + (int)ip[3], + (int)sqlite3_column_int(_sGetGateways,2)); // metric + break; + case 6: + Utils::snprintf(tmp,sizeof(tmp),"%s%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d\"", + (firstGateway) ? "\"" : ",\"", + (int)ip[0], + (int)ip[1], + (int)ip[2], + (int)ip[3], + (int)ip[4], + (int)ip[5], + (int)ip[6], + (int)ip[7], + (int)ip[8], + (int)ip[9], + (int)ip[10], + (int)ip[11], + (int)ip[12], + (int)ip[13], + (int)ip[14], + (int)ip[15], + (int)sqlite3_column_int(_sGetGateways,2)); // metric + break; + } + responseBody.append(tmp); + firstGateway = false; + } responseBody.append("],\n\t\"ipAssignmentPools\": ["); sqlite3_reset(_sGetIpAssignmentPools2); @@ -1223,7 +1353,7 @@ unsigned int SqliteNetworkController::_doCPGet( bool firstRule = true; while (sqlite3_step(_sListRules) == SQLITE_ROW) { responseBody.append(firstRule ? "\n\t{\n" : ",{\n"); - Utils::snprintf(json,sizeof(json),"\t\t\"ruleId\": %lld,\n",sqlite3_column_int64(_sListRules,0)); + Utils::snprintf(json,sizeof(json),"\t\t\"ruleNo\": %lld,\n",sqlite3_column_int64(_sListRules,0)); responseBody.append(json); if (sqlite3_column_type(_sListRules,1) != SQLITE_NULL) { Utils::snprintf(json,sizeof(json),"\t\t\"nodeId\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,1)); diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index 5c92cc0b1..d258933d5 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -123,7 +123,10 @@ private: sqlite3_stmt *_sDeleteRulesForNetwork; sqlite3_stmt *_sCreateIpAssignmentPool; sqlite3_stmt *_sDeleteMember; - sqlite3_stmt *_sDeleteNetworkAndRelated; + sqlite3_stmt *_sDeleteNetwork; + sqlite3_stmt *_sGetGateways; + sqlite3_stmt *_sDeleteGateways; + sqlite3_stmt *_sCreateGateway; Mutex _lock; }; diff --git a/controller/schema.sql b/controller/schema.sql index b5646ee90..809c7161d 100644 --- a/controller/schema.sql +++ b/controller/schema.sql @@ -3,53 +3,6 @@ CREATE TABLE Config ( v varchar(1024) NOT NULL ); -CREATE TABLE IpAssignment ( - networkId char(16) NOT NULL, - nodeId char(10) NOT NULL, - ip blob(16) NOT NULL, - ipNetmaskBits integer NOT NULL DEFAULT(0), - ipVersion integer NOT NULL DEFAULT(4) -); - -CREATE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip); - -CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId); - -CREATE INDEX IpAssignment_networkId ON IpAssignment (networkId); - -CREATE TABLE IpAssignmentPool ( - networkId char(16) NOT NULL, - ipNetwork blob(16) NOT NULL, - ipNetmaskBits integer NOT NULL, - ipVersion integer NOT NULL DEFAULT(4) -); - -CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId); - -CREATE TABLE Member ( - networkId char(16) NOT NULL, - nodeId char(10) NOT NULL, - authorized integer NOT NULL DEFAULT(0), - activeBridge integer NOT NULL DEFAULT(0) -); - -CREATE INDEX Member_networkId ON Member (networkId); - -CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge); - -CREATE UNIQUE INDEX Member_networkId_nodeId ON Member (networkId, nodeId); - -CREATE TABLE MulticastRate ( - networkId char(16) NOT NULL, - mgMac char(12) NOT NULL, - mgAdi integer NOT NULL DEFAULT(0), - preload integer NOT NULL, - maxBalance integer NOT NULL, - accrual integer NOT NULL -); - -CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId); - CREATE TABLE Network ( id char(16) PRIMARY KEY NOT NULL, name varchar(128) NOT NULL, @@ -63,16 +16,6 @@ CREATE TABLE Network ( revision integer NOT NULL DEFAULT(1) ); -CREATE TABLE Relay ( - networkId char(16) NOT NULL, - nodeId char(10) NOT NULL, - phyAddress varchar(64) NOT NULL -); - -CREATE INDEX Relay_networkId ON Relay (networkId); - -CREATE UNIQUE INDEX Relay_networkId_nodeId ON Relay (networkId, nodeId); - CREATE TABLE Node ( id char(10) PRIMARY KEY NOT NULL, identity varchar(4096) NOT NULL, @@ -81,10 +24,70 @@ CREATE TABLE Node ( firstSeen integer NOT NULL DEFAULT(0) ); +CREATE TABLE Gateway ( + networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE, + ip blob(16) NOT NULL, + ipVersion integer NOT NULL DEFAULT(4), + metric integer NOT NULL DEFAULT(0) +); + +CREATE UNIQUE INDEX Gateway_networkId_ip ON Gateway (networkId, ip); + +CREATE TABLE IpAssignment ( + networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE, + nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE, + ip blob(16) NOT NULL, + ipNetmaskBits integer NOT NULL DEFAULT(0), + ipVersion integer NOT NULL DEFAULT(4) +); + +CREATE UNIQUE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip); + +CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId); + +CREATE TABLE IpAssignmentPool ( + networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE, + ipNetwork blob(16) NOT NULL, + ipNetmaskBits integer NOT NULL, + ipVersion integer NOT NULL DEFAULT(4) +); + +CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId); + +CREATE TABLE Member ( + networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE, + nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE, + authorized integer NOT NULL DEFAULT(0), + activeBridge integer NOT NULL DEFAULT(0), + PRIMARY KEY (networkId, nodeId) +); + +CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge); + +CREATE TABLE MulticastRate ( + networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE, + mgMac char(12) NOT NULL, + mgAdi integer NOT NULL DEFAULT(0), + preload integer NOT NULL, + maxBalance integer NOT NULL, + accrual integer NOT NULL +); + +CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId); + +CREATE TABLE Relay ( + networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE, + nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE, + phyAddress varchar(64) NOT NULL, + PRIMARY KEY (networkId, nodeId) +); + +CREATE INDEX Relay_networkId ON Relay (networkId); + CREATE TABLE Rule ( - networkId char(16) NOT NULL, - ruleId integer NOT NULL, - nodeId char(10), + networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE, + ruleNo integer NOT NULL, + nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE, vlanId integer, vlanPcp integer, etherType integer, @@ -101,4 +104,4 @@ CREATE TABLE Rule ( "action" varchar(4096) NOT NULL DEFAULT('accept') ); -CREATE INDEX Rule_networkId ON Rule (networkId); \ No newline at end of file +CREATE UNIQUE INDEX Rule_networkId_ruleNo ON Rule (networkId, ruleNo); diff --git a/controller/schema.sql.c b/controller/schema.sql.c index 1384b9008..f1c663584 100644 --- a/controller/schema.sql.c +++ b/controller/schema.sql.c @@ -4,53 +4,6 @@ " v varchar(1024) NOT NULL\n"\ ");\n"\ "\n"\ -"CREATE TABLE IpAssignment (\n"\ -" networkId char(16) NOT NULL,\n"\ -" nodeId char(10) NOT NULL,\n"\ -" ip blob(16) NOT NULL,\n"\ -" ipNetmaskBits integer NOT NULL DEFAULT(0),\n"\ -" ipVersion integer NOT NULL DEFAULT(4)\n"\ -");\n"\ -"\n"\ -"CREATE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);\n"\ -"\n"\ -"CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);\n"\ -"\n"\ -"CREATE INDEX IpAssignment_networkId ON IpAssignment (networkId);\n"\ -"\n"\ -"CREATE TABLE IpAssignmentPool (\n"\ -" networkId char(16) NOT NULL,\n"\ -" ipNetwork blob(16) NOT NULL,\n"\ -" ipNetmaskBits integer NOT NULL,\n"\ -" ipVersion integer NOT NULL DEFAULT(4)\n"\ -");\n"\ -"\n"\ -"CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId);\n"\ -"\n"\ -"CREATE TABLE Member (\n"\ -" networkId char(16) NOT NULL,\n"\ -" nodeId char(10) NOT NULL,\n"\ -" authorized integer NOT NULL DEFAULT(0),\n"\ -" activeBridge integer NOT NULL DEFAULT(0)\n"\ -");\n"\ -"\n"\ -"CREATE INDEX Member_networkId ON Member (networkId);\n"\ -"\n"\ -"CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);\n"\ -"\n"\ -"CREATE UNIQUE INDEX Member_networkId_nodeId ON Member (networkId, nodeId);\n"\ -"\n"\ -"CREATE TABLE MulticastRate (\n"\ -" networkId char(16) NOT NULL,\n"\ -" mgMac char(12) NOT NULL,\n"\ -" mgAdi integer NOT NULL DEFAULT(0),\n"\ -" preload integer NOT NULL,\n"\ -" maxBalance integer NOT NULL,\n"\ -" accrual integer NOT NULL\n"\ -");\n"\ -"\n"\ -"CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId);\n"\ -"\n"\ "CREATE TABLE Network (\n"\ " id char(16) PRIMARY KEY NOT NULL,\n"\ " name varchar(128) NOT NULL,\n"\ @@ -64,16 +17,6 @@ " revision integer NOT NULL DEFAULT(1)\n"\ ");\n"\ "\n"\ -"CREATE TABLE Relay (\n"\ -" networkId char(16) NOT NULL,\n"\ -" nodeId char(10) NOT NULL,\n"\ -" phyAddress varchar(64) NOT NULL\n"\ -");\n"\ -"\n"\ -"CREATE INDEX Relay_networkId ON Relay (networkId);\n"\ -"\n"\ -"CREATE UNIQUE INDEX Relay_networkId_nodeId ON Relay (networkId, nodeId);\n"\ -"\n"\ "CREATE TABLE Node (\n"\ " id char(10) PRIMARY KEY NOT NULL,\n"\ " identity varchar(4096) NOT NULL,\n"\ @@ -82,10 +25,70 @@ " firstSeen integer NOT NULL DEFAULT(0)\n"\ ");\n"\ "\n"\ +"CREATE TABLE Gateway (\n"\ +" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\ +" ip blob(16) NOT NULL,\n"\ +" ipVersion integer NOT NULL DEFAULT(4),\n"\ +" metric integer NOT NULL DEFAULT(0)\n"\ +");\n"\ +"\n"\ +"CREATE UNIQUE INDEX Gateway_networkId_ip ON Gateway (networkId, ip);\n"\ +"\n"\ +"CREATE TABLE IpAssignment (\n"\ +" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\ +" nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,\n"\ +" ip blob(16) NOT NULL,\n"\ +" ipNetmaskBits integer NOT NULL DEFAULT(0),\n"\ +" ipVersion integer NOT NULL DEFAULT(4)\n"\ +");\n"\ +"\n"\ +"CREATE UNIQUE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);\n"\ +"\n"\ +"CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);\n"\ +"\n"\ +"CREATE TABLE IpAssignmentPool (\n"\ +" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\ +" ipNetwork blob(16) NOT NULL,\n"\ +" ipNetmaskBits integer NOT NULL,\n"\ +" ipVersion integer NOT NULL DEFAULT(4)\n"\ +");\n"\ +"\n"\ +"CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId);\n"\ +"\n"\ +"CREATE TABLE Member (\n"\ +" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\ +" nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,\n"\ +" authorized integer NOT NULL DEFAULT(0),\n"\ +" activeBridge integer NOT NULL DEFAULT(0),\n"\ +" PRIMARY KEY (networkId, nodeId)\n"\ +");\n"\ +"\n"\ +"CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);\n"\ +"\n"\ +"CREATE TABLE MulticastRate (\n"\ +" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\ +" mgMac char(12) NOT NULL,\n"\ +" mgAdi integer NOT NULL DEFAULT(0),\n"\ +" preload integer NOT NULL,\n"\ +" maxBalance integer NOT NULL,\n"\ +" accrual integer NOT NULL\n"\ +");\n"\ +"\n"\ +"CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId);\n"\ +"\n"\ +"CREATE TABLE Relay (\n"\ +" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\ +" nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,\n"\ +" phyAddress varchar(64) NOT NULL,\n"\ +" PRIMARY KEY (networkId, nodeId)\n"\ +");\n"\ +"\n"\ +"CREATE INDEX Relay_networkId ON Relay (networkId);\n"\ +"\n"\ "CREATE TABLE Rule (\n"\ -" networkId char(16) NOT NULL,\n"\ -" ruleId integer NOT NULL,\n"\ -" nodeId char(10),\n"\ +" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\ +" ruleNo integer NOT NULL,\n"\ +" nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,\n"\ " vlanId integer,\n"\ " vlanPcp integer,\n"\ " etherType integer,\n"\ @@ -102,5 +105,5 @@ " \"action\" varchar(4096) NOT NULL DEFAULT('accept')\n"\ ");\n"\ "\n"\ -"CREATE INDEX Rule_networkId ON Rule (networkId);\n"\ +"CREATE UNIQUE INDEX Rule_networkId_ruleNo ON Rule (networkId, ruleNo);\n"\ "" diff --git a/ext/installfiles/linux/buildinstaller.sh b/ext/installfiles/linux/buildinstaller.sh index 496ba32ce..4f661b8d5 100755 --- a/ext/installfiles/linux/buildinstaller.sh +++ b/ext/installfiles/linux/buildinstaller.sh @@ -49,9 +49,12 @@ case "$system" in echo "Assembling Linux installer for $machine and version $vmajor.$vminor.$revision" - mkdir -p 'build-installer/var/lib/zerotier-one' + mkdir -p 'build-installer/var/lib/zerotier-one/ui' cp -fp 'ext/installfiles/linux/uninstall.sh' 'build-installer/var/lib/zerotier-one' cp -fp 'zerotier-one' 'build-installer/var/lib/zerotier-one' + for f in ui/*.html ui/*.js ui/*.css ui/*.jsx ; do + cp -fp "$f" 'build-installer/var/lib/zerotier-one/ui' + done mkdir -p 'build-installer/tmp' cp -fp 'ext/installfiles/linux/init.d/zerotier-one' 'build-installer/tmp/init.d_zerotier-one' cp -fp 'ext/installfiles/linux/systemd/zerotier-one.service' 'build-installer/tmp/systemd_zerotier-one.service' diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 5b7251745..16e3f4d52 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -265,6 +265,16 @@ struct InetAddress : public sockaddr_storage */ inline unsigned int netmaskBits() const throw() { return port(); } + /** + * Alias for port() + * + * This just aliases port() because for gateways we use this field to + * store the gateway metric. + * + * @return Gateway metric + */ + inline unsigned int metric() const throw() { return port(); } + /** * Construct a full netmask as an InetAddress */ diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 4b9620a6c..5ed1dd3ff 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -163,6 +163,13 @@ void NetworkConfig::_fromDictionary(const Dictionary &d) std::sort(_staticIps.begin(),_staticIps.end()); std::unique(_staticIps.begin(),_staticIps.end()); + std::vector gatewaysSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS,"").c_str(),",","","")); + for(std::vector::const_iterator gwstr(gatewaysSplit.begin());gwstr!=gatewaysSplit.end();++gwstr) { + InetAddress gw(*gwstr); + if ((std::find(_gateways.begin(),_gateways.end(),gw) == _gateways.end())&&((gw.ss_family == AF_INET)||(gw.ss_family == AF_INET6))) + _gateways.push_back(gw); + } + std::vector activeBridgesSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES,"").c_str(),",","","")); for(std::vector::const_iterator a(activeBridgesSplit.begin());a!=activeBridgesSplit.end();++a) { if (a->length() == ZT_ADDRESS_LENGTH_HEX) { // ignore empty or garbage fields @@ -211,7 +218,9 @@ bool NetworkConfig::operator==(const NetworkConfig &nc) const if (_name != nc._name) return false; if (_description != nc._description) return false; if (_staticIps != nc._staticIps) return false; + if (_gateways != nc._gateways) return false; if (_activeBridges != nc._activeBridges) return false; + if (_relays != nc._relays) return false; if (_multicastRates.size() == nc._multicastRates.size()) { // uclibc++ doesn't seem to implement map<> != map<> correctly, so do // it ourselves. Note that this depends on the maps being sorted. diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 89d1aec50..2fb56d6aa 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -49,24 +49,61 @@ namespace ZeroTier { // These dictionary keys are short so they don't take up much room in // netconf response packets. + +// integer(hex)[,integer(hex),...] #define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES "et" + +// network ID #define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid" + +// integer(hex) #define ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP "ts" + +// integer(hex) #define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r" + +// address of member #define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id" + +// integer(hex) #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml" + +// dictionary of one or more of: MAC/ADI=preload,maxbalance,accrual #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES "mr" + +// 0/1 #define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE "p" + +// text #define ZT_NETWORKCONFIG_DICT_KEY_NAME "n" + +// text #define ZT_NETWORKCONFIG_DICT_KEY_DESC "d" + +// IP/bits[,IP/bits,...] #define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC "v4s" + +// IP/bits[,IP/bits,...] #define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC "v6s" + +// serialized CertificateOfMembership #define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP "com" + +// 0/1 #define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST "eb" + +// 0/1 #define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING "pb" + +// node[,node,...] #define ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES "ab" + +// node;IP/port[,node;IP/port] #define ZT_NETWORKCONFIG_DICT_KEY_RELAYS "rl" +// IP/metric[,IP/metric,...] +#define ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS "gw" + /** * Network configuration received from network controller nodes * @@ -146,6 +183,7 @@ public: inline const std::string &name() const throw() { return _name; } inline const std::string &description() const throw() { return _description; } inline const std::vector &staticIps() const throw() { return _staticIps; } + inline const std::vector &gateways() const throw() { return _gateways; } inline const std::vector
&activeBridges() const throw() { return _activeBridges; } inline const std::vector< std::pair > &relays() const throw() { return _relays; } inline const CertificateOfMembership &com() const throw() { return _com; } @@ -188,6 +226,7 @@ private: std::string _name; std::string _description; std::vector _staticIps; + std::vector _gateways; std::vector
_activeBridges; std::vector< std::pair > _relays; std::map _multicastRates; diff --git a/node/Packet.cpp b/node/Packet.cpp index a81873ffd..f75d1df08 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -51,6 +51,7 @@ const char *Packet::verbString(Verb v) case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_SET_EPHEMERAL_KEY: return "SET_EPHEMERAL_KEY"; + case VERB_CMA: return "CMA"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 2dfb75e4b..1ec145d5c 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -513,8 +513,8 @@ public: * Destination address types and formats (not all of these are used now): * 0 - None -- no destination address data present * 1 - Ethernet address -- format: <[6] Ethernet MAC> - * 4 - 6-byte IPv4 address -- format: <[4] IP>, <[2] port> - * 6 - 18-byte IPv6 address -- format: <[16] IP>, <[2] port> + * 4 - 6-byte IPv4 UDP address/port -- format: <[4] IP>, <[2] port> + * 6 - 18-byte IPv6 UDP address/port -- format: <[16] IP>, <[2] port> * * OK payload: * <[8] timestamp (echoed from original HELLO)> @@ -770,6 +770,9 @@ public: VERB_MULTICAST_FRAME = 14, /* Ephemeral (PFS) key push: + * <[2] flags (unused and reserved, must be 0)> + * <[2] length of padding / extra field section> + * <[...] padding / extra field section> * <[8] 64-bit PFS key set ID sender holds for recipient (0==none)> * <[8] 64-bit PFS key set ID of this key set> * [... begin PFS key record ...] @@ -791,6 +794,12 @@ public: * the first record with common symmetric cipher, public key type, * and relevant flags must be used. * + * The padding section may be filled with an arbitrary amount of random + * or empty payload. This may be used as a countermeasure to prevent PFS + * key pushes from being recognized by packet size vs. other packets in + * the stream. This also provides potential space for additional fields + * that might be indicated in the future by flags. + * * Flags (all unspecified flags must be zero): * 0x01 - FIPS mode, only use record if FIPS compliant crypto in use * @@ -814,7 +823,24 @@ public: * <[8] PFS key set ID of received key set> * <[1] index in record list of chosen key record> */ - VERB_SET_EPHEMERAL_KEY = 15 + VERB_SET_EPHEMERAL_KEY = 15, + + /* "Call me at" -- push of potential endpoints for direct communication: + * <[1] flags> + * <[2] number of addresses> + * <[...] address types and addresses> + * + * Address types and addresses are of the same format as the destination + * address type and address in HELLO. + * + * The receiver may, upon receiving a CMA push, attempt to establish a + * direct link to one or more of the indicated addresses. Senders should + * only send CMA pushes to peers that they have some relationship + * with such as a shared network membership or a mutual trust. + * + * OK/ERROR are not generated. + */ + VERB_CMA = 16 }; /** diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp index b373d9e27..d477f2e3d 100644 --- a/osdep/WindowsEthernetTap.cpp +++ b/osdep/WindowsEthernetTap.cpp @@ -92,9 +92,6 @@ static const WindowsEthernetTapEnv WINENV; // Only create or delete devices one at a time static Mutex _systemTapInitLock; -// Incrementing this causes everyone currently open to close and reopen -static volatile int _systemTapResetStatus = 0; - } // anonymous namespace WindowsEthernetTap::WindowsEthernetTap( @@ -268,12 +265,6 @@ WindowsEthernetTap::WindowsEthernetTap( } } else break; // no more keys or error occurred } - - // When we create a new tap device from scratch, existing taps for - // some reason go into 'unplugged' state. This can be fixed by - // closing and re-opening them. Incrementing this causes all - // existing tap threads to do this. - ++_systemTapResetStatus; } if (_netCfgInstanceId.length() > 0) { @@ -299,7 +290,6 @@ WindowsEthernetTap::WindowsEthernetTap( throw std::runtime_error("unable to find or create tap adapter"); } - // Convert device GUID junk... blech... is there an easier way to do this? { char nobraces[128]; const char *nbtmp1 = _netCfgInstanceId.c_str(); @@ -573,191 +563,199 @@ void WindowsEthernetTap::scanMulticastGroups(std::vector &added, void WindowsEthernetTap::threadMain() throw() { - char tapPath[256]; - OVERLAPPED tapOvlRead,tapOvlWrite; + char tapReadBuf[ZT_IF_MTU + 32]; + char tapPath[128]; HANDLE wait4[3]; - char *tapReadBuf = (char *)0; - - /* No idea why I did this. I did it a long time ago and there was only a - * a snarky comment. But I'd never do crap like this without a reason, so - * I am leaving it alone with a more descriptive snarky comment. */ - while (!tapReadBuf) { - tapReadBuf = (char *)::malloc(ZT_IF_MTU + 32); - if (!tapReadBuf) - Sleep(1000); - } + OVERLAPPED tapOvlRead,tapOvlWrite; Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str()); - int prevTapResetStatus = _systemTapResetStatus; - bool throwOneAway = true; // Restart once on startup, because Windows. - bool powerCycle = true; // If true, "power cycle" the device, because Windows. - while (_run) { - if (powerCycle) { - _disableTapDevice(); - Sleep(500); + + try { + while (_run) { _enableTapDevice(); Sleep(500); - } - _tap = CreateFileA(tapPath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,NULL); - if (_tap == INVALID_HANDLE_VALUE) { - fprintf(stderr,"Error opening %s -- retrying.\r\n",tapPath); - powerCycle = true; - continue; - } - - { - uint32_t tmpi = 1; - DWORD bytesReturned = 0; - DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL); - } - - { -#ifdef ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE - /* This inserts a fake default route and a fake ARP entry, forcing - * Windows to detect this as a "real" network and apply proper - * firewall rules. - * - * This hack is completely stupid, but Windows made me do it - * by being broken and insane. - * - * Background: Windows tries to detect its network location by - * matching it to the ARP address of the default route. Networks - * without default routes are "unidentified networks" and cannot - * have their firewall classification changed by the user (easily). - * - * Yes, you read that right. - * - * The common workaround is to set *NdisDeviceType to 1, which - * totally disables all Windows firewall functionality. This is - * the answer you'll find on most forums for things like OpenVPN. - * - * Yes, you read that right. - * - * The default route workaround is also known, but for this to - * work there must be a known default IP that resolves to a known - * ARP address. This works for an OpenVPN tunnel, but not here - * because this isn't a tunnel. It's a mesh. There is no "other - * end," or any other known always on IP. - * - * So let's make a fake one and shove it in there along with its - * fake static ARP entry. Also makes it instant-on and static. - * - * We'll have to see what DHCP does with this. In the future we - * probably will not want to do this on DHCP-enabled networks, so - * when we enable DHCP we will go in and yank this wacko hacko from - * the routing table before doing so. - * - * Like Jesse Pinkman would say: "YEEEEAAH BITCH!" */ - const uint32_t fakeIp = htonl(0x19fffffe); // 25.255.255.254 -- unrouted IPv4 block - for(int i=0;i<8;++i) { - MIB_IPNET_ROW2 ipnr; - memset(&ipnr,0,sizeof(ipnr)); - ipnr.Address.si_family = AF_INET; - ipnr.Address.Ipv4.sin_addr.s_addr = fakeIp; - ipnr.InterfaceLuid.Value = _deviceLuid.Value; - ipnr.PhysicalAddress[0] = _mac[0] ^ 0x10; // just make something up that's consistent and not part of this net - ipnr.PhysicalAddress[1] = 0x00; - ipnr.PhysicalAddress[2] = (UCHAR)((_deviceGuid.Data1 >> 24) & 0xff); - ipnr.PhysicalAddress[3] = (UCHAR)((_deviceGuid.Data1 >> 16) & 0xff); - ipnr.PhysicalAddress[4] = (UCHAR)((_deviceGuid.Data1 >> 8) & 0xff); - ipnr.PhysicalAddress[5] = (UCHAR)(_deviceGuid.Data1 & 0xff); - ipnr.PhysicalAddressLength = 6; - ipnr.State = NlnsPermanent; - ipnr.IsRouter = 1; - ipnr.IsUnreachable = 0; - ipnr.ReachabilityTime.LastReachable = 0x0fffffff; - ipnr.ReachabilityTime.LastUnreachable = 1; - DWORD result = CreateIpNetEntry2(&ipnr); - if (result != NO_ERROR) - Sleep(500); - else break; + _tap = CreateFileA(tapPath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,NULL); + if (_tap == INVALID_HANDLE_VALUE) { + _disableTapDevice(); + _enableTapDevice(); + Sleep(1000); + continue; } - for(int i=0;i<8;++i) { - MIB_IPFORWARD_ROW2 nr; - memset(&nr,0,sizeof(nr)); - InitializeIpForwardEntry(&nr); - nr.InterfaceLuid.Value = _deviceLuid.Value; - nr.DestinationPrefix.Prefix.si_family = AF_INET; // rest is left as 0.0.0.0/0 - nr.NextHop.si_family = AF_INET; - nr.NextHop.Ipv4.sin_addr.s_addr = fakeIp; - nr.Metric = 9999; // do not use as real default route - nr.Protocol = MIB_IPPROTO_NETMGMT; - DWORD result = CreateIpForwardEntry2(&nr); - if (result != NO_ERROR) - Sleep(500); - else break; + + { + uint32_t tmpi = 1; + DWORD bytesReturned = 0; + DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL); + } + +#ifdef ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE + { + /* This inserts a fake default route and a fake ARP entry, forcing + * Windows to detect this as a "real" network and apply proper + * firewall rules. + * + * This hack is completely stupid, but Windows made me do it + * by being broken and insane. + * + * Background: Windows tries to detect its network location by + * matching it to the ARP address of the default route. Networks + * without default routes are "unidentified networks" and cannot + * have their firewall classification changed by the user (easily). + * + * Yes, you read that right. + * + * The common workaround is to set *NdisDeviceType to 1, which + * totally disables all Windows firewall functionality. This is + * the answer you'll find on most forums for things like OpenVPN. + * + * Yes, you read that right. + * + * The default route workaround is also known, but for this to + * work there must be a known default IP that resolves to a known + * ARP address. This works for an OpenVPN tunnel, but not here + * because this isn't a tunnel. It's a mesh. There is no "other + * end," or any other known always on IP. + * + * So let's make a fake one and shove it in there along with its + * fake static ARP entry. Also makes it instant-on and static. + * + * We'll have to see what DHCP does with this. In the future we + * probably will not want to do this on DHCP-enabled networks, so + * when we enable DHCP we will go in and yank this wacko hacko from + * the routing table before doing so. + * + * Like Jesse Pinkman would say: "YEEEEAAH BITCH!" */ + const uint32_t fakeIp = htonl(0x19fffffe); // 25.255.255.254 -- unrouted IPv4 block + for(int i=0;i<8;++i) { + MIB_IPNET_ROW2 ipnr; + memset(&ipnr,0,sizeof(ipnr)); + ipnr.Address.si_family = AF_INET; + ipnr.Address.Ipv4.sin_addr.s_addr = fakeIp; + ipnr.InterfaceLuid.Value = _deviceLuid.Value; + ipnr.PhysicalAddress[0] = _mac[0] ^ 0x10; // just make something up that's consistent and not part of this net + ipnr.PhysicalAddress[1] = 0x00; + ipnr.PhysicalAddress[2] = (UCHAR)((_deviceGuid.Data1 >> 24) & 0xff); + ipnr.PhysicalAddress[3] = (UCHAR)((_deviceGuid.Data1 >> 16) & 0xff); + ipnr.PhysicalAddress[4] = (UCHAR)((_deviceGuid.Data1 >> 8) & 0xff); + ipnr.PhysicalAddress[5] = (UCHAR)(_deviceGuid.Data1 & 0xff); + ipnr.PhysicalAddressLength = 6; + ipnr.State = NlnsPermanent; + ipnr.IsRouter = 1; + ipnr.IsUnreachable = 0; + ipnr.ReachabilityTime.LastReachable = 0x0fffffff; + ipnr.ReachabilityTime.LastUnreachable = 1; + DWORD result = CreateIpNetEntry2(&ipnr); + if (result != NO_ERROR) + Sleep(500); + else break; + } + for(int i=0;i<8;++i) { + MIB_IPFORWARD_ROW2 nr; + memset(&nr,0,sizeof(nr)); + InitializeIpForwardEntry(&nr); + nr.InterfaceLuid.Value = _deviceLuid.Value; + nr.DestinationPrefix.Prefix.si_family = AF_INET; // rest is left as 0.0.0.0/0 + nr.NextHop.si_family = AF_INET; + nr.NextHop.Ipv4.sin_addr.s_addr = fakeIp; + nr.Metric = 9999; // do not use as real default route + nr.Protocol = MIB_IPPROTO_NETMGMT; + DWORD result = CreateIpForwardEntry2(&nr); + if (result != NO_ERROR) + Sleep(500); + else break; + } } #endif - } - memset(&tapOvlRead,0,sizeof(tapOvlRead)); - tapOvlRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); - memset(&tapOvlWrite,0,sizeof(tapOvlWrite)); - tapOvlWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + memset(&tapOvlRead,0,sizeof(tapOvlRead)); + tapOvlRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + memset(&tapOvlWrite,0,sizeof(tapOvlWrite)); + tapOvlWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); - wait4[0] = _injectSemaphore; - wait4[1] = tapOvlRead.hEvent; - wait4[2] = tapOvlWrite.hEvent; // only included if writeInProgress is true + wait4[0] = _injectSemaphore; + wait4[1] = tapOvlRead.hEvent; + wait4[2] = tapOvlWrite.hEvent; // only included if writeInProgress is true - ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead); - bool writeInProgress = false; - while (_run) { - if ((prevTapResetStatus != _systemTapResetStatus)||(throwOneAway)) { - powerCycle = throwOneAway; - throwOneAway = false; - prevTapResetStatus = _systemTapResetStatus; - break; // this will cause us to close and reopen the tap - } - DWORD r = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,2500,TRUE); - if (!_run) break; // will also break outer while(_run) + ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead); + bool writeInProgress = false; + ULONGLONG timeOfLastBorkCheck = GetTickCount64(); + while (_run) { + DWORD waitResult = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,2500,TRUE); + if (!_run) break; // will also break outer while(_run) - if ((r == WAIT_TIMEOUT)||(r == WAIT_FAILED)) - continue; - - if (HasOverlappedIoCompleted(&tapOvlRead)) { - DWORD bytesRead = 0; - if (GetOverlappedResult(_tap,&tapOvlRead,&bytesRead,FALSE)) { - if ((bytesRead > 14)&&(_enabled)) { - MAC to(tapReadBuf,6); - MAC from(tapReadBuf + 6,6); - unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)tapReadBuf[13]) & 0xff); - try { - // TODO: decode vlans - _handler(_arg,_nwid,from,to,etherType,0,tapReadBuf + 14,bytesRead - 14); - } catch ( ... ) {} // handlers should not throw + // Check for issues with adapter and close/reopen if any are detected. This + // check fixes a while boatload of Windows adapter 'coma' issues after + // sleep/wake and when adapters are added/removed. Basically if the tap + // device is borked, whack it. + { + ULONGLONG tc = GetTickCount64(); + if ((tc - timeOfLastBorkCheck) >= 2500) { + timeOfLastBorkCheck = tc; + MIB_IF_TABLE2 *ift = NULL; + if ((GetIfTable2(&ift) == NO_ERROR)&&(ift)) { + bool isBorked = false; + for(ULONG r=0;rNumEntries;++r) { + if (ift->Table[r].InterfaceLuid.Value == _deviceLuid.Value) { + if ((ift->Table[r].InterfaceAndOperStatusFlags.NotMediaConnected)||(ift->Table[r].MediaConnectState == MediaConnectStateDisconnected)) + isBorked = true; + break; + } + } + FreeMibTable(ift); + if (isBorked) { + // Close and reopen tap device if there's an issue (outer loop) + break; + } + } } } - ReadFile(_tap,tapReadBuf,ZT_IF_MTU + 32,NULL,&tapOvlRead); + + if ((waitResult == WAIT_TIMEOUT)||(waitResult == WAIT_FAILED)) + continue; + + if (HasOverlappedIoCompleted(&tapOvlRead)) { + DWORD bytesRead = 0; + if (GetOverlappedResult(_tap,&tapOvlRead,&bytesRead,FALSE)) { + if ((bytesRead > 14)&&(_enabled)) { + MAC to(tapReadBuf,6); + MAC from(tapReadBuf + 6,6); + unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)tapReadBuf[13]) & 0xff); + try { + // TODO: decode vlans + _handler(_arg,_nwid,from,to,etherType,0,tapReadBuf + 14,bytesRead - 14); + } catch ( ... ) {} // handlers should not throw + } + } + ReadFile(_tap,tapReadBuf,ZT_IF_MTU + 32,NULL,&tapOvlRead); + } + + if (writeInProgress) { + if (HasOverlappedIoCompleted(&tapOvlWrite)) { + writeInProgress = false; + _injectPending_m.lock(); + _injectPending.pop(); + } else continue; // still writing, so skip code below and wait + } else _injectPending_m.lock(); + + if (!_injectPending.empty()) { + WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&tapOvlWrite); + writeInProgress = true; + } + + _injectPending_m.unlock(); } - if (writeInProgress) { - if (HasOverlappedIoCompleted(&tapOvlWrite)) { - writeInProgress = false; - _injectPending_m.lock(); - _injectPending.pop(); - } else continue; // still writing, so skip code below and wait - } else _injectPending_m.lock(); + CancelIo(_tap); - if (!_injectPending.empty()) { - WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&tapOvlWrite); - writeInProgress = true; - } + CloseHandle(tapOvlRead.hEvent); + CloseHandle(tapOvlWrite.hEvent); + CloseHandle(_tap); + _tap = INVALID_HANDLE_VALUE; - _injectPending_m.unlock(); + // We will restart and re-open the tap unless _run == false } - - CancelIo(_tap); - - CloseHandle(tapOvlRead.hEvent); - CloseHandle(tapOvlWrite.hEvent); - CloseHandle(_tap); - _tap = INVALID_HANDLE_VALUE; - - // We will restart and re-open the tap unless _run == false - } - - ::free(tapReadBuf); + } catch ( ... ) {} // catch unexpected exceptions -- this should not happen but would prevent program crash or other weird issues since threads should not throw } void WindowsEthernetTap::destroyAllPersistentTapDevices(const char *pathToHelpers) diff --git a/osdep/WindowsEthernetTap.hpp b/osdep/WindowsEthernetTap.hpp index 670467633..944b53f33 100644 --- a/osdep/WindowsEthernetTap.hpp +++ b/osdep/WindowsEthernetTap.hpp @@ -98,8 +98,8 @@ private: GUID _deviceGuid; NET_LUID _deviceLuid; - std::string _netCfgInstanceId; // NetCfgInstanceId, a GUID - std::string _deviceInstanceId; // DeviceInstanceID, another kind of "instance ID" + std::string _netCfgInstanceId; + std::string _deviceInstanceId; std::vector _multicastGroups; diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp index 71b3fd3fb..4158ed43d 100644 --- a/service/ControlPlane.cpp +++ b/service/ControlPlane.cpp @@ -454,7 +454,7 @@ unsigned int ControlPlane::handleRequest( } else { #ifdef ZT_ENABLE_NETWORK_CONTROLLER if (_controller) - _controller->handleControlPlaneHttpGET(std::vector(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType); + scode = _controller->handleControlPlaneHttpGET(std::vector(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType); else scode = 404; #else scode = 404; @@ -490,7 +490,7 @@ unsigned int ControlPlane::handleRequest( } else { #ifdef ZT_ENABLE_NETWORK_CONTROLLER if (_controller) - _controller->handleControlPlaneHttpPOST(std::vector(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType); + scode = _controller->handleControlPlaneHttpPOST(std::vector(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType); else scode = 404; #else scode = 404; @@ -525,7 +525,7 @@ unsigned int ControlPlane::handleRequest( } else { #ifdef ZT_ENABLE_NETWORK_CONTROLLER if (_controller) - _controller->handleControlPlaneHttpDELETE(std::vector(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType); + scode = _controller->handleControlPlaneHttpDELETE(std::vector(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType); else scode = 404; #else scode = 404; diff --git a/service/README.md b/service/README.md index acad97a14..2a283cbd1 100644 --- a/service/README.md +++ b/service/README.md @@ -204,7 +204,7 @@ Relay objects define network-specific preferred relay nodes. Traffic to peers on * **Note**: at the moment, only rules specifying allowed Ethernet types are used. The database supports a richer rule set, but this is not implemented yet in the client. Other types of rules will have no effect (yet). -Rules are matched in order of ruleId. If no rules match, the default action is 'drop'. To allow all traffic, create a single rule with all *null* fields and an action of 'accept'. +Rules are matched in order of ruleNo. If no rules match, the default action is 'drop'. To allow all traffic, create a single rule with all *null* fields and an action of 'accept'. Rule object fields can be *null*, in which case they are omitted from the object. A null field indicates "no match on this criteria." @@ -212,7 +212,7 @@ IP related fields apply only to Ethernet frames of type IPv4 or IPV6. Otherwise - +
FieldTypeDescription
ruleIdintegerUser-defined rule ID and sort order
ruleNointegerUser-defined rule ID and sort order
nodeIdstring10-digit hex ZeroTier address of node (a.k.a. "port on switch")
vlanIdintegerEthernet VLAN ID
vlanPcpintegerEthernet VLAN priority code point (PCP) ID