Skip to content

Commit

Permalink
Probers
Browse files Browse the repository at this point in the history
  • Loading branch information
aBozowski committed Nov 2, 2023
1 parent 12307e4 commit c9d14ed
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 75 deletions.
1 change: 1 addition & 0 deletions src/tools/interop/idt/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@

enable_color = True
log_level = logging.INFO
# TODO: RE-Enable
enable_generic_prober_in_capture = False
4 changes: 2 additions & 2 deletions src/tools/interop/idt/discovery/dnssd.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ def browse_interactive(self) -> None:
finally:
zc.close()

async def browse_once(self) -> Zeroconf:
async def browse_once(self, browse_time_seconds: int) -> Zeroconf:
zc = Zeroconf()
ServiceBrowser(zc, list(_MDNS_TYPES.keys()), self)
await asyncio.sleep(10)
await asyncio.sleep(browse_time_seconds)
zc.close()
return zc
14 changes: 4 additions & 10 deletions src/tools/interop/idt/idt.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@
import os
import shutil
import sys

import probe.runner as probe_runner
from pathlib import Path

from capture import EcosystemController, EcosystemFactory, PacketCaptureRunner, PlatformFactory
from capture.utils.artifact import create_file_timestamp, safe_mkdir
from capture.utils.shell import Bash
from probe import GenericMatterProber
from discovery import MatterBleScanner, MatterDnssdListener
from log import border_print

Expand Down Expand Up @@ -230,19 +231,12 @@ def command_capture(self, args: argparse.Namespace) -> None:
border_print("Errors seen this run:")
EcosystemController.error_report(self.artifact_dir)

if config.enable_generic_prober_in_capture:
border_print("Starting generic Matter prober for local environment!")
safe_mkdir(self.dnssd_artifact_dir)
safe_mkdir(self.prober_dir)
GenericMatterProber(self.prober_dir, self.dnssd_artifact_dir).probe()
else:
border_print("Generic prober disabled in config!")

border_print("Compressing artifacts...")
self.zip_artifacts()

def command_probe(self, args: argparse.Namespace) -> None:
border_print("Starting generic Matter prober for local environment!")
safe_mkdir(self.dnssd_artifact_dir)
safe_mkdir(self.prober_dir)
GenericMatterProber(self.prober_dir, self.dnssd_artifact_dir).probe()
probe_runner.run_probes(self.prober_dir, self.dnssd_artifact_dir)
self.zip_artifacts()
6 changes: 4 additions & 2 deletions src/tools/interop/idt/probe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
# limitations under the License.
#

from .probe import GenericMatterProber
from .linux import ProberLinuxHost
from .mac import ProberMacHost

__all__ = [
'GenericMatterProber',
'ProberLinuxHost',
'ProberMacHost',
]
19 changes: 19 additions & 0 deletions src/tools/interop/idt/probe/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#
# Copyright (c) 2023 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

ping_count = 2
dnssd_browsing_time_seconds = 3
46 changes: 46 additions & 0 deletions src/tools/interop/idt/probe/linux.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#
# Copyright (c) 2023 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import probe.probe as p
from capture.utils.host_platform import get_ll_interface
from . import config
from log import get_logger

logger = get_logger(__file__)


class ProberLinuxHost(p.GenericMatterProber):

def __init__(self, artifact_dir: str, dnssd_artifact_dir: str) -> None:
super(ProberLinuxHost, self).__init__(artifact_dir, dnssd_artifact_dir)
self.logger = logger

def probe_v4(self, ipv4: str, port: str) -> None:
self.run_command(f"ping -c {config.ping_count} {ipv4}")

def probe_v6(self, ipv6: str, port: str) -> None:
self.run_command(f"ping -c {config.ping_count} -6 {ipv6}")

def probe_v6_ll(self, ipv6_ll: str, port: str) -> None:
interface = get_ll_interface()
self.run_command(f"ping -c {config.ping_count} -6 {ipv6_ll}%{interface}")

