Ryan Tharp 6 years ago
commit f283923cb7

@ -11,9 +11,9 @@ build:linux:
- linux
stage: build
before_script:
- apk add --update g++ make cmake automake libtool autoconf linux-headers libsodium
- apk add --update g++ make cmake linux-headers libsodium-dev ninja
script:
- make clean test
- make
artifacts:
paths:
- "lokinet"

@ -49,10 +49,6 @@ else()
set(THREAD_LIB pthread)
endif()
if (NOT MSVC)
add_cflags("-march=native")
add_cxxflags("-march=native")
endif(NOT MSVC)
if(STATIC_LINK)
add_cflags("-static -Wl,--whole-archive -lpthread -Wl,--no-whole-archive")
@ -358,6 +354,7 @@ set(LIB_SRC
llarp/path.cpp
llarp/pathbuilder.cpp
llarp/pathset.cpp
llarp/profiling.cpp
llarp/proofofwork.cpp
llarp/relay_commit.cpp
llarp/relay_up_down.cpp

@ -5,6 +5,8 @@ SIGN = gpg --sign --detach
REPO := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
PREFIX ?= /usr/local
CC ?= cc
CXX ?= c++
@ -97,7 +99,7 @@ shared: shared-configure
testnet:
cp $(EXE) $(TESTNET_EXE)
mkdir -p $(TESTNET_ROOT)
python3 contrib/testnet/genconf.py --bin=$(TESTNET_EXE) --svc=$(TESTNET_SERVERS) --clients=$(TESTNET_CLIENTS) --dir=$(TESTNET_ROOT) --out $(TESTNET_CONF)
python3 contrib/testnet/genconf.py --bin=$(TESTNET_EXE) --svc=$(TESTNET_SERVERS) --clients=$(TESTNET_CLIENTS) --dir=$(TESTNET_ROOT) --out $(TESTNET_CONF) --connect=3
LLARP_DEBUG=$(TESTNET_DEBUG) supervisord -n -d $(TESTNET_ROOT) -l $(TESTNET_LOG) -c $(TESTNET_CONF)
test: debug
@ -105,3 +107,9 @@ test: debug
format:
clang-format -i $$(find daemon llarp include | grep -E '\.[h,c](pp)?$$')
install:
rm -f $(PREFIX)/bin/lokinet
cp $(EXE) $(PREFIX)/bin/lokinet
chmod 755 $(PREFIX)/bin/lokinet
setcap cap_net_admin=+eip $(PREFIX)/bin/lokinet

@ -36,6 +36,9 @@ main(int argc, char *argv[])
if(ctx)
{
signal(SIGINT, handle_signal);
#ifndef _WIN32
signal(SIGHUP, handle_signal);
#endif
code = llarp_main_run(ctx);
llarp_main_free(ctx);
}

@ -1,225 +0,0 @@
invisible wire protocol version 0:
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].
cryptography:
see crypto_v0.txt
wire decryption:
the first 32 bytes are message authentication bytes, h
the next 32 bytes are nouce for cipher, n
the remaining bytes are interpreted as ciphertext, x
a shared secret S is generated in the session start message
next the integrity of the ciphertext is done by checking MDS(n + x, S) == h
if the ciphertext is valid then the frame is decrypted via SD(S, x, n)
wire encryption:
given variadic sized payload p, 32 byte nounce n and public encryption keys A
and B
x = SE(S, p, n[0:24])
h = MDS(n + x, S)
the resulting data is:
h + n + x
handshake:
0) intro
32 bytes hmac, h
32 bytes nounce, n
32 bytes encrypted alice's transport public encryption key e
variadic bytes padding, w0
Alice transmits ( h + n + e + w0 ) to Bob from the transport address matching
his public transport encryption key (b.k).
w0 = "[insert variable length random padding here]"
n = RAND(32)
e = SE(a.k, HS(b.k + n), n[0:24])
S = TKE(a.k, b.k, n)
h = MDS(n + e + w0, S)
Bob recieves ( s + n + e + w0 )
1) intro ack
sent in reply to an intro, bob sends an intro ack encrypted to Alice using
32 bytes hmac, h
32 bytes nounce, n
32 bytes ciphertext, x
variadic bytes padding, w1
w1 = "[insert variable length random padding here]"
token = RAND(32)
S = TKE(a.k, b.k, n)
x = SE(k, token, n[0:24])
h = MDS(n + x + w1, S)
Bob transmits ( s + n + x + w1 ), r is ignored and discarded
Alice recieves ( s + n + x + w1 ) and verifies the signature silently
dropping if it does not match.
2) session start
Alice uses the token from the previous message to start the wire session
32 bytes hmac, h
32 bytes nounce, n
32 bytes ciphertext, x
variadic byttes padding, w2
w2 = "[insert variable length random padding here]"
e_K = TKE(a.k, b.k, n)
x = SE(e_K, token, n[0:24])
h = MDS(n + x + w2, e_K)
T = HS(token + n)
K = TKE(a.k, b.k, T)
Alice transmits ( h + n + x + w2 )
Bob recieves ( h + n + x + w2) and verifies that h == MDS(n + x, k) silently
drops if not matching
the session is now established with session key K,
Bob replies by transmitting a LIM
IWP payload format:
ciphertext:
32 bytes hmac, h
32 bytes nounce, n
N bytes of ciphertext, x
plaintext header, H
8 bits protocol version, v (currently 0)
8 bits message type, t
16 bits payload size, s
8 bits reserved, r (currently 0)
8 bits flags, f
plaintext payload: P
s bytes of data
N bytes remaining data is discarded
Encryption:
D = H + P
x = SE(D, S, n)
h = MDS(n + x, S)
Alice transmits h + n + x
Bob recieves recieve h + n + x
Bob checks hmac by verifying h == MDS(n + x, S)
if the hmac fails the data is silently dropped
Decryption:
verify h == MDS(n + x, S)
D = SD(x, S, n)
H = D[0:6]
P = D[6:6+H.s]
message types:
ALIV = 0x00
keepalive message
XMIT = 0x01
begin link layer message transmission
ACKS = 0x02
acknolege link layer message fragment
FRAG = 0x03
transmit link layer message fragment
flags:
SESSION_INVALIDATED = 1 << 0
this session is now invalidated and a new session is required
HIGH_PACKET_DROP = 1 << 1
high packet drop detected
HIGH_MTU_DETECTED = 1 << 2
the network uses an mtu greater than 1488 bytes
PROTOCOL_UPGRADE = 1 << 3
indicates we want to do protocol upgrade (future use)
XMIT payload:
start transmiting a link layer message
msg_bytes = BE(msg)
32 bytes hash of message computed by HS(msg_bytes)
64 bits unsigned int message id
16 bits unsigned int fragment size bytes, S
16 bits size of last fragment in bytes, L
16 bits reserved for future, currently zero
8 bits unsigned int nonzero number of fragments, n
8 bits reserved flags, f
if f LSB is set then last fragment is included and is l bytes long
f's LSB MUST be set as of protocol version 0.
msg_bytes is S * (n - 1) + L bytes long
FRAG payload:
transmit a link layer message fragment
64 bits message id
8 bits unsigned int fragment number
S bytes of payload fragment data
remaining bytes discarded
ACKS payload:
indicates we which chunks we have recieved
64 bits message id
32 bits bitmask of chunks we have received
remaining bytes discarded
control flow:
To transmit link message over an established session the transmitter sends an
XMIT frame.
In reply to an XMIT frame the recipiant MUST send an ACKS frame with an emtpy
bitmask.
After the transmitter recieves the first ACKS frame it is allowed to start
sending FRAG messages.
When all fragmenets are obtained by the recipiant, the recipiant sends an ACKS
frame with a full bitfield (0xFFFF), to indicate the link message was recieved.
In the event of packet drop the sender decides when to retransmit FRAG frames
with expontential backoff.
In the event of packet loss greater than 50% over 10 second the session is
invalidated and must be renegotiated with a new handshake.

@ -266,14 +266,18 @@ identifies the sender as having the RC contained in r. The recipiant MUST
validate the RC's signature and ensure that the public key in use is listed in
the RC.a matching the ipv6 address it originated from.
if r is not present in sessions made by clients.
{
a: "i",
n: "<32 bytes nonce for key exhcange>",
p: uint64_milliseconds_session_period,
r: RC,
v: 0
v: 0,
z: "<64 bytes signature of entire message by r.k>"
}
the link session will be kept open for p milliseconds after which
the session MUST be renegotiated.
link relay commit message (LRCM)
request a commit to relay traffic to another node.
@ -452,7 +456,8 @@ always the first message sent
path latency message (PLM)
a latency measurement message, reply with a PLM response if we are the far end of a path.
a latency measurement message, reply with a PLM response if we are the far end
of a path.
variant 1, request, generated by the path creator:
@ -467,7 +472,7 @@ variant 2, response, generated by the endpoint that recieved the request.
{
A: "L",
S: uint64_sequence_number,
T: uint64_timestamp_sent,
T: uint64_timestamp_recieved,
V: 0
}
@ -530,6 +535,18 @@ B is set to a backoff value.
R contains additional metadata text describing why the exit was rejected.
discarded data fragment message (DDFM)
sent in reply to TDFM when we don't have a path locally or are doing network
congestion control. indcates a TDFM was discarded.
{
A: "D",
P: "<16 bytes path id>",
S: uint64_sequence_number_of_fragment_dropped,
V: 0
}
transfer data fragment message (TDFM)
transfer data between paths.
@ -538,13 +555,14 @@ transfer data between paths.
A: "T",
P: "<16 bytes path id>",
S: uint64_sequence_number,
T: message_transfered_between_paths,
T: hidden_service_frame,
V: 0
}
transfer data to another path with id P on the local router place a random 32 byte and T values
into y and z values into a LRDM message (respectively) and send it in the
downstream direction.
transfer data to another path with id P on the local router place a random 32
byte and T values into y and z values into a LRDM message (respectively) and
send it in the downstream direction. if this path does not exist on the router
it is replied to with a DDFM.
@ -622,7 +640,7 @@ M = {
D: D,
N: N,
V: 0,
Z: "\x00" * 32
Z: "\x00" * 64
},
V: 0
}
@ -680,12 +698,12 @@ transfer ip traffic for exit
A: "E",
S: uint64_sequence_number,
V: 0,
X: "<N bytes ipv6 packet>",
X: "<N bytes ip packet>",
Y: "<16 bytes nounce>",
Z: "<64 bytes signature using previously provided signing key>"
}
X is parsed as an IPv6 packet and the source addresss is extracted.
X is parsed as an IP packet and the source addresss is extracted.
Next we find the corrisponding signing key for a previously granted exit address
and use it to validate the siganture of the entire message. If the signing key
cannot be found or the signature is invalid this message is dropped, otherwise
@ -734,4 +752,4 @@ wrapper message for sending many dht messages down a path.
M: [many, dht, messages, here],
S: uint64_sequence_number,
V: 0
}
}

@ -0,0 +1,64 @@
Wire Protocol (version 0)
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]
1088 byte fragments are sent over UTP in an ordered fashion.
The each fragment has the following structure:
[ 32 bytes blake2 keyed hash of the following 1056 bytes ]
[ 24 bytes random nonce ]
[ 1032 bytes encrypted payload ]
the decrypted payload has the following structure:
[ big endian unsigned 32 bit flags (F) ]
[ big endian unsigned 32 bit fragment length (N) ]
[ N bytes of plaintext payload ]
if F is non zero then more fragments for the current message being transmitted
are expected. If F is zero then this fragment is the last in the sequence.
On each fragment append the N bytes of payload to an internal buffer.
This internal buffer MUST NOT exceed 8192 bytes, the maximum size of an inter
node message.
When the last fragment in the sequence is reached the internal buffer is
processed as a link layer message (see proto_v0.txt)
Handshake phase:
Before data flows a protocol handshake must happen.
The first message sent is a LIM (L) (see proto_v0.txt) by the connection initiator, Alice.
The receiving end MUST verify the signatures of the LIM and RC.
If any verification fails at any phase the underlying UTP session MUST be reset.
Each side re-computes the session key.
the session key kdf for K is:
t_h = HS(K + L.n)
K = TKE(A.p, B_a.e, sk, t_h)
the initial value of K is HS(B.k)
Periodically the connection initiator MUST renegotiate the session key by
sending a LIM after L.p milliseconds have elapsed.
If either party's RC changes while a connection is established they MUST
renegotioate the session keys to ensure the new RC is sent.
references:
[1] http://www.bittorrent.org/beps/bep_0029.html

@ -24,8 +24,7 @@ namespace llarp
llarp_config *config = nullptr;
llarp_nodedb *nodedb = nullptr;
llarp_ev_loop *mainloop = nullptr;
char nodedb_dir[256] = {0};
char conatctFile[256] = "router.signed";
std::string nodedb_dir;
bool
LoadConfig(const std::string &fname);

@ -224,11 +224,22 @@ namespace llarp
{
virtual ~IBEncodeMessage(){};
IBEncodeMessage(uint64_t v = LLARP_PROTO_VERSION)
{
version = v;
}
virtual bool
DecodeKey(llarp_buffer_t key, llarp_buffer_t* val) = 0;
DecodeKey(llarp_buffer_t key, llarp_buffer_t* val)
{
return false;
}
virtual bool
BEncode(llarp_buffer_t* buf) const = 0;
BEncode(llarp_buffer_t* buf) const
{
return false;
}
virtual bool
BDecode(llarp_buffer_t* buf)
@ -240,7 +251,7 @@ namespace llarp
}
// TODO: check for shadowed values elsewhere
uint64_t version = LLARP_PROTO_VERSION;
uint64_t version = 0;
static bool
OnKey(dict_reader* r, llarp_buffer_t* k)

@ -18,6 +18,12 @@ namespace llarp
Bucket(const Key_t& us) : nodes(XorMetric(us)){};
size_t
Size() const
{
return nodes.size();
}
bool
GetRandomNodeExcluding(Key_t& result,
const std::set< Key_t >& exclude) const
@ -120,7 +126,7 @@ namespace llarp
void
PutNode(const Val_t& val)
{
nodes.insert(std::make_pair(val.ID, val));
nodes[val.ID] = val;
}
void

@ -69,11 +69,15 @@ namespace llarp
std::vector< V > valuesFound;
TXOwner whoasked;
virtual bool
Validate(const V& value) const = 0;
void
OnFound(const Key_t& askedPeer, const V& value)
{
peersAsked.insert(askedPeer);
valuesFound.push_back(value);
if(Validate(value))
valuesFound.push_back(value);
}
virtual void
@ -116,6 +120,9 @@ namespace llarp
Context();
~Context();
llarp_crypto*
Crypto();
/// on behalf of whoasked request introset for target from dht router with
/// key askpeer
void
@ -147,6 +154,12 @@ namespace llarp
return true;
}
bool
HasRouterLookup(const RouterID& target) const
{
return pendingRouterLookups.HasLookupFor(target);
}
/// on behalf of whoasked request introsets with tag from dht router with
/// key askpeer with Recursion depth R
void
@ -257,22 +270,34 @@ namespace llarp
return itr->second.get();
}
bool
HasLookupFor(const K& target) const
{
return timeouts.find(target) != timeouts.end();
}
bool
HasPendingLookupFrom(const TXOwner& owner) const
{
return GetPendingLookupFrom(owner) != nullptr;
}
TX< K, V >*
NewTX(const TXOwner& owner, const K& k, TX< K, V >* t)
void
NewTX(const TXOwner& owner, const K& k, TX< K, V >* t,
bool forceStart = true)
{
tx.emplace(owner, std::unique_ptr< TX< K, V > >(t));
auto n = waiting.count(k);
waiting.insert(std::make_pair(k, owner));
auto itr = timeouts.find(k);
if(itr == timeouts.end())
{
timeouts.insert(
std::make_pair(k, llarp_time_now_ms() + requestTimeoutMS));
return t;
}
if(forceStart || n == 0)
t->Start(owner);
}
/// mark tx as not fond

@ -25,6 +25,12 @@ namespace llarp
rc = other;
ID = other.pubkey.data();
}
bool
operator<(const RCNode& other) const
{
return rc.OtherIsNewer(other.rc);
}
};
struct ISNode
@ -42,7 +48,12 @@ namespace llarp
{
introset = other;
introset.A.CalculateAddress(ID);
llarp::LogInfo("make ISNode with topic ", introset.topic.ToString());
}
bool
operator<(const ISNode& other) const
{
return introset.OtherIsNewer(other.introset);
}
};
} // namespace dht

@ -27,6 +27,18 @@ namespace llarp
return bencode_write_bytestring(buf, _data, _sz);
}
bool
operator==(const Encrypted& other) const
{
return _sz == other._sz && memcmp(_data, other._data, _sz) == 0;
}
bool
operator!=(const Encrypted& other) const
{
return !(*this == other);
}
Encrypted&
operator=(const Encrypted& other)
{

@ -45,8 +45,8 @@ namespace llarp
/// overrides Endpoint
/// handle inbound traffic
void
HandleDataMessage(service::ProtocolMessage* msg);
bool
ProcessDataMessage(service::ProtocolMessage* msg);
#ifndef _MINGW32_NO_THREADS
/// overrides Endpount
@ -97,6 +97,10 @@ namespace llarp
void
MarkIPActive(uint32_t ip);
/// mark this address as active forever
void
MarkIPActiveForever(uint32_t ip);
void
FlushSend();

@ -51,9 +51,27 @@ typedef struct ip_hdr
#define check ip_sum
#define ihl ip_hl
#endif
#if defined(__linux__)
#define ip_version version
struct ip_header
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ihl:4;
unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
unsigned int version:4;
unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
uint8_t tos;
uint16_t tot_len;
uint16_t id;
uint16_t frag_off;
uint8_t ttl;
uint8_t protocol;
uint16_t check;
uint32_t saddr;
uint32_t daddr;
};
namespace llarp
{
@ -99,40 +117,40 @@ namespace llarp
}
};
iphdr*
ip_header*
Header()
{
return (iphdr*)&buf[0];
return (ip_header*)&buf[0];
}
const iphdr*
const ip_header*
Header() const
{
return (iphdr*)&buf[0];
return (ip_header*)&buf[0];
}
uint32_t
src()
{
return Header()->saddr;
return ntohl(Header()->saddr);
}
uint32_t
dst()
{
return Header()->daddr;
return ntohl(Header()->daddr);
}
void
src(uint32_t ip)
{
Header()->saddr = htons(ip);
Header()->saddr = htonl(ip);
}
void
dst(uint32_t ip)
{
Header()->daddr = htons(ip);
Header()->daddr = htonl(ip);
}
// update ip packet checksum

@ -77,9 +77,6 @@ namespace llarp
bool
GetOurAddressInfo(AddressInfo& addr) const;
void
RemoveSessionVia(const Addr& addr);
virtual uint16_t
Rank() const = 0;
@ -122,19 +119,22 @@ namespace llarp
uint32_t tick_id;
protected:
typedef util::NullLock Lock;
typedef util::NullMutex Mutex;
void
PutSession(const Addr& addr, ILinkSession* s);
PutSession(ILinkSession* s);
llarp_logic* m_Logic = nullptr;
Addr m_ourAddr;
llarp_udp_io m_udp;
SecretKey m_SecretKey;
util::Mutex m_AuthedLinksMutex;
Mutex m_AuthedLinksMutex;
std::unordered_multimap< PubKey, std::unique_ptr< ILinkSession >,
PubKey::Hash >
m_AuthedLinks;
util::Mutex m_PendingMutex;
Mutex m_PendingMutex;
std::list< std::unique_ptr< ILinkSession > > m_Pending;
};
} // namespace llarp

@ -43,6 +43,12 @@ namespace llarp
std::function< const PubKey &(void) > GetPubKey;
/// get remote address
std::function< const Addr &(void) > GetRemoteEndpoint;
/// handle a valid LIM
std::function< bool(const LinkIntroMessage *msg) > GotLIM;
/// send queue current blacklog
std::function< size_t(void) > SendQueueBacklog;
};
} // namespace llarp

@ -2,5 +2,6 @@
#define LLARP_LINK_LAYER_HPP
#include <llarp/link/server.hpp>
#include <llarp/link/session.hpp>
constexpr size_t MAX_LINK_MSG_SIZE = 8192;
constexpr size_t MAX_LINK_MSG_SIZE = 8192;
constexpr llarp_time_t DefaultLinkSessionLifetime = 10 * 1000;
#endif

@ -1,7 +1,10 @@
#ifndef LLARP_MESSAGES_DISCARD_HPP
#define LLARP_MESSAGES_DISCARD_HPP
#include <llarp/link_message.hpp>
#include <llarp/routing/message.hpp>
#include <llarp/bencode.hpp>
#include <llarp/routing/handler.hpp>
namespace llarp
{
struct DiscardMessage : public ILinkMessage
@ -44,6 +47,62 @@ namespace llarp
return true;
}
};
namespace routing
{
struct DataDiscardMessage : public IMessage
{
PathID_t P;
DataDiscardMessage() : IMessage()
{
}
DataDiscardMessage(const PathID_t& dst, uint64_t s) : P(dst)
{
S = s;
version = LLARP_PROTO_VERSION;
}
bool
HandleMessage(IMessageHandler* h, llarp_router* r) const
{
return h->HandleDataDiscardMessage(this, r);
}
bool
DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf)
{
bool read = false;
if(!BEncodeMaybeReadDictEntry("P", P, read, k, buf))
return false;
if(!BEncodeMaybeReadDictInt("S", S, read, k, buf))
return false;
if(!BEncodeMaybeReadDictInt("V", version, read, k, buf))
return false;
return read;
}
bool
BEncode(llarp_buffer_t* buf) const
{
if(!bencode_start_dict(buf))
return false;
if(!BEncodeWriteDictMsgType(buf, "A", "D"))
return false;
if(!BEncodeWriteDictEntry("P", P, buf))
return false;
if(!BEncodeWriteDictInt("S", S, buf))
return false;
if(!BEncodeWriteDictInt("V", version, buf))
return false;
return bencode_end(buf);
}
};
} // namespace routing
} // namespace llarp
#endif

