From 08718624522a28a08531553ac2b4ad0a67c2f8df Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 27 Aug 2021 10:42:04 -0400 Subject: [PATCH 1/7] initial routing table refactor * move routing table manipulation to vpn platform * add initial linux implementation of vpn platform route manipulation --- llarp/ev/vpn.hpp | 38 +++++ llarp/router/route_poker.cpp | 34 ++-- llarp/vpn/linux.hpp | 292 ++++++++++++++++++++++++++++++++++- 3 files changed, 351 insertions(+), 13 deletions(-) diff --git a/llarp/ev/vpn.hpp b/llarp/ev/vpn.hpp index 6f5c3c540..f911a310b 100644 --- a/llarp/ev/vpn.hpp +++ b/llarp/ev/vpn.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace llarp { struct Context; @@ -59,6 +61,38 @@ namespace llarp::vpn WritePacket(net::IPPacket pkt) = 0; }; + class IRouteManager + { + public: + using IPVariant_t = std::variant; + + IRouteManager() = default; + IRouteManager(const IRouteManager&) = delete; + IRouteManager(IRouteManager&&) = delete; + virtual ~IRouteManager() = default; + + virtual void + AddRoute(IPVariant_t ip, IPVariant_t gateway) = 0; + + virtual void + DelRoute(IPVariant_t ip, IPVariant_t gateway) = 0; + + virtual void + AddDefaultRouteViaInterface(std::string ifname) = 0; + + virtual void + DelDefaultRouteViaInterface(std::string ifname) = 0; + + virtual std::vector + GetGatewaysNotOnInterface(std::string ifname) = 0; + + virtual void + AddBlackhole() = 0; + + virtual void + DelBlackhole() = 0; + }; + /// a vpn platform /// responsible for obtaining vpn interfaces class Platform @@ -73,6 +107,10 @@ namespace llarp::vpn /// blocks until ready, throws on error virtual std::shared_ptr ObtainInterface(InterfaceInfo info) = 0; + + /// get owned ip route manager for managing routing table + virtual IRouteManager& + RouteManager() = 0; }; /// create native vpn platform diff --git a/llarp/router/route_poker.cpp b/llarp/router/route_poker.cpp index 2d9b1e3ee..5e44c82e2 100644 --- a/llarp/router/route_poker.cpp +++ b/llarp/router/route_poker.cpp @@ -30,13 +30,15 @@ namespace llarp void RoutePoker::DisableRoute(huint32_t ip, huint32_t gateway) { - net::DelRoute(ip.ToString(), gateway.ToString()); + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + route.DelRoute(ip, gateway); } void RoutePoker::EnableRoute(huint32_t ip, huint32_t gateway) { - net::AddRoute(ip.ToString(), gateway.ToString()); + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + route.AddRoute(ip, gateway); } void @@ -86,12 +88,13 @@ namespace llarp RoutePoker::~RoutePoker() { + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); for (const auto& [ip, gateway] : m_PokedRoutes) { if (gateway.h) - net::DelRoute(ip.ToString(), gateway.ToString()); + route.DelRoute(ip, gateway); } - net::DelBlackhole(); + route.DelBlackhole(); } std::optional @@ -101,14 +104,17 @@ namespace llarp throw std::runtime_error("Attempting to use RoutePoker before calling Init"); const auto ep = m_Router->hiddenServiceContext().GetDefault(); - const auto gateways = net::GetGatewaysNotOnInterface(ep->GetIfName()); + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + const auto gateways = route.GetGatewaysNotOnInterface(ep->GetIfName()); if (gateways.empty()) { return std::nullopt; } - huint32_t addr{}; - addr.FromString(gateways[0]); - return addr; + if (auto* ptr = std::get_if(&gateways[0])) + { + return huint32_t{*ptr}; + } + return std::nullopt; } void @@ -186,15 +192,17 @@ namespace llarp void RoutePoker::Up() { + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + // black hole all routes by default - net::AddBlackhole(); + route.AddBlackhole(); // explicit route pokes for first hops m_Router->ForEachPeer( [&](auto session, auto) mutable { AddRoute(session->GetRemoteEndpoint().asIPv4()); }, false); // add default route const auto ep = m_Router->hiddenServiceContext().GetDefault(); - net::AddDefaultRouteViaInterface(ep->GetIfName()); + route.AddDefaultRouteViaInterface(ep->GetIfName()); } void @@ -206,9 +214,11 @@ namespace llarp false); // remove default route const auto ep = m_Router->hiddenServiceContext().GetDefault(); - net::DelDefaultRouteViaInterface(ep->GetIfName()); + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + + route.DelDefaultRouteViaInterface(ep->GetIfName()); // delete route blackhole - net::DelBlackhole(); + route.DelBlackhole(); } } // namespace llarp diff --git a/llarp/vpn/linux.hpp b/llarp/vpn/linux.hpp index 8d67a6256..a50f14f04 100644 --- a/llarp/vpn/linux.hpp +++ b/llarp/vpn/linux.hpp @@ -6,9 +6,19 @@ #include #include #include "common.hpp" -#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + namespace llarp::vpn { struct in6_ifreq @@ -121,14 +131,294 @@ namespace llarp::vpn } }; + class LinuxRouteManager : public IRouteManager + { + const int fd; + + enum class GatewayMode + { + eFirstHop, + eLowerDefault, + eUpperDefault + }; + + struct NLRequest + { + nlmsghdr n; + rtmsg r; + char buf[4096]; + + void + AddData(int type, const void* data, int alen) + { +#define NLMSG_TAIL(nmsg) ((struct rtattr*)(((intptr_t)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + + int len = RTA_LENGTH(alen); + rtattr* rta; + if (NLMSG_ALIGN(n.nlmsg_len) + RTA_ALIGN(len) > sizeof(*this)) + { + throw std::length_error{"nlrequest add data overflow"}; + } + rta = NLMSG_TAIL(&n); + rta->rta_type = type; + rta->rta_len = len; + if (alen) + { + memcpy(RTA_DATA(rta), data, alen); + } + n.nlmsg_len = NLMSG_ALIGN(n.nlmsg_len) + RTA_ALIGN(len); +#undef NLMSG_TAIL + } + }; + + /* Helper structure for ip address data and attributes */ + struct _inet_addr + { + unsigned char family; + unsigned char bitlen; + unsigned char data[sizeof(struct in6_addr)]; + + _inet_addr(huint32_t addr, int bits = 32) + { + family = AF_INET; + bitlen = bits; + htobe32buf(data, addr.h); + } + + _inet_addr(huint128_t addr, int bits = 128) + { + family = AF_INET6; + bitlen = bits; + const nuint128_t net = ToNet(addr); + std::memcpy(data, &net, 16); + } + }; + + void + Blackhole(int cmd, int flags, int af) + { + NLRequest nl_request{}; + /* Initialize request structure */ + nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags; + nl_request.n.nlmsg_type = cmd; + nl_request.n.nlmsg_pid = getpid(); + nl_request.r.rtm_family = af; + nl_request.r.rtm_table = RT_TABLE_LOCAL; + nl_request.r.rtm_type = RTN_BLACKHOLE; + nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE; + if (af == AF_INET) + { + uint32_t addr{}; + nl_request.AddData(RTA_DST, &addr, sizeof(addr)); + } + else + { + uint128_t addr{}; + nl_request.AddData(RTA_DST, &addr, sizeof(addr)); + } + send(fd, &nl_request, sizeof(nl_request), 0); + } + + void + Route( + int cmd, + int flags, + const _inet_addr& dst, + const _inet_addr& gw, + GatewayMode mode, + int if_idx) + { + NLRequest nl_request{}; + + /* Initialize request structure */ + nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags; + nl_request.n.nlmsg_type = cmd; + nl_request.n.nlmsg_pid = getpid(); + nl_request.r.rtm_family = dst.family; + nl_request.r.rtm_table = RT_TABLE_MAIN; + if (if_idx) + { + nl_request.r.rtm_scope = RT_SCOPE_LINK; + } + else + { + nl_request.r.rtm_scope = RT_SCOPE_NOWHERE; + } + /* Set additional flags if NOT deleting route */ + if (cmd != RTM_DELROUTE) + { + nl_request.r.rtm_protocol = RTPROT_BOOT; + nl_request.r.rtm_type = RTN_UNICAST; + } + + nl_request.r.rtm_family = dst.family; + nl_request.r.rtm_dst_len = dst.bitlen; + nl_request.r.rtm_scope = 0; + + /* Set gateway */ + if (gw.bitlen != 0 and dst.family == AF_INET) + { + nl_request.AddData(RTA_GATEWAY, &gw.data, gw.bitlen / 8); + } + nl_request.r.rtm_family = gw.family; + if (mode == GatewayMode::eFirstHop) + { + nl_request.AddData(RTA_DST, &dst.data, dst.bitlen / 8); + /* Set interface */ + nl_request.AddData(RTA_OIF, &if_idx, sizeof(int)); + } + if (mode == GatewayMode::eUpperDefault) + { + if (dst.family == AF_INET) + { + nl_request.AddData(RTA_DST, &dst.data, sizeof(uint32_t)); + } + else + { + nl_request.AddData(RTA_OIF, &if_idx, sizeof(int)); + nl_request.AddData(RTA_DST, &dst.data, sizeof(in6_addr)); + } + } + /* Send message to the netlink */ + send(fd, &nl_request, sizeof(nl_request), 0); + } + + void + DefaultRouteViaInterface(std::string ifname, int cmd, int flags) + { + int if_idx = if_nametoindex(ifname.c_str()); + const auto maybe = GetInterfaceAddr(ifname); + if (not maybe) + throw std::runtime_error{"we dont have our own network interface?"}; + + const _inet_addr gateway{maybe->asIPv4()}; + const _inet_addr lower{ipaddr_ipv4_bits(0, 0, 0, 0), 1}; + const _inet_addr upper{ipaddr_ipv4_bits(128, 0, 0, 0), 1}; + + Route(cmd, flags, lower, gateway, GatewayMode::eLowerDefault, if_idx); + Route(cmd, flags, upper, gateway, GatewayMode::eUpperDefault, if_idx); + + if (const auto maybe6 = GetInterfaceIPv6Address(ifname)) + { + const _inet_addr gateway6{*maybe6, 128}; + for (const std::string str : {"::", "4000::", "8000::", "c000::"}) + { + huint128_t _hole{}; + _hole.FromString(str); + const _inet_addr hole6{_hole, 2}; + Route(cmd, flags, hole6, gateway6, GatewayMode::eUpperDefault, if_idx); + } + } + } + + void + Route(int cmd, int flags, IPVariant_t ip, IPVariant_t gateway) + { + // do bullshit double std::visit because lol variants + std::visit( + [gateway, cmd, flags, this](auto&& ip) { + const _inet_addr toAddr{ip}; + std::visit( + [toAddr, cmd, flags, this](auto&& gateway) { + const _inet_addr gwAddr{gateway}; + Route(cmd, flags, toAddr, gwAddr, GatewayMode::eFirstHop, 0); + }, + gateway); + }, + ip); + } + + public: + LinuxRouteManager() : fd{socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)} + { + if (fd == -1) + throw std::runtime_error{"failed to make netlink socket"}; + } + + ~LinuxRouteManager() + { + close(fd); + } + + void + AddRoute(IPVariant_t ip, IPVariant_t gateway) override + { + Route(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, ip, gateway); + } + + void + DelRoute(IPVariant_t ip, IPVariant_t gateway) override + { + Route(RTM_DELROUTE, 0, ip, gateway); + } + + void + AddDefaultRouteViaInterface(std::string ifname) override + { + DefaultRouteViaInterface(ifname, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL); + } + + void + DelDefaultRouteViaInterface(std::string ifname) override + { + DefaultRouteViaInterface(ifname, RTM_DELROUTE, 0); + } + + std::vector + GetGatewaysNotOnInterface(std::string ifname) override + { + std::vector gateways{}; + std::ifstream inf{"/proc/net/route"}; + for (std::string line; std::getline(inf, line);) + { + const auto parts = split(line, "\t"); + if (parts[1].find_first_not_of('0') == std::string::npos and parts[0] != ifname) + { + const auto& ip = parts[2]; + if ((ip.size() == sizeof(uint32_t) * 2) and oxenmq::is_hex(ip)) + { + huint32_t x{}; + oxenmq::from_hex(ip.begin(), ip.end(), reinterpret_cast(&x.h)); + gateways.emplace_back(x); + } + } + } + return gateways; + } + + void + AddBlackhole() override + { + Blackhole(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, AF_INET); + Blackhole(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, AF_INET6); + } + + void + DelBlackhole() override + { + Blackhole(RTM_DELROUTE, 0, AF_INET); + Blackhole(RTM_DELROUTE, 0, AF_INET6); + } + }; + class LinuxPlatform : public Platform { + LinuxRouteManager _routeManager{}; + public: std::shared_ptr ObtainInterface(InterfaceInfo info) override { return std::make_shared(std::move(info)); }; + + IRouteManager& + RouteManager() override + { + return _routeManager; + } }; } // namespace llarp::vpn From 9791fd62a070b0eeb1f10dc0e8e39424dc944032 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 27 Aug 2021 11:02:21 -0400 Subject: [PATCH 2/7] initial win32 port of route manager --- llarp/vpn/win32.hpp | 156 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/llarp/vpn/win32.hpp b/llarp/vpn/win32.hpp index 405cfabe1..d56cf4b8b 100644 --- a/llarp/vpn/win32.hpp +++ b/llarp/vpn/win32.hpp @@ -159,6 +159,16 @@ namespace llarp::vpn } public: + static std::string + RouteCommand() + { + std::wstring wcmd = get_win_sys_path() + L"\\route.exe"; + + using convert_type = std::codecvt_utf8; + std::wstring_convert converter; + return converter.to_bytes(wcmd); + } + Win32Interface(InterfaceInfo info) : m_ReadQueue{1024}, m_Info{std::move(info)} { DWORD len; @@ -433,8 +443,149 @@ namespace llarp::vpn } }; + template + void + ForEachWIN32Interface(Visit visit) + { +#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) +#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) + MIB_IPFORWARDTABLE* pIpForwardTable; + DWORD dwSize = 0; + DWORD dwRetVal = 0; + + pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(sizeof(MIB_IPFORWARDTABLE)); + if (pIpForwardTable == nullptr) + return; + + if (GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) + { + FREE(pIpForwardTable); + pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(dwSize); + if (pIpForwardTable == nullptr) + { + return; + } + } + + if ((dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, 0)) == NO_ERROR) + { + for (int i = 0; i < (int)pIpForwardTable->dwNumEntries; i++) + { + visit(&pIpForwardTable->table[i]); + } + } + FREE(pIpForwardTable); +#undef MALLOC +#undef FREE + } + + class Win32RouteManager : public IRouteManager + { + void + Execute(std::string cmd) const + { + LogInfo(cmd); + ::system(cmd.c_str()); + } + + static std::string + RouteCommand() + { + return Win32Interface::RouteCommand(); + } + + void + Route(IPVariant_t ip, IPVariant_t gateway, std::string cmd) + { + std::stringstream ss; + std::string ip_str; + std::string gateway_str; + + std::visit([&ip_str](auto&& ip) { ip_str = ip.ToString(); }, ip); + std::visit([&gateway_str](auto&& gateway) { gateway_str = gateway.ToString(); }, gateway); + + ss << RouteCommand() << " " << cmd << " " << ip_str << " MASK 255.255.255.255 " << gateway_str + << " METRIC 2"; + Execute(ss.str()); + } + + void + DefaultRouteViaInterface(std::string ifname, std::string cmd) + { + // poke hole for loopback bacause god is dead on windows + Execute(RouteCommand() + " " + cmd + " 127.0.0.0 MASK 255.0.0.0 0.0.0.0"); + + huint32_t ip{}; + ip.FromString(ifname); + const auto ipv6 = net::ExpandV4Lan(ip); + + Execute(RouteCommand() + " " + cmd + " ::/2 " + ipv6.ToString()); + Execute(RouteCommand() + " " + cmd + " 4000::/2 " + ipv6.ToString()); + Execute(RouteCommand() + " " + cmd + " 8000::/2 " + ipv6.ToString()); + Execute(RouteCommand() + " " + cmd + " c000::/2 " + ipv6.ToString()); + + ifname.back()++; + Execute(RouteCommand() + " " + cmd + " 0.0.0.0 MASK 128.0.0.0 " + ifname); + Execute(RouteCommand() + " " + cmd + " 128.0.0.0 MASK 128.0.0.0 " + ifname); + } + + public: + void + AddRoute(IPVariant_t ip, IPVariant_t gateway) override + { + Route(ip, gateway, "ADD"); + } + + void + DelRoute(IPVariant_t ip, IPVariant_t gateway) override + { + Route(ip, gateway, "DELETE"); + } + + void + AddBlackhole() override{}; + + void + DelBlackhole() override{}; + + std::vector + GetGatewaysNotOnInterface(std::string ifname) override + { + std::vector gateways; + ForEachWIN32Interface([&](auto w32interface) { + struct in_addr gateway, interface_addr; + gateway.S_un.S_addr = (u_long)w32interface->dwForwardDest; + interface_addr.S_un.S_addr = (u_long)w32interface->dwForwardNextHop; + std::string interface_name{inet_ntoa(interface_addr)}; + if ((!gateway.S_un.S_addr) and interface_name != ifname) + { + llarp::LogTrace( + "Win32 find gateway: Adding gateway (", interface_name, ") to list of gateways."); + huint32_t x{}; + if (x.FromString(interface_name)) + gateways.push_back(x); + } + }); + return gateways; + } + + void + AddDefaultRouteViaInterface(std::string ifname) override + { + DefaultRouteViaInterface(ifname, "ADD"); + } + + void + DelDefaultRouteViaInterface(std::string ifname) override + { + DefaultRouteViaInterface(ifname, "DELETE"); + } + }; + class Win32Platform : public Platform { + Win32RouteManager _routeManager{}; + public: std::shared_ptr ObtainInterface(InterfaceInfo info) override @@ -443,6 +594,11 @@ namespace llarp::vpn netif->Start(); return netif; }; + IRouteManager& + RouteManager() override + { + return _routeManager; + } }; } // namespace llarp::vpn From 64cd2990bcfe39b6ba3577cb8233f86fd5e36e82 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 27 Aug 2021 11:07:54 -0400 Subject: [PATCH 3/7] remove old routing table maniuplation code --- llarp/CMakeLists.txt | 1 - llarp/net/route.cpp | 616 ----------------------------------- llarp/net/route.hpp | 36 -- llarp/router/route_poker.cpp | 1 - llarp/router/router.cpp | 1 - llarp/rpc/rpc_server.cpp | 1 - llarp/vpn/win32.hpp | 89 ++--- 7 files changed, 52 insertions(+), 693 deletions(-) delete mode 100644 llarp/net/route.cpp delete mode 100644 llarp/net/route.hpp diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index d1aed3353..3ae31ae4e 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -52,7 +52,6 @@ add_library(lokinet-platform net/ip_range.cpp net/net.cpp net/net_int.cpp - net/route.cpp net/sock_addr.cpp vpn/packet_router.cpp vpn/platform.cpp diff --git a/llarp/net/route.cpp b/llarp/net/route.cpp deleted file mode 100644 index 604208465..000000000 --- a/llarp/net/route.cpp +++ /dev/null @@ -1,616 +0,0 @@ -#include "route.hpp" - -#ifdef __linux__ -#include -#include -#include -#include -#include -#ifndef ANDROID -#include -#include -#endif -#include "net.hpp" -#include -#include -#endif -#ifdef __APPLE__ -#include "net.hpp" -#include -#endif -#ifdef _WIN32 -#include -#include -#include -#include -#include -#include -#include -#include -#include "net_int.hpp" -#include "ip.hpp" -#endif - -#include -#include -#include - -namespace llarp::net -{ -#ifndef __linux__ - void - Execute(std::string cmd) - { - LogInfo(cmd); - system(cmd.c_str()); - } -#endif - -#ifdef __linux__ -#ifndef ANDROID - - enum class GatewayMode - { - eFirstHop, - eLowerDefault, - eUpperDefault - }; - - struct NLSocket - { - NLSocket() : fd(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) - { - if (fd == -1) - throw std::runtime_error("failed to make netlink socket"); - } - - ~NLSocket() - { - if (fd != -1) - close(fd); - } - - const int fd; - }; - - /* Helper structure for ip address data and attributes */ - typedef struct - { - unsigned char family; - unsigned char bitlen; - unsigned char data[sizeof(struct in6_addr)]; - } _inet_addr; - - /* */ - -#define NLMSG_TAIL(nmsg) ((struct rtattr*)(((intptr_t)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) - - /* Add new data to rtattr */ - int - rtattr_add(struct nlmsghdr* n, unsigned int maxlen, int type, const void* data, int alen) - { - int len = RTA_LENGTH(alen); - struct rtattr* rta; - - if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) - { - fprintf(stderr, "rtattr_add error: message exceeded bound of %d\n", maxlen); - return -1; - } - - rta = NLMSG_TAIL(n); - rta->rta_type = type; - rta->rta_len = len; - - if (alen) - { - memcpy(RTA_DATA(rta), data, alen); - } - - n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); - - return 0; - } - - struct nl_req - { - struct nlmsghdr n; - struct rtmsg r; - char buf[4096]; - }; - - /// add/remove a route blackhole - int - do_blackhole(int sock, int cmd, int flags, int af) - { - nl_req nl_request{}; - /* Initialize request structure */ - nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags; - nl_request.n.nlmsg_type = cmd; - nl_request.n.nlmsg_pid = getpid(); - nl_request.r.rtm_family = af; - nl_request.r.rtm_table = RT_TABLE_LOCAL; - nl_request.r.rtm_type = RTN_BLACKHOLE; - nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE; - if (af == AF_INET) - { - uint32_t addr{}; - rtattr_add(&nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, &addr, sizeof(addr)); - } - else - { - uint128_t addr{}; - rtattr_add(&nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, &addr, sizeof(addr)); - } - return send(sock, &nl_request, sizeof(nl_request), 0); - } - - int - do_route( - int sock, - int cmd, - int flags, - const _inet_addr* dst, - const _inet_addr* gw, - GatewayMode mode, - int if_idx) - { - nl_req nl_request{}; - - /* Initialize request structure */ - nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags; - nl_request.n.nlmsg_type = cmd; - nl_request.n.nlmsg_pid = getpid(); - nl_request.r.rtm_family = dst->family; - nl_request.r.rtm_table = RT_TABLE_MAIN; - if (if_idx) - { - nl_request.r.rtm_scope = RT_SCOPE_LINK; - } - else - { - nl_request.r.rtm_scope = RT_SCOPE_NOWHERE; - } - /* Set additional flags if NOT deleting route */ - if (cmd != RTM_DELROUTE) - { - nl_request.r.rtm_protocol = RTPROT_BOOT; - nl_request.r.rtm_type = RTN_UNICAST; - } - - nl_request.r.rtm_family = dst->family; - nl_request.r.rtm_dst_len = dst->bitlen; - nl_request.r.rtm_scope = 0; - - /* Set gateway */ - if (gw->bitlen != 0 and dst->family == AF_INET) - { - rtattr_add(&nl_request.n, sizeof(nl_request), RTA_GATEWAY, &gw->data, gw->bitlen / 8); - } - nl_request.r.rtm_family = gw->family; - if (mode == GatewayMode::eFirstHop) - { - rtattr_add( - &nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, &dst->data, dst->bitlen / 8); - /* Set interface */ - rtattr_add(&nl_request.n, sizeof(nl_request), RTA_OIF, &if_idx, sizeof(int)); - } - if (mode == GatewayMode::eUpperDefault) - { - if (dst->family == AF_INET) - { - rtattr_add( - &nl_request.n, - sizeof(nl_request), - /*RTA_NEWDST*/ RTA_DST, - &dst->data, - sizeof(uint32_t)); - } - else - { - rtattr_add(&nl_request.n, sizeof(nl_request), RTA_OIF, &if_idx, sizeof(int)); - rtattr_add( - &nl_request.n, - sizeof(nl_request), - /*RTA_NEWDST*/ RTA_DST, - &dst->data, - sizeof(in6_addr)); - } - } - /* Send message to the netlink */ - return send(sock, &nl_request, sizeof(nl_request), 0); - } - - int - read_addr(const char* addr, _inet_addr* res, int bitlen = 32) - { - if (strchr(addr, ':')) - { - res->family = AF_INET6; - res->bitlen = bitlen; - } - else - { - res->family = AF_INET; - res->bitlen = bitlen; - } - return inet_pton(res->family, addr, res->data); - } - -#endif -#endif - -#ifdef _WIN32 - - std::wstring - get_win_sys_path() - { - wchar_t win_sys_path[MAX_PATH] = {0}; - const wchar_t* default_sys_path = L"C:\\Windows\\system32"; - - if (!GetSystemDirectoryW(win_sys_path, _countof(win_sys_path))) - { - wcsncpy(win_sys_path, default_sys_path, _countof(win_sys_path)); - win_sys_path[_countof(win_sys_path) - 1] = L'\0'; - } - return win_sys_path; - } - - std::string - RouteCommand() - { - std::wstring wcmd = get_win_sys_path() + L"\\route.exe"; - - using convert_type = std::codecvt_utf8; - std::wstring_convert converter; - return converter.to_bytes(wcmd); - } - - std::string - NetshCommand() - { - std::wstring wcmd = get_win_sys_path() + L"\\netsh.exe"; - - using convert_type = std::codecvt_utf8; - std::wstring_convert converter; - return converter.to_bytes(wcmd); - } - - template - void - ForEachWIN32Interface(Visit visit) - { -#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) -#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) - MIB_IPFORWARDTABLE* pIpForwardTable; - DWORD dwSize = 0; - DWORD dwRetVal = 0; - - pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(sizeof(MIB_IPFORWARDTABLE)); - if (pIpForwardTable == nullptr) - return; - - if (GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) - { - FREE(pIpForwardTable); - pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(dwSize); - if (pIpForwardTable == nullptr) - { - return; - } - } - - if ((dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, 0)) == NO_ERROR) - { - for (int i = 0; i < (int)pIpForwardTable->dwNumEntries; i++) - { - visit(&pIpForwardTable->table[i]); - } - } - FREE(pIpForwardTable); -#undef MALLOC -#undef FREE - } - - std::optional - GetInterfaceIndex(huint32_t ip) - { - std::optional ret = std::nullopt; - ForEachWIN32Interface([&ret, n = ToNet(ip)](auto* iface) { - if (ret.has_value()) - return; - if (iface->dwForwardNextHop == n.n) - { - ret = iface->dwForwardIfIndex; - } - }); - return ret; - } - -#endif - - void - AddRoute(std::string ip, std::string gateway) - { - LogInfo("Add route: ", ip, " via ", gateway); -#ifdef __linux__ -#ifndef ANDROID - NLSocket sock; - int if_idx = 0; - _inet_addr to_addr{}; - _inet_addr gw_addr{}; - int nl_cmd = RTM_NEWROUTE; - int nl_flags = NLM_F_CREATE | NLM_F_EXCL; - read_addr(gateway.c_str(), &gw_addr); - read_addr(ip.c_str(), &to_addr); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eFirstHop, if_idx); -#endif -#else - std::stringstream ss; -#if _WIN32 - ss << RouteCommand() << " ADD " << ip << " MASK 255.255.255.255 " << gateway << " METRIC 2"; -#elif __APPLE__ - ss << "/sbin/route -n add -host " << ip << " " << gateway; -#else -#error unsupported platform -#endif - Execute(ss.str()); -#endif - } - - void - DelRoute(std::string ip, std::string gateway) - { - LogInfo("Delete route: ", ip, " via ", gateway); -#ifdef __linux__ -#ifndef ANDROID - NLSocket sock; - int if_idx = 0; - _inet_addr to_addr{}; - _inet_addr gw_addr{}; - int nl_cmd = RTM_DELROUTE; - int nl_flags = 0; - read_addr(gateway.c_str(), &gw_addr); - read_addr(ip.c_str(), &to_addr); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eFirstHop, if_idx); -#endif -#else - std::stringstream ss; -#if _WIN32 - ss << RouteCommand() << " DELETE " << ip << " MASK 255.255.255.255 " << gateway << " METRIC 2"; -#elif __APPLE__ - ss << "/sbin/route -n delete -host " << ip << " " << gateway; -#else -#error unsupported platform -#endif - Execute(ss.str()); -#endif - } - - void - AddBlackhole() - { - LogInfo("adding route blackhole to drop all traffic"); -#if __linux__ -#ifndef ANDROID - NLSocket sock; - do_blackhole(sock.fd, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, AF_INET); - do_blackhole(sock.fd, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, AF_INET6); -#endif -#endif - } - - void - DelBlackhole() - { - LogInfo("remove route blackhole"); -#if __linux__ -#ifndef ANDROID - NLSocket sock; - do_blackhole(sock.fd, RTM_DELROUTE, 0, AF_INET); - do_blackhole(sock.fd, RTM_DELROUTE, 0, AF_INET6); -#endif -#endif - } - - void - AddDefaultRouteViaInterface(std::string ifname) - { - LogInfo("Add default route via ", ifname); -#ifdef __linux__ -#ifndef ANDROID - NLSocket sock; - int if_idx = if_nametoindex(ifname.c_str()); - _inet_addr to_addr{}; - _inet_addr gw_addr{}; - const auto maybe = GetInterfaceAddr(ifname); - if (not maybe.has_value()) - throw std::runtime_error("we dont have our own net interface?"); - int nl_cmd = RTM_NEWROUTE; - int nl_flags = NLM_F_CREATE | NLM_F_EXCL; - const auto host = maybe->asIPv4().ToString(); - read_addr(host.c_str(), &gw_addr); - read_addr("0.0.0.0", &to_addr, 1); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eLowerDefault, if_idx); - read_addr("128.0.0.0", &to_addr, 1); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eUpperDefault, if_idx); - const auto maybeInt = GetInterfaceIPv6Address(ifname); - if (maybeInt.has_value()) - { - const auto host = maybeInt->ToString(); - LogInfo("add v6 route via ", host); - read_addr(host.c_str(), &gw_addr, 128); - read_addr("::", &to_addr, 2); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eUpperDefault, if_idx); - read_addr("4000::", &to_addr, 2); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eUpperDefault, if_idx); - read_addr("8000::", &to_addr, 2); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eUpperDefault, if_idx); - read_addr("c000::", &to_addr, 2); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eUpperDefault, if_idx); - } -#endif -#elif _WIN32 - // poke hole for loopback bacause god is dead on windows - Execute(RouteCommand() + " ADD 127.0.0.0 MASK 255.0.0.0 0.0.0.0"); - - huint32_t ip{}; - ip.FromString(ifname); - const auto ipv6 = net::ExpandV4Lan(ip); - - Execute(RouteCommand() + " ADD ::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " ADD 4000::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " ADD 8000::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " ADD c000::/2 " + ipv6.ToString()); - ifname.back()++; - Execute(RouteCommand() + " ADD 0.0.0.0 MASK 128.0.0.0 " + ifname); - Execute(RouteCommand() + " ADD 128.0.0.0 MASK 128.0.0.0 " + ifname); - -#elif __APPLE__ - Execute("/sbin/route -n add -cloning -net 0.0.0.0 -netmask 128.0.0.0 -interface " + ifname); - Execute("/sbin/route -n add -cloning -net 128.0.0.0 -netmask 128.0.0.0 -interface " + ifname); - - Execute("/sbin/route -n add -inet6 -net ::/2 -interface " + ifname); - Execute("/sbin/route -n add -inet6 -net 4000::/2 -interface " + ifname); - Execute("/sbin/route -n add -inet6 -net 8000::/2 -interface " + ifname); - Execute("/sbin/route -n add -inet6 -net c000::/2 -interface " + ifname); -#else -#error unsupported platform -#endif - } - - void - DelDefaultRouteViaInterface(std::string ifname) - { - LogInfo("Remove default route via ", ifname); -#ifdef __linux__ -#ifndef ANDROID - NLSocket sock; - int if_idx = if_nametoindex(ifname.c_str()); - _inet_addr to_addr{}; - _inet_addr gw_addr{}; - const auto maybe = GetInterfaceAddr(ifname); - - if (not maybe.has_value()) - throw std::runtime_error("we dont have our own net interface?"); - int nl_cmd = RTM_DELROUTE; - int nl_flags = 0; - const auto host = maybe->asIPv4().ToString(); - read_addr(host.c_str(), &gw_addr); - read_addr("0.0.0.0", &to_addr, 1); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eLowerDefault, if_idx); - read_addr("128.0.0.0", &to_addr, 1); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eUpperDefault, if_idx); - - const auto maybeInt = GetInterfaceIPv6Address(ifname); - if (maybeInt.has_value()) - { - const auto host = maybeInt->ToString(); - LogInfo("del v6 route via ", host); - read_addr(host.c_str(), &gw_addr, 128); - read_addr("::", &to_addr, 2); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eUpperDefault, if_idx); - read_addr("4000::", &to_addr, 2); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eUpperDefault, if_idx); - read_addr("8000::", &to_addr, 2); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eUpperDefault, if_idx); - read_addr("c000::", &to_addr, 2); - do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, GatewayMode::eUpperDefault, if_idx); - } -#endif -#elif _WIN32 - huint32_t ip{}; - ip.FromString(ifname); - const auto ipv6 = net::ExpandV4Lan(ip); - - Execute(RouteCommand() + " DELETE ::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " DELETE 4000::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " DELETE 8000::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " DELETE c000::/2 " + ipv6.ToString()); - ifname.back()++; - Execute(RouteCommand() + " DELETE 0.0.0.0 MASK 128.0.0.0 " + ifname); - Execute(RouteCommand() + " DELETE 128.0.0.0 MASK 128.0.0.0 " + ifname); - Execute(RouteCommand() + " DELETE 127.0.0.0 MASK 255.0.0.0 0.0.0.0"); -#elif __APPLE__ - Execute("/sbin/route -n delete -cloning -net 0.0.0.0 -netmask 128.0.0.0 -interface " + ifname); - Execute( - "/sbin/route -n delete -cloning -net 128.0.0.0 -netmask 128.0.0.0 -interface " + ifname); - - Execute("/sbin/route -n delete -inet6 -net ::/2 -interface " + ifname); - Execute("/sbin/route -n delete -inet6 -net 4000::/2 -interface " + ifname); - Execute("/sbin/route -n delete -inet6 -net 8000::/2 -interface " + ifname); - Execute("/sbin/route -n delete -inet6 -net c000::/2 -interface " + ifname); - -#else -#error unsupported platform -#endif - } - - std::vector - GetGatewaysNotOnInterface(std::string ifname) - { - std::vector gateways; -#ifdef __linux__ -#ifdef ANDROID -#else - std::ifstream inf("/proc/net/route"); - for (std::string line; std::getline(inf, line);) - { - const auto parts = split(line, "\t"); - if (parts[1].find_first_not_of('0') == std::string::npos and parts[0] != ifname) - { - const auto& ip = parts[2]; - if ((ip.size() == sizeof(uint32_t) * 2) and oxenmq::is_hex(ip)) - { - huint32_t x{}; - oxenmq::from_hex(ip.begin(), ip.end(), reinterpret_cast(&x.h)); - gateways.emplace_back(x.ToString()); - } - } - } -#endif - return gateways; -#elif _WIN32 - ForEachWIN32Interface([&](auto w32interface) { - struct in_addr gateway, interface_addr; - gateway.S_un.S_addr = (u_long)w32interface->dwForwardDest; - interface_addr.S_un.S_addr = (u_long)w32interface->dwForwardNextHop; - std::string interface_name{inet_ntoa(interface_addr)}; - if ((!gateway.S_un.S_addr) and interface_name != ifname) - { - llarp::LogTrace( - "Win32 find gateway: Adding gateway (", interface_name, ") to list of gateways."); - gateways.push_back(std::move(interface_name)); - } - }); - return gateways; -#elif __APPLE__ - LogDebug("get gateways not on ", ifname); - // mac os is so godawful man - FILE* p = popen("/usr/sbin/netstat -rn -f inet", "r"); - if (p == nullptr) - { - return gateways; - } - char* line = nullptr; - size_t len = 0; - ssize_t read = 0; - while ((read = getline(&line, &len, p)) != -1) - { - const std::string line_str(line, len); - const auto parts = llarp::split_any(line_str, " "sv, true); - if (parts[0] == "default" and parts[3] != ifname) - { - gateways.emplace_back(parts[1]); - } - } - pclose(p); - return gateways; -#else -#error unsupported platform -#endif - } - -} // namespace llarp::net diff --git a/llarp/net/route.hpp b/llarp/net/route.hpp deleted file mode 100644 index 23e75f8d6..000000000 --- a/llarp/net/route.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include - -namespace llarp::net -{ - /// get every ip address that is a gateway that isn't owned by interface with name ifname - std::vector - GetGatewaysNotOnInterface(std::string ifname); - - /// add route to ipaddr via gateway ip - void - AddRoute(std::string ipaddr, std::string gateway); - - /// delete route to ipaddr via gateway ip - void - DelRoute(std::string ipaddr, std::string gateway); - - /// add default route via interface with name ifname - void - AddDefaultRouteViaInterface(std::string ifname); - - /// delete default route via interface with name ifname - void - DelDefaultRouteViaInterface(std::string ifname); - - /// add route blackhole for all traffic - void - AddBlackhole(); - - /// delete route blackhole for all traffic - void - DelBlackhole(); - -} // namespace llarp::net diff --git a/llarp/router/route_poker.cpp b/llarp/router/route_poker.cpp index 5e44c82e2..9017125bc 100644 --- a/llarp/router/route_poker.cpp +++ b/llarp/router/route_poker.cpp @@ -1,7 +1,6 @@ #include "route_poker.hpp" #include "abstractrouter.hpp" #include "net/sock_addr.hpp" -#include #include #include diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index d5333ba9f..4fa608afc 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/llarp/rpc/rpc_server.cpp b/llarp/rpc/rpc_server.cpp index ac4dac512..38045d6c4 100644 --- a/llarp/rpc/rpc_server.cpp +++ b/llarp/rpc/rpc_server.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/llarp/vpn/win32.hpp b/llarp/vpn/win32.hpp index d56cf4b8b..f0ad23207 100644 --- a/llarp/vpn/win32.hpp +++ b/llarp/vpn/win32.hpp @@ -117,6 +117,57 @@ namespace llarp::vpn return deviceid; } + template + void + ForEachWIN32Interface(Visit visit) + { +#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) +#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) + MIB_IPFORWARDTABLE* pIpForwardTable; + DWORD dwSize = 0; + DWORD dwRetVal = 0; + + pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(sizeof(MIB_IPFORWARDTABLE)); + if (pIpForwardTable == nullptr) + return; + + if (GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) + { + FREE(pIpForwardTable); + pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(dwSize); + if (pIpForwardTable == nullptr) + { + return; + } + } + + if ((dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, 0)) == NO_ERROR) + { + for (int i = 0; i < (int)pIpForwardTable->dwNumEntries; i++) + { + visit(&pIpForwardTable->table[i]); + } + } + FREE(pIpForwardTable); +#undef MALLOC +#undef FREE + } + + std::optional + GetInterfaceIndex(huint32_t ip) + { + std::optional ret = std::nullopt; + ForEachWIN32Interface([&ret, n = ToNet(ip)](auto* iface) { + if (ret.has_value()) + return; + if (iface->dwForwardNextHop == n.n) + { + ret = iface->dwForwardIfIndex; + } + }); + return ret; + } + class Win32Interface final : public NetworkInterface { std::atomic m_Run; @@ -316,7 +367,7 @@ namespace llarp::vpn { if (ifaddr.fam == AF_INET6) { - const auto maybe = net::GetInterfaceIndex(ip); + const auto maybe = GetInterfaceIndex(ip); if (maybe.has_value()) { NetSH( @@ -443,42 +494,6 @@ namespace llarp::vpn } }; - template - void - ForEachWIN32Interface(Visit visit) - { -#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) -#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) - MIB_IPFORWARDTABLE* pIpForwardTable; - DWORD dwSize = 0; - DWORD dwRetVal = 0; - - pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(sizeof(MIB_IPFORWARDTABLE)); - if (pIpForwardTable == nullptr) - return; - - if (GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) - { - FREE(pIpForwardTable); - pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(dwSize); - if (pIpForwardTable == nullptr) - { - return; - } - } - - if ((dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, 0)) == NO_ERROR) - { - for (int i = 0; i < (int)pIpForwardTable->dwNumEntries; i++) - { - visit(&pIpForwardTable->table[i]); - } - } - FREE(pIpForwardTable); -#undef MALLOC -#undef FREE - } - class Win32RouteManager : public IRouteManager { void From e25ae7192f0e735ab6b39a23bf48746402b64698 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 27 Aug 2021 11:40:40 -0400 Subject: [PATCH 4/7] introduce add/del route via interface to route manager --- llarp/ev/vpn.hpp | 6 ++++++ llarp/vpn/linux.hpp | 44 ++++++++++++++++++++++++++++++++++++++++++-- llarp/vpn/win32.hpp | 31 +++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/llarp/ev/vpn.hpp b/llarp/ev/vpn.hpp index f911a310b..65f96c594 100644 --- a/llarp/ev/vpn.hpp +++ b/llarp/ev/vpn.hpp @@ -83,6 +83,12 @@ namespace llarp::vpn virtual void DelDefaultRouteViaInterface(std::string ifname) = 0; + virtual void + AddRouteViaInterface(std::string ifname, IPRange range) = 0; + + virtual void + DelRouteViaInterface(std::string ifname, IPRange range) = 0; + virtual std::vector GetGatewaysNotOnInterface(std::string ifname) = 0; diff --git a/llarp/vpn/linux.hpp b/llarp/vpn/linux.hpp index a50f14f04..c3f81e1e1 100644 --- a/llarp/vpn/linux.hpp +++ b/llarp/vpn/linux.hpp @@ -178,14 +178,14 @@ namespace llarp::vpn unsigned char bitlen; unsigned char data[sizeof(struct in6_addr)]; - _inet_addr(huint32_t addr, int bits = 32) + _inet_addr(huint32_t addr, size_t bits = 32) { family = AF_INET; bitlen = bits; htobe32buf(data, addr.h); } - _inet_addr(huint128_t addr, int bits = 128) + _inet_addr(huint128_t addr, size_t bits = 128) { family = AF_INET6; bitlen = bits; @@ -313,6 +313,34 @@ namespace llarp::vpn } } + void + RouteViaInterface(int cmd, int flags, std::string ifname, IPRange range) + { + int if_idx = if_nametoindex(ifname.c_str()); + if (range.IsV4()) + { + const auto maybe = GetInterfaceAddr(ifname); + if (not maybe) + throw std::runtime_error{"we dont have our own network interface?"}; + + const _inet_addr gateway{maybe->asIPv4()}; + + const _inet_addr addr{ + net::TruncateV6(range.addr), bits::count_bits(net::TruncateV6(range.netmask_bits))}; + + Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, if_idx); + } + else + { + const auto maybe = GetInterfaceIPv6Address(ifname); + if (not maybe) + throw std::runtime_error{"we dont have our own network interface?"}; + const _inet_addr gateway{*maybe, 128}; + const _inet_addr addr{range.addr, bits::count_bits(range.netmask_bits)}; + Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, if_idx); + } + } + void Route(int cmd, int flags, IPVariant_t ip, IPVariant_t gateway) { @@ -366,6 +394,18 @@ namespace llarp::vpn DefaultRouteViaInterface(ifname, RTM_DELROUTE, 0); } + void + AddRouteViaInterface(std::string ifname, IPRange range) override + { + RouteViaInterface(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, ifname, range); + } + + void + DelRouteViaInterface(std::string ifname, IPRange range) override + { + RouteViaInterface(RTM_DELROUTE, 0, ifname, range); + } + std::vector GetGatewaysNotOnInterface(std::string ifname) override { diff --git a/llarp/vpn/win32.hpp b/llarp/vpn/win32.hpp index f0ad23207..64f1d1fed 100644 --- a/llarp/vpn/win32.hpp +++ b/llarp/vpn/win32.hpp @@ -544,6 +544,25 @@ namespace llarp::vpn Execute(RouteCommand() + " " + cmd + " 128.0.0.0 MASK 128.0.0.0 " + ifname); } + void + RouteViaInterface(std::string ifname, IPRange range, std::string cmd) + { + if (range.IsV4()) + { + const huint32_t addr4 = net::TruncateV6(range.addr); + const huint32_t mask4 = net::TruncateV6(range.netmask_bits); + Execute( + RouteCommand() + " " + cmd + " " + addr4.ToString() + " MASK " + mask4.ToString() + " " + + ifname); + } + else + { + Execute( + RouteCommand() + " " + cmd + range.addr.ToString() + " MASK " + + range.netmask_bits.ToString() + " " + ifname); + } + } + public: void AddRoute(IPVariant_t ip, IPVariant_t gateway) override @@ -557,6 +576,18 @@ namespace llarp::vpn Route(ip, gateway, "DELETE"); } + void + AddRouteViaInterface(std::string ifname, IPRange range) override + { + RouteViaInterface(ifname, range, "ADD"); + } + + void + DelRouteViaInterface(std::string ifname, IPRange range) override + { + RouteViaInterface(ifname, range, "DELETE"); + } + void AddBlackhole() override{}; From c9b9ed91c2b0d553544342c899b1d8e7298a7abe Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 27 Aug 2021 11:42:24 -0400 Subject: [PATCH 5/7] make add/del blackhole default to empty implementation --- llarp/ev/vpn.hpp | 4 ++-- llarp/vpn/win32.hpp | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/llarp/ev/vpn.hpp b/llarp/ev/vpn.hpp index 65f96c594..c43ac35bd 100644 --- a/llarp/ev/vpn.hpp +++ b/llarp/ev/vpn.hpp @@ -93,10 +93,10 @@ namespace llarp::vpn GetGatewaysNotOnInterface(std::string ifname) = 0; virtual void - AddBlackhole() = 0; + AddBlackhole(){}; virtual void - DelBlackhole() = 0; + DelBlackhole(){}; }; /// a vpn platform diff --git a/llarp/vpn/win32.hpp b/llarp/vpn/win32.hpp index 64f1d1fed..cda174802 100644 --- a/llarp/vpn/win32.hpp +++ b/llarp/vpn/win32.hpp @@ -588,12 +588,6 @@ namespace llarp::vpn RouteViaInterface(ifname, range, "DELETE"); } - void - AddBlackhole() override{}; - - void - DelBlackhole() override{}; - std::vector GetGatewaysNotOnInterface(std::string ifname) override { From 07a58ffa6c2b370775f7c13b74818d7c59af938f Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 27 Aug 2021 11:55:57 -0400 Subject: [PATCH 6/7] use vpn::NetworkInterface for add/del route via interface instead of string --- llarp/ev/vpn.hpp | 4 ++-- llarp/vpn/linux.hpp | 8 ++++---- llarp/vpn/win32.hpp | 13 +++++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/llarp/ev/vpn.hpp b/llarp/ev/vpn.hpp index c43ac35bd..c3346fcdd 100644 --- a/llarp/ev/vpn.hpp +++ b/llarp/ev/vpn.hpp @@ -84,10 +84,10 @@ namespace llarp::vpn DelDefaultRouteViaInterface(std::string ifname) = 0; virtual void - AddRouteViaInterface(std::string ifname, IPRange range) = 0; + AddRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0; virtual void - DelRouteViaInterface(std::string ifname, IPRange range) = 0; + DelRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0; virtual std::vector GetGatewaysNotOnInterface(std::string ifname) = 0; diff --git a/llarp/vpn/linux.hpp b/llarp/vpn/linux.hpp index c3f81e1e1..5401ef10a 100644 --- a/llarp/vpn/linux.hpp +++ b/llarp/vpn/linux.hpp @@ -395,15 +395,15 @@ namespace llarp::vpn } void - AddRouteViaInterface(std::string ifname, IPRange range) override + AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - RouteViaInterface(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, ifname, range); + RouteViaInterface(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, vpn.IfName(), range); } void - DelRouteViaInterface(std::string ifname, IPRange range) override + DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - RouteViaInterface(RTM_DELROUTE, 0, ifname, range); + RouteViaInterface(RTM_DELROUTE, 0, vpn.IfName(), range); } std::vector diff --git a/llarp/vpn/win32.hpp b/llarp/vpn/win32.hpp index cda174802..06e2c8297 100644 --- a/llarp/vpn/win32.hpp +++ b/llarp/vpn/win32.hpp @@ -175,7 +175,7 @@ namespace llarp::vpn std::vector m_Threads; thread::Queue m_ReadQueue; - const InterfaceInfo m_Info; + InterfaceInfo m_Info; static std::wstring get_win_sys_path() @@ -274,6 +274,7 @@ namespace llarp::vpn IPADDR sock[3]{}; const nuint32_t addr = xhtonl(net::TruncateV6(ifaddr.range.addr)); ip = net::TruncateV6(ifaddr.range.addr); + m_Info.ifname = ip.ToString(); const nuint32_t mask = xhtonl(net::TruncateV6(ifaddr.range.netmask_bits)); LogInfo("address ", addr, " netmask ", mask); sock[0] = addr.n; @@ -409,7 +410,7 @@ namespace llarp::vpn std::string IfName() const override { - return ""; + return m_Info.ifname; } void @@ -577,15 +578,15 @@ namespace llarp::vpn } void - AddRouteViaInterface(std::string ifname, IPRange range) override + AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - RouteViaInterface(ifname, range, "ADD"); + RouteViaInterface(vpn.IfName(), range, "ADD"); } void - DelRouteViaInterface(std::string ifname, IPRange range) override + DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - RouteViaInterface(ifname, range, "DELETE"); + RouteViaInterface(vpn.IfName(), range, "DELETE"); } std::vector From 6251c13d46b539c0ee6d6747b89e10d9dd9590ea Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 27 Aug 2021 19:49:01 -0400 Subject: [PATCH 7/7] add NOP implementation of VPN route manager for android --- llarp/vpn/android.hpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/llarp/vpn/android.hpp b/llarp/vpn/android.hpp index b2c3ef5b3..c9792a009 100644 --- a/llarp/vpn/android.hpp +++ b/llarp/vpn/android.hpp @@ -60,9 +60,32 @@ namespace llarp::vpn } }; + class AndroidRouteManager : public IRouteManager + { + void AddRoute(IPVariant_t, IPVariant_t) override{}; + + void DelRoute(IPVariant_t, IPVariant_t) override{}; + + void AddDefaultRouteViaInterface(std::string) override{}; + + void DelDefaultRouteViaInterface(std::string) override{}; + + void + AddRouteViaInterface(NetworkInterface&, IPRange) override{}; + + void + DelRouteViaInterface(NetworkInterface&, IPRange) override{}; + + std::vector GetGatewaysNotOnInterface(std::string) override + { + return std::vector{}; + }; + }; + class AndroidPlatform : public Platform { const int fd; + AndroidRouteManager _routeManager{}; public: AndroidPlatform(llarp::Context* ctx) : fd(ctx->androidFD) @@ -73,6 +96,11 @@ namespace llarp::vpn { return std::make_shared(std::move(info), fd); } + IRouteManager& + RouteManager() override + { + return _routeManager; + } }; } // namespace llarp::vpn