diff options
-rw-r--r-- | include/ZeroTierOne.h | 40 | ||||
-rw-r--r-- | node/Network.cpp | 128 | ||||
-rw-r--r-- | node/Network.hpp | 38 | ||||
-rw-r--r-- | node/Node.cpp | 22 | ||||
-rw-r--r-- | node/Node.hpp | 15 | ||||
-rw-r--r-- | node/Peer.cpp | 16 |
6 files changed, 192 insertions, 67 deletions
diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 2d0e837a..e787f7f1 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -323,6 +323,32 @@ typedef struct } ZT1_MulticastGroup; /** + * Virtual network configuration update type + */ +enum ZT1_VirtualNetworkConfigOperation +{ + /** + * Network is coming up (either for the first time or after service restart) + */ + ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_UP = 1, + + /** + * Network configuration has been updated + */ + ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE = 2, + + /** + * Network is going down (not permanently) + */ + ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN = 3, + + /** + * Network is going down permanently (leave/delete) + */ + ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY = 4 +}; + +/** * Virtual LAN configuration */ typedef struct @@ -548,14 +574,14 @@ typedef void ZT1_Node; /****************************************************************************/ /** - * Callback called to update virtual port configuration + * Callback called to update virtual network port configuration * * This can be called at any time to update the configuration of a virtual - * network port. If a port is deleted (via leave() or otherwise) this is - * called with a NULL config parameter. + * network port. The parameter after the network ID specifies whether this + * port is being brought up, updated, brought down, or permanently deleted. * * This in turn should be used by the underlying implementation to create - * and configure tap devices to handle frames, etc. + * and configure tap devices at the OS (or virtual network stack) layer. * * The supplied config pointer is not guaranteed to remain valid, so make * a copy if you want one. @@ -564,7 +590,7 @@ typedef void ZT1_Node; * on failure, and this results in the network being placed into the * PORT_ERROR state. */ -typedef int (*ZT1_VirtualNetworkConfigFunction)(ZT1_Node *,uint64_t,const ZT1_VirtualNetworkConfig *); +typedef int (*ZT1_VirtualNetworkConfigFunction)(ZT1_Node *,uint64_t,enum ZT1_VirtualNetworkConfigOperation,const ZT1_VirtualNetworkConfig *); /** * Callback for status messages @@ -771,7 +797,7 @@ enum ZT1_ResultCode ZT1_Node_leave(ZT1_Node *node,uint64_t nwid); * If this is not done, ARP will not work reliably. * * Multiple calls to subscribe to the same multicast address will have no - * effect. + * effect. It is perfectly safe to do this. * * This does not generate an update call to networkConfigCallback(). * @@ -836,7 +862,7 @@ ZT1_VirtualNetworkConfig *ZT1_Node_networkConfig(ZT1_Node *node,uint64_t nwid); * @param node Node instance * @return List of networks or NULL on failure */ -ZT1_VirtualNetworkList *ZT1_Node_listNetworks(ZT1_Node *node); +ZT1_VirtualNetworkList *ZT1_Node_networks(ZT1_Node *node); /** * Free a query result buffer diff --git a/node/Network.cpp b/node/Network.cpp index 7b033181..fb31f87c 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -100,20 +100,25 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid) : ZT1_VirtualNetworkConfig ctmp; _externalConfig(&ctmp); - _portError = RR->node->configureVirtualNetworkPort(_id,&ctmp); + _portError = RR->node->configureVirtualNetworkPort(_id,ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp); } Network::~Network() { - RR->node->configureVirtualNetworkPort(_id,(const ZT1_VirtualNetworkConfig *)0); + ZT1_VirtualNetworkConfig ctmp; + _externalConfig(&ctmp); char n[128]; if (_destroyed) { + RR->node->configureVirtualNetworkPort(_id,ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp); + Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id); RR->node->dataStoreDelete(n); Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.mcerts",_id); RR->node->dataStoreDelete(n); } else { + RR->node->configureVirtualNetworkPort(_id,ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp); + clean(); std::string buf("ZTMCD0"); @@ -132,49 +137,35 @@ Network::~Network() } } -// Function object used by rescanMulticastGroups() -class AnnounceMulticastGroupsToPeersWithActiveDirectPaths +void Network::multicastSubscribe(const MulticastGroup &mg) { -public: - AnnounceMulticastGroupsToPeersWithActiveDirectPaths(const RuntimeEnvironment *renv,Network *nw) : - RR(renv), - _now(Utils::now()), - _network(nw), - _supernodeAddresses(renv->topology->supernodeAddresses()) - {} + Mutex::Lock _l(_lock); + if (std::find(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg) != _myMulticastGroups.end()) + return; + _myMulticastGroups.push_back(mg); + std::sort(_myMulticastGroups.begin(),_myMulticastGroups.end()); +} - inline void operator()(Topology &t,const SharedPtr<Peer> &p) +void Network::multicastUnsubscribe(const MulticastGroup &mg) +{ + bool needAnnounce = false; { - if ( ( (p->hasActiveDirectPath(_now)) && (_network->isAllowed(p->address())) ) || (std::find(_supernodeAddresses.begin(),_supernodeAddresses.end(),p->address()) != _supernodeAddresses.end()) ) { - Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); - - std::vector<MulticastGroup> mgs(_network->multicastGroups()); - for(std::vector<MulticastGroup>::iterator mg(mgs.begin());mg!=mgs.end();++mg) { - if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) { - outp.armor(p->key(),true); - p->send(RR,outp.data(),outp.size(),_now); - outp.reset(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); - } + Mutex::Lock _l(_lock); - // network ID, MAC, ADI - outp.append((uint64_t)_network->id()); - mg->mac().appendTo(outp); - outp.append((uint32_t)mg->adi()); - } + std::vector<MulticastGroup> nmg; + for(std::vector<MulticastGroup>::const_iterator i(_myMulticastGroups.begin());i!=_myMulticastGroups.end();++i) { + if (*i != mg) + nmg.push_back(*i); + } - if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) { - outp.armor(p->key(),true); - p->send(RR,outp.data(),outp.size(),_now); - } + if (nmg.size() != _myMulticastGroups.size()) { + _myMulticastGroups.swap(nmg); + needAnnounce = true; } } - -private: - const RuntimeEnvironment *RR; - uint64_t _now; - Network *_network; - std::vector<Address> _supernodeAddresses; -}; + if (needAnnounce) + _announceMulticastGroups(); +} bool Network::applyConfiguration(const SharedPtr<NetworkConfig> &conf) { @@ -189,7 +180,7 @@ bool Network::applyConfiguration(const SharedPtr<NetworkConfig> &conf) ZT1_VirtualNetworkConfig ctmp; _externalConfig(&ctmp); - _portError = RR->node->configureVirtualNetworkPort(_id,&ctmp); + _portError = RR->node->configureVirtualNetworkPort(_id,ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE,&ctmp); return true; } else { @@ -405,6 +396,15 @@ void Network::learnBridgeRoute(const MAC &mac,const Address &addr) } } +void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now) +{ + Mutex::Lock _l(_lock); + unsigned long tmp = _multicastGroupsBehindMe.size(); + _multicastGroupsBehindMe[mg] = now; + if (tmp != _multicastGroupsBehindMe.size()) + _announceMulticastGroups(); +} + void Network::setEnabled(bool enabled) { Mutex::Lock _l(_lock); @@ -467,4 +467,54 @@ void Network::_externalConfig(ZT1_VirtualNetworkConfig *ec) const } else ec->assignedAddressCount = 0; } +// Used in Network::_announceMulticastGroups() +class _AnnounceMulticastGroupsToPeersWithActiveDirectPaths +{ +public: + _AnnounceMulticastGroupsToPeersWithActiveDirectPaths(const RuntimeEnvironment *renv,Network *nw) : + RR(renv), + _now(Utils::now()), + _network(nw), + _supernodeAddresses(renv->topology->supernodeAddresses()) + {} + + inline void operator()(Topology &t,const SharedPtr<Peer> &p) + { + if ( ( (p->hasActiveDirectPath(_now)) && (_network->isAllowed(p->address())) ) || (std::find(_supernodeAddresses.begin(),_supernodeAddresses.end(),p->address()) != _supernodeAddresses.end()) ) { + Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); + + std::vector<MulticastGroup> mgs(_network->allMulticastGroups()); + for(std::vector<MulticastGroup>::iterator mg(mgs.begin());mg!=mgs.end();++mg) { + if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) { + outp.armor(p->key(),true); + p->send(RR,outp.data(),outp.size(),_now); + outp.reset(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); + } + + // network ID, MAC, ADI + outp.append((uint64_t)_network->id()); + mg->mac().appendTo(outp); + outp.append((uint32_t)mg->adi()); + } + + if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) { + outp.armor(p->key(),true); + p->send(RR,outp.data(),outp.size(),_now); + } + } + } + +private: + const RuntimeEnvironment *RR; + uint64_t _now; + Network *_network; + std::vector<Address> _supernodeAddresses; +}; + +void Network::_announceMulticastGroups() +{ + _AnnounceMulticastGroupsToPeersWithActiveDirectPaths afunc(RR,this); + RR->topology->eachPeer<_AnnounceMulticastGroupsToPeersWithActiveDirectPaths &>(afunc); +} + } // namespace ZeroTier diff --git a/node/Network.hpp b/node/Network.hpp index b51164a3..ada491a6 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -89,7 +89,7 @@ public: inline Address controller() throw() { return Address(_id >> 24); } /** - * @return Latest list of multicast groups for this network's tap + * @return Multicast group memberships for this network's port (local, not learned via bridging) */ inline std::vector<MulticastGroup> multicastGroups() const { @@ -98,6 +98,21 @@ public: } /** + * @return All multicast groups including learned groups that are behind any bridges we're attached to + */ + inline std::vector<MulticastGroup> allMulticastGroups() const + { + Mutex::Lock _l(_lock); + std::vector<MulticastGroup> mgs(_myMulticastGroups); + for(std::map< MulticastGroup,uint64_t >::const_iterator i(_multicastGroupsBehindMe.begin());i!=_multicastGroupsBehindMe.end();++i) { + if (std::find(mgs.begin(),mgs.end(),i->first) == mgs.end()) + mgs.push_back(i->first); + } + std::sort(mgs.begin(),mgs.end()); + return mgs; + } + + /** * @param mg Multicast group * @return True if this network endpoint / peer is a member */ @@ -108,6 +123,20 @@ public: } /** + * Subscribe to a multicast group + * + * @param mg New multicast group + */ + void multicastSubscribe(const MulticastGroup &mg); + + /** + * Unsubscribe from a multicast group + * + * @param mg Multicast group + */ + void multicastUnsubscribe(const MulticastGroup &mg); + + /** * Apply a NetworkConfig to this network * * @param conf Configuration in NetworkConfig form @@ -308,11 +337,7 @@ public: * @param mg Multicast group * @param now Current time */ - inline void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now) - { - Mutex::Lock _l(_lock); - _multicastGroupsBehindMe[mg] = now; - } + void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now); /** * @return True if traffic on this network's tap is enabled @@ -336,6 +361,7 @@ public: private: ZT1_VirtualNetworkStatus _status() const; void _externalConfig(ZT1_VirtualNetworkConfig *ec) const; // assumes _lock is locked + void _announceMulticastGroups(); const RuntimeEnvironment *RR; uint64_t _id; diff --git a/node/Node.cpp b/node/Node.cpp index 29262eda..2167d9c1 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -151,10 +151,18 @@ ZT1_ResultCode Node::leave(uint64_t nwid) ZT1_ResultCode Node::multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi) { + Mutex::Lock _l(_networks_m); + std::map< uint64_t,SharedPtr<Network> >::iterator nw(_networks.find(nwid)); + if (nw != _networks.end()) + nw->second->multicastSubscribe(MulticastGroup(MAC(multicastGroup,(uint32_t)(multicastAdi & 0xffffffff)))); } ZT1_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi) { + Mutex::Lock _l(_networks_m); + std::map< uint64_t,SharedPtr<Network> >::iterator nw(_networks.find(nwid)); + if (nw != _networks.end()) + nw->second->multicastUnsubscribe(MulticastGroup(MAC(multicastGroup,(uint32_t)(multicastAdi & 0xffffffff)))); } void Node::status(ZT1_NodeStatus *status) @@ -167,9 +175,17 @@ ZT1_PeerList *Node::peers() ZT1_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) { + Mutex::Lock _l(_networks_m); + std::map< uint64_t,SharedPtr<Network> >::iterator nw(_networks.find(nwid)); + if (nw != _networks.end()) { + ZT1_VirtualNetworkConfig *nc = (ZT1_VirtualNetworkConfig *)::malloc(sizeof(ZT1_VirtualNetworkConfig)); + nw->second->externalConfig(nc); + return nc; + } + return (ZT1_VirtualNetworkConfig *)0; } -ZT1_VirtualNetworkList *Node::listNetworks() +ZT1_VirtualNetworkList *Node::networks() { } @@ -344,7 +360,7 @@ void ZT1_Node_status(ZT1_Node *node,ZT1_NodeStatus *status) ZT1_PeerList *ZT1_Node_peers(ZT1_Node *node) { try { - return reinterpret_cast<ZeroTier::Node *>(node)->peers(); + return reinterpret_cast<ZeroTier::Node *>(node)->listPeers(); } catch ( ... ) { return (ZT1_PeerList *)0; } @@ -359,7 +375,7 @@ ZT1_VirtualNetworkConfig *ZT1_Node_networkConfig(ZT1_Node *node,uint64_t nwid) } } -ZT1_VirtualNetworkList *ZT1_Node_listNetworks(ZT1_Node *node) +ZT1_VirtualNetworkList *ZT1_Node_networks(ZT1_Node *node) { try { return reinterpret_cast<ZeroTier::Node *>(node)->listNetworks(); diff --git a/node/Node.hpp b/node/Node.hpp index 5b158228..07bbbf8b 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -92,8 +92,8 @@ public: ZT1_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); void status(ZT1_NodeStatus *status); ZT1_PeerList *peers(); - ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid); - ZT1_VirtualNetworkList *listNetworks(); + bool networkConfig(uint64_t nwid,ZT1_VirtualNetworkConfig *ec); + ZT1_VirtualNetworkList *networks(); void freeQueryResult(void *qr); void setNetconfMaster(void *networkConfigMasterInstance); @@ -154,6 +154,15 @@ public: return ((nw == _networks.end()) ? SharedPtr<Network>() : nw->second); } + inline std::vector< SharedPtr<Network> > allNetworks() const + { + Mutex::Lock _l(_networks_m); + std::vector< SharedPtr<Network> > nw; + for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n) + nw.push_back(n->second); + return nw; + } + inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_dataStorePutFunction(reinterpret_cast<ZT1_Node *>(this),name,data,len,(int)secure) == 0); } inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); } inline void dataStoreDelete(const char *name) { _dataStorePutFunction(reinterpret_cast<ZT1_Node *>(this),name,(const void *)0,0,0); } @@ -161,7 +170,7 @@ public: inline void postEvent(ZT1_Event ev) { _statusCallback(reinterpret_cast<ZT1_Node *>(this),ev); } - inline int configureVirtualNetworkPort(uint64_t nwid,const ZT1_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT1_Node *>(this),nwid,nc); } + inline int configureVirtualNetworkPort(uint64_t nwid,ZT1_VirtualNetworkConfigOperation op,const ZT1_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT1_Node *>(this),nwid,op,nc); } void postNewerVersionIfNewer(unsigned int major,unsigned int minor,unsigned int rev); diff --git a/node/Peer.cpp b/node/Peer.cpp index 0bff2aae..076dbb41 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -106,20 +106,18 @@ void Peer::received( /* Announce multicast groups of interest to direct peers if they are * considered authorized members of a given network. Also announce to - * supernodes and network controllers. The other place this is done - * is in rescanMulticastGroups() in Network, but that only sends something - * if a network's multicast groups change. */ + * supernodes and network controllers. */ if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) { _lastAnnouncedTo = now; - bool isSupernode = RR->topology->isSupernode(_id.address()); + const bool isSupernode = RR->topology->isSupernode(_id.address()); Packet outp(_id.address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE); - std::vector< SharedPtr<Network> > networks(RR->nc->networks()); - for(std::vector< SharedPtr<Network> >::iterator n(networks.begin());n!=networks.end();++n) { - if ( ((*n)->isAllowed(_id.address())) || (isSupernode) ) { - std::set<MulticastGroup> mgs((*n)->multicastGroups()); - for(std::set<MulticastGroup>::iterator mg(mgs.begin());mg!=mgs.end();++mg) { + const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks()); + for(std::vector< SharedPtr<Network> >::const_iterator n(networks.begin());n!=networks.end();++n) { + if ( (isSupernode) || ((*n)->isAllowed(_id.address())) ) { + const std::vector<MulticastGroup> mgs((*n)->allMulticastGroups()); + for(std::vector<MulticastGroup>::const_iterator mg(mgs.begin());mg!=mgs.end();++mg) { if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) { outp.armor(_key,true); RR->node->putPacket(remoteAddr,outp.data(),outp.size(),linkDesperation,false); |