diff --git a/src/internet-stack/ipv6-extension-header.cc b/src/internet-stack/ipv6-extension-header.cc index d5850eed0..c71e3d046 100644 --- a/src/internet-stack/ipv6-extension-header.cc +++ b/src/internet-stack/ipv6-extension-header.cc @@ -120,6 +120,83 @@ uint32_t Ipv6ExtensionHeader::Deserialize (Buffer::Iterator start) return GetSerializedSize (); } +OptionField::OptionField(uint32_t optionsOffset) +: m_optionData(0), + m_optionsOffset(optionsOffset) +{ +} + +OptionField::~OptionField() +{ +} + +uint32_t OptionField::GetSerializedSize () const +{ + return m_optionData.GetSize() + CalculatePad((Ipv6OptionHeader::Alignment) {8,0}); +} + +void OptionField::Serialize (Buffer::Iterator start) const +{ + start.Write(m_optionData.Begin(), m_optionData.End()); + uint32_t fill = CalculatePad((Ipv6OptionHeader::Alignment) {8,0}); + NS_LOG_LOGIC("fill with " << fill << " bytes padding"); + switch(fill) + { + case 0: return; + case 1: Ipv6OptionPad1Header().Serialize(start); + return; + default: Ipv6OptionPadnHeader(fill).Serialize(start); + return; + } +} + +uint32_t OptionField::Deserialize (Buffer::Iterator start, uint32_t length) +{ + uint8_t buf[length]; + start.Read(buf,length); + m_optionData = Buffer(); + m_optionData.AddAtEnd(length); + m_optionData.Begin().Write(buf,length); + return length; +} + +void OptionField::AddOption(Ipv6OptionHeader const& option) +{ + NS_LOG_FUNCTION_NOARGS(); + + uint32_t pad = CalculatePad(option.GetAlignment()); + NS_LOG_LOGIC("need " << pad << " bytes padding"); + switch(pad) + { + case 0: break; //no padding needed + case 1: AddOption(Ipv6OptionPad1Header()); + break; + default: AddOption(Ipv6OptionPadnHeader(pad)); + break; + } + + m_optionData.AddAtEnd(option.GetSerializedSize()); + Buffer::Iterator it = m_optionData.End(); + it.Prev(option.GetSerializedSize()); + option.Serialize(it); +} + +uint32_t OptionField::CalculatePad(Ipv6OptionHeader::Alignment alignment) const +{ + return (alignment.offset - (m_optionData.GetSize() + m_optionsOffset)) % alignment.factor; +} + +uint32_t OptionField::GetOptionsOffset() +{ + return m_optionsOffset; +} + +Buffer OptionField::GetOptionBuffer() +{ + return m_optionData; +} + + NS_OBJECT_ENSURE_REGISTERED (Ipv6ExtensionHopByHopHeader); TypeId Ipv6ExtensionHopByHopHeader::GetTypeId () @@ -137,6 +214,7 @@ TypeId Ipv6ExtensionHopByHopHeader::GetInstanceTypeId () const } Ipv6ExtensionHopByHopHeader::Ipv6ExtensionHopByHopHeader () +: OptionField(2) { } @@ -151,7 +229,7 @@ void Ipv6ExtensionHopByHopHeader::Print (std::ostream &os) const uint32_t Ipv6ExtensionHopByHopHeader::GetSerializedSize () const { - return 2; + return 2 + OptionField::GetSerializedSize(); } void Ipv6ExtensionHopByHopHeader::Serialize (Buffer::Iterator start) const @@ -160,6 +238,7 @@ void Ipv6ExtensionHopByHopHeader::Serialize (Buffer::Iterator start) const i.WriteU8 (GetNextHeader ()); i.WriteU8 ((GetLength () >> 3) - 1); + OptionField::Serialize(i); } uint32_t Ipv6ExtensionHopByHopHeader::Deserialize (Buffer::Iterator start) @@ -168,6 +247,7 @@ uint32_t Ipv6ExtensionHopByHopHeader::Deserialize (Buffer::Iterator start) SetNextHeader (i.ReadU8 ()); SetLength ((i.ReadU8 () + 1) << 3); + OptionField::Deserialize(i, GetLength() - 2); return GetSerializedSize (); } @@ -189,6 +269,7 @@ TypeId Ipv6ExtensionDestinationHeader::GetInstanceTypeId () const } Ipv6ExtensionDestinationHeader::Ipv6ExtensionDestinationHeader () +: OptionField(2) { } @@ -203,7 +284,7 @@ void Ipv6ExtensionDestinationHeader::Print (std::ostream &os) const uint32_t Ipv6ExtensionDestinationHeader::GetSerializedSize () const { - return 2; + return 2 + OptionField::GetSerializedSize(); } void Ipv6ExtensionDestinationHeader::Serialize (Buffer::Iterator start) const @@ -212,6 +293,8 @@ void Ipv6ExtensionDestinationHeader::Serialize (Buffer::Iterator start) const i.WriteU8 (GetNextHeader ()); i.WriteU8 ((GetLength () >> 3) - 1); + + OptionField::Serialize(i); } uint32_t Ipv6ExtensionDestinationHeader::Deserialize (Buffer::Iterator start) @@ -220,6 +303,7 @@ uint32_t Ipv6ExtensionDestinationHeader::Deserialize (Buffer::Iterator start) SetNextHeader (i.ReadU8 ()); SetLength ((i.ReadU8 () + 1) << 3); + OptionField::Deserialize(i, GetLength() - 2); return GetSerializedSize (); } diff --git a/src/internet-stack/ipv6-extension-header.h b/src/internet-stack/ipv6-extension-header.h index 7e0f1cc17..80276640c 100644 --- a/src/internet-stack/ipv6-extension-header.h +++ b/src/internet-stack/ipv6-extension-header.h @@ -22,10 +22,12 @@ #define IPV6_EXTENSION_HEADER_H #include +#include #include #include "ns3/header.h" #include "ns3/ipv6-address.h" +#include "ipv6-option-header.h" namespace ns3 { @@ -126,11 +128,69 @@ class Ipv6ExtensionHeader : public Header Buffer m_data; }; +/** + * \brief Option field for an IPv6ExtensionHeader + * Enables adding options to an IPv6ExtensionHeader + * + * Implementor's note: Make sure to add the result of + * OptionField::GetSerializedSize() to your IPv6ExtensionHeader::GetSerializedSize() + * return value. Call OptionField::Serialize and OptionField::Deserialize at the + * end of your corresponding IPv6ExtensionHeader methods. + */ + +class OptionField +{ + public: + OptionField(uint32_t opitonsOffset); + ~OptionField(); + + /** + * \brief Get the serialized size of the packet. + * \return size + */ + uint32_t GetSerializedSize () const; + + /** + * \brief Serialize all added options. + * \param start Buffer iterator + */ + void Serialize (Buffer::Iterator start) const; + + /** + * \brief Deserialize the packet. + * \param start Buffer iterator + * \return size of the packet + */ + uint32_t Deserialize (Buffer::Iterator start, uint32_t length); + + /** + * \brief Serialize the option, prepending pad1 or padn option as necessary + * \param option the option header to serialize + */ + void AddOption(Ipv6OptionHeader const& option); + + /** + * \brief Get the offset where the options begin, measured from the start of + * the extension header. + * \return the offset from the start of the extension header + */ + uint32_t GetOptionsOffset(); + + Buffer GetOptionBuffer(); + + private: + + uint32_t CalculatePad (Ipv6OptionHeader::Alignment alignment) const; + + Buffer m_optionData; + uint32_t m_optionsOffset; +}; + /** * \class Ipv6ExtensionHopByHopHeader * \brief Header of IPv6 Extension "Hop by Hop" */ -class Ipv6ExtensionHopByHopHeader : public Ipv6ExtensionHeader +class Ipv6ExtensionHopByHopHeader : public Ipv6ExtensionHeader, public OptionField { public: /** @@ -186,7 +246,7 @@ class Ipv6ExtensionHopByHopHeader : public Ipv6ExtensionHeader * \class Ipv6ExtensionDestinationHeader * \brief Header of IPv6 Extension Destination */ -class Ipv6ExtensionDestinationHeader : public Ipv6ExtensionHeader +class Ipv6ExtensionDestinationHeader : public Ipv6ExtensionHeader, public OptionField { public: /** diff --git a/src/internet-stack/ipv6-extension.cc b/src/internet-stack/ipv6-extension.cc index 61bf3798f..28c847324 100644 --- a/src/internet-stack/ipv6-extension.cc +++ b/src/internet-stack/ipv6-extension.cc @@ -81,6 +81,87 @@ Ptr Ipv6Extension::GetNode () const return m_node; } +uint8_t +Ipv6Extension::ProcessOptions (Ptr& packet, uint8_t offset, uint8_t length, Ipv6Header const& ipv6Header, Ipv6Address dst, uint8_t *nextHeader, bool& isDropped) +{ + NS_LOG_FUNCTION (this << packet << offset << length << ipv6Header << dst << nextHeader << isDropped); + + // For ICMPv6 Error packets + Ptr malformedPacket = packet->Copy (); + malformedPacket->AddHeader (ipv6Header); + Ptr icmpv6 = GetNode ()->GetObject ()->GetIcmpv6 (); + + Ptr p = packet->Copy (); + p->RemoveAtStart (offset); + + Ptr ipv6OptionDemux = GetNode ()->GetObject (); + Ptr ipv6Option; + + uint8_t processedSize = 0; + const uint8_t *data = p->PeekData (); + uint8_t optionType = 0; + uint8_t optionLength = 0; + + while (length > processedSize && !isDropped) + { + optionType = *(data + processedSize); + ipv6Option = ipv6OptionDemux->GetOption (optionType); + + if (ipv6Option == 0) + { + optionType >>= 6; + switch (optionType) + { + case 0: + optionLength = *(data + processedSize + 1); + break; + + case 1: + NS_LOG_LOGIC ("Unknown Option. Drop!"); + m_dropTrace (packet); + optionLength = 0; + isDropped = true; + break; + + case 2: + NS_LOG_LOGIC ("Unknown Option. Drop!"); + /* TODO */ + /* icmpv6->SendErrorParameterError (malformedPacket, dst, ipv6Header.GetSourceAddress (), Icmpv6Header::ICMPV6_UNKNOWN_OPTION, offset + processedSize); */ + m_dropTrace (packet); + optionLength = 0; + isDropped = true; + break; + + case 3: + NS_LOG_LOGIC ("Unknown Option. Drop!"); + + if (!ipv6Header.GetDestinationAddress ().IsMulticast ()) + { + /* TODO */ + /* icmpv6->SendErrorParameterError (malformedPacket, dst, ipv6Header.GetSourceAddress (), Icmpv6Header::ICMPV6_UNKNOWN_OPTION, offset + processedSize); */ + } + + m_dropTrace (packet); + optionLength = 0; + isDropped = true; + break; + + default: + break; + } + } + else + { + optionLength = ipv6Option->Process (packet, offset + processedSize, ipv6Header, isDropped); + } + + processedSize += optionLength; + p->RemoveAtStart (optionLength); + } + + return processedSize; +} + NS_OBJECT_ENSURE_REGISTERED (Ipv6ExtensionHopByHop); @@ -116,10 +197,6 @@ uint8_t Ipv6ExtensionHopByHop::Process (Ptr& packet, uint8_t offset, Ipv { NS_LOG_FUNCTION (this << packet << offset << ipv6Header << dst << nextHeader << isDropped); - // For ICMPv6 Error packets - Ptr malformedPacket = packet->Copy (); - malformedPacket->AddHeader (ipv6Header); - Ptr p = packet->Copy (); p->RemoveAtStart (offset); @@ -130,74 +207,11 @@ uint8_t Ipv6ExtensionHopByHop::Process (Ptr& packet, uint8_t offset, Ipv *nextHeader = hopbyhopHeader.GetNextHeader (); } - Ptr icmpv6 = GetNode ()->GetObject ()->GetIcmpv6 (); + uint8_t processedSize = hopbyhopHeader.GetOptionsOffset(); + offset += processedSize; + uint8_t length = hopbyhopHeader.GetLength() - hopbyhopHeader.GetOptionsOffset(); - Ptr ipv6OptionDemux = GetNode ()->GetObject (); - Ptr ipv6Option; - - uint8_t totalSize = hopbyhopHeader.GetLength (); - uint8_t processedSize = hopbyhopHeader.GetSerializedSize (); - const uint8_t *data = p->PeekData (); - uint8_t optionType = 0; - uint8_t optionLength = 0; - isDropped = false; - - while (totalSize > processedSize && !isDropped) - { - optionType = *(data + processedSize); - ipv6Option = ipv6OptionDemux->GetOption (optionType); - - if (ipv6Option == 0) - { - optionType >>= 6; - switch (optionType) - { - case 0: - optionLength = *(data + processedSize + 1); - break; - - case 1: - NS_LOG_LOGIC ("Unknown Option. Drop!"); - m_dropTrace (packet); - optionLength = 0; - isDropped = true; - break; - - case 2: - NS_LOG_LOGIC ("Unknown Option. Drop!"); - /* TODO */ - /* icmpv6->SendErrorParameterError (malformedPacket, dst, ipv6Header.GetSourceAddress (), Icmpv6Header::ICMPV6_UNKNOWN_OPTION, offset + processedSize); */ - m_dropTrace (packet); - optionLength = 0; - isDropped = true; - break; - - case 3: - NS_LOG_LOGIC ("Unknown Option. Drop!"); - - if (!ipv6Header.GetDestinationAddress ().IsMulticast ()) - { - /* TODO */ - /* icmpv6->SendErrorParameterError (malformedPacket, dst, ipv6Header.GetSourceAddress (), Icmpv6Header::ICMPV6_UNKNOWN_OPTION, offset + processedSize); */ - } - - m_dropTrace (packet); - optionLength = 0; - isDropped = true; - break; - - default: - break; - } - } - else - { - optionLength = ipv6Option->Process (packet, offset + processedSize, ipv6Header, isDropped); - } - - processedSize += optionLength; - p->RemoveAtStart (optionLength); - } + processedSize += ProcessOptions(packet, offset, length, ipv6Header, dst, nextHeader, isDropped); return processedSize; } @@ -237,10 +251,6 @@ uint8_t Ipv6ExtensionDestination::Process (Ptr& packet, uint8_t offset, { NS_LOG_FUNCTION (this << packet << offset << ipv6Header << dst << nextHeader << isDropped); - // For ICMPv6 Error packets - Ptr malformedPacket = packet->Copy (); - malformedPacket->AddHeader (ipv6Header); - Ptr p = packet->Copy (); p->RemoveAtStart (offset); @@ -251,74 +261,11 @@ uint8_t Ipv6ExtensionDestination::Process (Ptr& packet, uint8_t offset, *nextHeader = destinationHeader.GetNextHeader (); } - Ptr icmpv6 = GetNode ()->GetObject ()->GetIcmpv6 (); + uint8_t processedSize = destinationHeader.GetOptionsOffset(); + offset += processedSize; + uint8_t length = destinationHeader.GetLength() - destinationHeader.GetOptionsOffset(); - Ptr ipv6OptionDemux = GetNode ()->GetObject (); - Ptr ipv6Option; - - uint8_t totalSize = destinationHeader.GetLength (); - uint8_t processedSize = destinationHeader.GetSerializedSize (); - const uint8_t *data = p->PeekData (); - uint8_t optionType = 0; - uint8_t optionLength = 0; - isDropped = false; - - while (totalSize > processedSize && !isDropped) - { - optionType = *(data + processedSize); - ipv6Option = ipv6OptionDemux->GetOption (optionType); - - if (ipv6Option == 0) - { - optionType >>= 6; - switch (optionType) - { - case 0: - optionLength = *(data + processedSize + 1); - break; - - case 1: - NS_LOG_LOGIC ("Unknown Option. Drop!"); - m_dropTrace (packet); - optionLength = 0; - isDropped = true; - break; - - case 2: - NS_LOG_LOGIC ("Unknown Option. Drop!"); - /* TODO */ - /* icmpv6->SendErrorParameterError (malformedPacket, dst, ipv6Header.GetSourceAddress (), Icmpv6Header::ICMPV6_UNKNOWN_OPTION, offset + processedSize); */ - m_dropTrace (packet); - optionLength = 0; - isDropped = true; - break; - - case 3: - NS_LOG_LOGIC ("Unknown Option. Drop!"); - - if (!ipv6Header.GetDestinationAddress ().IsMulticast ()) - { - /* TODO */ - /* icmpv6->SendErrorParameterError (malformedPacket, dst, ipv6Header.GetSourceAddress (), Icmpv6Header::ICMPV6_UNKNOWN_OPTION, offset + processedSize); */ - } - - m_dropTrace (packet); - optionLength = 0; - isDropped = true; - break; - - default: - break; - } - } - else - { - optionLength = ipv6Option->Process (packet, offset + processedSize, ipv6Header, isDropped); - } - - processedSize += optionLength; - p->RemoveAtStart (optionLength); - } + processedSize += ProcessOptions(packet, offset, length, ipv6Header, dst, nextHeader, isDropped); return processedSize; } diff --git a/src/internet-stack/ipv6-extension.h b/src/internet-stack/ipv6-extension.h index 1cb3a01a5..c501eed28 100644 --- a/src/internet-stack/ipv6-extension.h +++ b/src/internet-stack/ipv6-extension.h @@ -90,6 +90,21 @@ class Ipv6Extension : public Object */ virtual uint8_t Process (Ptr& packet, uint8_t offset, Ipv6Header const& ipv6Header, Ipv6Address dst, uint8_t *nextHeader, bool& isDropped) = 0; + /** + * \brief Process options + * Called by implementing classes to process the options + * + * \param packet the packet + * \param offset the offset of the first option to process + * \param length the total length of all options (as specified in the extension header) + * \param ipv6Header the IPv6 header of packet received + * \param dst destination address of the packet received (i.e. us) + * \param nextHeader the next header + * \param isDropped if the packet must be dropped + * \return the size processed + */ + virtual uint8_t ProcessOptions (Ptr& packet, uint8_t offset, uint8_t length, Ipv6Header const& ipv6Header, Ipv6Address dst, uint8_t *nextHeader, bool& isDropped); + protected: TracedCallback > m_dropTrace; diff --git a/src/internet-stack/ipv6-option-header.cc b/src/internet-stack/ipv6-option-header.cc index 21b7705e3..68c9cfd95 100644 --- a/src/internet-stack/ipv6-option-header.cc +++ b/src/internet-stack/ipv6-option-header.cc @@ -81,7 +81,7 @@ void Ipv6OptionHeader::Print (std::ostream &os) const uint32_t Ipv6OptionHeader::GetSerializedSize () const { - return 1; + return m_length + 2; } void Ipv6OptionHeader::Serialize (Buffer::Iterator start) const @@ -89,6 +89,9 @@ void Ipv6OptionHeader::Serialize (Buffer::Iterator start) const Buffer::Iterator i = start; i.WriteU8 (m_type); + i.WriteU8 (m_length); + + i.Write(m_data.Begin(), m_data.End()); } uint32_t Ipv6OptionHeader::Deserialize (Buffer::Iterator start) @@ -96,10 +99,23 @@ uint32_t Ipv6OptionHeader::Deserialize (Buffer::Iterator start) Buffer::Iterator i = start; m_type = i.ReadU8 (); + m_length = i.ReadU8(); + + m_data = Buffer(); + m_data.AddAtEnd(m_length); + Buffer::Iterator dataStart = i; + i.Next(m_length); + Buffer::Iterator dataEnd = i; + m_data.Begin().Write(dataStart, dataEnd); return GetSerializedSize (); } +Ipv6OptionHeader::Alignment Ipv6OptionHeader::GetAlignment() const +{ + return (Alignment){1,0}; +} + NS_OBJECT_ENSURE_REGISTERED (Ipv6OptionPad1Header); TypeId Ipv6OptionPad1Header::GetTypeId () @@ -118,6 +134,7 @@ TypeId Ipv6OptionPad1Header::GetInstanceTypeId () const Ipv6OptionPad1Header::Ipv6OptionPad1Header () { + SetType(0); } Ipv6OptionPad1Header::~Ipv6OptionPad1Header () @@ -166,8 +183,11 @@ TypeId Ipv6OptionPadnHeader::GetInstanceTypeId () const return GetTypeId (); } -Ipv6OptionPadnHeader::Ipv6OptionPadnHeader () +Ipv6OptionPadnHeader::Ipv6OptionPadnHeader (uint32_t pad) { + SetType(1); + NS_ASSERT_MSG (pad >= 2, "PadN must be at least 2 bytes long"); + SetLength (pad - 2); } Ipv6OptionPadnHeader::~Ipv6OptionPadnHeader () @@ -225,6 +245,7 @@ TypeId Ipv6OptionJumbogramHeader::GetInstanceTypeId () const Ipv6OptionJumbogramHeader::Ipv6OptionJumbogramHeader () { + SetType (0xC2); SetLength (4); } @@ -258,7 +279,7 @@ void Ipv6OptionJumbogramHeader::Serialize (Buffer::Iterator start) const i.WriteU8 (GetType ()); i.WriteU8 (GetLength ()); - i.WriteHtonU16 (m_dataLength); + i.WriteHtonU32 (m_dataLength); } uint32_t Ipv6OptionJumbogramHeader::Deserialize (Buffer::Iterator start) @@ -272,6 +293,11 @@ uint32_t Ipv6OptionJumbogramHeader::Deserialize (Buffer::Iterator start) return GetSerializedSize (); } +Ipv6OptionHeader::Alignment Ipv6OptionJumbogramHeader::GetAlignment() const +{ + return (Alignment){4,2}; //4n+2 +} + NS_OBJECT_ENSURE_REGISTERED (Ipv6OptionRouterAlertHeader); TypeId Ipv6OptionRouterAlertHeader::GetTypeId () @@ -338,5 +364,10 @@ uint32_t Ipv6OptionRouterAlertHeader::Deserialize (Buffer::Iterator start) return GetSerializedSize (); } +Ipv6OptionHeader::Alignment Ipv6OptionRouterAlertHeader::GetAlignment() const +{ + return (Alignment){2,0}; //2n+0 +} + } /* namespace ns3 */ diff --git a/src/internet-stack/ipv6-option-header.h b/src/internet-stack/ipv6-option-header.h index e3d90018d..9665d6bf9 100644 --- a/src/internet-stack/ipv6-option-header.h +++ b/src/internet-stack/ipv6-option-header.h @@ -35,6 +35,20 @@ namespace ns3 class Ipv6OptionHeader : public Header { public: + + /** + * \brief represents the alignment requirements of an option header + * + * Represented as factor*n+offset (eg. 8n+2) See RFC 2460. + * No alignemt is represented as 1n+0. + * + */ + struct Alignment + { + uint8_t factor; + uint8_t offset; + }; + /** * \brief Get the type identificator. * \return type identificator @@ -107,6 +121,15 @@ class Ipv6OptionHeader : public Header */ virtual uint32_t Deserialize (Buffer::Iterator start); + /** + * \brief Get the Alignment requirement of this option header + * \return The required alignment + * + * Subclasses should only implement this method, if special alignemt is + * required. Default is no alignment (1n+0). + */ + virtual Alignment GetAlignment() const; + private: /** * \brief The type of the option. @@ -118,6 +141,12 @@ class Ipv6OptionHeader : public Header */ uint8_t m_length; + /** + * \brief The anonymous data of this option + */ + + Buffer m_data; + }; /** @@ -197,8 +226,9 @@ class Ipv6OptionPadnHeader : public Ipv6OptionHeader /** * \brief Constructor. + * \param pad Number of bytes to pad (>=2) */ - Ipv6OptionPadnHeader (); + Ipv6OptionPadnHeader (uint32_t pad = 2); /** * \brief Destructor. @@ -299,6 +329,12 @@ class Ipv6OptionJumbogramHeader : public Ipv6OptionHeader */ virtual uint32_t Deserialize (Buffer::Iterator start); + /** + * \brief Get the Alignment requirement of this option header + * \return The required alignment + */ + virtual Alignment GetAlignment() const; + private: /** * \brief The data length. @@ -372,6 +408,12 @@ class Ipv6OptionRouterAlertHeader : public Ipv6OptionHeader */ virtual uint32_t Deserialize (Buffer::Iterator start); + /** + * \brief Get the Alignment requirement of this option header + * \return The required alignment + */ + virtual Alignment GetAlignment() const; + private: /** * \brief The value. diff --git a/src/internet-stack/wscript b/src/internet-stack/wscript index 73e3dc943..05f512b58 100644 --- a/src/internet-stack/wscript +++ b/src/internet-stack/wscript @@ -130,6 +130,7 @@ def build(bld): 'ipv4-l3-protocol.h', 'ipv6-l3-protocol.h', 'ipv6-extension-header.h', + 'ipv6-option-header.h', 'arp-l3-protocol.h', 'udp-l4-protocol.h', 'tcp-l4-protocol.h',