Merge pull request #1538 from majestrate/ip6nat-exit-2021-02-16

ip6 nat exits
pull/1544/head
Jeff 3 years ago committed by GitHub
commit 6825e9177d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -68,12 +68,14 @@ main(int argc, char* argv[])
("token", "exit auth token to use", cxxopts::value<std::string>())
("auth", "exit auth token to use", cxxopts::value<std::string>())
("status", "print status and exit", cxxopts::value<bool>())
("range", "ip range to map", cxxopts::value<std::string>())
;
// clang-format on
lokimq::address rpcURL("tcp://127.0.0.1:1190");
std::string exitAddress;
std::string endpoint = "default";
std::optional<std::string> token;
std::string range = "::/0";
lokimq::LogLevel logLevel = lokimq::LogLevel::warn;
bool goUp = false;
bool goDown = false;
@ -118,6 +120,10 @@ main(int argc, char* argv[])
{
token = result["auth"].as<std::string>();
}
if (result.count("range") > 0)
{
range = result["range"].as<std::string>();
}
}
catch (const cxxopts::option_not_exists_exception& ex)
{
@ -216,12 +222,12 @@ main(int argc, char* argv[])
lmq,
connID,
"llarp.exit",
nlohmann::json{{"exit", exitAddress}, {"range", "0.0.0.0/0"}, {"token", *token}});
nlohmann::json{{"exit", exitAddress}, {"range", range}, {"token", *token}});
}
else
{
maybe_result = LMQ_Request(
lmq, connID, "llarp.exit", nlohmann::json{{"exit", exitAddress}, {"range", "0.0.0.0/0"}});
lmq, connID, "llarp.exit", nlohmann::json{{"exit", exitAddress}, {"range", range}});
}
if (not maybe_result.has_value())
@ -238,7 +244,7 @@ main(int argc, char* argv[])
}
if (goDown)
{
LMQ_Request(lmq, connID, "llarp.exit", nlohmann::json{{"range", "0.0.0.0/0"}, {"unmap", true}});
LMQ_Request(lmq, connID, "llarp.exit", nlohmann::json{{"range", range}, {"unmap", true}});
}
return 0;

