2021-03-09 22:24:35 +00:00
|
|
|
#include "link_manager.hpp"
|
2019-06-26 21:39:29 +00:00
|
|
|
|
2023-08-28 20:50:06 +00:00
|
|
|
#include <llarp/router/i_rc_lookup_handler.hpp>
|
|
|
|
#include <llarp/nodedb.hpp>
|
2021-03-09 22:24:35 +00:00
|
|
|
#include <llarp/crypto/crypto.hpp>
|
2019-06-26 21:39:29 +00:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
namespace llarp
|
|
|
|
{
|
2023-08-28 20:50:06 +00:00
|
|
|
link::Endpoint*
|
|
|
|
LinkManager::GetCompatibleLink(const RouterContact& rc)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (stopping)
|
2019-06-26 21:39:29 +00:00
|
|
|
return nullptr;
|
|
|
|
|
2023-08-28 20:50:06 +00:00
|
|
|
for (auto& ep : endpoints)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
//TODO: need some notion of "is this link compatible with that address".
|
|
|
|
// iwp just checks that the link dialect ("iwp") matches the address info dialect,
|
|
|
|
// but that feels insufficient. For now, just return the first endpoint we have;
|
|
|
|
// we should probably only have 1 for now anyway until we make ipv6 work.
|
|
|
|
return &ep;
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-08-28 14:56:44 +00:00
|
|
|
//TODO: replace with control/data message sending with libquic
|
2019-06-26 21:39:29 +00:00
|
|
|
bool
|
2020-04-07 18:38:56 +00:00
|
|
|
LinkManager::SendTo(
|
2022-03-30 20:21:57 +00:00
|
|
|
const RouterID& remote,
|
|
|
|
const llarp_buffer_t& buf,
|
|
|
|
ILinkSession::CompletionHandler completed,
|
|
|
|
uint16_t priority)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (stopping)
|
2019-06-26 21:39:29 +00:00
|
|
|
return false;
|
|
|
|
|
2023-08-28 20:50:06 +00:00
|
|
|
if (not HaveConnection(remote))
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (completed)
|
2019-07-26 16:19:31 +00:00
|
|
|
{
|
|
|
|
completed(ILinkSession::DeliveryStatus::eDeliveryDropped);
|
|
|
|
}
|
2019-06-26 21:39:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-28 20:50:06 +00:00
|
|
|
//TODO: send the message
|
|
|
|
//TODO: if we keep bool return type, change this accordingly
|
|
|
|
return false;
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2023-08-28 20:50:06 +00:00
|
|
|
LinkManager::HaveConnection(const RouterID& remote, bool client_only) const
|
2021-06-07 22:31:57 +00:00
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
for (const auto& ep : endpoints)
|
2021-06-07 22:31:57 +00:00
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
if (auto itr = ep.connections.find(remote); itr != ep.connections.end())
|
|
|
|
{
|
2023-08-28 20:50:06 +00:00
|
|
|
if (not (itr->second.remote_is_relay and client_only))
|
|
|
|
return true;
|
|
|
|
return false;
|
2023-08-28 14:56:44 +00:00
|
|
|
}
|
2021-06-07 22:31:57 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-28 20:50:06 +00:00
|
|
|
bool
|
|
|
|
LinkManager::HaveClientConnection(const RouterID& remote) const
|
|
|
|
{
|
|
|
|
return HaveConnection(remote, true);
|
|
|
|
}
|
|
|
|
|
2020-11-10 14:24:58 +00:00
|
|
|
void
|
|
|
|
LinkManager::DeregisterPeer(RouterID remote)
|
|
|
|
{
|
|
|
|
m_PersistingSessions.erase(remote);
|
2023-08-28 14:56:44 +00:00
|
|
|
for (const auto& ep : endpoints)
|
2020-11-10 14:24:58 +00:00
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
if (auto itr = ep.connections.find(remote); itr != ep.connections.end())
|
2023-08-28 17:39:40 +00:00
|
|
|
{
|
2023-08-28 20:50:06 +00:00
|
|
|
/*
|
2023-08-28 14:56:44 +00:00
|
|
|
itr->second.conn->close(); //TODO: libquic needs some function for this
|
2023-08-28 20:50:06 +00:00
|
|
|
*/
|
2023-08-28 17:39:40 +00:00
|
|
|
}
|
2020-11-10 14:24:58 +00:00
|
|
|
}
|
|
|
|
|
2023-08-28 14:56:44 +00:00
|
|
|
LogInfo(remote, " has been de-registered");
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2023-08-28 20:50:06 +00:00
|
|
|
LinkManager::AddLink(const oxen::quic::opt::local_addr& bind, bool inbound)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
//TODO: libquic callbacks: new_conn_alpn_notify, new_conn_pubkey_ok, new_conn_established/ready
|
2023-08-28 23:20:57 +00:00
|
|
|
// stream_opened, stream_data, stream_closed, conn_closed
|
|
|
|
oxen::quic::dgram_data_callback dgram_cb = [this](oxen::quic::dgram_interface& dgi, bstring dgram){ HandleIncomingDataMessage(dgi, dgram); };
|
|
|
|
auto ep = quic->endpoint(bind, std::move(dgram_cb), oxen::quic::opt::enable_datagrams{oxen::quic::Splitting::ACTIVE});
|
2023-08-28 14:56:44 +00:00
|
|
|
endpoints.emplace_back();
|
|
|
|
auto& endp = endpoints.back();
|
|
|
|
endp.endpoint = std::move(ep);
|
2020-04-07 18:38:56 +00:00
|
|
|
if (inbound)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2023-08-28 23:20:57 +00:00
|
|
|
endp.endpoint->listen(tls_creds);
|
2023-08-28 14:56:44 +00:00
|
|
|
endp.inbound = true;
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
LinkManager::Stop()
|
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (stopping)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
De-abseil, part 2: mutex, locks, (most) time
- util::Mutex is now a std::shared_timed_mutex, which is capable of
exclusive and shared locks.
- util::Lock is still present as a std::lock_guard<util::Mutex>.
- the locking annotations are preserved, but updated to the latest
supported by clang rather than using abseil's older/deprecated ones.
- ACQUIRE_LOCK macro is gone since we don't pass mutexes by pointer into
locks anymore (WTF abseil).
- ReleasableLock is gone. Instead there are now some llarp::util helper
methods to obtain unique and/or shared locks:
- `auto lock = util::unique_lock(mutex);` gets an RAII-but-also
unlockable object (std::unique_lock<T>, with T inferred from
`mutex`).
- `auto lock = util::shared_lock(mutex);` gets an RAII shared (i.e.
"reader") lock of the mutex.
- `auto lock = util::unique_locks(mutex1, mutex2, mutex3);` can be
used to atomically lock multiple mutexes at once (returning a
tuple of the locks).
This are templated on the mutex which makes them a bit more flexible
than using a concrete type: they can be used for any type of lockable
mutex, not only util::Mutex. (Some of the code here uses them for
getting locks around a std::mutex). Until C++17, using the RAII types
is painfully verbose:
```C++
// pre-C++17 - needing to figure out the mutex type here is annoying:
std::unique_lock<util::Mutex> lock(mutex);
// pre-C++17 and even more verbose (but at least the type isn't needed):
std::unique_lock<decltype(mutex)> lock(mutex);
// our compromise:
auto lock = util::unique_lock(mutex);
// C++17:
std::unique_lock lock(mutex);
```
All of these functions will also warn (under gcc or clang) if you
discard the return value. You can also do fancy things like
`auto l = util::unique_lock(mutex, std::adopt_lock)` (which lets a
lock take over an already-locked mutex).
- metrics code is gone, which also removes a big pile of code that was
only used by metrics:
- llarp::util::Scheduler
- llarp::thread::TimerQueue
- llarp::util::Stopwatch
2020-02-21 17:21:11 +00:00
|
|
|
util::Lock l(_mutex);
|
2019-06-26 21:39:29 +00:00
|
|
|
|
|
|
|
LogInfo("stopping links");
|
|
|
|
stopping = true;
|
|
|
|
|
2023-08-28 14:56:44 +00:00
|
|
|
quic.reset();
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-04-07 18:38:56 +00:00
|
|
|
LinkManager::PersistSessionUntil(const RouterID& remote, llarp_time_t until)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (stopping)
|
2019-06-26 21:39:29 +00:00
|
|
|
return;
|
|
|
|
|
De-abseil, part 2: mutex, locks, (most) time
- util::Mutex is now a std::shared_timed_mutex, which is capable of
exclusive and shared locks.
- util::Lock is still present as a std::lock_guard<util::Mutex>.
- the locking annotations are preserved, but updated to the latest
supported by clang rather than using abseil's older/deprecated ones.
- ACQUIRE_LOCK macro is gone since we don't pass mutexes by pointer into
locks anymore (WTF abseil).
- ReleasableLock is gone. Instead there are now some llarp::util helper
methods to obtain unique and/or shared locks:
- `auto lock = util::unique_lock(mutex);` gets an RAII-but-also
unlockable object (std::unique_lock<T>, with T inferred from
`mutex`).
- `auto lock = util::shared_lock(mutex);` gets an RAII shared (i.e.
"reader") lock of the mutex.
- `auto lock = util::unique_locks(mutex1, mutex2, mutex3);` can be
used to atomically lock multiple mutexes at once (returning a
tuple of the locks).
This are templated on the mutex which makes them a bit more flexible
than using a concrete type: they can be used for any type of lockable
mutex, not only util::Mutex. (Some of the code here uses them for
getting locks around a std::mutex). Until C++17, using the RAII types
is painfully verbose:
```C++
// pre-C++17 - needing to figure out the mutex type here is annoying:
std::unique_lock<util::Mutex> lock(mutex);
// pre-C++17 and even more verbose (but at least the type isn't needed):
std::unique_lock<decltype(mutex)> lock(mutex);
// our compromise:
auto lock = util::unique_lock(mutex);
// C++17:
std::unique_lock lock(mutex);
```
All of these functions will also warn (under gcc or clang) if you
discard the return value. You can also do fancy things like
`auto l = util::unique_lock(mutex, std::adopt_lock)` (which lets a
lock take over an already-locked mutex).
- metrics code is gone, which also removes a big pile of code that was
only used by metrics:
- llarp::util::Scheduler
- llarp::thread::TimerQueue
- llarp::util::Stopwatch
2020-02-21 17:21:11 +00:00
|
|
|
util::Lock l(_mutex);
|
2021-06-06 14:51:29 +00:00
|
|
|
|
|
|
|
m_PersistingSessions[remote] = std::max(until, m_PersistingSessions[remote]);
|
2023-08-28 14:56:44 +00:00
|
|
|
if (HaveClientConnection(remote))
|
2021-06-07 12:39:38 +00:00
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
// mark this as a client so we don't try to back connect
|
|
|
|
m_Clients.Upsert(remote);
|
2021-06-07 12:39:38 +00:00
|
|
|
}
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
2023-08-28 14:56:44 +00:00
|
|
|
LinkManager::NumberOfConnectedRouters(bool clients_only) const
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
size_t count{0};
|
|
|
|
for (const auto& ep : endpoints)
|
|
|
|
{
|
|
|
|
for (const auto& conn : ep.connections)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2023-08-28 17:39:40 +00:00
|
|
|
if (not (conn.second.remote_is_relay and clients_only))
|
2023-08-28 14:56:44 +00:00
|
|
|
count++;
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
2023-08-28 14:56:44 +00:00
|
|
|
}
|
2019-06-26 21:39:29 +00:00
|
|
|
|
2023-08-28 14:56:44 +00:00
|
|
|
return count;
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
LinkManager::NumberOfConnectedClients() const
|
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
return NumberOfConnectedRouters(true);
|
2019-12-03 17:03:19 +00:00
|
|
|
}
|
|
|
|
|
2019-06-26 21:39:29 +00:00
|
|
|
bool
|
2020-04-07 18:38:56 +00:00
|
|
|
LinkManager::GetRandomConnectedRouter(RouterContact& router) const
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2021-03-09 18:39:40 +00:00
|
|
|
std::unordered_map<RouterID, RouterContact> connectedRouters;
|
2019-06-26 21:39:29 +00:00
|
|
|
|
2023-08-28 20:50:06 +00:00
|
|
|
for (const auto& ep : endpoints)
|
|
|
|
{
|
|
|
|
for (const auto& [router_id, conn] : ep.connections)
|
|
|
|
{
|
|
|
|
connectedRouters.emplace(router_id, conn.remote_rc);
|
|
|
|
}
|
|
|
|
}
|
2019-06-26 21:39:29 +00:00
|
|
|
|
|
|
|
const auto sz = connectedRouters.size();
|
2020-04-07 18:38:56 +00:00
|
|
|
if (sz)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
|
|
|
auto itr = connectedRouters.begin();
|
2020-04-07 18:38:56 +00:00
|
|
|
if (sz > 1)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
|
|
|
std::advance(itr, randint() % sz);
|
|
|
|
}
|
|
|
|
|
|
|
|
router = itr->second;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-28 14:56:44 +00:00
|
|
|
//TODO: this? perhaps no longer necessary in the same way?
|
2019-06-26 21:39:29 +00:00
|
|
|
void
|
|
|
|
LinkManager::CheckPersistingSessions(llarp_time_t now)
|
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
if (stopping)
|
2019-06-26 21:39:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-28 14:56:44 +00:00
|
|
|
//TODO: do we still need this concept?
|
2020-06-04 16:00:30 +00:00
|
|
|
void
|
|
|
|
LinkManager::updatePeerDb(std::shared_ptr<PeerDb> peerDb)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-08-28 14:56:44 +00:00
|
|
|
//TODO: this
|
2019-06-26 21:39:29 +00:00
|
|
|
util::StatusObject
|
|
|
|
LinkManager::ExtractStatus() const
|
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
return {};
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2023-08-28 14:56:44 +00:00
|
|
|
LinkManager::Init(I_RCLookupHandler* rcLookup)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2020-04-07 18:38:56 +00:00
|
|
|
stopping = false;
|
2023-08-28 14:56:44 +00:00
|
|
|
_rcLookup = rcLookup;
|
|
|
|
_nodedb = router->nodedb();
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-28 14:56:44 +00:00
|
|
|
void
|
|
|
|
LinkManager::Connect(RouterID router)
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
auto fn = [this](const RouterID& r, const RouterContact* const rc, const RCRequestResult res){
|
|
|
|
if (res == RCRequestResult::Success)
|
|
|
|
Connect(*rc);
|
|
|
|
/* TODO:
|
|
|
|
else
|
|
|
|
RC lookup failure callback here
|
|
|
|
*/
|
|
|
|
};
|
2019-06-26 21:39:29 +00:00
|
|
|
|
2023-08-28 14:56:44 +00:00
|
|
|
_rcLookup->GetRC(router, fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function assumes the RC has already had its signature verified and connection is allowed.
|
|
|
|
void
|
|
|
|
LinkManager::Connect(RouterContact rc)
|
|
|
|
{
|
|
|
|
//TODO: connection failed callback
|
|
|
|
if (HaveConnection(rc.pubkey))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// RC shouldn't be valid if this is the case, but may as well sanity check...
|
|
|
|
//TODO: connection failed callback
|
|
|
|
if (rc.addrs.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
//TODO: connection failed callback
|
|
|
|
auto* ep = GetCompatibleLink(rc);
|
|
|
|
if (ep == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
//TODO: connection established/failed callbacks
|
|
|
|
oxen::quic::stream_data_callback stream_cb = [this](oxen::quic::Stream& stream, bstring_view packet){ HandleIncomingControlMessage(stream, packet); };
|
|
|
|
|
|
|
|
//TODO: once "compatible link" cares about address, actually choose addr to connect to
|
|
|
|
// based on which one is compatible with the link we chose. For now, just use
|
|
|
|
// the first one.
|
|
|
|
auto& selected = rc.addrs[0];
|
2023-08-28 20:50:06 +00:00
|
|
|
oxen::quic::opt::remote_addr remote{selected.IPString(), selected.port};
|
2023-08-28 14:56:44 +00:00
|
|
|
//TODO: confirm remote end is using the expected pubkey (RouterID).
|
|
|
|
//TODO: ALPN for "client" vs "relay" (could just be set on endpoint creation)
|
2023-08-28 23:20:57 +00:00
|
|
|
//TODO: does connect() inherit the endpoint's datagram data callback, and do we want it to if so?
|
|
|
|
auto conn_interface = ep->endpoint->connect(remote, stream_cb, tls_creds);
|
2023-08-28 14:56:44 +00:00
|
|
|
|
|
|
|
std::shared_ptr<oxen::quic::Stream> stream = conn_interface->get_new_stream();
|
|
|
|
|
|
|
|
llarp::link::Connection conn;
|
|
|
|
conn.conn = conn_interface;
|
|
|
|
conn.control_stream = stream;
|
|
|
|
conn.remote_rc = rc;
|
|
|
|
conn.inbound = false;
|
|
|
|
conn.remote_is_relay = true;
|
|
|
|
|
|
|
|
ep->connections[rc.pubkey] = std::move(conn);
|
|
|
|
ep->connid_map[conn_interface->scid()] = rc.pubkey;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
LinkManager::ConnectToRandomRouters(int numDesired)
|
|
|
|
{
|
|
|
|
std::set<RouterID> exclude;
|
2023-08-28 20:50:06 +00:00
|
|
|
auto remainingDesired = numDesired;
|
2023-08-28 14:56:44 +00:00
|
|
|
do
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
auto filter = [exclude](const auto& rc) -> bool { return exclude.count(rc.pubkey) == 0; };
|
|
|
|
|
|
|
|
RouterContact other;
|
|
|
|
if (const auto maybe = _nodedb->GetRandom(filter))
|
2019-06-26 21:39:29 +00:00
|
|
|
{
|
2023-08-28 14:56:44 +00:00
|
|
|
other = *maybe;
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
2023-08-28 14:56:44 +00:00
|
|
|
else
|
|
|
|
break;
|
|
|
|
|
|
|
|
exclude.insert(other.pubkey);
|
|
|
|
if (not _rcLookup->SessionIsAllowed(other.pubkey))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Connect(other);
|
|
|
|
--remainingDesired;
|
|
|
|
} while (remainingDesired > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
LinkManager::HandleIncomingDataMessage(oxen::quic::dgram_interface& dgi, bstring dgram)
|
|
|
|
{
|
|
|
|
//TODO: this
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
LinkManager::HandleIncomingControlMessage(oxen::quic::Stream& stream, bstring_view packet)
|
|
|
|
{
|
|
|
|
//TODO: this
|
2019-06-26 21:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace llarp
|