Merge branch 'master' of ssh://github.com/majestrate/loki-network

pull/798/head
Jeff 5 years ago
commit 6df9f9bfb1

@ -13,6 +13,7 @@ project(${PROJECT_NAME} C CXX)
option(USE_AVX2 "enable avx2 code" )
option(USE_NETNS "enable networking namespace support. Linux only" )
option(AMD_RYZEN_HACK "hack for AMD Ryzen FPU bug (support FMA3 and FMA4 in FPU, but does not show in CPUID)" )
option(NATIVE_BUILD "optimise for host system and FPU, may not be portable" )
if (NOT MSVC)
option(STATIC_LINK_RUNTIME "link statically against compiler runtime, standard library and pthreads")
endif()
@ -106,10 +107,10 @@ if(WITH_SHELLHOOKS)
add_definitions(-DENABLE_SHELLHOOKS)
endif(WITH_SHELLHOOKS)
add_compile_options(${OPTIMIZE_FLAGS} ${CRYPTO_FLAGS})
# Always build PIC
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(ABSEIL_DIR vendor/abseil-cpp)
add_subdirectory(vendor/gtest)
add_subdirectory(${ABSEIL_DIR})
include_directories(SYSTEM ${ABSEIL_DIR})
@ -117,9 +118,6 @@ add_subdirectory(vendor/cxxopts)
add_subdirectory(vendor/nlohmann)
include_directories(SYSTEM vendor/cxxopts/include)
# Always build PIC
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wno-unknown-warning-option)
endif()
@ -160,6 +158,13 @@ if(AMD_RYZEN_HACK AND USE_AVX2)
message(WARNING "This option may be removed in a future release. Contact your computer manufacturer for updated ROMs or microcode patches.")
endif(AMD_RYZEN_HACK AND USE_AVX2)
if(NATIVE_BUILD)
message(WARNING "May fail at runtime if the floating-point unit on the target system does not implement required features, some platforms will check this for you")
set(CRYPTO_FLAGS -march=native -mfpmath=sse -mtune=native)
endif()
add_compile_options(${OPTIMIZE_FLAGS} ${CRYPTO_FLAGS})
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)

@ -13,7 +13,8 @@ CXX ?= c++
BUILD_TYPE ?= Debug
PYTHON ?= python3
PYTHON ?= python
PYTHON3 ?= python3
SETCAP ?= which setcap && setcap cap_net_admin,cap_net_bind_service=+eip
@ -165,7 +166,7 @@ shadow-build: shadow-configure
$(MAKE) -C $(BUILD_ROOT)
shadow-run: shadow-build
$(PYTHON) $(REPO)/contrib/shadow/genconf.py $(SHADOW_CONFIG)
$(PYTHON3) $(REPO)/contrib/shadow/genconf.py $(SHADOW_CONFIG)
cp $(SHADOW_PLUGIN) $(REPO)/libshadow-plugin-lokinet.so
$(SHADOW_BIN) $(SHADOW_OPTS) $(SHADOW_CONFIG) | $(SHADOW_PARSE)
@ -187,7 +188,7 @@ testnet-build: testnet-configure
testnet:
cp $(EXE) $(TESTNET_EXE)
mkdir -p $(TESTNET_ROOT)
$(PYTHON) $(REPO)/contrib/testnet/genconf.py --bin=$(TESTNET_EXE) --svc=$(TESTNET_SERVERS) --clients=$(TESTNET_CLIENTS) --dir=$(TESTNET_ROOT) --out $(TESTNET_CONF) --ifname=$(TESTNET_IFNAME) --baseport=$(TESTNET_BASEPORT) --ip=$(TESTNET_IP) --netid=$(TESTNET_NETID)
$(PYTHON3) $(REPO)/contrib/testnet/genconf.py --bin=$(TESTNET_EXE) --svc=$(TESTNET_SERVERS) --clients=$(TESTNET_CLIENTS) --dir=$(TESTNET_ROOT) --out $(TESTNET_CONF) --ifname=$(TESTNET_IFNAME) --baseport=$(TESTNET_BASEPORT) --ip=$(TESTNET_IP) --netid=$(TESTNET_NETID)
LLARP_DEBUG=$(TESTNET_DEBUG) supervisord -n -d $(TESTNET_ROOT) -l $(TESTNET_LOG) -c $(TESTNET_CONF)
$(TEST_EXE): debug
@ -279,7 +280,7 @@ docker-kubernetes:
docker build -f docker/loki-svc-kubernetes.Dockerfile .
install-pylokinet:
cd $(REPO)/contrib/py/pylokinet && $(PYTHON) setup.py install
cd $(REPO)/contrib/py/pylokinet && $(PYTHON3) setup.py install
kubernetes-install: install install-pylokinet

@ -14,5 +14,5 @@ include_directories(${CMAKE_MODULE_PATH})
include(ShadowTools)
add_compile_options(-fno-inline -fno-strict-aliasing )
add_definitions(-DTESTNET=1)
add_definitions(-DSHADOW_TESTNET)
add_definitions(-DLOKINET_SHADOW)
include_directories(${SHADOW_ROOT}/include)

@ -85,14 +85,12 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" OR ${CMAKE_SYSTEM_NAME} MATCHES "
set(LIBTUNTAP_IMPL ${TT_ROOT}/tuntap-unix-freebsd.c ${TT_ROOT}/tuntap-unix-bsd.c)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
find_library(FS_LIB NAMES c++fs c++experimental stdc++fs)
if(FS_LIB STREQUAL FS_LIB-NOTFOUND)
add_subdirectory(vendor)
include_directories("${CMAKE_CURRENT_LIST_DIR}/../vendor/cppbackport-master/lib")
add_definitions(-DLOKINET_USE_CPPBACKPORT)
set(FS_LIB cppbackport)
endif()
set(LIBTUNTAP_IMPL ${TT_ROOT}/tuntap-unix-darwin.c ${TT_ROOT}/tuntap-unix-bsd.c)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
set(LIBTUNTAP_IMPL ${TT_ROOT}/tuntap-unix-sunos.c)
@ -103,6 +101,8 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
include_directories("${CMAKE_CURRENT_LIST_DIR}/../vendor/cppbackport-master/lib")
add_definitions(-DLOKINET_USE_CPPBACKPORT)
set(FS_LIB cppbackport)
else()
set(FS_LIB stdc++fs)
endif()
else()
message(FATAL_ERROR "Your operating system is not supported yet")

@ -21,9 +21,7 @@ if(NOT MSVC_VERSION)
# GNU ld sees fit to merge *all* the .ident sections in object files
# to .r[o]data section one after the other!
add_compile_options(-fno-ident -Wa,-mbig-obj)
find_library(shlwapi_lib shlwapi)
find_library(dbghelp_lib dbghelp)
link_libraries( ${shlwapi_lib} ${dbghelp_lib} )
link_libraries( -lshlwapi -ldbghelp )
add_definitions(-DWINVER=0x0500 -D_WIN32_WINNT=0x0500)
# Wait a minute, if we're not Microsoft C++, nor a Clang paired with Microsoft C++,
# then the only possible option has to be GNU or a GNU-linked Clang!
@ -35,10 +33,7 @@ endif()
get_filename_component(LIBTUNTAP_IMPL ${TT_ROOT}/tuntap-windows.c ABSOLUTE)
get_filename_component(EV_SRC "llarp/ev/ev_win32.cpp" ABSOLUTE)
add_definitions(-DWIN32_LEAN_AND_MEAN -DWIN32 -DWINVER=0x0500)
find_library(ws2_32_lib ws2_32)
find_library(iphlpapi_lib iphlpapi)
set(EXE_LIBS ${STATIC_LIB} ${FS_LIB} ${ws2_32_lib} ${iphlpapi_lib})
set(EXE_LIBS ${STATIC_LIB} ${FS_LIB} ws2_32 iphlpapi)
if(RELEASE_MOTTO)
add_definitions(-DLLARP_RELEASE_MOTTO="${RELEASE_MOTTO}")

@ -13,7 +13,7 @@ def getSetting(s, name, fallback): return name in s and s[name] or fallback
shadowRoot = getSetting(os.environ, "SHADOW_ROOT",
os.path.join(os.environ['HOME'], '.shadow'))
libpath = 'libshadow-plugin-lokinet-shared.so'
libpath = 'libshadow-plugin-lokinet.so'
def nodeconf(conf, baseDir, name, ifname=None, port=None):

@ -1,2 +1,3 @@
#pragma once
#include <stdint.h>
typedef int32_t crypto_int32;
typedef int32_t crypto_int32;

@ -1,2 +1,3 @@
#pragma once
#include <stdint.h>
typedef uint32_t crypto_uint32;
typedef uint32_t crypto_uint32;

@ -1,5 +1,4 @@
#include "params.h"
#include <sodium/crypto_uint32.h>
#include "rq.h"
void

@ -1,5 +1,7 @@
FROM compose-base:latest
ENV LOKINET_NETID=docker
COPY ./docker/compose/bootstrap.ini /root/.lokinet/lokinet.ini
CMD ["/lokinet"]

@ -1,7 +1,3 @@
# this configuration was auto generated with 'sane' defaults
# change these values as desired
[router]
# number of crypto worker threads
threads=4
@ -13,18 +9,13 @@ transport-privkey=/root/.lokinet/transport.private
ident-privkey=/root/.lokinet/identity.private
# encryption key for onion routing
encryption-privkey=/root/.lokinet/encryption.private
block-bogons=false
# uncomment following line to set router nickname to 'lokinet'
#nickname=lokinet
nickname=bootstrap
[logging]
level=info
# uncomment for logging to file
#type=file
#file=/path/to/logfile
# uncomment for syslog logging
#type=syslog
[metrics]
json-metrics-path=/root/.lokinet/metrics.json
@ -32,9 +23,6 @@ json-metrics-path=/root/.lokinet/metrics.json
# admin api (disabled by default)
[api]
enabled=true
#authkey=insertpubkey1here
#authkey=insertpubkey2here
#authkey=insertpubkey3here
bind=127.0.0.1:1190
# system settings for privileges and such
@ -58,17 +46,12 @@ dir=/netdb
[lokid]
enabled=false
jsonrpc=127.0.0.1:22023
#service-node-seed=/path/to/servicenode/seed
# network settings
[network]
profiles=/root/.lokinet/profiles.dat
enabled=true
exit=false
#exit-blacklist=tcp:25
#exit-whitelist=tcp:*
#exit-whitelist=udp:*
ifaddr=10.200.0.1/8
ifname=loki-docker0
# ROUTERS ONLY: publish network interfaces for handling inbound traffic

@ -0,0 +1,6 @@
FROM compose-base:latest
COPY ./docker/compose/client.ini /root/.lokinet/lokinet.ini
CMD ["/lokinet"]
EXPOSE 1090/udp 1190/tcp

@ -0,0 +1,52 @@
[router]
# number of crypto worker threads
threads=4
# path to store signed RC
contact-file=/root/.lokinet/self.signed
# path to store transport private key
transport-privkey=/root/.lokinet/transport.private
# path to store identity signing key
ident-privkey=/root/.lokinet/identity.private
# encryption key for onion routing
encryption-privkey=/root/.lokinet/encryption.private
block-bogons=false
[logging]
level=info
[metrics]
json-metrics-path=/root/.lokinet/metrics.json
# admin api (disabled by default)
[api]
enabled=true
bind=127.0.0.1:1190
# system settings for privileges and such
[system]
user=lokinet
group=lokinet
pidfile=/root/.lokinet/lokinet.pid
# dns provider configuration section
[dns]
# resolver
upstream=1.1.1.1
bind=127.0.0.1:53
# network database settings block
[netdb]
# directory for network database skiplist storage
dir=/netdb
# lokid settings (disabled by default)
[lokid]
enabled=false
jsonrpc=127.0.0.1:22023
# network settings
[network]
profiles=/root/.lokinet/profiles.dat
enabled=true
exit=false
ifname=loki-docker0

@ -12,10 +12,8 @@ services:
ports:
- target: 1090
protocol: udp
mode: host
- target: 1190
protocol: tcp
mode: host
volumes:
- bootstrap-dir:/root/.lokinet/
environment:
@ -48,6 +46,34 @@ services:
networks:
testing_net:
client:
depends_on:
- bootstrap-router
build:
context: .
dockerfile: docker/compose/router.Dockerfile
image: router
devices:
- "/dev/net/tun:/dev/net/tun"
ports:
- target: 1090
protocol: udp
mode: host
- target: 1190
protocol: tcp
mode: host
- target: 53
protocol: tcp
mode: host
cap_add:
- NET_ADMIN
volumes:
- bootstrap-dir:/bootstrap/
environment:
- LOKINET_NETID=docker
networks:
testing_net:
volumes:
bootstrap-dir:

@ -1,7 +1,3 @@
# this configuration was auto generated with 'sane' defaults
# change these values as desired
[router]
# number of crypto worker threads
threads=4
@ -13,6 +9,7 @@ transport-privkey=/root/.lokinet/transport.private
ident-privkey=/root/.lokinet/identity.private
# encryption key for onion routing
encryption-privkey=/root/.lokinet/encryption.private
block-bogons=false
# uncomment following line to set router nickname to 'lokinet'
#nickname=lokinet
@ -32,9 +29,6 @@ json-metrics-path=/root/.lokinet/metrics.json
# admin api (disabled by default)
[api]
enabled=true
#authkey=insertpubkey1here
#authkey=insertpubkey2here
#authkey=insertpubkey3here
bind=127.0.0.1:1190
# system settings for privileges and such
@ -64,16 +58,12 @@ add-node=/bootstrap/self.signed
[lokid]
enabled=false
jsonrpc=127.0.0.1:22023
#service-node-seed=/path/to/servicenode/seed
# network settings
[network]
profiles=/root/.lokinet/profiles.dat
enabled=true
exit=false
#exit-blacklist=tcp:25
#exit-whitelist=tcp:*
#exit-whitelist=udp:*
ifaddr=10.200.0.1/8
ifname=loki-docker0

