diff --git a/tracevis.py b/tracevis.py
index 98ea76e..652d4f5 100755
--- a/tracevis.py
+++ b/tracevis.py
@@ -66,6 +66,9 @@ def get_args():
help="annotation for the second packets (dns and packet trace)")
parser.add_argument('--rexmit', action='store_true',
help="same as rexmit option (only one packet. all TTL steps, same stream)")
+ parser.add_argument('--paris', action='store_true',
+ help="same as 'new,rexmit' option (like Paris-Traceroute)")
+ # this argument ('-o', '--options') will be changed or removed before v1.0.0
parser.add_argument('-o', '--options', type=str, default="new",
help="change the behavior of the trace route"
+ " - 'rexmit' : to be similar to doing retransmission with incremental TTL (only one packet, one destination)"
@@ -128,14 +131,17 @@ def main(args):
edge_lable = args["label"].lower()
if args.get("rexmit"):
trace_retransmission = True
+ if args.get("paris"):
+ trace_with_retransmission = True
if args.get("options"):
- trace_options= args["options"].replace(' ', '').split(',')
+ # this argument will be changed or removed before v1.0.0
+ trace_options = args["options"].replace(' ', '').split(',')
if "new" in trace_options and "rexmit" in trace_options:
trace_with_retransmission = True
elif "rexmit" in trace_options:
trace_retransmission = True
else:
- pass # "new" is default
+ pass # "new" is default
if args.get("dns") or args.get("dnstcp"):
do_traceroute = True
name_prefix += "dns"
diff --git a/utils/convert_packetlist.py b/utils/convert_packetlist.py
new file mode 100644
index 0000000..d270bc4
--- /dev/null
+++ b/utils/convert_packetlist.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+
+from base64 import b64encode
+
+
+# this function source: https://stackoverflow.com/a/64410921
+def packet2json(packet_obj):
+ packet_dict = {}
+ layer = ''
+ for line in packet_obj.show2(dump=True).split('\n'):
+ if '###' in line:
+ layer = line.strip('#[] ')
+ packet_dict[layer] = {}
+ elif '=' in line:
+ key, val = line.split('=', 1)
+ if layer == 'Raw' and key.strip() == 'load':
+ packet_dict[layer][key.strip()] = b64encode(
+ packet_obj['Raw'].load).decode()
+ else:
+ packet_dict[layer][key.strip()] = val.strip()
+ return packet_dict
+
+
+def packetlist2json(answered, unanswered):
+ packetlist = {'sent': [], 'received': []}
+ if len(answered) == 0:
+ if len(unanswered) != 0:
+ packetlist["sent"] = packet2json(packet_obj=unanswered[0])
+ else:
+ for sentp, receivedp in answered:
+ if len(packetlist["sent"]) == 0:
+ packetlist["sent"] = packet2json(packet_obj=sentp)
+ packetlist["received"].append(
+ packet2json(packet_obj=receivedp))
+ return packetlist
diff --git a/utils/trace.py b/utils/trace.py
index b917fdb..4cd3b8c 100755
--- a/utils/trace.py
+++ b/utils/trace.py
@@ -7,6 +7,7 @@
import socket
import sys
import time
+from copy import deepcopy
from datetime import datetime
from time import sleep
@@ -26,38 +27,79 @@
OS_NAME = platform.system()
-def parse_packet(request_and_answer, current_ttl, elapsed_ms, summary_postfix=""):
- if request_and_answer is not None:
- req_answer = request_and_answer[1]
- packet_send_time = request_and_answer[0].sent_time
- packet_receive_time = req_answer.time
- packet_elapsed_ms = float(
- format(abs((packet_receive_time - packet_send_time) * 1000), '.3f'))
- if packet_elapsed_ms > 0:
- elapsed_ms = packet_elapsed_ms
- backttl = 0
- if req_answer[IP].ttl <= 20:
- backttl = int((current_ttl - req_answer[IP].ttl) / 2) + 1
- elif req_answer[IP].ttl <= 64:
- backttl = 64 - req_answer[IP].ttl + 1
- elif req_answer[IP].ttl <= 128:
- backttl = 128 - req_answer[IP].ttl + 1
+def choose_desirable_packet(request_and_answers, do_tcphandshake):
+ # request_and_answers.summary()
+ summary_postfix = str(request_and_answers.summary)
+ print(" " + summary_postfix)
+ if do_tcphandshake and not request_and_answers[0][1].haslayer(ICMP):
+ desirable_packet = None
+ # [0][0] = sent packet 1 -- [0][1] == received packet 1
+ # [1][0] = sent packet 1 -- [1][1] == received packet 2
+ # [2][0] = sent packet 1 -- [2][1] == received packet 3
+ if len(request_and_answers) > 1:
+ if request_and_answers[0][1][TCP].flags == "A" and request_and_answers[1][1].haslayer(ICMP):
+ # todo xhdix: flag the first hop as a middlebox
+ desirable_packet = request_and_answers[1]
+ # todo xhdix: flag as middlebox if [0][1][TCP].flags in ["R", "RA", "F", "FA"] and [1][1].haslayer(ICMP
+ elif request_and_answers[0][1][TCP].flags in ["R", "RA", "F", "FA"]:
+ desirable_packet = request_and_answers[0]
+ else:
+ desirable_packet = request_and_answers[1]
+ # we need hello from server, not ACK from middlebox
+ elif request_and_answers[0][1][TCP].flags != "A":
+ desirable_packet = request_and_answers[0]
+ # here we just want to have a correct path, so we ignore the lack of ACK before Server Hello in some weird networks
+ elif request_and_answers[0][1][TCP].flags == "A" and request_and_answers[0][1].haslayer(Raw):
+ desirable_packet = request_and_answers[0]
else:
- backttl = 255 - req_answer[IP].ttl + 1
- print(" <<< answer:"
- + " ip.src: " + req_answer[IP].src
- + " ip.ttl: " + str(req_answer[IP].ttl)
- + " back-ttl: " + str(backttl))
- answer_summary = req_answer.summary()
- print(" " + answer_summary)
- print("· - · · · rtt: " + str(elapsed_ms) + "ms · · · - · ")
- answer_summary += " . - - . - . " + summary_postfix
- return req_answer[IP].src, elapsed_ms, len(req_answer), req_answer[IP].ttl, answer_summary
+ return None, ""
+ return desirable_packet, summary_postfix
+ else:
+ desirable_packet = request_and_answers[0]
+ return desirable_packet, ""
+
+
+def guess_back_ttl(current_ttl, ttl):
+ backttl = 0
+ if ttl <= 20:
+ backttl = int((current_ttl - ttl) / 2) + 1
+ elif ttl <= 64:
+ backttl = 64 - ttl + 1
+ elif ttl <= 128:
+ backttl = 128 - ttl + 1
else:
- print(" *** no response *** ")
- print("· - · · · rtt: " + str(elapsed_ms) +
- "ms · · · · · · · · timeout ")
- return "***", elapsed_ms, 0, 0, "*"
+ backttl = 255 - ttl + 1
+ return backttl
+
+
+def parse_packet(answered, unanswered, current_ttl, elapsed_ms, do_tcphandshake):
+ if answered is not None and len(answered) != 0:
+ request_and_answer, summary_postfix = choose_desirable_packet(
+ answered, do_tcphandshake)
+ if request_and_answer is not None and len(answered) != 0:
+ req_answer = request_and_answer[1]
+ packet_send_time = request_and_answer[0].sent_time
+ packet_receive_time = req_answer.time
+ packet_elapsed_ms = float(
+ format(abs((packet_receive_time - packet_send_time) * 1000), '.3f'))
+ if packet_elapsed_ms > 0:
+ elapsed_ms = packet_elapsed_ms
+ backttl = guess_back_ttl(current_ttl, req_answer[IP].ttl)
+ print(" <<< answer:"
+ + " ip.src: " + req_answer[IP].src
+ + " ip.ttl: " + str(req_answer[IP].ttl)
+ + " back-ttl: " + str(backttl))
+ answer_summary = req_answer.summary()
+ print(" " + answer_summary)
+ print("· - · · · rtt: " + str(elapsed_ms) + "ms · · · - · ")
+ if len(summary_postfix) != 0:
+ answer_summary += " . - - . - . " + summary_postfix
+ return req_answer[IP].src, elapsed_ms, len(req_answer), req_answer[IP].ttl, answer_summary, answered, unanswered
+ # else for both:
+ print(" *** no response *** ")
+ print("· - · · · rtt: " + str(elapsed_ms) +
+ "ms · · · · · · · · timeout ")
+ return "***", elapsed_ms, 0, 0, "*", answered, unanswered
# ephemeral_port_reserve() function is based on https://github.com/Yelp/ephemeral-port-reserve
@@ -260,37 +302,10 @@ def send_packet(request_packet, request_ip, current_ttl, timeout, do_tcphandshak
return request_and_answers, unanswered
if do_tcphandshake:
sleep(timeout) # double sleep ( ̄o ̄) . z Z. maybe we should wait more
- if len(request_and_answers) == 0:
- return parse_packet(None, current_ttl, elapsed_ms)
- else:
- if do_tcphandshake and not request_and_answers[0][1].haslayer(ICMP):
- # request_and_answers.summary()
- summary_postfix = str(request_and_answers.summary)
- print(" " + summary_postfix)
- if len(request_and_answers) > 1:
- if request_and_answers[0][1][TCP].flags == "A" and request_and_answers[1][1].haslayer(ICMP):
- # todo xhdix: flag the first hop as a middlebox
- print(
- "--.- .-. -- the first answer is from a middlebox (╯°□°)╯︵ ┻━┻")
- return parse_packet(request_and_answers[1], current_ttl, elapsed_ms, summary_postfix)
- # todo xhdix: flag as middlebox if [0][1][TCP].flags in ["R", "RA", "F", "FA"] and [1][1].haslayer(ICMP
- elif request_and_answers[0][1][TCP].flags in ["R", "RA", "F", "FA"]:
- return parse_packet(request_and_answers[0], current_ttl, elapsed_ms, summary_postfix)
- else:
- return parse_packet(request_and_answers[1], current_ttl, elapsed_ms, summary_postfix)
- # we need hello from server, not ACK from middlebox
- elif request_and_answers[0][1][TCP].flags != "A":
- return parse_packet(request_and_answers[0], current_ttl, elapsed_ms, summary_postfix)
- # here we just want to have a correct path, so we ignore the lack of ACK before Server Hello in some weird networks
- elif request_and_answers[0][1][TCP].flags == "A" and request_and_answers[0][1].haslayer(Raw):
- return parse_packet(request_and_answers[0], current_ttl, elapsed_ms, summary_postfix)
- else:
- return parse_packet(None, current_ttl, elapsed_ms, summary_postfix)
- else:
- return parse_packet(request_and_answers[0], current_ttl, elapsed_ms)
+ return parse_packet(request_and_answers, unanswered, current_ttl, elapsed_ms, do_tcphandshake)
-def already_reached_destination(previous_node_id, current_node_ip):
+def already_reached_destination_int(previous_node_id, current_node_ip):
if previous_node_id == current_node_ip:
return True
else:
@@ -312,7 +327,7 @@ def are_equal(original_list, result_list):
return True
-def initialize_first_nodes(request_ips):
+def initialize_first_nodes_json(request_ips):
nodes = []
for _ in request_ips:
nodes.append(SOURCE_IP_ADDRESS)
@@ -324,7 +339,7 @@ def initialize_first_nodes(request_ips):
def initialize_json_first_nodes(
request_ips, annotation_1, annotation_2, packet_1_proto, packet_2_proto,
- packet_1_port, packet_2_port, packet_1_size, packet_2_size):
+ packet_1_port, packet_2_port, packet_1_size, packet_2_size, paris_id):
# source_address = get_if_addr(conf.iface) #todo: xhdix
source_address = SOURCE_IP_ADDRESS
start_time = int(datetime.utcnow().timestamp())
@@ -333,7 +348,7 @@ def initialize_json_first_nodes(
traceroute_data(
dst_addr=request_ip, annotation=annotation_1,
src_addr=source_address, proto=packet_1_proto, port=packet_1_port,
- timestamp=start_time, size=packet_1_size
+ timestamp=start_time, paris_id=paris_id, size=packet_1_size
)
)
if have_2_packet:
@@ -341,7 +356,7 @@ def initialize_json_first_nodes(
traceroute_data(
dst_addr=request_ip, annotation=annotation_2,
src_addr=source_address, proto=packet_2_proto, port=packet_2_port,
- timestamp=start_time, size=packet_2_size
+ timestamp=start_time, paris_id=paris_id, size=packet_2_size
)
)
@@ -384,19 +399,20 @@ def save_measurement_data(
end_time = int(datetime.utcnow().timestamp())
measurement_data_json = []
ip_steps = 0
+ measurement_data_save = deepcopy(measurement_data)
while ip_steps < len(request_ips):
- measurement_data[0][ip_steps].set_endtime(end_time)
+ measurement_data_save[0][ip_steps].set_endtime(end_time)
if not continue_to_max_ttl:
- measurement_data[0][ip_steps].clean_extra_result()
- measurement_data_json.append(measurement_data[0][ip_steps])
+ measurement_data_save[0][ip_steps].clean_extra_result()
+ measurement_data_json.append(measurement_data_save[0][ip_steps])
if have_2_packet:
- measurement_data[1][ip_steps].set_endtime(end_time)
+ measurement_data_save[1][ip_steps].set_endtime(end_time)
if not continue_to_max_ttl:
- measurement_data[1][ip_steps].clean_extra_result()
- measurement_data_json.append(measurement_data[1][ip_steps])
+ measurement_data_save[1][ip_steps].clean_extra_result()
+ measurement_data_json.append(measurement_data_save[1][ip_steps])
ip_steps += 1
data_path = output_dir + measurement_name + ".json"
- with open(data_path, "a") as jsonfile:
+ with open(data_path, "w") as jsonfile:
jsonfile.write(json.dumps(measurement_data_json,
default=lambda o: o.__dict__, indent=4))
print("saved: " + data_path)
@@ -503,11 +519,16 @@ def trace_route(
repeat_all_steps = 0
p1_proto, p2_proto, p1_port, p2_port, p1_size, p2_size = get_packets_info(
request_packets)
+ paris_id = 0
+ if trace_with_retransmission:
+ paris_id = repeat_requests
+ elif trace_retransmission:
+ paris_id = -1
initialize_json_first_nodes(
request_ips=request_ips, annotation_1=annotation_1, annotation_2=annotation_2,
packet_1_proto=p1_proto, packet_2_proto=p2_proto,
packet_1_port=p1_port, packet_2_port=p2_port,
- packet_1_size=p1_size, packet_2_size=p2_size
+ packet_1_size=p1_size, packet_2_size=p2_size, paris_id=paris_id
)
print("- · - · - - · - · - - · - · - - · - · -")
while repeat_all_steps < repeat_requests:
@@ -517,7 +538,7 @@ def trace_route(
request_packets_for_rexmit = generate_packets_for_each_ip(
request_packets, request_ips, do_tcphandshake)
trace_retransmission = True
- previous_node_ids = initialize_first_nodes(request_ips)
+ previous_node_ids = initialize_first_nodes_json(request_ips)
for current_ttl in range(1, max_ttl + 1):
if not continue_to_max_ttl and are_equal(request_ips, previous_node_ids):
ip_steps = 0
@@ -525,7 +546,7 @@ def trace_route(
while ip_steps < len(request_ips):
# to avoid confusing the order of results when we have already reached our destination
measurement_data[access_block_steps][ip_steps].add_hop(
- current_ttl, "", 0, 0, 0, ""
+ current_ttl, "", 0, 0, 0, "", None, None
)
ip_steps += 1
if have_2_packet and ip_steps == len(request_ips) and access_block_steps == 0:
@@ -540,7 +561,7 @@ def trace_route(
print(" · · · - - - · · · · · · - - - · · · · · · - - - · · · ")
while ip_steps < len(request_ips):
sleep_time = SLEEP_TIME
- not_yet_destination = not (already_reached_destination(
+ not_yet_destination = not (already_reached_destination_int(
previous_node_ids[access_block_steps][ip_steps],
request_ips[ip_steps]))
current_packet = None
@@ -550,26 +571,26 @@ def trace_route(
current_packet = request_packets[access_block_steps]
if not continue_to_max_ttl:
if not_yet_destination:
- answer_ip, elapsed_ms, packet_size, req_answer_ttl, answer_summary = send_packet(
+ answer_ip, elapsed_ms, packet_size, req_answer_ttl, answer_summary, answered, unanswered = send_packet(
current_packet, request_ips[ip_steps],
current_ttl, timeout, do_tcphandshake[access_block_steps],
trace_retransmission, False)
measurement_data[access_block_steps][ip_steps].add_hop(
- current_ttl, answer_ip, elapsed_ms, packet_size, req_answer_ttl, answer_summary
+ current_ttl, answer_ip, elapsed_ms, packet_size, req_answer_ttl, answer_summary, answered, unanswered
)
else:
sleep_time = 0
# to avoid confusing the order of results when we have already reached our destination
measurement_data[access_block_steps][ip_steps].add_hop(
- current_ttl, "", 0, 0, 0, ""
+ current_ttl, "", 0, 0, 0, "", None, None
)
else:
- answer_ip, elapsed_ms, packet_size, req_answer_ttl, answer_summary = send_packet(
+ answer_ip, elapsed_ms, packet_size, req_answer_ttl, answer_summary, answered, unanswered = send_packet(
current_packet, request_ips[ip_steps],
current_ttl, timeout, do_tcphandshake[access_block_steps],
trace_retransmission, False)
measurement_data[access_block_steps][ip_steps].add_hop(
- current_ttl, answer_ip, elapsed_ms, packet_size, req_answer_ttl, answer_summary
+ current_ttl, answer_ip, elapsed_ms, packet_size, req_answer_ttl, answer_summary, answered, unanswered
)
if not_yet_destination:
if answer_ip == "***":
diff --git a/utils/traceroute_struct.py b/utils/traceroute_struct.py
index ff56371..cfe7566 100755
--- a/utils/traceroute_struct.py
+++ b/utils/traceroute_struct.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python3
import json
+import utils.convert_packetlist
+
class traceroute_data:
def __init__(
@@ -29,7 +31,7 @@ def __init__(
self.timestamp = timestamp
self.ttr = ttr
- def add_hop(self, hop, from_ip, rtt, size, ttl, answer_summary):
+ def add_hop(self, hop, from_ip, rtt, size, ttl, answer_summary, answered, unanswered):
if len(self.result) < hop:
(self.result).append({"hop": hop, "result": []})
if rtt == 0:
@@ -37,16 +39,22 @@ def add_hop(self, hop, from_ip, rtt, size, ttl, answer_summary):
"x": "-",
})
elif from_ip == "***":
+ packetlist = utils.convert_packetlist.packetlist2json(
+ answered, unanswered)
self.result[hop - 1]["result"].append({
"x": "*",
+ "packets": packetlist,
})
else:
+ packetlist = utils.convert_packetlist.packetlist2json(
+ answered, unanswered)
self.result[hop - 1]["result"].append({
"from": from_ip,
"rtt": rtt,
"size": size,
"ttl": ttl,
- "summary": answer_summary
+ "summary": answer_summary,
+ "packets": packetlist,
})
def set_endtime(self, endtime):
diff --git a/utils/vis.py b/utils/vis.py
index 49adc7d..6ba8fcc 100755
--- a/utils/vis.py
+++ b/utils/vis.py
@@ -11,11 +11,18 @@
WINDOWS_COLOR = "blue"
LINUX_COLOR = "purple"
MIDDLEBOX_COLOR = "red"
+PEP_COLOR = "green"
+NAT_COLOR = "dodgerblue"
NO_RESPONSE_COLOR = "gray"
-DEVICE_OS_NAME = {
- ROUTER_COLOR: "Router", WINDOWS_COLOR: "Windows", LINUX_COLOR: "Linux",
- MIDDLEBOX_COLOR: "Middlebox", NO_RESPONSE_COLOR: "unknown"
-}
+
+ROUTER_NAME = "Router"
+WINDOWS_NAME = "Windows"
+LINUX_NAME = "Linux"
+MIDDLEBOX_NAME = "Middlebox"
+PEP_NAME = "PEP"
+NAT_NAME = "NAT"
+NO_RESPONSE_NAME = "unknown"
+
REQUEST_COLORS = [
"DarkTurquoise", "HotPink", "LimeGreen", "Red", "DodgerBlue", "Orange",
"MediumSlateBlue", "DarkGoldenrod", "Green", "Brown", "YellowGreen", "Magenta"
@@ -28,22 +35,96 @@
multi_directed_graph = nx.MultiDiGraph()
+def get_packet_type(packet_obj):
+ if len(packet_obj.keys()) > 1:
+ return list(packet_obj.keys())[1]
+
+
+def detect_nat_pep_middlebox(sent, received):
+ is_nat = False
+ is_middlebox = False
+ is_pep = False
+ packet_type = ""
+ tcpflag = ""
+ if not 'ICMP' in received[0].keys():
+ # sent packet 1 = {}
+ # received packets = [
+ # {received packet 1},
+ # {received packet 2},
+ # {received packet 3}
+ # ]
+ if 'TCP' in received[0].keys():
+ if len(received) > 1:
+ if received[0]['TCP']['flags'] == "A" and 'ICMP' in received[1].keys():
+ is_pep = True
+ packet_type = get_packet_type(received[1])
+ if received[1]['IP in ICMP']['id'] != sent['IP']['id']:
+ is_nat = True
+ elif received[0]['TCP']['flags'] in ["R", "RA", "F", "FA"] and 'ICMP' in received[1].keys():
+ is_pep = True
+ is_middlebox = True
+ packet_type = get_packet_type(received[1])
+ if received[1]['IP in ICMP']['id'] != sent['IP']['id']:
+ is_nat = True
+ elif received[0]['TCP']['flags'] in ["R", "RA", "F", "FA"]:
+ packet_type = get_packet_type(received[0])
+ tcpflag = received[0]['TCP']['flags']
+ if received[0]['IP']['id'] == sent['IP']['id']:
+ is_middlebox = True
+ else:
+ packet_type = get_packet_type(received[1])
+ if packet_type == 'TCP':
+ tcpflag = received[1]['TCP']['flags']
+ if received[1]['IP']['id'] == sent['IP']['id']:
+ is_middlebox = True
+ # we need hello from server, not ACK from middlebox
+ elif received[0]['TCP']['flags'] != "A":
+ packet_type = get_packet_type(received[0])
+ tcpflag = received[0]['TCP']['flags']
+ if received[0]['IP']['id'] == sent['IP']['id']:
+ is_middlebox = True
+ # here we just want to have a correct path, so we ignore the lack of ACK before Server Hello in some weird networks
+ elif received[0]['TCP']['flags'] == "A" and received[0].haslayer('Raw'):
+ packet_type = get_packet_type(received[0])
+ tcpflag = received[0]['TCP']['flags']
+ if received[0]['IP']['id'] == sent['IP']['id']:
+ is_middlebox = True
+ else:
+ is_pep = True
+ else:
+ packet_type = get_packet_type(received[0])
+ if received[0]['IP']['id'] == sent['IP']['id']:
+ is_middlebox = True
+ else:
+ packet_type = 'ICMP'
+ if received[0]['IP in ICMP']['id'] != sent['IP']['id']:
+ is_nat = True
+ return is_nat, is_middlebox, is_pep, packet_type, tcpflag
+
+
def parse_ttl(response_ttl, current_ttl):
device_color = ""
backttl = 0
+ is_middlebox = False
+ device_os_name = ""
if response_ttl <= 20:
backttl = int((current_ttl - response_ttl) / 2) + 1
device_color = MIDDLEBOX_COLOR
+ is_middlebox = True
+ device_os_name = MIDDLEBOX_NAME
elif response_ttl <= 64:
backttl = 64 - response_ttl + 1
device_color = LINUX_COLOR
+ device_os_name = LINUX_NAME
elif response_ttl <= 128:
backttl = 128 - response_ttl + 1
device_color = WINDOWS_COLOR
+ device_os_name = WINDOWS_NAME
else:
backttl = 255 - response_ttl + 1
device_color = ROUTER_COLOR
- return backttl, device_color
+ device_os_name = ROUTER_NAME
+ return backttl, device_color, device_os_name, is_middlebox
def visualize(previous_node_id, current_node_id,
@@ -58,9 +139,20 @@ def visualize(previous_node_id, current_node_id,
color=requset_color, title=current_edge_title)
+def tooltips_append_lines(is_nat, is_middlebox, is_pep, packet_type, tcpflag):
+ append_line = ''
+ if packet_type == "TCP":
+ append_line = "
response TCP flag: " + tcpflag
+ return ("
NAT: " + str(is_nat)
+ + "
Middlebox: " + str(is_middlebox)
+ + "
PEP: " + str(is_pep)
+ + "
response packet: " + packet_type
+ + append_line)
+
+
def styled_tooltips(
- current_request_colors, current_ttl_str, backttl, request_ip, elapsed_ms,
- packet_size, repeat_all_steps, device_os_name, annotation):
+ current_request_color, current_ttl_str, backttl, request_ip, elapsed_ms,
+ packet_size, repeat_step, device_os_name, append_lines, annotation):
time_size = "*"
elapsed_ms_str = "*"
packet_size_str = "*"
@@ -69,30 +161,37 @@ def styled_tooltips(
if elapsed_ms != "*":
elapsed_ms_str = str(format(elapsed_ms, '.3f')) + "ms"
time_size = str(format(elapsed_ms/packet_size, '.3f')) + "ms/B"
- return ("
TTL: " + current_ttl_str - + "") - - -def already_reached_destination(previous_node_id, current_node_ip): - if previous_node_id in { - str(current_node_ip), - ("middlebox" + str(current_node_ip) + "x")}: + tooltips_str = "
Back-TTL: " + backttl - + "
Request to: " + request_ip - + "
annotation: " + annotation - + "
Time: " + elapsed_ms_str - + "
Size: " + packet_size_str - + "
Time/Size: " + time_size - + "
OS: " + device_os_name - + "
Repeat step: " + str(repeat_all_steps) + "
TTL: " + current_ttl_str + tooltips_str += "" + return tooltips_str + + +def already_reached_destination_str(previous_node_id, dst_addr_id): + if dst_addr_id in previous_node_id: return True else: return False -def initialize_first_nodes(src_addr): +def initialize_detected(length_all): + nodes = [] + for _ in range(length_all): + nodes.append({"is_nat": False, "is_middlebox": False, "is_pep": False}) + return nodes + + +def initialize_first_nodes_nx(src_addr, length_all): nodes = [] - for _ in range(10): + for _ in range(length_all): nodes.append(str(src_addr)) return nodes @@ -118,29 +217,32 @@ def vis(measurement_path, attach_jscss, edge_lable: str = "none"): all_measurements = json.load(json_file) measurement_steps = 0 src_addr = all_measurements[0]["src_addr"] - src_addr_id = str(int(ipaddress.IPv4Address(src_addr))) + src_addr_id = 'x' + str(int(ipaddress.IPv4Address(src_addr))) + 'x' multi_directed_graph.add_node( src_addr_id, label=src_addr, color="Chocolate", title="source address", shape="diamond") for measurement in all_measurements: - previous_node_ids = initialize_first_nodes(src_addr_id) dst_addr = measurement["dst_addr"] - dst_addr_id = str(int(ipaddress.IPv4Address(dst_addr))) + dst_addr_id = 'x' + str(int(ipaddress.IPv4Address(dst_addr))) + 'x' annotation = "-" if "annotation" in measurement.keys(): annotation = measurement["annotation"] all_results = measurement["result"] + results_repeat_length = len(all_results[0]["result"]) + previous_node_ids = initialize_first_nodes_nx( + src_addr_id, results_repeat_length) + already_detected = initialize_detected(results_repeat_length) for try_step in all_results: # will be up to 255 current_ttl = try_step["hop"] current_ttl_str = str(current_ttl) results = try_step["result"] repeat_steps = 0 skip_next = False - for result in results: # will be up to 3 + for result in results: if skip_next: skip_next = False continue - not_yet_destination = not (already_reached_destination( + not_yet_destination = not (already_reached_destination_str( previous_node_ids[repeat_steps], dst_addr_id)) if not_yet_destination: if "late" in result.keys(): @@ -154,6 +256,9 @@ def vis(measurement_path, attach_jscss, edge_lable: str = "none"): packet_size = "*" backttl = "*" device_color = NO_RESPONSE_COLOR + device_name = NO_RESPONSE_NAME + append_lines = "" + is_middlebox = False if 'x' in result.keys(): current_node_id = ( "unknown" + previous_node_ids[repeat_steps] + "x") @@ -161,7 +266,7 @@ def vis(measurement_path, attach_jscss, edge_lable: str = "none"): current_edge_label = "*" else: answer_ip = result["from"] - backttl, device_color = parse_ttl( + backttl, device_color, device_name, is_middlebox_ttl = parse_ttl( result["ttl"], current_ttl) if "rtt" in result.keys(): elapsed_ms = result["rtt"] @@ -170,26 +275,59 @@ def vis(measurement_path, attach_jscss, edge_lable: str = "none"): current_edge_label = format(elapsed_ms, '.3f') elif edge_lable == "backttl": current_edge_label = str(backttl) - current_node_id = str( - int(ipaddress.IPv4Address(answer_ip))) - if device_color == MIDDLEBOX_COLOR: - current_node_id = ( - "middlebox" + str(current_node_id) + "x") + current_node_id = 'x' + str( + int(ipaddress.IPv4Address(answer_ip))) + 'x' + if "packets" in result.keys(): + if "received" in result['packets'].keys(): + if len(result['packets']['received']) != 0: + is_nat, is_middlebox, is_pep, packet_type, tcpflag = detect_nat_pep_middlebox( + result['packets']['sent'], result['packets']['received'] + ) + if (is_middlebox_ttl or is_middlebox + ) and not already_detected[repeat_steps]["is_middlebox"]: + pass # we decide about it later + elif is_pep and not already_detected[repeat_steps]["is_pep"]: + device_color = PEP_COLOR + device_name = PEP_NAME + current_node_shape = "star" + already_detected[repeat_steps]["is_pep"] = True + current_node_id = "pep" + current_node_id + "x" + elif is_nat and not already_detected[repeat_steps]["is_nat"]: + device_color = NAT_COLOR + device_name = NAT_NAME + already_detected[repeat_steps]["is_nat"] = True + current_node_id = "nat" + current_node_id + "x" + append_lines = tooltips_append_lines( + is_nat, is_middlebox, is_pep, packet_type, tcpflag) + if (is_middlebox_ttl or is_middlebox): + already_detected[repeat_steps]["is_middlebox"] = True + if is_pep: + already_detected[repeat_steps]["is_pep"] = True + if is_nat: + already_detected[repeat_steps]["is_nat"] = True + if is_middlebox_ttl or is_middlebox: + current_node_id = "middlebox" + current_node_id + "x" current_node_shape = "star" + device_color = MIDDLEBOX_COLOR + device_name = MIDDLEBOX_NAME + already_detected[repeat_steps]["is_middlebox"] = True elif current_node_id == dst_addr_id: current_node_shape = "square" current_node_label = answer_ip - current_edge_title = str(backttl) packet_size = result["size"] + repeat_step_str = str(repeat_steps + 1) current_edge_title = styled_tooltips( - REQUEST_COLORS[measurement_steps], current_ttl_str, - str(backttl), dst_addr, elapsed_ms, packet_size, - (repeat_steps + 1), DEVICE_OS_NAME[device_color], - annotation + current_request_color=( + REQUEST_COLORS[measurement_steps]), + current_ttl_str=current_ttl_str, backttl=str(backttl), + request_ip=dst_addr, elapsed_ms=elapsed_ms, + packet_size=packet_size, repeat_step=repeat_step_str, + device_os_name=device_name, append_lines=append_lines, + annotation=annotation ) visualize( previous_node_ids[repeat_steps], current_node_id, - current_node_label, DEVICE_OS_NAME[device_color], device_color, + current_node_label, device_name, device_color, current_edge_title, REQUEST_COLORS[measurement_steps], current_edge_label, current_node_shape )
Back-TTL: " + backttl + tooltips_str += "
Request to: " + request_ip + tooltips_str += "
annotation: " + annotation + tooltips_str += "
Time: " + elapsed_ms_str + tooltips_str += "
Size: " + packet_size_str + tooltips_str += "
Time/Size: " + time_size + tooltips_str += "
OS: " + device_os_name + tooltips_str += append_lines + tooltips_str += "
Repeat step: " + repeat_step + "