Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
71b68e1
Make internal IPv6 work
jagerman Oct 22, 2025
72bfc3c
Auto-adjust ifaddr= if set to the network address
jagerman Oct 23, 2025
8f015e7
Improve mapaddr config
jagerman Oct 23, 2025
5e392c7
Internalize internal code
jagerman Oct 23, 2025
3c6ac6e
Implement ICMPv6 unreachable for unmapped tun IPs
jagerman Oct 23, 2025
d1d216a
Add some path info getters for embedded clients
tewinget Oct 22, 2025
5fbf7be
Fix embedded random port assignment
jagerman Oct 23, 2025
483d407
fix segfault from container autovivify, catch exception too
tewinget Oct 23, 2025
9d8d34c
don't assign ip address if session creation fails...
tewinget Oct 23, 2025
96ff814
update tun traffic session init code
tewinget Oct 23, 2025
c4c9c53
fix session close on idle timeout, make inbound sessions do it too
tewinget Oct 23, 2025
8c75c54
update tun traffic session init code
tewinget Oct 23, 2025
f32bcbf
Merge remote-tracking branch 'tewinget/misc-fixes' into ipv6
jagerman Oct 24, 2025
bfe8054
Preserve IP mappings; persistent UDP tunnels; IPv6 tunnels
jagerman Oct 29, 2025
975b9a8
Create data-dir if it doesn't pre-exist
jagerman Oct 29, 2025
06bbb09
Fix interface name description
jagerman Oct 29, 2025
42c9bdb
Fix CC "not found" response handling
jagerman Oct 29, 2025
4203455
Replace "llarp" usage in thread name, endpoints
jagerman Oct 29, 2025
fb0d327
Only IPv6 by default; pubkey-based IPv6 addrs; fix A/AAAA DNS responses
jagerman Oct 30, 2025
8a17540
Fix on_established being called multiple times if it throws
jagerman Oct 30, 2025
c11d13b
version bump
jagerman Oct 30, 2025
b8f24cc
formatting
jagerman Oct 30, 2025
9ec8367
update submodules to session-foundation repo
jagerman Oct 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
url = https://github.com/fnc12/sqlite_orm
[submodule "external/oxen-mq"]
path = external/oxen-mq
url = https://github.com/oxen-io/oxen-mq
url = https://github.com/session-foundation/liboxenmq.git
[submodule "gui"]
path = gui
url = https://github.com/oxen-io/lokinet-gui.git
Expand All @@ -22,4 +22,4 @@
url = https://github.com/CLIUtils/CLI11.git
[submodule "external/oxen-libquic"]
path = external/oxen-libquic
url = https://github.com/oxen-io/oxen-libquic.git
url = https://github.com/session-foundation/libquic.git
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ endif()


project(session-router
VERSION 1.0.1
VERSION 1.0.2
DESCRIPTION "Session Router - IP packet onion router"
LANGUAGES ${LANGS})

Expand Down Expand Up @@ -339,7 +339,8 @@ if(APPLE AND SROUTER_FULL)
endif()

add_executable(libsession-router_jank_test EXCLUDE_FROM_ALL contrib/libsession-router_jank_test.cpp)
target_link_libraries(libsession-router_jank_test PRIVATE libsession-router)
target_include_directories(libsession-router_jank_test PUBLIC include/)
target_link_libraries(libsession-router_jank_test PRIVATE libsessionrouter)

# uninstall target
if(NOT TARGET uninstall)
Expand Down
93 changes: 80 additions & 13 deletions contrib/libsession-router_jank_test.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#include <session_router.hpp>
#include <session/router.hpp>

#include <csignal>
#include <exception>
#include <filesystem>
#include <future>
#include <iostream>
#include <thread>

extern "C"
{
#include <unistd.h>
}

using namespace std::literals;

Expand All @@ -16,25 +21,39 @@ int main(int argc, char** argv)
return 1;
}

// Block signal handling by default from all threads, so that we handle signals exclusively in
// this main thread below.
sigset_t signal_mask;
sigemptyset(&signal_mask);
for (auto sig : {SIGINT, SIGTERM, SIGHUP, SIGUSR1, SIGUSR2})
sigaddset(&signal_mask, sig);
pthread_sigmask(SIG_BLOCK, &signal_mask, nullptr);

std::string target{argv[1]};

session_router::Session Router loki{std::filesystem::path{"session_router.ini"}};
auto srouter = std::make_unique<session::router::SessionRouter>(std::filesystem::path{"jank.ini"});

