├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── dhalsim ├── __init__.py ├── command_line.py ├── init_database.py ├── network_attacks │ ├── __init__.py │ ├── adversarial_models │ │ ├── ctown_attacker_scaler.gz │ │ └── ctown_generator_100_percent │ │ │ ├── fingerprint.pb │ │ │ ├── keras_metadata.pb │ │ │ ├── saved_model.pb │ │ │ └── variables │ │ │ ├── variables.data-00000-of-00001 │ │ │ └── variables.index │ ├── black_box_concealment_attack.py │ ├── concealment_ae_model.py │ ├── concealment_mitm.py │ ├── concealment_netfilter_queue.py │ ├── cppo_server_mitm_attack.py │ ├── enip_cip_parser │ │ ├── __init__.py │ │ ├── cip.py │ │ ├── enip_tcp.py │ │ └── utils.py │ ├── mitm_attack.py │ ├── mitm_netfilter_queue.py │ ├── mitm_netfilter_queue_subprocess.py │ ├── naive_attack.py │ ├── naive_netfilter_queue.py │ ├── replay_mitm.py │ ├── replay_mitm_netfilter_queue.py │ ├── simple_dos_attack.py │ ├── synced_attack.py │ ├── training_data │ │ └── ctown │ │ │ └── training_data.csv │ ├── unconstrained_blackbox_netfilter_queue.py │ └── utilities.py ├── network_events │ ├── __init__.py │ ├── delay_and_loss.py │ ├── network_delay.py │ ├── packet_loss.py │ └── synced_event.py ├── parser │ ├── __init__.py │ ├── antlr │ │ ├── __init__.py │ │ ├── controlsLexer.py │ │ └── controlsParser.py │ ├── config_parser.py │ ├── file_generator.py │ ├── grammars │ │ └── controls.g4 │ └── input_parser.py ├── physical_process.py ├── py3_logger.py └── python2 │ ├── __init__.py │ ├── automatic_attacker.py │ ├── automatic_event.py │ ├── automatic_node.py │ ├── automatic_plant.py │ ├── automatic_plc.py │ ├── automatic_router.py │ ├── automatic_run.py │ ├── automatic_scada.py │ ├── basePLC.py │ ├── entities │ ├── __init__.py │ ├── attack.py │ └── control.py │ ├── generic_plc.py │ ├── generic_scada.py │ └── topo │ ├── __init__.py │ ├── complex_topo.py │ └── simple_topo.py ├── doc ├── .gitignore ├── Makefile ├── attacks.rst ├── conf.py ├── configuration.rst ├── developing.rst ├── events.rst ├── index.rst ├── installation.rst ├── make.bat ├── running.rst ├── static │ ├── complex_topo.svg │ ├── complex_topo_attack.svg │ ├── simple_topo.svg │ └── simple_topo_attack.svg └── user_guide.rst ├── examples ├── anytown_topology │ ├── anytown_concealment_mitm.yaml │ ├── anytown_config.yaml │ ├── anytown_delay_nwk_event.yaml │ ├── anytown_dos.yaml │ ├── anytown_map.inp │ ├── anytown_mitm.yaml │ ├── anytown_naive_mitm.yaml │ ├── anytown_nwk_delay_and_loss.yaml │ ├── anytown_nwk_event.yaml │ ├── anytown_plcs.yaml │ ├── demands_anytown.csv │ └── demands_anytown_small.csv ├── ctown_topology │ ├── ctown_concealment_mitm_path.yaml │ ├── ctown_concealment_mitm_value.yaml │ ├── ctown_config.yaml │ ├── ctown_dos.yaml │ ├── ctown_map.inp │ ├── ctown_mitm.yaml │ ├── ctown_naive_mitm.yaml │ ├── ctown_network_replay.yaml │ ├── ctown_network_replay_conceal.yaml │ ├── ctown_payload_replay_conceal.yaml │ ├── ctown_plcs.yaml │ ├── ctown_unconstrained_blackbox_concealment_mitm.yaml │ └── dataset │ │ ├── dataset_anomaly_config_files │ │ ├── attack_output_29 │ │ │ └── configuration │ │ │ │ └── config.yaml │ │ ├── concealed_values.txt │ │ ├── ctown_config_01.yaml │ │ ├── ctown_config_02.yaml │ │ ├── ctown_config_03.yaml │ │ ├── ctown_config_04.yaml │ │ ├── ctown_config_05.yaml │ │ ├── ctown_config_06.yaml │ │ ├── ctown_config_07.yaml │ │ ├── ctown_config_08.yaml │ │ ├── ctown_config_09.yaml │ │ ├── ctown_config_10.yaml │ │ ├── ctown_config_11.yaml │ │ ├── ctown_config_12.yaml │ │ ├── ctown_config_13.yaml │ │ ├── ctown_config_14.yaml │ │ ├── ctown_config_15.yaml │ │ ├── ctown_config_25.yaml │ │ ├── ctown_config_26.yaml │ │ ├── ctown_config_27.yaml │ │ ├── ctown_config_28.yaml │ │ ├── ctown_config_29.yaml │ │ ├── ctown_config_34.yaml │ │ ├── ctown_config_35.yaml │ │ ├── ctown_config_36.yaml │ │ ├── ctown_config_37.yaml │ │ ├── ctown_config_38.yaml │ │ ├── ctown_config_39.yaml │ │ ├── ctown_config_40.yaml │ │ ├── ctown_config_41.yaml │ │ ├── ctown_config_42.yaml │ │ ├── ctown_config_43.yaml │ │ ├── ctown_config_44.yaml │ │ ├── ctown_config_45.yaml │ │ ├── ctown_config_46.yaml │ │ ├── ctown_map.inp │ │ ├── ctown_plcs.yaml │ │ ├── dataset_attacks │ │ │ ├── attack_01.yaml │ │ │ ├── attack_02.yaml │ │ │ ├── attack_03.yaml │ │ │ ├── attack_04.yaml │ │ │ ├── attack_05.yaml │ │ │ ├── attack_06.yaml │ │ │ ├── attack_07.yaml │ │ │ ├── attack_08.yaml │ │ │ ├── attack_09.yaml │ │ │ ├── attack_10.yaml │ │ │ ├── attack_11.yaml │ │ │ ├── attack_12.yaml │ │ │ ├── attack_13.yaml │ │ │ ├── attack_14.yaml │ │ │ ├── attack_15.yaml │ │ │ ├── attack_25.yaml │ │ │ ├── attack_26.yaml │ │ │ ├── attack_27.yaml │ │ │ ├── attack_28.yaml │ │ │ ├── attack_29.yaml │ │ │ ├── attack_34.yaml │ │ │ ├── attack_35.yaml │ │ │ ├── attack_35_short.yaml │ │ │ ├── attack_36.yaml │ │ │ ├── attack_37.yaml │ │ │ ├── attack_38.yaml │ │ │ ├── attack_39.yaml │ │ │ ├── attack_40.yaml │ │ │ ├── attack_41.yaml │ │ │ ├── attack_42.yaml │ │ │ ├── attack_43.yaml │ │ │ ├── attack_44.yaml │ │ │ ├── attack_45.yaml │ │ │ └── attack_46.yaml │ │ └── events │ │ │ ├── delay_ctown_01.csv │ │ │ ├── demands_ctown_01.csv │ │ │ ├── loss_ctown_01.csv │ │ │ └── tanks_ctown_01.csv │ │ └── dataset_normal_operating_conditions │ │ ├── create_files.sh │ │ ├── ctown_config.yaml │ │ ├── ctown_map.inp │ │ ├── ctown_plcs.yaml │ │ ├── initial_tank_levels.csv │ │ ├── master_script.sh │ │ ├── network_delay_data.csv │ │ └── network_loss_data.csv ├── example │ └── example_attack.yaml ├── ky13_topology │ ├── ky13.inp │ ├── ky13_config.yaml │ └── ky13_plcs.yaml ├── ky_14_topology │ ├── ky14.inp │ ├── ky14_config.yaml │ └── ky14_plcs.yaml ├── ky_15_topology │ ├── ky15.inp │ ├── ky15_config.yaml │ ├── ky15_plcs_conf1.yaml │ ├── ky15_plcs_conf2.yaml │ ├── ky15_plcs_conf3.yaml │ ├── ky15_plcs_conf4.yaml │ └── ky15_plcs_original.yaml ├── minitown_topology │ ├── initial_tank.csv │ ├── minitown_attacks.yaml │ ├── minitown_config.yaml │ ├── minitown_map.inp │ └── minitown_plcs.yaml └── wadi_topology │ ├── delays_wadi.csv │ ├── demand_patterns │ ├── 0.csv │ ├── 1.csv │ └── 2.csv │ ├── initial_tank_wadi.csv │ ├── losses_wadi.csv │ ├── readme.md │ ├── wadi_attacks.yaml │ ├── wadi_config.yaml │ ├── wadi_map.inp │ └── wadi_plcs.yaml ├── install.sh ├── pyproject.toml ├── pytest.ini ├── requirements-dev.txt ├── requirements.txt ├── setup.py ├── sonar-project.properties └── test ├── __init__.py ├── auxilary_testing_files ├── automatic_attacker_files.yaml ├── intermediate-wadi-attack.yaml ├── intermediate-wadi-pda-original.yaml ├── intermediate.yaml ├── intermediate_yaml_network_attacks.yaml ├── wadi_attacks.yaml ├── wadi_config.yaml ├── wadi_map_pda_original.inp ├── wadi_map_pda_original_no_controls.inp └── wadi_plc.yaml ├── network_attacks ├── __init__.py ├── test_mitm_attack.py ├── test_naive_mitm_attack.py ├── test_synced_attack.py └── test_utilities.py ├── parser ├── __init__.py ├── test_batch_readme_generator.py ├── test_config_parser.py ├── test_config_schema.py ├── test_general_readme_generator.py └── test_inp_parser.py ├── physical_process ├── __init__.py └── test_physical_process.py └── python2 ├── __init__.py ├── attacks ├── __init__.py └── test_attacks.py ├── automatic_nodes ├── __init__.py ├── test_automatic_attacker.py ├── test_automatic_plant.py ├── test_automatic_plc.py └── test_automatic_scada.py ├── controls ├── __init__.py └── test_controls.py ├── test_automatic_run.py ├── test_generic_plc.py ├── test_generic_plc_cache.py ├── test_generic_plc_db.py ├── test_generic_scada.py ├── test_generic_scada_db.py └── topo ├── __init__.py ├── test_complex_topo.py ├── test_complex_topo_with_attackers.py ├── test_simple_topo.py └── test_simple_topo_with_attackers.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Declare files that will always have CRLF line endings on checkout. 2 | *.sln text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Python template 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # Installer logs 31 | pip-log.txt 32 | pip-delete-this-directory.txt 33 | 34 | # Unit test / coverage reports 35 | htmlcov/ 36 | .tox/ 37 | .nox/ 38 | .coverage 39 | .coverage.* 40 | .cache 41 | nosetests.xml 42 | coverage.xml 43 | coverage*.xml 44 | *.cover 45 | *.py,cover 46 | .hypothesis/ 47 | .pytest_cache/ 48 | cover/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 55 | __pypackages__/ 56 | 57 | # Environments 58 | .env 59 | .venv 60 | env/ 61 | venv/ 62 | ENV/ 63 | env.bak/ 64 | venv.bak/ 65 | 66 | 67 | ### JupyterNotebooks template 68 | # gitignore template for Jupyter Notebooks 69 | # website: http://jupyter.org/ 70 | 71 | .ipynb_checkpoints 72 | */.ipynb_checkpoints/* 73 | 74 | # IPython 75 | profile_default/ 76 | ipython_config.py 77 | 78 | # Remove previous ipynb_checkpoints 79 | # git rm -r .ipynb_checkpoints/ 80 | 81 | builds/ 82 | 83 | # Default DHALSIM output folder 84 | output/ 85 | logs/ 86 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Andrés F. Murillo, Robert van Dijk, Luc Jonker, Simcha Vos, Maarten Weyns, Davide Salaorni 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Digital HydrAuLic SIMulator (DHALSIM) 2 | _A Digital Twin for Water Distribution Systems. A work by the SUTD Critical Infrastructure Systems Lab, TU Delft Department of Water Management, CISPA, and iTrust_ 3 | 4 | DHALSIM uses the [WNTR](https://wntr.readthedocs.io/en/latest/index.html) EPANET wrapper to simulate the behaviour of water distribution systems. In addition, DHALSIM uses Mininet and MiniCPS to emulate the behavior of industrial control system controlling a water distribution system. This means that in addition to physical data, DHALSIM can also provide network captures of the PLCs, SCADA server, and other network and industrial devices present in the a water distribution system. 5 | 6 | DHALSIM was presented in the ICSS Workshop in ACSAC'20, with the paper: [Co-Simulating Physical Processes and Network Data for High-Fidelity Cyber-Security Experiments](https://dl.acm.org/doi/abs/10.1145/3442144.3442147) 7 | 8 | Two papers in the Journal of Water Resources Planning and Management explain in detail DHALSIM architecture, features, and capabilities: [High-fidelity cyber and physical simulation of water distribution systems. I: Models and Data](https://ascelibrary.org/doi/abs/10.1061/JWRMD5.WRENG-5853) and [High-fidelity cyber and physical simulation of water distribution systems. II: Enabling cyber-physical attack localization](https://ascelibrary.org/doi/abs/10.1061/JWRMD5.WRENG-5854) 9 | 10 | ## Installation 11 | 12 | In order to offer a simple installation we have included an installation script which will install DHALSIM on an Ubuntu 20.04 machine. This script is located in the root of the repository and can be run with ```./install.sh```. 13 | 14 | ## Running 15 | 16 | DHALSIM can be run using the command ```sudo dhalsim path/to/config.yaml```. 17 | 18 | Replacing the text between "< >" with the path to one example topology or your own configuration files. For example, for the anytown example, you'd use: 19 | ```sudo dhalsim ``` -------------------------------------------------------------------------------- /dhalsim/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/__init__.py -------------------------------------------------------------------------------- /dhalsim/command_line.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os.path 3 | import signal 4 | import subprocess 5 | import sys 6 | import time 7 | from pathlib import Path 8 | 9 | import yaml 10 | 11 | from dhalsim.init_database import DatabaseInitializer 12 | from dhalsim.parser.config_parser import ConfigParser 13 | from dhalsim.parser.file_generator import InputFilesCopier 14 | 15 | 16 | def is_valid_file(parser, arg): 17 | if not os.path.exists(arg): 18 | parser.error(arg + " does not exist.") 19 | else: 20 | return arg 21 | 22 | 23 | class Runner(): 24 | def __init__(self, config_file, output_folder): 25 | self.config_file = config_file 26 | self.output_folder = output_folder 27 | 28 | signal.signal(signal.SIGINT, self.sigint_handler) 29 | signal.signal(signal.SIGTERM, self.sigint_handler) 30 | 31 | self.automatic_run = None 32 | 33 | def sigint_handler(self, sig, frame): 34 | os.kill(self.automatic_run.pid, signal.SIGTERM) 35 | time.sleep(0.3) 36 | sys.exit(0) 37 | 38 | def run(self): 39 | config_parser = ConfigParser(self.config_file) 40 | 41 | if config_parser.batch_mode: 42 | # If in batch mode, generate all intermediate yamls and simulate one by one 43 | yaml_paths = [] 44 | for batch_index in range(config_parser.batch_simulations): 45 | config_parser.batch_index = batch_index 46 | yaml_paths.append(config_parser.generate_intermediate_yaml()) 47 | for yaml_path in yaml_paths: 48 | self.run_simulation(yaml_path) 49 | 50 | else: 51 | # Else generate the one we need and run the simulation 52 | intermediate_yaml_path = config_parser.generate_intermediate_yaml() 53 | self.run_simulation(intermediate_yaml_path) 54 | 55 | def run_simulation(self, intermediate_yaml_path): 56 | 57 | subprocess.run(["sudo", "pkill - f - u", "root", "python -m cpppo.server.enip"]) 58 | subprocess.run(["sudo", "mn", "-c"]) 59 | 60 | InputFilesCopier(self.config_file, intermediate_yaml_path).copy_input_files() 61 | 62 | db_initializer = DatabaseInitializer(intermediate_yaml_path) 63 | db_initializer.drop() 64 | db_initializer.write() 65 | db_initializer.print() 66 | automatic_run_path = Path(__file__).parent.absolute() / "python2" / "automatic_run.py" 67 | self.automatic_run = subprocess.Popen( 68 | ["python3", str(automatic_run_path), str(intermediate_yaml_path)]) 69 | self.automatic_run.wait() 70 | 71 | def main(): 72 | parser = argparse.ArgumentParser(description='Executes DHALSIM based on a config file') 73 | parser.add_argument(dest="config_file", 74 | help="config file and its path", metavar="FILE", 75 | type=lambda x: is_valid_file(parser, x)) 76 | parser.add_argument('-o', '--output', dest='output_folder', metavar="FOLDER", 77 | help='folder where output files will be saved', type=str) 78 | 79 | args = parser.parse_args() 80 | 81 | config_file = Path(args.config_file) 82 | output_folder = Path(args.output_folder if args.output_folder else "output") 83 | 84 | runner = Runner(config_file, output_folder) 85 | runner.run() 86 | 87 | 88 | if __name__ == '__main__': 89 | main() 90 | -------------------------------------------------------------------------------- /dhalsim/network_attacks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/network_attacks/__init__.py -------------------------------------------------------------------------------- /dhalsim/network_attacks/adversarial_models/ctown_attacker_scaler.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/network_attacks/adversarial_models/ctown_attacker_scaler.gz -------------------------------------------------------------------------------- /dhalsim/network_attacks/adversarial_models/ctown_generator_100_percent/fingerprint.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/network_attacks/adversarial_models/ctown_generator_100_percent/fingerprint.pb -------------------------------------------------------------------------------- /dhalsim/network_attacks/adversarial_models/ctown_generator_100_percent/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/network_attacks/adversarial_models/ctown_generator_100_percent/keras_metadata.pb -------------------------------------------------------------------------------- /dhalsim/network_attacks/adversarial_models/ctown_generator_100_percent/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/network_attacks/adversarial_models/ctown_generator_100_percent/saved_model.pb -------------------------------------------------------------------------------- /dhalsim/network_attacks/adversarial_models/ctown_generator_100_percent/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/network_attacks/adversarial_models/ctown_generator_100_percent/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /dhalsim/network_attacks/adversarial_models/ctown_generator_100_percent/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/network_attacks/adversarial_models/ctown_generator_100_percent/variables/variables.index -------------------------------------------------------------------------------- /dhalsim/network_attacks/enip_cip_parser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/network_attacks/enip_cip_parser/__init__.py -------------------------------------------------------------------------------- /dhalsim/network_attacks/enip_cip_parser/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Copyright (c) 2015 Nicolas Iooss, SUTD 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | """Useful routines and utilities which simplify code writing""" 23 | from scapy import all as scapy_all 24 | 25 | 26 | def hexdump(data, columns=16, indentlvl=""): 27 | """Return the hexadecimal representation of the data""" 28 | 29 | def do_line(line): 30 | return ( 31 | indentlvl + 32 | " ".join("{:02x}".format(ord(b)) for b in line) + 33 | " " * (columns - len(line)) + 34 | " " + 35 | "".join(b if 32 <= ord(b) < 127 else "." for b in line)) 36 | 37 | return "\n".join(do_line(data[i:i + columns]) for i in range(0, len(data), columns)) 38 | 39 | 40 | class LEShortLenField(scapy_all.FieldLenField): 41 | """A len field in a 2-byte integer""" 42 | 43 | def __init__(self, name, default, count_of=None, length_of=None): 44 | scapy_all.FieldLenField.__init__(self, name, default, fmt=" skip; 6 | ANY_SPACE : ' '+; 7 | 8 | /** States */ 9 | STATE : (OPEN | CLOSED | VALUE) ; 10 | OPEN : 'OPEN'; 11 | CLOSED : 'CLOSED'; 12 | 13 | 14 | /** Conditions */ 15 | CONDITION : (BELOW | ABOVE) ; 16 | BELOW : 'BELOW'; 17 | ABOVE : 'ABOVE'; 18 | 19 | /** Values */ 20 | VALUE : [0-9]*'.'*[0-9]+; 21 | 22 | ID : [a-zA-Z0-9_]+ ; 23 | CAPITALS : [A-Z].*? ; 24 | 25 | /** Skip all before [CONTROLS] section */ 26 | CONTROLS_HEADER : '[CONTROLS]'; 27 | PRECONTORLS : .*?CONTROLS_HEADER -> skip; 28 | 29 | /** Skip all other sections */ 30 | POSTCONTROLS : '['CAPITALS']'.*? -> skip; 31 | 32 | /** Skip over comments */ 33 | COMMENT : ';'.*?NEWLINES -> skip; 34 | 35 | /** Skip newlines, whitespaces */ 36 | NEWLINES : [\n]+ -> skip ; 37 | WS : [ \t\r\n]+ -> skip ; 38 | 39 | nodeControl : 'LINK' ANY_SPACE ID ANY_SPACE STATE ANY_SPACE 'IF' ANY_SPACE 'NODE' ANY_SPACE ID ANY_SPACE CONDITION ANY_SPACE STATE; 40 | timeControl : 'LINK' ANY_SPACE ID ANY_SPACE STATE ANY_SPACE 'AT' ANY_SPACE 'TIME' ANY_SPACE STATE; 41 | controls : (nodeControl | timeControl)*; -------------------------------------------------------------------------------- /dhalsim/py3_logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | 4 | 5 | def get_logger(logging_level): 6 | """ 7 | Gets a python3 logger, sets the format and logging level 8 | 9 | :param logging_level: logging level of logger 10 | """ 11 | switcher = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 12 | 'error': logging.ERROR, 'critical': logging.CRITICAL} 13 | 14 | log_level = switcher.get(logging_level) 15 | py3_logger = logging.getLogger('py3_logger') 16 | # Check if the logger has already been configured 17 | if len(py3_logger.handlers) > 0: 18 | return py3_logger 19 | # Configure logger level 20 | py3_handler = logging.StreamHandler(stream=sys.stdout) 21 | 22 | # Print originating filename only in debug mode 23 | if log_level == logging.DEBUG: 24 | logging_format = logging\ 25 | .Formatter('%(asctime)s - %(levelname)s @ %(filename)s: %(message)s', '%H:%M:%S') 26 | else: 27 | logging_format = logging.Formatter('%(asctime)s - %(levelname)s: %(message)s', '%H:%M:%S') 28 | 29 | py3_handler.setFormatter(logging_format) 30 | py3_logger.addHandler(py3_handler) 31 | # Set level of logger 32 | py3_logger.setLevel(log_level) 33 | return py3_logger 34 | -------------------------------------------------------------------------------- /dhalsim/python2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/python2/__init__.py -------------------------------------------------------------------------------- /dhalsim/python2/automatic_event.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import signal 4 | import subprocess 5 | import sys 6 | from dhalsim.py3_logger import get_logger 7 | from pathlib import Path 8 | 9 | from automatic_node import NodeControl 10 | 11 | 12 | class Error(Exception): 13 | """Base class for exceptions in this module.""" 14 | 15 | 16 | class NoSuchEvent(Error): 17 | """Raised when an undefined event is configured or the configuration file is empty""" 18 | 19 | 20 | class EventControl(NodeControl): 21 | """This class is started for an event. It starts a network event.""" 22 | 23 | def __init__(self, intermediate_yaml, event_index, interface): 24 | super(EventControl, self).__init__(intermediate_yaml) 25 | self.event_index = event_index 26 | self.interface_name = interface 27 | self.output_path = Path(self.data["output_path"]) 28 | self.tcp_dump_process = None 29 | self.attacker_process = None 30 | self.this_event_data = self.data["network_events"][self.event_index] 31 | self.logger = get_logger(self.data['log_level']) 32 | self.logger.debug('Network event index: ' + str(event_index)) 33 | self.logger.debug('Interface name: ' + str(self.interface_name)) 34 | 35 | def terminate(self): 36 | """This function stops the event process.""" 37 | 38 | self.logger.debug("Terminating event process") 39 | self.event_process.send_signal(signal.SIGINT) 40 | self.event_process.wait() 41 | if self.event_process.poll() is None: 42 | self.event_process.terminate() 43 | if self.event_process.poll() is None: 44 | self.event_process.kill() 45 | 46 | def main(self): 47 | """This function starts event process and then waits for the network event to finish.""" 48 | 49 | self.event_process = self.start_event() 50 | 51 | while self.event_process.poll() is None: 52 | pass 53 | 54 | self.terminate() 55 | 56 | def start_event(self): 57 | """Starts the event process.""" 58 | generic_event = None 59 | if self.this_event_data['type'] == 'packet_loss': 60 | generic_event = Path(__file__).parent.parent.absolute() / "network_events" / "packet_loss.py" 61 | elif self.this_event_data['type'] == 'network_delay': 62 | generic_event = Path(__file__).parent.parent.absolute() / "network_events" / "network_delay.py" 63 | elif self.this_event_data['type'] == 'network_delay_loss': 64 | generic_event = Path(__file__).parent.parent.absolute() / "network_events" / "delay_and_loss.py" 65 | else: 66 | raise NoSuchEvent("Event {event} does not exists.".format(event=self.this_event_data['type'])) 67 | 68 | cmd = ["python3", str(generic_event), str(self.intermediate_yaml), str(self.event_index), self.interface_name] 69 | 70 | # Network events are run at switches 71 | switch_process = subprocess.Popen(cmd, shell=False, stderr=sys.stderr, stdout=sys.stdout) 72 | return switch_process 73 | 74 | 75 | def is_valid_file(parser_instance, arg): 76 | """Verifies whether the intermediate yaml path is valid.""" 77 | if not os.path.exists(arg): 78 | parser_instance.error(arg + " does not exist") 79 | else: 80 | return arg 81 | 82 | 83 | if __name__ == "__main__": 84 | parser = argparse.ArgumentParser(description='Start everything for a network event') 85 | parser.add_argument(dest="intermediate_yaml", 86 | help="intermediate yaml file", metavar="FILE", 87 | type=lambda x: is_valid_file(parser, x)) 88 | parser.add_argument(dest="index", help="Index of PLC in intermediate yaml", type=int, 89 | metavar="N") 90 | parser.add_argument(dest="interface", help="Interface name of the network event") 91 | 92 | args = parser.parse_args() 93 | event_control = EventControl(Path(args.intermediate_yaml), args.index, args.interface) 94 | event_control.main() -------------------------------------------------------------------------------- /dhalsim/python2/automatic_node.py: -------------------------------------------------------------------------------- 1 | import signal 2 | import sys 3 | from abc import ABCMeta, abstractmethod 4 | 5 | import yaml 6 | 7 | 8 | class NodeControl: 9 | """ 10 | This class is started for a node. It can start all the subprocess and terminate them again. 11 | """ 12 | __metaclass__ = ABCMeta 13 | 14 | def __init__(self, intermediate_yaml): 15 | signal.signal(signal.SIGINT, self.sigint_handler) 16 | signal.signal(signal.SIGTERM, self.sigint_handler) 17 | 18 | self.intermediate_yaml = intermediate_yaml 19 | 20 | with self.intermediate_yaml.open(mode='r') as file: 21 | self.data = yaml.safe_load(file) 22 | 23 | def sigint_handler(self, sig, frame): 24 | """ 25 | Interrupt handler for :class:`~signal.SIGINT` and :class:`~signal.SIGINT`. 26 | """ 27 | self.terminate() 28 | sys.exit(0) 29 | 30 | @abstractmethod 31 | def terminate(self): 32 | """ 33 | This function should stop all the child processes. Its also called on the interrupt. 34 | """ 35 | 36 | @abstractmethod 37 | def main(self): 38 | """ 39 | This function should start all the child processes. 40 | """ 41 | -------------------------------------------------------------------------------- /dhalsim/python2/automatic_plant.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import signal 4 | import subprocess 5 | from pathlib import Path 6 | 7 | from automatic_node import NodeControl 8 | from dhalsim.py3_logger import get_logger 9 | 10 | 11 | class Error(Exception): 12 | """Base class for exceptions in this module.""" 13 | 14 | 15 | class UnsupportedSimulator(Error): 16 | """Raised when an unsupported simulator is launched""" 17 | 18 | 19 | class PlantControl(NodeControl): 20 | """ 21 | This class is started for a plant. It starts a simulation process. 22 | """ 23 | 24 | def __init__(self, intermediate_yaml): 25 | super(PlantControl, self).__init__(intermediate_yaml) 26 | 27 | self.logger = get_logger(self.data['log_level']) 28 | 29 | self.simulation_process = None 30 | 31 | def terminate(self): 32 | """ 33 | This function stops the physical process (child of this process). 34 | """ 35 | self.logger.debug("Stopping plant.") 36 | if self.simulation_process.poll() is None: 37 | self.simulation_process.send_signal(signal.SIGINT) 38 | self.simulation_process.wait() 39 | if self.simulation_process.poll() is None: 40 | self.simulation_process.terminate() 41 | if self.simulation_process.poll() is None: 42 | self.simulation_process.kill() 43 | self.logger.debug("Plant process stopped.") 44 | 45 | def main(self): 46 | """ 47 | This function starts the physical process and then waits for the physical 48 | process to finish. 49 | """ 50 | 51 | if self.data['simulator'] == 'wntr' or self.data['simulator'] == 'epynet': 52 | physical_process_path = Path(__file__).parent.absolute().parent / "physical_process.py" 53 | else: 54 | raise UnsupportedSimulator('Supported simulators are wntr, epynet') 55 | 56 | cmd = ["python3", str(physical_process_path), str(self.intermediate_yaml)] 57 | 58 | self.simulation_process = subprocess.Popen(cmd) 59 | 60 | while self.simulation_process.poll() is None: 61 | pass 62 | 63 | self.terminate() 64 | 65 | 66 | def is_valid_file(parser_instance, arg): 67 | """ 68 | Verifies whether the intermediate yaml path is valid. 69 | """ 70 | if not os.path.exists(arg): 71 | parser_instance.error(arg + " does not exist.") 72 | else: 73 | return arg 74 | 75 | 76 | if __name__ == "__main__": 77 | parser = argparse.ArgumentParser(description='Run a automatic plant') 78 | parser.add_argument(dest="intermediate_yaml", 79 | help="intermediate yaml file", metavar="FILE", 80 | type=lambda x: is_valid_file(parser, x)) 81 | 82 | args = parser.parse_args() 83 | plant_control = PlantControl(Path(args.intermediate_yaml)) 84 | plant_control.main() 85 | -------------------------------------------------------------------------------- /dhalsim/python2/automatic_plc.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import signal 4 | import subprocess 5 | import sys 6 | from pathlib import Path 7 | 8 | from automatic_node import NodeControl 9 | from dhalsim.py3_logger import get_logger 10 | 11 | empty_loc = '/dev/null' 12 | 13 | 14 | class PlcControl(NodeControl): 15 | """ 16 | This class is started for a plc. It starts a tcpdump and a plc process. 17 | """ 18 | 19 | PROCESS_TIMEOUT = 0.3 20 | """Timeout between sending SIGINT, SIGTERM, and a SIGKILL""" 21 | 22 | def __init__(self, intermediate_yaml, plc_index): 23 | super(PlcControl, self).__init__(intermediate_yaml) 24 | 25 | self.logger = get_logger(self.data['log_level']) 26 | 27 | self.plc_index = plc_index 28 | self.output_path = Path(self.data["output_path"]) 29 | self.process_tcp_dump = None 30 | self.plc_process = None 31 | self.this_plc_data = self.data["plcs"][self.plc_index] 32 | 33 | def terminate_process(self, process): 34 | if process.poll() is None: 35 | process.send_signal(signal.SIGINT) 36 | try: 37 | process.wait(self.PROCESS_TIMEOUT) 38 | except subprocess.TimeoutExpired: 39 | if process.poll() is None: 40 | process.terminate() 41 | if process.poll() is None: 42 | process.kill() 43 | 44 | def terminate(self): 45 | """ 46 | This function stops the tcp dump and the plc process. 47 | """ 48 | self.logger.debug("Stopping tcpdump process on PLC.") 49 | self.terminate_process(self.process_tcp_dump) 50 | self.logger.debug("tcpdump process on PLC stopped.") 51 | 52 | self.logger.debug("Stopping PLC.") 53 | self.terminate_process(self.plc_process) 54 | self.logger.debug("PLC stopped on automatic PLC.") 55 | 56 | def main(self): 57 | """ 58 | This function starts the tcp dump and plc process and then waits for the plc 59 | process to finish. 60 | """ 61 | self.process_tcp_dump = self.start_tcpdump_capture() 62 | 63 | self.plc_process = self.start_plc() 64 | while self.plc_process.poll() is None: 65 | pass 66 | self.terminate() 67 | 68 | def start_tcpdump_capture(self): 69 | """ 70 | Start a tcp dump. 71 | """ 72 | pcap = self.output_path / (self.this_plc_data["interface"] + '.pcap') 73 | 74 | # Output is not printed to console 75 | no_output = open(empty_loc, 'w') 76 | tcp_dump = subprocess.Popen(['tcpdump', '-i', self.this_plc_data["interface"], '-w', 77 | str(pcap)], shell=False, stderr=no_output, stdout=no_output) 78 | return tcp_dump 79 | 80 | def start_plc(self): 81 | """ 82 | Start a plc process. 83 | """ 84 | generic_plc_path = Path(__file__).parent.absolute() / "generic_plc.py" 85 | 86 | if self.data['log_level'] == 'debug': 87 | err_put = sys.stderr 88 | out_put = sys.stdout 89 | else: 90 | err_put = open(empty_loc, 'w') 91 | out_put = open(empty_loc, 'w') 92 | cmd = ["python3", str(generic_plc_path), str(self.intermediate_yaml), str(self.plc_index)] 93 | plc_process = subprocess.Popen(cmd, shell=False, stderr=err_put, stdout=out_put) 94 | return plc_process 95 | 96 | 97 | def is_valid_file(parser_instance, arg): 98 | """ 99 | Verifies whether the intermediate yaml path is valid. 100 | """ 101 | if not os.path.exists(arg): 102 | parser_instance.error(arg + " does not exist.") 103 | else: 104 | return arg 105 | 106 | 107 | if __name__ == "__main__": 108 | parser = argparse.ArgumentParser(description='Start everything for a plc') 109 | parser.add_argument(dest="intermediate_yaml", 110 | help="intermediate yaml file", metavar="FILE", 111 | type=lambda x: is_valid_file(parser, x)) 112 | parser.add_argument(dest="index", help="Index of PLC in intermediate yaml", type=int, 113 | metavar="N") 114 | 115 | args = parser.parse_args() 116 | plc_control = PlcControl(Path(args.intermediate_yaml), args.index) 117 | plc_control.main() 118 | -------------------------------------------------------------------------------- /dhalsim/python2/automatic_router.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import signal 4 | import subprocess 5 | import sys 6 | from pathlib import Path 7 | 8 | from automatic_node import NodeControl 9 | from dhalsim.py3_logger import get_logger 10 | 11 | empty_loc = '/dev/null' 12 | 13 | 14 | class RouterControl(NodeControl): 15 | """ 16 | This class is started for a router. It starts a tcpdump process. 17 | """ 18 | 19 | PROCESS_TIMEOUT = 0.3 20 | """Timeout between sending SIGINT, SIGTERM, and a SIGKILL""" 21 | 22 | def __init__(self, intermediate_yaml, router_name): 23 | super(RouterControl, self).__init__(intermediate_yaml) 24 | 25 | self.output_path = Path(self.data["output_path"]) 26 | self.logger = get_logger(self.data['log_level']) 27 | self.process_tcp_dump = None 28 | self.router_name = router_name 29 | self.interface_name = str(router_name) + '-eth0' 30 | 31 | def terminate(self): 32 | """ 33 | This function stops the tcp dump and the plc process. 34 | """ 35 | self.logger.debug("Stopping Router tcpdump process.") 36 | 37 | if self.process_tcp_dump.poll() is None: 38 | self.process_tcp_dump.send_signal(signal.SIGINT) 39 | 40 | try: 41 | self.process_tcp_dump.wait(self.PROCESS_TIMEOUT) 42 | except subprocess.TimeoutExpired: 43 | if self.process_tcp_dump.poll() is None: 44 | self.process_tcp_dump.terminate() 45 | if self.process_tcp_dump.poll() is None: 46 | self.process_tcp_dump.kill() 47 | 48 | self.logger.debug("Router tcpdump process finished.") 49 | 50 | def main(self): 51 | """ 52 | This function starts the tcp dump and plc process and then waits for the plc 53 | process to finish. 54 | """ 55 | 56 | self.process_tcp_dump = self.start_tcpdump_capture() 57 | 58 | while True: 59 | pass 60 | 61 | def start_tcpdump_capture(self): 62 | """ 63 | Start a tcp dump. 64 | """ 65 | pcap = self.output_path / (str(self.interface_name) + '.pcap') 66 | 67 | # Output is not printed to console 68 | no_output = open(empty_loc, 'w') 69 | cmd = ['tcpdump', '-i', self.interface_name, '-w', str(pcap)] 70 | tcp_dump = subprocess.Popen(cmd, shell=False, stderr=no_output, stdout=no_output) 71 | 72 | return tcp_dump 73 | 74 | 75 | def is_valid_file(parser_instance, arg): 76 | """ 77 | Verifies whether the intermediate yaml path is valid. 78 | """ 79 | if not os.path.exists(arg): 80 | parser_instance.error(arg + " does not exist.") 81 | else: 82 | return arg 83 | 84 | 85 | if __name__ == "__main__": 86 | parser = argparse.ArgumentParser(description='Start everything for a plc') 87 | parser.add_argument(dest="intermediate_yaml", 88 | help="intermediate yaml file", metavar="FILE", 89 | type=lambda x: is_valid_file(parser, x)) 90 | parser.add_argument(dest="name", help="Index of PLC in intermediate yaml") 91 | 92 | args = parser.parse_args() 93 | router_control = RouterControl(Path(args.intermediate_yaml), args.name) 94 | router_control.main() 95 | -------------------------------------------------------------------------------- /dhalsim/python2/automatic_scada.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import signal 4 | import subprocess 5 | import sys 6 | from pathlib import Path 7 | 8 | from automatic_node import NodeControl 9 | from dhalsim.py3_logger import get_logger 10 | 11 | empty_loc = '/dev/null' 12 | 13 | 14 | class ScadaControl(NodeControl): 15 | """ 16 | This class is started for a scada. It starts a tcpdump and a scada process. 17 | """ 18 | 19 | PROCESS_TIMEOUT = 1.0 20 | """Timeout between sending SIGINT, SIGTERM, and a SIGKILL""" 21 | 22 | def __init__(self, intermediate_yaml): 23 | super(ScadaControl, self).__init__(intermediate_yaml) 24 | 25 | self.logger = get_logger(self.data['log_level']) 26 | 27 | self.output_path = Path(self.data["output_path"]) 28 | self.process_tcp_dump = None 29 | self.scada_process = None 30 | 31 | def terminate_process(self, process): 32 | if process.poll() is None: 33 | process.send_signal(signal.SIGINT) 34 | try: 35 | process.wait(self.PROCESS_TIMEOUT) 36 | except subprocess.TimeoutExpired: 37 | if process.poll() is None: 38 | process.terminate() 39 | if process.poll() is None: 40 | process.kill() 41 | 42 | def terminate(self): 43 | """ 44 | This function stops the tcp dump and the plc process. 45 | """ 46 | self.logger.debug("Stopping tcpdump process on SCADA.") 47 | self.terminate_process(self.process_tcp_dump) 48 | self.logger.debug("Tcpdump process stopped on SCADA.") 49 | 50 | self.logger.debug("Stopping SCADA.") 51 | self.terminate_process(self.scada_process) 52 | self.logger.debug("SCADA stopped on automatic SCADA") 53 | 54 | def main(self): 55 | """ 56 | This function starts the tcp dump and scada process and then waits for the scada 57 | process to finish. 58 | """ 59 | self.process_tcp_dump = self.start_tcpdump_capture() 60 | 61 | self.scada_process = self.start_scada() 62 | 63 | while self.scada_process.poll() is None: 64 | pass 65 | 66 | self.terminate() 67 | 68 | def start_tcpdump_capture(self): 69 | """ 70 | Start a tcp dump. 71 | """ 72 | pcap = self.output_path / "scada-eth0.pcap" 73 | 74 | # Output is not printed to console 75 | no_output = open(empty_loc, 'w') 76 | tcp_dump = subprocess.Popen(['tcpdump', '-i', self.data["scada"]["interface"], '-w', 77 | str(pcap)], shell=False, stdout=no_output, stderr=no_output) 78 | return tcp_dump 79 | 80 | def start_scada(self): 81 | """ 82 | Start a scada process. 83 | """ 84 | generic_scada_path = Path(__file__).parent.absolute() / "generic_scada.py" 85 | 86 | if self.data['log_level'] == 'debug': 87 | err_put = sys.stderr 88 | out_put = sys.stdout 89 | else: 90 | err_put = open(empty_loc, 'w') 91 | out_put = open(empty_loc, 'w') 92 | cmd = ["python3", str(generic_scada_path), str(self.intermediate_yaml)] 93 | scada_process = subprocess.Popen(cmd, shell=False, stderr=err_put, stdout=out_put) 94 | return scada_process 95 | 96 | 97 | def is_valid_file(parser_instance, arg): 98 | """ 99 | Verifies whether the intermediate yaml path is valid. 100 | """ 101 | if not os.path.exists(arg): 102 | parser_instance.error(arg + " does not exist.") 103 | else: 104 | return arg 105 | 106 | 107 | if __name__ == "__main__": 108 | parser = argparse.ArgumentParser(description='Start everything for a plc') 109 | parser.add_argument(dest="intermediate_yaml", 110 | help="intermediate yaml file", metavar="FILE", 111 | type=lambda x: is_valid_file(parser, x)) 112 | 113 | args = parser.parse_args() 114 | scada = ScadaControl(Path(args.intermediate_yaml)) 115 | scada.main() 116 | -------------------------------------------------------------------------------- /dhalsim/python2/basePLC.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from minicps.devices import PLC 3 | 4 | 5 | class BasePLC(PLC): 6 | 7 | # Pulls a fresh value from the local DB and updates the local CPPPO 8 | def send_system_state(self): 9 | values = [] 10 | # Send sensor values (may have gaussian noise) 11 | for tag in self.sensors: 12 | # noinspection PyBroadException 13 | try: 14 | # Gaussian noise added with respect to noise_scale 15 | if self.noise_scale != 0: 16 | sensor_value = float(self.get(tag)) 17 | noise_value = np.random.normal(0, self.noise_scale*sensor_value) 18 | values.append(sensor_value + noise_value) 19 | else: 20 | values.append(float(self.get(tag))) 21 | except Exception: 22 | self.logger.error("Exception trying to get the tag.") 23 | continue 24 | # Send actuator values (unaffected by noise) 25 | for tag in self.actuators: 26 | # noinspection PyBroadException 27 | try: 28 | values.append(self.get(tag)) 29 | except Exception: 30 | self.logger.error("Exception trying to get the tag.") 31 | continue 32 | 33 | self.send_multiple(self.tags, values, self.send_adddress) 34 | 35 | def set_parameters(self, sensors, actuators, values, send_address, noise_scale, week_index=0): 36 | self.sensors = sensors 37 | self.actuators = actuators 38 | self.tags = self.sensors + self.actuators 39 | self.values = values 40 | self.send_adddress = send_address 41 | self.noise_scale = noise_scale 42 | self.week_index = week_index 43 | self.cache_thread = None -------------------------------------------------------------------------------- /dhalsim/python2/entities/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/python2/entities/__init__.py -------------------------------------------------------------------------------- /dhalsim/python2/entities/control.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | class Control: 4 | """Defines a control for a PLC to enforce 5 | 6 | :param actuator: actuator that the rule will apply to 7 | :param action: action that will be performed (OPEN or CLOSED) 8 | :param value: value that is checked (t0 > value, time == value) etc 9 | """ 10 | __metaclass__ = ABCMeta 11 | 12 | def __init__(self, actuator, action, value): 13 | """Constructor method""" 14 | self.actuator = actuator 15 | self.action = action 16 | self.value = value 17 | 18 | @abstractmethod 19 | def apply(self, generic_plc): 20 | """ 21 | Applies a control rule using a given PLC. 22 | 23 | :param generic_plc: the PLC that will apply the control actions 24 | """ 25 | pass 26 | 27 | 28 | class BelowControl(Control): 29 | """ 30 | Defines a BELOW control, which takes as a parameter a dependant. 31 | 32 | :param dependant: value that the condition depends on (such as value of tank T0) 33 | """ 34 | 35 | def __init__(self, actuator, action, dependant, value): 36 | super(BelowControl, self).__init__(actuator, action, value) 37 | self.dependant = dependant 38 | 39 | def apply(self, generic_plc): 40 | """Applies the BELOW control rule using a given PLC 41 | 42 | :param generic_plc: the PLC that will apply the control actions 43 | """ 44 | dep_val = generic_plc.get_tag(self.dependant) 45 | if dep_val < self.value: 46 | generic_plc.set_tag(self.actuator, self.action) 47 | # generic_plc.logger.debug( 48 | # generic_plc.intermediate_plc["name"] + " applied " + str(self) + 49 | # " because dep_val " + str(dep_val) + ".") 50 | 51 | def __str__(self): 52 | return "Control if {dependant} < {value} then set {actuator} to {action}".format( 53 | dependant=self.dependant, value=self.value, actuator=self.actuator, action=self.action) 54 | 55 | 56 | class AboveControl(Control): 57 | """ 58 | Defines a ABOVE control, which takes as a parameter a dependant. 59 | 60 | :param dependant: value that the condition depends on (such as value of tank T0) 61 | """ 62 | 63 | def __init__(self, actuator, action, dependant, value): 64 | super(AboveControl, self).__init__(actuator, action, value) 65 | self.dependant = dependant 66 | 67 | def apply(self, generic_plc): 68 | """ 69 | Applies the ABOVE control rule using a given PLC. 70 | 71 | :param generic_plc: the PLC that will apply the control actions 72 | """ 73 | dep_val = generic_plc.get_tag(self.dependant) 74 | if dep_val > self.value: 75 | generic_plc.set_tag(self.actuator, self.action) 76 | # generic_plc.logger.debug( 77 | # generic_plc.intermediate_plc["name"] + " applied " + str(self) + " because dep_val " + str(dep_val)) 78 | 79 | def __str__(self): 80 | return "Control if {dependant} > {value} then set {actuator} to {action}".format( 81 | dependant=self.dependant, value=self.value, actuator=self.actuator, action=self.action) 82 | 83 | 84 | class TimeControl(Control): 85 | """ 86 | Defines a TIME control, which takes no additional parameters. 87 | """ 88 | 89 | def apply(self, generic_plc): 90 | """Applies the TIME control rule using a given PLC 91 | 92 | :param generic_plc: the PLC that will apply the control actions 93 | """ 94 | curr_time = generic_plc.get_master_clock() 95 | if curr_time == self.value: 96 | generic_plc.set_tag(self.actuator, self.action) 97 | #generic_plc.logger.debug( 98 | # generic_plc.intermediate_plc["name"] + " applied " + str(self) + " because curr_time " + str(curr_time)) 99 | 100 | def __str__(self): 101 | return "Control if time = {value} then set {actuator} to {action}".format( 102 | value=self.value, actuator=self.actuator, action=self.action) 103 | -------------------------------------------------------------------------------- /dhalsim/python2/topo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/dhalsim/python2/topo/__init__.py -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | _build -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | 16 | sys.path.insert(0, os.path.abspath('../..')) 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'dhalsim' 21 | copyright = '2021, Andrés F. Murillo, Robert van Dijk, Luc Jonker, Simcha Vos, Maarten Weyns' 22 | author = 'Andrés F. Murillo, Robert van Dijk, Luc Jonker, Simcha Vos, Maarten Weyns' 23 | 24 | # -- General configuration --------------------------------------------------- 25 | import sphinx_rtd_theme 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be 28 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 29 | # ones. 30 | extensions = [ 31 | 'sphinx.ext.autodoc', 32 | 'sphinx_rtd_theme', 33 | 'sphinx-prompt', 34 | 'sphinx.ext.autosectionlabel', 35 | ] 36 | 37 | autodoc_mock_imports = ['thread', 'py2_logger', 'topo', 'basePLC', 'entities', 'automatic_node', 'minicps'] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | # templates_path = ['_templates'] 41 | 42 | # List of patterns, relative to source directory, that match files and 43 | # directories to ignore when looking for source files. 44 | # This pattern also affects html_static_path and html_extra_path. 45 | exclude_patterns = [] 46 | 47 | # -- Options for HTML output ------------------------------------------------- 48 | 49 | # The theme to use for HTML and HTML Help pages. See the documentation for 50 | # a list of builtin themes. 51 | # 52 | html_theme = 'sphinx_rtd_theme' 53 | 54 | # Add any paths that contain custom static files (such as style sheets) here, 55 | # relative to this directory. They are copied after the builtin static files, 56 | # so a file named "default.css" will overwrite the builtin "default.css". 57 | # html_static_path = ['_static'] 58 | 59 | # -- Options for Latex output ------------------------------------------------ 60 | 61 | latex_toplevel_sectioning = 'section' 62 | 63 | latex_elements = { 64 | 'maketitle': r'', 65 | 'tableofcontents': r'', 66 | 'makeindex': r'', 67 | 'printindex': r'', 68 | 'fncychap': '', 69 | } 70 | 71 | latex_docclass = { 72 | 'howto': 'TUD-report2020', 73 | 'manual': 'TUD-report2020', 74 | } -------------------------------------------------------------------------------- /doc/events.rst: -------------------------------------------------------------------------------- 1 | Events 2 | ======= 3 | 4 | DHALSIM has support for network events. This chapter will explain the configuration for these. 5 | 6 | If you want to put the events in a separate file, see the section :ref:`Events in a separate file`. 7 | 8 | network events 9 | -------------- 10 | 11 | Events are situations started by a trigger that do not necessarily are attacks. In addition, events do not require launching additional mininet nodes or having additional mininet nodes interacting with the simulation. 12 | 13 | Example: 14 | 15 | .. code-block:: yaml 16 | 17 | network_events: 18 | - name: link_loss 19 | type: packet_loss 20 | target: PLC1 21 | trigger: 22 | type: time 23 | start: 648 24 | end: 792 25 | value: 25The following sections will explain the different configuration parameters. 26 | 27 | name 28 | ~~~~ 29 | *This option is required* 30 | 31 | This defines the name of the event. It cannot have whitespaces. 32 | 33 | trigger 34 | ~~~~~~~~ 35 | *This option is required* 36 | 37 | This parameter defines when the event is triggered. There are 4 different types of triggers: 38 | 39 | * Timed events 40 | * :code:`time` - This is a timed event. This means that the event will start at a given iteration and stop at a given iteration 41 | * Sensor attacks: These are event that will be triggered when a certain sensor in the water network meets a certain condition. 42 | * :code:`below` - This will make the event trigger while a certain tag is below or equal to a given value 43 | * :code:`above` - This will make the event execute while a certain tag is above or equal to a given value 44 | * :code:`between` - This will ensure that the event is executed when a certain tag is between or equal to two given values 45 | 46 | These are the required parameters per type of trigger: 47 | 48 | * For :code:`time` events: 49 | * :code:`start` - The start time of the event (in iterations). 50 | * :code:`end` - The end time of the event (in iterations). 51 | * For :code:`below` and :code:`above` events: 52 | * :code:`sensor` - The sensor of which the value will be used as the trigger. 53 | * :code:`value` - The value which has to be reached in order to trigger the event. 54 | * For :code:`between` events: 55 | * :code:`sensor` - The sensor of which the value will be used as the trigger. 56 | * :code:`lower_value` - The lower bound. 57 | * :code:`upper_value` - The upper bound. 58 | 59 | target 60 | ~~~~~~~~~ 61 | *This option is required* 62 | 63 | This parameter defines the PLC link that will be affected. In DHALSIM, the PLCs have only one network link (interface) 64 | 65 | type 66 | ~~~~~~~ 67 | *This option is required* 68 | 69 | This parameter defines the type of network event. Currently, only packet_loss is supported 70 | 71 | value 72 | ~~~~~~~ 73 | *This option is required* 74 | This parameter defines the percentage of packets that will be lost in the network link during the event 75 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | DHALSIM Documenation 2 | ======================= 3 | 4 | DHALSIM is a Digital Twin for Water Distribution Systems. A work by the SUTD Critical Infrastructure Systems Lab, TU 5 | Delft Department of Water Management, CISPA, and iTrust 6 | 7 | DHALSIM uses the `WNTR `_ EPANET wrapper to simulate the behaviour of 8 | water distribution systems. In addition, DHALSIM uses Mininet and MiniCPS to emulate the behavior of industrial control 9 | system controlling a water distribution system. This means that in addition to physical data, DHALSIM can also provide 10 | network captures of the PLCs, SCADA server, and other network and industrial devices present in the a water distribution 11 | system. 12 | 13 | The documentation is distributed in the following files: 14 | 15 | * attacks: explains how to run experiments on DHALSIM with attacks, the available attacks, and a description of the 16 | attacks and its options 17 | * configuration: includes information on how to configure DHALSIM experiments 18 | * events: explains what are events in DHALSIM and how to run experiments with events 19 | * installation: explains how to install DHALSIM 20 | * user_guide: explains how to create a new topology in DHALSIM or how to use an existing topology 21 | * developing: explains (broadly) how DHALSIM works and considerations for future development -------------------------------------------------------------------------------- /doc/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | In this guide, we will describe how DHALSIM can be installed on an Ubuntu 22.04 machine. DHALSIM installation is done automatically throuh the provided script that will install every dependency. 4 | 5 | Ubuntu version 6 | ~~~~~~~~~~~~~~~~~~~~~~~~ 7 | DHALSIM has been developed and tested on Ubuntu 22.04 LTS. Therefore, we recommend installing and running DHALSIM on Ubuntu 22.04. The installation script can be ran using ``./install.sh``. The installation script has not been tested on other versions. If you want to use another version, we recommend a manual installation. 8 | 9 | Automatic installation 10 | ---------------------- 11 | After cloning the repository, you can use the install script to install DHALSIM and its prerequisites. 12 | 13 | .. prompt:: bash $ 14 | 15 | git clone git@github.com:afmurillo/DHALSIM.git 16 | cd dhalsim 17 | sudo chmod +x install.sh 18 | ./install.sh 19 | 20 | The installation script can also install all testing and documentation dependencies. To do this, simply run ``./install.sh`` with the option `-t` for testing or `-d` for documentation, for example ``./install.sh -t -d``. 21 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /doc/running.rst: -------------------------------------------------------------------------------- 1 | Running 2 | =========== 3 | To run DHALSIM, simply input the command : 4 | 5 | .. prompt:: bash $ 6 | 7 | sudo dhalsim path/to/config.yaml 8 | 9 | Output 10 | ------------- 11 | Once the simulation has finished, various output files will be produced at the location specified in the :code:`config.yaml` under :ref:`output_path`. 12 | 13 | PCAP 14 | ~~~~~~~~~~~~~~~~ 15 | :code:`.pcap` files will be produced for every PLC in the network, every attacker, and the SCADA. They will have the format :code:`plc_name.pcap` and :code:`scada.pcap`. 16 | These files capture the network traffic that the PLCs and SCADA produce, and can be viewed in a program like `Wireshark `_. 17 | 18 | CSV 19 | ~~~~~~~~~~~~~~~~ 20 | Two :code:`.csv` files will be produced, one by the water simulation software and one by the SCADA: 21 | 22 | * The :code:`ground_truth.csv` represents the *actual* values of all tanks, pumps, actuators, valves, attacks, etc. during the running of the simulation. 23 | * The :code:`scada_values.csv` represents the values seen over the network by the SCADA during the simulation, it will record sensor values and actuator states for every sensor or actuator belonging to a PLC in the network. 24 | 25 | By differentiating between these, if a cyber attack takes place that masks the true value of a tank for example, the :code:`ground_truth.csv` will 26 | show the real value and the :code:`scada_values.csv` will show the modified value from the attacker. 27 | 28 | Configuration save 29 | ~~~~~~~~~~~~~~~~~~ 30 | For your convenience, all input files are automatically saved in the :code:`output_folder` specified in the configuration file. Using these input files, the exact same experiment can be recreated and ran later. In addition, a :code:`/configuration/general_readme.md` is provided. This file contains the most important information about the experiment. In addition, batch mode will have :code:`/configuration/batch_readme.md` in each batch output folder. 31 | -------------------------------------------------------------------------------- /examples/anytown_topology/anytown_concealment_mitm.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc2attack 3 | type: concealment_mitm 4 | tag: T41 5 | target: PLC2 6 | value: 8.0 7 | concealment_data: 8 | type: value 9 | concealment_value: 10 | - tag: 11 | #type: path 12 | #path: concealment_test.csv 13 | trigger: 14 | start: 10 15 | end: 100 16 | type: time -------------------------------------------------------------------------------- /examples/anytown_topology/anytown_config.yaml: -------------------------------------------------------------------------------- 1 | inp_file: anytown_map.inp 2 | output_path: output 3 | iterations: 2880 4 | network_topology_type: complex 5 | plcs: !include anytown_plcs.yaml 6 | 7 | simulator: epynet 8 | demand: pdd 9 | log_level: debug 10 | demand_patterns: demands_anytown_small.csv 11 | #attacks: !include anytown_concealment_mitm.yaml 12 | #attacks: !include anytown_dos.yaml 13 | -------------------------------------------------------------------------------- /examples/anytown_topology/anytown_delay_nwk_event.yaml: -------------------------------------------------------------------------------- 1 | network_events: 2 | - name: link_delay 3 | type: network_delay 4 | target: PLC1 5 | trigger: 6 | type: time 7 | start: 648 8 | end: 792 9 | value: 4000 -------------------------------------------------------------------------------- /examples/anytown_topology/anytown_dos.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc1attack 3 | target: PLC1 4 | trigger: 5 | type: time 6 | start: 648 7 | end: 792 8 | type: simple_dos 9 | direction: destination -------------------------------------------------------------------------------- /examples/anytown_topology/anytown_mitm.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc1attack 3 | type: mitm 4 | target: PLC1 5 | offset: 7.0 6 | trigger: 7 | type: time 8 | start: 5 9 | end: 20 -------------------------------------------------------------------------------- /examples/anytown_topology/anytown_naive_mitm.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc1attack 3 | type: naive_mitm 4 | target: PLC1 5 | offset: 7.0 6 | trigger: 7 | type: time 8 | start: 200 9 | end: 250 -------------------------------------------------------------------------------- /examples/anytown_topology/anytown_nwk_delay_and_loss.yaml: -------------------------------------------------------------------------------- 1 | network_events: 2 | - name: combined_nwk 3 | type: network_delay_loss 4 | target: PLC1 5 | trigger: 6 | type: time 7 | start: 648 8 | end: 792 9 | loss_value: 10 10 | delay_value: 100 11 | -------------------------------------------------------------------------------- /examples/anytown_topology/anytown_nwk_event.yaml: -------------------------------------------------------------------------------- 1 | network_events: 2 | - name: link_loss 3 | type: network_delay 4 | target: PLC1 5 | trigger: 6 | type: time 7 | start: 5 8 | end: 15 9 | value: 0.1 -------------------------------------------------------------------------------- /examples/anytown_topology/anytown_plcs.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | actuators: 3 | - P78 4 | - P79 5 | - name: PLC2 6 | sensors: 7 | - T41 8 | - name: PLC3 9 | sensors: 10 | - T42 11 | 12 | -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_concealment_mitm_path.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc4attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T3 6 | value: 8.0 7 | - tag: T4 8 | value: 8.0 9 | target: PLC4 10 | concealment_data: 11 | type: path 12 | path: concealment_test.csv 13 | trigger: 14 | #start: 648 15 | #end: 792 16 | start: 10 17 | end: 15 18 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_concealment_mitm_value.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc4attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T3 6 | offset: 10.0 7 | - tag: T4 8 | offset: 10.0 9 | target: PLC4 10 | concealment_data: 11 | type: value 12 | concealment_value: 13 | - tag: T3 14 | value: 42.0 15 | - tag: T4 16 | value: 84.0 17 | trigger: 18 | start: 648 19 | end: 792 20 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_config.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | 6 | log_level: debug 7 | simulator: epynet 8 | demand: pdd 9 | #attacks: !include ctown_unconstrained_blackbox_concealment_mitm.yaml 10 | #attacks: !include ctown_concealment_mitm_value.yaml 11 | #attacks: !include ctown_payload_replay_conceal.yaml 12 | #ma attacks: !include ctown_network_replay_conceal.yaml 13 | -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_dos.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - direction: source 3 | name: plc4attack 4 | target: PLC4 5 | trigger: 6 | start: 648 7 | end: 936 8 | type: time 9 | type: simple_dos -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_mitm.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc3attack 3 | type: mitm 4 | target: PLC3 5 | value: 42.0 6 | tag: T3 7 | direction: destination 8 | trigger: 9 | start: 648 10 | end: 936 11 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_naive_mitm.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc4attack 3 | type: naive_mitm 4 | target: PLC4 5 | value: 7.0 6 | trigger: 7 | start: 648 8 | end: 936 9 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_network_replay.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc4attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T1 6 | offset: 10.0 7 | target: PLC2 8 | concealment_data: 9 | type: payload_replay 10 | capture_start: 10 11 | capture_end: 30 12 | replay_start: 40 13 | trigger: 14 | start: 9 15 | end: 61 16 | #start: 5 17 | #end: 50 18 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_network_replay_conceal.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc2attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T1 6 | offset: 10.0 7 | target: PLC2 8 | concealment_data: 9 | type: network_replay 10 | capture_start: 650 11 | capture_end: 720 12 | replay_start: 722 13 | trigger: 14 | start: 648 15 | end: 792 16 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_payload_replay_conceal.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc4attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T1 6 | offset: 10.0 7 | target: PLC2 8 | concealment_data: 9 | type: payload_replay 10 | capture_start: 650 11 | capture_end: 720 12 | replay_start: 722 13 | trigger: 14 | start: 648 15 | end: 792 16 | #start: 5 17 | #end: 50 18 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_plcs.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - PU1F 4 | - PU2F 5 | - J280 6 | - J269 7 | actuators: 8 | - PU1 9 | - PU2 10 | - name: PLC2 11 | sensors: 12 | - T1 13 | - name: PLC3 14 | sensors: 15 | - T2 16 | - V2F 17 | - J300 18 | - J256 19 | - J289 20 | - J415 21 | - J14 22 | - J422 23 | - PU4F 24 | - PU5F 25 | - PU6F 26 | - PU7F 27 | actuators: 28 | - V2 29 | - PU4 30 | - PU5 31 | - PU6 32 | - PU7 33 | - name: PLC4 # PLC4, T3, 34 | sensors: 35 | - T3 36 | - name: PLC5 # PLC5, PU8F PU10F PU11F J302 J306 J307 J317, PU8 PU10 PU11 37 | sensors: 38 | - PU8F 39 | - PU10F 40 | - PU11F 41 | - J302 42 | - J306 43 | - J307 44 | - J317 45 | actuators: 46 | - PU8 47 | - PU10 48 | - PU11 49 | - name: PLC6 # PLC6, T4, 50 | sensors: 51 | - T4 52 | - name: PLC7 # PLC7, T5, 53 | sensors: 54 | - T5 55 | - name: PLC8 # PLC8, T6, 56 | sensors: 57 | - T6 58 | - name: PLC9 # PLC9, T7, 59 | sensors: 60 | - T7 -------------------------------------------------------------------------------- /examples/ctown_topology/ctown_unconstrained_blackbox_concealment_mitm.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc3attack 3 | type: unconstrained_blackbox_concealment_mitm 4 | trigger: 5 | start: 648 6 | end: 792 7 | type: time 8 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/attack_output_29/configuration/config.yaml: -------------------------------------------------------------------------------- 1 | attacks: 2 | device_attacks: 3 | - actuator: V2 4 | command: open 5 | name: plc3attack2 6 | trigger: 7 | end: 2728 8 | start: 1600 9 | type: time 10 | network_attacks: 11 | - name: plc3conceal 12 | trigger: 13 | end: 2728 14 | start: 200 15 | type: time 16 | type: unconstrained_blackbox_concealment_mitm 17 | demand: pdd 18 | demand_patterns: events/demands_ctown_01.csv 19 | initial_tank_data: events/tanks_ctown_01.csv 20 | inp_file: ctown_map.inp 21 | iterations: 2880 22 | log_level: debug 23 | network_topology_type: complex 24 | output_path: attack_output_29 25 | plcs: 26 | - actuators: 27 | - PU1 28 | - PU2 29 | name: PLC1 30 | sensors: 31 | - PU1F 32 | - PU2F 33 | - J280 34 | - J269 35 | - name: PLC2 36 | sensors: 37 | - T1 38 | - actuators: 39 | - V2 40 | - PU4 41 | - PU5 42 | - PU6 43 | - PU7 44 | name: PLC3 45 | sensors: 46 | - T2 47 | - V2F 48 | - J300 49 | - J256 50 | - J289 51 | - J415 52 | - J14 53 | - J422 54 | - PU4F 55 | - PU5F 56 | - PU6F 57 | - PU7F 58 | - name: PLC4 59 | sensors: 60 | - T3 61 | - actuators: 62 | - PU8 63 | - PU10 64 | - PU11 65 | name: PLC5 66 | sensors: 67 | - PU8F 68 | - PU10F 69 | - PU11F 70 | - J302 71 | - J306 72 | - J307 73 | - J317 74 | - name: PLC6 75 | sensors: 76 | - T4 77 | - name: PLC7 78 | sensors: 79 | - T5 80 | - name: PLC8 81 | sensors: 82 | - T6 83 | - name: PLC9 84 | sensors: 85 | - T7 86 | simulator: epynet 87 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_01.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | log_level: debug 6 | simulator: epynet 7 | demand: pdd 8 | output_path: attack_output_01 9 | demand_patterns: events/demands_ctown_01.csv 10 | initial_tank_data: events/tanks_ctown_01.csv 11 | attacks: !include dataset_attacks/attack_01.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_02.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | log_level: debug 6 | simulator: epynet 7 | demand: pdd 8 | output_path: attack_output_02 9 | demand_patterns: events/demands_ctown_01.csv 10 | initial_tank_data: events/tanks_ctown_01.csv 11 | attacks: !include dataset_attacks/attack_02.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_03.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | log_level: debug 6 | simulator: epynet 7 | demand: pdd 8 | output_path: attack_output_03 9 | demand_patterns: events/demands_ctown_01.csv 10 | initial_tank_data: events/tanks_ctown_01.csv 11 | attacks: !include dataset_attacks/attack_03.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_04.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | log_level: debug 6 | simulator: epynet 7 | demand: pdd 8 | output_path: attack_output_04 9 | demand_patterns: events/demands_ctown_01.csv 10 | initial_tank_data: events/tanks_ctown_01.csv 11 | attacks: !include dataset_attacks/attack_04.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_05.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | log_level: debug 6 | simulator: epynet 7 | demand: pdd 8 | output_path: attack_output_05 9 | demand_patterns: events/demands_ctown_01.csv 10 | initial_tank_data: events/tanks_ctown_01.csv 11 | attacks: !include dataset_attacks/attack_05.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_06.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | log_level: debug 6 | simulator: epynet 7 | demand: pdd 8 | output_path: attack_output_06 9 | demand_patterns: events/demands_ctown_01.csv 10 | initial_tank_data: events/tanks_ctown_01.csv 11 | attacks: !include dataset_attacks/attack_06.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_07.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | log_level: debug 6 | simulator: epynet 7 | demand: pdd 8 | output_path: attack_output_07 9 | demand_patterns: events/demands_ctown_01.csv 10 | initial_tank_data: events/tanks_ctown_01.csv 11 | attacks: !include dataset_attacks/attack_07.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_08.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | log_level: debug 6 | simulator: epynet 7 | demand: pdd 8 | output_path: attack_output_08 9 | demand_patterns: events/demands_ctown_01.csv 10 | initial_tank_data: events/tanks_ctown_01.csv 11 | attacks: !include dataset_attacks/attack_08.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_09.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | log_level: debug 6 | simulator: epynet 7 | demand: pdd 8 | output_path: attack_output_09 9 | demand_patterns: events/demands_ctown_01.csv 10 | initial_tank_data: events/tanks_ctown_01.csv 11 | attacks: !include dataset_attacks/attack_09.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_10.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_10 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_10.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_11.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_11 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_11.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_12.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_12 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_12.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_13.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_13 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_13.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_14.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_14 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_14.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_15.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_15 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_15.yaml -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_25.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_25 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_25.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_26.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_26 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_26.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_27.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_27 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_27.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_28.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_28 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_28.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_29.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_29 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_29.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_34.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_34 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_34.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_35.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_35 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_35.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_36.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_36 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_36.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_37.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_37 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_37.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_38.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_38 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_38.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_39.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_39 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_39.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_40.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_40 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_40.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_41.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_41 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_41.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_42.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_42 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_42.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_43.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_43 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_43.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_44.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_44 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_44.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_45.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_45 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_45.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_config_46.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | simulator: epynet 6 | demand: pdd 7 | output_path: attack_output_46 8 | demand_patterns: events/demands_ctown_01.csv 9 | initial_tank_data: events/tanks_ctown_01.csv 10 | attacks: !include dataset_attacks/attack_46.yaml 11 | log_level: debug -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/ctown_plcs.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 # PLC1, PU1F PU2F J280 J269 , PU1 PU2 2 | sensors: 3 | - PU1F 4 | - PU2F 5 | - J280 6 | - J269 7 | actuators: 8 | - PU1 9 | - PU2 10 | - name: PLC2 # PLC2, T1, 11 | sensors: 12 | - T1 13 | - name: PLC3 # PLC3, T2 V2F J300 J256 J289 J415 J14 J422 PU4F PU5F PU6F PU7F , V2 PU4 PU5 PU6 PU7 14 | sensors: 15 | - T2 16 | - V2F 17 | - J300 18 | - J256 19 | - J289 20 | - J415 21 | - J14 22 | - J422 23 | - PU4F 24 | - PU5F 25 | - PU6F 26 | - PU7F 27 | actuators: 28 | - V2 29 | - PU4 30 | - PU5 31 | - PU6 32 | - PU7 33 | - name: PLC4 # PLC4, T3, 34 | sensors: 35 | - T3 36 | - name: PLC5 # PLC5, PU8F PU10F PU11F J302 J306 J307 J317, PU8 PU10 PU11 37 | sensors: 38 | - PU8F 39 | - PU10F 40 | - PU11F 41 | - J302 42 | - J306 43 | - J307 44 | - J317 45 | actuators: 46 | - PU8 47 | - PU10 48 | - PU11 49 | - name: PLC6 # PLC6, T4, 50 | sensors: 51 | - T4 52 | - name: PLC7 # PLC7, T5, 53 | sensors: 54 | - T5 55 | - name: PLC8 # PLC8, T6, 56 | sensors: 57 | - T6 58 | - name: PLC9 # PLC9, T7, 59 | sensors: 60 | - T7 -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_01.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc9attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T7 6 | offset: 0.0 7 | target: PLC9 8 | concealment_data: 9 | type: payload_replay 10 | capture_start: 300 11 | capture_end: 672 12 | replay_start: 1440 13 | trigger: 14 | start: 295 15 | end: 1820 16 | type: time 17 | 18 | device_attacks: 19 | - actuator: PU10 20 | command: closed 21 | name: plc5attack1 22 | trigger: 23 | start: 1440 24 | end: 1812 25 | type: time 26 | - actuator: PU11 27 | command: closed 28 | name: plc5attack2 29 | trigger: 30 | start: 1440 31 | end: 1812 32 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_02.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc9attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T7 6 | offset: 0.0 7 | target: PLC9 8 | concealment_data: 9 | type: network_replay 10 | capture_start: 300 11 | capture_end: 672 12 | replay_start: 1440 13 | trigger: 14 | start: 295 15 | end: 1820 16 | type: time 17 | 18 | device_attacks: 19 | - actuator: PU10 20 | command: closed 21 | name: plc5attack1 22 | trigger: 23 | start: 1440 24 | end: 1812 25 | type: time 26 | - actuator: PU11 27 | command: closed 28 | name: plc5attack2 29 | trigger: 30 | start: 1440 31 | end: 1812 32 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_03.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc9attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T7 6 | offset: 4.5 7 | target: PLC9 8 | concealment_data: 9 | type: payload_replay 10 | capture_start: 300 11 | capture_end: 672 12 | replay_start: 1440 13 | trigger: 14 | start: 295 15 | end: 1820 16 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_04.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc9attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T7 6 | offset: 4.5 7 | target: PLC9 8 | concealment_data: 9 | type: network_replay 10 | capture_start: 300 11 | capture_end: 672 12 | replay_start: 1440 13 | trigger: 14 | start: 295 15 | end: 1820 16 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_05.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc9attack 3 | type: mitm 4 | target: PLC9 5 | tag: T7 6 | offset: 4.5 7 | trigger: 8 | start: 1440 9 | end: 1812 10 | type: time 11 | 12 | - name: plc9conceal 13 | type: unconstrained_blackbox_concealment_mitm 14 | trigger: 15 | start: 1440 16 | end: 1812 17 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_06.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc5attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: PU10 6 | offset: 0.0 7 | - tag: PU11 8 | offset: 0.0 9 | - tag: PU10F 10 | offset: 0.0 11 | - tag: PU11F 12 | offset: 0.0 13 | target: PLC5 14 | concealment_data: 15 | type: payload_replay 16 | capture_start: 300 17 | capture_end: 672 18 | replay_start: 1440 19 | trigger: 20 | start: 295 21 | end: 1820 22 | type: time 23 | 24 | - name: plc9attack 25 | type: concealment_mitm 26 | tags: 27 | - tag: T7 28 | offset: 0.0 29 | target: PLC9 30 | concealment_data: 31 | type: payload_replay 32 | capture_start: 300 33 | capture_end: 672 34 | replay_start: 1440 35 | trigger: 36 | start: 295 37 | end: 1820 38 | type: time 39 | 40 | device_attacks: 41 | - actuator: PU10 42 | command: closed 43 | name: plc5attack2 44 | trigger: 45 | start: 1440 46 | end: 1812 47 | type: time 48 | - actuator: PU11 49 | command: closed 50 | name: plc5attack3 51 | trigger: 52 | start: 1440 53 | end: 1812 54 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_07.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc5attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: PU10 6 | offset: 0.0 7 | - tag: PU11 8 | offset: 0.0 9 | - tag: PU10F 10 | offset: 0.0 11 | - tag: PU11F 12 | offset: 0.0 13 | target: PLC5 14 | concealment_data: 15 | type: network_replay 16 | capture_start: 300 17 | capture_end: 672 18 | replay_start: 1440 19 | trigger: 20 | start: 295 21 | end: 1820 22 | type: time 23 | 24 | - name: plc9attack 25 | type: concealment_mitm 26 | tags: 27 | - tag: T7 28 | offset: 0.0 29 | target: PLC9 30 | concealment_data: 31 | type: network_replay 32 | capture_start: 300 33 | capture_end: 672 34 | replay_start: 1440 35 | trigger: 36 | start: 295 37 | end: 1820 38 | type: time 39 | 40 | device_attacks: 41 | - actuator: PU10 42 | command: closed 43 | name: plc5attack2 44 | trigger: 45 | start: 1440 46 | end: 1812 47 | type: time 48 | - actuator: PU11 49 | command: closed 50 | name: plc5attack3 51 | trigger: 52 | start: 1440 53 | end: 1812 54 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_08.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc9attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T7 6 | offset: 5.0 7 | target: PLC9 8 | concealment_data: 9 | type: payload_replay 10 | capture_start: 300 11 | capture_end: 672 12 | replay_start: 1440 13 | trigger: 14 | start: 295 15 | end: 1820 16 | type: time 17 | 18 | - name: plc5attack 19 | type: concealment_mitm 20 | tags: 21 | - tag: PU10 22 | offset: 0.0 23 | - tag: PU11 24 | offset: 0.0 25 | - tag: PU10F 26 | offset: 0.0 27 | - tag: PU11F 28 | offset: 0.0 29 | target: PLC5 30 | concealment_data: 31 | type: payload_replay 32 | capture_start: 300 33 | capture_end: 672 34 | replay_start: 1440 35 | trigger: 36 | start: 295 37 | end: 1820 38 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_09.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc9attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T7 6 | offset: 5.0 7 | target: PLC9 8 | concealment_data: 9 | type: network_replay 10 | capture_start: 300 11 | capture_end: 672 12 | replay_start: 1440 13 | trigger: 14 | start: 295 15 | end: 1820 16 | type: time 17 | 18 | - name: plc5attack 19 | type: concealment_mitm 20 | tags: 21 | - tag: PU10 22 | offset: 0.0 23 | - tag: PU11 24 | offset: 0.0 25 | - tag: PU10F 26 | offset: 0.0 27 | - tag: PU11F 28 | offset: 0.0 29 | target: PLC5 30 | concealment_data: 31 | type: network_replay 32 | capture_start: 300 33 | capture_end: 672 34 | replay_start: 1440 35 | trigger: 36 | start: 295 37 | end: 1820 38 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_10.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc9attack 3 | type: mitm 4 | target: PLC9 5 | tag: T7 6 | offset: 4.5 7 | trigger: 8 | start: 1440 9 | end: 1812 10 | type: time 11 | 12 | - name: plc9conceal 13 | type: unconstrained_blackbox_concealment_mitm 14 | trigger: 15 | start: 1440 16 | end: 1812 17 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_11.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc2attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T1 6 | value: 0.0 7 | target: PLC2 8 | concealment_data: 9 | type: payload_replay 10 | capture_start: 300 11 | capture_end: 672 12 | replay_start: 1440 13 | trigger: 14 | start: 295 15 | end: 1820 16 | type: time 17 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_12.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc2attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T1 6 | value: 0.0 7 | target: PLC2 8 | concealment_data: 9 | type: network_replay 10 | capture_start: 300 11 | capture_end: 672 12 | replay_start: 1440 13 | trigger: 14 | start: 295 15 | end: 1820 16 | type: time 17 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_13.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc2attack 3 | type: mitm 4 | target: PLC2 5 | tag: T1 6 | value: 0.0 7 | trigger: 8 | start: 1440 9 | end: 1812 10 | type: time 11 | 12 | - name: plc2conceal 13 | type: unconstrained_blackbox_concealment_mitm 14 | trigger: 15 | start: 1440 16 | end: 1812 17 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_14.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc1attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: PU1 6 | offset: 0.0 7 | - tag: PU2 8 | offset: 0.0 9 | - tag: PU1F 10 | offset: 0.0 11 | - tag: PU12F 12 | offset: 0.0 13 | - tag: J269 14 | offset: 0.0 15 | - tag: J280 16 | offset: 0.0 17 | target: PLC1 18 | concealment_data: 19 | type: payload_replay 20 | capture_start: 300 21 | capture_end: 1428 22 | replay_start: 1600 23 | trigger: 24 | start: 295 25 | end: 2728 26 | type: time 27 | 28 | - name: plc2attack 29 | type: concealment_mitm 30 | tags: 31 | - tag: T1 32 | value: 0.0 33 | target: PLC2 34 | concealment_data: 35 | type: payload_replay 36 | capture_start: 300 37 | capture_end: 1428 38 | replay_start: 1600 39 | trigger: 40 | start: 295 41 | end: 2728 42 | type: time 43 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_15.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc1attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: PU1 6 | offset: 0.0 7 | - tag: PU2 8 | offset: 0.0 9 | - tag: PU1F 10 | offset: 0.0 11 | - tag: PU12F 12 | offset: 0.0 13 | - tag: J269 14 | offset: 0.0 15 | - tag: J280 16 | offset: 0.0 17 | target: PLC1 18 | concealment_data: 19 | type: network_replay 20 | capture_start: 290 21 | capture_end: 1428 22 | replay_start: 1600 23 | trigger: 24 | start: 290 25 | end: 2728 26 | type: time 27 | 28 | - name: plc2attack 29 | type: concealment_mitm 30 | tags: 31 | - tag: T1 32 | value: 0.0 33 | target: PLC2 34 | concealment_data: 35 | type: network_replay 36 | capture_start: 290 37 | capture_end: 1428 38 | replay_start: 1600 39 | trigger: 40 | start: 290 41 | end: 2728 42 | type: time 43 | 44 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_25.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc3attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: T2 6 | offset: 0.0 7 | target: PLC3 8 | concealment_data: 9 | type: payload_replay 10 | capture_start: 300 11 | capture_end: 1428 12 | replay_start: 1600 13 | trigger: 14 | start: 295 15 | end: 2728 16 | type: time 17 | 18 | device_attacks: 19 | - actuator: V2 20 | command: open 21 | name: plc3attack2 22 | trigger: 23 | start: 1600 24 | end: 2728 25 | type: time 26 | 27 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_26.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc3attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: T2 6 | offset: 0.0 7 | target: PLC3 8 | concealment_data: 9 | type: network_replay 10 | capture_start: 300 11 | capture_end: 1428 12 | replay_start: 1600 13 | trigger: 14 | start: 295 15 | end: 2728 16 | type: time 17 | 18 | device_attacks: 19 | - actuator: V2 20 | command: open 21 | name: plc3attack2 22 | trigger: 23 | start: 1600 24 | end: 2728 25 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_27.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc3attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: T2 6 | offset: 0.0 7 | - tag: V2F 8 | offset: 0.0 9 | - tag: J14 10 | offset: 0.0 11 | - tag: J422 12 | offset: 0.0 13 | target: PLC3 14 | concealment_data: 15 | type: payload_replay 16 | capture_start: 200 17 | capture_end: 1400 18 | replay_start: 1600 19 | trigger: 20 | start: 200 21 | end: 2600 22 | type: time 23 | 24 | device_attacks: 25 | - actuator: V2 26 | command: open 27 | name: plc3attack2 28 | trigger: 29 | start: 1600 30 | end: 2728 31 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_28.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc3attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: T2 6 | offset: 0.0 7 | - tag: V2F 8 | offset: 0.0 9 | - tag: J14 10 | offset: 0.0 11 | - tag: J422 12 | offset: 0.0 13 | target: PLC3 14 | concealment_data: 15 | type: network_replay 16 | capture_start: 200 17 | capture_end: 1400 18 | replay_start: 1600 19 | trigger: 20 | start: 200 21 | end: 2600 22 | type: time 23 | 24 | device_attacks: 25 | - actuator: V2 26 | command: open 27 | name: plc3attack2 28 | trigger: 29 | start: 1600 30 | end: 2728 31 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_29.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc3conceal 3 | type: unconstrained_blackbox_concealment_mitm 4 | trigger: 5 | start: 1600 6 | end: 2728 7 | type: time 8 | 9 | device_attacks: 10 | - actuator: V2 11 | command: open 12 | name: plc3attack2 13 | trigger: 14 | start: 1600 15 | end: 2728 16 | type: time 17 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_34.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc6attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: T4 6 | offset: -5.0 7 | target: PLC6 8 | concealment_data: 9 | type: payload_replay 10 | capture_start: 228 11 | capture_end: 588 12 | replay_start: 1368 13 | trigger: 14 | start: 220 15 | end: 1750 16 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_35.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc6attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: T4 6 | offset: -5.0 7 | target: PLC6 8 | concealment_data: 9 | type: network_replay 10 | capture_start: 228 11 | capture_end: 588 12 | replay_start: 1368 13 | trigger: 14 | start: 220 15 | end: 1370 16 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_35_short.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc6attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: T4 6 | offset: -5.0 7 | target: PLC6 8 | concealment_data: 9 | type: network_replay 10 | capture_start: 10 11 | capture_end: 20 12 | replay_start: 30 13 | trigger: 14 | start: 10 15 | end: 40 16 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_36.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc6attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: T4 6 | offset: 0.0 7 | target: PLC6 8 | concealment_data: 9 | type: payload_replay 10 | capture_start: 228 11 | capture_end: 588 12 | replay_start: 1368 13 | trigger: 14 | start: 220 15 | end: 1750 16 | type: time 17 | 18 | device_attacks: 19 | - actuator: PU6 20 | command: open 21 | name: plc6attack2 22 | trigger: 23 | start: 1368 24 | end: 1728 25 | type: time 26 | - actuator: PU7 27 | command: open 28 | name: plc6attack3 29 | trigger: 30 | start: 1368 31 | end: 1728 32 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_37.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc6attack1 3 | type: concealment_mitm 4 | tags: 5 | - tag: T4 6 | offset: 0.0 7 | target: PLC6 8 | concealment_data: 9 | type: network_replay 10 | capture_start: 228 11 | capture_end: 588 12 | replay_start: 1368 13 | trigger: 14 | start: 220 15 | end: 1750 16 | type: time 17 | 18 | device_attacks: 19 | - actuator: PU6 20 | command: open 21 | name: plc6attack2 22 | trigger: 23 | start: 1368 24 | end: 1728 25 | type: time 26 | - actuator: PU7 27 | command: open 28 | name: plc6attack3 29 | trigger: 30 | start: 1368 31 | end: 1728 32 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_38.yaml: -------------------------------------------------------------------------------- 1 | device_attacks: 2 | - actuator: PU10 3 | command: closed 4 | name: plc5attack1 5 | trigger: 6 | end: 1812 7 | start: 1440 8 | type: time 9 | - actuator: PU11 10 | command: closed 11 | name: plc5attack2 12 | trigger: 13 | end: 1812 14 | start: 1440 15 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_39.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc2attack 3 | tag: T1 4 | target: PLC2 5 | trigger: 6 | end: 1820 7 | start: 1440 8 | type: time 9 | type: mitm 10 | value: 0.0 -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_40.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc2attack 3 | tag: T1 4 | target: PLC2 5 | trigger: 6 | end: 2728 7 | start: 1600 8 | type: time 9 | type: mitm 10 | value: 0.0 -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_41.yaml: -------------------------------------------------------------------------------- 1 | device_attacks: 2 | - actuator: V2 3 | command: open 4 | name: plc3attack2 5 | trigger: 6 | end: 2728 7 | start: 1600 8 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_42.yaml: -------------------------------------------------------------------------------- 1 | device_attacks: 2 | - actuator: V2 3 | command: open 4 | name: plc3attack2 5 | trigger: 6 | end: 2728 7 | start: 1600 8 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_43.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc6attack1 3 | offset: -5.0 4 | tag: T4 5 | target: PLC6 6 | trigger: 7 | end: 1750 8 | start: 1368 9 | type: time 10 | type: mitm -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_44.yaml: -------------------------------------------------------------------------------- 1 | device_attacks: 2 | - actuator: PU10 3 | command: closed 4 | name: plc5attack1 5 | trigger: 6 | start: 1440 7 | end: 1812 8 | type: time 9 | - actuator: PU11 10 | command: closed 11 | name: plc5attack2 12 | trigger: 13 | start: 1440 14 | end: 1812 15 | type: time 16 | 17 | network_attacks: 18 | - name: plc9conceal 19 | type: unconstrained_blackbox_concealment_mitm 20 | trigger: 21 | start: 1440 22 | end: 1812 23 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_45.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc9attack 3 | type: mitm 4 | target: PLC9 5 | tag: T7 6 | offset: 4.5 7 | trigger: 8 | start: 1440 9 | end: 1812 10 | type: time 11 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/dataset_attacks/attack_46.yaml: -------------------------------------------------------------------------------- 1 | device_attacks: 2 | - actuator: PU6 3 | command: open 4 | name: plc6attack2 5 | trigger: 6 | start: 1368 7 | end: 1728 8 | type: time 9 | - actuator: PU7 10 | command: open 11 | name: plc6attack3 12 | trigger: 13 | start: 1368 14 | end: 1728 15 | type: time -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/events/delay_ctown_01.csv: -------------------------------------------------------------------------------- 1 | PLC1,PLC2,PLC3,PLC4,PLC5,PLC6,PLC7,PLC8,PLC9,scada 2 | 14.23972092,4.613175298,11.07260256,8.128266119,7.112143035,14.85580728,3.766769773,11.92157224,9.666322031,3.067014259 3 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/events/loss_ctown_01.csv: -------------------------------------------------------------------------------- 1 | PLC1,PLC2,PLC3,PLC4,PLC5,PLC6,PLC7,PLC8,PLC9,scada 2 | 0.041547695,0.04920543,0.028836746,0.020626835,0.026423415,0.017022787,0.024766907,0.042542328,0.011252183,0.041232546 3 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_anomaly_config_files/events/tanks_ctown_01.csv: -------------------------------------------------------------------------------- 1 | T1,T2,T3,T4,T5,T6,T7 2 | 5.03684847,4.983032164,4.669619341,4.263891087,4.06813335,2.452301319,2.060531977 3 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_normal_operating_conditions/create_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for n in $(seq 1 154) ; do 4 | cp $1.yaml $1_$n.yaml 5 | done 6 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_normal_operating_conditions/ctown_config.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ctown_map.inp 2 | iterations: 2880 3 | network_topology_type: complex 4 | plcs: !include ctown_plcs.yaml 5 | batch_simulations: 53 6 | initial_tank_data: initial_tank_levels.csv 7 | demand_patterns: demand_patterns/ 8 | network_loss_data: network_loss_data.csv 9 | network_delay_data: network_delay_data.csv 10 | simulator: epynet 11 | demand: pdd 12 | -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_normal_operating_conditions/ctown_plcs.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 # PLC1, PU1F PU2F J280 J269 , PU1 PU2 2 | sensors: 3 | - PU1F 4 | - PU2F 5 | - J280 6 | - J269 7 | actuators: 8 | - PU1 9 | - PU2 10 | - name: PLC2 # PLC2, T1, 11 | sensors: 12 | - T1 13 | - name: PLC3 # PLC3, T2 V2F J300 J256 J289 J415 J14 J422 PU4F PU5F PU6F PU7F , V2 PU4 PU5 PU6 PU7 14 | sensors: 15 | - T2 16 | - V2F 17 | - J300 18 | - J256 19 | - J289 20 | - J415 21 | - J14 22 | - J422 23 | - PU4F 24 | - PU5F 25 | - PU6F 26 | - PU7F 27 | actuators: 28 | - V2 29 | - PU4 30 | - PU5 31 | - PU6 32 | - PU7 33 | - name: PLC4 # PLC4, T3, 34 | sensors: 35 | - T3 36 | - name: PLC5 # PLC5, PU8F PU10F PU11F J302 J306 J307 J317, PU8 PU10 PU11 37 | sensors: 38 | - PU8F 39 | - PU10F 40 | - PU11F 41 | - J302 42 | - J306 43 | - J307 44 | - J317 45 | actuators: 46 | - PU8 47 | - PU10 48 | - PU11 49 | - name: PLC6 # PLC6, T4, 50 | sensors: 51 | - T4 52 | - name: PLC7 # PLC7, T5, 53 | sensors: 54 | - T5 55 | - name: PLC8 # PLC8, T6, 56 | sensors: 57 | - T6 58 | - name: PLC9 # PLC9, T7, 59 | sensors: 60 | - T7 -------------------------------------------------------------------------------- /examples/ctown_topology/dataset/dataset_normal_operating_conditions/master_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for file in ./*.yaml 4 | do 5 | echo running for $file 6 | sudo dhalsim $file 7 | done 8 | -------------------------------------------------------------------------------- /examples/example/example_attack.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc4attack 3 | type: concealment_mitm 4 | tags: 5 | - tag: T3 6 | offset: 10.0 7 | - tag: T4 8 | offset: 10.0 9 | target: PLC4 10 | concealment_data: 11 | type: payload_replay 12 | capture_start: 10 13 | capture_end: 20 14 | replay_start: 30 15 | trigger: 16 | start: 10 17 | end: 50 18 | type: time -------------------------------------------------------------------------------- /examples/ky13_topology/ky13_config.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ky13.inp 2 | output_path: output 3 | iterations: 288 4 | network_topology_type: complex 5 | plcs: !include ky13_plcs.yaml 6 | simulator: epynet -------------------------------------------------------------------------------- /examples/ky13_topology/ky13_plcs.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - T_1 4 | - name: PLC2 5 | actuators: 6 | - XPump_1 7 | - name: PLC3 8 | sensors: 9 | - T_4 10 | - name: PLC4 11 | actuators: 12 | - XPump_2 -------------------------------------------------------------------------------- /examples/ky_14_topology/ky14_config.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ky14.inp 2 | output_path: output 3 | iterations: 288 4 | network_topology_type: complex 5 | plcs: !include ky14_plcs.yaml 6 | simulator: epynet -------------------------------------------------------------------------------- /examples/ky_14_topology/ky14_plcs.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - T_1 4 | - name: PLC2 5 | actuators: 6 | - XPump_3 -------------------------------------------------------------------------------- /examples/ky_15_topology/ky15_config.yaml: -------------------------------------------------------------------------------- 1 | inp_file: ky15.inp 2 | output_path: output 3 | iterations: 288 4 | network_topology_type: complex 5 | plcs: !include ky15_plcs_conf3.yaml 6 | simulator: epynet 7 | demand_patterns: demands_ky15.csv 8 | -------------------------------------------------------------------------------- /examples/ky_15_topology/ky15_plcs_conf1.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - T_1 4 | actuators: 5 | - XPump_3 6 | - name: PLC2 7 | sensors: 8 | - T_3 9 | - T_4 10 | - T_5 11 | - T_6 12 | actuators: 13 | - XPump_8 14 | - XPump_2 15 | - XPump_9 16 | - name: PLC3 17 | actuators: 18 | - XPump_1 19 | - name: PLC4 20 | sensors: 21 | - T_2 22 | actuators: 23 | - XPump_7 24 | - name: PLC5 25 | sensors: 26 | - T_7 27 | actuators: 28 | - XPump_6 -------------------------------------------------------------------------------- /examples/ky_15_topology/ky15_plcs_conf2.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - T_1 4 | actuators: 5 | - XPump_3 6 | - name: PLC2 7 | sensors: 8 | - T_3 9 | - T_4 10 | - T_5 11 | - T_6 12 | - name: PLC3 13 | actuators: 14 | - XPump_2 15 | - XPump_8 16 | - XPump_9 17 | - name: PLC4 18 | actuators: 19 | - XPump_1 20 | - name: PLC5 21 | sensors: 22 | - T_2 23 | actuators: 24 | - XPump_7 25 | - name: PLC6 26 | sensors: 27 | - T_7 28 | actuators: 29 | - XPump_6 30 | -------------------------------------------------------------------------------- /examples/ky_15_topology/ky15_plcs_conf3.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - T_1 4 | actuators: 5 | - XPump_3 6 | - name: PLC2 7 | sensors: 8 | - T_3 9 | - T_4 10 | - name: PLC3 11 | sensors: 12 | - T_5 13 | - T_6 14 | - name: PLC4 15 | actuators: 16 | - XPump_2 17 | - XPump_8 18 | - name: PLC5 19 | actuators: 20 | - XPump_9 21 | - name: PLC6 22 | actuators: 23 | - XPump_1 24 | - name: PLC7 25 | sensors: 26 | - T_2 27 | actuators: 28 | - XPump_7 29 | - name: PLC8 30 | sensors: 31 | - T_7 32 | actuators: 33 | - XPump_6 34 | -------------------------------------------------------------------------------- /examples/ky_15_topology/ky15_plcs_conf4.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - T_1 4 | actuators: 5 | - XPump_3 6 | - name: PLC2 7 | sensors: 8 | - T_2 9 | actuators: 10 | - XPump_7 11 | - name: PLC3 12 | sensors: 13 | - T_3 14 | actuators: 15 | - XPump_8 16 | - name: PLC4 17 | sensors: 18 | - T_4 19 | actuators: 20 | - XPump_1 21 | - name: PLC5 22 | sensors: 23 | - T_5 24 | actuators: 25 | - XPump_2 26 | - name: PLC6 27 | sensors: 28 | - T_6 29 | actuators: 30 | - XPump_9 31 | - name: PLC7 32 | sensors: 33 | - T7 34 | actuators: 35 | - XPump_6 -------------------------------------------------------------------------------- /examples/ky_15_topology/ky15_plcs_original.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - T_1 4 | - T_4 5 | - T_5 6 | actuators: 7 | - XPump_3 8 | - name: PLC2 9 | actuators: 10 | - XPump_1 11 | - XPump_2 12 | - name: PLC3 13 | sensors: 14 | - T_3 15 | - T_2 16 | - T_7 17 | - name: PLC4 18 | actuators: 19 | - XPump_8 20 | - XPump_7 21 | - XPump_6 22 | - name: PLC5 23 | sensors: 24 | - T_6 25 | - name: PLC6 26 | actuators: 27 | - XPump_9 -------------------------------------------------------------------------------- /examples/minitown_topology/initial_tank.csv: -------------------------------------------------------------------------------- 1 | TANK 2 | 2.08023668123243 3 | 2.630407287289067 4 | 5.047781621489811 5 | 3.468502261561202 6 | 1.9941270664742805 7 | 2.500606079770213 8 | 4.321736118183167 9 | 4.699886799516197 10 | 0.7277793564333627 11 | 3.807217188908116 12 | 1.9697686831428909 13 | 4.7026311423677605 14 | 0.9504608182470595 15 | 3.571291786206007 16 | 4.054814475167662 17 | 3.719258056381705 18 | 4.694373488974139 19 | 1.2296931560192572 20 | 2.6359750315745782 21 | 1.8847627667267193 22 | 3.1838411037297063 23 | 4.057362430149257 24 | 1.0986733423345052 25 | 5.170248488616648 26 | 2.7707013190528045 27 | 4.795422127847699 28 | 4.153435801873551 29 | 1.824133170098642 30 | 5.024334219146693 31 | 1.6902107230601877 32 | 3.2184923482605865 33 | 1.6931521614678382 34 | 4.497884404674674 35 | 1.8681023344678898 36 | 1.884231526856437 37 | 3.7691270582635887 38 | 4.482676601671438 39 | 1.2699288215830518 -------------------------------------------------------------------------------- /examples/minitown_topology/minitown_attacks.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - name: plc1attack 3 | type: mitm 4 | trigger: 5 | type: Time 6 | start: 250 7 | end: 750 8 | tags: 9 | - tag: TANK 10 | offset: -3 11 | target: PLC1 -------------------------------------------------------------------------------- /examples/minitown_topology/minitown_config.yaml: -------------------------------------------------------------------------------- 1 | inp_file: minitown_map.inp 2 | output_path: output 3 | iterations: 1000 4 | 5 | plcs: !include minitown_plcs.yaml 6 | attacks: !include minitown_attacks.yaml 7 | 8 | initial_tank_data: initial_tank.csv 9 | -------------------------------------------------------------------------------- /examples/minitown_topology/minitown_plcs.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - TANK 4 | - name: PLC2 5 | actuators: 6 | - PUMP1 7 | - PUMP2 -------------------------------------------------------------------------------- /examples/wadi_topology/delays_wadi.csv: -------------------------------------------------------------------------------- 1 | PLC1,PLC2,scada 2 | 89.55084013904876,46.174087139486836,4.134693808027478 3 | 18.02036361267007,71.61721128247179,20.418205360991667 4 | 46.764593995961405,40.14811824548883,52.01898975002857 5 | 62.97921097793712,69.44100619265261,29.656250818493934 6 | 16.177142578275078,62.10495907676028,50.33926647419674 7 | 75.6230078302943,16.0324131567625,60.059850908442485 8 | 66.34688868617265,19.896754304184363,71.69661638286775 9 | 70.6074801934558,33.10679755420969,46.68778666324777 10 | 66.78172672486424,61.25604216121409,52.04584495565653 11 | 26.189708277242044,14.833571861705057,11.1414216848879 12 | 23.23297988818185,92.48157488667395,12.918750848261542 13 | 56.58860159553475,51.1583329547354,91.64507152993592 14 | 18.34439885728614,24.170030681751033,57.46762238209243 15 | 37.601953591723735,101.7708312806536,24.002170740671964 16 | 28.82479069466816,37.31778917241066,46.51305826478793 17 | 59.239030365360136,78.09311363161676,9.55202202424984 18 | 43.613308111344125,32.867326819977734,73.06902056434292 19 | 33.8153994186125,63.24961673066001,37.5802018191641 20 | 19.365462172187545,66.7458954575585,39.46229294316606 21 | 22.54934497042499,37.59263228090465,83.82588811605092 22 | 31.831698380711156,74.47563170891799,51.046728598890226 23 | 38.3929543830715,64.06692414190397,39.93012814139027 24 | 23.43046358139733,74.38729140096865,67.4678969946774 25 | 16.18927488509274,32.69140748554894,32.535373582360634 26 | 39.967280493129074,26.434355456179958,51.734022125408934 27 | 89.42863825505673,46.51185255136477,8.616049467435564 28 | 35.340357778735104,60.12646840229505,34.843700663599606 29 | 75.66613441134716,28.15199138158096,33.727965777117554 30 | 55.59715424843602,68.90582112646142,37.79719935144706 31 | 74.20699212733118,19.88917215567087,72.15062556921266 32 | 63.230374000478356,17.378326435236257,25.784377937468395 33 | 62.97725477155753,26.9306457117952,54.64498221018931 34 | 18.646798150789692,30.395705959222038,63.331195414199 35 | 49.58276165663722,94.99874970984496,28.84798756964826 36 | 71.51774338654452,28.816098710495044,22.225537132057376 37 | 53.0499734761341,7.495150465166766,90.63991128658472 38 | 34.17334295312319,73.82041158956471,22.43931778617191 39 | 77.20752431634804,53.522380960753466,32.789565423069405 -------------------------------------------------------------------------------- /examples/wadi_topology/demand_patterns/0.csv: -------------------------------------------------------------------------------- 1 | Consumer01 2 | 0.14271687308805014 3 | 0.23241219117934597 4 | 0.09968121148138771 5 | 0.12543209632839522 6 | 0.20021153745421905 7 | 0.14724092500620242 8 | 0.06174582378527198 9 | 0.35334387953269064 10 | 0.06948002264767301 11 | 0.13539075835165068 12 | 0.05971969423577754 13 | 0.17366360723366792 14 | 0.19690258608390612 15 | 0.040559619763519614 16 | 0.3012152799753212 17 | 0.3138062273192133 18 | 0.26262952010115664 19 | 0.11469135433384628 20 | 0.1362370158907633 21 | 0.0837232341350412 22 | 0.31896790482013376 23 | 0.2382399204755192 24 | 0.1532669437678115 25 | 0.14470845950842257 26 | 0.11370453660131252 27 | 0.1737410424581641 28 | 0.2475836787381437 29 | 0.02635333273495896 30 | 0.15707796262181498 31 | 0.2804453965554323 32 | 0.3408984477491296 33 | 0.18879519997149616 34 | 0.07666220037265693 35 | 0.1982576285848933 36 | 0.10633722272885378 37 | 0.25999598911166444 38 | 0.1917931474640215 39 | 0.0997930703700122 40 | 0.33876009055954914 41 | 0.2004770951850907 42 | 0.19488995708390525 43 | 0.2775020973176319 44 | 0.19439536586596054 45 | 0.15282374842393556 46 | 0.19565303368763404 47 | 0.08491204577536116 48 | 0.21243620338975583 49 | 0.24794148806086794 50 | 0.26730208112529263 51 | 0.3568642849554934 52 | 0.20726868156140776 53 | 0.19132479847188094 54 | 0.08981354986628728 55 | 0.16950165129254002 56 | 0.20398177734284118 57 | 0.10761448953525828 58 | 0.1771858257563895 59 | 0.21430730980584106 60 | 0.08903091723279681 61 | 0.20738084579398078 62 | 0.053915372078978424 63 | 0.13707382741897842 64 | 0.1349383156436157 65 | 0.33966050296618233 66 | 0.27835589331504934 67 | 0.0827809519080068 68 | 0.39024626916638505 69 | 0.06799403664340985 70 | 0.049866608535426606 71 | 0.2178454948221662 72 | 0.2293420901110335 73 | 0.35323923276751745 74 | 0.2817911840637359 75 | 0.29452040443658234 76 | 0.15032228738006453 77 | 0.22649515141053014 78 | 0.2694775094015444 79 | 0.21282181165969244 80 | 0.42770596193662513 81 | 0.030896348821555567 82 | 0.31556186472415004 83 | 0.2590990350143419 84 | 0.2045609163457576 85 | 0.33129166504642577 86 | 0.10276946512308005 87 | 0.22441250285523268 88 | 0.20700453034301802 89 | 0.06275747449888934 90 | 0.12617547952547306 91 | 0.20640126786926719 92 | 0.20646800545011673 93 | 0.46367991182683 94 | 0.18058953747878956 95 | 0.2746372769476808 96 | -------------------------------------------------------------------------------- /examples/wadi_topology/demand_patterns/1.csv: -------------------------------------------------------------------------------- 1 | Consumer01 2 | 0.14271687308805014 3 | 0.23241219117934597 4 | 0.09968121148138771 5 | 0.12543209632839522 6 | 0.20021153745421905 7 | 0.14724092500620242 8 | 0.06174582378527198 9 | 0.35334387953269064 10 | 0.06948002264767301 11 | 0.13539075835165068 12 | 0.05971969423577754 13 | 0.17366360723366792 14 | 0.19690258608390612 15 | 0.040559619763519614 16 | 0.3012152799753212 17 | 0.3138062273192133 18 | 0.26262952010115664 19 | 0.11469135433384628 20 | 0.1362370158907633 21 | 0.0837232341350412 22 | 0.31896790482013376 23 | 0.2382399204755192 24 | 0.1532669437678115 25 | 0.14470845950842257 26 | 0.11370453660131252 27 | 0.1737410424581641 28 | 0.2475836787381437 29 | 0.02635333273495896 30 | 0.15707796262181498 31 | 0.2804453965554323 32 | 0.3408984477491296 33 | 0.18879519997149616 34 | 0.07666220037265693 35 | 0.1982576285848933 36 | 0.10633722272885378 37 | 0.25999598911166444 38 | 0.1917931474640215 39 | 0.0997930703700122 40 | 0.33876009055954914 41 | 0.2004770951850907 42 | 0.19488995708390525 43 | 0.2775020973176319 44 | 0.19439536586596054 45 | 0.15282374842393556 46 | 0.19565303368763404 47 | 0.08491204577536116 48 | 0.21243620338975583 49 | 0.24794148806086794 50 | 0.26730208112529263 51 | 0.3568642849554934 52 | 0.20726868156140776 53 | 0.19132479847188094 54 | 0.08981354986628728 55 | 0.16950165129254002 56 | 0.20398177734284118 57 | 0.10761448953525828 58 | 0.1771858257563895 59 | 0.21430730980584106 60 | 0.08903091723279681 61 | 0.20738084579398078 62 | 0.053915372078978424 63 | 0.13707382741897842 64 | 0.1349383156436157 65 | 0.33966050296618233 66 | 0.27835589331504934 67 | 0.0827809519080068 68 | 0.39024626916638505 69 | 0.06799403664340985 70 | 0.049866608535426606 71 | 0.2178454948221662 72 | 0.2293420901110335 73 | 0.35323923276751745 74 | 0.2817911840637359 75 | 0.29452040443658234 76 | 0.15032228738006453 77 | 0.22649515141053014 78 | 0.2694775094015444 79 | 0.21282181165969244 80 | 0.42770596193662513 81 | 0.030896348821555567 82 | 0.31556186472415004 83 | 0.2590990350143419 84 | 0.2045609163457576 85 | 0.33129166504642577 86 | 0.10276946512308005 87 | 0.22441250285523268 88 | 0.20700453034301802 89 | 0.06275747449888934 90 | 0.12617547952547306 91 | 0.20640126786926719 92 | 0.20646800545011673 93 | 0.46367991182683 94 | 0.18058953747878956 95 | 0.2746372769476808 96 | -------------------------------------------------------------------------------- /examples/wadi_topology/demand_patterns/2.csv: -------------------------------------------------------------------------------- 1 | Consumer01 2 | 0.14271687308805014 3 | 0.23241219117934597 4 | 0.09968121148138771 5 | 0.12543209632839522 6 | 0.20021153745421905 7 | 0.14724092500620242 8 | 0.06174582378527198 9 | 0.35334387953269064 10 | 0.06948002264767301 11 | 0.13539075835165068 12 | 0.05971969423577754 13 | 0.17366360723366792 14 | 0.19690258608390612 15 | 0.040559619763519614 16 | 0.3012152799753212 17 | 0.3138062273192133 18 | 0.26262952010115664 19 | 0.11469135433384628 20 | 0.1362370158907633 21 | 0.0837232341350412 22 | 0.31896790482013376 23 | 0.2382399204755192 24 | 0.1532669437678115 25 | 0.14470845950842257 26 | 0.11370453660131252 27 | 0.1737410424581641 28 | 0.2475836787381437 29 | 0.02635333273495896 30 | 0.15707796262181498 31 | 0.2804453965554323 32 | 0.3408984477491296 33 | 0.18879519997149616 34 | 0.07666220037265693 35 | 0.1982576285848933 36 | 0.10633722272885378 37 | 0.25999598911166444 38 | 0.1917931474640215 39 | 0.0997930703700122 40 | 0.33876009055954914 41 | 0.2004770951850907 42 | 0.19488995708390525 43 | 0.2775020973176319 44 | 0.19439536586596054 45 | 0.15282374842393556 46 | 0.19565303368763404 47 | 0.08491204577536116 48 | 0.21243620338975583 49 | 0.24794148806086794 50 | 0.26730208112529263 51 | 0.3568642849554934 52 | 0.20726868156140776 53 | 0.19132479847188094 54 | 0.08981354986628728 55 | 0.16950165129254002 56 | 0.20398177734284118 57 | 0.10761448953525828 58 | 0.1771858257563895 59 | 0.21430730980584106 60 | 0.08903091723279681 61 | 0.20738084579398078 62 | 0.053915372078978424 63 | 0.13707382741897842 64 | 0.1349383156436157 65 | 0.33966050296618233 66 | 0.27835589331504934 67 | 0.0827809519080068 68 | 0.39024626916638505 69 | 0.06799403664340985 70 | 0.049866608535426606 71 | 0.2178454948221662 72 | 0.2293420901110335 73 | 0.35323923276751745 74 | 0.2817911840637359 75 | 0.29452040443658234 76 | 0.15032228738006453 77 | 0.22649515141053014 78 | 0.2694775094015444 79 | 0.21282181165969244 80 | 0.42770596193662513 81 | 0.030896348821555567 82 | 0.31556186472415004 83 | 0.2590990350143419 84 | 0.2045609163457576 85 | 0.33129166504642577 86 | 0.10276946512308005 87 | 0.22441250285523268 88 | 0.20700453034301802 89 | 0.06275747449888934 90 | 0.12617547952547306 91 | 0.20640126786926719 92 | 0.20646800545011673 93 | 0.46367991182683 94 | 0.18058953747878956 95 | 0.2746372769476808 96 | -------------------------------------------------------------------------------- /examples/wadi_topology/initial_tank_wadi.csv: -------------------------------------------------------------------------------- 1 | T2 2 | 2.08023668123243 3 | 2.630407287289067 4 | 5.047781621489811 5 | 0.368502261561202 6 | 1.9941270664742805 7 | 2.500606079770213 8 | 4.321736118183167 9 | 4.699886799516197 10 | 0.7277793564333627 11 | 3.807217188908116 12 | 1.9697686831428909 13 | 4.7026311423677605 14 | 0.9504608182470595 15 | 3.571291786206007 16 | 4.054814475167662 17 | 3.719258056381705 18 | 4.694373488974139 19 | 1.2296931560192572 20 | 2.6359750315745782 21 | 1.8847627667267193 22 | 3.1838411037297063 23 | 4.057362430149257 24 | 1.0986733423345052 25 | 5.170248488616648 26 | 2.7707013190528045 27 | 4.795422127847699 28 | 4.153435801873551 29 | 1.824133170098642 30 | 5.024334219146693 31 | 1.6902107230601877 32 | 3.2184923482605865 33 | 1.6931521614678382 34 | 4.497884404674674 35 | 1.8681023344678898 36 | 1.884231526856437 37 | 3.7691270582635887 38 | 4.482676601671438 39 | 1.2699288215830518 -------------------------------------------------------------------------------- /examples/wadi_topology/losses_wadi.csv: -------------------------------------------------------------------------------- 1 | PLC1,PLC2,scada 2 | 0.14271687308805014,0.2708496621297529,0.16496511648703552 3 | 0.23241219117934597,0.17552208059176275,0.2566297826821671 4 | 0.09968121148138771,0.13190885054632517,0.38059444235594203 5 | 0.12543209632839522,0.06737791831307194,0.2400944259032637 6 | 0.20021153745421905,0.24912155525742696,0.2531926442635278 7 | 0.14724092500620242,0.09130995792354094,0.3024259748854733 8 | 0.06174582378527198,0.3502415358411979,0.2418401609909737 9 | 0.35334387953269064,0.05803561762108079,0.16521451482044236 10 | 0.06948002264767301,0.4502818992078698,0.46268462543619476 11 | 0.13539075835165068,0.15993685811553432,0.13578886931400613 12 | 0.05971969423577754,0.33876009055954914,0.39024626916638505 13 | 0.17366360723366792,0.2004770951850907,0.06799403664340985 14 | 0.19690258608390612,0.19488995708390525,0.049866608535426606 15 | 0.040559619763519614,0.2775020973176319,0.2178454948221662 16 | 0.3012152799753212,0.19439536586596054,0.2293420901110335 17 | 0.3138062273192133,0.15282374842393556,0.35323923276751745 18 | 0.26262952010115664,0.19565303368763404,0.2817911840637359 19 | 0.11469135433384628,0.08491204577536116,0.29452040443658234 20 | 0.1362370158907633,0.21243620338975583,0.15032228738006453 21 | 0.0837232341350412,0.24794148806086794,0.22649515141053014 22 | 0.31896790482013376,0.26730208112529263,0.2694775094015444 23 | 0.2382399204755192,0.3568642849554934,0.21282181165969244 24 | 0.1532669437678115,0.20726868156140776,0.42770596193662513 25 | 0.14470845950842257,0.19132479847188094,0.030896348821555567 26 | 0.11370453660131252,0.08981354986628728,0.31556186472415004 27 | 0.1737410424581641,0.16950165129254002,0.2590990350143419 28 | 0.2475836787381437,0.20398177734284118,0.2045609163457576 29 | 0.02635333273495896,0.10761448953525828,0.33129166504642577 30 | 0.15707796262181498,0.1771858257563895,0.10276946512308005 31 | 0.2804453965554323,0.21430730980584106,0.22441250285523268 32 | 0.3408984477491296,0.08903091723279681,0.20700453034301802 33 | 0.18879519997149616,0.20738084579398078,0.06275747449888934 34 | 0.07666220037265693,0.053915372078978424,0.12617547952547306 35 | 0.1982576285848933,0.13707382741897842,0.20640126786926719 36 | 0.10633722272885378,0.1349383156436157,0.20646800545011673 37 | 0.25999598911166444,0.33966050296618233,0.46367991182683 38 | 0.1917931474640215,0.27835589331504934,0.18058953747878956 39 | 0.0997930703700122,0.0827809519080068,0.2746372769476808 -------------------------------------------------------------------------------- /examples/wadi_topology/readme.md: -------------------------------------------------------------------------------- 1 | ## Disclaimer 2 | 3 | The WADI topology presented here is currently unsupported, as DHALSIM version v0.6.0. We have left it in the repository for educational purposes and in case the comunity wants to modify it to try running it with the current version of DHALSIM. -------------------------------------------------------------------------------- /examples/wadi_topology/wadi_attacks.yaml: -------------------------------------------------------------------------------- 1 | device_attacks: 2 | - name: V_ER2i_attack 3 | trigger: 4 | type: time 5 | start: 125 6 | end: 375 7 | actuator: V_ER2i 8 | command: closed -------------------------------------------------------------------------------- /examples/wadi_topology/wadi_config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # General simulation parameters 3 | inp_file: wadi_map.inp 4 | network_topology_type: complex 5 | iterations: 500 6 | log_level: info 7 | mininet_cli: false 8 | 9 | plcs: !include wadi_plcs.yaml 10 | 11 | attacks: !include wadi_attacks.yaml 12 | 13 | noise_scale: 0.25 14 | 15 | # Initial Conditions 16 | initial_tank_data: initial_tank_wadi.csv 17 | network_loss_data: losses_wadi.csv 18 | network_delay_data: delays_wadi.csv 19 | -------------------------------------------------------------------------------- /examples/wadi_topology/wadi_plcs.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - T0 4 | actuators: 5 | - P_RAW1 6 | - V_PUB 7 | - name: PLC2 8 | sensors: 9 | - T2 10 | actuators: 11 | - V_ER2i -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cwd=$(pwd) 4 | version=$(lsb_release -rs ) 5 | 6 | doc=false 7 | test=false 8 | 9 | # Setting up test and doc parameters 10 | while getopts ":dt" opt; do 11 | case $opt in 12 | d) 13 | printf "Installing with documentation dependencies.\n" 14 | doc=true 15 | ;; 16 | t) 17 | printf "Installing with testing dependencies.\n" 18 | test=true 19 | ;; 20 | \?) 21 | printf "Unknown option. Proceeding without installing documentation and testing dependencies.\n" 22 | ;; 23 | esac 24 | done 25 | 26 | sleep 3 27 | 28 | # Update apt 29 | sudo apt update 30 | 31 | # Installing necessary packages 32 | sudo apt install -y git python3 python3-pip curl 33 | sudo python3 -m pip install cpppo 34 | 35 | # MiniCPS 36 | cd ~ 37 | git clone --depth 1 https://github.com/scy-phy/minicps.git || git -C minicps pull 38 | cd minicps 39 | sudo python3 -m pip install . 40 | 41 | # epynet - An EPANET Python wrapper for WNTR 42 | cd ~ 43 | git clone --depth 1 https://github.com/afmurillo/DHALSIM-epynet || git -C DHALSIM-epynet pull 44 | cd DHALSIM-epynet/ 45 | sudo python3 -m pip install . 46 | 47 | # Mininet from source 48 | cd ~ 49 | git clone --depth 1 -b 2.3.1b4 https://github.com/mininet/mininet.git || git -C mininet pull 50 | cd mininet 51 | sudo PYTHON=python3 ./util/install.sh -fnv 52 | 53 | # Installing testing pip dependencies 54 | if [ "$test" = true ] 55 | then 56 | sudo python3 -m pip install pytest-timeout 57 | sudo python3 -m pip install pytest-cov 58 | sudo python3 -m pip install pytest-mock 59 | fi 60 | 61 | # Install netfilterqueue for Simple DoS attacks 62 | sudo apt install -y libnfnetlink-dev libnetfilter-queue-dev 63 | sudo python3 -m pip install -U git+https://github.com/kti/python-netfilterqueue 64 | 65 | # Install DHALSIM 66 | cd "${cwd}" || { printf "Failure: Could not find DHALSIM directory\n"; exit 1; } 67 | 68 | # Install without doc and test 69 | if [ "$test" = false ] && [ "$doc" = false ] 70 | then 71 | sudo python3 -m pip install -e . 72 | 73 | printf "\nInstallation finished. You can now run DHALSIM by using \n\t.\n" 74 | exit 0; 75 | fi 76 | 77 | # Install doc 78 | if [ "$test" = false ] 79 | then 80 | sudo python3 -m pip install -e .[doc] 81 | 82 | printf "\nInstallation finished. You can now run DHALSIM by using \n\t.\n" 83 | exit 0; 84 | fi 85 | 86 | # Install test 87 | if [ "$doc" = false ] 88 | then 89 | sudo python3 -m pip install -e .[test] 90 | 91 | printf "\nInstallation finished. You can now run DHALSIM by using \n\t.\n" 92 | exit 0; 93 | fi 94 | 95 | # Install test and doc 96 | sudo python3 -m pip install -e .[test,doc] 97 | 98 | printf "\nInstallation finished. You can now run DHALSIM by using \n\t.\n" 99 | exit 0; 100 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools==59.8.0", 4 | "wheel" 5 | ] 6 | build-backend = "setuptools.build_meta" 7 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | markers = 3 | integrationtest: mark a test as a integration test. -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | pyyaml 2 | pytest 3 | mininet -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyyaml 2 | mininet 3 | scapy -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | setuptools.setup( 4 | name="dhalsim", 5 | version="1.1.1", 6 | url="https://github.com/Critical-Infrastructure-Systems-Lab/DHALSIM", 7 | project_urls={ 8 | "Bug Tracker": "https://github.com/Critical-Infrastructure-Systems-Lab/DHALSIM/issues", 9 | }, 10 | classifiers=[ 11 | "Programming Language :: Python :: 3", 12 | "License :: OSI Approved :: MIT License", 13 | # "Operating System :: OS Independent", 14 | ], 15 | license='MIT', 16 | packages=['dhalsim'], 17 | install_requires=[ 18 | 'pyyaml==6.0.1', 19 | 'pyyaml-include==2.1', 20 | 'antlr4-python3-runtime==4.13.1', 21 | 'progressbar2', 22 | 'numpy==1.24.3', 23 | 'wntr', 24 | 'pandas', 25 | 'matplotlib', 26 | 'schema', 27 | 'scapy', 28 | 'pathlib', 29 | 'testresources', 30 | 'pytest-mock', 31 | 'netaddr', 32 | 'flaky', 33 | 'pytest', 34 | 'tensorflow', 35 | 'scikit-learn', 36 | 'keras==2.13.1', 37 | 'pytest', 38 | 'pytest-mock', 39 | 'mock' 40 | ], 41 | extras_require={ 42 | 'test': ['wget', 'coverage', 'pytest-cov'], 43 | 'doc': ['sphinx', 'sphinx-rtd-theme', 'sphinx-prompt'], 44 | }, 45 | python_requires=">=3.8.10", 46 | entry_points={ 47 | 'console_scripts': [ 48 | 'dhalsim = dhalsim.command_line:main', 49 | ], 50 | }, 51 | ) 52 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=dhalsim 2 | sonar.sources=dhalsim 3 | sonar.python.coverage.reportPaths=coverage*.xml 4 | 5 | sonar.coverage.exclusions=dhalsim/physical_process.py,dhalsim/python2/basePLC.py,dhalsim/parser/antlr/**/* -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/test/__init__.py -------------------------------------------------------------------------------- /test/auxilary_testing_files/automatic_attacker_files.yaml: -------------------------------------------------------------------------------- 1 | network_attacks: 2 | - fakeData1 3 | - fakeData2 4 | output_path: fakeOutputPath 5 | log_level: info -------------------------------------------------------------------------------- /test/auxilary_testing_files/intermediate-wadi-attack.yaml: -------------------------------------------------------------------------------- 1 | inp_file: test/auxilary_testing_files/wadi_map_pda_original.inp 2 | iterations: 8640 3 | run_attack: True 4 | simulator: wntr 5 | mininet_cli: False 6 | 7 | plcs: 8 | - name: PLC1 9 | sensors: 10 | - T0 11 | actuators: 12 | - P_RAW1 13 | - V_PUB 14 | controls: 15 | - type: below 16 | dependant: T0 17 | value: 0.256 18 | actuator: V_PUB 19 | action: open 20 | - type: above 21 | dependant: T0 22 | value: 0.448 23 | actuator: V_PUB 24 | action: closed 25 | - type: below 26 | dependant: T0 27 | value: 0.256 28 | actuator: P_RAW1 29 | action: closed 30 | - type: below 31 | dependant: T2 32 | value: 0.16 33 | actuator: P_RAW1 34 | action: open 35 | - type: above 36 | dependant: T2 37 | value: 0.32 38 | actuator: P_RAW1 39 | action: closed 40 | - type: time 41 | value: 0 42 | actuator: V_PUB 43 | action: open 44 | - type: time 45 | value: 0 46 | actuator: P_RAW1 47 | action: closed 48 | attacks: 49 | - name: "Close PRAW1 from iteration 5 to 10" 50 | trigger: 51 | - type: "time" 52 | start: 5 53 | end: 10 54 | actuators: 55 | - P_RAW1 56 | command: "closed" 57 | - name: "Close PRAW1 when T2 < 0.16" 58 | trigger: 59 | - type: "below" 60 | sensor: "T2" 61 | value: 0.16 62 | actuators: 63 | - P_RAW1 64 | command: "closed" 65 | - name: PLC2 66 | sensors: 67 | - T2 68 | actuators: 69 | - V_ER2i 70 | controls: 71 | - type: above 72 | dependant: T2 73 | value: 0.32 74 | actuator: V_ER2i 75 | action: closed 76 | - type: below 77 | dependant: T2 78 | value: 0.16 79 | actuator: V_ER2i 80 | action: open 81 | - type: time 82 | value: 0 83 | actuator: V_ER2i 84 | action: closed 85 | 86 | valves: 87 | - name: V_Gi_G 88 | initial_state: closed 89 | - name: V_Gi_B 90 | initial_state: open 91 | - name: V_SWaT 92 | initial_state: closed 93 | - name: V_PUB 94 | initial_state: open 95 | - name: V_ER2i 96 | initial_state: open 97 | - name: V_ER2o 98 | initial_state: open 99 | - name: V_ER1i 100 | initial_state: closed 101 | - name: V_ER1o 102 | initial_state: closed 103 | - name: FCV_ER 104 | initial_state: open 105 | - name: FCV_RWin 106 | initial_state: open 107 | - name: '1' 108 | initial_state: open 109 | - name: '2' 110 | initial_state: open 111 | - name: '3' 112 | initial_state: open 113 | - name: '4' 114 | initial_state: open 115 | - name: '5' 116 | initial_state: open 117 | - name: '6' 118 | initial_state: open 119 | - name: '7' 120 | initial_state: open 121 | - name: '8' 122 | initial_state: open 123 | - name: '9' 124 | initial_state: open 125 | - name: '10' 126 | initial_state: open 127 | - name: '11' 128 | initial_state: open 129 | - name: '12' 130 | initial_state: open 131 | 132 | tanks: 133 | - name: T1 134 | initial_value: 0.3129975 135 | - name: T2 136 | initial_value: 0.2369489 137 | - name: T0 138 | initial_value: 0.4259549 139 | 140 | 141 | pumps: 142 | - name: P_RAW1 143 | initial_state: open 144 | - name: P_RAW2 145 | initial_state: closed 146 | - name: P_B2 147 | initial_state: closed 148 | - name: P_B1 149 | initial_state: open 150 | 151 | time: 152 | - duration: 518400.0 153 | - hydraulic_timestep: 60.0 154 | -------------------------------------------------------------------------------- /test/auxilary_testing_files/intermediate-wadi-pda-original.yaml: -------------------------------------------------------------------------------- 1 | inp_file: test/auxilary_testing_files/wadi_map_pda_original.inp 2 | iterations: 8640 3 | plcs: 4 | - name: PLC1 5 | sensors: 6 | - T0 7 | actuators: 8 | - P_RAW1 9 | - V_PUB 10 | controls: 11 | - type: below 12 | dependant: T0 13 | value: 0.256 14 | actuator: V_PUB 15 | action: open 16 | - type: above 17 | dependant: T0 18 | value: 0.448 19 | actuator: V_PUB 20 | action: closed 21 | - type: below 22 | dependant: T0 23 | value: 0.256 24 | actuator: P_RAW1 25 | action: closed 26 | - type: below 27 | dependant: T2 28 | value: 0.16 29 | actuator: P_RAW1 30 | action: open 31 | - type: above 32 | dependant: T2 33 | value: 0.32 34 | actuator: P_RAW1 35 | action: closed 36 | - type: time 37 | value: 0 38 | actuator: V_PUB 39 | action: open 40 | - type: time 41 | value: 0 42 | actuator: P_RAW1 43 | action: closed 44 | - name: PLC2 45 | sensors: 46 | - T2 47 | actuators: 48 | - V_ER2i 49 | controls: 50 | - type: above 51 | dependant: T2 52 | value: 0.32 53 | actuator: V_ER2i 54 | action: closed 55 | - type: below 56 | dependant: T2 57 | value: 0.16 58 | actuator: V_ER2i 59 | action: open 60 | - type: time 61 | value: 0 62 | actuator: V_ER2i 63 | action: closed 64 | 65 | actuators: 66 | - name: P_RAW1 67 | initial_state: open 68 | - name: P_RAW2 69 | initial_state: closed 70 | - name: P_B2 71 | initial_state: closed 72 | - name: P_B1 73 | initial_state: open 74 | - name: V_Gi_G 75 | initial_state: closed 76 | - name: V_Gi_B 77 | initial_state: open 78 | - name: V_SWaT 79 | initial_state: closed 80 | - name: V_PUB 81 | initial_state: open 82 | - name: V_ER2i 83 | initial_state: open 84 | - name: V_ER2o 85 | initial_state: open 86 | - name: V_ER1i 87 | initial_state: closed 88 | - name: V_ER1o 89 | initial_state: closed 90 | - name: FCV_ER 91 | initial_state: open 92 | - name: FCV_RWin 93 | initial_state: open 94 | - name: '1' 95 | initial_state: open 96 | - name: '2' 97 | initial_state: open 98 | - name: '3' 99 | initial_state: open 100 | - name: '4' 101 | initial_state: open 102 | - name: '5' 103 | initial_state: open 104 | - name: '6' 105 | initial_state: open 106 | - name: '7' 107 | initial_state: open 108 | - name: '8' 109 | initial_state: open 110 | - name: '9' 111 | initial_state: open 112 | - name: '10' 113 | initial_state: open 114 | - name: '11' 115 | initial_state: open 116 | - name: '12' 117 | initial_state: open 118 | log_level: debug 119 | simulator: wntr 120 | initial_tank_values: 121 | T0: '0.4259549' 122 | T1: '0.3129975' 123 | T2: '0.2369489' 124 | time: 125 | - duration: 518400.0 126 | - hydraulic_timestep: 60 127 | -------------------------------------------------------------------------------- /test/auxilary_testing_files/intermediate.yaml: -------------------------------------------------------------------------------- 1 | actuators: 2 | - initial_state: open 3 | name: P_RAW1 4 | - initial_state: closed 5 | name: P_RAW2 6 | - initial_state: closed 7 | name: P_B2 8 | - initial_state: open 9 | name: P_B1 10 | - initial_state: closed 11 | name: V_Gi_G 12 | - initial_state: open 13 | name: V_Gi_B 14 | - initial_state: closed 15 | name: V_SWaT 16 | - initial_state: open 17 | name: V_PUB 18 | - initial_state: open 19 | name: V_ER2i 20 | - initial_state: open 21 | name: V_ER2o 22 | - initial_state: closed 23 | name: V_ER1i 24 | - initial_state: closed 25 | name: V_ER1o 26 | - initial_state: open 27 | name: FCV_ER 28 | - initial_state: open 29 | name: FCV_RWin 30 | - initial_state: open 31 | name: '1' 32 | - initial_state: open 33 | name: '2' 34 | - initial_state: open 35 | name: '3' 36 | - initial_state: open 37 | name: '4' 38 | - initial_state: open 39 | name: '5' 40 | - initial_state: open 41 | name: '6' 42 | - initial_state: open 43 | name: '7' 44 | - initial_state: open 45 | name: '8' 46 | - initial_state: open 47 | name: '9' 48 | - initial_state: open 49 | name: '10' 50 | - initial_state: open 51 | name: '11' 52 | - initial_state: open 53 | name: '12' 54 | iterations: 2 55 | log_level: debug 56 | mininet_cli: false 57 | network_attacks: [] 58 | network_events: [] 59 | network_topology_type: simple 60 | noise_scale: 0.0 61 | plcs: 62 | - actuators: 63 | - P_RAW1 64 | - V_PUB 65 | attacks: 66 | - actuator: P_RAW1 67 | command: closed 68 | name: Close_PRAW1_from_iteration_5_to_10 69 | trigger: 70 | end: 10 71 | start: 5 72 | type: time 73 | - actuator: P_RAW1 74 | command: closed 75 | name: Close_PRAW1_when_T2_<_0.16 76 | trigger: 77 | sensor: T2 78 | type: below 79 | value: 0.16 80 | controls: 81 | - action: open 82 | actuator: V_PUB 83 | dependant: T0 84 | type: below 85 | value: 0.256 86 | - action: closed 87 | actuator: V_PUB 88 | dependant: T0 89 | type: above 90 | value: 0.448 91 | - action: closed 92 | actuator: P_RAW1 93 | dependant: T0 94 | type: below 95 | value: 0.256 96 | - action: open 97 | actuator: P_RAW1 98 | dependant: T2 99 | type: below 100 | value: 0.16 101 | - action: closed 102 | actuator: P_RAW1 103 | dependant: T2 104 | type: above 105 | value: 0.32 106 | - action: open 107 | actuator: V_PUB 108 | type: time 109 | value: 0 110 | - action: closed 111 | actuator: P_RAW1 112 | type: time 113 | value: 0 114 | name: PLC1 115 | sensors: 116 | - T0 117 | - actuators: 118 | - V_ER2i 119 | controls: 120 | - action: closed 121 | actuator: V_ER2i 122 | dependant: T2 123 | type: above 124 | value: 0.32 125 | - action: open 126 | actuator: V_ER2i 127 | dependant: T2 128 | type: below 129 | value: 0.16 130 | - action: closed 131 | actuator: V_ER2i 132 | type: time 133 | value: 0 134 | name: PLC2 135 | sensors: 136 | - T2 137 | simulator: wntr 138 | demand: pdd 139 | initial_tank_values: 140 | T0: '0.4259549' 141 | T1: '0.3129975' 142 | T2: '0.2369489' 143 | time: 144 | - duration: 518400.0 145 | - hydraulic_timestep: 60 146 | output_path: temp/test/path 147 | -------------------------------------------------------------------------------- /test/auxilary_testing_files/wadi_attacks.yaml: -------------------------------------------------------------------------------- 1 | device_attacks: 2 | - name: "Close_PRAW1_from_iteration_5_to_10" 3 | trigger: 4 | type: Time 5 | start: 5 6 | end: 10 7 | actuator: P_RAW1 8 | command: closed 9 | - name: "Close_PRAW1_when_T2_<_0.16" 10 | trigger: 11 | type: Below 12 | sensor: T2 13 | value: 0.16 14 | actuator: P_RAW1 15 | command: closed -------------------------------------------------------------------------------- /test/auxilary_testing_files/wadi_config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # General simulation parameters 3 | inp_file: "wadi_map_pda_original.inp" 4 | network_topology_type: Simple 5 | output_path: "output/" 6 | iterations: 2 7 | mininet_cli: false 8 | log_level: debug 9 | simulator: wntr 10 | 11 | plcs: !include wadi_plc.yaml 12 | 13 | attacks: !include wadi_attacks.yaml 14 | 15 | #batch_simulations: 3 16 | #Initial Conditions 17 | #initial_tank_data: initial_tank_wadi.csv 18 | #demand_patterns: demand_patterns/ 19 | #demand_patterns: demand_patterns/2.csv 20 | #network_loss_data: losses_wadi.csv 21 | #network_delay_data: delays_wadi.csv 22 | -------------------------------------------------------------------------------- /test/auxilary_testing_files/wadi_plc.yaml: -------------------------------------------------------------------------------- 1 | - name: PLC1 2 | sensors: 3 | - T0 4 | actuators: 5 | - P_RAW1 6 | - V_PUB 7 | - name: PLC2 8 | sensors: 9 | - T2 10 | actuators: 11 | - V_ER2i -------------------------------------------------------------------------------- /test/network_attacks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/test/network_attacks/__init__.py -------------------------------------------------------------------------------- /test/network_attacks/test_utilities.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | from mock import call, Mock 5 | from scapy.layers.l2 import ARP, Ether 6 | 7 | from dhalsim.network_attacks.utilities import spoof_arp_cache, launch_arp_poison, restore_arp, \ 8 | get_mac, translate_payload_to_float, translate_float_to_payload 9 | 10 | 11 | def test_python_version(): 12 | assert sys.version_info.major is 3 13 | 14 | 15 | @pytest.fixture 16 | def get_mac_mock(mocker): 17 | macs = { 18 | '192.168.1.1': '42:aa:3d:69:21:bf', 19 | '192.168.1.2': 'aa:bb:cc:dd:ee:02', 20 | } 21 | 22 | mocked_get_mac = mocker.patch('dhalsim.network_attacks.utilities.get_mac') 23 | mocked_get_mac.side_effect = (lambda x: macs[x]) 24 | 25 | return mocked_get_mac 26 | 27 | 28 | @pytest.fixture 29 | def send_mock(mocker): 30 | mocked_send = mocker.patch('dhalsim.network_attacks.utilities.send') 31 | return mocked_send 32 | 33 | 34 | @pytest.fixture 35 | def srp_mock(mocker): 36 | mocked_srp = mocker.patch('dhalsim.network_attacks.utilities.srp') 37 | return mocked_srp 38 | 39 | 40 | def test_spoof_arp_cache(send_mock): 41 | spoof_arp_cache('192.168.1.1', '42:aa:3d:69:21:bf', '192.168.1.2') 42 | 43 | send_mock.assert_called_once_with( 44 | ARP(op=2, pdst='192.168.1.1', psrc='192.168.1.2', hwdst='42:aa:3d:69:21:bf'), verbose=False) 45 | 46 | 47 | def test_launch_arp_poison(get_mac_mock, send_mock): 48 | launch_arp_poison('192.168.1.1', '192.168.1.2') 49 | 50 | get_mac_mock.assert_has_calls([call('192.168.1.1'), call('192.168.1.2')], any_order=True) 51 | assert get_mac_mock.call_count == 2 52 | 53 | send_mock.assert_has_calls([ 54 | call(ARP(op=2, pdst='192.168.1.1', psrc='192.168.1.2', hwdst='42:aa:3d:69:21:bf'), 55 | verbose=False), 56 | call(ARP(op=2, pdst='192.168.1.2', psrc='192.168.1.1', hwdst='aa:bb:cc:dd:ee:02'), 57 | verbose=False) 58 | ], any_order=True) 59 | assert send_mock.call_count == 2 60 | 61 | 62 | def test_restore_arp(get_mac_mock, send_mock): 63 | restore_arp('192.168.1.1', '192.168.1.2') 64 | 65 | get_mac_mock.assert_has_calls([call('192.168.1.1'), call('192.168.1.2')], any_order=True) 66 | assert get_mac_mock.call_count == 2 67 | 68 | send_mock.assert_has_calls([ 69 | call(ARP(op=2, pdst='192.168.1.1', hwdst='42:aa:3d:69:21:bf', psrc='192.168.1.2', 70 | hwsrc='aa:bb:cc:dd:ee:02'), verbose=False), 71 | call(ARP(op=2, pdst='192.168.1.2', hwdst='aa:bb:cc:dd:ee:02', psrc='192.168.1.1', 72 | hwsrc='42:aa:3d:69:21:bf'), verbose=False) 73 | ], any_order=True) 74 | assert send_mock.call_count == 2 75 | 76 | 77 | def test_get_mac(srp_mock): 78 | result = Mock() 79 | result.hwsrc = 'aa:bb:cc:dd:ee:03' 80 | srp_mock.return_value = [[[{}, result]]] 81 | 82 | assert get_mac('192.168.1.3') == 'aa:bb:cc:dd:ee:03' 83 | 84 | arp_packet = Ether(dst='ff:ff:ff:ff:ff:ff') / ARP(op=1, pdst='192.168.1.3') 85 | srp_mock.assert_called_once_with(arp_packet, timeout=2, verbose=False) 86 | 87 | 88 | def test_translate_payload_to_float(): 89 | payload = b'o\x00\x1a\x00\x91\xb9\x1df\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2\x00\n\x00\xcc\x00\x00\x00\xca\x00\x18\x89\x81>' 90 | expected = 0.25299906730651855 91 | 92 | assert translate_payload_to_float(payload) == expected 93 | 94 | 95 | def test_translate_float_to_payload(): 96 | payload = b'o\x00\x1a\x00\x91\xb9\x1df\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2\x00\n\x00\xcc\x00\x00\x00\xca\x00\x00\x00\x00>' 97 | value = 0.25299906730651855 98 | expected = b'o\x00\x1a\x00\x91\xb9\x1df\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2\x00\n\x00\xcc\x00\x00\x00\xca\x00\x18\x89\x81>' 99 | 100 | assert translate_float_to_payload(value, payload) == expected 101 | -------------------------------------------------------------------------------- /test/parser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/test/parser/__init__.py -------------------------------------------------------------------------------- /test/parser/test_batch_readme_generator.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from pathlib import Path 3 | 4 | import pytest 5 | from wntr.network import WaterNetworkModel, Options 6 | 7 | from dhalsim.parser.file_generator import BatchReadmeGenerator 8 | 9 | 10 | @pytest.fixture 11 | def batch_gen(mocker): 12 | mocker.patch.object(BatchReadmeGenerator, "__init__", return_value=None) 13 | 14 | return BatchReadmeGenerator(None) 15 | 16 | 17 | @pytest.fixture 18 | def water_network_model(mocker): 19 | wn = mocker.patch.object(WaterNetworkModel, "__init__", return_value=None) 20 | opts = mocker.patch.object(Options, "__init__", return_value=None) 21 | opts.time.hydraulic_timestep = 100 22 | wn.options = opts 23 | return wn 24 | 25 | 26 | @pytest.fixture(scope="session") 27 | def tmp_path(tmpdir_factory): 28 | return tmpdir_factory.mktemp("data").join("batch_readme.md") 29 | 30 | 31 | def test_init(water_network_model): 32 | batch_rm_gen = BatchReadmeGenerator(Path(__file__).parent.parent / 'auxilary_testing_files' 33 | / 'intermediate.yaml', Path('../readme.md'), 34 | datetime(year=2021, month=6, day=1, second=1), 35 | datetime(year=2021, month=6, day=1, second=2), 36 | water_network_model, 5, 300) 37 | assert batch_rm_gen.readme_path == Path('../readme.md') 38 | assert batch_rm_gen.start_time == datetime(year=2021, month=6, day=1, second=1) 39 | assert batch_rm_gen.end_time == datetime(year=2021, month=6, day=1, second=2) 40 | assert batch_rm_gen.wn == water_network_model 41 | assert batch_rm_gen.master_time == 5 42 | 43 | 44 | def test_get_batch_information(batch_gen): 45 | batch_gen.intermediate_yaml = {'inp_file': 'mymap.inp', 'batch_index': 0, 46 | 'batch_simulations': 2} 47 | assert batch_gen.get_batch_information() == "# Auto-generated README of mymap for batch" \ 48 | " 1\n\nThis is batch 1 out of 2." 49 | 50 | 51 | def test_get_initial_tank_values(batch_gen): 52 | batch_gen.intermediate_yaml = {'initial_tank_values': {'T2': 2.08023668123243}} 53 | assert batch_gen.get_initial_tank_values() == "\n\n## Initial tank data" \ 54 | "\n\n{'T2': 2.08023668123243}" 55 | 56 | 57 | def test_get_network_loss_value(batch_gen): 58 | batch_gen.intermediate_yaml = {'network_loss_values': {'PLC1': 0.1427168730880501, 59 | 'PLC2': 0.2708496621297529, 60 | 'scada': 0.1649651164870355}} 61 | assert batch_gen.get_network_loss_value() == "\n\n## Network loss values\n\n" \ 62 | "{'PLC1': 0.1427168730880501, 'PLC2':" \ 63 | " 0.2708496621297529, 'scada': 0.1649651164870355}" 64 | 65 | 66 | def test_get_network_delay_values(batch_gen): 67 | batch_gen.intermediate_yaml = {'network_delay_values': {'PLC1': '89.55084013904876ms', 68 | 'PLC2': '46.17408713948684ms', 69 | 'scada': '4.134693808027478ms'}} 70 | assert batch_gen.get_network_delay_values() == "\n\n## Network delay values\n\n{'PLC1':" \ 71 | " '89.55084013904876ms', 'PLC2': '46.17408713948684ms'," \ 72 | " 'scada': '4.134693808027478ms'}" 73 | 74 | 75 | def test_get_time_information(batch_gen, water_network_model): 76 | batch_gen.master_time = 1 77 | batch_gen.intermediate_yaml = {'iterations': 4} 78 | 79 | water_network_model.options.time.hydraulic_timestep = 100 80 | batch_gen.hydraulic_timestep = 100 81 | batch_gen.wn = water_network_model 82 | batch_gen.start_time = datetime(year=2021, month=6, day=1, second=1) 83 | batch_gen.end_time = datetime(year=2021, month=6, day=1, second=2) 84 | assert batch_gen.get_time_information() == "\n\n## Information about this batch\n\nRan for 1 " \ 85 | "out of 4 iterations with hydraulic timestep 100." \ 86 | "\n\nStarted at 2021-06-01 00:00:01 and finished at " \ 87 | "2021-06-01 00:00:02.\n\nThe duration of this batch was" \ 88 | " 0:00:01." 89 | 90 | 91 | def test_verify_written(batch_gen, tmp_path, water_network_model): 92 | batch_gen.readme_path = tmp_path 93 | batch_gen.start_time = datetime(year=2021, month=6, day=1, second=1) 94 | batch_gen.end_time = datetime(year=2021, month=6, day=1, second=2) 95 | batch_gen.wn = water_network_model 96 | batch_gen.hydraulic_timestep = 100 97 | batch_gen.master_time = 1 98 | batch_gen.intermediate_yaml = {'inp_file': 'my_map.inp', 'batch_index': 0, 99 | 'batch_simulations': 2, 'iterations': 3} 100 | 101 | batch_gen.write_batch() 102 | 103 | open(tmp_path, 'r') 104 | 105 | -------------------------------------------------------------------------------- /test/parser/test_inp_parser.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pytest 3 | import yaml 4 | from pathlib import Path 5 | 6 | from dhalsim.parser.input_parser import InputParser 7 | 8 | 9 | @pytest.fixture 10 | def inp_path(tmpdir): 11 | return Path("test/auxilary_testing_files/wadi_map_pda_original.inp") 12 | 13 | 14 | @pytest.fixture 15 | def initial_dict(inp_path): 16 | return {"simulator": "wntr", "log_level": "debug", "inp_file": str(inp_path), 17 | "plcs": [{"name": "PLC1", "actuators": ["P_RAW1", "V_PUB"], "sensors": ["T0"]}, 18 | {"name": "PLC2", "actuators": ["V_ER2i"], "sensors": ["T2"]}]} 19 | 20 | 21 | @pytest.fixture 22 | def filled_yaml_path(): 23 | return Path("test/auxilary_testing_files/intermediate-wadi-pda-original.yaml") 24 | 25 | 26 | @pytest.fixture 27 | def written_intermediate_yaml(tmpdir, initial_dict): 28 | intermediate = tmpdir.join("intermediate.yaml") 29 | 30 | with intermediate.open(mode='w') as intermediate_yaml: 31 | yaml.dump(initial_dict, intermediate_yaml) 32 | return tmpdir.join("intermediate.yaml") 33 | 34 | 35 | def test_python_version(): 36 | assert sys.version_info.major is 3 37 | 38 | 39 | def test_node_and_time_controls(tmpdir, written_intermediate_yaml, filled_yaml_path): 40 | with written_intermediate_yaml.open(mode='r') as intermediate_yaml: 41 | original_data = yaml.safe_load(intermediate_yaml) 42 | 43 | filled_data = InputParser(original_data).write() 44 | 45 | with filled_yaml_path.open(mode='r') as expectation: 46 | expected = yaml.safe_load(expectation) 47 | 48 | assert filled_data == expected 49 | -------------------------------------------------------------------------------- /test/physical_process/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/test/physical_process/__init__.py -------------------------------------------------------------------------------- /test/physical_process/test_physical_process.py: -------------------------------------------------------------------------------- 1 | from dhalsim.physical_process import PhysicalPlant 2 | from pathlib import Path 3 | import pytest 4 | import filecmp 5 | import yaml 6 | 7 | @pytest.fixture 8 | def no_controls_path(tmpdir): 9 | return Path("test/auxilary_testing_files/wadi_map_pda_original_no_controls.inp") 10 | 11 | @pytest.fixture 12 | def controls_path(tmpdir): 13 | return Path("test/auxilary_testing_files/wadi_map_pda_original.inp") 14 | 15 | @pytest.fixture 16 | def filled_yaml_path(): 17 | return Path("test/auxilary_testing_files/intermediate-wadi-pda-original.yaml") 18 | 19 | 20 | def get_physical_plant(): 21 | """ 22 | Gets an instance of PhysicalPlant, using intermediate.yaml at auxilary_testing_files. 23 | """ 24 | return PhysicalPlant(Path('../auxilary_testing_files/intermediate.yaml')) 25 | 26 | #def test_remove_controls_from_inp_for_epynet(tmpdir, no_controls_path, controls_path, filled_yaml_path): 27 | # with filled_yaml_path.open(mode='r') as intermediate_yaml: 28 | # options = yaml.safe_load(intermediate_yaml) 29 | # plant = PhysicalPlant(intermediate_yaml) 30 | 31 | # original_inp_filename = options['inp_file'].rsplit('.', 1)[0] 32 | # processed_inp_filename = original_inp_filename + '_processed.inp' 33 | 34 | #no_controls_original = no_controls_path.open(mode='r') 35 | #no_control_process = processed_inp_filename.open(mode='r') 36 | 37 | #filecmp.cmp(no_controls_path, processed_inp_filename, shallow=True) 38 | -------------------------------------------------------------------------------- /test/python2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/test/python2/__init__.py -------------------------------------------------------------------------------- /test/python2/attacks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/test/python2/attacks/__init__.py -------------------------------------------------------------------------------- /test/python2/attacks/test_attacks.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pytest 3 | import pytest_mock 4 | from mock import MagicMock, call 5 | 6 | from dhalsim.python2.entities.attack import TimeAttack, TriggerBelowAttack, TriggerAboveAttack, TriggerBetweenAttack 7 | 8 | 9 | @pytest.fixture 10 | def time_attack(): 11 | return TimeAttack("TestTimeAttack", "P_RAW1", "closed", 20, 40) 12 | 13 | 14 | @pytest.fixture 15 | def trigger_attack_above(): 16 | return TriggerAboveAttack("TestAboveAttack", "P_RAW1", "closed", "T1", 0.20) 17 | 18 | 19 | @pytest.fixture 20 | def trigger_attack_below(): 21 | return TriggerBelowAttack("TestBelowAttack", "P_RAW1", "closed", "T1", 0.20) 22 | 23 | 24 | @pytest.fixture 25 | def trigger_between_attack(): 26 | return TriggerBetweenAttack("TestBetweenAttack", "P_RAW1", "closed", "T1", 0.10, 0.16) 27 | 28 | 29 | @pytest.fixture 30 | def mock_plc1(mocker): 31 | mocker.patch( 32 | 'dhalsim.python2.generic_plc.GenericPLC', 33 | mock_plc1 34 | ) 35 | mock = MagicMock() 36 | mock.get_tag.return_value = 0.35 37 | mock.set_tag.return_value = None 38 | mock.get_master_clock.return_value = 30 39 | return mock 40 | 41 | 42 | @pytest.fixture 43 | def mock_plc2(mocker): 44 | mocker.patch( 45 | 'dhalsim.python2.generic_plc.GenericPLC', 46 | mock_plc2 47 | ) 48 | mock = MagicMock() 49 | mock.get_tag.return_value = 0.15 50 | mock.set_tag.return_value = None 51 | mock.get_master_clock.return_value = 10 52 | return mock 53 | 54 | 55 | # Test that we are running python 2.7 56 | def test_python_version(): 57 | assert sys.version_info.major is 2 58 | assert sys.version_info.minor is 7 59 | 60 | 61 | def test_time_properties(time_attack): 62 | assert time_attack.name == "TestTimeAttack" 63 | assert time_attack.actuator == "P_RAW1" 64 | assert time_attack.command == "closed" 65 | assert time_attack.start == 20 66 | assert time_attack.end == 40 67 | 68 | 69 | def test_trigger_above_properties(trigger_attack_above): 70 | assert trigger_attack_above.name == "TestAboveAttack" 71 | assert trigger_attack_above.actuator == "P_RAW1" 72 | assert trigger_attack_above.command == "closed" 73 | assert trigger_attack_above.sensor == "T1" 74 | assert trigger_attack_above.value == 0.20 75 | 76 | 77 | def test_trigger_below_properties(trigger_attack_below): 78 | assert trigger_attack_below.name == "TestBelowAttack" 79 | assert trigger_attack_below.actuator == "P_RAW1" 80 | assert trigger_attack_below.command == "closed" 81 | assert trigger_attack_below.sensor == "T1" 82 | assert trigger_attack_below.value == 0.20 83 | 84 | 85 | def test_trigger_between_attack(trigger_between_attack): 86 | assert trigger_between_attack.name == "TestBetweenAttack" 87 | assert trigger_between_attack.actuator == "P_RAW1" 88 | assert trigger_between_attack.command == "closed" 89 | assert trigger_between_attack.sensor == "T1" 90 | assert trigger_between_attack.lower_value == 0.10 91 | assert trigger_between_attack.upper_value == 0.16 92 | 93 | 94 | def test_time_attack_apply_true(time_attack, mock_plc1): 95 | assert time_attack.apply(mock_plc1) is None 96 | mock_plc1.get_master_clock.assert_called_with() 97 | mock_plc1.set_tag.assert_any_call('P_RAW1', 'closed') 98 | 99 | 100 | def test_time_attack_apply_false(time_attack, mock_plc2): 101 | assert time_attack.apply(mock_plc2) is None 102 | mock_plc2.get_master_clock.assert_called_with() 103 | mock_plc2.set_tag.assert_not_called() 104 | 105 | 106 | def test_above_attack_apply_true(trigger_attack_above, mock_plc1): 107 | assert trigger_attack_above.apply(mock_plc1) is None 108 | mock_plc1.get_tag.assert_called_with('T1') 109 | mock_plc1.set_tag.assert_any_call('P_RAW1', 'closed') 110 | 111 | 112 | def test_above_attack_apply_false(trigger_attack_above, mock_plc2): 113 | assert trigger_attack_above.apply(mock_plc2) is None 114 | mock_plc2.get_tag.assert_called_with('T1') 115 | mock_plc2.set_tag.assert_not_called() 116 | 117 | 118 | def test_below_attack_apply_true(trigger_attack_below, mock_plc2): 119 | assert trigger_attack_below.apply(mock_plc2) is None 120 | mock_plc2.get_tag.assert_called_with('T1') 121 | mock_plc2.set_tag.assert_any_call('P_RAW1', 'closed') 122 | 123 | 124 | def test_below_attack_apply_false(trigger_attack_below, mock_plc1): 125 | assert trigger_attack_below.apply(mock_plc1) is None 126 | mock_plc1.get_tag.assert_called_with('T1') 127 | mock_plc1.set_tag.assert_not_called() 128 | 129 | 130 | def test_between_attack_apply_true(trigger_between_attack, mock_plc2): 131 | assert trigger_between_attack.apply(mock_plc2) is None 132 | mock_plc2.get_tag.assert_called_with('T1') 133 | mock_plc2.set_tag.assert_any_call('P_RAW1', 'closed') 134 | 135 | 136 | def test_between_attack_apply_false(trigger_between_attack, mock_plc1): 137 | assert trigger_between_attack.apply(mock_plc1) is None 138 | mock_plc1.get_tag.assert_called_with('T1') 139 | mock_plc1.set_tag.assert_not_called() 140 | -------------------------------------------------------------------------------- /test/python2/automatic_nodes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/test/python2/automatic_nodes/__init__.py -------------------------------------------------------------------------------- /test/python2/automatic_nodes/test_automatic_plant.py: -------------------------------------------------------------------------------- 1 | import signal 2 | import subprocess 3 | 4 | import pytest 5 | import yaml 6 | from mock import call, ANY 7 | from pathlib import Path 8 | 9 | from dhalsim.python2.automatic_plant import PlantControl 10 | 11 | 12 | @pytest.fixture 13 | def offline_after_three_process(mocker): 14 | process = mocker.Mock() 15 | process.poll.side_effect = [None, None, 42] 16 | process.send_signal.return_value = None 17 | process.terminate.return_value = None 18 | process.kill.return_value = None 19 | return process 20 | 21 | 22 | @pytest.fixture 23 | def subprocess_mock(mocker, offline_after_three_process): 24 | process = mocker.Mock() 25 | process.Popen.return_value = offline_after_three_process 26 | process.open.return_value = "testLoc" 27 | Path(__file__).parent.absolute() 28 | return process 29 | 30 | 31 | @pytest.fixture 32 | def patched_auto_plant(mocker, subprocess_mock): 33 | mocker.patch.object(PlantControl, "__init__", return_value=None) 34 | mocker.patch("subprocess.Popen", subprocess_mock.Popen) 35 | mocker.patch("__builtin__.open", subprocess_mock.open) 36 | logger_mock = mocker.Mock() 37 | 38 | auto_plant = PlantControl(None) 39 | auto_plant.logger = logger_mock 40 | 41 | return auto_plant, logger_mock 42 | 43 | 44 | @pytest.fixture 45 | def plant_yaml(): 46 | return Path("test/auxilary_testing_files/intermediate.yaml") 47 | 48 | 49 | def test_init(plant_yaml): 50 | plant = PlantControl(plant_yaml) 51 | assert plant.intermediate_yaml == plant_yaml 52 | assert plant.simulation_process is None 53 | assert plant.logger is not None 54 | 55 | with plant_yaml.open(mode='r') as file: 56 | expected = yaml.safe_load(file) 57 | 58 | assert plant.data == expected 59 | 60 | 61 | def test_terminate(patched_auto_plant, offline_after_three_process): 62 | auto_plant, logger_mock = patched_auto_plant 63 | auto_plant.simulation_process = offline_after_three_process 64 | 65 | auto_plant.terminate() 66 | 67 | logger_mock.debug.assert_called() 68 | 69 | offline_after_three_process.send_signal.assert_called_once_with(signal.SIGINT) 70 | assert offline_after_three_process.wait.call_count == 1 71 | assert offline_after_three_process.poll.call_count == 3 72 | assert offline_after_three_process.terminate.call_count == 1 73 | assert offline_after_three_process.kill.call_count == 0 74 | 75 | 76 | @pytest.mark.timeout(1) 77 | def test_main(mocker, patched_auto_plant, offline_after_three_process, subprocess_mock): 78 | auto_plant, logger_mock = patched_auto_plant 79 | mocker.patch.object(PlantControl, "terminate", return_value=None) 80 | auto_plant.intermediate_yaml = "plant_yaml" 81 | 82 | auto_plant.main() 83 | 84 | subprocess_mock.Popen.assert_called_once_with(["python3", ANY, "plant_yaml"]) 85 | 86 | offline_after_three_process.send_signal.assert_not_called() 87 | offline_after_three_process.wait.assert_not_called() 88 | assert offline_after_three_process.poll.call_count == 3 89 | offline_after_three_process.terminate.assert_not_called() 90 | offline_after_three_process.kill.assert_not_called() 91 | -------------------------------------------------------------------------------- /test/python2/controls/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/test/python2/controls/__init__.py -------------------------------------------------------------------------------- /test/python2/controls/test_controls.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from mock import MagicMock, call 3 | import sys 4 | import pytest 5 | 6 | from dhalsim.python2.entities.control import AboveControl, BelowControl, TimeControl 7 | 8 | 9 | def test_python_version(): 10 | assert sys.version_info.major is 2 11 | assert sys.version_info.minor is 7 12 | 13 | 14 | @pytest.fixture 15 | def mock_plc1(mocker): 16 | mocker.patch( 17 | 'dhalsim.python2.generic_plc.GenericPLC', 18 | mock_plc1 19 | ) 20 | mock = MagicMock() 21 | mock.get_tag.return_value = 20 22 | mock.set_tag.return_value = None 23 | mock.get_master_clock.return_value = 43 24 | return mock 25 | 26 | 27 | @pytest.fixture 28 | def mock_plc2(mocker): 29 | mocker.patch( 30 | 'dhalsim.python2.generic_plc.GenericPLC', 31 | mock_plc2 32 | ) 33 | mock = MagicMock() 34 | mock.get_tag.return_value = 3000 35 | mock.set_tag.return_value = None 36 | mock.get_master_clock.return_value = 10 37 | return mock 38 | 39 | 40 | @pytest.fixture 41 | def below_fixture(): 42 | return BelowControl("testActuator1", "action1", "testTank1", 41) 43 | 44 | 45 | @pytest.fixture 46 | def above_fixture(): 47 | return AboveControl("testActuator2", "action2", "testTank2", 42) 48 | 49 | 50 | @pytest.fixture 51 | def time_fixture(): 52 | return TimeControl("testActuator3", "action3", 43) 53 | 54 | 55 | def test_python_version(): 56 | assert sys.version_info.major is 2 57 | assert sys.version_info.minor is 7 58 | 59 | 60 | def test_below_properties(below_fixture): 61 | assert below_fixture.actuator == "testActuator1" 62 | assert below_fixture.action == "action1" 63 | assert below_fixture.dependant == "testTank1" 64 | assert below_fixture.value == 41 65 | 66 | 67 | def test_above_properties(above_fixture): 68 | assert above_fixture.actuator == "testActuator2" 69 | assert above_fixture.action == "action2" 70 | assert above_fixture.dependant == "testTank2" 71 | assert above_fixture.value == 42 72 | 73 | 74 | def test_time_properties(time_fixture): 75 | assert time_fixture.actuator == "testActuator3" 76 | assert time_fixture.action == "action3" 77 | assert time_fixture.value == 43 78 | 79 | 80 | def test_apply_true_BelowControl(below_fixture, mock_plc1): 81 | assert below_fixture.apply(mock_plc1) is None 82 | # Assert call.get tag called, and below value was true 83 | mock_plc1.get_tag.assert_called_with('testTank1') 84 | mock_plc1.set_tag.assert_called_with('testActuator1', 'action1') 85 | 86 | 87 | def test_apply_false_BelowControl(below_fixture, mock_plc2): 88 | assert below_fixture.apply(mock_plc2) is None 89 | # Assert call.get tag called, and below value was false 90 | mock_plc2.get_tag.assert_called_with('testTank1') 91 | mock_plc2.set_tag.assert_not_called() 92 | 93 | 94 | def test_apply_true_AboveControl(above_fixture, mock_plc2): 95 | assert above_fixture.apply(mock_plc2) is None 96 | # Assert call.get tag called, and above value was true 97 | mock_plc2.get_tag.assert_called_with('testTank2') 98 | mock_plc2.set_tag.assert_called_with('testActuator2', 'action2') 99 | 100 | 101 | def test_apply_false_AboveControl(above_fixture, mock_plc1): 102 | assert above_fixture.apply(mock_plc1) is None 103 | # Assert call.get tag called, and above value was false 104 | mock_plc1.get_tag.assert_called_with('testTank2') 105 | mock_plc1.set_tag.assert_not_called() 106 | 107 | def test_apply_true_TimeControl(time_fixture, mock_plc1): 108 | assert time_fixture.apply(mock_plc1) is None 109 | # Assert call.get tag called, and time == is true 110 | mock_plc1.get_master_clock.assert_called_with() 111 | mock_plc1.set_tag.assert_called_with('testActuator3', 'action3') 112 | 113 | 114 | def test_apply_false_TimeControl(time_fixture, mock_plc2): 115 | assert time_fixture.apply(mock_plc2) is None 116 | # Assert call.get tag called, and above value was true 117 | mock_plc2.get_master_clock.assert_called_with() 118 | mock_plc2.set_tag.assert_not_called() -------------------------------------------------------------------------------- /test/python2/test_automatic_run.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from mock import call 3 | 4 | from dhalsim.python2.automatic_run import GeneralCPS 5 | 6 | 7 | @pytest.fixture 8 | def online_process(mocker): 9 | process = mocker.Mock() 10 | process.poll.return_value = None 11 | return process 12 | 13 | 14 | @pytest.fixture 15 | def offline_after_five_process(mocker): 16 | process = mocker.Mock() 17 | process.poll.side_effect = [None, None, None, None, 42] 18 | return process 19 | 20 | 21 | @pytest.fixture 22 | def offline_after_three_process(mocker): 23 | process = mocker.Mock() 24 | process.poll.side_effect = [None, None, 42] 25 | return process 26 | 27 | 28 | @pytest.fixture 29 | def patched_auto_run(mocker): 30 | mocker.patch.object(GeneralCPS, "__init__", return_value=None) 31 | logger_mock = mocker.Mock() 32 | 33 | auto_run = GeneralCPS(None) 34 | auto_run.logger = logger_mock 35 | 36 | return auto_run, logger_mock 37 | 38 | 39 | @pytest.mark.timeout(1) 40 | def test_plant_shutdown_normal(patched_auto_run, online_process, offline_after_five_process): 41 | auto_run, logger_mock = patched_auto_run 42 | auto_run.plc_processes = [online_process] 43 | auto_run.attacker_processes = [online_process] 44 | auto_run.scada_process = online_process 45 | auto_run.plant_process = offline_after_five_process 46 | 47 | auto_run.poll_processes() 48 | 49 | assert logger_mock.debug.call_count == 1 50 | assert offline_after_five_process.poll.call_count == 5 51 | assert online_process.poll.call_count == 15 52 | 53 | 54 | @pytest.mark.timeout(1) 55 | def test_plc_process_shutdown(patched_auto_run, online_process, offline_after_three_process): 56 | auto_run, logger_mock = patched_auto_run 57 | auto_run.plc_processes = [online_process, offline_after_three_process] 58 | auto_run.attacker_processes = [online_process] 59 | auto_run.scada_process = online_process 60 | auto_run.plant_process = online_process 61 | 62 | auto_run.poll_processes() 63 | 64 | assert logger_mock.debug.call_count == 1 65 | assert offline_after_three_process.poll.call_count == 3 66 | assert online_process.poll.call_count == 9 67 | 68 | 69 | @pytest.mark.timeout(1) 70 | def test_attacker_process_shutdown(patched_auto_run, online_process, offline_after_three_process): 71 | auto_run, logger_mock = patched_auto_run 72 | auto_run.plc_processes = [online_process] 73 | auto_run.attacker_processes = [online_process, offline_after_three_process] 74 | auto_run.scada_process = online_process 75 | auto_run.plant_process = online_process 76 | 77 | auto_run.poll_processes() 78 | 79 | assert logger_mock.debug.call_count == 1 80 | assert offline_after_three_process.poll.call_count == 3 81 | assert online_process.poll.call_count == 10 82 | 83 | 84 | @pytest.mark.timeout(1) 85 | def test_scada_process_shutdown(patched_auto_run, online_process, offline_after_five_process): 86 | auto_run, logger_mock = patched_auto_run 87 | auto_run.plc_processes = [online_process] 88 | auto_run.attacker_processes = [online_process] 89 | auto_run.scada_process = offline_after_five_process 90 | auto_run.plant_process = online_process 91 | 92 | auto_run.poll_processes() 93 | 94 | assert logger_mock.debug.call_count == 1 95 | assert offline_after_five_process.poll.call_count == 5 96 | assert online_process.poll.call_count == 14 97 | -------------------------------------------------------------------------------- /test/python2/test_generic_plc_cache.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | import pytest 5 | import yaml 6 | from mock import MagicMock, call 7 | 8 | from dhalsim.python2.generic_plc import GenericPLC 9 | 10 | 11 | @pytest.fixture 12 | def magic_mock_network(): 13 | mock = MagicMock() 14 | # network 15 | mock.get.return_value = u'42' 16 | mock.set.return_value = u'42' 17 | mock.receive.return_value = u'0.15' 18 | # database 19 | mock.get_sync.return_value = 0 20 | mock.set_sync.return_value = None 21 | return mock 22 | 23 | 24 | @pytest.fixture 25 | def magic_mock_init(): 26 | mock = MagicMock() 27 | mock.do_super_construction.return_value = None 28 | mock.initialize_db.return_value = None 29 | return mock 30 | 31 | 32 | @pytest.fixture 33 | def yaml_file(tmpdir): 34 | dict = { 35 | "log_level": "info", 36 | "db_path": "/home/test/dhalsim.sqlite", 37 | "plcs": [{"name": "PLC1", 38 | "local_ip": "192.168.1.1", 39 | "public_ip": "192.168.1.1", 40 | "sensors": ["T0", ], 41 | "actuators": ["P_RAW1", ], 42 | "controls": [{"type": "Below", 43 | "dependant": "T2", 44 | "value": 0.16, 45 | "actuator": "P_RAW1", 46 | "action": "OPEN"}, 47 | {"type": "Below", 48 | "dependant": "T2", 49 | "value": 0.16, 50 | "actuator": "P_RAW1", 51 | "action": "CLOSED"}, ] 52 | }, 53 | {"name": "PLC2", 54 | "local_ip": "192.168.1.2", 55 | "public_ip": "192.168.1.2", 56 | "sensors": ["T2", ], 57 | "actuators": ["V_ER2i", ], 58 | "controls": [{"type": "Above", 59 | "dependant": "T2", 60 | "value": 0.32, 61 | "actuator": "V_ER2i", 62 | "action": "CLOSED"}, ] 63 | }, ], 64 | } 65 | file = tmpdir.join("intermediate.yaml") 66 | with file.open(mode='w') as intermediate_yaml: 67 | yaml.dump(dict, intermediate_yaml) 68 | return file 69 | 70 | 71 | def patch_methods(magic_mock_init, magic_mock_network, mocker): 72 | # Init mocker patches 73 | mocker.patch( 74 | 'dhalsim.python2.generic_plc.GenericPLC.initialize_db', 75 | magic_mock_init.initialize_db 76 | ) 77 | mocker.patch( 78 | 'dhalsim.python2.generic_plc.GenericPLC.do_super_construction', 79 | magic_mock_init.do_super_construction 80 | ) 81 | # Network mocker patches 82 | mocker.patch( 83 | 'dhalsim.python2.generic_plc.GenericPLC.get', 84 | magic_mock_network.get 85 | ) 86 | mocker.patch( 87 | 'dhalsim.python2.generic_plc.GenericPLC.set', 88 | magic_mock_network.set 89 | ) 90 | mocker.patch( 91 | 'dhalsim.python2.generic_plc.GenericPLC.receive', 92 | magic_mock_network.receive 93 | ) 94 | mocker.patch( 95 | 'dhalsim.python2.generic_plc.GenericPLC.get_sync', 96 | magic_mock_network.get_sync 97 | ) 98 | mocker.patch( 99 | 'dhalsim.python2.generic_plc.GenericPLC.set_sync', 100 | magic_mock_network.set_sync 101 | ) 102 | 103 | 104 | @pytest.fixture 105 | def generic_plc1(mocker, yaml_file, magic_mock_init, magic_mock_network): 106 | patch_methods(magic_mock_init, magic_mock_network, mocker) 107 | return GenericPLC(Path(str(yaml_file)), 0) 108 | 109 | 110 | def test_python_version(): 111 | assert sys.version_info.major is 2 112 | assert sys.version_info.minor is 7 113 | 114 | 115 | def test_generic_plc1_cache(generic_plc1, magic_mock_network): 116 | generic_plc1.main_loop(test_break=True) 117 | # Verify network function calls (applying control rule) 118 | expected_network_calls = [call.get_sync(), 119 | call.receive(('T2', 1), '192.168.1.2'), # Only called once 120 | call.set(('P_RAW1', 1), 1), 121 | call.set(('P_RAW1', 1), 0), 122 | call.set_sync(1)] 123 | assert magic_mock_network.mock_calls == expected_network_calls 124 | -------------------------------------------------------------------------------- /test/python2/topo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critical-Infrastructure-Systems-Lab/DHALSIM/f5dddfefb6cad7fe78d7b7ab5ce42787f4e116cd/test/python2/topo/__init__.py -------------------------------------------------------------------------------- /test/python2/topo/test_complex_topo_with_attackers.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | 4 | import pytest 5 | import yaml 6 | from mininet.net import Mininet 7 | from mininet.link import TCLink 8 | 9 | from dhalsim.python2.topo.complex_topo import ComplexTopo 10 | 11 | 12 | def test_python_version(): 13 | assert sys.version_info.major is 2 14 | assert sys.version_info.minor is 7 15 | 16 | 17 | @pytest.fixture 18 | def unmodified_yaml(tmpdir): 19 | dict = {'plcs': [{'name': 'PLC1', }, {'name': 'PLC2', }, ], 20 | 'network_attacks': [{'name': 'attack1', 'target': 'PLC2'}, 21 | {'name': 'attack2', 'target': 'PLC2'}, 22 | {'name': 'attack3', 'target': 'PLC1'}, 23 | {'name': 'attack4', 'target': 'scada'}, ]} 24 | file = tmpdir.join('intermediate.yaml') 25 | with file.open(mode='w') as intermediate_yaml: 26 | yaml.dump(dict, intermediate_yaml) 27 | return file 28 | 29 | 30 | @pytest.fixture 31 | def topo(unmodified_yaml): 32 | return ComplexTopo(unmodified_yaml) 33 | 34 | 35 | @pytest.fixture 36 | def net(topo): 37 | net = Mininet(topo=topo, link=TCLink) 38 | net.start() 39 | topo.setup_network(net) 40 | time.sleep(0.2) 41 | yield net 42 | net.stop() 43 | 44 | 45 | @pytest.mark.integrationtest 46 | @pytest.mark.parametrize('host1, host2', 47 | [('r0', 'r1'), ('r0', 'r2'), ('r0', 'r3'), ('r2', 'PLC1'), ('r3', 'PLC2'), 48 | ('r1', 'scada'), ('attack1', 'PLC2'), ('attack1', 'r3'), 49 | ('attack2', 'PLC2'), 50 | ('attack2', 'r3'), ('attack3', 'PLC1'), ('attack3', 'r2'), 51 | ('attack4', 'scada'), 52 | ('attack4', 'r1')]) 53 | @pytest.mark.flaky(max_runs=3) 54 | def test_ping(net, host1, host2): 55 | assert net.ping(hosts=[net.get(host1), net.get(host2)]) == 0.0 56 | 57 | 58 | @pytest.mark.integrationtest 59 | @pytest.mark.parametrize('host1, host2', 60 | [('r0', 'r1'), ('r0', 'r2'), ('r0', 'r3'), ('r1', 's1'), ('r2', 's2'), 61 | ('r3', 's3'), ('s2', 'PLC1'), ('s3', 'PLC2'), ('s3', 'attack1'), 62 | ('s3', 'attack2'), 63 | ('s2', 'attack3'), ('s1', 'attack4'), ('s1', 'scada')]) 64 | def test_links(net, host1, host2): 65 | assert net.linksBetween(net.get(host1), net.get(host2)) != [] 66 | 67 | 68 | @pytest.mark.integrationtest 69 | @pytest.mark.parametrize('host1, host2, mac1, mac2', 70 | [('r0', 'r1', 'aa:bb:cc:dd:00:', 'aa:bb:cc:dd:04:'), 71 | ('r0', 'r2', 'aa:bb:cc:dd:00:', 'aa:bb:cc:dd:04:'), 72 | ('r0', 'r3', 'aa:bb:cc:dd:00:', 'aa:bb:cc:dd:04:'), 73 | ('r1', 's1', 'aa:bb:cc:dd:03:', ''), 74 | ('r2', 's2', 'aa:bb:cc:dd:03:', ''), 75 | ('r3', 's3', 'aa:bb:cc:dd:03:', ''), 76 | ('s2', 'PLC1', '', 'aa:bb:cc:dd:02:'), 77 | ('s3', 'PLC2', '', 'aa:bb:cc:dd:02:'), 78 | ('s3', 'attack1', '', 'aa:bb:cc:dd:05:'), 79 | ('s3', 'attack2', '', 'aa:bb:cc:dd:05:'), 80 | ('s2', 'attack3', '', 'aa:bb:cc:dd:05:'), 81 | ('s1', 'attack4', '', 'aa:bb:cc:dd:05:'), 82 | ('s1', 'scada', '', 'aa:bb:cc:dd:01:')]) 83 | def test_mac_prefix(net, host1, host2, mac1, mac2): 84 | links = net.linksBetween(net.get(host1), net.get(host2)) 85 | assert links != [] 86 | link = links[0] 87 | full_mac_1 = link.intf1.MAC().lower() 88 | full_mac_2 = link.intf2.MAC().lower() 89 | assert (full_mac_1.startswith(mac1.lower()) and full_mac_2.startswith(mac2.lower())) or ( 90 | full_mac_1.startswith(mac2.lower()) and full_mac_2.startswith(mac1.lower())) 91 | 92 | 93 | @pytest.mark.integrationtest 94 | def test_number_of_links(net): 95 | assert len(net.links) == 13 96 | 97 | 98 | @pytest.mark.integrationtest 99 | @pytest.mark.parametrize('server, client, server_ip', 100 | [('PLC1', 'r0', '10.0.2.1'), ('PLC2', 'r0', '10.0.3.1'), 101 | ('PLC1', 'PLC2', '10.0.2.1'), ('PLC2', 'PLC1', '10.0.3.1'), 102 | ('PLC1', 'scada', '10.0.2.1'), ('PLC2', 'scada', '10.0.3.1'), 103 | ('PLC1', 'attack1', '10.0.2.1'), ('PLC2', 'attack1', '192.168.1.1'), 104 | ('PLC1', 'attack2', '10.0.2.1'), ('PLC2', 'attack2', '192.168.1.1'), 105 | ('PLC1', 'attack3', '192.168.1.1'), ('PLC2', 'attack3', '10.0.3.1'), 106 | ('PLC1', 'attack4', '10.0.2.1'), ('PLC2', 'attack4', '10.0.3.1')]) 107 | @pytest.mark.flaky(max_runs=3) 108 | def test_reachability(net, server, client, server_ip): 109 | net.get(server).cmd("echo 'test' | nc -q1 -l 44818 &") 110 | time.sleep(0.1) 111 | response = net.get(client).cmd("wget -qO - {ip}:44818".format(ip=server_ip)) 112 | assert response.rstrip() == 'test' 113 | -------------------------------------------------------------------------------- /test/python2/topo/test_simple_topo_with_attackers.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | 4 | import pytest 5 | import yaml 6 | from mininet.net import Mininet 7 | from mininet.link import TCLink 8 | from netaddr import IPNetwork, IPAddress 9 | 10 | from dhalsim.python2.topo.simple_topo import SimpleTopo 11 | 12 | 13 | def test_python_version(): 14 | assert sys.version_info.major is 2 15 | assert sys.version_info.minor is 7 16 | 17 | 18 | @pytest.fixture 19 | def unmodified_yaml(tmpdir): 20 | dict = {'plcs': [{'name': 'PLC1', }, {'name': 'PLC2', }, ], 21 | 'network_attacks': [{'name': 'attack1', 'target': 'PLC2'}, {'name': 'attack2', 'target': 'PLC2'}, 22 | {'name': 'attack3', 'target': 'PLC1'}, {'name': 'attack4', 'target': 'scada'}, ]} 23 | file = tmpdir.join('intermediate.yaml') 24 | with file.open(mode='w') as intermediate_yaml: 25 | yaml.dump(dict, intermediate_yaml) 26 | return file 27 | 28 | 29 | @pytest.fixture 30 | def topo(unmodified_yaml): 31 | return SimpleTopo(unmodified_yaml) 32 | 33 | 34 | @pytest.fixture 35 | def net(topo): 36 | net = Mininet(topo=topo, autoSetMacs=False, link=TCLink) 37 | net.start() 38 | topo.setup_network(net) 39 | time.sleep(0.2) 40 | yield net 41 | net.stop() 42 | 43 | 44 | @pytest.mark.integrationtest 45 | @pytest.mark.parametrize('host1, host2', 46 | [('r0', 'PLC1'), ('r0', 'PLC2'), ('r0', 'scada'), ('r0', 'attack1'), ('r0', 'attack2'), 47 | ('r0', 'attack3'), ('r0', 'attack4'), ('PLC1', 'attack1'), ('PLC1', 'attack2'), 48 | ('PLC1', 'attack3'), ('PLC1', 'attack4'), ('PLC2', 'attack1'), ('PLC2', 'attack2'), 49 | ('PLC2', 'attack3'), ('PLC2', 'attack4'), ('scada', 'attack1'), ('scada', 'attack2'), 50 | ('scada', 'attack3'), ('scada', 'attack4')]) 51 | @pytest.mark.flaky(max_runs=3) 52 | def test_ping(net, host1, host2): 53 | assert net.ping(hosts=[net.get(host1), net.get(host2)]) == 0.0 54 | 55 | 56 | @pytest.mark.integrationtest 57 | @pytest.mark.parametrize('host1, host2', 58 | [('r0', 's1'), ('r0', 's2'), ('s1', 'PLC1'), ('s1', 'PLC2'), ('s1', 'attack1'), 59 | ('s1', 'attack2'), ('s1', 'attack3'), ('s2', 'scada'), ('s2', 'attack4')]) 60 | def test_links(net, host1, host2): 61 | assert net.linksBetween(net.get(host1), net.get(host2)) != [] 62 | 63 | 64 | @pytest.mark.integrationtest 65 | @pytest.mark.parametrize('host1, host2, mac1, mac2', 66 | [('r0', 's1', 'aa:bb:cc:dd:00:01', ''), 67 | ('r0', 's2', 'aa:bb:cc:dd:00:02', ''), 68 | ('s1', 'PLC1', '', 'aa:bb:cc:dd:02:'), 69 | ('s1', 'PLC2', '', 'aa:bb:cc:dd:02:'), 70 | ('s1', 'attack1', '', 'aa:bb:cc:dd:05:'), 71 | ('s1', 'attack2', '', 'aa:bb:cc:dd:05:'), 72 | ('s1', 'attack3', '', 'aa:bb:cc:dd:05:'), 73 | ('s2', 'scada', '', 'aa:bb:cc:dd:01:'), 74 | ('s2', 'attack4', '', 'aa:bb:cc:dd:05:')]) 75 | def test_mac_prefix(net, host1, host2, mac1, mac2): 76 | links = net.linksBetween(net.get(host1), net.get(host2)) 77 | assert links != [] 78 | link = links[0] 79 | full_mac_1 = link.intf1.MAC().lower() 80 | full_mac_2 = link.intf2.MAC().lower() 81 | assert (full_mac_1.startswith(mac1.lower()) and full_mac_2.startswith(mac2.lower())) or ( 82 | full_mac_1.startswith(mac2.lower()) and full_mac_2.startswith(mac1.lower())) 83 | 84 | 85 | @pytest.mark.integrationtest 86 | def test_number_of_links(net): 87 | assert len(net.links) == 9 88 | 89 | 90 | @pytest.mark.integrationtest 91 | @pytest.mark.parametrize('hosts', 92 | [['PLC1', 'PLC2', 'attack1', 'attack2', 'attack3'], ['scada', 'attack4']]) 93 | def test_double_ips(net, hosts): 94 | ip_list = [net.get(node).IP() for node in hosts] 95 | assert len(ip_list) == len(set(ip_list)) 96 | 97 | 98 | @pytest.mark.integrationtest 99 | @pytest.mark.parametrize('host, subnet', 100 | [('PLC1', "192.168.1.0/24"), ('PLC2', "192.168.1.0/24"), ('attack1', "192.168.1.0/24"), 101 | ('attack2', "192.168.1.0/24"), ('attack3', "192.168.1.0/24"), ('scada', "192.168.2.0/24"), 102 | ('attack4', "192.168.2.0/24")]) 103 | def test_subnet_ips(net, host, subnet): 104 | assert IPAddress(net.get(host).IP()) in IPNetwork(subnet) 105 | 106 | 107 | @pytest.mark.integrationtest 108 | @pytest.mark.parametrize("server, client, server_ip", 109 | [("PLC1", "r0", "192.168.1.1"), ("PLC2", "r0", "192.168.1.2"), 110 | # ("PLC1", "PLC2", "192.168.1.1"), ("PLC2", "PLC1", "192.168.1.2"), 111 | ("PLC1", "scada", "192.168.1.1"), ("PLC2", "scada", "192.168.1.2")]) 112 | @pytest.mark.flaky(max_runs=3) 113 | def test_reachability(net, server, client, server_ip): 114 | net.get(server).cmd("echo 'test' | nc -q1 -l 44818 &") 115 | time.sleep(0.1) 116 | response = net.get(client).cmd("wget -qO - {ip}:44818".format(ip=server_ip)) 117 | assert response.rstrip() == "test" 118 | --------------------------------------------------------------------------------