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 os
 import sys
 import sys
+from collections import deque
+from datetime import datetime
+from random import randint, randrange, choice, uniform
 
 
 import ID2TLib.Botnet.libbotnetcomm as lb
 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.Botnet.Message as Bmsg
+import ID2TLib.Utility as Util
 from Attack import BaseAttack
 from Attack import BaseAttack
 from Attack.AttackParameters import Parameter as Param
 from Attack.AttackParameters import Parameter as Param
 from Attack.AttackParameters import ParameterTypes
 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.CommunicationProcessor import CommunicationProcessor
 from ID2TLib.Botnet.MessageMapping import MessageMapping
 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):
 class MembersMgmtCommAttack(BaseAttack.BaseAttack):
@@ -32,8 +27,9 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
 
 
         """
         """
         # Initialize communication
         # 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
         # Define allowed parameters and their type
         self.supported_params = {
         self.supported_params = {
@@ -84,13 +80,13 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         for msg_type in Bmsg.MessageType:
         for msg_type in Bmsg.MessageType:
             self.msg_types[msg_type.value] = msg_type
             self.msg_types[msg_type.value] = msg_type
 
 
+        self.DEFAULT_XML_PATH = None
+
     def init_params(self):
     def init_params(self):
         """
         """
         Initialize some parameters of this communication-attack using the user supplied command line parameters.
         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
         The remaining parameters are implicitly set in the provided data file. Note: the timestamps in the file
         have to be sorted in ascending order
         have to be sorted in ascending order
-
-        :param statistics: Reference to a statistics object.
         """
         """
         # set class constants
         # set class constants
         self.DEFAULT_XML_PATH = Util.RESOURCE_DIR + "Botnet/MembersMgmtComm_example.xml"
         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
         # create the final messages that have to be sent, including all bot configurations
         messages = self._create_messages()
         messages = self._create_messages()
 
 
-        if messages == []:
+        if not messages:
             return 0, None
             return 0, None
 
 
         # Setup (initial) parameters for packet creation loop
         # Setup (initial) parameters for packet creation loop
-        BUFFER_SIZE = 1000
+        buffer_size = 1000
         pkt_gen = Generator.PacketGenerator()
         pkt_gen = Generator.PacketGenerator()
         padding = self.get_param_value(Param.PACKET_PADDING)
         padding = self.get_param_value(Param.PACKET_PADDING)
-        packets = deque(maxlen=BUFFER_SIZE)
+        packets = deque(maxlen=buffer_size)
         total_pkts = 0
         total_pkts = 0
         limit_packetcount = self.get_param_value(Param.PACKETS_LIMIT)
         limit_packetcount = self.get_param_value(Param.PACKETS_LIMIT)
         limit_duration = self.get_param_value(Param.ATTACK_DURATION)
         limit_duration = self.get_param_value(Param.ATTACK_DURATION)
         path_attack_pcap = None
         path_attack_pcap = None
-        overThousand = False
+        over_thousand = False
 
 
         msg_packet_mapping = MessageMapping(messages, self.statistics.get_pcap_timestamp_start())
         msg_packet_mapping = MessageMapping(messages, self.statistics.get_pcap_timestamp_start())
         mark_packets = self.get_param_value(Param.HIDDEN_MARK)
         mark_packets = self.get_param_value(Param.HIDDEN_MARK)
@@ -157,7 +153,6 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         # create packets to write to PCAP file
         # create packets to write to PCAP file
         for msg in messages:
         for msg in messages:
             # retrieve the source and destination configurations
             # 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"]
             ip_src, ip_dst = msg.src["IP"], msg.dst["IP"]
             mac_src, mac_dst = msg.src["MAC"], msg.dst["MAC"]
             mac_src, mac_dst = msg.src["MAC"], msg.dst["MAC"]
             if msg.type.is_request():
             if msg.type.is_request():
@@ -180,9 +175,11 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 nl_size = randint(1, 25)    # what is max NL entries?
                 nl_size = randint(1, 25)    # what is max NL entries?
 
 
             # create suitable IP/UDP packet and add to packets list
             # 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
             packet.time = msg.time
 
 
@@ -201,32 +198,32 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             # Store timestamp of first packet (for attack label)
             # Store timestamp of first packet (for attack label)
             if total_pkts <= 1:
             if total_pkts <= 1:
                 self.attack_start_utime = packets[0].time
                 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)
                     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]
                     last_packet = packets[-1]
                     path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
                     path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
