Add better port binding controls

This commit is contained in:
Joseph Henry
2021-05-13 14:17:08 -07:00
parent 22f80797b8
commit 5d404034c3
7 changed files with 212 additions and 65 deletions

View File

@@ -1058,9 +1058,7 @@ int zts_py_getblocking(int fd);
// Central API //
//----------------------------------------------------------------------------//
#ifdef ZTS_ENABLE_PYTHON
#define ZTS_DISABLE_CENTRAL_API 1
#endif
#ifndef ZTS_DISABLE_CENTRAL_API
@@ -1332,6 +1330,40 @@ ZTS_API int ZTCALL zts_init_set_roots(const void* roots_data, unsigned int len);
*/
ZTS_API int ZTCALL zts_init_set_port(unsigned short port);
/**
* @brief Set range that random ports will be selected from. This is an initialization function that can
* only be called before `zts_node_start()`.
*
* @param start_port Start of port range
* @param end_port End of port range
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
* experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
ZTS_API int ZTCALL zts_init_set_random_port_range(unsigned short start_port, unsigned short end_port);
/**
* @brief Allow or disallow ZeroTier from automatically selecting a backup port to help get through
* buggy NAT. This is enabled by default. This port is randomly chosen and should be disabled if you
* want to control exactly which ports ZeroTier talks on and (iff) you know with absolute certainty
* that traffic on your chosen primary port is allowed. This is an initialization function that can
* only be called before `zts_node_start()`.
*
* @param port Port number
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
* experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
ZTS_API int ZTCALL zts_init_allow_secondary_port(unsigned int allowed);
/**
* @brief Allow or disallow the use of port-mapping. This is enabled by default. This is an
* initialization function that can only be called before `zts_node_start()`.
*
* @param port Port number
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
* experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
ZTS_API int ZTCALL zts_init_allow_port_mapping(unsigned int allowed);
/**
* @brief Enable or disable whether the node will cache network details
* (enabled by default when `zts_init_from_storage()` is used.) Must be called before

View File

@@ -130,7 +130,7 @@ int zts_init_blacklist_if(const char* prefix, unsigned int len)
int zts_init_set_roots(const void* roots_data, unsigned int len)
{
ACQUIRE_SERVICE_OFFLINE();
return zts_service->setWorld(roots_data, len);
return zts_service->setRoots(roots_data, len);
}
int zts_init_set_port(unsigned short port)
@@ -140,6 +140,25 @@ int zts_init_set_port(unsigned short port)
return ZTS_ERR_OK;
}
int zts_init_set_random_port_range(unsigned short start_port, unsigned short end_port)
{
ACQUIRE_SERVICE_OFFLINE();
zts_service->setRandomPortRange(start_port, end_port);
return ZTS_ERR_OK;
}
int zts_init_allow_secondary_port(unsigned int allowed)
{
ACQUIRE_SERVICE_OFFLINE();
return zts_service->allowSecondaryPort(allowed);
}
int zts_init_allow_port_mapping(unsigned int allowed)
{
ACQUIRE_SERVICE_OFFLINE();
return zts_service->allowPortMapping(allowed);
}
int zts_init_allow_peer_cache(unsigned int allowed)
{
ACQUIRE_SERVICE_OFFLINE();
@@ -155,7 +174,7 @@ int zts_init_allow_net_cache(unsigned int allowed)
int zts_init_allow_roots_cache(unsigned int allowed)
{
ACQUIRE_SERVICE_OFFLINE();
return zts_service->allowWorldCaching(allowed);
return zts_service->allowRootSetCaching(allowed);
}
int zts_init_allow_id_cache(unsigned int allowed)

View File

@@ -167,20 +167,25 @@ NodeService::NodeService()
, _node((Node*)0)
, _nodeId(0x0)
, _primaryPort()
, _secondaryPort(0)
, _tertiaryPort(0)
, _randomPortRangeStart(0)
, _randomPortRangeEnd(0)
, _udpPortPickerCounter(0)
, _lastDirectReceiveFromGlobal(0)
, _lastRestart(0)
, _nextBackgroundTaskDeadline(0)
, _run(false)
, _termReason(ONE_STILL_RUNNING)
, _portMappingEnabled(true)
, _allowPortMapping(true)
#ifdef ZT_USE_MINIUPNPC
, _portMapper((PortMapper*)0)
#endif
, _allowSecondaryPort(true)
, _allowNetworkCaching(true)
, _allowPeerCaching(true)
, _allowIdentityCaching(true)
, _allowWorldCaching(true)
, _allowRootSetCaching(true)
, _userDefinedWorld(false)
, _nodeIsOnline(false)
, _eventsEnabled(false)
@@ -240,6 +245,9 @@ NodeService::ReasonForTermination NodeService::run()
_node = new Node(this, (void*)0, &cb, OSUtils::now());
}
unsigned int minPort = (_randomPortRangeStart ? _randomPortRangeStart : 20000);
unsigned int maxPort = (_randomPortRangeEnd ? _randomPortRangeEnd : 45500);
// Make sure we can use the primary port, and hunt for one if
// configured to do so
const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random
@@ -247,10 +255,11 @@ NodeService::ReasonForTermination NodeService::run()
if (_primaryPort == 0) {
unsigned int randp = 0;
Utils::getSecureRandom(&randp, sizeof(randp));
_primaryPort = 20000 + (randp % 45500);
_primaryPort = (randp % (maxPort - minPort + 1)) + minPort;
}
if (_trialBind(_primaryPort)) {
_ports[0] = _primaryPort;
break;
}
else {
_primaryPort = 0;
@@ -268,25 +277,32 @@ NodeService::ReasonForTermination NodeService::run()
// fail if more than one device behind the same NAT tries to use the
// same internal private address port number. Buggy NATs are a
// running theme.
_ports[1] = (_secondaryPort == 0) ? 20000 + ((unsigned int)_node->address() % 45500) : _secondaryPort;
if (_allowSecondaryPort) {
//_ports[1] = (_secondaryPort == 0) ? minPort + ((unsigned int)_node->address() % maxPort) : _secondaryPort;
_ports[1] = (_secondaryPort == 0) ? (((unsigned int)_node->address() % (maxPort - minPort + 1)) + minPort)
: _secondaryPort;
for (int i = 0;; ++i) {
if (i > 1000) {
_ports[1] = 0;
break;
}
else if (++_ports[1] >= 65536) {
_ports[1] = 20000;
else if (++_ports[1] >= maxPort) {
_ports[1] = minPort;
}
if (_trialBind(_ports[1])) {
_secondaryPort = _ports[1];
break;
}
}
}
#ifdef ZT_USE_MINIUPNPC
if (_portMappingEnabled) {
if (_allowPortMapping) {
// If we're running uPnP/NAT-PMP, bind a *third* port for that.
// We can't use the other two ports for that because some NATs
// do really funky stuff with ports that are explicitly mapped
// that breaks things.
maxPort = (_randomPortRangeEnd ? _randomPortRangeEnd : 65536);
if (_ports[1]) {
_ports[2] = (_tertiaryPort == 0) ? _ports[1] : _tertiaryPort;
for (int i = 0;; ++i) {
@@ -294,10 +310,11 @@ NodeService::ReasonForTermination NodeService::run()
_ports[2] = 0;
break;
}
else if (++_ports[2] >= 65536) {
_ports[2] = 20000;
else if (++_ports[2] >= maxPort) {
_ports[2] = minPort;
}
if (_trialBind(_ports[2])) {
_tertiaryPort = _ports[2];
break;
}
}
@@ -511,7 +528,7 @@ void NodeService::terminate()
_allowNetworkCaching = true;
_allowPeerCaching = true;
_allowIdentityCaching = true;
_allowWorldCaching = true;
_allowRootSetCaching = true;
memset(_publicIdStr, 0, ZT_IDENTITY_STRING_BUFFER_LENGTH);
memset(_secretIdStr, 0, ZT_IDENTITY_STRING_BUFFER_LENGTH);
_interfacePrefixBlacklist.clear();
@@ -1270,7 +1287,7 @@ void NodeService::nodeStatePutFunction(
case ZT_STATE_OBJECT_PLANET:
sendEventToUser(ZTS_EVENT_STORE_PLANET, data, len);
memcpy(_rootsData, data, len);
if (_homePath.length() > 0 && _allowWorldCaching) {
if (_homePath.length() > 0 && _allowRootSetCaching) {
OSUtils::ztsnprintf(p, sizeof(p), "%s" ZT_PATH_SEPARATOR_S "roots", _homePath.c_str());
}
else {
@@ -1711,11 +1728,42 @@ int NodeService::setPrimaryPort(unsigned short primaryPort)
return ZTS_ERR_OK;
}
int NodeService::setRandomPortRange(unsigned short startPort, unsigned short endPort)
{
Mutex::Lock _lr(_run_m);
if (_run) {
return ZTS_ERR_SERVICE;
}
_randomPortRangeStart = startPort;
_randomPortRangeEnd = endPort;
return ZTS_ERR_OK;
}
unsigned short NodeService::getPrimaryPort() const
{
return _primaryPort;
}
int NodeService::allowPortMapping(unsigned int allowed)
{
Mutex::Lock _lr(_run_m);
if (_run) {
return ZTS_ERR_SERVICE;
}
_allowPortMapping = allowed;
return ZTS_ERR_OK;
}
int NodeService::allowSecondaryPort(unsigned int allowed)
{
Mutex::Lock _lr(_run_m);
if (_run) {
return ZTS_ERR_SERVICE;
}
_allowSecondaryPort = allowed;
return ZTS_ERR_OK;
}
int NodeService::setUserEventSystem(Events* events)
{
Mutex::Lock _lr(_run_m);
@@ -1735,7 +1783,7 @@ void NodeService::enableEvents()
_events->enable();
}
int NodeService::setWorld(const void* rootsData, unsigned int len)
int NodeService::setRoots(const void* rootsData, unsigned int len)
{
if (! rootsData || len <= 0 || len > ZTS_STORE_DATA_LEN) {
return ZTS_ERR_ARG;
@@ -1834,13 +1882,13 @@ int NodeService::allowIdentityCaching(unsigned int allowed)
return ZTS_ERR_OK;
}
int NodeService::allowWorldCaching(unsigned int allowed)
int NodeService::allowRootSetCaching(unsigned int allowed)
{
Mutex::Lock _lr(_run_m);
if (_run) {
return ZTS_ERR_SERVICE;
}
_allowWorldCaching = allowed;
_allowRootSetCaching = allowed;
return ZTS_ERR_OK;
}
int NodeService::getNetworkBroadcast(uint64_t net_id)

View File

@@ -117,9 +117,13 @@ class NodeService {
Node* _node;
uint64_t _nodeId;
unsigned int _primaryPort = 0;
unsigned int _secondaryPort = 0;
unsigned int _tertiaryPort = 0;
unsigned int _primaryPort;
unsigned int _secondaryPort;
unsigned int _tertiaryPort;
unsigned int _randomPortRangeStart;
unsigned int _randomPortRangeEnd;
volatile unsigned int _udpPortPickerCounter;
std::map<uint64_t, unsigned int> peerCache;
@@ -197,15 +201,16 @@ class NodeService {
std::string _fatalErrorMessage;
// uPnP/NAT-PMP port mapper if enabled
bool _portMappingEnabled; // local.conf settings
bool _allowPortMapping;
#ifdef ZT_USE_MINIUPNPC
PortMapper* _portMapper;
#endif
bool _allowSecondaryPort;
uint8_t _allowNetworkCaching;
uint8_t _allowPeerCaching;
uint8_t _allowIdentityCaching;
uint8_t _allowWorldCaching;
uint8_t _allowRootSetCaching;
char _publicIdStr[ZT_IDENTITY_STRING_BUFFER_LENGTH] = { 0 };
char _secretIdStr[ZT_IDENTITY_STRING_BUFFER_LENGTH] = { 0 };
@@ -368,19 +373,28 @@ class NodeService {
/** Instruct the NodeService on where to look for identity files and caches */
int setHomePath(const char* homePath);
/** Set the NodeService's primary port */
/** Set the primary port */
int setPrimaryPort(unsigned short primaryPort);
/** Get the NodeService's primary port */
/** Set random range to select backup ports from */
int setRandomPortRange(unsigned short startPort, unsigned short endPort);
/** Get the primary port */
unsigned short getPrimaryPort() const;
/** Allow or disallow port-mapping */
int allowPortMapping(unsigned int allowed);
/** Allow or disallow backup port */
int allowSecondaryPort(unsigned int allowed);
/** Set the event system instance used to convey messages to the user */
int setUserEventSystem(Events* events);
void enableEvents();
/** Set the roots definition */
int setWorld(const void* data, unsigned int len);
int setRoots(const void* data, unsigned int len);
/** Add Interface prefix to blacklist (prevents ZeroTier from using that interface) */
int addInterfacePrefixToBlacklist(const char* prefix, unsigned int len);
@@ -401,7 +415,7 @@ class NodeService {
int allowIdentityCaching(unsigned int allowed);
/** Allow ZeroTier to cache root definitions to storage */
int allowWorldCaching(unsigned int allowed);
int allowRootSetCaching(unsigned int allowed);
/** Return whether broadcast is enabled on the given network */
int getNetworkBroadcast(uint64_t net_id);

View File

@@ -688,15 +688,24 @@ SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_set_roots(void* jarg1, int jarg2)
return jresult;
}
SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_set_port(unsigned short jarg1)
SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_set_port(unsigned short port)
{
int jresult;
unsigned short arg1;
int result;
arg1 = (unsigned short)jarg1;
result = (int)zts_init_set_port(arg1);
jresult = result;
return jresult;
return zts_init_set_port(port);
}
SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_set_random_port_range(unsigned short start_port, unsigned short end_port)
{
return zts_init_set_random_port_range(start_port, end_port);
}
SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_allow_secondary_port(int allowed)
{
return zts_init_allow_secondary_port(allowed);
}
SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_allow_port_mapping(int allowed)
{
return zts_init_allow_port_mapping(allowed);
}
SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_allow_net_cache(int jarg1)