@ -176,9 +176,11 @@ set(LIB_SRC
handlers/null.cpp
handlers/tun.cpp
hook/shell.cpp
iwp/linklayer.cpp
iwp/outermessage.cpp
iwp/iwp.cpp
iwp/linklayer.cpp
iwp/message_buffer.cpp
iwp/session.cpp
link/factory.cpp
link/i_link_manager.cpp
link/link_manager.cpp
link/server.cpp

@ -4,13 +4,16 @@
#include <constants/defaults.hpp>
#include <constants/limits.hpp>
#include <net/net.hpp>
#include <router_contact.hpp>
#include <util/fs.hpp>
#include <util/logger.hpp>
#include <util/logger_syslog.hpp>
#include <util/logger.hpp>
#include <util/mem.hpp>
#include <util/memfn.hpp>
#include <util/str.hpp>
#include <absl/strings/strip.h>
#include <cstdlib>
#include <fstream>
#include <ios>
@ -31,9 +34,28 @@ namespace llarp
return std::atoi(str.c_str());
}
absl::optional< bool >
setOptBool(string_view val)
{
if(IsTrueValue(val))
{
return true;
}
else if(IsFalseValue(val))
{
return false;
}
return {};
}
void
RouterConfig::fromSection(string_view key, string_view val)
{
if(key == "default-protocol")
{
m_DefaultLinkProto = tostr(val);
LogInfo("overriding default link protocol to '", val, "'");
}
if(key == "netid")
{
if(val.size() <= NetID::size())
@ -139,6 +161,10 @@ namespace llarp
LogDebug("set to use ", m_numNetThreads, " net threads");
}
}
if(key == "block-bogons")
{
m_blockBogons = setOptBool(val);
}
}
void
@ -146,14 +172,7 @@ namespace llarp
{
if(key == "profiling")
{
if(IsTrueValue(val))
{
m_enableProfiling.emplace(true);
}
else if(IsFalseValue(val))
{
m_enableProfiling.emplace(false);
}
m_enableProfiling = setOptBool(val);
}
else if(key == "profiles")
{
@ -195,28 +214,31 @@ namespace llarp
}
void
IwpConfig::fromSection(string_view key, string_view val)
LinksConfig::fromSection(string_view key, string_view val)
{
// try IPv4 first
uint16_t proto = 0;
std::set< std::string > parsed_opts;
std::unordered_set< std::string > parsed_opts;
std::string v = tostr(val);
std::string::size_type idx;
static constexpr char delimiter = ',';
do
{
idx = v.find_first_of(',');
idx = v.find_first_of(delimiter);
if(idx != std::string::npos)
{
parsed_opts.insert(v.substr(0, idx));
std::string val = v.substr(0, idx);
absl::StripAsciiWhitespace(&val);
parsed_opts.emplace(std::move(val));
v = v.substr(idx + 1);
}
else
{
parsed_opts.insert(v);
absl::StripAsciiWhitespace(&v);
parsed_opts.insert(std::move(v));
}
} while(idx != std::string::npos);
std::unordered_set< std::string > opts;
/// for each option
for(const auto &item : parsed_opts)
{
@ -230,15 +252,20 @@ namespace llarp
proto = port;
}
}
else
{
opts.insert(item);
}
}
if(key == "*")
{
m_OutboundPort = proto;
m_OutboundLink = std::make_tuple(
"*", AF_INET, fromEnv(proto, "OUTBOUND_PORT"), std::move(opts));
}
else
{
m_servers.emplace_back(tostr(key), AF_INET, proto);
m_InboundLinks.emplace_back(tostr(key), AF_INET, proto, std::move(opts));
}
}
@ -398,7 +425,9 @@ namespace llarp
};
if(c.VisitSection(name.c_str(), visitor))
{
return ret;
}
return {};
}
@ -435,7 +464,7 @@ namespace llarp
connect = find_section< ConnectConfig >(parser, "connect");
netdb = find_section< NetdbConfig >(parser, "netdb");
dns = find_section< DnsConfig >(parser, "dns");
iwp_links = find_section< IwpConfig >(parser, "bind");
links = find_section< LinksConfig >(parser, "bind");
services = find_section< ServicesConfig >(parser, "services");
system = find_section< SystemConfig >(parser, "system");
metrics = find_section< MetricsConfig >(parser, "metrics");
@ -465,7 +494,7 @@ llarp_ensure_config(const char *fname, const char *basedir, bool overwrite,
return false;
}
std::string basepath = "";
std::string basepath;
if(basedir)
{
basepath = basedir;
@ -641,10 +670,14 @@ llarp_ensure_router_config(std::ofstream &f, std::string basepath)
// get ifname
std::string ifname;
if(llarp::GetBestNetIF(ifname, AF_INET))
{
f << ifname << "=1090\n";
}
else
{
f << "# could not autodetect network interface\n"
<< "#eth0=1090\n";
}
f << std::endl;
}
@ -658,7 +691,9 @@ llarp_ensure_client_config(std::ofstream &f, std::string basepath)
auto stream = llarp::util::OpenFileStream< std::ofstream >(
snappExample_fpath, std::ios::binary);
if(!stream)
{
return false;
}
auto &example_f = stream.value();
if(example_f.is_open())
{

@ -12,6 +12,7 @@
#include <string>
#include <utility>
#include <vector>
#include <unordered_set>
namespace llarp
{
@ -111,6 +112,8 @@ namespace llarp
// long term identity key
std::string m_identKeyfile = "identity.key";
absl::optional< bool > m_blockBogons;
bool m_publicOverride = false;
struct sockaddr_in m_ip4addr;
AddressInfo m_addrInfo;
@ -118,21 +121,25 @@ namespace llarp
int m_workerThreads = 1;
int m_numNetThreads = 1;
std::string m_DefaultLinkProto = "iwp";
public:
// clang-format off
size_t minConnectedRouters() const { return fromEnv(m_minConnectedRouters, "MIN_CONNECTED_ROUTERS"); }
size_t maxConnectedRouters() const { return fromEnv(m_maxConnectedRouters, "MAX_CONNECTED_ROUTERS"); }
std::string encryptionKeyfile() const { return fromEnv(m_encryptionKeyfile, "ENCRYPTION_KEYFILE"); }
std::string ourRcFile() const { return fromEnv(m_ourRcFile, "OUR_RC_FILE"); }
std::string transportKeyfile() const { return fromEnv(m_transportKeyfile, "TRANSPORT_KEYFILE"); }
std::string identKeyfile() const { return fromEnv(m_identKeyfile, "IDENT_KEYFILE"); }
std::string netId() const { return fromEnv(m_netId, "NETID"); }
std::string nickname() const { return fromEnv(m_nickname, "NICKNAME"); }
bool publicOverride() const { return fromEnv(m_publicOverride, "PUBLIC_OVERRIDE"); }
const struct sockaddr_in& ip4addr() const { return m_ip4addr; }
const AddressInfo& addrInfo() const { return m_addrInfo; }
int workerThreads() const { return fromEnv(m_workerThreads, "WORKER_THREADS"); }
int numNetThreads() const { return fromEnv(m_numNetThreads, "NUM_NET_THREADS"); }
size_t minConnectedRouters() const { return fromEnv(m_minConnectedRouters, "MIN_CONNECTED_ROUTERS"); }
size_t maxConnectedRouters() const { return fromEnv(m_maxConnectedRouters, "MAX_CONNECTED_ROUTERS"); }
std::string encryptionKeyfile() const { return fromEnv(m_encryptionKeyfile, "ENCRYPTION_KEYFILE"); }
std::string ourRcFile() const { return fromEnv(m_ourRcFile, "OUR_RC_FILE"); }
std::string transportKeyfile() const { return fromEnv(m_transportKeyfile, "TRANSPORT_KEYFILE"); }
std::string identKeyfile() const { return fromEnv(m_identKeyfile, "IDENT_KEYFILE"); }
std::string netId() const { return fromEnv(m_netId, "NETID"); }
std::string nickname() const { return fromEnv(m_nickname, "NICKNAME"); }
bool publicOverride() const { return fromEnv(m_publicOverride, "PUBLIC_OVERRIDE"); }
const struct sockaddr_in& ip4addr() const { return m_ip4addr; }
const AddressInfo& addrInfo() const { return m_addrInfo; }
int workerThreads() const { return fromEnv(m_workerThreads, "WORKER_THREADS"); }
int numNetThreads() const { return fromEnv(m_numNetThreads, "NUM_NET_THREADS"); }
std::string defaultLinkProto() const { return fromEnv(m_DefaultLinkProto, "LINK_PROTO"); }
absl::optional< bool > blockBogons() const { return fromEnv(m_blockBogons, "BLOCK_BOGONS"); }
// clang-format on
void
@ -184,20 +191,27 @@ namespace llarp
fromSection(string_view key, string_view val);
};
class IwpConfig
class LinksConfig
{
public:
using Servers = std::vector< std::tuple< std::string, int, uint16_t > >;
static constexpr int Interface = 0;
static constexpr int AddressFamily = 1;
static constexpr int Port = 2;
static constexpr int Options = 3;
private:
uint16_t m_OutboundPort = 0;
using ServerOptions = std::unordered_set< std::string >;
using LinkInfo = std::tuple< std::string, int, uint16_t, ServerOptions >;
using Links = std::vector< LinkInfo >;
Servers m_servers;
private:
LinkInfo m_OutboundLink;
Links m_InboundLinks;
public:
// clang-format off
uint16_t outboundPort() const { return fromEnv(m_OutboundPort, "OUTBOUND_PORT"); }
const Servers& servers() const { return m_servers; }
const LinkInfo& outboundLink() const { return m_OutboundLink; }
const Links& inboundLinks() const { return m_InboundLinks; }
// clang-format on
void
@ -296,7 +310,7 @@ namespace llarp
ConnectConfig connect;
NetdbConfig netdb;
DnsConfig dns;
IwpConfig iwp_links;
LinksConfig links;
ServicesConfig services;
SystemConfig system;
MetricsConfig metrics;

@ -1,3 +1,6 @@
#include <constants/version.hpp>
const char Version::LLARP_NET_ID[] = "testnet";
#ifndef LLARP_DEFAULT_NETID
#define LLARP_DEFAULT_NETID "testnet"
#endif
const char Version::LLARP_NET_ID[] = LLARP_DEFAULT_NETID;

@ -6,11 +6,11 @@
#endif
#ifndef LLARP_VERSION_MIN
#define LLARP_VERSION_MIN "4"
#define LLARP_VERSION_MIN "5"
#endif
#ifndef LLARP_VERSION_PATCH
#define LLARP_VERSION_PATCH "3"
#define LLARP_VERSION_PATCH "0"
#endif
#ifndef LLARP_VERSION_NUM

@ -97,6 +97,7 @@ namespace llarp
if(dht.pendingExploreLookups().HasPendingLookupFrom(owner))
{
LogDebug("got ", N.size(), " results in GRM for explore");
if(N.size() == 0)
dht.pendingExploreLookups().NotFound(owner, K);
else
@ -108,6 +109,7 @@ namespace llarp
// not explore lookup
if(dht.pendingRouterLookups().HasPendingLookupFrom(owner))
{
LogDebug("got ", R.size(), " results in GRM for lookup");
if(R.size() == 0)
dht.pendingRouterLookups().NotFound(owner, K);
else

@ -76,11 +76,10 @@ namespace llarp
parent->DHTSendTo(
whoasked.node.as_array(),
new GotRouterMessage({}, whoasked.txid, valuesFound, false), false);
// store this in our nodedb for caching
if(valuesFound.size() > 0)
parent->StoreRC(valuesFound[0]);
}
// store this in our nodedb for caching
if(valuesFound.size() > 0)
parent->StoreRC(valuesFound[0]);
}
} // namespace dht
} // namespace llarp

@ -40,6 +40,8 @@ typedef struct sockaddr_un
#include <sys/un.h>
#endif
struct llarp_ev_pkt_pipe;
#ifndef MAX_WRITE_QUEUE_SIZE
#define MAX_WRITE_QUEUE_SIZE (1024UL)
#endif
@ -772,6 +774,12 @@ struct llarp_ev_loop
virtual llarp::ev_io*
bind_tcp(llarp_tcp_acceptor* tcp, const sockaddr* addr) = 0;
virtual bool
add_pipe(llarp_ev_pkt_pipe*)
{
return false;
}
/// register event listener
virtual bool
add_ev(llarp::ev_io* ev, bool write) = 0;

@ -427,6 +427,77 @@ namespace libuv
}
};
struct pipe_glue : public glue
{
byte_t m_Buffer[1024 * 8];
llarp_ev_pkt_pipe* const m_Pipe;
pipe_glue(uv_loop_t* loop, llarp_ev_pkt_pipe* pipe) : m_Pipe(pipe)
{
m_Handle.data = this;
m_Ticker.data = this;
uv_poll_init(loop, &m_Handle, m_Pipe->fd);
uv_check_init(loop, &m_Ticker);
}
void
Tick()
{
m_Pipe->tick();
}
static void
OnRead(uv_poll_t* handle, int status, int)
{
if(status)
{
return;
}
pipe_glue* glue = static_cast< pipe_glue* >(handle->data);
int r = glue->m_Pipe->read(glue->m_Buffer, sizeof(glue->m_Buffer));
if(r <= 0)
return;
const llarp_buffer_t buf{glue->m_Buffer, static_cast< size_t >(r)};
glue->m_Pipe->OnRead(buf);
}
static void
OnClosed(uv_handle_t* h)
{
auto* self = static_cast< pipe_glue* >(h->data);
if(self)
{
h->data = nullptr;
delete self;
}
}
void
Close() override
{
uv_check_stop(&m_Ticker);
uv_close((uv_handle_t*)&m_Handle, &OnClosed);
}
static void
OnTick(uv_check_t* h)
{
static_cast< pipe_glue* >(h->data)->Tick();
}
bool
Start()
{
if(uv_poll_start(&m_Handle, UV_READABLE, &OnRead))
return false;
if(uv_check_start(&m_Ticker, &OnTick))
return false;
return true;
}
uv_poll_t m_Handle;
uv_check_t m_Ticker;
};
struct tun_glue : public glue
{
uv_poll_t m_Handle;
@ -703,4 +774,14 @@ namespace libuv
return false;
}
bool
Loop::add_pipe(llarp_ev_pkt_pipe* p)
{
auto* glue = new pipe_glue(m_Impl.get(), p);
if(glue->Start())
return true;
delete glue;
return false;
}
} // namespace libuv

@ -1,6 +1,7 @@
#ifndef LLARP_EV_LIBUV_HPP
#define LLARP_EV_LIBUV_HPP
#include <ev/ev.hpp>
#include <ev/pipe.hpp>
#include <uv.h>
#include <vector>
#include <functional>
@ -68,6 +69,9 @@ namespace libuv
bool
tcp_listen(llarp_tcp_acceptor* tcp, const sockaddr* addr) override;
bool
add_pipe(llarp_ev_pkt_pipe* p) override;
llarp::ev_io*
bind_tcp(llarp_tcp_acceptor*, const sockaddr*) override
{

@ -12,7 +12,7 @@ llarp_ev_pkt_pipe::llarp_ev_pkt_pipe(llarp_ev_loop_ptr loop)
}
bool
llarp_ev_pkt_pipe::Start()
llarp_ev_pkt_pipe::StartPipe()
{
#if defined(_WIN32)
llarp::LogError("llarp_ev_pkt_pipe not supported on win32");
@ -26,7 +26,7 @@ llarp_ev_pkt_pipe::Start()
}
fd = _fds[0];
writefd = _fds[1];
return true;
return m_Loop->add_pipe(this);
#endif
}

