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  *   This file includes definitions for Mesh Diagnostic module.
32  */
33 
34 #ifndef MESH_DIAG_HPP_
35 #define MESH_DIAG_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
40 
41 #if !OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE
42 #error "OPENTHREAD_CONFIG_MESH_DIAG_ENABLE requires OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE"
43 #endif
44 
45 #include <openthread/mesh_diag.h>
46 
47 #include "coap/coap.hpp"
48 #include "common/callback.hpp"
49 #include "common/locator.hpp"
50 #include "common/message.hpp"
51 #include "common/timer.hpp"
52 #include "net/ip6_address.hpp"
53 #include "thread/network_diagnostic.hpp"
54 #include "thread/network_diagnostic_tlvs.hpp"
55 
56 struct otMeshDiagIp6AddrIterator
57 {
58 };
59 
60 struct otMeshDiagChildIterator
61 {
62 };
63 
64 namespace ot {
65 namespace Utils {
66 
67 /**
68  * Implements the Mesh Diagnostics.
69  *
70  */
71 class MeshDiag : public InstanceLocator
72 {
73     friend class ot::NetworkDiagnostic::Client;
74 
75 public:
76     static constexpr uint16_t kVersionUnknown = OT_MESH_DIAG_VERSION_UNKNOWN; ///< Unknown version.
77 
78     typedef otMeshDiagDiscoverConfig                   DiscoverConfig;              ///< Discovery configuration.
79     typedef otMeshDiagDiscoverCallback                 DiscoverCallback;            ///< Discovery callback.
80     typedef otMeshDiagQueryChildTableCallback          QueryChildTableCallback;     ///< Query Child Table callback.
81     typedef otMeshDiagChildIp6AddrsCallback            ChildIp6AddrsCallback;       ///< Child IPv6 addresses callback.
82     typedef otMeshDiagQueryRouterNeighborTableCallback RouterNeighborTableCallback; ///< Neighbor table callback.
83 
84     /**
85      * Represents an iterator to go over list of IPv6 addresses of a router or an MTD child.
86      *
87      */
88     class Ip6AddrIterator : public otMeshDiagIp6AddrIterator
89     {
90         friend class MeshDiag;
91 
92     public:
93         /**
94          * Iterates through the discovered IPv6 address of a router.
95          *
96          * @param[out]     aIp6Address  A reference to return the next IPv6 address (if any).
97          *
98          * @retval kErrorNone      Successfully retrieved the next address. @p aIp6Address is updated.
99          * @retval kErrorNotFound  No more address. Reached the end of the list.
100          *
101          */
102         Error GetNextAddress(Ip6::Address &aAddress);
103 
104     private:
105         Error InitFrom(const Message &aMessage);
106 
107         const Message *mMessage;
108         uint16_t       mCurOffset;
109         uint16_t       mEndOffset;
110     };
111 
112     /**
113      * Represents information about a router in Thread mesh.
114      *
115      */
116     class RouterInfo : public otMeshDiagRouterInfo, public Clearable<RouterInfo>
117     {
118         friend class MeshDiag;
119 
120     private:
121         Error ParseFrom(const Message &aMessage);
122     };
123 
124     /**
125      * Represents information about a child in Thread mesh.
126      *
127      */
128     class ChildInfo : public otMeshDiagChildInfo, public Clearable<ChildInfo>
129     {
130     };
131 
132     /**
133      * Represents an iterator to go over list of IPv6 addresses of a router.
134      *
135      */
136     class ChildIterator : public otMeshDiagChildIterator
137     {
138         friend class MeshDiag;
139 
140     public:
141         /**
142          * Iterates through the discovered children of a router.
143          *
144          * @param[out]     aChildInfo  A reference to return the info for the next child (if any).
145          *
146          * @retval kErrorNone      Successfully retrieved the next child info. @p aChildInfo is updated.
147          * @retval kErrorNotFound  No more child entry. Reached the end of the list.
148          *
149          */
150         Error GetNextChildInfo(ChildInfo &aChildInfo);
151 
152     private:
153         Error InitFrom(const Message &aMessage, uint16_t aParentRloc16);
154 
155         const Message *mMessage;
156         uint16_t       mCurOffset;
157         uint16_t       mEndOffset;
158         uint16_t       mParentRloc16;
159     };
160 
161     /**
162      * Initializes the `MeshDiag` instance.
163      *
164      * @param[in] aInstance   The OpenThread instance.
165      *
166      */
167     explicit MeshDiag(Instance &aInstance);
168 
169     /**
170      * Starts network topology discovery.
171      *
172      * @param[in] aConfig          The configuration to use for discovery (e.g., which items to discover).
173      * @param[in] aCallback        The callback to report the discovered routers.
174      * @param[in] aContext         A context to pass in @p aCallback.
175      *
176      * @retval kErrorNone          The network topology discovery started successfully.
177      * @retval kErrorBusy          A previous discovery or query request is still ongoing.
178      * @retval kErrorInvalidState  Device is not attached.
179      * @retval kErrorNoBufs        Could not allocate buffer to send discovery messages.
180      *
181      */
182     Error DiscoverTopology(const DiscoverConfig &aConfig, DiscoverCallback aCallback, void *aContext);
183 
184     /**
185      * Starts query for child table for a given router.
186      *
187      * @param[in] aRloc16          The RLOC16 of router to query.
188      * @param[in] aCallback        The callback to report the queried child table.
189      * @param[in] aContext         A context to pass in @p aCallback.
190      *
191      * @retval kErrorNone          The query started successfully.
192      * @retval kErrorBusy          A previous discovery or query request is still ongoing.
193      * @retval kErrorInvalidArgs   The @p aRloc16 is not a valid router RLOC16.
194      * @retval kErrorInvalidState  Device is not attached.
195      * @retval kErrorNoBufs        Could not allocate buffer to send query messages.
196      *
197      */
198     Error QueryChildTable(uint16_t aRloc16, QueryChildTableCallback aCallback, void *aContext);
199 
200     /**
201      * Sends a query to a parent to retrieve the IPv6 addresses of all its MTD children.
202      *
203      * @param[in] aRloc16          The RLOC16 of parent to query.
204      * @param[in] aCallback        The callback to report the queried child IPv6 address list.
205      * @param[in] aContext         A context to pass in @p aCallback.
206      *
207      * @retval kErrorNone          The query started successfully.
208      * @retval kErrorBusy          A previous discovery or query request is still ongoing.
209      * @retval kErrorInvalidArgs   The @p aRloc16 is not a valid  RLOC16.
210      * @retval kErrorInvalidState  Device is not attached.
211      * @retval kErrorNoBufs        Could not allocate buffer to send query messages.
212      *
213      */
214     Error QueryChildrenIp6Addrs(uint16_t aRloc16, ChildIp6AddrsCallback aCallback, void *aContext);
215 
216     /**
217      * Starts query for router neighbor table for a given router.
218      *
219      * @param[in] aRloc16          The RLOC16 of router to query.
220      * @param[in] aCallback        The callback to report the queried table.
221      * @param[in] aContext         A context to pass in @p aCallback.
222      *
223      * @retval kErrorNone          The query started successfully.
224      * @retval kErrorBusy          A previous discovery or query request is still ongoing.
225      * @retval kErrorInvalidArgs   The @p aRloc16 is not a valid router RLOC16.
226      * @retval kErrorInvalidState  Device is not attached.
227      * @retval kErrorNoBufs        Could not allocate buffer to send query messages.
228      *
229      */
230     Error QueryRouterNeighborTable(uint16_t aRloc16, RouterNeighborTableCallback aCallback, void *aContext);
231 
232     /**
233      * Cancels an ongoing discovery or query operation if there one, otherwise no action.
234      *
235      * When ongoing discovery is cancelled, the callback from `DiscoverTopology()` or  `QueryChildTable()` will not be
236      * called anymore.
237      *
238      */
239     void Cancel(void);
240 
241 private:
242     typedef ot::NetworkDiagnostic::Tlv Tlv;
243 
244     static constexpr uint32_t kResponseTimeout = OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT;
245 
246     enum State : uint8_t
247     {
248         kStateIdle,
249         kStateDicoverTopology,
250         kStateQueryChildTable,
251         kStateQueryChildrenIp6Addrs,
252         kStateQueryRouterNeighborTable,
253     };
254 
255     struct DiscoverInfo
256     {
257         Callback<DiscoverCallback> mCallback;
258         Mle::RouterIdSet           mExpectedRouterIdSet;
259     };
260 
261     struct QueryChildTableInfo
262     {
263         Callback<QueryChildTableCallback> mCallback;
264         uint16_t                          mRouterRloc16;
265     };
266 
267     struct QueryChildrenIp6AddrsInfo
268     {
269         Callback<ChildIp6AddrsCallback> mCallback;
270         uint16_t                        mParentRloc16;
271     };
272 
273     struct QueryRouterNeighborTableInfo
274     {
275         Callback<RouterNeighborTableCallback> mCallback;
276         uint16_t                              mRouterRloc16;
277     };
278 
279     class ChildEntry : public otMeshDiagChildEntry
280     {
281         friend class MeshDiag;
282 
283     private:
284         void SetFrom(const NetworkDiagnostic::ChildTlv &aChildTlv);
285     };
286 
287     class RouterNeighborEntry : public otMeshDiagRouterNeighborEntry
288     {
289         friend class MeshDiag;
290 
291     private:
292         void SetFrom(const NetworkDiagnostic::RouterNeighborTlv &aTlv);
293     };
294 
295     Error SendQuery(uint16_t aRloc16, const uint8_t *aTlvs, uint8_t aTlvsLength);
296     void  Finalize(Error aError);
297     void  HandleTimer(void);
298     bool  HandleDiagnosticGetAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
299     Error ProcessMessage(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint16_t aSenderRloc16);
300     bool  ProcessChildTableAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
301     bool  ProcessChildrenIp6AddrsAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
302     bool  ProcessRouterNeighborTableAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
303 
304     void HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult);
305 
306     static void HandleDiagGetResponse(void                *aContext,
307                                       otMessage           *aMessage,
308                                       const otMessageInfo *aMessageInfo,
309                                       Error                aResult);
310 
311     using TimeoutTimer = TimerMilliIn<MeshDiag, &MeshDiag::HandleTimer>;
312 
313     State        mState;
314     uint16_t     mExpectedQueryId;
315     uint16_t     mExpectedAnswerIndex;
316     TimeoutTimer mTimer;
317 
318     union
319     {
320         DiscoverInfo                 mDiscover;
321         QueryChildTableInfo          mQueryChildTable;
322         QueryChildrenIp6AddrsInfo    mQueryChildrenIp6Addrs;
323         QueryRouterNeighborTableInfo mQueryRouterNeighborTable;
324     };
325 };
326 
327 } // namespace Utils
328 
329 DefineCoreType(otMeshDiagIp6AddrIterator, Utils::MeshDiag::Ip6AddrIterator);
330 DefineCoreType(otMeshDiagRouterInfo, Utils::MeshDiag::RouterInfo);
331 DefineCoreType(otMeshDiagChildInfo, Utils::MeshDiag::ChildInfo);
332 DefineCoreType(otMeshDiagChildIterator, Utils::MeshDiag::ChildIterator);
333 
334 } // namespace ot
335 
336 #endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
337 
338 #endif // MESH_DIAG_HPP_
339