Merge remote-tracking branch 'origin/stable' into debian/bullseye

This commit is contained in:
Jason Rhinelander 2021-06-10 16:36:32 -03:00
commit 7d470a1eab
86 changed files with 1899 additions and 693 deletions

3
.gitmodules vendored
View File

@ -35,6 +35,3 @@
[submodule "external/ngtcp2"] [submodule "external/ngtcp2"]
path = external/ngtcp2 path = external/ngtcp2
url = https://github.com/ngtcp2/ngtcp2.git url = https://github.com/ngtcp2/ngtcp2.git
[submodule "external/cpr"]
path = external/cpr
url = https://github.com/whoshuu/cpr

View File

@ -16,7 +16,7 @@ if(CCACHE_PROGRAM)
endif() endif()
project(lokinet project(lokinet
VERSION 0.9.2 VERSION 0.9.3
DESCRIPTION "lokinet - IP packet onion router" DESCRIPTION "lokinet - IP packet onion router"
LANGUAGES C CXX) LANGUAGES C CXX)

View File

@ -14,7 +14,7 @@ if(NOT MSVC_VERSION)
# GNU ld sees fit to merge *all* the .ident sections in object files # GNU ld sees fit to merge *all* the .ident sections in object files
# to .r[o]data section one after the other! # to .r[o]data section one after the other!
add_compile_options(-fno-ident -Wa,-mbig-obj) add_compile_options(-fno-ident -Wa,-mbig-obj)
link_libraries( -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv ) link_libraries( -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv)
# zmq requires windows xp or higher # zmq requires windows xp or higher
add_definitions(-DWINVER=0x0501 -D_WIN32_WINNT=0x0501) add_definitions(-DWINVER=0x0501 -D_WIN32_WINNT=0x0501)
endif() endif()
@ -29,12 +29,3 @@ if (NOT STATIC_LINK AND NOT MSVC)
message("must ship compiler runtime libraries with this build: libwinpthread-1.dll, libgcc_s_dw2-1.dll, and libstdc++-6.dll") message("must ship compiler runtime libraries with this build: libwinpthread-1.dll, libgcc_s_dw2-1.dll, and libstdc++-6.dll")
message("for release builds, turn on STATIC_LINK in cmake options") message("for release builds, turn on STATIC_LINK in cmake options")
endif() endif()
if (STATIC_LINK)
set(LIBUV_USE_STATIC ON)
if (WOW64_CROSS_COMPILE)
link_libraries( -static-libstdc++ -static-libgcc -static -Wl,--image-base=0x10000,--large-address-aware,--dynamicbase,--pic-executable,-e,_mainCRTStartup,--subsystem,console:5.00 )
else()
link_libraries( -static-libstdc++ -static-libgcc -static -Wl,--image-base=0x10000,--dynamicbase,--pic-executable,-e,mainCRTStartup )
endif()
endif()

View File

@ -1,6 +1,6 @@
if(NOT GUI_ZIP_URL) if(NOT GUI_ZIP_URL)
set(GUI_ZIP_URL "https://oxen.rocks/oxen-io/loki-network-control-panel/lokinet-gui-windows-32bit-v0.3.7.zip") set(GUI_ZIP_URL "https://oxen.rocks/oxen-io/loki-network-control-panel/lokinet-gui-windows-32bit-v0.3.8.zip")
set(GUI_ZIP_HASH_OPTS EXPECTED_HASH SHA256=faafb5c7c8b9831f572ed78bb2cf8454bfa0d5f79897ce31e64e4a4331d55045) set(GUI_ZIP_HASH_OPTS EXPECTED_HASH SHA256=60c2b738cf997e5684f307e5222498fd09143d495a932924105a49bf59ded8bb)
endif() endif()
set(TUNTAP_URL "https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe") set(TUNTAP_URL "https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe")
@ -32,7 +32,7 @@ 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 "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_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\\n")
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'"

View File

@ -4,18 +4,67 @@ import curses
import json import json
import sys import sys
import time import time
import platform
import os
from argparse import ArgumentParser as AP
import zmq is_windows = lambda : platform.system().lower() == 'windows'
is_linux = lambda : platform.system().lower() == 'linux'
try:
import zmq
except ImportError:
print("zmq module not found")
print()
if is_linux():
print("for debian-based linux do:")
print("\tsudo apt install python3-zmq")
print("for other linuxs do:")
print("\tpip3 install --user zmq")
else:
print("install it with:")
print("\tpip3 install --user zmq")
sys.quit()
geo = None geo = None
try: try:
import GeoIP import GeoIP
geo = GeoIP.open("/usr/share/GeoIP/GeoIP.dat", GeoIP.GEOIP_STANDARD) except ImportError:
except Exception as ex: print("geoip module not found")
print('no geoip: {}'.format(ex)) print()
if is_linux():
print("for debian-based linux do:")
print("\tsudo apt install python3-geoip")
print("for other linuxs do:")
print("\tpip3 install --user geoip")
print("for other linuxs you are responsible for obtaining your owen geoip databases, glhf")
else:
print("install it with:")
print("\tpip3 install --user geoip")
print("")
print("press enter to continue without geoip")
sys.stdin.read(1)
else:
try:
geoip_env_var = 'GEOIP_DB_FILE'
if is_windows():
geoip_default_db = '.\\GeoIP.dat'
else:
geoip_default_db = "/usr/share/GeoIP/GeoIP.dat"
geoip_db_file = geoip_env_var in os.environ and os.environ[geoip_env_var] or geoip_default_db
if not os.path.exists(geoip_db_file):
print("no geoip database found at {}".format(geoip_db_file))
print("you can override the path to it using the {} environmental variable".format(geoip_env_var))
sys.quit()
geo = GeoIP.open(geoip_db_file, GeoIP.GEOIP_STANDARD)
except Exception as ex:
print('failed to load geoip database: {}'.format(ex))
time.sleep(1) time.sleep(1)
now = lambda : int(time.time()) * 1000
def ip_to_flag(ip): def ip_to_flag(ip):
""" """
@ -38,7 +87,7 @@ class Monitor:
_sample_size = 12 _sample_size = 12
def __init__(self, url): def __init__(self, url, introsetMode=False):
self.txrate = 0 self.txrate = 0
self.rxrate = 0 self.rxrate = 0
self.data = dict() self.data = dict()
@ -52,6 +101,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
self._introsetMode = introsetMode
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()])
@ -104,8 +154,14 @@ class Monitor:
@staticmethod @staticmethod
def time_to(timestamp): def time_to(timestamp):
""" return time until timestamp in seconds formatted""" """ return time until timestamp in seconds formatted"""
now = time.time() * 1000 if timestamp:
return "{} seconds".format(int((timestamp - now) / 1000)) val = int((timestamp - now()) / 1000)
if val < 0:
return "{} seconds ago".format(0-val)
else:
return "{} seconds".format(val)
else:
return 'never'
@staticmethod @staticmethod
def speed_of(rate): def speed_of(rate):
@ -343,6 +399,102 @@ class Monitor:
self.win.addstr("search for {}".format(transaction["tx"]["target"])) self.win.addstr("search for {}".format(transaction["tx"]["target"]))
return y_pos return y_pos
def display_introsets(self, y_pos, service):
"""
display introsets on a service
"""
y_pos += 1
self.win.move(y_pos, 1)
self.win.addstr("localhost.loki")
y_pos = self._display_our_introset(y_pos, service)
y_pos += 1
remotes = service['remoteSessions'] or []
for session in remotes:
y_pos = self._display_session_introset(y_pos, session)
def _display_intro(self, y_pos, intro, label, paths):
y_pos += 1
self.win.move(y_pos, 1)
path = 'path' in intro and intro['path'][:4] or '????'
self.win.addstr('{}: ({}|{}) [expires in: {}] [{} paths]'.format(label, intro['router'][:8], path, self.time_to(intro['expiresAt']), self.count_endpoints_in_path(paths, intro['router'])))
return y_pos
@staticmethod
def count_endpoints_in_path(paths, endpoint):
num = 0
for path in paths:
if path['hops'][-1]['router'] == endpoint and path['ready']:
num += 1
return num
@staticmethod
def count_ready_paths(paths):
num = 0
for path in paths:
if path['ready']:
num += 1
return num
@staticmethod
def make_bar(timestamp, scale=1, char='#'):
if timestamp > 0:
return int((abs(timestamp - now()) / 1000) / scale) * char
return ''
def _display_our_introset(self, y_pos, context):
for intro in context['introset']['intros'] or []:
y_pos = self._display_intro(y_pos, intro, "introset intro", context['paths'])
for path in context['paths']:
y_pos = self._display_intro(y_pos, path['intro'], "inbound path intro", context['paths'])
return y_pos
def _display_session_introset(self, y_pos, context):
#print(context.keys())
y_pos += 1
self.win.move(y_pos, 1)
readyState = context['readyToSend'] and '✔️' or '❌'
self.win.addstr('{} ({}) [{}]'.format(context['remoteIdentity'], context['currentConvoTag'], readyState))
y_pos += 1
self.win.move(y_pos, 1)
self.win.addstr('last good send: {}'.format(self.time_to(context['lastGoodSend'])))
y_pos += 1
self.win.move(y_pos, 1)
self.win.addstr(self.make_bar(context['lastGoodSend']))
y_pos += 1
self.win.move(y_pos, 1)
self.win.addstr('last recv: {}'.format(self.time_to(context['lastRecv'])))
y_pos += 1
self.win.move(y_pos, 1)
self.win.addstr(self.make_bar(context['lastRecv']))
y_pos += 1
self.win.move(y_pos, 1)
self.win.addstr('last introset update: {}'.format(self.time_to(context['lastIntrosetUpdate'])))
y_pos += 1
self.win.move(y_pos, 1)
self.win.addstr(self.make_bar(context['lastIntrosetUpdate'], 30))
y_pos += 2
self.win.move(y_pos, 1)
self.win.addstr('last shift: {}'.format(self.time_to(context['lastShift'])))
paths = context['paths'] or []
y_pos = self._display_intro(y_pos + 1, context['nextIntro'], 'next intro', paths) + 1
y_pos = self._display_intro(y_pos, context['remoteIntro'], 'current intro', paths) + 1
for intro in context['currentRemoteIntroset']['intros'] or []:
y_pos = self._display_intro(y_pos, intro, "introset intro", paths)
y_pos += 1
for intro in context['badIntros'] or []:
y_pos = self._display_intro(y_pos, intro, "bad intro", paths)
y_pos += 1
return y_pos
def display_data(self): def display_data(self):
""" draw main window """ """ draw main window """
if self.data is not None: if self.data is not None:
@ -351,12 +503,16 @@ class Monitor:
services = self.data["services"] or {} services = self.data["services"] or {}
y_pos = 3 y_pos = 3
try: try:
if not self._introsetMode:
y_pos = self.display_links(y_pos, self.data["links"]) y_pos = self.display_links(y_pos, self.data["links"])
for key in services: for key in services:
y_pos = self.display_service(y_pos, key, services[key]) y_pos = self.display_service(y_pos, key, services[key])
y_pos = self.display_dht(y_pos, self.data["dht"]) y_pos = self.display_dht(y_pos, self.data["dht"])
except: else:
pass for key in services:
y_pos = self.display_introsets(y_pos, services[key])
except Exception as ex:
print('{}'.format(ex))
else: else:
self.win.move(1, 1) self.win.move(1, 1)
self.win.addstr("lokinet offline") self.win.addstr("lokinet offline")
@ -387,8 +543,17 @@ class Monitor:
def main(): def main():
""" main function """ """ main function """
ap = AP()
ap.add_argument("--introset", action='store_const', const=True, default=False, help="run in introset inspection mode")
ap.add_argument("--url", default='tcp://127.0.0.1:1190', type=str, help='url to lokinet rpc')
args = ap.parse_args()
mon = Monitor( mon = Monitor(
len(sys.argv) > 1 and sys.argv[1] or "tcp://127.0.0.1:1190" args.url,
args.introset
) )
mon.run() mon.run()

View File

