Browse Source

Merge branch 'botnet_cleanup' of stefan.schmidt/ID2T-toolkit into master

Jens Keim 5 years ago
parent
commit
c4d6e022c4
2 changed files with 157 additions and 130 deletions
  1. 122 96
      code/Attack/MembersMgmtCommAttack.py
  2. 35 34
      code/Core/Statistics.py

+ 122 - 96
code/Attack/MembersMgmtCommAttack.py

@@ -1,28 +1,23 @@
-from enum import Enum
-from random import randint, randrange, choice, uniform
-from collections import deque
-from scipy.stats import gamma
-from lea import Lea
-from datetime import datetime
 import os
 import sys
+from collections import deque
+from datetime import datetime
+from random import randint, randrange, choice, uniform
 
 import ID2TLib.Botnet.libbotnetcomm as lb
+from lea import Lea
+from scapy.layers.inet import IP, IPOption_Security
+
 import ID2TLib.Botnet.Message as Bmsg
+import ID2TLib.Utility as Util
 from Attack import BaseAttack
 from Attack.AttackParameters import Parameter as Param
 from Attack.AttackParameters import ParameterTypes
-from ID2TLib.Ports import PortSelectors
-import ID2TLib.Utility as Util
-
-from ID2TLib import FileUtils, Generator
-from ID2TLib.IPv4 import IPAddress
-from ID2TLib.PcapAddressOperations import PcapAddressOperations
+from ID2TLib import Generator
 from ID2TLib.Botnet.CommunicationProcessor import CommunicationProcessor
 from ID2TLib.Botnet.MessageMapping import MessageMapping
-from ID2TLib.PcapFile import PcapFile
-from Core.Statistics import Statistics
-from scapy.layers.inet import IP, IPOption_Security
+from ID2TLib.PcapAddressOperations import PcapAddressOperations
+from ID2TLib.Ports import PortSelectors
 
 
 class MembersMgmtCommAttack(BaseAttack.BaseAttack):
@@ -32,8 +27,9 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
 
         """
         # Initialize communication
-        super(MembersMgmtCommAttack, self).__init__("Membership Management Communication Attack (MembersMgmtCommAttack)",
-                                        "Injects Membership Management Communication", "Botnet communication")
+        super(MembersMgmtCommAttack, self).__init__(
+            "Membership Management Communication Attack (MembersMgmtCommAttack)",
+            "Injects Membership Management Communication", "Botnet communication")
 
         # Define allowed parameters and their type
         self.supported_params = {
@@ -84,13 +80,13 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         for msg_type in Bmsg.MessageType:
             self.msg_types[msg_type.value] = msg_type
 
+        self.DEFAULT_XML_PATH = None
+
     def init_params(self):
         """
         Initialize some parameters of this communication-attack using the user supplied command line parameters.
         The remaining parameters are implicitly set in the provided data file. Note: the timestamps in the file
         have to be sorted in ascending order
