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 MAC Filter.
32  */
33 
34 #include "cli_mac_filter.hpp"
35 
36 #include "cli/cli.hpp"
37 
38 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
39 
40 namespace ot {
41 namespace Cli {
42 
OutputFilter(uint8_t aFilters)43 void MacFilter::OutputFilter(uint8_t aFilters)
44 {
45     otMacFilterEntry    entry;
46     otMacFilterIterator iterator;
47 
48     if (aFilters & kAddressFilter)
49     {
50         if ((aFilters & ~kAddressFilter) != 0)
51         {
52             OutputFormat("Address Mode: ");
53         }
54 
55         OutputLine("%s", AddressModeToString(otLinkFilterGetAddressMode(GetInstancePtr())));
56 
57         iterator = OT_MAC_FILTER_ITERATOR_INIT;
58 
59         while (otLinkFilterGetNextAddress(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
60         {
61             OutputEntry(entry);
62         }
63     }
64 
65     if (aFilters & kRssFilter)
66     {
67         if ((aFilters & ~kRssFilter) != 0)
68         {
69             OutputLine("RssIn List:");
70         }
71 
72         iterator = OT_MAC_FILTER_ITERATOR_INIT;
73 
74         while (otLinkFilterGetNextRssIn(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE)
75         {
76             if (IsDefaultRss(entry.mExtAddress))
77             {
78                 OutputLine("Default rss: %d (lqi %u)", entry.mRssIn,
79                            otLinkConvertRssToLinkQuality(GetInstancePtr(), entry.mRssIn));
80             }
81             else
82             {
83                 OutputEntry(entry);
84             }
85         }
86     }
87 }
88 
IsDefaultRss(const otExtAddress & aExtAddress)89 bool MacFilter::IsDefaultRss(const otExtAddress &aExtAddress)
90 {
91     // In default RSS entry, the extended address will be all `0xff`.
92 
93     bool isDefault = true;
94 
95     for (uint8_t byte : aExtAddress.m8)
96     {
97         if (byte != 0xff)
98         {
99             isDefault = false;
100             break;
101         }
102     }
103 
104     return isDefault;
105 }
106 
AddressModeToString(otMacFilterAddressMode aMode)107 const char *MacFilter::AddressModeToString(otMacFilterAddressMode aMode)
108 {
109     static const char *const kModeStrings[] = {
110         "Disabled",  // (0) OT_MAC_FILTER_ADDRESS_MODE_DISABLED
111         "Allowlist", // (1) OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST
112         "Denylist",  // (2) OT_MAC_FILTER_ADDRESS_MODE_DENYLIST
113     };
114 
115     static_assert(0 == OT_MAC_FILTER_ADDRESS_MODE_DISABLED, "OT_MAC_FILTER_ADDRESS_MODE_DISABLED value is incorrect");
116     static_assert(1 == OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST, "OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST value is incorrect");
117     static_assert(2 == OT_MAC_FILTER_ADDRESS_MODE_DENYLIST, "OT_MAC_FILTER_ADDRESS_MODE_DENYLIST value is incorrect");
118 
119     return Stringify(aMode, kModeStrings);
120 }
121 
OutputEntry(const otMacFilterEntry & aEntry)122 void MacFilter::OutputEntry(const otMacFilterEntry &aEntry)
123 {
124     OutputExtAddress(aEntry.mExtAddress);
125 
126     if (aEntry.mRssIn != OT_MAC_FILTER_FIXED_RSS_DISABLED)
127     {
128         OutputFormat(" : rss %d (lqi %d)", aEntry.mRssIn,
129                      otLinkConvertRssToLinkQuality(GetInstancePtr(), aEntry.mRssIn));
130     }
131 
132     OutputNewLine();
133 }
134 
Process(Arg aArgs[])135 template <> otError MacFilter::Process<Cmd("addr")>(Arg aArgs[])
136 {
137     otError      error = OT_ERROR_NONE;
138     otExtAddress extAddr;
139 
140     /**
141      * @cli macfilter addr
142      * @code
143      * macfilter addr
144      * Allowlist
145      * 0f6127e33af6b403 : rss -95 (lqi 1)
146      * 0f6127e33af6b402
147      * Done
148      * @endcode
149      * @par
150      * Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.
151      * @par
152      * Provides the following information:
153      * - Current mode of the MAC filter list: Either `AllowList`, `DenyList,` or `Disabled`
154      * - A list of all the extended addresses in the filter. The received signal strength (rss) and
155      *   link quality indicator (lqi) are listed next to the address if these values have been set to be
156      *   different from the default values.
157      * @sa otLinkFilterGetAddressMode
158      */
159     if (aArgs[0].IsEmpty())
160     {
161         OutputFilter(kAddressFilter);
162     }
163     /**
164      * @cli macfilter addr add
165      * @code
166      * macfilter addr add 0f6127e33af6b403 -95
167      * Done
168      * @endcode
169      * @code
170      * macfilter addr add 0f6127e33af6b402
171      * Done
172      * @endcode
173      * @cparam macfilter addr add @ca{extaddr} [@ca{rss}]
174      * @par
175      * Is available only when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.
176      * @par
177      * Adds an IEEE 802.15.4 Extended Address to the MAC filter list.
178      * If you specify the optional `rss` argument, this fixes the received signal strength for messages from the
179      * address. If you do not use the `rss` option, the address will use whatever default value you have set.
180      * If you have not set a default, the signal strength will be the over-air signal.
181      * @sa otLinkFilterAddAddress
182      */
183     else if (aArgs[0] == "add")
184     {
185         SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
186         error = otLinkFilterAddAddress(GetInstancePtr(), &extAddr);
187 
188         VerifyOrExit(error == OT_ERROR_NONE || error == OT_ERROR_ALREADY);
189 
190         if (!aArgs[2].IsEmpty())
191         {
192             int8_t rss;
193 
194             SuccessOrExit(error = aArgs[2].ParseAsInt8(rss));
195             SuccessOrExit(error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss));
196         }
197     }
198     /**
199      * @cli macfilter addr remove
200      * @code
201      * macfilter addr remove 0f6127e33af6b402
202      * Done
203      * @endcode
204      * @cparam macfilter addr remove @ca{extaddr}
205      * @par
206      * Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.
207      * @par
208      * This command removes the specified extended address from the MAC filter list.
209      * @note No action is performed if the specified extended address does not match an entry in the MAC filter list.
210      * @sa otLinkFilterRemoveAddress
211      */
212     else if (aArgs[0] == "remove")
213     {
214         SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
215         otLinkFilterRemoveAddress(GetInstancePtr(), &extAddr);
216     }
217     /**
218      * @cli macfilter addr clear
219      * @code
220      * macfilter addr clear
221      * Done
222      * @endcode
223      * @par
224      * Is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.
225      * @par
226      * This command clears all the extended addresses from the MAC filter list.
227      * @note This command does not affect entries in the `RssIn` list. That list contains extended addresses where the
228      * `rss` has been set to a fixed value that differs from the default.
229      * @sa otLinkFilterClearAddresses
230      */
231     else if (aArgs[0] == "clear")
232     {
233         otLinkFilterClearAddresses(GetInstancePtr());
234     }
235     else
236     {
237         static const char *const kModeCommands[] = {
238             /**
239              * @cli macfilter addr disable
240              * @code
241              * macfilter addr disable
242              * Done
243              * @endcode
244              * @par
245              * Disables MAC filter modes.
246              */
247             "disable", // (0) OT_MAC_FILTER_ADDRESS_MODE_DISABLED
248             /**
249              * @cli macfilter addr allowlist
250              * @code
251              * macfilter addr allowlist
252              * Done
253              * @endcode
254              * @par
255              * Enables the `allowlist` MAC filter mode, which means that only the MAC addresses in the MAC filter list
256              * will be allowed access.
257              * @sa otLinkFilterSetAddressMode
258              */
259             "allowlist", // (1) OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST
260             /**
261              * @cli macfilter addr denylist
262              * @code
263              * macfilter addr denylist
264              * Done
265              * @endcode
266              * @par
267              * Enables the `denylist` MAC filter mode, which means that all MAC addresses in the MAC filter list
268              * will be denied access.
269              * @sa otLinkFilterSetAddressMode
270              */
271             "denylist", // (2) OT_MAC_FILTER_ADDRESS_MODE_DENYLIST
272         };
273 
274         for (size_t index = 0; index < OT_ARRAY_LENGTH(kModeCommands); index++)
275         {
276             if (aArgs[0] == kModeCommands[index])
277             {
278                 VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
279                 otLinkFilterSetAddressMode(GetInstancePtr(), static_cast<otMacFilterAddressMode>(index));
280                 ExitNow();
281             }
282         }
283 
284         error = OT_ERROR_INVALID_COMMAND;
285     }
286 
287 exit:
288     return error;
289 }
290 
291 /**
292  * @cli macfilter rss
293  * @code
294  * macfilter rss
295  * 0f6127e33af6b403 : rss -95 (lqi 1)
296  * Default rss: -50 (lqi 3)
297  * Done
298  * @endcode
299  * @par
300  * Provides the following information:
301  * - Listing of all the extended addresses
302  * where the received signal strength (`rss`) has been set to be different from
303  * the default value. The link quality indicator (`lqi`) is also shown. The `rss`
304  * and `lqi` settings map to each other. If you set one, the value of the other
305  * gets set automatically. This list of addresses is called the `RssIn List`.
306  * Setting either the `rsi` or the `lqi` adds the corresponding extended address
307  * to the `RssIn` list.
308  * - `Default rss`: Shows the default values, if applicable, for the `rss` and `lqi` settings.
309  * @sa otLinkFilterGetNextRssIn
310  */
Process(Arg aArgs[])311 template <> otError MacFilter::Process<Cmd("rss")>(Arg aArgs[])
312 {
313     otError      error = OT_ERROR_NONE;
314     otExtAddress extAddr;
315     int8_t       rss;
316 
317     if (aArgs[0].IsEmpty())
318     {
319         OutputFilter(kRssFilter);
320     }
321     /**
322      * @cli macfilter rss add-lqi
323      * @code
324      * macfilter rss add-lqi * 3
325      * Done
326      * @endcode
327      * @code
328      * macfilter rss add-lqi 0f6127e33af6b404 2
329      * Done
330      * @endcode
331      * @cparam macfilter rss add-lqi @ca{extaddr} @ca{lqi}
332      * To set a default value for the link quality indicator for all received messages,
333      * use the `*` for the `extaddr` argument. The allowed range is 0 to 3.
334      * @par
335      * Adds the specified Extended Address to the `RssIn` list (or modifies an existing address in the `RssIn` list)
336      * and sets the fixed link quality indicator for messages from that address.
337      * The Extended Address
338      * does not necessarily have to be in the `address allowlist/denylist` filter to set the `lqi`.
339      * @note The `RssIn` list contains Extended Addresses whose `lqi` or
340      * received signal strength (`rss`) values have been set to be different from the defaults.
341      * The `lqi` will automatically get converted to a corresponding `rss` value.
342      * @par
343      * This is available when `OPENTHREAD_CONFIG_MAC_FILTER_ENABLE` configuration is enabled.
344      * @sa otLinkConvertLinkQualityToRss
345      * @sa otLinkFilterSetDefaultRssIn
346      */
347     else if (aArgs[0] == "add-lqi")
348     {
349         uint8_t linkQuality;
350 
351         SuccessOrExit(error = aArgs[2].ParseAsUint8(linkQuality));
352         VerifyOrExit(linkQuality <= 3, error = OT_ERROR_INVALID_ARGS);
353         rss = otLinkConvertLinkQualityToRss(GetInstancePtr(), linkQuality);
354 
355         if (aArgs[1] == "*")
356         {
357             otLinkFilterSetDefaultRssIn(GetInstancePtr(), rss);
358         }
359         else
360         {
361             SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
362             error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss);
363         }
364     }
365     /**
366      * @cli macfilter rss add
367      * @code
368      * macfilter rss add * -50
369      * Done
370      * @endcode
371      * @code
372      * macfilter rss add 0f6127e33af6b404 -85
373      * Done
374      * @endcode
375      * @cparam macfilter rss add @ca{extaddr} @ca{rss}
376      * To set a default value for the received signal strength for all received messages,
377      * use the `*` for the `extaddr` argument.
378      * @par api_copy
379      * #otLinkFilterAddRssIn
380      */
381     else if (aArgs[0] == "add")
382     {
383         SuccessOrExit(error = aArgs[2].ParseAsInt8(rss));
384 
385         if (aArgs[1] == "*")
386         {
387             otLinkFilterSetDefaultRssIn(GetInstancePtr(), rss);
388         }
389         else
390         {
391             SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
392             error = otLinkFilterAddRssIn(GetInstancePtr(), &extAddr, rss);
393         }
394     }
395     /**
396      * @cli macfilter rss remove
397      * @code
398      * macfilter rss remove *
399      * Done
400      * @endcode
401      * @code
402      * macfilter rss remove 0f6127e33af6b404
403      * Done
404      * @endcode
405      * @cparam macfilter rss remove @ca{extaddr}
406      * If you wish to remove the default received signal strength and link quality indicator settings,
407      * use the `*` as the `extaddr`. This unsets the defaults but does not remove
408      * entries from the `RssIn` list.
409      * @par api_copy
410      * #otLinkFilterRemoveRssIn
411      */
412     else if (aArgs[0] == "remove")
413     {
414         if (aArgs[1] == "*")
415         {
416             otLinkFilterClearDefaultRssIn(GetInstancePtr());
417         }
418         else
419         {
420             SuccessOrExit(error = aArgs[1].ParseAsHexString(extAddr.m8));
421             otLinkFilterRemoveRssIn(GetInstancePtr(), &extAddr);
422         }
423     }
424     /**
425      * @cli macfilter rss clear
426      * @code
427      * macfilter rss clear
428      * Done
429      * @endcode
430      * @par api_copy
431      * #otLinkFilterClearAllRssIn
432      */
433     else if (aArgs[0] == "clear")
434     {
435         otLinkFilterClearAllRssIn(GetInstancePtr());
436     }
437     else
438     {
439         error = OT_ERROR_INVALID_COMMAND;
440     }
441 
442 exit:
443     return error;
444 }
445 
Process(Arg aArgs[])446 otError MacFilter::Process(Arg aArgs[])
447 {
448 #define CmdEntry(aCommandString)                                 \
449     {                                                            \
450         aCommandString, &MacFilter::Process<Cmd(aCommandString)> \
451     }
452 
453     static constexpr Command kCommands[] = {
454         CmdEntry("addr"),
455         CmdEntry("rss"),
456     };
457 
458 #undef CmdEntry
459 
460     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
461 
462     otError        error = OT_ERROR_INVALID_COMMAND;
463     const Command *command;
464 
465     /**
466      * @cli macfilter
467      * @code
468      * macfilter
469      * Address Mode: Allowlist
470      * 0f6127e33af6b403 : rss -95 (lqi 1)
471      * 0f6127e33af6b402
472      * RssIn List:
473      * 0f6127e33af6b403 : rss -95 (lqi 1)
474      * Default rss: -50 (lqi 3)
475      * Done
476      * @endcode
477      * @par
478      * Provides the following information:
479      * - `Address Mode`: Current mode of the MAC filter: Either `AllowList`, `DenyList,` or `Disabled`
480      * - A list of all the extended addresses in the MAC filter list. The received signal strength (rss) and
481      *   link quality indicator (lqi) are listed next to the address if these values have been set to be
482      *   different from the default values.
483      * - A separate list (`RssIn List`) that shows all the extended addresses where the `rss` has been set to
484      *   be different from the default value.
485      * - `Default rss`: Shows the default values, if applicable, for the `rss` and `lqi` settings.
486      * @note An extended address can be in the `RssIn` list without being in the MAC filter list.
487      * @sa otLinkFilterSetAddressMode
488      * @sa otLinkFilterGetNextAddress
489      * @sa otLinkFilterGetNextRssIn
490      */
491     if (aArgs[0].IsEmpty())
492     {
493         OutputFilter(kAddressFilter | kRssFilter);
494         ExitNow(error = OT_ERROR_NONE);
495     }
496 
497     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
498     VerifyOrExit(command != nullptr);
499 
500     error = (this->*command->mHandler)(aArgs + 1);
501 
502 exit:
503     return error;
504 }
505 
506 } // namespace Cli
507 } // namespace ot
508 
509 #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
510