[harness-simulation] add support for RF enclosure simulation (#8092)

This commit adds support for RF enclosure simulation. It can pass
Leader 9.2.9, Router 9.2.9 and Router 9.2.10 in Thread Test Harness
v56.0 now.
This commit is contained in:
Jiachen Dong
2022-09-15 05:31:06 +08:00
committed by GitHub
parent 436bde84f5
commit a0c4ede86d
15 changed files with 302 additions and 70 deletions
+12 -2
View File
@@ -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;
@@ -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
/**
+100 -22
View File
@@ -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);
}
}
}
@@ -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);
+3 -3
View File
@@ -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;
/**
+1 -1
View File
@@ -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
+1 -2
View File
@@ -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;
}
}
+4 -2
View File
@@ -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);
@@ -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] <br/> or <br/>[DUT and Commissioner] <br/>in an RF enclosure ", "Shield Devices"):
test_9_2_9_leader.placeRFEnclosure,
("Remove [Router1, Router2 and Sniffer] <br/> or <br/>[DUT and Commissioner] <br/>from RF enclosure ", "Unshield Devices"):
test_9_2_9_leader.removeRFEnclosure,
("Place [DUT,Router2 and sniffer] <br/> or <br/>[Leader and Commissioner] <br/>in an RF enclosure ", "Shield Devices"):
test_9_2_9_router.placeRFEnclosure,
("Remove [DUT, Router2 and sniffer] <br/> or <br/>[Leader and Commissioner] <br/>from RF enclosure ", "Unshield Devices"):
test_9_2_9_router.removeRFEnclosure,
("Place the <br/> [Leader and Commissioner] devices <br/> in an RF enclosure ", "Shield Devices"):
test_9_2_10_router.placeRFEnclosure,
("Remove <br/>[Leader and Commissioner] <br/>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()
@@ -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)
@@ -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']
@@ -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
@@ -1,5 +1,5 @@
{
"ot_path": "/home/pi/work/src/openthread-pr",
"ot_path": "/home/pi/repo/openthread",
"ot_build": {
"max_number": 64,
"ot": [
@@ -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__':
+10
View File
@@ -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):