예제 #1
0
파일: ps_dhcp.py 프로젝트: cclauss/PyTCP
 def __init__(self, raw_option=None, opt_srv_id=None):
     if raw_option:
         self.opt_code = raw_option[0]
         self.opt_len = raw_option[1]
         self.opt_srv_id = IPv4Address(raw_option[2:6])
     else:
         self.opt_code = DHCP_OPT_SRV_ID
         self.opt_len = DHCP_OPT_SRV_ID_LEN
         self.opt_srv_id = IPv4Address(opt_srv_id)
예제 #2
0
파일: ps_dhcp.py 프로젝트: cclauss/PyTCP
 def __init__(self, raw_option=None, opt_req_ip4_addr=None):
     if raw_option:
         self.opt_code = raw_option[0]
         self.opt_len = raw_option[1]
         self.opt_req_ip4_addr = IPv4Address(raw_option[2:6])
     else:
         self.opt_code = DHCP_OPT_REQ_IP4_ADDR
         self.opt_len = DHCP_OPT_REQ_IP4_ADDR_LEN
         self.opt_req_ip4_addr = IPv4Address(opt_req_ip4_addr)
예제 #3
0
파일: ps_dhcp.py 프로젝트: cclauss/PyTCP
 def __init__(self, raw_option=None, opt_dns=None):
     if raw_option:
         self.opt_code = raw_option[0]
         self.opt_len = raw_option[1]
         self.opt_dns = [IPv4Address(raw_option[_ : _ + 4]) for _ in range(2, 2 + self.opt_len, 4)]
     else:
         self.opt_code = DHCP_OPT_DNS
         self.opt_len = len(opt_dns) * 4
         self.opt_dns = [IPv4Address(_) for _ in opt_dns]
예제 #4
0
파일: ps_dhcp.py 프로젝트: cclauss/PyTCP
 def __init__(self, raw_option=None, opt_subnet_mask=None):
     if raw_option:
         self.opt_code = raw_option[0]
         self.opt_len = raw_option[1]
         self.opt_subnet_mask = IPv4Address(raw_option[2:6])
     else:
         self.opt_code = DHCP_OPT_SUBNET_MASK
         self.opt_len = DHCP_OPT_SUBNET_MASK_LEN
         self.opt_subnet_mask = IPv4Address(opt_subnet_mask)
예제 #5
0
 def __send_dhcp_packet(dhcp_packet_tx):
     socket.send_to(
         UdpMetadata(
             local_ip_address=IPv4Address("0.0.0.0"),
             local_port=68,
             remote_ip_address=IPv4Address("255.255.255.255"),
             remote_port=67,
             raw_data=dhcp_packet_tx.get_raw_packet(),
         ))
예제 #6
0
    def raw_header(self):
        """ Packet header in raw format """

        return struct.pack(
            "!HH BBH 6s 4s 6s 4s",
            self.arp_hrtype,
            self.arp_prtype,
            self.arp_hrlen,
            self.arp_prlen,
            self.arp_oper,
            bytes.fromhex(self.arp_sha.replace(":", "")),
            IPv4Address(self.arp_spa).packed,
            bytes.fromhex(self.arp_tha.replace(":", "")),
            IPv4Address(self.arp_tpa).packed,
        )
예제 #7
0
파일: ip_helper.py 프로젝트: isaacfgl/PyTCP
def ip_pick_version(ip_address):
    """ Return correct IPv6Address or IPv4Address based on address string provided """

    try:
        return IPv6Address(ip_address)
    except AddressValueError:
        return IPv4Address(ip_address)
