Files
Yakun Xu 0297ea0a0a [nexus] mark pending action when enqueuing packets on infra interface (#13699)
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().
2026-10-10 00:22:36 -07:00

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