@ -430,7 +430,7 @@ namespace llarp
const auto pos = arg.find(":");
if (pos == std::string::npos)
{
range.FromString("0.0.0.0/0");
range.FromString("::/0");
}
else if (not range.FromString(arg.substr(pos + 1)))
{

@ -182,8 +182,9 @@ namespace llarp
}
m_OurRange = *maybe;
}
m_OurIP = m_OurRange.addr;
m_UseV6 = not m_OurRange.IsV4();
m_UseV6 = false;
return Endpoint::Configure(conf, dnsConf);
}
@ -707,6 +708,11 @@ namespace llarp
vpn::InterfaceInfo info;
info.addrs.emplace(m_OurRange);
IPRange v6range = m_OurRange;
v6range.addr = net::ExpandV4Lan(net::TruncateV6(m_OurRange.addr));
LogInfo(Name(), " using v6 range: ", v6range);
info.addrs.emplace(v6range, AF_INET6);
info.ifname = m_IfName;
info.dnsaddr.FromString(m_LocalResolverAddr.toHost());
@ -736,6 +742,13 @@ namespace llarp
return false;
}
const auto maybe = GetInterfaceIPv6Address(m_IfName);
if (maybe.has_value())
{
m_OurIPv6 = *maybe;
LogInfo(Name(), " has ipv6 address ", m_OurIPv6);
}
netloop->add_ticker([&]() { Flush(); });
if (m_OnUp)
@ -826,7 +839,10 @@ namespace llarp
}
*/
if (m_state->m_ExitEnabled)
{
dst = net::ExpandV4(net::TruncateV6(dst));
}
auto itr = m_IPToAddr.find(dst);
if (itr == m_IPToAddr.end())
{
@ -843,8 +859,7 @@ namespace llarp
else
{
const auto addr = *exits.begin();
if (pkt.IsV4())
pkt.ZeroSourceAddress();
pkt.ZeroSourceAddress();
MarkAddressOutbound(addr);
EnsurePathToService(
addr,
@ -934,7 +949,10 @@ namespace llarp
if (pkt.IsV4())
dst = pkt.dst4to6Lan();
else if (pkt.IsV6())
{
dst = pkt.dstv6();
src = net::ExpandV4Lan(net::TruncateV6(src));
}
}
else
{
@ -948,11 +966,11 @@ namespace llarp
if (pkt.IsV4())
{
dst = m_OurIP;
src = pkt.src4to6();
src = pkt.src4to6Lan();
}
else if (pkt.IsV6())
{
dst = pkt.dstv6();
dst = m_OurIPv6;
src = pkt.srcv6();
}
// find what exit we think this should be for

@ -235,6 +235,9 @@ namespace llarp
std::unordered_map<huint128_t, llarp_time_t> m_IPActivity;
/// our ip address (host byte order)
huint128_t m_OurIP;
/// our network interface's ipv6 address
huint128_t m_OurIPv6;
/// next ip address to allocate (host byte order)
huint128_t m_NextIP;
/// highest ip address to allocate (host byte order)

@ -144,7 +144,7 @@ namespace llarp
}
else
{
if (const auto maybe = GetIFAddr(ifname, af))
if (const auto maybe = GetInterfaceAddr(ifname, af))
{
m_ourAddr = *maybe;
}

@ -19,7 +19,7 @@ namespace llarp::net
constexpr huint128_t
ExpandV4Lan(huint32_t x)
{
return huint128_t{uint128_t{0xfe80'0000'0000'0000UL, 0UL}} | huint128_t{x.h};
return huint128_t{uint128_t{0xfd00'0000'0000'0000UL, 0UL}} | huint128_t{x.h};
}
constexpr huint32_t

@ -161,6 +161,14 @@ namespace llarp
return ip;
}
huint128_t
IpAddress::toIP6() const
{
huint128_t ip;
ip.FromString(m_ipAddress);
return ip;
}
bool
IpAddress::operator<(const IpAddress& other) const
{

@ -126,6 +126,9 @@ namespace llarp
huint32_t
toIP() const;
huint128_t
toIP6() const;
// TODO: other utility functions left over from Addr which may be useful
// IsBogon() const;
// isPrivate() const;

@ -546,16 +546,29 @@ namespace llarp
}
std::optional<IpAddress>
GetIFAddr(const std::string& ifname, int af)
GetInterfaceAddr(const std::string& ifname, int af)
{
sockaddr_storage s;
sockaddr* sptr = (sockaddr*)&s;
sptr->sa_family = af;
if (!llarp_getifaddr(ifname.c_str(), af, sptr))
return std::nullopt;
llarp::SockAddr saddr = SockAddr(*sptr);
llarp::SockAddr saddr = SockAddr{*sptr};
return llarp::IpAddress(saddr);
}
std::optional<huint128_t>
GetInterfaceIPv6Address(std::string ifname)
{
sockaddr_storage s;
sockaddr* sptr = (sockaddr*)&s;
sptr->sa_family = AF_INET6;
if (!llarp_getifaddr(ifname.c_str(), AF_INET6, sptr))
return std::nullopt;
llarp::SockAddr addr{*sptr};
return addr.asIPv6();
}
bool
AllInterfaces(int af, IpAddress& result)
{

@ -75,7 +75,19 @@ namespace llarp
/// get network interface address for network interface with ifname
std::optional<IpAddress>
GetIFAddr(const std::string& ifname, int af = AF_INET);
GetInterfaceAddr(const std::string& ifname, int af = AF_INET);
/// get an interface's ip6 address
std::optional<huint128_t>
GetInterfaceIPv6Address(std::string ifname);
#ifdef _WIN32
namespace net
{
std::optional<int>
GetInterfaceIndex(huint32_t ip);
}
#endif
} // namespace llarp

@ -24,9 +24,11 @@
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <strsafe.h>
#include <cstring>
#include <locale>
#include <codecvt>
#include <net/net_int.hpp>
#include <net/ip.hpp>
#endif
#include <sstream>
@ -151,23 +153,13 @@ namespace llarp::net
nl_request.r.rtm_family = dst->family;
nl_request.r.rtm_dst_len = dst->bitlen;
/* Select scope, for simplicity we supports here only IPv6 and IPv4 */
if (nl_request.r.rtm_family == AF_INET6)
{
nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE;
}
else
{
nl_request.r.rtm_scope = RT_SCOPE_LINK;
}
nl_request.r.rtm_scope = 0;
/* Set gateway */
if (gw->bitlen != 0)
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_scope = 0;
nl_request.r.rtm_family = gw->family;
if (mode == GatewayMode::eFirstHop)
{
@ -178,8 +170,25 @@ namespace llarp::net
}
if (mode == GatewayMode::eUpperDefault)
{
rtattr_add(
&nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, &dst->data, sizeof(uint32_t));
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);
@ -191,7 +200,7 @@ namespace llarp::net
if (strchr(addr, ':'))
{
res->family = AF_INET6;
res->bitlen = 128;
res->bitlen = bitlen;
}
else
{
@ -230,6 +239,16 @@ namespace llarp::net
return converter.to_bytes(wcmd);
}
std::string
NetshCommand()
{
std::wstring wcmd = get_win_sys_path() + L"\\netsh.exe";
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
return converter.to_bytes(wcmd);
}
template <typename Visit>
void
ForEachWIN32Interface(Visit visit)
@ -265,6 +284,22 @@ namespace llarp::net
#undef MALLOC
#undef FREE
}
std::optional<int>
GetInterfaceIndex(huint32_t ip)
{
std::optional<int> 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
@ -335,7 +370,7 @@ namespace llarp::net
int if_idx = if_nametoindex(ifname.c_str());
_inet_addr to_addr{};
_inet_addr gw_addr{};
const auto maybe = GetIFAddr(ifname);
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;
@ -345,16 +380,46 @@ namespace llarp::net
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
@ -370,7 +435,7 @@ namespace llarp::net
int if_idx = if_nametoindex(ifname.c_str());
_inet_addr to_addr{};
_inet_addr gw_addr{};
const auto maybe = GetIFAddr(ifname);
const auto maybe = GetInterfaceAddr(ifname);
if (not maybe.has_value())
throw std::runtime_error("we dont have our own net interface?");
@ -381,8 +446,32 @@ namespace llarp::net
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);
@ -391,6 +480,12 @@ namespace llarp::net
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
@ -423,9 +518,7 @@ namespace llarp::net
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::array<char, 128> interface_str{};
StringCchCopy(interface_str.data(), interface_str.size(), inet_ntoa(interface_addr));
std::string interface_name{interface_str.data()};
std::string interface_name{inet_ntoa(interface_addr)};
if ((!gateway.S_un.S_addr) and interface_name != ifname)
{
llarp::LogTrace(

@ -1,4 +1,5 @@
#include <net/sock_addr.hpp>
#include <net/ip.hpp>
#include <net/net_bits.hpp>
#include <util/str.hpp>
#include <util/logging/logger.hpp>
@ -187,6 +188,12 @@ namespace llarp
sizeof(m_addr.sin6_addr.s6_addr)));
}
huint128_t
SockAddr::asIPv6() const
{
return net::In6ToHUInt(m_addr.sin6_addr);
}
void
SockAddr::fromString(std::string_view str)
{
@ -204,7 +211,12 @@ namespace llarp
// TODO: having ":port" at the end makes this ambiguous with IPv6
// come up with a strategy for implementing
if (splits.size() > 2)
throw std::runtime_error("IPv6 not yet supported");
{
std::string data{str};
if (inet_pton(AF_INET6, data.c_str(), m_addr.sin6_addr.s6_addr) == -1)
throw std::runtime_error{"invalid ip6 address: " + data};
return;
}
// split() shouldn't return an empty list if str is empty (checked above)
assert(splits.size() > 0);

@ -16,6 +16,7 @@ inet_pton(int af, const char* src, void* dst);
#include <string_view>
#include <string>
#include <net/net_int.hpp>
namespace llarp
{
@ -80,6 +81,9 @@ namespace llarp
uint16_t
getPort() const;
huint128_t
asIPv6() const;
private:
bool m_empty = true;
sockaddr_in6 m_addr;

@ -131,8 +131,7 @@ namespace llarp
DisableAllRoutes();
EnableAllRoutes();
const auto ep = m_Router->hiddenServiceContext().GetDefault();
net::AddDefaultRouteViaInterface(ep->GetIfName());
Up();
}
}
@ -156,4 +155,27 @@ namespace llarp
DisableAllRoutes();
}
void
RoutePoker::Up()
{
// explicit route pokes for first hops
m_Router->ForEachPeer(
[&](auto session, auto) mutable { AddRoute(session->GetRemoteEndpoint().toIP()); }, false);
// add default route
const auto ep = m_Router->hiddenServiceContext().GetDefault();
net::AddDefaultRouteViaInterface(ep->GetIfName());
}
void
RoutePoker::Down()
{
// unpoke routes for first hops
m_Router->ForEachPeer(
[&](auto session, auto) mutable { DelRoute(session->GetRemoteEndpoint().toIP()); }, false);
// remove default route
const auto ep = m_Router->hiddenServiceContext().GetDefault();
net::DelDefaultRouteViaInterface(ep->GetIfName());
}
} // namespace llarp

@ -2,6 +2,7 @@
#include <unordered_map>
#include <string>
#include <memory>
#include <net/net_int.hpp>
namespace llarp
@ -34,6 +35,14 @@ namespace llarp
void
Disable();
/// explicitly put routes up
void
Up();
/// explicitly put routes down
void
Down();
private:
void
DeleteAllRoutes();

@ -156,7 +156,6 @@ namespace llarp::rpc
{
map = false;
}
const auto range_itr = obj.find("range");
if (range_itr == obj.end())
{
@ -204,13 +203,7 @@ namespace llarp::rpc
reply(CreateJSONError("could not find exit"));
return;
}
r->ForEachPeer(
[r](auto session, auto) mutable {
const auto ip = session->GetRemoteEndpoint().toIP();
r->routePoker().AddRoute(ip);
},
false);
net::AddDefaultRouteViaInterface(ep->GetIfName());
r->routePoker().Up();
reply(CreateJSONResponse("OK"));
},
5s);
@ -250,14 +243,7 @@ namespace llarp::rpc
}
else if (not map)
{
net::DelDefaultRouteViaInterface(ep->GetIfName());
r->ForEachPeer(
[r](auto session, auto) mutable {
const auto ip = session->GetRemoteEndpoint().toIP();
r->routePoker().DelRoute(ip);
},
false);
r->routePoker().Down();
ep->UnmapExitRange(range);
}
reply(CreateJSONResponse("OK"));

