MembersMgmtCommAttack.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. import os
  2. import sys
  3. from collections import deque
  4. from datetime import datetime
  5. from random import randint, randrange, choice, uniform
  6. import ID2TLib.Botnet.libbotnetcomm as lb
  7. from lea import Lea
  8. from scapy.layers.inet import IP, IPOption_Security
  9. import ID2TLib.Botnet.Message as Bmsg
  10. import ID2TLib.Utility as Util
  11. from Attack import BaseAttack
  12. from Attack.AttackParameters import Parameter as Param
  13. from Attack.AttackParameters import ParameterTypes
  14. from ID2TLib import Generator
  15. from ID2TLib.Botnet.CommunicationProcessor import CommunicationProcessor
  16. from ID2TLib.Botnet.MessageMapping import MessageMapping
  17. from ID2TLib.PcapAddressOperations import PcapAddressOperations
  18. from ID2TLib.Ports import PortSelectors
  19. class MembersMgmtCommAttack(BaseAttack.BaseAttack):
  20. def __init__(self):
  21. """
  22. Creates a new instance of the Membership Management Communication.
  23. """
  24. # Initialize communication
  25. super(MembersMgmtCommAttack, self).__init__(
  26. "Membership Management Communication Attack (MembersMgmtCommAttack)",
  27. "Injects Membership Management Communication", "Botnet communication")
  28. # Define allowed parameters and their type
  29. self.supported_params = {
  30. # parameters regarding attack
  31. Param.INJECT_AT_TIMESTAMP: ParameterTypes.TYPE_FLOAT,
  32. Param.INJECT_AFTER_PACKET: ParameterTypes.TYPE_PACKET_POSITION,
  33. Param.PACKETS_LIMIT: ParameterTypes.TYPE_INTEGER_POSITIVE,
  34. Param.ATTACK_DURATION: ParameterTypes.TYPE_INTEGER_POSITIVE,
  35. # use num_attackers to specify number of communicating devices?
  36. Param.NUMBER_INITIATOR_BOTS: ParameterTypes.TYPE_INTEGER_POSITIVE,
  37. # input file containing botnet communication
  38. Param.FILE_CSV: ParameterTypes.TYPE_FILEPATH,
  39. Param.FILE_XML: ParameterTypes.TYPE_FILEPATH,
  40. # the percentage of IP reuse (if total and other is specified, percentages are multiplied)
  41. Param.IP_REUSE_TOTAL: ParameterTypes.TYPE_PERCENTAGE,
  42. Param.IP_REUSE_LOCAL: ParameterTypes.TYPE_PERCENTAGE,
  43. Param.IP_REUSE_EXTERNAL: ParameterTypes.TYPE_PERCENTAGE,
  44. Param.INJECT_INTO_IPS: ParameterTypes.TYPE_IP_ADDRESS,
  45. # the user-selected padding to add to every packet
  46. Param.PACKET_PADDING: ParameterTypes.TYPE_PADDING,
  47. # presence of NAT at the gateway of the network
  48. Param.NAT_PRESENT: ParameterTypes.TYPE_BOOLEAN,
  49. # whether the TTL distribution should be based on the input PCAP
  50. # or the CAIDA dataset
  51. Param.TTL_FROM_CAIDA: ParameterTypes.TYPE_BOOLEAN,
  52. # whether the destination port of a response should be the ephemeral port
  53. # its request came from or a static (server)port based on a hostname
  54. Param.MULTIPORT: ParameterTypes.TYPE_BOOLEAN,
  55. # information about the interval selection strategy
  56. Param.INTERVAL_SELECT_STRATEGY: ParameterTypes.TYPE_INTERVAL_SELECT_STRAT,
  57. Param.INTERVAL_SELECT_START: ParameterTypes.TYPE_INTEGER_POSITIVE,
  58. Param.INTERVAL_SELECT_END: ParameterTypes.TYPE_INTEGER_POSITIVE,
  59. # determines whether injected packets are marked with an unused IP option
  60. # to easily filter them in e.g. wireshark
  61. Param.HIDDEN_MARK: ParameterTypes.TYPE_BOOLEAN
  62. }
  63. # create dict with MessageType values for fast name lookup
  64. self.msg_types = {}
  65. for msg_type in Bmsg.MessageType:
  66. self.msg_types[msg_type.value] = msg_type
  67. self.DEFAULT_XML_PATH = None
  68. def init_params(self):
  69. """
  70. Initialize some parameters of this communication-attack using the user supplied command line parameters.
  71. The remaining parameters are implicitly set in the provided data file. Note: the timestamps in the file
  72. have to be sorted in ascending order
  73. """
  74. # set class constants
  75. self.DEFAULT_XML_PATH = Util.RESOURCE_DIR + "Botnet/MembersMgmtComm_example.xml"
  76. # PARAMETERS: initialize with default values
  77. # (values are overwritten if user specifies them)
  78. self.add_param_value(Param.INJECT_AFTER_PACKET, 1 + randint(0, self.statistics.get_packet_count() // 5))
  79. self.add_param_value(Param.FILE_XML, self.DEFAULT_XML_PATH)
  80. # Alternatively new attack parameter?
  81. duration = int(float(self.statistics.get_capture_duration()))
  82. self.add_param_value(Param.ATTACK_DURATION, duration)
  83. self.add_param_value(Param.NUMBER_INITIATOR_BOTS, 1)
  84. # NAT on by default
  85. self.add_param_value(Param.NAT_PRESENT, True)
  86. # TODO: change 1 to something better
  87. self.add_param_value(Param.IP_REUSE_TOTAL, 1)
  88. self.add_param_value(Param.IP_REUSE_LOCAL, 0.5)
  89. self.add_param_value(Param.IP_REUSE_EXTERNAL, 0.5)
  90. # add default additional padding
  91. self.add_param_value(Param.PACKET_PADDING, 20)
  92. # choose the input PCAP as default base for the TTL distribution
  93. self.add_param_value(Param.TTL_FROM_CAIDA, False)
  94. # do not use multiple ports for requests and responses
  95. self.add_param_value(Param.MULTIPORT, False)
  96. # interval selection strategy
  97. self.add_param_value(Param.INTERVAL_SELECT_STRATEGY, "optimal")
  98. self.add_param_value(Param.HIDDEN_MARK, False)
  99. def generate_attack_pcap(self):
  100. """
  101. Injects the packets of this attack into a PCAP and stores it as a temporary file.
  102. :return: a tuple of the number packets injected, the path to the temporary attack PCAP
  103. and a list of additionally created files
  104. """
  105. # create the final messages that have to be sent, including all bot configurations
  106. messages = self._create_messages()
  107. if not messages:
  108. return 0, None
  109. # Setup (initial) parameters for packet creation loop
  110. buffer_size = 1000
  111. pkt_gen = Generator.PacketGenerator()
  112. padding = self.get_param_value(Param.PACKET_PADDING)
  113. packets = deque(maxlen=buffer_size)
  114. total_pkts = 0
  115. limit_packetcount = self.get_param_value(Param.PACKETS_LIMIT)
  116. limit_duration = self.get_param_value(Param.ATTACK_DURATION)
  117. path_attack_pcap = None
  118. over_thousand = False
  119. msg_packet_mapping = MessageMapping(messages, self.statistics.get_pcap_timestamp_start())
  120. mark_packets = self.get_param_value(Param.HIDDEN_MARK)
  121. # create packets to write to PCAP file
  122. for msg in messages:
  123. # retrieve the source and destination configurations
  124. ip_src, ip_dst = msg.src["IP"], msg.dst["IP"]
  125. mac_src, mac_dst = msg.src["MAC"], msg.dst["MAC"]
  126. if msg.type.is_request():
  127. port_src, port_dst = int(msg.src["SrcPort"]), int(msg.dst["DstPort"])
  128. else:
  129. port_src, port_dst = int(msg.src["DstPort"]), int(msg.dst["SrcPort"])
  130. ttl = int(msg.src["TTL"])
  131. # update duration
  132. duration = msg.time - messages[0].time
  133. # if total number of packets has been sent or the attack duration has been exceeded, stop
  134. if ((limit_packetcount is not None and total_pkts >= limit_packetcount) or
  135. (limit_duration is not None and duration >= limit_duration)):
  136. break
  137. # if the type of the message is a NL reply, determine the number of entries
  138. nl_size = 0
  139. if msg.type == Bmsg.MessageType.SALITY_NL_REPLY:
  140. nl_size = randint(1, 25) # what is max NL entries?
  141. # create suitable IP/UDP packet and add to packets list
  142. packet = pkt_gen.generate_mmcom_packet(ip_src=ip_src, ip_dst=ip_dst, ttl=ttl, mac_src=mac_src,
  143. mac_dst=mac_dst,
  144. port_src=port_src, port_dst=port_dst, message_type=msg.type,
  145. neighborlist_entries=nl_size)
  146. Generator.add_padding(packet, padding, True, True)
  147. packet.time = msg.time
  148. if mark_packets and isinstance(packet.payload, IP): # do this only for ip-packets
  149. ip_data = packet.payload
  150. hidden_opt = IPOption_Security()
  151. hidden_opt.option = 2 # "normal" security opt
  152. hidden_opt.security = 16 # magic value indicating NSA
  153. ip_data.options = hidden_opt
  154. packets.append(packet)
  155. msg_packet_mapping.map_message(msg, packet)
  156. total_pkts += 1
  157. # Store timestamp of first packet (for attack label)
  158. if total_pkts <= 1:
  159. self.attack_start_utime = packets[0].time
  160. elif total_pkts % buffer_size == 0: # every 1000 packets write them to the PCAP file (append)
  161. if over_thousand: # if over 1000 packets written, packet-length for the last few packets may differ
  162. packets = list(packets)
  163. Generator.equal_length(packets, length=max_len, padding=padding, force_len=True)
  164. last_packet = packets[-1]
  165. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  166. packets = deque(maxlen=buffer_size)
  167. else:
  168. packets = list(packets)
  169. Generator.equal_length(packets, padding=padding)
  170. last_packet = packets[-1]
  171. max_len = len(last_packet)
  172. over_thousand = True
  173. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  174. packets = deque(maxlen=buffer_size)
  175. # if there are unwritten packets remaining, write them to the PCAP file
  176. if len(packets) > 0:
  177. if over_thousand:
  178. packets = list(packets)
  179. Generator.equal_length(packets, length=max_len, padding=padding, force_len=True)
  180. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  181. last_packet = packets[-1]
  182. else:
  183. packets = list(packets)
  184. Generator.equal_length(packets, padding=padding)
  185. path_attack_pcap = self.write_attack_pcap(packets, True, path_attack_pcap)
  186. last_packet = packets[-1]
  187. # write the mapping to a file
  188. current_ts = datetime.now().strftime("%Y%m%d-%H%M%S")
  189. mapping_filename = "mapping_" + current_ts + ".xml"
  190. msg_packet_mapping.write_to_file(mapping_filename)
  191. # Store timestamp of last packet
  192. self.attack_end_utime = last_packet.time
  193. # Return packets sorted by packet by timestamp and total number of packets (sent)
  194. return total_pkts, path_attack_pcap, [mapping_filename]
  195. def generate_attack_packets(self):
  196. pass
  197. def _create_messages(self):
  198. """
  199. Creates the messages that are to be injected into the PCAP.
  200. :return: the final messages as a list
  201. """
  202. def add_ids_to_config(ids_to_add: list, existing_ips: list, new_ips: list, bot_configs: dict,
  203. idtype: str = "local", router_mac: str = ""):
  204. """
  205. Creates IP and MAC configurations for the given IDs and adds them to the existing configurations object.
  206. :param ids_to_add: all sorted IDs that have to be configured and added
  207. :param existing_ips: the existing IPs in the PCAP file that should be assigned to some, or all, IDs
  208. :param new_ips: the newly generated IPs that should be assigned to some, or all, IDs
  209. :param bot_configs: the existing configurations for the bots
  210. :param idtype: the locality type of the IDs
  211. :param router_mac: the MAC address of the router in the PCAP
  212. """
  213. ids = ids_to_add.copy()
  214. # macgen only needed, when IPs are new local IPs (therefore creating the object here suffices for the
  215. # current callers to not end up with the same MAC paired with different IPs)
  216. macgen = Generator.MacAddressGenerator()
  217. # assign existing IPs and the corresponding MAC addresses in the PCAP to the IDs
  218. for ip in existing_ips:
  219. random_id = choice(ids)
  220. mac = self.statistics.process_db_query("macAddress(IPAddress=%s)" % ip)
  221. bot_configs[random_id] = {"Type": idtype, "IP": ip, "MAC": mac}
  222. ids.remove(random_id)
  223. # assign new IPs and for local IPs new MACs or for external IPs the router MAC to the IDs
  224. for ip in new_ips:
  225. random_id = choice(ids)
  226. if idtype == "local":
  227. mac = macgen.random_mac()
  228. elif idtype == "external":
  229. mac = router_mac
  230. bot_configs[random_id] = {"Type": idtype, "IP": ip, "MAC": mac}
  231. ids.remove(random_id)
  232. def assign_realistic_ttls(bot_configs: dict):
  233. """
  234. Assigns a realisitic ttl to each bot from @param: bot_configs. Uses statistics and distribution to be able
  235. to calculate a realisitc ttl.
  236. :param bot_configs: List that contains all bots that should be assigned with realistic ttls.
  237. """
  238. ids = sorted(bot_configs.keys())
  239. for pos, bot in enumerate(ids):
  240. bot_type = bot_configs[bot]["Type"]
  241. if bot_type == "local": # Set fix TTL for local Bots
  242. bot_configs[bot]["TTL"] = 128
  243. # Set TTL based on TTL distribution of IP address
  244. else: # Set varying TTl for external Bots
  245. bot_ttl_dist = self.statistics.get_ttl_distribution(bot_configs[bot]["IP"])
  246. if len(bot_ttl_dist) > 0:
  247. source_ttl_prob_dict = Lea.fromValFreqsDict(bot_ttl_dist)
  248. bot_configs[bot]["TTL"] = source_ttl_prob_dict.random()
  249. else:
  250. most_used_ttl = self.statistics.process_db_query("most_used(ttlValue)")
  251. if isinstance(most_used_ttl, list):
  252. bot_configs[bot]["TTL"] = choice(self.statistics.process_db_query("most_used(ttlValue)"))
  253. else:
  254. bot_configs[bot]["TTL"] = self.statistics.process_db_query("most_used(ttlValue)")
  255. def assign_realistic_timestamps(messages: list, external_ids: set, local_ids: set, avg_delay_local: list,
  256. avg_delay_external: list, zero_reference: float):
  257. """
  258. Assigns realistic timestamps to a set of messages
  259. :param messages: the set of messages to be updated
  260. :param external_ids: the set of bot ids, that are outside the network, i.e. external
  261. :param local_ids: the set of bot ids, that are inside the network, i.e. local
  262. :param avg_delay_local: the avg_delay distribution between the dispatch and the reception of a packet
  263. between local computers
  264. :param avg_delay_external: the avg_delay distribution between the dispatch and the reception of a packet
  265. between a local and an external computer
  266. :param zero_reference: the timestamp which is regarded as the beginning of the pcap_file and therefore
  267. handled like a timestamp that resembles 0
  268. """
  269. updated_msgs = []
  270. # Dict, takes a tuple of 2 Bot_IDs as a key (requester, responder), returns the time of the last response,
  271. # the requester received necessary in order to make sure, that additional requests are sent only after the
  272. # response to the last one was received
  273. last_response = {}
  274. for m in messages: # init
  275. last_response[(m.src, m.dst)] = -1
  276. # update all timestamps
  277. for req_msg in messages:
  278. if req_msg in updated_msgs:
  279. # message already updated
  280. continue
  281. # if req_msg.timestamp would be before the timestamp of the response to the last request, req_msg needs
  282. # to be sent later (else branch)
  283. if last_response[(req_msg.src, req_msg.dst)] == -1 or last_response[(req_msg.src, req_msg.dst)] < (
  284. zero_reference + req_msg.time - 0.05):
  285. # update req_msg timestamp with a variation of up to 50ms
  286. req_msg.time = zero_reference + req_msg.time + uniform(-0.05, 0.05)
  287. updated_msgs.append(req_msg)
  288. else:
  289. req_msg.time = last_response[(req_msg.src, req_msg.dst)] + 0.06 + uniform(-0.05, 0.05)
  290. # update response if necessary
  291. if req_msg.refer_msg_id != -1:
  292. respns_msg = messages[req_msg.refer_msg_id]
  293. # check for local or external communication and update response timestamp with the respective
  294. # avg delay
  295. if req_msg.src in external_ids or req_msg.dst in external_ids and avg_delay_external:
  296. # external communication
  297. dist = Lea.fromSeq(avg_delay_external)
  298. else:
  299. # local communication
  300. dist = Lea.fromSeq(avg_delay_local)
  301. delay = 0
  302. while delay < 50 or (float(delay)*0.000001 > 5):
  303. delay = dist.random()
  304. respns_msg.time = req_msg.time + float(delay) * 0.000001
  305. updated_msgs.append(respns_msg)
  306. last_response[(req_msg.src, req_msg.dst)] = respns_msg.time
  307. def assign_ttls_from_caida(bot_configs):
  308. """
  309. Assign realistic TTL values to bots with respect to their IP, based on the CAIDA dataset.
  310. If there exists an entry for a bot's IP, the TTL is chosen based on a distribution over all used TTLs by
  311. this IP.
  312. If there is no such entry, the TTL is chosen based on a distribution over all used TTLs and their
  313. respective frequency.
  314. :param bot_configs: the existing bot configurations
  315. """
  316. def get_ip_ttl_distrib():
  317. """
  318. Parses the CSV file containing a mapping between IP and their used TTLs.
  319. :return: returns a dict with the IPs as keys and dicts for their TTL distribution as values
  320. """
  321. ip_based_distrib = {}
  322. with open("resources/CaidaTTL_perIP.csv", "r") as file:
  323. # every line consists of: IP, TTL, Frequency
  324. next(file) # skip CSV header line
  325. for line in file:
  326. ip_addr, ttl, freq = line.split(",")
  327. if ip_addr not in ip_based_distrib:
  328. # the values for ip_based_distrib are dicts with key=TTL, value=Frequency
  329. ip_based_distrib[ip_addr] = {}
  330. ip_based_distrib[ip_addr][ttl] = int(freq)
  331. return ip_based_distrib
  332. def get_total_ttl_distrib():
  333. """
  334. Parses the CSV file containing an overview of all used TTLs and their respective frequency.
  335. :return: returns a dict with the TTLs as keys and their frequencies as keys
  336. """
  337. total_ttl_distrib = {}
  338. with open("resources/CaidaTTL_total.csv", "r") as file:
  339. # every line consists of: TTL, Frequency, Fraction
  340. next(file) # skip CSV header line
  341. for line in file:
  342. ttl, freq, _ = line.split(",")
  343. total_ttl_distrib[ttl] = int(freq)
  344. return total_ttl_distrib
  345. # get the TTL distribution for every IP that is available in "resources/CaidaTTL_perIP.csv"
  346. ip_ttl_distrib = get_ip_ttl_distrib()
  347. # build a probability dict for the total TTL distribution
  348. total_ttl_prob_dict = Lea.fromValFreqsDict(get_total_ttl_distrib())
  349. # loop over every bot id and assign a TTL to the respective bot
  350. for bot_id in sorted(bot_configs):
  351. bot_type = bot_configs[bot_id]["Type"]
  352. bot_ip = bot_configs[bot_id]["IP"]
  353. if bot_type == "local":
  354. bot_configs[bot_id]["TTL"] = 128
  355. # if there exists detailed information about the TTL distribution of this IP
  356. elif bot_ip in ip_ttl_distrib:
  357. ip_ttl_freqs = ip_ttl_distrib[bot_ip]
  358. # build a probability dict from this IP's TTL distribution
  359. source_ttl_prob_dict = Lea.fromValFreqsDict(ip_ttl_freqs)
  360. bot_configs[bot_id]["TTL"] = source_ttl_prob_dict.random()
  361. # otherwise assign a random TTL based on the total TTL distribution
  362. else:
  363. bot_configs[bot_id]["TTL"] = total_ttl_prob_dict.random()
  364. # parse input CSV or XML
  365. filepath_xml = self.get_param_value(Param.FILE_XML)
  366. filepath_csv = self.get_param_value(Param.FILE_CSV)
  367. # use C++ communication processor for faster interval finding
  368. cpp_comm_proc = lb.botnet_comm_processor()
  369. # only use CSV input if the XML path is the default one
  370. # --> prefer XML input over CSV input (in case both are given)
  371. print_updates = False
  372. if filepath_csv and filepath_xml == self.DEFAULT_XML_PATH:
  373. filename = os.path.splitext(os.path.basename(filepath_csv))[0]
  374. filesize = os.path.getsize(filepath_csv) / 2**20 # get filesize in MB
  375. if filesize > 10:
  376. print("\nParsing input CSV file...", end=" ")
  377. sys.stdout.flush()
  378. print_updates = True
  379. cpp_comm_proc.parse_csv(filepath_csv)
  380. if print_updates:
  381. print("done.")
  382. print("Writing corresponding XML file...", end=" ")
  383. sys.stdout.flush()
  384. filepath_xml = cpp_comm_proc.write_xml(Util.OUT_DIR, filename)
  385. if print_updates:
  386. print("done.")
  387. else:
  388. filesize = os.path.getsize(filepath_xml) / 2**20 # get filesize in MB
  389. if filesize > 10:
  390. print("Parsing input XML file...", end=" ")
  391. sys.stdout.flush()
  392. print_updates = True
  393. cpp_comm_proc.parse_xml(filepath_xml)
  394. if print_updates:
  395. print("done.")
  396. # find a good communication mapping in the input file that matches the users parameters
  397. nat = self.get_param_value(Param.NAT_PRESENT)
  398. comm_proc = CommunicationProcessor(self.msg_types, nat)
  399. duration = self.get_param_value(Param.ATTACK_DURATION)
  400. number_init_bots = self.get_param_value(Param.NUMBER_INITIATOR_BOTS)
  401. strategy = self.get_param_value(Param.INTERVAL_SELECT_STRATEGY)
  402. start_idx = self.get_param_value(Param.INTERVAL_SELECT_START)
  403. end_idx = self.get_param_value(Param.INTERVAL_SELECT_END)
  404. potential_long_find_time = (
  405. strategy == "optimal" and (filesize > 4 and self.statistics.get_packet_count() > 1000))
  406. if print_updates or potential_long_find_time:
  407. if not print_updates:
  408. print()
  409. print("Selecting communication interval from input CSV/XML file...", end=" ")
  410. sys.stdout.flush()
  411. if potential_long_find_time:
  412. print("\nWarning: Because of the large input files and the (chosen) interval selection strategy")
  413. print("'optimal', this may take a while. Consider using selection strategy 'random' or 'custom'...",
  414. end=" ")
  415. sys.stdout.flush()
  416. print_updates = True
  417. comm_interval = comm_proc.get_comm_interval(cpp_comm_proc, strategy, number_init_bots, duration, start_idx,
  418. end_idx)
  419. if not comm_interval:
  420. print("Error: An interval that satisfies the input cannot be found.")
  421. return []
  422. if print_updates:
  423. print("done.") # print corresponding message to interval finding message
  424. # retrieve the mapping information
  425. mapped_ids = comm_interval["IDs"]
  426. packet_start_idx = comm_interval["Start"]
  427. packet_end_idx = comm_interval["End"]
  428. while len(mapped_ids) > number_init_bots:
  429. rm_idx = randrange(0, len(mapped_ids))
  430. del mapped_ids[rm_idx]
  431. if print_updates:
  432. print("Generating attack packets...", end=" ")
  433. sys.stdout.flush()
  434. # get the messages contained in the chosen interval
  435. abstract_packets = cpp_comm_proc.get_messages(packet_start_idx, packet_end_idx)
  436. comm_proc.set_mapping(abstract_packets, mapped_ids)
  437. # determine ID roles and select the messages that are to be mapped into the PCAP
  438. messages = comm_proc.det_id_roles_and_msgs()
  439. # use the previously detetermined roles to assign the locality of all IDs
  440. local_ids, external_ids = comm_proc.det_ext_and_local_ids()
  441. # determine number of reused local and external IPs
  442. reuse_percent_total = self.get_param_value(Param.IP_REUSE_TOTAL)
  443. reuse_percent_external = self.get_param_value(Param.IP_REUSE_EXTERNAL)
  444. reuse_percent_local = self.get_param_value(Param.IP_REUSE_LOCAL)
  445. # create IP and MAC configurations for the IDs/Bots
  446. ipgen = Generator.IPGenerator()
  447. pcapops = PcapAddressOperations(self.statistics)
  448. router_mac = pcapops.get_probable_router_mac()
  449. bot_configs = {}
  450. # retrieve and assign the IPs and MACs for the bots with respect to the given parameters
  451. # (IDs are always added to bot_configs in the same order under a given seed)
  452. number_local_ids, number_external_ids = len(local_ids), len(external_ids)
  453. # assign addresses for local IDs
  454. if number_local_ids > 0:
  455. reuse_count_local = int(reuse_percent_total * reuse_percent_local * number_local_ids)
  456. existing_local_ips=[]
  457. if(self.get_param_value(Param.INJECT_INTO_IPS)):
  458. existing_local_ips.append(self.get_param_value(Param.INJECT_INTO_IPS))
  459. existing_local_ips+=(sorted(pcapops.get_existing_local_ips(reuse_count_local-1)))
  460. else:
  461. existing_local_ips = (sorted(pcapops.get_existing_local_ips(reuse_count_local)))
  462. new_local_ips = sorted(pcapops.get_new_local_ips(number_local_ids - len(existing_local_ips)))
  463. add_ids_to_config(sorted(local_ids), existing_local_ips, new_local_ips, bot_configs)
  464. # assign addresses for external IDs
  465. if number_external_ids > 0:
  466. reuse_count_external = int(reuse_percent_total * reuse_percent_external * number_external_ids)
  467. existing_external_ips = sorted(pcapops.get_existing_external_ips(reuse_count_external))
  468. remaining = len(external_ids) - len(existing_external_ips)
  469. for external_ip in existing_external_ips:
  470. ipgen.add_to_blacklist(external_ip)
  471. new_external_ips = sorted([ipgen.random_ip() for _ in range(remaining)])
  472. add_ids_to_config(sorted(external_ids), existing_external_ips, new_external_ips, bot_configs,
  473. idtype="external", router_mac=router_mac)
  474. # this is the timestamp at which the first packet should be injected, the packets have to be shifted to
  475. # the beginning of the pcap file (INJECT_AT_TIMESTAMP) and then the offset of the packets have to be
  476. # compensated to start at the given point in time
  477. zero_reference = self.get_param_value(Param.INJECT_AT_TIMESTAMP) - messages[0].time
  478. # calculate the average delay values for local and external responses
  479. avg_delay_local, avg_delay_external = self.statistics.get_avg_delay_distributions(False)
  480. # set timestamps
  481. assign_realistic_timestamps(messages, external_ids, local_ids, avg_delay_local, avg_delay_external,
  482. zero_reference)
  483. port_selector = PortSelectors.LINUX
  484. reserved_ports = set(int(line.strip()) for line in open(Util.RESOURCE_DIR + "reserved_ports.txt").readlines())
  485. def filter_reserved(get_port):
  486. port = get_port()
  487. while port in reserved_ports:
  488. port = get_port()
  489. return port
  490. # create port configurations for the bots
  491. use_multiple_ports = self.get_param_value(Param.MULTIPORT)
  492. for bot in sorted(bot_configs):
  493. bot_configs[bot]["SrcPort"] = filter_reserved(port_selector.select_port_udp)
  494. if not use_multiple_ports:
  495. bot_configs[bot]["DstPort"] = filter_reserved(Generator.gen_random_server_port)
  496. else:
  497. bot_configs[bot]["DstPort"] = filter_reserved(port_selector.select_port_udp)
  498. # assign realistic TTL for every bot
  499. if self.get_param_value(Param.TTL_FROM_CAIDA):
  500. assign_ttls_from_caida(bot_configs)
  501. else:
  502. assign_realistic_ttls(bot_configs)
  503. # put together the final messages including the full sender and receiver
  504. # configurations (i.e. IP, MAC, port, ...) for easier later use
  505. final_messages = []
  506. messages = sorted(messages, key=lambda m: m.time)
  507. new_id = 0
  508. for msg in messages:
  509. type_src, type_dst = bot_configs[msg.src]["Type"], bot_configs[msg.dst]["Type"]
  510. id_src, id_dst = msg.src, msg.dst
  511. # sort out messages that do not have a suitable locality setting
  512. if type_src == "external" and type_dst == "external":
  513. continue
  514. msg.src, msg.dst = bot_configs[id_src], bot_configs[id_dst]
  515. msg.src["ID"], msg.dst["ID"] = id_src, id_dst
  516. msg.msg_id = new_id
  517. new_id += 1
  518. # Important here to update refers, if needed later?
  519. final_messages.append(msg)
  520. return final_messages