@ -31,7 +31,7 @@ def main():
ap.add_argument('--ip', type=str, default=None) ap.add_argument('--ip', type=str, default=None)
ap.add_argument('--ifname', type=str, default='lo') ap.add_argument('--ifname', type=str, default='lo')
ap.add_argument('--netid', type=str, default=None) ap.add_argument('--netid', type=str, default=None)
ap.add_argument('--loglevel', type=str, default='info') ap.add_argument('--loglevel', type=str, default='debug')
args = ap.parse_args() args = ap.parse_args()
if args.valgrind: if args.valgrind:
@ -59,11 +59,15 @@ def main():
config['bind'] = { config['bind'] = {
args.ifname: str(args.baseport + nodeid) args.ifname: str(args.baseport + nodeid)
} }
config["logging"] = {
"level": args.loglevel
}
config['netdb'] = { config['netdb'] = {
'dir': 'netdb' 'dir': 'netdb'
} }
config['network'] = { config['network'] = {
'type' : 'null' 'type' : 'null',
'save-profiles': 'false'
} }
config['api'] = { config['api'] = {
'enabled': 'false' 'enabled': 'false'
@ -71,6 +75,9 @@ def main():
config['lokid'] = { config['lokid'] = {
'enabled': 'false', 'enabled': 'false',
} }
config["logging"] = {
"level": args.loglevel
}
d = os.path.join(args.dir, svcNodeName(nodeid)) d = os.path.join(args.dir, svcNodeName(nodeid))
if not os.path.exists(d): if not os.path.exists(d):
os.mkdir(d) os.mkdir(d)

View File

@ -33,6 +33,7 @@ endif()
foreach(exe lokinet lokinet-vpn lokinet-bootstrap) foreach(exe lokinet lokinet-vpn lokinet-bootstrap)
if(WIN32 AND NOT MSVC_VERSION) if(WIN32 AND NOT MSVC_VERSION)
target_sources(${exe} PRIVATE ../llarp/win32/version.rc) target_sources(${exe} PRIVATE ../llarp/win32/version.rc)
target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00)
target_link_libraries(${exe} PRIVATE ws2_32 iphlpapi) target_link_libraries(${exe} PRIVATE ws2_32 iphlpapi)
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
target_link_directories(${exe} PRIVATE /usr/local/lib) target_link_directories(${exe} PRIVATE /usr/local/lib)

View File

@ -25,7 +25,6 @@ if(SUBMODULE_CHECK)
check_submodule(uvw) check_submodule(uvw)
check_submodule(cpr) check_submodule(cpr)
check_submodule(ngtcp2) check_submodule(ngtcp2)
check_submodule(cpr)
endif() endif()
endif() endif()

View File

@ -8,7 +8,7 @@ extern "C"
/// get a free()-able null terminated string that holds our .loki address /// get a free()-able null terminated string that holds our .loki address
/// returns NULL if we dont have one right now /// returns NULL if we dont have one right now
char* char* EXPORT
lokinet_address(struct lokinet_context*); lokinet_address(struct lokinet_context*);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "lokinet_export.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
@ -12,40 +14,40 @@ extern "C"
struct lokinet_context; struct lokinet_context;
/// allocate a new lokinet context /// allocate a new lokinet context
struct lokinet_context* struct lokinet_context* EXPORT
lokinet_context_new(); lokinet_context_new();
/// free a context allocated by lokinet_context_new /// free a context allocated by lokinet_context_new
void void EXPORT
lokinet_context_free(struct lokinet_context*); lokinet_context_free(struct lokinet_context*);
/// spawn all the threads needed for operation and start running /// spawn all the threads needed for operation and start running
/// return 0 on success /// return 0 on success
/// return non zero on fail /// return non zero on fail
int int EXPORT
lokinet_context_start(struct lokinet_context*); lokinet_context_start(struct lokinet_context*);
/// return 0 if we our endpoint has published on the network and is ready to send /// return 0 if we our endpoint has published on the network and is ready to send
/// return -1 if we don't have enough paths ready /// return -1 if we don't have enough paths ready
/// retrun -2 if we look deadlocked /// retrun -2 if we look deadlocked
/// retrun -3 if context was null or not started yet /// retrun -3 if context was null or not started yet
int int EXPORT
lokinet_status(struct lokinet_context*); lokinet_status(struct lokinet_context*);
/// wait at most N milliseconds for lokinet to build paths and get ready /// wait at most N milliseconds for lokinet to build paths and get ready
/// return 0 if we are ready /// return 0 if we are ready
/// return nonzero if we are not ready /// return nonzero if we are not ready
int int EXPORT
lokinet_wait_for_ready(int N, struct lokinet_context*); lokinet_wait_for_ready(int N, struct lokinet_context*);
/// stop all operations on this lokinet context /// stop all operations on this lokinet context
void void EXPORT
lokinet_context_stop(struct lokinet_context*); lokinet_context_stop(struct lokinet_context*);
/// load a bootstrap RC from memory /// load a bootstrap RC from memory
/// return 0 on success /// return 0 on success
/// return non zero on fail /// return non zero on fail
int int EXPORT
lokinet_add_bootstrap_rc(const char*, size_t, struct lokinet_context*); lokinet_add_bootstrap_rc(const char*, size_t, struct lokinet_context*);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -0,0 +1,7 @@
#pragma once
#ifdef _WIN32
#define EXPORT __cdecl
#else
#define EXPORT
#endif

View File

@ -1,24 +1,24 @@
#pragma once #pragma once
#include "lokinet_export.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif #endif
/// change our network id globally across all contexts /// change our network id globally across all contexts
void void EXPORT
lokinet_set_netid(const char*); lokinet_set_netid(const char*);
/// get our current netid /// get our current netid
/// must be free()'d after use /// must be free()'d after use
const char* const char* EXPORT
lokinet_get_netid(); lokinet_get_netid();
/// set log level /// set log level
/// possible values: trace, debug, info, warn, error, none /// possible values: trace, debug, info, warn, error, none
/// return 0 on success /// return 0 on success
/// return non zero on fail /// return non zero on fail
int int EXPORT
lokinet_log_level(const char*); lokinet_log_level(const char*);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -11,11 +11,11 @@ extern "C"
/// poll many sockets for activity /// poll many sockets for activity
/// each pollfd.fd should be set to the socket id /// each pollfd.fd should be set to the socket id
/// returns 0 on sucess /// returns 0 on sucess
int int EXPORT
lokinet_poll(struct pollfd* poll, nfds_t numsockets, struct lokinet_context* ctx); lokinet_poll(struct pollfd* poll, nfds_t numsockets, struct lokinet_context* ctx);
/// close a udp socket or a stream socket by its id /// close a udp socket or a stream socket by its id
void void EXPORT
lokinet_close_socket(int id, struct lokinet_context* ctx); lokinet_close_socket(int id, struct lokinet_context* ctx);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -35,7 +35,7 @@ extern "C"
/// do a srv lookup on host for service /// do a srv lookup on host for service
/// caller MUST call lokinet_srv_lookup_done when they are done handling the result /// caller MUST call lokinet_srv_lookup_done when they are done handling the result
int int EXPORT
lokinet_srv_lookup( lokinet_srv_lookup(
char* host, char* host,
char* service, char* service,
@ -51,12 +51,12 @@ extern "C"
/// iterate over each srv record in a lookup result /// iterate over each srv record in a lookup result
/// user is passes into hook and called for each result and then with NULL as the result on the /// user is passes into hook and called for each result and then with NULL as the result on the
/// end of iteration /// end of iteration
void void EXPORT
lokinet_for_each_srv_record( lokinet_for_each_srv_record(
struct lokinet_srv_lookup_result* result, lokinet_srv_record_iterator iter, void* user); struct lokinet_srv_lookup_result* result, lokinet_srv_record_iterator iter, void* user);
/// free internal members of a srv lookup result after use of the result /// free internal members of a srv lookup result after use of the result
void void EXPORT
lokinet_srv_lookup_done(struct lokinet_srv_lookup_result* result); lokinet_srv_lookup_done(struct lokinet_srv_lookup_result* result);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -28,7 +28,7 @@ extern "C"
/// connect out to a remote endpoint /// connect out to a remote endpoint
/// remoteAddr is in the form of "name:port" /// remoteAddr is in the form of "name:port"
/// localAddr is either NULL for any or in the form of "ip:port" to bind to an explicit address /// localAddr is either NULL for any or in the form of "ip:port" to bind to an explicit address
void void EXPORT
lokinet_outbound_stream( lokinet_outbound_stream(
struct lokinet_stream_result* result, struct lokinet_stream_result* result,
const char* remoteAddr, const char* remoteAddr,
@ -44,13 +44,13 @@ extern "C"
/// set stream accepter filter /// set stream accepter filter
/// passes user parameter into stream filter as void * /// passes user parameter into stream filter as void *
/// returns stream id /// returns stream id
int int EXPORT
lokinet_inbound_stream_filter( lokinet_inbound_stream_filter(
lokinet_stream_filter acceptFilter, void* user, struct lokinet_context* context); lokinet_stream_filter acceptFilter, void* user, struct lokinet_context* context);
/// simple stream acceptor /// simple stream acceptor
/// simple variant of lokinet_inbound_stream_filter that maps port to localhost:port /// simple variant of lokinet_inbound_stream_filter that maps port to localhost:port
int int EXPORT
lokinet_inbound_stream(uint16_t port, struct lokinet_context* context); lokinet_inbound_stream(uint16_t port, struct lokinet_context* context);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -42,7 +42,7 @@ extern "C"
/// localAddr is the local ip:port to bind our socket to, if localAddr is NULL then /// localAddr is the local ip:port to bind our socket to, if localAddr is NULL then
/// lokinet_udp_sendmmsg MUST be used to send packets return 0 on success return nonzero on fail, /// lokinet_udp_sendmmsg MUST be used to send packets return 0 on success return nonzero on fail,
/// containing an errno value /// containing an errno value
int int EXPORT
lokinet_udp_establish( lokinet_udp_establish(
char* remoteHost, char* remoteHost,
char* remotePort, char* remotePort,
@ -66,7 +66,7 @@ extern "C"
/// ///
/// returns 0 on success /// returns 0 on success
/// returns nonzero on error in which it is an errno value /// returns nonzero on error in which it is an errno value
int int EXPORT
lokinet_udp_bind( lokinet_udp_bind(
int exposedPort, int exposedPort,
char* srv, char* srv,
@ -78,7 +78,7 @@ extern "C"
/// returns 0 on sucess /// returns 0 on sucess
/// ///
/// returns non zero errno on error /// returns non zero errno on error
int int EXPORT
lokinet_udp_poll( lokinet_udp_poll(
const int* socket_ids, const int* socket_ids,
size_t numsockets, size_t numsockets,
@ -93,7 +93,7 @@ extern "C"
}; };
/// analog to recvmmsg /// analog to recvmmsg
ssize_t ssize_t EXPORT
lokinet_udp_recvmmsg( lokinet_udp_recvmmsg(
int socket_id, int socket_id,
struct lokinet_udp_pkt* events, struct lokinet_udp_pkt* events,

View File

@ -103,6 +103,7 @@ add_library(liblokinet
dns/unbound_resolver.cpp dns/unbound_resolver.cpp
consensus/table.cpp consensus/table.cpp
consensus/reachability_testing.cpp
bootstrap.cpp bootstrap.cpp
context.cpp context.cpp

View File

@ -769,7 +769,7 @@ namespace llarp
} }
else else
{ {
info.interface = name; info.interface = std::string{name};
std::vector<std::string_view> splits = split(value, ','); std::vector<std::string_view> splits = split(value, ',');
for (std::string_view str : splits) for (std::string_view str : splits)

View File

@ -0,0 +1,156 @@
#include "reachability_testing.hpp"
#include <chrono>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/util/logging/logger.hpp>
#include <llarp/crypto/crypto.hpp>
using std::chrono::steady_clock;
namespace llarp::consensus
{
using fseconds = std::chrono::duration<float, std::chrono::seconds::period>;
using fminutes = std::chrono::duration<float, std::chrono::minutes::period>;
static void
check_incoming_tests_impl(
std::string_view name,
const time_point_t& now,
const time_point_t& startup,
detail::incoming_test_state& incoming)
{
const auto elapsed = now - std::max(startup, incoming.last_test);
bool failing = elapsed > reachability_testing::MAX_TIME_WITHOUT_PING;
bool whine = failing != incoming.was_failing
|| (failing && now - incoming.last_whine > reachability_testing::WHINING_INTERVAL);
incoming.was_failing = failing;
if (whine)
{
incoming.last_whine = now;
if (!failing)
{
LogInfo(name, " ping received; port is likely reachable again");
}
else
{
if (incoming.last_test.time_since_epoch() == 0s)
{
LogWarn("Have NEVER received ", name, " pings!");
}
else
{
LogWarn(
"Have not received ",
name,
" pings for a long time: ",
fminutes{elapsed}.count(),
" minutes");
}
LogWarn(
"Please check your ",
name,
" port. Not being reachable "
"over ",
name,
" may result in a deregistration!");
}
}
}
void
reachability_testing::check_incoming_tests(const time_point_t& now)
{
check_incoming_tests_impl("lokinet", now, startup, last);
}
void
reachability_testing::incoming_ping(const time_point_t& now)
{
last.last_test = now;
}
std::optional<RouterID>
reachability_testing::next_random(AbstractRouter* router, const time_point_t& now, bool requeue)
{
if (next_general_test > now)
return std::nullopt;
CSRNG rng;
next_general_test =
now + std::chrono::duration_cast<time_point_t::duration>(fseconds(TESTING_INTERVAL(rng)));
// Pull the next element off the queue, but skip ourself, any that are no longer registered, and
// any that are currently known to be failing (those are queued for testing separately).
RouterID my_pk{router->pubkey()};
while (!testing_queue.empty())
{
auto& pk = testing_queue.back();
std::optional<RouterID> sn;
if (pk != my_pk && !failing.count(pk))
sn = pk;
testing_queue.pop_back();
if (sn)
return sn;
}
if (!requeue)
return std::nullopt;
// FIXME: when a *new* node comes online we need to inject it into a random position in the SN
// list with probability (L/N) [L = current list size, N = potential list size]
//
// (FIXME: put this FIXME in a better place ;-) )
// We exhausted the queue so repopulate it and try again
testing_queue.clear();
const auto all = router->GetRouterWhitelist();
testing_queue.insert(testing_queue.begin(), all.begin(), all.end());
std::shuffle(testing_queue.begin(), testing_queue.end(), rng);
// Recurse with the rebuild list, but don't let it try rebuilding again
return next_random(router, now, false);
}
std::vector<std::pair<RouterID, int>>
reachability_testing::get_failing(const time_point_t& now)
{
// Our failing_queue puts the oldest retest times at the top, so pop them off into our result
// until the top node should be retested sometime in the future
std::vector<std::pair<RouterID, int>> result;
while (result.size() < MAX_RETESTS_PER_TICK && !failing_queue.empty())
{
auto& [pk, retest_time, failures] = failing_queue.top();
if (retest_time > now)
break;
result.emplace_back(pk, failures);
failing_queue.pop();
}
return result;
}
void
reachability_testing::add_failing_node(const RouterID& pk, int previous_failures)
{
using namespace std::chrono;
if (previous_failures < 0)
previous_failures = 0;
CSRNG rng;
auto next_test_in = duration_cast<time_point_t::duration>(
previous_failures * TESTING_BACKOFF + fseconds{TESTING_INTERVAL(rng)});
if (next_test_in > TESTING_BACKOFF_MAX)
next_test_in = TESTING_BACKOFF_MAX;
failing.insert(pk);
failing_queue.emplace(pk, steady_clock::now() + next_test_in, previous_failures + 1);
}
void
reachability_testing::remove_node_from_failing(const RouterID& pk)
{
failing.erase(pk);
}
} // namespace llarp::consensus

View File

@ -0,0 +1,148 @@
#pragma once
#include <chrono>
#include <queue>
#include <random>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <llarp/util/time.hpp>
#include <llarp/router_id.hpp>
namespace llarp
{
struct AbstractRouter;
}
namespace llarp::consensus
{
namespace detail
{
using clock_t = std::chrono::steady_clock;
using time_point_t = std::chrono::time_point<clock_t>;
// Returns std::greater on the std::get<N>(v)th element value.
template <typename T, size_t N>
struct nth_greater
{
constexpr bool
operator()(const T& lhs, const T& rhs) const
{
return std::greater<std::tuple_element_t<N, T>>{}(std::get<N>(lhs), std::get<N>(rhs));
}
};
struct incoming_test_state
{
time_point_t last_test{};
time_point_t last_whine{};
bool was_failing = false;
};
} // namespace detail
using time_point_t = detail::time_point_t;
using clock_t = detail::clock_t;
// How often we tick the timer to check whether we need to do any tests.
constexpr auto REACHABILITY_TESTING_TIMER_INTERVAL = 50ms;
class reachability_testing
{
public:
// Distribution for the seconds between node tests: we throw in some randomness to avoid
// potential clustering of tests. (Note that there is some granularity here as the test timer
// only runs every REACHABILITY_TESTING_TIMER_INTERVAL).
std::normal_distribution<float> TESTING_INTERVAL{10.0, 3.0};
// The linear backoff after each consecutive test failure before we re-test. Specifically we
// schedule the next re-test for (TESTING_BACKOFF*previous_failures) + TESTING_INTERVAL(rng).
inline static constexpr auto TESTING_BACKOFF = 10s;
// The upper bound for the re-test interval.
inline static constexpr auto TESTING_BACKOFF_MAX = 2min;
// The maximum number of nodes that we will re-test at once (i.e. per TESTING_TIMING_INTERVAL);
// mainly intended to throttle ourselves if, for instance, our own connectivity loss makes us
// accumulate tons of nodes to test all at once. (Despite the random intervals, this can happen
// if we also get decommissioned during which we can't test at all but still have lots of
// failing nodes we want to test right away when we get recommissioned).
inline static constexpr int MAX_RETESTS_PER_TICK = 4;
// Maximum time without a ping before we start whining about it.
//
// We have a probability of about 0.368* of *not* getting pinged within a ping interval (10s),
// and so the probability of not getting a ping for 2 minutes (i.e. 12 test spans) just because
// we haven't been selected is extremely small (0.0000061). It also coincides nicely with
// blockchain time (i.e. two minutes) and our max testing backoff.
//
// * = approx value of ((n-1)/n)^n for non-tiny values of n
inline static constexpr auto MAX_TIME_WITHOUT_PING = 2min;
// How often we whine in the logs about being unreachable
inline static constexpr auto WHINING_INTERVAL = 2min;
private:
// Queue of pubkeys of service nodes to test; we pop off the back of this until the queue
// empties then we refill it with a shuffled list of all pubkeys then pull off of it until it is
// empty again, etc.
std::vector<RouterID> testing_queue;
// The next time for a general test
time_point_t next_general_test = time_point_t::min();
// When we started, so that we know not to hold off on whining about no pings for a while.
const time_point_t startup = clock_t::now();
// Pubkeys, next test times, and sequential failure counts of service nodes that are currently
// in "failed" status along with the last time they failed; we retest them first after 10s then
// back off linearly by an additional 10s up to a max testing interval of 2m30s, until we get a
// successful response.
using FailingPK = std::tuple<RouterID, time_point_t, int>;
std::priority_queue<FailingPK, std::vector<FailingPK>, detail::nth_greater<FailingPK, 1>>
failing_queue;
std::unordered_set<RouterID> failing;
// Track the last time *this node* was tested by other network nodes; used to detect and warn
// about possible network issues.
detail::incoming_test_state last;
public:
// If it is time to perform another random test, this returns the next node to test from the
// testing queue and returns it, also updating the timer for the next test. If it is not yet
// time, or if the queue is empty and cannot current be replenished, returns std::nullopt. If
// the queue empties then this builds a new one by shuffling current public keys in the swarm's
// "all nodes" then starts using the new queue for this an subsequent calls.
//
// `requeue` is mainly for internal use: if false it avoids rebuilding the queue if we run
// out (and instead just return nullopt).
std::optional<RouterID>
next_random(
AbstractRouter* router, const time_point_t& now = clock_t::now(), bool requeue = true);
// Removes and returns up to MAX_RETESTS_PER_TICK nodes that are due to be tested (i.e.
// next-testing-time <= now). Returns [snrecord, #previous-failures] for each.
std::vector<std::pair<RouterID, int>>
get_failing(const time_point_t& now = clock_t::now());
// Adds a bad node pubkey to the failing list, to be re-tested soon (with a backoff depending on
// `failures`; see TESTING_BACKOFF). `previous_failures` should be the number of previous
// failures *before* this one, i.e. 0 for a random general test; or the failure count returned
// by `get_failing` for repeated failures.
void
add_failing_node(const RouterID& pk, int previous_failures = 0);
/// removes the public key from the failing set
void
remove_node_from_failing(const RouterID& pk);
// Called when this router receives an incomming session
void
incoming_ping(const time_point_t& now = clock_t::now());
// Check whether we received incoming pings recently
void
check_incoming_tests(const time_point_t& now = clock_t::now());
};
} // namespace llarp::consensus

View File

@ -5,6 +5,6 @@
#include <cstdlib> #include <cstdlib>
constexpr size_t MAX_LINK_MSG_SIZE = 8192; constexpr size_t MAX_LINK_MSG_SIZE = 8192;
static constexpr auto DefaultLinkSessionLifetime = 1min; static constexpr auto DefaultLinkSessionLifetime = 5min;
constexpr size_t MaxSendQueueSize = 1024; constexpr size_t MaxSendQueueSize = 1024 * 16;
static constexpr auto LinkLayerConnectTimeout = 5s; static constexpr auto LinkLayerConnectTimeout = 5s;

View File

@ -20,14 +20,16 @@ namespace llarp
constexpr std::chrono::milliseconds default_lifetime = 20min; constexpr std::chrono::milliseconds default_lifetime = 20min;
/// minimum into lifetime we will advertise /// minimum into lifetime we will advertise
constexpr std::chrono::milliseconds min_intro_lifetime = default_lifetime / 2; constexpr std::chrono::milliseconds min_intro_lifetime = default_lifetime / 2;
/// number of slices of path lifetime to spread intros out via
constexpr auto intro_spread_slices = 4;
/// spacing frequency at which we try to build paths for introductions /// spacing frequency at which we try to build paths for introductions
constexpr std::chrono::milliseconds intro_path_spread = default_lifetime / 5; constexpr std::chrono::milliseconds intro_path_spread = default_lifetime / intro_spread_slices;
/// Minimum paths to keep around for intros; mainly used at startup (the /// Minimum paths to keep around for intros; mainly used at startup (the
/// spread, above, should be able to maintain more than this number of paths /// spread, above, should be able to maintain more than this number of paths
/// normally once things are going). /// normally once things are going).
constexpr std::size_t min_intro_paths = 4; constexpr std::size_t min_intro_paths = 4;
/// after this many ms a path build times out /// after this many ms a path build times out
constexpr auto build_timeout = 30s; constexpr auto build_timeout = 10s;
/// measure latency every this interval ms /// measure latency every this interval ms
constexpr auto latency_interval = 20s; constexpr auto latency_interval = 20s;

View File

@ -382,7 +382,7 @@ namespace llarp
replies.emplace_back(new GotRouterMessage(requester, txid, {router->rc()}, false)); replies.emplace_back(new GotRouterMessage(requester, txid, {router->rc()}, false));
return; return;
} }
if (not GetRouter()->ConnectionToRouterAllowed(target.as_array())) if (not GetRouter()->SessionToRouterAllowed(target.as_array()))
{ {
// explicitly not allowed // explicitly not allowed
replies.emplace_back(new GotRouterMessage(requester, txid, {}, false)); replies.emplace_back(new GotRouterMessage(requester, txid, {}, false));

View File

@ -34,7 +34,7 @@ namespace llarp
Key_t peer; Key_t peer;
// check if we know this in our nodedb first // check if we know this in our nodedb first
if (not dht.GetRouter()->ConnectionToRouterAllowed(targetKey)) if (not dht.GetRouter()->SessionToRouterAllowed(targetKey))
{ {
// explicitly disallowed by network // explicitly disallowed by network
replies.emplace_back(new GotRouterMessage(k, txid, {}, false)); replies.emplace_back(new GotRouterMessage(k, txid, {}, false));

View File

@ -130,6 +130,9 @@ namespace llarp
std::string name, std::string name,
std::string service, std::string service,
std::function<void(std::vector<dns::SRVData>)> resultHandler) = 0; std::function<void(std::vector<dns::SRVData>)> resultHandler) = 0;
virtual void
MarkAddressOutbound(AddressVariant_t remote) = 0;
}; };
} // namespace llarp } // namespace llarp

View File

@ -51,6 +51,12 @@ namespace llarp
return shared_from_this(); return shared_from_this();
} }
std::weak_ptr<path::PathSet>
GetWeak() override
{
return weak_from_this();
}
void void
BlacklistSNode(const RouterID snode) override; BlacklistSNode(const RouterID snode) override;

View File

@ -124,7 +124,7 @@ namespace llarp
++itr; ++itr;
} }
if (not m_Router->ConnectionToRouterAllowed(*rid)) if (not m_Router->PathToRouterAllowed(*rid))
return false; return false;
ObtainSNodeSession(*rid, [data = payload.copy(), type](auto session) { ObtainSNodeSession(*rid, [data = payload.copy(), type](auto session) {
@ -150,7 +150,7 @@ namespace llarp
return false; return false;
if (auto* rid = std::get_if<RouterID>(&addr)) if (auto* rid = std::get_if<RouterID>(&addr))
{ {
if (m_SNodeKeys.count(PubKey{*rid}) or m_Router->ConnectionToRouterAllowed(*rid)) if (m_SNodeKeys.count(PubKey{*rid}) or m_Router->PathToRouterAllowed(*rid))
{ {
ObtainSNodeSession( ObtainSNodeSession(
*rid, [hook, routerID = *rid](std::shared_ptr<exit::BaseSession> session) { *rid, [hook, routerID = *rid](std::shared_ptr<exit::BaseSession> session) {
@ -338,7 +338,7 @@ namespace llarp
void void
ExitEndpoint::ObtainSNodeSession(const RouterID& router, exit::SessionReadyFunc obtainCb) ExitEndpoint::ObtainSNodeSession(const RouterID& router, exit::SessionReadyFunc obtainCb)
{ {
if (not m_Router->rcLookupHandler().RemoteIsAllowed(router)) if (not m_Router->rcLookupHandler().SessionIsAllowed(router))
{ {
obtainCb(nullptr); obtainCb(nullptr);
return; return;

View File

@ -41,6 +41,8 @@ namespace llarp
void void
SRVRecordsChanged() override; SRVRecordsChanged() override;
void MarkAddressOutbound(AddressVariant_t) override{};
bool bool
SendToOrQueue( SendToOrQueue(
service::ConvoTag tag, const llarp_buffer_t& payload, service::ProtocolType t) override; service::ConvoTag tag, const llarp_buffer_t& payload, service::ProtocolType t) override;

View File

@ -61,6 +61,12 @@ namespace llarp
return shared_from_this(); return shared_from_this();
} }
std::weak_ptr<path::PathSet>
GetWeak() override
{
return weak_from_this();
}
bool bool
SupportsV6() const override SupportsV6() const override
{ {

View File

@ -67,6 +67,11 @@ namespace llarp
: service::Endpoint(r, parent) : service::Endpoint(r, parent)
, m_UserToNetworkPktQueue("endpoint_sendq", r->loop(), r->loop()) , m_UserToNetworkPktQueue("endpoint_sendq", r->loop(), r->loop())
{ {
m_PacketSendWaker = r->loop()->make_waker([this]() { FlushWrite(); });
m_MessageSendWaker = r->loop()->make_waker([this]() {
FlushSend();
Pump(Now());
});
m_PacketRouter = std::make_unique<vpn::PacketRouter>( m_PacketRouter = std::make_unique<vpn::PacketRouter>(
[this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); }); [this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); });
#ifdef ANDROID #ifdef ANDROID
@ -224,6 +229,12 @@ namespace llarp
{ {
FlushSend(); FlushSend();
Pump(Now()); Pump(Now());
FlushWrite();
}
void
TunEndpoint::FlushWrite()
{
// flush network to user // flush network to user
while (not m_NetworkToUserPktQueue.empty()) while (not m_NetworkToUserPktQueue.empty())
{ {
@ -281,6 +292,7 @@ namespace llarp
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;
MarkAddressOutbound(addr);
return EnsurePathToService( return EnsurePathToService(
addr, addr,
[this, addr, msg, reply, isV6](const Address&, OutboundContext* ctx) { [this, addr, msg, reply, isV6](const Address&, OutboundContext* ctx) {
@ -307,7 +319,7 @@ namespace llarp
service::Address addr, auto msg) -> bool { service::Address addr, auto msg) -> bool {
using service::Address; using service::Address;
using service::OutboundContext; using service::OutboundContext;
MarkAddressOutbound(addr);
return EnsurePathToService( return EnsurePathToService(
addr, addr,
[msg, addr, reply](const Address&, OutboundContext* ctx) { [msg, addr, reply](const Address&, OutboundContext* ctx) {
@ -697,6 +709,7 @@ namespace llarp
m_AddrToIP[addr] = ip; m_AddrToIP[addr] = ip;
m_SNodes[addr] = SNode; m_SNodes[addr] = SNode;
MarkIPActiveForever(ip); MarkIPActiveForever(ip);
MarkAddressOutbound(addr);
return true; return true;
} }
@ -923,12 +936,13 @@ namespace llarp
MarkAddressOutbound(addr); MarkAddressOutbound(addr);
EnsurePathToService( EnsurePathToService(
addr, addr,
[addr, pkt, self = this](service::Address, service::OutboundContext* ctx) { [pkt](service::Address addr, service::OutboundContext* ctx) {
if (ctx) if (ctx)
{ {
ctx->sendTimeout = 5s; ctx->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::Exit);
return;
} }
self->SendToOrQueue(addr, pkt.ConstBuffer(), service::ProtocolType::Exit); LogWarn("cannot ensure path to exit ", addr, " so we drop some packets");
}, },
PathAlignmentTimeout()); PathAlignmentTimeout());
return; return;
@ -957,12 +971,40 @@ namespace llarp
else else
pkt.UpdateIPv6Address({0}, {0}); pkt.UpdateIPv6Address({0}, {0});
} }
if (SendToOrQueue(to, pkt.Buffer(), type)) // try sending it on an existing convotag
// this succeds for inbound convos, probably.
if (SendToOrQueue(to, pkt.ConstBuffer(), type))
{ {
MarkIPActive(dst); MarkIPActive(dst);
return; return;
} }
llarp::LogWarn(Name(), " did not flush packets"); // try establishing a path to this guy
// will fail if it's an inbound convo
EnsurePathTo(
to,
[pkt, type, dst, to, this](auto maybe) {
if (not maybe)
{
var::visit(
[&](auto&& addr) {
LogWarn(Name(), " failed to ensure path to ", addr, " no convo tag found");
},
to);
}
if (SendToOrQueue(*maybe, pkt.ConstBuffer(), type))
{
MarkIPActive(dst);
}
else
{
var::visit(
[&](auto&& addr) {
LogWarn(Name(), " failed to send to ", addr, ", SendToOrQueue failed");
},
to);
}
},
PathAlignmentTimeout());
}); });
} }
@ -1117,6 +1159,8 @@ namespace llarp
pkt.UpdateIPv6Address(src, dst); pkt.UpdateIPv6Address(src, dst);
} }
m_NetworkToUserPktQueue.push(std::move(write)); m_NetworkToUserPktQueue.push(std::move(write));
// wake up packet flushing event so we ensure that all packets are written to user
m_PacketSendWaker->Trigger();
return true; return true;
} }
@ -1163,7 +1207,9 @@ namespace llarp
m_AddrToIP[ident] = nextIP; m_AddrToIP[ident] = nextIP;
m_IPToAddr[nextIP] = ident; m_IPToAddr[nextIP] = ident;
m_SNodes[ident] = snode; m_SNodes[ident] = snode;
llarp::LogInfo(Name(), " mapped ", ident, " to ", nextIP); var::visit(
[&](auto&& remote) { llarp::LogInfo(Name(), " mapped ", remote, " to ", nextIP); },
addr);
MarkIPActive(nextIP); MarkIPActive(nextIP);
return nextIP; return nextIP;
} }
@ -1223,6 +1269,7 @@ namespace llarp
TunEndpoint::HandleGotUserPacket(net::IPPacket pkt) TunEndpoint::HandleGotUserPacket(net::IPPacket pkt)
{ {
m_UserToNetworkPktQueue.Emplace(std::move(pkt)); m_UserToNetworkPktQueue.Emplace(std::move(pkt));
m_MessageSendWaker->Trigger();
} }
TunEndpoint::~TunEndpoint() = default; TunEndpoint::~TunEndpoint() = default;

View File

@ -34,6 +34,12 @@ namespace llarp
return shared_from_this(); return shared_from_this();
} }
std::weak_ptr<path::PathSet>
GetWeak() override
{
return weak_from_this();
}
void void
Thaw() override; Thaw() override;
@ -209,6 +215,10 @@ namespace llarp
virtual void virtual void
FlushSend(); FlushSend();
/// flush writing ip packets to interface
void
FlushWrite();
/// maps ip to key (host byte order) /// maps ip to key (host byte order)
std::unordered_map<huint128_t, AlignedBuffer<32>> m_IPToAddr; std::unordered_map<huint128_t, AlignedBuffer<32>> m_IPToAddr;
/// maps key to ip (host byte order) /// maps key to ip (host byte order)
@ -228,7 +238,7 @@ namespace llarp
std::function<void(dns::Message)> reply, std::function<void(dns::Message)> reply,
bool sendIPv6) bool sendIPv6)
{ {
if (ctx or HasAddress(addr)) if (ctx)
{ {
huint128_t ip = ObtainIPForAddr(addr); huint128_t ip = ObtainIPForAddr(addr);
query->answers.clear(); query->answers.clear();
@ -275,6 +285,11 @@ namespace llarp
std::set<IPRange> m_OwnedRanges; std::set<IPRange> m_OwnedRanges;
/// how long to wait for path alignment /// how long to wait for path alignment
llarp_time_t m_PathAlignmentTimeout; llarp_time_t m_PathAlignmentTimeout;
/// idempotent wakeup for writing packets to user
std::shared_ptr<EventLoopWakeup> m_PacketSendWaker;
/// idempotent wakeup for writing messages to network
std::shared_ptr<EventLoopWakeup> m_MessageSendWaker;
}; };
} // namespace handlers } // namespace handlers

View File

@ -329,7 +329,8 @@ namespace llarp
{ {
if (m_State == State::Ready || m_State == State::LinkIntro) if (m_State == State::Ready || m_State == State::LinkIntro)
{ {
return now > m_LastRX && now - m_LastRX > SessionAliveTimeout; return now > m_LastRX
&& now - m_LastRX > (m_Inbound ? DefaultLinkSessionLifetime : SessionAliveTimeout);
} }
return now - m_CreatedAt >= LinkLayerConnectTimeout; return now - m_CreatedAt >= LinkLayerConnectTimeout;
} }

View File

@ -35,6 +35,12 @@ namespace llarp
virtual bool virtual bool
HasSessionTo(const RouterID& remote) const = 0; HasSessionTo(const RouterID& remote) const = 0;
// it is fine to have both an inbound and outbound session with
// another relay, and is useful for network testing. This test
// is more specific for use with "should we connect outbound?"
virtual bool
HasOutboundSessionTo(const RouterID& remote) const = 0;
/// return true if the session with this pubkey is a client /// return true if the session with this pubkey is a client
/// return false if the session with this pubkey is a router /// return false if the session with this pubkey is a router
/// return std::nullopt we have no session with this pubkey /// return std::nullopt we have no session with this pubkey

View File

@ -60,6 +60,17 @@ namespace llarp
return GetLinkWithSessionTo(remote) != nullptr; return GetLinkWithSessionTo(remote) != nullptr;
} }
bool
LinkManager::HasOutboundSessionTo(const RouterID& remote) const
{
for (const auto& link : outboundLinks)
{
if (link->HasSessionTo(remote))
return true;
}
return false;
}
std::optional<bool> std::optional<bool>
LinkManager::SessionIsClient(RouterID remote) const LinkManager::SessionIsClient(RouterID remote) const
{ {
@ -69,11 +80,8 @@ namespace llarp
if (session) if (session)
return not session->IsRelay(); return not session->IsRelay();
} }
for (const auto& link : outboundLinks) if (HasOutboundSessionTo(remote))
{
if (link->HasSessionTo(remote))
return false; return false;
}
return std::nullopt; return std::nullopt;
} }
@ -177,10 +185,16 @@ namespace llarp
return; return;
util::Lock l(_mutex); util::Lock l(_mutex);
auto& curr = m_PersistingSessions[remote];
if (until > curr) m_PersistingSessions[remote] = std::max(until, m_PersistingSessions[remote]);
curr = until; if (auto maybe = SessionIsClient(remote))
LogDebug("persist session to ", remote, " until ", curr - time_now_ms()); {
if (*maybe)
{
// mark this as a client so we don't try to back connect
m_Clients.Upsert(remote);
}
}
} }
void void
@ -329,43 +343,42 @@ namespace llarp
return; return;
std::vector<RouterID> sessionsNeeded; std::vector<RouterID> sessionsNeeded;
std::vector<RouterID> sessionsClosed;
{ {
util::Lock l(_mutex); util::Lock l(_mutex);
for (auto [remote, until] : m_PersistingSessions)
auto itr = m_PersistingSessions.begin();
while (itr != m_PersistingSessions.end())
{ {
if (now < itr->second) if (now < until)
{ {
auto link = GetLinkWithSessionTo(itr->first); auto link = GetLinkWithSessionTo(remote);
if (link) if (link)
{ {
link->KeepAliveSessionTo(itr->first); link->KeepAliveSessionTo(remote);
} }
else else if (not m_Clients.Contains(remote))
{ {
sessionsNeeded.push_back(itr->first); sessionsNeeded.push_back(remote);
} }
++itr;
} }
else else if (not m_Clients.Contains(remote))
{ {
const RouterID r(itr->first); sessionsClosed.push_back(remote);
LogInfo("commit to ", r, " expired");
itr = m_PersistingSessions.erase(itr);
for (const auto& link : outboundLinks)
{
link->CloseSessionTo(r);
}
} }
} }
} }
for (const auto& router : sessionsNeeded) for (const auto& router : sessionsNeeded)
{ {
LogDebug("ensuring session to ", router, " for previously made commitment");
_sessionMaker->CreateSessionTo(router, nullptr); _sessionMaker->CreateSessionTo(router, nullptr);
} }
for (const auto& router : sessionsClosed)
{
m_PersistingSessions.erase(router);
ForEachOutboundLink([router](auto link) { link->CloseSessionTo(router); });
}
} }
void void

View File

@ -33,6 +33,9 @@ namespace llarp
bool bool
HasSessionTo(const RouterID& remote) const override; HasSessionTo(const RouterID& remote) const override;
bool
HasOutboundSessionTo(const RouterID& remote) const override;
std::optional<bool> std::optional<bool>
SessionIsClient(RouterID remote) const override; SessionIsClient(RouterID remote) const override;
@ -108,6 +111,8 @@ namespace llarp
std::unordered_map<RouterID, SessionStats> m_lastRouterStats; std::unordered_map<RouterID, SessionStats> m_lastRouterStats;
util::DecayingHashSet<RouterID> m_Clients{path::default_lifetime};
IOutboundSessionMaker* _sessionMaker; IOutboundSessionMaker* _sessionMaker;
}; };

View File

@ -191,28 +191,20 @@ struct lokinet_srv_lookup_private
extern "C" extern "C"
{ {
struct lokinet_context* void EXPORT
lokinet_default()
{
if (not g_context)
g_context = std::make_unique<lokinet_context>();
return g_context.get();
}
void
lokinet_set_netid(const char* netid) lokinet_set_netid(const char* netid)
{ {
llarp::NetID::DefaultValue() = llarp::NetID{reinterpret_cast<const byte_t*>(netid)}; llarp::NetID::DefaultValue() = llarp::NetID{reinterpret_cast<const byte_t*>(netid)};
} }
const char* const char* EXPORT
lokinet_get_netid() lokinet_get_netid()
{ {
const auto netid = llarp::NetID::DefaultValue().ToString(); const auto netid = llarp::NetID::DefaultValue().ToString();
return strdup(netid.c_str()); return strdup(netid.c_str());
} }
int int EXPORT
lokinet_log_level(const char* level) lokinet_log_level(const char* level)
{ {
if (auto maybe = llarp::LogLevelFromString(level)) if (auto maybe = llarp::LogLevelFromString(level))
@ -223,7 +215,7 @@ extern "C"
return -1; return -1;
} }
char* char* EXPORT
lokinet_address(struct lokinet_context* ctx) lokinet_address(struct lokinet_context* ctx)
{ {
if (not ctx) if (not ctx)
@ -235,7 +227,7 @@ extern "C"
return strdup(addrStr.c_str()); return strdup(addrStr.c_str());
} }
int int EXPORT
lokinet_add_bootstrap_rc(const char* data, size_t datalen, struct lokinet_context* ctx) lokinet_add_bootstrap_rc(const char* data, size_t datalen, struct lokinet_context* ctx)
{ {
llarp_buffer_t buf{data, datalen}; llarp_buffer_t buf{data, datalen};
@ -253,20 +245,20 @@ extern "C"
return 0; return 0;
} }
struct lokinet_context* struct lokinet_context* EXPORT
lokinet_context_new() lokinet_context_new()
{ {
return new lokinet_context{}; return new lokinet_context{};
} }
void void EXPORT
lokinet_context_free(struct lokinet_context* ctx) lokinet_context_free(struct lokinet_context* ctx)
{ {
lokinet_context_stop(ctx); lokinet_context_stop(ctx);
delete ctx; delete ctx;
} }
int int EXPORT
lokinet_context_start(struct lokinet_context* ctx) lokinet_context_start(struct lokinet_context* ctx)
{ {
if (not ctx) if (not ctx)
@ -301,7 +293,7 @@ extern "C"
return 0; return 0;
} }
int int EXPORT
lokinet_status(struct lokinet_context* ctx) lokinet_status(struct lokinet_context* ctx)
{ {
if (ctx == nullptr) if (ctx == nullptr)
@ -314,7 +306,7 @@ extern "C"
return ctx->endpoint()->IsReady() ? 0 : -1; return ctx->endpoint()->IsReady() ? 0 : -1;
} }
int int EXPORT
lokinet_wait_for_ready(int ms, struct lokinet_context* ctx) lokinet_wait_for_ready(int ms, struct lokinet_context* ctx)
{ {
if (ctx == nullptr) if (ctx == nullptr)
@ -335,7 +327,7 @@ extern "C"
return ep->IsReady() ? 0 : -1; return ep->IsReady() ? 0 : -1;
} }
void void EXPORT
lokinet_context_stop(struct lokinet_context* ctx) lokinet_context_stop(struct lokinet_context* ctx)
{ {
if (not ctx) if (not ctx)
@ -354,7 +346,7 @@ extern "C"
ctx->runner.reset(); ctx->runner.reset();
} }
void void EXPORT
lokinet_outbound_stream( lokinet_outbound_stream(
struct lokinet_stream_result* result, struct lokinet_stream_result* result,
const char* remote, const char* remote,
@ -477,14 +469,14 @@ extern "C"
} }
} }
int int EXPORT
lokinet_inbound_stream(uint16_t port, struct lokinet_context* ctx) lokinet_inbound_stream(uint16_t port, struct lokinet_context* ctx)
{ {
/// FIXME: delete pointer later /// FIXME: delete pointer later
return lokinet_inbound_stream_filter(&accept_port, (void*)new std::uintptr_t{port}, ctx); return lokinet_inbound_stream_filter(&accept_port, (void*)new std::uintptr_t{port}, ctx);
} }
int int EXPORT
lokinet_inbound_stream_filter( lokinet_inbound_stream_filter(
lokinet_stream_filter acceptFilter, void* user, struct lokinet_context* ctx) lokinet_stream_filter acceptFilter, void* user, struct lokinet_context* ctx)
{ {
@ -531,7 +523,7 @@ extern "C"
return id; return id;
} }
void void EXPORT
lokinet_close_stream(int stream_id, struct lokinet_context* ctx) lokinet_close_stream(int stream_id, struct lokinet_context* ctx)
{ {
if (not ctx) if (not ctx)
@ -564,7 +556,7 @@ extern "C"
{} {}
} }
int int EXPORT
lokinet_srv_lookup( lokinet_srv_lookup(
char* host, char* host,
char* service, char* service,
@ -580,7 +572,7 @@ extern "C"
return result->internal->LookupSRV(host, service, ctx); return result->internal->LookupSRV(host, service, ctx);
} }
void void EXPORT
lokinet_for_each_srv_record( lokinet_for_each_srv_record(
struct lokinet_srv_lookup_result* result, lokinet_srv_record_iterator iter, void* user) struct lokinet_srv_lookup_result* result, lokinet_srv_record_iterator iter, void* user)
{ {
@ -594,7 +586,7 @@ extern "C"
} }
} }
void void EXPORT
lokinet_srv_lookup_done(struct lokinet_srv_lookup_result* result) lokinet_srv_lookup_done(struct lokinet_srv_lookup_result* result)
{ {
if (result == nullptr or result->internal == nullptr) if (result == nullptr or result->internal == nullptr)

View File

@ -111,7 +111,7 @@ namespace llarp
{ {
return path->HandleDownstream(llarp_buffer_t(X), Y, r); return path->HandleDownstream(llarp_buffer_t(X), Y, r);
} }
llarp::LogWarn("unhandled downstream message id=", pathid); llarp::LogWarn("no path for downstream message id=", pathid);
return false; return false;
} }
} // namespace llarp } // namespace llarp