예제 #8
0
def phrx_arp(self, ether_packet_rx, arp_packet_rx):
    """ Handle inbound ARP packets """

    # Validate ARP packet sanity
    if arp_packet_rx.sanity_check_failed:
        return

    self.logger.opt(ansi=True).info(f"<green>{arp_packet_rx.tracker}</green> - {arp_packet_rx}")

    if arp_packet_rx.arp_oper == ps_arp.ARP_OP_REQUEST:
        # Check if request contains our IP address in SPA field, this indicates IP address conflict
        if arp_packet_rx.arp_spa in self.ip4_unicast:
            self.logger.warning(f"IP ({arp_packet_rx.arp_spa}) conflict detected with host at {arp_packet_rx.arp_sha}")
            return

        # Check if the request is for one of our IP addresses, if so the craft ARP reply packet and send it out
        if arp_packet_rx.arp_tpa in self.ip4_unicast:
            self.phtx_arp(
                ether_src=self.mac_unicast,
                ether_dst=arp_packet_rx.arp_sha,
                arp_oper=ps_arp.ARP_OP_REPLY,
                arp_sha=self.mac_unicast,
                arp_spa=arp_packet_rx.arp_tpa,
                arp_tha=arp_packet_rx.arp_sha,
                arp_tpa=arp_packet_rx.arp_spa,
                echo_tracker=arp_packet_rx.tracker,
            )

            # Update ARP cache with the maping learned from the received ARP request that was destined to this stack
            if ARP_CACHE_UPDATE_FROM_DIRECT_REQUEST:
                self.logger.debug(f"Adding/refreshing ARP cache entry from direct request - {arp_packet_rx.arp_spa} -> {arp_packet_rx.arp_sha}")
                self.arp_cache.add_entry(arp_packet_rx.arp_spa, arp_packet_rx.arp_sha)

            return

    # Handle ARP reply
    elif arp_packet_rx.arp_oper == ps_arp.ARP_OP_REPLY:
        # Check for ARP reply that is response to our ARP probe, that indicates that IP address we trying to claim is in use
        if ether_packet_rx.ether_dst == self.mac_unicast:
            if (
                arp_packet_rx.arp_spa in [_.ip for _ in self.ip4_address_candidate]
                and arp_packet_rx.arp_tha == self.mac_unicast
                and arp_packet_rx.arp_tpa == IPv4Address("0.0.0.0")
            ):
                self.logger.warning(f"ARP Probe detected conflict for IP {arp_packet_rx.arp_spa} with host at {arp_packet_rx.arp_sha}")
                self.arp_probe_unicast_conflict.add(arp_packet_rx.arp_spa)
                return

        # Update ARP cache with maping received as direct ARP reply
        if ether_packet_rx.ether_dst == self.mac_unicast:
            self.logger.debug(f"Adding/refreshing ARP cache entry from direct reply - {arp_packet_rx.arp_spa} -> {arp_packet_rx.arp_sha}")
            self.arp_cache.add_entry(arp_packet_rx.arp_spa, arp_packet_rx.arp_sha)
            return

        # Update ARP cache with maping received as gratuitous ARP reply
        if ether_packet_rx.ether_dst == "ff:ff:ff:ff:ff:ff" and arp_packet_rx.arp_spa == arp_packet_rx.arp_tpa and ARP_CACHE_UPDATE_FROM_GRATUITOUS_REPLY:
            self.logger.debug(f"Adding/refreshing ARP cache entry from gratuitous reply - {arp_packet_rx.arp_spa} -> {arp_packet_rx.arp_sha}")
            self.arp_cache.add_entry(arp_packet_rx.arp_spa, arp_packet_rx.arp_sha)
            return
예제 #9
0
    def __init__(self, parent_packet=None, arp_sha=None, arp_spa=None, arp_tpa=None, arp_tha="00:00:00:00:00:00", arp_oper=ARP_OP_REQUEST, echo_tracker=None):
        """ Class constructor """

        self.logger = loguru.logger.bind(object_name="ps_arp.")
        self.sanity_check_failed = False

        # Packet parsing
        if parent_packet:
            self.tracker = parent_packet.tracker

            raw_packet = parent_packet.raw_data

            if not self.__pre_parse_sanity_check(raw_packet):
                self.sanity_check_failed = True
                return

            raw_header = raw_packet[:ARP_HEADER_LEN]

            self.arp_hrtype = struct.unpack("!H", raw_header[0:2])[0]
            self.arp_prtype = struct.unpack("!H", raw_header[2:4])[0]
            self.arp_hrlen = raw_header[4]
            self.arp_prlen = raw_header[5]
            self.arp_oper = struct.unpack("!H", raw_header[6:8])[0]
            self.arp_sha = ":".join([f"{_:0>2x}" for _ in raw_header[8:14]])
            self.arp_spa = IPv4Address(raw_header[14:18])
            self.arp_tha = ":".join([f"{_:0>2x}" for _ in raw_header[18:24]])
            self.arp_tpa = IPv4Address(raw_header[24:28])

            if not self.__post_parse_sanity_check():
                self.sanity_check_failed = True

        # Packet building
        else:
            self.tracker = Tracker("TX", echo_tracker)

            self.arp_hrtype = 1
            self.arp_prtype = 0x0800
            self.arp_hrlen = 6
            self.arp_prlen = 4
            self.arp_oper = arp_oper
            self.arp_sha = arp_sha
            self.arp_spa = IPv4Address(arp_spa)
            self.arp_tha = arp_tha
            self.arp_tpa = IPv4Address(arp_tpa)
