diff --git a/src/network/doc/packets.rst b/src/network/doc/packets.rst index f045f8c18..c08531aae 100644 --- a/src/network/doc/packets.rst +++ b/src/network/doc/packets.rst @@ -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 diff --git a/src/network/model/byte-tag-list.cc b/src/network/model/byte-tag-list.cc index e54e16c01..a9822a15c 100644 --- a/src/network/model/byte-tag-list.cc +++ b/src/network/model/byte-tag-list.cc @@ -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 (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 (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 diff --git a/src/network/model/byte-tag-list.h b/src/network/model/byte-tag-list.h index 3841b86ea..1678cee27 100644 --- a/src/network/model/byte-tag-list.h +++ b/src/network/model/byte-tag-list.h @@ -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: /** diff --git a/src/network/model/packet-tag-list.cc b/src/network/model/packet-tag-list.cc index 06b47dd69..e522c91ab 100644 --- a/src/network/model/packet-tag-list.cc +++ b/src/network/model/packet-tag-list.cc @@ -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 */ diff --git a/src/network/model/packet-tag-list.h b/src/network/model/packet-tag-list.h index a40f63de7..c32690db1 100644 --- a/src/network/model/packet-tag-list.h +++ b/src/network/model/packet-tag-list.h @@ -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: /** diff --git a/src/network/model/packet.cc b/src/network/model/packet.cc index 876bcf191..5eeb99593 100644 --- a/src/network/model/packet.cc +++ b/src/network/model/packet.cc @@ -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 nix = Create (); @@ -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 (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 (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); } diff --git a/src/network/test/packet-test-suite.cc b/src/network/test/packet-test-suite.cc index d296e30e6..834cc2962 100644 --- a/src/network/test/packet-test-suite.cc +++ b/src/network/test/packet-test-suite.cc @@ -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 p, const char *file, int line, uint32_t n, ...); + void DoCheckData (Ptr p, const char *file, int line, uint32_t n, ...); }; @@ -474,6 +494,45 @@ PacketTest::DoCheck (Ptr p, const char *file, int line, uint32_t n NS_TEST_EXPECT_MSG_EQ (j, expected.size (), "Size match"); } +void +PacketTest::DoCheckData (Ptr p, const char *file, int line, uint32_t n, ...) +{ + std::vector 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 (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 p1 = Create (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 p2 = Create (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 p1 = Create (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 p2 = Create (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}