summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--controller/SqliteNetworkController.cpp158
-rw-r--r--controller/SqliteNetworkController.hpp5
-rw-r--r--controller/schema.sql91
-rw-r--r--controller/schema.sql.c91
-rwxr-xr-xext/installfiles/linux/buildinstaller.sh5
-rw-r--r--node/InetAddress.hpp10
-rw-r--r--node/NetworkConfig.cpp9
-rw-r--r--node/NetworkConfig.hpp39
-rw-r--r--node/Packet.cpp1
-rw-r--r--node/Packet.hpp32
-rw-r--r--osdep/WindowsEthernetTap.cpp348
-rw-r--r--osdep/WindowsEthernetTap.hpp4
-rw-r--r--service/ControlPlane.cpp6
-rw-r--r--service/README.md4
14 files changed, 514 insertions, 289 deletions
diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp
index a1905221..35666fdb 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;kk<j->u.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<InetAddress> 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;rk<rj->u.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 5c92cc0b..d258933d 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 b5646ee9..809c7161 100644
--- a/controller/schema.sql
+++ b/controller/schema.sql
@@ -3,22 +3,50 @@ CREATE TABLE Config (
v varchar(1024) NOT NULL
);
+CREATE TABLE Network (
+ id char(16) PRIMARY KEY NOT NULL,
+ name varchar(128) NOT NULL,
+ private integer NOT NULL DEFAULT(1),
+ enableBroadcast integer NOT NULL DEFAULT(1),
+ allowPassiveBridging integer NOT NULL DEFAULT(0),
+ v4AssignMode varchar(8) NOT NULL DEFAULT('none'),
+ v6AssignMode varchar(8) NOT NULL DEFAULT('none'),
+ multicastLimit integer NOT NULL DEFAULT(32),
+ creationTime integer NOT NULL DEFAULT(0),
+ revision integer NOT NULL DEFAULT(1)
+);
+
+CREATE TABLE Node (
+ id char(10) PRIMARY KEY NOT NULL,
+ identity varchar(4096) NOT NULL,
+ lastAt varchar(64),
+ lastSeen integer NOT NULL DEFAULT(0),
+ 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,
- nodeId char(10) NOT NULL,
+ 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 INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);
+CREATE UNIQUE 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,
+ 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)
@@ -27,20 +55,17 @@ CREATE TABLE IpAssignmentPool (
CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId);
CREATE TABLE Member (
- networkId char(16) NOT NULL,
- nodeId char(10) NOT NULL,
+ 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)
+ activeBridge integer NOT NULL DEFAULT(0),
+ PRIMARY KEY (networkId, nodeId)
);
-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,
+ 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,
@@ -50,41 +75,19 @@ CREATE TABLE MulticastRate (
CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId);
-CREATE TABLE Network (
- id char(16) PRIMARY KEY NOT NULL,
- name varchar(128) NOT NULL,
- private integer NOT NULL DEFAULT(1),
- enableBroadcast integer NOT NULL DEFAULT(1),
- allowPassiveBridging integer NOT NULL DEFAULT(0),
- v4AssignMode varchar(8) NOT NULL DEFAULT('none'),
- v6AssignMode varchar(8) NOT NULL DEFAULT('none'),
- multicastLimit integer NOT NULL DEFAULT(32),
- creationTime integer NOT NULL DEFAULT(0),
- revision integer NOT NULL DEFAULT(1)
-);
-
CREATE TABLE Relay (
- networkId char(16) NOT NULL,
- nodeId char(10) NOT NULL,
- phyAddress varchar(64) NOT NULL
+ 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 UNIQUE INDEX Relay_networkId_nodeId ON Relay (networkId, nodeId);
-
-CREATE TABLE Node (
- id char(10) PRIMARY KEY NOT NULL,
- identity varchar(4096) NOT NULL,
- lastAt varchar(64),
- lastSeen integer NOT NULL DEFAULT(0),
- firstSeen integer NOT NULL DEFAULT(0)
-);
-
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 1384b900..f1c66358 100644
--- a/controller/schema.sql.c
+++ b/controller/schema.sql.c
@@ -4,22 +4,50 @@
" v varchar(1024) NOT NULL\n"\
");\n"\
"\n"\
+"CREATE TABLE Network (\n"\
+" id char(16) PRIMARY KEY NOT NULL,\n"\
+" name varchar(128) NOT NULL,\n"\
+" private integer NOT NULL DEFAULT(1),\n"\
+" enableBroadcast integer NOT NULL DEFAULT(1),\n"\
+" allowPassiveBridging integer NOT NULL DEFAULT(0),\n"\
+" v4AssignMode varchar(8) NOT NULL DEFAULT('none'),\n"\
+" v6AssignMode varchar(8) NOT NULL DEFAULT('none'),\n"\
+" multicastLimit integer NOT NULL DEFAULT(32),\n"\
+" creationTime integer NOT NULL DEFAULT(0),\n"\
+" revision integer NOT NULL DEFAULT(1)\n"\
+");\n"\
+"\n"\
+"CREATE TABLE Node (\n"\
+" id char(10) PRIMARY KEY NOT NULL,\n"\
+" identity varchar(4096) NOT NULL,\n"\
+" lastAt varchar(64),\n"\
+" lastSeen integer NOT NULL DEFAULT(0),\n"\
+" 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,\n"\
-" nodeId char(10) NOT NULL,\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 INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);\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 INDEX IpAssignment_networkId ON IpAssignment (networkId);\n"\
-"\n"\
"CREATE TABLE IpAssignmentPool (\n"\
-" networkId char(16) NOT NULL,\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"\
@@ -28,20 +56,17 @@
"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"\
+" 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"\
+" activeBridge integer NOT NULL DEFAULT(0),\n"\
+" PRIMARY KEY (networkId, nodeId)\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"\
+" 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"\
@@ -51,41 +76,19 @@
"\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"\
-" private integer NOT NULL DEFAULT(1),\n"\
-" enableBroadcast integer NOT NULL DEFAULT(1),\n"\
-" allowPassiveBridging integer NOT NULL DEFAULT(0),\n"\
-" v4AssignMode varchar(8) NOT NULL DEFAULT('none'),\n"\
-" v6AssignMode varchar(8) NOT NULL DEFAULT('none'),\n"\
-" multicastLimit integer NOT NULL DEFAULT(32),\n"\
-" creationTime integer NOT NULL DEFAULT(0),\n"\
-" 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"\
+" 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 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"\
-" lastAt varchar(64),\n"\
-" lastSeen integer NOT NULL DEFAULT(0),\n"\
-" firstSeen integer NOT NULL DEFAULT(0)\n"\
-");\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 496ba32c..4f661b8d 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 5b725174..16e3f4d5 100644
--- a/node/InetAddress.hpp
+++ b/node/InetAddress.hpp
@@ -266,6 +266,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
*/
InetAddress netmask() const
diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp
index 4b9620a6..5ed1dd3f 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<std::string> gatewaysSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS,"").c_str(),",","",""));
+ for(std::vector<std::string>::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<std::string> activeBridgesSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES,"").c_str(),",","",""));
for(std::vector<std::string>::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 89d1aec5..2fb56d6a 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<InetAddress> &staticIps() const throw() { return _staticIps; }
+ inline const std::vector<InetAddress> &gateways() const throw() { return _gateways; }
inline const std::vector<Address> &activeBridges() const throw() { return _activeBridges; }
inline const std::vector< std::pair<Address,InetAddress> > &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<InetAddress> _staticIps;
+ std::vector<InetAddress> _gateways;
std::vector<Address> _activeBridges;
std::vector< std::pair<Address,InetAddress> > _relays;
std::map<MulticastGroup,MulticastRate> _multicastRates;
diff --git a/node/Packet.cpp b/node/Packet.cpp
index a81873ff..f75d1df0 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 2dfb75e4..1ec145d5 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 b373d9e2..d477f2e3 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<MulticastGroup> &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;
- }
+ _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;
+ }
- {
- uint32_t tmpi = 1;
- DWORD bytesReturned = 0;
- DeviceIoControl(_tap,TAP_WIN_IOCTL_SET_MEDIA_STATUS,&tmpi,sizeof(tmpi),&tmpi,sizeof(tmpi),&bytesReturned,NULL);
- }
+ {
+ 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;
+ {
+ /* 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);
-
- 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)
-
- 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
+ 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
+
+ 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)
+
+ // 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;r<ift->NumEntries;++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 (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;
- }
+ 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);
+ }
- _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();
+
+ if (!_injectPending.empty()) {
+ WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&tapOvlWrite);
+ writeInProgress = true;
+ }
- CancelIo(_tap);
+ _injectPending_m.unlock();
+ }
- CloseHandle(tapOvlRead.hEvent);
- CloseHandle(tapOvlWrite.hEvent);
- CloseHandle(_tap);
- _tap = INVALID_HANDLE_VALUE;
+ CancelIo(_tap);
- // We will restart and re-open the tap unless _run == false
- }
+ CloseHandle(tapOvlRead.hEvent);
+ CloseHandle(tapOvlWrite.hEvent);
+ CloseHandle(_tap);
+ _tap = INVALID_HANDLE_VALUE;
- ::free(tapReadBuf);
+ // We will restart and re-open the tap unless _run == false
+ }
+ } 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 67046763..944b53f3 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<MulticastGroup> _multicastGroups;
diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp
index 71b3fd3f..4158ed43 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<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
+ scode = _controller->handleControlPlaneHttpGET(std::vector<std::string>(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<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
+ scode = _controller->handleControlPlaneHttpPOST(std::vector<std::string>(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<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
+ scode = _controller->handleControlPlaneHttpDELETE(std::vector<std::string>(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 acad97a1..2a283cbd 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, <u>only rules specifying allowed Ethernet types are used</u>. The database supports a richer rule set, but this is not implemented yet in the client. <u>Other types of rules will have no effect</u> (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
<table>
<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td></tr>
-<tr><td>ruleId</td><td>integer</td><td>User-defined rule ID and sort order</td></tr>
+<tr><td>ruleNo</td><td>integer</td><td>User-defined rule ID and sort order</td></tr>
<tr><td>nodeId</td><td>string</td><td>10-digit hex ZeroTier address of node (a.k.a. "port on switch")</td></tr>
<tr><td>vlanId</td><td>integer</td><td>Ethernet VLAN ID</td></tr>
<tr><td>vlanPcp</td><td>integer</td><td>Ethernet VLAN priority code point (PCP) ID</td></tr>