network: (merges !413) Add serialization for byte and packet tags

This commit is contained in:
Steven Smith
2020-09-29 14:43:17 -07:00
committed by Tom Henderson
parent eeadfbb182
commit df89d8fafd
7 changed files with 614 additions and 33 deletions

View File

@@ -339,10 +339,6 @@ behavior of packet tags and byte tags.
from the packet. Removing one of possibly multiple byte tags is not supported
by the current API.
As of *ns-3.5* and later, Tags are not serialized and deserialized to a buffer when
``Packet::Serialize ()`` and ``Packet::Deserialize ()`` are called; this is an
open bug.
If a user wants to take an existing packet object and reuse it as a new packet,
he or she should remove all byte tags and packet tags before doing so. An
example is the UdpEchoServer class, which takes the received packet and "turns

View File

@@ -424,5 +424,179 @@ ByteTagList::Deallocate (struct ByteTagListData *data)
#endif /* USE_FREE_LIST */
uint32_t
ByteTagList::GetSerializedSize (void) const
{
NS_LOG_FUNCTION_NOARGS ();
uint32_t size = 0;
// Number of tags in list
size += 4; // numberOfTags
ByteTagList::Iterator i = BeginAll ();
while (i.HasNext ())
{
ByteTagList::Iterator::Item item = i.Next ();
// TypeId hash; ensure size is multiple of 4 bytes
uint32_t hashSize = (sizeof (TypeId::hash_t)+3) & (~3);
size += hashSize;
size += 3 * 4; // size, start, end
// tag data; ensure size is multiple of 4 bytes
uint32_t tagWordSize = (item.size+3) & (~3);
size += tagWordSize;
}
return size;
}
uint32_t
ByteTagList::Serialize (uint32_t* buffer, uint32_t maxSize) const
{
NS_LOG_FUNCTION (this << buffer << maxSize);
uint32_t* p = buffer;
uint32_t size = 0;
uint32_t* numberOfTags = 0;
if (size + 4 <= maxSize)
{
numberOfTags = p;
*p++ = 0;
size += 4;
}
else
{
return 0;
}
ByteTagList::Iterator i = BeginAll ();
while (i.HasNext ())
{
ByteTagList::Iterator::Item item = i.Next ();
NS_LOG_INFO ("Serializing " << item.tid);
// ensure size is multiple of 4 bytes for 4 byte boundaries
uint32_t hashSize = (sizeof (TypeId::hash_t)+3) & (~3);
if (size + hashSize <= maxSize)
{
TypeId::hash_t tid = item.tid.GetHash ();
memcpy (p, &tid, sizeof (TypeId::hash_t));
p += hashSize / 4;
size += hashSize;
}
else
{
return 0;
}
if (size + 4 <= maxSize)
{
*p++ = item.size;
size += 4;
}
else
{
return 0;
}
if (size + 4 <= maxSize)
{
*p++ = item.start;
size += 4;
}
else
{
return 0;
}
if (size + 4 <= maxSize)
{
*p++ = item.end;
size += 4;
}
else
{
return 0;
}
// ensure size is multiple of 4 bytes for 4 byte boundaries
uint32_t tagWordSize = (item.size+3) & (~3);
if (size + tagWordSize <= maxSize)
{
item.buf.Read (reinterpret_cast<uint8_t *> (p), item.size);
size += tagWordSize;
p += tagWordSize / 4;
}
else
{
return 0;
}
(*numberOfTags)++;
}
// Serialized successfully
return 1;
}
uint32_t
ByteTagList::Deserialize (const uint32_t* buffer, uint32_t size)
{
NS_LOG_FUNCTION (this << buffer << size);
const uint32_t* p = buffer;
uint32_t sizeCheck = size - 4;
NS_ASSERT (sizeCheck >= 4);
uint32_t numberTagData = *p++;
sizeCheck -= 4;
NS_LOG_INFO ("Deserializing number of tags " << numberTagData);
for(uint32_t i = 0; i < numberTagData; ++i)
{
uint32_t hashSize = (sizeof (TypeId::hash_t)+3) & (~3);
NS_ASSERT (sizeCheck >= hashSize);
TypeId::hash_t hash;
memcpy (&hash, p, sizeof (TypeId::hash_t));
p += hashSize / 4;
sizeCheck -= hashSize;
TypeId tid = TypeId::LookupByHash (hash);
NS_ASSERT (sizeCheck >= 4);
uint32_t bufferSize = *p++;
sizeCheck -= 4;
NS_ASSERT (sizeCheck >= 4);
uint32_t start = *p++;
sizeCheck -= 4;
NS_ASSERT (sizeCheck >= 4);
uint32_t end = *p++;
sizeCheck -= 4;
NS_ASSERT (sizeCheck >= bufferSize);
TagBuffer buf = Add (tid, bufferSize, start, end);
buf.Write ( reinterpret_cast<const uint8_t *> (p), bufferSize);
// ensure 4 byte boundary
uint32_t tagSizeBytes = (bufferSize+3) & (~3);
sizeCheck -= tagSizeBytes;
p += tagSizeBytes / 4;
}
NS_ASSERT (sizeCheck == 0);
// return zero if buffer did not
// contain a complete message
return (sizeCheck != 0) ? 0 : 1;
}
} // namespace ns3