@ -10,7 +10,7 @@ struct llarp_ev_pkt_pipe : public llarp::ev_io
/// start the pipe, initialize fds
bool
Start();
StartPipe();
/// write to the pipe from outside the event loop
/// returns true on success

@ -7,22 +7,26 @@ namespace llarp
{
namespace iwp
{
std::unique_ptr< ILinkLayer >
NewServer(const SecretKey& enckey, GetRCFunc getrc, LinkMessageHandler h,
SessionEstablishedHandler est, SessionRenegotiateHandler reneg,
SignBufferFunc sign, TimeoutHandler t,
SessionClosedHandler closed)
LinkLayer_ptr
NewInboundLink(const SecretKey& routerEncSecret, GetRCFunc getrc,
LinkMessageHandler h, SignBufferFunc sign,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, TimeoutHandler timeout,
SessionClosedHandler closed)
{
(void)enckey;
(void)getrc;
(void)h;
(void)est;
(void)reneg;
(void)sign;
(void)t;
(void)closed;
// TODO: implement me
return nullptr;
return std::make_shared< LinkLayer >(routerEncSecret, getrc, h, sign, est,
reneg, timeout, closed, true);
}
LinkLayer_ptr
NewOutboundLink(const SecretKey& routerEncSecret, GetRCFunc getrc,
LinkMessageHandler h, SignBufferFunc sign,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, TimeoutHandler timeout,
SessionClosedHandler closed)
{
return std::make_shared< LinkLayer >(routerEncSecret, getrc, h, sign, est,
reneg, timeout, closed, false);
}
} // namespace iwp
} // namespace llarp

@ -2,21 +2,25 @@
#define LLARP_IWP_HPP
#include <link/server.hpp>
#include <iwp/linklayer.hpp>
#include <memory>
namespace llarp
{
struct AbstractRouter;
namespace iwp
{
std::unique_ptr< ILinkLayer >
NewServer(const SecretKey& routerEncSecret, llarp::GetRCFunc getrc,
llarp::LinkMessageHandler h, llarp::SessionEstablishedHandler est,
llarp::SessionRenegotiateHandler reneg,
llarp::SignBufferFunc sign, llarp::TimeoutHandler timeout,
llarp::SessionClosedHandler closed);
LinkLayer_ptr
NewInboundLink(const SecretKey& routerEncSecret, GetRCFunc getrc,
LinkMessageHandler h, SignBufferFunc sign,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, TimeoutHandler timeout,
SessionClosedHandler closed);
LinkLayer_ptr
NewOutboundLink(const SecretKey& routerEncSecret, GetRCFunc getrc,
LinkMessageHandler h, SignBufferFunc sign,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, TimeoutHandler timeout,
SessionClosedHandler closed);
} // namespace iwp
} // namespace llarp

@ -1,16 +1,20 @@
#include <iwp/linklayer.hpp>
#include <iwp/session.hpp>
namespace llarp
{
namespace iwp
{
LinkLayer::LinkLayer(const SecretKey& enckey, GetRCFunc getrc,
LinkMessageHandler h, SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, SignBufferFunc sign,
TimeoutHandler t, SessionClosedHandler closed)
: ILinkLayer(enckey, getrc, h, sign, est, reneg, t, closed)
LinkLayer::LinkLayer(const SecretKey& routerEncSecret, GetRCFunc getrc,
LinkMessageHandler h, SignBufferFunc sign,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg,
TimeoutHandler timeout, SessionClosedHandler closed,
bool allowInbound)
: ILinkLayer(routerEncSecret, getrc, h, sign, est, reneg, timeout,
closed)
, permitInbound{allowInbound}
{
m_FlowCookie.Randomize();
}
LinkLayer::~LinkLayer() = default;
@ -18,7 +22,28 @@ namespace llarp
void
LinkLayer::Pump()
{
std::set< RouterID > sessions;
{
Lock l(&m_AuthedLinksMutex);
auto itr = m_AuthedLinks.begin();
while(itr != m_AuthedLinks.end())
{
sessions.insert(itr->first);
++itr;
}
}
ILinkLayer::Pump();
{
Lock l(&m_AuthedLinksMutex);
for(const auto& pk : sessions)
{
if(m_AuthedLinks.count(pk) == 0)
{
// all sessions were removed
SessionClosed(pk);
}
}
}
}
const char*
@ -44,135 +69,57 @@ namespace llarp
bool
LinkLayer::Start(std::shared_ptr< Logic > l)
{
if(!ILinkLayer::Start(l))
return false;
return false;
return ILinkLayer::Start(l);
}
void
LinkLayer::RecvFrom(const Addr& from, const void* pkt, size_t sz)
{
m_OuterMsg.Clear();
llarp_buffer_t sigbuf(pkt, sz);
llarp_buffer_t decodebuf(pkt, sz);
if(!m_OuterMsg.Decode(&decodebuf))
std::shared_ptr< ILinkSession > session;
auto itr = m_AuthedAddrs.find(from);
if(itr == m_AuthedAddrs.end())
{
LogError("failed to decode outer message");
return;
util::Lock lock(&m_PendingMutex);
if(m_Pending.count(from) == 0)
{
if(not permitInbound)
return;
m_Pending.insert({from, std::make_shared< Session >(this, from)});
}
session = m_Pending.find(from)->second;
}
NetID ourNetID;
switch(m_OuterMsg.command)
else
{
case eOCMD_ObtainFlowID:
sigbuf.sz -= m_OuterMsg.Zsig.size();
if(!CryptoManager::instance()->verify(m_OuterMsg.pubkey, sigbuf,
m_OuterMsg.Zsig))
{
LogError("failed to verify signature on '",
(char)m_OuterMsg.command, "' message from ", from);
return;
}
if(!ShouldSendFlowID(from))
{
SendReject(from, "no flo 4u :^)");
return;
}
if(m_OuterMsg.netid == ourNetID)
{
if(GenFlowIDFor(m_OuterMsg.pubkey, from, m_OuterMsg.flow))
SendFlowID(from, m_OuterMsg.flow);
else
SendReject(from, "genflow fail");
}
else
SendReject(from, "bad netid");
auto range = m_AuthedLinks.equal_range(itr->second);
session = range.first->second;
}
if(session)
{
const llarp_buffer_t buf{pkt, sz};
session->Recv_LL(buf);
}
}
std::shared_ptr< ILinkSession >
LinkLayer::NewOutboundSession(const RouterContact& rc,
const AddressInfo& ai)
{
(void)rc;
(void)ai;
// TODO: implement me
return {};
}
void
LinkLayer::SendFlowID(const Addr& to, const FlowID_t& flow)
{
// TODO: implement me
(void)to;
(void)flow;
}
bool
LinkLayer::VerifyFlowID(const PubKey& pk, const Addr& from,
const FlowID_t& flow) const
{
FlowID_t expected;
if(!GenFlowIDFor(pk, from, expected))
return false;
return expected == flow;
}
bool
LinkLayer::GenFlowIDFor(const PubKey& pk, const Addr& from,
FlowID_t& flow) const
LinkLayer::MapAddr(const RouterID& r, ILinkSession* s)
{
std::array< byte_t, 128 > tmp = {{0}};
if(inet_ntop(AF_INET6, from.addr6(), (char*)tmp.data(), tmp.size())
== nullptr)
if(!ILinkLayer::MapAddr(r, s))
return false;
std::copy_n(pk.begin(), pk.size(), tmp.begin() + 64);
std::copy_n(m_FlowCookie.begin(), m_FlowCookie.size(),
tmp.begin() + 64 + pk.size());
llarp_buffer_t buf(tmp);
ShortHash h;
if(!CryptoManager::instance()->shorthash(h, buf))
return false;
std::copy_n(h.begin(), flow.size(), flow.begin());
m_AuthedAddrs.emplace(s->GetRemoteEndpoint(), r);
return true;
}
bool
LinkLayer::ShouldSendFlowID(const Addr& to) const
void
LinkLayer::UnmapAddr(const Addr& a)
{
(void)to;
// TODO: implement me
return false;
m_AuthedAddrs.erase(a);
}
void
LinkLayer::SendReject(const Addr& to, const char* msg)
std::shared_ptr< ILinkSession >
LinkLayer::NewOutboundSession(const RouterContact& rc,
const AddressInfo& ai)
{
if(strlen(msg) > 14)
{
throw std::logic_error("reject message too big");
}
std::array< byte_t, 120 > pkt;
auto now = Now();
PubKey pk = GetOurRC().pubkey;
OuterMessage m;
m.CreateReject(msg, now, pk);
llarp_buffer_t encodebuf(pkt);
if(!m.Encode(&encodebuf))
{
LogError("failed to encode reject message to ", to);
return;
}
llarp_buffer_t signbuf(pkt.data(), pkt.size() - m.Zsig.size());
if(!Sign(m.Zsig, signbuf))
{
LogError("failed to sign reject messsage to ", to);
return;
}
std::copy_n(m.Zsig.begin(), m.Zsig.size(),
pkt.begin() + (pkt.size() - m.Zsig.size()));
llarp_buffer_t pktbuf(pkt);
SendTo_LL(to, pktbuf);
return std::make_shared< Session >(this, rc, ai);
}
} // namespace iwp
} // namespace llarp

@ -6,7 +6,6 @@
#include <crypto/encrypted.hpp>
#include <crypto/types.hpp>
#include <link/server.hpp>
#include <iwp/outermessage.hpp>
namespace llarp
{
@ -14,10 +13,11 @@ namespace llarp
{
struct LinkLayer final : public ILinkLayer
{
LinkLayer(const SecretKey &encryptionSecretKey, GetRCFunc getrc,
LinkMessageHandler h, SessionEstablishedHandler established,
SessionRenegotiateHandler reneg, SignBufferFunc sign,
TimeoutHandler timeout, SessionClosedHandler closed);
LinkLayer(const SecretKey &routerEncSecret, GetRCFunc getrc,
LinkMessageHandler h, SignBufferFunc sign,
SessionEstablishedHandler est, SessionRenegotiateHandler reneg,
TimeoutHandler timeout, SessionClosedHandler closed,
bool permitInbound);
~LinkLayer() override;
@ -40,41 +40,21 @@ namespace llarp
uint16_t
Rank() const override;
/// verify that a new flow id matches addresses and pubkey
bool
VerifyFlowID(const PubKey &pk, const Addr &from,
const FlowID_t &flow) const;
void
RecvFrom(const Addr &from, const void *buf, size_t sz) override;
private:
bool
GenFlowIDFor(const PubKey &pk, const Addr &from, FlowID_t &flow) const;
bool
ShouldSendFlowID(const Addr &from) const;
void
SendReject(const Addr &to, const char *msg);
MapAddr(const RouterID &pk, ILinkSession *s) override;
void
SendFlowID(const Addr &to, const FlowID_t &flow);
UnmapAddr(const Addr &addr);
using ActiveFlows_t =
std::unordered_map< FlowID_t, RouterID, FlowID_t::Hash >;
ActiveFlows_t m_ActiveFlows;
using PendingFlows_t = std::unordered_map< Addr, FlowID_t, Addr::Hash >;
/// flows that are pending authentication
PendingFlows_t m_PendingFlows;
/// cookie used in flow id computation
AlignedBuffer< 32 > m_FlowCookie;
OuterMessage m_OuterMsg;
private:
std::unordered_map< Addr, RouterID, Addr::Hash > m_AuthedAddrs;
const bool permitInbound;
};
using LinkLayer_ptr = std::shared_ptr< LinkLayer >;
} // namespace iwp
} // namespace llarp

