From 2b67cd8ebbc087198fd17690193acd3c05d2f573 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 13 Feb 2023 08:42:36 -0800 Subject: [PATCH] [simul-platform] enhance radio `nodeidfilter` command (#8740) This commit updates the custom CLI command `nodeidfilter` defined for simulation platform enabling filtering at radio level. This commit enhances this command so it can be used as both a deny-list or an allow-list. It also adds support to output the list and filter mode when no arg is given. --- examples/apps/cli/main.c | 12 ++++- examples/platforms/simulation/radio.c | 77 +++++++++++++++++++++++---- tests/toranj/cli/cli.py | 12 +++++ 3 files changed, 88 insertions(+), 13 deletions(-) diff --git a/examples/apps/cli/main.c b/examples/apps/cli/main.c index 33fd584d0..c680628ca 100644 --- a/examples/apps/cli/main.c +++ b/examples/apps/cli/main.c @@ -76,9 +76,17 @@ static const otCliCommand kCommands[] = { #if OPENTHREAD_EXAMPLES_SIMULATION /* * The CLI command `nodeidfilter` only works for simulation in real time. + * + * It can be used either as an allow list or a deny list. Once the filter is cleared, the first `nodeidfilter allow` + * or `nodeidfilter deny` will determine whether it is set up as an allow or deny list. Subsequent calls should + * use the same sub-command to add new node IDs, e.g., if we first call `nodeidfilter allow` (which sets the filter + * up as an allow list), a subsequent `nodeidfilter deny` will result in `InvalidState` error. + * * The usage of the command `nodeidfilter`: - * - `nodeidfilter deny `: It denies the connection to a specified node. - * - `nodeidfilter clear`: It restores the filter state to default. + * - `nodeidfilter deny ` : It denies the connection to a specified node (use as deny-list). + * - `nodeidfilter allow : It allows the connection to a specified node (use as allow-list). + * - `nodeidfilter clear` : It restores the filter state to default. + * - `nodeidfilter` : Outputs filter mode (allow-list or deny-list) and filtered node IDs. */ {"nodeidfilter", ProcessNodeIdFilter}, #endif diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index c86a5e8be..9fe37b66c 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -169,39 +170,90 @@ static otRadioKeyType sKeyType; static int8_t GetRssi(uint16_t aChannel); #if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 -static uint8_t sDeniedNodeIdsBitVector[(MAX_NETWORK_SIZE + 7) / 8]; + +static enum { + kFilterOff, + kFilterDenyList, + kFilterAllowList, +} sFilterMode = kFilterOff; + +static uint8_t sFilterNodeIdsBitVector[(MAX_NETWORK_SIZE + 7) / 8]; + +static bool FilterContainsId(uint16_t aNodeId) +{ + uint16_t index = aNodeId - 1; + + return (sFilterNodeIdsBitVector[index / 8] & (0x80 >> (index % 8))) != 0; +} static bool NodeIdFilterIsConnectable(uint16_t aNodeId) { - uint16_t index = aNodeId - 1; + bool isConnectable = true; - return (sDeniedNodeIdsBitVector[index / 8] & (0x80 >> (index % 8))) == 0; + switch (sFilterMode) + { + case kFilterOff: + break; + case kFilterDenyList: + isConnectable = !FilterContainsId(aNodeId); + break; + case kFilterAllowList: + isConnectable = FilterContainsId(aNodeId); + break; + } + + return isConnectable; } -static void NodeIdFilterDeny(uint16_t aNodeId) +static void AddNodeIdToFilter(uint16_t aNodeId) { uint16_t index = aNodeId - 1; - sDeniedNodeIdsBitVector[index / 8] |= 0x80 >> (index % 8); + sFilterNodeIdsBitVector[index / 8] |= 0x80 >> (index % 8); } -static void NodeIdFilterClear(void) { memset(sDeniedNodeIdsBitVector, 0, sizeof(sDeniedNodeIdsBitVector)); } +OT_TOOL_WEAK void otCliOutputFormat(const char *aFmt, ...) { OT_UNUSED_VARIABLE(aFmt); } otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aContext); otError error = OT_ERROR_NONE; + bool deny = false; - otEXPECT_ACTION(aArgsLength > 0, error = OT_ERROR_INVALID_COMMAND); + if (aArgsLength == 0) + { + switch (sFilterMode) + { + case kFilterOff: + otCliOutputFormat("off"); + break; + case kFilterDenyList: + otCliOutputFormat("deny-list"); + break; + case kFilterAllowList: + otCliOutputFormat("allow-list"); + break; + } - if (!strcmp(aArgs[0], "clear")) + for (uint16_t nodeId = 0; nodeId <= MAX_NETWORK_SIZE; nodeId++) + { + if (FilterContainsId(nodeId)) + { + otCliOutputFormat(" %d", nodeId); + } + } + + otCliOutputFormat("\r\n"); + } + else if (!strcmp(aArgs[0], "clear")) { otEXPECT_ACTION(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); - NodeIdFilterClear(); + memset(sFilterNodeIdsBitVector, 0, sizeof(sFilterNodeIdsBitVector)); + sFilterMode = kFilterOff; } - else if (!strcmp(aArgs[0], "deny")) + else if ((deny = !strcmp(aArgs[0], "deny")) || !strcmp(aArgs[0], "allow")) { uint16_t nodeId; char *endptr; @@ -213,7 +265,10 @@ otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[]) otEXPECT_ACTION(*endptr == '\0', error = OT_ERROR_INVALID_ARGS); otEXPECT_ACTION(1 <= nodeId && nodeId <= MAX_NETWORK_SIZE, error = OT_ERROR_INVALID_ARGS); - NodeIdFilterDeny(nodeId); + otEXPECT_ACTION(sFilterMode != (deny ? kFilterAllowList : kFilterDenyList), error = OT_ERROR_INVALID_STATE); + + AddNodeIdToFilter(nodeId); + sFilterMode = deny ? kFilterDenyList : kFilterAllowList; } else { diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index e47eb648b..ea89bc365 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -723,6 +723,18 @@ class Node(object): def set_macfilter_lqi_to_node(self, node, lqi): self._cli_no_output('macfilter rss add-lqi', node.get_ext_addr(), lqi) + # ------------------------------------------------------------------------------------------------------------------ + # Radio nodeidfilter + + def nodeidfilter_clear(self, node): + self._cli_no_output('nodeidfilter clear') + + def nodeidfilter_allow(self, node): + self._cli_no_output('nodeidfilter allow', node.index) + + def nodeidfilter_deny(self, node): + self._cli_no_output('nodeidfilter deny', node.index) + # ------------------------------------------------------------------------------------------------------------------ # Parsing helpers