Compare commits

...
33 Commits
Author SHA1 Message Date
Adam Ierymenko d37c3ad30f VERSION 0.9.3: unreleased update with some fixes for Pi users
This version is being tagged and bagged, despite the fact that it's not
going to be released and won't be merged into master until 1.0.0 is ready.

It contains several Linux build fixes, a fix for a unix domain socket resource
leak, and build fixes for the Raspberry Pi.
2014-09-18 13:40:58 -07:00
Adam Ierymenko 4708231046 Fix for GitHub issue #122 and other fixes 2014-09-18 10:01:30 -07:00
Adam Ierymenko 924f030994 docs 2014-09-18 09:41:12 -07:00
Adam Ierymenko 6b76cac6cf Back off from thrashing shutdownIfUnreadableCheck, fix bug in control service. 2014-09-17 14:19:58 -07:00
Adam Ierymenko 2762db8408 More work on control bus refactor... 2014-09-17 12:54:39 -07:00
Adam Ierymenko edff13dcae More control/ refactoring 2014-09-17 09:50:49 -07:00
Adam Ierymenko e22fae2397 it compiles again! 2014-09-16 15:47:00 -07:00
Adam Ierymenko 73dd9d3b74 Make sure open() has third argument 2014-09-16 08:58:49 -07:00
Adam Ierymenko 8d2e20ede6 Get rid of __align stuff in Salsa20 -- not portable, does not seem to help much on newer chips. 2014-09-16 08:53:18 -07:00
Adam Ierymenko 5c1c70a604 cleanup 2014-09-16 08:19:39 -07:00
Adam Ierymenko fb22ef053b Move rest of NodeControl stuff out of node/ and into control/ 2014-09-15 16:32:55 -07:00
Adam Ierymenko 4ba4269344 cleanup 2014-09-15 09:45:57 -07:00
Adam Ierymenko 2dcf584834 rename ipc/ to control/ 2014-09-15 09:44:29 -07:00
Adam Ierymenko 13aba7640b refactoring for SDK 2014-09-15 09:43:57 -07:00
Adam Ierymenko 2e46bb4264 build fixes 2014-09-13 14:15:26 -07:00
Adam Ierymenko 1d37204a37 Refactoring in progress... pardon our dust... 2014-09-12 16:57:37 -07:00
Adam Ierymenko 6b4346d1ac work in progress on API refactor 2014-09-10 17:16:40 -07:00
Adam Ierymenko 4e9280fc7a Rip out dead "firewall opener" code, replace in pipeline with anti-symmetric-NAT tactics. 2014-09-05 16:23:24 -07:00
Adam Ierymenko d02ecfb288 IP assignment and change cleanup -- leave IPs that were never configured via ZT static assignment alone. 2014-09-05 15:35:09 -07:00
Adam Ierymenko 3afc629ac5 Bit of network config parser cleanup. 2014-09-05 14:56:11 -07:00
Adam Ierymenko 6e1339fedf Remove zerotier-idtool symlink on mac uninstall.sh 2014-09-05 11:39:54 -07:00
Adam Ierymenko 7d4b6767eb Old-school GCC build fix -- make sure __GCC__ is defined 2014-09-05 17:48:59 +00:00
Adam Ierymenko 93f9b4392d Enable configuration of multicast algorithm parameters in netconf 2014-09-04 14:25:07 -07:00
Adam Ierymenko 6df9546742 Fix for missing broadcast address on Linux Ethernet taps. 2014-09-04 13:36:25 -04:00
Adam Ierymenko 4dbeb97eb9 docs 2014-09-03 16:50:06 -07:00
Adam Ierymenko 34349d1a61 docs 2014-09-03 16:48:02 -07:00
Adam Ierymenko 6497a4a0f6 initdb changes 2014-09-03 19:15:53 -04:00
Adam Ierymenko b8d29cb6ba Some netconf-service renames and add initdb script for user use. 2014-09-03 20:04:58 +00:00
Adam Ierymenko e77e224171 bin2c warning removed 2014-09-03 12:25:28 -07:00
Adam Ierymenko dcea212e40 Add noupdate flag in root topologies, add ability for mktopology to read from a template. 2014-09-03 11:56:36 -07:00
Adam Ierymenko 644db7a04a Let people build installers for any machine type on Linux, just canonicalize the x86 and x86_64 types. 2014-08-27 20:15:41 -04:00
Adam Ierymenko 00b16f6aa6 Fix for GitHub issue #110 -- failure to bind IPv6 now non-fatal 2014-08-27 20:09:56 -04:00
Adam Ierymenko 1952db5069 Only enable SSE Salsa20 on x86 and x86_64 Linux 2014-08-27 20:01:24 -04:00
60 changed files with 2077 additions and 1119 deletions

No files matched your search

