From 711a55857f6ca7310bb55362cdcd20af53bef8d0 Mon Sep 17 00:00:00 2001 From: Alberto Gallegos Ramonet Date: Fri, 25 Jul 2025 16:07:42 +0900 Subject: [PATCH] zigbee: Add Groupcast (Multicast) support --- CHANGES.md | 7 +- RELEASE_NOTES.md | 1 + src/zigbee/doc/figures/zigbeeStackArch.dia | Bin 6654 -> 6818 bytes src/zigbee/doc/zigbee.rst | 188 +++++++--- src/zigbee/examples/zigbee-aps-data.cc | 193 +++++----- src/zigbee/model/zigbee-aps-tables.h | 6 +- src/zigbee/model/zigbee-aps.cc | 288 ++++++++++++--- src/zigbee/model/zigbee-aps.h | 202 +++++++++- src/zigbee/model/zigbee-nwk-tables.cc | 8 + src/zigbee/model/zigbee-nwk-tables.h | 15 +- src/zigbee/model/zigbee-nwk.cc | 405 ++++++++++++++++++--- src/zigbee/model/zigbee-nwk.h | 131 +++++-- src/zigbee/model/zigbee-stack.cc | 11 +- src/zigbee/model/zigbee-stack.h | 7 +- 14 files changed, 1186 insertions(+), 276 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cc7c41c88..2fbc2f275 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,10 +20,11 @@ This file is a best-effort approach to solving this issue; we will do our best b ### Changes to existing API -* (antenna) Reformatted documentation +* (antenna) Reformatted documentation. +* (internet) Added check for longest prefix match in GlobalRouting. * (lr-wpan) Debloat MAC PD-DATA.indication and reduce packet copies. -* (zigbee) Added group table -* (internet) Added check for longest prefix match in GlobalRouting +* (zigbee) Added group table. +* (zigbee) Added Groupcast (Multicast) support. ### Changes to build system diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8d1478249..321b4df7d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -35,6 +35,7 @@ ns-3 has switched to the C++23 standard by default. - (antenna) !2516 - Reformatted documentation - (core) A stacktrace will now be printed on fatal errors in supported platforms. - (wifi) !2524 - Fix corrupted radiotap header when EHT is used. +- (zigbee) !2512 - Added Groupcast (Multicast) support ### Bugs fixed diff --git a/src/zigbee/doc/figures/zigbeeStackArch.dia b/src/zigbee/doc/figures/zigbeeStackArch.dia index f46123817d7a76b698f6a5d37cd51a7ef0464323..d56d36a8e922b49402b60a479ebbc36647904d7e 100644 GIT binary patch literal 6818 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 Q^7Fm_2Lj{Rb?HI^0IX4GC;$Ke literal 6654 zcmVUkHUW^^WkYP{EO*%{Pfe#`^Dny z(Zh$IK7A6C&*#I%Y%V6_58{0E@IS-JWcV-~^l<0r2fg0v366$~Vf@+hr^Cf!K0f%c z81<&Z)6q{m2gAePPUf=@)1#g0sO52ov&n4U`#7BZwDX7K<=4)`<%u3%Inym?KO3Hm z4(6lbZ_Ou{;miBYPJT9;UqA8b+3b8A4q1FYyKzXvdE)<9j#(ac9uAtG{QQT<_=oC} zmOr>WSKWm+3R#>E=O^RoO*!G|OsYqrh0|Jx$3fnCY0;{SXY%dh4z??9vR!fW?TQ

