lokinet/llarp/link/server.hpp

270 lines
7.7 KiB
C++
Raw Normal View History

#pragma once
#include <llarp/crypto/types.hpp>
#include <llarp/ev/ev.hpp>
#include "session.hpp"
#include <llarp/net/sock_addr.hpp>
#include <llarp/router_contact.hpp>
#include <llarp/util/status.hpp>
#include <llarp/util/thread/threading.hpp>
#include <llarp/config/key_manager.hpp>
2018-12-12 01:32:10 +00:00
2018-09-07 20:36:06 +00:00
#include <list>
2019-04-02 09:03:53 +00:00
#include <memory>
2018-12-12 01:32:10 +00:00
#include <unordered_map>
2018-09-03 12:08:02 +00:00
namespace llarp
{
/// handle a link layer message. this allows for the message to be handled by "upper layers"
///
/// currently called from iwp::Session when messages are sent or received.
using LinkMessageHandler = std::function<bool(ILinkSession*, const llarp_buffer_t&)>;
/// sign a buffer with identity key. this function should take the given `llarp_buffer_t` and
/// sign it, prividing the signature in the out variable `Signature&`.
///
/// currently called from iwp::Session for signing LIMs (link introduction messages)
using SignBufferFunc = std::function<bool(Signature&, const llarp_buffer_t&)>;
/// handle connection timeout
///
/// currently called from ILinkLayer::Pump() when an unestablished session times out
using TimeoutHandler = std::function<void(ILinkSession*)>;
/// get our RC
///
/// currently called by iwp::Session to include as part of a LIM (link introduction message)
using GetRCFunc = std::function<const llarp::RouterContact&(void)>;
/// handler of session established
2019-02-27 12:55:26 +00:00
/// return false to reject
/// return true to accept
///
/// currently called in iwp::Session when a valid LIM is received.
2020-06-11 19:02:34 +00:00
using SessionEstablishedHandler = std::function<bool(ILinkSession*, bool)>;
/// f(new, old)
/// handler of session renegotiation
/// returns true if the new rc is valid
/// returns false otherwise and the session is terminated
///
/// currently called from iwp::Session when we receive a renegotiation LIM
using SessionRenegotiateHandler = std::function<bool(llarp::RouterContact, llarp::RouterContact)>;
/// handles close of all sessions with pubkey
///
/// Note that this handler is called while m_AuthedLinksMutex is held
///
/// currently called from iwp::ILinkSession when a previously established session times out
using SessionClosedHandler = std::function<void(llarp::RouterID)>;
/// notifies router that a link session has ended its pump and we should flush
/// messages to upper layers
///
/// currently called at the end of every iwp::Session::Pump() call
using PumpDoneHandler = std::function<void(void)>;
using Work_t = std::function<void(void)>;
/// queue work to worker thread
using WorkerFunc_t = std::function<void(Work_t)>;
/// before connection hook, called before we try connecting via outbound link
using BeforeConnectFunc_t = std::function<void(llarp::RouterContact)>;
2019-04-19 15:10:26 +00:00
struct ILinkLayer
{
ILinkLayer(
std::shared_ptr<KeyManager> keyManager,
GetRCFunc getrc,
LinkMessageHandler handler,
SignBufferFunc signFunc,
BeforeConnectFunc_t before,
SessionEstablishedHandler sessionEstablish,
SessionRenegotiateHandler renegotiate,
TimeoutHandler timeout,
SessionClosedHandler closed,
PumpDoneHandler pumpDone,
WorkerFunc_t doWork);
virtual ~ILinkLayer() = default;
2018-10-29 16:48:36 +00:00
/// get current time via event loop
llarp_time_t
Now() const;
bool
HasSessionTo(const RouterID& pk);
void
ForEachSession(std::function<void(const ILinkSession*)> visit, bool randomize = false) const
EXCLUDES(m_AuthedLinksMutex);
void
ForEachSession(std::function<void(ILinkSession*)> visit) EXCLUDES(m_AuthedLinksMutex);
void
UnmapAddr(const SockAddr& addr);
2019-01-03 21:10:40 +00:00
void
Replace libuv with uvw & related refactoring - removes all the llarp_ev_* functions, replacing with methods/classes/functions in the llarp namespace. - banish ev/ev.h to the void - Passes various things by const lvalue ref, especially shared_ptr's that don't need to be copied (to avoid an atomic refcount increment/decrement). - Add a llarp::UDPHandle abstract class for UDP handling - Removes the UDP tick handler; code that needs tick can just do a separate handler on the event loop outside the UDP socket. - Adds an "OwnedBuffer" which owns its own memory but is implicitly convertible to a llarp_buffer_t. This is mostly needed to take over ownership of buffers from uvw without copying them as, currently, uvw does its own allocation (pending some open upstream issues/PRs). - Logic: - add `make_caller`/`call_forever`/`call_every` utility functions to abstract Call wrapping and dependent timed tasks. - Add inLogicThread() so that code can tell its inside the logic thread (typically for debugging assertions). - get rid of janky integer returns and dealing with cancellations on call_later: the other methods added here and the event loop code remove the need for them. - Event loop: - redo everything with uvw instead of libuv - rename EventLoopWakeup::Wakeup to EventLoopWakeup::Trigger to better reflect what it does. - add EventLoopRepeater for repeated events, and replace the code that reschedules itself every time it is called with a repeater. - Split up `EventLoop::run()` into a non-virtual base method and abstract `run_loop()` methods; the base method does a couple extra setup/teardown things that don't need to be in the derived class. - udp_listen is replaced with ev->udp(...) which returns a new UDPHandle object rather that needing gross C-style-but-not-actually-C-compatible structs. - Remove unused register_poll_fd_(un)readable - Use shared_ptr for EventLoopWakeup rather than returning a raw pointer; uvw lets us not have to worry about having the event loop class maintain ownership of it. - Add factory EventLoop::create() function to create a default (uvw-based) event loop (previously this was one of the llarp_ev_blahblah unnamespaced functions). - ev_libuv: this is mostly rewritten; all of the glue code/structs, in particular, are gone as they are no longer needed with uvw. - DNS: - Rename DnsHandler to DnsInterceptor to better describe what it does (this is the code that intercepts all DNS to the tun IP range for Android). - endpoint: - remove unused "isolated network" code - remove distinct (but actually always the same) variables for router/endpoint logic objects - llarp_buffer_t - make constructors type-safe against being called with points to non-size-1 values - tun packet reading: - read all available packets off the device/file descriptor; previously we were reading one packet at a time then returning to the event loop to poll again. - ReadNextPacket() now returns a 0-size packet if the read would block (so that we can implement the previous point). - ReadNextPacket() now throws on I/O error - Miscellaneous code cleanups/simplifications
2021-03-02 02:06:20 +00:00
SendTo_LL(const SockAddr& to, const llarp_buffer_t& pkt);
2019-01-03 21:10:40 +00:00
2019-08-07 16:33:29 +00:00
virtual bool
Configure(AbstractRouter* loop, const std::string& ifname, int af, uint16_t port);
virtual std::shared_ptr<ILinkSession>
NewOutboundSession(const RouterContact& rc, const AddressInfo& ai) = 0;
/// fetch a session by the identity pubkey it claims
std::shared_ptr<ILinkSession>
FindSessionByPubkey(RouterID pk);
virtual void
Pump();
virtual void
2020-05-06 20:38:44 +00:00
RecvFrom(const SockAddr& from, ILinkSession::Packet_t pkt) = 0;
bool
PickAddress(const RouterContact& rc, AddressInfo& picked) const;
bool
2018-12-19 17:48:29 +00:00
TryEstablishTo(RouterContact rc);
2019-09-05 17:39:09 +00:00
bool
2021-03-02 22:27:35 +00:00
Start();
2019-08-07 16:33:29 +00:00
virtual void
Stop();
virtual const char*
Name() const = 0;
2019-02-18 19:44:41 +00:00
util::StatusObject
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
ExtractStatus() const EXCLUDES(m_AuthedLinksMutex);
void
CloseSessionTo(const RouterID& remote);
void
KeepAliveSessionTo(const RouterID& remote);
2019-08-07 16:33:29 +00:00
virtual bool
SendTo(
const RouterID& remote,
const llarp_buffer_t& buf,
ILinkSession::CompletionHandler completed,
uint16_t priority);
2019-08-07 16:33:29 +00:00
virtual bool
GetOurAddressInfo(AddressInfo& addr) const;
bool
VisitSessionByPubkey(const RouterID& pk, std::function<bool(ILinkSession*)> visit)
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
EXCLUDES(m_AuthedLinksMutex);
virtual uint16_t
Rank() const = 0;
const byte_t*
TransportPubKey() const;
const SecretKey&
RouterEncryptionSecret() const
{
return m_RouterEncSecret;
}
const SecretKey&
TransportSecretKey() const;
2019-01-05 13:45:05 +00:00
bool
IsCompatable(const llarp::RouterContact& other) const
{
const std::string us = Name();
for (const auto& ai : other.addrs)
if (ai.dialect == us)
2019-01-05 13:45:05 +00:00
return true;
return false;
}
bool
MapAddr(const RouterID& pk, ILinkSession* s);
2018-09-04 19:15:06 +00:00
2019-02-18 19:44:41 +00:00
void
Tick(llarp_time_t now);
2018-09-06 20:31:58 +00:00
LinkMessageHandler HandleMessage;
TimeoutHandler HandleTimeout;
SignBufferFunc Sign;
GetRCFunc GetOurRC;
BeforeConnectFunc_t BeforeConnect;
SessionEstablishedHandler SessionEstablished;
SessionClosedHandler SessionClosed;
SessionRenegotiateHandler SessionRenegotiate;
PumpDoneHandler PumpDone;
std::shared_ptr<KeyManager> keyManager;
WorkerFunc_t QueueWork;
bool
operator<(const ILinkLayer& other) const
{
return Rank() < other.Rank() || Name() < other.Name() || m_ourAddr < other.m_ourAddr;
}
2019-01-07 16:35:25 +00:00
/// called by link session to remove a pending session who is timed out
2019-03-11 13:01:43 +00:00
// void
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
// RemovePending(ILinkSession* s) EXCLUDES(m_PendingMutex);
2019-01-07 16:35:25 +00:00
2019-12-03 17:03:19 +00:00
/// count the number of sessions that are yet to be fully connected
size_t
NumberOfPendingSessions() const
{
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
Lock_t lock(m_PendingMutex);
2019-12-03 17:03:19 +00:00
return m_Pending.size();
}
// Returns the file description of the UDP server, if available.
std::optional<int>
GetUDPFD() const;
// Gets a pointer to the router owning us.
AbstractRouter*
Router() const
{
return m_Router;
}
private:
const SecretKey& m_RouterEncSecret;
protected:
#ifdef TRACY_ENABLE
using Lock_t = std::lock_guard<LockableBase(std::mutex)>;
using Mutex_t = std::mutex;
#else
using Lock_t = util::NullLock;
using Mutex_t = util::NullMutex;
#endif
2019-01-07 12:47:57 +00:00
bool
PutSession(const std::shared_ptr<ILinkSession>& s);
AbstractRouter* m_Router;
SockAddr m_ourAddr;
Replace libuv with uvw & related refactoring - removes all the llarp_ev_* functions, replacing with methods/classes/functions in the llarp namespace. - banish ev/ev.h to the void - Passes various things by const lvalue ref, especially shared_ptr's that don't need to be copied (to avoid an atomic refcount increment/decrement). - Add a llarp::UDPHandle abstract class for UDP handling - Removes the UDP tick handler; code that needs tick can just do a separate handler on the event loop outside the UDP socket. - Adds an "OwnedBuffer" which owns its own memory but is implicitly convertible to a llarp_buffer_t. This is mostly needed to take over ownership of buffers from uvw without copying them as, currently, uvw does its own allocation (pending some open upstream issues/PRs). - Logic: - add `make_caller`/`call_forever`/`call_every` utility functions to abstract Call wrapping and dependent timed tasks. - Add inLogicThread() so that code can tell its inside the logic thread (typically for debugging assertions). - get rid of janky integer returns and dealing with cancellations on call_later: the other methods added here and the event loop code remove the need for them. - Event loop: - redo everything with uvw instead of libuv - rename EventLoopWakeup::Wakeup to EventLoopWakeup::Trigger to better reflect what it does. - add EventLoopRepeater for repeated events, and replace the code that reschedules itself every time it is called with a repeater. - Split up `EventLoop::run()` into a non-virtual base method and abstract `run_loop()` methods; the base method does a couple extra setup/teardown things that don't need to be in the derived class. - udp_listen is replaced with ev->udp(...) which returns a new UDPHandle object rather that needing gross C-style-but-not-actually-C-compatible structs. - Remove unused register_poll_fd_(un)readable - Use shared_ptr for EventLoopWakeup rather than returning a raw pointer; uvw lets us not have to worry about having the event loop class maintain ownership of it. - Add factory EventLoop::create() function to create a default (uvw-based) event loop (previously this was one of the llarp_ev_blahblah unnamespaced functions). - ev_libuv: this is mostly rewritten; all of the glue code/structs, in particular, are gone as they are no longer needed with uvw. - DNS: - Rename DnsHandler to DnsInterceptor to better describe what it does (this is the code that intercepts all DNS to the tun IP range for Android). - endpoint: - remove unused "isolated network" code - remove distinct (but actually always the same) variables for router/endpoint logic objects - llarp_buffer_t - make constructors type-safe against being called with points to non-size-1 values - tun packet reading: - read all available packets off the device/file descriptor; previously we were reading one packet at a time then returning to the event loop to poll again. - ReadNextPacket() now returns a 0-size packet if the read would block (so that we can implement the previous point). - ReadNextPacket() now throws on I/O error - Miscellaneous code cleanups/simplifications
2021-03-02 02:06:20 +00:00
std::shared_ptr<llarp::UDPHandle> m_udp;
SecretKey m_SecretKey;
2018-09-07 20:36:06 +00:00
using AuthedLinks = std::unordered_multimap<RouterID, std::shared_ptr<ILinkSession>>;
using Pending = std::unordered_map<SockAddr, std::shared_ptr<ILinkSession>>;
mutable DECLARE_LOCK(Mutex_t, m_AuthedLinksMutex, ACQUIRED_BEFORE(m_PendingMutex));
2019-03-29 14:03:07 +00:00
AuthedLinks m_AuthedLinks GUARDED_BY(m_AuthedLinksMutex);
mutable DECLARE_LOCK(Mutex_t, m_PendingMutex, ACQUIRED_AFTER(m_AuthedLinksMutex));
2019-03-29 14:03:07 +00:00
Pending m_Pending GUARDED_BY(m_PendingMutex);
std::unordered_map<SockAddr, RouterID> m_AuthedAddrs;
std::unordered_map<SockAddr, llarp_time_t> m_RecentlyClosed;
Replace libuv with uvw & related refactoring - removes all the llarp_ev_* functions, replacing with methods/classes/functions in the llarp namespace. - banish ev/ev.h to the void - Passes various things by const lvalue ref, especially shared_ptr's that don't need to be copied (to avoid an atomic refcount increment/decrement). - Add a llarp::UDPHandle abstract class for UDP handling - Removes the UDP tick handler; code that needs tick can just do a separate handler on the event loop outside the UDP socket. - Adds an "OwnedBuffer" which owns its own memory but is implicitly convertible to a llarp_buffer_t. This is mostly needed to take over ownership of buffers from uvw without copying them as, currently, uvw does its own allocation (pending some open upstream issues/PRs). - Logic: - add `make_caller`/`call_forever`/`call_every` utility functions to abstract Call wrapping and dependent timed tasks. - Add inLogicThread() so that code can tell its inside the logic thread (typically for debugging assertions). - get rid of janky integer returns and dealing with cancellations on call_later: the other methods added here and the event loop code remove the need for them. - Event loop: - redo everything with uvw instead of libuv - rename EventLoopWakeup::Wakeup to EventLoopWakeup::Trigger to better reflect what it does. - add EventLoopRepeater for repeated events, and replace the code that reschedules itself every time it is called with a repeater. - Split up `EventLoop::run()` into a non-virtual base method and abstract `run_loop()` methods; the base method does a couple extra setup/teardown things that don't need to be in the derived class. - udp_listen is replaced with ev->udp(...) which returns a new UDPHandle object rather that needing gross C-style-but-not-actually-C-compatible structs. - Remove unused register_poll_fd_(un)readable - Use shared_ptr for EventLoopWakeup rather than returning a raw pointer; uvw lets us not have to worry about having the event loop class maintain ownership of it. - Add factory EventLoop::create() function to create a default (uvw-based) event loop (previously this was one of the llarp_ev_blahblah unnamespaced functions). - ev_libuv: this is mostly rewritten; all of the glue code/structs, in particular, are gone as they are no longer needed with uvw. - DNS: - Rename DnsHandler to DnsInterceptor to better describe what it does (this is the code that intercepts all DNS to the tun IP range for Android). - endpoint: - remove unused "isolated network" code - remove distinct (but actually always the same) variables for router/endpoint logic objects - llarp_buffer_t - make constructors type-safe against being called with points to non-size-1 values - tun packet reading: - read all available packets off the device/file descriptor; previously we were reading one packet at a time then returning to the event loop to poll again. - ReadNextPacket() now returns a 0-size packet if the read would block (so that we can implement the previous point). - ReadNextPacket() now throws on I/O error - Miscellaneous code cleanups/simplifications
2021-03-02 02:06:20 +00:00
private:
std::shared_ptr<int> m_repeater_keepalive;
};
2019-05-15 15:54:26 +00:00
using LinkLayer_ptr = std::shared_ptr<ILinkLayer>;
} // namespace llarp