/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2006,2007 INRIA * * 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/log.h" #include "packet-metadata.h" #include "buffer.h" #include "header.h" #include "trailer.h" NS_LOG_COMPONENT_DEFINE ("PacketMetadata"); namespace ns3 { bool PacketMetadata::m_enable = false; bool PacketMetadata::m_enableChecking = false; bool PacketMetadata::m_metadataSkipped = false; uint32_t PacketMetadata::m_maxSize = 0; uint16_t PacketMetadata::m_chunkUid = 0; PacketMetadata::DataFreeList PacketMetadata::m_freeList; PacketMetadata::DataFreeList::~DataFreeList () { for (iterator i = begin (); i != end (); i++) { PacketMetadata::Deallocate (*i); } PacketMetadata::m_enable = false; } void PacketMetadata::Enable (void) { NS_ASSERT_MSG (!m_metadataSkipped, "Error: attempting to enable the packet metadata " "subsystem too late in the simulation, which is not allowed.\n" "A common cause for this problem is to enable ASCII tracing " "after sending any packets. One way to fix this problem is " "to call ns3::PacketMetadata::Enable () near the beginning of" " the program, before any packets are sent."); m_enable = true; } void PacketMetadata::EnableChecking (void) { Enable (); m_enableChecking = true; } 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); } } bool PacketMetadata::IsSharedPointerOk (uint16_t pointer) const { bool ok = pointer == 0xffff || pointer <= m_data->m_size; return ok; } bool PacketMetadata::IsPointerOk (uint16_t pointer) const { bool ok = pointer == 0xffff || pointer <= m_used; return ok; } bool PacketMetadata::IsStateOk (void) const { bool ok = m_used <= m_data->m_size; ok &= IsPointerOk (m_head); ok &= IsPointerOk (m_tail); uint16_t current = m_head; while (ok && current != 0xffff) { struct PacketMetadata::SmallItem item; PacketMetadata::ExtraItem extraItem; ReadItems (current, &item, &extraItem); ok &= IsSharedPointerOk (item.next); ok &= IsSharedPointerOk (item.prev); if (current != m_head) { ok &= IsPointerOk (item.prev); } if (current != m_tail) { ok &= IsPointerOk (item.next); } if (current == m_tail) { break; } current = item.next; } return ok; } 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; } void PacketMetadata::Append32 (uint32_t value, uint8_t *buffer) { buffer[0] = value & 0xff; buffer[1] = (value >> 8) & 0xff; buffer[2] = (value >> 16) & 0xff; buffer[3] = (value >> 24) & 0xff; } 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) { NS_LOG_FUNCTION (this << 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) { NS_LOG_FUNCTION (this << 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_LOG_FUNCTION (this << item->next << item->prev << item->typeUid << item->size << item->chunkUid); NS_ASSERT (m_data != 0); NS_ASSERT (m_used != item->prev && m_used != item->next); uint32_t typeUidSize = GetUleb128Size (item->typeUid); uint32_t sizeSize = GetUleb128Size (item->size); uint32_t n = 2 + 2 + typeUidSize + sizeSize + 2; if (m_used + n > m_data->m_size || (m_head != 0xffff && m_data->m_count != 1 && m_used != m_data->m_dirtyEnd)) { ReserveCopy (n); } 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); return n; } uint16_t PacketMetadata::AddBig (uint32_t next, uint32_t prev, const PacketMetadata::SmallItem *item, const PacketMetadata::ExtraItem *extraItem) { NS_LOG_FUNCTION (this << next << prev << item->next << item->prev << item->typeUid << item->size << item->chunkUid << extraItem->fragmentStart << extraItem->fragmentEnd << extraItem->packetUid); 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); uint32_t typeUidSize = GetUleb128Size (typeUid); uint32_t sizeSize = GetUleb128Size (item->size); uint32_t fragStartSize = GetUleb128Size (extraItem->fragmentStart); uint32_t fragEndSize = GetUleb128Size (extraItem->fragmentEnd); uint32_t n = 2 + 2 + typeUidSize + sizeSize + 2 + fragStartSize + fragEndSize + 4; if (m_used + n > m_data->m_size || (m_head != 0xffff && m_data->m_count != 1 && m_used != m_data->m_dirtyEnd)) { ReserveCopy (n); } uint8_t *buffer = &m_data->m_data[m_used]; Append16 (next, buffer); buffer += 2; Append16 (prev, buffer); buffer += 2; AppendValue (typeUid, buffer); buffer += typeUidSize; AppendValue (item->size, buffer); buffer += sizeSize; Append16 (item->chunkUid, buffer); buffer += 2; AppendValue (extraItem->fragmentStart, buffer); buffer += fragStartSize; AppendValue (extraItem->fragmentEnd, buffer); buffer += fragEndSize; Append32 (extraItem->packetUid, buffer); return n; } /** * \param item the item data to write * \param extraItem the extra item data to write * \param available the number of bytes which can * be written without having to rewrite the buffer entirely. */ void PacketMetadata::ReplaceTail (PacketMetadata::SmallItem *item, PacketMetadata::ExtraItem *extraItem, uint32_t available) { NS_LOG_FUNCTION (this << item->next << item->prev << item->typeUid << item->size << item->chunkUid << extraItem->fragmentStart << extraItem->fragmentEnd << extraItem->packetUid << available); NS_ASSERT (m_data != 0); /* If the tail we want to replace is located at the end of the data array, * and if there is extra room at the end of this array, then, * we can try to use that extra space to avoid falling in the slow * path below. */ if (m_tail + available == m_used && m_used == m_data->m_dirtyEnd) { available = m_data->m_size - m_tail; } uint32_t typeUid = ((item->typeUid & 0x1) == 0x1) ? item->typeUid : item->typeUid+1; uint32_t typeUidSize = GetUleb128Size (typeUid); uint32_t sizeSize = GetUleb128Size (item->size); uint32_t fragStartSize = GetUleb128Size (extraItem->fragmentStart); uint32_t fragEndSize = GetUleb128Size (extraItem->fragmentEnd); uint32_t n = 2 + 2 + typeUidSize + sizeSize + 2 + fragStartSize + fragEndSize + 4; if (available >= n && m_data->m_count == 1) { uint8_t *buffer = &m_data->m_data[m_tail]; Append16 (item->next, buffer); buffer += 2; Append16 (item->prev, buffer); buffer += 2; AppendValue (typeUid, buffer); buffer += typeUidSize; AppendValue (item->size, buffer); buffer += sizeSize; Append16 (item->chunkUid, buffer); buffer += 2; AppendValue (extraItem->fragmentStart, buffer); buffer += fragStartSize; AppendValue (extraItem->fragmentEnd, buffer); buffer += fragEndSize; Append32 (extraItem->packetUid, buffer); m_used = std::max (m_used, (uint16_t)(buffer - &m_data->m_data[0])); m_data->m_dirtyEnd = m_used; return; } /* Below is the slow path which is hit if the new tail we want * to append is bigger than the previous tail. */ // create a copy of the packet without its tail. 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); current = tmpItem.next; } // append new tail. uint16_t written = h.AddBig (0xffff, h.m_tail, item, extraItem); h.UpdateTail (written); *this = h; } /** * \param current the offset we should start reading the data from * \param item pointer to where we should store the data to return to the caller * \param extraItem pointer to where we should store the data to return to the caller * \returns the number of bytes read. */ uint32_t PacketMetadata::ReadItems (uint16_t current, struct PacketMetadata::SmallItem *item, struct PacketMetadata::ExtraItem *extraItem) const { NS_LOG_FUNCTION (this << current); NS_ASSERT (current <= m_data->m_size); 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_LOG_LOGIC ("create size="< 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. if (m_head == m_tail) { m_head = 0xffff; m_tail = 0xffff; } else { 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); while (current != 0xffff && current != m_tail) { current = item.next; ReadItems (current, &item, &extraItem); written = fragment.AddBig (0xffff, fragment.m_tail, &item, &extraItem); fragment.UpdateTail (written); } *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); NS_ASSERT (IsStateOk ()); } void PacketMetadata::RemoveAtEnd (uint32_t end) { NS_LOG_FUNCTION (this << end); NS_ASSERT (IsStateOk ()); if (!m_enable) { m_metadataSkipped = true; 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. if (m_head == m_tail) { m_head = 0xffff; m_tail = 0xffff; } else { 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); while (current != 0xffff && current != m_head) { current = item.prev; ReadItems (current, &item, &extraItem); written = fragment.AddBig (fragment.m_head, 0xffff, &item, &extraItem); fragment.UpdateHead (written); } *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); NS_ASSERT (IsStateOk ()); } 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; } uint64_t PacketMetadata::GetUid (void) const { return m_packetUid; } PacketMetadata::ItemIterator PacketMetadata::BeginItem (Buffer buffer) const { return ItemIterator (this, buffer); } PacketMetadata::ItemIterator::ItemIterator (const PacketMetadata *metadata, Buffer buffer) : m_metadata (metadata), m_buffer (buffer), m_current (metadata->m_head), m_offset (0), m_hasReadTail (false) { } bool PacketMetadata::ItemIterator::HasNext (void) const { if (m_current == 0xffff) { return false; } if (m_hasReadTail) { return false; } return true; } PacketMetadata::Item PacketMetadata::ItemIterator::Next (void) { struct PacketMetadata::Item item; struct PacketMetadata::SmallItem smallItem; struct PacketMetadata::ExtraItem extraItem; m_metadata->ReadItems (m_current, &smallItem, &extraItem); if (m_current == m_metadata->m_tail) { m_hasReadTail = true; } m_current = smallItem.next; uint32_t uid = (smallItem.typeUid & 0xfffffffe) >> 1; item.tid.SetUid (uid); item.currentTrimedFromStart = extraItem.fragmentStart; item.currentTrimedFromEnd = extraItem.fragmentEnd - smallItem.size; item.currentSize = extraItem.fragmentEnd - extraItem.fragmentStart; if (extraItem.fragmentStart != 0 || extraItem.fragmentEnd != smallItem.size) { item.isFragment = true; } else { item.isFragment = false; } TypeId tid; tid.SetUid (uid); if (uid == 0) { item.type = PacketMetadata::Item::PAYLOAD; } else if (tid.IsChildOf (Header::GetTypeId ())) { item.type = PacketMetadata::Item::HEADER; if (!item.isFragment) { ns3::Buffer tmp = m_buffer; tmp.RemoveAtStart (m_offset); tmp.RemoveAtEnd (tmp.GetSize () - item.currentSize); item.current = tmp.Begin (); } } else if (tid.IsChildOf (Trailer::GetTypeId ())) { item.type = PacketMetadata::Item::TRAILER; if (!item.isFragment) { ns3::Buffer tmp = m_buffer; tmp.RemoveAtEnd (tmp.GetSize () - (m_offset + smallItem.size)); tmp.RemoveAtStart (tmp.GetSize () - item.currentSize); item.current = tmp.End (); } } else { NS_ASSERT (false); } m_offset += extraItem.fragmentEnd - extraItem.fragmentStart; return item; } uint32_t PacketMetadata::GetSerializedSize (void) const { NS_LOG_FUNCTION (this); uint32_t totalSize = 0; // add 8 bytes for the packet uid totalSize += 8; // if packet-metadata not enabled, total size // is simply 4-bytes for itself plus 8-bytes // for packet uid if (!m_enable) { return totalSize; } struct PacketMetadata::SmallItem item; struct PacketMetadata::ExtraItem extraItem; uint32_t current = m_head; while (current != 0xffff) { ReadItems (current, &item, &extraItem); uint32_t uid = (item.typeUid & 0xfffffffe) >> 1; if (uid == 0) { totalSize += 4; } else { TypeId tid; tid.SetUid (uid); totalSize += 4 + tid.GetName ().size (); } totalSize += 1 + 4 + 2 + 4 + 4 + 8; if (current == m_tail) { break; } NS_ASSERT (current != item.next); current = item.next; } return totalSize; } uint32_t PacketMetadata::Serialize (uint8_t* buffer, uint32_t maxSize) const { NS_LOG_FUNCTION (this); uint8_t* start = buffer; buffer = AddToRawU64 (m_packetUid, start, buffer, maxSize); if (buffer == 0) { return 0; } struct PacketMetadata::SmallItem item; struct PacketMetadata::ExtraItem extraItem; uint32_t current = m_head; while (current != 0xffff) { ReadItems (current, &item, &extraItem); NS_LOG_LOGIC ("bytesWritten=" << static_cast (buffer - start) << ", typeUid="<< item.typeUid << ", size="<