-                    packets = deque(maxlen=BUFFER_SIZE)
+                    packets = deque(maxlen=buffer_size)
                 else:
                 else:
                     packets = list(packets)
                     packets = list(packets)
-                    Generator.equal_length(packets, padding = padding)
+                    Generator.equal_length(packets, padding=padding)
                     last_packet = packets[-1]
                     last_packet = packets[-1]
                     max_len = len(last_packet)
                     max_len = len(last_packet)
-                    overThousand = True
+                    over_thousand = True
                     path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
                     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 there are unwritten packets remaining, write them to the PCAP file
         if len(packets) > 0:
         if len(packets) > 0:
-            if overThousand:
+            if over_thousand:
                 packets = list(packets)
                 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)
                 path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
                 last_packet = packets[-1]
                 last_packet = packets[-1]
             else:
             else:
                 packets = list(packets)
                 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)
                 path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
                 last_packet = packets[-1]
                 last_packet = packets[-1]
 
 
@@ -239,20 +236,19 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         self.attack_end_utime = last_packet.time
         self.attack_end_utime = last_packet.time
 
 
         # Return packets sorted by packet by timestamp and total number of packets (sent)
         # 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):
     def generate_attack_packets(self):
         pass
         pass
 
 
-
     def _create_messages(self):
     def _create_messages(self):
         """
         """
         Creates the messages that are to be injected into the PCAP.
         Creates the messages that are to be injected into the PCAP.
         :return: the final messages as a list
         :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.
             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()
             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()
             macgen = Generator.MacAddressGenerator()
 
 
             # assign existing IPs and the corresponding MAC addresses in the PCAP to the IDs
             # 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}
                 bot_configs[random_id] = {"Type": idtype, "IP": ip, "MAC": mac}
                 ids.remove(random_id)
                 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
             Assigns a realisitic ttl to each bot from @param: bot_configs. Uses statistics and distribution to be able
             to calculate a realisitc ttl.
             to calculate a realisitc ttl.
             :param bot_configs: List that contains all bots that should be assigned with realistic ttls.
             :param bot_configs: List that contains all bots that should be assigned with realistic ttls.
