Netif and quic Tunnel reduction

- link::TunnelManager will be re-implemented in link::QUICTunnel to make and accept TCP connections
- Will be simplified using Libevent and hooked into the new LocalEndpoint/RemoteHandler model
- TCP{Handle,Socket} ownership model will be revised to more monadic model; this will allow the TCPSockets (sockets representing accepted connections) to function entirely independantly of the bound socket
This commit is contained in:
dr7ana 2024-04-12 08:20:42 -07:00
parent d9cee71cf5
commit 6e5d8413f5
78 changed files with 326 additions and 1516 deletions

View File

@ -6,7 +6,7 @@ namespace llarp
{
static auto logcat = log::Cat("address");
std::optional<NetworkAddress> NetworkAddress::from_network_addr(std::string arg)
std::optional<NetworkAddress> NetworkAddress::from_network_addr(const std::string& arg)
{
std::optional<NetworkAddress> ret = std::nullopt;
@ -24,6 +24,11 @@ namespace llarp
return ret;
}
NetworkAddress NetworkAddress::from_pubkey(const RouterID& rid, bool is_client)
{
return NetworkAddress{rid, is_client};
}
NetworkAddress::NetworkAddress(std::string_view arg, std::string_view tld) : _tld{tld}
{
if (not _pubkey.from_string(arg.substr(0, _tld.size())))

View File

@ -3,6 +3,7 @@
#include "keys.hpp"
#include "utils.hpp"
#include <llarp/router_id.hpp>
#include <llarp/service/name.hpp>
#include <llarp/util/aligned.hpp>
#include <llarp/util/concept.hpp>
@ -27,30 +28,22 @@ namespace llarp
private:
PubKey _pubkey;
std::string _tld;
bool _is_client{false};
std::string _tld;
// This private constructor expects a '.snode' or '.loki' suffix
explicit NetworkAddress(std::string_view addr, std::string_view tld);
public:
template <RemotePubKeyType pubkey_t>
explicit NetworkAddress(pubkey_t pubkey, std::string_view tld) : NetworkAddress{pubkey.to_view(), tld}
// This private constructor expects NO '.snode' or '.loki' suffix
explicit NetworkAddress(RouterID rid, bool is_client) : _pubkey{std::move(rid)}, _is_client{is_client}
{}
public:
NetworkAddress() = default;
~NetworkAddress() = default;
explicit NetworkAddress(RelayPubKey rpk) : NetworkAddress{rpk, TLD::SNODE}
{}
explicit NetworkAddress(ClientPubKey cpk) : NetworkAddress{cpk, TLD::LOKI}
{}
NetworkAddress(const NetworkAddress& other) : NetworkAddress{other._pubkey, other._tld}
{}
NetworkAddress(NetworkAddress&& other) : NetworkAddress{std::move(other._pubkey), std::move(other._tld)}
{}
NetworkAddress(const NetworkAddress& other) = default;
NetworkAddress(NetworkAddress&& other) = default;
NetworkAddress& operator=(const NetworkAddress& other) = default;
NetworkAddress& operator=(NetworkAddress&& other) = default;
@ -66,7 +59,10 @@ namespace llarp
// Will throw invalid_argument with bad input. Assumes that the network address terminates in either '.loki'
// or '.snode'
static std::optional<NetworkAddress> from_network_addr(std::string arg);
static std::optional<NetworkAddress> from_network_addr(const std::string& arg);
// Assumes that the pubkey passed is NOT terminated in either a '.loki' or '.snode' suffix
static NetworkAddress from_pubkey(const RouterID& rid, bool is_client);
bool is_client() const
{
@ -104,11 +100,14 @@ namespace llarp
lokinet relay. This object is NOT meant to be used in any scope referring to a hidden service or exit node
being operated on that remote relay (not that service nodes operate exit nodes anyways) -- for that, use the
above NetworkAddress type.
This object will become more differentiated from NetworkAddress once {Relay,Client}PubKey is implemented.
That is a whole other can of worms...
*/
struct RelayAddress
{
private:
RelayPubKey _pubkey;
PubKey _pubkey;
explicit RelayAddress(std::string_view addr);
@ -116,7 +115,7 @@ namespace llarp
RelayAddress() = default;
~RelayAddress() = default;
explicit RelayAddress(RelayPubKey cpk) : _pubkey{std::move(cpk)}
explicit RelayAddress(PubKey cpk) : _pubkey{std::move(cpk)}
{}
RelayAddress(const RelayAddress& other) = default;
@ -134,7 +133,7 @@ namespace llarp
// Will throw invalid_argument with bad input
static std::optional<RelayAddress> from_relay_addr(std::string arg);
const RelayPubKey& pubkey()
const PubKey& pubkey()
{
return _pubkey;
}

View File

@ -43,57 +43,4 @@ namespace llarp
{
return !(*this == other);
}
std::string RelayPubKey::to_string() const
{
return oxenc::to_hex(begin(), end());
}
RelayPubKey& RelayPubKey::operator=(const RelayPubKey& other)
{
std::memcpy(begin(), other.begin(), PUBKEYSIZE);
return *this;
}
bool RelayPubKey::operator<(const RelayPubKey& other) const
{
return as_array() < other.as_array();
}
bool RelayPubKey::operator==(const RelayPubKey& other) const
{
return as_array() == other.as_array();
}
bool RelayPubKey::operator!=(const RelayPubKey& other) const
{
return !(*this == other);
}
std::string ClientPubKey::to_string() const
{
return oxenc::to_hex(begin(), end());
}
bool ClientPubKey::operator<(const ClientPubKey& other) const
{
return as_array() < other.as_array();
}
bool ClientPubKey::operator==(const ClientPubKey& other) const
{
return as_array() == other.as_array();
}
bool ClientPubKey::operator!=(const ClientPubKey& other) const
{
return !(*this == other);
}
ClientPubKey& ClientPubKey::operator=(const ClientPubKey& other)
{
std::memcpy(begin(), other.begin(), PUBKEYSIZE);
return *this;
}
} // namespace llarp

View File

@ -42,70 +42,6 @@ namespace llarp
bool operator==(const PubKey& other) const;
bool operator!=(const PubKey& other) const;
};
struct RelayPubKey final : public PubKey
{
RelayPubKey() = default;
explicit RelayPubKey(const uint8_t* data) : PubKey{data}
{}
explicit RelayPubKey(const std::array<uint8_t, PUBKEYSIZE>& data) : PubKey{data}
{}
explicit RelayPubKey(ustring_view data) : PubKey{data.data()}
{}
explicit RelayPubKey(std::string_view data) : RelayPubKey{to_usv(data)}
{}
RelayPubKey(const RelayPubKey& other) : RelayPubKey{other.data()}
{}
RelayPubKey(RelayPubKey&& other) : RelayPubKey{other.data()}
{}
std::string to_string() const;
RelayPubKey& operator=(const RelayPubKey& other);
bool operator<(const RelayPubKey& other) const;
bool operator==(const RelayPubKey& other) const;
bool operator!=(const RelayPubKey& other) const;
};
struct ClientPubKey final : public PubKey
{
ClientPubKey() = default;
explicit ClientPubKey(const uint8_t* data) : PubKey{data}
{}
explicit ClientPubKey(const std::array<uint8_t, PUBKEYSIZE>& data) : PubKey{data}
{}
explicit ClientPubKey(ustring_view data) : PubKey{data.data()}
{}
explicit ClientPubKey(std::string_view data) : ClientPubKey{to_usv(data)}
{}
ClientPubKey(const ClientPubKey& other) : ClientPubKey{other.data()}
{}
ClientPubKey(ClientPubKey&& other) : ClientPubKey{other.data()}
{}
std::string to_string() const;
ClientPubKey& operator=(const ClientPubKey& other);
bool operator<(const ClientPubKey& other) const;
bool operator==(const ClientPubKey& other) const;
bool operator!=(const ClientPubKey& other) const;
};
template <typename addr_t>
concept CONCEPT_COMPAT RemotePubKeyType = std::is_base_of_v<PubKey, addr_t>;
template <RemotePubKeyType addr_t>
addr_t make_from_hex(const std::string& str)
{
addr_t p;
oxenc::from_hex(str.begin(), str.end(), p.begin());
return p;
}
} // namespace llarp
namespace std
@ -113,12 +49,4 @@ namespace std
template <>
struct hash<llarp::PubKey> : public hash<llarp::AlignedBuffer<PUBKEYSIZE>>
{};
template <>
struct hash<llarp::ClientPubKey> : public hash<llarp::PubKey>
{};
template <>
struct hash<llarp::RelayPubKey> : public hash<llarp::PubKey>
{};
} // namespace std

View File