@ -8,6 +8,8 @@ namespace llarp
struct LinkIntroMessage : public ILinkMessage
{
static constexpr size_t MaxSize = MAX_RC_SIZE + 256;
LinkIntroMessage() : ILinkMessage()
{
}
@ -19,8 +21,12 @@ namespace llarp
~LinkIntroMessage();
RouterContact rc;
KeyExchangeNonce N;
Signature Z;
uint64_t P;
LinkIntroMessage&
operator=(const LinkIntroMessage& msg);
bool
DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf);
@ -30,6 +36,12 @@ namespace llarp
bool
HandleMessage(llarp_router* router) const;
bool
Sign(llarp_crypto* c, const SecretKey& signKeySecret);
bool
Verify(llarp_crypto* c) const;
};
} // namespace llarp

@ -14,7 +14,6 @@ namespace llarp
{
PathID_t P;
service::ProtocolFrame T;
uint64_t V = 0;
TunnelNonce Y;
PathTransferMessage();
@ -38,4 +37,4 @@ namespace llarp
} // namespace routing
} // namespace llarp
#endif
#endif

@ -6,7 +6,7 @@
#include <llarp/link_message.hpp>
#include <llarp/path_types.hpp>
#include <llarp/pow.hpp>
#include <vector>
#include <array>
namespace llarp
{

@ -49,6 +49,13 @@ struct llarp_nodedb_iter
int
llarp_nodedb_iterate_all(struct llarp_nodedb *n, struct llarp_nodedb_iter i);
/// visit all loaded rc
/// stop iteration if visit return false
void
llarp_nodedb_visit_loaded(
struct llarp_nodedb *n,
std::function< bool(const llarp::RouterContact &) > visit);
/// return number of RC loaded
size_t
llarp_nodedb_num_loaded(struct llarp_nodedb *n);
@ -66,6 +73,13 @@ bool
llarp_nodedb_get_rc(struct llarp_nodedb *n, const llarp::RouterID &pk,
llarp::RouterContact &result);
/**
remove rc by public key from nodedb
returns true if removed
*/
bool
llarp_nodedb_del_rc(struct llarp_nodedb *n, const llarp::RouterID &pk);
/// struct for async rc verification
struct llarp_async_verify_rc;

@ -24,8 +24,8 @@
#define MAXHOPS (8)
#define DEFAULT_PATH_LIFETIME (10 * 60 * 1000)
#define PATH_BUILD_TIMEOUT (10 * 1000)
#define MESSAGE_PAD_SIZE (1024)
#define PATH_BUILD_TIMEOUT (15 * 1000)
#define MESSAGE_PAD_SIZE (512)
namespace llarp
{
@ -159,6 +159,10 @@ namespace llarp
HandleRoutingMessage(const llarp::routing::IMessage* msg,
llarp_router* r);
bool
HandleDataDiscardMessage(const llarp::routing::DataDiscardMessage* msg,
llarp_router* r);
bool
HandlePathConfirmMessage(const llarp::routing::PathConfirmMessage* msg,
llarp_router* r);
@ -220,8 +224,11 @@ namespace llarp
struct Path : public IHopHandler, public llarp::routing::IMessageHandler
{
typedef std::function< void(Path*) > BuildResultHookFunc;
typedef std::function< bool(Path*, llarp_time_t) > CheckForDeadFunc;
typedef std::function< bool(Path*, const PathID_t&, uint64_t) >
DropHandlerFunc;
typedef std::vector< PathHopConfig > HopList;
typedef std::function< bool(const service::ProtocolFrame*) >
typedef std::function< bool(Path*, const service::ProtocolFrame*) >
DataHandlerFunc;
HopList hops;
@ -229,7 +236,7 @@ namespace llarp
llarp::service::Introduction intro;
llarp_time_t buildStarted;
PathStatus status;
PathStatus _status;
Path(const std::vector< RouterContact >& routers);
@ -242,6 +249,21 @@ namespace llarp
m_DataHandler = func;
}
void
SetDropHandler(DropHandlerFunc func)
{
m_DropHandler = func;
}
void
SetDeadChecker(CheckForDeadFunc func)
{
m_CheckForDead = func;
}
void
EnterState(PathStatus st);
llarp_time_t
ExpireTime() const
{
@ -257,6 +279,10 @@ namespace llarp
bool
SendRoutingMessage(llarp::routing::IMessage* msg, llarp_router* r);
bool
HandleDataDiscardMessage(const llarp::routing::DataDiscardMessage* msg,
llarp_router* r);
bool
HandlePathConfirmMessage(const llarp::routing::PathConfirmMessage* msg,
llarp_router* r);
@ -306,12 +332,17 @@ namespace llarp
RouterID
Upstream() const;
std::string
Name() const;
protected:
llarp::routing::InboundMessageParser m_InboundMessageParser;
private:
BuildResultHookFunc m_BuiltHook;
DataHandlerFunc m_DataHandler;
DropHandlerFunc m_DropHandler;
CheckForDeadFunc m_CheckForDead;
llarp_time_t m_LastLatencyTestTime = 0;
uint64_t m_LastLatencyTestID = 0;
};

@ -101,6 +101,9 @@ namespace llarp
Path*
GetPathByRouter(const RouterID& router) const;
Path*
GetNewestPathByRouter(const RouterID& router) const;
Path*
GetPathByID(const PathID_t& id) const;

@ -0,0 +1,76 @@
#ifndef LLARP_PROFILING_HPP
#define LLARP_PROFILING_HPP
#include <llarp/bencode.hpp>
#include <llarp/threading.hpp>
#include <llarp/router_id.hpp>
#include <llarp/path.hpp>
#include <map>
namespace llarp
{
struct RouterProfile : public IBEncodeMessage
{
static constexpr size_t MaxSize = 256;
uint64_t connectTimeoutCount = 0;
uint64_t connectGoodCount = 0;
uint64_t pathSuccessCount = 0;
uint64_t pathFailCount = 0;
RouterProfile() : IBEncodeMessage(){};
~RouterProfile(){};
bool
BEncode(llarp_buffer_t* buf) const;
bool
DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf);
bool
IsGood(uint64_t chances) const;
};
struct Profiling : public IBEncodeMessage
{
Profiling() : IBEncodeMessage(){};
~Profiling()
{
}
bool
IsBad(const RouterID& r, uint64_t chances = 8);
void
MarkSuccess(const RouterID& r);
void
MarkTimeout(const RouterID& r);
bool
BEncode(llarp_buffer_t* buf) const;
bool
DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf);
bool
Load(const char* fname);
bool
Save(const char* fname);
void
MarkPathFail(path::Path* p);
void
MarkPathSuccess(path::Path* p);
private:
typedef llarp::util::Lock lock_t;
typedef llarp::util::Mutex mtx_t;
mtx_t m_ProfilesMutex;
std::map< RouterID, RouterProfile > m_Profiles;
};
} // namespace llarp
#endif

@ -33,13 +33,13 @@ namespace llarp
}
// advertised addresses
std::vector< AddressInfo > addrs = {};
std::vector< AddressInfo > addrs;
// public encryption public key
llarp::PubKey enckey;
// public signing public key
llarp::PubKey pubkey;
// advertised exits
std::vector< ExitInfo > exits = {};
std::vector< ExitInfo > exits;
// signature
llarp::Signature signature;
/// node nickname, yw kee
@ -84,6 +84,12 @@ namespace llarp
bool
Sign(llarp_crypto *crypto, const llarp::SecretKey &secret);
bool
OtherIsNewer(const RouterContact &other) const
{
return last_updated < other.last_updated;
}
bool
Read(const char *fname);

@ -7,15 +7,22 @@
#include <llarp/messages/path_confirm.hpp>
#include <llarp/messages/path_latency.hpp>
#include <llarp/messages/path_transfer.hpp>
namespace llarp
{
namespace routing
{
struct DataDiscardMessage;
// handles messages on the routing level
struct IMessageHandler
{
virtual bool
HandleDataDiscardMessage(const DataDiscardMessage *msg,
llarp_router *r) = 0;
virtual bool
HandlePathTransferMessage(const PathTransferMessage *msg,
llarp_router *r) = 0;
@ -36,4 +43,4 @@ namespace llarp
} // namespace routing
} // namespace llarp
#endif
#endif

@ -16,6 +16,13 @@ namespace llarp
{
llarp::PathID_t from;
uint64_t S = 0;
IMessage() : llarp::IBEncodeMessage()
{
}
virtual ~IMessage(){};
virtual bool
HandleMessage(IMessageHandler* h, llarp_router* r) const = 0;
};
@ -38,4 +45,4 @@ namespace llarp
} // namespace routing
} // namespace llarp
#endif
#endif

@ -85,6 +85,7 @@ namespace llarp
signkey = other.signkey;
version = other.version;
vanity = other.vanity;
version = other.version;
UpdateAddr();
return *this;
};

@ -28,6 +28,20 @@ namespace llarp
expiresAt = other.expiresAt;
}
bool
IsExpired(llarp_time_t now) const
{
return now >= expiresAt;
}
bool
ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 5000) const
{
if(dlt)
return now >= (expiresAt - (llarp_randint() % dlt));
return IsExpired(now);
}
~Introduction();
friend std::ostream&
@ -51,8 +65,22 @@ namespace llarp
{
return expiresAt < other.expiresAt || pathID < other.pathID;
}
bool
operator==(const Introduction& other) const
{
return expiresAt == other.expiresAt && version == other.version
&& pathID == other.pathID && router == other.router
&& latency == other.latency;
}
bool
operator!=(const Introduction& other) const
{
return !(*this == other);
}
};
} // namespace service
} // namespace llarp
#endif
#endif

@ -28,7 +28,7 @@ namespace llarp
IntroSet() = default;
IntroSet(const IntroSet&& other)
IntroSet(IntroSet&& other)
{
A = std::move(other.A);
I = std::move(other.I);
@ -75,6 +75,12 @@ namespace llarp
return A < other.A;
}
bool
OtherIsNewer(const IntroSet& other) const
{
return GetNewestIntroExpiration() < other.GetNewestIntroExpiration();
}
friend std::ostream&
operator<<(std::ostream& out, const IntroSet& i)
{
@ -101,21 +107,13 @@ namespace llarp
return out << " V=" << i.version << " Z=" << i.Z;
}
bool
IsNewerThan(const IntroSet& other) const
{
return GetNewestIntro().expiresAt > other.GetNewestIntro().expiresAt;
}
Introduction
GetNewestIntro() const
llarp_time_t
GetNewestIntroExpiration() const
{
Introduction i;
i.expiresAt = 0;
llarp_time_t t = 0;
for(const auto& intro : I)
if(intro.expiresAt > i.expiresAt)
i = intro;
return i;
t = std::max(intro.expiresAt, t);
return t;
}
bool
@ -136,4 +134,4 @@ namespace llarp
} // namespace service
} // namespace llarp
#endif
#endif

@ -7,10 +7,18 @@
#include <llarp/service/protocol.hpp>
#include <llarp/path.hpp>
// minimum time between interoset shifts
#ifndef MIN_SHIFT_INTERVAL
#define MIN_SHIFT_INTERVAL (5 * 1000)
#endif
namespace llarp
{
namespace service
{
// foward declare
struct AsyncKeyExchange;
struct Endpoint : public path::Builder,
public ILookupHolder,
public IDataHandler
@ -73,9 +81,15 @@ namespace llarp
bool
ShouldPublishDescriptors(llarp_time_t now) const;
void
EnsureReplyPath(const ServiceInfo& addr);
bool
PublishIntroSet(llarp_router* r);
bool
PublishIntroSetVia(llarp_router* r, path::Path* p);
bool
HandleGotIntroMessage(const llarp::dht::GotIntroMessage* msg);
@ -83,7 +97,8 @@ namespace llarp
HandleGotRouterMessage(const llarp::dht::GotRouterMessage* msg);
bool
HandleHiddenServiceFrame(const llarp::service::ProtocolFrame* msg);
HandleHiddenServiceFrame(path::Path* p,
const llarp::service::ProtocolFrame* msg);
/// return true if we have an established path to a hidden service
bool
@ -99,10 +114,13 @@ namespace llarp
bool
ForgetPathToService(const Address& remote);
virtual void
HandleDataMessage(ProtocolMessage* msg)
bool
HandleDataMessage(const PathID_t&, ProtocolMessage* msg);
virtual bool
ProcessDataMessage(ProtocolMessage* msg)
{
// override me in subclass
return true;
}
/// ensure that we know a router, looks up if it doesn't
@ -143,31 +161,82 @@ namespace llarp
}
};
bool
HandleDataDrop(path::Path* p, const PathID_t& dst, uint64_t s);
bool
CheckPathIsDead(path::Path* p, llarp_time_t latency);
typedef std::queue< PendingBuffer > PendingBufferQueue;
struct SendContext
{
SendContext(const ServiceInfo& ident, const Introduction& intro,
PathSet* send, Endpoint* ep);
void
AsyncEncryptAndSendTo(llarp_buffer_t payload, ProtocolType t);
/// send a fully encrypted hidden service frame
/// via a path on our pathset with path id p
void
Send(ProtocolFrame& f);
llarp::SharedSecret sharedKey;
ServiceInfo remoteIdent;
Introduction remoteIntro;
PathSet* m_PathSet;
IDataHandler* m_DataHandler;
Endpoint* m_Endpoint;
uint64_t sequenceNo = 0;
virtual void
ShiftIntroduction(){};
virtual void
UpdateIntroSet(){};
virtual void
MarkCurrentIntroBad(){};
private:
void
EncryptAndSendTo(llarp_buffer_t payload, ProtocolType t);
virtual void
AsyncGenIntro(llarp_buffer_t payload, ProtocolType t)
{
}
};
static void
HandlePathDead(void*);
/// context needed to initiate an outbound hidden service session
struct OutboundContext : public path::Builder
struct OutboundContext : public path::Builder, public SendContext
{
OutboundContext(const IntroSet& introSet, Endpoint* parent);
~OutboundContext();
/// the remote hidden service's curren intro set
IntroSet currentIntroSet;
/// the current selected intro
Introduction selectedIntro;
bool
HandleDataDrop(path::Path* p, const PathID_t& dst, uint64_t s);
/// set to true if we are updating the remote introset right now
bool updatingIntroSet;
/// update the current selected intro to be a new best introduction
void
ShiftIntroduction();
/// mark the current remote intro as bad
void
MarkCurrentIntroBad();
/// tick internal state
/// return true to remove otherwise don't remove
bool
Tick(llarp_time_t now);
/// encrypt asynchronously and send to remote endpoint from us
void
AsyncEncryptAndSendTo(llarp_buffer_t D, ProtocolType protocol);
AsyncGenIntro(llarp_buffer_t payload, ProtocolType t);
/// issues a lookup to find the current intro set of the remote service
void
@ -181,29 +250,22 @@ namespace llarp
RouterContact& cur, size_t hop);
bool
HandleHiddenServiceFrame(const ProtocolFrame* frame);
HandleHiddenServiceFrame(path::Path* p, const ProtocolFrame* frame);
std::string
Name() const;
private:
bool
OnIntroSetUpdate(const IntroSet* i);
void
EncryptAndSendTo(path::Path* p, llarp_buffer_t payload, ProtocolType t);
OnGeneratedIntroFrame(AsyncKeyExchange* k, PathID_t p);
void
AsyncGenIntro(path::Path* p, llarp_buffer_t payload, ProtocolType t);
/// send a fully encrypted hidden service frame
void
Send(ProtocolFrame& f);
bool
OnIntroSetUpdate(const Address& addr, const IntroSet* i);
uint64_t sequenceNo = 0;
llarp::SharedSecret sharedKey;
Endpoint* m_Parent;
// uint64_t m_UpdateIntrosetTX = 0;
uint64_t m_UpdateIntrosetTX = 0;
IntroSet currentIntroSet;
std::set< Introduction > m_BadIntros;
llarp_time_t lastShift = 0;
};
// passed a sendto context when we have a path established otherwise
@ -249,12 +311,15 @@ namespace llarp
void
PutNewOutboundContext(const IntroSet& introset);
protected:
virtual void
IntroSetPublishFail();
virtual void
IntroSetPublished();
protected:
void
RegenAndPublishIntroSet(llarp_time_t now);
IServiceLookup*
GenerateLookupByTag(const Tag& tag);
@ -275,7 +340,7 @@ namespace llarp
private:
bool
OnOutboundLookup(const IntroSet* i); /* */
OnOutboundLookup(const Address&, const IntroSet* i); /* */
static bool
SetupIsolatedNetwork(void* user, bool success);
@ -319,6 +384,10 @@ namespace llarp
std::unordered_map< Address, std::unique_ptr< OutboundContext >,
Address::Hash >
m_RemoteSessions;
std::unordered_map< Address, ServiceInfo, Address::Hash >
m_AddressToService;
std::unordered_map< Address, PathEnsureHook, Address::Hash >
m_PendingServiceLookups;
@ -348,6 +417,7 @@ namespace llarp
uint64_t m_CurrentPublishTX = 0;
llarp_time_t m_LastPublish = 0;
llarp_time_t m_LastPublishAttempt = 0;
llarp_time_t m_MinPathLatency = 10000;
/// our introset
service::IntroSet m_IntroSet;
/// pending remote service lookups by id

@ -3,6 +3,8 @@
#include <llarp/aligned.hpp>
#include <llarp/crypto.hpp>
#include <llarp/service/IntroSet.hpp>
#include <llarp/path_types.hpp>
namespace llarp
{
namespace service
@ -12,8 +14,8 @@ namespace llarp
struct ProtocolMessage;
struct IDataHandler
{
virtual void
HandleDataMessage(ProtocolMessage* msg) = 0;
virtual bool
HandleDataMessage(const PathID_t&, ProtocolMessage* msg) = 0;
virtual bool
GetCachedSessionKeyFor(const ConvoTag& remote,
@ -41,4 +43,4 @@ namespace llarp
} // namespace service
} // namespace llarp
#endif
#endif

@ -28,12 +28,14 @@ namespace llarp
ProtocolMessage(const ConvoTag& tag);
ProtocolMessage();
~ProtocolMessage();
ProtocolType proto = eProtocolText;
ProtocolType proto = eProtocolTraffic;
llarp_time_t queued = 0;
std::vector< byte_t > payload;
Introduction introReply;
ServiceInfo sender;
IDataHandler* handler = nullptr;
/// local path we got this message from
PathID_t srcPath;
ConvoTag tag;
bool
@ -58,10 +60,33 @@ namespace llarp
llarp::Signature Z;
llarp::service::ConvoTag T;
ProtocolFrame();
ProtocolFrame(const ProtocolFrame& other)
: llarp::routing::IMessage()
, C(other.C)
, D(other.D)
, N(other.N)
, Z(other.Z)
, T(other.T)
{
S = other.S;
version = other.version;
}
ProtocolFrame() : llarp::routing::IMessage()
{
}
~ProtocolFrame();
bool
operator==(const ProtocolFrame& other) const;
bool
operator!=(const ProtocolFrame& other) const
{
return !(*this == other);
}
ProtocolFrame&
operator=(const ProtocolFrame& other);
@ -71,7 +96,7 @@ namespace llarp
bool
AsyncDecryptAndVerify(llarp_logic* logic, llarp_crypto* c,
llarp_threadpool* worker,
const PathID_t& srcpath, llarp_threadpool* worker,
const Identity& localIdent,
IDataHandler* handler) const;
@ -94,4 +119,4 @@ namespace llarp
} // namespace service
} // namespace llarp
#endif
#endif

@ -6,11 +6,11 @@
#endif
#ifndef LLARP_VERSION_MIN
#define LLARP_VERSION_MIN "1"
#define LLARP_VERSION_MIN "2"
#endif
#ifndef LLARP_VERSION_PATCH
#define LLARP_VERSION_PATCH "0"
#define LLARP_VERSION_PATCH "2"
#endif
#ifndef LLARP_VERSION_NUM

@ -75,14 +75,7 @@ namespace llarp
// encryption public key
if(llarp_buffer_eq(key, "e"))
{
if(!bencode_read_string(buf, &strbuf))
return false;
if(strbuf.sz != PUBKEYSIZE)
return false;
pubkey = strbuf.base;
return true;
return pubkey.BDecode(buf);
}
// ip address

@ -34,7 +34,6 @@ namespace llarp
dns = find_section(top, "dns", section_t{});
iwp_links = find_section(top, "bind", section_t{});
services = find_section(top, "services", section_t{});
dns = find_section(top, "dns", section_t{});
system = find_section(top, "system", section_t{});
return true;
}

