mirror of
https://github.com/espressif/openthread.git
synced 2026-08-16 23:57:46 +00:00
[net-diag] define Child, Neighbor, and Child IPv6 Address List TLVs (#8866)
This commit contains multiple changes to Network Diagnostics modules. It adds new TLVs: Child TLV, Child IPv6 Address List TLV, Router Neighbor TLV and MLE counters TLV. Child TLV contains information about a child entry including RLOC16, extended MAC address, mode (rx-on-idle, FTD/MTD, full netdata, CSL-sync), timeout, age, supervision interval, CSL period and timeout, and link quality info such as RSS (last and average) and frame/message error rates. Neighbor TLV provides similar info for a router neighbor. The Child IPv6 Address List TLV allows to query a parent to get the list of IPv6 addresses registered by its MTD children. The new Child and Neighbor TLVs can be requested in Network Diagnostic Get Query "d/dq" message to a router (unicast) or multiple routers (multicast). The router responds with one or more Network Diagnostic Get Answer "d/da" messages. We allow multiple Answer messages to be sent in response to a Query. This is to support the case where a router has many children, and we want to avoid sending a large message that contains all of the TLVs for all of the children. Instead, we can send a sequence of Answer messages, each of which contains a subset of the TLVs. Each Answer message includes an Answer TLV that indicates the index of the message in the sequence. The first Answer message has an index of 0, the second has an index of 1, and so on. The Answer TLV also indicates whether the message is the last one in the sequence, or if there are more Answer messages to come. Answer messages are sent in sequence, and the next one is not sent until we receive a successful CoAP ACK/response for the previous one. This commit adds a Query ID TLV (with a 16-bit identifier as it value) that can be optionally included in a Query message. The Query ID is echoed back in all Answer messages associated with the same Query message. This allows a node that sends multiple Queries to distinguish between the received Answers. This commit updates `MeshDiag` to use the newly added TLVs to query the child table or children IPv6 addresses of a router. New public OpenThread APIs under `otMeshDiag` are added for this, along with CLI commands and their documentation. A test-case is added to validate the newly added mechanism.
This commit is contained in:
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (340)
|
||||
#define OPENTHREAD_API_VERSION (341)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -82,7 +82,7 @@ typedef struct otMeshDiagIp6AddrIterator otMeshDiagIp6AddrIterator;
|
||||
typedef struct otMeshDiagChildIterator otMeshDiagChildIterator;
|
||||
|
||||
/**
|
||||
* This constant indicates that Thread Version is unknown.
|
||||
* Specifies that Thread Version is unknown.
|
||||
*
|
||||
* This is used in `otMeshDiagRouterInfo` for `mVersion` property when device does not provide its version. This
|
||||
* indicates that device is likely running 1.3.0 (version value 4) or earlier.
|
||||
@@ -91,7 +91,7 @@ typedef struct otMeshDiagChildIterator otMeshDiagChildIterator;
|
||||
#define OT_MESH_DIAG_VERSION_UNKNOWN 0xffff
|
||||
|
||||
/**
|
||||
* Represents information about a router in Thread mesh.
|
||||
* Represents information about a router in Thread mesh discovered using `otMeshDiagDiscoverTopology()`.
|
||||
*
|
||||
*/
|
||||
typedef struct otMeshDiagRouterInfo
|
||||
@@ -106,8 +106,8 @@ typedef struct otMeshDiagRouterInfo
|
||||
bool mIsBorderRouter : 1; ///< Whether router acts as a border router providing ext connectivity.
|
||||
|
||||
/**
|
||||
* This array provides the link quality from this router to other routers, also indicating whether a link is
|
||||
* established between the routers.
|
||||
* Provides the link quality from this router to other routers, also indicating whether a link is established
|
||||
* between the routers.
|
||||
*
|
||||
* The array is indexed based on Router ID. `mLinkQualities[routerId]` indicates the incoming link quality, the
|
||||
* router sees to the router with `routerId`. Link quality is a value in [0, 3]. Value zero indicates no link.
|
||||
@@ -142,7 +142,7 @@ typedef struct otMeshDiagRouterInfo
|
||||
} otMeshDiagRouterInfo;
|
||||
|
||||
/**
|
||||
* Represents information about a discovered child in Thread mesh.
|
||||
* Represents information about a discovered child in Thread mesh using `otMeshDiagDiscoverTopology()`.
|
||||
*
|
||||
*/
|
||||
typedef struct otMeshDiagChildInfo
|
||||
@@ -198,7 +198,12 @@ otError otMeshDiagDiscoverTopology(otInstance *aInstance,
|
||||
void otMeshDiagCancel(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Iterates through the discovered IPv6 address of a router.
|
||||
* Iterates through the discovered IPv6 addresses of a router or an MTD child.
|
||||
*
|
||||
* MUST be used
|
||||
* - from the callback `otMeshDiagDiscoverCallback()` and use the `mIp6AddrIterator` from the `aRouterInfo` struct that
|
||||
* is provided as input to the callback, or
|
||||
* - from the callback `otMeshDiagChildIp6AddrsCallback()` along with provided `aIp6AddrIterator`.
|
||||
*
|
||||
* @param[in,out] aIterator The address iterator to use.
|
||||
* @param[out] aIp6Address A pointer to return the next IPv6 address (if any).
|
||||
@@ -212,6 +217,9 @@ otError otMeshDiagGetNextIp6Address(otMeshDiagIp6AddrIterator *aIterator, otIp6A
|
||||
/**
|
||||
* Iterates through the discovered children of a router.
|
||||
*
|
||||
* This function MUST be used from the callback `otMeshDiagDiscoverCallback()` and use the `mChildIterator` from the
|
||||
* `aRouterInfo` struct that is provided as input to the callback.
|
||||
*
|
||||
* @param[in,out] aIterator The address iterator to use.
|
||||
* @param[out] aChildInfo A pointer to return the child info (if any).
|
||||
*
|
||||
@@ -221,6 +229,181 @@ otError otMeshDiagGetNextIp6Address(otMeshDiagIp6AddrIterator *aIterator, otIp6A
|
||||
*/
|
||||
otError otMeshDiagGetNextChildInfo(otMeshDiagChildIterator *aIterator, otMeshDiagChildInfo *aChildInfo);
|
||||
|
||||
/**
|
||||
* Represents information about a child entry from `otMeshDiagQueryChildTable()`.
|
||||
*
|
||||
* `mSupportsErrRate` indicates whether or not the error tracking feature is supported and `mFrameErrorRate` and
|
||||
* `mMessageErrorRate` values are valid. The frame error rate tracks frame tx errors (towards the child) at MAC
|
||||
* layer, while `mMessageErrorRate` tracks the IPv6 message error rate (above MAC layer and after MAC retries) when
|
||||
* an IPv6 message is dropped. For example, if the message is large and requires 6LoWPAN fragmentation, message tx is
|
||||
* considered as failed if one of its fragment frame tx fails (for example, never acked).
|
||||
*
|
||||
*/
|
||||
typedef struct otMeshDiagChildEntry
|
||||
{
|
||||
bool mRxOnWhenIdle : 1; ///< Is rx-on when idle (vs sleepy).
|
||||
bool mDeviceTypeFtd : 1; ///< Is device FTD (vs MTD).
|
||||
bool mFullNetData : 1; ///< Whether device gets full Network Data (vs stable sub-set).
|
||||
bool mCslSynchronized : 1; ///< Is CSL capable and CSL synchronized.
|
||||
bool mSupportsErrRate : 1; ///< `mFrameErrorRate` and `mMessageErrorRate` values are valid.
|
||||
uint16_t mRloc16; ///< RLOC16.
|
||||
otExtAddress mExtAddress; ///< Extended Address.
|
||||
uint16_t mVersion; ///< Version.
|
||||
uint32_t mTimeout; ///< Timeout in seconds.
|
||||
uint32_t mAge; ///< Seconds since last heard from the child.
|
||||
uint32_t mConnectionTime; ///< Seconds since child attach.
|
||||
uint16_t mSupervisionInterval; ///< Supervision interval in seconds. Zero to indicate not used.
|
||||
uint8_t mLinkMargin; ///< Link Margin in dB.
|
||||
int8_t mAverageRssi; ///< Average RSSI.
|
||||
int8_t mLastRssi; ///< RSSI of last received frame.
|
||||
uint16_t mFrameErrorRate; ///< Frame error rate (0x0000->0%, 0xffff->100%).
|
||||
uint16_t mMessageErrorRate; ///< (IPv6) msg error rate (0x0000->0%, 0xffff->100%).
|
||||
uint16_t mQueuedMessageCount; ///< Number of queued messages for indirect tx to child.
|
||||
uint16_t mCslPeriod; ///< CSL Period in unit of 10-symbols-time. Zero indicates CSL is disabled.
|
||||
uint32_t mCslTimeout; ///< CSL Timeout in seconds.
|
||||
uint8_t mCslChannel; ///< CSL channel.
|
||||
} otMeshDiagChildEntry;
|
||||
|
||||
/**
|
||||
* Represents the callback used by `otMeshDiagQueryChildTable()` to provide information about child table entries.
|
||||
*
|
||||
* When @p aError is `OT_ERROR_PENDING`, it indicates that the table still has more entries and the callback will be
|
||||
* invoked again.
|
||||
*
|
||||
* @param[in] aError OT_ERROR_PENDING Indicates there are more entries in the table.
|
||||
* OT_ERROR_NONE Indicates the table is finished.
|
||||
* OT_ERROR_RESPONSE_TIMEOUT Timed out waiting for response.
|
||||
* @param[in] aChildEntry The child entry (can be null if `aError` is OT_ERROR_RESPONSE_TIMEOUT or OT_ERROR_NONE).
|
||||
* @param[in] aContext Application-specific context.
|
||||
*
|
||||
*/
|
||||
typedef void (*otMeshDiagQueryChildTableCallback)(otError aError,
|
||||
const otMeshDiagChildEntry *aChildEntry,
|
||||
void *aContext);
|
||||
|
||||
/**
|
||||
* Starts query for child table for a given router.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance.
|
||||
* @param[in] aRloc16 The RLOC16 of router to query.
|
||||
* @param[in] aCallback The callback to report the queried child table.
|
||||
* @param[in] aContext A context to pass in @p aCallback.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The query started successfully.
|
||||
* @retval OT_ERROR_BUSY A previous discovery or query request is still ongoing.
|
||||
* @retval OT_ERROR_INVALID_ARGS The @p aRloc16 is not a valid router RLOC16.
|
||||
* @retval OT_ERROR_INVALID_STATE Device is not attached.
|
||||
* @retval OT_ERROR_NO_BUFS Could not allocate buffer to send query messages.
|
||||
*
|
||||
*/
|
||||
otError otMeshDiagQueryChildTable(otInstance *aInstance,
|
||||
uint16_t aRloc16,
|
||||
otMeshDiagQueryChildTableCallback aCallback,
|
||||
void *aContext);
|
||||
|
||||
/**
|
||||
* Represents the callback used by `otMeshDiagQueryChildrenIp6Addrs()` to provide information about an MTD child and
|
||||
* its list of IPv6 addresses.
|
||||
*
|
||||
* When @p aError is `OT_ERROR_PENDING`, it indicates that there are more children and the callback will be invoked
|
||||
* again.
|
||||
*
|
||||
* @param[in] aError OT_ERROR_PENDING Indicates there are more children in the table.
|
||||
* OT_ERROR_NONE Indicates the table is finished.
|
||||
* OT_ERROR_RESPONSE_TIMEOUT Timed out waiting for response.
|
||||
* @param[in] aChildRloc16 The RLOC16 of the child. `0xfffe` is used on `OT_ERROR_RESPONSE_TIMEOUT`.
|
||||
* @param[in] aIp6AddrIterator An iterator to go through the IPv6 addresses of the child with @p aRloc using
|
||||
* `otMeshDiagGetNextIp6Address()`. Set to NULL on `OT_ERROR_RESPONSE_TIMEOUT`.
|
||||
* @param[in] aContext Application-specific context.
|
||||
*
|
||||
*/
|
||||
typedef void (*otMeshDiagChildIp6AddrsCallback)(otError aError,
|
||||
uint16_t aChildRloc16,
|
||||
otMeshDiagIp6AddrIterator *aIp6AddrIterator,
|
||||
void *aContext);
|
||||
|
||||
/**
|
||||
* Sends a query to a parent to retrieve the IPv6 addresses of all its MTD children.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance.
|
||||
* @param[in] aRloc16 The RLOC16 of parent to query.
|
||||
* @param[in] aCallback The callback to report the queried child IPv6 address list.
|
||||
* @param[in] aContext A context to pass in @p aCallback.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The query started successfully.
|
||||
* @retval OT_ERROR_BUSY A previous discovery or query request is still ongoing.
|
||||
* @retval OT_ERROR_INVALID_ARGS The @p aRloc16 is not a valid RLOC16.
|
||||
* @retval OT_ERROR_INVALID_STATE Device is not attached.
|
||||
* @retval OT_ERROR_NO_BUFS Could not allocate buffer to send query messages.
|
||||
*
|
||||
*/
|
||||
otError otMeshDiagQueryChildrenIp6Addrs(otInstance *aInstance,
|
||||
uint16_t aRloc16,
|
||||
otMeshDiagChildIp6AddrsCallback aCallback,
|
||||
void *aContext);
|
||||
|
||||
/**
|
||||
* Represents information about a router neighbor entry from `otMeshDiagQueryRouterNeighborTable()`.
|
||||
*
|
||||
* `mSupportsErrRate` indicates whether or not the error tracking feature is supported and `mFrameErrorRate` and
|
||||
* `mMessageErrorRate` values are valid. The frame error rate tracks frame tx errors (towards the child) at MAC
|
||||
* layer, while `mMessageErrorRate` tracks the IPv6 message error rate (above MAC layer and after MAC retries) when
|
||||
* an IPv6 message is dropped. For example, if the message is large and requires 6LoWPAN fragmentation, message tx is
|
||||
* considered as failed if one of its fragment frame tx fails (for example, never acked).
|
||||
*
|
||||
*/
|
||||
typedef struct otMeshDiagRouterNeighborEntry
|
||||
{
|
||||
bool mSupportsErrRate : 1; ///< `mFrameErrorRate` and `mMessageErrorRate` values are valid.
|
||||
uint16_t mRloc16; ///< RLOC16.
|
||||
otExtAddress mExtAddress; ///< Extended Address.
|
||||
uint16_t mVersion; ///< Version.
|
||||
uint32_t mConnectionTime; ///< Seconds since link establishment.
|
||||
uint8_t mLinkMargin; ///< Link Margin in dB.
|
||||
int8_t mAverageRssi; ///< Average RSSI.
|
||||
int8_t mLastRssi; ///< RSSI of last received frame.
|
||||
uint16_t mFrameErrorRate; ///< Frame error rate (0x0000->0%, 0xffff->100%).
|
||||
uint16_t mMessageErrorRate; ///< (IPv6) msg error rate (0x0000->0%, 0xffff->100%).
|
||||
} otMeshDiagRouterNeighborEntry;
|
||||
|
||||
/**
|
||||
* Represents the callback used by `otMeshDiagQueryRouterNeighborTable()` to provide information about neighbor router
|
||||
* table entries.
|
||||
*
|
||||
* When @p aError is `OT_ERROR_PENDING`, it indicates that the table still has more entries and the callback will be
|
||||
* invoked again.
|
||||
*
|
||||
* @param[in] aError OT_ERROR_PENDING Indicates there are more entries in the table.
|
||||
* OT_ERROR_NONE Indicates the table is finished.
|
||||
* OT_ERROR_RESPONSE_TIMEOUT Timed out waiting for response.
|
||||
* @param[in] aNeighborEntry The neighbor entry (can be null if `aError` is RESPONSE_TIMEOUT or NONE).
|
||||
* @param[in] aContext Application-specific context.
|
||||
*
|
||||
*/
|
||||
typedef void (*otMeshDiagQueryRouterNeighborTableCallback)(otError aError,
|
||||
const otMeshDiagRouterNeighborEntry *aNeighborEntry,
|
||||
void *aContext);
|
||||
|
||||
/**
|
||||
* Starts query for router neighbor table for a given router.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance.
|
||||
* @param[in] aRloc16 The RLOC16 of router to query.
|
||||
* @param[in] aCallback The callback to report the queried table.
|
||||
* @param[in] aContext A context to pass in @p aCallback.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The query started successfully.
|
||||
* @retval OT_ERROR_BUSY A previous discovery or query request is still ongoing.
|
||||
* @retval OT_ERROR_INVALID_ARGS The @p aRloc16 is not a valid router RLOC16.
|
||||
* @retval OT_ERROR_INVALID_STATE Device is not attached.
|
||||
* @retval OT_ERROR_NO_BUFS Could not allocate buffer to send query messages.
|
||||
*
|
||||
*/
|
||||
otError otMeshDiagQueryRouterNeighborTable(otInstance *aInstance,
|
||||
uint16_t aRloc16,
|
||||
otMeshDiagQueryRouterNeighborTableCallback aCallback,
|
||||
void *aContext);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -87,6 +87,13 @@ enum
|
||||
OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL = 26, ///< Vendor Model TLV
|
||||
OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION = 27, ///< Vendor SW Version TLV
|
||||
OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION = 28, ///< Thread Stack Version TLV
|
||||
OT_NETWORK_DIAGNOSTIC_TLV_CHILD = 29, ///< Child TLV
|
||||
OT_NETWORK_DIAGNOSTIC_TLV_CHILD_IP6_ADDR_LIST = 30, ///< Child IPv6 Address List TLV
|
||||
OT_NETWORK_DIAGNOSTIC_TLV_ROUTER_NEIGHBOR = 31, ///< Router Neighbor TLV
|
||||
OT_NETWORK_DIAGNOSTIC_TLV_ANSWER = 32, ///< Answer TLV
|
||||
OT_NETWORK_DIAGNOSTIC_TLV_QUERY_ID = 33, ///< Query ID TLV
|
||||
OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS = 34, ///< MLE Counters TLV
|
||||
|
||||
};
|
||||
|
||||
#define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_NAME_TLV_LENGTH 32 ///< Max length of Vendor Name TLV.
|
||||
@@ -201,6 +208,29 @@ typedef struct otNetworkDiagMacCounters
|
||||
uint32_t mIfOutDiscards;
|
||||
} otNetworkDiagMacCounters;
|
||||
|
||||
/**
|
||||
* Represents a Network Diagnostics MLE Counters value.
|
||||
*
|
||||
*/
|
||||
typedef struct otNetworkDiagMleCounters
|
||||
{
|
||||
uint16_t mDisabledRole; ///< Number of times device entered disabled role.
|
||||
uint16_t mDetachedRole; ///< Number of times device entered detached role.
|
||||
uint16_t mChildRole; ///< Number of times device entered child role.
|
||||
uint16_t mRouterRole; ///< Number of times device entered router role.
|
||||
uint16_t mLeaderRole; ///< Number of times device entered leader role.
|
||||
uint16_t mAttachAttempts; ///< Number of attach attempts while device was detached.
|
||||
uint16_t mPartitionIdChanges; ///< Number of changes to partition ID.
|
||||
uint16_t mBetterPartitionAttachAttempts; ///< Number of attempts to attach to a better partition.
|
||||
uint16_t mParentChanges; ///< Number of time device changed its parent.
|
||||
uint64_t mTrackedTime; ///< Milliseconds tracked by next counters (zero if not supported).
|
||||
uint64_t mDisabledTime; ///< Milliseconds device has been in disabled role.
|
||||
uint64_t mDetachedTime; ///< Milliseconds device has been in detached role.
|
||||
uint64_t mChildTime; ///< Milliseconds device has been in child role.
|
||||
uint64_t mRouterTime; ///< Milliseconds device has been in router role.
|
||||
uint64_t mLeaderTime; ///< Milliseconds device has been in leader role.
|
||||
} otNetworkDiagMleCounters;
|
||||
|
||||
/**
|
||||
* Represents a Network Diagnostic Child Table Entry.
|
||||
*
|
||||
@@ -252,6 +282,7 @@ typedef struct otNetworkDiagTlv
|
||||
otNetworkDiagRoute mRoute;
|
||||
otLeaderData mLeaderData;
|
||||
otNetworkDiagMacCounters mMacCounters;
|
||||
otNetworkDiagMleCounters mMleCounters;
|
||||
uint8_t mBatteryLevel;
|
||||
uint16_t mSupplyVoltage;
|
||||
uint32_t mMaxChildTimeout;
|
||||
|
||||
@@ -2059,6 +2059,91 @@ id:62 rloc16:0xf800 ext-addr:ce349873897233a5 ver:4 - br
|
||||
children: none
|
||||
```
|
||||
|
||||
### meshdiag childtable \<router-rloc16\>
|
||||
|
||||
Start a query for child table of a router with a given RLOC16.
|
||||
|
||||
Output lists all child entries. Information per child:
|
||||
|
||||
- RLOC16
|
||||
- Extended MAC address
|
||||
- Thread Version
|
||||
- Timeout (in seconds)
|
||||
- Age (seconds since last heard)
|
||||
- Supervision interval (in seconds)
|
||||
- Number of queued messages (in case the child is sleepy)
|
||||
- Device Mode
|
||||
- RSS (average and last) and link margin
|
||||
- Error rates, frame tx (at MAC layer), IPv6 message tx (above MAC)
|
||||
- Connection time (seconds since link establishment {dd}d.{hh}:{mm}:{ss} format)
|
||||
- CSL info
|
||||
- If synchronized
|
||||
- Period (in unit of 10-symbols-time)
|
||||
- Timeout (in seconds)
|
||||
- Channel
|
||||
|
||||
```bash
|
||||
> meshdiag childtable 0x6400
|
||||
rloc16:0x6402 ext-addr:8e6f4d323bbed1fe ver:4
|
||||
timeout:120 age:36 supvn:129 q-msg:0
|
||||
rx-on:yes type:ftd full-net:yes
|
||||
rss - ave:-20 last:-20 margin:80
|
||||
err-rate - frame:11.51% msg:0.76%
|
||||
conn-time:00:11:07
|
||||
csl - sync:no period:0 timeout:0 channel:0
|
||||
rloc16:0x6403 ext-addr:ee24e64ecf8c079a ver:4
|
||||
timeout:120 age:19 supvn:129 q-msg:0
|
||||
rx-on:no type:mtd full-net:no
|
||||
rss - ave:-20 last:-20 margin:80
|
||||
err-rate - frame:0.73% msg:0.00%
|
||||
conn-time:01:08:53
|
||||
csl - sync:no period:0 timeout:0 channel:0
|
||||
Done
|
||||
```
|
||||
|
||||
### meshdiag childip6 \<parent-rloc16\>
|
||||
|
||||
Send a query to a parent to retrieve the IPv6 addresses of all its MTD children.
|
||||
|
||||
```bash
|
||||
> meshdiag childip6 0xdc00
|
||||
child-rloc16: 0xdc02
|
||||
fdde:ad00:beef:0:ded8:cd58:b73:2c21
|
||||
fd00:2:0:0:c24a:456:3b6b:c597
|
||||
fd00:1:0:0:120b:95fe:3ecc:d238
|
||||
child-rloc16: 0xdc03
|
||||
fdde:ad00:beef:0:3aa6:b8bf:e7d6:eefe
|
||||
fd00:2:0:0:8ff8:a188:7436:6720
|
||||
fd00:1:0:0:1fcf:5495:790a:370f
|
||||
Done
|
||||
```
|
||||
|
||||
### meshdiag routerneighbortable \<router-rloc16\>
|
||||
|
||||
Start a query for router neighbor table of a router with a given RLOC16.
|
||||
|
||||
Output lists all router neighbor entries. Information per entry:
|
||||
|
||||
- RLOC16
|
||||
- Extended MAC address
|
||||
- Thread Version
|
||||
- RSS (average and last) and link margin
|
||||
- Error rates, frame tx (at MAC layer), IPv6 message tx (above MAC)
|
||||
- Connection time (seconds since link establishment {dd}d.{hh}:{mm}:{ss} format)
|
||||
|
||||
```bash
|
||||
> meshdiag routerneighbortable 0x7400
|
||||
rloc16:0x9c00 ext-addr:764788cf6e57a4d2 ver:4
|
||||
rss - ave:-20 last:-20 margin:80
|
||||
err-rate - frame:1.38% msg:0.00%
|
||||
conn-time:01:54:02
|
||||
rloc16:0x7c00 ext-addr:4ed24fceec9bf6d3 ver:4
|
||||
rss - ave:-20 last:-20 margin:80
|
||||
err-rate - frame:0.72% msg:0.00%
|
||||
conn-time:00:11:27
|
||||
Done
|
||||
```
|
||||
|
||||
### mliid \<iid\>
|
||||
|
||||
Set the Mesh Local IID.
|
||||
|
||||
+306
-9
@@ -5018,6 +5018,129 @@ template <> otError Interpreter::Process<Cmd("meshdiag")>(Arg aArgs[])
|
||||
SuccessOrExit(error = otMeshDiagDiscoverTopology(GetInstancePtr(), &config, HandleMeshDiagDiscoverDone, this));
|
||||
error = OT_ERROR_PENDING;
|
||||
}
|
||||
/**
|
||||
* @cli meshdiag childtable
|
||||
* @code
|
||||
* meshdiag childtable 0x6400
|
||||
* rloc16:0x6402 ext-addr:8e6f4d323bbed1fe ver:4
|
||||
* timeout:120 age:36 supvn:129 q-msg:0
|
||||
* rx-on:yes type:ftd full-net:yes
|
||||
* rss - ave:-20 last:-20 margin:80
|
||||
* err-rate - frame:11.51% msg:0.76%
|
||||
* conn-time:00:11:07
|
||||
* csl - sync:no period:0 timeout:0 channel:0
|
||||
* rloc16:0x6403 ext-addr:ee24e64ecf8c079a ver:4
|
||||
* timeout:120 age:19 supvn:129 q-msg:0
|
||||
* rx-on:no type:mtd full-net:no
|
||||
* rss - ave:-20 last:-20 margin:80
|
||||
* err-rate - frame:0.73% msg:0.00%
|
||||
* conn-time:01:08:53
|
||||
* csl - sync:no period:0 timeout:0 channel:0
|
||||
* Done
|
||||
* @endcode
|
||||
* @par
|
||||
* Start a query for child table of a router with a given RLOC16.
|
||||
* Output lists all child entries. Information per child:
|
||||
* - RLOC16
|
||||
* - Extended MAC address
|
||||
* - Thread Version
|
||||
* - Timeout (in seconds)
|
||||
* - Age (seconds since last heard)
|
||||
* - Supervision interval (in seconds)
|
||||
* - Number of queued messages (in case child is sleepy)
|
||||
* - Device Mode
|
||||
* - RSS (average and last)
|
||||
* - Error rates: frame tx (at MAC layer), IPv6 message tx (above MAC)
|
||||
* - Connection time (seconds since link establishment `{dd}d.{hh}:{mm}:{ss}` format)
|
||||
* - CSL info:
|
||||
* - If synchronized
|
||||
* - Period (in unit of 10-symbols-time)
|
||||
* - Timeout (in seconds)
|
||||
*
|
||||
* @cparam meshdiag childtable @ca{router-rloc16}
|
||||
* @sa otMeshDiagQueryChildTable
|
||||
*/
|
||||
else if (aArgs[0] == "childtable")
|
||||
{
|
||||
uint16_t routerRloc16;
|
||||
|
||||
SuccessOrExit(error = aArgs[1].ParseAsUint16(routerRloc16));
|
||||
VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
SuccessOrExit(error = otMeshDiagQueryChildTable(GetInstancePtr(), routerRloc16,
|
||||
HandleMeshDiagQueryChildTableResult, this));
|
||||
|
||||
error = OT_ERROR_PENDING;
|
||||
}
|
||||
/**
|
||||
* @cli meshdiag childip6
|
||||
* @code
|
||||
* meshdiag childip6 0xdc00
|
||||
* child-rloc16: 0xdc02
|
||||
* fdde:ad00:beef:0:ded8:cd58:b73:2c21
|
||||
* fd00:2:0:0:c24a:456:3b6b:c597
|
||||
* fd00:1:0:0:120b:95fe:3ecc:d238
|
||||
* child-rloc16: 0xdc03
|
||||
* fdde:ad00:beef:0:3aa6:b8bf:e7d6:eefe
|
||||
* fd00:2:0:0:8ff8:a188:7436:6720
|
||||
* fd00:1:0:0:1fcf:5495:790a:370f
|
||||
* Done
|
||||
* @endcode
|
||||
* @par
|
||||
* Send a query to a parent to retrieve the IPv6 addresses of all its MTD children.
|
||||
* @cparam meshdiag childip6 @ca{parent-rloc16}
|
||||
* @sa otMeshDiagQueryChildrenIp6Addrs
|
||||
*/
|
||||
else if (aArgs[0] == "childip6")
|
||||
{
|
||||
uint16_t parentRloc16;
|
||||
|
||||
SuccessOrExit(error = aArgs[1].ParseAsUint16(parentRloc16));
|
||||
VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
SuccessOrExit(error = otMeshDiagQueryChildrenIp6Addrs(GetInstancePtr(), parentRloc16,
|
||||
HandleMeshDiagQueryChildIp6Addrs, this));
|
||||
|
||||
error = OT_ERROR_PENDING;
|
||||
}
|
||||
/**
|
||||
* @cli meshdiag routerneighbortable
|
||||
* @code
|
||||
* meshdiag routerneighbortable 0x7400
|
||||
* rloc16:0x9c00 ext-addr:764788cf6e57a4d2 ver:4
|
||||
* rss - ave:-20 last:-20 margin:80
|
||||
* err-rate - frame:1.38% msg:0.00%
|
||||
* conn-time:01:54:02
|
||||
* rloc16:0x7c00 ext-addr:4ed24fceec9bf6d3 ver:4
|
||||
* rss - ave:-20 last:-20 margin:80
|
||||
* err-rate - frame:0.72% msg:0.00%
|
||||
* conn-time:00:11:27
|
||||
* Done
|
||||
* @endcode
|
||||
* @par
|
||||
* Start a query for router neighbor table of a router with a given RLOC16.
|
||||
* Output lists all router neighbor entries. Information per entry:
|
||||
* - RLOC16
|
||||
* - Extended MAC address
|
||||
* - Thread Version
|
||||
* - RSS (average and last) and link margin
|
||||
* - Error rates, frame tx (at MAC layer), IPv6 message tx (above MAC)
|
||||
* - Connection time (seconds since link establishment `{dd}d.{hh}:{mm}:{ss}` format)
|
||||
* @cparam meshdiag routerneighbortable @ca{router-rloc16}
|
||||
* @sa otMeshDiagQueryRouterNeighborTable
|
||||
*/
|
||||
else if (aArgs[0] == "routerneighbortable")
|
||||
{
|
||||
uint16_t routerRloc16;
|
||||
|
||||
SuccessOrExit(error = aArgs[1].ParseAsUint16(routerRloc16));
|
||||
VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
SuccessOrExit(error = otMeshDiagQueryRouterNeighborTable(GetInstancePtr(), routerRloc16,
|
||||
HandleMeshDiagQueryRouterNeighborTableResult, this));
|
||||
|
||||
error = OT_ERROR_PENDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = OT_ERROR_INVALID_COMMAND;
|
||||
@@ -5148,6 +5271,116 @@ exit:
|
||||
OutputResult(aError);
|
||||
}
|
||||
|
||||
void Interpreter::HandleMeshDiagQueryChildTableResult(otError aError,
|
||||
const otMeshDiagChildEntry *aChildEntry,
|
||||
void *aContext)
|
||||
{
|
||||
reinterpret_cast<Interpreter *>(aContext)->HandleMeshDiagQueryChildTableResult(aError, aChildEntry);
|
||||
}
|
||||
|
||||
void Interpreter::HandleMeshDiagQueryChildTableResult(otError aError, const otMeshDiagChildEntry *aChildEntry)
|
||||
{
|
||||
PercentageStringBuffer stringBuffer;
|
||||
char string[OT_DURATION_STRING_SIZE];
|
||||
|
||||
VerifyOrExit(aChildEntry != nullptr);
|
||||
|
||||
OutputFormat("rloc16:0x%04x ext-addr:", aChildEntry->mRloc16);
|
||||
OutputExtAddress(aChildEntry->mExtAddress);
|
||||
OutputLine(" ver:%u", aChildEntry->mVersion);
|
||||
|
||||
OutputLine(kIndentSize, "timeout:%lu age:%lu supvn:%u q-msg:%u", ToUlong(aChildEntry->mTimeout),
|
||||
ToUlong(aChildEntry->mAge), aChildEntry->mSupervisionInterval, aChildEntry->mQueuedMessageCount);
|
||||
|
||||
OutputLine(kIndentSize, "rx-on:%s type:%s full-net:%s", aChildEntry->mRxOnWhenIdle ? "yes" : "no",
|
||||
aChildEntry->mDeviceTypeFtd ? "ftd" : "mtd", aChildEntry->mFullNetData ? "yes" : "no");
|
||||
|
||||
OutputLine(kIndentSize, "rss - ave:%d last:%d margin:%d", aChildEntry->mAverageRssi, aChildEntry->mLastRssi,
|
||||
aChildEntry->mLinkMargin);
|
||||
|
||||
if (aChildEntry->mSupportsErrRate)
|
||||
{
|
||||
OutputFormat(kIndentSize, "err-rate - frame:%s%% ",
|
||||
PercentageToString(aChildEntry->mFrameErrorRate, stringBuffer));
|
||||
OutputLine("msg:%s%% ", PercentageToString(aChildEntry->mMessageErrorRate, stringBuffer));
|
||||
}
|
||||
|
||||
otConvertDurationInSecondsToString(aChildEntry->mConnectionTime, string, sizeof(string));
|
||||
OutputLine(kIndentSize, "conn-time:%s", string);
|
||||
|
||||
OutputLine(kIndentSize, "csl - sync:%s period:%u timeout:%lu channel:%u",
|
||||
aChildEntry->mCslSynchronized ? "yes" : "no", aChildEntry->mCslPeriod, ToUlong(aChildEntry->mCslTimeout),
|
||||
aChildEntry->mCslChannel);
|
||||
|
||||
exit:
|
||||
OutputResult(aError);
|
||||
}
|
||||
|
||||
void Interpreter::HandleMeshDiagQueryRouterNeighborTableResult(otError aError,
|
||||
const otMeshDiagRouterNeighborEntry *aNeighborEntry,
|
||||
void *aContext)
|
||||
{
|
||||
reinterpret_cast<Interpreter *>(aContext)->HandleMeshDiagQueryRouterNeighborTableResult(aError, aNeighborEntry);
|
||||
}
|
||||
|
||||
void Interpreter::HandleMeshDiagQueryRouterNeighborTableResult(otError aError,
|
||||
const otMeshDiagRouterNeighborEntry *aNeighborEntry)
|
||||
{
|
||||
PercentageStringBuffer stringBuffer;
|
||||
char string[OT_DURATION_STRING_SIZE];
|
||||
|
||||
VerifyOrExit(aNeighborEntry != nullptr);
|
||||
|
||||
OutputFormat("rloc16:0x%04x ext-addr:", aNeighborEntry->mRloc16);
|
||||
OutputExtAddress(aNeighborEntry->mExtAddress);
|
||||
OutputLine(" ver:%u", aNeighborEntry->mVersion);
|
||||
|
||||
OutputLine(kIndentSize, "rss - ave:%d last:%d margin:%d", aNeighborEntry->mAverageRssi, aNeighborEntry->mLastRssi,
|
||||
aNeighborEntry->mLinkMargin);
|
||||
|
||||
if (aNeighborEntry->mSupportsErrRate)
|
||||
{
|
||||
OutputFormat(kIndentSize, "err-rate - frame:%s%% ",
|
||||
PercentageToString(aNeighborEntry->mFrameErrorRate, stringBuffer));
|
||||
OutputLine("msg:%s%% ", PercentageToString(aNeighborEntry->mMessageErrorRate, stringBuffer));
|
||||
}
|
||||
|
||||
otConvertDurationInSecondsToString(aNeighborEntry->mConnectionTime, string, sizeof(string));
|
||||
OutputLine(kIndentSize, "conn-time:%s", string);
|
||||
|
||||
exit:
|
||||
OutputResult(aError);
|
||||
}
|
||||
|
||||
void Interpreter::HandleMeshDiagQueryChildIp6Addrs(otError aError,
|
||||
uint16_t aChildRloc16,
|
||||
otMeshDiagIp6AddrIterator *aIp6AddrIterator,
|
||||
void *aContext)
|
||||
{
|
||||
reinterpret_cast<Interpreter *>(aContext)->HandleMeshDiagQueryChildIp6Addrs(aError, aChildRloc16, aIp6AddrIterator);
|
||||
}
|
||||
|
||||
void Interpreter::HandleMeshDiagQueryChildIp6Addrs(otError aError,
|
||||
uint16_t aChildRloc16,
|
||||
otMeshDiagIp6AddrIterator *aIp6AddrIterator)
|
||||
{
|
||||
otIp6Address ip6Address;
|
||||
|
||||
VerifyOrExit(aError == OT_ERROR_NONE || aError == OT_ERROR_PENDING);
|
||||
VerifyOrExit(aIp6AddrIterator != nullptr);
|
||||
|
||||
OutputLine("child-rloc16: 0x%04x", aChildRloc16);
|
||||
|
||||
while (otMeshDiagGetNextIp6Address(aIp6AddrIterator, &ip6Address) == OT_ERROR_NONE)
|
||||
{
|
||||
OutputSpaces(kIndentSize);
|
||||
OutputIp6AddressLine(ip6Address);
|
||||
}
|
||||
|
||||
exit:
|
||||
OutputResult(aError);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE
|
||||
|
||||
#endif // OPENTHREAD_FTD
|
||||
@@ -7198,6 +7431,10 @@ void Interpreter::HandleDiagnosticGetResponse(otError aError,
|
||||
OutputLine("MAC Counters:");
|
||||
OutputNetworkDiagMacCounters(kIndentSize, diagTlv.mData.mMacCounters);
|
||||
break;
|
||||
case OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS:
|
||||
OutputLine("MLE Counters:");
|
||||
OutputNetworkDiagMleCounters(kIndentSize, diagTlv.mData.mMleCounters);
|
||||
break;
|
||||
case OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL:
|
||||
OutputLine("Battery Level: %u%%", diagTlv.mData.mBatteryLevel);
|
||||
break;
|
||||
@@ -7293,15 +7530,75 @@ void Interpreter::OutputLeaderData(uint8_t aIndentSize, const otLeaderData &aLea
|
||||
|
||||
void Interpreter::OutputNetworkDiagMacCounters(uint8_t aIndentSize, const otNetworkDiagMacCounters &aMacCounters)
|
||||
{
|
||||
OutputLine(aIndentSize, "IfInUnknownProtos: %lu", ToUlong(aMacCounters.mIfInUnknownProtos));
|
||||
OutputLine(aIndentSize, "IfInErrors: %lu", ToUlong(aMacCounters.mIfInErrors));
|
||||
OutputLine(aIndentSize, "IfOutErrors: %lu", ToUlong(aMacCounters.mIfOutErrors));
|
||||
OutputLine(aIndentSize, "IfInUcastPkts: %lu", ToUlong(aMacCounters.mIfInUcastPkts));
|
||||
OutputLine(aIndentSize, "IfInBroadcastPkts: %lu", ToUlong(aMacCounters.mIfInBroadcastPkts));
|
||||
OutputLine(aIndentSize, "IfInDiscards: %lu", ToUlong(aMacCounters.mIfInDiscards));
|
||||
OutputLine(aIndentSize, "IfOutUcastPkts: %lu", ToUlong(aMacCounters.mIfOutUcastPkts));
|
||||
OutputLine(aIndentSize, "IfOutBroadcastPkts: %lu", ToUlong(aMacCounters.mIfOutBroadcastPkts));
|
||||
OutputLine(aIndentSize, "IfOutDiscards: %lu", ToUlong(aMacCounters.mIfOutDiscards));
|
||||
struct CounterName
|
||||
{
|
||||
const uint32_t otNetworkDiagMacCounters::*mValuePtr;
|
||||
const char *mName;
|
||||
};
|
||||
|
||||
static const CounterName kCounterNames[] = {
|
||||
{&otNetworkDiagMacCounters::mIfInUnknownProtos, "IfInUnknownProtos"},
|
||||
{&otNetworkDiagMacCounters::mIfInErrors, "IfInErrors"},
|
||||
{&otNetworkDiagMacCounters::mIfOutErrors, "IfOutErrors"},
|
||||
{&otNetworkDiagMacCounters::mIfInUcastPkts, "IfInUcastPkts"},
|
||||
{&otNetworkDiagMacCounters::mIfInBroadcastPkts, "IfInBroadcastPkts"},
|
||||
{&otNetworkDiagMacCounters::mIfInDiscards, "IfInDiscards"},
|
||||
{&otNetworkDiagMacCounters::mIfOutUcastPkts, "IfOutUcastPkts"},
|
||||
{&otNetworkDiagMacCounters::mIfOutBroadcastPkts, "IfOutBroadcastPkts"},
|
||||
{&otNetworkDiagMacCounters::mIfOutDiscards, "IfOutDiscards"},
|
||||
};
|
||||
|
||||
for (const CounterName &counter : kCounterNames)
|
||||
{
|
||||
OutputLine(aIndentSize, "%s: %lu", counter.mName, ToUlong(aMacCounters.*counter.mValuePtr));
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::OutputNetworkDiagMleCounters(uint8_t aIndentSize, const otNetworkDiagMleCounters &aMleCounters)
|
||||
{
|
||||
struct CounterName
|
||||
{
|
||||
const uint16_t otNetworkDiagMleCounters::*mValuePtr;
|
||||
const char *mName;
|
||||
};
|
||||
|
||||
struct TimeCounterName
|
||||
{
|
||||
const uint64_t otNetworkDiagMleCounters::*mValuePtr;
|
||||
const char *mName;
|
||||
};
|
||||
|
||||
static const CounterName kCounterNames[] = {
|
||||
{&otNetworkDiagMleCounters::mDisabledRole, "DisabledRole"},
|
||||
{&otNetworkDiagMleCounters::mDetachedRole, "DetachedRole"},
|
||||
{&otNetworkDiagMleCounters::mChildRole, "ChildRole"},
|
||||
{&otNetworkDiagMleCounters::mRouterRole, "RouterRole"},
|
||||
{&otNetworkDiagMleCounters::mLeaderRole, "LeaderRole"},
|
||||
{&otNetworkDiagMleCounters::mAttachAttempts, "AttachAttempts"},
|
||||
{&otNetworkDiagMleCounters::mPartitionIdChanges, "PartitionIdChanges"},
|
||||
{&otNetworkDiagMleCounters::mBetterPartitionAttachAttempts, "BetterPartitionAttachAttempts"},
|
||||
{&otNetworkDiagMleCounters::mParentChanges, "ParentChanges"},
|
||||
};
|
||||
|
||||
static const TimeCounterName kTimeCounterNames[] = {
|
||||
{&otNetworkDiagMleCounters::mTrackedTime, "TrackedTime"},
|
||||
{&otNetworkDiagMleCounters::mDisabledTime, "DisabledTime"},
|
||||
{&otNetworkDiagMleCounters::mDetachedTime, "DetachedTime"},
|
||||
{&otNetworkDiagMleCounters::mChildTime, "ChildTime"},
|
||||
{&otNetworkDiagMleCounters::mRouterTime, "RouterTime"},
|
||||
{&otNetworkDiagMleCounters::mLeaderTime, "LeaderTime"},
|
||||
};
|
||||
|
||||
for (const CounterName &counter : kCounterNames)
|
||||
{
|
||||
OutputLine(aIndentSize, "%s: %u", counter.mName, aMleCounters.*counter.mValuePtr);
|
||||
}
|
||||
|
||||
for (const TimeCounterName &counter : kTimeCounterNames)
|
||||
{
|
||||
OutputFormat("%s: ", counter.mName);
|
||||
OutputUint64Line(aMleCounters.*counter.mValuePtr);
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiagChildEntry &aChildEntry)
|
||||
|
||||
@@ -428,6 +428,23 @@ private:
|
||||
#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
|
||||
static void HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext);
|
||||
void HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo);
|
||||
static void HandleMeshDiagQueryChildTableResult(otError aError,
|
||||
const otMeshDiagChildEntry *aChildEntry,
|
||||
void *aContext);
|
||||
void HandleMeshDiagQueryChildTableResult(otError aError, const otMeshDiagChildEntry *aChildEntry);
|
||||
static void HandleMeshDiagQueryChildIp6Addrs(otError aError,
|
||||
uint16_t aChildRloc16,
|
||||
otMeshDiagIp6AddrIterator *aIp6AddrIterator,
|
||||
void *aContext);
|
||||
void HandleMeshDiagQueryChildIp6Addrs(otError aError,
|
||||
uint16_t aChildRloc16,
|
||||
otMeshDiagIp6AddrIterator *aIp6AddrIterator);
|
||||
static void HandleMeshDiagQueryRouterNeighborTableResult(otError aError,
|
||||
const otMeshDiagRouterNeighborEntry *aNeighborEntry,
|
||||
void *aContext);
|
||||
void HandleMeshDiagQueryRouterNeighborTableResult(otError aError,
|
||||
const otMeshDiagRouterNeighborEntry *aNeighborEntry);
|
||||
|
||||
#endif
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
static void HandleMlrRegResult(void *aContext,
|
||||
@@ -465,6 +482,7 @@ private:
|
||||
void OutputRouteData(uint8_t aIndentSize, const otNetworkDiagRouteData &aRouteData);
|
||||
void OutputLeaderData(uint8_t aIndentSize, const otLeaderData &aLeaderData);
|
||||
void OutputNetworkDiagMacCounters(uint8_t aIndentSize, const otNetworkDiagMacCounters &aMacCounters);
|
||||
void OutputNetworkDiagMleCounters(uint8_t aIndentSize, const otNetworkDiagMleCounters &aMleCounters);
|
||||
void OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiagChildEntry &aChildEntry);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -668,6 +668,7 @@ openthread_core_files = [
|
||||
"thread/network_data_types.hpp",
|
||||
"thread/network_diagnostic.cpp",
|
||||
"thread/network_diagnostic.hpp",
|
||||
"thread/network_diagnostic_tlvs.cpp",
|
||||
"thread/network_diagnostic_tlvs.hpp",
|
||||
"thread/panid_query_server.cpp",
|
||||
"thread/panid_query_server.hpp",
|
||||
|
||||
@@ -226,6 +226,7 @@ set(COMMON_SOURCES
|
||||
thread/network_data_tlvs.cpp
|
||||
thread/network_data_types.cpp
|
||||
thread/network_diagnostic.cpp
|
||||
thread/network_diagnostic_tlvs.cpp
|
||||
thread/panid_query_server.cpp
|
||||
thread/radio_selector.cpp
|
||||
thread/router_table.cpp
|
||||
|
||||
@@ -64,4 +64,28 @@ otError otMeshDiagGetNextChildInfo(otMeshDiagChildIterator *aIterator, otMeshDia
|
||||
return AsCoreType(aIterator).GetNextChildInfo(AsCoreType(aChildInfo));
|
||||
}
|
||||
|
||||
otError otMeshDiagQueryChildTable(otInstance *aInstance,
|
||||
uint16_t aRloc16,
|
||||
otMeshDiagQueryChildTableCallback aCallback,
|
||||
void *aContext)
|
||||
{
|
||||
return AsCoreType(aInstance).Get<Utils::MeshDiag>().QueryChildTable(aRloc16, aCallback, aContext);
|
||||
}
|
||||
|
||||
otError otMeshDiagQueryChildrenIp6Addrs(otInstance *aInstance,
|
||||
uint16_t aRloc16,
|
||||
otMeshDiagChildIp6AddrsCallback aCallback,
|
||||
void *aContext)
|
||||
{
|
||||
return AsCoreType(aInstance).Get<Utils::MeshDiag>().QueryChildrenIp6Addrs(aRloc16, aCallback, aContext);
|
||||
}
|
||||
|
||||
otError otMeshDiagQueryRouterNeighborTable(otInstance *aInstance,
|
||||
uint16_t aRloc16,
|
||||
otMeshDiagQueryRouterNeighborTableCallback aCallback,
|
||||
void *aContext)
|
||||
{
|
||||
return AsCoreType(aInstance).Get<Utils::MeshDiag>().QueryRouterNeighborTable(aRloc16, aCallback, aContext);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
|
||||
|
||||
@@ -57,6 +57,13 @@ const uint8_t *Tlv::GetValue(void) const
|
||||
Error Tlv::AppendTo(Message &aMessage) const { return aMessage.AppendBytes(this, static_cast<uint16_t>(GetSize())); }
|
||||
|
||||
Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv)
|
||||
{
|
||||
uint16_t offset;
|
||||
|
||||
return FindTlv(aMessage, aType, aMaxSize, aTlv, offset);
|
||||
}
|
||||
|
||||
Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv, uint16_t &aOffset)
|
||||
{
|
||||
Error error;
|
||||
ParsedInfo info;
|
||||
@@ -69,11 +76,11 @@ Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tl
|
||||
}
|
||||
|
||||
aMessage.ReadBytes(info.mOffset, &aTlv, aMaxSize);
|
||||
aOffset = info.mOffset;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error Tlv::FindTlvValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aValueOffset, uint16_t &aLength)
|
||||
{
|
||||
Error error;
|
||||
|
||||
@@ -263,6 +263,23 @@ public:
|
||||
*/
|
||||
static Error FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv);
|
||||
|
||||
/**
|
||||
* Searches for and reads a requested TLV out of a given message.
|
||||
*
|
||||
* Can be used independent of whether the read TLV (from message) is an Extended TLV or not.
|
||||
*
|
||||
* @param[in] aMessage A reference to the message.
|
||||
* @param[in] aType The Type value to search for.
|
||||
* @param[in] aMaxSize Maximum number of bytes to read.
|
||||
* @param[out] aTlv A reference to the TLV that will be copied to.
|
||||
* @param[out] aOffset A reference to return the offset to start of the TLV in @p aMessage.
|
||||
*
|
||||
* @retval kErrorNone Successfully copied the TLV.
|
||||
* @retval kErrorNotFound Could not find the TLV with Type @p aType.
|
||||
*
|
||||
*/
|
||||
static Error FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv, uint16_t &aOffset);
|
||||
|
||||
/**
|
||||
* Searches for and reads a requested TLV out of a given message.
|
||||
*
|
||||
@@ -282,6 +299,26 @@ public:
|
||||
return FindTlv(aMessage, TlvType::kType, sizeof(TlvType), aTlv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for and reads a requested TLV out of a given message.
|
||||
*
|
||||
* Can be used independent of whether the read TLV (from message) is an Extended TLV or not.
|
||||
*
|
||||
* @tparam TlvType The TlvType to search for (must be a sub-class of `Tlv`).
|
||||
*
|
||||
* @param[in] aMessage A reference to the message.
|
||||
* @param[out] aTlv A reference to the TLV that will be copied to.
|
||||
* @param[out] aOffset A reference to return the offset to start of the TLV in @p aMessage.
|
||||
*
|
||||
* @retval kErrorNone Successfully copied the TLV.
|
||||
* @retval kErrorNotFound Could not find the TLV with Type @p aType.
|
||||
*
|
||||
*/
|
||||
template <typename TlvType> static Error FindTlv(const Message &aMessage, TlvType &aTlv, uint16_t &aOffset)
|
||||
{
|
||||
return FindTlv(aMessage, TlvType::kType, sizeof(TlvType), aTlv, aOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the offset and length of TLV value for a given TLV type within @p aMessage.
|
||||
*
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "common/instance.hpp"
|
||||
#include "common/locator_getters.hpp"
|
||||
#include "common/log.hpp"
|
||||
#include "common/random.hpp"
|
||||
#include "mac/mac.hpp"
|
||||
#include "net/netif.hpp"
|
||||
#include "thread/mesh_forwarder.hpp"
|
||||
@@ -401,11 +402,7 @@ exit:
|
||||
template <>
|
||||
void Server::HandleTmf<kUriDiagnosticGetQuery>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
Coap::Message *response = nullptr;
|
||||
Tmf::MessageInfo responseInfo(GetInstance());
|
||||
|
||||
VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop);
|
||||
VerifyOrExit(aMessage.IsPostRequest());
|
||||
|
||||
LogInfo("Received %s from %s", UriToString<kUriDiagnosticGetQuery>(),
|
||||
aMessageInfo.GetPeerAddr().ToString().AsCString());
|
||||
@@ -416,18 +413,377 @@ void Server::HandleTmf<kUriDiagnosticGetQuery>(Coap::Message &aMessage, const Ip
|
||||
IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
|
||||
}
|
||||
|
||||
response = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriDiagnosticGetAnswer);
|
||||
VerifyOrExit(response != nullptr, error = kErrorNoBufs);
|
||||
|
||||
SuccessOrExit(error = AppendRequestedTlvs(aMessage, *response));
|
||||
|
||||
PrepareMessageInfoForDest(aMessageInfo.GetPeerAddr(), responseInfo);
|
||||
SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*response, responseInfo));
|
||||
#if OPENTHREAD_MTD
|
||||
SendAnswer(aMessageInfo.GetPeerAddr(), aMessage);
|
||||
#elif OPENTHREAD_FTD
|
||||
PrepareAndSendAnswers(aMessageInfo.GetPeerAddr(), aMessage);
|
||||
#endif
|
||||
|
||||
exit:
|
||||
FreeMessageOnError(response, error);
|
||||
return;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_MTD
|
||||
|
||||
void Server::SendAnswer(const Ip6::Address &aDestination, const Message &aRequest)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
Coap::Message *answer = nullptr;
|
||||
Tmf::MessageInfo messageInfo(GetInstance());
|
||||
AnswerTlv answerTlv;
|
||||
uint16_t queryId;
|
||||
|
||||
answer = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriDiagnosticGetAnswer);
|
||||
VerifyOrExit(answer != nullptr, error = kErrorNoBufs);
|
||||
|
||||
IgnoreError(answer->SetPriority(aRequest.GetPriority()));
|
||||
|
||||
if (Tlv::Find<QueryIdTlv>(aRequest, queryId) == kErrorNone)
|
||||
{
|
||||
SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*answer, queryId));
|
||||
}
|
||||
|
||||
SuccessOrExit(error = AppendRequestedTlvs(aRequest, *answer));
|
||||
|
||||
answerTlv.Init(0, /* aIsLast */ true);
|
||||
SuccessOrExit(answer->Append(answerTlv));
|
||||
|
||||
PrepareMessageInfoForDest(aDestination, messageInfo);
|
||||
|
||||
error = Get<Tmf::Agent>().SendMessage(*answer, messageInfo);
|
||||
|
||||
exit:
|
||||
FreeMessageOnError(answer, error);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_MTD
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
|
||||
Error Server::AllocateAnswer(Coap::Message *&aAnswer, AnswerInfo &aInfo)
|
||||
{
|
||||
// Allocate an `Answer` message, adds it in `mAnswerQueue`,
|
||||
// update the `aInfo.mFirstAnswer` if it is the first allocated
|
||||
// messages, and appends `QueryIdTlv` to the message (if needed).
|
||||
|
||||
Error error = kErrorNone;
|
||||
|
||||
aAnswer = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriDiagnosticGetAnswer);
|
||||
VerifyOrExit(aAnswer != nullptr, error = kErrorNoBufs);
|
||||
IgnoreError(aAnswer->SetPriority(aInfo.mPriority));
|
||||
|
||||
mAnswerQueue.Enqueue(*aAnswer);
|
||||
|
||||
if (aInfo.mFirstAnswer == nullptr)
|
||||
{
|
||||
aInfo.mFirstAnswer = aAnswer;
|
||||
}
|
||||
|
||||
if (aInfo.mHasQueryId)
|
||||
{
|
||||
SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*aAnswer, aInfo.mQueryId));
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
bool Server::IsLastAnswer(const Coap::Message &aAnswer) const
|
||||
{
|
||||
// Indicates whether `aAnswer` is the last one associated with
|
||||
// the same query.
|
||||
|
||||
bool isLast = true;
|
||||
AnswerTlv answerTlv;
|
||||
|
||||
// If there is no Answer TLV, we assume it is the last answer.
|
||||
|
||||
SuccessOrExit(Tlv::FindTlv(aAnswer, answerTlv));
|
||||
isLast = answerTlv.IsLast();
|
||||
|
||||
exit:
|
||||
return isLast;
|
||||
}
|
||||
|
||||
void Server::FreeAllRelatedAnswers(Coap::Message &aFirstAnswer)
|
||||
{
|
||||
// This method dequeues and frees all answer messages related to
|
||||
// same query as `aFirstAnswer`. Note that related answers are
|
||||
// enqueued in order.
|
||||
|
||||
Coap::Message *answer = &aFirstAnswer;
|
||||
|
||||
while (answer != nullptr)
|
||||
{
|
||||
Coap::Message *next = IsLastAnswer(*answer) ? nullptr : answer->GetNextCoapMessage();
|
||||
|
||||
mAnswerQueue.DequeueAndFree(*answer);
|
||||
answer = next;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Message &aRequest)
|
||||
{
|
||||
Coap::Message *answer;
|
||||
Error error;
|
||||
AnswerInfo info;
|
||||
uint16_t offset;
|
||||
uint16_t length;
|
||||
uint16_t endOffset;
|
||||
AnswerTlv answerTlv;
|
||||
|
||||
if (Tlv::Find<QueryIdTlv>(aRequest, info.mQueryId) == kErrorNone)
|
||||
{
|
||||
info.mHasQueryId = true;
|
||||
}
|
||||
|
||||
info.mPriority = aRequest.GetPriority();
|
||||
|
||||
SuccessOrExit(error = AllocateAnswer(answer, info));
|
||||
|
||||
SuccessOrExit(error = Tlv::FindTlvValueOffset(aRequest, Tlv::kTypeList, offset, length));
|
||||
endOffset = offset + length;
|
||||
|
||||
for (; offset < endOffset; offset++)
|
||||
{
|
||||
uint8_t tlvType;
|
||||
|
||||
SuccessOrExit(error = aRequest.Read(offset, tlvType));
|
||||
|
||||
switch (tlvType)
|
||||
{
|
||||
case ChildTlv::kType:
|
||||
SuccessOrExit(error = AppendChildTableAsChildTlvs(answer, info));
|
||||
break;
|
||||
case ChildIp6AddressListTlv::kType:
|
||||
SuccessOrExit(error = AppendChildTableIp6AddressList(answer, info));
|
||||
break;
|
||||
case RouterNeighborTlv::kType:
|
||||
SuccessOrExit(error = AppendRouterNeighborTlvs(answer, info));
|
||||
break;
|
||||
default:
|
||||
SuccessOrExit(error = AppendDiagTlv(tlvType, *answer));
|
||||
break;
|
||||
}
|
||||
|
||||
SuccessOrExit(error = CheckAnswerLength(answer, info));
|
||||
}
|
||||
|
||||
answerTlv.Init(info.mAnswerIndex, /* aIsLast */ true);
|
||||
SuccessOrExit(error = answer->Append(answerTlv));
|
||||
|
||||
SendNextAnswer(*info.mFirstAnswer, aDestination);
|
||||
|
||||
exit:
|
||||
if ((error != kErrorNone) && (info.mFirstAnswer != nullptr))
|
||||
{
|
||||
FreeAllRelatedAnswers(*info.mFirstAnswer);
|
||||
}
|
||||
}
|
||||
|
||||
Error Server::CheckAnswerLength(Coap::Message *&aAnswer, AnswerInfo &aInfo)
|
||||
{
|
||||
// This method checks the length of the `aAnswer` message and if it
|
||||
// is above the threshold, it enqueues the message for transmission
|
||||
// after appending an Answer TLV with the current index to the
|
||||
// message. In this case, it will also allocate a new answer
|
||||
// message.
|
||||
|
||||
Error error = kErrorNone;
|
||||
AnswerTlv answerTlv;
|
||||
|
||||
VerifyOrExit(aAnswer->GetLength() >= kAnswerMessageLengthThreshold);
|
||||
|
||||
answerTlv.Init(aInfo.mAnswerIndex++, /* aIsLast */ false);
|
||||
SuccessOrExit(error = aAnswer->Append(answerTlv));
|
||||
|
||||
error = AllocateAnswer(aAnswer, aInfo);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void Server::SendNextAnswer(Coap::Message &aAnswer, const Ip6::Address &aDestination)
|
||||
{
|
||||
// This method send the given next `aAnswer` associated with
|
||||
// a query to the `aDestination`.
|
||||
|
||||
Error error = kErrorNone;
|
||||
Coap::Message *nextAnswer = IsLastAnswer(aAnswer) ? nullptr : aAnswer.GetNextCoapMessage();
|
||||
Tmf::MessageInfo messageInfo(GetInstance());
|
||||
|
||||
mAnswerQueue.Dequeue(aAnswer);
|
||||
|
||||
PrepareMessageInfoForDest(aDestination, messageInfo);
|
||||
|
||||
// When sending the message, we pass `nextAnswer` as `aContext`
|
||||
// to be used when invoking callback `HandleAnswerResponse()`.
|
||||
|
||||
error = Get<Tmf::Agent>().SendMessage(aAnswer, messageInfo, HandleAnswerResponse, nextAnswer);
|
||||
|
||||
if (error != kErrorNone)
|
||||
{
|
||||
// If the `SendMessage()` fails, we `Free` the dequeued
|
||||
// `aAnswer` and all the related next answers in the queue.
|
||||
|
||||
aAnswer.Free();
|
||||
|
||||
if (nextAnswer != nullptr)
|
||||
{
|
||||
FreeAllRelatedAnswers(*nextAnswer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::HandleAnswerResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult)
|
||||
{
|
||||
Coap::Message *nextAnswer = static_cast<Coap::Message *>(aContext);
|
||||
|
||||
VerifyOrExit(nextAnswer != nullptr);
|
||||
|
||||
nextAnswer->Get<Server>().HandleAnswerResponse(*nextAnswer, AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo),
|
||||
aResult);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Server::HandleAnswerResponse(Coap::Message &aNextAnswer,
|
||||
Coap::Message *aResponse,
|
||||
const Ip6::MessageInfo *aMessageInfo,
|
||||
Error aResult)
|
||||
{
|
||||
Error error = aResult;
|
||||
|
||||
SuccessOrExit(error);
|
||||
VerifyOrExit(aResponse != nullptr && aMessageInfo != nullptr, error = kErrorDrop);
|
||||
VerifyOrExit(aResponse->GetCode() == Coap::kCodeChanged, error = kErrorDrop);
|
||||
|
||||
SendNextAnswer(aNextAnswer, aMessageInfo->GetPeerAddr());
|
||||
|
||||
exit:
|
||||
if (error != kErrorNone)
|
||||
{
|
||||
FreeAllRelatedAnswers(aNextAnswer);
|
||||
}
|
||||
}
|
||||
|
||||
Error Server::AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
ChildTlv childTlv;
|
||||
|
||||
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
|
||||
{
|
||||
childTlv.InitFrom(child);
|
||||
|
||||
SuccessOrExit(error = childTlv.AppendTo(*aAnswer));
|
||||
SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
|
||||
}
|
||||
|
||||
// Add empty TLV to indicate end of the list
|
||||
|
||||
childTlv.InitAsEmpty();
|
||||
SuccessOrExit(error = childTlv.AppendTo(*aAnswer));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error Server::AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
RouterNeighborTlv neighborTlv;
|
||||
|
||||
for (Router &router : Get<RouterTable>())
|
||||
{
|
||||
if (!router.IsStateValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
neighborTlv.InitFrom(router);
|
||||
|
||||
SuccessOrExit(error = neighborTlv.AppendTo(*aAnswer));
|
||||
SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
|
||||
}
|
||||
|
||||
// Add empty TLV to indicate end of the list
|
||||
|
||||
neighborTlv.InitAsEmpty();
|
||||
SuccessOrExit(error = neighborTlv.AppendTo(*aAnswer));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error Server::AppendChildTableIp6AddressList(Coap::Message *&aAnswer, AnswerInfo &aInfo)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
Tlv tlv;
|
||||
|
||||
for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
|
||||
{
|
||||
SuccessOrExit(error = AppendChildIp6AddressListTlv(*aAnswer, child));
|
||||
SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
|
||||
}
|
||||
|
||||
// Add empty TLV to indicate end of the list
|
||||
|
||||
tlv.SetType(Tlv::kChildIp6AddressList);
|
||||
tlv.SetLength(0);
|
||||
SuccessOrExit(error = aAnswer->Append(tlv));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error Server::AppendChildIp6AddressListTlv(Coap::Message &aAnswer, const Child &aChild)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
uint16_t numIp6Addr = 0;
|
||||
ChildIp6AddressListTlvValue tlvValue;
|
||||
|
||||
for (const Ip6::Address &address : aChild.IterateIp6Addresses())
|
||||
{
|
||||
OT_UNUSED_VARIABLE(address);
|
||||
numIp6Addr++;
|
||||
}
|
||||
|
||||
VerifyOrExit(numIp6Addr > 0);
|
||||
|
||||
if ((numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue)) <= Tlv::kBaseTlvMaxLength)
|
||||
{
|
||||
Tlv tlv;
|
||||
|
||||
tlv.SetType(Tlv::kChildIp6AddressList);
|
||||
tlv.SetLength(static_cast<uint8_t>(numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue)));
|
||||
SuccessOrExit(error = aAnswer.Append(tlv));
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtendedTlv extTlv;
|
||||
|
||||
extTlv.SetType(Tlv::kChildIp6AddressList);
|
||||
extTlv.SetLength(numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue));
|
||||
SuccessOrExit(error = aAnswer.Append(extTlv));
|
||||
}
|
||||
|
||||
tlvValue.SetRloc16(aChild.GetRloc16());
|
||||
|
||||
SuccessOrExit(error = aAnswer.Append(tlvValue));
|
||||
|
||||
for (const Ip6::Address &address : aChild.IterateIp6Addresses())
|
||||
{
|
||||
SuccessOrExit(error = aAnswer.Append(address));
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_FTD
|
||||
|
||||
template <>
|
||||
void Server::HandleTmf<kUriDiagnosticGetRequest>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
@@ -442,6 +798,7 @@ void Server::HandleTmf<kUriDiagnosticGetRequest>(Coap::Message &aMessage, const
|
||||
response = Get<Tmf::Agent>().NewResponseMessage(aMessage);
|
||||
VerifyOrExit(response != nullptr, error = kErrorNoBufs);
|
||||
|
||||
IgnoreError(response->SetPriority(aMessage.GetPriority()));
|
||||
SuccessOrExit(error = AppendRequestedTlvs(aMessage, *response));
|
||||
SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*response, aMessageInfo));
|
||||
|
||||
@@ -476,6 +833,10 @@ template <> void Server::HandleTmf<kUriDiagnosticReset>(Coap::Message &aMessage,
|
||||
Get<Mac::Mac>().ResetCounters();
|
||||
break;
|
||||
|
||||
case Tlv::kMleCounters:
|
||||
Get<Mle::Mle>().ResetCounters();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -494,6 +855,7 @@ exit:
|
||||
|
||||
Client::Client(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mQueryId(Random::NonCrypto::GetUint16())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -507,11 +869,12 @@ Error Client::SendDiagnosticGet(const Ip6::Address &aDestination,
|
||||
|
||||
if (aDestination.IsMulticast())
|
||||
{
|
||||
error = SendCommand(kUriDiagnosticGetQuery, aDestination, aTlvTypes, aCount);
|
||||
error = SendCommand(kUriDiagnosticGetQuery, Message::kPriorityNormal, aDestination, aTlvTypes, aCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
error = SendCommand(kUriDiagnosticGetRequest, aDestination, aTlvTypes, aCount, &HandleGetResponse, this);
|
||||
error = SendCommand(kUriDiagnosticGetRequest, Message::kPriorityNormal, aDestination, aTlvTypes, aCount,
|
||||
&HandleGetResponse, this);
|
||||
}
|
||||
|
||||
SuccessOrExit(error);
|
||||
@@ -523,6 +886,7 @@ exit:
|
||||
}
|
||||
|
||||
Error Client::SendCommand(Uri aUri,
|
||||
Message::Priority aPriority,
|
||||
const Ip6::Address &aDestination,
|
||||
const uint8_t aTlvTypes[],
|
||||
uint8_t aCount,
|
||||
@@ -549,12 +913,18 @@ Error Client::SendCommand(Uri aUri,
|
||||
}
|
||||
|
||||
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
||||
IgnoreError(message->SetPriority(aPriority));
|
||||
|
||||
if (aCount > 0)
|
||||
{
|
||||
SuccessOrExit(error = Tlv::Append<TypeListTlv>(*message, aTlvTypes, aCount));
|
||||
}
|
||||
|
||||
if (aUri == kUriDiagnosticGetQuery)
|
||||
{
|
||||
SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*message, ++mQueryId));
|
||||
}
|
||||
|
||||
Get<Server>().PrepareMessageInfoForDest(aDestination, messageInfo);
|
||||
|
||||
SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aHandler, aContext));
|
||||
@@ -589,7 +959,13 @@ void Client::HandleTmf<kUriDiagnosticGetAnswer>(Coap::Message &aMessage, const I
|
||||
LogInfo("Received %s from %s", ot::UriToString<kUriDiagnosticGetAnswer>(),
|
||||
aMessageInfo.GetPeerAddr().ToString().AsCString());
|
||||
|
||||
mGetCallback.InvokeIfSet(kErrorNone, &aMessage, &aMessageInfo);
|
||||
#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
|
||||
// Let the `MeshDiag` process the message first.
|
||||
if (!Get<Utils::MeshDiag>().HandleDiagnosticGetAnswer(aMessage, aMessageInfo))
|
||||
#endif
|
||||
{
|
||||
mGetCallback.InvokeIfSet(kErrorNone, &aMessage, &aMessageInfo);
|
||||
}
|
||||
|
||||
IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
|
||||
|
||||
@@ -599,7 +975,7 @@ exit:
|
||||
|
||||
Error Client::SendDiagnosticReset(const Ip6::Address &aDestination, const uint8_t aTlvTypes[], uint8_t aCount)
|
||||
{
|
||||
return SendCommand(kUriDiagnosticReset, aDestination, aTlvTypes, aCount);
|
||||
return SendCommand(kUriDiagnosticReset, Message::kPriorityNormal, aDestination, aTlvTypes, aCount);
|
||||
}
|
||||
|
||||
static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDiagRoute)
|
||||
@@ -770,6 +1146,16 @@ Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator,
|
||||
break;
|
||||
}
|
||||
|
||||
case Tlv::kMleCounters:
|
||||
{
|
||||
MleCountersTlv mleCoutersTlv;
|
||||
|
||||
SuccessOrExit(error = aMessage.Read(offset, mleCoutersTlv));
|
||||
VerifyOrExit(mleCoutersTlv.IsValid(), error = kErrorParse);
|
||||
mleCoutersTlv.Read(aTlvInfo.mData.mMleCounters);
|
||||
break;
|
||||
}
|
||||
|
||||
case Tlv::kBatteryLevel:
|
||||
SuccessOrExit(error = Tlv::Read<BatteryLevelTlv>(aMessage, offset, aTlvInfo.mData.mBatteryLevel));
|
||||
break;
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
|
||||
namespace ot {
|
||||
|
||||
namespace Utils {
|
||||
class MeshDiag;
|
||||
}
|
||||
|
||||
namespace NetworkDiagnostic {
|
||||
|
||||
/**
|
||||
@@ -144,7 +148,27 @@ public:
|
||||
#endif // OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
|
||||
|
||||
private:
|
||||
static constexpr uint16_t kMaxChildEntries = 398;
|
||||
static constexpr uint16_t kMaxChildEntries = 398;
|
||||
static constexpr uint16_t kAnswerMessageLengthThreshold = 800;
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
struct AnswerInfo
|
||||
{
|
||||
AnswerInfo(void)
|
||||
: mAnswerIndex(0)
|
||||
, mQueryId(0)
|
||||
, mHasQueryId(false)
|
||||
, mFirstAnswer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t mAnswerIndex;
|
||||
uint16_t mQueryId;
|
||||
bool mHasQueryId;
|
||||
Message::Priority mPriority;
|
||||
Coap::Message *mFirstAnswer;
|
||||
};
|
||||
#endif
|
||||
|
||||
static const char kVendorName[];
|
||||
static const char kVendorModel[];
|
||||
@@ -153,10 +177,34 @@ private:
|
||||
Error AppendDiagTlv(uint8_t aTlvType, Message &aMessage);
|
||||
Error AppendIp6AddressList(Message &aMessage);
|
||||
Error AppendMacCounters(Message &aMessage);
|
||||
Error AppendChildTable(Message &aMessage);
|
||||
Error AppendRequestedTlvs(const Message &aRequest, Message &aResponse);
|
||||
void PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::MessageInfo &aMessageInfo) const;
|
||||
|
||||
#if OPENTHREAD_MTD
|
||||
void SendAnswer(const Ip6::Address &aDestination, const Message &aRequest);
|
||||
#elif OPENTHREAD_FTD
|
||||
Error AllocateAnswer(Coap::Message *&aAnswer, AnswerInfo &aInfo);
|
||||
Error CheckAnswerLength(Coap::Message *&aAnswer, AnswerInfo &aInfo);
|
||||
bool IsLastAnswer(const Coap::Message &aAnswer) const;
|
||||
void FreeAllRelatedAnswers(Coap::Message &aFirstAnswer);
|
||||
void PrepareAndSendAnswers(const Ip6::Address &aDestination, const Message &aRequest);
|
||||
void SendNextAnswer(Coap::Message &aAnswer, const Ip6::Address &aDestination);
|
||||
Error AppendChildTable(Message &aMessage);
|
||||
Error AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo);
|
||||
Error AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo);
|
||||
Error AppendChildTableIp6AddressList(Coap::Message *&aAnswer, AnswerInfo &aInfo);
|
||||
Error AppendChildIp6AddressListTlv(Coap::Message &aAnswer, const Child &aChild);
|
||||
|
||||
static void HandleAnswerResponse(void *aContext,
|
||||
otMessage *aMessage,
|
||||
const otMessageInfo *aMessageInfo,
|
||||
Error aResult);
|
||||
void HandleAnswerResponse(Coap::Message &aNextAnswer,
|
||||
Coap::Message *aResponse,
|
||||
const Ip6::MessageInfo *aMessageInfo,
|
||||
Error aResult);
|
||||
#endif
|
||||
|
||||
template <Uri kUri> void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||
|
||||
#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
|
||||
@@ -166,6 +214,10 @@ private:
|
||||
VendorModelTlv::StringType mVendorModel;
|
||||
VendorSwVersionTlv::StringType mVendorSwVersion;
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
Coap::MessageQueue mAnswerQueue;
|
||||
#endif
|
||||
};
|
||||
|
||||
DeclareTmfHandler(Server, kUriDiagnosticGetRequest);
|
||||
@@ -181,6 +233,7 @@ DeclareTmfHandler(Server, kUriDiagnosticGetAnswer);
|
||||
class Client : public InstanceLocator, private NonCopyable
|
||||
{
|
||||
friend class Tmf::Agent;
|
||||
friend class Utils::MeshDiag;
|
||||
|
||||
public:
|
||||
typedef otNetworkDiagIterator Iterator; ///< Iterator to go through TLVs in `GetNextDiagTlv()`.
|
||||
@@ -239,8 +292,17 @@ public:
|
||||
*/
|
||||
static Error GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo);
|
||||
|
||||
/**
|
||||
* This method returns the query ID used for the last Network Diagnostic Query command.
|
||||
*
|
||||
* @returns The query ID used for last query.
|
||||
*
|
||||
*/
|
||||
uint16_t GetLastQueryId(void) const { return mQueryId; }
|
||||
|
||||
private:
|
||||
Error SendCommand(Uri aUri,
|
||||
Message::Priority aPriority,
|
||||
const Ip6::Address &aDestination,
|
||||
const uint8_t aTlvTypes[],
|
||||
uint8_t aCount,
|
||||
@@ -259,6 +321,7 @@ private:
|
||||
static const char *UriToString(Uri aUri);
|
||||
#endif
|
||||
|
||||
uint16_t mQueryId;
|
||||
Callback<GetCallback> mGetCallback;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2023, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file implements methods for generating and processing Network Diagnostics TLVs.
|
||||
*/
|
||||
|
||||
#include "network_diagnostic_tlvs.hpp"
|
||||
|
||||
#include "common/time.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace NetworkDiagnostic {
|
||||
|
||||
using ot::Encoding::BigEndian::HostSwap16;
|
||||
using ot::Encoding::BigEndian::HostSwap32;
|
||||
using ot::Encoding::BigEndian::HostSwap64;
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
|
||||
void ChildTlv::InitFrom(const Child &aChild)
|
||||
{
|
||||
Clear();
|
||||
|
||||
SetType(kChild);
|
||||
SetLength(sizeof(*this) - sizeof(Tlv));
|
||||
|
||||
mFlags |= aChild.IsRxOnWhenIdle() ? kFlagsRxOnWhenIdle : 0;
|
||||
mFlags |= aChild.IsFullThreadDevice() ? kFlagsFtd : 0;
|
||||
mFlags |= (aChild.GetNetworkDataType() == NetworkData::kFullSet) ? kFlagsFullNetdta : 0;
|
||||
mFlags |= kFlagsTrackErrRate;
|
||||
|
||||
mRloc16 = HostSwap16(aChild.GetRloc16());
|
||||
mExtAddress = aChild.GetExtAddress();
|
||||
mVersion = HostSwap16(aChild.GetVersion());
|
||||
mTimeout = HostSwap32(aChild.GetTimeout());
|
||||
mAge = HostSwap32(Time::MsecToSec(TimerMilli::GetNow() - aChild.GetLastHeard()));
|
||||
mConnectionTime = HostSwap32(aChild.GetConnectionTime());
|
||||
mSupervisionInterval = HostSwap16(aChild.GetSupervisionInterval());
|
||||
mLinkMargin = aChild.GetLinkInfo().GetLinkMargin();
|
||||
mAverageRssi = aChild.GetLinkInfo().GetAverageRss();
|
||||
mLastRssi = aChild.GetLinkInfo().GetLastRss();
|
||||
mFrameErrorRate = HostSwap16(aChild.GetLinkInfo().GetFrameErrorRate());
|
||||
mMessageErrorRate = HostSwap16(aChild.GetLinkInfo().GetMessageErrorRate());
|
||||
mQueuedMessageCount = HostSwap16(aChild.GetIndirectMessageCount());
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
|
||||
mFlags |= aChild.IsCslSynchronized() ? kFlagsCslSync : 0;
|
||||
mCslPeriod = HostSwap16(aChild.GetCslPeriod());
|
||||
mCslTimeout = HostSwap32(aChild.GetCslTimeout());
|
||||
mCslChannel = aChild.GetCslChannel();
|
||||
#endif
|
||||
}
|
||||
|
||||
void RouterNeighborTlv::InitFrom(const Router &aRouter)
|
||||
{
|
||||
Clear();
|
||||
|
||||
SetType(kRouterNeighbor);
|
||||
SetLength(sizeof(*this) - sizeof(Tlv));
|
||||
|
||||
mFlags |= kFlagsTrackErrRate;
|
||||
mRloc16 = HostSwap16(aRouter.GetRloc16());
|
||||
mExtAddress = aRouter.GetExtAddress();
|
||||
mVersion = HostSwap16(aRouter.GetVersion());
|
||||
mConnectionTime = HostSwap32(aRouter.GetConnectionTime());
|
||||
mLinkMargin = aRouter.GetLinkInfo().GetLinkMargin();
|
||||
mAverageRssi = aRouter.GetLinkInfo().GetAverageRss();
|
||||
mLastRssi = aRouter.GetLinkInfo().GetLastRss();
|
||||
mFrameErrorRate = HostSwap16(aRouter.GetLinkInfo().GetFrameErrorRate());
|
||||
mMessageErrorRate = HostSwap16(aRouter.GetLinkInfo().GetMessageErrorRate());
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_FTD
|
||||
|
||||
void AnswerTlv::Init(uint16_t aIndex, bool aIsLast)
|
||||
{
|
||||
SetType(kAnswer);
|
||||
SetLength(sizeof(*this) - sizeof(Tlv));
|
||||
|
||||
SetFlagsIndex((aIndex & kIndexMask) | (aIsLast ? kIsLastFlag : 0));
|
||||
}
|
||||
|
||||
void MleCountersTlv::Init(const Mle::Counters &aMleCounters)
|
||||
{
|
||||
SetType(kMleCounters);
|
||||
SetLength(sizeof(*this) - sizeof(Tlv));
|
||||
|
||||
mDisabledRole = HostSwap16(aMleCounters.mDisabledRole);
|
||||
mDetachedRole = HostSwap16(aMleCounters.mDetachedRole);
|
||||
mChildRole = HostSwap16(aMleCounters.mChildRole);
|
||||
mRouterRole = HostSwap16(aMleCounters.mRouterRole);
|
||||
mLeaderRole = HostSwap16(aMleCounters.mLeaderRole);
|
||||
mAttachAttempts = HostSwap16(aMleCounters.mAttachAttempts);
|
||||
mPartitionIdChanges = HostSwap16(aMleCounters.mPartitionIdChanges);
|
||||
mBetterPartitionAttachAttempts = HostSwap16(aMleCounters.mBetterPartitionAttachAttempts);
|
||||
mParentChanges = HostSwap16(aMleCounters.mParentChanges);
|
||||
mTrackedTime = HostSwap64(aMleCounters.mTrackedTime);
|
||||
mDisabledTime = HostSwap64(aMleCounters.mDisabledTime);
|
||||
mDetachedTime = HostSwap64(aMleCounters.mDetachedTime);
|
||||
mChildTime = HostSwap64(aMleCounters.mChildTime);
|
||||
mRouterTime = HostSwap64(aMleCounters.mRouterTime);
|
||||
mLeaderTime = HostSwap64(aMleCounters.mLeaderTime);
|
||||
}
|
||||
|
||||
void MleCountersTlv::Read(MleCounters &aDiagMleCounters) const
|
||||
{
|
||||
aDiagMleCounters.mDisabledRole = HostSwap16(mDisabledRole);
|
||||
aDiagMleCounters.mDetachedRole = HostSwap16(mDetachedRole);
|
||||
aDiagMleCounters.mChildRole = HostSwap16(mChildRole);
|
||||
aDiagMleCounters.mRouterRole = HostSwap16(mRouterRole);
|
||||
aDiagMleCounters.mLeaderRole = HostSwap16(mLeaderRole);
|
||||
aDiagMleCounters.mAttachAttempts = HostSwap16(mAttachAttempts);
|
||||
aDiagMleCounters.mPartitionIdChanges = HostSwap16(mPartitionIdChanges);
|
||||
aDiagMleCounters.mBetterPartitionAttachAttempts = HostSwap16(mBetterPartitionAttachAttempts);
|
||||
aDiagMleCounters.mParentChanges = HostSwap16(mParentChanges);
|
||||
aDiagMleCounters.mTrackedTime = HostSwap64(mTrackedTime);
|
||||
aDiagMleCounters.mDisabledTime = HostSwap64(mDisabledTime);
|
||||
aDiagMleCounters.mDetachedTime = HostSwap64(mDetachedTime);
|
||||
aDiagMleCounters.mChildTime = HostSwap64(mChildTime);
|
||||
aDiagMleCounters.mRouterTime = HostSwap64(mRouterTime);
|
||||
aDiagMleCounters.mLeaderTime = HostSwap64(mLeaderTime);
|
||||
}
|
||||
|
||||
} // namespace NetworkDiagnostic
|
||||
} // namespace ot
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file includes definitions for generating and processing MLE TLVs.
|
||||
* This file includes definitions for generating and processing Network Diagnostics TLVs.
|
||||
*/
|
||||
|
||||
#ifndef NETWORK_DIAGNOSTIC_TLVS_HPP_
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "thread/link_quality.hpp"
|
||||
#include "thread/mle_tlvs.hpp"
|
||||
#include "thread/mle_types.hpp"
|
||||
#include "thread/topology.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace NetworkDiagnostic {
|
||||
@@ -69,27 +70,33 @@ public:
|
||||
*/
|
||||
enum Type : uint8_t
|
||||
{
|
||||
kExtMacAddress = OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS,
|
||||
kAddress16 = OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS,
|
||||
kMode = OT_NETWORK_DIAGNOSTIC_TLV_MODE,
|
||||
kTimeout = OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT,
|
||||
kConnectivity = OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY,
|
||||
kRoute = OT_NETWORK_DIAGNOSTIC_TLV_ROUTE,
|
||||
kLeaderData = OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA,
|
||||
kNetworkData = OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA,
|
||||
kIp6AddressList = OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST,
|
||||
kMacCounters = OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS,
|
||||
kBatteryLevel = OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL,
|
||||
kSupplyVoltage = OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE,
|
||||
kChildTable = OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE,
|
||||
kChannelPages = OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES,
|
||||
kTypeList = OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST,
|
||||
kMaxChildTimeout = OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT,
|
||||
kVersion = OT_NETWORK_DIAGNOSTIC_TLV_VERSION,
|
||||
kVendorName = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME,
|
||||
kVendorModel = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL,
|
||||
kVendorSwVersion = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION,
|
||||
kThreadStackVersion = OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION,
|
||||
kExtMacAddress = OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS,
|
||||
kAddress16 = OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS,
|
||||
kMode = OT_NETWORK_DIAGNOSTIC_TLV_MODE,
|
||||
kTimeout = OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT,
|
||||
kConnectivity = OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY,
|
||||
kRoute = OT_NETWORK_DIAGNOSTIC_TLV_ROUTE,
|
||||
kLeaderData = OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA,
|
||||
kNetworkData = OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA,
|
||||
kIp6AddressList = OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST,
|
||||
kMacCounters = OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS,
|
||||
kBatteryLevel = OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL,
|
||||
kSupplyVoltage = OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE,
|
||||
kChildTable = OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE,
|
||||
kChannelPages = OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES,
|
||||
kTypeList = OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST,
|
||||
kMaxChildTimeout = OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT,
|
||||
kVersion = OT_NETWORK_DIAGNOSTIC_TLV_VERSION,
|
||||
kVendorName = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME,
|
||||
kVendorModel = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL,
|
||||
kVendorSwVersion = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION,
|
||||
kThreadStackVersion = OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION,
|
||||
kChild = OT_NETWORK_DIAGNOSTIC_TLV_CHILD,
|
||||
kChildIp6AddressList = OT_NETWORK_DIAGNOSTIC_TLV_CHILD_IP6_ADDR_LIST,
|
||||
kRouterNeighbor = OT_NETWORK_DIAGNOSTIC_TLV_ROUTER_NEIGHBOR,
|
||||
kAnswer = OT_NETWORK_DIAGNOSTIC_TLV_ANSWER,
|
||||
kQueryId = OT_NETWORK_DIAGNOSTIC_TLV_QUERY_ID,
|
||||
kMleCounters = OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -224,6 +231,18 @@ typedef StringTlvInfo<Tlv::kVendorSwVersion, Tlv::kMaxVendorSwVersionLength> Ven
|
||||
*/
|
||||
typedef StringTlvInfo<Tlv::kThreadStackVersion, Tlv::kMaxThreadStackVersionLength> ThreadStackVersionTlv;
|
||||
|
||||
/**
|
||||
* Defines Child IPv6 Address List TLV constants and types.
|
||||
*
|
||||
*/
|
||||
typedef TlvInfo<Tlv::kChildIp6AddressList> ChildIp6AddressListTlv;
|
||||
|
||||
/**
|
||||
* Defines Query ID TLV constants and types.
|
||||
*
|
||||
*/
|
||||
typedef UintTlvInfo<Tlv::kQueryId, uint16_t> QueryIdTlv;
|
||||
|
||||
typedef otNetworkDiagConnectivity Connectivity; ///< Network Diagnostic Connectivity value.
|
||||
|
||||
/**
|
||||
@@ -667,6 +686,465 @@ public:
|
||||
}
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
|
||||
/**
|
||||
* Implements Child TLV generation and parsing.
|
||||
*
|
||||
*/
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class ChildTlv : public Tlv, public TlvInfo<Tlv::kChild>, public Clearable<ChildTlv>
|
||||
{
|
||||
public:
|
||||
static constexpr uint8_t kFlagsRxOnWhenIdle = 1 << 7; ///< Device mode - Rx-on when idle.
|
||||
static constexpr uint8_t kFlagsFtd = 1 << 6; ///< Device mode - Full Thread Device (FTD).
|
||||
static constexpr uint8_t kFlagsFullNetdta = 1 << 5; ///< Device mode - Full Network Data.
|
||||
static constexpr uint8_t kFlagsCslSync = 1 << 4; ///< Is CSL capable and CSL synchronized.
|
||||
static constexpr uint8_t kFlagsTrackErrRate = 1 << 3; ///< Supports tracking error rates.
|
||||
|
||||
/**
|
||||
* Initializes the TLV using information from a given `Child`.
|
||||
*
|
||||
* @param[in] aChild The child to initialize the TLV from.
|
||||
*
|
||||
*/
|
||||
void InitFrom(const Child &aChild);
|
||||
|
||||
/**
|
||||
* Initializes the TLV as empty (zero length).
|
||||
*
|
||||
*/
|
||||
void InitAsEmpty(void)
|
||||
{
|
||||
SetType(kChild);
|
||||
SetLength(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Flags field (`kFlags*` constants define bits in flags).
|
||||
*
|
||||
* @returns The Flags field.
|
||||
*
|
||||
*/
|
||||
uint8_t GetFlags(void) const { return mFlags; }
|
||||
|
||||
/**
|
||||
* Returns the RLOC16 field.
|
||||
*
|
||||
* @returns The RLOC16 of the child.
|
||||
*
|
||||
*/
|
||||
uint16_t GetRloc16(void) const { return HostSwap16(mRloc16); }
|
||||
|
||||
/**
|
||||
* Returns the Extended Address.
|
||||
*
|
||||
* @returns The Extended Address of the child.
|
||||
*
|
||||
*/
|
||||
const Mac::ExtAddress &GetExtAddress(void) const { return mExtAddress; }
|
||||
|
||||
/**
|
||||
* Returns the Version field.
|
||||
*
|
||||
* @returns The Version of the child.
|
||||
*
|
||||
*/
|
||||
uint16_t GetVersion(void) const { return HostSwap16(mVersion); }
|
||||
|
||||
/**
|
||||
* Returns the Timeout field
|
||||
*
|
||||
* @returns The Timeout value in seconds.
|
||||
*
|
||||
*/
|
||||
uint32_t GetTimeout(void) const { return HostSwap32(mTimeout); }
|
||||
|
||||
/**
|
||||
* Returns the Age field.
|
||||
*
|
||||
* @returns The Age field (seconds since last heard from the child).
|
||||
*
|
||||
*/
|
||||
uint32_t GetAge(void) const { return HostSwap32(mAge); }
|
||||
|
||||
/**
|
||||
* Returns the Connection Time field.
|
||||
*
|
||||
* @returns The Connection Time field (seconds since attach).
|
||||
*
|
||||
*/
|
||||
uint32_t GetConnectionTime(void) const { return HostSwap32(mConnectionTime); }
|
||||
|
||||
/**
|
||||
* Returns the Supervision Interval field
|
||||
*
|
||||
* @returns The Supervision Interval in seconds. Zero indicates not used.
|
||||
*
|
||||
*/
|
||||
uint16_t GetSupervisionInterval(void) const { return HostSwap16(mSupervisionInterval); }
|
||||
|
||||
/**
|
||||
* Returns the Link Margin field.
|
||||
*
|
||||
* @returns The Link Margin in dB.
|
||||
*
|
||||
*/
|
||||
uint8_t GetLinkMargin(void) const { return mLinkMargin; }
|
||||
|
||||
/**
|
||||
* Returns the Average RSSI field.
|
||||
*
|
||||
* @returns The Average RSSI in dBm. 127 if not available or unknown.
|
||||
*
|
||||
*/
|
||||
int8_t GetAverageRssi(void) const { return mAverageRssi; }
|
||||
|
||||
/**
|
||||
* Returns the Last RSSI field (RSSI of last received frame from child).
|
||||
*
|
||||
* @returns The Last RSSI field in dBm. 127 if not available or unknown.
|
||||
*
|
||||
*/
|
||||
int8_t GetLastRssi(void) const { return mLastRssi; }
|
||||
|
||||
/**
|
||||
* Returns the Frame Error Rate field.
|
||||
*
|
||||
* `kFlagsTrackErrRate` from `GetFlags()` indicates whether or not the implementation supports tracking of error
|
||||
* rates and whether or not the value in this field is valid.
|
||||
*
|
||||
* @returns The Frame Error Rate (0x0000->0%, 0xffff->100%).
|
||||
*
|
||||
*/
|
||||
uint16_t GetFrameErrorRate(void) const { return HostSwap16(mFrameErrorRate); }
|
||||
|
||||
/**
|
||||
* Returns the Message Error Rate field.
|
||||
*
|
||||
* `kFlagsTrackErrRate` from `GetFlags()` indicates whether or not the implementation supports tracking of error
|
||||
* rates and whether or not the value in this field is valid.
|
||||
*
|
||||
* @returns The Message Error Rate (0x0000->0%, 0xffff->100%).
|
||||
*
|
||||
*/
|
||||
uint16_t GetMessageErrorRate(void) const { return HostSwap16(mMessageErrorRate); }
|
||||
|
||||
/**
|
||||
* Returns the Queued Message Count field.
|
||||
*
|
||||
* @returns The Queued Message Count (number of queued messages for indirect tx to child).
|
||||
*
|
||||
*/
|
||||
uint16_t GetQueuedMessageCount(void) const { return HostSwap16(mQueuedMessageCount); }
|
||||
|
||||
/**
|
||||
* Returns the CSL Period in unit of 10 symbols.
|
||||
*
|
||||
* @returns The CSL Period in unit of 10-symbols-time. Zero if CSL is not supported.
|
||||
*
|
||||
*/
|
||||
uint16_t GetCslPeriod(void) const { return HostSwap16(mCslPeriod); }
|
||||
|
||||
/**
|
||||
* Returns the CSL Timeout in seconds.
|
||||
*
|
||||
* @returns The CSL Timeout in seconds. Zero if unknown on parent of if CSL Is not supported.
|
||||
*
|
||||
*/
|
||||
uint32_t GetCslTimeout(void) const { return HostSwap32(mCslTimeout); }
|
||||
|
||||
/**
|
||||
* Returns the CSL Channel.
|
||||
*
|
||||
* @returns The CSL channel.
|
||||
*
|
||||
*/
|
||||
uint8_t GetCslChannel(void) const { return mCslChannel; }
|
||||
|
||||
private:
|
||||
uint8_t mFlags; // Flags (`kFlags*` constants).
|
||||
uint16_t mRloc16; // RLOC16.
|
||||
Mac::ExtAddress mExtAddress; // Extended Address.
|
||||
uint16_t mVersion; // Version.
|
||||
uint32_t mTimeout; // Timeout in seconds.
|
||||
uint32_t mAge; // Seconds since last heard from the child.
|
||||
uint32_t mConnectionTime; // Seconds since attach.
|
||||
uint16_t mSupervisionInterval; // Supervision interval in seconds. Zero to indicate not used.
|
||||
uint8_t mLinkMargin; // Link margin in dB.
|
||||
int8_t mAverageRssi; // Average RSSI. 127 if not available or unknown
|
||||
int8_t mLastRssi; // RSSI of last received frame. 127 if not available or unknown.
|
||||
uint16_t mFrameErrorRate; // Frame error rate (0x0000->0%, 0xffff->100%).
|
||||
uint16_t mMessageErrorRate; // (IPv6) msg error rate (0x0000->0%, 0xffff->100%)
|
||||
uint16_t mQueuedMessageCount; // Number of queued messages for indirect tx to child.
|
||||
uint16_t mCslPeriod; // CSL Period in unit of 10 symbols.
|
||||
uint32_t mCslTimeout; // CSL Timeout in seconds.
|
||||
uint8_t mCslChannel; // CSL channel.
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
/**
|
||||
* Implements Child IPv6 Address List Value generation and parsing.
|
||||
*
|
||||
* This TLV can use extended or normal format depending on the number of IPv6 addresses.
|
||||
*
|
||||
*/
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class ChildIp6AddressListTlvValue
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns the RLOC16 of the child.
|
||||
*
|
||||
* @returns The RLOC16 of the child.
|
||||
*
|
||||
*/
|
||||
uint16_t GetRloc16(void) const { return HostSwap16(mRloc16); }
|
||||
|
||||
/**
|
||||
* Sets the RLOC16.
|
||||
*
|
||||
* @param[in] aRloc16 The RLOC16 value.
|
||||
*
|
||||
*/
|
||||
void SetRloc16(uint16_t aRloc16) { mRloc16 = HostSwap16(aRloc16); }
|
||||
|
||||
private:
|
||||
uint16_t mRloc16;
|
||||
// Followed by zero or more IPv6 address(es).
|
||||
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
/**
|
||||
* Implements Router Neighbor TLV generation and parsing.
|
||||
*
|
||||
*/
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class RouterNeighborTlv : public Tlv, public TlvInfo<Tlv::kRouterNeighbor>, public Clearable<RouterNeighborTlv>
|
||||
{
|
||||
public:
|
||||
static constexpr uint8_t kFlagsTrackErrRate = 1 << 7; ///< Supports tracking error rates.
|
||||
|
||||
/**
|
||||
* Initializes the TLV using information from a given `Router`.
|
||||
*
|
||||
* @param[in] aRouter The router to initialize the TLV from.
|
||||
*
|
||||
*/
|
||||
void InitFrom(const Router &aRouter);
|
||||
|
||||
/**
|
||||
* Initializes the TLV as empty (zero length).
|
||||
*
|
||||
*/
|
||||
void InitAsEmpty(void)
|
||||
{
|
||||
SetType(kRouterNeighbor);
|
||||
SetLength(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Flags field (`kFlags*` constants define bits in flags).
|
||||
*
|
||||
* @returns The Flags field.
|
||||
*
|
||||
*/
|
||||
uint8_t GetFlags(void) const { return mFlags; }
|
||||
|
||||
/**
|
||||
* Returns the RLOC16 field.
|
||||
*
|
||||
* @returns The RLOC16 of the router.
|
||||
*
|
||||
*/
|
||||
uint16_t GetRloc16(void) const { return HostSwap16(mRloc16); }
|
||||
|
||||
/**
|
||||
* Returns the Extended Address.
|
||||
*
|
||||
* @returns The Extended Address of the router.
|
||||
*
|
||||
*/
|
||||
const Mac::ExtAddress &GetExtAddress(void) const { return mExtAddress; }
|
||||
|
||||
/**
|
||||
* Returns the Version field.
|
||||
*
|
||||
* @returns The Version of the router.
|
||||
*
|
||||
*/
|
||||
uint16_t GetVersion(void) const { return HostSwap16(mVersion); }
|
||||
|
||||
/**
|
||||
* Returns the Connection Time field.
|
||||
*
|
||||
* @returns The Connection Time field (seconds since link establishment).
|
||||
*
|
||||
*/
|
||||
uint32_t GetConnectionTime(void) const { return HostSwap32(mConnectionTime); }
|
||||
|
||||
/**
|
||||
* Returns the Link Margin field.
|
||||
*
|
||||
* @returns The Link Margin in dB.
|
||||
*
|
||||
*/
|
||||
uint8_t GetLinkMargin(void) const { return mLinkMargin; }
|
||||
|
||||
/**
|
||||
* Returns the Average RSSI field.
|
||||
*
|
||||
* @returns The Average RSSI in dBm. 127 if not available or unknown.
|
||||
*
|
||||
*/
|
||||
int8_t GetAverageRssi(void) const { return mAverageRssi; }
|
||||
|
||||
/**
|
||||
* Returns the Last RSSI field (RSSI of last received frame from router).
|
||||
*
|
||||
* @returns The Last RSSI field in dBm. 127 if not available or unknown.
|
||||
*
|
||||
*/
|
||||
int8_t GetLastRssi(void) const { return mLastRssi; }
|
||||
|
||||
/**
|
||||
* Returns the Frame Error Rate field.
|
||||
*
|
||||
* `kFlagsTrackErrRate` from `GetFlags()` indicates whether or not the implementation supports tracking of error
|
||||
* rates and whether or not the value in this field is valid.
|
||||
*
|
||||
* @returns The Frame Error Rate (0x0000->0%, 0xffff->100%).
|
||||
*
|
||||
*/
|
||||
uint16_t GetFrameErrorRate(void) const { return HostSwap16(mFrameErrorRate); }
|
||||
|
||||
/**
|
||||
* Returns the Message Error Rate field.
|
||||
*
|
||||
* `kFlagsTrackErrRate` from `GetFlags()` indicates whether or not the implementation supports tracking of error
|
||||
* rates and whether or not the value in this field is valid.
|
||||
*
|
||||
* @returns The Message Error Rate (0x0000->0%, 0xffff->100%).
|
||||
*
|
||||
*/
|
||||
uint16_t GetMessageErrorRate(void) const { return HostSwap16(mMessageErrorRate); }
|
||||
|
||||
private:
|
||||
uint8_t mFlags; // Flags (`kFlags*` constants).
|
||||
uint16_t mRloc16; // RLOC16.
|
||||
Mac::ExtAddress mExtAddress; // Extended Address.
|
||||
uint16_t mVersion; // Version.
|
||||
uint32_t mConnectionTime; // Seconds since link establishment.
|
||||
uint8_t mLinkMargin; // Link Margin.
|
||||
int8_t mAverageRssi; // Average RSSI. 127 if not available or unknown
|
||||
int8_t mLastRssi; // RSSI of last received frame. 127 if not available or unknown.
|
||||
uint16_t mFrameErrorRate; // Frame error rate (0x0000->0%, 0xffff->100%).
|
||||
uint16_t mMessageErrorRate; // (IPv6) msg error rate (0x0000->0%, 0xffff->100%)
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
#endif // OPENTHREAD_FTD
|
||||
|
||||
/**
|
||||
* Implements Answer TLV generation and parsing.
|
||||
*
|
||||
*/
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class AnswerTlv : public Tlv, public TlvInfo<Tlv::kAnswer>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes the TLV.
|
||||
*
|
||||
* @param[in] aIndex The index value.
|
||||
* @param[in] aIsLast The "IsLast" flag value.
|
||||
*
|
||||
*/
|
||||
void Init(uint16_t aIndex, bool aIsLast);
|
||||
|
||||
/**
|
||||
* Indicates whether or not the "IsLast" flag is set
|
||||
*
|
||||
* @retval TRUE "IsLast" flag si set (this is the last answer for this query).
|
||||
* @retval FALSE "IsLast" flag is not set (more answer messages are expected for this query).
|
||||
*
|
||||
*/
|
||||
bool IsLast(void) const { return GetFlagsIndex() & kIsLastFlag; }
|
||||
|
||||
/**
|
||||
* Gets the index.
|
||||
*
|
||||
* @returns The index.
|
||||
*
|
||||
*/
|
||||
uint16_t GetIndex(void) const { return GetFlagsIndex() & kIndexMask; }
|
||||
|
||||
private:
|
||||
static constexpr uint16_t kIsLastFlag = 1 << 15;
|
||||
static constexpr uint16_t kIndexMask = 0x7f;
|
||||
|
||||
uint16_t GetFlagsIndex(void) const { return HostSwap16(mFlagsIndex); }
|
||||
void SetFlagsIndex(uint16_t aFlagsIndex) { mFlagsIndex = HostSwap16(aFlagsIndex); }
|
||||
|
||||
uint16_t mFlagsIndex;
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
/**
|
||||
* Represents the MLE Counters.
|
||||
*
|
||||
*/
|
||||
typedef otNetworkDiagMleCounters MleCounters;
|
||||
|
||||
/**
|
||||
* Implements MLE Counters TLV generation and parsing.
|
||||
*
|
||||
*/
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class MleCountersTlv : public Tlv, public TlvInfo<Tlv::kMleCounters>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes the TLV.
|
||||
*
|
||||
* @param[in] aMleCounter The MLE counters to initialize the TLV with.
|
||||
*
|
||||
*/
|
||||
void Init(const Mle::Counters &aMleCounters);
|
||||
|
||||
/**
|
||||
* Indicates whether or not the TLV appears to be well-formed.
|
||||
*
|
||||
* @retval TRUE If the TLV appears to be well-formed.
|
||||
* @retval FALSE If the TLV does not appear to be well-formed.
|
||||
*
|
||||
*/
|
||||
bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(Tlv); }
|
||||
|
||||
/**
|
||||
*
|
||||
* Reads the counters from TLV.
|
||||
*
|
||||
* @param[out] aDiagMleCounters A reference to `NetworkDiagnostic::MleCounters` to populate.
|
||||
*
|
||||
*/
|
||||
void Read(MleCounters &aDiagMleCounters) const;
|
||||
|
||||
private:
|
||||
uint16_t mDisabledRole; // Number of times device entered disabled role.
|
||||
uint16_t mDetachedRole; // Number of times device entered detached role.
|
||||
uint16_t mChildRole; // Number of times device entered child role.
|
||||
uint16_t mRouterRole; // Number of times device entered router role.
|
||||
uint16_t mLeaderRole; // Number of times device entered leader role.
|
||||
uint16_t mAttachAttempts; // Number of attach attempts while device was detached.
|
||||
uint16_t mPartitionIdChanges; // Number of changes to partition ID.
|
||||
uint16_t mBetterPartitionAttachAttempts; // Number of attempts to attach to a better partition.
|
||||
uint16_t mParentChanges; // Number of time device changed its parent.
|
||||
uint64_t mTrackedTime; // Milliseconds tracked by next counters.
|
||||
uint64_t mDisabledTime; // Milliseconds device has been in disabled role.
|
||||
uint64_t mDetachedTime; // Milliseconds device has been in detached role.
|
||||
uint64_t mChildTime; // Milliseconds device has been in child role.
|
||||
uint64_t mRouterTime; // Milliseconds device has been in router role.
|
||||
uint64_t mLeaderTime; // Milliseconds device has been in leader role.
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
} // namespace NetworkDiagnostic
|
||||
} // namespace ot
|
||||
|
||||
|
||||
+390
-53
@@ -45,7 +45,7 @@
|
||||
namespace ot {
|
||||
namespace Utils {
|
||||
|
||||
using namespace ot::NetworkDiagnostic;
|
||||
using namespace NetworkDiagnostic;
|
||||
|
||||
RegisterLogModule("MeshDiag");
|
||||
|
||||
@@ -54,54 +54,23 @@ RegisterLogModule("MeshDiag");
|
||||
|
||||
MeshDiag::MeshDiag(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mState(kStateIdle)
|
||||
, mExpectedQueryId(0)
|
||||
, mExpectedAnswerIndex(0)
|
||||
, mTimer(aInstance)
|
||||
{
|
||||
}
|
||||
|
||||
Error MeshDiag::DiscoverTopology(const DiscoverConfig &aConfig, DiscoverCallback aCallback, void *aContext)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
|
||||
VerifyOrExit(Get<Mle::Mle>().IsAttached(), error = kErrorInvalidState);
|
||||
VerifyOrExit(!mTimer.IsRunning(), error = kErrorBusy);
|
||||
|
||||
Get<RouterTable>().GetRouterIdSet(mExpectedRouterIdSet);
|
||||
|
||||
for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
|
||||
{
|
||||
if (mExpectedRouterIdSet.Contains(routerId))
|
||||
{
|
||||
SuccessOrExit(error = SendDiagGetTo(Mle::Rloc16FromRouterId(routerId), aConfig));
|
||||
}
|
||||
}
|
||||
|
||||
mDiscoverCallback.Set(aCallback, aContext);
|
||||
mTimer.Start(kResponseTimeout);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void MeshDiag::Cancel(void)
|
||||
{
|
||||
mTimer.Stop();
|
||||
IgnoreError(Get<Tmf::Agent>().AbortTransaction(HandleDiagGetResponse, this));
|
||||
}
|
||||
|
||||
Error MeshDiag::SendDiagGetTo(uint16_t aRloc16, const DiscoverConfig &aConfig)
|
||||
{
|
||||
static constexpr uint8_t kMaxTlvsToRequest = 6;
|
||||
|
||||
Error error = kErrorNone;
|
||||
Coap::Message *message = nullptr;
|
||||
Tmf::MessageInfo messageInfo(GetInstance());
|
||||
uint8_t tlvs[kMaxTlvsToRequest];
|
||||
uint8_t tlvsLength = 0;
|
||||
Error error = kErrorNone;
|
||||
uint8_t tlvs[kMaxTlvsToRequest];
|
||||
uint8_t tlvsLength = 0;
|
||||
|
||||
message = Get<Tmf::Agent>().NewConfirmablePostMessage(kUriDiagnosticGetRequest);
|
||||
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
|
||||
|
||||
IgnoreError(message->SetPriority(Message::kPriorityLow));
|
||||
VerifyOrExit(Get<Mle::Mle>().IsAttached(), error = kErrorInvalidState);
|
||||
VerifyOrExit(mState == kStateIdle, error = kErrorBusy);
|
||||
|
||||
tlvs[tlvsLength++] = Address16Tlv::kType;
|
||||
tlvs[tlvsLength++] = ExtMacAddressTlv::kType;
|
||||
@@ -118,13 +87,29 @@ Error MeshDiag::SendDiagGetTo(uint16_t aRloc16, const DiscoverConfig &aConfig)
|
||||
tlvs[tlvsLength++] = ChildTableTlv::kType;
|
||||
}
|
||||
|
||||
SuccessOrExit(error = Tlv::Append<TypeListTlv>(*message, tlvs, tlvsLength));
|
||||
Get<RouterTable>().GetRouterIdSet(mDiscover.mExpectedRouterIdSet);
|
||||
|
||||
messageInfo.SetSockAddrToRlocPeerAddrTo(aRloc16);
|
||||
error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, HandleDiagGetResponse, this);
|
||||
for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
|
||||
{
|
||||
Ip6::Address destination;
|
||||
|
||||
if (!mDiscover.mExpectedRouterIdSet.Contains(routerId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
destination = Get<Mle::MleRouter>().GetMeshLocal16();
|
||||
destination.GetIid().SetLocator(Mle::Rloc16FromRouterId(routerId));
|
||||
|
||||
SuccessOrExit(error = Get<Client>().SendCommand(kUriDiagnosticGetRequest, Message::kPriorityLow, destination,
|
||||
tlvs, tlvsLength, HandleDiagGetResponse, this));
|
||||
}
|
||||
|
||||
mDiscover.mCallback.Set(aCallback, aContext);
|
||||
mState = kStateDicoverTopology;
|
||||
mTimer.Start(kResponseTimeout);
|
||||
|
||||
exit:
|
||||
FreeMessageOnError(message, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -147,7 +132,8 @@ void MeshDiag::HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::Message
|
||||
ChildIterator childIterator;
|
||||
|
||||
SuccessOrExit(aResult);
|
||||
VerifyOrExit((aMessage != nullptr) && mTimer.IsRunning());
|
||||
VerifyOrExit(aMessage != nullptr);
|
||||
VerifyOrExit(mState == kStateDicoverTopology);
|
||||
|
||||
SuccessOrExit(routerInfo.ParseFrom(*aMessage));
|
||||
|
||||
@@ -161,11 +147,12 @@ void MeshDiag::HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::Message
|
||||
routerInfo.mChildIterator = &childIterator;
|
||||
}
|
||||
|
||||
mExpectedRouterIdSet.Remove(routerInfo.mRouterId);
|
||||
mDiscover.mExpectedRouterIdSet.Remove(routerInfo.mRouterId);
|
||||
|
||||
if (mExpectedRouterIdSet.GetNumberOfAllocatedIds() == 0)
|
||||
if (mDiscover.mExpectedRouterIdSet.GetNumberOfAllocatedIds() == 0)
|
||||
{
|
||||
error = kErrorNone;
|
||||
error = kErrorNone;
|
||||
mState = kStateIdle;
|
||||
mTimer.Stop();
|
||||
}
|
||||
else
|
||||
@@ -173,21 +160,326 @@ void MeshDiag::HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::Message
|
||||
error = kErrorPending;
|
||||
}
|
||||
|
||||
mDiscoverCallback.InvokeIfSet(error, &routerInfo);
|
||||
mDiscover.mCallback.InvokeIfSet(error, &routerInfo);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void MeshDiag::HandleTimer(void)
|
||||
Error MeshDiag::SendQuery(uint16_t aRloc16, const uint8_t *aTlvs, uint8_t aTlvsLength)
|
||||
{
|
||||
// Timed out waiting for response from one or more routers.
|
||||
Error error = kErrorNone;
|
||||
Ip6::Address destination;
|
||||
|
||||
IgnoreError(Get<Tmf::Agent>().AbortTransaction(HandleDiagGetResponse, this));
|
||||
VerifyOrExit(Get<Mle::Mle>().IsAttached(), error = kErrorInvalidState);
|
||||
VerifyOrExit(mState == kStateIdle, error = kErrorBusy);
|
||||
VerifyOrExit(Mle::IsActiveRouter(aRloc16), error = kErrorInvalidArgs);
|
||||
VerifyOrExit(Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(aRloc16)), error = kErrorNotFound);
|
||||
|
||||
mDiscoverCallback.InvokeIfSet(kErrorResponseTimeout, nullptr);
|
||||
destination = Get<Mle::MleRouter>().GetMeshLocal16();
|
||||
destination.GetIid().SetLocator(aRloc16);
|
||||
|
||||
SuccessOrExit(error = Get<Client>().SendCommand(kUriDiagnosticGetQuery, Message::kPriorityNormal, destination,
|
||||
aTlvs, aTlvsLength));
|
||||
|
||||
mExpectedQueryId = Get<Client>().GetLastQueryId();
|
||||
mExpectedAnswerIndex = 0;
|
||||
|
||||
mTimer.Start(kResponseTimeout);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error MeshDiag::QueryChildTable(uint16_t aRloc16, QueryChildTableCallback aCallback, void *aContext)
|
||||
{
|
||||
static const uint8_t kTlvTypes[] = {ChildTlv::kType};
|
||||
|
||||
Error error;
|
||||
|
||||
SuccessOrExit(error = SendQuery(aRloc16, kTlvTypes, sizeof(kTlvTypes)));
|
||||
|
||||
mQueryChildTable.mCallback.Set(aCallback, aContext);
|
||||
mQueryChildTable.mRouterRloc16 = aRloc16;
|
||||
mState = kStateQueryChildTable;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error MeshDiag::QueryChildrenIp6Addrs(uint16_t aRloc16, ChildIp6AddrsCallback aCallback, void *aContext)
|
||||
{
|
||||
static const uint8_t kTlvTypes[] = {ChildIp6AddressListTlv::kType};
|
||||
|
||||
Error error;
|
||||
|
||||
SuccessOrExit(error = SendQuery(aRloc16, kTlvTypes, sizeof(kTlvTypes)));
|
||||
|
||||
mQueryChildrenIp6Addrs.mCallback.Set(aCallback, aContext);
|
||||
mQueryChildrenIp6Addrs.mParentRloc16 = aRloc16;
|
||||
mState = kStateQueryChildrenIp6Addrs;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error MeshDiag::QueryRouterNeighborTable(uint16_t aRloc16, RouterNeighborTableCallback aCallback, void *aContext)
|
||||
{
|
||||
static const uint8_t kTlvTypes[] = {RouterNeighborTlv::kType};
|
||||
|
||||
Error error;
|
||||
|
||||
SuccessOrExit(error = SendQuery(aRloc16, kTlvTypes, sizeof(kTlvTypes)));
|
||||
|
||||
mQueryRouterNeighborTable.mCallback.Set(aCallback, aContext);
|
||||
mQueryRouterNeighborTable.mRouterRloc16 = aRloc16;
|
||||
mState = kStateQueryRouterNeighborTable;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
bool MeshDiag::HandleDiagnosticGetAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
bool didPorcess = false;
|
||||
|
||||
switch (mState)
|
||||
{
|
||||
case kStateQueryChildTable:
|
||||
didPorcess = ProcessChildTableAnswer(aMessage, aMessageInfo);
|
||||
break;
|
||||
|
||||
case kStateQueryChildrenIp6Addrs:
|
||||
didPorcess = ProcessChildrenIp6AddrsAnswer(aMessage, aMessageInfo);
|
||||
break;
|
||||
|
||||
case kStateQueryRouterNeighborTable:
|
||||
didPorcess = ProcessRouterNeighborTableAnswer(aMessage, aMessageInfo);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return didPorcess;
|
||||
}
|
||||
|
||||
Error MeshDiag::ProcessMessage(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint16_t aSenderRloc16)
|
||||
{
|
||||
// This method processes the received answer message to
|
||||
// check whether it is from the intended sender and matches
|
||||
// the expected query ID and answer index.
|
||||
|
||||
Error error = kErrorFailed;
|
||||
AnswerTlv answerTlv;
|
||||
uint16_t queryId;
|
||||
|
||||
VerifyOrExit(Get<Mle::Mle>().IsRoutingLocator(aMessageInfo.GetPeerAddr()));
|
||||
VerifyOrExit(aMessageInfo.GetPeerAddr().GetIid().GetLocator() == aSenderRloc16);
|
||||
|
||||
SuccessOrExit(Tlv::Find<QueryIdTlv>(aMessage, queryId));
|
||||
VerifyOrExit(queryId == mExpectedQueryId);
|
||||
|
||||
SuccessOrExit(Tlv::FindTlv(aMessage, answerTlv));
|
||||
|
||||
if (answerTlv.GetIndex() != mExpectedAnswerIndex)
|
||||
{
|
||||
Finalize(kErrorResponseTimeout);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
mExpectedAnswerIndex++;
|
||||
error = kErrorNone;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
bool MeshDiag::ProcessChildTableAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
bool didPorcess = false;
|
||||
ChildTlv childTlv;
|
||||
ChildEntry entry;
|
||||
uint16_t offset;
|
||||
|
||||
SuccessOrExit(ProcessMessage(aMessage, aMessageInfo, mQueryChildTable.mRouterRloc16));
|
||||
|
||||
while (true)
|
||||
{
|
||||
SuccessOrExit(Tlv::FindTlv(aMessage, childTlv, offset));
|
||||
VerifyOrExit(!childTlv.IsExtended());
|
||||
|
||||
didPorcess = true;
|
||||
|
||||
if (childTlv.GetLength() == 0)
|
||||
{
|
||||
// We reached end of the list.
|
||||
mState = kStateIdle;
|
||||
mTimer.Stop();
|
||||
mQueryChildTable.mCallback.InvokeIfSet(kErrorNone, nullptr);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
VerifyOrExit(childTlv.GetLength() >= sizeof(ChildTlv) - sizeof(Tlv));
|
||||
IgnoreError(aMessage.Read(offset, childTlv));
|
||||
|
||||
entry.SetFrom(childTlv);
|
||||
mQueryChildTable.mCallback.InvokeIfSet(kErrorPending, &entry);
|
||||
|
||||
// Make sure query operation is not canceled from the
|
||||
// callback.
|
||||
VerifyOrExit(mState == kStateQueryChildTable);
|
||||
|
||||
aMessage.SetOffset(static_cast<uint16_t>(offset + childTlv.GetSize()));
|
||||
}
|
||||
|
||||
exit:
|
||||
return didPorcess;
|
||||
}
|
||||
|
||||
bool MeshDiag::ProcessRouterNeighborTableAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
bool didPorcess = false;
|
||||
RouterNeighborTlv neighborTlv;
|
||||
RouterNeighborEntry entry;
|
||||
uint16_t offset;
|
||||
|
||||
SuccessOrExit(ProcessMessage(aMessage, aMessageInfo, mQueryRouterNeighborTable.mRouterRloc16));
|
||||
|
||||
while (true)
|
||||
{
|
||||
SuccessOrExit(Tlv::FindTlv(aMessage, neighborTlv, offset));
|
||||
VerifyOrExit(!neighborTlv.IsExtended());
|
||||
|
||||
didPorcess = true;
|
||||
|
||||
if (neighborTlv.GetLength() == 0)
|
||||
{
|
||||
// We reached end of the list.
|
||||
mState = kStateIdle;
|
||||
mTimer.Stop();
|
||||
mQueryRouterNeighborTable.mCallback.InvokeIfSet(kErrorNone, nullptr);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
VerifyOrExit(neighborTlv.GetLength() >= sizeof(RouterNeighborTlv) - sizeof(Tlv));
|
||||
|
||||
entry.SetFrom(neighborTlv);
|
||||
mQueryRouterNeighborTable.mCallback.InvokeIfSet(kErrorPending, &entry);
|
||||
|
||||
// Make sure query operation is not canceled from the
|
||||
// callback.
|
||||
VerifyOrExit(mState == kStateQueryRouterNeighborTable);
|
||||
|
||||
aMessage.SetOffset(static_cast<uint16_t>(offset + neighborTlv.GetSize()));
|
||||
}
|
||||
|
||||
exit:
|
||||
return didPorcess;
|
||||
}
|
||||
|
||||
bool MeshDiag::ProcessChildrenIp6AddrsAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
bool didPorcess = false;
|
||||
uint16_t offset;
|
||||
uint16_t endOffset;
|
||||
ChildIp6AddressListTlvValue tlvValue;
|
||||
Ip6AddrIterator ip6AddrIterator;
|
||||
|
||||
SuccessOrExit(ProcessMessage(aMessage, aMessageInfo, mQueryChildrenIp6Addrs.mParentRloc16));
|
||||
|
||||
while (true)
|
||||
{
|
||||
SuccessOrExit(Tlv::FindTlvValueStartEndOffsets(aMessage, ChildIp6AddressListTlv::kType, offset, endOffset));
|
||||
|
||||
didPorcess = true;
|
||||
|
||||
if (offset == endOffset)
|
||||
{
|
||||
// We reached end of the list
|
||||
mState = kStateIdle;
|
||||
mTimer.Stop();
|
||||
mQueryChildrenIp6Addrs.mCallback.InvokeIfSet(kErrorNone, Mle::kInvalidRloc16, nullptr);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
// Read the `ChildIp6AddressListTlvValue` (which contains the
|
||||
// child RLOC16) and then prepare the `Ip6AddrIterator`.
|
||||
|
||||
VerifyOrExit(offset + sizeof(tlvValue) <= endOffset);
|
||||
IgnoreError(aMessage.Read(offset, tlvValue));
|
||||
offset += sizeof(tlvValue);
|
||||
|
||||
ip6AddrIterator.mMessage = &aMessage;
|
||||
ip6AddrIterator.mCurOffset = offset;
|
||||
ip6AddrIterator.mEndOffset = endOffset;
|
||||
|
||||
mQueryChildrenIp6Addrs.mCallback.InvokeIfSet(kErrorPending, tlvValue.GetRloc16(), &ip6AddrIterator);
|
||||
|
||||
// Make sure query operation is not canceled from the
|
||||
// callback.
|
||||
VerifyOrExit(mState == kStateQueryChildrenIp6Addrs);
|
||||
|
||||
aMessage.SetOffset(endOffset);
|
||||
}
|
||||
|
||||
exit:
|
||||
return didPorcess;
|
||||
}
|
||||
|
||||
void MeshDiag::Cancel(void)
|
||||
{
|
||||
switch (mState)
|
||||
{
|
||||
case kStateIdle:
|
||||
case kStateQueryChildTable:
|
||||
case kStateQueryChildrenIp6Addrs:
|
||||
case kStateQueryRouterNeighborTable:
|
||||
break;
|
||||
|
||||
case kStateDicoverTopology:
|
||||
IgnoreError(Get<Tmf::Agent>().AbortTransaction(HandleDiagGetResponse, this));
|
||||
break;
|
||||
}
|
||||
|
||||
mState = kStateIdle;
|
||||
mTimer.Stop();
|
||||
}
|
||||
|
||||
void MeshDiag::Finalize(Error aError)
|
||||
{
|
||||
// Finalize an ongoing query operation (if any) invoking
|
||||
// the corresponding callback with `aError`.
|
||||
|
||||
State oldState = mState;
|
||||
|
||||
Cancel();
|
||||
|
||||
switch (oldState)
|
||||
{
|
||||
case kStateIdle:
|
||||
break;
|
||||
|
||||
case kStateDicoverTopology:
|
||||
mDiscover.mCallback.InvokeIfSet(aError, nullptr);
|
||||
break;
|
||||
|
||||
case kStateQueryChildTable:
|
||||
mQueryChildTable.mCallback.InvokeIfSet(aError, nullptr);
|
||||
break;
|
||||
|
||||
case kStateQueryChildrenIp6Addrs:
|
||||
mQueryChildrenIp6Addrs.mCallback.InvokeIfSet(aError, Mle::kInvalidRloc16, nullptr);
|
||||
break;
|
||||
|
||||
case kStateQueryRouterNeighborTable:
|
||||
mQueryRouterNeighborTable.mCallback.InvokeIfSet(aError, nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MeshDiag::HandleTimer(void) { Finalize(kErrorResponseTimeout); }
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// MeshDiag::RouterInfo
|
||||
|
||||
@@ -299,6 +591,51 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// MeshDiag::ChildEntry
|
||||
|
||||
void MeshDiag::ChildEntry::SetFrom(const ChildTlv &aChildTlv)
|
||||
{
|
||||
mRxOnWhenIdle = (aChildTlv.GetFlags() & ChildTlv::kFlagsRxOnWhenIdle);
|
||||
mDeviceTypeFtd = (aChildTlv.GetFlags() & ChildTlv::kFlagsFtd);
|
||||
mFullNetData = (aChildTlv.GetFlags() & ChildTlv::kFlagsFullNetdta);
|
||||
mCslSynchronized = (aChildTlv.GetFlags() & ChildTlv::kFlagsCslSync);
|
||||
mSupportsErrRate = (aChildTlv.GetFlags() & ChildTlv::kFlagsTrackErrRate);
|
||||
mRloc16 = aChildTlv.GetRloc16();
|
||||
mExtAddress = aChildTlv.GetExtAddress();
|
||||
mVersion = aChildTlv.GetVersion();
|
||||
mTimeout = aChildTlv.GetTimeout();
|
||||
mAge = aChildTlv.GetAge();
|
||||
mConnectionTime = aChildTlv.GetConnectionTime();
|
||||
mSupervisionInterval = aChildTlv.GetSupervisionInterval();
|
||||
mLinkMargin = aChildTlv.GetLinkMargin();
|
||||
mAverageRssi = aChildTlv.GetAverageRssi();
|
||||
mLastRssi = aChildTlv.GetLastRssi();
|
||||
mFrameErrorRate = aChildTlv.GetFrameErrorRate();
|
||||
mMessageErrorRate = aChildTlv.GetMessageErrorRate();
|
||||
mQueuedMessageCount = aChildTlv.GetQueuedMessageCount();
|
||||
mCslPeriod = aChildTlv.GetCslPeriod();
|
||||
mCslTimeout = aChildTlv.GetCslTimeout();
|
||||
mCslChannel = aChildTlv.GetCslChannel();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// MeshDiag::RouterNeighborEntry
|
||||
|
||||
void MeshDiag::RouterNeighborEntry::SetFrom(const RouterNeighborTlv &aTlv)
|
||||
{
|
||||
mSupportsErrRate = (aTlv.GetFlags() & RouterNeighborTlv::kFlagsTrackErrRate);
|
||||
mRloc16 = aTlv.GetRloc16();
|
||||
mExtAddress = aTlv.GetExtAddress();
|
||||
mVersion = aTlv.GetVersion();
|
||||
mConnectionTime = aTlv.GetConnectionTime();
|
||||
mLinkMargin = aTlv.GetLinkMargin();
|
||||
mAverageRssi = aTlv.GetAverageRssi();
|
||||
mLastRssi = aTlv.GetLastRssi();
|
||||
mFrameErrorRate = aTlv.GetFrameErrorRate();
|
||||
mMessageErrorRate = aTlv.GetMessageErrorRate();
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace ot
|
||||
|
||||
|
||||
+135
-11
@@ -38,6 +38,10 @@
|
||||
|
||||
#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
|
||||
|
||||
#if !OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
|
||||
#error "OPENTHREAD_CONFIG_MESH_DIAG_ENABLE requires OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE"
|
||||
#endif
|
||||
|
||||
#include <openthread/mesh_diag.h>
|
||||
|
||||
#include "coap/coap.hpp"
|
||||
@@ -46,6 +50,7 @@
|
||||
#include "common/message.hpp"
|
||||
#include "common/timer.hpp"
|
||||
#include "net/ip6_address.hpp"
|
||||
#include "thread/network_diagnostic.hpp"
|
||||
#include "thread/network_diagnostic_tlvs.hpp"
|
||||
|
||||
struct otMeshDiagIp6AddrIterator
|
||||
@@ -65,14 +70,19 @@ namespace Utils {
|
||||
*/
|
||||
class MeshDiag : public InstanceLocator
|
||||
{
|
||||
friend class ot::NetworkDiagnostic::Client;
|
||||
|
||||
public:
|
||||
static constexpr uint16_t kVersionUnknown = OT_MESH_DIAG_VERSION_UNKNOWN; ///< Unknown version.
|
||||
|
||||
typedef otMeshDiagDiscoverConfig DiscoverConfig; ///< The discovery configuration.
|
||||
typedef otMeshDiagDiscoverCallback DiscoverCallback; ///< The discovery callback function pointer type.
|
||||
typedef otMeshDiagDiscoverConfig DiscoverConfig; ///< Discovery configuration.
|
||||
typedef otMeshDiagDiscoverCallback DiscoverCallback; ///< Discovery callback.
|
||||
typedef otMeshDiagQueryChildTableCallback QueryChildTableCallback; ///< Query Child Table callback.
|
||||
typedef otMeshDiagChildIp6AddrsCallback ChildIp6AddrsCallback; ///< Child IPv6 addresses callback.
|
||||
typedef otMeshDiagQueryRouterNeighborTableCallback RouterNeighborTableCallback; ///< Neighbor table callback.
|
||||
|
||||
/**
|
||||
* Represents an iterator to go over list of IPv6 addresses of a router.
|
||||
* Represents an iterator to go over list of IPv6 addresses of a router or an MTD child.
|
||||
*
|
||||
*/
|
||||
class Ip6AddrIterator : public otMeshDiagIp6AddrIterator
|
||||
@@ -164,7 +174,7 @@ public:
|
||||
* @param[in] aContext A context to pass in @p aCallback.
|
||||
*
|
||||
* @retval kErrorNone The network topology discovery started successfully.
|
||||
* @retval kErrorBusy A previous discovery request is still ongoing.
|
||||
* @retval kErrorBusy A previous discovery or query request is still ongoing.
|
||||
* @retval kErrorInvalidState Device is not attached.
|
||||
* @retval kErrorNoBufs Could not allocate buffer to send discovery messages.
|
||||
*
|
||||
@@ -172,9 +182,58 @@ public:
|
||||
Error DiscoverTopology(const DiscoverConfig &aConfig, DiscoverCallback aCallback, void *aContext);
|
||||
|
||||
/**
|
||||
* Cancels an ongoing topology discovery if there one, otherwise no action.
|
||||
* Starts query for child table for a given router.
|
||||
*
|
||||
* When ongoing discovery is cancelled, the callback from `DiscoverTopology()` will not be called anymore.
|
||||
* @param[in] aRloc16 The RLOC16 of router to query.
|
||||
* @param[in] aCallback The callback to report the queried child table.
|
||||
* @param[in] aContext A context to pass in @p aCallback.
|
||||
*
|
||||
* @retval kErrorNone The query started successfully.
|
||||
* @retval kErrorBusy A previous discovery or query request is still ongoing.
|
||||
* @retval kErrorInvalidArgs The @p aRloc16 is not a valid router RLOC16.
|
||||
* @retval kErrorInvalidState Device is not attached.
|
||||
* @retval kErrorNoBufs Could not allocate buffer to send query messages.
|
||||
*
|
||||
*/
|
||||
Error QueryChildTable(uint16_t aRloc16, QueryChildTableCallback aCallback, void *aContext);
|
||||
|
||||
/**
|
||||
* Sends a query to a parent to retrieve the IPv6 addresses of all its MTD children.
|
||||
*
|
||||
* @param[in] aRloc16 The RLOC16 of parent to query.
|
||||
* @param[in] aCallback The callback to report the queried child IPv6 address list.
|
||||
* @param[in] aContext A context to pass in @p aCallback.
|
||||
*
|
||||
* @retval kErrorNone The query started successfully.
|
||||
* @retval kErrorBusy A previous discovery or query request is still ongoing.
|
||||
* @retval kErrorInvalidArgs The @p aRloc16 is not a valid RLOC16.
|
||||
* @retval kErrorInvalidState Device is not attached.
|
||||
* @retval kErrorNoBufs Could not allocate buffer to send query messages.
|
||||
*
|
||||
*/
|
||||
Error QueryChildrenIp6Addrs(uint16_t aRloc16, ChildIp6AddrsCallback aCallback, void *aContext);
|
||||
|
||||
/**
|
||||
* Starts query for router neighbor table for a given router.
|
||||
*
|
||||
* @param[in] aRloc16 The RLOC16 of router to query.
|
||||
* @param[in] aCallback The callback to report the queried table.
|
||||
* @param[in] aContext A context to pass in @p aCallback.
|
||||
*
|
||||
* @retval kErrorNone The query started successfully.
|
||||
* @retval kErrorBusy A previous discovery or query request is still ongoing.
|
||||
* @retval kErrorInvalidArgs The @p aRloc16 is not a valid router RLOC16.
|
||||
* @retval kErrorInvalidState Device is not attached.
|
||||
* @retval kErrorNoBufs Could not allocate buffer to send query messages.
|
||||
*
|
||||
*/
|
||||
Error QueryRouterNeighborTable(uint16_t aRloc16, RouterNeighborTableCallback aCallback, void *aContext);
|
||||
|
||||
/**
|
||||
* Cancels an ongoing discovery or query operation if there one, otherwise no action.
|
||||
*
|
||||
* When ongoing discovery is cancelled, the callback from `DiscoverTopology()` or `QueryChildTable()` will not be
|
||||
* called anymore.
|
||||
*
|
||||
*/
|
||||
void Cancel(void);
|
||||
@@ -184,9 +243,65 @@ private:
|
||||
|
||||
static constexpr uint32_t kResponseTimeout = OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT;
|
||||
|
||||
Error SendDiagGetTo(uint16_t aRloc16, const DiscoverConfig &aConfig);
|
||||
enum State : uint8_t
|
||||
{
|
||||
kStateIdle,
|
||||
kStateDicoverTopology,
|
||||
kStateQueryChildTable,
|
||||
kStateQueryChildrenIp6Addrs,
|
||||
kStateQueryRouterNeighborTable,
|
||||
};
|
||||
|
||||
struct DiscoverInfo
|
||||
{
|
||||
Callback<DiscoverCallback> mCallback;
|
||||
Mle::RouterIdSet mExpectedRouterIdSet;
|
||||
};
|
||||
|
||||
struct QueryChildTableInfo
|
||||
{
|
||||
Callback<QueryChildTableCallback> mCallback;
|
||||
uint16_t mRouterRloc16;
|
||||
};
|
||||
|
||||
struct QueryChildrenIp6AddrsInfo
|
||||
{
|
||||
Callback<ChildIp6AddrsCallback> mCallback;
|
||||
uint16_t mParentRloc16;
|
||||
};
|
||||
|
||||
struct QueryRouterNeighborTableInfo
|
||||
{
|
||||
Callback<RouterNeighborTableCallback> mCallback;
|
||||
uint16_t mRouterRloc16;
|
||||
};
|
||||
|
||||
class ChildEntry : public otMeshDiagChildEntry
|
||||
{
|
||||
friend class MeshDiag;
|
||||
|
||||
private:
|
||||
void SetFrom(const NetworkDiagnostic::ChildTlv &aChildTlv);
|
||||
};
|
||||
|
||||
class RouterNeighborEntry : public otMeshDiagRouterNeighborEntry
|
||||
{
|
||||
friend class MeshDiag;
|
||||
|
||||
private:
|
||||
void SetFrom(const NetworkDiagnostic::RouterNeighborTlv &aTlv);
|
||||
};
|
||||
|
||||
Error SendQuery(uint16_t aRloc16, const uint8_t *aTlvs, uint8_t aTlvsLength);
|
||||
void Finalize(Error aError);
|
||||
void HandleTimer(void);
|
||||
void HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult);
|
||||
bool HandleDiagnosticGetAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||
Error ProcessMessage(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint16_t aSenderRloc16);
|
||||
bool ProcessChildTableAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||
bool ProcessChildrenIp6AddrsAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||
bool ProcessRouterNeighborTableAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||
|
||||
void HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult);
|
||||
|
||||
static void HandleDiagGetResponse(void *aContext,
|
||||
otMessage *aMessage,
|
||||
@@ -195,9 +310,18 @@ private:
|
||||
|
||||
using TimeoutTimer = TimerMilliIn<MeshDiag, &MeshDiag::HandleTimer>;
|
||||
|
||||
Callback<DiscoverCallback> mDiscoverCallback;
|
||||
Mle::RouterIdSet mExpectedRouterIdSet;
|
||||
TimeoutTimer mTimer;
|
||||
State mState;
|
||||
uint16_t mExpectedQueryId;
|
||||
uint16_t mExpectedAnswerIndex;
|
||||
TimeoutTimer mTimer;
|
||||
|
||||
union
|
||||
{
|
||||
DiscoverInfo mDiscover;
|
||||
QueryChildTableInfo mQueryChildTable;
|
||||
QueryChildrenIp6AddrsInfo mQueryChildrenIp6Addrs;
|
||||
QueryRouterNeighborTableInfo mQueryRouterNeighborTable;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
@@ -250,7 +250,7 @@ class Cert_5_7_02_CoapDiagCommands(thread_cert.TestCase):
|
||||
pkts.filter_wpan_src64(LEADER).\
|
||||
filter_ipv6_dst(DUT_RLOC).\
|
||||
filter_coap_request(DIAG_GET_URI).\
|
||||
filter(lambda p: dut_payload_tlvs == set(p.thread_diagnostic.tlv.type)).\
|
||||
filter(lambda p: dut_payload_tlvs <= set(p.thread_diagnostic.tlv.type)).\
|
||||
must_next()
|
||||
dut_payload_tlvs.remove(DG_TYPE_LIST_TLV)
|
||||
dut_payload_tlvs.remove(DG_ROUTE64_TLV)
|
||||
@@ -277,7 +277,7 @@ class Cert_5_7_02_CoapDiagCommands(thread_cert.TestCase):
|
||||
filter(lambda p: {
|
||||
DG_TYPE_LIST_TLV,
|
||||
DG_MAC_COUNTERS_TLV
|
||||
} == set(p.thread_diagnostic.tlv.type)
|
||||
} <= set(p.thread_diagnostic.tlv.type)
|
||||
).\
|
||||
must_next()
|
||||
pkts.filter_wpan_src64(DUT).\
|
||||
@@ -285,7 +285,7 @@ class Cert_5_7_02_CoapDiagCommands(thread_cert.TestCase):
|
||||
filter_coap_ack(DIAG_GET_URI).\
|
||||
filter(lambda p: {
|
||||
DG_MAC_COUNTERS_TLV
|
||||
} == set(p.thread_diagnostic.tlv.type)
|
||||
} <= set(p.thread_diagnostic.tlv.type)
|
||||
).\
|
||||
must_next()
|
||||
|
||||
@@ -304,7 +304,7 @@ class Cert_5_7_02_CoapDiagCommands(thread_cert.TestCase):
|
||||
DG_TYPE_LIST_TLV,
|
||||
DG_TIMEOUT_TLV,
|
||||
DG_CHILD_TABLE_TLV
|
||||
} == set(p.thread_diagnostic.tlv.type)
|
||||
} <= set(p.thread_diagnostic.tlv.type)
|
||||
).\
|
||||
must_next()
|
||||
pkts.filter_wpan_src64(DUT).\
|
||||
@@ -327,7 +327,7 @@ class Cert_5_7_02_CoapDiagCommands(thread_cert.TestCase):
|
||||
DG_TYPE_LIST_TLV,
|
||||
DG_BATTERY_LEVEL_TLV,
|
||||
DG_SUPPLY_VOLTAGE_TLV
|
||||
} == set(p.thread_diagnostic.tlv.type)
|
||||
} <= set(p.thread_diagnostic.tlv.type)
|
||||
).\
|
||||
must_next()
|
||||
pkts.filter_wpan_src64(DUT).\
|
||||
@@ -347,7 +347,7 @@ class Cert_5_7_02_CoapDiagCommands(thread_cert.TestCase):
|
||||
filter(lambda p: {
|
||||
DG_TYPE_LIST_TLV,
|
||||
DG_MAC_COUNTERS_TLV
|
||||
} == set(p.thread_diagnostic.tlv.type)
|
||||
} <= set(p.thread_diagnostic.tlv.type)
|
||||
).\
|
||||
must_next()
|
||||
pkts.filter_wpan_src64(DUT).\
|
||||
@@ -370,7 +370,7 @@ class Cert_5_7_02_CoapDiagCommands(thread_cert.TestCase):
|
||||
filter(lambda p: {
|
||||
DG_TYPE_LIST_TLV,
|
||||
DG_MAC_COUNTERS_TLV
|
||||
} == set(p.thread_diagnostic.tlv.type)
|
||||
} <= set(p.thread_diagnostic.tlv.type)
|
||||
).\
|
||||
must_next()
|
||||
pkts.filter_wpan_src64(DUT).\
|
||||
@@ -378,7 +378,7 @@ class Cert_5_7_02_CoapDiagCommands(thread_cert.TestCase):
|
||||
filter_coap_ack(DIAG_GET_URI).\
|
||||
filter(lambda p: {
|
||||
DG_MAC_COUNTERS_TLV
|
||||
} == set(p.thread_diagnostic.tlv.type)
|
||||
} <= set(p.thread_diagnostic.tlv.type)
|
||||
).\
|
||||
must_next()
|
||||
|
||||
@@ -401,7 +401,7 @@ class Cert_5_7_02_CoapDiagCommands(thread_cert.TestCase):
|
||||
pkts.filter_wpan_src64(LEADER).\
|
||||
filter_ipv6_dst(REALM_LOCAL_All_THREAD_NODES_MULTICAST_ADDRESS).\
|
||||
filter_coap_request(DIAG_GET_QRY_URI).\
|
||||
filter(lambda p: dut_payload_tlvs == set(p.thread_diagnostic.tlv.type)).\
|
||||
filter(lambda p: dut_payload_tlvs <= set(p.thread_diagnostic.tlv.type)).\
|
||||
must_next()
|
||||
|
||||
dut_payload_tlvs.remove(DG_TYPE_LIST_TLV)
|
||||
|
||||
@@ -189,7 +189,7 @@ class Cert_5_7_03_CoapDiagCommands_Base(thread_cert.TestCase):
|
||||
}
|
||||
if self.TOPOLOGY[ROUTER1]['name'] == 'DUT':
|
||||
dut_payload_tlvs.update({DG_CONNECTIVITY_TLV, DG_ROUTE64_TLV, DG_CHILD_TABLE_TLV})
|
||||
_qr_pkt.must_verify(lambda p: dut_payload_tlvs == set(p.thread_diagnostic.tlv.type))
|
||||
_qr_pkt.must_verify(lambda p: dut_payload_tlvs <= set(p.thread_diagnostic.tlv.type))
|
||||
|
||||
# Step 3: The DUT automatically responds with a DIAG_GET.ans response
|
||||
# MUST contain the requested diagnostic TLVs:
|
||||
@@ -208,7 +208,7 @@ class Cert_5_7_03_CoapDiagCommands_Base(thread_cert.TestCase):
|
||||
filter_ipv6_dst(LEADER_RLOC).\
|
||||
filter_coap_request(DIAG_GET_ANS_URI).\
|
||||
filter(lambda p:
|
||||
dut_payload_tlvs == set(p.thread_diagnostic.tlv.type) and\
|
||||
dut_payload_tlvs <= set(p.thread_diagnostic.tlv.type) and\
|
||||
{str(p.wpan.src64), colon_hex(dut_addr16, 2), '0f'}
|
||||
< set(p.thread_diagnostic.tlv.general)
|
||||
).\
|
||||
@@ -221,7 +221,7 @@ class Cert_5_7_03_CoapDiagCommands_Base(thread_cert.TestCase):
|
||||
filter_ipv6_dst(REALM_LOCAL_All_THREAD_NODES_MULTICAST_ADDRESS).\
|
||||
filter_coap_request(DIAG_GET_QRY_URI).\
|
||||
filter(lambda p:
|
||||
dut_payload_tlvs == set(p.thread_diagnostic.tlv.type)
|
||||
dut_payload_tlvs <= set(p.thread_diagnostic.tlv.type)
|
||||
).\
|
||||
must_next()
|
||||
|
||||
@@ -240,7 +240,7 @@ class Cert_5_7_03_CoapDiagCommands_Base(thread_cert.TestCase):
|
||||
filter_ipv6_dst(LEADER_RLOC).\
|
||||
filter_coap_request(DIAG_GET_ANS_URI).\
|
||||
filter(lambda p:
|
||||
dut_payload_tlvs == set(p.thread_diagnostic.tlv.type) and\
|
||||
dut_payload_tlvs <= set(p.thread_diagnostic.tlv.type) and\
|
||||
{str(p.wpan.src64), colon_hex(dut_addr16, 2), '0f'}
|
||||
< set(p.thread_diagnostic.tlv.general)
|
||||
).\
|
||||
|
||||
Executable
+164
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2023, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from cli import verify
|
||||
from cli import verify_within
|
||||
import cli
|
||||
import time
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test description: Validate Mesh Diag module commands.
|
||||
#
|
||||
# Network topology
|
||||
#
|
||||
# r1 ---- r2 ---- r3
|
||||
# /\ /|\
|
||||
# / \ / | \
|
||||
# f1 s1 f2 s2 s3
|
||||
|
||||
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
|
||||
print('-' * 120)
|
||||
print('Starting \'{}\''.format(test_name))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Creating `cli.Node` instances
|
||||
|
||||
speedup = 40
|
||||
cli.Node.set_time_speedup_factor(speedup)
|
||||
|
||||
r1 = cli.Node()
|
||||
r2 = cli.Node()
|
||||
r3 = cli.Node()
|
||||
fed1 = cli.Node()
|
||||
fed2 = cli.Node()
|
||||
sed1 = cli.Node()
|
||||
sed2 = cli.Node()
|
||||
sed3 = cli.Node()
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Form topology
|
||||
|
||||
r1.allowlist_node(r2)
|
||||
r1.allowlist_node(fed1)
|
||||
r1.allowlist_node(sed1)
|
||||
|
||||
r2.allowlist_node(r1)
|
||||
r2.allowlist_node(r3)
|
||||
|
||||
r3.allowlist_node(r2)
|
||||
r3.allowlist_node(fed2)
|
||||
r3.allowlist_node(sed2)
|
||||
r3.allowlist_node(sed3)
|
||||
|
||||
fed1.allowlist_node(r1)
|
||||
fed2.allowlist_node(r3)
|
||||
sed1.allowlist_node(r1)
|
||||
sed2.allowlist_node(r3)
|
||||
sed3.allowlist_node(r3)
|
||||
|
||||
r1.form('mesh-diag')
|
||||
|
||||
r1.add_prefix('fd00:1::/64', 'poas')
|
||||
r1.add_prefix('fd00:2::/64', 'poas')
|
||||
r1.register_netdata()
|
||||
|
||||
r2.join(r1)
|
||||
r3.join(r1)
|
||||
fed1.join(r1, cli.JOIN_TYPE_REED)
|
||||
fed2.join(r1, cli.JOIN_TYPE_REED)
|
||||
sed1.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE)
|
||||
sed2.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE)
|
||||
sed3.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE)
|
||||
|
||||
verify(r1.get_state() == 'leader')
|
||||
verify(r2.get_state() == 'router')
|
||||
verify(r3.get_state() == 'router')
|
||||
verify(fed1.get_state() == 'child')
|
||||
verify(fed2.get_state() == 'child')
|
||||
verify(sed1.get_state() == 'child')
|
||||
verify(sed2.get_state() == 'child')
|
||||
verify(sed3.get_state() == 'child')
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test Implementation
|
||||
|
||||
r1_rloc = int(r1.get_rloc16(), 16)
|
||||
r2_rloc = int(r2.get_rloc16(), 16)
|
||||
r3_rloc = int(r3.get_rloc16(), 16)
|
||||
|
||||
fed1_rloc = int(fed1.get_rloc16(), 16)
|
||||
fed2_rloc = int(fed2.get_rloc16(), 16)
|
||||
|
||||
sed1_rloc = int(sed1.get_rloc16(), 16)
|
||||
sed2_rloc = int(sed2.get_rloc16(), 16)
|
||||
sed3_rloc = int(sed3.get_rloc16(), 16)
|
||||
|
||||
topo = r1.cli('meshdiag topology')
|
||||
verify(len(topo) == 6)
|
||||
|
||||
routers = []
|
||||
for line in topo:
|
||||
if line.startswith('id:'):
|
||||
routers.append(int(line.split(' ')[1].split(':')[1], 16))
|
||||
|
||||
verify(r1_rloc in routers)
|
||||
verify(r2_rloc in routers)
|
||||
verify(r3_rloc in routers)
|
||||
|
||||
r2.cli('meshdiag topology ip6-addrs')
|
||||
r3.cli('meshdiag topology ip6-addrs children')
|
||||
r1.cli('meshdiag topology children')
|
||||
|
||||
childtable = r2.cli('meshdiag childtable', r1_rloc)
|
||||
verify(len([line for line in childtable if line.startswith('rloc16')]) == 2)
|
||||
|
||||
childtable = r1.cli('meshdiag childtable', r3_rloc)
|
||||
verify(len([line for line in childtable if line.startswith('rloc16')]) == 3)
|
||||
|
||||
childtable = r1.cli('meshdiag childtable', r2_rloc)
|
||||
verify(len([line for line in childtable if line.startswith('rloc16')]) == 0)
|
||||
|
||||
childrenip6addrs = r2.cli('meshdiag childip6', r1_rloc)
|
||||
verify(len([line for line in childrenip6addrs if line.startswith('child-rloc16')]) == 1)
|
||||
verify(len([line for line in childrenip6addrs if line.startswith(' ')]) == 3)
|
||||
|
||||
childrenip6addrs = r1.cli('meshdiag childip6', r3_rloc)
|
||||
verify(len([line for line in childrenip6addrs if line.startswith('child-rloc16')]) == 2)
|
||||
|
||||
neightable = r1.cli('meshdiag routerneighbortable', r2_rloc)
|
||||
verify(len([line for line in neightable if line.startswith('rloc16')]) == 2)
|
||||
|
||||
neightable = r3.cli('meshdiag routerneighbortable', r1_rloc)
|
||||
verify(len([line for line in neightable if line.startswith('rloc16')]) == 1)
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test finished
|
||||
|
||||
cli.Node.finalize_all_nodes()
|
||||
|
||||
print('\'{}\' passed.'.format(test_name))
|
||||
@@ -187,6 +187,7 @@ if [ "$TORANJ_CLI" = 1 ]; then
|
||||
run cli/test-020-net-diag-vendor-info.py
|
||||
run cli/test-021-br-route-prf.py
|
||||
run cli/test-022-netdata-full.py
|
||||
run cli/test-023-mesh-diag.py
|
||||
run cli/test-400-srp-client-server.py
|
||||
run cli/test-601-channel-manager-channel-change.py
|
||||
# Skip the "channel-select" test on a TREL only radio link, since it
|
||||
|
||||
Reference in New Issue
Block a user