diff options
Diffstat (limited to 'node/Demarc.cpp')
-rw-r--r-- | node/Demarc.cpp | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/node/Demarc.cpp b/node/Demarc.cpp new file mode 100644 index 00000000..ae52db48 --- /dev/null +++ b/node/Demarc.cpp @@ -0,0 +1,210 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2012-2013 ZeroTier Networks LLC + * + * 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 <vector> +#include "Demarc.hpp" +#include "RuntimeEnvironment.hpp" +#include "Logger.hpp" +#include "UdpSocket.hpp" +#include "InetAddress.hpp" +#include "Switch.hpp" +#include "Buffer.hpp" + +namespace ZeroTier { + +const Demarc::Port Demarc::ANY_PORT; +const Demarc::Port Demarc::NULL_PORT; + +Demarc::Demarc(const RuntimeEnvironment *renv) : + _r(renv) +{ +} + +Demarc::~Demarc() +{ + for(std::map< Port,DemarcPortObj >::iterator pe(_ports.begin());pe!=_ports.end();++pe) { + switch (pe->second.type) { + case PORT_TYPE_UDP_SOCKET_V4: + case PORT_TYPE_UDP_SOCKET_V6: + delete ((UdpSocket *)pe->second.obj); + break; + case PORT_TYPE_LOCAL_ETHERNET: + case PORT_TYPE_RELAY_TUNNEL: + break; + } + } +} + +std::string Demarc::describe(Demarc::Port p) + throw() +{ + char buf[64]; + switch ((DemarcPortType)(((uint64_t)p) >> 60)) { + case PORT_TYPE_UDP_SOCKET_V4: + sprintf(buf,"udp/4/%d",(int)((uint64_t)p & 0xffff)); + return std::string(buf); + case PORT_TYPE_UDP_SOCKET_V6: + sprintf(buf,"udp/6/%d",(int)((uint64_t)p & 0xffff)); + return std::string(buf); + case PORT_TYPE_LOCAL_ETHERNET: + return std::string("ethernet"); + case PORT_TYPE_RELAY_TUNNEL: + return std::string("relay"); + } + return std::string("(null)"); +} + +bool Demarc::has(Port p) const + throw() +{ + Mutex::Lock _l(_ports_m); + return (_ports.count(p)); +} + +bool Demarc::bindLocalUdp(unsigned int localPort) + throw() +{ + Mutex::Lock _l(_ports_m); + + uint64_t v4p = ((uint64_t)PORT_TYPE_UDP_SOCKET_V4 << 60) | (uint64_t)localPort; + uint64_t v6p = ((uint64_t)PORT_TYPE_UDP_SOCKET_V6 << 60) | (uint64_t)localPort; + if ((_ports.count((Port)v4p))||(_ports.count((Port)v6p))) + return true; + + UdpSocket *v4; + try { + DemarcPortObj *v4r = &(_ports[(Port)v4p]); + v4r->port = (Port)v4p; + v4r->parent = this; + v4r->obj = v4 = new UdpSocket(localPort,false,&Demarc::_CBudpSocketPacketHandler,v4r); + v4r->type = PORT_TYPE_UDP_SOCKET_V4; + } catch ( ... ) { + _ports.erase((Port)v4p); + return false; + } + + UdpSocket *v6; + try { + DemarcPortObj *v6r = &(_ports[(Port)v6p]); + v6r->port = (Port)v6p; + v6r->parent = this; + v6r->obj = v6 = new UdpSocket(localPort,true,&Demarc::_CBudpSocketPacketHandler,v6r); + v6r->type = PORT_TYPE_UDP_SOCKET_V6; + } catch ( ... ) { + delete v4; + _ports.erase((Port)v4p); + _ports.erase((Port)v6p); + return false; + } + + return true; +} + +Demarc::Port Demarc::pick(const InetAddress &to) const + throw() +{ + Mutex::Lock _l(_ports_m); + try { + std::vector< std::map< Port,DemarcPortObj >::const_iterator > possibilities; + for(std::map< Port,DemarcPortObj >::const_iterator pe(_ports.begin());pe!=_ports.end();++pe) { + switch (pe->second.type) { + case PORT_TYPE_UDP_SOCKET_V4: + if (to.isV4()) + possibilities.push_back(pe); + break; + case PORT_TYPE_UDP_SOCKET_V6: + if (to.isV6()) + possibilities.push_back(pe); + break; + default: + break; + } + } + if (possibilities.size()) + return possibilities[Utils::randomInt<unsigned int>() % possibilities.size()]->first; + else return NULL_PORT; + } catch ( ... ) { + return NULL_PORT; + } +} + +Demarc::Port Demarc::send(Demarc::Port fromPort,const InetAddress &to,const void *data,unsigned int len,int hopLimit) const + throw() +{ + _ports_m.lock(); + + std::map< Port,DemarcPortObj >::const_iterator pe(_ports.find(fromPort)); + if (pe == _ports.end()) { + try { + std::vector< std::map< Port,DemarcPortObj >::const_iterator > possibilities; + for(pe=_ports.begin();pe!=_ports.end();++pe) { + switch (pe->second.type) { + case PORT_TYPE_UDP_SOCKET_V4: + if (to.isV4()) + possibilities.push_back(pe); + break; + case PORT_TYPE_UDP_SOCKET_V6: + if (to.isV6()) + possibilities.push_back(pe); + break; + default: + break; + } + } + if (possibilities.size()) + pe = possibilities[Utils::randomInt<unsigned int>() % possibilities.size()]; + else { + _ports_m.unlock(); + return NULL_PORT; + } + } catch ( ... ) { + _ports_m.unlock(); + return NULL_PORT; + } + } + + switch (pe->second.type) { + case PORT_TYPE_UDP_SOCKET_V4: + case PORT_TYPE_UDP_SOCKET_V6: + _ports_m.unlock(); + if (((UdpSocket *)pe->second.obj)->send(to,data,len,hopLimit)) + return pe->first; + return NULL_PORT; + default: + break; + } + + _ports_m.unlock(); + return NULL_PORT; +} + +void Demarc::_CBudpSocketPacketHandler(UdpSocket *sock,void *arg,const InetAddress &from,const void *data,unsigned int len) +{ + ((DemarcPortObj *)arg)->parent->_r->sw->onRemotePacket(((DemarcPortObj *)arg)->port,from,Buffer<4096>(data,len)); +} + +} // namespace ZeroTier |