std::promise<void> prom;
loki.on_connected([&] {

bool first_conn = true;
srouter->on_connected([&] {
if (!first_conn)
return;
first_conn = false;
std::cout << "\n\x1b[32;1mSession Router connected!\x1b[0m\n\n\x1b[33;1mINITIATING SESSION TO " << target
<< "\x1b[0m\n\n"
<< std::flush;
loki.establish_udp(
srouter->establish_udp(
target,
12345,
[](auto udp_info) {
[&prom](auto udp_info) {
std::cout << "\n\x1b[32;1mUDP bound to port " << udp_info.local_port << "\x1b[0m\n\n" << std::flush;
prom.set_value();
},
[&prom](std::string_view fail_msg) {
[&prom]() {
try
{
throw std::runtime_error{std::string{fail_msg}};
throw std::runtime_error{"Session timed out!"};
}
catch (...)
{
Expand All @@ -45,24 +64,72 @@ int main(int argc, char** argv)
try
{
prom.get_future().get();
const auto current_path = srouter->get_path_for_session(target);
if (!current_path)
{
std::cerr << "future returned with no session / no current path.\n";
return 1;
}
size_t hop_count = 1;
std::cout << "Path to snode:\n";
for (const auto& [snode, ip] : *current_path)
{
std::cout << "\tHop " << hop_count << ":\n\t\t";
std::cout << snode << " @ " << ip << "\n";
hop_count++;
}
}
catch (const std::exception& e)
{
std::cerr << "\n\n\x1b[31;1mError establishing session to " << target << ": " << e.what() << "\x1b[0m\n\n";
return 1;
}

auto pid = getpid();
std::cout << "\n\n\x1b[32;1mTunnel running.\n\n"
<< argv[0] << " signal controls:\n\n"
<< " kill -SIGHUP " << pid << " -- close tunnels\n"
<< " kill -SIGUSR1 " << pid << " -- re-open UDP tunnel\n"
<< " kill -SIGUSR2 " << pid << " -- re-open TCP tunnel\n"
<< " Ctrl-C -- shut down\x1b[0m\n\n\n";

/*
loki.map_tcp_remote_port(std::string{argv[1]}, 12345,
srouter.map_tcp_remote_port(std::string{argv[1]}, 12345,
[&](auto tunnel_info) {
std::cout << "\n\nTCP bound to port " << tunnel_info.local_port << "\n\n";
},
[&](auto error_str) {
std::cerr << "\nTCP Tunnel map error: " << error_str << "\n";
});
*/
std::cout << "\nPRESS ENTER TO EXIT\n";
std::string ignored;
std::getline(std::cin, ignored);
std::cout << "\nEXITING\n";

std::thread sig_thread{[&] {
while (srouter)
{
int signo;
sigwait(&signal_mask, &signo);
switch (signo)
{
case SIGHUP:
std::cout << "\n\n\n\x1b[33;1mHangup signal received; closing UDP tunnel\x1b[0m\n\n\n";
srouter->close_udp(target, 12345);
break;
case SIGUSR1:
{
std::cout << "\n\n\n\x1b[32;1mSIGUSR1 received: (re-)opening UDP tunnel\x1b[0m\n";
auto ti = srouter->establish_udp(target, 12345);
std::cout << "\n\x1b[32;1mUDP bound to port " << ti.local_port << "\x1b[0m\n\n";
break;
}
case SIGUSR2:
std::cout << "\n\x1b[31;1mSIGUSR2 received: TODO FIXME: reopen TCP tunnel\x1b[0m\n\n";
break;
default:
std::cout << "\n\n\n\x1b[33;1mSignal " << signo << " received, shutting down\x1b\[0m\n\n\n";
srouter.reset();
break;
}
}
}};
sig_thread.join();
}
89 changes: 62 additions & 27 deletions include/session/router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <filesystem>
#include <functional>
#include <memory>
#include <optional>
#include <thread>
#include <type_traits>

Expand All @@ -26,7 +27,8 @@ namespace session::router
TESTNET
};

/// Metadata returned when establishing a session for a TCP or UDP tunnel.
/// Object returned when establishing a session for a TCP or UDP tunnel. The tunnel port will
/// be kept open until close_udp()/close_tcp() is called with the same remote and port.
struct tunnel_info
{
/// The requested remote address. If an ONS entry was requested, this will be the resolved
Expand All @@ -41,18 +43,22 @@ namespace session::router
/// mapping for UDP).
uint16_t remote_port;

/// The bound local port. After establishing a Session Router session, clients connect (TCP) or
/// send (UDP) to this port (on address 127.0.0.1) to reach the destination through session_router.
/// The bound local port. After establishing a Session Router session, clients connect
/// (TCP) or send (UDP) to this port (on IPv6 localhost address ::1) to reach the
/// destination through session_router.
uint16_t local_port;

/// A suggested maximum MTU for the connection. If the application supports a configurable
/// MTU, this value is the recommended value that avoids some additional overhead from
/// packet splitting, which can slightly reduce latency and jitter. If the application
/// doesn't support MTU configuration then this value can simply be ignored and Session Router will
/// split any "too large" packets into two.
/// doesn't support MTU configuration then this value can simply be ignored and Session
/// Router will split any "too large" packets into two.
uint16_t suggested_mtu;
};

using snode_path = std::vector<std::pair<std::string, std::string>>;
using session_path = std::pair<snode_path, std::string>;

class SessionRouter
{
std::unique_ptr<srouter::Context> context;
Expand Down Expand Up @@ -95,31 +101,60 @@ namespace session::router
// is not currently connected then the callback will be scheduled immediately.
void on_disconnected(std::function<void()> callback, bool persist = false);

// Establishes a UDP session to the given remote (.loki or .snode) and port. When the
// Session Router session to the remote is established, the callback is invoked with the info
// corresponding to the session and tunnel. This call can happen instantly (before this
// function call returns) if a session to the given address is already established, but
// otherwise the callback will be called at some future point when the callback is
// established.
// Establishes a session to the given remote (.loki or .snode), with an IPv6 localhost port
// mapped to a port on the remote. A limited number of packets (e.g. to establish a
// connection) can be sent to the mapped port immediately even before the session
// establishes: a few packets will be queued and delivered once (and if) the session
// establishes.
//
// The returned object contains the port information. The tunnel will remain active until
// drop_udp() is called with the same remote address and port (or the SessionRouter instance
// is destroyed). The caller should track this and drop UDP ports when no longer needed.
//
// Calling with an already-established remote/port simply returns that existing mapping, it
// does *not* create a new one.
//
// This method will throw if the given address is unparseable.
//
// If a connection cannot be established for whatever reason, the `on_failed` callback is
// invoked with a string giving a descriptive reason. Like `on_established`, it is possible
// for this to fire immediately, such as for an unparseable address or if Session Router can
// determine immediately that the connection will fail.
// If an `on_established` callback is provided then it will be called once the full session
// is established, and passed the same tunnel_info data that was returned by the initial
// call. Note that `on_established` can be called immediately (i.e. before
// `establish_udp()` returns), if a session to the remote is already established.
//
// The callbacks must not block as they are called from Session Router's logic thread (and so any
// blocking will stall Session Router).
void establish_udp(
std::string_view remote_view,
// `on_timeout` is invoked instead of `on_established` if the session fails to establish for
// whatever reason, with a string giving a descriptive reason. Note that `on_timeout` is
// *not* called if the call throws (such as if given an unparseable address). Note that an
// `on_timeout` call does *not* mean the tunnel has been cancelled: it will remain and future
// attempts to connect to the tunnel port will attempt to (re-)establish the session. If
// you want to cancel it on session initiation failure, call `close_udp()` from within the
// on_timeout callback.
//
// Take care not to use very slow or blocking code inside the callbacks: they are called
// from Session Router's logic thread (and so any blocking will stall Session Router).
tunnel_info establish_udp(
std::string_view remote,
uint16_t port,
std::function<void(tunnel_info info)> on_established,
std::function<void(std::string errmsg)> on_failed);

// Simple synchronous wrapper around the above: this blocks until either `on_established` or
// `on_failed` is called then returns the tunnel info (success) or throws the error message
// (failure). This is provided for quick-and-dirty implementation code; generally code
// should prefer the callback-based async version, above.
tunnel_info establish_udp_blocking(std::string_view remote, uint16_t port);
std::function<void(tunnel_info)> on_established = nullptr,
std::function<void()> on_timeout = nullptr);

// Closes a tunnel socket to the given remote/port combination, releasing any internal
// mappings set up from previous connections through the tunnel.
void close_udp(std::string_view remote, uint16_t port);

// If we have a session with the given remote, returns the path we are currently using for
// that session. In the case of a client<->client session, this will be the relay which we
// are using as a pivot.
//
// If there is a session but no current path, an empty vector is
// returned.
// If there is not a session to the remote, std::nullopt is returned.
std::optional<snode_path> get_path_for_session(std::string_view remote);

// Returns the path we're currently using for each session along with the remote endpoint
// of that session. In the case of snode (relay) sessions, the remote endpoint will be
// the same as the path terminus. In the case of client<->client sessions, the remote
// endpoint is the client which we're connected to via that path as a relay.
std::vector<session_path> get_all_session_paths();
};

template SessionRouter::SessionRouter(const std::filesystem::path&, std::shared_ptr<oxen::quic::Loop>);
Expand Down
1 change: 0 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ session_router_add_library(session-router-contact
session_router_add_library(session-router-ip
net/ip_packet.cpp
net/policy.cpp
net/utils.cpp
)

# Addressing and event loop files used by session-router-core and other libraries
Expand Down
5 changes: 1 addition & 4 deletions src/address/address.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ namespace srouter
// (false).
NetworkAddress(const RouterID& rid, bool is_client) : _pubkey{rid}, _is_client{is_client} {}

bool operator==(const NetworkAddress& other) const
{
return std::tie(_pubkey, _is_client) == std::tie(other._pubkey, other._is_client);
}
bool operator==(const NetworkAddress& other) const = default;

bool empty() const { return _pubkey.is_zero(); }

Expand Down
Loading