@ -153,7 +153,8 @@ namespace llarp::vpn
static void
NetSH(std::string commands)
{
commands = NetSHCommand() + " " + commands;
commands = NetSHCommand() + " interface IPv6 " + commands;
LogInfo(commands);
::system(commands.c_str());
}
@ -203,6 +204,7 @@ namespace llarp::vpn
}
LogInfo("setting addresses");
huint32_t ip{};
// set ipv4 addresses
for (const auto& ifaddr : m_Info.addrs)
{
@ -210,6 +212,7 @@ namespace llarp::vpn
{
IPADDR sock[3]{};
const nuint32_t addr = xhtonl(net::TruncateV6(ifaddr.range.addr));
ip = net::TruncateV6(ifaddr.range.addr);
const nuint32_t mask = xhtonl(net::TruncateV6(ifaddr.range.netmask_bits));
LogInfo("address ", addr, " netmask ", mask);
sock[0] = addr.n;
@ -299,19 +302,18 @@ namespace llarp::vpn
}
}
// set ipv6 addresses
/*
for (const auto& ifaddr : m_Info.addrs)
{
if (ifaddr.fam == AF_INET6)
{
IPRange range = ifaddr.range;
range.netmask_bits = netmask_ipv6_bits(128);
NetSH(
"interface ipv6 set address " + std::to_string(ifindex) + " " + range.ToString()
+ " store=active");
const auto maybe = net::GetInterfaceIndex(ip);
if (maybe.has_value())
{
NetSH(
"add address interface=" + std::to_string(*maybe) + " " + ifaddr.range.ToString());
}
}
}
*/
}
~Win32Interface()

Loading…
Cancel
Save