[nexus] implement infra interface for backbone simulation (#12683)

Implement the InfraIf class and associated platform logic to simulate
a shared infrastructure link (backbone) between Border Routers and
external hosts within the Nexus simulation environment.

Infra interface simulation:
- Implement shared Ethernet-like link for IPv6 traffic delivery.
- Add automated SLAAC address configuration based on ICMPv6 RAs.
- Support sending/receiving ICMPv6 Neighbor Discovery (RS, RA, NS, NA).
- Implement manual ICMPv6 checksum calculation for raw packets.
- Add infrastructure-level loop prevention and destination filtering.
- Provide helper methods to find nodes by infrastructure addresses.

Platform integration:
- Implement otPlatInfraIf APIs and integrate with otInstance.
- Use native Message and MessageQueue for pending traffic management.
- Add support for custom test variables in SaveTestInfo() JSON output.
- Update Node::Reset() to properly clear pending infra interface tasks.

Core enhancements:
- Add MulticastListenersTable::Has() to check for address presence.
- Add PrefixInfoOption::GetPrefixLength() and SetPrefixLength().
- Enable MLR and Backbone Router multicast routing in Nexus config.
This commit is contained in:
Jonathan Hui
2026-03-15 02:39:57 -05:00
committed by GitHub
parent 0b7427884e
commit 5df29f74e1
12 changed files with 711 additions and 15 deletions
@@ -130,6 +130,23 @@ void MulticastListenersTable::Expire(void)
CheckInvariants();
}
bool MulticastListenersTable::Has(const Ip6::Address &aAddress) const
{
bool has = false;
for (uint16_t i = 0; i < mNumValidListeners; i++)
{
if (mListeners[i].GetAddress() == aAddress)
{
has = true;
ExitNow();
}
}
exit:
return has;
}
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
void MulticastListenersTable::Log(Action aAction,
const Ip6::Address &aAddress,
@@ -148,6 +148,16 @@ public:
*/
uint16_t Count(void) const { return mNumValidListeners; }
/**
* Indicates whether or not the Multicast Listeners Table contains the given address.
*
* @param[in] aAddress The Multicast Listener address.
*
* @retval True If the Multicast Listeners Table contains the given address.
* @retval False If the Multicast Listeners Table does not contain the given address.
*/
bool Has(const Ip6::Address &aAddress) const;
/**
* Enables range-based `for` loop iteration over all Multicast Listeners.
*
+14
View File
@@ -279,6 +279,20 @@ public:
*/
uint32_t GetPreferredLifetime(void) const { return BigEndian::HostSwap32(mPreferredLifetime); }
/**
* Returns the prefix length (in bits).
*
* @returns The prefix length (in bits).
*/
uint8_t GetPrefixLength(void) const { return mPrefixLength; }
/**
* Sets the prefix length (in bits).
*
* @param[in] aPrefixLength The prefix length (in bits).
*/
void SetPrefixLength(uint8_t aPrefixLength) { mPrefixLength = aPrefixLength; }
/**
* Sets the prefix.
*
@@ -82,6 +82,7 @@
#define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 1
#define OPENTHREAD_CONFIG_JOINER_ENABLE 1
#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO
#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 1
#define OPENTHREAD_CONFIG_LOG_LEVEL_INIT OT_LOG_LEVEL_CRIT
#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED
@@ -102,6 +103,8 @@
#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE 1
#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE 1
#define OPENTHREAD_CONFIG_MLE_MAX_CHILDREN 128
#define OPENTHREAD_CONFIG_MLR_ENABLE 1
#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE 1
#define OPENTHREAD_CONFIG_MULTICAST_DNS_AUTO_ENABLE_ON_INFRA_IF 1
#define OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE 1
#define OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE 1
+81 -2
View File
@@ -28,8 +28,8 @@
#include "nexus_core.hpp"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "mac_frame.h"
#include "nexus_node.hpp"
@@ -224,7 +224,13 @@ void Core::SaveTestInfo(const char *aFilename, Node *aLeaderNode)
{
Ip6::Prefix prefix;
prefix.Set(leaderNode->Get<Mle::Mle>().GetMeshLocalPrefix());
fprintf(file, " \"mesh_local_prefix\": \"%s\"\n", prefix.ToString().AsCString());
fprintf(file, " \"mesh_local_prefix\": \"%s\"%s\n", prefix.ToString().AsCString(),
mTestVars.IsEmpty() ? "" : ",");
}
for (const TestVar &var : mTestVars)
{
fprintf(file, " \"%s\": \"%s\"%s\n", var.mName.AsCString(), var.mValue.AsCString(),
(&var == mTestVars.Back()) ? "" : ",");
}
fprintf(file, " }\n");
@@ -239,6 +245,14 @@ exit:
void Core::AddNetworkKey(const NetworkKey &aKey) { SuccessOrQuit(mNetworkKeys.PushBack(aKey)); }
void Core::AddTestVar(const char *aName, const char *aValue)
{
TestVar *var = mTestVars.PushBack();
VerifyOrQuit(var != nullptr);
var->mName.Clear().Append("%s", aName);
var->mValue.Clear().Append("%s", aValue);
}
Core::~Core(void) { sInUse = false; }
Node &Core::CreateNode(void)
@@ -250,10 +264,14 @@ Node &Core::CreateNode(void)
node->GetInstance().SetId(mCurNodeId++);
node->mInfraIf.Init(*node);
mNodes.Push(*node);
node->GetInstance().AfterInit();
otIp6SetReceiveCallback(&node->GetInstance(), Node::HandleIp6Receive, node);
return *node;
}
@@ -322,10 +340,13 @@ void Core::AdvanceTime(uint32_t aDuration)
void Core::Process(Node &aNode)
{
SetActiveNode(&aNode);
otTaskletsProcess(&aNode.GetInstance());
ProcessRadio(aNode);
ProcessMdns(aNode);
ProcessInfraIf(aNode);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
ProcessTrel(aNode);
#endif
@@ -341,6 +362,8 @@ void Core::Process(Node &aNode)
aNode.mAlarmMicro.mScheduled = false;
otPlatAlarmMicroFired(&aNode.GetInstance());
}
SetActiveNode(nullptr);
}
void Core::ProcessRadio(Node &aNode)
@@ -505,6 +528,62 @@ void Core::ProcessMdns(Node &aNode)
aNode.mMdns.mPendingTxList.Free();
}
void Core::ProcessInfraIf(Node &aNode)
{
// Deliver pending packets on the infrastructure interface.
Message *message;
while ((message = aNode.mInfraIf.mPendingTxQueue.GetHead()) != nullptr)
{
Ip6::Header header;
Node *targetNode = nullptr;
Heap::Data msgData;
aNode.mInfraIf.mPendingTxQueue.Dequeue(*message);
SuccessOrQuit(message->Read(0, header));
VerifyOrQuit(message->GetLength() >= sizeof(Ip6::Header) && header.IsVersion6());
SuccessOrQuit(msgData.SetFrom(*message, 0, message->GetLength()));
mPcap.WritePacket(msgData.GetBytes(), msgData.GetLength(), mNow);
if (!header.GetDestination().IsMulticast())
{
targetNode = FindNodeByInfraIfAddress(header.GetDestination());
}
for (Node &rxNode : mNodes)
{
if (targetNode != nullptr && &rxNode != targetNode)
{
continue;
}
rxNode.mInfraIf.Receive(aNode, header, *message);
}
message->Free();
}
}
Node *Core::FindNodeByInfraIfAddress(const Ip6::Address &aAddress)
{
Node *matchedNode = nullptr;
for (Node &node : mNodes)
{
if (node.mInfraIf.HasAddress(aAddress))
{
matchedNode = &node;
break;
}
}
return matchedNode;
}
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
void Core::ProcessTrel(Node &aNode)
+11
View File
@@ -65,6 +65,7 @@ public:
void SaveTestInfo(const char *aFilename, Node *aLeaderNode = nullptr);
void AddNetworkKey(const NetworkKey &aKey);
void AddTestVar(const char *aName, const char *aValue);
void SendAndVerifyEchoRequest(Node &aSender,
const Ip6::Address &aDestination,
uint16_t aPayloadSize = 0,
@@ -101,13 +102,22 @@ private:
bool mResponseReceived;
};
struct TestVar
{
String<32> mName;
String<64> mValue;
};
void Process(Node &aNode);
void ProcessRadio(Node &aNode);
void ProcessMdns(Node &aNode);
void ProcessInfraIf(Node &aNode);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
void ProcessTrel(Node &aNode);
#endif
Node *FindNodeByInfraIfAddress(const Ip6::Address &aAddress);
static void HandleIcmpResponse(void *aContext,
otMessage *aMessage,
const otMessageInfo *aMessageInfo,
@@ -119,6 +129,7 @@ private:
OwningList<Node> mNodes;
Pcap mPcap;
Array<NetworkKey, 16> mNetworkKeys;
Array<TestVar, 16> mTestVars;
uint16_t mCurNodeId;
bool mPendingAction;
uint64_t mNow;
+427 -5
View File
@@ -26,21 +26,443 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <openthread/platform/infra_if.h>
#include "nexus_infra_if.hpp"
#include "nexus_core.hpp"
#include "nexus_node.hpp"
namespace ot {
namespace Nexus {
InfraIf::InfraIf(void)
: mNode(nullptr)
, mNodeId(0)
, mIfIndex(0)
{
}
void InfraIf::Init(Node &aNode)
{
Ip6::Address address;
otPlatInfraIfLinkLayerAddress mac;
Ip6::InterfaceIdentifier iid;
mIfIndex = 1;
mNode = &aNode;
mNodeId = aNode.GetId();
GetLinkLayerAddress(mac);
iid.mFields.m8[0] = mac.mAddress[0] ^ 0x02;
iid.mFields.m8[1] = mac.mAddress[1];
iid.mFields.m8[2] = mac.mAddress[2];
iid.mFields.m8[3] = 0xff;
iid.mFields.m8[4] = 0xfe;
iid.mFields.m8[5] = mac.mAddress[3];
iid.mFields.m8[6] = mac.mAddress[4];
iid.mFields.m8[7] = mac.mAddress[5];
address.SetToLinkLocalAddress(iid);
AddAddress(address);
}
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));
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();
break;
}
}
}
const Ip6::Address *InfraIf::FindAddress(const char *aPrefix) const
{
Ip6::Prefix prefix;
const Ip6::Address *matchedAddress = nullptr;
SuccessOrQuit(prefix.FromString(aPrefix));
for (const Ip6::Address &address : mAddresses)
{
if (address.MatchesPrefix(prefix))
{
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;
}
void InfraIf::SendIcmp6Nd(const Ip6::Address &aDestAddress, const uint8_t *aBuffer, uint16_t aBufferLength)
{
Message *message = GetNode().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);
mPendingTxQueue.Enqueue(*message);
}
void InfraIf::ProcessIcmp6Nd(const Ip6::Address &aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength)
{
Ip6::Nd::Icmp6Packet packet;
OT_UNUSED_VARIABLE(aSrcAddress);
packet.Init(aBuffer, aBufferLength);
VerifyOrExit(packet.GetLength() >= sizeof(Ip6::Icmp::Header));
switch (reinterpret_cast<const Ip6::Icmp::Header *>(packet.GetBytes())->GetType())
{
case Ip6::Icmp::Header::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::Icmp::Header::kTypeRouterSolicit:
case Ip6::Icmp::Header::kTypeNeighborAdvert:
case Ip6::Icmp::Header::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;
otPlatInfraIfLinkLayerAddress mac;
VerifyOrExit(aPio.IsAutoAddrConfigFlagSet());
aPio.GetPrefix(prefix);
// Generate address using SLAAC (EUI-64 style for simplicity in test)
address = AsCoreType(&prefix.mPrefix);
GetLinkLayerAddress(mac);
address.mFields.m8[8] = mac.mAddress[0] ^ 0x02;
address.mFields.m8[9] = mac.mAddress[1];
address.mFields.m8[10] = mac.mAddress[2];
address.mFields.m8[11] = 0xff;
address.mFields.m8[12] = 0xfe;
address.mFields.m8[13] = mac.mAddress[3];
address.mFields.m8[14] = mac.mAddress[4];
address.mFields.m8[15] = mac.mAddress[5];
if (aPio.GetValidLifetime() == 0)
{
if (HasAddress(address))
{
RemoveAddress(address);
Log("Node %lu (%s) removed address %s from RA (lifetime 0)", ToUlong(GetNode().GetId()),
GetNode().GetName(), address.ToString().AsCString());
}
ExitNow();
}
AddAddress(address);
Log("Node %lu (%s) auto-configured address %s from RA", ToUlong(GetNode().GetId()), GetNode().GetName(),
address.ToString().AsCString());
exit:
return;
}
void InfraIf::SendIp6(const Ip6::Address &aSrcAddress,
const Ip6::Address &aDestAddress,
const uint8_t *aBuffer,
uint16_t aBufferLength)
{
Message *message = GetNode().Get<MessagePool>().Allocate(Message::kTypeIp6);
VerifyOrQuit(message != nullptr);
Log("InfraIf::SendIp6 from %s to %s (len:%u)", aSrcAddress.ToString().AsCString(),
aDestAddress.ToString().AsCString(), aBufferLength);
SuccessOrQuit(message->AppendBytes(aBuffer, aBufferLength));
mPendingTxQueue.Enqueue(*message);
}
void InfraIf::SendEchoRequest(const Ip6::Address &aSrcAddress,
const Ip6::Address &aDestAddress,
uint16_t aIdentifier,
uint16_t aPayloadSize)
{
Message *message;
Ip6::Header ip6Header;
Ip6::Icmp::Header icmpHeader;
message = GetNode().Get<MessagePool>().Allocate(Message::kTypeIp6);
VerifyOrQuit(message != nullptr);
ip6Header.Clear();
ip6Header.InitVersionTrafficClassFlow();
ip6Header.SetPayloadLength(sizeof(Ip6::Icmp::Header) + aPayloadSize);
ip6Header.SetNextHeader(Ip6::kProtoIcmp6);
ip6Header.SetHopLimit(64);
ip6Header.SetSource(aSrcAddress);
ip6Header.SetDestination(aDestAddress);
icmpHeader.Clear();
icmpHeader.SetType(Ip6::Icmp::Header::kTypeEchoRequest);
icmpHeader.SetCode(static_cast<Ip6::Icmp::Header::Code>(0));
icmpHeader.SetId(aIdentifier);
icmpHeader.SetSequence(0);
SuccessOrQuit(message->SetLength(aPayloadSize));
SuccessOrQuit(message->Prepend(icmpHeader));
message->SetOffset(0);
Checksum::UpdateMessageChecksum(*message, aSrcAddress, aDestAddress, Ip6::kProtoIcmp6);
SuccessOrQuit(message->Prepend(ip6Header));
mPendingTxQueue.Enqueue(*message);
}
void InfraIf::Receive(Node &aSrcNode, const Ip6::Header &aHeader, Message &aMessage)
{
Node &node = GetNode();
bool isIcmp6Nd = false;
bool isEchoRequest = false;
VerifyOrExit(!node.mInfraIf.HasAddress(aHeader.GetSource()));
VerifyOrExit(!node.Get<NetworkData::Leader>().IsOnMesh(aHeader.GetSource()));
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal())
{
VerifyOrExit(node.Get<BackboneRouter::Local>().IsPrimary());
VerifyOrExit(node.Get<BackboneRouter::MulticastListenersTable>().Has(aHeader.GetDestination()));
}
#endif
Core::Get().SetActiveNode(&node);
if (aHeader.GetNextHeader() == Ip6::kProtoIcmp6 &&
aMessage.GetLength() >= sizeof(Ip6::Header) + sizeof(Ip6::Icmp::Header) &&
(aHeader.GetDestination() == Ip6::Address::GetLinkLocalAllNodesMulticast() ||
aHeader.GetDestination() == Ip6::Address::GetLinkLocalAllRoutersMulticast() ||
node.mInfraIf.HasAddress(aHeader.GetDestination())))
{
Ip6::Icmp::Header icmpHeader;
SuccessOrQuit(aMessage.Read(sizeof(Ip6::Header), icmpHeader));
switch (icmpHeader.GetType())
{
case Ip6::Icmp::Header::kTypeRouterAdvert:
case Ip6::Icmp::Header::kTypeRouterSolicit:
case Ip6::Icmp::Header::kTypeNeighborAdvert:
case Ip6::Icmp::Header::kTypeNeighborSolicit:
isIcmp6Nd = true;
break;
case Ip6::Icmp::Header::kTypeEchoRequest:
isEchoRequest = true;
break;
default:
break;
}
}
if (isIcmp6Nd)
{
Heap::Data payload;
uint16_t offset = sizeof(Ip6::Header);
SuccessOrQuit(payload.SetFrom(aMessage, offset, aMessage.GetLength() - offset));
otPlatInfraIfRecvIcmp6Nd(&node.GetInstance(), mIfIndex,
reinterpret_cast<const otIp6Address *>(&aHeader.GetSource()), payload.GetBytes(),
payload.GetLength());
node.mInfraIf.ProcessIcmp6Nd(aHeader.GetSource(), payload.GetBytes(), payload.GetLength());
}
else if (isEchoRequest)
{
HandleEchoRequest(aHeader, aMessage);
}
else
{
// 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;
messagePtr.Reset(node.Get<Ip6::Ip6>().NewMessage());
VerifyOrQuit(messagePtr != nullptr);
SuccessOrQuit(messagePtr->AppendBytesFromMessage(aMessage, 0, aMessage.GetLength()));
messagePtr->SetOrigin(Message::kOriginHostUntrusted);
messagePtr->SetLoopbackToHostAllowed(false);
SuccessOrQuit(node.Get<Ip6::Ip6>().SendRaw(messagePtr.PassOwnership()));
}
Core::Get().SetActiveNode(&aSrcNode);
exit:
return;
}
void InfraIf::HandleEchoRequest(const Ip6::Header &aHeader, Message &aMessage)
{
Node &node = GetNode();
Message *replyMessage;
Ip6::Header replyHeader;
Ip6::Icmp::Header replyIcmp;
uint16_t payloadLen = aMessage.GetLength() - sizeof(Ip6::Header);
replyMessage = node.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::Icmp::Header::kTypeEchoReply);
replyMessage->Write(0, replyIcmp);
// Recalculate ICMPv6 checksum
replyMessage->SetOffset(0);
Checksum::UpdateMessageChecksum(*replyMessage, replyHeader.GetSource(), replyHeader.GetDestination(),
Ip6::kProtoIcmp6);
SuccessOrQuit(replyMessage->Prepend(replyHeader));
mPendingTxQueue.Enqueue(*replyMessage);
}
void InfraIf::GetLinkLayerAddress(otPlatInfraIfLinkLayerAddress &aLinkLayerAddress) const
{
// Use a unique MAC address based on Node ID
ClearAllBytes(aLinkLayerAddress);
aLinkLayerAddress.mLength = 6;
aLinkLayerAddress.mAddress[0] = 0x02;
aLinkLayerAddress.mAddress[5] = static_cast<uint8_t>(mNodeId);
}
Node &InfraIf::GetNode(void)
{
OT_ASSERT(mNode != nullptr);
return *mNode;
}
const Node &InfraIf::GetNode(void) const
{
OT_ASSERT(mNode != nullptr);
return *mNode;
}
extern "C" {
bool otPlatInfraIfHasAddress(otInstance *, uint32_t, const otIp6Address *) { return false; }
otError otPlatInfraIfSendIcmp6Nd(otInstance *, uint32_t, const otIp6Address *, const uint8_t *, uint16_t)
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 *, uint32_t) { return OT_ERROR_NOT_IMPLEMENTED; }
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(*aLinkLayerAddress);
return OT_ERROR_NONE;
}
} // extern "C"
+89
View File
@@ -0,0 +1,89 @@
/*
* 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.
*/
#ifndef OT_NEXUS_PLATFORM_NEXUS_INFRA_IF_HPP_
#define OT_NEXUS_PLATFORM_NEXUS_INFRA_IF_HPP_
#include <openthread/platform/infra_if.h>
#include "instance/instance.hpp"
namespace ot {
namespace Nexus {
class Node;
class InfraIf
{
public:
InfraIf(void);
void Init(Node &aNode);
bool IsInitialized(void) const { return mIfIndex != 0; }
bool HasAddress(const Ip6::Address &aAddress) const;
void AddAddress(const Ip6::Address &aAddress);
void RemoveAddress(const Ip6::Address &aAddress);
const Ip6::Address *FindAddress(const char *aPrefix) const;
const Ip6::Address &FindMatchingAddress(const char *aPrefix) const;
const Ip6::Address &GetLinkLocalAddress(void) const { return mAddresses[0]; }
void SendIcmp6Nd(const Ip6::Address &aDestAddress, const uint8_t *aBuffer, uint16_t aBufferLength);
void SendIp6(const Ip6::Address &aSrcAddress,
const Ip6::Address &aDestAddress,
const uint8_t *aBuffer,
uint16_t aBufferLength);
void SendEchoRequest(const Ip6::Address &aSrcAddress,
const Ip6::Address &aDestAddress,
uint16_t aIdentifier,
uint16_t aPayloadSize);
void Receive(Node &aSrcNode, const Ip6::Header &aHeader, Message &aMessage);
void GetLinkLayerAddress(otPlatInfraIfLinkLayerAddress &aLinkLayerAddress) const;
Node &GetNode(void);
const Node &GetNode(void) const;
MessageQueue mPendingTxQueue;
private:
void ProcessIcmp6Nd(const Ip6::Address &aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength);
void HandlePrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio);
void HandleEchoRequest(const Ip6::Header &aHeader, Message &aMessage);
Node *mNode;
uint32_t mNodeId;
uint32_t mIfIndex;
Heap::Array<Ip6::Address> mAddresses;
};
} // namespace Nexus
} // namespace ot
#endif // OT_NEXUS_PLATFORM_NEXUS_INFRA_IF_HPP_
+44 -2
View File
@@ -27,7 +27,6 @@
*/
#include "nexus_node.hpp"
#include "nexus_utils.hpp"
namespace ot {
namespace Nexus {
@@ -41,7 +40,10 @@ void Node::Reset(void)
mAlarmMilli.Reset();
mAlarmMicro.Reset();
mMdns.Reset();
mInfraIf.mPendingTxQueue.DequeueAndFreeAll();
mPendingTasklet = false;
otIp6SetReceiveCallback(&GetInstance(), HandleIp6Receive, &GetInstance());
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
mTrel.Reset();
#endif
@@ -109,7 +111,8 @@ void Node::UnallowList(Node &aNode) { Get<Mac::Filter>().RemoveAddress(aNode.Get
void Node::SendEchoRequest(const Ip6::Address &aDestination,
uint16_t aIdentifier,
uint16_t aPayloadSize,
uint8_t aHopLimit)
uint8_t aHopLimit,
const Ip6::Address *aSrcAddress)
{
Message *message;
Ip6::MessageInfo messageInfo;
@@ -122,6 +125,11 @@ void Node::SendEchoRequest(const Ip6::Address &aDestination,
messageInfo.SetPeerAddr(aDestination);
messageInfo.SetHopLimit(aHopLimit);
if (aSrcAddress != nullptr)
{
messageInfo.SetSockAddr(*aSrcAddress);
}
Log("Sending Echo Request from Node %lu (%s) to %s (payload-size:%u)", ToUlong(GetId()), GetName(),
aDestination.ToString().AsCString(), aPayloadSize);
@@ -130,6 +138,40 @@ void Node::SendEchoRequest(const Ip6::Address &aDestination,
void Node::SetName(const char *aPrefix, uint16_t aIndex) { mName.Clear().Append("%s_%u", aPrefix, aIndex); }
void Node::HandleIp6Receive(otMessage *aMessage, void *aContext)
{
static_cast<Node *>(aContext)->HandleReceive(aMessage);
}
void Node::HandleReceive(otMessage *aMessage)
{
uint16_t length = otMessageGetLength(aMessage);
uint8_t buffer[1500];
const Ip6::Header *header;
VerifyOrExit(length <= sizeof(buffer));
VerifyOrQuit(otMessageRead(aMessage, 0, buffer, length) == length);
VerifyOrExit(length >= sizeof(Ip6::Header));
header = reinterpret_cast<const Ip6::Header *>(buffer);
// Forward packets to InfraIf if they are intended for the backbone.
// We avoid forwarding link-local and realm-local scope packets.
VerifyOrExit(mInfraIf.IsInitialized());
VerifyOrExit(header->GetHopLimit() > 1);
VerifyOrExit(header->GetDestination().GetScope() > Ip6::Address::kRealmLocalScope);
Core::Get().SetActiveNode(this);
VerifyOrExit(Get<NetworkData::Leader>().IsOnMesh(header->GetSource()));
mInfraIf.SendIp6(header->GetSource(), header->GetDestination(), buffer, length);
exit:
otMessageFree(aMessage);
}
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
void Node::GetTrelSockAddr(Ip6::SockAddr &aSockAddr) const
{
+11 -3
View File
@@ -33,6 +33,7 @@
#include "nexus_alarm.hpp"
#include "nexus_core.hpp"
#include "nexus_infra_if.hpp"
#include "nexus_mdns.hpp"
#include "nexus_radio.hpp"
#include "nexus_settings.hpp"
@@ -49,6 +50,7 @@ public:
Alarm mAlarmMilli;
Alarm mAlarmMicro;
Mdns mMdns;
InfraIf mInfraIf;
Settings mSettings;
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
Trel mTrel;
@@ -62,7 +64,7 @@ protected:
}
};
class Node : public Platform, public Heap::Allocatable<Node>, public LinkedListEntry<Node>, private Instance
class Node : public Platform, public Heap::Allocatable<Node>, public LinkedListEntry<Node>, public Instance
{
friend class Heap::Allocatable<Node>;
@@ -88,9 +90,11 @@ public:
void AllowList(Node &aNode);
void UnallowList(Node &aNode);
void SendEchoRequest(const Ip6::Address &aDestination,
uint16_t aIdentifier,
uint16_t aIdentifier = 0,
uint16_t aPayloadSize = 0,
uint8_t aHopLimit = 64);
uint8_t aHopLimit = 64,
const Ip6::Address *aSrcAddress = nullptr);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
void GetTrelSockAddr(Ip6::SockAddr &aSockAddr) const;
#endif
@@ -122,8 +126,12 @@ public:
static Node &From(otInstance *aInstance) { return static_cast<Node &>(*aInstance); }
static void HandleIp6Receive(otMessage *aMessage, void *aContext);
void HandleReceive(otMessage *aMessage);
using Platform::mAlarmMicro;
using Platform::mAlarmMilli;
using Platform::mInfraIf;
using Platform::mMdns;
using Platform::mPendingTasklet;
using Platform::mRadio;
+1 -3
View File
@@ -26,10 +26,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <openthread/platform/alarm-milli.h>
#include "nexus_node.hpp"
#include "nexus_settings.hpp"
#include "nexus_node.hpp"
namespace ot {
namespace Nexus {
+3
View File
@@ -406,6 +406,9 @@ Message *PrepareIcmp6Message(Node &aNode,
SuccessOrQuit(message->Append<uint8_t>(static_cast<uint8_t>(i & 0xff)));
}
message->SetOffset(sizeof(Ip6::Header));
Checksum::UpdateMessageChecksum(*message, ip6Header.GetSource(), ip6Header.GetDestination(), Ip6::kProtoIcmp6);
return message;
}