summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2015-04-06 18:27:24 -0700
committerAdam Ierymenko <adam.ierymenko@gmail.com>2015-04-06 18:27:24 -0700
commit51f46a009a7de20153ff9594a407c55684468a1e (patch)
treef247a785078edf5ee4336bc2d7657a6d389a5ef7
parent8001b2c0cb6d35bfbc7b7be78d1a8b6d0fafbd53 (diff)
downloadinfinitytier-51f46a009a7de20153ff9594a407c55684468a1e.tar.gz
infinitytier-51f46a009a7de20153ff9594a407c55684468a1e.zip
Multicast group join/leave and group membership announcement.
-rw-r--r--include/ZeroTierOne.h40
-rw-r--r--node/Network.cpp128
-rw-r--r--node/Network.hpp38
-rw-r--r--node/Node.cpp22
-rw-r--r--node/Node.hpp15
-rw-r--r--node/Peer.cpp16
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);