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, werror=true,
cmake_extra='', cmake_extra='',
extra_cmds=[], extra_cmds=[],
jobs=6,
loki_repo=false, loki_repo=false,
allow_fail=false) = { allow_fail=false) = {
kind: 'pipeline', kind: 'pipeline',
@ -55,7 +56,7 @@ local debian_pipeline(name, image,
(if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') +
'-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') + '-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') +
cmake_extra, cmake_extra,
'ninja -v', 'ninja -j' + jobs + ' -v',
'../contrib/ci/drone-gdb.sh ./test/testAll --use-colour yes', '../contrib/ci/drone-gdb.sh ./test/testAll --use-colour yes',
] + extra_cmds, ] + extra_cmds,
} }
@ -93,6 +94,7 @@ local windows_cross_pipeline(name, image,
cmake_extra='', cmake_extra='',
toolchain='32', toolchain='32',
extra_cmds=[], extra_cmds=[],
jobs=6,
allow_fail=false) = { allow_fail=false) = {
kind: 'pipeline', kind: 'pipeline',
type: 'docker', type: 'docker',
@ -121,7 +123,7 @@ local windows_cross_pipeline(name, image,
(if lto then '' else '-DWITH_LTO=OFF ') + (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" + "-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, cmake_extra,
'ninja -v package', 'ninja -j' + jobs + ' -v package',
] + extra_cmds, ] + extra_cmds,
} }
], ],
@ -178,7 +180,13 @@ local deb_builder(image, distro, distro_branch, arch='amd64', loki_repo=true) =
// Macos build // 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', kind: 'pipeline',
type: 'exec', type: 'exec',
name: name, name: name,
@ -198,7 +206,7 @@ local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra
'cd build', 'cd build',
'cmake .. -G Ninja -DCMAKE_CXX_FLAGS=-fcolor-diagnostics -DCMAKE_BUILD_TYPE='+build_type+' ' + 'cmake .. -G Ninja -DCMAKE_CXX_FLAGS=-fcolor-diagnostics -DCMAKE_BUILD_TYPE='+build_type+' ' +
(if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + cmake_extra, (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + cmake_extra,
'ninja -v', 'ninja -j' + jobs + ' -v',
'./test/testAll --use-colour yes', './test/testAll --use-colour yes',
] + extra_cmds, ] + 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), cmake_extra='-DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8', loki_repo=true),
// ARM builds (ARM64 and armhf) // ARM builds (ARM64 and armhf)
debian_pipeline("Debian sid (ARM64)", "debian:sid", arch="arm64"), 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'), debian_pipeline("Debian buster (armhf)", "arm32v7/debian:buster", arch="arm64", cmake_extra='-DDOWNLOAD_SODIUM=ON', jobs=4),
// Static armhf build (gets uploaded) // Static armhf build (gets uploaded)
debian_pipeline("Static (buster armhf)", "arm32v7/debian:buster", arch="arm64", deps='g++ python3-dev automake libtool', 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 ' + 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=[ extra_cmds=[
'../contrib/ci/drone-check-static-libs.sh', '../contrib/ci/drone-check-static-libs.sh',
'UPLOAD_OS=linux-armhf ../contrib/ci/drone-static-upload.sh' 'UPLOAD_OS=linux-armhf ../contrib/ci/drone-static-upload.sh'
]), ],
jobs=4),
// android apk builder // android apk builder
apk_builder("android apk", "registry.oxen.rocks/lokinet-ci-android", extra_cmds=['UPLOAD_OS=anrdoid ../contrib/ci/drone-static-upload.sh']), 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() endif()
project(lokinet project(lokinet
VERSION 0.9.0 VERSION 0.9.1
DESCRIPTION "lokinet - IP packet onion router" DESCRIPTION "lokinet - IP packet onion router"
LANGUAGES C CXX) 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_MAJOR=${lokinet_VERSION_MAJOR})
add_definitions(-DLLARP_VERSION_MINOR=${lokinet_VERSION_MINOR}) add_definitions(-DLLARP_VERSION_MINOR=${lokinet_VERSION_MINOR})

