Browse Source

Some work on new netconf service...

Adam Ierymenko 11 years ago
parent
commit
93f24ea86a
4 changed files with 278 additions and 18 deletions
  1. 1 2
      .gitignore
  2. 0 16
      netconf-service/README.txt
  3. 263 0
      netconf-service/index.js
  4. 14 0
      netconf-service/package.json

+ 1 - 2
.gitignore

@@ -6,8 +6,7 @@
 .DS_Store
 .Apple*
 *.dSYM
-/netconf-service/netconf-test
-/netconf-service/netconf.service
+/netconf-service/node_modules
 /ipch
 /windows/ZeroTierOne.sdf
 /windows/ZeroTierOne.v11.suo

+ 0 - 16
netconf-service/README.txt

@@ -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.

+ 263 - 0
netconf-service/index.js

@@ -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');

+ 14 - 0
netconf-service/package.json

@@ -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"
+  }
+}