mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
[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:
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user