@ -0,0 +1,213 @@
#include <iwp/message_buffer.hpp>
#include <iwp/session.hpp>
#include <crypto/crypto.hpp>
namespace llarp
{
namespace iwp
{
OutboundMessage::OutboundMessage(uint64_t msgid, const llarp_buffer_t &pkt,
llarp_time_t now,
ILinkSession::CompletionHandler handler)
: m_Size{(uint16_t)std::min(pkt.sz, MAX_LINK_MSG_SIZE)}
, m_MsgID{msgid}
, m_Completed{handler}
, m_StartedAt{now}
{
m_Data.Zero();
std::copy_n(pkt.base, m_Size, m_Data.begin());
const llarp_buffer_t buf(m_Data.data(), m_Size);
CryptoManager::instance()->shorthash(digest, buf);
}
std::vector< byte_t >
OutboundMessage::XMIT() const
{
std::vector< byte_t > xmit{
LLARP_PROTO_VERSION, Command::eXMIT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
htobe16buf(xmit.data() + 2, m_Size);
htobe64buf(xmit.data() + 4, m_MsgID);
std::copy(digest.begin(), digest.end(), std::back_inserter(xmit));
return xmit;
}
void
OutboundMessage::Completed()
{
if(m_Completed)
{
m_Completed(ILinkSession::DeliveryStatus::eDeliverySuccess);
}
m_Completed = nullptr;
}
bool
OutboundMessage::ShouldFlush(llarp_time_t now) const
{
return now - m_LastFlush >= Session::TXFlushInterval;
}
void
OutboundMessage::Ack(byte_t bitmask)
{
m_Acks = std::bitset< 8 >(bitmask);
}
void
OutboundMessage::FlushUnAcked(
std::function< void(const llarp_buffer_t &) > sendpkt, llarp_time_t now)
{
uint16_t idx = 0;
while(idx < m_Size)
{
if(not m_Acks[idx / FragmentSize])
{
std::vector< byte_t > frag{LLARP_PROTO_VERSION,
Command::eDATA,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0};
htobe16buf(frag.data() + 2, idx);
htobe64buf(frag.data() + 4, m_MsgID);
if(idx + FragmentSize < m_Size)
std::copy(m_Data.begin() + idx, m_Data.begin() + idx + FragmentSize,
std::back_inserter(frag));
else
std::copy(m_Data.begin() + idx, m_Data.begin() + m_Size,
std::back_inserter(frag));
const llarp_buffer_t pkt(frag);
sendpkt(pkt);
}
idx += FragmentSize;
}
m_LastFlush = now;
}
bool
OutboundMessage::IsTransmitted() const
{
for(uint16_t idx = 0; idx < m_Size; idx += FragmentSize)
{
if(!m_Acks.test(idx / FragmentSize))
return false;
}
return true;
}
bool
OutboundMessage::IsTimedOut(const llarp_time_t now) const
{
// TODO: make configurable by outbound message deliverer
return now > m_StartedAt && now - m_StartedAt > Session::DeliveryTimeout;
}
void
OutboundMessage::InformTimeout()
{
if(m_Completed)
{
m_Completed(ILinkSession::DeliveryStatus::eDeliveryDropped);
}
m_Completed = nullptr;
}
InboundMessage::InboundMessage(uint64_t msgid, uint16_t sz, ShortHash h,
llarp_time_t now)
: m_Digset{std::move(h)}
, m_Size{sz}
, m_MsgID{msgid}
, m_LastActiveAt{now}
{
}
void
InboundMessage::HandleData(uint16_t idx, const llarp_buffer_t &buf,
llarp_time_t now)
{
if(idx + buf.sz > m_Data.size())
return;
auto *dst = m_Data.data() + idx;
std::copy_n(buf.base, buf.sz, dst);
m_Acks.set(idx / FragmentSize);
LogDebug("got fragment ", idx / FragmentSize, " of ", m_Size);
m_LastActiveAt = now;
}
std::vector< byte_t >
InboundMessage::ACKS() const
{
std::vector< byte_t > acks{LLARP_PROTO_VERSION,
Command::eACKS,
0,
0,
0,
0,
0,
0,
0,
0,
uint8_t{(uint8_t)m_Acks.to_ulong()}};
htobe64buf(acks.data() + 2, m_MsgID);
return acks;
}
bool
InboundMessage::IsCompleted() const
{
for(uint16_t idx = 0; idx < m_Size; idx += FragmentSize)
{
if(!m_Acks.test(idx / FragmentSize))
return false;
}
return true;
}
bool
InboundMessage::ShouldSendACKS(llarp_time_t now) const
{
return now - m_LastACKSent > 1000 || IsCompleted();
}
bool
InboundMessage::IsTimedOut(const llarp_time_t now) const
{
return now > m_LastActiveAt
&& now - m_LastActiveAt > Session::DeliveryTimeout;
}
void
InboundMessage::SendACKS(
std::function< void(const llarp_buffer_t &) > sendpkt, llarp_time_t now)
{
auto acks = ACKS();
AddRandomPadding(acks);
const llarp_buffer_t pkt(acks);
sendpkt(pkt);
m_LastACKSent = now;
}
bool
InboundMessage::Verify() const
{
ShortHash gotten;
const llarp_buffer_t buf(m_Data.data(), m_Size);
CryptoManager::instance()->shorthash(gotten, buf);
LogDebug("gotten=", gotten.ToHex());
if(gotten != m_Digset)
{
DumpBuffer(buf);
return false;
}
return true;
}
} // namespace iwp
} // namespace llarp

@ -0,0 +1,114 @@
#ifndef LLARP_IWP_MESSAGE_BUFFER_HPP
#define LLARP_IWP_MESSAGE_BUFFER_HPP
#include <vector>
#include <constants/link_layer.hpp>
#include <link/session.hpp>
#include <util/aligned.hpp>
#include <util/buffer.hpp>
#include <util/types.hpp>
namespace llarp
{
namespace iwp
{
enum Command
{
/// keep alive message
ePING = 0,
/// begin transission
eXMIT = 1,
/// fragment data
eDATA = 2,
/// acknolege fragments
eACKS = 3,
/// negative ack
eNACK = 4,
/// close session
eCLOS = 5
};
static constexpr size_t FragmentSize = 1024;
struct OutboundMessage
{
OutboundMessage() = default;
OutboundMessage(uint64_t msgid, const llarp_buffer_t &pkt,
llarp_time_t now,
ILinkSession::CompletionHandler handler);
AlignedBuffer< MAX_LINK_MSG_SIZE > m_Data;
uint16_t m_Size = 0;
uint64_t m_MsgID = 0;
std::bitset< MAX_LINK_MSG_SIZE / FragmentSize > m_Acks;
ILinkSession::CompletionHandler m_Completed;
llarp_time_t m_LastFlush = 0;
ShortHash digest;
llarp_time_t m_StartedAt = 0;
std::vector< byte_t >
XMIT() const;
void
Ack(byte_t bitmask);
void
FlushUnAcked(std::function< void(const llarp_buffer_t &) > sendpkt,
llarp_time_t now);
bool
ShouldFlush(llarp_time_t now) const;
void
Completed();
bool
IsTransmitted() const;
bool
IsTimedOut(llarp_time_t now) const;
void
InformTimeout();
};
struct InboundMessage
{
InboundMessage() = default;
InboundMessage(uint64_t msgid, uint16_t sz, ShortHash h,
llarp_time_t now);
AlignedBuffer< MAX_LINK_MSG_SIZE > m_Data;
ShortHash m_Digset;
uint16_t m_Size = 0;
uint64_t m_MsgID = 0;
llarp_time_t m_LastACKSent = 0;
llarp_time_t m_LastActiveAt = 0;
std::bitset< MAX_LINK_MSG_SIZE / FragmentSize > m_Acks;
void
HandleData(uint16_t idx, const llarp_buffer_t &buf, llarp_time_t now);
bool
IsCompleted() const;
bool
IsTimedOut(llarp_time_t now) const;
bool
Verify() const;
bool
ShouldSendACKS(llarp_time_t now) const;
void
SendACKS(std::function< void(const llarp_buffer_t &) > sendpkt,
llarp_time_t now);
std::vector< byte_t >
ACKS() const;
};
} // namespace iwp
} // namespace llarp
#endif

@ -1,155 +0,0 @@
#include <iwp/outermessage.hpp>
#include <memory>
namespace llarp
{
namespace iwp
{
std::array< byte_t, 6 > OuterMessage::obtain_flow_id_magic =
std::array< byte_t, 6 >{{'n', 'e', 't', 'i', 'd', '?'}};
std::array< byte_t, 6 > OuterMessage::give_flow_id_magic =
std::array< byte_t, 6 >{{'n', 'e', 't', 'i', 'd', '!'}};
OuterMessage::OuterMessage()
{
Clear();
}
OuterMessage::~OuterMessage() = default;
void
OuterMessage::Clear()
{
command = 0;
flow.Zero();
netid.Zero();
reject.fill(0);
N.Zero();
X.Zero();
Xsize = 0;
Zsig.Zero();
Zhash.Zero();
pubkey.Zero();
magic.fill(0);
uinteger = 0;
A.reset();
}
void
OuterMessage::CreateReject(const char* msg, llarp_time_t now,
const PubKey& pk)
{
Clear();
std::copy_n(msg, std::min(strlen(msg), reject.size()), reject.begin());
uinteger = now;
pubkey = pk;
}
bool
OuterMessage::Encode(llarp_buffer_t* buf) const
{
if(buf->size_left() < 2)
return false;
*buf->cur = command;
buf->cur++;
*buf->cur = '=';
buf->cur++;
switch(command)
{
case eOCMD_ObtainFlowID:
case eOCMD_GiveFlowID:
if(!buf->write(reject.begin(), reject.end()))
return false;
if(!buf->write(give_flow_id_magic.begin(), give_flow_id_magic.end()))
return false;
if(!buf->write(flow.begin(), flow.end()))
return false;
if(!buf->write(pubkey.begin(), pubkey.end()))
return false;
return buf->write(Zsig.begin(), Zsig.end());
default:
return false;
}
}
bool
OuterMessage::Decode(llarp_buffer_t* buf)
{
static constexpr size_t header_size = 2;
if(buf->size_left() < header_size)
return false;
command = *buf->cur;
++buf->cur;
if(*buf->cur != '=')
return false;
++buf->cur;
switch(command)
{
case eOCMD_ObtainFlowID:
if(!buf->read_into(magic.begin(), magic.end()))
return false;
if(!buf->read_into(netid.begin(), netid.end()))
return false;
if(!buf->read_uint64(uinteger))
return false;
if(!buf->read_into(pubkey.begin(), pubkey.end()))
return false;
if(buf->size_left() <= Zsig.size())
return false;
Xsize = buf->size_left() - Zsig.size();
if(!buf->read_into(X.begin(), X.begin() + Xsize))
return false;
return buf->read_into(Zsig.begin(), Zsig.end());
case eOCMD_GiveFlowID:
if(!buf->read_into(magic.begin(), magic.end()))
return false;
if(!buf->read_into(flow.begin(), flow.end()))
return false;
if(!buf->read_into(pubkey.begin(), pubkey.end()))
return false;
buf->cur += buf->size_left() - Zsig.size();
return buf->read_into(Zsig.begin(), Zsig.end());
case eOCMD_Reject:
if(!buf->read_into(reject.begin(), reject.end()))
return false;
if(!buf->read_uint64(uinteger))
return false;
if(!buf->read_into(pubkey.begin(), pubkey.end()))
return false;
buf->cur += buf->size_left() - Zsig.size();
return buf->read_into(Zsig.begin(), Zsig.end());
case eOCMD_SessionNegotiate:
if(!buf->read_into(flow.begin(), flow.end()))
return false;
if(!buf->read_into(pubkey.begin(), pubkey.end()))
return false;
if(!buf->read_uint64(uinteger))
return false;
if(buf->size_left() == Zsig.size() + 32)
{
A = std::make_unique< AlignedBuffer< 32 > >();
if(!buf->read_into(A->begin(), A->end()))
return false;
}
return buf->read_into(Zsig.begin(), Zsig.end());
case eOCMD_TransmitData:
if(!buf->read_into(flow.begin(), flow.end()))
return false;
if(!buf->read_into(N.begin(), N.end()))
return false;
if(buf->size_left() <= Zhash.size())
return false;
Xsize = buf->size_left() - Zhash.size();
if(!buf->read_into(X.begin(), X.begin() + Xsize))
return false;
return buf->read_into(Zhash.begin(), Zhash.end());
default:
return false;
}
}
} // namespace iwp
} // namespace llarp

@ -1,86 +0,0 @@
#ifndef LLARP_IWP_OUTERMESSAGE_HPP
#define LLARP_IWP_OUTERMESSAGE_HPP
#include <crypto/types.hpp>
#include <router_contact.hpp>
#include <util/aligned.hpp>
#include <array>
namespace llarp
{
namespace iwp
{
using FlowID_t = AlignedBuffer< 32 >;
using OuterCommand_t = byte_t;
constexpr OuterCommand_t eOCMD_ObtainFlowID = 'O';
constexpr OuterCommand_t eOCMD_GiveFlowID = 'G';
constexpr OuterCommand_t eOCMD_Reject = 'R';
constexpr OuterCommand_t eOCMD_SessionNegotiate = 'S';
constexpr OuterCommand_t eOCMD_TransmitData = 'D';
using InnerCommand_t = byte_t;
constexpr InnerCommand_t eICMD_KeepAlive = 'k';
constexpr InnerCommand_t eICMD_KeepAliveAck = 'l';
constexpr InnerCommand_t eICMD_Congestion = 'c';
constexpr InnerCommand_t eICMD_AntiCongestion = 'd';
constexpr InnerCommand_t eICMD_Transmit = 't';
constexpr InnerCommand_t eICMD_Ack = 'a';
constexpr InnerCommand_t eICMD_RotateKeys = 'r';
constexpr InnerCommand_t eICMD_UpgradeProtocol = 'u';
constexpr InnerCommand_t eICMD_VersionUpgrade = 'v';
struct OuterMessage
{
// required members
byte_t command;
FlowID_t flow;
OuterMessage();
~OuterMessage();
// static members
static std::array< byte_t, 6 > obtain_flow_id_magic;
static std::array< byte_t, 6 > give_flow_id_magic;
void
CreateReject(const char *msg, llarp_time_t now, const PubKey &pk);
// optional members follow
std::array< byte_t, 6 > magic;
NetID netid;
// either timestamp or counter
uint64_t uinteger;
std::array< byte_t, 14 > reject;
AlignedBuffer< 24 > N;
PubKey pubkey;
std::unique_ptr< AlignedBuffer< 32 > > A;
static constexpr size_t ipv6_mtu = 1280;
static constexpr size_t overhead_size = 16 + 24 + 32;
static constexpr size_t payload_size = ipv6_mtu - overhead_size;
AlignedBuffer< payload_size > X;
size_t Xsize;
ShortHash Zhash;
Signature Zsig;
/// encode to buffer
bool
Encode(llarp_buffer_t *buf) const;
/// decode from buffer
bool
Decode(llarp_buffer_t *buf);
/// clear members
void
Clear();
};
} // namespace iwp
} // namespace llarp
#endif

@ -0,0 +1,685 @@
#include <iwp/session.hpp>
#include <util/memfn.hpp>
#include <messages/link_intro.hpp>
#include <messages/discard.hpp>
namespace llarp
{
namespace iwp
{
static constexpr size_t PacketOverhead = HMACSIZE + TUNNONCESIZE;
void
AddRandomPadding(std::vector< byte_t >& pkt, size_t min, size_t variance)
{
const auto sz = pkt.size();
const size_t randpad = min + randint() % variance;
pkt.resize(sz + randpad);
CryptoManager::instance()->randbytes(pkt.data() + sz, randpad);
}
Session::Session(LinkLayer* p, RouterContact rc, AddressInfo ai)
: m_State{State::Initial}
, m_Inbound{false}
, m_Parent{p}
, m_CreatedAt{p->Now()}
, m_RemoteAddr{ai}
, m_ChosenAI{std::move(ai)}
, m_RemoteRC{std::move(rc)}
{
token.Zero();
GotLIM = util::memFn(&Session::GotOutboundLIM, this);
}
Session::Session(LinkLayer* p, Addr from)
: m_State{State::Initial}
, m_Inbound{true}
, m_Parent{p}
, m_CreatedAt{p->Now()}
, m_RemoteAddr{from}
{
token.Randomize();
GotLIM = util::memFn(&Session::GotInboundLIM, this);
}
void
Session::Send_LL(const llarp_buffer_t& pkt)
{
LogDebug("send ", pkt.sz, " to ", m_RemoteAddr);
m_Parent->SendTo_LL(m_RemoteAddr, pkt);
m_LastTX = time_now_ms();
}
bool
Session::GotInboundLIM(const LinkIntroMessage* msg)
{
if(msg->rc.enckey != m_RemoteOnionKey)
{
LogError("key missmatch");
return false;
}
m_State = State::Ready;
GotLIM = util::memFn(&Session::GotRenegLIM, this);
m_RemoteRC = msg->rc;
m_Parent->MapAddr(m_RemoteRC.pubkey, this);
return m_Parent->SessionEstablished(this);
}
bool
Session::GotOutboundLIM(const LinkIntroMessage* msg)
{
if(msg->rc.pubkey != m_RemoteRC.pubkey)
{
LogError("ident key missmatch");
return false;
}
m_RemoteRC = msg->rc;
GotLIM = util::memFn(&Session::GotRenegLIM, this);
auto self = shared_from_this();
SendOurLIM([self](ILinkSession::DeliveryStatus st) {
if(st == ILinkSession::DeliveryStatus::eDeliverySuccess)
{
self->m_State = State::Ready;
self->m_Parent->MapAddr(self->m_RemoteRC.pubkey, self.get());
self->m_Parent->SessionEstablished(self.get());
}
});
return true;
}
void
Session::SendOurLIM(ILinkSession::CompletionHandler h)
{
LinkIntroMessage msg;
msg.rc = m_Parent->GetOurRC();
msg.N.Randomize();
msg.P = 60000;
if(not msg.Sign(m_Parent->Sign))
{
LogError("failed to sign our RC for ", m_RemoteAddr);
return;
}
AlignedBuffer< LinkIntroMessage::MaxSize > data;
llarp_buffer_t buf(data);
if(not msg.BEncode(&buf))
{
LogError("failed to encode LIM for ", m_RemoteAddr);
}
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
if(!SendMessageBuffer(buf, h))
{
LogError("failed to send LIM to ", m_RemoteAddr);
}
LogDebug("sent LIM to ", m_RemoteAddr);
}
void
Session::EncryptAndSend(const llarp_buffer_t& data)
{
std::vector< byte_t > pkt;
pkt.resize(data.sz + PacketOverhead);
CryptoManager::instance()->randbytes(pkt.data(), pkt.size());
llarp_buffer_t pktbuf(pkt);
pktbuf.base += PacketOverhead;
pktbuf.cur = pktbuf.base;
pktbuf.sz -= PacketOverhead;
byte_t* nonce_ptr = pkt.data() + HMACSIZE;
CryptoManager::instance()->xchacha20_alt(pktbuf, data, m_SessionKey,
nonce_ptr);
pktbuf.base = nonce_ptr;
pktbuf.sz = data.sz + 32;
CryptoManager::instance()->hmac(pkt.data(), pktbuf, m_SessionKey);
pktbuf.base = pkt.data();
pktbuf.cur = pkt.data();
pktbuf.sz = pkt.size();
Send_LL(pktbuf);
}
void
Session::Close()
{
if(m_State == State::Closed)
return;
std::vector< byte_t > close_msg = {LLARP_PROTO_VERSION, Command::eCLOS};
AddRandomPadding(close_msg);
const llarp_buffer_t buf(close_msg);
EncryptAndSend(buf);
if(m_State == State::Ready)
m_Parent->UnmapAddr(m_RemoteAddr);
m_State = State::Closed;
LogInfo("closing connection to ", m_RemoteAddr);
}
bool
Session::SendMessageBuffer(const llarp_buffer_t& buf,
ILinkSession::CompletionHandler completed)
{
const auto now = m_Parent->Now();
const auto msgid = m_TXID++;
auto& msg =
m_TXMsgs.emplace(msgid, OutboundMessage{msgid, buf, now, completed})
.first->second;
auto xmit = msg.XMIT();
AddRandomPadding(xmit);
const llarp_buffer_t pkt(xmit);
EncryptAndSend(pkt);
msg.FlushUnAcked(util::memFn(&Session::EncryptAndSend, this), now);
LogDebug("send message ", msgid);
return true;
}
void
Session::Pump()
{
const auto now = m_Parent->Now();
if(m_State == State::Ready || m_State == State::LinkIntro)
{
if(ShouldPing())
SendKeepAlive();
for(auto& item : m_RXMsgs)
{
if(item.second.ShouldSendACKS(now))
{
item.second.SendACKS(util::memFn(&Session::EncryptAndSend, this),
now);
}
}
for(auto& item : m_TXMsgs)
{
if(item.second.ShouldFlush(now))
{
item.second.FlushUnAcked(
util::memFn(&Session::EncryptAndSend, this), now);
}
}
}
}
bool
Session::GotRenegLIM(const LinkIntroMessage* lim)
{
LogDebug("renegotiate session on ", m_RemoteAddr);
return m_Parent->SessionRenegotiate(lim->rc, m_RemoteRC);
}
bool
Session::RenegotiateSession()
{
SendOurLIM();
return true;
}
bool
Session::ShouldPing() const
{
if(m_State == State::Ready)
{
const auto now = m_Parent->Now();
return now - m_LastTX > PingInterval;
}
return false;
}
util::StatusObject
Session::ExtractStatus() const
{
return {{"remoteAddr", m_RemoteAddr.ToString()},
{"remoteRC", m_RemoteRC.ExtractStatus()}};
}
bool
Session::TimedOut(llarp_time_t now) const
{
if(m_State == State::Ready || m_State == State::LinkIntro)
{
return now > m_LastRX && now - m_LastRX > SessionAliveTimeout;
}
return now - m_CreatedAt > SessionAliveTimeout;
}
void
Session::Tick(llarp_time_t now)
{
// remove pending outbound messsages that timed out
// inform waiters
{
auto itr = m_TXMsgs.begin();
while(itr != m_TXMsgs.end())
{
if(itr->second.IsTimedOut(now))
{
itr->second.InformTimeout();
itr = m_TXMsgs.erase(itr);
}
else
++itr;
}
}
{
// remove pending inbound messages that timed out
auto itr = m_RXMsgs.begin();
while(itr != m_RXMsgs.end())
{
if(itr->second.IsTimedOut(now))
{
itr = m_RXMsgs.erase(itr);
}
else
++itr;
}
}
{
// decay replay window
auto itr = m_ReplayFilter.begin();
while(itr != m_ReplayFilter.end())
{
if(itr->second + ReplayWindow <= now)
{
itr = m_ReplayFilter.erase(itr);
}
else
++itr;
}
}
}
using Introduction = AlignedBuffer< 64 >;
void
Session::GenerateAndSendIntro()
{
Introduction intro;
TunnelNonce N;
N.Randomize();
if(not CryptoManager::instance()->transport_dh_client(
m_SessionKey, m_ChosenAI.pubkey,
m_Parent->RouterEncryptionSecret(), N))
{
LogError("failed to transport_dh_client on outbound session to ",
m_RemoteAddr);
return;
}
const auto pk = m_Parent->RouterEncryptionSecret().toPublic();
std::copy_n(pk.begin(), pk.size(), intro.begin());
std::copy(N.begin(), N.end(), intro.begin() + PubKey::SIZE);
LogDebug("pk=", pk.ToHex(), " N=", N.ToHex(),
" remote-pk=", m_ChosenAI.pubkey.ToHex());
std::vector< byte_t > req;
std::copy_n(intro.begin(), intro.size(), std::back_inserter(req));
AddRandomPadding(req);
const llarp_buffer_t buf(req);
Send_LL(buf);
m_State = State::Introduction;
LogDebug("sent intro to ", m_RemoteAddr);
}
void
Session::HandleCreateSessionRequest(const llarp_buffer_t& buf)
{
std::vector< byte_t > result;
if(not DecryptMessage(buf, result))
{
LogError("failed to decrypt session request from ", m_RemoteAddr);
return;
}
if(result.size() < token.size())
{
LogError("bad session request size, ", result.size(), " < ",
token.size(), " from ", m_RemoteAddr);
return;
}
if(not std::equal(result.begin(), result.begin() + token.size(),
token.begin()))
{
LogError("token missmatch from ", m_RemoteAddr);
return;
}
m_LastRX = m_Parent->Now();
m_State = State::LinkIntro;
SendOurLIM();
}
void
Session::HandleGotIntro(const llarp_buffer_t& buf)
{
if(buf.sz < Introduction::SIZE)
{
LogWarn("intro too small from ", m_RemoteAddr);
return;
}
TunnelNonce N;
std::copy_n(buf.base, PubKey::SIZE, m_RemoteOnionKey.begin());
std::copy_n(buf.base + PubKey::SIZE, TunnelNonce::SIZE, N.begin());
const PubKey pk = m_Parent->TransportSecretKey().toPublic();
LogDebug("got intro: remote-pk=", m_RemoteOnionKey.ToHex(),
" N=", N.ToHex(), " local-pk=", pk.ToHex(), " sz=", buf.sz);
if(not CryptoManager::instance()->transport_dh_server(
m_SessionKey, m_RemoteOnionKey, m_Parent->TransportSecretKey(), N))
{
LogError("failed to transport_dh_server on inbound intro from ",
m_RemoteAddr);
return;
}
std::vector< byte_t > reply;
std::copy_n(token.begin(), token.size(), std::back_inserter(reply));
AddRandomPadding(reply);
const llarp_buffer_t pkt(reply);
m_LastRX = m_Parent->Now();
EncryptAndSend(pkt);
LogDebug("sent intro ack to ", m_RemoteAddr);
m_State = State::Introduction;
}
void
Session::HandleGotIntroAck(const llarp_buffer_t& buf)
{
std::vector< byte_t > reply;
if(not DecryptMessage(buf, reply))
{
LogError("intro ack decrypt failed from ", m_RemoteAddr);
return;
}
if(reply.size() < token.size())
{
LogError("bad intro ack size ", reply.size(), " < ", token.size(),
" from ", m_RemoteAddr);
return;
}
m_LastRX = m_Parent->Now();
std::copy_n(reply.begin(), token.size(), token.begin());
const llarp_buffer_t pkt(token);
EncryptAndSend(pkt);
LogDebug("sent session request to ", m_RemoteAddr);
m_State = State::LinkIntro;
}
bool
Session::DecryptMessage(const llarp_buffer_t& buf,
std::vector< byte_t >& result)
{
if(buf.sz <= PacketOverhead)
{
LogError("packet too small ", buf.sz);
return false;
}
ShortHash H;
llarp_buffer_t curbuf(buf.base, buf.sz);
curbuf.base += ShortHash::SIZE;
curbuf.sz -= ShortHash::SIZE;
if(not CryptoManager::instance()->hmac(H.data(), curbuf, m_SessionKey))
{
LogError("failed to caclulate keyed hash for ", m_RemoteAddr);
return false;
}
const ShortHash expected{buf.base};
if(H != expected)
{
LogError("keyed hash missmatch ", H, " != ", expected, " from ",
m_RemoteAddr, " state=", int(m_State), " size=", buf.sz);
return false;
}
const byte_t* nonce_ptr = curbuf.base;
curbuf.base += 32;
curbuf.sz -= 32;
result.resize(buf.sz - PacketOverhead);
const llarp_buffer_t outbuf(result);
LogDebug("decrypt: ", result.size(), " bytes from ", m_RemoteAddr);
return CryptoManager::instance()->xchacha20_alt(outbuf, curbuf,
m_SessionKey, nonce_ptr);
}
void
Session::Start()
{
if(m_Inbound)
return;
GenerateAndSendIntro();
}
void
Session::HandleSessionData(const llarp_buffer_t& buf)
{
std::vector< byte_t > result;
if(not DecryptMessage(buf, result))
{
LogError("failed to decrypt session data from ", m_RemoteAddr);
return;
}
if(result[0] != LLARP_PROTO_VERSION)
{
LogError("protocol version missmatch ", int(result[0]),
" != ", LLARP_PROTO_VERSION);
return;
}
LogDebug("command ", int(result[1]), " from ", m_RemoteAddr);
switch(result[1])
{
case Command::eXMIT:
HandleXMIT(std::move(result));
return;
case Command::eDATA:
HandleDATA(std::move(result));
return;
case Command::eACKS:
HandleACKS(std::move(result));
return;
case Command::ePING:
HandlePING(std::move(result));
return;
case Command::eNACK:
HandleNACK(std::move(result));
return;
case Command::eCLOS:
HandleCLOS(std::move(result));
return;
}
LogError("invalid command ", int(result[1]));
}
void
Session::HandleNACK(std::vector< byte_t > data)
{
if(data.size() < 10)
{
LogError("short nack from ", m_RemoteAddr);
return;
}
uint64_t txid = bufbe64toh(data.data() + 2);
LogDebug("got nack on ", txid, " from ", m_RemoteAddr);
auto itr = m_TXMsgs.find(txid);
if(itr != m_TXMsgs.end())
{
auto xmit = itr->second.XMIT();
AddRandomPadding(xmit);
const llarp_buffer_t pkt(xmit);
EncryptAndSend(pkt);
}
m_LastRX = m_Parent->Now();
}
void
Session::HandleXMIT(std::vector< byte_t > data)
{
if(data.size() < 44)
{
LogError("short XMIT from ", m_RemoteAddr, " ", data.size(), " < 44");
return;
}
uint16_t sz = bufbe16toh(data.data() + 2);
uint64_t rxid = bufbe64toh(data.data() + 4);
ShortHash h{data.data() + 12};
LogDebug("rxid=", rxid, " sz=", sz, " h=", h.ToHex());
m_LastRX = m_Parent->Now();
{
// check for replay
auto itr = m_ReplayFilter.find(rxid);
if(itr != m_ReplayFilter.end())
{
LogDebug("duplicate rxid=", rxid, " from ", m_RemoteAddr);
return;
}
}
{
auto itr = m_RXMsgs.find(rxid);
if(itr == m_RXMsgs.end())
m_RXMsgs.emplace(
rxid, InboundMessage{rxid, sz, std::move(h), m_Parent->Now()});
else
LogDebug("got duplicate xmit on ", rxid, " from ", m_RemoteAddr);
}
}
void
Session::HandleDATA(std::vector< byte_t > data)
{
if(data.size() <= 12)
{
LogError("short DATA from ", m_RemoteAddr, " ", data.size());
return;
}
m_LastRX = m_Parent->Now();
uint16_t sz = bufbe16toh(data.data() + 2);
uint64_t rxid = bufbe64toh(data.data() + 4);
auto itr = m_RXMsgs.find(rxid);
if(itr == m_RXMsgs.end())
{
LogDebug("no rxid=", rxid, " for ", m_RemoteAddr);
std::vector< byte_t > nack = {
LLARP_PROTO_VERSION, Command::eNACK, 0, 0, 0, 0, 0, 0, 0, 0};
htobe64buf(nack.data() + 2, rxid);
AddRandomPadding(nack);
const llarp_buffer_t nackbuf(nack);
EncryptAndSend(nackbuf);
return;
}
{
const llarp_buffer_t buf(data.data() + 12, data.size() - 12);
itr->second.HandleData(sz, buf, m_Parent->Now());
}
if(itr->second.IsCompleted())
{
itr->second.SendACKS(util::memFn(&Session::EncryptAndSend, this),
m_Parent->Now());
if(itr->second.Verify())
{
auto msg = std::move(itr->second);
const llarp_buffer_t buf(msg.m_Data.data(), msg.m_Size);
m_Parent->HandleMessage(this, buf);
m_ReplayFilter.emplace(itr->first, m_Parent->Now());
}
else
{
LogError("hash missmatch for message ", itr->first);
}
m_RXMsgs.erase(itr);
}
}
void
Session::HandleACKS(std::vector< byte_t > data)
{
if(data.size() < 11)
{
LogError("short ACKS from ", m_RemoteAddr, " ", data.size(), " < 11");
return;
}
const auto now = m_Parent->Now();
m_LastRX = now;
uint64_t txid = bufbe64toh(data.data() + 2);
auto itr = m_TXMsgs.find(txid);
if(itr == m_TXMsgs.end())
{
LogDebug("no txid=", txid, " for ", m_RemoteAddr);
return;
}
itr->second.Ack(data[10]);
if(itr->second.IsTransmitted())
{
LogDebug("sent message ", itr->first);
itr->second.Completed();
itr = m_TXMsgs.erase(itr);
}
else
{
itr->second.FlushUnAcked(util::memFn(&Session::EncryptAndSend, this),
now);
}
}
void Session::HandleCLOS(std::vector< byte_t >)
{
LogInfo("remote closed by ", m_RemoteAddr);
Close();
}
void Session::HandlePING(std::vector< byte_t >)
{
m_LastRX = m_Parent->Now();
}
bool
Session::SendKeepAlive()
{
if(m_State == State::Ready)
{
std::vector< byte_t > ping{LLARP_PROTO_VERSION, Command::ePING};
const llarp_buffer_t buf(ping);
EncryptAndSend(buf);
return true;
}
return false;
}
bool
Session::IsEstablished() const
{
return m_State == State::Ready;
}
void
Session::Recv_LL(const llarp_buffer_t& buf)
{
switch(m_State)
{
case State::Initial:
if(m_Inbound)
{
// initial data
// enter introduction phase
HandleGotIntro(buf);
}
else
{
// this case should never happen
::abort();
}
break;
case State::Introduction:
if(m_Inbound)
{
// we are replying to an intro ack
HandleCreateSessionRequest(buf);
}
else
{
// we got an intro ack
// send a session request
HandleGotIntroAck(buf);
}
break;
case State::LinkIntro:
default:
HandleSessionData(buf);
break;
}
}
} // namespace iwp
} // namespace llarp

@ -0,0 +1,219 @@
#ifndef LLARP_IWP_SESSION_HPP
#define LLARP_IWP_SESSION_HPP
#include <link/session.hpp>
#include <iwp/linklayer.hpp>
#include <iwp/message_buffer.hpp>
namespace llarp
{
namespace iwp
{
void
AddRandomPadding(std::vector< byte_t >& pkt, size_t min = 16,
size_t variance = 16);
struct Session : public ILinkSession,
public std::enable_shared_from_this< Session >
{
/// Time how long we try delivery for
static constexpr llarp_time_t DeliveryTimeout = 5000;
/// How long to keep a replay window for
static constexpr llarp_time_t ReplayWindow = (DeliveryTimeout * 3) / 2;
/// Time how long we wait to recieve a message
static constexpr llarp_time_t RecievalTimeout = (DeliveryTimeout * 8) / 5;
/// How often to acks RX messages
static constexpr llarp_time_t ACKResendInterval = 500;
/// How often to retransmit TX fragments
static constexpr llarp_time_t TXFlushInterval =
(ACKResendInterval * 3) / 2;
/// How often we send a keepalive
static constexpr llarp_time_t PingInterval = 2000;
/// How long we wait for a session to die with no tx from them
static constexpr llarp_time_t SessionAliveTimeout =
(PingInterval * 13) / 3;
/// outbound session
Session(LinkLayer* parent, RouterContact rc, AddressInfo ai);
/// inbound session
Session(LinkLayer* parent, Addr from);
~Session() = default;
std::shared_ptr< ILinkSession >
BorrowSelf() override
{
return shared_from_this();
}
void
Pump() override;
void
Tick(llarp_time_t now) override;
bool
SendMessageBuffer(const llarp_buffer_t& buf,
CompletionHandler resultHandler) override;
void
Send_LL(const llarp_buffer_t& pkt);
void
EncryptAndSend(const llarp_buffer_t& data);
void
Start() override;
void
Close() override;
void
Recv_LL(const llarp_buffer_t& pkt) override;
bool
SendKeepAlive() override;
bool
IsEstablished() const override;
bool
TimedOut(llarp_time_t now) const override;
PubKey
GetPubKey() const override
{
return m_RemoteRC.pubkey;
}
Addr
GetRemoteEndpoint() const override
{
return m_RemoteAddr;
}
RouterContact
GetRemoteRC() const override
{
return m_RemoteRC;
}
size_t
SendQueueBacklog() const override
{
return m_TXMsgs.size();
}
ILinkLayer*
GetLinkLayer() const override
{
return m_Parent;
}
bool
RenegotiateSession() override;
bool
ShouldPing() const override;
util::StatusObject
ExtractStatus() const override;
private:
enum class State
{
/// we have no data recv'd
Initial,
/// we are in introduction phase
Introduction,
/// we sent our LIM
LinkIntro,
/// handshake done and LIM has been obtained
Ready,
/// we are closed now
Closed
};
State m_State;
/// are we inbound session ?
const bool m_Inbound;
/// parent link layer
LinkLayer* const m_Parent;
const llarp_time_t m_CreatedAt;
const Addr m_RemoteAddr;
AddressInfo m_ChosenAI;
/// remote rc
RouterContact m_RemoteRC;
/// session key
SharedSecret m_SessionKey;
/// session token
AlignedBuffer< 24 > token;
PubKey m_RemoteOnionKey;
llarp_time_t m_LastTX = 0;
llarp_time_t m_LastRX = 0;
uint64_t m_TXID = 0;
std::unordered_map< uint64_t, InboundMessage > m_RXMsgs;
std::unordered_map< uint64_t, OutboundMessage > m_TXMsgs;
/// maps rxid to time recieved
std::unordered_map< uint64_t, llarp_time_t > m_ReplayFilter;
void
HandleGotIntro(const llarp_buffer_t& buf);
void
HandleGotIntroAck(const llarp_buffer_t& buf);
void
HandleCreateSessionRequest(const llarp_buffer_t& buf);
void
HandleAckSession(const llarp_buffer_t& buf);
void
HandleSessionData(const llarp_buffer_t& buf);
bool
DecryptMessage(const llarp_buffer_t& buf, std::vector< byte_t >& result);
void
GenerateAndSendIntro();
bool
GotInboundLIM(const LinkIntroMessage* msg);
bool
GotOutboundLIM(const LinkIntroMessage* msg);
bool
GotRenegLIM(const LinkIntroMessage* msg);
void
SendOurLIM(ILinkSession::CompletionHandler h = nullptr);
void
HandleXMIT(std::vector< byte_t > msg);
void
HandleDATA(std::vector< byte_t > msg);
void
HandleACKS(std::vector< byte_t > msg);
void
HandleNACK(std::vector< byte_t > msg);
void
HandlePING(std::vector< byte_t > msg);
void
HandleCLOS(std::vector< byte_t > msg);
};
} // namespace iwp
} // namespace llarp
#endif

@ -0,0 +1,52 @@
#include <link/factory.hpp>
#include <iwp/iwp.hpp>
#include <utp/utp.hpp>
namespace llarp
{
LinkFactory::LinkType
LinkFactory::TypeFromName(string_view str)
{
if(str == "utp")
return LinkType::eLinkUTP;
if(str == "iwp")
return LinkType::eLinkIWP;
if(str == "mempipe")
return LinkType::eLinkMempipe;
return LinkType::eLinkUnknown;
}
std::string
LinkFactory::NameFromType(LinkFactory::LinkType tp)
{
switch(tp)
{
case LinkType::eLinkUTP:
return "utp";
case LinkType::eLinkIWP:
return "iwp";
case LinkType::eLinkMempipe:
return "mempipe";
default:
return "unspec";
}
}
LinkFactory::Factory
LinkFactory::Obtain(LinkFactory::LinkType tp, bool permitInbound)
{
switch(tp)
{
case LinkType::eLinkUTP:
if(permitInbound)
return llarp::utp::NewInboundLink;
return llarp::utp::NewOutboundLink;
case LinkType::eLinkIWP:
if(permitInbound)
return llarp::iwp::NewInboundLink;
return llarp::iwp::NewOutboundLink;
default:
return nullptr;
}
}
} // namespace llarp

@ -0,0 +1,43 @@
#ifndef LLARP_LINK_FACTORY_HPP
#define LLARP_LINK_FACTORY_HPP
#include <util/string_view.hpp>
#include <functional>
#include <link/server.hpp>
namespace llarp
{
/// LinkFactory is responsible for returning std::functions that create the
/// link layer types
struct LinkFactory
{
enum class LinkType
{
eLinkUTP,
eLinkIWP,
eLinkMempipe,
eLinkUnknown
};
using Factory = std::function< LinkLayer_ptr(
const SecretKey&, GetRCFunc, LinkMessageHandler, SignBufferFunc,
SessionEstablishedHandler, SessionRenegotiateHandler, TimeoutHandler,
SessionClosedHandler) >;
/// get link type by name string
/// if invalid returns eLinkUnspec
static LinkType
TypeFromName(string_view name);
/// turns a link type into a string representation
static std::string
NameFromType(LinkType t);
/// obtain a link factory of a certain type
static Factory
Obtain(LinkType t, bool permitInbound);
};
} // namespace llarp
#endif

@ -346,21 +346,20 @@ namespace llarp
if(stopping)
return nullptr;
for(const auto &link : inboundLinks)
for(const auto &link : outboundLinks)
{
if(link->HasSessionTo(remote))
{
return link;
}
}
for(const auto &link : outboundLinks)
for(const auto &link : inboundLinks)
{
if(link->HasSessionTo(remote))
{
return link;
}
}
return nullptr;
}