예제 #10
0
    def send_arp_probe(self, ip4_unicast):
        """ Send out ARP probe to detect possible IP conflict """

        self.phtx_arp(
            ether_src=self.mac_unicast,
            ether_dst="ff:ff:ff:ff:ff:ff",
            arp_oper=ps_arp.ARP_OP_REQUEST,
            arp_sha=self.mac_unicast,
            arp_spa=IPv4Address("0.0.0.0"),
            arp_tha="00:00:00:00:00:00",
            arp_tpa=ip4_unicast,
        )
        self.logger.debug(f"Sent out ARP probe for {ip4_unicast}")
예제 #11
0
파일: arp_cache.py 프로젝트: isaacfgl/PyTCP
    def __send_arp_request(self, arp_tpa):
        """ Enqueue ARP request packet with TX ring """

        self.packet_handler.phtx_arp(
            ether_src=self.packet_handler.mac_unicast,
            ether_dst="ff:ff:ff:ff:ff:ff",
            arp_oper=ps_arp.ARP_OP_REQUEST,
            arp_sha=self.packet_handler.mac_unicast,
            arp_spa=self.packet_handler.ip4_unicast[0]
            if self.packet_handler.ip4_unicast else IPv4Address("0.0.0.0"),
            arp_tha="00:00:00:00:00:00",
            arp_tpa=arp_tpa,
        )
예제 #12
0
    def parse_stack_ip4_address_candidate(self,
                                          configured_ip4_address_candidate):
        """ Parse IPv4 candidate addresses configured in stack.py module """

        valid_address_candidate = []

        for address, gateway in configured_ip4_address_candidate:
            self.logger.debug(f"Parsing ('{address}', '{gateway}') entry")
            try:
                address = IPv4Interface(address)
            except AddressValueError:
                self.logger.warning(
                    f"Invalid host address '{address}' format, skiping...")
                continue
            if address.is_multicast or address.is_reserved or address.is_loopback or address.is_unspecified:
                self.logger.warning(
                    f"Invalid host address '{address.ip}' type, skiping...")
                continue
            if address.ip == address.network_address or address.ip == address.broadcast_address:
                self.logger.warning(
                    f"Invalid host address '{address.ip}' configured for network '{address.network}', skiping..."
                )
                continue
            if address.ip in [_.ip for _ in valid_address_candidate]:
                self.logger.warning(
                    f"Duplicate host address '{address.ip}' configured, skiping..."
                )
                continue
            if gateway is not None:
                try:
                    gateway = IPv4Address(gateway)
                    if gateway not in address.network or gateway == address.network_address or gateway == address.broadcast_address or gateway == address.ip:
                        self.logger.warning(
                            f"Invalid gateway '{gateway}' configured for interface address '{address}', skiping..."
                        )
                        gateway = None
                except AddressValueError:
                    self.logger.warning(
                        f"Invalid gateway '{gateway}' format configured for interface address '{address}' skiping..."
                    )
                    gateway = None
            address.gateway = gateway
            valid_address_candidate.append(address)
            self.logger.debug(
                f"Parsed ('{address}', '{address.gateway}') entry")

        return valid_address_candidate
