3272 lines
116 KiB
C++
3272 lines
116 KiB
C++
/*
|
|
* Copyright (c) 2024 Tokushima University, Japan
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*
|
|
* Authors:
|
|
*
|
|
* Alberto Gallegos Ramonet <alramonet@is.tokushima-u.ac.jp>
|
|
* Ryo Okuda <c611901200@tokushima-u.ac.jp>
|
|
*/
|
|
|
|
#include "zigbee-nwk.h"
|
|
|
|
#include "zigbee-nwk-tables.h"
|
|
|
|
#include "ns3/log.h"
|
|
#include "ns3/packet.h"
|
|
#include "ns3/simulator.h"
|
|
|
|
using namespace ns3::lrwpan;
|
|
|
|
#undef NS_LOG_APPEND_CONTEXT
|
|
#define NS_LOG_APPEND_CONTEXT \
|
|
std::clog << "[" << m_nwkNetworkAddress << " | " << m_nwkIeeeAddress << "] ";
|
|
|
|
namespace ns3
|
|
{
|
|
namespace zigbee
|
|
{
|
|
|
|
NS_LOG_COMPONENT_DEFINE("ZigbeeNwk");
|
|
NS_OBJECT_ENSURE_REGISTERED(ZigbeeNwk);
|
|
|
|
TypeId
|
|
ZigbeeNwk::GetTypeId()
|
|
{
|
|
static TypeId tid =
|
|
TypeId("ns3::zigbee::ZigbeeNwk")
|
|
.SetParent<Object>()
|
|
.SetGroupName("Zigbee")
|
|
.AddConstructor<ZigbeeNwk>()
|
|
.AddAttribute("NwkcCoordinatorCapable",
|
|
"[Constant] Indicates whether the device is capable of becoming a"
|
|
"Zigbee coordinator.",
|
|
BooleanValue(true),
|
|
MakeBooleanAccessor(&ZigbeeNwk::m_nwkcCoordinatorCapable),
|
|
MakeBooleanChecker())
|
|
.AddAttribute("NwkcProtocolVersion",
|
|
"[Constant] The version of the Zigbee NWK protocol in"
|
|
"the device (placeholder)",
|
|
UintegerValue(0x02),
|
|
MakeUintegerAccessor(&ZigbeeNwk::m_nwkcProtocolVersion),
|
|
MakeUintegerChecker<uint8_t>())
|
|
.AddAttribute("NwkcRouteDiscoveryTime",
|
|
"[Constant] The duration until a route discovery expires",
|
|
TimeValue(MilliSeconds(0x2710)),
|
|
MakeTimeAccessor(&ZigbeeNwk::m_nwkcRouteDiscoveryTime),
|
|
MakeTimeChecker())
|
|
|
|
.AddAttribute("NwkcInitialRREQRetries",
|
|
"[Constant] The number of times the first broadcast transmission"
|
|
"of a RREQ cmd frame is retried.",
|
|
UintegerValue(0x03),
|
|
MakeUintegerAccessor(&ZigbeeNwk::m_nwkcInitialRREQRetries),
|
|
MakeUintegerChecker<uint8_t>())
|
|
.AddAttribute("NwkcRREQRetries",
|
|
"[Constant] The number of times the broadcast transmission of a"
|
|
"RREQ cmd frame is retried on relay by intermediate router or"
|
|
"coordinator.",
|
|
UintegerValue(0x02),
|
|
MakeUintegerAccessor(&ZigbeeNwk::m_nwkcRREQRetries),
|
|
MakeUintegerChecker<uint8_t>())
|
|
.AddAttribute("NwkcRREQRetryInterval",
|
|
"[Constant] The duration between retries of a broadcast RREQ "
|
|
"cmd frame.",
|
|
TimeValue(MilliSeconds(0xFE)),
|
|
MakeTimeAccessor(&ZigbeeNwk::m_nwkcRREQRetryInterval),
|
|
MakeTimeChecker())
|
|
.AddAttribute("NwkcMinRREQJitter",
|
|
"[Constant] The minimum jitter for broadcast retransmission "
|
|
"of a RREQ (msec)",
|
|
DoubleValue(2),
|
|
MakeDoubleAccessor(&ZigbeeNwk::m_nwkcMinRREQJitter),
|
|
MakeDoubleChecker<double>())
|
|
.AddAttribute("NwkcMaxRREQJitter",
|
|
"[Constant] The duration between retries of a broadcast RREQ (msec)",
|
|
DoubleValue(128),
|
|
MakeDoubleAccessor(&ZigbeeNwk::m_nwkcMaxRREQJitter),
|
|
MakeDoubleChecker<double>())
|
|
.AddAttribute("MaxPendingTxQueueSize",
|
|
"The maximum size of the table storing pending packets awaiting "
|
|
"to be transmitted after discovering a route to the destination.",
|
|
UintegerValue(10),
|
|
MakeUintegerAccessor(&ZigbeeNwk::m_maxPendingTxQueueSize),
|
|
MakeUintegerChecker<uint32_t>())
|
|
.AddTraceSource("RreqRetriesExhausted",
|
|
"Trace source indicating when a node has "
|
|
"reached the maximum allowed number of RREQ retries during a "
|
|
"route discovery request",
|
|
MakeTraceSourceAccessor(&ZigbeeNwk::m_rreqRetriesExhaustedTrace),
|
|
"ns3::zigbee::ZigbeeNwk::RreqRetriesExhaustedTracedCallback");
|
|
return tid;
|
|
}
|
|
|
|
ZigbeeNwk::ZigbeeNwk()
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::NotifyConstructionCompleted()
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
m_pendPrimitiveNwk = PendingPrimitiveNwk::NLDE_NLME_NONE;
|
|
m_uniformRandomVariable = CreateObject<UniformRandomVariable>();
|
|
m_uniformRandomVariable->SetAttribute("Min", DoubleValue(0.0));
|
|
m_uniformRandomVariable->SetAttribute("Max", DoubleValue(255.0));
|
|
|
|
m_scanEnergyThreshold = 127;
|
|
|
|
m_netFormParams = {};
|
|
m_netFormParamsGen = nullptr;
|
|
m_beaconPayload = nullptr;
|
|
m_nwkNetworkAddress = Mac16Address("ff:ff");
|
|
m_nwkPanId = 0xffff;
|
|
m_nwkExtendedPanId = 0xffffffffffffffff;
|
|
m_nwkCapabilityInformation = 0;
|
|
|
|
m_nwkStackProfile = ZIGBEE_PRO;
|
|
m_nwkAddrAlloc = STOCHASTIC_ALLOC;
|
|
m_nwkMaxDepth = 5;
|
|
m_nwkMaxChildren = 20;
|
|
m_nwkMaxRouters = 6;
|
|
m_nwkEndDeviceTimeoutDefault = 8;
|
|
m_nwkUseTreeRouting = false;
|
|
|
|
m_nwkReportConstantCost = false;
|
|
m_nwkSymLink = false;
|
|
|
|
m_nwkMaxBroadcastRetries = 0x03;
|
|
m_countRREQRetries = 0;
|
|
|
|
m_nwkIsConcentrator = false;
|
|
m_nwkConcentratorRadius = 5;
|
|
m_nwkConcentratorDiscoveryTime = 0x00;
|
|
|
|
// TODO, set according to equation 3.5.2.1?
|
|
m_nwkNetworkBroadcastDeliveryTime = Seconds(9);
|
|
|
|
m_nwkSequenceNumber = SequenceNumber8(m_uniformRandomVariable->GetValue());
|
|
m_routeRequestId = SequenceNumber8(m_uniformRandomVariable->GetValue());
|
|
m_macHandle = SequenceNumber8(m_uniformRandomVariable->GetValue());
|
|
m_txBufferMaxSize = 10;
|
|
|
|
m_rreqJitter = CreateObject<UniformRandomVariable>();
|
|
m_rreqJitter->SetAttribute("Min", DoubleValue(m_nwkcMinRREQJitter));
|
|
m_rreqJitter->SetAttribute("Max", DoubleValue(m_nwkcMaxRREQJitter));
|
|
|
|
m_routeExpiryTime = Seconds(255);
|
|
}
|
|
|
|
ZigbeeNwk::~ZigbeeNwk()
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::DoInitialize()
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
Object::DoInitialize();
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::DoDispose()
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
m_panIdTable.Dispose();
|
|
m_nwkNeighborTable.Dispose();
|
|
m_nwkRoutingTable.Dispose();
|
|
m_nwkRouteDiscoveryTable.Dispose();
|
|
m_rreqRetryTable.Dispose();
|
|
m_panIdTable.Dispose();
|
|
m_btt.Dispose();
|
|
|
|
DisposeTxPktBuffer();
|
|
DisposePendingTx();
|
|
|
|
m_nlmeDirectJoinConfirmCallback = MakeNullCallback<void, NlmeDirectJoinConfirmParams>();
|
|
m_nlmeJoinConfirmCallback = MakeNullCallback<void, NlmeJoinConfirmParams>();
|
|
m_nlmeJoinIndicationCallback = MakeNullCallback<void, NlmeJoinIndicationParams>();
|
|
m_nlmeNetworkDiscoveryConfirmCallback =
|
|
MakeNullCallback<void, NlmeNetworkDiscoveryConfirmParams>();
|
|
m_nlmeNetworkFormationConfirmCallback =
|
|
MakeNullCallback<void, NlmeNetworkFormationConfirmParams>();
|
|
m_nlmeRouteDiscoveryConfirmCallback = MakeNullCallback<void, NlmeRouteDiscoveryConfirmParams>();
|
|
m_nlmeStartRouterConfirmCallback = MakeNullCallback<void, NlmeStartRouterConfirmParams>();
|
|
|
|
m_nldeDataConfirmCallback = MakeNullCallback<void, NldeDataConfirmParams>();
|
|
m_nldeDataIndicationCallback = MakeNullCallback<void, NldeDataIndicationParams, Ptr<Packet>>();
|
|
|
|
m_mac = nullptr;
|
|
|
|
Object::DoDispose();
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SetMac(Ptr<LrWpanMacBase> mac)
|
|
{
|
|
m_mac = mac;
|
|
}
|
|
|
|
Ptr<LrWpanMacBase>
|
|
ZigbeeNwk::GetMac() const
|
|
{
|
|
return m_mac;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::PrintRoutingTable(Ptr<OutputStreamWrapper> stream) const
|
|
{
|
|
std::ostream* os = stream->GetStream();
|
|
std::ios oldState(nullptr);
|
|
oldState.copyfmt(*os);
|
|
|
|
std::ostringstream nwkAddr;
|
|
std::ostringstream ieeeAddr;
|
|
|
|
nwkAddr << m_nwkNetworkAddress;
|
|
ieeeAddr << m_nwkIeeeAddress;
|
|
|
|
*os << std::resetiosflags(std::ios::adjustfield) << std::setiosflags(std::ios::left);
|
|
*os << "[" << ieeeAddr.str() << " | " << nwkAddr.str() << "] | ";
|
|
*os << "Time: " << Simulator::Now().As(Time::S) << " | ";
|
|
m_nwkRoutingTable.Print(stream);
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::PrintRouteDiscoveryTable(Ptr<OutputStreamWrapper> stream)
|
|
{
|
|
std::ostream* os = stream->GetStream();
|
|
std::ios oldState(nullptr);
|
|
oldState.copyfmt(*os);
|
|
|
|
std::ostringstream nwkAddr;
|
|
std::ostringstream ieeeAddr;
|
|
|
|
nwkAddr << m_nwkNetworkAddress;
|
|
ieeeAddr << m_nwkIeeeAddress;
|
|
|
|
*os << std::resetiosflags(std::ios::adjustfield) << std::setiosflags(std::ios::left);
|
|
*os << "[" << ieeeAddr.str() << " | " << nwkAddr.str() << "] | ";
|
|
*os << "Time: " << Simulator::Now().As(Time::S) << " | ";
|
|
m_nwkRouteDiscoveryTable.Print(stream);
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::PrintNeighborTable(Ptr<OutputStreamWrapper> stream) const
|
|
{
|
|
std::ostream* os = stream->GetStream();
|
|
std::ios oldState(nullptr);
|
|
oldState.copyfmt(*os);
|
|
|
|
std::ostringstream nwkAddr;
|
|
std::ostringstream ieeeAddr;
|
|
|
|
nwkAddr << m_nwkNetworkAddress;
|
|
ieeeAddr << m_nwkIeeeAddress;
|
|
|
|
*os << std::resetiosflags(std::ios::adjustfield) << std::setiosflags(std::ios::left);
|
|
*os << "[" << ieeeAddr.str() << " | " << nwkAddr.str() << "] | ";
|
|
*os << "Time: " << Simulator::Now().As(Time::S) << " | ";
|
|
m_nwkNeighborTable.Print(stream);
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::PrintRREQRetryTable(Ptr<OutputStreamWrapper> stream) const
|
|
{
|
|
std::ostream* os = stream->GetStream();
|
|
std::ios oldState(nullptr);
|
|
oldState.copyfmt(*os);
|
|
|
|
std::ostringstream nwkAddr;
|
|
std::ostringstream ieeeAddr;
|
|
|
|
nwkAddr << m_nwkNetworkAddress;
|
|
ieeeAddr << m_nwkIeeeAddress;
|
|
|
|
*os << std::resetiosflags(std::ios::adjustfield) << std::setiosflags(std::ios::left);
|
|
*os << "[" << ieeeAddr.str() << " | " << nwkAddr.str() << "] | ";
|
|
*os << "Time: " << Simulator::Now().As(Time::S) << " | ";
|
|
m_rreqRetryTable.Print(stream);
|
|
}
|
|
|
|
Mac16Address
|
|
ZigbeeNwk::FindRoute(Mac16Address dst, bool& neighbor)
|
|
{
|
|
Ptr<NeighborTableEntry> neighborEntry;
|
|
if (m_nwkNeighborTable.LookUpEntry(dst, neighborEntry))
|
|
{
|
|
neighbor = true;
|
|
return dst;
|
|
}
|
|
|
|
Ptr<RoutingTableEntry> entry;
|
|
if (m_nwkRoutingTable.LookUpEntry(dst, entry))
|
|
{
|
|
if (entry->GetStatus() == ROUTE_ACTIVE)
|
|
{
|
|
neighbor = false;
|
|
return entry->GetNextHopAddr();
|
|
}
|
|
}
|
|
|
|
neighbor = false;
|
|
return Mac16Address("FF:FF"); // route not found
|
|
}
|
|
|
|
Mac16Address
|
|
ZigbeeNwk::GetNetworkAddress() const
|
|
{
|
|
return m_nwkNetworkAddress;
|
|
}
|
|
|
|
Mac64Address
|
|
ZigbeeNwk::GetIeeeAddress() const
|
|
{
|
|
return m_nwkIeeeAddress;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::McpsDataIndication(McpsDataIndicationParams params, Ptr<Packet> msdu)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
ZigbeeNwkHeader nwkHeader;
|
|
msdu->RemoveHeader(nwkHeader);
|
|
|
|
// Decrease the radius in the network header as it might be retransmitted
|
|
// to a next hop.
|
|
uint8_t radius = nwkHeader.GetRadius();
|
|
nwkHeader.SetRadius(radius - 1);
|
|
|
|
// Check if the received frame is from a neighbor and update LQI if necessary
|
|
Ptr<NeighborTableEntry> neighborEntry;
|
|
if (m_nwkNeighborTable.LookUpEntry(nwkHeader.GetSrcAddr(), neighborEntry))
|
|
{
|
|
neighborEntry->SetLqi(params.m_mpduLinkQuality);
|
|
neighborEntry->SetOutgoingCost(GetLQINonLinearValue(params.m_mpduLinkQuality));
|
|
}
|
|
|
|
switch (nwkHeader.GetFrameType())
|
|
{
|
|
case DATA:
|
|
if (nwkHeader.IsMulticast())
|
|
{
|
|
// DATA MULTICAST
|
|
NS_FATAL_ERROR("Multicast DATA transmission not supported");
|
|
}
|
|
else if (IsBroadcastAddress(nwkHeader.GetDstAddr()))
|
|
{
|
|
// DATA BROADCAST
|
|
|
|
Ptr<BroadcastTransactionRecord> btr;
|
|
if (m_btt.LookUpEntry(nwkHeader.GetSeqNum(), btr))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Check the capabilities of the current device
|
|
CapabilityInformation capability;
|
|
capability.SetCapability(m_nwkCapabilityInformation);
|
|
|
|
bool rebroadcastFlag = false;
|
|
|
|
if (nwkHeader.GetDstAddr() == 0xFFFF ||
|
|
(nwkHeader.GetDstAddr() == 0xFFFD && capability.IsReceiverOnWhenIdle()) ||
|
|
(nwkHeader.GetDstAddr() == 0xFFFC && capability.GetDeviceType() != ENDDEVICE))
|
|
{
|
|
rebroadcastFlag = true;
|
|
}
|
|
else if (nwkHeader.GetDstAddr() == 0xFFFB)
|
|
{
|
|
NS_FATAL_ERROR("Broadcast to low power routers not supported");
|
|
return;
|
|
}
|
|
|
|
if (rebroadcastFlag && nwkHeader.GetRadius() > 0)
|
|
{
|
|
Ptr<Packet> rebroadcastPkt = msdu->Copy();
|
|
rebroadcastPkt->AddHeader(nwkHeader);
|
|
SendDataBcst(rebroadcastPkt, 0);
|
|
}
|
|
|
|
// Add a new broadcast transaction record to the
|
|
// broadcasst transaction table
|
|
btr = Create<BroadcastTransactionRecord>(nwkHeader.GetSrcAddr(),
|
|
nwkHeader.GetSeqNum(),
|
|
Simulator::Now() +
|
|
m_nwkNetworkBroadcastDeliveryTime);
|
|
m_btt.AddEntry(btr);
|
|
|
|
if (!m_nldeDataIndicationCallback.IsNull())
|
|
{
|
|
NldeDataIndicationParams dataParams;
|
|
dataParams.m_srcAddr = nwkHeader.GetSrcAddr();
|
|
dataParams.m_dstAddr = nwkHeader.GetDstAddr();
|
|
dataParams.m_dstAddrMode = UCST_BCST;
|
|
dataParams.m_linkQuality = params.m_mpduLinkQuality;
|
|
dataParams.m_nsduLength = msdu->GetSize();
|
|
dataParams.m_rxTime = Simulator::Now();
|
|
dataParams.m_securityUse = false;
|
|
m_nldeDataIndicationCallback(dataParams, msdu);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// DATA UNICAST
|
|
if (nwkHeader.GetDstAddr() == m_nwkNetworkAddress)
|
|
{
|
|
// Zigbee specification r22.1.0 Sections 3.6.2.2 and 3.6.3.3
|
|
if (!m_nldeDataIndicationCallback.IsNull())
|
|
{
|
|
NldeDataIndicationParams dataParams;
|
|
dataParams.m_srcAddr = nwkHeader.GetSrcAddr();
|
|
dataParams.m_dstAddr = nwkHeader.GetDstAddr();
|
|
dataParams.m_dstAddrMode = UCST_BCST;
|
|
dataParams.m_linkQuality = params.m_mpduLinkQuality;
|
|
dataParams.m_nsduLength = msdu->GetSize();
|
|
dataParams.m_rxTime = Simulator::Now();
|
|
dataParams.m_securityUse = false;
|
|
m_nldeDataIndicationCallback(dataParams, msdu);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is not the packet destination,
|
|
// Add again the network header to the DATA packet and
|
|
// route the packet to the next hop
|
|
msdu->AddHeader(nwkHeader);
|
|
SendDataUcst(msdu, 0);
|
|
}
|
|
}
|
|
break;
|
|
case NWK_COMMAND: {
|
|
ZigbeePayloadType payloadType;
|
|
msdu->RemoveHeader(payloadType);
|
|
|
|
if (payloadType.GetCmdType() == ROUTE_REQ_CMD || payloadType.GetCmdType() == ROUTE_REP_CMD)
|
|
{
|
|
CapabilityInformation capability;
|
|
capability.SetCapability(m_nwkCapabilityInformation);
|
|
if (capability.GetDeviceType() != ROUTER)
|
|
{
|
|
// Received RREQ or RREP but the device
|
|
// has no routing capabilities
|
|
return;
|
|
}
|
|
}
|
|
// NOTE: this cover the cases for MESH routing
|
|
// TREE routing is not supported
|
|
uint8_t linkCost = GetLinkCost(params.m_mpduLinkQuality);
|
|
|
|
if (payloadType.GetCmdType() == ROUTE_REQ_CMD)
|
|
{
|
|
ZigbeePayloadRouteRequestCommand payload;
|
|
msdu->RemoveHeader(payload);
|
|
// Zigbee specification r22.1.0 Section 3.6.3.5.2
|
|
ReceiveRREQ(params.m_srcAddr, linkCost, nwkHeader, payload);
|
|
}
|
|
else if (payloadType.GetCmdType() == ROUTE_REP_CMD)
|
|
{
|
|
ZigbeePayloadRouteReplyCommand payload;
|
|
msdu->RemoveHeader(payload);
|
|
// Zigbee specification r22.1.0 Section 3.6.3.5.3
|
|
ReceiveRREP(params.m_srcAddr, linkCost, nwkHeader, payload);
|
|
}
|
|
break;
|
|
}
|
|
case INTER_PAN:
|
|
NS_LOG_ERROR("Inter PAN frame received but not supported");
|
|
break;
|
|
default:
|
|
NS_LOG_ERROR("Unknown frame received in NWK layer");
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::ReceiveRREQ(Mac16Address macSrcAddr,
|
|
uint8_t linkCost,
|
|
ZigbeeNwkHeader nwkHeader,
|
|
ZigbeePayloadRouteRequestCommand payload)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
if (nwkHeader.GetSrcAddr() == m_nwkNetworkAddress)
|
|
{
|
|
// I am the original initiator of the RREQ, ignore request
|
|
return;
|
|
}
|
|
|
|
// Calculate the pathcost on the RREQ receiving device
|
|
uint8_t pathCost = linkCost + payload.GetPathCost();
|
|
|
|
// Many-to-one routing
|
|
|
|
if (payload.GetCmdOptManyToOneField() != ManyToOne::NO_MANY_TO_ONE)
|
|
{
|
|
RouteDiscoveryStatus routeStatus =
|
|
ProcessManyToOneRoute(macSrcAddr, pathCost, nwkHeader, payload);
|
|
|
|
// Update the path cost of the RREQ
|
|
payload.SetPathCost(pathCost);
|
|
|
|
// Note: At this point we already have the updated radius, which was updated as soon
|
|
// as the frame was received (i.e. In the MCPS-DATA.indication).
|
|
|
|
if (routeStatus == MANY_TO_ONE_ROUTE || routeStatus == ROUTE_UPDATED)
|
|
{
|
|
Simulator::Schedule(MilliSeconds(m_rreqJitter->GetValue()),
|
|
&ZigbeeNwk::SendRREQ,
|
|
this,
|
|
nwkHeader,
|
|
payload,
|
|
0);
|
|
m_nwkSequenceNumber++;
|
|
m_routeRequestId++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Mesh Routing
|
|
|
|
Mac16Address nextHop;
|
|
RouteDiscoveryStatus nextHopStatus =
|
|
FindNextHop(macSrcAddr, pathCost, nwkHeader, payload, nextHop);
|
|
|
|
if (payload.GetDstAddr() == m_nwkNetworkAddress || nextHopStatus == ROUTE_FOUND)
|
|
{
|
|
// RREQ is for this device or its children
|
|
NS_LOG_DEBUG("RREQ is for me or my children, sending a RREP to [" << macSrcAddr << "]");
|
|
|
|
SendRREP(macSrcAddr,
|
|
nwkHeader.GetSrcAddr(),
|
|
payload.GetDstAddr(),
|
|
payload.GetRouteReqId(),
|
|
pathCost);
|
|
}
|
|
else if (nextHopStatus == ROUTE_NOT_FOUND || nextHopStatus == ROUTE_UPDATED)
|
|
{
|
|
NS_LOG_DEBUG("Route for device [" << payload.GetDstAddr()
|
|
<< "] not found, forwarding RREQ");
|
|
|
|
// Update path cost and resend the RREQ
|
|
payload.SetPathCost(pathCost);
|
|
Simulator::Schedule(MilliSeconds(m_rreqJitter->GetValue()),
|
|
&ZigbeeNwk::SendRREQ,
|
|
this,
|
|
nwkHeader,
|
|
payload,
|
|
m_nwkcRREQRetries);
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::ReceiveRREP(Mac16Address macSrcAddr,
|
|
uint8_t linkCost,
|
|
ZigbeeNwkHeader nwkHeader,
|
|
ZigbeePayloadRouteReplyCommand payload)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
// RREP received, cancel any ongoing RREQ retry events for that
|
|
// RREQ ID and remove entry from RREQ retry table.
|
|
Ptr<RreqRetryTableEntry> rreqRetryTableEntry;
|
|
if (m_rreqRetryTable.LookUpEntry(payload.GetRouteReqId(), rreqRetryTableEntry))
|
|
{
|
|
rreqRetryTableEntry->GetRreqEventId().Cancel();
|
|
m_rreqRetryTable.Delete(payload.GetRouteReqId());
|
|
}
|
|
|
|
uint8_t pathCost = linkCost + payload.GetPathCost();
|
|
|
|
if (payload.GetOrigAddr() == m_nwkNetworkAddress)
|
|
{
|
|
// The RREP is destined for this device
|
|
Ptr<RouteDiscoveryTableEntry> discEntry;
|
|
if (m_nwkRouteDiscoveryTable.LookUpEntry(payload.GetRouteReqId(),
|
|
payload.GetOrigAddr(),
|
|
discEntry))
|
|
{
|
|
Ptr<RoutingTableEntry> routeEntry;
|
|
if (m_nwkRoutingTable.LookUpEntry(payload.GetRespAddr(), routeEntry))
|
|
{
|
|
if (routeEntry->GetStatus() == ROUTE_DISCOVERY_UNDERWAY)
|
|
{
|
|
if (routeEntry->IsGroupIdPresent())
|
|
{
|
|
routeEntry->SetStatus(ROUTE_VALIDATION_UNDERWAY);
|
|
}
|
|
else
|
|
{
|
|
routeEntry->SetStatus(ROUTE_ACTIVE);
|
|
}
|
|
routeEntry->SetNextHopAddr(macSrcAddr);
|
|
discEntry->SetResidualCost(pathCost);
|
|
}
|
|
else if (routeEntry->GetStatus() == ROUTE_VALIDATION_UNDERWAY ||
|
|
routeEntry->GetStatus() == ROUTE_ACTIVE)
|
|
{
|
|
if (pathCost < discEntry->GetResidualCost())
|
|
{
|
|
routeEntry->SetNextHopAddr(macSrcAddr);
|
|
discEntry->SetResidualCost(pathCost);
|
|
}
|
|
}
|
|
|
|
NS_LOG_DEBUG("RREP from source [" << payload.GetRespAddr()
|
|
<< "] is for me; received from last hop ["
|
|
<< macSrcAddr << "]");
|
|
|
|
if (m_pendPrimitiveNwk == NLME_ROUTE_DISCOVERY)
|
|
{
|
|
// We only report the result of the route discovery request
|
|
// with the first RREP received.
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
if (!m_nlmeRouteDiscoveryConfirmCallback.IsNull())
|
|
{
|
|
NlmeRouteDiscoveryConfirmParams routeDiscConfirmParams;
|
|
routeDiscConfirmParams.m_status = NwkStatus::SUCCESS;
|
|
m_nlmeRouteDiscoveryConfirmCallback(routeDiscConfirmParams);
|
|
}
|
|
}
|
|
|
|
Ptr<PendingTxPkt> pendingTxPkt = Create<PendingTxPkt>();
|
|
if (!m_pendingTxQueue.empty() &&
|
|
DequeuePendingTx(payload.GetRespAddr(), pendingTxPkt))
|
|
{
|
|
// Buffer a copy of the DATA packet that will be transmitted
|
|
// for handling after transmission (i.e. NSDE-DATA.confirm)
|
|
BufferTxPkt(pendingTxPkt->txPkt->Copy(),
|
|
m_macHandle.GetValue(),
|
|
pendingTxPkt->nsduHandle);
|
|
|
|
// There is a pending packet awaiting to be transmitted
|
|
// to the next hop, send it.
|
|
McpsDataRequestParams mcpsDataparams;
|
|
mcpsDataparams.m_txOptions = 0x01; // Acknowledment on.
|
|
mcpsDataparams.m_dstPanId = m_nwkPanId;
|
|
mcpsDataparams.m_msduHandle = m_macHandle.GetValue();
|
|
mcpsDataparams.m_srcAddrMode = SHORT_ADDR;
|
|
mcpsDataparams.m_dstAddrMode = SHORT_ADDR;
|
|
mcpsDataparams.m_dstAddr = routeEntry->GetNextHopAddr();
|
|
m_macHandle++;
|
|
|
|
Simulator::ScheduleNow(&LrWpanMacBase::McpsDataRequest,
|
|
m_mac,
|
|
mcpsDataparams,
|
|
pendingTxPkt->txPkt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_nwkRouteDiscoveryTable.Delete(payload.GetRouteReqId(), payload.GetOrigAddr());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The RREP is NOT destined for this device
|
|
Ptr<RouteDiscoveryTableEntry> discEntry;
|
|
if (m_nwkRouteDiscoveryTable.LookUpEntry(payload.GetRouteReqId(),
|
|
payload.GetOrigAddr(),
|
|
discEntry))
|
|
{
|
|
if (payload.GetPathCost() < discEntry->GetResidualCost())
|
|
{
|
|
Ptr<RoutingTableEntry> routeEntry;
|
|
if (m_nwkRoutingTable.LookUpEntry(payload.GetRespAddr(), routeEntry))
|
|
{
|
|
routeEntry->SetNextHopAddr(macSrcAddr);
|
|
routeEntry->SetStatus(ROUTE_ACTIVE);
|
|
discEntry->SetResidualCost(pathCost);
|
|
// Forward route reply to the next hop back to the original route requester
|
|
SendRREP(discEntry->GetSenderAddr(),
|
|
payload.GetOrigAddr(),
|
|
payload.GetRespAddr(),
|
|
payload.GetRouteReqId(),
|
|
pathCost);
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_ERROR("Route discovery entry detected but no corresponding routing "
|
|
"table entry found");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
ZigbeeNwk::IsBroadcastAddress(Mac16Address address)
|
|
{
|
|
return address == "FF:FF" || address == "FF:FD" || address == "FF:FC" || address == "FF:FB";
|
|
}
|
|
|
|
RouteDiscoveryStatus
|
|
ZigbeeNwk::FindNextHop(Mac16Address macSrcAddr,
|
|
uint8_t pathCost,
|
|
ZigbeeNwkHeader nwkHeader,
|
|
ZigbeePayloadRouteRequestCommand payload,
|
|
Mac16Address& nextHop)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
// Mesh routing
|
|
|
|
// Check if the destination is our neighbor
|
|
Ptr<NeighborTableEntry> neighborEntry;
|
|
if (m_nwkNeighborTable.LookUpEntry(payload.GetDstAddr(), neighborEntry))
|
|
{
|
|
nextHop = payload.GetDstAddr();
|
|
return ROUTE_FOUND;
|
|
}
|
|
|
|
Ptr<RoutingTableEntry> entry;
|
|
if (m_nwkRoutingTable.LookUpEntry(payload.GetDstAddr(), entry))
|
|
{
|
|
if (!(entry->GetStatus() == ROUTE_ACTIVE) &&
|
|
!(entry->GetStatus() == ROUTE_VALIDATION_UNDERWAY))
|
|
{
|
|
// Entry found but is not valid
|
|
entry->SetStatus(ROUTE_DISCOVERY_UNDERWAY);
|
|
}
|
|
else
|
|
{
|
|
// Entry found
|
|
nextHop = entry->GetNextHopAddr();
|
|
return ROUTE_FOUND;
|
|
}
|
|
}
|
|
else if (nwkHeader.GetDiscoverRoute() == DiscoverRouteType::ENABLE_ROUTE_DISCOVERY)
|
|
{
|
|
// Check that the max routing capacity has not been reached. If the capacity was reached,
|
|
// attempt to delete the first found expired entry, if the table persist to be full
|
|
// then send a route error.
|
|
if (m_nwkRoutingTable.GetSize() >= m_nwkRoutingTable.GetMaxTableSize())
|
|
{
|
|
m_nwkRoutingTable.DeleteExpiredEntry();
|
|
|
|
if (m_nwkRoutingTable.GetSize() >= m_nwkRoutingTable.GetMaxTableSize())
|
|
{
|
|
if (!m_nlmeRouteDiscoveryConfirmCallback.IsNull())
|
|
{
|
|
NlmeRouteDiscoveryConfirmParams confirmParams;
|
|
confirmParams.m_status = ROUTE_ERROR;
|
|
confirmParams.m_networkStatusCode = NO_ROUTING_CAPACITY;
|
|
m_nlmeRouteDiscoveryConfirmCallback(confirmParams);
|
|
}
|
|
return TABLE_FULL;
|
|
}
|
|
}
|
|
|
|
// Entry not found
|
|
Ptr<RoutingTableEntry> newRoutingEntry =
|
|
Create<RoutingTableEntry>(payload.GetDstAddr(),
|
|
ROUTE_DISCOVERY_UNDERWAY,
|
|
true, // TODO no route cache
|
|
false, // TODO: Many to one
|
|
false, // TODO: Route record
|
|
false, // TODO: Group id
|
|
Mac16Address("FF:FF"));
|
|
newRoutingEntry->SetLifeTime(Simulator::Now() + m_routeExpiryTime);
|
|
|
|
m_nwkRoutingTable.AddEntry(newRoutingEntry);
|
|
}
|
|
else
|
|
{
|
|
if (!m_nlmeRouteDiscoveryConfirmCallback.IsNull())
|
|
{
|
|
NlmeRouteDiscoveryConfirmParams confirmParams;
|
|
confirmParams.m_status = ROUTE_ERROR;
|
|
confirmParams.m_networkStatusCode = NO_ROUTE_AVAILABLE;
|
|
m_nlmeRouteDiscoveryConfirmCallback(confirmParams);
|
|
}
|
|
return NO_DISCOVER_ROUTE;
|
|
}
|
|
|
|
// 2- Find entry in DISCOVERY TABLE
|
|
Ptr<RouteDiscoveryTableEntry> discEntry;
|
|
if (m_nwkRouteDiscoveryTable.LookUpEntry(payload.GetRouteReqId(),
|
|
nwkHeader.GetSrcAddr(),
|
|
discEntry))
|
|
{
|
|
// Entry Found
|
|
if (pathCost < discEntry->GetForwardCost())
|
|
{
|
|
// More optimal route found, update route discovery values.
|
|
discEntry->SetSenderAddr(macSrcAddr);
|
|
discEntry->SetForwardCost(pathCost);
|
|
discEntry->SetExpTime(Simulator::Now() + m_nwkcRouteDiscoveryTime);
|
|
return ROUTE_UPDATED;
|
|
}
|
|
else
|
|
{
|
|
return DISCOVER_UNDERWAY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Entry NOT found, add NEW entry to route discovery table.
|
|
Ptr<RouteDiscoveryTableEntry> newDiscEntry =
|
|
Create<RouteDiscoveryTableEntry>(payload.GetRouteReqId(),
|
|
nwkHeader.GetSrcAddr(),
|
|
macSrcAddr, // macSrcAddr,
|
|
pathCost, // payload.GetPathCost(), // Forward cost
|
|
0xff, // Residual cost
|
|
(Simulator::Now() + m_nwkcRouteDiscoveryTime));
|
|
|
|
if (!m_nwkRouteDiscoveryTable.AddEntry(newDiscEntry))
|
|
{
|
|
return TABLE_FULL;
|
|
}
|
|
}
|
|
return ROUTE_NOT_FOUND;
|
|
}
|
|
|
|
RouteDiscoveryStatus
|
|
ZigbeeNwk::ProcessManyToOneRoute(Mac16Address macSrcAddr,
|
|
uint8_t pathCost,
|
|
ZigbeeNwkHeader nwkHeader,
|
|
ZigbeePayloadRouteRequestCommand payload)
|
|
{
|
|
Ptr<RouteDiscoveryTableEntry> discEntry;
|
|
if (m_nwkRouteDiscoveryTable.LookUpEntry(payload.GetRouteReqId(),
|
|
nwkHeader.GetSrcAddr(),
|
|
discEntry))
|
|
{
|
|
Ptr<RoutingTableEntry> routeEntry;
|
|
if (m_nwkRoutingTable.LookUpEntry(nwkHeader.GetSrcAddr(), routeEntry))
|
|
{
|
|
if (routeEntry->GetStatus() == ROUTE_VALIDATION_UNDERWAY ||
|
|
routeEntry->GetStatus() == ROUTE_ACTIVE)
|
|
{
|
|
if (pathCost < discEntry->GetForwardCost())
|
|
{
|
|
// Update with a better route.
|
|
routeEntry->SetNextHopAddr(macSrcAddr);
|
|
discEntry->SetForwardCost(pathCost);
|
|
discEntry->SetExpTime(Simulator::Now() + m_nwkcRouteDiscoveryTime);
|
|
return ROUTE_UPDATED;
|
|
}
|
|
return NO_ROUTE_CHANGE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_ERROR("Entry found in the discovery table but not the routing table");
|
|
return NO_ROUTE_CHANGE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Entry NOT found, add NEW entry to route discovery table.
|
|
Ptr<RouteDiscoveryTableEntry> newDiscEntry =
|
|
Create<RouteDiscoveryTableEntry>(payload.GetRouteReqId(),
|
|
nwkHeader.GetSrcAddr(),
|
|
macSrcAddr, // previous hop address
|
|
pathCost, // Forward cost
|
|
0xff, // Residual cost (not used by Many-to-One)
|
|
(Simulator::Now() + m_nwkcRouteDiscoveryTime));
|
|
|
|
// TODO: what to do if route discovery table is full?
|
|
m_nwkRouteDiscoveryTable.AddEntry(newDiscEntry);
|
|
|
|
// Define the type of Many-To-One routing (with or without route record)
|
|
bool routeRecord = false;
|
|
if (payload.GetCmdOptManyToOneField() == ManyToOne::ROUTE_RECORD)
|
|
{
|
|
routeRecord = true;
|
|
}
|
|
|
|
Ptr<RoutingTableEntry> routeEntry;
|
|
if (m_nwkRoutingTable.LookUpEntry(nwkHeader.GetSrcAddr(), routeEntry))
|
|
{
|
|
if (routeEntry->GetStatus() == ROUTE_VALIDATION_UNDERWAY ||
|
|
routeEntry->GetStatus() == ROUTE_ACTIVE)
|
|
{
|
|
// The entry exist in routing table but it was not in discovery table
|
|
// Refresh the Route
|
|
routeEntry->SetNextHopAddr(macSrcAddr);
|
|
// TODO: other parameters
|
|
return ROUTE_UPDATED;
|
|
}
|
|
return NO_ROUTE_CHANGE;
|
|
}
|
|
else
|
|
{
|
|
// New routing table entry
|
|
// Check that the max routing capacity has not been reached. If the capacity was
|
|
// reached, attempt to delete the first found expired entry, if the table persist to be
|
|
// full then send a route error.
|
|
if (m_nwkRoutingTable.GetSize() >= m_nwkRoutingTable.GetMaxTableSize())
|
|
{
|
|
m_nwkRoutingTable.DeleteExpiredEntry();
|
|
|
|
if (m_nwkRoutingTable.GetSize() >= m_nwkRoutingTable.GetMaxTableSize())
|
|
{
|
|
if (!m_nlmeRouteDiscoveryConfirmCallback.IsNull())
|
|
{
|
|
NlmeRouteDiscoveryConfirmParams confirmParams;
|
|
confirmParams.m_status = ROUTE_ERROR;
|
|
confirmParams.m_networkStatusCode = NO_ROUTING_CAPACITY;
|
|
m_nlmeRouteDiscoveryConfirmCallback(confirmParams);
|
|
}
|
|
return TABLE_FULL;
|
|
}
|
|
}
|
|
|
|
Ptr<RoutingTableEntry> newRoutingEntry =
|
|
Create<RoutingTableEntry>(nwkHeader.GetSrcAddr(),
|
|
ROUTE_ACTIVE,
|
|
true, // TODO no route cache
|
|
true, // TODO: Many to one
|
|
routeRecord, // TODO: Route record
|
|
false, // TODO: Group id
|
|
macSrcAddr);
|
|
|
|
newRoutingEntry->SetLifeTime(Simulator::Now() + m_routeExpiryTime);
|
|
|
|
m_nwkRoutingTable.AddEntry(newRoutingEntry);
|
|
return MANY_TO_ONE_ROUTE;
|
|
}
|
|
}
|
|
return NO_ROUTE_CHANGE;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SendDataUcst(Ptr<Packet> packet, uint8_t nwkHandle)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
// Obtain information from the DATA packet
|
|
ZigbeeNwkHeader nwkHeaderData;
|
|
packet->PeekHeader(nwkHeaderData);
|
|
|
|
// Construct a RREQ network header and payload for possible
|
|
// route discovery request
|
|
|
|
ZigbeeNwkHeader nwkHeaderRreq;
|
|
nwkHeaderRreq.SetFrameType(NWK_COMMAND);
|
|
nwkHeaderRreq.SetProtocolVer(m_nwkcProtocolVersion);
|
|
nwkHeaderRreq.SetDiscoverRoute(nwkHeaderData.GetDiscoverRoute());
|
|
// See r22.1.0, Table 3-69
|
|
// Set destination to broadcast (all routers and coordinator)
|
|
nwkHeaderRreq.SetDstAddr(Mac16Address("FF:FC"));
|
|
nwkHeaderRreq.SetSrcAddr(m_nwkNetworkAddress);
|
|
nwkHeaderRreq.SetSeqNum(m_nwkSequenceNumber.GetValue());
|
|
// see Zigbee specification 3.2.2.33.3
|
|
if (nwkHeaderData.GetRadius() == 0)
|
|
{
|
|
nwkHeaderRreq.SetRadius(m_nwkMaxDepth * 2);
|
|
}
|
|
else
|
|
{
|
|
nwkHeaderRreq.SetRadius(nwkHeaderData.GetRadius());
|
|
}
|
|
|
|
ZigbeePayloadRouteRequestCommand payloadRreq;
|
|
payloadRreq.SetRouteReqId(m_routeRequestId.GetValue());
|
|
payloadRreq.SetDstAddr(nwkHeaderData.GetDstAddr());
|
|
payloadRreq.SetPathCost(0);
|
|
|
|
Mac16Address nextHop;
|
|
RouteDiscoveryStatus nextHopStatus =
|
|
FindNextHop(m_nwkNetworkAddress, 0, nwkHeaderRreq, payloadRreq, nextHop);
|
|
|
|
if (nextHopStatus == ROUTE_FOUND)
|
|
{
|
|
// Buffer a copy of the DATA packet that will be transmitted
|
|
// for handling after transmission (i.e. NSDE-DATA.confirm)
|
|
BufferTxPkt(packet->Copy(), m_macHandle.GetValue(), nwkHandle);
|
|
|
|
// Parameters as described in Section 3.6.3.3
|
|
McpsDataRequestParams mcpsDataparams;
|
|
mcpsDataparams.m_dstPanId = m_nwkPanId;
|
|
mcpsDataparams.m_msduHandle = m_macHandle.GetValue();
|
|
mcpsDataparams.m_txOptions = 0x01; // Acknowledment on.
|
|
mcpsDataparams.m_srcAddrMode = SHORT_ADDR;
|
|
mcpsDataparams.m_dstAddrMode = SHORT_ADDR;
|
|
mcpsDataparams.m_dstAddr = nextHop;
|
|
m_macHandle++;
|
|
Simulator::ScheduleNow(&LrWpanMacBase::McpsDataRequest, m_mac, mcpsDataparams, packet);
|
|
}
|
|
else if (nextHopStatus == ROUTE_NOT_FOUND)
|
|
{
|
|
// Route not found. Route marked as DISCOVER UNDERWAY,
|
|
// packet added to pending Tx Queue and we initiate route
|
|
// discovery
|
|
|
|
EnqueuePendingTx(packet, nwkHandle);
|
|
|
|
Simulator::Schedule(MilliSeconds(m_rreqJitter->GetValue()),
|
|
&ZigbeeNwk::SendRREQ,
|
|
this,
|
|
nwkHeaderRreq,
|
|
payloadRreq,
|
|
m_nwkcInitialRREQRetries);
|
|
|
|
m_nwkSequenceNumber++;
|
|
m_routeRequestId++;
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SendDataBcst(Ptr<Packet> packet, uint8_t nwkHandle)
|
|
{
|
|
// Buffer a copy of the DATA packet that will be transmitted
|
|
// for handling after transmission (i.e. NSDE-DATA.confirm)
|
|
BufferTxPkt(packet->Copy(), m_macHandle.GetValue(), nwkHandle);
|
|
|
|
// Parameters as described in Section 3.6.5
|
|
McpsDataRequestParams mcpsDataparams;
|
|
mcpsDataparams.m_dstPanId = m_nwkPanId;
|
|
mcpsDataparams.m_msduHandle = m_macHandle.GetValue();
|
|
mcpsDataparams.m_srcAddrMode = SHORT_ADDR;
|
|
mcpsDataparams.m_dstAddrMode = SHORT_ADDR;
|
|
mcpsDataparams.m_dstAddr = Mac16Address("FF:FF");
|
|
m_macHandle++;
|
|
Simulator::ScheduleNow(&LrWpanMacBase::McpsDataRequest, m_mac, mcpsDataparams, packet);
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::McpsDataConfirm(McpsDataConfirmParams params)
|
|
{
|
|
Ptr<TxPkt> bufferedElement;
|
|
if (RetrieveTxPkt(params.m_msduHandle, bufferedElement))
|
|
{
|
|
ZigbeeNwkHeader nwkHeader;
|
|
bufferedElement->txPkt->PeekHeader(nwkHeader);
|
|
|
|
if (nwkHeader.GetFrameType() == DATA)
|
|
{
|
|
if (IsBroadcastAddress(nwkHeader.GetDstAddr()))
|
|
{
|
|
}
|
|
else if (nwkHeader.GetSrcAddr() == m_nwkNetworkAddress)
|
|
{
|
|
// Send the confirmation to next layer after the packet transmission,
|
|
// only if packet transmitted was in the initiator of the NLDE-DATA.request
|
|
|
|
if (!m_nldeDataConfirmCallback.IsNull())
|
|
{
|
|
// Zigbee Specification r22.1.0, End of Section 3.2.1.1.3
|
|
// Report the the results of a request to a transmission of a packet
|
|
NldeDataConfirmParams nldeDataConfirmParams;
|
|
// nldeDataConfirmParams.m_status =
|
|
// static_cast<ZigbeeNwkStatus>(params.m_status);
|
|
nldeDataConfirmParams.m_nsduHandle = bufferedElement->nwkHandle;
|
|
m_nldeDataConfirmCallback(nldeDataConfirmParams);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::MlmeScanConfirm(MlmeScanConfirmParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
if (m_pendPrimitiveNwk == NLME_NETWORK_FORMATION && params.m_scanType == MLMESCAN_ED)
|
|
{
|
|
if (params.m_status != MacStatus::SUCCESS)
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_netFormParams = {};
|
|
m_netFormParamsGen = nullptr;
|
|
|
|
// See Zigbee specification r22.1.0, Section 3.2.2.5.3, (6.b.i)
|
|
if (!m_nlmeNetworkFormationConfirmCallback.IsNull())
|
|
{
|
|
NlmeNetworkFormationConfirmParams confirmParams;
|
|
confirmParams.m_status = GetNwkStatus(params.m_status);
|
|
m_nlmeNetworkFormationConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: Continue energy detection (ED) scan in other interfaces if supported.
|
|
|
|
// See Zigbee specification r22.1.0, Section 3.2.2.5.3, (6.b.ii)
|
|
// Pick the list of acceptable channels on which to continue doing an ACTIVE scan
|
|
std::vector<uint8_t> energyList = params.m_energyDetList;
|
|
uint32_t channelMask = m_netFormParams.m_scanChannelList.channelsField[0];
|
|
|
|
NS_LOG_DEBUG("[NLME-NETWORK-FORMATION.request]: \n "
|
|
<< "EnergyThreshold: " << m_scanEnergyThreshold << " | ChannelMask: 0x"
|
|
<< std::hex << channelMask << std::dec << " | EnergyList: " << energyList);
|
|
|
|
m_filteredChannelMask = 0;
|
|
uint32_t countAcceptableChannels = 0;
|
|
uint8_t energyListPos = 0;
|
|
for (uint32_t i = 0; i < 32; i++)
|
|
{
|
|
// check if the i position exist in the ChannelMask
|
|
if (channelMask & (1 << i))
|
|
{
|
|
if (energyList[energyListPos] <= m_scanEnergyThreshold)
|
|
{
|
|
m_filteredChannelMask |= (1 << i);
|
|
countAcceptableChannels++;
|
|
}
|
|
energyListPos++;
|
|
}
|
|
}
|
|
|
|
NS_LOG_DEBUG("[NLME-NETWORK-FORMATION.request]:\n "
|
|
<< "Energy scan complete, " << countAcceptableChannels
|
|
<< " acceptable channels found : 0x" << std::hex << m_filteredChannelMask
|
|
<< std::dec);
|
|
|
|
if (countAcceptableChannels == 0)
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_netFormParams = {};
|
|
m_netFormParamsGen = nullptr;
|
|
|
|
if (!m_nlmeNetworkFormationConfirmCallback.IsNull())
|
|
{
|
|
NlmeNetworkFormationConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::STARTUP_FAILURE;
|
|
m_nlmeNetworkFormationConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// See Zigbee specification r22.1.0, Section 3.2.2.5.3, (6.c)
|
|
MlmeScanRequestParams mlmeParams;
|
|
mlmeParams.m_chPage = (m_filteredChannelMask >> 27) & (0x01F);
|
|
mlmeParams.m_scanChannels = m_filteredChannelMask;
|
|
mlmeParams.m_scanDuration = m_netFormParams.m_scanDuration;
|
|
mlmeParams.m_scanType = MLMESCAN_ACTIVE;
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeScanRequest, m_mac, mlmeParams);
|
|
}
|
|
}
|
|
}
|
|
else if (m_pendPrimitiveNwk == NLME_NETWORK_FORMATION && params.m_scanType == MLMESCAN_ACTIVE)
|
|
{
|
|
if (params.m_status == MacStatus::NO_BEACON || params.m_status == MacStatus::SUCCESS)
|
|
{
|
|
// TODO: We should ACTIVE scan channels on each interface
|
|
// (only possible when more interfaces (nwkMacInterfaceTable) are supported)
|
|
// for now, only a single interface is considered.
|
|
|
|
// See Zigbee specification r22.1.0, (3.2.2.5.3, 6.d.ii)
|
|
// Check results of an ACTIVE scan an select a different PAN ID and channel:
|
|
// Choose a random PAN ID
|
|
// Page is always 0 until more interfaces supported
|
|
uint8_t channel = 0;
|
|
uint8_t page = 0;
|
|
uint16_t panId = m_uniformRandomVariable->GetInteger(1, 0xFFF7);
|
|
|
|
std::vector<uint8_t> pansPerChannel(27);
|
|
uint32_t secondFilteredChannelMask = m_filteredChannelMask;
|
|
for (const auto& panDescriptor : params.m_panDescList)
|
|
{
|
|
// Clear all the bit positions of channels with active PAN networks
|
|
// (The channels with PAN descriptors received)
|
|
secondFilteredChannelMask &= ~(1 << panDescriptor.m_logCh);
|
|
// Add to the number of PAN detected in this channel
|
|
pansPerChannel[panDescriptor.m_logCh] += 1;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < 32; i++)
|
|
{
|
|
// Pick the first channel in the list that does not contain
|
|
// any PAN network
|
|
if (secondFilteredChannelMask & (1 << i))
|
|
{
|
|
channel = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (channel < 11 || channel > 26)
|
|
{
|
|
// Extreme case: All the channels in the previous step contained PAN networks
|
|
// therefore choose the channel with the least PAN networks.
|
|
uint8_t channelIndex = 0;
|
|
uint8_t lowestPanNum = 99;
|
|
for (const auto& numPans : pansPerChannel)
|
|
{
|
|
if (numPans != 0 && numPans <= lowestPanNum)
|
|
{
|
|
channel = channelIndex;
|
|
lowestPanNum = numPans;
|
|
}
|
|
channelIndex++;
|
|
}
|
|
|
|
NS_ASSERT_MSG(channel < 11 || channel > 26,
|
|
"Invalid channel in PAN descriptor list during ACTIVE scan");
|
|
}
|
|
|
|
// store the chosen page, channel and pan Id.
|
|
m_netFormParamsGen = Create<NetFormPendingParamsGen>();
|
|
m_netFormParamsGen->page = page;
|
|
m_netFormParamsGen->channel = channel;
|
|
m_netFormParamsGen->panId = panId;
|
|
|
|
NS_LOG_DEBUG("[NLME-NETWORK-FORMATION.request]:\n "
|
|
<< "Active scan complete, page " << std::dec << page << ", channel "
|
|
<< std::dec << channel << " and PAN ID 0x" << std::hex << panId << std::dec
|
|
<< " chosen.");
|
|
|
|
// Set the device short address (3.2.2.5.3 , 6.f)
|
|
Ptr<MacPibAttributes> pibAttr = Create<MacPibAttributes>();
|
|
if (m_netFormParams.m_distributedNetwork)
|
|
{
|
|
pibAttr->macShortAddress = m_netFormParams.m_distributedNetworkAddress;
|
|
m_nwkNetworkAddress = m_netFormParams.m_distributedNetworkAddress;
|
|
}
|
|
else
|
|
{
|
|
pibAttr->macShortAddress = Mac16Address("00:00");
|
|
m_nwkNetworkAddress = Mac16Address("00:00");
|
|
}
|
|
// Set Short Address and continue with beacon payload afterwards.
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeSetRequest,
|
|
m_mac,
|
|
MacPibAttributeIdentifier::macShortAddress,
|
|
pibAttr);
|
|
}
|
|
else
|
|
{
|
|
// Error occurred during network formation active scan
|
|
// report to higher layer (Section 3.2.2.5.3, 6.d)
|
|
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_netFormParams = {};
|
|
m_netFormParamsGen = nullptr;
|
|
|
|
if (!m_nlmeNetworkFormationConfirmCallback.IsNull())
|
|
{
|
|
NlmeNetworkFormationConfirmParams confirmParams;
|
|
confirmParams.m_status = GetNwkStatus(params.m_status);
|
|
m_nlmeNetworkFormationConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
}
|
|
else if (m_pendPrimitiveNwk == NLME_NET_DISCV && params.m_scanType == MLMESCAN_ACTIVE)
|
|
{
|
|
NlmeNetworkDiscoveryConfirmParams netDiscConfirmParams;
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
|
|
if (params.m_status == MacStatus::SUCCESS)
|
|
{
|
|
NS_LOG_DEBUG("[NLME-NETWORK-DISCOVERY.request]:\n "
|
|
<< "Active scan, " << m_networkDescriptorList.size()
|
|
<< " PARENT capable device(s) found");
|
|
|
|
netDiscConfirmParams.m_netDescList = m_networkDescriptorList;
|
|
netDiscConfirmParams.m_networkCount = m_networkDescriptorList.size();
|
|
netDiscConfirmParams.m_status = NwkStatus::SUCCESS;
|
|
m_networkDescriptorList = {};
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_DEBUG("[NLME-NETWORK-DISCOVERY.request]: Active scan failed with"
|
|
" status: "
|
|
<< GetNwkStatus(params.m_status));
|
|
netDiscConfirmParams.m_status = GetNwkStatus(params.m_status);
|
|
}
|
|
|
|
if (!m_nlmeNetworkDiscoveryConfirmCallback.IsNull())
|
|
{
|
|
m_nlmeNetworkDiscoveryConfirmCallback(netDiscConfirmParams);
|
|
}
|
|
}
|
|
else if (m_pendPrimitiveNwk == NLME_JOIN && params.m_scanType == MLMESCAN_ORPHAN)
|
|
{
|
|
// TODO: Add macInterfaceIndex and channelListStructure params when supported
|
|
if (params.m_status == MacStatus::SUCCESS)
|
|
{
|
|
// Orphan scan was successful (Join success), first update the extended
|
|
// PAN id and the capability information, then the
|
|
// the nwkNetworkAddress with the macShortAddress, this
|
|
// will be followed by an update of the m_nwkPanId with macPanId
|
|
// and finally the join confirmation
|
|
m_nwkExtendedPanId = m_joinParams.m_extendedPanId;
|
|
m_nwkCapabilityInformation = m_joinParams.m_capabilityInfo;
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeGetRequest,
|
|
m_mac,
|
|
MacPibAttributeIdentifier::macShortAddress);
|
|
}
|
|
else
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_joinParams = {};
|
|
NS_LOG_DEBUG("[NLME-JOIN.request]: Orphan scan completed but no networks found");
|
|
|
|
if (!m_nlmeJoinConfirmCallback.IsNull())
|
|
{
|
|
NlmeJoinConfirmParams joinConfirmParams;
|
|
joinConfirmParams.m_status = NwkStatus::NO_NETWORKS;
|
|
m_nlmeJoinConfirmCallback(joinConfirmParams);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::MlmeAssociateConfirm(MlmeAssociateConfirmParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
if (m_pendPrimitiveNwk == NLME_JOIN)
|
|
{
|
|
NlmeJoinConfirmParams joinConfirmParams;
|
|
joinConfirmParams.m_extendedPanId = m_joinParams.m_extendedPanId;
|
|
joinConfirmParams.m_enhancedBeacon = false; // hardcoded, no support
|
|
joinConfirmParams.m_macInterfaceIndex = 0; // hardcoded, no support
|
|
joinConfirmParams.m_networkAddress = params.m_assocShortAddr;
|
|
|
|
Ptr<NeighborTableEntry> entry;
|
|
|
|
if (params.m_status == MacStatus::SUCCESS)
|
|
{
|
|
joinConfirmParams.m_status = NwkStatus::SUCCESS;
|
|
joinConfirmParams.m_networkAddress = params.m_assocShortAddr;
|
|
|
|
// Update NWK NIB values
|
|
m_nwkNetworkAddress = params.m_assocShortAddr;
|
|
m_nwkExtendedPanId = m_joinParams.m_extendedPanId;
|
|
m_nwkPanId = m_associateParams.m_coordPanId;
|
|
|
|
// Update relationship
|
|
if (m_associateParams.m_coordAddrMode == lrwpan::AddressMode::EXT_ADDR)
|
|
{
|
|
if (m_nwkNeighborTable.LookUpEntry(m_associateParams.m_coordExtAddr, entry))
|
|
{
|
|
entry->SetRelationship(NBR_PARENT);
|
|
|
|
NS_LOG_DEBUG("[NLME-JOIN.request]:\n "
|
|
<< "Status: " << joinConfirmParams.m_status << " | PAN ID: 0x"
|
|
<< std::hex << m_nwkPanId << " | Extended PAN ID: 0x"
|
|
<< m_nwkExtendedPanId << std::dec);
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_ERROR("Entry not found while updating relationship");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_nwkNeighborTable.LookUpEntry(m_associateParams.m_coordShortAddr, entry))
|
|
{
|
|
entry->SetRelationship(NBR_PARENT);
|
|
|
|
NS_LOG_DEBUG("[NLME-JOIN.request]:\n "
|
|
<< "Status: " << joinConfirmParams.m_status << " | PAN ID: 0x"
|
|
<< std::hex << m_nwkPanId << " | Extended PAN ID: 0x"
|
|
<< m_nwkExtendedPanId << std::dec);
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_ERROR("Entry not found while updating relationship");
|
|
}
|
|
}
|
|
|
|
// TODO:m_nwkUpdateId
|
|
}
|
|
else
|
|
{
|
|
if (params.m_status == MacStatus::FULL_CAPACITY)
|
|
{
|
|
// Discard neighbor as potential parent
|
|
if (m_associateParams.m_coordAddrMode == lrwpan::AddressMode::EXT_ADDR)
|
|
{
|
|
if (m_nwkNeighborTable.LookUpEntry(m_associateParams.m_coordExtAddr, entry))
|
|
{
|
|
entry->SetPotentialParent(false);
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_ERROR("Neighbor not found when discarding as potential parent");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_nwkNeighborTable.LookUpEntry(m_associateParams.m_coordShortAddr, entry))
|
|
{
|
|
entry->SetPotentialParent(false);
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_ERROR("Neighbor not found when discarding as potential parent");
|
|
}
|
|
}
|
|
|
|
joinConfirmParams.m_status = NwkStatus::NEIGHBOR_TABLE_FULL;
|
|
}
|
|
else
|
|
{
|
|
joinConfirmParams.m_status = GetNwkStatus(params.m_status);
|
|
}
|
|
}
|
|
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_joinParams = {};
|
|
m_associateParams = {};
|
|
|
|
if (!m_nlmeJoinConfirmCallback.IsNull())
|
|
{
|
|
m_nlmeJoinConfirmCallback(joinConfirmParams);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::MlmeStartConfirm(MlmeStartConfirmParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
NwkStatus nwkConfirmStatus;
|
|
nwkConfirmStatus = GetNwkStatus(params.m_status);
|
|
|
|
if (nwkConfirmStatus != NwkStatus::SUCCESS)
|
|
{
|
|
m_nwkExtendedPanId = 0xffffffffffffffed;
|
|
m_nwkNetworkAddress = Mac16Address("ff:ff");
|
|
m_nwkPanId = 0xffff;
|
|
}
|
|
|
|
if (m_pendPrimitiveNwk == NLME_NETWORK_FORMATION)
|
|
{
|
|
NS_LOG_DEBUG("[NLME-NETWORK-FORMATION.request]:\n "
|
|
<< "Status: " << nwkConfirmStatus << " | PAN ID:" << std::hex << m_nwkPanId
|
|
<< " | Extended PAN ID: 0x" << m_nwkExtendedPanId << std::dec);
|
|
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_netFormParams = {};
|
|
m_netFormParamsGen = nullptr;
|
|
|
|
if (!m_nlmeNetworkFormationConfirmCallback.IsNull())
|
|
{
|
|
NlmeNetworkFormationConfirmParams confirmParams;
|
|
confirmParams.m_status = nwkConfirmStatus;
|
|
m_nlmeNetworkFormationConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
else if (m_pendPrimitiveNwk == NLME_START_ROUTER)
|
|
{
|
|
NS_LOG_DEBUG("[NLME-START-ROUTER.request]:\n "
|
|
<< "Status: " << nwkConfirmStatus << " | PAN ID: 0x" << std::hex << m_nwkPanId
|
|
<< " | Extended PAN ID: 0x" << m_nwkExtendedPanId << std::dec);
|
|
|
|
if (nwkConfirmStatus != NwkStatus::SUCCESS)
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_startRouterParams = {};
|
|
if (!m_nlmeStartRouterConfirmCallback.IsNull())
|
|
{
|
|
NlmeStartRouterConfirmParams confirmParams;
|
|
confirmParams.m_status = nwkConfirmStatus;
|
|
m_nlmeStartRouterConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UpdateBeaconPayloadLength();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::MlmeSetConfirm(MlmeSetConfirmParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this << params.id);
|
|
|
|
if (m_pendPrimitiveNwk == NLME_NETWORK_FORMATION)
|
|
{
|
|
if (params.m_status == MacStatus::SUCCESS &&
|
|
params.id == MacPibAttributeIdentifier::macShortAddress)
|
|
{
|
|
// Section (3.2.2.5.3 , 6.g)
|
|
// Getting this device MAC extended address using MLME-GET
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeGetRequest,
|
|
m_mac,
|
|
MacPibAttributeIdentifier::macExtendedAddress);
|
|
}
|
|
else if (params.m_status == MacStatus::SUCCESS &&
|
|
params.id == MacPibAttributeIdentifier::macBeaconPayload)
|
|
{
|
|
// Finalize Network Formation (Start network)
|
|
MlmeStartRequestParams startParams;
|
|
startParams.m_logCh = m_netFormParamsGen->channel;
|
|
startParams.m_logChPage = m_netFormParamsGen->page;
|
|
startParams.m_PanId = m_netFormParamsGen->panId;
|
|
startParams.m_bcnOrd = m_netFormParams.m_beaconOrder;
|
|
startParams.m_sfrmOrd = m_netFormParams.m_superFrameOrder;
|
|
startParams.m_battLifeExt = m_netFormParams.m_batteryLifeExtension;
|
|
startParams.m_coorRealgn = false;
|
|
startParams.m_panCoor = true;
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeStartRequest, m_mac, startParams);
|
|
}
|
|
else if (params.m_status == MacStatus::SUCCESS &&
|
|
params.id == MacPibAttributeIdentifier::macBeaconPayloadLength)
|
|
{
|
|
UpdateBeaconPayload();
|
|
}
|
|
else
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_netFormParams = {};
|
|
m_netFormParamsGen = nullptr;
|
|
|
|
if (!m_nlmeNetworkFormationConfirmCallback.IsNull())
|
|
{
|
|
NlmeNetworkFormationConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::STARTUP_FAILURE;
|
|
m_nlmeNetworkFormationConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
}
|
|
else if (m_pendPrimitiveNwk == NLME_JOIN_INDICATION)
|
|
{
|
|
if (params.m_status == MacStatus::SUCCESS &&
|
|
params.id == MacPibAttributeIdentifier::macBeaconPayloadLength)
|
|
{
|
|
UpdateBeaconPayload();
|
|
}
|
|
else
|
|
{
|
|
NlmeJoinIndicationParams joinIndParams = m_joinIndParams;
|
|
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_joinIndParams = {};
|
|
|
|
if (!m_nlmeJoinIndicationCallback.IsNull())
|
|
{
|
|
m_nlmeJoinIndicationCallback(joinIndParams);
|
|
}
|
|
}
|
|
}
|
|
else if (m_pendPrimitiveNwk == NLME_START_ROUTER)
|
|
{
|
|
if (params.m_status == MacStatus::SUCCESS &&
|
|
params.id == MacPibAttributeIdentifier::macBeaconPayloadLength)
|
|
{
|
|
UpdateBeaconPayload();
|
|
}
|
|
else if (params.m_status == MacStatus::SUCCESS &&
|
|
params.id == MacPibAttributeIdentifier::macBeaconPayload)
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_startRouterParams = {};
|
|
if (!m_nlmeStartRouterConfirmCallback.IsNull())
|
|
{
|
|
NlmeStartRouterConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::SUCCESS;
|
|
m_nlmeStartRouterConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_ERROR("Beacon payload update failed during a NLME-START-ROUTER.request");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::MlmeGetConfirm(MacStatus status,
|
|
MacPibAttributeIdentifier id,
|
|
Ptr<MacPibAttributes> attribute)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
// Update the values of attributes in the network layer
|
|
if (status == MacStatus::SUCCESS)
|
|
{
|
|
if (id == MacPibAttributeIdentifier::macExtendedAddress)
|
|
{
|
|
m_nwkIeeeAddress = attribute->macExtendedAddress;
|
|
}
|
|
else if (id == MacPibAttributeIdentifier::macShortAddress)
|
|
{
|
|
m_nwkNetworkAddress = attribute->macShortAddress;
|
|
}
|
|
else if (id == MacPibAttributeIdentifier::macPanId)
|
|
{
|
|
m_nwkPanId = attribute->macPanId;
|
|
}
|
|
else if (id == MacPibAttributeIdentifier::pCurrentChannel)
|
|
{
|
|
m_currentChannel = attribute->pCurrentChannel;
|
|
}
|
|
}
|
|
|
|
if (m_pendPrimitiveNwk == PendingPrimitiveNwk::NLME_NETWORK_FORMATION)
|
|
{
|
|
if (id == MacPibAttributeIdentifier::macExtendedAddress && status == MacStatus::SUCCESS)
|
|
{
|
|
// Section (3.2.2.5.3 , 6.g)
|
|
// Set nwkExtendedPanId and m_nwkIeeeAddress and nwkPanId
|
|
m_nwkExtendedPanId = m_nwkIeeeAddress.ConvertToInt();
|
|
m_nwkPanId = m_netFormParamsGen->panId;
|
|
|
|
// Configure the capability information of the PAN coordinator
|
|
CapabilityInformation capaInfo;
|
|
capaInfo.SetDeviceType(zigbee::MacDeviceType::ROUTER);
|
|
m_nwkCapabilityInformation = capaInfo.GetCapability();
|
|
|
|
// Set Beacon payload size followed by the beacon payload content
|
|
// before starting a network
|
|
// See Figure 3-37 Establishing a Network
|
|
// See also 3.6.7.
|
|
UpdateBeaconPayloadLength();
|
|
}
|
|
else
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_netFormParams = {};
|
|
m_netFormParamsGen = nullptr;
|
|
|
|
if (!m_nlmeNetworkFormationConfirmCallback.IsNull())
|
|
{
|
|
NlmeNetworkFormationConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::STARTUP_FAILURE;
|
|
m_nlmeNetworkFormationConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
}
|
|
else if (m_pendPrimitiveNwk == PendingPrimitiveNwk::NLME_JOIN && status == MacStatus::SUCCESS)
|
|
{
|
|
if (id == MacPibAttributeIdentifier::macShortAddress)
|
|
{
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeGetRequest,
|
|
m_mac,
|
|
MacPibAttributeIdentifier::macPanId);
|
|
}
|
|
else if (id == MacPibAttributeIdentifier::macPanId)
|
|
{
|
|
NlmeJoinConfirmParams joinConfirmParams;
|
|
joinConfirmParams.m_channelList = m_joinParams.m_scanChannelList;
|
|
joinConfirmParams.m_status = NwkStatus::SUCCESS;
|
|
joinConfirmParams.m_networkAddress = m_nwkNetworkAddress;
|
|
joinConfirmParams.m_extendedPanId = m_nwkExtendedPanId;
|
|
joinConfirmParams.m_enhancedBeacon = false;
|
|
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_joinParams = {};
|
|
|
|
if (!m_nlmeJoinConfirmCallback.IsNull())
|
|
{
|
|
m_nlmeJoinConfirmCallback(joinConfirmParams);
|
|
}
|
|
}
|
|
}
|
|
else if (m_pendPrimitiveNwk == PendingPrimitiveNwk::NLME_START_ROUTER &&
|
|
status == MacStatus::SUCCESS)
|
|
{
|
|
if (id == MacPibAttributeIdentifier::pCurrentChannel)
|
|
{
|
|
// TODO: MLME-START.request should be issue sequentially to all the interfaces in the
|
|
// nwkMacInterfaceTable (currently not supported), for the moment only a single
|
|
// interface is supported.
|
|
MlmeStartRequestParams startParams;
|
|
startParams.m_logCh = m_currentChannel;
|
|
startParams.m_logChPage = 0; // In zigbee, only page 0 is supported.
|
|
startParams.m_PanId = m_nwkPanId;
|
|
startParams.m_bcnOrd = m_startRouterParams.m_beaconOrder;
|
|
startParams.m_sfrmOrd = m_startRouterParams.m_superframeOrder;
|
|
startParams.m_battLifeExt = m_startRouterParams.m_batteryLifeExt;
|
|
startParams.m_coorRealgn = false;
|
|
startParams.m_panCoor = false;
|
|
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeStartRequest, m_mac, startParams);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::MlmeOrphanIndication(MlmeOrphanIndicationParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
Ptr<NeighborTableEntry> entry;
|
|
MlmeOrphanResponseParams respParams;
|
|
|
|
if (m_nwkNeighborTable.LookUpEntry(params.m_orphanAddr, entry))
|
|
{
|
|
respParams.m_assocMember = true;
|
|
respParams.m_orphanAddr = params.m_orphanAddr;
|
|
respParams.m_shortAddr = entry->GetNwkAddr();
|
|
|
|
// Temporarily store the NLME-JOIN.indications parameters that will be
|
|
// returned after the DIRECT_JOIN process concludes.
|
|
// (after MLME-COMM-STATUS.indication is received)
|
|
CapabilityInformation capability;
|
|
capability.SetReceiverOnWhenIdle(entry->IsRxOnWhenIdle());
|
|
|
|
if (entry->GetDeviceType() == NwkDeviceType::ZIGBEE_ROUTER)
|
|
{
|
|
capability.SetDeviceType(MacDeviceType::ROUTER);
|
|
}
|
|
else if (entry->GetDeviceType() == NwkDeviceType::ZIGBEE_ENDDEVICE)
|
|
{
|
|
capability.SetDeviceType(MacDeviceType::ENDDEVICE);
|
|
}
|
|
m_joinIndParams.m_capabilityInfo = capability.GetCapability();
|
|
m_joinIndParams.m_extendedAddress = params.m_orphanAddr;
|
|
m_joinIndParams.m_networkAddress = entry->GetNwkAddr();
|
|
m_joinIndParams.m_rejoinNetwork = DIRECT_OR_REJOIN;
|
|
|
|
NS_LOG_DEBUG("[NLME-JOIN.request]: ["
|
|
<< params.m_orphanAddr << " | " << entry->GetNwkAddr()
|
|
<< "] found in neighbor table, responding to orphaned device");
|
|
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeOrphanResponse, m_mac, respParams);
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::MlmeCommStatusIndication(MlmeCommStatusIndicationParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
// Return the results to the next layer of the router or coordinator
|
|
// only after a SUCCESSFUL join to the network.
|
|
if (params.m_status == MacStatus::SUCCESS)
|
|
{
|
|
if (params.m_dstExtAddr == m_joinIndParams.m_extendedAddress &&
|
|
m_joinIndParams.m_rejoinNetwork == DIRECT_OR_REJOIN)
|
|
{
|
|
NlmeJoinIndicationParams joinIndParams = m_joinIndParams;
|
|
m_joinIndParams = {};
|
|
|
|
if (!m_nlmeJoinIndicationCallback.IsNull())
|
|
{
|
|
m_nlmeJoinIndicationCallback(joinIndParams);
|
|
}
|
|
}
|
|
else if (params.m_dstExtAddr == m_joinIndParams.m_extendedAddress &&
|
|
m_joinIndParams.m_rejoinNetwork == ASSOCIATION)
|
|
{
|
|
m_pendPrimitiveNwk = NLME_JOIN_INDICATION;
|
|
UpdateBeaconPayloadLength();
|
|
}
|
|
}
|
|
|
|
// TODO: Handle other situations for MlmeCommStatusIndication according
|
|
// to the status and primitive in use.
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::MlmeBeaconNotifyIndication(MlmeBeaconNotifyIndicationParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
// Zigbee specification Section 3.6.1.3
|
|
// Update Neighbor Table with information of the beacon payload
|
|
// during a network-discovery
|
|
|
|
if ((params.m_sdu->GetSize() == 0) ||
|
|
(params.m_panDescriptor.m_coorAddrMode != lrwpan::AddressMode::SHORT_ADDR))
|
|
{
|
|
// The beacon do not contain beacon payload or is for a different network
|
|
// stop any further process.
|
|
return;
|
|
}
|
|
|
|
ZigbeeBeaconPayload beaconPayload;
|
|
params.m_sdu->RemoveHeader(beaconPayload);
|
|
|
|
if (beaconPayload.GetProtocolId() != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// TODO: Add a Permit to join, stack profile , update id and capability check
|
|
|
|
if (m_pendPrimitiveNwk == NLME_NET_DISCV)
|
|
{
|
|
// Keep a network descriptor list from the information in the beacon
|
|
// to later on pass to the next higher layer when the network-discovery
|
|
// process is over (NLME-NETWORK-DISCOVERY.confirm)
|
|
NetworkDescriptor descriptor;
|
|
descriptor.m_extPanId = beaconPayload.GetExtPanId();
|
|
descriptor.m_panId = params.m_panDescriptor.m_coorPanId;
|
|
descriptor.m_updateId = 0; // TODO: unknown
|
|
descriptor.m_logCh = params.m_panDescriptor.m_logCh;
|
|
descriptor.m_stackProfile = static_cast<StackProfile>(beaconPayload.GetStackProfile());
|
|
descriptor.m_zigbeeVersion = beaconPayload.GetProtocolId();
|
|
|
|
SuperframeInformation superframe(params.m_panDescriptor.m_superframeSpec);
|
|
descriptor.m_beaconOrder = superframe.GetBeaconOrder();
|
|
descriptor.m_superframeOrder = superframe.GetFrameOrder();
|
|
descriptor.m_permitJoining = superframe.IsAssocPermit();
|
|
|
|
descriptor.m_routerCapacity = beaconPayload.GetRouterCapacity();
|
|
descriptor.m_endDeviceCapacity = beaconPayload.GetEndDevCapacity();
|
|
m_networkDescriptorList.emplace_back(descriptor);
|
|
|
|
// Keep track of the pan id (16 bits) and the extended PAN id for
|
|
// future join (association) procedures.
|
|
m_panIdTable.AddEntry(descriptor.m_extPanId, descriptor.m_panId);
|
|
// NOTE: In Zigbee all PAN coordinators or routers work with a
|
|
// SOURCE short address addressing mode, therefore the PAN descriptors only
|
|
// contain the short address.
|
|
NS_LOG_DEBUG("Received beacon frame from [" << params.m_panDescriptor.m_coorShortAddr
|
|
<< "]");
|
|
}
|
|
|
|
Ptr<NeighborTableEntry> entry;
|
|
if (m_nwkNeighborTable.LookUpEntry(params.m_panDescriptor.m_coorShortAddr, entry))
|
|
{
|
|
// Update Neighbor table with the info of the received beacon
|
|
|
|
entry->SetNwkAddr(params.m_panDescriptor.m_coorShortAddr);
|
|
entry->SetTimeoutCounter(Seconds(15728640));
|
|
entry->SetDevTimeout(Minutes(RequestedTimeoutField[m_nwkEndDeviceTimeoutDefault]));
|
|
entry->SetLqi(params.m_panDescriptor.m_linkQuality);
|
|
entry->SetOutgoingCost(GetLQINonLinearValue(params.m_panDescriptor.m_linkQuality));
|
|
// m_nwkNeighborTable.Update(params.m_panDescriptor.m_coorShortAddr, entry);
|
|
// TODO: Update other fields if necessary and
|
|
// Additional and optional fields.
|
|
}
|
|
else
|
|
{
|
|
// Add a new entry to the neighbor table, information comes from
|
|
// the MAC PAN descriptor and the beacon payload received.
|
|
NwkDeviceType devType;
|
|
if (params.m_panDescriptor.m_coorShortAddr == Mac16Address("00:00"))
|
|
{
|
|
devType = NwkDeviceType::ZIGBEE_COORDINATOR;
|
|
}
|
|
else
|
|
{
|
|
devType = NwkDeviceType::ZIGBEE_ROUTER;
|
|
}
|
|
|
|
// Create neighbor table entry with the basic fields
|
|
Ptr<NeighborTableEntry> newEntry =
|
|
Create<NeighborTableEntry>(Mac64Address("FF:FF:FF:FF:FF:FF:FF:FF"),
|
|
params.m_panDescriptor.m_coorShortAddr,
|
|
devType,
|
|
true,
|
|
0,
|
|
Seconds(15728640),
|
|
Minutes(RequestedTimeoutField[m_nwkEndDeviceTimeoutDefault]),
|
|
NBR_NONE,
|
|
0,
|
|
params.m_panDescriptor.m_linkQuality,
|
|
GetLQINonLinearValue(params.m_panDescriptor.m_linkQuality),
|
|
0,
|
|
false,
|
|
0);
|
|
|
|
// If necessary add information to the
|
|
// additional and optional fields. Currently only 2 additional fields are added:
|
|
newEntry->SetExtPanId(beaconPayload.GetExtPanId());
|
|
newEntry->SetLogicalCh(params.m_panDescriptor.m_logCh);
|
|
|
|
m_nwkNeighborTable.AddEntry(newEntry);
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::MlmeAssociateIndication(MlmeAssociateIndicationParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
// Joining Procedure through Association (Parent procedure)
|
|
// Zigbee Specification 3.6.1.4.1
|
|
|
|
CapabilityInformation receivedCapability(params.capabilityInfo);
|
|
auto devType = static_cast<NwkDeviceType>(receivedCapability.GetDeviceType());
|
|
|
|
Ptr<NeighborTableEntry> entry;
|
|
if (m_nwkNeighborTable.LookUpEntry(params.m_extDevAddr, entry))
|
|
{
|
|
if (entry->GetDeviceType() == devType)
|
|
{
|
|
MlmeAssociateResponseParams responseParams;
|
|
responseParams.m_status = MacStatus::SUCCESS;
|
|
responseParams.m_assocShortAddr = entry->GetNwkAddr();
|
|
responseParams.m_extDevAddr = entry->GetExtAddr();
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeAssociateResponse, m_mac, responseParams);
|
|
}
|
|
else
|
|
{
|
|
m_nwkNeighborTable.Delete(params.m_extDevAddr);
|
|
MlmeAssociateIndication(params);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Device currently do not exist in coordinator,
|
|
// allocate an address and add to neighbor table.
|
|
|
|
Mac16Address allocatedAddr;
|
|
if (receivedCapability.IsAllocateAddrOn())
|
|
{
|
|
allocatedAddr = AllocateNetworkAddress();
|
|
}
|
|
else
|
|
{
|
|
// The device is associated but it will only use its
|
|
// extended address (EUI-64 also known as IEEE Address)
|
|
allocatedAddr = Mac16Address("FF:FE");
|
|
}
|
|
|
|
CapabilityInformation capability(params.capabilityInfo);
|
|
|
|
Ptr<NeighborTableEntry> newEntry =
|
|
Create<NeighborTableEntry>(params.m_extDevAddr,
|
|
allocatedAddr,
|
|
devType,
|
|
capability.IsReceiverOnWhenIdle(),
|
|
0,
|
|
Seconds(15728640),
|
|
Minutes(RequestedTimeoutField[m_nwkEndDeviceTimeoutDefault]),
|
|
NBR_CHILD,
|
|
0,
|
|
params.lqi,
|
|
0,
|
|
0,
|
|
true,
|
|
0);
|
|
// Optional parameters
|
|
newEntry->SetExtPanId(m_nwkExtendedPanId);
|
|
|
|
MlmeAssociateResponseParams responseParams;
|
|
responseParams.m_extDevAddr = params.m_extDevAddr;
|
|
|
|
if (m_nwkNeighborTable.AddEntry(newEntry))
|
|
{
|
|
responseParams.m_status = MacStatus::SUCCESS;
|
|
responseParams.m_assocShortAddr = allocatedAddr;
|
|
|
|
// Temporarily store the NLME-JOIN.indications parameters that will be
|
|
// returned after the association process concludes.
|
|
// (after MLME-COMM-STATUS.indication received and beacon payload updated)
|
|
m_joinIndParams.m_capabilityInfo = receivedCapability.GetCapability();
|
|
m_joinIndParams.m_extendedAddress = params.m_extDevAddr;
|
|
m_joinIndParams.m_networkAddress = allocatedAddr;
|
|
m_joinIndParams.m_rejoinNetwork = ASSOCIATION;
|
|
}
|
|
else
|
|
{
|
|
responseParams.m_status = MacStatus::FULL_CAPACITY;
|
|
responseParams.m_assocShortAddr = Mac16Address("FF:FF");
|
|
}
|
|
|
|
NS_LOG_DEBUG("\n "
|
|
<< "Storing an Associate response command with the allocated address "
|
|
<< "[" << responseParams.m_assocShortAddr << "]");
|
|
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeAssociateResponse, m_mac, responseParams);
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::NldeDataRequest(NldeDataRequestParams params, Ptr<Packet> packet)
|
|
{
|
|
NS_LOG_FUNCTION(this << packet);
|
|
|
|
if (params.m_dstAddr == m_nwkNetworkAddress)
|
|
{
|
|
NS_LOG_DEBUG("The source and the destination of the route request are the same!");
|
|
return;
|
|
}
|
|
|
|
// Zigbee specification r22.1.0, Section 3.2.1.1.3 and Section 3.6.2.1
|
|
// check that we are associated
|
|
if (m_nwkNetworkAddress == "FF:FF")
|
|
{
|
|
NS_LOG_DEBUG("Cannot send data, the device is not currently associated");
|
|
|
|
if (!m_nldeDataConfirmCallback.IsNull())
|
|
{
|
|
NldeDataConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::INVALID_REQUEST;
|
|
confirmParams.m_txTime = Simulator::Now();
|
|
confirmParams.m_nsduHandle = params.m_nsduHandle;
|
|
m_nldeDataConfirmCallback(confirmParams);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Constructing the NPDU (Zigbee specification r22.1.0, Section 3.2.1.1.3 and Section 3.6.2.1)
|
|
ZigbeeNwkHeader nwkHeader;
|
|
nwkHeader.SetFrameType(DATA);
|
|
nwkHeader.SetProtocolVer(3);
|
|
nwkHeader.SetDiscoverRoute(static_cast<DiscoverRouteType>(params.m_discoverRoute));
|
|
nwkHeader.SetDstAddr(params.m_dstAddr);
|
|
|
|
if (params.m_useAlias)
|
|
{
|
|
nwkHeader.SetSrcAddr(params.m_aliasSrcAddr);
|
|
nwkHeader.SetSeqNum(params.m_aliasSeqNumber.GetValue());
|
|
}
|
|
else
|
|
{
|
|
nwkHeader.SetSrcAddr(m_nwkNetworkAddress);
|
|
nwkHeader.SetSeqNum(m_nwkSequenceNumber.GetValue());
|
|
}
|
|
|
|
if (params.m_radius == 0)
|
|
{
|
|
nwkHeader.SetRadius(m_nwkMaxDepth * 2);
|
|
}
|
|
else
|
|
{
|
|
nwkHeader.SetRadius(params.m_radius);
|
|
}
|
|
|
|
if (params.m_securityEnable)
|
|
{
|
|
// TODO: Secure processing (Section 3.6.2.1)
|
|
NS_ABORT_MSG("Security processing is currently not supported");
|
|
}
|
|
|
|
// Check the current device capabilities
|
|
CapabilityInformation capability;
|
|
capability.SetCapability(m_nwkCapabilityInformation);
|
|
|
|
if (capability.GetDeviceType() == ENDDEVICE)
|
|
{
|
|
nwkHeader.SetEndDeviceInitiator();
|
|
}
|
|
|
|
if (params.m_dstAddrMode == MCST)
|
|
{
|
|
nwkHeader.SetMulticast();
|
|
// TODO:
|
|
// set the nwkHeader multicast control according to
|
|
// the values of the non-member radios parameter
|
|
// See 3.2.1.1.3
|
|
}
|
|
|
|
packet->AddHeader(nwkHeader);
|
|
|
|
if (capability.GetDeviceType() == ROUTER)
|
|
{
|
|
if (params.m_dstAddrMode == MCST)
|
|
{
|
|
// The destination is MULTICAST (See 3.6.2)
|
|
NS_ABORT_MSG("Multicast is currently not supported");
|
|
// TODO
|
|
return;
|
|
}
|
|
else if (IsBroadcastAddress(params.m_dstAddr))
|
|
{
|
|
// The destination is BROADCAST (See 3.6.5)
|
|
SendDataBcst(packet, params.m_nsduHandle);
|
|
}
|
|
else
|
|
{
|
|
// The destination is UNICAST (See 3.6.3.3)
|
|
SendDataUcst(packet, params.m_nsduHandle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The device is an END DEVICE
|
|
// direct message to its Parent device (Coordinator)
|
|
Ptr<NeighborTableEntry> entry;
|
|
if (m_nwkNeighborTable.GetParent(entry))
|
|
{
|
|
// Buffer a copy of the DATA packet that will be transmitted
|
|
// for handling after transmission (i.e. NSDE-DATA.confirm)
|
|
BufferTxPkt(packet->Copy(), m_macHandle.GetValue(), params.m_nsduHandle);
|
|
|
|
McpsDataRequestParams mcpsDataparams;
|
|
mcpsDataparams.m_txOptions = 0x01; // Acknowledment on.
|
|
mcpsDataparams.m_dstPanId = m_nwkPanId;
|
|
mcpsDataparams.m_msduHandle = m_macHandle.GetValue();
|
|
mcpsDataparams.m_srcAddrMode = SHORT_ADDR;
|
|
mcpsDataparams.m_dstAddrMode = SHORT_ADDR;
|
|
mcpsDataparams.m_dstAddr = entry->GetNwkAddr();
|
|
m_macHandle++;
|
|
Simulator::ScheduleNow(&LrWpanMacBase::McpsDataRequest, m_mac, mcpsDataparams, packet);
|
|
}
|
|
else
|
|
{
|
|
// Section 3.6.3.7.1
|
|
// Link failure with Parent device
|
|
// TODO
|
|
/*
|
|
if (!m_nlmeNwkStatusIndicationCallback.IsNull())
|
|
{
|
|
NlmeNetworkStatusIndication indicationParams;
|
|
|
|
m_networkStatusCode = PARENT_LINK_FAILURE;
|
|
m_nlmeNwkStatusIndicationCallback(confirmParams);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::NlmeNetworkFormationRequest(NlmeNetworkFormationRequestParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
NS_ASSERT_MSG(m_netFormParams.m_scanChannelList.channelPageCount ==
|
|
m_netFormParams.m_scanChannelList.channelsField.size(),
|
|
"channelsField and its channelPageCount size do not match "
|
|
"in networkFormationParams");
|
|
|
|
if (!m_nwkcCoordinatorCapable)
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_netFormParams = {};
|
|
m_netFormParamsGen = nullptr;
|
|
|
|
if (!m_nlmeNetworkFormationConfirmCallback.IsNull())
|
|
{
|
|
NlmeNetworkFormationConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::INVALID_REQUEST;
|
|
m_nlmeNetworkFormationConfirmCallback(confirmParams);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (params.m_distributedNetwork)
|
|
{
|
|
// Zigbee Specification r22.1.0, 3.2.2.5 , 3)
|
|
// Verify Distributed Network Address is in a valid range.
|
|
// TODO: Verify the address is not > 0xFFF7
|
|
if (params.m_distributedNetwork == Mac16Address("00:00"))
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_netFormParams = {};
|
|
m_netFormParamsGen = nullptr;
|
|
|
|
if (!m_nlmeNetworkFormationConfirmCallback.IsNull())
|
|
{
|
|
NlmeNetworkFormationConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::INVALID_REQUEST;
|
|
m_nlmeNetworkFormationConfirmCallback(confirmParams);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 4. On receipt of this primitive the NLME shall first validate the
|
|
// ChannelListStructure parameter according to section 3.2.2.2.2.
|
|
// (if nwkMacInterfaceTable support is added)
|
|
// If validation fails the NLME-NETWORK-FORMATION.confirm
|
|
// primitive shall be issued with a Status parameter set to INVALID_PARAMETER.
|
|
|
|
if (params.m_scanChannelList.channelPageCount != 1)
|
|
{
|
|
NS_FATAL_ERROR("Multi page scanning not supported");
|
|
}
|
|
|
|
// Only page 0 is supported (O-QPSK 250 kbps)
|
|
// Take 5 MSB bits: b27-b31 to check the page
|
|
uint32_t page = (params.m_scanChannelList.channelsField[0] >> 27) & (0x01F);
|
|
|
|
if (page != 0)
|
|
{
|
|
NS_FATAL_ERROR("PHY band not supported (Only page 0 is supported)");
|
|
}
|
|
|
|
uint8_t channelsCount = 0;
|
|
for (int i = 11; i <= 26; i++)
|
|
{
|
|
channelsCount += (params.m_scanChannelList.channelsField[0] >> i) & 1;
|
|
}
|
|
|
|
m_pendPrimitiveNwk = NLME_NETWORK_FORMATION;
|
|
m_netFormParams = params;
|
|
|
|
if (channelsCount == 1)
|
|
{
|
|
// There is only 1 channel, skip energy scan and go directly to
|
|
// active scan instead
|
|
MlmeScanRequestParams mlmeParams;
|
|
mlmeParams.m_chPage = page;
|
|
mlmeParams.m_scanChannels = params.m_scanChannelList.channelsField[0];
|
|
mlmeParams.m_scanDuration = params.m_scanDuration;
|
|
mlmeParams.m_scanType = MLMESCAN_ACTIVE;
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeScanRequest, m_mac, mlmeParams);
|
|
}
|
|
else if (channelsCount > 1)
|
|
{
|
|
MlmeScanRequestParams mlmeParams;
|
|
mlmeParams.m_chPage = page;
|
|
mlmeParams.m_scanChannels = params.m_scanChannelList.channelsField[0];
|
|
mlmeParams.m_scanDuration = params.m_scanDuration;
|
|
mlmeParams.m_scanType = MLMESCAN_ED;
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeScanRequest, m_mac, mlmeParams);
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::NlmeRouteDiscoveryRequest(NlmeRouteDiscoveryRequestParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
if (params.m_dstAddr == m_nwkNetworkAddress && params.m_dstAddrMode == UCST_BCST)
|
|
{
|
|
NS_FATAL_ERROR("The source and the destination of the route request are the same!");
|
|
return;
|
|
}
|
|
|
|
// (See 3.2.2.33.3)
|
|
// - Check the device has routing capacity
|
|
// - Check the device dstAddrMode != NO_ADDRESS && dst != Broadcast address
|
|
if (params.m_dstAddrMode != NO_ADDRESS && IsBroadcastAddress(params.m_dstAddr))
|
|
{
|
|
if (!m_nlmeRouteDiscoveryConfirmCallback.IsNull())
|
|
{
|
|
NlmeRouteDiscoveryConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::INVALID_REQUEST;
|
|
m_nlmeRouteDiscoveryConfirmCallback(confirmParams);
|
|
}
|
|
return;
|
|
}
|
|
|
|
CapabilityInformation capability;
|
|
capability.SetCapability(m_nwkCapabilityInformation);
|
|
if (capability.GetDeviceType() != ROUTER && params.m_dstAddrMode != NO_ADDRESS)
|
|
{
|
|
if (!m_nlmeRouteDiscoveryConfirmCallback.IsNull())
|
|
{
|
|
NlmeRouteDiscoveryConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::ROUTE_ERROR;
|
|
m_nlmeRouteDiscoveryConfirmCallback(confirmParams);
|
|
}
|
|
return;
|
|
}
|
|
|
|
m_pendPrimitiveNwk = NLME_ROUTE_DISCOVERY;
|
|
|
|
ZigbeeNwkHeader nwkHeader;
|
|
nwkHeader.SetFrameType(NWK_COMMAND);
|
|
nwkHeader.SetProtocolVer(m_nwkcProtocolVersion);
|
|
nwkHeader.SetDiscoverRoute(DiscoverRouteType::ENABLE_ROUTE_DISCOVERY);
|
|
// See r22.1.0, Table 3-69
|
|
// Set destination to broadcast (all routers and coordinator)
|
|
nwkHeader.SetDstAddr(Mac16Address("FF:FC"));
|
|
nwkHeader.SetSrcAddr(m_nwkNetworkAddress);
|
|
nwkHeader.SetSeqNum(m_nwkSequenceNumber.GetValue());
|
|
|
|
ZigbeePayloadRouteRequestCommand payload;
|
|
payload.SetRouteReqId(m_routeRequestId.GetValue());
|
|
payload.SetPathCost(0);
|
|
|
|
if (params.m_dstAddrMode == UCST_BCST)
|
|
{
|
|
// Set the rest of the nwkHeader and command payload parameters
|
|
// as described in Zigbee specification, Section 3.2.2.33.3
|
|
if (params.m_radius == 0)
|
|
{
|
|
nwkHeader.SetRadius(m_nwkMaxDepth * 2);
|
|
}
|
|
else
|
|
{
|
|
nwkHeader.SetRadius(params.m_radius);
|
|
}
|
|
|
|
payload.SetDstAddr(params.m_dstAddr);
|
|
|
|
Mac16Address nextHop;
|
|
RouteDiscoveryStatus routeStatus =
|
|
FindNextHop(m_nwkNetworkAddress, 0, nwkHeader, payload, nextHop);
|
|
|
|
if (routeStatus == ROUTE_FOUND)
|
|
{
|
|
if (!m_nlmeRouteDiscoveryConfirmCallback.IsNull())
|
|
{
|
|
NlmeRouteDiscoveryConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::SUCCESS;
|
|
m_nlmeRouteDiscoveryConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
else if (routeStatus == ROUTE_NOT_FOUND)
|
|
{
|
|
// Route not found. Route marked as DISCOVER UNDERWAY,
|
|
// we initiate route discovery.
|
|
Simulator::Schedule(MilliSeconds(m_rreqJitter->GetValue()),
|
|
&ZigbeeNwk::SendRREQ,
|
|
this,
|
|
nwkHeader,
|
|
payload,
|
|
m_nwkcInitialRREQRetries);
|
|
|
|
m_nwkSequenceNumber++;
|
|
m_routeRequestId++;
|
|
}
|
|
}
|
|
else if (params.m_dstAddrMode == MCST)
|
|
{
|
|
NS_ABORT_MSG("Multicast Route discovery not supported");
|
|
}
|
|
else if (params.m_dstAddrMode == NO_ADDRESS)
|
|
{
|
|
// Many-to-one route discovery.
|
|
// (See Last paragraph of Zigbee Specification, Section 3.6.3.5.1)
|
|
m_nwkIsConcentrator = true;
|
|
|
|
nwkHeader.SetRadius(m_nwkConcentratorRadius);
|
|
|
|
payload.SetDstAddr(Mac16Address("FF:FF"));
|
|
if (params.m_noRouteCache)
|
|
{
|
|
payload.SetCmdOptManyToOneField(ManyToOne::NO_ROUTE_RECORD);
|
|
}
|
|
else
|
|
{
|
|
payload.SetCmdOptManyToOneField(ManyToOne::ROUTE_RECORD);
|
|
}
|
|
|
|
RouteDiscoveryStatus routeStatus =
|
|
ProcessManyToOneRoute(m_nwkNetworkAddress, 0, nwkHeader, payload);
|
|
|
|
if (routeStatus == MANY_TO_ONE_ROUTE || routeStatus == ROUTE_UPDATED)
|
|
{
|
|
// TODO if nwkConcentratorDiscoveryTime != 0, schedule
|
|
// RREQ every nwkConcentratorDiscovery time.
|
|
|
|
Simulator::Schedule(MilliSeconds(m_rreqJitter->GetValue()),
|
|
&ZigbeeNwk::SendRREQ,
|
|
this,
|
|
nwkHeader,
|
|
payload,
|
|
0);
|
|
m_nwkSequenceNumber++;
|
|
m_routeRequestId++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::NlmeNetworkDiscoveryRequest(NlmeNetworkDiscoveryRequestParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
if (params.m_scanDuration > 14)
|
|
{
|
|
NS_FATAL_ERROR("Scan duration must be an int between 0 and 14");
|
|
}
|
|
|
|
if (params.m_scanChannelList.channelPageCount != params.m_scanChannelList.channelsField.size())
|
|
{
|
|
NS_FATAL_ERROR("In scanChannelList parameter, channelPageCount "
|
|
"and the channelsField structure size does not match");
|
|
}
|
|
|
|
// TODO: Add support to scan other MAC interfaces, for the moment
|
|
// only a single interface and only Page 0 is supported (PHY O-QPSK 250 kbps)
|
|
|
|
if (params.m_scanChannelList.channelsField.size() != 1)
|
|
{
|
|
NS_FATAL_ERROR("Only a single MAC interface supported");
|
|
}
|
|
|
|
uint8_t page = (params.m_scanChannelList.channelsField[0] >> 27) & (0x01F);
|
|
if (page != 0)
|
|
{
|
|
NS_FATAL_ERROR("Only Page 0 (O-QPSK 250 kbps) is supported.");
|
|
}
|
|
|
|
m_pendPrimitiveNwk = NLME_NET_DISCV;
|
|
|
|
MlmeScanRequestParams scanParams;
|
|
scanParams.m_chPage = 0; // Only page 0 is supported.
|
|
scanParams.m_scanChannels = params.m_scanChannelList.channelsField[0];
|
|
scanParams.m_scanDuration = params.m_scanDuration;
|
|
scanParams.m_scanType = MLMESCAN_ACTIVE;
|
|
|
|
NS_LOG_DEBUG("Active scanning started, "
|
|
<< " on page " << page << " and channels 0x" << std::hex
|
|
<< params.m_scanChannelList.channelsField[0] << std::dec);
|
|
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeScanRequest, m_mac, scanParams);
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::NlmeDirectJoinRequest(NlmeDirectJoinRequestParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
// TODO: Check the device is router or coordinator, send invalid_request
|
|
// status otherwise. See 3.6.1.4.3.
|
|
|
|
Ptr<NeighborTableEntry> entry;
|
|
if (m_nwkNeighborTable.LookUpEntry(params.m_deviceAddr, entry))
|
|
{
|
|
NS_LOG_WARN("[NLME-DIRECT-JOIN.request]: "
|
|
"Device already present in neighbor table. ");
|
|
|
|
if (!m_nlmeDirectJoinConfirmCallback.IsNull())
|
|
{
|
|
NlmeDirectJoinConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::ALREADY_PRESENT;
|
|
confirmParams.m_deviceAddr = params.m_deviceAddr;
|
|
m_nlmeDirectJoinConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CapabilityInformation capaInfo;
|
|
capaInfo.SetCapability(params.m_capabilityInfo);
|
|
|
|
Mac16Address allocatedAddr;
|
|
if (capaInfo.IsAllocateAddrOn())
|
|
{
|
|
allocatedAddr = AllocateNetworkAddress();
|
|
}
|
|
else
|
|
{
|
|
// The device is associated but it will only use its
|
|
// extended address (EUI-64 also known as IEEE Address)
|
|
allocatedAddr = Mac16Address("FF:FE");
|
|
}
|
|
|
|
NwkDeviceType devType;
|
|
if (capaInfo.GetDeviceType() == MacDeviceType::ROUTER)
|
|
{
|
|
devType = NwkDeviceType::ZIGBEE_ROUTER;
|
|
}
|
|
else
|
|
{
|
|
devType = NwkDeviceType::ZIGBEE_ENDDEVICE;
|
|
}
|
|
|
|
Ptr<NeighborTableEntry> newEntry =
|
|
Create<NeighborTableEntry>(params.m_deviceAddr,
|
|
allocatedAddr,
|
|
devType,
|
|
capaInfo.IsReceiverOnWhenIdle(),
|
|
0,
|
|
Seconds(15728640),
|
|
Minutes(RequestedTimeoutField[m_nwkEndDeviceTimeoutDefault]),
|
|
NBR_CHILD,
|
|
0,
|
|
255,
|
|
0,
|
|
0,
|
|
true,
|
|
0);
|
|
|
|
NlmeDirectJoinConfirmParams confirmParams;
|
|
|
|
if (m_nwkNeighborTable.AddEntry(newEntry))
|
|
{
|
|
NS_LOG_DEBUG("Device added to neighbor table (" << m_nwkNeighborTable.GetSize()
|
|
<< ") with address [" << allocatedAddr
|
|
<< " | " << params.m_deviceAddr << "]");
|
|
if (!m_nlmeDirectJoinConfirmCallback.IsNull())
|
|
{
|
|
confirmParams.m_status = NwkStatus::SUCCESS;
|
|
confirmParams.m_deviceAddr = params.m_deviceAddr;
|
|
m_nlmeDirectJoinConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_WARN("Error, neighbor table is full");
|
|
if (!m_nlmeDirectJoinConfirmCallback.IsNull())
|
|
{
|
|
confirmParams.m_status = NwkStatus::NEIGHBOR_TABLE_FULL;
|
|
confirmParams.m_deviceAddr = params.m_deviceAddr;
|
|
m_nlmeDirectJoinConfirmCallback(confirmParams);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::NlmeJoinRequest(NlmeJoinRequestParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
if (params.m_scanDuration > 14)
|
|
{
|
|
NS_FATAL_ERROR("Scan duration must be an int between 0 and 14");
|
|
}
|
|
|
|
if (params.m_scanChannelList.channelPageCount != params.m_scanChannelList.channelsField.size())
|
|
{
|
|
NS_FATAL_ERROR("In scanChannelList parameter, channelPageCount "
|
|
"and the channelsField structure size does not match");
|
|
}
|
|
|
|
// TODO: Add support to scan other MAC interfaces, for the moment
|
|
// only a single interface and only Page 0 is supported (PHY O-QPSK 250 kbps)
|
|
|
|
// TODO: Only devices who have not join another network can call JOIN.
|
|
|
|
if (params.m_scanChannelList.channelsField.size() != 1)
|
|
{
|
|
NS_FATAL_ERROR("Only a single MAC interface supported");
|
|
}
|
|
|
|
uint8_t page = (params.m_scanChannelList.channelsField[0] >> 27) & (0x01F);
|
|
if (page != 0)
|
|
{
|
|
NS_FATAL_ERROR("Only Page 0 (O-QPSK 250 kbps) is supported.");
|
|
}
|
|
|
|
m_pendPrimitiveNwk = NLME_JOIN;
|
|
m_joinParams = params;
|
|
|
|
if (params.m_rejoinNetwork == DIRECT_OR_REJOIN)
|
|
{
|
|
// Zigbee specification r22.1.0 Section 3.6.1.4.3.1,
|
|
// Child procedure for joining or re-joining a network through
|
|
// orphaning (DIRECT JOIN procedure).
|
|
|
|
MlmeScanRequestParams scanParams;
|
|
scanParams.m_chPage = page;
|
|
scanParams.m_scanChannels = params.m_scanChannelList.channelsField[0];
|
|
|
|
// Note: Scan duration is fixed to a macResponseWaitTime in an Orphan scan
|
|
// (i.e. It does not use the scanDuration parameter)
|
|
scanParams.m_scanType = MLMESCAN_ORPHAN;
|
|
NS_LOG_DEBUG("Orphan scanning started, "
|
|
<< "sending orphan notifications on page " << page << " and channels "
|
|
<< std::hex << params.m_scanChannelList.channelsField[0]);
|
|
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeScanRequest, m_mac, scanParams);
|
|
}
|
|
else if (params.m_rejoinNetwork == ASSOCIATION)
|
|
{
|
|
// Check if we have the MAC pan id info recorded during the discovery process
|
|
uint16_t panId;
|
|
if (!m_panIdTable.GetEntry(params.m_extendedPanId, panId))
|
|
{
|
|
NS_LOG_ERROR("Error PAN id of neighbor device not found");
|
|
}
|
|
|
|
// Zigbee specification r22.1.0 Section 3.6.1.4.1
|
|
// Child procedure for joining a network through ASSOCIATION.
|
|
|
|
NlmeJoinConfirmParams joinConfirmParams;
|
|
Ptr<NeighborTableEntry> bestParentEntry;
|
|
|
|
if (m_nwkNeighborTable.LookUpForBestParent(params.m_extendedPanId, bestParentEntry))
|
|
{
|
|
MlmeAssociateRequestParams assocParams;
|
|
m_nwkCapabilityInformation = params.m_capabilityInfo;
|
|
|
|
assocParams.m_chNum = bestParentEntry->GetLogicalCh();
|
|
assocParams.m_chPage = 0; // Zigbee assumes Page is always 0
|
|
assocParams.m_capabilityInfo = params.m_capabilityInfo;
|
|
assocParams.m_coordPanId = panId;
|
|
|
|
if (bestParentEntry->GetNwkAddr() != Mac16Address("FF:FE"))
|
|
{
|
|
assocParams.m_coordAddrMode = lrwpan::AddressMode::SHORT_ADDR;
|
|
assocParams.m_coordShortAddr = bestParentEntry->GetNwkAddr();
|
|
NS_LOG_DEBUG("\n "
|
|
<< "Send Association Request [" << bestParentEntry->GetNwkAddr()
|
|
<< "] | PAN ID: " << std::hex << "0x" << panId
|
|
<< " | Extended PAN ID: 0x" << params.m_extendedPanId << std::dec);
|
|
}
|
|
else
|
|
{
|
|
assocParams.m_coordAddrMode = lrwpan::AddressMode::EXT_ADDR;
|
|
assocParams.m_coordExtAddr = bestParentEntry->GetExtAddr();
|
|
NS_LOG_DEBUG("Send Assoc. Req. to [" << bestParentEntry->GetNwkAddr()
|
|
<< "] in \nPAN id and Ext PAN id: " << std::hex
|
|
<< "(0x" << panId << " | 0x"
|
|
<< params.m_extendedPanId << ")" << std::dec);
|
|
}
|
|
|
|
m_nwkParentInformation = 0;
|
|
m_nwkCapabilityInformation = params.m_capabilityInfo;
|
|
|
|
// Temporarily store MLME-ASSOCIATE.request parameters until the JOIN process concludes
|
|
m_associateParams = assocParams;
|
|
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeAssociateRequest, m_mac, assocParams);
|
|
}
|
|
else
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_joinParams = {};
|
|
|
|
if (!m_nlmeJoinConfirmCallback.IsNull())
|
|
{
|
|
joinConfirmParams.m_extendedPanId = params.m_extendedPanId;
|
|
joinConfirmParams.m_networkAddress = Mac16Address("FF:FF");
|
|
joinConfirmParams.m_enhancedBeacon = false;
|
|
joinConfirmParams.m_macInterfaceIndex = 0;
|
|
joinConfirmParams.m_status = NwkStatus::NOT_PERMITED;
|
|
m_nlmeJoinConfirmCallback(joinConfirmParams);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NS_FATAL_ERROR("Joining method not supported");
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::NlmeStartRouterRequest(NlmeStartRouterRequestParams params)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
NS_ASSERT_MSG(params.m_beaconOrder == 15, "Beacon mode not supported for zigbee");
|
|
NS_ASSERT_MSG(params.m_superframeOrder == 15, "Beacon mode not supported for zigbee");
|
|
|
|
CapabilityInformation capability;
|
|
capability.SetCapability(m_nwkCapabilityInformation);
|
|
|
|
if (capability.GetDeviceType() != MacDeviceType::ROUTER)
|
|
{
|
|
m_pendPrimitiveNwk = NLDE_NLME_NONE;
|
|
m_startRouterParams = {};
|
|
if (!m_nlmeStartRouterConfirmCallback.IsNull())
|
|
{
|
|
NlmeStartRouterConfirmParams confirmParams;
|
|
confirmParams.m_status = NwkStatus::INVALID_REQUEST;
|
|
m_nlmeStartRouterConfirmCallback(confirmParams);
|
|
}
|
|
NS_LOG_ERROR("This device is not a Zigbee Router or is not joined to this network");
|
|
}
|
|
else
|
|
{
|
|
m_pendPrimitiveNwk = NLME_START_ROUTER;
|
|
// store the NLME-START-ROUTER.request params while request the current channel
|
|
m_startRouterParams = params;
|
|
// request an update of the current channel in use in the PHY
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeGetRequest,
|
|
m_mac,
|
|
MacPibAttributeIdentifier::pCurrentChannel);
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SetNldeDataIndicationCallback(NldeDataIndicationCallback c)
|
|
{
|
|
m_nldeDataIndicationCallback = c;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SetNldeDataConfirmCallback(NldeDataConfirmCallback c)
|
|
{
|
|
m_nldeDataConfirmCallback = c;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SetNlmeNetworkFormationConfirmCallback(NlmeNetworkFormationConfirmCallback c)
|
|
{
|
|
m_nlmeNetworkFormationConfirmCallback = c;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SetNlmeNetworkDiscoveryConfirmCallback(NlmeNetworkDiscoveryConfirmCallback c)
|
|
{
|
|
m_nlmeNetworkDiscoveryConfirmCallback = c;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SetNlmeRouteDiscoveryConfirmCallback(NlmeRouteDiscoveryConfirmCallback c)
|
|
{
|
|
m_nlmeRouteDiscoveryConfirmCallback = c;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SetNlmeDirectJoinConfirmCallback(NlmeDirectJoinConfirmCallback c)
|
|
{
|
|
m_nlmeDirectJoinConfirmCallback = c;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SetNlmeJoinConfirmCallback(NlmeJoinConfirmCallback c)
|
|
{
|
|
m_nlmeJoinConfirmCallback = c;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SetNlmeJoinIndicationCallback(NlmeJoinIndicationCallback c)
|
|
{
|
|
m_nlmeJoinIndicationCallback = c;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SetNlmeStartRouterConfirmCallback(NlmeStartRouterConfirmCallback c)
|
|
{
|
|
m_nlmeStartRouterConfirmCallback = c;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::EnqueuePendingTx(Ptr<Packet> p, uint8_t nsduHandle)
|
|
{
|
|
// TODO : PurgeTxQueue();
|
|
if (m_pendingTxQueue.size() < m_maxPendingTxQueueSize)
|
|
{
|
|
ZigbeeNwkHeader peekedNwkHeader;
|
|
p->PeekHeader(peekedNwkHeader);
|
|
|
|
Ptr<PendingTxPkt> pendingTxPkt = Create<PendingTxPkt>();
|
|
pendingTxPkt->dstAddr = peekedNwkHeader.GetDstAddr();
|
|
pendingTxPkt->nsduHandle = nsduHandle;
|
|
pendingTxPkt->txPkt = p;
|
|
// TODO: expiration time here
|
|
m_pendingTxQueue.emplace_back(pendingTxPkt);
|
|
// TODO: pending trace here
|
|
}
|
|
else
|
|
{
|
|
// TODO: Drop trace here
|
|
}
|
|
}
|
|
|
|
bool
|
|
ZigbeeNwk::DequeuePendingTx(Mac16Address dst, Ptr<PendingTxPkt> entry)
|
|
{
|
|
// TODO : PurgeTxQueue();
|
|
|
|
/* std::erase_if(m_pendingTxQueue, [&dst](Ptr<PendingTxPkt> pkt) {
|
|
return pkt->dstAddr == dst;
|
|
});*/
|
|
|
|
for (auto iter = m_pendingTxQueue.begin(); iter != m_pendingTxQueue.end(); iter++)
|
|
{
|
|
if ((*iter)->dstAddr == dst)
|
|
{
|
|
*entry = **iter;
|
|
// TODO: Dequeue trace if needed here.
|
|
m_pendingTxQueue.erase(iter);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::DisposePendingTx()
|
|
{
|
|
for (auto element : m_pendingTxQueue)
|
|
{
|
|
element = nullptr;
|
|
}
|
|
m_pendingTxQueue.clear();
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::BufferTxPkt(Ptr<Packet> p, uint8_t macHandle, uint8_t nwkHandle)
|
|
{
|
|
if (m_txBuffer.size() < m_txBufferMaxSize)
|
|
{
|
|
Ptr<TxPkt> txPkt = Create<TxPkt>();
|
|
txPkt->macHandle = macHandle;
|
|
txPkt->nwkHandle = nwkHandle;
|
|
txPkt->txPkt = p;
|
|
m_txBuffer.emplace_back(txPkt);
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_DEBUG("Zigbee Tx Buffer is full, packet dropped.");
|
|
// TODO : Drop trace for TX buffer
|
|
}
|
|
}
|
|
|
|
bool
|
|
ZigbeeNwk::RetrieveTxPkt(uint8_t macHandle, Ptr<TxPkt>& txPkt)
|
|
{
|
|
for (auto bufferedPkt : m_txBuffer)
|
|
{
|
|
if (bufferedPkt->macHandle == macHandle)
|
|
{
|
|
txPkt = bufferedPkt;
|
|
|
|
std::erase_if(m_txBuffer,
|
|
[&macHandle](Ptr<TxPkt> pkt) { return pkt->macHandle == macHandle; });
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::DisposeTxPktBuffer()
|
|
{
|
|
for (auto element : m_txBuffer)
|
|
{
|
|
element = nullptr;
|
|
}
|
|
m_txBuffer.clear();
|
|
}
|
|
|
|
NwkStatus
|
|
ZigbeeNwk::GetNwkStatus(MacStatus macStatus) const
|
|
{
|
|
return static_cast<NwkStatus>(macStatus);
|
|
}
|
|
|
|
Mac16Address
|
|
ZigbeeNwk::AllocateNetworkAddress()
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
if (m_nwkAddrAlloc == DISTRIBUTED_ALLOC)
|
|
{
|
|
NS_FATAL_ERROR("Distributed allocation not supported");
|
|
return Mac16Address("FF:FF");
|
|
}
|
|
else if (m_nwkAddrAlloc == STOCHASTIC_ALLOC)
|
|
{
|
|
// See nwkNetworkAddress valid range Zigbee specification r22.1.0, 3.5.2
|
|
// Valid values in the Zigbee specification goes from 1 to 0xFFF7,
|
|
// However, the range 0x8000 to 0x9FFF is used for multicast in other networks
|
|
// (i.e. IPV6 over IEEE 802.15.4) for this reason, we avoid this range as well.
|
|
// See RFC 4944, Section 9
|
|
uint16_t rndValue = m_uniformRandomVariable->GetInteger(1, 0x7FFF);
|
|
uint16_t rndValue2 = m_uniformRandomVariable->GetInteger(0xA000, 0xFFF7);
|
|
uint16_t rndValue3 = m_uniformRandomVariable->GetInteger(1, 2);
|
|
|
|
Mac16Address allocAddr;
|
|
if (rndValue3 == 1)
|
|
{
|
|
allocAddr = Mac16Address(rndValue);
|
|
}
|
|
else
|
|
{
|
|
allocAddr = Mac16Address(rndValue2);
|
|
}
|
|
return allocAddr;
|
|
}
|
|
else
|
|
{
|
|
NS_FATAL_ERROR("Address allocation method not supported");
|
|
return Mac16Address("FF:FF");
|
|
}
|
|
}
|
|
|
|
uint8_t
|
|
ZigbeeNwk::GetLQINonLinearValue(uint8_t lqi) const
|
|
{
|
|
uint8_t mappedValue;
|
|
|
|
if (lqi > 50)
|
|
{
|
|
mappedValue = 1;
|
|
}
|
|
else if ((lqi <= 50) && (lqi > 45))
|
|
{
|
|
mappedValue = 2;
|
|
}
|
|
else if ((lqi <= 45) && (lqi > 40))
|
|
{
|
|
mappedValue = 3;
|
|
}
|
|
else if ((lqi <= 40) && (lqi > 38))
|
|
{
|
|
mappedValue = 4;
|
|
}
|
|
else if ((lqi <= 38) && (lqi > 35))
|
|
{
|
|
mappedValue = 5;
|
|
}
|
|
else if ((lqi <= 35) && (lqi > 24))
|
|
{
|
|
mappedValue = 6;
|
|
}
|
|
else
|
|
{
|
|
mappedValue = 7;
|
|
}
|
|
|
|
return mappedValue;
|
|
}
|
|
|
|
uint8_t
|
|
ZigbeeNwk::GetLinkCost(uint8_t lqi) const
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
if (m_nwkReportConstantCost)
|
|
{
|
|
// Hop count based. Report constant value
|
|
return 7;
|
|
}
|
|
else
|
|
{
|
|
// Based on non-linear mapping of LQI
|
|
return GetLQINonLinearValue(lqi);
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SendRREQ(ZigbeeNwkHeader nwkHeader,
|
|
ZigbeePayloadRouteRequestCommand payload,
|
|
uint8_t rreqRetries)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
ZigbeePayloadType payloadType(ROUTE_REQ_CMD);
|
|
|
|
Ptr<Packet> nsdu = Create<Packet>();
|
|
nsdu->AddHeader(payload);
|
|
nsdu->AddHeader(payloadType);
|
|
nsdu->AddHeader(nwkHeader);
|
|
|
|
if (payload.GetCmdOptManyToOneField() == ManyToOne::NO_MANY_TO_ONE &&
|
|
nwkHeader.GetRadius() != 0)
|
|
{
|
|
// Set RREQ RETRIES
|
|
Time rreqRetryTime = m_nwkcRREQRetryInterval + MilliSeconds(m_rreqJitter->GetValue());
|
|
|
|
Ptr<RreqRetryTableEntry> rreqRetryTableEntry;
|
|
if (m_rreqRetryTable.LookUpEntry(payload.GetRouteReqId(), rreqRetryTableEntry))
|
|
{
|
|
if (rreqRetryTableEntry->GetRreqRetryCount() >= rreqRetries)
|
|
{
|
|
NS_LOG_DEBUG("Maximum RREQ retries reached for dst [" << payload.GetDstAddr()
|
|
<< "] and rreq ID "
|
|
<< payload.GetRouteReqId());
|
|
// Note: The value of the maximum number of retries (rreqRetries) is either
|
|
// nwkcInitialRREQRetries or nwkcRREQRetries depending on where the RREQ is
|
|
// transmitted. See Zigbee specification r22.1.0, Section 3.6.3.5.1 This trace here
|
|
// is used to keep track when the maximum RREQ retries is reached.
|
|
m_rreqRetriesExhaustedTrace(payload.GetRouteReqId(),
|
|
payload.GetDstAddr(),
|
|
rreqRetries);
|
|
}
|
|
else
|
|
{
|
|
// Schedule the next RREQ RETRY event and update entry.
|
|
EventId rreqRetryEvent = Simulator::Schedule(rreqRetryTime,
|
|
&ZigbeeNwk::SendRREQ,
|
|
this,
|
|
nwkHeader,
|
|
payload,
|
|
rreqRetries);
|
|
|
|
rreqRetryTableEntry->SetRreqRetryCount(rreqRetryTableEntry->GetRreqRetryCount() +
|
|
1);
|
|
rreqRetryTableEntry->SetRreqEventId(rreqRetryEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Schedule the next RREQ RETRY and add a new record of the event.
|
|
EventId rreqRetryEvent = Simulator::Schedule(rreqRetryTime,
|
|
&ZigbeeNwk::SendRREQ,
|
|
this,
|
|
nwkHeader,
|
|
payload,
|
|
rreqRetries);
|
|
|
|
Ptr<RreqRetryTableEntry> newEntry =
|
|
Create<RreqRetryTableEntry>(payload.GetRouteReqId(), rreqRetryEvent, 0);
|
|
|
|
m_rreqRetryTable.AddEntry(newEntry);
|
|
}
|
|
}
|
|
|
|
// Send the RREQ
|
|
// See Section 3.4.1.1 Mac Data Service Requirements for RREQ
|
|
if (nwkHeader.GetRadius() != 0)
|
|
{
|
|
McpsDataRequestParams params;
|
|
params.m_dstPanId = m_nwkPanId;
|
|
params.m_srcAddrMode = SHORT_ADDR;
|
|
params.m_dstAddrMode = SHORT_ADDR;
|
|
params.m_dstAddr = Mac16Address::GetBroadcast();
|
|
params.m_msduHandle = m_macHandle.GetValue();
|
|
m_macHandle++;
|
|
Simulator::ScheduleNow(&LrWpanMacBase::McpsDataRequest, m_mac, params, nsdu);
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_DEBUG("Maximum radius reached, dropping RREQ");
|
|
}
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::SendRREP(Mac16Address nextHop,
|
|
Mac16Address originator,
|
|
Mac16Address responder,
|
|
uint8_t rreqId,
|
|
uint8_t pathcost)
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
ZigbeeNwkHeader nwkHeader;
|
|
nwkHeader.SetFrameType(NWK_COMMAND);
|
|
nwkHeader.SetProtocolVer(m_nwkcProtocolVersion);
|
|
nwkHeader.SetDiscoverRoute(DiscoverRouteType::ENABLE_ROUTE_DISCOVERY);
|
|
|
|
nwkHeader.SetDstAddr(nextHop);
|
|
nwkHeader.SetSrcAddr(m_nwkNetworkAddress);
|
|
m_nwkSequenceNumber++;
|
|
nwkHeader.SetSeqNum(m_nwkSequenceNumber.GetValue());
|
|
// see Zigbee specification 3.4.2.2
|
|
// Use the maximum possible radius
|
|
nwkHeader.SetRadius(m_nwkMaxDepth * 2);
|
|
|
|
ZigbeePayloadType payloadType(ROUTE_REP_CMD);
|
|
|
|
ZigbeePayloadRouteReplyCommand payload;
|
|
payload.SetRouteReqId(rreqId);
|
|
payload.SetOrigAddr(originator);
|
|
payload.SetRespAddr(responder);
|
|
payload.SetPathCost(pathcost);
|
|
|
|
// See Section 3.4.2 Mac Data Service Requirements for RREP
|
|
McpsDataRequestParams params;
|
|
params.m_dstPanId = m_nwkPanId;
|
|
params.m_srcAddrMode = SHORT_ADDR;
|
|
params.m_dstAddrMode = SHORT_ADDR;
|
|
params.m_dstAddr = nextHop;
|
|
params.m_msduHandle = m_macHandle.GetValue();
|
|
m_macHandle++;
|
|
|
|
Ptr<Packet> nsdu = Create<Packet>();
|
|
nsdu->AddHeader(payload);
|
|
nsdu->AddHeader(payloadType);
|
|
nsdu->AddHeader(nwkHeader);
|
|
|
|
Simulator::ScheduleNow(&LrWpanMacBase::McpsDataRequest, m_mac, params, nsdu);
|
|
}
|
|
|
|
int64_t
|
|
ZigbeeNwk::AssignStreams(int64_t stream)
|
|
{
|
|
NS_LOG_FUNCTION(this << stream);
|
|
m_uniformRandomVariable->SetStream(stream);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::UpdateBeaconPayloadLength()
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
ZigbeeBeaconPayload beaconPayloadHeader;
|
|
beaconPayloadHeader.SetStackProfile(static_cast<uint8_t>(m_nwkStackProfile));
|
|
beaconPayloadHeader.SetRouterCapacity(m_nwkcCoordinatorCapable);
|
|
beaconPayloadHeader.SetDeviceDepth(0); // Not used by stack profile (0x02 =ZIGBEE pro)
|
|
beaconPayloadHeader.SetEndDevCapacity(true);
|
|
beaconPayloadHeader.SetExtPanId(m_nwkExtendedPanId);
|
|
beaconPayloadHeader.SetTxOffset(0xFFFFFF);
|
|
// TODO: beaconPayload.SetNwkUpdateId(m_nwkUpdateId);
|
|
|
|
// Set the beacon payload length in the MAC PIBs and then proceed to
|
|
// update the beacon payload
|
|
m_beaconPayload = Create<Packet>();
|
|
m_beaconPayload->AddHeader(beaconPayloadHeader);
|
|
Ptr<MacPibAttributes> pibAttr = Create<MacPibAttributes>();
|
|
pibAttr->macBeaconPayloadLength = m_beaconPayload->GetSize();
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeSetRequest,
|
|
m_mac,
|
|
MacPibAttributeIdentifier::macBeaconPayloadLength,
|
|
pibAttr);
|
|
}
|
|
|
|
void
|
|
ZigbeeNwk::UpdateBeaconPayload()
|
|
{
|
|
NS_LOG_FUNCTION(this);
|
|
|
|
// Extract octets from m_beaconPayload and copy them into the
|
|
// macBeaconPayload attribute
|
|
Ptr<MacPibAttributes> pibAttr = Create<MacPibAttributes>();
|
|
pibAttr->macBeaconPayload.resize(m_beaconPayload->GetSize());
|
|
m_beaconPayload->CopyData(pibAttr->macBeaconPayload.data(), m_beaconPayload->GetSize());
|
|
m_beaconPayload = nullptr;
|
|
Simulator::ScheduleNow(&LrWpanMacBase::MlmeSetRequest,
|
|
m_mac,
|
|
MacPibAttributeIdentifier::macBeaconPayload,
|
|
pibAttr);
|
|
}
|
|
|
|
std::ostream&
|
|
operator<<(std::ostream& os, const NwkStatus& state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case NwkStatus::SUCCESS:
|
|
os << "SUCCESS";
|
|
break;
|
|
case NwkStatus::FULL_CAPACITY:
|
|
os << "FULL CAPACITY";
|
|
break;
|
|
case NwkStatus::ACCESS_DENIED:
|
|
os << "ACCESS DENIED";
|
|
break;
|
|
case NwkStatus::COUNTER_ERROR:
|
|
os << "COUNTER ERROR";
|
|
break;
|
|
case NwkStatus::IMPROPER_KEY_TYPE:
|
|
os << "IMPROPER KEY TYPE";
|
|
break;
|
|
case NwkStatus::IMPROPER_SECURITY_LEVEL:
|
|
os << "IMPROPER SECURITY LEVEL";
|
|
break;
|
|
case NwkStatus::UNSUPPORTED_LEGACY:
|
|
os << "UNSUPPORTED LEGACY";
|
|
break;
|
|
case NwkStatus::UNSUPPORTED_SECURITY:
|
|
os << "UNSUPPORTED SECURITY";
|
|
break;
|
|
case NwkStatus::BEACON_LOSS:
|
|
os << "BEACON LOSS";
|
|
break;
|
|
case NwkStatus::CHANNEL_ACCESS_FAILURE:
|
|
os << "CHANNEL ACCESS FAILURE";
|
|
break;
|
|
case NwkStatus::DENIED:
|
|
os << "DENIED";
|
|
break;
|
|
case NwkStatus::DISABLE_TRX_FAILURE:
|
|
os << "DISABLE TRX FAILURE";
|
|
break;
|
|
case NwkStatus::SECURITY_ERROR:
|
|
os << "SECURITY ERROR";
|
|
break;
|
|
case NwkStatus::FRAME_TOO_LONG:
|
|
os << "FRAME TOO LONG";
|
|
break;
|
|
case NwkStatus::INVALID_GTS:
|
|
os << "INVALID GTS";
|
|
break;
|
|
case NwkStatus::INVALID_HANDLE:
|
|
os << "INVALID HANDLE";
|
|
break;
|
|
case NwkStatus::INVALID_PARAMETER_MAC:
|
|
os << "INVALID PARAMETER MAC";
|
|
break;
|
|
case NwkStatus::NO_ACK:
|
|
os << "NO ACKNOLEDGMENT";
|
|
break;
|
|
case NwkStatus::NO_BEACON:
|
|
os << "NO BEACON";
|
|
break;
|
|
case NwkStatus::NO_DATA:
|
|
os << "NO DATA";
|
|
break;
|
|
case NwkStatus::NO_SHORT_ADDRESS:
|
|
os << "NO SHORT ADDRESS";
|
|
break;
|
|
case NwkStatus::OUT_OF_CAP:
|
|
os << "OUT OF CAP";
|
|
break;
|
|
case NwkStatus::PAN_ID_CONFLICT:
|
|
os << "PAN ID CONFLICT";
|
|
break;
|
|
case NwkStatus::REALIGMENT:
|
|
os << "REALIGMENT";
|
|
break;
|
|
case NwkStatus::TRANSACTION_EXPIRED:
|
|
os << "TRANSACTION EXPIRED";
|
|
break;
|
|
case NwkStatus::TRANSACTION_OVERFLOW:
|
|
os << "TRANSACTION OVERFLOW";
|
|
break;
|
|
case NwkStatus::TX_ACTIVE:
|
|
os << "TX ACTIVE";
|
|
break;
|
|
case NwkStatus::UNAVAILABLE_KEY:
|
|
os << "UNAVAILABLE KEY";
|
|
break;
|
|
case NwkStatus::INVALID_ADDRESS:
|
|
os << "INVALID ADDRESS";
|
|
break;
|
|
case NwkStatus::ON_TIME_TOO_LONG:
|
|
os << "ON TIME TOO LONG";
|
|
break;
|
|
case NwkStatus::PAST_TIME:
|
|
os << "PAST TIME";
|
|
break;
|
|
case NwkStatus::TRACKING_OFF:
|
|
os << "TRACKING OFF";
|
|
break;
|
|
case NwkStatus::INVALID_INDEX:
|
|
os << "INVALID INDEX";
|
|
break;
|
|
case NwkStatus::READ_ONLY:
|
|
os << "READ ONLY";
|
|
break;
|
|
case NwkStatus::SUPERFRAME_OVERLAP:
|
|
os << "SUPERFRAME OVERLAP";
|
|
break;
|
|
case NwkStatus::INVALID_PARAMETER:
|
|
os << "INVALID PARAMETER";
|
|
break;
|
|
case NwkStatus::INVALID_REQUEST:
|
|
os << "INVALID REQUEST";
|
|
break;
|
|
case NwkStatus::NOT_PERMITED:
|
|
os << "NO PERMITED";
|
|
break;
|
|
case NwkStatus::STARTUP_FAILURE:
|
|
os << "STARTUP FAILURE";
|
|
break;
|
|
case NwkStatus::ALREADY_PRESENT:
|
|
os << "ALREADY PRESENT";
|
|
break;
|
|
case NwkStatus::SYNC_FAILURE:
|
|
os << "SYNC FAILURE";
|
|
break;
|
|
case NwkStatus::NEIGHBOR_TABLE_FULL:
|
|
os << "NEIGHBOR TABLE FULL";
|
|
break;
|
|
case NwkStatus::UNKNOWN_DEVICE:
|
|
os << "UNKNOWN DEVICE";
|
|
break;
|
|
case NwkStatus::UNSUPPORTED_ATTRIBUTE:
|
|
os << "UNSUPPORTED ATTRIBUTE";
|
|
break;
|
|
case NwkStatus::NO_NETWORKS:
|
|
os << "NO NETWORKS";
|
|
break;
|
|
case NwkStatus::MAX_FRM_COUNTER:
|
|
os << "MAX FRAME COUNTER";
|
|
break;
|
|
case NwkStatus::NO_KEY:
|
|
os << "NO KEY";
|
|
break;
|
|
case NwkStatus::BAD_CCM_OUTPUT:
|
|
os << "BAD CCM OUTPUT";
|
|
break;
|
|
case NwkStatus::ROUTE_DISCOVERY_FAILED:
|
|
os << "ROUTE DISCOVERY FAILED";
|
|
break;
|
|
case NwkStatus::ROUTE_ERROR:
|
|
os << "ROUTE ERROR";
|
|
break;
|
|
case NwkStatus::BT_TABLE_FULL:
|
|
os << "BT TABLE FULL";
|
|
break;
|
|
case NwkStatus::FRAME_NOT_BUFFERED:
|
|
os << "FRAME NOT BUFFERED";
|
|
break;
|
|
case NwkStatus::INVALID_INTERFACE:
|
|
os << "INVALID INTERFACE";
|
|
break;
|
|
case NwkStatus::LIMIT_REACHED:
|
|
os << "LIMIT REACHED";
|
|
break;
|
|
case NwkStatus::SCAN_IN_PROGRESS:
|
|
os << "SCAN IN PROGRESS";
|
|
break;
|
|
}
|
|
return os;
|
|
}
|
|
|
|
std::ostream&
|
|
operator<<(std::ostream& os, const std::vector<uint8_t>& vec)
|
|
{
|
|
std::copy(vec.begin(), vec.end(), std::ostream_iterator<uint16_t>(os, " "));
|
|
return os;
|
|
}
|
|
|
|
std::ostream&
|
|
operator<<(std::ostream& os, const uint8_t& num)
|
|
{
|
|
os << static_cast<uint16_t>(num);
|
|
return os;
|
|
}
|
|
|
|
} // namespace zigbee
|
|
} // namespace ns3
|