@ -129,7 +129,7 @@ namespace llarp
auto itr = m_AuthedLinks.begin();
while(itr != m_AuthedLinks.end())
{
if(itr->second.get() && !itr->second->TimedOut(_now))
if(not itr->second->TimedOut(_now))
{
itr->second->Pump();
++itr;
@ -149,7 +149,7 @@ namespace llarp
auto itr = m_Pending.begin();
while(itr != m_Pending.end())
{
if(itr->second.get() && !itr->second->TimedOut(_now))
if(not itr->second->TimedOut(_now))
{
itr->second->Pump();
++itr;
@ -157,7 +157,12 @@ namespace llarp
else
{
LogInfo("pending session at ", itr->first, " timed out");
itr->second->Close();
// defer call so we can acquire mutexes later
auto self = itr->second->BorrowSelf();
m_Logic->queue_func([&, self]() {
this->HandleTimeout(self.get());
self->Close();
});
itr = m_Pending.erase(itr);
}
}
@ -175,6 +180,7 @@ namespace llarp
{
if(m_AuthedLinks.count(pk) > MaxSessionsPerKey)
{
LogWarn("too many session for ", pk);
s->Close();
return false;
}

@ -103,7 +103,7 @@ namespace llarp
llarp_ev_udp_sendto(&m_udp, to, pkt);
}
bool
virtual bool
Configure(llarp_ev_loop_ptr loop, const std::string& ifname, int af,
uint16_t port);
@ -125,7 +125,7 @@ namespace llarp
virtual bool
Start(std::shared_ptr< llarp::Logic > l);
void
virtual void
Stop();
virtual const char*
@ -140,11 +140,11 @@ namespace llarp
void
KeepAliveSessionTo(const RouterID& remote);
bool
virtual bool
SendTo(const RouterID& remote, const llarp_buffer_t& buf,
ILinkSession::CompletionHandler completed);
bool
virtual bool
GetOurAddressInfo(AddressInfo& addr) const;
bool
@ -186,7 +186,7 @@ namespace llarp
bool
GenEphemeralKeys();
bool
virtual bool
MapAddr(const RouterID& pk, ILinkSession* s);
void
@ -200,6 +200,12 @@ namespace llarp
SessionClosedHandler SessionClosed;
SessionRenegotiateHandler SessionRenegotiate;
std::shared_ptr< Logic >
logic()
{
return m_Logic;
}
bool
operator<(const ILinkLayer& other) const
{

@ -25,9 +25,14 @@ namespace llarp
eDeliveryDropped = 1
};
/// equiv of shared_from_this but for the interface type so
/// that each implementation can use shared_from_this
virtual std::shared_ptr< ILinkSession >
BorrowSelf() = 0;
/// hook for utp for when we have established a connection
virtual void
OnLinkEstablished(ILinkLayer *p) = 0;
OnLinkEstablished(ILinkLayer *){};
/// called every event loop tick
virtual void
@ -50,6 +55,13 @@ namespace llarp
virtual void
Close() = 0;
/// recv packet on low layer
/// not used by utp
virtual void
Recv_LL(const llarp_buffer_t &)
{
}
/// send a keepalive to the remote endpoint
virtual bool
SendKeepAlive() = 0;

@ -205,7 +205,8 @@ namespace llarp
publishData(const std::vector< std::string > &toSend,
const std::string &host, short port)
{
struct addrinfo hints, *addrs;
struct addrinfo hints;
struct addrinfo *addrs;
bzero(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

@ -26,6 +26,10 @@ typedef unsigned int in_addr_t;
#include <sys/socket.h>
#endif
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <sys/types.h>
bool

@ -170,18 +170,13 @@ namespace llarp
{
const llarp_buffer_t buf(msg.first);
auto callback = msg.second;
if(!_linkManager->SendTo(
remote, buf, [=](ILinkSession::DeliveryStatus status) {
if(status == ILinkSession::DeliveryStatus::eDeliverySuccess)
DoCallback(callback, SendStatus::Success);
else
DoCallback(callback, SendStatus::Congestion);
}))
{
DoCallback(callback, SendStatus::Congestion);
return false;
}
return true;
return _linkManager->SendTo(
remote, buf, [=](ILinkSession::DeliveryStatus status) {
if(status == ILinkSession::DeliveryStatus::eDeliverySuccess)
DoCallback(callback, SendStatus::Success);
else
DoCallback(callback, SendStatus::Congestion);
});
}
bool

@ -226,6 +226,8 @@ namespace llarp
bool
OutboundSessionMaker::ShouldConnectTo(const RouterID &router) const
{
if(router == us)
return false;
size_t numPending = 0;
{
util::Lock lock(&_mutex);

@ -61,6 +61,12 @@ namespace llarp
std::shared_ptr< Logic > logic, llarp_nodedb *nodedb,
std::shared_ptr< llarp::thread::ThreadPool > threadpool);
void
SetOurRouter(RouterID r)
{
us = std::move(r);
}
/// always maintain this many connections to other routers
size_t minConnectedRouters = 4;
/// hard upperbound limit on the number of router to router connections
@ -108,6 +114,7 @@ namespace llarp
std::shared_ptr< Logic > _logic;
llarp_nodedb *_nodedb;
std::shared_ptr< llarp::thread::ThreadPool > _threadpool;
RouterID us;
};
} // namespace llarp

@ -133,6 +133,7 @@ namespace llarp
if(not rc.Verify(_dht->impl->Now()))
{
LogWarn("RC for ", RouterID(rc.pubkey), " is invalid");
return false;
}

@ -336,10 +336,11 @@ namespace llarp
_encryption = nextOnionKey;
}
}
nextRC.last_updated = Now();
if(!nextRC.Sign(identity()))
return false;
_rc = nextRC;
if(!nextRC.Verify(time_now_ms(), false))
return false;
_rc = std::move(nextRC);
// propagate RC by renegotiating sessions
ForEachPeer([](ILinkSession *s) {
if(s->RenegotiateSession())
@ -368,9 +369,17 @@ namespace llarp
// reset netid in our rc
_rc.netID = llarp::NetID();
}
const auto linktypename = conf->router.defaultLinkProto();
_defaultLinkType = LinkFactory::TypeFromName(linktypename);
if(_defaultLinkType == LinkFactory::LinkType::eLinkUnknown)
{
LogError("failed to set link type to '", linktypename,
"' as that is invalid");
return false;
}
// IWP config
m_OutboundPort = conf->iwp_links.outboundPort();
m_OutboundPort = std::get< LinksConfig::Port >(conf->links.outboundLink());
// Router config
_rc.SetNick(conf->router.nickname());
_outboundSessionMaker.maxConnectedRouters =
@ -384,6 +393,11 @@ namespace llarp
publicOverride = conf->router.publicOverride();
ip4addr = conf->router.ip4addr();
if(!conf->router.blockBogons().value_or(true))
{
RouterContact::BlockBogons = false;
}
// Lokid Config
usingSNSeed = conf->lokid.usingSNSeed;
ident_keyfile = conf->lokid.ident_keyfile;
@ -393,7 +407,7 @@ namespace llarp
lokidRPCPassword = conf->lokid.lokidRPCPassword;
// TODO: add config flag for "is service node"
if(conf->iwp_links.servers().size())
if(conf->links.inboundLinks().size())
{
m_isServiceNode = true;
}
@ -481,15 +495,34 @@ namespace llarp
}
// create inbound links, if we are a service node
for(const auto &serverConfig : conf->iwp_links.servers())
for(const auto &serverConfig : conf->links.inboundLinks())
{
auto server = llarp::utp::NewInboundLink(
// get default factory
auto inboundLinkFactory = LinkFactory::Obtain(_defaultLinkType, true);
// for each option if provided ...
for(const auto &opt : std::get< LinksConfig::Options >(serverConfig))
{
// try interpreting it as a link type
const auto linktype = LinkFactory::TypeFromName(opt);
if(linktype != LinkFactory::LinkType::eLinkUnknown)
{
// override link factory if it's a valid link type
auto factory = LinkFactory::Obtain(linktype, true);
if(factory)
{
inboundLinkFactory = std::move(factory);
break;
}
}
}
auto server = inboundLinkFactory(
encryption(), util::memFn(&AbstractRouter::rc, this),
util::memFn(&AbstractRouter::HandleRecvLinkMessageBuffer, this),
util::memFn(&AbstractRouter::Sign, this),
util::memFn(&IOutboundSessionMaker::OnSessionEstablished,
&_outboundSessionMaker),
util::memFn(&AbstractRouter::CheckRenegotiateValid, this),
util::memFn(&AbstractRouter::Sign, this),
util::memFn(&IOutboundSessionMaker::OnConnectTimeout,
&_outboundSessionMaker),
util::memFn(&AbstractRouter::SessionClosed, this));
@ -500,9 +533,9 @@ namespace llarp
return false;
}
const auto &key = std::get< 0 >(serverConfig);
int af = std::get< 1 >(serverConfig);
uint16_t port = std::get< 2 >(serverConfig);
const auto &key = std::get< LinksConfig::Interface >(serverConfig);
int af = std::get< LinksConfig::AddressFamily >(serverConfig);
uint16_t port = std::get< LinksConfig::Port >(serverConfig);
if(!server->Configure(netloop(), key, af, port))
{
LogError("failed to bind inbound link on ", key, " port ", port);
@ -851,7 +884,7 @@ namespace llarp
ai.ip = *publicAddr.addr6();
ai.port = publicAddr.port();
}
if(IsBogon(ai.ip))
if(RouterContact::BlockBogons && IsBogon(ai.ip))
return;
_rc.addrs.push_back(ai);
if(ExitEnabled())
@ -879,7 +912,7 @@ namespace llarp
LogError("failed to save RC");
return false;
}
_outboundSessionMaker.SetOurRouter(pubkey());
if(!_linkManager.StartLinks(_logic))
{
LogWarn("One or more links failed to start.");
@ -1067,48 +1100,44 @@ namespace llarp
bool
Router::InitOutboundLinks()
{
using LinkFactory = std::function< LinkLayer_ptr(
const SecretKey &, GetRCFunc, LinkMessageHandler,
SessionEstablishedHandler, SessionRenegotiateHandler, SignBufferFunc,
TimeoutHandler, SessionClosedHandler) >;
const auto linkTypeName = LinkFactory::NameFromType(_defaultLinkType);
LogInfo("initialize outbound link: ", linkTypeName);
auto factory = LinkFactory::Obtain(_defaultLinkType, false);
if(factory == nullptr)
{
LogError("cannot initialize outbound link of type '", linkTypeName,
"' as it has no implementation");
return false;
}
auto link =
factory(encryption(), util::memFn(&AbstractRouter::rc, this),
util::memFn(&AbstractRouter::HandleRecvLinkMessageBuffer, this),
util::memFn(&AbstractRouter::Sign, this),
util::memFn(&IOutboundSessionMaker::OnSessionEstablished,
&_outboundSessionMaker),
util::memFn(&AbstractRouter::CheckRenegotiateValid, this),
util::memFn(&IOutboundSessionMaker::OnConnectTimeout,
&_outboundSessionMaker),
util::memFn(&AbstractRouter::SessionClosed, this));
if(!link)
return false;
if(!link->EnsureKeys(transport_keyfile.string().c_str()))
{
LogError("failed to load ", transport_keyfile);
return false;
}
static std::list< LinkFactory > linkFactories = {utp::NewOutboundLink,
iwp::NewServer};
const auto afs = {AF_INET, AF_INET6};
bool addedAtLeastOne = false;
for(const auto &factory : linkFactories)
for(const auto af : afs)
{
auto link = factory(
encryption(), util::memFn(&AbstractRouter::rc, this),
util::memFn(&AbstractRouter::HandleRecvLinkMessageBuffer, this),
util::memFn(&IOutboundSessionMaker::OnSessionEstablished,
&_outboundSessionMaker),
util::memFn(&AbstractRouter::CheckRenegotiateValid, this),
util::memFn(&AbstractRouter::Sign, this),
util::memFn(&IOutboundSessionMaker::OnConnectTimeout,
&_outboundSessionMaker),
util::memFn(&AbstractRouter::SessionClosed, this));
if(!link)
continue;
if(!link->EnsureKeys(transport_keyfile.string().c_str()))
{
LogError("failed to load ", transport_keyfile);
if(!link->Configure(netloop(), "*", af, m_OutboundPort))
continue;
}
const auto afs = {AF_INET, AF_INET6};
for(const auto af : afs)
{
if(!link->Configure(netloop(), "*", af, m_OutboundPort))
continue;
_linkManager.AddLink(std::move(link), false);
addedAtLeastOne = true;
break;
}
_linkManager.AddLink(std::move(link), false);
return true;
}
return addedAtLeastOne;
return false;
}
bool

@ -28,6 +28,7 @@
#include <router/outbound_message_handler.hpp>
#include <router/outbound_session_maker.hpp>
#include <link/link_manager.hpp>
#include <link/factory.hpp>
#include <router/rc_lookup_handler.hpp>
#include <functional>
@ -175,6 +176,8 @@ namespace llarp
struct sockaddr_in ip4addr;
AddressInfo addrInfo;
LinkFactory::LinkType _defaultLinkType;
llarp_ev_loop_ptr _netloop;
std::shared_ptr< llarp::thread::ThreadPool > cryptoworker;
std::shared_ptr< Logic > _logic;

@ -23,7 +23,7 @@ namespace llarp
return defaultID;
}
bool RouterContact::IgnoreBogons = false;
bool RouterContact::BlockBogons = true;
#ifdef TESTNET
// 1 minute for testnet
@ -37,7 +37,7 @@ namespace llarp
/// an RC inserted long enough ago (30 min) is considered stale and is removed
llarp_time_t RouterContact::StaleInsertionAge = 30 * 60 * 1000;
NetID::NetID(const byte_t *val) : AlignedBuffer< 8 >()
NetID::NetID(const byte_t *val)
{
size_t len = strnlen(reinterpret_cast< const char * >(val), size());
std::copy(val, val + len, begin());
@ -67,6 +67,7 @@ namespace llarp
llarp_buffer_t strbuf;
if(!bencode_read_string(buf, &strbuf))
return false;
if(strbuf.sz > size())
return false;
@ -106,13 +107,17 @@ namespace llarp
return false;
std::string nick = Nick();
if(nick.size())
if(!nick.empty())
{
/* write nickname */
if(!bencode_write_bytestring(buf, "n", 1))
{
return false;
}
if(!bencode_write_bytestring(buf, nick.c_str(), nick.size()))
{
return false;
}
}
/* write encryption pubkey */
@ -167,7 +172,9 @@ namespace llarp
{"addresses", addrs}};
if(HasNick())
{
obj["nickname"] = Nick();
}
return obj;
}
@ -189,9 +196,13 @@ namespace llarp
{
llarp_buffer_t strbuf;
if(!bencode_read_string(buf, &strbuf))
{
return false;
if(strbuf.sz > nickname.size())
}
if(strbuf.sz > llarp::AlignedBuffer< (32) >::size())
{
return false;
}
nickname.Zero();
std::copy(strbuf.base, strbuf.base + strbuf.sz, nickname.begin());
return true;
@ -218,7 +229,7 @@ namespace llarp
bool
RouterContact::IsPublicRouter() const
{
return addrs.size() > 0;
return !addrs.empty();
}
bool
@ -277,7 +288,9 @@ namespace llarp
signature.Zero();
last_updated = time_now_ms();
if(!BEncode(&buf))
{
return false;
}
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
return CryptoManager::instance()->sign(signature, secretkey, buf);
@ -303,7 +316,7 @@ namespace llarp
}
for(const auto &a : addrs)
{
if(IsBogon(a.ip) && !IgnoreBogons)
if(IsBogon(a.ip) && BlockBogons)
{
llarp::LogError("invalid address info: ", a);
return false;
@ -349,17 +362,23 @@ namespace llarp
std::array< byte_t, MAX_RC_SIZE > tmp;
llarp_buffer_t buf(tmp);
if(!BEncode(&buf))
{
return false;
}
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
const fs::path fpath = std::string(fname); /* */
auto optional_f =
llarp::util::OpenFileStream< std::ofstream >(fpath, std::ios::binary);
if(!optional_f)
{
return false;
}
auto &f = optional_f.value();
if(!f.is_open())
{
return false;
}
f.write((char *)buf.base, buf.sz);
return true;
}
@ -379,7 +398,9 @@ namespace llarp
f.seekg(0, std::ios::end);
auto l = f.tellg();
if(l > static_cast< std::streamoff >(sizeof tmp))
{
return false;
}
f.seekg(0, std::ios::beg);
f.read((char *)tmp.data(), l);
return BDecode(&buf);

@ -67,7 +67,7 @@ namespace llarp
struct RouterContact
{
/// for unit tests
static bool IgnoreBogons;
static bool BlockBogons;
static llarp_time_t Lifetime;
static llarp_time_t UpdateInterval;
@ -144,7 +144,7 @@ namespace llarp
bool
IsExit() const
{
return exits.size() > 0;
return !exits.empty();
}
bool

@ -21,7 +21,9 @@ namespace llarp
{
auto pos = str.find(".snode");
if(pos == std::string::npos || pos == 0)
{
return false;
}
return Base32Decode(str.substr(0, pos), *this);
}
} // namespace llarp

@ -12,7 +12,7 @@ namespace llarp
using Data = std::array< byte_t, SIZE >;
RouterID() : AlignedBuffer< SIZE >()
RouterID()
{
}

@ -509,7 +509,7 @@ namespace llarp
{
auto msg = std::make_shared< routing::DHTMessage >();
msg->M.emplace_back(
std::make_unique< dht::PublishIntroMessage >(m_IntroSet, txid, 1));
std::make_unique< dht::PublishIntroMessage >(m_IntroSet, txid, 5));
return msg;
}
@ -1205,11 +1205,12 @@ namespace llarp
// time from now that the newest intro expires at
if(intro.ExpiresSoon(now))
return should;
const auto dlt = intro.expiresAt - now;
const auto dlt = now - (intro.expiresAt - path::default_lifetime);
return should
|| ( // try spacing tunnel builds out evenly in time
(dlt <= (path::default_lifetime / 4))
&& (NumInStatus(path::ePathBuilding) < numPaths));
(dlt >= (path::default_lifetime / 4))
&& (NumInStatus(path::ePathBuilding) < numPaths)
&& !path::Builder::BuildCooldownHit(now));
}
std::shared_ptr< Logic >