def discover_targets_by_neighbor(self) -> None:
pass

def check_routes(self) -> None:
pass
47 changes: 47 additions & 0 deletions src/tools/interop/idt/probe/mac.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#
# Copyright (c) 2023 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import probe.probe as p
from capture.utils.host_platform import get_ll_interface
from log import get_logger
from . import config

logger = get_logger(__file__)


class ProberMacHost(p.GenericMatterProber):

def __init__(self, artifact_dir: str, dnssd_artifact_dir: str) -> None:
super(ProberMacHost, self).__init__(artifact_dir, dnssd_artifact_dir)
self.logger = logger

def probe_v4(self, ipv4: str, port: str) -> None:
self.run_command(f"ping -c {config.ping_count} {ipv4}")

def probe_v6(self, ipv6: str, port: str) -> None:
self.run_command(f"ping6 -c {config.ping_count} {ipv6}")

def probe_v6_ll(self, ipv6_ll: str, port: str) -> None:
interface = get_ll_interface()
self.run_command(f"ping6 -c {config.ping_count} -I {interface} {ipv6_ll}")

def discover_targets_by_neighbor(self) -> None:
self.run_command("arp -a") # v4
self.run_command("ndp -an") # v6

def check_routes(self) -> None:
self.run_command("netstat -r -f inet6 -n")
115 changes: 54 additions & 61 deletions src/tools/interop/idt/probe/probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,25 @@
import asyncio
import ipaddress
import os.path
from abc import ABC, abstractmethod

from capture.utils.artifact import safe_mkdir, create_standard_log_name
from capture.utils.artifact import create_standard_log_name
from capture.utils.host_platform import is_mac, get_ll_interface
from capture.utils.shell import Bash
from discovery import MatterDnssdListener
from discovery.dnssd import ServiceInfo
from log import get_logger
from . import config

logger = get_logger(__file__)


class GenericMatterProber:
class GenericMatterProber(ABC):

def __init__(self, artifact_dir: str, dnssd_artifact_dir: str) -> None:
"""
Class that probes the local environment for all matter devices
Platform and ecosystem agnostic; run at the end of captures
Platform and ecosystem agnostic
"""
self.artifact_dir = artifact_dir
self.dnssd_artifact_dir = dnssd_artifact_dir
Expand All @@ -45,16 +47,12 @@ def __init__(self, artifact_dir: str, dnssd_artifact_dir: str) -> None:
self.suffix = f"2>&1 | tee {self.output}"
self.interface = None if is_mac() else get_ll_interface()

def discover_targets(self) -> None:
browser = MatterDnssdListener(self.dnssd_artifact_dir)
asyncio.run(browser.browse_once())
for name in browser.discovered_matter_devices:
info: ServiceInfo = browser.discovered_matter_devices[name]
self.logger.info(f"NAME {name}")
self.targets[name] = []
for addr in info.parsed_scoped_addresses():
self.logger.info(f"ADDR {addr}:{info.port}")
self.targets[name].append((addr, info.port))
def run_command(self, cmd: str, capture_output=False) -> Bash:
cmd = f"{cmd} {self.suffix}"
self.logger.info(cmd)
bash = Bash(cmd, sync=True, capture_output=capture_output)
bash.start_command()
return bash

@staticmethod
def is_ipv4(ip: str) -> bool:
Expand All @@ -74,47 +72,53 @@ def is_ipv6(ip: str) -> bool:

@staticmethod
def is_ipv6_ll(ipv6: str) -> bool:
return ipaddress.IPv6Address(ipv6).is_link_local
try:
return ipaddress.IPv6Address(ipv6).is_link_local
except ipaddress.AddressValueError:
return False

def run_command(self, cmd: str) -> None:
cmd = f"{cmd} {self.suffix}"
self.logger.info(cmd)
Bash(cmd, sync=True).start_command()

def route(self, ip: str) -> None:
if is_mac():
self.run_command(f"traceroute {ip}")
else:
self.run_command(f"ip route {ip}")

