Merge pull request #1646 from oxen-io/dev

v0.9.1
pull/1766/head v0.9.1
Jeff 3 years ago committed by GitHub
commit 9564e750d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -21,6 +21,7 @@ local debian_pipeline(name, image,
werror=true,
cmake_extra='',
extra_cmds=[],
jobs=6,
loki_repo=false,
allow_fail=false) = {
kind: 'pipeline',
@ -55,7 +56,7 @@ local debian_pipeline(name, image,
(if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') +
'-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') +
cmake_extra,
'ninja -v',
'ninja -j' + jobs + ' -v',
'../contrib/ci/drone-gdb.sh ./test/testAll --use-colour yes',
] + extra_cmds,
}
@ -93,6 +94,7 @@ local windows_cross_pipeline(name, image,
cmake_extra='',
toolchain='32',
extra_cmds=[],
jobs=6,
allow_fail=false) = {
kind: 'pipeline',
type: 'docker',
@ -121,7 +123,7 @@ local windows_cross_pipeline(name, image,
(if lto then '' else '-DWITH_LTO=OFF ') +
"-DBUILD_STATIC_DEPS=ON -DDOWNLOAD_SODIUM=ON -DBUILD_PACKAGE=ON -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -DNATIVE_BUILD=OFF -DSTATIC_LINK=ON" +
cmake_extra,
'ninja -v package',
'ninja -j' + jobs + ' -v package',
] + extra_cmds,
}
],
@ -178,7 +180,13 @@ local deb_builder(image, distro, distro_branch, arch='amd64', loki_repo=true) =
// Macos build
local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra_cmds=[], allow_fail=false) = {
local mac_builder(name,
build_type='Release',
werror=true,
cmake_extra='',
extra_cmds=[],
jobs=6,
allow_fail=false) = {
kind: 'pipeline',
type: 'exec',
name: name,
@ -198,7 +206,7 @@ local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra
'cd build',
'cmake .. -G Ninja -DCMAKE_CXX_FLAGS=-fcolor-diagnostics -DCMAKE_BUILD_TYPE='+build_type+' ' +
(if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + cmake_extra,
'ninja -v',
'ninja -j' + jobs + ' -v',
'./test/testAll --use-colour yes',
] + extra_cmds,
}
@ -233,8 +241,8 @@ local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra
cmake_extra='-DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8', loki_repo=true),
// ARM builds (ARM64 and armhf)
debian_pipeline("Debian sid (ARM64)", "debian:sid", arch="arm64"),
debian_pipeline("Debian buster (armhf)", "arm32v7/debian:buster", arch="arm64", cmake_extra='-DDOWNLOAD_SODIUM=ON'),
debian_pipeline("Debian sid (ARM64)", "debian:sid", arch="arm64", jobs=4),
debian_pipeline("Debian buster (armhf)", "arm32v7/debian:buster", arch="arm64", cmake_extra='-DDOWNLOAD_SODIUM=ON', jobs=4),
// Static armhf build (gets uploaded)
debian_pipeline("Static (buster armhf)", "arm32v7/debian:buster", arch="arm64", deps='g++ python3-dev automake libtool',
cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' +
@ -243,7 +251,8 @@ local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra
extra_cmds=[
'../contrib/ci/drone-check-static-libs.sh',
'UPLOAD_OS=linux-armhf ../contrib/ci/drone-static-upload.sh'
]),
],
jobs=4),
// android apk builder
apk_builder("android apk", "registry.oxen.rocks/lokinet-ci-android", extra_cmds=['UPLOAD_OS=anrdoid ../contrib/ci/drone-static-upload.sh']),

@ -16,11 +16,11 @@ if(CCACHE_PROGRAM)
endif()
project(lokinet
VERSION 0.9.0
VERSION 0.9.1
DESCRIPTION "lokinet - IP packet onion router"
LANGUAGES C CXX)
set(RELEASE_MOTTO "Proof of soon" CACHE STRING "Release motto")
set(RELEASE_MOTTO "A Series of Tubes" CACHE STRING "Release motto")
add_definitions(-DLLARP_VERSION_MAJOR=${lokinet_VERSION_MAJOR})
add_definitions(-DLLARP_VERSION_MINOR=${lokinet_VERSION_MINOR})

@ -114,11 +114,17 @@ public class LokinetDaemon extends VpnService
builder.setMtu(1500);
String[] parts = ourRange.split("/");
String ourIP = parts[0];
String ourIPv4 = parts[0];
int ourMask = Integer.parseInt(parts[1]);
builder.addAddress(ourIP, ourMask);
// set ip4
builder.addAddress(ourIPv4, ourMask);
builder.addRoute("0.0.0.0", 0);
// set ip6
// TODO: convert ipv4 to fd00::/8 range for ipv6
// builder.addAddress(ourIPv6, ourMask + 96);
// builder.addRoute("::", 0);
builder.addDnsServer(upstreamDNS);
builder.setSession("Lokinet");
builder.setConfigureIntent(null);
@ -134,24 +140,10 @@ public class LokinetDaemon extends VpnService
InjectVPNFD();
if (!Configure(config))
{
//TODO: close vpn FD if this fails, either on native side, or here if possible
Log.e(LOG_TAG, "failed to configure daemon");
return START_NOT_STICKY;
}
m_UDPSocket = GetUDPSocket();
if (m_UDPSocket <= 0)
{
Log.e(LOG_TAG, "failed to get proper UDP handle from daemon, aborting.");
return START_NOT_STICKY;
}
protect(m_UDPSocket);
new Thread(() -> {
Configure(config);
m_UDPSocket = GetUDPSocket();
protect(m_UDPSocket);
Mainloop();
}).start();

