mirror of https://github.com/oxen-io/lokinet
updates:
* add udptest example * fix up udp codepath in liblokinet
This commit is contained in:
parent
b20e7bedf8
commit
5286d442fb
|
@ -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;
|
||||||
|
}
|
|
@ -14,7 +14,13 @@ namespace llarp::handlers
|
||||||
{
|
{
|
||||||
NullEndpoint(AbstractRouter* r, llarp::service::Context* parent)
|
NullEndpoint(AbstractRouter* r, llarp::service::Context* parent)
|
||||||
: llarp::service::Endpoint{r, parent}
|
: llarp::service::Endpoint{r, parent}
|
||||||
, m_PacketRouter{new vpn::EgresPacketRouter{[](auto, auto) {}}}
|
, m_PacketRouter{new vpn::EgresPacketRouter{[](auto from, auto pkt) {
|
||||||
|
var::visit(
|
||||||
|
[&pkt](auto&& from) {
|
||||||
|
LogError("unhandled traffic from: ", from, " of ", pkt.sz, " bytes");
|
||||||
|
},
|
||||||
|
from);
|
||||||
|
}}}
|
||||||
{
|
{
|
||||||
r->loop()->add_ticker([this] { Pump(Now()); });
|
r->loop()->add_ticker([this] { Pump(Now()); });
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,7 @@ namespace
|
||||||
flow.m_FlowInfo = flow_addr;
|
flow.m_FlowInfo = flow_addr;
|
||||||
flow.m_FlowTimeout = std::chrono::seconds{flow_timeoutseconds};
|
flow.m_FlowTimeout = std::chrono::seconds{flow_timeoutseconds};
|
||||||
flow.m_FlowUserData = flow_userdata;
|
flow.m_FlowUserData = flow_userdata;
|
||||||
|
flow.m_Recv = m_Recv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -285,11 +286,8 @@ struct lokinet_context
|
||||||
impl->router->loop()->call([ep, &result, udp, exposePort]() {
|
impl->router->loop()->call([ep, &result, udp, exposePort]() {
|
||||||
if (auto pkt = ep->EgresPacketRouter())
|
if (auto pkt = ep->EgresPacketRouter())
|
||||||
{
|
{
|
||||||
pkt->AddUDPHandler(exposePort, [udp = std::weak_ptr{udp}](auto from, auto pkt) {
|
pkt->AddUDPHandler(exposePort, [udp](auto from, auto pkt) {
|
||||||
if (auto ptr = udp.lock())
|
udp->HandlePacketFrom(std::move(from), std::move(pkt));
|
||||||
{
|
|
||||||
ptr->HandlePacketFrom(std::move(from), std::move(pkt));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
result.set_value(true);
|
result.set_value(true);
|
||||||
}
|
}
|
||||||
|
@ -1031,8 +1029,13 @@ extern "C"
|
||||||
}
|
}
|
||||||
std::promise<bool> gotten;
|
std::promise<bool> gotten;
|
||||||
ctx->impl->router->loop()->call([addr = *maybe, ep, &gotten]() {
|
ctx->impl->router->loop()->call([addr = *maybe, ep, &gotten]() {
|
||||||
ep->EnsurePathTo(
|
ep->MarkAddressOutbound(addr);
|
||||||
|
auto res = ep->EnsurePathTo(
|
||||||
addr, [&gotten](auto result) { gotten.set_value(result.has_value()); }, 5s);
|
addr, [&gotten](auto result) { gotten.set_value(result.has_value()); }, 5s);
|
||||||
|
if (not res)
|
||||||
|
{
|
||||||
|
gotten.set_value(false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if (gotten.get_future().get())
|
if (gotten.get_future().get())
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace llarp::vpn
|
||||||
void
|
void
|
||||||
AddSubHandler(nuint16_t localport, EgresPacketHandlerFunc handler) override
|
AddSubHandler(nuint16_t localport, EgresPacketHandlerFunc handler) override
|
||||||
{
|
{
|
||||||
m_LocalPorts.emplace(localport, std::move(handler));
|
m_LocalPorts.emplace(std::move(localport), std::move(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -26,14 +26,15 @@ namespace llarp::vpn
|
||||||
void
|
void
|
||||||
HandleIPPacketFrom(AddressVariant_t from, net::IPPacket pkt) override
|
HandleIPPacketFrom(AddressVariant_t from, net::IPPacket pkt) override
|
||||||
{
|
{
|
||||||
const uint8_t* ptr = pkt.buf + (pkt.Header()->ihl * 4) + 2;
|
if (auto dstPort = pkt.DstPort())
|
||||||
const nuint16_t dstPort{*reinterpret_cast<const uint16_t*>(ptr)};
|
|
||||||
if (auto itr = m_LocalPorts.find(dstPort); itr != m_LocalPorts.end())
|
|
||||||
{
|
{
|
||||||
itr->second(std::move(from), std::move(pkt));
|
if (auto itr = m_LocalPorts.find(*dstPort); itr != m_LocalPorts.end())
|
||||||
|
{
|
||||||
|
itr->second(std::move(from), std::move(pkt));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
m_BaseHandler(std::move(from), std::move(pkt));
|
||||||
m_BaseHandler(std::move(from), std::move(pkt));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,7 +81,7 @@ namespace llarp::vpn
|
||||||
{
|
{
|
||||||
m_IPProtoHandler.emplace(udp_proto, std::make_unique<EgresUDPPacketHandler>(m_BaseHandler));
|
m_IPProtoHandler.emplace(udp_proto, std::make_unique<EgresUDPPacketHandler>(m_BaseHandler));
|
||||||
}
|
}
|
||||||
m_IPProtoHandler[udp_proto]->AddSubHandler(ToNet(localport), func);
|
m_IPProtoHandler[udp_proto]->AddSubHandler(ToNet(localport), std::move(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
Loading…
Reference in New Issue