-27
View File
@@ -1,27 +0,0 @@
#!/usr/bin/ruby
#
# This can be used with the debug build option ZT_TRACE_MULTICAST to trace
# a multicast cascade.
#
# Define ZT_TRACE_MULTICAST to the IP/port where this script will be listening.
# The default port here is 6060, so an example would be to add:
#
# -DZT_TRACE_MULTICAST=\"10.0.0.1/6060\"
#
# ... to DEFS in the Makefile. Then build and run ZeroTier One on a testnet and
# the box defined as the trace endpoint will get spammed with UDP packets
# containing trace information for multicast propagation. This script then dumps
# these trace packets to stdout. Look at the code in PacketDecoder.cpp to see
# what this information entails.
#
require 'socket'
s = UDPSocket.new
s.bind('0.0.0.0',6060)
loop {
m = s.recvfrom(4096)[0].chomp
puts m if m.length > 0
}
-3
View File
@@ -38,9 +38,6 @@ case "$system" in
x86_64|amd64|x64)
machine="x64"
;;
*)
echo "Unsupported machine type: $machine"
exit 2
esac
echo "Assembling Linux installer for $machine and version $vmajor.$vminor.$revision"
@@ -36,16 +36,20 @@
#include "IpcConnection.hpp"
#ifndef __WINDOWS__
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/select.h>
#endif
namespace ZeroTier {
IpcConnection::IpcConnection(const char *endpoint,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
IpcConnection::IpcConnection(const char *endpoint,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
_handler(commandHandler),
_arg(arg),
_timeout(timeout),
#ifdef __WINDOWS__
_sock(INVALID_HANDLE_VALUE),
_incoming(false),
@@ -81,12 +85,13 @@ IpcConnection::IpcConnection(const char *endpoint,void (*commandHandler)(void *,
}
#ifdef __WINDOWS__
IpcConnection::IpcConnection(HANDLE s,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
IpcConnection::IpcConnection(HANDLE s,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
#else
IpcConnection::IpcConnection(int s,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
IpcConnection::IpcConnection(int s,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
#endif
_handler(commandHandler),
_arg(arg),
_timeout(timeout),
_sock(s),
#ifdef __WINDOWS__
_incoming(true),
@@ -104,18 +109,23 @@ IpcConnection::~IpcConnection()
_writeLock.unlock();
#ifdef __WINDOWS__
while (_running) {
Thread::cancelIO(_thread);
Thread::cancelIO(_thread); // cause Windows to break from blocking read and detect shutdown
Sleep(100);
}
#else
#else // !__WINDOWS__
int s = _sock;
_sock = 0;
if (s > 0) {
::shutdown(s,SHUT_RDWR);
::close(s);
}
#endif
Thread::join(_thread);
#endif // __WINDOWS__ / !__WINDOWS__
}
void IpcConnection::printf(const char *format,...)
@@ -134,7 +144,7 @@ void IpcConnection::printf(const char *format,...)
#ifdef __WINDOWS__
_writeBuf.append(tmp,n);
Thread::cancelIO(_thread);
Thread::cancelIO(_thread); // cause Windows to break from blocking read and service write buffer
#else
if (_sock > 0)
::write(_sock,tmp,n);
@@ -144,20 +154,39 @@ void IpcConnection::printf(const char *format,...)
void IpcConnection::threadMain()
throw()
{
char tmp[65536];
char linebuf[65536];
char tmp[16384];
char linebuf[16384];
unsigned int lineptr = 0;
char c;
#ifdef __WINDOWS__
DWORD n,i;
std::string wbuf;
#else
#else // !__WINDOWS__
int s,n,i;
#endif
fd_set readfds,writefds,errorfds;
struct timeval tout;
#ifdef SO_NOSIGPIPE
if (_sock > 0) {
i = 1;
::setsockopt(_sock,SOL_SOCKET,SO_NOSIGPIPE,(char *)&i,sizeof(i));
}
#endif // SO_NOSIGPIPE
#endif // __WINDOWS__ / !__WINDOWS__
while (_run) {
#ifdef __WINDOWS__
/* Note that we do not use fucking timeouts in Windows, since it does seem
* to properly detect named pipe endpoint close. But we do use a write buffer
* because Windows won't let you divorce reading and writing threads without
* all that OVERLAPPED cruft. */
{
Mutex::Lock _l(_writeLock);
if (!_run)
@@ -187,16 +216,42 @@ void IpcConnection::threadMain()
}
if (!_run)
break;
#else
#else // !__WINDOWS__
/* So today I learned that there is no reliable way to detect a half-closed
* Unix domain socket. So to make sure we don't leave orphaned sockets around
* we just use fucking timeouts. If a socket fucking times out, we break from
* the I/O loop and terminate the thread. But this IpcConnection code is ugly
* so maybe the OS is simply offended by it and refuses to reveal its mysteries
* to me. Oh well... this IPC code will probably get canned when we go to
* local HTTP RESTful interfaces or soemthing like that. */
if ((s = _sock) <= 0)
break;
n = (int)::read(s,tmp,sizeof(tmp));
if ((n <= 0)||(_sock <= 0))
break;
#endif
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&errorfds);
FD_SET(s,&readfds);
FD_SET(s,&errorfds);
tout.tv_sec = _timeout; // use a fucking timeout
tout.tv_usec = 0;
if (select(s+1,&readfds,&writefds,&errorfds,&tout) <= 0) {
break; // socket has fucking timed out
} else {
if (FD_ISSET(s,&errorfds))
break; // socket has an exception... sometimes works
else {
n = (int)::read(s,tmp,sizeof(tmp));
if ((n <= 0)||(_sock <= 0))
break; // read returned error... sometimes works
}
}
#endif // __WINDOWS__ / !__WINDOWS__
for(i=0;i<n;++i) {
c = (linebuf[lineptr] = tmp[i]);
if ((c == '\r')||(c == '\n')||(lineptr == (sizeof(linebuf) - 1))) {
if ((c == '\r')||(c == '\n')||(c == (char)0)||(lineptr == (sizeof(linebuf) - 1))) {
if (lineptr) {
linebuf[lineptr] = (char)0;
_handler(_arg,this,IPC_EVENT_COMMAND,linebuf);
@@ -211,11 +266,13 @@ void IpcConnection::threadMain()
_writeLock.unlock();
#ifdef __WINDOWS__
if (_incoming)
DisconnectNamedPipe(_sock);
CloseHandle(_sock);
_running = false;
#endif
#endif // __WINDOWS__
if (r)
_handler(_arg,this,IPC_EVENT_CONNECTION_CLOSED,(const char *)0);
@@ -28,10 +28,10 @@
#ifndef ZT_IPCCONNECTION_HPP
#define ZT_IPCCONNECTION_HPP
#include "Constants.hpp"
#include "Thread.hpp"
#include "NonCopyable.hpp"
#include "Mutex.hpp"
#include "../node/Constants.hpp"
#include "../node/Thread.hpp"
#include "../node/NonCopyable.hpp"
#include "../node/Mutex.hpp"
#ifdef __WINDOWS__
#include <WinSock2.h>
@@ -61,11 +61,12 @@ public:
* Connect to an IPC endpoint
*
* @param endpoint Endpoint path
* @param timeout Inactivity timeout in seconds
* @param commandHandler Command handler function
* @param arg First argument to command handler
* @throws std::runtime_error Unable to connect
*/
IpcConnection(const char *endpoint,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
IpcConnection(const char *endpoint,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
~IpcConnection();
/**
@@ -80,13 +81,14 @@ public:
private:
// Used by IpcListener to construct incoming connections
#ifdef __WINDOWS__
IpcConnection(HANDLE s,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
IpcConnection(HANDLE s,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
#else
IpcConnection(int s,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
IpcConnection(int s,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
#endif
void (*_handler)(void *,IpcConnection *,IpcConnection::EventType,const char *);
void *_arg;
unsigned int _timeout;
#ifdef __WINDOWS__
HANDLE _sock;
std::string _writeBuf;
@@ -42,10 +42,11 @@
namespace ZeroTier {
IpcListener::IpcListener(const char *ep,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
IpcListener::IpcListener(const char *ep,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
_endpoint(ep),
_handler(commandHandler),
_arg(arg),
_timeout(timeout),
#ifdef __WINDOWS__
_run(true),
_running(true)
@@ -130,7 +131,7 @@ void IpcListener::threadMain()
break;
}
try {
_handler(_arg,new IpcConnection(s,_handler,_arg),IpcConnection::IPC_EVENT_NEW_CONNECTION,(const char *)0);
_handler(_arg,new IpcConnection(s,_timeout,_handler,_arg),IpcConnection::IPC_EVENT_NEW_CONNECTION,(const char *)0);
} catch ( ... ) {} // handlers should not throw
} else {
CloseHandle(s);
@@ -155,7 +156,7 @@ void IpcListener::threadMain()
break;
}
try {
_handler(_arg,new IpcConnection(s,_handler,_arg),IpcConnection::IPC_EVENT_NEW_CONNECTION,(const char *)0);
_handler(_arg,new IpcConnection(s,_timeout,_handler,_arg),IpcConnection::IPC_EVENT_NEW_CONNECTION,(const char *)0);
} catch ( ... ) {} // handlers should not throw
}
#endif
@@ -28,22 +28,14 @@
#ifndef ZT_IPCLISTENER_HPP
#define ZT_IPCLISTENER_HPP
#include "Constants.hpp"
#include "Thread.hpp"
#include "NonCopyable.hpp"
#include "../node/Constants.hpp"
#include "../node/Thread.hpp"
#include "../node/NonCopyable.hpp"
#include "IpcConnection.hpp"
#include <string>
#include <stdexcept>
#ifdef __WINDOWS__
#include <WinSock2.h>
#include <Windows.h>
#define ZT_IPC_ENDPOINT_BASE "\\\\.\\pipe\\ZeroTierOne-"
#else
#define ZT_IPC_ENDPOINT_BASE "/tmp/.ZeroTierOne-"
#endif
namespace ZeroTier {
/**
@@ -58,7 +50,7 @@ public:
* The supplied handler is passed on to incoming instances of IpcConnection. When
* a connection is first opened, it is called with IPC_EVENT_NEW_CONNECTION. The
* receiver must take ownership of the connection object. When a connection is
* closed, IPC_EVENT_CONNECTION_CLOSING is generated. At this point (or after) the
* closed, IPC_EVENT_CONNECTION_CLOSED is generated. At this point (or after) the
* receiver must delete the object. IPC_EVENT_COMMAND is generated when lines of
* text are read, and in this cases the last argument is not NULL. No closed event
* is generated in the event of manual delete if the connection is still open.
@@ -67,11 +59,13 @@ public:
* some kind of actor model or something if it gets too unweildy. But for now the
* use cases are simple enough that it's not too bad.
*
* @param IPC endpoint name (OS-specific)
* @param timeout Endpoint inactivity timeout in seconds
* @param commandHandler Function to call for each command
* @param arg First argument to pass to handler
* @throws std::runtime_error Unable to bind to endpoint
*/
IpcListener(const char *ep,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
IpcListener(const char *ep,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
~IpcListener();
@@ -82,6 +76,7 @@ private:
std::string _endpoint;
void (*_handler)(void *,IpcConnection *,IpcConnection::EventType,const char *);
void *_arg;
unsigned int _timeout;
#ifdef __WINDOWS__
volatile bool _run;
volatile bool _running;
+154
View File
@@ -0,0 +1,154 @@
/*
* 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/
*/
#include "NodeControlClient.hpp"
#include "../node/Constants.hpp"
#include "../node/Utils.hpp"
#include "../node/Defaults.hpp"
#include "IpcConnection.hpp"
#include "IpcListener.hpp"
#include "NodeControlService.hpp"
namespace ZeroTier {
struct _NodeControlClientImpl
{
void (*resultHandler)(void *,const char *);
void *arg;
IpcConnection *ipcc;
std::string err;
};
static void _CBipcResultHandler(void *arg,IpcConnection *ipcc,IpcConnection::EventType event,const char *result)
{
if ((event == IpcConnection::IPC_EVENT_COMMAND)&&(result)) {
if (strcmp(result,"200 auth OK"))
((_NodeControlClientImpl *)arg)->resultHandler(((_NodeControlClientImpl *)arg)->arg,result);
}
}
NodeControlClient::NodeControlClient(const char *ep,const char *authToken,void (*resultHandler)(void *,const char *),void *arg)
throw() :
_impl((void *)new _NodeControlClientImpl)
{
_NodeControlClientImpl *impl = (_NodeControlClientImpl *)_impl;
impl->resultHandler = resultHandler;
impl->arg = arg;
try {
impl->ipcc = new IpcConnection(ep,ZT_IPC_TIMEOUT,&_CBipcResultHandler,_impl);
impl->ipcc->printf("auth %s"ZT_EOL_S,authToken);
} catch ( ... ) {
impl->ipcc = (IpcConnection *)0;
impl->err = "failure connecting to running ZeroTier One service";
}
}
NodeControlClient::~NodeControlClient()
{
if (_impl) {
delete ((_NodeControlClientImpl *)_impl)->ipcc;
delete (_NodeControlClientImpl *)_impl;
}
}
const char *NodeControlClient::error() const
throw()
{
if (((_NodeControlClientImpl *)_impl)->err.length())
return ((_NodeControlClientImpl *)_impl)->err.c_str();
return (const char *)0;
}
void NodeControlClient::send(const char *command)
throw()
{
try {
if (((_NodeControlClientImpl *)_impl)->ipcc)
((_NodeControlClientImpl *)_impl)->ipcc->printf("%s"ZT_EOL_S,command);
} catch ( ... ) {}
}
std::vector<std::string> NodeControlClient::splitLine(const char *line)
{
return Utils::split(line," ","\\","\"");
}
const char *NodeControlClient::authTokenDefaultUserPath()
{
static std::string dlp;
static Mutex dlp_m;
Mutex::Lock _l(dlp_m);
#ifdef __WINDOWS__
if (!dlp.length()) {
char buf[16384];
if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_APPDATA,NULL,0,buf)))
dlp = (std::string(buf) + "\\ZeroTier\\One\\authtoken.secret");
}
#else // not __WINDOWS__
if (!dlp.length()) {
const char *home = getenv("HOME");
if (home) {
#ifdef __APPLE__
dlp = (std::string(home) + "/Library/Application Support/ZeroTier/One/authtoken.secret");
#else
dlp = (std::string(home) + "/.zeroTierOneAuthToken");
#endif
}
}
#endif // __WINDOWS__ or not __WINDOWS__
return dlp.c_str();
}
std::string NodeControlClient::getAuthToken(const char *path,bool generateIfNotFound)
{
unsigned char randbuf[24];
std::string token;
if (Utils::readFile(path,token))
return Utils::trim(token);
else token = "";
if (generateIfNotFound) {
Utils::getSecureRandom(randbuf,sizeof(randbuf));
for(unsigned int i=0;i<sizeof(randbuf);++i)
token.push_back(("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")[(unsigned int)randbuf[i] % 62]);
if (!Utils::writeFile(path,token))
return std::string();
Utils::lockDownFile(path,false);
}
return token;
}
} // namespace ZeroTier
+116
View File
@@ -0,0 +1,116 @@
/*
* 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/
*/
#ifndef ZT_NODECONTROLCLIENT_HPP
#define ZT_NODECONTROLCLIENT_HPP
#include <string>
#include <vector>
#ifdef __WINDOWS__
#define ZT_IPC_ENDPOINT_BASE "\\\\.\\pipe\\ZeroTierOne-"
#else
#define ZT_IPC_ENDPOINT_BASE "/tmp/.ZeroTierOne-"
#endif
namespace ZeroTier {
/**
* Client for controlling a local ZeroTier One node
*/
class NodeControlClient
{
public:
/**
* Create a new node config client
*
* Initialization may fail. Call error() to check.
*
* @param ep Endpoint to connect to (OS-dependent)
* @param resultHandler Function to call when commands provide results
* @param arg First argument to result handler
*/
NodeControlClient(const char *ep,const char *authToken,void (*resultHandler)(void *,const char *),void *arg)
throw();
~NodeControlClient();
/**
* @return Initialization error or NULL if none
*/
const char *error() const
throw();
/**
* Send a command to the local node
*
* Note that the returned conversation ID will never be 0. A return value
* of 0 indicates a fatal error such as failure to bind to any local UDP
* port.
*
* @param command
* @return Conversation ID that will be provided to result handler when/if results are sent back
*/
void send(const char *command)
throw();
inline void send(const std::string &command)
throw() { return send(command.c_str()); }
/**
* Split a line of results
*
* @param line Line to split
* @return Vector of fields
*/
static std::vector<std::string> splitLine(const char *line);
static inline std::vector<std::string> splitLine(const std::string &line) { return splitLine(line.c_str()); }
/**
* @return Default path for current user's authtoken.secret or ~/.zeroTierOneAuthToken (location is platform-dependent)
*/
static const char *authTokenDefaultUserPath();
/**
* Load (or generate) the authentication token
*
* @param path Full path to authtoken.secret
* @param generateIfNotFound If true, generate and save if not found or readable (requires appropriate privileges, returns empty on failure)
* @return Authentication token or empty string on failure
*/
static std::string getAuthToken(const char *path,bool generateIfNotFound);
private:
// NodeControlClient is not copyable
NodeControlClient(const NodeControlClient&);
const NodeControlClient& operator=(const NodeControlClient&);
void *_impl;
};
} // namespace ZeroTier
#endif
+238
View File
@@ -0,0 +1,238 @@
/*
* 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/
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../node/Constants.hpp"
#include "NodeControlService.hpp"
#include "NodeControlClient.hpp"
#include "../node/Node.hpp"
#include "../node/Utils.hpp"
namespace ZeroTier {
NodeControlService::NodeControlService(Node *node,const char *authToken) :
_node(node),
_listener((IpcListener *)0),
_authToken(authToken),
_running(true),
_thread(Thread::start(this))
{
}
NodeControlService::~NodeControlService()
{
_running = false;
Thread::join(_thread);
{
Mutex::Lock _l(_connections_m);
for(std::map< IpcConnection *,bool >::iterator c(_connections.begin());c!=_connections.end();++c)
delete c->first;
_connections.clear();
}
delete _listener;
}
void NodeControlService::threadMain()
throw()
{
char tmp[1024];
try {
while (_running) {
if (!_node->running()) {
if (_node->started())
break;
} else if ((_node->initialized())&&(_node->address())) {
Utils::snprintf(tmp,sizeof(tmp),"%s%.10llx",ZT_IPC_ENDPOINT_BASE,(unsigned long long)_node->address());
_listener = new IpcListener(tmp,ZT_IPC_TIMEOUT,&_CBcommandHandler,this);
break;
}
Thread::sleep(100); // wait for Node to start
}
} catch ( ... ) {
delete _listener;
_listener = (IpcListener *)0;
}
}
void NodeControlService::_CBcommandHandler(void *arg,IpcConnection *ipcc,IpcConnection::EventType event,const char *commandLine)
{
switch(event) {
case IpcConnection::IPC_EVENT_COMMAND: {
if ((!((NodeControlService *)arg)->_running)||(!commandLine)||(!commandLine[0]))
return;
((NodeControlService *)arg)->_doCommand(ipcc,commandLine);
} break;
case IpcConnection::IPC_EVENT_NEW_CONNECTION: {
Mutex::Lock _l(((NodeControlService *)arg)->_connections_m);
((NodeControlService *)arg)->_connections[ipcc] = false; // not yet authenticated
} break;
case IpcConnection::IPC_EVENT_CONNECTION_CLOSED: {
Mutex::Lock _l(((NodeControlService *)arg)->_connections_m);
((NodeControlService *)arg)->_connections.erase(ipcc);
delete ipcc;
} break;
}
}
void NodeControlService::_doCommand(IpcConnection *ipcc,const char *commandLine)
{
std::vector<std::string> r;
std::vector<std::string> cmd(Utils::split(commandLine,"\r\n \t","\\","'"));
if ((cmd.empty())||(cmd[0] == "help")) {
ipcc->printf("200 help help"ZT_EOL_S);
ipcc->printf("200 help auth <token>"ZT_EOL_S);
ipcc->printf("200 help info"ZT_EOL_S);
ipcc->printf("200 help listpeers"ZT_EOL_S);
ipcc->printf("200 help listnetworks"ZT_EOL_S);
ipcc->printf("200 help join <network ID>"ZT_EOL_S);
ipcc->printf("200 help leave <network ID>"ZT_EOL_S);
ipcc->printf("200 help terminate [<reason>]"ZT_EOL_S);
ipcc->printf("200 help updatecheck"ZT_EOL_S);
} else if (cmd[0] == "auth") {
if ((cmd.size() > 1)&&(_authToken.length() > 0)&&(_authToken == cmd[1])) {
Mutex::Lock _l(_connections_m);
_connections[ipcc] = true;
ipcc->printf("200 auth OK"ZT_EOL_S);
} else ipcc->printf("403 auth failed"ZT_EOL_S);
} else {
{
Mutex::Lock _l(_connections_m);
if (!_connections[ipcc]) {
ipcc->printf("403 %s unauthorized"ZT_EOL_S"."ZT_EOL_S,cmd[0].c_str());
return;
}
}
if (cmd[0] == "info") {
ipcc->printf("200 info %.10llx %s %s"ZT_EOL_S,_node->address(),(_node->online() ? "ONLINE" : "OFFLINE"),Node::versionString());
} else if (cmd[0] == "listpeers") {
ipcc->printf("200 listpeers <ztaddr> <paths> <latency> <version>"ZT_EOL_S);
ZT1_Node_PeerList *pl = _node->listPeers();
if (pl) {
for(unsigned int i=0;i<pl->numPeers;++i) {
ipcc->printf("200 listpeers %.10llx ",(unsigned long long)pl->peers[i].rawAddress);
if (pl->peers[i].numPaths == 0)
ipcc->printf("-");
else {
for(unsigned int j=0;j<pl->peers[i].numPaths;++j) {
if (j > 0)
ipcc->printf(",");
switch(pl->peers[i].paths[j].type) {
default:
ipcc->printf("unknown;");
break;
case ZT1_Node_PhysicalPath::ZT1_Node_PhysicalPath_TYPE_UDP:
ipcc->printf("udp;");
break;
case ZT1_Node_PhysicalPath::ZT1_Node_PhysicalPath_TYPE_TCP_OUT:
ipcc->printf("tcp_out;");
break;
case ZT1_Node_PhysicalPath::ZT1_Node_PhysicalPath_TYPE_TCP_IN:
ipcc->printf("tcp_in;");
break;
case ZT1_Node_PhysicalPath::ZT1_Node_PhysicalPath_TYPE_ETHERNET:
ipcc->printf("eth;");
break;
}
ipcc->printf("%s/%d;%ld;%ld;%ld;%s",
pl->peers[i].paths[j].address.ascii,
(int)pl->peers[i].paths[j].address.port,
pl->peers[i].paths[j].lastSend,
pl->peers[i].paths[j].lastReceive,
pl->peers[i].paths[j].lastPing,
(pl->peers[i].paths[j].fixed ? "fixed" : (pl->peers[i].paths[j].active ? "active" : "inactive")));
}
}
ipcc->printf(" %u %s"ZT_EOL_S,pl->peers[i].latency,(pl->peers[i].remoteVersion[0]) ? pl->peers[i].remoteVersion : "-");
}
_node->freeQueryResult(pl);
}
} else if (cmd[0] == "listnetworks") {
ipcc->printf("200 listnetworks <nwid> <name> <mac> <status> <config age> <type> <dev> <ips>"ZT_EOL_S);
ZT1_Node_NetworkList *nl = _node->listNetworks();
if (nl) {
for(unsigned int i=0;i<nl->numNetworks;++i) {
ipcc->printf("200 listnetworks %s %s %s %s %ld %s %s ",
nl->networks[i].nwidHex,
nl->networks[i].name,
nl->networks[i].macStr,
nl->networks[i].statusStr,
nl->networks[i].configAge,
(nl->networks[i].isPrivate ? "private" : "public"),
nl->networks[i].device);
if (nl->networks[i].numIps > 0) {
for(unsigned int j=0;j<nl->networks[i].numIps;++j) {
if (j > 0)
ipcc->printf(",");
ipcc->printf("%s/%d",nl->networks[i].ips[j].ascii,(int)nl->networks[i].ips[j].port);
}
} else ipcc->printf("-");
ipcc->printf(ZT_EOL_S);
}
_node->freeQueryResult(nl);
}
} else if (cmd[0] == "join") {
if (cmd.size() > 1) {
uint64_t nwid = Utils::hexStrToU64(cmd[1].c_str());
_node->join(nwid);
ipcc->printf("200 join %.16llx OK"ZT_EOL_S,(unsigned long long)nwid);
} else {
ipcc->printf("400 join requires a network ID (>0) in hexadecimal format"ZT_EOL_S);
}
} else if (cmd[0] == "leave") {
if (cmd.size() > 1) {
uint64_t nwid = Utils::hexStrToU64(cmd[1].c_str());
_node->leave(nwid);
ipcc->printf("200 leave %.16llx OK"ZT_EOL_S,(unsigned long long)nwid);
} else {
ipcc->printf("400 leave requires a network ID (>0) in hexadecimal format"ZT_EOL_S);
}
} else if (cmd[0] == "terminate") {
if (cmd.size() > 1)
_node->terminate(Node::NODE_NORMAL_TERMINATION,cmd[1].c_str());
else _node->terminate(Node::NODE_NORMAL_TERMINATION,"terminate via IPC command");
} else if (cmd[0] == "updatecheck") {
if (_node->updateCheck()) {
ipcc->printf("500 software updates are not enabled"ZT_EOL_S);
} else {
ipcc->printf("200 OK"ZT_EOL_S);
}
} else {
ipcc->printf("404 %s No such command. Use 'help' for help."ZT_EOL_S,cmd[0].c_str());
}
}
ipcc->printf("."ZT_EOL_S); // blank line ends response
}
} // namespace ZeroTier
+84
View File
@@ -0,0 +1,84 @@
/*
* 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/
*/
#ifndef ZT_NODECONTROLSERVICE_HPP
#define ZT_NODECONTROLSERVICE_HPP
#include <string>
#include <map>
#include "IpcConnection.hpp"
#include "IpcListener.hpp"
#include "../node/Constants.hpp"
#include "../node/NonCopyable.hpp"
#include "../node/Thread.hpp"
namespace ZeroTier {
class Node;
/**
* Background controller service that controls and configures a node
*
* This is used with system-installed instances of ZeroTier One to
* provide the IPC-based control bus service for node configuration.
*/
class NodeControlService : NonCopyable
{
public:
/**
* @param node Node to control and configure
* @param authToken Authorization token for clients
*/
NodeControlService(Node *node,const char *authToken);
~NodeControlService();
// Background thread waits for node to initialize, then creates IpcListener and
// terminates. It also terminates on delete if it hasn't bootstrapped yet.
void threadMain()
throw();
private:
static void _CBcommandHandler(void *arg,IpcConnection *ipcc,IpcConnection::EventType event,const char *commandLine);
void _doCommand(IpcConnection *ipcc,const char *commandLine);
Node *_node;
IpcListener *_listener;
std::string _authToken;
std::map< IpcConnection *,bool > _connections;
Mutex _connections_m;
volatile bool _running;
Thread _thread;
};
} // namespace ZeroTier
#endif
+1
View File
@@ -0,0 +1 @@
This code is responsible for the local command bus used to control the ZeroTier One service on a local machine via zerotier-cli or the Qt GUI. It's not part of the core node implementation. It uses Unix domain sockets on unix-like OSes and named pipes on Windows. Authentication is via a simple token mechanism. (Eventually this part of the software is getting a rework.)
+1
View File
@@ -0,0 +1 @@
The ext/ folder contains third party code, drivers, installation support files, etc.
-1
View File
@@ -1 +0,0 @@
ext/ contains external code, such as libraries, and scripts for building such.
+1
View File
@@ -30,6 +30,7 @@ fi
echo "Erasing service and support files..."
rm -f /usr/bin/zerotier-cli
rm -f /usr/bin/zerotier-idtool
cd "$zthome"
rm -f zerotier-one *.persist identity.public *.log *.pid *.sh shutdownIfUnreadable
rm -rf pre10.8 tap.kext updates.d networks.d
+329
View File
@@ -0,0 +1,329 @@
/*
* 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/
*/
/*
* This defines the external C API for ZeroTier One, the core network
* virtualization engine.
*/
#ifndef ZT_ZEROTIERONE_H
#define ZT_ZEROTIERONE_H
#include <stdint.h>
/* ------------------------------------------------------------------------ */
/* Query result buffers */
/* ------------------------------------------------------------------------ */
/**
* Node status result buffer
*/
struct ZT1_Node_Status
{
/**
* Public identity in string form
*/
char publicIdentity[256];
/**
* ZeroTier address in 10-digit hex form
*/
char address[16];
/**
* ZeroTier address in least significant 40 bits of 64-bit integer
*/
uint64_t rawAddress;
/**
* Number of known peers (including supernodes)
*/
unsigned int knownPeers;
/**
* Number of upstream supernodes
*/
unsigned int supernodes;
/**
* Number of peers with active direct links
*/
unsigned int directlyConnectedPeers;
/**
* Number of peers that have recently communicated with us
*/
unsigned int alivePeers;
/**
* Success rate at establishing direct links (0.0 to 1.0, approximate)
*/
float directLinkSuccessRate;
/**
* True if connectivity appears good
*/
bool online;
/**
* True if running; all other fields are technically undefined if this is false
*/
bool running;
};
/**
* Physical address result buffer
*/
struct ZT1_Node_PhysicalAddress
{
/**
* Physical address type
*/
enum {
ZT1_Node_PhysicalAddress_TYPE_NULL = 0, /* none/invalid */
ZT1_Node_PhysicalAddress_TYPE_IPV4 = 1, /* 32-bit IPv4 address (and port) */
ZT1_Node_PhysicalAddress_TYPE_IPV6 = 2, /* 128-bit IPv6 address (and port) */
ZT1_Node_PhysicalAddress_TYPE_ETHERNET = 3 /* 48-bit Ethernet MAC address */
} type;
/**
* Address in raw binary form -- length depends on type
*/
unsigned char bits[16];
/**
* Port or netmask bits (for IPV4 and IPV6)
*/
unsigned int port;
/**
* Address in canonical human-readable form
*/
char ascii[64];
/**
* Zone index identifier (thing after % on IPv6 link-local addresses only)
*/
char zoneIndex[16];
};
/**
* Network path result buffer
*/
struct ZT1_Node_PhysicalPath
{
/**
* Physical path type
*/
enum { /* These must be numerically the same as type in Path.hpp */
ZT1_Node_PhysicalPath_TYPE_NULL = 0, /* none/invalid */
ZT1_Node_PhysicalPath_TYPE_UDP = 1, /* UDP association */
ZT1_Node_PhysicalPath_TYPE_TCP_OUT = 2, /* outgoing TCP tunnel using pseudo-SSL */
ZT1_Node_PhysicalPath_TYPE_TCP_IN = 3, /* incoming TCP tunnel using pseudo-SSL */
ZT1_Node_PhysicalPath_TYPE_ETHERNET = 4 /* raw ethernet frames over trusted backplane */
} type;
/**
* Physical address of endpoint
*/
struct ZT1_Node_PhysicalAddress address;
/**
* Time since last send in milliseconds or -1 for never
*/
long lastSend;
/**
* Time since last receive in milliseconds or -1 for never
*/
long lastReceive;
/**
* Time since last ping in milliseconds or -1 for never
*/
long lastPing;
/**
* Is path active/connected? Non-fixed active paths may be garbage collected over time.
*/
bool active;
/**
* Is path fixed? (i.e. not learned, static)
*/
bool fixed;
};
/**
* Peer status result buffer
*/
struct ZT1_Node_Peer
{
/**
* Remote peer version: major.minor.revision (or empty if unknown)
*/
char remoteVersion[16];
/**
* ZeroTier address of peer as 10-digit hex string
*/
char address[16];
/**
* ZeroTier address in least significant 40 bits of 64-bit integer
*/
uint64_t rawAddress;
/**
* Last measured latency in milliseconds or zero if unknown
*/
unsigned int latency;
/**
* Array of network paths to peer
*/
struct ZT1_Node_PhysicalPath *paths;
/**
* Number of paths (size of paths[])
*/
unsigned int numPaths;
};
/**
* List of peers
*/
struct ZT1_Node_PeerList
{
struct ZT1_Node_Peer *peers;
unsigned int numPeers;
};
/**
* Network status result buffer
*/
struct ZT1_Node_Network
{
/**
* 64-bit network ID
*/
uint64_t nwid;
/**
* 64-bit network ID in hex form
*/
char nwidHex[32];
/**
* Short network name
*/
char name[256];
/**
* Longer network description
*/
char description[4096];
/**
* Device name (system-dependent)
*/
char device[256];
/**
* Status code in string format
*/
char statusStr[64];
/**
* Ethernet MAC address of this endpoint in string form
*/
char macStr[32];
/**
* Ethernet MAC address of this endpoint on the network in raw binary form
*/
unsigned char mac[6];
/**
* Age of configuration in milliseconds or -1 if never refreshed
*/
long configAge;
/**
* Assigned layer-3 IPv4 and IPv6 addresses
*
* Note that PhysicalAddress also supports other address types, but this
* list will only list IP address assignments. The port field will contain
* the number of bits in the netmask -- e.g. 192.168.1.1/24.
*/
struct ZT1_Node_PhysicalAddress *ips;
/**
* Number of layer-3 IPs (size of ips[])
*/
unsigned int numIps;
/**
* Network status code
*/
enum { /* Must be same as Status in Network.hpp */
ZT1_Node_Network_INITIALIZING = 0,
ZT1_Node_Network_WAITING_FOR_FIRST_AUTOCONF = 1,
ZT1_Node_Network_OK = 2,
ZT1_Node_Network_ACCESS_DENIED = 3,
ZT1_Node_Network_NOT_FOUND = 4,
ZT1_Node_Network_INITIALIZATION_FAILED = 5,
ZT1_Node_Network_NO_MORE_DEVICES = 6
} status;
/**
* True if traffic on network is enabled
*/
bool enabled;
/**
* Is this a private network? If false, network lacks access control.
*/
bool isPrivate;
};
/**
* Return buffer for list of networks
*/
struct ZT1_Node_NetworkList
{
struct ZT1_Node_Network *networks;
unsigned int numNetworks;
};
/* ------------------------------------------------------------------------ */
/* ZeroTier One C API */
/* ------------------------------------------------------------------------ */
/* coming soon... */
#endif
+45 -11
View File
@@ -72,6 +72,9 @@
#include "node/EthernetTapFactory.hpp"
#include "node/RoutingTable.hpp"
#include "control/NodeControlClient.hpp"
#include "control/NodeControlService.hpp"
#ifdef __WINDOWS__
#include "osnet/WindowsEthernetTapFactory.hpp"
#include "osnet/WindowsRoutingTable.hpp"
@@ -111,9 +114,10 @@ static void printHelp(FILE *out,const char *cn)
static void _CBresultHandler(void *arg,const char *line)
{
if (line) {
if ((line[0] == '.')&&(line[1] == (char)0))
if ((line[0] == '.')&&(line[1] == (char)0)) {
fflush(stdout);
*((bool *)arg) = true;
else fprintf(stdout,"%s"ZT_EOL_S,line);
} else fprintf(stdout,"%s"ZT_EOL_S,line);
}
}
@@ -150,19 +154,43 @@ static int main(const char *homeDir,int argc,char **argv)
return 1;
}
if (!homeDir)
homeDir = ZT_DEFAULTS.defaultHomePath.c_str();
try {
std::string buf;
if (!Utils::readFile((std::string(homeDir) + ZT_PATH_SEPARATOR_S + "identity.public").c_str(),buf)) {
fprintf(stderr,"%s: fatal error: unable to read node address from identity.public in home path"ZT_EOL_S,argv[0]);
return 1;
}
Identity id;
if (!id.fromString(buf)) {
fprintf(stderr,"%s: fatal error: unable to read node address from identity.public in home path"ZT_EOL_S,argv[0]);
return 1;
}
std::string authToken(NodeControlClient::getAuthToken((std::string(homeDir) + ZT_PATH_SEPARATOR_S + "authtoken.secret").c_str(),false));
if (!authToken.length())
authToken = NodeControlClient::getAuthToken(NodeControlClient::authTokenDefaultUserPath(),false);
if (!authToken.length()) {
fprintf(stderr,"%s: fatal error: unable to read authentication token from home path or user home"ZT_EOL_S,argv[0]);
return 1;
}
volatile bool done = false;
Node::NodeControlClient client(homeDir,&_CBresultHandler,(void *)&done);
NodeControlClient client((std::string(ZT_IPC_ENDPOINT_BASE) + id.address().toString()).c_str(),authToken.c_str(),&_CBresultHandler,(void *)&done);
const char *err = client.error();
if (err) {
fprintf(stderr,"%s: fatal error: unable to connect (is ZeroTier One running?) (%s)"ZT_EOL_S,argv[0],err);
return 1;
}
client.send(query.c_str());
while (!done)
Thread::sleep(100); // ghetto
while (!done) Thread::sleep(100); // dis be ghetto
} catch (std::exception &exc) {
fprintf(stderr,"%s: fatal error: unable to connect (is ZeroTier One running?) (%s)"ZT_EOL_S,argv[0],exc.what());
return 1;
} catch ( ... ) {
fprintf(stderr,"%s: fatal error: unable to connect (is ZeroTier One running?)"ZT_EOL_S,argv[0]);
fprintf(stderr,"%s: fatal error: unable to connect (is ZeroTier One running?) (unknown exception)"ZT_EOL_S,argv[0]);
return 1;
}
@@ -755,12 +783,19 @@ int main(int argc,char **argv)
bool needsReset = false;
EthernetTapFactory *tapFactory = (EthernetTapFactory *)0;
RoutingTable *routingTable = (RoutingTable *)0;
NodeControlService *controlService = (NodeControlService *)0;
try {
// Get or create authtoken.secret -- note that if this fails, authentication
// will always fail since an empty auth token won't work. This should always
// succeed unless something is wrong with the filesystem.
std::string authToken(NodeControlClient::getAuthToken((std::string(homeDir) + ZT_PATH_SEPARATOR_S + "authtoken.secret").c_str(),true));
tapFactory = ZTCreatePlatformEthernetTapFactory;
routingTable = ZTCreatePlatformRoutingTable;
node = new Node(homeDir,tapFactory,routingTable,udpPort,tcpPort,needsReset);
controlService = new NodeControlService(node,authToken.c_str());
switch(node->run()) {
#ifdef __WINDOWS__
@@ -778,7 +813,7 @@ int main(int argc,char **argv)
} break;
#else // __UNIX_LIKE__
case Node::NODE_RESTART_FOR_UPGRADE: {
const char *upgPath = node->reasonForTermination();
const char *upgPath = node->terminationMessage();
// On Unix-type OSes we exec() right into the upgrade. This in turn will
// end with us being re-launched either via the upgrade itself or something
// like OSX's launchd.
@@ -797,16 +832,13 @@ int main(int argc,char **argv)
case Node::NODE_UNRECOVERABLE_ERROR: {
exitCode = 3;
const char *termReason = node->reasonForTermination();
const char *termReason = node->terminationMessage();
fprintf(stderr,"%s: abnormal termination: %s\n",argv[0],(termReason) ? termReason : "(unknown reason)");
} break;
default:
break;
}
delete node;
node = (Node *)0;
} catch ( std::exception &exc ) {
fprintf(stderr,"%s: unexpected exception: %s"ZT_EOL_S,argv[0],exc.what());
exitCode = 3;
@@ -815,6 +847,8 @@ int main(int argc,char **argv)
exitCode = 3;
}
delete controlService;
delete node; node = (Node *)0;
delete routingTable;
delete tapFactory;
+20 -2
View File
@@ -6,8 +6,26 @@ INCLUDES=
DEFS=
LIBS=
# Enable SSE-optimized Salsa20 in all modes
DEFS+=-DZT_SALSA20_SSE
# Enable SSE-optimized Salsa20 on x86 and x86_64 machines
MACHINE=$(shell uname -m)
ifeq ($(MACHINE),x86_64)
DEFS+=-DZT_SALSA20_SSE
endif
ifeq ($(MACHINE),amd64)
DEFS+=-DZT_SALSA20_SSE
endif
ifeq ($(MACHINE),i686)
DEFS+=-DZT_SALSA20_SSE
endif
ifeq ($(MACHINE),i586)
DEFS+=-DZT_SALSA20_SSE
endif
ifeq ($(MACHINE),i386)
DEFS+=-DZT_SALSA20_SSE
endif
ifeq ($(MACHINE),x86)
DEFS+=-DZT_SALSA20_SSE
endif
# "make official" is a shortcut for this
ifeq ($(ZT_OFFICIAL_RELEASE),1)
+43 -25
View File
@@ -1,33 +1,51 @@
# ZeroTier One Network Configuration Service Helper
# ZeroTier One Network Configuration Service
This code will at the moment probably not be of much interest to end users.
It's a "service," which is a sub-process that ZeroTier One can execute that
provides additional functionaly not found in the core executable.
## What is it?
ZeroTier One communicates with services by sending and receiving string-serialized
Dictionary objects terminated by an empty line for each Dictionary.
It's the thing that controls virtual networks. It's a completely separate subsystem from supernodes; see the [Technical FAQ](https://github.com/zerotier/ZeroTierOne/wiki/Technical-FAQ) for more information on that.
The netconf service, written in node.js and making use of the zerotier-idtool
binary (see index.js) handles responding to virtual network config requests.
It only runs on netconf masters. It fetches its configuration information from
a Redis database.
ZeroTier's 16-digit / 64-bit network IDs are actually two numbers packed together into one. The most significant 40 bits / first 10 digits of a network ID are the ZeroTier address of the *network configuration master* responsible for issuing network configurations and certificates to members of the network. The least significant 24 bits / last 6 digits are an arbitrary 24-bit number used to identify this network on its given master.
The schama for that database is in redis-schema.txt. Some record types such
as users will not be of interest to outside people. Pay attention to the
network record type.
When a ZeroTier node joins a network or updates its network configuration, it queries the network configuration master and receives a response containing either an error or a dictionary with that node's membership information and (if it's a private network) membership certificate.
To install, copy the netconf-service folder to /var/lib/zerotier-one/services.d
(create services.d first). Then run "npm install" from the netconf-service
folder there and finally copy netconf.service to the parent services.d folder.
Make sure that shell script is executable. This is what the ZeroTier One
service will execute. It in turn sets up the node environment and runs index.js.
Network configuration masters can go offline without affecting communication on the network, since they're only needed when it's necessary to issue new configurations. (Certificates are a more involved topic that's beyond the scope of this document, but suffice to say that the same applies there.) They can also be made fault tolerant by mirroring their identities and databases to a backup server that can take over if the main server dies.
The utility zerotier-idtool must be present as /usr/local/bin/zerotier-idtool. This
is hard coded for the moment. To get it build ZeroTier One from source and then
"sudo cp zerotier-one /usr/local/bin/zerotier-idtool" (the binary determines its
personality from argv[0] on execution).
Networks managed through the *zerotier.com* site are managed by network configuration masters run by ZeroTier networks, but if you're willing to do a bit of manual system administration you can set up and run your own.
Note: Windows builds of ZeroTier One do not support services. This code has only
been tested on Linux but will probably work on Mac too.
## Installation
*Adam Ierymenko @ ZeroTier*
The first step is to choose a node to act as a netconf master. The netconf master subprocess is only supported on Unix-like platforms and has only been tested so far on Linux, but will probably work on Mac as well. But Windows builds don't support service subprocesses so the master cannot be a Windows node.
At the moment, netconf masters *cannot join their own networks*. We recommend using a node that you don't intend to make a member of the networks it administrates. Or, as an alternative, you can run ZeroTier One on a different port and with a different home folder. This is what we do, and on our masters we run it separately on startup with a script that executes "sudo /var/lib/zerotier-one/zerotier-one -p9994 /var/lib/zerotier-one-netconf-master &". This runs the service on UDP port 9994 and uses "/var/lib/zerotier-one-netconf-master" as its home folder. *Two or more instances of ZeroTier One can co-exist on the same machine as long as only one of them tries to actually join networks.*
Before setting up, three prerequisites must be installed: [Node](http://nodejs.org), npm (node's package manager), and Redis (a NoSQL database). On most Linux distributions there are packages for all these, so installing them should be easy. On CentOS/RedHat based distributions they are in the [EPEL](https://fedoraproject.org/wiki/EPEL) repository, so after installing or enabling EPEL you can just type "sudo yum install nodejs npm redis". Then start the Redis service with "sudo service redis start" and enable it on boot with "sudo chkconfig redis on". (Commands may differ on other Linux distributions.) Verify that it's running by typing "redis-cli" and testing whether it connects.
Once prerequisites are installed, follow these instructions:
1) Go to the ZeroTier home (for the node you plan to designate as master) and create a subfolder called "services.d".
2) Copy or symlink "netconf-master" from this repository into "services.d".
3) In the "services.d" folder, create a symlink called "netconf.service" that points to "netconf-master/netconf.service". Also check to make sure that "netconf-master/netconf.service" is executable. This is what the ZeroTier One service will execute to launch the subprocess.
4) In "services.d/netconf-master" type "npm install" to install NodeJS package dependencies.
5) Edit the initdb.js and change the network ID and other settings of the network you want to create. Remember how network IDs are made; the *first 10 digits* of your network's ID *must be the ZeroTier address of your netconf master node*. If it doesn't have one yet, try starting the ZeroTier service briefly and then killing it and it will generate *identity.public* and *identity.secret* automatically. The address is the first hex field in either of these files.
6) Run "node initdb.js" to initialize your Redis database. (If you want to use a Redis database other than 0, such as to avoid polluting another Redis database on the same machine, edit "config.js" and change the Redis database index there before initializing the database or running the service.)
7) Start or restart the ZeroTier One service. Check its "node.log" and your system process list to ensure that it successfully started the netconf server slave process.
## Using Your Own Networks
Once the netconf master service is running, you can test it by simply joining the network ID of the network you created from any ZeroTier One node anywhere in the world. If everything is set up correctly, it should just work. Your netconf service is controlling this network.
## Private Networks and Certificates
If you set the "private" flag to "1" and designated your network as private, members must be authorized. Each time a member attempts to join a network, a member record is created in Redis. These will have Redis names like "zt1:network:################:member:##########:~". Open this hash in any Redis database editor (or use hgetall and hset in redis-cli) and set "authorized" to "1" for whichever members you wish to authorize.
## What About the Web UI?
At the moment, the web-based administration interface used on zerotier.com is the only part of the system that is not open source. Right now it's all tied in with our payment processor, and our model right now is to charge for the convenience of using it.
We *might* open source it in the future, but in the meantime anyone with a bit of developer expertise should be able to write a few scripts to list networks, change network parameters, authorize members on private networks, etc. The Redis database schema is documented in *redis-schema.md*, and only the stuff under "zt1:network:..." is actually used by the netconf master service. The stuff under "zt1:user:..." is used by our web UI and doesn't need to be present for the netconf service to operate.
+73
View File
@@ -0,0 +1,73 @@
/*
* Populates a new Redis database with data, which can be edited below.
*/
var INIT_DATA = {
// Must be present in any database
"zt1": 1,
/* The network ID here must be set to the ZeroTier address of your netconf
* master (the node where netconf-master will be running) plus an arbitrary
* 24-bit network ID. This will create the full 16-digit network ID of the
* network you will join. This must be in the object name and in the "id"
* field within the object itself. */
"zt1:network:d40e9d860d111111:~": {
"id": "d40e9d860d111111", // netconf master ZT address + 24-bit ID
"name": "zerotier-testnet", // short name, no spaces or special chars
"desc": "Test Network", // description
"infrastructure": 0, // unused by netconf-master
"private": 0, // set to '1' to require member approval
"creationTime": 0, // unuxed by netconf-master
"owner": "", // unused by netconf-master
"etherTypes": "0800,0806", // hex ethernet frame types allowed
"enableBroadcast": 1, // set to '1' to enable ff:ff:ff:ff:ff:ff
"v4AssignMode": "zt", // 'zt' to assign, 'none' to let OS do it
"v4AssignPool": "192.168.123.0/24", // IPv4 net block / netmask bits
"v6AssignMode": "none" // 'zt' to assign, 'none' to let OS do it
}
};
var config = require('./config.js');
var async = require('async');
var redis = require('redis');
var DB = redis.createClient();
DB.on("error",function(err) { console.error('redis query error: '+err); });
DB.select(config.redisDb,function() {});
DB.get("zt1",function(err,value) {
if ((value)&&(!err)) {
console.log("Redis database #"+config.redisDb+" appears to already contain data; flush it first!");
return process.exit(0);
}
async.eachSeries(Object.keys(INIT_DATA),function(key,next) {
var value = INIT_DATA[key];
if (typeof value === 'object') {
console.log(key);
async.eachSeries(Object.keys(value),function(hkey,next2) {
var hvalue = value[hkey];
if (hvalue === true)
hvalue = 1;
if (hvalue === false)
hvalue = 0;
if (typeof hvalue !== 'string')
hvalue = hvalue.toString();
console.log('\t'+hkey+': '+hvalue);
DB.hset(key,hkey,hvalue,next2);
},next);
} else if ((typeof value !== 'undefined')&&(value !== null)) {
if (value === true)
value = 1;
if (value === false)
value = 0;
if (typeof value !== 'string')
value = value.toString();
console.log(key+': '+value);
DB.set(key,value,next);
} else return next(null);
},function(err) {
console.log('Done!');
return process.exit(0);
});
});
@@ -527,13 +527,24 @@ function doNetconfRequest(message)
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] = '';
if (network['p5MulticastPrefixBits'])
netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_PREFIX_BITS] = network['p5MulticastPrefixBits'];
if (network['p5MulticastDepth'])
netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_DEPTH] = network['p5MulticastDepth'];
if (network['multicastRates']) {
var ratesD = new Dictionary();
var ratesJ = JSON.parse(network['multicastRates']);
for(var k in ratesJ) {
if ((k)&&(ratesJ[k]))
ratesD.data[k] = ratesJ[k];
}
netconf.data[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES] = ratesD.toString();
}
netconf.data[ZT_NETWORKCONFIG_DICT_KEY_PRIVATE] = privateNetwork ? '1' : '0';
if (network['name'])
netconf.data[ZT_NETWORKCONFIG_DICT_KEY_NAME] = network['name'];
@@ -549,6 +560,7 @@ function doNetconfRequest(message)
netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING] = ztDbTrue(network['allowPassiveBridging']) ? '1' : '0';
if ((activeBridges)&&(activeBridges.length > 0))
netconf.data[ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES] = activeBridges; // comma-delimited list
response.data['netconf'] = netconf.toString();
}
Loaded 20 of 60 files, more files were not shown because too many files have changed in this diff. Show more