@ -53,8 +53,7 @@ namespace llarp
iter.user = this;
iter.visit = &iter_config;
llarp_config_iter(config, &iter);
llarp::LogInfo("config [", configfile, "] loaded");
return true;
return router->ReloadConfig(config);
}
void
@ -72,10 +71,6 @@ namespace llarp
ctx->worker = llarp_init_threadpool(workers, "llarp-worker");
}
}
else if(!strcmp(key, "contact-file"))
{
strncpy(ctx->conatctFile, val, fmin(255, strlen(val)));
}
else if(!strcmp(key, "net-threads"))
{
ctx->num_nethreads = atoi(val);
@ -89,7 +84,7 @@ namespace llarp
{
if(!strcmp(key, "dir"))
{
strncpy(ctx->nodedb_dir, val, sizeof(ctx->nodedb_dir));
ctx->nodedb_dir = val;
}
}
}
@ -99,20 +94,14 @@ namespace llarp
{
llarp_crypto_init(&crypto);
nodedb = llarp_nodedb_new(&crypto);
if(!nodedb_dir[0])
{
llarp::LogError("no nodedb_dir configured");
return 0;
}
nodedb_dir[sizeof(nodedb_dir) - 1] = 0;
if(!llarp_nodedb_ensure_dir(nodedb_dir))
if(!llarp_nodedb_ensure_dir(nodedb_dir.c_str()))
{
llarp::LogError("nodedb_dir is incorrect");
return 0;
}
// llarp::LogInfo("nodedb_dir [", nodedb_dir, "] configured!");
ssize_t loaded = llarp_nodedb_load_dir(nodedb, nodedb_dir);
ssize_t loaded = llarp_nodedb_load_dir(nodedb, nodedb_dir.c_str());
llarp::LogInfo("nodedb_dir loaded ", loaded, " RCs from [", nodedb_dir,
"]");
if(loaded < 0)
@ -151,7 +140,8 @@ namespace llarp
{
llarp::LogInfo(LLARP_VERSION, " ", LLARP_RELEASE_MOTTO);
llarp::LogInfo("starting up");
this->LoadDatabase();
if(!this->LoadDatabase())
return -1;
llarp_ev_loop_alloc(&mainloop);
// ensure worker thread pool
@ -412,7 +402,7 @@ llarp_main_getDatabase(struct llarp_main *ptr, byte_t *pk)
//llarp_rc *rc = new llarp_rc;
llarp::RouterContact *rc = new llarp::RouterContact;
//llarp_rc_new(rc);
llarp::LogInfo("FIXME: Loading ", ptr->ctx->conatctFile);
//llarp::LogInfo("FIXME: Loading ", ptr->ctx->conatctFile);
// FIXME
/*
if(llarp_rc_read(ptr->ctx->conatctFile, rc))

@ -27,7 +27,7 @@ namespace llarp
Context::Explore(size_t N)
{
// ask N random peers for new routers
llarp::LogInfo("Exploring network");
llarp::LogInfo("Exploring network via ", N, " peers");
std::set< Key_t > peers;
if(nodes->GetManyRandom(peers, N))
@ -46,6 +46,13 @@ namespace llarp
{
}
bool
Validate(const RouterID &) const
{
// TODO: check with lokid
return true;
}
void
Start(const TXOwner &peer)
{
@ -70,13 +77,9 @@ namespace llarp
llarp::LogInfo("got ", valuesFound.size(), " routers from exploration");
for(const auto &pk : valuesFound)
{
RouterContact rc;
if(!llarp_nodedb_get_rc(parent->router->nodedb, pk, rc))
{
// try connecting to it we don't know it
// this triggers a dht lookup
parent->router->TryEstablishTo(pk);
}
// try connecting to it we don't know it
// this triggers a dht lookup
parent->router->TryEstablishTo(pk);
}
}
};
@ -85,9 +88,8 @@ namespace llarp
Context::ExploreNetworkVia(const Key_t &askpeer)
{
TXOwner peer(askpeer, ++ids);
auto tx = pendingExploreLookups.NewTX(
peer, askpeer, new ExploreNetworkJob(askpeer, this));
tx->Start(peer);
pendingExploreLookups.NewTX(peer, askpeer,
new ExploreNetworkJob(askpeer, this));
}
void
@ -96,7 +98,7 @@ namespace llarp
if(left)
return;
Context *ctx = static_cast< Context * >(u);
ctx->Explore();
ctx->Explore(1);
llarp_logic_call_later(ctx->router->logic,
{orig, ctx, &handle_explore_timer});
}
@ -120,7 +122,7 @@ namespace llarp
{
if(itr->second.introset.IsExpired(now))
{
llarp::LogInfo("introset expired ", itr->second.introset.A.Addr());
llarp::LogDebug("introset expired ", itr->second.introset.A.Addr());
itr = nodes.erase(itr);
}
else
@ -192,7 +194,7 @@ namespace llarp
{
// we are the target, give them our RC
replies.emplace_back(
new GotRouterMessage(requester, txid, {router->rc}, false));
new GotRouterMessage(requester, txid, {router->rc()}, false));
return;
}
Key_t next;
@ -322,6 +324,23 @@ namespace llarp
peersAsked.insert(ctx->OurKey());
}
bool
Validate(const service::IntroSet &value) const
{
if(!value.VerifySignature(parent->Crypto()))
{
llarp::LogWarn(
"Got introset with invalid signature from service lookup");
return false;
}
if(value.A.Addr() != target)
{
llarp::LogWarn("got introset with wrong target from service lookup");
return false;
}
return true;
}
void
DoNextRequest(const Key_t &nextPeer)
{
@ -422,10 +441,9 @@ namespace llarp
{
TXOwner asker(OurKey(), txid);
TXOwner peer(askpeer, ++ids);
auto tx = pendingIntrosetLookups.NewTX(
pendingIntrosetLookups.NewTX(
peer, addr,
new LocalServiceAddressLookup(path, txid, addr, this, askpeer));
tx->Start(peer);
}
struct PublishServiceJob : public TX< service::Address, service::IntroSet >
@ -444,6 +462,18 @@ namespace llarp
{
}
bool
Validate(const service::IntroSet &introset) const
{
if(I.A != introset.A)
{
llarp::LogWarn(
"publish introset acknoledgement acked a different service");
return false;
}
return true;
}
void
Start(const TXOwner &peer)
{
@ -481,10 +511,9 @@ namespace llarp
TXOwner asker(from, txid);
TXOwner peer(tellpeer, ++ids);
service::Address addr = introset.A.Addr();
auto tx = pendingIntrosetLookups.NewTX(
asker, addr,
new PublishServiceJob(asker, introset, this, S, exclude));
tx->Start(peer);
pendingIntrosetLookups.NewTX(
asker, addr, new PublishServiceJob(asker, introset, this, S, exclude),
true);
}
void
@ -495,9 +524,8 @@ namespace llarp
{
TXOwner asker(whoasked, txid);
TXOwner peer(askpeer, ++ids);
auto tx = pendingIntrosetLookups.NewTX(
pendingIntrosetLookups.NewTX(
peer, addr, new ServiceAddressLookup(asker, addr, this, R, handler));
tx->Start(peer);
}
void
@ -508,9 +536,8 @@ namespace llarp
{
TXOwner asker(whoasked, txid);
TXOwner peer(askpeer, ++ids);
auto tx = pendingIntrosetLookups.NewTX(
pendingIntrosetLookups.NewTX(
peer, addr, new ServiceAddressLookup(asker, addr, this, 0, handler));
tx->Start(peer);
}
struct TagLookup : public TX< service::Tag, service::IntroSet >
@ -522,6 +549,22 @@ namespace llarp
{
}
bool
Validate(const service::IntroSet &introset) const
{
if(!introset.VerifySignature(parent->Crypto()))
{
llarp::LogWarn("got introset from tag lookup with invalid signature");
return false;
}
if(introset.topic != target)
{
llarp::LogWarn("got introset with missmatched topic in tag lookup");
return false;
}
return true;
}
void
Start(const TXOwner &peer)
{
@ -573,9 +616,7 @@ namespace llarp
{
TXOwner asker(whoasked, whoaskedTX);
TXOwner peer(askpeer, ++ids);
auto tx = pendingTagLookups.NewTX(peer, tag,
new TagLookup(asker, tag, this, R));
tx->Start(peer);
pendingTagLookups.NewTX(peer, tag, new TagLookup(asker, tag, this, R));
}
bool
@ -586,11 +627,22 @@ namespace llarp
std::vector< RouterID > closer;
Key_t t(target.data());
std::set< Key_t > found;
if(!nodes->GetManyNearExcluding(t, found, 4,
// TODO: also load from nodedb
size_t nodeCount = nodes->Size();
if(nodeCount == 0)
{
llarp::LogError(
"cannot handle exploritory router lookup, no dht peers");
return false;
}
size_t want = std::min(size_t(4), nodeCount);
if(!nodes->GetManyNearExcluding(t, found, want,
std::set< Key_t >{ourKey, requester}))
{
llarp::LogError(
"not enough dht nodes to handle exploritory router lookup");
"not enough dht nodes to handle exploritory router lookup, "
"need a minimum of ",
want, " dht peers");
return false;
}
for(const auto &f : found)
@ -611,6 +663,17 @@ namespace llarp
peersAsked.insert(ctx->OurKey());
}
bool
Validate(const RouterContact &rc) const
{
if(!rc.VerifySignature(parent->Crypto()))
{
llarp::LogWarn("rc has invalid signature from lookup result");
return false;
}
return true;
}
bool
GetNextPeer(Key_t &next, const std::set< Key_t > &exclude)
{
@ -631,12 +694,6 @@ namespace llarp
new FindRouterMessage(parent->OurKey(), target, peer.txid));
}
void
SendTo(const Key_t &peer, IMessage *msg) const
{
return parent->DHTSendTo(peer, msg);
}
virtual void
SendReply()
{
@ -698,9 +755,8 @@ namespace llarp
{
TXOwner peer(askpeer, ++ids);
auto tx = pendingRouterLookups.NewTX(
pendingRouterLookups.NewTX(
peer, target, new LocalRouterLookup(path, txid, target, this));
tx->Start(peer);
}
void
@ -711,10 +767,18 @@ namespace llarp
{
TXOwner asker(whoasked, txid);
TXOwner peer(askpeer, ++ids);
auto tx = pendingRouterLookups.NewTX(
peer, target,
new RecursiveRouterLookup(asker, target, this, handler));
tx->Start(peer);
if(target != askpeer)
{
pendingRouterLookups.NewTX(
peer, target,
new RecursiveRouterLookup(asker, target, this, handler));
}
}
llarp_crypto *
Context::Crypto()
{
return &router->crypto;
}
} // namespace dht

@ -22,7 +22,7 @@ namespace llarp
if(path)
{
replies.emplace_back(
new GotRouterMessage(K.data(), txid, {dht.router->rc}, false));
new GotRouterMessage(K.data(), txid, {dht.router->rc()}, false));
return true;
}
return false;
@ -30,6 +30,15 @@ namespace llarp
Key_t peer;
Key_t k = K.data();
// check if we know this in our nodedb first
RouterContact found;
if(llarp_nodedb_get_rc(dht.router->nodedb, K, found))
{
replies.emplace_back(
new GotRouterMessage(K.data(), txid, {found}, false));
return true;
}
// lookup if we don't have it in our nodedb
if(dht.nodes->FindClosest(k, peer))
dht.LookupRouterForPath(K, txid, pathID, peer);
return true;
@ -146,8 +155,15 @@ namespace llarp
llarp::LogWarn("Duplicate FRM from ", From, " txid=", txid);
return false;
}
RouterContact found;
if(exploritory)
return dht.HandleExploritoryRouterLookup(From, txid, K, replies);
else if(llarp_nodedb_get_rc(dht.router->nodedb, K, found))
{
replies.emplace_back(
new GotRouterMessage(K.data(), txid, {found}, false));
return true;
}
else
dht.LookupRouterRelayed(From, txid, K.data(), !iterative, replies);
return true;

@ -36,6 +36,8 @@ namespace llarp
From);
return false;
}
else
dht.services->PutNode(introset);
}
TXOwner owner(From, T);
auto tagLookup = dht.pendingTagLookups.GetPendingLookupFrom(owner);

@ -43,12 +43,7 @@ namespace llarp
bool
queue_write(const void* data, size_t sz)
{
return m_writeq.EmplaceIf(
[&](WriteBuffer& pkt) -> bool {
return m_writeq.Size() < MAX_WRITE_QUEUE_SIZE
&& sz <= sizeof(pkt.buf);
},
data, sz);
return write(fd, data, sz) != -1;
}
/// called in event loop when fd is ready for writing

@ -143,21 +143,12 @@ namespace llarp
struct llarp_epoll_loop : public llarp_ev_loop
{
int epollfd;
int pipefds[2];
llarp_epoll_loop() : epollfd(-1)
{
pipefds[0] = -1;
pipefds[1] = -1;
}
~llarp_epoll_loop()
{
if(pipefds[0] != -1)
close(pipefds[0]);
if(pipefds[1] != -1)
close(pipefds[1]);
if(epollfd != -1)
close(epollfd);
}
@ -173,16 +164,6 @@ struct llarp_epoll_loop : public llarp_ev_loop
{
if(epollfd == -1)
epollfd = epoll_create(1);
if(epollfd != -1)
{
if(pipe(pipefds) == -1)
return false;
epoll_event sig_ev;
sig_ev.data.fd = pipefds[0];
sig_ev.events = EPOLLIN;
return epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefds[0], &sig_ev) != -1;
}
return false;
}
@ -197,12 +178,6 @@ struct llarp_epoll_loop : public llarp_ev_loop
int idx = 0;
while(idx < result)
{
// handle signalfd
if(events[idx].data.fd == pipefds[0])
{
llarp::LogDebug("exiting epoll loop");
return 0;
}
llarp::ev_io* ev = static_cast< llarp::ev_io* >(events[idx].data.ptr);
if(events[idx].events & EPOLLIN)
{
@ -211,7 +186,8 @@ struct llarp_epoll_loop : public llarp_ev_loop
++idx;
}
}
tick_listeners();
if(result != -1)
tick_listeners();
return result;
}
@ -228,12 +204,6 @@ struct llarp_epoll_loop : public llarp_ev_loop
int idx = 0;
while(idx < result)
{
// handle signalfd
if(events[idx].data.fd == pipefds[0])
{
llarp::LogDebug("exiting epoll loop");
return 0;
}
llarp::ev_io* ev = static_cast< llarp::ev_io* >(events[idx].data.ptr);
if(events[idx].events & EPOLLIN)
{
@ -242,7 +212,8 @@ struct llarp_epoll_loop : public llarp_ev_loop
++idx;
}
}
tick_listeners();
if(result != -1)
tick_listeners();
} while(epollfd != -1);
return result;
}
@ -356,9 +327,9 @@ struct llarp_epoll_loop : public llarp_ev_loop
void
stop()
{
int i = 1;
auto val = write(pipefds[1], &i, sizeof(i));
(void)val;
if(epollfd != -1)
close(epollfd);
epollfd = -1;
}
};

@ -195,7 +195,8 @@ struct llarp_kqueue_loop : public llarp_ev_loop
++idx;
}
}
tick_listeners();
if(result != -1)
tick_listeners();
return result;
}
@ -229,7 +230,8 @@ struct llarp_kqueue_loop : public llarp_ev_loop
++idx;
}
}
tick_listeners();
if(result != -1)
tick_listeners();
} while(result != -1);
return result;
}

@ -23,6 +23,7 @@ namespace fs = std::experimental::filesystem;
// linux gcc 7.2 needs this
namespace fs = cpp17::filesystem;
#endif
#include <dirent.h>
namespace llarp
{
@ -30,31 +31,25 @@ namespace llarp
{
typedef std::function< bool(const fs::path &) > PathVisitor;
typedef std::function< void(const fs::path &, PathVisitor) > PathIter;
#if defined(CPP17) && defined(USE_CXX17_FILESYSTEM)
static PathIter IterDir = [](const fs::path &path, PathVisitor visit) {
fs::directory_iterator i(path);
auto itr = fs::begin(i);
while(itr != fs::end(i))
{
fs::path p = path / *itr;
if(!visit(p))
return;
++itr;
}
};
#else
static PathIter IterDir = [](const fs::path &path, PathVisitor visit) {
fs::directory_iterator i(path);
auto itr = i.begin();
while(itr != itr.end())
DIR *d = opendir(path.string().c_str());
if(d == nullptr)
return;
struct dirent *ent = nullptr;
do
{
fs::path p = path / *itr;
ent = readdir(d);
if(!ent)
break;
if(ent->d_name[0] == '.')
continue;
fs::path p = path / fs::path(ent->d_name);
if(!visit(p))
return;
++itr;
}
break;
} while(ent);
closedir(d);
};
#endif
} // namespace util
} // namespace llarp
#endif // end LLARP_FS_HPP

@ -38,21 +38,22 @@ namespace llarp
auto addr_str = v.substr(0, pos);
if(!addr.FromString(addr_str))
{
llarp::LogError("cannot map invalid address ", addr_str);
llarp::LogError(Name() + " cannot map invalid address ", addr_str);
return false;
}
auto ip_str = v.substr(pos + 1);
uint32_t ip;
in_addr ip;
if(inet_pton(AF_INET, ip_str.c_str(), &ip) != 1)
{
llarp::LogError("cannot map to invalid ip ", ip_str);
return false;
}
return MapAddress(addr, ip);
return MapAddress(addr, ntohl(ip.s_addr));
}
if(k == "ifname")
{
strncpy(tunif.ifname, v.c_str(), sizeof(tunif.ifname) - 1);
llarp::LogInfo(Name() + " setting ifname to ", tunif.ifname);
return true;
}
if(k == "ifaddr")
@ -78,7 +79,8 @@ namespace llarp
tunif.netmask = 32;
addr = v;
}
llarp::LogInfo("set ifaddr to ", addr, " with netmask ", tunif.netmask);
llarp::LogInfo(Name() + " set ifaddr to ", addr, " with netmask ",
tunif.netmask);
strncpy(tunif.ifaddr, addr.c_str(), sizeof(tunif.ifaddr) - 1);
return true;
}
@ -88,19 +90,18 @@ namespace llarp
bool
TunEndpoint::MapAddress(const service::Address &addr, uint32_t ip)
{
char buf[32] = {0};
inet_ntop(AF_INET, &ip, buf, sizeof(buf));
auto itr = m_IPToAddr.find(ip);
if(itr != m_IPToAddr.end())
{
llarp::LogWarn(buf, " already mapped to ", itr->second.ToString());
llarp::LogWarn(inet_ntoa({ip}), " already mapped to ",
itr->second.ToString());
return false;
}
llarp::LogInfo("map ", addr.ToString(), " to ", buf);
llarp::LogInfo(Name() + " map ", addr.ToString(), " to ",
inet_ntoa({ip}));
m_IPToAddr.insert(std::make_pair(ip, addr));
m_AddrToIP.insert(std::make_pair(addr, ip));
// TODO: make ip mapping persist forever
MarkIPActive(ip);
MarkIPActiveForever(ip);
return true;
}
@ -141,12 +142,12 @@ namespace llarp
llarp::LogError(Name(), " failed to set up tun interface");
return false;
}
m_OurIP = inet_addr(tunif.ifaddr);
m_OurIP = ntohl(inet_addr(tunif.ifaddr));
m_NextIP = m_OurIP;
uint32_t mask = tunif.netmask;
uint32_t baseaddr = (ntohs(m_OurIP) & netmask_ipv4_bits(mask));
m_MaxIP = (ntohs(baseaddr) | ~ntohs(netmask_ipv4_bits(mask)));
uint32_t baseaddr = (htonl(m_OurIP) & netmask_ipv4_bits(mask));
m_MaxIP = (htonl(baseaddr) | ~htonl(netmask_ipv4_bits(mask)));
char buf[128] = {0};
llarp::LogInfo(Name(), " set ", tunif.ifname, " to have address ",
inet_ntop(AF_INET, &m_OurIP, buf, sizeof(buf)));
@ -183,65 +184,68 @@ namespace llarp
auto itr = m_IPToAddr.find(pkt.dst());
if(itr == m_IPToAddr.end())
{
in_addr a;
a.s_addr = pkt.dst();
llarp::LogWarn("drop packet to ", inet_ntoa(a));
llarp::DumpBuffer(pkt.Buffer());
llarp::LogWarn(Name(), " has no endpoint for ",
inet_ntoa({htonl(pkt.dst())}));
return true;
}
return SendToOrQueue(itr->second, pkt.Buffer(),
service::eProtocolTraffic);
if(!SendToOrQueue(itr->second, pkt.Buffer(), service::eProtocolTraffic))
{
llarp::LogWarn(Name(), " did not flush packets");
}
return true;
});
}
void
TunEndpoint::HandleDataMessage(service::ProtocolMessage *msg)
bool
TunEndpoint::ProcessDataMessage(service::ProtocolMessage *msg)
{
if(msg->proto != service::eProtocolTraffic)
{
llarp::LogWarn("dropping unwarrented message, not ip traffic, proto=",
msg->proto);
return;
}
uint32_t themIP = ObtainIPForAddr(msg->sender.Addr());
uint32_t usIP = m_OurIP;
auto buf = llarp::Buffer(msg->payload);
if(!m_NetworkToUserPktQueue.EmplaceIf(
if(m_NetworkToUserPktQueue.EmplaceIf(
[buf, themIP, usIP](net::IPv4Packet &pkt) -> bool {
// do packet info rewrite here
// TODO: don't truncate packet here
memcpy(pkt.buf, buf.base, std::min(buf.sz, sizeof(pkt.buf)));
pkt.sz = std::min(buf.sz, sizeof(pkt.buf));
memcpy(pkt.buf, buf.base, pkt.sz);
pkt.src(themIP);
pkt.dst(usIP);
pkt.UpdateChecksum();
return true;
}))
{
llarp::LogWarn("failed to parse buffer for ip traffic");
llarp::DumpBuffer(buf);
}
llarp::LogInfo(Name(), " handle data message ", msg->payload.size(),
" bytes from ", inet_ntoa({htonl(themIP)}));
else
llarp::LogWarn(Name(), " dropped packet");
return true;
}
uint32_t
TunEndpoint::ObtainIPForAddr(const service::Address &addr)
{
llarp_time_t now = llarp_time_now_ms();
uint32_t nextIP;
uint32_t nextIP = 0;
{
// previously allocated address
auto itr = m_AddrToIP.find(addr);
if(itr != m_AddrToIP.end())
{
// mark ip active
m_IPActivity[itr->second] = now;
MarkIPActive(itr->second);
return itr->second;
}
}
// allocate new address
if(m_NextIP < m_MaxIP)
{
nextIP = ++m_NextIP;
m_AddrToIP.insert(std::make_pair(addr, nextIP));
m_IPToAddr.insert(std::make_pair(nextIP, addr));
llarp::LogInfo(Name(), " mapped ", addr, " to ",
inet_ntoa({htonl(nextIP)}));
MarkIPActive(nextIP);
return nextIP;
}
else
{
@ -271,7 +275,7 @@ namespace llarp
}
// mark ip active
m_IPActivity[nextIP] = now;
m_IPActivity[nextIP] = std::max(m_IPActivity[nextIP], now);
return nextIP;
}
@ -285,7 +289,13 @@ namespace llarp
void
TunEndpoint::MarkIPActive(uint32_t ip)
{
m_IPActivity[ip] = llarp_time_now_ms();
m_IPActivity[ip] = std::max(llarp_time_now_ms(), m_IPActivity[ip]);
}
void
TunEndpoint::MarkIPActiveForever(uint32_t ip)
{
m_IPActivity[ip] = std::numeric_limits< uint64_t >::max();
}
void
@ -331,9 +341,9 @@ namespace llarp
if(!self->m_UserToNetworkPktQueue.EmplaceIf(
[self, buf, sz](net::IPv4Packet &pkt) -> bool {
return pkt.Load(llarp::InitBuffer(buf, sz))
&& pkt.Header()->ip_version == 4;
&& pkt.Header()->version == 4;
}))
llarp::LogError("Failed to parse ipv4 packet");
llarp::LogDebug("Failed to parse ipv4 packet");
}
TunEndpoint::~TunEndpoint()

@ -3,6 +3,8 @@
#include <llarp/ip.hpp>
#include "llarp/buffer.hpp"
#include "mem.hpp"
#include <llarp/endian.h>
#include <map>
namespace llarp
{
@ -26,30 +28,65 @@ namespace llarp
return llarp::InitBuffer(buf, sz);
}
void
IPv4Packet::UpdateChecksum()
static uint16_t
ipchksum(const byte_t *buf, size_t sz, uint32_t sum = 0)
{
auto hdr = Header();
hdr->check = 0;
size_t count = hdr->ihl;
uint32_t sum = 0;
byte_t *addr = buf;
while(count > 1)
while(sz > 1)
{
sum += ntohs(*(uint16_t *)addr);
count -= sizeof(uint16_t);
addr += sizeof(uint16_t);
sum += *(const uint16_t *)buf;
sz -= sizeof(uint16_t);
buf += sizeof(uint16_t);
}
if(count > 0)
sum += *(byte_t *)addr;
if(sz > 0)
sum += *(const byte_t *)buf;
while(sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
hdr->check = htons(~sum);
return ~sum;
}
static std::map<
byte_t, std::function< void(const ip_header *, byte_t *, size_t) > >
protoCheckSummer = {
/// ICMP
{1,
[](const ip_header *hdr, byte_t *buf, size_t sz) {
auto len = hdr->ihl * 4;
uint16_t *check = (uint16_t *)buf + len + 2;
*check = 0;
*check = ipchksum(buf, sz);
}},
/// TCP
{6, [](const ip_header *hdr, byte_t *pkt, size_t sz) {
byte_t pktbuf[1500];
auto len = hdr->ihl * 4;
size_t pktsz = sz - len;
uint16_t *check = (uint16_t *)(pkt + len + 16);
*check = 0;
memcpy(pktbuf, &hdr->saddr, 4);
memcpy(pktbuf + 4, &hdr->daddr, 4);
pktbuf[8] = 0;
pktbuf[9] = 6;
// TODO: endian (?)
pktbuf[10] = (pktsz & 0xff00) >> 8;
pktbuf[11] = pktsz & 0x00ff;
memcpy(pktbuf + 12, pkt + len, pktsz);
*check = ipchksum(pktbuf, 12 + pktsz);
}}};
void
IPv4Packet::UpdateChecksum()
{
auto hdr = Header();
hdr->check = 0;
auto len = hdr->ihl * 4;
hdr->check = ipchksum(buf, len);
auto proto = hdr->protocol;
auto itr = protoCheckSummer.find(proto);
if(itr != protoCheckSummer.end())
{
itr->second(hdr, buf, sz);
}
}
} // namespace net
} // namespace llarp

@ -10,7 +10,7 @@ namespace llarp
bool
ILinkLayer::HasSessionTo(const PubKey& pk)
{
util::Lock l(m_AuthedLinksMutex);
Lock l(m_AuthedLinksMutex);
return m_AuthedLinks.find(pk) != m_AuthedLinks.end();
}
@ -37,7 +37,7 @@ namespace llarp
{
auto now = llarp_time_now_ms();
{
util::Lock lock(m_AuthedLinksMutex);
Lock lock(m_AuthedLinksMutex);
auto itr = m_AuthedLinks.begin();
while(itr != m_AuthedLinks.end())
{
@ -51,7 +51,7 @@ namespace llarp
}
}
{
util::Lock lock(m_PendingMutex);
Lock lock(m_PendingMutex);
auto itr = m_Pending.begin();
while(itr != m_Pending.end())
@ -70,8 +70,8 @@ namespace llarp
void
ILinkLayer::MapAddr(const PubKey& pk, ILinkSession* s)
{
util::Lock l_authed(m_AuthedLinksMutex);
util::Lock l_pending(m_PendingMutex);
Lock l_authed(m_AuthedLinksMutex);
Lock l_pending(m_PendingMutex);
auto itr = m_Pending.begin();
while(itr != m_Pending.end())
{
@ -111,7 +111,7 @@ namespace llarp
llarp::Addr addr(to);
auto s = NewOutboundSession(rc, to);
s->Start();
PutSession(addr, s);
PutSession(s);
}
bool
@ -132,7 +132,7 @@ namespace llarp
void
ILinkLayer::CloseSessionTo(const PubKey& remote)
{
util::Lock l(m_AuthedLinksMutex);
Lock l(m_AuthedLinksMutex);
auto range = m_AuthedLinks.equal_range(remote);
auto itr = range.first;
while(itr != range.second)
@ -145,7 +145,7 @@ namespace llarp
void
ILinkLayer::KeepAliveSessionTo(const PubKey& remote)
{
util::Lock l(m_AuthedLinksMutex);
Lock l(m_AuthedLinksMutex);
auto range = m_AuthedLinks.equal_range(remote);
auto itr = range.first;
while(itr != range.second)
@ -158,17 +158,23 @@ namespace llarp
bool
ILinkLayer::SendTo(const PubKey& remote, llarp_buffer_t buf)
{
util::Lock l(m_AuthedLinksMutex);
Lock l(m_AuthedLinksMutex);
auto range = m_AuthedLinks.equal_range(remote);
auto itr = range.first;
// TODO: random selection
// pick lowest backlog session
size_t min = std::numeric_limits< size_t >::max();
ILinkSession* s = nullptr;
while(itr != range.second)
{
if(itr->second->SendMessageBuffer(buf))
return true;
auto backlog = itr->second->SendQueueBacklog();
if(backlog < min)
{
s = itr->second.get();
min = backlog;
}
++itr;
}
return false;
return s && s->SendMessageBuffer(buf);
}
bool
@ -218,9 +224,9 @@ namespace llarp
}
void
ILinkLayer::PutSession(const Addr& addr, ILinkSession* s)
ILinkLayer::PutSession(ILinkSession* s)
{
util::Lock lock(m_PendingMutex);
Lock lock(m_PendingMutex);
m_Pending.emplace_back(s);
}

@ -31,6 +31,11 @@ namespace llarp
FragmentOverheadSize + FragmentBodySize;
typedef llarp::AlignedBuffer< FragmentBufferSize > FragmentBuffer;
constexpr size_t MaxSend = 64;
/// maximum size for send queue for a session before we drop
constexpr size_t MaxSendQueueSize = 128;
typedef llarp::AlignedBuffer< MAX_LINK_MSG_SIZE > MessageBuffer;
struct LinkLayer;
@ -47,8 +52,8 @@ namespace llarp
llarp_time_t lastActive;
const static llarp_time_t sessionTimeout = 30 * 1000;
std::deque< utp_iovec > vecq;
std::deque< FragmentBuffer > sendq;
size_t sendBufOffset;
FragmentBuffer recvBuf;
size_t recvBufOffset;
@ -60,14 +65,14 @@ namespace llarp
Alive();
/// base
BaseSession(llarp_router* r);
BaseSession(LinkLayer* p);
/// outbound
BaseSession(llarp_router* r, utp_socket* s, const RouterContact& rc,
BaseSession(LinkLayer* p, utp_socket* s, const RouterContact& rc,
const AddressInfo& addr);
/// inbound
BaseSession(llarp_router* r, utp_socket* s, const Addr& remote);
BaseSession(LinkLayer* p, utp_socket* s, const Addr& remote);
enum State
{
@ -96,16 +101,46 @@ namespace llarp
EnterState(State st);
BaseSession();
virtual ~BaseSession();
~BaseSession();
void
PumpWrite()
{
if(!sock)
return;
ssize_t expect = 0;
std::vector< utp_iovec > vecs;
for(const auto& vec : vecq)
{
expect += vec.iov_len;
vecs.push_back(vec);
}
if(expect)
{
ssize_t s = utp_writev(sock, vecs.data(), vecs.size());
llarp::LogDebug("utp_writev wrote=", s, " expect=", expect,
" to=", remoteAddr);
while(s > vecq.front().iov_len)
{
s -= vecq.front().iov_len;
vecq.pop_front();
sendq.pop_front();
}
if(vecq.size())
{
auto& front = vecq.front();
front.iov_len -= s;
front.iov_base = ((byte_t*)front.iov_base) + s;
}
}
/*
while(sendq.size() > 0 && !stalled)
{
ssize_t expect = FragmentBufferSize - sendBufOffset;
ssize_t s = write_ll(sendq.front().data() + sendBufOffset, expect);
if(s != expect)
ssize_t s = write_ll(sendq.front().data() + sendBufOffset,
expect); if(s != expect)
{
llarp::LogDebug("stalled at offset=", sendBufOffset, " sz=", s,
" to ", remoteAddr);
@ -118,6 +153,7 @@ namespace llarp
sendq.pop_front();
}
}
*/
}
ssize_t
@ -142,15 +178,10 @@ namespace llarp
bool
QueueWriteBuffers(llarp_buffer_t buf)
{
llarp::LogDebug("write ", buf.sz, " bytes to ", remoteAddr);
if(state != eSessionReady)
{
llarp::LogWarn("failed to send ", buf.sz,
" bytes on non ready session state=", state);
if(sendq.size() >= MaxSendQueueSize)
return false;
}
else
lastActive = llarp_time_now_ms();
llarp::LogDebug("write ", buf.sz, " bytes to ", remoteAddr);
lastActive = llarp_time_now_ms();
size_t sz = buf.sz;
byte_t* ptr = buf.base;
while(sz)
@ -174,59 +205,36 @@ namespace llarp
OutboundLinkEstablished(LinkLayer* p)
{
OnLinkEstablished(p);
KeyExchangeNonce nonce;
nonce.Randomize();
gotLIM = true;
if(DoKeyExchange(Router()->crypto.transport_dh_client, nonce,
remoteTransportPubKey, Router()->encryption))
{
SendHandshake(nonce);
EnterState(eSessionReady);
SendKeepAlive();
}
OutboundHandshake();
}
// send our RC to the remote
// send first message
void
SendHandshake(const KeyExchangeNonce& n)
{
FragmentBuffer tmp;
auto buf = InitBuffer(tmp.data(), tmp.size());
// fastforward buffer for handshake to fit before
buf.cur += sizeof(uint32_t) * 2;
byte_t* begin = buf.cur;
LinkIntroMessage msg;
msg.rc = Router()->rc;
msg.N = n;
if(!msg.BEncode(&buf))
{
llarp::LogError("failed to encode our RC for handshake");
Close();
return;
}
uint32_t sz = buf.cur - begin;
llarp::LogDebug("handshake is of size ", sz, " bytes");
// write handshake header
buf.cur = buf.base;
llarp_buffer_put_uint32(&buf, LLARP_PROTO_VERSION);
llarp_buffer_put_uint32(&buf, sz);
// send it
write_ll(tmp.data(), sz + (sizeof(uint32_t) * 2));
}
OutboundHandshake();
// mix keys
bool
DoKeyExchange(llarp_transport_dh_func dh, const KeyExchangeNonce& n,
const PubKey& other, const SecretKey& secret)
{
PubKey us = llarp::seckey_topublic(secret);
llarp::LogDebug("DH us=", us, " them=", other, " n=", n);
if(!dh(sessionKey, other, secret, n))
ShortHash t_h;
AlignedBuffer< 64 > tmp;
memcpy(tmp.data(), sessionKey, sessionKey.size());
memcpy(tmp.data() + sessionKey.size(), n, n.size());
// t_h = HS(K + L.n)
if(!Router()->crypto.shorthash(t_h, ConstBuffer(tmp)))
{
llarp::LogError("failed to mix key to ", remoteAddr);
return false;
}
// K = TKE(a.p, B_a.e, sk, t_h)
if(!dh(sessionKey, other, secret, t_h))
{
llarp::LogError("key exchange with ", other, " failed");
Close();
return false;
}
llarp::LogDebug("keys mixed with session to ", remoteAddr);
return true;
}
@ -238,19 +246,11 @@ namespace llarp
void
Close();
bool
RecvHandshake(const void* buf, size_t bufsz, LinkLayer* p);
bool
Recv(const void* buf, size_t sz)
{
if(state != eSessionReady)
{
llarp::LogWarn("session not ready via ", remoteAddr);
return false;
}
Alive();
byte_t* ptr = (const byte_t*)buf;
byte_t* ptr = (byte_t*)buf;
llarp::LogDebug("utp read ", sz, " from ", remoteAddr);
size_t s = sz;
// process leftovers
@ -290,9 +290,17 @@ namespace llarp
return true;
}
bool
InboundLIM(const LinkIntroMessage* msg);
bool
OutboundLIM(const LinkIntroMessage* msg);
bool
IsTimedOut(llarp_time_t now) const
{
if(state == eClose)
return true;
if(now < lastActive)
return false;
auto dlt = now - lastActive;
@ -346,7 +354,18 @@ namespace llarp
static uint64
OnError(utp_callback_arguments* arg)
{
llarp::LogError(utp_error_code_names[arg->error_code]);
BaseSession* session =
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
if(session)
{
if(arg->error_code == UTP_ETIMEDOUT)
{
session->Router()->OnConnectTimeout(session->GetPubKey());
}
llarp::LogError(utp_error_code_names[arg->error_code], " via ",
session->remoteAddr);
session->Close();
}
return 0;
}
@ -481,7 +500,28 @@ namespace llarp
#ifdef __linux__
ProcessICMP();
#endif
std::set< PubKey > 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.find(pk) == m_AuthedLinks.end())
{
// all sessions were removed
router->SessionClosed(pk);
}
}
}
}
void
Stop()
@ -527,13 +567,16 @@ namespace llarp
return std::unique_ptr< LinkLayer >(new LinkLayer(r));
}
BaseSession::BaseSession(llarp_router* r)
BaseSession::BaseSession(LinkLayer* p)
{
parent = p;
remoteTransportPubKey.Zero();
parent = nullptr;
recvMsgOffset = 0;
SendQueueBacklog = [&]() -> size_t { return sendq.size(); };
SendKeepAlive = [&]() -> bool {
if(false && sendq.size() == 0)
if(sendq.size() == 0 && state == eSessionReady)
{
DiscardMessage msg;
byte_t tmp[128] = {0};
@ -547,14 +590,15 @@ namespace llarp
}
return true;
};
sendBufOffset = 0;
gotLIM = false;
recvBufOffset = 0;
TimedOut = [&](llarp_time_t now) -> bool {
return this->IsTimedOut(now) || this->state == eClose;
};
GetPubKey = std::bind(&BaseSession::RemotePubKey, this);
lastActive = llarp_time_now_ms();
Pump = std::bind(&BaseSession::PumpWrite, this);
// Pump = []() {};
Pump = std::bind(&BaseSession::PumpWrite, this);
Tick = std::bind(&BaseSession::TickImpl, this, std::placeholders::_1);
SendMessageBuffer = std::bind(&BaseSession::QueueWriteBuffers, this,
std::placeholders::_1);
@ -567,9 +611,9 @@ namespace llarp
GetRemoteEndpoint = std::bind(&BaseSession::RemoteEndpoint, this);
}
BaseSession::BaseSession(llarp_router* r, utp_socket* s,
BaseSession::BaseSession(LinkLayer* p, utp_socket* s,
const RouterContact& rc, const AddressInfo& addr)
: BaseSession(r)
: BaseSession(p)
{
remoteRC.Clear();
remoteTransportPubKey = addr.pubkey;
@ -579,17 +623,107 @@ namespace llarp
assert(s == sock);
remoteAddr = addr;
Start = std::bind(&BaseSession::Connect, this);
GotLIM =
std::bind(&BaseSession::OutboundLIM, this, std::placeholders::_1);
}
BaseSession::BaseSession(llarp_router* r, utp_socket* s, const Addr& addr)
: BaseSession(r)
BaseSession::BaseSession(LinkLayer* p, utp_socket* s, const Addr& addr)
: BaseSession(p)
{
p->router->crypto.shorthash(sessionKey,
InitBuffer(p->router->pubkey(), PUBKEYSIZE));
remoteRC.Clear();
sock = s;
assert(s == sock);
assert(utp_set_userdata(sock, this) == this);
remoteAddr = addr;
Start = []() {};
GotLIM = std::bind(&BaseSession::InboundLIM, this, std::placeholders::_1);
}
bool
BaseSession::InboundLIM(const LinkIntroMessage* msg)
{
if(gotLIM && remoteRC.pubkey != msg->rc.pubkey)
{
return false;
}
remoteRC = msg->rc;
gotLIM = true;
if(!DoKeyExchange(Router()->crypto.transport_dh_server, msg->N,
remoteRC.enckey, parent->TransportSecretKey()))
return false;
EnterState(eSessionReady);
return true;
}
bool
BaseSession::OutboundLIM(const LinkIntroMessage* msg)
{
if(gotLIM && remoteRC.pubkey != msg->rc.pubkey)
{
return false;
}
remoteRC = msg->rc;
gotLIM = true;
// TODO: update address info pubkey
return DoKeyExchange(Router()->crypto.transport_dh_client, msg->N,
remoteTransportPubKey, Router()->encryption);
}
void
BaseSession::OutboundHandshake()
{
// set session key
Router()->crypto.shorthash(sessionKey, ConstBuffer(remoteRC.pubkey));
byte_t tmp[LinkIntroMessage::MaxSize];
auto buf = StackBuffer< decltype(tmp) >(tmp);
// build our RC
LinkIntroMessage msg;
msg.rc = Router()->rc();
if(!msg.rc.VerifySignature(&Router()->crypto))
{
llarp::LogError("our RC is invalid? closing session to", remoteAddr);
Close();
return;
}
msg.N.Randomize();
msg.P = DefaultLinkSessionLifetime;
if(!msg.Sign(&Router()->crypto, Router()->identity))
{
llarp::LogError("failed to sign LIM for outbound handshake to ",
remoteAddr);
Close();
return;
}
// encode
if(!msg.BEncode(&buf))
{
llarp::LogError("failed to encode LIM for handshake to ", remoteAddr);
Close();
return;
}
// rewind
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// send
if(!SendMessageBuffer(buf))
{
llarp::LogError("failed to send handshake to ", remoteAddr);
Close();
return;
}
// mix keys
if(!DoKeyExchange(Router()->crypto.transport_dh_client, msg.N,
remoteTransportPubKey, Router()->encryption))
{
llarp::LogError("failed to mix keys for outbound session to ",
remoteAddr);
Close();
return;
}
EnterState(eSessionReady);
}
llarp_router*
@ -611,14 +745,12 @@ namespace llarp
LinkLayer::NewOutboundSession(const RouterContact& rc,
const AddressInfo& addr)
{
return new BaseSession(router, utp_create_socket(_utp_ctx), rc, addr);
return new BaseSession(this, utp_create_socket(_utp_ctx), rc, addr);
}
uint64
LinkLayer::OnRead(utp_callback_arguments* arg)
{
LinkLayer* parent =
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
BaseSession* self =
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
@ -628,30 +760,18 @@ namespace llarp
{
return 0;
}
else if(self->state == BaseSession::eSessionReady)
if(!self->Recv(arg->buf, arg->len))
{
if(!self->Recv(arg->buf, arg->len))
{
llarp::LogDebug("recv fail for ", self->remoteAddr);
self->Close();
return 0;
}
utp_read_drained(arg->socket);
}
else if(self->state == BaseSession::eLinkEstablished)
{
if(!self->RecvHandshake(arg->buf, arg->len, parent))
{
llarp::LogDebug("recv handshake failed for ", self->remoteAddr);
self->Close();
return 0;
}
utp_read_drained(arg->socket);
llarp::LogDebug("recv fail for ", self->remoteAddr);
self->Close();
return 0;
}
utp_read_drained(arg->socket);
}
else
{
llarp::LogWarn("utp_socket got data with no underlying session");
utp_close(arg->socket);
}
return 0;
}
@ -675,12 +795,7 @@ namespace llarp
}
else if(arg->state == UTP_STATE_WRITABLE)
{
if(session->IsEstablished())
{
llarp::LogDebug("write resumed for ", session->remoteAddr);
session->stalled = false;
session->PumpWrite();
}
session->PumpWrite();
}
else if(arg->state == UTP_STATE_EOF)
{
@ -698,8 +813,8 @@ namespace llarp
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
Addr remote(*arg->address);
llarp::LogDebug("utp accepted from ", remote);
BaseSession* session = new BaseSession(self->router, arg->socket, remote);
self->PutSession(remote, session);
BaseSession* session = new BaseSession(self, arg->socket, remote);
self->PutSession(session);
session->OnLinkEstablished(self);
return 0;
}
@ -709,12 +824,12 @@ namespace llarp
bool isLastFragment)
{
if(state != eSessionReady)
{
llarp::LogWarn("tried to send to non ready session on ", remoteAddr);
return;
}
FragmentBuffer buf;
sendq.emplace_back();
auto& buf = sendq.back();
vecq.emplace_back();
auto& vec = vecq.back();
vec.iov_base = buf.data();
vec.iov_len = FragmentBufferSize;
llarp::LogDebug("encrypt then hash ", sz, " bytes last=", isLastFragment);
buf.Randomize();
byte_t* nonce = buf.data() + FragmentHashSize;
@ -740,8 +855,6 @@ namespace llarp
payload.sz = FragmentBufferSize - FragmentHashSize;
// key'd hash
Router()->crypto.hmac(buf.data(), payload, sessionKey);
// push back
sendq.push_back(std::move(buf));
}
void
@ -811,83 +924,6 @@ namespace llarp
return true;
}
bool
BaseSession::RecvHandshake(const void* buf, size_t bufsz, LinkLayer* p)
{
size_t sz = bufsz;
parent = p;
llarp::LogDebug("recv handshake ", sz, " from ", remoteAddr);
if(sz <= 8)
{
llarp::LogDebug("handshake too small from ", remoteAddr);
Close();
return false;
}
// process handshake header
byte_t* ptr = (byte_t*)buf;
uint32_t version = bufbe32toh(ptr);
if(version != LLARP_PROTO_VERSION)
{
llarp::LogWarn("protocol version missmatch ", version,
" != ", LLARP_PROTO_VERSION);
Close();
return false;
}
ptr += sizeof(uint32_t);
sz -= sizeof(uint32_t);
uint32_t limsz = bufbe32toh(ptr);
ptr += sizeof(uint32_t);
sz -= sizeof(uint32_t);
if(limsz > sz)
{
// not enough data
// TODO: don't bail here, continue reading
llarp::LogDebug("not enough data for handshake, want ", limsz,
" bytes but got ", sz);
Close();
return false;
}
llarp::LogDebug("from ", bufsz, " bytes reading ", limsz, " of ", sz,
" bytes");
// process LIM
auto mbuf = InitBuffer(ptr, limsz);
LinkIntroMessage msg(this);
if(!msg.BDecode(&mbuf))
{
llarp::LogError("Failed to parse LIM from ", remoteAddr);
llarp::DumpBuffer(mbuf);
Close();
return false;
}
if(!msg.HandleMessage(Router()))
{
llarp::LogError("failed to verify signature of rc");
return false;
}
gotLIM = true;
sz -= limsz;
remoteRC = msg.rc;
if(!DoKeyExchange(Router()->crypto.transport_dh_server, msg.N,
remoteRC.enckey, parent->TransportSecretKey()))
return false;
EnterState(eSessionReady);
if(sz)
{
llarp::LogDebug("got ", sz, " leftover from handshake from ",
remoteAddr);
return Recv(ptr + limsz, sz);
}
else
{
llarp::LogDebug("no leftovers in handshake from ", remoteAddr);
}
return true;
}
void
BaseSession::Close()
{

@ -29,6 +29,10 @@ namespace llarp
llarp::LogWarn("failed to decode nonce in LIM");
return false;
}
if(llarp_buffer_eq(key, "p"))
{
return bencode_read_integer(buf, &P);
}
if(llarp_buffer_eq(key, "r"))
{
if(rc.BDecode(buf))
@ -50,6 +54,10 @@ namespace llarp
llarp::LogDebug("LIM version ", version);
return true;
}
else if(llarp_buffer_eq(key, "z"))
{
return Z.BDecode(buf);
}
else
{
llarp::LogWarn("invalid LIM key: ", *key.cur);
@ -73,6 +81,11 @@ namespace llarp
if(!N.BEncode(buf))
return false;
if(!bencode_write_bytestring(buf, "p", 1))
return false;
if(!bencode_write_uint64(buf, P))
return false;
if(!bencode_write_bytestring(buf, "r", 1))
return false;
if(!rc.BEncode(buf))
@ -81,12 +94,71 @@ namespace llarp
if(!bencode_write_version_entry(buf))
return false;
if(!bencode_write_bytestring(buf, "z", 1))
return false;
if(!Z.BEncode(buf))
return false;
return bencode_end(buf);
}
LinkIntroMessage&
LinkIntroMessage::operator=(const LinkIntroMessage& msg)
{
version = msg.version;
Z = msg.Z;
rc = msg.rc;
N = msg.N;
P = msg.P;
return *this;
}
bool
LinkIntroMessage::HandleMessage(llarp_router* router) const
{
return rc.VerifySignature(&router->crypto);
if(!Verify(&router->crypto))
return false;
return session->GotLIM(this);
}
bool
LinkIntroMessage::Sign(llarp_crypto* c, const SecretKey& k)
{
Z.Zero();
byte_t tmp[MaxSize] = {0};
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
if(!BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
return c->sign(Z, k, buf);
}
bool
LinkIntroMessage::Verify(llarp_crypto* c) const
{
LinkIntroMessage copy;
copy = *this;
copy.Z.Zero();
byte_t tmp[MaxSize] = {0};
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
if(!copy.BEncode(&buf))
return false;
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// outer signature
if(!c->verify(rc.pubkey, buf, Z))
{
llarp::LogError("outer signature failure");
return false;
}
// rc signature
if(!rc.VerifySignature(c))
{
llarp::LogError("inner signature failure");
return false;
}
return true;
}
} // namespace llarp