View File

@@ -30,7 +30,6 @@ namespace ZeroTier.Core
public class Node {
static ulong _id = 0x0;
static ushort _primaryPort;
static ushort _secondaryPort;
static ushort _tertiaryPort;
static int _versionMajor;
@@ -39,7 +38,7 @@ namespace ZeroTier.Core
static bool _isOnline = false;
static bool _hasBeenFreed = false;
string _configFilePath;
ushort _servicePort;
ushort _primaryPort;
static ZeroTierManagedEventCallback _managedCallback;
CSharpCallbackWithStruct _unmanagedCallback;
@@ -64,7 +63,6 @@ namespace ZeroTier.Core
_versionRev = 0;
_isOnline = false;
_configFilePath = string.Empty;
_servicePort = 0;
_networks.Clear();
_peers.Clear();
}
@@ -99,11 +97,16 @@ namespace ZeroTier.Core
{
int res = Constants.ERR_OK;
if ((res = zts_init_set_port(port)) == Constants.ERR_OK) {
_servicePort = port;
_primaryPort = port;
}
return res;
}
public int InitSetRandomPortRange(UInt16 startPort, UInt16 endPort)
{
return zts_init_set_random_port_range(startPort, endPort);
}
public int InitSetRoots(byte[] roots_data, int len)
{
IntPtr unmanagedPointer = Marshal.AllocHGlobal(roots_data.Length);
@@ -118,6 +121,16 @@ namespace ZeroTier.Core
return zts_init_allow_net_cache(Convert.ToByte(allowed));
}
public int InitAllowSecondaryPort(bool allowed)
{
return zts_init_allow_secondary_port(Convert.ToByte(allowed));
}
public int InitAllowPortMapping(bool allowed)
{
return zts_init_allow_port_mapping(Convert.ToByte(allowed));
}
public int InitAllowPeerCaching(bool allowed)
{
return zts_init_allow_peer_cache(Convert.ToByte(allowed));
@@ -338,7 +351,7 @@ namespace ZeroTier.Core
(zts_route_info_t)Marshal.PtrToStructure(msg.route, typeof(zts_route_info_t));
newEvent = new ZeroTier.Core.Event();
newEvent.Code = msg.event_code;
newEvent.RouteInfo = default; // new RouteInfo();
// newEvent.RouteInfo = default; // new RouteInfo();
if (msg.event_code == Constants.EVENT_ROUTE_ADDED) {
newEvent.Name = "EVENT_ROUTE_ADDED";
@@ -354,10 +367,10 @@ namespace ZeroTier.Core
zts_peer_info_t peer_info = (zts_peer_info_t)Marshal.PtrToStructure(msg.peer, typeof(zts_peer_info_t));
newEvent = new ZeroTier.Core.Event();
newEvent.Code = msg.event_code;
newEvent.PeerInfo = default; // new PeerInfo();
// newEvent.PeerInfo = default; // new PeerInfo();
if (peer_info.role == Constants.PEER_ROLE_PLANET) {
Console.WriteLine("ROOT");
newEvent.Name = "PEER_ROLE_PLANET";
}
if (msg.event_code == Constants.EVENT_PEER_DIRECT) {
newEvent.Name = "EVENT_PEER_DIRECT";
@@ -381,7 +394,7 @@ namespace ZeroTier.Core
(zts_addr_info_t)Marshal.PtrToStructure(msg.addr, typeof(zts_addr_info_t));
newEvent = new ZeroTier.Core.Event();
newEvent.Code = msg.event_code;
newEvent.AddressInfo = default; // new AddressInfo();
// newEvent.AddressInfo = default; // new AddressInfo();
if (msg.event_code == Constants.EVENT_ADDR_ADDED_IP4) {
newEvent.Name = "EVENT_ADDR_ADDED_IP4";
@@ -402,7 +415,7 @@ namespace ZeroTier.Core
if (msg.cache != IntPtr.Zero) {
newEvent = new ZeroTier.Core.Event();
newEvent.Code = msg.event_code;
newEvent.AddressInfo = default; // new AddressInfo();
// newEvent.AddressInfo = default; // new AddressInfo();
if (msg.event_code == Constants.EVENT_STORE_IDENTITY_SECRET) {
newEvent.Name = "EVENT_STORE_IDENTITY_SECRET";
@@ -580,12 +593,22 @@ namespace ZeroTier.Core
}
}
// id
[DllImport("libzt", CharSet = CharSet.Ansi, EntryPoint = "CSharp_zts_id_new")] static extern int
zts_id_new(string arg1, IntPtr arg2);
[DllImport("libzt", CharSet = CharSet.Ansi, EntryPoint = "CSharp_zts_id_pair_is_valid")]
static extern int zts_id_pair_is_valid(string arg1, int arg2);
// init
[DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_net_cache")]
static extern int zts_init_allow_net_cache(int arg1);
[DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_peer_cache")]
static extern int zts_init_allow_peer_cache(int arg1);
[DllImport("libzt", CharSet = CharSet.Ansi, EntryPoint = "CSharp_zts_init_from_storage")]
static extern int zts_init_from_storage(string arg1);
@@ -604,6 +627,15 @@ namespace ZeroTier.Core
[DllImport("libzt", EntryPoint = "CSharp_zts_init_set_port")]
static extern int zts_init_set_port(ushort arg1);
[DllImport("libzt", EntryPoint = "CSharp_zts_init_set_random_port_range")]
static extern int zts_init_set_random_port_range(ushort arg1, ushort arg2);
[DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_secondary_port")]
static extern int zts_init_allow_secondary_port(int arg1);
[DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_port_mapping")]
static extern int zts_init_allow_port_mapping(int arg1);
// Core query API
[DllImport("libzt", EntryPoint = "CSharp_zts_core_lock_obtain")]
@@ -643,17 +675,6 @@ namespace ZeroTier.Core
[DllImport("libzt", EntryPoint = "CSharp_zts_core_query_mc")]
static extern int zts_core_query_mc(ulong net_id, int idx, ref ulong mac, ref uint adi);
// init
[DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_net_cache")]
static extern int zts_init_allow_net_cache(int arg1);
[DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_peer_cache")]
static extern int zts_init_allow_peer_cache(int arg1);
[DllImport("libzt", EntryPoint = "CSharp_zts_init_clear")]
static extern int zts_init_clear();
// addr
[DllImport("libzt", EntryPoint = "CSharp_zts_addr_is_assigned")]

View File

@@ -26,13 +26,16 @@ public class ExampleApp {
// node.InitAllowIdentityCaching(true);
// node.InitAllowWorldCaching(false);
node.InitSetEventHandler(OnZeroTierEvent);
node.InitSetPort(0); // Will randomly attempt ports if set to 0
//node.InitSetPort(0); // Will randomly attempt ports if not specified or is set to 0
node.InitSetRandomPortRange(40000, 50000);
// node.InitAllowSecondaryPort(false);
// (OPTIONAL) Set custom signed roots
// In this case we only allow ZeroTier to contact our Amsterdam root server
// To see examples of how to generate and sign roots definitions see docs.zerotier.com
/*
var rootsData = new byte[] {
0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0xea, 0xc9, 0x0a, 0x00, 0x00, 0x01, 0x6c, 0xe3, 0xe2, 0x39, 0x55, 0x74,
0xeb, 0x27, 0x9d, 0xc9, 0xe7, 0x5a, 0x52, 0xbb, 0x91, 0x8f, 0xf7, 0x43, 0x3c, 0xbf, 0x77, 0x5a, 0x4b, 0x57,
@@ -52,6 +55,7 @@ public class ExampleApp {
0x00, 0x00, 0x00, 0x00, 0x27, 0x09
};
node.InitSetRoots(rootsData, rootsData.Length);
*/
node.Start(); // Network activity only begins after calling Start()
while (! node.Online) {