From e62914e7385f109708c80ad69d6d081f8c4482d3 Mon Sep 17 00:00:00 2001 From: Sebastian Lenzlinger Date: Mon, 1 Jul 2024 00:08:04 +0200 Subject: [PATCH] HANDIN COMMIT --- .gitignore | 13 +-- code/iottb-project/.gitignore | 36 ------ code/iottb-project/docs/command_reference.txt | 110 ++++++++++++++++++ code/iottb-project/docs/help.txt | 38 ++++++ code/iottb-project/docs/help_messages.md | 8 +- .../iottb/commands/initialize_testbed.py | 100 ---------------- .../iottb/commands/raw.py} | 0 code/iottb-project/iottb/commands/sniff.py | 43 ++++--- code/iottb-project/iottb/commands/testbed.py | 53 --------- .../iottb/scripts/generate_help.py | 15 +++ code/iottb-project/pyproject.toml | 2 +- code/iottb/.gitignore | 3 - code/iottb/__main__.py | 30 ----- code/iottb/commands/__init__.py | 0 code/iottb/commands/sniff.py | 108 ----------------- code/iottb/config.json | 1 - code/iottb/config.py | 45 ------- code/iottb/logger.py | 0 code/iottb/models/__init__.py | 0 code/iottb/models/capture_metadata.py | 29 ----- code/iottb/models/device_metadata.py | 27 ----- code/iottb/pyproject.toml | 18 --- code/iottb/scripts/wifi_ctl.sh | 55 --------- .../templates/capture_metadata_template.json | 15 --- code/iottb/templates/config_template.json | 4 - .../templates/device_metadata_template.json | 14 --- code/iottb/test/test_Config.py | 43 ------- .../test/test_ensure_directory_exists.py | 38 ------ code/iottb/test/test_is_ip_address.py | 62 ---------- code/iottb/test/test_is_mac_address.py | 64 ---------- code/iottb/utils/__init__.py | 0 code/iottb/utils/capture_utils.py | 20 ---- code/iottb/utils/diagram1.py | 29 ----- code/iottb/utils/diagramm2.py | 27 ----- code/iottb/utils/file_utils.py | 19 --- code/iottb/utils/tcpdump_utils.py | 9 -- 36 files changed, 195 insertions(+), 883 deletions(-) delete mode 100644 code/iottb-project/.gitignore create mode 100644 code/iottb-project/docs/command_reference.txt create mode 100644 code/iottb-project/docs/help.txt delete mode 100644 code/iottb-project/iottb/commands/initialize_testbed.py rename code/{iottb/__init__.py => iottb-project/iottb/commands/raw.py} (100%) delete mode 100644 code/iottb/.gitignore delete mode 100644 code/iottb/__main__.py delete mode 100644 code/iottb/commands/__init__.py delete mode 100644 code/iottb/commands/sniff.py delete mode 100644 code/iottb/config.json delete mode 100644 code/iottb/config.py delete mode 100644 code/iottb/logger.py delete mode 100644 code/iottb/models/__init__.py delete mode 100644 code/iottb/models/capture_metadata.py delete mode 100644 code/iottb/models/device_metadata.py delete mode 100644 code/iottb/pyproject.toml delete mode 100644 code/iottb/scripts/wifi_ctl.sh delete mode 100644 code/iottb/templates/capture_metadata_template.json delete mode 100644 code/iottb/templates/config_template.json delete mode 100644 code/iottb/templates/device_metadata_template.json delete mode 100644 code/iottb/test/test_Config.py delete mode 100644 code/iottb/test/test_ensure_directory_exists.py delete mode 100644 code/iottb/test/test_is_ip_address.py delete mode 100644 code/iottb/test/test_is_mac_address.py delete mode 100644 code/iottb/utils/__init__.py delete mode 100644 code/iottb/utils/capture_utils.py delete mode 100644 code/iottb/utils/diagram1.py delete mode 100644 code/iottb/utils/diagramm2.py delete mode 100644 code/iottb/utils/file_utils.py delete mode 100644 code/iottb/utils/tcpdump_utils.py diff --git a/.gitignore b/.gitignore index b76c2fd..a457143 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,3 @@ -.obsidian -venv -__pycache__ -*.log -.idea/* -*/.idea -*.idea -/.idea -.idea/ -2024-bsc-sebastian-lenzlinger.iml __pycache__ .venv iottb.egg-info @@ -41,3 +31,6 @@ logs/ .idea/**/dynamic.xml .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml + +.private/ +*.pcap \ No newline at end of file diff --git a/code/iottb-project/.gitignore b/code/iottb-project/.gitignore deleted file mode 100644 index a457143..0000000 --- a/code/iottb-project/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -__pycache__ -.venv -iottb.egg-info -.idea -*.log -logs/ -*.pyc -.obsidian - -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -.private/ -*.pcap \ No newline at end of file diff --git a/code/iottb-project/docs/command_reference.txt b/code/iottb-project/docs/command_reference.txt new file mode 100644 index 0000000..8bc3ae3 --- /dev/null +++ b/code/iottb-project/docs/command_reference.txt @@ -0,0 +1,110 @@ +Usage: iottb [OPTIONS] COMMAND [ARGS]... + +Options: + -v, --verbosity Set verbosity [default: 0; 0<=x<=3] + -d, --debug Enable debug mode + --dry-run [default: True] + --cfg-file PATH Path to iottb config file [default: + /home/seb/.config/iottb/iottb.cfg] + --help Show this message and exit. + +Commands: + add-device Add a device to a database + init-db + rm-cfg Removes the cfg file from the filesystem. + rm-dbs Removes ALL(!) databases from the filesystem if... + set-key-in-table-to Edit config or metadata files. + show-all Show everything: configuration, databases, and... + show-cfg Show the current configuration context + sniff Sniff packets with tcpdump +Usage: iottb init-db [OPTIONS] + +Options: + -d, --dest PATH Location to put (new) iottb database + -n, --name TEXT Name of new database. [default: iottb.db] + --update-default / --no-update-default + If new db should be set as the new default + [default: update-default] + --help Show this message and exit. +Usage: iottb add-device [OPTIONS] + + Add a device to a database + +Options: + --dev, --device-name TEXT The name of the device to be added. If this + string contains spaces or other special + characters normalization is + performed to derive a canonical name [required] + --db, --database DIRECTORY Database in which to add this device. If not + specified use default from config. [env var: + IOTTB_DB] + --guided Add device interactively [env var: + IOTTB_GUIDED_ADD] + --help Show this message and exit. +Usage: iottb sniff [OPTIONS] [TCPDUMP-ARGS] [DEVICE] + + Sniff packets with tcpdump + +Options: + Testbed sources: + --db, --database TEXT Database of device. Only needed if not current + default. [env var: IOTTB_DB] + --app TEXT Companion app being used during capture + Runtime behaviour: + --unsafe Disable checks for otherwise required options. + [env var: IOTTB_UNSAFE] + --guided [env var: IOTTB_GUIDED] + --pre TEXT Script to be executed before main command is + started. + --post TEXT Script to be executed upon completion of main + command. + Tcpdump options: + -i, --interface TEXT Network interface to capture on.If not specified + tcpdump tries to find and appropriate one. [env + var: IOTTB_CAPTURE_INTERFACE] + -a, --address TEXT IP or MAC address to filter packets by. [env var: + IOTTB_CAPTURE_ADDRESS] + -I, --monitor-mode Put interface into monitor mode. + --ff TEXT tcpdump filter as string or file path. [env var: + IOTTB_CAPTURE_FILTER] + -#, --print-pacno Print packet number at beginning of line. True by + default. [default: True] + -e, --print-ll Print link layer headers. True by default. + -c, --count INTEGER Number of packets to capture. [default: 1000] + --help Show this message and exit. +Utility Commands mostly for development +Usage: iottb rm-cfg [OPTIONS] + + Removes the cfg file from the filesystem. + + This is mostly a utility during development. Once non-standard database + locations are implemented, deleting this would lead to iottb not being able + to find them anymore. + +Options: + --yes Confirm the action without prompting. + --help Show this message and exit. +Usage: iottb rm-dbs [OPTIONS] + + Removes ALL(!) databases from the filesystem if they're empty. + + Development utility currently unfit for use. + +Options: + --yes Confirm the action without prompting. + --help Show this message and exit. +Usage: iottb show-cfg [OPTIONS] + + Show the current configuration context + +Options: + --cfg-file PATH Path to the config file [default: + /home/seb/.config/iottb/iottb.cfg] + -pp Pretty Print + --help Show this message and exit. +Usage: iottb show-all [OPTIONS] + + Show everything: configuration, databases, and device metadata + +Options: + --help Show this message and exit. diff --git a/code/iottb-project/docs/help.txt b/code/iottb-project/docs/help.txt new file mode 100644 index 0000000..67c567d --- /dev/null +++ b/code/iottb-project/docs/help.txt @@ -0,0 +1,38 @@ +Usage: iottb [OPTIONS] COMMAND [ARGS]... + +Options: + -v, --verbosity Set verbosity [default: 0; 0<=x<=3] + -d, --debug Enable debug mode + --dry-run [default: True] + --cfg-file PATH Path to iottb config file [default: + /home/seb/.config/iottb/iottb.cfg] + --help Show this message and exit. + +Commands: + add-device Add a device to a database + init-db + rm-cfg Removes the cfg file from the filesystem. + rm-dbs Removes ALL(!) databases from the filesystem if... + set-key-in-table-to Edit config or metadata files. + show-all Show everything: configuration, databases, and... + show-cfg Show the current configuration context + sniff Sniff packets with tcpdump +Usage: iottb [OPTIONS] COMMAND [ARGS]... + +Options: + -v, --verbosity Set verbosity [default: 0; 0<=x<=3] + -d, --debug Enable debug mode + --dry-run [default: True] + --cfg-file PATH Path to iottb config file [default: + /home/seb/.config/iottb/iottb.cfg] + --help Show this message and exit. + +Commands: + add-device Add a device to a database + init-db + rm-cfg Removes the cfg file from the filesystem. + rm-dbs Removes ALL(!) databases from the filesystem if... + set-key-in-table-to Edit config or metadata files. + show-all Show everything: configuration, databases, and... + show-cfg Show the current configuration context + sniff Sniff packets with tcpdump diff --git a/code/iottb-project/docs/help_messages.md b/code/iottb-project/docs/help_messages.md index 4936087..1521c85 100644 --- a/code/iottb-project/docs/help_messages.md +++ b/code/iottb-project/docs/help_messages.md @@ -1,9 +1,9 @@ -Main Command: iottb -Testbed [I] - Usage: [OPTIONS] COMMAND [ARGS]... +# Main Command: `iottb` + + Usage: `iottb [OPTIONS] COMMAND [ARGS]...` Options: - -v, --verbosity Set verbosity [0<=x<=3] + -v, --verbosity Set verbosity [0<=x<=3] \n -d, --debug Enable debug mode --dry-run --cfg-file PATH Path to iottb config file diff --git a/code/iottb-project/iottb/commands/initialize_testbed.py b/code/iottb-project/iottb/commands/initialize_testbed.py deleted file mode 100644 index d7373ea..0000000 --- a/code/iottb-project/iottb/commands/initialize_testbed.py +++ /dev/null @@ -1,100 +0,0 @@ -import click -from pathlib import Path -import logging -from logging.handlers import RotatingFileHandler -import sys -from iottb.models.iottb_config import IottbConfig -from iottb.definitions import DB_NAME - -logger = logging.getLogger(__name__) - - -@click.command() -@click.option('-d', '--dest', type=click.Path(), help='Location to put (new) iottb database') -@click.option('-n', '--name', default=DB_NAME, type=str, help='Name of new database.') -@click.option('--update-default/--no-update-default', default=True, help='If new db should be set as the new default') -@click.pass_context -def init_db(ctx, dest, name, update_default): - logger.info('init-db invoked') - config = ctx.obj['CONFIG'] - logger.debug(f'str(config)') - # Use the default path from config if dest is not provided - known_dbs = config.get_known_databases() - logger.debug(f'Known databases: {known_dbs}') - if name in known_dbs: - dest = config.get_database_location(name) - if Path(dest).joinpath(name).is_dir(): - click.echo(f'A database {name} already exists.') - logger.debug(f'DB {name} exists in {dest}') - click.echo(f'Exiting...') - exit() - logger.debug(f'DB name {name} registered but does not exist.') - if not dest: - logger.info('No dest set, choosing default destination.') - dest = Path(config.default_db_location).parent - - db_path = Path(dest).joinpath(name) - logger.debug(f'Full path for db {str(db_path)}') - # Create the directory if it doesn't exist - db_path.mkdir(parents=True, exist_ok=True) - logger.info(f"mkdir {db_path} successful") - click.echo(f'Created {db_path}') - - # Update configuration - config.set_database_location(name, str(dest)) - if update_default: - config.set_default_database(name, str(dest)) - config.save_config() - logger.info(f"Updated configuration with database {name} at {db_path}") - - -@click.command() -@click.option('-d', '--dest', type=click.Path(), help='Location to put (new) iottb database') -@click.option('-n', '--name', default=DB_NAME, type=str, help='Name of new database.') -@click.option('--update-default/--no-update-default', default=True, help='If new db should be set as the new default') -@click.pass_context -def init_db_inactive(ctx, dest, name, update_default): - logger.info('init-db invoked') - config = ctx.obj['CONFIG'] - logger.debug(f'str(config)') - - # Retrieve known databases - known_dbs = config.get_known_databases() - - # Determine destination path - if name in known_dbs: - dest = Path(config.get_database_location(name)) - if dest.joinpath(name).is_dir(): - click.echo(f'A database {name} already exists.') - logger.debug(f'DB {name} exists in {dest}') - click.echo(f'Exiting...') - exit() - logger.debug(f'DB name {name} registered but does not exist.') - elif not dest: - logger.info('No destination set, using default path from config.') - dest = Path(config.default_db_location).parent - - # Ensure destination path is absolute - dest = dest.resolve() - - # Combine destination path with database name - db_path = dest / name - logger.debug(f'Full path for database: {str(db_path)}') - - # Create the directory if it doesn't exist - try: - db_path.mkdir(parents=True, exist_ok=True) - logger.info(f'Directory {db_path} created successfully.') - click.echo(f'Created {db_path}') - except Exception as e: - logger.error(f'Failed to create directory {db_path}: {e}') - click.echo(f'Failed to create directory {db_path}: {e}', err=True) - exit(1) - - # Update configuration - config.set_database_location(name, str(db_path)) - if update_default: - config.set_default_database(name, str(db_path)) - config.save_config() - logger.info(f'Updated configuration with database {name} at {db_path}') - click.echo(f'Updated configuration with database {name} at {db_path}') diff --git a/code/iottb/__init__.py b/code/iottb-project/iottb/commands/raw.py similarity index 100% rename from code/iottb/__init__.py rename to code/iottb-project/iottb/commands/raw.py diff --git a/code/iottb-project/iottb/commands/sniff.py b/code/iottb-project/iottb/commands/sniff.py index 4401862..aaf277e 100644 --- a/code/iottb-project/iottb/commands/sniff.py +++ b/code/iottb-project/iottb/commands/sniff.py @@ -1,18 +1,16 @@ +import json +import logging import os -import shutil +import re +import subprocess import uuid +from datetime import datetime +from pathlib import Path from time import time import click -import subprocess -import json -from pathlib import Path -import logging -import re -from datetime import datetime from click_option_group import optgroup -from iottb.definitions import APP_NAME, CFG_FILE_PATH -from iottb.models.iottb_config import IottbConfig + from iottb.utils.string_processing import make_canonical_name # Setup logger @@ -47,11 +45,13 @@ def validate_sniff(ctx, param, value): def run_pre(pre): - pass + subprocess.run(pre, shell=True) + logger.debug(f'finnished {pre}') def run_post(post): - pass + subprocess.run(post, shell=True) + logger.debug(f'finnished {post}') @click.command('sniff', help='Sniff packets with tcpdump') @@ -63,9 +63,8 @@ def run_post(post): @optgroup.option('--unsafe', is_flag=True, default=False, envvar='IOTTB_UNSAFE', is_eager=True, help='Disable checks for otherwise required options.\n', show_envvar=True) @optgroup.option('--guided', is_flag=True, default=False, envvar='IOTTB_GUIDED', show_envvar=True) -@optgroup.option('--pre', type=click.Path(exists=True, executable=True), help='Script to be executed before main ' - 'command' - 'is started.') +@optgroup.option('--pre', help='Script to be executed before main command is started.') +@optgroup.option('--post', help='Script to be executed upon completion of main command.') @optgroup.group('Tcpdump options') @optgroup.option('-i', '--interface', help='Network interface to capture on.' + @@ -74,11 +73,11 @@ def run_post(post): @optgroup.option('-a', '--address', callback=validate_sniff, help='IP or MAC address to filter packets by.\n', show_envvar=True, envvar='IOTTB_CAPTURE_ADDRESS') -@optgroup.option('-I', '--monitor-mode', help='Put interface into monitor mode.', is_flag=True) +@optgroup.option('-I', '--monitor-mode', help='Put interface into monitor mode.\n', is_flag=True) @optgroup.option('--ff', type=str, envvar='IOTTB_CAPTURE_FILTER', show_envvar=True, help='tcpdump filter as string or file path.') @optgroup.option('-#', '--print-pacno', is_flag=True, default=True, - help='Print packet number at beginning of line. True by default.') + help='Print packet number at beginning of line. True by default.\n') @optgroup.option('-e', '--print-ll', is_flag=True, default=False, help='Print link layer headers. True by default.') @optgroup.option('-c', '--count', type=int, help='Number of packets to capture.', default=1000) @@ -263,7 +262,7 @@ def sniff(ctx, device, interface, print_pacno, ff, count, monitor_mode, print_ll out.write(tcp_complete.stdout) err.write(tcp_complete.stderr) - #click.echo(f'Mock sniff execution') + # click.echo(f'Mock sniff execution') click.echo(f"Capture complete. Saved to {pcap_file}") except subprocess.CalledProcessError as e: logger.error(f'Failed to capture packets: {e}') @@ -285,6 +284,8 @@ def sniff(ctx, device, interface, print_pacno, ff, count, monitor_mode, print_ll end_time = datetime.now().strftime("%H:%M:%S") end = time() delta = end - start + + click.echo(f'tcpdump took {delta:.2f} seconds.') # Step 9: Register metadata metadata = { @@ -310,7 +311,9 @@ def sniff(ctx, device, interface, print_pacno, ff, count, monitor_mode, print_ll 'resources': { 'pcap_file': str(pcap_file), 'stdout_log': str(stdout_log_file), - 'stderr_log': str(stderr_log_file) + 'stderr_log': str(stderr_log_file), + 'pre': str(pre), + 'post': str(post) }, 'environment': { 'capture_dir': capture_dir, @@ -337,5 +340,7 @@ def sniff(ctx, device, interface, print_pacno, ff, count, monitor_mode, print_ll click.echo(f'END SNIFF SUBCOMMAND') if post: - click.echo(f'Running post command {post}') + click.echo(f'Running post script {post}') run_post(post) + + diff --git a/code/iottb-project/iottb/commands/testbed.py b/code/iottb-project/iottb/commands/testbed.py index eb26a9f..da67a12 100644 --- a/code/iottb-project/iottb/commands/testbed.py +++ b/code/iottb-project/iottb/commands/testbed.py @@ -65,56 +65,3 @@ def init_db(ctx, dest, name, update_default): # config.save_config() - - - -@click.command() -@click.option('-d', '--dest', type=click.Path(), help='Location to put (new) iottb database') -@click.option('-n', '--name', default=DB_NAME, type=str, help='Name of new database.') -@click.option('--update-default/--no-update-default', default=True, help='If new db should be set as the new default') -@click.pass_context -def init_db_inactive(ctx, dest, name, update_default): - logger.info('init-db invoked') - config = ctx.obj['CONFIG'] - logger.debug(f'str(config)') - - # Retrieve known databases - known_dbs = config.get_known_databases() - - # Determine destination path - if name in known_dbs: - dest = Path(config.get_database_location(name)) - if dest.joinpath(name).is_dir(): - click.echo(f'A database {name} already exists.') - logger.debug(f'DB {name} exists in {dest}') - click.echo(f'Exiting...') - exit() - logger.debug(f'DB name {name} registered but does not exist.') - elif not dest: - logger.info('No destination set, using default path from config.') - dest = Path(config.default_db_location).parent - - # Ensure destination path is absolute - dest = dest.resolve() - - # Combine destination path with database name - db_path = dest / name - logger.debug(f'Full path for database: {str(db_path)}') - - # Create the directory if it doesn't exist - try: - db_path.mkdir(parents=True, exist_ok=True) - logger.info(f'Directory {db_path} created successfully.') - click.echo(f'Created {db_path}') - except Exception as e: - logger.error(f'Failed to create directory {db_path}: {e}') - click.echo(f'Failed to create directory {db_path}: {e}', err=True) - exit(1) - - # Update configuration - config.set_database_location(name, str(db_path)) - if update_default: - config.set_default_database(name, str(db_path)) - config.save_config() - logger.info(f'Updated configuration with database {name} at {db_path}') - click.echo(f'Updated configuration with database {name} at {db_path}') diff --git a/code/iottb-project/iottb/scripts/generate_help.py b/code/iottb-project/iottb/scripts/generate_help.py index 2b4d29d..7cfc6e7 100755 --- a/code/iottb-project/iottb/scripts/generate_help.py +++ b/code/iottb-project/iottb/scripts/generate_help.py @@ -50,8 +50,23 @@ def write_help_to_file(cli, filename): f.write("\n\n") +def manual(): + comands = [ + 'init-db', + 'add-device', + 'sniff' + ] + dev_commands = [ + 'show-all', + 'rm-dbs', + 'show-cfg', + 'show-all' + ] + + if __name__ == "__main__": from iottb import DOCS_FOLDER + print('Must be in project root for this to work properly!') print(f'CWD is {str(Path.cwd())}') DOCS_FOLDER.mkdir(exist_ok=True) diff --git a/code/iottb-project/pyproject.toml b/code/iottb-project/pyproject.toml index 80acba6..9343dba 100644 --- a/code/iottb-project/pyproject.toml +++ b/code/iottb-project/pyproject.toml @@ -8,7 +8,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" click = "^8.1" -scapy = "^2.5" +# scapy = "^2.5" [tool.poetry.scripts] iottb = "iottb.main:cli" diff --git a/code/iottb/.gitignore b/code/iottb/.gitignore deleted file mode 100644 index 852cc91..0000000 --- a/code/iottb/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -__pycache__ -.venv -iottb.egg-info \ No newline at end of file diff --git a/code/iottb/__main__.py b/code/iottb/__main__.py deleted file mode 100644 index fb99a10..0000000 --- a/code/iottb/__main__.py +++ /dev/null @@ -1,30 +0,0 @@ -import argparse -import logging -from pathlib import Path - -from .commands.sniff import setup_sniff_parser -from .config import Config -from .utils.file_utils import ensure_directory_exists - - -def setup_logging(): - logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(name)s: %(message)s') - - -def main(): - setup_logging() - - parser = argparse.ArgumentParser(description='IoT Testbed') - subparsers = parser.add_subparsers() - - setup_sniff_parser(subparsers) - - args = parser.parse_args() - if hasattr(args, 'func'): - args.func(args) - else: - parser.print_help() - - -if __name__ == "__main__": - main() diff --git a/code/iottb/commands/__init__.py b/code/iottb/commands/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/code/iottb/commands/sniff.py b/code/iottb/commands/sniff.py deleted file mode 100644 index f8e97ad..0000000 --- a/code/iottb/commands/sniff.py +++ /dev/null @@ -1,108 +0,0 @@ -import subprocess -import re -from datetime import datetime -from pathlib import Path -import logging -from models.capture_metadata import CaptureMetadata -from models.device_metadata import DeviceMetadata -from utils.capture_utils import get_capture_src_folder, make_capture_src_folder -from utils.tcpdump_utils import check_installed -from utils.file_utils import ensure_directory_exists -from config import Config - -logger = logging.getLogger('iottb.sniff') - - -def is_ip_address(address): - ip_pattern = re.compile(r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$") - return ip_pattern.match(address) is not None - - -def is_mac_address(address): - mac_pattern = re.compile(r"^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$") - return mac_pattern.match(address) is not None - - -class Sniffer: - def __init__(self, device_id, capture_interface, address=None, safe=True): - #TODO Decide if we want to use the device_id as the device_name (seems unhandy) - self.device_id = device_id - self.capture_interface = capture_interface - self.address = address - self.safe = safe - self.config = Config().load_config() - self.device_path = self.initialize_device(device_id) - self.filter = self.generate_filter() - - def initialize_device(self, device_id): - db_path = Path(self.config.get('database_path', '~/iottb.db')).expanduser() - device_path = db_path / device_id - ensure_directory_exists(device_path) - - metadata_file = device_path / 'device_metadata.json' - if not metadata_file.exists(): - device_metadata = DeviceMetadata(device_name=device_id, device_root_path=device_path) - device_metadata.save_to_file() - return device_path - - def get_capture_metadata(self, capture_dir): - metadata = CaptureMetadata(device_id=self.device_id, capture_dir=capture_dir) - metadata.build_capture_file_name() - metadata.interface = self.capture_interface - metadata.device_ip_address = self.address or "No IP Address set" - return metadata - - def generate_filter(self): - - if not self.address and self.safe: - raise ValueError("Address must be provided in safe mode.") - - if is_ip_address(self.address): - return f"host {self.address}" - elif is_mac_address(self.address): - return f"ether host {self.address}" - else: - raise ValueError("Invalid address format.") - - def capture(self): - if not check_installed(): - print('Please install tcpdump first') - return - - capture_dir = make_capture_src_folder(get_capture_src_folder(self.device_path)) - metadata = self.get_capture_metadata(capture_dir) - pcap_file = capture_dir / metadata.capture_file - cmd = ['sudo', 'tcpdump', '-i', self.capture_interface, '-w', str(pcap_file)] - - if self.filter: - cmd.append(self.filter) - - metadata.tcpdump_command = ' '.join(cmd) - print(f'Executing: {metadata.tcpdump_command}') - - try: - metadata.start_time = datetime.now().isoformat() - subprocess.run(cmd, check=True) - metadata.stop_time = datetime.now().isoformat() - except subprocess.CalledProcessError as e: - logger.error(f'Failed to capture packets: {e}') - return - - metadata.save_to_file() - print(f"Capture complete. Metadata saved to {capture_dir / 'metadata.json'}") - - -def setup_sniff_parser(subparsers): - parser = subparsers.add_parser('sniff', help='Sniff packets with tcpdump') - parser.add_argument('device_id', help='ID of the device to sniff') - parser.add_argument('-i', '--interface', required=True, help='Network interface to capture on') - parser.add_argument('-a', '--address', help='IP or MAC address to filter packets by') - parser.add_argument('-u', '--unsafe', action='store_true', help='Run in unsafe mode without supplying an address. ' - 'Highly discouraged.') - parser.set_defaults(func=handle_sniff) - - -def handle_sniff(args): - sniffer = Sniffer(device_id=args.device_id, capture_interface=args.interface, address=args.address, - safe=not args.unsafe) - sniffer.capture() diff --git a/code/iottb/config.json b/code/iottb/config.json deleted file mode 100644 index ec311ec..0000000 --- a/code/iottb/config.json +++ /dev/null @@ -1 +0,0 @@ -{"database_path": "~/.iottb.db", "log_level": "INFO"} \ No newline at end of file diff --git a/code/iottb/config.py b/code/iottb/config.py deleted file mode 100644 index 6c0e532..0000000 --- a/code/iottb/config.py +++ /dev/null @@ -1,45 +0,0 @@ -import json -from pathlib import Path -import logging - -logger = logging.getLogger('iottb.config') - - -class Config: - DEFAULT_CONFIG = { - "database_path": "~/.iottb.db", - "log_level": "INFO" - } - - def __init__(self, config_file=None): - self.config_file = Path(config_file or "config.json") - if not self.config_file.exists(): - self.create_default_config() - else: - self.config = self.load_config() - - def create_default_config(self): - try: - self.save_config(self.DEFAULT_CONFIG) - except (IsADirectoryError, PermissionError) as e: - logger.error(f"Error creating default config: {e}") - raise - - def load_config(self): - try: - with open(self.config_file, "r") as file: - return json.load(file) - except IsADirectoryError as e: - logger.error(f"Error loading config: {e}") - raise - except PermissionError as e: - logger.error(f"Error loading config: {e}") - raise - - def save_config(self, config): - try: - with open(self.config_file, "w") as f: - json.dump(config, f, indent=2) - except (IsADirectoryError, PermissionError) as e: - logger.error(f"Error saving config: {e}") - raise diff --git a/code/iottb/logger.py b/code/iottb/logger.py deleted file mode 100644 index e69de29..0000000 diff --git a/code/iottb/models/__init__.py b/code/iottb/models/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/code/iottb/models/capture_metadata.py b/code/iottb/models/capture_metadata.py deleted file mode 100644 index 4c7e1c0..0000000 --- a/code/iottb/models/capture_metadata.py +++ /dev/null @@ -1,29 +0,0 @@ -import json -import uuid -from datetime import datetime -from pathlib import Path - - -class CaptureMetadata: - def __init__(self, device_id, capture_dir): - self.device_id = device_id - self.capture_id = str(uuid.uuid4()) - self.capture_date = datetime.now().isoformat() - self.capture_dir = Path(capture_dir) - self.capture_file = "" - self.start_time = "" - self.stop_time = "" - self.tcpdump_command = "" - self.interface = "" - self.device_ip_address = "" - - def build_capture_file_name(self): - self.capture_file = f"{self.device_id}_{self.capture_id}.pcap" - - def to_dict(self): - return self.__dict__ - - def save_to_file(self, file_path=None): - file_path = file_path or self.capture_dir / 'metadata.json' - with open(file_path, 'w') as f: - json.dump(self.to_dict(), f, indent=4) diff --git a/code/iottb/models/device_metadata.py b/code/iottb/models/device_metadata.py deleted file mode 100644 index 5c5cb91..0000000 --- a/code/iottb/models/device_metadata.py +++ /dev/null @@ -1,27 +0,0 @@ -import json -import uuid -from datetime import datetime -from pathlib import Path - - -class DeviceMetadata: - def __init__(self, device_name, device_root_path): - self.device_name = device_name - self.device_short_name = device_name.lower().replace(' ', '_') - self.device_id = str(uuid.uuid4()) - self.date_created = datetime.now().isoformat() - self.device_root_path = Path(device_root_path) - - def to_dict(self): - return self.__dict__ - - def save_to_file(self): - file_path = self.device_root_path / 'device_metadata.json' - with open(file_path, 'w') as f: - json.dump(self.to_dict(), f, indent=4) - - @classmethod - def load_from_file(cls, file_path): - with open(file_path, 'r') as f: - data = json.load(f) - return cls(**data) diff --git a/code/iottb/pyproject.toml b/code/iottb/pyproject.toml deleted file mode 100644 index 580c3e8..0000000 --- a/code/iottb/pyproject.toml +++ /dev/null @@ -1,18 +0,0 @@ -[build-system] -requires = ["setuptools>=42", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "iottb" -version = "0.1.0" -authors = [{name = "Sebastian Lenzlinger", email = "sebastian.lenzlinger@unibas.ch"}] -description = "Automation Tool for Capturing Network packets of IoT devices." -requires-python = ">=3.8" -dependencies = [] - -[tool.setuptools.packages.find] -where = ["."] -exclude = ["tests*", "docs*"] - -[project.scripts] -iottb = "iottb.__main__:main" diff --git a/code/iottb/scripts/wifi_ctl.sh b/code/iottb/scripts/wifi_ctl.sh deleted file mode 100644 index 076d2fd..0000000 --- a/code/iottb/scripts/wifi_ctl.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -# Note, this is not my original work. Source: https://linuxtldr.com/changing-interface-mode/ - -function list_nic_info () { - ip addr show -} - -function enable_monm_iw () { - interface=$1 - sudo ip link set "$interface" down - sudo iw "$interface" set monitor control - sudo ip link set "$interface" up -} - -function disable_monm_iw () { - interface=$1 - sudo ip link set "$interface" down - sudo iw "$interface" set type managed - sudo ip link set "$interface" up -} - -function enable_monm_iwconfig () { - interface=$1 - sudo ifconfig "$interface" down - sudo iwconfig "$interface" mode monitor - sudo ifconfig "$interface" up -} - -function disable_monm_iwconfig () { - interface=$1 - sudo ifconfig "$interface" down - sudo iwconfig "$interface" mode managed - sudo ifconfig "$interface" up -} - -function enable_monm_acng () { - interface=$1 - sudo airmon-ng check - sudo airmon-ng check kill - sudo airmon-ng start "$interface" -} - -function disable_monm_acng () { - interface="${1}mon" - sudo airmon-ng stop "$interface" - sudo systemctl restart NetworkManager -} - -if declare -f "$1" > /dev/null -then - "$@" -else - echo "Unknown function '$1'" >&2 - exit 1 -fi \ No newline at end of file diff --git a/code/iottb/templates/capture_metadata_template.json b/code/iottb/templates/capture_metadata_template.json deleted file mode 100644 index ec49e81..0000000 --- a/code/iottb/templates/capture_metadata_template.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "device_id": "", - "capture_id": "", - "capture_date": "", - "capture_file": "", - "start_time": "", - "stop_time": "", - "capture_duration": "", - "interfaces": "", - "device_ip_address": "", - "device_mac_address": "", - "contacted_ip_address": [], - "device_firmware_version": "", - "campanion_app": "" -} \ No newline at end of file diff --git a/code/iottb/templates/config_template.json b/code/iottb/templates/config_template.json deleted file mode 100644 index 89338dc..0000000 --- a/code/iottb/templates/config_template.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "database_path": "~/.iottb.db", - "log_level": "INFO" -} \ No newline at end of file diff --git a/code/iottb/templates/device_metadata_template.json b/code/iottb/templates/device_metadata_template.json deleted file mode 100644 index 79a17f1..0000000 --- a/code/iottb/templates/device_metadata_template.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "device_id": "", - "device_name": "", - "device_short_name": "", - "date_created": "", - "description": "", - "model": "", - "manufacturer": "", - "firmware_version": "", - "device_type": "", - "supported_interfaces": "", - "companion_applications": "", - "last_metadata_update": "" -} \ No newline at end of file diff --git a/code/iottb/test/test_Config.py b/code/iottb/test/test_Config.py deleted file mode 100644 index cb21267..0000000 --- a/code/iottb/test/test_Config.py +++ /dev/null @@ -1,43 +0,0 @@ -import json -from pathlib import Path -from unittest import mock - -from config import Config - -import unittest - - -class TestConfig(unittest.TestCase): - - def test_creates_new_config_file_if_not_exists(self): - config_path = Path("test_config.json") - if config_path.exists(): - config_path.unlink() - config = Config(config_file=config_path) - self.assertTrue(config_path.exists()) - config_path.unlink() - - def test_writes_default_configuration_to_config_file(self): - config_path = Path("test_config.json") - if config_path.exists(): - config_path.unlink() - config = Config(config_file=config_path) - with open(config_path, "r") as f: - data = json.load(f) - self.assertEqual(data, {"database_path": "~/.iottb.db", "log_level": "INFO"}) - config_path.unlink() - - @unittest.mock.patch("builtins.open", side_effect=PermissionError) - def test_config_file_path_not_writable(self, mock_open): - config_path = Path("test_config.json") - with self.assertRaises(PermissionError): - config = Config(config_file=config_path) - config.create_default_config() - - def test_config_file_path_is_directory(self): - config_dir = Path("test_config_dir") - config_dir.mkdir(exist_ok=True) - with self.assertRaises(IsADirectoryError): - config = Config(config_file=config_dir) - config.create_default_config() - config_dir.rmdir() diff --git a/code/iottb/test/test_ensure_directory_exists.py b/code/iottb/test/test_ensure_directory_exists.py deleted file mode 100644 index ac5b1fd..0000000 --- a/code/iottb/test/test_ensure_directory_exists.py +++ /dev/null @@ -1,38 +0,0 @@ -from pathlib import Path - -# Generated by CodiumAI -import unittest - -from utils.file_utils import ensure_directory_exists - - -class TestEnsureDirectoryExists(unittest.TestCase): - - # creates directory if it does not exist - def test_creates_directory_if_not_exists(self): - path = Path('/tmp/testdir') - if path.exists(): - path.rmdir() - ensure_directory_exists(path) - self.assertTrue(path.exists()) - path.rmdir() - - # does not create directory if it already exists - def test_does_not_create_directory_if_exists(self): - path = Path('/tmp/testdir') - path.mkdir(exist_ok=True) - ensure_directory_exists(path) - self.assertTrue(path.exists()) - path.rmdir() - - # path is a symbolic link - def test_path_is_a_symbolic_link(self): - target_dir = Path('/tmp/targetdir') - symlink_path = Path('/tmp/symlinkdir') - target_dir.mkdir(exist_ok=True) - symlink_path.symlink_to(target_dir) - ensure_directory_exists(symlink_path) - self.assertTrue(symlink_path.exists()) - self.assertTrue(symlink_path.is_symlink()) - symlink_path.unlink() - target_dir.rmdir() diff --git a/code/iottb/test/test_is_ip_address.py b/code/iottb/test/test_is_ip_address.py deleted file mode 100644 index a13c679..0000000 --- a/code/iottb/test/test_is_ip_address.py +++ /dev/null @@ -1,62 +0,0 @@ -from commands.sniff import is_ip_address - -import unittest - - -class TestIsIpAddress(unittest.TestCase): - - def test_valid_ipv4_address_all_octets_in_range(self): - self.assertTrue(is_ip_address("192.168.1.1")) - self.assertTrue(is_ip_address("0.0.0.0")) - self.assertTrue(is_ip_address("255.255.255.255")) - - def test_ipv4_address_with_leading_zeros(self): - self.assertTrue(is_ip_address("192.168.001.001")) - self.assertTrue(is_ip_address("0.0.0.0")) - self.assertTrue(is_ip_address("255.255.255.255")) - - def test_ipv4_address_mixed_single_double_digit_octets(self): - self.assertTrue(is_ip_address("192.168.1.01")) - self.assertTrue(is_ip_address("0.0.0.0")) - self.assertTrue(is_ip_address("255.255.255.255")) - - def test_ipv4_address_maximum_values_in_octets(self): - self.assertTrue(is_ip_address("255.255.255.255")) - self.assertTrue(is_ip_address("0.0.0.0")) - self.assertTrue(is_ip_address("192.168.1.1")) - - - def test_ipv4_address_minimum_values_in_octets(self): - self.assertTrue(is_ip_address("0.0.0.0")) - self.assertTrue(is_ip_address("192.168.1.1")) - self.assertTrue(is_ip_address("255.255.255.255")) - - - def test_ipv4_address_more_than_four_octets_invalid(self): - self.assertFalse(is_ip_address("192.168.1.1.1")) - self.assertFalse(is_ip_address("0.0.0.0.0")) - self.assertFalse(is_ip_address("255.255.255.255.255")) - - - def test_ipv4_address_fewer_than_four_octets_invalid(self): - self.assertFalse(is_ip_address("192.168.1")) - self.assertFalse(is_ip_address("0.0")) - self.assertFalse(is_ip_address("255")) - - - def test_ipv4_address_non_numeric_characters_invalid(self): - self.assertFalse(is_ip_address("192.a.b.c")) - self.assertFalse(is_ip_address("0.x.y.z")) - self.assertFalse(is_ip_address("255.q.w.e")) - - - def test_ipv4_address_octets_out_of_range_invalid(self): - self.assertFalse(is_ip_address("256.256.256.256")) - self.assertFalse(is_ip_address("300.300.300.300")) - self.assertFalse(is_ip_address("999.999.999.999")) - - - def test_ipv4_address_empty_string_invalid(self): - self.assertFalse(is_ip_address("")) - self.assertFalse(is_ip_address(" ")) - self.assertFalse(is_ip_address(None)) diff --git a/code/iottb/test/test_is_mac_address.py b/code/iottb/test/test_is_mac_address.py deleted file mode 100644 index 09e11a2..0000000 --- a/code/iottb/test/test_is_mac_address.py +++ /dev/null @@ -1,64 +0,0 @@ - -from commands.sniff import is_mac_address - -import unittest - -class TestIsMacAddress(unittest.TestCase): - - - def test_valid_mac_address_lowercase(self): - self.assertTrue(is_mac_address("aa:bb:cc:dd:ee:ff")) - self.assertFalse(is_mac_address("192.168.1.1")) - self.assertFalse(is_mac_address("aa:bb:cc:dd:ee:ff:gg")) - - - def test_valid_mac_address_uppercase(self): - self.assertTrue(is_mac_address("AA:BB:CC:DD:EE:FF")) - self.assertFalse(is_mac_address("10.0.0.1")) - self.assertFalse(is_mac_address("AA:BB:CC:DD:EE")) - - - def test_valid_mac_address_mixed_case(self): - self.assertTrue(is_mac_address("Aa:Bb:Cc:Dd:Ee:Ff")) - self.assertFalse(is_mac_address("172.16.0.1")) - self.assertFalse(is_mac_address("Aa:Bb:Cc:Dd:Ee:Ff:Gg")) - - - def test_valid_mac_address_digits(self): - self.assertTrue(is_mac_address("00:11:22:33:44:55")) - self.assertFalse(is_mac_address("8.8.8.8")) - self.assertFalse(is_mac_address("00:11:22:33:44")) - - # returns False for an empty string - def test_empty_string(self): - self.assertFalse(is_mac_address("")) - self.assertFalse(is_mac_address(":")) - - def test_invalid_characters(self): - self.assertFalse(is_mac_address("gh:ij:kl:mn:op:qr")) - self.assertFalse(is_mac_address("192.168.0.256")) - self.assertFalse(is_mac_address("ghij::klmn::opqr")) - - # returns False for a MAC address with incorrect length - def test_incorrect_length(self): - self.assertFalse(is_mac_address("aa:bb:cc")) - self.assertFalse(is_mac_address("10.0.0.256")) - self.assertFalse(is_mac_address("aa::bb::cc::dd::ee::ff::gg")) - - # returns False for a MAC address with missing colons - def test_missing_colons(self): - self.assertFalse(is_mac_address("aabbccddeeff")) - self.assertFalse(is_mac_address("127.0.0.1")) - self.assertFalse(is_mac_address("aabbccddeeffgg")) - - # returns False for a MAC address with extra colons - def test_extra_colons(self): - self.assertFalse(is_mac_address("aa::bb::cc::dd::ee::ff")) - self.assertFalse(is_mac_address("192.168.1.256")) - self.assertFalse(is_mac_address("aa::bb::cc::dd::ee::ff::gg")) - - # returns False for a MAC address with spaces - def test_spaces_in_mac(self): - self.assertFalse(is_mac_address("aa bb cc dd ee ff")) - self.assertFalse(is_mac_address("8.8.4.4")) - self.assertFalse(is_mac_address("aa bb cc dd ee ff gg")) diff --git a/code/iottb/utils/__init__.py b/code/iottb/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/code/iottb/utils/capture_utils.py b/code/iottb/utils/capture_utils.py deleted file mode 100644 index d6d73b3..0000000 --- a/code/iottb/utils/capture_utils.py +++ /dev/null @@ -1,20 +0,0 @@ -from pathlib import Path -from datetime import datetime - - -def get_capture_src_folder(device_path): - today_str = datetime.now().strftime('%Y-%m-%d') - capture_base_path = device_path / today_str - capture_base_path.mkdir(parents=True, exist_ok=True) - - existing_captures = [d for d in capture_base_path.iterdir() if d.is_dir()] - nth_capture = len(existing_captures) + 1 - capture_dir = capture_base_path / f'capture_{nth_capture}' - capture_dir.mkdir(parents=True, exist_ok=True) - - return capture_dir - - -def make_capture_src_folder(capture_src_folder): - capture_src_folder.mkdir(parents=True, exist_ok=True) - return capture_src_folder diff --git a/code/iottb/utils/diagram1.py b/code/iottb/utils/diagram1.py deleted file mode 100644 index 28a9657..0000000 --- a/code/iottb/utils/diagram1.py +++ /dev/null @@ -1,29 +0,0 @@ -import matplotlib.pyplot as plt -import networkx as nx - -# Create the graph -G1 = nx.DiGraph() - -# Add nodes with positions -G1.add_node("IoT Device", pos=(1, 3)) -G1.add_node("AP", pos=(3, 3)) -G1.add_node("Switch (Port Mirroring Enabled)", pos=(5, 3)) -G1.add_node("Gateway Router", pos=(7, 3)) -G1.add_node("Internet", pos=(9, 3)) -G1.add_node("Capture Device", pos=(5, 1)) - -# Add edges -G1.add_edge("IoT Device", "AP") -G1.add_edge("AP", "Switch (Port Mirroring Enabled)") -G1.add_edge("Switch (Port Mirroring Enabled)", "Gateway Router") -G1.add_edge("Gateway Router", "Internet") -G1.add_edge("Switch (Port Mirroring Enabled)", "Capture Device") - -# Draw the graph -pos = nx.get_node_attributes(G1, 'pos') -plt.figure(figsize=(12, 8)) -nx.draw(G1, pos, with_labels=True, node_size=3000, node_color='lightblue', font_size=10, font_weight='bold') -nx.draw_networkx_edge_labels(G1, pos, edge_labels={("Switch (Port Mirroring Enabled)", "Capture Device"): "Mirrored Traffic"}, font_color='red') - -plt.title("IoT Device Connected via AP to Gateway Router via Switch with Port Mirroring Enabled") -plt.show() diff --git a/code/iottb/utils/diagramm2.py b/code/iottb/utils/diagramm2.py deleted file mode 100644 index 4e723da..0000000 --- a/code/iottb/utils/diagramm2.py +++ /dev/null @@ -1,27 +0,0 @@ -import matplotlib.pyplot as plt -import networkx as nx - -# Create the graph -G2 = nx.DiGraph() - -# Add nodes with positions -G2.add_node("IoT Device", pos=(1, 3)) -G2.add_node("Capture Device (Hotspot)", pos=(3, 3)) -G2.add_node("Ethernet Connection", pos=(5, 3)) -G2.add_node("Gateway Router", pos=(7, 3)) -G2.add_node("Internet", pos=(9, 3)) - -# Add edges -G2.add_edge("IoT Device", "Capture Device (Hotspot)") -G2.add_edge("Capture Device (Hotspot)", "Ethernet Connection") -G2.add_edge("Ethernet Connection", "Gateway Router") -G2.add_edge("Gateway Router", "Internet") - -# Draw the graph -pos = nx.get_node_attributes(G2, 'pos') -plt.figure(figsize=(12, 8)) -nx.draw(G2, pos, with_labels=True, node_size=3000, node_color='lightblue', font_size=10, font_weight='bold') -nx.draw_networkx_edge_labels(G2, pos, edge_labels={("Capture Device (Hotspot)", "Ethernet Connection"): "Bridged Traffic"}, font_color='red') - -plt.title("Capture Device Provides Hotspot and Bridges to Ethernet for Internet") -plt.show() diff --git a/code/iottb/utils/file_utils.py b/code/iottb/utils/file_utils.py deleted file mode 100644 index 5ccb42f..0000000 --- a/code/iottb/utils/file_utils.py +++ /dev/null @@ -1,19 +0,0 @@ -import json -from pathlib import Path - - -def load_json_template(template_path): - with open(template_path, 'r') as f: - return json.load(f) - - -def save_json(data, file_path): - with open(file_path, 'w') as f: - json.dump(data, f, indent=4) - - -def ensure_directory_exists(path): - path = Path(path) - if not path.exists(): - path.mkdir(parents=True, exist_ok=True) - return path diff --git a/code/iottb/utils/tcpdump_utils.py b/code/iottb/utils/tcpdump_utils.py deleted file mode 100644 index 53b77e8..0000000 --- a/code/iottb/utils/tcpdump_utils.py +++ /dev/null @@ -1,9 +0,0 @@ -import subprocess - - -def check_installed(): - try: - subprocess.run(['tcpdump', '--version'], check=True, capture_output=True) - return True - except subprocess.CalledProcessError: - return False