@ -27,6 +27,18 @@ struct llarp_nodedb
entries;
fs::path nodePath;
bool
Remove(const llarp::PubKey &pk)
{
llarp::util::Lock lock(access);
auto itr = entries.find(pk);
if(itr == entries.end())
return false;
entries.erase(itr);
fs::remove(fs::path(getRCFilePath(pk)));
return true;
}
void
Clear()
{
@ -139,7 +151,6 @@ struct llarp_nodedb
if(fpath.extension() != RC_FILE_EXT)
return false;
llarp::RouterContact rc;
if(!rc.Read(fpath.string().c_str()))
{
llarp::LogError("failed to read file ", fpath);
@ -157,6 +168,19 @@ struct llarp_nodedb
return true;
}
void
visit(std::function< bool(const llarp::RouterContact &) > visit)
{
llarp::util::Lock lock(access);
auto itr = entries.begin();
while(itr != entries.end())
{
if(!visit(itr->second))
return;
++itr;
}
}
bool
iterate(struct llarp_nodedb_iter i)
{
@ -339,6 +363,14 @@ llarp_nodedb_iterate_all(struct llarp_nodedb *n, struct llarp_nodedb_iter i)
return n->entries.size();
}
void
llarp_nodedb_visit_loaded(
struct llarp_nodedb *n,
std::function< bool(const llarp::RouterContact &) > visit)
{
return n->visit(visit);
}
/// maybe rename to verify_and_set
void
llarp_nodedb_async_verify(struct llarp_async_verify_rc *job)
@ -373,6 +405,12 @@ llarp_nodedb_num_loaded(struct llarp_nodedb *n)
return n->entries.size();
}
bool
llarp_nodedb_del_rc(struct llarp_nodedb *n, const llarp::RouterID &pk)
{
return n->Remove(pk);
}
bool
llarp_nodedb_select_random_hop(struct llarp_nodedb *n,
const llarp::RouterContact &prev,

@ -3,6 +3,7 @@
#include <llarp/path.hpp>
#include <llarp/pathbuilder.hpp>
#include <llarp/messages/dht.hpp>
#include <llarp/messages/discard.hpp>
#include "buffer.hpp"
#include "router.hpp"
@ -341,6 +342,7 @@ namespace llarp
intro.router = hops[hsz - 1].rc.pubkey;
// TODO: or is it rxid ?
intro.pathID = hops[hsz - 1].txID;
EnterState(ePathBuilding);
}
void
@ -370,7 +372,7 @@ namespace llarp
bool
Path::IsReady() const
{
return intro.latency > 0 && status == ePathEstablished;
return intro.latency > 0 && _status == ePathEstablished;
}
RouterID
@ -379,11 +381,40 @@ namespace llarp
return hops[0].rc.pubkey;
}
void
Path::EnterState(PathStatus st)
{
if(st == ePathTimeout)
{
llarp::LogInfo("path ", Name(), " has timed out");
}
else if(st == ePathBuilding)
{
llarp::LogInfo("path ", Name(), " is building");
buildStarted = llarp_time_now_ms();
}
_status = st;
}
void
Path::Tick(llarp_time_t now, llarp_router* r)
{
if(Expired(now))
return;
if(_status == ePathBuilding)
{
if(now < buildStarted)
return;
auto dlt = now - buildStarted;
if(dlt >= PATH_BUILD_TIMEOUT)
{
r->routerProfiling.MarkPathFail(this);
EnterState(ePathTimeout);
return;
}
}
if(now < m_LastLatencyTestTime)
return;
auto dlt = now - m_LastLatencyTestTime;
@ -395,6 +426,23 @@ namespace llarp
m_LastLatencyTestTime = now;
SendRoutingMessage(&latency, r);
}
// check to see if this path is dead
if(_status == ePathEstablished)
{
if(m_CheckForDead)
{
if(m_CheckForDead(this, dlt))
{
r->routerProfiling.MarkPathFail(this);
EnterState(ePathTimeout);
}
}
else if(dlt >= 10000)
{
r->routerProfiling.MarkPathFail(this);
EnterState(ePathTimeout);
}
}
}
bool
@ -420,14 +468,22 @@ namespace llarp
bool
Path::Expired(llarp_time_t now) const
{
if(status == ePathEstablished)
if(_status == ePathEstablished)
return now - buildStarted > hops[0].lifetime;
else if(status == ePathBuilding)
return now - buildStarted > PATH_BUILD_TIMEOUT;
else if(_status == ePathBuilding)
return false;
else
return true;
}
std::string
Path::Name() const
{
std::stringstream ss;
ss << "TX=" << TXID() << " RX=" << RXID();
return ss.str();
}
bool
Path::HandleDownstream(llarp_buffer_t buf, const TunnelNonce& Y,
llarp_router* r)
@ -488,21 +544,33 @@ namespace llarp
return false;
}
bool
Path::HandleDataDiscardMessage(
const llarp::routing::DataDiscardMessage* msg, llarp_router* r)
{
if(m_DropHandler)
return m_DropHandler(this, msg->P, msg->S);
return true;
}
bool
Path::HandlePathConfirmMessage(
const llarp::routing::PathConfirmMessage* msg, llarp_router* r)
{
if(status == ePathBuilding)
if(_status == ePathBuilding)
{
// finish initializing introduction
intro.expiresAt = buildStarted + hops[0].lifetime;
// confirm that we build the path
status = ePathEstablished;
llarp::LogInfo("path is confirmed tx=", TXID(), " rx=", RXID());
EnterState(ePathEstablished);
llarp::LogInfo("path is confirmed tx=", TXID(), " rx=", RXID(),
" took ", llarp_time_now_ms() - buildStarted, " ms");
if(m_BuiltHook)
m_BuiltHook(this);
m_BuiltHook = nullptr;
r->routerProfiling.MarkPathSuccess(this);
// persist session with upstream router until the path is done
r->PersistSessionUntil(Upstream(), intro.expiresAt);
@ -522,7 +590,7 @@ namespace llarp
Path::HandleHiddenServiceFrame(const llarp::service::ProtocolFrame* frame)
{
if(m_DataHandler)
return m_DataHandler(frame);
return m_DataHandler(this, frame);
return false;
}
@ -530,11 +598,12 @@ namespace llarp
Path::HandlePathLatencyMessage(
const llarp::routing::PathLatencyMessage* msg, llarp_router* r)
{
if(msg->L == m_LastLatencyTestID && status == ePathEstablished)
// TODO: reanimate dead paths if they get this message
if(msg->L == m_LastLatencyTestID && _status == ePathEstablished)
{
intro.latency = llarp_time_now_ms() - m_LastLatencyTestTime;
llarp::LogInfo("path latency is ", intro.latency, " ms for tx=", TXID(),
" rx=", RXID());
llarp::LogDebug("path latency is ", intro.latency,
" ms for tx=", TXID(), " rx=", RXID());
m_LastLatencyTestID = 0;
return true;
}

@ -48,7 +48,7 @@ namespace llarp
hop.nonce))
{
llarp::LogError("Failed to generate shared key for path build");
abort();
delete ctx;
return;
}
// generate nonceXOR valueself->hop->pathKey
@ -82,6 +82,7 @@ namespace llarp
{
// failed to encode?
llarp::LogError("Failed to generate Commit Record");
delete ctx;
return;
}
// use ephameral keypair for frame
@ -90,6 +91,7 @@ namespace llarp
if(!frame.EncryptInPlace(framekey, hop.rc.enckey, ctx->crypto))
{
llarp::LogError("Failed to encrypt LRCR");
delete ctx;
return;
}
@ -137,15 +139,15 @@ namespace llarp
if(!router->SendToOrQueue(remote, ctx->LRCM))
{
llarp::LogError("failed to send LRCM");
delete ctx;
return;
}
ctx->path->status = llarp::path::ePathBuilding;
ctx->path->buildStarted = llarp_time_now_ms();
// persist session with router until this path is done
router->PersistSessionUntil(remote, ctx->path->ExpireTime());
// add own path
router->paths.AddOwnPath(ctx->pathset, ctx->path);
delete ctx;
}
namespace path

