more work

pull/209/head
Jeff Becker 5 years ago
parent 593e2ddac6
commit e88c39b9e2
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -177,6 +177,7 @@ if(JEMALLOC)
set(MALLOC_LIB jemalloc)
endif(JEMALLOC)
# FS_LIB should resolve to nothing on all other platforms
# it is only required on win32 -rick
set(LIBS ${LIBS} ${MALLOC_LIB} ${FS_LIB})
@ -517,10 +518,9 @@ set(LIB_SRC
llarp/handlers/tun.cpp
llarp/ini.cpp
llarp/ip.cpp
llarp/iwp.cpp
llarp/link/curvecp.cpp
llarp/link/dtls.cpp
llarp/link/encoder.cpp
llarp/link/iwp.cpp
llarp/link/server.cpp
llarp/link/session.cpp
llarp/link/utp.cpp
@ -613,7 +613,7 @@ set(TEST_SRC
test/obtain_exit_unittest.cpp
test/pq_unittest.cpp
test/net_unittest.cpp
test/utp_unittest.cpp
test/link_layer_unittest.cpp
test/test_dns_unit.cpp
test/test_dnsc_unit.cpp
test/test_dnsd_unit.cpp

@ -0,0 +1,90 @@
Wire Protocol (version ½)
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in RFC 2119 [RFC2119].
LLARP supports by default an authenticated and framed transport over UTP [1]
Handshake:
Alice establishes a UTP "connection" with Bob.
Alice sends a LIM a_L encrpyted with the initial b_K key
if Bob accepts Alice's router, Bob replies with a LIM b_L encrpyted with the
b_K key.
next the session keys are generated via:
a_h = HS(a_K + a_L.n)
b_h = HS(b_K + b_L.n)
a_K = TKE(A.p, B_a.e, sk, a_h)
b_K = TKE(A.p, B_a.e, sk, b_h)
A.tx_K = b_K
A.rx_K = a_K
B.tx_K = a_K
B.rx_K = B_K
the initial value of a_K is HS(A.k) and b_K is HS(B.k)
1120 byte fragments are sent over UTP in an ordered fashion.
The each fragment F has the following structure:
[ 32 bytes blake2 keyed hash of the following 1088 bytes (h)]
[ 32 bytes random nonce (n)]
[ 1056 bytes encrypted payload (p)]
the recipiant verifies F.h == MDS(F.n + F.p, rx_K) and the UTP session
is reset if verification fails.
the decrypted payload P has the following structure:
[ 24 bytes random (A) ]
[ big endian unsigned 32 bit message id (I) ]
[ big endian unsigned 16 bit fragment length (N) ]
[ big endian unsigned 16 bit fragment remaining bytes (R) ]
[ N bytes of plaintext payload (X) ]
[ trailing bytes discarded ]
link layer messages fragmented and delievered in any order the sender chooses.
recipaint ensures a buffer for message number P.I exists, allocating one if it
does not exist.
recipiant appends P.X to the end of the buffer for message P.I
if P.R is zero then message number P.I is completed and processed as a link
layer messages. otherwise the recipiant expects P.R additional bytes.
P.R's value MUST decrease by P.N in the next fragment sent.
message size MUST NOT exceed 8192 bytes.
if a message is not received in 2 seconds it is discarded and any further
fragments for the message are also discarded.
P.I MUST have the initial value 0
P.I MUST be incremeneted by 1 for each new messsage transmitted
P.I MAY wrap around back to 0
after every fragment F the session key K is mutated via:
K = HS(K + P.A)
Periodically the connection initiator MUST renegotiate the session key by
sending a LIM after L.p milliseconds have elapsed.
If the local RC changes while a connection is established they MUST
renegotioate the session keys by sending a LIM to ensure the new RC is sent.
references:
[1] http://www.bittorrent.org/beps/bep_0029.html

@ -300,6 +300,7 @@ the RC.a matching the ipv6 address it originated from.
{
a: "i",
e: "<32 bytes ephemeral public encryption key>",
n: "<32 bytes nonce for key exhcange>",
p: uint64_milliseconds_session_period,
r: RC,

@ -1,90 +1,166 @@
Wire Protocol (version ½)
Wire Protocol (version 1)
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in RFC 2119 [RFC2119].
LLARP supports by default an authenticated and framed transport over UTP [1]
LLARP supports by default an authenticated message transport over a
datagram based network layer.
Handshake:
Alice establishes a UTP "connection" with Bob.
outer message format:
Alice sends a LIM a_L encrpyted with the initial b_K key
{
A: command,
B: <16 bytes flow id>,
C: <optional 32 bytes cookie>,
X: <N bytes payload>
}
if Bob accepts Alice's router, Bob replies with a LIM b_L encrpyted with the
b_K key.
comamnds:
next the session keys are generated via:
A - get handshake cookie
a_h = HS(a_K + a_L.n)
b_h = HS(b_K + b_L.n)
a_K = TKE(A.p, B_a.e, sk, a_h)
b_K = TKE(A.p, B_a.e, sk, b_h)
obtain a handshake cookie
A.tx_K = b_K
A.rx_K = a_K
B.tx_K = a_K
B.rx_K = B_K
B is randomized
X MUST contain the user agent string of the requester.
the initial value of a_K is HS(A.k) and b_K is HS(B.k)
the if the network id differs from the current network's id a reject message is
sent:
1120 byte fragments are sent over UTP in an ordered fashion.
{
A: R,
B: msg.B,
X: "<reply line>"
}
The each fragment F has the following structure:
MUST be replied to with a message rejected or a give handshake cookie
[ 32 bytes blake2 keyed hash of the following 1088 bytes (h)]
[ 32 bytes random nonce (n)]
[ 1056 bytes encrypted payload (p)]
C - give handshake cookie
the recipiant verifies F.h == MDS(F.n + F.p, rx_K) and the UTP session
is reset if verification fails.
give a handshake cookie to a remote endpoint that asks for one
the decrypted payload P has the following structure:
B is the B value from the get handshake cookie message
X is a 32 byte handshake cookie, calcuated via:
[ 24 bytes random (A) ]
[ big endian unsigned 32 bit message id (I) ]
[ big endian unsigned 16 bit fragment length (N) ]
[ big endian unsigned 16 bit fragment remaining bytes (R) ]
[ N bytes of plaintext payload (X) ]
[ trailing bytes discarded ]
r = RAND(32)
a = "<ascii representation of ip>" + " " + "<port number>"
X = HS(a + B + r)
link layer messages fragmented and delievered in any order the sender chooses.
R - message rejected
recipaint ensures a buffer for message number P.I exists, allocating one if it
does not exist.
B is the flow id from the recipiant
X is a reply line
recipiant appends P.X to the end of the buffer for message P.I
reject a message with flow id B
if P.R is zero then message number P.I is completed and processed as a link
layer messages. otherwise the recipiant expects P.R additional bytes.
P.R's value MUST decrease by P.N in the next fragment sent.
S - session negotiation
message size MUST NOT exceed 8192 bytes.
negotiate encrypted session
if a message is not received in 2 seconds it is discarded and any further
fragments for the message are also discarded.
B is the flow id from the recipiant
C is the handshake cookie
X is encrypted session negotiation data
P.I MUST have the initial value 0
P.I MUST be incremeneted by 1 for each new messsage transmitted
P.I MAY wrap around back to 0
D - encrypted data transmission
transmit encrypted data on session
after every fragment F the session key K is mutated via:
B is the flow id from the recipiant
X is authenticated and encrypted data
K = HS(K + P.A)
BNF:
Periodically the connection initiator MUST renegotiate the session key by
sending a LIM after L.p milliseconds have elapsed.
<reply-line> ::= <status-code> <space> <method> <space> <message>
If the local RC changes while a connection is established they MUST
renegotioate the session keys by sending a LIM to ensure the new RC is sent.
<status-code> ::= <integer> <digit> <digit>
<word> ::= <letter>+
references:
<message> ::= <word> <space> <word>* | <word>
[1] http://www.bittorrent.org/beps/bep_0029.html
<method> ::= "COOKIE" | "HANDSHAKE"
<user-agent> ::= <net-id> <space> <protocol-version> <space> <agent-version>
<net-id> ::= "lokinet" | "testnet"
<space> ::= " "
<zero> ::= "0"
<integer> ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
<digit> ::= <zero> | <integer>
<number> ::= <zero> | <integer> <digit>*
<agent-version> ::= <number> "." <number> "." <number>
<protocol-version> ::= <number>
session negotiation:
The session starts out with each side having 2 session keys rx_K and tx_K for
decrypting inbound messages and encrypting outbound messages respectively.
The initiator (alice) and the recipiant (bob) start out with static session keys
k_a = HS(a.k)
k_b = HS(b.k)
a.rx_K = k_a
b.rx_K = k_b
a.tx_K = k_b
b.tx_K = k_a
inner message format:
<32 bytes blake2s keyed hash of following data>
<24 bytes nounce>
<remaining bytes encrypted payload>
decryption is done via:
SD(remaining, rx_K, nounce)
encrypted payload is bencoded LIM (see proto_v0.txt)
the initiator starts out by sending a LIM a_LIM to the recipiant.
the recipiant replies with a LIM b_LIM to the initiator.
when the initiator gets a valid LIM from the recipiant the session keys for data
transmission are set to:
k_a = TKE(a.k, b.k, a.sk, a_LIM.n)
k_b = TKE(b.k, a.k, b.sk, b_LIM.n)
a.rx_K = k_a
b.rx_K = k_b
a.tx_K = k_b
b.tx_K = k_a
afterwards data transmission may happen
data tranmission:
message format:
<10 byte header>
<remaining data payload>
header format:
<1 byte proto version>
<1 byte command>
<1 byte flags>
<1 byte fragno>
<2 bytes fraglen>
<4 bytes seqno>

@ -1 +0,0 @@
#include <iwp.hpp>

@ -1,23 +0,0 @@
#ifndef LLARP_IWP_HPP
#define LLARP_IWP_HPP
#include <crypto.h>
#include <string>
namespace llarp
{
class Logic;
struct Router;
} // namespace llarp
struct llarp_iwp_args
{
struct llarp::Crypto* crypto;
llarp::Logic* logic;
struct llarp_threadpool* cryptoworker;
struct llarp::Router* router;
bool permitInbound;
};
#endif

@ -1,319 +0,0 @@
#include <link/dtls_internal.hpp>
#include <crypto.hpp>
#include <router.hpp>
#include <endian.hpp>
namespace llarp
{
namespace dtls
{
const mbedtls_ecp_group_id LinkLayer::AllowedCurve[2] = {
MBEDTLS_ECP_DP_CURVE25519, MBEDTLS_ECP_DP_NONE};
const int LinkLayer::AllowedHash[2] = {MBEDTLS_MD_SHA256, MBEDTLS_MD_NONE};
const int LinkLayer::CipherSuite[2] = {
MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0};
const mbedtls_x509_crt_profile LinkLayer::X509Profile = {
MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256),
MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECDSA),
MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_CURVE25519), 0};
static int
Random(void *ctx, unsigned char *buf, size_t sz)
{
static_cast< llarp::Crypto * >(ctx)->randbytes(buf, sz);
return 0;
}
static int
WriteCookie(void *ctx, unsigned char **p, unsigned char *,
const unsigned char *info, size_t ilen)
{
Session *self = static_cast< Session * >(ctx);
if(!self->crypto->hmac(*p, llarp::InitBuffer(info, ilen),
self->m_Parent->CookieSec()))
return -1;
*p += 32;
return 0;
}
static int
VerifyCookie(void *ctx, const unsigned char *cookie, size_t clen,
const unsigned char *info, size_t ilen)
{
if(clen != 32)
return -1;
Session *self = static_cast< Session * >(ctx);
ShortHash check;
if(!self->crypto->hmac(check.data(), llarp::InitBuffer(info, ilen),
self->m_Parent->CookieSec()))
return -1;
if(memcmp(check.data(), cookie, clen) == 0)
return 0;
return -1;
}
static int
InboundVerifyCert(void *, mbedtls_x509_crt *, int, unsigned int *)
{
return 0;
}
static int
OutboundVerifyCert(void *, mbedtls_x509_crt *, int, unsigned int *)
{
return 0;
}
Session::Session(LinkLayer *parent) : ILinkSession(), crypto(parent->crypto)
{
m_Parent = parent;
mbedtls_ssl_config_init(&m_config);
mbedtls_ssl_conf_transport(&m_config, MBEDTLS_SSL_TRANSPORT_DATAGRAM);
mbedtls_ssl_conf_authmode(&m_config, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_sig_hashes(&m_config, LinkLayer::AllowedHash);
m_config.p_vrfy = this;
m_config.key_cert = &m_Parent->ourKeys;
m_config.p_cookie = this;
m_config.f_cookie_write = &WriteCookie;
m_config.f_cookie_check = &VerifyCookie;
}
Session::Session(LinkLayer *parent, const llarp::Addr &from)
: Session(parent)
{
remoteAddr = from;
m_config.f_vrfy = &InboundVerifyCert;
byte_t buf[20] = {0};
parent->crypto->randbytes(buf, sizeof(buf));
htobe16buf(buf, from.port());
memcpy(buf + 2, from.addr6()->s6_addr, 16);
mbedtls_ssl_set_client_transport_id(&m_ctx, buf, sizeof(buf));
}
Session::Session(LinkLayer *parent, const RouterContact &rc,
const AddressInfo &ai)
: Session(parent)
{
remoteRC = rc;
remoteAddr = ai;
m_config.f_vrfy = &OutboundVerifyCert;
}
Session::~Session()
{
mbedtls_ssl_session_free(&m_session);
mbedtls_ssl_free(&m_ctx);
mbedtls_ssl_config_free(&m_config);
}
void
Session::Connect()
{
mbedtls_ssl_conf_endpoint(&m_config, MBEDTLS_SSL_IS_CLIENT);
Configure();
}
void
Session::Accept()
{
mbedtls_ssl_conf_endpoint(&m_config, MBEDTLS_SSL_IS_SERVER);
Configure();
}
void
Session::Configure()
{
m_config.ciphersuite_list[0] = LinkLayer::CipherSuite;
m_config.ciphersuite_list[1] = LinkLayer::CipherSuite;
m_config.ciphersuite_list[2] = LinkLayer::CipherSuite;
m_config.ciphersuite_list[3] = LinkLayer::CipherSuite;
m_config.p_dbg = nullptr;
m_config.f_dbg = &Session::Debug;
m_config.p_rng = m_Parent->crypto;
m_config.f_rng = &Random;
const auto *conf = &m_config;
mbedtls_ssl_setup(&m_ctx, conf);
}
void
Session::Recv_ll(const void *buf, size_t sz)
{
ll_recv.emplace_back(sz);
auto &back = ll_recv.back();
memcpy(back.data(), buf, sz);
}
void
Session::PumpIO()
{
llarp_time_t now = m_Parent->Now();
if(m_ctx.state == MBEDTLS_SSL_HANDSHAKE_OVER)
{
// pump inbound acks
{
auto itr = m_Inbound.begin();
while(itr != m_Inbound.end())
{
if(!itr->second.IsExpired(now))
{
if(itr->second.IsDone())
{
m_Parent->HandleMessage(this, itr->second.msg.as_buffer());
itr = m_Inbound.erase(itr);
continue;
}
else if(itr->second.ShouldRetransmit(now))
{
itr->second.TransmitAcks(&m_ctx, itr->first);
}
++itr;
}
else
itr = m_Inbound.erase(itr);
}
}
// pump outbound fragments
{
auto itr = m_Outbound.begin();
while(itr != m_Outbound.end())
{
if(itr->second.IsExpired(now) || itr->second.IsDone())
{
itr = m_Outbound.erase(itr);
continue;
}
else if(itr->second.ShouldRetransmit(now))
itr->second.TransmitUnacked(&m_ctx, itr->first);
++itr;
}
}
}
else
{
/// step the handshake
int res = mbedtls_ssl_handshake_step(&m_ctx);
switch(res)
{
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
break;
default:
// drop send queue
ll_send.clear();
// drop recv queue
ll_recv.clear();
// reset session
mbedtls_ssl_session_reset(&m_ctx);
return;
}
}
// low level sendto
while(ll_send.size())
{
m_Parent->SendTo_LL(remoteAddr, llarp::ConstBuffer(ll_send.front()));
ll_send.pop_front();
}
}
void
Session::Debug(void *, int, const char *fname, int lineno, const char *msg)
{
llarp::_Log(llarp::eLogInfo, fname, lineno, msg);
}
LinkLayer::LinkLayer(llarp::Crypto *c, const SecretKey &encryptionSecretKey,
const SecretKey &identitySecretKey,
llarp::GetRCFunc getrc, llarp::LinkMessageHandler h,
llarp::SignBufferFunc sign,
llarp::SessionEstablishedHandler established,
llarp::SessionRenegotiateHandler reneg,
llarp::TimeoutHandler timeout,
llarp::SessionClosedHandler closed)
: llarp::ILinkLayer(encryptionSecretKey, getrc, h, sign, established,
reneg, timeout, closed)
, crypto(c)
, m_IdentityKey(identitySecretKey)
{
}
bool
LinkLayer::Start(llarp::Logic *l)
{
if(!ILinkLayer::Start(l))
return false;
return crypto->shorthash(m_CookieSec, llarp::ConstBuffer(m_IdentityKey));
}
void
LinkLayer::RecvFrom(const llarp::Addr &from, const void *buf, size_t sz)
{
auto itr = m_Pending.find(from);
if(itr == m_Pending.end())
{
itr = m_Pending.insert(std::make_pair(from, new Session(this, from)))
.first;
itr->second->Start();
}
static_cast< Session * >(itr->second.get())->Recv_ll(buf, sz);
}
ILinkSession *
LinkLayer::NewOutboundSession(const llarp::RouterContact &rc,
const llarp::AddressInfo &ai)
{
return new Session(this, rc, ai);
}
void
LinkLayer::Pump()
{
std::set< RouterID > sessions;
{
Lock l(m_AuthedLinksMutex);
auto itr = m_AuthedLinks.begin();
while(itr != m_AuthedLinks.end())
{
sessions.insert(itr->first);
++itr;
}
}
ILinkLayer::Pump();
{
Lock l(m_AuthedLinksMutex);
for(const auto &pk : sessions)
{
if(m_AuthedLinks.count(pk) == 0)
{
// all sessions were removed
SessionClosed(pk);
}
}
}
}
std::unique_ptr< ILinkLayer >
NewServerFromRouter(llarp::Router *r)
{
return std::unique_ptr< LinkLayer >(new LinkLayer(
&r->crypto, r->encryption, r->identity,
std::bind(&llarp::Router::rc, r),
std::bind(&llarp::Router::HandleRecvLinkMessageBuffer, r,
std::placeholders::_1, std::placeholders::_2),
std::bind(&llarp::Router::Sign, r, std::placeholders::_1,
std::placeholders::_2),
std::bind(&llarp::Router::OnSessionEstablished, r,
std::placeholders::_1),
std::bind(&llarp::Router::CheckRenegotiateValid, r,
std::placeholders::_1, std::placeholders::_2),
std::bind(&llarp::Router::OnConnectTimeout, r, std::placeholders::_1),
std::bind(&llarp::Router::SessionClosed, r, std::placeholders::_1)));
}
} // namespace dtls
} // namespace llarp

@ -1,18 +0,0 @@
#ifndef LLARP_LINK_DTLS_HPP
#define LLARP_LINK_DTLS_HPP
#include <memory>
namespace llarp
{
struct ILinkLayer;
struct Router;
namespace dtls
{
std::unique_ptr< ILinkLayer >
NewServerFromRouter(llarp::Router* r);
}
} // namespace llarp
#endif

@ -0,0 +1,24 @@
#include <link/iwp_internal.hpp>
#include <router.hpp>
namespace llarp
{
namespace iwp
{
std::unique_ptr< ILinkLayer >
NewServerFromRouter(llarp::Router*)
{
// TODO: implement me
return nullptr;
}
std::unique_ptr< ILinkLayer >
NewServer(llarp::Crypto*, const SecretKey&, llarp::GetRCFunc,
llarp::LinkMessageHandler, llarp::SessionEstablishedHandler,
llarp::SessionRenegotiateHandler, llarp::SignBufferFunc,
llarp::TimeoutHandler, llarp::SessionClosedHandler)
{
// TODO: implement me
return nullptr;
}
} // namespace iwp
} // namespace llarp

@ -0,0 +1,25 @@
#ifndef LLARP_LINK_IWP_HPP
#define LLARP_LINK_IWP_HPP
#include <memory>
#include <link/server.hpp>
namespace llarp
{
namespace iwp
{
std::unique_ptr< ILinkLayer >
NewServer(llarp::Crypto* crypto, const SecretKey& routerEncSecret,
llarp::GetRCFunc getrc, llarp::LinkMessageHandler h,
llarp::SessionEstablishedHandler est,
llarp::SessionRenegotiateHandler reneg,
llarp::SignBufferFunc sign, llarp::TimeoutHandler timeout,
llarp::SessionClosedHandler closed);
std::unique_ptr< ILinkLayer >
NewServerFromRouter(llarp::Router* r);
} // namespace iwp
} // namespace llarp
#endif

@ -1,20 +1,15 @@
#ifndef LLARP_LINK_DTLS_INTERNAL_HPP
#define LLARP_LINK_DTLS_INTERNAL_HPP
#ifndef LLARP_LINK_IWP_INTERNAL_HPP
#define LLARP_LINK_IWP_INTERNAL_HPP
#include <link/server.hpp>
#include <link/session.hpp>
#include <link_layer.hpp>
#include <mbedtls/ssl_internal.h>
#include <mbedtls/ecp.h>
#include <mbedtls/md.h>
#include <mbedtls/x509.h>
#include <bitset>
#include <deque>
namespace llarp
{
namespace dtls
namespace iwp
{
struct LinkLayer;
@ -33,10 +28,15 @@ namespace llarp
PumpIO();
void
TickIO();
TickIO(llarp_time_t now);
bool
QueueMessageBuffer(llarp_buffer_t buf);
/// return true if the session is established and handshaked and all that
/// jazz
bool
QueueBuffer(llarp_buffer_t buf);
SessionIsEstablished();
/// inbound start
void
@ -53,22 +53,16 @@ namespace llarp
void
Configure();
/// ll recv
/// low level recv
void
Recv_ll(const void *buf, size_t sz);
static int
ssl_recv(void *ctx, unsigned char *buf, size_t sz);
static int
ssl_send(void *ctx, const unsigned char *buf, size_t sz);
static void
Debug(void *ctx, int lvl, const char *fname, int lineno, const char *msg);
/// verify a lim
bool
VerfiyLIM(const llarp::LinkIntroMessage *msg);
mbedtls_ssl_config m_config;
mbedtls_ssl_context m_ctx;
mbedtls_ssl_session m_session;
SharedSecret m_TXKey;
SharedSecret m_RXKey;
LinkLayer *m_Parent;
llarp::Crypto *const crypto;
llarp::RouterContact remoteRC;
@ -227,9 +221,11 @@ namespace llarp
return now > lastActiveAt && now - lastActiveAt > 500;
}
template < typename write_pkt_func >
bool
TransmitUnacked(mbedtls_ssl_context *ctx, Seqno_t seqno) const
TransmitUnacked(write_pkt_func write_pkt, Seqno_t seqno) const
{
static FragLen_t maxfragsize = fragsize;
FragmentHeader hdr;
hdr.seqno = seqno;
hdr.cmd = XMIT;
@ -240,7 +236,7 @@ namespace llarp
FragLen_t len = sz;
while(idx < maxfrags)
{
const FragLen_t l = std::min(len, fragsize);
const FragLen_t l = std::min(len, maxfragsize);
if(!acks.test(idx))
{
hdr.fragno = idx;
@ -250,7 +246,7 @@ namespace llarp
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
len -= l;
if(mbedtls_ssl_write(ctx, buf.base, buf.sz) != int(buf.sz))
if(write_pkt(buf.base, buf.sz) != int(buf.sz))
return false;
}
ptr += l;
@ -263,8 +259,9 @@ namespace llarp
return true;
}
template < typename write_pkt_func >
bool
TransmitAcks(mbedtls_ssl_context *ctx, Seqno_t seqno)
TransmitAcks(write_pkt_func write_pkt, Seqno_t seqno)
{
FragmentHeader hdr;
hdr.seqno = seqno;
@ -283,7 +280,7 @@ namespace llarp
auto buf = frag.as_buffer();
if(!hdr.Encode(&buf, llarp::InitBuffer(nullptr, 0)))
return false;
return mbedtls_ssl_write(ctx, buf.base, buf.sz) == int(buf.sz);
return write_pkt(buf.base, buf.sz) == int(buf.sz);
}
};
@ -312,11 +309,6 @@ namespace llarp
~LinkLayer();
llarp::Crypto *const crypto;
static const mbedtls_ecp_group_id AllowedCurve[2];
static const int AllowedHash[2];
static const int CipherSuite[2];
static const mbedtls_x509_crt_profile X509Profile;
bool
Start(llarp::Logic *l) override;
@ -336,8 +328,6 @@ namespace llarp
uint16_t
Rank() const override;
mbedtls_ssl_key_cert ourKeys;
const byte_t *
IndentityKey() const
{
@ -350,6 +340,12 @@ namespace llarp
return m_CookieSec;
}
RouterID
GetRouterID() const
{
return m_IdentityKey.toPublic();
}
private:
bool
SignBuffer(llarp::Signature &sig, llarp_buffer_t buf) const
@ -363,7 +359,7 @@ namespace llarp
void
RecvFrom(const llarp::Addr &from, const void *buf, size_t sz) override;
};
} // namespace dtls
} // namespace iwp
} // namespace llarp
#endif

