summaryrefslogtreecommitdiff
path: root/osdep
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2016-06-15 15:02:40 -0700
committerAdam Ierymenko <adam.ierymenko@gmail.com>2016-06-15 15:02:40 -0700
commitb90e66f7c7546aaf9c0c8a6bf14cc834f82fa680 (patch)
tree2c9e5f87535084e91ebe26e6e43a3136dcc4c258 /osdep
parent4446dbde5edfd8f7ec9730886e5d577259d73de2 (diff)
downloadinfinitytier-b90e66f7c7546aaf9c0c8a6bf14cc834f82fa680.tar.gz
infinitytier-b90e66f7c7546aaf9c0c8a6bf14cc834f82fa680.zip
ManagedRoute, which applies C++ RAII to injected routes. Move RoutingTable to attic.
Diffstat (limited to 'osdep')
-rw-r--r--osdep/ManagedRoute.cpp456
-rw-r--r--osdep/ManagedRoute.hpp95
-rw-r--r--osdep/RoutingTable.cpp608
-rw-r--r--osdep/RoutingTable.hpp92
4 files changed, 551 insertions, 700 deletions
diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp
new file mode 100644
index 00000000..c31f6dd0
--- /dev/null
+++ b/osdep/ManagedRoute.cpp
@@ -0,0 +1,456 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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/>.
+ */
+
+#include "../node/Constants.hpp"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __WINDOWS__
+#include <WinSock2.h>
+#include <Windows.h>
+#include <netioapi.h>
+#include <IPHlpApi.h>
+#endif
+
+#ifdef __UNIX_LIKE__
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <ifaddrs.h>
+#endif
+
+#include <vector>
+#include <algorithm>
+#include <utility>
+
+#include "ManagedRoute.hpp"
+
+#define ZT_BSD_ROUTE_CMD "/sbin/route"
+#define ZT_LINUX_IP_COMMAND "/sbin/ip"
+
+namespace ZeroTier {
+
+namespace {
+
+// Fork a target into two more specific targets e.g. 0.0.0.0/0 -> 0.0.0.0/1, 128.0.0.0/1
+// If the target is already maximally-specific, 'right' will be unchanged and 'left' will be 't'
+static void _forkTarget(const InetAddress &t,InetAddress &left,InetAddress &right)
+{
+ const unsigned int bits = t.netmaskBits() + 1;
+ left = t;
+ if ((t.ss_family == AF_INET)&&(bits <= 32)) {
+ left.setPort(bits);
+ right = t;
+ reinterpret_cast<struct sockaddr_in *>(&right)->sin_addr.s_addr ^= Utils::hton((uint32_t)(1 << (32 - bits)));
+ right.setPort(bits);
+ } else if ((t.ss_family == AF_INET6)&&(bits <= 128)) {
+ left.setPort(bits);
+ right = t;
+ uint8_t *b = reinterpret_cast<uint8_t *>(reinterpret_cast<struct sockaddr_in6 *>(&right)->sin6_addr.s6_addr);
+ b[bits / 8] ^= 1 << (8 - (bits % 8));
+ right.setPort(bits);
+ }
+}
+
+#ifdef __BSD__ // ------------------------------------------------------------
+#define ZT_ROUTING_SUPPORT_FOUND 1
+
+struct _RTE
+{
+ InetAddress target;
+ InetAddress via;
+ char device[128];
+ int metric;
+ bool ifscope;
+};
+
+static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains)
+{
+ std::vector<_RTE> rtes;
+ int mib[6];
+ size_t needed;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ if (!sysctl(mib,6,NULL,&needed,NULL,0)) {
+ if (needed <= 0)
+ return rtes;
+
+ char *buf = (char *)::malloc(needed);
+ if (buf) {
+ if (!sysctl(mib,6,buf,&needed,NULL,0)) {
+ struct rt_msghdr *rtm;
+ for(char *next=buf,*end=buf+needed;next<end;) {
+ rtm = (struct rt_msghdr *)next;
+ char *saptr = (char *)(rtm + 1);
+ char *saend = next + rtm->rtm_msglen;
+
+ InetAddress sa_t,sa_v;
+ int deviceIndex = -9999;
+
+ if (((rtm->rtm_flags & RTF_LLINFO) == 0)&&((rtm->rtm_flags & RTF_HOST) == 0)&&((rtm->rtm_flags & RTF_UP) != 0)&&((rtm->rtm_flags & RTF_MULTICAST) == 0)) {
+ int which = 0;
+ while (saptr < saend) {
+ struct sockaddr *sa = (struct sockaddr *)saptr;
+ unsigned int salen = sa->sa_len;
+ if (!salen)
+ break;
+
+ // Skip missing fields in rtm_addrs bit field
+ while ((rtm->rtm_addrs & 1) == 0) {
+ rtm->rtm_addrs >>= 1;
+ ++which;
+ if (which > 6)
+ break;
+ }
+ if (which > 6)
+ break;
+
+ rtm->rtm_addrs >>= 1;
+ switch(which++) {
+ case 0:
+ //printf("RTA_DST\n");
+ if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ if ((sin6->sin6_addr.s6_addr[0] == 0xfe)&&((sin6->sin6_addr.s6_addr[1] & 0xc0) == 0x80)) {
+ // BSD uses this fucking strange in-band signaling method to encode device scope IDs for IPv6 addresses... probably a holdover from very early versions of the spec.
+ unsigned int interfaceIndex = ((((unsigned int)sin6->sin6_addr.s6_addr[2]) << 8) & 0xff) | (((unsigned int)sin6->sin6_addr.s6_addr[3]) & 0xff);
+ sin6->sin6_addr.s6_addr[2] = 0;
+ sin6->sin6_addr.s6_addr[3] = 0;
+ if (!sin6->sin6_scope_id)
+ sin6->sin6_scope_id = interfaceIndex;
+ }
+ }
+ sa_t = *sa;
+ break;
+ case 1:
+ //printf("RTA_GATEWAY\n");
+ switch(sa->sa_family) {
+ case AF_LINK:
+ deviceIndex = (int)((const struct sockaddr_dl *)sa)->sdl_index;
+ break;
+ case AF_INET:
+ case AF_INET6:
+ sa_v = *sa;
+ break;
+ }
+ break;
+ case 2: {
+ //printf("RTA_NETMASK\n");
+ if (sa_t.ss_family == AF_INET6) {
+ salen = sizeof(struct sockaddr_in6);
+ unsigned int bits = 0;
+ for(int i=0;i<16;++i) {
+ unsigned char c = (unsigned char)((const struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[i];
+ if (c == 0xff)
+ bits += 8;
+ else break;
+ }
+ sa_t.setPort(bits);
+ } else if (sa_t.ss_family == AF_INET) {
+ salen = sizeof(struct sockaddr_in);
+ sa_t.setPort((unsigned int)Utils::countBits((uint32_t)((const struct sockaddr_in *)sa)->sin_addr.s_addr));
+ }
+ } break;
+ /*
+ case 3:
+ //printf("RTA_GENMASK\n");
+ break;
+ case 4:
+ //printf("RTA_IFP\n");
+ break;
+ case 5:
+ //printf("RTA_IFA\n");
+ break;
+ case 6:
+ //printf("RTA_AUTHOR\n");
+ break;
+ */
+ }
+
+ saptr += salen;
+ }
+
+ if (((contains)&&(sa_t.containsAddress(target)))||(sa_t == target)) {
+ rtes.push_back(_RTE());
+ rtes.back().target = sa_t;
+ rtes.back().via = sa_v;
+ if (deviceIndex >= 0) {
+ if_indextoname(deviceIndex,rtes.back().device);
+ } else {
+ rtes.back().device[0] = (char)0;
+ }
+ rtes.back().metric = ((int)rtm->rtm_rmx.rmx_hopcount < 0) ? 0 : (int)rtm->rtm_rmx.rmx_hopcount;
+ }
+ }
+
+ next = saend;
+ }
+ }
+
+ ::free(buf);
+ }
+ }
+
+ return rtes;
+}
+
+static void _routeCmd(const char *op,const InetAddress &target,const InetAddress &via,const char *ifscope,const char *localInterface)
+{
+ long p = (long)fork();
+ if (p > 0) {
+ int exitcode = -1;
+ ::waitpid(p,&exitcode,0);
+ } else if (p == 0) {
+ //::close(STDOUT_FILENO);
+ //::close(STDERR_FILENO);
+ if (via) {
+ if ((ifscope)&&(ifscope[0])) {
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),via.toIpString().c_str(),(const char *)0);
+ } else {
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),via.toIpString().c_str(),(const char *)0);
+ }
+ } else if ((localInterface)&&(localInterface[0])) {
+ if ((ifscope)&&(ifscope[0])) {
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),"-interface",localInterface,(const char *)0);
+ } else {
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),"-interface",localInterface,(const char *)0);
+ }
+ }
+ ::_exit(-1);
+ }
+}
+
+#endif // __BSD__ ------------------------------------------------------------
+
+#ifdef __LINUX__ // ----------------------------------------------------------
+#define ZT_ROUTING_SUPPORT_FOUND 1
+
+#endif // __LINUX__ ----------------------------------------------------------
+
+#ifdef __WINDOWS__ // --------------------------------------------------------
+#define ZT_ROUTING_SUPPORT_FOUND 1
+
+#endif // __WINDOWS__ --------------------------------------------------------
+
+} // anonymous namespace
+
+bool ManagedRoute::sync()
+{
+ if (this->target.isDefaultRoute()) {
+ /* In ZeroTier we use a forked-route trick to override the default
+ * with a more specific one while leaving the original system route
+ * intact. We also create a shadow more specific route to the
+ * original gateway that is device-bound so that ZeroTier's device
+ * bound ports go via the physical Internet link. This has to be
+ * done *slightly* differently on different platforms. */
+
+ InetAddress leftt,rightt;
+ _forkTarget(this->target,leftt,rightt);
+
+#ifdef __BSD__ // ------------------------------------------------------------
+
+ InetAddress systemVia;
+ char systemDevice[128];
+ int systemMetric = 9999999;
+ systemDevice[0] = (char)0;
+
+ std::vector<_RTE> rtes(_getRTEs(this->target,false));
+ for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
+ if (r->via) {
+ if ((!systemVia)||(r->metric < systemMetric)) {
+ systemVia = r->via;
+ Utils::scopy(systemDevice,sizeof(systemDevice),r->device);
+ systemMetric = r->metric;
+ }
+ }
+ }
+
+ if (!systemDevice[0]) {
+ rtes = _getRTEs(systemVia,true);
+ for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
+ if (r->device[0])
+ Utils::scopy(systemDevice,sizeof(systemDevice),r->device);
+ }
+ }
+
+ if ((!systemVia)||(!systemDevice[0]))
+ return false;
+
+ _routeCmd("add",leftt,systemVia,systemDevice,(const char *)0);
+ _routeCmd("change",leftt,systemVia,systemDevice,(const char *)0);
+ _routeCmd("add",rightt,systemVia,systemDevice,(const char *)0);
+ _routeCmd("change",rightt,systemVia,systemDevice,(const char *)0);
+
+ if (this->via) {
+ _routeCmd("add",leftt,this->via,(const char *)0,(const char *)0);
+ _routeCmd("change",leftt,this->via,(const char *)0,(const char *)0);
+ _routeCmd("add",rightt,this->via,(const char *)0,(const char *)0);
+ _routeCmd("change",rightt,this->via,(const char *)0,(const char *)0);
+ } else if ((this->device)&&(this->device[0])) {
+ _routeCmd("add",leftt,this->via,(const char *)0,this->device);
+ _routeCmd("change",leftt,this->via,(const char *)0,this->device);
+ _routeCmd("add",rightt,this->via,(const char *)0,this->device);
+ _routeCmd("change",rightt,this->via,(const char *)0,this->device);
+ }
+
+#endif // __BSD__ ------------------------------------------------------------
+
+#ifdef __LINUX__ // ----------------------------------------------------------
+
+#endif // __LINUX__ ----------------------------------------------------------
+
+#ifdef __WINDOWS__ // --------------------------------------------------------
+
+#endif // __WINDOWS__ --------------------------------------------------------
+
+ } else {
+
+ // TODO
+
+#ifdef __BSD__ // ------------------------------------------------------------
+
+#endif // __BSD__ ------------------------------------------------------------
+
+#ifdef __LINUX__ // ----------------------------------------------------------
+
+#endif // __LINUX__ ----------------------------------------------------------
+
+#ifdef __WINDOWS__ // --------------------------------------------------------
+
+#endif // __WINDOWS__ --------------------------------------------------------
+
+ }
+
+ return true;
+}
+
+void ManagedRoute::remove()
+{
+ if (!this->applied)
+ return;
+
+ if (this->target.isDefaultRoute()) {
+ /* In ZeroTier we use a forked-route trick to override the default
+ * with a more specific one while leaving the original system route
+ * intact. We also create a shadow more specific route to the
+ * original gateway that is device-bound so that ZeroTier's device
+ * bound ports go via the physical Internet link. This has to be
+ * done *slightly* differently on different platforms. */
+
+ InetAddress leftt,rightt;
+ _forkTarget(this->target,leftt,rightt);
+
+#ifdef __BSD__ // ------------------------------------------------------------
+
+ InetAddress systemVia;
+ char systemDevice[128];
+ int systemMetric = 9999999;
+ systemDevice[0] = (char)0;
+
+ std::vector<_RTE> rtes(_getRTEs(this->target,false));
+ for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
+ if (r->via) {
+ if ((!systemVia)||(r->metric < systemMetric)) {
+ systemVia = r->via;
+ Utils::scopy(systemDevice,sizeof(systemDevice),r->device);
+ systemMetric = r->metric;
+ }
+ }
+ }
+
+ if (!systemDevice[0]) {
+ rtes = _getRTEs(systemVia,true);
+ for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
+ if (r->device[0])
+ Utils::scopy(systemDevice,sizeof(systemDevice),r->device);
+ }
+ }
+
+ if ((!systemVia)||(!systemDevice[0]))
+ return false;
+
+ _routeCmd("delete",leftt,systemVia,systemDevice,(const char *)0);
+ _routeCmd("delete",rightt,systemVia,systemDevice,(const char *)0);
+
+ if (this->via) {
+ _routeCmd("delete",leftt,this->via,(const char *)0,(const char *)0);
+ _routeCmd("delete",rightt,this->via,(const char *)0,(const char *)0);
+ } else if ((this->device)&&(this->device[0])) {
+ _routeCmd("delete",leftt,this->via,(const char *)0,this->device);
+ _routeCmd("delete",rightt,this->via,(const char *)0,this->device);
+ }
+
+#endif // __BSD__ ------------------------------------------------------------
+
+#ifdef __LINUX__ // ----------------------------------------------------------
+
+#endif // __LINUX__ ----------------------------------------------------------
+
+#ifdef __WINDOWS__ // --------------------------------------------------------
+
+#endif // __WINDOWS__ --------------------------------------------------------
+
+ } else {
+
+ // TODO
+
+#ifdef __BSD__ // ------------------------------------------------------------
+
+#endif // __BSD__ ------------------------------------------------------------
+
+#ifdef __LINUX__ // ----------------------------------------------------------
+
+#endif // __LINUX__ ----------------------------------------------------------
+
+#ifdef __WINDOWS__ // --------------------------------------------------------
+
+#endif // __WINDOWS__ --------------------------------------------------------
+
+ }
+}
+
+} // namespace ZeroTier
+
+#ifndef ZT_ROUTING_SUPPORT_FOUND
+#error ManagedRoute.cpp has no support for managing routes on this platform! You'll need to check and see if one of the existing ones will work and make sure proper defines are set, or write one. Please do a Github pull request if you do this for a new OS!
+#endif
+
+/*
+int main(int argc,char **argv)
+{
+ ZeroTier::ManagedRoute t;
+ t.set(ZeroTier::InetAddress("0.0.0.0/0"),ZeroTier::InetAddress("10.6.6.112"),"zt2");
+ sleep(10000);
+}
+*/
diff --git a/osdep/ManagedRoute.hpp b/osdep/ManagedRoute.hpp
new file mode 100644
index 00000000..081d516d
--- /dev/null
+++ b/osdep/ManagedRoute.hpp
@@ -0,0 +1,95 @@
+#ifndef ZT_MANAGEDROUTE_HPP
+#define ZT_MANAGEDROUTE_HPP
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "../node/InetAddress.hpp"
+#include "../node/Utils.hpp"
+
+#include <stdexcept>
+#include <vector>
+
+namespace ZeroTier {
+
+/**
+ * A ZT-managed route that used C++ RAII semantics to automatically clean itself up on deallocate
+ */
+class ManagedRoute
+{
+public:
+ ManagedRoute() :
+ target(),
+ via(),
+ applied(false)
+ {
+ device[0] = (char)0;
+ }
+
+ ~ManagedRoute()
+ {
+ this->remove();
+ }
+
+ /**
+ * @param target Route target (e.g. 0.0.0.0/0 for default)
+ * @param via Route next L3 hop or NULL InetAddress if local
+ * @param device Device name/ID if 'via' is null and route is local, otherwise ignored
+ * @return True if route was successfully set
+ */
+ inline bool set(const InetAddress &target,const InetAddress &via,const char *device)
+ {
+ if ((!via)&&((!device)||(!device[0])))
+ return false;
+ this->remove();
+ this->target = target;
+ this->via = via;
+ this->applied = true;
+ Utils::scopy(this->device,sizeof(this->device),device);
+ return this->sync();
+ }
+
+ /**
+ * Set or update currently set route
+ *
+ * This must be called periodically for routes that shadow others so that
+ * shadow routes can be updated. In some cases it has no effect
+ *
+ * @return True if route add/update was successful
+ */
+ bool sync();
+
+ /**
+ * Remove and clear this ManagedRoute (also done automatically on destruct)
+ *
+ * This does nothing if this ManagedRoute is not set or has already been removed.
+ */
+ void remove();
+
+private:
+ /*
+ static inline bool _viaCompare(const InetAddress &v1,const InetAddress &v2)
+ {
+ if (v1) {
+ if (v2)
+ return v1.ipsEqual(v2);
+ else return false;
+ } else if (v2)
+ return false;
+ else return true;
+ }
+ */
+
+ // non-copyable
+ ManagedRoute(const ManagedRoute &mr) {}
+ inline ManagedRoute &operator=(const ManagedRoute &mr) { return *this; }
+
+ InetAddress target;
+ InetAddress via;
+ bool applied;
+ char device[128];
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/osdep/RoutingTable.cpp b/osdep/RoutingTable.cpp
deleted file mode 100644
index 40523898..00000000
--- a/osdep/RoutingTable.cpp
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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/>.
- */
-
-#include "../node/Constants.hpp"
-
-#ifdef __WINDOWS__
-#include <WinSock2.h>
-#include <Windows.h>
-#include <netioapi.h>
-#include <IPHlpApi.h>
-#endif
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef __UNIX_LIKE__
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <net/route.h>
-#include <net/if.h>
-#include <net/if_dl.h>
-#include <ifaddrs.h>
-#endif
-
-#include <vector>
-#include <algorithm>
-#include <utility>
-
-#include "RoutingTable.hpp"
-
-#define ZT_BSD_ROUTE_CMD "/sbin/route"
-#define ZT_LINUX_IP_COMMAND "/sbin/ip"
-
-namespace ZeroTier {
-
-// ---------------------------------------------------------------------------
-
-#ifdef __LINUX__
-
-std::vector<RoutingTable::Entry> RoutingTable::get(bool includeLinkLocal,bool includeLoopback)
-{
- char buf[131072];
- char *stmp,*stmp2;
- std::vector<RoutingTable::Entry> entries;
-
- {
- int fd = ::open("/proc/net/route",O_RDONLY);
- if (fd <= 0)
- buf[0] = (char)0;
- else {
- int n = (int)::read(fd,buf,sizeof(buf) - 1);
- ::close(fd);
- if (n < 0) n = 0;
- buf[n] = (char)0;
- }
- }
-
- int lineno = 0;
- for(char *line=Utils::stok(buf,"\r\n",&stmp);(line);line=Utils::stok((char *)0,"\r\n",&stmp)) {
- if (lineno == 0) {
- ++lineno;
- continue; // skip header
- }
-
- char *iface = (char *)0;
- uint32_t destination = 0;
- uint32_t gateway = 0;
- int metric = 0;
- uint32_t mask = 0;
-
- int fno = 0;
- for(char *f=Utils::stok(line,"\t \r\n",&stmp2);(f);f=Utils::stok((char *)0,"\t \r\n",&stmp2)) {
- switch(fno) {
- case 0: iface = f; break;
- case 1: destination = (uint32_t)Utils::hexStrToULong(f); break;
- case 2: gateway = (uint32_t)Utils::hexStrToULong(f); break;
- case 6: metric = (int)Utils::strToInt(f); break;
- case 7: mask = (uint32_t)Utils::hexStrToULong(f); break;
- }
- ++fno;
- }
-
- if ((iface)&&(destination)) {
- RoutingTable::Entry e;
- if (destination)
- e.destination.set(&destination,4,Utils::countBits(mask));
- e.gateway.set(&gateway,4,0);
- e.deviceIndex = 0; // not used on Linux
- e.metric = metric;
- Utils::scopy(e.device,sizeof(e.device),iface);
- if ((e.destination)&&((includeLinkLocal)||(!e.destination.isLinkLocal()))&&((includeLoopback)||((!e.destination.isLoopback())&&(!e.gateway.isLoopback())&&(strcmp(iface,"lo")))))
- entries.push_back(e);
- }
-
- ++lineno;
- }
-
- {
- int fd = ::open("/proc/net/ipv6_route",O_RDONLY);
- if (fd <= 0)
- buf[0] = (char)0;
- else {
- int n = (int)::read(fd,buf,sizeof(buf) - 1);
- ::close(fd);
- if (n < 0) n = 0;
- buf[n] = (char)0;
- }
- }
-
- for(char *line=Utils::stok(buf,"\r\n",&stmp);(line);line=Utils::stok((char *)0,"\r\n",&stmp)) {
- char *destination = (char *)0;
- unsigned int destPrefixLen = 0;
- char *gateway = (char *)0; // next hop in ipv6 terminology
- int metric = 0;
- char *device = (char *)0;
-
- int fno = 0;
- for(char *f=Utils::stok(line,"\t \r\n",&stmp2);(f);f=Utils::stok((char *)0,"\t \r\n",&stmp2)) {
- switch(fno) {
- case 0: destination = f; break;
- case 1: destPrefixLen = (unsigned int)Utils::hexStrToULong(f); break;
- case 4: gateway = f; break;
- case 5: metric = (int)Utils::hexStrToLong(f); break;
- case 9: device = f; break;
- }
- ++fno;
- }
-
- if ((device)&&(destination)) {
- unsigned char tmp[16];
- RoutingTable::Entry e;
- Utils::unhex(destination,tmp,16);
- if ((!Utils::isZero(tmp,16))&&(tmp[0] != 0xff))
- e.destination.set(tmp,16,destPrefixLen);
- Utils::unhex(gateway,tmp,16);
- e.gateway.set(tmp,16,0);
- e.deviceIndex = 0; // not used on Linux
- e.metric = metric;
- Utils::scopy(e.device,sizeof(e.device),device);
- if ((e.destination)&&((includeLinkLocal)||(!e.destination.isLinkLocal()))&&((includeLoopback)||((!e.destination.isLoopback())&&(!e.gateway.isLoopback())&&(strcmp(device,"lo")))))
- entries.push_back(e);
- }
- }
-
- std::sort(entries.begin(),entries.end());
- return entries;
-}
-
-RoutingTable::Entry RoutingTable::set(const InetAddress &destination,const InetAddress &gateway,const char *device,int metric,bool ifscope)
-{
- char metstr[128];
-
- if ((!gateway)&&((!device)||(!device[0])))
- return RoutingTable::Entry();
-
- Utils::snprintf(metstr,sizeof(metstr),"%d",metric);
-
- if (metric < 0) {
- long pid = (long)vfork();
- if (pid == 0) {
- if (gateway) {
- if ((device)&&(device[0])) {
- ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","del",destination.toString().c_str(),"via",gateway.toIpString().c_str(),"dev",device,(const char *)0);
- } else {
- ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","del",destination.toString().c_str(),"via",gateway.toIpString().c_str(),(const char *)0);
- }
- } else {
- ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","del",destination.toString().c_str(),"dev",device,(const char *)0);
- }
- ::_exit(-1);
- } else if (pid > 0) {
- int exitcode = -1;
- ::waitpid(pid,&exitcode,0);
- }
- } else {
- long pid = (long)vfork();
- if (pid == 0) {
- if (gateway) {
- if ((device)&&(device[0])) {
- ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","replace",destination.toString().c_str(),"metric",metstr,"via",gateway.toIpString().c_str(),"dev",device,(const char *)0);
- } else {
- ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","replace",destination.toString().c_str(),"metric",metstr,"via",gateway.toIpString().c_str(),(const char *)0);
- }
- } else {
- ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,"route","replace",destination.toString().c_str(),"metric",metstr,"dev",device,(const char *)0);
- }
- ::_exit(-1);
- } else if (pid > 0) {
- int exitcode = -1;
- ::waitpid(pid,&exitcode,0);
- }
- }
-
- std::vector<RoutingTable::Entry> rtab(get(true,true));
- std::vector<RoutingTable::Entry>::iterator bestEntry(rtab.end());
- for(std::vector<RoutingTable::Entry>::iterator e(rtab.begin());e!=rtab.end();++e) {
- if ((e->destination == destination)&&(e->gateway.ipsEqual(gateway))) {
- if ((device)&&(device[0])) {
- if (!strcmp(device,e->device)) {
- if (metric == e->metric)
- bestEntry = e;
- }
- }
- if (bestEntry == rtab.end())
- bestEntry = e;
- }
- }
- if (bestEntry != rtab.end())
- return *bestEntry;
-
- return RoutingTable::Entry();
-}
-
-#endif // __LINUX__
-
-// ---------------------------------------------------------------------------
-
-#ifdef __BSD__
-
-std::vector<RoutingTable::Entry> RoutingTable::get(bool includeLinkLocal,bool includeLoopback)
-{
- std::vector<RoutingTable::Entry> entries;
- int mib[6];
- size_t needed;
-
- mib[0] = CTL_NET;
- mib[1] = PF_ROUTE;
- mib[2] = 0;
- mib[3] = 0;
- mib[4] = NET_RT_DUMP;
- mib[5] = 0;
- if (!sysctl(mib,6,NULL,&needed,NULL,0)) {
- if (needed <= 0)
- return entries;
-
- char *buf = (char *)::malloc(needed);
- if (buf) {
- if (!sysctl(mib,6,buf,&needed,NULL,0)) {
- struct rt_msghdr *rtm;
- for(char *next=buf,*end=buf+needed;next<end;) {
- rtm = (struct rt_msghdr *)next;
- char *saptr = (char *)(rtm + 1);
- char *saend = next + rtm->rtm_msglen;
-
- if (((rtm->rtm_flags & RTF_LLINFO) == 0)&&((rtm->rtm_flags & RTF_HOST) == 0)&&((rtm->rtm_flags & RTF_UP) != 0)&&((rtm->rtm_flags & RTF_MULTICAST) == 0)) {
- RoutingTable::Entry e;
- e.deviceIndex = -9999; // unset
-
- int which = 0;
- while (saptr < saend) {
- struct sockaddr *sa = (struct sockaddr *)saptr;
- unsigned int salen = sa->sa_len;
- if (!salen)
- break;
-
- // Skip missing fields in rtm_addrs bit field
- while ((rtm->rtm_addrs & 1) == 0) {
- rtm->rtm_addrs >>= 1;
- ++which;
- if (which > 6)
- break;
- }
- if (which > 6)
- break;
-
- rtm->rtm_addrs >>= 1;
- switch(which++) {
- case 0:
- //printf("RTA_DST\n");
- if (sa->sa_family == AF_INET6) {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
- // Nobody expects the Spanish inquisition!
- if ((sin6->sin6_addr.s6_addr[0] == 0xfe)&&((sin6->sin6_addr.s6_addr[1] & 0xc0) == 0x80)) {
- // Our chief weapon is... in-band signaling!
- // Seriously who in the living fuck thought this was a good idea and
- // then had the sadistic idea to not document it anywhere? Of course it's
- // not like there is any documentation on BSD sysctls anyway.
- unsigned int interfaceIndex = ((((unsigned int)sin6->sin6_addr.s6_addr[2]) << 8) & 0xff) | (((unsigned int)sin6->sin6_addr.s6_addr[3]) & 0xff);
- sin6->sin6_addr.s6_addr[2] = 0;
- sin6->sin6_addr.s6_addr[3] = 0;
- if (!sin6->sin6_scope_id)
- sin6->sin6_scope_id = interfaceIndex;
- }
- }
- e.destination = *sa;
- break;
- case 1:
- //printf("RTA_GATEWAY\n");
- switch(sa->sa_family) {
- case AF_LINK:
- e.deviceIndex = (int)((const struct sockaddr_dl *)sa)->sdl_index;
- break;
- case AF_INET:
- case AF_INET6:
- e.gateway = *sa;
- break;
- }
- break;
- case 2: {
- if (e.destination.isV6()) {
- salen = sizeof(struct sockaddr_in6); // Confess!
- unsigned int bits = 0;
- for(int i=0;i<16;++i) {
- unsigned char c = (unsigned char)((const struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[i];
- if (c == 0xff)
- bits += 8;
- else break;
- /* must they be multiples of 8? Most of the BSD source I can find says yes..?
- else {
- while ((c & 0x80) == 0x80) {
- ++bits;
- c <<= 1;
- }
- break;
- }
- */
- }
- e.destination.setPort(bits);
- } else {
- salen = sizeof(struct sockaddr_in); // Confess!
- e.destination.setPort((unsigned int)Utils::countBits((uint32_t)((const struct sockaddr_in *)sa)->sin_addr.s_addr));
- }
- //printf("RTA_NETMASK\n");
- } break;
- /*
- case 3:
- //printf("RTA_GENMASK\n");
- break;
- case 4:
- //printf("RTA_IFP\n");
- break;
- case 5:
- //printf("RTA_IFA\n");
- break;
- case 6:
- //printf("RTA_AUTHOR\n");
- break;
- */
- }
-
- saptr += salen;
- }
-
- e.metric = (int)rtm->rtm_rmx.rmx_hopcount;
- if (e.metric < 0)
- e.metric = 0;
-
- InetAddress::IpScope dscope = e.destination.ipScope();
- if ( ((includeLinkLocal)||(dscope != InetAddress::IP_SCOPE_LINK_LOCAL)) && ((includeLoopback)||((dscope != InetAddress::IP_SCOPE_LOOPBACK) && (e.gateway.ipScope() != InetAddress::IP_SCOPE_LOOPBACK) )))
- entries.push_back(e);
- }
-
- next = saend;
- }
- }
-
- ::free(buf);
- }
- }
-
- for(std::vector<ZeroTier::RoutingTable::Entry>::iterator e1(entries.begin());e1!=entries.end();++e1) {
- if ((!e1->device[0])&&(e1->deviceIndex >= 0))
- if_indextoname(e1->deviceIndex,e1->device);
- }
- for(std::vector<ZeroTier::RoutingTable::Entry>::iterator e1(entries.begin());e1!=entries.end();++e1) {
- if ((!e1->device[0])&&(e1->gateway)) {
- int bestMetric = 9999999;
- for(std::vector<ZeroTier::RoutingTable::Entry>::iterator e2(entries.begin());e2!=entries.end();++e2) {
- if ((e2->destination.containsAddress(e1->gateway))&&(e2->metric <= bestMetric)) {
- bestMetric = e2->metric;
- Utils::scopy(e1->device,sizeof(e1->device),e2->device);
- }
- }
- }
- }
-
- std::sort(entries.begin(),entries.end());
-
- return entries;
-}
-
-RoutingTable::Entry RoutingTable::set(const InetAddress &destination,const InetAddress &gateway,const char *device,int metric,bool ifscope)
-{
- if ((!gateway)&&((!device)||(!device[0])))
- return RoutingTable::Entry();
-
- std::vector<RoutingTable::Entry> rtab(get(true,true));
-
- for(std::vector<RoutingTable::Entry>::iterator e(rtab.begin());e!=rtab.end();++e) {
- if (e->destination == destination) {
- if (((!device)||(!device[0]))||(!strcmp(device,e->device))) {
- long p = (long)fork();
- if (p > 0) {
- int exitcode = -1;
- ::waitpid(p,&exitcode,0);
- } else if (p == 0) {
- ::close(STDOUT_FILENO);
- ::close(STDERR_FILENO);
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,"delete",(destination.isV6() ? "-inet6" : "-inet"),destination.toString().c_str(),(const char *)0);
- ::_exit(-1);
- }
- }
- }
- }
-
- if (metric < 0)
- return RoutingTable::Entry();
-
- {
- char hcstr[64];
- Utils::snprintf(hcstr,sizeof(hcstr),"%d",metric);
- long p = (long)fork();
- if (p > 0) {
- int exitcode = -1;
- ::waitpid(p,&exitcode,0);
- } else if (p == 0) {
- ::close(STDOUT_FILENO);
- ::close(STDERR_FILENO);
- if (gateway) {
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,"add",(destination.isV6() ? "-inet6" : "-inet"),destination.toString().c_str(),gateway.toIpString().c_str(),"-hopcount",hcstr,(const char *)0);
- } else if ((device)&&(device[0])) {
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,"add",(destination.isV6() ? "-inet6" : "-inet"),destination.toString().c_str(),"-interface",device,"-hopcount",hcstr,(const char *)0);
- }
- ::_exit(-1);
- }
- }
-
- rtab = get(true,true);
- std::vector<RoutingTable::Entry>::iterator bestEntry(rtab.end());
- for(std::vector<RoutingTable::Entry>::iterator e(rtab.begin());e!=rtab.end();++e) {
- if ((e->destination == destination)&&(e->gateway.ipsEqual(gateway))) {
- if ((device)&&(device[0])) {
- if (!strcmp(device,e->device)) {
- if (metric == e->metric)
- bestEntry = e;
- }
- }
- if (bestEntry == rtab.end())
- bestEntry = e;
- }
- }
- if (bestEntry != rtab.end())
- return *bestEntry;
-
- return RoutingTable::Entry();
-}
-
-#endif // __BSD__
-
-// ---------------------------------------------------------------------------
-
-#ifdef __WINDOWS__
-
-static void _copyInetAddressToSockaddrInet(const InetAddress &a,SOCKADDR_INET &sinet)
-{
- memset(&sinet,0,sizeof(sinet));
- if (a.isV4()) {
- sinet.Ipv4.sin_addr.S_un.S_addr = *((const uint32_t *)a.rawIpData());
- sinet.Ipv4.sin_family = AF_INET;
- sinet.Ipv4.sin_port = htons(a.port());
- } else if (a.isV6()) {
- memcpy(sinet.Ipv6.sin6_addr.u.Byte,a.rawIpData(),16);
- sinet.Ipv6.sin6_family = AF_INET6;
- sinet.Ipv6.sin6_port = htons(a.port());
- }
-}
-
-std::vector<RoutingTable::Entry> RoutingTable::get(bool includeLinkLocal,bool includeLoopback) const
-{
- std::vector<RoutingTable::Entry> entries;
- PMIB_IPFORWARD_TABLE2 rtbl = NULL;
-
- if (GetIpForwardTable2(AF_UNSPEC,&rtbl) != NO_ERROR)
- return entries;
- if (!rtbl)
- return entries;
-
- for(ULONG r=0;r<rtbl->NumEntries;++r) {
- RoutingTable::Entry e;
- switch(rtbl->Table[r].DestinationPrefix.Prefix.si_family) {
- case AF_INET:
- e.destination.set(&(rtbl->Table[r].DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr),4,rtbl->Table[r].DestinationPrefix.PrefixLength);
- break;
- case AF_INET6:
- e.destination.set(rtbl->Table[r].DestinationPrefix.Prefix.Ipv6.sin6_addr.u.Byte,16,rtbl->Table[r].DestinationPrefix.PrefixLength);
- break;
- }
- switch(rtbl->Table[r].NextHop.si_family) {
- case AF_INET:
- e.gateway.set(&(rtbl->Table[r].NextHop.Ipv4.sin_addr.S_un.S_addr),4,0);
- break;
- case AF_INET6:
- e.gateway.set(rtbl->Table[r].NextHop.Ipv6.sin6_addr.u.Byte,16,0);
- break;
- }
- e.deviceIndex = (int)rtbl->Table[r].InterfaceIndex;
- e.metric = (int)rtbl->Table[r].Metric;
- ConvertInterfaceLuidToNameA(&(rtbl->Table[r].InterfaceLuid),e.device,sizeof(e.device));
- if ((e.destination)&&((includeLinkLocal)||(!e.destination.isLinkLocal()))&&((includeLoopback)||((!e.destination.isLoopback())&&(!e.gateway.isLoopback()))))
- entries.push_back(e);
- }
-
- FreeMibTable(rtbl);
- std::sort(entries.begin(),entries.end());
- return entries;
-}
-
-RoutingTable::Entry RoutingTable::set(const InetAddress &destination,const InetAddress &gateway,const char *device,int metric,bool ifscope)
-{
- NET_LUID luid;
- luid.Value = 0;
- if (ConvertInterfaceNameToLuidA(device,&luid) != NO_ERROR)
- return RoutingTable::Entry();
-
- bool needCreate = true;
- PMIB_IPFORWARD_TABLE2 rtbl = NULL;
- if (GetIpForwardTable2(AF_UNSPEC,&rtbl) != NO_ERROR)
- return RoutingTable::Entry();
- if (!rtbl)
- return RoutingTable::Entry();
- for(ULONG r=0;r<rtbl->NumEntries;++r) {
- if (rtbl->Table[r].InterfaceLuid.Value == luid.Value) {
- InetAddress rdest;
- switch(rtbl->Table[r].DestinationPrefix.Prefix.si_family) {
- case AF_INET:
- rdest.set(&(rtbl->Table[r].DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr),4,rtbl->Table[r].DestinationPrefix.PrefixLength);
- break;
- case AF_INET6:
- rdest.set(rtbl->Table[r].DestinationPrefix.Prefix.Ipv6.sin6_addr.u.Byte,16,rtbl->Table[r].DestinationPrefix.PrefixLength);
- break;
- }
- if (rdest == destination) {
- if (metric >= 0) {
- _copyInetAddressToSockaddrInet(gateway,rtbl->Table[r].NextHop);
- rtbl->Table[r].Metric = metric;
- SetIpForwardEntry2(&(rtbl->Table[r]));
- needCreate = false;
- } else {
- DeleteIpForwardEntry2(&(rtbl->Table[r]));
- FreeMibTable(rtbl);
- return RoutingTable::Entry();
- }
- }
- }
- }
- FreeMibTable(rtbl);
-
- if ((metric >= 0)&&(needCreate)) {
- MIB_IPFORWARD_ROW2 nr;
- InitializeIpForwardEntry(&nr);
- nr.InterfaceLuid.Value = luid.Value;
- _copyInetAddressToSockaddrInet(destination,nr.DestinationPrefix.Prefix);
- nr.DestinationPrefix.PrefixLength = destination.netmaskBits();
- _copyInetAddressToSockaddrInet(gateway,nr.NextHop);
- nr.Metric = metric;
- nr.Protocol = MIB_IPPROTO_NETMGMT;
- DWORD result = CreateIpForwardEntry2(&nr);
- if (result != NO_ERROR)
- return RoutingTable::Entry();
- }
-
- std::vector<RoutingTable::Entry> rtab(get(true,true));
- std::vector<RoutingTable::Entry>::iterator bestEntry(rtab.end());
- for(std::vector<RoutingTable::Entry>::iterator e(rtab.begin());e!=rtab.end();++e) {
- if ((e->destination == destination)&&(e->gateway.ipsEqual(gateway))) {
- if ((device)&&(device[0])) {
- if (!strcmp(device,e->device)) {
- if (metric == e->metric)
- bestEntry = e;
- }
- }
- if (bestEntry == rtab.end())
- bestEntry = e;
- }
- }
- if (bestEntry != rtab.end())
- return *bestEntry;
- return RoutingTable::Entry();
-}
-
-#endif // __WINDOWS__
-
-// ---------------------------------------------------------------------------
-
-} // namespace ZeroTier
diff --git a/osdep/RoutingTable.hpp b/osdep/RoutingTable.hpp
deleted file mode 100644
index 71ca006d..00000000
--- a/osdep/RoutingTable.hpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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/>.
- */
-
-#ifndef ZT_ROUTINGTABLE_HPP
-#define ZT_ROUTINGTABLE_HPP
-
-#include <vector>
-
-#include "../node/Constants.hpp"
-#include "../node/InetAddress.hpp"
-
-namespace ZeroTier {
-
-class RoutingTable
-{
-public:
- struct Entry
- {
- /**
- * Destination IP and netmask bits (CIDR format)
- */
- InetAddress destination;
-
- /**
- * Gateway or null address if direct link-level route, netmask/port part of InetAddress not used
- */
- InetAddress gateway;
-
- /**
- * Metric or hop count -- higher = lower routing priority
- */
- int metric;
-
- /**
- * Device index -- not used on all platforms
- */
- int deviceIndex;
-
- /**
- * Interface scoped route? (always false if not meaningful on this OS)
- */
- bool ifscope;
-
- /**
- * System device name (may be empty if it doesn't exist or isn't important on this OS)
- */
- char device[128];
-
- /**
- * @return True if at least one required field is present (object is not null)
- */
- inline operator bool() const { return ((destination)||(gateway)); }
- };
-
- /**
- * Get routing table
- *
- * @param includeLinkLocal Include link-local IPs?
- * @param includeLoopback Include loopback routes?
- */
- static std::vector<RoutingTable::Entry> get(bool includeLinkLocal,bool includeLoopback);
-
- /**
- * Add or replace a routing table entry
- *
- * @param destination Route destination
- * @param gateway Gateway or null if local
- * @param device Device name (if applicable)
- * @param metric Route metric (if applicable)
- * @param ifscope Interface bound route? If so, device must be set. (only applicable on some OSes)
- */
- static RoutingTable::Entry set(const InetAddress &destination,const InetAddress &gateway,const char *device,int metric,bool ifscope);
-};
-
-} // namespace ZeroTier
-
-#endif