-            '''
+            """
             ids = sorted(bot_configs.keys())
             ids = sorted(bot_configs.keys())
-            for pos,bot in enumerate(ids):
+            for pos, bot in enumerate(ids):
                 bot_type = bot_configs[bot]["Type"]
                 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
                     bot_configs[bot]["TTL"] = 128
                     # Set TTL based on TTL distribution of IP address
                     # 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"])
                     bot_ttl_dist = self.statistics.get_ttl_distribution(bot_configs[bot]["IP"])
                     if len(bot_ttl_dist) > 0:
                     if len(bot_ttl_dist) > 0:
                         source_ttl_prob_dict = Lea.fromValFreqsDict(bot_ttl_dist)
                         source_ttl_prob_dict = Lea.fromValFreqsDict(bot_ttl_dist)
@@ -310,33 +306,43 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                         else:
                         else:
                             bot_configs[bot]["TTL"] = self.statistics.process_db_query("most_used(ttlValue)")
                             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
             Assigns realistic timestamps to a set of messages
 
 
             :param messages: the set of messages to be updated
             :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 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
             :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 = []
             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
             # update all timestamps
             for req_msg in messages:
             for req_msg in messages:
 
 
-                if(req_msg in updated_msgs):
+                if req_msg in updated_msgs:
                     # message already updated
                     # message already updated
                     continue
                     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)
                     req_msg.time = zero_reference + req_msg.time + uniform(-0.05, 0.05)
                     updated_msgs.append(req_msg)
                     updated_msgs.append(req_msg)
 
 
@@ -347,14 +353,17 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 if req_msg.refer_msg_id != -1:
                 if req_msg.refer_msg_id != -1:
                     respns_msg = messages[req_msg.refer_msg_id]
                     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:
                     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:
                     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)
                     updated_msgs.append(respns_msg)
                     last_response[(req_msg.src, req_msg.dst)] = respns_msg.time
                     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):
         def assign_ttls_from_caida(bot_configs):
             """
             """
             Assign realistic TTL values to bots with respect to their IP, based on the CAIDA dataset.
             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
             :param bot_configs: the existing bot configurations
             """
             """
@@ -371,7 +382,7 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             def get_ip_ttl_distrib():
             def get_ip_ttl_distrib():
                 """
                 """
                 Parses the CSV file containing a mapping between IP and their used TTLs.
                 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 = {}
                 ip_based_distrib = {}
                 with open("resources/CaidaTTL_perIP.csv", "r") as file:
                 with open("resources/CaidaTTL_perIP.csv", "r") as file:
@@ -380,7 +391,8 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                     for line in file:
                     for line in file:
                         ip_addr, ttl, freq = line.split(",")
                         ip_addr, ttl, freq = line.split(",")
                         if ip_addr not in ip_based_distrib:
                         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)
                         ip_based_distrib[ip_addr][ttl] = int(freq)
 
 
                 return ip_based_distrib
                 return ip_based_distrib
@@ -417,7 +429,8 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 # if there exists detailed information about the TTL distribution of this IP
                 # if there exists detailed information about the TTL distribution of this IP
                 elif bot_ip in ip_ttl_distrib:
                 elif bot_ip in ip_ttl_distrib:
                     ip_ttl_freqs = ip_ttl_distrib[bot_ip]
                     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()
                     bot_configs[bot_id]["TTL"] = source_ttl_prob_dict.random()
 
 
                 # otherwise assign a random TTL based on the total TTL distribution
                 # 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)
         filepath_csv = self.get_param_value(Param.FILE_CSV)
 
 
         # use C++ communication processor for faster interval finding
         # 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
         # only use CSV input if the XML path is the default one
         # --> prefer XML input over CSV input (in case both are given)
         # --> 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=" ")
                 print("Writing corresponding XML file...", end=" ")
                 sys.stdout.flush()
                 sys.stdout.flush()
             filepath_xml = cpp_comm_proc.write_xml(Util.OUT_DIR, filename)
             filepath_xml = cpp_comm_proc.write_xml(Util.OUT_DIR, filename)
-            if print_updates: print("done.")
+            if print_updates:
+                print("done.")
         else:
         else:
             filesize = os.path.getsize(filepath_xml) / 2**20  # get filesize in MB
             filesize = os.path.getsize(filepath_xml) / 2**20  # get filesize in MB
             if filesize > 10:
             if filesize > 10:
@@ -455,7 +469,8 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
                 sys.stdout.flush()
                 sys.stdout.flush()
                 print_updates = True
                 print_updates = True
             cpp_comm_proc.parse_xml(filepath_xml)
             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
         # find a good communication mapping in the input file that matches the users parameters
         nat = self.get_param_value(Param.NAT_PRESENT)
         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)
         start_idx = self.get_param_value(Param.INTERVAL_SELECT_START)
         end_idx = self.get_param_value(Param.INTERVAL_SELECT_END)
         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 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=" ")
             print("Selecting communication interval from input CSV/XML file...", end=" ")
             sys.stdout.flush()
             sys.stdout.flush()
             if potential_long_find_time:
             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()
                 sys.stdout.flush()
             print_updates = True
             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:
         if not comm_interval:
             print("Error: An interval that satisfies the input cannot be found.")
             print("Error: An interval that satisfies the input cannot be found.")
             return []
             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
         # 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:
         while len(mapped_ids) > number_init_bots:
             rm_idx = randrange(0, len(mapped_ids))
             rm_idx = randrange(0, len(mapped_ids))
             del mapped_ids[rm_idx]
             del mapped_ids[rm_idx]
 
 
-        if print_updates: print("Generating attack packets...", end=" ")
+        if print_updates:
+            print("Generating attack packets...", end=" ")
         sys.stdout.flush()
         sys.stdout.flush()
         # get the messages contained in the chosen interval
         # 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)
         comm_proc.set_mapping(abstract_packets, mapped_ids)
         # determine ID roles and select the messages that are to be mapped into the PCAP
         # determine ID roles and select the messages that are to be mapped into the PCAP
         messages = comm_proc.det_id_roles_and_msgs()
         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_total = self.get_param_value(Param.IP_REUSE_TOTAL)
         reuse_percent_external = self.get_param_value(Param.IP_REUSE_EXTERNAL)
         reuse_percent_external = self.get_param_value(Param.IP_REUSE_EXTERNAL)
         reuse_percent_local = self.get_param_value(Param.IP_REUSE_LOCAL)
         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
         # create IP and MAC configurations for the IDs/Bots
         ipgen = Generator.IPGenerator()
         ipgen = Generator.IPGenerator()
@@ -529,22 +550,27 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             existing_external_ips = sorted(pcapops.get_existing_external_ips(reuse_count_external))
             existing_external_ips = sorted(pcapops.get_existing_external_ips(reuse_count_external))
             remaining = len(external_ids) - len(existing_external_ips)
             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)])
             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
         zero_reference = self.get_param_value(Param.INJECT_AT_TIMESTAMP) - messages[0].time
 
 
         # calculate the average delay values for local and external responses
         # calculate the average delay values for local and external responses
         avg_delay_local, avg_delay_external = self.statistics.get_avg_delay_local_ext()
         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())
         reserved_ports = set(int(line.strip()) for line in open(Util.RESOURCE_DIR + "reserved_ports.txt").readlines())
+
         def filter_reserved(get_port):
         def filter_reserved(get_port):
             port = get_port()
             port = get_port()
             while port in reserved_ports:
             while port in reserved_ports:
@@ -554,11 +580,11 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
         # create port configurations for the bots
         # create port configurations for the bots
         use_multiple_ports = self.get_param_value(Param.MULTIPORT)
         use_multiple_ports = self.get_param_value(Param.MULTIPORT)
         for bot in sorted(bot_configs):
         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:
             if not use_multiple_ports:
                 bot_configs[bot]["DstPort"] = filter_reserved(Generator.gen_random_server_port)
                 bot_configs[bot]["DstPort"] = filter_reserved(Generator.gen_random_server_port)
             else:
             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
         # assign realistic TTL for every bot
         if self.get_param_value(Param.TTL_FROM_CAIDA):
         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
         # put together the final messages including the full sender and receiver
         # configurations (i.e. IP, MAC, port, ...) for easier later use
         # configurations (i.e. IP, MAC, port, ...) for easier later use
         final_messages = []
         final_messages = []
-        messages = sorted(messages, key=lambda msg: msg.time)
+        messages = sorted(messages, key=lambda m: m.time)
         new_id = 0
         new_id = 0
 
 
         for msg in messages:
         for msg in messages:
@@ -584,7 +610,7 @@ class MembersMgmtCommAttack(BaseAttack.BaseAttack):
             msg.src["ID"], msg.dst["ID"] = id_src, id_dst
             msg.src["ID"], msg.dst["ID"] = id_src, id_dst
             msg.msg_id = new_id
             msg.msg_id = new_id
             new_id += 1
             new_id += 1
-            ### Important here to update refers, if needed later?
+            # Important here to update refers, if needed later?
             final_messages.append(msg)
             final_messages.append(msg)
 
 
         return final_messages
         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)
         :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 = []
             external_conv = []
             local_conv = []
             local_conv = []
 
 
             for conv in conv_delays:
             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)
                     external_conv.append(conv)
                 else:
                 else:
                     local_conv.append(conv)
                     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_external = 0.0
             avg_delay_local = 0.0
             avg_delay_local = 0.0
             default_ext = False
             default_ext = False
             default_local = False
             default_local = False
 
 
-            if(local_conv):
+            if local_conv:
                 for conv in local_conv:
                 for conv in local_conv:
                     avg_delay_local += conv[2]
                     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:
             else:
                 # no local conversations in statistics found
                 # no local conversations in statistics found
                 avg_delay_local = 0.055
                 avg_delay_local = 0.055
                 default_local = True
                 default_local = True
 
 
-            if(external_conv):
+            if external_conv:
                 for conv in external_conv:
                 for conv in external_conv:
                     avg_delay_external += conv[2]
                     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:
             else:
                 # no external conversations in statistics found
                 # no external conversations in statistics found
                 avg_delay_external = 0.09
                 avg_delay_external = 0.09
                 default_ext = True
                 default_ext = True
         else:
         else:
-            #if no statistics were found, use these numbers
+            # if no statistics were found, use these numbers
             avg_delay_external = 0.09
             avg_delay_external = 0.09
             avg_delay_local = 0.055
             avg_delay_local = 0.055
             default_ext = True
             default_ext = True
@@ -740,13 +742,14 @@ class Statistics:
         # print information, that (default) values are used, that are not collected from the Input PCAP
         # print information, that (default) values are used, that are not collected from the Input PCAP
         if default_ext or default_local:
         if default_ext or default_local:
             if default_ext and 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:
             elif default_ext:
                 print("Warning: Could not collect average delays for external communication, using following values:")
                 print("Warning: Could not collect average delays for external communication, using following values:")
             elif default_local:
             elif default_local:
                 print("Warning: Could not collect average delays for local communication, using following values:")
                 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
         return avg_delay_local, avg_delay_external
 
 
@@ -762,7 +765,7 @@ class Statistics:
                 "SELECT ipAddress, %s FROM ip_degrees" % degree_type)
                 "SELECT ipAddress, %s FROM ip_degrees" % degree_type)
 
 
         degrees = []
         degrees = []
-        if(degrees_raw):
+        if degrees_raw:
             for deg in degrees_raw:
             for deg in degrees_raw:
                 if int(deg[1]) > 0:
                 if int(deg[1]) > 0:
                     degrees.append(deg)
                     degrees.append(deg)
@@ -1298,13 +1301,13 @@ class Statistics:
             plt.ylabel('IpAddress')
             plt.ylabel('IpAddress')
             plt.xlabel('Indegree')
             plt.xlabel('Indegree')
 
 
-            #set width of the bars
+            # set width of the bars
             width = 0.3
             width = 0.3
 
 
             # set scalings
             # set scalings
             plt.figure(figsize=(int(len(graphx))/20 + 5, int(len(graphy)/5) + 5))  # these proportions just worked well
             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.ylim([0, len(graphy)])
             plt.xlim([0, max(graphx) + 10])
             plt.xlim([0, max(graphx) + 10])
 
 
@@ -1322,7 +1325,7 @@ class Statistics:
             plt.yticks(graphy, labels)
             plt.yticks(graphy, labels)
             out = self.pcap_filepath.replace('.pcap', '_plot-In Degree of an IP' + file_ending)
             out = self.pcap_filepath.replace('.pcap', '_plot-In Degree of an IP' + file_ending)
             plt.tight_layout()
             plt.tight_layout()
-            plt.savefig(out,dpi=500)
+            plt.savefig(out, dpi=500)
 
 
             return out
             return out
 
 
@@ -1351,13 +1354,13 @@ class Statistics:
             plt.ylabel('IpAddress')
             plt.ylabel('IpAddress')
             plt.xlabel('Outdegree')
             plt.xlabel('Outdegree')
 
 
-            #set width of the bars
+            # set width of the bars
             width = 0.3
             width = 0.3
 
 
             # set scalings
             # set scalings
             plt.figure(figsize=(int(len(graphx))/20 + 5, int(len(graphy)/5) + 5))  # these proportions just worked well
             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.ylim([0, len(graphy)])
             plt.xlim([0, max(graphx) + 10])
             plt.xlim([0, max(graphx) + 10])
 
 
@@ -1375,7 +1378,7 @@ class Statistics:
             plt.yticks(graphy, labels)
             plt.yticks(graphy, labels)
             out = self.pcap_filepath.replace('.pcap', '_plot-Out Degree of an IP' + file_ending)
             out = self.pcap_filepath.replace('.pcap', '_plot-Out Degree of an IP' + file_ending)
             plt.tight_layout()
             plt.tight_layout()
-            plt.savefig(out,dpi=500)
+            plt.savefig(out, dpi=500)
 
 
             return out
             return out
 
 
@@ -1404,13 +1407,13 @@ class Statistics:
             plt.ylabel('IpAddress')
             plt.ylabel('IpAddress')
             plt.xlabel('Overalldegree')
             plt.xlabel('Overalldegree')
 
 
-            #set width of the bars
+            # set width of the bars
             width = 0.3
             width = 0.3
 
 
             # set scalings
             # set scalings
             plt.figure(figsize=(int(len(graphx))/20 + 5, int(len(graphy)/5) + 5))  # these proportions just worked well
             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.ylim([0, len(graphy)])
             plt.xlim([0, max(graphx) + 10])
             plt.xlim([0, max(graphx) + 10])
 
 
@@ -1428,10 +1431,10 @@ class Statistics:
             plt.yticks(graphy, labels)
             plt.yticks(graphy, labels)
             out = self.pcap_filepath.replace('.pcap', '_plot-Overall Degree of an IP' + file_ending)
             out = self.pcap_filepath.replace('.pcap', '_plot-Overall Degree of an IP' + file_ending)
             plt.tight_layout()
             plt.tight_layout()
-            plt.savefig(out,dpi=500)
+            plt.savefig(out, dpi=500)
             return out
             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. 
             Plots the desired statistc per connection as horizontal bar plot. 
             Included are 'half-open' connections, where only one packet is exchanged.
             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.
             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 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 title: The title of the created plot
             :param xlabel: The name of the x-axis 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
             :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(
             result = self.stats_db.process_user_defined_query(
                 "SELECT ipAddressA, portA, ipAddressB, portB, %s FROM conv_statistics_extended" % attr)
                 "SELECT ipAddressA, portA, ipAddressB, portB, %s FROM conv_statistics_extended" % attr)
 
 
-            if (result):
+            if result:
                 graphy, graphx = [], []
                 graphy, graphx = [], []
                 # plot data in descending order
                 # plot data in descending order
-                result = sorted(result, key=lambda row: row[4])
+                result = sorted(result, key=lambda r: r[4])
                 # compute plot data
                 # compute plot data
                 for i, row in enumerate(result):
                 for i, row in enumerate(result):
                     addr1, addr2 = "%s:%d" % (row[0], row[1]), "%s:%d" % (row[2], row[3])
                     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
             # compute plot height in inches for scaling the plot
             dist_mult_height = 0.55  # this value turned out to work well
             dist_mult_height = 0.55  # this value turned out to work well
             plt_height = len(graphy) * dist_mult_height
             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().set_size_inches(plt.gcf().get_size_inches()[0], plt_height)  # set plot height
             plt.gcf().subplots_adjust(left=0.35)
             plt.gcf().subplots_adjust(left=0.35)
@@ -1566,7 +1569,7 @@ class Statistics:
             # plot data and return outpath
             # plot data and return outpath
             return plot_big_conv_ext_stat("totalConversationDuration", title, 'Duration', suffix)
             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.
             Plots a histogram about the specified attribute for communications.
             :param attr: The statistics attribute for this histogram
             :param attr: The statistics attribute for this histogram
@@ -1598,7 +1601,6 @@ class Statistics:
             plt.title(title)
             plt.title(title)
             plt.ylabel("Relative frequency of connections")
             plt.ylabel("Relative frequency of connections")
             plt.xlabel(label)
             plt.xlabel(label)
-            width = 0.5
             plt.grid(True)
             plt.grid(True)
 
 
             # create 11 bins
             # create 11 bins
@@ -1619,7 +1621,7 @@ class Statistics:
             plt.savefig(out, dpi=500, bbox_inches='tight', pad=0.2)
             plt.savefig(out, dpi=500, bbox_inches='tight', pad=0.2)
             return out
             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.
             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
             :param degree_type: The type of degree, i.e. inDegree, outDegree or overallDegree
@@ -1644,7 +1646,6 @@ class Statistics:
             plt.title(title)
             plt.title(title)
             plt.ylabel("Relative frequency of IPs")
             plt.ylabel("Relative frequency of IPs")
             plt.xlabel(label)
             plt.xlabel(label)
-            width = 0.5
             plt.grid(True)
             plt.grid(True)
 
 
             # create 11 bins
             # create 11 bins