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 Interpreter::GetInterpreter().ProcessGetSet(aArgs, otBackboneRouterGetRegistrationJitter,
294                                                        otBackboneRouterSetRegistrationJitter);
295 }
296 
297 /**
298  * @cli bbr register
299  * @code
300  * bbr register
301  * Done
302  * @endcode
303  * @par api_copy
304  * #otBackboneRouterRegister
305  */
Process(Arg aArgs[])306 template <> otError Bbr::Process<Cmd("register")>(Arg aArgs[])
307 {
308     OT_UNUSED_VARIABLE(aArgs);
309 
310     return otBackboneRouterRegister(GetInstancePtr());
311 }
312 
313 /**
314  * @cli bbr state
315  * @code
316  * bbr state
317  * Disabled
318  * Done
319  * @endcode
320  * @code
321  * bbr state
322  * Primary
323  * Done
324  * @endcode
325  * @code
326  * bbr state
327  * Secondary
328  * Done
329  * @endcode
330  * @par
331  * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled.
332  * @par api_copy
333  * #otBackboneRouterGetState
334  */
Process(Arg aArgs[])335 template <> otError Bbr::Process<Cmd("state")>(Arg aArgs[])
336 
337 {
338     static const char *const kStateStrings[] = {
339         "Disabled",  // (0) OT_BACKBONE_ROUTER_STATE_DISABLED
340         "Secondary", // (1) OT_BACKBONE_ROUTER_STATE_SECONDARY
341         "Primary",   // (2) OT_BACKBONE_ROUTER_STATE_PRIMARY
342     };
343 
344     static_assert(0 == OT_BACKBONE_ROUTER_STATE_DISABLED, "OT_BACKBONE_ROUTER_STATE_DISABLED value is incorrect");
345     static_assert(1 == OT_BACKBONE_ROUTER_STATE_SECONDARY, "OT_BACKBONE_ROUTER_STATE_SECONDARY value is incorrect");
346     static_assert(2 == OT_BACKBONE_ROUTER_STATE_PRIMARY, "OT_BACKBONE_ROUTER_STATE_PRIMARY value is incorrect");
347 
348     OT_UNUSED_VARIABLE(aArgs);
349 
350     OutputLine("%s", Stringify(otBackboneRouterGetState(GetInstancePtr()), kStateStrings));
351 
352     return OT_ERROR_NONE;
353 }
354 
355 /**
356  * @cli bbr config
357  * @code
358  * bbr config
359  * seqno:    10
360  * delay:    120 secs
361  * timeout:  300 secs
362  * Done
363  * @endcode
364  * @par api_copy
365  * #otBackboneRouterGetConfig
366  */
Process(Arg aArgs[])367 template <> otError Bbr::Process<Cmd("config")>(Arg aArgs[])
368 {
369     otError                error = OT_ERROR_NONE;
370     otBackboneRouterConfig config;
371 
372     otBackboneRouterGetConfig(GetInstancePtr(), &config);
373 
374     if (aArgs[0].IsEmpty())
375     {
376         OutputConfig(config);
377     }
378     else
379     {
380         // Set local Backbone Router configuration.
381         /**
382          * @cli bbr config (set)
383          * @code
384          * bbr config seqno 20 delay 30
385          * Done
386          * @endcode
387          * @cparam bbr config [seqno @ca{seqno}] [delay @ca{delay}] [timeout @ca{timeout}]
388          * @par
389          * `bbr register` should be issued explicitly to register Backbone Router service to Leader
390          * for Secondary Backbone Router.
391          * @par api_copy
392          * #otBackboneRouterSetConfig
393          */
394         for (Arg *arg = &aArgs[0]; !arg->IsEmpty(); arg++)
395         {
396             if (*arg == "seqno")
397             {
398                 arg++;
399                 SuccessOrExit(error = arg->ParseAsUint8(config.mSequenceNumber));
400             }
401             else if (*arg == "delay")
402             {
403                 arg++;
404                 SuccessOrExit(error = arg->ParseAsUint16(config.mReregistrationDelay));
405             }
406             else if (*arg == "timeout")
407             {
408                 arg++;
409                 SuccessOrExit(error = arg->ParseAsUint32(config.mMlrTimeout));
410             }
411             else
412             {
413                 ExitNow(error = OT_ERROR_INVALID_ARGS);
414             }
415         }
416 
417         error = otBackboneRouterSetConfig(GetInstancePtr(), &config);
418     }
419 
420 exit:
421     return error;
422 }
423 
424 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
425 
Process(Arg aArgs[])426 otError Bbr::Process(Arg aArgs[])
427 {
428 #define CmdEntry(aCommandString) {aCommandString, &Bbr::Process<Cmd(aCommandString)>}
429 
430     otError error = OT_ERROR_INVALID_COMMAND;
431 
432     /**
433      * @cli bbr
434      * @code
435      * bbr
436      * BBR Primary:
437      * server16: 0xE400
438      * seqno:    10
439      * delay:    120 secs
440      * timeout:  300 secs
441      * Done
442      * @endcode
443      * @code
444      * bbr
445      * BBR Primary: None
446      * Done
447      * @endcode
448      * @par
449      * Returns the current Primary Backbone Router information for the Thread device.
450      */
451     if (aArgs[0].IsEmpty())
452     {
453         otBackboneRouterConfig config;
454 
455         OutputFormat("BBR Primary:");
456 
457         if (otBackboneRouterGetPrimary(GetInstancePtr(), &config) == OT_ERROR_NONE)
458         {
459             OutputNewLine();
460             OutputLine("server16: 0x%04X", config.mServer16);
461             OutputConfig(config);
462         }
463         else
464         {
465             OutputLine(" None");
466         }
467 
468         error = OT_ERROR_NONE;
469         ExitNow();
470     }
471 
472 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
473     {
474         static constexpr Command kCommands[] = {
475             CmdEntry("config"), CmdEntry("disable"),  CmdEntry("enable"), CmdEntry("jitter"),
476             CmdEntry("mgmt"),   CmdEntry("register"), CmdEntry("state"),
477         };
478 
479 #undef CmdEntry
480 
481         static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
482 
483         const Command *command;
484 
485         command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
486         VerifyOrExit(command != nullptr);
487 
488         error = (this->*command->mHandler)(aArgs + 1);
489     }
490 #endif // #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
491 
492 exit:
493     return error;
494 }
495 
496 } // namespace Cli
497 } // namespace ot
498 
499 #endif // #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
500