mirror of https://github.com/oxen-io/lokinet
initial routing table refactor
* move routing table manipulation to vpn platform * add initial linux implementation of vpn platform route manipulation
This commit is contained in:
parent
37dde7da05
commit
0871862452
|
@ -4,6 +4,8 @@
|
|||
#include <llarp/net/ip_packet.hpp>
|
||||
#include <set>
|
||||
|
||||
#include <oxenmq/variant.h>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
struct Context;
|
||||
|
@ -59,6 +61,38 @@ namespace llarp::vpn
|
|||
WritePacket(net::IPPacket pkt) = 0;
|
||||
};
|
||||
|
||||
class IRouteManager
|
||||
{
|
||||
public:
|
||||
using IPVariant_t = std::variant<huint32_t, huint128_t>;
|
||||
|
||||
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<IPVariant_t>
|
||||
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<NetworkInterface>
|
||||
ObtainInterface(InterfaceInfo info) = 0;
|
||||
|
||||
/// get owned ip route manager for managing routing table
|
||||
virtual IRouteManager&
|
||||
RouteManager() = 0;
|
||||
};
|
||||
|
||||
/// create native vpn platform
|
||||
|
|
|
@ -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<huint32_t>
|
||||
|
@ -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<huint32_t>(&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
|
||||
|
|
|
@ -6,9 +6,19 @@
|
|||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include "common.hpp"
|
||||
#include <linux/if.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <exception>
|
||||
|
||||
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<IPVariant_t>
|
||||
GetGatewaysNotOnInterface(std::string ifname) override
|
||||
{
|
||||
std::vector<IPVariant_t> 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<char*>(&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<NetworkInterface>
|
||||
ObtainInterface(InterfaceInfo info) override
|
||||
{
|
||||
return std::make_shared<LinuxInterface>(std::move(info));
|
||||
};
|
||||
|
||||
IRouteManager&
|
||||
RouteManager() override
|
||||
{
|
||||
return _routeManager;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace llarp::vpn
|
||||
|
|
Loading…
Reference in New Issue