@ -30,7 +30,7 @@ namespace llarp
}
bool
ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 15000) const
ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 30000) const
{
if(dlt)
return now >= (expiresAt - dlt);

@ -1,4 +1,4 @@
#ifdef SHADOW_TESTNET
#ifdef LOKINET_SHADOW
#include <llarp.h>
/** insert shadow test network specific code here */

@ -7,7 +7,8 @@ static short old_attrs;
namespace llarp
{
Win32LogStream::Win32LogStream(std::ostream& out) : OStreamLogStream(out)
Win32LogStream::Win32LogStream(std::ostream& out)
: OStreamLogStream(out), m_Out(out)
{
// Attempt to use ANSI escapes directly
// if the modern console is active.
@ -26,6 +27,45 @@ namespace llarp
void
Win32LogStream::PreLog(std::stringstream& ss, LogLevel lvl, const char* fname,
int lineno, const std::string& nodename) const
{
if(!isConsoleModern)
{
switch(lvl)
{
case eLogNone:
break;
case eLogDebug:
ss << "[DBG] ";
break;
case eLogInfo:
ss << "[NFO] ";
break;
case eLogWarn:
ss << "[WRN] ";
break;
case eLogError:
ss << "[ERR] ";
break;
}
ss << "[" << nodename << "]"
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << fname
<< ":" << lineno << "\t";
}
else
OStreamLogStream::PreLog(ss, lvl, fname, lineno, nodename);
}
void
Win32LogStream::PostLog(std::stringstream& ss) const
{
if(!isConsoleModern)
ss << std::endl;
else
OStreamLogStream::PostLog(ss);
}
void
Win32LogStream::Print(LogLevel lvl, const char*, const std::string& msg)
{
if(!isConsoleModern)
{
@ -39,46 +79,33 @@ namespace llarp
SetConsoleTextAttribute(fd1,
FOREGROUND_RED | FOREGROUND_GREEN
| FOREGROUND_BLUE); // low white on black
ss << "[DBG] ";
break;
case eLogInfo:
SetConsoleTextAttribute(
fd1,
FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN
| FOREGROUND_BLUE); // high white on black
ss << "[NFO] ";
break;
case eLogWarn:
SetConsoleTextAttribute(fd1,
FOREGROUND_RED | FOREGROUND_GREEN
| FOREGROUND_INTENSITY); // bright yellow
ss << "[WRN] ";
break;
case eLogError:
SetConsoleTextAttribute(
fd1, FOREGROUND_RED | FOREGROUND_INTENSITY); // bright red
ss << "[ERR] ";
break;
}
ss << "[" << nodename << "]"
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << fname
<< ":" << lineno << "\t";
}
else
OStreamLogStream::PreLog(ss, lvl, fname, lineno, nodename);
}
void
Win32LogStream::PostLog(std::stringstream& ss) const
{
m_Out << msg << std::flush;
if(!isConsoleModern)
{
SetConsoleTextAttribute(
fd1, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
ss << std::endl;
}
else
OStreamLogStream::PostLog(ss);
}
} // namespace llarp
#endif

@ -20,8 +20,15 @@ namespace llarp
void Tick(llarp_time_t) override{};
void
Print(LogLevel lvl, const char*, const std::string& msg) override;
private:
std::ostream& m_Out;
bool isConsoleModern =
true; // qol fix so oldfag clients don't see ugly escapes
HANDLE fd1 = GetStdHandle(STD_OUTPUT_HANDLE);
};
} // namespace llarp

