1 /*
2  *  Copyright (c) 2020, 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 commands for Network Data.
32  */
33 
34 #include "cli_network_data.hpp"
35 
36 #include <openthread/border_router.h>
37 #include <openthread/netdata_publisher.h>
38 #include <openthread/server.h>
39 
40 #include "cli/cli.hpp"
41 #include "common/encoding.hpp"
42 
43 namespace ot {
44 namespace Cli {
45 
46 constexpr NetworkData::Command NetworkData::sCommands[];
47 
NetworkData(Interpreter & aInterpreter)48 NetworkData::NetworkData(Interpreter &aInterpreter)
49     : mInterpreter(aInterpreter)
50 {
51 }
52 
OutputPrefix(const otBorderRouterConfig & aConfig)53 void NetworkData::OutputPrefix(const otBorderRouterConfig &aConfig)
54 {
55     enum
56     {
57         // BorderRouter flag is `uint16_t` (though some of the bits are
58         // reserved for future use), so we use 17 chars string (16 flags
59         // plus null char at end of string).
60         kMaxFlagStringSize = 17,
61     };
62 
63     char  flagsString[kMaxFlagStringSize];
64     char *flagsPtr = &flagsString[0];
65 
66     mInterpreter.OutputIp6Prefix(aConfig.mPrefix);
67 
68     if (aConfig.mPreferred)
69     {
70         *flagsPtr++ = 'p';
71     }
72 
73     if (aConfig.mSlaac)
74     {
75         *flagsPtr++ = 'a';
76     }
77 
78     if (aConfig.mDhcp)
79     {
80         *flagsPtr++ = 'd';
81     }
82 
83     if (aConfig.mConfigure)
84     {
85         *flagsPtr++ = 'c';
86     }
87 
88     if (aConfig.mDefaultRoute)
89     {
90         *flagsPtr++ = 'r';
91     }
92 
93     if (aConfig.mOnMesh)
94     {
95         *flagsPtr++ = 'o';
96     }
97 
98     if (aConfig.mStable)
99     {
100         *flagsPtr++ = 's';
101     }
102 
103     if (aConfig.mNdDns)
104     {
105         *flagsPtr++ = 'n';
106     }
107 
108     if (aConfig.mDp)
109     {
110         *flagsPtr++ = 'D';
111     }
112 
113     *flagsPtr = '\0';
114 
115     if (flagsPtr != &flagsString[0])
116     {
117         mInterpreter.OutputFormat(" %s", flagsString);
118     }
119 
120     OutputPreference(aConfig.mPreference);
121 
122     mInterpreter.OutputLine(" %04x", aConfig.mRloc16);
123 }
124 
OutputRoute(const otExternalRouteConfig & aConfig)125 void NetworkData::OutputRoute(const otExternalRouteConfig &aConfig)
126 {
127     enum
128     {
129         // ExternalRoute flag is `uint8_t` (though some of the bits are
130         // reserved for future use), so we use 9 chars string (8 flags
131         // plus null char at end of string).
132         kMaxFlagStringSize = 9,
133     };
134 
135     char  flagsString[kMaxFlagStringSize];
136     char *flagsPtr = &flagsString[0];
137 
138     mInterpreter.OutputIp6Prefix(aConfig.mPrefix);
139 
140     if (aConfig.mStable)
141     {
142         *flagsPtr++ = 's';
143     }
144 
145     if (aConfig.mNat64)
146     {
147         *flagsPtr++ = 'n';
148     }
149 
150     *flagsPtr = '\0';
151 
152     if (flagsPtr != &flagsString[0])
153     {
154         mInterpreter.OutputFormat(" %s", flagsString);
155     }
156 
157     OutputPreference(aConfig.mPreference);
158 
159     mInterpreter.OutputLine(" %04x", aConfig.mRloc16);
160 }
161 
OutputPreference(signed int aPreference)162 void NetworkData::OutputPreference(signed int aPreference)
163 {
164     switch (aPreference)
165     {
166     case OT_ROUTE_PREFERENCE_LOW:
167         mInterpreter.OutputFormat(" low");
168         break;
169 
170     case OT_ROUTE_PREFERENCE_MED:
171         mInterpreter.OutputFormat(" med");
172         break;
173 
174     case OT_ROUTE_PREFERENCE_HIGH:
175         mInterpreter.OutputFormat(" high");
176         break;
177 
178     default:
179         break;
180     }
181 }
182 
OutputService(const otServiceConfig & aConfig)183 void NetworkData::OutputService(const otServiceConfig &aConfig)
184 {
185     mInterpreter.OutputFormat("%u ", aConfig.mEnterpriseNumber);
186     mInterpreter.OutputBytes(aConfig.mServiceData, aConfig.mServiceDataLength);
187     mInterpreter.OutputFormat(" ");
188     mInterpreter.OutputBytes(aConfig.mServerConfig.mServerData, aConfig.mServerConfig.mServerDataLength);
189 
190     if (aConfig.mServerConfig.mStable)
191     {
192         mInterpreter.OutputFormat(" s");
193     }
194 
195     mInterpreter.OutputLine(" %04x", aConfig.mServerConfig.mRloc16);
196 }
197 
ProcessHelp(Arg aArgs[])198 otError NetworkData::ProcessHelp(Arg aArgs[])
199 {
200     OT_UNUSED_VARIABLE(aArgs);
201 
202     for (const Command &command : sCommands)
203     {
204         mInterpreter.OutputLine(command.mName);
205     }
206 
207     return OT_ERROR_NONE;
208 }
209 
210 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
ProcessPublish(Arg aArgs[])211 otError NetworkData::ProcessPublish(Arg aArgs[])
212 {
213     otError error = OT_ERROR_NONE;
214 
215 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
216     if (aArgs[0] == "dnssrp")
217     {
218         if (aArgs[1] == "anycast")
219         {
220             uint8_t sequenceNumber;
221 
222             SuccessOrExit(error = aArgs[2].ParseAsUint8(sequenceNumber));
223             otNetDataPublishDnsSrpServiceAnycast(mInterpreter.mInstance, sequenceNumber);
224             ExitNow();
225         }
226 
227         if (aArgs[1] == "unicast")
228         {
229             otIp6Address address;
230             uint16_t     port;
231 
232             if (aArgs[3].IsEmpty())
233             {
234                 SuccessOrExit(error = aArgs[2].ParseAsUint16(port));
235                 otNetDataPublishDnsSrpServiceUnicastMeshLocalEid(mInterpreter.mInstance, port);
236                 ExitNow();
237             }
238 
239             SuccessOrExit(error = aArgs[2].ParseAsIp6Address(address));
240             SuccessOrExit(error = aArgs[3].ParseAsUint16(port));
241             otNetDataPublishDnsSrpServiceUnicast(mInterpreter.mInstance, &address, port);
242             ExitNow();
243         }
244     }
245 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
246 
247 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
248     if (aArgs[0] == "prefix")
249     {
250         otBorderRouterConfig config;
251 
252         SuccessOrExit(error = Interpreter::ParsePrefix(aArgs + 1, config));
253         error = otNetDataPublishOnMeshPrefix(mInterpreter.mInstance, &config);
254         ExitNow();
255     }
256 
257     if (aArgs[0] == "route")
258     {
259         otExternalRouteConfig config;
260 
261         SuccessOrExit(error = Interpreter::ParseRoute(aArgs + 1, config));
262         error = otNetDataPublishExternalRoute(mInterpreter.mInstance, &config);
263         ExitNow();
264     }
265 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
266 
267     error = OT_ERROR_INVALID_ARGS;
268 
269 exit:
270     return error;
271 }
272 
ProcessUnpublish(Arg aArgs[])273 otError NetworkData::ProcessUnpublish(Arg aArgs[])
274 {
275     otError error = OT_ERROR_NONE;
276 
277 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
278     if (aArgs[0] == "dnssrp")
279     {
280         otNetDataUnpublishDnsSrpService(mInterpreter.mInstance);
281         ExitNow();
282     }
283 #endif
284 
285 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
286     {
287         otIp6Prefix prefix;
288 
289         if (aArgs[0].ParseAsIp6Prefix(prefix) == OT_ERROR_NONE)
290         {
291             error = otNetDataUnpublishPrefix(mInterpreter.mInstance, &prefix);
292             ExitNow();
293         }
294     }
295 #endif
296 
297     error = OT_ERROR_INVALID_ARGS;
298 
299 exit:
300     return error;
301 }
302 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
303 
304 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
ProcessRegister(Arg aArgs[])305 otError NetworkData::ProcessRegister(Arg aArgs[])
306 {
307     OT_UNUSED_VARIABLE(aArgs);
308 
309     otError error = OT_ERROR_NONE;
310 
311 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
312     SuccessOrExit(error = otBorderRouterRegister(mInterpreter.mInstance));
313 #else
314     SuccessOrExit(error = otServerRegister(mInterpreter.mInstance));
315 #endif
316 
317 exit:
318     return error;
319 }
320 #endif
321 
ProcessSteeringData(Arg aArgs[])322 otError NetworkData::ProcessSteeringData(Arg aArgs[])
323 {
324     otError           error;
325     otExtAddress      addr;
326     otJoinerDiscerner discerner;
327 
328     VerifyOrExit(aArgs[0] == "check", error = OT_ERROR_INVALID_ARGS);
329 
330     error = Interpreter::ParseJoinerDiscerner(aArgs[1], discerner);
331 
332     if (error == OT_ERROR_NOT_FOUND)
333     {
334         discerner.mLength = 0;
335         error             = aArgs[1].ParseAsHexString(addr.m8);
336     }
337 
338     SuccessOrExit(error);
339 
340     if (discerner.mLength)
341     {
342         error = otNetDataSteeringDataCheckJoinerWithDiscerner(mInterpreter.mInstance, &discerner);
343     }
344     else
345     {
346         error = otNetDataSteeringDataCheckJoiner(mInterpreter.mInstance, &addr);
347     }
348 
349 exit:
350     return error;
351 }
352 
OutputPrefixes(void)353 void NetworkData::OutputPrefixes(void)
354 {
355     otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
356     otBorderRouterConfig  config;
357 
358     mInterpreter.OutputLine("Prefixes:");
359 
360     while (otNetDataGetNextOnMeshPrefix(mInterpreter.mInstance, &iterator, &config) == OT_ERROR_NONE)
361     {
362         OutputPrefix(config);
363     }
364 }
365 
OutputRoutes(void)366 void NetworkData::OutputRoutes(void)
367 {
368     otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
369     otExternalRouteConfig config;
370 
371     mInterpreter.OutputLine("Routes:");
372 
373     while (otNetDataGetNextRoute(mInterpreter.mInstance, &iterator, &config) == OT_ERROR_NONE)
374     {
375         OutputRoute(config);
376     }
377 }
378 
OutputServices(void)379 void NetworkData::OutputServices(void)
380 {
381     otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
382     otServiceConfig       config;
383 
384     mInterpreter.OutputLine("Services:");
385 
386     while (otNetDataGetNextService(mInterpreter.mInstance, &iterator, &config) == OT_ERROR_NONE)
387     {
388         OutputService(config);
389     }
390 }
391 
OutputBinary(void)392 otError NetworkData::OutputBinary(void)
393 {
394     otError error = OT_ERROR_NONE;
395     uint8_t data[255];
396     uint8_t len = sizeof(data);
397 
398     SuccessOrExit(error = otNetDataGet(mInterpreter.mInstance, false, data, &len));
399 
400     mInterpreter.OutputBytes(data, static_cast<uint8_t>(len));
401     mInterpreter.OutputLine("");
402 
403 exit:
404     return error;
405 }
406 
ProcessShow(Arg aArgs[])407 otError NetworkData::ProcessShow(Arg aArgs[])
408 {
409     otError error = OT_ERROR_INVALID_ARGS;
410 
411     if (aArgs[0].IsEmpty())
412     {
413         OutputPrefixes();
414         OutputRoutes();
415         OutputServices();
416         error = OT_ERROR_NONE;
417     }
418     else if (aArgs[0] == "-x")
419     {
420         error = OutputBinary();
421     }
422 
423     return error;
424 }
425 
Process(Arg aArgs[])426 otError NetworkData::Process(Arg aArgs[])
427 {
428     otError        error = OT_ERROR_INVALID_COMMAND;
429     const Command *command;
430 
431     if (aArgs[0].IsEmpty())
432     {
433         IgnoreError(ProcessHelp(aArgs));
434         ExitNow();
435     }
436 
437     command = Utils::LookupTable::Find(aArgs[0].GetCString(), sCommands);
438     VerifyOrExit(command != nullptr);
439 
440     error = (this->*command->mHandler)(aArgs + 1);
441 
442 exit:
443     return error;
444 }
445 
446 } // namespace Cli
447 } // namespace ot
448