@ -22,10 +22,7 @@ namespace llarp
{
for(auto& item : m_Paths)
{
if(item.second->status == ePathEstablished)
{
item.second->Tick(now, r);
}
item.second->Tick(now, r);
}
}
@ -67,20 +64,48 @@ namespace llarp
return path;
}
Path*
PathSet::GetNewestPathByRouter(const RouterID& id) const
{
Path* chosen = nullptr;
auto itr = m_Paths.begin();
while(itr != m_Paths.end())
{
if(itr->second->IsReady())
{
if(itr->second->Endpoint() == id)
{
if(chosen == nullptr)
chosen = itr->second;
else if(chosen->intro.expiresAt < itr->second->intro.expiresAt)
chosen = itr->second;
}
}
++itr;
}
return chosen;
}
Path*
PathSet::GetPathByRouter(const RouterID& id) const
{
auto itr = m_Paths.begin();
Path* chosen = nullptr;
auto itr = m_Paths.begin();
while(itr != m_Paths.end())
{
if(itr->second->IsReady())
{
if(itr->second->Endpoint() == id)
return itr->second;
{
if(chosen == nullptr)
chosen = itr->second;
else if(chosen->intro.latency > itr->second->intro.latency)
chosen = itr->second;
}
}
++itr;
}
return nullptr;
return chosen;
}
Path*
@ -103,7 +128,7 @@ namespace llarp
auto itr = m_Paths.begin();
while(itr != m_Paths.end())
{
if(itr->second->status == st)
if(itr->second->_status == st)
++count;
++itr;
}

@ -0,0 +1,161 @@
#include <llarp/profiling.hpp>
#include <fstream>
namespace llarp
{
bool
RouterProfile::BEncode(llarp_buffer_t* buf) const
{
if(!bencode_start_dict(buf))
return false;
if(!BEncodeWriteDictInt("g", connectGoodCount, buf))
return false;
if(!BEncodeWriteDictInt("p", pathSuccessCount, buf))
return false;
if(!BEncodeWriteDictInt("s", pathFailCount, buf))
return false;
if(!BEncodeWriteDictInt("t", connectTimeoutCount, buf))
return false;
if(!BEncodeWriteDictInt("v", version, buf))
return false;
return bencode_end(buf);
}
bool
RouterProfile::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf)
{
bool read = false;
if(!BEncodeMaybeReadDictInt("g", connectGoodCount, read, k, buf))
return false;
if(!BEncodeMaybeReadDictInt("t", connectTimeoutCount, read, k, buf))
return false;
if(!BEncodeMaybeReadDictInt("v", version, read, k, buf))
return false;
if(!BEncodeMaybeReadDictInt("s", pathFailCount, read, k, buf))
return false;
if(!BEncodeMaybeReadDictInt("p", pathSuccessCount, read, k, buf))
return false;
return read;
}
bool
RouterProfile::IsGood(uint64_t chances) const
{
return connectTimeoutCount <= connectGoodCount
/// 4 hops + N chances
&& (pathSuccessCount * 4 * chances) >= (pathFailCount / chances);
}
bool
Profiling::IsBad(const RouterID& r, uint64_t chances)
{
lock_t lock(m_ProfilesMutex);
auto itr = m_Profiles.find(r);
if(itr == m_Profiles.end())
return false;
return !itr->second.IsGood(chances);
}
void
Profiling::MarkTimeout(const RouterID& r)
{
lock_t lock(m_ProfilesMutex);
m_Profiles[r].connectTimeoutCount += 1;
}
void
Profiling::MarkSuccess(const RouterID& r)
{
lock_t lock(m_ProfilesMutex);
m_Profiles[r].connectGoodCount += 1;
}
void
Profiling::MarkPathFail(path::Path* p)
{
lock_t lock(m_ProfilesMutex);
for(const auto& hop : p->hops)
{
// TODO: also mark bad?
m_Profiles[hop.rc.pubkey].pathFailCount += 1;
}
}
void
Profiling::MarkPathSuccess(path::Path* p)
{
lock_t lock(m_ProfilesMutex);
for(const auto& hop : p->hops)
{
m_Profiles[hop.rc.pubkey].pathSuccessCount += 1;
}
}
bool
Profiling::Save(const char* fname)
{
lock_t lock(m_ProfilesMutex);
size_t sz = (m_Profiles.size() * (RouterProfile::MaxSize + 32 + 8)) + 8;
byte_t* tmp = new byte_t[sz];
auto buf = llarp::InitBuffer(tmp, sz);
auto res = BEncode(&buf);
if(res)
{
buf.sz = buf.cur - buf.base;
std::ofstream f;
f.open(fname);
if(f.is_open())
{
f.write((char*)buf.base, buf.sz);
}
}
delete[] tmp;
return res;
}
bool
Profiling::BEncode(llarp_buffer_t* buf) const
{
if(!bencode_start_dict(buf))
return false;
auto itr = m_Profiles.begin();
while(itr != m_Profiles.end())
{
if(!itr->first.BEncode(buf))
return false;
if(!itr->second.BEncode(buf))
return false;
++itr;
}
return bencode_end(buf);
}
bool
Profiling::DecodeKey(llarp_buffer_t k, llarp_buffer_t* buf)
{
if(k.sz != 32)
return false;
RouterProfile profile;
if(!profile.BDecode(buf))
return false;
RouterID pk = k.base;
return m_Profiles.insert(std::make_pair(pk, profile)).second;
}
bool
Profiling::Load(const char* fname)
{
lock_t lock(m_ProfilesMutex);
m_Profiles.clear();
if(!BDecodeReadFile(fname, *this))
{
llarp::LogWarn("failed to load router profiles from ", fname);
return false;
}
return true;
}
} // namespace llarp

@ -119,6 +119,7 @@ namespace llarp
{
return path->HandleDownstream(X.Buffer(), Y, r);
}
llarp::LogWarn("unhandled downstream message");
return false;
}
} // namespace llarp