예제 #13
0
        ))

        self.logger.debug(
            f"Sent out DHCP Request message to {dhcp_packet_rx.dhcp_srv_id}")

        # Wait for DHCP Ack
        if not (packet := socket.receive_from(timeout=5)):
            self.logger.warning("Timeout waiting for DHCP Ack message")
            return None, None

        dhcp_packet_rx = ps_dhcp.DhcpPacket(packet.raw_data)
        if dhcp_packet_rx.dhcp_msg_type != ps_dhcp.DHCP_ACK:
            self.logger.warning("Didn't receive DHCP Offer message")
            socket.close()
            return None, None

        self.logger.debug(
            f"Received DHCP Offer from {dhcp_packet_rx.dhcp_srv_id}" +
            f"IP: {dhcp_packet_rx.dhcp_yiaddr}, Mask: {dhcp_packet_rx.dhcp_subnet_mask}, Router: {dhcp_packet_rx.dhcp_router}"
            +
            f"DNS: {dhcp_packet_rx.dhcp_dns}, Domain: {dhcp_packet_rx.dhcp_domain_name}"
        )
        socket.close()
        return (
            IPv4Interface(
                str(dhcp_packet_rx.dhcp_yiaddr) + "/" + str(
                    IPv4Address._make_netmask(
                        str(dhcp_packet_rx.dhcp_subnet_mask))[1])),
            dhcp_packet_rx.dhcp_router[0],
        )
예제 #14
0
파일: ps_ip4.py 프로젝트: cclauss/PyTCP
    def __init__(
        self,
        parent_packet=None,
        ip4_src=None,
        ip4_dst=None,
        ip4_ttl=config.ip4_default_ttl,
        ip4_dscp=0,
        ip4_ecn=0,
        ip4_packet_id=0,
        ip4_flag_df=False,
        ip4_flag_mf=False,
        ip4_frag_offset=0,
        ip4_options=None,
        child_packet=None,
        ip4_proto=None,
        raw_data=b"",
        tracker=None,
    ):
        """ Class constructor """

        self.logger = loguru.logger.bind(object_name="ps_ip4.")
        self.sanity_check_failed = False

        # Packet parsing
        if parent_packet:
            self.tracker = parent_packet.tracker

            raw_packet = parent_packet.raw_data

            if not self.__pre_parse_sanity_check(raw_packet):
                self.sanity_check_failed = True
                return

            raw_header = raw_packet[:IP4_HEADER_LEN]
            raw_options = raw_packet[IP4_HEADER_LEN:(raw_packet[0]
                                                     & 0b00001111) << 2]

            self.raw_data = raw_packet[(raw_packet[0] & 0b00001111) << 2:struct
                                       .unpack("!H", raw_header[2:4])[0]]

            self.ip4_ver = raw_header[0] >> 4
            self.ip4_hlen = (raw_header[0] & 0b00001111) << 2
            self.ip4_dscp = (raw_header[1] & 0b11111100) >> 2
            self.ip4_ecn = raw_header[1] & 0b00000011
            self.ip4_plen = struct.unpack("!H", raw_header[2:4])[0]
            self.ip4_packet_id = struct.unpack("!H", raw_header[4:6])[0]
            self.ip4_flag_reserved = bool(
                struct.unpack("!H", raw_header[6:8])[0] & 0b1000000000000000)
            self.ip4_flag_df = bool(
                struct.unpack("!H", raw_header[6:8])[0] & 0b0100000000000000)
            self.ip4_flag_mf = bool(
                struct.unpack("!H", raw_header[6:8])[0] & 0b0010000000000000)
            self.ip4_frag_offset = (struct.unpack("!H", raw_header[6:8])[0]
                                    & 0b0001111111111111) << 3
            self.ip4_ttl = raw_header[8]
            self.ip4_proto = raw_header[9]
            self.ip4_cksum = struct.unpack("!H", raw_header[10:12])[0]
            self.ip4_src = IPv4Address(raw_header[12:16])
            self.ip4_dst = IPv4Address(raw_header[16:20])

            self.ip4_options = []

            opt_cls = {}

            i = 0

            while i < len(raw_options):

                if raw_options[i] == IP4_OPT_EOL:
                    self.ip4_options.append(Ip4OptEol())
                    break

                if raw_options[i] == IP4_OPT_NOP:
                    self.ip4_options.append(Ip4OptNop())
                    i += IP4_OPT_NOP_LEN
                    continue

                self.ip4_options.append(
                    opt_cls.get(raw_options[i], Ip4OptUnk)(
                        raw_options[i:i + raw_options[i + 1]]))
                i += self.raw_options[i + 1]

            if not self.__post_parse_sanity_check():
                self.sanity_check_failed = True

        # Packet building
        else:
            if tracker:
                self.tracker = tracker
            else:
                self.tracker = child_packet.tracker

            self.ip4_ver = 4
            self.ip4_hlen = None
            self.ip4_dscp = ip4_dscp
            self.ip4_ecn = ip4_ecn
            self.ip4_plen = None
            self.ip4_packet_id = ip4_packet_id
            self.ip4_flag_reserved = 0
            self.ip4_flag_df = ip4_flag_df
            self.ip4_flag_mf = ip4_flag_mf
            self.ip4_frag_offset = ip4_frag_offset
            self.ip4_ttl = ip4_ttl
            self.ip4_cksum = 0
            self.ip4_src = IPv4Address(ip4_src)
            self.ip4_dst = IPv4Address(ip4_dst)

            self.ip4_options = [] if ip4_options is None else ip4_options

            self.ip4_hlen = IP4_HEADER_LEN + len(self.raw_options)

            assert self.ip4_hlen % 4 == 0, "IP header len is not multiplcation of 4 bytes, check options"

            if child_packet:
                assert child_packet.protocol in {
                    "ICMPv4", "UDP", "TCP"
                }, f"Not supported protocol: {child_packet.protocol}"

                if child_packet.protocol == "ICMPv4":
                    self.ip4_proto = IP4_PROTO_ICMP4
                    self.raw_data = child_packet.get_raw_packet()
                    self.ip4_plen = self.ip4_hlen + len(self.raw_data)

                if child_packet.protocol == "UDP":
                    self.ip4_proto = IP4_PROTO_UDP
                    self.ip4_plen = self.ip4_hlen + child_packet.udp_plen
                    self.raw_data = child_packet.get_raw_packet(
                        self.ip_pseudo_header)

                if child_packet.protocol == "TCP":
                    self.ip4_proto = IP4_PROTO_TCP
                    self.ip4_plen = self.ip4_hlen + child_packet.tcp_hlen + len(
                        child_packet.raw_data)
                    self.raw_data = child_packet.get_raw_packet(
                        self.ip_pseudo_header)

            else:
                self.ip4_proto = ip4_proto
                self.raw_data = raw_data
                self.ip4_plen = self.ip4_hlen + len(self.raw_data)
