4 changed files with 278 additions and 18 deletions
@ -1,16 +0,0 @@
|
||||
This is the netconf service, which can be built and run by placing it in the |
||||
services.d subfolder of the ZeroTier One home directory. |
||||
|
||||
Users probably won't be interested in this. It's for running a "netconf |
||||
master," which handles certificate issuing, static IP assignment, and other |
||||
things for a network. The ZeroTier address of the netconf master forms the |
||||
first 40 bits of a hexadecimal network ID, permitting the master to be |
||||
located and queried. |
||||
|
||||
Masters currently don't support multi-homing, but they can easily be made |
||||
fault tolerant via fail-over. If the master node goes down, it can be |
||||
started elsewhere with the same ZT1 identity. (The underlying database would |
||||
also have to be fault tolerant.) |
||||
|
||||
For this to work it requires a MySQL backend with a properly structured |
||||
database. |
||||
@ -0,0 +1,263 @@
|
||||
//
|
||||
// ZeroTier One - Global Peer to Peer Ethernet
|
||||
// Copyright (C) 2011-2014 ZeroTier Networks LLC
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// --
|
||||
//
|
||||
// ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
// are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
//
|
||||
// If you would like to embed ZeroTier into a commercial application or
|
||||
// redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
// LLC. Start here: http://www.zerotier.com/
|
||||
//
|
||||
|
||||
// Fields in netconf response dictionary
|
||||
var ZT_NETWORKCONFIG_DICT_KEY_NETCONF_SERVICE_VERSION = "ncver"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES = "et"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID = "nwid"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP = "ts"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO = "id"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS = "mpb"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH = "md"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_ARP_CACHE_TTL = "cARP"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_NDP_CACHE_TTL = "cNDP"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_EMULATE_ARP = "eARP"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_EMULATE_NDP = "eNDP"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_IS_OPEN = "o"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_NAME = "name"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_DESC = "desc"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC = "v4s"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC = "v6s"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES = "mr"; |
||||
var ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP = "com"; |
||||
|
||||
// Path to zerotier-idtool
|
||||
var ZEROTIER_IDTOOL = '/usr/local/bin/zerotier-idtool'; |
||||
|
||||
// Connect to redis, assuming database 0 and no auth (for now)
|
||||
var redis = require('redis'); |
||||
var DB = redis.createClient(); |
||||
DB.on("error",function(err) { |
||||
console.error('Redis query error: '+err); |
||||
}); |
||||
|
||||
// Encoding-compatible with Dictionary.hpp in ZeroTier One C++ code base
|
||||
function Dictionary(fromStr) |
||||
{ |
||||
var thiz = this; |
||||
|
||||
this.data = {}; |
||||
|
||||
this._esc = function(data) { |
||||
var es = ''; |
||||
for(var i=0;i<data.length;++i) { |
||||
var c = data.charAt(i); |
||||
switch(c) { |
||||
case '\0': es += '\\0'; break; |
||||
case '\r': es += '\\r'; break; |
||||
case '\n': es += '\\n'; break; |
||||
case '\\': es += '\\\\'; break; |
||||
case '=': es += '\\='; break; |
||||
default: es += c; break; |
||||
} |
||||
} |
||||
return es; |
||||
}; |
||||
this._unesc = function(s) { |
||||
if (typeof s !== 'string') |
||||
return ''; |
||||
var uns = ''; |
||||
var escapeState = false; |
||||
for(var i=0;i<s.length;++i) { |
||||
var c = s.charAt(i); |
||||
if (escapeState) { |
||||
escapeState = false; |
||||
switch(c) { |
||||
case '0': uns += '\0'; break; |
||||
case 'r': uns += '\r'; break; |
||||
case 'n': uns += '\n'; break; |
||||
default: uns += c; break; |
||||
} |
||||
} else{ |
||||
if ((c !== '\r')&&(c !== '\n')&&(c !== '\0')) { |
||||
if (c === '\\') |
||||
escapeState = true; |
||||
else uns += c; |
||||
} |
||||
} |
||||
} |
||||
return uns; |
||||
}; |
||||
|
||||
this.toString = function() { |
||||
var str = ''; |
||||
|
||||
for(var key in thiz.data) { |
||||
str += thiz._esc(key); |
||||
str += '='; |
||||
var value = thiz.data[key]; |
||||
if (value) |
||||
str += thiz._esc(value.toString()); |
||||
str += '\n'; |
||||
} |
||||
|
||||
return str; |
||||
}; |
||||
|
||||
this.fromString = function(str) { |
||||
thiz.data = {}; |
||||
if (typeof str !== 'string') |
||||
return thiz; |
||||
|
||||
var lines = str.split('\n'); |
||||
for(var l=0;l<lines.length;++l) { |
||||
var escapeState = false; |
||||
var eqAt = 0; |
||||
for(;eqAt<lines[l].length;++eqAt) { |
||||
var c = lines[l].charAt(eqAt); |
||||
if (escapeState) |
||||
escapeState = false; |
||||
else if (c === '\\') |
||||
escapeState = true; |
||||
else if (c === '=') |
||||
break; |
||||
} |
||||
|
||||
var k = thiz._unesc(lines[l].substr(0,eqAt)); |
||||
++eqAt; |
||||
if ((k)&&(k.length > 0)) |
||||
thiz.data[k] = thiz._unesc((eqAt < lines[l].length) ? lines[l].substr(eqAt) : ''); |
||||
} |
||||
|
||||
return thiz; |
||||
}; |
||||
|
||||
if ((typeof fromStr === 'string')&&(fromStr.length > 0)) |
||||
thiz.fromString(fromStr); |
||||
}; |
||||
|
||||
/* Dictionary tester |
||||
var testDict1 = new Dictionary(); |
||||
var testDict2 = new Dictionary(); |
||||
testDict1.data['foo'] = '1'; |
||||
testDict1.data['bar'] = 'The quick brown fox\ncontained a carriage return.'; |
||||
testDict2.data['embeddedDictionary'] = testDict1.toString(); |
||||
testDict2.data['baz'] = 'eklrjelkrnlqkejrnlkqerne'; |
||||
console.log(testDict2.toString()); |
||||
console.log('After fromString(toString())...\n'); |
||||
console.log((new Dictionary(testDict2.toString())).toString()); |
||||
process.exit(0); |
||||
*/ |
||||
|
||||
// Variables initialized by netconf-init message
|
||||
var netconfSigningIdentity = null; |
||||
|
||||
//
|
||||
// Identity implementation using zerotier-idtool as subprocess to do actual crypto work
|
||||
//
|
||||
|
||||
function Identity(idstr) |
||||
{ |
||||
var thiz = this; |
||||
|
||||
this.str = ''; |
||||
this.fields = []; |
||||
|
||||
this.toString = function() { |
||||
return thiz.str; |
||||
}; |
||||
|
||||
this.fromString = function(str) { |
||||
thiz.str = ''; |
||||
thiz.fields = []; |
||||
if (!str) |
||||
return; |
||||
var fields = str.split(':'); |
||||
if ((fields.length < 3)||(fields[0].length !== 10)||(fields[1] !== '0')) |
||||
return; |
||||
thiz.fields = fields; |
||||
} |
||||
|
||||
this.isValid = function() { |
||||
if ((thiz.fields.length < 3)||(thiz.fields[0].length !== 10)||(thiz.fields[1] !== '0')) |
||||
return true; |
||||
return false; |
||||
} |
||||
|
||||
if (typeof idstr === 'string') |
||||
thiz.fromString(idstr); |
||||
}; |
||||
|
||||
//
|
||||
// Message handler for messages over ZeroTier One service bus
|
||||
//
|
||||
|
||||
function handleMessage(dictStr) |
||||
{ |
||||
var message = new Dictionary(dictStr); |
||||
var response = new Dictionary(); |
||||
|
||||
if (!('type' in message.data)) |
||||
return; // no request type
|
||||
|
||||
if (message.data['type'] === 'netconf-init') { |
||||
netconfSigningIdentity = new Identity(message.data['netconfId']); |
||||
if (!netconfSigningIdentity.isValid()) |
||||
netconfSigningIdentity = null; // empty strings and such are not valid
|
||||
return; // no response expected
|
||||
} else if (message.data['type'] === 'netconf-request') { |
||||
// Get required fields
|
||||
var peerId = new Identity(message.data['peerId']); |
||||
var fromIpAndPort = message.data['from']; |
||||
var nwid = message.data['nwid']; |
||||
var requestId = message.data['requestId']; |
||||
if ((!peerId)||(!peerId.isValid())||(!fromIpAndPort)||(!nwid)||(nwid.length !== 16)||(!requestId)) |
||||
return; |
||||
|
||||
// Get optional fields
|
||||
var meta = new Dictionary(message.data['meta']); |
||||
} else return; |
||||
|
||||
process.stdout.write(response.toString()+'\n'); |
||||
}; |
||||
|
||||
//
|
||||
// Read stream of double-CR-terminated dictionaries from stdin until close/EOF
|
||||
//
|
||||
|
||||
var stdinReadBuffer = ''; |
||||
process.stdin.on('readable',function() { |
||||
var chunk = process.stdin.read(); |
||||
if (chunk) |
||||
stdinReadBuffer += chunk; |
||||
if ((stdinReadBuffer.length >= 2)&&(stdinReadBuffer.substr(stdinReadBuffer.length - 2) === '\n\n')) { |
||||
handleMessage(stdinReadBuffer); |
||||
stdinReadBuffer = ''; |
||||
} |
||||
}); |
||||
process.stdin.on('end',function() { |
||||
process.exit(0); |
||||
}); |
||||
process.stdin.on('close',function() { |
||||
process.exit(0); |
||||
}); |
||||
process.stdin.on('error',function() { |
||||
process.exit(0); |
||||
}); |
||||
|
||||
// Tell ZeroTier One that the service is running, solicit netconf-init
|
||||
process.stdout.write('type=ready\n\n'); |
||||
@ -0,0 +1,14 @@
|
||||
{ |
||||
"name": "zt1-netconf-service", |
||||
"version": "0.0.0", |
||||
"description": "Worker in charge of issuing network configuration from ZeroTier One netconf masters", |
||||
"main": "index.js", |
||||
"scripts": { |
||||
"test": "echo \"Error: no test specified\" && exit 1" |
||||
}, |
||||
"author": "ZeroTier Networks LLC", |
||||
"license": "GPL", |
||||
"dependencies": { |
||||
"redis": "~0.10.1" |
||||
} |
||||
} |
||||
Loading…
Reference in new issue