initial routing table refactor

* move routing table manipulation to vpn platform
* add initial linux implementation of vpn platform route manipulation
pull/1721/head
Jeff Becker 3 years ago
parent 37dde7da05
commit 0871862452
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -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…
Cancel
Save