View File

@@ -230,6 +230,30 @@ private:
*
*/
void AddAtStart (int32_t prependOffset);
/**
* Returns number of bytes required for packet serialization.
*
* \returns number of bytes required for packet serialization
*/
uint32_t GetSerializedSize (void) const;
/**
* Serialize the tag list into a byte buffer.
*
* \param [in,out] buffer The byte buffer to which the tag list will be serialized
* \param [in] maxSize Max The max size of the buffer for bounds checking
*
* \returns zero if complete tag list is not serialized
*/
uint32_t Serialize (uint32_t* buffer, uint32_t maxSize) const;
/**
* Deserialize tag list from the provided buffer.
*
* \param [in] buffer The buffer to read from.
* \param [in] size The number of bytes to deserialize.
*
* \returns zero if complete tag list is not deserialized
*/
uint32_t Deserialize (const uint32_t* buffer, uint32_t size);
private:
/**

View File

@@ -256,12 +256,12 @@ PacketTagList::ReplaceWriter (Tag & tag, bool preMerge,
return found;
}
void
void
PacketTagList::Add (const Tag &tag) const
{
NS_LOG_FUNCTION (this << tag.GetInstanceTypeId ());
// ensure this id was not yet added
for (struct TagData *cur = m_next; cur != 0; cur = cur->next)
for (struct TagData *cur = m_next; cur != 0; cur = cur->next)
{
NS_ASSERT_MSG (cur->tid != tag.GetInstanceTypeId (),
"Error: cannot add the same kind of tag twice.");
@@ -281,9 +281,9 @@ PacketTagList::Peek (Tag &tag) const
{
NS_LOG_FUNCTION (this << tag.GetInstanceTypeId ());
TypeId tid = tag.GetInstanceTypeId ();
for (struct TagData *cur = m_next; cur != 0; cur = cur->next)
for (struct TagData *cur = m_next; cur != 0; cur = cur->next)
{
if (cur->tid == tid)
if (cur->tid == tid)
{
/* found tag */
tag.Deserialize (TagBuffer (cur->data, cur->data + cur->size));
@@ -300,5 +300,164 @@ PacketTagList::Head (void) const
return m_next;
}
uint32_t
PacketTagList::GetSerializedSize (void) const
{
NS_LOG_FUNCTION_NOARGS ();
uint32_t size = 0;
size = 4; // numberOfTags
for (struct TagData *cur = m_next; cur != 0; cur = cur->next)
{
size += 4; // TagData -> size
// TypeId hash; ensure size is multiple of 4 bytes
uint32_t hashSize = (sizeof (TypeId::hash_t)+3) & (~3);
size += hashSize;
// TagData -> data; ensure size is multiple of 4 bytes
uint32_t tagWordSize = (cur->size+3) & (~3);
size += tagWordSize;
}
return size;
}
uint32_t
PacketTagList::Serialize (uint32_t* buffer, uint32_t maxSize) const
{
NS_LOG_FUNCTION (this << buffer << maxSize);
uint32_t* p = buffer;
uint32_t size = 0;
uint32_t* numberOfTags = 0;
if (size + 4 <= maxSize)
{
numberOfTags = p;
*p++ = 0;
size += 4;
}
else
{
return 0;
}
for (struct TagData *cur = m_next; cur != 0; cur = cur->next)
{
if (size + 4 <= maxSize)
{
*p++ = cur->size;
size += 4;
}
else
{
return 0;
}
NS_LOG_INFO("Serializing tag id " << cur->tid);
// ensure size is multiple of 4 bytes for 4 byte boundaries
uint32_t hashSize = (sizeof (TypeId::hash_t)+3) & (~3);
if (size + hashSize <= maxSize)
{
TypeId::hash_t tid = cur->tid.GetHash ();
memcpy (p, &tid, sizeof (TypeId::hash_t));
p += hashSize / 4;
size += hashSize;
}
else
{
return 0;
}
// ensure size is multiple of 4 bytes for 4 byte boundaries
uint32_t tagWordSize = (cur->size+3) & (~3);
if (size + tagWordSize <= maxSize)
{
memcpy (p, cur->data, cur->size);
size += tagWordSize;
p += tagWordSize / 4;
}
else
{
return 0;
}
(*numberOfTags)++;
}
// Serialized successfully
return 1;
}
uint32_t
PacketTagList::Deserialize (const uint32_t* buffer, uint32_t size)
{
NS_LOG_FUNCTION (this << buffer << size);
const uint32_t* p = buffer;
uint32_t sizeCheck = size - 4;
NS_ASSERT (sizeCheck >= 4);
uint32_t numberOfTags = *p++;
sizeCheck -= 4;
NS_LOG_INFO("Deserializing number of tags " << numberOfTags);
struct TagData * prevTag = 0;
for (uint32_t i = 0; i < numberOfTags; ++i)
{
NS_ASSERT (sizeCheck >= 4);
uint32_t tagSize = *p++;
sizeCheck -= 4;
uint32_t hashSize = (sizeof (TypeId::hash_t)+3) & (~3);
NS_ASSERT (sizeCheck >= hashSize);
TypeId::hash_t hash;
memcpy (&hash, p, sizeof (TypeId::hash_t));
p += hashSize / 4;
sizeCheck -= hashSize;
TypeId tid = TypeId::LookupByHash(hash);
NS_LOG_INFO ("Deserializing tag of type " << tid);
struct TagData * newTag = CreateTagData (tagSize);
newTag->count = 1;
newTag->next = 0;
newTag->tid = tid;
NS_ASSERT (sizeCheck >= tagSize);
memcpy (newTag->data, p, tagSize);
// ensure 4 byte boundary
uint32_t tagWordSize = (tagSize+3) & (~3);
p += tagWordSize / 4;
sizeCheck -= tagWordSize;
// Set link list pointers.
if (i==0)
{
m_next = newTag;
}
else
{
prevTag->next = newTag;
}
prevTag = newTag;
}
NS_ASSERT (sizeCheck == 0);
// return zero if buffer did not
// contain a complete message
return (sizeCheck != 0) ? 0 : 1;
}
} /* namespace ns3 */