@ -52,12 +52,22 @@ struct TryConnectJob
void
AttemptTimedout()
{
router->routerProfiling.MarkTimeout(rc.pubkey);
if(ShouldRetry())
{
Attempt();
return;
}
if(router->routerProfiling.IsBad(rc.pubkey))
llarp_nodedb_del_rc(router->nodedb, rc.pubkey);
// delete this
router->pendingEstablishJobs.erase(rc.pubkey);
}
void
Attempt()
{
--triesLeft;
link->TryEstablishTo(rc);
}
@ -93,7 +103,7 @@ llarp_router_try_connect(struct llarp_router *router,
std::make_unique< TryConnectJob >(remote, link, numretries, router)));
TryConnectJob *job = itr.first->second.get();
// try establishing async
job->Attempt();
llarp_logic_queue_job(router->logic, {job, &on_try_connecting});
return true;
}
@ -142,6 +152,8 @@ llarp_router::PersistSessionUntil(const llarp::RouterID &remote,
}
}
constexpr size_t MaxPendingSendQueueSize = 8;
bool
llarp_router::SendToOrQueue(const llarp::RouterID &remote,
const llarp::ILinkMessage *msg)
@ -171,15 +183,24 @@ llarp_router::SendToOrQueue(const llarp::RouterID &remote,
return false;
// queue buffer
auto &q = outboundMessageQueue[remote];
buf.sz = buf.cur - buf.base;
if(q.size() >= MaxPendingSendQueueSize)
{
llarp::LogWarn("tried to queue a message to ", remote,
" but the queue is full so we drop it like it's hawt");
return false;
}
buf.sz = buf.cur - buf.base;
q.emplace(buf.sz);
memcpy(q.back().data(), buf.base, buf.sz);
llarp::RouterContact remoteRC;
// we don't have an open session to that router right now
if(llarp_nodedb_get_rc(nodedb, remote, rc))
if(llarp_nodedb_get_rc(nodedb, remote, remoteRC))
{
// try connecting directly as the rc is loaded from disk
llarp_router_try_connect(this, rc, 10);
llarp_router_try_connect(this, remoteRC, 10);
return true;
}
@ -198,6 +219,7 @@ llarp_router::HandleDHTLookupForSendTo(
{
llarp_nodedb_put_rc(nodedb, results[0]);
llarp_router_try_connect(this, results[0], 10);
async_verify_RC(results[0]);
}
else
{
@ -264,13 +286,13 @@ bool
llarp_router::SaveRC()
{
llarp::LogDebug("verify RC signature");
if(!rc.VerifySignature(&crypto))
if(!rc().VerifySignature(&crypto))
{
rc.Dump< MAX_RC_SIZE >();
rc().Dump< MAX_RC_SIZE >();
llarp::LogError("RC has bad signature not saving");
return false;
}
return rc.Write(our_rc_file.string().c_str());
return rc().Write(our_rc_file.string().c_str());
}
void
@ -297,6 +319,9 @@ llarp_router::on_verify_client_rc(llarp_async_verify_rc *job)
llarp::async_verify_context *ctx =
static_cast< llarp::async_verify_context * >(job->user);
ctx->router->pendingEstablishJobs.erase(job->rc.pubkey);
auto router = ctx->router;
llarp::PubKey pk(job->rc.pubkey);
router->FlushOutboundFor(pk, router->GetLinkWithSessionByPubkey(pk));
delete ctx;
}
@ -334,6 +359,9 @@ llarp_router::on_verify_server_rc(llarp_async_verify_rc *job)
// track valid router in dht
router->dht->impl.nodes->PutNode(rc);
// mark success in profile
router->routerProfiling.MarkSuccess(pk);
// this was an outbound establish job
if(ctx->establish_job)
{
@ -362,22 +390,43 @@ llarp_router::TryEstablishTo(const llarp::RouterID &remote)
// try connecting async
llarp_router_try_connect(this, rc, 5);
}
else
else if(!routerProfiling.IsBad(remote))
{
if(dht->impl.HasRouterLookup(remote))
return;
llarp::LogInfo("looking up router ", remote);
// dht lookup as we don't know it
dht->impl.LookupRouter(
remote,
std::bind(&llarp_router::HandleDHTLookupForTryEstablishTo, this,
std::bind(&llarp_router::HandleDHTLookupForTryEstablishTo, this, remote,
std::placeholders::_1));
}
}
void
llarp_router::OnConnectTimeout(const llarp::RouterID &remote)
{
auto itr = pendingEstablishJobs.find(remote);
if(itr != pendingEstablishJobs.end())
{
itr->second->AttemptTimedout();
}
}
void
llarp_router::HandleDHTLookupForTryEstablishTo(
const std::vector< llarp::RouterContact > &results)
llarp::RouterID remote, const std::vector< llarp::RouterContact > &results)
{
if(results.size() == 0)
{
routerProfiling.MarkTimeout(remote);
}
for(const auto &result : results)
{
llarp_nodedb_put_rc(nodedb, result);
llarp_router_try_connect(this, result, 10);
async_verify_RC(result);
}
}
size_t
@ -397,14 +446,7 @@ llarp_router::Tick()
while(itr != m_PersistingSessions.end())
{
auto link = GetLinkWithSessionByPubkey(itr->first);
if(now > itr->second)
{
// persisting ended
if(link)
link->CloseSessionTo(itr->first);
itr = m_PersistingSessions.erase(itr);
}
else
if(now < itr->second)
{
if(link)
{
@ -416,26 +458,28 @@ llarp_router::Tick()
llarp::LogDebug("establish to ", itr->first);
TryEstablishTo(itr->first);
}
++itr;
}
++itr;
}
}
if(inboundLinks.size() == 0)
{
auto N = llarp_nodedb_num_loaded(nodedb);
if(N > 3)
{
paths.BuildPaths();
}
else
if(N < minRequiredRouters)
{
llarp::LogInfo(
"We need more than 3 service nodes to build paths but we have ", N);
dht->impl.Explore(N);
llarp::LogInfo("We need at least ", minRequiredRouters,
" service nodes to build paths but we have ", N);
auto explore = std::max(NumberOfConnectedRouters(), size_t(1));
dht->impl.Explore(explore);
}
paths.BuildPaths();
hiddenServiceContext.Tick();
}
if(NumberOfConnectedRouters() < minConnectedRouters)
{
ConnectToRandomRouters(minConnectedRouters);
}
paths.TickPaths();
}
@ -589,6 +633,7 @@ llarp_router::async_verify_RC(const llarp::RouterContact &rc)
void
llarp_router::Run()
{
routerProfiling.Load(routerProfilesFile.string().c_str());
// zero out router contact
sockaddr *dest = (sockaddr *)&this->ip4addr;
llarp::Addr publicAddr(*dest);
@ -614,7 +659,7 @@ llarp_router::Run()
if(!a.isPrivate())
{
llarp::LogInfo("Loading Addr: ", a, " into our RC");
rc.addrs.push_back(addr);
_rc.addrs.push_back(addr);
}
};
if(this->publicOverride)
@ -640,19 +685,18 @@ llarp_router::Run()
this->addrInfo.ip = *publicAddr.addr6();
this->addrInfo.port = publicAddr.port();
llarp::LogInfo("Loaded our public ", publicAddr, " override into RC!");
// we need the link to set the pubkey
rc.addrs.push_back(this->addrInfo);
_rc.addrs.push_back(this->addrInfo);
}
}
// set public encryption key
rc.enckey = llarp::seckey_topublic(encryption);
llarp::LogInfo("Your Encryption pubkey ", rc.enckey);
_rc.enckey = llarp::seckey_topublic(encryption);
llarp::LogInfo("Your Encryption pubkey ", rc().enckey);
// set public signing key
rc.pubkey = llarp::seckey_topublic(identity);
llarp::LogInfo("Your Identity pubkey ", rc.pubkey);
_rc.pubkey = llarp::seckey_topublic(identity);
llarp::LogInfo("Your Identity pubkey ", rc().pubkey);
llarp::LogInfo("Signing rc...");
if(!rc.Sign(&crypto, identity))
if(!_rc.Sign(&crypto, identity))
{
llarp::LogError("failed to sign rc");
return;
@ -663,6 +707,8 @@ llarp_router::Run()
return;
}
llarp::LogInfo("have ", llarp_nodedb_num_loaded(nodedb), " routers");
llarp::LogDebug("starting outbound link");
if(!outboundLink->Start(logic))
{
@ -693,6 +739,18 @@ llarp_router::Run()
}
else
{
// we are a client
// regenerate keys and resign rc before everything else
crypto.identity_keygen(identity);
crypto.encryption_keygen(encryption);
_rc.pubkey = llarp::seckey_topublic(identity);
_rc.enckey = llarp::seckey_topublic(encryption);
if(!_rc.Sign(&crypto, identity))
{
llarp::LogError("failed to regenerate keys and sign RC");
return;
}
// delayed connect all for clients
uint64_t delay = ((llarp_randint() % 10) * 500) + 500;
llarp_logic_call_later(logic, {delay, this, &ConnectAll});
@ -719,12 +777,47 @@ llarp_router::ConnectAll(void *user, uint64_t orig, uint64_t left)
if(left)
return;
llarp_router *self = static_cast< llarp_router * >(user);
// connect to all explicit connections in connect block
for(const auto &itr : self->connect)
{
llarp::LogInfo("connecting to node ", itr.first);
self->try_connect(itr.second);
}
}
bool
llarp_router::HasSessionTo(const llarp::RouterID &remote) const
{
return validRouters.find(remote) != validRouters.end();
}
void
llarp_router::ConnectToRandomRouters(int want)
{
int wanted = want;
llarp_router *self = this;
llarp_nodedb_visit_loaded(
self->nodedb, [self, &want](const llarp::RouterContact &other) -> bool {
if(llarp_randint() % 2 == 0
&& !(self->HasSessionTo(other.pubkey)
|| self->HasPendingConnectJob(other.pubkey)))
{
llarp_router_try_connect(self, other, 5);
--want;
}
return want > 0;
});
if(wanted != want)
llarp::LogInfo("connecting to ", abs(want - wanted), " out of ", wanted,
" random routers");
}
bool
llarp_router::ReloadConfig(const llarp_config *conf)
{
return true;
}
bool
llarp_router::InitOutboundLink()
{
@ -807,7 +900,10 @@ void
llarp_stop_router(struct llarp_router *router)
{
if(router)
{
router->Close();
router->routerProfiling.Save(router->routerProfilesFile.string().c_str());
}
}
void
@ -966,14 +1062,26 @@ namespace llarp
}
else if(StrEq(section, "network"))
{
if(StrEq(key, "profiles"))
{
self->routerProfilesFile = fs::path(val);
}
if(StrEq(key, "min-connected"))
{
self->minConnectedRouters = std::max(atoi(val), 0);
}
if(StrEq(key, "max-connected"))
{
self->maxConnectedRouters = std::max(atoi(val), 1);
}
}
else if(StrEq(section, "router"))
{
if(StrEq(key, "nickname"))
{
self->rc.SetNick(val);
self->_rc.SetNick(val);
// set logger name here
_glog.nodeName = self->rc.Nick();
_glog.nodeName = self->rc().Nick();
}
if(StrEq(key, "encryption-privkey"))
{

@ -18,6 +18,7 @@
#include <llarp/routing/handler.hpp>
#include <llarp/service.hpp>
#include <llarp/establish_job.hpp>
#include <llarp/profiling.hpp>
#include "crypto.hpp"
#include "fs.hpp"
@ -47,7 +48,13 @@ struct llarp_router
fs::path our_rc_file = "rc.signed";
// our router contact
llarp::RouterContact rc;
llarp::RouterContact _rc;
const llarp::RouterContact &
rc() const
{
return _rc;
}
// our ipv4 public setting
bool publicOverride = false;
@ -69,6 +76,13 @@ struct llarp_router
// buffer for serializing link messages
byte_t linkmsg_buffer[MAX_LINK_MSG_SIZE];
/// always maintain this many connections to other routers
size_t minConnectedRouters = 5;
/// hard upperbound limit on the number of router to router connections
size_t maxConnectedRouters = 2000;
int minRequiredRouters = 4;
// should we be sending padded messages every interval?
bool sendPadding = false;
@ -82,6 +96,9 @@ struct llarp_router
std::unique_ptr< llarp::ILinkLayer > outboundLink;
std::list< std::unique_ptr< llarp::ILinkLayer > > inboundLinks;
llarp::Profiling routerProfiling;
fs::path routerProfilesFile = "profiles.dat";
typedef std::queue< std::vector< byte_t > > MessageQueue;
/// outbound message queue
@ -156,12 +173,18 @@ struct llarp_router
return llarp::seckey_topublic(identity);
}
void
OnConnectTimeout(const llarp::RouterID &remote);
bool
HasPendingConnectJob(const llarp::RouterID &remote);
void
try_connect(fs::path rcfile);
bool
ReloadConfig(const llarp_config *conf);
/// send to remote router or queue for sending
/// returns false on overflow
/// returns true on successful queue
@ -207,6 +230,9 @@ struct llarp_router
llarp::ILinkLayer *
GetLinkWithSessionByPubkey(const llarp::RouterID &remote);
void
ConnectToRandomRouters(int N);
size_t
NumberOfConnectedRouters() const;
@ -220,8 +246,12 @@ struct llarp_router
HandleDHTLookupForSendTo(llarp::RouterID remote,
const std::vector< llarp::RouterContact > &results);
bool
HasSessionTo(const llarp::RouterID &remote) const;
void
HandleDHTLookupForTryEstablishTo(
llarp::RouterID remote,
const std::vector< llarp::RouterContact > &results);
static void

@ -223,13 +223,9 @@ namespace llarp
RouterContact &
RouterContact::operator=(const RouterContact &other)
{
addrs.clear();
exits.clear();
addrs = other.addrs;
exits = other.exits;
signature = other.signature;
addrs = other.addrs;
exits = other.exits;
signature = other.signature;
last_updated = other.last_updated;
enckey = other.enckey;
pubkey = other.pubkey;

@ -2,6 +2,7 @@
#include <llarp/messages/path_confirm.hpp>
#include <llarp/messages/path_latency.hpp>
#include <llarp/messages/path_transfer.hpp>
#include <llarp/messages/discard.hpp>
#include <llarp/routing/message.hpp>
#include "mem.hpp"
@ -41,6 +42,9 @@ namespace llarp
self->key = *strbuf.cur;
switch(self->key)
{
case 'D':
self->msg = new DataDiscardMessage();
break;
case 'L':
self->msg = new PathLatencyMessage();
break;
@ -93,4 +97,4 @@ namespace llarp
return result;
}
} // namespace routing
} // namespace llarp
} // namespace llarp

@ -24,7 +24,7 @@ namespace llarp
return false;
if(!BEncodeMaybeReadDictEntry("T", T, read, key, val))
return false;
if(!BEncodeMaybeReadDictInt("V", V, read, key, val))
if(!BEncodeMaybeReadDictInt("V", version, read, key, val))
return false;
if(!BEncodeMaybeReadDictEntry("Y", Y, read, key, val))
return false;

@ -105,7 +105,7 @@ namespace llarp
auto highest = now;
for(const auto& i : I)
highest = std::max(i.expiresAt, highest);
return highest == now;
return highest <= now;
}
Introduction::~Introduction()
@ -260,7 +260,12 @@ namespace llarp
return false;
// decode
inf.read((char*)buf.base, sz);
return BDecode(&buf);
if(!BDecode(&buf))
return false;
// update pubkey
pub.Update(llarp::seckey_topublic(enckey),
llarp::seckey_topublic(signkey));
return true;
}
bool

