Membership.hpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /*
  2. * ZeroTier One - Network Virtualization Everywhere
  3. * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #ifndef ZT_MEMBERSHIP_HPP
  19. #define ZT_MEMBERSHIP_HPP
  20. #include <stdint.h>
  21. #include <map>
  22. #include "Constants.hpp"
  23. #include "../include/ZeroTierOne.h"
  24. #include "CertificateOfMembership.hpp"
  25. #include "Capability.hpp"
  26. #include "Tag.hpp"
  27. #include "Hashtable.hpp"
  28. #include "NetworkConfig.hpp"
  29. namespace ZeroTier {
  30. class RuntimeEnvironment;
  31. class Network;
  32. /**
  33. * A container for certificates of membership and other network credentials
  34. *
  35. * This is kind of analogous to a join table between Peer and Network. It is
  36. * presently held by the Network object for each participating Peer.
  37. *
  38. * This is not thread safe. It must be locked externally.
  39. */
  40. class Membership
  41. {
  42. private:
  43. // Tags and related state
  44. struct TState
  45. {
  46. TState() : lastPushed(0),lastReceived(0) {}
  47. // Last time we pushed OUR tag to this peer (with this ID)
  48. uint64_t lastPushed;
  49. // Last time we received THEIR tag (with this ID)
  50. uint64_t lastReceived;
  51. // THEIR tag
  52. Tag tag;
  53. };
  54. // Credentials and related state
  55. struct CState
  56. {
  57. CState() : lastPushed(0),lastReceived(0) {}
  58. // Last time we pushed OUR capability to this peer (with this ID)
  59. uint64_t lastPushed;
  60. // Last time we received THEIR capability (with this ID)
  61. uint64_t lastReceived;
  62. // THEIR capability
  63. Capability cap;
  64. };
  65. public:
  66. /**
  67. * A wrapper to iterate through member capabilities in ascending order of capability ID and return only valid ones
  68. */
  69. class CapabilityIterator
  70. {
  71. public:
  72. CapabilityIterator(const Membership &m) :
  73. _m(m),
  74. _i(m._caps.begin()),
  75. _e(m._caps.end())
  76. {
  77. }
  78. inline const Capability *next(const NetworkConfig &nconf)
  79. {
  80. while (_i != _e) {
  81. if ((_i->second.lastReceived)&&(_m.isCredentialTimestampValid(nconf,_i->second.cap)))
  82. return &((_i++)->second.cap);
  83. else ++_i;
  84. }
  85. return (const Capability *)0;
  86. }
  87. private:
  88. const Membership &_m;
  89. std::map<uint32_t,CState>::const_iterator _i,_e;
  90. };
  91. friend class CapabilityIterator;
  92. Membership() :
  93. _lastUpdatedMulticast(0),
  94. _lastPushAttempt(0),
  95. _lastPushedCom(0),
  96. _blacklistBefore(0),
  97. _com(),
  98. _caps(),
  99. _tags(8)
  100. {
  101. }
  102. /**
  103. * Send COM and other credentials to this peer if needed
  104. *
  105. * This checks last pushed times for our COM and for other credentials and
  106. * sends VERB_NETWORK_CREDENTIALS if the recipient might need them.
  107. *
  108. * @param RR Runtime environment
  109. * @param now Current time
  110. * @param peerAddress Address of member peer (the one that this Membership describes)
  111. * @param nconf My network config
  112. * @param cap Capability to send or 0 if none
  113. */
  114. void sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,const Capability *cap);
  115. /**
  116. * Check whether we should push MULTICAST_LIKEs to this peer
  117. *
  118. * @param now Current time
  119. * @return True if we should update multicasts
  120. */
  121. inline bool shouldLikeMulticasts(const uint64_t now) const { return ((now - _lastUpdatedMulticast) >= ZT_MULTICAST_ANNOUNCE_PERIOD); }
  122. /**
  123. * Set time we last updated multicasts for this peer
  124. *
  125. * @param now Current time
  126. */
  127. inline void likingMulticasts(const uint64_t now) { _lastUpdatedMulticast = now; }
  128. /**
  129. * @param nconf Our network config
  130. * @return True if this peer is allowed on this network at all
  131. */
  132. inline bool isAllowedOnNetwork(const NetworkConfig &nconf) const
  133. {
  134. if (nconf.isPublic())
  135. return true;
  136. if ((_blacklistBefore)&&(_com.timestamp().first <= _blacklistBefore))
  137. return false;
  138. return nconf.com.agreesWith(_com);
  139. }
  140. /**
  141. * @return True if this member has been on this network recently (or network is public)
  142. */
  143. inline bool recentlyAllowedOnNetwork(const NetworkConfig &nconf) const
  144. {
  145. if (nconf.isPublic())
  146. return true;
  147. if (_com) {
  148. const uint64_t a = _com.timestamp().first;
  149. if ((_blacklistBefore)&&(a <= _blacklistBefore))
  150. return false;
  151. const uint64_t b = nconf.com.timestamp().first;
  152. return ((a <= b) ? ((b - a) <= ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA) : true);
  153. }
  154. return false;
  155. }
  156. /**
  157. * Check whether a capability or tag is within its max delta from the timestamp of our network config and newer than any blacklist cutoff time
  158. *
  159. * @param cred Credential to check -- must have timestamp() accessor method
  160. * @return True if credential is NOT expired
  161. */
  162. template<typename C>
  163. inline bool isCredentialTimestampValid(const NetworkConfig &nconf,const C &cred) const
  164. {
  165. const uint64_t ts = cred.timestamp();
  166. const uint64_t delta = (ts >= nconf.timestamp) ? (ts - nconf.timestamp) : (nconf.timestamp - ts);
  167. return ((delta <= nconf.credentialTimeMaxDelta)&&(ts > _blacklistBefore));
  168. }
  169. /**
  170. * @param nconf Network configuration
  171. * @param id Tag ID
  172. * @return Pointer to tag or NULL if not found
  173. */
  174. inline const Tag *getTag(const NetworkConfig &nconf,const uint32_t id) const
  175. {
  176. const TState *t = _tags.get(id);
  177. return ((t) ? (((t->lastReceived != 0)&&(isCredentialTimestampValid(nconf,t->tag))) ? &(t->tag) : (const Tag *)0) : (const Tag *)0);
  178. }
  179. /**
  180. * @param nconf Network configuration
  181. * @param ids Array to store IDs into
  182. * @param values Array to store values into
  183. * @param maxTags Capacity of ids[] and values[]
  184. * @return Number of tags added to arrays
  185. */
  186. inline unsigned int getAllTags(const NetworkConfig &nconf,uint32_t *ids,uint32_t *values,unsigned int maxTags) const
  187. {
  188. unsigned int n = 0;
  189. uint32_t *id = (uint32_t *)0;
  190. TState *ts = (TState *)0;
  191. Hashtable<uint32_t,TState>::Iterator i(const_cast<Membership *>(this)->_tags);
  192. while (i.next(id,ts)) {
  193. if ((ts->lastReceived)&&(isCredentialTimestampValid(nconf,ts->tag))) {
  194. if (n >= maxTags)
  195. return n;
  196. ids[n] = *id;
  197. values[n] = ts->tag.value();
  198. }
  199. }
  200. return n;
  201. }
  202. /**
  203. * @param nconf Network configuration
  204. * @param id Capablity ID
  205. * @return Pointer to capability or NULL if not found
  206. */
  207. inline const Capability *getCapability(const NetworkConfig &nconf,const uint32_t id) const
  208. {
  209. std::map<uint32_t,CState>::const_iterator c(_caps.find(id));
  210. return ((c != _caps.end()) ? (((c->second.lastReceived != 0)&&(isCredentialTimestampValid(nconf,c->second.cap))) ? &(c->second.cap) : (const Capability *)0) : (const Capability *)0);
  211. }
  212. /**
  213. * Validate and add a credential if signature is okay and it's otherwise good
  214. *
  215. * @param RR Runtime environment
  216. * @param com Certificate of membership
  217. * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
  218. */
  219. int addCredential(const RuntimeEnvironment *RR,const CertificateOfMembership &com);
  220. /**
  221. * Validate and add a credential if signature is okay and it's otherwise good
  222. *
  223. * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
  224. */
  225. int addCredential(const RuntimeEnvironment *RR,const Tag &tag);
  226. /**
  227. * Validate and add a credential if signature is okay and it's otherwise good
  228. *
  229. * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
  230. */
  231. int addCredential(const RuntimeEnvironment *RR,const Capability &cap);
  232. /**
  233. * Blacklist COM, tags, and capabilities before this time
  234. *
  235. * @param ts Blacklist cutoff
  236. */
  237. inline void blacklistBefore(const uint64_t ts)
  238. {
  239. _blacklistBefore = ts;
  240. }
  241. /**
  242. * Clean up old or stale entries
  243. *
  244. * @param nconf Network config
  245. */
  246. inline void clean(const NetworkConfig &nconf)
  247. {
  248. for(std::map<uint32_t,CState>::iterator i(_caps.begin());i!=_caps.end();) {
  249. if (!isCredentialTimestampValid(nconf,i->second.cap)) {
  250. _caps.erase(i++);
  251. } else {
  252. ++i;
  253. }
  254. }
  255. uint32_t *i = (uint32_t *)0;
  256. TState *ts = (TState *)0;
  257. Hashtable<uint32_t,TState>::Iterator tsi(_tags);
  258. while (tsi.next(i,ts)) {
  259. if (!isCredentialTimestampValid(nconf,ts->tag))
  260. _tags.erase(*i);
  261. }
  262. }
  263. private:
  264. // Last time we pushed MULTICAST_LIKE(s)
  265. uint64_t _lastUpdatedMulticast;
  266. // Last time we checked if credential push was needed
  267. uint64_t _lastPushAttempt;
  268. // Last time we pushed our COM to this peer
  269. uint64_t _lastPushedCom;
  270. // Time before which to blacklist credentials from this peer
  271. uint64_t _blacklistBefore;
  272. // COM from this peer
  273. CertificateOfMembership _com;
  274. // Capability-related state (we need an ordered container here, hence std::map)
  275. std::map<uint32_t,CState> _caps;
  276. // Tag-related state
  277. Hashtable<uint32_t,TState> _tags;
  278. };
  279. } // namespace ZeroTier
  280. #endif