@ -155,6 +155,16 @@ namespace llarp
const SecretKey&
TransportSecretKey() const;
bool
IsCompatable(const llarp::RouterContact& other) const
{
const std::string us = Name();
for(const auto& ai : other.addrs)
if(ai.dialect == us)
return true;
return false;
}
bool
EnsureKeys(const char* fpath);

@ -42,7 +42,7 @@ namespace llarp
std::function< bool(llarp_time_t) > TimedOut;
/// get remote public identity key
std::function< const PubKey &(void) > GetPubKey;
std::function< PubKey(void) > GetPubKey;
/// get remote address
std::function< Addr(void) > GetRemoteEndpoint;

@ -11,7 +11,7 @@ namespace llarp
namespace utp
{
std::unique_ptr< ILinkLayer >
NewServer(llarp::Crypto* crypto, const byte_t* routerEncSecret,
NewServer(llarp::Crypto* crypto, const SecretKey& routerEncSecret,
llarp::GetRCFunc getrc, llarp::LinkMessageHandler h,
llarp::SessionEstablishedHandler est,
llarp::SessionRenegotiateHandler reneg,

@ -1,8 +1,8 @@
#include <buffer.hpp>
#include <encode.hpp>
#include <iwp.hpp>
#include <link/server.hpp>
#include <link/utp.hpp>
#include <link/iwp.hpp>
#include <link_message.hpp>
#include <logger.hpp>
#include <net.hpp>
@ -98,28 +98,6 @@ on_try_connecting(void *u)
j->Attempt();
}
bool
llarp_router_try_connect(llarp::Router *router,
const llarp::RouterContact &remote,
uint16_t numretries)
{
// do we already have a pending job for this remote?
if(router->HasPendingConnectJob(remote.pubkey))
{
llarp::LogDebug("We have pending connect jobs to ", remote.pubkey);
return false;
}
auto link = router->outboundLink.get();
auto itr = router->pendingEstablishJobs.emplace(
remote.pubkey,
std::make_unique< TryConnectJob >(remote, link, numretries, router));
TryConnectJob *job = itr.first->second.get();
// try establishing async
router->logic->queue_job({job, &on_try_connecting});
return true;
}
bool
llarp_findOrCreateIdentity(llarp::Crypto *crypto, const char *fpath,
llarp::SecretKey &secretkey)
@ -182,6 +160,32 @@ llarp_findOrCreateEncryption(llarp::Crypto *crypto, const char *fpath,
namespace llarp
{
bool
Router::TryConnectAsync(llarp::RouterContact remote, uint16_t numretries)
{
// do we already have a pending job for this remote?
if(HasPendingConnectJob(remote.pubkey))
{
llarp::LogDebug("We have pending connect jobs to ", remote.pubkey);
return false;
}
for(auto &link : outboundLinks)
{
if(!link->IsCompatable(remote))
continue;
auto itr = pendingEstablishJobs.emplace(
remote.pubkey,
std::make_unique< TryConnectJob >(remote, link.get(), numretries,
this));
TryConnectJob *job = itr.first->second.get();
// try establishing async
logic->queue_job({job, &on_try_connecting});
return true;
}
return false;
}
void
Router::OnSessionEstablished(llarp::RouterContact rc)
{
@ -270,12 +274,14 @@ namespace llarp
return true;
}
}
if(outboundLink && outboundLink->HasSessionTo(remote))
for(const auto &link : outboundLinks)
{
SendTo(remote, msg, outboundLink.get());
return true;
if(link->HasSessionTo(remote))
{
SendTo(remote, msg, link.get());
return true;
}
}
// no link available
// this will create an entry in the obmq if it's not already there
@ -308,7 +314,7 @@ namespace llarp
if(nodedb->Get(remote, remoteRC))
{
// try connecting directly as the rc is loaded from disk
llarp_router_try_connect(this, remoteRC, 10);
TryConnectAsync(remoteRC, 10);
return true;
}
@ -334,7 +340,7 @@ namespace llarp
if(results[0].Verify(&crypto, Now()))
{
nodedb->Insert(results[0]);
llarp_router_try_connect(this, results[0], 10);
TryConnectAsync(results[0], 10);
return;
}
}
@ -345,8 +351,11 @@ namespace llarp
Router::ForEachPeer(
std::function< void(const llarp::ILinkSession *, bool) > visit) const
{
outboundLink->ForEachSession(
[visit](const llarp::ILinkSession *peer) { visit(peer, true); });
for(const auto &link : outboundLinks)
{
link->ForEachSession(
[visit](const llarp::ILinkSession *peer) { visit(peer, true); });
}
for(const auto &link : inboundLinks)
{
link->ForEachSession(
@ -357,8 +366,10 @@ namespace llarp
void
Router::ForEachPeer(std::function< void(llarp::ILinkSession *) > visit)
{
outboundLink->ForEachSession(
[visit](llarp::ILinkSession *peer) { visit(peer); });
for(const auto &link : inboundLinks)
{
link->ForEachSession([visit](llarp::ILinkSession *peer) { visit(peer); });
}
for(const auto &link : inboundLinks)
{
link->ForEachSession([visit](llarp::ILinkSession *peer) { visit(peer); });
@ -382,7 +393,7 @@ namespace llarp
{
llarp::LogWarn("failed to store");
}
if(!llarp_router_try_connect(this, remote, 10))
if(!TryConnectAsync(remote, 10))
{
// or error?
llarp::LogWarn("session already made");
@ -411,7 +422,7 @@ namespace llarp
void
Router::AddInboundLink(std::unique_ptr< llarp::ILinkLayer > &link)
{
inboundLinks.push_back(std::move(link));
inboundLinks.insert(std::move(link));
}
bool
@ -421,7 +432,7 @@ namespace llarp
iter.user = this;
iter.visit = llarp::router_iter_config;
llarp_config_iter(conf, &iter);
if(!InitOutboundLink())
if(!InitOutboundLinks())
return false;
if(!Ready())
{
@ -433,7 +444,7 @@ namespace llarp
bool
Router::Ready()
{
return outboundLink != nullptr;
return outboundLinks.size() > 0;
}
bool
@ -461,7 +472,7 @@ namespace llarp
llarp::LogInfo("closing router");
llarp_ev_loop_stop(netloop);
inboundLinks.clear();
outboundLink.reset(nullptr);
outboundLinks.clear();
}
void
@ -590,7 +601,7 @@ namespace llarp
if(nodedb->Get(remote, rc))
{
// try connecting async
llarp_router_try_connect(this, rc, 5);
TryConnectAsync(rc, 5);
}
else if(IsServiceNode() || !routerProfiling.IsBad(remote))
{
@ -635,7 +646,7 @@ namespace llarp
&& lokinetRouters.find(result.pubkey) == lokinetRouters.end())
continue;
nodedb->Insert(result);
llarp_router_try_connect(this, result, 10);
TryConnectAsync(result, 10);
}
}
@ -771,7 +782,7 @@ namespace llarp
{
for(const auto &rc : bootstrapRCList)
{
llarp_router_try_connect(this, rc, 4);
TryConnectAsync(rc, 4);
dht->impl.ExploreNetworkVia(dht::Key_t{rc.pubkey});
}
}
@ -821,19 +832,17 @@ namespace llarp
if(selected->SendTo(remote, buf))
return;
}
bool sent = outboundLink->SendTo(remote, buf);
if(!sent)
for(const auto &link : outboundLinks)
{
for(const auto &link : inboundLinks)
{
if(!sent)
{
sent = link->SendTo(remote, buf);
}
}
if(link->SendTo(remote, buf))
return;
}
if(!sent)
llarp::LogWarn("message to ", remote, " was dropped");
for(const auto &link : inboundLinks)
{
if(link->SendTo(remote, buf))
return;
}
llarp::LogWarn("message to ", remote, " was dropped");
}
void
@ -854,8 +863,11 @@ namespace llarp
llarp::ILinkLayer *
Router::GetLinkWithSessionByPubkey(const llarp::RouterID &pubkey)
{
if(outboundLink && outboundLink->HasSessionTo(pubkey))
return outboundLink.get();
for(const auto &link : outboundLinks)
{
if(link->HasSessionTo(pubkey))
return link.get();
}
for(const auto &link : inboundLinks)
{
if(link->HasSessionTo(pubkey))
@ -995,52 +1007,21 @@ namespace llarp
}
llarp::LogInfo("You have ", inboundLinks.size(), " inbound links");
llarp::AddressInfo ai;
for(const auto &link : inboundLinks)
{
llarp::AddressInfo addr;
if(!link->GetOurAddressInfo(addr))
continue;
llarp::Addr a(addr);
if(this->publicOverride && a.sameAddr(publicAddr))
{
llarp::LogInfo("Found adapter for public address");
}
if(!llarp::IsBogon(*a.addr6()))
{
llarp::LogInfo("Loading Addr: ", a, " into our RC");
_rc.addrs.push_back(addr);
}
};
if(this->publicOverride)
{
llarp::ILinkLayer *link = nullptr;
// llarp::LogWarn("Need to load our public IP into RC!");
if(inboundLinks.size() == 1)
if(link->GetOurAddressInfo(ai))
{
link = inboundLinks[0].get();
}
else
{
if(inboundLinks.size())
{
link = inboundLinks[0].get();
}
else
// override ip and port
if(this->publicOverride)
{
llarp::LogWarn(
"No need to set public ipv4 and port if no external interface "
"binds, turning off public override");
this->publicOverride = false;
link = nullptr;
ai.ip = *publicAddr.addr6();
ai.port = publicAddr.port();
}
}
if(link && link->GetOurAddressInfo(this->addrInfo))
{
// override ip and port
this->addrInfo.ip = *publicAddr.addr6();
this->addrInfo.port = publicAddr.port();
llarp::LogInfo("Loaded our public ", publicAddr, " override into RC!");
_rc.addrs.push_back(this->addrInfo);
if(llarp::IsBogon(ai.ip))
continue;
_rc.addrs.push_back(ai);
}
}
@ -1074,11 +1055,14 @@ namespace llarp
llarp::LogInfo("have ", nodedb->num_loaded(), " routers");
llarp::LogDebug("starting outbound link");
if(!outboundLink->Start(logic))
llarp::LogDebug("starting outbound links");
for(const auto &link : outboundLinks)
{
llarp::LogWarn("outbound link failed to start");
return false;
if(link->Start(logic))
{
llarp::LogWarn("outbound link failed to start");
return false;
}
}
int IBLinksStarted = 0;
@ -1159,8 +1143,9 @@ namespace llarp
Router::StopLinks()
{
llarp::LogInfo("stopping links");
outboundLink->Stop();
for(auto &link : inboundLinks)
for(const auto &link : outboundLinks)
link->Stop();
for(const auto &link : inboundLinks)
link->Stop();
}
@ -1204,7 +1189,7 @@ namespace llarp
&& !(self->HasSessionTo(other.pubkey)
|| self->HasPendingConnectJob(other.pubkey)))
{
llarp_router_try_connect(self, other, 5);
self->TryConnectAsync(other, 5);
--want;
}
return want > 0;
@ -1291,31 +1276,35 @@ namespace llarp
}
bool
Router::InitOutboundLink()
Router::InitOutboundLinks()
{
if(outboundLink)
if(outboundLinks.size() > 0)
return true;
auto link = llarp::utp::NewServerFromRouter(this);
static std::list<
std::function< std::unique_ptr< ILinkLayer >(llarp::Router *) > >
linkFactories = {llarp::utp::NewServerFromRouter,
llarp::iwp::NewServerFromRouter};
if(!link->EnsureKeys(transport_keyfile.string().c_str()))
for(const auto &factory : linkFactories)
{
llarp::LogError("failed to load ", transport_keyfile);
return false;
}
auto link = factory(this);
if(!link->EnsureKeys(transport_keyfile.string().c_str()))
{
llarp::LogError("failed to load ", transport_keyfile);
continue;
}
auto afs = {AF_INET, AF_INET6};
auto afs = {AF_INET, AF_INET6};
for(auto af : afs)
{
if(link->Configure(netloop, "*", af, 0))
for(auto af : afs)
{
outboundLink = std::move(link);
llarp::LogInfo("outbound link ready");
return true;
if(!link->Configure(netloop, "*", af, 0))
continue;
outboundLinks.insert(std::move(link));
}
}
return false;
return outboundLinks.size() > 0;
}
bool

@ -31,6 +31,7 @@
#include <map>
#include <vector>
#include <unordered_map>
#include <set>
bool
llarp_findOrCreateEncryption(llarp::Crypto *crypto, const char *fpath,
@ -44,6 +45,19 @@ struct TryConnectJob;
namespace llarp
{
template < typename T >
struct CompareLinks
{
bool
operator()(const std::unique_ptr< T > &left,
const std::unique_ptr< T > &right) const
{
const std::string leftName = left->Name();
const std::string rightName = right->Name();
return left->Rank() < right->Rank() || leftName < rightName;
}
};
struct Router
{
bool ready;
@ -150,8 +164,12 @@ namespace llarp
std::unique_ptr< llarp::rpc::Caller > rpcCaller;
std::string lokidRPCAddr = DefaultLokidRPCAddr;
std::unique_ptr< llarp::ILinkLayer > outboundLink;
std::vector< std::unique_ptr< llarp::ILinkLayer > > inboundLinks;
std::set< std::unique_ptr< llarp::ILinkLayer >,
CompareLinks< llarp::ILinkLayer > >
outboundLinks;
std::set< std::unique_ptr< llarp::ILinkLayer >,
CompareLinks< llarp::ILinkLayer > >
inboundLinks;
llarp::Profiling routerProfiling;
std::string routerProfilesFile = "profiles.dat";
@ -201,7 +219,7 @@ namespace llarp
AddInboundLink(std::unique_ptr< llarp::ILinkLayer > &link);
bool
InitOutboundLink();
InitOutboundLinks();
bool
GetRandomGoodRouter(RouterID &r);
@ -370,6 +388,9 @@ namespace llarp
size_t
NumberOfConnectedRouters() const;
bool
TryConnectAsync(llarp::RouterContact rc, uint16_t tries);
bool
GetRandomConnectedRouter(llarp::RouterContact &result) const;

@ -0,0 +1,338 @@
#include <gtest/gtest.h>
#include <link/dtls.hpp>
#include <messages/link_intro.hpp>
#include <messages/discard.hpp>
#include <ev.h>
struct DTLSTest : public ::testing::Test
{
static constexpr uint16_t AlicePort = 5000;
static constexpr uint16_t BobPort = 6000;
struct Context
{
Context(llarp::Crypto& c)
{
crypto = &c;
crypto->identity_keygen(signingKey);
crypto->encryption_keygen(encryptionKey);
rc.pubkey = llarp::seckey_topublic(signingKey);
rc.enckey = llarp::seckey_topublic(encryptionKey);
}
llarp::SecretKey signingKey;
llarp::SecretKey encryptionKey;
llarp::RouterContact rc;
llarp::Crypto* crypto;
bool gotLIM = false;
const llarp::RouterContact&
GetRC() const
{
return rc;
}
llarp::RouterID
GetRouterID() const
{
return rc.pubkey;
}
/// regenerate rc and rotate onion key
bool
Regen()
{
crypto->encryption_keygen(encryptionKey);
rc.enckey = llarp::seckey_topublic(encryptionKey);
return rc.Sign(crypto, signingKey);
}
std::unique_ptr< llarp::ILinkLayer > link;
static std::string
localLoopBack()
{
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
|| (__APPLE__ && __MACH__)
return "lo0";
#else
return "lo";
#endif
}
bool
Start(llarp::Logic* logic, llarp_ev_loop* loop, uint16_t port)
{
if(!link->Configure(loop, localLoopBack(), AF_INET, port))
return false;
if(!link->GenEphemeralKeys())
return false;
rc.addrs.emplace_back();
if(!link->GetOurAddressInfo(rc.addrs[0]))
return false;
if(!rc.Sign(crypto, signingKey))
return false;
return link->Start(logic);
}
void
Stop()
{
link->Stop();
}
void
TearDown()
{
Stop();
link.reset();
}
};
llarp::Crypto crypto;
Context Alice;
Context Bob;
bool success = false;
llarp_ev_loop* netLoop;
std::unique_ptr< llarp::Logic > logic;
llarp_time_t oldRCLifetime;
DTLSTest()
: crypto(llarp::Crypto::sodium{})
, Alice(crypto)
, Bob(crypto)
, netLoop(nullptr)
{
}
void
SetUp()
{
oldRCLifetime = llarp::RouterContact::Lifetime;
llarp::RouterContact::IgnoreBogons = true;
llarp::RouterContact::Lifetime = 500;
llarp_ev_loop_alloc(&netLoop);
logic.reset(new llarp::Logic());
}
void
TearDown()
{
Alice.TearDown();
Bob.TearDown();
logic.reset();
llarp_ev_loop_free(&netLoop);
llarp::RouterContact::IgnoreBogons = false;
llarp::RouterContact::Lifetime = oldRCLifetime;
}
static void
OnTimeout(void* u, uint64_t, uint64_t left)
{
if(left)
return;
static_cast< DTLSTest* >(u)->Stop();
}
void
RunMainloop()
{
logic->call_later({5000, this, &OnTimeout});
llarp_ev_loop_run_single_process(netLoop, logic->thread, logic.get());
}
void
Stop()
{
llarp_ev_loop_stop(netLoop);
}
bool AliceGotMessage(llarp_buffer_t)
{
success = true;
Stop();
return true;
}
};
TEST_F(DTLSTest, TestAliceRenegWithBob)
{
Alice.link = llarp::dtls::NewServer(
&crypto, Alice.encryptionKey, Alice.signingKey,
[&]() -> const llarp::RouterContact& { return Alice.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
if(Alice.gotLIM)
{
Alice.Regen();
return s->RenegotiateSession();
}
else
{
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Alice.gotLIM = true;
return true;
}
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Bob.GetRC());
llarp::LogInfo("alice established with bob");
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Alice.signingKey, buf);
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
Stop();
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Bob.GetRouterID()); });
auto sendDiscardMessage = [](llarp::ILinkSession* s) -> bool {
// send discard message in reply to complete unit test
byte_t tmp[32] = {0};
auto otherBuf = llarp::StackBuffer< decltype(tmp) >(tmp);
llarp::DiscardMessage discard;
if(!discard.BEncode(&otherBuf))
return false;
otherBuf.sz = otherBuf.cur - otherBuf.base;
otherBuf.cur = otherBuf.base;
return s->SendMessageBuffer(otherBuf);
};
Bob.link = llarp::dtls::NewServer(
&crypto, Bob.encryptionKey, Bob.signingKey,
[&]() -> const llarp::RouterContact& { return Bob.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Bob.gotLIM = true;
return sendDiscardMessage(s);
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Alice.GetRC());
llarp::LogInfo("bob established with alice");
Bob.link->VisitSessionByPubkey(Alice.GetRC().pubkey.as_array(),
sendDiscardMessage);
},
[&](llarp::RouterContact newrc, llarp::RouterContact oldrc) -> bool {
success = newrc.pubkey == oldrc.pubkey;
return true;
},
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Bob.signingKey, buf);
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Alice.GetRouterID()); });
ASSERT_TRUE(Alice.Start(logic.get(), netLoop, AlicePort));
ASSERT_TRUE(Bob.Start(logic.get(), netLoop, BobPort));
ASSERT_TRUE(Alice.link->TryEstablishTo(Bob.GetRC()));
RunMainloop();
ASSERT_TRUE(Alice.gotLIM);
ASSERT_TRUE(Bob.gotLIM);
ASSERT_TRUE(success);
}
TEST_F(DTLSTest, TestAliceConnectToBob)
{
Alice.link = llarp::dtls::NewServer(
&crypto, Alice.encryptionKey, Alice.signingKey,
[&]() -> const llarp::RouterContact& { return Alice.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
if(Alice.gotLIM)
{
return AliceGotMessage(buf);
}
else
{
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Alice.gotLIM = true;
return true;
}
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Bob.GetRC());
llarp::LogInfo("alice established with bob");
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Alice.signingKey, buf);
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
Stop();
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Bob.GetRouterID()); });
auto sendDiscardMessage = [](llarp::ILinkSession* s) -> bool {
// send discard message in reply to complete unit test
byte_t tmp[32] = {0};
auto otherBuf = llarp::StackBuffer< decltype(tmp) >(tmp);
llarp::DiscardMessage discard;
if(!discard.BEncode(&otherBuf))
return false;
otherBuf.sz = otherBuf.cur - otherBuf.base;
otherBuf.cur = otherBuf.base;
return s->SendMessageBuffer(otherBuf);
};
Bob.link = llarp::dtls::NewServer(
&crypto, Bob.encryptionKey, Bob.signingKey,
[&]() -> const llarp::RouterContact& { return Bob.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Bob.gotLIM = true;
return true;
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Alice.GetRC());
llarp::LogInfo("bob established with alice");
Bob.link->VisitSessionByPubkey(Alice.GetRC().pubkey.as_array(),
sendDiscardMessage);
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Bob.signingKey, buf);
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Alice.GetRouterID()); });
ASSERT_TRUE(Alice.Start(logic.get(), netLoop, AlicePort));
ASSERT_TRUE(Bob.Start(logic.get(), netLoop, BobPort));
ASSERT_TRUE(Alice.link->TryEstablishTo(Bob.GetRC()));
RunMainloop();
ASSERT_TRUE(Alice.gotLIM);
ASSERT_TRUE(Bob.gotLIM);
ASSERT_TRUE(success);
}