@ -25,14 +25,14 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${CMAKE_BINARY_DIR}/lokinet-g
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui)
install(PROGRAMS ${TUNTAP_EXE} DESTINATION bin)
install(FILES ${BOOTSTRAP_FILE} DESTINATION share)
install(PROGRAMS ${TUNTAP_EXE} DESTINATION bin COMPONENT tuntap)
install(FILES ${BOOTSTRAP_FILE} DESTINATION share COMPONENT lokinet)
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet")
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/win32-setup/lokinet.ico")
set(CPACK_NSIS_DEFINES "RequestExecutionLevel admin")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '$INSTDIR\\\\bin\\\\tuntap-install.exe /S'\\nExecWait '$INSTDIR\\\\bin\\\\lokinet.exe --install'\\nExecWait 'sc failure lokinet reset= 60 actions= restart/1000'\\nExecWait '$INSTDIR\\\\bin\\\\lokinet.exe -g C:\\\\ProgramData\\\\lokinet\\\\lokinet.ini'\\nCopyFiles '$INSTDIR\\\\share\\\\bootstrap.signed' C:\\\\ProgramData\\\\lokinet\\\\bootstrap.signed\\nExecWait '$INSTDIR\\\\bin\\\\lokinet-bootstrap.exe'")
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ifFileExists $INSTDIR\\\\bin\\\\tuntap-install.exe 0 +2\\nExecWait '$INSTDIR\\\\bin\\\\tuntap-install.exe /S'\\nExecWait '$INSTDIR\\\\bin\\\\lokinet.exe --install'\\nExecWait 'sc failure lokinet reset= 60 actions= restart/1000'\\nExecWait '$INSTDIR\\\\bin\\\\lokinet.exe -g C:\\\\ProgramData\\\\lokinet\\\\lokinet.ini'\\nCopyFiles '$INSTDIR\\\\share\\\\bootstrap.signed' C:\\\\ProgramData\\\\lokinet\\\\bootstrap.signed\\nExecWait '$INSTDIR\\\\bin\\\\lokinet-bootstrap.exe'")
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "ExecWait 'net stop lokinet'\\nExecWait 'taskkill /f /t /im lokinet-gui.exe'\\nExecWait '$INSTDIR\\\\bin\\\\lokinet.exe --remove'\\nRMDir /r /REBOOTOK C:\\\\ProgramData\\\\lokinet")
set(CPACK_NSIS_CREATE_ICONS_EXTRA
"CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Lokinet.lnk' '$INSTDIR\\\\share\\\\gui\\\\lokinet-gui.exe'"
@ -40,3 +40,6 @@ set(CPACK_NSIS_CREATE_ICONS_EXTRA
set(CPACK_NSIS_DELETE_ICONS_EXTRA
"Delete '$SMPROGRAMS\\\\$START_MENU\\\\Lokinet.lnk'"
)
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")

@ -7,6 +7,32 @@ import time
import zmq
geo = None
try:
import GeoIP
geo = GeoIP.open("/usr/share/GeoIP/GeoIP.dat", GeoIP.GEOIP_STANDARD)
except Exception as ex:
print('no geoip: {}'.format(ex))
time.sleep(1)
def ip_to_flag(ip):
"""
convert an ip to a flag emoji
"""
# bail if no geoip available
if not geo:
return ''
# trim off excess ipv6 jizz
ip = ip.replace("::ffff:", "")
# get the country code
cc = geo.country_code_by_addr(ip)
# Unicode flag sequences are just country codes transposed into the REGIONAL
# INDICATOR SYMBOL LETTER A ... Z range (U+1F1E6 ... U+1F1FF):
flag = ''.join(chr(0x1f1e6 + ord(i) - ord('A')) for i in cc)
return '({}) {}'.format(cc, flag)
class Monitor:
@ -26,7 +52,7 @@ class Monitor:
self._rpc_socket.connect(url)
self._speed_samples = [(0,0,0,0)] * self._sample_size
self._run = True
def rpc(self, method):
self._rpc_socket.send_multipart([method.encode(), b'lokinetmon'+method.encode()])
if not self._rpc_socket.poll(timeout=50):
@ -34,10 +60,10 @@ class Monitor:
reply = self._rpc_socket.recv_multipart()
if len(reply) >= 3 and reply[0:2] == [b'REPLY', b'lokinetmon'+method.encode()]:
return reply[2].decode()
def _close(self):
self._rpc_socket.close(linger=0)
self._run = False
self._run = False
curses.endwin()
def update_data(self):
@ -62,7 +88,11 @@ class Monitor:
y_pos += 1
self.win.addstr("me -> ")
for hop in path["hops"]:
self.win.addstr(" {} ->".format(hop["router"][:4]))
hopstr = hop['router'][:4]
if 'ip' in hop:
hopstr += ' {}'.format(ip_to_flag(hop['ip']))
self.win.addstr(" {} ->".format(hopstr))
self.win.addstr(" [{} ms latency]".format(path["intro"]["latency"]))
self.win.addstr(" [{} until expire]".format(self.time_to(path["expiresAt"])))
if path["expiresSoon"]:
@ -174,7 +204,7 @@ class Monitor:
barstr = "#" * (samp - badsamp)
pad = " " * (maxsamp - samp)
return pad, barstr, '#' * badsamp
def display_speedgraph(self, y_pos, maxsz=40):
""" display global speed graph """
txmax, rxmax = 1024, 1024
@ -260,9 +290,13 @@ class Monitor:
self.win.move(y_pos, 1)
self.txrate += sess["txRateCurrent"]
self.rxrate += sess["rxRateCurrent"]
addr = sess['remoteAddr']
if geo:
ip = addr.split(':')[0]
addr += '\t{}'.format(ip_to_flag(ip))
self.win.addstr(
"{}\t[{}\ttx]\t[{}\trx]".format(
sess["remoteAddr"], self.speed_of(sess["txRateCurrent"]), self.speed_of(sess["rxRateCurrent"])
addr, self.speed_of(sess["txRateCurrent"]), self.speed_of(sess["rxRateCurrent"])
)
)
if (sess['txMsgQueueSize'] or 0) > 1:
@ -333,7 +367,7 @@ class Monitor:
self.version = json.loads(self.rpc("llarp.version"))['result']['version']
except:
self.version = None
while self._run:
if self.update_data():
self.win.box()

@ -1,6 +1,20 @@
To be put at `/usr/lib/systemd/resolved.conf.d/lokinet.conf` for distro use and `/etc/systemd/resolved.conf.d/lokinet.conf` for local admin use.
Lokinet now talks to systemd directly via sdbus to set up DNS, but in order for this to work the
user running lokinet (assumed `_lokinet` in these example files) needs permission to set dns servers
and domains.
To make use of it:
To set up the permissions:
- If lokinet is running as some user other than `_lokinet` the change the `_lokinet` username inside
`lokinet.rules` and `lokinet.pkla`.
- If on a Debian or Debian-derived distribution (such as Ubuntu) using polkit 105,
copy `lokinet.pkla` to `/var/lib/polkit-1/localauthority/10-vendor.d/lokinet.pkla` (for a distro
install) or `/etc/polkit-1/localauthority.conf.d/` (for a local install).
- Copy `lokinet.rules` to `/usr/share/polkit-1/rules.d/` (distro install) or `/etc/polkit-1/rules.d`
(local install).
Make use of it by switching to systemd-resolved:
```
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
sudo systemctl enable --now systemd-resolved

@ -1,3 +0,0 @@
[Resolve]
DNS=127.3.2.1
Domains=~loki ~snode

@ -0,0 +1,4 @@
[Allow lokinet to set DNS settings]
Identity=unix-user:_lokinet
Action=org.freedesktop.resolve1.set-dns-servers;org.freedesktop.resolve1.set-domains
ResultAny=yes

@ -0,0 +1,9 @@
/* Allow lokinet to set DNS settings */
polkit.addRule(function(action, subject) {
if ((action.id == "org.freedesktop.resolve1.set-dns-servers" ||
action.id == "org.freedesktop.resolve1.set-domains") &&
subject.user == "_lokinet") {
return polkit.Result.YES;
}
});

@ -12,6 +12,7 @@ cmake \
-DBUILD_PACKAGE=ON \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=ON \
-DWITH_TESTS=OFF \
-DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \

@ -24,11 +24,11 @@ BE(x) is bittorrent encode x
BD(x) is bittorrent decode x
{ a: b, y: z } is a dictionary with two keys a and y
who's values are b and z respectively
whose values are b and z respectively
[ a, b, c ... ] is a list containing a b c and more items in that order
"<description>" is a bytestring who's contents and length is described by the
"<description>" is a bytestring whose contents and length is described by the
quoted value <description>
"<value>" * N is a bytestring containing the <value> concatenated N times.
@ -354,8 +354,8 @@ hop length.
link relay commit record (LRCR)
record requesting relaying messages for 600 seconds to router
on network who's i is equal to RC.k and decrypt data any messages using
PKE(n, rc.p, c) as symettric key for encryption and decryption.
on network whose i is equal to RC.k and decrypt data any messages using
PKE(n, rc.p, c) as symmetric key for encryption and decryption.
if l is provided and is less than 600 and greater than 10 then that lifespan
is used (in seconds) instead of 600 seconds.
@ -845,8 +845,8 @@ X is parsed as a list of IP packet buffers.
for each ip packet the source addresss is extracted and sent on the
appropriate network interface.
When we recieve an ip packet from the internet to an exit address, we put it
into a TITM, and send it downstream the corrisponding path in an LRDM.
When we receive an ip packet from the internet to an exit address, we put it
into a TITM, and send it downstream the corresponding path in an LRDM.
update exit path message (UXPM)

@ -186,6 +186,7 @@ add_library(liblokinet
router/rc_gossiper.cpp
router/router.cpp
router/route_poker.cpp
router/systemd_resolved.cpp
routing/dht_message.cpp
routing/message_parser.cpp
routing/path_confirm_message.cpp
@ -248,12 +249,16 @@ if(BUILD_LIBLOKINET)
include(GNUInstallDirs)
add_library(lokinet-shared SHARED lokinet_shared.cpp)
target_link_libraries(lokinet-shared PUBLIC liblokinet)
install(TARGETS lokinet-shared LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
if(WIN32)
set(CMAKE_SHARED_LIBRARY_PREFIX_CXX "")
target_link_libraries(lokinet-shared PUBLIC ws2_32 iphlpapi -fstack-protector)
endif()
set_target_properties(lokinet-shared PROPERTIES OUTPUT_NAME lokinet)
if(WIN32)
target_link_libraries(lokinet-shared PUBLIC ws2_32 iphlpapi -fstack-protector)
install(TARGETS lokinet-shared DESTINATION bin COMPONENT liblokinet)
else()
install(TARGETS lokinet-shared LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT liblokinet)
endif()
add_log_tag(lokinet-shared)
endif()

@ -659,6 +659,21 @@ namespace llarp
m_SRVRecords.push_back(std::move(newSRV));
});
conf.defineOption<int>(
"network",
"path-alignment-timeout",
ClientOnly,
Comment{
"time in seconds how long to wait for a path to align to pivot routers",
"if not provided a sensible default will be used",
},
[this](int val) {
if (val <= 0)
throw std::invalid_argument{
"invalid path alignment timeout: " + std::to_string(val) + " <= 0"};
m_PathAlignmentTimeout = std::chrono::seconds{val};
});
// Deprecated options:
conf.defineOption<std::string>("network", "enabled", Deprecated);
}
@ -989,7 +1004,7 @@ namespace llarp
constexpr Default DefaultLogType{"file"};
constexpr Default DefaultLogFile{""};
constexpr Default DefaultLogLevel{"info"};
constexpr Default DefaultLogLevel{"warn"};
conf.defineOption<std::string>(
"logging",

@ -125,6 +125,8 @@ namespace llarp
std::set<IPRange> m_OwnedRanges;
std::optional<net::TrafficPolicy> m_TrafficPolicy;
std::optional<llarp_time_t> m_PathAlignmentTimeout;
// TODO:
// on-up
// on-down

@ -27,7 +27,7 @@ namespace llarp
{
if (!loop)
return false;
loop->call(std::move(f));
loop->call_soon(std::move(f));
return true;
}

@ -368,7 +368,10 @@ namespace llarp::uv
bool
Loop::inEventLoop() const
{
return m_EventLoopThreadID and *m_EventLoopThreadID == std::this_thread::get_id();
if (m_EventLoopThreadID)
return *m_EventLoopThreadID == std::this_thread::get_id();
// assume we are in it because we haven't started up yet
return true;
}
} // namespace llarp::uv

@ -13,6 +13,7 @@
#include <llarp/ev/ev.hpp>
#include <llarp/net/net.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/router/systemd_resolved.hpp>
#include <llarp/service/context.hpp>
#include <llarp/service/outbound_context.hpp>
#include <llarp/service/endpoint_state.hpp>
@ -166,6 +167,13 @@ namespace llarp
m_BaseV6Address = conf.m_baseV6Address;
if (conf.m_PathAlignmentTimeout)
{
m_PathAlignmentTimeout = *conf.m_PathAlignmentTimeout;
}
else
m_PathAlignmentTimeout = service::Endpoint::PathAlignmentTimeout();
for (const auto& item : conf.m_mapAddrs)
{
if (not MapAddress(item.second, item.first, false))
@ -261,24 +269,24 @@ namespace llarp
bool
TunEndpoint::HandleHookedDNSMessage(dns::Message msg, std::function<void(dns::Message)> reply)
{
auto ReplyToSNodeDNSWhenReady = [self = this, reply = reply](
RouterID snode, auto msg, bool isV6) -> bool {
return self->EnsurePathToSNode(
auto ReplyToSNodeDNSWhenReady = [this, reply](RouterID snode, auto msg, bool isV6) -> bool {
return EnsurePathToSNode(
snode,
[=](const RouterID&, exit::BaseSession_ptr s, [[maybe_unused]] service::ConvoTag tag) {
self->SendDNSReply(snode, s, msg, reply, isV6);
[this, snode, msg, reply, isV6](
const RouterID&, exit::BaseSession_ptr s, [[maybe_unused]] service::ConvoTag tag) {
SendDNSReply(snode, s, msg, reply, isV6);
});
};
auto ReplyToLokiDNSWhenReady = [self = this, reply = reply](
auto ReplyToLokiDNSWhenReady = [this, reply, timeout = PathAlignmentTimeout()](
service::Address addr, auto msg, bool isV6) -> bool {
using service::Address;
using service::OutboundContext;
return self->EnsurePathToService(
return EnsurePathToService(
addr,
[=](const Address&, OutboundContext* ctx) {
self->SendDNSReply(addr, ctx, msg, reply, isV6);
[this, addr, msg, reply, isV6](const Address&, OutboundContext* ctx) {
SendDNSReply(addr, ctx, msg, reply, isV6);
},
2s);
timeout);
};
auto ReplyToDNSWhenReady = [ReplyToLokiDNSWhenReady, ReplyToSNodeDNSWhenReady](
@ -295,14 +303,14 @@ namespace llarp
}
};
auto ReplyToLokiSRVWhenReady = [self = this, reply = reply](
auto ReplyToLokiSRVWhenReady = [this, reply, timeout = PathAlignmentTimeout()](
service::Address addr, auto msg) -> bool {
using service::Address;
using service::OutboundContext;
return self->EnsurePathToService(
return EnsurePathToService(
addr,
[=](const Address&, OutboundContext* ctx) {
[msg, addr, reply](const Address&, OutboundContext* ctx) {
if (ctx == nullptr)
return;
@ -310,7 +318,7 @@ namespace llarp
msg->AddSRVReply(introset.GetMatchingSRVRecords(addr.subdomain));
reply(*msg);
},
2s);
timeout);
};
if (msg.answers.size() > 0)
@ -784,6 +792,12 @@ namespace llarp
Router()->loop()->add_ticker([this] { Flush(); });
// Attempt to register DNS on the interface
systemd_resolved_set_dns(
m_IfName,
m_LocalResolverAddr.createSockAddr(),
false /* just .loki/.snode DNS initially */);
if (m_OnUp)
{
m_OnUp->NotifyAsync(NotifyParams());
@ -916,7 +930,7 @@ namespace llarp
}
self->SendToOrQueue(addr, pkt.ConstBuffer(), service::ProtocolType::Exit);
},
1s);
PathAlignmentTimeout());
return;
}
bool rewriteAddrs = true;

@ -135,6 +135,12 @@ namespace llarp
return m_OwnedRanges;
}
llarp_time_t
PathAlignmentTimeout() const override
{
return m_PathAlignmentTimeout;
}
/// ip packet against any exit policies we have
/// returns false if this traffic is disallowed by any of those policies
/// returns true otherwise
@ -222,7 +228,7 @@ namespace llarp
std::function<void(dns::Message)> reply,
bool sendIPv6)
{
if (ctx)
if (ctx or HasAddress(addr))
{
huint128_t ip = ObtainIPForAddr(addr);
query->answers.clear();
@ -267,6 +273,8 @@ namespace llarp
std::optional<net::TrafficPolicy> m_TrafficPolicy;
/// ranges we advetise as reachable
std::set<IPRange> m_OwnedRanges;
/// how long to wait for path alignment
llarp_time_t m_PathAlignmentTimeout;
};
} // namespace handlers

@ -600,6 +600,40 @@ namespace llarp
return false;
}
#if !defined(TESTNET)
static constexpr std::array bogonRanges_v6 = {
// zero
IPRange{huint128_t{0}, netmask_ipv6_bits(128)},
// loopback
IPRange{huint128_t{1}, netmask_ipv6_bits(128)},
// yggdrasil
IPRange{huint128_t{uint128_t{0x0200'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(7)},
// multicast
IPRange{huint128_t{uint128_t{0xff00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)},
// local
IPRange{huint128_t{uint128_t{0xfc00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)},
// local
IPRange{huint128_t{uint128_t{0xf800'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}};
static constexpr std::array bogonRanges_v4 = {
IPRange::FromIPv4(0, 0, 0, 0, 8),
IPRange::FromIPv4(10, 0, 0, 0, 8),
IPRange::FromIPv4(100, 64, 0, 0, 10),
IPRange::FromIPv4(127, 0, 0, 0, 8),
IPRange::FromIPv4(169, 254, 0, 0, 16),
IPRange::FromIPv4(172, 16, 0, 0, 12),
IPRange::FromIPv4(192, 0, 0, 0, 24),
IPRange::FromIPv4(192, 0, 2, 0, 24),
IPRange::FromIPv4(192, 88, 99, 0, 24),
IPRange::FromIPv4(192, 168, 0, 0, 16),
IPRange::FromIPv4(198, 18, 0, 0, 15),
IPRange::FromIPv4(198, 51, 100, 0, 24),
IPRange::FromIPv4(203, 0, 113, 0, 24),
IPRange::FromIPv4(224, 0, 0, 0, 4),
IPRange::FromIPv4(240, 0, 0, 0, 4)};
#endif
bool
IsBogon(const in6_addr& addr)
{
@ -607,11 +641,14 @@ namespace llarp
(void)addr;
return false;
#else
if (!ipv6_is_mapped_ipv4(addr))
if (not ipv6_is_mapped_ipv4(addr))
{
static in6_addr zero = {};
if (addr == zero)
return true;
const auto ip = net::In6ToHUInt(addr);
for (const auto& range : bogonRanges_v6)
{
if (range.Contains(ip))
return true;
}
return false;
}
return IsIPv4Bogon(
@ -636,28 +673,10 @@ namespace llarp
}
#if !defined(TESTNET)
static constexpr std::array bogonRanges = {
IPRange::FromIPv4(0, 0, 0, 0, 8),
IPRange::FromIPv4(10, 0, 0, 0, 8),
IPRange::FromIPv4(21, 0, 0, 0, 8),
IPRange::FromIPv4(100, 64, 0, 0, 10),
IPRange::FromIPv4(127, 0, 0, 0, 8),
IPRange::FromIPv4(169, 254, 0, 0, 16),
IPRange::FromIPv4(172, 16, 0, 0, 12),
IPRange::FromIPv4(192, 0, 0, 0, 24),
IPRange::FromIPv4(192, 0, 2, 0, 24),
IPRange::FromIPv4(192, 88, 99, 0, 24),
IPRange::FromIPv4(192, 168, 0, 0, 16),
IPRange::FromIPv4(198, 18, 0, 0, 15),
IPRange::FromIPv4(198, 51, 100, 0, 24),
IPRange::FromIPv4(203, 0, 113, 0, 24),
IPRange::FromIPv4(224, 0, 0, 0, 4),
IPRange::FromIPv4(240, 0, 0, 0, 4)};
bool
IsIPv4Bogon(const huint32_t& addr)
{
for (const auto& bogon : bogonRanges)
for (const auto& bogon : bogonRanges_v4)
{
if (bogon.Contains(addr))
{

@ -498,6 +498,8 @@ namespace llarp::net
{
std::vector<std::string> gateways;
#ifdef __linux__
#ifdef ANDROID
#else
std::ifstream inf("/proc/net/route");
for (std::string line; std::getline(inf, line);)
{
@ -513,7 +515,7 @@ namespace llarp::net
}
}
}
#endif
return gateways;
#elif _WIN32
ForEachWIN32Interface([&](auto w32interface) {

@ -342,6 +342,17 @@ namespace llarp
return {m_addr4.sin_addr.s_addr};
}
nuint128_t
SockAddr::getIPv6() const
{
nuint128_t a;
// Explicit cast to void* here to avoid non-trivial type copying warnings (technically this
// isn't trivial because of the zeroing default constructor, but it's trivial enough that this
// copy is safe).
std::memcpy(static_cast<void*>(&a), &m_addr.sin6_addr, 16);
return a;
}
void
SockAddr::setIPv4(nuint32_t ip)
{

@ -303,7 +303,9 @@ namespace llarp
util::StatusObject
PathHopConfig::ExtractStatus() const
{
const auto ip = net::In6ToHUInt(rc.addrs[0].ip);
util::StatusObject obj{
{"ip", ip.ToString()},
{"lifetime", to_json(lifetime)},
{"router", rc.pubkey.ToHex()},
{"txid", txID.ToHex()},
@ -410,9 +412,6 @@ namespace llarp
auto dlt = now - m_LastLatencyTestTime;
if (dlt > path::latency_interval && m_LastLatencyTestID == 0)
{
// bail doing test if we are active
if (now - m_LastRecvMessage < path::latency_interval)
return;
routing::PathLatencyMessage latency;
latency.T = randint();
m_LastLatencyTestID = latency.T;
@ -497,6 +496,9 @@ namespace llarp
}
}
/// how long we wait for a path to become active again after it times out
constexpr auto PathReanimationTimeout = 45s;
bool
Path::Expired(llarp_time_t now) const
{
@ -504,7 +506,11 @@ namespace llarp
return true;
if (_status == ePathBuilding)
return false;
if (_status == ePathEstablished || _status == ePathTimeout)
if (_status == ePathTimeout)
{
return now >= m_LastRecvMessage + PathReanimationTimeout;
}
if (_status == ePathEstablished)
{
return now >= ExpireTime();
}

@ -302,7 +302,7 @@ namespace llarp
{
if (itr->second->Expired(now))
{
m_Router->outboundMessageHandler().QueueRemoveEmptyPath(itr->first);
m_Router->outboundMessageHandler().RemovePath(itr->first);
itr = map.erase(itr);
}
else

@ -96,7 +96,10 @@ namespace llarp
{
// farthest hop
// TODO: encrypt junk frames because our public keys are not eligator
loop->call([self = shared_from_this()] { self->result(self); });
loop->call([self = shared_from_this()] {
self->result(self);
self->result = nullptr;
});
}
else
{
@ -125,47 +128,56 @@ namespace llarp
static void
PathBuilderKeysGenerated(std::shared_ptr<AsyncPathKeyExchangeContext> ctx)
{
if (!ctx->pathset->IsStopped())
{
ctx->router->NotifyRouterEvent<tooling::PathAttemptEvent>(ctx->router->pubkey(), ctx->path);
if (ctx->pathset->IsStopped())
return;
const RouterID remote = ctx->path->Upstream();
auto sentHandler = [ctx](auto status) {
if (status == SendStatus::Success)
{
ctx->router->pathContext().AddOwnPath(ctx->pathset, ctx->path);
ctx->pathset->PathBuildStarted(std::move(ctx->path));
}
else
{
LogError(ctx->pathset->Name(), " failed to send LRCM to ", ctx->path->Upstream());
ctx->path->EnterState(path::ePathFailed, ctx->router->Now());
}
ctx->path = nullptr;
ctx->pathset = nullptr;
};
if (ctx->router->SendToOrQueue(remote, ctx->LRCM, sentHandler))
{
// persist session with router until this path is done
if (ctx->path)
ctx->router->PersistSessionUntil(remote, ctx->path->ExpireTime());
}
else
ctx->router->NotifyRouterEvent<tooling::PathAttemptEvent>(ctx->router->pubkey(), ctx->path);
ctx->router->pathContext().AddOwnPath(ctx->pathset, ctx->path);
ctx->pathset->PathBuildStarted(ctx->path);
const RouterID remote = ctx->path->Upstream();
auto sentHandler = [router = ctx->router, path = ctx->path](auto status) {
if (status != SendStatus::Success)
{
LogError(ctx->pathset->Name(), " failed to queue LRCM to ", remote);
sentHandler(SendStatus::NoLink);
path->EnterState(path::ePathFailed, router->Now());
}
};
if (ctx->router->SendToOrQueue(remote, ctx->LRCM, sentHandler))
{
// persist session with router until this path is done
if (ctx->path)
ctx->router->PersistSessionUntil(remote, ctx->path->ExpireTime());
}
else
{
LogError(ctx->pathset->Name(), " failed to queue LRCM to ", remote);
sentHandler(SendStatus::NoLink);
}
}
namespace path
{
bool
BuildLimiter::Attempt(const RouterID& router)
{
return m_EdgeLimiter.Insert(router);
}
void
BuildLimiter::Decay(llarp_time_t now)
{
m_EdgeLimiter.Decay(now);
}
bool
BuildLimiter::Limited(const RouterID& router) const
{
return m_EdgeLimiter.Contains(router);
}
Builder::Builder(AbstractRouter* p_router, size_t pathNum, size_t hops)
: path::PathSet{pathNum}
, m_EdgeLimiter{MIN_PATH_BUILD_INTERVAL}
, _run{true}
, m_router{p_router}
, numHops{hops}
: path::PathSet{pathNum}, _run{true}, m_router{p_router}, numHops{hops}
{
CryptoManager::instance()->encryption_keygen(enckey);
}
@ -180,7 +192,6 @@ namespace llarp
void Builder::Tick(llarp_time_t)
{
const auto now = llarp::time_now_ms();
m_EdgeLimiter.Decay(now);
ExpirePaths(now, m_router);
if (ShouldBuildMore(now))
BuildOne();
@ -226,7 +237,7 @@ namespace llarp
if (exclude.count(rc.pubkey))
return;
if (m_EdgeLimiter.Contains(rc.pubkey))
if (BuildCooldownHit(rc.pubkey))
return;
found = rc;
@ -253,6 +264,14 @@ namespace llarp
Builder::Stop()
{
_run = false;
// tell all our paths that they have expired
const auto now = Now();
for (auto& item : m_Paths)
{
item.second->EnterState(ePathExpired, now);
}
// remove expired paths
ExpirePaths(now, m_router);
return true;
}
@ -277,7 +296,7 @@ namespace llarp
bool
Builder::BuildCooldownHit(RouterID edge) const
{
return m_EdgeLimiter.Contains(edge);
return m_router->pathBuildLimiter().Limited(edge);
}
bool
@ -399,7 +418,7 @@ namespace llarp
return;
lastBuild = Now();
const RouterID edge{hops[0].pubkey};
if (not m_EdgeLimiter.Insert(edge))
if (not m_router->pathBuildLimiter().Attempt(edge))
{
LogWarn(Name(), " building too fast to edge router ", edge);
return;
@ -437,8 +456,6 @@ namespace llarp
{
PathSet::HandlePathBuildFailedAt(p, edge);
DoPathBuildBackoff();
/// add it to the edge limter even if it's not an edge for simplicity
m_EdgeLimiter.Insert(edge);
}
void

@ -15,11 +15,31 @@ namespace llarp
static constexpr auto MIN_PATH_BUILD_INTERVAL = 500ms;
static constexpr auto PATH_BUILD_RATE = 100ms;
/// limiter for path builds
/// prevents overload and such
class BuildLimiter
{
util::DecayingHashSet<RouterID> m_EdgeLimiter;
public:
/// attempt a build
/// return true if we are allowed to continue
bool
Attempt(const RouterID& router);
/// decay limit entries
void
Decay(llarp_time_t now);
/// return true if this router is currently limited
bool
Limited(const RouterID& router) const;
};
struct Builder : public PathSet
{
private:
llarp_time_t m_LastWarn = 0s;
util::DecayingHashSet<RouterID> m_EdgeLimiter;
protected:
/// flag for PathSet::Stop()

@ -85,9 +85,9 @@ namespace llarp
if (itr->second->Expired(now))
{
PathID_t txid = itr->second->TXID();
router->outboundMessageHandler().QueueRemoveEmptyPath(std::move(txid));
router->outboundMessageHandler().RemovePath(std::move(txid));
PathID_t rxid = itr->second->RXID();
router->outboundMessageHandler().QueueRemoveEmptyPath(std::move(rxid));
router->outboundMessageHandler().RemovePath(std::move(rxid));
itr = m_Paths.erase(itr);
}
else
@ -156,7 +156,8 @@ namespace llarp
{
if (chosen == nullptr)
chosen = itr->second;
else if (chosen->intro.latency > itr->second->intro.latency)
else if (
chosen->intro.latency != 0s and chosen->intro.latency > itr->second->intro.latency)
chosen = itr->second;
}
}
@ -429,7 +430,7 @@ namespace llarp
llarp_time_t minLatency = 30s;
for (const auto& path : established)
{
if (path->intro.latency < minLatency)
if (path->intro.latency < minLatency and path->intro.latency != 0s)
{
minLatency = path->intro.latency;
chosen = path;

@ -50,17 +50,20 @@ namespace llarp
{
Profiling();
inline static const int profiling_chances = 4;
/// generic variant
bool
IsBad(const RouterID& r, uint64_t chances = 8) EXCLUDES(m_ProfilesMutex);
IsBad(const RouterID& r, uint64_t chances = profiling_chances) EXCLUDES(m_ProfilesMutex);
/// check if this router should have paths built over it
bool
IsBadForPath(const RouterID& r, uint64_t chances = 8) EXCLUDES(m_ProfilesMutex);
IsBadForPath(const RouterID& r, uint64_t chances = profiling_chances) EXCLUDES(m_ProfilesMutex);
/// check if this router should be connected directly to
bool
IsBadForConnect(const RouterID& r, uint64_t chances = 8) EXCLUDES(m_ProfilesMutex);
IsBadForConnect(const RouterID& r, uint64_t chances = profiling_chances)
EXCLUDES(m_ProfilesMutex);
void
MarkConnectTimeout(const RouterID& r) EXCLUDES(m_ProfilesMutex);

@ -292,6 +292,16 @@ namespace llarp
virtual bool
ConnectionToRouterAllowed(const RouterID& router) const = 0;
/// return true if we have an exit as a client
virtual bool
HasClientExit() const
{
return false;
};
virtual path::BuildLimiter&
pathBuildLimiter() = 0;
/// return true if we have at least 1 session to this router in either
/// direction
virtual bool

@ -38,7 +38,7 @@ namespace llarp
Tick() = 0;
virtual void
QueueRemoveEmptyPath(const PathID_t& pathid) = 0;
RemovePath(const PathID_t& pathid) = 0;
virtual util::StatusObject
ExtractStatus() const = 0;

@ -58,6 +58,9 @@ namespace llarp
virtual size_t
NumberOfStrictConnectRouters() const = 0;
virtual bool
HaveReceivedWhitelist() const = 0;
};
} // namespace llarp

@ -15,14 +15,17 @@ namespace llarp
{
const PathID_t OutboundMessageHandler::zeroID;
using namespace std::chrono_literals;
OutboundMessageHandler::OutboundMessageHandler(size_t maxQueueSize)
: outboundQueue(maxQueueSize), removedPaths(20), removedSomePaths(false)
: outboundQueue(maxQueueSize), recentlyRemovedPaths(5s), removedSomePaths(false)
{}
bool
OutboundMessageHandler::QueueMessage(
const RouterID& remote, const ILinkMessage& msg, SendStatusHandler callback)
{
// if the destination is invalid, callback with failure and return
if (not _linkManager->SessionIsClient(remote) and not _lookupHandler->RemoteIsAllowed(remote))
{
DoCallback(callback, SendStatus::InvalidRouter);
@ -44,26 +47,31 @@ namespace llarp
std::copy_n(buf.base, buf.sz, message.first.data());
// if we have a session to the destination, queue the message and return
if (_linkManager->HasSessionTo(remote))
{
QueueOutboundMessage(remote, std::move(message), msg.pathid, priority);
return true;
}
// if we don't have a session to the destination, queue the message onto
// a special pending session queue for that destination, and then create
// that pending session if there is not already a session establish attempt
// in progress.
bool shouldCreateSession = false;
{
util::Lock l(_mutex);
// create queue for <remote> if it doesn't exist, and get iterator
auto itr_pair = pendingSessionMessageQueues.emplace(remote, MessageQueue());
auto [queue_itr, is_new] = pendingSessionMessageQueues.emplace(remote, MessageQueue());
MessageQueueEntry entry;
entry.priority = priority;
entry.message = message;
entry.router = remote;
itr_pair.first->second.push(std::move(entry));
queue_itr->second.push(std::move(entry));
shouldCreateSession = itr_pair.second;
shouldCreateSession = is_new;
}
if (shouldCreateSession)
@ -77,26 +85,33 @@ namespace llarp
void
OutboundMessageHandler::Tick()
{
m_Killer.TryAccess([self = this]() {
self->ProcessOutboundQueue();
self->RemoveEmptyPathQueues();
self->SendRoundRobin();
m_Killer.TryAccess([this]() {
recentlyRemovedPaths.Decay();
ProcessOutboundQueue();
SendRoundRobin();
});
}
void
OutboundMessageHandler::QueueRemoveEmptyPath(const PathID_t& pathid)
OutboundMessageHandler::RemovePath(const PathID_t& pathid)
{
m_Killer.TryAccess([self = this, pathid]() {
if (self->removedPaths.full())
m_Killer.TryAccess([this, pathid]() {
/* add the path id to a list of recently removed paths to act as a filter
* for messages that are queued but haven't been sorted into path queues yet.
*
* otherwise these messages would re-create the path queue we just removed, and
* those path queues would be leaked / never removed.
*/
recentlyRemovedPaths.Insert(pathid);
auto itr = outboundMessageQueues.find(pathid);
if (itr != outboundMessageQueues.end())
{
self->RemoveEmptyPathQueues();
outboundMessageQueues.erase(itr);
}
self->removedPaths.pushBack(pathid);
removedSomePaths = true;
});
}
// TODO: this
util::StatusObject
OutboundMessageHandler::ExtractStatus() const
{
@ -241,6 +256,8 @@ namespace llarp
{
MessageQueueEntry entry;
entry.message = std::move(msg);
// copy callback in case we need to call it, so we can std::move(entry)
auto callback_copy = entry.message.second;
entry.router = remote;
entry.pathid = pathid;
@ -266,17 +283,23 @@ namespace llarp
{
while (not outboundQueue.empty())
{
// TODO: can we add util::thread::Queue::front() for move semantics here?
MessageQueueEntry entry = outboundQueue.popFront();
auto itr_pair = outboundMessageQueues.emplace(entry.pathid, MessageQueue());
// messages may still be queued for processing when a pathid is removed,
// so check here if the pathid was recently removed.
if (recentlyRemovedPaths.Contains(entry.pathid))
{
return;
}
auto [queue_itr, is_new] = outboundMessageQueues.emplace(entry.pathid, MessageQueue());
if (itr_pair.second && !entry.pathid.IsZero())
if (is_new && !entry.pathid.IsZero())
{
roundRobinOrder.push(entry.pathid);
}
MessageQueue& path_queue = itr_pair.first->second;
MessageQueue& path_queue = queue_itr->second;
if (path_queue.size() < MAX_PATH_QUEUE_SIZE || entry.pathid.IsZero())
{
@ -290,41 +313,25 @@ namespace llarp
}
}
void
OutboundMessageHandler::RemoveEmptyPathQueues()
{
removedSomePaths = false;
if (removedPaths.empty())
return;
while (not removedPaths.empty())
{
auto itr = outboundMessageQueues.find(removedPaths.popFront());
if (itr != outboundMessageQueues.end())
{
outboundMessageQueues.erase(itr);
}
}
removedSomePaths = true;
}
void
OutboundMessageHandler::SendRoundRobin()
{
m_queueStats.numTicks++;
// send non-routing messages first priority
auto& non_routing_mq = outboundMessageQueues[zeroID];
while (not non_routing_mq.empty())
// send routing messages first priority
auto& routing_mq = outboundMessageQueues[zeroID];
while (not routing_mq.empty())
{
const MessageQueueEntry& entry = non_routing_mq.top();
const MessageQueueEntry& entry = routing_mq.top();
Send(entry.router, entry.message);
non_routing_mq.pop();
routing_mq.pop();
}
size_t empty_count = 0;
size_t num_queues = roundRobinOrder.size();
// if any paths have been removed since last tick, remove any stale
// entries from the round-robin ordering
if (removedSomePaths)
{
for (size_t i = 0; i < num_queues; i++)
@ -338,6 +345,7 @@ namespace llarp
}
}
}
removedSomePaths = false;
num_queues = roundRobinOrder.size();
size_t sent_count = 0;
@ -346,7 +354,10 @@ namespace llarp
return;
}
while (sent_count < MAX_OUTBOUND_MESSAGES_PER_TICK) // TODO: better stop condition
// send messages for each pathid in roundRobinOrder, stopping when
// either every path's queue is empty or a set maximum amount of
// messages have been sent.
while (sent_count < MAX_OUTBOUND_MESSAGES_PER_TICK)
{
PathID_t pathid = std::move(roundRobinOrder.front());
roundRobinOrder.pop();

@ -4,6 +4,7 @@
#include <llarp/ev/ev.hpp>
#include <llarp/util/thread/queue.hpp>
#include <llarp/util/decaying_hashset.hpp>
#include <llarp/path/path_types.hpp>
#include <llarp/router_id.hpp>
@ -27,15 +28,45 @@ namespace llarp
OutboundMessageHandler(size_t maxQueueSize = MAX_OUTBOUND_QUEUE_SIZE);
/* Called to queue a message to be sent to a router.
*
* If there is no session with the destination router, the message is added to a
* pending session queue for that router. If there is no pending session to that
* router, one is created.
*
* If there is a session to the destination router, the message is placed on the shared
* outbound message queue to be processed on Tick().
*
* When this class' Tick() is called, that queue is emptied and the messages there
* are placed in their paths' respective individual queues.
*
* Returns false if encoding the message into a buffer fails, true otherwise.
* A return value of true merely means we successfully processed the queue request,
* so for example an invalid destination still yields a true return.
*/
bool
QueueMessage(const RouterID& remote, const ILinkMessage& msg, SendStatusHandler callback)
override EXCLUDES(_mutex);
/* Called once per event loop tick.
*
* Processes messages on the shared message queue into their paths' respective
* individual queues.
*
* Removes the individual queues for paths which have died / expired, as informed by
* QueueRemoveEmptyPath.
*
* Sends all routing messages that have been queued, indicated by pathid 0 when queued.
* Sends messages from path queues until all are empty or a set cap has been reached.
*/
void
Tick() override;
/* Called from outside this class to inform it that a path has died / expired
* and its queue should be discarded.
*/
void
QueueRemoveEmptyPath(const PathID_t& pathid) override;
RemovePath(const PathID_t& pathid) override;
util::StatusObject
ExtractStatus() const override;
@ -46,6 +77,9 @@ namespace llarp
private:
using Message = std::pair<std::vector<byte_t>, SendStatusHandler>;
/* A message that has been queued for sending, but not yet
* processed into an individual path's message queue.
*/
struct MessageQueueEntry
{
uint16_t priority;
@ -73,6 +107,14 @@ namespace llarp
using MessageQueue = std::priority_queue<MessageQueueEntry>;
/* If a session is not yet created with the destination router for a message,
* a special queue is created for that router and an attempt is made to
* establish a session. When this establish attempt concludes, either
* the messages are then sent to that router immediately, on success, or
* the messages are dropped and their send status callbacks are invoked with
* the appropriate send status.
*/
void
OnSessionEstablished(const RouterID& router);
@ -91,6 +133,7 @@ namespace llarp
void
OnSessionResult(const RouterID& router, const SessionResult result);
/* queues a message's send result callback onto the event loop */
void
DoCallback(SendStatusHandler callback, SendStatus status);
@ -100,30 +143,62 @@ namespace llarp
bool
EncodeBuffer(const ILinkMessage& msg, llarp_buffer_t& buf);
/* sends the message along to the link layer, and hopefully out to the network
*
* returns the result of the call to LinkManager::SendTo()
*/
bool
Send(const RouterID& remote, const Message& msg);
/* Sends the message along to the link layer if we have a session to the remote
*
* returns the result of the Send() call, or false if no session.
*/
bool
SendIfSession(const RouterID& remote, const Message& msg);
/* queues a message to the shared outbound message queue.
*
* If the queue is full, the message is dropped and the message's status
* callback is invoked with a congestion status.
*
* When this class' Tick() is called, that queue is emptied and the messages there
* are placed in their paths' respective individual queues.
*/
bool
QueueOutboundMessage(
const RouterID& remote, Message&& msg, const PathID_t& pathid, uint16_t priority = 0);
/* Processes messages on the shared message queue into their paths' respective
* individual queues.
*/
void
ProcessOutboundQueue();
void
RemoveEmptyPathQueues();
/*
* Sends all routing messages that have been queued, indicated by pathid 0 when queued.
*
* Sends messages from path queues until all are empty or a set cap has been reached.
* This will send one message from each queue in a round-robin fashion such that they
* all have roughly equal access to bandwidth. A notion of priority may be introduced
* at a later time, but for now only routing messages get priority.
*/
void
SendRoundRobin();
/* Invoked when an outbound session establish attempt has concluded.
*
* If the outbound session was successfully created, sends any messages queued
* for that destination along to it.
*
* If the session was unsuccessful, invokes the send status callbacks of those
* queued messages and drops them.
*/
void
FinalizeSessionRequest(const RouterID& router, SendStatus status) EXCLUDES(_mutex);
llarp::thread::Queue<MessageQueueEntry> outboundQueue;
llarp::thread::Queue<PathID_t> removedPaths;
llarp::util::DecayingHashSet<PathID_t> recentlyRemovedPaths;
bool removedSomePaths;
mutable util::Mutex _mutex; // protects pendingSessionMessageQueues

@ -232,7 +232,7 @@ namespace llarp
bool
OutboundSessionMaker::ShouldConnectTo(const RouterID& router) const
{
if (router == us)
if (router == us or not _rcLookup->RemoteIsAllowed(router))
return false;
size_t numPending = 0;
{

@ -49,7 +49,7 @@ namespace llarp
}
bool
RCLookupHandler::HaveReceivedWhitelist()
RCLookupHandler::HaveReceivedWhitelist() const
{
util::Lock l(_mutex);
return not whitelistRouters.empty();
@ -127,14 +127,12 @@ namespace llarp
return false;
}
util::Lock l(_mutex);
if (not useWhitelist)
return true;
if (useWhitelist && whitelistRouters.find(remote) == whitelistRouters.end())
{
return false;
}
util::Lock lock{_mutex};
return true;
return whitelistRouters.count(remote);
}
bool

@ -44,7 +44,7 @@ namespace llarp
SetRouterWhitelist(const std::vector<RouterID>& routers) override EXCLUDES(_mutex);
bool
HaveReceivedWhitelist();
HaveReceivedWhitelist() const override;
void
GetRC(const RouterID& router, RCRequestCallback callback, bool forceLookup = false) override

@ -1,5 +1,6 @@
#include "route_poker.hpp"
#include "abstractrouter.hpp"
#include "net/sock_addr.hpp"
#include <llarp/net/route.hpp>
#include <llarp/service/context.hpp>
#include <unordered_set>
@ -119,7 +120,9 @@ namespace llarp
const auto maybe = GetDefaultGateway();
if (not maybe.has_value())
{
#ifndef ANDROID
LogError("Network is down");
#endif
// mark network lost
m_HasNetwork = false;
return;
@ -157,6 +160,11 @@ namespace llarp
Update();
m_Enabling = false;
m_Enabled = true;
systemd_resolved_set_dns(
m_Router->hiddenServiceContext().GetDefault()->GetIfName(),
m_Router->GetConfig()->dns.m_bind.createSockAddr(),
true /* route all DNS */);
}
void
@ -167,6 +175,11 @@ namespace llarp
DisableAllRoutes();
m_Enabled = false;
systemd_resolved_set_dns(
m_Router->hiddenServiceContext().GetDefault()->GetIfName(),
m_Router->GetConfig()->dns.m_bind.createSockAddr(),
false /* route DNS only for .loki/.snode */);
}
void

@ -3,7 +3,9 @@
#include <unordered_map>
#include <string>
#include <memory>
#include <optional>
#include <llarp/net/net_int.hpp>
#include "systemd_resolved.hpp"
namespace llarp
{

@ -374,9 +374,21 @@ namespace llarp
return inbound_routing_msg_parser.ParseMessageBuffer(buf, h, rxid, this);
}
bool
Router::LooksDeregistered() const
{
return IsServiceNode() and whitelistRouters and _rcLookupHandler.HaveReceivedWhitelist()
and not _rcLookupHandler.RemoteIsAllowed(pubkey());
}
bool
Router::ConnectionToRouterAllowed(const RouterID& router) const
{
if (LooksDeregistered())
{
// we are deregistered don't allow any connections outbound at all
return false;
}
return _rcLookupHandler.RemoteIsAllowed(router);
}
@ -735,7 +747,8 @@ namespace llarp
{
ss << " snode | known/svc/clients: " << nodedb()->NumLoaded() << "/"
<< NumberOfConnectedRouters() << "/" << NumberOfConnectedClients() << " | "
<< pathContext().CurrentTransitPaths() << " active paths";
<< pathContext().CurrentTransitPaths() << " active paths | "
<< "block " << m_lokidRpcClient->BlockHeight();
}
else
{
@ -752,6 +765,8 @@ namespace llarp
}
#endif
m_PathBuildLimiter.Decay(now);
routerProfiling().Tick();
if (ShouldReportStats(now))
@ -763,7 +778,9 @@ namespace llarp
_rcLookupHandler.PeriodicUpdate(now);
const bool gotWhitelist = _rcLookupHandler.HaveReceivedWhitelist();
const bool isSvcNode = IsServiceNode();
const bool looksDeregistered = LooksDeregistered();
if (_rc.ExpiresSoon(now, std::chrono::milliseconds(randint() % 10000))
|| (now - _rc.last_updated) > rcRegenInterval)
@ -772,11 +789,10 @@ namespace llarp
if (!UpdateOurRC(false))
LogError("Failed to update our RC");
}
else
else if (not looksDeregistered)
{
GossipRCIfNeeded(_rc);
}
const bool gotWhitelist = _rcLookupHandler.HaveReceivedWhitelist();
// remove RCs for nodes that are no longer allowed by network policy
nodedb()->RemoveIf([&](const RouterContact& rc) -> bool {
// don't purge bootstrap nodes from nodedb
@ -844,7 +860,7 @@ namespace llarp
const int interval = isSvcNode ? 5 : 2;
const auto timepoint_now = Clock_t::now();
if (timepoint_now >= m_NextExploreAt)
if (timepoint_now >= m_NextExploreAt and not looksDeregistered)
{
_rcLookupHandler.ExploreNetwork();
m_NextExploreAt = timepoint_now + std::chrono::seconds(interval);
@ -856,7 +872,17 @@ namespace llarp
connectToNum = strictConnect;
}
if (connected < connectToNum)
if (looksDeregistered)
{
// kill all sessions that are open because we think we are deregistered
_linkManager.ForEachPeer([](auto* peer) {
if (peer)
peer->Close();
});
// complain about being deregistered
LogError("We are running as a service node but we seem to be decommissioned");
}
else if (connected < connectToNum)
{
size_t dlt = connectToNum - connected;
LogDebug("connecting to ", dlt, " random routers to keep alive");
@ -883,7 +909,16 @@ namespace llarp
if (m_peerDb->shouldFlush(now))
{
LogDebug("Queing database flush...");
QueueDiskIO([this]() { m_peerDb->flushDatabase(); });
QueueDiskIO([this]() {
try
{
m_peerDb->flushDatabase();
}
catch (std::exception& ex)
{
LogError("Could not flush peer stats database: ", ex.what());
}
});
}
}

@ -74,6 +74,14 @@ namespace llarp
LMQ_ptr m_lmq;
path::BuildLimiter m_PathBuildLimiter;
path::BuildLimiter&
pathBuildLimiter() override
{
return m_PathBuildLimiter;
}
const LMQ_ptr&
lmq() const override
{
@ -173,6 +181,10 @@ namespace llarp
void
QueueDiskIO(std::function<void(void)> func) override;
/// return true if we look like we are a deregistered service node
bool
LooksDeregistered() const;
std::optional<SockAddr> _ourAddress;
EventLoop_ptr _loop;
@ -390,7 +402,7 @@ namespace llarp
/// return true if we are a client with an exit configured
bool
HasClientExit() const;
HasClientExit() const override;
const byte_t*
pubkey() const override

@ -0,0 +1,171 @@
#include "systemd_resolved.hpp"
#include <llarp/util/logging/logger.hpp>
#ifndef WITH_SYSTEMD
namespace llarp
{
bool
systemd_resolved_set_dns(std::string, llarp::SockAddr, bool)
{
LogDebug("lokinet is not built with systemd support, cannot set systemd resolved DNS");
return false;
}
} // namespace llarp
#else
#include <stdexcept>
extern "C"
{
#include <systemd/sd-bus.h>
#include <net/if.h>
}
using namespace std::literals;
namespace llarp
{
namespace
{
template <typename... T>
void
resolved_call(sd_bus* bus, const char* method, const char* arg_format, T... args)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message* msg = nullptr;
int r = sd_bus_call_method(
bus,
"org.freedesktop.resolve1",
"/org/freedesktop/resolve1",
"org.freedesktop.resolve1.Manager",
method,
&error,
&msg,
arg_format,
args...);
if (r < 0)
throw std::runtime_error{"sdbus resolved "s + method + " failed: " + strerror(-r)};
sd_bus_message_unref(msg);
sd_bus_error_free(&error);
}
struct sd_bus_deleter
{
void
operator()(sd_bus* ptr) const
{
sd_bus_unref(ptr);
}
};
} // namespace
bool
systemd_resolved_set_dns(std::string ifname, llarp::SockAddr dns, bool global)
{
unsigned int if_ndx = if_nametoindex(ifname.c_str());
if (if_ndx == 0)
{
LogWarn("No such interface '", ifname, "'");
return false;
}
// Connect to the system bus
sd_bus* bus = nullptr;
int r = sd_bus_open_system(&bus);
if (r < 0)
{
LogWarn("Failed to connect to system bus to set DNS: ", strerror(-r));
return false;
}
std::unique_ptr<sd_bus, sd_bus_deleter> bus_ptr{bus};
try
{
// This passing address by bytes and using two separate calls for ipv4/ipv6 is gross, but the
// alternative is to build up a bunch of crap with va_args, which is slightly more gross.
if (dns.isIPv6())
{
auto ipv6 = dns.getIPv6();
static_assert(sizeof(ipv6) == 16);
auto* a = reinterpret_cast<const uint8_t*>(&ipv6);
resolved_call(
bus,
"SetLinkDNSEx",
"ia(iayqs)",
(int32_t)if_ndx,
(int)1, // number of "iayqs"s we are passing
(int32_t)AF_INET6, // network address type
(int)16, // network addr byte size
// clang-format off
a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7],
a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], // yuck
// clang-format on
(uint16_t)dns.getPort(),
nullptr // dns server name (for TLS SNI which we don't care about)
);
}
else
{
auto ipv4 = dns.getIPv4();
static_assert(sizeof(ipv4) == 4);
auto* a = reinterpret_cast<const uint8_t*>(&ipv4);
resolved_call(
bus,
"SetLinkDNSEx",
"ia(iayqs)",
(int32_t)if_ndx,
(int)1, // number of "iayqs"s we are passing
(int32_t)AF_INET, // network address type
(int)4, // network addr byte size
// clang-format off
a[0], a[1], a[2], a[3], // yuck
// clang-format on
(uint16_t)dns.getPort(),
nullptr // dns server name (for TLS SNI which we don't care about)
);
}
if (global)
// Setting "." as a routing domain gives this DNS server higher priority in resolution
// compared to dns servers that are set without a domain (e.g. the default for a
// DHCP-configured DNS server)
resolved_call(
bus,
"SetLinkDomains",
"ia(sb)",
(int32_t)if_ndx,
(int)1, // array size
"." // global DNS root
);
else
// Only resolve .loki and .snode through lokinet (so you keep using your local DNS server
// for everything else, which is nicer than forcing everything though lokinet's upstream
// DNS).
resolved_call(
bus,
"SetLinkDomains",
"ia(sb)",
(int32_t)if_ndx,
(int)2, // array size
"loki", // domain
(int)1, // routing domain = true
"snode", // domain
(int)1 // routing domain = true
);
return true;
}
catch (const std::exception& e)
{
LogWarn("Failed to set DNS via systemd-resolved: ", e.what());
}
return false;
}
} // namespace llarp
#endif // WITH_SYSTEMD

@ -0,0 +1,19 @@
#pragma once
#include <string>
#include <llarp/net/sock_addr.hpp>
namespace llarp
{
/// Attempts to set lokinet as the DNS server for systemd-resolved. Returns true if successful,
/// false if unsupported or fails. (When compiled without systemd support this always returns
/// false without doing anything).
///
/// \param if_name -- the interface name to which we add the DNS servers, e.g. lokitun0.
/// Typically tun_endpoint.GetIfName().
/// \param dns -- the listening address of the lokinet DNS server
/// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode
/// addresses (false).
bool
systemd_resolved_set_dns(std::string if_name, llarp::SockAddr dns, bool global);
} // namespace llarp

@ -45,6 +45,7 @@ namespace llarp
auto lokidCategory = m_lokiMQ->add_category("lokid", oxenmq::Access{oxenmq::AuthLevel::none});
lokidCategory.add_request_command(
"get_peer_stats", [this](oxenmq::Message& m) { HandleGetPeerStats(m); });
m_UpdatingList = false;
}
void
@ -82,8 +83,20 @@ namespace llarp
" parts instead of 2 parts so we will not update the list of service nodes");
return; // bail
}
LogDebug("new block at hieght ", msg.data[0]);
UpdateServiceNodeList(std::string{msg.data[1]});
try
{
m_BlockHeight = std::stoll(std::string{msg.data[0]});
}
catch (std::exception& ex)
{
LogError("bad block hieght: ", ex.what());
return; // bail
}
LogDebug("new block at hieght ", m_BlockHeight);
// don't upadate on block notification if an update is pending
if (not m_UpdatingList)
UpdateServiceNodeList(std::string{msg.data[1]});
}
void
@ -95,9 +108,11 @@ namespace llarp
request["active_only"] = true;
if (not topblock.empty())
request["poll_block_hash"] = topblock;
m_UpdatingList = true;
Request(
"rpc.get_service_nodes",
[self = shared_from_this()](bool success, std::vector<std::string> data) {
self->m_UpdatingList = false;
if (not success)
{
LogWarn("failed to update service node list");

@ -30,6 +30,13 @@ namespace llarp
SecretKey
ObtainIdentityKey();
/// get what the current block height is according to oxend
uint64_t
BlockHeight() const
{
return m_BlockHeight;
}
void
LookupLNSNameHash(
dht::Key_t namehash,
@ -76,6 +83,9 @@ namespace llarp
LMQ_ptr m_lokiMQ;
AbstractRouter* const m_Router;
std::atomic<bool> m_UpdatingList;
uint64_t m_BlockHeight;
};
} // namespace rpc

@ -402,7 +402,7 @@ namespace llarp::rpc
{
service::Address addr;
const auto exit_str = exit_itr->get<std::string>();
if (service::NameIsValid(exit_str))
if (service::NameIsValid(exit_str) or exit_str == "null")
{
lnsExit = exit_str;
}
@ -456,39 +456,54 @@ namespace llarp::rpc
{
auto mapExit = [=](service::Address addr) mutable {
ep->MapExitRange(range, addr);
r->routePoker().Enable();
r->routePoker().Up();
bool shouldSendAuth = false;
if (token.has_value())
{
shouldSendAuth = true;
ep->SetAuthInfoForEndpoint(*exit, service::AuthInfo{*token});
}
auto onGoodResult = [r, reply](std::string reason) {
if (r->HasClientExit())
reply(CreateJSONResponse(reason));
else
reply(CreateJSONError("we dont have an exit?"));
};
auto onBadResult = [r, reply, ep, range](std::string reason) {
r->routePoker().Down();
ep->UnmapExitRange(range);
reply(CreateJSONError(reason));
};
if (addr.IsZero())
{
onGoodResult("added null exit");
return;
}
ep->EnsurePathToService(
addr,
[reply, r, shouldSendAuth](auto, service::OutboundContext* ctx) {
[onBadResult, onGoodResult, shouldSendAuth, addrStr = addr.ToString()](
auto, service::OutboundContext* ctx) {
if (ctx == nullptr)
{
reply(CreateJSONError("could not find exit"));
onBadResult("could not find exit");
return;
}
auto onGoodResult = [r, reply](std::string reason) {
r->routePoker().Enable();
r->routePoker().Up();
reply(CreateJSONResponse(reason));
};
if (not shouldSendAuth)
{
onGoodResult("OK");
onGoodResult("OK: connected to " + addrStr);
return;
}
ctx->AsyncSendAuth([onGoodResult, reply](service::AuthResult result) {
// TODO: refactor this code. We are 5 lambdas deep here!
if (result.code != service::AuthResultCode::eAuthAccepted)
{
reply(CreateJSONError(result.reason));
return;
}
onGoodResult(result.reason);
});
ctx->AsyncSendAuth(
[onGoodResult, onBadResult](service::AuthResult result) {
// TODO: refactor this code. We are 5 lambdas deep here!
if (result.code != service::AuthResultCode::eAuthAccepted)
{
onBadResult(result.reason);
return;
}
onGoodResult(result.reason);
});
},
5s);
};
@ -521,7 +536,6 @@ namespace llarp::rpc
else
{
reply(CreateJSONError("lns name resolved to a snode"));
return;
}
});
}

@ -46,11 +46,12 @@ namespace llarp
namespace service
{
Endpoint::Endpoint(AbstractRouter* r, Context* parent)
: path::Builder(r, 3, path::default_len)
, context(parent)
, m_InboundTrafficQueue(512)
, m_SendQueue(512)
, m_RecvQueue(512)
: path::Builder{r, 3, path::default_len}
, context{parent}
, m_InboundTrafficQueue{512}
, m_SendQueue{512}
, m_RecvQueue{512}
, m_IntrosetLookupFilter{5s}
{
m_state = std::make_unique<EndpointState>();
m_state->m_Router = r;
@ -283,7 +284,8 @@ namespace llarp
{
RegenAndPublishIntroSet();
}
// decay introset lookup filter
m_IntrosetLookupFilter.Decay(now);
// expire name cache
m_state->nameCache.Decay(now);
// expire snode sessions
@ -518,13 +520,15 @@ namespace llarp
void
Endpoint::ConvoTagTX(const ConvoTag& tag)
{
Sessions()[tag].TX();
if (Sessions().count(tag))
Sessions()[tag].TX();
}
void
Endpoint::ConvoTagRX(const ConvoTag& tag)
{
Sessions()[tag].RX();
if (Sessions().count(tag))
Sessions()[tag].RX();
}
bool
@ -620,8 +624,12 @@ namespace llarp
Endpoint* m_Endpoint;
uint64_t m_relayOrder;
PublishIntroSetJob(
Endpoint* parent, uint64_t id, EncryptedIntroSet introset, uint64_t relayOrder)
: IServiceLookup(parent, id, "PublishIntroSet")
Endpoint* parent,
uint64_t id,
EncryptedIntroSet introset,
uint64_t relayOrder,
llarp_time_t timeout)
: IServiceLookup(parent, id, "PublishIntroSet", timeout)
, m_IntroSet(std::move(introset))
, m_Endpoint(parent)
, m_relayOrder(relayOrder)
@ -663,6 +671,8 @@ namespace llarp
}
}
constexpr auto PublishIntrosetTimeout = 20s;
bool
Endpoint::PublishIntroSetVia(
const EncryptedIntroSet& introset,
@ -670,7 +680,8 @@ namespace llarp
path::Path_ptr path,
uint64_t relayOrder)
{
auto job = new PublishIntroSetJob(this, GenTXID(), introset, relayOrder);
auto job =
new PublishIntroSetJob(this, GenTXID(), introset, relayOrder, PublishIntrosetTimeout);
if (job->SendRequestViaPath(path, r))
{
m_state->m_LastPublishAttempt = Now();
@ -747,6 +758,8 @@ namespace llarp
path::Builder::PathBuildStarted(path);
}
constexpr auto MaxOutboundContextPerRemote = 4;
void
Endpoint::PutNewOutboundContext(const service::IntroSet& introset, llarp_time_t left)
{
@ -755,7 +768,7 @@ namespace llarp
auto& remoteSessions = m_state->m_RemoteSessions;
auto& serviceLookups = m_state->m_PendingServiceLookups;
if (remoteSessions.count(addr) >= MAX_OUTBOUND_CONTEXT_COUNT)
if (remoteSessions.count(addr) >= MaxOutboundContextPerRemote)
{
auto itr = remoteSessions.find(addr);
@ -930,9 +943,10 @@ namespace llarp
if (result)
{
var::visit(
[&](auto&& value) {
[&result, &cache, name](auto&& value) {
if (value.IsZero())
{
cache.Remove(name);
result = std::nullopt;
}
},
@ -942,10 +956,6 @@ namespace llarp
{
cache.Put(name, *result);
}
else
{
cache.Remove(name);
}
handler(result);
};
@ -955,7 +965,7 @@ namespace llarp
for (const auto& path : paths)
{
LogInfo(Name(), " lookup ", name, " from ", path->Endpoint());
auto job = new LookupNameJob(this, GenTXID(), name, resultHandler);
auto job = new LookupNameJob{this, GenTXID(), name, resultHandler};
job->SendRequestViaPath(path, m_router);
}
}
@ -1003,7 +1013,7 @@ namespace llarp
msg.S = path->NextSeqNo();
if (path && path->SendRoutingMessage(msg, Router()))
{
RouterLookupJob job(this, handler);
RouterLookupJob job{this, handler};
assert(msg.M.size() == 1);
auto dhtMsg = dynamic_cast<FindRouterMessage*>(msg.M[0].get());
@ -1011,7 +1021,7 @@ namespace llarp
m_router->NotifyRouterEvent<tooling::FindRouterSentEvent>(m_router->pubkey(), *dhtMsg);
routers.emplace(router, RouterLookupJob(this, handler));
routers.emplace(router, std::move(job));
return true;
}
}
@ -1164,6 +1174,9 @@ namespace llarp
Endpoint::SendAuthResult(
path::Path_ptr path, PathID_t replyPath, ConvoTag tag, AuthResult result)
{
// not applicable because we are not an exit or don't have an endpoint auth policy
if ((not m_state->m_ExitEnabled) or m_AuthPolicy == nullptr)
return;
ProtocolFrame f;
f.R = AuthResultCodeAsInt(result.code);
f.T = tag;
@ -1263,6 +1276,7 @@ namespace llarp
void
Endpoint::HandlePathDied(path::Path_ptr p)
{
m_router->routerProfiling().MarkPathTimeout(p.get());
ManualRebuild(1);
RegenAndPublishIntroSet();
path::Builder::HandlePathDied(p);
@ -1346,13 +1360,8 @@ namespace llarp
// add response hook to list for address.
m_state->m_PendingServiceLookups.emplace(remote, hook);
auto& lookupTimes = m_state->m_LastServiceLookupTimes;
const auto now = Now();
// if most recent lookup was within last INTROSET_LOOKUP_RETRY_COOLDOWN
// just add callback to the list and return
if (lookupTimes.find(remote) != lookupTimes.end()
&& now < (lookupTimes[remote] + INTROSET_LOOKUP_RETRY_COOLDOWN))
/// check replay filter
if (not m_IntrosetLookupFilter.Insert(remote))
return true;
const auto paths = GetManyPathsWithUniqueEndpoints(this, NumParallelLookups);
@ -1376,6 +1385,7 @@ namespace llarp
},
location,
PubKey{remote.as_array()},
path->Endpoint(),
order,
GenTXID(),
timeout);
@ -1391,12 +1401,7 @@ namespace llarp
order++;
if (job->SendRequestViaPath(path, Router()))
{
if (not hookAdded)
{
// if any of the lookups is successful, set last lookup time
lookupTimes[remote] = now;
hookAdded = true;
}
hookAdded = true;
}
else
LogError(Name(), " send via path failed for lookup");
@ -1608,8 +1613,8 @@ namespace llarp
}
if (session.inbound)
{
auto path = GetPathByRouter(session.intro.router);
if (path)
auto path = GetPathByRouter(session.replyIntro.router);
if (path and path->IsReady())
{
const auto rttEstimate = (session.replyIntro.latency + path->intro.latency) * 2;
if (rttEstimate < rtt)
@ -1618,10 +1623,6 @@ namespace llarp
rtt = rttEstimate;
}
}
else
{
LogWarn("no path for inbound session T=", tag);
}
}
else
{
@ -1849,7 +1850,7 @@ namespace llarp
}
self->m_state->m_PendingTraffic.erase(addr);
},
1500ms);
PathAlignmentTimeout());
return true;
}
LogDebug("SendOrQueue failed: no inbound/outbound sessions");

@ -61,8 +61,6 @@ namespace llarp
public IDataHandler,
public EndpointBase
{
static const size_t MAX_OUTBOUND_CONTEXT_COUNT = 1;
Endpoint(AbstractRouter* r, Context* parent);
~Endpoint() override;
@ -308,6 +306,13 @@ namespace llarp
bool
ShouldBuildMore(llarp_time_t now) const override;
virtual llarp_time_t
PathAlignmentTimeout() const
{
constexpr auto DefaultPathAlignmentTimeout = 30s;
return DefaultPathAlignmentTimeout;
}
bool
EnsurePathTo(
std::variant<Address, RouterID> addr,
@ -525,6 +530,9 @@ namespace llarp
ConvoMap& Sessions();
// clang-format on
thread::Queue<RecvDataEvent> m_RecvQueue;
/// for rate limiting introset lookups
util::DecayingHashSet<Address> m_IntrosetLookupFilter;
};
using Endpoint_ptr = std::shared_ptr<Endpoint>;

@ -50,7 +50,7 @@ namespace llarp
{
--tries;
const auto path = ep->PickRandomEstablishedPath();
if (path)
if (path and path->IsReady())
paths.emplace(path);
} while (tries > 0 and paths.size() < N);
return paths;

@ -13,6 +13,7 @@ namespace llarp
HandlerFunc h,
const dht::Key_t& l,
const PubKey& k,
const RouterID& ep,
uint64_t order,
uint64_t tx,
llarp_time_t timeout)
@ -21,13 +22,15 @@ namespace llarp
, relayOrder(order)
, location(l)
, handle(std::move(h))
{}
{
endpoint = ep;
}
bool
HiddenServiceAddressLookup::HandleIntrosetResponse(const std::set<EncryptedIntroSet>& results)
{
std::optional<IntroSet> found;
const Address remote(rootkey);
const Address remote{rootkey};
if (results.size() > 0)
{
EncryptedIntroSet selected;

@ -23,6 +23,7 @@ namespace llarp
HandlerFunc h,
const dht::Key_t& location,
const PubKey& rootkey,
const RouterID& routerAsked,
uint64_t relayOrder,
uint64_t tx,
llarp_time_t timeout);

@ -45,20 +45,19 @@ namespace llarp
if (dst == remoteIntro.pathID && remoteIntro.router == p->Endpoint())
{
LogWarn(Name(), " message ", seq, " dropped by endpoint ", p->Endpoint(), " via ", dst);
if (MarkCurrentIntroBad(Now()))
{
SwapIntros();
}
UpdateIntroSet();
MarkCurrentIntroBad(Now());
ShiftIntroduction(false);
}
return true;
}
constexpr auto OutboundContextNumPaths = 2;
OutboundContext::OutboundContext(const IntroSet& introset, Endpoint* parent)
: path::Builder(parent->Router(), 4, parent->numHops)
, SendContext(introset.addressKeys, {}, this, parent)
, location(introset.addressKeys.Addr().ToKey())
, currentIntroSet(introset)
: path::Builder{parent->Router(), OutboundContextNumPaths, parent->numHops}
, SendContext{introset.addressKeys, {}, this, parent}
, location{introset.addressKeys.Addr().ToKey()}
, currentIntroSet{introset}
{
updatingIntroSet = false;
@ -243,8 +242,12 @@ namespace llarp
void
OutboundContext::UpdateIntroSet()
{
if (updatingIntroSet || markedBad)
constexpr auto IntrosetUpdateInterval = 10s;
const auto now = Now();
if (updatingIntroSet or markedBad or now < m_LastIntrosetUpdateAt + IntrosetUpdateInterval)
return;
LogInfo(Name(), " updating introset");
m_LastIntrosetUpdateAt = now;
const auto addr = currentIntroSet.addressKeys.Addr();
// we want to use the parent endpoint's paths because outbound context
// does not implement path::PathSet::HandleGotIntroMessage
@ -257,6 +260,7 @@ namespace llarp
util::memFn(&OutboundContext::OnIntroSetUpdate, shared_from_this()),
location,
PubKey{addr.as_array()},
path->Endpoint(),
relayOrder,
m_Endpoint->GenTXID(),
5s);
@ -410,40 +414,17 @@ namespace llarp
return t >= now + path::default_lifetime / 4;
}
bool
void
OutboundContext::MarkCurrentIntroBad(llarp_time_t now)
{
return MarkIntroBad(remoteIntro, now);
MarkIntroBad(remoteIntro, now);
}
bool
void
OutboundContext::MarkIntroBad(const Introduction& intro, llarp_time_t now)
{
// insert bad intro
m_BadIntros[intro] = now;
// try shifting intro without rebuild
if (ShiftIntroduction(false))
{
// we shifted
// check if we have a path to the next intro router
if (GetNewestPathByRouter(m_NextIntro.router))
return true;
// we don't have a path build one if we aren't building too fast
if (!BuildCooldownHit(now))
BuildOneAlignedTo(m_NextIntro.router);
return true;
}
// we didn't shift check if we should update introset
if (now - lastShift >= MIN_SHIFT_INTERVAL || currentIntroSet.HasExpiredIntros(now)
|| currentIntroSet.IsExpired(now))
{
// update introset
LogInfo(Name(), " updating introset");
UpdateIntroSet();
return true;
}
return false;
}
bool
@ -587,7 +568,7 @@ namespace llarp
// verify source
if (!frame.Verify(si))
{
LogWarn("signature failed");
LogWarn("signature verification failed, T=", frame.T);
return false;
}
// remove convotag it doesn't exist

@ -60,10 +60,10 @@ namespace llarp
ShiftIntroRouter(const RouterID remote);
/// mark the current remote intro as bad
bool
void
MarkCurrentIntroBad(llarp_time_t now) override;
bool
void
MarkIntroBad(const Introduction& marked, llarp_time_t now);
/// return true if we are ready to send
@ -153,6 +153,7 @@ namespace llarp
bool m_GotInboundTraffic = false;
bool sentIntro = false;
std::function<void(OutboundContext*)> m_ReadyHook;
llarp_time_t m_LastIntrosetUpdateAt = 0s;
};
} // namespace service

@ -65,7 +65,7 @@ namespace llarp
virtual void
UpdateIntroSet() = 0;
virtual bool
virtual void
MarkCurrentIntroBad(llarp_time_t now) = 0;
void

@ -27,7 +27,7 @@ namespace llarp
const auto lastUsed = std::max(lastSend, lastRecv);
if (lastUsed == 0s)
return intro.IsExpired(now);
return now > lastUsed && (now - lastUsed > lifetime || intro.IsExpired(now));
return now >= lastUsed && (now - lastUsed > lifetime);
}
void

@ -45,7 +45,6 @@ namespace llarp
if (now == 0s)
now = llarp::time_now_ms();
EraseIf([&](const auto& item) { return (m_CacheInterval + item.second) <= now; });
m_Values.rehash(0);
}
Time_t

@ -24,7 +24,7 @@ namespace llarp
LogContext();
LogLevel curLevel = eLogInfo;
LogLevel startupLevel = eLogInfo;
LogLevel runtimeLevel = eLogInfo;
LogLevel runtimeLevel = eLogWarn;
ILogStream_ptr logStream;
std::string nodeName = "lokinet";

@ -80,11 +80,6 @@ TEST_CASE("Bogon")
REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(192, 168, 1, 111)));
}
SECTION("Bogon_DoD_8")
{
REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(21, 3, 37, 70)));
}
SECTION("Bogon_127_8")
{
REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(127, 0, 0, 1)));

@ -6,7 +6,7 @@
llarp::RuntimeOptions opts = {false, false, false};
/// make a llarp_main* with 1 endpoint that specifies a keyfile
/// make a context with 1 endpoint that specifies a keyfile
static std::shared_ptr<llarp::Context>
make_context(std::optional<fs::path> keyfile)
{

Loading…
Cancel
Save