mirror of
https://github.com/espressif/openthread.git
synced 2026-10-26 17:24:00 +00:00
Unlike otTaskletsSignalPending() and otPlatRadioTransmit(), InfraIf previously enqueued outgoing packets into mPendingTxQueue without calling Core::Get().MarkPendingAction(). When a higher-index node (e.g., an unattached candidate node1) sends a packet over InfraIf to a lower-index node (e.g., node0 Border Agent), ProcessInfraIf(node1) delivers the packet into node0.mInfraIf.Receive() after Process(node0) has already run in the current Core::AdvanceTime() pass. Any synchronous response packet enqueued into node0.mInfraIf.mPendingTxQueue remained undelivered when mPendingAction stayed false, causing Core::AdvanceTime() to jump mNow forward to the next unrelated scheduled timer. For a 6-flight DTLS handshake between node1 and node0, all three server flights stalled until separate background timers expired, intermittently pushing handshake completion past 1 second. Add InfraIf::EnqueueTxPacket() to enqueue outgoing messages and call Core::Get().MarkPendingAction(), make mPendingTxQueue private with Core as a friend class, and add InfraIf::Reset() for Node::Reset().
708 lines
23 KiB
C++
708 lines
23 KiB
C++
/*
|
|
* Copyright (c) 2025, 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.
|
|
*/
|
|
|
|
#include "nexus_infra_if.hpp"
|
|
|
|
#include <openthread/platform/infra_if.h>
|
|
|
|
#include "nexus_core.hpp"
|
|
#include "nexus_node.hpp"
|
|
|
|
namespace ot {
|
|
namespace Nexus {
|
|
|
|
InfraIf::InfraIf(Instance &aInstance)
|
|
: InstanceLocator(aInstance)
|
|
, mIfIndex(0)
|
|
, mUdpHook(nullptr)
|
|
, mHasRioPrefix(false)
|
|
, mDhcp6PdListening(false)
|
|
, mIsDnsServer(false)
|
|
, mRaTimer(aInstance)
|
|
{
|
|
}
|
|
|
|
void InfraIf::AfterInit(void)
|
|
{
|
|
Ip6::Address address;
|
|
LinkLayerAddress mac;
|
|
Ip6::InterfaceIdentifier iid;
|
|
|
|
mIfIndex = 1;
|
|
|
|
GetLinkLayerAddress(mac);
|
|
SuccessOrQuit(mac.ConvertToIid(iid));
|
|
|
|
address.InitAsLinkLocalAddress(iid);
|
|
|
|
AddAddress(address);
|
|
}
|
|
|
|
void InfraIf::Reset(void) { mPendingTxQueue.DequeueAndFreeAll(); }
|
|
|
|
bool InfraIf::HasAddress(const Ip6::Address &aAddress) const { return mAddresses.Contains(aAddress); }
|
|
|
|
void InfraIf::AddAddress(const Ip6::Address &aAddress)
|
|
{
|
|
VerifyOrExit(!HasAddress(aAddress));
|
|
|
|
SuccessOrQuit(mAddresses.PushBack(aAddress));
|
|
Get<Mdns>().HandleHostAddressEvent(aAddress, /* aAdded */ true);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void InfraIf::RemoveAddress(const Ip6::Address &aAddress)
|
|
{
|
|
for (uint16_t index = 0; index < mAddresses.GetLength(); index++)
|
|
{
|
|
if (mAddresses[index] == aAddress)
|
|
{
|
|
mAddresses[index] = *mAddresses.Back();
|
|
mAddresses.PopBack();
|
|
Get<Mdns>().HandleHostAddressEvent(aAddress, /* aAdded */ false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void InfraIf::RemoveAllAddresses(void)
|
|
{
|
|
mAddresses.Clear();
|
|
Get<Mdns>().HandleHostAddressRemoveAll();
|
|
}
|
|
|
|
const Ip6::Address *InfraIf::FindAddress(const char *aPrefix) const
|
|
{
|
|
Ip6::Prefix prefix;
|
|
|
|
SuccessOrQuit(prefix.FromString(aPrefix));
|
|
return FindAddress(prefix);
|
|
}
|
|
|
|
const Ip6::Address *InfraIf::FindAddress(const Ip6::Prefix &aPrefix) const
|
|
{
|
|
const Ip6::Address *matchedAddress = nullptr;
|
|
|
|
for (const Ip6::Address &address : mAddresses)
|
|
{
|
|
if (address.MatchesPrefix(aPrefix))
|
|
{
|
|
matchedAddress = &address;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return matchedAddress;
|
|
}
|
|
|
|
const Ip6::Address &InfraIf::FindMatchingAddress(const char *aPrefix) const
|
|
{
|
|
const Ip6::Address *matchedAddress = FindAddress(aPrefix);
|
|
|
|
VerifyOrQuit(matchedAddress != nullptr, "no matching address found on infrastructure interface");
|
|
|
|
return *matchedAddress;
|
|
}
|
|
|
|
const Ip6::Address &InfraIf::SelectSourceAddress(const Ip6::Address &aDestination) const
|
|
{
|
|
const Ip6::Address *bestMatch = &GetLinkLocalAddress();
|
|
|
|
if (aDestination.IsLinkLocalUnicast())
|
|
{
|
|
// For link-local destinations, we always use the link-local address.
|
|
ExitNow();
|
|
}
|
|
|
|
for (const Ip6::Address &address : mAddresses)
|
|
{
|
|
if (address.IsMulticast())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (address.GetScope() == aDestination.GetScope())
|
|
{
|
|
bestMatch = &address;
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return *bestMatch;
|
|
}
|
|
|
|
void InfraIf::SendIcmp6Nd(const Ip6::Address &aDestAddress, const uint8_t *aBuffer, uint16_t aBufferLength)
|
|
{
|
|
Message *message = Get<MessagePool>().Allocate(Message::kTypeIp6);
|
|
Ip6::Header ip6Header;
|
|
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
ip6Header.InitVersionTrafficClassFlow();
|
|
ip6Header.SetPayloadLength(aBufferLength);
|
|
ip6Header.SetNextHeader(Ip6::kProtoIcmp6);
|
|
ip6Header.SetHopLimit(255);
|
|
ip6Header.SetSource(GetLinkLocalAddress());
|
|
ip6Header.SetDestination(aDestAddress);
|
|
|
|
SuccessOrQuit(message->Append(ip6Header));
|
|
SuccessOrQuit(message->AppendBytes(aBuffer, aBufferLength));
|
|
|
|
// Calculate ICMPv6 checksum
|
|
message->SetOffset(sizeof(Ip6::Header));
|
|
Checksum::UpdateMessageChecksum(*message, ip6Header.GetSource(), ip6Header.GetDestination(), Ip6::kProtoIcmp6);
|
|
|
|
EnqueueTxPacket(*message);
|
|
}
|
|
|
|
void InfraIf::EnqueueTxPacket(Message &aMessage)
|
|
{
|
|
mPendingTxQueue.Enqueue(aMessage);
|
|
Core::Get().MarkPendingAction();
|
|
}
|
|
|
|
void InfraIf::SendRouterAdvertisement(const Ip6::Address &aDestination,
|
|
const Ip6::Prefix *aPioPrefix,
|
|
const Ip6::Prefix *aRioPrefix)
|
|
{
|
|
Ip6::Nd::RouterAdvert::TxMessage ra;
|
|
Ip6::Nd::RouterAdvert::Header header;
|
|
Ip6::Nd::Icmp6Packet packet;
|
|
|
|
header.SetToDefault();
|
|
header.SetRouterLifetime(1800);
|
|
SuccessOrQuit(ra.Append(header));
|
|
|
|
if (aPioPrefix != nullptr)
|
|
{
|
|
SuccessOrQuit(ra.AppendPrefixInfoOption(*aPioPrefix, 1800, 1800,
|
|
Ip6::Nd::PrefixInfoOption::kOnLinkFlag |
|
|
Ip6::Nd::PrefixInfoOption::kAutoConfigFlag));
|
|
}
|
|
|
|
if (aRioPrefix != nullptr)
|
|
{
|
|
SuccessOrQuit(ra.AppendRouteInfoOption(*aRioPrefix, 1800, NetworkData::kRoutePreferenceMedium));
|
|
}
|
|
|
|
// ETH_2 (Node 4) acts as DNS server
|
|
if (mIsDnsServer)
|
|
{
|
|
const Ip6::Address dnsServerAddr = GetLinkLocalAddress();
|
|
SuccessOrQuit(ra.AppendRecursiveDnsServerOption(&dnsServerAddr, 1, 1800));
|
|
}
|
|
|
|
ra.GetAsPacket(packet);
|
|
|
|
SendIcmp6Nd(aDestination, packet.GetBytes(), packet.GetLength());
|
|
}
|
|
|
|
void InfraIf::StartRouterAdvertisement(const Ip6::Prefix &aPioPrefix, const Ip6::Prefix *aRioPrefix)
|
|
{
|
|
mPioPrefix = aPioPrefix;
|
|
if (aRioPrefix != nullptr)
|
|
{
|
|
mRioPrefix = *aRioPrefix;
|
|
mHasRioPrefix = true;
|
|
}
|
|
else
|
|
{
|
|
mHasRioPrefix = false;
|
|
}
|
|
|
|
// Trigger initial RA immediately
|
|
SendPeriodicRouterAdvertisement();
|
|
}
|
|
|
|
void InfraIf::StopRouterAdvertisement(void) { mRaTimer.Stop(); }
|
|
|
|
void InfraIf::HandleRaTimer(void) { SendPeriodicRouterAdvertisement(); }
|
|
|
|
void InfraIf::SendPeriodicRouterAdvertisement(void)
|
|
{
|
|
const uint32_t kRaInterval = 4000; // 4 seconds in milliseconds
|
|
|
|
SendRouterAdvertisement(Ip6::Address::GetLinkLocalAllNodesMulticast(), &mPioPrefix,
|
|
mHasRioPrefix ? &mRioPrefix : nullptr);
|
|
mRaTimer.Start(kRaInterval);
|
|
}
|
|
|
|
void InfraIf::ProcessIcmp6Nd(const Ip6::Address &aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength)
|
|
{
|
|
Ip6::Nd::Icmp6Packet packet;
|
|
|
|
packet.Init(aBuffer, aBufferLength);
|
|
|
|
VerifyOrExit(packet.GetLength() >= sizeof(Ip6::Icmp6Header));
|
|
|
|
switch (reinterpret_cast<const Ip6::Icmp6Header *>(packet.GetBytes())->GetType())
|
|
{
|
|
case Ip6::Icmp6Header::kTypeRouterAdvert:
|
|
{
|
|
Ip6::Nd::RouterAdvert::RxMessage raMessage(packet);
|
|
|
|
VerifyOrExit(raMessage.IsValid());
|
|
|
|
for (const Ip6::Nd::Option &option : raMessage)
|
|
{
|
|
if (option.GetType() == Ip6::Nd::Option::kTypePrefixInfo)
|
|
{
|
|
HandlePrefixInfoOption(static_cast<const Ip6::Nd::PrefixInfoOption &>(option));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Ip6::Icmp6Header::kTypeRouterSolicit:
|
|
HandleRouterSolicitation(aSrcAddress);
|
|
break;
|
|
|
|
case Ip6::Icmp6Header::kTypeNeighborAdvert:
|
|
case Ip6::Icmp6Header::kTypeNeighborSolicit:
|
|
// TODO: Handle other ND messages as needed for the simulation.
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void InfraIf::HandlePrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio)
|
|
{
|
|
Ip6::Prefix prefix;
|
|
Ip6::Address address;
|
|
LinkLayerAddress mac;
|
|
|
|
VerifyOrExit(aPio.IsAutoAddrConfigFlagSet());
|
|
|
|
aPio.GetPrefix(prefix);
|
|
|
|
// Generate address using SLAAC (EUI-64 style for simplicity in test)
|
|
|
|
address = AsCoreType(&prefix.mPrefix);
|
|
GetLinkLayerAddress(mac);
|
|
SuccessOrQuit(mac.ConvertToIid(address.GetIid()));
|
|
|
|
if (aPio.GetValidLifetime() == 0)
|
|
{
|
|
if (HasAddress(address))
|
|
{
|
|
RemoveAddress(address);
|
|
Log("Node %lu (%s) removed address %s from RA (lifetime 0)", ToUlong(GetInstance().GetId()),
|
|
Get<Node>().GetName(), address.ToString().AsCString());
|
|
}
|
|
ExitNow();
|
|
}
|
|
|
|
AddAddress(address);
|
|
Log("Node %lu (%s) auto-configured address %s from RA", ToUlong(GetInstance().GetId()), Get<Node>().GetName(),
|
|
address.ToString().AsCString());
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void InfraIf::HandleRouterSolicitation(const Ip6::Address &aSrcAddress)
|
|
{
|
|
if (mRaTimer.IsRunning())
|
|
{
|
|
const Ip6::Address &dest =
|
|
aSrcAddress.IsUnspecified() ? Ip6::Address::GetLinkLocalAllNodesMulticast() : aSrcAddress;
|
|
SendRouterAdvertisement(dest, &mPioPrefix, mHasRioPrefix ? &mRioPrefix : nullptr);
|
|
}
|
|
}
|
|
|
|
void InfraIf::SendIp6(const Ip6::Header &aHeader, OwnedPtr<Message> aMessagePtr)
|
|
{
|
|
VerifyOrQuit(aMessagePtr != nullptr);
|
|
|
|
Log("InfraIf::SendIp6 from %s to %s (len:%u)", aHeader.GetSource().ToString().AsCString(),
|
|
aHeader.GetDestination().ToString().AsCString(), aMessagePtr->GetLength());
|
|
|
|
EnqueueTxPacket(*aMessagePtr.Release());
|
|
}
|
|
|
|
void InfraIf::SendEchoRequest(const Ip6::Address &aSrcAddress,
|
|
const Ip6::Address &aDestAddress,
|
|
uint16_t aIdentifier,
|
|
uint16_t aPayloadSize,
|
|
uint8_t aHopLimit)
|
|
{
|
|
Message *message;
|
|
Ip6::Header ip6Header;
|
|
Ip6::Icmp6Header icmpHeader;
|
|
|
|
message = Get<Ip6::Ip6>().NewMessage();
|
|
VerifyOrQuit(message != nullptr);
|
|
|
|
ip6Header.Clear();
|
|
ip6Header.InitVersionTrafficClassFlow();
|
|
ip6Header.SetPayloadLength(sizeof(Ip6::Icmp6Header) + aPayloadSize);
|
|
ip6Header.SetNextHeader(Ip6::kProtoIcmp6);
|
|
ip6Header.SetHopLimit(aHopLimit);
|
|
ip6Header.SetSource(aSrcAddress);
|
|
ip6Header.SetDestination(aDestAddress);
|
|
|
|
icmpHeader.Clear();
|
|
icmpHeader.SetType(Ip6::Icmp6Header::kTypeEchoRequest);
|
|
icmpHeader.SetCode(static_cast<Ip6::Icmp6Header::Code>(0));
|
|
icmpHeader.SetId(aIdentifier);
|
|
icmpHeader.SetSequence(0);
|
|
|
|
SuccessOrQuit(message->Append(icmpHeader));
|
|
SuccessOrQuit(message->IncreaseLength(aPayloadSize));
|
|
|
|
Checksum::UpdateMessageChecksum(*message, aSrcAddress, aDestAddress, Ip6::kProtoIcmp6);
|
|
|
|
SuccessOrQuit(message->Prepend(ip6Header));
|
|
|
|
EnqueueTxPacket(*message);
|
|
}
|
|
|
|
void InfraIf::SendUdp(const Ip6::Address &aSrcAddress,
|
|
const Ip6::Address &aDestAddress,
|
|
uint16_t aSourcePort,
|
|
uint16_t aDestPort,
|
|
Message &aPayload)
|
|
{
|
|
Ip6::Header ip6Header;
|
|
Ip6::UdpHeader udpHeader;
|
|
|
|
ip6Header.Clear();
|
|
ip6Header.InitVersionTrafficClassFlow();
|
|
ip6Header.SetPayloadLength(sizeof(Ip6::UdpHeader) + aPayload.GetLength());
|
|
ip6Header.SetNextHeader(Ip6::kProtoUdp);
|
|
ip6Header.SetHopLimit(64);
|
|
ip6Header.SetSource(aSrcAddress);
|
|
ip6Header.SetDestination(aDestAddress);
|
|
|
|
udpHeader.SetSourcePort(aSourcePort);
|
|
udpHeader.SetDestinationPort(aDestPort);
|
|
udpHeader.SetLength(sizeof(Ip6::UdpHeader) + aPayload.GetLength());
|
|
udpHeader.SetChecksum(0);
|
|
|
|
SuccessOrQuit(aPayload.Prepend(udpHeader));
|
|
aPayload.SetOffset(0);
|
|
Checksum::UpdateMessageChecksum(aPayload, aSrcAddress, aDestAddress, Ip6::kProtoUdp);
|
|
SuccessOrQuit(aPayload.Prepend(ip6Header));
|
|
aPayload.SetOffset(0);
|
|
|
|
if (aDestAddress.IsMulticast())
|
|
{
|
|
Message *loopbackMessage = aPayload.Clone<kNoReservedHeader>();
|
|
|
|
VerifyOrQuit(loopbackMessage != nullptr);
|
|
Receive(*loopbackMessage);
|
|
loopbackMessage->Free();
|
|
}
|
|
|
|
EnqueueTxPacket(aPayload);
|
|
}
|
|
|
|
void InfraIf::SetDhcp6ListeningEnabled(bool aEnable) { mDhcp6PdListening = aEnable; }
|
|
|
|
void InfraIf::SendDhcp6(Message &aMessage, const Ip6::Address &aDestAddress)
|
|
{
|
|
SendUdp(SelectSourceAddress(aDestAddress), aDestAddress, Dhcp6::kDhcpClientPort, Dhcp6::kDhcpServerPort, aMessage);
|
|
}
|
|
|
|
void InfraIf::Receive(Message &aMessage)
|
|
{
|
|
Ip6::Headers headers;
|
|
|
|
aMessage.SetOffset(0);
|
|
SuccessOrExit(headers.ParseFrom(aMessage));
|
|
|
|
if (mDhcp6PdListening && headers.IsUdp() && headers.GetDestinationPort() == Dhcp6::kDhcpClientPort)
|
|
{
|
|
Message *payload = aMessage.Clone<kNoReservedHeader>();
|
|
|
|
VerifyOrQuit(payload != nullptr);
|
|
payload->RemoveHeader(sizeof(Ip6::Header) + sizeof(Ip6::UdpHeader));
|
|
otPlatInfraIfDhcp6PdClientHandleReceived(&GetInstance(), payload, mIfIndex);
|
|
ExitNow();
|
|
}
|
|
|
|
if (headers.IsIcmp6() && (headers.GetDestinationAddress() == Ip6::Address::GetLinkLocalAllNodesMulticast() ||
|
|
headers.GetDestinationAddress() == Ip6::Address::GetLinkLocalAllRoutersMulticast() ||
|
|
HasAddress(headers.GetDestinationAddress())))
|
|
{
|
|
switch (headers.GetIcmpHeader().GetType())
|
|
{
|
|
case Ip6::Icmp6Header::kTypeRouterAdvert:
|
|
case Ip6::Icmp6Header::kTypeRouterSolicit:
|
|
case Ip6::Icmp6Header::kTypeNeighborAdvert:
|
|
case Ip6::Icmp6Header::kTypeNeighborSolicit:
|
|
{
|
|
Heap::Data payload;
|
|
uint16_t offset = sizeof(Ip6::Header);
|
|
|
|
SuccessOrQuit(payload.SetFrom(aMessage, offset, aMessage.GetLength() - offset));
|
|
|
|
otPlatInfraIfRecvIcmp6Nd(&GetInstance(), mIfIndex, &headers.GetSourceAddress(), payload.GetBytes(),
|
|
payload.GetLength());
|
|
Get<InfraIf>().ProcessIcmp6Nd(headers.GetSourceAddress(), payload.GetBytes(), payload.GetLength());
|
|
ExitNow();
|
|
}
|
|
|
|
case Ip6::Icmp6Header::kTypeEchoRequest:
|
|
HandleEchoRequest(headers.GetIp6Header(), aMessage);
|
|
ExitNow();
|
|
|
|
case Ip6::Icmp6Header::kTypeEchoReply:
|
|
HandleEchoReply(headers.GetIp6Header(), aMessage);
|
|
ExitNow();
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (headers.IsUdp() && headers.GetDestinationPort() == Mdns::kUdpPort)
|
|
{
|
|
if (headers.GetDestinationAddress().IsMulticast() || HasAddress(headers.GetDestinationAddress()))
|
|
{
|
|
Mdns::AddressInfo senderAddress;
|
|
Message *payload = aMessage.Clone<kNoReservedHeader>();
|
|
|
|
VerifyOrQuit(payload != nullptr);
|
|
payload->RemoveHeader(sizeof(Ip6::Header) + sizeof(Ip6::UdpHeader));
|
|
|
|
senderAddress.mAddress = headers.GetSourceAddress();
|
|
senderAddress.mPort = headers.GetSourcePort();
|
|
senderAddress.mInfraIfIndex = Mdns::kInfraIfIndex;
|
|
|
|
Get<Mdns>().Receive(GetInstance(), *payload, !headers.GetDestinationAddress().IsMulticast(), senderAddress);
|
|
payload->Free();
|
|
}
|
|
ExitNow();
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
|
|
if (headers.IsUdp() && headers.GetDestinationPort() == Get<Trel>().mUdpPort && Get<Trel>().mEnabled)
|
|
{
|
|
if (headers.GetDestinationAddress().IsMulticast() || HasAddress(headers.GetDestinationAddress()))
|
|
{
|
|
Ip6::SockAddr senderAddr;
|
|
Heap::Data payload;
|
|
uint16_t offset = sizeof(Ip6::Header) + sizeof(Ip6::UdpHeader);
|
|
|
|
senderAddr.SetAddress(headers.GetSourceAddress());
|
|
senderAddr.SetPort(headers.GetSourcePort());
|
|
|
|
SuccessOrQuit(
|
|
payload.SetFrom(aMessage, offset, headers.GetUdpHeader().GetLength() - sizeof(Ip6::UdpHeader)));
|
|
Get<Trel>().Receive(GetInstance(), payload, senderAddr);
|
|
}
|
|
ExitNow();
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
|
|
if (headers.IsUdp() && headers.GetDestinationPort() == UpstreamDns::kDnsPort)
|
|
{
|
|
aMessage.SetOffset(sizeof(Ip6::Header) + sizeof(Ip6::UdpHeader));
|
|
if (Get<UpstreamDns>().HandleUpstreamDnsResponse(headers.GetSourceAddress(), aMessage))
|
|
{
|
|
ExitNow();
|
|
}
|
|
aMessage.SetOffset(0);
|
|
}
|
|
#endif
|
|
|
|
if (headers.IsUdp() && Get<Udp>().HandleReceive(aMessage, headers))
|
|
{
|
|
ExitNow();
|
|
}
|
|
|
|
if (headers.IsUdp() && mUdpHook != nullptr)
|
|
{
|
|
Ip6::MessageInfo messageInfo;
|
|
messageInfo.SetPeerAddr(headers.GetSourceAddress());
|
|
messageInfo.SetPeerPort(headers.GetSourcePort());
|
|
messageInfo.SetSockAddr(headers.GetDestinationAddress());
|
|
messageInfo.SetSockPort(headers.GetDestinationPort());
|
|
|
|
aMessage.SetOffset(sizeof(Ip6::Header) + sizeof(Ip6::UdpHeader));
|
|
if (mUdpHook(GetInstance(), aMessage, messageInfo))
|
|
{
|
|
ExitNow();
|
|
}
|
|
aMessage.SetOffset(0);
|
|
}
|
|
|
|
{
|
|
// We also deliver generic IPv6 packets to the stack if they are NOT ICMPv6 ND packets.
|
|
// (ND packets were already delivered via otPlatInfraIfRecvIcmp6Nd above).
|
|
OwnedPtr<Message> messagePtr;
|
|
Ip6::Header updatedHeader = headers.GetIp6Header();
|
|
|
|
VerifyOrExit(!HasAddress(headers.GetSourceAddress()));
|
|
VerifyOrExit(!Get<NetworkData::Leader>().IsOnMesh(headers.GetSourceAddress()));
|
|
|
|
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
|
|
if (headers.GetDestinationAddress().IsMulticastLargerThanRealmLocal())
|
|
{
|
|
VerifyOrExit(Get<BackboneRouter::Local>().IsPrimary());
|
|
VerifyOrExit(Get<BackboneRouter::MulticastListenersTable>().Has(headers.GetDestinationAddress()));
|
|
}
|
|
#endif
|
|
|
|
VerifyOrExit(updatedHeader.GetHopLimit() > 1);
|
|
updatedHeader.SetHopLimit(updatedHeader.GetHopLimit() - 1);
|
|
|
|
messagePtr.Reset(aMessage.Clone<kNoReservedHeader>());
|
|
VerifyOrQuit(messagePtr != nullptr);
|
|
|
|
messagePtr->Write(0, updatedHeader);
|
|
messagePtr->SetOrigin(Message::kOriginHostUntrusted);
|
|
messagePtr->SetLoopbackToHostAllowed(false);
|
|
|
|
IgnoreError(Get<Ip6::Ip6>().SendRaw(messagePtr.PassOwnership()));
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void InfraIf::HandleEchoRequest(const Ip6::Header &aHeader, Message &aMessage)
|
|
{
|
|
Message *replyMessage;
|
|
Ip6::Header replyHeader;
|
|
Ip6::Icmp6Header replyIcmp;
|
|
uint16_t payloadLen = aMessage.GetLength() - sizeof(Ip6::Header);
|
|
|
|
replyMessage = Get<MessagePool>().Allocate(Message::kTypeIp6);
|
|
VerifyOrQuit(replyMessage != nullptr);
|
|
|
|
SuccessOrQuit(replyMessage->SetLength(payloadLen));
|
|
replyMessage->WriteBytesFromMessage(0, aMessage, sizeof(Ip6::Header), payloadLen);
|
|
|
|
SuccessOrQuit(replyMessage->Read(0, replyIcmp));
|
|
|
|
replyHeader.InitVersionTrafficClassFlow();
|
|
replyHeader.SetPayloadLength(payloadLen);
|
|
replyHeader.SetNextHeader(Ip6::kProtoIcmp6);
|
|
replyHeader.SetHopLimit(64);
|
|
replyHeader.SetSource(aHeader.GetDestination());
|
|
replyHeader.SetDestination(aHeader.GetSource());
|
|
|
|
replyIcmp.SetType(Ip6::Icmp6Header::kTypeEchoReply);
|
|
replyMessage->Write(0, replyIcmp);
|
|
|
|
// Recalculate ICMPv6 checksum
|
|
replyMessage->SetOffset(0);
|
|
Checksum::UpdateMessageChecksum(*replyMessage, replyHeader.GetSource(), replyHeader.GetDestination(),
|
|
Ip6::kProtoIcmp6);
|
|
|
|
SuccessOrQuit(replyMessage->Prepend(replyHeader));
|
|
|
|
EnqueueTxPacket(*replyMessage);
|
|
}
|
|
|
|
void InfraIf::HandleEchoReply(const Ip6::Header &aHeader, Message &aMessage)
|
|
{
|
|
Ip6::Icmp6Header icmpHeader;
|
|
|
|
SuccessOrQuit(aMessage.Read(sizeof(Ip6::Header), icmpHeader));
|
|
mEchoReplyCallback.InvokeIfSet(aHeader.GetSource(), icmpHeader.GetId(), icmpHeader.GetSequence());
|
|
}
|
|
|
|
void InfraIf::GetLinkLayerAddress(LinkLayerAddress &aLinkLayerAddress) const
|
|
{
|
|
// Use a unique MAC address based on Node ID
|
|
ClearAllBytes(aLinkLayerAddress);
|
|
aLinkLayerAddress.mLength = 6;
|
|
aLinkLayerAddress.mAddress[0] = 0x02;
|
|
BigEndian::WriteUint32(GetInstance().GetId(), &aLinkLayerAddress.mAddress[2]);
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
bool otPlatInfraIfHasAddress(otInstance *aInstance, uint32_t aInfraIfIndex, const otIp6Address *aAddress)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInfraIfIndex);
|
|
|
|
return AsNode(aInstance).mInfraIf.HasAddress(AsCoreType(aAddress));
|
|
}
|
|
|
|
otError otPlatInfraIfSendIcmp6Nd(otInstance *aInstance,
|
|
uint32_t aInfraIfIndex,
|
|
const otIp6Address *aDestAddress,
|
|
const uint8_t *aBuffer,
|
|
uint16_t aBufferLength)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInfraIfIndex);
|
|
|
|
AsNode(aInstance).mInfraIf.SendIcmp6Nd(AsCoreType(aDestAddress), aBuffer, aBufferLength);
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatInfraIfDiscoverNat64Prefix(otInstance *aInstance, uint32_t aInfraIfIndex)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
OT_UNUSED_VARIABLE(aInfraIfIndex);
|
|
|
|
return OT_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
otError otPlatGetInfraIfLinkLayerAddress(otInstance *aInstance,
|
|
uint32_t aInfraIfIndex,
|
|
otPlatInfraIfLinkLayerAddress *aLinkLayerAddress)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInfraIfIndex);
|
|
|
|
AsNode(aInstance).mInfraIf.GetLinkLayerAddress(AsCoreType(aLinkLayerAddress));
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
void otPlatInfraIfDhcp6PdClientSetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInfraIfIndex);
|
|
AsNode(aInstance).mInfraIf.SetDhcp6ListeningEnabled(aEnable);
|
|
}
|
|
|
|
void otPlatInfraIfDhcp6PdClientSend(otInstance *aInstance, otMessage *aMessage, otIp6Address *aDest, uint32_t aIfIndex)
|
|
{
|
|
OT_UNUSED_VARIABLE(aIfIndex);
|
|
AsNode(aInstance).mInfraIf.SendDhcp6(AsCoreType(aMessage), AsCoreType(aDest));
|
|
}
|
|
|
|
} // extern "C"
|
|
|
|
} // namespace Nexus
|
|
} // namespace ot
|