Jelajahi Sumber

Clean out unused netconf fields, rename a few, work on new netconf server.

Adam Ierymenko 11 tahun lalu
induk
melakukan
1e6475fad6
4 mengubah file dengan 293 tambahan dan 241 penghapusan
  1. 284 216
      netconf-service/index.js
  2. 1 1
      netconf-service/redis-schema.md
  3. 1 5
      node/NetworkConfig.cpp
  4. 7 19
      node/NetworkConfig.hpp

+ 284 - 216
netconf-service/index.js

@@ -26,20 +26,15 @@
 //
 //
 
 
 // Fields in netconf response dictionary
 // 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_ALLOWED_ETHERNET_TYPES = "et";
 var ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID = "nwid";
 var ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID = "nwid";
 var ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP = "ts";
 var ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP = "ts";
 var ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO = "id";
 var ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO = "id";
 var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS = "mpb";
 var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS = "mpb";
 var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH = "md";
 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_PRIVATE = "p";
+var ZT_NETWORKCONFIG_DICT_KEY_NAME = "n";
+var ZT_NETWORKCONFIG_DICT_KEY_DESC = "d";
 var ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC = "v4s";
 var ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC = "v4s";
 var ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC = "v6s";
 var ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC = "v6s";
 var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES = "mr";
 var ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES = "mr";
@@ -48,6 +43,9 @@ var ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP = "com";
 // Path to zerotier-idtool binary, invoked to enerate certificates of membership
 // Path to zerotier-idtool binary, invoked to enerate certificates of membership
 var ZEROTIER_IDTOOL = '/usr/local/bin/zerotier-idtool';
 var ZEROTIER_IDTOOL = '/usr/local/bin/zerotier-idtool';
 
 
+// From Constants.hpp in node/
+var ZT_NETWORK_AUTOCONF_DELAY = 60000;
+
 // Connect to redis, assuming database 0 and no auth (for now)
 // Connect to redis, assuming database 0 and no auth (for now)
 var redis = require('redis');
 var redis = require('redis');
 var DB = redis.createClient();
 var DB = redis.createClient();
@@ -58,6 +56,8 @@ DB.on("error",function(err) {
 // Global variables -- these are initialized on startup or netconf-init message
 // Global variables -- these are initialized on startup or netconf-init message
 var netconfSigningIdentity = null; // identity of netconf master, with private key portion
 var netconfSigningIdentity = null; // identity of netconf master, with private key portion
 
 
+var spawn = require('child_process').spawn;
+
 function ztDbTrue(v) { return ((v === '1')||(v === 'true')||(v > 0)); }
 function ztDbTrue(v) { return ((v === '1')||(v === 'true')||(v > 0)); }
 function csvToArray(csv) { return (((typeof csv === 'string')&&(csv.length > 0)) ? csv.split(',') : []); }
 function csvToArray(csv) { return (((typeof csv === 'string')&&(csv.length > 0)) ? csv.split(',') : []); }
 function arrayToCsv(a) { return ((Array.isArray(a)) ? ((a.length > 0) ? a.join(',') : '') : (((a !== null)&&(typeof a !== 'undefined')) ? a.toString() : '')); }
 function arrayToCsv(a) { return ((Array.isArray(a)) ? ((a.length > 0) ? a.join(',') : '') : (((a !== null)&&(typeof a !== 'undefined')) ? a.toString() : '')); }
@@ -208,241 +208,309 @@ function Identity(idstr)
 		thiz.fromString(idstr);
 		thiz.fromString(idstr);
 };
 };
 
 
+function generateCertificateOfMembership(nwid,peerAddress,callback)
+{
+	var comTimestamp = '0,' + Date.now().toString(16) + ',' + (ZT_NETWORK_AUTOCONF_DELAY * 4).toString(16);
+	var comNwid = '1,' + nwid + ',0';
+	var comIssuedTo = '2,' + peerAddress + ',ffffffffffffffff';
+	var cert = '';
+	var idtool = spawn(ZEROTIER_IDTOOL,[ 'mkcom',netconfSigningIdentity,comTimestamp,comNwid,comIssuedTo ]);
+	idtool.stdout.on('data',function(data) {
+		if (typeof data === 'string')
+			cert += data;
+	});
+	idtool.on('close',function(exitCode) {
+		return callback((cert.length > 0) ? cert : null,exitCode);
+	});
+};
+
 //
 //
 // Message handler for messages over ZeroTier One service bus
 // Message handler for messages over ZeroTier One service bus
 //
 //
 
 