예제 #15
0
def phrx_udp(self, ip_packet_rx, udp_packet_rx):
    """ Handle inbound UDP packets """

    # Validate UDP packet sanity
    if udp_packet_rx.sanity_check_failed:
        return

    self.logger.opt(ansi=True).info(f"<green>{udp_packet_rx.tracker}</green> - {udp_packet_rx}")

    # Set universal names for src and dst IP addresses whether packet was delivered by IPv6 or IPv4 protocol
    ip_packet_rx.ip_dst = ip_packet_rx.ip6_dst if ip_packet_rx.protocol == "IPv6" else ip_packet_rx.ip4_dst
    ip_packet_rx.ip_src = ip_packet_rx.ip6_src if ip_packet_rx.protocol == "IPv6" else ip_packet_rx.ip4_src

    # Create UdpMetadata object and try to find matching UDP socket
    packet = UdpMetadata(
        local_ip_address=ip_packet_rx.ip_dst,
        local_port=udp_packet_rx.udp_dport,
        remote_ip_address=ip_packet_rx.ip_src,
        remote_port=udp_packet_rx.udp_sport,
        raw_data=udp_packet_rx.raw_data,
        tracker=udp_packet_rx.tracker,
    )

    for socket_id in packet.socket_id_patterns:
        socket = stack.udp_sockets.get(socket_id, None)
        if socket:
            loguru.logger.bind(object_name="socket.").debug(f"{packet.tracker} - Found matching listening socket {socket_id}")
            socket.process_packet(packet)
            return

    # Silently drop packet if it has all zero source IP address
    if ip_packet_rx.ip_src in {IPv4Address("0.0.0.0"), IPv6Address("::")}:
        self.logger.debug(
            f"Received UDP packet from {ip_packet_rx.ip_src}, port {udp_packet_rx.udp_sport} to {ip_packet_rx.ip_dst}, port {udp_packet_rx.udp_dport}, droping"
        )
        return

    # Respond with ICMPv4 Port Unreachable message if no matching socket has been found
    self.logger.debug(f"Received UDP packet from {ip_packet_rx.ip_src} to closed port {udp_packet_rx.udp_dport}, sending ICMPv4 Port Unreachable")

    if ip_packet_rx.protocol == "IPv6":
        self.phtx_icmp6(
            ip6_src=ip_packet_rx.ip6_dst,
            ip6_dst=ip_packet_rx.ip6_src,
            icmp6_type=ps_icmp6.ICMP6_UNREACHABLE,
            icmp6_code=ps_icmp6.ICMP6_UNREACHABLE__PORT,
            icmp6_un_raw_data=ip_packet_rx.get_raw_packet(),
            echo_tracker=udp_packet_rx.tracker,
        )

    if ip_packet_rx.protocol == "IPv4":
        self.phtx_icmp4(
            ip4_src=ip_packet_rx.ip_dst,
            ip4_dst=ip_packet_rx.ip_src,
            icmp4_type=ps_icmp4.ICMP4_UNREACHABLE,
            icmp4_code=ps_icmp4.ICMP4_UNREACHABLE__PORT,
            icmp4_un_raw_data=ip_packet_rx.get_raw_packet(),
            echo_tracker=udp_packet_rx.tracker,
        )

    return
