From 235f4762b700174c795b28de7d4fe2f70cddbcd8 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Mon, 6 Jul 2015 15:51:04 -0700
Subject: [PATCH] Plumbing for local interface addresses -- GitHub issue #180

---
 include/ZeroTierOne.h | 32 ++++++++++++++++++++++++++++++++
 node/Node.cpp         | 28 ++++++++++++++++++++++++++++
 node/Node.hpp         |  5 +++++
 node/Path.hpp         | 21 +++++++--------------
 node/RemotePath.hpp   | 16 ++++++++++++----
 5 files changed, 84 insertions(+), 18 deletions(-)

diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index b6ff69abe..4790795e2 100644
--- a/include/ZeroTierOne.h
+++ b/include/ZeroTierOne.h
@@ -628,6 +628,15 @@ typedef struct
 	unsigned long peerCount;
 } ZT1_PeerList;
 
+/**
+ * Local interface trust levels
+ */
+typedef enum {
+	ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL = 0,
+	ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_PRIVACY = 1,
+	ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 2
+} ZT1_LocalInterfaceAddressTrust;
+
 /**
  * An instance of a ZeroTier One node (opaque)
  */
@@ -958,6 +967,29 @@ ZT1_VirtualNetworkList *ZT1_Node_networks(ZT1_Node *node);
  */
 void ZT1_Node_freeQueryResult(ZT1_Node *node,void *qr);
 
+/**
+ * Add a local interface address
+ *
+ * Local interface addresses may be added if you want remote peers
+ * with whom you have a trust relatinship (e.g. common network membership)
+ * to receive information about these endpoints as potential endpoints for
+ * direct communication.
+ *
+ * Take care that these are never ZeroTier interface addresses, otherwise
+ * strange things might happen or they simply won't work.
+ *
+ * @param addr Local interface address
+ * @param metric Local interface metric
+ * @param trust How much do you trust the local network under this interface?
+ * @param reliable If nonzero, this interface doesn't link to anything behind a NAT or stateful firewall
+ */
+void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable);
+
+/**
+ * Clear local interface addresses
+ */
+void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node);
+
 /**
  * Set a network configuration master instance for this node
  *
diff --git a/node/Node.cpp b/node/Node.cpp
index 7c70db97f..057dc285c 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -426,6 +426,20 @@ void Node::freeQueryResult(void *qr)
 		::free(qr);
 }
 
+void Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable)
+{
+	Mutex::Lock _l(_directPaths_m);
+	_directPaths.push_back(Path(*(reinterpret_cast<const InetAddress *>(addr)),metric,(Path::Trust)trust,reliable != 0));
+	std::sort(_directPaths.begin(),_directPaths.end());
+	_directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end());
+}
+
+void Node::clearLocalInterfaceAddresses()
+{
+	Mutex::Lock _l(_directPaths_m);
+	_directPaths.clear();
+}
+
 void Node::setNetconfMaster(void *networkControllerInstance)
 {
 	RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance);
@@ -679,6 +693,20 @@ void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkControllerInstance)
 	} catch ( ... ) {}
 }
 
+void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable)
+{
+	try {
+		reinterpret_cast<ZeroTier::Node *>(node)->addLocalInterfaceAddress(addr,metric,trust,reliable);
+	} catch ( ... ) {}
+}
+
+void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node)
+{
+	try {
+		reinterpret_cast<ZeroTier::Node *>(node)->clearLocalInterfaceAddresses();
+	} catch ( ... ) {}
+}
+
 void ZT1_version(int *major,int *minor,int *revision,unsigned long *featureFlags)
 {
 	if (major) *major = ZEROTIER_ONE_VERSION_MAJOR;
diff --git a/node/Node.hpp b/node/Node.hpp
index cd8d68fc2..1c260545c 100644
--- a/node/Node.hpp
+++ b/node/Node.hpp
@@ -104,6 +104,8 @@ public:
 	ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid) const;
 	ZT1_VirtualNetworkList *networks() const;
 	void freeQueryResult(void *qr);
+	void addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable);
+	void clearLocalInterfaceAddresses();
 	void setNetconfMaster(void *networkControllerInstance);
 
 	// Internal functions ------------------------------------------------------
@@ -172,6 +174,9 @@ public:
 		return nw;
 	}
 
+	/**
+	 * @return Potential direct paths to me a.k.a. local interface addresses
+	 */
 	inline std::vector<Path> directPaths() const
 	{
 		Mutex::Lock _l(_directPaths_m);
diff --git a/node/Path.hpp b/node/Path.hpp
index b43b3f6d6..80b9a3c01 100644
--- a/node/Path.hpp
+++ b/node/Path.hpp
@@ -37,28 +37,27 @@ namespace ZeroTier {
 class Path
 {
 public:
+	// Must be the same values as ZT1_LocalInterfaceAddressTrust in ZeroTierOne.h
 	enum Trust
 	{
-		TRUST_NORMAL,
-		TRUST_PRIVACY,
-		TRUST_ULTIMATE
+		TRUST_NORMAL = 0,
+		TRUST_PRIVACY = 1,
+		TRUST_ULTIMATE = 2
 	};
 
 	Path() :
 		_addr(),
 		_metric(0),
 		_trust(TRUST_NORMAL),
-		_reliable(false),
-		_fixed(false)
+		_reliable(false)
 	{
 	}
 
-	Path(const InetAddress &addr,int metric,Trust trust,bool reliable,bool fixed) :
+	Path(const InetAddress &addr,int metric,Trust trust,bool reliable) :
 		_addr(addr),
 		_metric(metric),
 		_trust(trust),
-		_reliable(reliable),
-		_fixed(fixed)
+		_reliable(reliable)
 	{
 	}
 
@@ -82,11 +81,6 @@ public:
 	 */
 	inline bool reliable() const throw() { return _reliable; }
 
-	/**
-	 * @return Is this a fixed path?
-	 */
-	inline bool fixed() const throw() { return _fixed; }
-
 	/**
 	 * @return True if address is non-NULL
 	 */
@@ -105,7 +99,6 @@ protected:
 	int _metric; // negative == blacklisted
 	Trust _trust;
 	bool _reliable;
-	bool _fixed;
 };
 
 } // namespace ZeroTier
diff --git a/node/RemotePath.hpp b/node/RemotePath.hpp
index 47c8916da..5592c8e1f 100644
--- a/node/RemotePath.hpp
+++ b/node/RemotePath.hpp
@@ -52,18 +52,25 @@ public:
 	RemotePath() :
 		Path(),
 		_lastSend(0),
-		_lastReceived(0) {}
+		_lastReceived(0),
+		_fixed(false) {}
 
 	RemotePath(const InetAddress &addr,bool fixed) :
-		Path(addr,0,TRUST_NORMAL,false,fixed),
+		Path(addr,0,TRUST_NORMAL,false),
 		_lastSend(0),
-		_lastReceived(0) {}
+		_lastReceived(0),
+		_fixed(fixed) {}
 
 	inline uint64_t lastSend() const throw() { return _lastSend; }
 	inline uint64_t lastReceived() const throw() { return _lastReceived; }
 
 	/**
-	 * @param f New value of parent 'fixed' field
+	 * @return Is this a fixed path?
+	 */
+	inline bool fixed() const throw() { return _fixed; }
+
+	/**
+	 * @param f New value of fixed flag
 	 */
 	inline void setFixed(const bool f)
 		throw()
@@ -127,6 +134,7 @@ public:
 private:
 	uint64_t _lastSend;
 	uint64_t _lastReceived;
+	bool _fixed;
 };
 
 } // namespace ZeroTier