예제 #1
0
    def setUp(self):
        self.ip_ver = 4
        # UDP
        self.l4_proto = 17

        self.ip_daddr = "1.1.1.1"
        self.ip_saddr = traceflow.socket_handler.get_egress_ip(self.ip_daddr)

        self.udp_src_port = 35000
        self.udp_dst_port = 53

        self.ttl = 3

        # packet_encode(ip_ver, ip_daddr, udp_src_port, udp_dst_port, ttl, l4_proto, ip_id, **kwargs)
        self.test_encode_class_instance = traceflow.packet_encode(
            self.ip_ver,
            self.ip_daddr,
            self.udp_src_port,
            self.udp_dst_port,
            self.ttl,
            self.l4_proto,
            None,
        )
예제 #2
0
def main():
    # ha ha ha
    args = get_help()

    # CLI arguments set here.
    daddr = socket.gethostbyname(args.destination)
    print(f"Resolved {args.destination} to {daddr}")
    TOT_RUNS = args.paths
    DST_PORT = args.dstport
    SRC_PORT = args.srcport
    MAX_TTL = args.ttl

    if args.debug:
        logger = logging.getLogger()
        logger.setLevel(logging.DEBUG)

    # Setup the background thread listener here. Note that we need to pass daddr so we can snag the dst port unreachable
    # ICMP message.
    listener = traceflow.socket_listener(daddr)

    run_ids = dict()

    # Keep track of which path we're looking to enumerate
    for path in range(1, TOT_RUNS + 1):
        port = SRC_PORT + path
        run_ids[path] = port
        print(f"Looking at Path ID {path} (src port:{port} , dst port:{DST_PORT})")
        for ttl in list(range(1, MAX_TTL)):
            # Here we will combine the path we're after with the TTL, and use this to track the returning ICMP payload
            ip_id = ints_to_ipid(path, ttl)
            # TODO: Hide this behind a class
            packet = {
                "ip_ver": 4,
                "ip_daddr": daddr,
                "udp_src_port": port,
                "udp_dst_port": DST_PORT,
                "ttl": ttl,
                "l4_proto": 17,
                "ip_id": ip_id,
            }
            # Create our packet here.
            i = traceflow.packet_encode(**packet)
            # TODO: Maybe refactor to hide these behind a single function, to be v4/v6 agnostic
            # Combine the IPv4 and UDP headers here
            probe = i.encode_ipv4_header() + i.encode_ipv4_udp_packet()

            s = traceflow.socket_handler(packet["ip_daddr"])
            _ = s.send_ipv4(probe)
            time.sleep(args.wait)
            # Since we are not running a sequential trace, we should check in to see if we've gotten a reply from the destination yet
            packets = listener.get_packets_by_pathid(path)
            end = [i for i in packets if i["ip_saddr"] == daddr]
            if len(end) > 0:
                logging.debug(f"Breaking trace to {daddr} at TTL {ttl}")
                break

    # We should get all the packets the listener received here
    rx_icmp = listener.get_all_packets()
    traces = dict()

    for i in rx_icmp:
        icmp_packet = traceflow.packet_decode.decode_icmp(rx_icmp[i]["payload"])
        ipv4_packet = traceflow.packet_decode.decode_ipv4_header(icmp_packet["payload"])
        (path, ttl) = ipid_to_ints(ipv4_packet["ip_id"])
        if path not in traces.keys():
            traces[path] = dict()
        if ttl not in traces[path].keys():
            traces[path][ttl] = rx_icmp[i]["ip_saddr"]
        logging.debug("Run: %s TTL: %s" % (path, ttl))

    # Here we will fill in missing probes with a *
    # We should also trim any duplicate replies from daddr
    # and also fill in an x to pad up unequal path lengths
    path_max = max([max(traces[i].keys()) for i in traces.keys()])
    for path in traces.keys():
        # Remove any duplicate answers from daddr
        dup_keys = [i for i in traces[path] if traces[path][i] == daddr]
        while len(dup_keys) > 1:
            logging.debug("dup keys: %s" % dup_keys)
            traces[path].pop(max(dup_keys))
            dup_keys = [i for i in traces[path] if traces[path][i] == daddr]
        # Now we fill in * for any missing hops
        last_ttl = sorted(traces[path])[-1]
        for ttl in list(range(1, last_ttl + 1)):
            if ttl not in traces[path]:
                logging.debug(f"Missing TTL({ttl}) for path {path}")
                traces[path][ttl] = "*"
        # Now we should handle unequal length paths
        path_length = len(traces[path])
        if path_length < path_max:
            for i in range(path_length, path_max + 1):
                if i not in traces[path].keys():
                    logging.debug(f"Insert fake hop at {i} for path {path}")
                    traces[path][i] = "x"

    if args.format.lower() == "vert":
        # Print horizontal results
        traceflow.printer.print_vertical(traces)
    if args.format.lower() == "horiz":
        # print vertical results
        traceflow.printer.print_horizontal(traces)
    if args.format.lower() == "viz":
        # Experimental vis.js / browser based visualisation
        traceflow.printer.start_viz(traces)
    exit(0)