View File

@ -204,6 +204,7 @@ namespace llarp
static void static void
OnForwardLRCMResult( OnForwardLRCMResult(
AbstractRouter* router, AbstractRouter* router,
std::shared_ptr<path::TransitHop> path,
const PathID_t pathid, const PathID_t pathid,
const RouterID nextHop, const RouterID nextHop,
const SharedSecret pathKey, const SharedSecret pathKey,
@ -236,9 +237,8 @@ namespace llarp
std::abort(); std::abort();
break; break;
} }
router->QueueWork([router, path, pathid, nextHop, pathKey, status] {
router->QueueWork([router, pathid, nextHop, pathKey, status] { LR_StatusMessage::CreateAndSend(router, path, pathid, nextHop, pathKey, status);
LR_StatusMessage::CreateAndSend(router, pathid, nextHop, pathKey, status);
}); });
} }
@ -251,6 +251,7 @@ namespace llarp
llarp::LogError("duplicate transit hop ", self->hop->info); llarp::LogError("duplicate transit hop ", self->hop->info);
LR_StatusMessage::CreateAndSend( LR_StatusMessage::CreateAndSend(
self->context->Router(), self->context->Router(),
self->hop,
self->hop->info.rxID, self->hop->info.rxID,
self->hop->info.downstream, self->hop->info.downstream,
self->hop->pathKey, self->hop->pathKey,
@ -269,6 +270,7 @@ namespace llarp
llarp::LogError("client path build hit limit ", *self->fromAddr); llarp::LogError("client path build hit limit ", *self->fromAddr);
OnForwardLRCMResult( OnForwardLRCMResult(
self->context->Router(), self->context->Router(),
self->hop,
self->hop->info.rxID, self->hop->info.rxID,
self->hop->info.downstream, self->hop->info.downstream,
self->hop->pathKey, self->hop->pathKey,
@ -279,7 +281,7 @@ namespace llarp
#endif #endif
} }
if (!self->context->Router()->ConnectionToRouterAllowed(self->hop->info.upstream)) if (not self->context->Router()->PathToRouterAllowed(self->hop->info.upstream))
{ {
// we are not allowed to forward it ... now what? // we are not allowed to forward it ... now what?
llarp::LogError( llarp::LogError(
@ -288,6 +290,7 @@ namespace llarp
"not allowed, dropping build request on the floor"); "not allowed, dropping build request on the floor");
OnForwardLRCMResult( OnForwardLRCMResult(
self->context->Router(), self->context->Router(),
self->hop,
self->hop->info.rxID, self->hop->info.rxID,
self->hop->info.downstream, self->hop->info.downstream,
self->hop->pathKey, self->hop->pathKey,
@ -305,15 +308,17 @@ namespace llarp
self->context->PutTransitHop(self->hop); self->context->PutTransitHop(self->hop);
// forward to next hop // forward to next hop
using std::placeholders::_1; using std::placeholders::_1;
auto func = std::bind( auto func = [self](auto status) {
&OnForwardLRCMResult, OnForwardLRCMResult(
self->context->Router(), self->context->Router(),
self->hop,
self->hop->info.rxID, self->hop->info.rxID,
self->hop->info.downstream, self->hop->info.downstream,
self->hop->pathKey, self->hop->pathKey,
_1); status);
self->context->ForwardLRCM(self->hop->info.upstream, self->frames, func);
self->hop = nullptr; self->hop = nullptr;
};
self->context->ForwardLRCM(self->hop->info.upstream, self->frames, func);
} }
// this is called from the logic thread // this is called from the logic thread
@ -338,6 +343,7 @@ namespace llarp
if (!LR_StatusMessage::CreateAndSend( if (!LR_StatusMessage::CreateAndSend(
self->context->Router(), self->context->Router(),
self->hop,
self->hop->info.rxID, self->hop->info.rxID,
self->hop->info.downstream, self->hop->info.downstream,
self->hop->pathKey, self->hop->pathKey,

View File

@ -22,21 +22,21 @@ namespace llarp
std::array<EncryptedFrame, 8> frames; std::array<EncryptedFrame, 8> frames;
uint64_t status = 0; uint64_t status = 0;
HopHandler_ptr path; HopHandler_ptr hop;
AbstractRouter* router; AbstractRouter* router;
PathID_t pathid; PathID_t pathid;
LRSM_AsyncHandler( LRSM_AsyncHandler(
std::array<EncryptedFrame, 8> _frames, std::array<EncryptedFrame, 8> _frames,
uint64_t _status, uint64_t _status,
HopHandler_ptr _path, HopHandler_ptr _hop,
AbstractRouter* _router, AbstractRouter* _router,
const PathID_t& pathid) const PathID_t& pathid)
: frames(std::move(_frames)) : frames{std::move(_frames)}
, status(_status) , status{_status}
, path(std::move(_path)) , hop{std::move(_hop)}
, router(_router) , router{_router}
, pathid(pathid) , pathid{pathid}
{} {}
~LRSM_AsyncHandler() = default; ~LRSM_AsyncHandler() = default;
@ -45,8 +45,7 @@ namespace llarp
handle() handle()
{ {
router->NotifyRouterEvent<tooling::PathStatusReceivedEvent>(router->pubkey(), pathid, status); router->NotifyRouterEvent<tooling::PathStatusReceivedEvent>(router->pubkey(), pathid, status);
hop->HandleLRSM(status, frames, router);
path->HandleLRSM(status, frames, router);
} }
void void
@ -133,16 +132,13 @@ namespace llarp
} }
auto path = router->pathContext().GetByUpstream(session->GetPubKey(), pathid); auto path = router->pathContext().GetByUpstream(session->GetPubKey(), pathid);
if (!path) if (not path)
{ {
llarp::LogWarn("unhandled LR_Status message: no associated path found pathid=", pathid); llarp::LogWarn("unhandled LR_Status message: no associated path found pathid=", pathid);
return false; return false;
} }
auto handler = std::make_shared<LRSM_AsyncHandler>(frames, status, path, router, pathid); auto handler = std::make_shared<LRSM_AsyncHandler>(frames, status, path, router, pathid);
handler->queue_handle(); handler->queue_handle();
return true; return true;
} }
@ -157,6 +153,7 @@ namespace llarp
bool bool
LR_StatusMessage::CreateAndSend( LR_StatusMessage::CreateAndSend(
AbstractRouter* router, AbstractRouter* router,
std::shared_ptr<path::TransitHop> hop,
const PathID_t pathid, const PathID_t pathid,
const RouterID nextHop, const RouterID nextHop,
const SharedSecret pathKey, const SharedSecret pathKey,
@ -169,12 +166,9 @@ namespace llarp
message->SetDummyFrames(); message->SetDummyFrames();
if (!message->AddFrame(pathKey, status)) message->AddFrame(pathKey, status);
{
return false;
}
QueueSendMessage(router, nextHop, message); QueueSendMessage(router, nextHop, message, hop);
return true; // can't guarantee delivery here, as far as we know it's fine return true; // can't guarantee delivery here, as far as we know it's fine
} }
@ -221,10 +215,19 @@ namespace llarp
void void
LR_StatusMessage::QueueSendMessage( LR_StatusMessage::QueueSendMessage(
AbstractRouter* router, const RouterID nextHop, std::shared_ptr<LR_StatusMessage> msg) AbstractRouter* router,
const RouterID nextHop,
std::shared_ptr<LR_StatusMessage> msg,
std::shared_ptr<path::TransitHop> hop)
{ {
router->loop()->call( router->loop()->call([router, nextHop, msg = std::move(msg), hop = std::move(hop)] {
[router, nextHop, msg = std::move(msg)] { SendMessage(router, nextHop, msg); }); SendMessage(router, nextHop, msg);
// destroy hop as needed
if ((msg->status & LR_StatusRecord::SUCCESS) != LR_StatusRecord::SUCCESS)
{
hop->QueueDestroySelf(router);
}
});
} }
void void
@ -273,4 +276,32 @@ namespace llarp
return status == other.status; return status == other.status;
} }
std::string
LRStatusCodeToString(uint64_t status)
{
std::map<uint64_t, std::string> codes = {
{LR_StatusRecord::SUCCESS, "success"},
{LR_StatusRecord::FAIL_TIMEOUT, "timeout"},
{LR_StatusRecord::FAIL_CONGESTION, "congestion"},
{LR_StatusRecord::FAIL_DEST_UNKNOWN, "destination unknown"},
{LR_StatusRecord::FAIL_DECRYPT_ERROR, "decrypt error"},
{LR_StatusRecord::FAIL_MALFORMED_RECORD, "malformed record"},
{LR_StatusRecord::FAIL_DEST_INVALID, "destination invalid"},
{LR_StatusRecord::FAIL_CANNOT_CONNECT, "cannot connect"},
{LR_StatusRecord::FAIL_DUPLICATE_HOP, "duplicate hop"}};
std::stringstream ss;
ss << "[";
bool found = false;
for (const auto& [val, message] : codes)
{
if ((status & val) == val)
{
ss << (found ? ", " : "") << message;
found = true;
}
}
ss << "]";
return ss.str();
}
} // namespace llarp } // namespace llarp

View File

@ -18,6 +18,7 @@ namespace llarp
{ {
struct PathContext; struct PathContext;
struct IHopHandler; struct IHopHandler;
struct TransitHop;
} // namespace path } // namespace path
struct LR_StatusRecord struct LR_StatusRecord
@ -49,6 +50,9 @@ namespace llarp
OnKey(llarp_buffer_t* buffer, llarp_buffer_t* key); OnKey(llarp_buffer_t* buffer, llarp_buffer_t* key);
}; };
std::string
LRStatusCodeToString(uint64_t status);
struct LR_StatusMessage : public ILinkMessage struct LR_StatusMessage : public ILinkMessage
{ {
std::array<EncryptedFrame, 8> frames; std::array<EncryptedFrame, 8> frames;
@ -83,6 +87,7 @@ namespace llarp
static bool static bool
CreateAndSend( CreateAndSend(
AbstractRouter* router, AbstractRouter* router,
std::shared_ptr<path::TransitHop> hop,
const PathID_t pathid, const PathID_t pathid,
const RouterID nextHop, const RouterID nextHop,
const SharedSecret pathKey, const SharedSecret pathKey,
@ -93,7 +98,10 @@ namespace llarp
static void static void
QueueSendMessage( QueueSendMessage(
AbstractRouter* router, const RouterID nextHop, std::shared_ptr<LR_StatusMessage> msg); AbstractRouter* router,
const RouterID nextHop,
std::shared_ptr<LR_StatusMessage> msg,
std::shared_ptr<path::TransitHop> hop);
static void static void
SendMessage( SendMessage(

View File

@ -7,6 +7,7 @@
#include <llarp/messages/relay_status.hpp> #include <llarp/messages/relay_status.hpp>
#include "pathbuilder.hpp" #include "pathbuilder.hpp"
#include "transit_hop.hpp" #include "transit_hop.hpp"
#include <llarp/nodedb.hpp>
#include <llarp/profiling.hpp> #include <llarp/profiling.hpp>
#include <llarp/router/abstractrouter.hpp> #include <llarp/router/abstractrouter.hpp>
#include <llarp/routing/dht_message.hpp> #include <llarp/routing/dht_message.hpp>
@ -25,10 +26,10 @@ namespace llarp
{ {
Path::Path( Path::Path(
const std::vector<RouterContact>& h, const std::vector<RouterContact>& h,
PathSet* parent, std::weak_ptr<PathSet> pathset,
PathRole startingRoles, PathRole startingRoles,
std::string shortName) std::string shortName)
: m_PathSet(parent), _role(startingRoles), m_shortName(std::move(shortName)) : m_PathSet{pathset}, _role{startingRoles}, m_shortName{std::move(shortName)}
{ {
hops.resize(h.size()); hops.resize(h.size());
@ -54,7 +55,7 @@ namespace llarp
// initialize parts of the introduction // initialize parts of the introduction
intro.router = hops[hsz - 1].rc.pubkey; intro.router = hops[hsz - 1].rc.pubkey;
intro.pathID = hops[hsz - 1].txID; intro.pathID = hops[hsz - 1].txID;
if (parent) if (auto parent = m_PathSet.lock())
EnterState(ePathBuilding, parent->Now()); EnterState(ePathBuilding, parent->Now());
} }
@ -184,10 +185,6 @@ namespace llarp
{ {
failedAt = hops[index + 1].rc.pubkey; failedAt = hops[index + 1].rc.pubkey;
} }
else
{
failedAt = hops[index].rc.pubkey;
}
break; break;
} }
++index; ++index;
@ -203,7 +200,14 @@ namespace llarp
if (failedAt) if (failedAt)
{ {
r->NotifyRouterEvent<tooling::PathBuildRejectedEvent>(Endpoint(), RXID(), *failedAt); r->NotifyRouterEvent<tooling::PathBuildRejectedEvent>(Endpoint(), RXID(), *failedAt);
LogWarn(Name(), " build failed at ", *failedAt); LogWarn(
Name(),
" build failed at ",
*failedAt,
" status=",
LRStatusCodeToString(currentStatus),
" hops=",
HopsString());
r->routerProfiling().MarkHopFail(*failedAt); r->routerProfiling().MarkHopFail(*failedAt);
} }
else else
@ -229,6 +233,13 @@ namespace llarp
llarp::LogDebug( llarp::LogDebug(
"Path build failed due to one or more nodes considered an " "Path build failed due to one or more nodes considered an "
"invalid destination"); "invalid destination");
if (failedAt)
{
r->loop()->call([nodedb = r->nodedb(), router = *failedAt]() {
LogInfo("router ", router, " is deregistered so we remove it");
nodedb->Remove(router);
});
}
} }
else if (currentStatus & LR_StatusRecord::FAIL_CANNOT_CONNECT) else if (currentStatus & LR_StatusRecord::FAIL_CANNOT_CONNECT)
{ {
@ -257,7 +268,10 @@ namespace llarp
edge = *failedAt; edge = *failedAt;
r->loop()->call([r, self = shared_from_this(), edge]() { r->loop()->call([r, self = shared_from_this(), edge]() {
self->EnterState(ePathFailed, r->Now()); self->EnterState(ePathFailed, r->Now());
self->m_PathSet->HandlePathBuildFailedAt(self, edge); if (auto parent = self->m_PathSet.lock())
{
parent->HandlePathBuildFailedAt(self, edge);
}
}); });
} }
@ -276,7 +290,10 @@ namespace llarp
if (st == ePathExpired && _status == ePathBuilding) if (st == ePathExpired && _status == ePathBuilding)
{ {
_status = st; _status = st;
m_PathSet->HandlePathBuildTimeout(shared_from_this()); if (auto parent = m_PathSet.lock())
{
parent->HandlePathBuildTimeout(shared_from_this());
}
} }
else if (st == ePathBuilding) else if (st == ePathBuilding)
{ {
@ -291,12 +308,19 @@ namespace llarp
{ {
LogInfo("path ", Name(), " died"); LogInfo("path ", Name(), " died");
_status = st; _status = st;
m_PathSet->HandlePathDied(shared_from_this()); if (auto parent = m_PathSet.lock())
{
parent->HandlePathDied(shared_from_this());
}
} }
else if (st == ePathEstablished && _status == ePathTimeout) else if (st == ePathEstablished && _status == ePathTimeout)
{ {
LogInfo("path ", Name(), " reanimated"); LogInfo("path ", Name(), " reanimated");
} }
else if (st == ePathIgnore)
{
LogInfo("path ", Name(), " ignored");
}
_status = st; _status = st;
} }
@ -370,12 +394,32 @@ namespace llarp
void void
Path::Rebuild() Path::Rebuild()
{
if (auto parent = m_PathSet.lock())
{ {
std::vector<RouterContact> newHops; std::vector<RouterContact> newHops;
for (const auto& hop : hops) for (const auto& hop : hops)
newHops.emplace_back(hop.rc); newHops.emplace_back(hop.rc);
LogInfo(Name(), " rebuilding on ", ShortName()); LogInfo(Name(), " rebuilding on ", ShortName());
m_PathSet->Build(newHops); parent->Build(newHops);
}
}
bool
Path::SendLatencyMessage(AbstractRouter* r)
{
const auto now = r->Now();
// send path latency test
routing::PathLatencyMessage latency{};
latency.T = randint();
latency.S = NextSeqNo();
m_LastLatencyTestID = latency.T;
m_LastLatencyTestTime = now;
LogDebug(Name(), " send latency test id=", latency.T);
if (not SendRoutingMessage(latency, r))
return false;
FlushUpstream(r);
return true;
} }
void void
@ -412,12 +456,12 @@ 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)
{ {
routing::PathLatencyMessage latency; SendLatencyMessage(r);
latency.T = randint(); // latency test FEC
m_LastLatencyTestID = latency.T; r->loop()->call_later(2s, [self = shared_from_this(), r]() {
m_LastLatencyTestTime = now; if (self->m_LastLatencyTestID)
SendRoutingMessage(latency, r); self->SendLatencyMessage(r);
FlushUpstream(r); });
return; return;
} }
dlt = now - m_LastRecvMessage; dlt = now - m_LastRecvMessage;
@ -510,7 +554,7 @@ namespace llarp
{ {
return now >= m_LastRecvMessage + PathReanimationTimeout; return now >= m_LastRecvMessage + PathReanimationTimeout;
} }
if (_status == ePathEstablished) if (_status == ePathEstablished or _status == ePathIgnore)
{ {
return now >= ExpireTime(); return now >= ExpireTime();
} }
@ -654,16 +698,7 @@ namespace llarp
// persist session with upstream router until the path is done // persist session with upstream router until the path is done
r->PersistSessionUntil(Upstream(), intro.expiresAt); r->PersistSessionUntil(Upstream(), intro.expiresAt);
MarkActive(now); MarkActive(now);
// send path latency test return SendLatencyMessage(r);
routing::PathLatencyMessage latency;
latency.T = randint();
latency.S = NextSeqNo();
m_LastLatencyTestID = latency.T;
m_LastLatencyTestTime = now;
if (!SendRoutingMessage(latency, r))
return false;
FlushUpstream(r);
return true;
} }
LogWarn("got unwarranted path confirm message on tx=", RXID(), " rx=", RXID()); LogWarn("got unwarranted path confirm message on tx=", RXID(), " rx=", RXID());
return false; return false;
@ -678,28 +713,48 @@ namespace llarp
bool bool
Path::HandleHiddenServiceFrame(const service::ProtocolFrame& frame) Path::HandleHiddenServiceFrame(const service::ProtocolFrame& frame)
{ {
MarkActive(m_PathSet->Now()); if (auto parent = m_PathSet.lock())
{
MarkActive(parent->Now());
return m_DataHandler && m_DataHandler(shared_from_this(), frame); return m_DataHandler && m_DataHandler(shared_from_this(), frame);
} }
return false;
}
template <typename Samples_t>
static llarp_time_t
computeLatency(const Samples_t& samps)
{
llarp_time_t mean = 0s;
if (samps.empty())
return mean;
for (const auto& samp : samps)
mean += samp;
return mean / samps.size();
}
constexpr auto MaxLatencySamples = 8;
bool bool
Path::HandlePathLatencyMessage(const routing::PathLatencyMessage& msg, AbstractRouter* r) Path::HandlePathLatencyMessage(const routing::PathLatencyMessage&, AbstractRouter* r)
{ {
const auto now = r->Now(); const auto now = r->Now();
MarkActive(now); MarkActive(now);
if (msg.L == m_LastLatencyTestID) if (m_LastLatencyTestID)
{ {
intro.latency = now - m_LastLatencyTestTime; m_LatencySamples.emplace_back(now - m_LastLatencyTestTime);
while (m_LatencySamples.size() > MaxLatencySamples)
m_LatencySamples.pop_front();
intro.latency = computeLatency(m_LatencySamples);
m_LastLatencyTestID = 0; m_LastLatencyTestID = 0;
EnterState(ePathEstablished, now); EnterState(ePathEstablished, now);
if (m_BuiltHook) if (m_BuiltHook)
m_BuiltHook(shared_from_this()); m_BuiltHook(shared_from_this());
m_BuiltHook = nullptr; m_BuiltHook = nullptr;
return true;
} }
return true;
LogWarn("unwarranted path latency message via ", Upstream());
return false;
} }
/// this is the Client's side of handling a DHT message. it's handled /// this is the Client's side of handling a DHT message. it's handled

View File

@ -87,7 +87,7 @@ namespace llarp
HopList hops; HopList hops;
PathSet* const m_PathSet; std::weak_ptr<PathSet> m_PathSet;
service::Introduction intro; service::Introduction intro;
@ -95,7 +95,7 @@ namespace llarp
Path( Path(
const std::vector<RouterContact>& routers, const std::vector<RouterContact>& routers,
PathSet* parent, std::weak_ptr<PathSet> parent,
PathRole startingRoles, PathRole startingRoles,
std::string shortName); std::string shortName);
@ -400,6 +400,9 @@ namespace llarp
HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, AbstractRouter* r) override; HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, AbstractRouter* r) override;
private: private:
bool
SendLatencyMessage(AbstractRouter* r);
/// call obtained exit hooks /// call obtained exit hooks
bool bool
InformExitResult(llarp_time_t b); InformExitResult(llarp_time_t b);
@ -424,7 +427,7 @@ namespace llarp
uint64_t m_RXRate = 0; uint64_t m_RXRate = 0;
uint64_t m_LastTXRate = 0; uint64_t m_LastTXRate = 0;
uint64_t m_TXRate = 0; uint64_t m_TXRate = 0;
std::deque<llarp_time_t> m_LatencySamples;
const std::string m_shortName; const std::string m_shortName;
}; };
} // namespace path } // namespace path