@ -114,11 +114,17 @@ public class LokinetDaemon extends VpnService
builder.setMtu(1500); builder.setMtu(1500);
String[] parts = ourRange.split("/"); String[] parts = ourRange.split("/");
String ourIP = parts[0]; String ourIPv4 = parts[0];
int ourMask = Integer.parseInt(parts[1]); int ourMask = Integer.parseInt(parts[1]);
builder.addAddress(ourIP, ourMask); // set ip4
builder.addAddress(ourIPv4, ourMask);
builder.addRoute("0.0.0.0", 0); 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.addDnsServer(upstreamDNS);
builder.setSession("Lokinet"); builder.setSession("Lokinet");
builder.setConfigureIntent(null); builder.setConfigureIntent(null);
@ -134,24 +140,10 @@ public class LokinetDaemon extends VpnService
InjectVPNFD(); 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(() -> { new Thread(() -> {
Configure(config);
m_UDPSocket = GetUDPSocket();
protect(m_UDPSocket);
Mainloop(); Mainloop();
}).start(); }).start();

@ -25,14 +25,14 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${CMAKE_BINARY_DIR}/lokinet-g
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui) install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui)
install(PROGRAMS ${TUNTAP_EXE} DESTINATION bin) install(PROGRAMS ${TUNTAP_EXE} DESTINATION bin COMPONENT tuntap)
install(FILES ${BOOTSTRAP_FILE} DESTINATION share) install(FILES ${BOOTSTRAP_FILE} DESTINATION share COMPONENT lokinet)
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet") set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet")
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/win32-setup/lokinet.ico") set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/win32-setup/lokinet.ico")
set(CPACK_NSIS_DEFINES "RequestExecutionLevel admin") set(CPACK_NSIS_DEFINES "RequestExecutionLevel admin")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) 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_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 set(CPACK_NSIS_CREATE_ICONS_EXTRA
"CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Lokinet.lnk' '$INSTDIR\\\\share\\\\gui\\\\lokinet-gui.exe'" "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 set(CPACK_NSIS_DELETE_ICONS_EXTRA
"Delete '$SMPROGRAMS\\\\$START_MENU\\\\Lokinet.lnk'" "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 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: class Monitor:
@ -26,7 +52,7 @@ class Monitor:
self._rpc_socket.connect(url) self._rpc_socket.connect(url)
self._speed_samples = [(0,0,0,0)] * self._sample_size self._speed_samples = [(0,0,0,0)] * self._sample_size
self._run = True self._run = True
def rpc(self, method): def rpc(self, method):
self._rpc_socket.send_multipart([method.encode(), b'lokinetmon'+method.encode()]) self._rpc_socket.send_multipart([method.encode(), b'lokinetmon'+method.encode()])
if not self._rpc_socket.poll(timeout=50): if not self._rpc_socket.poll(timeout=50):
@ -34,10 +60,10 @@ class Monitor:
reply = self._rpc_socket.recv_multipart() reply = self._rpc_socket.recv_multipart()
if len(reply) >= 3 and reply[0:2] == [b'REPLY', b'lokinetmon'+method.encode()]: if len(reply) >= 3 and reply[0:2] == [b'REPLY', b'lokinetmon'+method.encode()]:
return reply[2].decode() return reply[2].decode()
def _close(self): def _close(self):
self._rpc_socket.close(linger=0) self._rpc_socket.close(linger=0)
self._run = False self._run = False
curses.endwin() curses.endwin()
def update_data(self): def update_data(self):
@ -62,7 +88,11 @@ class Monitor:
y_pos += 1 y_pos += 1
self.win.addstr("me -> ") self.win.addstr("me -> ")
for hop in path["hops"]: 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(" [{} ms latency]".format(path["intro"]["latency"]))
self.win.addstr(" [{} until expire]".format(self.time_to(path["expiresAt"]))) self.win.addstr(" [{} until expire]".format(self.time_to(path["expiresAt"])))
if path["expiresSoon"]: if path["expiresSoon"]:
@ -174,7 +204,7 @@ class Monitor:
barstr = "#" * (samp - badsamp) barstr = "#" * (samp - badsamp)
pad = " " * (maxsamp - samp) pad = " " * (maxsamp - samp)
return pad, barstr, '#' * badsamp return pad, barstr, '#' * badsamp
def display_speedgraph(self, y_pos, maxsz=40): def display_speedgraph(self, y_pos, maxsz=40):
""" display global speed graph """ """ display global speed graph """
txmax, rxmax = 1024, 1024 txmax, rxmax = 1024, 1024
@ -260,9 +290,13 @@ class Monitor:
self.win.move(y_pos, 1) self.win.move(y_pos, 1)
self.txrate += sess["txRateCurrent"] self.txrate += sess["txRateCurrent"]
self.rxrate += sess["rxRateCurrent"] self.rxrate += sess["rxRateCurrent"]
addr = sess['remoteAddr']
if geo:
ip = addr.split(':')[0]
addr += '\t{}'.format(ip_to_flag(ip))
self.win.addstr( self.win.addstr(
"{}\t[{}\ttx]\t[{}\trx]".format( "{}\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: if (sess['txMsgQueueSize'] or 0) > 1:
@ -333,7 +367,7 @@ class Monitor:
self.version = json.loads(self.rpc("llarp.version"))['result']['version'] self.version = json.loads(self.rpc("llarp.version"))['result']['version']
except: except:
self.version = None self.version = None
while self._run: while self._run:
if self.update_data(): if self.update_data():
self.win.box() 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 ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
sudo systemctl enable --now systemd-resolved 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_PACKAGE=ON \
-DBUILD_SHARED_LIBS=OFF \ -DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \ -DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=ON \
-DWITH_TESTS=OFF \ -DWITH_TESTS=OFF \
-DNATIVE_BUILD=OFF \ -DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \ -DSTATIC_LINK=ON \

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

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

@ -659,6 +659,21 @@ namespace llarp
m_SRVRecords.push_back(std::move(newSRV)); 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: // Deprecated options:
conf.defineOption<std::string>("network", "enabled", Deprecated); conf.defineOption<std::string>("network", "enabled", Deprecated);
} }
@ -989,7 +1004,7 @@ namespace llarp
constexpr Default DefaultLogType{"file"}; constexpr Default DefaultLogType{"file"};
constexpr Default DefaultLogFile{""}; constexpr Default DefaultLogFile{""};
constexpr Default DefaultLogLevel{"info"}; constexpr Default DefaultLogLevel{"warn"};
conf.defineOption<std::string>( conf.defineOption<std::string>(
"logging", "logging",

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

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

@ -368,7 +368,10 @@ namespace llarp::uv
bool bool
Loop::inEventLoop() const 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 } // namespace llarp::uv

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

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

@ -600,6 +600,40 @@ namespace llarp
return false; 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 bool
IsBogon(const in6_addr& addr) IsBogon(const in6_addr& addr)
{ {
@ -607,11 +641,14 @@ namespace llarp
(void)addr; (void)addr;
return false; return false;
#else #else
if (!ipv6_is_mapped_ipv4(addr)) if (not ipv6_is_mapped_ipv4(addr))
{ {
static in6_addr zero = {}; const auto ip = net::In6ToHUInt(addr);
if (addr == zero) for (const auto& range : bogonRanges_v6)
return true; {
if (range.Contains(ip))
return true;
}
return false; return false;
} }
return IsIPv4Bogon( return IsIPv4Bogon(
@ -636,28 +673,10 @@ namespace llarp
} }
#if !defined(TESTNET) #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 bool
IsIPv4Bogon(const huint32_t& addr) IsIPv4Bogon(const huint32_t& addr)
{ {
for (const auto& bogon : bogonRanges) for (const auto& bogon : bogonRanges_v4)
{ {
if (bogon.Contains(addr)) if (bogon.Contains(addr))
{ {

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

@ -342,6 +342,17 @@ namespace llarp
return {m_addr4.sin_addr.s_addr}; 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 void
SockAddr::setIPv4(nuint32_t ip) SockAddr::setIPv4(nuint32_t ip)
{ {

@ -303,7 +303,9 @@ namespace llarp
util::StatusObject util::StatusObject
PathHopConfig::ExtractStatus() const PathHopConfig::ExtractStatus() const
{ {
const auto ip = net::In6ToHUInt(rc.addrs[0].ip);
util::StatusObject obj{ util::StatusObject obj{
{"ip", ip.ToString()},
{"lifetime", to_json(lifetime)}, {"lifetime", to_json(lifetime)},
{"router", rc.pubkey.ToHex()}, {"router", rc.pubkey.ToHex()},
{"txid", txID.ToHex()}, {"txid", txID.ToHex()},
@ -410,9 +412,6 @@ namespace llarp
auto dlt = now - m_LastLatencyTestTime; auto dlt = now - m_LastLatencyTestTime;
if (dlt > path::latency_interval && m_LastLatencyTestID == 0) 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; routing::PathLatencyMessage latency;
latency.T = randint(); latency.T = randint();
m_LastLatencyTestID = latency.T; 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 bool
Path::Expired(llarp_time_t now) const Path::Expired(llarp_time_t now) const
{ {
@ -504,7 +506,11 @@ namespace llarp
return true; return true;
if (_status == ePathBuilding) if (_status == ePathBuilding)
return false; return false;
if (_status == ePathEstablished || _status == ePathTimeout) if (_status == ePathTimeout)
{
return now >= m_LastRecvMessage + PathReanimationTimeout;
}
if (_status == ePathEstablished)
{ {
return now >= ExpireTime(); return now >= ExpireTime();
} }

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

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

@ -15,11 +15,31 @@ namespace llarp
static constexpr auto MIN_PATH_BUILD_INTERVAL = 500ms; static constexpr auto MIN_PATH_BUILD_INTERVAL = 500ms;
static constexpr auto PATH_BUILD_RATE = 100ms; 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 struct Builder : public PathSet
{ {
private: private:
llarp_time_t m_LastWarn = 0s; llarp_time_t m_LastWarn = 0s;
util::DecayingHashSet<RouterID> m_EdgeLimiter;
protected: protected:
/// flag for PathSet::Stop() /// flag for PathSet::Stop()

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

@ -50,17 +50,20 @@ namespace llarp
{ {
Profiling(); Profiling();
inline static const int profiling_chances = 4;
/// generic variant /// generic variant
bool 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 /// check if this router should have paths built over it
bool 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 /// check if this router should be connected directly to
bool 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 void
MarkConnectTimeout(const RouterID& r) EXCLUDES(m_ProfilesMutex); MarkConnectTimeout(const RouterID& r) EXCLUDES(m_ProfilesMutex);

@ -292,6 +292,16 @@ namespace llarp
virtual bool virtual bool
ConnectionToRouterAllowed(const RouterID& router) const = 0; 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 /// return true if we have at least 1 session to this router in either
/// direction /// direction
virtual bool virtual bool

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

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

@ -15,14 +15,17 @@ namespace llarp
{ {
const PathID_t OutboundMessageHandler::zeroID; const PathID_t OutboundMessageHandler::zeroID;
using namespace std::chrono_literals;
OutboundMessageHandler::OutboundMessageHandler(size_t maxQueueSize) OutboundMessageHandler::OutboundMessageHandler(size_t maxQueueSize)
: outboundQueue(maxQueueSize), removedPaths(20), removedSomePaths(false) : outboundQueue(maxQueueSize), recentlyRemovedPaths(5s), removedSomePaths(false)
{} {}
bool bool
OutboundMessageHandler::QueueMessage( OutboundMessageHandler::QueueMessage(
const RouterID& remote, const ILinkMessage& msg, SendStatusHandler callback) 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)) if (not _linkManager->SessionIsClient(remote) and not _lookupHandler->RemoteIsAllowed(remote))
{ {
DoCallback(callback, SendStatus::InvalidRouter); DoCallback(callback, SendStatus::InvalidRouter);
@ -44,26 +47,31 @@ namespace llarp
std::copy_n(buf.base, buf.sz, message.first.data()); 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)) if (_linkManager->HasSessionTo(remote))
{ {
QueueOutboundMessage(remote, std::move(message), msg.pathid, priority); QueueOutboundMessage(remote, std::move(message), msg.pathid, priority);
return true; 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; bool shouldCreateSession = false;
{ {
util::Lock l(_mutex); util::Lock l(_mutex);
// create queue for <remote> if it doesn't exist, and get iterator // 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; MessageQueueEntry entry;
entry.priority = priority; entry.priority = priority;
entry.message = message; entry.message = message;
entry.router = remote; 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) if (shouldCreateSession)
@ -77,26 +85,33 @@ namespace llarp
void void
OutboundMessageHandler::Tick() OutboundMessageHandler::Tick()
{ {
m_Killer.TryAccess([self = this]() { m_Killer.TryAccess([this]() {
self->ProcessOutboundQueue(); recentlyRemovedPaths.Decay();
self->RemoveEmptyPathQueues(); ProcessOutboundQueue();
self->SendRoundRobin(); SendRoundRobin();
}); });
} }
void void
OutboundMessageHandler::QueueRemoveEmptyPath(const PathID_t& pathid) OutboundMessageHandler::RemovePath(const PathID_t& pathid)
{ {
m_Killer.TryAccess([self = this, pathid]() { m_Killer.TryAccess([this, pathid]() {
if (self->removedPaths.full()) /* 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 util::StatusObject
OutboundMessageHandler::ExtractStatus() const OutboundMessageHandler::ExtractStatus() const
{ {
@ -241,6 +256,8 @@ namespace llarp
{ {
MessageQueueEntry entry; MessageQueueEntry entry;
entry.message = std::move(msg); 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; auto callback_copy = entry.message.second;
entry.router = remote; entry.router = remote;
entry.pathid = pathid; entry.pathid = pathid;
@ -266,17 +283,23 @@ namespace llarp
{ {
while (not outboundQueue.empty()) while (not outboundQueue.empty())
{ {
// TODO: can we add util::thread::Queue::front() for move semantics here?
MessageQueueEntry entry = outboundQueue.popFront(); 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); 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()) 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 void
OutboundMessageHandler::SendRoundRobin() OutboundMessageHandler::SendRoundRobin()
{ {
m_queueStats.numTicks++; m_queueStats.numTicks++;
// send non-routing messages first priority // send routing messages first priority
auto& non_routing_mq = outboundMessageQueues[zeroID]; auto& routing_mq = outboundMessageQueues[zeroID];
while (not non_routing_mq.empty()) while (not routing_mq.empty())
{ {
const MessageQueueEntry& entry = non_routing_mq.top(); const MessageQueueEntry& entry = routing_mq.top();
Send(entry.router, entry.message); Send(entry.router, entry.message);
non_routing_mq.pop(); routing_mq.pop();
} }
size_t empty_count = 0; size_t empty_count = 0;
size_t num_queues = roundRobinOrder.size(); 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) if (removedSomePaths)
{ {
for (size_t i = 0; i < num_queues; i++) for (size_t i = 0; i < num_queues; i++)
@ -338,6 +345,7 @@ namespace llarp
} }
} }
} }
removedSomePaths = false;
num_queues = roundRobinOrder.size(); num_queues = roundRobinOrder.size();
size_t sent_count = 0; size_t sent_count = 0;
@ -346,7 +354,10 @@ namespace llarp
return; 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()); PathID_t pathid = std::move(roundRobinOrder.front());
roundRobinOrder.pop(); roundRobinOrder.pop();

@ -4,6 +4,7 @@
#include <llarp/ev/ev.hpp> #include <llarp/ev/ev.hpp>
#include <llarp/util/thread/queue.hpp> #include <llarp/util/thread/queue.hpp>
#include <llarp/util/decaying_hashset.hpp>
#include <llarp/path/path_types.hpp> #include <llarp/path/path_types.hpp>
#include <llarp/router_id.hpp> #include <llarp/router_id.hpp>
@ -27,15 +28,45 @@ namespace llarp
OutboundMessageHandler(size_t maxQueueSize = MAX_OUTBOUND_QUEUE_SIZE); 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 bool
QueueMessage(const RouterID& remote, const ILinkMessage& msg, SendStatusHandler callback) QueueMessage(const RouterID& remote, const ILinkMessage& msg, SendStatusHandler callback)
override EXCLUDES(_mutex); 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 void
Tick() override; Tick() override;
/* Called from outside this class to inform it that a path has died / expired
* and its queue should be discarded.
*/
void void
QueueRemoveEmptyPath(const PathID_t& pathid) override; RemovePath(const PathID_t& pathid) override;
util::StatusObject util::StatusObject
ExtractStatus() const override; ExtractStatus() const override;
@ -46,6 +77,9 @@ namespace llarp
private: private:
using Message = std::pair<std::vector<byte_t>, SendStatusHandler>; 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 struct MessageQueueEntry
{ {
uint16_t priority; uint16_t priority;
@ -73,6 +107,14 @@ namespace llarp
using MessageQueue = std::priority_queue<MessageQueueEntry>; 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 void
OnSessionEstablished(const RouterID& router); OnSessionEstablished(const RouterID& router);
@ -91,6 +133,7 @@ namespace llarp
void void
OnSessionResult(const RouterID& router, const SessionResult result); OnSessionResult(const RouterID& router, const SessionResult result);
/* queues a message's send result callback onto the event loop */
void void
DoCallback(SendStatusHandler callback, SendStatus status); DoCallback(SendStatusHandler callback, SendStatus status);
@ -100,30 +143,62 @@ namespace llarp
bool bool
EncodeBuffer(const ILinkMessage& msg, llarp_buffer_t& buf); 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 bool
Send(const RouterID& remote, const Message& msg); 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 bool
SendIfSession(const RouterID& remote, const Message& msg); 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 bool
QueueOutboundMessage( QueueOutboundMessage(
const RouterID& remote, Message&& msg, const PathID_t& pathid, uint16_t priority = 0); 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 void
ProcessOutboundQueue(); 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 void
SendRoundRobin(); 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 void
FinalizeSessionRequest(const RouterID& router, SendStatus status) EXCLUDES(_mutex); FinalizeSessionRequest(const RouterID& router, SendStatus status) EXCLUDES(_mutex);
llarp::thread::Queue<MessageQueueEntry> outboundQueue; llarp::thread::Queue<MessageQueueEntry> outboundQueue;
llarp::thread::Queue<PathID_t> removedPaths; llarp::util::DecayingHashSet<PathID_t> recentlyRemovedPaths;
bool removedSomePaths; bool removedSomePaths;
mutable util::Mutex _mutex; // protects pendingSessionMessageQueues mutable util::Mutex _mutex; // protects pendingSessionMessageQueues

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

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

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

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

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

@ -374,9 +374,21 @@ namespace llarp
return inbound_routing_msg_parser.ParseMessageBuffer(buf, h, rxid, this); 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 bool
Router::ConnectionToRouterAllowed(const RouterID& router) const 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); return _rcLookupHandler.RemoteIsAllowed(router);
} }
@ -735,7 +747,8 @@ namespace llarp
{ {
ss << " snode | known/svc/clients: " << nodedb()->NumLoaded() << "/" ss << " snode | known/svc/clients: " << nodedb()->NumLoaded() << "/"
<< NumberOfConnectedRouters() << "/" << NumberOfConnectedClients() << " | " << NumberOfConnectedRouters() << "/" << NumberOfConnectedClients() << " | "
<< pathContext().CurrentTransitPaths() << " active paths"; << pathContext().CurrentTransitPaths() << " active paths | "
<< "block " << m_lokidRpcClient->BlockHeight();
} }
else else
{ {
@ -752,6 +765,8 @@ namespace llarp
} }
#endif #endif
m_PathBuildLimiter.Decay(now);
routerProfiling().Tick(); routerProfiling().Tick();
if (ShouldReportStats(now)) if (ShouldReportStats(now))
@ -763,7 +778,9 @@ namespace llarp
_rcLookupHandler.PeriodicUpdate(now); _rcLookupHandler.PeriodicUpdate(now);
const bool gotWhitelist = _rcLookupHandler.HaveReceivedWhitelist();
const bool isSvcNode = IsServiceNode(); const bool isSvcNode = IsServiceNode();
const bool looksDeregistered = LooksDeregistered();
if (_rc.ExpiresSoon(now, std::chrono::milliseconds(randint() % 10000)) if (_rc.ExpiresSoon(now, std::chrono::milliseconds(randint() % 10000))
|| (now - _rc.last_updated) > rcRegenInterval) || (now - _rc.last_updated) > rcRegenInterval)
@ -772,11 +789,10 @@ namespace llarp
if (!UpdateOurRC(false)) if (!UpdateOurRC(false))
LogError("Failed to update our RC"); LogError("Failed to update our RC");
} }
else else if (not looksDeregistered)
{ {
GossipRCIfNeeded(_rc); GossipRCIfNeeded(_rc);
} }
const bool gotWhitelist = _rcLookupHandler.HaveReceivedWhitelist();
// remove RCs for nodes that are no longer allowed by network policy // remove RCs for nodes that are no longer allowed by network policy
nodedb()->RemoveIf([&](const RouterContact& rc) -> bool { nodedb()->RemoveIf([&](const RouterContact& rc) -> bool {
// don't purge bootstrap nodes from nodedb // don't purge bootstrap nodes from nodedb
@ -844,7 +860,7 @@ namespace llarp
const int interval = isSvcNode ? 5 : 2; const int interval = isSvcNode ? 5 : 2;
const auto timepoint_now = Clock_t::now(); const auto timepoint_now = Clock_t::now();
if (timepoint_now >= m_NextExploreAt) if (timepoint_now >= m_NextExploreAt and not looksDeregistered)
{ {
_rcLookupHandler.ExploreNetwork(); _rcLookupHandler.ExploreNetwork();
m_NextExploreAt = timepoint_now + std::chrono::seconds(interval); m_NextExploreAt = timepoint_now + std::chrono::seconds(interval);
@ -856,7 +872,17 @@ namespace llarp
connectToNum = strictConnect; 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; size_t dlt = connectToNum - connected;
LogDebug("connecting to ", dlt, " random routers to keep alive"); LogDebug("connecting to ", dlt, " random routers to keep alive");
@ -883,7 +909,16 @@ namespace llarp
if (m_peerDb->shouldFlush(now)) if (m_peerDb->shouldFlush(now))
{ {
LogDebug("Queing database flush..."); 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; LMQ_ptr m_lmq;
path::BuildLimiter m_PathBuildLimiter;
path::BuildLimiter&
pathBuildLimiter() override
{
return m_PathBuildLimiter;
}
const LMQ_ptr& const LMQ_ptr&
lmq() const override lmq() const override
{ {
@ -173,6 +181,10 @@ namespace llarp
void void
QueueDiskIO(std::function<void(void)> func) override; 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; std::optional<SockAddr> _ourAddress;
EventLoop_ptr _loop; EventLoop_ptr _loop;
@ -390,7 +402,7 @@ namespace llarp
/// return true if we are a client with an exit configured /// return true if we are a client with an exit configured
bool bool
HasClientExit() const; HasClientExit() const override;
const byte_t* const byte_t*
pubkey() const override 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}); auto lokidCategory = m_lokiMQ->add_category("lokid", oxenmq::Access{oxenmq::AuthLevel::none});
lokidCategory.add_request_command( lokidCategory.add_request_command(
"get_peer_stats", [this](oxenmq::Message& m) { HandleGetPeerStats(m); }); "get_peer_stats", [this](oxenmq::Message& m) { HandleGetPeerStats(m); });
m_UpdatingList = false;
} }
void void
@ -82,8 +83,20 @@ namespace llarp
" parts instead of 2 parts so we will not update the list of service nodes"); " parts instead of 2 parts so we will not update the list of service nodes");
return; // bail return; // bail
} }
LogDebug("new block at hieght ", msg.data[0]); try
UpdateServiceNodeList(std::string{msg.data[1]}); {
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 void
@ -95,9 +108,11 @@ namespace llarp
request["active_only"] = true; request["active_only"] = true;
if (not topblock.empty()) if (not topblock.empty())
request["poll_block_hash"] = topblock; request["poll_block_hash"] = topblock;
m_UpdatingList = true;
Request( Request(
"rpc.get_service_nodes", "rpc.get_service_nodes",
[self = shared_from_this()](bool success, std::vector<std::string> data) { [self = shared_from_this()](bool success, std::vector<std::string> data) {
self->m_UpdatingList = false;
if (not success) if (not success)
{ {
LogWarn("failed to update service node list"); LogWarn("failed to update service node list");

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -80,11 +80,6 @@ TEST_CASE("Bogon")
REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(192, 168, 1, 111))); 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") SECTION("Bogon_127_8")
{ {
REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(127, 0, 0, 1))); REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(127, 0, 0, 1)));

@ -6,7 +6,7 @@
llarp::RuntimeOptions opts = {false, false, false}; 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> static std::shared_ptr<llarp::Context>
make_context(std::optional<fs::path> keyfile) make_context(std::optional<fs::path> keyfile)
{ {

Loading…
Cancel
Save