diff --git a/.hgtags b/.hgtags index 6ffe67f46..35fb54f89 100644 --- a/.hgtags +++ b/.hgtags @@ -1,3 +1,4 @@ 56928998e05c9c11f5f3aefe79be8d2843e0db88 release ns-3.0.1 7ac5a4b0969b255c4824c926c2b37ef450136ce9 release ns-3.0.2 +0dc81e76166c56aaae64da48b673b62155943aad packet-history-working 38099dd26e9467b8f49f8632f22789858149a6e7 release ns-3.0.3 diff --git a/SConstruct b/SConstruct index 44f168c6b..eb287a041 100644 --- a/SConstruct +++ b/SConstruct @@ -187,6 +187,8 @@ common.add_sources([ 'chunk.cc', 'header.cc', 'trailer.cc', + 'packet-printer.cc', + 'packet-metadata.cc', 'packet.cc', 'tags.cc', 'pcap-writer.cc', @@ -208,6 +210,8 @@ common.add_inst_headers([ 'trailer.h', 'tags.h', 'packet.h', + 'packet-printer.h', + 'packet-metadata.h', 'uv-trace-source.h', 'sv-trace-source.h', 'fv-trace-source.h', @@ -303,8 +307,6 @@ inode.add_sources ([ 'udp-impl.cc', ]) inode.add_headers ([ - 'ipv4-header.h', - 'udp-header.h', 'ipv4-checksum.h', 'arp-header.h', 'arp-cache.h', @@ -334,6 +336,8 @@ inode.add_inst_headers ([ 'internet-node.h', 'ascii-trace.h', 'pcap-trace.h', + 'ipv4-header.h', + 'udp-header.h', ]) @@ -367,9 +371,9 @@ bench_object.add_deps(['core']) bench_object.add_source('bench-object.cc') bench_packets = build.Ns3Module('bench-packets', 'utils') -#ns3.add(bench_packets) +ns3.add(bench_packets) bench_packets.set_executable() -bench_packets.add_dep('core') +bench_packets.add_deps (['core', 'common']) bench_packets.add_source('bench-packets.cc') bench_simu = build.Ns3Module('bench-simulator', 'utils') @@ -393,6 +397,13 @@ sample_debug.add_dep('core') sample_debug.add_source('main-debug.cc') sample_debug.add_source('main-debug-other.cc') +sample_packet_printer = build.Ns3Module('sample-packet-printer', 'samples') +sample_packet_printer.set_executable() +ns3.add(sample_packet_printer) +sample_packet_printer.add_deps (['common', 'internet-node']) +sample_packet_printer.add_source('main-packet-printer.cc') + + sample_callback = build.Ns3Module('sample-callback', 'samples') sample_callback.set_executable() ns3.add(sample_callback) diff --git a/samples/main-packet-printer.cc b/samples/main-packet-printer.cc new file mode 100644 index 000000000..5e0612595 --- /dev/null +++ b/samples/main-packet-printer.cc @@ -0,0 +1,149 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +#include "ns3/packet.h" +#include "ns3/header.h" +#include "ns3/packet-printer.h" +#include "ns3/ipv4-header.h" +#include "ns3/udp-header.h" + +using namespace ns3; + + +void DefaultPrint (void) +{ + // We create a packet with 1000 bytes of zero payload + // and add 3 headers to this packet. + Packet p (1000); + Ipv4Header ipv4; + UdpHeader udp; + ipv4.SetSource (Ipv4Address ("192.168.0.1")); + ipv4.SetDestination (Ipv4Address ("192.168.0.2")); + udp.SetSource (1025); + udp.SetDestination (80); + p.AddHeader (udp); + p.AddHeader (ipv4); + + std::cout << "full packet size=" << p.GetSize () << std::endl; + p.Print (std::cout); + std::cout << std::endl; + + + // Now, we fragment our packet in 3 consecutive pieces. + Packet p1 = p.CreateFragment (0, 2); + Packet p2 = p.CreateFragment (2, 1000); + Packet p3 = p.CreateFragment (1002, 26); + + std::cout << "fragment1" << std::endl; + p1.Print (std::cout); + std::cout << std::endl; + std::cout << "fragment2" << std::endl; + p2.Print (std::cout); + std::cout << std::endl; + std::cout << "fragment3" << std::endl; + p3.Print (std::cout); + std::cout << std::endl; + + // And, finally, we re-aggregate the 3 consecutive pieces. + Packet aggregate = p1; + aggregate.AddAtEnd (p2); + aggregate.AddAtEnd (p3); + std::cout << "aggregated" << std::endl; + aggregate.Print (std::cout); + std::cout << std::endl; +} + +void +DoPrintDefault (std::ostream &os,uint32_t packetUid, uint32_t size, + std::string &name, struct PacketPrinter::FragmentInformation info) +{ + os << name <<" (size " << size << " trim_start " << info.start << " trim_end " << info.end << ")"; +} +void +DoPrintPayload (std::ostream & os,uint32_t packetUid,uint32_t size, + struct PacketPrinter::FragmentInformation info) +{ + os << "PAYLOAD (size " << size << " trim_start " << info.start << " trim_end " << info.end << ")"; +} +void +DoPrintIpv4Header (std::ostream &os, uint32_t packetUid, uint32_t size, const Ipv4Header *ipv4) +{ + os << "IPV4 " << ipv4->GetSource () << " > " << ipv4->GetDestination (); +} +void +DoPrintIpv4HeaderFragment (std::ostream &os, uint32_t packetUid, uint32_t size, + std::string &name, struct PacketPrinter::FragmentInformation info) +{ + os << "IPV4 fragment"; +} + +void NonDefaultPrint (void) +{ + // create an adhoc packet printer. + PacketPrinter printer; + // print from first header to last trailer + printer.PrintForward (); + // set a string separator automatically inserted + // between each call to a printing function. + printer.SetSeparator (" - "); + // set the default print function: invoked if no + // specialized function has been provided for a header + // or trailer + printer.AddDefaultPrinter (MakeCallback (&DoPrintDefault)); + // set the payload print function + printer.AddPayloadPrinter (MakeCallback (&DoPrintPayload)); + // set the print function for the header type Ipv4Header. + printer.AddHeaderPrinter (MakeCallback (&DoPrintIpv4Header), + MakeCallback (&DoPrintIpv4HeaderFragment)); + + + // We create a packet with 1000 bytes of zero payload + Packet p (1000); + Ipv4Header ipv4; + UdpHeader udp; + ipv4.SetSource (Ipv4Address ("192.168.0.1")); + ipv4.SetDestination (Ipv4Address ("192.168.0.2")); + udp.SetSource (1025); + udp.SetDestination (80); + p.AddHeader (udp); + p.AddHeader (ipv4); + + std::cout << "full packet size=" << p.GetSize () << std::endl; + p.Print (std::cout, printer); + std::cout << std::endl; + + + // fragment our packet in 3 pieces + Packet p1 = p.CreateFragment (0, 2); + Packet p2 = p.CreateFragment (2, 1000); + Packet p3 = p.CreateFragment (1002, 26); + std::cout << "fragment1" << std::endl; + p1.Print (std::cout, printer); + std::cout << std::endl; + std::cout << "fragment2" << std::endl; + p2.Print (std::cout, printer); + std::cout << std::endl; + std::cout << "fragment3" << std::endl; + p3.Print (std::cout, printer); + std::cout << std::endl; + + // aggregate all 3 fragments of the original packet + // to reconstruct a copy of the original packet. + Packet aggregate = p1; + aggregate.AddAtEnd (p2); + aggregate.AddAtEnd (p3); + std::cout << "aggregated" << std::endl; + aggregate.Print (std::cout, printer); + std::cout << std::endl; +} + + + +int main (int argc, char *argv[]) +{ + Packet::EnableMetadata (); + + DefaultPrint (); + + NonDefaultPrint (); + + return 0; +} diff --git a/samples/main-packet.cc b/samples/main-packet.cc index 01b46097c..777f95815 100644 --- a/samples/main-packet.cc +++ b/samples/main-packet.cc @@ -15,6 +15,7 @@ public: void SetData (uint16_t data); uint16_t GetData (void) const; private: + virtual std::string DoGetName (void) const; virtual void PrintTo (std::ostream &os) const; virtual void SerializeTo (Buffer::Iterator start) const; virtual uint32_t DeserializeFrom (Buffer::Iterator start); @@ -27,6 +28,11 @@ MyHeader::MyHeader () {} MyHeader::~MyHeader () {} +std::string +MyHeader::DoGetName (void) const +{ + return "MyHeader"; +} void MyHeader::PrintTo (std::ostream &os) const { diff --git a/src/common/buffer.cc b/src/common/buffer.cc index 2c64f8920..07a370afa 100644 --- a/src/common/buffer.cc +++ b/src/common/buffer.cc @@ -224,7 +224,7 @@ Buffer::AddAtEnd (uint32_t end) uint32_t newSize = m_size + end; struct Buffer::BufferData *newData = Buffer::Allocate (newSize, 0); memcpy (newData->m_data, GetStart (), m_size); - newData->m_initialStart = m_data->m_initialStart; + newData->m_initialStart = m_data->m_initialStart - m_start; m_data->m_count--; if (m_data->m_count == 0) { @@ -663,6 +663,12 @@ BufferTest::RunTests (void) i.Prev (4); i.WriteU8 (1, 4); + buffer = Buffer (1); + buffer.AddAtEnd (100); + i = buffer.End (); + i.Prev (100); + i.WriteU8 (1, 100); + return ok; } diff --git a/src/common/buffer.h b/src/common/buffer.h index 4986d4998..02f1f69f2 100644 --- a/src/common/buffer.h +++ b/src/common/buffer.h @@ -320,6 +320,8 @@ public: */ inline Buffer::Iterator End (void) const; + void TransformIntoRealBuffer (void) const; + inline Buffer (Buffer const &o); inline Buffer &operator = (Buffer const &o); inline Buffer (); @@ -337,7 +339,6 @@ private: typedef std::vector BufferDataList; inline uint8_t *GetStart (void) const; - void TransformIntoRealBuffer (void) const; static void Recycle (struct Buffer::BufferData *data); static struct Buffer::BufferData *Create (void); static struct Buffer::BufferData *Allocate (uint32_t size, uint32_t start); @@ -543,6 +544,8 @@ Buffer::Iterator::Write (Iterator start, Iterator end) { NS_ASSERT (start.m_data == end.m_data); NS_ASSERT (start.m_current <= end.m_current); + NS_ASSERT (start.m_zeroStart == end.m_zeroStart); + NS_ASSERT (start.m_zeroEnd == end.m_zeroEnd); NS_ASSERT (m_data != start.m_data); uint32_t size = end.m_current - start.m_current; uint8_t *src = start.m_data + start.GetIndex (size); diff --git a/src/common/chunk.cc b/src/common/chunk.cc index 34c6cbce6..52c1112c3 100644 --- a/src/common/chunk.cc +++ b/src/common/chunk.cc @@ -30,6 +30,11 @@ Chunk::Chunk () Chunk::~Chunk () {} +std::string +Chunk::GetName (void) const +{ + return DoGetName (); +} void Chunk::Print (std::ostream &os) const { diff --git a/src/common/chunk.h b/src/common/chunk.h index 9aa025f96..ab568cd9f 100644 --- a/src/common/chunk.h +++ b/src/common/chunk.h @@ -33,11 +33,13 @@ public: Chunk (); virtual ~Chunk (); + std::string GetName (void) const; void Print (std::ostream &os) const; uint32_t GetSize (void) const; void Serialize (Buffer::Iterator start) const; uint32_t Deserialize (Buffer::Iterator start); private: + virtual std::string DoGetName (void) const = 0; virtual void PrintTo (std::ostream &os) const = 0; virtual uint32_t GetSerializedSize (void) const = 0; virtual void SerializeTo (Buffer::Iterator i) const = 0; diff --git a/src/common/data-rate.cc b/src/common/data-rate.cc index 705da04bd..832fa98cb 100644 --- a/src/common/data-rate.cc +++ b/src/common/data-rate.cc @@ -23,9 +23,8 @@ #include "ns3/nstime.h" #include "ns3/fatal-error.h" -namespace { -bool +static bool DoParse (const std::string s, uint64_t *v) { std::string::size_type n = s.find_first_not_of("0123456789."); @@ -123,7 +122,6 @@ DoParse (const std::string s, uint64_t *v) return true; } -} namespace ns3 { diff --git a/src/common/header.h b/src/common/header.h index 0a8eb9643..609729b26 100644 --- a/src/common/header.h +++ b/src/common/header.h @@ -41,9 +41,26 @@ class Header : public Chunk { public: virtual ~Header (); private: + /** + * \returns a user-readable name to identify this type of header. + * + * The string returned is expected to be a single word with + * all capital letters + */ + virtual std::string DoGetName (void) const = 0; /** * \param os the std output stream in which this * protocol header must print itself. + * + * Although the header is free to format its output as it + * wishes, it is recommended to follow a few rules to integrate + * with the packet pretty printer: + * - start with flags, small field values located between a + * pair of parens. Values should be separated by whitespace. + * - follow the parens with the important fields, separated by + * whitespace. + * i.e.: + * (field1 val1 field2 val2 field3 val3) field4 val4 field5 val5 */ virtual void PrintTo (std::ostream &os) const = 0; diff --git a/src/common/packet-metadata.cc b/src/common/packet-metadata.cc new file mode 100644 index 000000000..8839d79a4 --- /dev/null +++ b/src/common/packet-metadata.cc @@ -0,0 +1,1714 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006,2007 INRIA + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ +#include +#include +#include "ns3/assert.h" +#include "ns3/fatal-error.h" +#include "ns3/debug.h" +#include "packet-metadata.h" +#include "chunk.h" +#include "buffer.h" + +NS_DEBUG_COMPONENT_DEFINE ("PacketMetadata"); + +namespace ns3 { + +bool PacketMetadata::m_enable = false; +uint32_t PacketMetadata::m_maxSize = 0; +uint16_t PacketMetadata::m_chunkUid = 0; +PacketMetadata::DataFreeList PacketMetadata::m_freeList; +bool g_optOne = false; + +void +PacketMetadata::Enable (void) +{ + m_enable = true; +} + +void +PacketMetadata::SetOptOne (bool optOne) +{ + g_optOne = optOne; +} + +void +PacketMetadata::ReserveCopy (uint32_t size) +{ + struct PacketMetadata::Data *newData = PacketMetadata::Create (m_used + size); + memcpy (newData->m_data, m_data->m_data, m_used); + newData->m_dirtyEnd = m_used; + m_data->m_count--; + if (m_data->m_count == 0) + { + PacketMetadata::Recycle (m_data); + } + m_data = newData; + if (m_head != 0xffff) + { + uint8_t *start; + NS_ASSERT (m_tail != 0xffff); + // clear the next field of the tail + start = &m_data->m_data[m_tail]; + Append16 (0xffff, start); + // clear the prev field of the head + start = &m_data->m_data[m_head] + 2; + Append16 (0xffff, start); + } +} +void +PacketMetadata::Reserve (uint32_t size) +{ + NS_ASSERT (m_data != 0); + if (m_data->m_size >= m_used + size && + (m_head == 0xffff || + m_data->m_count == 1 || + m_data->m_dirtyEnd == m_used)) + { + /* enough room, not dirty. */ + } + else + { + /* (enough room and dirty) or (not enough room) */ + ReserveCopy (size); + } +} + +uint32_t +PacketMetadata::GetUleb128Size (uint32_t value) const +{ + if (value < 0x80) + { + return 1; + } + if (value < 0x4000) + { + return 2; + } + if (value < 0x200000) + { + return 3; + } + if (value < 0x10000000) + { + return 4; + } + return 5; +} +uint32_t +PacketMetadata::ReadUleb128 (const uint8_t **pBuffer) const +{ + const uint8_t *buffer = *pBuffer; + uint32_t result = 0; + uint8_t byte; + result = 0; + byte = buffer[0]; + result = (byte & (~0x80)); + if (!(byte & 0x80)) + { + *pBuffer = buffer + 1; + return result; + } + byte = buffer[1]; + result |= (byte & (~0x80)) << 7; + if (!(byte & 0x80)) + { + *pBuffer = buffer + 2; + return result; + } + byte = buffer[2]; + result |= (byte & (~0x80)) << 14; + if (!(byte & 0x80)) + { + *pBuffer = buffer + 3; + return result; + } + byte = buffer[3]; + result |= (byte & (~0x80)) << 21; + if (!(byte & 0x80)) + { + *pBuffer = buffer + 4; + return result; + } + byte = buffer[4]; + result |= (byte & (~0x80)) << 28; + if (!(byte & 0x80)) + { + *pBuffer = buffer + 5; + return result; + } + /* This means that the LEB128 number was not valid. + * ie: the last (5th) byte did not have the high-order bit zeroed. + */ + NS_ASSERT (false); + return 0; +} + +void +PacketMetadata::Append16 (uint16_t value, uint8_t *buffer) +{ + buffer[0] = value & 0xff; + value >>= 8; + buffer[1] = value; +} +bool +PacketMetadata::TryToAppendFast (uint32_t value, uint8_t **pBuffer, uint8_t *end) +{ + uint8_t *start = *pBuffer; + if (value < 0x80 && start < end) + { + start[0] = value; + *pBuffer = start + 1; + return true; + } + if (value < 0x4000 && start + 1 < end) + { + uint8_t byte = value & (~0x80); + start[0] = 0x80 | byte; + value >>= 7; + start[1] = value; + *pBuffer = start + 2; + return true; + } + return false; +} +bool +PacketMetadata::TryToAppend16 (uint16_t value, uint8_t **pBuffer, uint8_t *end) +{ + uint8_t *start = *pBuffer; + if (start + 1 < end) + { + start[0] = value & 0xff; + start[1] = value >> 8; + *pBuffer = start + 2; + return true; + } + return false; +} +bool +PacketMetadata::TryToAppend32 (uint32_t value, uint8_t **pBuffer, uint8_t *end) +{ + uint8_t *start = *pBuffer; + if (start + 3 < end) + { + start[0] = value & 0xff; + start[1] = (value >> 8) & 0xff; + start[2] = (value >> 16) & 0xff; + start[3] = (value >> 24) & 0xff; + *pBuffer = start + 4; + return true; + } + return false; +} +bool +PacketMetadata::TryToAppend (uint32_t value, uint8_t **pBuffer, uint8_t *end) +{ + uint8_t *start = *pBuffer; + if (value < 0x80 && start < end) + { + start[0] = value; + *pBuffer = start + 1; + return true; + } + if (value < 0x4000 && start + 1 < end) + { + uint8_t byte = value & (~0x80); + start[0] = 0x80 | byte; + value >>= 7; + start[1] = value; + *pBuffer = start + 2; + return true; + } + if (value < 0x200000 && start + 2 < end) + { + uint8_t byte = value & (~0x80); + start[0] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + start[1] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + start[2] = value; + *pBuffer = start + 3; + return true; + } + if (value < 0x10000000 && start + 3 < end) + { + uint8_t byte = value & (~0x80); + start[0] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + start[1] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + start[2] = 0x80 | byte; + value >>= 7; + start[3] = value; + *pBuffer = start + 4; + return true; + } + if (start + 4 < end) + { + uint8_t byte = value & (~0x80); + start[0] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + start[1] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + start[2] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + start[3] = 0x80 | byte; + value >>= 7; + start[4] = value; + *pBuffer = start + 5; + return true; + } + return false; +} + +void +PacketMetadata::AppendValueExtra (uint32_t value, uint8_t *buffer) +{ + if (value < 0x200000) + { + uint8_t byte = value & (~0x80); + buffer[0] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + buffer[1] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + buffer[2] = value; + return; + } + if (value < 0x10000000) + { + uint8_t byte = value & (~0x80); + buffer[0] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + buffer[1] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + buffer[2] = 0x80 | byte; + value >>= 7; + buffer[3] = value; + return; + } + { + uint8_t byte = value & (~0x80); + buffer[0] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + buffer[1] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + buffer[2] = 0x80 | byte; + value >>= 7; + byte = value & (~0x80); + buffer[3] = 0x80 | byte; + value >>= 7; + buffer[4] = value; + } +} + +void +PacketMetadata::AppendValue (uint32_t value, uint8_t *buffer) +{ + if (value < 0x80) + { + buffer[0] = value; + return; + } + if (value < 0x4000) + { + uint8_t byte = value & (~0x80); + buffer[0] = 0x80 | byte; + value >>= 7; + buffer[1] = value; + return; + } + AppendValueExtra (value, buffer); +} + +void +PacketMetadata::UpdateTail (uint16_t written) +{ + if (m_head == 0xffff) + { + NS_ASSERT (m_tail == 0xffff); + m_head = m_used; + m_tail = m_used; + } + else + { + NS_ASSERT (m_tail != 0xffff); + // overwrite the next field of the previous tail of the list. + uint8_t *previousTail = &m_data->m_data[m_tail]; + Append16 (m_used, previousTail); + // update the tail of the list to the new node. + m_tail = m_used; + } + NS_ASSERT (m_tail != 0xffff); + NS_ASSERT (m_head != 0xffff); + NS_ASSERT (written >= 8); + m_used += written; + m_data->m_dirtyEnd = m_used; +} + + +void +PacketMetadata::UpdateHead (uint16_t written) +{ + if (m_head == 0xffff) + { + NS_ASSERT (m_tail == 0xffff); + m_head = m_used; + m_tail = m_used; + } + else + { + NS_ASSERT (m_head != 0xffff); + // overwrite the prev field of the previous head of the list. + uint8_t *previousHead = &m_data->m_data[m_head + 2]; + Append16 (m_used, previousHead); + // update the head of list to the new node. + m_head = m_used; + } + NS_ASSERT (m_tail != 0xffff); + NS_ASSERT (m_head != 0xffff); + NS_ASSERT (written >= 8); + m_used += written; + m_data->m_dirtyEnd = m_used; +} + +uint16_t +PacketMetadata::AddSmall (const struct PacketMetadata::SmallItem *item) +{ + NS_ASSERT (m_data != 0); + NS_ASSERT (m_used != item->prev && m_used != item->next); + if (g_optOne) + { + uint32_t typeUidSize = GetUleb128Size (item->typeUid); + uint32_t sizeSize = GetUleb128Size (item->size); + uint32_t n = typeUidSize + sizeSize + 2 + 2 + 2; + restart: + if (m_used + n <= m_data->m_size && + (m_head == 0xffff || + m_data->m_count == 1 || + m_used == m_data->m_dirtyEnd)) + { + uint8_t *buffer = &m_data->m_data[m_used]; + Append16 (item->next, buffer); + buffer += 2; + Append16 (item->prev, buffer); + buffer += 2; + AppendValue (item->typeUid, buffer); + buffer += typeUidSize; + AppendValue (item->size, buffer); + buffer += sizeSize; + Append16 (item->chunkUid, buffer); + } + else + { + ReserveCopy (n); + goto restart; + } + return n; + } + append: + uint8_t *start = &m_data->m_data[m_used]; + uint8_t *end = &m_data->m_data[m_data->m_size]; + if (end - start >= 8 && + (m_head == 0xffff || + m_data->m_count == 1 || + m_used == m_data->m_dirtyEnd)) + { + uint8_t *buffer = start; + + Append16 (item->next, buffer); + buffer += 2; + Append16 (item->prev, buffer); + buffer += 2; + if (TryToAppendFast (item->typeUid, &buffer, end) && + TryToAppendFast (item->size, &buffer, end) && + TryToAppend16 (item->chunkUid, &buffer, end)) + { + uintptr_t written = buffer - start; + NS_ASSERT (written <= 0xffff); + NS_ASSERT (written >= 8); + return written; + } + } + uint32_t n = GetUleb128Size (item->typeUid); + n += GetUleb128Size (item->size); + n += 2; + n += 2 + 2; + Reserve (n); + goto append; +} + +uint16_t +PacketMetadata::AddBig (uint32_t next, uint32_t prev, + const PacketMetadata::SmallItem *item, + const PacketMetadata::ExtraItem *extraItem) +{ + NS_ASSERT (m_data != 0); + uint32_t typeUid = ((item->typeUid & 0x1) == 0x1)?item->typeUid:item->typeUid+1; + NS_ASSERT (m_used != prev && m_used != next); + append: + uint8_t *start = &m_data->m_data[m_used]; + uint8_t *end = &m_data->m_data[m_data->m_size]; + if (end - start >= 14 && + (m_head == 0xffff || + m_data->m_count == 1 || + m_used == m_data->m_dirtyEnd)) + { + uint8_t *buffer = start; + + Append16 (next, buffer); + buffer += 2; + Append16 (prev, buffer); + buffer += 2; + if (TryToAppend (typeUid, &buffer, end) && + TryToAppend (item->size, &buffer, end) && + TryToAppend16 (item->chunkUid, &buffer, end) && + TryToAppend (extraItem->fragmentStart, &buffer, end) && + TryToAppend (extraItem->fragmentEnd, &buffer, end) && + TryToAppend32 (extraItem->packetUid, &buffer, end)) + { + uintptr_t written = buffer - start; + NS_ASSERT (written <= 0xffff); + NS_ASSERT (written >= 14); + return written; + } + } + + uint32_t n = GetUleb128Size (typeUid); + n += GetUleb128Size (item->size); + n += 2; + n += GetUleb128Size (extraItem->fragmentStart); + n += GetUleb128Size (extraItem->fragmentEnd); + n += 4; + n += 2 + 2; + ReserveCopy (n); + goto append; +} + +void +PacketMetadata::ReplaceTail (PacketMetadata::SmallItem *item, + PacketMetadata::ExtraItem *extraItem, + uint32_t available) +{ + NS_ASSERT (m_data != 0); + if (available >= 14 && + m_data->m_count == 1) + { + uint8_t *buffer = &m_data->m_data[m_tail]; + uint8_t *end = buffer + available; + + Append16 (item->next, buffer); + buffer += 2; + Append16 (item->prev, buffer); + buffer += 2; + if (TryToAppend (item->typeUid, &buffer, end) && + TryToAppend (item->size, &buffer, end) && + TryToAppend16 (item->chunkUid, &buffer, end) && + TryToAppend (extraItem->fragmentStart, &buffer, end) && + TryToAppend (extraItem->fragmentEnd, &buffer, end) && + TryToAppend32 (extraItem->packetUid, &buffer, end)) + { + m_used = buffer - &m_data->m_data[0]; + m_data->m_dirtyEnd = m_used; + return; + } + } + + // create a copy of the packet. + PacketMetadata h (m_packetUid, 0); + uint16_t current = m_head; + while (current != 0xffff && current != m_tail) + { + struct PacketMetadata::SmallItem tmpItem; + PacketMetadata::ExtraItem tmpExtraItem; + ReadItems (current, &tmpItem, &tmpExtraItem); + uint16_t written = h.AddBig (0xffff, h.m_tail, + &tmpItem, &tmpExtraItem); + h.UpdateTail (written); + } + // append new tail. + uint16_t written = h.AddBig (0xffff, h.m_tail, item, extraItem); + h.UpdateTail (written); + + *this = h; +} + +uint32_t +PacketMetadata::ReadItems (uint16_t current, + struct PacketMetadata::SmallItem *item, + struct PacketMetadata::ExtraItem *extraItem) const +{ + const uint8_t *buffer = &m_data->m_data[current]; + item->next = buffer[0]; + item->next |= (buffer[1]) << 8; + item->prev = buffer[2]; + item->prev |= (buffer[3]) << 8; + buffer += 4; + item->typeUid = ReadUleb128 (&buffer); + item->size = ReadUleb128 (&buffer); + item->chunkUid = buffer[0]; + item->chunkUid |= (buffer[1]) << 8; + buffer += 2; + + bool isExtra = (item->typeUid & 0x1) == 0x1; + if (isExtra) + { + extraItem->fragmentStart = ReadUleb128 (&buffer); + extraItem->fragmentEnd = ReadUleb128 (&buffer); + extraItem->packetUid = buffer[0]; + extraItem->packetUid |= buffer[1] << 8; + extraItem->packetUid |= buffer[2] << 16; + extraItem->packetUid |= buffer[3] << 24; + buffer += 4; + } + else + { + extraItem->fragmentStart = 0; + extraItem->fragmentEnd = item->size; + extraItem->packetUid = m_packetUid; + } + NS_ASSERT (buffer <= &m_data->m_data[m_data->m_size]); + return buffer - &m_data->m_data[current]; +} + +struct PacketMetadata::Data * +PacketMetadata::Create (uint32_t size) +{ + NS_DEBUG ("create size="<m_dirtyEnd) + { + lastTailSize = m_data->m_size - m_tail; + } + + uint16_t current = o.m_head; + while (current != 0xffff) + { + struct PacketMetadata::SmallItem item; + PacketMetadata::ExtraItem extraItem; + o.ReadItems (current, &item, &extraItem); + if (extraItem.packetUid == lastExtraItem.packetUid && + item.typeUid == lastItem.typeUid && + item.chunkUid == lastItem.chunkUid && + item.size == lastItem.size && + extraItem.fragmentStart == lastExtraItem.fragmentEnd) + { + // replace previous tail. + lastExtraItem.fragmentEnd = extraItem.fragmentEnd; + NS_ASSERT (m_tail == lastTail); + // XXX This call might be wrong. + ReplaceTail (&lastItem, &lastExtraItem, lastTailSize); + } + else + { + // append the extra items. + uint16_t written = AddBig (0xffff, m_tail, &item, &extraItem); + UpdateTail (written); + } + if (current == o.m_tail) + { + break; + } + current = item.next; + } +} +void +PacketMetadata::AddPaddingAtEnd (uint32_t end) +{ + if (!m_enable) + { + return; + } +} +void +PacketMetadata::RemoveAtStart (uint32_t start) +{ + if (!m_enable) + { + return; + } + NS_ASSERT (m_data != 0); + uint32_t leftToRemove = start; + uint16_t current = m_head; + while (current != 0xffff && leftToRemove > 0) + { + struct PacketMetadata::SmallItem item; + PacketMetadata::ExtraItem extraItem; + ReadItems (current, &item, &extraItem); + uint32_t itemRealSize = extraItem.fragmentEnd - extraItem.fragmentStart; + if (itemRealSize <= leftToRemove) + { + // remove from list. + m_head = item.next; + leftToRemove -= itemRealSize; + } + else + { + // fragment the list item. + PacketMetadata fragment (m_packetUid, 0); + extraItem.fragmentStart += leftToRemove; + leftToRemove = 0; + uint16_t written = fragment.AddBig (0xffff, fragment.m_tail, + &item, &extraItem); + fragment.UpdateTail (written); + current = item.next; + while (current != 0xffff) + { + ReadItems (current, &item, &extraItem); + written = fragment.AddBig (0xffff, fragment.m_tail, + &item, &extraItem); + fragment.UpdateTail (written); + if (current == m_tail) + { + break; + } + current = item.next; + } + *this = fragment; + } + NS_ASSERT (item.size >= extraItem.fragmentEnd - extraItem.fragmentStart && + extraItem.fragmentStart <= extraItem.fragmentEnd); + if (current == m_tail) + { + break; + } + current = item.next; + } + NS_ASSERT (leftToRemove == 0); +} +void +PacketMetadata::RemoveAtEnd (uint32_t end) +{ + if (!m_enable) + { + return; + } + NS_ASSERT (m_data != 0); + + uint32_t leftToRemove = end; + uint16_t current = m_tail; + while (current != 0xffff && leftToRemove > 0) + { + struct PacketMetadata::SmallItem item; + PacketMetadata::ExtraItem extraItem; + ReadItems (current, &item, &extraItem); + uint32_t itemRealSize = extraItem.fragmentEnd - extraItem.fragmentStart; + if (itemRealSize <= leftToRemove) + { + // remove from list. + m_tail = item.prev; + leftToRemove -= itemRealSize; + } + else + { + // fragment the list item. + PacketMetadata fragment (m_packetUid, 0); + NS_ASSERT (extraItem.fragmentEnd > leftToRemove); + extraItem.fragmentEnd -= leftToRemove; + leftToRemove = 0; + uint16_t written = fragment.AddBig (fragment.m_head, 0xffff, + &item, &extraItem); + fragment.UpdateHead (written); + current = item.prev; + while (current != 0xffff) + { + ReadItems (current, &item, &extraItem); + written = fragment.AddBig (fragment.m_head, 0xffff, + &item, &extraItem); + fragment.UpdateHead (written); + if (current == m_head) + { + break; + } + current = item.prev; + } + *this = fragment; + } + NS_ASSERT (item.size >= extraItem.fragmentEnd - extraItem.fragmentStart && + extraItem.fragmentStart <= extraItem.fragmentEnd); + if (current == m_head) + { + break; + } + current = item.prev; + } + NS_ASSERT (leftToRemove == 0); +} + +void +PacketMetadata::PrintDefault (std::ostream &os, Buffer buffer) const +{ + Print (os, buffer, PacketPrinter::GetDefault ()); +} + +uint32_t +PacketMetadata::DoPrint (const struct PacketMetadata::SmallItem *item, + const struct PacketMetadata::ExtraItem *extraItem, + Buffer data, uint32_t offset, const PacketPrinter &printer, + std::ostream &os) const +{ + uint32_t uid = item->typeUid & 0xfffffffe; + if (uid == 0) + { + // payload. + printer.PrintPayload (os, extraItem->packetUid, item->size, + extraItem->fragmentStart, + item->size - extraItem->fragmentEnd); + } + else if (extraItem->fragmentStart != 0 || + extraItem->fragmentEnd != item->size) + { + printer.PrintChunkFragment (uid, os, extraItem->packetUid, item->size, + extraItem->fragmentStart, + item->size - extraItem->fragmentEnd); + } + else if (PacketPrinter::IsHeader (uid)) + { + ns3::Buffer::Iterator j = data.Begin (); + j.Next (offset); + printer.PrintChunk (uid, j, os, extraItem->packetUid, item->size); + } + else if (PacketPrinter::IsTrailer (uid)) + { + ns3::Buffer::Iterator j = data.End (); + j.Prev (data.GetSize () - (offset + item->size)); + printer.PrintChunk (uid, j, os, extraItem->packetUid, item->size); + } + else + { + NS_ASSERT (false); + } + return extraItem->fragmentEnd - extraItem->fragmentStart; +} + +uint32_t +PacketMetadata::GetTotalSize (void) const +{ + uint32_t totalSize = 0; + uint16_t current = m_head; + uint16_t tail = m_tail; + while (current != 0xffff) + { + struct PacketMetadata::SmallItem item; + PacketMetadata::ExtraItem extraItem; + ReadItems (current, &item, &extraItem); + totalSize += extraItem.fragmentEnd - extraItem.fragmentStart; + if (current == tail) + { + break; + } + NS_ASSERT (current != item.next); + current = item.next; + } + return totalSize; +} + +uint32_t +PacketMetadata::GetUid (void) const +{ + return m_packetUid; +} + +void +PacketMetadata::Print (std::ostream &os, Buffer data, const PacketPrinter &printer) const +{ + if (!m_enable) + { + return; + } + NS_ASSERT (m_data != 0); + NS_ASSERT (GetTotalSize () == data.GetSize ()); + struct PacketMetadata::SmallItem item; + struct PacketMetadata::ExtraItem extraItem; + if (printer.m_forward) + { + uint32_t current = m_head; + uint32_t offset = 0; + while (current != 0xffff) + { + ReadItems (current, &item, &extraItem); + uint32_t realSize = DoPrint (&item, &extraItem, data, offset, printer, os); + offset += realSize; + if (current == m_tail) + { + break; + } + if (item.next != 0xffff) + { + os << printer.m_separator; + } + NS_ASSERT (current != item.next); + current = item.next; + } + } + else + { + uint32_t current = m_tail; + uint32_t offset = data.GetSize (); + while (current != 0xffff) + { + ReadItems (current, &item, &extraItem); + uint32_t realSize = DoPrint (&item, &extraItem, data, offset - item.size, printer, os); + offset -= realSize; + if (current == m_head) + { + break; + } + if (item.prev != 0xffff) + { + os << printer.m_separator; + } + NS_ASSERT (current != item.prev); + current = item.prev; + } + } +} + + + +}; // namespace ns3 + +#include +#include +#include +#include "ns3/test.h" +#include "header.h" +#include "trailer.h" +#include "packet.h" + +namespace ns3 { + +template +class HistoryHeader : public Header +{ +public: + HistoryHeader (); + bool IsOk (void) const; +private: + virtual std::string DoGetName (void) const; + virtual void PrintTo (std::ostream &os) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void SerializeTo (Buffer::Iterator start) const; + virtual uint32_t DeserializeFrom (Buffer::Iterator start); + bool m_ok; +}; + +template +HistoryHeader::HistoryHeader () + : m_ok (false) +{} + +template +bool +HistoryHeader::IsOk (void) const +{ + return m_ok; +} + +template +std::string +HistoryHeader::DoGetName (void) const +{ + std::ostringstream oss; + oss << N; + return oss.str (); +} + +template +void +HistoryHeader::PrintTo (std::ostream &os) const +{ + NS_ASSERT (false); +} +template +uint32_t +HistoryHeader::GetSerializedSize (void) const +{ + return N; +} +template +void +HistoryHeader::SerializeTo (Buffer::Iterator start) const +{ + start.WriteU8 (N, N); +} +template +uint32_t +HistoryHeader::DeserializeFrom (Buffer::Iterator start) +{ + m_ok = true; + for (int i = 0; i < N; i++) + { + if (start.ReadU8 () != N) + { + m_ok = false; + } + } + return N; +} + +template +class HistoryTrailer : public Trailer +{ +public: + HistoryTrailer (); + bool IsOk (void) const; +private: + virtual std::string DoGetName (void) const; + virtual void PrintTo (std::ostream &os) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void SerializeTo (Buffer::Iterator start) const; + virtual uint32_t DeserializeFrom (Buffer::Iterator start); + bool m_ok; +}; + +template +HistoryTrailer::HistoryTrailer () + : m_ok (false) +{} + +template +bool +HistoryTrailer::IsOk (void) const +{ + return m_ok; +} + +template +std::string +HistoryTrailer::DoGetName (void) const +{ + std::ostringstream oss; + oss << N; + return oss.str (); +} +template +void +HistoryTrailer::PrintTo (std::ostream &os) const +{ + NS_ASSERT (false); +} +template +uint32_t +HistoryTrailer::GetSerializedSize (void) const +{ + return N; +} +template +void +HistoryTrailer::SerializeTo (Buffer::Iterator start) const +{ + start.Prev (N); + start.WriteU8 (N, N); +} +template +uint32_t +HistoryTrailer::DeserializeFrom (Buffer::Iterator start) +{ + m_ok = true; + start.Prev (N); + for (int i = 0; i < N; i++) + { + if (start.ReadU8 () != N) + { + m_ok = false; + } + } + return N; +} + + + +class PacketMetadataTest : public Test { +public: + PacketMetadataTest (); + virtual ~PacketMetadataTest (); + bool CheckHistory (Packet p, const char *file, int line, uint32_t n, ...); + virtual bool RunTests (void); +private: + template + void PrintHeader (std::ostream &os, uint32_t packetUid, uint32_t size, const HistoryHeader *header); + template + void PrintTrailer (std::ostream &os, uint32_t packetUid, uint32_t size, const HistoryTrailer *trailer); + void PrintFragment (std::ostream &os,uint32_t packetUid, + uint32_t size,std::string & name, + struct PacketPrinter::FragmentInformation info); + void PrintDefault (std::ostream& os,uint32_t packetUid, + uint32_t size,std::string& name, + struct PacketPrinter::FragmentInformation info); + void PrintPayload (std::ostream &os,uint32_t packetUid, + uint32_t size, + struct PacketPrinter::FragmentInformation info); + template + void RegisterHeader (void); + template + void RegisterTrailer (void); + void CleanupPrints (void); + bool Check (const char *file, int line, std::list expected); + + + bool m_headerError; + bool m_trailerError; + std::list m_prints; + PacketPrinter m_printer; +}; + +PacketMetadataTest::PacketMetadataTest () + : Test ("PacketMetadata") +{ + m_printer.AddPayloadPrinter (MakeCallback (&PacketMetadataTest::PrintPayload, this)); + m_printer.AddDefaultPrinter (MakeCallback (&PacketMetadataTest::PrintDefault, this)); +} + +PacketMetadataTest::~PacketMetadataTest () +{} + +template +void +PacketMetadataTest::RegisterHeader (void) +{ + static bool registered = false; + if (!registered) + { + m_printer.AddHeaderPrinter (MakeCallback (&PacketMetadataTest::PrintHeader, this), + MakeCallback (&PacketMetadataTest::PrintFragment, this)); + registered = true; + } +} + +template +void +PacketMetadataTest::RegisterTrailer (void) +{ + static bool registered = false; + if (!registered) + { + m_printer.AddTrailerPrinter (MakeCallback (&PacketMetadataTest::PrintTrailer, this), + MakeCallback (&PacketMetadataTest::PrintFragment, this)); + registered = true; + } +} + + +template +void +PacketMetadataTest::PrintHeader (std::ostream &os, uint32_t packetUid, uint32_t size, + const HistoryHeader *header) +{ + if (!header->IsOk ()) + { + m_headerError = true; + } + m_prints.push_back (N); +} + +template +void +PacketMetadataTest::PrintTrailer (std::ostream &os, uint32_t packetUid, uint32_t size, + const HistoryTrailer *trailer) +{ + if (!trailer->IsOk ()) + { + m_trailerError = true; + } + m_prints.push_back (N); +} +void +PacketMetadataTest::PrintFragment (std::ostream &os,uint32_t packetUid, + uint32_t size,std::string & name, + struct PacketPrinter::FragmentInformation info) +{ + m_prints.push_back (size - (info.end + info.start)); +} +void +PacketMetadataTest::PrintDefault (std::ostream& os,uint32_t packetUid, + uint32_t size,std::string& name, + struct PacketPrinter::FragmentInformation info) +{ + NS_ASSERT (false); +} +void +PacketMetadataTest::PrintPayload (std::ostream &os,uint32_t packetUid, + uint32_t size, + struct PacketPrinter::FragmentInformation info) +{ + m_prints.push_back (size - (info.end + info.start)); +} + + +void +PacketMetadataTest::CleanupPrints (void) +{ + m_prints.clear (); +} + +bool +PacketMetadataTest::Check (const char *file, int line, std::list expected) +{ + if (m_headerError) + { + std::cout << "PacketMetadata header error. file=" << file + << ", line=" << line << std::endl; + return false; + } + if (m_trailerError) + { + std::cout << "PacketMetadata trailer error. file=" << file + << ", line=" << line << std::endl; + return false; + } + if (expected.size () != m_prints.size ()) + { + goto error; + } + for (std::list::iterator i = m_prints.begin (), + j = expected.begin (); + i != m_prints.end (); i++, j++) + { + NS_ASSERT (j != expected.end ()); + if (*j != *i) + { + goto error; + } + } + return true; + error: + std::cout << "PacketMetadata error. file="<< file + << ", line=" << line << ", got:\""; + for (std::list::iterator i = m_prints.begin (); + i != m_prints.end (); i++) + { + std::cout << *i << ", "; + } + std::cout << "\", expected: \""; + for (std::list::iterator j = expected.begin (); + j != expected.end (); j++) + { + std::cout << *j << ", "; + } + std::cout << "\"" << std::endl; + return false; +} + +bool +PacketMetadataTest::CheckHistory (Packet p, const char *file, int line, uint32_t n, ...) +{ + m_headerError = false; + m_trailerError = false; + std::list expected; + va_list ap; + va_start (ap, n); + for (uint32_t j = 0; j < n; j++) + { + int v = va_arg (ap, int); + expected.push_back (v); + } + va_end (ap); + + m_printer.PrintForward (); + p.Print (std::cerr, m_printer); + bool ok = Check (file, line, expected); + CleanupPrints (); + if (!ok) + { + return false; + } + + m_printer.PrintBackward (); + p.Print (std::cerr, m_printer); + expected.reverse (); + ok = Check (file, line, expected); + CleanupPrints (); + return ok; +} + +#define ADD_HEADER(p, n) \ + { \ + HistoryHeader header; \ + RegisterHeader (); \ + p.AddHeader (header); \ + } +#define ADD_TRAILER(p, n) \ + { \ + HistoryTrailer trailer; \ + RegisterTrailer (); \ + p.AddTrailer (trailer); \ + } +#define REM_HEADER(p, n) \ + { \ + HistoryHeader header; \ + RegisterHeader (); \ + p.RemoveHeader (header); \ + } +#define REM_TRAILER(p, n) \ + { \ + HistoryTrailer trailer; \ + RegisterTrailer (); \ + p.RemoveTrailer (trailer); \ + } +#define CHECK_HISTORY(p, ...) \ + { \ + if (!CheckHistory (p, __FILE__, \ + __LINE__, __VA_ARGS__)) \ + { \ + ok = false; \ + } \ + } + +bool +PacketMetadataTest::RunTests (void) +{ + bool ok = true; + + PacketMetadata::Enable (); + + Packet p = Packet (0); + Packet p1 = Packet (0); + + p = Packet (10); + ADD_TRAILER (p, 100); + CHECK_HISTORY (p, 2, 10, 100); + + p = Packet (10); + ADD_HEADER (p, 1); + ADD_HEADER (p, 2); + ADD_HEADER (p, 3); + CHECK_HISTORY (p, 4, + 3, 2, 1, 10); + ADD_HEADER (p, 5); + CHECK_HISTORY (p, 5, + 5, 3, 2, 1, 10); + ADD_HEADER (p, 6); + CHECK_HISTORY (p, 6, + 6, 5, 3, 2, 1, 10); + + p = Packet (10); + ADD_HEADER (p, 1); + ADD_HEADER (p, 2); + ADD_HEADER (p, 3); + REM_HEADER (p, 3); + CHECK_HISTORY (p, 3, + 2, 1, 10); + + p = Packet (10); + ADD_HEADER (p, 1); + ADD_HEADER (p, 2); + ADD_HEADER (p, 3); + REM_HEADER (p, 3); + REM_HEADER (p, 2); + CHECK_HISTORY (p, 2, + 1, 10); + + p = Packet (10); + ADD_HEADER (p, 1); + ADD_HEADER (p, 2); + ADD_HEADER (p, 3); + REM_HEADER (p, 3); + REM_HEADER (p, 2); + REM_HEADER (p, 1); + CHECK_HISTORY (p, 1, 10); + + p = Packet (10); + ADD_HEADER (p, 1); + ADD_HEADER (p, 2); + ADD_HEADER (p, 3); + p1 = p; + REM_HEADER (p1, 3); + REM_HEADER (p1, 2); + REM_HEADER (p1, 1); + CHECK_HISTORY (p1, 1, 10); + CHECK_HISTORY (p, 4, + 3, 2, 1, 10); + ADD_HEADER (p1, 1); + ADD_HEADER (p1, 2); + CHECK_HISTORY (p1, 3, + 2, 1, 10); + CHECK_HISTORY (p, 4, + 3, 2, 1, 10); + ADD_HEADER (p, 3); + CHECK_HISTORY (p, 5, + 3, 3, 2, 1, 10); + ADD_TRAILER (p, 4); + CHECK_HISTORY (p, 6, + 3, 3, 2, 1, 10, 4); + ADD_TRAILER (p, 5); + CHECK_HISTORY (p, 7, + 3, 3, 2, 1, 10, 4, 5); + REM_HEADER (p, 3); + CHECK_HISTORY (p, 6, + 3, 2, 1, 10, 4, 5); + REM_TRAILER (p, 5); + CHECK_HISTORY (p, 5, + 3, 2, 1, 10, 4); + p1 = p; + REM_TRAILER (p, 4); + CHECK_HISTORY (p, 4, + 3, 2, 1, 10); + CHECK_HISTORY (p1, 5, + 3, 2, 1, 10, 4); + p1.RemoveAtStart (3); + CHECK_HISTORY (p1, 4, + 2, 1, 10, 4); + p1.RemoveAtStart (1); + CHECK_HISTORY (p1, 4, + 1, 1, 10, 4); + p1.RemoveAtStart (1); + CHECK_HISTORY (p1, 3, + 1, 10, 4); + p1.RemoveAtEnd (4); + CHECK_HISTORY (p1, 2, + 1, 10); + p1.RemoveAtStart (1); + CHECK_HISTORY (p1, 1, 10); + + p = Packet (10); + ADD_HEADER (p, 8); + ADD_TRAILER (p, 8); + ADD_TRAILER (p, 8); + p.RemoveAtStart (8+10+8); + CHECK_HISTORY (p, 1, 8); + + p = Packet (10); + ADD_HEADER (p, 10); + ADD_HEADER (p, 8); + ADD_TRAILER (p, 6); + ADD_TRAILER (p, 7); + ADD_TRAILER (p, 9); + p.RemoveAtStart (5); + p.RemoveAtEnd (12); + CHECK_HISTORY (p, 5, 3, 10, 10, 6, 4); + + p = Packet (10); + ADD_HEADER (p, 10); + ADD_TRAILER (p, 6); + p.RemoveAtEnd (18); + ADD_TRAILER (p, 5); + ADD_HEADER (p, 3); + CHECK_HISTORY (p, 3, 3, 8, 5); + p.RemoveAtStart (12); + CHECK_HISTORY (p, 1, 4); + p.RemoveAtEnd (2); + CHECK_HISTORY (p, 1, 2); + ADD_HEADER (p, 10); + CHECK_HISTORY (p, 2, 10, 2); + p.RemoveAtEnd (5); + CHECK_HISTORY (p, 1, 7); + + Packet p2 = Packet (0); + Packet p3 = Packet (0); + + p = Packet (40); + ADD_HEADER (p, 5); + ADD_HEADER (p, 8); + CHECK_HISTORY (p, 3, 8, 5, 40); + p1 = p.CreateFragment (0, 5); + p2 = p.CreateFragment (5, 5); + p3 = p.CreateFragment (10, 43); + CHECK_HISTORY (p1, 1, 5); + CHECK_HISTORY (p2, 2, 3, 2); + CHECK_HISTORY (p3, 2, 3, 40); + p1.AddAtEnd (p2); + CHECK_HISTORY (p1, 2, 8, 2); + CHECK_HISTORY (p2, 2, 3, 2); + p1.AddAtEnd (p3); + CHECK_HISTORY (p1, 3, 8, 5, 40); + CHECK_HISTORY (p2, 2, 3, 2); + CHECK_HISTORY (p3, 2, 3, 40); + p1 = p.CreateFragment (0, 5); + CHECK_HISTORY (p1, 1, 5); + + p3 = Packet (50); + ADD_HEADER (p3, 8); + CHECK_HISTORY (p3, 2, 8, 50); + CHECK_HISTORY (p1, 1, 5); + p1.AddAtEnd (p3); + CHECK_HISTORY (p1, 3, 5, 8, 50); + ADD_HEADER (p1, 5); + CHECK_HISTORY (p1, 4, 5, 5, 8, 50); + ADD_TRAILER (p1, 2); + CHECK_HISTORY (p1, 5, 5, 5, 8, 50, 2); + REM_HEADER (p1, 5); + CHECK_HISTORY (p1, 4, 5, 8, 50, 2); + p1.RemoveAtEnd (60); + CHECK_HISTORY (p1, 1, 5); + p1.AddAtEnd (p2); + CHECK_HISTORY (p1, 2, 8, 2); + CHECK_HISTORY (p2, 2, 3, 2); + + p3 = Packet (40); + ADD_HEADER (p3, 5); + ADD_HEADER (p3, 5); + CHECK_HISTORY (p3, 3, 5, 5, 40); + p1 = p3.CreateFragment (0, 5); + p2 = p3.CreateFragment (5, 5); + CHECK_HISTORY (p1, 1, 5); + CHECK_HISTORY (p2, 1, 5); + p1.AddAtEnd (p2); + CHECK_HISTORY (p1, 2, 5, 5); + + p = Packet (0); + CHECK_HISTORY (p, 0); + + p3 = Packet (0); + ADD_HEADER (p3, 5); + ADD_HEADER (p3, 5); + CHECK_HISTORY (p3, 2, 5, 5); + p1 = p3.CreateFragment (0, 4); + p2 = p3.CreateFragment (9, 1); + CHECK_HISTORY (p1, 1, 4); + CHECK_HISTORY (p2, 1, 1); + p1.AddAtEnd (p2); + CHECK_HISTORY (p1, 2, 4, 1); + + + p = Packet (2000); + CHECK_HISTORY (p, 1, 2000); + + p = Packet (); + ADD_TRAILER (p, 10); + ADD_HEADER (p, 5); + p1 = p.CreateFragment (0, 8); + p2 = p.CreateFragment (8, 7); + p1.AddAtEnd (p2); + CHECK_HISTORY (p, 2, 5, 10); + + p = Packet (); + ADD_TRAILER (p, 10); + REM_TRAILER (p, 10); + ADD_TRAILER (p, 10); + CHECK_HISTORY (p, 1, 10); + + p = Packet (); + ADD_HEADER (p, 10); + REM_HEADER (p, 10); + ADD_HEADER (p, 10); + CHECK_HISTORY (p, 1, 10); + + return ok; +} + +static PacketMetadataTest g_packetHistoryTest; + +}//namespace ns3 diff --git a/src/common/packet-metadata.h b/src/common/packet-metadata.h new file mode 100644 index 000000000..f24b35bed --- /dev/null +++ b/src/common/packet-metadata.h @@ -0,0 +1,335 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006,2007 INRIA + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ +#ifndef PACKET_METADATA_H +#define PACKET_METADATA_H + +#include +#include +#include "ns3/callback.h" +#include "ns3/assert.h" +#include "packet-printer.h" + +namespace ns3 { + +class Chunk; +class Buffer; + +/** + * \internal + * \brief handle packet metadata about packet headers and trailers + * + * This class is used by the Packet class to record every operation + * performed on the packet's buffer. This class also provides + * an implementation of the Packet::Print methods which uses + * the metadata to analyse the content of the packet's buffer. + * + * To achieve this, this class maintains a linked list of so-called + * "items", each of which represents a header or a trailer, or + * payload, or a fragment of any of these. Each item contains a "next" + * and a "prev" field which point to the next and previous entries + * in the linked list. The PacketMetadata class maintains a pair + * of pointers to the head and the tail of the linked list. + * + * Each entry in the list also maintains: + * - its native size (the size it had when it was first added + * to the packet) + * - its type: identifies what kind of header, what kind of trailer, + * if it is payload or not + * - the uid of the packet to which it was first added + * - the start and end of the area represented by a fragment + * if it is one. + * + * This linked list is flattened in a byte buffer stored in + * struct PacketMetadata::Data. Each entry of the linked list is + * identified by an offset which identifies the first byte of the + * entry from the start of the data buffer. The size of this data + * buffer is 2^16-1 bytes maximum which somewhat limits the number + * of entries which can be stored in this linked list but it is + * quite unlikely to hit this limit in practice. + * + * Each item of the linked list is a variable-sized byte buffer + * made of a number of fields. Some of these fields are stored + * as fixed-size 32 bit integers, others as fixed-size 16 bit + * integers, and some others as variable-size 32-bit integers. + * The variable-size 32 bit integers are stored using the uleb128 + * encoding. + */ +class PacketMetadata { +public: + static void Enable (void); + static void SetOptOne (bool optOne); + + inline PacketMetadata (uint32_t uid, uint32_t size); + inline PacketMetadata (PacketMetadata const &o); + inline PacketMetadata &operator = (PacketMetadata const& o); + inline ~PacketMetadata (); + + template + void AddHeader (T const &header, uint32_t size); + template + void RemoveHeader (T const &header, uint32_t size); + + template + void AddTrailer (T const &trailer, uint32_t size); + template + void RemoveTrailer (T const &trailer, uint32_t size); + + PacketMetadata CreateFragment (uint32_t start, uint32_t end) const; + void AddAtEnd (PacketMetadata const&o); + void AddPaddingAtEnd (uint32_t end); + void RemoveAtStart (uint32_t start); + void RemoveAtEnd (uint32_t end); + + uint32_t GetUid (void) const; + + void PrintDefault (std::ostream &os, Buffer buffer) const; + void Print (std::ostream &os, Buffer buffer, PacketPrinter const &printer) const; + + static void PrintStats (void); + +private: + struct Data { + /* number of references to this struct Data instance. */ + uint16_t m_count; + /* size (in bytes) of m_data buffer below */ + uint16_t m_size; + /* max of the m_used field over all objects which + * reference this struct Data instance */ + uint16_t m_dirtyEnd; + /* variable-sized buffer of bytes */ + uint8_t m_data[10]; + }; + /* Note that since the next and prev fields are 16 bit integers + and since the value 0xffff is reserved to identify the + fact that the end or the start of the list is reached, + only a limited number of elements can be stored in + a m_data byte buffer. + */ + struct SmallItem { + /* offset (in bytes) from start of m_data buffer + to next element in linked list. value is 0xffff + if next element does not exist. + stored as a fixed-size 16 bit integer. + */ + uint16_t next; + /* offset (in bytes) from start of m_data buffer + to previous element in linked list. value is 0xffff + if previous element does not exist. + stored as a fixed-size 16 bit integer. + */ + uint16_t prev; + /* the high 31 bits of this field identify the + type of the header or trailer represented by + this item: the value zero represents payload. + If the low bit of this uid is one, an ExtraItem + structure follows this SmallItem structure. + stored as a variable-size 32 bit integer. + */ + uint32_t typeUid; + /* the size (in bytes) of the header or trailer represented + by this element. + stored as a variable-size 32 bit integer. + */ + uint32_t size; + /* this field tries to uniquely identify each header or + trailer _instance_ while the typeUid field uniquely + identifies each header or trailer _type_. This field + is used to test whether two items are equal in the sense + that they represent the same header or trailer instance. + That equality test is based on the typeUid and chunkUid + fields so, the likelyhood that two header instances + share the same chunkUid _and_ typeUid is very small + unless they are really representations of the same header + instance. + stored as a fixed-size 16 bit integer. + */ + uint16_t chunkUid; + }; + struct ExtraItem { + /* offset (in bytes) from start of original header to + the start of the fragment still present. + stored as a variable-size 32 bit integer. + */ + uint32_t fragmentStart; + /* offset (in bytes) from start of original header to + the end of the fragment still present. + stored as a variable-size 32 bit integer. + */ + uint32_t fragmentEnd; + /* the packetUid of the packet in which this header or trailer + was first added. It could be different from the m_packetUid + field if the user has aggregated multiple packets into one. + stored as a fixed-size 32 bit integer. + */ + uint32_t packetUid; + }; + + typedef std::vector DataFreeList; + + PacketMetadata (); + void DoAddHeader (uint32_t uid, uint32_t size); + void DoRemoveHeader (uint32_t uid, uint32_t size); + void DoAddTrailer (uint32_t uid, uint32_t size); + void DoRemoveTrailer (uint32_t uid, uint32_t size); + + inline uint16_t AddSmall (const PacketMetadata::SmallItem *item); + uint16_t AddBig (uint32_t head, uint32_t tail, + const PacketMetadata::SmallItem *item, + const PacketMetadata::ExtraItem *extraItem); + void ReplaceTail (PacketMetadata::SmallItem *item, + PacketMetadata::ExtraItem *extraItem, + uint32_t available); + inline void UpdateHead (uint16_t written); + inline void UpdateTail (uint16_t written); + uint32_t GetUleb128Size (uint32_t value) const; + uint32_t ReadUleb128 (const uint8_t **pBuffer) const; + inline void Append16 (uint16_t value, uint8_t *buffer); + inline bool TryToAppend (uint32_t value, uint8_t **pBuffer, uint8_t *end); + inline bool TryToAppendFast (uint32_t value, uint8_t **pBuffer, uint8_t *end); + inline bool TryToAppend32 (uint32_t value, uint8_t **pBuffer, uint8_t *end); + inline bool TryToAppend16 (uint16_t value, uint8_t **pBuffer, uint8_t *end); + void AppendValue (uint32_t value, uint8_t *buffer); + void AppendValueExtra (uint32_t value, uint8_t *buffer); + inline void Reserve (uint32_t n); + void ReserveCopy (uint32_t n); + uint32_t DoPrint (const struct PacketMetadata::SmallItem *item, + const struct PacketMetadata::ExtraItem *extraItem, + Buffer data, uint32_t offset, const PacketPrinter &printer, + std::ostream &os) const; + uint32_t GetTotalSize (void) const; + uint32_t ReadItems (uint16_t current, + struct PacketMetadata::SmallItem *item, + struct PacketMetadata::ExtraItem *extraItem) const; + + + static struct PacketMetadata::Data *Create (uint32_t size); + static void Recycle (struct PacketMetadata::Data *data); + static struct PacketMetadata::Data *Allocate (uint32_t n); + static void Deallocate (struct PacketMetadata::Data *data); + + static DataFreeList m_freeList; + static bool m_enable; + static uint32_t m_maxSize; + static uint16_t m_chunkUid; + + struct Data *m_data; + /** + head -(next)-> tail + ^ | + \---(prev)---| + */ + uint16_t m_head; + uint16_t m_tail; + uint16_t m_used; + uint32_t m_packetUid; +}; + +}; // namespace ns3 + +namespace ns3 { + +template +void +PacketMetadata::AddHeader (T const &header, uint32_t size) +{ + DoAddHeader (PacketPrinter::GetHeaderUid (), size); +} + +template +void +PacketMetadata::RemoveHeader (T const &header, uint32_t size) +{ + DoRemoveHeader (PacketPrinter::GetHeaderUid (), size); +} +template +void +PacketMetadata::AddTrailer (T const &trailer, uint32_t size) +{ + DoAddTrailer (PacketPrinter::GetTrailerUid (), size); +} +template +void +PacketMetadata::RemoveTrailer (T const &trailer, uint32_t size) +{ + DoRemoveTrailer (PacketPrinter::GetTrailerUid (), size); +} + + +PacketMetadata::PacketMetadata (uint32_t uid, uint32_t size) + : m_data (m_data = PacketMetadata::Create (10)), + m_head (0xffff), + m_tail (0xffff), + m_used (0), + m_packetUid (uid) +{ + memset (m_data->m_data, 0xff, 4); + if (size > 0) + { + DoAddHeader (0, size); + } +} +PacketMetadata::PacketMetadata (PacketMetadata const &o) + : m_data (o.m_data), + m_head (o.m_head), + m_tail (o.m_tail), + m_used (o.m_used), + m_packetUid (o.m_packetUid) +{ + NS_ASSERT (m_data != 0); + m_data->m_count++; +} +PacketMetadata & +PacketMetadata::operator = (PacketMetadata const& o) +{ + if (m_data == o.m_data) + { + // self assignment + return *this; + } + NS_ASSERT (m_data != 0); + m_data->m_count--; + if (m_data->m_count == 0) + { + PacketMetadata::Recycle (m_data); + } + m_data = o.m_data; + m_head = o.m_head; + m_tail = o.m_tail; + m_used = o.m_used; + m_packetUid = o.m_packetUid; + NS_ASSERT (m_data != 0); + m_data->m_count++; + return *this; +} +PacketMetadata::~PacketMetadata () +{ + NS_ASSERT (m_data != 0); + m_data->m_count--; + if (m_data->m_count == 0) + { + PacketMetadata::Recycle (m_data); + } +} + +}; // namespace ns3 + + +#endif /* PACKET_METADATA_H */ diff --git a/src/common/packet-printer.cc b/src/common/packet-printer.cc new file mode 100644 index 000000000..9ccda7668 --- /dev/null +++ b/src/common/packet-printer.cc @@ -0,0 +1,235 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 INRIA + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ + +#include "packet-printer.h" + +namespace ns3 { + +PacketPrinter::PacketPrinter () + : m_forward (true), + m_separator ("") +{} + +void +PacketPrinter::PrintForward (void) +{ + m_forward = true; +} +void +PacketPrinter::PrintBackward (void) +{ + m_forward = false; +} +void +PacketPrinter::SetSeparator (std::string separator) +{ + m_separator = separator; +} +void +PacketPrinter::AddPayloadPrinter (PayloadPrinter printer) +{ + m_payloadPrinter = printer; +} +void +PacketPrinter::AddDefaultPrinter (DefaultPrinter printer) +{ + m_defaultPrinter = printer; +} + +PacketPrinter::RegisteredChunks * +PacketPrinter::GetRegisteredChunks (void) +{ + static RegisteredChunks registeredChunks; + return ®isteredChunks; +} + +PacketPrinter +PacketPrinter::GetDefault (void) +{ + return *(PacketPrinter::PeekDefault ()); +} +PacketPrinter * +PacketPrinter::PeekDefault (void) +{ + static PacketPrinter *tmp = PacketPrinter::CreateStaticDefault (); + return tmp; +} +PacketPrinter * +PacketPrinter::CreateStaticDefault (void) +{ + static PacketPrinter tmp; + tmp.PrintForward (); + tmp.AddPayloadPrinter (MakeCallback (&PacketPrinter::DoDefaultPrintPayload)); + tmp.SetSeparator (" "); + return &tmp; +} + + +void +PacketPrinter::PrintChunk (uint32_t chunkUid, + Buffer::Iterator start, + std::ostream &os, + uint32_t packetUid, + uint32_t size) const +{ + RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks (); + NS_ASSERT (chunkUid >= 1 && chunkUid/2 <= registeredChunks->size ()); + for (PrinterList::const_iterator i = m_printerList.begin (); i != m_printerList.end (); i++) + { + if (i->m_chunkUid == chunkUid) + { + DoPrintCallback cb = (*registeredChunks)[chunkUid/2-1].printCallback; + cb (i->m_printer, start, os, packetUid, size); + return; + } + } + DoGetNameCallback cb = (*registeredChunks)[chunkUid/2-1].getNameCallback; + std::string name = cb (); + struct PacketPrinter::FragmentInformation info; + info.start = 0; + info.end = 0; + if (!m_defaultPrinter.IsNull ()) + { + m_defaultPrinter (os, packetUid, size, name, info); + } +} +void +PacketPrinter::PrintChunkFragment (uint32_t chunkUid, + std::ostream &os, + uint32_t packetUid, + uint32_t size, + uint32_t fragmentStart, + uint32_t fragmentEnd) const +{ + RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks (); + NS_ASSERT (chunkUid >= 1 && chunkUid/2 <= registeredChunks->size ()); + DoGetNameCallback cb = (*registeredChunks)[chunkUid/2-1].getNameCallback; + std::string name = cb (); + struct PacketPrinter::FragmentInformation info; + info.start = fragmentStart; + info.end = fragmentEnd; + for (PrinterList::const_iterator i = m_printerList.begin (); i != m_printerList.end (); i++) + { + if (i->m_chunkUid == chunkUid) + { + i->m_fragmentPrinter (os, packetUid, size, name, info); + return; + } + } + if (!m_defaultPrinter.IsNull ()) + { + m_defaultPrinter (os, packetUid, size, name, info); + } +} +void +PacketPrinter::PrintPayload (std::ostream &os, uint32_t packetUid, uint32_t size, + uint32_t fragmentStart, uint32_t fragmentEnd) const +{ + struct PacketPrinter::FragmentInformation info; + info.start = fragmentStart; + info.end = fragmentEnd; + if (!m_payloadPrinter.IsNull ()) + { + m_payloadPrinter (os, packetUid, size, info); + } +} + +void +PacketPrinter::DoDefaultPrintPayload (std::ostream & os, + uint32_t packetUid, + uint32_t size, + struct PacketPrinter::FragmentInformation info) +{ + os << "DATA (" + << "length " << size - (info.end + info.start); + if (info.start != 0 || info.end != 0) + { + os << " " + << "trim_start " << info.start << " " + << "trim_end " << info.end; + } + os << ")"; +} + +void +PacketPrinter::DoDefaultPrintDefault (std::ostream & os, + uint32_t packetUid, + uint32_t size, + std::string &name, + struct PacketPrinter::FragmentInformation info) +{ + NS_ASSERT_MSG (false, "This should never happen because we provide a printer for _all_ chunk types."); +} + +void +PacketPrinter::DoDefaultPrintFragment (std::ostream & os, + uint32_t packetUid, + uint32_t size, + std::string &name, + struct PacketPrinter::FragmentInformation info) +{ + NS_ASSERT (info.start != 0 || info.end != 0); + os << name << " " + << "(" + << "length " << size - (info.end + info.start) << " " + << "trim_start " << info.start << " " + << "trim_end " << info.end + << ")" + ; +} + +void +PacketPrinter::DoAddPrinter (uint32_t uid, + Ptr printer, + Callback fragmentPrinter) +{ + struct PacketPrinter::Printer p; + p.m_chunkUid = uid; + p.m_printer = printer; + p.m_fragmentPrinter = fragmentPrinter; + m_printerList.push_back (p); +} + +bool +PacketPrinter::IsTrailer (uint32_t uid) +{ + RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks (); + NS_ASSERT (uid >= 1 && uid/2 <= registeredChunks->size ()); + bool isHeader = (*registeredChunks)[uid/2-1].isHeader; + return !isHeader; +} +bool +PacketPrinter::IsHeader (uint32_t uid) +{ + RegisteredChunks *registeredChunks = PacketPrinter::GetRegisteredChunks (); + NS_ASSERT (uid >= 1 && uid/2 <= registeredChunks->size ()); + bool isHeader = (*registeredChunks)[uid/2-1].isHeader; + return isHeader; +} + + + +} // namespace ns3 diff --git a/src/common/packet-printer.h b/src/common/packet-printer.h new file mode 100644 index 000000000..cd340545d --- /dev/null +++ b/src/common/packet-printer.h @@ -0,0 +1,318 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 INRIA + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mathieu Lacage + */ +#ifndef PACKET_PRINTER_H +#define PACKET_PRINTER_H + +#include "ns3/callback.h" +#include "ns3/ptr.h" +#include "buffer.h" +#include + +namespace { + class ItemList; +} + +namespace ns3 { + +class Chunk; + +/** + * \brief hold a list of print callbacks for packet headers and trailers + * + * Users can register in instances of this class print callbacks + * which are used by Packet::Print to print the content of a packet. + */ +class PacketPrinter +{ +public: + /** + * \brief indicates how many bytes were trimmed from a header + * or a trailer. + */ + struct FragmentInformation + { + /** + * The number of bytes trimmed from the start of the header or the trailer. + */ + uint32_t start; + /** + * The number of bytes trimmed from the end of the header or the trailer. + */ + uint32_t end; + }; + /** + * \brief callback to print payload. + * + * Arguments: output stream, packet uid, size, fragment information + */ + typedef Callback + PayloadPrinter; + + /** + * \brief callback to print fragmented chunks. + * + * Arguments: output stream, packet uid, size, header/trailer name, fragment information + */ + typedef Callback + ChunkFragmentPrinter; + + /** + * \brief callback to print chunks for which no specific callback was specified. + * + * Arguments: output stream, packet uid, size, header/trailer name, fragment information + */ + typedef Callback + DefaultPrinter; + + PacketPrinter (); + + /** + * Print the content of the packet forward. + */ + void PrintForward (void); + /** + * Print the content of the packet backward. + */ + void PrintBackward (void); + void SetSeparator (std::string separator); + /** + * \param printer printer for payload + */ + void AddPayloadPrinter (PayloadPrinter printer); + /** + * \param printer printer for the specified chunk + * \param fragmentPrinter printer for a fragment of the specified chunk + * + * If the user has not specified a callback for a specific header present + * in a packet, the "default" callback is invoked. If no such callback + * was specified, nothing happens. + */ + template + void AddHeaderPrinter (Callback printer, + ChunkFragmentPrinter fragmentPrinter); + /** + * \param printer printer for the specified chunk + * \param fragmentPrinter printer for a fragment of the specified chunk + * + * If the user has not specified a callback for a specific trailer present + * in a packet, the "default" callback is invoked. If no such callback + * was specified, nothing happens. + */ + template + void AddTrailerPrinter (Callback printer, + ChunkFragmentPrinter fragmentPrinter); + /** + * \param printer printer for a chunk for which no callback was specified explicitely + */ + void AddDefaultPrinter (DefaultPrinter printer); + +private: + friend class PacketMetadata; + typedef void (*DoPrintCallback) (Ptr, Buffer::Iterator, std::ostream &, + uint32_t, uint32_t); + typedef std::string (*DoGetNameCallback) (void); + struct Printer + { + uint32_t m_chunkUid; + Ptr m_printer; + Callback + m_fragmentPrinter; + }; + struct RegisteredChunk + { + DoPrintCallback printCallback; + DoGetNameCallback getNameCallback; + bool isHeader; + }; + typedef std::vector PrinterList; + typedef std::vector RegisteredChunks; + + + static PacketPrinter GetDefault (void); + static PacketPrinter *PeekDefault (void); + static PacketPrinter *CreateStaticDefault (void); + static void DoDefaultPrintPayload (std::ostream & os, + uint32_t packetUid, + uint32_t size, + struct PacketPrinter::FragmentInformation info); + static void DoDefaultPrintDefault (std::ostream & os, + uint32_t packetUid, + uint32_t size, + std::string &name, + struct PacketPrinter::FragmentInformation info); + template + static void DoDefaultPrint (std::ostream &os, uint32_t packetUid, uint32_t size, const T *chunk); + static void DoDefaultPrintFragment (std::ostream & os, + uint32_t packetUid, + uint32_t size, + std::string &name, + struct PacketPrinter::FragmentInformation info); + + template + static void DoPrint (Ptr callbackPrinter, + Buffer::Iterator i, + std::ostream &os, + uint32_t packetUid, + uint32_t size); + template + static std::string DoGetName (void); + template + static uint32_t GetTrailerUid (void); + template + static uint32_t GetHeaderUid (void); + template + static uint32_t AllocateUid (bool isHeader); + static RegisteredChunks *GetRegisteredChunks (void); + static bool IsTrailer (uint32_t uid); + static bool IsHeader (uint32_t uid); + + + void PrintChunk (uint32_t uid, + Buffer::Iterator i, + std::ostream &os, + uint32_t packetUid, + uint32_t size) const; + void PrintChunkFragment (uint32_t uid, + std::ostream &os, + uint32_t packetUid, + uint32_t size, + uint32_t fragmentStart, + uint32_t fragmentEnd) const; + void PrintPayload (std::ostream &os, uint32_t packetUid, uint32_t size, + uint32_t fragmentStart, uint32_t fragmentEnd) const; + void DoAddPrinter (uint32_t uid, + Ptr printer, + Callback fragmentPrinter); + + static PacketPrinter m_defaultPacketPrinter; + PrinterList m_printerList; + PayloadPrinter m_payloadPrinter; + DefaultPrinter m_defaultPrinter; + bool m_forward; + std::string m_separator; +}; + +} // namespace ns3 + +namespace ns3 { + +template +void +PacketPrinter::AddHeaderPrinter (Callback printer, + Callback fragmentPrinter) +{ + static uint32_t uid = PacketPrinter::GetHeaderUid (); + DoAddPrinter (uid, printer.GetImpl (), fragmentPrinter); +} + +template +void +PacketPrinter::AddTrailerPrinter (Callback printer, + Callback fragmentPrinter) +{ + static uint32_t uid = PacketPrinter::GetTrailerUid (); + DoAddPrinter (uid, printer.GetImpl (), fragmentPrinter); +} + +template +void +PacketPrinter::DoPrint (Ptr printerCallback, + Buffer::Iterator i, + std::ostream &os, + uint32_t packetUid, + uint32_t size) +{ + T chunk = T (); + chunk.Deserialize (i); + Callback callback; + callback.Assign (printerCallback); + callback (os, packetUid, size, &chunk); +} + +template +std::string +PacketPrinter::DoGetName (void) +{ + T chunk = T (); + return chunk.GetName (); +} + +template +uint32_t +PacketPrinter::GetHeaderUid (void) +{ + static uint32_t uid = PacketPrinter::AllocateUid (true); + return uid; +} + +template +uint32_t +PacketPrinter::GetTrailerUid (void) +{ + static uint32_t uid = PacketPrinter::AllocateUid (false); + return uid; +} + +template +uint32_t +PacketPrinter::AllocateUid (bool isHeader) +{ + RegisteredChunks *chunks = PacketPrinter::GetRegisteredChunks (); + RegisteredChunk chunk; + chunk.printCallback = &PacketPrinter::DoPrint; + chunk.getNameCallback = &PacketPrinter::DoGetName; + chunk.isHeader = isHeader; + chunks->push_back (chunk); + uint32_t uid = chunks->size () * 2; + PacketPrinter::PeekDefault ()->DoAddPrinter (uid, + MakeCallback (&PacketPrinter::DoDefaultPrint).GetImpl (), + MakeCallback (&PacketPrinter::DoDefaultPrintFragment)); + return uid; +} + +template +void +PacketPrinter::DoDefaultPrint (std::ostream &os, uint32_t packetUid, uint32_t size, const T *chunk) +{ + os << chunk->GetName () << " "; + chunk->Print (os); +} + + + +} // namespace ns3 + +#endif /* PACKET_PRINTER_H */ diff --git a/src/common/packet.cc b/src/common/packet.cc index 4acc7fe0e..8f989f8f3 100644 --- a/src/common/packet.cc +++ b/src/common/packet.cc @@ -19,6 +19,7 @@ * Author: Mathieu Lacage */ #include "packet.h" +#include "packet-printer.h" #include "ns3/assert.h" namespace ns3 { @@ -27,20 +28,20 @@ uint32_t Packet::m_globalUid = 0; Packet::Packet () : m_buffer (), - m_uid (m_globalUid) + m_metadata (m_globalUid, 0) { m_globalUid++; } Packet::Packet (uint32_t size) : m_buffer (size), - m_uid (m_globalUid) + m_metadata (m_globalUid, size) { m_globalUid++; } Packet::Packet (uint8_t const*buffer, uint32_t size) : m_buffer (), - m_uid (m_globalUid) + m_metadata (m_globalUid, size) { m_globalUid++; m_buffer.AddAtStart (size); @@ -48,17 +49,20 @@ Packet::Packet (uint8_t const*buffer, uint32_t size) i.Write (buffer, size); } -Packet::Packet (Buffer buffer, Tags tags, uint32_t uid) +Packet::Packet (Buffer buffer, Tags tags, PacketMetadata metadata) : m_buffer (buffer), m_tags (tags), - m_uid (uid) + m_metadata (metadata) {} Packet Packet::CreateFragment (uint32_t start, uint32_t length) const { Buffer buffer = m_buffer.CreateFragment (start, length); - return Packet (buffer, m_tags, m_uid); + NS_ASSERT (m_buffer.GetSize () >= start + length); + uint32_t end = m_buffer.GetSize () - (start + length); + PacketMetadata metadata = m_metadata.CreateFragment (start, end); + return Packet (buffer, m_tags, metadata); } uint32_t @@ -70,6 +74,9 @@ Packet::GetSize (void) const void Packet::AddAtEnd (Packet packet) { + packet.m_buffer.TransformIntoRealBuffer (); + m_buffer.TransformIntoRealBuffer (); + Buffer src = packet.m_buffer; m_buffer.AddAtEnd (src.GetSize ()); Buffer::Iterator destStart = m_buffer.End (); @@ -79,21 +86,25 @@ Packet::AddAtEnd (Packet packet) * XXX: we might need to merge the tag list of the * other packet into the current packet. */ + m_metadata.AddAtEnd (packet.m_metadata); } void Packet::AddPaddingAtEnd (uint32_t size) { m_buffer.AddAtEnd (size); + m_metadata.AddPaddingAtEnd (size); } void Packet::RemoveAtEnd (uint32_t size) { m_buffer.RemoveAtEnd (size); + m_metadata.RemoveAtEnd (size); } void Packet::RemoveAtStart (uint32_t size) { m_buffer.RemoveAtStart (size); + m_metadata.RemoveAtStart (size); } void @@ -111,12 +122,26 @@ Packet::PeekData (void) const uint32_t Packet::GetUid (void) const { - return m_uid; + return m_metadata.GetUid (); } void Packet::Print (std::ostream &os) const -{} +{ + m_metadata.PrintDefault (os, m_buffer); +} + +void +Packet::Print (std::ostream &os, const PacketPrinter &printer) const +{ + m_metadata.Print (os, m_buffer, printer); +} + +void +Packet::EnableMetadata (void) +{ + PacketMetadata::Enable (); +} }; // namespace ns3 diff --git a/src/common/packet.h b/src/common/packet.h index 4a0154d73..09090164c 100644 --- a/src/common/packet.h +++ b/src/common/packet.h @@ -26,11 +26,14 @@ #include "header.h" #include "trailer.h" #include "tags.h" +#include "packet-metadata.h" #include "ns3/callback.h" #include "ns3/assert.h" namespace ns3 { +class PacketPrinter; + /** * \brief network packets * @@ -129,7 +132,7 @@ public: uint32_t GetSize (void) const; /** * Add header to this packet. This method invokes the - * ns3::Header::GetSerializedSize and ns3::Header::SerializeTo + * ns3::Chunk::GetSerializedSize and ns3::Chunk::SerializeTo * methods to reserve space in the buffer and request the * header to serialize itself in the packet buffer. * @@ -139,8 +142,7 @@ public: void AddHeader (T const &header); /** * Deserialize and remove the header from the internal buffer. - * This method invokes ns3::Header::DeserializeFrom - * and then removes the deserialized bytes from the buffer. + * This method invokes ns3::Chunk::DeserializeFrom. * * \param header a reference to the header to remove from the internal buffer. * \returns the number of bytes removed from the packet. @@ -149,7 +151,7 @@ public: uint32_t RemoveHeader (T &header); /** * Add trailer to this packet. This method invokes the - * ns3::Trailer::GetSerializedSize and ns3::Trailer::serializeTo + * ns3::Chunk::GetSerializedSize and ns3::Trailer::serializeTo * methods to reserve space in the buffer and request the trailer * to serialize itself in the packet buffer. * @@ -159,8 +161,7 @@ public: void AddTrailer (T const &trailer); /** * Remove a deserialized trailer from the internal buffer. - * This method invokes the ns3::Trailer::DeserializeFrom method - * and then removes the deserialized bytes from the buffer. + * This method invokes the ns3::Chunk::DeserializeFrom method. * * \param trailer a reference to the trailer to remove from the internal buffer. * \returns the number of bytes removed from the end of the packet. @@ -254,12 +255,50 @@ public: */ uint32_t GetUid (void) const; + /** + * \param os output stream in which the data should be printed. + * + * Iterate over the headers and trailers present in this packet, + * from the first header to the last trailer and invoke, for + * each of them, the user-provided method Header::DoPrint or + * Trailer::DoPrint methods. + */ void Print (std::ostream &os) const; + /** + * \param os output stream in which the data should be printed. + * \param printer the output formatter to use to print + * the content of this packet. + * + * Iterate over the headers and trailers present in this packet, + * either in the "forward" (first header to last trailer) or in + * the "backward" (last trailer to first header) direction, as + * specified by the PacketPrinter::PrintForward or the + * PacketPrinter::PrintBackward methods. For each header, trailer, + * or fragment of a header or a trailer, invoke the user-specified + * print callback stored in the specified PacketPrinter. + */ + void Print (std::ostream &os, const PacketPrinter &printer) const; + + /** + * By default, packets do not keep around enough metadata to + * perform the operations requested by the Print methods. If you + * want to be able to invoke any of the two ::Print methods, + * you need to invoke this method at least once during the + * simulation setup and before any packet is created. + * + * The packet metadata is also used to perform extensive + * sanity checks at runtime when performing operations on a + * Packet. For example, this metadata is used to verify that + * when you remove a header from a packet, this same header + * was actually present at the front of the packet. These + * errors will be detected and will abort the program. + */ + static void EnableMetadata (void); private: - Packet (Buffer buffer, Tags tags, uint32_t uid); + Packet (Buffer buffer, Tags tags, PacketMetadata metadata); Buffer m_buffer; Tags m_tags; - uint32_t m_uid; + PacketMetadata m_metadata; static uint32_t m_globalUid; }; @@ -282,6 +321,7 @@ Packet::AddHeader (T const &header) uint32_t size = header.GetSize (); m_buffer.AddAtStart (size); header.Serialize (m_buffer.Begin ()); + m_metadata.AddHeader (header, size); } template uint32_t @@ -291,6 +331,7 @@ Packet::RemoveHeader (T &header) "Must pass Header subclass to Packet::RemoveHeader"); uint32_t deserialized = header.Deserialize (m_buffer.Begin ()); m_buffer.RemoveAtStart (deserialized); + m_metadata.RemoveHeader (header, deserialized); return deserialized; } template @@ -303,6 +344,7 @@ Packet::AddTrailer (T const &trailer) m_buffer.AddAtEnd (size); Buffer::Iterator end = m_buffer.End (); trailer.Serialize (end); + m_metadata.AddTrailer (trailer, size); } template uint32_t @@ -312,6 +354,7 @@ Packet::RemoveTrailer (T &trailer) "Must pass Trailer subclass to Packet::RemoveTrailer"); uint32_t deserialized = trailer.Deserialize (m_buffer.End ()); m_buffer.RemoveAtEnd (deserialized); + m_metadata.RemoveTrailer (trailer, deserialized); return deserialized; } diff --git a/src/common/trailer.h b/src/common/trailer.h index a2f9589ae..92aef295f 100644 --- a/src/common/trailer.h +++ b/src/common/trailer.h @@ -67,9 +67,26 @@ class Trailer : public Chunk { public: virtual ~Trailer (); private: + /** + * \returns a user-readable name to identify this type of header. + * + * The string returned is expected to be a single word with + * all capital letters + */ + virtual std::string DoGetName (void) const = 0; /** * \param os the std output stream in which this * protocol trailer must print itself. + * + * Although the header is free to format its output as it + * wishes, it is recommended to follow a few rules to integrate + * with the packet pretty printer: + * - start with flags, small field values located between a + * pair of parens. Values should be separated by whitespace. + * - follow the parens with the important fields, separated by + * whitespace. + * i.e.: + * (field1 val1 field2 val2 field3 val3) field4 val4 field5 val5 */ virtual void PrintTo (std::ostream &os) const = 0; diff --git a/src/common/wscript b/src/common/wscript index 65753d930..c6f3be7f0 100644 --- a/src/common/wscript +++ b/src/common/wscript @@ -10,9 +10,11 @@ def build(bld): common.uselib_local = ['ns3-core', 'ns3-simulator'] common.source = [ 'buffer.cc', - 'header.cc', 'chunk.cc', + 'header.cc', 'trailer.cc', + 'packet-printer.cc', + 'packet-metadata.cc', 'packet.cc', 'tags.cc', 'pcap-writer.cc', @@ -28,11 +30,13 @@ def build(bld): headers = bld.create_obj('ns3header') headers.source = [ 'buffer.h', - 'header.h', 'chunk.h', + 'header.h', 'trailer.h', 'tags.h', 'packet.h', + 'packet-printer.h', + 'packet-metadata.h', 'uv-trace-source.h', 'sv-trace-source.h', 'fv-trace-source.h', diff --git a/src/core/callback.h b/src/core/callback.h index 6acdd81a7..79597cca0 100644 --- a/src/core/callback.h +++ b/src/core/callback.h @@ -225,6 +225,7 @@ class CallbackBase { public: virtual ~CallbackBase () {} virtual CallbackImplBase *PeekImpl (void) const = 0; + virtual Ptr GetImpl (void) const = 0; }; /** @@ -278,7 +279,7 @@ public: : m_impl (impl) {} - bool IsNull (void) { + bool IsNull (void) const { return (PeekImpl () == 0)?true:false; } void Nullify (void) { @@ -330,6 +331,19 @@ public: const Callback *goodType = static_cast *> (&other); *this = *goodType; } + void Assign (Ptr other) { + CallbackImpl *impl = dynamic_cast *> (PeekPointer (other)); + if (other == 0) + { + NS_FATAL_ERROR ("Incompatible types. (feed to \"c++filt -t\")" + " got=" << typeid (other).name () << + ", expected=" << typeid (*impl).name ()); + } + *this = Callback (impl); + } + virtual PtrGetImpl (void) const { + return m_impl; + } private: virtual CallbackImpl *PeekImpl (void) const { return PeekPointer (m_impl); diff --git a/src/internet-node/arp-header.cc b/src/internet-node/arp-header.cc index 3554681ee..e340404f4 100644 --- a/src/internet-node/arp-header.cc +++ b/src/internet-node/arp-header.cc @@ -83,24 +83,36 @@ ArpHeader::GetDestinationIpv4Address (void) return m_ipv4Dest; } +std::string +ArpHeader::DoGetName (void) const +{ + return "ARP"; +} void ArpHeader::PrintTo (std::ostream &os) const { - os << "(arp)"; if (IsRequest ()) { - os << " source mac: " << m_macSource - << " source ipv4: " << m_ipv4Source - << " dest ipv4: " << m_ipv4Dest; + os << "(" + << "request " + << "source mac: " << m_macSource << " " + << "source ipv4: " << m_ipv4Source << " " + << "dest ipv4: " << m_ipv4Dest + << ")" + ; } else { NS_ASSERT (IsReply ()); - os << " source mac: " << m_macSource - << " source ipv4: " << m_ipv4Source - << " dest mac: " << m_macDest - << " dest ipv4: " < " << m_destination + ; } uint32_t Ipv4Header::GetSerializedSize (void) const diff --git a/src/internet-node/ipv4-header.h b/src/internet-node/ipv4-header.h index 910313073..b45ed9f02 100644 --- a/src/internet-node/ipv4-header.h +++ b/src/internet-node/ipv4-header.h @@ -140,6 +140,7 @@ public: bool IsChecksumOk (void) const; private: + virtual std::string DoGetName (void) const; virtual void PrintTo (std::ostream &os) const; virtual uint32_t GetSerializedSize (void) const; virtual void SerializeTo (Buffer::Iterator start) const; diff --git a/src/internet-node/udp-header.cc b/src/internet-node/udp-header.cc index 38a7fa7d8..a7a95ca49 100644 --- a/src/internet-node/udp-header.cc +++ b/src/internet-node/udp-header.cc @@ -91,15 +91,20 @@ UdpHeader::InitializeChecksum (Ipv4Address source, m_initialChecksum = Ipv4ChecksumCalculate (0, buf, 12); } - +std::string +UdpHeader::DoGetName (void) const +{ + return "UDP"; +} void UdpHeader::PrintTo (std::ostream &os) const { - os << "(udp)" - << ", port source=" << m_sourcePort - << ", port destination=" << m_destinationPort - << ", length=" << m_payloadSize; + os << "(" + << "length: " << m_payloadSize + GetSize () + << ") " + << m_sourcePort << " > " << m_destinationPort + ; } uint32_t diff --git a/src/internet-node/udp-header.h b/src/internet-node/udp-header.h index 1948440cf..332614ba0 100644 --- a/src/internet-node/udp-header.h +++ b/src/internet-node/udp-header.h @@ -81,6 +81,7 @@ public: uint8_t protocol); private: + virtual std::string DoGetName (void) const; virtual void PrintTo (std::ostream &os) const; virtual uint32_t GetSerializedSize (void) const; virtual void SerializeTo (Buffer::Iterator start) const; diff --git a/src/node/llc-snap-header.cc b/src/node/llc-snap-header.cc index 6f4b2295f..ee37ffecf 100644 --- a/src/node/llc-snap-header.cc +++ b/src/node/llc-snap-header.cc @@ -47,14 +47,20 @@ LlcSnapHeader::GetSerializedSize (void) const return 1 + 1 + 1 + 3 + 2; } +std::string +LlcSnapHeader::DoGetName (void) const +{ + return "LLCSNAP"; +} + void LlcSnapHeader::PrintTo (std::ostream &os) const { - os << "(mac)" - << " EtherType: "; + os << "(type 0x"; os.setf (std::ios::hex, std::ios::basefield); os << m_etherType; os.setf (std::ios::dec, std::ios::basefield); + os << ")"; } void diff --git a/src/node/llc-snap-header.h b/src/node/llc-snap-header.h index 5b7457ad6..ad98cda7c 100644 --- a/src/node/llc-snap-header.h +++ b/src/node/llc-snap-header.h @@ -37,6 +37,7 @@ class LlcSnapHeader : public Header { uint16_t GetType (void); private: + virtual std::string DoGetName (void) const; virtual void PrintTo (std::ostream &os) const; virtual uint32_t GetSerializedSize (void) const; virtual void SerializeTo (Buffer::Iterator start) const; diff --git a/utils/bench-packets.cc b/utils/bench-packets.cc index b99274b94..a6986d272 100644 --- a/utils/bench-packets.cc +++ b/utils/bench-packets.cc @@ -18,97 +18,168 @@ * * Author: Mathieu Lacage */ -#include "ns3/wall-clock-ms.h" +#include "ns3/system-wall-clock-ms.h" #include "ns3/packet.h" -#include "ns3/chunk-constant-data.h" -#include "ns3/chunk-udp.h" -#include "ns3/chunk-ipv4.h" +#include "ns3/packet-metadata.h" #include +#include using namespace ns3; +template +class BenchHeader : public Header +{ +public: + BenchHeader (); + bool IsOk (void) const; +private: + virtual std::string DoGetName (void) const; + virtual void PrintTo (std::ostream &os) const; + virtual uint32_t GetSerializedSize (void) const; + virtual void SerializeTo (Buffer::Iterator start) const; + virtual uint32_t DeserializeFrom (Buffer::Iterator start); + bool m_ok; +}; + +template +BenchHeader::BenchHeader () + : m_ok (false) +{} + +template +bool +BenchHeader::IsOk (void) const +{ + return m_ok; +} + +template +std::string +BenchHeader::DoGetName (void) const +{ + std::ostringstream oss; + oss << N; + return oss.str (); +} + +template +void +BenchHeader::PrintTo (std::ostream &os) const +{ + NS_ASSERT (false); +} +template +uint32_t +BenchHeader::GetSerializedSize (void) const +{ + return N; +} +template +void +BenchHeader::SerializeTo (Buffer::Iterator start) const +{ + start.WriteU8 (N, N); +} +template +uint32_t +BenchHeader::DeserializeFrom (Buffer::Iterator start) +{ + m_ok = true; + for (int i = 0; i < N; i++) + { + if (start.ReadU8 () != N) + { + m_ok = false; + } + } + return N; +} + + + static void benchPtrA (uint32_t n) { - ChunkConstantData data = ChunkConstantData (2000, 1); - ChunkUdp udp; - ChunkIpv4 ipv4; + BenchHeader<25> ipv4; + BenchHeader<8> udp; for (uint32_t i = 0; i < n; i++) { - Packet p; - p.add (&data); - p.add (&udp); - p.add (&ipv4); + Packet p (2000); + p.AddHeader (udp); + p.AddHeader (ipv4); Packet o = p; - o.peek (&ipv4); - o.remove (&ipv4); - o.peek (&udp); - o.remove (&udp); - o.peek (&data); - o.remove (&data); + o.RemoveHeader (ipv4); + o.RemoveHeader (udp); } } static void benchPtrB (uint32_t n) { - ChunkConstantData data = ChunkConstantData (2000, 1); - ChunkUdp udp; - ChunkIpv4 ipv4; + BenchHeader<25> ipv4; + BenchHeader<8> udp; for (uint32_t i = 0; i < n; i++) { - Packet p; - p.add (&data); - p.add (&udp); - p.add (&ipv4); + Packet p (2000); + p.AddHeader (udp); + p.AddHeader (ipv4); } } static void ptrC2 (Packet p) { - ChunkConstantData data = ChunkConstantData (2000, 1); - ChunkUdp udp; + BenchHeader<8> udp; - p.peek (&udp); - p.remove (&udp); - p.peek (&data); - p.remove (&data); + p.RemoveHeader (udp); } static void ptrC1 (Packet p) { - ChunkIpv4 ipv4; - p.peek (&ipv4); - p.remove (&ipv4); + BenchHeader<25> ipv4; + p.RemoveHeader (ipv4); ptrC2 (p); } static void benchPtrC (uint32_t n) { - ChunkConstantData data = ChunkConstantData (2000, 1); - ChunkUdp udp; - ChunkIpv4 ipv4; + BenchHeader<25> ipv4; + BenchHeader<8> udp; for (uint32_t i = 0; i < n; i++) { - Packet p; - p.add (&data); - p.add (&udp); - p.add (&ipv4); + Packet p (2000); + p.AddHeader (udp); + p.AddHeader (ipv4); ptrC1 (p); } } +static void +benchPrint (uint32_t n) +{ + PacketPrinter printer; + BenchHeader<25> ipv4; + BenchHeader<8> udp; + Packet p (2000); + p.AddHeader (udp); + p.AddHeader (ipv4); + + for (uint32_t i = 0; i < n; i++) + { + p.Print (std::cerr, printer); + } +} + static void runBench (void (*bench) (uint32_t), uint32_t n, char const *name) { - WallClockMs time; - time.start (); + SystemWallClockMs time; + time.Start (); (*bench) (n); - unsigned long long deltaMs = time.end (); + unsigned long long deltaMs = time.End (); double ps = n; ps *= 1000; ps /= deltaMs; @@ -119,17 +190,31 @@ int main (int argc, char *argv[]) { uint32_t n = 0; while (argc > 0) { - if (strncmp ("--n=", argv[0],strlen ("--n=")) == 0) { + if (strncmp ("--n=", argv[0],strlen ("--n=")) == 0) + { char const *nAscii = argv[0] + strlen ("--n="); n = atoi (nAscii); - } + } argc--; argv++; } + runBench (&benchPtrA, n, "a"); runBench (&benchPtrB, n, "b"); runBench (&benchPtrC, n, "c"); + Packet::EnableMetadata (); + runBench (&benchPrint, n, "print"); + PacketMetadata::SetOptOne (false); + runBench (&benchPtrA, n, "meta-a"); + runBench (&benchPtrB, n, "meta-b"); + runBench (&benchPtrC, n, "meta-c"); + PacketMetadata::SetOptOne (true); + runBench (&benchPtrA, n, "meta-a-opt"); + runBench (&benchPtrB, n, "meta-b-opt"); + runBench (&benchPtrC, n, "meta-c-opt"); + + return 0; }