View File

@ -95,8 +95,9 @@ namespace llarp
typename Map_t, typename Map_t,
typename Key_t, typename Key_t,
typename CheckValue_t, typename CheckValue_t,
typename GetFunc_t> typename GetFunc_t,
HopHandler_ptr typename Return_ptr = HopHandler_ptr>
Return_ptr
MapGet(Map_t& map, const Key_t& k, CheckValue_t check, GetFunc_t get) MapGet(Map_t& map, const Key_t& k, CheckValue_t check, GetFunc_t get)
{ {
Lock_t lock(map.first); Lock_t lock(map.first);
@ -172,6 +173,46 @@ namespace llarp
}); });
} }
std::optional<std::weak_ptr<TransitHop>>
PathContext::TransitHopByInfo(const TransitHopInfo& info)
{
// this is ugly as sin
auto own = MapGet<
SyncTransitMap_t::Lock_t,
decltype(m_TransitPaths),
PathID_t,
std::function<bool(const std::shared_ptr<TransitHop>&)>,
std::function<TransitHop*(const std::shared_ptr<TransitHop>&)>,
TransitHop*>(
m_TransitPaths,
info.txID,
[info](const auto& hop) -> bool { return hop->info == info; },
[](const auto& hop) -> TransitHop* { return hop.get(); });
if (own)
return own->weak_from_this();
return std::nullopt;
}
std::optional<std::weak_ptr<TransitHop>>
PathContext::TransitHopByUpstream(const RouterID& upstream, const PathID_t& id)
{
// this is ugly as sin as well
auto own = MapGet<
SyncTransitMap_t::Lock_t,
decltype(m_TransitPaths),
PathID_t,
std::function<bool(const std::shared_ptr<TransitHop>&)>,
std::function<TransitHop*(const std::shared_ptr<TransitHop>&)>,
TransitHop*>(
m_TransitPaths,
id,
[upstream](const auto& hop) -> bool { return hop->info.upstream == upstream; },
[](const auto& hop) -> TransitHop* { return hop.get(); });
if (own)
return own->weak_from_this();
return std::nullopt;
}
HopHandler_ptr HopHandler_ptr
PathContext::GetByUpstream(const RouterID& remote, const PathID_t& id) PathContext::GetByUpstream(const RouterID& remote, const PathID_t& id)
{ {
@ -225,7 +266,8 @@ namespace llarp
auto itr = map.second.find(id); auto itr = map.second.find(id);
if (itr != map.second.end()) if (itr != map.second.end())
{ {
return itr->second->m_PathSet->GetSelf(); if (auto parent = itr->second->m_PathSet.lock())
return parent;
} }
return nullptr; return nullptr;
} }

View File

@ -77,6 +77,12 @@ namespace llarp
HopHandler_ptr HopHandler_ptr
GetByDownstream(const RouterID& id, const PathID_t& path); GetByDownstream(const RouterID& id, const PathID_t& path);
std::optional<std::weak_ptr<TransitHop>>
TransitHopByInfo(const TransitHopInfo&);
std::optional<std::weak_ptr<TransitHop>>
TransitHopByUpstream(const RouterID&, const PathID_t&);
PathSet_ptr PathSet_ptr
GetLocalPathSet(const PathID_t& id); GetLocalPathSet(const PathID_t& id);

View File

@ -6,8 +6,10 @@
#include "path_context.hpp" #include "path_context.hpp"
#include <llarp/profiling.hpp> #include <llarp/profiling.hpp>
#include <llarp/router/abstractrouter.hpp> #include <llarp/router/abstractrouter.hpp>
#include <llarp/router/i_rc_lookup_handler.hpp>
#include <llarp/util/buffer.hpp> #include <llarp/util/buffer.hpp>
#include <llarp/tooling/path_event.hpp> #include <llarp/tooling/path_event.hpp>
#include <llarp/link/link_manager.hpp>
#include <functional> #include <functional>
@ -192,6 +194,9 @@ 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_router->pathBuildLimiter().Decay(now);
ExpirePaths(now, m_router); ExpirePaths(now, m_router);
if (ShouldBuildMore(now)) if (ShouldBuildMore(now))
BuildOne(); BuildOne();
@ -211,8 +216,8 @@ namespace llarp
{ {
util::StatusObject obj{ util::StatusObject obj{
{"buildStats", m_BuildStats.ExtractStatus()}, {"buildStats", m_BuildStats.ExtractStatus()},
{"numHops", uint64_t(numHops)}, {"numHops", uint64_t{numHops}},
{"numPaths", uint64_t(numDesiredPaths)}}; {"numPaths", uint64_t{numDesiredPaths}}};
std::transform( std::transform(
m_Paths.begin(), m_Paths.begin(),
m_Paths.end(), m_Paths.end(),
@ -240,6 +245,9 @@ namespace llarp
if (BuildCooldownHit(rc.pubkey)) if (BuildCooldownHit(rc.pubkey))
return; return;
if (m_router->routerProfiling().IsBadForPath(rc.pubkey))
return;
found = rc; found = rc;
} }
}, },
@ -251,7 +259,7 @@ namespace llarp
Builder::GetHopsForBuild() Builder::GetHopsForBuild()
{ {
auto filter = [r = m_router](const auto& rc) -> bool { auto filter = [r = m_router](const auto& rc) -> bool {
return not r->routerProfiling().IsBadForPath(rc.pubkey); return not r->routerProfiling().IsBadForPath(rc.pubkey, 1);
}; };
if (const auto maybe = m_router->nodedb()->GetRandom(filter)) if (const auto maybe = m_router->nodedb()->GetRandom(filter))
{ {
@ -264,14 +272,12 @@ namespace llarp
Builder::Stop() Builder::Stop()
{ {
_run = false; _run = false;
// tell all our paths that they have expired // tell all our paths that they are to be ignored
const auto now = Now(); const auto now = Now();
for (auto& item : m_Paths) for (auto& item : m_Paths)
{ {
item.second->EnterState(ePathExpired, now); item.second->EnterState(ePathIgnore, now);
} }
// remove expired paths
ExpirePaths(now, m_router);
return true; return true;
} }
@ -284,7 +290,7 @@ namespace llarp
bool bool
Builder::ShouldRemove() const Builder::ShouldRemove() const
{ {
return IsStopped(); return IsStopped() and NumInStatus(ePathEstablished) == 0;
} }
const SecretKey& const SecretKey&
@ -368,7 +374,7 @@ namespace llarp
hopsSet.insert(endpointRC); hopsSet.insert(endpointRC);
hopsSet.insert(hops.begin(), hops.end()); hopsSet.insert(hops.begin(), hops.end());
if (r->routerProfiling().IsBadForPath(rc.pubkey)) if (r->routerProfiling().IsBadForPath(rc.pubkey, 1))
return false; return false;
for (const auto& hop : hopsSet) for (const auto& hop : hopsSet)
{ {
@ -430,7 +436,7 @@ namespace llarp
ctx->pathset = self; ctx->pathset = self;
std::string path_shortName = "[path " + m_router->ShortName() + "-"; std::string path_shortName = "[path " + m_router->ShortName() + "-";
path_shortName = path_shortName + std::to_string(m_router->NextPathBuildNumber()) + "]"; path_shortName = path_shortName + std::to_string(m_router->NextPathBuildNumber()) + "]";
auto path = std::make_shared<path::Path>(hops, self.get(), roles, std::move(path_shortName)); auto path = std::make_shared<path::Path>(hops, GetWeak(), roles, std::move(path_shortName));
LogInfo(Name(), " build ", path->ShortName(), ": ", path->HopsString()); LogInfo(Name(), " build ", path->ShortName(), ": ", path->HopsString());
path->SetBuildResultHook([self](Path_ptr p) { self->HandlePathBuilt(p); }); path->SetBuildResultHook([self](Path_ptr p) { self->HandlePathBuilt(p); });
@ -473,6 +479,31 @@ namespace llarp
m_router->routerProfiling().MarkPathTimeout(p.get()); m_router->routerProfiling().MarkPathTimeout(p.get());
PathSet::HandlePathBuildTimeout(p); PathSet::HandlePathBuildTimeout(p);
DoPathBuildBackoff(); DoPathBuildBackoff();
for (const auto& hop : p->hops)
{
const RouterID router{hop.rc.pubkey};
// look up router and see if it's still on the network
m_router->loop()->call_soon([router, r = m_router]() {
LogInfo("looking up ", router, " because of path build timeout");
r->rcLookupHandler().GetRC(
router,
[r](const auto& router, const auto* rc, auto result) {
if (result == RCRequestResult::Success && rc != nullptr)
{
LogInfo("refreshed rc for ", router);
r->nodedb()->PutIfNewer(*rc);
}
else
{
// remove all connections to this router as it's probably not registered anymore
LogWarn("removing router ", router, " because of path build timeout");
r->linkManager().DeregisterPeer(router);
r->nodedb()->Remove(router);
}
},
true);
});
}
} }
void void

View File

@ -57,7 +57,7 @@ namespace llarp
DoPathBuildBackoff(); DoPathBuildBackoff();
public: public:
AbstractRouter* m_router; AbstractRouter* const m_router;
SecretKey enckey; SecretKey enckey;
size_t numHops; size_t numHops;
llarp_time_t lastBuild = 0s; llarp_time_t lastBuild = 0s;

View File

@ -96,9 +96,10 @@ namespace llarp
} }
Path_ptr Path_ptr
PathSet::GetEstablishedPathClosestTo(RouterID id, PathRole roles) const PathSet::GetEstablishedPathClosestTo(
RouterID id, std::unordered_set<RouterID> excluding, PathRole roles) const
{ {
Lock_t l(m_PathsMutex); Lock_t l{m_PathsMutex};
Path_ptr path = nullptr; Path_ptr path = nullptr;
AlignedBuffer<32> dist; AlignedBuffer<32> dist;
AlignedBuffer<32> to = id; AlignedBuffer<32> to = id;
@ -109,6 +110,8 @@ namespace llarp
continue; continue;
if (!item.second->SupportsAnyRoles(roles)) if (!item.second->SupportsAnyRoles(roles))
continue; continue;
if (excluding.count(item.second->Endpoint()))
continue;
AlignedBuffer<32> localDist = item.second->Endpoint() ^ to; AlignedBuffer<32> localDist = item.second->Endpoint() ^ to;
if (localDist < dist) if (localDist < dist)
{ {
@ -280,44 +283,24 @@ namespace llarp
return itr->second; return itr->second;
} }
bool std::optional<std::set<service::Introduction>>
PathSet::GetCurrentIntroductionsWithFilter( PathSet::GetCurrentIntroductionsWithFilter(
std::set<service::Introduction>& intros,
std::function<bool(const service::Introduction&)> filter) const std::function<bool(const service::Introduction&)> filter) const
{ {
intros.clear(); std::set<service::Introduction> intros;
size_t count = 0; Lock_t l{m_PathsMutex};
Lock_t l(m_PathsMutex);
auto itr = m_Paths.begin(); auto itr = m_Paths.begin();
while (itr != m_Paths.end()) while (itr != m_Paths.end())
{ {
if (itr->second->IsReady() && filter(itr->second->intro)) if (itr->second->IsReady() and filter(itr->second->intro))
{ {
intros.insert(itr->second->intro); intros.insert(itr->second->intro);
++count;
} }
++itr; ++itr;
} }
return count > 0; if (intros.empty())
} return std::nullopt;
return intros;
bool
PathSet::GetCurrentIntroductions(std::set<service::Introduction>& intros) const
{
intros.clear();
size_t count = 0;
Lock_t l(m_PathsMutex);
auto itr = m_Paths.begin();
while (itr != m_Paths.end())
{
if (itr->second->IsReady())
{
intros.insert(itr->second->intro);
++count;
}
++itr;
}
return count > 0;
} }
void void

View File

@ -13,6 +13,7 @@
#include <list> #include <list>
#include <map> #include <map>
#include <tuple> #include <tuple>
#include <unordered_set>
namespace std namespace std
{ {
@ -116,6 +117,10 @@ namespace llarp
virtual PathSet_ptr virtual PathSet_ptr
GetSelf() = 0; GetSelf() = 0;
/// get a weak_ptr of ourself
virtual std::weak_ptr<PathSet>
GetWeak() = 0;
virtual void virtual void
BuildOne(PathRole roles = ePathRoleAny) = 0; BuildOne(PathRole roles = ePathRoleAny) = 0;
@ -235,7 +240,10 @@ namespace llarp
} }
Path_ptr Path_ptr
GetEstablishedPathClosestTo(RouterID router, PathRole roles = ePathRoleAny) const; GetEstablishedPathClosestTo(
RouterID router,
std::unordered_set<RouterID> excluding = {},
PathRole roles = ePathRoleAny) const;
Path_ptr Path_ptr
PickEstablishedPath(PathRole roles = ePathRoleAny) const; PickEstablishedPath(PathRole roles = ePathRoleAny) const;
@ -258,14 +266,10 @@ namespace llarp
Path_ptr Path_ptr
GetByEndpointWithID(RouterID router, PathID_t id) const; GetByEndpointWithID(RouterID router, PathID_t id) const;
bool std::optional<std::set<service::Introduction>>
GetCurrentIntroductionsWithFilter( GetCurrentIntroductionsWithFilter(
std::set<service::Introduction>& intros,
std::function<bool(const service::Introduction&)> filter) const; std::function<bool(const service::Introduction&)> filter) const;
bool
GetCurrentIntroductions(std::set<service::Introduction>& intros) const;
virtual bool virtual bool
PublishIntroSet(const service::EncryptedIntroSet&, AbstractRouter*) PublishIntroSet(const service::EncryptedIntroSet&, AbstractRouter*)
{ {

View File

@ -64,22 +64,9 @@ namespace llarp
// TODO: add to IHopHandler some notion of "path status" // TODO: add to IHopHandler some notion of "path status"
const uint64_t ourStatus = LR_StatusRecord::SUCCESS; const uint64_t ourStatus = LR_StatusRecord::SUCCESS;
if (!msg->AddFrame(pathKey, ourStatus))
{
return false;
}
LR_StatusMessage::QueueSendMessage(r, info.downstream, msg);
if ((status & LR_StatusRecord::SUCCESS) != LR_StatusRecord::SUCCESS)
{
LogWarn(
"TransitHop received non-successful LR_StatusMessage, queueing "
"self-destruct status=",
status);
QueueDestroySelf(r);
}
msg->AddFrame(pathKey, ourStatus);
LR_StatusMessage::QueueSendMessage(r, info.downstream, msg, shared_from_this());
return true; return true;
} }

View File

@ -185,6 +185,9 @@ namespace llarp
void void
FlushDownstream(AbstractRouter* r) override; FlushDownstream(AbstractRouter* r) override;
void
QueueDestroySelf(AbstractRouter* r);
protected: protected:
void void
UpstreamWork(TrafficQueue_ptr queue, AbstractRouter* r) override; UpstreamWork(TrafficQueue_ptr queue, AbstractRouter* r) override;
@ -202,9 +205,6 @@ namespace llarp
void void
SetSelfDestruct(); SetSelfDestruct();
void
QueueDestroySelf(AbstractRouter* r);
std::set<std::shared_ptr<TransitHop>, ComparePtr<std::shared_ptr<TransitHop>>> m_FlushOthers; std::set<std::shared_ptr<TransitHop>, ComparePtr<std::shared_ptr<TransitHop>>> m_FlushOthers;
thread::Queue<RelayUpstreamMessage> m_UpstreamGather; thread::Queue<RelayUpstreamMessage> m_UpstreamGather;
thread::Queue<RelayDownstreamMessage> m_DownstreamGather; thread::Queue<RelayDownstreamMessage> m_DownstreamGather;

View File

@ -64,7 +64,7 @@ namespace llarp
void void
RouterProfile::Tick() RouterProfile::Tick()
{ {
static constexpr auto updateInterval = 30min; static constexpr auto updateInterval = 30s;
const auto now = llarp::time_now_ms(); const auto now = llarp::time_now_ms();
if (lastDecay < now && now - lastDecay > updateInterval) if (lastDecay < now && now - lastDecay > updateInterval)
Decay(); Decay();
@ -96,8 +96,9 @@ namespace llarp
bool bool
RouterProfile::IsGoodForPath(uint64_t chances) const RouterProfile::IsGoodForPath(uint64_t chances) const
{ {
return checkIsGood(pathFailCount, pathSuccessCount, chances) if (pathTimeoutCount > chances)
and checkIsGood(pathTimeoutCount, pathSuccessCount, chances); return false;
return checkIsGood(pathFailCount, pathSuccessCount, chances);
} }
Profiling::Profiling() : m_DisableProfiling(false) Profiling::Profiling() : m_DisableProfiling(false)
@ -210,16 +211,11 @@ namespace llarp
Profiling::MarkPathTimeout(path::Path* p) Profiling::MarkPathTimeout(path::Path* p)
{ {
util::Lock lock{m_ProfilesMutex}; util::Lock lock{m_ProfilesMutex};
size_t idx = 0;
for (const auto& hop : p->hops) for (const auto& hop : p->hops)
{
if (idx)
{ {
m_Profiles[hop.rc.pubkey].pathTimeoutCount += 1; m_Profiles[hop.rc.pubkey].pathTimeoutCount += 1;
m_Profiles[hop.rc.pubkey].lastUpdated = llarp::time_now_ms(); m_Profiles[hop.rc.pubkey].lastUpdated = llarp::time_now_ms();
} }
++idx;
}
} }
void void

View File

@ -528,6 +528,7 @@ namespace llarp::quic
if (not continue_connecting( if (not continue_connecting(
pport, (bool)maybe_remote, "endpoint ONS lookup", remote_addr)) pport, (bool)maybe_remote, "endpoint ONS lookup", remote_addr))
return; return;
service_endpoint_.MarkAddressOutbound(*maybe_remote);
service_endpoint_.EnsurePathTo(*maybe_remote, after_path, open_timeout); service_endpoint_.EnsurePathTo(*maybe_remote, after_path, open_timeout);
}); });
return result; return result;
@ -539,7 +540,10 @@ namespace llarp::quic
if (auto maybe_convo = service_endpoint_.GetBestConvoTagFor(remote)) if (auto maybe_convo = service_endpoint_.GetBestConvoTagFor(remote))
after_path(maybe_convo); after_path(maybe_convo);
else else
{
service_endpoint_.MarkAddressOutbound(remote);
service_endpoint_.EnsurePathTo(remote, after_path, open_timeout); service_endpoint_.EnsurePathTo(remote, after_path, open_timeout);
}
return result; return result;
} }

View File

