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