예제 #16
0
파일: ps_dhcp.py 프로젝트: cclauss/PyTCP
    def __init__(
        self,
        raw_packet=None,
        dhcp_op=BOOT_REQUEST,
        dhcp_xid=None,
        dhcp_flag_b=False,
        dhcp_ciaddr=IPv4Address("0.0.0.0"),
        dhcp_yiaddr=IPv4Address("0.0.0.0"),
        dhcp_siaddr=IPv4Address("0.0.0.0"),
        dhcp_giaddr=IPv4Address("0.0.0.0"),
        dhcp_chaddr=None,
        dhcp_subnet_mask=None,
        dhcp_router=None,
        dhcp_dns=None,
        dhcp_host_name=None,
        dhcp_domain_name=None,
        dhcp_req_ip4_addr=None,
        dhcp_addr_lease_time=None,
        dhcp_srv_id=None,
        dhcp_param_req_list=None,
        dhcp_msg_type=None,
    ):
        """ Class constructor """

        # Packet parsing
        if raw_packet:
            raw_header = raw_packet[:DHCP_HEADER_LEN]

            raw_options = raw_packet[DHCP_HEADER_LEN:]

            self.dhcp_op = raw_header[0]
            self.dhcp_htype = raw_header[1]
            self.dhcp_hlen = raw_header[2]
            self.dhcp_hops = raw_header[3]
            self.dhcp_xid = struct.unpack("!L", raw_header[4:8])[0]
            self.dhcp_secs = struct.unpack("!H", raw_header[8:10])[0]
            self.dhcp_flag_b = bool(struct.unpack("!H", raw_header[10:12])[0] & 0b1000000000000000)
            self.dhcp_ciaddr = IPv4Address(raw_header[12:16])
            self.dhcp_yiaddr = IPv4Address(raw_header[16:20])
            self.dhcp_siaddr = IPv4Address(raw_header[20:24])
            self.dhcp_giaddr = IPv4Address(raw_header[24:28])
            self.dhcp_chaddr = raw_header[28 : 28 + self.dhcp_hlen]
            self.dhcp_sname = raw_header[44:108]
            self.dhcp_file = raw_header[108:236]

            self.dhcp_options = []

            opt_cls = {
                DHCP_OPT_SUBNET_MASK: DhcpOptSubnetMask,
                DHCP_OPT_ROUTER: DhcpOptRouter,
                DHCP_OPT_DNS: DhcpOptDns,
                DHCP_OPT_HOST_NAME: DhcpOptHostName,
                DHCP_OPT_DOMAIN_NAME: DhcpOptDomainName,
                DHCP_OPT_REQ_IP4_ADDR: DhcpOptReqIpAddr,
                DHCP_OPT_ADDR_LEASE_TIME: DhcpOptAddrLeaseTime,
                DHCP_OPT_PARAM_REQ_LIST: DhcpOptParamReqList,
                DHCP_OPT_SRV_ID: DhcpOptSrvId,
                DHCP_OPT_MSG_TYPE: DhcpOptMsgType,
            }

            i = 0

            while i < len(raw_options):

                if raw_options[i] == DHCP_OPT_END:
                    self.dhcp_options.append(DhcpOptEnd())
                    break

                if raw_options[i] == DHCP_OPT_PAD:
                    self.dhcp_options.append(DhcpOptPad())
                    i += DHCP_OPT_PAD_LEN
                    continue

                self.dhcp_options.append(opt_cls.get(raw_options[i], DhcpOptUnk)(raw_options[i : i + raw_options[i + 1] + 2]))
                i += self.raw_options[i + 1] + 2

        # Packet building
        else:
            self.dhcp_op = dhcp_op
            self.dhcp_htype = 1
            self.dhcp_hlen = 6
            self.dhcp_hops = 0
            self.dhcp_xid = dhcp_xid
            self.dhcp_secs = 0
            self.dhcp_flag_b = dhcp_flag_b
            self.dhcp_ciaddr = dhcp_ciaddr
            self.dhcp_yiaddr = dhcp_yiaddr
            self.dhcp_siaddr = dhcp_siaddr
            self.dhcp_giaddr = dhcp_giaddr
            self.dhcp_chaddr = dhcp_chaddr
            self.dhcp_sname = b"\0" * 64
            self.dhcp_file = b"\0" * 128

            self.dhcp_options = []

            if dhcp_subnet_mask:
                self.dhcp_options.append(DhcpOptSubnetMask(opt_subnet_mask=dhcp_subnet_mask))

            if dhcp_router:
                self.dhcp_options.append(DhcpOptRouter(opt_router=dhcp_router))

            if dhcp_dns:
                self.dhcp_options.append(DhcpOptDns(opt_dns=dhcp_dns))

            if dhcp_host_name:
                self.dhcp_options.append(DhcpOptHostName(opt_host_name=dhcp_host_name))

            if dhcp_domain_name:
                self.dhcp_options.append(DhcpOptDomainName(opt_domain_name=dhcp_domain_name))

            if dhcp_req_ip4_addr:
                self.dhcp_options.append(DhcpOptReqIpAddr(opt_req_ip4_addr=dhcp_req_ip4_addr))

            if dhcp_addr_lease_time:
                self.dhcp_options.append(DhcpOptAddrLeaseTime(opt_addr_lease_time=dhcp_addr_lease_time))

            if dhcp_srv_id:
                self.dhcp_options.append(DhcpOptSrvId(opt_srv_id=dhcp_srv_id))

            if dhcp_param_req_list:
                self.dhcp_options.append(DhcpOptParamReqList(opt_param_req_list=dhcp_param_req_list))

            if dhcp_msg_type:
                self.dhcp_options.append(DhcpOptMsgType(opt_msg_type=dhcp_msg_type))

            self.dhcp_options.append(DhcpOptEnd())