예제 #3
0
class TestPacketEncode(unittest.TestCase):
    packet = dict()
    packet["ip_ver"] = 4
    # UDP
    packet["l4_proto"] = 17

    packet["ip_daddr"] = "1.1.1.1"
    packet["ip_saddr"] = traceflow.socket_handler.get_egress_ip(
        packet["ip_daddr"])

    packet["udp_src_port"] = 35000
    packet["udp_dst_port"] = 53

    packet["ttl"] = 3

    global i
    i = traceflow.packet_encode(**packet)

    # IPv4 Tests here
    def test_decode_ipv4(self):
        ipv4_hdr = i.encode_ipv4_header()
        self.assertIsInstance(ipv4_hdr, bytes)

    # packet["ttl"] = 3
    def test_encode_ipv4_ttl(self):
        # TTL is 9th byte
        ttl = i.encode_ipv4_header()[8]
        self.assertEqual(ttl, 3)

    # packet["l4_proto"] = 17
    def test_encode_ipv4_l4_proto(self):
        # Proto is 9th byte
        l4_proto = i.encode_ipv4_header()[9]
        self.assertEqual(l4_proto, 17)

    # Checksum is 9059 - pre-computed.
    def test_encode_ipv4_l4_proto(self):
        # Checksum is 10th and 11th bytes
        l4_proto = i.encode_ipv4_header()[10:12]
        self.assertEqual(l4_proto, struct.pack("H", 9059))

    # packet["ip_saddr"] = traceflow.socket_handler.get_egress_ip(packet["ip_daddr"])
    # This was pre-computed to 192.168.0.31 for this packet.
    def test_encode_ipv4_l4_proto(self):
        # Checksum is 10th and 11th bytes
        ip_saddr = i.encode_ipv4_header()[12:16]
        self.assertEqual(ip_saddr, socket.inet_aton("192.168.0.31"))

    # packet["ip_daddr"] = "1.1.1.1"
    def test_encode_ipv4_l4_proto(self):
        # Checksum is 10th and 11th bytes
        ip_saddr = i.encode_ipv4_header()[16:20]
        self.assertEqual(ip_saddr, socket.inet_aton("1.1.1.1"))

    # UDP Tests here
    def test_decode_udp(self):
        udp_hdr = i.encode_ipv4_udp_packet()
        self.assertIsInstance(udp_hdr, bytes)

    # packet["udp_src_port"] = 35000
    def test_encode_udp_src(self):
        # src is 1st word
        udp_src_port = i.encode_ipv4_udp_packet()[0:2]
        self.assertEqual(udp_src_port, struct.pack("!H", 35000))

    # packet["udp_dst_port"] = 53
    def test_encode_udp_dst(self):
        # dst is 2nd word
        udp_dst_port = i.encode_ipv4_udp_packet()[2:4]
        self.assertEqual(udp_dst_port, struct.pack("!H", 53))

    # UDP Packet Length: 18, computed.
    def test_encode_udp_len(self):
        # Len is 3rd word
        udp_len = i.encode_ipv4_udp_packet()[4:6]
        self.assertEqual(udp_len, struct.pack("!H", 18))