@ -11,7 +11,7 @@ namespace llarp
namespace service
{
Endpoint::Endpoint(const std::string& name, llarp_router* r)
: path::Builder(r, r->dht, 2, 4), m_Router(r), m_Name(name)
: path::Builder(r, r->dht, 4, 4), m_Router(r), m_Name(name)
{
m_Tag.Zero();
}
@ -43,6 +43,12 @@ namespace llarp
m_NetNS = v;
m_OnInit.push_back(std::bind(&Endpoint::IsolateNetwork, this));
}
if(k == "min-latency")
{
auto val = atoi(v.c_str());
if(val > 0)
m_MinPathLatency = val;
}
return true;
}
@ -84,41 +90,55 @@ namespace llarp
!= m_PendingServiceLookups.end();
}
void
Endpoint::RegenAndPublishIntroSet(llarp_time_t now)
{
std::set< Introduction > I;
if(!GetCurrentIntroductions(I))
{
llarp::LogWarn("could not publish descriptors for endpoint ", Name(),
" because we couldn't get any introductions");
if(ShouldBuildMore())
ManualRebuild(1);
return;
}
IntroSet introset = m_IntroSet;
introset.I.clear();
for(const auto& intro : I)
{
llarp::LogInfo(intro);
if(!intro.ExpiresSoon(now))
introset.I.push_back(intro);
}
if(introset.I.size() == 0)
{
llarp::LogWarn("not enough intros to publish introset for ", Name());
return;
}
introset.topic = m_Tag;
if(!m_Identity.SignIntroSet(introset, &m_Router->crypto))
{
llarp::LogWarn("failed to sign introset for endpoint ", Name());
return;
}
m_IntroSet = introset;
if(PublishIntroSet(m_Router))
{
llarp::LogInfo("(re)publishing introset for endpoint ", Name());
}
else
{
llarp::LogWarn("failed to publish intro set for endpoint ", Name());
}
}
void
Endpoint::Tick(llarp_time_t now)
{
/// reset tx id for publish
if(now - m_LastPublishAttempt >= INTROSET_PUBLISH_RETRY_INTERVAL)
m_CurrentPublishTX = 0;
// publish descriptors
if(ShouldPublishDescriptors(now))
{
std::set< Introduction > I;
if(!GetCurrentIntroductions(I))
{
llarp::LogWarn("could not publish descriptors for endpoint ", Name(),
" because we couldn't get any introductions");
if(ShouldBuildMore())
ManualRebuild(1);
return;
}
m_IntroSet.I.clear();
for(const auto& intro : I)
m_IntroSet.I.push_back(intro);
m_IntroSet.topic = m_Tag;
if(!m_Identity.SignIntroSet(m_IntroSet, &m_Router->crypto))
{
llarp::LogWarn("failed to sign introset for endpoint ", Name());
return;
}
if(PublishIntroSet(m_Router))
{
llarp::LogInfo("publishing introset for endpoint ", Name());
}
else
{
llarp::LogWarn("failed to publish intro set for endpoint ", Name());
}
RegenAndPublishIntroSet(now);
}
// expire pending tx
{
@ -346,6 +366,7 @@ namespace llarp
{
inserted |= tags.insert(itr->first).second;
}
++itr;
}
return inserted;
}
@ -451,46 +472,78 @@ namespace llarp
bool
Endpoint::PublishIntroSet(llarp_router* r)
{
auto path = GetEstablishedPathClosestTo(m_Identity.pub.Addr().ToRouter());
if(path)
auto path = GetEstablishedPathClosestTo(m_Identity.pub.Addr().data());
if(path && PublishIntroSetVia(r, path))
{
m_CurrentPublishTX = llarp_randint();
llarp::routing::DHTMessage msg;
msg.M.emplace_back(new llarp::dht::PublishIntroMessage(
m_IntroSet, m_CurrentPublishTX, 4));
if(path->SendRoutingMessage(&msg, r))
{
m_LastPublishAttempt = llarp_time_now_ms();
llarp::LogInfo(Name(), " publishing introset");
return true;
}
path = PickRandomEstablishedPath();
return path && PublishIntroSetVia(r, path);
}
llarp::LogWarn(Name(), " publish introset failed, no path");
return false;
}
struct PublishIntroSetJob : public IServiceLookup
{
IntroSet m_IntroSet;
Endpoint* m_Endpoint;
PublishIntroSetJob(Endpoint* parent, uint64_t id,
const IntroSet& introset)
: IServiceLookup(parent, id, "PublishIntroSet")
, m_IntroSet(introset)
, m_Endpoint(parent)
{
}
llarp::routing::IMessage*
BuildRequestMessage()
{
llarp::routing::DHTMessage* msg = new llarp::routing::DHTMessage();
msg->M.emplace_back(
new llarp::dht::PublishIntroMessage(m_IntroSet, txid, 4));
return msg;
}
bool
HandleResponse(const std::set< IntroSet >& response)
{
if(response.size())
m_Endpoint->IntroSetPublished();
else
m_Endpoint->IntroSetPublishFail();
return true;
}
};
void
Endpoint::IntroSetPublishFail()
{
llarp::LogWarn("failed to publish introset for ", Name());
m_CurrentPublishTX = 0;
// TODO: linear backoff
}
bool
Endpoint::PublishIntroSetVia(llarp_router* r, path::Path* path)
{
auto job = new PublishIntroSetJob(this, GenTXID(), m_IntroSet);
if(job->SendRequestViaPath(path, r))
{
m_LastPublishAttempt = llarp_time_now_ms();
return true;
}
return false;
}
bool
Endpoint::ShouldPublishDescriptors(llarp_time_t now) const
{
if(m_IntroSet.HasExpiredIntros(now))
return m_CurrentPublishTX == 0
&& now - m_LastPublishAttempt >= INTROSET_PUBLISH_RETRY_INTERVAL;
return m_CurrentPublishTX == 0
&& now - m_LastPublish >= INTROSET_PUBLISH_INTERVAL;
return now - m_LastPublishAttempt >= INTROSET_PUBLISH_RETRY_INTERVAL;
return now - m_LastPublishAttempt >= INTROSET_PUBLISH_INTERVAL;
}
void
Endpoint::IntroSetPublished()
{
m_CurrentPublishTX = 0;
m_LastPublish = llarp_time_now_ms();
m_LastPublish = llarp_time_now_ms();
llarp::LogInfo(Name(), " IntroSet publish confirmed");
}
@ -501,7 +554,8 @@ namespace llarp
}
Address remote;
typedef std::function< bool(const IntroSet*) > HandlerFunc;
typedef std::function< bool(const Address&, const IntroSet*) >
HandlerFunc;
HandlerFunc handle;
HiddenServiceAddressLookup(Endpoint* p, HandlerFunc h,
@ -514,19 +568,11 @@ namespace llarp
HandleResponse(const std::set< IntroSet >& results)
{
llarp::LogInfo("found ", results.size(), " for ", remote.ToString());
if(results.size() == 1)
if(results.size() > 0)
{
llarp::LogInfo("hidden service lookup for ", remote.ToString(),
" success");
handle(&*results.begin());
return handle(remote, &*results.begin());
}
else
{
llarp::LogInfo("no response in hidden service lookup for ",
remote.ToString());
handle(nullptr);
}
return false;
return handle(remote, nullptr);
}
llarp::routing::IMessage*
@ -638,14 +684,67 @@ namespace llarp
Endpoint::HandlePathBuilt(path::Path* p)
{
p->SetDataHandler(std::bind(&Endpoint::HandleHiddenServiceFrame, this,
std::placeholders::_1));
std::placeholders::_1,
std::placeholders::_2));
p->SetDropHandler(std::bind(&Endpoint::HandleDataDrop, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3));
p->SetDeadChecker(std::bind(&Endpoint::CheckPathIsDead, this,
std::placeholders::_1,
std::placeholders::_2));
RegenAndPublishIntroSet(llarp_time_now_ms());
}
bool
Endpoint::HandleHiddenServiceFrame(const ProtocolFrame* frame)
Endpoint::HandleDataDrop(path::Path* p, const PathID_t& dst, uint64_t seq)
{
llarp::LogWarn(Name(), " message ", seq, " dropped by endpoint ",
p->Endpoint(), " via ", dst);
return true;
}
bool
Endpoint::OutboundContext::HandleDataDrop(path::Path* p,
const PathID_t& dst, uint64_t seq)
{
llarp::LogWarn(Name(), " message ", seq, " dropped by endpoint ",
p->Endpoint(), " via ", dst);
// pick another intro
if(dst == remoteIntro.pathID)
{
MarkCurrentIntroBad();
ShiftIntroduction();
}
UpdateIntroSet();
return true;
}
bool
Endpoint::HandleDataMessage(const PathID_t& src, ProtocolMessage* msg)
{
msg->sender.UpdateAddr();
PutIntroFor(msg->tag, msg->introReply);
EnsureReplyPath(msg->sender);
return ProcessDataMessage(msg);
}
bool
Endpoint::HandleHiddenServiceFrame(path::Path* p,
const ProtocolFrame* frame)
{
return frame->AsyncDecryptAndVerify(EndpointLogic(), Crypto(), p->RXID(),
Worker(), m_Identity, m_DataHandler);
}
Endpoint::SendContext::SendContext(const ServiceInfo& ident,
const Introduction& intro, PathSet* send,
Endpoint* ep)
: remoteIdent(ident)
, remoteIntro(intro)
, m_PathSet(send)
, m_DataHandler(ep)
, m_Endpoint(ep)
{
return frame->AsyncDecryptAndVerify(EndpointLogic(), Crypto(), Worker(),
m_Identity, m_DataHandler);
}
void
@ -653,21 +752,54 @@ namespace llarp
{
p->SetDataHandler(
std::bind(&Endpoint::OutboundContext::HandleHiddenServiceFrame, this,
std::placeholders::_1));
std::placeholders::_1, std::placeholders::_2));
p->SetDropHandler(std::bind(
&Endpoint::OutboundContext::HandleDataDrop, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
p->SetDeadChecker(std::bind(&Endpoint::CheckPathIsDead, m_Endpoint,
std::placeholders::_1,
std::placeholders::_2));
}
void
Endpoint::HandlePathDead(void* user)
{
Endpoint* self = static_cast< Endpoint* >(user);
self->RegenAndPublishIntroSet(llarp_time_now_ms());
}
bool
Endpoint::CheckPathIsDead(path::Path*, llarp_time_t latency)
{
if(latency >= m_MinPathLatency)
{
// rebuild path next tick
llarp_logic_queue_job(EndpointLogic(), {this, &HandlePathDead});
return true;
}
return false;
}
bool
Endpoint::OutboundContext::HandleHiddenServiceFrame(
const ProtocolFrame* frame)
path::Path* p, const ProtocolFrame* frame)
{
return m_Parent->HandleHiddenServiceFrame(frame);
return m_Endpoint->HandleHiddenServiceFrame(p, frame);
}
bool
Endpoint::OnOutboundLookup(const IntroSet* introset)
Endpoint::OnOutboundLookup(const Address& addr, const IntroSet* introset)
{
if(!introset)
{
auto itr = m_PendingServiceLookups.find(addr);
if(itr != m_PendingServiceLookups.end())
{
m_PendingServiceLookups.erase(itr);
itr->second(addr, nullptr);
}
return false;
}
PutNewOutboundContext(*introset);
return true;
}
@ -704,7 +836,8 @@ namespace llarp
HiddenServiceAddressLookup* job = new HiddenServiceAddressLookup(
this,
std::bind(&Endpoint::OnOutboundLookup, this, std::placeholders::_1),
std::bind(&Endpoint::OnOutboundLookup, this, std::placeholders::_1,
std::placeholders::_2),
remote, GenTXID());
if(job->SendRequestViaPath(path, Router()))
@ -716,11 +849,11 @@ namespace llarp
Endpoint::OutboundContext::OutboundContext(const IntroSet& intro,
Endpoint* parent)
: path::Builder(parent->m_Router, parent->m_Router->dht, 2, 4)
, SendContext(intro.A, {}, this, parent)
, currentIntroSet(intro)
, m_Parent(parent)
{
selectedIntro.Clear();
updatingIntroSet = false;
ShiftIntroduction();
}
@ -729,12 +862,15 @@ namespace llarp
}
bool
Endpoint::OutboundContext::OnIntroSetUpdate(const IntroSet* i)
Endpoint::OutboundContext::OnIntroSetUpdate(const Address& addr,
const IntroSet* i)
{
if(i && i->IsNewerThan(currentIntroSet))
if(i)
{
currentIntroSet = *i;
ShiftIntroduction();
}
updatingIntroSet = false;
return true;
}
@ -742,8 +878,64 @@ namespace llarp
Endpoint::SendToOrQueue(const Address& remote, llarp_buffer_t data,
ProtocolType t)
{
{
auto itr = m_AddressToService.find(remote);
if(itr != m_AddressToService.end())
{
routing::PathTransferMessage transfer;
ProtocolFrame& f = transfer.T;
path::Path* p = nullptr;
std::set< ConvoTag > tags;
if(!GetConvoTagsForService(itr->second, tags))
{
llarp::LogError("no convo tag");
return false;
}
Introduction remoteIntro;
const byte_t* K = nullptr;
for(const auto& tag : tags)
{
if(p == nullptr && GetIntroFor(tag, remoteIntro))
{
p = GetPathByRouter(remoteIntro.router);
if(p)
{
f.T = tag;
if(!GetCachedSessionKeyFor(tag, K))
{
llarp::LogError("no cached session key");
return false;
}
}
}
}
if(p)
{
// TODO: check expiration of our end
ProtocolMessage m(f.T);
m.proto = t;
m.introReply = p->intro;
m.sender = m_Identity.pub;
m.PutBuffer(data);
f.N.Randomize();
f.S = GetSeqNoForConvo(f.T);
f.C.Zero();
transfer.Y.Randomize();
transfer.P = remoteIntro.pathID;
if(!f.EncryptAndSign(&Router()->crypto, m, K, m_Identity))
{
llarp::LogError("failed to encrypt and sign");
return false;
}
llarp::LogInfo(Name(), " send ", data.sz, " via ", remoteIntro);
return p->SendRoutingMessage(&transfer, Router());
}
}
}
if(HasPathToService(remote))
{
llarp::LogDebug(Name(), " has session to ", remote, " sending ",
data.sz, " bytes");
m_RemoteSessions[remote]->AsyncEncryptAndSendTo(data, t);
return true;
}
@ -752,89 +944,104 @@ namespace llarp
if(itr == m_PendingTraffic.end())
{
m_PendingTraffic.insert(std::make_pair(remote, PendingBufferQueue()));
EnsurePathToService(remote,
[&](Address addr, OutboundContext* ctx) {
if(ctx)
{
auto itr = m_PendingTraffic.find(addr);
if(itr != m_PendingTraffic.end())
{
while(itr->second.size())
{
auto& front = itr->second.front();
ctx->AsyncEncryptAndSendTo(front.Buffer(),
front.protocol);
itr->second.pop();
}
}
}
m_PendingTraffic.erase(addr);
},
10000);
EnsurePathToService(
remote,
[&](Address addr, OutboundContext* ctx) {
if(ctx)
{
auto itr = m_PendingTraffic.find(addr);
if(itr != m_PendingTraffic.end())
{
while(itr->second.size())
{
auto& front = itr->second.front();
ctx->AsyncEncryptAndSendTo(front.Buffer(), front.protocol);
itr->second.pop();
}
}
}
else
{
llarp::LogWarn("failed to obtain outbound context to ", addr,
" within timeout");
}
m_PendingTraffic.erase(addr);
},
10000);
}
m_PendingTraffic[remote].emplace(data, t);
return true;
}
void
Endpoint::OutboundContext::MarkCurrentIntroBad()
{
m_BadIntros.insert(remoteIntro);
}
void
Endpoint::OutboundContext::ShiftIntroduction()
{
auto now = llarp_time_now_ms();
if(now - lastShift < MIN_SHIFT_INTERVAL)
return;
bool shifted = false;
for(const auto& intro : currentIntroSet.I)
{
m_Parent->EnsureRouterIsKnown(selectedIntro.router);
if(intro.expiresAt > selectedIntro.expiresAt)
m_Endpoint->EnsureRouterIsKnown(intro.router);
if(m_BadIntros.count(intro) == 0 && remoteIntro != intro)
{
selectedIntro = intro;
shifted = intro.router != remoteIntro.router;
remoteIntro = intro;
break;
}
}
ManualRebuild(2);
if(shifted)
{
lastShift = now;
ManualRebuild(1);
}
}
void
Endpoint::OutboundContext::AsyncEncryptAndSendTo(llarp_buffer_t data,
ProtocolType protocol)
Endpoint::SendContext::AsyncEncryptAndSendTo(llarp_buffer_t data,
ProtocolType protocol)
{
auto path = GetPathByRouter(selectedIntro.router);
if(!path)
{
llarp::LogError("No Path to ", selectedIntro.router, " yet");
return;
}
if(sequenceNo)
{
EncryptAndSendTo(path, data, protocol);
EncryptAndSendTo(data, protocol);
}
else
{
AsyncGenIntro(path, data, protocol);
AsyncGenIntro(data, protocol);
}
}
struct AsyncIntroGen
struct AsyncKeyExchange
{
llarp_logic* logic;
llarp_crypto* crypto;
byte_t* sharedKey;
SharedSecret sharedKey;
ServiceInfo remote;
const Identity& m_LocalIdentity;
ProtocolMessage msg;
ProtocolFrame frame;
Introduction intro;
const PQPubKey introPubKey;
Introduction remoteIntro;
std::function< void(ProtocolFrame&) > hook;
IDataHandler* handler;
AsyncIntroGen(llarp_logic* l, llarp_crypto* c, byte_t* key,
const ServiceInfo& r, const Identity& localident,
const PQPubKey& introsetPubKey, const Introduction& us,
IDataHandler* h)
AsyncKeyExchange(llarp_logic* l, llarp_crypto* c, const ServiceInfo& r,
const Identity& localident,
const PQPubKey& introsetPubKey,
const Introduction& remote, IDataHandler* h)
: logic(l)
, crypto(c)
, sharedKey(key)
, remote(r)
, m_LocalIdentity(localident)
, intro(us)
, introPubKey(introsetPubKey)
, remoteIntro(remote)
, handler(h)
{
}
@ -842,19 +1049,20 @@ namespace llarp
static void
Result(void* user)
{
AsyncIntroGen* self = static_cast< AsyncIntroGen* >(user);
AsyncKeyExchange* self = static_cast< AsyncKeyExchange* >(user);
// put values
self->handler->PutCachedSessionKeyFor(self->msg.tag, self->sharedKey);
self->handler->PutIntroFor(self->msg.tag, self->msg.introReply);
self->handler->PutIntroFor(self->msg.tag, self->remoteIntro);
self->handler->PutSenderFor(self->msg.tag, self->remote);
self->hook(self->frame);
delete self;
}
/// given protocol message make protocol frame
static void
Work(void* user)
Encrypt(void* user)
{
AsyncIntroGen* self = static_cast< AsyncIntroGen* >(user);
AsyncKeyExchange* self = static_cast< AsyncKeyExchange* >(user);
// derive ntru session key component
SharedSecret K;
self->crypto->pqe_encrypt(self->frame.C, K, self->introPubKey);
@ -875,86 +1083,94 @@ namespace llarp
self->msg.tag.Randomize();
// set sender
self->msg.sender = self->m_LocalIdentity.pub;
// set our introduction
self->msg.introReply = self->intro;
// set version
self->msg.version = LLARP_PROTO_VERSION;
// set protocol
self->msg.proto = eProtocolTraffic;
// encrypt and sign
if(self->frame.EncryptAndSign(self->crypto, self->msg, K,
self->m_LocalIdentity))
llarp_logic_queue_job(self->logic, {self, &Result});
else
{
llarp::LogError("failed to encrypt and sign");
delete self;
}
}
};
void
Endpoint::OutboundContext::AsyncGenIntro(path::Path* p,
llarp_buffer_t payload,
Endpoint::EnsureReplyPath(const ServiceInfo& ident)
{
auto itr = m_AddressToService.find(ident.Addr());
if(itr == m_AddressToService.end())
m_AddressToService.insert(std::make_pair(ident.Addr(), ident));
}
void
Endpoint::OutboundContext::AsyncGenIntro(llarp_buffer_t payload,
ProtocolType t)
{
AsyncIntroGen* ex = new AsyncIntroGen(
m_Parent->RouterLogic(), m_Parent->Crypto(), sharedKey,
currentIntroSet.A, m_Parent->GetIdentity(), currentIntroSet.K,
selectedIntro, m_Parent->m_DataHandler);
auto path = m_PathSet->GetPathByRouter(remoteIntro.router);
if(path == nullptr)
return;
AsyncKeyExchange* ex =
new AsyncKeyExchange(m_Endpoint->RouterLogic(), m_Endpoint->Crypto(),
remoteIdent, m_Endpoint->GetIdentity(),
currentIntroSet.K, remoteIntro, m_DataHandler);
ex->hook = std::bind(&Endpoint::OutboundContext::Send, this,
std::placeholders::_1);
ex->msg.PutBuffer(payload);
ex->msg.introReply = p->intro;
llarp_threadpool_queue_job(m_Parent->Worker(),
{ex, &AsyncIntroGen::Work});
ex->msg.introReply = path->intro;
llarp_threadpool_queue_job(m_Endpoint->Worker(),
{ex, &AsyncKeyExchange::Encrypt});
}
void
Endpoint::OutboundContext::Send(ProtocolFrame& msg)
Endpoint::SendContext::Send(ProtocolFrame& msg)
{
// in this context we assume the message contents are encrypted
auto now = llarp_time_now_ms();
if(currentIntroSet.HasExpiredIntros(now))
{
UpdateIntroSet();
}
if(selectedIntro.expiresAt <= now || now - selectedIntro.expiresAt > 1000)
{
ShiftIntroduction();
}
// XXX: this may be a different path that that was put into the protocol
// message inside the protocol frame
auto path = GetPathByRouter(selectedIntro.router);
auto path = m_PathSet->GetPathByRouter(remoteIntro.router);
if(path)
{
routing::PathTransferMessage transfer(msg, selectedIntro.pathID);
llarp::LogDebug("sending frame via ", path->Upstream(), " to ",
path->Endpoint(), " for ", Name());
if(!path->SendRoutingMessage(&transfer, m_Parent->Router()))
auto now = llarp_time_now_ms();
if(remoteIntro.ExpiresSoon(now))
{
MarkCurrentIntroBad();
ShiftIntroduction();
}
routing::PathTransferMessage transfer(msg, remoteIntro.pathID);
if(!path->SendRoutingMessage(&transfer, m_Endpoint->Router()))
llarp::LogError("Failed to send frame on path");
}
else
{
llarp::LogWarn("No path to ", selectedIntro.router);
}
llarp::LogError("cannot send becuase we have no path to ",
remoteIntro.router);
}
std::string
Endpoint::OutboundContext::Name() const
{
return "OBContext:" + m_Parent->Name() + "-"
return "OBContext:" + m_Endpoint->Name() + "-"
+ currentIntroSet.A.Addr().ToString();
}
void
Endpoint::OutboundContext::UpdateIntroSet()
{
if(updatingIntroSet)
return;
auto addr = currentIntroSet.A.Addr();
auto path = m_Parent->GetEstablishedPathClosestTo(addr.ToRouter());
auto path = m_Endpoint->PickRandomEstablishedPath();
if(path)
{
HiddenServiceAddressLookup* job = new HiddenServiceAddressLookup(
m_Parent,
m_Endpoint,
std::bind(&Endpoint::OutboundContext::OnIntroSetUpdate, this,
std::placeholders::_1),
addr, m_Parent->GenTXID());
std::placeholders::_1, std::placeholders::_2),
addr, m_Endpoint->GenTXID());
if(!job->SendRequestViaPath(path, m_Parent->Router()))
llarp::LogError("send via path failed");
updatingIntroSet = job->SendRequestViaPath(path, m_Endpoint->Router());
}
else
{
@ -967,12 +1183,16 @@ namespace llarp
bool
Endpoint::OutboundContext::Tick(llarp_time_t now)
{
if(selectedIntro.expiresAt >= now
|| selectedIntro.expiresAt - now < 30000)
if(remoteIntro.ExpiresSoon(now))
{
UpdateIntroSet();
MarkCurrentIntroBad();
ShiftIntroduction();
}
m_Parent->EnsureRouterIsKnown(selectedIntro.router);
m_Endpoint->EnsureRouterIsKnown(remoteIntro.router);
std::remove_if(m_BadIntros.cbegin(), m_BadIntros.cend(),
[now](const Introduction& intro) -> bool {
return intro.IsExpired(now);
});
// TODO: check for expiration of outbound context
return false;
}
@ -984,7 +1204,7 @@ namespace llarp
{
if(hop == numHops - 1)
{
if(llarp_nodedb_get_rc(db, selectedIntro.router, cur))
if(llarp_nodedb_get_rc(db, remoteIntro.router, cur))
{
return true;
}
@ -994,13 +1214,12 @@ namespace llarp
llarp::LogError(
"cannot build aligned path, don't have router for "
"introduction ",
selectedIntro);
m_Parent->EnsureRouterIsKnown(selectedIntro.router);
remoteIntro);
m_Endpoint->EnsureRouterIsKnown(remoteIntro.router);
return false;
}
}
else
return path::Builder::SelectHop(db, prev, cur, hop);
return path::Builder::SelectHop(db, prev, cur, hop);
}
uint64_t
@ -1013,58 +1232,57 @@ namespace llarp
}
void
Endpoint::OutboundContext::EncryptAndSendTo(path::Path* p,
llarp_buffer_t payload,
ProtocolType t)
Endpoint::SendContext::EncryptAndSendTo(llarp_buffer_t payload,
ProtocolType t)
{
auto path = GetPathByRouter(selectedIntro.router);
if(path)
std::set< ConvoTag > tags;
if(!m_DataHandler->GetConvoTagsForService(remoteIdent, tags))
{
std::set< ConvoTag > tags;
if(!m_Parent->m_DataHandler->GetConvoTagsForService(currentIntroSet.A,
tags))
{
llarp::LogError("no open converstations with remote endpoint?");
return;
}
auto crypto = m_Parent->Crypto();
const byte_t* shared = nullptr;
routing::PathTransferMessage msg;
ProtocolFrame& f = msg.T;
f.N.Randomize();
f.T = *tags.begin();
f.S = m_Parent->GetSeqNoForConvo(f.T);
if(m_Parent->m_DataHandler->GetCachedSessionKeyFor(f.T, shared))
{
ProtocolMessage m;
m.proto = t;
m.introReply = path->intro;
m.sender = m_Parent->m_Identity.pub;
m.PutBuffer(payload);
llarp::LogError("no open converstations with remote endpoint?");
return;
}
auto crypto = m_Endpoint->Router()->crypto;
const byte_t* shared = nullptr;
routing::PathTransferMessage msg;
ProtocolFrame& f = msg.T;
f.N.Randomize();
f.T = *tags.begin();
f.S = m_Endpoint->GetSeqNoForConvo(f.T);
auto path = m_PathSet->GetNewestPathByRouter(remoteIntro.router);
if(!path)
{
llarp::LogError("cannot encrypt and send: no path for intro ",
remoteIntro);
return;
}
if(!f.EncryptAndSign(crypto, m, shared, m_Parent->m_Identity))
{
llarp::LogError("failed to sign");
return;
}
}
else
if(m_DataHandler->GetCachedSessionKeyFor(f.T, shared))
{
ProtocolMessage m;
m.proto = t;
m_DataHandler->PutIntroFor(f.T, remoteIntro);
m.introReply = path->intro;
m.sender = m_Endpoint->m_Identity.pub;
m.PutBuffer(payload);
if(!f.EncryptAndSign(&crypto, m, shared, m_Endpoint->m_Identity))
{
llarp::LogError("No cached session key");
llarp::LogError("failed to sign");
return;
}
msg.P = selectedIntro.pathID;
msg.Y.Randomize();
if(!path->SendRoutingMessage(&msg, m_Parent->Router()))
{
llarp::LogWarn("Failed to send routing message for data");
}
}
else
{
llarp::LogError("no outbound path for sending message");
llarp::LogError("No cached session key");
return;
}
msg.P = remoteIntro.pathID;
msg.Y.Randomize();
if(!path->SendRoutingMessage(&msg, m_Endpoint->Router()))
{
llarp::LogWarn("Failed to send routing message for data");
}
}

