You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lokinet/llarp/handlers/tun.cpp

1586 lines
43 KiB
C++

#include <algorithm>
#include <iterator>
#include <variant>
#include "tun.hpp"
#include <sys/types.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <netdb.h>
#endif
#include <llarp/dns/dns.hpp>
#include <llarp/ev/ev.hpp>
QUIC lokinet integration refactor Refactors how quic packets get handled: the actual tunnels now live in tunnel.hpp's TunnelManager which holds and manages all the quic<->tcp tunnelling. service::Endpoint now holds a TunnelManager rather than a quic::Server. We only need one quic server, but we need a separate quic client instance per outgoing quic tunnel, and TunnelManager handles all that glue now. Adds QUIC packet handling to get to the right tunnel code. This required multiplexing incoming quic packets, as follows: Adds a very small quic tunnel packet header of 4 bytes: [1, SPORT, ECN] for client->server packets, where SPORT is our source "port" (really: just a uint16_t unique quic instance identifier) or [2, DPORT, ECN] for server->client packets where the DPORT is the SPORT from above. (This also reworks ECN bits to get properly carried over lokinet.) We don't need a destination/source port for the server-side because there is only ever one quic server (and we know we're going to it when the first byte of the header is 1). Removes the config option for quic exposing ports; a full lokinet will simply accept anything incoming on quic and tunnel it to the requested port on the the local endpoint IP (this handler will come in a following commit). Replace ConvoTags with full addresses: we need to carry the port, as well, which the ConvoTag can't give us, so change those to more general SockAddrs from which we can extract both the ConvoTag *and* the port. Add a pending connection queue along with new quic-side handlers to call when a stream becomes available (TunnelManager uses this to wire up pending incoming conns with quic streams as streams open up). Completely get rid of tunnel_server/tunnel_client.cpp code; it is now moved to tunnel.hpp. Add listen()/forget() methods in TunnelManager for setting up quic listening sockets (for liblokinet usage). Add open()/close() methods in TunnelManager for spinning up new quic clients for outgoing quic connections.
4 years ago
#include <llarp/net/net.hpp>
#include <llarp/router/router.hpp>
#include <llarp/router/route_poker.hpp>
#include <llarp/service/context.hpp>
#include <llarp/service/outbound_context.hpp>
#include <llarp/service/endpoint_state.hpp>
#include <llarp/service/outbound_context.hpp>
#include <llarp/service/name.hpp>
QUIC lokinet integration refactor Refactors how quic packets get handled: the actual tunnels now live in tunnel.hpp's TunnelManager which holds and manages all the quic<->tcp tunnelling. service::Endpoint now holds a TunnelManager rather than a quic::Server. We only need one quic server, but we need a separate quic client instance per outgoing quic tunnel, and TunnelManager handles all that glue now. Adds QUIC packet handling to get to the right tunnel code. This required multiplexing incoming quic packets, as follows: Adds a very small quic tunnel packet header of 4 bytes: [1, SPORT, ECN] for client->server packets, where SPORT is our source "port" (really: just a uint16_t unique quic instance identifier) or [2, DPORT, ECN] for server->client packets where the DPORT is the SPORT from above. (This also reworks ECN bits to get properly carried over lokinet.) We don't need a destination/source port for the server-side because there is only ever one quic server (and we know we're going to it when the first byte of the header is 1). Removes the config option for quic exposing ports; a full lokinet will simply accept anything incoming on quic and tunnel it to the requested port on the the local endpoint IP (this handler will come in a following commit). Replace ConvoTags with full addresses: we need to carry the port, as well, which the ConvoTag can't give us, so change those to more general SockAddrs from which we can extract both the ConvoTag *and* the port. Add a pending connection queue along with new quic-side handlers to call when a stream becomes available (TunnelManager uses this to wire up pending incoming conns with quic streams as streams open up). Completely get rid of tunnel_server/tunnel_client.cpp code; it is now moved to tunnel.hpp. Add listen()/forget() methods in TunnelManager for setting up quic listening sockets (for liblokinet usage). Add open()/close() methods in TunnelManager for spinning up new quic clients for outgoing quic connections.
4 years ago
#include <llarp/service/protocol_type.hpp>
#include <llarp/util/meta/memfn.hpp>
#include <llarp/nodedb.hpp>
QUIC lokinet integration refactor Refactors how quic packets get handled: the actual tunnels now live in tunnel.hpp's TunnelManager which holds and manages all the quic<->tcp tunnelling. service::Endpoint now holds a TunnelManager rather than a quic::Server. We only need one quic server, but we need a separate quic client instance per outgoing quic tunnel, and TunnelManager handles all that glue now. Adds QUIC packet handling to get to the right tunnel code. This required multiplexing incoming quic packets, as follows: Adds a very small quic tunnel packet header of 4 bytes: [1, SPORT, ECN] for client->server packets, where SPORT is our source "port" (really: just a uint16_t unique quic instance identifier) or [2, DPORT, ECN] for server->client packets where the DPORT is the SPORT from above. (This also reworks ECN bits to get properly carried over lokinet.) We don't need a destination/source port for the server-side because there is only ever one quic server (and we know we're going to it when the first byte of the header is 1). Removes the config option for quic exposing ports; a full lokinet will simply accept anything incoming on quic and tunnel it to the requested port on the the local endpoint IP (this handler will come in a following commit). Replace ConvoTags with full addresses: we need to carry the port, as well, which the ConvoTag can't give us, so change those to more general SockAddrs from which we can extract both the ConvoTag *and* the port. Add a pending connection queue along with new quic-side handlers to call when a stream becomes available (TunnelManager uses this to wire up pending incoming conns with quic streams as streams open up). Completely get rid of tunnel_server/tunnel_client.cpp code; it is now moved to tunnel.hpp. Add listen()/forget() methods in TunnelManager for setting up quic listening sockets (for liblokinet usage). Add open()/close() methods in TunnelManager for spinning up new quic clients for outgoing quic connections.
4 years ago
#include <llarp/quic/tunnel.hpp>
#include <llarp/rpc/endpoint_rpc.hpp>
#include <llarp/util/str.hpp>
#include <llarp/util/logging/buffer.hpp>
#include <llarp/dns/srv_data.hpp>
#include <llarp/constants/net.hpp>
#include <llarp/constants/platform.hpp>
3 years ago
#include <oxenc/bt.h>
1 year ago
namespace llarp::handlers
{
1 year ago
static auto logcat = log::Cat("tun");
bool
TunEndpoint::MaybeHookDNS(
std::shared_ptr<dns::PacketSource_Base> source,
const dns::Message& query,
const SockAddr& to,
const SockAddr& from)
{
1 year ago
if (not ShouldHookDNSMessage(query))
return false;
1 year ago
auto job = std::make_shared<dns::QueryJob>(source, query, to, from);
if (HandleHookedDNSMessage(query, [job](auto msg) { job->SendReply(msg.ToBuffer()); }))
router()->TriggerPump();
else
job->Cancel();
return true;
}
/// Intercepts DNS IP packets on platforms where binding to a low port isn't viable.
/// (windows/macos/ios/android ... aka everything that is not linux... funny that)
class DnsInterceptor : public dns::PacketSource_Base
{
std::function<void(net::IPPacket)> m_Reply;
net::ipaddr_t m_OurIP;
llarp::DnsConfig m_Config;
1 year ago
public:
explicit DnsInterceptor(
std::function<void(net::IPPacket)> reply, net::ipaddr_t our_ip, llarp::DnsConfig conf)
: m_Reply{std::move(reply)}, m_OurIP{std::move(our_ip)}, m_Config{std::move(conf)}
{}
1 year ago
~DnsInterceptor() override = default;
1 year ago
void
SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override
{
auto pkt = net::IPPacket::make_udp(from, to, std::move(buf));
1 year ago
if (pkt.empty())
return;
m_Reply(std::move(pkt));
}
1 year ago
void
Stop() override{};
1 year ago
std::optional<SockAddr>
BoundOn() const override
{
return std::nullopt;
}
1 year ago
bool
WouldLoop(const SockAddr& to, const SockAddr& from) const override
{
if constexpr (platform::is_apple)
{
1 year ago
// DNS on Apple is a bit weird because in order for the NetworkExtension itself to send
// data through the tunnel we have to proxy DNS requests through Apple APIs (and so our
// actual upstream DNS won't be set in our resolvers, which is why the vanilla WouldLoop
// won't work for us). However when active the mac also only queries the main tunnel IP
// for DNS, so we consider anything else to be upstream-bound DNS to let it through the
// tunnel.
return to.getIP() != m_OurIP;
}
1 year ago
else if (auto maybe_addr = m_Config.m_QueryBind)
{
1 year ago
const auto& addr = *maybe_addr;
// omit traffic to and from our dns socket
return addr == to or addr == from;
}
1 year ago
return false;
}
};
1 year ago
class TunDNS : public dns::Server
{
TunEndpoint* const m_Endpoint;
1 year ago
std::optional<SockAddr> m_QueryBind;
net::ipaddr_t m_OurIP;
1 year ago
public:
std::shared_ptr<dns::PacketSource_Base> PacketSource;
1 year ago
virtual ~TunDNS() = default;
1 year ago
explicit TunDNS(TunEndpoint* ep, const llarp::DnsConfig& conf)
: dns::Server{ep->router()->loop(), conf, 0}
, m_Endpoint{ep}
1 year ago
, m_QueryBind{conf.m_QueryBind}
, m_OurIP{ToNet(ep->GetIfAddr())}
{}
1 year ago
std::shared_ptr<dns::PacketSource_Base>
MakePacketSourceOn(const SockAddr&, const llarp::DnsConfig& conf) override
{
1 year ago
auto ptr = std::make_shared<DnsInterceptor>(
[ep = m_Endpoint](auto pkt) {
ep->HandleWriteIPPacket(pkt.ConstBuffer(), pkt.srcv6(), pkt.dstv6(), 0);
},
m_OurIP,
conf);
PacketSource = ptr;
return ptr;
}
1 year ago
};
1 year ago
TunEndpoint::TunEndpoint(Router* r, service::Context* parent) : service::Endpoint{r, parent}
{
m_PacketRouter = std::make_shared<vpn::PacketRouter>(
[this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); });
}
void
TunEndpoint::SetupDNS()
{
const auto& info = GetVPNInterface()->Info();
if (m_DnsConfig.m_raw_dns)
{
1 year ago
auto dns = std::make_shared<TunDNS>(this, m_DnsConfig);
m_DNS = dns;
m_PacketRouter->AddUDPHandler(huint16_t{53}, [this, dns](net::IPPacket pkt) {
auto dns_pkt_src = dns->PacketSource;
if (const auto& reply = pkt.reply)
dns_pkt_src = std::make_shared<dns::PacketSource_Wrapper>(dns_pkt_src, reply);
if (dns->MaybeHandlePacket(
std::move(dns_pkt_src), pkt.dst(), pkt.src(), *pkt.L4OwnedBuffer()))
return;
1 year ago
HandleGotUserPacket(std::move(pkt));
});
}
else
m_DNS = std::make_shared<dns::Server>(Loop(), m_DnsConfig, info.index);
m_DNS->AddResolver(weak_from_this());
m_DNS->Start();
1 year ago
if (m_DnsConfig.m_raw_dns)
{
if (auto vpn = router()->vpn_platform())
{
1 year ago
// get the first local address we know of
std::optional<SockAddr> localaddr;
for (auto res : m_DNS->GetAllResolvers())
{
1 year ago
if (auto ptr = res.lock())
{
1 year ago
localaddr = ptr->GetLocalAddr();
if (localaddr)
break;
}
}
if (platform::is_windows)
{
auto dns_io = vpn->create_packet_io(0, localaddr);
router()->loop()->add_ticker([r = router(), dns_io, handler = m_PacketRouter]() {
net::IPPacket pkt = dns_io->ReadNextPacket();
while (not pkt.empty())
{
1 year ago
handler->HandleIPPacket(std::move(pkt));
pkt = dns_io->ReadNextPacket();
}
1 year ago
});
m_RawDNS = dns_io;
}
}
1 year ago
if (m_RawDNS)
m_RawDNS->Start();
}
}
util::StatusObject
TunEndpoint::ExtractStatus() const
{
auto obj = service::Endpoint::ExtractStatus();
obj["ifaddr"] = m_OurRange.ToString();
obj["ifname"] = m_IfName;
std::vector<std::string> upstreamRes;
for (const auto& ent : m_DnsConfig.m_upstreamDNS)
upstreamRes.emplace_back(ent.ToString());
obj["ustreamResolvers"] = upstreamRes;
std::vector<std::string> localRes;
for (const auto& ent : m_DnsConfig.m_bind)
localRes.emplace_back(ent.ToString());
obj["localResolvers"] = localRes;
// for backwards compat
if (not m_DnsConfig.m_bind.empty())
obj["localResolver"] = localRes[0];
util::StatusObject ips{};
for (const auto& item : m_IPActivity)
{
util::StatusObject ipObj{{"lastActive", to_json(item.second)}};
std::string remoteStr;
AlignedBuffer<32> addr = m_IPToAddr.at(item.first);
if (m_SNodes.at(addr))
remoteStr = RouterID(addr.as_array()).ToString();
else
remoteStr = service::Address(addr.as_array()).ToString();
ipObj["remote"] = remoteStr;
std::string ipaddr = item.first.ToString();
ips[ipaddr] = ipObj;
}
1 year ago
obj["addrs"] = ips;
obj["ourIP"] = m_OurIP.ToString();
obj["nextIP"] = m_NextIP.ToString();
obj["maxIP"] = m_MaxIP.ToString();
return obj;
}
void
TunEndpoint::Thaw()
{
if (m_DNS)
m_DNS->Reset();
}
6 years ago
1 year ago
void
TunEndpoint::ReconfigureDNS(std::vector<SockAddr> servers)
{
if (m_DNS)
{
1 year ago
for (auto weak : m_DNS->GetAllResolvers())
{
1 year ago
if (auto ptr = weak.lock())
ptr->ResetResolver(servers);
}
}
1 year ago
}
1 year ago
bool
TunEndpoint::Configure(const NetworkConfig& conf, const DnsConfig& dnsConf)
{
if (conf.m_reachable)
{
1 year ago
_publish_introset = true;
log::info(link_cat, "TunEndpoint setting to be reachable by default");
}
1 year ago
else
{
1 year ago
_publish_introset = false;
log::info(link_cat, "TunEndpoint setting to be not reachable by default");
}
1 year ago
if (conf.m_AuthType == service::AuthType::eAuthTypeFile)
6 years ago
{
_auth_policy = service::MakeFileAuthPolicy(router(), conf.m_AuthFiles, conf.m_AuthFileType);
1 year ago
}
else if (conf.m_AuthType != service::AuthType::eAuthTypeNone)
{
std::string url, method;
if (conf.m_AuthUrl.has_value() and conf.m_AuthMethod.has_value())
{
1 year ago
url = *conf.m_AuthUrl;
method = *conf.m_AuthMethod;
}
1 year ago
auto auth = std::make_shared<rpc::EndpointAuthRPC>(
url,
method,
conf.m_AuthWhitelist,
conf.m_AuthStaticTokens,
router()->lmq(),
shared_from_this());
auth->Start();
_auth_policy = std::move(auth);
}
1 year ago
m_DnsConfig = dnsConf;
m_TrafficPolicy = conf.m_TrafficPolicy;
m_OwnedRanges = conf.m_OwnedRanges;
4 years ago
1 year ago
m_BaseV6Address = conf.m_baseV6Address;
1 year ago
if (conf.m_PathAlignmentTimeout)
{
m_PathAlignmentTimeout = *conf.m_PathAlignmentTimeout;
}
else
m_PathAlignmentTimeout = service::Endpoint::PathAlignmentTimeout();
1 year ago
for (const auto& item : conf.m_mapAddrs)
{
if (not MapAddress(item.second, item.first, false))
return false;
}
1 year ago
m_IfName = conf.m_ifname;
if (m_IfName.empty())
{
const auto maybe = router()->net().FindFreeTun();
1 year ago
if (not maybe.has_value())
throw std::runtime_error("cannot find free interface name");
m_IfName = *maybe;
}
1 year ago
m_OurRange = conf.m_ifaddr;
if (!m_OurRange.addr.h)
{
const auto maybe = router()->net().FindFreeRange();
1 year ago
if (not maybe.has_value())
{
1 year ago
throw std::runtime_error("cannot find free address range");
6 years ago
}
1 year ago
m_OurRange = *maybe;
}
4 years ago
1 year ago
m_OurIP = m_OurRange.addr;
m_UseV6 = false;
1 year ago
m_PersistAddrMapFile = conf.m_AddrMapPersistFile;
if (m_PersistAddrMapFile)
{
const auto& file = *m_PersistAddrMapFile;
if (fs::exists(file))
{
1 year ago
bool shouldLoadFile = true;
{
1 year ago
constexpr auto LastModifiedWindow = 1min;
const auto lastmodified = fs::last_write_time(file);
const auto now = decltype(lastmodified)::clock::now();
if (now < lastmodified or now - lastmodified > LastModifiedWindow)
{
1 year ago
shouldLoadFile = false;
}
1 year ago
}
std::vector<char> data;
if (auto maybe = util::OpenFileStream<fs::ifstream>(file, std::ios_base::binary);
maybe and shouldLoadFile)
{
LogInfo(Name(), " loading address map file from ", file);
maybe->seekg(0, std::ios_base::end);
const size_t len = maybe->tellg();
maybe->seekg(0, std::ios_base::beg);
data.resize(len);
LogInfo(Name(), " reading ", len, " bytes");
maybe->read(data.data(), data.size());
}
else
{
if (shouldLoadFile)
{
1 year ago
LogInfo(Name(), " address map file ", file, " does not exist, so we won't load it");
}
else
1 year ago
LogInfo(Name(), " address map file ", file, " not loaded because it's stale");
}
if (not data.empty())
{
std::string_view bdata{data.data(), data.size()};
LogDebug(Name(), " parsing address map data: ", bdata);
const auto parsed = oxenc::bt_deserialize<oxenc::bt_dict>(bdata);
for (const auto& [key, value] : parsed)
{
1 year ago
huint128_t ip{};
if (not ip.FromString(key))
{
1 year ago
LogWarn(Name(), " malformed IP in addr map data: ", key);
continue;
}
1 year ago
if (m_OurIP == ip)
continue;
if (not m_OurRange.Contains(ip))
{
1 year ago
LogWarn(Name(), " out of range IP in addr map data: ", ip);
continue;
}
EndpointBase::AddressVariant_t addr;
1 year ago
if (const auto* str = std::get_if<std::string>(&value))
{
if (auto maybe = service::parse_address(*str))
{
1 year ago
addr = *maybe;
}
else
{
1 year ago
LogWarn(Name(), " invalid address in addr map: ", *str);
continue;
}
}
1 year ago
else
{
LogWarn(Name(), " invalid first entry in addr map, not a string");
continue;
}
if (const auto* loki = std::get_if<service::Address>(&addr))
{
m_IPToAddr.emplace(ip, loki->data());
m_AddrToIP.emplace(loki->data(), ip);
m_SNodes[*loki] = false;
LogInfo(Name(), " remapped ", ip, " to ", *loki);
}
if (const auto* snode = std::get_if<RouterID>(&addr))
{
m_IPToAddr.emplace(ip, snode->data());
m_AddrToIP.emplace(snode->data(), ip);
m_SNodes[*snode] = true;
LogInfo(Name(), " remapped ", ip, " to ", *snode);
}
if (m_NextIP < ip)
m_NextIP = ip;
// make sure we dont unmap this guy
MarkIPActive(ip);
}
}
}
1 year ago
else
{
1 year ago
LogInfo(Name(), " skipping loading addr map at ", file, " as it does not currently exist");
}
6 years ago
}
1 year ago
if (auto* quic = GetQUICTunnel())
6 years ago
{
1 year ago
quic->listen([this](std::string_view, uint16_t port) {
return llarp::SockAddr{net::TruncateV6(GetIfAddr()), huint16_t{port}};
});
6 years ago
}
1 year ago
return Endpoint::Configure(conf, dnsConf);
}
6 years ago
1 year ago
bool
TunEndpoint::HasLocalIP(const huint128_t& ip) const
{
return m_IPToAddr.find(ip) != m_IPToAddr.end();
}
void
TunEndpoint::Pump(llarp_time_t now)
{
// flush network to user
while (not m_NetworkToUserPktQueue.empty())
{
1 year ago
m_NetIf->WritePacket(m_NetworkToUserPktQueue.top().pkt);
m_NetworkToUserPktQueue.pop();
}
service::Endpoint::Pump(now);
}
static bool
is_random_snode(const dns::Message& msg)
{
return msg.questions[0].IsName("random.snode");
}
static bool
is_localhost_loki(const dns::Message& msg)
{
return msg.questions[0].IsLocalhost();
}
static dns::Message&
clear_dns_message(dns::Message& msg)
{
msg.authorities.resize(0);
msg.additional.resize(0);
msg.answers.resize(0);
msg.hdr_fields &= ~dns::flags_RCODENameError;
return msg;
}
std::optional<std::variant<service::Address, RouterID>>
TunEndpoint::ObtainAddrForIP(huint128_t ip) const
{
auto itr = m_IPToAddr.find(ip);
if (itr == m_IPToAddr.end())
return std::nullopt;
if (m_SNodes.at(itr->second))
return RouterID{itr->second.as_array()};
else
return service::Address{itr->second.as_array()};
}
bool
TunEndpoint::HandleHookedDNSMessage(dns::Message msg, std::function<void(dns::Message)> reply)
{
auto ReplyToSNodeDNSWhenReady = [this, reply](RouterID snode, auto msg, bool isV6) -> bool {
return EnsurePathToSNode(
snode,
[this, snode, msg, reply, isV6](
const RouterID&, exit::BaseSession_ptr s, [[maybe_unused]] service::ConvoTag tag) {
SendDNSReply(snode, s, msg, reply, isV6);
});
};
auto ReplyToLokiDNSWhenReady = [this, reply, timeout = PathAlignmentTimeout()](
service::Address addr, auto msg, bool isV6) -> bool {
using service::Address;
using service::OutboundContext;
if (HasInboundConvo(addr))
{
1 year ago
// if we have an inbound convo to this address don't mark as outbound so we don't have a
// state race this codepath is hit when an application verifies that reverse and forward
// dns records match for an inbound session
SendDNSReply(addr, this, msg, reply, isV6);
return true;
}
1 year ago
MarkAddressOutbound(addr);
return EnsurePathToService(
addr,
[this, addr, msg, reply, isV6](const Address&, OutboundContext* ctx) {
SendDNSReply(addr, ctx, msg, reply, isV6);
},
timeout);
};
1 year ago
auto ReplyToDNSWhenReady = [ReplyToLokiDNSWhenReady, ReplyToSNodeDNSWhenReady](
std::string name, auto msg, bool isV6) {
if (auto saddr = service::Address(); saddr.FromString(name))
ReplyToLokiDNSWhenReady(saddr, msg, isV6);
if (auto rid = RouterID(); rid.FromString(name))
ReplyToSNodeDNSWhenReady(rid, msg, isV6);
1 year ago
};
1 year ago
auto ReplyToLokiSRVWhenReady = [this, reply, timeout = PathAlignmentTimeout()](
service::Address addr, auto msg) -> bool {
using service::Address;
using service::OutboundContext;
// TODO: how do we handle SRV record lookups for inbound sessions?
MarkAddressOutbound(addr);
return EnsurePathToService(
addr,
[msg, addr, reply](const Address&, OutboundContext* ctx) {
if (ctx == nullptr)
return;
const auto& introset = ctx->GetCurrentIntroSet();
msg->AddSRVReply(introset.GetMatchingSRVRecords(addr.subdomain));
reply(*msg);
},
timeout);
};
1 year ago
if (msg.answers.size() > 0)
{
1 year ago
const auto& answer = msg.answers[0];
if (answer.HasCNameForTLD(".snode"))
{
llarp_buffer_t buf(answer.rData);
auto qname = dns::DecodeName(&buf, true);
if (not qname)
return false;
RouterID addr;
if (not addr.FromString(*qname))
return false;
auto replyMsg = std::make_shared<dns::Message>(clear_dns_message(msg));
return ReplyToSNodeDNSWhenReady(addr, std::move(replyMsg), false);
}
else if (answer.HasCNameForTLD(".loki"))
{
llarp_buffer_t buf(answer.rData);
auto qname = dns::DecodeName(&buf, true);
if (not qname)
return false;
1 year ago
service::Address addr;
if (not addr.FromString(*qname))
return false;
auto replyMsg = std::make_shared<dns::Message>(clear_dns_message(msg));
return ReplyToLokiDNSWhenReady(addr, replyMsg, false);
}
}
if (msg.questions.size() != 1)
{
1 year ago
llarp::LogWarn("bad number of dns questions: ", msg.questions.size());
return false;
}
1 year ago
std::string qname = msg.questions[0].Name();
const auto nameparts = split(qname, ".");
std::string ons_name;
1 year ago
if (nameparts.size() >= 2 and ends_with(qname, ".loki"))
{
ons_name = nameparts[nameparts.size() - 2];
ons_name += ".loki"sv;
}
1 year ago
if (msg.questions[0].qtype == dns::qTypeTXT)
6 years ago
{
1 year ago
RouterID snode;
if (snode.FromString(qname))
{
router()->lookup_router(
snode, [r = router(), msg = std::move(msg), reply](oxen::quic::message m) mutable {
if (m)
{
std::string payload;
try
{
oxenc::bt_dict_consumer btdc{m.body()};
payload = btdc.require<std::string>("RC");
}
catch (...)
{
log::warning(link_cat, "Failed to parse Find Router response!");
throw;
}
r->node_db()->put_rc_if_newer(RouterContact{payload});
msg.AddTXTReply(payload);
}
else
{
msg.AddNXReply();
r->link_manager().handle_find_router_error(std::move(m));
}
reply(msg);
});
1 year ago
return true;
}
if (msg.questions[0].IsLocalhost() and msg.questions[0].HasSubdomains())
1 year ago
{
const auto subdomain = msg.questions[0].Subdomains();
if (subdomain == "exit")
{
1 year ago
if (HasExit())
{
std::string s;
_exit_map.ForEachEntry([&s](const auto& range, const auto& exit) {
fmt::format_to(std::back_inserter(s), "{}={}; ", range, exit);
});
msg.AddTXTReply(std::move(s));
}
else
{
msg.AddNXReply();
}
}
1 year ago
else if (subdomain == "netid")
{
msg.AddTXTReply(fmt::format("netid={};", router()->rc().netID));
}
1 year ago
else
{
1 year ago
msg.AddNXReply();
}
}
1 year ago
else
6 years ago
{
1 year ago
msg.AddNXReply();
6 years ago
}
1 year ago
reply(msg);
}
else if (msg.questions[0].qtype == dns::qTypeMX)
{
// mx record
service::Address addr;
if (addr.FromString(qname, ".loki") || addr.FromString(qname, ".snode")
|| is_random_snode(msg) || is_localhost_loki(msg))
{
1 year ago
msg.AddMXReply(qname, 1);
}
else if (service::is_valid_name(ons_name))
{
lookup_name(ons_name, [msg, ons_name, reply](oxen::quic::message m) mutable {
if (m)
{
std::string result;
try
{
oxenc::bt_dict_consumer btdc{m.body()};
result = btdc.require<std::string>("NAME");
}
catch (...)
{
log::warning(logcat, "Failed to parse find name response!");
throw;
}
msg.AddMXReply(result, 1);
}
else
msg.AddNXReply();
1 year ago
reply(msg);
});
1 year ago
return true;
}
1 year ago
else
msg.AddNXReply();
reply(msg);
}
else if (msg.questions[0].qtype == dns::qTypeCNAME)
{
if (is_random_snode(msg))
{
1 year ago
RouterID random;
if (router()->GetRandomGoodRouter(random))
{
1 year ago
msg.AddCNAMEReply(random.ToString(), 1);
}
else
msg.AddNXReply();
}
1 year ago
else if (msg.questions[0].IsLocalhost() and msg.questions[0].HasSubdomains())
{
1 year ago
const auto subdomain = msg.questions[0].Subdomains();
if (subdomain == "exit" and HasExit())
{
1 year ago
_exit_map.ForEachEntry(
[&msg](const auto&, const auto& exit) { msg.AddCNAMEReply(exit.ToString(), 1); });
}
1 year ago
else
{
1 year ago
msg.AddNXReply();
}
1 year ago
}
else if (is_localhost_loki(msg))
{
size_t counter = 0;
context->ForEachService(
[&](const std::string&, const std::shared_ptr<service::Endpoint>& service) -> bool {
const service::Address addr = service->GetIdentity().pub.Addr();
msg.AddCNAMEReply(addr.ToString(), 1);
++counter;
return true;
});
if (counter == 0)
msg.AddNXReply();
}
else
msg.AddNXReply();
reply(msg);
}
else if (msg.questions[0].qtype == dns::qTypeA || msg.questions[0].qtype == dns::qTypeAAAA)
{
const bool isV6 = msg.questions[0].qtype == dns::qTypeAAAA;
const bool isV4 = msg.questions[0].qtype == dns::qTypeA;
llarp::service::Address addr;
if (isV6 && !SupportsV6())
{ // empty reply but not a NXDOMAIN so that client can retry IPv4
msg.AddNSReply("localhost.loki.");
}
// on MacOS this is a typeA query
else if (is_random_snode(msg))
{
RouterID random;
if (router()->GetRandomGoodRouter(random))
{
1 year ago
msg.AddCNAMEReply(random.ToString(), 1);
return ReplyToSNodeDNSWhenReady(random, std::make_shared<dns::Message>(msg), isV6);
}
msg.AddNXReply();
}
1 year ago
else if (is_localhost_loki(msg))
6 years ago
{
1 year ago
const bool lookingForExit = msg.questions[0].Subdomains() == "exit";
huint128_t ip = GetIfAddr();
if (ip.h)
6 years ago
{
1 year ago
if (lookingForExit)
{
1 year ago
if (HasExit())
{
1 year ago
_exit_map.ForEachEntry(
[&msg](const auto&, const auto& exit) { msg.AddCNAMEReply(exit.ToString()); });
msg.AddINReply(ip, isV6);
}
else
{
1 year ago
msg.AddNXReply();
}
}
else
{
1 year ago
msg.AddCNAMEReply(_identity.pub.Name(), 1);
msg.AddINReply(ip, isV6);
}
6 years ago
}
1 year ago
else
6 years ago
{
1 year ago
msg.AddNXReply();
6 years ago
}
1 year ago
}
else if (addr.FromString(qname, ".loki"))
{
if (isV4 && SupportsV6())
6 years ago
{
1 year ago
msg.hdr_fields |= dns::flags_QR | dns::flags_AA | dns::flags_RA;
6 years ago
}
1 year ago
else
{
1 year ago
return ReplyToLokiDNSWhenReady(addr, std::make_shared<dns::Message>(msg), isV6);
}
6 years ago
}
1 year ago
else if (addr.FromString(qname, ".snode"))
6 years ago
{
1 year ago
if (isV4 && SupportsV6())
6 years ago
{
1 year ago
msg.hdr_fields |= dns::flags_QR | dns::flags_AA | dns::flags_RA;
6 years ago
}
1 year ago
else
{
1 year ago
return ReplyToSNodeDNSWhenReady(
addr.as_array(), std::make_shared<dns::Message>(msg), isV6);
}
1 year ago
}
else if (service::is_valid_name(ons_name))
1 year ago
{
lookup_name(
ons_name,
1 year ago
[msg = std::make_shared<dns::Message>(msg),
name = Name(),
ons_name,
1 year ago
isV6,
reply,
ReplyToDNSWhenReady](oxen::quic::message m) mutable {
if (m)
{
std::string name;
try
{
oxenc::bt_dict_consumer btdc{m.body()};
name = btdc.require<std::string>("NAME");
}
catch (...)
{
log::warning(logcat, "Failed to parse find name response!");
throw;
}
ReplyToDNSWhenReady(name, msg, isV6);
}
else
{
log::warning(logcat, "{} (ONS name: {}) not resolved", name, ons_name);
msg->AddNXReply();
1 year ago
reply(*msg);
}
});
return true;
}
6 years ago
else
msg.AddNXReply();
6 years ago
1 year ago
reply(msg);
}
1 year ago
else if (msg.questions[0].qtype == dns::qTypePTR)
5 years ago
{
1 year ago
// reverse dns
if (auto ip = dns::DecodePTR(msg.questions[0].qname))
6 years ago
{
1 year ago
if (auto maybe = ObtainAddrForIP(*ip))
6 years ago
{
1 year ago
var::visit([&msg](auto&& result) { msg.AddAReply(result.ToString()); }, *maybe);
reply(msg);
return true;
6 years ago
}
}
1 year ago
msg.AddNXReply();
reply(msg);
return true;
}
else if (msg.questions[0].qtype == dns::qTypeSRV)
{
auto srv_for = msg.questions[0].Subdomains();
auto name = msg.questions[0].qname;
if (is_localhost_loki(msg))
{
1 year ago
msg.AddSRVReply(intro_set().GetMatchingSRVRecords(srv_for));
reply(msg);
return true;
}
1 year ago
LookupServiceAsync(
name,
srv_for,
[reply, msg = std::make_shared<dns::Message>(std::move(msg))](auto records) {
if (records.empty())
{
msg->AddNXReply();
}
else
{
msg->AddSRVReply(records);
}
reply(*msg);
});
return true;
}
else
{
msg.AddNXReply();
reply(msg);
6 years ago
}
1 year ago
return true;
}
6 years ago
1 year ago
void
TunEndpoint::ResetInternalState()
{
service::Endpoint::ResetInternalState();
}
bool
TunEndpoint::SupportsV6() const
{
return m_UseV6;
}
// FIXME: pass in which question it should be addressing
bool
TunEndpoint::ShouldHookDNSMessage(const dns::Message& msg) const
{
llarp::service::Address addr;
if (msg.questions.size() == 1)
6 years ago
{
1 year ago
/// hook every .loki
if (msg.questions[0].HasTLD(".loki"))
return true;
/// hook every .snode
if (msg.questions[0].HasTLD(".snode"))
return true;
// hook any ranges we own
if (msg.questions[0].qtype == llarp::dns::qTypePTR)
6 years ago
{
1 year ago
if (auto ip = dns::DecodePTR(msg.questions[0].qname))
return m_OurRange.Contains(*ip);
6 years ago
return false;
}
1 year ago
}
for (const auto& answer : msg.answers)
{
if (answer.HasCNameForTLD(".loki"))
return true;
if (answer.HasCNameForTLD(".snode"))
return true;
}
return false;
}
bool
TunEndpoint::MapAddress(const service::Address& addr, huint128_t ip, bool SNode)
{
auto itr = m_IPToAddr.find(ip);
if (itr != m_IPToAddr.end())
{
llarp::LogWarn(
ip, " already mapped to ", service::Address(itr->second.as_array()).ToString());
return false;
}
llarp::LogInfo(Name() + " map ", addr.ToString(), " to ", ip);
m_IPToAddr[ip] = addr;
m_AddrToIP[addr] = ip;
m_SNodes[addr] = SNode;
MarkIPActiveForever(ip);
MarkAddressOutbound(addr);
return true;
}
std::string
TunEndpoint::GetIfName() const
{
#ifdef _WIN32
return net::TruncateV6(GetIfAddr()).ToString();
#else
return m_IfName;
#endif
}
bool
TunEndpoint::Start()
{
if (not Endpoint::Start())
return false;
return SetupNetworking();
}
bool
TunEndpoint::IsSNode() const
{
// TODO : implement me
return false;
}
bool
TunEndpoint::SetupTun()
{
m_NextIP = m_OurIP;
m_MaxIP = m_OurRange.HighestAddr();
llarp::LogInfo(Name(), " set ", m_IfName, " to have address ", m_OurIP);
llarp::LogInfo(Name(), " allocated up to ", m_MaxIP, " on range ", m_OurRange);
6 years ago
1 year ago
const service::Address ourAddr = _identity.pub.Addr();
6 years ago
1 year ago
if (not MapAddress(ourAddr, GetIfAddr(), false))
{
1 year ago
return false;
}
1 year ago
vpn::InterfaceInfo info;
info.addrs.emplace_back(m_OurRange);
6 years ago
1 year ago
if (m_BaseV6Address)
{
1 year ago
IPRange v6range = m_OurRange;
v6range.addr = (*m_BaseV6Address) | m_OurRange.addr;
LogInfo(Name(), " using v6 range: ", v6range);
info.addrs.emplace_back(v6range, AF_INET6);
}
1 year ago
info.ifname = m_IfName;
1 year ago
LogInfo(Name(), " setting up network...");
1 year ago
try
{
m_NetIf = router()->vpn_platform()->CreateInterface(std::move(info), router());
}
catch (std::exception& ex)
{
LogError(Name(), " failed to set up network interface: ", ex.what());
return false;
}
1 year ago
m_IfName = m_NetIf->Info().ifname;
LogInfo(Name(), " got network interface ", m_IfName);
1 year ago
auto handle_packet = [netif = m_NetIf, pkt_router = m_PacketRouter](auto pkt) {
pkt.reply = [netif](auto pkt) { netif->WritePacket(std::move(pkt)); };
pkt_router->HandleIPPacket(std::move(pkt));
};
4 years ago
1 year ago
if (not router()->loop()->add_network_interface(m_NetIf, std::move(handle_packet)))
{
LogError(Name(), " failed to add network interface");
return false;
}
1 year ago
m_OurIPv6 = llarp::huint128_t{
llarp::uint128_t{0xfd2e'6c6f'6b69'0000, llarp::net::TruncateV6(m_OurRange.addr).h}};
1 year ago
if constexpr (not llarp::platform::is_apple)
{
if (auto maybe = router()->net().GetInterfaceIPv6Address(m_IfName))
5 years ago
{
1 year ago
m_OurIPv6 = *maybe;
LogInfo(Name(), " has ipv6 address ", m_OurIPv6);
}
1 year ago
}
1 year ago
LogInfo(Name(), " setting up dns...");
SetupDNS();
Loop()->call_soon([this]() { router()->route_poker()->set_dns_mode(false); });
1 year ago
return HasAddress(ourAddr);
}
1 year ago
std::unordered_map<std::string, std::string>
TunEndpoint::NotifyParams() const
{
auto env = Endpoint::NotifyParams();
env.emplace("IP_ADDR", m_OurIP.ToString());
env.emplace("IF_ADDR", m_OurRange.ToString());
env.emplace("IF_NAME", m_IfName);
std::string strictConnect;
for (const auto& addr : m_StrictConnectAddrs)
strictConnect += addr.ToString() + " ";
env.emplace("STRICT_CONNECT_ADDRS", strictConnect);
return env;
}
bool
TunEndpoint::SetupNetworking()
{
llarp::LogInfo("Set Up networking for ", Name());
return SetupTun();
}
1 year ago
void
TunEndpoint::Tick(llarp_time_t now)
{
Endpoint::Tick(now);
}
1 year ago
bool
TunEndpoint::Stop()
{
// stop vpn tunnel
if (m_NetIf)
m_NetIf->Stop();
if (m_RawDNS)
m_RawDNS->Stop();
// save address map if applicable
if (m_PersistAddrMapFile and not platform::is_android)
{
const auto& file = *m_PersistAddrMapFile;
LogInfo(Name(), " saving address map to ", file);
if (auto maybe = util::OpenFileStream<fs::ofstream>(file, std::ios_base::binary))
4 years ago
{
1 year ago
std::map<std::string, std::string> addrmap;
for (const auto& [ip, addr] : m_IPToAddr)
{
1 year ago
if (not m_SNodes.at(addr))
{
const service::Address a{addr.as_array()};
if (HasInboundConvo(a))
addrmap[ip.ToString()] = a.ToString();
}
}
1 year ago
const auto data = oxenc::bt_serialize(addrmap);
maybe->write(data.data(), data.size());
4 years ago
}
6 years ago
}
1 year ago
if (m_DNS)
m_DNS->Stop();
return llarp::service::Endpoint::Stop();
}
std::optional<service::Address>
TunEndpoint::ObtainExitAddressFor(
huint128_t ip,
std::function<service::Address(std::unordered_set<service::Address>)> exitSelectionStrat)
{
// is it already mapped? return the mapping
if (auto itr = m_ExitIPToExitAddress.find(ip); itr != m_ExitIPToExitAddress.end())
return itr->second;
6 years ago
const auto& net = router()->net();
1 year ago
const bool is_bogon = net.IsBogonIP(ip);
// build up our candidates to choose
std::unordered_set<service::Address> candidates;
for (const auto& entry : _exit_map.FindAllEntries(ip))
6 years ago
{
1 year ago
// in the event the exit's range is a bogon range, make sure the ip is located in that range
// to allow it
if ((is_bogon and net.IsBogonRange(entry.first) and entry.first.Contains(ip))
or entry.first.Contains(ip))
candidates.emplace(entry.second);
6 years ago
}
1 year ago
// no candidates? bail.
if (candidates.empty())
return std::nullopt;
if (not exitSelectionStrat)
6 years ago
{
1 year ago
// default strat to random choice
exitSelectionStrat = [](auto candidates) {
auto itr = candidates.begin();
std::advance(itr, llarp::randint() % candidates.size());
return *itr;
};
6 years ago
}
1 year ago
// map the exit and return the endpoint we mapped it to
return m_ExitIPToExitAddress.emplace(ip, exitSelectionStrat(candidates)).first->second;
}
6 years ago
1 year ago
void
TunEndpoint::HandleGotUserPacket(net::IPPacket pkt)
{
huint128_t dst, src;
if (pkt.IsV4())
6 years ago
{
1 year ago
dst = pkt.dst4to6();
src = pkt.src4to6();
6 years ago
}
1 year ago
else
{
1 year ago
dst = pkt.dstv6();
src = pkt.srcv6();
}
1 year ago
if constexpr (llarp::platform::is_apple)
{
1 year ago
if (dst == m_OurIP)
{
1 year ago
HandleWriteIPPacket(pkt.ConstBuffer(), src, dst, 0);
return;
}
}
1 year ago
if (_state->is_exit_enabled)
6 years ago
{
1 year ago
dst = net::ExpandV4(net::TruncateV6(dst));
}
12 months ago
1 year ago
auto itr = m_IPToAddr.find(dst);
12 months ago
1 year ago
if (itr == m_IPToAddr.end())
{
service::Address addr{};
1 year ago
if (auto maybe = ObtainExitAddressFor(dst))
addr = *maybe;
else
{
1 year ago
// send icmp unreachable as we dont have any exits for this ip
if (const auto icmp = pkt.MakeICMPUnreachable())
HandleWriteIPPacket(icmp->ConstBuffer(), dst, src, 0);
1 year ago
return;
}
12 months ago
1 year ago
std::function<void(void)> extra_cb;
12 months ago
1 year ago
if (not HasFlowToService(addr))
{
1 year ago
extra_cb = [poker = router()->route_poker()]() { poker->put_up(); };
}
12 months ago
1 year ago
pkt.ZeroSourceAddress();
MarkAddressOutbound(addr);
12 months ago
1 year ago
EnsurePathToService(
addr,
[pkt, extra_cb, this](service::Address addr, service::OutboundContext* ctx) mutable {
1 year ago
if (ctx)
{
1 year ago
if (extra_cb)
extra_cb();
ctx->send_packet_to_remote(pkt.to_string());
router()->TriggerPump();
1 year ago
return;
}
1 year ago
LogWarn("cannot ensure path to exit ", addr, " so we drop some packets");
},
PathAlignmentTimeout());
1 year ago
return;
}
std::variant<service::Address, RouterID> to;
service::ProtocolType type;
if (m_SNodes.at(itr->second))
{
to = RouterID{itr->second.as_array()};
type = service::ProtocolType::TrafficV4;
}
else
{
to = service::Address{itr->second.as_array()};
type = _state->is_exit_enabled and src != m_OurIP ? service::ProtocolType::Exit
: pkt.ServiceProtocol();
6 years ago
}
1 year ago
// prepare packet for insertion into network
// this includes clearing IP addresses, recalculating checksums, etc
// this does not happen for exits because the point is they don't rewrite addresses
if (type != service::ProtocolType::Exit)
{
1 year ago
if (pkt.IsV4())
pkt.UpdateIPv4Address({0}, {0});
else
pkt.UpdateIPv6Address({0}, {0});
}
// try sending it on an existing convotag
// this succeds for inbound convos, probably.
if (auto maybe = GetBestConvoTagFor(to))
{
12 months ago
if (send_to(*maybe, pkt.to_string()))
{
1 year ago
MarkIPActive(dst);
router()->TriggerPump();
return;
}
1 year ago
}
// try establishing a path to this guy
// will fail if it's an inbound convo
EnsurePathTo(
to,
12 months ago
[pkt, dst, to, this](auto maybe) mutable {
1 year ago
if (not maybe)
{
var::visit(
[this](auto&& addr) {
LogWarn(Name(), " failed to ensure path to ", addr, " no convo tag found");
},
to);
}
12 months ago
if (send_to(*maybe, pkt.to_string()))
1 year ago
{
MarkIPActive(dst);
router()->TriggerPump();
}
else
{
var::visit(
[this](auto&& addr) {
LogWarn(Name(), " failed to send to ", addr, ", SendToOrQueue failed");
},
to);
}
},
PathAlignmentTimeout());
}
1 year ago
bool
TunEndpoint::ShouldAllowTraffic(const net::IPPacket& pkt) const
{
if (const auto exitPolicy = GetExitPolicy())
{
if (not exitPolicy->AllowsTraffic(pkt))
return false;
}
1 year ago
return true;
}
bool
TunEndpoint::HandleInboundPacket(
const service::ConvoTag tag,
const llarp_buffer_t& buf,
service::ProtocolType t,
uint64_t seqno)
{
LogTrace("Inbound ", t, " packet (", buf.sz, "B) on convo ", tag);
if (t == service::ProtocolType::QUIC)
{
1 year ago
auto* quic = GetQUICTunnel();
if (!quic)
QUIC lokinet integration refactor Refactors how quic packets get handled: the actual tunnels now live in tunnel.hpp's TunnelManager which holds and manages all the quic<->tcp tunnelling. service::Endpoint now holds a TunnelManager rather than a quic::Server. We only need one quic server, but we need a separate quic client instance per outgoing quic tunnel, and TunnelManager handles all that glue now. Adds QUIC packet handling to get to the right tunnel code. This required multiplexing incoming quic packets, as follows: Adds a very small quic tunnel packet header of 4 bytes: [1, SPORT, ECN] for client->server packets, where SPORT is our source "port" (really: just a uint16_t unique quic instance identifier) or [2, DPORT, ECN] for server->client packets where the DPORT is the SPORT from above. (This also reworks ECN bits to get properly carried over lokinet.) We don't need a destination/source port for the server-side because there is only ever one quic server (and we know we're going to it when the first byte of the header is 1). Removes the config option for quic exposing ports; a full lokinet will simply accept anything incoming on quic and tunnel it to the requested port on the the local endpoint IP (this handler will come in a following commit). Replace ConvoTags with full addresses: we need to carry the port, as well, which the ConvoTag can't give us, so change those to more general SockAddrs from which we can extract both the ConvoTag *and* the port. Add a pending connection queue along with new quic-side handlers to call when a stream becomes available (TunnelManager uses this to wire up pending incoming conns with quic streams as streams open up). Completely get rid of tunnel_server/tunnel_client.cpp code; it is now moved to tunnel.hpp. Add listen()/forget() methods in TunnelManager for setting up quic listening sockets (for liblokinet usage). Add open()/close() methods in TunnelManager for spinning up new quic clients for outgoing quic connections.
4 years ago
{
1 year ago
LogWarn("incoming quic packet but this endpoint is not quic capable; dropping");
return false;
}
1 year ago
if (buf.sz < 4)
{
LogWarn("invalid incoming quic packet, dropping");
return false;
1 year ago
}
LogInfo("tag active T=", tag);
quic->receive_packet(tag, buf);
return true;
}
1 year ago
if (t != service::ProtocolType::TrafficV4 && t != service::ProtocolType::TrafficV6
&& t != service::ProtocolType::Exit)
return false;
std::variant<service::Address, RouterID> addr;
if (auto maybe = GetEndpointWithConvoTag(tag))
{
addr = *maybe;
}
else
return false;
huint128_t src, dst;
1 year ago
net::IPPacket pkt;
if (not pkt.Load(buf))
return false;
1 year ago
if (_state->is_exit_enabled)
{
// exit side from exit
1 year ago
// check packet against exit policy and if as needed
if (not ShouldAllowTraffic(pkt))
return false;
src = ObtainIPForAddr(addr);
if (t == service::ProtocolType::Exit)
{
if (pkt.IsV4())
1 year ago
dst = pkt.dst4to6();
else if (pkt.IsV6())
{
1 year ago
dst = pkt.dstv6();
src = net::ExpandV4Lan(net::TruncateV6(src));
}
}
else
{
1 year ago
// non exit traffic on exit
dst = m_OurIP;
}
}
1 year ago
else if (t == service::ProtocolType::Exit)
6 years ago
{
1 year ago
// client side exit traffic from exit
if (pkt.IsV4())
{
1 year ago
dst = m_OurIP;
src = pkt.src4to6();
}
else if (pkt.IsV6())
{
1 year ago
dst = m_OurIPv6;
src = pkt.srcv6();
}
1 year ago
// find what exit we think this should be for
service::Address fromAddr{};
if (const auto* ptr = std::get_if<service::Address>(&addr))
{
fromAddr = *ptr;
}
else // don't allow snode
return false;
// make sure the mapping matches
if (auto itr = m_ExitIPToExitAddress.find(src); itr != m_ExitIPToExitAddress.end())
{
if (itr->second != fromAddr)
return false;
}
else
return false;
6 years ago
}
1 year ago
else
{
1 year ago
// snapp traffic
src = ObtainIPForAddr(addr);
dst = m_OurIP;
}
1 year ago
HandleWriteIPPacket(buf, src, dst, seqno);
return true;
}
6 years ago
1 year ago
bool
TunEndpoint::HandleWriteIPPacket(
const llarp_buffer_t& b, huint128_t src, huint128_t dst, uint64_t seqno)
{
ManagedBuffer buf(b);
WritePacket write;
write.seqno = seqno;
auto& pkt = write.pkt;
// load
if (!pkt.Load(buf))
{
return false;
}
if (pkt.IsV4())
{
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(dst)));
}
else if (pkt.IsV6())
6 years ago
{
1 year ago
pkt.UpdateIPv6Address(src, dst);
}
m_NetworkToUserPktQueue.push(std::move(write));
// wake up so we ensure that all packets are written to user
router()->TriggerPump();
return true;
}
huint128_t
TunEndpoint::GetIfAddr() const
{
return m_OurIP;
}
1 year ago
huint128_t
TunEndpoint::ObtainIPForAddr(std::variant<service::Address, RouterID> addr)
{
llarp_time_t now = Now();
huint128_t nextIP = {0};
AlignedBuffer<32> ident{};
bool snode = false;
1 year ago
var::visit([&ident](auto&& val) { ident = val.data(); }, addr);
if (std::get_if<RouterID>(&addr))
{
snode = true;
}
1 year ago
{
// previously allocated address
auto itr = m_AddrToIP.find(ident);
if (itr != m_AddrToIP.end())
{
1 year ago
// mark ip active
MarkIPActive(itr->second);
return itr->second;
}
1 year ago
}
// allocate new address
if (m_NextIP < m_MaxIP)
{
do
{
1 year ago
nextIP = ++m_NextIP;
} while (m_IPToAddr.find(nextIP) != m_IPToAddr.end() && m_NextIP < m_MaxIP);
if (nextIP < m_MaxIP)
{
m_AddrToIP[ident] = nextIP;
m_IPToAddr[nextIP] = ident;
m_SNodes[ident] = snode;
var::visit(
[&](auto&& remote) { llarp::LogInfo(Name(), " mapped ", remote, " to ", nextIP); },
addr);
MarkIPActive(nextIP);
return nextIP;
}
1 year ago
}
1 year ago
// we are full
// expire least active ip
// TODO: prevent DoS
std::pair<huint128_t, llarp_time_t> oldest = {huint128_t{0}, 0s};
1 year ago
// find oldest entry
auto itr = m_IPActivity.begin();
while (itr != m_IPActivity.end())
{
if (itr->second <= now)
{
1 year ago
if ((now - itr->second) > oldest.second)
{
1 year ago
oldest.first = itr->first;
oldest.second = itr->second;
}
}
1 year ago
++itr;
}
// remap address
m_IPToAddr[oldest.first] = ident;
m_AddrToIP[ident] = oldest.first;
m_SNodes[ident] = snode;
nextIP = oldest.first;
1 year ago
// mark ip active
m_IPActivity[nextIP] = std::max(m_IPActivity[nextIP], now);
6 years ago
1 year ago
return nextIP;
}
6 years ago
1 year ago
bool
TunEndpoint::HasRemoteForIP(huint128_t ip) const
{
return m_IPToAddr.find(ip) != m_IPToAddr.end();
}
6 years ago
1 year ago
void
TunEndpoint::MarkIPActive(huint128_t ip)
{
llarp::LogDebug(Name(), " address ", ip, " is active");
m_IPActivity[ip] = std::max(Now(), m_IPActivity[ip]);
}
1 year ago
void
TunEndpoint::MarkIPActiveForever(huint128_t ip)
{
m_IPActivity[ip] = std::numeric_limits<llarp_time_t>::max();
}
6 years ago
1 year ago
TunEndpoint::~TunEndpoint() = default;
1 year ago
} // namespace llarp::handlers