View File

@@ -217,6 +217,30 @@ public:
* \returns pointer to head of tag list
*/
const struct PacketTagList::TagData *Head (void) const;
/**
* Returns number of bytes required for packet serialization.
*
* \returns number of bytes required for packet serialization
*/
uint32_t GetSerializedSize (void) const;
/**
* Serialize the tag list into a byte buffer.
*
* \param [in,out] buffer The byte buffer to which the tag list will be serialized
* \param [in] maxSize Max The max size of the buffer for bounds checking
*
* \returns zero if complete tag list is not serialized
*/
uint32_t Serialize (uint32_t* buffer, uint32_t maxSize) const;
/**
* Deserialize tag list from the provided buffer.
*
* \param [in] buffer The buffer to read from.
* \param [in] size The number of bytes to deserialize.
*
* \returns zero if complete tag list is not deserialized
*/
uint32_t Deserialize (const uint32_t* buffer, uint32_t size);
private:
/**

View File

@@ -603,9 +603,19 @@ uint32_t Packet::GetSerializedSize (void) const
size += 4;
}
//Tag size
/// \todo Serialze Tags size
//size += m_tags.GetSerializedSize ();
// increment total size by size of packet tag list
// ensuring 4-byte boundary
size += ((m_packetTagList.GetSerializedSize () + 3) & (~3));
// add 4-bytes for entry of total length of packet tag list
size += 4;
// increment total size by size of byte tag list
// ensuring 4-byte boundary
size += ((m_byteTagList.GetSerializedSize () + 3) & (~3));
// add 4-bytes for entry of total length of byte tag list
size += 4;
// increment total size by size of meta-data
// ensuring 4-byte boundary
@@ -677,8 +687,61 @@ Packet::Serialize (uint8_t* buffer, uint32_t maxSize) const
}
}
// Serialize Tags
/// \todo Serialize Tags
// Serialize byte tag list
uint32_t byteTagSize = m_byteTagList.GetSerializedSize ();
if (size + byteTagSize <= maxSize)
{
// put the total length of byte tag list in the
// buffer. this includes 4-bytes for total
// length itself
*p++ = byteTagSize + 4;
size += byteTagSize;
// serialize the byte tag list
uint32_t serialized = m_byteTagList.Serialize (p, byteTagSize);
if (serialized)
{
// increment p by byteTagSize bytes
// ensuring 4-byte boundary
p += ((byteTagSize+3) & (~3)) / 4;
}
else
{
return 0;
}
}
else
{
return 0;
}
// Serialize packet tag list
uint32_t packetTagSize = m_packetTagList.GetSerializedSize ();
if (size + packetTagSize <= maxSize)
{
// put the total length of packet tag list in the
// buffer. this includes 4-bytes for total
// length itself
*p++ = packetTagSize + 4;
size += packetTagSize;
// serialize the packet tag list
uint32_t serialized = m_packetTagList.Serialize (p, packetTagSize);
if (serialized)
{
// increment p by packetTagSize bytes
// ensuring 4-byte boundary
p += ((packetTagSize+3) & (~3)) / 4;
}
else
{
return 0;
}
}
else
{
return 0;
}
// Serialize Metadata
uint32_t metaSize = m_metadata.GetSerializedSize ();
@@ -713,7 +776,7 @@ Packet::Serialize (uint8_t* buffer, uint32_t maxSize) const
if (size + bufSize <= maxSize)
{
// put the total length of the buffer in the
// buffer. this includes 4-bytes for total
// buffer. this includes 4-bytes for total
// length itself
*p++ = bufSize + 4;
@@ -748,8 +811,6 @@ Packet::Deserialize (const uint8_t* buffer, uint32_t size)
// will be overrun, assert
NS_ASSERT (size >= nixSize);
size -= nixSize;
if (nixSize > 4)
{
Ptr<NixVector> nix = Create<NixVector> ();
@@ -765,52 +826,84 @@ Packet::Deserialize (const uint8_t* buffer, uint32_t size)
// 4-byte boundary
p += ((((nixSize - 4) + 3) & (~3)) / 4);
}
size -= nixSize;
// read tags
/// \todo Deserialize Tags
//uint32_t tagsDeserialized = m_tags.Deserialize (buffer.Begin ());
//buffer.RemoveAtStart (tagsDeserialized);
// read byte tags
uint32_t byteTagSize = *p++;
// if size less than byteTagSize, the buffer
// will be overrun, assert
NS_ASSERT (size >= byteTagSize);
uint32_t byteTagDeserialized =
m_byteTagList.Deserialize (p, byteTagSize);
if (!byteTagDeserialized)
{
// byte tags not deserialized completely
return 0;
}
// increment p by byteTagSize ensuring
// 4-byte boundary
p += ((((byteTagSize - 4) + 3) & (~3)) / 4);
size -= byteTagSize;
// read packet tags
uint32_t packetTagSize = *p++;
// if size less than packetTagSize, the buffer
// will be overrun, assert
NS_ASSERT (size >= packetTagSize);
uint32_t packetTagDeserialized =
m_packetTagList.Deserialize (p, packetTagSize);
if (!packetTagDeserialized)
{
// packet tags not deserialized completely
return 0;
}
// increment p by packetTagSize ensuring
// 4-byte boundary
p += ((((packetTagSize - 4) + 3) & (~3)) / 4);
size -= packetTagSize;
// read metadata
uint32_t metaSize = *p++;
// if size less than metaSize, the buffer
// if size less than metaSize, the buffer
// will be overrun, assert
NS_ASSERT (size >= metaSize);
size -= metaSize;
uint32_t metadataDeserialized =
uint32_t metadataDeserialized =
m_metadata.Deserialize (reinterpret_cast<const uint8_t *> (p), metaSize);
if (!metadataDeserialized)
{
// meta-data not deserialized
// meta-data not deserialized
// completely
return 0;
}
// increment p by metaSize ensuring
// increment p by metaSize ensuring
// 4-byte boundary
p += ((((metaSize - 4) + 3) & (~3)) / 4);
size -= metaSize;
// read buffer contents
uint32_t bufSize = *p++;
// if size less than bufSize, the buffer
// if size less than bufSize, the buffer
// will be overrun, assert
NS_ASSERT (size >= bufSize);
size -= bufSize;
uint32_t bufferDeserialized =
m_buffer.Deserialize (reinterpret_cast<const uint8_t *> (p), bufSize);
if (!bufferDeserialized)
{
// buffer not deserialized
// buffer not deserialized
// completely
return 0;
}
size -= bufSize;
// return zero if did not deserialize the
// return zero if did not deserialize the
// number of expected bytes
return (size == 0);
}

View File

@@ -394,21 +394,40 @@ struct Expected
* \param end_ End
*/
Expected (uint32_t n_, uint32_t start_, uint32_t end_)
: n (n_), start (start_), end (end_) {}
: n (n_), start (start_), end (end_), data(0) {}
/**
* Constructor
* \param n_ Number of elements
* \param start_ Start
* \param end_ End
* \param data_ Data stored in tag
*/
Expected (uint32_t n_, uint32_t start_, uint32_t end_, uint8_t data_)
: n (n_), start (start_), end (end_), data(data_) {}
uint32_t n; //!< Number of elements
uint32_t start; //!< Start
uint32_t end; //!< End
uint8_t data; //!< Optional data
};
}
// tag name, start, end
#define E(a,b,c) a,b,c
#define E(name,start,end) name,start,end
// tag name, start, end, data
#define E_DATA(name,start,end,data) name,start,end,data
// Check byte tags on a packet, checks name, start, end
#define CHECK(p, n, ...) \
DoCheck (p, __FILE__, __LINE__, n, __VA_ARGS__)
// Check byte tags on a packet, checks name, start, end, data
#define CHECK_DATA(p, n, ...) \
DoCheckData (p, __FILE__, __LINE__, n, __VA_ARGS__)
/**
* \ingroup network-test
* \ingroup tests
@@ -430,6 +449,7 @@ private:
* \param ... The variable arguments
*/
void DoCheck (Ptr<const Packet> p, const char *file, int line, uint32_t n, ...);
void DoCheckData (Ptr<const Packet> p, const char *file, int line, uint32_t n, ...);
};
@@ -474,6 +494,45 @@ PacketTest::DoCheck (Ptr<const Packet> p, const char *file, int line, uint32_t n
NS_TEST_EXPECT_MSG_EQ (j, expected.size (), "Size match");
}
void
PacketTest::DoCheckData (Ptr<const Packet> p, const char *file, int line, uint32_t n, ...)
{
std::vector<struct Expected> expected;
va_list ap;
va_start (ap, n);
for (uint32_t k = 0; k < n; ++k)
{
uint32_t N = va_arg (ap, uint32_t);
uint32_t start = va_arg (ap, uint32_t);
uint32_t end = va_arg (ap, uint32_t);
int data = va_arg (ap, int);
expected.push_back (Expected (N, start, end, data));
}
va_end (ap);
ByteTagIterator i = p->GetByteTagIterator ();
uint32_t j = 0;
while (i.HasNext () && j < expected.size ())
{
ByteTagIterator::Item item = i.Next ();
struct Expected e = expected[j];
std::ostringstream oss;
oss << "anon::ATestTag<" << e.n << ">";
NS_TEST_EXPECT_MSG_EQ_INTERNAL (item.GetTypeId ().GetName (), oss.str (), "trivial", file, line);
NS_TEST_EXPECT_MSG_EQ_INTERNAL (item.GetStart (), e.start, "trivial", file, line);
NS_TEST_EXPECT_MSG_EQ_INTERNAL (item.GetEnd (), e.end, "trivial", file, line);
ATestTagBase *tag = dynamic_cast<ATestTagBase *> (item.GetTypeId ().GetConstructor () ());
NS_TEST_EXPECT_MSG_NE (tag, 0, "trivial");
item.GetTag (*tag);
NS_TEST_EXPECT_MSG_EQ (tag->m_error, false, "trivial");
NS_TEST_EXPECT_MSG_EQ (tag->GetData (), e.data, "trivial");
delete tag;
j++;
}
NS_TEST_EXPECT_MSG_EQ (i.HasNext (), false, "Nothing left");
NS_TEST_EXPECT_MSG_EQ (j, expected.size (), "Size match");
}
void
PacketTest::DoRun (void)
{
@@ -641,6 +700,58 @@ PacketTest::DoRun (void)
NS_TEST_EXPECT_MSG_EQ (p.PeekPacketTag (b), false, "trivial");
}
/* Test Serialization and Deserialization of Packet with PacketTag data */
{
Ptr<Packet> p1 = Create<Packet> (1000);;
ATestTag<10> a1(65);
ATestTag<11> b1(66);
ATestTag<12> c1(67);
p1->AddPacketTag (a1);
p1->AddPacketTag (b1);
p1->AddPacketTag (c1);
uint32_t serializedSize = p1->GetSerializedSize ();
uint8_t* buffer = new uint8_t[serializedSize + 16];
p1->Serialize (buffer, serializedSize);
Ptr<Packet> p2 = Create<Packet> (buffer, serializedSize, true);
ATestTag<10> a2;
ATestTag<11> b2;
ATestTag<12> c2;
NS_TEST_EXPECT_MSG_EQ (p2 -> PeekPacketTag(a2), true, "trivial");
NS_TEST_EXPECT_MSG_EQ (a2.GetData (), 65, "trivial");
NS_TEST_EXPECT_MSG_EQ (p2 -> PeekPacketTag(b2), true, "trivial");
NS_TEST_EXPECT_MSG_EQ (b2.GetData (), 66, "trivial");
NS_TEST_EXPECT_MSG_EQ (p2 -> PeekPacketTag(c2), true, "trivial");
NS_TEST_EXPECT_MSG_EQ (c2.GetData (), 67, "trivial");
}
/* Test Serialization and Deserialization of Packet with ByteTag data */
{
Ptr<Packet> p1 = Create<Packet> (1000);;
ATestTag<10> a1(65);
ATestTag<11> b1(66);
ATestTag<12> c1(67);
p1->AddByteTag (a1);
p1->AddByteTag (b1);
p1->AddByteTag (c1);
CHECK (p1, 3, E (10, 0, 1000), E (11, 0, 1000), E (12, 0, 1000));
uint32_t serializedSize = p1->GetSerializedSize ();
uint8_t* buffer = new uint8_t[serializedSize];
p1->Serialize (buffer, serializedSize);
Ptr<Packet> p2 = Create<Packet> (buffer, serializedSize, true);
CHECK_DATA (p2, 3, E_DATA (10, 0, 1000, 65), E_DATA (11, 0, 1000, 66), E_DATA (12, 0, 1000, 67));
}
{
/// \internal
/// See \bugid{572}