From 217b28e6aacdc02d035e8f0aa4aec43c42fe3541 Mon Sep 17 00:00:00 2001 From: Tommaso Pecorella Date: Sun, 17 Jan 2021 16:54:52 +0000 Subject: [PATCH] sixlowpan: add context-based (stateful) IPHC compression --- RELEASE_NOTES | 1 + src/sixlowpan/doc/sixlowpan.rst | 19 +- src/sixlowpan/helper/sixlowpan-helper.cc | 60 ++ src/sixlowpan/helper/sixlowpan-helper.h | 68 +- src/sixlowpan/model/sixlowpan-header.cc | 265 +++--- src/sixlowpan/model/sixlowpan-header.h | 79 +- src/sixlowpan/model/sixlowpan-net-device.cc | 783 +++++++++++++++--- src/sixlowpan/model/sixlowpan-net-device.h | 112 ++- src/sixlowpan/test/mock-net-device.cc | 348 ++++++++ src/sixlowpan/test/mock-net-device.h | 132 +++ .../test/sixlowpan-iphc-stateful-test.cc | 313 +++++++ src/sixlowpan/wscript | 2 + 12 files changed, 1836 insertions(+), 346 deletions(-) create mode 100644 src/sixlowpan/test/mock-net-device.cc create mode 100644 src/sixlowpan/test/mock-net-device.h create mode 100644 src/sixlowpan/test/sixlowpan-iphc-stateful-test.cc diff --git a/RELEASE_NOTES b/RELEASE_NOTES index d5e00c508..d3e4a5d16 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -26,6 +26,7 @@ requirements (Note: not all ns-3 features are available on all systems): New user-visible features ------------------------- +- (sixlowpan) Added support for stateful (i.e., context-based) RFC6282 compression. Bugs fixed ---------- diff --git a/src/sixlowpan/doc/sixlowpan.rst b/src/sixlowpan/doc/sixlowpan.rst index 814270387..cadf4b164 100644 --- a/src/sixlowpan/doc/sixlowpan.rst +++ b/src/sixlowpan/doc/sixlowpan.rst @@ -23,17 +23,15 @@ standpoint, as it does extend it beyond the original scope by supporting also other kinds of networks. Other than that, the module strictly follows :rfc:`4944` and :rfc:`6282`, with the -following exceptions: - -* HC2 encoding is not supported -* IPHC's SAC and DAC are not supported - -The HC2 encoding is not supported, as it has been superseded by IPHC and NHC +exception that HC2 encoding is not supported, as it has been superseded by IPHC and NHC compression type (\ :rfc:`6282`). -IPHC SAC and DAC are not yet supported, as they do require :rfc:`6775` +IPHC sateful (context-based) compression is supported but, since :rfc:`6775` ("Neighbor Discovery Optimization for IPv6 over Low-Power Wireless Personal Area Networks (6LoWPANs)") -for full compliance. It is planned to support them in the future. +is not yet implemented, it is necessary to add the context to the nodes manually. + +This is possible though the ``SixLowPanHelper::AddContext`` function. +Mind that installing different contexts in different nodes will lead to decompression failures. NetDevice ######### @@ -117,8 +115,9 @@ Scope and Limitations Contex-based compression ######################## -The present implementation does not support context-based (stateful) compression. -This limitation will be removed in the future. +IPHC sateful (context-based) compression is supported but, since :rfc:`6775` +("Neighbor Discovery Optimization for IPv6 over Low-Power Wireless Personal Area Networks (6LoWPANs)") +is not yet implemented, it is necessary to add the context to the nodes manually. 6LoWPAM-ND ########## diff --git a/src/sixlowpan/helper/sixlowpan-helper.cc b/src/sixlowpan/helper/sixlowpan-helper.cc index bafa66b3c..f4f62dc64 100644 --- a/src/sixlowpan/helper/sixlowpan-helper.cc +++ b/src/sixlowpan/helper/sixlowpan-helper.cc @@ -66,6 +66,66 @@ NetDeviceContainer SixLowPanHelper::Install (const NetDeviceContainer c) return devs; } +void SixLowPanHelper::AddContext (NetDeviceContainer c, uint8_t contextId, Ipv6Prefix context, Time validity) +{ + NS_LOG_FUNCTION (this << +contextId << context << validity); + + for (uint32_t i = 0; i < c.GetN (); ++i) + { + Ptr device = c.Get (i); + Ptr sixDevice = DynamicCast (device); + if (sixDevice) + { + sixDevice->AddContext (contextId, context, true, validity); + } + } +} + +void SixLowPanHelper::RenewContext (NetDeviceContainer c, uint8_t contextId, Time validity) +{ + NS_LOG_FUNCTION (this << +contextId << validity); + + for (uint32_t i = 0; i < c.GetN (); ++i) + { + Ptr device = c.Get (i); + Ptr sixDevice = DynamicCast (device); + if (sixDevice) + { + sixDevice->RenewContext (contextId, validity); + } + } +} + +void SixLowPanHelper::InvalidateContext (NetDeviceContainer c, uint8_t contextId) +{ + NS_LOG_FUNCTION (this << +contextId); + + for (uint32_t i = 0; i < c.GetN (); ++i) + { + Ptr device = c.Get (i); + Ptr sixDevice = DynamicCast (device); + if (sixDevice) + { + sixDevice->InvalidateContext (contextId); + } + } +} + +void SixLowPanHelper::RemoveContext (NetDeviceContainer c, uint8_t contextId) +{ + NS_LOG_FUNCTION (this << +contextId); + + for (uint32_t i = 0; i < c.GetN (); ++i) + { + Ptr device = c.Get (i); + Ptr sixDevice = DynamicCast (device); + if (sixDevice) + { + sixDevice->RemoveContext (contextId); + } + } +} + int64_t SixLowPanHelper::AssignStreams (NetDeviceContainer c, int64_t stream) { int64_t currentStream = stream; diff --git a/src/sixlowpan/helper/sixlowpan-helper.h b/src/sixlowpan/helper/sixlowpan-helper.h index 3b45ce083..5785821fa 100644 --- a/src/sixlowpan/helper/sixlowpan-helper.h +++ b/src/sixlowpan/helper/sixlowpan-helper.h @@ -80,16 +80,64 @@ public: NetDeviceContainer Install (NetDeviceContainer c); /** - * Assign a fixed random variable stream number to the random variables - * used by this model. Return the number of streams (possibly zero) that - * have been assigned. The Install() method should have previously been - * called by the user. - * - * \param [in] c NetDeviceContainer of the set of net devices for which the - * SixLowPanNetDevice should be modified to use a fixed stream. - * \param [in] stream First stream index to use. - * \return The number of stream indices assigned by this helper. - */ + * \brief Adds a compression Context to a set of NetDevices. + * + * This function installs one Compression Context on a set of NetDevices. + * The context is used only in IPHC compression and decompression. + * + * \param [in] c The NetDevice container. + * \param [in] id The context id (must be less than 16). + * \param [in] prefix The context prefix. + * \param [in] validity the context validity time (relative to the actual time). + */ + void AddContext (NetDeviceContainer c, uint8_t contextId, Ipv6Prefix context, Time validity); + + /** + * \brief Renew a compression Context in a set of NetDevices. + * + * The context will have its lifetime extended and its validity for compression re-enabled. + * + * \param [in] c The NetDevice container. + * \param [in] id The context id (must be less than 16). + * \param [in] validity the context validity time (relative to the actual time). + */ + void RenewContext (NetDeviceContainer c, uint8_t contextId, Time validity); + + /** + * \brief Invalidates a compression Context in a set of NetDevices. + * + * An invalid context is used only in IPHC decompression and not + * in IPHC compression. + * + * This is necessary to avoid that a context reaching its validity lifetime + * can not be used for decompression whie packets are traveling the network. + * + * \param [in] c The NetDevice container. + * \param [in] id The context id (must be less than 16). + */ + void InvalidateContext (NetDeviceContainer c, uint8_t contextId); + + /** + * \brief Remove a compression Context in a set of NetDevices. + * + * The context is removed immediately from the contexts in the devices. + * + * \param [in] c The NetDevice container. + * \param [in] id The context id (must be less than 16). + */ + void RemoveContext (NetDeviceContainer c, uint8_t contextId); + + /** + * Assign a fixed random variable stream number to the random variables + * used by this model. Return the number of streams (possibly zero) that + * have been assigned. The Install() method should have previously been + * called by the user. + * + * \param [in] c NetDeviceContainer of the set of net devices for which the + * SixLowPanNetDevice should be modified to use a fixed stream. + * \param [in] stream First stream index to use. + * \return The number of stream indices assigned by this helper. + */ int64_t AssignStreams (NetDeviceContainer c, int64_t stream); private: diff --git a/src/sixlowpan/model/sixlowpan-header.cc b/src/sixlowpan/model/sixlowpan-header.cc index e62c993bb..d584f7e97 100644 --- a/src/sixlowpan/model/sixlowpan-header.cc +++ b/src/sixlowpan/model/sixlowpan-header.cc @@ -793,6 +793,7 @@ SixLowPanIphc::SixLowPanIphc () { // 011x xxxx xxxx xxxx m_baseFormat = 0x6000; + m_srcdstContextId = 0; } SixLowPanIphc::SixLowPanIphc (uint8_t dispatch) @@ -800,6 +801,7 @@ SixLowPanIphc::SixLowPanIphc (uint8_t dispatch) // 011x xxxx xxxx xxxx m_baseFormat = dispatch; m_baseFormat <<= 8; + m_srcdstContextId = 0; } TypeId SixLowPanIphc::GetTypeId (void) @@ -818,7 +820,48 @@ TypeId SixLowPanIphc::GetInstanceTypeId (void) const void SixLowPanIphc::Print (std::ostream & os) const { - os << "Compression kind: " << +m_baseFormat; + switch ( GetTf () ) + { + case TF_FULL: + os << "TF_FULL(" << +m_ecn << ", " << +m_dscp << ", " << m_flowLabel << ")"; + break; + case TF_DSCP_ELIDED: + os << "TF_DSCP_ELIDED(" << +m_ecn << ", " << m_flowLabel << ")"; + break; + case TF_FL_ELIDED: + os << "TF_FL_ELIDED(" << +m_ecn << ", " << +m_dscp << ")"; + break; + default: + os << "TF_ELIDED"; + break; + } + + GetNh () ? os << " NH(1)" : os << " NH(0)"; + + switch ( GetHlim () ) + { + case HLIM_INLINE: + os << " HLIM_INLINE(" << +m_hopLimit << ")"; + break; + case HLIM_COMPR_1: + os << " HLIM_COMPR_1(1)"; + break; + case HLIM_COMPR_64: + os << " HLIM_COMPR_64(64)"; + break; + default: + os << " HLIM_COMPR_255(255)"; + break; + } + + GetCid () ? os << " CID(" << +m_srcdstContextId << ")" : os << " CID(0)"; + + GetSac () ? os << " SAC(1)" : os << " SAC(0)"; + os << " SAM (" << GetSam () << ")"; + + GetM () ? os << " M(1)" : os << " M(0)"; + GetDac () ? os << " DAC(1)" : os << " DAC(0)"; + os << " DAM (" << GetDam () << ")"; } uint32_t SixLowPanIphc::GetSerializedSize () const @@ -983,22 +1026,17 @@ void SixLowPanIphc::Serialize (Buffer::Iterator start) const // Source Address switch (GetSam () ) { - uint8_t temp[16]; case HC_INLINE: if ( GetSac () == false ) { - uint8_t temp[16]; - m_srcAddress.Serialize (temp); - i.Write (temp, 16); + i.Write (m_srcInlinePart, 16); } break; case HC_COMPR_64: - m_srcAddress.Serialize (temp); - i.Write (temp + 8, 8); + i.Write (m_srcInlinePart, 8); break; case HC_COMPR_16: - m_srcAddress.Serialize (temp); - i.Write (temp + 14, 2); + i.Write (m_srcInlinePart, 2); break; case HC_COMPR_0: default: @@ -1007,23 +1045,17 @@ void SixLowPanIphc::Serialize (Buffer::Iterator start) const // Destination Address if ( GetM () == false) { - uint8_t temp[16]; + // unicast switch (GetDam () ) { case HC_INLINE: - if ( GetDac () == false ) - { - m_dstAddress.Serialize (temp); - i.Write (temp, 16); - } + i.Write (m_dstInlinePart, 16); break; case HC_COMPR_64: - m_dstAddress.Serialize (temp); - i.Write (temp + 8, 8); + i.Write (m_dstInlinePart, 8); break; case HC_COMPR_16: - m_dstAddress.Serialize (temp); - i.Write (temp + 14, 2); + i.Write (m_dstInlinePart, 2); break; case HC_COMPR_0: default: @@ -1032,45 +1064,22 @@ void SixLowPanIphc::Serialize (Buffer::Iterator start) const } else { + // multicast switch (GetDam () ) { - uint8_t temp[16]; case HC_INLINE: - if ( GetDac () == false ) - { - m_dstAddress.Serialize (temp); - i.Write (temp, 16); - } - else - { - m_dstAddress.Serialize (temp); - i.Write (temp + 1, 2); - i.Write (temp + 12, 4); - } + i.Write (m_dstInlinePart, 16); break; case HC_COMPR_64: - if ( GetDac () == false ) - { - m_dstAddress.Serialize (temp); - i.Write (temp + 1, 1); - i.Write (temp + 11, 5); - } + i.Write (m_dstInlinePart, 6); break; case HC_COMPR_16: - if ( GetDac () == false ) - { - m_dstAddress.Serialize (temp); - i.Write (temp + 1, 1); - i.Write (temp + 13, 3); - } + i.Write (m_dstInlinePart, 4); break; case HC_COMPR_0: - default: - if ( GetDac () == false ) - { - m_dstAddress.Serialize (temp); - i.WriteU8 (temp[15]); - } + i.Write (m_dstInlinePart, 1); + break; + default: break; } } @@ -1086,6 +1095,10 @@ uint32_t SixLowPanIphc::Deserialize (Buffer::Iterator start) { m_srcdstContextId = i.ReadU8 (); } + else + { + m_srcdstContextId = 0; + } // Traffic Class and Flow Label switch ( GetTf () ) { @@ -1142,68 +1155,41 @@ uint32_t SixLowPanIphc::Deserialize (Buffer::Iterator start) } // Source Address + memset (m_srcInlinePart, 0x00, sizeof (m_srcInlinePart)); switch (GetSam () ) { - uint8_t temp[16]; case HC_INLINE: if ( GetSac () == false ) { - i.Read (temp, 16); - m_srcAddress = Ipv6Address::Deserialize (temp); + i.Read (m_srcInlinePart, 16); } break; case HC_COMPR_64: - memset (temp, 0x00, sizeof (temp)); - i.Read (temp + 8, 8); - temp[0] = 0xfe; - temp[1] = 0x80; - m_srcAddress = Ipv6Address::Deserialize (temp); + i.Read (m_srcInlinePart, 8); break; case HC_COMPR_16: - memset (temp, 0x00, sizeof (temp)); - i.Read (temp + 14, 2); - temp[0] = 0xfe; - temp[1] = 0x80; - temp[11] = 0xff; - temp[12] = 0xfe; - m_srcAddress = Ipv6Address::Deserialize (temp); + i.Read (m_srcInlinePart, 2); break; case HC_COMPR_0: default: break; } - if ( GetSac () == true ) - { - PostProcessSac (); - } + // Destination Address + memset (m_dstInlinePart, 0x00, sizeof (m_dstInlinePart)); if ( GetM () == false) { - uint8_t temp[16]; + // unicast switch (GetDam () ) { case HC_INLINE: - if ( GetDac () == false ) - { - i.Read (temp, 16); - m_dstAddress = Ipv6Address::Deserialize (temp); - } + i.Read (m_dstInlinePart, 16); break; case HC_COMPR_64: - memset (temp, 0x00, sizeof (temp)); - i.Read (temp + 8, 8); - temp[0] = 0xfe; - temp[1] = 0x80; - m_dstAddress = Ipv6Address::Deserialize (temp); + i.Read (m_dstInlinePart, 8); break; case HC_COMPR_16: - memset (temp, 0x00, sizeof (temp)); - i.Read (temp + 14, 2); - temp[0] = 0xfe; - temp[1] = 0x80; - temp[11] = 0xff; - temp[12] = 0xfe; - m_dstAddress = Ipv6Address::Deserialize (temp); + i.Read (m_dstInlinePart, 2); break; case HC_COMPR_0: default: @@ -1212,61 +1198,26 @@ uint32_t SixLowPanIphc::Deserialize (Buffer::Iterator start) } else { + // multicast switch (GetDam () ) { - uint8_t temp[16]; case HC_INLINE: - if ( GetDac () == false ) - { - i.Read (temp, 16); - m_dstAddress = Ipv6Address::Deserialize (temp); - } - else - { - memset (temp, 0x00, sizeof (temp)); - i.Read (temp + 1, 2); - i.Read (temp + 12, 4); - temp[0] = 0xff; - m_dstAddress = Ipv6Address::Deserialize (temp); - } + i.Read (m_dstInlinePart, 16); break; case HC_COMPR_64: - if ( GetDac () == false ) - { - memset (temp, 0x00, sizeof (temp)); - i.Read (temp + 1, 1); - i.Read (temp + 11, 5); - temp[0] = 0xff; - m_dstAddress = Ipv6Address::Deserialize (temp); - } + i.Read (m_dstInlinePart, 6); break; case HC_COMPR_16: - if ( GetDac () == false ) - { - memset (temp, 0x00, sizeof (temp)); - i.Read (temp + 1, 1); - i.Read (temp + 13, 3); - temp[0] = 0xff; - m_dstAddress = Ipv6Address::Deserialize (temp); - } + i.Read (m_dstInlinePart, 4); break; case HC_COMPR_0: + i.Read (m_dstInlinePart, 1); + break; default: - if ( GetDac () == false ) - { - memset (temp, 0x00, sizeof (temp)); - temp[15] = i.ReadU8 (); - temp[0] = 0xff; - temp[1] = 0x02; - m_dstAddress = Ipv6Address::Deserialize (temp); - } break; } } - if ( GetDac () == true ) - { - PostProcessDac (); - } + return GetSerializedSize (); } @@ -1336,6 +1287,19 @@ SixLowPanIphc::HeaderCompression_e SixLowPanIphc::GetSam (void) const return HeaderCompression_e ((m_baseFormat >> 4) & 0x3); } +const uint8_t* SixLowPanIphc::GetSrcInlinePart (void) const +{ + return m_srcInlinePart; +} + +void SixLowPanIphc::SetSrcInlinePart (uint8_t srcInlinePart[16], uint8_t size) +{ + NS_ASSERT_MSG (size <= 16, "Src inline part too large"); + + memcpy (m_srcInlinePart, srcInlinePart, size); + return; +} + void SixLowPanIphc::SetM (bool mField) { uint16_t field = mField; @@ -1369,6 +1333,19 @@ SixLowPanIphc::HeaderCompression_e SixLowPanIphc::GetDam (void) const return HeaderCompression_e (m_baseFormat & 0x3); } +const uint8_t* SixLowPanIphc::GetDstInlinePart (void) const +{ + return m_dstInlinePart; +} + +void SixLowPanIphc::SetDstInlinePart (uint8_t dstInlinePart[16], uint8_t size) +{ + NS_ASSERT_MSG (size <= 16, "Dst inline part too large"); + + memcpy (m_dstInlinePart, dstInlinePart, size); + return; +} + void SixLowPanIphc::SetSrcContextId (uint8_t srcContextId) { NS_ASSERT_MSG (srcContextId < 16, "Src Context ID too large"); @@ -1444,38 +1421,6 @@ uint8_t SixLowPanIphc::GetHopLimit (void) const return m_hopLimit; } -void SixLowPanIphc::SetSrcAddress (Ipv6Address srcAddress) -{ - m_srcAddress = srcAddress; -} - -Ipv6Address SixLowPanIphc::GetSrcAddress () const -{ - return m_srcAddress; -} - -void SixLowPanIphc::SetDstAddress (Ipv6Address dstAddress) -{ - m_dstAddress = dstAddress; -} - -Ipv6Address SixLowPanIphc::GetDstAddress () const -{ - return m_dstAddress; -} - -void SixLowPanIphc::PostProcessSac () -{ - NS_ABORT_MSG ("Unsupported; Context destination is not implemented"); - return; -} - -void SixLowPanIphc::PostProcessDac () -{ - NS_ABORT_MSG ("Unsupported; Context destination is not implemented"); - return; -} - std::ostream & operator << (std::ostream & os, const SixLowPanIphc & h) { h.Print (os); diff --git a/src/sixlowpan/model/sixlowpan-header.h b/src/sixlowpan/model/sixlowpan-header.h index 78f4023f0..76d0270cb 100644 --- a/src/sixlowpan/model/sixlowpan-header.h +++ b/src/sixlowpan/model/sixlowpan-header.h @@ -768,6 +768,19 @@ public: */ HeaderCompression_e GetSam (void) const; + /** + * brief Set the source address inline part + * \param srcInlinePart The inline portion of the compressed source address (16 bytes) + * \param size The number of inline bytes + */ + void SetSrcInlinePart (uint8_t srcInlinePart[16], uint8_t size); + + /** + * brief Get the source address inline part + * \return The inline portion of the compressed source address (16 bytes) + */ + const uint8_t* GetSrcInlinePart (void) const; + /** * \brief Set the M (Multicast) compression. * \param [in] mField True if destination is multicast. @@ -805,6 +818,19 @@ public: HeaderCompression_e GetDam (void) const; /** + * brief Set the destination address inline part + * \param dstInlinePart The inline portion of the compressed destination address (16 bytes) + * \param size The number of inline bytes + */ + void SetDstInlinePart (uint8_t dstInlinePart[16], uint8_t size); + + /** + * brief Get the destination address inline part + * \return The inline portion of the compressed destination address (16 bytes) + */ + const uint8_t* GetDstInlinePart (void) const; + + /** * \brief Set the SrcContextId. * \param [in] srcContextId Valid values are [0:15]. */ @@ -888,51 +914,16 @@ public: */ uint8_t GetHopLimit (void) const; - /** - * \brief Set the Source Address. - * \param [in] srcAddress The Source Address. - */ - void SetSrcAddress (Ipv6Address srcAddress); - - /** - * \brief Get the Source Address. - * \return The Source Address. - */ - Ipv6Address GetSrcAddress () const; - - /** - * \brief Set the Destination Address. - * \param [in] dstAddress The Destination Address. - */ - void SetDstAddress (Ipv6Address dstAddress); - - /** - * \brief Get the Destination Address. - * \return The Destination Address. - */ - Ipv6Address GetDstAddress () const; - private: - uint16_t m_baseFormat; //!< Dispatch + encoding fields. - uint8_t m_srcdstContextId; //!< Src and Dst Context ID. - uint8_t m_ecn : 2; //!< ECN bits. - uint8_t m_dscp : 6; //!< DSCP bits. - uint32_t m_flowLabel : 20; //!< Flow Label bits. - uint8_t m_nextHeader; //!< Next header. - uint8_t m_hopLimit; //!< Hop Limit. - Ipv6Address m_srcAddress; //!< Src address. - Ipv6Address m_dstAddress; //!< Dst address. - - /** - * \brief Post-process the Source address stateful compression - * \note Currently unsupported. - */ - void PostProcessSac (); - /** - * \brief Post-process the Destination address stateful compression. - * \note Currently unsupported. - */ - void PostProcessDac (); + uint16_t m_baseFormat; //!< Dispatch + encoding fields. + uint8_t m_srcdstContextId; //!< Src and Dst Context ID. + uint8_t m_ecn : 2; //!< ECN bits. + uint8_t m_dscp : 6; //!< DSCP bits. + uint32_t m_flowLabel : 20; //!< Flow Label bits. + uint8_t m_nextHeader; //!< Next header. + uint8_t m_hopLimit; //!< Hop Limit. + uint8_t m_srcInlinePart[16]; //!< source address inline part. + uint8_t m_dstInlinePart[16]; //!< destination address inline part. }; diff --git a/src/sixlowpan/model/sixlowpan-net-device.cc b/src/sixlowpan/model/sixlowpan-net-device.cc index 4aa8d41a6..22727c14a 100644 --- a/src/sixlowpan/model/sixlowpan-net-device.cc +++ b/src/sixlowpan/model/sixlowpan-net-device.cc @@ -196,7 +196,6 @@ void SixLowPanNetDevice::ReceiveFromDevice (Ptr incomingPort, PacketType packetType) { NS_LOG_FUNCTION (this << incomingPort << packet << protocol << src << dst); - NS_LOG_DEBUG ("UID is " << packet->GetUid ()); uint8_t dispatchRawVal = 0; SixLowPanDispatch::Dispatch_e dispatchVal; @@ -349,8 +348,14 @@ void SixLowPanNetDevice::ReceiveFromDevice (Ptr incomingPort, m_dropTrace (DROP_DISALLOWED_COMPRESSION, copyPkt, m_node->GetObject (), GetIfIndex ()); return; } - DecompressLowPanIphc (copyPkt, realSrc, realDst); - isPktDecompressed = true; + if (DecompressLowPanIphc (copyPkt, realSrc, realDst)) + { + m_dropTrace (DROP_SATETFUL_DECOMPRESSION_PROBLEM, copyPkt, m_node->GetObject (), GetIfIndex ()); + } + else + { + isPktDecompressed = true; + } break; default: NS_LOG_DEBUG ("Unsupported 6LoWPAN encoding: dropping."); @@ -711,6 +716,8 @@ SixLowPanNetDevice::CompressLowPanHc1 (Ptr packet, Address const &src, A SixLowPanHc1 hc1Header; uint32_t size = 0; + NS_LOG_DEBUG ( "Original packet: " << *packet << " Size " << packet->GetSize () ); + if ( packet->PeekHeader (ipHeader) != 0 ) { packet->RemoveHeader (ipHeader); @@ -922,7 +929,7 @@ SixLowPanNetDevice::DecompressLowPanHc1 (Ptr packet, Address const &src, packet->AddHeader (ipHeader); - NS_LOG_DEBUG ( "Rebuilt packet: " << *packet << " Size " << packet->GetSize () ); + NS_LOG_DEBUG ( "Rebuilt packet: " << *packet << " Size " << packet->GetSize () ); } uint32_t @@ -934,6 +941,7 @@ SixLowPanNetDevice::CompressLowPanIphc (Ptr packet, Address const &src, SixLowPanIphc iphcHeader; uint32_t size = 0; + NS_LOG_DEBUG ( "Original packet: " << *packet << " Size " << packet->GetSize () << " src: " << src << " dst: " << dst); if ( packet->PeekHeader (ipHeader) != 0 ) { @@ -1002,7 +1010,6 @@ SixLowPanNetDevice::CompressLowPanIphc (Ptr packet, Address const &src, iphcHeader.SetNextHeader (nextHeader); } - // Set the HLIM field if (ipHeader.GetHopLimit () == 1) { @@ -1023,44 +1030,101 @@ SixLowPanNetDevice::CompressLowPanIphc (Ptr packet, Address const &src, iphcHeader.SetHopLimit (ipHeader.GetHopLimit ()); } - // \todo Add the check of CID if there is context-based compression - // Set the CID field + // Set the CID + SAC + DAC fields to their default value iphcHeader.SetCid (false); - - // \todo Add the check of SAC if there is context-based compression - // Set the SAC field iphcHeader.SetSac (false); + iphcHeader.SetDac (false); - uint8_t addressBuf[16]; - uint8_t unicastAddrCheckerBuf[16]; - Ipv6Address srcAddr = ipHeader.GetSourceAddress (); - srcAddr.GetBytes (addressBuf); Ipv6Address checker = Ipv6Address ("fe80:0000:0000:0000:0000:00ff:fe00:1"); + uint8_t unicastAddrCheckerBuf[16]; checker.GetBytes (unicastAddrCheckerBuf); + uint8_t addressBuf[16]; - // \todo Add the check of SAC if there is context-based compression - // Set the Source Address - iphcHeader.SetSrcAddress (srcAddr); + // This is just to limit the scope of some variables. + if (true) + { + Ipv6Address srcAddr = ipHeader.GetSourceAddress (); + uint8_t srcContextId; - Ipv6Address mySrcAddr = Ipv6Address::MakeAutoconfiguredLinkLocalAddress (src); - NS_LOG_LOGIC ("Checking source compression: " << mySrcAddr << " - " << srcAddr ); + // The "::" address is compressed as a fake stateful compression. + if (srcAddr == Ipv6Address::GetAny ()) + { + // No context information is needed. + iphcHeader.SetSam (SixLowPanIphc::HC_INLINE); + iphcHeader.SetSac (true); + } + // Check if the address can be compressed with stateful compression + else if ( FindUnicastCompressionContext (srcAddr, srcContextId) ) + { + // We can do stateful compression. + NS_LOG_LOGIC ("Checking stateful source compression: " << srcAddr ); - if ( mySrcAddr == srcAddr ) - { - iphcHeader.SetSam (SixLowPanIphc::HC_COMPR_0); - } - else if (memcmp (addressBuf, unicastAddrCheckerBuf, 14) == 0) - { - iphcHeader.SetSam (SixLowPanIphc::HC_COMPR_16); - } - else if ( srcAddr.IsLinkLocal () ) - { - iphcHeader.SetSam (SixLowPanIphc::HC_COMPR_64); - } - else - { - iphcHeader.SetSam (SixLowPanIphc::HC_INLINE); + iphcHeader.SetSac (true); + if (srcContextId != 0) + { + // the default context is zero, no need to explicit it if it's zero + iphcHeader.SetSrcContextId (srcContextId); + iphcHeader.SetCid (true); + } + + // Note that a context might include parts of the EUI-64 (i.e., be as long as 128 bits). + + if (Ipv6Address::MakeAutoconfiguredAddress (src, m_contextTable[srcContextId].contextPrefix) == srcAddr) + { + iphcHeader.SetSam (SixLowPanIphc::HC_COMPR_0); + } + else + { + Ipv6Address cleanedAddr = CleanPrefix (srcAddr, m_contextTable[srcContextId].contextPrefix); + uint8_t serializedCleanedAddress[16]; + cleanedAddr.Serialize (serializedCleanedAddress); + + if ( serializedCleanedAddress[8] == 0x00 && serializedCleanedAddress[9] == 0x00 && + serializedCleanedAddress[10] == 0x00 && serializedCleanedAddress[11] == 0xff && + serializedCleanedAddress[12] == 0xfe && serializedCleanedAddress[13] == 0x00 ) + { + iphcHeader.SetSam (SixLowPanIphc::HC_COMPR_16); + iphcHeader.SetSrcInlinePart (serializedCleanedAddress+14, 2); + } + else + { + iphcHeader.SetSam (SixLowPanIphc::HC_COMPR_64); + iphcHeader.SetSrcInlinePart (serializedCleanedAddress+8, 8); + + } + } + } + else + { + // We must do stateless compression. + NS_LOG_LOGIC ("Checking stateless source compression: " << srcAddr ); + + srcAddr.GetBytes (addressBuf); + + uint8_t serializedSrcAddress[16]; + srcAddr.Serialize (serializedSrcAddress); + + if ( srcAddr == Ipv6Address::MakeAutoconfiguredLinkLocalAddress (src) ) + { + iphcHeader.SetSam (SixLowPanIphc::HC_COMPR_0); + } + else if (memcmp (addressBuf, unicastAddrCheckerBuf, 14) == 0) + { + iphcHeader.SetSrcInlinePart (serializedSrcAddress+14, 2); + iphcHeader.SetSam (SixLowPanIphc::HC_COMPR_16); + } + else if ( srcAddr.IsLinkLocal () ) + { + iphcHeader.SetSrcInlinePart (serializedSrcAddress+8, 8); + iphcHeader.SetSam (SixLowPanIphc::HC_COMPR_64); + } + else + { + iphcHeader.SetSrcInlinePart (serializedSrcAddress, 16); + iphcHeader.SetSam (SixLowPanIphc::HC_INLINE); + } + } } // Set the M field @@ -1073,69 +1137,156 @@ SixLowPanNetDevice::CompressLowPanIphc (Ptr packet, Address const &src, iphcHeader.SetM (false); } - // \todo Add the check of DAC if there is context-based compression - // Set the DAC field - iphcHeader.SetDac (false); - - Ipv6Address dstAddr = ipHeader.GetDestinationAddress (); - dstAddr.GetBytes (addressBuf); - - // \todo Add the check of DAC if there is context-based compression - // Set the Destination Address - iphcHeader.SetDstAddress (dstAddr); - - Ipv6Address myDstAddr = Ipv6Address::MakeAutoconfiguredLinkLocalAddress (dst); - NS_LOG_LOGIC ("Checking destination compression: " << myDstAddr << " - " << dstAddr ); - - if ( !iphcHeader.GetM () ) - // Unicast address + // This is just to limit the scope of some variables. + if (true) { - if ( myDstAddr == dstAddr ) + Ipv6Address dstAddr = ipHeader.GetDestinationAddress (); + dstAddr.GetBytes (addressBuf); + + NS_LOG_LOGIC ("Checking destination compression: " << dstAddr ); + + uint8_t serializedDstAddress[16]; + dstAddr.Serialize (serializedDstAddress); + + if ( !iphcHeader.GetM () ) { - iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_0); - } - else if (memcmp (addressBuf, unicastAddrCheckerBuf, 14) == 0) - { - iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_16); - } - else if ( dstAddr.IsLinkLocal () ) - { - iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_64); + // Unicast address + + uint8_t dstContextId; + if ( FindUnicastCompressionContext (dstAddr, dstContextId) ) + { + // We can do stateful compression. + NS_LOG_LOGIC ("Checking stateful destination compression: " << dstAddr ); + + iphcHeader.SetDac (true); + if (dstContextId != 0) + { + // the default context is zero, no need to explicit it if it's zero + iphcHeader.SetDstContextId (dstContextId); + iphcHeader.SetCid (true); + } + + // Note that a context might include parts of the EUI-64 (i.e., be as long as 128 bits). + if (Ipv6Address::MakeAutoconfiguredAddress (dst, m_contextTable[dstContextId].contextPrefix) == dstAddr) + { + iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_0); + } + else + { + Ipv6Address cleanedAddr = CleanPrefix (dstAddr, m_contextTable[dstContextId].contextPrefix); + + uint8_t serializedCleanedAddress[16]; + cleanedAddr.Serialize (serializedCleanedAddress); + + if ( serializedCleanedAddress[8] == 0x00 && serializedCleanedAddress[9] == 0x00 && + serializedCleanedAddress[10] == 0x00 && serializedCleanedAddress[11] == 0xff && + serializedCleanedAddress[12] == 0xfe && serializedCleanedAddress[13] == 0x00 ) + { + iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_16); + iphcHeader.SetDstInlinePart (serializedCleanedAddress+14, 2); + } + else + { + iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_64); + iphcHeader.SetDstInlinePart (serializedCleanedAddress+8, 8); + } + } + } + else + { + NS_LOG_LOGIC ("Checking stateless destination compression: " << dstAddr ); + + if ( dstAddr == Ipv6Address::MakeAutoconfiguredLinkLocalAddress (dst) ) + { + iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_0); + } + else if (memcmp (addressBuf, unicastAddrCheckerBuf, 14) == 0) + { + iphcHeader.SetDstInlinePart (serializedDstAddress+14, 2); + iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_16); + } + else if ( dstAddr.IsLinkLocal () ) + { + iphcHeader.SetDstInlinePart (serializedDstAddress+8, 8); + iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_64); + } + else + { + iphcHeader.SetDstInlinePart (serializedDstAddress, 16); + iphcHeader.SetDam (SixLowPanIphc::HC_INLINE); + } + } } else { - iphcHeader.SetDam (SixLowPanIphc::HC_INLINE); - } - } - else - { - // Multicast address - uint8_t multicastAddrCheckerBuf[16]; - Ipv6Address multicastCheckAddress = Ipv6Address ("ff02::1"); - multicastCheckAddress.GetBytes (multicastAddrCheckerBuf); + // Multicast address - // The address takes the form ff02::00XX. - if ( memcmp (addressBuf, multicastAddrCheckerBuf, 15) == 0 ) - { - iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_0); - } - // The address takes the form ffXX::00XX:XXXX. - // ffXX:0000:0000:0000:0000:0000:00XX:XXXX. - else if ( (addressBuf[0] == multicastAddrCheckerBuf[0]) - && (memcmp (addressBuf + 2, multicastAddrCheckerBuf + 2, 11) == 0) ) - { - iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_16); - } - // The address takes the form ffXX::00XX:XXXX:XXXX. - // ffXX:0000:0000:0000:0000:00XX:XXXX:XXXX. - else if ( (addressBuf[0] == multicastAddrCheckerBuf[0]) - && (memcmp (addressBuf + 2, multicastAddrCheckerBuf + 2, 9) == 0) ) - { - iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_64); - } - else - { - iphcHeader.SetDam (SixLowPanIphc::HC_INLINE); + uint8_t dstContextId; + if ( FindMulticastCompressionContext (dstAddr, dstContextId) ) + { + // Stateful compression (only one possible case) + + // ffXX:XXLL:PPPP:PPPP:PPPP:PPPP:XXXX:XXXX + uint8_t dstInlinePart[6] = {}; + dstInlinePart[0] = serializedDstAddress[1]; + dstInlinePart[1] = serializedDstAddress[2]; + dstInlinePart[2] = serializedDstAddress[12]; + dstInlinePart[3] = serializedDstAddress[13]; + dstInlinePart[4] = serializedDstAddress[14]; + dstInlinePart[5] = serializedDstAddress[15]; + + iphcHeader.SetDac (true); + if (dstContextId != 0) + { + // the default context is zero, no need to explicit it if it's zero + iphcHeader.SetDstContextId (dstContextId); + iphcHeader.SetCid (true); + } + iphcHeader.SetDstInlinePart (dstInlinePart, 6); + iphcHeader.SetDam (SixLowPanIphc::HC_INLINE); + } + else + { + // Stateless compression + + uint8_t multicastAddrCheckerBuf[16]; + Ipv6Address multicastCheckAddress = Ipv6Address ("ff02::1"); + multicastCheckAddress.GetBytes (multicastAddrCheckerBuf); + + // The address takes the form ff02::00XX. + if ( memcmp (addressBuf, multicastAddrCheckerBuf, 15) == 0 ) + { + iphcHeader.SetDstInlinePart (serializedDstAddress+15, 1); + iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_0); + } + // The address takes the form ffXX::00XX:XXXX. + // ffXX:0000:0000:0000:0000:0000:00XX:XXXX. + else if ( (addressBuf[0] == multicastAddrCheckerBuf[0]) + && (memcmp (addressBuf + 2, multicastAddrCheckerBuf + 2, 11) == 0) ) + { + uint8_t dstInlinePart[4] = {}; + memcpy (dstInlinePart, serializedDstAddress+1, 1); + memcpy (dstInlinePart+1, serializedDstAddress+13, 3); + iphcHeader.SetDstInlinePart (dstInlinePart, 4); + iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_16); + } + // The address takes the form ffXX::00XX:XXXX:XXXX. + // ffXX:0000:0000:0000:0000:00XX:XXXX:XXXX. + else if ( (addressBuf[0] == multicastAddrCheckerBuf[0]) + && (memcmp (addressBuf + 2, multicastAddrCheckerBuf + 2, 9) == 0) ) + { + uint8_t dstInlinePart[6] = {}; + memcpy (dstInlinePart, serializedDstAddress+1, 1); + memcpy (dstInlinePart+1, serializedDstAddress+11, 5); + iphcHeader.SetDstInlinePart (dstInlinePart, 6); + iphcHeader.SetDam (SixLowPanIphc::HC_COMPR_64); + } + else + { + iphcHeader.SetDstInlinePart (serializedDstAddress, 16); + iphcHeader.SetDam (SixLowPanIphc::HC_INLINE); + } + } } } @@ -1148,7 +1299,6 @@ SixLowPanNetDevice::CompressLowPanIphc (Ptr packet, Address const &src, return size; } - return 0; } @@ -1173,7 +1323,7 @@ SixLowPanNetDevice::CanCompressLowPanNhc (uint8_t nextHeader) return ret; } -void +bool SixLowPanNetDevice::DecompressLowPanIphc (Ptr packet, Address const &src, Address const &dst) { NS_LOG_FUNCTION (this << *packet << src << dst); @@ -1191,29 +1341,99 @@ SixLowPanNetDevice::DecompressLowPanIphc (Ptr packet, Address const &src // Source address if ( encoding.GetSac () ) { + // Source address compression uses stateful, context-based compression. if ( encoding.GetSam () == SixLowPanIphc::HC_INLINE ) { ipHeader.SetSourceAddress ( Ipv6Address::GetAny () ); } else { - NS_ABORT_MSG ("SAC option not yet implemented"); + uint8_t contextId = encoding.GetSrcContextId (); + if (m_contextTable.find (contextId) == m_contextTable.end ()) + { + NS_LOG_LOGIC ("Unknown Source compression context (" << +contextId << "), dropping packet"); + return true; + } + if (m_contextTable[contextId].validLifetime < Simulator::Now ()) + { + NS_LOG_LOGIC ("Expired Source compression context (" << +contextId << "), dropping packet"); + return true; + } + + uint8_t contexPrefix[16]; + m_contextTable[contextId].contextPrefix.GetBytes(contexPrefix); + uint8_t contextLength = m_contextTable[contextId].contextPrefix.GetPrefixLength (); + + uint8_t srcAddress[16] = { }; + if ( encoding.GetSam () == SixLowPanIphc::HC_COMPR_64 ) + { + memcpy (srcAddress +8, encoding.GetSrcInlinePart (), 8); + } + else if ( encoding.GetSam () == SixLowPanIphc::HC_COMPR_16 ) + { + srcAddress[11] = 0xff; + srcAddress[12] = 0xfe; + memcpy (srcAddress +14, encoding.GetSrcInlinePart (), 2); + } + else // SixLowPanIphc::HC_COMPR_0 + { + Ipv6Address::MakeAutoconfiguredLinkLocalAddress (src).GetBytes (srcAddress); + } + + uint8_t bytesToCopy = contextLength / 8; + uint8_t bitsToCopy = contextLength % 8; + + // Do not combine the prefix - we want to override the bytes. + for (uint8_t i=0; i packet, Address const &src { NS_ABORT_MSG ("Reserved code found"); } + + uint8_t contextId = encoding.GetDstContextId (); + if (m_contextTable.find (contextId) == m_contextTable.end ()) + { + NS_LOG_LOGIC ("Unknown Destination compression context (" << +contextId << "), dropping packet"); + return true; + } + if (m_contextTable[contextId].validLifetime < Simulator::Now ()) + { + NS_LOG_LOGIC ("Expired Destination compression context (" << +contextId << "), dropping packet"); + return true; + } + + uint8_t contexPrefix[16]; + m_contextTable[contextId].contextPrefix.GetBytes(contexPrefix); + uint8_t contextLength = m_contextTable[contextId].contextPrefix.GetPrefixLength (); + + if (encoding.GetM () == false) + { + // unicast + uint8_t dstAddress[16] = { }; + if ( encoding.GetDam () == SixLowPanIphc::HC_COMPR_64 ) + { + memcpy (dstAddress +8, encoding.GetDstInlinePart (), 8); + } + else if ( encoding.GetDam () == SixLowPanIphc::HC_COMPR_16 ) + { + dstAddress[11] = 0xff; + dstAddress[12] = 0xfe; + memcpy (dstAddress +14, encoding.GetDstInlinePart (), 2); + } + else // SixLowPanIphc::HC_COMPR_0 + { + Ipv6Address::MakeAutoconfiguredLinkLocalAddress (dst).GetBytes (dstAddress); + } + + uint8_t bytesToCopy = m_contextTable[contextId].contextPrefix.GetPrefixLength () / 8; + uint8_t bitsToCopy = contextLength % 8; + + // Do not combine the prefix - we want to override the bytes. + for (uint8_t i=0; i packet, Address const &src } else { - ipHeader.SetNextHeader (DecompressLowPanNhc (packet, src, dst, ipHeader.GetSourceAddress (), ipHeader.GetDestinationAddress ())); + std::pair retval = DecompressLowPanNhc (packet, src, dst, ipHeader.GetSourceAddress (), ipHeader.GetDestinationAddress ()); + if ( retval.second == true ) + { + return true; + } + else + { + ipHeader.SetNextHeader (retval.first); + } } } else @@ -1294,8 +1641,9 @@ SixLowPanNetDevice::DecompressLowPanIphc (Ptr packet, Address const &src packet->AddHeader (ipHeader); - NS_LOG_DEBUG ( "Rebuilt packet: " << *packet << " Size " << packet->GetSize () ); + NS_LOG_DEBUG ( "Rebuilt packet: " << *packet << " Size " << packet->GetSize () ); + return false; } uint32_t @@ -1557,7 +1905,7 @@ SixLowPanNetDevice::CompressLowPanNhc (Ptr packet, uint8_t headerType, A return size; } -uint8_t +std::pair SixLowPanNetDevice::DecompressLowPanNhc (Ptr packet, Address const &src, Address const &dst, Ipv6Address srcAddress, Ipv6Address dstAddress) { NS_LOG_FUNCTION (this << *packet); @@ -1602,7 +1950,7 @@ SixLowPanNetDevice::DecompressLowPanNhc (Ptr packet, Address const &src, } else { - blobData [0] = DecompressLowPanNhc (packet, src, dst, srcAddress, dstAddress); + blobData [0] = DecompressLowPanNhc (packet, src, dst, srcAddress, dstAddress).first; } } else @@ -1654,7 +2002,7 @@ SixLowPanNetDevice::DecompressLowPanNhc (Ptr packet, Address const &src, } else { - blobData [0] = DecompressLowPanNhc (packet, src, dst, srcAddress, dstAddress); + blobData [0] = DecompressLowPanNhc (packet, src, dst, srcAddress, dstAddress).first; } } else @@ -1686,7 +2034,7 @@ SixLowPanNetDevice::DecompressLowPanNhc (Ptr packet, Address const &src, } else { - blobData [0] = DecompressLowPanNhc (packet, src, dst, srcAddress, dstAddress); + blobData [0] = DecompressLowPanNhc (packet, src, dst, srcAddress, dstAddress).first; } } else @@ -1720,7 +2068,7 @@ SixLowPanNetDevice::DecompressLowPanNhc (Ptr packet, Address const &src, } else { - blobData [0] = DecompressLowPanNhc (packet, src, dst, srcAddress, dstAddress); + blobData [0] = DecompressLowPanNhc (packet, src, dst, srcAddress, dstAddress).first; } } else @@ -1759,7 +2107,11 @@ SixLowPanNetDevice::DecompressLowPanNhc (Ptr packet, Address const &src, break; case SixLowPanNhcExtension::EID_IPv6_H: actualHeaderType = Ipv6Header::IPV6_IPV6; - DecompressLowPanIphc (packet, src, dst); + if (DecompressLowPanIphc (packet, src, dst)) + { + m_dropTrace (DROP_SATETFUL_DECOMPRESSION_PROBLEM, packet, m_node->GetObject (), GetIfIndex ()); + return std::pair (0, true); + } break; default: NS_ABORT_MSG ("Trying to decode unknown Extension Header"); @@ -1767,7 +2119,7 @@ SixLowPanNetDevice::DecompressLowPanNhc (Ptr packet, Address const &src, } NS_LOG_DEBUG ( "Rebuilt packet: " << *packet << " Size " << packet->GetSize () ); - return actualHeaderType; + return std::pair (actualHeaderType, false); } uint32_t @@ -2013,7 +2365,11 @@ bool SixLowPanNetDevice::ProcessFragment (Ptr& packet, Address const &sr DecompressLowPanHc1 (p, src, dst); break; case SixLowPanDispatch::LOWPAN_IPHC: - DecompressLowPanIphc (p, src, dst); + if (DecompressLowPanIphc (p, src, dst)) + { + m_dropTrace (DROP_SATETFUL_DECOMPRESSION_PROBLEM, p, m_node->GetObject (), GetIfIndex ()); + return false; + } break; default: NS_FATAL_ERROR ("Unsupported 6LoWPAN encoding, exiting."); @@ -2294,6 +2650,205 @@ void SixLowPanNetDevice::HandleTimeout (void) return; } +void SixLowPanNetDevice::AddContext (uint8_t contextId, Ipv6Prefix contextPrefix, bool compressionAllowed, Time validLifetime) +{ + NS_LOG_FUNCTION (this << +contextId << Ipv6Address::GetOnes ().CombinePrefix (contextPrefix) << contextPrefix << compressionAllowed << validLifetime.As (Time::S)); + + if (contextId > 15) + { + NS_LOG_LOGIC ("Invalid context ID (" << +contextId << "), ignoring"); + return; + } + + if (validLifetime == Time(0)) + { + NS_LOG_LOGIC ("Context (" << +contextId << "), removed (validity time is zero)"); + m_contextTable.erase (contextId); + return; + } + + m_contextTable[contextId].contextPrefix = contextPrefix; + m_contextTable[contextId].compressionAllowed = compressionAllowed; + m_contextTable[contextId].validLifetime = Simulator::Now () + validLifetime; + + return; +} + +bool SixLowPanNetDevice::GetContext (uint8_t contextId, Ipv6Prefix& contextPrefix, bool& compressionAllowed, Time& validLifetime) +{ + NS_LOG_FUNCTION (this << +contextId); + + if (contextId > 15) + { + NS_LOG_LOGIC ("Invalid context ID (" << +contextId << "), ignoring"); + return false; + } + + if (m_contextTable.find (contextId) == m_contextTable.end ()) + { + NS_LOG_LOGIC ("Context not found (" << +contextId << "), ignoring"); + return false; + } + + contextPrefix = m_contextTable[contextId].contextPrefix; + compressionAllowed = m_contextTable[contextId].compressionAllowed; + validLifetime = m_contextTable[contextId].validLifetime; + + return true; +} + +void SixLowPanNetDevice::RenewContext (uint8_t contextId, Time validLifetime) +{ + NS_LOG_FUNCTION (this << +contextId << validLifetime.As (Time::S)); + + if (contextId > 15) + { + NS_LOG_LOGIC ("Invalid context ID (" << +contextId << "), ignoring"); + return; + } + + if (m_contextTable.find (contextId) == m_contextTable.end ()) + { + NS_LOG_LOGIC ("Context not found (" << +contextId << "), ignoring"); + return; + } + m_contextTable[contextId].compressionAllowed = true; + m_contextTable[contextId].validLifetime = Simulator::Now () + validLifetime; + return; +} + + +void SixLowPanNetDevice::InvalidateContext (uint8_t contextId) +{ + NS_LOG_FUNCTION (this << +contextId); + + if (contextId > 15) + { + NS_LOG_LOGIC ("Invalid context ID (" << +contextId << "), ignoring"); + return; + } + + if (m_contextTable.find (contextId) == m_contextTable.end ()) + { + NS_LOG_LOGIC ("Context not found (" << +contextId << "), ignoring"); + return; + } + m_contextTable[contextId].compressionAllowed = false; + return; +} + +void SixLowPanNetDevice::RemoveContext (uint8_t contextId) +{ + NS_LOG_FUNCTION (this << +contextId); + + if (contextId > 15) + { + NS_LOG_LOGIC ("Invalid context ID (" << +contextId << "), ignoring"); + return; + } + + if (m_contextTable.find (contextId) == m_contextTable.end ()) + { + NS_LOG_LOGIC ("Context not found (" << +contextId << "), ignoring"); + return; + } + + m_contextTable.erase (contextId); + return; +} + +bool SixLowPanNetDevice::FindUnicastCompressionContext (Ipv6Address address, uint8_t& contextId) +{ + NS_LOG_FUNCTION (this << address); + + for (const auto& iter: m_contextTable) + { + ContextEntry context = iter.second; + + if ( (context.compressionAllowed == true) && (context.validLifetime > Simulator::Now ()) ) + { + + if (address.HasPrefix (context.contextPrefix)) + { + NS_LOG_LOGIC ("Fount context " << +contextId << " " << + Ipv6Address::GetOnes ().CombinePrefix (context.contextPrefix) << context.contextPrefix << " matching"); + + contextId = iter.first; + return true; + } + } + } + return false; +} + +bool SixLowPanNetDevice::FindMulticastCompressionContext (Ipv6Address address, uint8_t& contextId) +{ + NS_LOG_FUNCTION (this << address); + + // The only allowed context-based compressed multicast address is in the form + // ffXX:XXLL:PPPP:PPPP:PPPP:PPPP:XXXX:XXXX + + for (const auto& iter: m_contextTable) + { + ContextEntry context = iter.second; + + if ( (context.compressionAllowed == true) && (context.validLifetime > Simulator::Now ()) ) + { + uint8_t contextLength = context.contextPrefix.GetPrefixLength (); + + if (contextLength <= 64) // only 64-bit prefixes or less are allowed. + { + uint8_t contextBytes[16]; + uint8_t addressBytes[16]; + + context.contextPrefix.GetBytes (contextBytes); + address.GetBytes (addressBytes); + + if (addressBytes[3] == contextLength && + addressBytes[4] == contextBytes[0] && + addressBytes[5] == contextBytes[1] && + addressBytes[6] == contextBytes[2] && + addressBytes[7] == contextBytes[3] && + addressBytes[8] == contextBytes[4] && + addressBytes[9] == contextBytes[5] && + addressBytes[10] == contextBytes[6] && + addressBytes[11] == contextBytes[7]) + { + NS_LOG_LOGIC ("Fount context " << +contextId << " " << + Ipv6Address::GetOnes ().CombinePrefix (context.contextPrefix) << context.contextPrefix << " matching"); + + contextId = iter.first; + return true; + } + } + } + } + return false; +} + +Ipv6Address SixLowPanNetDevice::CleanPrefix (Ipv6Address address, Ipv6Prefix prefix) +{ + uint8_t addressBytes[16]; + address.GetBytes (addressBytes); + uint8_t prefixLength = prefix.GetPrefixLength (); + + uint8_t bytesToClean = prefixLength / 8; + uint8_t bitsToClean = prefixLength % 8; + for (uint8_t i=0; i sixNetDevice, uint32_t ifindex); + /** + * Add, remove, or update a context used in IPHC stateful compression. + * + * A context with a zero validLifetime will be immediately removed. + * + * \param [in] contextId context id (most be between 0 and 15 included). + * \param [in] contextPrefix context prefix to be used in compression/decompression. + * \param [in] compressionAllowed compression and decompression allowed (true), decompression only (false). + * \param [in] validLifetime validity time (relative to the actual time). + * + */ + void AddContext (uint8_t contextId, Ipv6Prefix contextPrefix, bool compressionAllowed, Time validLifetime); + + /** + * Get a context used in IPHC stateful compression. + * + * \param [in] contextId context id (most be between 0 and 15 included). + * \param [out] contextPrefix context prefix to be used in compression/decompression. + * \param [out] compressionAllowed compression and decompression allowed (true), decompression only (false). + * \param [out] validLifetime validity time (relative to the actual time). + * + * \return false if the context has not been found. + * + */ + bool GetContext (uint8_t contextId, Ipv6Prefix& contextPrefix, bool& compressionAllowed, Time& validLifetime); + + /** + * Renew a context used in IPHC stateful compression. + * + * The context will have its lifetime extended and its validity for compression re-enabled. + * + * \param [in] contextId context id (most be between 0 and 15 included). + * \param [in] validLifetime validity time (relative to the actual time). + */ + void RenewContext (uint8_t contextId, Time validLifetime); + + /** + * Invalidate a context used in IPHC stateful compression. + * + * An invalid context will not be used for compression but it will be used for decompression. + * + * \param [in] contextId context id (most be between 0 and 15 included). + */ + void InvalidateContext (uint8_t contextId); + + /** + * Remove a context used in IPHC stateful compression. + * + * \param [in] contextId context id (most be between 0 and 15 included). + */ + void RemoveContext (uint8_t contextId); + protected: virtual void DoDispose (void); @@ -312,8 +365,9 @@ private: * \param [in] packet The packet to be compressed. * \param [in] src The MAC source address. * \param [in] dst The MAC destination address. + * \return true if the packet can not be decompressed due to wrong context informations. */ - void DecompressLowPanIphc (Ptr packet, Address const &src, Address const &dst); + bool DecompressLowPanIphc (Ptr packet, Address const &src, Address const &dst); /** * \brief Compress the headers according to NHC compression. @@ -332,9 +386,9 @@ private: * \param [in] dst The MAC destination address. * \param [in] srcAddress The IPv6 source address. * \param [in] dstAddress The IPv6 destination address. - * \return The decompressed header type. + * \return A std::pair containing the decompressed header type and a flag - true if the packet can not be decompressed due to wrong context informations. */ - uint8_t DecompressLowPanNhc (Ptr packet, Address const &src, Address const &dst, Ipv6Address srcAddress, Ipv6Address dstAddress); + std::pair DecompressLowPanNhc (Ptr packet, Address const &src, Address const &dst, Ipv6Address srcAddress, Ipv6Address dstAddress); /** * \brief Compress the headers according to NHC compression. @@ -549,6 +603,48 @@ private: uint32_t m_compressionThreshold; //!< Minimum L2 payload size. Ptr m_rng; //!< Rng for the fragments tag. + + /** + * Structure holding the informations for a context (used in compression and decompression) + */ + struct ContextEntry + { + Ipv6Prefix contextPrefix; //!< context prefix to be used in compression/decompression + bool compressionAllowed; //!< compression and decompression allowed (true), decompression only (false) + Time validLifetime; //!< validity period + }; + + std::map m_contextTable; //!< Table of the contexts used in compression/decompression + + /** + * \brief Finds if the given unicast address matches a context for compression + * + * \param[in] address the address to check + * \param[out] the context found + * \return true if a valid context has been found + */ + bool FindUnicastCompressionContext (Ipv6Address address, uint8_t& contextId); + + /** + * \brief Finds if the given multicast address matches a context for compression + * + * \param[in] address the address to check + * \param[out] the context found + * \return true if a valid context has been found + */ + bool FindMulticastCompressionContext (Ipv6Address address, uint8_t& contextId); + + /** + * \brief Clean an address from its prefix. + * + * This function is used to find the relevant bits to be sent in stateful IPHC compression. + * Only the pefix length is used - the address prefix is assumed to be matching the prefix. + * + * \param address the address to be cleaned + * \param the prefix to remove + * \return An address with the prefix zeroed. + */ + Ipv6Address CleanPrefix (Ipv6Address address, Ipv6Prefix prefix); }; } // namespace ns3 diff --git a/src/sixlowpan/test/mock-net-device.cc b/src/sixlowpan/test/mock-net-device.cc new file mode 100644 index 000000000..6f8fea978 --- /dev/null +++ b/src/sixlowpan/test/mock-net-device.cc @@ -0,0 +1,348 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2020 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ +#include "mock-net-device.h" +#include "ns3/node.h" +#include "ns3/packet.h" +#include "ns3/log.h" +#include "ns3/pointer.h" +#include "ns3/trace-source-accessor.h" +#include "ns3/boolean.h" +#include "ns3/simulator.h" +#include "ns3/channel.h" +#include "ns3/mac64-address.h" +#include "ns3/mac48-address.h" +#include "ns3/mac16-address.h" +#include "ns3/mac8-address.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("MockNetDevice"); +NS_OBJECT_ENSURE_REGISTERED (MockNetDevice); + +TypeId +MockNetDevice::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::MockNetDevice") + .SetParent () + .SetGroupName("Network") + .AddConstructor () + .AddAttribute ("PointToPointMode", + "The device is configured in Point to Point mode", + BooleanValue (false), + MakeBooleanAccessor (&MockNetDevice::m_pointToPointMode), + MakeBooleanChecker ()) + ; + return tid; +} + +MockNetDevice::MockNetDevice () + : m_node (0), + m_mtu (0xffff), + m_ifIndex (0), + m_linkUp (true) +{ + NS_LOG_FUNCTION (this); +} + +void +MockNetDevice::Receive (Ptr packet, uint16_t protocol, + Address to, Address from, NetDevice::PacketType packetType) +{ + NS_LOG_FUNCTION (this << packet << protocol << to << from); + + if (packetType != NetDevice::PACKET_OTHERHOST) + { + m_rxCallback (this, packet, protocol, from); + } + + if (!m_promiscCallback.IsNull ()) + { + m_promiscCallback (this, packet, protocol, from, to, packetType); + } +} + +void +MockNetDevice::SetIfIndex (const uint32_t index) +{ + NS_LOG_FUNCTION (this << index); + m_ifIndex = index; +} + +uint32_t +MockNetDevice::GetIfIndex (void) const +{ + NS_LOG_FUNCTION (this); + return m_ifIndex; +} + +Ptr +MockNetDevice::GetChannel (void) const +{ + NS_LOG_FUNCTION (this); + return 0; +} + +void +MockNetDevice::SetAddress (Address address) +{ + NS_LOG_FUNCTION (this << address); + m_address = address; +} + +Address +MockNetDevice::GetAddress (void) const +{ + NS_LOG_FUNCTION (this); + return m_address; +} + +bool +MockNetDevice::SetMtu (const uint16_t mtu) +{ + NS_LOG_FUNCTION (this << mtu); + m_mtu = mtu; + return true; +} + +uint16_t +MockNetDevice::GetMtu (void) const +{ + NS_LOG_FUNCTION (this); + return m_mtu; +} + +bool +MockNetDevice::IsLinkUp (void) const +{ + NS_LOG_FUNCTION (this); + return m_linkUp; +} + +void +MockNetDevice::AddLinkChangeCallback (Callback callback) +{ + NS_LOG_FUNCTION (this << &callback); + m_linkChangeCallbacks.ConnectWithoutContext (callback); +} + +bool +MockNetDevice::IsBroadcast (void) const +{ + NS_LOG_FUNCTION (this); + if (m_pointToPointMode) + { + return false; + } + if (Mac64Address::IsMatchingType (m_address)) + { + return false; + } + if (Mac8Address::IsMatchingType (m_address)) + { + return false; + } + + return true; +} + +Address +MockNetDevice::GetBroadcast (void) const +{ + NS_LOG_FUNCTION (this); + + Address address; + + if (Mac48Address::IsMatchingType (m_address)) + { + address = Mac48Address::GetBroadcast (); + } + else if (Mac16Address::IsMatchingType (m_address)) + { + address = Mac16Address::GetBroadcast (); + } + + return address; +} + +bool +MockNetDevice::IsMulticast (void) const +{ + NS_LOG_FUNCTION (this); + if (m_pointToPointMode) + { + return false; + } + if (Mac64Address::IsMatchingType (m_address)) + { + return false; + } + if (Mac8Address::IsMatchingType (m_address)) + { + return false; + } + + return true; +} + +Address +MockNetDevice::GetMulticast (Ipv4Address multicastGroup) const +{ + NS_LOG_FUNCTION (this << multicastGroup); + + Address address; + + if (Mac48Address::IsMatchingType (m_address)) + { + address = Mac48Address::GetMulticast (multicastGroup); + } + else if (Mac16Address::IsMatchingType (m_address)) + { + address = Mac16Address::GetMulticast (Ipv6Address::MakeIpv4MappedAddress (multicastGroup)); + } + + return address; +} + +Address MockNetDevice::GetMulticast (Ipv6Address addr) const +{ + NS_LOG_FUNCTION (this << addr); + Address address; + + if (Mac48Address::IsMatchingType (m_address)) + { + address = Mac48Address::GetMulticast (addr); + } + else if (Mac16Address::IsMatchingType (m_address)) + { + address = Mac16Address::GetMulticast (addr); + } + + return address; +} + +bool +MockNetDevice::IsPointToPoint (void) const +{ + NS_LOG_FUNCTION (this); + if (m_pointToPointMode) + { + return true; + } + return false; +} + +bool +MockNetDevice::IsBridge (void) const +{ + NS_LOG_FUNCTION (this); + return false; +} + +bool +MockNetDevice::Send (Ptr packet, const Address& dest, uint16_t protocolNumber) +{ + NS_LOG_FUNCTION (this << packet << dest << protocolNumber); + + return SendFrom (packet, m_address, dest, protocolNumber); +} + +bool +MockNetDevice::SendFrom (Ptr p, const Address& source, const Address& dest, uint16_t protocolNumber) +{ + NS_LOG_FUNCTION (this << p << source << dest << protocolNumber); + if (p->GetSize () > GetMtu ()) + { + return false; + } + + if (!m_sendCallback.IsNull ()) + { + m_sendCallback (this, p, protocolNumber, source, dest, NetDevice::PACKET_HOST); + } + + return true; +} + + +Ptr +MockNetDevice::GetNode (void) const +{ + NS_LOG_FUNCTION (this); + return m_node; +} +void +MockNetDevice::SetNode (Ptr node) +{ + NS_LOG_FUNCTION (this << node); + m_node = node; +} + +bool +MockNetDevice::NeedsArp (void) const +{ + NS_LOG_FUNCTION (this); + if (m_pointToPointMode) + { + return false; + } + return true; +} + +void +MockNetDevice::SetReceiveCallback (NetDevice::ReceiveCallback cb) +{ + NS_LOG_FUNCTION (this << &cb); + m_rxCallback = cb; +} + +void +MockNetDevice::DoDispose (void) +{ + NS_LOG_FUNCTION (this); + m_node = 0; + m_rxCallback.Nullify (); + m_promiscCallback.Nullify (); + m_sendCallback.Nullify (); + NetDevice::DoDispose (); +} + +void +MockNetDevice::SetPromiscReceiveCallback (PromiscReceiveCallback cb) +{ + NS_LOG_FUNCTION (this << &cb); + m_promiscCallback = cb; +} + +bool +MockNetDevice::SupportsSendFrom (void) const +{ + NS_LOG_FUNCTION (this); + return true; +} + +void +MockNetDevice::SetSendCallback (PromiscReceiveCallback cb) +{ + NS_LOG_FUNCTION (this << &cb); + m_sendCallback = cb; +} + + +} // namespace ns3 diff --git a/src/sixlowpan/test/mock-net-device.h b/src/sixlowpan/test/mock-net-device.h new file mode 100644 index 000000000..8e339aa9b --- /dev/null +++ b/src/sixlowpan/test/mock-net-device.h @@ -0,0 +1,132 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2020 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ +#ifndef MOCK_NET_DEVICE_H +#define MOCK_NET_DEVICE_H + +#include +#include + +#include "ns3/traced-callback.h" +#include "ns3/net-device.h" + +namespace ns3 { + +class Node; + +/** + * \ingroup netdevice + * + * This device assumes 48-bit mac addressing; there is also the possibility to + * add an ErrorModel if you want to force losses on the device. + * + * The device can be installed on a node through the MockNetDeviceHelper. + * In case of manual creation, the user is responsible for assigning an unique + * address to the device. + * + * By default the device is in Broadcast mode, with infinite bandwidth. + * + * \brief simple net device for simple things and testing + */ +class MockNetDevice : public NetDevice +{ +public: + /** + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + MockNetDevice (); + + /** + * Pretend that a packet has been received from a connected Channel. + * + * Note that no analysis is performed on the addresses, and the + * packet is forwarded to the callbacks according to the packetType. + * + * \param packet Packet received on the channel + * \param protocol protocol number + * \param to address packet should be sent to + * \param from address packet was sent from + * \param packetType type of the packet (e.g., NetDevice::PACKET_HOST, NetDevice::PACKET_OTHERHOST, etc.) + */ + void Receive (Ptr packet, uint16_t protocol, Address to, Address from, NetDevice::PacketType packetType); + + // inherited from NetDevice base class. + virtual void SetIfIndex (const uint32_t index); + virtual uint32_t GetIfIndex (void) const; + virtual Ptr GetChannel (void) const; + virtual void SetAddress (Address address); + virtual Address GetAddress (void) const; + virtual bool SetMtu (const uint16_t mtu); + virtual uint16_t GetMtu (void) const; + virtual bool IsLinkUp (void) const; + virtual void AddLinkChangeCallback (Callback callback); + virtual bool IsBroadcast (void) const; + virtual Address GetBroadcast (void) const; + virtual bool IsMulticast (void) const; + virtual Address GetMulticast (Ipv4Address multicastGroup) const; + virtual Address GetMulticast (Ipv6Address addr) const; + virtual bool IsPointToPoint (void) const; + virtual bool IsBridge (void) const; + virtual bool Send (Ptr packet, const Address& dest, uint16_t protocolNumber); + virtual bool SendFrom (Ptr packet, const Address& source, const Address& dest, uint16_t protocolNumber); + virtual Ptr GetNode (void) const; + virtual void SetNode (Ptr node); + virtual bool NeedsArp (void) const; + virtual void SetReceiveCallback (NetDevice::ReceiveCallback cb); + virtual void SetPromiscReceiveCallback (PromiscReceiveCallback cb); + virtual bool SupportsSendFrom (void) const; + + /** + * + * Add a callback to be invoked when the MockNetDevice has a packet to "send". + * + * In the callback the PacketType is always set to NetDevice::PACKET_HOST. + * + * \param cb callback to invoke whenever the MockNetDevice has one packet to "send". + * + */ + void SetSendCallback (PromiscReceiveCallback cb); + +protected: + virtual void DoDispose (void); + +private: + + NetDevice::ReceiveCallback m_rxCallback; //!< Receive callback + NetDevice::PromiscReceiveCallback m_promiscCallback; //!< Promiscuous receive callback + NetDevice::PromiscReceiveCallback m_sendCallback; //!< Send callback + Ptr m_node; //!< Node this netDevice is associated to + uint16_t m_mtu; //!< MTU + uint32_t m_ifIndex; //!< Interface index + Address m_address; //!< MAC address + + bool m_linkUp; //!< Flag indicating whether or not the link is up + bool m_pointToPointMode; //!< Enabling this will disable Broadcast and Arp. + + /** + * List of callbacks to fire if the link changes state (up or down). + */ + TracedCallback<> m_linkChangeCallbacks; +}; + +} // namespace ns3 + +#endif /* MOCK_NET_DEVICE_H */ diff --git a/src/sixlowpan/test/sixlowpan-iphc-stateful-test.cc b/src/sixlowpan/test/sixlowpan-iphc-stateful-test.cc new file mode 100644 index 000000000..f303711fd --- /dev/null +++ b/src/sixlowpan/test/sixlowpan-iphc-stateful-test.cc @@ -0,0 +1,313 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2020 Universita' di Firenze, Italy + * + * 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: Tommaso Pecorella + */ + +#include "ns3/test.h" +#include "ns3/socket-factory.h" +#include "ns3/simulator.h" +#include "ns3/socket.h" +#include "ns3/boolean.h" + +#include "ns3/log.h" +#include "ns3/node.h" +#include "ns3/inet6-socket-address.h" +#include "ns3/internet-stack-helper.h" +#include "ns3/ipv6-address-helper.h" +#include "ns3/icmpv6-l4-protocol.h" + +#include "ns3/sixlowpan-net-device.h" +#include "ns3/sixlowpan-header.h" +#include "ns3/sixlowpan-helper.h" +#include "mock-net-device.h" + +#include +#include +#include + +using namespace ns3; + +/** + * \ingroup sixlowpan + * \defgroup sixlowpan-test 6LoWPAN module tests + */ + + +/** + * \ingroup sixlowpan-test + * \ingroup tests + * + * \brief 6LoWPAN IPHC stateful compression Test + */ +class SixlowpanIphcStatefulImplTest : public TestCase +{ + /** + * \brief Structure to hold the Rx/Tx packets. + */ + typedef struct + { + Ptr packet; /**< Packet data */ + Address src; /**< Source address */ + Address dst; /**< Destination address */ + } Data; + + + std::vector m_txPackets; //!< Transmitted packets + std::vector m_rxPackets; //!< Received packets + + bool ReceiveFromMockDevice (Ptr device, Ptr packet, uint16_t protocol, + Address const &source, Address const &destination, NetDevice::PacketType packetType); + + bool PromiscReceiveFromSixLowPanDevice (Ptr device, Ptr packet, uint16_t protocol, + Address const &source, Address const &destination, NetDevice::PacketType packetType); + + void SendOnePacket (NetDeviceContainer devices, Ipv6Address from, Ipv6Address to); + + NetDeviceContainer m_mockDevices; //!< MockNetDevice container + NetDeviceContainer m_sixDevices; //!< SixLowPanNetDevice container + +public: + virtual void DoRun (void); + SixlowpanIphcStatefulImplTest (); +}; + +SixlowpanIphcStatefulImplTest::SixlowpanIphcStatefulImplTest () + : TestCase ("Sixlowpan IPHC stateful implementation") +{ +} + +bool +SixlowpanIphcStatefulImplTest::ReceiveFromMockDevice (Ptr device, Ptr packet, uint16_t protocol, + Address const &source, Address const &destination, NetDevice::PacketType packetType) +{ + Data incomingPkt; + incomingPkt.packet = packet->Copy (); + incomingPkt.src = source; + incomingPkt.dst = destination; + m_txPackets.push_back (incomingPkt); + + Ptr mockDev = DynamicCast (m_mockDevices.Get(1)); + if (mockDev) + { + uint32_t id = mockDev->GetNode ()->GetId (); + Simulator::ScheduleWithContext (id, Time(1), &MockNetDevice::Receive, mockDev, incomingPkt.packet, protocol, destination, source, packetType); + } + return true; +} + +bool +SixlowpanIphcStatefulImplTest::PromiscReceiveFromSixLowPanDevice (Ptr device, Ptr packet, uint16_t protocol, + Address const &source, Address const &destination, NetDevice::PacketType packetType) +{ + Data incomingPkt; + incomingPkt.packet = packet->Copy (); + incomingPkt.src = source; + incomingPkt.dst = destination; + m_rxPackets.push_back (incomingPkt); + + return true; +} + +void +SixlowpanIphcStatefulImplTest::SendOnePacket (NetDeviceContainer devices, Ipv6Address from, Ipv6Address to) +{ + Ptr pkt = Create (10); + Ipv6Header ipHdr; + ipHdr.SetSourceAddress (from); + ipHdr.SetDestinationAddress (to); + ipHdr.SetHopLimit (64); + ipHdr.SetPayloadLength (10); + ipHdr.SetNextHeader (0xff); + pkt->AddHeader (ipHdr); + + devices.Get (0)->Send (pkt, Mac48Address ("00:00:00:00:00:02"), 0); +} + +void +SixlowpanIphcStatefulImplTest::DoRun (void) +{ + NodeContainer nodes; + nodes.Create(2); + + // First node, setup NetDevices. + Ptr mockNetDevice0 = CreateObject (); + nodes.Get (0)->AddDevice (mockNetDevice0); + mockNetDevice0->SetNode (nodes.Get (0)); + mockNetDevice0->SetAddress (Mac48Address ("00:00:00:00:00:01")); + mockNetDevice0->SetMtu (150); + mockNetDevice0->SetSendCallback ( MakeCallback (&SixlowpanIphcStatefulImplTest::ReceiveFromMockDevice, this) ); + m_mockDevices.Add (mockNetDevice0); + + // Second node, setup NetDevices. + Ptr mockNetDevice1 = CreateObject (); + nodes.Get (1)->AddDevice (mockNetDevice1); + mockNetDevice1->SetNode (nodes.Get (1)); + mockNetDevice1->SetAddress (Mac48Address ("00:00:00:00:00:02")); + mockNetDevice1->SetMtu (150); + m_mockDevices.Add (mockNetDevice1); + + InternetStackHelper internetv6; + internetv6.Install (nodes); + + SixLowPanHelper sixlowpan; + m_sixDevices = sixlowpan.Install (m_mockDevices); + m_sixDevices.Get (1)->SetPromiscReceiveCallback ( MakeCallback (&SixlowpanIphcStatefulImplTest::PromiscReceiveFromSixLowPanDevice, this) ); + + Ipv6AddressHelper ipv6; + ipv6.SetBase (Ipv6Address ("2001:2::"), Ipv6Prefix (64)); + Ipv6InterfaceContainer deviceInterfaces; + deviceInterfaces = ipv6.Assign (m_sixDevices); + + // This is a hack to prevent Router Solicitations and Duplicate Address Detection being sent. + for (auto i = nodes.Begin (); i != nodes.End (); i++) + { + Ptr node = *i; + Ptr ipv6L3 = (*i)->GetObject (); + if (ipv6L3) + { + ipv6L3->SetAttribute ("IpForward", BooleanValue (true)); + ipv6L3->SetAttribute ("SendIcmpv6Redirect", BooleanValue (false)); + } + Ptr icmpv6 = (*i)->GetObject (); + if (icmpv6) + { + icmpv6->SetAttribute ("DAD", BooleanValue (false)); + } + } + + sixlowpan.AddContext (m_sixDevices, 0, Ipv6Prefix ("2001:2::", 64), Time (Minutes (30))); + sixlowpan.AddContext (m_sixDevices, 1, Ipv6Prefix ("2001:1::", 64), Time (Minutes (30))); + + Ipv6Address srcElided = deviceInterfaces.GetAddress (0, 1); + Ipv6Address dstElided = Ipv6Address::MakeAutoconfiguredAddress (Mac48Address ("00:00:00:00:00:02"), Ipv6Prefix ("2001:2::", 64)); + + Simulator::Schedule (Seconds (1), &SixlowpanIphcStatefulImplTest::SendOnePacket, this, m_sixDevices, + Ipv6Address::GetAny (), + dstElided); + + Simulator::Schedule (Seconds (2), &SixlowpanIphcStatefulImplTest::SendOnePacket, this, m_sixDevices, + Ipv6Address ("2001:2::f00d:f00d:cafe:cafe"), + Ipv6Address ("2001:1::0000:00ff:fe00:cafe")); + + Simulator::Schedule (Seconds (3), &SixlowpanIphcStatefulImplTest::SendOnePacket, this, m_sixDevices, + Ipv6Address ("2001:1::0000:00ff:fe00:cafe"), + Ipv6Address ("2001:1::f00d:f00d:cafe:cafe")); + + Simulator::Schedule (Seconds (4), &SixlowpanIphcStatefulImplTest::SendOnePacket, this, m_sixDevices, + srcElided, + Ipv6Address ("2001:1::f00d:f00d:cafe:cafe")); + + Simulator::Stop (Seconds (10)); + + Simulator::Run (); + Simulator::Destroy (); + + // ------ Now the tests ------------ + + SixLowPanIphc iphcHdr; + Ipv6Header ipv6Hdr; + + // first packet sent, expected CID(0) SAC(1) SAM (0) M(0) DAC(1) DAM (3) + m_txPackets[0].packet->RemoveHeader (iphcHdr); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetCid (), false, "CID should be false, is true"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSac (), true, "SAC should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSam (), SixLowPanIphc::HC_INLINE, "SAM should be HC_INLINE, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetM (), false, "M should be false, is true"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDac (), true, "DAC should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSrcContextId (), 0, "Src context should be 0, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDstContextId (), 0, "Dst context should be 0, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDam (), SixLowPanIphc::HC_COMPR_0, "DAM should be HC_COMPR_0, it is not"); + + // first packet received, expected :: -> dstElided + m_rxPackets[0].packet->RemoveHeader (ipv6Hdr); + NS_TEST_EXPECT_MSG_EQ (ipv6Hdr.GetSourceAddress (), Ipv6Address::GetAny (), "Src address wrongly rebuilt"); + NS_TEST_EXPECT_MSG_EQ (ipv6Hdr.GetDestinationAddress (), dstElided, "Dst address wrongly rebuilt"); + + // second packet sent, expected CID(1) SAC(1) SAM (1) M(0) DAC(1) DAM (2) + m_txPackets[1].packet->RemoveHeader (iphcHdr); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetCid (), true, "CID should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSac (), true, "SAC should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSam (), SixLowPanIphc::HC_COMPR_64, "SAM should be HC_COMPR_64, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetM (), false, "M should be false, is true"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDac (), true, "DAC should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSrcContextId (), 0, "Src context should be 0, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDstContextId (), 1, "Dst context should be 1, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDam (), SixLowPanIphc::HC_COMPR_16, "DAM should be HC_COMPR_16, it is not"); + + // second packet received, expected 2001:2::f00d:f00d:cafe:cafe -> 2001:1::0000:00ff:fe00:cafe + m_rxPackets[1].packet->RemoveHeader (ipv6Hdr); + NS_TEST_EXPECT_MSG_EQ (ipv6Hdr.GetSourceAddress (), Ipv6Address ("2001:2::f00d:f00d:cafe:cafe"), "Src address wrongly rebuilt"); + NS_TEST_EXPECT_MSG_EQ (ipv6Hdr.GetDestinationAddress (), Ipv6Address ("2001:1::0000:00ff:fe00:cafe"), "Dst address wrongly rebuilt"); + + // third packet sent, expected CID(17) SAC(1) SAM (2) M(0) DAC(1) DAM (1) + m_txPackets[2].packet->RemoveHeader (iphcHdr); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetCid (), true, "CID should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSac (), true, "SAC should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSam (), SixLowPanIphc::HC_COMPR_16, "SAM should be HC_COMPR_16, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetM (), false, "M should be false, is true"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDac (), true, "DAC should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSrcContextId (), 1, "Src context should be 1, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDstContextId (), 1, "Dst context should be 1, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDam (), SixLowPanIphc::HC_COMPR_64, "DAM should be HC_COMPR_64, it is not"); + + // third packet received, expected 2001:1::0000:00ff:fe00:cafe -> 2001:1::f00d:f00d:cafe:cafe + m_rxPackets[2].packet->RemoveHeader (ipv6Hdr); + NS_TEST_EXPECT_MSG_EQ (ipv6Hdr.GetSourceAddress (), Ipv6Address ("2001:1::0000:00ff:fe00:cafe"), "Src address wrongly rebuilt"); + NS_TEST_EXPECT_MSG_EQ (ipv6Hdr.GetDestinationAddress (), Ipv6Address ("2001:1::f00d:f00d:cafe:cafe"), "Dst address wrongly rebuilt"); + + // fourth packet sent, expected CID(1) SAC(1) SAM (3) M(0) DAC(1) DAM (1) + m_txPackets[3].packet->RemoveHeader (iphcHdr); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetCid (), true, "CID should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSac (), true, "SAC should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSam (), SixLowPanIphc::HC_COMPR_0, "SAM should be HC_COMPR_0, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetM (), false, "M should be false, is true"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDac (), true, "DAC should be true, is false"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetSrcContextId (), 0, "Src context should be 0, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDstContextId (), 1, "Dst context should be 1, it is not"); + NS_TEST_EXPECT_MSG_EQ (iphcHdr.GetDam (), SixLowPanIphc::HC_COMPR_64, "DAM should be HC_COMPR_64, it is not"); + + // fourth packet received, expected srcElided -> 2001:1::f00d:f00d:cafe:cafe + m_rxPackets[3].packet->RemoveHeader (ipv6Hdr); + NS_TEST_EXPECT_MSG_EQ (ipv6Hdr.GetSourceAddress (), srcElided, "Src address wrongly rebuilt"); + NS_TEST_EXPECT_MSG_EQ (ipv6Hdr.GetDestinationAddress (), Ipv6Address ("2001:1::f00d:f00d:cafe:cafe"), "Dst address wrongly rebuilt"); + + m_rxPackets.clear (); + m_txPackets.clear (); +} + + +/** + * \ingroup sixlowpan-test + * \ingroup tests + * + * \brief 6LoWPAN IPHC TestSuite + */ +class SixlowpanIphcStatefulTestSuite : public TestSuite +{ +public: + SixlowpanIphcStatefulTestSuite (); +private: +}; + +SixlowpanIphcStatefulTestSuite::SixlowpanIphcStatefulTestSuite () + : TestSuite ("sixlowpan-iphc-stateful", UNIT) +{ + AddTestCase (new SixlowpanIphcStatefulImplTest (), TestCase::QUICK); +} + +static SixlowpanIphcStatefulTestSuite g_sixlowpanIphcStatefulTestSuite; //!< Static variable for test initialization diff --git a/src/sixlowpan/wscript b/src/sixlowpan/wscript index b932992e0..6e51db111 100644 --- a/src/sixlowpan/wscript +++ b/src/sixlowpan/wscript @@ -11,8 +11,10 @@ def build(bld): module_test = bld.create_ns3_module_test_library('sixlowpan') module_test.source = [ + 'test/mock-net-device.cc', 'test/sixlowpan-hc1-test.cc', 'test/sixlowpan-iphc-test.cc', + 'test/sixlowpan-iphc-stateful-test.cc', 'test/sixlowpan-fragmentation-test.cc', ]