[ip6] add IPv6 fragmentation and reassembly support (#3948)

This commit is contained in:
Christian Stauffer
2019-08-28 10:31:55 -07:00
committed by Jonathan Hui
parent 3198a3be69
commit 864e3458f5
21 changed files with 770 additions and 37 deletions
+2 -1
View File
@@ -59,6 +59,7 @@ python --version || die
-DOPENTHREAD_CONFIG_DIAG_ENABLE=1 \
-DOPENTHREAD_CONFIG_DNS_CLIENT_ENABLE=1 \
-DOPENTHREAD_CONFIG_ECDSA_ENABLE=1 \
-DOPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE=1 \
-DOPENTHREAD_CONFIG_LEGACY_ENABLE=1 \
-DOPENTHREAD_CONFIG_JAM_DETECTION_ENABLE=1 \
-DOPENTHREAD_CONFIG_JOINER_ENABLE=1 \
@@ -184,7 +185,7 @@ build_nrf52811() {
build_nrf52840() {
# Default OpenThread switches for nRF52840 platform
OPENTHREAD_FLAGS="BORDER_AGENT=1 BORDER_ROUTER=1 COAP=1 COAPS=1 COMMISSIONER=1 SLAAC=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 JOINER=1 LINK_RAW=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 SNTP_CLIENT=1 UDP_FORWARD=1"
OPENTHREAD_FLAGS="BORDER_AGENT=1 BORDER_ROUTER=1 COAP=1 COAPS=1 COMMISSIONER=1 SLAAC=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 DNS_CLIENT=1 ECDSA=1 FULL_LOGS=1 IP6_FRAGM=1 JOINER=1 LINK_RAW=1 MAC_FILTER=1 MTD_NETDIAG=1 SERVICE=1 SNTP_CLIENT=1 UDP_FORWARD=1"
# UART transport
git checkout -- . || die
+1
View File
@@ -48,6 +48,7 @@ DHCP6_SERVER ?= 1
DIAGNOSTIC ?= 1
DNS_CLIENT ?= 1
ECDSA ?= 1
IP6_FRAGM ?= 1
JAM_DETECTION ?= 1
JOINER ?= 1
LEGACY ?= 1
+5
View File
@@ -46,6 +46,7 @@ DISABLE_DOC ?= 0
DNS_CLIENT ?= 0
ECDSA ?= 0
EXTERNAL_HEAP ?= 0
IP6_FRAGM ?= 0
JAM_DETECTION ?= 0
JOINER ?= 0
LEGACY ?= 0
@@ -138,6 +139,10 @@ ifeq ($(EXTERNAL_HEAP),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE=1
endif
ifeq ($(IP6_FRAGM),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE=1
endif
ifeq ($(JAM_DETECTION),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_JAM_DETECTION_ENABLE=1
endif
+6 -3
View File
@@ -58,9 +58,11 @@ extern "C" {
*/
typedef enum otIcmp6Type
{
OT_ICMP6_TYPE_DST_UNREACH = 1, ///< Destination Unreachable
OT_ICMP6_TYPE_ECHO_REQUEST = 128, ///< Echo Request
OT_ICMP6_TYPE_ECHO_REPLY = 129, ///< Echo Reply
OT_ICMP6_TYPE_DST_UNREACH = 1, ///< Destination Unreachable
OT_ICMP6_TYPE_PACKET_TO_BIG = 2, ///< Packet To Big
OT_ICMP6_TYPE_TIME_EXCEEDED = 3, ///< Time Exceeded
OT_ICMP6_TYPE_ECHO_REQUEST = 128, ///< Echo Request
OT_ICMP6_TYPE_ECHO_REPLY = 129, ///< Echo Reply
} otIcmp6Type;
/**
@@ -70,6 +72,7 @@ typedef enum otIcmp6Type
typedef enum otIcmp6Code
{
OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE = 0, ///< Destination Unreachable No Route
OT_ICMP6_CODE_FRAGM_REAS_TIME_EX = 1, ///< Fragment Reassembly Time Exceeded
} otIcmp6Code;
#define OT_ICMP6_HEADER_DATA_SIZE 4 ///< Size of an message specific data of ICMPv6 Header.
+12
View File
@@ -115,6 +115,18 @@ Send a UDP message.
> udp send fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234 hello
```
### send \-s \<message_size\> \<ip\> \<port\>
Send a few bytes over UDP.
* message_size: number of bytes to be send.
* ip: the IPv6 destination address.
* port: the UDP destination port.
```bash
> udp send -s 800 fdde:ad00:beef:0:bb1:ebd6:ad10:f33 1234
```
### send \<message\>
Send a UDP message on a connected socket.
+53 -5
View File
@@ -136,14 +136,27 @@ otError UdpExample::ProcessSend(int argc, char *argv[])
{
otError error;
otMessageInfo messageInfo;
otMessage * message = NULL;
int curArg = 0;
otMessage * message = NULL;
int curArg = 0;
unsigned long autoGenMessageLength = 0;
memset(&messageInfo, 0, sizeof(messageInfo));
VerifyOrExit(argc == 1 || argc == 3, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(argc == 1 || argc == 3 || argc == 4, error = OT_ERROR_INVALID_ARGS);
if (argc == 3)
if (argc == 4)
{
if (strcmp(argv[curArg++], "-s") == 0)
{
Interpreter::ParseUnsignedLong(argv[curArg++], autoGenMessageLength);
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
if (argc >= 3)
{
long value;
@@ -159,7 +172,14 @@ otError UdpExample::ProcessSend(int argc, char *argv[])
message = otUdpNewMessage(mInterpreter.mInstance, NULL);
VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS);
error = otMessageAppend(message, argv[curArg], static_cast<uint16_t>(strlen(argv[curArg])));
if (autoGenMessageLength != 0)
{
error = WriteCharToBuffer(message, static_cast<uint16_t>(autoGenMessageLength));
}
else
{
error = otMessageAppend(message, argv[curArg], static_cast<uint16_t>(strlen(argv[curArg])));
}
SuccessOrExit(error);
error = otUdpSend(&mSocket, message, &messageInfo);
@@ -174,6 +194,34 @@ exit:
return error;
}
otError UdpExample::WriteCharToBuffer(otMessage *aMessage, uint16_t aMessageSize)
{
otError error = OT_ERROR_NONE;
uint8_t character = 0x30; // 0
for (uint16_t index = 0; index < aMessageSize; index++)
{
SuccessOrExit(error = otMessageAppend(aMessage, &character, 1));
character++;
switch (character)
{
case 0x3A: // 9
character = 0x41; // A
break;
case 0x5B: // Z
character = 0x61; // a
break;
case 0x7B: // z
character = 0x30; // 0
break;
}
}
exit:
return error;
}
otError UdpExample::Process(int argc, char *argv[])
{
otError error = OT_ERROR_PARSE;
+1
View File
@@ -80,6 +80,7 @@ private:
otError ProcessConnect(int argc, char *argv[]);
otError ProcessOpen(int argc, char *argv[]);
otError ProcessSend(int argc, char *argv[]);
otError WriteCharToBuffer(otMessage *aMessage, uint16_t aSize);
static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
void HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo);
+7 -5
View File
@@ -88,10 +88,11 @@ struct MessageInfo
PriorityQueue *mPriority; ///< Identifies the priority queue (if any) where this message is queued.
} mQueue; ///< Identifies the queue (if any) where this message is queued.
uint32_t mDatagramTag; ///< The datagram tag used for 6LoWPAN fragmentation or identification used for IPv6
///< fragmentation.
uint16_t mReserved; ///< Number of header bytes reserved for the message.
uint16_t mLength; ///< Number of bytes within the message.
uint16_t mOffset; ///< A byte offset within the message.
uint16_t mDatagramTag; ///< The datagram tag used for 6LoWPAN fragmentation.
RssAverager mRssAverager; ///< The averager maintaining the received signal strength (RSS) average.
uint8_t mChildMask[kChildMaskBytes]; ///< A bit-vector to indicate which sleepy children need to receive this.
@@ -477,12 +478,13 @@ public:
Message *Clone(void) const { return Clone(GetLength()); }
/**
* This method returns the datagram tag used for 6LoWPAN fragmentation.
* This method returns the datagram tag used for 6LoWPAN fragmentation or the identification used for IPv6
* fragmentation.
*
* @returns The 6LoWPAN datagram tag.
* @returns The 6LoWPAN datagram tag or the IPv6 fragment identification.
*
*/
uint16_t GetDatagramTag(void) const { return mBuffer.mHead.mInfo.mDatagramTag; }
uint32_t GetDatagramTag(void) const { return mBuffer.mHead.mInfo.mDatagramTag; }
/**
* This method sets the datagram tag used for 6LoWPAN fragmentation.
@@ -490,7 +492,7 @@ public:
* @param[in] aTag The 6LoWPAN datagram tag.
*
*/
void SetDatagramTag(uint16_t aTag) { mBuffer.mHead.mInfo.mDatagramTag = aTag; }
void SetDatagramTag(uint32_t aTag) { mBuffer.mHead.mInfo.mDatagramTag = aTag; }
/**
* This method returns whether or not the message forwarding is scheduled for the child.
+32
View File
@@ -75,6 +75,38 @@
#define OPENTHREAD_CONFIG_IP6_MAX_DATAGRAM_LENGTH 1280
#endif
/**
* @def OPENTHREAD_CONFIG_ENABLE_IP6_FRAGMENTATION
*
* Define as 1 to enable IPv6 Fragmentation support.
*
*/
#ifndef OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
#define OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_IP6_MAX_ASSEMBLED_DATAGRAM
*
* This setting configures the max datagram length of a unfragmented IPv6 packet.
*
*/
#ifndef OPENTHREAD_CONFIG_IP6_MAX_ASSEMBLED_DATAGRAM
#define OPENTHREAD_CONFIG_IP6_MAX_ASSEMBLED_DATAGRAM 2000
#endif
/**
* @def OPENTHREAD_CONFIG_IP6_REASSEMBLY_TIMEOUT
*
* This setting configures the reassembly timeout for IPv6 in seconds.
*
* RFC 2460 \> 60 seconds
*
*/
#ifndef OPENTHREAD_CONFIG_IP6_REASSEMBLY_TIMEOUT
#define OPENTHREAD_CONFIG_IP6_REASSEMBLY_TIMEOUT 60
#endif
/**
* @def OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
*
+6 -3
View File
@@ -83,9 +83,11 @@ public:
*/
enum Type
{
kTypeDstUnreach = OT_ICMP6_TYPE_DST_UNREACH, ///< Destination Unreachable
kTypeEchoRequest = OT_ICMP6_TYPE_ECHO_REQUEST, ///< Echo Request
kTypeEchoReply = OT_ICMP6_TYPE_ECHO_REPLY, ///< Echo Reply
kTypeDstUnreach = OT_ICMP6_TYPE_DST_UNREACH, ///< Destination Unreachable
kTypePacketToBig = OT_ICMP6_TYPE_PACKET_TO_BIG, ///< Packet To Big
kTypeTimeExceeded = OT_ICMP6_TYPE_TIME_EXCEEDED, ///< Time Exceeded
kTypeEchoRequest = OT_ICMP6_TYPE_ECHO_REQUEST, ///< Echo Request
kTypeEchoReply = OT_ICMP6_TYPE_ECHO_REPLY, ///< Echo Reply
};
/**
@@ -95,6 +97,7 @@ public:
enum Code
{
kCodeDstUnreachNoRoute = OT_ICMP6_CODE_DST_UNREACH_NO_ROUTE, ///< Destination Unreachable No Route
kCodeFragmReasTimeEx = OT_ICMP6_CODE_FRAGM_REAS_TIME_EX, ///< Fragment Reassembly Time Exceeded
};
/**
+323 -10
View File
@@ -39,6 +39,7 @@
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "common/message.hpp"
#include "common/random.hpp"
#include "net/icmp6.hpp"
#include "net/ip6_address.hpp"
#include "net/netif.hpp"
@@ -59,6 +60,9 @@ Ip6::Ip6(Instance &aInstance)
, mIcmp(aInstance)
, mUdp(aInstance)
, mMpl(aInstance)
#if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
, mTimer(aInstance, &Ip6::HandleTimer, this)
#endif
{
}
@@ -518,7 +522,14 @@ exit:
if (error == OT_ERROR_NONE)
{
EnqueueDatagram(aMessage);
if (aMessage.GetLength() > kMaxDatagramLength)
{
error = FragmentDatagram(aMessage, aIpProto);
}
else
{
EnqueueDatagram(aMessage);
}
}
return error;
@@ -603,8 +614,300 @@ exit:
return error;
}
otError Ip6::HandleFragment(Message &aMessage)
#if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
otError Ip6::FragmentDatagram(Message &aMessage, IpProto aIpProto)
{
otError error = OT_ERROR_NONE;
Header header;
FragmentHeader fragmentHeader;
Message * fragment = NULL;
uint16_t fragmentCnt = 0;
uint16_t payloadFragment = 0;
uint16_t offset = 0;
int assertValue = 0;
uint16_t maxPayloadFragment =
FragmentHeader::MakeDivisibleByEight(kMinimalMtu - aMessage.GetOffset() - sizeof(fragmentHeader));
uint16_t payloadLeft = aMessage.GetLength() - aMessage.GetOffset();
VerifyOrExit(aMessage.GetLength() <= kMaxAssembledDatagramLength, error = OT_ERROR_NO_BUFS);
VerifyOrExit(aMessage.Read(0, sizeof(header), &header) == sizeof(header), error = OT_ERROR_PARSE);
header.SetNextHeader(kProtoFragment);
fragmentHeader.Init();
fragmentHeader.SetIdentification(Random::NonCrypto::GetUint32());
fragmentHeader.SetNextHeader(aIpProto);
fragmentHeader.SetMoreFlag();
while (payloadLeft != 0)
{
if (payloadLeft < maxPayloadFragment)
{
fragmentHeader.ClearMoreFlag();
payloadFragment = payloadLeft;
payloadLeft = 0;
otLogDebgIp6("Last Fragment");
}
else
{
payloadLeft -= maxPayloadFragment;
payloadFragment = maxPayloadFragment;
}
offset = fragmentCnt * FragmentHeader::BytesToFragmentOffset(maxPayloadFragment);
fragmentHeader.SetOffset(offset);
VerifyOrExit((fragment = NewMessage(0)) != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = fragment->SetLength(aMessage.GetOffset() + sizeof(fragmentHeader) + payloadFragment));
header.SetPayloadLength(payloadFragment + sizeof(fragmentHeader));
assertValue = fragment->Write(0, sizeof(header), &header);
assert(assertValue == sizeof(header));
SuccessOrExit(error = fragment->SetOffset(aMessage.GetOffset()));
assertValue = fragment->Write(aMessage.GetOffset(), sizeof(fragmentHeader), &fragmentHeader);
assert(assertValue == sizeof(fragmentHeader));
VerifyOrExit(aMessage.CopyTo(aMessage.GetOffset() + FragmentHeader::FragmentOffsetToBytes(offset),
aMessage.GetOffset() + sizeof(fragmentHeader), payloadFragment,
*fragment) == static_cast<int>(payloadFragment),
error = OT_ERROR_NO_BUFS);
EnqueueDatagram(*fragment);
fragmentCnt++;
fragment = NULL;
otLogInfoIp6("Fragment %d with %d bytes sent", fragmentCnt, payloadFragment);
}
aMessage.Free();
exit:
if (error == OT_ERROR_NO_BUFS)
{
otLogWarnIp6("No buffer for Ip6 fragmentation");
}
if (error != OT_ERROR_NONE && fragment != NULL)
{
fragment->Free();
}
return error;
}
otError Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromNcpHost)
{
otError error = OT_ERROR_NONE;
Header header, headerBuffer;
FragmentHeader fragmentHeader;
Message * message = NULL;
uint16_t offset = 0;
uint16_t payloadFragment = 0;
int assertValue = 0;
bool isFragmented = true;
VerifyOrExit(aMessage.Read(0, sizeof(header), &header) == sizeof(header), error = OT_ERROR_PARSE);
VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(fragmentHeader), &fragmentHeader) == sizeof(fragmentHeader),
error = OT_ERROR_PARSE);
if (fragmentHeader.GetOffset() == 0 && !fragmentHeader.IsMoreFlagSet())
{
isFragmented = false;
error = aMessage.MoveOffset(sizeof(fragmentHeader));
ExitNow();
}
for (message = mReassemblyList.GetHead(); message; message = message->GetNext())
{
VerifyOrExit(aMessage.Read(0, sizeof(headerBuffer), &headerBuffer) == sizeof(headerBuffer),
error = OT_ERROR_PARSE);
if (message->GetDatagramTag() == fragmentHeader.GetIdentification() &&
headerBuffer.GetSource() == header.GetSource() && headerBuffer.GetDestination() == header.GetDestination())
{
break;
}
}
offset = FragmentHeader::FragmentOffsetToBytes(fragmentHeader.GetOffset());
payloadFragment = aMessage.GetLength() - aMessage.GetOffset() - sizeof(fragmentHeader);
if (message == NULL)
{
VerifyOrExit((message = NewMessage(0)) != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = message->SetLength(aMessage.GetOffset()));
message->SetTimeout(kIp6ReassemblyTimeout);
SuccessOrExit(error = message->SetOffset(0));
message->SetDatagramTag(fragmentHeader.GetIdentification());
// copying the non-fragmentable header to the fragmentation buffer
assertValue = aMessage.CopyTo(0, 0, aMessage.GetOffset(), *message);
assert(assertValue == aMessage.GetOffset());
if (!mTimer.IsRunning())
{
mTimer.Start(kStateUpdatePeriod);
}
mReassemblyList.Enqueue(*message);
otLogDebgIp6("start reassembly.");
}
otLogInfoIp6("Fragment with id %d received > %d bytes, offset %d", message->GetDatagramTag(), payloadFragment,
offset);
if (offset + payloadFragment + aMessage.GetOffset() > kMaxAssembledDatagramLength)
{
otLogWarnIp6("Package too large for fragment buffer");
ExitNow(error = OT_ERROR_NO_BUFS);
}
// increase message buffer if necessary
if (message->GetLength() < offset + payloadFragment + aMessage.GetOffset())
{
SuccessOrExit(error = message->SetLength(offset + payloadFragment + aMessage.GetOffset()));
}
// copy the fragment payload into the message buffer
assertValue = aMessage.CopyTo(aMessage.GetOffset() + sizeof(fragmentHeader), aMessage.GetOffset() + offset,
payloadFragment, *message);
assert(assertValue == static_cast<int>(payloadFragment));
// check if it is the last frame
if (!fragmentHeader.IsMoreFlagSet())
{
// use the offset value for the whole ip message length
SuccessOrExit(error = message->SetOffset(offset + payloadFragment + aMessage.GetOffset()));
}
if (message->GetOffset() >= message->GetLength())
{
// creates the header for the reassembled ipv6 package
VerifyOrExit(aMessage.Read(0, sizeof(header), &header) == sizeof(header), error = OT_ERROR_PARSE);
header.SetPayloadLength(message->GetLength() - sizeof(header));
header.SetNextHeader(static_cast<IpProto>(fragmentHeader.GetNextHeader()));
assertValue = message->Write(0, sizeof(header), &header);
assert(assertValue == sizeof(header));
otLogDebgIp6("Reassembly complete.");
mReassemblyList.Dequeue(*message);
error = HandleDatagram(*message, aNetif, aMessageInfo.mLinkInfo, aFromNcpHost);
}
exit:
if (error != OT_ERROR_DROP && error != OT_ERROR_NONE && isFragmented)
{
if (message != NULL)
{
mReassemblyList.Dequeue(*message);
message->Free();
}
otLogWarnIp6("Reassembly failed: %s", otThreadErrorToString(error));
}
if (isFragmented)
{
// drop all fragments, the payload is stored in the fragment buffer
error = OT_ERROR_DROP;
}
return error;
}
void Ip6::CleanupFragmentationBuffer(void)
{
for (Message *message = mReassemblyList.GetHead(); message;)
{
Message *next = message->GetNext();
mReassemblyList.Dequeue(*message);
message->Free();
message = next;
}
}
void Ip6::HandleTimer(Timer &aTimer)
{
aTimer.GetOwner<Ip6>().HandleUpdateTimer();
}
void Ip6::HandleUpdateTimer(void)
{
UpdateReassemblyList();
if (mReassemblyList.GetHead() != NULL)
{
mTimer.Start(kStateUpdatePeriod);
}
}
void Ip6::UpdateReassemblyList(void)
{
Message *next;
for (Message *message = mReassemblyList.GetHead(); message; message = next)
{
next = message->GetNext();
if (message->GetTimeout() > 0)
{
message->DecrementTimeout();
}
else
{
otLogNoteIp6("Reassembly timeout.");
SendIcmpError(*message, IcmpHeader::kTypeTimeExceeded, IcmpHeader::kCodeFragmReasTimeEx);
mReassemblyList.Dequeue(*message);
message->Free();
}
}
}
otError Ip6::SendIcmpError(Message &aMessage, IcmpHeader::Type aIcmpType, IcmpHeader::Code aIcmpCode)
{
otError error = OT_ERROR_NONE;
Header header;
MessageInfo messageInfo;
VerifyOrExit(aMessage.Read(0, sizeof(header), &header) == sizeof(header), error = OT_ERROR_PARSE);
messageInfo.SetPeerAddr(header.GetSource());
messageInfo.SetSockAddr(header.GetDestination());
messageInfo.SetHopLimit(header.GetHopLimit());
messageInfo.SetLinkInfo(NULL);
SuccessOrExit(error = mIcmp.SendError(aIcmpType, aIcmpCode, messageInfo, header));
exit:
return error;
}
#else
otError Ip6::FragmentDatagram(Message &aMessage, IpProto aIpProto)
{
OT_UNUSED_VARIABLE(aIpProto);
EnqueueDatagram(aMessage);
return OT_ERROR_NONE;
}
otError Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromNcpHost)
{
OT_UNUSED_VARIABLE(aNetif);
OT_UNUSED_VARIABLE(aMessageInfo);
OT_UNUSED_VARIABLE(aFromNcpHost);
otError error = OT_ERROR_NONE;
FragmentHeader fragmentHeader;
@@ -618,12 +921,16 @@ otError Ip6::HandleFragment(Message &aMessage)
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
otError Ip6::HandleExtensionHeaders(Message &aMessage,
Header & aHeader,
uint8_t &aNextHeader,
bool aForward,
bool aReceive)
otError Ip6::HandleExtensionHeaders(Message & aMessage,
Netif * aNetif,
MessageInfo &aMessageInfo,
Header & aHeader,
uint8_t & aNextHeader,
bool aForward,
bool aFromNcpHost,
bool aReceive)
{
otError error = OT_ERROR_NONE;
ExtensionHeader extHeader;
@@ -640,7 +947,7 @@ otError Ip6::HandleExtensionHeaders(Message &aMessage,
break;
case kProtoFragment:
SuccessOrExit(error = HandleFragment(aMessage));
SuccessOrExit(error = HandleFragment(aMessage, aNetif, aMessageInfo, aFromNcpHost));
break;
case kProtoDstOpts:
@@ -672,7 +979,12 @@ otError Ip6::HandlePayload(Message &aMessage, MessageInfo &aMessageInfo, uint8_t
switch (aIpProto)
{
case kProtoUdp:
ExitNow(error = mUdp.HandleMessage(aMessage, aMessageInfo));
error = mUdp.HandleMessage(aMessage, aMessageInfo);
if (error == OT_ERROR_DROP)
{
otLogNoteIp6("Error UDP Checksum");
}
ExitNow();
case kProtoIcmp6:
ExitNow(error = mIcmp.HandleMessage(aMessage, aMessageInfo));
@@ -882,7 +1194,8 @@ otError Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkM
// process IPv6 Extension Headers
nextHeader = static_cast<uint8_t>(header.GetNextHeader());
SuccessOrExit(error = HandleExtensionHeaders(aMessage, header, nextHeader, forward, receive));
SuccessOrExit(error = HandleExtensionHeaders(aMessage, aNetif, messageInfo, header, nextHeader, forward,
aFromNcpHost, receive));
// process IPv6 Payload
if (receive)
+29 -8
View File
@@ -44,6 +44,7 @@
#include "common/encoding.hpp"
#include "common/locator.hpp"
#include "common/message.hpp"
#include "common/timer.hpp"
#include "net/icmp6.hpp"
#include "net/ip6_address.hpp"
#include "net/ip6_headers.hpp"
@@ -104,8 +105,12 @@ class Ip6 : public InstanceLocator
public:
enum
{
kDefaultHopLimit = OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT,
kMaxDatagramLength = OPENTHREAD_CONFIG_IP6_MAX_DATAGRAM_LENGTH,
kDefaultHopLimit = OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT,
kMaxDatagramLength = OPENTHREAD_CONFIG_IP6_MAX_DATAGRAM_LENGTH,
kMaxAssembledDatagramLength = OPENTHREAD_CONFIG_IP6_MAX_ASSEMBLED_DATAGRAM,
kIp6ReassemblyTimeout = OPENTHREAD_CONFIG_IP6_REASSEMBLY_TIMEOUT,
kMinimalMtu = 1280,
kStateUpdatePeriod = 1000,
};
/**
@@ -344,12 +349,23 @@ private:
const MessageInfo &aMessageInfo,
uint8_t aIpProto,
bool aFromNcpHost);
otError HandleExtensionHeaders(Message &aMessage,
Header & aHeader,
uint8_t &aNextHeader,
bool aForward,
bool aReceive);
otError HandleFragment(Message &aMessage);
otError HandleExtensionHeaders(Message & aMessage,
Netif * aNetif,
MessageInfo &aMessageInfo,
Header & aHeader,
uint8_t & aNextHeader,
bool aForward,
bool aFromNcpHost,
bool aReceive);
otError FragmentDatagram(Message &aMessage, IpProto aIpProto);
otError HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromNcpHost);
#if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
void CleanupFragmentationBuffer(void);
void HandleUpdateTimer(void);
void UpdateReassemblyList(void);
otError SendIcmpError(Message &aMessage, IcmpHeader::Type aIcmpType, IcmpHeader::Code aIcmpCode);
static void HandleTimer(Timer &aTimer);
#endif
otError AddMplOption(Message &aMessage, Header &aHeader);
otError AddTunneledMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMessageInfo);
otError InsertMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMessageInfo);
@@ -370,6 +386,11 @@ private:
Icmp mIcmp;
Udp mUdp;
Mpl mMpl;
#if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
TimerMilli mTimer;
MessageQueue mReassemblyList;
#endif
};
/**
+4
View File
@@ -60,7 +60,11 @@ bool Header::IsValid(void) const
VerifyOrExit(IsVersion6(), ret = false);
// check Payload Length
#if !OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
VerifyOrExit((sizeof(*this) + GetPayloadLength()) <= Ip6::kMaxDatagramLength, ret = false);
#else
VerifyOrExit((sizeof(*this) + GetPayloadLength()) <= Ip6::kMaxAssembledDatagramLength, ret = false);
#endif
exit:
return ret;
+45
View File
@@ -551,6 +551,7 @@ public:
void Init(void)
{
mReserved = 0;
mOffsetMore = 0;
mIdentification = 0;
}
@@ -610,6 +611,50 @@ public:
*/
void SetMoreFlag(void) { mOffsetMore = HostSwap16(HostSwap16(mOffsetMore) | kMoreFlag); }
/**
* This method returns the frame identification.
*
* @returns The frame identification.
*
*/
uint32_t GetIdentification(void) const { return mIdentification; }
/**
* This method sets the frame identification.
*
* @param[in] aIdentification The fragment identification value.
*/
void SetIdentification(uint32_t aIdentification) { mIdentification = aIdentification; }
/**
* This method returns the next valid payload length for a fragment.
*
* @param[in] aLength The payload length to be validated for a fragment.
*
* @returns Valid IPv6 fragment payload length.
*
*/
static inline uint16_t MakeDivisibleByEight(uint16_t aLength) { return aLength & 0xfff8; }
/**
* This method converts the fragment offset of 8-octet units into bytes.
*
* @param[in] aOffset The fragment offset in 8-octet units.
*
* @returns The fragment offset in bytes.
*
*/
static inline uint16_t FragmentOffsetToBytes(uint16_t aOffset) { return static_cast<uint16_t>(aOffset << 3); }
/**
* This method converts a fragment offset in bytes into a fragment offset in 8-octet units.
*
* @param[in] aOffset The fragment offset in bytes.
*
* @returns The fragment offset in 8-octet units.
*/
static inline uint16_t BytesToFragmentOffset(uint16_t aOffset) { return aOffset >> 3; }
private:
uint8_t mNextHeader;
uint8_t mReserved;
+2 -2
View File
@@ -797,7 +797,7 @@ start:
fragmentHeader = reinterpret_cast<Lowpan::FragmentHeader *>(payload);
fragmentHeader->Init();
fragmentHeader->SetDatagramSize(aMessage.GetLength());
fragmentHeader->SetDatagramTag(aMessage.GetDatagramTag());
fragmentHeader->SetDatagramTag(static_cast<uint16_t>(aMessage.GetDatagramTag()));
fragmentHeader->SetDatagramOffset(0);
payload += fragmentHeader->GetHeaderLength();
@@ -824,7 +824,7 @@ start:
fragmentHeader = reinterpret_cast<Lowpan::FragmentHeader *>(payload);
fragmentHeader->Init();
fragmentHeader->SetDatagramSize(aMessage.GetLength());
fragmentHeader->SetDatagramTag(aMessage.GetDatagramTag());
fragmentHeader->SetDatagramTag(static_cast<uint16_t>(aMessage.GetDatagramTag()));
fragmentHeader->SetDatagramOffset(aMessage.GetOffset());
payload += fragmentHeader->GetHeaderLength();
+1
View File
@@ -49,6 +49,7 @@ DHCP6_SERVER ?= 1
DIAGNOSTIC ?= 1
DNS_CLIENT ?= 1
ECDSA ?= 1
IP6_FRAGM ?= 1
JAM_DETECTION ?= 1
JOINER ?= 1
LEGACY ?= 1
+3
View File
@@ -147,6 +147,7 @@ EXTRA_DIST = \
test_common.py \
test_crypto.py \
test_diag.py \
test_ipv6_fragmentation.py \
test_ipv6.py \
test_lowpan.py \
test_mac802154.py \
@@ -166,6 +167,7 @@ check_SCRIPTS = \
test_common.py \
test_crypto.py \
test_diag.py \
test_ipv6_fragmentation.py \
test_ipv6.py \
test_lowpan.py \
test_mac802154.py \
@@ -281,6 +283,7 @@ TESTS = \
XFAIL_NCP_TESTS = \
test_coaps.py \
test_diag.py \
test_ipv6_fragmentation.py \
test_service.py \
Cert_5_3_10_AddressQuery.py \
Cert_8_1_01_Commissioning.py \
+65
View File
@@ -45,6 +45,7 @@ except ImportError:
IPV6_NEXT_HEADER_HOP_BY_HOP = 0
IPV6_NEXT_HEADER_TCP = 6
IPV6_NEXT_HEADER_UDP = 17
IPV6_NEXT_HEADER_FRAGMENT = 44
IPV6_NEXT_HEADER_ICMP = 58
UPPER_LAYER_PROTOCOLS = [
@@ -55,6 +56,7 @@ UPPER_LAYER_PROTOCOLS = [
# ICMP Protocol codes
ICMP_DESTINATION_UNREACHABLE = 1
ICMP_TIME_EXCEEDED = 3
ICMP_ECHO_REQUEST = 128
ICMP_ECHO_RESPONSE = 129
@@ -377,6 +379,7 @@ class IPv6Packet(ConvertibleToBytes):
Extension headers:
- HopByHop
- Routing header (not implemented in this module)
- Fragment Header
Upper layer protocols:
- ICMPv6
@@ -643,6 +646,68 @@ class ICMPv6(UpperLayerProtocol):
return len(self.header) + len(self.body)
class FragmentHeader(ExtensionHeader):
""" Class representing Fragment extension header.
+-------------+----------+-----------------+-----+---+----------------+
| Next Header | Reserved | Fragment Offset | Res | M | Identification |
+-------------+----------+-----------------+-----+---+----------------+
Fragment extention header consists of:
- next_header type (8 bit)
- fragment offset which is multiple of 8 (13 bit)
- more_flag to indicate further data (1 bit)
- identification for all associated fragments (32 bit)
"""
@property
def type(self):
return 44
@property
def identification(self):
return self._identification
@property
def more_flag(self):
return self._more_flag
@property
def offset(self):
return self._fragm_offset
def __init__(self, next_header=None, fragm_offset=0, more_flag=False, identification=0):
super(FragmentHeader, self).__init__(next_header, 0)
self._fragm_offset = fragm_offset
self._more_flag = more_flag
self._identification = identification
def callculate_offset(self, position):
return position >> 3
def to_bytes(self):
data = bytearray([self.next_header, 0x00])
data += bytearray([self._fragm_offset >> 5, ((self._fragm_offset << 3) | self._more_flag) & 0xff])
data += struct.pack(">I", self._identification)
return data
@classmethod
def from_bytes(cls, data):
next_header = struct.unpack(">B", data.read(1))[0]
struct.unpack(">B", data.read(1))[0] # reserved
fragment_offset = struct.unpack(">H", data.read(2))[0]
more_flag = fragment_offset & 0x1
identificaton = struct.unpack(">I", data.read(4))[0]
fragment_offset = fragment_offset >> 3
return cls(next_header, fragment_offset, more_flag, identificaton)
def __len__(self):
return 64
class HopByHop(ExtensionHeader):
""" Class representing HopByHop extension header.
+25
View File
@@ -1052,6 +1052,31 @@ class Node:
payload += tlv.to_hex()
self.commissioner_mgmtset(self.bytes_to_hex_str(payload))
def udp_start(self, local_ipaddr, local_port):
cmd = 'udp open'
self.send_command(cmd)
self._expect('Done')
cmd = 'udp bind %s %s' % (local_ipaddr, local_port)
self.send_command(cmd)
self._expect('Done')
def udp_stop(self):
cmd = 'udp close'
self.send_command(cmd)
self._expect('Done')
def udp_send(self, bytes, ipaddr, port, success=True):
cmd = 'udp send -s %d %s %d' % (bytes, ipaddr, port)
self.send_command(cmd)
if success:
self._expect('Done')
else:
self._expect('Error')
def udp_check_rx(self, bytes_should_rx):
self._expect('%d bytes' % bytes_should_rx)
if __name__ == '__main__':
unittest.main()
+53
View File
@@ -61,6 +61,7 @@ from ipv6 import (
BytesPayloadFactory,
ICMPv6DestinationUnreachable,
UdpBasedOnSrcDstPortsPayloadFactory,
FragmentHeader,
)
import common
@@ -157,6 +158,18 @@ def any_checksum():
return any_uint(16)
def any_fragment_offset():
return any_uint(13)
def any_bool():
return (any_uint(1) == 1)
def any_fragment_identification():
return any_uint(32)
def any_icmp_payload(_type, code, checksum, body):
return bytearray([_type, code, (checksum >> 8) & 0xff, checksum & 0xff]) + body
@@ -752,6 +765,46 @@ class TestUDPDatagram(unittest.TestCase):
self.assertEqual(expected_udp_dgram_bytes, udp_dgram_bytes)
class TestIPv6FragmentHeader(unittest.TestCase):
def test_shold_convert_IPv6_fragment_header_to_bytes_when_to_bytes_method_is_called(self):
# GIVEN
type = any_type()
offset = any_fragment_offset()
more_flag = any_bool()
identification = any_fragment_identification()
ipv6_fragment_header = FragmentHeader(type, offset, more_flag, identification)
# WHEN
actual = ipv6_fragment_header.to_bytes()
# THEN
expected = bytearray([type, 0x00, offset >> 5, ((offset << 3) & 0xff) | more_flag])\
+ struct.pack("!I", identification)
self.assertEqual(expected, actual)
def test_should_create_FragmentHeader_when_from_bytes_classmethod_is_called(self):
# GIVEN
type = any_type()
offset = any_fragment_offset()
more_flag = any_bool()
identification = any_fragment_identification()
data = bytearray([type, 0x00, offset >> 5, ((offset << 3) & 0xff) | more_flag])\
+ struct.pack("!I", identification)
# WHEN
ipv6_fragment_header = FragmentHeader.from_bytes(io.BytesIO(data))
# THEN
self.assertEqual(type, ipv6_fragment_header.next_header)
self.assertEqual(offset, ipv6_fragment_header.offset)
self.assertEqual(more_flag, ipv6_fragment_header.more_flag)
self.assertEqual(identification, ipv6_fragment_header.identification)
class TestICMPv6(unittest.TestCase):
def test_should_creates_bytes_from_ICMPv6Header_and_body_when_to_bytes_method_is_called(self):
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env python
#
# Copyright (c) 2019, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
import unittest
import config
import node
LEADER = 1
ROUTER = 2
class TestIPv6Fragmentation(unittest.TestCase):
def setUp(self):
self.simulator = config.create_default_simulator()
self.nodes = {}
for i in range(1, 3):
self.nodes[i] = node.Node(i, simulator=self.simulator)
self.nodes[LEADER].set_panid(0xcafe)
self.nodes[LEADER].set_mode('rsdn')
self.nodes[LEADER].add_whitelist(self.nodes[ROUTER].get_addr64())
self.nodes[LEADER].enable_whitelist()
self.nodes[ROUTER].set_panid(0xcafe)
self.nodes[ROUTER].set_mode('rsdn')
self.nodes[ROUTER].add_whitelist(self.nodes[LEADER].get_addr64())
self.nodes[ROUTER].enable_whitelist()
self.nodes[ROUTER].set_router_selection_jitter(1)
def tearDown(self):
for n in list(self.nodes.values()):
n.stop()
n.destroy()
self.simulator.stop()
def test(self):
self.nodes[LEADER].start()
self.simulator.go(5)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER].start()
self.simulator.go(5)
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
mleid_leader = self.nodes[LEADER].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
mleid_router = self.nodes[ROUTER].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.nodes[LEADER].udp_start("::", 12345)
self.nodes[ROUTER].udp_start("::", 12345)
self.nodes[LEADER].udp_send(1952, mleid_router, 12345)
self.simulator.go(5)
self.nodes[ROUTER].udp_check_rx(1952)
self.nodes[ROUTER].udp_send(1831, mleid_leader, 12345)
self.simulator.go(5)
self.nodes[LEADER].udp_check_rx(1831)
self.nodes[ROUTER].udp_send(1953, mleid_leader, 12345, False)
self.nodes[ROUTER].udp_stop()
self.nodes[LEADER].udp_stop()
if __name__ == '__main__':
unittest.main()