-function handleMessage(dictStr)
+function doNetconfInit(message)
 {
 {
-	var message = new Dictionary(dictStr);
+	netconfSigningIdentity = new Identity(message.data['netconfId']);
+	if (!netconfSigningIdentity.hasPrivate()) {
+		netconfSigningIdentity = null;
+		console.error('got invalid netconf signing identity in netconf-init');
+	}
+}
 
 
-	if (!('type' in message.data)) {
-		console.error('ignored message without request type field');
+function doNetconfRequest(message)
+{
+	if ((!netconfSigningIdentity)||(!netconfSigningIdentity.hasPrivate())) {
+		console.error('got netconf-request before netconf-init, ignored');
 		return;
 		return;
 	}
 	}
 
 
-	if (message.data['type'] === 'netconf-init') {
+	// 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;
 
 
-		netconfSigningIdentity = new Identity(message.data['netconfId']);
-		if (!netconfSigningIdentity.hasPrivate()) {
-			netconfSigningIdentity = null;
-			console.error('got invalid netconf signing identity');
-		}
+	var network = null;
+	var member = null;
 
 
-	} else if (message.data['type'] === 'netconf-request') {
-		if ((!netconfSigningIdentity)||(!netconfSigningIdentity.hasPrivate())) {
-			console.error('got netconf-request before netconf-init, ignored');
-			return;
-		}
+	var authorized = false;
 
 
-		// 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;
+	var v4NeedAssign = false;
+	var v6NeedAssign = false;
+	var v4Assignments = [];
+	var v6Assignments = [];
+	var ipAssignments = []; // both v4 and v6
+
+	async.series([function(next) {
 
 
-		// Get optional fields
-		var meta = new Dictionary(message.data['meta']);
-		var clientVersion = message.data['clientVersion'];
-		var clientOs = message.data['clientOs'];
-
-		var network = null;
-		var member = null;
-		var authorized = false;
-		var v4NeedAssign = false;
-		var v6NeedAssign = false;
-		var v4Assignments = [];
-		var v6Assignments = [];
-
-		async.series([function(next) { // network lookup
-			DB.hgetall('zt1:network:'+nwid+':~',function(err,obj) {
-				network = obj;
+		// network lookup
+		DB.hgetall('zt1:network:'+nwid+':~',function(err,obj) {
+			network = obj;
+			return next(err);
+		});
+
+	},function(next) {
+
+		// member record lookup, unless public network
+		if ((!network)||(!('nwid' in network)||(network['nwid'] !== nwid))
+			return next(null);
+
+		var memberKey = 'zt1:network:'+nwid+':member:'+peerId.address()+':~';
+		DB.hgetall(memberKey,function(err,obj) {
+			if (err)
 				return next(err);
 				return next(err);
-			});
-		},function(next) { // member record lookup, unless public network
-			if ((!network)||(!('nwid' in network)||(network['nwid'] !== nwid))
-				return next(null);
-			var memberKey = 'zt1:network:'+nwid+':member:'+peerId.address()+':~';
-			DB.hgetall(memberKey,function(err,obj) {
-				if (err)
-					return next(err);
-				else if (obj) {
-					// Update member object
-					member = obj;
-					authorized = (ztDbTrue(network['private']) || ztDbTrue(member['authorized']));
-					DB.hmset(memberKey,{
-						'lastSeen': Date.now(),
-						'lastAt': fromIpAndPort,
-						'clientVersion': (clientVersion) ? clientVersion : '?.?.?',
-						'clientOs': (clientOs) ? clientOs : '?'
-					},next);
-				} else {
-					// Add member object for new and unauthorized member
-					authorized = false;
-					member = {
-						'id': peerId.address(),
-						'nwid': nwid,
-						'authorized': 0,
-						'identity': peerId.toString(),
-						'firstSeen': Date.now(),
-						'lastSeen': Date.now(),
-						'lastAt': fromIpAndPort,
-						'clientVersion': (clientVersion) ? clientVersion : '?.?.?',
-						'clientOs': (clientOs) ? clientOs : '?'
-					};
-					DB.hmset(memberKey,member,next);
-				}
-			});
-		},function(next) { // IP address auto-assignment, if needed
-			if (!authorized)
-				return next(null);
-
-			v4NeedAssign = (network['v4AssignMode'] === 'zt');
-			v6NeedAssign = (network['v6AssignMode'] === 'zt');
-
-			var ipa = csvToArray(member['ipAssignments']);
-			for(var i=0;i<ipa.length;++i) {
-				if ((ipa[i].indexOf('.') > 0)&&(v4NeedAssign))
-					v4Assignments.push(ipa[i]);
-				else if ((ipa[i].indexOf(':') > 0)&&(v6NeedAssign))
-					v6Assignments.push(ipa[i]);
+
+			if (obj) {
+				// Update existing member record with new last seen time, etc.
+				member = obj;
+				authorized = (ztDbTrue(network['private']) || ztDbTrue(member['authorized']));
+				DB.hmset(memberKey,{
+					'lastSeen': Date.now(),
+					'lastAt': fromIpAndPort,
+					'clientVersion': (clientVersion) ? clientVersion : '?.?.?',
+					'clientOs': (clientOs) ? clientOs : '?'
+				},next);
+			} else {
+				// Add member record to network for newly seen peer
+				authorized = ztDbTrue(network['private']) ? false : true; // public networks authorize everyone by default
+				var now = Date.now().toString();
+				member = {
+					'id': peerId.address(),
+					'nwid': nwid,
+					'authorized': authorized ? '1' : '0',
+					'identity': peerId.toString(),
+					'firstSeen': now,
+					'lastSeen': now,
+					'lastAt': fromIpAndPort,
+					'clientVersion': (message.data['clientVersion']) ? message.data['clientVersion'] : '?.?.?',
+					'clientOs': (message.data['clientOs']) ? message.data['clientOs'] : '?'
+				};
+				DB.hmset(memberKey,member,next);
 			}
 			}
+		});
 
 
+	},function(next) {
+
+		// Figure out which IP address auto-assignments we need to look up or make
+		if (!authorized)
 			return next(null);
 			return next(null);
-		},function(next) { // assign IPv4 if needed
-			if ((!authorized)||(!v4NeedAssign))
-				return next(null);
-
-			var ipAssignmentAttempts = 0; // for sanity-checking
-			var v4pool = network['v4AssignPool'];
-			var ztaddr = peerId.address();
-
-			var network = 0;
-			var netmask = 0;
-			var netmaskBits = 0;
-			if (v4pool) {
-				var v4poolSplit = v4Pool.split('/');
-				if (v4poolSplit.length === 2) {
-					var networkSplit = v4poolSplit[0].split('.');
-					if (networkSplit.length === 4) {
-						network |= (parseInt(networkSplit[0],10) << 24) & 0xff000000;
-						network |= (parseInt(networkSplit[1],10) << 16) & 0x00ff0000;
-						network |= (parseInt(networkSplit[2],10) << 8) & 0x0000ff00;
-						network |= parseInt(networkSplit[3],10) & 0x000000ff;
-						netmaskBits = parseInt(v4poolSplit[1],10);
-						if (netmaskBits > 32)
-							netmaskBits = 32; // sanity check
-						for(var i=0;i<netmaskBits;++i)
-							netmask |= (0x80000000 >> i);
-					}
+
+		v4NeedAssign = (network['v4AssignMode'] === 'zt');
+		v6NeedAssign = (network['v6AssignMode'] === 'zt');
+
+		var ipa = csvToArray(member['ipAssignments']);
+		for(var i=0;i<ipa.length;++i) {
+			if (ipa[i])
+				ipAssignments.push(ipa[i]);
+			if ((ipa[i].indexOf('.') > 0)&&(v4NeedAssign)) {
+				v4Assignments.push(ipa[i]);
+			} else if ((ipa[i].indexOf(':') > 0)&&(v6NeedAssign)) {
+				v6Assignments.push(ipa[i]);
+			}
+		}
+
+		return next(null);
+
+	},function(next) {
+
+		// assign IPv4 if needed
+		if ((!authorized)||(!v4NeedAssign)||(v4Assignments.length > 0))
+			return next(null);
+
+		var ipAssignmentAttempts = 0;
+		var v4pool = network['v4AssignPool']; // technically csv but only one netblock currently supported
+		var peerAddress = peerId.address();
+
+		var network = 0;
+		var netmask = 0;
+		var netmaskBits = 0;
+		if (v4pool) {
+			var v4poolSplit = v4Pool.split('/');
+			if (v4poolSplit.length === 2) {
+				var networkSplit = v4poolSplit[0].split('.');
+				if (networkSplit.length === 4) {
+					network |= (parseInt(networkSplit[0],10) << 24) & 0xff000000;
+					network |= (parseInt(networkSplit[1],10) << 16) & 0x00ff0000;
+					network |= (parseInt(networkSplit[2],10) << 8) & 0x0000ff00;
+					network |= parseInt(networkSplit[3],10) & 0x000000ff;
+					netmaskBits = parseInt(v4poolSplit[1],10);
+					if (netmaskBits > 32)
+						netmaskBits = 32; // sanity check
+					for(var i=0;i<netmaskBits;++i)
+						netmask |= (0x80000000 >> i);
+					netmask &= 0xffffffff;
 				}
 				}
 			}
 			}
-			var invmask = netmask ^ 0xffffffff;
-			var abcd = 0;
-			var assignment = null;
-
-			var ipAssignmentsKey = 'zt1:network:'+nwid+':ipAssignments';
-			var memberKey = 'zt1:network:'+nwid+':member:'+ztaddr+':~';
-
-			async.whilst(
-				function() { return ((v4NeedAssign)&&(v4Assignments.length === 0)&&(network !== 0)&&(netmask !== 0xffffffff)&&(ipAssignmentAttempts < 1000)); },
-				function(next2) {
-					++ipAssignmentAttempts;
-
-					// Generate or increment IP address
-					if (abcd === 0) {
-						var a = parseInt(ztaddr.substr(2,2),16) & 0xff;
-						var b = parseInt(ztaddr.substr(4,2),16) & 0xff;
-						var c = parseInt(ztaddr.substr(6,2),16) & 0xff;
-						var d = parseInt(ztaddr.substr(8,2),16) & 0xff;
-						abcd = (a << 24) | (b << 16) | (c << 8) | d;
-					} else ++abcd;
-					if ((abcd & 0xff) === 0)
-						abcd |= 1;
-
-					// Derive an IP to test and generate assignment ip/bits string
-					var ip = (abcd & invmask) | (network & netmask);
-					assignment = ((ip >> 24) & 0xff).toString(10) + '.' + ((ip >> 16) & 0xff).toString(10) + '.' + ((ip >> 8) & 0xff).toString(10) + '.' + (ip & 0xff).toString(10) + '/' + netmaskBits.toString(10);
-
-					DB.hget(ipAssignmentsKey,assignment,function(err,value) {
+		}
+		if ((network === 0)||(netmask === 0xffffffff))
+			return next(null);
+		var invmask = netmask ^ 0xffffffff;
+		var abcd = 0;
+
+		var assignment = null;
+
+		var ipAssignmentsKey = 'zt1:network:'+nwid+':ipAssignments';
+		var memberKey = 'zt1:network:'+nwid+':member:'+peerAddress+':~';
+
+		async.whilst(
+			function() { return ((v4Assignments.length === 0)&&(ipAssignmentAttempts < 1000)); },
+			function(next2) {
+				++ipAssignmentAttempts;
+
+				// Generate or increment IP address source bits
+				if (abcd === 0) {
+					var a = parseInt(peerAddress.substr(2,2),16) & 0xff;
+					var b = parseInt(peerAddress.substr(4,2),16) & 0xff;
+					var c = parseInt(peerAddress.substr(6,2),16) & 0xff;
+					var d = parseInt(peerAddress.substr(8,2),16) & 0xff;
+					abcd = (a << 24) | (b << 16) | (c << 8) | d;
+				} else ++abcd;
+				if ((abcd & 0xff) === 0)
+					abcd |= 1;
+				abcd &= 0xffffffff;
+
+				// Derive an IP to test and generate assignment ip/bits string
+				var ip = (abcd & invmask) | (network & netmask);
+				assignment = ((ip >> 24) & 0xff).toString(10) + '.' + ((ip >> 16) & 0xff).toString(10) + '.' + ((ip >> 8) & 0xff).toString(10) + '.' + (ip & 0xff).toString(10) + '/' + netmaskBits.toString(10);
+
+				// Check :ipAssignments to see if this IP is already taken
+				DB.hget(ipAssignmentsKey,assignment,function(err,value) {
+					if (err)
+						return next2(err);
+
+					// IP is already taken, try again via async.whilst()
+					if ((value)&&(value !== peerAddress))
+						return next2(null); // if someone's already got this IP, keep looking
+
+					v4Assignments.push(assignment);
+					ipAssignments.push(assignment);
+
+					// Save assignment to :ipAssignments hash
+					DB.hset(ipAssignmentsKey,assignment,peerAddress,function(err) {
 						if (err)
 						if (err)
 							return next2(err);
 							return next2(err);
-						if ((value)&&(value !== ztaddr))
-							return next2(null); // if someone's already got this IP, keep looking
-
-						v4Assignments.push(assignment);
-
-						// Save assignment to :ipAssignments hash
-						DB.hset(ipAssignmentsKey,assignment,ztaddr,function(err) {
-							if (err)
-								return next2(err);
-
-							// Save updated CSV list of assignments to member record
-							var ipAssignments = member['ipAssignments'];
-							if (!ipAssignments)
-								ipAssignments = '';
-							if (ipAssignments.length > 0)
-								ipAssignments += ',';
-							ipAssignments += assignment;
-							member['ipAssignments'] = ipAssignments;
-							DB.hset(memberKey,'ipAssignments',ipAssignments,next2);
-						});
+
+						// Save updated CSV list of assignments to member record
+						var ipacsv = ipAssignments.join(',');
+						member['ipAssignments'] = ipacsv;
+						DB.hset(memberKey,'ipAssignments',ipacsv,next2);
 					});
 					});
-				},
-				next
-			);
+				});
+			},
+			next
+		);
 
 
-		},function(next) { // assign IPv6 if needed -- TODO
-			if ((!authorized)||(!v6NeedAssign))
-				return next(null);
+	},function(next) {
 
 
+		// assign IPv6 if needed -- TODO
+		if ((!authorized)||(!v6NeedAssign)||(v6Assignments.length > 0))
 			return next(null);
 			return next(null);
-		}],function(err) {
-			if (err) {
-				console.log('error composing response for '+peerId.address()+': '+err);
-				return;
-			} else if (authorized) {
-				// TODO: COM!!!
-				var certificateOfMembership = null;
-
-				var netconf = new Dictionary();
-				netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NETCONF_SERVICE_VERSION] = '0.0.0';
-				netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = network['etherTypes'];
-				netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = nwid;
-				netconf.data[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = Date.now().toString();
-				netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO] = peerId.address();
-				//netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS] = 0;
-				//netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH] = 0;
-				//netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES] = '';
-				//netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ARP_CACHE_TTL] = 0;
-				//netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NDP_CACHE_TTL] = 0;
-				netconf.data[ZT_NETWORKCONFIG_DICT_KEY_EMULATE_ARP] = '0';
-				netconf.data[ZT_NETWORKCONFIG_DICT_KEY_EMULATE_NDP] = '0';
-				netconf.data[ZT_NETWORKCONFIG_DICT_KEY_IS_OPEN] = ztDbTrue(network['private']) ? '0' : '1';
-				netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NAME] = network['name'];
-				if (network['desc'])
-					netconf.data[ZT_NETWORKCONFIG_DICT_KEY_DESC] = network['desc'];
-				if (v4NeedAssign)
-					netconf.data[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = (v4Assignments.length > 0) ? v4Assignments.join(',') : '';
-				if (v6NeedAssign)
-					netconf.data[ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC] = (v6Assignments.length > 0) ? v6Assignments.join(',') : '';
-				if (certificateOfMembership !== null)
-					netconf.data[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = certificateOfMembership;
-
-				var response = new Dictionary();
-				response.data['peer'] = peerId.address();
-				response.data['nwid'] = nwid;
-				response.data['type'] = 'netconf-response';
-				response.data['requestId'] = requestId;
-				response.data['netconf'] = netconf.toString();
+
+		return next(null);
+
+	}],function(err) {
+
+		if (err) {
+			console.log('error composing response for '+peerId.address()+': '+err);
+			return;
+		}
+
+		var response = new Dictionary();
+		response.data['peer'] = peerId.address();
+		response.data['nwid'] = nwid;
+		response.data['type'] = 'netconf-response';
+		response.data['requestId'] = requestId;
+
+		if (authorized) {
+			var certificateOfMembership = null;
+			var privateNetwork = ztDbTrue(network['private']);
+
+			async.series([function(next) {
+
+				// Generate certificate of membership if necessary
+				if (privateNetwork) {
+					generateCertificateOfMembership(nwid,peerId.address(),function(cert,exitCode) {
+						if (cert) {
+							certificateOfMembership = cert;
+							return next(null);
+						} else return next(new Error('zerotier-idtool returned '+exitCode));
+					});
+				} else return next(null);
+
+			}],function(err) {
+
+				if (err) {
+					console.error('unable to generate certificate for peer '+peerId.address()+' on network '+nwid+': '+err);
+					response.data['error'] = 'ACCESS_DENIED'; // unable to generate certificate
+				} else {
+					var netconf = new Dictionary();
+					netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = network['etherTypes'];
+					netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = nwid;
+					netconf.data[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = Date.now().toString(16);
+					netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO] = peerId.address();
+					//netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS] = 0;
+					//netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH] = 0;
+					//netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES] = '';
+					netconf.data[ZT_NETWORKCONFIG_DICT_KEY_PRIVATE] = privateNetwork ? '1' : '0';
+					netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NAME] = network['name'];
+					if (network['desc'])
+						netconf.data[ZT_NETWORKCONFIG_DICT_KEY_DESC] = network['desc'];
+					if ((v4NeedAssign)&&(v4Assignments.length > 0))
+						netconf.data[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4Assignments.join(',');
+					if ((v6NeedAssign)&&(v6Assignments.length > 0))
+						netconf.data[ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC] = v6Assignments.join(',');
+					if (certificateOfMembership !== null)
+						netconf.data[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = certificateOfMembership;
+					response.data['netconf'] = netconf.toString();
+				}
 
 
 				process.stdout.write(response.toString()+'\n');
 				process.stdout.write(response.toString()+'\n');
-				return;
-			} else {
-			}
-		});
+
+			});
+
+		} else {
+
+			// Peer not authorized to join network
+			response.data['error'] = 'ACCESS_DENIED';
+			process.stdout.write(response.toString()+'\n');
+
+		}
+
+	});
+}
+
+function handleMessage(dictStr)
+{
+	var message = new Dictionary(dictStr);
+	if (!('type' in message.data)) {
+		console.error('ignored message without request type field');
+		return;
+	} else if (message.data['type'] === 'netconf-init') {
+		doNetconfInit(message);
+	} else if (message.data['type'] === 'netconf-request') {
+		doNetconfRequest(message);
 	} else {
 	} else {
 		console.error('ignored unrecognized message type: '+message.data['type']);
 		console.error('ignored unrecognized message type: '+message.data['type']);
 	}
 	}

+ 1 - 1
netconf-service/redis-schema.md

@@ -58,7 +58,7 @@ Each network has a network record indexed by its 64-bit network ID in lower-case
 - R infrastructure :: if true, network can't be deleted through API or web UI
 - R infrastructure :: if true, network can't be deleted through API or web UI
 - M private :: if true, network requires authentication
 - M private :: if true, network requires authentication
 - R creationTime :: timestamp of network creation
 - R creationTime :: timestamp of network creation
-- M etherTypes :: comma-delimited list of integers indicating Ethernet types permitted on network
+- M etherTypes :: comma-delimited list of *hexadecimal* integers indicating Ethernet types permitted on network
 - M enableBroadcast :: if true, ff:ff:ff:ff:ff:ff is enabled network-wide
 - M enableBroadcast :: if true, ff:ff:ff:ff:ff:ff is enabled network-wide
 - M v4AssignMode :: 'none' (or null/empty/etc.), 'zt', 'dhcp'
 - M v4AssignMode :: 'none' (or null/empty/etc.), 'zt', 'dhcp'
 - M v4AssignPool :: network/bits from which to assign IPs
 - M v4AssignPool :: network/bits from which to assign IPs

+ 1 - 5
node/NetworkConfig.cpp

@@ -84,11 +84,7 @@ void NetworkConfig::_fromDictionary(const Dictionary &d)
 	_issuedTo = Address(d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO));
 	_issuedTo = Address(d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO));
 	_multicastPrefixBits = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS,_zero).c_str());
 	_multicastPrefixBits = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS,_zero).c_str());
 	_multicastDepth = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH,_zero).c_str());
 	_multicastDepth = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH,_zero).c_str());