@ -12,6 +12,7 @@
#include <llarp/router_contact.hpp> #include <llarp/router_contact.hpp>
#include <llarp/tooling/router_event.hpp> #include <llarp/tooling/router_event.hpp>
#include <llarp/peerstats/peer_db.hpp> #include <llarp/peerstats/peer_db.hpp>
#include <llarp/consensus/reachability_testing.hpp>
#include <optional> #include <optional>
@ -283,14 +284,21 @@ namespace llarp
/// set router's service node whitelist /// set router's service node whitelist
virtual void virtual void
SetRouterWhitelist(const std::vector<RouterID> routers) = 0; SetRouterWhitelist(
const std::vector<RouterID>& whitelist, const std::vector<RouterID>& greylist) = 0;
virtual std::unordered_set<RouterID>
GetRouterWhitelist() const = 0;
/// visit each connected link session /// visit each connected link session
virtual void virtual void
ForEachPeer(std::function<void(const ILinkSession*, bool)> visit, bool randomize) const = 0; ForEachPeer(std::function<void(const ILinkSession*, bool)> visit, bool randomize) const = 0;
virtual bool virtual bool
ConnectionToRouterAllowed(const RouterID& router) const = 0; SessionToRouterAllowed(const RouterID& router) const = 0;
virtual bool
PathToRouterAllowed(const RouterID& router) const = 0;
/// return true if we have an exit as a client /// return true if we have an exit as a client
virtual bool virtual bool

View File

@ -33,13 +33,20 @@ namespace llarp
RemoveValidRouter(const RouterID& router) = 0; RemoveValidRouter(const RouterID& router) = 0;
virtual void virtual void
SetRouterWhitelist(const std::vector<RouterID>& routers) = 0; SetRouterWhitelist(
const std::vector<RouterID>& whitelist, const std::vector<RouterID>& greylist) = 0;
virtual void virtual void
GetRC(const RouterID& router, RCRequestCallback callback, bool forceLookup = false) = 0; GetRC(const RouterID& router, RCRequestCallback callback, bool forceLookup = false) = 0;
virtual bool virtual bool
RemoteIsAllowed(const RouterID& remote) const = 0; PathIsAllowed(const RouterID& remote) const = 0;
virtual bool
SessionIsAllowed(const RouterID& remote) const = 0;
virtual bool
IsGreylisted(const RouterID& remote) const = 0;
virtual bool virtual bool
CheckRC(const RouterContact& rc) const = 0; CheckRC(const RouterContact& rc) const = 0;

View File

@ -26,12 +26,11 @@ namespace llarp
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 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->SessionIsAllowed(remote))
{ {
DoCallback(callback, SendStatus::InvalidRouter); DoCallback(callback, SendStatus::InvalidRouter);
return true; return true;
} }
const uint16_t priority = msg.Priority(); const uint16_t priority = msg.Priority();
std::array<byte_t, MAX_LINK_MSG_SIZE> linkmsg_buffer; std::array<byte_t, MAX_LINK_MSG_SIZE> linkmsg_buffer;
llarp_buffer_t buf(linkmsg_buffer); llarp_buffer_t buf(linkmsg_buffer);

View File

@ -13,6 +13,8 @@
#include <llarp/crypto/crypto.hpp> #include <llarp/crypto/crypto.hpp>
#include <utility> #include <utility>
#include <llarp/rpc/lokid_rpc_client.hpp>
namespace llarp namespace llarp
{ {
struct PendingSession struct PendingSession
@ -35,18 +37,18 @@ namespace llarp
// TODO: do we want to keep it // TODO: do we want to keep it
const auto router = RouterID(session->GetPubKey()); const auto router = RouterID(session->GetPubKey());
const bool isOutbound = not session->IsInbound();
const std::string remoteType = session->GetRemoteRC().IsPublicRouter() ? "router" : "client"; const std::string remoteType = session->GetRemoteRC().IsPublicRouter() ? "router" : "client";
LogInfo("session with ", remoteType, " [", router, "] established"); LogInfo(
"session with ", remoteType, " [", router, "] ", isOutbound ? "established" : "received");
if (not _rcLookup->RemoteIsAllowed(router)) if (not _rcLookup->SessionIsAllowed(router))
{ {
FinalizeRequest(router, SessionResult::InvalidRouter); FinalizeRequest(router, SessionResult::InvalidRouter);
return false; return false;
} }
auto func = std::bind(&OutboundSessionMaker::VerifyRC, this, session->GetRemoteRC()); work([this, rc = session->GetRemoteRC()] { VerifyRC(rc); });
work(func);
return true; return true;
} }
@ -54,13 +56,9 @@ namespace llarp
void void
OutboundSessionMaker::OnConnectTimeout(ILinkSession* session) OutboundSessionMaker::OnConnectTimeout(ILinkSession* session)
{ {
// TODO: retry/num attempts const auto router = RouterID(session->GetPubKey());
LogWarn( LogWarn("Session establish attempt to ", router, " timed out.", session->GetRemoteEndpoint());
"Session establish attempt to ", FinalizeRequest(router, SessionResult::Timeout);
RouterID(session->GetPubKey()),
" timed out.",
session->GetRemoteEndpoint());
FinalizeRequest(session->GetPubKey(), SessionResult::Timeout);
} }
void void
@ -76,6 +74,7 @@ namespace llarp
if (HavePendingSessionTo(router)) if (HavePendingSessionTo(router))
{ {
LogDebug("has pending session to", router);
return; return;
} }
@ -133,7 +132,7 @@ namespace llarp
break; break;
exclude.insert(other.pubkey); exclude.insert(other.pubkey);
if (not _rcLookup->RemoteIsAllowed(other.pubkey)) if (not _rcLookup->SessionIsAllowed(other.pubkey))
{ {
continue; continue;
} }
@ -227,12 +226,20 @@ namespace llarp
{ {
_loop->call([this, router] { DoEstablish(router); }); _loop->call([this, router] { DoEstablish(router); });
} }
else if (_linkManager->HasSessionTo(router))
{
FinalizeRequest(router, SessionResult::Establish);
}
else
{
FinalizeRequest(router, SessionResult::NoLink);
}
} }
bool bool
OutboundSessionMaker::ShouldConnectTo(const RouterID& router) const OutboundSessionMaker::ShouldConnectTo(const RouterID& router) const
{ {
if (router == us or not _rcLookup->RemoteIsAllowed(router)) if (router == us or not _rcLookup->SessionIsAllowed(router))
return false; return false;
size_t numPending = 0; size_t numPending = 0;
{ {
@ -240,8 +247,10 @@ namespace llarp
if (pendingSessions.find(router) == pendingSessions.end()) if (pendingSessions.find(router) == pendingSessions.end())
numPending += pendingSessions.size(); numPending += pendingSessions.size();
} }
if (_linkManager->HasSessionTo(router)) if (_linkManager->HasOutboundSessionTo(router))
return false; return false;
if (_router->IsServiceNode())
return true;
return _linkManager->NumberOfConnectedRouters() + numPending < maxConnectedRouters; return _linkManager->NumberOfConnectedRouters() + numPending < maxConnectedRouters;
} }
@ -263,6 +272,7 @@ namespace llarp
{ {
if (not HavePendingSessionTo(router)) if (not HavePendingSessionTo(router))
{ {
LogError("no pending session to ", router);
return; return;
} }
@ -276,6 +286,7 @@ namespace llarp
else else
{ {
LogError("RCRequestResult::Success but null rc pointer given"); LogError("RCRequestResult::Success but null rc pointer given");
InvalidRouter(router);
} }
break; break;
case RCRequestResult::InvalidRouter: case RCRequestResult::InvalidRouter:
@ -285,6 +296,7 @@ namespace llarp
RouterNotFound(router); RouterNotFound(router);
break; break;
default: default:
RouterNotFound(router);
break; break;
} }
} }

View File

@ -33,17 +33,23 @@ namespace llarp
} }
void void
RCLookupHandler::SetRouterWhitelist(const std::vector<RouterID>& routers) RCLookupHandler::SetRouterWhitelist(
const std::vector<RouterID>& whitelist, const std::vector<RouterID>& greylist)
{ {
if (routers.empty()) if (whitelist.empty())
return; return;
util::Lock l(_mutex); util::Lock l(_mutex);
whitelistRouters.clear(); whitelistRouters.clear();
for (auto& router : routers) greylistRouters.clear();
for (auto& router : whitelist)
{ {
whitelistRouters.emplace(router); whitelistRouters.emplace(router);
} }
for (auto& router : greylist)
{
greylistRouters.emplace(router);
}
LogInfo("lokinet service node list now has ", whitelistRouters.size(), " routers"); LogInfo("lokinet service node list now has ", whitelistRouters.size(), " routers");
} }
@ -119,7 +125,24 @@ namespace llarp
} }
bool bool
RCLookupHandler::RemoteIsAllowed(const RouterID& remote) const RCLookupHandler::IsGreylisted(const RouterID& remote) const
{
if (_strictConnectPubkeys.size() && _strictConnectPubkeys.count(remote) == 0
&& !RemoteInBootstrap(remote))
{
return false;
}
if (not useWhitelist)
return false;
util::Lock lock{_mutex};
return greylistRouters.count(remote);
}
bool
RCLookupHandler::PathIsAllowed(const RouterID& remote) const
{ {
if (_strictConnectPubkeys.size() && _strictConnectPubkeys.count(remote) == 0 if (_strictConnectPubkeys.size() && _strictConnectPubkeys.count(remote) == 0
&& !RemoteInBootstrap(remote)) && !RemoteInBootstrap(remote))
@ -135,10 +158,27 @@ namespace llarp
return whitelistRouters.count(remote); return whitelistRouters.count(remote);
} }
bool
RCLookupHandler::SessionIsAllowed(const RouterID& remote) const
{
if (_strictConnectPubkeys.size() && _strictConnectPubkeys.count(remote) == 0
&& !RemoteInBootstrap(remote))
{
return false;
}
if (not useWhitelist)
return true;
util::Lock lock{_mutex};
return whitelistRouters.count(remote) or greylistRouters.count(remote);
}
bool bool
RCLookupHandler::CheckRC(const RouterContact& rc) const RCLookupHandler::CheckRC(const RouterContact& rc) const
{ {
if (not RemoteIsAllowed(rc.pubkey)) if (not SessionIsAllowed(rc.pubkey))
{ {
_dht->impl->DelRCNodeAsync(dht::Key_t{rc.pubkey}); _dht->impl->DelRCNodeAsync(dht::Key_t{rc.pubkey});
return false; return false;
@ -189,7 +229,7 @@ namespace llarp
if (newrc.pubkey != oldrc.pubkey) if (newrc.pubkey != oldrc.pubkey)
return false; return false;
if (!RemoteIsAllowed(newrc.pubkey)) if (!SessionIsAllowed(newrc.pubkey))
return false; return false;
auto func = std::bind(&RCLookupHandler::CheckRC, this, newrc); auto func = std::bind(&RCLookupHandler::CheckRC, this, newrc);
@ -332,7 +372,7 @@ namespace llarp
return; return;
} }
if (not RemoteIsAllowed(remote)) if (not SessionIsAllowed(remote))
{ {
FinalizeRequest(remote, &results[0], RCRequestResult::InvalidRouter); FinalizeRequest(remote, &results[0], RCRequestResult::InvalidRouter);
return; return;

View File

@ -41,7 +41,9 @@ namespace llarp
RemoveValidRouter(const RouterID& router) override EXCLUDES(_mutex); RemoveValidRouter(const RouterID& router) override EXCLUDES(_mutex);
void void
SetRouterWhitelist(const std::vector<RouterID>& routers) override EXCLUDES(_mutex); SetRouterWhitelist(
const std::vector<RouterID>& whitelist, const std::vector<RouterID>& greylist) override
EXCLUDES(_mutex);
bool bool
HaveReceivedWhitelist() const override; HaveReceivedWhitelist() const override;
@ -51,7 +53,13 @@ namespace llarp
EXCLUDES(_mutex); EXCLUDES(_mutex);
bool bool
RemoteIsAllowed(const RouterID& remote) const override EXCLUDES(_mutex); PathIsAllowed(const RouterID& remote) const override EXCLUDES(_mutex);
bool
SessionIsAllowed(const RouterID& remote) const override EXCLUDES(_mutex);
bool
IsGreylisted(const RouterID& remote) const override EXCLUDES(_mutex);
bool bool
CheckRC(const RouterContact& rc) const override; CheckRC(const RouterContact& rc) const override;
@ -84,6 +92,13 @@ namespace llarp
bool useWhitelist_arg, bool useWhitelist_arg,
bool isServiceNode_arg); bool isServiceNode_arg);
std::unordered_set<RouterID>
Whitelist() const
{
util::Lock lock{_mutex};
return whitelistRouters;
}
private: private:
void void
HandleDHTLookupResult(RouterID remote, const std::vector<RouterContact>& results); HandleDHTLookupResult(RouterID remote, const std::vector<RouterContact>& results);
@ -120,6 +135,7 @@ namespace llarp
bool isServiceNode = false; bool isServiceNode = false;
std::unordered_set<RouterID> whitelistRouters GUARDED_BY(_mutex); std::unordered_set<RouterID> whitelistRouters GUARDED_BY(_mutex);
std::unordered_set<RouterID> greylistRouters GUARDED_BY(_mutex);
using TimePoint = std::chrono::steady_clock::time_point; using TimePoint = std::chrono::steady_clock::time_point;
std::unordered_map<RouterID, TimePoint> _routerLookupTimes; std::unordered_map<RouterID, TimePoint> _routerLookupTimes;

View File

@ -42,7 +42,7 @@
#include <oxenmq/oxenmq.h> #include <oxenmq/oxenmq.h>
static constexpr std::chrono::milliseconds ROUTER_TICK_INTERVAL = 100ms; static constexpr std::chrono::milliseconds ROUTER_TICK_INTERVAL = 250ms;
namespace llarp namespace llarp
{ {
@ -63,7 +63,6 @@ namespace llarp
#else #else
, _randomStartDelay(std::chrono::seconds((llarp::randint() % 30) + 10)) , _randomStartDelay(std::chrono::seconds((llarp::randint() % 30) + 10))
#endif #endif
, m_lokidRpcClient(std::make_shared<rpc::LokidRpcClient>(m_lmq, this))
{ {
m_keyManager = std::make_shared<KeyManager>(); m_keyManager = std::make_shared<KeyManager>();
// for lokid, so we don't close the connection when syncing the whitelist // for lokid, so we don't close the connection when syncing the whitelist
@ -290,7 +289,10 @@ namespace llarp
auto& conf = *m_Config; auto& conf = *m_Config;
whitelistRouters = conf.lokid.whitelistRouters; whitelistRouters = conf.lokid.whitelistRouters;
if (whitelistRouters) if (whitelistRouters)
{
lokidRPCAddr = oxenmq::address(conf.lokid.lokidRPCAddr); lokidRPCAddr = oxenmq::address(conf.lokid.lokidRPCAddr);
m_lokidRpcClient = std::make_shared<rpc::LokidRpcClient>(m_lmq, weak_from_this());
}
enableRPCServer = conf.api.m_enableRPCServer; enableRPCServer = conf.api.m_enableRPCServer;
if (enableRPCServer) if (enableRPCServer)
@ -375,21 +377,27 @@ namespace llarp
} }
bool bool
Router::LooksDeregistered() const Router::LooksDecommissioned() const
{ {
return IsServiceNode() and whitelistRouters and _rcLookupHandler.HaveReceivedWhitelist() return IsServiceNode() and whitelistRouters and _rcLookupHandler.HaveReceivedWhitelist()
and not _rcLookupHandler.RemoteIsAllowed(pubkey()); and _rcLookupHandler.IsGreylisted(pubkey());
} }
bool bool
Router::ConnectionToRouterAllowed(const RouterID& router) const Router::SessionToRouterAllowed(const RouterID& router) const
{ {
if (LooksDeregistered()) return _rcLookupHandler.SessionIsAllowed(router);
}
bool
Router::PathToRouterAllowed(const RouterID& router) const
{ {
// we are deregistered don't allow any connections outbound at all if (LooksDecommissioned())
{
// we are decom'd don't allow any paths outbound at all
return false; return false;
} }
return _rcLookupHandler.RemoteIsAllowed(router); return _rcLookupHandler.PathIsAllowed(router);
} }
size_t size_t
@ -748,7 +756,7 @@ 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(); << "block " << (m_lokidRpcClient ? m_lokidRpcClient->BlockHeight() : 0);
} }
else else
{ {
@ -780,7 +788,7 @@ namespace llarp
const bool gotWhitelist = _rcLookupHandler.HaveReceivedWhitelist(); const bool gotWhitelist = _rcLookupHandler.HaveReceivedWhitelist();
const bool isSvcNode = IsServiceNode(); const bool isSvcNode = IsServiceNode();
const bool looksDeregistered = LooksDeregistered(); const bool decom = LooksDecommissioned();
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)
@ -789,8 +797,10 @@ namespace llarp
if (!UpdateOurRC(false)) if (!UpdateOurRC(false))
LogError("Failed to update our RC"); LogError("Failed to update our RC");
} }
else if (not looksDeregistered) else if (whitelistRouters and gotWhitelist and _rcLookupHandler.SessionIsAllowed(pubkey()))
{ {
// if we have the whitelist enabled, we have fetched the list and we are in either
// the white or grey list, we want to gossip our RC
GossipRCIfNeeded(_rc); GossipRCIfNeeded(_rc);
} }
// remove RCs for nodes that are no longer allowed by network policy // remove RCs for nodes that are no longer allowed by network policy
@ -815,7 +825,7 @@ namespace llarp
// the whitelist enabled and we got the whitelist // the whitelist enabled and we got the whitelist
// check against the whitelist and remove if it's not // check against the whitelist and remove if it's not
// in the whitelist OR if there is no whitelist don't remove // in the whitelist OR if there is no whitelist don't remove
return not _rcLookupHandler.RemoteIsAllowed(rc.pubkey); return not _rcLookupHandler.SessionIsAllowed(rc.pubkey);
}); });
// find all deregistered relays // find all deregistered relays
@ -827,7 +837,7 @@ namespace llarp
if (not session) if (not session)
return; return;
const auto pk = session->GetPubKey(); const auto pk = session->GetPubKey();
if (session->IsRelay() and not _rcLookupHandler.RemoteIsAllowed(pk)) if (session->IsRelay() and not _rcLookupHandler.SessionIsAllowed(pk))
{ {
closePeers.emplace(pk); closePeers.emplace(pk);
} }
@ -860,7 +870,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 and not looksDeregistered) if (timepoint_now >= m_NextExploreAt and not decom)
{ {
_rcLookupHandler.ExploreNetwork(); _rcLookupHandler.ExploreNetwork();
m_NextExploreAt = timepoint_now + std::chrono::seconds(interval); m_NextExploreAt = timepoint_now + std::chrono::seconds(interval);
@ -872,15 +882,15 @@ namespace llarp
connectToNum = strictConnect; connectToNum = strictConnect;
} }
if (looksDeregistered) if (decom)
{ {
// kill all sessions that are open because we think we are deregistered
_linkManager.ForEachPeer([](auto* peer) {
if (peer)
peer->Close();
});
// complain about being deregistered // complain about being deregistered
if (now >= m_NextDecommissionWarn)
{
constexpr auto DecommissionWarnInterval = 30s;
LogError("We are running as a service node but we seem to be decommissioned"); LogError("We are running as a service node but we seem to be decommissioned");
m_NextDecommissionWarn = now + DecommissionWarnInterval;
}
} }
else if (connected < connectToNum) else if (connected < connectToNum)
{ {
@ -1032,9 +1042,10 @@ namespace llarp
} }
void void
Router::SetRouterWhitelist(const std::vector<RouterID> routers) Router::SetRouterWhitelist(
const std::vector<RouterID>& whitelist, const std::vector<RouterID>& greylist)
{ {
_rcLookupHandler.SetRouterWhitelist(routers); _rcLookupHandler.SetRouterWhitelist(whitelist, greylist);
} }
bool bool
@ -1181,6 +1192,75 @@ namespace llarp
#if defined(WITH_SYSTEMD) #if defined(WITH_SYSTEMD)
::sd_notify(0, "READY=1"); ::sd_notify(0, "READY=1");
#endif #endif
if (whitelistRouters)
{
// do service node testing if we are in service node whitelist mode
_loop->call_every(consensus::REACHABILITY_TESTING_TIMER_INTERVAL, weak_from_this(), [this] {
// dont run tests if we are not running or we are stopping
if (not _running)
return;
// dont run tests if we are decommissioned
if (LooksDecommissioned())
return;
auto tests = m_routerTesting.get_failing();
if (auto maybe = m_routerTesting.next_random(this))
{
tests.emplace_back(*maybe, 0);
}
for (const auto& [router, fails] : tests)
{
if (not SessionToRouterAllowed(router))
{
LogDebug(
router,
" is no longer a registered service node so we remove it from the testing list");
m_routerTesting.remove_node_from_failing(router);
continue;
}
LogDebug("Establishing session to ", router, " for SN testing");
// try to make a session to this random router
// this will do a dht lookup if needed
_outboundSessionMaker.CreateSessionTo(
router, [previous_fails = fails, this](const auto& router, const auto result) {
auto rpc = RpcClient();
if (result != SessionResult::Establish)
{
// failed connection mark it as so
m_routerTesting.add_failing_node(router, previous_fails);
LogInfo(
"FAILED SN connection test to ",
router,
" (",
previous_fails + 1,
" consecutive failures)");
}
else
{
m_routerTesting.remove_node_from_failing(router);
if (previous_fails > 0)
{
LogInfo(
"Successful SN connection test to ",
router,
" after ",
previous_fails,
" failures");
}
else
{
LogDebug("Successful SN connection test to ", router);
}
}
if (rpc)
{
// inform as needed
rpc->InformConnection(router, result == SessionResult::Establish);
}
});
}
});
}
LogContext::Instance().DropToRuntimeLevel(); LogContext::Instance().DropToRuntimeLevel();
return _running; return _running;
} }
@ -1314,7 +1394,7 @@ namespace llarp
return false; return false;
} }
if (!_rcLookupHandler.RemoteIsAllowed(rc.pubkey)) if (not _rcLookupHandler.SessionIsAllowed(rc.pubkey))
{ {
return false; return false;
} }
@ -1327,8 +1407,10 @@ namespace llarp
void void
Router::QueueWork(std::function<void(void)> func) Router::QueueWork(std::function<void(void)> func)
{ {
_loop->call_soon(func); if (m_isServiceNode)
// m_lmq->job(std::move(func)); _loop->call_soon(std::move(func));
else
m_lmq->job(std::move(func));
} }
void void

View File

@ -131,7 +131,14 @@ namespace llarp
ModifyOurRC(std::function<std::optional<RouterContact>(RouterContact)> modify) override; ModifyOurRC(std::function<std::optional<RouterContact>(RouterContact)> modify) override;
void void
SetRouterWhitelist(const std::vector<RouterID> routers) override; SetRouterWhitelist(
const std::vector<RouterID>& whitelist, const std::vector<RouterID>& greylist) override;
std::unordered_set<RouterID>
GetRouterWhitelist() const override
{
return _rcLookupHandler.Whitelist();
}
exit::Context& exit::Context&
exitContext() override exitContext() override
@ -181,9 +188,9 @@ 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 /// return true if we look like we are a decommissioned service node
bool bool
LooksDeregistered() const; LooksDecommissioned() const;
std::optional<SockAddr> _ourAddress; std::optional<SockAddr> _ourAddress;
@ -392,7 +399,9 @@ namespace llarp
EnsureEncryptionKey(); EnsureEncryptionKey();
bool bool
ConnectionToRouterAllowed(const RouterID& router) const override; SessionToRouterAllowed(const RouterID& router) const override;
bool
PathToRouterAllowed(const RouterID& router) const override;
void void
HandleSaveRC() const; HandleSaveRC() const;
@ -530,12 +539,14 @@ namespace llarp
bool m_isServiceNode = false; bool m_isServiceNode = false;
llarp_time_t m_LastStatsReport = 0s; llarp_time_t m_LastStatsReport = 0s;
llarp_time_t m_NextDecommissionWarn = 0s;
std::shared_ptr<llarp::KeyManager> m_keyManager; std::shared_ptr<llarp::KeyManager> m_keyManager;
std::shared_ptr<PeerDb> m_peerDb; std::shared_ptr<PeerDb> m_peerDb;
uint32_t path_build_count = 0; uint32_t path_build_count = 0;
consensus::reachability_testing m_routerTesting;
bool bool
ShouldReportStats(llarp_time_t now) const; ShouldReportStats(llarp_time_t now) const;

View File

