1 /*
2  *  Copyright (c) 2023, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  * @brief
32  *  This file defines the OpenThread Mesh Diagnostic APIs.
33  */
34 
35 #ifndef OPENTHREAD_MESH_DIAG_H_
36 #define OPENTHREAD_MESH_DIAG_H_
37 
38 #include <openthread/instance.h>
39 #include <openthread/thread.h>
40 
41 #ifdef __cplusplus
42 extern "C" {
43 #endif
44 
45 /**
46  * @addtogroup api-mesh-diag
47  *
48  * @brief
49  *   This module includes definitions and functions for Mesh Diagnostics.
50  *
51  *   The Mesh Diagnostics APIs require `OPENTHREAD_CONFIG_MESH_DIAG_ENABLE` and `OPENTHREAD_FTD`.
52  *
53  * @{
54  */
55 
56 /**
57  * Represents the set of configurations used when discovering mesh topology indicating which items to
58  * discover.
59  */
60 typedef struct otMeshDiagDiscoverConfig
61 {
62     bool mDiscoverIp6Addresses : 1; ///< Whether or not to discover IPv6 addresses of every router.
63     bool mDiscoverChildTable : 1;   ///< Whether or not to discover children of every router.
64 } otMeshDiagDiscoverConfig;
65 
66 /**
67  * An opaque iterator to iterate over list of IPv6 addresses of a router.
68  *
69  * Pointers to instance of this type are provided in `otMeshDiagRouterInfo`.
70  */
71 typedef struct otMeshDiagIp6AddrIterator otMeshDiagIp6AddrIterator;
72 
73 /**
74  * An opaque iterator to iterate over list of children of a router.
75  *
76  * Pointers to instance of this type are provided in `otMeshDiagRouterInfo`.
77  */
78 typedef struct otMeshDiagChildIterator otMeshDiagChildIterator;
79 
80 /**
81  * Specifies that Thread Version is unknown.
82  *
83  * This is used in `otMeshDiagRouterInfo` for `mVersion` property when device does not provide its version. This
84  * indicates that device is likely running 1.3.0 (version value 4) or earlier.
85  */
86 #define OT_MESH_DIAG_VERSION_UNKNOWN 0xffff
87 
88 /**
89  * Represents information about a router in Thread mesh discovered using `otMeshDiagDiscoverTopology()`.
90  */
91 typedef struct otMeshDiagRouterInfo
92 {
93     otExtAddress mExtAddress;             ///< Extended MAC address.
94     uint16_t     mRloc16;                 ///< RLOC16.
95     uint8_t      mRouterId;               ///< Router ID.
96     uint16_t     mVersion;                ///< Thread Version. `OT_MESH_DIAG_VERSION_UNKNOWN` if unknown.
97     bool         mIsThisDevice : 1;       ///< Whether router is this device itself.
98     bool         mIsThisDeviceParent : 1; ///< Whether router is parent of this device (when device is a child).
99     bool         mIsLeader : 1;           ///< Whether router is leader.
100     bool         mIsBorderRouter : 1;     ///< Whether router acts as a border router providing ext connectivity.
101 
102     /**
103      * Provides the link quality from this router to other routers, also indicating whether a link is established
104      * between the routers.
105      *
106      * The array is indexed based on Router ID. `mLinkQualities[routerId]` indicates the incoming link quality, the
107      * router sees to the router with `routerId`. Link quality is a value in [0, 3]. Value zero indicates no link.
108      * Larger value indicate better link quality (as defined by Thread specification).
109      */
110     uint8_t mLinkQualities[OT_NETWORK_MAX_ROUTER_ID + 1];
111 
112     /**
113      * A pointer to an iterator to go through the list of IPv6 addresses of the router.
114      *
115      * The pointer is valid only while `otMeshDiagRouterInfo` is valid. It can be used in `otMeshDiagGetNextIp6Address`
116      * to iterate through the IPv6 addresses.
117      *
118      * The pointer can be NULL when there was no request to discover IPv6 addresses (in `otMeshDiagDiscoverConfig`) or
119      * if the router did not provide the list.
120      */
121     otMeshDiagIp6AddrIterator *mIp6AddrIterator;
122 
123     /**
124      * A pointer to an iterator to go through the list of children of the router.
125      *
126      * The pointer is valid only while `otMeshDiagRouterInfo` is valid. It can be used in `otMeshDiagGetNextChildInfo`
127      * to iterate through the children of the router.
128      *
129      * The pointer can be NULL when there was no request to discover children (in `otMeshDiagDiscoverConfig`) or
130      * if the router did not provide the list.
131      */
132     otMeshDiagChildIterator *mChildIterator;
133 } otMeshDiagRouterInfo;
134 
135 /**
136  * Represents information about a discovered child in Thread mesh using `otMeshDiagDiscoverTopology()`.
137  */
138 typedef struct otMeshDiagChildInfo
139 {
140     uint16_t         mRloc16;             ///< RLOC16.
141     otLinkModeConfig mMode;               ///< Device mode.
142     uint8_t          mLinkQuality;        ///< Incoming link quality to child from parent.
143     bool             mIsThisDevice : 1;   ///< Whether child is this device itself.
144     bool             mIsBorderRouter : 1; ///< Whether child acts as a border router providing ext connectivity.
145 } otMeshDiagChildInfo;
146 
147 /**
148  * Pointer type represents the callback used by `otMeshDiagDiscoverTopology()` to provide information
149  * about a discovered router.
150  *
151  * When @p aError is `OT_ERROR_PENDING`, it indicates that the discovery is not yet finished and there will be more
152  * routers to discover and the callback will be invoked again.
153  *
154  * @param[in] aError       OT_ERROR_PENDING            Indicates there are more routers to be discovered.
155  *                         OT_ERROR_NONE               Indicates this is the last router and mesh discovery is done.
156  *                         OT_ERROR_RESPONSE_TIMEOUT   Timed out waiting for response from one or more routers.
157  * @param[in] aRouterInfo  The discovered router info (can be null if `aError` is OT_ERROR_RESPONSE_TIMEOUT).
158  * @param[in] aContext     Application-specific context.
159  */
160 typedef void (*otMeshDiagDiscoverCallback)(otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext);
161 
162 /**
163  * Starts network topology discovery.
164  *
165  * @param[in] aInstance        The OpenThread instance.
166  * @param[in] aConfig          The configuration to use for discovery (e.g., which items to discover).
167  * @param[in] aCallback        The callback to report the discovered routers.
168  * @param[in] aContext         A context to pass in @p aCallback.
169  *
170  * @retval OT_ERROR_NONE            The network topology discovery started successfully.
171  * @retval OT_ERROR_BUSY            A previous discovery request is still ongoing.
172  * @retval OT_ERROR_INVALID_STATE   Device is not attached.
173  * @retval OT_ERROR_NO_BUFS         Could not allocate buffer to send discovery messages.
174  */
175 otError otMeshDiagDiscoverTopology(otInstance                     *aInstance,
176                                    const otMeshDiagDiscoverConfig *aConfig,
177                                    otMeshDiagDiscoverCallback      aCallback,
178                                    void                           *aContext);
179 
180 /**
181  * Cancels an ongoing topology discovery if there is one, otherwise no action.
182  *
183  * When ongoing discovery is cancelled, the callback from `otMeshDiagDiscoverTopology()` will not be called anymore.
184  */
185 void otMeshDiagCancel(otInstance *aInstance);
186 
187 /**
188  * Iterates through the discovered IPv6 addresses of a router or an MTD child.
189  *
190  * MUST be used
191  * - from the callback `otMeshDiagDiscoverCallback()` and use the `mIp6AddrIterator` from the `aRouterInfo` struct that
192  *   is provided as input to the callback, or
193  * - from the callback `otMeshDiagChildIp6AddrsCallback()` along with provided `aIp6AddrIterator`.
194  *
195  * @param[in,out]  aIterator    The address iterator to use.
196  * @param[out]     aIp6Address  A pointer to return the next IPv6 address (if any).
197  *
198  * @retval OT_ERROR_NONE       Successfully retrieved the next address. @p aIp6Address and @p aIterator are updated.
199  * @retval OT_ERROR_NOT_FOUND  No more address. Reached the end of the list.
200  */
201 otError otMeshDiagGetNextIp6Address(otMeshDiagIp6AddrIterator *aIterator, otIp6Address *aIp6Address);
202 
203 /**
204  * Iterates through the discovered children of a router.
205  *
206  * This function MUST be used from the callback `otMeshDiagDiscoverCallback()` and use the `mChildIterator` from the
207  * `aRouterInfo` struct that is provided as input to the callback.
208  *
209  * @param[in,out]  aIterator    The address iterator to use.
210  * @param[out]     aChildInfo   A pointer to return the child info (if any).
211  *
212  * @retval OT_ERROR_NONE       Successfully retrieved the next child. @p aChildInfo and @p aIterator are updated.
213  * @retval OT_ERROR_NOT_FOUND  No more child. Reached the end of the list.
214  */
215 otError otMeshDiagGetNextChildInfo(otMeshDiagChildIterator *aIterator, otMeshDiagChildInfo *aChildInfo);
216 
217 /**
218  * Represents information about a child entry from `otMeshDiagQueryChildTable()`.
219  *
220  * `mSupportsErrRate` indicates whether or not the error tracking feature is supported and `mFrameErrorRate` and
221  * `mMessageErrorRate` values are valid. The frame error rate tracks frame tx errors (towards the child) at MAC
222  * layer,  while `mMessageErrorRate` tracks the IPv6 message error rate (above MAC layer and after MAC retries) when
223  * an IPv6 message is dropped. For example, if the message is large and requires 6LoWPAN fragmentation, message tx is
224  * considered as failed if one of its fragment frame tx fails (for example, never acked).
225  */
226 typedef struct otMeshDiagChildEntry
227 {
228     bool         mRxOnWhenIdle : 1;    ///< Is rx-on when idle (vs sleepy).
229     bool         mDeviceTypeFtd : 1;   ///< Is device FTD (vs MTD).
230     bool         mFullNetData : 1;     ///< Whether device gets full Network Data (vs stable sub-set).
231     bool         mCslSynchronized : 1; ///< Is CSL capable and CSL synchronized.
232     bool         mSupportsErrRate : 1; ///< `mFrameErrorRate` and `mMessageErrorRate` values are valid.
233     uint16_t     mRloc16;              ///< RLOC16.
234     otExtAddress mExtAddress;          ///< Extended Address.
235     uint16_t     mVersion;             ///< Version.
236     uint32_t     mTimeout;             ///< Timeout in seconds.
237     uint32_t     mAge;                 ///< Seconds since last heard from the child.
238     uint32_t     mConnectionTime;      ///< Seconds since child attach.
239     uint16_t     mSupervisionInterval; ///< Supervision interval in seconds. Zero to indicate not used.
240     uint8_t      mLinkMargin;          ///< Link Margin in dB.
241     int8_t       mAverageRssi;         ///< Average RSSI.
242     int8_t       mLastRssi;            ///< RSSI of last received frame.
243     uint16_t     mFrameErrorRate;      ///< Frame error rate (0x0000->0%, 0xffff->100%).
244     uint16_t     mMessageErrorRate;    ///< (IPv6) msg error rate (0x0000->0%, 0xffff->100%).
245     uint16_t     mQueuedMessageCount;  ///< Number of queued messages for indirect tx to child.
246     uint16_t     mCslPeriod;           ///< CSL Period in unit of 10-symbols-time. Zero indicates CSL is disabled.
247     uint32_t     mCslTimeout;          ///< CSL Timeout in seconds.
248     uint8_t      mCslChannel;          ///< CSL channel.
249 } otMeshDiagChildEntry;
250 
251 /**
252  * Represents the callback used by `otMeshDiagQueryChildTable()` to provide information about child table entries.
253  *
254  * When @p aError is `OT_ERROR_PENDING`, it indicates that the table still has more entries and the callback will be
255  * invoked again.
256  *
257  * @param[in] aError       OT_ERROR_PENDING            Indicates there are more entries in the table.
258  *                         OT_ERROR_NONE               Indicates the table is finished.
259  *                         OT_ERROR_RESPONSE_TIMEOUT   Timed out waiting for response.
260  * @param[in] aChildEntry  The child entry (can be null if `aError` is OT_ERROR_RESPONSE_TIMEOUT or OT_ERROR_NONE).
261  * @param[in] aContext     Application-specific context.
262  */
263 typedef void (*otMeshDiagQueryChildTableCallback)(otError                     aError,
264                                                   const otMeshDiagChildEntry *aChildEntry,
265                                                   void                       *aContext);
266 
267 /**
268  * Starts query for child table for a given router.
269  *
270  * @param[in] aInstance        The OpenThread instance.
271  * @param[in] aRloc16          The RLOC16 of router to query.
272  * @param[in] aCallback        The callback to report the queried child table.
273  * @param[in] aContext         A context to pass in @p aCallback.
274  *
275  * @retval OT_ERROR_NONE           The query started successfully.
276  * @retval OT_ERROR_BUSY           A previous discovery or query request is still ongoing.
277  * @retval OT_ERROR_INVALID_ARGS   The @p aRloc16 is not a valid router RLOC16.
278  * @retval OT_ERROR_INVALID_STATE  Device is not attached.
279  * @retval OT_ERROR_NO_BUFS        Could not allocate buffer to send query messages.
280  */
281 otError otMeshDiagQueryChildTable(otInstance                       *aInstance,
282                                   uint16_t                          aRloc16,
283                                   otMeshDiagQueryChildTableCallback aCallback,
284                                   void                             *aContext);
285 
286 /**
287  * Represents the callback used by `otMeshDiagQueryChildrenIp6Addrs()` to provide information about an MTD child and
288  * its list of IPv6 addresses.
289  *
290  * When @p aError is `OT_ERROR_PENDING`, it indicates that there are more children and the callback will be invoked
291  * again.
292  *
293  * @param[in] aError            OT_ERROR_PENDING            Indicates there are more children in the table.
294  *                              OT_ERROR_NONE               Indicates the table is finished.
295  *                              OT_ERROR_RESPONSE_TIMEOUT   Timed out waiting for response.
296  * @param[in] aChildRloc16      The RLOC16 of the child. `0xfffe` is used on `OT_ERROR_RESPONSE_TIMEOUT`.
297  * @param[in] aIp6AddrIterator  An iterator to go through the IPv6 addresses of the child with @p aRloc using
298  *                              `otMeshDiagGetNextIp6Address()`. Set to NULL on `OT_ERROR_RESPONSE_TIMEOUT`.
299  * @param[in] aContext          Application-specific context.
300  */
301 typedef void (*otMeshDiagChildIp6AddrsCallback)(otError                    aError,
302                                                 uint16_t                   aChildRloc16,
303                                                 otMeshDiagIp6AddrIterator *aIp6AddrIterator,
304                                                 void                      *aContext);
305 
306 /**
307  * Sends a query to a parent to retrieve the IPv6 addresses of all its MTD children.
308  *
309  * @param[in] aInstance        The OpenThread instance.
310  * @param[in] aRloc16          The RLOC16 of parent to query.
311  * @param[in] aCallback        The callback to report the queried child IPv6 address list.
312  * @param[in] aContext         A context to pass in @p aCallback.
313  *
314  * @retval OT_ERROR_NONE           The query started successfully.
315  * @retval OT_ERROR_BUSY           A previous discovery or query request is still ongoing.
316  * @retval OT_ERROR_INVALID_ARGS   The @p aRloc16 is not a valid  RLOC16.
317  * @retval OT_ERROR_INVALID_STATE  Device is not attached.
318  * @retval OT_ERROR_NO_BUFS        Could not allocate buffer to send query messages.
319  */
320 otError otMeshDiagQueryChildrenIp6Addrs(otInstance                     *aInstance,
321                                         uint16_t                        aRloc16,
322                                         otMeshDiagChildIp6AddrsCallback aCallback,
323                                         void                           *aContext);
324 
325 /**
326  * Represents information about a router neighbor entry from `otMeshDiagQueryRouterNeighborTable()`.
327  *
328  * `mSupportsErrRate` indicates whether or not the error tracking feature is supported and `mFrameErrorRate` and
329  * `mMessageErrorRate` values are valid. The frame error rate tracks frame tx errors (towards the child) at MAC
330  * layer,  while `mMessageErrorRate` tracks the IPv6 message error rate (above MAC layer and after MAC retries) when
331  * an IPv6 message is dropped. For example, if the message is large and requires 6LoWPAN fragmentation, message tx is
332  * considered as failed if one of its fragment frame tx fails (for example, never acked).
333  */
334 typedef struct otMeshDiagRouterNeighborEntry
335 {
336     bool         mSupportsErrRate : 1; ///< `mFrameErrorRate` and `mMessageErrorRate` values are valid.
337     uint16_t     mRloc16;              ///< RLOC16.
338     otExtAddress mExtAddress;          ///< Extended Address.
339     uint16_t     mVersion;             ///< Version.
340     uint32_t     mConnectionTime;      ///< Seconds since link establishment.
341     uint8_t      mLinkMargin;          ///< Link Margin in dB.
342     int8_t       mAverageRssi;         ///< Average RSSI.
343     int8_t       mLastRssi;            ///< RSSI of last received frame.
344     uint16_t     mFrameErrorRate;      ///< Frame error rate (0x0000->0%, 0xffff->100%).
345     uint16_t     mMessageErrorRate;    ///< (IPv6) msg error rate (0x0000->0%, 0xffff->100%).
346 } otMeshDiagRouterNeighborEntry;
347 
348 /**
349  * Represents the callback used by `otMeshDiagQueryRouterNeighborTable()` to provide information about neighbor router
350  * table entries.
351  *
352  * When @p aError is `OT_ERROR_PENDING`, it indicates that the table still has more entries and the callback will be
353  * invoked again.
354  *
355  * @param[in] aError          OT_ERROR_PENDING            Indicates there are more entries in the table.
356  *                            OT_ERROR_NONE               Indicates the table is finished.
357  *                            OT_ERROR_RESPONSE_TIMEOUT   Timed out waiting for response.
358  * @param[in] aNeighborEntry  The neighbor entry (can be null if `aError` is RESPONSE_TIMEOUT or NONE).
359  * @param[in] aContext        Application-specific context.
360  */
361 typedef void (*otMeshDiagQueryRouterNeighborTableCallback)(otError                              aError,
362                                                            const otMeshDiagRouterNeighborEntry *aNeighborEntry,
363                                                            void                                *aContext);
364 
365 /**
366  * Starts query for router neighbor table for a given router.
367  *
368  * @param[in] aInstance        The OpenThread instance.
369  * @param[in] aRloc16          The RLOC16 of router to query.
370  * @param[in] aCallback        The callback to report the queried table.
371  * @param[in] aContext         A context to pass in @p aCallback.
372  *
373  * @retval OT_ERROR_NONE           The query started successfully.
374  * @retval OT_ERROR_BUSY           A previous discovery or query request is still ongoing.
375  * @retval OT_ERROR_INVALID_ARGS   The @p aRloc16 is not a valid router RLOC16.
376  * @retval OT_ERROR_INVALID_STATE  Device is not attached.
377  * @retval OT_ERROR_NO_BUFS        Could not allocate buffer to send query messages.
378  */
379 otError otMeshDiagQueryRouterNeighborTable(otInstance                                *aInstance,
380                                            uint16_t                                   aRloc16,
381                                            otMeshDiagQueryRouterNeighborTableCallback aCallback,
382                                            void                                      *aContext);
383 
384 /**
385  * @}
386  */
387 
388 #ifdef __cplusplus
389 } // extern "C"
390 #endif
391 
392 #endif // OPENTHREAD_MESH_DIAG_H_
393