@ -25,14 +25,14 @@ namespace llarp
{
payload.resize(buf.sz);
memcpy(payload.data(), buf.base, buf.sz);
payload.shrink_to_fit();
}
void
ProtocolMessage::ProcessAsync(void* user)
{
ProtocolMessage* self = static_cast< ProtocolMessage* >(user);
self->handler->HandleDataMessage(self);
if(!self->handler->HandleDataMessage(self->srcPath, self))
llarp::LogWarn("failed to handle data message from ", self->srcPath);
delete self;
}
@ -81,7 +81,7 @@ namespace llarp
if(!BEncodeWriteDictEntry("t", tag, buf))
return false;
}
if(!bencode_write_version_entry(buf))
if(!BEncodeWriteDictInt("v", version, buf))
return false;
return bencode_end(buf);
}
@ -156,9 +156,10 @@ namespace llarp
const byte_t* sharedkey,
ProtocolMessage& msg) const
{
msg.PutBuffer(D.Buffer());
auto buf = llarp::Buffer(msg.payload);
return crypto->xchacha20(buf, sharedkey, N);
Encrypted tmp = D;
auto buf = tmp.Buffer();
crypto->xchacha20(*buf, sharedkey, N);
return msg.BDecode(buf);
}
bool
@ -200,11 +201,11 @@ namespace llarp
ProtocolMessage* msg;
const Identity& m_LocalIdentity;
IDataHandler* handler;
const ProtocolFrame* frame;
const ProtocolFrame frame;
AsyncFrameDecrypt(llarp_logic* l, llarp_crypto* c,
const Identity& localIdent, IDataHandler* h,
ProtocolMessage* m, const ProtocolFrame* f)
ProtocolMessage* m, const ProtocolFrame& f)
: crypto(c)
, logic(l)
, msg(m)
@ -222,20 +223,18 @@ namespace llarp
SharedSecret K;
SharedSecret sharedKey;
// copy
ProtocolFrame frame;
frame = *self->frame;
if(!crypto->pqe_decrypt(self->frame->C, K,
ProtocolFrame frame(self->frame);
if(!crypto->pqe_decrypt(self->frame.C, K,
pq_keypair_to_secret(self->m_LocalIdentity.pq)))
{
llarp::LogError("pqke failed C=", self->frame->C);
frame.Dump< MAX_PROTOCOL_MESSAGE_SIZE >();
llarp::LogError("pqke failed C=", self->frame.C);
delete self->msg;
delete self;
return;
}
// decrypt
auto buf = frame.D.Buffer();
crypto->xchacha20(*buf, K, self->frame->N);
crypto->xchacha20(*buf, K, self->frame.N);
if(!self->msg->BDecode(buf))
{
llarp::LogError("failed to decode inner protocol message");
@ -245,11 +244,12 @@ namespace llarp
return;
}
// verify signature of outer message after we parsed the inner message
if(!self->frame->Verify(crypto, self->msg->sender))
if(!self->frame.Verify(crypto, self->msg->sender))
{
llarp::LogError("intro frame has invalid signature Z=",
self->frame->Z, " from ", self->msg->sender);
self->frame->Dump< MAX_PROTOCOL_MESSAGE_SIZE >();
llarp::LogError("intro frame has invalid signature Z=", self->frame.Z,
" from ", self->msg->sender.Addr());
self->frame.Dump< MAX_PROTOCOL_MESSAGE_SIZE >();
self->msg->Dump< MAX_PROTOCOL_MESSAGE_SIZE >();
delete self->msg;
delete self;
return;
@ -258,11 +258,11 @@ namespace llarp
// K
memcpy(tmp, K, 32);
// PKE (A, B, N)
if(!self->m_LocalIdentity.KeyExchange(
crypto->dh_server, tmp + 32, self->msg->sender, self->frame->N))
if(!self->m_LocalIdentity.KeyExchange(crypto->dh_server, tmp + 32,
self->msg->sender, self->frame.N))
{
llarp::LogError("x25519 key exchange failed");
self->frame->Dump< MAX_PROTOCOL_MESSAGE_SIZE >();
self->frame.Dump< MAX_PROTOCOL_MESSAGE_SIZE >();
delete self->msg;
delete self;
return;
@ -284,16 +284,19 @@ namespace llarp
ProtocolFrame&
ProtocolFrame::operator=(const ProtocolFrame& other)
{
C = other.C;
D = other.D;
N = other.N;
Z = other.Z;
T = other.T;
C = other.C;
D = other.D;
N = other.N;
Z = other.Z;
T = other.T;
S = other.S;
version = other.version;
return *this;
}
bool
ProtocolFrame::AsyncDecryptAndVerify(llarp_logic* logic, llarp_crypto* c,
const PathID_t& srcPath,
llarp_threadpool* worker,
const Identity& localIdent,
IDataHandler* handler) const
@ -301,9 +304,10 @@ namespace llarp
if(T.IsZero())
{
ProtocolMessage* msg = new ProtocolMessage();
msg->srcPath = srcPath;
// we need to dh
auto dh =
new AsyncFrameDecrypt(logic, c, localIdent, handler, msg, this);
new AsyncFrameDecrypt(logic, c, localIdent, handler, msg, *this);
llarp_threadpool_queue_job(worker, {dh, &AsyncFrameDecrypt::Work});
return true;
}
@ -321,7 +325,7 @@ namespace llarp
}
if(!Verify(c, si))
{
llarp::LogError("Signature failure");
llarp::LogError("Signature failure from ", si.Addr());
return false;
}
ProtocolMessage* msg = new ProtocolMessage();
@ -331,47 +335,48 @@ namespace llarp
delete msg;
return false;
}
msg->srcPath = srcPath;
msg->handler = handler;
llarp_logic_queue_job(logic, {msg, &ProtocolMessage::ProcessAsync});
return true;
}
ProtocolFrame::ProtocolFrame()
bool
ProtocolFrame::operator==(const ProtocolFrame& other) const
{
T.Zero();
return C == other.C && D == other.D && N == other.N && Z == other.Z
&& T == other.T && S == other.S && version == other.version;
}
bool
ProtocolFrame::Verify(llarp_crypto* crypto, const ServiceInfo& from) const
{
ProtocolFrame copy;
copy = *this;
ProtocolFrame copy(*this);
// save signature
// zero out signature for verify
copy.Z.Zero();
bool result = false;
// serialize
byte_t tmp[MAX_PROTOCOL_MESSAGE_SIZE];
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
if(copy.BEncode(&buf))
if(!copy.BEncode(&buf))
{
// rewind buffer
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// verify
result = from.Verify(crypto, buf, Z);
llarp::LogError("bencode fail");
return false;
}
// restore signature
return result;
// rewind buffer
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// verify
return from.Verify(crypto, buf, Z);
}
bool
ProtocolFrame::HandleMessage(llarp::routing::IMessageHandler* h,
llarp_router* r) const
{
llarp::LogInfo("Got hidden service frame");
return h->HandleHiddenServiceFrame(this);
}
} // namespace service
} // namespace llarp
} // namespace llarp

@ -1,5 +1,6 @@
#include <llarp/path.hpp>
#include <llarp/routing/handler.hpp>
#include <llarp/messages/discard.hpp>
#include "buffer.hpp"
#include "router.hpp"
@ -53,7 +54,6 @@ namespace llarp
TransitHop::SendRoutingMessage(llarp::routing::IMessage* msg,
llarp_router* r)
{
msg->S = m_SequenceNum++;
byte_t tmp[MAX_LINK_MSG_SIZE - 1024];
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
if(!msg->BEncode(&buf))
@ -135,6 +135,14 @@ namespace llarp
return false;
}
bool
TransitHop::HandleDataDiscardMessage(
const llarp::routing::DataDiscardMessage* msg, llarp_router* r)
{
llarp::LogWarn("unwarranted path data discard message on ", info);
return false;
}
bool
TransitHop::HandlePathTransferMessage(
const llarp::routing::PathTransferMessage* msg, llarp_router* r)
@ -142,8 +150,9 @@ namespace llarp
auto path = r->paths.GetByUpstream(r->pubkey(), msg->P);
if(!path)
{
llarp::LogWarn("No such path for path transfer pathid=", msg->P);
return false;
llarp::routing::DataDiscardMessage discarded(msg->P, msg->S);
path = r->paths.GetByUpstream(r->pubkey(), msg->from);
return path && path->SendRoutingMessage(&discarded, r);
}
byte_t tmp[service::MAX_PROTOCOL_MESSAGE_SIZE];

@ -1 +1 @@
traffic correlation nation
first mostly working version

@ -1,6 +1,6 @@
# LokiNet
Lokinet is a private, decentralized and Sybil resistant overlay network for the internet, it uses a new routing protocol called LLARP (Low latency anonymous routing protocol)
LokiNet is the reference implementation of LLARP (low latency anonymous routing protocol), a layer 3 onion routing protocol.
You can learn more about the high level design of LLARP [here](doc/high-level.txt)
<<<<<<< Updated upstream
@ -42,4 +42,52 @@ Build requirements:
Building a debug build:
>>>>>>> Stashed changes
To build lokinet see the [lokinet-builder](https://github.com/loki-project/lokinet-builder) repository.
## Building
![build status](https://gitlab.com/lokiproject/loki-network/badges/master/pipeline.svg "build status")
If you don't have libsodium 1.0.16 or higher use the [lokinet builder](https://github.com/loki-project/lokinet-builder) repo.
Otherwise:
$ sudo apt install git libcap-dev build-essential ninja-build cmake libsodium-dev
$ git clone https://github.com/loki-project/loki-network
$ cd loki-network
$ make
## Usage
### Windows
Windows only supports client mode so you run `lokinet.exe` and that's it.
### Linux
Client mode:
For simple testing do:
$ lokinet
On systemd based distros you can persist it in the background:
# systemctl enable --now lokinet-client
Relay mode:
you can participate as a relay node trivially (for now).
On systemd based linux distros do:
# systemctl enable --now lokinet-relay
Alternatively:
# mkdir /usr/local/lokinet
# cd /usr/local/lokinet
# lokinet --genconf daemon.ini
# lokinet --check daemon.ini
# lokinet /usr/local/lokinet/daemon.ini

@ -35,187 +35,215 @@
#include "tuntap.h"
int
tuntap_sys_start(struct device *dev, int mode, int tun) {
int fd;
int persist;
char *ifname;
struct ifreq ifr;
/* Get the persistence bit */
if (mode & TUNTAP_MODE_PERSIST) {
mode &= ~TUNTAP_MODE_PERSIST;
persist = 1;
} else {
persist = 0;
}
/* Set the mode: tun or tap */
(void)memset(&ifr, '\0', sizeof ifr);
if (mode == TUNTAP_MODE_ETHERNET) {
ifr.ifr_flags = IFF_TAP;
ifname = "tap%i";
} else if (mode == TUNTAP_MODE_TUNNEL) {
ifr.ifr_flags = IFF_TUN;
ifname = "tun%i";
} else {
tuntap_log(TUNTAP_LOG_ERR, "Invalid parameter 'mode'");
return -1;
}
ifr.ifr_flags |= IFF_NO_PI;
if (tun < 0) {
tuntap_log(TUNTAP_LOG_ERR, "Invalid parameter 'tun'");
return -1;
tuntap_sys_start(struct device *dev, int mode, int tun)
{
int fd;
int persist;
char *ifname;
struct ifreq ifr;
/* Get the persistence bit */
if(mode & TUNTAP_MODE_PERSIST)
{
mode &= ~TUNTAP_MODE_PERSIST;
persist = 1;
}
else
{
persist = 0;
}
/* Set the mode: tun or tap */
(void)memset(&ifr, '\0', sizeof ifr);
if(mode == TUNTAP_MODE_ETHERNET)
{
ifr.ifr_flags = IFF_TAP;
ifname = "tap%i";
}
else if(mode == TUNTAP_MODE_TUNNEL)
{
ifr.ifr_flags = IFF_TUN;
if(dev->if_name[0])
strncpy(ifr.ifr_name, dev->if_name, sizeof(ifr.ifr_name));
else
ifname = "tun%i";
}
else
{
tuntap_log(TUNTAP_LOG_ERR, "Invalid parameter 'mode'");
return -1;
}
ifr.ifr_flags |= IFF_NO_PI;
if(tun < 0)
{
tuntap_log(TUNTAP_LOG_ERR, "Invalid parameter 'tun'");
return -1;
}
/* Open the clonable interface */
fd = -1;
if((fd = open("/dev/net/tun", O_RDWR)) == -1)
{
tuntap_log(TUNTAP_LOG_ERR, "Can't open /dev/net/tun");
return -1;
}
/* Set the interface name, if any */
if(fd > TUNTAP_ID_MAX)
{
return -1;
}
if(ifr.ifr_name[0] == 0 && tun)
(void)snprintf(ifr.ifr_name, sizeof ifr.ifr_name, ifname, tun);
/* Save interface name *after* SIOCGIFFLAGS */
/* Configure the interface */
if(ioctl(fd, TUNSETIFF, &ifr) == -1)
{
tuntap_log(TUNTAP_LOG_ERR, "Can't set interface name");
return -1;
}
/* Set it persistent if needed */
if(persist == 1)
{
if(ioctl(fd, TUNSETPERSIST, 1) == -1)
{
tuntap_log(TUNTAP_LOG_ERR, "Can't set persistent");
return -1;
}
/* Open the clonable interface */
fd = -1;
if ((fd = open("/dev/net/tun", O_RDWR)) == -1) {
tuntap_log(TUNTAP_LOG_ERR, "Can't open /dev/net/tun");
return -1;
}
/* Set the interface name, if any */
if (tun != TUNTAP_ID_ANY) {
if (fd > TUNTAP_ID_MAX) {
return -1;
}
(void)snprintf(ifr.ifr_name, sizeof ifr.ifr_name,
ifname, tun);
/* Save interface name *after* SIOCGIFFLAGS */
}
/* Configure the interface */
if (ioctl(fd, TUNSETIFF, &ifr) == -1) {
tuntap_log(TUNTAP_LOG_ERR, "Can't set interface name");
return -1;
}
/* Set it persistent if needed */
if (persist == 1) {
if (ioctl(fd, TUNSETPERSIST, 1) == -1) {
tuntap_log(TUNTAP_LOG_ERR, "Can't set persistent");
return -1;
}
}
/* Get the interface default values */
if (ioctl(dev->ctrl_sock, SIOCGIFFLAGS, &ifr) == -1) {
tuntap_log(TUNTAP_LOG_ERR, "Can't get interface values");
return -1;
}
/* Save flags for tuntap_{up, down} */
dev->flags = ifr.ifr_flags;
/* Save interface name */
(void)memcpy(dev->if_name, ifr.ifr_name, sizeof ifr.ifr_name);
/* Save pre-existing MAC address */
if (mode == TUNTAP_MODE_ETHERNET) {
struct ifreq ifr_hw;
(void)memcpy(ifr_hw.ifr_name, dev->if_name,
sizeof(dev->if_name));
if (ioctl(fd, SIOCGIFHWADDR, &ifr_hw) == -1) {
tuntap_log(TUNTAP_LOG_WARN,
"Can't get link-layer address");
return fd;
}
(void)memcpy(dev->hwaddr, ifr_hw.ifr_hwaddr.sa_data, ETH_ALEN);
}
return fd;
}
/* Get the interface default values */
if(ioctl(dev->ctrl_sock, SIOCGIFFLAGS, &ifr) == -1)
{
tuntap_log(TUNTAP_LOG_ERR, "Can't get interface values");
return -1;
}
/* Save flags for tuntap_{up, down} */
dev->flags = ifr.ifr_flags;
/* Save interface name */
(void)memcpy(dev->if_name, ifr.ifr_name, sizeof ifr.ifr_name);
/* Save pre-existing MAC address */
if(mode == TUNTAP_MODE_ETHERNET)
{
struct ifreq ifr_hw;
(void)memcpy(ifr_hw.ifr_name, dev->if_name, sizeof(dev->if_name));
if(ioctl(fd, SIOCGIFHWADDR, &ifr_hw) == -1)
{
tuntap_log(TUNTAP_LOG_WARN, "Can't get link-layer address");
return fd;
}
(void)memcpy(dev->hwaddr, ifr_hw.ifr_hwaddr.sa_data, ETH_ALEN);
}
return fd;
}
void
tuntap_sys_destroy(struct device *dev) {
if (ioctl(dev->tun_fd, TUNSETPERSIST, 0) == -1) {
tuntap_log(TUNTAP_LOG_WARN, "Can't destroy the interface");
}
tuntap_sys_destroy(struct device *dev)
{
if(ioctl(dev->tun_fd, TUNSETPERSIST, 0) == -1)
{
tuntap_log(TUNTAP_LOG_WARN, "Can't destroy the interface");
}
}
int
tuntap_sys_set_hwaddr(struct device *dev, struct ether_addr *eth_addr) {
struct ifreq ifr;
(void)memset(&ifr, '\0', sizeof ifr);
(void)memcpy(ifr.ifr_name, dev->if_name, sizeof dev->if_name);
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
(void)memcpy(ifr.ifr_hwaddr.sa_data, eth_addr->ether_addr_octet, 6);
/* Linux has a special flag for setting the MAC address */
if (ioctl(dev->ctrl_sock, SIOCSIFHWADDR, &ifr) == -1) {
tuntap_log(TUNTAP_LOG_ERR, "Can't set link-layer address");
return -1;
}
return 0;
tuntap_sys_set_hwaddr(struct device *dev, struct ether_addr *eth_addr)
{
struct ifreq ifr;
(void)memset(&ifr, '\0', sizeof ifr);
(void)memcpy(ifr.ifr_name, dev->if_name, sizeof dev->if_name);
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
(void)memcpy(ifr.ifr_hwaddr.sa_data, eth_addr->ether_addr_octet, 6);
/* Linux has a special flag for setting the MAC address */
if(ioctl(dev->ctrl_sock, SIOCSIFHWADDR, &ifr) == -1)
{
tuntap_log(TUNTAP_LOG_ERR, "Can't set link-layer address");
return -1;
}
return 0;
}
int
tuntap_sys_set_ipv4(struct device *dev, t_tun_in_addr *s4, uint32_t bits) {
struct ifreq ifr;
struct sockaddr_in mask;
(void)memset(&ifr, '\0', sizeof ifr);
(void)memcpy(ifr.ifr_name, dev->if_name, sizeof dev->if_name);
/* Set the IP address first */
(void)memcpy(&(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr),
s4, sizeof(struct in_addr));
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(dev->ctrl_sock, SIOCSIFADDR, &ifr) == -1) {
tuntap_log(TUNTAP_LOG_ERR, "Can't set IP address");
return -1;
}
/* Reinit the struct ifr */
(void)memset(&ifr.ifr_addr, '\0', sizeof ifr.ifr_addr);
/* Then set the netmask */
(void)memset(&mask, '\0', sizeof mask);
mask.sin_family = AF_INET;
mask.sin_addr.s_addr = bits;
(void)memcpy(&ifr.ifr_netmask, &mask, sizeof ifr.ifr_netmask);
if (ioctl(dev->ctrl_sock, SIOCSIFNETMASK, &ifr) == -1) {
tuntap_log(TUNTAP_LOG_ERR, "Can't set netmask");
return -1;
}
return 0;
tuntap_sys_set_ipv4(struct device *dev, t_tun_in_addr *s4, uint32_t bits)
{
struct ifreq ifr;
struct sockaddr_in mask;
(void)memset(&ifr, '\0', sizeof ifr);
(void)memcpy(ifr.ifr_name, dev->if_name, sizeof dev->if_name);
/* Set the IP address first */
(void)memcpy(&(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr), s4,
sizeof(struct in_addr));
ifr.ifr_addr.sa_family = AF_INET;
if(ioctl(dev->ctrl_sock, SIOCSIFADDR, &ifr) == -1)
{
tuntap_log(TUNTAP_LOG_ERR, "Can't set IP address");
return -1;
}
/* Reinit the struct ifr */
(void)memset(&ifr.ifr_addr, '\0', sizeof ifr.ifr_addr);
/* Then set the netmask */
(void)memset(&mask, '\0', sizeof mask);
mask.sin_family = AF_INET;
mask.sin_addr.s_addr = bits;
(void)memcpy(&ifr.ifr_netmask, &mask, sizeof ifr.ifr_netmask);
if(ioctl(dev->ctrl_sock, SIOCSIFNETMASK, &ifr) == -1)
{
tuntap_log(TUNTAP_LOG_ERR, "Can't set netmask");
return -1;
}
return 0;
}
int
tuntap_sys_set_ipv6(struct device *dev, t_tun_in6_addr *s6, uint32_t bits) {
(void)dev;
(void)s6;
(void)bits;
tuntap_log(TUNTAP_LOG_NOTICE, "IPv6 is not implemented on your system");
return -1;
tuntap_sys_set_ipv6(struct device *dev, t_tun_in6_addr *s6, uint32_t bits)
{
(void)dev;
(void)s6;
(void)bits;
tuntap_log(TUNTAP_LOG_NOTICE, "IPv6 is not implemented on your system");
return -1;
}
int
tuntap_sys_set_ifname(struct device *dev, const char *ifname, size_t len) {
struct ifreq ifr;
(void)strncpy(ifr.ifr_name, dev->if_name, IF_NAMESIZE);
(void)strncpy(ifr.ifr_newname, ifname, len);
if (ioctl(dev->ctrl_sock, SIOCSIFNAME, &ifr) == -1) {
perror(NULL);
tuntap_log(TUNTAP_LOG_ERR, "Can't set interface name");
return -1;
}
return 0;
tuntap_sys_set_ifname(struct device *dev, const char *ifname, size_t len)
{
struct ifreq ifr;
(void)strncpy(ifr.ifr_name, dev->if_name, IF_NAMESIZE);
(void)strncpy(ifr.ifr_newname, ifname, len);
if(ioctl(dev->ctrl_sock, SIOCSIFNAME, &ifr) == -1)
{
perror(NULL);
tuntap_log(TUNTAP_LOG_ERR, "Can't set interface name");
return -1;
}
return 0;
}
int
tuntap_sys_set_descr(struct device *dev, const char *descr, size_t len) {
(void)dev;
(void)descr;
(void)len;
tuntap_log(TUNTAP_LOG_NOTICE,
"Your system does not support tuntap_set_descr()");
return -1;
tuntap_sys_set_descr(struct device *dev, const char *descr, size_t len)
{
(void)dev;
(void)descr;
(void)len;
tuntap_log(TUNTAP_LOG_NOTICE,
"Your system does not support tuntap_set_descr()");
return -1;
}

Loading…
Cancel
Save