@ -0,0 +1,426 @@
#include <gtest/gtest.h>
#include <link/utp.hpp>
#include <link/iwp.hpp>
#include <messages/link_intro.hpp>
#include <messages/discard.hpp>
#include <ev.h>
struct LinkLayerTest : public ::testing::Test
{
static constexpr uint16_t AlicePort = 5000;
static constexpr uint16_t BobPort = 6000;
struct Context
{
Context(llarp::Crypto& c)
{
crypto = &c;
crypto->identity_keygen(signingKey);
crypto->encryption_keygen(encryptionKey);
rc.pubkey = llarp::seckey_topublic(signingKey);
rc.enckey = llarp::seckey_topublic(encryptionKey);
}
llarp::SecretKey signingKey;
llarp::SecretKey encryptionKey;
llarp::RouterContact rc;
llarp::Crypto* crypto;
bool gotLIM = false;
const llarp::RouterContact&
GetRC() const
{
return rc;
}
llarp::RouterID
GetRouterID() const
{
return rc.pubkey;
}
/// regenerate rc and rotate onion key
bool
Regen()
{
crypto->encryption_keygen(encryptionKey);
rc.enckey = llarp::seckey_topublic(encryptionKey);
return rc.Sign(crypto, signingKey);
}
std::unique_ptr< llarp::ILinkLayer > link;
static std::string
localLoopBack()
{
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
|| (__APPLE__ && __MACH__)
return "lo0";
#else
return "lo";
#endif
}
bool
Start(llarp::Logic* logic, llarp_ev_loop* loop, uint16_t port)
{
if(!link)
return false;
if(!link->Configure(loop, localLoopBack(), AF_INET, port))
return false;
if(!link->GenEphemeralKeys())
return false;
rc.addrs.emplace_back();
if(!link->GetOurAddressInfo(rc.addrs[0]))
return false;
if(!rc.Sign(crypto, signingKey))
return false;
return link->Start(logic);
}
void
Stop()
{
if(link)
link->Stop();
}
void
TearDown()
{
Stop();
link.reset();
}
};
llarp::Crypto crypto;
Context Alice;
Context Bob;
bool success = false;
llarp_ev_loop* netLoop;
std::unique_ptr< llarp::Logic > logic;
llarp_time_t oldRCLifetime;
LinkLayerTest()
: crypto(llarp::Crypto::sodium{})
, Alice(crypto)
, Bob(crypto)
, netLoop(nullptr)
{
}
void
SetUp()
{
oldRCLifetime = llarp::RouterContact::Lifetime;
llarp::RouterContact::IgnoreBogons = true;
llarp::RouterContact::Lifetime = 500;
llarp_ev_loop_alloc(&netLoop);
logic.reset(new llarp::Logic());
}
void
TearDown()
{
Alice.TearDown();
Bob.TearDown();
logic.reset();
llarp_ev_loop_free(&netLoop);
llarp::RouterContact::IgnoreBogons = false;
llarp::RouterContact::Lifetime = oldRCLifetime;
}
static void
OnTimeout(void* u, uint64_t, uint64_t left)
{
if(left)
return;
static_cast< LinkLayerTest* >(u)->Stop();
}
void
RunMainloop()
{
logic->call_later({5000, this, &OnTimeout});
llarp_ev_loop_run_single_process(netLoop, logic->thread, logic.get());
}
void
Stop()
{
llarp_ev_loop_stop(netLoop);
}
bool AliceGotMessage(llarp_buffer_t)
{
success = true;
Stop();
return true;
}
};
TEST_F(LinkLayerTest, TestUTPAliceRenegWithBob)
{
Alice.link = llarp::utp::NewServer(
&crypto, Alice.encryptionKey,
[&]() -> const llarp::RouterContact& { return Alice.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
if(Alice.gotLIM)
{
Alice.Regen();
return s->RenegotiateSession();
}
else
{
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Alice.gotLIM = true;
return true;
}
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Bob.GetRC());
llarp::LogInfo("alice established with bob");
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Alice.signingKey, buf);
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
Stop();
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Bob.GetRouterID()); });
auto sendDiscardMessage = [](llarp::ILinkSession* s) -> bool {
// send discard message in reply to complete unit test
byte_t tmp[32] = {0};
auto otherBuf = llarp::StackBuffer< decltype(tmp) >(tmp);
llarp::DiscardMessage discard;
if(!discard.BEncode(&otherBuf))
return false;
otherBuf.sz = otherBuf.cur - otherBuf.base;
otherBuf.cur = otherBuf.base;
return s->SendMessageBuffer(otherBuf);
};
Bob.link = llarp::utp::NewServer(
&crypto, Bob.encryptionKey,
[&]() -> const llarp::RouterContact& { return Bob.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Bob.gotLIM = true;
return sendDiscardMessage(s);
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Alice.GetRC());
llarp::LogInfo("bob established with alice");
Bob.link->VisitSessionByPubkey(Alice.GetRC().pubkey.as_array(),
sendDiscardMessage);
},
[&](llarp::RouterContact newrc, llarp::RouterContact oldrc) -> bool {
success = newrc.pubkey == oldrc.pubkey;
return true;
},
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Bob.signingKey, buf);
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Alice.GetRouterID()); });
ASSERT_TRUE(Alice.Start(logic.get(), netLoop, AlicePort));
ASSERT_TRUE(Bob.Start(logic.get(), netLoop, BobPort));
ASSERT_TRUE(Alice.link->TryEstablishTo(Bob.GetRC()));
RunMainloop();
ASSERT_TRUE(Alice.gotLIM);
ASSERT_TRUE(Bob.gotLIM);
ASSERT_TRUE(success);
}
TEST_F(LinkLayerTest, TestUTPAliceConnectToBob)
{
Alice.link = llarp::utp::NewServer(
&crypto, Alice.encryptionKey,
[&]() -> const llarp::RouterContact& { return Alice.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
if(Alice.gotLIM)
{
return AliceGotMessage(buf);
}
else
{
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Alice.gotLIM = true;
return true;
}
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Bob.GetRC());
llarp::LogInfo("alice established with bob");
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Alice.signingKey, buf);
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
Stop();
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Bob.GetRouterID()); });
auto sendDiscardMessage = [](llarp::ILinkSession* s) -> bool {
// send discard message in reply to complete unit test
byte_t tmp[32] = {0};
auto otherBuf = llarp::StackBuffer< decltype(tmp) >(tmp);
llarp::DiscardMessage discard;
if(!discard.BEncode(&otherBuf))
return false;
otherBuf.sz = otherBuf.cur - otherBuf.base;
otherBuf.cur = otherBuf.base;
return s->SendMessageBuffer(otherBuf);
};
Bob.link = llarp::utp::NewServer(
&crypto, Bob.encryptionKey,
[&]() -> const llarp::RouterContact& { return Bob.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Bob.gotLIM = true;
return true;
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Alice.GetRC());
llarp::LogInfo("bob established with alice");
Bob.link->VisitSessionByPubkey(Alice.GetRC().pubkey.as_array(),
sendDiscardMessage);
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Bob.signingKey, buf);
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Alice.GetRouterID()); });
ASSERT_TRUE(Alice.Start(logic.get(), netLoop, AlicePort));
ASSERT_TRUE(Bob.Start(logic.get(), netLoop, BobPort));
ASSERT_TRUE(Alice.link->TryEstablishTo(Bob.GetRC()));
RunMainloop();
ASSERT_TRUE(Alice.gotLIM);
ASSERT_TRUE(Bob.gotLIM);
ASSERT_TRUE(success);
}
TEST_F(LinkLayerTest, TestIWPAliceConnectToBob)
{
Alice.link = llarp::iwp::NewServer(
&crypto, Alice.encryptionKey,
[&]() -> const llarp::RouterContact& { return Alice.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
if(Alice.gotLIM)
{
return AliceGotMessage(buf);
}
else
{
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Alice.gotLIM = true;
return true;
}
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Bob.GetRC());
llarp::LogInfo("alice established with bob");
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Alice.signingKey, buf);
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
Stop();
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Bob.GetRouterID()); });
auto sendDiscardMessage = [](llarp::ILinkSession* s) -> bool {
// send discard message in reply to complete unit test
byte_t tmp[32] = {0};
auto otherBuf = llarp::StackBuffer< decltype(tmp) >(tmp);
llarp::DiscardMessage discard;
if(!discard.BEncode(&otherBuf))
return false;
otherBuf.sz = otherBuf.cur - otherBuf.base;
otherBuf.cur = otherBuf.base;
return s->SendMessageBuffer(otherBuf);
};
Bob.link = llarp::iwp::NewServer(
&crypto, Bob.encryptionKey,
[&]() -> const llarp::RouterContact& { return Bob.GetRC(); },
[&](llarp::ILinkSession* s, llarp_buffer_t buf) -> bool {
llarp::LinkIntroMessage msg;
if(!msg.BDecode(&buf))
return false;
if(!s->GotLIM(&msg))
return false;
Bob.gotLIM = true;
return true;
},
[&](llarp::RouterContact rc) {
ASSERT_EQ(rc, Alice.GetRC());
llarp::LogInfo("bob established with alice");
Bob.link->VisitSessionByPubkey(Alice.GetRC().pubkey.as_array(),
sendDiscardMessage);
},
[&](llarp::RouterContact, llarp::RouterContact) -> bool { return true; },
[&](llarp::Signature& sig, llarp_buffer_t buf) -> bool {
return crypto.sign(sig, Bob.signingKey, buf);
},
[&](llarp::ILinkSession* session) {
ASSERT_FALSE(session->IsEstablished());
},
[&](llarp::RouterID router) { ASSERT_EQ(router, Alice.GetRouterID()); });
ASSERT_TRUE(Alice.Start(logic.get(), netLoop, AlicePort));
ASSERT_TRUE(Bob.Start(logic.get(), netLoop, BobPort));
ASSERT_TRUE(Alice.link->TryEstablishTo(Bob.GetRC()));
RunMainloop();
ASSERT_TRUE(Alice.gotLIM);
ASSERT_TRUE(Bob.gotLIM);
ASSERT_TRUE(success);
}

@ -0,0 +1,9 @@
#include <gtest/gtest.h>
struct DTLSTest : public ::testing::Test
{
};
TEST_F(DTLSTest, TestAliceConnectToBob)
{
}

@ -52,7 +52,7 @@ struct UTPTest : public ::testing::Test
return rc.Sign(crypto, signingKey);
}
std::unique_ptr< Link_t > link;
std::unique_ptr< ILinkLayer > link;
static std::string
localLoopBack()

Loading…
Cancel
Save