@ -32,8 +32,8 @@ namespace llarp
} }
} }
LokidRpcClient::LokidRpcClient(LMQ_ptr lmq, AbstractRouter* r) LokidRpcClient::LokidRpcClient(LMQ_ptr lmq, std::weak_ptr<AbstractRouter> r)
: m_lokiMQ(std::move(lmq)), m_Router(r) : m_lokiMQ{std::move(lmq)}, m_Router{std::move(r)}
{ {
// m_lokiMQ->log_level(toLokiMQLogLevel(LogLevel::Instance().curLevel)); // m_lokiMQ->log_level(toLokiMQLogLevel(LogLevel::Instance().curLevel));
@ -51,7 +51,9 @@ namespace llarp
void void
LokidRpcClient::ConnectAsync(oxenmq::address url) LokidRpcClient::ConnectAsync(oxenmq::address url)
{ {
if (not m_Router->IsServiceNode()) if (auto router = m_Router.lock())
{
if (not router->IsServiceNode())
{ {
throw std::runtime_error("we cannot talk to lokid while not a service node"); throw std::runtime_error("we cannot talk to lokid while not a service node");
} }
@ -61,9 +63,13 @@ namespace llarp
[self = shared_from_this()](oxenmq::ConnectionID) { self->Connected(); }, [self = shared_from_this()](oxenmq::ConnectionID) { self->Connected(); },
[self = shared_from_this(), url](oxenmq::ConnectionID, std::string_view f) { [self = shared_from_this(), url](oxenmq::ConnectionID, std::string_view f) {
llarp::LogWarn("Failed to connect to lokid: ", f); llarp::LogWarn("Failed to connect to lokid: ", f);
self->m_Router->loop()->call([self, url]() { self->ConnectAsync(url); }); if (auto router = self->m_Router.lock())
{
router->loop()->call([self, url]() { self->ConnectAsync(url); });
}
}); });
} }
}
void void
LokidRpcClient::Command(std::string_view cmd) LokidRpcClient::Command(std::string_view cmd)
@ -89,25 +95,25 @@ namespace llarp
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
LogError("bad block hieght: ", ex.what()); LogError("bad block height: ", ex.what());
return; // bail return; // bail
} }
LogDebug("new block at hieght ", m_BlockHeight); LogDebug("new block at height ", m_BlockHeight);
// don't upadate on block notification if an update is pending // don't upadate on block notification if an update is pending
if (not m_UpdatingList) if (not m_UpdatingList)
UpdateServiceNodeList(std::string{msg.data[1]}); UpdateServiceNodeList();
} }
void void
LokidRpcClient::UpdateServiceNodeList(std::string topblock) LokidRpcClient::UpdateServiceNodeList()
{ {
nlohmann::json request, fields; nlohmann::json request, fields;
fields["pubkey_ed25519"] = true; fields["pubkey_ed25519"] = true;
fields["service_node_pubkey"] = true;
fields["funded"] = true;
fields["active"] = true;
request["fields"] = fields; request["fields"] = fields;
request["active_only"] = true;
if (not topblock.empty())
request["poll_block_hash"] = topblock;
m_UpdatingList = true; m_UpdatingList = true;
Request( Request(
"rpc.get_service_nodes", "rpc.get_service_nodes",
@ -161,7 +167,7 @@ namespace llarp
}; };
m_lokiMQ->add_timer(makePingRequest, PingInterval); m_lokiMQ->add_timer(makePingRequest, PingInterval);
// initial fetch of service node list // initial fetch of service node list
UpdateServiceNodeList(""); UpdateServiceNodeList();
} }
void void
@ -179,8 +185,8 @@ namespace llarp
} }
} }
} }
std::unordered_map<RouterID, PubKey> keymap;
std::vector<RouterID> nodeList; std::vector<RouterID> activeNodeList, nonActiveNodeList;
{ {
const auto itr = j.find("service_node_states"); const auto itr = j.find("service_node_states");
if (itr != j.end() and itr->is_array()) if (itr != j.end() and itr->is_array())
@ -190,23 +196,82 @@ namespace llarp
const auto ed_itr = j_itr->find("pubkey_ed25519"); const auto ed_itr = j_itr->find("pubkey_ed25519");
if (ed_itr == j_itr->end() or not ed_itr->is_string()) if (ed_itr == j_itr->end() or not ed_itr->is_string())
continue; continue;
const auto svc_itr = j_itr->find("service_node_pubkey");
if (svc_itr == j_itr->end() or not svc_itr->is_string())
continue;
const auto funded_itr = j_itr->find("funded");
if (funded_itr == j_itr->end() or not funded_itr->is_boolean())
continue;
const auto active_itr = j_itr->find("active");
if (active_itr == j_itr->end() or not active_itr->is_boolean())
continue;
const bool active = active_itr->get<bool>();
const bool funded = funded_itr->get<bool>();
if (not funded)
continue;
RouterID rid; RouterID rid;
if (rid.FromHex(ed_itr->get<std::string>())) PubKey pk;
nodeList.emplace_back(std::move(rid)); if (rid.FromHex(ed_itr->get<std::string>()) and pk.FromHex(svc_itr->get<std::string>()))
{
keymap[rid] = pk;
if (active)
activeNodeList.emplace_back(std::move(rid));
else
nonActiveNodeList.emplace_back(std::move(rid));
}
} }
} }
} }
if (nodeList.empty()) if (activeNodeList.empty())
{ {
LogWarn("got empty service node list, ignoring."); LogWarn("got empty service node list, ignoring.");
return; return;
} }
// inform router about the new list // inform router about the new list
m_Router->loop()->call([r = m_Router, nodeList = std::move(nodeList)]() mutable { if (auto router = m_Router.lock())
r->SetRouterWhitelist(std::move(nodeList)); {
auto& loop = router->loop();
loop->call([this,
active = std::move(activeNodeList),
inactive = std::move(nonActiveNodeList),
keymap = std::move(keymap),
router = std::move(router)]() mutable {
m_KeyMap = std::move(keymap);
router->SetRouterWhitelist(active, inactive);
}); });
} }
else
LogWarn("Cannot update whitelist: router object has gone away");
}
void
LokidRpcClient::InformConnection(RouterID router, bool success)
{
if (auto r = m_Router.lock())
{
r->loop()->call([router, success, this]() {
if (auto itr = m_KeyMap.find(router); itr != m_KeyMap.end())
{
const nlohmann::json request = {
{"passed", success}, {"pubkey", itr->second.ToHex()}, {"type", "lokinet"}};
Request(
"admin.report_peer_status",
[self = shared_from_this()](bool success, std::vector<std::string>) {
if (not success)
{
LogError("Failed to report connection status to oxend");
return;
}
LogDebug("reported connection status to core");
},
request.dump());
}
});
}
}
SecretKey SecretKey
LokidRpcClient::ObtainIdentityKey() LokidRpcClient::ObtainIdentityKey()
@ -261,7 +326,7 @@ namespace llarp
const nlohmann::json req{{"type", 2}, {"name_hash", namehash.ToHex()}}; const nlohmann::json req{{"type", 2}, {"name_hash", namehash.ToHex()}};
Request( Request(
"rpc.lns_resolve", "rpc.lns_resolve",
[r = m_Router, resultHandler](bool success, std::vector<std::string> data) { [this, resultHandler](bool success, std::vector<std::string> data) {
std::optional<service::EncryptedName> maybe = std::nullopt; std::optional<service::EncryptedName> maybe = std::nullopt;
if (success) if (success)
{ {
@ -285,8 +350,11 @@ namespace llarp
LogError("failed to parse response from lns lookup: ", ex.what()); LogError("failed to parse response from lns lookup: ", ex.what());
} }
} }
if (auto r = m_Router.lock())
{
r->loop()->call( r->loop()->call(
[resultHandler, maybe = std::move(maybe)]() { resultHandler(std::move(maybe)); }); [resultHandler, maybe = std::move(maybe)]() { resultHandler(std::move(maybe)); });
}
}, },
req.dump()); req.dump());
} }
@ -300,9 +368,9 @@ namespace llarp
LogInfo(" :", str); LogInfo(" :", str);
} }
assert(m_Router != nullptr); if (auto router = m_Router.lock())
{
if (not m_Router->peerDb()) if (not router->peerDb())
{ {
LogWarn("HandleGetPeerStats called when router has no peerDb set up."); LogWarn("HandleGetPeerStats called when router has no peerDb set up.");
@ -342,7 +410,7 @@ namespace llarp
routerIds.push_back(std::move(id)); routerIds.push_back(std::move(id));
} }
auto statsList = m_Router->peerDb()->listPeerStats(routerIds); auto statsList = router->peerDb()->listPeerStats(routerIds);
int32_t bufSize = int32_t bufSize =
256 + (statsList.size() * 1024); // TODO: tune this or allow to grow dynamically 256 + (statsList.size() * 1024); // TODO: tune this or allow to grow dynamically
@ -351,7 +419,8 @@ namespace llarp
PeerStats::BEncodeList(statsList, &llarpBuf); PeerStats::BEncodeList(statsList, &llarpBuf);
msg.send_reply(std::string_view((const char*)llarpBuf.base, llarpBuf.cur - llarpBuf.base)); msg.send_reply(
std::string_view((const char*)llarpBuf.base, llarpBuf.cur - llarpBuf.base));
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -359,6 +428,6 @@ namespace llarp
msg.send_reply("server error"); msg.send_reply("server error");
} }
} }
}
} // namespace rpc } // namespace rpc
} // namespace llarp } // namespace llarp

View File

@ -19,7 +19,7 @@ namespace llarp
/// The LokidRpcClient uses loki-mq to talk to make API requests to lokid. /// The LokidRpcClient uses loki-mq to talk to make API requests to lokid.
struct LokidRpcClient : public std::enable_shared_from_this<LokidRpcClient> struct LokidRpcClient : public std::enable_shared_from_this<LokidRpcClient>
{ {
explicit LokidRpcClient(LMQ_ptr lmq, AbstractRouter* r); explicit LokidRpcClient(LMQ_ptr lmq, std::weak_ptr<AbstractRouter> r);
/// Connect to lokid async /// Connect to lokid async
void void
@ -42,6 +42,10 @@ namespace llarp
dht::Key_t namehash, dht::Key_t namehash,
std::function<void(std::optional<service::EncryptedName>)> resultHandler); std::function<void(std::optional<service::EncryptedName>)> resultHandler);
/// inform that if connected to a router successfully
void
InformConnection(RouterID router, bool success);
private: private:
/// called when we have connected to lokid via lokimq /// called when we have connected to lokid via lokimq
void void
@ -52,7 +56,7 @@ namespace llarp
Command(std::string_view cmd); Command(std::string_view cmd);
void void
UpdateServiceNodeList(std::string topblock); UpdateServiceNodeList();
template <typename HandlerFunc_t, typename Args_t> template <typename HandlerFunc_t, typename Args_t>
void void
@ -82,9 +86,11 @@ namespace llarp
std::optional<oxenmq::ConnectionID> m_Connection; std::optional<oxenmq::ConnectionID> m_Connection;
LMQ_ptr m_lokiMQ; LMQ_ptr m_lokiMQ;
AbstractRouter* const m_Router; std::weak_ptr<AbstractRouter> m_Router;
std::atomic<bool> m_UpdatingList; std::atomic<bool> m_UpdatingList;
std::unordered_map<RouterID, PubKey> m_KeyMap;
uint64_t m_BlockHeight; uint64_t m_BlockHeight;
}; };

View File

@ -480,6 +480,7 @@ namespace llarp::rpc
onGoodResult("added null exit"); onGoodResult("added null exit");
return; return;
} }
ep->MarkAddressOutbound(addr);
ep->EnsurePathToService( ep->EnsurePathToService(
addr, addr,
[onBadResult, onGoodResult, shouldSendAuth, addrStr = addr.ToString()]( [onBadResult, onGoodResult, shouldSendAuth, addrStr = addr.ToString()](

View File

@ -101,11 +101,16 @@ namespace llarp
{ {
const auto now = llarp::time_now_ms(); const auto now = llarp::time_now_ms();
m_LastIntrosetRegenAttempt = now; m_LastIntrosetRegenAttempt = now;
std::set<Introduction> introset; std::set<Introduction, CompareIntroTimestamp> intros;
if (!GetCurrentIntroductionsWithFilter( if (const auto maybe =
introset, [now](const service::Introduction& intro) -> bool { GetCurrentIntroductionsWithFilter([now](const service::Introduction& intro) -> bool {
return not intro.ExpiresSoon(now, path::min_intro_lifetime); return not intro.ExpiresSoon(
now, path::default_lifetime - path::min_intro_lifetime);
})) }))
{
intros.insert(maybe->begin(), maybe->end());
}
else
{ {
LogWarn( LogWarn(
"could not publish descriptors for endpoint ", "could not publish descriptors for endpoint ",
@ -146,8 +151,9 @@ namespace llarp
} }
introSet().intros.clear(); introSet().intros.clear();
for (auto& intro : introset) for (auto& intro : intros)
{ {
if (introSet().intros.size() < numDesiredPaths)
introSet().intros.emplace_back(std::move(intro)); introSet().intros.emplace_back(std::move(intro));
} }
if (introSet().intros.empty()) if (introSet().intros.empty())
@ -710,8 +716,10 @@ namespace llarp
return false; return false;
auto next_pub = m_state->m_LastPublishAttempt auto next_pub = m_state->m_LastPublishAttempt
+ (m_state->m_IntroSet.HasExpiredIntros(now) ? INTROSET_PUBLISH_RETRY_INTERVAL + (m_state->m_IntroSet.HasStaleIntros(
: INTROSET_PUBLISH_INTERVAL); now, path::default_lifetime - path::intro_path_spread)
? IntrosetPublishRetryCooldown
: IntrosetPublishInterval);
return now >= next_pub and m_LastIntrosetRegenAttempt + 1s <= now; return now >= next_pub and m_LastIntrosetRegenAttempt + 1s <= now;
} }
@ -739,8 +747,11 @@ namespace llarp
{ {
std::unordered_set<RouterID> exclude; std::unordered_set<RouterID> exclude;
ForEachPath([&exclude](auto path) { exclude.insert(path->Endpoint()); }); ForEachPath([&exclude](auto path) { exclude.insert(path->Endpoint()); });
const auto maybe = m_router->nodedb()->GetRandom( const auto maybe =
[exclude](const auto& rc) -> bool { return exclude.count(rc.pubkey) == 0; }); m_router->nodedb()->GetRandom([exclude, r = m_router](const auto& rc) -> bool {
return exclude.count(rc.pubkey) == 0
and not r->routerProfiling().IsBadForPath(rc.pubkey);
});
if (not maybe.has_value()) if (not maybe.has_value())
return std::nullopt; return std::nullopt;
return GetHopsForBuildWithEndpoint(maybe->pubkey); return GetHopsForBuildWithEndpoint(maybe->pubkey);
@ -758,46 +769,27 @@ namespace llarp
path::Builder::PathBuildStarted(path); path::Builder::PathBuildStarted(path);
} }
constexpr auto MaxOutboundContextPerRemote = 4; constexpr auto MaxOutboundContextPerRemote = 1;
void void
Endpoint::PutNewOutboundContext(const service::IntroSet& introset, llarp_time_t left) Endpoint::PutNewOutboundContext(const service::IntroSet& introset, llarp_time_t left)
{ {
Address addr{introset.addressKeys.Addr()}; const Address addr{introset.addressKeys.Addr()};
auto& remoteSessions = m_state->m_RemoteSessions; auto& remoteSessions = m_state->m_RemoteSessions;
auto& serviceLookups = m_state->m_PendingServiceLookups;
if (remoteSessions.count(addr) >= MaxOutboundContextPerRemote) if (remoteSessions.count(addr) < MaxOutboundContextPerRemote)
{ {
auto itr = remoteSessions.find(addr); remoteSessions.emplace(addr, std::make_shared<OutboundContext>(introset, this));
auto range = serviceLookups.equal_range(addr);
auto i = range.first;
while (i != range.second)
{
itr->second->SetReadyHook(
[callback = i->second, addr](auto session) { callback(addr, session); }, left);
++i;
}
serviceLookups.erase(addr);
return;
}
auto session = std::make_shared<OutboundContext>(introset, this);
remoteSessions.emplace(addr, session);
LogInfo("Created New outbound context for ", addr.ToString()); LogInfo("Created New outbound context for ", addr.ToString());
// inform pending
auto range = serviceLookups.equal_range(addr);
auto itr = range.first;
if (itr != range.second)
{
session->SetReadyHook(
[callback = itr->second, addr](auto session) { callback(addr, session); }, left);
++itr;
} }
serviceLookups.erase(addr);
auto sessionRange = remoteSessions.equal_range(addr);
for (auto itr = sessionRange.first; itr != sessionRange.second; ++itr)
{
itr->second->AddReadyHook(
[addr, this](auto session) { InformPathToService(addr, session); }, left);
}
} }
void void
@ -924,7 +916,7 @@ namespace llarp
paths.insert(path); paths.insert(path);
}); });
constexpr size_t min_unique_lns_endpoints = 3; constexpr size_t min_unique_lns_endpoints = 2;
// not enough paths // not enough paths
if (paths.size() < min_unique_lns_endpoints) if (paths.size() < min_unique_lns_endpoints)
@ -1013,7 +1005,15 @@ 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, router, nodedb = m_router->nodedb()](auto results) {
if (results.empty())
{
LogInfo("could not find ", router, ", remove it from nodedb");
nodedb->Remove(router);
}
if (handler)
handler(results);
}};
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());
@ -1066,11 +1066,11 @@ namespace llarp
void void
Endpoint::QueueRecvData(RecvDataEvent ev) Endpoint::QueueRecvData(RecvDataEvent ev)
{ {
if (m_RecvQueue.full() || m_RecvQueue.empty()) if (m_RecvQueue.full() or m_RecvQueue.empty())
{ {
m_router->loop()->call([this] { FlushRecvData(); }); m_router->loop()->call_soon([this] { FlushRecvData(); });
} }
m_RecvQueue.pushBack(std::move(ev)); m_RecvQueue.tryPushBack(std::move(ev));
} }
bool bool
@ -1078,14 +1078,15 @@ namespace llarp
path::Path_ptr path, const PathID_t from, std::shared_ptr<ProtocolMessage> msg) path::Path_ptr path, const PathID_t from, std::shared_ptr<ProtocolMessage> msg)
{ {
msg->sender.UpdateAddr(); msg->sender.UpdateAddr();
if (not HasOutboundConvo(msg->sender.Addr()))
PutSenderFor(msg->tag, msg->sender, true); PutSenderFor(msg->tag, msg->sender, true);
PutReplyIntroFor(msg->tag, path->intro); PutReplyIntroFor(msg->tag, path->intro);
Introduction intro; Introduction intro;
intro.pathID = from; intro.pathID = from;
intro.router = PubKey{path->Endpoint()}; intro.router = PubKey{path->Endpoint()};
intro.expiresAt = std::min(path->ExpireTime(), msg->introReply.expiresAt); intro.expiresAt = std::min(path->ExpireTime(), msg->introReply.expiresAt);
intro.latency = path->intro.latency;
PutIntroFor(msg->tag, intro); PutIntroFor(msg->tag, intro);
PutReplyIntroFor(msg->tag, path->intro);
ConvoTagRX(msg->tag); ConvoTagRX(msg->tag);
return ProcessDataMessage(msg); return ProcessDataMessage(msg);
} }
@ -1178,10 +1179,11 @@ namespace llarp
// not applicable because we are not an exit or don't have an endpoint auth policy // 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) if ((not m_state->m_ExitEnabled) or m_AuthPolicy == nullptr)
return; return;
ProtocolFrame f; ProtocolFrame f{};
f.R = AuthResultCodeAsInt(result.code); f.R = AuthResultCodeAsInt(result.code);
f.T = tag; f.T = tag;
f.F = path->intro.pathID; f.F = path->intro.pathID;
f.N.Randomize();
if (result.code == AuthResultCode::eAuthAccepted) if (result.code == AuthResultCode::eAuthAccepted)
{ {
ProtocolMessage msg; ProtocolMessage msg;
@ -1189,10 +1191,7 @@ namespace llarp
std::vector<byte_t> reason{}; std::vector<byte_t> reason{};
reason.resize(result.reason.size()); reason.resize(result.reason.size());
std::copy_n(result.reason.c_str(), reason.size(), reason.data()); std::copy_n(result.reason.c_str(), reason.size(), reason.data());
msg.PutBuffer(reason); msg.PutBuffer(reason);
f.N.Randomize();
f.C.Zero();
if (m_AuthPolicy) if (m_AuthPolicy)
msg.proto = ProtocolType::Auth; msg.proto = ProtocolType::Auth;
else else
@ -1234,6 +1233,23 @@ namespace llarp
Sessions().erase(t); Sessions().erase(t);
} }
void
Endpoint::ResetConvoTag(ConvoTag tag, path::Path_ptr p, PathID_t from)
{
// send reset convo tag message
ProtocolFrame f{};
f.R = 1;
f.T = tag;
f.F = p->intro.pathID;
f.Sign(m_Identity);
{
LogWarn("invalidating convotag T=", tag);
RemoveConvoTag(tag);
m_SendQueue.tryPushBack(
SendEvent_t{std::make_shared<routing::PathTransferMessage>(f, from), p});
}
}
bool bool
Endpoint::HandleHiddenServiceFrame(path::Path_ptr p, const ProtocolFrame& frame) Endpoint::HandleHiddenServiceFrame(path::Path_ptr p, const ProtocolFrame& frame)
{ {
@ -1247,29 +1263,13 @@ namespace llarp
if (!frame.Verify(si)) if (!frame.Verify(si))
return false; return false;
// remove convotag it doesn't exist // remove convotag it doesn't exist
LogWarn("remove convotag T=", frame.T, " R=", frame.R); LogWarn("remove convotag T=", frame.T, " R=", frame.R, " from ", si.Addr());
RemoveConvoTag(frame.T); RemoveConvoTag(frame.T);
return true; return true;
} }
if (not frame.AsyncDecryptAndVerify(Router()->loop(), p, m_Identity, this)) if (not frame.AsyncDecryptAndVerify(Router()->loop(), p, m_Identity, this))
{ {
LogError("Failed to decrypt protocol frame"); ResetConvoTag(frame.T, p, frame.F);
if (not frame.C.IsZero())
{
// send reset convo tag message
ProtocolFrame f;
f.R = 1;
f.T = frame.T;
f.F = p->intro.pathID;
f.Sign(m_Identity);
{
LogWarn("invalidating convotag T=", frame.T);
RemoveConvoTag(frame.T);
m_SendQueue.tryPushBack(
SendEvent_t{std::make_shared<routing::PathTransferMessage>(f, frame.F), p});
}
}
} }
return true; return true;
} }
@ -1279,8 +1279,8 @@ namespace llarp
{ {
m_router->routerProfiling().MarkPathTimeout(p.get()); m_router->routerProfiling().MarkPathTimeout(p.get());
ManualRebuild(1); ManualRebuild(1);
RegenAndPublishIntroSet();
path::Builder::HandlePathDied(p); path::Builder::HandlePathDied(p);
RegenAndPublishIntroSet();
} }
bool bool
@ -1294,23 +1294,66 @@ namespace llarp
const Address& addr, const Address& addr,
std::optional<IntroSet> introset, std::optional<IntroSet> introset,
const RouterID& endpoint, const RouterID& endpoint,
llarp_time_t timeLeft) llarp_time_t timeLeft,
uint64_t relayOrder)
{ {
// tell all our existing remote sessions about this introset update
const auto now = Router()->Now(); const auto now = Router()->Now();
auto& fails = m_state->m_ServiceLookupFails;
auto& lookups = m_state->m_PendingServiceLookups; auto& lookups = m_state->m_PendingServiceLookups;
if (introset)
{
auto& sessions = m_state->m_RemoteSessions;
auto range = sessions.equal_range(addr);
auto itr = range.first;
while (itr != range.second)
{
itr->second->OnIntroSetUpdate(addr, introset, endpoint, timeLeft, relayOrder);
// we got a successful lookup
if (itr->second->ReadyToSend() and not introset->IsExpired(now))
{
// inform all lookups
auto lookup_range = lookups.equal_range(addr);
auto i = lookup_range.first;
while (i != lookup_range.second)
{
i->second(addr, itr->second.get());
++i;
}
lookups.erase(addr);
}
++itr;
}
}
auto& fails = m_state->m_ServiceLookupFails;
if (not introset or introset->IsExpired(now)) if (not introset or introset->IsExpired(now))
{ {
LogError(Name(), " failed to lookup ", addr.ToString(), " from ", endpoint); LogError(
Name(),
" failed to lookup ",
addr.ToString(),
" from ",
endpoint,
" order=",
relayOrder);
fails[endpoint] = fails[endpoint] + 1; fails[endpoint] = fails[endpoint] + 1;
// inform one
const auto pendingForAddr = std::count_if(
m_state->m_PendingLookups.begin(),
m_state->m_PendingLookups.end(),
[addr](const auto& item) -> bool { return item.second->IsFor(addr); });
// inform all if we have no more pending lookups for this address
if (pendingForAddr == 0)
{
auto range = lookups.equal_range(addr); auto range = lookups.equal_range(addr);
auto itr = range.first; auto itr = range.first;
if (itr != range.second) while (itr != range.second)
{ {
itr->second(addr, nullptr); itr->second(addr, nullptr);
itr = lookups.erase(itr); itr = lookups.erase(itr);
} }
}
return false; return false;
} }
// check for established outbound context // check for established outbound context
@ -1323,9 +1366,10 @@ namespace llarp
} }
void void
Endpoint::MarkAddressOutbound(const Address& addr) Endpoint::MarkAddressOutbound(AddressVariant_t addr)
{ {
m_state->m_OutboundSessions.insert(addr); if (auto* ptr = std::get_if<Address>(&addr))
m_state->m_OutboundSessions.insert(*ptr);
} }
bool bool
@ -1334,15 +1378,38 @@ namespace llarp
return m_state->m_OutboundSessions.count(addr) > 0; return m_state->m_OutboundSessions.count(addr) > 0;
} }
void
Endpoint::InformPathToService(const Address remote, OutboundContext* ctx)
{
auto& serviceLookups = m_state->m_PendingServiceLookups;
auto range = serviceLookups.equal_range(remote);
auto itr = range.first;
while (itr != range.second)
{
itr->second(remote, ctx);
++itr;
}
serviceLookups.erase(remote);
}
bool bool
Endpoint::EnsurePathToService(const Address remote, PathEnsureHook hook, llarp_time_t timeout) Endpoint::EnsurePathToService(const Address remote, PathEnsureHook hook, llarp_time_t timeout)
{ {
if (not WantsOutboundSession(remote))
{
// we don't want to ensure paths to addresses that are inbound
// inform fail right away in that case
hook(remote, nullptr);
return false;
}
/// how many routers to use for lookups /// how many routers to use for lookups
static constexpr size_t NumParallelLookups = 2; static constexpr size_t NumParallelLookups = 2;
/// how many requests per router /// how many requests per router
static constexpr size_t RequestsPerLookup = 2; static constexpr size_t RequestsPerLookup = 2;
MarkAddressOutbound(remote); // add response hook to list for address.
m_state->m_PendingServiceLookups.emplace(remote, hook);
auto& sessions = m_state->m_RemoteSessions; auto& sessions = m_state->m_RemoteSessions;
{ {
@ -1352,20 +1419,17 @@ namespace llarp
{ {
if (itr->second->ReadyToSend()) if (itr->second->ReadyToSend())
{ {
hook(remote, itr->second.get()); InformPathToService(remote, itr->second.get());
return true; return true;
} }
++itr; ++itr;
} }
} }
// add response hook to list for address.
m_state->m_PendingServiceLookups.emplace(remote, hook);
/// check replay filter /// check replay filter
if (not m_IntrosetLookupFilter.Insert(remote)) if (not m_IntrosetLookupFilter.Insert(remote))
return true; return true;
const auto paths = GetManyPathsWithUniqueEndpoints(this, NumParallelLookups); const auto paths = GetManyPathsWithUniqueEndpoints(this, NumParallelLookups, remote.ToKey());
using namespace std::placeholders; using namespace std::placeholders;
const dht::Key_t location = remote.ToKey(); const dht::Key_t location = remote.ToKey();
@ -1381,15 +1445,15 @@ namespace llarp
{ {
HiddenServiceAddressLookup* job = new HiddenServiceAddressLookup( HiddenServiceAddressLookup* job = new HiddenServiceAddressLookup(
this, this,
[this](auto addr, auto result, auto from, auto left) { [this](auto addr, auto result, auto from, auto left, auto order) {
return OnLookup(addr, result, from, left); return OnLookup(addr, result, from, left, order);
}, },
location, location,
PubKey{remote.as_array()}, PubKey{remote.as_array()},
path->Endpoint(), path->Endpoint(),
order, order,
GenTXID(), GenTXID(),
timeout); timeout + (2 * path->intro.latency));
LogInfo( LogInfo(
"doing lookup for ", "doing lookup for ",
remote, remote,
@ -1425,14 +1489,8 @@ namespace llarp
bool bool
Endpoint::EnsurePathToSNode(const RouterID snode, SNodeEnsureHook h) Endpoint::EnsurePathToSNode(const RouterID snode, SNodeEnsureHook h)
{ {
static constexpr size_t MaxConcurrentSNodeSessions = 16;
auto& nodeSessions = m_state->m_SNodeSessions; auto& nodeSessions = m_state->m_SNodeSessions;
if (nodeSessions.size() >= MaxConcurrentSNodeSessions)
{
// a quick client side work arround before we do proper limiting
LogError(Name(), " has too many snode sessions");
return false;
}
using namespace std::placeholders; using namespace std::placeholders;
if (nodeSessions.count(snode) == 0) if (nodeSessions.count(snode) == 0)
{ {
@ -1615,6 +1673,14 @@ namespace llarp
if (session.inbound) if (session.inbound)
{ {
auto path = GetPathByRouter(session.replyIntro.router); auto path = GetPathByRouter(session.replyIntro.router);
// if we have no path to the remote router that's fine still use it just in case this
// is the ONLY one we have
if (path == nullptr)
{
ret = tag;
continue;
}
if (path and path->IsReady()) 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;
@ -1679,6 +1745,13 @@ namespace llarp
Loop()->call_soon([tag, hook]() { hook(tag); }); Loop()->call_soon([tag, hook]() { hook(tag); });
return true; return true;
} }
if (not WantsOutboundSession(*ptr))
{
// we don't want to connect back to inbound sessions
hook(std::nullopt);
return true;
}
return EnsurePathToService( return EnsurePathToService(
*ptr, *ptr,
[hook](auto, auto* ctx) { [hook](auto, auto* ctx) {
@ -1799,8 +1872,7 @@ namespace llarp
LogError("failed to encrypt and sign"); LogError("failed to encrypt and sign");
return; return;
} }
self->m_SendQueue.pushBack(SendEvent_t{transfer, p}); self->m_SendQueue.tryPushBack(SendEvent_t{transfer, p});
;
}); });
return true; return true;
} }
@ -1811,7 +1883,7 @@ namespace llarp
} }
else else
{ {
LogWarn("Have inbound convo but get-best returned none; bug?"); LogWarn("Have inbound convo from ", remote, " but get-best returned none; bug?");
} }
} }
@ -1883,10 +1955,13 @@ namespace llarp
bool bool
Endpoint::ShouldBuildMore(llarp_time_t now) const Endpoint::ShouldBuildMore(llarp_time_t now) const
{ {
if (not path::Builder::ShouldBuildMore(now)) if (BuildCooldownHit(now))
return false; return false;
return ((now - lastBuild) > path::intro_path_spread) const auto requiredPaths = std::max(numDesiredPaths, path::min_intro_paths);
|| NumInStatus(path::ePathEstablished) < path::min_intro_paths; if (NumInStatus(path::ePathBuilding) >= requiredPaths)
return false;
return NumPathsExistingAt(now + (path::default_lifetime - path::intro_path_spread))
< requiredPaths;
} }
AbstractRouter* AbstractRouter*

View File

@ -49,12 +49,13 @@ namespace llarp
struct OutboundContext; struct OutboundContext;
/// minimum interval for publishing introsets /// minimum interval for publishing introsets
static constexpr auto INTROSET_PUBLISH_INTERVAL = static constexpr auto IntrosetPublishInterval = path::intro_path_spread / 2;
std::chrono::milliseconds(path::default_lifetime) / 4;
static constexpr auto INTROSET_PUBLISH_RETRY_INTERVAL = 5s; /// how agressively should we retry publishing introset on failure
static constexpr auto IntrosetPublishRetryCooldown = 1s;
static constexpr auto INTROSET_LOOKUP_RETRY_COOLDOWN = 3s; /// how aggressively should we retry looking up introsets
static constexpr auto IntrosetLookupCooldown = 250ms;
struct Endpoint : public path::Builder, struct Endpoint : public path::Builder,
public ILookupHolder, public ILookupHolder,
@ -284,8 +285,8 @@ namespace llarp
bool bool
WantsOutboundSession(const Address&) const override; WantsOutboundSession(const Address&) const override;
void /// this MUST be called if you want to call EnsurePathTo on the given address
MarkAddressOutbound(const Address&) override; void MarkAddressOutbound(AddressVariant_t) override;
bool bool
ShouldBundleRC() const override ShouldBundleRC() const override
@ -330,6 +331,9 @@ namespace llarp
using SNodeEnsureHook = std::function<void(const RouterID, exit::BaseSession_ptr, ConvoTag)>; using SNodeEnsureHook = std::function<void(const RouterID, exit::BaseSession_ptr, ConvoTag)>;
void
InformPathToService(const Address remote, OutboundContext* ctx);
/// ensure a path to a service node by public key /// ensure a path to a service node by public key
bool bool
EnsurePathToSNode(const RouterID remote, SNodeEnsureHook h); EnsurePathToSNode(const RouterID remote, SNodeEnsureHook h);
@ -415,6 +419,9 @@ namespace llarp
uint64_t uint64_t
GenTXID(); GenTXID();
void
ResetConvoTag(ConvoTag tag, path::Path_ptr path, PathID_t from);
const std::set<RouterID>& const std::set<RouterID>&
SnodeBlacklist() const; SnodeBlacklist() const;
@ -471,7 +478,8 @@ namespace llarp
const service::Address& addr, const service::Address& addr,
std::optional<IntroSet> i, std::optional<IntroSet> i,
const RouterID& endpoint, const RouterID& endpoint,
llarp_time_t timeLeft); llarp_time_t timeLeft,
uint64_t relayOrder);
bool bool
DoNetworkIsolation(bool failed); DoNetworkIsolation(bool failed);

