i2pd/SSUData.cpp

516 lines
15 KiB
C++
Raw Normal View History

#include <stdlib.h>
2014-07-20 18:38:39 +04:00
#include <boost/bind.hpp>
2014-04-22 19:39:26 +04:00
#include "Log.h"
2014-07-20 18:38:39 +04:00
#include "Timestamp.h"
2014-09-18 22:37:29 +04:00
#include "NetDb.h"
2014-04-22 19:39:26 +04:00
#include "SSU.h"
#include "SSUData.h"
namespace i2p
{
namespace transport
2014-04-22 19:39:26 +04:00
{
SSUData::SSUData (SSUSession& session):
2015-02-08 16:50:05 +03:00
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()),
m_IncompleteMessagesCleanupTimer (session.GetService ())
2014-04-22 19:39:26 +04:00
{
2014-10-29 18:17:30 +03:00
m_MaxPacketSize = session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE;
m_PacketSize = m_MaxPacketSize;
2014-09-15 01:54:55 +04:00
auto remoteRouter = session.GetRemoteRouter ();
if (remoteRouter)
2014-09-18 22:37:29 +04:00
AdjustPacketSize (*remoteRouter);
2014-04-22 19:39:26 +04:00
}
SSUData::~SSUData ()
{
}
2015-02-08 16:50:05 +03:00
void SSUData::Start ()
{
ScheduleIncompleteMessagesCleanup ();
}
2015-02-08 01:58:29 +03:00
void SSUData::Stop ()
{
m_ResendTimer.cancel ();
2015-02-08 16:50:05 +03:00
m_DecayTimer.cancel ();
m_IncompleteMessagesCleanupTimer.cancel ();
2015-02-08 01:58:29 +03:00
}
2014-09-18 22:37:29 +04:00
void SSUData::AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter)
{
auto ssuAddress = remoteRouter.GetSSUAddress ();
if (ssuAddress && ssuAddress->mtu)
{
2014-10-29 18:17:30 +03:00
if (m_Session.IsV6 ())
m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
else
m_PacketSize = ssuAddress->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
2014-09-18 22:37:29 +04:00
if (m_PacketSize > 0)
{
// make sure packet size multiple of 16
m_PacketSize >>= 4;
m_PacketSize <<= 4;
2014-10-29 18:17:30 +03:00
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
2014-09-18 22:37:29 +04:00
LogPrint ("MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
}
else
{
2014-10-29 00:55:58 +03:00
LogPrint (eLogWarning, "Unexpected MTU ", ssuAddress->mtu);
2014-10-29 18:17:30 +03:00
m_PacketSize = m_MaxPacketSize;
2014-09-18 22:37:29 +04:00
}
}
}
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent)
{
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
if (routerInfo)
AdjustPacketSize (*routerInfo);
}
2014-06-12 19:14:22 +04:00
void SSUData::ProcessSentMessageAck (uint32_t msgID)
{
auto it = m_SentMessages.find (msgID);
if (it != m_SentMessages.end ())
{
m_SentMessages.erase (it);
2014-07-20 18:38:39 +04:00
if (m_SentMessages.empty ())
m_ResendTimer.cancel ();
2014-06-12 19:14:22 +04:00
}
}
2014-07-17 23:22:32 +04:00
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag)
2014-04-22 19:39:26 +04:00
{
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
{
// explicit ACKs
uint8_t numAcks =*buf;
buf++;
2014-06-12 19:14:22 +04:00
for (int i = 0; i < numAcks; i++)
ProcessSentMessageAck (bufbe32toh (buf+i*4));
2014-04-22 19:39:26 +04:00
buf += numAcks*4;
}
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
{
// explicit ACK bitfields
uint8_t numBitfields =*buf;
buf++;
for (int i = 0; i < numBitfields; i++)
{
uint32_t msgID = bufbe32toh (buf);
2014-04-22 19:39:26 +04:00
buf += 4; // msgID
2014-07-20 02:53:02 +04:00
auto it = m_SentMessages.find (msgID);
2014-07-17 23:00:33 +04:00
// process individual Ack bitfields
bool isNonLast = false;
int fragment = 0;
do
{
uint8_t bitfield = *buf;
isNonLast = bitfield & 0x80;
bitfield &= 0x7F; // clear MSB
if (bitfield && it != m_SentMessages.end ())
{
2014-07-20 18:38:39 +04:00
int numSentFragments = it->second->fragments.size ();
2014-07-17 23:00:33 +04:00
// process bits
2014-09-12 20:20:17 +04:00
uint8_t mask = 0x01;
2014-07-17 23:00:33 +04:00
for (int j = 0; j < 7; j++)
{
if (bitfield & mask)
{
if (fragment < numSentFragments)
2015-01-31 04:30:39 +03:00
it->second->fragments[fragment].reset (nullptr);
2014-07-17 23:00:33 +04:00
}
fragment++;
2014-09-12 20:20:17 +04:00
mask <<= 1;
2014-07-17 23:00:33 +04:00
}
}
2014-04-22 19:39:26 +04:00
buf++;
2014-07-17 23:00:33 +04:00
}
while (isNonLast);
2014-04-22 19:39:26 +04:00
}
2014-07-17 23:22:32 +04:00
}
}
void SSUData::ProcessFragments (uint8_t * buf)
{
2014-04-22 19:39:26 +04:00
uint8_t numFragments = *buf; // number of fragments
buf++;
for (int i = 0; i < numFragments; i++)
{
uint32_t msgID = bufbe32toh (buf); // message ID
2014-04-22 19:39:26 +04:00
buf += 4;
uint8_t frag[4];
frag[0] = 0;
memcpy (frag + 1, buf, 3);
buf += 3;
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
2014-04-22 19:39:26 +04:00
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
bool isLast = fragmentInfo & 0x010000; // bit 16
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
2014-11-03 18:15:01 +03:00
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
{
LogPrint (eLogError, "Fragment size ", fragmentSize, "exceeds max SSU packet size");
return;
}
2014-07-15 06:06:58 +04:00
// find message with msgID
2014-04-22 19:39:26 +04:00
I2NPMessage * msg = nullptr;
auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end ())
2014-07-15 06:06:58 +04:00
// message exists
msg = it->second->msg;
2014-07-15 06:06:58 +04:00
else
2014-04-22 19:39:26 +04:00
{
2014-07-15 06:06:58 +04:00
// create new message
2015-03-11 18:28:51 +03:00
msg = NewI2NPShortMessage ();
2015-01-02 07:00:33 +03:00
msg->len -= I2NP_SHORT_HEADER_SIZE;
it = m_IncompleteMessages.insert (std::make_pair (msgID,
std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first;
}
std::unique_ptr<IncompleteMessage>& incompleteMessage = it->second;
2014-04-22 19:39:26 +04:00
2014-07-15 06:06:58 +04:00
// handle current fragment
if (fragmentNum == incompleteMessage->nextFragmentNum)
{
// expected fragment
2015-03-11 18:28:51 +03:00
if (msg->len + fragmentSize > msg->maxLen)
{
LogPrint (eLogInfo, "Short I2NP message of size ", msg->maxLen, " is not enough");
I2NPMessage * newMsg = NewI2NPMessage ();
*newMsg = *msg;
DeleteI2NPMessage (msg);
msg = newMsg;
it->second->msg = msg;
}
2014-07-15 06:06:58 +04:00
memcpy (msg->buf + msg->len, buf, fragmentSize);
msg->len += fragmentSize;
incompleteMessage->nextFragmentNum++;
if (!isLast && !incompleteMessage->savedFragments.empty ())
2014-04-22 19:39:26 +04:00
{
2014-07-15 06:06:58 +04:00
// try saved fragments
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
2014-04-22 19:39:26 +04:00
{
2015-02-05 20:13:37 +03:00
auto& savedFragment = *it1;
2014-07-15 06:06:58 +04:00
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
2014-04-22 19:39:26 +04:00
{
2014-07-15 06:06:58 +04:00
memcpy (msg->buf + msg->len, savedFragment->buf, savedFragment->len);
msg->len += savedFragment->len;
isLast = savedFragment->isLast;
incompleteMessage->nextFragmentNum++;
incompleteMessage->savedFragments.erase (it1++);
}
2014-04-22 19:39:26 +04:00
else
2014-07-15 06:06:58 +04:00
break;
2014-07-15 16:03:45 +04:00
}
if (isLast)
2014-10-29 00:55:58 +03:00
LogPrint (eLogDebug, "Message ", msgID, " complete");
2014-07-15 06:06:58 +04:00
}
}
else
{
if (fragmentNum < incompleteMessage->nextFragmentNum)
// duplicate fragment
2014-10-29 00:55:58 +03:00
LogPrint (eLogWarning, "Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored");
2014-07-15 06:06:58 +04:00
else
{
// missing fragment
2014-10-29 00:55:58 +03:00
LogPrint (eLogWarning, "Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
2014-07-15 16:03:45 +04:00
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
2015-02-08 16:50:05 +03:00
if (incompleteMessage->savedFragments.insert (std::unique_ptr<Fragment>(savedFragment)).second)
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
else
2014-10-29 00:55:58 +03:00
LogPrint (eLogWarning, "Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
2014-04-22 19:39:26 +04:00
}
2014-07-15 06:06:58 +04:00
isLast = false;
}
if (isLast)
{
// delete incomplete message
2015-02-05 20:13:37 +03:00
incompleteMessage->msg = nullptr;
m_IncompleteMessages.erase (msgID);
2014-07-15 06:06:58 +04:00
// process message
SendMsgAck (msgID);
msg->FromSSU (msgID);
if (m_Session.GetState () == eSessionStateEstablished)
2014-09-30 04:08:26 +04:00
{
if (!m_ReceivedMessages.count (msgID))
{
2015-02-08 16:50:05 +03:00
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES)
m_ReceivedMessages.clear ();
else
ScheduleDecay ();
2014-09-30 04:08:26 +04:00
m_ReceivedMessages.insert (msgID);
2015-01-30 06:35:57 +03:00
m_Handler.PutNextMessage (msg);
2014-09-30 04:08:26 +04:00
}
else
{
2014-10-29 00:55:58 +03:00
LogPrint (eLogWarning, "SSU message ", msgID, " already received");
2014-09-30 04:08:26 +04:00
i2p::DeleteI2NPMessage (msg);
}
}
2014-07-15 06:06:58 +04:00
else
{
// we expect DeliveryStatus
2015-01-02 07:00:33 +03:00
if (msg->GetTypeID () == eI2NPDeliveryStatus)
2014-07-15 06:06:58 +04:00
{
LogPrint ("SSU session established");
m_Session.Established ();
}
else
2015-01-02 07:00:33 +03:00
LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetTypeID ());
2014-07-15 06:06:58 +04:00
DeleteI2NPMessage (msg);
}
}
else
SendFragmentAck (msgID, fragmentNum);
2014-04-22 19:39:26 +04:00
buf += fragmentSize;
}
}
2015-02-15 22:17:55 +03:00
void SSUData::FlushReceivedMessage ()
{
m_Handler.Flush ();
}
2014-07-17 23:22:32 +04:00
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
{
//uint8_t * start = buf;
uint8_t flag = *buf;
buf++;
2015-02-03 04:15:49 +03:00
LogPrint (eLogDebug, "Process SSU data flags=", (int)flag, " len=", len);
2014-07-17 23:22:32 +04:00
// process acks if presented
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
ProcessAcks (buf, flag);
// extended data if presented
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED)
{
uint8_t extendedDataSize = *buf;
buf++; // size
2014-10-29 00:55:58 +03:00
LogPrint (eLogDebug, "SSU extended data of ", extendedDataSize, " bytes presented");
2014-07-17 23:22:32 +04:00
buf += extendedDataSize;
}
// process data
ProcessFragments (buf);
}
2014-06-10 18:39:29 +04:00
void SSUData::Send (i2p::I2NPMessage * msg)
{
2014-06-12 19:14:22 +04:00
uint32_t msgID = msg->ToSSU ();
2014-07-07 01:48:16 +04:00
if (m_SentMessages.count (msgID) > 0)
{
2014-10-29 00:55:58 +03:00
LogPrint (eLogWarning, "SSU message ", msgID, " already sent");
2014-07-07 01:48:16 +04:00
DeleteI2NPMessage (msg);
return;
2014-07-20 18:38:39 +04:00
}
if (m_SentMessages.empty ()) // schedule resend at first message only
ScheduleResend ();
auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr<SentMessage>(new SentMessage)));
std::unique_ptr<SentMessage>& sentMessage = ret.first->second;
if (ret.second)
{
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
sentMessage->numResends = 0;
}
2014-07-20 18:38:39 +04:00
auto& fragments = sentMessage->fragments;
2014-09-15 01:54:55 +04:00
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
2014-06-10 18:39:29 +04:00
size_t len = msg->GetLength ();
uint8_t * msgBuf = msg->GetSSUHeader ();
uint32_t fragmentNum = 0;
while (len > 0)
{
2014-07-20 18:38:39 +04:00
Fragment * fragment = new Fragment;
2014-09-14 19:54:18 +04:00
fragment->fragmentNum = fragmentNum;
2014-07-20 18:38:39 +04:00
uint8_t * buf = fragment->buf;
2014-06-12 19:14:22 +04:00
uint8_t * payload = buf + sizeof (SSUHeader);
2014-06-10 18:39:29 +04:00
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
payload++;
*payload = 1; // always 1 message fragment per message
payload++;
htobe32buf (payload, msgID);
2014-06-10 18:39:29 +04:00
payload += 4;
bool isLast = (len <= payloadSize);
size_t size = isLast ? len : payloadSize;
uint32_t fragmentInfo = (fragmentNum << 17);
if (isLast)
fragmentInfo |= 0x010000;
fragmentInfo |= size;
fragmentInfo = htobe32 (fragmentInfo);
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3);
payload += 3;
memcpy (payload, msgBuf, size);
size += payload - buf;
if (size & 0x0F) // make sure 16 bytes boundary
size = ((size >> 4) + 1) << 4; // (/16 + 1)*16
2014-07-20 18:38:39 +04:00
fragment->len = size;
2015-01-31 04:30:39 +03:00
fragments.push_back (std::unique_ptr<Fragment> (fragment));
2014-06-10 18:39:29 +04:00
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size);
2015-02-15 01:23:15 +03:00
try
{
m_Session.Send (buf, size);
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogError, "Can't send SSU fragment ", ec.what ());
}
2014-06-10 18:39:29 +04:00
if (!isLast)
{
len -= payloadSize;
msgBuf += payloadSize;
}
else
len = 0;
fragmentNum++;
}
DeleteI2NPMessage (msg);
}
2014-06-10 19:19:31 +04:00
void SSUData::SendMsgAck (uint32_t msgID)
{
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
payload++;
*payload = 1; // number of ACKs
payload++;
2015-01-02 07:00:33 +03:00
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
2014-06-10 19:19:31 +04:00
payload += 4;
*payload = 0; // number of fragments
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
m_Session.Send (buf, 48);
}
void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum)
{
if (fragmentNum > 64)
{
2014-10-29 00:55:58 +03:00
LogPrint (eLogWarning, "Fragment number ", fragmentNum, " exceeds 64");
return;
}
uint8_t buf[64 + 18];
uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
payload++;
*payload = 1; // number of ACK bitfields
payload++;
// one ack
2015-01-02 07:00:33 +03:00
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
payload += 4;
div_t d = div (fragmentNum, 7);
memset (payload, 0x80, d.quot); // 0x80 means non-last
payload += d.quot;
2014-09-12 20:20:17 +04:00
*payload = 0x01 << d.rem; // set corresponding bit
payload++;
*payload = 0; // number of fragments
size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1)
// encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
m_Session.Send (buf, len);
}
2014-07-20 18:38:39 +04:00
void SSUData::ScheduleResend()
{
m_ResendTimer.cancel ();
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
2014-11-24 20:26:11 +03:00
auto s = m_Session.shared_from_this();
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleResendTimer (ecode); });
2014-07-20 18:38:39 +04:00
}
2015-02-08 16:50:05 +03:00
2014-07-20 18:38:39 +04:00
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
2015-02-08 16:50:05 +03:00
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
2014-07-20 18:38:39 +04:00
{
2015-02-08 16:50:05 +03:00
if (ts >= it->second->nextResendTime)
2014-07-20 18:38:39 +04:00
{
2015-02-08 16:50:05 +03:00
if (it->second->numResends < MAX_NUM_RESENDS)
{
for (auto& f: it->second->fragments)
2015-02-15 01:23:15 +03:00
if (f)
{
try
{
m_Session.Send (f->buf, f->len); // resend
}
catch (boost::system::system_error& ec)
{
LogPrint (eLogError, "Can't resend SSU fragment ", ec.what ());
}
}
2014-07-20 18:38:39 +04:00
2015-02-08 16:50:05 +03:00
it->second->numResends++;
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
it++;
}
else
{
LogPrint (eLogError, "SSU message has not been ACKed after ", MAX_NUM_RESENDS, " attempts. Deleted");
it = m_SentMessages.erase (it);
}
2014-07-20 18:38:39 +04:00
}
2015-02-08 16:50:05 +03:00
else
it++;
2014-07-20 18:38:39 +04:00
}
2014-07-22 06:13:57 +04:00
ScheduleResend ();
2014-07-20 18:38:39 +04:00
}
}
2015-02-08 16:50:05 +03:00
void SSUData::ScheduleDecay ()
{
m_DecayTimer.cancel ();
m_DecayTimer.expires_from_now (boost::posix_time::seconds(DECAY_INTERVAL));
auto s = m_Session.shared_from_this();
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleDecayTimer (ecode); });
}
void SSUData::HandleDecayTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
m_ReceivedMessages.clear ();
}
void SSUData::ScheduleIncompleteMessagesCleanup ()
{
m_IncompleteMessagesCleanupTimer.cancel ();
m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT));
auto s = m_Session.shared_from_this();
m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode)
{ s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); });
}
void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
2015-02-08 16:50:05 +03:00
{
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
{
2015-02-08 17:15:13 +03:00
LogPrint (eLogError, "SSU message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds. Deleted");
it = m_IncompleteMessages.erase (it);
2015-02-08 16:50:05 +03:00
}
else
it++;
}
ScheduleIncompleteMessagesCleanup ();
}
}
2014-04-22 19:39:26 +04:00
}
}