From 7f68f82b83dd2feffcea643635f60466b37d3c18 Mon Sep 17 00:00:00 2001 From: noursaidi Date: Sun, 16 May 2021 21:06:10 +0100 Subject: [PATCH 1/8] break existing ntp_update into sepreate functions --- subset/network/ntp_tests.py | 138 +++++++++++++++++++++++++++++------- subset/network/test_network | 4 +- 2 files changed, 114 insertions(+), 28 deletions(-) diff --git a/subset/network/ntp_tests.py b/subset/network/ntp_tests.py index 094b45e75f..d10530bc61 100644 --- a/subset/network/ntp_tests.py +++ b/subset/network/ntp_tests.py @@ -1,12 +1,15 @@ from __future__ import absolute_import, division -from scapy.all import NTP, rdpcap +from scapy.all import NTP, DNSQR, rdpcap, DNS import sys import os +import re +import json arguments = sys.argv test_request = str(arguments[1]) pcap_file = str(arguments[2]) +device_address = str(arguments[3]) report_filename = 'ntp_tests.txt' ignore = '%%' @@ -14,7 +17,8 @@ result = 'fail' dash_break_line = '--------------------\n' description_ntp_support = 'Device supports NTP version 4.' -description_ntp_update = 'Device synchronizes its time to the NTP server.' +description_ntp_update_dhcp = 'Device synchronizes its time to the NTP server using DHCP.' +description_ntp_update_dns = 'Device synchronizes its time to the NTP server using DNS' NTP_VERSION_PASS = 4 LOCAL_PREFIX = '10.20.' @@ -26,22 +30,25 @@ OFFSET_ALLOWANCE = 0.128 LEAP_ALARM = 3 +IP_REGEX = r'(([0-9]{1,3}\.){3}[0-9]{1,3})' +NTP_SERVER_IP_SUFFIX = '.2' +NTP_SERVER_HOSTNAME = 'ntp.daqlocal' def write_report(string_to_append): with open(report_filename, 'a+') as file_open: file_open.write(string_to_append) -# Extracts the NTP version from the first client NTP packet def ntp_client_version(capture): + """ Extracts the NTP version from the first client NTP packet """ client_packets = ntp_packets(capture, MODE_CLIENT) if len(client_packets) == 0: return None return ntp_payload(client_packets[0]).version -# Filters the packets by type (NTP) def ntp_packets(capture, mode=None): + """ Filters the packets by type (NTP) """ packets = [] for packet in capture: if NTP in packet: @@ -53,8 +60,30 @@ def ntp_packets(capture, mode=None): return packets -# Extracts the NTP payload from a packet of type NTP +def ntp_configured_by_dns(): + """Checks module_config + """ + module_config = open('module_config.json') + module_config = json.load(module_config) + try: + ntp_by_dns = bool(module_config['modules']['network']['ntp_dns']) + except KeyError: + ntp_by_dns = False + return ntp_by_dns + + + +def dns_query_for_hostname(capture, mode=None): + """Finds if """ + packets = [] + for packet in capture: + if DNS in packet: + packets.append(packet.qd.qname) + return packets + + def ntp_payload(packet): + """ Extracts the NTP payload from a packet of type NTP """ ip = packet.payload udp = ip.payload ntp = udp.payload @@ -62,6 +91,7 @@ def ntp_payload(packet): def test_ntp_support(): + """ Tests support for NTPv4 """ capture = rdpcap(pcap_file) packets = ntp_packets(capture) if len(packets) > 0: @@ -80,26 +110,60 @@ def test_ntp_support(): return 'skip' -def test_ntp_update(): - capture = rdpcap(pcap_file) - packets = ntp_packets(capture) - if len(packets) < 2: - add_summary("Not enough NTP packets received.") - return 'skip' - # Check that DAQ NTP server has been used - using_local_server = False +def dns_requests_for_hostname(hostname, packet_capture): + """Checks for DNS requests for a given hostname + + Args: + packet_capture path to tcpdump packet capture file + hostname hostname to look for + + Returns: + true/false if any matching DNS requests detected to hostname + """ + capture = rdpcap(packet_capture) + fqdn = hostname + '.' + for packet in capture: + if DNS in packet: + if (packet.qd.qname.decode("utf8") == fqdn): + return True + return False + + +def ntp_server_from_ip(ip_address): + """Returns the IP address of the NTP server provided by DAQ + + Args: + ip_address: IP address of the device under test + + Returns: + IP address of NTP server + """ + return re.sub(r'\.\d+$', NTP_SERVER_IP_SUFFIX, ip_address) + + +def check_ntp_synchronized(ntp_packets_array, ntp_server_ip): + """ Checks if NTP packets indicate a device is syncronized with the provided + IP address + + Args: + packet_capture Array of scapy object of packet capture with NTP + packets from ntp_packets() + ntp_server_ip IP address of server to check + + Returns: + boolean true/false if synchronized with provided NTP server. + """ + local_ntp_packets = [] - for packet in packets: - # Packet is to or from local NTP server - if ((packet.payload.dst.startswith(LOCAL_PREFIX) and - packet.payload.dst.endswith(NTP_SERVER_SUFFIX)) or - (packet.payload.src.startswith(LOCAL_PREFIX) and - packet.payload.src.endswith(NTP_SERVER_SUFFIX))): - using_local_server = True + for packet in ntp_packets_array: + # Packet is to or from NTP server + if (packet.payload.dst == ntp_server_ip or packet.payload.dst == ntp_server_ip): + using_given_server = True local_ntp_packets.append(packet) - if not using_local_server or len(local_ntp_packets) < 2: - add_summary("Device clock not synchronized with local NTP server.") - return 'fail' + + if not using_given_server or len(local_ntp_packets) < 2: + return False + # Obtain the latest NTP poll p1 = p2 = p3 = p4 = None for i in range(len(local_ntp_packets)): @@ -122,9 +186,10 @@ def test_ntp_update(): p3 = p4 = None else: p3 = local_ntp_packets[i] + if p1 is None or p2 is None: - add_summary("Device clock not synchronized with local NTP server.") - return 'fail' + return False + t1 = ntp_payload(p1).sent t2 = ntp_payload(p1).time t3 = ntp_payload(p2).sent @@ -142,6 +207,22 @@ def test_ntp_update(): offset = abs((t2 - t1) + (t3 - t4))/2 if offset < OFFSET_ALLOWANCE and not ntp_payload(p1).leap == LEAP_ALARM: + return True + else: + return False + +def test_ntp_update(): + """Runs NTP Update Test """ + capture = rdpcap(pcap_file) + packets = ntp_packets(capture) + if len(packets) < 2: + add_summary("Not enough NTP packets received.") + return 'skip' + + local_ntp_ip = ntp_server_from_ip(device_address) + is_sync_with_local = check_ntp_synchronized(packets, local_ntp_ip) + + if is_sync_with_local: add_summary("Device clock synchronized.") return 'pass' else: @@ -153,7 +234,7 @@ def add_summary(text): global summary_text summary_text = summary_text + " " + text if summary_text else text - +""" write_report("{b}{t}\n{b}".format(b=dash_break_line, t=test_request)) @@ -165,3 +246,8 @@ def add_summary(text): result = test_ntp_update() write_report("RESULT {r} {t} {s}\n".format(r=result, t=test_request, s=summary_text.strip())) +""" + +print(dns_requests_for_hostname('ntp.ubuntu.com','monitor.pcap')) +print(ntp_server_from_ip('123.123.123.123')) +print(ntp_configured_by_dns()) \ No newline at end of file diff --git a/subset/network/test_network b/subset/network/test_network index 022c91bedb..ee75e471f5 100755 --- a/subset/network/test_network +++ b/subset/network/test_network @@ -11,8 +11,8 @@ python network_tests.py communication.network.type $MONITOR $TARGET_IP cat network_tests.txt >> $REPORT # NTP Tests -python ntp_tests.py ntp.network.ntp_support $MONITOR -python ntp_tests.py ntp.network.ntp_update $MONITOR +python ntp_tests.py ntp.network.ntp_support $MONITOR $TARGET_IP +python ntp_tests.py ntp.network.ntp_update $MONITOR $TARGET_IP cat ntp_tests.txt >> $REPORT From 2ac77b458fb1ccc30a59d1a47b79382aefa51afb Mon Sep 17 00:00:00 2001 From: noursaidi Date: Sun, 16 May 2021 22:04:14 +0100 Subject: [PATCH 2/8] Add support for both dhcp and dns --- docs/device_report.md | 19 ++- resources/setups/common/tests_config.json | 7 +- subset/network/Dockerfile.test_network | 4 +- subset/network/__init__.py | 0 subset/network/ntp_tests.py | 165 +++++++++++----------- subset/network/test_network | 12 +- subset/network/test_result.py | 31 ++++ testing/test_aux.out | 9 +- 8 files changed, 150 insertions(+), 97 deletions(-) create mode 100644 subset/network/__init__.py create mode 100644 subset/network/test_result.py diff --git a/docs/device_report.md b/docs/device_report.md index 1047645436..1c03236c3c 100644 --- a/docs/device_report.md +++ b/docs/device_report.md @@ -53,7 +53,7 @@ Overall device result FAIL |Base|2|FAIL|1/0/1|0/0/0|0/0/0|0/0/0|0/0/0|0/0/0| |Connection|12|FAIL|3/5/4|0/0/0|0/0/0|0/0/0|0/0/0|0/0/0| |Security|13|FAIL|2/4/4|0/0/0|0/0/1|0/0/0|0/2/0|0/0/0| -|NTP|2|PASS|2/0/0|0/0/0|0/0/0|0/0/0|0/0/0|0/0/0| +|NTP|3|PASS|2/0/1|0/0/0|0/0/0|0/0/0|0/0/0|0/0/0| |DNS|1|SKIP|0/0/1|0/0/0|0/0/0|0/0/0|0/0/0|0/0/0| |Communication|2|PASS|2/0/0|0/0/0|0/0/0|0/0/0|0/0/0|0/0/0| |Protocol|2|FAIL|0/0/0|0/0/0|0/1/1|0/0/0|0/0/0|0/0/0| @@ -64,7 +64,7 @@ Syntax: Pass / Fail / Skip |Expectation|pass|fail|skip|gone| |---|---|---|---|---| -|Required Pass|10|1|10|8| +|Required Pass|10|1|11|8| |Required Pass for PoE Devices|0|0|1|0| |Required Pass for BACnet Devices|0|1|2|0| |Required Pass for IoT Devices|0|0|1|0| @@ -97,7 +97,8 @@ Syntax: Pass / Fail / Skip |skip|dns.network.hostname_resolution|DNS|Required Pass|Device did not send any DNS requests| |pass|dot1x.dot1x|Other|Other|Authentication for 9a:02:57:1e:8f:01 succeeded.| |pass|ntp.network.ntp_support|NTP|Required Pass|Using NTPv4.| -|pass|ntp.network.ntp_update|NTP|Required Pass|Device clock synchronized.| +|pass|ntp.network.ntp_update_dhcp|NTP|Required Pass|Device clock synchronized.| +|skip|ntp.network.ntp_update_dns|NTP|Required Pass|Device not configured for NTP via DNS| |skip|poe.switch.power|PoE|Required Pass for PoE Devices|No local IP has been set, check system config| |fail|protocol.bacext.pic|Protocol|Required Pass for BACnet Devices|PICS file defined however a BACnet device was not found.| |skip|protocol.bacext.version|Protocol|Required Pass for BACnet Devices|Bacnet device not found.| @@ -557,11 +558,17 @@ Device supports NTP version 4. -------------------- RESULT pass ntp.network.ntp_support Using NTPv4. -------------------- -ntp.network.ntp_update +ntp.network.ntp_update_dhcp -------------------- -Device synchronizes its time to the NTP server. +Device synchronizes its time to the NTP server using DHCP -------------------- -RESULT pass ntp.network.ntp_update Device clock synchronized. +RESULT pass ntp.network.ntp_update_dhcp Device clock synchronized. +-------------------- +ntp.network.ntp_update_dns +-------------------- +Device synchronizes its time to the NTP server using DNS +-------------------- +RESULT skip ntp.network.ntp_update_dns Device not configured for NTP via DNS -------------------- connection.network.mac_oui -------------------- diff --git a/resources/setups/common/tests_config.json b/resources/setups/common/tests_config.json index 163163a88d..24a5ce5ce6 100644 --- a/resources/setups/common/tests_config.json +++ b/resources/setups/common/tests_config.json @@ -89,7 +89,12 @@ "required": "pass", "expected": "Required Pass" }, - "ntp.network.ntp_update": { + "ntp.network.ntp_update_dhcp": { + "category": "NTP", + "required": "pass", + "expected": "Required Pass" + }, + "ntp.network.ntp_update_dns": { "category": "NTP", "required": "pass", "expected": "Required Pass" diff --git a/subset/network/Dockerfile.test_network b/subset/network/Dockerfile.test_network index ef4b204207..bd50bc0a5e 100644 --- a/subset/network/Dockerfile.test_network +++ b/subset/network/Dockerfile.test_network @@ -4,11 +4,11 @@ RUN $AG update && $AG install openjdk-8-jre RUN $AG update && $AG install openjdk-8-jdk git -RUN $AG update && $AG install python python-setuptools python-pip netcat +RUN $AG update && $AG install python3.8 python3-setuptools python3-pip netcat RUN $AG update && $AG install curl -RUN pip install scapy +RUN python3.8 -m pip install scapy COPY subset/network/ . diff --git a/subset/network/__init__.py b/subset/network/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/subset/network/ntp_tests.py b/subset/network/ntp_tests.py index d10530bc61..0c7505e7ed 100644 --- a/subset/network/ntp_tests.py +++ b/subset/network/ntp_tests.py @@ -1,9 +1,9 @@ from __future__ import absolute_import, division -from scapy.all import NTP, DNSQR, rdpcap, DNS import sys -import os import re import json +import test_result +from scapy.all import NTP, rdpcap, DNS arguments = sys.argv @@ -12,12 +12,9 @@ device_address = str(arguments[3]) report_filename = 'ntp_tests.txt' -ignore = '%%' -summary_text = '' -result = 'fail' -dash_break_line = '--------------------\n' + description_ntp_support = 'Device supports NTP version 4.' -description_ntp_update_dhcp = 'Device synchronizes its time to the NTP server using DHCP.' +description_ntp_update_dhcp = 'Device synchronizes its time to the NTP server using DHCP' description_ntp_update_dns = 'Device synchronizes its time to the NTP server using DNS' NTP_VERSION_PASS = 4 @@ -33,6 +30,10 @@ IP_REGEX = r'(([0-9]{1,3}\.){3}[0-9]{1,3})' NTP_SERVER_IP_SUFFIX = '.2' NTP_SERVER_HOSTNAME = 'ntp.daqlocal' +MODULE_CONFIG_PATH = '/config/device/module_config.json' + +TEST_DHCP = 'dhpc' +TEST_DNS = 'dns' def write_report(string_to_append): with open(report_filename, 'a+') as file_open: @@ -61,25 +62,18 @@ def ntp_packets(capture, mode=None): def ntp_configured_by_dns(): - """Checks module_config + """Checks module_config for parameter that NTP is configured using DNS + + Parameter must be (bool) True, else will be considered false """ - module_config = open('module_config.json') + module_config = open(MODULE_CONFIG_PATH) module_config = json.load(module_config) try: - ntp_by_dns = bool(module_config['modules']['network']['ntp_dns']) + ntp_by_dns = (module_config['modules']['network']['ntp_dns']) except KeyError: ntp_by_dns = False - return ntp_by_dns - - -def dns_query_for_hostname(capture, mode=None): - """Finds if """ - packets = [] - for packet in capture: - if DNS in packet: - packets.append(packet.qd.qname) - return packets + return ntp_by_dns is True def ntp_payload(packet): @@ -90,33 +84,13 @@ def ntp_payload(packet): return ntp -def test_ntp_support(): - """ Tests support for NTPv4 """ - capture = rdpcap(pcap_file) - packets = ntp_packets(capture) - if len(packets) > 0: - version = ntp_client_version(packets) - if version is None: - add_summary("No NTP packets received.") - return 'skip' - if version == NTP_VERSION_PASS: - add_summary("Using NTPv" + str(NTP_VERSION_PASS) + ".") - return 'pass' - else: - add_summary("Not using NTPv" + str(NTP_VERSION_PASS) + ".") - return 'fail' - else: - add_summary("No NTP packets received.") - return 'skip' - - def dns_requests_for_hostname(hostname, packet_capture): """Checks for DNS requests for a given hostname Args: packet_capture path to tcpdump packet capture file hostname hostname to look for - + Returns: true/false if any matching DNS requests detected to hostname """ @@ -124,7 +98,7 @@ def dns_requests_for_hostname(hostname, packet_capture): fqdn = hostname + '.' for packet in capture: if DNS in packet: - if (packet.qd.qname.decode("utf8") == fqdn): + if packet.qd.qname.decode("utf8") == fqdn: return True return False @@ -136,7 +110,7 @@ def ntp_server_from_ip(ip_address): ip_address: IP address of the device under test Returns: - IP address of NTP server + IP address of NTP server """ return re.sub(r'\.\d+$', NTP_SERVER_IP_SUFFIX, ip_address) @@ -145,25 +119,26 @@ def check_ntp_synchronized(ntp_packets_array, ntp_server_ip): """ Checks if NTP packets indicate a device is syncronized with the provided IP address - Args: + Args: packet_capture Array of scapy object of packet capture with NTP - packets from ntp_packets() - ntp_server_ip IP address of server to check + packets from ntp_packets() + ntp_server_ip IP address of server to checK Returns: boolean true/false if synchronized with provided NTP server. """ local_ntp_packets = [] + using_given_server = False for packet in ntp_packets_array: # Packet is to or from NTP server - if (packet.payload.dst == ntp_server_ip or packet.payload.dst == ntp_server_ip): + if (packet.payload.dst == ntp_server_ip or packet.payload.src == ntp_server_ip): using_given_server = True local_ntp_packets.append(packet) if not using_given_server or len(local_ntp_packets) < 2: return False - + # Obtain the latest NTP poll p1 = p2 = p3 = p4 = None for i in range(len(local_ntp_packets)): @@ -186,9 +161,9 @@ def check_ntp_synchronized(ntp_packets_array, ntp_server_ip): p3 = p4 = None else: p3 = local_ntp_packets[i] - + if p1 is None or p2 is None: - return False + return False t1 = ntp_payload(p1).sent t2 = ntp_payload(p1).time @@ -211,43 +186,75 @@ def check_ntp_synchronized(ntp_packets_array, ntp_server_ip): else: return False -def test_ntp_update(): - """Runs NTP Update Test """ + +def test_ntp_support(): + """ Tests support for NTPv4 """ capture = rdpcap(pcap_file) packets = ntp_packets(capture) - if len(packets) < 2: - add_summary("Not enough NTP packets received.") - return 'skip' + test_ntp = test_result.test_result( name='ntp.network.ntp_support', + description=description_ntp_support) + if len(packets) > 0: + version = ntp_client_version(packets) + if version is None: + test_ntp.add_summary("No NTP packets received.") + test_ntp.result = test_result.SKIP + if version == NTP_VERSION_PASS: + test_ntp.add_summary("Using NTPv" + str(NTP_VERSION_PASS) + ".") + test_ntp.result = test_result.PASS + else: + test_ntp.add_summary("Not using NTPv" + str(NTP_VERSION_PASS) + ".") + test_ntp.result = test_result.FAIL + else: + test_ntp.add_summary("No NTP packets received.") + test_ntp.result = test_result.SKIP - local_ntp_ip = ntp_server_from_ip(device_address) - is_sync_with_local = check_ntp_synchronized(packets, local_ntp_ip) + test_ntp.write_results(report_filename) - if is_sync_with_local: - add_summary("Device clock synchronized.") - return 'pass' - else: - add_summary("Device clock not synchronized with local NTP server.") - return 'fail' +def test_ntp_update(): + """Runs NTP Update Test for both DHCP and DNS""" + # Used to always print test output in the same order + ntp_tests = {} + ntp_tests[TEST_DHCP] = test_result.test_result( + name='ntp.network.ntp_update_dhcp', + description=description_ntp_update_dhcp) + ntp_tests[TEST_DNS] = test_result.test_result( + name='ntp.network.ntp_update_dns', + description=description_ntp_update_dns) + + capture = rdpcap(pcap_file) + packets = ntp_packets(capture) -def add_summary(text): - global summary_text - summary_text = summary_text + " " + text if summary_text else text + if len(packets) < 2: + for test in ntp_tests: + ntp_tests[test].add_summary("Not enough NTP packets received.") + ntp_tests[test].result = test_result.SKIP + ntp_tests[test].write_results(report_filename) + else: + test_dns = ntp_configured_by_dns() + local_ntp_ip = ntp_server_from_ip(device_address) + device_sync_local_server = check_ntp_synchronized(packets, local_ntp_ip) + + if test_dns: + active_test = TEST_DNS + ntp_tests[TEST_DHCP].add_summary("Device not configured for NTP via DHCP") + ntp_tests[TEST_DHCP].result = test_result.SKIP + else: + active_test = TEST_DHCP + ntp_tests[TEST_DNS].add_summary("Device not configured for NTP via DNS") + ntp_tests[TEST_DNS].result = test_result.SKIP -""" -write_report("{b}{t}\n{b}".format(b=dash_break_line, t=test_request)) + if device_sync_local_server: + ntp_tests[active_test].add_summary("Device clock synchronized.") + ntp_tests[active_test].result = test_result.PASS + else: + ntp_tests[active_test].add_summary("Device clock not synchronized with local NTP server.") + ntp_tests[active_test].result = test_result.FAIL + ntp_tests[TEST_DHCP].write_results(report_filename) + ntp_tests[TEST_DNS].write_results(report_filename) if test_request == 'ntp.network.ntp_support': - write_report("{d}\n{b}".format(b=dash_break_line, d=description_ntp_support)) - result = test_ntp_support() + test_ntp_support() elif test_request == 'ntp.network.ntp_update': - write_report("{d}\n{b}".format(b=dash_break_line, d=description_ntp_update)) - result = test_ntp_update() - -write_report("RESULT {r} {t} {s}\n".format(r=result, t=test_request, s=summary_text.strip())) -""" - -print(dns_requests_for_hostname('ntp.ubuntu.com','monitor.pcap')) -print(ntp_server_from_ip('123.123.123.123')) -print(ntp_configured_by_dns()) \ No newline at end of file + test_ntp_update() diff --git a/subset/network/test_network b/subset/network/test_network index ee75e471f5..b51f5f00b9 100755 --- a/subset/network/test_network +++ b/subset/network/test_network @@ -1,18 +1,18 @@ -#!/bin/bash -e +#!/bin/bash REPORT=/tmp/report.txt MONITOR=/scans/monitor.pcap # General Network Tests -python network_tests.py communication.network.min_send $MONITOR $TARGET_IP -python network_tests.py communication.network.type $MONITOR $TARGET_IP +python3.8 network_tests.py communication.network.min_send $MONITOR $TARGET_IP +python3.8 network_tests.py communication.network.type $MONITOR $TARGET_IP cat network_tests.txt >> $REPORT # NTP Tests -python ntp_tests.py ntp.network.ntp_support $MONITOR $TARGET_IP -python ntp_tests.py ntp.network.ntp_update $MONITOR $TARGET_IP +python3.8 ntp_tests.py ntp.network.ntp_support $MONITOR $TARGET_IP +python3.8 ntp_tests.py ntp.network.ntp_update $MONITOR $TARGET_IP cat ntp_tests.txt >> $REPORT @@ -20,6 +20,6 @@ cat ntp_tests.txt >> $REPORT ./run_macoui_test $TARGET_MAC $REPORT # DNS Tests -python dns_tests.py dns.network.hostname_resolution $MONITOR $TARGET_IP +python3.8 dns_tests.py dns.network.hostname_resolution $MONITOR $TARGET_IP cat dns_tests.txt >> $REPORT diff --git a/subset/network/test_result.py b/subset/network/test_result.py new file mode 100644 index 0000000000..3f8995240d --- /dev/null +++ b/subset/network/test_result.py @@ -0,0 +1,31 @@ +""" Wrapper to build test result output +""" +from dataclasses import dataclass + +PASS = 'pass' +SKIP = 'skip' +FAIL = 'fail' + +@dataclass +class test_result: + _dash_break_line = '--------------------\n' + _file = '' + file_name = '' + _file_handler = None + + name: str + description: str + summary: str = '' + result: str = 'fail' + + def write_results(self, report_filename): + """Writes result to file""" + with open(report_filename, 'a+') as file_open: + file_open.write("{b}{t}\n{b}".format(b=self._dash_break_line, t=self.name)) + file_open.write("{d}\n{b}".format(b=self._dash_break_line, d=self.description)) + file_open.write("RESULT {r} {t} {s}\n".format(r=self.result, t=self.name, s=self.summary.strip())) + + + def add_summary(self, text): + """Adds text to result summary""" + self.summary = self.summary + " " + text if self.summary else text diff --git a/testing/test_aux.out b/testing/test_aux.out index de4b085911..72c2840a86 100644 --- a/testing/test_aux.out +++ b/testing/test_aux.out @@ -38,21 +38,24 @@ RESULT pass security.discover.firmware version found: ?\xFF\xFF\x19,>u\x08\x00no RESULT pass communication.network.min_send ARP packets received. Data packets were sent at a frequency of less than 5 minutes RESULT pass communication.network.type Broadcast packets received. Unicast packets received. RESULT pass ntp.network.ntp_support Using NTPv4. -RESULT pass ntp.network.ntp_update Device clock synchronized. +RESULT pass ntp.network.ntp_update_dhcp Device clock synchronized. +RESULT skip ntp.network.ntp_update_dns Device not configured for NTP via DNS RESULT fail connection.network.mac_oui Manufacturer prefix not found! RESULT pass connection.network.mac_address Device MAC address is 9a:02:57:1e:8f:01 RESULT skip dns.network.hostname_resolution Device did not send any DNS requests RESULT pass communication.network.min_send ARP packets received. Data packets were sent at a frequency of less than 5 minutes RESULT pass communication.network.type Broadcast packets received. Unicast packets received. RESULT fail ntp.network.ntp_support Not using NTPv4. -RESULT fail ntp.network.ntp_update Device clock not synchronized with local NTP server. +RESULT fail ntp.network.ntp_update_dhcp Device clock not synchronized with local NTP server. +RESULT skip ntp.network.ntp_update_dns Device not configured for NTP via DNS RESULT pass connection.network.mac_oui Manufacturer: Google found for address 3c:5a:b4:1e:8f:0b RESULT pass connection.network.mac_address Device MAC address is 3c:5a:b4:1e:8f:0b RESULT fail dns.network.hostname_resolution Device sent DNS requests to servers other than the DHCP provided server RESULT pass communication.network.min_send ARP packets received. Data packets were sent at a frequency of less than 5 minutes RESULT pass communication.network.type Broadcast packets received. Unicast packets received. RESULT skip ntp.network.ntp_support No NTP packets received. -RESULT skip ntp.network.ntp_update Not enough NTP packets received. +RESULT skip ntp.network.ntp_update_dhcp Not enough NTP packets received. +RESULT skip ntp.network.ntp_update_dns Not enough NTP packets received. RESULT pass connection.network.mac_oui Manufacturer: Google found for address 3c:5a:b4:1e:8f:0a RESULT pass connection.network.mac_address Device MAC address is 3c:5a:b4:1e:8f:0a RESULT pass dns.network.hostname_resolution Device sends DNS requests and resolves host names From 74c9818ba0a42ea385ab1ede30486c907875e571 Mon Sep 17 00:00:00 2001 From: noursaidi Date: Wed, 19 May 2021 19:14:44 +0100 Subject: [PATCH 3/8] stickler fixes --- subset/network/ntp_tests.py | 22 +++++++++++----------- subset/network/test_result.py | 28 +++++++++++++++++++--------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/subset/network/ntp_tests.py b/subset/network/ntp_tests.py index 0c7505e7ed..4ab57ee751 100644 --- a/subset/network/ntp_tests.py +++ b/subset/network/ntp_tests.py @@ -66,7 +66,7 @@ def ntp_configured_by_dns(): Parameter must be (bool) True, else will be considered false """ - module_config = open(MODULE_CONFIG_PATH) + module_config = open(MODULE_CONFIG_PATH) module_config = json.load(module_config) try: ntp_by_dns = (module_config['modules']['network']['ntp_dns']) @@ -191,8 +191,8 @@ def test_ntp_support(): """ Tests support for NTPv4 """ capture = rdpcap(pcap_file) packets = ntp_packets(capture) - test_ntp = test_result.test_result( name='ntp.network.ntp_support', - description=description_ntp_support) + test_ntp = test_result.test_result(name='ntp.network.ntp_support', + description=description_ntp_support) if len(packets) > 0: version = ntp_client_version(packets) if version is None: @@ -213,7 +213,7 @@ def test_ntp_support(): def test_ntp_update(): """Runs NTP Update Test for both DHCP and DNS""" - # Used to always print test output in the same order + # Used to always print test output in the same order ntp_tests = {} ntp_tests[TEST_DHCP] = test_result.test_result( name='ntp.network.ntp_update_dhcp', @@ -221,7 +221,7 @@ def test_ntp_update(): ntp_tests[TEST_DNS] = test_result.test_result( name='ntp.network.ntp_update_dns', description=description_ntp_update_dns) - + capture = rdpcap(pcap_file) packets = ntp_packets(capture) @@ -236,20 +236,20 @@ def test_ntp_update(): device_sync_local_server = check_ntp_synchronized(packets, local_ntp_ip) if test_dns: - active_test = TEST_DNS + active = TEST_DNS ntp_tests[TEST_DHCP].add_summary("Device not configured for NTP via DHCP") ntp_tests[TEST_DHCP].result = test_result.SKIP else: - active_test = TEST_DHCP + active = TEST_DHCP ntp_tests[TEST_DNS].add_summary("Device not configured for NTP via DNS") ntp_tests[TEST_DNS].result = test_result.SKIP if device_sync_local_server: - ntp_tests[active_test].add_summary("Device clock synchronized.") - ntp_tests[active_test].result = test_result.PASS + ntp_tests[active].add_summary("Device clock synchronized.") + ntp_tests[active].result = test_result.PASS else: - ntp_tests[active_test].add_summary("Device clock not synchronized with local NTP server.") - ntp_tests[active_test].result = test_result.FAIL + ntp_tests[active].add_summary("Device clock not synchronized with local NTP server.") + ntp_tests[active].result = test_result.FAIL ntp_tests[TEST_DHCP].write_results(report_filename) ntp_tests[TEST_DNS].write_results(report_filename) diff --git a/subset/network/test_result.py b/subset/network/test_result.py index 3f8995240d..c0e36433ff 100644 --- a/subset/network/test_result.py +++ b/subset/network/test_result.py @@ -6,26 +6,36 @@ SKIP = 'skip' FAIL = 'fail' + @dataclass class test_result: _dash_break_line = '--------------------\n' - _file = '' - file_name = '' - _file_handler = None name: str description: str summary: str = '' result: str = 'fail' - + def write_results(self, report_filename): - """Writes result to file""" + """Writes result to file + + Args: + report_filename: path to file to write results to + """ with open(report_filename, 'a+') as file_open: file_open.write("{b}{t}\n{b}".format(b=self._dash_break_line, t=self.name)) - file_open.write("{d}\n{b}".format(b=self._dash_break_line, d=self.description)) - file_open.write("RESULT {r} {t} {s}\n".format(r=self.result, t=self.name, s=self.summary.strip())) - + file_open.write("{d}\n{b}".format( + b=self._dash_break_line, d=self.description)) + file_open.write("RESULT {r} {t} {s}\n".format( + r=self.result, t=self.name, s=self.summary.strip())) def add_summary(self, text): - """Adds text to result summary""" + """Adds summary text to result. + + e.g. RESULT pass test.name + Appends to existing text with a space if any. + + Args: + Text to add. + """ self.summary = self.summary + " " + text if self.summary else text From ea1559c45b100b937fd2f5c33bf2562e2c4739fe Mon Sep 17 00:00:00 2001 From: noursaidi Date: Wed, 19 May 2021 19:38:57 +0100 Subject: [PATCH 4/8] add notfail example --- daq/report.py | 8 ++++++-- test_config.json | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 test_config.json diff --git a/daq/report.py b/daq/report.py index 40fde8aa3d..d09a074985 100644 --- a/daq/report.py +++ b/daq/report.py @@ -270,7 +270,8 @@ def _analyse_and_write_results(self): # The device overall fails if any result is unexpected if result_dict["result"] != required_result: - passes = False + if required_result == 'notfail' and result_dict["result"] == 'fail': + passes = False if result_dict["result"] == 'gone': gone = True @@ -308,7 +309,7 @@ def _write_category_table(self): total = 0 results = [[0, 0, 0] for _ in range(len(self._expected_headers))] - result = self._NO_REQUIRED # Overall category result + result = self._NO_REQUIRED # Overall category result is n/a if no tests for test_name, result_dict in self._results.items(): test_info = self._get_test_info(test_name) @@ -335,6 +336,9 @@ def _write_category_table(self): # TODO remove when info tests are removed if result_dict["result"] == 'info': result_dict["result"] = 'pass' + elif (result_dict["result"] == 'skip' + and test_info['required'] == 'notfail'): + result = 'pass' else: result = "fail" else: diff --git a/test_config.json b/test_config.json new file mode 100644 index 0000000000..49af910fb2 --- /dev/null +++ b/test_config.json @@ -0,0 +1,4 @@ +{ + "include": "include.json", + "over": "no" +} \ No newline at end of file From 6ec3d7a96b76b3df6d09b97e3253f4dec520af57 Mon Sep 17 00:00:00 2001 From: noursaidi Date: Wed, 19 May 2021 20:33:32 +0100 Subject: [PATCH 5/8] add result "notfail" option --- daq/report.py | 8 +++++--- resources/setups/common/tests_config.json | 4 ++-- subset/network/ntp_tests.py | 1 - 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/daq/report.py b/daq/report.py index d09a074985..a6dd881fe6 100644 --- a/daq/report.py +++ b/daq/report.py @@ -270,7 +270,9 @@ def _analyse_and_write_results(self): # The device overall fails if any result is unexpected if result_dict["result"] != required_result: - if required_result == 'notfail' and result_dict["result"] == 'fail': + if required_result == 'notfail' and result_dict["result"] != 'fail': + pass + else: passes = False if result_dict["result"] == 'gone': @@ -336,8 +338,8 @@ def _write_category_table(self): # TODO remove when info tests are removed if result_dict["result"] == 'info': result_dict["result"] = 'pass' - elif (result_dict["result"] == 'skip' - and test_info['required'] == 'notfail'): + elif (result_dict["result"] == 'skip' + and test_info['required'] == 'notfail'): result = 'pass' else: result = "fail" diff --git a/resources/setups/common/tests_config.json b/resources/setups/common/tests_config.json index 24a5ce5ce6..a32b6f586e 100644 --- a/resources/setups/common/tests_config.json +++ b/resources/setups/common/tests_config.json @@ -91,12 +91,12 @@ }, "ntp.network.ntp_update_dhcp": { "category": "NTP", - "required": "pass", + "required": "notfail", "expected": "Required Pass" }, "ntp.network.ntp_update_dns": { "category": "NTP", - "required": "pass", + "required": "notfail", "expected": "Required Pass" }, "communication.network.min_send": { diff --git a/subset/network/ntp_tests.py b/subset/network/ntp_tests.py index 4ab57ee751..6e899cc042 100644 --- a/subset/network/ntp_tests.py +++ b/subset/network/ntp_tests.py @@ -229,7 +229,6 @@ def test_ntp_update(): for test in ntp_tests: ntp_tests[test].add_summary("Not enough NTP packets received.") ntp_tests[test].result = test_result.SKIP - ntp_tests[test].write_results(report_filename) else: test_dns = ntp_configured_by_dns() local_ntp_ip = ntp_server_from_ip(device_address) From 3c3e1588db6bb1d94144349e1162e2c9a73fa517 Mon Sep 17 00:00:00 2001 From: Noureddine Date: Wed, 19 May 2021 23:57:32 +0100 Subject: [PATCH 6/8] Delete test_config.json --- test_config.json | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 test_config.json diff --git a/test_config.json b/test_config.json deleted file mode 100644 index 49af910fb2..0000000000 --- a/test_config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "include": "include.json", - "over": "no" -} \ No newline at end of file From 4b19b29856706b1f9bb41b14bdee524e3679290c Mon Sep 17 00:00:00 2001 From: Noureddine Date: Thu, 20 May 2021 00:00:35 +0100 Subject: [PATCH 7/8] Update report.py --- daq/report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daq/report.py b/daq/report.py index a6dd881fe6..bd29844eec 100644 --- a/daq/report.py +++ b/daq/report.py @@ -338,8 +338,8 @@ def _write_category_table(self): # TODO remove when info tests are removed if result_dict["result"] == 'info': result_dict["result"] = 'pass' - elif (result_dict["result"] == 'skip' - and test_info['required'] == 'notfail'): + elif (result_dict["result"] == 'skip' and + test_info['required'] == 'notfail'): result = 'pass' else: result = "fail" From 609496c317f958ffdacf318b340f53206b587478 Mon Sep 17 00:00:00 2001 From: noursaidi Date: Mon, 7 Jun 2021 22:59:17 +0100 Subject: [PATCH 8/8] add dns check, add ci --- docker/include/bin/start_faux | 7 +++++++ subset/network/ntp_tests.py | 21 ++++++++++++++++----- testing/test_modules.sh | 1 + 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/docker/include/bin/start_faux b/docker/include/bin/start_faux index 87779b809d..6a558df1c9 100755 --- a/docker/include/bin/start_faux +++ b/docker/include/bin/start_faux @@ -166,6 +166,13 @@ if [ -n "${options[ntpv4]}" ]; then java -jar NTPClient/build/libs/NTPClient-1.0-SNAPSHOT.jar $ntp_server 123 4 > ntp.log sleep 8 done) & +elif [ -n "${options[ntpv4dns]}" ]; then + ntp_server=ntp.daqlocal + echo Transmitting NTP query to $ntp_server using NTPv4 + (while true; do + java -jar NTPClient/build/libs/NTPClient-1.0-SNAPSHOT.jar $ntp_server 123 4 > ntp.log + sleep 8 + done) & elif [ -n "${options[ntpv3]}" ]; then STATIC_NTP_SERVER=216.239.35.8 echo Transmitting NTP query to $STATIC_NTP_SERVER using NTPv3 diff --git a/subset/network/ntp_tests.py b/subset/network/ntp_tests.py index 6e899cc042..010049ab37 100644 --- a/subset/network/ntp_tests.py +++ b/subset/network/ntp_tests.py @@ -2,6 +2,8 @@ import sys import re import json + +from scapy.config import scapy_delete_temp_files import test_result from scapy.all import NTP, rdpcap, DNS @@ -242,14 +244,23 @@ def test_ntp_update(): active = TEST_DHCP ntp_tests[TEST_DNS].add_summary("Device not configured for NTP via DNS") ntp_tests[TEST_DNS].result = test_result.SKIP - - if device_sync_local_server: - ntp_tests[active].add_summary("Device clock synchronized.") - ntp_tests[active].result = test_result.PASS - else: + + if not device_sync_local_server: ntp_tests[active].add_summary("Device clock not synchronized with local NTP server.") ntp_tests[active].result = test_result.FAIL + if device_sync_local_server: + # DNS and DHCP NTP currently resolve to the same IP address so + # ensure a device which says it's configured using DHCP does not + # perform DNS lookups for the NTP server + if (active == TEST_DHCP and dns_requests_for_hostname(NTP_SERVER_HOSTNAME, + capture)): + ntp_tests[active].add_summary("Device used DNS for NTP") + ntp_tests[active].result = test_result.FAiL + else: + ntp_tests[active].add_summary("Device clock synchronized.") + ntp_tests[active].result = test_result.PASS + ntp_tests[TEST_DHCP].write_results(report_filename) ntp_tests[TEST_DNS].write_results(report_filename) diff --git a/testing/test_modules.sh b/testing/test_modules.sh index 279db6d058..475ce51f26 100755 --- a/testing/test_modules.sh +++ b/testing/test_modules.sh @@ -27,6 +27,7 @@ cat > $TEST_LIST <