mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-30 09:48:54 +00:00
Version bump, cleanup
This commit is contained in:
parent
a019c3dd5d
commit
54d2fa65dd
1042
attic/Cluster.cpp
1042
attic/Cluster.cpp
File diff suppressed because it is too large
Load Diff
@ -1,463 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*
|
|
||||||
* --
|
|
||||||
*
|
|
||||||
* You can be released from the requirements of the license by purchasing
|
|
||||||
* a commercial license. Buying such a license is mandatory as soon as you
|
|
||||||
* develop commercial closed-source software that incorporates or links
|
|
||||||
* directly against ZeroTier software without disclosing the source code
|
|
||||||
* of your own application.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ZT_CLUSTER_HPP
|
|
||||||
#define ZT_CLUSTER_HPP
|
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "Constants.hpp"
|
|
||||||
#include "../include/ZeroTierOne.h"
|
|
||||||
#include "Address.hpp"
|
|
||||||
#include "InetAddress.hpp"
|
|
||||||
#include "SHA512.hpp"
|
|
||||||
#include "Utils.hpp"
|
|
||||||
#include "Buffer.hpp"
|
|
||||||
#include "Mutex.hpp"
|
|
||||||
#include "SharedPtr.hpp"
|
|
||||||
#include "Hashtable.hpp"
|
|
||||||
#include "Packet.hpp"
|
|
||||||
#include "SharedPtr.hpp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timeout for cluster members being considered "alive"
|
|
||||||
*
|
|
||||||
* A cluster member is considered dead and will no longer have peers
|
|
||||||
* redirected to it if we have not heard a heartbeat in this long.
|
|
||||||
*/
|
|
||||||
#define ZT_CLUSTER_TIMEOUT 5000
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Desired period between doPeriodicTasks() in milliseconds
|
|
||||||
*/
|
|
||||||
#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 20
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How often to flush outgoing message queues (maximum interval)
|
|
||||||
*/
|
|
||||||
#define ZT_CLUSTER_FLUSH_PERIOD ZT_CLUSTER_PERIODIC_TASK_PERIOD
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of queued outgoing packets per sender address
|
|
||||||
*/
|
|
||||||
#define ZT_CLUSTER_MAX_QUEUE_PER_SENDER 16
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expiration time for send queue entries
|
|
||||||
*/
|
|
||||||
#define ZT_CLUSTER_QUEUE_EXPIRATION 3000
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chunk size for allocating queue entries
|
|
||||||
*
|
|
||||||
* Queue entries are allocated in chunks of this many and are added to a pool.
|
|
||||||
* ZT_CLUSTER_MAX_QUEUE_GLOBAL must be evenly divisible by this.
|
|
||||||
*/
|
|
||||||
#define ZT_CLUSTER_QUEUE_CHUNK_SIZE 32
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of chunks to ever allocate
|
|
||||||
*
|
|
||||||
* This is a global sanity limit to prevent resource exhaustion attacks. It
|
|
||||||
* works out to about 600mb of RAM. You'll never see this on a normal edge
|
|
||||||
* node. We're unlikely to see this on a root server unless someone is DOSing
|
|
||||||
* us. In that case cluster relaying will be affected but other functions
|
|
||||||
* should continue to operate normally.
|
|
||||||
*/
|
|
||||||
#define ZT_CLUSTER_MAX_QUEUE_CHUNKS 8194
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Max data per queue entry
|
|
||||||
*/
|
|
||||||
#define ZT_CLUSTER_SEND_QUEUE_DATA_MAX 1500
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We won't send WANT_PEER to other members more than every (ms) per recipient
|
|
||||||
*/
|
|
||||||
#define ZT_CLUSTER_WANT_PEER_EVERY 1000
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
class RuntimeEnvironment;
|
|
||||||
class MulticastGroup;
|
|
||||||
class Peer;
|
|
||||||
class Identity;
|
|
||||||
|
|
||||||
// Internal class implemented inside Cluster.cpp
|
|
||||||
class _ClusterSendQueue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Multi-homing cluster state replication and packet relaying
|
|
||||||
*
|
|
||||||
* Multi-homing means more than one node sharing the same ZeroTier identity.
|
|
||||||
* There is nothing in the protocol to prevent this, but to make it work well
|
|
||||||
* requires the devices sharing an identity to cooperate and share some
|
|
||||||
* information.
|
|
||||||
*
|
|
||||||
* There are three use cases we want to fulfill:
|
|
||||||
*
|
|
||||||
* (1) Multi-homing of root servers with handoff for efficient routing,
|
|
||||||
* HA, and load balancing across many commodity nodes.
|
|
||||||
* (2) Multi-homing of network controllers for the same reason.
|
|
||||||
* (3) Multi-homing of nodes on virtual networks, such as domain servers
|
|
||||||
* and other important endpoints.
|
|
||||||
*
|
|
||||||
* These use cases are in order of escalating difficulty. The initial
|
|
||||||
* version of Cluster is aimed at satisfying the first, though you are
|
|
||||||
* free to try #2 and #3.
|
|
||||||
*/
|
|
||||||
class Cluster
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* State message types
|
|
||||||
*/
|
|
||||||
enum StateMessageType
|
|
||||||
{
|
|
||||||
CLUSTER_MESSAGE_NOP = 0,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This cluster member is alive:
|
|
||||||
* <[2] version minor>
|
|
||||||
* <[2] version major>
|
|
||||||
* <[2] version revision>
|
|
||||||
* <[1] protocol version>
|
|
||||||
* <[4] X location (signed 32-bit)>
|
|
||||||
* <[4] Y location (signed 32-bit)>
|
|
||||||
* <[4] Z location (signed 32-bit)>
|
|
||||||
* <[8] local clock at this member>
|
|
||||||
* <[8] load average>
|
|
||||||
* <[8] number of peers>
|
|
||||||
* <[8] flags (currently unused, must be zero)>
|
|
||||||
* <[1] number of preferred ZeroTier endpoints>
|
|
||||||
* <[...] InetAddress(es) of preferred ZeroTier endpoint(s)>
|
|
||||||
*
|
|
||||||
* Cluster members constantly broadcast an alive heartbeat and will only
|
|
||||||
* receive peer redirects if they've done so within the timeout.
|
|
||||||
*/
|
|
||||||
CLUSTER_MESSAGE_ALIVE = 1,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cluster member has this peer:
|
|
||||||
* <[...] serialized identity of peer>
|
|
||||||
*
|
|
||||||
* This is typically sent in response to WANT_PEER but can also be pushed
|
|
||||||
* to prepopulate if this makes sense.
|
|
||||||
*/
|
|
||||||
CLUSTER_MESSAGE_HAVE_PEER = 2,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cluster member wants this peer:
|
|
||||||
* <[5] ZeroTier address of peer>
|
|
||||||
*
|
|
||||||
* Members that have a direct link to this peer will respond with
|
|
||||||
* HAVE_PEER.
|
|
||||||
*/
|
|
||||||
CLUSTER_MESSAGE_WANT_PEER = 3,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A remote packet that we should also possibly respond to:
|
|
||||||
* <[2] 16-bit length of remote packet>
|
|
||||||
* <[...] remote packet payload>
|
|
||||||
*
|
|
||||||
* Cluster members may relay requests by relaying the request packet.
|
|
||||||
* These may include requests such as WHOIS and MULTICAST_GATHER. The
|
|
||||||
* packet must be already decrypted, decompressed, and authenticated.
|
|
||||||
*
|
|
||||||
* This can only be used for small request packets as per the cluster
|
|
||||||
* message size limit, but since these are the only ones in question
|
|
||||||
* this is fine.
|
|
||||||
*
|
|
||||||
* If a response is generated it is sent via PROXY_SEND.
|
|
||||||
*/
|
|
||||||
CLUSTER_MESSAGE_REMOTE_PACKET = 4,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request that VERB_RENDEZVOUS be sent to a peer that we have:
|
|
||||||
* <[5] ZeroTier address of peer on recipient's side>
|
|
||||||
* <[5] ZeroTier address of peer on sender's side>
|
|
||||||
* <[1] 8-bit number of sender's peer's active path addresses>
|
|
||||||
* <[...] series of serialized InetAddresses of sender's peer's paths>
|
|
||||||
*
|
|
||||||
* This requests that we perform NAT-t introduction between a peer that
|
|
||||||
* we have and one on the sender's side. The sender furnishes contact
|
|
||||||
* info for its peer, and we send VERB_RENDEZVOUS to both sides: to ours
|
|
||||||
* directly and with PROXY_SEND to theirs.
|
|
||||||
*/
|
|
||||||
CLUSTER_MESSAGE_PROXY_UNITE = 5,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request that a cluster member send a packet to a locally-known peer:
|
|
||||||
* <[5] ZeroTier address of recipient>
|
|
||||||
* <[1] packet verb>
|
|
||||||
* <[2] length of packet payload>
|
|
||||||
* <[...] packet payload>
|
|
||||||
*
|
|
||||||
* This differs from RELAY in that it requests the receiving cluster
|
|
||||||
* member to actually compose a ZeroTier Packet from itself to the
|
|
||||||
* provided recipient. RELAY simply says "please forward this blob."
|
|
||||||
* RELAY is used to implement peer-to-peer relaying with RENDEZVOUS,
|
|
||||||
* while PROXY_SEND is used to implement proxy sending (which right
|
|
||||||
* now is only used to send RENDEZVOUS).
|
|
||||||
*/
|
|
||||||
CLUSTER_MESSAGE_PROXY_SEND = 6,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replicate a network config for a network we belong to:
|
|
||||||
* <[...] network config chunk>
|
|
||||||
*
|
|
||||||
* This is used by clusters to avoid every member having to query
|
|
||||||
* for the same netconf for networks all members belong to.
|
|
||||||
*
|
|
||||||
* The first field of a network config chunk is the network ID,
|
|
||||||
* so this can be checked to look up the network on receipt.
|
|
||||||
*/
|
|
||||||
CLUSTER_MESSAGE_NETWORK_CONFIG = 7
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a new cluster
|
|
||||||
*/
|
|
||||||
Cluster(
|
|
||||||
const RuntimeEnvironment *renv,
|
|
||||||
uint16_t id,
|
|
||||||
const std::vector<InetAddress> &zeroTierPhysicalEndpoints,
|
|
||||||
int32_t x,
|
|
||||||
int32_t y,
|
|
||||||
int32_t z,
|
|
||||||
void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
|
|
||||||
void *sendFunctionArg,
|
|
||||||
int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
|
|
||||||
void *addressToLocationFunctionArg);
|
|
||||||
|
|
||||||
~Cluster();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return This cluster member's ID
|
|
||||||
*/
|
|
||||||
inline uint16_t id() const throw() { return _id; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle an incoming intra-cluster message
|
|
||||||
*
|
|
||||||
* @param data Message data
|
|
||||||
* @param len Message length (max: ZT_CLUSTER_MAX_MESSAGE_LENGTH)
|
|
||||||
*/
|
|
||||||
void handleIncomingStateMessage(const void *msg,unsigned int len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Broadcast that we have a given peer
|
|
||||||
*
|
|
||||||
* This should be done when new peers are first contacted.
|
|
||||||
*
|
|
||||||
* @param id Identity of peer
|
|
||||||
*/
|
|
||||||
void broadcastHavePeer(const Identity &id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Broadcast a network config chunk to other members of cluster
|
|
||||||
*
|
|
||||||
* @param chunk Chunk data
|
|
||||||
* @param len Length of chunk
|
|
||||||
*/
|
|
||||||
void broadcastNetworkConfigChunk(const void *chunk,unsigned int len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the cluster has this peer, prepare the packet to send via cluster
|
|
||||||
*
|
|
||||||
* Note that outp is only armored (or modified at all) if the return value is a member ID.
|
|
||||||
*
|
|
||||||
* @param toPeerAddress Value of outp.destination(), simply to save additional lookup
|
|
||||||
* @param ts Result: set to time of last HAVE_PEER from the cluster
|
|
||||||
* @param peerSecret Result: Buffer to fill with peer secret on valid return value, must be at least ZT_PEER_SECRET_KEY_LENGTH bytes
|
|
||||||
* @return -1 if cluster does not know this peer, or a member ID to pass to sendViaCluster()
|
|
||||||
*/
|
|
||||||
int checkSendViaCluster(const Address &toPeerAddress,uint64_t &mostRecentTs,void *peerSecret);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send data via cluster front plane (packet head or fragment)
|
|
||||||
*
|
|
||||||
* @param haveMemberId Member ID that has this peer as returned by prepSendviaCluster()
|
|
||||||
* @param toPeerAddress Destination peer address
|
|
||||||
* @param data Packet or packet fragment data
|
|
||||||
* @param len Length of packet or fragment
|
|
||||||
* @return True if packet was sent (and outp was modified via armoring)
|
|
||||||
*/
|
|
||||||
bool sendViaCluster(int haveMemberId,const Address &toPeerAddress,const void *data,unsigned int len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Relay a packet via the cluster
|
|
||||||
*
|
|
||||||
* This is used in the outgoing packet and relaying logic in Switch to
|
|
||||||
* relay packets to other cluster members. It isn't PROXY_SEND-- that is
|
|
||||||
* used internally in Cluster to send responses to peer queries.
|
|
||||||
*
|
|
||||||
* @param fromPeerAddress Source peer address (if known, should be NULL for fragments)
|
|
||||||
* @param toPeerAddress Destination peer address
|
|
||||||
* @param data Packet or packet fragment data
|
|
||||||
* @param len Length of packet or fragment
|
|
||||||
* @param unite If true, also request proxy unite across cluster
|
|
||||||
*/
|
|
||||||
void relayViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a distributed query to other cluster members
|
|
||||||
*
|
|
||||||
* Some queries such as WHOIS or MULTICAST_GATHER need a response from other
|
|
||||||
* cluster members. Replies (if any) will be sent back to the peer via
|
|
||||||
* PROXY_SEND across the cluster.
|
|
||||||
*
|
|
||||||
* @param pkt Packet to distribute
|
|
||||||
*/
|
|
||||||
void sendDistributedQuery(const Packet &pkt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call every ~ZT_CLUSTER_PERIODIC_TASK_PERIOD milliseconds.
|
|
||||||
*/
|
|
||||||
void doPeriodicTasks();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a member ID to this cluster
|
|
||||||
*
|
|
||||||
* @param memberId Member ID
|
|
||||||
*/
|
|
||||||
void addMember(uint16_t memberId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a member ID from this cluster
|
|
||||||
*
|
|
||||||
* @param memberId Member ID to remove
|
|
||||||
*/
|
|
||||||
void removeMember(uint16_t memberId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a better cluster endpoint for this peer (if any)
|
|
||||||
*
|
|
||||||
* @param redirectTo InetAddress to be set to a better endpoint (if there is one)
|
|
||||||
* @param peerAddress Address of peer to (possibly) redirect
|
|
||||||
* @param peerPhysicalAddress Physical address of peer's current best path (where packet was most recently received or getBestPath()->address())
|
|
||||||
* @param offload Always redirect if possible -- can be used to offload peers during shutdown
|
|
||||||
* @return True if redirectTo was set to a new address, false if redirectTo was not modified
|
|
||||||
*/
|
|
||||||
bool findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ip Address to check
|
|
||||||
* @return True if this is a cluster frontplane address (excluding our addresses)
|
|
||||||
*/
|
|
||||||
bool isClusterPeerFrontplane(const InetAddress &ip) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill out ZT_ClusterStatus structure (from core API)
|
|
||||||
*
|
|
||||||
* @param status Reference to structure to hold result (anything there is replaced)
|
|
||||||
*/
|
|
||||||
void status(ZT_ClusterStatus &status) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void _send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len);
|
|
||||||
void _flush(uint16_t memberId);
|
|
||||||
|
|
||||||
void _doREMOTE_WHOIS(uint64_t fromMemberId,const Packet &remotep);
|
|
||||||
void _doREMOTE_MULTICAST_GATHER(uint64_t fromMemberId,const Packet &remotep);
|
|
||||||
|
|
||||||
// These are initialized in the constructor and remain immutable ------------
|
|
||||||
uint16_t _masterSecret[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)];
|
|
||||||
unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH];
|
|
||||||
const RuntimeEnvironment *RR;
|
|
||||||
_ClusterSendQueue *const _sendQueue;
|
|
||||||
void (*_sendFunction)(void *,unsigned int,const void *,unsigned int);
|
|
||||||
void *_sendFunctionArg;
|
|
||||||
int (*_addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *);
|
|
||||||
void *_addressToLocationFunctionArg;
|
|
||||||
const int32_t _x;
|
|
||||||
const int32_t _y;
|
|
||||||
const int32_t _z;
|
|
||||||
const uint16_t _id;
|
|
||||||
const std::vector<InetAddress> _zeroTierPhysicalEndpoints;
|
|
||||||
// end immutable fields -----------------------------------------------------
|
|
||||||
|
|
||||||
struct _Member
|
|
||||||
{
|
|
||||||
unsigned char key[ZT_PEER_SECRET_KEY_LENGTH];
|
|
||||||
|
|
||||||
uint64_t lastReceivedAliveAnnouncement;
|
|
||||||
uint64_t lastAnnouncedAliveTo;
|
|
||||||
|
|
||||||
uint64_t load;
|
|
||||||
uint64_t peers;
|
|
||||||
int32_t x,y,z;
|
|
||||||
|
|
||||||
std::vector<InetAddress> zeroTierPhysicalEndpoints;
|
|
||||||
|
|
||||||
Buffer<ZT_CLUSTER_MAX_MESSAGE_LENGTH> q;
|
|
||||||
|
|
||||||
Mutex lock;
|
|
||||||
|
|
||||||
inline void clear()
|
|
||||||
{
|
|
||||||
lastReceivedAliveAnnouncement = 0;
|
|
||||||
lastAnnouncedAliveTo = 0;
|
|
||||||
load = 0;
|
|
||||||
peers = 0;
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
z = 0;
|
|
||||||
zeroTierPhysicalEndpoints.clear();
|
|
||||||
q.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
_Member() { this->clear(); }
|
|
||||||
~_Member() { Utils::burn(key,sizeof(key)); }
|
|
||||||
};
|
|
||||||
_Member *const _members;
|
|
||||||
|
|
||||||
std::vector<uint16_t> _memberIds;
|
|
||||||
Mutex _memberIds_m;
|
|
||||||
|
|
||||||
struct _RemotePeer
|
|
||||||
{
|
|
||||||
_RemotePeer() : lastHavePeerReceived(0),lastSentWantPeer(0) {}
|
|
||||||
~_RemotePeer() { Utils::burn(key,ZT_PEER_SECRET_KEY_LENGTH); }
|
|
||||||
uint64_t lastHavePeerReceived;
|
|
||||||
uint64_t lastSentWantPeer;
|
|
||||||
uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]; // secret key from identity agreement
|
|
||||||
};
|
|
||||||
std::map< std::pair<Address,unsigned int>,_RemotePeer > _remotePeers; // we need ordered behavior and lower_bound here
|
|
||||||
Mutex _remotePeers_m;
|
|
||||||
|
|
||||||
uint64_t _lastFlushed;
|
|
||||||
uint64_t _lastCleanedRemotePeers;
|
|
||||||
uint64_t _lastCleanedQueue;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif // ZT_ENABLE_CLUSTER
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*
|
|
||||||
* --
|
|
||||||
*
|
|
||||||
* You can be released from the requirements of the license by purchasing
|
|
||||||
* a commercial license. Buying such a license is mandatory as soon as you
|
|
||||||
* develop commercial closed-source software that incorporates or links
|
|
||||||
* directly against ZeroTier software without disclosing the source code
|
|
||||||
* of your own application.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ZT_CLUSTERDEFINITION_HPP
|
|
||||||
#define ZT_CLUSTERDEFINITION_HPP
|
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "../node/Constants.hpp"
|
|
||||||
#include "../node/Utils.hpp"
|
|
||||||
#include "../node/NonCopyable.hpp"
|
|
||||||
#include "../osdep/OSUtils.hpp"
|
|
||||||
|
|
||||||
#include "ClusterGeoIpService.hpp"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parser for cluster definition file
|
|
||||||
*/
|
|
||||||
class ClusterDefinition : NonCopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
struct MemberDefinition
|
|
||||||
{
|
|
||||||
MemberDefinition() : id(0),x(0),y(0),z(0) { name[0] = (char)0; }
|
|
||||||
|
|
||||||
unsigned int id;
|
|
||||||
int x,y,z;
|
|
||||||
char name[256];
|
|
||||||
InetAddress clusterEndpoint;
|
|
||||||
std::vector<InetAddress> zeroTierEndpoints;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load and initialize cluster definition and GeoIP data if any
|
|
||||||
*
|
|
||||||
* @param myAddress My ZeroTier address
|
|
||||||
* @param pathToClusterFile Path to cluster definition file
|
|
||||||
* @throws std::runtime_error Invalid cluster definition or unable to load data
|
|
||||||
*/
|
|
||||||
ClusterDefinition(uint64_t myAddress,const char *pathToClusterFile)
|
|
||||||
{
|
|
||||||
std::string cf;
|
|
||||||
if (!OSUtils::readFile(pathToClusterFile,cf))
|
|
||||||
return;
|
|
||||||
|
|
||||||
char myAddressStr[64];
|
|
||||||
Utils::ztsnprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
|
|
||||||
|
|
||||||
std::vector<std::string> lines(OSUtils::split(cf.c_str(),"\r\n","",""));
|
|
||||||
for(std::vector<std::string>::iterator l(lines.begin());l!=lines.end();++l) {
|
|
||||||
std::vector<std::string> fields(OSUtils::split(l->c_str()," \t","",""));
|
|
||||||
if ((fields.size() < 5)||(fields[0][0] == '#')||(fields[0] != myAddressStr))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// <address> geo <CSV path> <ip start column> <ip end column> <latitutde column> <longitude column>
|
|
||||||
if (fields[1] == "geo") {
|
|
||||||
if ((fields.size() >= 7)&&(OSUtils::fileExists(fields[2].c_str()))) {
|
|
||||||
int ipStartColumn = Utils::strToInt(fields[3].c_str());
|
|
||||||
int ipEndColumn = Utils::strToInt(fields[4].c_str());
|
|
||||||
int latitudeColumn = Utils::strToInt(fields[5].c_str());
|
|
||||||
int longitudeColumn = Utils::strToInt(fields[6].c_str());
|
|
||||||
if (_geo.load(fields[2].c_str(),ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn) <= 0)
|
|
||||||
throw std::runtime_error(std::string("failed to load geo-ip data from ")+fields[2]);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// <address> <ID> <name> <backplane IP/port(s)> <ZT frontplane IP/port(s)> <x,y,z>
|
|
||||||
int id = Utils::strToUInt(fields[1].c_str());
|
|
||||||
if ((id < 0)||(id > ZT_CLUSTER_MAX_MEMBERS))
|
|
||||||
throw std::runtime_error(std::string("invalid cluster member ID: ")+fields[1]);
|
|
||||||
MemberDefinition &md = _md[id];
|
|
||||||
|
|
||||||
md.id = (unsigned int)id;
|
|
||||||
if (fields.size() >= 6) {
|
|
||||||
std::vector<std::string> xyz(OSUtils::split(fields[5].c_str(),",","",""));
|
|
||||||
md.x = (xyz.size() > 0) ? Utils::strToInt(xyz[0].c_str()) : 0;
|
|
||||||
md.y = (xyz.size() > 1) ? Utils::strToInt(xyz[1].c_str()) : 0;
|
|
||||||
md.z = (xyz.size() > 2) ? Utils::strToInt(xyz[2].c_str()) : 0;
|
|
||||||
}
|
|
||||||
Utils::scopy(md.name,sizeof(md.name),fields[2].c_str());
|
|
||||||
md.clusterEndpoint.fromString(fields[3]);
|
|
||||||
if (!md.clusterEndpoint)
|
|
||||||
continue;
|
|
||||||
std::vector<std::string> zips(OSUtils::split(fields[4].c_str(),",","",""));
|
|
||||||
for(std::vector<std::string>::iterator zip(zips.begin());zip!=zips.end();++zip) {
|
|
||||||
InetAddress i;
|
|
||||||
i.fromString(*zip);
|
|
||||||
if (i)
|
|
||||||
md.zeroTierEndpoints.push_back(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ids.push_back((unsigned int)id);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(_ids.begin(),_ids.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return All member definitions in this cluster by ID (ID is array index)
|
|
||||||
*/
|
|
||||||
inline const MemberDefinition &operator[](unsigned int id) const throw() { return _md[id]; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Number of members in this cluster
|
|
||||||
*/
|
|
||||||
inline unsigned int size() const throw() { return (unsigned int)_ids.size(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return IDs of members in this cluster sorted by ID
|
|
||||||
*/
|
|
||||||
inline const std::vector<unsigned int> &ids() const throw() { return _ids; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return GeoIP service for this cluster
|
|
||||||
*/
|
|
||||||
inline ClusterGeoIpService &geo() throw() { return _geo; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return A vector (new copy) containing all cluster members
|
|
||||||
*/
|
|
||||||
inline std::vector<MemberDefinition> members() const
|
|
||||||
{
|
|
||||||
std::vector<MemberDefinition> m;
|
|
||||||
for(std::vector<unsigned int>::const_iterator i(_ids.begin());i!=_ids.end();++i)
|
|
||||||
m.push_back(_md[*i]);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
MemberDefinition _md[ZT_CLUSTER_MAX_MEMBERS];
|
|
||||||
std::vector<unsigned int> _ids;
|
|
||||||
ClusterGeoIpService _geo;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif // ZT_ENABLE_CLUSTER
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,243 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*
|
|
||||||
* --
|
|
||||||
*
|
|
||||||
* You can be released from the requirements of the license by purchasing
|
|
||||||
* a commercial license. Buying such a license is mandatory as soon as you
|
|
||||||
* develop commercial closed-source software that incorporates or links
|
|
||||||
* directly against ZeroTier software without disclosing the source code
|
|
||||||
* of your own application.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include "ClusterGeoIpService.hpp"
|
|
||||||
|
|
||||||
#include "../node/Utils.hpp"
|
|
||||||
#include "../osdep/OSUtils.hpp"
|
|
||||||
|
|
||||||
#define ZT_CLUSTERGEOIPSERVICE_FILE_MODIFICATION_CHECK_EVERY 10000
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
ClusterGeoIpService::ClusterGeoIpService() :
|
|
||||||
_pathToCsv(),
|
|
||||||
_ipStartColumn(-1),
|
|
||||||
_ipEndColumn(-1),
|
|
||||||
_latitudeColumn(-1),
|
|
||||||
_longitudeColumn(-1),
|
|
||||||
_lastFileCheckTime(0),
|
|
||||||
_csvModificationTime(0),
|
|
||||||
_csvFileSize(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ClusterGeoIpService::~ClusterGeoIpService()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClusterGeoIpService::locate(const InetAddress &ip,int &x,int &y,int &z)
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
|
|
||||||
if ((_pathToCsv.length() > 0)&&((OSUtils::now() - _lastFileCheckTime) > ZT_CLUSTERGEOIPSERVICE_FILE_MODIFICATION_CHECK_EVERY)) {
|
|
||||||
_lastFileCheckTime = OSUtils::now();
|
|
||||||
if ((_csvFileSize != OSUtils::getFileSize(_pathToCsv.c_str()))||(_csvModificationTime != OSUtils::getLastModified(_pathToCsv.c_str())))
|
|
||||||
_load(_pathToCsv.c_str(),_ipStartColumn,_ipEndColumn,_latitudeColumn,_longitudeColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We search by looking up the upper bound of the sorted vXdb vectors
|
|
||||||
* and then iterating down for a matching IP range. We stop when we hit
|
|
||||||
* the beginning or an entry whose start and end are before the IP we
|
|
||||||
* are searching. */
|
|
||||||
|
|
||||||
if ((ip.ss_family == AF_INET)&&(_v4db.size() > 0)) {
|
|
||||||
_V4E key;
|
|
||||||
key.start = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr));
|
|
||||||
std::vector<_V4E>::const_iterator i(std::upper_bound(_v4db.begin(),_v4db.end(),key));
|
|
||||||
while (i != _v4db.begin()) {
|
|
||||||
--i;
|
|
||||||
if ((key.start >= i->start)&&(key.start <= i->end)) {
|
|
||||||
x = i->x;
|
|
||||||
y = i->y;
|
|
||||||
z = i->z;
|
|
||||||
//printf("%s : %f,%f %d,%d,%d\n",ip.toIpString().c_str(),i->lat,i->lon,x,y,z);
|
|
||||||
return true;
|
|
||||||
} else if ((key.start > i->start)&&(key.start > i->end))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if ((ip.ss_family == AF_INET6)&&(_v6db.size() > 0)) {
|
|
||||||
_V6E key;
|
|
||||||
memcpy(key.start,reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
|
|
||||||
std::vector<_V6E>::const_iterator i(std::upper_bound(_v6db.begin(),_v6db.end(),key));
|
|
||||||
while (i != _v6db.begin()) {
|
|
||||||
--i;
|
|
||||||
const int s_vs_s = memcmp(key.start,i->start,16);
|
|
||||||
const int s_vs_e = memcmp(key.start,i->end,16);
|
|
||||||
if ((s_vs_s >= 0)&&(s_vs_e <= 0)) {
|
|
||||||
x = i->x;
|
|
||||||
y = i->y;
|
|
||||||
z = i->z;
|
|
||||||
//printf("%s : %f,%f %d,%d,%d\n",ip.toIpString().c_str(),i->lat,i->lon,x,y,z);
|
|
||||||
return true;
|
|
||||||
} else if ((s_vs_s > 0)&&(s_vs_e > 0))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClusterGeoIpService::_parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
|
|
||||||
{
|
|
||||||
std::vector<std::string> ls(OSUtils::split(line,",\t","\\","\"'"));
|
|
||||||
if ( ((ipStartColumn >= 0)&&(ipStartColumn < (int)ls.size()))&&
|
|
||||||
((ipEndColumn >= 0)&&(ipEndColumn < (int)ls.size()))&&
|
|
||||||
((latitudeColumn >= 0)&&(latitudeColumn < (int)ls.size()))&&
|
|
||||||
((longitudeColumn >= 0)&&(longitudeColumn < (int)ls.size())) ) {
|
|
||||||
InetAddress ipStart(ls[ipStartColumn].c_str(),0);
|
|
||||||
InetAddress ipEnd(ls[ipEndColumn].c_str(),0);
|
|
||||||
const double lat = strtod(ls[latitudeColumn].c_str(),(char **)0);
|
|
||||||
const double lon = strtod(ls[longitudeColumn].c_str(),(char **)0);
|
|
||||||
|
|
||||||
if ((ipStart.ss_family == ipEnd.ss_family)&&(ipStart)&&(ipEnd)&&(std::isfinite(lat))&&(std::isfinite(lon))) {
|
|
||||||
const double latRadians = lat * 0.01745329251994; // PI / 180
|
|
||||||
const double lonRadians = lon * 0.01745329251994; // PI / 180
|
|
||||||
const double cosLat = cos(latRadians);
|
|
||||||
const int x = (int)round((-6371.0) * cosLat * cos(lonRadians)); // 6371 == Earth's approximate radius in kilometers
|
|
||||||
const int y = (int)round(6371.0 * sin(latRadians));
|
|
||||||
const int z = (int)round(6371.0 * cosLat * sin(lonRadians));
|
|
||||||
|
|
||||||
if (ipStart.ss_family == AF_INET) {
|
|
||||||
v4db.push_back(_V4E());
|
|
||||||
v4db.back().start = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ipStart)->sin_addr.s_addr));
|
|
||||||
v4db.back().end = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&ipEnd)->sin_addr.s_addr));
|
|
||||||
v4db.back().lat = (float)lat;
|
|
||||||
v4db.back().lon = (float)lon;
|
|
||||||
v4db.back().x = x;
|
|
||||||
v4db.back().y = y;
|
|
||||||
v4db.back().z = z;
|
|
||||||
//printf("%s - %s : %d,%d,%d\n",ipStart.toIpString().c_str(),ipEnd.toIpString().c_str(),x,y,z);
|
|
||||||
} else if (ipStart.ss_family == AF_INET6) {
|
|
||||||
v6db.push_back(_V6E());
|
|
||||||
memcpy(v6db.back().start,reinterpret_cast<const struct sockaddr_in6 *>(&ipStart)->sin6_addr.s6_addr,16);
|
|
||||||
memcpy(v6db.back().end,reinterpret_cast<const struct sockaddr_in6 *>(&ipEnd)->sin6_addr.s6_addr,16);
|
|
||||||
v6db.back().lat = (float)lat;
|
|
||||||
v6db.back().lon = (float)lon;
|
|
||||||
v6db.back().x = x;
|
|
||||||
v6db.back().y = y;
|
|
||||||
v6db.back().z = z;
|
|
||||||
//printf("%s - %s : %d,%d,%d\n",ipStart.toIpString().c_str(),ipEnd.toIpString().c_str(),x,y,z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long ClusterGeoIpService::_load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
|
|
||||||
{
|
|
||||||
// assumes _lock is locked
|
|
||||||
|
|
||||||
FILE *f = fopen(pathToCsv,"rb");
|
|
||||||
if (!f)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
std::vector<_V4E> v4db;
|
|
||||||
std::vector<_V6E> v6db;
|
|
||||||
v4db.reserve(16777216);
|
|
||||||
v6db.reserve(16777216);
|
|
||||||
|
|
||||||
char buf[4096];
|
|
||||||
char linebuf[1024];
|
|
||||||
unsigned int lineptr = 0;
|
|
||||||
for(;;) {
|
|
||||||
int n = (int)fread(buf,1,sizeof(buf),f);
|
|
||||||
if (n <= 0)
|
|
||||||
break;
|
|
||||||
for(int i=0;i<n;++i) {
|
|
||||||
if ((buf[i] == '\r')||(buf[i] == '\n')||(buf[i] == (char)0)) {
|
|
||||||
if (lineptr) {
|
|
||||||
linebuf[lineptr] = (char)0;
|
|
||||||
_parseLine(linebuf,v4db,v6db,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
|
|
||||||
}
|
|
||||||
lineptr = 0;
|
|
||||||
} else if (lineptr < (unsigned int)sizeof(linebuf))
|
|
||||||
linebuf[lineptr++] = buf[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lineptr) {
|
|
||||||
linebuf[lineptr] = (char)0;
|
|
||||||
_parseLine(linebuf,v4db,v6db,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
if ((v4db.size() > 0)||(v6db.size() > 0)) {
|
|
||||||
std::sort(v4db.begin(),v4db.end());
|
|
||||||
std::sort(v6db.begin(),v6db.end());
|
|
||||||
|
|
||||||
_pathToCsv = pathToCsv;
|
|
||||||
_ipStartColumn = ipStartColumn;
|
|
||||||
_ipEndColumn = ipEndColumn;
|
|
||||||
_latitudeColumn = latitudeColumn;
|
|
||||||
_longitudeColumn = longitudeColumn;
|
|
||||||
|
|
||||||
_lastFileCheckTime = OSUtils::now();
|
|
||||||
_csvModificationTime = OSUtils::getLastModified(pathToCsv);
|
|
||||||
_csvFileSize = OSUtils::getFileSize(pathToCsv);
|
|
||||||
|
|
||||||
_v4db.swap(v4db);
|
|
||||||
_v6db.swap(v6db);
|
|
||||||
|
|
||||||
return (long)(_v4db.size() + _v6db.size());
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif // ZT_ENABLE_CLUSTER
|
|
||||||
|
|
||||||
/*
|
|
||||||
int main(int argc,char **argv)
|
|
||||||
{
|
|
||||||
char buf[1024];
|
|
||||||
|
|
||||||
ZeroTier::ClusterGeoIpService gip;
|
|
||||||
printf("loading...\n");
|
|
||||||
gip.load("/Users/api/Code/ZeroTier/Infrastructure/root-servers/zerotier-one/cluster-geoip.csv",0,1,5,6);
|
|
||||||
printf("... done!\n"); fflush(stdout);
|
|
||||||
|
|
||||||
while (gets(buf)) { // unsafe, testing only
|
|
||||||
ZeroTier::InetAddress addr(buf,0);
|
|
||||||
printf("looking up: %s\n",addr.toString().c_str()); fflush(stdout);
|
|
||||||
int x = 0,y = 0,z = 0;
|
|
||||||
if (gip.locate(addr,x,y,z)) {
|
|
||||||
//printf("%s: %d,%d,%d\n",addr.toString().c_str(),x,y,z); fflush(stdout);
|
|
||||||
} else {
|
|
||||||
printf("%s: not found!\n",addr.toString().c_str()); fflush(stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
*/
|
|
@ -1,151 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*
|
|
||||||
* --
|
|
||||||
*
|
|
||||||
* You can be released from the requirements of the license by purchasing
|
|
||||||
* a commercial license. Buying such a license is mandatory as soon as you
|
|
||||||
* develop commercial closed-source software that incorporates or links
|
|
||||||
* directly against ZeroTier software without disclosing the source code
|
|
||||||
* of your own application.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ZT_CLUSTERGEOIPSERVICE_HPP
|
|
||||||
#define ZT_CLUSTERGEOIPSERVICE_HPP
|
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_CLUSTER
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "../node/Constants.hpp"
|
|
||||||
#include "../node/Mutex.hpp"
|
|
||||||
#include "../node/NonCopyable.hpp"
|
|
||||||
#include "../node/InetAddress.hpp"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a GeoIP CSV into memory for fast lookup, reloading as needed
|
|
||||||
*
|
|
||||||
* This was designed around the CSV from https://db-ip.com but can be used
|
|
||||||
* with any similar GeoIP CSV database that is presented in the form of an
|
|
||||||
* IP range and lat/long coordinates.
|
|
||||||
*
|
|
||||||
* It loads the whole database into memory, which can be kind of large. If
|
|
||||||
* the CSV file changes, the changes are loaded automatically.
|
|
||||||
*/
|
|
||||||
class ClusterGeoIpService : NonCopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ClusterGeoIpService();
|
|
||||||
~ClusterGeoIpService();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load or reload CSV file
|
|
||||||
*
|
|
||||||
* CSV column indexes start at zero. CSVs can be quoted with single or
|
|
||||||
* double quotes. Whitespace before or after commas is ignored. Backslash
|
|
||||||
* may be used for escaping whitespace as well.
|
|
||||||
*
|
|
||||||
* @param pathToCsv Path to (uncompressed) CSV file
|
|
||||||
* @param ipStartColumn Column with IP range start
|
|
||||||
* @param ipEndColumn Column with IP range end (inclusive)
|
|
||||||
* @param latitudeColumn Column with latitude
|
|
||||||
* @param longitudeColumn Column with longitude
|
|
||||||
* @return Number of valid records loaded or -1 on error (invalid file, not found, etc.)
|
|
||||||
*/
|
|
||||||
inline long load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return _load(pathToCsv,ipStartColumn,ipEndColumn,latitudeColumn,longitudeColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to locate an IP
|
|
||||||
*
|
|
||||||
* This returns true if x, y, and z are set. If the return value is false
|
|
||||||
* the values of x, y, and z are undefined.
|
|
||||||
*
|
|
||||||
* @param ip IPv4 or IPv6 address
|
|
||||||
* @param x Reference to variable to receive X
|
|
||||||
* @param y Reference to variable to receive Y
|
|
||||||
* @param z Reference to variable to receive Z
|
|
||||||
* @return True if coordinates were set
|
|
||||||
*/
|
|
||||||
bool locate(const InetAddress &ip,int &x,int &y,int &z);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if IP database/service is available for queries (otherwise locate() will always be false)
|
|
||||||
*/
|
|
||||||
inline bool available() const
|
|
||||||
{
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
return ((_v4db.size() + _v6db.size()) > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct _V4E
|
|
||||||
{
|
|
||||||
uint32_t start;
|
|
||||||
uint32_t end;
|
|
||||||
float lat,lon;
|
|
||||||
int16_t x,y,z;
|
|
||||||
|
|
||||||
inline bool operator<(const _V4E &e) const { return (start < e.start); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _V6E
|
|
||||||
{
|
|
||||||
uint8_t start[16];
|
|
||||||
uint8_t end[16];
|
|
||||||
float lat,lon;
|
|
||||||
int16_t x,y,z;
|
|
||||||
|
|
||||||
inline bool operator<(const _V6E &e) const { return (memcmp(start,e.start,16) < 0); }
|
|
||||||
};
|
|
||||||
|
|
||||||
static void _parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn);
|
|
||||||
long _load(const char *pathToCsv,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn);
|
|
||||||
|
|
||||||
std::string _pathToCsv;
|
|
||||||
int _ipStartColumn;
|
|
||||||
int _ipEndColumn;
|
|
||||||
int _latitudeColumn;
|
|
||||||
int _longitudeColumn;
|
|
||||||
|
|
||||||
uint64_t _lastFileCheckTime;
|
|
||||||
uint64_t _csvModificationTime;
|
|
||||||
int64_t _csvFileSize;
|
|
||||||
|
|
||||||
std::vector<_V4E> _v4db;
|
|
||||||
std::vector<_V6E> _v6db;
|
|
||||||
|
|
||||||
Mutex _lock;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif // ZT_ENABLE_CLUSTER
|
|
||||||
|
|
||||||
#endif
|
|
101
attic/FCV.hpp
101
attic/FCV.hpp
@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*
|
|
||||||
* --
|
|
||||||
*
|
|
||||||
* You can be released from the requirements of the license by purchasing
|
|
||||||
* a commercial license. Buying such a license is mandatory as soon as you
|
|
||||||
* develop commercial closed-source software that incorporates or links
|
|
||||||
* directly against ZeroTier software without disclosing the source code
|
|
||||||
* of your own application.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Constants.hpp"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A really simple fixed capacity vector
|
|
||||||
*
|
|
||||||
* This class does no bounds checking, so the user must ensure that
|
|
||||||
* no more than C elements are ever added and that accesses are in
|
|
||||||
* bounds.
|
|
||||||
*
|
|
||||||
* @tparam T Type to contain
|
|
||||||
* @tparam C Capacity of vector
|
|
||||||
*/
|
|
||||||
template<typename T,unsigned long C>
|
|
||||||
class FCV
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FCV() : _s(0) {}
|
|
||||||
~FCV() { clear(); }
|
|
||||||
|
|
||||||
FCV(const FCV &v) :
|
|
||||||
_s(v._s)
|
|
||||||
{
|
|
||||||
for(unsigned long i=0;i<_s;++i) {
|
|
||||||
new (reinterpret_cast<T *>(_mem + (sizeof(T) * i))) T(reinterpret_cast<const T *>(v._mem)[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline FCV &operator=(const FCV &v)
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
_s = v._s;
|
|
||||||
for(unsigned long i=0;i<_s;++i) {
|
|
||||||
new (reinterpret_cast<T *>(_mem + (sizeof(T) * i))) T(reinterpret_cast<const T *>(v._mem)[i]);
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef T * iterator;
|
|
||||||
typedef const T * const_iterator;
|
|
||||||
typedef unsigned long size_type;
|
|
||||||
|
|
||||||
inline iterator begin() { return (T *)_mem; }
|
|
||||||
inline iterator end() { return (T *)(_mem + (sizeof(T) * _s)); }
|
|
||||||
inline iterator begin() const { return (const T *)_mem; }
|
|
||||||
inline iterator end() const { return (const T *)(_mem + (sizeof(T) * _s)); }
|
|
||||||
|
|
||||||
inline T &operator[](const size_type i) { return reinterpret_cast<T *>(_mem)[i]; }
|
|
||||||
inline const T &operator[](const size_type i) const { return reinterpret_cast<const T *>(_mem)[i]; }
|
|
||||||
|
|
||||||
inline T &front() { return reinterpret_cast<T *>(_mem)[0]; }
|
|
||||||
inline const T &front() const { return reinterpret_cast<const T *>(_mem)[0]; }
|
|
||||||
inline T &back() { return reinterpret_cast<T *>(_mem)[_s - 1]; }
|
|
||||||
inline const T &back() const { return reinterpret_cast<const T *>(_mem)[_s - 1]; }
|
|
||||||
|
|
||||||
inline void push_back(const T &v) { new (reinterpret_cast<T *>(_mem + (sizeof(T) * _s++))) T(v); }
|
|
||||||
inline void pop_back() { reinterpret_cast<T *>(_mem + (sizeof(T) * --_s))->~T(); }
|
|
||||||
|
|
||||||
inline size_type size() const { return _s; }
|
|
||||||
inline size_type capacity() const { return C; }
|
|
||||||
|
|
||||||
inline void clear()
|
|
||||||
{
|
|
||||||
for(unsigned long i=0;i<_s;++i)
|
|
||||||
reinterpret_cast<T *>(_mem + (sizeof(T) * i))->~T();
|
|
||||||
_s = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
char _mem[sizeof(T) * C];
|
|
||||||
unsigned long _s;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
@ -1,703 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*
|
|
||||||
* --
|
|
||||||
*
|
|
||||||
* You can be released from the requirements of the license by purchasing
|
|
||||||
* a commercial license. Buying such a license is mandatory as soon as you
|
|
||||||
* develop commercial closed-source software that incorporates or links
|
|
||||||
* directly against ZeroTier software without disclosing the source code
|
|
||||||
* of your own application.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <sys/cdefs.h>
|
|
||||||
#include <sys/uio.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <net/route.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#include <net/if_arp.h>
|
|
||||||
#include <net/if_dl.h>
|
|
||||||
#include <net/if_media.h>
|
|
||||||
#include <sys/sysctl.h>
|
|
||||||
#include <netinet6/in6_var.h>
|
|
||||||
#include <netinet/in_var.h>
|
|
||||||
#include <netinet/icmp6.h>
|
|
||||||
|
|
||||||
// OSX compile fix... in6_var defines this in a struct which namespaces it for C++ ... why?!?
|
|
||||||
struct prf_ra {
|
|
||||||
u_char onlink : 1;
|
|
||||||
u_char autonomous : 1;
|
|
||||||
u_char reserved : 6;
|
|
||||||
} prf_ra;
|
|
||||||
|
|
||||||
#include <netinet6/nd6.h>
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
|
|
||||||
// These are KERNEL_PRIVATE... why?
|
|
||||||
#ifndef SIOCAUTOCONF_START
|
|
||||||
#define SIOCAUTOCONF_START _IOWR('i', 132, struct in6_ifreq) /* accept rtadvd on this interface */
|
|
||||||
#endif
|
|
||||||
#ifndef SIOCAUTOCONF_STOP
|
|
||||||
#define SIOCAUTOCONF_STOP _IOWR('i', 133, struct in6_ifreq) /* stop accepting rtadv for this interface */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// This source is from:
|
|
||||||
// http://www.opensource.apple.com/source/Libinfo/Libinfo-406.17/gen.subproj/getifmaddrs.c?txt
|
|
||||||
// It's here because OSX 10.6 does not have this convenience function.
|
|
||||||
|
|
||||||
#define SALIGN (sizeof(uint32_t) - 1)
|
|
||||||
#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \
|
|
||||||
(SALIGN + 1))
|
|
||||||
#define MAX_SYSCTL_TRY 5
|
|
||||||
#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA)
|
|
||||||
|
|
||||||
/* FreeBSD uses NET_RT_IFMALIST and RTM_NEWMADDR from <sys/socket.h> */
|
|
||||||
/* We can use NET_RT_IFLIST2 and RTM_NEWMADDR2 on Darwin */
|
|
||||||
//#define DARWIN_COMPAT
|
|
||||||
|
|
||||||
//#ifdef DARWIN_COMPAT
|
|
||||||
#define GIM_SYSCTL_MIB NET_RT_IFLIST2
|
|
||||||
#define GIM_RTM_ADDR RTM_NEWMADDR2
|
|
||||||
//#else
|
|
||||||
//#define GIM_SYSCTL_MIB NET_RT_IFMALIST
|
|
||||||
//#define GIM_RTM_ADDR RTM_NEWMADDR
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
// Not in 10.6 includes so use our own
|
|
||||||
struct _intl_ifmaddrs {
|
|
||||||
struct _intl_ifmaddrs *ifma_next;
|
|
||||||
struct sockaddr *ifma_name;
|
|
||||||
struct sockaddr *ifma_addr;
|
|
||||||
struct sockaddr *ifma_lladdr;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline int _intl_getifmaddrs(struct _intl_ifmaddrs **pif)
|
|
||||||
{
|
|
||||||
int icnt = 1;
|
|
||||||
int dcnt = 0;
|
|
||||||
int ntry = 0;
|
|
||||||
size_t len;
|
|
||||||
size_t needed;
|
|
||||||
int mib[6];
|
|
||||||
int i;
|
|
||||||
char *buf;
|
|
||||||
char *data;
|
|
||||||
char *next;
|
|
||||||
char *p;
|
|
||||||
struct ifma_msghdr2 *ifmam;
|
|
||||||
struct _intl_ifmaddrs *ifa, *ift;
|
|
||||||
struct rt_msghdr *rtm;
|
|
||||||
struct sockaddr *sa;
|
|
||||||
|
|
||||||
mib[0] = CTL_NET;
|
|
||||||
mib[1] = PF_ROUTE;
|
|
||||||
mib[2] = 0; /* protocol */
|
|
||||||
mib[3] = 0; /* wildcard address family */
|
|
||||||
mib[4] = GIM_SYSCTL_MIB;
|
|
||||||
mib[5] = 0; /* no flags */
|
|
||||||
do {
|
|
||||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
|
|
||||||
return (-1);
|
|
||||||
if ((buf = (char *)malloc(needed)) == NULL)
|
|
||||||
return (-1);
|
|
||||||
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
|
|
||||||
if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
|
|
||||||
free(buf);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
free(buf);
|
|
||||||
buf = NULL;
|
|
||||||
}
|
|
||||||
} while (buf == NULL);
|
|
||||||
|
|
||||||
for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
|
|
||||||
rtm = (struct rt_msghdr *)(void *)next;
|
|
||||||
if (rtm->rtm_version != RTM_VERSION)
|
|
||||||
continue;
|
|
||||||
switch (rtm->rtm_type) {
|
|
||||||
case GIM_RTM_ADDR:
|
|
||||||
ifmam = (struct ifma_msghdr2 *)(void *)rtm;
|
|
||||||
if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
|
|
||||||
break;
|
|
||||||
icnt++;
|
|
||||||
p = (char *)(ifmam + 1);
|
|
||||||
for (i = 0; i < RTAX_MAX; i++) {
|
|
||||||
if ((RTA_MASKS & ifmam->ifmam_addrs &
|
|
||||||
(1 << i)) == 0)
|
|
||||||
continue;
|
|
||||||
sa = (struct sockaddr *)(void *)p;
|
|
||||||
len = SA_RLEN(sa);
|
|
||||||
dcnt += len;
|
|
||||||
p += len;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data = (char *)malloc(sizeof(struct _intl_ifmaddrs) * icnt + dcnt);
|
|
||||||
if (data == NULL) {
|
|
||||||
free(buf);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ifa = (struct _intl_ifmaddrs *)(void *)data;
|
|
||||||
data += sizeof(struct _intl_ifmaddrs) * icnt;
|
|
||||||
|
|
||||||
memset(ifa, 0, sizeof(struct _intl_ifmaddrs) * icnt);
|
|
||||||
ift = ifa;
|
|
||||||
|
|
||||||
for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
|
|
||||||
rtm = (struct rt_msghdr *)(void *)next;
|
|
||||||
if (rtm->rtm_version != RTM_VERSION)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (rtm->rtm_type) {
|
|
||||||
case GIM_RTM_ADDR:
|
|
||||||
ifmam = (struct ifma_msghdr2 *)(void *)rtm;
|
|
||||||
if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
p = (char *)(ifmam + 1);
|
|
||||||
for (i = 0; i < RTAX_MAX; i++) {
|
|
||||||
if ((RTA_MASKS & ifmam->ifmam_addrs &
|
|
||||||
(1 << i)) == 0)
|
|
||||||
continue;
|
|
||||||
sa = (struct sockaddr *)(void *)p;
|
|
||||||
len = SA_RLEN(sa);
|
|
||||||
switch (i) {
|
|
||||||
case RTAX_GATEWAY:
|
|
||||||
ift->ifma_lladdr =
|
|
||||||
(struct sockaddr *)(void *)data;
|
|
||||||
memcpy(data, p, len);
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RTAX_IFP:
|
|
||||||
ift->ifma_name =
|
|
||||||
(struct sockaddr *)(void *)data;
|
|
||||||
memcpy(data, p, len);
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RTAX_IFA:
|
|
||||||
ift->ifma_addr =
|
|
||||||
(struct sockaddr *)(void *)data;
|
|
||||||
memcpy(data, p, len);
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p += len;
|
|
||||||
}
|
|
||||||
ift->ifma_next = ift + 1;
|
|
||||||
ift = ift->ifma_next;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
|
|
||||||
if (ift > ifa) {
|
|
||||||
ift--;
|
|
||||||
ift->ifma_next = NULL;
|
|
||||||
*pif = ifa;
|
|
||||||
} else {
|
|
||||||
*pif = NULL;
|
|
||||||
free(ifa);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _intl_freeifmaddrs(struct _intl_ifmaddrs *ifmp)
|
|
||||||
{
|
|
||||||
free(ifmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "../node/Constants.hpp"
|
|
||||||
#include "../node/Utils.hpp"
|
|
||||||
#include "../node/Mutex.hpp"
|
|
||||||
#include "../node/Dictionary.hpp"
|
|
||||||
#include "OSUtils.hpp"
|
|
||||||
#include "OSXEthernetTap.hpp"
|
|
||||||
|
|
||||||
// ff:ff:ff:ff:ff:ff with no ADI
|
|
||||||
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
|
|
||||||
|
|
||||||
static inline bool _setIpv6Stuff(const char *ifname,bool performNUD,bool acceptRouterAdverts)
|
|
||||||
{
|
|
||||||
struct in6_ndireq nd;
|
|
||||||
struct in6_ifreq ifr;
|
|
||||||
|
|
||||||
int s = socket(AF_INET6,SOCK_DGRAM,0);
|
|
||||||
if (s <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
memset(&nd,0,sizeof(nd));
|
|
||||||
strncpy(nd.ifname,ifname,sizeof(nd.ifname));
|
|
||||||
|
|
||||||
if (ioctl(s,SIOCGIFINFO_IN6,&nd)) {
|
|
||||||
close(s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long oldFlags = (unsigned long)nd.ndi.flags;
|
|
||||||
|
|
||||||
if (performNUD)
|
|
||||||
nd.ndi.flags |= ND6_IFF_PERFORMNUD;
|
|
||||||
else nd.ndi.flags &= ~ND6_IFF_PERFORMNUD;
|
|
||||||
|
|
||||||
if (oldFlags != (unsigned long)nd.ndi.flags) {
|
|
||||||
if (ioctl(s,SIOCSIFINFO_FLAGS,&nd)) {
|
|
||||||
close(s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&ifr,0,sizeof(ifr));
|
|
||||||
strncpy(ifr.ifr_name,ifname,sizeof(ifr.ifr_name));
|
|
||||||
if (ioctl(s,acceptRouterAdverts ? SIOCAUTOCONF_START : SIOCAUTOCONF_STOP,&ifr)) {
|
|
||||||
close(s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
static long globalTapsRunning = 0;
|
|
||||||
static Mutex globalTapCreateLock;
|
|
||||||
|
|
||||||
OSXEthernetTap::OSXEthernetTap(
|
|
||||||
const char *homePath,
|
|
||||||
const MAC &mac,
|
|
||||||
unsigned int mtu,
|
|
||||||
unsigned int metric,
|
|
||||||
uint64_t nwid,
|
|
||||||
const char *friendlyName,
|
|
||||||
void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len),
|
|
||||||
void *arg) :
|
|
||||||
_handler(handler),
|
|
||||||
_arg(arg),
|
|
||||||
_nwid(nwid),
|
|
||||||
_homePath(homePath),
|
|
||||||
_mtu(mtu),
|
|
||||||
_metric(metric),
|
|
||||||
_fd(0),
|
|
||||||
_enabled(true)
|
|
||||||
{
|
|
||||||
char devpath[64],ethaddr[64],mtustr[32],metstr[32],nwids[32];
|
|
||||||
struct stat stattmp;
|
|
||||||
|
|
||||||
OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid);
|
|
||||||
|
|
||||||
Mutex::Lock _gl(globalTapCreateLock);
|
|
||||||
|
|
||||||
if (::stat("/dev/zt0",&stattmp)) {
|
|
||||||
long kextpid = (long)vfork();
|
|
||||||
if (kextpid == 0) {
|
|
||||||
::chdir(homePath);
|
|
||||||
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
|
|
||||||
::execl("/sbin/kextload","/sbin/kextload","-q","-repository",homePath,"tap.kext",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (kextpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(kextpid,&exitcode,0);
|
|
||||||
}
|
|
||||||
::usleep(500); // give tap device driver time to start up and try again
|
|
||||||
if (::stat("/dev/zt0",&stattmp))
|
|
||||||
throw std::runtime_error("/dev/zt# tap devices do not exist and cannot load tap.kext");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to reopen the last device we had, if we had one and it's still unused.
|
|
||||||
std::map<std::string,std::string> globalDeviceMap;
|
|
||||||
FILE *devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"r");
|
|
||||||
if (devmapf) {
|
|
||||||
char buf[256];
|
|
||||||
while (fgets(buf,sizeof(buf),devmapf)) {
|
|
||||||
char *x = (char *)0;
|
|
||||||
char *y = (char *)0;
|
|
||||||
char *saveptr = (char *)0;
|
|
||||||
for(char *f=Utils::stok(buf,"\r\n=",&saveptr);(f);f=Utils::stok((char *)0,"\r\n=",&saveptr)) {
|
|
||||||
if (!x) x = f;
|
|
||||||
else if (!y) y = f;
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
if ((x)&&(y)&&(x[0])&&(y[0]))
|
|
||||||
globalDeviceMap[x] = y;
|
|
||||||
}
|
|
||||||
fclose(devmapf);
|
|
||||||
}
|
|
||||||
bool recalledDevice = false;
|
|
||||||
std::map<std::string,std::string>::const_iterator gdmEntry = globalDeviceMap.find(nwids);
|
|
||||||
if (gdmEntry != globalDeviceMap.end()) {
|
|
||||||
std::string devpath("/dev/"); devpath.append(gdmEntry->second);
|
|
||||||
if (stat(devpath.c_str(),&stattmp) == 0) {
|
|
||||||
_fd = ::open(devpath.c_str(),O_RDWR);
|
|
||||||
if (_fd > 0) {
|
|
||||||
_dev = gdmEntry->second;
|
|
||||||
recalledDevice = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the first unused tap device if we didn't recall a previous one.
|
|
||||||
if (!recalledDevice) {
|
|
||||||
for(int i=0;i<64;++i) {
|
|
||||||
OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/zt%d",i);
|
|
||||||
if (stat(devpath,&stattmp))
|
|
||||||
throw std::runtime_error("no more TAP devices available");
|
|
||||||
_fd = ::open(devpath,O_RDWR);
|
|
||||||
if (_fd > 0) {
|
|
||||||
char foo[16];
|
|
||||||
OSUtils::ztsnprintf(foo,sizeof(foo),"zt%d",i);
|
|
||||||
_dev = foo;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_fd <= 0)
|
|
||||||
throw std::runtime_error("unable to open TAP device or no more devices available");
|
|
||||||
|
|
||||||
if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) {
|
|
||||||
::close(_fd);
|
|
||||||
throw std::runtime_error("unable to set flags on file descriptor for TAP device");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure MAC address and MTU, bring interface up
|
|
||||||
OSUtils::ztsnprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
|
|
||||||
OSUtils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu);
|
|
||||||
OSUtils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric);
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
if (exitcode) {
|
|
||||||
::close(_fd);
|
|
||||||
throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_setIpv6Stuff(_dev.c_str(),true,false);
|
|
||||||
|
|
||||||
// Set close-on-exec so that devices cannot persist if we fork/exec for update
|
|
||||||
fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC);
|
|
||||||
|
|
||||||
::pipe(_shutdownSignalPipe);
|
|
||||||
|
|
||||||
++globalTapsRunning;
|
|
||||||
|
|
||||||
globalDeviceMap[nwids] = _dev;
|
|
||||||
devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"w");
|
|
||||||
if (devmapf) {
|
|
||||||
gdmEntry = globalDeviceMap.begin();
|
|
||||||
while (gdmEntry != globalDeviceMap.end()) {
|
|
||||||
fprintf(devmapf,"%s=%s\n",gdmEntry->first.c_str(),gdmEntry->second.c_str());
|
|
||||||
++gdmEntry;
|
|
||||||
}
|
|
||||||
fclose(devmapf);
|
|
||||||
}
|
|
||||||
|
|
||||||
_thread = Thread::start(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
OSXEthernetTap::~OSXEthernetTap()
|
|
||||||
{
|
|
||||||
::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
|
|
||||||
Thread::join(_thread);
|
|
||||||
|
|
||||||
::close(_fd);
|
|
||||||
::close(_shutdownSignalPipe[0]);
|
|
||||||
::close(_shutdownSignalPipe[1]);
|
|
||||||
|
|
||||||
{
|
|
||||||
Mutex::Lock _gl(globalTapCreateLock);
|
|
||||||
if (--globalTapsRunning <= 0) {
|
|
||||||
globalTapsRunning = 0; // sanity check -- should not be possible
|
|
||||||
|
|
||||||
char tmp[16384];
|
|
||||||
sprintf(tmp,"%s/%s",_homePath.c_str(),"tap.kext");
|
|
||||||
long kextpid = (long)vfork();
|
|
||||||
if (kextpid == 0) {
|
|
||||||
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
|
|
||||||
::execl("/sbin/kextunload","/sbin/kextunload",tmp,(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (kextpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(kextpid,&exitcode,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::setEnabled(bool en)
|
|
||||||
{
|
|
||||||
_enabled = en;
|
|
||||||
// TODO: interface status change
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OSXEthernetTap::enabled() const
|
|
||||||
{
|
|
||||||
return _enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OSXEthernetTap::addIp(const InetAddress &ip)
|
|
||||||
{
|
|
||||||
if (!ip)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
char tmp[128];
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString(tmp),"alias",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
return (exitcode == 0);
|
|
||||||
} // else return false...
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OSXEthernetTap::removeIp(const InetAddress &ip)
|
|
||||||
{
|
|
||||||
if (!ip)
|
|
||||||
return true;
|
|
||||||
std::vector<InetAddress> allIps(ips());
|
|
||||||
for(std::vector<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) {
|
|
||||||
if (*i == ip) {
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
char tmp[128];
|
|
||||||
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString(tmp),"-alias",(const char *)0);
|
|
||||||
_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
waitpid(cpid,&exitcode,0);
|
|
||||||
return (exitcode == 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<InetAddress> OSXEthernetTap::ips() const
|
|
||||||
{
|
|
||||||
struct ifaddrs *ifa = (struct ifaddrs *)0;
|
|
||||||
if (getifaddrs(&ifa))
|
|
||||||
return std::vector<InetAddress>();
|
|
||||||
|
|
||||||
std::vector<InetAddress> r;
|
|
||||||
|
|
||||||
struct ifaddrs *p = ifa;
|
|
||||||
while (p) {
|
|
||||||
if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) {
|
|
||||||
switch(p->ifa_addr->sa_family) {
|
|
||||||
case AF_INET: {
|
|
||||||
struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr;
|
|
||||||
struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask;
|
|
||||||
r.push_back(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr)));
|
|
||||||
} break;
|
|
||||||
case AF_INET6: {
|
|
||||||
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr;
|
|
||||||
struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask;
|
|
||||||
uint32_t b[4];
|
|
||||||
memcpy(b,nm->sin6_addr.s6_addr,sizeof(b));
|
|
||||||
r.push_back(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3])));
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p = p->ifa_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifa)
|
|
||||||
freeifaddrs(ifa);
|
|
||||||
|
|
||||||
std::sort(r.begin(),r.end());
|
|
||||||
r.erase(std::unique(r.begin(),r.end()),r.end());
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
|
|
||||||
{
|
|
||||||
char putBuf[ZT_MAX_MTU + 64];
|
|
||||||
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
|
|
||||||
to.copyTo(putBuf,6);
|
|
||||||
from.copyTo(putBuf + 6,6);
|
|
||||||
*((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType);
|
|
||||||
memcpy(putBuf + 14,data,len);
|
|
||||||
len += 14;
|
|
||||||
::write(_fd,putBuf,len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string OSXEthernetTap::deviceName() const
|
|
||||||
{
|
|
||||||
return _dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::setFriendlyName(const char *friendlyName)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
|
|
||||||
{
|
|
||||||
std::vector<MulticastGroup> newGroups;
|
|
||||||
|
|
||||||
struct _intl_ifmaddrs *ifmap = (struct _intl_ifmaddrs *)0;
|
|
||||||
if (!_intl_getifmaddrs(&ifmap)) {
|
|
||||||
struct _intl_ifmaddrs *p = ifmap;
|
|
||||||
while (p) {
|
|
||||||
if (p->ifma_addr->sa_family == AF_LINK) {
|
|
||||||
struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
|
|
||||||
struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
|
|
||||||
if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
|
|
||||||
newGroups.push_back(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0));
|
|
||||||
}
|
|
||||||
p = p->ifma_next;
|
|
||||||
}
|
|
||||||
_intl_freeifmaddrs(ifmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<InetAddress> allIps(ips());
|
|
||||||
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
|
|
||||||
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
|
|
||||||
|
|
||||||
std::sort(newGroups.begin(),newGroups.end());
|
|
||||||
std::unique(newGroups.begin(),newGroups.end());
|
|
||||||
|
|
||||||
for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
|
|
||||||
if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))
|
|
||||||
added.push_back(*m);
|
|
||||||
}
|
|
||||||
for(std::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) {
|
|
||||||
if (!std::binary_search(newGroups.begin(),newGroups.end(),*m))
|
|
||||||
removed.push_back(*m);
|
|
||||||
}
|
|
||||||
|
|
||||||
_multicastGroups.swap(newGroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::setMtu(unsigned int mtu)
|
|
||||||
{
|
|
||||||
if (mtu != _mtu) {
|
|
||||||
_mtu = mtu;
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
char tmp[64];
|
|
||||||
OSUtils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu);
|
|
||||||
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0);
|
|
||||||
_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
waitpid(cpid,&exitcode,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::threadMain()
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
fd_set readfds,nullfds;
|
|
||||||
MAC to,from;
|
|
||||||
int n,nfds,r;
|
|
||||||
char getBuf[ZT_MAX_MTU + 64];
|
|
||||||
|
|
||||||
Thread::sleep(500);
|
|
||||||
|
|
||||||
FD_ZERO(&readfds);
|
|
||||||
FD_ZERO(&nullfds);
|
|
||||||
nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
|
|
||||||
|
|
||||||
r = 0;
|
|
||||||
for(;;) {
|
|
||||||
FD_SET(_shutdownSignalPipe[0],&readfds);
|
|
||||||
FD_SET(_fd,&readfds);
|
|
||||||
select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0);
|
|
||||||
|
|
||||||
if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (FD_ISSET(_fd,&readfds)) {
|
|
||||||
n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r);
|
|
||||||
if (n < 0) {
|
|
||||||
if ((errno != EINTR)&&(errno != ETIMEDOUT))
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
// Some tap drivers like to send the ethernet frame and the
|
|
||||||
// payload in two chunks, so handle that by accumulating
|
|
||||||
// data until we have at least a frame.
|
|
||||||
r += n;
|
|
||||||
if (r > 14) {
|
|
||||||
if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms
|
|
||||||
r = _mtu + 14;
|
|
||||||
|
|
||||||
if (_enabled) {
|
|
||||||
to.setTo(getBuf,6);
|
|
||||||
from.setTo(getBuf + 6,6);
|
|
||||||
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
|
|
||||||
// TODO: VLAN support
|
|
||||||
_handler(_arg,(void *)0,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
@ -1,650 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
|
||||||
*
|
|
||||||
* 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 <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <sys/cdefs.h>
|
|
||||||
#include <sys/uio.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <net/route.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#include <net/if_arp.h>
|
|
||||||
#include <net/if_dl.h>
|
|
||||||
#include <net/if_media.h>
|
|
||||||
#include <netinet6/in6_var.h>
|
|
||||||
#include <netinet/in_var.h>
|
|
||||||
#include <netinet/icmp6.h>
|
|
||||||
|
|
||||||
#include <pcap/pcap.h>
|
|
||||||
|
|
||||||
// OSX compile fix... in6_var defines this in a struct which namespaces it for C++ ... why?!?
|
|
||||||
struct prf_ra {
|
|
||||||
u_char onlink : 1;
|
|
||||||
u_char autonomous : 1;
|
|
||||||
u_char reserved : 6;
|
|
||||||
} prf_ra;
|
|
||||||
|
|
||||||
#include <netinet6/nd6.h>
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
|
|
||||||
// These are KERNEL_PRIVATE... why?
|
|
||||||
#ifndef SIOCAUTOCONF_START
|
|
||||||
#define SIOCAUTOCONF_START _IOWR('i', 132, struct in6_ifreq) /* accept rtadvd on this interface */
|
|
||||||
#endif
|
|
||||||
#ifndef SIOCAUTOCONF_STOP
|
|
||||||
#define SIOCAUTOCONF_STOP _IOWR('i', 133, struct in6_ifreq) /* stop accepting rtadv for this interface */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ETH_ALEN
|
|
||||||
#define ETH_ALEN 6
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// This source is from:
|
|
||||||
// http://www.opensource.apple.com/source/Libinfo/Libinfo-406.17/gen.subproj/getifmaddrs.c?txt
|
|
||||||
// It's here because OSX 10.6 does not have this convenience function.
|
|
||||||
|
|
||||||
#define SALIGN (sizeof(uint32_t) - 1)
|
|
||||||
#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \
|
|
||||||
(SALIGN + 1))
|
|
||||||
#define MAX_SYSCTL_TRY 5
|
|
||||||
#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA)
|
|
||||||
|
|
||||||
/* FreeBSD uses NET_RT_IFMALIST and RTM_NEWMADDR from <sys/socket.h> */
|
|
||||||
/* We can use NET_RT_IFLIST2 and RTM_NEWMADDR2 on Darwin */
|
|
||||||
//#define DARWIN_COMPAT
|
|
||||||
|
|
||||||
//#ifdef DARWIN_COMPAT
|
|
||||||
#define GIM_SYSCTL_MIB NET_RT_IFLIST2
|
|
||||||
#define GIM_RTM_ADDR RTM_NEWMADDR2
|
|
||||||
//#else
|
|
||||||
//#define GIM_SYSCTL_MIB NET_RT_IFMALIST
|
|
||||||
//#define GIM_RTM_ADDR RTM_NEWMADDR
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
// Not in 10.6 includes so use our own
|
|
||||||
struct _intl_ifmaddrs {
|
|
||||||
struct _intl_ifmaddrs *ifma_next;
|
|
||||||
struct sockaddr *ifma_name;
|
|
||||||
struct sockaddr *ifma_addr;
|
|
||||||
struct sockaddr *ifma_lladdr;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline int _intl_getifmaddrs(struct _intl_ifmaddrs **pif)
|
|
||||||
{
|
|
||||||
int icnt = 1;
|
|
||||||
int dcnt = 0;
|
|
||||||
int ntry = 0;
|
|
||||||
size_t len;
|
|
||||||
size_t needed;
|
|
||||||
int mib[6];
|
|
||||||
int i;
|
|
||||||
char *buf;
|
|
||||||
char *data;
|
|
||||||
char *next;
|
|
||||||
char *p;
|
|
||||||
struct ifma_msghdr2 *ifmam;
|
|
||||||
struct _intl_ifmaddrs *ifa, *ift;
|
|
||||||
struct rt_msghdr *rtm;
|
|
||||||
struct sockaddr *sa;
|
|
||||||
|
|
||||||
mib[0] = CTL_NET;
|
|
||||||
mib[1] = PF_ROUTE;
|
|
||||||
mib[2] = 0; /* protocol */
|
|
||||||
mib[3] = 0; /* wildcard address family */
|
|
||||||
mib[4] = GIM_SYSCTL_MIB;
|
|
||||||
mib[5] = 0; /* no flags */
|
|
||||||
do {
|
|
||||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
|
|
||||||
return (-1);
|
|
||||||
if ((buf = (char *)malloc(needed)) == NULL)
|
|
||||||
return (-1);
|
|
||||||
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
|
|
||||||
if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
|
|
||||||
free(buf);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
free(buf);
|
|
||||||
buf = NULL;
|
|
||||||
}
|
|
||||||
} while (buf == NULL);
|
|
||||||
|
|
||||||
for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
|
|
||||||
rtm = (struct rt_msghdr *)(void *)next;
|
|
||||||
if (rtm->rtm_version != RTM_VERSION)
|
|
||||||
continue;
|
|
||||||
switch (rtm->rtm_type) {
|
|
||||||
case GIM_RTM_ADDR:
|
|
||||||
ifmam = (struct ifma_msghdr2 *)(void *)rtm;
|
|
||||||
if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
|
|
||||||
break;
|
|
||||||
icnt++;
|
|
||||||
p = (char *)(ifmam + 1);
|
|
||||||
for (i = 0; i < RTAX_MAX; i++) {
|
|
||||||
if ((RTA_MASKS & ifmam->ifmam_addrs &
|
|
||||||
(1 << i)) == 0)
|
|
||||||
continue;
|
|
||||||
sa = (struct sockaddr *)(void *)p;
|
|
||||||
len = SA_RLEN(sa);
|
|
||||||
dcnt += len;
|
|
||||||
p += len;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data = (char *)malloc(sizeof(struct _intl_ifmaddrs) * icnt + dcnt);
|
|
||||||
if (data == NULL) {
|
|
||||||
free(buf);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ifa = (struct _intl_ifmaddrs *)(void *)data;
|
|
||||||
data += sizeof(struct _intl_ifmaddrs) * icnt;
|
|
||||||
|
|
||||||
memset(ifa, 0, sizeof(struct _intl_ifmaddrs) * icnt);
|
|
||||||
ift = ifa;
|
|
||||||
|
|
||||||
for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
|
|
||||||
rtm = (struct rt_msghdr *)(void *)next;
|
|
||||||
if (rtm->rtm_version != RTM_VERSION)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (rtm->rtm_type) {
|
|
||||||
case GIM_RTM_ADDR:
|
|
||||||
ifmam = (struct ifma_msghdr2 *)(void *)rtm;
|
|
||||||
if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
p = (char *)(ifmam + 1);
|
|
||||||
for (i = 0; i < RTAX_MAX; i++) {
|
|
||||||
if ((RTA_MASKS & ifmam->ifmam_addrs &
|
|
||||||
(1 << i)) == 0)
|
|
||||||
continue;
|
|
||||||
sa = (struct sockaddr *)(void *)p;
|
|
||||||
len = SA_RLEN(sa);
|
|
||||||
switch (i) {
|
|
||||||
case RTAX_GATEWAY:
|
|
||||||
ift->ifma_lladdr =
|
|
||||||
(struct sockaddr *)(void *)data;
|
|
||||||
memcpy(data, p, len);
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RTAX_IFP:
|
|
||||||
ift->ifma_name =
|
|
||||||
(struct sockaddr *)(void *)data;
|
|
||||||
memcpy(data, p, len);
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RTAX_IFA:
|
|
||||||
ift->ifma_addr =
|
|
||||||
(struct sockaddr *)(void *)data;
|
|
||||||
memcpy(data, p, len);
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p += len;
|
|
||||||
}
|
|
||||||
ift->ifma_next = ift + 1;
|
|
||||||
ift = ift->ifma_next;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
|
|
||||||
if (ift > ifa) {
|
|
||||||
ift--;
|
|
||||||
ift->ifma_next = NULL;
|
|
||||||
*pif = ifa;
|
|
||||||
} else {
|
|
||||||
*pif = NULL;
|
|
||||||
free(ifa);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _intl_freeifmaddrs(struct _intl_ifmaddrs *ifmp)
|
|
||||||
{
|
|
||||||
free(ifmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "../node/Constants.hpp"
|
|
||||||
#include "../node/Utils.hpp"
|
|
||||||
#include "../node/Mutex.hpp"
|
|
||||||
#include "../node/Dictionary.hpp"
|
|
||||||
#include "OSUtils.hpp"
|
|
||||||
#include "OSXEthernetTap.hpp"
|
|
||||||
|
|
||||||
// ff:ff:ff:ff:ff:ff with no ADI
|
|
||||||
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
|
|
||||||
|
|
||||||
static inline bool _setIpv6Stuff(const char *ifname,bool performNUD,bool acceptRouterAdverts)
|
|
||||||
{
|
|
||||||
struct in6_ndireq nd;
|
|
||||||
struct in6_ifreq ifr;
|
|
||||||
|
|
||||||
int s = socket(AF_INET6,SOCK_DGRAM,0);
|
|
||||||
if (s <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
memset(&nd,0,sizeof(nd));
|
|
||||||
strncpy(nd.ifname,ifname,sizeof(nd.ifname));
|
|
||||||
|
|
||||||
if (ioctl(s,SIOCGIFINFO_IN6,&nd)) {
|
|
||||||
close(s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long oldFlags = (unsigned long)nd.ndi.flags;
|
|
||||||
|
|
||||||
if (performNUD)
|
|
||||||
nd.ndi.flags |= ND6_IFF_PERFORMNUD;
|
|
||||||
else nd.ndi.flags &= ~ND6_IFF_PERFORMNUD;
|
|
||||||
|
|
||||||
if (oldFlags != (unsigned long)nd.ndi.flags) {
|
|
||||||
if (ioctl(s,SIOCSIFINFO_FLAGS,&nd)) {
|
|
||||||
close(s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&ifr,0,sizeof(ifr));
|
|
||||||
strncpy(ifr.ifr_name,ifname,sizeof(ifr.ifr_name));
|
|
||||||
if (ioctl(s,acceptRouterAdverts ? SIOCAUTOCONF_START : SIOCAUTOCONF_STOP,&ifr)) {
|
|
||||||
close(s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
static std::set<std::string> globalDeviceNames;
|
|
||||||
static Mutex globalTapCreateLock;
|
|
||||||
|
|
||||||
OSXEthernetTap::OSXEthernetTap(
|
|
||||||
const char *homePath,
|
|
||||||
const MAC &mac,
|
|
||||||
unsigned int mtu,
|
|
||||||
unsigned int metric,
|
|
||||||
uint64_t nwid,
|
|
||||||
const char *friendlyName,
|
|
||||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len),
|
|
||||||
void *arg) :
|
|
||||||
_handler(handler),
|
|
||||||
_arg(arg),
|
|
||||||
_pcap((void *)0),
|
|
||||||
_nwid(nwid),
|
|
||||||
_mac(mac),
|
|
||||||
_homePath(homePath),
|
|
||||||
_mtu(mtu),
|
|
||||||
_metric(metric),
|
|
||||||
_enabled(true)
|
|
||||||
{
|
|
||||||
char errbuf[PCAP_ERRBUF_SIZE];
|
|
||||||
char devname[64],ethaddr[64],mtustr[32],metstr[32],nwids[32];
|
|
||||||
|
|
||||||
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
|
|
||||||
|
|
||||||
if (mtu > 2800)
|
|
||||||
throw std::runtime_error("max tap MTU is 2800");
|
|
||||||
|
|
||||||
Mutex::Lock _gl(globalTapCreateLock);
|
|
||||||
|
|
||||||
std::string desiredDevice;
|
|
||||||
Dictionary devmap;
|
|
||||||
{
|
|
||||||
std::string devmapbuf;
|
|
||||||
if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) {
|
|
||||||
devmap.fromString(devmapbuf);
|
|
||||||
desiredDevice = devmap.get(nwids,"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((desiredDevice.length() >= 9)&&(desiredDevice.substr(0,6) == "bridge")) {
|
|
||||||
// length() >= 9 matches bridge### or bridge####
|
|
||||||
_dev = desiredDevice;
|
|
||||||
} else {
|
|
||||||
if (globalDeviceNames.size() >= (10000 - 128)) // sanity check... this would be nuts
|
|
||||||
throw std::runtime_error("too many devices!");
|
|
||||||
unsigned int pseudoBridgeNo = (unsigned int)((nwid ^ (nwid >> 32)) % (10000 - 128)) + 128; // range: bridge128 to bridge9999
|
|
||||||
sprintf(devname,"bridge%u",pseudoBridgeNo);
|
|
||||||
while (globalDeviceNames.count(std::string(devname)) > 0) {
|
|
||||||
++pseudoBridgeNo;
|
|
||||||
if (pseudoBridgeNo > 9999)
|
|
||||||
pseudoBridgeNo = 64;
|
|
||||||
sprintf(devname,"bridge%u",pseudoBridgeNo);
|
|
||||||
}
|
|
||||||
_dev = devname;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure MAC address and MTU, bring interface up
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"create",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
if (exitcode != 0)
|
|
||||||
throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
|
|
||||||
} else throw std::runtime_error("unable to fork()");
|
|
||||||
Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
|
|
||||||
Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
|
|
||||||
Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
|
|
||||||
cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
if (exitcode != 0)
|
|
||||||
throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
|
|
||||||
} else throw std::runtime_error("unable to fork()");
|
|
||||||
|
|
||||||
_setIpv6Stuff(_dev.c_str(),true,false);
|
|
||||||
|
|
||||||
_pcap = (void *)pcap_create(_dev.c_str(),errbuf);
|
|
||||||
if (!_pcap) {
|
|
||||||
cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"destroy",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
}
|
|
||||||
throw std::runtime_error((std::string("pcap_create() on new bridge device failed: ") + errbuf).c_str());
|
|
||||||
}
|
|
||||||
pcap_set_promisc(reinterpret_cast<pcap_t *>(_pcap),1);
|
|
||||||
pcap_set_timeout(reinterpret_cast<pcap_t *>(_pcap),120000);
|
|
||||||
pcap_set_immediate_mode(reinterpret_cast<pcap_t *>(_pcap),1);
|
|
||||||
if (pcap_set_buffer_size(reinterpret_cast<pcap_t *>(_pcap),1024 * 1024 * 16) != 0) // 16MB
|
|
||||||
fprintf(stderr,"WARNING: pcap_set_buffer_size() failed!\n");
|
|
||||||
if (pcap_set_snaplen(reinterpret_cast<pcap_t *>(_pcap),4096) != 0)
|
|
||||||
fprintf(stderr,"WARNING: pcap_set_snaplen() failed!\n");
|
|
||||||
if (pcap_activate(reinterpret_cast<pcap_t *>(_pcap)) != 0) {
|
|
||||||
pcap_close(reinterpret_cast<pcap_t *>(_pcap));
|
|
||||||
cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"destroy",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
}
|
|
||||||
throw std::runtime_error("pcap_activate() on new bridge device failed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
globalDeviceNames.insert(_dev);
|
|
||||||
|
|
||||||
devmap[nwids] = _dev;
|
|
||||||
OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmap.toString());
|
|
||||||
|
|
||||||
_thread = Thread::start(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
OSXEthernetTap::~OSXEthernetTap()
|
|
||||||
{
|
|
||||||
_enabled = false;
|
|
||||||
|
|
||||||
Mutex::Lock _gl(globalTapCreateLock);
|
|
||||||
globalDeviceNames.erase(_dev);
|
|
||||||
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"destroy",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
if (exitcode == 0) {
|
|
||||||
// Destroying the interface nukes pcap and terminates the thread.
|
|
||||||
Thread::join(_thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pcap_close(reinterpret_cast<pcap_t *>(_pcap));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
|
|
||||||
{
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
|
|
||||||
_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
waitpid(cpid,&exitcode,0);
|
|
||||||
return (exitcode == 0);
|
|
||||||
}
|
|
||||||
return false; // never reached, make compiler shut up about return value
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OSXEthernetTap::addIp(const InetAddress &ip)
|
|
||||||
{
|
|
||||||
if (!ip)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::vector<InetAddress> allIps(ips());
|
|
||||||
if (std::binary_search(allIps.begin(),allIps.end(),ip))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Remove and reconfigure if address is the same but netmask is different
|
|
||||||
for(std::vector<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) {
|
|
||||||
if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) {
|
|
||||||
if (___removeIp(_dev,*i))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
return (exitcode == 0);
|
|
||||||
} // else return false...
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OSXEthernetTap::removeIp(const InetAddress &ip)
|
|
||||||
{
|
|
||||||
if (!ip)
|
|
||||||
return true;
|
|
||||||
std::vector<InetAddress> allIps(ips());
|
|
||||||
if (!std::binary_search(allIps.begin(),allIps.end(),ip)) {
|
|
||||||
if (___removeIp(_dev,ip))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<InetAddress> OSXEthernetTap::ips() const
|
|
||||||
{
|
|
||||||
struct ifaddrs *ifa = (struct ifaddrs *)0;
|
|
||||||
if (getifaddrs(&ifa))
|
|
||||||
return std::vector<InetAddress>();
|
|
||||||
|
|
||||||
std::vector<InetAddress> r;
|
|
||||||
|
|
||||||
struct ifaddrs *p = ifa;
|
|
||||||
while (p) {
|
|
||||||
if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) {
|
|
||||||
switch(p->ifa_addr->sa_family) {
|
|
||||||
case AF_INET: {
|
|
||||||
struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr;
|
|
||||||
struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask;
|
|
||||||
r.push_back(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr)));
|
|
||||||
} break;
|
|
||||||
case AF_INET6: {
|
|
||||||
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr;
|
|
||||||
struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask;
|
|
||||||
uint32_t b[4];
|
|
||||||
memcpy(b,nm->sin6_addr.s6_addr,sizeof(b));
|
|
||||||
r.push_back(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3])));
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p = p->ifa_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifa)
|
|
||||||
freeifaddrs(ifa);
|
|
||||||
|
|
||||||
std::sort(r.begin(),r.end());
|
|
||||||
std::unique(r.begin(),r.end());
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
|
|
||||||
{
|
|
||||||
char putBuf[4096];
|
|
||||||
if ((len <= _mtu)&&(_enabled)) {
|
|
||||||
to.copyTo(putBuf,6);
|
|
||||||
from.copyTo(putBuf + 6,6);
|
|
||||||
*((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType);
|
|
||||||
memcpy(putBuf + 14,data,len);
|
|
||||||
len += 14;
|
|
||||||
int r = pcap_inject(reinterpret_cast<pcap_t *>(_pcap),putBuf,len);
|
|
||||||
if (r <= 0) {
|
|
||||||
printf("%s: pcap_inject() failed\n",_dev.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
printf("%s: inject %s -> %s etherType==%u len=%u r==%d\n",_dev.c_str(),from.toString().c_str(),to.toString().c_str(),etherType,len,r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string OSXEthernetTap::deviceName() const
|
|
||||||
{
|
|
||||||
return _dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::setFriendlyName(const char *friendlyName)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
|
|
||||||
{
|
|
||||||
std::vector<MulticastGroup> newGroups;
|
|
||||||
|
|
||||||
struct _intl_ifmaddrs *ifmap = (struct _intl_ifmaddrs *)0;
|
|
||||||
if (!_intl_getifmaddrs(&ifmap)) {
|
|
||||||
struct _intl_ifmaddrs *p = ifmap;
|
|
||||||
while (p) {
|
|
||||||
if (p->ifma_addr->sa_family == AF_LINK) {
|
|
||||||
struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
|
|
||||||
struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
|
|
||||||
if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
|
|
||||||
newGroups.push_back(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0));
|
|
||||||
}
|
|
||||||
p = p->ifma_next;
|
|
||||||
}
|
|
||||||
_intl_freeifmaddrs(ifmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<InetAddress> allIps(ips());
|
|
||||||
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
|
|
||||||
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
|
|
||||||
|
|
||||||
std::sort(newGroups.begin(),newGroups.end());
|
|
||||||
std::unique(newGroups.begin(),newGroups.end());
|
|
||||||
|
|
||||||
for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
|
|
||||||
if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))
|
|
||||||
added.push_back(*m);
|
|
||||||
}
|
|
||||||
for(std::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) {
|
|
||||||
if (!std::binary_search(newGroups.begin(),newGroups.end(),*m))
|
|
||||||
removed.push_back(*m);
|
|
||||||
}
|
|
||||||
|
|
||||||
_multicastGroups.swap(newGroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _pcapHandler(u_char *ptr,const struct pcap_pkthdr *hdr,const u_char *data)
|
|
||||||
{
|
|
||||||
OSXEthernetTap *tap = reinterpret_cast<OSXEthernetTap *>(ptr);
|
|
||||||
if (hdr->caplen > 14) {
|
|
||||||
MAC to(data,6);
|
|
||||||
MAC from(data + 6,6);
|
|
||||||
if (from == tap->_mac) {
|
|
||||||
unsigned int etherType = ntohs(((const uint16_t *)data)[6]);
|
|
||||||
printf("%s: %s -> %s etherType==%u len==%u\n",tap->_dev.c_str(),from.toString().c_str(),to.toString().c_str(),etherType,(unsigned int)hdr->caplen);
|
|
||||||
// TODO: VLAN support
|
|
||||||
tap->_handler(tap->_arg,tap->_nwid,from,to,etherType,0,(const void *)(data + 14),hdr->len - 14);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::threadMain()
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
pcap_loop(reinterpret_cast<pcap_t *>(_pcap),-1,&_pcapHandler,reinterpret_cast<u_char *>(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
@ -1,831 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
|
||||||
*
|
|
||||||
* 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 <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <sys/cdefs.h>
|
|
||||||
#include <sys/uio.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/sys_domain.h>
|
|
||||||
#include <sys/kern_control.h>
|
|
||||||
#include <net/if_utun.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <net/route.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#include <net/if_arp.h>
|
|
||||||
#include <net/if_dl.h>
|
|
||||||
#include <net/if_media.h>
|
|
||||||
#include <netinet6/in6_var.h>
|
|
||||||
#include <netinet/in_var.h>
|
|
||||||
#include <netinet/icmp6.h>
|
|
||||||
|
|
||||||
// OSX compile fix... in6_var defines this in a struct which namespaces it for C++ ... why?!?
|
|
||||||
struct prf_ra {
|
|
||||||
u_char onlink : 1;
|
|
||||||
u_char autonomous : 1;
|
|
||||||
u_char reserved : 6;
|
|
||||||
} prf_ra;
|
|
||||||
|
|
||||||
#include <netinet6/nd6.h>
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
|
|
||||||
// These are KERNEL_PRIVATE... why?
|
|
||||||
#ifndef SIOCAUTOCONF_START
|
|
||||||
#define SIOCAUTOCONF_START _IOWR('i', 132, struct in6_ifreq) /* accept rtadvd on this interface */
|
|
||||||
#endif
|
|
||||||
#ifndef SIOCAUTOCONF_STOP
|
|
||||||
#define SIOCAUTOCONF_STOP _IOWR('i', 133, struct in6_ifreq) /* stop accepting rtadv for this interface */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// This source is from:
|
|
||||||
// http://www.opensource.apple.com/source/Libinfo/Libinfo-406.17/gen.subproj/getifmaddrs.c?txt
|
|
||||||
// It's here because OSX 10.6 does not have this convenience function.
|
|
||||||
|
|
||||||
#define SALIGN (sizeof(uint32_t) - 1)
|
|
||||||
#define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \
|
|
||||||
(SALIGN + 1))
|
|
||||||
#define MAX_SYSCTL_TRY 5
|
|
||||||
#define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA)
|
|
||||||
|
|
||||||
/* FreeBSD uses NET_RT_IFMALIST and RTM_NEWMADDR from <sys/socket.h> */
|
|
||||||
/* We can use NET_RT_IFLIST2 and RTM_NEWMADDR2 on Darwin */
|
|
||||||
//#define DARWIN_COMPAT
|
|
||||||
|
|
||||||
//#ifdef DARWIN_COMPAT
|
|
||||||
#define GIM_SYSCTL_MIB NET_RT_IFLIST2
|
|
||||||
#define GIM_RTM_ADDR RTM_NEWMADDR2
|
|
||||||
//#else
|
|
||||||
//#define GIM_SYSCTL_MIB NET_RT_IFMALIST
|
|
||||||
//#define GIM_RTM_ADDR RTM_NEWMADDR
|
|
||||||
//#endif
|
|
||||||
|
|
||||||
// Not in 10.6 includes so use our own
|
|
||||||
struct _intl_ifmaddrs {
|
|
||||||
struct _intl_ifmaddrs *ifma_next;
|
|
||||||
struct sockaddr *ifma_name;
|
|
||||||
struct sockaddr *ifma_addr;
|
|
||||||
struct sockaddr *ifma_lladdr;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline int _intl_getifmaddrs(struct _intl_ifmaddrs **pif)
|
|
||||||
{
|
|
||||||
int icnt = 1;
|
|
||||||
int dcnt = 0;
|
|
||||||
int ntry = 0;
|
|
||||||
size_t len;
|
|
||||||
size_t needed;
|
|
||||||
int mib[6];
|
|
||||||
int i;
|
|
||||||
char *buf;
|
|
||||||
char *data;
|
|
||||||
char *next;
|
|
||||||
char *p;
|
|
||||||
struct ifma_msghdr2 *ifmam;
|
|
||||||
struct _intl_ifmaddrs *ifa, *ift;
|
|
||||||
struct rt_msghdr *rtm;
|
|
||||||
struct sockaddr *sa;
|
|
||||||
|
|
||||||
mib[0] = CTL_NET;
|
|
||||||
mib[1] = PF_ROUTE;
|
|
||||||
mib[2] = 0; /* protocol */
|
|
||||||
mib[3] = 0; /* wildcard address family */
|
|
||||||
mib[4] = GIM_SYSCTL_MIB;
|
|
||||||
mib[5] = 0; /* no flags */
|
|
||||||
do {
|
|
||||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
|
|
||||||
return (-1);
|
|
||||||
if ((buf = (char *)malloc(needed)) == NULL)
|
|
||||||
return (-1);
|
|
||||||
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
|
|
||||||
if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
|
|
||||||
free(buf);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
free(buf);
|
|
||||||
buf = NULL;
|
|
||||||
}
|
|
||||||
} while (buf == NULL);
|
|
||||||
|
|
||||||
for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
|
|
||||||
rtm = (struct rt_msghdr *)(void *)next;
|
|
||||||
if (rtm->rtm_version != RTM_VERSION)
|
|
||||||
continue;
|
|
||||||
switch (rtm->rtm_type) {
|
|
||||||
case GIM_RTM_ADDR:
|
|
||||||
ifmam = (struct ifma_msghdr2 *)(void *)rtm;
|
|
||||||
if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
|
|
||||||
break;
|
|
||||||
icnt++;
|
|
||||||
p = (char *)(ifmam + 1);
|
|
||||||
for (i = 0; i < RTAX_MAX; i++) {
|
|
||||||
if ((RTA_MASKS & ifmam->ifmam_addrs &
|
|
||||||
(1 << i)) == 0)
|
|
||||||
continue;
|
|
||||||
sa = (struct sockaddr *)(void *)p;
|
|
||||||
len = SA_RLEN(sa);
|
|
||||||
dcnt += len;
|
|
||||||
p += len;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data = (char *)malloc(sizeof(struct _intl_ifmaddrs) * icnt + dcnt);
|
|
||||||
if (data == NULL) {
|
|
||||||
free(buf);
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ifa = (struct _intl_ifmaddrs *)(void *)data;
|
|
||||||
data += sizeof(struct _intl_ifmaddrs) * icnt;
|
|
||||||
|
|
||||||
memset(ifa, 0, sizeof(struct _intl_ifmaddrs) * icnt);
|
|
||||||
ift = ifa;
|
|
||||||
|
|
||||||
for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
|
|
||||||
rtm = (struct rt_msghdr *)(void *)next;
|
|
||||||
if (rtm->rtm_version != RTM_VERSION)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (rtm->rtm_type) {
|
|
||||||
case GIM_RTM_ADDR:
|
|
||||||
ifmam = (struct ifma_msghdr2 *)(void *)rtm;
|
|
||||||
if ((ifmam->ifmam_addrs & RTA_IFA) == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
p = (char *)(ifmam + 1);
|
|
||||||
for (i = 0; i < RTAX_MAX; i++) {
|
|
||||||
if ((RTA_MASKS & ifmam->ifmam_addrs &
|
|
||||||
(1 << i)) == 0)
|
|
||||||
continue;
|
|
||||||
sa = (struct sockaddr *)(void *)p;
|
|
||||||
len = SA_RLEN(sa);
|
|
||||||
switch (i) {
|
|
||||||
case RTAX_GATEWAY:
|
|
||||||
ift->ifma_lladdr =
|
|
||||||
(struct sockaddr *)(void *)data;
|
|
||||||
memcpy(data, p, len);
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RTAX_IFP:
|
|
||||||
ift->ifma_name =
|
|
||||||
(struct sockaddr *)(void *)data;
|
|
||||||
memcpy(data, p, len);
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RTAX_IFA:
|
|
||||||
ift->ifma_addr =
|
|
||||||
(struct sockaddr *)(void *)data;
|
|
||||||
memcpy(data, p, len);
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
data += len;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p += len;
|
|
||||||
}
|
|
||||||
ift->ifma_next = ift + 1;
|
|
||||||
ift = ift->ifma_next;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
|
|
||||||
if (ift > ifa) {
|
|
||||||
ift--;
|
|
||||||
ift->ifma_next = NULL;
|
|
||||||
*pif = ifa;
|
|
||||||
} else {
|
|
||||||
*pif = NULL;
|
|
||||||
free(ifa);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _intl_freeifmaddrs(struct _intl_ifmaddrs *ifmp)
|
|
||||||
{
|
|
||||||
free(ifmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "../node/Constants.hpp"
|
|
||||||
#include "../node/Utils.hpp"
|
|
||||||
#include "../node/Mutex.hpp"
|
|
||||||
#include "../node/Dictionary.hpp"
|
|
||||||
#include "Arp.hpp"
|
|
||||||
#include "OSUtils.hpp"
|
|
||||||
#include "OSXEthernetTap.hpp"
|
|
||||||
|
|
||||||
// ff:ff:ff:ff:ff:ff with no ADI
|
|
||||||
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
|
|
||||||
|
|
||||||
static inline bool _setIpv6Stuff(const char *ifname,bool performNUD,bool acceptRouterAdverts)
|
|
||||||
{
|
|
||||||
struct in6_ndireq nd;
|
|
||||||
struct in6_ifreq ifr;
|
|
||||||
|
|
||||||
int s = socket(AF_INET6,SOCK_DGRAM,0);
|
|
||||||
if (s <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
memset(&nd,0,sizeof(nd));
|
|
||||||
strncpy(nd.ifname,ifname,sizeof(nd.ifname));
|
|
||||||
|
|
||||||
if (ioctl(s,SIOCGIFINFO_IN6,&nd)) {
|
|
||||||
close(s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long oldFlags = (unsigned long)nd.ndi.flags;
|
|
||||||
|
|
||||||
if (performNUD)
|
|
||||||
nd.ndi.flags |= ND6_IFF_PERFORMNUD;
|
|
||||||
else nd.ndi.flags &= ~ND6_IFF_PERFORMNUD;
|
|
||||||
|
|
||||||
if (oldFlags != (unsigned long)nd.ndi.flags) {
|
|
||||||
if (ioctl(s,SIOCSIFINFO_FLAGS,&nd)) {
|
|
||||||
close(s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&ifr,0,sizeof(ifr));
|
|
||||||
strncpy(ifr.ifr_name,ifname,sizeof(ifr.ifr_name));
|
|
||||||
if (ioctl(s,acceptRouterAdverts ? SIOCAUTOCONF_START : SIOCAUTOCONF_STOP,&ifr)) {
|
|
||||||
close(s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an OSX-native utun device (utun# where # is desiredNumber)
|
|
||||||
// Adapted from public domain utun example code by Jonathan Levin
|
|
||||||
static int _make_utun(int desiredNumber)
|
|
||||||
{
|
|
||||||
struct sockaddr_ctl sc;
|
|
||||||
struct ctl_info ctlInfo;
|
|
||||||
struct ifreq ifr;
|
|
||||||
|
|
||||||
memset(&ctlInfo, 0, sizeof(ctlInfo));
|
|
||||||
if (strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name)) >= sizeof(ctlInfo.ctl_name)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
|
|
||||||
if (fd == -1)
|
|
||||||
return -1;
|
|
||||||
if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) {
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.sc_id = ctlInfo.ctl_id;
|
|
||||||
sc.sc_len = sizeof(sc);
|
|
||||||
sc.sc_family = AF_SYSTEM;
|
|
||||||
sc.ss_sysaddr = AF_SYS_CONTROL;
|
|
||||||
sc.sc_unit = desiredNumber + 1;
|
|
||||||
|
|
||||||
if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) {
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&ifr,0,sizeof(ifr));
|
|
||||||
sprintf(ifr.ifr_name,"utun%d",desiredNumber);
|
|
||||||
if (ioctl(fd,SIOCGIFFLAGS,(void *)&ifr) < 0) {
|
|
||||||
printf("SIOCGIFFLAGS failed\n");
|
|
||||||
}
|
|
||||||
ifr.ifr_flags &= ~IFF_POINTOPOINT;
|
|
||||||
if (ioctl(fd,SIOCSIFFLAGS,(void *)&ifr) < 0) {
|
|
||||||
printf("clear IFF_POINTOPOINT failed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
static long globalTapsRunning = 0;
|
|
||||||
static Mutex globalTapCreateLock;
|
|
||||||
|
|
||||||
OSXEthernetTap::OSXEthernetTap(
|
|
||||||
const char *homePath,
|
|
||||||
const MAC &mac,
|
|
||||||
unsigned int mtu,
|
|
||||||
unsigned int metric,
|
|
||||||
uint64_t nwid,
|
|
||||||
const char *friendlyName,
|
|
||||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len),
|
|
||||||
void *arg) :
|
|
||||||
_handler(handler),
|
|
||||||
_arg(arg),
|
|
||||||
_arp((Arp *)0),
|
|
||||||
_nwid(nwid),
|
|
||||||
_homePath(homePath),
|
|
||||||
_mtu(mtu),
|
|
||||||
_metric(metric),
|
|
||||||
_fd(0),
|
|
||||||
_utun(false),
|
|
||||||
_enabled(true)
|
|
||||||
{
|
|
||||||
char devpath[64],ethaddr[64],mtustr[32],metstr[32],nwids[32];
|
|
||||||
struct stat stattmp;
|
|
||||||
|
|
||||||
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
|
|
||||||
|
|
||||||
if (mtu > 2800)
|
|
||||||
throw std::runtime_error("max tap MTU is 2800");
|
|
||||||
|
|
||||||
Mutex::Lock _gl(globalTapCreateLock);
|
|
||||||
|
|
||||||
// Read remembered previous device name, if any -- we'll try to reuse
|
|
||||||
Dictionary devmap;
|
|
||||||
std::string desiredDevice;
|
|
||||||
{
|
|
||||||
std::string devmapbuf;
|
|
||||||
if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) {
|
|
||||||
devmap.fromString(devmapbuf);
|
|
||||||
desiredDevice = devmap.get(nwids,"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (::stat((_homePath + ZT_PATH_SEPARATOR_S + "tap.kext").c_str(),&stattmp) == 0) {
|
|
||||||
// Try to init kext if it's there, otherwise revert to utun mode
|
|
||||||
|
|
||||||
if (::stat("/dev/zt0",&stattmp)) {
|
|
||||||
long kextpid = (long)vfork();
|
|
||||||
if (kextpid == 0) {
|
|
||||||
::chdir(homePath);
|
|
||||||
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
|
|
||||||
::execl("/sbin/kextload","/sbin/kextload","-q","-repository",homePath,"tap.kext",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (kextpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(kextpid,&exitcode,0);
|
|
||||||
}
|
|
||||||
::usleep(500); // give tap device driver time to start up and try again
|
|
||||||
if (::stat("/dev/zt0",&stattmp))
|
|
||||||
_utun = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_utun) {
|
|
||||||
// See if we can re-use the last device we had.
|
|
||||||
bool recalledDevice = false;
|
|
||||||
if (desiredDevice.length() > 2) {
|
|
||||||
Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",desiredDevice.c_str());
|
|
||||||
if (stat(devpath,&stattmp) == 0) {
|
|
||||||
_fd = ::open(devpath,O_RDWR);
|
|
||||||
if (_fd > 0) {
|
|
||||||
_dev = desiredDevice;
|
|
||||||
recalledDevice = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the first unused tap device if we didn't recall a previous one.
|
|
||||||
if (!recalledDevice) {
|
|
||||||
for(int i=0;i<64;++i) {
|
|
||||||
Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i);
|
|
||||||
if (stat(devpath,&stattmp)) {
|
|
||||||
_utun = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_fd = ::open(devpath,O_RDWR);
|
|
||||||
if (_fd > 0) {
|
|
||||||
char foo[16];
|
|
||||||
Utils::snprintf(foo,sizeof(foo),"zt%d",i);
|
|
||||||
_dev = foo;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_fd <= 0)
|
|
||||||
_utun = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_utun = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_utun) {
|
|
||||||
// Use OSX built-in utun device if kext is not available or doesn't work
|
|
||||||
|
|
||||||
int utunNo = 0;
|
|
||||||
|
|
||||||
if ((desiredDevice.length() > 4)&&(desiredDevice.substr(0,4) == "utun")) {
|
|
||||||
utunNo = Utils::strToInt(desiredDevice.substr(4).c_str());
|
|
||||||
if (utunNo >= 0)
|
|
||||||
_fd = _make_utun(utunNo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_fd <= 0) {
|
|
||||||
// Start at utun8 to leave lower utuns unused since other stuff might
|
|
||||||
// want them -- OpenVPN, cjdns, etc. I'm not sure if those are smart
|
|
||||||
// enough to scan upward like this.
|
|
||||||
for(utunNo=8;utunNo<=256;++utunNo) {
|
|
||||||
if ((_fd = _make_utun(utunNo)) > 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_fd <= 0)
|
|
||||||
throw std::runtime_error("unable to find/load ZeroTier tap driver OR use built-in utun driver in OSX; permission or system problem or too many open devices?");
|
|
||||||
|
|
||||||
Utils::snprintf(devpath,sizeof(devpath),"utun%d",utunNo);
|
|
||||||
_dev = devpath;
|
|
||||||
|
|
||||||
// Configure address and bring it up
|
|
||||||
Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
|
|
||||||
Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",mtustr,"metric",metstr,"up",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
if (exitcode) {
|
|
||||||
::close(_fd);
|
|
||||||
throw std::runtime_error("ifconfig failure activating utun interface");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Use our ZeroTier OSX tun/tap driver for zt# Ethernet tap device
|
|
||||||
|
|
||||||
if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) {
|
|
||||||
::close(_fd);
|
|
||||||
throw std::runtime_error("unable to set flags on file descriptor for TAP device");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure MAC address and MTU, bring interface up
|
|
||||||
Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
|
|
||||||
Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
|
|
||||||
Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
if (exitcode) {
|
|
||||||
::close(_fd);
|
|
||||||
throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_setIpv6Stuff(_dev.c_str(),true,false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set close-on-exec so that devices cannot persist if we fork/exec for update
|
|
||||||
fcntl(_fd,F_SETFD,fcntl(_fd,F_GETFD) | FD_CLOEXEC);
|
|
||||||
|
|
||||||
::pipe(_shutdownSignalPipe);
|
|
||||||
|
|
||||||
++globalTapsRunning;
|
|
||||||
|
|
||||||
devmap[nwids] = _dev;
|
|
||||||
OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmap.toString());
|
|
||||||
|
|
||||||
_thread = Thread::start(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
OSXEthernetTap::~OSXEthernetTap()
|
|
||||||
{
|
|
||||||
Mutex::Lock _gl(globalTapCreateLock);
|
|
||||||
|
|
||||||
::write(_shutdownSignalPipe[1],(const void *)this,1); // writing a byte causes thread to exit
|
|
||||||
Thread::join(_thread);
|
|
||||||
|
|
||||||
::close(_fd);
|
|
||||||
::close(_shutdownSignalPipe[0]);
|
|
||||||
::close(_shutdownSignalPipe[1]);
|
|
||||||
|
|
||||||
if (_utun) {
|
|
||||||
delete _arp;
|
|
||||||
} else {
|
|
||||||
if (--globalTapsRunning <= 0) {
|
|
||||||
globalTapsRunning = 0; // sanity check -- should not be possible
|
|
||||||
|
|
||||||
char tmp[16384];
|
|
||||||
sprintf(tmp,"%s/%s",_homePath.c_str(),"tap.kext");
|
|
||||||
long kextpid = (long)vfork();
|
|
||||||
if (kextpid == 0) {
|
|
||||||
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
|
|
||||||
::execl("/sbin/kextunload","/sbin/kextunload",tmp,(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (kextpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(kextpid,&exitcode,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::setEnabled(bool en)
|
|
||||||
{
|
|
||||||
_enabled = en;
|
|
||||||
// TODO: interface status change
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OSXEthernetTap::enabled() const
|
|
||||||
{
|
|
||||||
return _enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
|
|
||||||
{
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
|
|
||||||
_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
waitpid(cpid,&exitcode,0);
|
|
||||||
return (exitcode == 0);
|
|
||||||
}
|
|
||||||
return false; // never reached, make compiler shut up about return value
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OSXEthernetTap::addIp(const InetAddress &ip)
|
|
||||||
{
|
|
||||||
if (!ip)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::vector<InetAddress> allIps(ips());
|
|
||||||
if (std::binary_search(allIps.begin(),allIps.end(),ip))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Remove and reconfigure if address is the same but netmask is different
|
|
||||||
for(std::vector<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i) {
|
|
||||||
if ((i->ipsEqual(ip))&&(i->netmaskBits() != ip.netmaskBits())) {
|
|
||||||
if (___removeIp(_dev,*i))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_utun) {
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
if (ip.ss_family == AF_INET6) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet6",ip.toString().c_str(),"alias",(const char *)0);
|
|
||||||
} else {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.toString().c_str(),ip.toIpString().c_str(),"alias",(const char *)0);
|
|
||||||
}
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
|
|
||||||
if (exitcode == 0) {
|
|
||||||
if (ip.ss_family == AF_INET) {
|
|
||||||
// Add route to network over tun for IPv4 -- otherwise it behaves
|
|
||||||
// as a simple point to point tunnel instead of a true route.
|
|
||||||
cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::close(STDERR_FILENO);
|
|
||||||
::close(STDOUT_FILENO);
|
|
||||||
::execl("/sbin/route","/sbin/route","add",ip.network().toString().c_str(),ip.toIpString().c_str(),(const char *)0);
|
|
||||||
::exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
return (exitcode == 0);
|
|
||||||
}
|
|
||||||
} else return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
long cpid = (long)vfork();
|
|
||||||
if (cpid == 0) {
|
|
||||||
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
|
|
||||||
::_exit(-1);
|
|
||||||
} else if (cpid > 0) {
|
|
||||||
int exitcode = -1;
|
|
||||||
::waitpid(cpid,&exitcode,0);
|
|
||||||
return (exitcode == 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OSXEthernetTap::removeIp(const InetAddress &ip)
|
|
||||||
{
|
|
||||||
if (!ip)
|
|
||||||
return true;
|
|
||||||
std::vector<InetAddress> allIps(ips());
|
|
||||||
if (!std::binary_search(allIps.begin(),allIps.end(),ip)) {
|
|
||||||
if (___removeIp(_dev,ip))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<InetAddress> OSXEthernetTap::ips() const
|
|
||||||
{
|
|
||||||
struct ifaddrs *ifa = (struct ifaddrs *)0;
|
|
||||||
if (getifaddrs(&ifa))
|
|
||||||
return std::vector<InetAddress>();
|
|
||||||
|
|
||||||
std::vector<InetAddress> r;
|
|
||||||
|
|
||||||
struct ifaddrs *p = ifa;
|
|
||||||
while (p) {
|
|
||||||
if ((!strcmp(p->ifa_name,_dev.c_str()))&&(p->ifa_addr)&&(p->ifa_netmask)&&(p->ifa_addr->sa_family == p->ifa_netmask->sa_family)) {
|
|
||||||
switch(p->ifa_addr->sa_family) {
|
|
||||||
case AF_INET: {
|
|
||||||
struct sockaddr_in *sin = (struct sockaddr_in *)p->ifa_addr;
|
|
||||||
struct sockaddr_in *nm = (struct sockaddr_in *)p->ifa_netmask;
|
|
||||||
r.push_back(InetAddress(&(sin->sin_addr.s_addr),4,Utils::countBits((uint32_t)nm->sin_addr.s_addr)));
|
|
||||||
} break;
|
|
||||||
case AF_INET6: {
|
|
||||||
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)p->ifa_addr;
|
|
||||||
struct sockaddr_in6 *nm = (struct sockaddr_in6 *)p->ifa_netmask;
|
|
||||||
uint32_t b[4];
|
|
||||||
memcpy(b,nm->sin6_addr.s6_addr,sizeof(b));
|
|
||||||
r.push_back(InetAddress(sin->sin6_addr.s6_addr,16,Utils::countBits(b[0]) + Utils::countBits(b[1]) + Utils::countBits(b[2]) + Utils::countBits(b[3])));
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p = p->ifa_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifa)
|
|
||||||
freeifaddrs(ifa);
|
|
||||||
|
|
||||||
std::sort(r.begin(),r.end());
|
|
||||||
std::unique(r.begin(),r.end());
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
|
|
||||||
{
|
|
||||||
char putBuf[4096];
|
|
||||||
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
|
|
||||||
to.copyTo(putBuf,6);
|
|
||||||
from.copyTo(putBuf + 6,6);
|
|
||||||
*((uint16_t *)(putBuf + 12)) = htons((uint16_t)etherType);
|
|
||||||
memcpy(putBuf + 14,data,len);
|
|
||||||
len += 14;
|
|
||||||
::write(_fd,putBuf,len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string OSXEthernetTap::deviceName() const
|
|
||||||
{
|
|
||||||
return _dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::setFriendlyName(const char *friendlyName)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
|
|
||||||
{
|
|
||||||
std::vector<MulticastGroup> newGroups;
|
|
||||||
|
|
||||||
struct _intl_ifmaddrs *ifmap = (struct _intl_ifmaddrs *)0;
|
|
||||||
if (!_intl_getifmaddrs(&ifmap)) {
|
|
||||||
struct _intl_ifmaddrs *p = ifmap;
|
|
||||||
while (p) {
|
|
||||||
if (p->ifma_addr->sa_family == AF_LINK) {
|
|
||||||
struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
|
|
||||||
struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
|
|
||||||
if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
|
|
||||||
newGroups.push_back(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0));
|
|
||||||
}
|
|
||||||
p = p->ifma_next;
|
|
||||||
}
|
|
||||||
_intl_freeifmaddrs(ifmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<InetAddress> allIps(ips());
|
|
||||||
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
|
|
||||||
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
|
|
||||||
|
|
||||||
std::sort(newGroups.begin(),newGroups.end());
|
|
||||||
std::unique(newGroups.begin(),newGroups.end());
|
|
||||||
|
|
||||||
for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
|
|
||||||
if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))
|
|
||||||
added.push_back(*m);
|
|
||||||
}
|
|
||||||
for(std::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) {
|
|
||||||
if (!std::binary_search(newGroups.begin(),newGroups.end(),*m))
|
|
||||||
removed.push_back(*m);
|
|
||||||
}
|
|
||||||
|
|
||||||
_multicastGroups.swap(newGroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OSXEthernetTap::threadMain()
|
|
||||||
throw()
|
|
||||||
{
|
|
||||||
fd_set readfds,nullfds;
|
|
||||||
MAC to,from;
|
|
||||||
int n,nfds,r;
|
|
||||||
char getBuf[8194];
|
|
||||||
|
|
||||||
Thread::sleep(500);
|
|
||||||
|
|
||||||
FD_ZERO(&readfds);
|
|
||||||
FD_ZERO(&nullfds);
|
|
||||||
nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
|
|
||||||
|
|
||||||
r = 0;
|
|
||||||
for(;;) {
|
|
||||||
FD_SET(_shutdownSignalPipe[0],&readfds);
|
|
||||||
FD_SET(_fd,&readfds);
|
|
||||||
select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0);
|
|
||||||
|
|
||||||
if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (FD_ISSET(_fd,&readfds)) {
|
|
||||||
n = (int)::read(_fd,getBuf + r,sizeof(getBuf) - r);
|
|
||||||
if (n < 0) {
|
|
||||||
if ((errno != EINTR)&&(errno != ETIMEDOUT))
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
// Some tap drivers like to send the ethernet frame and the
|
|
||||||
// payload in two chunks, so handle that by accumulating
|
|
||||||
// data until we have at least a frame.
|
|
||||||
r += n;
|
|
||||||
if (r > 14) {
|
|
||||||
if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms
|
|
||||||
r = _mtu + 14;
|
|
||||||
|
|
||||||
if (_enabled) {
|
|
||||||
to.setTo(getBuf,6);
|
|
||||||
from.setTo(getBuf + 6,6);
|
|
||||||
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
|
|
||||||
// TODO: VLAN support
|
|
||||||
_handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*
|
|
||||||
* --
|
|
||||||
*
|
|
||||||
* You can be released from the requirements of the license by purchasing
|
|
||||||
* a commercial license. Buying such a license is mandatory as soon as you
|
|
||||||
* develop commercial closed-source software that incorporates or links
|
|
||||||
* directly against ZeroTier software without disclosing the source code
|
|
||||||
* of your own application.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ZT_OSXETHERNETTAP_HPP
|
|
||||||
#define ZT_OSXETHERNETTAP_HPP
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "../node/Constants.hpp"
|
|
||||||
#include "../node/MAC.hpp"
|
|
||||||
#include "../node/InetAddress.hpp"
|
|
||||||
#include "../node/MulticastGroup.hpp"
|
|
||||||
|
|
||||||
#include "Thread.hpp"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OSX Ethernet tap using ZeroTier kernel extension zt# devices
|
|
||||||
*/
|
|
||||||
class OSXEthernetTap
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OSXEthernetTap(
|
|
||||||
const char *homePath,
|
|
||||||
const MAC &mac,
|
|
||||||
unsigned int mtu,
|
|
||||||
unsigned int metric,
|
|
||||||
uint64_t nwid,
|
|
||||||
const char *friendlyName,
|
|
||||||
void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
|
|
||||||
void *arg);
|
|
||||||
|
|
||||||
~OSXEthernetTap();
|
|
||||||
|
|
||||||
void setEnabled(bool en);
|
|
||||||
bool enabled() const;
|
|
||||||
bool addIp(const InetAddress &ip);
|
|
||||||
bool removeIp(const InetAddress &ip);
|
|
||||||
std::vector<InetAddress> ips() const;
|
|
||||||
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
|
||||||
std::string deviceName() const;
|
|
||||||
void setFriendlyName(const char *friendlyName);
|
|
||||||
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
|
|
||||||
void setMtu(unsigned int mtu);
|
|
||||||
|
|
||||||
void threadMain()
|
|
||||||
throw();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
|
|
||||||
void *_arg;
|
|
||||||
uint64_t _nwid;
|
|
||||||
Thread _thread;
|
|
||||||
std::string _homePath;
|
|
||||||
std::string _dev;
|
|
||||||
std::vector<MulticastGroup> _multicastGroups;
|
|
||||||
unsigned int _mtu;
|
|
||||||
unsigned int _metric;
|
|
||||||
int _fd;
|
|
||||||
int _shutdownSignalPipe[2];
|
|
||||||
volatile bool _enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
|
||||||
*
|
|
||||||
* 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_OSXETHERNETTAP_HPP
|
|
||||||
#define ZT_OSXETHERNETTAP_HPP
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "../node/Constants.hpp"
|
|
||||||
#include "../node/MAC.hpp"
|
|
||||||
#include "../node/InetAddress.hpp"
|
|
||||||
#include "../node/MulticastGroup.hpp"
|
|
||||||
|
|
||||||
#include "Thread.hpp"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OSX Ethernet tap using ZeroTier kernel extension zt# devices
|
|
||||||
*/
|
|
||||||
class OSXEthernetTap
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OSXEthernetTap(
|
|
||||||
const char *homePath,
|
|
||||||
const MAC &mac,
|
|
||||||
unsigned int mtu,
|
|
||||||
unsigned int metric,
|
|
||||||
uint64_t nwid,
|
|
||||||
const char *friendlyName,
|
|
||||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
|
|
||||||
void *arg);
|
|
||||||
|
|
||||||
~OSXEthernetTap();
|
|
||||||
|
|
||||||
inline void setEnabled(bool en) { _enabled = en; }
|
|
||||||
inline bool enabled() const { return _enabled; }
|
|
||||||
bool addIp(const InetAddress &ip);
|
|
||||||
bool removeIp(const InetAddress &ip);
|
|
||||||
std::vector<InetAddress> ips() const;
|
|
||||||
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
|
||||||
std::string deviceName() const;
|
|
||||||
void setFriendlyName(const char *friendlyName);
|
|
||||||
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
|
|
||||||
|
|
||||||
void threadMain()
|
|
||||||
throw();
|
|
||||||
|
|
||||||
// Private members of OSXEthernetTap have public visibility to be accessable
|
|
||||||
// from an internal bounce function; don't modify directly.
|
|
||||||
void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
|
|
||||||
void *_arg;
|
|
||||||
void *_pcap; // pcap_t *
|
|
||||||
uint64_t _nwid;
|
|
||||||
MAC _mac;
|
|
||||||
Thread _thread;
|
|
||||||
std::string _homePath;
|
|
||||||
std::string _dev;
|
|
||||||
std::vector<MulticastGroup> _multicastGroups;
|
|
||||||
unsigned int _mtu;
|
|
||||||
unsigned int _metric;
|
|
||||||
volatile bool _enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
|
||||||
*
|
|
||||||
* 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_OSXETHERNETTAP_HPP
|
|
||||||
#define ZT_OSXETHERNETTAP_HPP
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "../node/Constants.hpp"
|
|
||||||
#include "../node/MAC.hpp"
|
|
||||||
#include "../node/InetAddress.hpp"
|
|
||||||
#include "../node/MulticastGroup.hpp"
|
|
||||||
|
|
||||||
#include "Thread.hpp"
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
class Arp;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OSX Ethernet tap supporting either ZeroTier tun/tap kext or OSX-native utun
|
|
||||||
*/
|
|
||||||
class OSXEthernetTap
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OSXEthernetTap(
|
|
||||||
const char *homePath,
|
|
||||||
const MAC &mac,
|
|
||||||
unsigned int mtu,
|
|
||||||
unsigned int metric,
|
|
||||||
uint64_t nwid,
|
|
||||||
const char *friendlyName,
|
|
||||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
|
|
||||||
void *arg);
|
|
||||||
|
|
||||||
~OSXEthernetTap();
|
|
||||||
|
|
||||||
void setEnabled(bool en);
|
|
||||||
bool enabled() const;
|
|
||||||
bool addIp(const InetAddress &ip);
|
|
||||||
bool removeIp(const InetAddress &ip);
|
|
||||||
std::vector<InetAddress> ips() const;
|
|
||||||
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
|
||||||
std::string deviceName() const;
|
|
||||||
void setFriendlyName(const char *friendlyName);
|
|
||||||
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
|
|
||||||
|
|
||||||
inline bool isNativeUtun() const { return _utun; }
|
|
||||||
|
|
||||||
void threadMain()
|
|
||||||
throw();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
|
|
||||||
void *_arg;
|
|
||||||
Arp *_arp; // created and used if utun is enabled
|
|
||||||
uint64_t _nwid;
|
|
||||||
Thread _thread;
|
|
||||||
std::string _homePath;
|
|
||||||
std::string _dev;
|
|
||||||
std::vector<MulticastGroup> _multicastGroups;
|
|
||||||
unsigned int _mtu;
|
|
||||||
unsigned int _metric;
|
|
||||||
int _fd;
|
|
||||||
int _shutdownSignalPipe[2];
|
|
||||||
bool _utun;
|
|
||||||
volatile bool _enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,497 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2018 ZeroTier, Inc.
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//#define ZT_CONTROLLER_USE_RETHINKDB
|
|
||||||
|
|
||||||
#ifdef ZT_CONTROLLER_USE_RETHINKDB
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "RethinkDB.hpp"
|
|
||||||
#include "EmbeddedNetworkController.hpp"
|
|
||||||
|
|
||||||
#include "../version.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include "../ext/librethinkdbxx/build/include/rethinkdb.h"
|
|
||||||
|
|
||||||
namespace R = RethinkDB;
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
namespace ZeroTier {
|
|
||||||
|
|
||||||
static const char *_timestr()
|
|
||||||
{
|
|
||||||
time_t t = time(0);
|
|
||||||
char *ts = ctime(&t);
|
|
||||||
char *p = ts;
|
|
||||||
if (!p)
|
|
||||||
return "";
|
|
||||||
while (*p) {
|
|
||||||
if (*p == '\n') {
|
|
||||||
*p = (char)0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
return ts;
|
|
||||||
}
|
|
||||||
|
|
||||||
RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) :
|
|
||||||
DB(nc,myId,path),
|
|
||||||
_ready(2), // two tables need to be synchronized before we're ready, so this is ready when it reaches 0
|
|
||||||
_run(1),
|
|
||||||
_waitNoticePrinted(false)
|
|
||||||
{
|
|
||||||
// rethinkdb:host:port:db[:auth]
|
|
||||||
std::vector<std::string> ps(OSUtils::split(path,":","",""));
|
|
||||||
if ((ps.size() < 4)||(ps[0] != "rethinkdb"))
|
|
||||||
throw std::runtime_error("invalid rethinkdb database url");
|
|
||||||
_host = ps[1];
|
|
||||||
_port = Utils::strToInt(ps[2].c_str());
|
|
||||||
_db = ps[3];
|
|
||||||
if (ps.size() > 4)
|
|
||||||
_auth = ps[4];
|
|
||||||
|
|
||||||
_readyLock.lock();
|
|
||||||
|
|
||||||
_membersDbWatcher = std::thread([this]() {
|
|
||||||
try {
|
|
||||||
while (_run == 1) {
|
|
||||||
try {
|
|
||||||
std::unique_ptr<R::Connection> rdb(R::connect(this->_host,this->_port,this->_auth));
|
|
||||||
if (rdb) {
|
|
||||||
_membersDbWatcherConnection = (void *)rdb.get();
|
|
||||||
auto cur = R::db(this->_db).table("Member",R::optargs("read_mode","outdated")).get_all(this->_myAddressStr,R::optargs("index","controllerId")).changes(R::optargs("squash",0.05,"include_initial",true,"include_types",true,"include_states",true)).run(*rdb);
|
|
||||||
while (cur.has_next()) {
|
|
||||||
if (_run != 1) break;
|
|
||||||
json tmp(json::parse(cur.next().as_json()));
|
|
||||||
if ((tmp["type"] == "state")&&(tmp["state"] == "ready")) {
|
|
||||||
if (++this->_ready == 2) {
|
|
||||||
if (_waitNoticePrinted)
|
|
||||||
fprintf(stderr,"[%s] NOTICE: %.10llx controller RethinkDB data download complete." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
|
||||||
this->_readyLock.unlock();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
json &ov = tmp["old_val"];
|
|
||||||
json &nv = tmp["new_val"];
|
|
||||||
json oldConfig,newConfig;
|
|
||||||
if (ov.is_object()) oldConfig = ov["config"];
|
|
||||||
if (nv.is_object()) newConfig = nv["config"];
|
|
||||||
if (oldConfig.is_object()||newConfig.is_object())
|
|
||||||
this->_memberChanged(oldConfig,newConfig,(this->_ready <= 0));
|
|
||||||
} catch ( ... ) {} // ignore bad records
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (member change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
|
|
||||||
} catch (R::Error &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (member change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
|
|
||||||
} catch ( ... ) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (member change stream): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
|
||||||
}
|
|
||||||
} catch ( ... ) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
_networksDbWatcher = std::thread([this]() {
|
|
||||||
try {
|
|
||||||
while (_run == 1) {
|
|
||||||
try {
|
|
||||||
std::unique_ptr<R::Connection> rdb(R::connect(this->_host,this->_port,this->_auth));
|
|
||||||
if (rdb) {
|
|
||||||
_networksDbWatcherConnection = (void *)rdb.get();
|
|
||||||
auto cur = R::db(this->_db).table("Network",R::optargs("read_mode","outdated")).get_all(this->_myAddressStr,R::optargs("index","controllerId")).changes(R::optargs("squash",0.05,"include_initial",true,"include_types",true,"include_states",true)).run(*rdb);
|
|
||||||
while (cur.has_next()) {
|
|
||||||
if (_run != 1) break;
|
|
||||||
json tmp(json::parse(cur.next().as_json()));
|
|
||||||
if ((tmp["type"] == "state")&&(tmp["state"] == "ready")) {
|
|
||||||
if (--this->_ready == 0) {
|
|
||||||
if (_waitNoticePrinted)
|
|
||||||
fprintf(stderr,"[%s] NOTICE: %.10llx controller RethinkDB data download complete." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
|
||||||
this->_readyLock.unlock();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
json &ov = tmp["old_val"];
|
|
||||||
json &nv = tmp["new_val"];
|
|
||||||
json oldConfig,newConfig;
|
|
||||||
if (ov.is_object()) oldConfig = ov["config"];
|
|
||||||
if (nv.is_object()) newConfig = nv["config"];
|
|
||||||
if (oldConfig.is_object()||newConfig.is_object())
|
|
||||||
this->_networkChanged(oldConfig,newConfig,(this->_ready <= 0));
|
|
||||||
} catch ( ... ) {} // ignore bad records
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (network change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
|
|
||||||
} catch (R::Error &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (network change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
|
|
||||||
} catch ( ... ) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (network change stream): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
|
||||||
}
|
|
||||||
} catch ( ... ) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
for(int t=0;t<ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS;++t) {
|
|
||||||
_commitThread[t] = std::thread([this]() {
|
|
||||||
try {
|
|
||||||
std::unique_ptr<R::Connection> rdb;
|
|
||||||
nlohmann::json *config = (nlohmann::json *)0;
|
|
||||||
while ((this->_commitQueue.get(config))&&(_run == 1)) {
|
|
||||||
if (!config)
|
|
||||||
continue;
|
|
||||||
nlohmann::json record;
|
|
||||||
const char *table = (const char *)0;
|
|
||||||
std::string deleteId;
|
|
||||||
try {
|
|
||||||
const std::string objtype = (*config)["objtype"];
|
|
||||||
if (objtype == "member") {
|
|
||||||
const std::string nwid = (*config)["nwid"];
|
|
||||||
const std::string id = (*config)["id"];
|
|
||||||
record["id"] = nwid + "-" + id;
|
|
||||||
record["controllerId"] = this->_myAddressStr;
|
|
||||||
record["networkId"] = nwid;
|
|
||||||
record["nodeId"] = id;
|
|
||||||
record["config"] = *config;
|
|
||||||
table = "Member";
|
|
||||||
} else if (objtype == "network") {
|
|
||||||
const std::string id = (*config)["id"];
|
|
||||||
record["id"] = id;
|
|
||||||
record["controllerId"] = this->_myAddressStr;
|
|
||||||
record["config"] = *config;
|
|
||||||
table = "Network";
|
|
||||||
} else if (objtype == "trace") {
|
|
||||||
record = *config;
|
|
||||||
table = "RemoteTrace";
|
|
||||||
} else if (objtype == "_delete_network") {
|
|
||||||
deleteId = (*config)["id"];
|
|
||||||
table = "Network";
|
|
||||||
} else if (objtype == "_delete_member") {
|
|
||||||
deleteId = (*config)["nwid"];
|
|
||||||
deleteId.push_back('-');
|
|
||||||
const std::string tmp = (*config)["id"];
|
|
||||||
deleteId.append(tmp);
|
|
||||||
table = "Member";
|
|
||||||
}
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update record creation): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
|
|
||||||
table = (const char *)0;
|
|
||||||
} catch (R::Error &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update record creation): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
|
|
||||||
table = (const char *)0;
|
|
||||||
} catch ( ... ) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update record creation): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
|
||||||
table = (const char *)0;
|
|
||||||
}
|
|
||||||
delete config;
|
|
||||||
if (!table)
|
|
||||||
continue;
|
|
||||||
const std::string jdump(OSUtils::jsonDump(record,-1));
|
|
||||||
|
|
||||||
while (_run == 1) {
|
|
||||||
try {
|
|
||||||
if (!rdb)
|
|
||||||
rdb = R::connect(this->_host,this->_port,this->_auth);
|
|
||||||
if (rdb) {
|
|
||||||
if (deleteId.length() > 0) {
|
|
||||||
//printf("DELETE: %s" ZT_EOL_S,deleteId.c_str());
|
|
||||||
R::db(this->_db).table(table).get(deleteId).delete_().run(*rdb);
|
|
||||||
} else {
|
|
||||||
//printf("UPSERT: %s" ZT_EOL_S,record.dump().c_str());
|
|
||||||
R::db(this->_db).table(table).insert(R::Datum::from_json(jdump),R::optargs("conflict","update","return_changes",false)).run(*rdb);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): connect failed (will retry)" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
|
||||||
rdb.reset();
|
|
||||||
}
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what(),jdump.c_str());
|
|
||||||
rdb.reset();
|
|
||||||
} catch (R::Error &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str(),jdump.c_str());
|
|
||||||
rdb.reset();
|
|
||||||
} catch ( ... ) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): unknown exception [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),jdump.c_str());
|
|
||||||
rdb.reset();
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update outer loop): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
|
|
||||||
} catch (R::Error &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update outer loop): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
|
|
||||||
} catch ( ... ) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update outer loop): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_onlineNotificationThread = std::thread([this]() {
|
|
||||||
int64_t lastUpdatedNetworkStatus = 0;
|
|
||||||
std::unordered_map< std::pair<uint64_t,uint64_t>,int64_t,_PairHasher > lastOnlineCumulative;
|
|
||||||
try {
|
|
||||||
std::unique_ptr<R::Connection> rdb;
|
|
||||||
while (_run == 1) {
|
|
||||||
try {
|
|
||||||
if (!rdb) {
|
|
||||||
_connected = 0;
|
|
||||||
rdb = R::connect(this->_host,this->_port,this->_auth);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rdb) {
|
|
||||||
_connected = 1;
|
|
||||||
R::Array batch;
|
|
||||||
R::Object tmpobj;
|
|
||||||
|
|
||||||
std::unordered_map< std::pair<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_PairHasher > lastOnline;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(_lastOnline_l);
|
|
||||||
lastOnline.swap(_lastOnline);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto i=lastOnline.begin();i!=lastOnline.end();++i) {
|
|
||||||
lastOnlineCumulative[i->first] = i->second.first;
|
|
||||||
char tmp[64],tmp2[64];
|
|
||||||
OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx-%.10llx",i->first.first,i->first.second);
|
|
||||||
tmpobj["id"] = tmp;
|
|
||||||
tmpobj["ts"] = i->second.first;
|
|
||||||
tmpobj["phy"] = i->second.second.toIpString(tmp2);
|
|
||||||
batch.emplace_back(tmpobj);
|
|
||||||
if (batch.size() >= 1024) {
|
|
||||||
R::db(this->_db).table("MemberStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
|
|
||||||
batch.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (batch.size() > 0) {
|
|
||||||
R::db(this->_db).table("MemberStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
|
|
||||||
batch.clear();
|
|
||||||
}
|
|
||||||
tmpobj.clear();
|
|
||||||
|
|
||||||
const int64_t now = OSUtils::now();
|
|
||||||
if ((now - lastUpdatedNetworkStatus) > 10000) {
|
|
||||||
lastUpdatedNetworkStatus = now;
|
|
||||||
|
|
||||||
std::vector< std::pair< uint64_t,std::shared_ptr<_Network> > > networks;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(_networks_l);
|
|
||||||
networks.reserve(_networks.size() + 1);
|
|
||||||
for(auto i=_networks.begin();i!=_networks.end();++i)
|
|
||||||
networks.push_back(*i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto i=networks.begin();i!=networks.end();++i) {
|
|
||||||
char tmp[64];
|
|
||||||
Utils::hex(i->first,tmp);
|
|
||||||
tmpobj["id"] = tmp;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l2(i->second->lock);
|
|
||||||
tmpobj["authorizedMemberCount"] = i->second->authorizedMembers.size();
|
|
||||||
tmpobj["totalMemberCount"] = i->second->members.size();
|
|
||||||
unsigned long onlineMemberCount = 0;
|
|
||||||
for(auto m=i->second->members.begin();m!=i->second->members.end();++m) {
|
|
||||||
auto lo = lastOnlineCumulative.find(std::pair<uint64_t,uint64_t>(i->first,m->first));
|
|
||||||
if (lo != lastOnlineCumulative.end()) {
|
|
||||||
if ((now - lo->second) <= (ZT_NETWORK_AUTOCONF_DELAY * 2))
|
|
||||||
++onlineMemberCount;
|
|
||||||
else lastOnlineCumulative.erase(lo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpobj["onlineMemberCount"] = onlineMemberCount;
|
|
||||||
tmpobj["bridgeCount"] = i->second->activeBridgeMembers.size();
|
|
||||||
tmpobj["ts"] = now;
|
|
||||||
}
|
|
||||||
batch.emplace_back(tmpobj);
|
|
||||||
if (batch.size() >= 1024) {
|
|
||||||
R::db(this->_db).table("NetworkStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
|
|
||||||
batch.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (batch.size() > 0) {
|
|
||||||
R::db(this->_db).table("NetworkStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
|
|
||||||
batch.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (node status update): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
|
|
||||||
rdb.reset();
|
|
||||||
} catch (R::Error &e) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (node status update): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
|
|
||||||
rdb.reset();
|
|
||||||
} catch ( ... ) {
|
|
||||||
fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (node status update): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
|
||||||
rdb.reset();
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
|
||||||
}
|
|
||||||
} catch ( ... ) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
_heartbeatThread = std::thread([this]() {
|
|
||||||
try {
|
|
||||||
R::Object controllerRecord;
|
|
||||||
std::unique_ptr<R::Connection> rdb;
|
|
||||||
|
|
||||||
{
|
|
||||||
char publicId[1024];
|
|
||||||
//char secretId[1024];
|
|
||||||
char hostname[1024];
|
|
||||||
this->_myId.toString(false,publicId);
|
|
||||||
//this->_myId.toString(true,secretId);
|
|
||||||
if (gethostname(hostname,sizeof(hostname)) != 0) {
|
|
||||||
hostname[0] = (char)0;
|
|
||||||
} else {
|
|
||||||
for(int i=0;i<sizeof(hostname);++i) {
|
|
||||||
if ((hostname[i] == '.')||(hostname[i] == 0)) {
|
|
||||||
hostname[i] = (char)0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
controllerRecord["id"] = this->_myAddressStr.c_str();
|
|
||||||
controllerRecord["publicIdentity"] = publicId;
|
|
||||||
//controllerRecord["secretIdentity"] = secretId;
|
|
||||||
if (hostname[0])
|
|
||||||
controllerRecord["clusterHost"] = hostname;
|
|
||||||
controllerRecord["vMajor"] = ZEROTIER_ONE_VERSION_MAJOR;
|
|
||||||
controllerRecord["vMinor"] = ZEROTIER_ONE_VERSION_MINOR;
|
|
||||||
controllerRecord["vRev"] = ZEROTIER_ONE_VERSION_REVISION;
|
|
||||||
controllerRecord["vBuild"] = ZEROTIER_ONE_VERSION_BUILD;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (_run == 1) {
|
|
||||||
try {
|
|
||||||
if (!rdb)
|
|
||||||
rdb = R::connect(this->_host,this->_port,this->_auth);
|
|
||||||
if (rdb) {
|
|
||||||
controllerRecord["lastAlive"] = OSUtils::now();
|
|
||||||
//printf("HEARTBEAT: %s" ZT_EOL_S,tmp);
|
|
||||||
R::db(this->_db).table("Controller",R::optargs("read_mode","outdated")).insert(controllerRecord,R::optargs("conflict","update")).run(*rdb);
|
|
||||||
}
|
|
||||||
} catch ( ... ) {
|
|
||||||
rdb.reset();
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
|
||||||
}
|
|
||||||
} catch ( ... ) {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
RethinkDB::~RethinkDB()
|
|
||||||
{
|
|
||||||
_run = 0;
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
_commitQueue.stop();
|
|
||||||
for(int t=0;t<ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS;++t)
|
|
||||||
_commitThread[t].join();
|
|
||||||
if (_membersDbWatcherConnection)
|
|
||||||
((R::Connection *)_membersDbWatcherConnection)->close();
|
|
||||||
if (_networksDbWatcherConnection)
|
|
||||||
((R::Connection *)_networksDbWatcherConnection)->close();
|
|
||||||
_membersDbWatcher.join();
|
|
||||||
_networksDbWatcher.join();
|
|
||||||
_heartbeatThread.join();
|
|
||||||
_onlineNotificationThread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RethinkDB::waitForReady()
|
|
||||||
{
|
|
||||||
while (_ready > 0) {
|
|
||||||
if (!_waitNoticePrinted) {
|
|
||||||
_waitNoticePrinted = true;
|
|
||||||
fprintf(stderr,"[%s] NOTICE: %.10llx controller RethinkDB waiting for initial data download..." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
|
|
||||||
}
|
|
||||||
_readyLock.lock();
|
|
||||||
_readyLock.unlock();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RethinkDB::isReady()
|
|
||||||
{
|
|
||||||
return ((_ready)&&(_connected));
|
|
||||||
}
|
|
||||||
|
|
||||||
void RethinkDB::save(nlohmann::json *orig,nlohmann::json &record)
|
|
||||||
{
|
|
||||||
if (!record.is_object()) // sanity check
|
|
||||||
return;
|
|
||||||
waitForReady();
|
|
||||||
if (orig) {
|
|
||||||
if (*orig != record) {
|
|
||||||
record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1;
|
|
||||||
_commitQueue.post(new nlohmann::json(record));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
record["revision"] = 1;
|
|
||||||
_commitQueue.post(new nlohmann::json(record));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RethinkDB::eraseNetwork(const uint64_t networkId)
|
|
||||||
{
|
|
||||||
char tmp2[24];
|
|
||||||
waitForReady();
|
|
||||||
Utils::hex(networkId,tmp2);
|
|
||||||
json *tmp = new json();
|
|
||||||
(*tmp)["id"] = tmp2;
|
|
||||||
(*tmp)["objtype"] = "_delete_network"; // pseudo-type, tells thread to delete network
|
|
||||||
_commitQueue.post(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RethinkDB::eraseMember(const uint64_t networkId,const uint64_t memberId)
|
|
||||||
{
|
|
||||||
char tmp2[24];
|
|
||||||
json *tmp = new json();
|
|
||||||
waitForReady();
|
|
||||||
Utils::hex(networkId,tmp2);
|
|
||||||
(*tmp)["nwid"] = tmp2;
|
|
||||||
Utils::hex10(memberId,tmp2);
|
|
||||||
(*tmp)["id"] = tmp2;
|
|
||||||
(*tmp)["objtype"] = "_delete_member"; // pseudo-type, tells thread to delete network
|
|
||||||
_commitQueue.post(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RethinkDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> l(_lastOnline_l);
|
|
||||||
std::pair<int64_t,InetAddress> &i = _lastOnline[std::pair<uint64_t,uint64_t>(networkId,memberId)];
|
|
||||||
i.first = OSUtils::now();
|
|
||||||
if (physicalAddress)
|
|
||||||
i.second = physicalAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif // ZT_CONTROLLER_USE_RETHINKDB
|
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZeroTier One - Network Virtualization Everywhere
|
|
||||||
* Copyright (C) 2011-2018 ZeroTier, Inc.
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef ZT_CONTROLLER_USE_RETHINKDB
|
|
||||||
|
|
||||||
#ifndef ZT_CONTROLLER_RETHINKDB_HPP
|
|
||||||
#define ZT_CONTROLLER_RETHINKDB_HPP
|
|
||||||
|
|
||||||
#include "DB.hpp"
|
|
||||||
|
|
||||||
#define ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS 4
|
|
||||||
|
|
||||||
namespace ZeroTier
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A controller database driver that talks to RethinkDB
|
|
||||||
*
|
|
||||||
* This is for use with ZeroTier Central. Others are free to build and use it
|
|
||||||
* but be aware that we might change it at any time.
|
|
||||||
*/
|
|
||||||
class RethinkDB : public DB
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path);
|
|
||||||
virtual ~RethinkDB();
|
|
||||||
|
|
||||||
virtual bool waitForReady();
|
|
||||||
virtual bool isReady();
|
|
||||||
virtual void save(nlohmann::json *orig,nlohmann::json &record);
|
|
||||||
virtual void eraseNetwork(const uint64_t networkId);
|
|
||||||
virtual void eraseMember(const uint64_t networkId,const uint64_t memberId);
|
|
||||||
virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
struct _PairHasher
|
|
||||||
{
|
|
||||||
inline std::size_t operator()(const std::pair<uint64_t,uint64_t> &p) const { return (std::size_t)(p.first ^ p.second); }
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string _host;
|
|
||||||
std::string _db;
|
|
||||||
std::string _auth;
|
|
||||||
int _port;
|
|
||||||
|
|
||||||
void *_networksDbWatcherConnection;
|
|
||||||
void *_membersDbWatcherConnection;
|
|
||||||
std::thread _networksDbWatcher;
|
|
||||||
std::thread _membersDbWatcher;
|
|
||||||
|
|
||||||
BlockingQueue< nlohmann::json * > _commitQueue;
|
|
||||||
std::thread _commitThread[ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS];
|
|
||||||
|
|
||||||
std::unordered_map< std::pair<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_PairHasher > _lastOnline;
|
|
||||||
mutable std::mutex _lastOnline_l;
|
|
||||||
std::thread _onlineNotificationThread;
|
|
||||||
|
|
||||||
std::thread _heartbeatThread;
|
|
||||||
|
|
||||||
mutable std::mutex _readyLock; // locked until ready
|
|
||||||
std::atomic<int> _ready,_connected,_run;
|
|
||||||
mutable volatile bool _waitNoticePrinted;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZeroTier
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // ZT_CONTROLLER_USE_RETHINKDB
|
|
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"configVersion": 1,
|
|
||||||
"defaultCentral": "@my.zerotier.com",
|
|
||||||
"defaultController": "@my.zerotier.com",
|
|
||||||
"defaultOne": "@local",
|
|
||||||
"things": {
|
|
||||||
"local": {
|
|
||||||
"auth": "local_service_auth_token_replaced_automatically",
|
|
||||||
"type": "one",
|
|
||||||
"url": "http://127.0.0.1:9993/"
|
|
||||||
},
|
|
||||||
"my.zerotier.com": {
|
|
||||||
"auth": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
|
||||||
"type": "central",
|
|
||||||
"url": "https://my.zerotier.com/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
FROM node:4.4
|
|
||||||
EXPOSE 8080/tcp 9993/udp
|
|
||||||
|
|
||||||
# Install ZT network conf files
|
|
||||||
RUN mkdir -p /var/lib/zerotier-one/networks.d
|
|
||||||
ADD *.conf /var/lib/zerotier-one/networks.d/
|
|
||||||
ADD *.conf /
|
|
||||||
ADD zerotier-one /
|
|
||||||
ADD zerotier-cli /
|
|
||||||
ADD .zerotierCliSettings /
|
|
||||||
|
|
||||||
# Install App
|
|
||||||
ADD server.js /
|
|
||||||
|
|
||||||
# script which will start/auth VM on ZT network
|
|
||||||
ADD entrypoint.sh /
|
|
||||||
RUN chmod -v +x /entrypoint.sh
|
|
||||||
|
|
||||||
CMD ["./entrypoint.sh"]
|
|
@ -1,150 +0,0 @@
|
|||||||
Kubernetes + ZeroTier
|
|
||||||
====
|
|
||||||
|
|
||||||
A self-authorizing Kubernetes cluster deployment over a private ZeroTier network.
|
|
||||||
|
|
||||||
This is a quick tutorial for setting up a Kubernetes deployment which can self-authorize each new replica onto your private ZeroTier network with no additional configuration needed when you scale. The Kubernetes-specific instructions and content is based on the [hellonode](http://kubernetes.io/docs/hellonode/) tutorial. All of the files discussed below can be found [here]();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Preliminary tasks
|
|
||||||
|
|
||||||
**Step 1: Go to [my.zerotier.com](https://my.zerotier.com) and generate a network controller API key. This key will be used by ZeroTier to automatically authorize new instances of your VMs to join your secure deployment network during replication.**
|
|
||||||
|
|
||||||
**Step 2: Create a new `private` network. Take note of the network ID, henceforth: `nwid`**
|
|
||||||
|
|
||||||
**Step 3: Follow the instructions from the [hellonode](ttp://kubernetes.io/docs/hellonode/) tutorial to set up your development system.**
|
|
||||||
|
|
||||||
***
|
|
||||||
## Construct docker image
|
|
||||||
|
|
||||||
**Step 4: Create necessary files for inclusion into image, your resultant directory should contain:**
|
|
||||||
|
|
||||||
- `ztkube/<nwid>.conf`
|
|
||||||
- `ztkube/Dockerfile`
|
|
||||||
- `ztkube/entrypoint.sh`
|
|
||||||
- `ztkube/server.js`
|
|
||||||
- `ztkube/zerotier-cli`
|
|
||||||
- `ztkube/zerotier-one`
|
|
||||||
|
|
||||||
Start by creating a build directory to copy all required files into `mkdir ztkube`. Then build the following:
|
|
||||||
- `make one`
|
|
||||||
- `make cli`
|
|
||||||
|
|
||||||
Add the following files to the `ztkube` directory. These files will be compiled into the Docker image.
|
|
||||||
|
|
||||||
- Create an empty `<nwid>.conf` file to specify the private deployment network you created in *Step 2*:
|
|
||||||
|
|
||||||
- Create a CLI tool config file `.zerotierCliSettings` which should only contain your network controller API key to authorize new devices on your network (the local service API key will be filled in automatically). In this example the default controller is hosted by us at [my.zerotier.com](https://my.zerotier.com). Alternatively, you can host your own network controller but you'll need to modify the CLI config file accordingly.
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"configVersion": 1,
|
|
||||||
"defaultCentral": "@my.zerotier.com",
|
|
||||||
"defaultController": "@my.zerotier.com",
|
|
||||||
"defaultOne": "@local",
|
|
||||||
"things": {
|
|
||||||
"local": {
|
|
||||||
"auth": "local_service_auth_token_replaced_automatically",
|
|
||||||
"type": "one",
|
|
||||||
"url": "http://127.0.0.1:9993/"
|
|
||||||
},
|
|
||||||
"my.zerotier.com": {
|
|
||||||
"auth": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
|
||||||
"type": "central",
|
|
||||||
"url": "https://my.zerotier.com/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
- Create a `Dockerfile` which will copy the ZeroTier service as well as the ZeroTier CLI to the image:
|
|
||||||
|
|
||||||
```
|
|
||||||
FROM node:4.4
|
|
||||||
EXPOSE 8080/tcp 9993/udp
|
|
||||||
|
|
||||||
# Install ZT network conf files
|
|
||||||
RUN mkdir -p /var/lib/zerotier-one/networks.d
|
|
||||||
ADD *.conf /var/lib/zerotier-one/networks.d/
|
|
||||||
ADD *.conf /
|
|
||||||
ADD zerotier-one /
|
|
||||||
ADD zerotier-cli /
|
|
||||||
ADD .zerotierCliSettings /
|
|
||||||
|
|
||||||
# Install App
|
|
||||||
ADD server.js /
|
|
||||||
|
|
||||||
# script which will start/auth VM on ZT network
|
|
||||||
ADD entrypoint.sh /
|
|
||||||
RUN chmod -v +x /entrypoint.sh
|
|
||||||
|
|
||||||
CMD ["./entrypoint.sh"]
|
|
||||||
```
|
|
||||||
|
|
||||||
- Create the `entrypoint.sh` script which will start the ZeroTier service in the VM, attempt to join your deployment network and automatically authorize the new VM if your network is set to private:
|
|
||||||
|
|
||||||
```
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo '*** ZeroTier-Kubernetes self-auth test script'
|
|
||||||
chown -R daemon /var/lib/zerotier-one
|
|
||||||
chgrp -R daemon /var/lib/zerotier-one
|
|
||||||
su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
|
|
||||||
dev=""
|
|
||||||
nwconf=$(ls *.conf)
|
|
||||||
nwid="${nwconf%.*}"
|
|
||||||
|
|
||||||
sleep 10
|
|
||||||
dev=$(cat /var/lib/zerotier-one/identity.public| cut -d ':' -f 1)
|
|
||||||
|
|
||||||
echo '*** Joining'
|
|
||||||
./zerotier-cli join "$nwid".conf
|
|
||||||
# Fill out local service auth token
|
|
||||||
AUTHTOKEN=$(cat /var/lib/zerotier-one/authtoken.secret)
|
|
||||||
sed "s|\local_service_auth_token_replaced_automatically|${AUTHTOKEN}|" .zerotierCliSettings > /root/.zerotierCliSettings
|
|
||||||
echo '*** Authorizing'
|
|
||||||
./zerotier-cli net-auth @my.zerotier.com "$nwid" "$dev"
|
|
||||||
echo '*** Cleaning up' # Remove controller auth token
|
|
||||||
rm -rf .zerotierCliSettings /root/.zerotierCliSettings
|
|
||||||
node server.js
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step 5: Build the image:**
|
|
||||||
|
|
||||||
- `docker build -t gcr.io/$PROJECT_ID/hello-node .`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Step 6: Push the docker image to your *Container Registry***
|
|
||||||
|
|
||||||
- `gcloud docker push gcr.io/$PROJECT_ID/hello-node:v1`
|
|
||||||
|
|
||||||
***
|
|
||||||
## Deploy!
|
|
||||||
|
|
||||||
**Step 7: Create Kubernetes Cluster**
|
|
||||||
|
|
||||||
- `gcloud config set compute/zone us-central1-a`
|
|
||||||
|
|
||||||
- `gcloud container clusters create hello-world`
|
|
||||||
|
|
||||||
- `gcloud container clusters get-credentials hello-world`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Step 8: Create your pod**
|
|
||||||
|
|
||||||
- `kubectl run hello-node --image=gcr.io/$PROJECT_ID/hello-node:v1 --port=8080`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Step 9: Scale**
|
|
||||||
|
|
||||||
- `kubectl scale deployment hello-node --replicas=4`
|
|
||||||
|
|
||||||
***
|
|
||||||
## Verify
|
|
||||||
|
|
||||||
Now, after a minute or so you can use `zerotier-cli net-members <nwid>` to show all of your VM instances on your ZeroTier deployment network. If you haven't [configured your local CLI](https://github.com/zerotier/ZeroTierOne/tree/dev/cli), you can simply log into [my.zerotier.com](https://my.zerotier.com), go to *Networks -> nwid* to check that your VMs are indeed members of your private network. You should also note that the `entrypoint.sh` script will automatically delete your network controller API key once it has authorized your VM. This is merely a security measure and can be removed if needed.
|
|
@ -1,23 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo '*** ZeroTier-Kubernetes self-auth test script'
|
|
||||||
chown -R daemon /var/lib/zerotier-one
|
|
||||||
chgrp -R daemon /var/lib/zerotier-one
|
|
||||||
su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
|
|
||||||
dev=""
|
|
||||||
nwconf=$(ls *.conf)
|
|
||||||
nwid="${nwconf%.*}"
|
|
||||||
|
|
||||||
sleep 10
|
|
||||||
dev=$(cat /var/lib/zerotier-one/identity.public| cut -d ':' -f 1)
|
|
||||||
|
|
||||||
echo '*** Joining'
|
|
||||||
./zerotier-cli join "$nwid".conf
|
|
||||||
# Fill out local service auth token
|
|
||||||
AUTHTOKEN=$(cat /var/lib/zerotier-one/authtoken.secret)
|
|
||||||
sed "s|\local_service_auth_token_replaced_automatically|${AUTHTOKEN}|" .zerotierCliSettings > /root/.zerotierCliSettings
|
|
||||||
echo '*** Authorizing'
|
|
||||||
./zerotier-cli net-auth @my.zerotier.com "$nwid" "$dev"
|
|
||||||
echo '*** Cleaning up' # Remove controller auth token
|
|
||||||
rm -rf .zerotierCliSettings /root/.zerotierCliSettings
|
|
||||||
node server.js
|
|
@ -1,8 +0,0 @@
|
|||||||
var http = require('http');
|
|
||||||
var handleRequest = function(request, response) {
|
|
||||||
console.log('Received request for URL: ' + request.url);
|
|
||||||
response.writeHead(200);
|
|
||||||
response.end('Hello World!');
|
|
||||||
};
|
|
||||||
var www = http.createServer(handleRequest);
|
|
||||||
www.listen(8080);
|
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -1,3 +1,9 @@
|
|||||||
|
zerotier-one (1.2.99) unstable; urgency=medium
|
||||||
|
|
||||||
|
* 1.4.0pre release
|
||||||
|
|
||||||
|
-- Adam Ierymenko <adam.ierymenko@zerotier.com> Thu, 27 Jun 2019 01:00:00 -0700
|
||||||
|
|
||||||
zerotier-one (1.2.12) unstable; urgency=medium
|
zerotier-one (1.2.12) unstable; urgency=medium
|
||||||
|
|
||||||
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
* See https://github.com/zerotier/ZeroTierOne for release notes.
|
||||||
|
@ -664,7 +664,7 @@
|
|||||||
<key>USE_HFS+_COMPRESSION</key>
|
<key>USE_HFS+_COMPRESSION</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>VERSION</key>
|
<key>VERSION</key>
|
||||||
<string>1.2.12</string>
|
<string>1.2.99</string>
|
||||||
</dict>
|
</dict>
|
||||||
<key>PROJECT_COMMENTS</key>
|
<key>PROJECT_COMMENTS</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
From: https://raw.githubusercontent.com/zerotier/ZeroTierOne/master/COPYING
|
|
||||||
|
|
||||||
LICENSE
|
|
||||||
|
|
||||||
ZeroTier One 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.
|
|
||||||
|
|
||||||
See the file ‘LICENSE.GPL-3’ for the text of the GNU GPL version 3.
|
|
||||||
If that file is not present, see <http://www.gnu.org/licenses/>.
|
|
@ -1,5 +0,0 @@
|
|||||||
VERIFICATION
|
|
||||||
Verification is intended to assist the Chocolatey moderators and community
|
|
||||||
in verifying that this package's contents are trustworthy.
|
|
||||||
|
|
||||||
Our MSI installer should be signed by ZeroTier, Inc. using a certificate from DigiCert.
|
|
@ -1,8 +0,0 @@
|
|||||||
$packageName = 'zerotier-one'
|
|
||||||
$installerType = 'msi'
|
|
||||||
$url = 'https://download.zerotier.com/RELEASES/1.2.4/dist/ZeroTier%20One.msi'
|
|
||||||
$url64 = 'https://download.zerotier.com/RELEASES/1.2.4/dist/ZeroTier%20One.msi'
|
|
||||||
$silentArgs = '/quiet'
|
|
||||||
$validExitCodes = @(0,3010)
|
|
||||||
|
|
||||||
Install-ChocolateyPackage $packageName $installerType $silentArgs $url $url64 -validExitCodes $validExitCodes
|
|
@ -1,30 +0,0 @@
|
|||||||
$ErrorActionPreference = 'Stop';
|
|
||||||
|
|
||||||
$packageName = 'zerotier-one'
|
|
||||||
$softwareName = 'ZeroTier One*'
|
|
||||||
$installerType = 'MSI'
|
|
||||||
|
|
||||||
$silentArgs = '/qn /norestart'
|
|
||||||
$validExitCodes = @(0, 3010, 1605, 1614, 1641)
|
|
||||||
$uninstalled = $false
|
|
||||||
|
|
||||||
[array]$key = Get-UninstallRegistryKey -SoftwareName $softwareName
|
|
||||||
|
|
||||||
if ($key.Count -eq 1) {
|
|
||||||
$key | % {
|
|
||||||
$silentArgs = "$($_.PSChildName) $silentArgs"
|
|
||||||
$file = ''
|
|
||||||
Uninstall-ChocolateyPackage -PackageName $packageName `
|
|
||||||
-FileType $installerType `
|
|
||||||
-SilentArgs "$silentArgs" `
|
|
||||||
-ValidExitCodes $validExitCodes `
|
|
||||||
-File "$file"
|
|
||||||
}
|
|
||||||
} elseif ($key.Count -eq 0) {
|
|
||||||
Write-Warning "$packageName has already been uninstalled by other means."
|
|
||||||
} elseif ($key.Count -gt 1) {
|
|
||||||
Write-Warning "$key.Count matches found!"
|
|
||||||
Write-Warning "To prevent accidental data loss, no programs will be uninstalled."
|
|
||||||
Write-Warning "Please alert package maintainer the following keys were matched:"
|
|
||||||
$key | % {Write-Warning "- $_.DisplayName"}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Read this before creating packages: https://github.com/chocolatey/chocolatey/wiki/CreatePackages -->
|
|
||||||
<!-- It is especially important to read the above link to understand additional requirements when publishing packages to the community feed aka dot org (https://chocolatey.org/packages). -->
|
|
||||||
|
|
||||||
<!-- Test your packages in a test environment: https://github.com/chocolatey/chocolatey-test-environment -->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Reference. Chocolatey uses a special version of NuGet.Core that allows us to do more than was initially possible. As such there are certain things to be aware of:
|
|
||||||
|
|
||||||
* the package xmlns schema url may cause issues with nuget.exe
|
|
||||||
* Any of the following elements can ONLY be used by choco tools - projectSourceUrl, docsUrl, mailingListUrl, bugTrackerUrl, packageSourceUrl, provides, conflicts, replaces
|
|
||||||
* nuget.exe can still install packages with those elements but they are ignored. Any authoring tools or commands will error on those elements
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- You can embed software files directly into packages, as long as you are not bound by distribution rights. -->
|
|
||||||
<!-- * If you are an organization making private packages, you probably have no issues here -->
|
|
||||||
<!-- * If you are releasing to the community feed, you need to consider distribution rights. -->
|
|
||||||
<!-- Do not remove this test for UTF-8: if “Ω” doesn’t appear as greek uppercase omega letter enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. -->
|
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
|
||||||
<metadata>
|
|
||||||
<!-- == PACKAGE SPECIFIC SECTION == -->
|
|
||||||
<!-- This section is about this package, although id and version have ties back to the software -->
|
|
||||||
<!-- id is lowercase and if you want a good separator for words, use '-', not '.'. Dots are only acceptable as suffixes for certain types of packages, e.g. .install, .portable, .extension, .template -->
|
|
||||||
<!-- If the software is cross-platform, attempt to use the same id as the debian/rpm package(s) if possible. -->
|
|
||||||
<id>zerotier-one</id>
|
|
||||||
<!-- version should MATCH as closely as possible with the underlying software -->
|
|
||||||
<!-- Is the version a prerelease of a version? https://docs.nuget.org/create/versioning#creating-prerelease-packages -->
|
|
||||||
<!-- Note that unstable versions like 0.0.1 can be considered a released version, but it's possible that one can release a 0.0.1-beta before you release a 0.0.1 version. If the version number is final, that is considered a released version and not a prerelease. -->
|
|
||||||
<version>1.2.12</version>
|
|
||||||
<!-- <packageSourceUrl>Where is this Chocolatey package located (think GitHub)? packageSourceUrl is highly recommended for the community feed</packageSourceUrl>-->
|
|
||||||
<!-- owners is a poor name for maintainers of the package. It sticks around by this name for compatibility reasons. It basically means you. -->
|
|
||||||
<!--<owners>ZeroTier, Inc.</owners>-->
|
|
||||||
<!-- ============================== -->
|
|
||||||
|
|
||||||
<!-- == SOFTWARE SPECIFIC SECTION == -->
|
|
||||||
<!-- This section is about the software itself -->
|
|
||||||
<title>zerotier-one (Install)</title>
|
|
||||||
<authors>ZeroTier, Inc.</authors>
|
|
||||||
<!-- projectUrl is required for the community feed -->
|
|
||||||
<projectUrl>https://www.zerotier.com/</projectUrl>
|
|
||||||
<!--<iconUrl>https://www.zerotier.com/img/ZeroTierIcon.png</iconUrl>-->
|
|
||||||
<!-- <copyright>2011-2016 ZeroTier, Inc.</copyright> -->
|
|
||||||
<!-- If there is a license Url available, it is is required for the community feed -->
|
|
||||||
<!-- <licenseUrl>https://raw.githubusercontent.com/zerotier/ZeroTierOne/master/COPYING</licenseUrl>
|
|
||||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>-->
|
|
||||||
<!--<projectSourceUrl>https://github.com/zerotier/ZeroTierOne</projectSourceUrl>-->
|
|
||||||
<!--<docsUrl>https://www.zerotier.com/</docsUrl>-->
|
|
||||||
<!--<mailingListUrl></mailingListUrl>-->
|
|
||||||
<!--<bugTrackerUrl>https://github.com/zerotier/ZeroTierOne/issues</bugTrackerUrl>-->
|
|
||||||
<tags>zerotier-one admin</tags>
|
|
||||||
<summary>ZeroTier One Virtual Network Endpoint for Windows</summary>
|
|
||||||
<description>ZeroTier is a smart switch for Earth with VLAN capability. See https://www.zerotier.com/ for more information.</description>
|
|
||||||
<!-- <releaseNotes>__REPLACE_OR_REMOVE__MarkDown_Okay</releaseNotes> -->
|
|
||||||
<!-- =============================== -->
|
|
||||||
|
|
||||||
<!-- Specifying dependencies and version ranges? https://docs.nuget.org/create/versioning#specifying-version-ranges-in-.nuspec-files -->
|
|
||||||
<!--<dependencies>
|
|
||||||
<dependency id="" version="__MINIMUM_VERSION__" />
|
|
||||||
<dependency id="" version="[__EXACT_VERSION__]" />
|
|
||||||
<dependency id="" version="[_MIN_VERSION_INCLUSIVE, MAX_VERSION_INCLUSIVE]" />
|
|
||||||
<dependency id="" version="[_MIN_VERSION_INCLUSIVE, MAX_VERSION_EXCLUSIVE)" />
|
|
||||||
<dependency id="" />
|
|
||||||
<dependency id="chocolatey-uninstall.extension" />
|
|
||||||
</dependencies>-->
|
|
||||||
<!-- chocolatey-uninstall.extension - If supporting 0.9.9.x (or below) and including a chocolateyUninstall.ps1 file to uninstall an EXE/MSI, you probably want to include chocolatey-uninstall.extension as a dependency. Please verify whether you are using a helper function from that package. -->
|
|
||||||
|
|
||||||
<!--<provides>NOT YET IMPLEMENTED</provides>-->
|
|
||||||
<!--<conflicts>NOT YET IMPLEMENTED</conflicts>-->
|
|
||||||
<!--<replaces>NOT YET IMPLEMENTED</replaces>-->
|
|
||||||
</metadata>
|
|
||||||
<files>
|
|
||||||
<!-- this section controls what actually gets packaged into the Chocolatey package -->
|
|
||||||
<file src="tools\**" target="tools" />
|
|
||||||
<!--Building from Linux? You may need this instead: <file src="tools/**" target="tools" />-->
|
|
||||||
</files>
|
|
||||||
</package>
|
|
@ -1,5 +1,5 @@
|
|||||||
Name: zerotier-one
|
Name: zerotier-one
|
||||||
Version: 1.2.12
|
Version: 1.2.99
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: ZeroTier One network virtualization service
|
Summary: ZeroTier One network virtualization service
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user