lokinet/llarp/iwp/session.cpp

965 lines
28 KiB
C++
Raw Normal View History

2019-08-22 20:53:27 +00:00
#include <iwp/session.hpp>
2019-09-01 12:38:03 +00:00
2019-08-22 20:53:27 +00:00
#include <messages/link_intro.hpp>
2019-08-23 11:32:52 +00:00
#include <messages/discard.hpp>
2019-09-01 12:38:03 +00:00
#include <util/meta/memfn.hpp>
2019-08-22 20:53:27 +00:00
namespace llarp
{
namespace iwp
{
2019-09-12 14:34:27 +00:00
ILinkSession::Packet_t
CreatePacket(Command cmd, size_t plainsize, size_t minpad, size_t variance)
{
const size_t pad = minpad > 0 ? minpad + (variance > 0 ? randint() % variance : 0) : 0;
ILinkSession::Packet_t pkt(PacketOverhead + plainsize + pad + CommandOverhead);
2019-09-12 14:34:27 +00:00
// randomize pad
if (pad)
2019-09-12 14:34:27 +00:00
{
CryptoManager::instance()->randbytes(
pkt.data() + PacketOverhead + CommandOverhead + plainsize, pad);
2019-09-12 14:34:27 +00:00
}
// randomize nounce
CryptoManager::instance()->randbytes(pkt.data() + HMACSIZE, TUNNONCESIZE);
pkt[PacketOverhead] = LLARP_PROTO_VERSION;
2019-09-12 14:34:27 +00:00
pkt[PacketOverhead + 1] = cmd;
return pkt;
}
Session::Session(LinkLayer* p, const RouterContact& rc, const AddressInfo& ai)
2019-08-22 20:53:27 +00:00
: m_State{State::Initial}
, m_Inbound{false}
2019-11-20 16:10:52 +00:00
, m_Parent(p)
2019-08-22 20:53:27 +00:00
, m_CreatedAt{p->Now()}
2020-05-06 20:38:44 +00:00
, m_RemoteAddr(ai.toIpAddress())
2019-11-20 16:10:52 +00:00
, m_ChosenAI(ai)
, m_RemoteRC(rc)
2019-08-22 20:53:27 +00:00
{
token.Zero();
GotLIM = util::memFn(&Session::GotOutboundLIM, this);
CryptoManager::instance()->shorthash(m_SessionKey, llarp_buffer_t(rc.pubkey));
2019-08-22 20:53:27 +00:00
}
2020-05-06 20:38:44 +00:00
Session::Session(LinkLayer* p, const IpAddress& from)
2019-08-22 20:53:27 +00:00
: m_State{State::Initial}
, m_Inbound{true}
2019-11-20 16:10:52 +00:00
, m_Parent(p)
2019-08-22 20:53:27 +00:00
, m_CreatedAt{p->Now()}
2019-11-20 16:10:52 +00:00
, m_RemoteAddr(from)
2019-08-22 20:53:27 +00:00
{
token.Randomize();
GotLIM = util::memFn(&Session::GotInboundLIM, this);
const PubKey pk = m_Parent->GetOurRC().pubkey;
CryptoManager::instance()->shorthash(m_SessionKey, llarp_buffer_t(pk));
2019-08-22 20:53:27 +00:00
}
void
2019-11-19 20:30:51 +00:00
Session::Send_LL(const byte_t* buf, size_t sz)
2019-08-22 20:53:27 +00:00
{
2019-11-19 20:30:51 +00:00
LogDebug("send ", sz, " to ", m_RemoteAddr);
const llarp_buffer_t pkt(buf, sz);
2020-05-06 20:38:44 +00:00
m_Parent->SendTo_LL(m_RemoteAddr.createSockAddr(), pkt);
2019-08-22 20:53:27 +00:00
m_LastTX = time_now_ms();
2019-12-17 14:36:56 +00:00
m_TXRate += sz;
2019-08-22 20:53:27 +00:00
}
bool
2019-08-23 11:32:52 +00:00
Session::GotInboundLIM(const LinkIntroMessage* msg)
2019-08-22 20:53:27 +00:00
{
if (msg->rc.pubkey != m_ExpectedIdent)
2019-08-23 11:32:52 +00:00
{
LogError(
2020-05-15 13:07:28 +00:00
"ident key mismatch from ", m_RemoteAddr, " ", msg->rc.pubkey, " != ", m_ExpectedIdent);
2019-08-22 20:53:27 +00:00
return false;
2019-08-23 11:32:52 +00:00
}
m_State = State::Ready;
GotLIM = util::memFn(&Session::GotRenegLIM, this);
2019-08-23 11:32:52 +00:00
m_RemoteRC = msg->rc;
m_Parent->MapAddr(m_RemoteRC.pubkey, this);
2020-06-11 19:02:34 +00:00
return m_Parent->SessionEstablished(this, true);
2019-08-22 20:53:27 +00:00
}
bool
2019-08-23 11:32:52 +00:00
Session::GotOutboundLIM(const LinkIntroMessage* msg)
2019-08-22 20:53:27 +00:00
{
if (msg->rc.pubkey != m_RemoteRC.pubkey)
2019-08-23 11:32:52 +00:00
{
LogError("ident key mismatch");
2019-08-22 20:53:27 +00:00
return false;
2019-08-23 11:32:52 +00:00
}
m_RemoteRC = msg->rc;
GotLIM = util::memFn(&Session::GotRenegLIM, this);
auto self = shared_from_this();
assert(self.use_count() > 1);
2019-08-23 11:32:52 +00:00
SendOurLIM([self](ILinkSession::DeliveryStatus st) {
if (st == ILinkSession::DeliveryStatus::eDeliverySuccess)
2019-08-23 11:32:52 +00:00
{
self->m_State = State::Ready;
self->m_Parent->MapAddr(self->m_RemoteRC.pubkey, self.get());
2020-06-11 19:02:34 +00:00
self->m_Parent->SessionEstablished(self.get(), false);
2019-08-23 11:32:52 +00:00
}
});
2019-08-22 20:53:27 +00:00
return true;
}
void
2019-08-23 11:32:52 +00:00
Session::SendOurLIM(ILinkSession::CompletionHandler h)
2019-08-22 20:53:27 +00:00
{
LinkIntroMessage msg;
msg.rc = m_Parent->GetOurRC();
msg.N.Randomize();
msg.P = 60000;
if (not msg.Sign(m_Parent->Sign))
2019-08-22 20:53:27 +00:00
{
LogError("failed to sign our RC for ", m_RemoteAddr);
return;
}
ILinkSession::Message_t data(LinkIntroMessage::MaxSize + PacketOverhead);
llarp_buffer_t buf(data);
if (not msg.BEncode(&buf))
2019-08-22 20:53:27 +00:00
{
LogError("failed to encode LIM for ", m_RemoteAddr);
}
if (!SendMessageBuffer(std::move(data), h))
2019-08-22 20:53:27 +00:00
{
LogError("failed to send LIM to ", m_RemoteAddr);
}
LogDebug("sent LIM to ", m_RemoteAddr);
}
void
2019-09-12 14:34:27 +00:00
Session::EncryptAndSend(ILinkSession::Packet_t data)
2019-08-22 20:53:27 +00:00
{
if (m_EncryptNext == nullptr)
m_EncryptNext = std::make_shared<CryptoQueue_t>();
m_EncryptNext->emplace_back(std::move(data));
if (!IsEstablished())
{
2019-09-05 14:57:01 +00:00
EncryptWorker(std::move(m_EncryptNext));
m_EncryptNext = nullptr;
}
2019-09-05 14:57:01 +00:00
}
void
Session::EncryptWorker(CryptoQueue_ptr msgs)
2019-09-05 14:57:01 +00:00
{
LogDebug("encrypt worker ", msgs->size(), " messages");
for (auto& pkt : *msgs)
2019-09-05 14:57:01 +00:00
{
llarp_buffer_t pktbuf(pkt);
const TunnelNonce nonce_ptr{pkt.data() + HMACSIZE};
2019-09-05 14:57:01 +00:00
pktbuf.base += PacketOverhead;
pktbuf.cur = pktbuf.base;
pktbuf.sz -= PacketOverhead;
CryptoManager::instance()->xchacha20(pktbuf, m_SessionKey, nonce_ptr);
pktbuf.base = pkt.data() + HMACSIZE;
pktbuf.sz = pkt.size() - HMACSIZE;
2019-09-05 14:57:01 +00:00
CryptoManager::instance()->hmac(pkt.data(), pktbuf, m_SessionKey);
2019-11-19 20:30:51 +00:00
Send_LL(pkt.data(), pkt.size());
2019-09-05 14:57:01 +00:00
}
2019-08-22 20:53:27 +00:00
}
void
Session::Close()
{
if (m_State == State::Closed)
2019-08-22 20:53:27 +00:00
return;
2019-09-12 14:34:27 +00:00
auto close_msg = CreatePacket(Command::eCLOS, 0, 16, 16);
if (m_State == State::Ready)
2019-08-23 11:32:52 +00:00
m_Parent->UnmapAddr(m_RemoteAddr);
2019-08-22 20:53:27 +00:00
m_State = State::Closed;
2019-12-05 16:31:58 +00:00
EncryptAndSend(std::move(close_msg));
2019-08-23 11:32:52 +00:00
LogInfo("closing connection to ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
}
bool
Session::SendMessageBuffer(
ILinkSession::Message_t buf, ILinkSession::CompletionHandler completed)
2019-08-22 20:53:27 +00:00
{
if (m_TXMsgs.size() >= MaxSendQueueSize)
return false;
const auto now = m_Parent->Now();
2019-08-22 20:53:27 +00:00
const auto msgid = m_TXID++;
2020-03-23 17:20:32 +00:00
const auto bufsz = buf.size();
auto& msg = m_TXMsgs.emplace(msgid, OutboundMessage{msgid, std::move(buf), now, completed})
.first->second;
EncryptAndSend(msg.XMIT());
if (bufsz > FragmentSize)
2019-11-30 02:57:40 +00:00
{
msg.FlushUnAcked(util::memFn(&Session::EncryptAndSend, this), now);
}
2020-02-07 18:43:40 +00:00
m_Stats.totalInFlightTX++;
2019-08-22 20:53:27 +00:00
LogDebug("send message ", msgid);
return true;
}
void
Session::SendMACK()
{
// send multi acks
while (not m_SendMACKs.empty())
{
const auto sz = m_SendMACKs.size();
const auto max = Session::MaxACKSInMACK;
auto numAcks = std::min(sz, max);
auto mack = CreatePacket(Command::eMACK, 1 + (numAcks * sizeof(uint64_t)));
mack[PacketOverhead + CommandOverhead] = byte_t{static_cast<byte_t>(numAcks)};
byte_t* ptr = mack.data() + 3 + PacketOverhead;
LogDebug("send ", numAcks, " macks to ", m_RemoteAddr);
const auto& itr = m_SendMACKs.top();
while (numAcks > 0)
{
htobe64buf(ptr, itr);
m_SendMACKs.pop();
numAcks--;
ptr += sizeof(uint64_t);
}
2019-09-12 14:34:27 +00:00
EncryptAndSend(std::move(mack));
}
}
2019-08-22 20:53:27 +00:00
void
Session::Pump()
{
const auto now = m_Parent->Now();
if (m_State == State::Ready || m_State == State::LinkIntro)
2019-08-22 20:53:27 +00:00
{
if (ShouldPing())
2019-08-23 11:57:57 +00:00
SendKeepAlive();
for (auto& item : m_RXMsgs)
2019-08-22 20:53:27 +00:00
{
if (item.second.ShouldSendACKS(now))
2019-08-22 20:53:27 +00:00
{
item.second.SendACKS(util::memFn(&Session::EncryptAndSend, this), now);
2019-08-22 20:53:27 +00:00
}
}
for (auto& item : m_TXMsgs)
2019-08-22 20:53:27 +00:00
{
if (item.second.ShouldFlush(now))
{
item.second.FlushUnAcked(util::memFn(&Session::EncryptAndSend, this), now);
}
2019-08-22 20:53:27 +00:00
}
}
2019-09-12 14:34:27 +00:00
auto self = shared_from_this();
assert(self.use_count() > 1);
if (m_EncryptNext && !m_EncryptNext->empty())
{
m_Parent->QueueWork([self, data = std::move(m_EncryptNext)] { self->EncryptWorker(data); });
m_EncryptNext = nullptr;
}
2019-09-05 14:57:01 +00:00
if (m_DecryptNext && !m_DecryptNext->empty())
{
m_Parent->QueueWork([self, data = std::move(m_DecryptNext)] { self->DecryptWorker(data); });
m_DecryptNext = nullptr;
}
2019-08-22 20:53:27 +00:00
}
2019-08-23 11:32:52 +00:00
bool
Session::GotRenegLIM(const LinkIntroMessage* lim)
2019-08-22 20:53:27 +00:00
{
2019-08-23 12:30:07 +00:00
LogDebug("renegotiate session on ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return m_Parent->SessionRenegotiate(lim->rc, m_RemoteRC);
}
2019-08-23 11:32:52 +00:00
bool
2019-08-22 20:53:27 +00:00
Session::RenegotiateSession()
{
SendOurLIM();
return true;
}
bool
Session::ShouldPing() const
{
if (m_State == State::Ready)
2019-08-23 11:32:52 +00:00
{
const auto now = m_Parent->Now();
2019-08-23 11:32:52 +00:00
return now - m_LastTX > PingInterval;
}
return false;
2019-08-22 20:53:27 +00:00
}
2020-06-04 16:00:30 +00:00
SessionStats
Session::GetSessionStats() const
{
// TODO: thread safety
return m_Stats;
}
2019-08-22 20:53:27 +00:00
util::StatusObject
Session::ExtractStatus() const
{
2020-02-07 18:43:40 +00:00
const auto now = m_Parent->Now();
return {{"txRateCurrent", m_Stats.currentRateTX},
{"rxRateCurrent", m_Stats.currentRateRX},
{"rxPktsRcvd", m_Stats.totalPacketsRX},
// leave 'tx' and 'rx' as duplicates of 'xRateCurrent' for compat
{"tx", m_Stats.currentRateTX},
{"rx", m_Stats.currentRateRX},
2020-02-07 18:43:40 +00:00
{"txPktsAcked", m_Stats.totalAckedTX},
{"txPktsDropped", m_Stats.totalDroppedTX},
{"txPktsInFlight", m_Stats.totalInFlightTX},
{"state", StateToString(m_State)},
2019-12-17 14:36:56 +00:00
{"inbound", m_Inbound},
{"replayFilter", m_ReplayFilter.size()},
2020-02-07 18:43:40 +00:00
{"txMsgQueueSize", m_TXMsgs.size()},
{"rxMsgQueueSize", m_RXMsgs.size()},
2020-05-06 20:38:44 +00:00
{"remoteAddr", m_RemoteAddr.toString()},
2020-02-07 18:43:40 +00:00
{"remoteRC", m_RemoteRC.ExtractStatus()},
{"created", to_json(m_CreatedAt)},
{"uptime", to_json(now - m_CreatedAt)}};
2019-08-22 20:53:27 +00:00
}
bool
Session::TimedOut(llarp_time_t now) const
{
if (m_State == State::Ready || m_State == State::LinkIntro)
2019-08-23 11:32:52 +00:00
{
return now > m_LastRX && now - m_LastRX > SessionAliveTimeout;
}
return now - m_CreatedAt > SessionAliveTimeout;
2019-08-22 20:53:27 +00:00
}
2019-12-17 14:36:56 +00:00
bool
Session::ShouldResetRates(llarp_time_t now) const
{
return now >= m_ResetRatesAt;
}
void
Session::ResetRates()
{
2020-02-07 18:43:40 +00:00
m_Stats.currentRateTX = m_TXRate;
m_Stats.currentRateRX = m_RXRate;
m_RXRate = 0;
m_TXRate = 0;
2019-12-17 14:36:56 +00:00
}
void
Session::Tick(llarp_time_t now)
2019-08-22 20:53:27 +00:00
{
if (ShouldResetRates(now))
2019-12-17 14:36:56 +00:00
{
ResetRates();
2020-02-24 19:40:45 +00:00
m_ResetRatesAt = now + 1s;
2019-12-17 14:36:56 +00:00
}
// remove pending outbound messsages that timed out
// inform waiters
{
2019-08-27 12:13:55 +00:00
auto itr = m_TXMsgs.begin();
while (itr != m_TXMsgs.end())
{
if (itr->second.IsTimedOut(now))
2019-08-27 12:13:55 +00:00
{
2020-02-07 18:43:40 +00:00
m_Stats.totalDroppedTX++;
m_Stats.totalInFlightTX--;
LogDebug("Dropped unacked packet to ", m_RemoteAddr);
2019-08-27 12:13:55 +00:00
itr->second.InformTimeout();
itr = m_TXMsgs.erase(itr);
}
else
++itr;
}
}
{
// remove pending inbound messages that timed out
auto itr = m_RXMsgs.begin();
while (itr != m_RXMsgs.end())
2019-08-27 12:13:55 +00:00
{
if (itr->second.IsTimedOut(now))
2019-08-27 12:13:55 +00:00
{
m_ReplayFilter.emplace(itr->first, now);
2019-08-27 12:13:55 +00:00
itr = m_RXMsgs.erase(itr);
}
else
++itr;
}
}
2019-08-28 11:11:03 +00:00
{
// decay replay window
auto itr = m_ReplayFilter.begin();
while (itr != m_ReplayFilter.end())
2019-08-28 11:11:03 +00:00
{
if (itr->second + ReplayWindow <= now)
2019-08-28 11:11:03 +00:00
{
itr = m_ReplayFilter.erase(itr);
}
else
++itr;
}
}
2019-08-22 20:53:27 +00:00
}
using Introduction =
AlignedBuffer<PubKey::SIZE + PubKey::SIZE + TunnelNonce::SIZE + Signature::SIZE>;
2019-08-22 20:53:27 +00:00
2019-08-23 11:32:52 +00:00
void
2019-08-22 20:53:27 +00:00
Session::GenerateAndSendIntro()
{
TunnelNonce N;
N.Randomize();
2019-11-20 16:10:52 +00:00
{
ILinkSession::Packet_t req(Introduction::SIZE + PacketOverhead);
const auto pk = m_Parent->GetOurRC().pubkey;
2019-11-20 16:10:52 +00:00
const auto e_pk = m_Parent->RouterEncryptionSecret().toPublic();
auto itr = req.data() + PacketOverhead;
2019-11-20 16:10:52 +00:00
std::copy_n(pk.data(), pk.size(), itr);
itr += pk.size();
std::copy_n(e_pk.data(), e_pk.size(), itr);
itr += e_pk.size();
std::copy_n(N.data(), N.size(), itr);
Signature Z;
llarp_buffer_t signbuf(req.data() + PacketOverhead, Introduction::SIZE - Signature::SIZE);
2019-11-20 16:10:52 +00:00
m_Parent->Sign(Z, signbuf);
std::copy_n(
Z.data(),
Z.size(),
req.data() + PacketOverhead + (Introduction::SIZE - Signature::SIZE));
CryptoManager::instance()->randbytes(req.data() + HMACSIZE, TUNNONCESIZE);
2019-11-20 16:10:52 +00:00
EncryptAndSend(std::move(req));
}
2019-08-22 20:53:27 +00:00
m_State = State::Introduction;
if (not CryptoManager::instance()->transport_dh_client(
m_SessionKey, m_ChosenAI.pubkey, m_Parent->RouterEncryptionSecret(), N))
2019-09-05 14:57:01 +00:00
{
LogError("failed to transport_dh_client on outbound session to ", m_RemoteAddr);
2019-09-05 14:57:01 +00:00
return;
}
2019-08-23 11:32:52 +00:00
LogDebug("sent intro to ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
}
2019-08-23 11:32:52 +00:00
2019-08-22 20:53:27 +00:00
void
2019-09-12 14:34:27 +00:00
Session::HandleCreateSessionRequest(Packet_t pkt)
2019-08-22 20:53:27 +00:00
{
if (not DecryptMessageInPlace(pkt))
2019-08-22 20:53:27 +00:00
{
LogError("failed to decrypt session request from ", m_RemoteAddr);
return;
}
if (pkt.size() < token.size() + PacketOverhead)
2019-08-22 20:53:27 +00:00
{
LogError(
"bad session request size, ",
pkt.size(),
" < ",
token.size() + PacketOverhead,
" from ",
m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return;
}
2019-10-02 13:06:14 +00:00
const auto begin = pkt.data() + PacketOverhead;
if (not std::equal(begin, begin + token.size(), token.data()))
2019-08-22 20:53:27 +00:00
{
LogError("token mismatch from ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return;
}
2019-08-23 11:32:52 +00:00
m_LastRX = m_Parent->Now();
m_State = State::LinkIntro;
2019-08-22 20:53:27 +00:00
SendOurLIM();
}
2019-08-23 11:32:52 +00:00
void
2019-09-12 14:34:27 +00:00
Session::HandleGotIntro(Packet_t pkt)
2019-08-22 20:53:27 +00:00
{
if (pkt.size() < (Introduction::SIZE + PacketOverhead))
2019-08-23 11:32:52 +00:00
{
LogWarn("intro too small from ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return;
2019-08-23 11:32:52 +00:00
}
byte_t* ptr = pkt.data() + PacketOverhead;
2019-08-22 20:53:27 +00:00
TunnelNonce N;
2019-10-28 21:39:24 +00:00
std::copy_n(ptr, PubKey::SIZE, m_ExpectedIdent.data());
ptr += PubKey::SIZE;
2019-10-28 21:39:24 +00:00
std::copy_n(ptr, PubKey::SIZE, m_RemoteOnionKey.data());
ptr += PubKey::SIZE;
2019-10-28 21:39:24 +00:00
std::copy_n(ptr, TunnelNonce::SIZE, N.data());
ptr += TunnelNonce::SIZE;
Signature Z;
2019-10-28 21:46:39 +00:00
std::copy_n(ptr, Z.size(), Z.data());
const llarp_buffer_t verifybuf(
pkt.data() + PacketOverhead, Introduction::SIZE - Signature::SIZE);
if (!CryptoManager::instance()->verify(m_ExpectedIdent, verifybuf, Z))
{
LogError("intro verify failed from ", m_RemoteAddr);
return;
}
2019-08-22 20:53:27 +00:00
const PubKey pk = m_Parent->TransportSecretKey().toPublic();
LogDebug(
"got intro: remote-pk=",
m_RemoteOnionKey.ToHex(),
" N=",
N.ToHex(),
" local-pk=",
pk.ToHex());
if (not CryptoManager::instance()->transport_dh_server(
m_SessionKey, m_RemoteOnionKey, m_Parent->TransportSecretKey(), N))
{
LogError("failed to transport_dh_server on inbound intro from ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return;
}
2019-10-02 13:06:14 +00:00
Packet_t reply(token.size() + PacketOverhead);
2019-09-12 14:34:27 +00:00
// random nonce
CryptoManager::instance()->randbytes(reply.data() + HMACSIZE, TUNNONCESIZE);
2019-09-12 14:34:27 +00:00
// set token
2019-10-28 21:39:24 +00:00
std::copy_n(token.data(), token.size(), reply.data() + PacketOverhead);
2019-08-22 20:53:27 +00:00
m_LastRX = m_Parent->Now();
2019-09-12 14:34:27 +00:00
EncryptAndSend(std::move(reply));
2019-08-23 11:32:52 +00:00
LogDebug("sent intro ack to ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
m_State = State::Introduction;
}
void
2019-09-12 14:34:27 +00:00
Session::HandleGotIntroAck(Packet_t pkt)
2019-08-22 20:53:27 +00:00
{
if (pkt.size() < (token.size() + PacketOverhead))
2019-08-22 20:53:27 +00:00
{
LogError(
"bad intro ack size ",
pkt.size(),
" < ",
token.size() + PacketOverhead,
" from ",
m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return;
}
2019-10-02 13:06:14 +00:00
Packet_t reply(token.size() + PacketOverhead);
if (not DecryptMessageInPlace(pkt))
2019-08-22 20:53:27 +00:00
{
2019-09-12 14:34:27 +00:00
LogError("intro ack decrypt failed from ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return;
}
m_LastRX = m_Parent->Now();
2019-10-28 21:39:24 +00:00
std::copy_n(pkt.data() + PacketOverhead, token.size(), token.data());
std::copy_n(token.data(), token.size(), reply.data() + PacketOverhead);
2019-09-12 14:34:27 +00:00
// random nounce
CryptoManager::instance()->randbytes(reply.data() + HMACSIZE, TUNNONCESIZE);
2019-09-12 14:34:27 +00:00
EncryptAndSend(std::move(reply));
2019-08-23 11:32:52 +00:00
LogDebug("sent session request to ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
m_State = State::LinkIntro;
}
bool
2019-09-12 14:34:27 +00:00
Session::DecryptMessageInPlace(Packet_t& pkt)
2019-08-22 20:53:27 +00:00
{
if (pkt.size() <= PacketOverhead)
2019-08-23 11:32:52 +00:00
{
2019-09-12 14:34:27 +00:00
LogError("packet too small from ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return false;
2019-08-23 11:32:52 +00:00
}
2019-09-12 14:34:27 +00:00
const llarp_buffer_t buf(pkt);
2019-08-22 20:53:27 +00:00
ShortHash H;
llarp_buffer_t curbuf(buf.base, buf.sz);
2019-08-22 20:53:27 +00:00
curbuf.base += ShortHash::SIZE;
curbuf.sz -= ShortHash::SIZE;
if (not CryptoManager::instance()->hmac(H.data(), curbuf, m_SessionKey))
2019-08-22 20:53:27 +00:00
{
LogError("failed to caclulate keyed hash for ", m_RemoteAddr);
return false;
}
const ShortHash expected{buf.base};
if (H != expected)
{
LogError(
"keyed hash mismatch ",
H,
" != ",
expected,
" from ",
m_RemoteAddr,
" state=",
int(m_State),
" size=",
buf.sz);
2019-08-22 20:53:27 +00:00
return false;
}
const TunnelNonce N{curbuf.base};
2019-08-22 20:53:27 +00:00
curbuf.base += 32;
curbuf.sz -= 32;
LogDebug("decrypt: ", curbuf.sz, " bytes from ", m_RemoteAddr);
2019-09-12 14:34:27 +00:00
return CryptoManager::instance()->xchacha20(curbuf, m_SessionKey, N);
2019-08-22 20:53:27 +00:00
}
void
Session::Start()
{
if (m_Inbound)
2019-08-22 20:53:27 +00:00
return;
GenerateAndSendIntro();
}
void
2019-09-12 14:34:27 +00:00
Session::HandleSessionData(Packet_t pkt)
2019-08-22 20:53:27 +00:00
{
if (m_DecryptNext == nullptr)
m_DecryptNext = std::make_shared<CryptoQueue_t>();
2019-10-02 13:06:14 +00:00
m_DecryptNext->emplace_back(std::move(pkt));
2019-09-05 14:57:01 +00:00
}
void
Session::DecryptWorker(CryptoQueue_ptr msgs)
2019-09-05 14:57:01 +00:00
{
CryptoQueue_ptr recvMsgs = std::make_shared<CryptoQueue_t>();
for (auto& pkt : *msgs)
2019-08-22 20:53:27 +00:00
{
if (not DecryptMessageInPlace(pkt))
2019-09-05 14:57:01 +00:00
{
LogError("failed to decrypt session data from ", m_RemoteAddr);
continue;
}
if (pkt[PacketOverhead] != LLARP_PROTO_VERSION)
2019-09-05 14:57:01 +00:00
{
LogError(
"protocol version mismatch ", int(pkt[PacketOverhead]), " != ", LLARP_PROTO_VERSION);
2019-09-05 14:57:01 +00:00
continue;
}
2019-10-02 13:06:14 +00:00
recvMsgs->emplace_back(std::move(pkt));
2019-08-22 20:53:27 +00:00
}
2019-10-02 13:06:14 +00:00
LogDebug("decrypted ", recvMsgs->size(), " packets from ", m_RemoteAddr);
2020-05-24 12:07:37 +00:00
LogicCall(m_Parent->logic(), [self = shared_from_this(), msgs = recvMsgs] {
self->HandlePlaintext(std::move(msgs));
});
2019-09-05 14:57:01 +00:00
}
void
2019-10-02 13:06:14 +00:00
Session::HandlePlaintext(CryptoQueue_ptr msgs)
2019-09-05 14:57:01 +00:00
{
2020-06-01 13:23:17 +00:00
for (auto& result : *msgs)
2019-08-22 20:53:27 +00:00
{
LogDebug("Command ", int(result[PacketOverhead + 1]));
switch (result[PacketOverhead + 1])
2019-09-05 14:57:01 +00:00
{
case Command::eXMIT:
HandleXMIT(std::move(result));
break;
case Command::eDATA:
HandleDATA(std::move(result));
break;
case Command::eACKS:
HandleACKS(std::move(result));
break;
case Command::ePING:
HandlePING(std::move(result));
break;
case Command::eNACK:
HandleNACK(std::move(result));
break;
case Command::eCLOS:
HandleCLOS(std::move(result));
break;
case Command::eMACK:
HandleMACK(std::move(result));
break;
default:
LogError("invalid command ", int(result[PacketOverhead + 1]), " from ", m_RemoteAddr);
2019-09-05 14:57:01 +00:00
}
2019-08-22 20:53:27 +00:00
}
SendMACK();
2019-11-14 16:48:02 +00:00
Pump();
m_Parent->PumpDone();
2019-08-22 20:53:27 +00:00
}
void
2019-10-02 13:06:14 +00:00
Session::HandleMACK(Packet_t data)
{
if (data.size() < (3 + PacketOverhead))
{
LogError("impossibly short mack from ", m_RemoteAddr);
return;
}
byte_t numAcks = data[CommandOverhead + PacketOverhead];
if (data.size() < 1 + CommandOverhead + PacketOverhead + (numAcks * sizeof(uint64_t)))
{
LogError("short mack from ", m_RemoteAddr);
return;
}
LogDebug("got ", int(numAcks), " mack from ", m_RemoteAddr);
byte_t* ptr = data.data() + CommandOverhead + PacketOverhead + 1;
while (numAcks > 0)
{
uint64_t acked = bufbe64toh(ptr);
LogDebug("mack containing txid=", acked, " from ", m_RemoteAddr);
auto itr = m_TXMsgs.find(acked);
if (itr != m_TXMsgs.end())
{
2020-02-07 18:43:40 +00:00
m_Stats.totalAckedTX++;
m_Stats.totalInFlightTX--;
itr->second.Completed();
m_TXMsgs.erase(itr);
}
else
{
LogDebug("ignored mack for txid=", acked, " from ", m_RemoteAddr);
}
ptr += sizeof(uint64_t);
numAcks--;
}
}
2019-08-22 20:53:27 +00:00
void
2019-10-02 13:06:14 +00:00
Session::HandleNACK(Packet_t data)
2019-08-23 11:32:52 +00:00
{
if (data.size() < (CommandOverhead + sizeof(uint64_t) + PacketOverhead))
{
LogError("short nack from ", m_RemoteAddr);
return;
}
uint64_t txid = bufbe64toh(data.data() + CommandOverhead + PacketOverhead);
2019-08-23 11:32:52 +00:00
LogDebug("got nack on ", txid, " from ", m_RemoteAddr);
auto itr = m_TXMsgs.find(txid);
if (itr != m_TXMsgs.end())
2019-08-23 11:32:52 +00:00
{
EncryptAndSend(itr->second.XMIT());
2019-08-23 11:32:52 +00:00
}
2019-08-23 11:36:11 +00:00
m_LastRX = m_Parent->Now();
2019-08-23 11:32:52 +00:00
}
void
2019-10-02 13:06:14 +00:00
Session::HandleXMIT(Packet_t data)
2019-08-22 20:53:27 +00:00
{
2020-03-31 13:26:39 +00:00
static constexpr size_t XMITOverhead =
(CommandOverhead + PacketOverhead + sizeof(uint16_t) + sizeof(uint64_t)
+ ShortHash::SIZE);
if (data.size() < XMITOverhead)
2019-08-22 20:53:27 +00:00
{
LogError("short XMIT from ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return;
}
uint16_t sz = bufbe16toh(data.data() + CommandOverhead + PacketOverhead);
uint64_t rxid = bufbe64toh(data.data() + CommandOverhead + sizeof(uint16_t) + PacketOverhead);
ShortHash h{data.data() + CommandOverhead + sizeof(uint16_t) + sizeof(uint64_t)
+ PacketOverhead};
2019-08-22 20:53:27 +00:00
LogDebug("rxid=", rxid, " sz=", sz, " h=", h.ToHex());
m_LastRX = m_Parent->Now();
{
// check for replay
auto itr = m_ReplayFilter.find(rxid);
if (itr != m_ReplayFilter.end())
{
m_SendMACKs.emplace(rxid);
LogDebug("duplicate rxid=", rxid, " from ", m_RemoteAddr);
return;
}
}
{
2019-11-30 02:12:11 +00:00
const auto now = m_Parent->Now();
auto itr = m_RXMsgs.find(rxid);
if (itr == m_RXMsgs.end())
2019-11-30 02:08:13 +00:00
{
itr =
m_RXMsgs.emplace(rxid, InboundMessage{rxid, sz, std::move(h), m_Parent->Now()}).first;
2020-03-23 17:38:14 +00:00
sz = std::min(sz, uint16_t{FragmentSize});
if ((data.size() - XMITOverhead) == sz)
2019-11-30 02:08:13 +00:00
{
{
2019-11-30 02:57:40 +00:00
const llarp_buffer_t buf(data.data() + (data.size() - sz), sz);
itr->second.HandleData(0, buf, now);
if (not itr->second.IsCompleted())
{
return;
}
2020-05-21 14:26:51 +00:00
if (not itr->second.Verify())
2019-11-30 02:57:40 +00:00
{
LogError("bad short xmit hash from ", m_RemoteAddr);
return;
}
2019-11-30 02:08:13 +00:00
}
2019-11-30 02:12:11 +00:00
auto msg = std::move(itr->second);
2019-11-30 02:08:13 +00:00
const llarp_buffer_t buf(msg.m_Data);
m_Parent->HandleMessage(this, buf);
if (m_ReplayFilter.emplace(rxid, m_Parent->Now()).second)
2020-02-10 20:00:53 +00:00
m_SendMACKs.emplace(rxid);
2019-11-30 02:08:13 +00:00
m_RXMsgs.erase(rxid);
}
}
else
LogDebug("got duplicate xmit on ", rxid, " from ", m_RemoteAddr);
}
2019-08-22 20:53:27 +00:00
}
void
2019-10-02 13:06:14 +00:00
Session::HandleDATA(Packet_t data)
2019-08-22 20:53:27 +00:00
{
if (data.size() < (CommandOverhead + sizeof(uint16_t) + sizeof(uint64_t) + PacketOverhead))
2019-08-22 20:53:27 +00:00
{
2019-08-23 11:32:52 +00:00
LogError("short DATA from ", m_RemoteAddr, " ", data.size());
2019-08-22 20:53:27 +00:00
return;
}
m_LastRX = m_Parent->Now();
uint16_t sz = bufbe16toh(data.data() + CommandOverhead + PacketOverhead);
uint64_t rxid = bufbe64toh(data.data() + CommandOverhead + sizeof(uint16_t) + PacketOverhead);
auto itr = m_RXMsgs.find(rxid);
if (itr == m_RXMsgs.end())
2019-08-22 20:53:27 +00:00
{
if (m_ReplayFilter.find(rxid) == m_ReplayFilter.end())
{
LogDebug("no rxid=", rxid, " for ", m_RemoteAddr);
auto nack = CreatePacket(Command::eNACK, 8);
htobe64buf(nack.data() + PacketOverhead + CommandOverhead, rxid);
EncryptAndSend(std::move(nack));
}
else
{
LogDebug("replay hit for rxid=", rxid, " for ", m_RemoteAddr);
m_SendMACKs.emplace(rxid);
}
2019-08-22 20:53:27 +00:00
return;
}
{
const llarp_buffer_t buf(
data.data() + PacketOverhead + 12, data.size() - (PacketOverhead + 12));
itr->second.HandleData(sz, buf, m_Parent->Now());
}
2019-08-23 11:32:52 +00:00
if (itr->second.IsCompleted())
2019-08-23 11:32:52 +00:00
{
if (itr->second.Verify())
2019-08-23 11:32:52 +00:00
{
2019-08-23 11:50:22 +00:00
auto msg = std::move(itr->second);
const llarp_buffer_t buf(msg.m_Data);
2019-08-23 11:32:52 +00:00
m_Parent->HandleMessage(this, buf);
if (m_ReplayFilter.emplace(itr->first, m_Parent->Now()).second)
2020-02-10 20:00:53 +00:00
m_SendMACKs.emplace(itr->first);
2019-08-23 11:32:52 +00:00
}
else
{
LogError("hash mismatch for message ", itr->first);
2019-08-23 11:32:52 +00:00
}
2019-08-28 11:59:08 +00:00
m_RXMsgs.erase(itr);
2019-08-23 11:32:52 +00:00
}
2019-08-22 20:53:27 +00:00
}
2019-08-23 11:32:52 +00:00
void
2019-10-02 13:06:14 +00:00
Session::HandleACKS(Packet_t data)
2019-08-22 20:53:27 +00:00
{
if (data.size() < (11 + PacketOverhead))
2019-08-22 20:53:27 +00:00
{
LogError("short ACKS from ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return;
}
const auto now = m_Parent->Now();
m_LastRX = now;
uint64_t txid = bufbe64toh(data.data() + 2 + PacketOverhead);
auto itr = m_TXMsgs.find(txid);
if (itr == m_TXMsgs.end())
2019-08-22 20:53:27 +00:00
{
2019-08-27 17:10:25 +00:00
LogDebug("no txid=", txid, " for ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
return;
}
itr->second.Ack(data[10 + PacketOverhead]);
2019-08-23 11:32:52 +00:00
if (itr->second.IsTransmitted())
2019-08-23 11:32:52 +00:00
{
LogDebug("sent message ", itr->first);
itr->second.Completed();
itr = m_TXMsgs.erase(itr);
}
else
{
itr->second.FlushUnAcked(util::memFn(&Session::EncryptAndSend, this), now);
}
2019-08-22 20:53:27 +00:00
}
2019-10-02 13:06:14 +00:00
void Session::HandleCLOS(Packet_t)
2019-08-22 20:53:27 +00:00
{
2019-08-23 11:32:52 +00:00
LogInfo("remote closed by ", m_RemoteAddr);
2019-08-22 20:53:27 +00:00
Close();
}
2019-10-02 13:06:14 +00:00
void Session::HandlePING(Packet_t)
2019-08-22 20:53:27 +00:00
{
m_LastRX = m_Parent->Now();
}
bool
Session::SendKeepAlive()
{
if (m_State == State::Ready)
2019-08-23 11:32:52 +00:00
{
2019-10-02 13:06:14 +00:00
EncryptAndSend(CreatePacket(Command::ePING, 0));
2019-08-23 11:32:52 +00:00
return true;
}
2019-08-22 20:53:27 +00:00
return false;
}
bool
Session::IsEstablished() const
{
return m_State == State::Ready;
}
bool
Session::Recv_LL(ILinkSession::Packet_t data)
2019-08-22 20:53:27 +00:00
{
2019-12-17 14:36:56 +00:00
m_RXRate += data.size();
2020-02-07 18:43:40 +00:00
// TODO: differentiate between good and bad RX packets here
m_Stats.totalPacketsRX++;
switch (m_State)
2019-08-22 20:53:27 +00:00
{
case State::Initial:
if (m_Inbound)
2019-08-22 20:53:27 +00:00
{
// initial data
// enter introduction phase
if (DecryptMessageInPlace(data))
{
HandleGotIntro(std::move(data));
}
else
{
LogWarn("bad intro from ", m_RemoteAddr);
return false;
}
2019-08-22 20:53:27 +00:00
}
break;
case State::Introduction:
if (m_Inbound)
2019-08-22 20:53:27 +00:00
{
// we are replying to an intro ack
HandleCreateSessionRequest(std::move(data));
2019-08-22 20:53:27 +00:00
}
else
{
// we got an intro ack
// send a session request
HandleGotIntroAck(std::move(data));
2019-08-22 20:53:27 +00:00
}
break;
case State::LinkIntro:
default:
HandleSessionData(std::move(data));
2019-08-22 20:53:27 +00:00
break;
}
return true;
2019-08-22 20:53:27 +00:00
}
2020-02-07 18:43:40 +00:00
std::string
Session::StateToString(State state)
2020-02-07 18:43:40 +00:00
{
switch (state)
2020-02-07 18:43:40 +00:00
{
case State::Initial:
return "Initial";
case State::Introduction:
return "Introduction";
case State::LinkIntro:
return "LinkIntro";
case State::Ready:
return "Ready";
case State::Closed:
return "Close";
default:
return "Invalid";
}
}
2019-08-22 20:53:27 +00:00
} // namespace iwp
2019-09-01 12:38:03 +00:00
} // namespace llarp