def probe_v4(self, ipv4: str) -> None:
self.logger.info(f"Probe ipv4 {ipv4}")
self.run_command(f"ping -c 2 {ipv4}")

def probe_v6(self, ipv6: str) -> None:
self.logger.info(f"Probe ipv6 {ipv6}")
if is_mac():
self.run_command(f"ping6 -c 2 {ipv6}")
else:
self.run_command(f"ping -c 2 -6 {ipv6}")

def probe_v6_ll(self, ipv6_ll: str) -> None:
self.logger.info(f"Probe ipv6_ll {ipv6_ll}")
interface = get_ll_interface()
if is_mac():
self.run_command(f"ping6 -c 2 -I {interface} {ipv6_ll}")
else:
self.run_command(f"ping -c 2 -6 {ipv6_ll}%{interface}")
@abstractmethod
def probe_v4(self, ipv4: str, port: str) -> None:
raise NotImplementedError

@abstractmethod
def probe_v6(self, ipv6: str, port: str) -> None:
raise NotImplementedError

@abstractmethod
def probe_v6_ll(self, ipv6_ll: str, port: str) -> None:
raise NotImplementedError

@abstractmethod
def discover_targets_by_neighbor(self) -> None:
raise NotImplementedError

@abstractmethod
def check_routes(self) -> None:
raise NotImplementedError

def discover_targets_by_browsing(self) -> None:
browser = MatterDnssdListener(self.dnssd_artifact_dir)
asyncio.run(browser.browse_once(config.dnssd_browsing_time_seconds))
for name in browser.discovered_matter_devices:
info: ServiceInfo = browser.discovered_matter_devices[name]
self.logger.info(f"NAME {name}")
self.targets[name] = []
for addr in info.parsed_scoped_addresses():
self.logger.info(f"ADDR {addr}:{info.port}")
self.targets[name].append((addr, info.port))

def probe_single_target(self, ip: str, port: str) -> None:
self.logger.info(f"Probe {ip} {port}")
if self.is_ipv4(ip):
self.probe_v4(ip)
self.logger.info(f"Probing v4 {ip}")
self.probe_v4(ip, port) # Platform dependent
elif self.is_ipv6_ll(ip): # Check LL first
self.logger.info(f"Probing v6 ll {ip}")
self.probe_v6_ll(ip, port) # Platform dependent
elif self.is_ipv6(ip):
if self.is_ipv6_ll(ip):
self.probe_v6_ll(ip)
else:
self.probe_v6(ip)
self.logger.info(f"Probing v6 {ip}")
self.probe_v6(ip, port) # Platform dependent

def probe_targets(self) -> None:
for target, addrs in self.targets.items():
Expand All @@ -123,17 +127,6 @@ def probe_targets(self) -> None:
self.probe_single_target(addr[0], addr[1])

def probe(self) -> None:
self.discover_targets()
self.discover_targets_by_browsing()
self.discover_targets_by_neighbor() # Platform dependent
self.probe_targets()


if __name__ == "__main__":
a_dir = os.path.join(os.path.dirname(__file__),
os.environ['IDT_OUTPUT_DIR'],
"probes_temp")
d_dir = os.path.join(os.path.dirname(__file__),
os.environ['IDT_OUTPUT_DIR'],
"dnssd_temp")
safe_mkdir(a_dir)
safe_mkdir(d_dir)
GenericMatterProber(a_dir, d_dir).probe()
27 changes: 27 additions & 0 deletions src/tools/interop/idt/probe/runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#
# Copyright (c) 2023 Project CHIP Authors
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from capture.utils.host_platform import is_mac
from .mac import ProberMacHost
from .linux import ProberLinuxHost


def run_probes(artifact_dir: str, dnssd_dir: str) -> None:
if is_mac():
ProberMacHost(artifact_dir, dnssd_dir).probe()
else:
ProberLinuxHost(artifact_dir, dnssd_dir).probe()

0 comments on commit c9d14ed

Please sign in to comment.