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

1523 lines
41 KiB
C++

#include "tun.hpp"
#include <algorithm>
#include <iterator>
#include <variant>
#ifndef _WIN32
#include <sys/socket.h>
#endif
#include <llarp/constants/platform.hpp>
#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.
3 years ago
#include <llarp/net/net.hpp>
#include <llarp/nodedb.hpp>
#include <llarp/router/route_poker.hpp>
#include <llarp/router/router.hpp>
#include <llarp/rpc/endpoint_rpc.hpp>
#include <llarp/service/context.hpp>
#include <llarp/service/endpoint_state.hpp>
#include <llarp/service/name.hpp>
#include <llarp/service/outbound_context.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.
3 years ago
#include <llarp/service/protocol_type.hpp>
#include <llarp/util/str.hpp>
8 months ago
namespace llarp::handlers
{
8 months 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)
{
8 months ago
if (not ShouldHookDNSMessage(query))
return false;
8 months 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;
8 months 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)}
{}
8 months ago
~DnsInterceptor() override = default;
8 months ago
void
SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override
{
auto pkt = net::IPPacket::make_udp(from, to, std::move(buf));
8 months ago
if (pkt.empty())
return;
m_Reply(std::move(pkt));
}
8 months ago
void
Stop() override{};
8 months ago
std::optional<SockAddr>
BoundOn() const override
{
return std::nullopt;
}
8 months ago
bool
WouldLoop(const SockAddr& to, const SockAddr& from) const override
{
if constexpr (platform::is_apple)
{
8 months 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;
}
else if (auto maybe_addr = m_Config.query_bind)
{
8 months ago
const auto& addr = *maybe_addr;
// omit traffic to and from our dns socket
return addr == to or addr == from;
}
8 months ago
return false;
}
};
8 months ago
class TunDNS : public dns::Server
{
TunEndpoint* const m_Endpoint;
8 months ago
std::optional<SockAddr> m_QueryBind;
net::ipaddr_t m_OurIP;
8 months ago
public:
std::shared_ptr<dns::PacketSource_Base> PacketSource;
8 months ago
virtual ~TunDNS() = default;
8 months ago
explicit TunDNS(TunEndpoint* ep, const llarp::DnsConfig& conf)
: dns::Server{ep->router()->loop(), conf, 0}
, m_Endpoint{ep}
, m_QueryBind{conf.query_bind}
8 months ago
, m_OurIP{ToNet(ep->GetIfAddr())}
{}
8 months ago
std::shared_ptr<dns::PacketSource_Base>
MakePacketSourceOn(const SockAddr&, const llarp::DnsConfig& conf) override
{
8 months 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;
}
8 months ago
};
8 months 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.raw)
{
8 months 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;
8 months 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();
if (m_DnsConfig.raw)
8 months ago
{
if (auto vpn = router()->vpn_platform())
{
8 months ago
// get the first local address we know of
std::optional<SockAddr> localaddr;
for (auto res : m_DNS->GetAllResolvers())
{
8 months ago
if (auto ptr = res.lock())
{
8 months 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())
{
8 months ago
handler->HandleIPPacket(std::move(pkt));
pkt = dns_io->ReadNextPacket();
}
8 months ago
});
m_RawDNS = dns_io;
}
}
8 months 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.upstream_dns)
8 months ago
upstreamRes.emplace_back(ent.ToString());
obj["ustreamResolvers"] = upstreamRes;
std::vector<std::string> localRes;
for (const auto& ent : m_DnsConfig.bind_addr)
8 months ago
localRes.emplace_back(ent.ToString());
obj["localResolvers"] = localRes;
// for backwards compat
if (not m_DnsConfig.bind_addr.empty())
8 months ago
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;
}
8 months 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
8 months ago
void
TunEndpoint::ReconfigureDNS(std::vector<SockAddr> servers)
{
if (m_DNS)
{
8 months ago
for (auto weak : m_DNS->GetAllResolvers())
{
8 months ago
if (auto ptr = weak.lock())
ptr->ResetResolver(servers);
}
}
8 months ago
}
8 months ago
bool
TunEndpoint::Configure(const NetworkConfig& conf, const DnsConfig& dnsConf)
{
if (conf.is_reachable)
{
8 months ago
_publish_introset = true;
log::info(link_cat, "TunEndpoint setting to be reachable by default");
}
8 months ago
else
{
8 months ago
_publish_introset = false;
log::info(link_cat, "TunEndpoint setting to be not reachable by default");
}
if (conf.auth_type == service::AuthType::FILE)
6 years ago
{
_auth_policy = service::make_file_auth_policy(router(), conf.auth_files, conf.auth_file_type);
8 months ago
}
else if (conf.auth_type != service::AuthType::NONE)
8 months ago
{
std::string url, method;
if (conf.auth_url.has_value() and conf.auth_method.has_value())
{
url = *conf.auth_url;
method = *conf.auth_method;
}
8 months ago
auto auth = std::make_shared<rpc::EndpointAuthRPC>(
url,
method,
conf.auth_whitelist,
conf.auth_static_tokens,
8 months ago
router()->lmq(),
shared_from_this());
auth->Start();
_auth_policy = std::move(auth);
}
8 months ago
m_DnsConfig = dnsConf;
m_TrafficPolicy = conf.traffic_policy;
m_OwnedRanges = conf.owned_ranges;
4 years ago
m_BaseV6Address = conf.base_ipv6_addr;
if (conf.path_alignment_timeout)
8 months ago
{
m_PathAlignmentTimeout = *conf.path_alignment_timeout;
8 months ago
}
else
m_PathAlignmentTimeout = service::Endpoint::PathAlignmentTimeout();
for (const auto& item : conf.map_addrs)
8 months ago
{
if (not MapAddress(item.second, item.first, false))
return false;
}
m_IfName = conf.if_name;
8 months ago
if (m_IfName.empty())
{
const auto maybe = router()->net().FindFreeTun();
8 months ago
if (not maybe.has_value())
throw std::runtime_error("cannot find free interface name");
m_IfName = *maybe;
}
m_OurRange = conf.if_addr;
8 months ago
if (!m_OurRange.addr.h)
{
const auto maybe = router()->net().FindFreeRange();
8 months ago
if (not maybe.has_value())
{
8 months ago
throw std::runtime_error("cannot find free address range");
6 years ago
}
8 months ago
m_OurRange = *maybe;
}
3 years ago
8 months ago
m_OurIP = m_OurRange.addr;
m_UseV6 = false;
m_PersistAddrMapFile = conf.addr_map_persist_file;
8 months ago
if (m_PersistAddrMapFile)
{
const auto& file = *m_PersistAddrMapFile;
if (fs::exists(file))
{
8 months ago
bool shouldLoadFile = true;
{
8 months 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)
{
8 months ago
shouldLoadFile = false;
}
8 months 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)
{
8 months ago
LogInfo(Name(), " address map file ", file, " does not exist, so we won't load it");
}
else
8 months 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()};
8 months ago
LogDebug(Name(), " parsing address map data: ", bdata);
8 months ago
const auto parsed = oxenc::bt_deserialize<oxenc::bt_dict>(bdata);
8 months ago
for (const auto& [key, value] : parsed)
{
8 months ago
huint128_t ip{};
if (not ip.FromString(key))
{
8 months ago
LogWarn(Name(), " malformed IP in addr map data: ", key);
continue;
}
8 months ago
if (m_OurIP == ip)
continue;
if (not m_OurRange.Contains(ip))
{
8 months ago
LogWarn(Name(), " out of range IP in addr map data: ", ip);
continue;
}
EndpointBase::AddressVariant_t addr;
8 months ago
if (const auto* str = std::get_if<std::string>(&value))
{
if (auto maybe = service::parse_address(*str))
{
8 months ago
addr = *maybe;
}
else
{
8 months ago
LogWarn(Name(), " invalid address in addr map: ", *str);
continue;
}
}
8 months 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);
}
}
}
8 months ago
else
{
8 months ago
LogInfo(Name(), " skipping loading addr map at ", file, " as it does not currently exist");
}
6 years ago
}
// if (auto* quic = GetQUICTunnel())
// {
// TODO:
// quic->listen([this](std::string_view, uint16_t port) {
// return llarp::SockAddr{net::TruncateV6(GetIfAddr()), huint16_t{port}};
// });
// }
8 months ago
return Endpoint::Configure(conf, dnsConf);
}
6 years ago
8 months ago
bool
TunEndpoint::HasLocalIP(const huint128_t& ip) const
{
return m_IPToAddr.find(ip) != m_IPToAddr.end();
}
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))
{
8 months 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;
}
8 months ago
MarkAddressOutbound(addr);
return EnsurePathToService(
addr,
[this, addr, msg, reply, isV6](const Address&, OutboundContext* ctx) {
SendDNSReply(addr, ctx, msg, reply, isV6);
},
timeout);
};
8 months 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.from_string(name))
ReplyToSNodeDNSWhenReady(rid, msg, isV6);
8 months ago
};
8 months 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);
};
8 months ago
if (msg.answers.size() > 0)
{
8 months 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.from_string(*qname))
8 months ago
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;
8 months 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)
{
8 months ago
llarp::LogWarn("bad number of dns questions: ", msg.questions.size());
return false;
}
8 months ago
std::string qname = msg.questions[0].Name();
const auto nameparts = split(qname, ".");
std::string ons_name;
8 months ago
if (nameparts.size() >= 2 and ends_with(qname, ".loki"))
{
ons_name = nameparts[nameparts.size() - 2];
ons_name += ".loki"sv;
}
8 months ago
if (msg.questions[0].qtype == dns::qTypeTXT)
6 years ago
{
8 months ago
RouterID snode;
if (snode.from_string(qname))
8 months ago
{
if (auto rc = router()->node_db()->get_rc(snode))
msg.AddTXTReply(std::string{rc->view()});
else
msg.AddNXReply();
reply(msg);
8 months ago
return true;
}
if (msg.questions[0].IsLocalhost() and msg.questions[0].HasSubdomains())
8 months ago
{
const auto subdomain = msg.questions[0].Subdomains();
if (subdomain == "exit")
{
8 months 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();
}
}
8 months ago
else if (subdomain == "netid")
{
msg.AddTXTReply(fmt::format("netid={};", RouterContact::ACTIVE_NETID));
}
8 months ago
else
{
8 months ago
msg.AddNXReply();
}
}
8 months ago
else
6 years ago
{
8 months ago
msg.AddNXReply();
6 years ago
}
8 months 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))
{
8 months ago
msg.AddMXReply(qname, 1);
}
else if (service::is_valid_name(ons_name))
{
lookup_name(
ons_name, [msg, ons_name, reply](std::string name_result, bool success) mutable {
if (success)
{
msg.AddMXReply(name_result, 1);
}
else
msg.AddNXReply();
reply(msg);
});
8 months ago
return true;
}
8 months ago
else
msg.AddNXReply();
reply(msg);
}
else if (msg.questions[0].qtype == dns::qTypeCNAME)
{
if (is_random_snode(msg))
{
if (auto random = router()->GetRandomGoodRouter())
{
msg.AddCNAMEReply(random->ToString(), 1);
}
else
msg.AddNXReply();
}
8 months ago
else if (msg.questions[0].IsLocalhost() and msg.questions[0].HasSubdomains())
{
8 months ago
const auto subdomain = msg.questions[0].Subdomains();
if (subdomain == "exit" and HasExit())
{
8 months ago
_exit_map.ForEachEntry(
[&msg](const auto&, const auto& exit) { msg.AddCNAMEReply(exit.ToString(), 1); });
}
8 months ago
else
{
8 months ago
msg.AddNXReply();
}
8 months 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))
{
if (auto random = router()->GetRandomGoodRouter())
{
msg.AddCNAMEReply(random->ToString(), 1);
return ReplyToSNodeDNSWhenReady(*random, std::make_shared<dns::Message>(msg), isV6);
}
msg.AddNXReply();
}
8 months ago
else if (is_localhost_loki(msg))
6 years ago
{
8 months ago
const bool lookingForExit = msg.questions[0].Subdomains() == "exit";
huint128_t ip = GetIfAddr();
if (ip.h)
5 years ago
{
8 months ago
if (lookingForExit)
{
8 months ago
if (HasExit())
{
8 months ago
_exit_map.ForEachEntry(
[&msg](const auto&, const auto& exit) { msg.AddCNAMEReply(exit.ToString()); });
msg.AddINReply(ip, isV6);
}
else
{
8 months ago
msg.AddNXReply();
}
}
else
{
8 months ago
msg.AddCNAMEReply(_identity.pub.Name(), 1);
msg.AddINReply(ip, isV6);
}
6 years ago
}
8 months ago
else
6 years ago
{
8 months ago
msg.AddNXReply();
6 years ago
}
8 months ago
}
else if (addr.FromString(qname, ".loki"))
{
if (isV4 && SupportsV6())
6 years ago
{
8 months ago
msg.hdr_fields |= dns::flags_QR | dns::flags_AA | dns::flags_RA;
6 years ago
}
8 months ago
else
{
8 months ago
return ReplyToLokiDNSWhenReady(addr, std::make_shared<dns::Message>(msg), isV6);
}
6 years ago
}
8 months ago
else if (addr.FromString(qname, ".snode"))
6 years ago
{
8 months ago
if (isV4 && SupportsV6())
6 years ago
{
8 months ago
msg.hdr_fields |= dns::flags_QR | dns::flags_AA | dns::flags_RA;
6 years ago
}
8 months ago
else
{
8 months ago
return ReplyToSNodeDNSWhenReady(
addr.as_array(), std::make_shared<dns::Message>(msg), isV6);
}
8 months ago
}
else if (service::is_valid_name(ons_name))
8 months ago
{
lookup_name(
ons_name,
8 months ago
[msg = std::make_shared<dns::Message>(msg),
name = Name(),
ons_name,
8 months ago
isV6,
reply,
ReplyToDNSWhenReady](std::string name_result, bool success) mutable {
if (not success)
{
log::warning(logcat, "{} (ONS name: {}) not resolved", name, ons_name);
msg->AddNXReply();
8 months ago
reply(*msg);
}
ReplyToDNSWhenReady(name_result, msg, isV6);
});
return true;
}
6 years ago
else
msg.AddNXReply();
6 years ago
8 months ago
reply(msg);
}
8 months ago
else if (msg.questions[0].qtype == dns::qTypePTR)
5 years ago
{
8 months ago
// reverse dns
if (auto ip = dns::DecodePTR(msg.questions[0].qname))
6 years ago
{
8 months ago
if (auto maybe = ObtainAddrForIP(*ip))
6 years ago
{
8 months ago
var::visit([&msg](auto&& result) { msg.AddAReply(result.ToString()); }, *maybe);
reply(msg);
return true;
6 years ago
}
}
8 months 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))
{
8 months ago
msg.AddSRVReply(intro_set().GetMatchingSRVRecords(srv_for));
reply(msg);
return true;
}
8 months 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
}
8 months ago
return true;
}
6 years ago
8 months 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
{
8 months 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
{
8 months ago
if (auto ip = dns::DecodePTR(msg.questions[0].qname))
return m_OurRange.Contains(*ip);
6 years ago
return false;
}
8 months 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
8 months ago
const service::Address ourAddr = _identity.pub.Addr();
6 years ago
8 months ago
if (not MapAddress(ourAddr, GetIfAddr(), false))
{
8 months ago
return false;
}
8 months ago
vpn::InterfaceInfo info;
info.addrs.emplace_back(m_OurRange);
6 years ago
8 months ago
if (m_BaseV6Address)
{
8 months 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);
}
8 months ago
info.ifname = m_IfName;
8 months ago
LogInfo(Name(), " setting up network...");
8 months 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;
}
8 months ago
m_IfName = m_NetIf->Info().ifname;
LogInfo(Name(), " got network interface ", m_IfName);
8 months 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));
};
3 years ago
8 months ago
if (not router()->loop()->add_network_interface(m_NetIf, std::move(handle_packet)))
{
LogError(Name(), " failed to add network interface");
return false;
}
8 months ago
m_OurIPv6 = llarp::huint128_t{
llarp::uint128_t{0xfd2e'6c6f'6b69'0000, llarp::net::TruncateV6(m_OurRange.addr).h}};
8 months ago
if constexpr (not llarp::platform::is_apple)
{
if (auto maybe = router()->net().GetInterfaceIPv6Address(m_IfName))
5 years ago
{
8 months ago
m_OurIPv6 = *maybe;
LogInfo(Name(), " has ipv6 address ", m_OurIPv6);
}
8 months ago
}
8 months ago
LogInfo(Name(), " setting up dns...");
SetupDNS();
Loop()->call_soon([this]() { router()->route_poker()->set_dns_mode(false); });
8 months ago
return HasAddress(ourAddr);
}
8 months 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();
}
8 months ago
void
TunEndpoint::Tick(llarp_time_t now)
{
Endpoint::Tick(now);
}
8 months 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))
3 years ago
{
8 months ago
std::map<std::string, std::string> addrmap;
for (const auto& [ip, addr] : m_IPToAddr)
{
8 months ago
if (not m_SNodes.at(addr))
{
const service::Address a{addr.as_array()};
if (HasInboundConvo(a))
addrmap[ip.ToString()] = a.ToString();
}
}
8 months ago
const auto data = oxenc::bt_serialize(addrmap);
maybe->write(data.data(), data.size());
3 years ago
}
6 years ago
}
8 months 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();
8 months 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))
5 years ago
{
8 months 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);
5 years ago
}
8 months ago
// no candidates? bail.
if (candidates.empty())
return std::nullopt;
if (not exitSelectionStrat)
6 years ago
{
8 months 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
}
8 months ago
// map the exit and return the endpoint we mapped it to
return m_ExitIPToExitAddress.emplace(ip, exitSelectionStrat(candidates)).first->second;
}
6 years ago
8 months ago
void
TunEndpoint::HandleGotUserPacket(net::IPPacket pkt)
{
huint128_t dst, src;
if (pkt.IsV4())
6 years ago
{
8 months ago
dst = pkt.dst4to6();
src = pkt.src4to6();
6 years ago
}
8 months ago
else
{
8 months ago
dst = pkt.dstv6();
src = pkt.srcv6();
}
8 months ago
if constexpr (llarp::platform::is_apple)
{
8 months ago
if (dst == m_OurIP)
{
8 months ago
HandleWriteIPPacket(pkt.ConstBuffer(), src, dst, 0);
return;
}
}
8 months ago
if (_state->is_exit_enabled)
6 years ago
{
8 months ago
dst = net::ExpandV4(net::TruncateV6(dst));
}
8 months ago
8 months ago
auto itr = m_IPToAddr.find(dst);
8 months ago
8 months ago
if (itr == m_IPToAddr.end())
{
service::Address addr{};
8 months ago
if (auto maybe = ObtainExitAddressFor(dst))
addr = *maybe;
else
{
8 months 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);
8 months ago
return;
}
8 months ago
8 months ago
std::function<void(void)> extra_cb;
8 months ago
8 months ago
if (not HasFlowToService(addr))
{
8 months ago
extra_cb = [poker = router()->route_poker()]() { poker->put_up(); };
}
8 months ago
8 months ago
pkt.ZeroSourceAddress();
MarkAddressOutbound(addr);
8 months ago
8 months ago
EnsurePathToService(
addr,
[pkt, extra_cb, this](service::Address addr, service::OutboundContext* ctx) mutable {
8 months ago
if (ctx)
{
8 months ago
if (extra_cb)
extra_cb();
ctx->send_packet_to_remote(pkt.to_string());
router()->TriggerPump();
8 months ago
return;
}
8 months ago
LogWarn("cannot ensure path to exit ", addr, " so we drop some packets");
},
PathAlignmentTimeout());
8 months 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
}
8 months 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)
{
8 months 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))
{
8 months ago
if (send_to(*maybe, pkt.to_string()))
{
8 months ago
MarkIPActive(dst);
router()->TriggerPump();
return;
}
8 months ago
}
// try establishing a path to this guy
// will fail if it's an inbound convo
EnsurePathTo(
to,
8 months ago
[pkt, dst, to, this](auto maybe) mutable {
8 months ago
if (not maybe)
{
var::visit(
[this](auto&& addr) {
LogWarn(Name(), " failed to ensure path to ", addr, " no convo tag found");
},
to);
}
8 months ago
if (send_to(*maybe, pkt.to_string()))
8 months ago
{
MarkIPActive(dst);
router()->TriggerPump();
}
else
{
var::visit(
[this](auto&& addr) {
LogWarn(Name(), " failed to send to ", addr, ", SendToOrQueue failed");
},
to);
}
},
PathAlignmentTimeout());
}
8 months ago
bool
TunEndpoint::ShouldAllowTraffic(const net::IPPacket& pkt) const
{
if (const auto exitPolicy = GetExitPolicy())
{
if (not exitPolicy->AllowsTraffic(pkt))
return false;
}
8 months 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)
{
8 months 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.
3 years ago
{
8 months ago
LogWarn("incoming quic packet but this endpoint is not quic capable; dropping");
return false;
}
8 months ago
if (buf.sz < 4)
{
LogWarn("invalid incoming quic packet, dropping");
return false;
8 months ago
}
LogInfo("tag active T=", tag);
// TODO:
// quic->receive_packet(tag, buf);
8 months ago
return true;
}
8 months 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;
8 months ago
net::IPPacket pkt;
if (not pkt.Load(buf))
return false;
8 months ago
if (_state->is_exit_enabled)
{
// exit side from exit
8 months 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())
8 months ago
dst = pkt.dst4to6();
else if (pkt.IsV6())
{
8 months ago
dst = pkt.dstv6();
src = net::ExpandV4Lan(net::TruncateV6(src));
}
}
else
{
8 months ago
// non exit traffic on exit
dst = m_OurIP;
}
}
8 months ago
else if (t == service::ProtocolType::Exit)
6 years ago
{
8 months ago
// client side exit traffic from exit
if (pkt.IsV4())
{
8 months ago
dst = m_OurIP;
src = pkt.src4to6();
}
else if (pkt.IsV6())
{
8 months ago
dst = m_OurIPv6;
src = pkt.srcv6();
}
8 months 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
}
8 months ago
else
{
8 months ago
// snapp traffic
src = ObtainIPForAddr(addr);
dst = m_OurIP;
}
8 months ago
HandleWriteIPPacket(buf, src, dst, seqno);
return true;
}
6 years ago
8 months 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
{
8 months ago
pkt.UpdateIPv6Address(src, dst);
}
// TODO: send this along but without a fucking huint182_t
// m_NetworkToUserPktQueue.push(std::move(write));
8 months ago
// wake up so we ensure that all packets are written to user
router()->TriggerPump();
return true;
}
huint128_t
TunEndpoint::GetIfAddr() const
{
return m_OurIP;
}
8 months 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;
8 months ago
var::visit([&ident](auto&& val) { ident = val.data(); }, addr);
if (std::get_if<RouterID>(&addr))
{
snode = true;
}
8 months ago
{
// previously allocated address
auto itr = m_AddrToIP.find(ident);
if (itr != m_AddrToIP.end())
{
8 months ago
// mark ip active
MarkIPActive(itr->second);
return itr->second;
}
8 months ago
}
// allocate new address
if (m_NextIP < m_MaxIP)
{
do
{
8 months 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;
}
8 months ago
}
8 months ago
// we are full
// expire least active ip
// TODO: prevent DoS
std::pair<huint128_t, llarp_time_t> oldest = {huint128_t{0}, 0s};
8 months ago
// find oldest entry
auto itr = m_IPActivity.begin();
while (itr != m_IPActivity.end())
{
if (itr->second <= now)
{
8 months ago
if ((now - itr->second) > oldest.second)
{
8 months ago
oldest.first = itr->first;
oldest.second = itr->second;
}
}
8 months ago
++itr;
}
// remap address
m_IPToAddr[oldest.first] = ident;
m_AddrToIP[ident] = oldest.first;
m_SNodes[ident] = snode;
nextIP = oldest.first;
8 months ago
// mark ip active
m_IPActivity[nextIP] = std::max(m_IPActivity[nextIP], now);
6 years ago
8 months ago
return nextIP;
}
6 years ago
8 months ago
bool
TunEndpoint::HasRemoteForIP(huint128_t ip) const
{
return m_IPToAddr.find(ip) != m_IPToAddr.end();
}
6 years ago
8 months ago
void
TunEndpoint::MarkIPActive(huint128_t ip)
{
llarp::LogDebug(Name(), " address ", ip, " is active");
m_IPActivity[ip] = std::max(Now(), m_IPActivity[ip]);
}
8 months ago
void
TunEndpoint::MarkIPActiveForever(huint128_t ip)
{
m_IPActivity[ip] = std::numeric_limits<llarp_time_t>::max();
}
6 years ago
8 months ago
TunEndpoint::~TunEndpoint() = default;
8 months ago
} // namespace llarp::handlers