-
-        :param statistics: Reference to a statistics object.
         """
         # set class constants
         self.DEFAULT_XML_PATH = Util.RESOURCE_DIR + "Botnet/MembersMgmtComm_example.xml"
@@ -137,19 +133,19 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         # create the final messages that have to be sent, including all bot configurations
         messages = self._create_messages()
 
-        if messages == []:
+        if not messages:
             return 0, None
 
         # Setup (initial) parameters for packet creation loop
-        BUFFER_SIZE = 1000
+        buffer_size = 1000
         pkt_gen = Generator.PacketGenerator()
         padding = self.get_param_value(Param.PACKET_PADDING)
-        packets = deque(maxlen=BUFFER_SIZE)
+        packets = deque(maxlen=buffer_size)
         total_pkts = 0
         limit_packetcount = self.get_param_value(Param.PACKETS_LIMIT)
         limit_duration = self.get_param_value(Param.ATTACK_DURATION)
         path_attack_pcap = None
-        overThousand = False
+        over_thousand = False
 
         msg_packet_mapping = MessageMapping(messages, self.statistics.get_pcap_timestamp_start())
         mark_packets = self.get_param_value(Param.HIDDEN_MARK)
@@ -157,7 +153,6 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         # create packets to write to PCAP file
         for msg in messages:
             # retrieve the source and destination configurations
-            id_src, id_dst = msg.src["ID"], msg.dst["ID"]
             ip_src, ip_dst = msg.src["IP"], msg.dst["IP"]
             mac_src, mac_dst = msg.src["MAC"], msg.dst["MAC"]
             if msg.type.is_request():
@@ -180,9 +175,11 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 nl_size = randint(1, 25)    # what is max NL entries?
 
             # create suitable IP/UDP packet and add to packets list
-            packet = pkt_gen.generate_mmcom_packet(ip_src=ip_src, ip_dst=ip_dst, ttl=ttl, mac_src=mac_src, mac_dst=mac_dst,
-                port_src=port_src, port_dst=port_dst, message_type=msg.type, neighborlist_entries=nl_size)
-            Generator.add_padding(packet, padding,True, True)
+            packet = pkt_gen.generate_mmcom_packet(ip_src=ip_src, ip_dst=ip_dst, ttl=ttl, mac_src=mac_src,
+                                                   mac_dst=mac_dst,
+                                                   port_src=port_src, port_dst=port_dst, message_type=msg.type,
+                                                   neighborlist_entries=nl_size)
+            Generator.add_padding(packet, padding, True, True)
 
             packet.time = msg.time
 
@@ -201,32 +198,32 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             # Store timestamp of first packet (for attack label)
             if total_pkts <= 1:
                 self.attack_start_utime = packets[0].time
-            elif total_pkts % BUFFER_SIZE == 0: # every 1000 packets write them to the PCAP file (append)
-                if overThousand: # if over 1000 packets written, there may be a different packet-length for the last few packets 
+            elif total_pkts % buffer_size == 0:  # every 1000 packets write them to the PCAP file (append)
+                if over_thousand:  # if over 1000 packets written, packet-length for the last few packets may differ
                     packets = list(packets)
-                    Generator.equal_length(packets, length = max_len, padding = padding, force_len = True)
+                    Generator.equal_length(packets, length=max_len, padding=padding, force_len=True)
                     last_packet = packets[-1]
                     path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
-                    packets = deque(maxlen=BUFFER_SIZE)
+                    packets = deque(maxlen=buffer_size)
                 else:
                     packets = list(packets)
-                    Generator.equal_length(packets, padding = padding)
+                    Generator.equal_length(packets, padding=padding)
                     last_packet = packets[-1]
                     max_len = len(last_packet)
-                    overThousand = True
+                    over_thousand = True
                     path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
-                    packets = deque(maxlen=BUFFER_SIZE)
+                    packets = deque(maxlen=buffer_size)
 
         # if there are unwritten packets remaining, write them to the PCAP file
         if len(packets) > 0:
-            if overThousand:
+            if over_thousand:
                 packets = list(packets)
-                Generator.equal_length(packets, length = max_len, padding = padding, force_len = True)
+                Generator.equal_length(packets, length=max_len, padding=padding, force_len=True)
                 path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
                 last_packet = packets[-1]
             else:
                 packets = list(packets)
-                Generator.equal_length(packets, padding = padding)
+                Generator.equal_length(packets, padding=padding)
                 path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
                 last_packet = packets[-1]
 
@@ -239,20 +236,19 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         self.attack_end_utime = last_packet.time
 
         # Return packets sorted by packet by timestamp and total number of packets (sent)
-        return total_pkts , path_attack_pcap, [mapping_filename]
-
+        return total_pkts, path_attack_pcap, [mapping_filename]
 
     def generate_attack_packets(self):
         pass
 
-
     def _create_messages(self):
         """
         Creates the messages that are to be injected into the PCAP.
         :return: the final messages as a list
         """
 
-        def add_ids_to_config(ids_to_add: list, existing_ips: list, new_ips: list, bot_configs: dict, idtype:str="local", router_mac:str=""):
+        def add_ids_to_config(ids_to_add: list, existing_ips: list, new_ips: list, bot_configs: dict,
+                              idtype: str = "local", router_mac: str = ""):
             """
             Creates IP and MAC configurations for the given IDs and adds them to the existing configurations object.
 