예제 #17
0
def validate_src_ip4_address(self, ip4_src, ip4_dst):
    """ Make sure source ip address is valid, supplemt with valid one as appropriate """

    # Check if the the source IP address belongs to this stack or its set to all zeros (for DHCP client comunication)
    if ip4_src not in {
            *self.ip4_unicast, *self.ip4_multicast, *self.ip4_broadcast,
            IPv4Address("0.0.0.0")
    }:
        self.logger.warning(
            f"Unable to sent out IPv4 packet, stack doesn't own IPv4 address {ip4_src}"
        )
        return None

    # If packet is a response to multicast then replace source address with primary address of the stack
    if ip4_src in self.ip4_multicast:
        if self.ip4_unicast:
            ip4_src = self.ip4_unicast[0]
            self.logger.debug(
                f"Packet is response to multicast, replaced source with stack primary IPv4 address {ip4_src}"
            )
        else:
            self.logger.warning(
                "Unable to sent out IPv4 packet, no stack primary unicast IPv4 address available"
            )
            return None

    # If packet is a response to limited broadcast then replace source address with primary address of the stack
    if ip4_src.is_limited_broadcast:
        if self.ip4_unicast:
            ip4_src = self.ip4_unicast[0]
            self.logger.debug(
                f"Packet is response to limited broadcast, replaced source with stack primary IPv4 address {ip4_src}"
            )
        else:
            self.logger.warning(
                "Unable to sent out IPv4 packet, no stack primary unicast IPv4 address available"
            )
            return None

    # If packet is a response to directed braodcast then replace source address with first stack address that belongs to appropriate subnet
    if ip4_src in self.ip4_broadcast:
        ip4_src = [
            _.ip for _ in self.ip4_address if _.broadcast_address == ip4_src
        ]
        if ip4_src:
            ip4_src = ip4_src[0]
            self.logger.debug(
                f"Packet is response to directed broadcast, replaced source with apropriate IPv4 address {ip4_src}"
            )
        else:
            self.logger.warning(
                "Unable to sent out IPv4 packet, no appropriate stack unicast IPv4 address available"
            )
            return None

    # If source is unspecified check if destination belongs to any of local networks, if so pick source address from that network
    if ip4_src.is_unspecified:
        for ip4_address in self.ip4_address:
            if ip4_dst in ip4_address.network:
                return ip4_address.ip

    # If source unspcified and destination is external pick source from first network that has default gateway set
    if ip4_src.is_unspecified:
        for ip4_address in self.ip4_address:
            if ip4_address.gateway:
                return ip4_address.ip

    return ip4_src
