mirror of
https://github.com/espressif/openthread.git
synced 2026-11-08 15:33:21 +00:00
[ip6] allow OpenThread-generated ICMPv6 to be passed to the host (#13814)
With `OPENTHREAD_CONFIG_IP6_ALLOW_LOOP_BACK_HOST_DATAGRAMS` set to 0, `MessagePool::Allocate()` gives every new message the "not allowed" default, including messages OpenThread generates itself. `Ip6::PassToHost()` then drops echo requests sent by `PingSender`, echo replies, and ICMPv6 errors that are addressed to the host. Explicitly allow these internally generated messages to be passed to the host, and copy the flag to each fragment in `Ip6::FragmentDatagram()` so that large datagrams keep it. Messages allocated through the public APIs still get the configured default. Also add unit tests verifying that fragments inherit the original message's loopback-to-host flag and that ICMPv6 echo requests and replies are passed to the host.
This commit is contained in:
@@ -109,6 +109,8 @@ Error Icmp::SendError(Icmp6Header::Type aType,
|
||||
|
||||
VerifyOrExit((message = Get<Ip6>().NewMessage(settings)) != nullptr, error = kErrorNoBufs);
|
||||
|
||||
message->SetLoopbackToHostAllowed(true);
|
||||
|
||||
// Prepare the ICMPv6 error message. We only include the IPv6 header
|
||||
// of the original message causing the error.
|
||||
|
||||
@@ -201,6 +203,8 @@ Error Icmp::HandleEchoRequest(Message &aRequestMessage, const MessageInfo &aMess
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
replyMessage->SetLoopbackToHostAllowed(true);
|
||||
|
||||
dataOffset = aRequestMessage.GetOffset() + Header::kDataFieldOffset;
|
||||
|
||||
SuccessOrExit(error = replyMessage->AppendBytes(&replyHeader, Header::kDataFieldOffset));
|
||||
|
||||
@@ -584,6 +584,7 @@ Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto)
|
||||
|
||||
VerifyOrExit((fragment = NewMessage()) != nullptr, error = kErrorNoBufs);
|
||||
IgnoreError(fragment->SetPriority(aMessage.GetPriority()));
|
||||
fragment->SetLoopbackToHostAllowed(aMessage.IsLoopbackToHostAllowed());
|
||||
SuccessOrExit(error = fragment->SetLength(aMessage.GetOffset() + sizeof(fragmentHeader) + payloadFragment));
|
||||
|
||||
header.SetPayloadLength(payloadFragment + sizeof(fragmentHeader));
|
||||
|
||||
@@ -133,6 +133,8 @@ void PingSender::SendPing(void)
|
||||
message = Get<Ip6::Icmp>().NewMessage();
|
||||
VerifyOrExit(message != nullptr);
|
||||
|
||||
message->SetLoopbackToHostAllowed(true);
|
||||
|
||||
SuccessOrExit(message->Append(BigEndian::HostSwap32(now.GetValue())));
|
||||
|
||||
if (mConfig.mSize > message->GetLength())
|
||||
|
||||
@@ -537,6 +537,173 @@ void TestAppender(void)
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
|
||||
|
||||
static uint16_t sNumFragmentsPassedToHost;
|
||||
|
||||
static void HandleIp6ReceiveFragment(otMessage *aMessage, void *aContext)
|
||||
{
|
||||
Message &message = AsCoreType(aMessage);
|
||||
Ip6::Header header;
|
||||
|
||||
OT_UNUSED_VARIABLE(aContext);
|
||||
|
||||
SuccessOrQuit(message.Read(0, header));
|
||||
VerifyOrQuit(header.GetNextHeader() == Ip6::kProtoFragment);
|
||||
sNumFragmentsPassedToHost++;
|
||||
|
||||
message.Free();
|
||||
}
|
||||
|
||||
void TestFragmentLoopbackToHost(void)
|
||||
{
|
||||
// Send a datagram larger than `kMaxDatagramLength` to an off-mesh
|
||||
// destination so that it is fragmented and the fragments are
|
||||
// forwarded to the host. Verify that the fragments inherit the
|
||||
// loopback-to-host flag of the original message.
|
||||
|
||||
static const uint8_t kPayload[Ip6::kMaxDatagramLength] = {0};
|
||||
static const bool kAllowLoopbackToHost[] = {true, false};
|
||||
|
||||
Instance *instance;
|
||||
Ip6::Address source;
|
||||
Ip6::Address destination;
|
||||
|
||||
printf("TestFragmentLoopbackToHost\n");
|
||||
|
||||
instance = static_cast<Instance *>(testInitInstance());
|
||||
VerifyOrQuit(instance != nullptr);
|
||||
|
||||
otIp6SetReceiveCallback(instance, HandleIp6ReceiveFragment, nullptr);
|
||||
|
||||
SuccessOrQuit(source.FromString("2001:db8::1"));
|
||||
SuccessOrQuit(destination.FromString("2001:db8::2"));
|
||||
|
||||
for (bool allowLoopbackToHost : kAllowLoopbackToHost)
|
||||
{
|
||||
Message *message;
|
||||
Ip6::MessageInfo messageInfo;
|
||||
|
||||
printf(" - LoopbackToHostAllowed: %s\n", allowLoopbackToHost ? "true" : "false");
|
||||
|
||||
message = instance->Get<Ip6::Ip6>().NewMessage();
|
||||
VerifyOrQuit(message != nullptr);
|
||||
SuccessOrQuit(message->AppendBytes(kPayload, sizeof(kPayload)));
|
||||
message->SetLoopbackToHostAllowed(allowLoopbackToHost);
|
||||
|
||||
messageInfo.SetSockAddr(source);
|
||||
messageInfo.SetPeerAddr(destination);
|
||||
|
||||
sNumFragmentsPassedToHost = 0;
|
||||
|
||||
SuccessOrQuit(instance->Get<Ip6::Ip6>().SendDatagram(*message, messageInfo, Ip6::kProtoUdp));
|
||||
|
||||
while (otTaskletsArePending(instance))
|
||||
{
|
||||
otTaskletsProcess(instance);
|
||||
}
|
||||
|
||||
printf(" fragments passed to host: %u\n", sNumFragmentsPassedToHost);
|
||||
|
||||
if (allowLoopbackToHost)
|
||||
{
|
||||
VerifyOrQuit(sNumFragmentsPassedToHost > 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
VerifyOrQuit(sNumFragmentsPassedToHost == 0);
|
||||
}
|
||||
}
|
||||
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
|
||||
|
||||
static uint16_t sNumEchoRequestsPassedToHost;
|
||||
static uint16_t sNumEchoRepliesPassedToHost;
|
||||
|
||||
static void HandleIp6ReceiveIcmp(otMessage *aMessage, void *aContext)
|
||||
{
|
||||
Message &message = AsCoreType(aMessage);
|
||||
Ip6::Header header;
|
||||
Ip6::Icmp6Header icmpHeader;
|
||||
|
||||
OT_UNUSED_VARIABLE(aContext);
|
||||
|
||||
SuccessOrQuit(message.Read(0, header));
|
||||
VerifyOrQuit(header.GetNextHeader() == Ip6::kProtoIcmp6);
|
||||
SuccessOrQuit(message.Read(sizeof(header), icmpHeader));
|
||||
|
||||
switch (icmpHeader.GetType())
|
||||
{
|
||||
case Ip6::Icmp6Header::kTypeEchoRequest:
|
||||
sNumEchoRequestsPassedToHost++;
|
||||
break;
|
||||
case Ip6::Icmp6Header::kTypeEchoReply:
|
||||
sNumEchoRepliesPassedToHost++;
|
||||
break;
|
||||
default:
|
||||
VerifyOrQuit(false);
|
||||
}
|
||||
|
||||
message.Free();
|
||||
}
|
||||
|
||||
void TestIcmpLoopbackToHost(void)
|
||||
{
|
||||
// Ping one of the device's own (non mesh-local) addresses and
|
||||
// verify that both the echo request (from `PingSender`) and the
|
||||
// echo reply (from `Icmp`) are passed to the host, independent
|
||||
// of `OPENTHREAD_CONFIG_IP6_ALLOW_LOOP_BACK_HOST_DATAGRAMS`.
|
||||
|
||||
Instance *instance;
|
||||
otNetifAddress netifAddress;
|
||||
otPingSenderConfig config;
|
||||
|
||||
printf("TestIcmpLoopbackToHost\n");
|
||||
|
||||
instance = static_cast<Instance *>(testInitInstance());
|
||||
VerifyOrQuit(instance != nullptr);
|
||||
|
||||
otIp6SetReceiveCallback(instance, HandleIp6ReceiveIcmp, nullptr);
|
||||
|
||||
ClearAllBytes(netifAddress);
|
||||
SuccessOrQuit(AsCoreType(&netifAddress.mAddress).FromString("2001:db8::1"));
|
||||
netifAddress.mPrefixLength = 64;
|
||||
netifAddress.mPreferred = true;
|
||||
netifAddress.mValid = true;
|
||||
SuccessOrQuit(otIp6AddUnicastAddress(instance, &netifAddress));
|
||||
|
||||
ClearAllBytes(config);
|
||||
config.mSource = netifAddress.mAddress;
|
||||
config.mDestination = netifAddress.mAddress;
|
||||
config.mCount = 1;
|
||||
|
||||
sNumEchoRequestsPassedToHost = 0;
|
||||
sNumEchoRepliesPassedToHost = 0;
|
||||
|
||||
SuccessOrQuit(otPingSenderPing(instance, &config));
|
||||
|
||||
while (otTaskletsArePending(instance))
|
||||
{
|
||||
otTaskletsProcess(instance);
|
||||
}
|
||||
|
||||
printf(" - echo requests passed to host: %u, echo replies passed to host: %u\n", sNumEchoRequestsPassedToHost,
|
||||
sNumEchoRepliesPassedToHost);
|
||||
|
||||
VerifyOrQuit(sNumEchoRequestsPassedToHost == 1);
|
||||
VerifyOrQuit(sNumEchoRepliesPassedToHost == 1);
|
||||
|
||||
otPingSenderStop(instance);
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
@@ -550,6 +717,12 @@ int main(void)
|
||||
|
||||
ot::UnitTester::TestCloning();
|
||||
ot::TestAppender();
|
||||
#if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
|
||||
ot::TestFragmentLoopbackToHost();
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
|
||||
ot::TestIcmpLoopbackToHost();
|
||||
#endif
|
||||
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user