mirror of
https://github.com/espressif/openthread.git
synced 2026-09-06 17:20:09 +00:00
[ip6] add IPv6 fragmentation and reassembly support (#3948)
This commit is contained in:
committed by
Jonathan Hui
parent
3198a3be69
commit
864e3458f5
+2
-1
@@ -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
|
||||
|
||||
@@ -48,6 +48,7 @@ DHCP6_SERVER ?= 1
|
||||
DIAGNOSTIC ?= 1
|
||||
DNS_CLIENT ?= 1
|
||||
ECDSA ?= 1
|
||||
IP6_FRAGM ?= 1
|
||||
JAM_DETECTION ?= 1
|
||||
JOINER ?= 1
|
||||
LEGACY ?= 1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -49,6 +49,7 @@ DHCP6_SERVER ?= 1
|
||||
DIAGNOSTIC ?= 1
|
||||
DNS_CLIENT ?= 1
|
||||
ECDSA ?= 1
|
||||
IP6_FRAGM ?= 1
|
||||
JAM_DETECTION ?= 1
|
||||
JOINER ?= 1
|
||||
LEGACY ?= 1
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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()
|
||||
Reference in New Issue
Block a user