예제 #18
0
def phtx_ip4(self,
             child_packet,
             ip4_dst,
             ip4_src,
             ip4_ttl=config.ip4_default_ttl):
    """ Handle outbound IP packets """

    # Check if IPv4 protocol support is enabled, if not then silently drop the packet
    if not config.ip4_support:
        return

    # Make sure source and destination addresses are the right object type
    ip4_src = IPv4Address(ip4_src)
    ip4_dst = IPv4Address(ip4_dst)

    # Validate source address
    ip4_src = validate_src_ip4_address(self, ip4_src, ip4_dst)
    if not ip4_src:
        return

    # Validate destination address
    ip4_dst = validate_dst_ip4_address(self, ip4_dst)
    if not ip4_dst:
        return

    # Generate new IPv4 ID
    self.ip4_packet_id += 1
    if self.ip4_packet_id > 65535:
        self.ip4_packet_id = 1

    # Check if packet can be sent out without fragmentation, if so send it out
    if ps_ip4.IP4_HEADER_LEN + len(child_packet.raw_packet) <= config.mtu:
        ip4_packet_tx = ps_ip4.Ip4Packet(ip4_src=ip4_src,
                                         ip4_dst=ip4_dst,
                                         ip4_packet_id=self.ip4_packet_id,
                                         child_packet=child_packet)

        self.logger.debug(f"{ip4_packet_tx.tracker} - {ip4_packet_tx}")
        self.phtx_ether(child_packet=ip4_packet_tx)
        return

    # Fragment packet and send all fragments out
    self.logger.debug(
        "Packet exceedes available MTU, IP fragmentation needed...")

    if child_packet.protocol == "ICMPv4":
        ip4_proto = ps_ip4.IP4_PROTO_ICMP4
        raw_data = child_packet.get_raw_packet()

    if child_packet.protocol in {"UDP", "TCP"}:
        ip4_proto = ps_ip4.IP4_PROTO_UDP if child_packet.protocol == "UDP" else ps_ip4.IP4_PROTO_TCP
        raw_data = child_packet.get_raw_packet(
            struct.pack(
                "! 4s 4s BBH",
                ip4_src.packed,
                ip4_dst.packed,
                0,
                ip4_proto,
                len(child_packet.raw_packet),
            ))

    raw_data_mtu = (config.mtu - ps_ether.ETHER_HEADER_LEN -
                    ps_ip4.IP4_HEADER_LEN) & 0b1111111111111000
    raw_data_fragments = [
        raw_data[_:raw_data_mtu + _]
        for _ in range(0, len(raw_data), raw_data_mtu)
    ]

    pointer = 0
    offset = 0

    for raw_data_fragment in raw_data_fragments:
        ip4_packet_tx = ps_ip4.Ip4Packet(
            ip4_src=ip4_src,
            ip4_dst=ip4_dst,
            ip4_proto=ip4_proto,
            ip4_packet_id=self.ip4_packet_id,
            ip4_flag_mf=pointer < len(raw_data_fragments) - 1,
            ip4_frag_offset=offset,
            ip4_ttl=ip4_ttl,
            raw_data=raw_data_fragment,
            tracker=child_packet.tracker,
        )
        pointer += 1
        offset += len(raw_data_fragment)

        self.logger.debug(f"{ip4_packet_tx.tracker} - {ip4_packet_tx}")
        self.phtx_ether(child_packet=ip4_packet_tx)

    return