GNNl>xdZmY#z9FG@^*;lUrcsMy<=LV`D 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 ID-JLM03#dGetNwk()->NlmeRouteDiscoveryRequest(routeDiscParams); .. note:: - Important: The process described above assumes that devices have already joined the network. + The process described above assumes that devices have already joined the network. A route discovery request issued before a device is part of the network (join process) will result in failure. -**Mesh Routing** +Mesh Routing +^^^^^^^^^^^^ -Mesh routing in Zigbee is often attributed to the mechanisms used by the AODV routing protocol (`RFC 3561 `_). +Mesh routing in Zigbee is often attributed to the mechanisms used by the AODV routing protocol (:rfc3561). Although Zigbee mesh routing and the AODV protocol share some similarities, there are significant differences between them that directly influence performance. Firstly, AODV was designed for IP-based networks, whereas Zigbee operates without the concept of IP addresses, thus eliminating their associated overhead. @@ -309,37 +314,140 @@ Alternatively, a Mesh route discovery can be performed along a data transmission zstack->GetNwk()->NldeDataRequest(dataReqParams, p); .. note:: - Important: The process described above assumes that devices have already joined the network. + The process described above assumes that devices have already joined the network. A route discovery request issued before a device is part of the network (join process) will result in failure. The application support sub-layer (APS) --------------------------------------- -As its name suggests, this intermediate layer exists between the Network (NWK) layer and the Application Framework (AF). +As its name suggests, the APS (Application Support) layer acts as an intermediate layer between the Network (NWK) layer and the Application Framework (AF). It provides services to both the Zigbee Device Object (ZDO) and the AF. -The APS layer introduces abstract concepts such as **EndPoints**, **Clusters**, and **Profiles**; however, it does not assign specific meanings to these concepts. -Instead, it simply adds this information to the transmitted frames. -Effective management of **EndPoints**, **Clusters**, and **Profile IDs** requires the use of the Zigbee Cluster Library (ZCL) and the ZDO, which are not currently supported by this implementation. -This implementation offers only the most basic functions of the APS, allowing data transmission using known 16-bit address destinations. -Features such as groupcasting, transmission to IEEE addresses (64-bit address destinations), fragmentation, duplication detection, and security are not currently supported but are under development. +The primary functions of the APS layer include: -By default, the APS layer is integrated within the Zigbee stack. +- Handling security and data integrity through encryption and decryption of data. +- Managing the binding table, which establishes relationships between devices. +- Managing the group table, which facilitates group destinations similar to multicast groups. +- Offering various data transmission modes, such as unicast, broadcast, and groupcast. +- Detecting and filtering duplicate data transmissions. +- Managing the fragmentation and reassembly of data packets. +- Providing a mechanism for data acknowledgment and retransmission. + +The APS layer introduces abstract concepts such as **EndPoints**, **Clusters**, and **Profiles**. +However, it does not assign specific meanings to these concepts; instead, it simply adds ID information to the transmitted frames. + +Effective interpretation and management of **EndPoints**, **Clusters**, and **Profile IDs** require the use of the Zigbee Cluster Library (ZCL) and the ZDO, which are not currently supported in this implementation. +Similarly, the APS layer can transmit data that includes information about source and destination endpoints, but it cannot initiate network formation or joining procedures. Typically, these processes are managed by the ZDO; however, since the ZDO is currently unavailable, users must interact directly with the NWK layer’s network formation and joining primitives to initiate these options. + +This implementation provides only the most basic functions of the APS, enabling data transmission using 16-bit or group address destinations. +Features such as transmission to IEEE addresses (64-bit address destinations), fragmentation, duplication detection, and security are not currently supported. + +By default, the APS layer is integrated and active within the Zigbee stack. However, users can choose to work solely with the NWK layer. -To do this, they must disable the APS by using the `SetNwkLayerOnly()` function in the Zigbee helper. +To do this, they must disable the APS by using the ``SetNwkLayerOnly()`` function in the Zigbee helper. -The APS layer can transmit data that includes information regarding source and destination endpoints, but it cannot initiate network formation or network joining procedures. -Typically, the ZDO manages these processes; however, since we currently do not have the ZDO, users must directly interact with the NWK layer’s network formation and joining primitives to trigger these options. The following is a list of APS primitives are included: -- APSDE-DATA (Request, Confirm, Indication) <------ Only Address mode 0x02 (16-bit address and destination endpoint present) supported. -- APSME-BIND (Request, Confirm) <----Included but inconsequential without group table support -- APSME-UNBIND (Request, Confirm) <----Included but inconsequential without group table support +- ``APSDE-DATA`` (Request, Confirm, Indication) : Only Address mode 0x01 and 0x02 (Regular unicast and groupcast) supported. +- ``APSME-BIND`` (Request, Confirm) +- ``APSME-UNBIND`` (Request, Confirm) +- ``APSME-ADD-GROUP`` (Request, Confirm) +- ``APSME-REMOVE-GROUP`` (Request, Confirm) +- ``APSME-REMOVE-ALL-GROUPS`` (Request, Confirm) + +Data Transmission +~~~~~~~~~~~~~~~~~ + +The APS layer must transmit data using the ``APSD-DATA.request`` primitive. This primitive supports 4 types of destination address modes: + +- **0x00**: No address (Use Binding table to establish destinations) +- **0x01**: Group address (Groupcasting) +- **0x02**: 16-bit address with destination endpoint present (Regular Unicast or Broadcast) +- **0x03**: 64-bit address with destination endpoint not present (Regular Unicast) + + +GroupCasting (Address Mode: 0x01) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Group casting (Multicasting) is a method of transmitting data to multiple devices endpoints within a specific group. +This is particularly useful in scenarios where a device needs to send the same data to multiple endpoints simultaneously, +such as in lighting control systems or sensor networks. +In Zigbee, group addresses are used to facilitate this type of communication. +The APS layer manages group addresses and ensures that data is delivered to all devices in the group. +To use group casting, devices must first be added to a group. This is done using the ``APSME-ADD-GROUP`` primitive, which allows a device to join a group by specifying the group ID.:: + + // Multiple groups can be added to the same device. + // Multiple endpoints can be added to the same group. + // The group address is a 16-bit address. + // In this example, group [BE:EF] and endpoint 5 is added to a device. + ApsmeGroupRequestParams groupParams; + groupParams.m_groupAddress = Mac16Address("BE:EF"); + groupParams.m_endPoint = 5; + zstack->GetNwk()->GetAps()->ApsmeAddGroupRequest(groupParams); + +Once a device is part of a group, any device can send data to ALL the endpoints in the group using the APSDE-DATA.request primitive with +the destination address mode ``GROUP_ADDR_DST_ENDPOINT_NOT_PRESENT`` (i.e., 0x01 address mode) and the appropriate group address.:: + + // The message will be delivered to ALL the endpoints in any device in the network + // that is part of the group [BE:EF]. + ApsdeDataRequestParams dataReqParams; + ZigbeeApsTxOptions txOptions; + dataReqParams.m_useAlias = false; + // Default, use 16 bit address destination (No option), equivalent to 0x00 + dataReqParams.m_txOptions = txOptions.GetTxOptions(); + dataReqParams.m_srcEndPoint = 4; // Arbitrary value, must not be 0 + dataReqParams.m_clusterId = 5; // Arbitrary value (depends on the application) + dataReqParams.m_profileId = 2; // Arbitrary value (depends on the application) + dataReqParams.m_dstAddrMode = ApsDstAddressMode::GROUP_ADDR_DST_ENDPOINT_NOT_PRESENT; // dstAddrMode 0x01 + dataReqParams.m_dstAddr16 = Mac16Address("BE:EF"); // The destination group address + zstack->GetNwk()->GetAps()->ApsdeDataRequest(dataReqParams, p); + + +Prior Zigbee Specification Revision 22 1.0 (2017), groupcasting used a form of controlled broadcast to propagate data to all group members in the network. +However, the current specification has changed this behavior to a more efficient group addressing scheme. In the new scheme, data transmissions, +propagate differently depending on whether or not the receiving or initiating device is member of the group. +If the devices are members of the group, they broadcast the data to the nearby devices (like in the old approach), +however, if device are not a members of the group, group addresses are treated as regular unicast addresses, and a route discovery is performed +to find the closest device which is part of the group. These route are stored in the routing table, and subsequent transmissions to the same group address will use the stored route. +In this way, the group address is treated as a unicast address, and the APS layer will not broadcast the data to all devices in the network. +This change improves the efficiency of groupcasting by reducing unnecessary broadcasts. + + + +UniCasting (Address Mode: 0x02) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Unicasting is a method of transmitting data to a single specific device endpoint. +In Zigbee, this is achieved by using the ``APSDE-DATA.request`` primitive with the appropriate destination address mode and address information. +To use unicasting, the sender must know the 16-bit address of the destination device and the endpoint it wants to communicate with. +The ``APSDE-DATA.request`` primitive is used to send data to a specific device endpoint using the address mode 0x02 (Regular Unicast). + +The following example demonstrates how to send data to a specific device endpoint using the APSDE-DATA.request primitive with address mode 0x02:: + + // The message will be delivered to the endpoint 5 of the device with address AB:1C + ApsdeDataRequestParams dataReqParams; + ZigbeeApsTxOptions txOptions; + dataReqParams.m_useAlias = false; + // Default, use 16 bit address destination (No option), equivalent to 0x00 + dataReqParams.m_txOptions = txOptions.GetTxOptions(); + dataReqParams.m_srcEndPoint = 4; // Arbitrary value, must not be 0 + dataReqParams.m_clusterId = 5; // Arbitrary value (depends on the application) + dataReqParams.m_profileId = 2; // Arbitrary value (depends on the application) + dataReqParams.m_dstAddrMode = ApsDstAddressMode::DST_ADDR16_DST_ENDPOINT_PRESENT; // dstAddrMode 0x02 + dataReqParams.m_dstAddr16 = Mac16Address("AB:1C"); // The destination device address + dataReqParams.m_dstEndPoint = 5; // The destination endpoint, must not be 0 + zstack->GetNwk()->GetAps()->ApsdeDataRequest(dataReqParams, p); Usage ----- +Zigbee is implemented as a |ns3| module, which means it can be used in the same way as other |ns3| modules. +It requires the ``lr-wpan`` module to be installed and operational, as it relies on the lr-wpan MAC layer to function. +Zigbee networks can be created by using the ``ZigbeeHelper`` class, +which simplifies the process of configuring and installing the Zigbee stack but it also possible to create and configure +Zigbee stacks manually. + Helpers ~~~~~~~ @@ -395,8 +503,6 @@ Below is a list of the currently supported attributes: - ``NwkcMaxRREQJitter``: [Constant] The duration between retries of a broadcast RREQ (msec). - ``MaxPendingTxQueueSize``: The maximum size of the table storing pending packets awaiting to be transmitted after discovering a route to the destination. - - Traces ~~~~~~ @@ -417,7 +523,7 @@ All the examples listed here shows scenarios in which a quasi-layer implementati * ``zigbee-association-join.cc``: An example showing the NWK layer join process of 3 devices in a zigbee network (MAC association). * ``zigbee-nwk-routing.cc``: Shows a simple topology of 5 router devices sequentially joining a network. Data transmission and route discovery (MESH routing) are also shown in this example * ``zigbee-nwk-routing-grid.cc``: Shows a complex grid topology of 50 router devices sequentially joining a network. Route discovery (MANY-TO-ONE routing) is also shown in this example. -* ``zigbee-aps-data.cc``: Demonstrates the usage of the APS layer to transmit data. +* ``zigbee-aps-data.cc``: Demonstrates the usage of the APS layer to transmit data (Unicast and Groupcast).) The following unit test have been developed to ensure the correct behavior of the module: diff --git a/src/zigbee/examples/zigbee-aps-data.cc b/src/zigbee/examples/zigbee-aps-data.cc index b6703b128..dbbec075c 100644 --- a/src/zigbee/examples/zigbee-aps-data.cc +++ b/src/zigbee/examples/zigbee-aps-data.cc @@ -16,6 +16,10 @@ * handle automatically by the NWK layer. There is no ZDO, therefore we need to manually * use the NWK to form the network and establish the router devices. * + * The example demonstrates the following: + * - Sending data using the APS layer (Unicast and Groupcast destinations). + * - Using the APS layer to establish groupcast groups and endpoints. + * * * Network Extended PAN id: 0X000000000000CA:FE (based on the PAN coordinator address) * @@ -29,10 +33,14 @@ * * Topology: * - * ZC--------ZR1------------ZR2----------ZR3 + * ZC--------ZR1------------ZR2----------ZR3 (GroupID Member: [01:23] | Endpoints: 3) * | * | - * ZR4 + * ZR4 (GroupID Member: [01:23] | Endpoints: 3, 9) + * + * + * In example, Both ZR4 and ZR3 are part of the multicast group [01:23]. + * ZR4 has two endpoints in the group, 3 and 9. ZR3 has only one endpoint in the group, 3. */ #include "ns3/constant-position-mobility-model.h" @@ -56,67 +64,31 @@ NS_LOG_COMPONENT_DEFINE("ZigbeeRouting"); ZigbeeStackContainer zigbeeStacks; -static void -TraceRoute(Mac16Address src, Mac16Address dst) -{ - std::cout << "\nTime " << Simulator::Now().As(Time::S) << " | " - << "Traceroute to destination [" << dst << "]:\n"; - Mac16Address target = src; - uint32_t count = 1; - while (target != Mac16Address("FF:FF") && target != dst) - { - Ptr zstack; - - for (auto i = zigbeeStacks.Begin(); i != zigbeeStacks.End(); i++) - { - zstack = *i; - if (zstack->GetNwk()->GetNetworkAddress() == target) - { - break; - } - } - - bool neighbor = false; - target = zstack->GetNwk()->FindRoute(dst, neighbor); - if (target == Mac16Address("FF:FF")) - { - std::cout << count << ". Node " << zstack->GetNode()->GetId() << " [" - << zstack->GetNwk()->GetNetworkAddress() << " | " - << zstack->GetNwk()->GetIeeeAddress() << "]: " - << " Destination Unreachable\n"; - } - else - { - std::cout << count << ". Node " << zstack->GetNode()->GetId() << " [" - << zstack->GetNwk()->GetNetworkAddress() << " | " - << zstack->GetNwk()->GetIeeeAddress() << "]: " - << "NextHop [" << target << "] "; - if (neighbor) - { - std::cout << "(*Neighbor)\n"; - } - else - { - std::cout << "\n"; - } - count++; - } - } - std::cout << "\n"; -} - static void ApsDataIndication(Ptr stack, ApsdeDataIndicationParams params, Ptr p) { - std::cout << Simulator::Now().As(Time::S) << " Node " << stack->GetNode()->GetId() << " | " - << "ApsdeDataIndication: Received packet of size " << p->GetSize() + std::cout << Simulator::Now().As(Time::S) << " Node " << stack->GetNode()->GetId() << " | "; + if (params.m_dstAddrMode == ApsDstAddressMode::DST_ADDR16_DST_ENDPOINT_PRESENT) + { + std::cout << "UCST | "; + } + else if (params.m_dstAddrMode == ApsDstAddressMode::GROUP_ADDR_DST_ENDPOINT_NOT_PRESENT) + { + std::cout << "GROUPCAST | "; + } + else + { + std::cout << "Unknown | "; + } + std::cout << "ApsdeDataIndication: Received DATA packet with size " << p->GetSize() << " for destination EndPoint " << params.m_dstEndPoint << "\n"; } static void NwkNetworkFormationConfirm(Ptr stack, NlmeNetworkFormationConfirmParams params) { - std::cout << "NlmeNetworkFormationConfirmStatus = " << params.m_status << "\n"; + std::cout << Simulator::Now().As(Time::S) << " Node " << stack->GetNode()->GetId() << " | " + << "NlmeNetworkFormationConfirmStatus = " << params.m_status << "\n"; } static void @@ -129,19 +101,9 @@ NwkNetworkDiscoveryConfirm(Ptr stack, NlmeNetworkDiscoveryConfirmPa if (params.m_status == NwkStatus::SUCCESS) { - std::cout << " Network discovery confirm Received. Networks found (" - << params.m_netDescList.size() << "):\n"; - - for (const auto& netDescriptor : params.m_netDescList) - { - std::cout << " ExtPanID: 0x" << std::hex << netDescriptor.m_extPanId << "\n" - << std::dec << " CH: " << static_cast(netDescriptor.m_logCh) - << "\n" - << std::hex << " Pan ID: 0x" << netDescriptor.m_panId << "\n" - << " Stack profile: " << std::dec - << static_cast(netDescriptor.m_stackProfile) << "\n" - << "--------------------\n"; - } + std::cout << Simulator::Now().As(Time::S) << " Node " << stack->GetNode()->GetId() << " | " + << " Network discovery confirm Received. Networks found (" + << params.m_netDescList.size() << ")\n"; NlmeJoinRequestParams joinParams; @@ -168,7 +130,7 @@ NwkJoinConfirm(Ptr stack, NlmeJoinConfirmParams params) { std::cout << Simulator::Now().As(Time::S) << " Node " << stack->GetNode()->GetId() << " | " << " The device joined the network SUCCESSFULLY with short address " << std::hex - << params.m_networkAddress << " on the Extended PAN Id: " << std::hex + << params.m_networkAddress << " on the Extended PAN Id: 0x" << std::hex << params.m_extendedPanId << "\n" << std::dec; @@ -193,7 +155,7 @@ NwkRouteDiscoveryConfirm(Ptr stack, NlmeRouteDiscoveryConfirmParams } static void -SendData(Ptr stackSrc, Ptr stackDst) +SendDataUcst(Ptr stackSrc, Ptr stackDst) { // Send data from a device with stackSrc to device with stackDst. @@ -213,32 +175,39 @@ SendData(Ptr stackSrc, Ptr stackDst) dataReqParams.m_useAlias = false; // Default, use 16 bit address destination (No option), equivalent to 0x00 dataReqParams.m_txOptions = txOptions.GetTxOptions(); - dataReqParams.m_srcEndPoint = 3; - dataReqParams.m_clusterId = 5; // Arbitrary value - dataReqParams.m_profileId = 2; // Arbitrary value + dataReqParams.m_srcEndPoint = 4; // Arbitrary value, must not be 0 + dataReqParams.m_clusterId = 5; // Arbitrary value + dataReqParams.m_profileId = 2; // Arbitrary value dataReqParams.m_dstAddrMode = ApsDstAddressMode::DST_ADDR16_DST_ENDPOINT_PRESENT; dataReqParams.m_dstAddr16 = stackDst->GetNwk()->GetNetworkAddress(); - dataReqParams.m_dstEndPoint = 4; + dataReqParams.m_dstEndPoint = 3; Simulator::ScheduleNow(&ZigbeeAps::ApsdeDataRequest, stackSrc->GetAps(), dataReqParams, p); +} - // Give a few seconds to allow the creation of the route and - // then print the route trace and tables from the source - Simulator::Schedule(Seconds(3), - &TraceRoute, - stackSrc->GetNwk()->GetNetworkAddress(), - stackDst->GetNwk()->GetNetworkAddress()); +static void +SendDataGcst(Ptr stackSrc) +{ + Ptr p = Create(5); + // Src and Dst Endpoints must not be 0, because this is reserved for the ZDO. + // Other Endpoint numbers can help to differentiate between different applications + // running in the same node (similar to the concept of a port in TCP/IP). + // Likewise, because we currently do not have ZDO or ZCL or AF, clusterId + // and profileId numbers are non-sensical. + ApsdeDataRequestParams dataReqParams; + ZigbeeApsTxOptions txOptions; + dataReqParams.m_useAlias = false; + // Default, use 16 bit address destination (No option), equivalent to 0x00 + dataReqParams.m_txOptions = txOptions.GetTxOptions(); + dataReqParams.m_srcEndPoint = 4; // Arbitrary value, must not be 0 + dataReqParams.m_clusterId = 5; // Arbitrary value + dataReqParams.m_profileId = 2; // Arbitrary value - Ptr stream = Create(&std::cout); - Simulator::Schedule(Seconds(4), &ZigbeeNwk::PrintNeighborTable, stackSrc->GetNwk(), stream); + dataReqParams.m_dstAddrMode = ApsDstAddressMode::GROUP_ADDR_DST_ENDPOINT_NOT_PRESENT; + dataReqParams.m_dstAddr16 = Mac16Address("01:23"); // The destination group address - Simulator::Schedule(Seconds(4), &ZigbeeNwk::PrintRoutingTable, stackSrc->GetNwk(), stream); - - Simulator::Schedule(Seconds(4), - &ZigbeeNwk::PrintRouteDiscoveryTable, - stackSrc->GetNwk(), - stream); + Simulator::ScheduleNow(&ZigbeeAps::ApsdeDataRequest, stackSrc->GetAps(), dataReqParams, p); } int @@ -247,6 +216,7 @@ main(int argc, char* argv[]) LogComponentEnableAll(LogLevel(LOG_PREFIX_TIME | LOG_PREFIX_FUNC | LOG_PREFIX_NODE)); // Enable logs for further details // LogComponentEnable("ZigbeeNwk", LOG_LEVEL_DEBUG); + // LogComponentEnable("ZigbeeAps", LOG_LEVEL_DEBUG); RngSeedManager::SetSeed(3); RngSeedManager::SetRun(4); @@ -373,6 +343,11 @@ main(int argc, char* argv[]) zstack4->GetAps()->SetApsdeDataIndicationCallback( MakeBoundCallback(&ApsDataIndication, zstack4)); + // NWK Layer + // We do not have a ZDO, therefore we need to manually + // initiate the network formation and discovery procedures + // using the NWK layer. + // 1 - Initiate the Zigbee coordinator, start the network // ALL_CHANNELS = 0x07FFF800 (Channels 11~26) NlmeNetworkFormationRequestParams netFormParams; @@ -431,9 +406,49 @@ main(int argc, char* argv[]) zstack4->GetNwk(), netDiscParams4); - // 5- Find Route and Send data (Call to APS layer) + // APS Layer + // 4- Establish the Groupcast groups and its endpoints + // Add group [01:23] and endpoint 3 to Devices 3 and 4 + // Also add endpoint 9 to device 4 in the same group. + ApsmeGroupRequestParams groupParams; + groupParams.m_groupAddress = Mac16Address("01:23"); + groupParams.m_endPoint = 3; + Simulator::ScheduleWithContext(zstack3->GetNode()->GetId(), + Seconds(7), + &ZigbeeAps::ApsmeAddGroupRequest, + zstack3->GetAps(), + groupParams); - Simulator::Schedule(Seconds(8), &SendData, zstack0, zstack3); + Simulator::ScheduleWithContext(zstack4->GetNode()->GetId(), + Seconds(7), + &ZigbeeAps::ApsmeAddGroupRequest, + zstack4->GetAps(), + groupParams); + + groupParams.m_endPoint = 9; // Add endpoint 9 to the same group + Simulator::ScheduleWithContext(zstack4->GetNode()->GetId(), + Seconds(7), + &ZigbeeAps::ApsmeAddGroupRequest, + zstack4->GetAps(), + groupParams); + + // 4- Send data using the APS layer + + // GROUPCAST + // Transmit data to all endpoints in devices that are + // part of the group [01:23] (i.e. endpoints in the ZR3 and ZR4). + Simulator::Schedule(Seconds(9), &SendDataGcst, zstack0); + + // UNICAST + // Transmit data to a specific endpoint in a device + // In this case, we send data to the endpoint 3 of ZR3. + // We require the destination address, but we do not know this apriori, + // therefore we can request it from zstack3 on runtime. + Simulator::Schedule(Seconds(10), &SendDataUcst, zstack0, zstack3); + + // Print the contents of the routing table in the initiator device (ZC) + Ptr stream = Create(&std::cout); + Simulator::Schedule(Seconds(11), &ZigbeeNwk::PrintRoutingTable, zstack0->GetNwk(), stream); Simulator::Stop(Seconds(20)); Simulator::Run(); diff --git a/src/zigbee/model/zigbee-aps-tables.h b/src/zigbee/model/zigbee-aps-tables.h index b14371031..248d0fe03 100644 --- a/src/zigbee/model/zigbee-aps-tables.h +++ b/src/zigbee/model/zigbee-aps-tables.h @@ -26,7 +26,7 @@ namespace zigbee { /** - * @ingroup zigbee + * @ingroup Zigbee * * APS Destination Address Mode for Binding * Zigbee Specification r22.1.0, Table 2-6 @@ -39,7 +39,7 @@ enum class ApsDstAddressModeBind : std::uint8_t }; /** - * @ingroup zigbee + * @ingroup Zigbee * * The status resulting of interactions with the binding table. */ @@ -53,7 +53,7 @@ enum class BindingTableStatus : std::uint8_t }; /** - * @ingroup zigbee + * @ingroup Zigbee * * Binding Table entry: Source portion of the table. * As described in Zigbee Specification r22.1.0, Table 2-134 diff --git a/src/zigbee/model/zigbee-aps.cc b/src/zigbee/model/zigbee-aps.cc index 210e6a29d..dce5d0c33 100644 --- a/src/zigbee/model/zigbee-aps.cc +++ b/src/zigbee/model/zigbee-aps.cc @@ -28,7 +28,13 @@ ZigbeeAps::GetTypeId() static TypeId tid = TypeId("ns3::zigbee::ZigbeeAps") .SetParent() .SetGroupName("Zigbee") - .AddConstructor(); + .AddConstructor() + .AddAttribute("ApsNonMemberRadius", + "The value to be used for the NonmemberRadius parameter" + " when using NWK layer multicast", + UintegerValue(0x02), + MakeUintegerAccessor(&ZigbeeAps::m_apsNonMemberRadius), + MakeUintegerChecker()); return tid; } @@ -71,6 +77,12 @@ ZigbeeAps::SetNwk(Ptr nwk) m_nwk = nwk; } +void +ZigbeeAps::SetGroupTable(Ptr groupTable) +{ + m_apsGroupTable = groupTable; +} + Ptr ZigbeeAps::GetNwk() const { @@ -125,8 +137,8 @@ ZigbeeAps::ApsdeDataRequest(ApsdeDataRequestParams params, Ptr asdu) break; } case ApsDstAddressMode::GROUP_ADDR_DST_ENDPOINT_NOT_PRESENT: { - // TODO: Add Groupcast (multicast) support - NS_ABORT_MSG("GROUP ADDRESS (MCST) not supported"); + // Groupcast (a kind of multicast) support + SendDataGroup(params, asdu); break; } case ApsDstAddressMode::DST_ADDR16_DST_ENDPOINT_PRESENT: { @@ -304,6 +316,55 @@ ZigbeeAps::SendDataUcstBcst(ApsdeDataRequestParams params, Ptr asdu) Simulator::ScheduleNow(&ZigbeeNwk::NldeDataRequest, m_nwk, nwkParams, asdu); } +void +ZigbeeAps::SendDataGroup(ApsdeDataRequestParams params, Ptr asdu) +{ + NS_LOG_FUNCTION(this); + + // Fill APSDE-data.confirm parameters in case we need to return an error + ApsdeDataConfirmParams confirmParams; + confirmParams.m_dstAddrMode = params.m_dstAddrMode; + confirmParams.m_dstAddr16 = params.m_dstAddr16; + confirmParams.m_dstAddr64 = params.m_dstAddr64; + confirmParams.m_dstEndPoint = params.m_dstEndPoint; + confirmParams.m_srcEndPoint = params.m_srcEndPoint; + + if (params.m_dstAddr16 == Mac16Address("00:00")) + { + if (!m_apsdeDataConfirmCallback.IsNull()) + { + confirmParams.m_status = ApsStatus::INVALID_GROUP; + confirmParams.m_txTime = Simulator::Now(); + m_apsdeDataConfirmCallback(confirmParams); + } + return; + } + + // APS Header + ZigbeeApsTxOptions txOptions(params.m_txOptions); + ZigbeeApsHeader apsHeader; + apsHeader.SetFrameType(ApsFrameType::APS_DATA); + apsHeader.SetSrcEndpoint(params.m_srcEndPoint); + apsHeader.SetProfileId(params.m_profileId); + apsHeader.SetClusterId(params.m_clusterId); + apsHeader.SetExtHeaderPresent(false); + apsHeader.SetDeliveryMode(ApsDeliveryMode::APS_GROUP_ADDRESSING); + apsHeader.SetGroupAddress(params.m_dstAddr16.ConvertToInt()); + + // NLDE-data.request params + NldeDataRequestParams nwkParams; + nwkParams.m_radius = params.m_radius; + nwkParams.m_discoverRoute = DiscoverRouteType::ENABLE_ROUTE_DISCOVERY; + nwkParams.m_securityEnable = txOptions.IsSecurityEnabled(); + nwkParams.m_dstAddrMode = AddressMode::MCST; + nwkParams.m_dstAddr = params.m_dstAddr16; + nwkParams.m_nonMemberRadius = m_apsNonMemberRadius; + + asdu->AddHeader(apsHeader); + + Simulator::ScheduleNow(&ZigbeeNwk::NldeDataRequest, m_nwk, nwkParams, asdu); +} + void ZigbeeAps::ApsmeBindRequest(ApsmeBindRequestParams params) { @@ -374,9 +435,72 @@ ZigbeeAps::ApsmeUnbindRequest(ApsmeBindRequestParams params) { } +void +ZigbeeAps::ApsmeAddGroupRequest(ApsmeGroupRequestParams params) +{ + NS_LOG_FUNCTION(this); + + ApsmeGroupConfirmParams confirmParams; + confirmParams.m_status = ApsStatus::SUCCESS; + confirmParams.m_groupAddress = params.m_groupAddress; + confirmParams.m_endPoint = params.m_endPoint; + + if (!m_apsGroupTable->AddEntry(params.m_groupAddress.ConvertToInt(), params.m_endPoint)) + { + confirmParams.m_status = ApsStatus::TABLE_FULL; + } + + if (!m_apsmeAddGroupConfirmCallback.IsNull()) + { + m_apsmeAddGroupConfirmCallback(confirmParams); + } +} + +void +ZigbeeAps::ApsmeRemoveGroupRequest(ApsmeGroupRequestParams params) +{ + NS_LOG_FUNCTION(this); + + ApsmeGroupConfirmParams confirmParams; + confirmParams.m_status = ApsStatus::SUCCESS; + confirmParams.m_groupAddress = params.m_groupAddress; + confirmParams.m_endPoint = params.m_endPoint; + + if (!m_apsGroupTable->RemoveEntry(params.m_groupAddress.ConvertToInt(), params.m_endPoint)) + { + confirmParams.m_status = ApsStatus::INVALID_GROUP; + } + + if (!m_apsmeRemoveGroupConfirmCallback.IsNull()) + { + m_apsmeRemoveGroupConfirmCallback(confirmParams); + } +} + +void +ZigbeeAps::ApsmeRemoveAllGroupsRequest(uint8_t endPoint) +{ + NS_LOG_FUNCTION(this); + + ApsmeRemoveAllGroupsConfirmParams confirmParams; + confirmParams.m_status = ApsStatus::SUCCESS; + confirmParams.m_endPoint = endPoint; + + if (!m_apsGroupTable->RemoveMembership(endPoint)) + { + confirmParams.m_status = ApsStatus::INVALID_PARAMETER; + } + + if (!m_apsmeRemoveAllGroupsConfirmCallback.IsNull()) + { + m_apsmeRemoveAllGroupsConfirmCallback(confirmParams); + } +} + void ZigbeeAps::NldeDataConfirm(NldeDataConfirmParams params) { + // TODO: Handle the confirmation of the data request } void @@ -384,64 +508,124 @@ ZigbeeAps::NldeDataIndication(NldeDataIndicationParams params, Ptr nsdu) { NS_LOG_FUNCTION(this); + // See section 2.2.4.1.3. + // - TODO: Handle Security + // - Handle groupcast (MCST) delivery (Note group table shared by NWK and APS) + // - Handle UCST, BCST delivery + // - TODO: Handle binding + // - TODO: Handle fragmentation + // - TODO: Detect Duplicates + // - TODO: Handle ACK + // - TODO: Handle other frame types (APS Command, APS Inter-PAN) + ZigbeeApsHeader apsHeader; nsdu->RemoveHeader(apsHeader); - ApsdeDataIndicationParams indicationParams; - indicationParams.m_status = ApsStatus::SUCCESS; - - // TODO: - // See section 2.2.4.1.3. - // - Handle Security - // - Handle grouping(MCST) (Note group table shared by NWK and APS) - // - Handle binding - // - Handle fragmentation - // - Detect Duplicates - // - Handle ACK - // Check if packet is fragmented if (apsHeader.IsExtHeaderPresent()) { - indicationParams.m_status = ApsStatus::DEFRAG_UNSUPPORTED; if (!m_apsdeDataIndicationCallback.IsNull()) { + ApsdeDataIndicationParams indicationParams; + indicationParams.m_status = ApsStatus::DEFRAG_UNSUPPORTED; m_apsdeDataIndicationCallback(indicationParams, nsdu); } + NS_LOG_WARN("Extended Header (Fragmentation) not supported"); return; + // TODO: Handle fragmentation See 2.2.8.4.5 + } + + // TODO: IF security is present, remove as described in Section 4.4. + if (params.m_securityUse) + { + NS_LOG_ERROR("Security is not currently supported"); + } + + // TODO: Duplicate detection here + // See last paragraph of section 2.2.8.4.2 + + switch (apsHeader.GetFrameType()) + { + case ApsFrameType::APS_DATA: + // Zigbee Specification, Section 2.2.8.4.2 + // Reception and Rejection + ReceiveData(apsHeader, params, nsdu); + break; + case ApsFrameType::APS_ACK: + NS_LOG_ERROR("APS ACK frames are not supported"); + break; + case ApsFrameType::APS_COMMAND: + NS_LOG_ERROR("APS Command frames are not supported"); + break; + case ApsFrameType::APS_INTERPAN_APS: + NS_LOG_ERROR("APS Inter-PAN frames are not supported"); + break; + } +} + +void +ZigbeeAps::ReceiveData(const ZigbeeApsHeader& apsHeader, + const NldeDataIndicationParams& params, + Ptr nsdu) +{ + NS_LOG_FUNCTION(this); + + ApsdeDataIndicationParams indicationParams; + indicationParams.m_status = ApsStatus::SUCCESS; + indicationParams.m_srcAddress16 = params.m_srcAddr; + indicationParams.m_srcEndpoint = apsHeader.GetSrcEndpoint(); + indicationParams.m_profileId = apsHeader.GetProfileId(); + indicationParams.m_clusterId = apsHeader.GetClusterId(); + indicationParams.asduLength = nsdu->GetSize(); + indicationParams.m_securityStatus = ApsSecurityStatus::UNSECURED; + indicationParams.m_linkQuality = params.m_linkQuality; + indicationParams.m_rxTime = Simulator::Now(); + + // UNICAST or BROADCAST delivery + + if (apsHeader.GetDeliveryMode() == ApsDeliveryMode::APS_UCST || + apsHeader.GetDeliveryMode() == ApsDeliveryMode::APS_BCST) + { + if (!m_apsdeDataIndicationCallback.IsNull()) + { + // Note: Extracting the Address directly from the NWK, creates a dependency on this NWK + // implementation. This is not a very good design, but in practice, it is unavoidable + // due to the quasi cross-layer design of the specification + //(tigly coupled design of the APS in respect to the NWK). + indicationParams.m_dstAddr16 = m_nwk->GetNetworkAddress(); + indicationParams.m_dstAddrMode = ApsDstAddressMode::DST_ADDR16_DST_ENDPOINT_PRESENT; + indicationParams.m_dstEndPoint = apsHeader.GetDstEndpoint(); + indicationParams.m_srcAddrMode = ApsSrcAddressMode::SRC_ADDR16_SRC_ENDPOINT_PRESENT; + m_apsdeDataIndicationCallback(indicationParams, nsdu); + } + return; } - if (apsHeader.GetFrameType() == ApsFrameType::APS_DATA) - { - if (apsHeader.GetDeliveryMode() == ApsDeliveryMode::APS_UCST || - apsHeader.GetDeliveryMode() == ApsDeliveryMode::APS_BCST) - { - indicationParams.m_dstAddrMode = ApsDstAddressMode::DST_ADDR16_DST_ENDPOINT_PRESENT; - // Note: Extracting the Address directly from the NWK, creates a dependency on this NWK - // implementation. This is not a very good design, but in practice, it is unavoidable - // due to the descriptions in the specification. - indicationParams.m_dstAddr16 = m_nwk->GetNetworkAddress(); - indicationParams.m_dstEndPoint = apsHeader.GetDstEndpoint(); - indicationParams.m_srcAddrMode = ApsSrcAddressMode::SRC_ADDR16_SRC_ENDPOINT_PRESENT; - indicationParams.m_srcAddress16 = params.m_srcAddr; - indicationParams.m_srcEndpoint = apsHeader.GetSrcEndpoint(); - indicationParams.m_profileId = apsHeader.GetProfileId(); - indicationParams.m_clusterId = apsHeader.GetClusterId(); - indicationParams.asduLength = nsdu->GetSize(); - indicationParams.m_securityStatus = ApsSecurityStatus::UNSECURED; - indicationParams.m_linkQuality = params.m_linkQuality; - indicationParams.m_rxTime = Simulator::Now(); + // GROUPCAST delivery - if (!m_apsdeDataIndicationCallback.IsNull()) + if (apsHeader.GetDeliveryMode() == ApsDeliveryMode::APS_GROUP_ADDRESSING && + params.m_dstAddrMode == AddressMode::MCST) + { + std::vector endPoints; + if (m_apsGroupTable->LookUpEndPoints(apsHeader.GetGroupAddress(), endPoints)) + { + // Give a copy of the packet to each endpoint associated with the group ID. + for (const auto& endPoint : endPoints) { - m_apsdeDataIndicationCallback(indicationParams, nsdu); + if (!m_apsdeDataIndicationCallback.IsNull()) + { + indicationParams.m_dstAddr16 = Mac16Address(apsHeader.GetGroupAddress()); + indicationParams.m_dstAddrMode = + ApsDstAddressMode::GROUP_ADDR_DST_ENDPOINT_NOT_PRESENT; + indicationParams.m_dstEndPoint = endPoint; + indicationParams.m_srcAddrMode = + ApsSrcAddressMode::SRC_ADDR16_SRC_ENDPOINT_PRESENT; + m_apsdeDataIndicationCallback(indicationParams, nsdu->Copy()); + } } } - else - { - // TODO: Group deliveryMode == (MCST) - NS_LOG_WARN("Group delivery not supported"); - } + return; } } @@ -469,6 +653,24 @@ ZigbeeAps::SetApsmeUnbindConfirmCallback(ApsmeUnbindConfirmCallback c) m_apsmeUnbindConfirmCallback = c; } +void +ZigbeeAps::SetApsmeAddGroupConfirmCallback(ApsmeAddGroupConfirmCallback c) +{ + m_apsmeAddGroupConfirmCallback = c; +} + +void +ZigbeeAps::SetApsmeRemoveGroupConfirmCallback(ApsmeRemoveGroupConfirmCallback c) +{ + m_apsmeRemoveGroupConfirmCallback = c; +} + +void +ZigbeeAps::SetApsmeRemoveAllGroupsConfirmCallback(ApsmeRemoveAllGroupsConfirmCallback c) +{ + m_apsmeRemoveAllGroupsConfirmCallback = c; +} + ////////////////////////// // ZigbeeApsTxOptions // ////////////////////////// diff --git a/src/zigbee/model/zigbee-aps.h b/src/zigbee/model/zigbee-aps.h index 059645cd2..30f459458 100644 --- a/src/zigbee/model/zigbee-aps.h +++ b/src/zigbee/model/zigbee-aps.h @@ -13,6 +13,7 @@ #include "zigbee-aps-header.h" #include "zigbee-aps-tables.h" +#include "zigbee-group-table.h" #include "zigbee-nwk.h" #include "ns3/event-id.h" @@ -222,6 +223,44 @@ struct ApsmeBindConfirmParams uint8_t m_dstEndPoint{0xF0}; //!< The application destination endpoint }; +/** + * @ingroup zigbee + * + * Zigbee Specification r22.1.0, Section 2.2.4.5.1 and 2.2.4.5.3 + * APSME-ADD-GROUP.request and APSME-REMOVE-GROUP.request params + */ +struct ApsmeGroupRequestParams +{ + Mac16Address m_groupAddress; //!< The group address to add + uint8_t m_endPoint{1}; //!< The endpoint to which the group address is associated +}; + +/** + * @ingroup zigbee + * + * Zigbee Specification r22.1.0, Section 2.2.4.5.2 and 2.2.4.5.4 + * APSME-ADD-GROUP.confirm and APSME-REMOVE-GROUP.confirm params + */ +struct ApsmeGroupConfirmParams +{ + ApsStatus m_status{ApsStatus::INVALID_PARAMETER}; //!< The status of the add group request + Mac16Address m_groupAddress; //!< The group address being added + uint8_t m_endPoint{1}; //!< The endpoint to which the given group is being added. +}; + +/** + * @ingroup zigbee + * + * Zigbee Specification r22.1.0, Section 2.2.4.5.6 + * APSME-REMOVE-ALL-GROUPS.request params + */ +struct ApsmeRemoveAllGroupsConfirmParams +{ + ApsStatus m_status{ + ApsStatus::INVALID_PARAMETER}; //!< The status of the remove all groups request + uint8_t m_endPoint{1}; //!< The endpoint from which all groups are being removed. +}; + ////////////////////// // Callbacks // ////////////////////// @@ -257,6 +296,30 @@ typedef Callback ApsmeBindConfirmCallback; */ typedef Callback ApsmeUnbindConfirmCallback; +/** + * @ingroup zigbee + * + * This callback is called to confirm a successfully addition of a group address + * and or endPoint into the group table. + */ +typedef Callback ApsmeAddGroupConfirmCallback; + +/** + * @ingroup zigbee + * + * This callback is called to confirm a successfully removal of a group address + * and or endPoint from the group table. + */ +typedef Callback ApsmeRemoveGroupConfirmCallback; + +/** + * @ingroup zigbee + * + * This callback is called to confirm a successfully removal of an endpoint from + * all the the groups. + */ +typedef Callback ApsmeRemoveAllGroupsConfirmCallback; + /** * @ingroup zigbee * @@ -286,6 +349,13 @@ class ZigbeeAps : public Object */ void SetNwk(Ptr nwk); + /** + * Get the group table used by this Zigbee APS. + * + * @param groupTable The pointer to the group table to set. + */ + void SetGroupTable(Ptr groupTable); + /** * Get the underlying NWK used by the current Zigbee APS. * @@ -321,6 +391,36 @@ class ZigbeeAps : public Object */ void ApsmeUnbindRequest(ApsmeBindRequestParams params); + /** + * Zigbee Specification r22.1.0, Section 2.2.4.5.1 + * APSME-ADD-GROUP.request + * Request that group membership for a particular group be added + * for a particular endpoint. + * + * @param params The APSME add group request params + */ + void ApsmeAddGroupRequest(ApsmeGroupRequestParams params); + + /** + * Zigbee Specification r22.1.0, Section 2.2.4.5.3 + * APSME-REMOVE-GROUP.request + * Request that group membership for a particular group be removed + * for a particular endpoint. + * + * @param params The APSME remove group request params + */ + void ApsmeRemoveGroupRequest(ApsmeGroupRequestParams params); + + /** + * Zigbee Specification r22.1.0, Section 2.2.4.5.5 + * APSME-REMOVE-ALL-GROUPS.request + * Remove membership in all groups from an endpoint, so that no group-addressed frames + * will be delivered to that endpoint. + * + * @param endPoint The endpoint from which all groups are being removed. + */ + void ApsmeRemoveAllGroupsRequest(uint8_t endPoint); + /** * Zigbee Specification r22.1.0, Section 3.2.1.2 * NLDE-DATA.confirm @@ -340,6 +440,18 @@ class ZigbeeAps : public Object */ void NldeDataIndication(NldeDataIndicationParams params, Ptr nsdu); + /** + * Zigbee Specification r22.1.0, Section 2.2.8.4.2 + * Reception and Rejection of data from the NWK. + * + * @param apsHeader The APS header of the received packet + * @param params The NLDE data indication params + * @param nsdu The packet received + */ + void ReceiveData(const ZigbeeApsHeader& apsHeader, + const NldeDataIndicationParams& params, + Ptr nsdu); + /** * Set the callback as part of the interconnections between the APS and * the next layer or service (typically the application framework). The callback @@ -376,6 +488,33 @@ class ZigbeeAps : public Object */ void SetApsmeUnbindConfirmCallback(ApsmeUnbindConfirmCallback c); + /** + * Set the callback as part of the interconnections between the APS and + * the next layer or service (typically the application framework). The callback + * implements the callback used in a APSME-ADD-GROUP.confirm + * + * @param c the ApsmeAddGroupConfirm callback + */ + void SetApsmeAddGroupConfirmCallback(ApsmeAddGroupConfirmCallback c); + + /** + * Set the callback as part of the interconnections between the APS and + * the next layer or service (typically the application framework). The callback + * implements the callback used in a APSME-REMOVE-GROUP.confirm + * + * @param c the ApsmeRemoveGroupConfirm callback + */ + void SetApsmeRemoveGroupConfirmCallback(ApsmeRemoveGroupConfirmCallback c); + + /** + * Set the callback as part of the interconnections between the APS and + * the next layer or service (typically the application framework). The callback + * implements the callback used in a APSME-REMOVE-ALL-GROUPS.confirm + * + * @param c the ApsmeRemoveAllGroupsConfirm callback + */ + void SetApsmeRemoveAllGroupsConfirmCallback(ApsmeRemoveAllGroupsConfirmCallback c); + protected: void DoInitialize() override; void DoDispose() override; @@ -399,9 +538,13 @@ class ZigbeeAps : public Object */ void SendDataUcstBcst(ApsdeDataRequestParams params, Ptr asdu); - Ptr m_nwk; //!< Pointer to the underlying NWK connected to this Zigbee APS - SequenceNumber8 m_apsCounter; //!< The sequence number used in packet Tx with APS headers. - BindingTable m_apsBindingTable; //!< The binding table associated to this APS layer + /** + * Send a Groupcast to a group address destination. + * + * @param params The APSDE data request params + * @param asdu The packet to transmit + */ + void SendDataGroup(ApsdeDataRequestParams params, Ptr asdu); /** * This callback is used to to notify the results of a data transmission @@ -430,6 +573,59 @@ class ZigbeeAps : public Object * See Zigbee specification r22.1.0, Section 2.2.4.3.4 */ ApsmeUnbindConfirmCallback m_apsmeUnbindConfirmCallback; + + /** + * This callback is used to to notify the result of endpoint addition + * request in the APS to the Application framework (AF). + * See Zigbee specification r22.1.0, Section 2.2.4.5.2 + */ + ApsmeAddGroupConfirmCallback m_apsmeAddGroupConfirmCallback; + + /** + * This callback is used to to notify the result of a endpoint removal + * request in the APS to the Application framework (AF). + * See Zigbee specification r22.1.0, Section 2.2.4.5.4 + */ + ApsmeRemoveGroupConfirmCallback m_apsmeRemoveGroupConfirmCallback; + + /** + * This callback is used to to notify the result of a endpoint removal + * request in the APS to the Application framework (AF). + * See Zigbee specification r22.1.0, Section 2.2.4.5.5 + */ + ApsmeRemoveAllGroupsConfirmCallback m_apsmeRemoveAllGroupsConfirmCallback; + + /** + * The underlying Zigbee NWK connected to this Zigbee APS. + */ + Ptr m_nwk; + + /** + * The group table used by this Zigbee APS. + * The group table is a shared resource with the NWK layer. + * Zigbee Specification r22.1.0, Section 2.2.7.2 + */ + Ptr m_apsGroupTable; + + /** + * The sequence number used in packet Tx with APS headers. + * Zigbee Specification r22.1.0, Section 2.2.7.2 + */ + SequenceNumber8 m_apsCounter; + + /** + * The binding table used by this Zigbee APS. + * Zigbee Specification r22.1.0, Section 2.2.7.2 + */ + BindingTable m_apsBindingTable; + + /** + * The APS non-member radius, used to limit the number of hops + * for non-member multicast group devices when using NWK layer multicast. + * Valid range 0x00 to 0x07. + * Zigbee Specification r22.1.0, Section 2.2.7.2 + */ + uint8_t m_apsNonMemberRadius; }; /** diff --git a/src/zigbee/model/zigbee-nwk-tables.cc b/src/zigbee/model/zigbee-nwk-tables.cc index 2749f8f01..05cad700e 100644 --- a/src/zigbee/model/zigbee-nwk-tables.cc +++ b/src/zigbee/model/zigbee-nwk-tables.cc @@ -1326,6 +1326,14 @@ BroadcastTransactionTable::LookUpEntry(uint8_t seq, Ptr entry) { + return entry->GetSeqNum() == seq; + }); +} + void BroadcastTransactionTable::Purge() { diff --git a/src/zigbee/model/zigbee-nwk-tables.h b/src/zigbee/model/zigbee-nwk-tables.h index e4f21e0da..330b626e9 100644 --- a/src/zigbee/model/zigbee-nwk-tables.h +++ b/src/zigbee/model/zigbee-nwk-tables.h @@ -924,10 +924,10 @@ class BroadcastTransactionRecord : public SimpleRefCount stream) const; private: - Mac16Address m_srcAddr; //!< The 16-bit network address of the broadcast initiator. - uint8_t m_sequenceNumber; //!< The RREQ sequence number of the initiator's broadcast. - Time m_expirationTime; //!< An indicator of when the entry expires - uint8_t m_broadcastRetryCount; //!< The number of times this BCST has been retried. + Mac16Address m_srcAddr; //!< The 16-bit network address of the broadcast initiator. + uint8_t m_sequenceNumber{0}; //!< The RREQ sequence number of the initiator's broadcast. + Time m_expirationTime; //!< An indicator of when the entry expires + uint8_t m_broadcastRetryCount{1}; //!< The number of times this BCST has been retried. }; /** @@ -1297,6 +1297,13 @@ class BroadcastTransactionTable */ bool LookUpEntry(uint8_t seq, Ptr& entryFound); + /** + * Delete a broadcast transaction record (BTR) from the broadcast transaction table (BTT). + * + * @param seq The sequence number of the broadcasted frame to delete. + */ + void Delete(uint8_t seq); + /** * Purge expired entries from the broadcast transaction table (BTT). */ diff --git a/src/zigbee/model/zigbee-nwk.cc b/src/zigbee/model/zigbee-nwk.cc index e2da53693..9f6a5c783 100644 --- a/src/zigbee/model/zigbee-nwk.cc +++ b/src/zigbee/model/zigbee-nwk.cc @@ -56,7 +56,6 @@ ZigbeeNwk::GetTypeId() 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.", @@ -87,6 +86,23 @@ ZigbeeNwk::GetTypeId() DoubleValue(128), MakeDoubleAccessor(&ZigbeeNwk::m_nwkcMaxRREQJitter), MakeDoubleChecker()) + .AddAttribute("NwkcMaxBroadcastJitter", + "[Constant] The duration between retries of a broadcast RREQ (msec)", + DoubleValue(40), + MakeDoubleAccessor(&ZigbeeNwk::m_nwkcMaxBroadcastJitter), + MakeDoubleChecker()) + .AddAttribute("nwkMaxBroadcastRetries", + "The maximum number of retries allowed after a broadcast " + "transmission failure.", + UintegerValue(0x03), + MakeUintegerAccessor(&ZigbeeNwk::m_nwkMaxBroadcastRetries), + MakeUintegerChecker()) + .AddAttribute("NwkPassiveAckTimeout", + "The maximum time duration in milliseconds allowed for the parent " + "all the child devices to retransmit a broadcast message.", + TimeValue(MilliSeconds(500)), + MakeTimeAccessor(&ZigbeeNwk::m_nwkPassiveAckTimeout), + MakeTimeChecker()) .AddAttribute("MaxPendingTxQueueSize", "The maximum size of the table storing pending packets awaiting " "to be transmitted after discovering a route to the destination.", @@ -138,7 +154,6 @@ ZigbeeNwk::NotifyConstructionCompleted() m_nwkReportConstantCost = false; m_nwkSymLink = false; - m_nwkMaxBroadcastRetries = 0x03; m_countRREQRetries = 0; m_nwkIsConcentrator = false; @@ -157,6 +172,10 @@ ZigbeeNwk::NotifyConstructionCompleted() m_rreqJitter->SetAttribute("Min", DoubleValue(m_nwkcMinRREQJitter)); m_rreqJitter->SetAttribute("Max", DoubleValue(m_nwkcMaxRREQJitter)); + m_bcstJitter = CreateObject(); + m_bcstJitter->SetAttribute("Min", DoubleValue(0)); + m_bcstJitter->SetAttribute("Max", DoubleValue(m_nwkcMaxBroadcastJitter)); + m_routeExpiryTime = Seconds(255); } @@ -211,6 +230,12 @@ ZigbeeNwk::SetMac(Ptr mac) m_mac = mac; } +void +ZigbeeNwk::SetGroupTable(Ptr groupTable) +{ + m_nwkGroupIdTable = groupTable; +} + Ptr ZigbeeNwk::GetMac() const { @@ -352,11 +377,29 @@ ZigbeeNwk::McpsDataIndication(McpsDataIndicationParams params, Ptr msdu) switch (nwkHeader.GetFrameType()) { - case DATA: + case DATA: { if (nwkHeader.IsMulticast()) { // DATA MULTICAST - NS_FATAL_ERROR("Multicast DATA transmission not supported"); + if (nwkHeader.GetMulticastMode() == MulticastMode::MEMBER) + { + // Mcst Member Mode,Zigbee specification r22.1.0 Section 3.6.6.3 + ReceiveMulticastMemberFrame(nwkHeader, msdu, params); + } + else + { + // Mcst Non-Member Mode,Zigbee specification r22.1.0 Section 3.6.6.4 + if (m_nwkGroupIdTable->IsGroupMember(nwkHeader.GetDstAddr().ConvertToInt())) + { + // Treat frame as a multicast member + nwkHeader.SetMulticastMode(MulticastMode::MEMBER); + ReceiveMulticastMemberFrame(nwkHeader, msdu, params); + } + else + { + ReceiveMulticastNonMemberFrame(nwkHeader, msdu, params); + } + } } else if (IsBroadcastAddress(nwkHeader.GetDstAddr())) { @@ -443,6 +486,7 @@ ZigbeeNwk::McpsDataIndication(McpsDataIndicationParams params, Ptr msdu) } } break; + } case NWK_COMMAND: { ZigbeePayloadType payloadType; msdu->RemoveHeader(payloadType); @@ -530,7 +574,7 @@ ZigbeeNwk::ReceiveRREQ(Mac16Address macSrcAddr, return; } - // Mesh Routing + // Mesh Routing (Unicast or Multicast Group) Mac16Address nextHop; RouteDiscoveryStatus nextHopStatus = @@ -699,6 +743,132 @@ ZigbeeNwk::ReceiveRREP(Mac16Address macSrcAddr, } } +void +ZigbeeNwk::ReceiveMulticastMemberFrame(ZigbeeNwkHeader nwkHeader, + Ptr msdu, + McpsDataIndicationParams params) +{ + NS_LOG_FUNCTION(this); + + // As described in Zigbee specification r22.1.0 Section 3.6.6.3 + + // Drop if we've already seen this multicast frame + Ptr btr; + if (m_btt.LookUpEntry(nwkHeader.GetSeqNum(), btr)) + { + return; + } + + // Add new record to BTT + Ptr newBtr = + Create(nwkHeader.GetSrcAddr(), + nwkHeader.GetSeqNum(), + Simulator::Now() + m_nwkNetworkBroadcastDeliveryTime); + + if (!m_btt.AddEntry(newBtr)) + { + return; + } + + // Verify if the device is capable of relaying multicast frames + // (i.e., not an end device) + CapabilityInformation capability(m_nwkCapabilityInformation); + bool canRelay = (capability.GetDeviceType() != MacDeviceType::ENDDEVICE); + + // Case 1: Group Member — process and optionally retransmit + + if (m_nwkGroupIdTable->IsGroupMember(nwkHeader.GetDstAddr().ConvertToInt())) + { + // Push the multicast frame to the APS layer + if (!m_nldeDataIndicationCallback.IsNull()) + { + NldeDataIndicationParams dataParams; + dataParams.m_srcAddr = nwkHeader.GetSrcAddr(); + dataParams.m_dstAddr = nwkHeader.GetDstAddr(); + dataParams.m_dstAddrMode = AddressMode::MCST; + dataParams.m_linkQuality = params.m_mpduLinkQuality; + dataParams.m_nsduLength = msdu->GetSize(); + dataParams.m_rxTime = Simulator::Now(); + dataParams.m_securityUse = false; + m_nldeDataIndicationCallback(dataParams, msdu->Copy()); + } + // TODO: Add Rx Trace here + + if (canRelay) + { + nwkHeader.SetMulticastMode(MulticastMode::MEMBER); + nwkHeader.SetNonMemberRadius(nwkHeader.GetMaxNonMemberRadius()); + msdu->AddHeader(nwkHeader); + Simulator::Schedule(MilliSeconds(m_bcstJitter->GetValue()), + &ZigbeeNwk::SendDataBcst, + this, + msdu, + 0); + } + return; + } + + // Case 2: Non Group Member — forward if non-zero radius + + uint8_t radius = nwkHeader.GetNonMemberRadius(); + if (radius == 0) + { + m_btt.Delete(nwkHeader.GetSeqNum()); + return; + } + + // Decrement radius and retransmit if allowed + nwkHeader.SetNonMemberRadius(radius - 1); + + if (radius != 0 && canRelay) + { + msdu->AddHeader(nwkHeader); + Simulator::Schedule(MilliSeconds(m_bcstJitter->GetValue()), + &ZigbeeNwk::SendDataBcst, + this, + msdu, + 0); + } +} + +void +ZigbeeNwk::ReceiveMulticastNonMemberFrame(ZigbeeNwkHeader nwkHeader, + Ptr msdu, + McpsDataIndicationParams params) +{ + NS_LOG_FUNCTION(this); + + NS_ASSERT_MSG(!nwkHeader.GetDstAddr().IsBroadcast(), + "Error: Non-member multicast frame should have a Group ID address"); + + // As described in Zigbee specification r22.1.0 Section 3.6.6.4 + // If the GROUP ID (destination address) is in our routing table, + // our non member multicast frame is transmitted as a unicast frame + // to the next hop in the routing table, otherwise it is discarded. + Ptr entry; + if (m_nwkRoutingTable.LookUpEntry(nwkHeader.GetDstAddr(), entry)) + { + if (entry->GetStatus() == RouteStatus::ROUTE_VALIDATION_UNDERWAY) + { + entry->SetStatus(RouteStatus::ROUTE_ACTIVE); + } + + if ((entry->GetStatus() == ROUTE_ACTIVE)) + { + msdu->AddHeader(nwkHeader); + McpsDataRequestParams mcpsDataparams; + mcpsDataparams.m_dstPanId = m_nwkPanId; + mcpsDataparams.m_msduHandle = m_macHandle.GetValue(); + mcpsDataparams.m_txOptions = 0x01; // Acknowledment on for Non-Member Multicast + mcpsDataparams.m_srcAddrMode = SHORT_ADDR; + mcpsDataparams.m_dstAddrMode = SHORT_ADDR; + mcpsDataparams.m_dstAddr = entry->GetNextHopAddr(); + m_macHandle++; + Simulator::ScheduleNow(&LrWpanMacBase::McpsDataRequest, m_mac, mcpsDataparams, msdu); + } + } +} + bool ZigbeeNwk::IsBroadcastAddress(Mac16Address address) { @@ -714,8 +884,25 @@ ZigbeeNwk::FindNextHop(Mac16Address macSrcAddr, { NS_LOG_FUNCTION(this); + if (payload.GetMulticastField() && + m_nwkGroupIdTable->IsGroupMember(payload.GetDstAddr().ConvertToInt())) + { + // If the destination is a group member, we can directly return the next hop + // as the destination address. + nextHop = payload.GetDstAddr(); + return ROUTE_FOUND; + } + // Mesh routing + // Check if I am the destination + if (payload.GetDstAddr() == m_nwkNetworkAddress) + { + // I am the destination, return myself as next hop + nextHop = m_nwkNetworkAddress; + return ROUTE_FOUND; + } + // Check if the destination is our neighbor Ptr neighborEntry; if (m_nwkNeighborTable.LookUpEntry(payload.GetDstAddr(), neighborEntry)) @@ -762,14 +949,14 @@ ZigbeeNwk::FindNextHop(Mac16Address macSrcAddr, } } - // Entry not found + // Entry not found, create a new routing entry Ptr newRoutingEntry = Create(payload.GetDstAddr(), ROUTE_DISCOVERY_UNDERWAY, - true, // TODO no route cache - false, // TODO: Many to one - false, // TODO: Route record - false, // TODO: Group id + true, // TODO no route cache + false, // Many to one + false, // TODO: Route record + payload.GetMulticastField(), // Group id Mac16Address("FF:FF")); newRoutingEntry->SetLifeTime(Simulator::Now() + m_routeExpiryTime); @@ -922,7 +1109,7 @@ ZigbeeNwk::ProcessManyToOneRoute(Mac16Address macSrcAddr, Create(nwkHeader.GetSrcAddr(), ROUTE_ACTIVE, true, // TODO no route cache - true, // TODO: Many to one + true, // Many to one routeRecord, // TODO: Route record false, // TODO: Group id macSrcAddr); @@ -957,6 +1144,7 @@ ZigbeeNwk::SendDataUcst(Ptr packet, uint8_t nwkHandle) 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) { @@ -972,6 +1160,15 @@ ZigbeeNwk::SendDataUcst(Ptr packet, uint8_t nwkHandle) payloadRreq.SetDstAddr(nwkHeaderData.GetDstAddr()); payloadRreq.SetPathCost(0); + // If the original Data packet is marked as multicast, + // then set the multicast field in the RREQ payload + // In essence, this will initiate a multicast route discovery + // if necessary + if (nwkHeaderData.IsMulticast()) + { + payloadRreq.SetMulticastField(true); + } + Mac16Address nextHop; RouteDiscoveryStatus nextHopStatus = FindNextHop(m_nwkNetworkAddress, 0, nwkHeaderRreq, payloadRreq, nextHop); @@ -1021,6 +1218,7 @@ ZigbeeNwk::SendDataBcst(Ptr packet, uint8_t nwkHandle) BufferTxPkt(packet->Copy(), m_macHandle.GetValue(), nwkHandle); // Parameters as described in Section 3.6.5 + // TxOptions = 0 by default, i.e. no acknowledgment McpsDataRequestParams mcpsDataparams; mcpsDataparams.m_dstPanId = m_nwkPanId; mcpsDataparams.m_msduHandle = m_macHandle.GetValue(); @@ -1040,28 +1238,58 @@ ZigbeeNwk::McpsDataConfirm(McpsDataConfirmParams params) ZigbeeNwkHeader nwkHeader; bufferedElement->txPkt->PeekHeader(nwkHeader); - if (nwkHeader.GetFrameType() == DATA) + if (nwkHeader.GetFrameType() != NwkType::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 + NS_LOG_ERROR("Received DATA.confirm for a non-DATA packet"); + return; + } - if (!m_nldeDataConfirmCallback.IsNull()) + if (IsBroadcastAddress(nwkHeader.GetDstAddr()) || nwkHeader.IsMulticast()) + { + if (params.m_status != MacStatus::SUCCESS) + { + // Broadcast or Multicast failed, retry sending the packet. + // Zigbee specification r22.1.0 Section 3.6.6.3 + Ptr btr; + if (m_btt.LookUpEntry(nwkHeader.GetSeqNum(), btr)) { - // 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(params.m_status); - nldeDataConfirmParams.m_nsduHandle = bufferedElement->nwkHandle; - m_nldeDataConfirmCallback(nldeDataConfirmParams); + btr->SetBcstRetryCount(btr->GetBcstRetryCount() + 1); + if (btr->GetBcstRetryCount() <= m_nwkMaxBroadcastRetries) + { + // Retry sending the Broadcast or Multicast packet + + Simulator::Schedule(MilliSeconds(m_nwkPassiveAckTimeout.GetMilliSeconds()), + &ZigbeeNwk::SendDataBcst, + this, + bufferedElement->txPkt, + bufferedElement->nwkHandle); + } + else + { + // Broadcast retries exhausted, silently discard the packet + NS_LOG_WARN("Broadcast or Multicast packet retries exhausted for packet" + " with sequence number " + << nwkHeader.GetSeqNum()); + } } } } + 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(params.m_status); + nldeDataConfirmParams.m_nsduHandle = bufferedElement->nwkHandle; + m_nldeDataConfirmCallback(nldeDataConfirmParams); + } + } } } @@ -1970,7 +2198,7 @@ ZigbeeNwk::NldeDataRequest(NldeDataRequestParams params, Ptr packet) if (params.m_dstAddr == m_nwkNetworkAddress) { - NS_LOG_DEBUG("The source and the destination of the route request are the same!"); + NS_LOG_WARN("The source and the destination of the route request are the same!"); return; } @@ -1978,7 +2206,7 @@ ZigbeeNwk::NldeDataRequest(NldeDataRequestParams params, Ptr packet) // check that we are associated if (m_nwkNetworkAddress == "FF:FF") { - NS_LOG_DEBUG("Cannot send data, the device is not currently associated"); + NS_LOG_WARN("Cannot send data, the device is not currently associated"); if (!m_nldeDataConfirmCallback.IsNull()) { @@ -1998,7 +2226,7 @@ ZigbeeNwk::NldeDataRequest(NldeDataRequestParams params, Ptr packet) nwkHeader.SetDiscoverRoute(static_cast(params.m_discoverRoute)); nwkHeader.SetDstAddr(params.m_dstAddr); - if (params.m_useAlias) + if (params.m_useAlias && params.m_dstAddrMode != AddressMode::MCST) { nwkHeader.SetSrcAddr(params.m_aliasSrcAddr); nwkHeader.SetSeqNum(params.m_aliasSeqNumber.GetValue()); @@ -2028,32 +2256,93 @@ ZigbeeNwk::NldeDataRequest(NldeDataRequestParams params, Ptr packet) CapabilityInformation capability; capability.SetCapability(m_nwkCapabilityInformation); - if (capability.GetDeviceType() == ENDDEVICE) + if (capability.GetDeviceType() == MacDeviceType::ENDDEVICE) { nwkHeader.SetEndDeviceInitiator(); } - if (params.m_dstAddrMode == MCST) + // CASE 1: The destination is a multicast address (Group address) + // See Sections 3.6.6.2 and 3.2.1.1.3 + if (params.m_dstAddrMode == AddressMode::MCST) { + if (nwkHeader.GetEndDeviceInitiator()) + { + NS_LOG_WARN("EndDevices cannot send multicast packets, dropping the packet"); + 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; + // TODO: Add trace here. + } + nwkHeader.SetMulticast(); - // TODO: - // set the nwkHeader multicast control according to - // the values of the non-member radios parameter - // See 3.2.1.1.3 + nwkHeader.SetNonMemberRadius(params.m_nonMemberRadius); + nwkHeader.SetMaxNonMemberRadius(params.m_nonMemberRadius); + + // Check if the current device is member of the multicast Group (dstAddr) + bool groupMember = m_nwkGroupIdTable->IsGroupMember(params.m_dstAddr.ConvertToInt()); + + if (groupMember) + { + nwkHeader.SetMulticastMode(MulticastMode::MEMBER); + + // Add an entry to the Broadcast Transaction Table (BTT) + Ptr btr = Create( + nwkHeader.GetSrcAddr(), + nwkHeader.GetSeqNum(), + Simulator::Now() + m_nwkNetworkBroadcastDeliveryTime); + + if (!m_btt.AddEntry(btr)) + { + NS_LOG_DEBUG("Broadcast Transaction Table is full, dropping the packet"); + // TODO: Add multicast packet trace here + if (!m_nldeDataConfirmCallback.IsNull()) + { + NldeDataConfirmParams confirmParams; + confirmParams.m_status = NwkStatus::BT_TABLE_FULL; + confirmParams.m_txTime = Simulator::Now(); + confirmParams.m_nsduHandle = params.m_nsduHandle; + m_nldeDataConfirmCallback(confirmParams); + } + return; + } + } + else + { + nwkHeader.SetMulticastMode(MulticastMode::NONMEMBER); + } + + packet->AddHeader(nwkHeader); + + if (groupMember) + { + // See 3.6.6.2.1: Group Members initiate the transmission + // using a broadcast (MAC level) + SendDataBcst(packet, params.m_nsduHandle); + } + else + { + // See 3.6.6.2.2: Non-Group member initiate the transmission + // using a unicast (MAC level) and search for the next hop + // in its routing table if necessary. + SendDataUcst(packet, params.m_nsduHandle); + } + return; } + // CASE 2: The destination is a unicast address, a broadcast address + // or has no address (Many to one routing) + packet->AddHeader(nwkHeader); - if (capability.GetDeviceType() == ROUTER) + if (capability.GetDeviceType() == MacDeviceType::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)) + if (IsBroadcastAddress(params.m_dstAddr)) { // The destination is BROADCAST (See 3.6.5) SendDataBcst(packet, params.m_nsduHandle); @@ -2223,6 +2512,10 @@ ZigbeeNwk::NlmeRouteDiscoveryRequest(NlmeRouteDiscoveryRequestParams params) confirmParams.m_status = NwkStatus::INVALID_REQUEST; m_nlmeRouteDiscoveryConfirmCallback(confirmParams); } + + NS_LOG_WARN("The destination address is a broadcast address, or the destination " + "address mode is NO_ADDRESS, cannot send route discovery request"); + return; } @@ -2234,8 +2527,12 @@ ZigbeeNwk::NlmeRouteDiscoveryRequest(NlmeRouteDiscoveryRequestParams params) { NlmeRouteDiscoveryConfirmParams confirmParams; confirmParams.m_status = NwkStatus::ROUTE_ERROR; + confirmParams.m_networkStatusCode = NetworkStatusCode::NO_ROUTING_CAPACITY; m_nlmeRouteDiscoveryConfirmCallback(confirmParams); } + + NS_LOG_WARN("Device does not have routing capacity, cannot send route discovery request"); + return; } @@ -2255,10 +2552,11 @@ ZigbeeNwk::NlmeRouteDiscoveryRequest(NlmeRouteDiscoveryRequestParams params) payload.SetRouteReqId(m_routeRequestId.GetValue()); payload.SetPathCost(0); - if (params.m_dstAddrMode == UCST_BCST) + if (params.m_dstAddrMode == AddressMode::UCST_BCST || params.m_dstAddrMode == AddressMode::MCST) { - // Set the rest of the nwkHeader and command payload parameters - // as described in Zigbee specification, Section 3.2.2.33.3 + // Set the rest of the nwkHeader and command payload (RREQ) parameters + // as described in Zigbee specification r22.1.0, Section 3.2.2.33.3 + if (params.m_radius == 0) { nwkHeader.SetRadius(m_nwkMaxDepth * 2); @@ -2270,6 +2568,13 @@ ZigbeeNwk::NlmeRouteDiscoveryRequest(NlmeRouteDiscoveryRequestParams params) payload.SetDstAddr(params.m_dstAddr); + if (params.m_dstAddrMode == AddressMode::MCST) + { + // Set the Multicast flag and proceed with the + // multicast destination route discovery + payload.SetMulticastField(true); + } + Mac16Address nextHop; RouteDiscoveryStatus routeStatus = FindNextHop(m_nwkNetworkAddress, 0, nwkHeader, payload, nextHop); @@ -2298,10 +2603,6 @@ ZigbeeNwk::NlmeRouteDiscoveryRequest(NlmeRouteDiscoveryRequestParams params) 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. diff --git a/src/zigbee/model/zigbee-nwk.h b/src/zigbee/model/zigbee-nwk.h index 7f1ef78c0..74e78db66 100644 --- a/src/zigbee/model/zigbee-nwk.h +++ b/src/zigbee/model/zigbee-nwk.h @@ -12,6 +12,7 @@ #ifndef ZIGBEE_NWK_H #define ZIGBEE_NWK_H +#include "zigbee-group-table.h" #include "zigbee-nwk-fields.h" #include "zigbee-nwk-header.h" #include "zigbee-nwk-payload-header.h" @@ -49,7 +50,7 @@ static constexpr uint32_t ALL_CHANNELS = 0x07FFF800; //!< Bitmap representing al //!< Page 0 in Zigbee (250kbps O-QPSK) /** - * @ingroup zigbee + * @ingroup Zigbee * * Indicates a pending NWK primitive */ @@ -67,7 +68,7 @@ enum PendingPrimitiveNwk : std::uint8_t }; /** - * @ingroup zigbee + * @ingroup Zigbee * * Table 3.2 (Address Mode) NLDE-DATA-Request parameters */ @@ -137,7 +138,7 @@ enum RouteDiscoveryStatus : std::uint8_t }; /** - * @ingroup zigbee + * @ingroup Zigbee * * Network layer status values * Combines Zigbee Specification r22.1.0 Table 3-73 and @@ -235,7 +236,7 @@ std::ostream& operator<<(std::ostream& os, const std::vector& vec); std::ostream& operator<<(std::ostream& os, const uint8_t& num); /** - * @ingroup zigbee + * @ingroup Zigbee * * Status codes for network status command frame and route discovery failures. * @@ -265,7 +266,7 @@ enum NetworkStatusCode : std::uint8_t }; /** - * @ingroup zigbee + * @ingroup Zigbee * * Channel List Structure. See Zigbee Specification 3.2.2.2.1 */ @@ -283,7 +284,7 @@ struct ChannelList }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLDE-DATA.confirm params. See Zigbee Specification 3.2.1.2 */ @@ -297,7 +298,7 @@ struct NldeDataConfirmParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLDE-DATA.indication params. See Zigbee Specification 3.2.1.3.1 */ @@ -315,7 +316,7 @@ struct NldeDataIndicationParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLDE-DATA.request params. See Zigbee Specification 3.2.1.1 */ @@ -340,7 +341,7 @@ struct NldeDataRequestParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-NETWORK-FORMATION.request params. See Zigbee Specification 3.2.2.5.1 */ @@ -372,7 +373,7 @@ struct NlmeNetworkFormationRequestParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * A group of pending parameters arranged into a structure during the execution of * a NLME-NETWORK-FORMATION.request primitive. @@ -385,7 +386,7 @@ struct NetFormPendingParamsGen : public SimpleRefCount }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-NETWORK-FORMATION.confirm params. See Zigbee Specification 3.2.2.6.1 */ @@ -396,14 +397,15 @@ struct NlmeNetworkFormationConfirmParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-ROUTE-DISCOVERY.request params. See Zigbee Specification 3.2.2.33 */ struct NlmeRouteDiscoveryRequestParams { AddressMode m_dstAddrMode{UCST_BCST}; //!< Specifies the kind of destination address. - Mac16Address m_dstAddr; //!< The destination of the route discovery. + Mac16Address m_dstAddr; //!< The 16-bit destination address or Group ID (MCST destination) + //!< of the route discovery. uint16_t m_radius{0}; //!< Optional parameter that describes the number of hops that the //!< route request will travel through the network. bool m_noRouteCache{true}; //!< This flag determines whether the NWK should establish a @@ -411,7 +413,7 @@ struct NlmeRouteDiscoveryRequestParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-ROUTE-DISCOVERY.confirm params. See Zigbee Specification r22.1.0, 3.2.2.34 */ @@ -425,7 +427,7 @@ struct NlmeRouteDiscoveryConfirmParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-DIRECT-JOIN.request params. * See Zigbee Specification r22.1.0, 3.2.2.16 @@ -438,7 +440,7 @@ struct NlmeDirectJoinRequestParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-DIRECT-JOIN.confirm params. * See Zigbee Specification r22.1.0, 3.2.2.17 @@ -452,7 +454,7 @@ struct NlmeDirectJoinConfirmParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-NETWORK-DISCOVERY.request params. * See Zigbee Specification r22.1.0, 3.2.2.3 @@ -492,7 +494,7 @@ struct NetworkDescriptor }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-NETWORK-DISCOVERY.confirm params. See Zigbee Specification r22.1.0, 3.2.2.4 */ @@ -506,7 +508,7 @@ struct NlmeNetworkDiscoveryConfirmParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-JOIN.request params. * See Zigbee Specification r22.1.0, 3.2.2.13 @@ -528,7 +530,7 @@ struct NlmeJoinRequestParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-JOIN.confirm params. * See Zigbee Specification r22.1.0, 3.2.2.15 @@ -560,7 +562,7 @@ struct NlmeJoinConfirmParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-JOIN.indication params. * See Zigbee Specification r22.1.0, 3.2.2.14 @@ -579,7 +581,7 @@ struct NlmeJoinIndicationParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-START-ROUTER.request params. * See Zigbee Specification r22.1.0, 3.2.2.13 @@ -592,7 +594,7 @@ struct NlmeStartRouterRequestParams }; /** - * @ingroup zigbee + * @ingroup Zigbee * * NLME-START-ROUTER.confirm params. * See Zigbee Specification r22.1.0, 3.2.2.10 @@ -608,7 +610,7 @@ struct NlmeStartRouterConfirmParams ////////////////////// /** - * @ingroup zigbee + * @ingroup Zigbee * * This callback is called after a NSDU has successfully received and * NWK push it to deliver it to the next higher layer. @@ -617,7 +619,7 @@ struct NlmeStartRouterConfirmParams typedef Callback> NldeDataIndicationCallback; /** - * @ingroup zigbee + * @ingroup Zigbee * * This callback is used to notify the next higher layer with a confirmation in response to * a previously issued NLDE-DATA.request. @@ -626,7 +628,7 @@ typedef Callback> NldeDataIndication typedef Callback NldeDataConfirmCallback; /** - * @ingroup zigbee + * @ingroup Zigbee * * This callback is used to notify the next higher layer with a confirmation in response to * a previously issued NLME-NETWORK-FORMATION.request. @@ -634,7 +636,7 @@ typedef Callback NldeDataConfirmCallback; typedef Callback NlmeNetworkFormationConfirmCallback; /** - * @ingroup zigbee + * @ingroup Zigbee * * This callback is used to notify the next higher layer with a confirmation in response to * a previously issued NLME-NETWORK-DISCOVERY.request. @@ -642,7 +644,7 @@ typedef Callback NlmeNetworkFormationCo typedef Callback NlmeNetworkDiscoveryConfirmCallback; /** - * @ingroup zigbee + * @ingroup Zigbee * * This callback is used to notify the next higher layer with a confirmation in response to * a previously issued NLME-ROUTE-DISCOVERY.request. @@ -650,7 +652,7 @@ typedef Callback NlmeNetworkDiscoveryCo typedef Callback NlmeRouteDiscoveryConfirmCallback; /** - * @ingroup zigbee + * @ingroup Zigbee * * This callback is used to notify the next higher layer with a confirmation in response to * a previously issued NLME-DIRECT-JOIN.request. @@ -658,7 +660,7 @@ typedef Callback NlmeRouteDiscoveryConfir typedef Callback NlmeDirectJoinConfirmCallback; /** - * @ingroup zigbee + * @ingroup Zigbee * * This callback is used to notify the next higher layer with a confirmation in response to * a previously issued NLME-JOIN.request. @@ -666,7 +668,7 @@ typedef Callback NlmeDirectJoinConfirmCallbac typedef Callback NlmeJoinConfirmCallback; /** - * @ingroup zigbee + * @ingroup Zigbee * * This callback is used to notify the next higher layer with an indication that a new * device has successfully joined its network by association or rejoining. @@ -674,7 +676,7 @@ typedef Callback NlmeJoinConfirmCallback; typedef Callback NlmeJoinIndicationCallback; /** - * @ingroup zigbee + * @ingroup Zigbee * * This callback is used to notify the next higher layer with a confirmation in response to * a previously issued NLME-START-ROUTER.request. @@ -682,7 +684,7 @@ typedef Callback NlmeJoinIndicationCallback; typedef Callback NlmeStartRouterConfirmCallback; /** - * @ingroup zigbee + * @ingroup Zigbee * * Class that implements the Zigbee Specification Network Layer */ @@ -709,6 +711,13 @@ class ZigbeeNwk : public Object */ void SetMac(Ptr mac); + /** + * Get the group table used by this Zigbee NWK. + * + * @param groupTable The pointer to the group table to set. + */ + void SetGroupTable(Ptr groupTable); + /** * Get the underlying MAC used by the current Zigbee NWK. * @@ -1283,6 +1292,12 @@ class ZigbeeNwk : public Object */ BroadcastTransactionTable m_btt; + /** + * The Group Table used by this Zigbee NWK. + * See Zigbee specification r22.1.0, 3.6.3.2 + */ + Ptr m_nwkGroupIdTable; + /** * Enqueue a packet in the pending transmission queue until * a route is discovered for its destination. @@ -1445,6 +1460,30 @@ class ZigbeeNwk : public Object ZigbeeNwkHeader nwkHeader, ZigbeePayloadRouteReplyCommand payload); + /** + * Process a Multicast Frame received as a member of a multicast group. + * See Zigbee specification r22.1.0 Section 3.6.6.3 + * + * @param nwkHeader The network header of the received multicast frame. + * @param msdu The MSDU (MSDU = MAC Service Data Unit) received + * @param params The MCPS-DATA.indication parameters associated to the received frame. + */ + void ReceiveMulticastMemberFrame(ZigbeeNwkHeader nwkHeader, + Ptr msdu, + lrwpan::McpsDataIndicationParams params); + + /** + * Process a Multicast Frame received as a non member of a multicast group. + * See Zigbee specification r22.1.0 Section 3.6.6.4 + * + * @param nwkHeader The network header of the received multicast frame. + * @param msdu The MSDU (MSDU = MAC Service Data Unit) received + * @param params The MCPS-DATA.indication parameters associated to the received frame. + */ + void ReceiveMulticastNonMemberFrame(ZigbeeNwkHeader nwkHeader, + Ptr msdu, + lrwpan::McpsDataIndicationParams params); + /** * Returns true if the address is a broadcast address according to * Zigbee specification r22.1.0, Section 3.6.5, Table 3-69 @@ -1480,6 +1519,18 @@ class ZigbeeNwk : public Object */ void SendDataBcst(Ptr packet, uint8_t nwkHandle); + /** + * Send a data multicast packet to all the members of a multicast group. + * + * @param packet The NPDU (nwkHeader + data payload) to transmit. + * @param nwkHandle The NWK handle associated to this packet (nsdu handle). + * A handle of 0 implies that the unicast transmission is a + * retransmission and do not requires a handle which is + * used to identify a packet in confirmations to + * requests (NLDE-DATA.confirm). + */ + void SendDataMcst(Ptr packet, uint8_t nwkHandle); + /** * Find the next hop in route to a destination. * @@ -1526,6 +1577,11 @@ class ZigbeeNwk : public Object */ Ptr m_rreqJitter; + /** + * Provides uniform random values for the broadcast jitter + */ + Ptr m_bcstJitter; + /** * Temporarily store beacons information from POS routers and PAN coordinators * during a network-discovery process. @@ -1613,6 +1669,14 @@ class ZigbeeNwk : public Object */ double m_nwkcMaxRREQJitter; + /** + * Minimum Broadcast jitter time (msec). + * Zigbee Specification r22.1.0, Table 3-57. Defined as a constant + * in the specification but here is defined as variable to allow the selection of values + * per device. + */ + double m_nwkcMaxBroadcastJitter; + ////////////////////////////// // Network layer attributes // ////////////////////////////// @@ -1763,6 +1827,7 @@ class ZigbeeNwk : public Object * The maximum time duration in milliseconds allowed for the parent all the child devices to * retransmit a broadcast message. * See Zigbe Specification r22.1.0, Table 3-58 (NIB attributes) + * See Zigbee-2007 Layer PICS and Stack Profiles */ Time m_nwkPassiveAckTimeout; diff --git a/src/zigbee/model/zigbee-stack.cc b/src/zigbee/model/zigbee-stack.cc index cda11bcda..812a29445 100644 --- a/src/zigbee/model/zigbee-stack.cc +++ b/src/zigbee/model/zigbee-stack.cc @@ -41,6 +41,7 @@ ZigbeeStack::ZigbeeStack() m_nwk = CreateObject(); m_aps = CreateObject(); + m_groupTable = Create(); m_nwkOnly = false; } @@ -59,6 +60,7 @@ ZigbeeStack::DoDispose() m_node = nullptr; m_aps = nullptr; m_nwk = nullptr; + m_groupTable = nullptr; m_mac = nullptr; Object::DoDispose(); } @@ -68,8 +70,6 @@ ZigbeeStack::DoInitialize() { NS_LOG_FUNCTION(this); - // AggregateObject(m_aps); - NS_ABORT_MSG_UNLESS(m_netDevice, "Invalid NetDevice found when attempting to install ZigbeeStack"); @@ -108,10 +108,17 @@ ZigbeeStack::DoInitialize() m_nwk->SetNldeDataConfirmCallback(MakeCallback(&ZigbeeAps::NldeDataConfirm, m_aps)); m_nwk->SetNldeDataIndicationCallback(MakeCallback(&ZigbeeAps::NldeDataIndication, m_aps)); + m_nwk->SetGroupTable(m_groupTable); + m_aps->SetGroupTable(m_groupTable); + m_aps->Initialize(); m_aps->SetNwk(m_nwk); AggregateObject(m_aps); } + else + { + m_nwk->SetGroupTable(m_groupTable); + } // Obtain Extended address as soon as NWK is set to begin operations m_mac->MlmeGetRequest(MacPibAttributeIdentifier::macExtendedAddress); diff --git a/src/zigbee/model/zigbee-stack.h b/src/zigbee/model/zigbee-stack.h index 19013f8af..6d9f29308 100644 --- a/src/zigbee/model/zigbee-stack.h +++ b/src/zigbee/model/zigbee-stack.h @@ -11,6 +11,7 @@ #define ZIGBEE_STACK_H #include "zigbee-aps.h" +#include "zigbee-group-table.h" #include "zigbee-nwk.h" #include "ns3/lr-wpan-mac-base.h" @@ -18,7 +19,6 @@ #include "ns3/traced-callback.h" #include -#include namespace ns3 { @@ -136,8 +136,9 @@ class ZigbeeStack : public Object Ptr m_mac; //!< The underlying LrWpan MAC connected to this Zigbee Stack. Ptr m_nwk; //!< The Zigbee Network layer. Ptr m_aps; //!< The Zigbee Application Support Sub-layer - Ptr m_node; //!< The node associated with this NetDevice. - Ptr m_netDevice; //!< Smart pointer to the underlying NetDevice. + Ptr m_groupTable; //!< The Zigbee Group Table used by both NWK and APS layers. + Ptr m_node; //!< The node associated with this NetDevice. + Ptr m_netDevice; //!< Smart pointer to the underlying NetDevice. bool m_nwkOnly; //!< Indicates that only the NWK layer is present in the Zigbee stack };