@ -15,7 +15,8 @@ namespace llarp
{
struct LinkLayer;
struct Session : public ILinkSession
struct Session : public ILinkSession,
public std::enable_shared_from_this< Session >
{
/// remote router's rc
RouterContact remoteRC;
@ -155,6 +156,12 @@ namespace llarp
void
Pump() override;
std::shared_ptr< ILinkSession >
BorrowSelf() override
{
return shared_from_this();
}
bool
SendKeepAlive() override;

@ -10,9 +10,10 @@ namespace llarp
{
LinkLayer_ptr
NewOutboundLink(const SecretKey& routerEncSecret, GetRCFunc getrc,
LinkMessageHandler h, SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, SignBufferFunc sign,
TimeoutHandler timeout, SessionClosedHandler closed)
LinkMessageHandler h, SignBufferFunc sign,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, TimeoutHandler timeout,
SessionClosedHandler closed)
{
return std::make_shared< LinkLayer >(routerEncSecret, getrc, h, sign, est,
reneg, timeout, closed, false);
@ -20,9 +21,10 @@ namespace llarp
LinkLayer_ptr
NewInboundLink(const SecretKey& routerEncSecret, GetRCFunc getrc,
LinkMessageHandler h, SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, SignBufferFunc sign,
TimeoutHandler timeout, SessionClosedHandler closed)
LinkMessageHandler h, SignBufferFunc sign,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, TimeoutHandler timeout,
SessionClosedHandler closed)
{
return std::make_shared< LinkLayer >(routerEncSecret, getrc, h, sign, est,
reneg, timeout, closed, true);

@ -6,20 +6,20 @@
namespace llarp
{
struct AbstractRouter;
namespace utp
{
LinkLayer_ptr
NewInboundLink(const SecretKey& routerEncSecret, GetRCFunc getrc,
LinkMessageHandler h, SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, SignBufferFunc sign,
TimeoutHandler timeout, SessionClosedHandler closed);
LinkMessageHandler h, SignBufferFunc sign,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, TimeoutHandler timeout,
SessionClosedHandler closed);
LinkLayer_ptr
NewOutboundLink(const SecretKey& routerEncSecret, GetRCFunc getrc,
LinkMessageHandler h, SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, SignBufferFunc sign,
TimeoutHandler timeout, SessionClosedHandler closed);
LinkMessageHandler h, SignBufferFunc sign,
SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, TimeoutHandler timeout,
SessionClosedHandler closed);
/// shim
const auto NewServer = NewInboundLink;
} // namespace utp

@ -10,6 +10,7 @@
// these need to be in a specific order
#include <assert.h>
#include <stdbool.h>
#include <net/net.h>
#include <windows.h>
#include <iphlpapi.h>

@ -1 +1 @@
just a lot of wind
breaking winds

@ -102,10 +102,10 @@ metric-tank-host=52.80.56.123:2003
ASSERT_FALSE(config.metrics.disableMetrics);
{
using kv = IwpConfig::Servers::value_type;
using kv = LinksConfig::Links::value_type;
ASSERT_THAT(config.iwp_links.servers(),
UnorderedElementsAre(kv("eth0", AF_INET, 5501)));
ASSERT_THAT(config.links.inboundLinks(),
UnorderedElementsAre(kv("eth0", AF_INET, 5501, {})));
}
ASSERT_THAT(config.bootstrap.routers,

@ -1,11 +1,13 @@
#include <crypto/crypto_noop.hpp>
#include <crypto/crypto_libsodium.hpp>
#include <ev/ev.h>
#include <iwp/iwp.hpp>
#include <llarp_test.hpp>
#include <iwp/iwp.hpp>
#include <messages/link_intro.hpp>
#include <messages/discard.hpp>
#include <utp/utp.hpp>
#include <test_util.hpp>
#include <gtest/gtest.h>
@ -13,7 +15,7 @@
using namespace ::llarp;
using namespace ::testing;
struct LinkLayerTest : public test::LlarpTest< NoOpCrypto >
struct LinkLayerTest : public test::LlarpTest< llarp::sodium::CryptoLibSodium >
{
static constexpr uint16_t AlicePort = 5000;
static constexpr uint16_t BobPort = 6000;
@ -62,7 +64,7 @@ struct LinkLayerTest : public test::LlarpTest< NoOpCrypto >
localLoopBack()
{
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
|| (__APPLE__ && __MACH__)
|| (__APPLE__ && __MACH__) || (__sun)
return "lo0";
#else
return "lo";
@ -118,10 +120,10 @@ struct LinkLayerTest : public test::LlarpTest< NoOpCrypto >
void
SetUp()
{
oldRCLifetime = RouterContact::Lifetime;
RouterContact::IgnoreBogons = true;
RouterContact::Lifetime = 500;
netLoop = llarp_make_ev_loop();
oldRCLifetime = RouterContact::Lifetime;
RouterContact::BlockBogons = false;
RouterContact::Lifetime = 500;
netLoop = llarp_make_ev_loop();
m_logic.reset(new Logic());
}
@ -132,8 +134,8 @@ struct LinkLayerTest : public test::LlarpTest< NoOpCrypto >
Bob.TearDown();
m_logic.reset();
netLoop.reset();
RouterContact::IgnoreBogons = false;
RouterContact::Lifetime = oldRCLifetime;
RouterContact::BlockBogons = true;
RouterContact::Lifetime = oldRCLifetime;
}
static void
@ -167,12 +169,12 @@ struct LinkLayerTest : public test::LlarpTest< NoOpCrypto >
}
};
TEST_F(LinkLayerTest, TestUTPAliceRenegWithBob)
TEST_F(LinkLayerTest, TestIWP)
{
#ifdef WIN32
GTEST_SKIP();
GTEST_SKIP();
#else
Alice.link = utp::NewServer(
Alice.link = iwp::NewInboundLink(
Alice.encryptionKey,
[&]() -> const RouterContact& { return Alice.GetRC(); },
[&](ILinkSession* s, const llarp_buffer_t& buf) -> bool {
@ -193,14 +195,108 @@ TEST_F(LinkLayerTest, TestUTPAliceRenegWithBob)
return true;
}
},
[&](Signature& sig, const llarp_buffer_t& buf) -> bool {
return m_crypto.sign(sig, Alice.signingKey, buf);
},
[&](ILinkSession* s) -> bool {
const auto rc = s->GetRemoteRC();
return rc.pubkey == Bob.GetRC().pubkey;
},
[&](RouterContact, RouterContact) -> bool { return true; },
[&](ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
Stop();
},
[&](RouterID router) { ASSERT_EQ(router, Bob.GetRouterID()); });
auto sendDiscardMessage = [](ILinkSession* s) -> bool {
// send discard message in reply to complete unit test
std::array< byte_t, 32 > tmp;
llarp_buffer_t otherBuf(tmp);
DiscardMessage discard;
if(!discard.BEncode(&otherBuf))
return false;
otherBuf.sz = otherBuf.cur - otherBuf.base;
otherBuf.cur = otherBuf.base;
return s->SendMessageBuffer(otherBuf, nullptr);
};
Bob.link = iwp::NewInboundLink(
Bob.encryptionKey, [&]() -> const RouterContact& { return Bob.GetRC(); },
[&](ILinkSession* s, const llarp_buffer_t& buf) -> bool {
LinkIntroMessage msg;
ManagedBuffer copy{buf};
if(!msg.BDecode(&copy.underlying))
return false;
if(!s->GotLIM(&msg))
return false;
Bob.gotLIM = true;
return sendDiscardMessage(s);
},
[&](Signature& sig, const llarp_buffer_t& buf) -> bool {
return m_crypto.sign(sig, Bob.signingKey, buf);
},
[&](ILinkSession* s) -> bool {
if(s->GetRemoteRC().pubkey != Alice.GetRC().pubkey)
return false;
LogInfo("bob established with alice");
return Bob.link->VisitSessionByPubkey(Alice.GetRC().pubkey.as_array(),
sendDiscardMessage);
},
[&](RouterContact newrc, RouterContact oldrc) -> bool {
success = newrc.pubkey == oldrc.pubkey;
return true;
},
[&](ILinkSession* session) { ASSERT_FALSE(session->IsEstablished()); },
[&](RouterID router) { ASSERT_EQ(router, Alice.GetRouterID()); });
ASSERT_TRUE(Alice.Start(m_logic, netLoop, AlicePort));
ASSERT_TRUE(Bob.Start(m_logic, netLoop, BobPort));
ASSERT_TRUE(Alice.link->TryEstablishTo(Bob.GetRC()));
RunMainloop();
ASSERT_TRUE(Bob.gotLIM);
#endif
};
TEST_F(LinkLayerTest, TestUTPAliceRenegWithBob)
{
#ifdef WIN32
GTEST_SKIP();
#else
Alice.link = utp::NewServer(
Alice.encryptionKey,
[&]() -> const RouterContact& { return Alice.GetRC(); },
[&](ILinkSession* s, const llarp_buffer_t& buf) -> bool {
if(Alice.gotLIM)
{
Alice.Regen();
return s->RenegotiateSession();
}
else
{
LinkIntroMessage msg;
ManagedBuffer copy{buf};
if(!msg.BDecode(&copy.underlying))
return false;
if(!s->GotLIM(&msg))
return false;
Alice.gotLIM = true;
return true;
}
},
[&](Signature& sig, const llarp_buffer_t& buf) -> bool {
return m_crypto.sign(sig, Alice.signingKey, buf);
},
[&](ILinkSession* s) -> bool {
const auto rc = s->GetRemoteRC();
return rc.pubkey == Bob.GetRC().pubkey;
},
[&](RouterContact, RouterContact) -> bool { return true; },
[&](ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
Stop();
@ -231,6 +327,10 @@ TEST_F(LinkLayerTest, TestUTPAliceRenegWithBob)
Bob.gotLIM = true;
return sendDiscardMessage(s);
},
[&](Signature& sig, const llarp_buffer_t& buf) -> bool {
return m_crypto.sign(sig, Bob.signingKey, buf);
},
[&](ILinkSession* s) -> bool {
if(s->GetRemoteRC().pubkey != Alice.GetRC().pubkey)
return false;
@ -242,9 +342,6 @@ TEST_F(LinkLayerTest, TestUTPAliceRenegWithBob)
success = newrc.pubkey == oldrc.pubkey;
return true;
},
[&](Signature& sig, const llarp_buffer_t& buf) -> bool {
return m_crypto.sign(sig, Bob.signingKey, buf);
},
[&](ILinkSession* session) { ASSERT_FALSE(session->IsEstablished()); },
[&](RouterID router) { ASSERT_EQ(router, Alice.GetRouterID()); });
@ -280,6 +377,9 @@ TEST_F(LinkLayerTest, TestUTPAliceConnectToBob)
return false;
}
return AliceGotMessage(buf);
},
[&](Signature& sig, const llarp_buffer_t& buf) -> bool {
return m_crypto.sign(sig, Alice.signingKey, buf);
},
[&](ILinkSession* s) -> bool {
if(s->GetRemoteRC().pubkey != Bob.GetRC().pubkey)
@ -288,9 +388,7 @@ TEST_F(LinkLayerTest, TestUTPAliceConnectToBob)
return true;
},
[&](RouterContact, RouterContact) -> bool { return true; },
[&](Signature& sig, const llarp_buffer_t& buf) -> bool {
return m_crypto.sign(sig, Alice.signingKey, buf);
},
[&](ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
Stop();
@ -312,6 +410,9 @@ TEST_F(LinkLayerTest, TestUTPAliceConnectToBob)
return false;
}
return true;
},
[&](Signature& sig, const llarp_buffer_t& buf) -> bool {
return m_crypto.sign(sig, Bob.signingKey, buf);
},
[&](ILinkSession* s) -> bool {
if(s->GetRemoteRC().pubkey != Alice.GetRC().pubkey)
@ -332,9 +433,6 @@ TEST_F(LinkLayerTest, TestUTPAliceConnectToBob)
return true;
},
[&](RouterContact, RouterContact) -> bool { return true; },
[&](Signature& sig, const llarp_buffer_t& buf) -> bool {
return m_crypto.sign(sig, Bob.signingKey, buf);
},
[&](ILinkSession* session) { ASSERT_FALSE(session->IsEstablished()); },
[&](RouterID router) { ASSERT_EQ(router, Alice.GetRouterID()); });