View File

@ -37,6 +37,7 @@ namespace llarp
void void
EndpointUtil::ExpirePendingTx(llarp_time_t now, PendingLookups& lookups) EndpointUtil::ExpirePendingTx(llarp_time_t now, PendingLookups& lookups)
{ {
std::vector<std::unique_ptr<IServiceLookup>> timedout;
for (auto itr = lookups.begin(); itr != lookups.end();) for (auto itr = lookups.begin(); itr != lookups.end();)
{ {
if (!itr->second->IsTimedOut(now)) if (!itr->second->IsTimedOut(now))
@ -44,11 +45,14 @@ namespace llarp
++itr; ++itr;
continue; continue;
} }
std::unique_ptr<IServiceLookup> lookup = std::move(itr->second); timedout.emplace_back(std::move(itr->second));
itr = lookups.erase(itr);
}
for (const auto& lookup : timedout)
{
LogWarn(lookup->name, " timed out txid=", lookup->txid); LogWarn(lookup->name, " timed out txid=", lookup->txid);
lookup->HandleTimeout(); lookup->HandleTimeout();
itr = lookups.erase(itr);
} }
} }
@ -95,7 +99,11 @@ namespace llarp
itr->second->Tick(now); itr->second->Tick(now);
if (itr->second->Pump(now)) if (itr->second->Pump(now))
{ {
LogInfo("marking session as dead T=", itr->second->currentConvoTag); LogInfo(
"marking session as dead T=",
itr->second->currentConvoTag,
" to ",
itr->second->Addr());
itr->second->Stop(); itr->second->Stop();
sessions.erase(itr->second->currentConvoTag); sessions.erase(itr->second->currentConvoTag);
deadSessions.emplace(std::move(*itr)); deadSessions.emplace(std::move(*itr));
@ -106,6 +114,10 @@ namespace llarp
++itr; ++itr;
} }
} }
for (auto& item : deadSessions)
{
item.second->Tick(now);
}
} }
void void
@ -116,7 +128,7 @@ namespace llarp
{ {
if (itr->second.IsExpired(now)) if (itr->second.IsExpired(now))
{ {
LogInfo("Expire session T=", itr->first); LogInfo("Expire session T=", itr->first, " to ", itr->second.Addr());
itr = sessions.erase(itr); itr = sessions.erase(itr);
} }
else else

View File

@ -43,15 +43,31 @@ namespace llarp
template <typename Endpoint_t> template <typename Endpoint_t>
static path::Path::UniqueEndpointSet_t static path::Path::UniqueEndpointSet_t
GetManyPathsWithUniqueEndpoints(Endpoint_t* ep, size_t N, size_t tries = 10) GetManyPathsWithUniqueEndpoints(
Endpoint_t* ep,
size_t N,
std::optional<dht::Key_t> maybeLocation = std::nullopt,
size_t tries = 10)
{ {
std::unordered_set<RouterID> exclude;
path::Path::UniqueEndpointSet_t paths; path::Path::UniqueEndpointSet_t paths;
do do
{ {
--tries; --tries;
const auto path = ep->PickRandomEstablishedPath(); path::Path_ptr path;
if (maybeLocation)
{
path = ep->GetEstablishedPathClosestTo(RouterID{maybeLocation->as_array()}, exclude);
}
else
{
path = ep->PickRandomEstablishedPath();
}
if (path and path->IsReady()) if (path and path->IsReady())
{
paths.emplace(path); paths.emplace(path);
exclude.insert(path->Endpoint());
}
} while (tries > 0 and paths.size() < N); } while (tries > 0 and paths.size() < N);
return paths; return paths;
} }

View File

@ -77,10 +77,6 @@ namespace llarp
/// do we want a session outbound to addr /// do we want a session outbound to addr
virtual bool virtual bool
WantsOutboundSession(const Address& addr) const = 0; WantsOutboundSession(const Address& addr) const = 0;
virtual void
MarkAddressOutbound(const Address& addr) = 0;
virtual void virtual void
QueueRecvData(RecvDataEvent ev) = 0; QueueRecvData(RecvDataEvent ev) = 0;
}; };

View File

@ -46,7 +46,7 @@ namespace llarp
found = *maybe; found = *maybe;
} }
} }
return handle(remote, found, endpoint, TimeLeft(time_now_ms())); return handle(remote, found, endpoint, TimeLeft(time_now_ms()), relayOrder);
} }
std::shared_ptr<routing::IMessage> std::shared_ptr<routing::IMessage>

View File