-	_arpCacheTtl = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ARP_CACHE_TTL,_zero).c_str());
-	_ndpCacheTtl = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_NDP_CACHE_TTL,_zero).c_str());
-	_emulateArp = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_EMULATE_ARP,_zero).c_str()) != 0);
-	_emulateNdp = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_EMULATE_NDP,_zero).c_str()) != 0);
-	_isOpen = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_IS_OPEN,_zero).c_str()) != 0);
+	_private = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE,_zero).c_str()) != 0);
 	_name = d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME);
 	_name = d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME);
 	_description = d.get(ZT_NETWORKCONFIG_DICT_KEY_DESC,std::string());
 	_description = d.get(ZT_NETWORKCONFIG_DICT_KEY_DESC,std::string());
 
 

+ 7 - 19
node/NetworkConfig.hpp

@@ -48,23 +48,18 @@ namespace ZeroTier {
 
 
 // These dictionary keys are short so they don't take up much room in
 // These dictionary keys are short so they don't take up much room in
 // netconf response packets.
 // netconf response packets.
-#define ZT_NETWORKCONFIG_DICT_KEY_NETCONF_SERVICE_VERSION "ncver"
 #define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES "et"
 #define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES "et"
 #define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid"
 #define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid"
 #define ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP "ts"
 #define ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP "ts"
 #define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id"
 #define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id"
 #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS "mpb"
 #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS "mpb"
 #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH "md"
 #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH "md"
-#define ZT_NETWORKCONFIG_DICT_KEY_ARP_CACHE_TTL "cARP"
-#define ZT_NETWORKCONFIG_DICT_KEY_NDP_CACHE_TTL "cNDP"
-#define ZT_NETWORKCONFIG_DICT_KEY_EMULATE_ARP "eARP"
-#define ZT_NETWORKCONFIG_DICT_KEY_EMULATE_NDP "eNDP"
-#define ZT_NETWORKCONFIG_DICT_KEY_IS_OPEN "o"
-#define ZT_NETWORKCONFIG_DICT_KEY_NAME "name"
-#define ZT_NETWORKCONFIG_DICT_KEY_DESC "desc"
+#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES "mr"
+#define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE "p"
+#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
+#define ZT_NETWORKCONFIG_DICT_KEY_DESC "d"
 #define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC "v4s"
 #define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC "v4s"
 #define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC "v6s"
 #define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC "v6s"
-#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES "mr"
 #define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP "com"
 #define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP "com"
 
 
 /**
 /**
@@ -122,11 +117,8 @@ public:
 	inline const Address &issuedTo() const throw() { return _issuedTo; }
 	inline const Address &issuedTo() const throw() { return _issuedTo; }
 	inline unsigned int multicastPrefixBits() const throw() { return _multicastPrefixBits; }
 	inline unsigned int multicastPrefixBits() const throw() { return _multicastPrefixBits; }
 	inline unsigned int multicastDepth() const throw() { return _multicastDepth; }
 	inline unsigned int multicastDepth() const throw() { return _multicastDepth; }
-	inline unsigned int arpCacheTtl() const throw() { return _arpCacheTtl; }
-	inline unsigned int ndpCacheTtl() const throw() { return _ndpCacheTtl; }
-	inline bool emulateArp() const throw() { return _emulateArp; }
-	inline bool emulateNdp() const throw() { return _emulateNdp; }
-	inline bool isOpen() const throw() { return _isOpen; }
+	inline bool isOpen() const throw() { return (!_private); }
+	inline bool isPrivate() const throw() { return _private; }
 	inline const std::string &name() const throw() { return _name; }
 	inline const std::string &name() const throw() { return _name; }
 	inline const std::string &description() const throw() { return _description; }
 	inline const std::string &description() const throw() { return _description; }
 	inline const std::set<InetAddress> &staticIps() const throw() { return _staticIps; }
 	inline const std::set<InetAddress> &staticIps() const throw() { return _staticIps; }
@@ -162,11 +154,7 @@ private:
 	Address _issuedTo;
 	Address _issuedTo;
 	unsigned int _multicastPrefixBits;
 	unsigned int _multicastPrefixBits;
 	unsigned int _multicastDepth;
 	unsigned int _multicastDepth;
-	unsigned int _arpCacheTtl;
-	unsigned int _ndpCacheTtl;
-	bool _emulateArp;
-	bool _emulateNdp;
-	bool _isOpen;
+	bool _private;
 	std::string _name;
 	std::string _name;
 	std::string _description;
 	std::string _description;
 	std::set<InetAddress> _staticIps;
 	std::set<InetAddress> _staticIps;