@ -184,7 +184,11 @@ int GetCPU() {
// global constructors.
static class VDSOInitHelper {
public:
VDSOInitHelper() { VDSOSupport::Init(); }
VDSOInitHelper() {
#ifndef LOKINET_SHADOW
VDSOSupport::Init();
#endif
}
} vdso_init_helper;
} // namespace debugging_internal

@ -4739,7 +4739,7 @@ void UnitTest::AddTestPartResult(
((defined(__clang__) || defined(__GNUC__)) && \
(defined(__x86_64__) || defined(__i386__)))
// with clang/gcc we can achieve the same effect on x86 by invoking int3
asm("int3");
asm("int $0x3");
#else
// Dereference nullptr through a volatile pointer to prevent the compiler
// from removing. We use this rather than abort() or __builtin_trap() for

@ -89,6 +89,7 @@ Source: "{#DevPath}ui-win32\bin\release\lokinetui.pdb"; DestDir: "{app}"; Flags:
#endif
; eh, might as well ship the 32-bit port of everything else
Source: "{#DevPath}build\testAll.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#DevPath}build\lokinet-rcutil.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#DevPath}LICENSE"; DestDir: "{app}"; Flags: ignoreversion
; delet this after finishing setup, we only need it to extract the drivers
; and download an initial RC. The UI has its own bootstrap built-in!

Loading…
Cancel
Save