예제 #4
0
def main():
    # ha ha ha
    args = helpers.get_help()

    # CLI arguments set here.
    try:
        daddr = socket.gethostbyname(args.destination)
    except socket.gaierror as e:
        if "Name or service not known" in str(e):
            print(f"Error, could not resolve {args.destination}, exiting")
            exit(1)
        else:
            print(f"General error resolving {args.destination}")
            print("exiting")
            exit(1)
    print(f"Resolved {args.destination} to {daddr}")
    TOT_RUNS = args.paths
    DST_PORT = args.dstport
    SRC_PORT = args.srcport
    MAX_TTL = args.ttl
    BIND_IP = args.bind

    if args.debug:
        logger = logging.getLogger()
        logger.setLevel(logging.DEBUG)
    if TOT_RUNS > 255:
        print(
            f"Max paths we can probe is 255. Setting --paths to 255 and continuing"
        )
        TOT_RUNS = 255
    # Setup the background thread listener here. Note that we need to pass daddr so we can snag the dst port unreachable
    # ICMP message.
    listener = traceflow.socket_listener(daddr)

    run_ids = dict()

    # Keep track of which path we're looking to enumerate
    for path in range(1, TOT_RUNS + 1):
        port = SRC_PORT + path
        run_ids[path] = port
        print(
            f"Looking at Path ID {path} (src port:{port} , dst port:{DST_PORT})"
        )
        for ttl in list(range(1, MAX_TTL)):
            # Here we will combine the path we're after with the TTL, and use this to track the returning ICMP payload
            ip_id = helpers.ints_to_ipid(path, ttl)
            # TODO: Hide this behind a class
            ip_ver = 4
            ip_daddr = daddr
            udp_src_port = port
            udp_dst_port = DST_PORT
            ttl = ttl
            l4_proto = 17
            ip_id = ip_id
            additional_params = {"ip_tos": None, "ip_frag_off": None}
            # Create our packet here.
            i = traceflow.packet_encode(
                ip_ver,
                ip_daddr,
                udp_src_port,
                udp_dst_port,
                ttl,
                l4_proto,
                ip_id,
                **additional_params,
            )
            # TODO: Maybe refactor to hide these behind a single function, to be v4/v6 agnostic
            # Combine the IPv4 and UDP headers here
            probe = i.ipv4_packet + i.udp_packet

            s = traceflow.socket_handler(ip_daddr)
            _ = s.send_ipv4(probe)
            time.sleep(args.wait)
            # Since we are not running a sequential trace, we should check in to see if we've gotten a reply from the destination yet
            packets = listener.get_packets_by_pathid(path)
            end = [i for i in packets if i["ip_saddr"] == daddr]
            if len(end) > 0:
                logging.debug(f"Breaking trace to {daddr} at TTL {ttl}")
                break

    # We should get all the packets the listener received here
    rx_icmp = listener.get_all_packets()
    if len(rx_icmp) == 0:
        logging.debug(f"rx_icmp is  {len(rx_icmp)}")
        print(f"Did not receive any TTL expired ICMP packets. Exiting")
        exit(1)
    traces = dict()

    # For each packet the listener got, loop across the ICMP message and see what the TTL/Path combo is.
    # Then add them to the dict traces as: traces[path][ttl]
    for i in rx_icmp:
        icmp_packet = traceflow.packet_decode.decode_icmp(
            rx_icmp[i]["payload"])
        ipv4_packet = traceflow.packet_decode.decode_ipv4_header(
            icmp_packet["payload"])
        (path, ttl) = helpers.ipid_to_ints(ipv4_packet["ip_id"])
        if path not in traces.keys():
            traces[path] = dict()
        if ttl not in traces[path].keys():
            traces[path][ttl] = rx_icmp[i]["ip_saddr"]
        logging.debug("Run: %s TTL: %s" % (path, ttl))

    # Here we will fill in missing probes with a *
    # We should also trim any duplicate replies from daddr
    # and also fill in an x to pad up unequal path lengths
    traces = helpers.remove_duplicates(traces, daddr)
    path_max = max([max(traces[i].keys()) for i in traces.keys()])
    for path in traces.keys():
        # Now we fill in * for any missing hops
        last_ttl = sorted(traces[path])[-1]
        for ttl in list(range(1, last_ttl + 1)):
            if ttl not in traces[path]:
                logging.debug(f"Missing TTL({ttl}) for path {path}")
                traces[path][ttl] = "*"
        # Now we should handle unequal length paths
        path_length = len(traces[path])
        if path_length < path_max:
            for i in range(path_length, path_max + 1):
                if i not in traces[path].keys():
                    logging.debug(f"Insert fake hop at {i} for path {path}")
                    traces[path][i] = "x"

    if args.dedup:
        traces = helpers.remove_duplicate_paths(traces)
    if args.format.lower() == "vert":
        # Print horizontal results
        traceflow.printer.print_vertical(traces)
    if args.format.lower() == "horiz":
        # print vertical results
        traceflow.printer.print_horizontal(traces)
    if args.format.lower() == "viz":
        # Experimental vis.js / browser based visualisation
        traceflow.printer.start_viz(traces, BIND_IP)
    exit(0)