@ -15,7 +15,7 @@ namespace llarp
uint64_t relayOrder; uint64_t relayOrder;
const dht::Key_t location; const dht::Key_t location;
using HandlerFunc = std::function<bool( using HandlerFunc = std::function<bool(
const Address&, std::optional<IntroSet>, const RouterID&, llarp_time_t)>; const Address&, std::optional<IntroSet>, const RouterID&, llarp_time_t, uint64_t)>;
HandlerFunc handle; HandlerFunc handle;
HiddenServiceAddressLookup( HiddenServiceAddressLookup(
@ -30,6 +30,16 @@ namespace llarp
~HiddenServiceAddressLookup() override = default; ~HiddenServiceAddressLookup() override = default;
virtual bool
IsFor(EndpointBase::AddressVariant_t addr) const override
{
if (const auto* ptr = std::get_if<Address>(&addr))
{
return Address{rootkey} == *ptr;
}
return false;
}
bool bool
HandleIntrosetResponse(const std::set<EncryptedIntroSet>& results) override; HandleIntrosetResponse(const std::set<EncryptedIntroSet>& results) override;

View File

@ -9,6 +9,7 @@ namespace llarp
{ {
util::StatusObject obj{ util::StatusObject obj{
{"router", router.ToHex()}, {"router", router.ToHex()},
{"path", pathID.ToHex()},
{"expiresAt", to_json(expiresAt)}, {"expiresAt", to_json(expiresAt)},
{"latency", to_json(latency)}, {"latency", to_json(latency)},
{"version", uint64_t(version)}}; {"version", uint64_t(version)}};
@ -66,8 +67,9 @@ namespace llarp
std::ostream& std::ostream&
Introduction::print(std::ostream& stream, int level, int spaces) const Introduction::print(std::ostream& stream, int level, int spaces) const
{ {
const RouterID r{router};
Printer printer(stream, level, spaces); Printer printer(stream, level, spaces);
printer.printAttribute("k", RouterID(router)); printer.printAttribute("k", r.ToString());
printer.printAttribute("l", latency.count()); printer.printAttribute("l", latency.count());
printer.printAttribute("p", pathID); printer.printAttribute("p", pathID);
printer.printAttribute("v", version); printer.printAttribute("v", version);

View File

@ -77,6 +77,16 @@ namespace llarp
{ {
return i.print(out, -1, -1); return i.print(out, -1, -1);
} }
/// comparator for introset timestamp
struct CompareIntroTimestamp
{
bool
operator()(const Introduction& left, const Introduction& right) const
{
return left.expiresAt > right.expiresAt;
}
};
} // namespace service } // namespace service
} // namespace llarp } // namespace llarp

View File

@ -351,6 +351,15 @@ namespace llarp::service
return false; return false;
} }
bool
IntroSet::HasStaleIntros(llarp_time_t now, llarp_time_t delta) const
{
for (const auto& intro : intros)
if (intro.ExpiresSoon(now, delta))
return true;
return false;
}
bool bool
IntroSet::IsExpired(llarp_time_t now) const IntroSet::IsExpired(llarp_time_t now) const
{ {

View File

@ -69,6 +69,10 @@ namespace llarp
bool bool
HasExpiredIntros(llarp_time_t now) const; HasExpiredIntros(llarp_time_t now) const;
/// return true if any of our intros expires soon given a delta
bool
HasStaleIntros(llarp_time_t now, llarp_time_t delta) const;
bool bool
IsExpired(llarp_time_t now) const; IsExpired(llarp_time_t now) const;

View File

@ -4,6 +4,8 @@
#include "intro_set.hpp" #include "intro_set.hpp"
#include <llarp/path/pathset.hpp> #include <llarp/path/pathset.hpp>
#include <llarp/endpoint_base.hpp>
#include <set> #include <set>
namespace llarp namespace llarp
@ -73,6 +75,12 @@ namespace llarp
const std::string name; const std::string name;
RouterID endpoint; RouterID endpoint;
/// return true if this lookup is for a remote address
virtual bool IsFor(EndpointBase::AddressVariant_t) const
{
return false;
}
util::StatusObject util::StatusObject
ExtractStatus() const ExtractStatus() const
{ {

View File

@ -48,6 +48,7 @@ namespace llarp
MarkCurrentIntroBad(Now()); MarkCurrentIntroBad(Now());
ShiftIntroduction(false); ShiftIntroduction(false);
UpdateIntroSet(); UpdateIntroSet();
SwapIntros();
} }
return true; return true;
} }
@ -58,17 +59,22 @@ namespace llarp
: path::Builder{parent->Router(), OutboundContextNumPaths, 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()}
, addr{introset.addressKeys.Addr()}
, currentIntroSet{introset} , currentIntroSet{introset}
{ {
updatingIntroSet = false; updatingIntroSet = false;
for (const auto& intro : introset.intros) for (const auto& intro : introset.intros)
{ {
if (intro.expiresAt > m_NextIntro.expiresAt) if (m_NextIntro.latency == 0s or m_NextIntro.latency > intro.latency)
m_NextIntro = intro; m_NextIntro = intro;
} }
currentConvoTag.Randomize(); currentConvoTag.Randomize();
lastShift = Now();
// add send and connect timeouts to the parent endpoints path alignment timeout
// this will make it so that there is less of a chance for timing races
sendTimeout += parent->PathAlignmentTimeout();
connectTimeout += parent->PathAlignmentTimeout();
} }
OutboundContext::~OutboundContext() = default; OutboundContext::~OutboundContext() = default;
@ -82,12 +88,28 @@ namespace llarp
remoteIntro = m_NextIntro; remoteIntro = m_NextIntro;
m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.addressKeys, false); m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.addressKeys, false);
m_DataHandler->PutIntroFor(currentConvoTag, remoteIntro); m_DataHandler->PutIntroFor(currentConvoTag, remoteIntro);
ShiftIntroRouter(m_NextIntro.router);
// if we have not made a handshake to the remote endpoint do so
if (not IntroGenerated())
{
KeepAlive();
} }
} }
}
Address
OutboundContext::Addr() const
{
return addr;
}
bool bool
OutboundContext::OnIntroSetUpdate( OutboundContext::OnIntroSetUpdate(
const Address&, std::optional<IntroSet> foundIntro, const RouterID& endpoint, llarp_time_t) const Address&,
std::optional<IntroSet> foundIntro,
const RouterID& endpoint,
llarp_time_t,
uint64_t relayOrder)
{ {
if (markedBad) if (markedBad)
return true; return true;
@ -112,8 +134,9 @@ namespace llarp
return true; return true;
} }
currentIntroSet = *foundIntro; currentIntroSet = *foundIntro;
ShiftIntroRouter(RouterID{});
} }
else else if (relayOrder > 0)
{ {
++m_LookupFails; ++m_LookupFails;
LogWarn(Name(), " failed to look up introset, fails=", m_LookupFails); LogWarn(Name(), " failed to look up introset, fails=", m_LookupFails);
@ -128,7 +151,7 @@ namespace llarp
return false; return false;
if (remoteIntro.router.IsZero()) if (remoteIntro.router.IsZero())
return false; return false;
return GetPathByRouter(remoteIntro.router) != nullptr; return IntroSent();
} }
void void
@ -145,8 +168,8 @@ namespace llarp
} }
if (selectedIntro.router.IsZero() || selectedIntro.ExpiresSoon(now)) if (selectedIntro.router.IsZero() || selectedIntro.ExpiresSoon(now))
return; return;
LogWarn(Name(), " shfiting intro off of ", r, " to ", RouterID(selectedIntro.router));
m_NextIntro = selectedIntro; m_NextIntro = selectedIntro;
lastShift = now;
} }
void void
@ -177,7 +200,7 @@ namespace llarp
p->SetDataHandler(util::memFn(&OutboundContext::HandleHiddenServiceFrame, this)); p->SetDataHandler(util::memFn(&OutboundContext::HandleHiddenServiceFrame, this));
p->SetDropHandler(util::memFn(&OutboundContext::HandleDataDrop, this)); p->SetDropHandler(util::memFn(&OutboundContext::HandleDataDrop, this));
// we now have a path to the next intro, swap intros // we now have a path to the next intro, swap intros
if (p->Endpoint() == m_NextIntro.router or p->Endpoint() == remoteIntro.router) if (p->Endpoint() == m_NextIntro.router)
SwapIntros(); SwapIntros();
else else
{ {
@ -188,29 +211,25 @@ namespace llarp
void void
OutboundContext::AsyncGenIntro(const llarp_buffer_t& payload, ProtocolType t) OutboundContext::AsyncGenIntro(const llarp_buffer_t& payload, ProtocolType t)
{ {
if (sentIntro) if (generatedIntro)
{
LogWarn(Name(), " dropping packet as we are not fully handshaked right now");
return; return;
}
if (remoteIntro.router.IsZero()) if (remoteIntro.router.IsZero())
{ {
LogWarn(Name(), " dropping intro frame we have no intro ready yet"); LogWarn(Name(), " dropping intro frame we have no intro ready yet");
return; return;
} }
auto path = m_PathSet->GetPathByRouter(remoteIntro.router); auto path = GetPathByRouter(remoteIntro.router);
if (path == nullptr) if (path == nullptr)
{ {
// try parent as fallback LogError(Name(), " has no path to ", remoteIntro.router, " when we should have had one");
path = m_Endpoint->GetPathByRouter(remoteIntro.router);
if (path == nullptr)
{
if (!BuildCooldownHit(Now()))
BuildOneAlignedTo(remoteIntro.router);
LogWarn(Name(), " dropping intro frame, no path to ", remoteIntro.router);
return; return;
} }
}
sentIntro = true;
auto frame = std::make_shared<ProtocolFrame>(); auto frame = std::make_shared<ProtocolFrame>();
frame->Clear();
auto ex = std::make_shared<AsyncKeyExchange>( auto ex = std::make_shared<AsyncKeyExchange>(
m_Endpoint->Loop(), m_Endpoint->Loop(),
remoteIdent, remoteIdent,
@ -222,22 +241,29 @@ namespace llarp
t); t);
ex->hook = [self = shared_from_this(), path](auto frame) { ex->hook = [self = shared_from_this(), path](auto frame) {
self->Send(std::move(frame), path); if (not self->Send(std::move(frame), path))
return;
self->m_Endpoint->Loop()->call_later(
self->remoteIntro.latency, [self]() { self->sentIntro = true; });
}; };
ex->msg.PutBuffer(payload); ex->msg.PutBuffer(payload);
ex->msg.introReply = path->intro; ex->msg.introReply = path->intro;
frame->F = ex->msg.introReply.pathID; frame->F = ex->msg.introReply.pathID;
frame->R = 0; frame->R = 0;
generatedIntro = true;
// ensure we have a sender put for this convo tag
m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.addressKeys, false);
// encrypt frame async
m_Endpoint->Router()->QueueWork([ex, frame] { return AsyncKeyExchange::Encrypt(ex, frame); }); m_Endpoint->Router()->QueueWork([ex, frame] { return AsyncKeyExchange::Encrypt(ex, frame); });
LogInfo("send intro frame");
LogInfo(Name(), " send intro frame T=", currentConvoTag);
} }
std::string std::string
OutboundContext::Name() const OutboundContext::Name() const
{ {
return "OBContext:" + m_Endpoint->Name() + "-" return "OBContext:" + currentIntroSet.addressKeys.Addr().ToString();
+ currentIntroSet.addressKeys.Addr().ToString();
} }
void void
@ -249,10 +275,9 @@ namespace llarp
return; return;
LogInfo(Name(), " updating introset"); LogInfo(Name(), " updating introset");
m_LastIntrosetUpdateAt = now; m_LastIntrosetUpdateAt = now;
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
const auto paths = GetManyPathsWithUniqueEndpoints(m_Endpoint, 2); const auto paths = GetManyPathsWithUniqueEndpoints(m_Endpoint, 2, location);
uint64_t relayOrder = 0; uint64_t relayOrder = 0;
for (const auto& path : paths) for (const auto& path : paths)
{ {
@ -264,7 +289,7 @@ namespace llarp
path->Endpoint(), path->Endpoint(),
relayOrder, relayOrder,
m_Endpoint->GenTXID(), m_Endpoint->GenTXID(),
5s); (IntrosetUpdateInterval / 2) + (2 * path->intro.latency));
relayOrder++; relayOrder++;
if (job->SendRequestViaPath(path, m_Endpoint->Router())) if (job->SendRequestViaPath(path, m_Endpoint->Router()))
updatingIntroSet = true; updatingIntroSet = true;
@ -280,13 +305,15 @@ namespace llarp
obj["remoteIntro"] = remoteIntro.ExtractStatus(); obj["remoteIntro"] = remoteIntro.ExtractStatus();
obj["sessionCreatedAt"] = to_json(createdAt); obj["sessionCreatedAt"] = to_json(createdAt);
obj["lastGoodSend"] = to_json(lastGoodSend); obj["lastGoodSend"] = to_json(lastGoodSend);
obj["lastRecv"] = to_json(m_LastInboundTraffic);
obj["lastIntrosetUpdate"] = to_json(m_LastIntrosetUpdateAt);
obj["seqno"] = sequenceNo; obj["seqno"] = sequenceNo;
obj["markedBad"] = markedBad; obj["markedBad"] = markedBad;
obj["lastShift"] = to_json(lastShift); obj["lastShift"] = to_json(lastShift);
obj["remoteIdentity"] = remoteIdent.Addr().ToString(); obj["remoteIdentity"] = addr.ToString();
obj["currentRemoteIntroset"] = currentIntroSet.ExtractStatus(); obj["currentRemoteIntroset"] = currentIntroSet.ExtractStatus();
obj["nextIntro"] = m_NextIntro.ExtractStatus(); obj["nextIntro"] = m_NextIntro.ExtractStatus();
obj["readyToSend"] = ReadyToSend();
std::transform( std::transform(
m_BadIntros.begin(), m_BadIntros.begin(),
m_BadIntros.end(), m_BadIntros.end(),
@ -306,36 +333,31 @@ namespace llarp
bool bool
OutboundContext::Pump(llarp_time_t now) OutboundContext::Pump(llarp_time_t now)
{ {
// we are probably dead af
if (m_LookupFails > 16 || m_BuildFails > 10)
return true;
constexpr auto InboundTrafficTimeout = 5s;
if (ReadyToSend() and remoteIntro.router.IsZero()) if (ReadyToSend() and remoteIntro.router.IsZero())
{ {
SwapIntros(); SwapIntros();
} }
if (m_GotInboundTraffic and m_LastInboundTraffic + InboundTrafficTimeout <= now) if ((remoteIntro.router.IsZero() or m_BadIntros.count(remoteIntro))
{ and GetPathByRouter(m_NextIntro.router))
if (std::chrono::abs(now - lastGoodSend) < InboundTrafficTimeout) SwapIntros();
if (m_GotInboundTraffic and m_LastInboundTraffic + sendTimeout <= now)
{ {
// timeout on other side // timeout on other side
UpdateIntroSet();
MarkCurrentIntroBad(now); MarkCurrentIntroBad(now);
ShiftIntroRouter(remoteIntro.router);
} }
} // check for stale intros
// update the introset if we think we need to
// check for expiration if (currentIntroSet.HasStaleIntros(now, path::intro_path_spread))
if (remoteIntro.ExpiresSoon(now))
{ {
UpdateIntroSet(); UpdateIntroSet();
// shift intro if it expires "soon"
if (ShiftIntroduction())
SwapIntros(); // swap intros if we shifted
} }
// lookup router in intro if set and unknown // lookup router in intro if set and unknown
m_Endpoint->EnsureRouterIsKnown(remoteIntro.router); if (not m_NextIntro.router.IsZero())
m_Endpoint->EnsureRouterIsKnown(m_NextIntro.router);
// expire bad intros // expire bad intros
auto itr = m_BadIntros.begin(); auto itr = m_BadIntros.begin();
while (itr != m_BadIntros.end()) while (itr != m_BadIntros.end())
@ -346,7 +368,7 @@ namespace llarp
++itr; ++itr;
} }
if (ReadyToSend() and m_ReadyHook) if (ReadyToSend() and not m_ReadyHooks.empty())
{ {
const auto path = GetPathByRouter(remoteIntro.router); const auto path = GetPathByRouter(remoteIntro.router);
if (not path) if (not path)
@ -354,33 +376,41 @@ namespace llarp
LogWarn(Name(), " ready but no path to ", remoteIntro.router, " ???"); LogWarn(Name(), " ready but no path to ", remoteIntro.router, " ???");
return true; return true;
} }
m_ReadyHook(this); for (const auto& hook : m_ReadyHooks)
m_ReadyHook = nullptr; hook(this);
m_ReadyHooks.clear();
} }
if (lastGoodSend > 0s and now >= lastGoodSend + (sendTimeout / 2)) const auto timeout = std::max(lastGoodSend, m_LastInboundTraffic);
if (lastGoodSend > 0s and now >= timeout + (sendTimeout / 2))
{ {
// send a keep alive to keep this session alive // send a keep alive to keep this session alive
KeepAlive(); KeepAlive();
} }
// if we are dead return true so we are removed // if we are dead return true so we are removed
return lastGoodSend > 0s ? (now >= lastGoodSend && now - lastGoodSend > sendTimeout) return timeout > 0s ? (now >= timeout && now - timeout > sendTimeout)
: (now >= createdAt && now - createdAt > connectTimeout); : (now >= createdAt && now - createdAt > connectTimeout);
} }
void void
OutboundContext::SetReadyHook(std::function<void(OutboundContext*)> hook, llarp_time_t timeout) OutboundContext::AddReadyHook(std::function<void(OutboundContext*)> hook, llarp_time_t timeout)
{ {
if (m_ReadyHook) if (ReadyToSend())
{
hook(this);
return; return;
m_ReadyHook = hook; }
if (m_ReadyHooks.empty())
{
m_router->loop()->call_later(timeout, [this]() { m_router->loop()->call_later(timeout, [this]() {
if (m_ReadyHook) LogWarn(Name(), " did not obtain session in time");
m_ReadyHook(nullptr); for (const auto& hook : m_ReadyHooks)
m_ReadyHook = nullptr; hook(nullptr);
m_ReadyHooks.clear();
}); });
} }
m_ReadyHooks.push_back(hook);
}
std::optional<std::vector<RouterContact>> std::optional<std::vector<RouterContact>>
OutboundContext::GetHopsForBuild() OutboundContext::GetHopsForBuild()
@ -397,16 +427,19 @@ namespace llarp
bool bool
OutboundContext::ShouldBuildMore(llarp_time_t now) const OutboundContext::ShouldBuildMore(llarp_time_t now) const
{ {
if (markedBad || not path::Builder::ShouldBuildMore(now)) if (markedBad or path::Builder::BuildCooldownHit(now))
return false; return false;
if (NumInStatus(path::ePathBuilding) >= numDesiredPaths) if (NumInStatus(path::ePathBuilding) >= numDesiredPaths)
return false; return false;
llarp_time_t t = 0s;
ForEachPath([&t](path::Path_ptr path) { size_t numValidPaths = 0;
if (path->IsReady()) ForEachPath([now, &numValidPaths](path::Path_ptr path) {
t = std::max(path->ExpireTime(), t); if (not path->IsReady())
return;
if (not path->intro.ExpiresSoon(now, path::default_lifetime - path::intro_path_spread))
numValidPaths++;
}); });
return t >= now + path::default_lifetime / 4; return numValidPaths < numDesiredPaths;
} }
void void
@ -422,12 +455,24 @@ namespace llarp
m_BadIntros[intro] = now; m_BadIntros[intro] = now;
} }
bool
OutboundContext::IntroSent() const
{
return sentIntro;
}
bool
OutboundContext::IntroGenerated() const
{
return generatedIntro;
}
bool bool
OutboundContext::ShiftIntroduction(bool rebuild) OutboundContext::ShiftIntroduction(bool rebuild)
{ {
bool success = false; bool success = false;
auto now = Now(); const auto now = Now();
if (now - lastShift < MIN_SHIFT_INTERVAL) if (abs(now - lastShift) < shiftTimeout)
return false; return false;
bool shifted = false; bool shifted = false;
std::vector<Introduction> intros = currentIntroSet.intros; std::vector<Introduction> intros = currentIntroSet.intros;
@ -488,7 +533,7 @@ namespace llarp
{ {
// unconditionally update introset // unconditionally update introset
UpdateIntroSet(); UpdateIntroSet();
const RouterID endpoint(path->Endpoint()); const RouterID endpoint{path->Endpoint()};
// if a path to our current intro died... // if a path to our current intro died...
if (endpoint == remoteIntro.router) if (endpoint == remoteIntro.router)
{ {
@ -498,49 +543,12 @@ namespace llarp
if (p->Endpoint() == endpoint && p->IsReady()) if (p->Endpoint() == endpoint && p->IsReady())
++num; ++num;
}); });
// if we have more than two then we are probably fine if (num == 0)
if (num > 2)
return;
// if we have one working one ...
if (num == 1)
{ {
num = 0; // we have no more paths to this endpoint so we want to pivot off of it
ForEachPath([&](const path::Path_ptr& p) { MarkCurrentIntroBad(Now());
if (p->Endpoint() == endpoint) ShiftIntroRouter(endpoint);
++num; if (m_NextIntro.router != endpoint)
});
// if we have 2 or more established or pending don't do anything
if (num > 2)
return;
BuildOneAlignedTo(endpoint);
}
else if (num == 0)
{
// we have no paths to this router right now
// hop off it
Introduction picked;
// get the latest intro that isn't on that endpoint
for (const auto& intro : currentIntroSet.intros)
{
if (intro.router == endpoint)
continue;
if (intro.expiresAt > picked.expiresAt)
picked = intro;
}
// we got nothing
if (picked.router.IsZero())
{
return;
}
m_NextIntro = picked;
// check if we have a path to this router
num = 0;
ForEachPath([&](const path::Path_ptr& p) {
// don't count timed out paths
if (p->Status() != path::ePathTimeout && p->Endpoint() == m_NextIntro.router)
++num;
});
// build a path if one isn't already pending build or established
BuildOneAlignedTo(m_NextIntro.router); BuildOneAlignedTo(m_NextIntro.router);
} }
} }

View File

@ -38,6 +38,15 @@ namespace llarp
return shared_from_this(); return shared_from_this();
} }
std::weak_ptr<path::PathSet>
GetWeak() override
{
return weak_from_this();
}
Address
Addr() const;
bool bool
Stop() override; Stop() override;
@ -57,7 +66,7 @@ namespace llarp
/// shift the intro off the current router it is using /// shift the intro off the current router it is using
void void
ShiftIntroRouter(const RouterID remote); ShiftIntroRouter(const RouterID remote) override;
/// mark the current remote intro as bad /// mark the current remote intro as bad
void void
@ -71,7 +80,7 @@ namespace llarp
ReadyToSend() const; ReadyToSend() const;
void void
SetReadyHook(std::function<void(OutboundContext*)> readyHook, llarp_time_t timeout); AddReadyHook(std::function<void(OutboundContext*)> readyHook, llarp_time_t timeout);
/// for exits /// for exits
void void
@ -129,19 +138,26 @@ namespace llarp
llarp_time_t llarp_time_t
RTT() const; RTT() const;
bool
OnIntroSetUpdate(
const Address& addr,
std::optional<IntroSet> i,
const RouterID& endpoint,
llarp_time_t,
uint64_t relayOrder);
private: private:
/// swap remoteIntro with next intro /// swap remoteIntro with next intro
void void
SwapIntros(); SwapIntros();
void
OnGeneratedIntroFrame(AsyncKeyExchange* k, PathID_t p);
bool bool
OnIntroSetUpdate( IntroGenerated() const override;
const Address& addr, std::optional<IntroSet> i, const RouterID& endpoint, llarp_time_t); bool
IntroSent() const override;
const dht::Key_t location; const dht::Key_t location;
const Address addr;
uint64_t m_UpdateIntrosetTX = 0; uint64_t m_UpdateIntrosetTX = 0;
IntroSet currentIntroSet; IntroSet currentIntroSet;
Introduction m_NextIntro; Introduction m_NextIntro;
@ -151,8 +167,9 @@ namespace llarp
uint16_t m_BuildFails = 0; uint16_t m_BuildFails = 0;
llarp_time_t m_LastInboundTraffic = 0s; llarp_time_t m_LastInboundTraffic = 0s;
bool m_GotInboundTraffic = false; bool m_GotInboundTraffic = false;
bool generatedIntro = false;
bool sentIntro = false; bool sentIntro = false;
std::function<void(OutboundContext*)> m_ReadyHook; std::vector<std::function<void(OutboundContext*)>> m_ReadyHooks;
llarp_time_t m_LastIntrosetUpdateAt = 0s; llarp_time_t m_LastIntrosetUpdateAt = 0s;
}; };
} // namespace service } // namespace service

View File

@ -458,15 +458,21 @@ namespace llarp
} }
}; };
handler->Router()->QueueWork( handler->Router()->QueueWork(
[v, msg = std::move(msg), recvPath = std::move(recvPath), callback]() { [v, msg = std::move(msg), recvPath = std::move(recvPath), callback, handler]() {
auto resetTag = [handler, tag = v->frame.T, from = v->frame.F, path = recvPath]() {
handler->ResetConvoTag(tag, path, from);
};
if (not v->frame.Verify(v->si)) if (not v->frame.Verify(v->si))
{ {
LogError("Signature failure from ", v->si.Addr()); LogError("Signature failure from ", v->si.Addr());
handler->Loop()->call_soon(resetTag);
return; return;
} }
if (not v->frame.DecryptPayloadInto(v->shared, *msg)) if (not v->frame.DecryptPayloadInto(v->shared, *msg))
{ {
LogError("failed to decrypt message"); LogError("failed to decrypt message from ", v->si.Addr());
handler->Loop()->call_soon(resetTag);
return; return;
} }
callback(msg); callback(msg);

View File

@ -96,7 +96,7 @@ namespace llarp
version = other.version; version = other.version;
} }
ProtocolFrame() : routing::IMessage() ProtocolFrame() : routing::IMessage{}
{ {
Clear(); Clear();
} }

View File

@ -21,18 +21,19 @@ namespace llarp
, m_Endpoint(ep) , m_Endpoint(ep)
, createdAt(ep->Now()) , createdAt(ep->Now())
, m_SendQueue(SendContextQueueSize) , m_SendQueue(SendContextQueueSize)
{} {
m_FlushWakeup = ep->Loop()->make_waker([this] { FlushUpstream(); });
}
bool bool
SendContext::Send(std::shared_ptr<ProtocolFrame> msg, path::Path_ptr path) SendContext::Send(std::shared_ptr<ProtocolFrame> msg, path::Path_ptr path)
{ {
if (m_SendQueue.empty() or m_SendQueue.full()) if (not path->IsReady())
{ return false;
m_Endpoint->Loop()->call([this] { FlushUpstream(); }); m_FlushWakeup->Trigger();
} return m_SendQueue.tryPushBack(std::make_pair(
m_SendQueue.pushBack(std::make_pair( std::make_shared<routing::PathTransferMessage>(*msg, remoteIntro.pathID), path))
std::make_shared<routing::PathTransferMessage>(*msg, remoteIntro.pathID), path)); == thread::QueueReturn::Success;
return true;
} }
void void
@ -84,13 +85,15 @@ namespace llarp
auto path = m_PathSet->GetPathByRouter(remoteIntro.router); auto path = m_PathSet->GetPathByRouter(remoteIntro.router);
if (!path) if (!path)
{ {
LogWarn(m_Endpoint->Name(), " cannot encrypt and send: no path for intro ", remoteIntro); ShiftIntroRouter(remoteIntro.router);
LogWarn(m_PathSet->Name(), " cannot encrypt and send: no path for intro ", remoteIntro);
return; return;
} }
if (!m_DataHandler->GetCachedSessionKeyFor(f->T, shared)) if (!m_DataHandler->GetCachedSessionKeyFor(f->T, shared))
{ {
LogWarn(m_Endpoint->Name(), " has no cached session key on session T=", f->T); LogWarn(
m_PathSet->Name(), " could not send, has no cached session key on session T=", f->T);
return; return;
} }
@ -104,7 +107,7 @@ namespace llarp
} }
else else
{ {
LogWarn(m_Endpoint->Name(), " no session T=", f->T); LogWarn(m_PathSet->Name(), " could not get sequence number for session T=", f->T);
return; return;
} }
m->introReply = path->intro; m->introReply = path->intro;
@ -115,7 +118,7 @@ namespace llarp
m_Endpoint->Router()->QueueWork([f, m, shared, path, this] { m_Endpoint->Router()->QueueWork([f, m, shared, path, this] {
if (not f->EncryptAndSign(*m, shared, m_Endpoint->GetIdentity())) if (not f->EncryptAndSign(*m, shared, m_Endpoint->GetIdentity()))
{ {
LogError(m_Endpoint->Name(), " failed to sign message"); LogError(m_PathSet->Name(), " failed to sign message");
return; return;
} }
Send(f, path); Send(f, path);
@ -140,11 +143,21 @@ namespace llarp
void void
SendContext::AsyncEncryptAndSendTo(const llarp_buffer_t& data, ProtocolType protocol) SendContext::AsyncEncryptAndSendTo(const llarp_buffer_t& data, ProtocolType protocol)
{ {
if (lastGoodSend != 0s) if (IntroSent())
{ {
EncryptAndSendTo(data, protocol); EncryptAndSendTo(data, protocol);
return; return;
} }
// have we generated the initial intro but not sent it yet? bail here so we don't cause
// bullshittery
if (IntroGenerated() and not IntroSent())
{
LogWarn(
m_PathSet->Name(),
" we have generated an intial handshake but have not sent it yet so we drop a packet "
"to prevent bullshittery");
return;
}
const auto maybe = m_Endpoint->MaybeGetAuthInfoForEndpoint(remoteIdent.Addr()); const auto maybe = m_Endpoint->MaybeGetAuthInfoForEndpoint(remoteIdent.Addr());
if (maybe.has_value()) if (maybe.has_value())
{ {

View File

@ -44,8 +44,9 @@ namespace llarp
uint64_t sequenceNo = 0; uint64_t sequenceNo = 0;
llarp_time_t lastGoodSend = 0s; llarp_time_t lastGoodSend = 0s;
const llarp_time_t createdAt; const llarp_time_t createdAt;
llarp_time_t sendTimeout = 40s; llarp_time_t sendTimeout = path::build_timeout;
llarp_time_t connectTimeout = 60s; llarp_time_t connectTimeout = path::build_timeout;
llarp_time_t shiftTimeout = (path::build_timeout * 5) / 2;
llarp_time_t estimatedRTT = 0s; llarp_time_t estimatedRTT = 0s;
bool markedBad = false; bool markedBad = false;
using Msg_ptr = std::shared_ptr<routing::PathTransferMessage>; using Msg_ptr = std::shared_ptr<routing::PathTransferMessage>;
@ -55,6 +56,8 @@ namespace llarp
std::function<void(AuthResult)> authResultListener; std::function<void(AuthResult)> authResultListener;
std::shared_ptr<EventLoopWakeup> m_FlushWakeup;
virtual bool virtual bool
ShiftIntroduction(bool rebuild = true) ShiftIntroduction(bool rebuild = true)
{ {
@ -62,6 +65,9 @@ namespace llarp
return true; return true;
} }
virtual void
ShiftIntroRouter(const RouterID) = 0;
virtual void virtual void
UpdateIntroSet() = 0; UpdateIntroSet() = 0;
@ -72,6 +78,11 @@ namespace llarp
AsyncSendAuth(std::function<void(AuthResult)> replyHandler); AsyncSendAuth(std::function<void(AuthResult)> replyHandler);
private: private:
virtual bool
IntroGenerated() const = 0;
virtual bool
IntroSent() const = 0;
void void
EncryptAndSendTo(const llarp_buffer_t& payload, ProtocolType t); EncryptAndSendTo(const llarp_buffer_t& payload, ProtocolType t);

View File

@ -11,7 +11,7 @@ namespace llarp
{"lastSend", to_json(lastSend)}, {"lastSend", to_json(lastSend)},
{"lastRecv", to_json(lastRecv)}, {"lastRecv", to_json(lastRecv)},
{"replyIntro", replyIntro.ExtractStatus()}, {"replyIntro", replyIntro.ExtractStatus()},
{"remote", remote.Addr().ToString()}, {"remote", Addr().ToString()},
{"seqno", seqno}, {"seqno", seqno},
{"tx", messagesSend}, {"tx", messagesSend},
{"rx", messagesRecv}, {"rx", messagesRecv},
@ -19,6 +19,12 @@ namespace llarp
return obj; return obj;
} }
Address
Session::Addr() const
{
return remote.Addr();
}
bool bool
Session::IsExpired(llarp_time_t now, llarp_time_t lifetime) const Session::IsExpired(llarp_time_t now, llarp_time_t lifetime) const
{ {

View File

@ -49,6 +49,9 @@ namespace llarp
bool bool
IsExpired(llarp_time_t now, llarp_time_t lifetime = SessionLifetime) const; IsExpired(llarp_time_t now, llarp_time_t lifetime = SessionLifetime) const;
Address
Addr() const;
}; };
} // namespace service } // namespace service

View File

@ -38,6 +38,13 @@ namespace llarp
return m_Values.try_emplace(v, now).second; return m_Values.try_emplace(v, now).second;
} }
/// upsert will insert or update a value with time as now
void
Upsert(const Val_t& v)
{
m_Values[v] = llarp::time_now_ms();
}
/// decay hashset entries /// decay hashset entries
void void
Decay(Time_t now = 0s) Decay(Time_t now = 0s)

View File

@ -54,6 +54,12 @@ namespace llarp
return shared_from_this(); return shared_from_this();
} }
std::weak_ptr<path::PathSet>
GetWeak() override
{
return weak_from_this();
}
bool bool
SupportsV6() const override SupportsV6() const override
{ {

View File

@ -20,7 +20,7 @@ MakePath(std::vector< char > hops)
std::vector< RC_t > pathHops; std::vector< RC_t > pathHops;
for(const auto& hop : hops) for(const auto& hop : hops)
pathHops.push_back(MakeHop(hop)); pathHops.push_back(MakeHop(hop));
return std::make_shared< Path_t >(pathHops, nullptr, 0, "test"); return std::make_shared< Path_t >(pathHops, std::weak_ptr<llarp::path::PathSet>{}, 0, "test");
} }
TEST_CASE("UniqueEndpointSet_t has unique endpoints", "[path]") TEST_CASE("UniqueEndpointSet_t has unique endpoints", "[path]")