diff --git a/examples/apps/cli/main.c b/examples/apps/cli/main.c
index 165f32f93..83de78e81 100644
--- a/examples/apps/cli/main.c
+++ b/examples/apps/cli/main.c
@@ -67,7 +67,7 @@ void otTaskletsSignalPending(otInstance *aInstance)
}
#if OPENTHREAD_POSIX && !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
-static void ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[])
+static otError ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aContext);
OT_UNUSED_VARIABLE(aArgsLength);
@@ -75,9 +75,19 @@ static void ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[])
exit(EXIT_SUCCESS);
}
-static const otCliCommand kCommands[] = {{"exit", ProcessExit}};
+
+#if OPENTHREAD_EXAMPLES_SIMULATION
+extern otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[]);
#endif
+static const otCliCommand kCommands[] = {
+ {"exit", ProcessExit},
+#if OPENTHREAD_EXAMPLES_SIMULATION
+ {"nodeidfilter", ProcessNodeIdFilter},
+#endif
+};
+#endif // OPENTHREAD_POSIX && !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+
int main(int argc, char *argv[])
{
otInstance *instance;
diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h
index 9379421d4..ecd40b9ee 100644
--- a/examples/platforms/simulation/platform-simulation.h
+++ b/examples/platforms/simulation/platform-simulation.h
@@ -232,6 +232,18 @@ void otSimSendUartWriteEvent(const uint8_t *aData, uint16_t aLength);
*/
bool platformRadioIsTransmitPending(void);
+/**
+ * This function parses an environment variable as an unsigned 16-bit integer.
+ *
+ * If the environment variable does not exist, this function does nothing.
+ * If it is not a valid integer, this function will terminate the process with an error message.
+ *
+ * @param[in] aEnvName The name of the environment variable.
+ * @param[out] aValue A pointer to the unsigned 16-bit integer.
+ *
+ */
+void parseFromEnvAsUint16(const char *aEnvName, uint16_t *aValue);
+
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
/**
diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c
index 2b627c2f2..4d6ab8810 100644
--- a/examples/platforms/simulation/radio.c
+++ b/examples/platforms/simulation/radio.c
@@ -71,10 +71,12 @@ enum
#if OPENTHREAD_SIMULATION_VIRTUAL_TIME
extern int sSockFd;
+extern uint16_t sPortBase;
extern uint16_t sPortOffset;
#else
static int sTxFd = -1;
static int sRxFd = -1;
+static uint16_t sPortBase = 9000;
static uint16_t sPortOffset = 0;
static uint16_t sPort = 0;
#endif
@@ -166,6 +168,75 @@ 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 bool NodeIdFilterIsConnectable(uint16_t aNodeId)
+{
+ uint16_t index = aNodeId - 1;
+
+ return (sDeniedNodeIdsBitVector[index / 8] & (0x80 >> (index % 8))) == 0;
+}
+
+static void NodeIdFilterDeny(uint16_t aNodeId)
+{
+ uint16_t index = aNodeId - 1;
+
+ sDeniedNodeIdsBitVector[index / 8] |= 0x80 >> (index % 8);
+}
+
+static void NodeIdFilterClear(void)
+{
+ memset(sDeniedNodeIdsBitVector, 0, sizeof(sDeniedNodeIdsBitVector));
+}
+
+otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[])
+{
+ OT_UNUSED_VARIABLE(aContext);
+
+ otError error = OT_ERROR_NONE;
+
+ otEXPECT_ACTION(aArgsLength > 0, error = OT_ERROR_INVALID_COMMAND);
+
+ if (!strcmp(aArgs[0], "clear"))
+ {
+ otEXPECT_ACTION(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS);
+
+ NodeIdFilterClear();
+ }
+ else if (!strcmp(aArgs[0], "deny"))
+ {
+ uint16_t nodeId;
+ char * endptr;
+
+ otEXPECT_ACTION(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
+
+ nodeId = (uint16_t)strtol(aArgs[1], &endptr, 0);
+
+ otEXPECT_ACTION(*endptr == '\0', error = OT_ERROR_INVALID_ARGS);
+ otEXPECT_ACTION(1 <= nodeId && nodeId <= MAX_NETWORK_SIZE, error = OT_ERROR_INVALID_ARGS);
+
+ NodeIdFilterDeny(nodeId);
+ }
+ else
+ {
+ error = OT_ERROR_INVALID_COMMAND;
+ }
+
+exit:
+ return error;
+}
+#else
+otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[])
+{
+ OT_UNUSED_VARIABLE(aContext);
+ OT_UNUSED_VARIABLE(aArgsLength);
+ OT_UNUSED_VARIABLE(aArgs);
+
+ return OT_ERROR_NOT_IMPLEMENTED;
+}
+#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+
static bool IsTimeAfterOrEqual(uint32_t aTimeA, uint32_t aTimeB)
{
return (aTimeA - aTimeB) < (1U << 31);
@@ -298,7 +369,7 @@ static void initFds(void)
otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)"));
- sPort = (uint16_t)(9000 + sPortOffset + gNodeId);
+ sPort = (uint16_t)(sPortBase + sPortOffset + gNodeId);
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(sPort);
sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
@@ -337,7 +408,7 @@ static void initFds(void)
}
sockaddr.sin_family = AF_INET;
- sockaddr.sin_port = htons((uint16_t)(9000 + sPortOffset));
+ sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset));
sockaddr.sin_addr.s_addr = inet_addr(OT_RADIO_GROUP);
otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)"));
@@ -356,24 +427,10 @@ exit:
void platformRadioInit(void)
{
#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
- char *offset;
+ parseFromEnvAsUint16("PORT_BASE", &sPortBase);
- offset = getenv("PORT_OFFSET");
-
- if (offset)
- {
- char *endptr;
-
- sPortOffset = (uint16_t)strtol(offset, &endptr, 0);
-
- if (*endptr != '\0')
- {
- fprintf(stderr, "Invalid PORT_OFFSET: %s\n", offset);
- exit(EXIT_FAILURE);
- }
-
- sPortOffset *= (MAX_NETWORK_SIZE + 1);
- }
+ parseFromEnvAsUint16("PORT_OFFSET", &sPortOffset);
+ sPortOffset *= (MAX_NETWORK_SIZE + 1);
initFds();
#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
@@ -829,7 +886,10 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const
if (rval > 0)
{
- if (sockaddr.sin_port != htons(sPort))
+ uint16_t srcPort = ntohs(sockaddr.sin_port);
+ uint16_t srcNodeId = srcPort - sPortOffset - sPortBase;
+
+ if (NodeIdFilterIsConnectable(srcNodeId) && srcPort != sPort)
{
sReceiveFrame.mLength = (uint16_t)(rval - 1);
@@ -847,7 +907,7 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const
exit(EXIT_FAILURE);
}
}
-#endif
+#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
if (platformRadioIsTransmitPending())
{
radioSendMessage(aInstance);
@@ -870,7 +930,7 @@ void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFr
sockaddr.sin_family = AF_INET;
inet_pton(AF_INET, OT_RADIO_GROUP, &sockaddr.sin_addr);
- sockaddr.sin_port = htons((uint16_t)(9000 + sPortOffset));
+ sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset));
rval =
sendto(sTxFd, (const char *)aMessage, 1 + aFrame->mLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
@@ -1321,3 +1381,21 @@ otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode)
exit:
return error;
}
+
+void parseFromEnvAsUint16(const char *aEnvName, uint16_t *aValue)
+{
+ char *env = getenv(aEnvName);
+
+ if (env)
+ {
+ char *endptr;
+
+ *aValue = (uint16_t)strtol(env, &endptr, 0);
+
+ if (*endptr != '\0')
+ {
+ fprintf(stderr, "Invalid %s: %s\n", aEnvName, env);
+ exit(EXIT_FAILURE);
+ }
+ }
+}
diff --git a/examples/platforms/simulation/virtual_time/platform-sim.c b/examples/platforms/simulation/virtual_time/platform-sim.c
index fa0dd9055..20eb94717 100644
--- a/examples/platforms/simulation/virtual_time/platform-sim.c
+++ b/examples/platforms/simulation/virtual_time/platform-sim.c
@@ -61,6 +61,7 @@ char **gArguments = NULL;
uint64_t sNow = 0; // microseconds
int sSockFd;
+uint16_t sPortBase = 9000;
uint16_t sPortOffset;
static void handleSignal(int aSignal)
@@ -78,7 +79,7 @@ void otSimSendEvent(const struct Event *aEvent)
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &sockaddr.sin_addr);
- sockaddr.sin_port = htons(9000 + sPortOffset);
+ sockaddr.sin_port = htons(sPortBase + sPortOffset);
rval = sendto(sSockFd, aEvent, offsetof(struct Event, mData) + aEvent->mDataLength, 0, (struct sockaddr *)&sockaddr,
sizeof(sockaddr));
@@ -176,28 +177,15 @@ otError otPlatUartFlush(void)
static void socket_init(void)
{
struct sockaddr_in sockaddr;
- char * offset;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
- offset = getenv("PORT_OFFSET");
+ parseFromEnvAsUint16("PORT_BASE", &sPortBase);
- if (offset)
- {
- char *endptr;
+ parseFromEnvAsUint16("PORT_OFFSET", &sPortOffset);
+ sPortOffset *= (MAX_NETWORK_SIZE + 1);
- sPortOffset = (uint16_t)strtol(offset, &endptr, 0);
-
- if (*endptr != '\0')
- {
- fprintf(stderr, "Invalid PORT_OFFSET: %s\n", offset);
- exit(EXIT_FAILURE);
- }
-
- sPortOffset *= (MAX_NETWORK_SIZE + 1);
- }
-
- sockaddr.sin_port = htons((uint16_t)(9000 + sPortOffset + gNodeId));
+ sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset + gNodeId));
sockaddr.sin_addr.s_addr = INADDR_ANY;
sSockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
diff --git a/include/openthread/cli.h b/include/openthread/cli.h
index 3fa5c5653..dcc33411a 100644
--- a/include/openthread/cli.h
+++ b/include/openthread/cli.h
@@ -52,9 +52,9 @@ extern "C" {
typedef struct otCliCommand
{
const char *mName; ///< A pointer to the command string.
- void (*mCommand)(void * aContext,
- uint8_t aArgsLength,
- char * aArgs[]); ///< A function pointer to process the command.
+ otError (*mCommand)(void * aContext,
+ uint8_t aArgsLength,
+ char * aArgs[]); ///< A function pointer to process the command.
} otCliCommand;
/**
diff --git a/include/openthread/instance.h b/include/openthread/instance.h
index d026a75d4..36c567037 100644
--- a/include/openthread/instance.h
+++ b/include/openthread/instance.h
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
-#define OPENTHREAD_API_VERSION (242)
+#define OPENTHREAD_API_VERSION (243)
/**
* @addtogroup api-instance
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
index 4ef9b60e1..25acbef6a 100644
--- a/src/cli/cli.cpp
+++ b/src/cli/cli.cpp
@@ -299,8 +299,7 @@ otError Interpreter::ProcessUserCommands(Arg aArgs[])
char *args[kMaxArgs];
Arg::CopyArgsToStringArray(aArgs, args);
- mUserCommands[i].mCommand(mUserCommandsContext, Arg::GetArgsLength(aArgs) - 1, args + 1);
- error = OT_ERROR_NONE;
+ error = mUserCommands[i].mCommand(mUserCommandsContext, Arg::GetArgsLength(aArgs) - 1, args + 1);
break;
}
}
diff --git a/src/posix/main.c b/src/posix/main.c
index df014ea5f..d6de188a4 100644
--- a/src/posix/main.c
+++ b/src/posix/main.c
@@ -325,17 +325,19 @@ void otPlatReset(otInstance *aInstance)
assert(false);
}
-static void ProcessNetif(void *aContext, uint8_t aArgsLength, char *aArgs[])
+static otError ProcessNetif(void *aContext, uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aContext);
OT_UNUSED_VARIABLE(aArgsLength);
OT_UNUSED_VARIABLE(aArgs);
otCliOutputFormat("%s:%u\r\n", otSysGetThreadNetifName(), otSysGetThreadNetifIndex());
+
+ return OT_ERROR_NONE;
}
#if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
-static void ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[])
+static otError ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[])
{
OT_UNUSED_VARIABLE(aContext);
OT_UNUSED_VARIABLE(aArgsLength);
diff --git a/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py b/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py
index 194b6ad03..90bc43f3d 100644
--- a/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py
+++ b/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py
@@ -29,6 +29,7 @@
import grpc
import ipaddress
+import itertools
import json
import netifaces
import select
@@ -38,6 +39,9 @@ import time
import win32api
import winreg as wr
+from GRLLibs.UtilityModules.ModuleHelper import ModuleHelper
+from GRLLibs.UtilityModules.SnifferManager import SnifferManager
+from GRLLibs.UtilityModules.TopologyManager import TopologyManager
from Sniffer.ISniffer import ISniffer
from THCI.OpenThread import watched
from simulation.Sniffer.proto import sniffer_pb2
@@ -56,7 +60,79 @@ IPPROTO_IPV6 = 41
WINREG_KEY = r'SYSTEM\CurrentControlSet\Control\Network\{4d36e972-e325-11ce-bfc1-08002be10318}'
+# When Harness requires an RF shield box, it will pop up a message box via `ModuleHelper.UIMsgBox`
+# Replace the function to add and remove an RF enclosure simulation automatically without popping up a window
+def UIMsgBoxDecorator(UIMsgBox, replaceFuncs):
+
+ @staticmethod
+ def UIMsgBoxWrapper(msg='Confirm ??',
+ title='User Input Required',
+ inputRequired=False,
+ default='',
+ choices=None,
+ timeout=None,
+ isPrompt=False):
+ func = replaceFuncs.get((msg, title))
+ if func is None:
+ return UIMsgBox(msg, title, inputRequired, default, choices, timeout, isPrompt)
+ else:
+ return func()
+
+ return UIMsgBoxWrapper
+
+
+class DeviceManager:
+
+ def __init__(self):
+ deviceManager = TopologyManager.m_DeviceManager
+ self._devices = {'DUT': deviceManager.AutoDUTDeviceObject.THCI_Object}
+ for device_obj in deviceManager.DeviceList.values():
+ if device_obj.IsDeviceUsed:
+ self._devices[device_obj.DeviceTopologyInfo] = device_obj.THCI_Object
+
+ def __getitem__(self, deviceName):
+ device = self._devices.get(deviceName)
+ if device is None:
+ raise KeyError('Device Name "%s" not found' % deviceName)
+ return device
+
+
+class RFEnclosureManager:
+
+ def __init__(self, deviceNames1, deviceNames2, snifferPartitionId):
+ self.deviceNames1 = deviceNames1
+ self.deviceNames2 = deviceNames2
+ self.snifferPartitionId = snifferPartitionId
+
+ def placeRFEnclosure(self):
+ devices = DeviceManager()
+ self._partition1 = [devices[role] for role in self.deviceNames1]
+ self._partition2 = [devices[role] for role in self.deviceNames2]
+ sniffer_denied_partition = self._partition1 if self.snifferPartitionId == 2 else self._partition2
+
+ for device1 in self._partition1:
+ for device2 in self._partition2:
+ device1.addBlockedNodeId(device2.node_id)
+ device2.addBlockedNodeId(device1.node_id)
+
+ for sniffer in SnifferManager.SnifferObjects.values():
+ if sniffer.isSnifferCapturing():
+ sniffer.filterNodes(device.node_id for device in sniffer_denied_partition)
+
+ def removeRFEnclosure(self):
+ for device in itertools.chain(self._partition1, self._partition2):
+ device.clearBlockedNodeIds()
+
+ for sniffer in SnifferManager.SnifferObjects.values():
+ if sniffer.isSnifferCapturing():
+ sniffer.filterNodes(())
+
+ self._partition1 = None
+ self._partition2 = None
+
+
class SimSniffer(ISniffer):
+ replaced_msgbox = False
@watched
def __init__(self, **kwargs):
@@ -66,12 +142,47 @@ class SimSniffer(ISniffer):
self._local_pcapng_location = None
if self.addr_port is not None:
+ # Replace `ModuleHelper.UIMsgBox` only when simulation devices exist
+ self._replaceMsgBox()
+
self._sniffer = grpc.insecure_channel(self.addr_port)
self._stub = sniffer_pb2_grpc.SnifferStub(self._sniffer)
# Close the sniffer only when Harness exits
win32api.SetConsoleCtrlHandler(self.__disconnect, True)
+ @watched
+ def _replaceMsgBox(self):
+ # Replace the function only once
+ if SimSniffer.replaced_msgbox:
+ return
+ SimSniffer.replaced_msgbox = True
+
+ test_9_2_9_leader = RFEnclosureManager(['Router_1', 'Router_2'], ['DUT', 'Commissioner'], 1)
+ test_9_2_9_router = RFEnclosureManager(['DUT', 'Router_2'], ['Leader', 'Commissioner'], 1)
+ test_9_2_10_router = RFEnclosureManager(['Leader', 'Commissioner'], ['DUT', 'MED_1', 'SED_1'], 2)
+
+ # Alter the behavior of `ModuleHelper.UIMsgBox` only when it comes to the following test cases:
+ # - Leader 9.2.9
+ # - Router 9.2.9
+ # - Router 9.2.10
+ ModuleHelper.UIMsgBox = UIMsgBoxDecorator(
+ ModuleHelper.UIMsgBox,
+ replaceFuncs={
+ ("Place [Router1, Router2 and Sniffer]
or
[DUT and Commissioner]
in an RF enclosure ", "Shield Devices"):
+ test_9_2_9_leader.placeRFEnclosure,
+ ("Remove [Router1, Router2 and Sniffer]
or
[DUT and Commissioner]
from RF enclosure ", "Unshield Devices"):
+ test_9_2_9_leader.removeRFEnclosure,
+ ("Place [DUT,Router2 and sniffer]
or
[Leader and Commissioner]
in an RF enclosure ", "Shield Devices"):
+ test_9_2_9_router.placeRFEnclosure,
+ ("Remove [DUT, Router2 and sniffer]
or
[Leader and Commissioner]
from RF enclosure ", "Unshield Devices"):
+ test_9_2_9_router.removeRFEnclosure,
+ ("Place the
[Leader and Commissioner] devices
in an RF enclosure ", "Shield Devices"):
+ test_9_2_10_router.placeRFEnclosure,
+ ("Remove
[Leader and Commissioner]
from RF enclosure ", "Unshield Devices"):
+ test_9_2_10_router.removeRFEnclosure,
+ })
+
def __repr__(self):
return '%r' % self.__dict__
@@ -164,6 +275,18 @@ class SimSniffer(ISniffer):
self.is_active = False
+ @watched
+ def filterNodes(self, nodeids):
+ if not self.is_active:
+ return
+
+ request = sniffer_pb2.FilterNodesRequest()
+ request.nodeids.extend(nodeids)
+
+ response = self._stub.FilterNodes(request)
+ if response.status != sniffer_pb2.OK:
+ raise RuntimeError('filterNodes error: %s' % sniffer_pb2.Status.Name(response.status))
+
def __disconnect(self, dwCtrlType):
if self._sniffer is not None:
self._sniffer.close()
diff --git a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py
index 0f68e6761..17c8698b7 100644
--- a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py
+++ b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py
@@ -123,6 +123,7 @@ class OpenThread_BR_Sim(OpenThread_BR):
self.docker_name, self.ssh_ip = discovery_add.split('@')
self.tag, self.node_id = self.docker_name.split('_')
+ self.node_id = int(self.node_id)
# Let it crash if it is an invalid IP address
ipaddress.ip_address(self.ssh_ip)
diff --git a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py
index 59eebcbc7..18c622ec0 100644
--- a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py
+++ b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py
@@ -155,16 +155,17 @@ class OpenThread_Sim(OpenThreadTHCI, IThci):
prefix, self.ssh_ip = discovery_add.split('@')
self.tag, self.node_id = prefix.split('_')
+ self.node_id = int(self.node_id)
# Let it crash if it is an invalid IP address
ipaddress.ip_address(self.ssh_ip)
# Do not use `os.path.join` as it uses backslash as the separator on Windows
+ global config
self.device = '/'.join([config['ot_path'], ot_subpath[self.tag], 'examples/apps/cli/ot-cli-ftd'])
self.connectType = 'ip'
self.telnetIp = self.port = discovery_add
- global config
ssh = config['ssh']
self.telnetPort = ssh['port']
self.telnetUsername = ssh['username']
diff --git a/tools/harness-simulation/posix/launch_testbed.py b/tools/harness-simulation/posix/launch_testbed.py
index c7363362c..dd49fa3bd 100755
--- a/tools/harness-simulation/posix/launch_testbed.py
+++ b/tools/harness-simulation/posix/launch_testbed.py
@@ -109,7 +109,7 @@ def advertise_sniffers(s: socket.socket, dst, add: str, ports: Iterable[int]):
_advertise(s, dst, info)
-def start_sniffer(addr: str, port: int, ot_path: str) -> subprocess.Popen:
+def start_sniffer(addr: str, port: int, ot_path: str, max_nodes_num: int) -> subprocess.Popen:
if isinstance(ipaddress.ip_address(addr), ipaddress.IPv6Address):
server = f'[{addr}]:{port}'
else:
@@ -117,7 +117,11 @@ def start_sniffer(addr: str, port: int, ot_path: str) -> subprocess.Popen:
cmd = [
'python3',
- os.path.join(ot_path, 'tools/harness-simulation/posix/sniffer_sim/sniffer.py'), '--grpc-server', server
+ os.path.join(ot_path, 'tools/harness-simulation/posix/sniffer_sim/sniffer.py'),
+ '--grpc-server',
+ server,
+ '--max-nodes-num',
+ str(max_nodes_num),
]
logging.info('Executing command: %s', ' '.join(cmd))
return subprocess.Popen(cmd)
@@ -172,7 +176,7 @@ def main():
sniffer_server_port_base = config['sniffer']['server_port_base']
sniffer_procs = []
for i in range(sniffer_num):
- sniffer_procs.append(start_sniffer(addr, i + sniffer_server_port_base, ot_path))
+ sniffer_procs.append(start_sniffer(addr, i + sniffer_server_port_base, ot_path, max_nodes_num))
# OTBR firewall scripts create rules inside the Docker container
# Run modprobe to load the kernel modules for iptables
diff --git a/tools/harness-simulation/posix/simulation.conf b/tools/harness-simulation/posix/simulation.conf
index c506f92c9..7c7ab8859 100644
--- a/tools/harness-simulation/posix/simulation.conf
+++ b/tools/harness-simulation/posix/simulation.conf
@@ -1,5 +1,5 @@
{
- "ot_path": "/home/pi/work/src/openthread-pr",
+ "ot_path": "/home/pi/repo/openthread",
"ot_build": {
"max_number": 64,
"ot": [
diff --git a/tools/harness-simulation/posix/sniffer_sim/sniffer.py b/tools/harness-simulation/posix/sniffer_sim/sniffer.py
index 2e4bc8e89..b059cf3da 100644
--- a/tools/harness-simulation/posix/sniffer_sim/sniffer.py
+++ b/tools/harness-simulation/posix/sniffer_sim/sniffer.py
@@ -58,21 +58,21 @@ class SnifferServicer(sniffer_pb2_grpc.Sniffer):
RECV_BUFFER_SIZE = 4096
TIMEOUT = 0.1
- MAX_NODES_NUM = 64
def _reset(self):
self._state = CaptureState.NONE
self._pcap = None
- self._allowed_nodeids = None
+ self._denied_nodeids = None
self._transport = None
self._thread = None
self._thread_alive.clear()
self._pcapng_filename = None
self._tshark_proc = None
- def __init__(self):
+ def __init__(self, max_nodes_num):
+ self._max_nodes_num = max_nodes_num
self._thread_alive = threading.Event()
- self._mutex = threading.Lock() # for self._allowed_nodeids
+ self._mutex = threading.Lock() # for self._denied_nodeids
self._reset()
def Start(self, request, context):
@@ -104,8 +104,7 @@ class SnifferServicer(sniffer_pb2_grpc.Sniffer):
self._pcap = pcap_codec.PcapCodec(request.channel, fifo_name)
# Sniffer all nodes in default, i.e. there is no RF enclosure
- # In this case, self._allowed_nodeids is set to None
- self._allowed_nodeids = None
+ self._denied_nodeids = set()
# Create transport
transport_factory = sniffer_transport.SnifferTransportFactory()
@@ -130,10 +129,10 @@ class SnifferServicer(sniffer_pb2_grpc.Sniffer):
continue
with self._mutex:
- allowed_nodeids = self._allowed_nodeids
+ denied_nodeids = self._denied_nodeids
# Equivalent to RF enclosure
- if allowed_nodeids is None or nodeid in allowed_nodeids:
+ if nodeid not in denied_nodeids:
self._pcap.append(data)
def FilterNodes(self, request, context):
@@ -145,14 +144,14 @@ class SnifferServicer(sniffer_pb2_grpc.Sniffer):
if not (self._state & CaptureState.THREAD):
return sniffer_pb2.FilterNodesResponse(status=sniffer_pb2.OPERATION_ERROR)
- allowed_nodeids = set(request.nodeids)
+ denied_nodeids = set(request.nodeids)
# Validate the node IDs
- for nodeid in allowed_nodeids:
- if not 1 <= nodeid <= self.MAX_NODES_NUM:
+ for nodeid in denied_nodeids:
+ if not 1 <= nodeid <= self._max_nodes_num:
return sniffer_pb2.FilterNodesResponse(status=sniffer_pb2.VALUE_ERROR)
with self._mutex:
- self._allowed_nodeids = allowed_nodeids
+ self._denied_nodeids = denied_nodeids
return sniffer_pb2.FilterNodesResponse(status=sniffer_pb2.OK)
@@ -182,9 +181,9 @@ class SnifferServicer(sniffer_pb2_grpc.Sniffer):
return sniffer_pb2.StopResponse(status=sniffer_pb2.OK, pcap_content=pcap_content)
-def serve(address_port):
+def serve(address_port, max_nodes_num):
server = grpc.server(futures.ThreadPoolExecutor(max_workers=1))
- sniffer_pb2_grpc.add_SnifferServicer_to_server(SnifferServicer(), server)
+ sniffer_pb2_grpc.add_SnifferServicer_to_server(SnifferServicer(max_nodes_num), server)
# add_secure_port requires a web domain
server.add_insecure_port(address_port)
logging.info('server starts on %s', address_port)
@@ -208,9 +207,14 @@ def run_sniffer():
type=str,
required=True,
help='the address of the sniffer server')
+ parser.add_argument('--max-nodes-num',
+ dest='max_nodes_num',
+ type=int,
+ required=True,
+ help='the maximum number of nodes')
args = parser.parse_args()
- serve(args.grpc_server)
+ serve(args.grpc_server, args.max_nodes_num)
if __name__ == '__main__':
diff --git a/tools/harness-thci/OpenThread.py b/tools/harness-thci/OpenThread.py
index 8d65cc464..297272f22 100644
--- a/tools/harness-thci/OpenThread.py
+++ b/tools/harness-thci/OpenThread.py
@@ -3259,6 +3259,16 @@ class OpenThreadTHCI(object):
def setVrCheckSkip(self):
self.__executeCommand("tvcheck disable")
+ @API
+ def addBlockedNodeId(self, node_id):
+ cmd = 'nodeidfilter deny %d' % node_id
+ self.__executeCommand(cmd)
+
+ @API
+ def clearBlockedNodeIds(self):
+ cmd = 'nodeidfilter clear'
+ self.__executeCommand(cmd)
+
class OpenThread(OpenThreadTHCI, IThci):