@@ -265,8 +261,8 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             """
 
             ids = ids_to_add.copy()
-            # macgen only needed, when IPs are new local IPs (therefore creating the object here suffices for the current callers
-            # to not end up with the same MAC paired with different IPs)
+            # macgen only needed, when IPs are new local IPs (therefore creating the object here suffices for the
+            # current callers to not end up with the same MAC paired with different IPs)
             macgen = Generator.MacAddressGenerator()
 
             # assign existing IPs and the corresponding MAC addresses in the PCAP to the IDs
@@ -286,19 +282,19 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 bot_configs[random_id] = {"Type": idtype, "IP": ip, "MAC": mac}
                 ids.remove(random_id)
 
-        def assign_realistic_ttls(bot_configs:list):
-            '''
+        def assign_realistic_ttls(bot_configs: dict):
+            """
             Assigns a realisitic ttl to each bot from @param: bot_configs. Uses statistics and distribution to be able
             to calculate a realisitc ttl.
             :param bot_configs: List that contains all bots that should be assigned with realistic ttls.
-            '''
+            """
             ids = sorted(bot_configs.keys())
-            for pos,bot in enumerate(ids):
+            for pos, bot in enumerate(ids):
                 bot_type = bot_configs[bot]["Type"]
-                if(bot_type == "local"): # Set fix TTL for local Bots
+                if bot_type == "local":  # Set fix TTL for local Bots
                     bot_configs[bot]["TTL"] = 128
                     # Set TTL based on TTL distribution of IP address
-                else: # Set varying TTl for external Bots
+                else:  # Set varying TTl for external Bots
                     bot_ttl_dist = self.statistics.get_ttl_distribution(bot_configs[bot]["IP"])
                     if len(bot_ttl_dist) > 0:
                         source_ttl_prob_dict = Lea.fromValFreqsDict(bot_ttl_dist)
@@ -310,33 +306,43 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                         else:
                             bot_configs[bot]["TTL"] = self.statistics.process_db_query("most_used(ttlValue)")
 
-        def assign_realistic_timestamps(messages: list, external_ids: set, local_ids: set, avg_delay_local:float, avg_delay_external: float, zero_reference:float):
+        def assign_realistic_timestamps(messages: list, external_ids: set, local_ids: set, avg_delay_local: float,
+                                        avg_delay_external: float, zero_reference: float):
             """
             Assigns realistic timestamps to a set of messages
 
             :param messages: the set of messages to be updated
             :param external_ids: the set of bot ids, that are outside the network, i.e. external
             :param local_ids: the set of bot ids, that are inside the network, i.e. local
-            :avg_delay_local: the avg_delay between the dispatch and the reception of a packet between local computers
-            :avg_delay_external: the avg_delay between the dispatch and the reception of a packet between a local and an external computer
-            :zero_reference: the timestamp which is regarded as the beginning of the pcap_file and therefore handled like a timestamp that resembles 0
+            :param avg_delay_local: the avg_delay between the dispatch and the reception of a packet between local
+                                    computers
+            :param avg_delay_external: the avg_delay between the dispatch and the reception of a packet between a local
+                                       and an external computer
+            :param zero_reference: the timestamp which is regarded as the beginning of the pcap_file and therefore
+                                   handled like a timestamp that resembles 0
             """
             updated_msgs = []
-            last_response = {}      # Dict, takes a tuple of 2 Bot_IDs as a key (requester, responder), returns the time of the last response, the requester received
-                                    # necessary in order to make sure, that additional requests are sent only after the response to the last one was received
-            for msg in messages:    # init
-                last_response[(msg.src, msg.dst)] = -1
+
+            # Dict, takes a tuple of 2 Bot_IDs as a key (requester, responder), returns the time of the last response,
+            # the requester received necessary in order to make sure, that additional requests are sent only after the
+            # response to the last one was received
+            last_response = {}
+
+            for m in messages:    # init
+                last_response[(m.src, m.dst)] = -1
 
             # update all timestamps
             for req_msg in messages:
 
-                if(req_msg in updated_msgs):
+                if req_msg in updated_msgs:
                     # message already updated
                     continue
 
-                # if req_msg.timestamp would be before the timestamp of the response to the last request, req_msg needs to be sent later (else branch)
-                if last_response[(req_msg.src, req_msg.dst)] == -1 or last_response[(req_msg.src, req_msg.dst)] < (zero_reference + req_msg.time - 0.05):
-                    ## update req_msg timestamp with a variation of up to 50ms
+                # if req_msg.timestamp would be before the timestamp of the response to the last request, req_msg needs
+                # to be sent later (else branch)
+                if last_response[(req_msg.src, req_msg.dst)] == -1 or last_response[(req_msg.src, req_msg.dst)] < (
+                        zero_reference + req_msg.time - 0.05):
+                    # update req_msg timestamp with a variation of up to 50ms
                     req_msg.time = zero_reference + req_msg.time + uniform(-0.05, 0.05)
                     updated_msgs.append(req_msg)
 
@@ -347,14 +353,17 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 if req_msg.refer_msg_id != -1:
                     respns_msg = messages[req_msg.refer_msg_id]
 
-                    # check for local or external communication and update response timestamp with the respective avg delay
+                    # check for local or external communication and update response timestamp with the respective
+                    # avg delay
                     if req_msg.src in external_ids or req_msg.dst in external_ids:
-                        #external communication
-                        respns_msg.time = req_msg.time + avg_delay_external + uniform(-0.1*avg_delay_external, 0.1*avg_delay_external)
+                        # external communication
+                        respns_msg.time = req_msg.time + avg_delay_external + uniform(-0.1 * avg_delay_external,
+                                                                                      0.1 * avg_delay_external)
 
                     else:
-                        #local communication
-                        respns_msg.time = req_msg.time + avg_delay_local + uniform(-0.1*avg_delay_local, 0.1*avg_delay_local)
+                        # local communication
+                        respns_msg.time = req_msg.time + avg_delay_local + uniform(-0.1 * avg_delay_local,
+                                                                                   0.1 * avg_delay_local)
 
                     updated_msgs.append(respns_msg)
                     last_response[(req_msg.src, req_msg.dst)] = respns_msg.time
@@ -362,8 +371,10 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         def assign_ttls_from_caida(bot_configs):
             """
             Assign realistic TTL values to bots with respect to their IP, based on the CAIDA dataset.
-            If there exists an entry for a bot's IP, the TTL is chosen based on a distribution over all used TTLs by this IP.
-            If there is no such entry, the TTL is chosen based on a distribution over all used TTLs and their respective frequency.
+            If there exists an entry for a bot's IP, the TTL is chosen based on a distribution over all used TTLs by
+            this IP.
+            If there is no such entry, the TTL is chosen based on a distribution over all used TTLs and their
+            respective frequency.
 
             :param bot_configs: the existing bot configurations
             """
@@ -371,7 +382,7 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             def get_ip_ttl_distrib():
                 """
                 Parses the CSV file containing a mapping between IP and their used TTLs.
-                :return: returns a dict with the IPs as keys and dicts for their TTL disribution as values
+                :return: returns a dict with the IPs as keys and dicts for their TTL distribution as values
                 """
                 ip_based_distrib = {}
                 with open("resources/CaidaTTL_perIP.csv", "r") as file:
@@ -380,7 +391,8 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                     for line in file:
                         ip_addr, ttl, freq = line.split(",")
                         if ip_addr not in ip_based_distrib:
-                            ip_based_distrib[ip_addr] = {}  # the values for ip_based_distrib are dicts with key=TTL, value=Frequency
+                            # the values for ip_based_distrib are dicts with key=TTL, value=Frequency
+                            ip_based_distrib[ip_addr] = {}
                         ip_based_distrib[ip_addr][ttl] = int(freq)
 
                 return ip_based_distrib
@@ -417,7 +429,8 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 # if there exists detailed information about the TTL distribution of this IP
                 elif bot_ip in ip_ttl_distrib:
                     ip_ttl_freqs = ip_ttl_distrib[bot_ip]
-                    source_ttl_prob_dict = Lea.fromValFreqsDict(ip_ttl_freqs)  # build a probability dict from this IP's TTL distribution
+                    # build a probability dict from this IP's TTL distribution
+                    source_ttl_prob_dict = Lea.fromValFreqsDict(ip_ttl_freqs)
                     bot_configs[bot_id]["TTL"] = source_ttl_prob_dict.random()
 
                 # otherwise assign a random TTL based on the total TTL distribution
@@ -429,7 +442,7 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         filepath_csv = self.get_param_value(Param.FILE_CSV)
 
         # use C++ communication processor for faster interval finding
-        cpp_comm_proc = lb.botnet_comm_processor();
+        cpp_comm_proc = lb.botnet_comm_processor()
 
         # only use CSV input if the XML path is the default one
         # --> prefer XML input over CSV input (in case both are given)
@@ -447,7 +460,8 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 print("Writing corresponding XML file...", end=" ")
                 sys.stdout.flush()
             filepath_xml = cpp_comm_proc.write_xml(Util.OUT_DIR, filename)
-            if print_updates: print("done.")
+            if print_updates:
+                print("done.")
         else:
             filesize = os.path.getsize(filepath_xml) / 2**20  # get filesize in MB
             if filesize > 10:
@@ -455,7 +469,8 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 sys.stdout.flush()
                 print_updates = True
             cpp_comm_proc.parse_xml(filepath_xml)
-            if print_updates: print("done.")
+            if print_updates:
+                print("done.")
 
         # find a good communication mapping in the input file that matches the users parameters
         nat = self.get_param_value(Param.NAT_PRESENT)
@@ -466,34 +481,42 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         start_idx = self.get_param_value(Param.INTERVAL_SELECT_START)
         end_idx = self.get_param_value(Param.INTERVAL_SELECT_END)
 
-        potential_long_find_time = (strategy == "optimal" and (filesize > 4 and self.statistics.get_packet_count() > 1000))
+        potential_long_find_time = (
+                    strategy == "optimal" and (filesize > 4 and self.statistics.get_packet_count() > 1000))
         if print_updates or potential_long_find_time:
-            if not print_updates: print()
+            if not print_updates:
+                print()
             print("Selecting communication interval from input CSV/XML file...", end=" ")
             sys.stdout.flush()
             if potential_long_find_time:
-                print("\nWarning: Because of the large input files and the (chosen) interval selection strategy 'optimal',")
-                print("this may take a while. Consider using selection strategy 'random' or 'custom'...", end=" ")
+                print("\nWarning: Because of the large input files and the (chosen) interval selection strategy")
+                print("'optimal', this may take a while. Consider using selection strategy 'random' or 'custom'...",
+                      end=" ")
                 sys.stdout.flush()
             print_updates = True
 
-        comm_interval = comm_proc.get_comm_interval(cpp_comm_proc, strategy, number_init_bots, duration, start_idx, end_idx)
+        comm_interval = comm_proc.get_comm_interval(cpp_comm_proc, strategy, number_init_bots, duration, start_idx,
+                                                    end_idx)
 
         if not comm_interval:
             print("Error: An interval that satisfies the input cannot be found.")
             return []
-        if print_updates: print("done.")  # print corresponding message to interval finding message
+        if print_updates:
+            print("done.")  # print corresponding message to interval finding message
 
         # retrieve the mapping information
-        mapped_ids, packet_start_idx, packet_end_idx = comm_interval["IDs"], comm_interval["Start"], comm_interval["End"]
+        mapped_ids = comm_interval["IDs"]
+        packet_start_idx = comm_interval["Start"]
+        packet_end_idx = comm_interval["End"]
         while len(mapped_ids) > number_init_bots:
             rm_idx = randrange(0, len(mapped_ids))
             del mapped_ids[rm_idx]
 
-        if print_updates: print("Generating attack packets...", end=" ")
+        if print_updates:
+            print("Generating attack packets...", end=" ")
         sys.stdout.flush()
         # get the messages contained in the chosen interval
-        abstract_packets = cpp_comm_proc.get_messages(packet_start_idx, packet_end_idx);
+        abstract_packets = cpp_comm_proc.get_messages(packet_start_idx, packet_end_idx)
         comm_proc.set_mapping(abstract_packets, mapped_ids)
         # determine ID roles and select the messages that are to be mapped into the PCAP
         messages = comm_proc.det_id_roles_and_msgs()
@@ -504,8 +527,6 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         reuse_percent_total = self.get_param_value(Param.IP_REUSE_TOTAL)
         reuse_percent_external = self.get_param_value(Param.IP_REUSE_EXTERNAL)
         reuse_percent_local = self.get_param_value(Param.IP_REUSE_LOCAL)
-        reuse_count_external = int(reuse_percent_total * reuse_percent_external * len(mapped_ids))
-        reuse_count_local = int(reuse_percent_total * reuse_percent_local * len(mapped_ids))
 
         # create IP and MAC configurations for the IDs/Bots
         ipgen = Generator.IPGenerator()
@@ -529,22 +550,27 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             existing_external_ips = sorted(pcapops.get_existing_external_ips(reuse_count_external))
             remaining = len(external_ids) - len(existing_external_ips)
 
-            for external_ip in existing_external_ips: ipgen.add_to_blacklist(external_ip)
+            for external_ip in existing_external_ips:
+                ipgen.add_to_blacklist(external_ip)
             new_external_ips = sorted([ipgen.random_ip() for _ in range(remaining)])
-            add_ids_to_config(sorted(external_ids), existing_external_ips, new_external_ips, bot_configs, idtype="external", router_mac=router_mac)
+            add_ids_to_config(sorted(external_ids), existing_external_ips, new_external_ips, bot_configs,
+                              idtype="external", router_mac=router_mac)
 
-        # this is the timestamp at which the first packet should be injected, the packets have to be shifted to the beginning of the
-        # pcap file (INJECT_AT_TIMESTAMP) and then the offset of the packets have to be compensated to start at the given point in time
+        # this is the timestamp at which the first packet should be injected, the packets have to be shifted to
+        # the beginning of the pcap file (INJECT_AT_TIMESTAMP) and then the offset of the packets have to be
+        # compensated to start at the given point in time
         zero_reference = self.get_param_value(Param.INJECT_AT_TIMESTAMP) - messages[0].time
 
         # calculate the average delay values for local and external responses
         avg_delay_local, avg_delay_external = self.statistics.get_avg_delay_local_ext()
 
-        #set timestamps
-        assign_realistic_timestamps(messages, external_ids, local_ids, avg_delay_local, avg_delay_external, zero_reference)
+        # set timestamps
+        assign_realistic_timestamps(messages, external_ids, local_ids, avg_delay_local, avg_delay_external,
+                                    zero_reference)
 
-        portSelector = PortSelectors.LINUX
+        port_selector = PortSelectors.LINUX
         reserved_ports = set(int(line.strip()) for line in open(Util.RESOURCE_DIR + "reserved_ports.txt").readlines())
+
         def filter_reserved(get_port):
             port = get_port()
             while port in reserved_ports:
@@ -554,11 +580,11 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         # create port configurations for the bots
         use_multiple_ports = self.get_param_value(Param.MULTIPORT)
         for bot in sorted(bot_configs):
-            bot_configs[bot]["SrcPort"] = filter_reserved(portSelector.select_port_udp)
+            bot_configs[bot]["SrcPort"] = filter_reserved(port_selector.select_port_udp)
             if not use_multiple_ports:
                 bot_configs[bot]["DstPort"] = filter_reserved(Generator.gen_random_server_port)
             else:
-                bot_configs[bot]["DstPort"] = filter_reserved(portSelector.select_port_udp)
+                bot_configs[bot]["DstPort"] = filter_reserved(port_selector.select_port_udp)
 
         # assign realistic TTL for every bot
         if self.get_param_value(Param.TTL_FROM_CAIDA):
@@ -569,7 +595,7 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         # put together the final messages including the full sender and receiver
         # configurations (i.e. IP, MAC, port, ...) for easier later use
         final_messages = []
-        messages = sorted(messages, key=lambda msg: msg.time)
+        messages = sorted(messages, key=lambda m: m.time)
         new_id = 0
 
         for msg in messages:
@@ -584,7 +610,7 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             msg.src["ID"], msg.dst["ID"] = id_src, id_dst
             msg.msg_id = new_id
             new_id += 1
-            ### Important here to update refers, if needed later?
+            # Important here to update refers, if needed later?
             final_messages.append(msg)
 
         return final_messages

+ 35 - 34
code/Core/Statistics.py

@@ -688,46 +688,48 @@ class Statistics:
         :return: tuple consisting of avg delay for local and external communication, (local, external)
         """
 
-        conv_delays = self.stats_db.process_user_defined_query("SELECT ipAddressA, ipAddressB, avgDelay FROM conv_statistics")
-        if(conv_delays):
+        conv_delays = self.stats_db.process_user_defined_query(
+            "SELECT ipAddressA, ipAddressB, avgDelay FROM conv_statistics")
+        if conv_delays:
             external_conv = []
             local_conv = []
 
             for conv in conv_delays:
-                IPA = IPAddress.parse(conv[0])
-                IPB = IPAddress.parse(conv[1])
+                ip_a = IPAddress.parse(conv[0])
+                ip_b = IPAddress.parse(conv[1])
 
-                #split into local and external conversations
-                if(not IPA.is_private() or not IPB.is_private()):
+                # split into local and external conversations
+                if not ip_a.is_private() or not ip_b.is_private():
                     external_conv.append(conv)
                 else:
                     local_conv.append(conv)
    
-            # calculate avg local and external delay by summing up the respective delays and dividing them by the number of conversations
+            # calculate avg local and external delay by summing up the respective delays and dividing them by the
+            # number of conversations
             avg_delay_external = 0.0
             avg_delay_local = 0.0
             default_ext = False
             default_local = False
 
-            if(local_conv):
+            if local_conv:
                 for conv in local_conv:
                     avg_delay_local += conv[2]
-                avg_delay_local = (avg_delay_local/len(local_conv)) * 0.001 #ms
+                avg_delay_local = (avg_delay_local/len(local_conv)) * 0.001  # ms
             else:
                 # no local conversations in statistics found
                 avg_delay_local = 0.055
                 default_local = True
 
-            if(external_conv):
+            if external_conv:
                 for conv in external_conv:
                     avg_delay_external += conv[2]
-                avg_delay_external = (avg_delay_external/len(external_conv)) * 0.001 #ms
+                avg_delay_external = (avg_delay_external/len(external_conv)) * 0.001  # ms
             else:
                 # no external conversations in statistics found
                 avg_delay_external = 0.09
                 default_ext = True
         else:
-            #if no statistics were found, use these numbers
+            # if no statistics were found, use these numbers
             avg_delay_external = 0.09
             avg_delay_local = 0.055
             default_ext = True
@@ -740,13 +742,14 @@ class Statistics:
         # print information, that (default) values are used, that are not collected from the Input PCAP
         if default_ext or default_local:
             if default_ext and default_local:
-                print("Warning: Could not collect average delays for local or external communication, using following values:")
+                print("Warning: Could not collect average delays for local or external communication, using "
+                      "following values:")
             elif default_ext:
                 print("Warning: Could not collect average delays for external communication, using following values:")
             elif default_local:
                 print("Warning: Could not collect average delays for local communication, using following values:")
-            print("Avg delay of external communication: {0}s,  Avg delay of local communication: {1}s".format(avg_delay_external, avg_delay_local))
-            
+            print("Avg delay of external communication: {0}s,  Avg delay of local communication: {1}s".format(
+                avg_delay_external, avg_delay_local))
 
         return avg_delay_local, avg_delay_external
 
@@ -762,7 +765,7 @@ class Statistics:
                 "SELECT ipAddress, %s FROM ip_degrees" % degree_type)
 
         degrees = []
-        if(degrees_raw):
+        if degrees_raw:
             for deg in degrees_raw:
                 if int(deg[1]) > 0:
                     degrees.append(deg)
@@ -1298,13 +1301,13 @@ class Statistics:
             plt.ylabel('IpAddress')
             plt.xlabel('Indegree')
 
-            #set width of the bars
+            # set width of the bars
             width = 0.3
 
             # set scalings
             plt.figure(figsize=(int(len(graphx))/20 + 5, int(len(graphy)/5) + 5))  # these proportions just worked well
 
-            #set limits of the axis
+            # set limits of the axis
             plt.ylim([0, len(graphy)])
             plt.xlim([0, max(graphx) + 10])
 
@@ -1322,7 +1325,7 @@ class Statistics:
             plt.yticks(graphy, labels)
             out = self.pcap_filepath.replace('.pcap', '_plot-In Degree of an IP' + file_ending)
             plt.tight_layout()
-            plt.savefig(out,dpi=500)
+            plt.savefig(out, dpi=500)
 
             return out
 
@@ -1351,13 +1354,13 @@ class Statistics:
             plt.ylabel('IpAddress')
             plt.xlabel('Outdegree')
 
-            #set width of the bars
+            # set width of the bars
             width = 0.3
 
             # set scalings
             plt.figure(figsize=(int(len(graphx))/20 + 5, int(len(graphy)/5) + 5))  # these proportions just worked well
 
-            #set limits of the axis
+            # set limits of the axis
             plt.ylim([0, len(graphy)])
             plt.xlim([0, max(graphx) + 10])
 
@@ -1375,7 +1378,7 @@ class Statistics:
             plt.yticks(graphy, labels)
             out = self.pcap_filepath.replace('.pcap', '_plot-Out Degree of an IP' + file_ending)
             plt.tight_layout()
-            plt.savefig(out,dpi=500)
+            plt.savefig(out, dpi=500)
 
             return out
 
@@ -1404,13 +1407,13 @@ class Statistics:
             plt.ylabel('IpAddress')
             plt.xlabel('Overalldegree')
 
-            #set width of the bars
+            # set width of the bars
             width = 0.3
 
             # set scalings
             plt.figure(figsize=(int(len(graphx))/20 + 5, int(len(graphy)/5) + 5))  # these proportions just worked well
 
-            #set limits of the axis
+            # set limits of the axis
             plt.ylim([0, len(graphy)])
             plt.xlim([0, max(graphx) + 10])
 
@@ -1428,10 +1431,10 @@ class Statistics:
             plt.yticks(graphy, labels)
             out = self.pcap_filepath.replace('.pcap', '_plot-Overall Degree of an IP' + file_ending)
             plt.tight_layout()
-            plt.savefig(out,dpi=500)
+            plt.savefig(out, dpi=500)
             return out
 
-        def plot_big_conv_ext_stat(attr:str, title:str, xlabel:str, suffix:str):
+        def plot_big_conv_ext_stat(attr: str, title: str, xlabel: str, suffix: str):
             """
             Plots the desired statistc per connection as horizontal bar plot. 
             Included are 'half-open' connections, where only one packet is exchanged.
@@ -1440,7 +1443,6 @@ class Statistics:
             Note: there may be cutoff/scaling problems within the plot if there is too little data.
 
             :param attr: The desired statistic, named with respect to its attribute in the given statistics table
-            :param table: The statistics table 
             :param title: The title of the created plot
             :param xlabel: The name of the x-axis of the created plot
             :param suffix: The suffix of the created file, including file extension
@@ -1450,10 +1452,10 @@ class Statistics:
             result = self.stats_db.process_user_defined_query(
                 "SELECT ipAddressA, portA, ipAddressB, portB, %s FROM conv_statistics_extended" % attr)
 
-            if (result):
+            if result:
                 graphy, graphx = [], []
                 # plot data in descending order
-                result = sorted(result, key=lambda row: row[4])
+                result = sorted(result, key=lambda r: r[4])
                 # compute plot data
                 for i, row in enumerate(result):
                     addr1, addr2 = "%s:%d" % (row[0], row[1]), "%s:%d" % (row[2], row[3])
@@ -1473,7 +1475,8 @@ class Statistics:
             # compute plot height in inches for scaling the plot
             dist_mult_height = 0.55  # this value turned out to work well
             plt_height = len(graphy) * dist_mult_height
-            title_distance = 1 + 0.012*52.8/plt_height  # orginally, a good title distance turned out to be 1.012 with a plot height of 52.8
+            # originally, a good title distance turned out to be 1.012 with a plot height of 52.8
+            title_distance = 1 + 0.012*52.8/plt_height
 
             plt.gcf().set_size_inches(plt.gcf().get_size_inches()[0], plt_height)  # set plot height
             plt.gcf().subplots_adjust(left=0.35)
@@ -1566,7 +1569,7 @@ class Statistics:
             # plot data and return outpath
             return plot_big_conv_ext_stat("totalConversationDuration", title, 'Duration', suffix)
 
-        def plot_comm_histogram(attr:str, title:str, label:str, suffix:str):
+        def plot_comm_histogram(attr: str, title: str, label: str, suffix: str):
             """
             Plots a histogram about the specified attribute for communications.
             :param attr: The statistics attribute for this histogram
@@ -1598,7 +1601,6 @@ class Statistics:
             plt.title(title)
             plt.ylabel("Relative frequency of connections")
             plt.xlabel(label)
-            width = 0.5
             plt.grid(True)
 
             # create 11 bins
@@ -1619,7 +1621,7 @@ class Statistics:
             plt.savefig(out, dpi=500, bbox_inches='tight', pad=0.2)
             return out
 
-        def plot_histogram_degree(degree_type:str, title:str, label:str, suffix:str):          
+        def plot_histogram_degree(degree_type: str, title: str, label: str, suffix: str):
             """
             Plots a histogram about the specified type for the degree of an IP.
             :param degree_type: The type of degree, i.e. inDegree, outDegree or overallDegree
@@ -1644,7 +1646,6 @@ class Statistics:
             plt.title(title)
             plt.ylabel("Relative frequency of IPs")
             plt.xlabel(label)
-            width = 0.5
             plt.grid(True)
 
             # create 11 bins