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 implements CLI for Backbone Router.
32  */
33 
34 #include "cli_bbr.hpp"
35 
36 #include "cli/cli.hpp"
37 
38 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
39 
40 namespace ot {
41 namespace Cli {
42 
OutputConfig(const otBackboneRouterConfig & aConfig)43 void Bbr::OutputConfig(const otBackboneRouterConfig &aConfig)
44 {
45     OutputLine("seqno:    %u", aConfig.mSequenceNumber);
46     OutputLine("delay:    %u secs", aConfig.mReregistrationDelay);
47     OutputLine("timeout:  %lu secs", ToUlong(aConfig.mMlrTimeout));
48 }
49 
50 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
51 
52 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
53 
Process(Arg aArgs[])54 template <> otError Bbr::Process<Cmd("mlr")>(Arg aArgs[])
55 {
56     otError error = OT_ERROR_INVALID_COMMAND;
57 
58     /**
59      * @cli bbr mgmt mlr listener
60      * @code
61      * bbr mgmt mlr listener
62      * ff04:0:0:0:0:0:0:abcd 3534000
63      * ff04:0:0:0:0:0:0:eeee 3537610
64      * Done
65      * @endcode
66      * @par
67      * Returns the Multicast Listeners with the #otBackboneRouterMulticastListenerInfo
68      * `mTimeout` in seconds.
69      * @par
70      * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` and
71      * `OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE` are enabled.
72      * @sa otBackboneRouterMulticastListenerGetNext
73      */
74     if (aArgs[0] == "listener")
75     {
76         if (aArgs[1].IsEmpty())
77         {
78             otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
79             otBackboneRouterMulticastListenerInfo     listenerInfo;
80 
81             while (otBackboneRouterMulticastListenerGetNext(GetInstancePtr(), &iter, &listenerInfo) == OT_ERROR_NONE)
82             {
83                 OutputIp6Address(listenerInfo.mAddress);
84                 OutputLine(" %lu", ToUlong(listenerInfo.mTimeout));
85             }
86 
87             ExitNow(error = OT_ERROR_NONE);
88         }
89 
90 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
91         /**
92          * @cli bbr mgmt mlr listener clear
93          * @code
94          * bbr mgmt mlr listener clear
95          * Done
96          * @endcode
97          * @par api_copy
98          * #otBackboneRouterMulticastListenerClear
99          */
100         if (aArgs[1] == "clear")
101         {
102             otBackboneRouterMulticastListenerClear(GetInstancePtr());
103             error = OT_ERROR_NONE;
104         }
105         /**
106          * @cli bbr mgmt mlr listener add
107          * @code
108          * bbr mgmt mlr listener add ff04::1
109          * Done
110          * @endcode
111          * @code
112          * bbr mgmt mlr listener add ff04::2 300
113          * Done
114          * @endcode
115          * @cparam bbr mgmt mlr listener add @ca{ipaddress} [@ca{timeout-seconds}]
116          * @par api_copy
117          * #otBackboneRouterMulticastListenerAdd
118          */
119         else if (aArgs[1] == "add")
120         {
121             otIp6Address address;
122             uint32_t     timeout = 0;
123 
124             SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address));
125 
126             if (!aArgs[3].IsEmpty())
127             {
128                 SuccessOrExit(error = aArgs[3].ParseAsUint32(timeout));
129             }
130 
131             error = otBackboneRouterMulticastListenerAdd(GetInstancePtr(), &address, timeout);
132         }
133     }
134     /**
135      * @cli bbr mgmt mlr response
136      * @code
137      * bbr mgmt mlr response 2
138      * Done
139      * @endcode
140      * @cparam bbr mgmt mlr response @ca{status-code}
141      * For `status-code`, use:
142      * *    0: ST_MLR_SUCCESS
143      * *    2: ST_MLR_INVALID
144      * *    3: ST_MLR_NO_PERSISTENT
145      * *    4: ST_MLR_NO_RESOURCES
146      * *    5: ST_MLR_BBR_NOT_PRIMARY
147      * *    6: ST_MLR_GENERAL_FAILURE
148      * @par api_copy
149      * #otBackboneRouterConfigNextMulticastListenerRegistrationResponse
150      */
151     else if (aArgs[0] == "response")
152     {
153         uint8_t status;
154 
155         SuccessOrExit(error = aArgs[1].ParseAsUint8(status));
156         otBackboneRouterConfigNextMulticastListenerRegistrationResponse(GetInstancePtr(), status);
157         error = OT_ERROR_NONE;
158 
159 #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
160     }
161 
162 exit:
163     return error;
164 }
165 
166 #endif // #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
167 
Process(Arg aArgs[])168 template <> otError Bbr::Process<Cmd("mgmt")>(Arg aArgs[])
169 {
170     otError error = OT_ERROR_INVALID_COMMAND;
171 
172     if (aArgs[0].IsEmpty())
173     {
174         ExitNow(error = OT_ERROR_INVALID_COMMAND);
175     }
176 
177 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
178     /**
179      * @cli bbr mgmt dua
180      * @code
181      * bbr mgmt dua 1 2f7c235e5025a2fd
182      * Done
183      * @endcode
184      * @code
185      * bbr mgmt dua 160
186      * Done
187      * @endcode
188      * @cparam bbr mgmt dua @ca{status|coap-code} [@ca{meshLocalIid}]
189      * For `status` or `coap-code`, use:
190      * *    0: ST_DUA_SUCCESS
191      * *    1: ST_DUA_REREGISTER
192      * *    2: ST_DUA_INVALID
193      * *    3: ST_DUA_DUPLICATE
194      * *    4: ST_DUA_NO_RESOURCES
195      * *    5: ST_DUA_BBR_NOT_PRIMARY
196      * *    6: ST_DUA_GENERAL_FAILURE
197      * *    160: COAP code 5.00
198      * @par
199      * With the `meshLocalIid` included, this command configures the response status
200      * for the next DUA registration. Without `meshLocalIid`, respond to the next
201      * DUA.req with the specified `status` or `coap-code`.
202      * @par
203      * Available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
204      * @sa otBackboneRouterConfigNextDuaRegistrationResponse
205      */
206     if (aArgs[0] == "dua")
207     {
208         uint8_t                   status;
209         otIp6InterfaceIdentifier *mlIid = nullptr;
210         otIp6InterfaceIdentifier  iid;
211 
212         SuccessOrExit(error = aArgs[1].ParseAsUint8(status));
213 
214         if (!aArgs[2].IsEmpty())
215         {
216             SuccessOrExit(error = aArgs[2].ParseAsHexString(iid.mFields.m8));
217             mlIid = &iid;
218             VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
219         }
220 
221         otBackboneRouterConfigNextDuaRegistrationResponse(GetInstancePtr(), mlIid, status);
222         ExitNow();
223     }
224 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
225 
226 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
227     if (aArgs[0] == "mlr")
228     {
229         error = Process<Cmd("mlr")>(aArgs + 1);
230         ExitNow();
231     }
232 #endif
233 
234 exit:
235     return error;
236 }
237 
238 /**
239  * @cli bbr enable
240  * @code
241  * bbr enable
242  * Done
243  * @endcode
244  * @par api_copy
245  * #otBackboneRouterSetEnabled
246  */
Process(Arg aArgs[])247 template <> otError Bbr::Process<Cmd("enable")>(Arg aArgs[])
248 {
249     OT_UNUSED_VARIABLE(aArgs);
250     otBackboneRouterSetEnabled(GetInstancePtr(), true);
251 
252     return OT_ERROR_NONE;
253 }
254 
255 /**
256  * @cli bbr disable
257  * @code
258  * bbr disable
259  * Done
260  * @endcode
261  * @par api_copy
262  * #otBackboneRouterSetEnabled
263  */
Process(Arg aArgs[])264 template <> otError Bbr::Process<Cmd("disable")>(Arg aArgs[])
265 {
266     OT_UNUSED_VARIABLE(aArgs);
267     otBackboneRouterSetEnabled(GetInstancePtr(), false);
268 
269     return OT_ERROR_NONE;
270 }
271 
272 /**
273  * @cli bbr jitter (get,set)
274  * @code
275  * bbr jitter
276  * 20
277  * Done
278  * @endcode
279  * @code
280  * bbr jitter 10
281  * Done
282  * @endcode
283  * @cparam bbr jitter [@ca{jitter}]
284  * @par
285  * Gets or sets jitter (in seconds) for Backbone Router registration.
286  * @par
287  * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled.
288  * @sa otBackboneRouterGetRegistrationJitter
289  * @sa otBackboneRouterSetRegistrationJitter
290  */
Process(Arg aArgs[])291 template <> otError Bbr::Process<Cmd("jitter")>(Arg aArgs[])
292 {
293     return ProcessGetSet(aArgs, otBackboneRouterGetRegistrationJitter, otBackboneRouterSetRegistrationJitter);
294 }
295 
296 /**
297  * @cli bbr register
298  * @code
299  * bbr register
300  * Done
301  * @endcode
302  * @par api_copy
303  * #otBackboneRouterRegister
304  */
Process(Arg aArgs[])305 template <> otError Bbr::Process<Cmd("register")>(Arg aArgs[])
306 {
307     OT_UNUSED_VARIABLE(aArgs);
308 
309     return otBackboneRouterRegister(GetInstancePtr());
310 }
311 
312 /**
313  * @cli bbr state
314  * @code
315  * bbr state
316  * Disabled
317  * Done
318  * @endcode
319  * @code
320  * bbr state
321  * Primary
322  * Done
323  * @endcode
324  * @code
325  * bbr state
326  * Secondary
327  * Done
328  * @endcode
329  * @par
330  * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled.
331  * @par api_copy
332  * #otBackboneRouterGetState
333  */
Process(Arg aArgs[])334 template <> otError Bbr::Process<Cmd("state")>(Arg aArgs[])
335 
336 {
337     static const char *const kStateStrings[] = {
338         "Disabled",  // (0) OT_BACKBONE_ROUTER_STATE_DISABLED
339         "Secondary", // (1) OT_BACKBONE_ROUTER_STATE_SECONDARY
340         "Primary",   // (2) OT_BACKBONE_ROUTER_STATE_PRIMARY
341     };
342 
343     static_assert(0 == OT_BACKBONE_ROUTER_STATE_DISABLED, "OT_BACKBONE_ROUTER_STATE_DISABLED value is incorrect");
344     static_assert(1 == OT_BACKBONE_ROUTER_STATE_SECONDARY, "OT_BACKBONE_ROUTER_STATE_SECONDARY value is incorrect");
345     static_assert(2 == OT_BACKBONE_ROUTER_STATE_PRIMARY, "OT_BACKBONE_ROUTER_STATE_PRIMARY value is incorrect");
346 
347     OT_UNUSED_VARIABLE(aArgs);
348 
349     OutputLine("%s", Stringify(otBackboneRouterGetState(GetInstancePtr()), kStateStrings));
350 
351     return OT_ERROR_NONE;
352 }
353 
354 /**
355  * @cli bbr config
356  * @code
357  * bbr config
358  * seqno:    10
359  * delay:    120 secs
360  * timeout:  300 secs
361  * Done
362  * @endcode
363  * @par api_copy
364  * #otBackboneRouterGetConfig
365  */
Process(Arg aArgs[])366 template <> otError Bbr::Process<Cmd("config")>(Arg aArgs[])
367 {
368     otError                error = OT_ERROR_NONE;
369     otBackboneRouterConfig config;
370 
371     otBackboneRouterGetConfig(GetInstancePtr(), &config);
372 
373     if (aArgs[0].IsEmpty())
374     {
375         OutputConfig(config);
376     }
377     else
378     {
379         // Set local Backbone Router configuration.
380         /**
381          * @cli bbr config (set)
382          * @code
383          * bbr config seqno 20 delay 30
384          * Done
385          * @endcode
386          * @cparam bbr config [seqno @ca{seqno}] [delay @ca{delay}] [timeout @ca{timeout}]
387          * @par
388          * `bbr register` should be issued explicitly to register Backbone Router service to Leader
389          * for Secondary Backbone Router.
390          * @par api_copy
391          * #otBackboneRouterSetConfig
392          */
393         for (Arg *arg = &aArgs[0]; !arg->IsEmpty(); arg++)
394         {
395             if (*arg == "seqno")
396             {
397                 arg++;
398                 SuccessOrExit(error = arg->ParseAsUint8(config.mSequenceNumber));
399             }
400             else if (*arg == "delay")
401             {
402                 arg++;
403                 SuccessOrExit(error = arg->ParseAsUint16(config.mReregistrationDelay));
404             }
405             else if (*arg == "timeout")
406             {
407                 arg++;
408                 SuccessOrExit(error = arg->ParseAsUint32(config.mMlrTimeout));
409             }
410             else
411             {
412                 ExitNow(error = OT_ERROR_INVALID_ARGS);
413             }
414         }
415 
416         error = otBackboneRouterSetConfig(GetInstancePtr(), &config);
417     }
418 
419 exit:
420     return error;
421 }
422 
423 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
424 
Process(Arg aArgs[])425 otError Bbr::Process(Arg aArgs[])
426 {
427 #define CmdEntry(aCommandString) {aCommandString, &Bbr::Process<Cmd(aCommandString)>}
428 
429     otError error = OT_ERROR_INVALID_COMMAND;
430 
431     /**
432      * @cli bbr
433      * @code
434      * bbr
435      * BBR Primary:
436      * server16: 0xE400
437      * seqno:    10
438      * delay:    120 secs
439      * timeout:  300 secs
440      * Done
441      * @endcode
442      * @code
443      * bbr
444      * BBR Primary: None
445      * Done
446      * @endcode
447      * @par
448      * Returns the current Primary Backbone Router information for the Thread device.
449      */
450     if (aArgs[0].IsEmpty())
451     {
452         otBackboneRouterConfig config;
453 
454         OutputFormat("BBR Primary:");
455 
456         if (otBackboneRouterGetPrimary(GetInstancePtr(), &config) == OT_ERROR_NONE)
457         {
458             OutputNewLine();
459             OutputLine("server16: 0x%04X", config.mServer16);
460             OutputConfig(config);
461         }
462         else
463         {
464             OutputLine(" None");
465         }
466 
467         error = OT_ERROR_NONE;
468         ExitNow();
469     }
470 
471 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
472     {
473         static constexpr Command kCommands[] = {
474             CmdEntry("config"), CmdEntry("disable"),  CmdEntry("enable"), CmdEntry("jitter"),
475             CmdEntry("mgmt"),   CmdEntry("register"), CmdEntry("state"),
476         };
477 
478 #undef CmdEntry
479 
480         static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
481 
482         const Command *command;
483 
484         command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
485         VerifyOrExit(command != nullptr);
486 
487         error = (this->*command->mHandler)(aArgs + 1);
488     }
489 #endif // #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
490 
491 exit:
492     return error;
493 }
494 
495 } // namespace Cli
496 } // namespace ot
497 
498 #endif // #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
499