mirror of https://github.com/oxen-io/lokinet
Merge pull request #1748 from majestrate/liblokinet-udp-api-2021-09-19
liblokinet updates
This commit is contained in:
commit
838183e36e
|
@ -5,8 +5,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
# Has to be set before `project()`, and ignored on non-macos:
|
# Has to be set before `project()`, and ignored on non-macos:
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "macOS deployment target (Apple clang only)")
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "macOS deployment target (Apple clang only)")
|
||||||
|
|
||||||
|
option(BUILD_DAEMON "build lokinet daemon and associated utils" ON)
|
||||||
|
|
||||||
|
|
||||||
set(LANGS C CXX)
|
set(LANGS C CXX)
|
||||||
if(APPLE)
|
if(APPLE AND BUILD_DAEMON)
|
||||||
set(LANGS ${LANGS} OBJC Swift)
|
set(LANGS ${LANGS} OBJC Swift)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -43,6 +46,12 @@ endif()
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||||
|
|
||||||
|
set(DEFAULT_WITH_BOOTSTRAP ON)
|
||||||
|
if(APPLE)
|
||||||
|
set(DEFAULT_WITH_BOOTSTRAP OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# Core options
|
# Core options
|
||||||
option(USE_AVX2 "enable avx2 code" OFF)
|
option(USE_AVX2 "enable avx2 code" OFF)
|
||||||
option(USE_NETNS "enable networking namespace support. Linux only" OFF)
|
option(USE_NETNS "enable networking namespace support. Linux only" OFF)
|
||||||
|
@ -60,6 +69,7 @@ option(TRACY_ROOT "include tracy profiler source" OFF)
|
||||||
option(WITH_TESTS "build unit tests" OFF)
|
option(WITH_TESTS "build unit tests" OFF)
|
||||||
option(WITH_HIVE "build simulation stubs" OFF)
|
option(WITH_HIVE "build simulation stubs" OFF)
|
||||||
option(BUILD_PACKAGE "builds extra components for making an installer (with 'make package')" OFF)
|
option(BUILD_PACKAGE "builds extra components for making an installer (with 'make package')" OFF)
|
||||||
|
option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP})
|
||||||
|
|
||||||
include(cmake/enable_lto.cmake)
|
include(cmake/enable_lto.cmake)
|
||||||
|
|
||||||
|
@ -182,7 +192,7 @@ if(OXENMQ_FOUND)
|
||||||
message(STATUS "Found system liboxenmq ${OXENMQ_VERSION}")
|
message(STATUS "Found system liboxenmq ${OXENMQ_VERSION}")
|
||||||
else()
|
else()
|
||||||
message(STATUS "using oxenmq submodule")
|
message(STATUS "using oxenmq submodule")
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/external/oxen-mq)
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/oxen-mq)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
@ -327,8 +337,9 @@ endif()
|
||||||
|
|
||||||
add_subdirectory(crypto)
|
add_subdirectory(crypto)
|
||||||
add_subdirectory(llarp)
|
add_subdirectory(llarp)
|
||||||
add_subdirectory(daemon)
|
if(BUILD_DAEMON)
|
||||||
|
add_subdirectory(daemon)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WITH_HIVE)
|
if(WITH_HIVE)
|
||||||
add_subdirectory(pybind)
|
add_subdirectory(pybind)
|
||||||
|
|
|
@ -294,7 +294,6 @@ build_external(expat
|
||||||
)
|
)
|
||||||
add_static_target(expat expat_external libexpat.a)
|
add_static_target(expat expat_external libexpat.a)
|
||||||
|
|
||||||
|
|
||||||
build_external(unbound
|
build_external(unbound
|
||||||
DEPENDS openssl_external expat_external
|
DEPENDS openssl_external expat_external
|
||||||
CONFIGURE_COMMAND ./configure ${cross_host} ${cross_rc} --prefix=${DEPS_DESTDIR} --disable-shared
|
CONFIGURE_COMMAND ./configure ${cross_host} ${cross_rc} --prefix=${DEPS_DESTDIR} --disable-shared
|
||||||
|
@ -352,6 +351,10 @@ set_target_properties(libzmq PROPERTIES
|
||||||
INTERFACE_LINK_LIBRARIES "${libzmq_link_libs}"
|
INTERFACE_LINK_LIBRARIES "${libzmq_link_libs}"
|
||||||
INTERFACE_COMPILE_DEFINITIONS "ZMQ_STATIC")
|
INTERFACE_COMPILE_DEFINITIONS "ZMQ_STATIC")
|
||||||
|
|
||||||
|
if(NOT WITH_BOOTSTRAP)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
set(curl_extra)
|
set(curl_extra)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(curl_ssl_opts --without-ssl --with-schannel)
|
set(curl_ssl_opts --without-ssl --with-schannel)
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(udptest LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
add_executable(udptest udptest.cpp)
|
||||||
|
include_directories(../../include)
|
||||||
|
target_link_libraries(udptest PUBLIC lokinet)
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# liblokinet examples
|
||||||
|
|
||||||
|
building:
|
||||||
|
|
||||||
|
$ mkdir -p build
|
||||||
|
$ cd build
|
||||||
|
$ cp /path/to/liblokinet.so .
|
||||||
|
$ cmake .. -DCMAKE_EXE_LINKER_FLAGS='-L.'
|
||||||
|
$ make
|
||||||
|
|
||||||
|
running:
|
||||||
|
|
||||||
|
$ ./udptest /path/to/bootstrap.signed
|
|
@ -0,0 +1,239 @@
|
||||||
|
#include <lokinet.h>
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
bool _run{true};
|
||||||
|
|
||||||
|
using Lokinet_ptr = std::shared_ptr<lokinet_context>;
|
||||||
|
|
||||||
|
[[nodiscard]] auto
|
||||||
|
MakeLokinet(const std::vector<char>& bootstrap)
|
||||||
|
{
|
||||||
|
auto ctx = std::shared_ptr<lokinet_context>(lokinet_context_new(), lokinet_context_free);
|
||||||
|
if (auto err = lokinet_add_bootstrap_rc(bootstrap.data(), bootstrap.size(), ctx.get()))
|
||||||
|
throw std::runtime_error{strerror(err)};
|
||||||
|
if (lokinet_context_start(ctx.get()))
|
||||||
|
throw std::runtime_error{"could not start context"};
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WaitForReady(const Lokinet_ptr& ctx)
|
||||||
|
{
|
||||||
|
while (_run and lokinet_wait_for_ready(1000, ctx.get()))
|
||||||
|
{
|
||||||
|
std::cout << "waiting for context..." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Flow
|
||||||
|
{
|
||||||
|
lokinet_udp_flowinfo const _info;
|
||||||
|
lokinet_context* const _ctx;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Flow(const lokinet_udp_flowinfo* info, lokinet_context* ctx) : _info{*info}, _ctx{ctx}
|
||||||
|
{}
|
||||||
|
|
||||||
|
lokinet_context*
|
||||||
|
Context() const
|
||||||
|
{
|
||||||
|
return _ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
String() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::string{_info.remote_host} << ":" << std::to_string(_info.remote_port)
|
||||||
|
<< " on socket " << _info.socket_id;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConnectJob
|
||||||
|
{
|
||||||
|
lokinet_udp_flowinfo remote;
|
||||||
|
lokinet_context* ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
CreateOutboundFlow(void* user, void** flowdata, int* timeout)
|
||||||
|
{
|
||||||
|
auto* job = static_cast<ConnectJob*>(user);
|
||||||
|
Flow* flow = new Flow{&job->remote, job->ctx};
|
||||||
|
*flowdata = flow;
|
||||||
|
*timeout = 30;
|
||||||
|
std::cout << "made outbound flow: " << flow->String() << std::endl;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ProcessNewInboundFlow(void* user, const lokinet_udp_flowinfo* remote, void** flowdata, int* timeout)
|
||||||
|
{
|
||||||
|
auto* ctx = static_cast<lokinet_context*>(user);
|
||||||
|
Flow* flow = new Flow{remote, ctx};
|
||||||
|
std::cout << "new udp flow: " << flow->String() << std::endl;
|
||||||
|
*flowdata = flow;
|
||||||
|
*timeout = 30;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DeleteFlow(const lokinet_udp_flowinfo* remote, void* flowdata)
|
||||||
|
{
|
||||||
|
auto* flow = static_cast<Flow*>(flowdata);
|
||||||
|
std::cout << "udp flow from " << flow->String() << " timed out" << std::endl;
|
||||||
|
delete flow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HandleUDPPacket(const lokinet_udp_flowinfo* remote, const char* pkt, size_t len, void* flowdata)
|
||||||
|
{
|
||||||
|
auto* flow = static_cast<Flow*>(flowdata);
|
||||||
|
std::cout << "we got " << len << " bytes of udp from " << flow->String() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BounceUDPPacket(const lokinet_udp_flowinfo* remote, const char* pkt, size_t len, void* flowdata)
|
||||||
|
{
|
||||||
|
auto* flow = static_cast<Flow*>(flowdata);
|
||||||
|
std::cout << "bounce " << len << " bytes of udp from " << flow->String() << std::endl;
|
||||||
|
if (auto err = lokinet_udp_flow_send(remote, pkt, len, flow->Context()))
|
||||||
|
{
|
||||||
|
std::cout << "bounce failed: " << strerror(err) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Lokinet_ptr sender, recip;
|
||||||
|
|
||||||
|
void
|
||||||
|
signal_handler(int)
|
||||||
|
{
|
||||||
|
_run = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
if (argc == 1)
|
||||||
|
{
|
||||||
|
std::cout << "usage: " << argv[0] << " bootstrap.signed" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::vector<char> bootstrap;
|
||||||
|
|
||||||
|
// load bootstrap.signed
|
||||||
|
{
|
||||||
|
std::ifstream inf{argv[1], std::ifstream::ate | std::ifstream::binary};
|
||||||
|
size_t len = inf.tellg();
|
||||||
|
inf.seekg(0);
|
||||||
|
bootstrap.resize(len);
|
||||||
|
inf.read(bootstrap.data(), bootstrap.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* loglevel = getenv("LOKINET_LOG"))
|
||||||
|
lokinet_log_level(loglevel);
|
||||||
|
else
|
||||||
|
lokinet_log_level("none");
|
||||||
|
|
||||||
|
std::cout << "starting up" << std::endl;
|
||||||
|
|
||||||
|
recip = MakeLokinet(bootstrap);
|
||||||
|
WaitForReady(recip);
|
||||||
|
|
||||||
|
lokinet_udp_bind_result recipBindResult{};
|
||||||
|
|
||||||
|
const auto port = 10000;
|
||||||
|
|
||||||
|
if (auto err = lokinet_udp_bind(
|
||||||
|
port,
|
||||||
|
ProcessNewInboundFlow,
|
||||||
|
BounceUDPPacket,
|
||||||
|
DeleteFlow,
|
||||||
|
recip.get(),
|
||||||
|
&recipBindResult,
|
||||||
|
recip.get()))
|
||||||
|
{
|
||||||
|
std::cout << "failed to bind recip udp socket " << strerror(err) << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "bound recip udp" << std::endl;
|
||||||
|
|
||||||
|
sender = MakeLokinet(bootstrap);
|
||||||
|
WaitForReady(sender);
|
||||||
|
|
||||||
|
std::string recipaddr{lokinet_address(recip.get())};
|
||||||
|
|
||||||
|
std::cout << "recip ready at " << recipaddr << std::endl;
|
||||||
|
|
||||||
|
lokinet_udp_bind_result senderBindResult{};
|
||||||
|
|
||||||
|
if (auto err = lokinet_udp_bind(
|
||||||
|
port,
|
||||||
|
ProcessNewInboundFlow,
|
||||||
|
HandleUDPPacket,
|
||||||
|
DeleteFlow,
|
||||||
|
sender.get(),
|
||||||
|
&senderBindResult,
|
||||||
|
sender.get()))
|
||||||
|
{
|
||||||
|
std::cout << "failed to bind sender udp socket " << strerror(err) << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectJob connect{};
|
||||||
|
connect.remote.socket_id = senderBindResult.socket_id;
|
||||||
|
connect.remote.remote_port = port;
|
||||||
|
std::copy_n(recipaddr.c_str(), recipaddr.size(), connect.remote.remote_host);
|
||||||
|
connect.ctx = sender.get();
|
||||||
|
|
||||||
|
std::cout << "bound sender udp" << std::endl;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
std::cout << "try establish to " << connect.remote.remote_host << std::endl;
|
||||||
|
if (auto err =
|
||||||
|
lokinet_udp_establish(CreateOutboundFlow, &connect, &connect.remote, sender.get()))
|
||||||
|
{
|
||||||
|
std::cout << "failed to establish to recip: " << strerror(err) << std::endl;
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
} while (true);
|
||||||
|
std::cout << "sender established" << std::endl;
|
||||||
|
|
||||||
|
const std::string buf{"liblokinet"};
|
||||||
|
|
||||||
|
const std::string senderAddr{lokinet_address(sender.get())};
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
std::cout << senderAddr << " send to remote: " << buf << std::endl;
|
||||||
|
if (auto err = lokinet_udp_flow_send(&connect.remote, buf.data(), buf.size(), sender.get()))
|
||||||
|
{
|
||||||
|
std::cout << "send failed: " << strerror(err) << std::endl;
|
||||||
|
}
|
||||||
|
usleep(100000);
|
||||||
|
} while (_run);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,8 +1,3 @@
|
||||||
set(DEFAULT_WITH_BOOTSTRAP ON)
|
|
||||||
if(APPLE)
|
|
||||||
set(DEFAULT_WITH_BOOTSTRAP OFF)
|
|
||||||
endif()
|
|
||||||
option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP})
|
|
||||||
|
|
||||||
add_executable(lokinet-vpn lokinet-vpn.cpp)
|
add_executable(lokinet-vpn lokinet-vpn.cpp)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
@ -11,11 +6,10 @@ if(APPLE)
|
||||||
else()
|
else()
|
||||||
add_executable(lokinet lokinet.cpp)
|
add_executable(lokinet lokinet.cpp)
|
||||||
enable_lto(lokinet lokinet-vpn)
|
enable_lto(lokinet lokinet-vpn)
|
||||||
|
endif()
|
||||||
if(WITH_BOOTSTRAP)
|
if(WITH_BOOTSTRAP)
|
||||||
add_executable(lokinet-bootstrap lokinet-bootstrap.cpp)
|
add_executable(lokinet-bootstrap lokinet-bootstrap.cpp)
|
||||||
enable_lto(lokinet-bootstrap)
|
enable_lto(lokinet-bootstrap)
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -56,26 +56,27 @@ add_ngtcp2_lib()
|
||||||
|
|
||||||
# cpr configuration. Ideally we'd just do this via add_subdirectory, but cpr's cmake requires
|
# cpr configuration. Ideally we'd just do this via add_subdirectory, but cpr's cmake requires
|
||||||
# 3.15+, and we target lower than that (and this is fairly simple to build).
|
# 3.15+, and we target lower than that (and this is fairly simple to build).
|
||||||
|
if(WITH_BOOTSTRAP)
|
||||||
|
if(NOT BUILD_STATIC_DEPS)
|
||||||
|
find_package(CURL REQUIRED COMPONENTS HTTP HTTPS SSL)
|
||||||
|
|
||||||
if(NOT BUILD_STATIC_DEPS)
|
# CURL::libcurl wasn't added to FindCURL until cmake 3.12, so add it if necessary
|
||||||
find_package(CURL REQUIRED COMPONENTS HTTP HTTPS SSL)
|
if (CMAKE_VERSION VERSION_LESS 3.12 AND NOT TARGET CURL::libcurl)
|
||||||
|
add_library(libcurl UNKNOWN IMPORTED GLOBAL)
|
||||||
# CURL::libcurl wasn't added to FindCURL until cmake 3.12, so add it if necessary
|
set_target_properties(libcurl PROPERTIES
|
||||||
if (CMAKE_VERSION VERSION_LESS 3.12 AND NOT TARGET CURL::libcurl)
|
IMPORTED_LOCATION ${CURL_LIBRARIES}
|
||||||
add_library(libcurl UNKNOWN IMPORTED GLOBAL)
|
INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}")
|
||||||
set_target_properties(libcurl PROPERTIES
|
add_library(CURL_libcurl INTERFACE)
|
||||||
IMPORTED_LOCATION ${CURL_LIBRARIES}
|
target_link_libraries(CURL_libcurl INTERFACE libcurl)
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}")
|
add_library(CURL::libcurl ALIAS CURL_libcurl)
|
||||||
add_library(CURL_libcurl INTERFACE)
|
endif()
|
||||||
target_link_libraries(CURL_libcurl INTERFACE libcurl)
|
|
||||||
add_library(CURL::libcurl ALIAS CURL_libcurl)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
file(GLOB cpr_sources ${conf_depends} cpr/cpr/*.cpp)
|
||||||
|
|
||||||
|
add_library(cpr STATIC EXCLUDE_FROM_ALL ${cpr_sources})
|
||||||
|
target_link_libraries(cpr PUBLIC CURL::libcurl)
|
||||||
|
target_include_directories(cpr PUBLIC cpr/include)
|
||||||
|
target_compile_definitions(cpr PUBLIC CPR_CURL_NOSIGNAL)
|
||||||
|
add_library(cpr::cpr ALIAS cpr)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
file(GLOB cpr_sources ${conf_depends} cpr/cpr/*.cpp)
|
|
||||||
|
|
||||||
add_library(cpr STATIC EXCLUDE_FROM_ALL ${cpr_sources})
|
|
||||||
target_link_libraries(cpr PUBLIC CURL::libcurl)
|
|
||||||
target_include_directories(cpr PUBLIC cpr/include)
|
|
||||||
target_compile_definitions(cpr PUBLIC CPR_CURL_NOSIGNAL)
|
|
||||||
add_library(cpr::cpr ALIAS cpr)
|
|
||||||
|
|
|
@ -21,6 +21,17 @@ extern "C"
|
||||||
int EXPORT
|
int EXPORT
|
||||||
lokinet_log_level(const char*);
|
lokinet_log_level(const char*);
|
||||||
|
|
||||||
|
typedef void (*lokinet_logger_func)(const char*, void*);
|
||||||
|
|
||||||
|
/// set a custom logger function
|
||||||
|
void EXPORT
|
||||||
|
lokinet_set_logger(lokinet_logger_func func, void* user);
|
||||||
|
|
||||||
|
/// @brief take in hex and turn it into base32z
|
||||||
|
/// @return value must be free()'d later
|
||||||
|
char* EXPORT
|
||||||
|
lokinet_hex_to_base32z(const char* hex);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,7 +8,6 @@ extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// the result of a lokinet stream mapping attempt
|
/// the result of a lokinet stream mapping attempt
|
||||||
#pragma pack(1)
|
|
||||||
struct lokinet_stream_result
|
struct lokinet_stream_result
|
||||||
{
|
{
|
||||||
/// set to zero on success otherwise the error that happened
|
/// set to zero on success otherwise the error that happened
|
||||||
|
@ -23,7 +22,6 @@ extern "C"
|
||||||
/// the id of the stream we created
|
/// the id of the stream we created
|
||||||
int stream_id;
|
int stream_id;
|
||||||
};
|
};
|
||||||
#pragma pack()
|
|
||||||
|
|
||||||
/// connect out to a remote endpoint
|
/// connect out to a remote endpoint
|
||||||
/// remoteAddr is in the form of "name:port"
|
/// remoteAddr is in the form of "name:port"
|
||||||
|
@ -39,7 +37,7 @@ extern "C"
|
||||||
/// return 0 to accept
|
/// return 0 to accept
|
||||||
/// return -1 to explicitly reject
|
/// return -1 to explicitly reject
|
||||||
/// return -2 to silently drop
|
/// return -2 to silently drop
|
||||||
typedef int (*lokinet_stream_filter)(const char* remote, uint16_t port, void*);
|
typedef int (*lokinet_stream_filter)(const char* remote, uint16_t port, void* userdata);
|
||||||
|
|
||||||
/// set stream accepter filter
|
/// set stream accepter filter
|
||||||
/// passes user parameter into stream filter as void *
|
/// passes user parameter into stream filter as void *
|
||||||
|
@ -53,6 +51,9 @@ extern "C"
|
||||||
int EXPORT
|
int EXPORT
|
||||||
lokinet_inbound_stream(uint16_t port, struct lokinet_context* context);
|
lokinet_inbound_stream(uint16_t port, struct lokinet_context* context);
|
||||||
|
|
||||||
|
void EXPORT
|
||||||
|
lokinet_close_stream(int stream_id, struct lokinet_context* context);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,54 +2,22 @@
|
||||||
|
|
||||||
#include "lokinet_context.h"
|
#include "lokinet_context.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
struct iovec
|
|
||||||
{
|
|
||||||
void* iov_base;
|
|
||||||
size_t iov_len;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#include <sys/uio.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// information about a udp flow
|
/// information about a udp flow
|
||||||
struct lokinet_udp_flow
|
struct lokinet_udp_flowinfo
|
||||||
{
|
{
|
||||||
|
/// remote endpoint's .loki or .snode address
|
||||||
|
char remote_host[256];
|
||||||
|
/// remote endpont's port
|
||||||
|
uint16_t remote_port;
|
||||||
/// the socket id for this flow used for i/o purposes and closing this socket
|
/// the socket id for this flow used for i/o purposes and closing this socket
|
||||||
int socket_id;
|
int socket_id;
|
||||||
/// remote endpoint's .loki or .snode address
|
|
||||||
char remote_addr[256];
|
|
||||||
/// local endpoint's ip address
|
|
||||||
char local_addr[64];
|
|
||||||
/// remote endpont's port
|
|
||||||
int remote_port;
|
|
||||||
/// local endpoint's port
|
|
||||||
int local_port;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// establish an outbound udp flow
|
|
||||||
/// remoteHost is the remote .loki or .snode address conneting to
|
|
||||||
/// remotePort is either a string integer or an srv record name to lookup, e.g. thingservice in
|
|
||||||
/// which we do a srv lookup for _udp.thingservice.remotehost.tld and use the "best" port provided
|
|
||||||
/// localAddr is the local ip:port to bind our socket to, if localAddr is NULL then
|
|
||||||
/// lokinet_udp_sendmmsg MUST be used to send packets return 0 on success return nonzero on fail,
|
|
||||||
/// containing an errno value
|
|
||||||
int EXPORT
|
|
||||||
lokinet_udp_establish(
|
|
||||||
char* remoteHost,
|
|
||||||
char* remotePort,
|
|
||||||
char* localAddr,
|
|
||||||
struct lokinet_udp_flow* flow,
|
|
||||||
struct lokinet_context* ctx);
|
|
||||||
|
|
||||||
/// a result from a lokinet_udp_bind call
|
/// a result from a lokinet_udp_bind call
|
||||||
struct lokinet_udp_bind_result
|
struct lokinet_udp_bind_result
|
||||||
{
|
{
|
||||||
|
@ -57,48 +25,97 @@ extern "C"
|
||||||
int socket_id;
|
int socket_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// flow acceptor hook, return 0 success, return nonzero with errno on failure
|
||||||
|
typedef int (*lokinet_udp_flow_filter)(
|
||||||
|
void* userdata,
|
||||||
|
const struct lokinet_udp_flowinfo* remote_address,
|
||||||
|
void** flow_userdata,
|
||||||
|
int* timeout_seconds);
|
||||||
|
|
||||||
|
/// callback to make a new outbound flow
|
||||||
|
typedef void(lokinet_udp_create_flow_func)(
|
||||||
|
void* userdata, void** flow_userdata, int* timeout_seconds);
|
||||||
|
|
||||||
|
/// hook function for handling packets
|
||||||
|
typedef void (*lokinet_udp_flow_recv_func)(
|
||||||
|
const struct lokinet_udp_flowinfo* remote_address,
|
||||||
|
const char* pkt_data,
|
||||||
|
size_t pkt_length,
|
||||||
|
void* flow_userdata);
|
||||||
|
|
||||||
|
/// hook function for flow timeout
|
||||||
|
typedef void (*lokinet_udp_flow_timeout_func)(
|
||||||
|
const struct lokinet_udp_flowinfo* remote_address, void* flow_userdata);
|
||||||
|
|
||||||
/// inbound listen udp socket
|
/// inbound listen udp socket
|
||||||
/// expose udp port exposePort to the void
|
/// expose udp port exposePort to the void
|
||||||
/// if srv is not NULL add an srv record for this port, the format being "thingservice" in which
|
////
|
||||||
/// will add a srv record "_udp.thingservice.ouraddress.tld" that advertises this port provide
|
/// @param filter MUST be non null, pointing to a flow filter for accepting new udp flows, called
|
||||||
/// localAddr to forward inbound udp packets to "ip:port" if localAddr is NULL then the resulting
|
/// with user data
|
||||||
/// socket MUST be drained by lokinet_udp_recvmmsg
|
|
||||||
///
|
///
|
||||||
/// returns 0 on success
|
/// @param recv MUST be non null, pointing to a packet handler function for each flow, called
|
||||||
/// returns nonzero on error in which it is an errno value
|
/// with per flow user data provided by filter function if accepted
|
||||||
|
///
|
||||||
|
/// @param timeout MUST be non null,
|
||||||
|
/// pointing to a cleanup function to clean up a stale flow, staleness determined by the value
|
||||||
|
/// given by the filter function returns 0 on success
|
||||||
|
///
|
||||||
|
/// @returns nonzero on error in which it is an errno value
|
||||||
int EXPORT
|
int EXPORT
|
||||||
lokinet_udp_bind(
|
lokinet_udp_bind(
|
||||||
int exposedPort,
|
uint16_t exposedPort,
|
||||||
char* srv,
|
lokinet_udp_flow_filter filter,
|
||||||
char* localAddr,
|
lokinet_udp_flow_recv_func recv,
|
||||||
struct lokinet_udp_listen_result* result,
|
lokinet_udp_flow_timeout_func timeout,
|
||||||
|
void* user,
|
||||||
|
struct lokinet_udp_bind_result* result,
|
||||||
struct lokinet_context* ctx);
|
struct lokinet_context* ctx);
|
||||||
|
|
||||||
/// poll many udp sockets for activity
|
/// @brief establish a udp flow to remote endpoint
|
||||||
/// returns 0 on sucess
|
|
||||||
///
|
///
|
||||||
/// returns non zero errno on error
|
/// @param create_flow the callback to create the new flow if we establish one
|
||||||
|
///
|
||||||
|
/// @param user passed to new_flow as user data
|
||||||
|
///
|
||||||
|
/// @param remote the remote address to establish to
|
||||||
|
///
|
||||||
|
/// @param ctx the lokinet context to use
|
||||||
|
///
|
||||||
|
/// @return 0 on success, non zero errno on fail
|
||||||
int EXPORT
|
int EXPORT
|
||||||
lokinet_udp_poll(
|
lokinet_udp_establish(
|
||||||
const int* socket_ids,
|
lokinet_udp_create_flow_func create_flow,
|
||||||
size_t numsockets,
|
void* user,
|
||||||
const struct timespec* timeout,
|
const struct lokinet_udp_flowinfo* remote,
|
||||||
struct lokinet_context* ctx);
|
struct lokinet_context* ctx);
|
||||||
|
|
||||||
struct lokinet_udp_pkt
|
/// @brief send on an established flow to remote endpoint
|
||||||
{
|
/// blocks until we have sent the packet
|
||||||
char remote_addr[256];
|
///
|
||||||
int remote_port;
|
/// @param flowinfo remote flow to use for sending
|
||||||
struct iovec pkt;
|
///
|
||||||
};
|
/// @param ptr pointer to data to send
|
||||||
|
///
|
||||||
|
/// @param len the length of the data
|
||||||
|
///
|
||||||
|
/// @param ctx the lokinet context to use
|
||||||
|
///
|
||||||
|
/// @returns 0 on success and non zero errno on fail
|
||||||
|
int EXPORT
|
||||||
|
lokinet_udp_flow_send(
|
||||||
|
const struct lokinet_udp_flowinfo* remote,
|
||||||
|
const void* ptr,
|
||||||
|
size_t len,
|
||||||
|
struct lokinet_context* ctx);
|
||||||
|
|
||||||
/// analog to recvmmsg
|
/// @brief close a bound udp socket
|
||||||
ssize_t EXPORT
|
/// closes all flows immediately
|
||||||
lokinet_udp_recvmmsg(
|
///
|
||||||
int socket_id,
|
/// @param socket_id the bound udp socket's id
|
||||||
struct lokinet_udp_pkt* events,
|
///
|
||||||
size_t max_events,
|
/// @param ctx lokinet context
|
||||||
struct lokient_context* ctx);
|
void EXPORT
|
||||||
|
lokinet_udp_close(int socket_id, struct lokinet_context* ctx);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ add_library(lokinet-platform
|
||||||
net/net_int.cpp
|
net/net_int.cpp
|
||||||
net/sock_addr.cpp
|
net/sock_addr.cpp
|
||||||
vpn/packet_router.cpp
|
vpn/packet_router.cpp
|
||||||
|
vpn/egres_packet_router.cpp
|
||||||
vpn/platform.cpp
|
vpn/platform.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "bootstrap.hpp"
|
#include "bootstrap.hpp"
|
||||||
#include "util/bencode.hpp"
|
#include "util/bencode.hpp"
|
||||||
|
#include "util/logging/logger.hpp"
|
||||||
|
#include "util/logging/buffer.hpp"
|
||||||
|
|
||||||
namespace llarp
|
namespace llarp
|
||||||
{
|
{
|
||||||
|
@ -16,9 +18,12 @@ namespace llarp
|
||||||
[&](llarp_buffer_t* b, bool more) -> bool {
|
[&](llarp_buffer_t* b, bool more) -> bool {
|
||||||
if (more)
|
if (more)
|
||||||
{
|
{
|
||||||
RouterContact rc;
|
RouterContact rc{};
|
||||||
if (not rc.BDecode(b))
|
if (not rc.BDecode(b))
|
||||||
|
{
|
||||||
|
LogError("invalid rc in bootstrap list: ", llarp::buffer_printer{*b});
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
emplace(std::move(rc));
|
emplace(std::move(rc));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <llarp/bootstrap.hpp>
|
||||||
#include <llarp/crypto/types.hpp>
|
#include <llarp/crypto/types.hpp>
|
||||||
#include <llarp/router_contact.hpp>
|
#include <llarp/router_contact.hpp>
|
||||||
#include <llarp/util/fs.hpp>
|
#include <llarp/util/fs.hpp>
|
||||||
|
@ -196,7 +197,7 @@ namespace llarp
|
||||||
struct BootstrapConfig
|
struct BootstrapConfig
|
||||||
{
|
{
|
||||||
std::vector<fs::path> files;
|
std::vector<fs::path> files;
|
||||||
std::set<RouterContact> routers;
|
BootstrapList routers;
|
||||||
bool seednode;
|
bool seednode;
|
||||||
void
|
void
|
||||||
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);
|
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);
|
||||||
|
|
|
@ -5,87 +5,120 @@
|
||||||
#include <llarp/quic/tunnel.hpp>
|
#include <llarp/quic/tunnel.hpp>
|
||||||
#include <llarp/router/abstractrouter.hpp>
|
#include <llarp/router/abstractrouter.hpp>
|
||||||
#include <llarp/ev/ev.hpp>
|
#include <llarp/ev/ev.hpp>
|
||||||
|
#include <llarp/vpn/egres_packet_router.hpp>
|
||||||
|
|
||||||
namespace llarp
|
namespace llarp::handlers
|
||||||
{
|
{
|
||||||
namespace handlers
|
struct NullEndpoint final : public llarp::service::Endpoint,
|
||||||
|
public std::enable_shared_from_this<NullEndpoint>
|
||||||
{
|
{
|
||||||
struct NullEndpoint final : public llarp::service::Endpoint,
|
NullEndpoint(AbstractRouter* r, llarp::service::Context* parent)
|
||||||
public std::enable_shared_from_this<NullEndpoint>
|
: llarp::service::Endpoint{r, parent}
|
||||||
|
, m_PacketRouter{new vpn::EgresPacketRouter{[](auto from, auto pkt) {
|
||||||
|
var::visit(
|
||||||
|
[&pkt](auto&& from) {
|
||||||
|
LogError("unhandled traffic from: ", from, " of ", pkt.sz, " bytes");
|
||||||
|
},
|
||||||
|
from);
|
||||||
|
}}}
|
||||||
{
|
{
|
||||||
NullEndpoint(AbstractRouter* r, llarp::service::Context* parent)
|
r->loop()->add_ticker([this] { Pump(Now()); });
|
||||||
: llarp::service::Endpoint(r, parent)
|
}
|
||||||
{
|
|
||||||
r->loop()->add_ticker([this] { Pump(Now()); });
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
HandleInboundPacket(
|
HandleInboundPacket(
|
||||||
const service::ConvoTag tag,
|
const service::ConvoTag tag,
|
||||||
const llarp_buffer_t& buf,
|
const llarp_buffer_t& buf,
|
||||||
service::ProtocolType t,
|
service::ProtocolType t,
|
||||||
uint64_t) override
|
uint64_t) override
|
||||||
|
{
|
||||||
|
LogTrace("Inbound ", t, " packet (", buf.sz, "B) on convo ", tag);
|
||||||
|
if (t == service::ProtocolType::Control)
|
||||||
{
|
{
|
||||||
LogTrace("Inbound ", t, " packet (", buf.sz, "B) on convo ", tag);
|
|
||||||
if (t == service::ProtocolType::Control)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (t != service::ProtocolType::QUIC)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto* quic = GetQUICTunnel();
|
|
||||||
if (!quic)
|
|
||||||
{
|
|
||||||
LogWarn("incoming quic packet but this endpoint is not quic capable; dropping");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (buf.sz < 4)
|
|
||||||
{
|
|
||||||
LogWarn("invalid incoming quic packet, dropping");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
quic->receive_packet(tag, buf);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (t == service::ProtocolType::TrafficV4 or t == service::ProtocolType::TrafficV6)
|
||||||
std::string
|
|
||||||
GetIfName() const override
|
|
||||||
{
|
{
|
||||||
return "";
|
if (auto from = GetEndpointWithConvoTag(tag))
|
||||||
|
{
|
||||||
|
net::IPPacket pkt{};
|
||||||
|
if (not pkt.Load(buf))
|
||||||
|
{
|
||||||
|
LogWarn("invalid ip packet from remote T=", tag);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_PacketRouter->HandleIPPacketFrom(std::move(*from), std::move(pkt));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogWarn("did not handle packet, no endpoint with convotag T=", tag);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (t != service::ProtocolType::QUIC)
|
||||||
|
return false;
|
||||||
|
|
||||||
path::PathSet_ptr
|
auto* quic = GetQUICTunnel();
|
||||||
GetSelf() override
|
if (!quic)
|
||||||
{
|
|
||||||
return shared_from_this();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::weak_ptr<path::PathSet>
|
|
||||||
GetWeak() override
|
|
||||||
{
|
|
||||||
return weak_from_this();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SupportsV6() const override
|
|
||||||
{
|
{
|
||||||
|
LogWarn("incoming quic packet but this endpoint is not quic capable; dropping");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (buf.sz < 4)
|
||||||
void
|
|
||||||
SendPacketToRemote(const llarp_buffer_t&, service::ProtocolType) override{};
|
|
||||||
|
|
||||||
huint128_t ObtainIPForAddr(std::variant<service::Address, RouterID>) override
|
|
||||||
{
|
{
|
||||||
return {0};
|
LogWarn("invalid incoming quic packet, dropping");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
quic->receive_packet(tag, buf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::variant<service::Address, RouterID>> ObtainAddrForIP(
|
std::string
|
||||||
huint128_t) const override
|
GetIfName() const override
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return "";
|
||||||
}
|
}
|
||||||
};
|
|
||||||
} // namespace handlers
|
path::PathSet_ptr
|
||||||
} // namespace llarp
|
GetSelf() override
|
||||||
|
{
|
||||||
|
return shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::weak_ptr<path::PathSet>
|
||||||
|
GetWeak() override
|
||||||
|
{
|
||||||
|
return weak_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
SupportsV6() const override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SendPacketToRemote(const llarp_buffer_t&, service::ProtocolType) override{};
|
||||||
|
|
||||||
|
huint128_t ObtainIPForAddr(std::variant<service::Address, RouterID>) override
|
||||||
|
{
|
||||||
|
return {0};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::variant<service::Address, RouterID>> ObtainAddrForIP(
|
||||||
|
huint128_t) const override
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
vpn::EgresPacketRouter*
|
||||||
|
EgresPacketRouter() override
|
||||||
|
{
|
||||||
|
return m_PacketRouter.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<vpn::EgresPacketRouter> m_PacketRouter;
|
||||||
|
};
|
||||||
|
} // namespace llarp::handlers
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include "service/protocol_type.hpp"
|
#include <llarp/service/protocol_type.hpp>
|
||||||
|
|
||||||
namespace llarp
|
namespace llarp
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
#include <lokinet.h>
|
||||||
|
#include <llarp.hpp>
|
||||||
#include "lokinet.h"
|
|
||||||
#include "llarp.hpp"
|
|
||||||
#include <llarp/config/config.hpp>
|
#include <llarp/config/config.hpp>
|
||||||
#include <llarp/crypto/crypto_libsodium.hpp>
|
#include <llarp/crypto/crypto_libsodium.hpp>
|
||||||
|
|
||||||
|
@ -10,7 +8,13 @@
|
||||||
#include <llarp/quic/tunnel.hpp>
|
#include <llarp/quic/tunnel.hpp>
|
||||||
#include <llarp/nodedb.hpp>
|
#include <llarp/nodedb.hpp>
|
||||||
|
|
||||||
|
#include <llarp/util/logging/buffer.hpp>
|
||||||
|
|
||||||
|
#include <oxenmq/base32z.h>
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define EHOSTDOWN ENETDOWN
|
#define EHOSTDOWN ENETDOWN
|
||||||
|
@ -18,6 +22,34 @@
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
struct Logger : public llarp::ILogStream
|
||||||
|
{
|
||||||
|
lokinet_logger_func func;
|
||||||
|
void* user;
|
||||||
|
|
||||||
|
explicit Logger(lokinet_logger_func _func, void* _user) : func{_func}, user{_user}
|
||||||
|
{}
|
||||||
|
|
||||||
|
void
|
||||||
|
PreLog(std::stringstream&, llarp::LogLevel, std::string_view, int, const std::string&)
|
||||||
|
const override
|
||||||
|
{}
|
||||||
|
|
||||||
|
void
|
||||||
|
Print(llarp::LogLevel, std::string_view, const std::string& msg) override
|
||||||
|
{
|
||||||
|
func(msg.c_str(), user);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PostLog(std::stringstream&) const override{};
|
||||||
|
|
||||||
|
void
|
||||||
|
ImmediateFlush() override{};
|
||||||
|
|
||||||
|
void Tick(llarp_time_t) override{};
|
||||||
|
};
|
||||||
|
|
||||||
struct Context : public llarp::Context
|
struct Context : public llarp::Context
|
||||||
{
|
{
|
||||||
using llarp::Context::Context;
|
using llarp::Context::Context;
|
||||||
|
@ -28,6 +60,162 @@ namespace
|
||||||
return std::make_shared<llarp::NodeDB>();
|
return std::make_shared<llarp::NodeDB>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct UDPFlow
|
||||||
|
{
|
||||||
|
using Clock_t = std::chrono::steady_clock;
|
||||||
|
void* m_FlowUserData;
|
||||||
|
std::chrono::seconds m_FlowTimeout;
|
||||||
|
std::chrono::time_point<Clock_t> m_ExpiresAt;
|
||||||
|
lokinet_udp_flowinfo m_FlowInfo;
|
||||||
|
lokinet_udp_flow_recv_func m_Recv;
|
||||||
|
|
||||||
|
/// call timeout hook for this flow
|
||||||
|
void
|
||||||
|
TimedOut(lokinet_udp_flow_timeout_func timeout)
|
||||||
|
{
|
||||||
|
timeout(&m_FlowInfo, m_FlowUserData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// mark this flow as active
|
||||||
|
/// updates the expires at timestamp
|
||||||
|
void
|
||||||
|
MarkActive()
|
||||||
|
{
|
||||||
|
m_ExpiresAt = Clock_t::now() + m_FlowTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns true if we think this flow is expired
|
||||||
|
bool
|
||||||
|
IsExpired() const
|
||||||
|
{
|
||||||
|
return Clock_t::now() >= m_ExpiresAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HandlePacket(const llarp::net::IPPacket& pkt)
|
||||||
|
{
|
||||||
|
if (auto maybe = pkt.L4Data())
|
||||||
|
{
|
||||||
|
MarkActive();
|
||||||
|
m_Recv(&m_FlowInfo, maybe->first, maybe->second, m_FlowUserData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UDPHandler
|
||||||
|
{
|
||||||
|
using AddressVariant_t = llarp::vpn::AddressVariant_t;
|
||||||
|
int m_SocketID;
|
||||||
|
llarp::nuint16_t m_LocalPort;
|
||||||
|
lokinet_udp_flow_filter m_Filter;
|
||||||
|
lokinet_udp_flow_recv_func m_Recv;
|
||||||
|
lokinet_udp_flow_timeout_func m_Timeout;
|
||||||
|
void* m_User;
|
||||||
|
std::weak_ptr<llarp::service::Endpoint> m_Endpoint;
|
||||||
|
|
||||||
|
std::unordered_map<AddressVariant_t, UDPFlow> m_Flows;
|
||||||
|
|
||||||
|
std::mutex m_Access;
|
||||||
|
|
||||||
|
explicit UDPHandler(
|
||||||
|
int socketid,
|
||||||
|
llarp::nuint16_t localport,
|
||||||
|
lokinet_udp_flow_filter filter,
|
||||||
|
lokinet_udp_flow_recv_func recv,
|
||||||
|
lokinet_udp_flow_timeout_func timeout,
|
||||||
|
void* user,
|
||||||
|
std::weak_ptr<llarp::service::Endpoint> ep)
|
||||||
|
: m_SocketID{socketid}
|
||||||
|
, m_LocalPort{localport}
|
||||||
|
, m_Filter{filter}
|
||||||
|
, m_Recv{recv}
|
||||||
|
, m_Timeout{timeout}
|
||||||
|
, m_User{user}
|
||||||
|
, m_Endpoint{ep}
|
||||||
|
{}
|
||||||
|
|
||||||
|
void
|
||||||
|
KillAllFlows()
|
||||||
|
{
|
||||||
|
std::unique_lock lock{m_Access};
|
||||||
|
for (auto& item : m_Flows)
|
||||||
|
{
|
||||||
|
item.second.TimedOut(m_Timeout);
|
||||||
|
}
|
||||||
|
m_Flows.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AddFlow(
|
||||||
|
const AddressVariant_t& from,
|
||||||
|
const lokinet_udp_flowinfo& flow_addr,
|
||||||
|
void* flow_userdata,
|
||||||
|
int flow_timeoutseconds,
|
||||||
|
std::optional<llarp::net::IPPacket> firstPacket = std::nullopt)
|
||||||
|
{
|
||||||
|
std::unique_lock lock{m_Access};
|
||||||
|
auto& flow = m_Flows[from];
|
||||||
|
flow.m_FlowInfo = flow_addr;
|
||||||
|
flow.m_FlowTimeout = std::chrono::seconds{flow_timeoutseconds};
|
||||||
|
flow.m_FlowUserData = flow_userdata;
|
||||||
|
flow.m_Recv = m_Recv;
|
||||||
|
if (firstPacket)
|
||||||
|
flow.HandlePacket(*firstPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ExpireOldFlows()
|
||||||
|
{
|
||||||
|
std::unique_lock lock{m_Access};
|
||||||
|
for (auto itr = m_Flows.begin(); itr != m_Flows.end();)
|
||||||
|
{
|
||||||
|
if (itr->second.IsExpired())
|
||||||
|
{
|
||||||
|
itr->second.TimedOut(m_Timeout);
|
||||||
|
itr = m_Flows.erase(itr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HandlePacketFrom(AddressVariant_t from, llarp::net::IPPacket pkt)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock lock{m_Access};
|
||||||
|
if (m_Flows.count(from))
|
||||||
|
{
|
||||||
|
m_Flows[from].HandlePacket(pkt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lokinet_udp_flowinfo flow_addr{};
|
||||||
|
// set flow remote address
|
||||||
|
std::string addrstr = var::visit([&flow_addr](auto&& from) { return from.ToString(); }, from);
|
||||||
|
|
||||||
|
std::copy_n(
|
||||||
|
addrstr.data(),
|
||||||
|
std::min(addrstr.size(), sizeof(flow_addr.remote_host)),
|
||||||
|
flow_addr.remote_host);
|
||||||
|
// set socket id
|
||||||
|
flow_addr.socket_id = m_SocketID;
|
||||||
|
// get source port
|
||||||
|
if (const auto srcport = pkt.SrcPort())
|
||||||
|
{
|
||||||
|
flow_addr.remote_port = ToHost(*srcport).h;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return; // invalid data so we bail
|
||||||
|
void* flow_userdata = nullptr;
|
||||||
|
int flow_timeoutseconds{};
|
||||||
|
// got a new flow, let's check if we want it
|
||||||
|
if (m_Filter(m_User, &flow_addr, &flow_userdata, &flow_timeoutseconds))
|
||||||
|
return;
|
||||||
|
AddFlow(from, flow_addr, flow_userdata, flow_timeoutseconds, pkt);
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
struct lokinet_context
|
struct lokinet_context
|
||||||
|
@ -39,7 +227,10 @@ struct lokinet_context
|
||||||
|
|
||||||
std::unique_ptr<std::thread> runner;
|
std::unique_ptr<std::thread> runner;
|
||||||
|
|
||||||
lokinet_context() : impl{std::make_shared<Context>()}, config{llarp::Config::EmbeddedConfig()}
|
int _socket_id;
|
||||||
|
|
||||||
|
lokinet_context()
|
||||||
|
: impl{std::make_shared<Context>()}, config{llarp::Config::EmbeddedConfig()}, _socket_id{0}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
~lokinet_context()
|
~lokinet_context()
|
||||||
|
@ -48,6 +239,91 @@ struct lokinet_context
|
||||||
runner->join();
|
runner->join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
next_socket_id()
|
||||||
|
{
|
||||||
|
int id = ++_socket_id;
|
||||||
|
// handle overflow
|
||||||
|
if (id < 0)
|
||||||
|
{
|
||||||
|
_socket_id = 0;
|
||||||
|
id = ++_socket_id;
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// make a udp handler and hold onto it
|
||||||
|
/// return its id
|
||||||
|
[[nodiscard]] std::optional<int>
|
||||||
|
make_udp_handler(
|
||||||
|
const std::shared_ptr<llarp::service::Endpoint>& ep,
|
||||||
|
llarp::huint16_t exposePort,
|
||||||
|
lokinet_udp_flow_filter filter,
|
||||||
|
lokinet_udp_flow_recv_func recv,
|
||||||
|
lokinet_udp_flow_timeout_func timeout,
|
||||||
|
void* user)
|
||||||
|
{
|
||||||
|
if (udp_sockets.empty())
|
||||||
|
{
|
||||||
|
// start udp flow expiration timer
|
||||||
|
impl->router->loop()->call_every(1s, std::make_shared<int>(0), [this]() {
|
||||||
|
std::unique_lock lock{m_access};
|
||||||
|
for (auto& item : udp_sockets)
|
||||||
|
{
|
||||||
|
item.second->ExpireOldFlows();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto udp = std::make_shared<UDPHandler>(
|
||||||
|
next_socket_id(), llarp::ToNet(exposePort), filter, recv, timeout, user, std::weak_ptr{ep});
|
||||||
|
auto id = udp->m_SocketID;
|
||||||
|
std::promise<bool> result;
|
||||||
|
|
||||||
|
impl->router->loop()->call([ep, &result, udp, exposePort]() {
|
||||||
|
if (auto pkt = ep->EgresPacketRouter())
|
||||||
|
{
|
||||||
|
pkt->AddUDPHandler(exposePort, [udp](auto from, auto pkt) {
|
||||||
|
udp->HandlePacketFrom(std::move(from), std::move(pkt));
|
||||||
|
});
|
||||||
|
result.set_value(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result.set_value(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.get_future().get())
|
||||||
|
{
|
||||||
|
udp_sockets[udp->m_SocketID] = std::move(udp);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
remove_udp_handler(int socket_id)
|
||||||
|
{
|
||||||
|
std::shared_ptr<UDPHandler> udp;
|
||||||
|
{
|
||||||
|
std::unique_lock lock{m_access};
|
||||||
|
if (auto itr = udp_sockets.find(socket_id); itr != udp_sockets.end())
|
||||||
|
{
|
||||||
|
udp = std::move(itr->second);
|
||||||
|
udp_sockets.erase(itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (udp)
|
||||||
|
{
|
||||||
|
udp->KillAllFlows();
|
||||||
|
// remove packet handler
|
||||||
|
impl->router->loop()->call(
|
||||||
|
[ep = udp->m_Endpoint.lock(), localport = llarp::ToHost(udp->m_LocalPort)]() {
|
||||||
|
if (auto pkt = ep->EgresPacketRouter())
|
||||||
|
pkt->RemoveUDPHandler(localport);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// acquire mutex for accessing this context
|
/// acquire mutex for accessing this context
|
||||||
[[nodiscard]] auto
|
[[nodiscard]] auto
|
||||||
acquire()
|
acquire()
|
||||||
|
@ -62,6 +338,7 @@ struct lokinet_context
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_map<int, bool> streams;
|
std::unordered_map<int, bool> streams;
|
||||||
|
std::unordered_map<int, std::shared_ptr<UDPHandler>> udp_sockets;
|
||||||
|
|
||||||
void
|
void
|
||||||
inbound_stream(int id)
|
inbound_stream(int id)
|
||||||
|
@ -78,8 +355,6 @@ struct lokinet_context
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::unique_ptr<lokinet_context> g_context;
|
|
||||||
|
|
||||||
void
|
void
|
||||||
stream_error(lokinet_stream_result* result, int err)
|
stream_error(lokinet_stream_result* result, int err)
|
||||||
{
|
{
|
||||||
|
@ -230,18 +505,39 @@ extern "C"
|
||||||
int EXPORT
|
int EXPORT
|
||||||
lokinet_add_bootstrap_rc(const char* data, size_t datalen, struct lokinet_context* ctx)
|
lokinet_add_bootstrap_rc(const char* data, size_t datalen, struct lokinet_context* ctx)
|
||||||
{
|
{
|
||||||
|
if (data == nullptr or datalen == 0)
|
||||||
|
return -3;
|
||||||
llarp_buffer_t buf{data, datalen};
|
llarp_buffer_t buf{data, datalen};
|
||||||
llarp::RouterContact rc{};
|
|
||||||
if (ctx == nullptr)
|
if (ctx == nullptr)
|
||||||
return -3;
|
return -3;
|
||||||
auto lock = ctx->acquire();
|
auto lock = ctx->acquire();
|
||||||
// add a temp cryptography implementation here so rc.Verify works
|
// add a temp cryptography implementation here so rc.Verify works
|
||||||
llarp::CryptoManager instance{new llarp::sodium::CryptoLibSodium{}};
|
llarp::CryptoManager instance{new llarp::sodium::CryptoLibSodium{}};
|
||||||
if (not rc.BDecode(&buf))
|
if (data[0] == 'l')
|
||||||
return -1;
|
{
|
||||||
if (not rc.Verify(llarp::time_now_ms()))
|
if (not ctx->config->bootstrap.routers.BDecode(&buf))
|
||||||
return -2;
|
{
|
||||||
ctx->config->bootstrap.routers.insert(std::move(rc));
|
llarp::LogError("Cannot decode bootstrap list: ", llarp::buffer_printer{buf});
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (const auto& rc : ctx->config->bootstrap.routers)
|
||||||
|
{
|
||||||
|
if (not rc.Verify(llarp::time_now_ms()))
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
llarp::RouterContact rc{};
|
||||||
|
if (not rc.BDecode(&buf))
|
||||||
|
{
|
||||||
|
llarp::LogError("failed to decode signle RC: ", llarp::buffer_printer{buf});
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (not rc.Verify(llarp::time_now_ms()))
|
||||||
|
return -2;
|
||||||
|
ctx->config->bootstrap.routers.insert(std::move(rc));
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,11 +630,11 @@ extern "C"
|
||||||
return;
|
return;
|
||||||
auto lock = ctx->acquire();
|
auto lock = ctx->acquire();
|
||||||
|
|
||||||
if (not ctx->impl->IsStopping())
|
if (ctx->impl->IsStopping())
|
||||||
{
|
return;
|
||||||
ctx->impl->CloseAsync();
|
|
||||||
ctx->impl->Wait();
|
ctx->impl->CloseAsync();
|
||||||
}
|
ctx->impl->Wait();
|
||||||
|
|
||||||
if (ctx->runner)
|
if (ctx->runner)
|
||||||
ctx->runner->join();
|
ctx->runner->join();
|
||||||
|
@ -523,6 +819,27 @@ extern "C"
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* EXPORT
|
||||||
|
lokinet_hex_to_base32z(const char* hex)
|
||||||
|
{
|
||||||
|
std::string_view hexview{hex};
|
||||||
|
if (not oxenmq::is_hex(hexview))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const size_t byte_len = hexview.size() / 2;
|
||||||
|
const size_t b32z_len = (byte_len * 8 + 4) / 5; // = ⌈N×8÷5⌉ because 5 bits per 32z char
|
||||||
|
auto buf = std::make_unique<char[]>(b32z_len + 1);
|
||||||
|
char* end = buf.get() + b32z_len;
|
||||||
|
*end = 0; // null terminate
|
||||||
|
// Write the bytes into the *end* of the buffer so that when we rewrite the final b32z chars
|
||||||
|
// into the buffer we won't overwrite any byte values until after we've consumed them.
|
||||||
|
char* bytepos = end - byte_len;
|
||||||
|
oxenmq::from_hex(hexview.begin(), hexview.end(), bytepos);
|
||||||
|
// In-place conversion into the buffer
|
||||||
|
oxenmq::to_base32z(bytepos, end, buf.get());
|
||||||
|
return buf.release(); // leak the buffer to the caller
|
||||||
|
}
|
||||||
|
|
||||||
void EXPORT
|
void EXPORT
|
||||||
lokinet_close_stream(int stream_id, struct lokinet_context* ctx)
|
lokinet_close_stream(int stream_id, struct lokinet_context* ctx)
|
||||||
{
|
{
|
||||||
|
@ -594,4 +911,168 @@ extern "C"
|
||||||
delete result->internal;
|
delete result->internal;
|
||||||
result->internal = nullptr;
|
result->internal = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int EXPORT
|
||||||
|
lokinet_udp_bind(
|
||||||
|
uint16_t exposedPort,
|
||||||
|
lokinet_udp_flow_filter filter,
|
||||||
|
lokinet_udp_flow_recv_func recv,
|
||||||
|
lokinet_udp_flow_timeout_func timeout,
|
||||||
|
void* user,
|
||||||
|
struct lokinet_udp_bind_result* result,
|
||||||
|
struct lokinet_context* ctx)
|
||||||
|
{
|
||||||
|
if (filter == nullptr or recv == nullptr or timeout == nullptr or result == nullptr
|
||||||
|
or ctx == nullptr)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
auto lock = ctx->acquire();
|
||||||
|
if (auto ep = ctx->endpoint())
|
||||||
|
{
|
||||||
|
if (auto maybe =
|
||||||
|
ctx->make_udp_handler(ep, llarp::huint16_t{exposedPort}, filter, recv, timeout, user))
|
||||||
|
{
|
||||||
|
result->socket_id = *maybe;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EXPORT
|
||||||
|
lokinet_udp_close(int socket_id, struct lokinet_context* ctx)
|
||||||
|
{
|
||||||
|
if (ctx)
|
||||||
|
{
|
||||||
|
ctx->remove_udp_handler(socket_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int EXPORT
|
||||||
|
lokinet_udp_flow_send(
|
||||||
|
const struct lokinet_udp_flowinfo* remote,
|
||||||
|
const void* ptr,
|
||||||
|
size_t len,
|
||||||
|
struct lokinet_context* ctx)
|
||||||
|
{
|
||||||
|
if (remote == nullptr or remote->remote_port == 0 or ptr == nullptr or len == 0
|
||||||
|
or ctx == nullptr)
|
||||||
|
return EINVAL;
|
||||||
|
std::shared_ptr<llarp::EndpointBase> ep;
|
||||||
|
llarp::nuint16_t srcport{0};
|
||||||
|
llarp::nuint16_t dstport{llarp::ToNet(llarp::huint16_t{remote->remote_port})};
|
||||||
|
{
|
||||||
|
auto lock = ctx->acquire();
|
||||||
|
if (auto itr = ctx->udp_sockets.find(remote->socket_id); itr != ctx->udp_sockets.end())
|
||||||
|
{
|
||||||
|
ep = itr->second->m_Endpoint.lock();
|
||||||
|
srcport = itr->second->m_LocalPort;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return EHOSTUNREACH;
|
||||||
|
}
|
||||||
|
if (auto maybe = llarp::service::ParseAddress(std::string{remote->remote_host}))
|
||||||
|
{
|
||||||
|
llarp::net::IPPacket pkt = llarp::net::IPPacket::UDP(
|
||||||
|
llarp::nuint32_t{0},
|
||||||
|
srcport,
|
||||||
|
llarp::nuint32_t{0},
|
||||||
|
dstport,
|
||||||
|
llarp_buffer_t{reinterpret_cast<const uint8_t*>(ptr), len});
|
||||||
|
|
||||||
|
if (pkt.sz == 0)
|
||||||
|
return EINVAL;
|
||||||
|
std::promise<int> ret;
|
||||||
|
ctx->impl->router->loop()->call([addr = *maybe, pkt = std::move(pkt), ep, &ret]() {
|
||||||
|
if (auto tag = ep->GetBestConvoTagFor(addr))
|
||||||
|
{
|
||||||
|
if (ep->SendToOrQueue(*tag, pkt.ConstBuffer(), llarp::service::ProtocolType::TrafficV4))
|
||||||
|
{
|
||||||
|
ret.set_value(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret.set_value(ENETUNREACH);
|
||||||
|
});
|
||||||
|
return ret.get_future().get();
|
||||||
|
}
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EXPORT
|
||||||
|
lokinet_udp_establish(
|
||||||
|
lokinet_udp_create_flow_func create_flow,
|
||||||
|
void* user,
|
||||||
|
const struct lokinet_udp_flowinfo* remote,
|
||||||
|
struct lokinet_context* ctx)
|
||||||
|
{
|
||||||
|
if (create_flow == nullptr or remote == nullptr or ctx == nullptr)
|
||||||
|
return EINVAL;
|
||||||
|
std::shared_ptr<llarp::EndpointBase> ep;
|
||||||
|
{
|
||||||
|
auto lock = ctx->acquire();
|
||||||
|
if (ctx->impl->router->loop()->inEventLoop())
|
||||||
|
{
|
||||||
|
llarp::LogError("cannot call udp_establish from internal event loop");
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (auto itr = ctx->udp_sockets.find(remote->socket_id); itr != ctx->udp_sockets.end())
|
||||||
|
{
|
||||||
|
ep = itr->second->m_Endpoint.lock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return EHOSTUNREACH;
|
||||||
|
}
|
||||||
|
if (auto maybe = llarp::service::ParseAddress(std::string{remote->remote_host}))
|
||||||
|
{
|
||||||
|
{
|
||||||
|
// check for pre existing flow
|
||||||
|
auto lock = ctx->acquire();
|
||||||
|
if (auto itr = ctx->udp_sockets.find(remote->socket_id); itr != ctx->udp_sockets.end())
|
||||||
|
{
|
||||||
|
auto& udp = itr->second;
|
||||||
|
if (udp->m_Flows.count(*maybe))
|
||||||
|
{
|
||||||
|
// we already have a flow.
|
||||||
|
return EADDRINUSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::promise<bool> gotten;
|
||||||
|
ctx->impl->router->loop()->call([addr = *maybe, ep, &gotten]() {
|
||||||
|
ep->MarkAddressOutbound(addr);
|
||||||
|
auto res = ep->EnsurePathTo(
|
||||||
|
addr, [&gotten](auto result) { gotten.set_value(result.has_value()); }, 5s);
|
||||||
|
if (not res)
|
||||||
|
{
|
||||||
|
gotten.set_value(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (gotten.get_future().get())
|
||||||
|
{
|
||||||
|
void* flow_data{nullptr};
|
||||||
|
int flow_timeoutseconds{};
|
||||||
|
create_flow(user, &flow_data, &flow_timeoutseconds);
|
||||||
|
{
|
||||||
|
auto lock = ctx->acquire();
|
||||||
|
if (auto itr = ctx->udp_sockets.find(remote->socket_id); itr != ctx->udp_sockets.end())
|
||||||
|
{
|
||||||
|
itr->second->AddFlow(*maybe, *remote, flow_data, flow_timeoutseconds);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return EADDRINUSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return ETIMEDOUT;
|
||||||
|
}
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EXPORT
|
||||||
|
lokinet_set_logger(lokinet_logger_func func, void* user)
|
||||||
|
{
|
||||||
|
llarp::LogContext::Instance().logStream.reset(new Logger{func, user});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,19 @@ namespace llarp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<nuint16_t>
|
||||||
|
IPPacket::SrcPort() const
|
||||||
|
{
|
||||||
|
switch (IPProtocol{Header()->protocol})
|
||||||
|
{
|
||||||
|
case IPProtocol::TCP:
|
||||||
|
case IPProtocol::UDP:
|
||||||
|
return nuint16_t{*reinterpret_cast<const uint16_t*>(buf + (Header()->ihl * 4))};
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
huint32_t
|
huint32_t
|
||||||
IPPacket::srcv4() const
|
IPPacket::srcv4() const
|
||||||
{
|
{
|
||||||
|
@ -571,6 +584,26 @@ namespace llarp
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::pair<const char*, size_t>>
|
||||||
|
IPPacket::L4Data() const
|
||||||
|
{
|
||||||
|
const auto* hdr = Header();
|
||||||
|
size_t l4_HeaderSize = 0;
|
||||||
|
if (hdr->protocol == 0x11)
|
||||||
|
{
|
||||||
|
l4_HeaderSize = 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
// check for invalid size
|
||||||
|
if (sz < (hdr->ihl * 4) + l4_HeaderSize)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const uint8_t* ptr = buf + ((hdr->ihl * 4) + l4_HeaderSize);
|
||||||
|
return std::make_pair(reinterpret_cast<const char*>(ptr), std::distance(ptr, buf + sz));
|
||||||
|
}
|
||||||
|
|
||||||
IPPacket
|
IPPacket
|
||||||
IPPacket::UDP(
|
IPPacket::UDP(
|
||||||
nuint32_t srcaddr,
|
nuint32_t srcaddr,
|
||||||
|
|
|
@ -293,6 +293,14 @@ namespace llarp
|
||||||
std::optional<nuint16_t>
|
std::optional<nuint16_t>
|
||||||
DstPort() const;
|
DstPort() const;
|
||||||
|
|
||||||
|
/// get source port if applicable
|
||||||
|
std::optional<nuint16_t>
|
||||||
|
SrcPort() const;
|
||||||
|
|
||||||
|
/// get pointer and size of layer 4 data
|
||||||
|
std::optional<std::pair<const char*, size_t>>
|
||||||
|
L4Data() const;
|
||||||
|
|
||||||
void
|
void
|
||||||
UpdateIPv4Address(nuint32_t src, nuint32_t dst);
|
UpdateIPv4Address(nuint32_t src, nuint32_t dst);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
#include <oxenmq/variant.h>
|
#include <oxenmq/variant.h>
|
||||||
|
|
||||||
|
#include <llarp/vpn/egres_packet_router.hpp>
|
||||||
|
|
||||||
// minimum time between introset shifts
|
// minimum time between introset shifts
|
||||||
#ifndef MIN_SHIFT_INTERVAL
|
#ifndef MIN_SHIFT_INTERVAL
|
||||||
#define MIN_SHIFT_INTERVAL 5s
|
#define MIN_SHIFT_INTERVAL 5s
|
||||||
|
@ -168,6 +170,12 @@ namespace llarp
|
||||||
void
|
void
|
||||||
HandlePathDied(path::Path_ptr p) override;
|
HandlePathDied(path::Path_ptr p) override;
|
||||||
|
|
||||||
|
virtual vpn::EgresPacketRouter*
|
||||||
|
EgresPacketRouter()
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PublishIntroSet(const EncryptedIntroSet& i, AbstractRouter* r) override;
|
PublishIntroSet(const EncryptedIntroSet& i, AbstractRouter* r) override;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
#include "egres_packet_router.hpp"
|
||||||
|
|
||||||
|
namespace llarp::vpn
|
||||||
|
{
|
||||||
|
struct EgresUDPPacketHandler : public EgresLayer4Handler
|
||||||
|
{
|
||||||
|
EgresPacketHandlerFunc m_BaseHandler;
|
||||||
|
std::unordered_map<nuint16_t, EgresPacketHandlerFunc> m_LocalPorts;
|
||||||
|
|
||||||
|
explicit EgresUDPPacketHandler(EgresPacketHandlerFunc baseHandler)
|
||||||
|
: m_BaseHandler{std::move(baseHandler)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
void
|
||||||
|
AddSubHandler(nuint16_t localport, EgresPacketHandlerFunc handler) override
|
||||||
|
{
|
||||||
|
m_LocalPorts.emplace(std::move(localport), std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RemoveSubHandler(nuint16_t localport) override
|
||||||
|
{
|
||||||
|
m_LocalPorts.erase(localport);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HandleIPPacketFrom(AddressVariant_t from, net::IPPacket pkt) override
|
||||||
|
{
|
||||||
|
if (auto dstPort = pkt.DstPort())
|
||||||
|
{
|
||||||
|
if (auto itr = m_LocalPorts.find(*dstPort); itr != m_LocalPorts.end())
|
||||||
|
{
|
||||||
|
itr->second(std::move(from), std::move(pkt));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_BaseHandler(std::move(from), std::move(pkt));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EgresGenericLayer4Handler : public EgresLayer4Handler
|
||||||
|
{
|
||||||
|
EgresPacketHandlerFunc m_BaseHandler;
|
||||||
|
|
||||||
|
explicit EgresGenericLayer4Handler(EgresPacketHandlerFunc baseHandler)
|
||||||
|
: m_BaseHandler{std::move(baseHandler)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
void
|
||||||
|
HandleIPPacketFrom(AddressVariant_t from, net::IPPacket pkt) override
|
||||||
|
{
|
||||||
|
m_BaseHandler(std::move(from), std::move(pkt));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EgresPacketRouter::EgresPacketRouter(EgresPacketHandlerFunc baseHandler)
|
||||||
|
: m_BaseHandler{std::move(baseHandler)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
void
|
||||||
|
EgresPacketRouter::HandleIPPacketFrom(AddressVariant_t from, net::IPPacket pkt)
|
||||||
|
{
|
||||||
|
const auto proto = pkt.Header()->protocol;
|
||||||
|
if (const auto itr = m_IPProtoHandler.find(proto); itr != m_IPProtoHandler.end())
|
||||||
|
{
|
||||||
|
itr->second->HandleIPPacketFrom(std::move(from), std::move(pkt));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_BaseHandler(std::move(from), std::move(pkt));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr byte_t udp_proto = 0x11;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EgresPacketRouter::AddUDPHandler(huint16_t localport, EgresPacketHandlerFunc func)
|
||||||
|
{
|
||||||
|
if (m_IPProtoHandler.find(udp_proto) == m_IPProtoHandler.end())
|
||||||
|
{
|
||||||
|
m_IPProtoHandler.emplace(udp_proto, std::make_unique<EgresUDPPacketHandler>(m_BaseHandler));
|
||||||
|
}
|
||||||
|
m_IPProtoHandler[udp_proto]->AddSubHandler(ToNet(localport), std::move(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EgresPacketRouter::AddIProtoHandler(uint8_t proto, EgresPacketHandlerFunc func)
|
||||||
|
{
|
||||||
|
m_IPProtoHandler[proto] = std::make_unique<EgresGenericLayer4Handler>(std::move(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EgresPacketRouter::RemoveUDPHandler(huint16_t localport)
|
||||||
|
{
|
||||||
|
if (auto itr = m_IPProtoHandler.find(udp_proto); itr != m_IPProtoHandler.end())
|
||||||
|
{
|
||||||
|
itr->second->RemoveSubHandler(ToNet(localport));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace llarp::vpn
|
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
#include <llarp/net/net_int.hpp>
|
||||||
|
#include <llarp/net/ip_packet.hpp>
|
||||||
|
#include <llarp/endpoint_base.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace llarp::vpn
|
||||||
|
{
|
||||||
|
using AddressVariant_t = llarp::EndpointBase::AddressVariant_t;
|
||||||
|
using EgresPacketHandlerFunc = std::function<void(AddressVariant_t, net::IPPacket)>;
|
||||||
|
|
||||||
|
struct EgresLayer4Handler
|
||||||
|
{
|
||||||
|
virtual ~EgresLayer4Handler() = default;
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
HandleIPPacketFrom(AddressVariant_t from, net::IPPacket pkt) = 0;
|
||||||
|
|
||||||
|
virtual void AddSubHandler(nuint16_t, EgresPacketHandlerFunc){};
|
||||||
|
virtual void RemoveSubHandler(nuint16_t){};
|
||||||
|
};
|
||||||
|
|
||||||
|
class EgresPacketRouter
|
||||||
|
{
|
||||||
|
EgresPacketHandlerFunc m_BaseHandler;
|
||||||
|
std::unordered_map<uint8_t, std::unique_ptr<EgresLayer4Handler>> m_IPProtoHandler;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// baseHandler will be called if no other handlers matches a packet
|
||||||
|
explicit EgresPacketRouter(EgresPacketHandlerFunc baseHandler);
|
||||||
|
|
||||||
|
/// feed in an ip packet for handling
|
||||||
|
void
|
||||||
|
HandleIPPacketFrom(AddressVariant_t, net::IPPacket pkt);
|
||||||
|
|
||||||
|
/// add a non udp packet handler using ip protocol proto
|
||||||
|
void
|
||||||
|
AddIProtoHandler(uint8_t proto, EgresPacketHandlerFunc func);
|
||||||
|
|
||||||
|
/// helper that adds a udp packet handler for UDP destinted for localport
|
||||||
|
void
|
||||||
|
AddUDPHandler(huint16_t localport, EgresPacketHandlerFunc func);
|
||||||
|
|
||||||
|
/// remove a udp handler that is already set up by bound port
|
||||||
|
void
|
||||||
|
RemoveUDPHandler(huint16_t localport);
|
||||||
|
};
|
||||||
|
} // namespace llarp::vpn
|
|
@ -17,7 +17,6 @@ namespace llarp::vpn
|
||||||
|
|
||||||
virtual void AddSubHandler(nuint16_t, PacketHandlerFunc){};
|
virtual void AddSubHandler(nuint16_t, PacketHandlerFunc){};
|
||||||
};
|
};
|
||||||
|
|
||||||
class PacketRouter
|
class PacketRouter
|
||||||
{
|
{
|
||||||
PacketHandlerFunc m_BaseHandler;
|
PacketHandlerFunc m_BaseHandler;
|
||||||
|
@ -38,5 +37,9 @@ namespace llarp::vpn
|
||||||
/// helper that adds a udp packet handler for UDP destinted for localport
|
/// helper that adds a udp packet handler for UDP destinted for localport
|
||||||
void
|
void
|
||||||
AddUDPHandler(huint16_t localport, PacketHandlerFunc func);
|
AddUDPHandler(huint16_t localport, PacketHandlerFunc func);
|
||||||
|
|
||||||
|
/// remove a udp handler that is already set up by bound port
|
||||||
|
void
|
||||||
|
RemoveUDPHandler(huint16_t localport);
|
||||||
};
|
};
|
||||||
} // namespace llarp::vpn
|
} // namespace llarp::vpn
|
||||||
|
|
|
@ -75,18 +75,18 @@ namespace llarp
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def(
|
.def(
|
||||||
"setOutboundLink",
|
"setOutboundLink",
|
||||||
[](LinksConfig& self, std::string interface, int family, uint16_t port) {
|
[](LinksConfig& self, std::string _interface, int family, uint16_t port) {
|
||||||
LinksConfig::LinkInfo info;
|
LinksConfig::LinkInfo info;
|
||||||
info.m_interface = std::move(interface);
|
info.m_interface = std::move(_interface);
|
||||||
info.addressFamily = family;
|
info.addressFamily = family;
|
||||||
info.port = port;
|
info.port = port;
|
||||||
self.m_OutboundLink = std::move(info);
|
self.m_OutboundLink = std::move(info);
|
||||||
})
|
})
|
||||||
.def(
|
.def(
|
||||||
"addInboundLink",
|
"addInboundLink",
|
||||||
[](LinksConfig& self, std::string interface, int family, uint16_t port) {
|
[](LinksConfig& self, std::string _interface, int family, uint16_t port) {
|
||||||
LinksConfig::LinkInfo info;
|
LinksConfig::LinkInfo info;
|
||||||
info.m_interface = std::move(interface);
|
info.m_interface = std::move(_interface);
|
||||||
info.addressFamily = family;
|
info.addressFamily = family;
|
||||||
info.port = port;
|
info.port = port;
|
||||||
self.m_InboundLinks.push_back(info);
|
self.m_InboundLinks.push_back(info);
|
||||||
|
|
Loading…
Reference in New Issue