@ -5,7 +5,6 @@
#include <llarp/crypto/constants.hpp>
#include <llarp/util/logging.hpp>
#include <llarp/util/str.hpp>
#include <llarp/util/types.hpp>
#include <charconv>
#include <optional>
@ -13,6 +12,8 @@
#include <string_view>
#include <system_error>
using namespace std::literals;
namespace llarp
{
namespace PREFIX

View File

@ -1683,7 +1683,7 @@ namespace llarp
config->load();
config->logging.level = log::Level::off;
config->api.enable_rpc_server = false;
config->network.endpoint_type = "embedded";
config->network.init_tun = false;
config->network.save_profiles = false;
config->bootstrap.files.clear();
return config;

View File

@ -108,13 +108,12 @@ namespace llarp
std::optional<fs::path> keyfile;
std::string endpoint_type{"tun"};
std::optional<int> hops;
std::optional<int> paths;
bool allow_exit = false;
bool is_reachable = false;
bool allow_exit{false};
bool is_reachable{false};
bool init_tun{true};
std::set<RouterID> snode_blacklist;

View File

@ -1,6 +1,5 @@
#pragma once
#include <llarp/util/time.hpp>
#include <llarp/util/types.hpp>
#include <cstdlib>

View File

@ -1,7 +1,6 @@
#pragma once
#include <llarp/util/time.hpp>
#include <llarp/util/types.hpp>
#include <chrono>
#include <cstddef>

View File

@ -19,6 +19,8 @@
#include <crypt.h>
#endif
using namespace std::literals;
namespace llarp
{
static auto logcat = log::Cat("crypto");

View File

@ -4,7 +4,6 @@
#include <llarp/util/aligned.hpp>
#include <llarp/util/fs.hpp>
#include <llarp/util/types.hpp>
#include <algorithm>
#include <iostream>

View File

@ -3,8 +3,6 @@
#include "kademlia.hpp"
#include "key.hpp"
#include <llarp/util/types.hpp>
#include <map>
#include <set>
#include <vector>
@ -22,9 +20,9 @@ namespace llarp::dht
Bucket(const Key_t& us, Random_t r) : nodes(XorMetric(us)), random(std::move(r))
{}
StatusObject ExtractStatus() const
nlohmann::json ExtractStatus() const
{
StatusObject obj{};
nlohmann::json obj{};
for (const auto& item : nodes)
{
obj[item.first.to_string()] = item.second.ExtractStatus();

View File

@ -25,7 +25,7 @@ namespace llarp::dht
Key_t() : AlignedBuffer<SIZE>()
{}
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
std::string to_string() const
{

View File

@ -22,7 +22,7 @@ namespace llarp::dht
RCNode(const RouterContact& other) : rc(other), ID(other.router_id())
{}
StatusObject ExtractStatus() const
nlohmann::json ExtractStatus() const
{
return rc.extract_status();
}
@ -49,7 +49,7 @@ namespace llarp::dht
ID = Key_t(introset.derived_signing_key.as_array());
}
StatusObject ExtractStatus() const
nlohmann::json ExtractStatus() const
{
return introset.ExtractStatus();
}

View File

@ -47,9 +47,9 @@ namespace llarp::dns
return true;
}
StatusObject MessageHeader::ToJSON() const
nlohmann::json MessageHeader::ToJSON() const
{
return StatusObject{};
return nlohmann::json{};
}
Message::Message(Message&& other)
@ -129,10 +129,10 @@ namespace llarp::dns
return true;
}
StatusObject Message::ToJSON() const
nlohmann::json Message::ToJSON() const
{
std::vector<StatusObject> ques;
std::vector<StatusObject> ans;
std::vector<nlohmann::json> ques;
std::vector<nlohmann::json> ans;
for (const auto& q : questions)
{
ques.push_back(q.ToJSON());
@ -141,7 +141,7 @@ namespace llarp::dns
{
ans.push_back(a.ToJSON());
}
return StatusObject{{"questions", ques}, {"answers", ans}};
return nlohmann::json{{"questions", ques}, {"answers", ans}};
}
std::vector<uint8_t> Message::to_buffer() const

View File

@ -33,7 +33,7 @@ namespace llarp
bool Decode(llarp_buffer_t* buf) override;
StatusObject ToJSON() const override;
nlohmann::json ToJSON() const override;
bool operator==(const MessageHeader& other) const
{
@ -50,7 +50,7 @@ namespace llarp
Message(Message&& other);
Message(const Message& other);
StatusObject ToJSON() const override;
nlohmann::json ToJSON() const override;
void add_nx_reply(RR_TTL_t ttl = 1);

View File

@ -51,9 +51,9 @@ namespace llarp::dns
return true;
}
StatusObject Question::ToJSON() const
nlohmann::json Question::ToJSON() const
{
return StatusObject{{"qname", qname}, {"qtype", qtype}, {"qclass", qclass}};
return nlohmann::json{{"qname", qname}, {"qtype", qtype}, {"qclass", qclass}};
}
bool Question::IsName(const std::string& other) const

View File

@ -52,6 +52,6 @@ namespace llarp::dns
/// determine if we are using this TLD
bool HasTLD(const std::string& tld) const;
StatusObject ToJSON() const override;
nlohmann::json ToJSON() const override;
};
} // namespace llarp::dns

View File

@ -73,9 +73,9 @@ namespace llarp::dns
return true;
}
StatusObject ResourceRecord::ToJSON() const
nlohmann::json ResourceRecord::ToJSON() const
{
return StatusObject{
return nlohmann::json{
{"name", rr_name},
{"type", rr_type},
{"class", rr_class},

View File

@ -27,7 +27,7 @@ namespace llarp::dns
bool Decode(llarp_buffer_t* buf) override;
StatusObject ToJSON() const override;
nlohmann::json ToJSON() const override;
std::string to_string() const;

View File

@ -1,7 +1,8 @@
#pragma once
#include <llarp/util/buffer.hpp>
#include <llarp/util/types.hpp>
#include <nlohmann/json.hpp>
#include <vector>
@ -19,7 +20,7 @@ namespace llarp::dns
virtual bool Decode(llarp_buffer_t* buf) = 0;
/// convert this whatever into json
virtual StatusObject ToJSON() const = 0;
virtual nlohmann::json ToJSON() const = 0;
};
bool EncodeRData(llarp_buffer_t* buf, const std::vector<uint8_t>& rdata);

View File

@ -1,7 +1,6 @@
#include "srv_data.hpp"
#include <llarp/util/str.hpp>
#include <llarp/util/types.hpp>
#include <oxenc/bt_serialize.h>
@ -163,9 +162,9 @@ namespace llarp::dns
return std::nullopt;
}
StatusObject SRVData::ExtractStatus() const
nlohmann::json SRVData::ExtractStatus() const
{
return StatusObject{
return nlohmann::json{
{"proto", service_proto}, {"priority", priority}, {"weight", weight}, {"port", port}, {"target", target}};
}
} // namespace llarp::dns

View File

@ -4,8 +4,6 @@
#include "name.hpp"
#include "serialize.hpp"
#include <llarp/util/types.hpp>
#include <string_view>
#include <tuple>
@ -89,7 +87,7 @@ namespace llarp::dns
bool bt_decode(std::string buf);
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
private:
bool bt_decode(oxenc::bt_dict_consumer& btdc);

View File

@ -50,7 +50,7 @@ namespace llarp
: _loop{std::make_shared<oxen::quic::Loop>(std::move(loop_ptr), thread_id)}
{}
bool EventLoop::add_network_interface(std::shared_ptr<vpn::NetworkInterface> netif, udp_pkt_hook handler)
bool EventLoop::add_network_interface(std::shared_ptr<vpn::NetworkInterface> netif, ip_pkt_hook handler)
{
(void)netif;
(void)handler;

View File

@ -59,7 +59,7 @@ namespace llarp
return _loop->in_event_loop();
}
bool add_network_interface(std::shared_ptr<vpn::NetworkInterface> netif, udp_pkt_hook handler);
bool add_network_interface(std::shared_ptr<vpn::NetworkInterface> netif, ip_pkt_hook handler);
template <typename Callable>
void call(Callable&& f)

View File

@ -37,19 +37,6 @@ namespace llarp
auto *handle = reinterpret_cast<TCPHandle *>(user_arg);
assert(handle);
auto bfd = bufferevent_getfd(bev);
if (auto maybe_str = handle->get_socket_stream(bfd))
{
log::trace(logcat, "TCP handle passing received data to corresponding stream!");
maybe_str->send(ustring_view{buf.data(), nwrite});
}
else
{
log::error(logcat, "TCP handle could not find corresponding stream to fd:{}", bfd);
handle->close_socket(bfd);
}
};
static void tcp_event_cb(struct bufferevent *bev, short what, void *user_arg)
@ -82,6 +69,8 @@ namespace llarp
auto *b = evconnlistener_get_base(listener);
auto *bevent = bufferevent_socket_new(b, fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE);
// TODO: make TCPSocket here!
bufferevent_setcb(bevent, tcp_read_cb, nullptr, tcp_event_cb, user_arg);
bufferevent_enable(bevent, EV_READ | EV_WRITE);
@ -100,7 +89,8 @@ namespace llarp
// DISCUSS: close everything here?
};
TCPSocket::TCPSocket(struct bufferevent *_bev, const oxen::quic::Address &_src) : bev{_bev}, src{_src}
TCPSocket::TCPSocket(struct bufferevent *_bev, evutil_socket_t _fd, const oxen::quic::Address &_src)
: bev{_bev}, fd{_fd}, src{_src}
{}
TCPSocket::~TCPSocket()
@ -109,33 +99,17 @@ namespace llarp
log::debug(logcat, "TCPSocket shut down!");
}
TCPHandle::TCPHandle(const std::shared_ptr<EventLoop> &ev_loop, const oxen::quic::Address &bind, rcv_data_hook cb)
: _ev{ev_loop}, _receive_cb{std::move(cb)}
TCPHandle::TCPHandle(const std::shared_ptr<EventLoop> &ev_loop, const oxen::quic::Address &bind, tcpsock_hook cb)
: _ev{ev_loop}, _socket_maker{std::move(cb)}
{
assert(_ev);
if (!_receive_cb)
if (!_socket_maker)
throw std::logic_error{"TCPSocket construction requires a non-empty receive callback"};
_init_internals(bind);
}
std::shared_ptr<oxen::quic::Stream> TCPHandle::get_socket_stream(evutil_socket_t fd)
{
if (auto itr = routing.find(fd); itr != routing.end())
{
if (auto str = itr->second->stream.lock())
return str;
}
return nullptr;
}
void TCPHandle::close_socket(evutil_socket_t fd)
{
routing.erase(fd);
}
void TCPHandle::_init_internals(const oxen::quic::Address &bind)
{
sockaddr_in _tcp{};

View File

@ -13,15 +13,23 @@ extern "C"
#include <event2/listener.h>
}
/** TODO:
- set up session_map in EndpointBase
- OutboundSession will create the path
- separate Socket and Handle from QUIC-like "ownership" model
- TCPHandle has a socket creation callback instead of a data callback
- Fire socket creation cb on accepting connection
- Socket creation cb could be given a created socket to set the callbacks on
- QUIC stream held by TCPSocket needs to have a receive data callback that writes to the TCP connection
*/
namespace llarp
{
class TCPHandle;
struct TCPSocket
{
TCPSocket() = delete;
TCPSocket(struct bufferevent* _bev, const oxen::quic::Address& _src);
TCPSocket(struct bufferevent* _bev, evutil_socket_t _fd, const oxen::quic::Address& _src);
/// Non-copyable and non-moveable
TCPSocket(const TCPSocket& s) = delete;
@ -32,11 +40,14 @@ namespace llarp
~TCPSocket();
struct bufferevent* bev;
evutil_socket_t fd;
oxen::quic::Address src;
std::weak_ptr<oxen::quic::Stream> stream;
};
using tcpsock_hook = std::function<std::shared_ptr<TCPSocket>()>;
class TCPHandle
{
public:
@ -49,7 +60,7 @@ namespace llarp
;
TCPHandle() = delete;
explicit TCPHandle(const std::shared_ptr<EventLoop>& ev, const oxen::quic::Address& bind, rcv_data_hook cb);
explicit TCPHandle(const std::shared_ptr<EventLoop>& ev, const oxen::quic::Address& bind, tcpsock_hook cb);
~TCPHandle();
@ -59,19 +70,13 @@ namespace llarp
socket_t _sock;
oxen::quic::Address _bound;
rcv_data_hook _receive_cb;
tcpsock_hook _socket_maker;
std::unordered_map<evutil_socket_t, std::shared_ptr<TCPSocket>> routing;
void _init_internals(const oxen::quic::Address& bind);
public:
// void map_buffer_socket(evutil_socket_t fd)
std::shared_ptr<oxen::quic::Stream> get_socket_stream(evutil_socket_t fd);
void close_socket(evutil_socket_t fd);
oxen::quic::Address bind()
{
return _bound;

View File

@ -12,7 +12,6 @@ namespace llarp
using udp_pkt_hook = std::function<void(UDPPacket&& pkt)>;
using ip_pkt_hook = std::function<void(IPPacket)>;
using rcv_data_hook = std::function<void(ustring)>;
using UDPSocket = oxen::quic::UDPSocket;

View File

@ -6,12 +6,12 @@ namespace llarp::handlers
{
static auto logcat = llarp::log::Cat("base_handler");
void BaseHandler::load_key_file(std::optional<fs::path> p, Router& r)
void BaseHandler::load_key_file(std::optional<fs::path> p)
{
try
{
if (p.has_value())
_identity.ensure_keys(*p, r.key_manager()->needs_backup());
_identity.ensure_keys(*p, _router.key_manager()->needs_backup());
else
_identity.regenerate_keys();
}

View File

@ -17,7 +17,6 @@
namespace llarp::handlers
{
inline constexpr auto LOKI_RESOLVER = "lokinet"sv;
/** This class holds methods common to handlers::{Tun,Null}Endpoints in regards to their packet
routing and other API capabilities.
@ -59,7 +58,7 @@ namespace llarp::handlers
virtual bool setup_networking() = 0;
virtual void load_key_file(std::optional<fs::path> p, Router& r);
virtual void load_key_file(std::optional<fs::path> p);
virtual vpn::EgresPacketRouter* egres_packet_router()
{

View File

@ -19,7 +19,7 @@ namespace llarp::handlers
bool _is_snode_service{false}; // TODO:
bool _is_v4;
std::string _name{"LocalEndpoint"};
const std::string _name{"LocalEndpoint"};
std::string _if_name;
oxen::quic::Address _local_addr;

View File

@ -23,10 +23,9 @@ namespace llarp
public std::enable_shared_from_this<RemoteHandler>
{
private:
std::string _name{"RemoteHandler"};
const std::string _name{"RemoteHandler"};
address_map<oxen::quic::Address, NetworkAddress> _address_map;
address_map<IPRange, NetworkAddress> _range_map;
IPRange _local_range;

View File

@ -128,8 +128,24 @@ namespace llarp::handlers
}
};
TunEndpoint::TunEndpoint(Router& r) : BaseHandler{r}, _packet_router{}
TunEndpoint::TunEndpoint(Router& r) : _router{r}, _packet_router{}
{
auto& keyfile = _router.config()->network.keyfile;
try
{
if (keyfile.has_value())
_identity.ensure_keys(*keyfile, _router.key_manager()->needs_backup());
else
_identity.regenerate_keys();
}
catch (const std::exception& e)
{
auto err = "API endpoint keyfile failed to load: {}"_format(e.what());
log::error(logcat, "{}", err);
throw std::runtime_error{err};
}
_packet_router =
std::make_shared<vpn::PacketRouter>([this](IPPacket pkt) { handle_user_packet(std::move(pkt)); });
@ -138,8 +154,10 @@ namespace llarp::handlers
void TunEndpoint::setup_dns()
{
log::info(logcat, "{} setting up DNS...", name());
auto& dns_config = _router.config()->dns;
const auto& info = get_vpn_interface()->Info();
const auto& info = get_vpn_interface()->interface_info();
if (dns_config.raw)
{
@ -165,14 +183,14 @@ namespace llarp::handlers
});
}
else
_dns = std::make_shared<dns::Server>(router().loop(), dns_config, info.index);
_dns = std::make_shared<dns::Server>(_router.loop(), dns_config, info.index);
_dns->add_resolver(weak_from_this());
_dns->start();
if (dns_config.raw)
{
if (auto vpn = router().vpn_platform())
if (auto vpn = _router.vpn_platform())
{
// get the first local address we know of
std::optional<oxen::quic::Address> localaddr;
@ -207,7 +225,7 @@ namespace llarp::handlers
}
}
StatusObject TunEndpoint::ExtractStatus() const
nlohmann::json TunEndpoint::ExtractStatus() const
{
// auto obj = service::Endpoint::ExtractStatus();
// obj["ifaddr"] = m_OurRange.to_string();
@ -227,10 +245,10 @@ namespace llarp::handlers
// if (not m_DnsConfig.bind_addr.empty())
// obj["localResolver"] = localRes[0];
// StatusObject ips{};
// nlohmann::json ips{};
// for (const auto& item : m_IPActivity)
// {
// StatusObject ipObj{{"lastActive", to_json(item.second)}};
// nlohmann::json ipObj{{"lastActive", to_json(item.second)}};
// std::string remoteStr;
// AlignedBuffer<32> addr = m_IPToAddr.at(item.first);
// if (m_SNodes.at(addr))
@ -261,7 +279,7 @@ namespace llarp::handlers
}
}
bool TunEndpoint::configure()
void TunEndpoint::configure()
{
auto& net_conf = _router.config()->network;
@ -292,7 +310,6 @@ namespace llarp::handlers
}
_traffic_policy = net_conf.traffic_policy;
_base_ipv6_range = net_conf._base_ipv6_range;
if (net_conf.path_alignment_timeout)
@ -308,7 +325,7 @@ namespace llarp::handlers
_local_addr = *net_conf._local_addr;
_local_ip = *net_conf._local_ip;
_use_v6 = not _local_range.is_ipv4();
_is_ipv6 = not _local_range.is_ipv4();
_persisting_addr_file = net_conf.addr_map_persist_file;
@ -320,6 +337,50 @@ namespace llarp::handlers
}
}
_local_netaddr = NetworkAddress::from_pubkey(_router.pubkey(), not _router.is_service_node());
local_ip_mapping.insert_or_assign(get_if_addr(), _local_netaddr);
vpn::InterfaceInfo info;
info.addrs.emplace_back(_local_range);
if (_base_ipv6_range)
{
log::info(logcat, "{} using ipv6 range:{}", name(), *_base_ipv6_range);
info.addrs.emplace_back(*_base_ipv6_range);
}
info.ifname = _if_name;
log::info(logcat, "{} setting up network...", name());
_net_if = router().vpn_platform()->CreateInterface(std::move(info), &_router);
_if_name = _net_if->interface_info().ifname;
log::info(logcat, "{} got network interface:{}", name(), _if_name);
auto handle_packet = [netif = _net_if, pktrouter = _packet_router](IPPacket pkt) {
// TODO: packets used to have reply hooks
// pkt.reply = [netif](auto pkt) { netif->WritePacket(std::move(pkt)); };
pktrouter->handle_ip_packet(std::move(pkt));
};
if (not router().loop()->add_network_interface(_net_if, std::move(handle_packet)))
{
auto err = "{} failed to add network interface!"_format(name());
log::error(logcat, "{}", err);
throw std::runtime_error{std::move(err)};
}
_local_ipv6 = _is_ipv6 ? _local_addr : _local_addr.mapped_ipv4_as_ipv6();
if constexpr (not llarp::platform::is_apple)
{
if (auto maybe = router().net().get_interface_ipv6_addr(_if_name))
{
_local_ipv6 = *maybe;
log::info(logcat, "{} has ipv6 address:{}", name(), _local_ipv6);
}
}
// if (auto* quic = GetQUICTunnel())
// {
// TODO:
@ -327,7 +388,9 @@ namespace llarp::handlers
// return llarp::SockAddr{net::TruncateV6(GetIfAddr()), huint16_t{port}};
// });
// }
return true;
setup_dns();
assert(has_mapped_address(_local_netaddr));
}
static bool is_random_snode(const dns::Message& msg)
@ -733,7 +796,7 @@ namespace llarp::handlers
bool TunEndpoint::supports_ipv6() const
{
return _use_v6;
return _is_ipv6;
}
// FIXME: pass in which question it should be addressing
@ -775,11 +838,6 @@ namespace llarp::handlers
#endif
}
bool TunEndpoint::start()
{
return setup_networking();
}
bool TunEndpoint::is_service_node() const
{
return _router.is_service_node();
@ -790,94 +848,6 @@ namespace llarp::handlers
return _router.is_exit_node();
}
bool TunEndpoint::setup_tun()
{
_next_ip = _local_ip;
// _max_ip = _local_range.HighestAddr();
log::info(logcat, "{} set {} to have address {}", name(), _if_name, _local_addr);
// log::info(logcat, "{} allocated up to {} on range {}", name(), _max_ip, _local_range);
auto& local_netaddr = _identity.pub.address();
local_ip_mapping.insert_or_assign(get_if_addr(), local_netaddr);
vpn::InterfaceInfo info;
info.addrs.emplace_back(_local_range);
if (_base_ipv6_range)
{
log::info(logcat, "{} using ipv6 range:{}", name(), *_base_ipv6_range);
info.addrs.emplace_back(*_base_ipv6_range);
}
info.ifname = _if_name;
log::info(logcat, "{} setting up network...", name());
try
{
_net_if = router().vpn_platform()->CreateInterface(std::move(info), &_router);
}
catch (std::exception& ex)
{
log::error(logcat, "{} failed to set up network interface: ", name(), ex.what());
return false;
}
_if_name = _net_if->Info().ifname;
log::info(logcat, "{} got network interface:{}", name(), _if_name);
auto handle_packet = [netif = _net_if, pktrouter = _packet_router](UDPPacket pkt) {
// TODO: packets used to have reply hooks
// pkt.reply = [netif](auto pkt) { netif->WritePacket(std::move(pkt)); };
pktrouter->handle_ip_packet(std::move(pkt));
};
if (not router().loop()->add_network_interface(_net_if, std::move(handle_packet)))
{
log::error(logcat, "{} failed to add network interface!", name());
return false;
}
_local_ipv6 = _local_range.is_ipv4()
? oxen::quic::Address{_local_range.address().to_ipv6(), _local_range.address().port()}
: _local_range.address();
if constexpr (not llarp::platform::is_apple)
{
if (auto maybe = router().net().get_interface_ipv6_addr(_if_name))
{
_local_ipv6 = *maybe;
log::info(logcat, "{} has ipv6 address:{}", name(), _local_ipv6);
}
}
log::info(logcat, "{} setting up DNS...", name());
setup_dns();
// loop()->call_soon([this]() { router().route_poker()->set_dns_mode(false); });
return has_mapped_address(local_netaddr);
}
// std::unordered_map<std::string, std::string>
// TunEndpoint::NotifyParams() const
// {
// auto env = Endpoint::NotifyParams();
// env.emplace("IP_ADDR", m_OurIP.to_string());
// env.emplace("IF_ADDR", m_OurRange.to_string());
// env.emplace("IF_NAME", m_IfName);
// std::string strictConnect;
// for (const auto& addr : m_StrictConnectAddrs)
// strictConnect += addr.to_string() + " ";
// env.emplace("STRICT_CONNECT_ADDRS", strictConnect);
// return env;
// }
bool TunEndpoint::setup_networking()
{
log::info(logcat, "Set Up networking for {}", name());
return setup_tun();
}
bool TunEndpoint::stop()
{
// stop vpn tunnel

View File

@ -17,38 +17,22 @@
#include <type_traits>
#include <variant>
/**
DISCUSS:
- Q: Where should {Tun,Null}Endpoint live in the heirarchy?
- Q: Does it make more sense for {Tun,Null}Endpoint to bypass service::Handler and directly
inherit from PathBuilder?
- A: Likely. The addition of handlers::RemoteHandler to the inheritance of service::Handler
and exit::Handler strengthen this argument, as those functionalities are not necessary for
{Tun,Null}Endpoint.
- Q: Is EndpointBase necessary? Or is session management outside the scope of {Tun,Null}Endpoint
- A: Likely not. But will leave it in at the moment. The previous implementation brought in
EndpointBase via service::Endpoint, and it seems reachability in regards to introsets may
be important.
*/
namespace llarp::handlers
{
inline const auto TUN = "tun"s;
inline constexpr auto TUN = "tun"sv;
inline constexpr auto LOKI_RESOLVER = "lokinet"sv;
struct TunEndpoint final : public dns::Resolver_Base,
public BaseHandler,
public std::enable_shared_from_this<TunEndpoint>
struct TunEndpoint : public dns::Resolver_Base, public std::enable_shared_from_this<TunEndpoint>
{
TunEndpoint(Router& r);
~TunEndpoint() override;
vpn::NetworkInterface* get_vpn_interface() override
vpn::NetworkInterface* get_vpn_interface()
{
return _net_if.get();
}
std::string name() const override
std::string_view name() const
{
return TUN;
}
@ -72,16 +56,13 @@ namespace llarp::handlers
// Reconfigures DNS servers and restarts libunbound with the new servers.
void reconfigure_dns(std::vector<oxen::quic::Address> servers);
bool configure() override;
void configure();
std::string get_if_name() const override;
std::string get_if_name() const;
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
// std::unordered_map<std::string, std::string>
// NotifyParams() const override;
bool supports_ipv6() const override;
bool supports_ipv6() const;
bool should_hook_dns_message(const dns::Message& msg) const;
@ -89,31 +70,21 @@ namespace llarp::handlers
void tick_tun(std::chrono::milliseconds now);
bool start();
bool stop();
bool is_service_node() const;
bool is_exit_node() const;
/// set up tun interface, blocking
bool setup_tun();
void setup_dns();
/// overrides Endpoint
// std::shared_ptr<dns::Server> DNS() const override
// {
// return _dns;
// };
/// overrides BaseHandler
bool setup_networking() override;
/// overrides BaseHandler
bool handle_inbound_packet(
const service::SessionTag tag, const llarp_buffer_t& pkt, service::ProtocolType t, uint64_t seqno) override;
const service::SessionTag tag, const llarp_buffer_t& pkt, service::ProtocolType t, uint64_t seqno);
/// handle inbound traffic
bool handle_write_ip_packet(const llarp_buffer_t& buf, huint128_t src, huint128_t dst, uint64_t seqno);
@ -147,6 +118,16 @@ namespace llarp::handlers
bool has_mapped_address(const NetworkAddress& addr) const;
const Router& router() const
{
return _router;
}
Router& router()
{
return _router;
}
protected:
struct WritePacket
{
@ -181,6 +162,8 @@ namespace llarp::handlers
reply(*query);
}
Router& _router;
/// dns subsystem for this endpoint
std::shared_ptr<dns::Server> _dns;
@ -188,6 +171,9 @@ namespace llarp::handlers
oxen::quic::Address _local_addr;
ip _local_ip;
/// Our local Network Address holding our network pubkey
NetworkAddress _local_netaddr;
/// our network interface's ipv6 address
oxen::quic::Address _local_ipv6;
@ -202,7 +188,7 @@ namespace llarp::handlers
/// list of strict connect addresses for hooks
// std::vector<IpAddress> _strict_connect_addrs;
/// use v6?
bool _use_v6;
bool _is_ipv6;
std::string _if_name;
std::optional<IPRange> _base_ipv6_range = std::nullopt;
@ -219,6 +205,8 @@ namespace llarp::handlers
/// a file to load / store the ephemeral address map to
std::optional<fs::path> _persisting_addr_file = std::nullopt;
service::Identity _identity;
/// for raw packet dns
std::shared_ptr<vpn::I_Packet_IO> _raw_DNS;
};

View File

@ -34,9 +34,9 @@ namespace llarp
return enc;
}
StatusObject Contacts::ExtractStatus() const
nlohmann::json Contacts::ExtractStatus() const
{
StatusObject obj{{"services", _introset_nodes->ExtractStatus()}, {"local_key", _local_key.ToHex()}};
nlohmann::json obj{{"services", _introset_nodes->ExtractStatus()}, {"local_key", _local_key.ToHex()}};
return obj;
}

View File

@ -29,7 +29,7 @@ namespace llarp
std::optional<service::EncryptedIntroSet> get_encrypted_introset(const dht::Key_t& key) const;
// TODO: rename every ExtractStatus function to be uniformly snake cased
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
void put_intro(service::EncryptedIntroSet enc);

View File

@ -481,7 +481,7 @@ namespace llarp
return true;
}
log::critical(logcat, "Queueing message to ");
log::critical(logcat, "Queueing message to {}", remote);
_router.loop()->call(
[this, remote, endpoint = std::move(endpoint), body = std::move(body), f = std::move(func)]() {
@ -632,7 +632,7 @@ namespace llarp
}
// TODO: this
StatusObject LinkManager::extract_status() const
nlohmann::json LinkManager::extract_status() const
{
return {};
}

View File

@ -243,7 +243,7 @@ namespace llarp
void check_persisting_conns(std::chrono::milliseconds now);
StatusObject extract_status() const;
nlohmann::json extract_status() const;
void for_each_connection(std::function<void(link::Connection&)> func);

View File

@ -1,674 +1 @@
#include "tunnel.hpp"
#include <llarp/service/name.hpp>
#include <llarp/service/tag.hpp>
#include <llarp/util/logging.hpp>
#include <llarp/util/logging/buffer.hpp>
#include <llarp/util/str.hpp>
#include <limits>
#include <memory>
#include <stdexcept>
#include <type_traits>
namespace llarp::quic
{
static auto logcat = log::Cat("tun");
namespace
{
// Takes data from the tcp connection and pushes it down the quic tunnel
void on_outgoing_data(uvw::DataEvent& event, uvw::TCPHandle& client)
{
auto stream = client.data<Stream>();
assert(stream);
std::string_view data{event.data.get(), event.length};
auto peer = client.peer();
log::trace(logcat, "{}:{} -> lokinet {}", peer.ip, peer.port, buffer_printer{data});
// Steal the buffer from the DataEvent's unique_ptr<char[]>:
stream->append_buffer(reinterpret_cast<const std::byte*>(event.data.release()), event.length);
if (stream->used() >= tunnel::PAUSE_SIZE)
{
log::debug(
logcat,
"Quic tunnel is congested ({} bytes in flight); pausing local TCP connection reads",
stream->used());
client.stop();
stream->when_available([](Stream& s) {
auto client = s.data<uvw::TCPHandle>();
if (s.used() < tunnel::PAUSE_SIZE)
{
log::debug(logcat, "Quic tunnel is no longer congested; resuming TCP connection reading!");
client->read();
return true;
}
return false;
});
}
else
{
log::debug(logcat, "Queued {} bytes", event.length);
}
}
// Received data from the quic tunnel and sends it to the TCP connection
void on_incoming_data(Stream& stream, bstring_view bdata)
{
auto tcp = stream.data<uvw::TCPHandle>();
if (!tcp)
return; // TCP connection is gone, which would have already sent a stream close, so
// just drop it.
std::string_view data{reinterpret_cast<const char*>(bdata.data()), bdata.size()};
auto peer = tcp->peer();
log::trace(logcat, "{}:{} -> lokinet {}", peer.ip, peer.port, buffer_printer{data});
if (data.empty())
return;
// Try first to write immediately from the existing buffer to avoid needing an
// allocation and copy:
auto written = tcp->tryWrite(const_cast<char*>(data.data()), data.size());
if (written < (int)data.size())
{
data.remove_prefix(written);
auto wdata = std::make_unique<char[]>(data.size());
std::copy(data.begin(), data.end(), wdata.get());
tcp->write(std::move(wdata), data.size());
}
}
void close_tcp_pair(quic::Stream& st, std::optional<uint64_t> /*errcode*/)
{
if (auto tcp = st.data<uvw::TCPHandle>())
{
log::trace(logcat, "Closing TCP connection...");
tcp->close();
}
};
// Creates a new tcp handle that forwards incoming data/errors/closes into appropriate
// actions on the given quic stream.
void install_stream_forwarding(uvw::TCPHandle& tcp, Stream& stream)
{
tcp.data(stream.shared_from_this());
stream.weak_data(tcp.weak_from_this());
tcp.clear(); // Clear any existing initial event handlers
tcp.on<uvw::CloseEvent>([](auto&, uvw::TCPHandle& c) {
// This fires sometime after we call `close()` to signal that the close is done.
if (auto stream = c.data<Stream>())
{
log::info(
logcat, "Local TCP connection closed, closing associated quic stream id:{}", stream->id());
stream->close();
stream->data(nullptr);
}
c.data(nullptr);
});
tcp.on<uvw::EndEvent>([](auto&, uvw::TCPHandle& c) {
// This fires on eof, most likely because the other side of the TCP connection
// closed it.
log::info(logcat, "EOF on connection to {}:{}", c.peer().ip, c.peer().port);
c.close();
});
tcp.on<uvw::ErrorEvent>([](const uvw::ErrorEvent& e, uvw::TCPHandle& tcp) {
log::error(
logcat,
"Error: [{}:{}] on connection with {}:{}; shutting down quic stream",
e.name(),
e.what(),
tcp.peer().ip,
tcp.peer().port);
if (auto stream = tcp.data<Stream>())
{
stream->close(tunnel::ERROR_TCP);
stream->data(nullptr);
tcp.data(nullptr);
}
// tcp.closeReset();
});
tcp.on<uvw::DataEvent>(on_outgoing_data);
stream.data_callback = on_incoming_data;
stream.close_callback = close_tcp_pair;
}
// This initial data handler is responsible for pulling off the initial stream data that
// comes back, confirming that the tunnel is opened on the other end. Currently this is a
// null byte (CONNECT_INIT) but in the future we might encode additional data here (and, if
// that happens, we want this older implementation to fail).
//
// If the initial byte checks out we replace this handler with the regular stream handler
// (and forward the rest of the data to it if we got more than just the single byte).
void initial_client_data_handler(uvw::TCPHandle& client, Stream& stream, bstring_view bdata)
{
log::trace(logcat, "Initial client handler -- data: {}", buffer_printer{bdata});
if (bdata.empty())
return;
client.clear(); // Clear these initial event handlers: we either set up the proper
// ones, or close
if (auto b0 = bdata[0]; b0 == tunnel::CONNECT_INIT)
{
// Set up callbacks, which replaces both of these initial callbacks
client.read(); // Unfreeze (we stop() before putting into pending)
install_stream_forwarding(client, stream);
if (bdata.size() > 1)
{
bdata.remove_prefix(1);
stream.data_callback(stream, std::move(bdata));
}
log::trace(logcat, "Starting client read...");
}
else
{
log::warning(
logcat,
"Remote connection returned invalid initial byte (0x{}); dropping connection",
oxenc::to_hex(bdata.begin(), bdata.begin() + 1));
stream.close(tunnel::ERROR_BAD_INIT);
client.close();
}
stream.io_ready();
}
// Initial close handler that gets replaced as soon as we receive a valid byte (in the above
// handler). If this gets called then it means the quic remote quic end closed before we
// established the end-to-end tunnel (for example because the remote's tunnel connection
// failed):
void initial_client_close_handler(
uvw::TCPHandle& client, Stream& /*stream*/, std::optional<uint64_t> error_code)
{
if (error_code && *error_code == tunnel::ERROR_CONNECT)
log::debug(logcat, "Remote TCP connection failed, closing local connection");
else
log::warning(
logcat,
"Stream connection closed {}; closing local TCP connection.",
error_code ? "with error " + std::to_string(*error_code) : "gracefully");
auto peer = client.peer();
log::debug(logcat, "Closing connection to {}:{}", peer.ip, peer.port);
client.clear();
// TOFIX: this logic
// if (error_code)
// client.close();
// else
client.close();
}
} // namespace
TunnelManager::TunnelManager(EndpointBase& se) : service_endpoint_{se}
{
// Cleanup callback to clear out closed tunnel connections
service_endpoint_.Loop()->call_every(500ms, timer_keepalive_, [this] {
log::trace(logcat, "Checking quic tunnels for finished connections...");
for (auto ctit = client_tunnels_.begin(); ctit != client_tunnels_.end();)
{
// Clear any accepted connections that have been closed:
auto& [port, ct] = *ctit;
for (auto it = ct.conns.begin(); it != ct.conns.end();)
{
// TCP connections keep a shared_ptr to their quic::Stream while open and clear
// it when closed. (We don't want to use `.active()` here because we do
// deliberately temporarily stop the TCP connection when the quic side gets
// congested.
if (not *it or not(*it)->data())
{
log::debug(logcat, "Cleanup up closed outgoing tunnel on quic port: {}", port);
it = ct.conns.erase(it);
}
else
++it;
}
// If there are not accepted connections left *and* we stopped listening for new
// ones then destroy the whole thing.
if (ct.conns.empty() and (not ct.tcp or not ct.tcp->active()))
{
log::debug(logcat, "All sockets closed on quic port:{},destroying tunnel data", port);
ctit = client_tunnels_.erase(ctit);
}
else
++ctit;
}
log::trace(logcat, "Finished quic tunnel cleanup!");
});
}
void TunnelManager::make_server()
{
// auto loop = get_loop();
server_ = std::make_unique<Server>(service_endpoint_);
server_->stream_open_callback = [this](Stream& stream, uint16_t port) -> bool {
stream.close_callback = close_tcp_pair;
auto& conn = stream.get_connection();
auto remote = service_endpoint_.GetEndpointWithConvoTag(conn.path.remote);
if (!remote)
{
log::warning(logcat, "Received new stream open from invalid/unknown convo tag, dropping stream");
return false;
}
auto lokinet_addr = var::visit([](auto&& remote) { return remote.to_string(); }, *remote);
auto tunnel_to = allow_connection(lokinet_addr, port);
if (not tunnel_to)
return false;
log::info(logcat, "Quic stream from {} to port:{} tunnelling to {}", lokinet_addr, port, *tunnel_to);
auto tcp = get_loop()->resource<uvw::TCPHandle>();
[[maybe_unused]] auto error_handler =
tcp->once<uvw::ErrorEvent>([&stream, to = *tunnel_to](const uvw::ErrorEvent&, uvw::TCPHandle&) {
log::warning(logcat, "Failed to connect to {}; shutting down quic stream", to);
stream.close(tunnel::ERROR_CONNECT);
});
// As soon as we connect to the local tcp tunnel port we fire a CONNECT_INIT down the
// stream tunnel to let the other end know the connection was successful, then set up
// regular stream handling to handle any other to/from data.
tcp->once<uvw::ConnectEvent>([streamw = stream.weak_from_this()](
const uvw::ConnectEvent&, uvw::TCPHandle& tcp) {
auto peer = tcp.peer();
auto stream = streamw.lock();
if (!stream)
{
log::warning(
logcat,
"Connected to TCP {}:{} but quic stream has gone away; close/resetting local TCP connection",
peer.ip,
peer.port);
tcp.close();
return;
}
log::debug(logcat, "Connected to {}:{} for quic stream ID:{}", peer.ip, peer.port, stream->id());
// Set up the data stream forwarding (which also clears these initial handlers).
install_stream_forwarding(tcp, *stream);
assert(stream->used() == 0);
// Send the magic byte, and start reading from the tcp tunnel in the logic
// thread
stream->append_buffer(new std::byte[1]{tunnel::CONNECT_INIT}, 1);
tcp.read();
});
tcp->connect(*tunnel_to->operator const sockaddr*());
return true;
};
}
int TunnelManager::listen(ListenHandler handler)
{
if (!handler)
throw std::logic_error{"Cannot call listen() with a null handler"};
assert(service_endpoint_.Loop()->inEventLoop());
if (not server_)
make_server();
int id = next_handler_id_++;
incoming_handlers_.emplace_hint(incoming_handlers_.end(), id, std::move(handler));
return id;
}
int TunnelManager::listen(SockAddr addr)
{
return listen([addr](std::string_view, uint16_t p) -> std::optional<SockAddr> {
log::info(logcat, "try accepting {}", addr.getPort());
if (p == addr.getPort())
return addr;
return std::nullopt;
});
}
void TunnelManager::forget(int id)
{
incoming_handlers_.erase(id);
}
std::optional<SockAddr> TunnelManager::allow_connection(std::string_view lokinet_addr, uint16_t port)
{
for (auto& [id, handler] : incoming_handlers_)
{
try
{
if (auto addr = handler(lokinet_addr, port))
return addr;
}
catch (const std::exception& e)
{
log::warning(
logcat, "Incoming Quic connection from {} to port:{} denied:{}", lokinet_addr, port, e.what());
return std::nullopt;
}
}
log::warning(logcat, "Incoming Quic connection from {} to port:{} denied by all handlers!", lokinet_addr, port);
return std::nullopt;
}
std::shared_ptr<uvw::Loop> TunnelManager::get_loop()
{
if (auto loop = service_endpoint_.Loop()->MaybeGetUVWLoop())
return loop;
throw std::logic_error{"TunnelManager requires a libuv-based event loop"};
}
// Finds the first unused key in `map`, starting at `start` and wrapping back to 0 if we hit the
// end. Requires an unsigned int type for the key. Requires nullopt if the map is completely
// full, otherwise returns the free key.
template <typename K, typename V, typename = std::enable_if_t<std::is_integral_v<K> && std::is_unsigned_v<K>>>
static std::optional<K> find_unused_key(std::map<K, V>& map, K start)
{
if (map.size() == std::numeric_limits<K>::max())
return std::nullopt; // The map is completely full
[[maybe_unused]] bool from_zero = (start == K{0});
// Start at the first key >= start, then walk 1-by-1 (incrementing start) until we find a
// strictly > key, which means we've found a hole we can use
auto it = map.lower_bound(start);
if (it == map.end())
return start;
for (; it != map.end(); ++it, ++start)
if (it->first != start)
return start;
if (start != 0) // `start` didn't wrap which means we found an empty slot
return start;
assert(!from_zero); // There *must* be a free slot somewhere in [0, max] (otherwise the map
// would be completely full and we'd have returned nullopt).
return find_unused_key(map, K{0});
}
// Wrap common tasks and cleanup that we need to do from multiple places while establishing a
// tunnel
bool TunnelManager::continue_connecting(
uint16_t pseudo_port, bool step_success, std::string_view step_name, std::string_view addr)
{
assert(service_endpoint_.Loop()->inEventLoop());
auto it = client_tunnels_.find(pseudo_port);
if (it == client_tunnels_.end())
{
log::debug(logcat, "Quic tunnel to {} closed before step (name:{}) finished!", addr, step_name);
return false;
}
if (!step_success)
{
log::warning(logcat, "Quic tunnel to {} failed during step (name:{}); aborting tunnel!", addr, step_name);
it->second.tcp->close();
if (it->second.open_cb)
it->second.open_cb(false);
client_tunnels_.erase(it);
}
return step_success;
}
std::pair<SockAddr, uint16_t> TunnelManager::open(
std::string_view remote_address, uint16_t port, OpenCallback on_open, SockAddr bind_addr)
{
std::string remote_addr = lowercase_ascii_string(std::string{remote_address});
std::pair<SockAddr, uint16_t> result;
auto& [saddr, pport] = result;
auto maybe_remote = service::ParseAddress(remote_addr);
if (!maybe_remote)
{
if (not service::NameIsValid(remote_addr))
throw std::invalid_argument{"Invalid remote lokinet name/address"};
// Otherwise it's a valid ONS name, so we'll initiate an ONS lookup below
}
// Open the TCP tunnel right away; it will just block new incoming connections until the
// quic connection is established, but this still allows the caller to connect right away
// and queue an initial request (rather than having to wait via a callback before
// connecting). It also makes sure we can actually listen on the given address before we go
// ahead with establishing the quic connection.
auto tcp_tunnel = get_loop()->resource<uvw::TCPHandle>();
const char* failed = nullptr;
auto err_handler = tcp_tunnel->once<uvw::ErrorEvent>([&failed](auto& evt, auto&) { failed = evt.what(); });
tcp_tunnel->bind(*bind_addr.operator const sockaddr*());
tcp_tunnel->on<uvw::ListenEvent>([this](const uvw::ListenEvent&, uvw::TCPHandle& tcp_tunnel) {
auto client = tcp_tunnel.loop().resource<uvw::TCPHandle>();
tcp_tunnel.accept(*client);
// Freeze the connection (after accepting) because we may need to stall it until a
// stream becomes available; flush_pending_incoming will unfreeze it.
client->stop();
auto pport = tcp_tunnel.data<uint16_t>();
if (pport)
{
if (auto it = client_tunnels_.find(*pport); it != client_tunnels_.end())
{
it->second.pending_incoming.emplace(std::move(client));
flush_pending_incoming(it->second);
return;
}
tcp_tunnel.data(nullptr);
}
client->close();
});
tcp_tunnel->listen();
tcp_tunnel->erase(err_handler);
if (failed)
{
tcp_tunnel->close();
throw std::runtime_error{
fmt::format("Failed to bind/listen local TCP tunnel socket on {}: {}", bind_addr, failed)};
}
auto bound = tcp_tunnel->sock();
saddr = SockAddr{bound.ip, huint16_t{static_cast<uint16_t>(bound.port)}};
// Find the first unused psuedo-port value starting from next_pseudo_port_.
if (auto p = find_unused_key(client_tunnels_, next_pseudo_port_))
pport = *p;
else
throw std::runtime_error{"Unable to open an outgoing quic connection: too many existing connections"};
(next_pseudo_port_ = pport)++;
log::info(logcat, "Bound TCP tunnel {} for quic client pseudo_port:{}", saddr, pport);
// We are emplacing into client_tunnels_ here: beyond this point we must not throw until we
// return (or if we do, make sure we remove this row from client_tunnels_ first).
assert(client_tunnels_.count(pport) == 0);
auto& ct = client_tunnels_[pport];
ct.open_cb = std::move(on_open);
ct.tcp = std::move(tcp_tunnel);
// We use this pport shared_ptr value on the listening tcp socket both to hand to pport into
// the accept handler, and to let the accept handler know that `this` is still safe to use.
ct.tcp->data(std::make_shared<uint16_t>(pport));
auto after_path = [this, port, pport = pport, remote_addr](auto maybe_convo) {
if (not continue_connecting(pport, (bool)maybe_convo, "path build", remote_addr))
return;
SockAddr dest{maybe_convo->ToV6()};
dest.setPort(port);
make_client(dest, *client_tunnels_.find(pport));
};
if (!maybe_remote)
{
// We were given an ONS address, so it's a two-step process: first we resolve the ONS
// name, then we have to build a path to that address.
service_endpoint_.LookupNameAsync(
remote_addr,
[this, after_path = std::move(after_path), pport = pport, remote_addr = std::move(remote_addr)](
auto maybe_remote) {
if (not continue_connecting(pport, (bool)maybe_remote, "endpoint ONS lookup", remote_addr))
return;
service_endpoint_.MarkAddressOutbound(*maybe_remote);
service_endpoint_.EnsurePathTo(*maybe_remote, after_path, open_timeout);
});
return result;
}
auto& remote = *maybe_remote;
// See if we have an existing convo tag we can use to start things immediately
if (auto maybe_convo = service_endpoint_.GetBestConvoTagFor(remote))
after_path(maybe_convo);
else
{
service_endpoint_.MarkAddressOutbound(remote);
service_endpoint_.EnsurePathTo(remote, after_path, open_timeout);
}
return result;
}
void TunnelManager::close(int id)
{
if (auto it = client_tunnels_.find(id); it != client_tunnels_.end())
{
it->second.tcp->close();
it->second.tcp->data(nullptr);
it->second.tcp.reset();
}
}
TunnelManager::ClientTunnel::~ClientTunnel()
{
if (tcp)
{
tcp->close();
tcp->data(nullptr);
tcp.reset();
}
for (auto& conn : conns)
conn->close();
conns.clear();
while (not pending_incoming.empty())
{
if (auto tcp = pending_incoming.front().lock())
{
tcp->clear();
tcp->close();
}
pending_incoming.pop();
}
}
void TunnelManager::make_client(const SockAddr& remote, std::pair<const uint16_t, ClientTunnel>& row)
{
assert(remote.getPort() > 0);
auto& [pport, tunnel] = row;
assert(not tunnel.client);
tunnel.client = std::make_unique<Client>(service_endpoint_, remote, pport);
auto conn = tunnel.client->get_connection();
conn->on_stream_available = [this, id = row.first](Connection&) {
log::debug(logcat, "Quic connection (id:{}) established; streams now available", id);
if (auto it = client_tunnels_.find(id); it != client_tunnels_.end())
flush_pending_incoming(it->second);
};
}
void TunnelManager::flush_pending_incoming(ClientTunnel& ct)
{
if (!ct.client)
return; // Happens if we're still waiting for a path to build
if (not ct.client->get_connection())
return;
auto& conn = *ct.client->get_connection();
int available = conn.get_streams_available();
while (available > 0 and not ct.pending_incoming.empty())
{
auto tcp_client = ct.pending_incoming.front().lock();
ct.pending_incoming.pop();
if (not tcp_client)
continue;
try
{
auto str = conn.open_stream(
[tcp_client](auto&&... args) {
initial_client_data_handler(*tcp_client, std::forward<decltype(args)>(args)...);
},
[tcp_client](auto&&... args) {
initial_client_close_handler(*tcp_client, std::forward<decltype(args)>(args)...);
});
available--;
}
catch (const std::exception& e)
{
log::warning(logcat, "Opening quic stream failed: {}", e.what());
tcp_client->close();
}
log::trace(logcat, "Set up new stream!");
conn.io_ready();
}
}
void TunnelManager::receive_packet(const service::ConvoTag& tag, const llarp_buffer_t& buf)
{
if (buf.sz <= 4)
{
log::warning(logcat, "Invalid quic packet: packet size ({}) too small", buf.sz);
return;
}
auto type = static_cast<std::byte>(buf.base[0]);
nuint16_t pseudo_port_n;
std::memcpy(&pseudo_port_n.n, &buf.base[1], 2);
uint16_t pseudo_port = ToHost(pseudo_port_n).h;
auto ecn = static_cast<uint8_t>(buf.base[3]);
bstring_view data{reinterpret_cast<const std::byte*>(&buf.base[4]), buf.sz - 4};
SockAddr remote{tag.ToV6()};
quic::Endpoint* ep = nullptr;
if (type == CLIENT_TO_SERVER)
{
log::trace(logcat, "Packet is client-to-server from client pseudo-port:{}", pseudo_port);
// Client-to-server: the header port is the return port
remote.setPort(pseudo_port);
if (!server_)
{
log::warning(logcat, "Dropping incoming quic packet to server: no listeners");
return;
}
ep = server_.get();
}
else if (type == SERVER_TO_CLIENT)
{
log::trace(logcat, "Packet is server-to-client to client pseudo-port:{}", pseudo_port);
// Server-to-client: the header port tells us which client tunnel this is going to
if (auto it = client_tunnels_.find(pseudo_port); it != client_tunnels_.end())
ep = it->second.client.get();
if (not ep)
{
log::warning(logcat, "Incoming quic packet to invalid/closed client; dropping");
return;
}
// The server doesn't send back the port because we already know it 1-to-1 from our
// outgoing connection.
if (auto conn = static_cast<quic::Client&>(*ep).get_connection())
{
remote.setPort(conn->path.remote.port());
log::trace(logcat, "Remoter port is {}", remote.getPort());
}
else
{
log::warning(logcat, "Incoming quic to a quic::Client without an active quic::Connection; dropping");
return;
}
}
else
{
log::warning(logcat, "Invalid incoming quic packet type {}; dropping packet", type);
return;
}
ep->receive_packet(remote, ecn, data);
}
} // namespace llarp::quic

View File

@ -1,219 +1,13 @@
#pragma once
#include <llarp/service/tag.hpp>
#include <llarp/util/time.hpp>
#include <charconv>
#include <chrono>
#include <cstdint>
#include <queue>
#include <string>
#include <string_view>
#include <unordered_set>
namespace llarp::link
namespace llarp
{
// struct Endpoint;
using namespace std::chrono_literals;
namespace tunnel
class QUICTunnel
{
// The server sends back a 0x00 to signal that the remote TCP connection was established and
// that it is now accepting stream data; the client is not allowed to send any other data
// down the stream until this comes back (any data sent down the stream before then is
// discarded.)
inline constexpr std::byte CONNECT_INIT{0x00};
// QUIC application error codes we sent on failures:
// Failure to establish an initial connection:
inline constexpr uint64_t ERROR_CONNECT{0x5471907};
// Error if we receive something other than CONNECT_INIT as the initial stream data from the
// server
inline constexpr uint64_t ERROR_BAD_INIT{0x5471908};
// Close error code sent if we get an error on the TCP socket (other than an initial connect
// failure)
inline constexpr uint64_t ERROR_TCP{0x5471909};
// We pause reading from the local TCP socket if we have more than this amount of
// outstanding unacked data in the quic tunnel, then resume once it drops below this.
inline constexpr size_t PAUSE_SIZE = 64 * 1024;
} // namespace tunnel
/// Manager class for incoming and outgoing QUIC tunnels.
class TunnelManager
{
public:
using ListenHandler = std::function<std::optional<oxen::quic::Address>(
std::string_view lokinet_addr, // The remote's full lokinet address
uint16_t port // The requested port the tunnel wants to reach
)>;
// Timeout for the next `open()`. Note that when `open()` is given a ONS name to resolve
// this includes the resolution time.
std::chrono::milliseconds open_timeout = 4s;
TunnelManager();
/// Adds an incoming listener callback. When a new incoming quic connection is initiated to
/// us by some remote we invoke these callback(s) in order of registration. Each one has
/// three options:
/// - return a concrete llarp::SockAddr giving the TCP address/port to which we should
/// connect new incoming streams over the connection.
/// - returns std::nullopt to decline handling the connection (we will try the next listen
/// handler, in order of registration).
/// - throws an exception (derived from std::exception) in which case we refuse the
/// connection without trying any additional handlers.
///
/// If `listen()` is not called at all then new incoming connections will be immediately
/// dropped.
///
/// For plain-C wrappers around this see [FIXME].
int listen(ListenHandler handler);
/// Simple wrapper around `listen(...)` that adds a handler that accepts all incoming
/// connections trying to tunnel to port `port` and maps them to `localhost:port`.
int listen(oxen::quic::Address port);
/// Removes an incoming connection handler; takes the ID returned by `listen()`.
void forget(int id);
/// Called when open succeeds or times out.
using OpenCallback = std::function<void(bool success)>;
/// Opens a quic tunnel to some remote lokinet address. (Should only be called from the
/// event loop thread.)
///
/// \param remote_addr is the lokinet address or ONS name (e.g. `azfojblahblahblah.loki` or
/// `blocks.loki`) that the tunnel should connect to.
/// \param port is the tunneled port on the remote that the client wants to reach. (This is
/// *not* the quic pseudo-port, which is always 0).
/// \param callback callback invoked when the quic connection has been established, or has
/// timed out. \param bind_addr is the bind address and port that we should use for the
/// localhost TCP connection. Use port 0 to let the OS choose a random high port. Defaults
/// to `127.0.0.1:0`.
///
/// This call immediately opens the local TCP socket, and initiates the lokinet connection
/// and QUIC tunnel to the remote. If the connection fails, the TCP socket will be closed.
/// Note, however, that this TCP socket will block until the underlying quic connection is
/// established.
///
/// Each connection to the local TCP socket establishes a new stream over the QUIC
/// connection.
///
/// \return a pair:
/// - SockAddr containing the just-opened localhost socket that tunnels to the remote. This
/// is typically the same IP as `bind_addr`, with the port filled in (if bind_addr had a 0
/// port). Note that, while you can connect to this socket immediately, it will block until
/// the actual connection and streams are established (and will be closed if they fail).
/// - unique integer that can be passed to close() to stop listening for new connections.
/// This also serves as a unique internal "pseudo-port" number to route returned quic
/// packets to the right connection.
///
/// TODO: add a callback to invoke when QUIC connection succeeds or fails.
/// TODO: add a plain C wrapper around this
std::pair<oxen::quic::Address, uint16_t> open(
std::string_view remote_addr,
uint16_t port,
OpenCallback on_open = {},
oxen::quic::Address bind_addr = {"127.0.0.1", 0});
/// Start closing an outgoing tunnel; takes the ID returned by `open()`. Note that an
/// existing established tunneled connections will not be forcibly closed; this simply stops
/// accepting new tunnel connections.
void close(int id);
/// Called from tun code to deliver a quic packet.
///
/// \param dest - the convotag for which the packet arrived
/// \param buf - the raw arriving packet
///
void receive_packet(const service::SessionTag& tag, const llarp_buffer_t& buf);
/// return true if we have any listeners added
inline bool hasListeners() const
{
return not incoming_handlers_.empty();
}
private:
// EndpointBase& service_endpoint_;
struct ClientTunnel
{
// quic endpoint
// std::unique_ptr<Endpoint> client;
// Callback to invoke on quic connection established (true argument) or failed (false
// arg)
OpenCallback open_cb;
// TCP listening socket
// std::shared_ptr<uvw::TCPHandle> tcp;
// // Accepted TCP connections
// std::unordered_set<std::shared_ptr<uvw::TCPHandle>> conns;
// // Queue of incoming connections that are waiting for a stream to become available
// (either
// // because we are still handshaking, or we reached the stream limit).
// std::queue<std::weak_ptr<uvw::TCPHandle>> pending_incoming;
~ClientTunnel()
{
// if (tcp)
// {
// tcp->close();
// tcp->data(nullptr);
// tcp.reset();
// }
// for (auto& conn : conns)
// conn->close();
// conns.clear();
// while (not pending_incoming.empty())
// {
// if (auto tcp = pending_incoming.front().lock())
// {
// tcp->clear();
// tcp->close();
// }
// pending_incoming.pop();
// }
}
};
// pseudo-port -> Client instance (the "port" is used to route incoming quic packets to the
// right quic endpoint); pseudo-ports start at 1.
std::map<uint16_t, ClientTunnel> client_tunnels_;
uint16_t next_pseudo_port_ = 0;
// bool pport_wrapped_ = false;
bool continue_connecting(
uint16_t pseudo_port, bool step_success, std::string_view step_name, std::string_view addr);
void make_client(const oxen::quic::Address& remote, std::pair<const uint16_t, ClientTunnel>& row);
void flush_pending_incoming(ClientTunnel& ct);
// Server instance; this listens on pseudo-port 0 (if it listens). This is automatically
// instantiated the first time `listen()` is called; if not instantiated we simply drop any
// inbound client-to-server quic packets.
// std::unique_ptr<Server> server_;
void make_server();
// Called when a new during connection handshaking once we have the established transport
// parameters (which include the port) if this is an incoming connection (and this endpoint
// is a server). This checks handlers to see whether the stream is allowed and, if so,
// returns a SockAddr containing the IP/port the tunnel should map to. Returns nullopt if
// the connection should be rejected.
std::optional<oxen::quic::Address> allow_connection(std::string_view lokinet_addr, uint16_t port);
// Incoming stream handlers
std::map<int, ListenHandler> incoming_handlers_;
int next_handler_id_ = 1;
// std::shared_ptr<uvw::Loop>
// get_loop();
// Cleanup member
std::shared_ptr<int> timer_keepalive_ = std::make_shared<int>(0);
//
};
} // namespace llarp::link
} // namespace llarp

View File

@ -12,6 +12,8 @@
namespace llarp
{
using namespace std::literals;
namespace messages
{
static auto logcat = log::Cat("messages");

View File

@ -253,9 +253,9 @@ namespace llarp::net
return true;
}
StatusObject ProtocolInfo::ExtractStatus() const
nlohmann::json ProtocolInfo::ExtractStatus() const
{
StatusObject status{
nlohmann::json status{
{"protocol", static_cast<uint32_t>(protocol)},
};
if (port)
@ -263,19 +263,19 @@ namespace llarp::net
return status;
}
StatusObject TrafficPolicy::ExtractStatus() const
nlohmann::json TrafficPolicy::ExtractStatus() const
{
std::vector<StatusObject> rangesStatus;
std::vector<nlohmann::json> rangesStatus;
std::transform(ranges.begin(), ranges.end(), std::back_inserter(rangesStatus), [](const auto& range) {
return range.to_string();
});
std::vector<StatusObject> protosStatus;
std::vector<nlohmann::json> protosStatus;
std::transform(protocols.begin(), protocols.end(), std::back_inserter(protosStatus), [](const auto& proto) {
return proto.ExtractStatus();
});
return StatusObject{{"ranges", rangesStatus}, {"protocols", protosStatus}};
return nlohmann::json{{"ranges", rangesStatus}, {"protocols", protosStatus}};
}
} // namespace llarp::net

View File

@ -2,7 +2,6 @@
#include <llarp/address/ip_packet.hpp>
#include <llarp/address/ip_range.hpp>
#include <llarp/util/types.hpp>
#include <oxenc/bt.h>
@ -42,7 +41,7 @@ namespace llarp::net
bool bt_decode(std::string_view buf);
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
/// returns true if an ip packet looks like it matches this protocol info
/// returns false otherwise
@ -72,7 +71,7 @@ namespace llarp::net
void bt_decode(oxenc::bt_dict_consumer& btdc);
bool bt_decode(std::string_view buf);
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
/// returns true if we allow the traffic in this ip packet
/// returns false otherwise

View File

@ -246,9 +246,9 @@ namespace llarp::path
return hops_str;
}
StatusObject PathHopConfig::ExtractStatus() const
nlohmann::json PathHopConfig::ExtractStatus() const
{
StatusObject obj{
nlohmann::json obj{
{"ip", rc.addr().to_string()},
{"lifetime", to_json(lifetime)},
{"router", rc.router_id().ToHex()},
@ -257,11 +257,11 @@ namespace llarp::path
return obj;
}
StatusObject Path::ExtractStatus() const
nlohmann::json Path::ExtractStatus() const
{
auto now = llarp::time_now_ms();
StatusObject obj{
nlohmann::json obj{
{"intro", intro.ExtractStatus()},
{"lastRecvMsg", to_json(last_recv_msg)},
{"lastLatencyTest", to_json(last_latency_test)},
@ -275,8 +275,8 @@ namespace llarp::path
// {"hasExit", SupportsAnyRoles(ePathRoleExit)}
};
std::vector<StatusObject> hopsObj;
std::transform(hops.begin(), hops.end(), std::back_inserter(hopsObj), [](const auto& hop) -> StatusObject {
std::vector<nlohmann::json> hopsObj;
std::transform(hops.begin(), hops.end(), std::back_inserter(hopsObj), [](const auto& hop) -> nlohmann::json {
return hop.ExtractStatus();
});
obj["hops"] = hopsObj;

View File

@ -60,7 +60,7 @@ namespace llarp
return weak_from_this();
}
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
void MarkActive(std::chrono::milliseconds now)
{

View File

@ -7,7 +7,6 @@
#include <llarp/ev/loop.hpp>
#include <llarp/util/compare_ptr.hpp>
#include <llarp/util/decaying_hashset.hpp>
#include <llarp/util/types.hpp>
#include <memory>
#include <unordered_map>

View File

@ -32,9 +32,9 @@ namespace llarp::path
return _edge_limiter.Contains(router);
}
StatusObject BuildStats::ExtractStatus() const
nlohmann::json BuildStats::ExtractStatus() const
{
return StatusObject{
return nlohmann::json{
{"success", success}, {"attempts", attempts}, {"timeouts", timeouts}, {"fails", build_fails}};
}
@ -314,14 +314,14 @@ namespace llarp::path
}
}
StatusObject PathHandler::ExtractStatus() const
nlohmann::json PathHandler::ExtractStatus() const
{
StatusObject obj{
nlohmann::json obj{
{"buildStats", _build_stats.ExtractStatus()},
{"numHops", uint64_t{num_hops}},
{"numPaths", uint64_t{num_paths_desired}}};
std::transform(
_paths.begin(), _paths.end(), std::back_inserter(obj["paths"]), [](const auto& item) -> StatusObject {
_paths.begin(), _paths.end(), std::back_inserter(obj["paths"]), [](const auto& item) -> nlohmann::json {
return item.second->ExtractStatus();
});
return obj;

View File

@ -7,7 +7,6 @@
#include <llarp/util/decaying_hashset.hpp>
#include <llarp/util/thread/threading.hpp>
#include <llarp/util/time.hpp>
#include <llarp/util/types.hpp>
#include <atomic>
#include <set>
@ -68,7 +67,7 @@ namespace llarp
uint64_t path_fails = 0; // path failures post-build
uint64_t timeouts = 0;
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
double SuccessRatio() const;
@ -171,7 +170,7 @@ namespace llarp
std::optional<std::set<service::Introduction>> get_path_intros_conditional(
std::function<bool(const service::Introduction&)> filter) const;
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
virtual bool should_build_more() const;

View File

@ -33,7 +33,7 @@ namespace llarp
// lifetime
std::chrono::milliseconds lifetime = DEFAULT_LIFETIME;
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
bool operator<(const PathHopConfig& other) const
{

View File

@ -45,7 +45,7 @@ namespace llarp::path
if (r.path_context().has_transit_hop(hop->info))
throw std::runtime_error{PathBuildMessage::BAD_PATHID};
if (!crypto::dh_server(hop->shared.data(), symmkey.data(), r.pubkey(), symmnonce.data()))
if (!crypto::dh_server(hop->shared.data(), symmkey.data(), r.pubkey().data(), symmnonce.data()))
throw std::runtime_error{PathBuildMessage::BAD_CRYPTO};
// generate hash of hop key for nonce mutation

View File

@ -5,16 +5,13 @@
#include <llarp/constants/time.hpp>
#include <llarp/crypto/crypto.hpp>
#include <llarp/dht/node.hpp>
#include <llarp/handlers/common.hpp>
#include <llarp/handlers/embedded.hpp>
#include <llarp/handlers/tun.hpp>
#include <llarp/link/contacts.hpp>
#include <llarp/link/link_manager.hpp>
#include <llarp/messages/dht.hpp>
#include <llarp/net/net.hpp>
#include <llarp/nodedb.hpp>
#include <llarp/util/formattable.hpp>
#include <llarp/util/logging.hpp>
#include <llarp/util/types.hpp>
#include <cstdlib>
#include <iterator>
@ -56,20 +53,20 @@ namespace llarp
_lmq->MAX_MSG_SIZE = -1;
}
StatusObject Router::ExtractStatus() const
nlohmann::json Router::ExtractStatus() const
{
if (not _is_running)
StatusObject{{"running", false}};
nlohmann::json{{"running", false}};
return StatusObject{
return nlohmann::json{
{"running", true}, {"numNodesKnown", _node_db->num_rcs()}, {"links", _link_manager->extract_status()}};
}
// TODO: investigate changes needed for libquic integration
StatusObject Router::ExtractSummaryStatus() const
nlohmann::json Router::ExtractSummaryStatus() const
{
// if (!is_running)
// return StatusObject{{"running", false}};
// return nlohmann::json{{"running", false}};
// auto services = _hidden_service_context.ExtractStatus();
@ -140,7 +137,7 @@ namespace llarp
// }
// double ratio = static_cast<double>(success) / (attempts + 1);
StatusObject stats{
nlohmann::json stats{
{"running", true},
{"version", llarp::LOKINET_VERSION_FULL},
{"uptime", to_json(Uptime())},
@ -234,9 +231,9 @@ namespace llarp
try
{
_identity = rpc_client()->obtain_identity_key();
const RouterID pk{pubkey()};
_id_pubkey = seckey_to_pubkey(_identity);
log::warning(logcat, "Obtained lokid identity key: {}", pk);
log::warning(logcat, "Obtained lokid identity key: {}", _id_pubkey);
rpc_client()->start_pings();
break;
}
@ -253,6 +250,7 @@ namespace llarp
else
{
_identity = _key_manager->identity_key;
_id_pubkey = seckey_to_pubkey(_identity);
}
if (_identity.is_zero())
@ -477,67 +475,14 @@ namespace llarp
}
}
void Router::init_net_if()
void Router::init_tun()
{
auto& network_config = _config->network;
vpn::InterfaceInfo info;
info.ifname = *network_config._if_name;
info.addrs.emplace_back(*network_config._local_ip_range);
auto if_net = vpn_platform()->CreateInterface(std::move(info), this);
if (not if_net)
if (_tun = std::make_unique<handlers::TunEndpoint>(*this); _tun != nullptr)
{
auto err = "Could not create net interface"s;
log::error(logcat, "{}", err);
throw std::runtime_error{err};
}
if (not loop()->add_network_interface(
if_net, [](UDPPacket pkt [[maybe_unused]]) { /* OnInetPacket(std::move(pkt)); */ }))
{
auto err = "Could not create tunnel for net interface"s;
log::error(logcat, "{}", err);
throw std::runtime_error{err};
}
// _router->loop()->add_ticker([this] { Flush(); });
#ifndef _WIN32
// TOFIX:
// resolver =
// std::make_shared<dns::Server>(_router->loop(), dns_conf,
// if_nametoindex(if_name.c_str()));
// resolver->Start();
#endif
}
using api_constructor = std::function<std::unique_ptr<handlers::BaseHandler>(Router&)>;
const std::map<std::string, api_constructor> api_constructors = {
{"tun", [](Router& r) { return std::make_unique<handlers::TunEndpoint>(r); }},
{"android", [](Router& r) { return std::make_unique<handlers::TunEndpoint>(r); }},
{"ios", [](Router& r) { return std::make_unique<handlers::TunEndpoint>(r); }},
{"embedded", [](Router& r) { return std::make_unique<handlers::EmbeddedEndpoint>(r); }}};
void Router::init_api()
{
auto& net_config = _config->network;
auto& type = net_config.endpoint_type;
auto& key_file = net_config.keyfile;
if (auto itr = api_constructors.find(type); itr != api_constructors.end())
{
_api = itr->second(*this);
if (not _api)
throw std::runtime_error{"Failed to construct API endpoint of type {}"_format(type)};
_api->load_key_file(key_file, *this);
_api->configure();
_tun->configure();
}
else
throw std::runtime_error{"API endpoint of type {} does not exist"_format(type)};
throw std::runtime_error{"Failed to construct TunEndpoint!"};
}
bool Router::configure(std::shared_ptr<Config> c, std::shared_ptr<NodeDB> nodedb)
@ -619,19 +564,15 @@ namespace llarp
_remote_handler = std::make_shared<handlers::RemoteHandler>(*this);
_remote_handler->configure();
if (conf.network.endpoint_type != "embedded")
{
_should_init_tun = true;
init_net_if();
}
// API config
// all instances have an API
// all clients have Tun or Null
// all snodes have Tun
//
// TODO: change this for snodes running hidden service
init_api();
// Full clients have TUN
// Embedded clients have nothing
// All relays have TUN
if (_should_init_tun = conf.network.init_tun; _should_init_tun)
{
init_tun();
}
if (not ensure_identity())
throw std::runtime_error{"EnsureIdentity() failed"};

View File

@ -10,6 +10,7 @@
#include <llarp/ev/loop.hpp>
#include <llarp/handlers/endpoint.hpp>
#include <llarp/handlers/remote.hpp>
#include <llarp/handlers/tun.hpp>
#include <llarp/path/path_context.hpp>
#include <llarp/profiling.hpp>
#include <llarp/router_contact.hpp>
@ -21,7 +22,6 @@
#include <llarp/util/service_manager.hpp>
#include <llarp/util/str.hpp>
#include <llarp/util/time.hpp>
#include <llarp/util/types.hpp>
#include <llarp/vpn/platform.hpp>
#include <oxenmq/address.h>
@ -37,11 +37,6 @@
namespace llarp
{
namespace handlers
{
struct BaseHandler;
}
namespace link
{
struct Connection;
@ -125,13 +120,14 @@ namespace llarp
std::shared_ptr<handlers::RemoteHandler> _remote_handler;
std::shared_ptr<handlers::LocalEndpoint> _local_endpoint;
// TunEndpoint or NullEndpoint, depending on lokinet configuration
std::unique_ptr<handlers::BaseHandler> _api;
// Only created in full client and relay instances (not embedded clients)
std::unique_ptr<handlers::TunEndpoint> _tun;
std::shared_ptr<EventLoop> _loop;
std::shared_ptr<vpn::Platform> _vpn;
path::PathContext paths;
SecretKey _identity;
RouterID _id_pubkey;
SecretKey _encryption;
std::shared_ptr<Contacts> _contacts;
std::shared_ptr<NodeDB> _node_db;
@ -179,9 +175,7 @@ namespace llarp
void init_rpc();
void init_net_if();
void init_api();
void init_tun();
void init_bootstrap();
@ -340,9 +334,9 @@ namespace llarp
oxen::quic::Address listen_addr() const;
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
StatusObject ExtractSummaryStatus() const;
nlohmann::json ExtractSummaryStatus() const;
const std::set<RouterID>& get_whitelist() const;
@ -437,11 +431,16 @@ namespace llarp
bool PathToRouterAllowed(const RouterID& router) const;
const uint8_t* pubkey() const
const RouterID& pubkey() const
{
return local_rid().data();
return _id_pubkey;
}
// const uint8_t* pubkey() const
// {
// return seckey_to_pubkey(_identity);
// }
/// send to remote router or queue for sending
/// returns false on overflow
/// returns true on successful queue

View File

@ -120,9 +120,9 @@ namespace llarp
return true;
}
StatusObject RouterContact::extract_status() const
nlohmann::json RouterContact::extract_status() const
{
StatusObject obj{
nlohmann::json obj{
{"lastUpdated", _timestamp.time_since_epoch().count()},
{"publicRouter", is_public_addressable()},
{"identity", _router_id.to_string()},
@ -132,7 +132,7 @@ namespace llarp
// {
// obj["routerVersion"] = routerVersion->to_string();
// }
// std::vector<StatusObject> srv;
// std::vector<nlohmann::json> srv;
// for (const auto& record : srvRecords)
// {
// srv.emplace_back(record.ExtractStatus());

View File

@ -8,7 +8,6 @@
#include <llarp/dns/srv_data.hpp>
#include <llarp/util/aligned.hpp>
#include <llarp/util/time.hpp>
#include <llarp/util/types.hpp>
#include <nlohmann/json.hpp>
#include <oxen/quic.hpp>
@ -113,7 +112,7 @@ namespace llarp
/// should we serialize the exit info?
static const bool serializeExit = true;
StatusObject extract_status() const;
nlohmann::json extract_status() const;
nlohmann::json to_json() const
{

View File

@ -19,9 +19,9 @@ namespace llarp
return oxenc::to_base32z(begin(), begin() + 5);
}
StatusObject RouterID::ExtractStatus() const
nlohmann::json RouterID::ExtractStatus() const
{
StatusObject obj{{"snode", to_string()}, {"hex", ToHex()}};
nlohmann::json obj{{"snode", to_string()}, {"hex", ToHex()}};
return obj;
}

View File

@ -5,6 +5,8 @@
#include <llarp/crypto/types.hpp>
#include <llarp/util/formattable.hpp>
#include <nlohmann/json.hpp>
namespace llarp
{
struct RouterID : public PubKey
@ -27,7 +29,7 @@ namespace llarp
RouterID(std::string_view data) : RouterID(to_usv(data))
{}
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
std::string to_string() const;

View File

@ -131,7 +131,7 @@ namespace llarp::rpc
void RPCServer::invoke(Version& version)
{
StatusObject result{{"version", llarp::LOKINET_VERSION_FULL}, {"uptime", to_json(m_Router.Uptime())}};
nlohmann::json result{{"version", llarp::LOKINET_VERSION_FULL}, {"uptime", to_json(m_Router.Uptime())}};
SetJSONResponse(result, version.response);
}
@ -196,7 +196,7 @@ namespace llarp::rpc
// auto [addr, id] = quic->open(
// req.remoteHost, req.port, [](auto&&) {}, laddr);
StatusObject status;
nlohmann::json status;
// status["addr"] = addr.to_string();
// status["id"] = id;
@ -258,7 +258,7 @@ namespace llarp::rpc
return;
}
StatusObject result;
nlohmann::json result;
result["id"] = id;
std::string localAddress;
var::visit([&](auto&& addr) { localAddress = addr.to_string(); }, endpoint->local_address());
@ -310,7 +310,7 @@ namespace llarp::rpc
// if (session and session->IsReady())
// {
// const auto ip = net::TruncateV6(endpoint->GetIPForIdent(PubKey{routerID}));
// StatusObject status{{"ip", ip.to_string()}};
// nlohmann::json status{{"ip", ip.to_string()}};
// SetJSONResponse(status, lookupsnode.response);
// return;
// }

View File

@ -1,7 +1,5 @@
#include "info.hpp"
#include "address.hpp"
#include <llarp/crypto/crypto.hpp>
namespace llarp::service

View File

@ -6,9 +6,9 @@ namespace llarp::service
{
static auto logcat = log::Cat("introduction");
StatusObject Introduction::ExtractStatus() const
nlohmann::json Introduction::ExtractStatus() const
{
StatusObject obj{
nlohmann::json obj{
{"router", pivot_router.ToHex()},
{"path", hop_id.ToHex()},
{"expiresAt", to_json(expiry)},

View File

@ -2,7 +2,6 @@
#include <llarp/crypto/types.hpp>
#include <llarp/path/path_types.hpp>
#include <llarp/util/types.hpp>
#include <oxenc/bt.h>
@ -22,7 +21,7 @@ namespace llarp::service
Introduction() = default;
Introduction(std::string buf);
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
bool is_expired(std::chrono::milliseconds now) const
{

View File

@ -18,7 +18,7 @@ namespace llarp::service
introset_payload{reinterpret_cast<uint8_t*>(enc_payload.data()), enc_payload.size()},
nonce{reinterpret_cast<uint8_t*>(nonce.data())}
{
derived_signing_key = make_from_hex<PubKey>(signing_key);
derived_signing_key.from_hex(signing_key);
sig.from_string(std::move(s));
}
@ -35,7 +35,7 @@ namespace llarp::service
}
}
StatusObject EncryptedIntroSet::ExtractStatus() const
nlohmann::json EncryptedIntroSet::ExtractStatus() const
{
const auto sz = introset_payload.size();
return {{"location", derived_signing_key.to_string()}, {"signedAt", to_json(signed_at)}, {"size", sz}};
@ -180,33 +180,33 @@ namespace llarp::service
reinterpret_cast<uint8_t*>(sig.data()));
}
StatusObject IntroSet::ExtractStatus() const
nlohmann::json IntroSet::ExtractStatus() const
{
StatusObject obj{{"published", to_json(time_signed)}};
nlohmann::json obj{{"published", to_json(time_signed)}};
// TODO: this
// std::vector<StatusObject> introsObjs;
// std::vector<nlohmann::json> introsObjs;
// std::transform(
// intros.begin(),
// intros.end(),
// std::back_inserter(introsObjs),
// [](const auto& intro) -> StatusObject { return intro.ExtractStatus(); });
// [](const auto& intro) -> nlohmann::json { return intro.ExtractStatus(); });
// obj["intros"] = introsObjs;
// if (!topic.IsZero())
// obj["topic"] = topic.to_string();
// std::vector<StatusObject> protocols;
// std::vector<nlohmann::json> protocols;
// std::transform(
// supported_protocols.begin(),
// supported_protocols.end(),
// std::back_inserter(protocols),
// [](const auto& proto) -> StatusObject { return service::to_string(proto); });
// [](const auto& proto) -> nlohmann::json { return service::to_string(proto); });
// obj["protos"] = protocols;
// std::vector<StatusObject> ranges;
// std::vector<nlohmann::json> ranges;
// std::transform(
// owned_ranges.begin(),
// owned_ranges.end(),
// std::back_inserter(ranges),
// [](const auto& range) -> StatusObject { return range.to_string(); });
// [](const auto& range) -> nlohmann::json { return range.to_string(); });
// obj["advertisedRanges"] = ranges;
// if (exit_policy)

View File

@ -9,7 +9,6 @@
#include <llarp/dns/srv_data.hpp>
#include <llarp/net/traffic_policy.hpp>
#include <llarp/util/time.hpp>
#include <llarp/util/types.hpp>
#include <algorithm>
#include <functional>
@ -75,7 +74,7 @@ namespace llarp::service
bool verify(std::chrono::milliseconds now) const;
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
};
inline bool operator<(const IntroSet& lhs, const IntroSet& rhs)
@ -139,7 +138,7 @@ namespace llarp::service
std::string to_string() const;
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
std::optional<IntroSet> decrypt(const PubKey& root) const;
};

View File

@ -2,9 +2,9 @@
namespace llarp::service
{
StatusObject Session::ExtractStatus() const
nlohmann::json Session::ExtractStatus() const
{
StatusObject obj{
nlohmann::json obj{
{"lastSend", to_json(lastSend)},
{"lastRecv", to_json(lastRecv)},
{"replyIntro", replyIntro.ExtractStatus()},

View File

@ -5,7 +5,6 @@
#include <llarp/crypto/types.hpp>
#include <llarp/path/path.hpp>
#include <llarp/util/types.hpp>
namespace llarp::service
{
@ -34,7 +33,7 @@ namespace llarp::service
std::chrono::milliseconds lastSend{};
std::chrono::milliseconds lastRecv{};
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
/// called to indicate we recieved on this session
void RX();

View File

@ -62,7 +62,7 @@ namespace llarp::session
p->rebuild();
}
StatusObject OutboundSession::ExtractStatus() const
nlohmann::json OutboundSession::ExtractStatus() const
{
auto obj = path::PathHandler::ExtractStatus();
obj["lastExitUse"] = to_json(_last_use);

View File

@ -92,7 +92,7 @@ namespace llarp
void build_more(size_t n = 0) override;
StatusObject ExtractStatus() const;
nlohmann::json ExtractStatus() const;
void reset_path_state() override;

View File

@ -2,7 +2,6 @@
#include "common.hpp"
#include "mem.h"
#include "types.hpp"
#include <algorithm>
#include <cassert>

View File

@ -2,16 +2,16 @@
#include "concept.hpp"
#include <fmt/format.h>
// #include <fmt/format.h>
#include <oxen/log/format.hpp>
#include <oxen/quic/format.hpp>
#include <type_traits>
// Formattable types can specialize this to true and will get automatic fmt formattering support via
// their .to_string() method.
namespace llarp
{
using namespace oxen::log::literals;
// Types can opt-in to being fmt-formattable by ensuring they have a ::to_string() method defined
template <typename T>
concept CONCEPT_COMPAT ToStringFormattable = oxen::quic::ToStringFormattable<T>;

View File

@ -1,20 +1,18 @@
#include "time.hpp"
#include "types.hpp"
namespace llarp
{
using namespace std::literals;
namespace
{
using Clock_t = std::chrono::system_clock;
template <typename Res, typename Clock>
static std::chrono::milliseconds time_since_epoch(std::chrono::time_point<Clock> point)
{
return std::chrono::duration_cast<Res>(point.time_since_epoch());
}
static const auto started_at_system = Clock_t::now();
static const auto started_at_system = std::chrono::system_clock::now();
static const auto started_at_steady = std::chrono::steady_clock::now();
} // namespace
@ -52,7 +50,7 @@ namespace llarp
#ifdef TESTNET_SPEED
t /= uint64_t{TESTNET_SPEED};
#endif
return t + time_since_epoch<std::chrono::milliseconds, Clock_t>(started_at_system);
return t + time_since_epoch<std::chrono::milliseconds, std::chrono::system_clock>(started_at_system);
}
nlohmann::json to_json(const std::chrono::milliseconds& t)
@ -69,46 +67,24 @@ namespace llarp
(std::chrono::duration_cast<std::chrono::milliseconds>(dur) % 1s).count());
}
std::string short_time_from_now(const TimePoint_t& t, const std::chrono::milliseconds& now_threshold)
std::string short_time_from_now(
const std::chrono::system_clock::time_point& t, const std::chrono::milliseconds& now_threshold)
{
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(llarp::TimePoint_t::clock::now() - t);
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::time_point::clock::now() - t);
bool future = delta < 0s;
if (future)
delta = -delta;
auto [hours, mins, secs, ms] = extract_h_m_s_ms(delta);
using namespace fmt::literals;
return fmt::format(
delta < now_threshold ? "now"
: delta < 10s ? "{in}{secs:d}.{ms:03d}s{ago}"
: delta < 1h ? "{in}{mins:d}m{secs:02d}s{ago}"
: "{in}{hours:d}h{mins:02d}m{ago}",
"in"_a = future ? "in " : "",
"ago"_a = future ? "" : " ago",
"hours"_a = hours,
"mins"_a = mins,
"secs"_a = secs,
"ms"_a = ms);
auto in = future ? "in "sv : ""sv;
auto ago = future ? ""sv : " ago"sv;
return delta < now_threshold ? "now"s
: delta < 10s ? "{}{}.{:03d}s{}"_format(in, secs, ms, ago)
: delta < 1h ? "{}{}m{:02d}s{}"_format(in, mins, secs, ago)
: "{}{}h{:02d}m{}"_format(in, hours, mins, ago);
}
std::string to_string(std::chrono::milliseconds delta)
{
bool neg = delta < 0s;
if (neg)
delta = -delta;
auto [hours, mins, secs, ms] = extract_h_m_s_ms(delta);
using namespace fmt::literals;
return fmt::format(
delta < 1min ? "{neg}{secs:d}.{ms:03d}s"
: delta < 1h ? "{neg}{mins:d}m{secs:02d}.{ms:03d}s"
: "{neg}{hours:d}h{mins:02d}m{secs:02d}.{ms:03d}s",
"neg"_a = neg ? "-" : "",
"hours"_a = hours,
"mins"_a = mins,
"secs"_a = secs,
"ms"_a = ms);
}
} // namespace llarp

View File

@ -1,6 +1,7 @@
#pragma once
#include "types.hpp"
#include "buffer.hpp"
#include "formattable.hpp"
#include <fmt/chrono.h>
#include <fmt/format.h>
@ -33,11 +34,8 @@ namespace llarp
// Returns a string such as "27m13s ago" or "in 1h12m" or "now". You get precision of minutes
// (for >=1h), seconds (>=10s), or milliseconds. The `now_threshold` argument controls how
// close to current time (default 1s) the time has to be to get the "now" argument.
std::string short_time_from_now(const TimePoint_t& t, const std::chrono::milliseconds& now_threshold = 1s);
// Makes a duration human readable. This always has full millisecond precision, but formats up
// to hours. E.g. "-4h04m12.123s" or "1234h00m09.876s.
std::string to_string(std::chrono::milliseconds t);
std::string short_time_from_now(
const std::chrono::system_clock::time_point& t, const std::chrono::milliseconds& now_threshold = 1s);
inline timeval loop_time_to_timeval(loop_time t)
{

View File

@ -1,22 +0,0 @@
#pragma once
#include <nlohmann/json.hpp>
#include <oxen/log/format.hpp>
#include <chrono>
#include <cstdint>
#include <string>
namespace llarp
{
using namespace std::literals;
using namespace oxen::log::literals;
/// convert to milliseconds
uint64_t to_milliseconds(std::chrono::milliseconds duration);
using DateClock_t = std::chrono::system_clock;
using TimePoint_t = DateClock_t::time_point;
using StatusObject = nlohmann::json;
} // namespace llarp

View File

@ -1,7 +1,6 @@
#pragma once
#include <llarp/address/ip_packet.hpp>
#include <llarp/util/types.hpp>
#include <functional>

View File

@ -314,7 +314,7 @@ namespace llarp::vpn
void default_route_via_interface(NetworkInterface& vpn, int cmd, int flags)
{
const auto& info = vpn.Info();
const auto& info = vpn.interface_info();
const auto maybe = Net().get_interface_addr(info.ifname);
if (not maybe)
@ -340,7 +340,7 @@ namespace llarp::vpn
void route_via_interface(int cmd, int flags, NetworkInterface& vpn, IPRange range)
{
const auto& info = vpn.Info();
const auto& info = vpn.interface_info();
if (range.is_ipv4())
{
const auto maybe = Net().get_interface_addr(info.ifname);
@ -431,7 +431,7 @@ namespace llarp::vpn
std::vector<oxen::quic::Address> get_non_interface_gateways(NetworkInterface& vpn) override
{
const auto& ifname = vpn.Info().ifname;
const auto& ifname = vpn.interface_info().ifname;
std::vector<oxen::quic::Address> gateways{};
std::ifstream inf{"/proc/net/route"};

View File

@ -1,5 +1,4 @@
#pragma once
#include <llarp/util/types.hpp>
#include <functional>
#include <vector>

View File

@ -50,7 +50,7 @@ namespace llarp::vpn
PacketRouter::PacketRouter(ip_pkt_hook baseHandler) : _handler{std::move(baseHandler)}
{}
void PacketRouter::handle_ip_packet(UDPPacket pkt)
void PacketRouter::handle_ip_packet(IPPacket pkt)
{
(void)pkt;
// const auto proto = pkt.Header()->protocol;

View File

@ -9,8 +9,6 @@ namespace llarp::vpn
{
struct Layer4Handler;
// DISCUSS: do we need this fucking protocol mapping shit??
class PacketRouter
{
ip_pkt_hook _handler;
@ -21,7 +19,7 @@ namespace llarp::vpn
explicit PacketRouter(ip_pkt_hook baseHandler);
/// feed in an ip packet for handling
void handle_ip_packet(UDPPacket pkt);
void handle_ip_packet(IPPacket pkt);
/// add a non udp packet handler using ip protocol proto
void add_ip_proto_handler(uint8_t proto, ip_pkt_hook func);

View File

@ -57,7 +57,7 @@ namespace llarp::vpn
NetworkInterface(const NetworkInterface&) = delete;
NetworkInterface(NetworkInterface&&) = delete;
const InterfaceInfo& Info() const
const InterfaceInfo& interface_info() const
{
return _info;
}
@ -116,7 +116,7 @@ namespace llarp::vpn
virtual ~Platform() = default;
/// create and start a network interface
inline std::shared_ptr<NetworkInterface> CreateInterface(InterfaceInfo info, Router* router)
std::shared_ptr<NetworkInterface> CreateInterface(InterfaceInfo info, Router* router)
{
if (auto netif = ObtainInterface(std::move(info), router))
{

View File

@ -1,6 +1,5 @@
#pragma once
#include <llarp/util/service_manager.hpp>
#include <llarp/util/types.hpp>
#include <windows.h>