예제 #5
0
def compute_traces(daddr,
                   tot_runs=4,
                   dst_port=33452,
                   src_port=33452,
                   max_ttl=64,
                   to_wait=0.1):
    # Setup the background thread listener here.
    # Note that we need to pass daddr
    # so we can snag the dst port unreachable ICMP message.
    listener = traceflow.socket_listener(daddr)

    run_ids = dict()

    # Keep track of which path we're looking to enumerate
    for path in range(1, tot_runs + 1):
        port = src_port + path
        run_ids[path] = port
        print(
            f"Looking at Path ID {path} (src port:{port} , dst port:{dst_port})"
        )
        for ttl in list(range(1, max_ttl)):
            # Here we will combine the path we're after with the TTL,
            # and use this to track the returning ICMP payload
            ip_id = helpers.ints_to_ipid(path, ttl)
            # TODO: Hide this behind a class
            ip_ver = 4
            ip_daddr = daddr
            udp_src_port = port
            udp_dst_port = dst_port
            ttl = ttl
            l4_proto = 17
            ip_id = ip_id
            additional_params = {"ip_tos": None, "ip_frag_off": None}
            # Create our packet here.
            i = traceflow.packet_encode(
                ip_ver,
                ip_daddr,
                udp_src_port,
                udp_dst_port,
                ttl,
                l4_proto,
                ip_id,
                **additional_params,
            )
            # TODO: Maybe refactor to hide these behind a single function, to be v4/v6 agnostic
            # Combine the IPv4 and UDP headers here
            probe = i.ipv4_packet + i.udp_packet

            s = traceflow.socket_handler(ip_daddr)
            _ = s.send_ipv4(probe)
            time.sleep(to_wait)
            # Since we are not running a sequential trace,
            # we should check in to see if we've gotten a reply from the destination yet
            packets = listener.get_packets_by_pathid(path)
            end = [i for i in packets if i["ip_saddr"] == daddr]
            if len(end) > 0:
                logging.debug(f"Breaking trace to {daddr} at TTL {ttl}")
                break

    # We should get all the packets the listener received here
    rx_icmp = listener.get_all_packets()
    if len(rx_icmp) == 0:
        logging.debug(f"rx_icmp is  {len(rx_icmp)}")
        print(f"Did not receive any TTL expired ICMP packets. Exiting")
        exit(1)
    traces = dict()

    # For each packet the listener got, loop across the ICMP message
    # and see what the TTL/Path combo is.
    # Then add them to the dict traces as: traces[path][ttl]
    for i in rx_icmp:
        icmp_packet = traceflow.packet_decode.decode_icmp(
            rx_icmp[i]["payload"])
        ipv4_packet = traceflow.packet_decode.decode_ipv4_header(
            icmp_packet["payload"])
        (path, ttl) = helpers.ipid_to_ints(ipv4_packet["ip_id"])
        if path not in traces.keys():
            traces[path] = dict()
        if ttl not in traces[path].keys():
            traces[path][ttl] = rx_icmp[i]["ip_saddr"]
        logging.debug("Run: %s TTL: %s" % (path, ttl))

    # Here we will fill in missing probes with a *
    # We should also trim any duplicate replies from daddr
    # and also fill in an x to pad up unequal path lengths
    traces = helpers.remove_duplicates(traces, daddr)
    path_max = max([max(traces[i].keys()) for i in traces.keys()])
    for path in traces.keys():
        # Now we fill in * for any missing hops
        last_ttl = sorted(traces[path])[-1]
        for ttl in list(range(1, last_ttl + 1)):
            if ttl not in traces[path]:
                logging.debug(f"Missing TTL({ttl}) for path {path}")
                traces[path][ttl] = "*"
        # Now we should handle unequal length paths
        path_length = len(traces[path])
        if path_length < path_max:
            for i in range(path_length, path_max + 1):
                if i not in traces[path].keys():
                    logging.debug(f"Insert fake hop at {i} for path {path}")
                    traces[path][i] = "x"

    return traces