예제 #1
0
    async def async_start(self):
        """Start watching for dhcp packets."""
        try:
            _verify_l2socket_creation_permission()
        except (Scapy_Exception, OSError) as ex:
            if os.geteuid() == 0:
                _LOGGER.error("Cannot watch for dhcp packets: %s", ex)
            else:
                _LOGGER.debug(
                    "Cannot watch for dhcp packets without root or CAP_NET_RAW: %s",
                    ex)
            return

        try:
            await _async_verify_working_pcap(self.hass, FILTER)
        except (Scapy_Exception, ImportError) as ex:
            _LOGGER.error(
                "Cannot watch for dhcp packets without a functional packet filter: %s",
                ex,
            )
            return

        self._sniffer = AsyncSniffer(
            filter=FILTER,
            started_callback=self._started.set,
            prn=self.handle_dhcp_packet,
            store=0,
        )
        self._sniffer.start()
예제 #2
0
파일: ping.py 프로젝트: RazCrimson/Lab_Main
 def __init__(self, dst: str):
     self.dst = dst
     self.__last_received_sequence_number = -1
     self.__last_generated_sequence_number = -1
     self.__sniffer = AsyncSniffer(prn=self.__handle_filtered_pkts,
                                   filter=f'src {self.dst} and icmp')
     self.__received_count = 0
    def listen_for_tunnel_pkts(self):
        """
        Listens for tunnel packets that are trapped to CPU

        These packets may be trapped if there is no neighbor info for the
        inner packet destination IP in the hardware.
        """

        def _ping_inner_dst(packet):
            """
            Pings the inner destination IP for an encapsulated packet

            Args:
                packet: The encapsulated packet received
            """
            inner_packet_type = self.get_inner_pkt_type(packet)
            if inner_packet_type and packet[IP].dst == self_ip:
                cmds = ['timeout', '0.2', 'ping', '-c1',
                        '-W1', '-i0', '-n', '-q']
                if inner_packet_type == IPv6:
                    cmds.append('-6')
                dst_ip = packet[IP].payload[inner_packet_type].dst
                cmds.append(dst_ip)
                logger.log_info("Running command '{}'".format(' '.join(cmds)))
                subprocess.run(cmds, stdout=subprocess.DEVNULL)

        self_ip, peer_ip = self.get_ipinip_tunnel_addrs()
        if self_ip is None or peer_ip is None:
            logger.log_notice('Could not get tunnel addresses from '
                              'config DB, exiting...')
            return None

        packet_filter = 'host {} and host {}'.format(self_ip, peer_ip)
        logger.log_notice('Starting tunnel packet handler for {}'
                          .format(packet_filter))

        sniff_intfs = self.get_up_portchannels()
        logger.log_info("Listening on interfaces {}".format(sniff_intfs))

        sniffer = AsyncSniffer(
            iface=sniff_intfs,
            filter=packet_filter,
            prn=_ping_inner_dst,
            store=0
        )
        sniffer.start()
        while True:
            msgs = self.wait_for_netlink_msgs()
            if self.sniffer_restart_required(msgs):
                sniffer.stop()
                sniff_intfs = self.get_up_portchannels()
                logger.log_notice('Restarting tunnel packet handler on '
                                  'interfaces {}'.format(sniff_intfs))
                sniffer = AsyncSniffer(
                    iface=sniff_intfs,
                    filter=packet_filter,
                    prn=_ping_inner_dst,
                    store=0
                )
                sniffer.start()
예제 #4
0
 def __init__(self, interface):
     self.interface = interface
     self.sniffer = AsyncSniffer(iface=interface,
                                 prn=self.__packet_handler,
                                 store=False)
     self.handlers = dict()
     self.channel = None
예제 #5
0
    async def async_start(self):
        """Start watching for dhcp packets."""
        # disable scapy promiscuous mode as we do not need it
        conf.sniff_promisc = 0

        try:
            await self.hass.async_add_executor_job(_verify_l2socket_setup,
                                                   FILTER)
        except (Scapy_Exception, OSError) as ex:
            if os.geteuid() == 0:
                _LOGGER.error("Cannot watch for dhcp packets: %s", ex)
            else:
                _LOGGER.debug(
                    "Cannot watch for dhcp packets without root or CAP_NET_RAW: %s",
                    ex)
            return

        try:
            await self.hass.async_add_executor_job(_verify_working_pcap,
                                                   FILTER)
        except (Scapy_Exception, ImportError) as ex:
            _LOGGER.error(
                "Cannot watch for dhcp packets without a functional packet filter: %s",
                ex,
            )
            return

        self._sniffer = AsyncSniffer(
            filter=FILTER,
            started_callback=self._started.set,
            prn=self.handle_dhcp_packet,
            store=0,
        )

        self._sniffer.start()
예제 #6
0
파일: sniffer.py 프로젝트: duymanhit/CIC
def create_sniffer(input_file,
                   input_interface,
                   output_mode,
                   output_file,
                   url_model=None):
    assert (input_file is None) ^ (input_interface is None)

    NewFlowSession = generate_session_class(output_mode, output_file,
                                            url_model)

    if input_file is not None:
        return AsyncSniffer(
            offline=input_file,
            filter="ip and (tcp or udp)",
            prn=None,
            session=NewFlowSession,
            store=False,
        )
    else:
        return AsyncSniffer(
            iface=input_interface,
            filter="ip and (tcp or udp)",
            prn=None,
            session=NewFlowSession,
            store=False,
        )
예제 #7
0
    def __init__(self, config, new_packets):
        self.config = config
        self.new_packets = new_packets

        self.sniffer = AsyncSniffer(iface=self.config.interface,
                                    filter=self.config.generate_frame_filter(),
                                    store=False,
                                    prn=self.new_packet)
    def start_sniffer(self):
        """
        Starts an AsyncSniffer and waits for it to inititalize fully
        """
        self.sniffer = AsyncSniffer(iface=self.sniff_intfs,
                                    filter=self.packet_filter,
                                    prn=self.ping_inner_dst,
                                    store=0)
        self.sniffer.start()

        while not hasattr(self.sniffer, 'stop_cb'):
            time.sleep(0.1)
예제 #9
0
    async def async_start(self):
        """Start watching for dhcp packets."""
        # Local import because importing from scapy has side effects such as opening
        # sockets
        from scapy import (  # pylint: disable=import-outside-toplevel,unused-import  # noqa: F401
            arch,
        )

        #
        # Importing scapy.sendrecv will cause a scapy resync which will
        # import scapy.arch.read_routes which will import scapy.sendrecv
        #
        # We avoid this circular import by importing arch above to ensure
        # the module is loaded and avoid the problem
        #
        from scapy.sendrecv import (  # pylint: disable=import-outside-toplevel
            AsyncSniffer,
        )

        # disable scapy promiscuous mode as we do not need it
        conf.sniff_promisc = 0

        try:
            await self.hass.async_add_executor_job(_verify_l2socket_setup, FILTER)
        except (Scapy_Exception, OSError) as ex:
            if os.geteuid() == 0:
                _LOGGER.error("Cannot watch for dhcp packets: %s", ex)
            else:
                _LOGGER.debug(
                    "Cannot watch for dhcp packets without root or CAP_NET_RAW: %s", ex
                )
            return

        try:
            await self.hass.async_add_executor_job(_verify_working_pcap, FILTER)
        except (Scapy_Exception, ImportError) as ex:
            _LOGGER.error(
                "Cannot watch for dhcp packets without a functional packet filter: %s",
                ex,
            )
            return

        self._sniffer = AsyncSniffer(
            filter=FILTER,
            started_callback=self._started.set,
            prn=self.handle_dhcp_packet,
            store=0,
        )

        self._sniffer.start()
        if self._sniffer.thread:
            self._sniffer.thread.name = self.__class__.__name__
예제 #10
0
    def _start_sniffer(self):
        # drop dhclient requests, this would avoid lease
        # renewal after freezing time.
        subprocess.check_call(["ovs-ofctl", "add-flow", self._br,
                               "priority=100,in_port=" + self.up_link_port + ",action=drop"])
        time.sleep(PKT_CAPTURE_WAIT)

        self._sniffer = AsyncSniffer(iface=self._br,
                                     filter="udp and (port 67 or 68)",
                                     prn=self._handle_dhcp_req_packet)

        self.pkt_list = []
        self._sniffer.start()
        LOG.debug("sniffer started")
        time.sleep(PKT_CAPTURE_WAIT)
예제 #11
0
    def start(self, filter, folder):
        logging.debug(
            'Starting sniffer for interface {0} and filter {1}'.format(
                self.__interface, filter))
        assert (not self.__impl)

        # Although Scapy docs say we could pass iface=None to sniff on all, this doesn't actually work.
        # Scapy listens on 1st available interface.
        # Therefore we need to collect list of interfaces by ourselves.
        ifaces = Network.SIPpNetwork.get_interfaces()
        if ifaces:

            class started:
                flag = False
                cond = threading.Condition()

            def started_callback():
                with started.cond:
                    started.flag = True
                    started.cond.notify()
                logging.debug('Started sniffer for interface {0}'.format(
                    self.__interface))

            self.__folder = folder
            self.__impl = AsyncSniffer(
                filter=filter,
                iface=list(
                    ifaces
                ),  # scapy waits for list(str), while we have set(str)
                started_callback=started_callback)
            self.__impl.start()
            # Need to wait for sniffing thread actually to start.
            # Otherwise we might start SIP dialogs when thread is not capturing yet,
            # and therefore miss to capture some initial messages.
            with started.cond:
                # Issue #35: Busy-loop wait.
                # Otherwise we could wait forever if Scapy threads terminates before setting the started.flag.
                # For example, due to exception inside it.
                while not started.flag:
                    started.cond.wait(1)
                    if not self.__impl.thread.is_alive():
                        raise Exception(
                            "Scapy thread has terminated while waiting for it to start"
                        )
        else:
            logging.debug(
                'Found no available real interfaces to sniff for dummy interface {0}'
                .format(self.__interface))
 def __init__(self, queue):
     """
     :param queue: queue to push detected items
     """
     super().__init__(
         AsyncSniffer(filter=self.SNIFF_FILTER,
                      prn=self.__filter_by_blacklist), queue)
예제 #13
0
def create_sniffer(input_file, input_interface, output_mode, output_file):
    assert (input_file is None) ^ (input_interface is None)

    NewFlowSession = generate_session_class(output_mode, output_file)

    if input_file is not None:
        return AsyncSniffer(offline=input_file,
                            filter='tcp port 443',
                            prn=None,
                            session=NewFlowSession,
                            store=False)
    else:
        return AsyncSniffer(iface=input_interface,
                            filter='tcp port 443',
                            prn=None,
                            session=NewFlowSession,
                            store=False)
예제 #14
0
class PacketSniffer:
    """
    Wrapper around the 'AsyncSniffer' class from the Scapy project.
    """
    def __init__(self, config, new_packets):
        self.config = config
        self.new_packets = new_packets

        self.sniffer = AsyncSniffer(iface=self.config.interface,
                                    filter=self.config.generate_frame_filter(),
                                    store=False,
                                    prn=self.new_packet)

    def start(self):
        """
        Starts the packet sniffer.
        """

        self.sniffer.start()

    def stop(self):
        """
        Stops the packet sniffer.
        """

        self.sniffer.stop()

    def is_running(self):
        """
        Returns true if the sniffer is running, false otherwise.
        """

        return self.sniffer.running

    def new_packet(self, packet):
        """
        Adds the packet given as parameter to the queue to be processed by the
        parser.
        """

        self.new_packets.put(packet)
예제 #15
0
class Sniffer:
    def __init__(self, interface):
        self.interface = interface
        self.sniffer = AsyncSniffer(iface=interface,
                                    prn=self.__packet_handler,
                                    store=False)
        self.handlers = dict()
        self.channel = None

    def add_packet_handler(self, handler):
        self.handlers[type(handler).__name__] = handler

    def remove_packet_handler(self, handler):
        del self.handlers[type(handler).__name__]

    def start(self):
        self.sniffer.start()

    def stop(self):
        try:
            self.sniffer.stop()
        except Scapy_Exception as e:
            logger.exception(e)

        self.sniffer.join()

    def __packet_handler(self, pkt):
        for _, handler in self.handlers.items():
            handler(pkt)
예제 #16
0
class Sniffer:
    def __init__(self, interface):
        self.interface = interface
        self.sniffer = AsyncSniffer(iface=interface,
                                    prn=self.__packet_handler,
                                    store=False)
        self.handlers = dict()
        self.channel = None

    def set_channel(self, channel: int):
        self.channel = channel

    def add_packet_handler(self, handler):
        self.handlers[type(handler).__name__] = handler

    def remove_packet_handler(self, handler):
        del self.handlers[type(handler).__name__]

    def start(self):
        self.sniffer.start()

    def stop(self):
        self.sniffer.stop()
        self.sniffer.join()

    def __packet_handler(self, pkt):
        for k, handler in self.handlers.items():
            handler(pkt)
예제 #17
0
 def sniff(self):
     # type: () -> None
     from scapy.supersocket import StreamSocket
     ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     ssock.bind((get_if_addr(self.optsniff.get("iface",
                                               conf.iface)), self.port))
     ssock.listen()
     sniffers = []
     try:
         while True:
             clientsocket, address = ssock.accept()
             print("%s connected" % repr(address))
             sock = StreamSocket(clientsocket, self.cls)
             optsniff = self.optsniff.copy()
             optsniff["prn"] = functools.partial(self.reply,
                                                 send_function=sock.send,
                                                 address=address)
             del optsniff["iface"]
             sniffer = AsyncSniffer(opened_socket=sock, **optsniff)
             sniffer.start()
             sniffers.append((sniffer, sock))
     finally:
         for (sniffer, sock) in sniffers:
             try:
                 sniffer.stop()
             except Exception:
                 pass
             sock.close()
         self.close()
         ssock.close()
예제 #18
0
def sniff_gen(**kwargs):
    pkts = queue.SimpleQueue()
    t = AsyncSniffer(store=False, prn=pkts.put, **kwargs)
    t.start()
    try:
        while True:
            yield pkts.get()
    except KeyboardInterrupt:
        pass
    t.stop()
예제 #19
0
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.mac_dict = {}
            self.ip_dict = {}
            self.spoofed_ip_dict = {}
            self.spoofed_mac_dict = {}
            self.filter_dict = {}
            self.hscl_b, self.hscl_a = Pipe(
                duplex=False)  # Host Scanner Communication Line
            self.b, self.a = Pipe(duplex=False)
            self.scanner_active = False
            self.load_filter_list()

            AsyncSniffer(
                prn=self.traffic_filter,
                filter=f"not ether proto arp and not host {self.my_ip}",
                iface=self.interface,
                store=0).start()
예제 #20
0
class DhcpClient(unittest.TestCase):
    def setUp(self):
        self._br = "dh_br0"
        self.vlan_sw = "vlan_sw"
        self.up_link_port = ""

        try:
            subprocess.check_call(["pkill", "dnsmasq"])
        except subprocess.CalledProcessError:
            pass

        self.dhcp_wait = threading.Condition()
        self.pkt_list_lock = threading.Condition()
        self.dhcp_store = {}
        self.gw_info_map = {}
        self.gw_info = UplinkGatewayInfo(self.gw_info_map)
        self._sniffer = None
        self._last_xid = -1

    def tearDown(self):
        self._dhcp_client.stop()
        BridgeTools.destroy_bridge(self._br)

    @unittest.skipIf(os.getuid(), reason="needs root user")
    def test_dhcp_lease1(self):
        self._setup_dhcp_vlan_off()

        mac1 = MacAddress("11:22:33:44:55:66")
        self._validate_dhcp_alloc_renew(mac1, None)

    """
    Network diagram of vlan test setup                                    VETH pair        +---------------------+
    ==================================                                                     |  Namespace 1 with   |
                                                                      +--------------------+  DHCP server        |
                                                                      |                    |                     |
                                                                      |                    +---------------------+
                                                                      |
    +----------------+  Patch   +--------------+  uplink-    +--------+-------+            +---------------------+
    |                |  Port    |              |  iface      |                | VETH Pair  |  Namespace 2 with   |
    |  GTP_BR0       +----------+  UPLINK_BR0  +-------------+    VLAN_SW     +------------+  DHCP server        |
    |                |          |              |             |                |            |                     |
    +----------------+          +--------------+             +----------------+            +---------------------+
    """

    @unittest.skipIf(os.getuid(), reason="needs root user")
    def test_dhcp_vlan(self):
        vlan1: int = 2
        mac1 = MacAddress("11:22:33:44:55:66")

        self._setup_vlan_network()
        self._setup_dhcp_on_vlan(vlan1)

        self._validate_dhcp_alloc_renew(mac1, vlan1)
        self._validate_ip_subnet(mac1, vlan1)
        self._release_ip(mac1, vlan1)

    @unittest.skip("needs more investigation.")
    def test_dhcp_vlan_multi(self):
        self._setup_vlan_network()

        vlan1 = 51
        mac1 = MacAddress("11:22:33:44:55:66")
        vlan2 = 52
        mac2 = MacAddress("22:22:33:44:55:66")
        vlan3 = 53
        mac3 = MacAddress("11:22:33:44:55:66")

        self._setup_dhcp_on_vlan(vlan1)
        self._setup_dhcp_on_vlan(vlan2)
        self._setup_dhcp_on_vlan(vlan3)

        self._validate_dhcp_alloc_renew(mac1, vlan1)
        self._validate_dhcp_alloc_renew(mac2, vlan2)
        self._validate_dhcp_alloc_renew(mac3, vlan3)

        self._validate_ip_subnet(mac1, vlan1)
        self._validate_ip_subnet(mac2, vlan2)
        self._validate_ip_subnet(mac3, vlan3)

        self._release_ip(mac1, vlan1)
        self._release_ip(mac2, vlan2)
        self._release_ip(mac3, vlan3)

    def _validate_dhcp_alloc_renew(self, mac1: MacAddress, vlan: int):
        self._alloc_ip_address_from_dhcp(mac1, vlan)
        self._validate_req_state(mac1, DHCPState.REQUEST, vlan)
        self._validate_state_as_current(mac1, vlan)

        # trigger lease reneval before deadline
        self._last_xid = self._get_state_xid(mac1, vlan)
        LOG.debug("time: %s", datetime.datetime.now())
        time1 = datetime.datetime.now() + datetime.timedelta(seconds=100)
        self._start_sniffer()
        with freeze_time(time1):
            LOG.debug("check req packets time: %s", datetime.datetime.now())
            self._stop_sniffer_and_check(DHCPState.REQUEST, mac1, vlan)
            self._validate_req_state(mac1, DHCPState.REQUEST, vlan)
            self._validate_state_as_current(mac1, vlan)

            # trigger lease after deadline
            self._last_xid = self._get_state_xid(mac1, vlan)
            time2 = datetime.datetime.now() + datetime.timedelta(seconds=2000)
            self._start_sniffer()
            LOG.debug(
                "check discover packets time: %s",
                datetime.datetime.now(),
            )
            with freeze_time(time2):
                LOG.debug("check discover after lease loss")
                self._stop_sniffer_and_check(DHCPState.DISCOVER, mac1, vlan)
                self._validate_req_state(mac1, DHCPState.REQUEST, vlan)
                self._validate_state_as_current(mac1, vlan)

    def _setup_dhcp_vlan_off(self):
        self.up_link_port = "cl1uplink_p0"
        setup_dhcp_server = SCRIPT_PATH + "scripts/setup-test-dhcp-srv.sh"
        subprocess.check_call([setup_dhcp_server, "cl1"])

        setup_uplink_br = [
            SCRIPT_PATH + "scripts/setup-uplink-br.sh",
            self._br,
            self.up_link_port,
            DHCP_IFACE,
        ]
        subprocess.check_call(setup_uplink_br)
        self._setup_dhclp_client()

    def _setup_vlan_network(self):
        self.up_link_port = "v_ul_0"
        setup_vlan_switch = SCRIPT_PATH + "scripts/setup-uplink-vlan-sw.sh"
        subprocess.check_call([setup_vlan_switch, self.vlan_sw, "v"])

        setup_uplink_br = [
            SCRIPT_PATH + "scripts/setup-uplink-br.sh",
            self._br,
            self.up_link_port,
            DHCP_IFACE,
        ]
        subprocess.check_call(setup_uplink_br)
        self._setup_dhclp_client()

    def _setup_dhclp_client(self):
        self._dhcp_client = DHCPClient(
            dhcp_wait=self.dhcp_wait,
            dhcp_store=self.dhcp_store,
            gw_info=self.gw_info,
            iface=DHCP_IFACE,
            lease_renew_wait_min=1,
        )
        self._dhcp_client.run()

    def _setup_dhcp_on_vlan(self, vlan: int):
        setup_vlan_switch = SCRIPT_PATH + "scripts/setup-uplink-vlan-srv.sh"
        subprocess.check_call([setup_vlan_switch, self.vlan_sw, str(vlan)])

    def _validate_req_state(
        self,
        mac: MacAddress,
        state: DHCPState,
        vlan: int,
    ):
        for x in range(RETRY_LIMIT):
            LOG.debug("wait for state: %d" % x)
            with self.dhcp_wait:
                dhcp1 = self.dhcp_store.get(mac.as_redis_key(vlan))
                if state == DHCPState.RELEASE and dhcp1 is None:
                    return
                if dhcp1.state_requested == state:
                    return
            time.sleep(PKT_CAPTURE_WAIT)

        assert 0

    def _get_state_xid(self, mac: MacAddress, vlan: int):
        with self.dhcp_wait:
            dhcp1 = self.dhcp_store.get(mac.as_redis_key(vlan))
            return dhcp1.xid

    def _validate_state_as_current(self, mac: MacAddress, vlan: int):
        with self.dhcp_wait:
            dhcp1 = self.dhcp_store.get(mac.as_redis_key(vlan))
            self.assert_(
                dhcp1.state == DHCPState.OFFER
                or dhcp1.state == DHCPState.ACK, )

    def _alloc_ip_address_from_dhcp(
        self,
        mac: MacAddress,
        vlan: int,
    ) -> DHCPDescriptor:
        retry_count = 0
        with self.dhcp_wait:
            dhcp_desc = None
            while (retry_count < 60
                   and (dhcp_desc is None
                        or dhcp_desc.ip_is_allocated() is not True)):
                if retry_count % 5 == 0:
                    self._dhcp_client.send_dhcp_packet(
                        mac,
                        vlan,
                        DHCPState.DISCOVER,
                    )

                self.dhcp_wait.wait(timeout=1)
                dhcp_desc = self._dhcp_client.get_dhcp_desc(mac, vlan)
                retry_count = retry_count + 1

            return dhcp_desc

    def _validate_ip_subnet(self, mac: MacAddress, vlan: int):
        # vlan is configured with subnet : 10.200.x.1
        # router IP is 10.200.x.211
        # x is vlan id
        exptected_subnet = ipaddress.ip_network("10.200.%s.0/24" % vlan)
        exptected_router_ip = ipaddress.ip_address("10.200.%s.211" % vlan)
        with self.dhcp_wait:
            dhcp1 = self.dhcp_store.get(mac.as_redis_key(vlan))
            self.assertEqual(dhcp1.subnet, str(exptected_subnet))
            self.assertEqual(dhcp1.router_ip, exptected_router_ip)
            self.assert_(ipaddress.ip_address(dhcp1.ip) in exptected_subnet)

    def _release_ip(self, mac: MacAddress, vlan: int):
        self._dhcp_client.release_ip_address(mac, vlan)
        time.sleep(PKT_CAPTURE_WAIT)
        self._validate_req_state(mac, DHCPState.RELEASE, vlan)

    def _handle_dhcp_req_packet(self, packet):
        if DHCP not in packet:
            return
        with self.pkt_list_lock:
            self.pkt_list.append(packet)

    def _start_sniffer(self):
        # drop dhclient requests, this would avoid lease
        # renewal after freezing time.
        subprocess.check_call([
            "ovs-ofctl",
            "add-flow",
            self._br,
            "priority=100,in_port=" + self.up_link_port + ",action=drop",
        ])
        time.sleep(PKT_CAPTURE_WAIT)

        self._sniffer = AsyncSniffer(
            iface=self._br,
            filter="udp and (port 67 or 68)",
            store=False,
            prn=self._handle_dhcp_req_packet,
        )

        self.pkt_list = []
        self._sniffer.start()
        LOG.debug("sniffer started")
        time.sleep(PKT_CAPTURE_WAIT)

    def _stop_sniffer_and_check(self, state: DHCPState, mac: MacAddress,
                                vlan: int):
        LOG.debug("delete drop flow")
        subprocess.check_call([
            "ovs-ofctl",
            "del-flows",
            self._br,
            "in_port=" + self.up_link_port,
        ])

        for x in range(RETRY_LIMIT):
            LOG.debug("wait for pkt: %d" % x)
            time.sleep(PKT_CAPTURE_WAIT)

            with self.pkt_list_lock:
                for pkt in self.pkt_list:
                    if DHCP in pkt:
                        if vlan and (Dot1Q not in pkt
                                     or vlan != pkt[Dot1Q].vlan):
                            continue

                        if pkt[DHCP].options[0][1] == int(state) and \
                                pkt[Ether].src == str(mac):
                            self._sniffer.stop()
                            return

        LOG.debug("Failed check for dhcp packet: %s", state)
        with self.pkt_list_lock:
            for pkt in self.pkt_list:
                LOG.debug("DHCP pkt %s", pkt.show(dump=True))

        # validate if any dhcp packet was sent.
        if state == DHCPState.DISCOVER:
            self.assertNotEqual(
                self._last_xid,
                self._get_state_xid(mac, vlan),
            )
예제 #21
0
    def __init__(self,
                 can_socket,  # type: "CANSocket"
                 src_id,  # type: int
                 dst_id,  # type: int
                 padding=False,  # type: bool
                 extended_addr=None,  # type: Optional[int]
                 extended_rx_addr=None,  # type: Optional[int]
                 rx_block_size=0,  # type: int
                 stmin=0,  # type: int
                 listen_only=False  # type: bool
                 ):
        # type: (...) -> None
        self.can_socket = can_socket
        self.dst_id = dst_id
        self.src_id = src_id
        self.padding = padding
        self.fc_timeout = 1
        self.cf_timeout = 1

        self.filter_warning_emitted = False

        self.extended_rx_addr = extended_rx_addr
        self.ea_hdr = b""
        if extended_addr is not None:
            self.ea_hdr = struct.pack("B", extended_addr)
        self.listen_only = listen_only

        self.rxfc_bs = rx_block_size
        self.rxfc_stmin = stmin

        self.rx_queue = ObjectPipe()
        self.rx_len = -1
        self.rx_buf = None  # type: Optional[bytes]
        self.rx_sn = 0
        self.rx_bs = 0
        self.rx_idx = 0
        self.rx_ts = 0.0  # type: Union[float, EDecimal]
        self.rx_state = ISOTP_IDLE

        self.txfc_bs = 0
        self.txfc_stmin = 0
        self.tx_gap = 0

        self.tx_buf = None  # type: Optional[bytes]
        self.tx_sn = 0
        self.tx_bs = 0
        self.tx_idx = 0
        self.rx_ll_dl = 0
        self.tx_state = ISOTP_IDLE

        self.tx_timeout_handle = None  # type: Optional[TimeoutScheduler.Handle]  # noqa: E501
        self.rx_timeout_handle = None  # type: Optional[TimeoutScheduler.Handle]  # noqa: E501
        self.rx_thread_started = Event()
        self.rx_thread = AsyncSniffer(
            store=False, opened_socket=can_socket, prn=self.on_can_recv,
            started_callback=self.rx_thread_started.set)

        self.tx_mutex = Lock()
        self.rx_mutex = Lock()
        self.send_mutex = Lock()

        self.tx_done = Event()
        self.tx_exception = None  # type: Optional[str]

        self.tx_callbacks = []  # type: List[Callable[[], None]]
        self.rx_callbacks = []  # type: List[Callable[[bytes], None]]

        self.rx_thread.start()
        self.rx_thread_started.wait(5)
예제 #22
0
 def _setup_sniffer(self):
     self._sniffer = AsyncSniffer(iface=DHCP_IFACE,
                                  filter="udp and (port 67 or 68)",
                                  prn=self._handle_dhcp_req_packet)
예제 #23
0
class DhcpClient(unittest.TestCase):
    def setUp(self):
        self._br = "t_up_br0"
        try:
            subprocess.check_call(["pkill", "dnsmasq"])
        except subprocess.CalledProcessError:
            pass

        setup_dhcp_server = SCRIPT_PATH + "scripts/setup-test-dhcp-srv.sh"
        subprocess.check_call([setup_dhcp_server, "t0"])

        setup_uplink_br = [SCRIPT_PATH + "scripts/setup-uplink-br.sh",
                           self._br,
                           DHCP_IFACE,
                           "8A:00:00:00:00:01"]
        subprocess.check_call(setup_uplink_br)

        self.dhcp_wait = threading.Condition()
        self.dhcp_store = {}
        self.gw_info_map = {}
        self.gw_info = UplinkGatewayInfo(self.gw_info_map)
        self._dhcp_client = DHCPClient(dhcp_wait=self.dhcp_wait,
                                       dhcp_store=self.dhcp_store,
                                       gw_info=self.gw_info,
                                       iface="t_dhcp0",
                                       lease_renew_wait_min=1)
        self._dhcp_client.run()

    def tearDown(self):
        self._dhcp_client.stop()
        BridgeTools.destroy_bridge(self._br)

    @unittest.skipIf(os.getuid(), reason="needs root user")
    def test_dhcp_lease1(self):
        self._setup_sniffer()
        mac1 = MacAddress("11:22:33:44:55:66")
        dhcp1 = self._alloc_ip_address_from_dhcp(mac1)
        self.assertEqual(dhcp1.state_requested, DHCPState.REQUEST)
        assert (dhcp1.state == DHCPState.OFFER or dhcp1.state == DHCPState.ACK)

        # trigger lease reneval before deadline
        time1 = datetime.datetime.now() + datetime.timedelta(seconds=100)
        self._start_sniffer()
        with freeze_time(time1):
            time.sleep(PKT_CAPTURE_WAIT)
            self._stop_sniffer_and_check(DHCPState.REQUEST, mac1)
            self.assertEqual(dhcp1.state_requested, DHCPState.REQUEST)
            assert (dhcp1.state == DHCPState.OFFER or dhcp1.state == DHCPState.ACK)

            # trigger lease after deadline
            time2 = datetime.datetime.now() + datetime.timedelta(seconds=200)
            self._start_sniffer()
            with freeze_time(time2):
                time.sleep(PKT_CAPTURE_WAIT)
                self._stop_sniffer_and_check(DHCPState.DISCOVER, mac1)
                self.assertEqual(dhcp1.state_requested, DHCPState.REQUEST)
                assert (dhcp1.state == DHCPState.OFFER or dhcp1.state == DHCPState.ACK)

        self._dhcp_client.release_ip_address(mac1)

        dhcp1 = self.dhcp_store.get(mac1.as_redis_key())
        self.assertEqual(dhcp1.state_requested, DHCPState.RELEASE)

    def _alloc_ip_address_from_dhcp(self, mac: MacAddress) -> DHCPDescriptor:
        retry_count = 0
        with self.dhcp_wait:
            dhcp_desc = None
            while (retry_count < 60 and (dhcp_desc is None or
                                         dhcp_desc.ip_is_allocated() is not True)):
                if retry_count % 5 == 0:
                    self._dhcp_client.send_dhcp_packet(mac, DHCPState.DISCOVER)

                self.dhcp_wait.wait(timeout=1)
                dhcp_desc = self._dhcp_client.get_dhcp_desc(mac)
                retry_count = retry_count + 1

            return dhcp_desc

    def _handle_dhcp_req_packet(self, packet):
        if DHCP not in packet:
            return
        self.pkt_list.append(packet)

    def _setup_sniffer(self):
        self._sniffer = AsyncSniffer(iface=DHCP_IFACE,
                                     filter="udp and (port 67 or 68)",
                                     prn=self._handle_dhcp_req_packet)

    def _start_sniffer(self):
        self.pkt_list = []
        self._sniffer.start()
        time.sleep(.5)

    def _stop_sniffer_and_check(self, state: DHCPState, mac: MacAddress):
        self._sniffer.stop()
        for pkt in self.pkt_list:
            logging.debug("pkt: %s " % pkt.summary())
            if DHCP in pkt:
                if pkt[DHCP].options[0][1] == int(state) and \
                        pkt[Ether].src == str(mac):
                    return
        assert 0
예제 #24
0
class DHCPWatcher(WatcherBase):
    """Class to watch dhcp requests."""
    def __init__(self, opp, address_data, integration_matchers):
        """Initialize class."""
        super().__init__(opp, address_data, integration_matchers)
        self._sniffer = None
        self._started = threading.Event()

    async def async_stop(self):
        """Stop watching for new device trackers."""
        await self.opp.async_add_executor_job(self._stop)

    def _stop(self):
        """Stop the thread."""
        if self._started.is_set():
            self._sniffer.stop()

    async def async_start(self):
        """Start watching for dhcp packets."""
        # disable scapy promiscuous mode as we do not need it
        conf.sniff_promisc = 0

        try:
            await self.opp.async_add_executor_job(_verify_l2socket_setup,
                                                  FILTER)
        except (Scapy_Exception, OSError) as ex:
            if os.geteuid() == 0:
                _LOGGER.error("Cannot watch for dhcp packets: %s", ex)
            else:
                _LOGGER.debug(
                    "Cannot watch for dhcp packets without root or CAP_NET_RAW: %s",
                    ex)
            return

        try:
            await self.opp.async_add_executor_job(_verify_working_pcap, FILTER)
        except (Scapy_Exception, ImportError) as ex:
            _LOGGER.error(
                "Cannot watch for dhcp packets without a functional packet filter: %s",
                ex,
            )
            return

        self._sniffer = AsyncSniffer(
            filter=FILTER,
            started_callback=self._started.set,
            prn=self.handle_dhcp_packet,
            store=0,
        )

        self._sniffer.start()
        if self._sniffer.thread:
            self._sniffer.thread.name = self.__class__.__name__

    def handle_dhcp_packet(self, packet):
        """Process a dhcp packet."""
        if DHCP not in packet:
            return

        options = packet[DHCP].options

        request_type = _decode_dhcp_option(options, MESSAGE_TYPE)
        if request_type != DHCP_REQUEST:
            # DHCP request
            return

        ip_address = _decode_dhcp_option(options,
                                         REQUESTED_ADDR) or packet[IP].src
        hostname = _decode_dhcp_option(options, HOSTNAME)
        mac_address = _format_mac(packet[Ether].src)

        if ip_address is None or hostname is None or mac_address is None:
            return

        self.process_client(ip_address, hostname, mac_address)

    def create_task(self, task):
        """Pass a task to opp.add_job since we are in a thread."""
        return self.opp.add_job(task)
예제 #25
0
    def _start(self):
        """Start watching for dhcp packets."""
        # Local import because importing from scapy has side effects such as opening
        # sockets
        from scapy import (  # pylint: disable=import-outside-toplevel,unused-import  # noqa: F401
            arch, )
        from scapy.layers.dhcp import DHCP  # pylint: disable=import-outside-toplevel
        from scapy.layers.inet import IP  # pylint: disable=import-outside-toplevel
        from scapy.layers.l2 import Ether  # pylint: disable=import-outside-toplevel

        #
        # Importing scapy.sendrecv will cause a scapy resync which will
        # import scapy.arch.read_routes which will import scapy.sendrecv
        #
        # We avoid this circular import by importing arch above to ensure
        # the module is loaded and avoid the problem
        #
        from scapy.sendrecv import (  # pylint: disable=import-outside-toplevel
            AsyncSniffer, )

        def _handle_dhcp_packet(packet):
            """Process a dhcp packet."""
            if DHCP not in packet:
                return

            options = packet[DHCP].options
            request_type = _decode_dhcp_option(options, MESSAGE_TYPE)
            if request_type != DHCP_REQUEST:
                # Not a DHCP request
                return

            ip_address = _decode_dhcp_option(options,
                                             REQUESTED_ADDR) or packet[IP].src
            hostname = _decode_dhcp_option(options, HOSTNAME) or ""
            mac_address = _format_mac(packet[Ether].src)

            if ip_address is not None and mac_address is not None:
                self.process_client(ip_address, hostname, mac_address)

        # disable scapy promiscuous mode as we do not need it
        conf.sniff_promisc = 0

        try:
            _verify_l2socket_setup(FILTER)
        except (Scapy_Exception, OSError) as ex:
            if os.geteuid() == 0:
                _LOGGER.error("Cannot watch for dhcp packets: %s", ex)
            else:
                _LOGGER.debug(
                    "Cannot watch for dhcp packets without root or CAP_NET_RAW: %s",
                    ex)
            return

        try:
            _verify_working_pcap(FILTER)
        except (Scapy_Exception, ImportError) as ex:
            _LOGGER.error(
                "Cannot watch for dhcp packets without a functional packet filter: %s",
                ex,
            )
            return

        self._sniffer = AsyncSniffer(
            filter=FILTER,
            started_callback=self._started.set,
            prn=_handle_dhcp_packet,
            store=0,
        )

        self._sniffer.start()
        if self._sniffer.thread:
            self._sniffer.thread.name = self.__class__.__name__
예제 #26
0
class ISOTPSocketImplementation:
    """
    Implementation of an ISOTP "state machine".

    Most of the ISOTP logic was taken from
    https://github.com/hartkopp/can-isotp/blob/master/net/can/isotp.c

    This class is separated from ISOTPSoftSocket to make sure the background
    thread can't hold a reference to ISOTPSoftSocket, allowing it to be
    collected by the GC.

    :param can_socket: a CANSocket instance, preferably filtering only can
                       frames with identifier equal to did
    :param src_id: the CAN identifier of the sent CAN frames
    :param dst_id: the CAN identifier of the received CAN frames
    :param padding: If True, pads sending packets with 0x00 which not
                    count to the payload.
                    Does not affect receiving packets.
    :param extended_addr: Extended Address byte to be added at the
            beginning of every CAN frame _sent_ by this object. Can be None
            in order to disable extended addressing on sent frames.
    :param extended_rx_addr: Extended Address byte expected to be found at
            the beginning of every CAN frame _received_ by this object. Can
            be None in order to disable extended addressing on received
            frames.
    :param rx_block_size: Block Size byte to be included in every Control
            Flow Frame sent by this object. The default value of 0 means
            that all the data will be received in a single block.
    :param stmin: Time Minimum Separation byte to be
            included in every Control Flow Frame sent by this object. The
            default value of 0 indicates that the peer will not wait any
            time between sending frames.
    :param listen_only: Disables send of flow control frames
    """

    def __init__(self,
                 can_socket,  # type: "CANSocket"
                 src_id,  # type: int
                 dst_id,  # type: int
                 padding=False,  # type: bool
                 extended_addr=None,  # type: Optional[int]
                 extended_rx_addr=None,  # type: Optional[int]
                 rx_block_size=0,  # type: int
                 stmin=0,  # type: int
                 listen_only=False  # type: bool
                 ):
        # type: (...) -> None
        self.can_socket = can_socket
        self.dst_id = dst_id
        self.src_id = src_id
        self.padding = padding
        self.fc_timeout = 1
        self.cf_timeout = 1

        self.filter_warning_emitted = False

        self.extended_rx_addr = extended_rx_addr
        self.ea_hdr = b""
        if extended_addr is not None:
            self.ea_hdr = struct.pack("B", extended_addr)
        self.listen_only = listen_only

        self.rxfc_bs = rx_block_size
        self.rxfc_stmin = stmin

        self.rx_queue = ObjectPipe()
        self.rx_len = -1
        self.rx_buf = None  # type: Optional[bytes]
        self.rx_sn = 0
        self.rx_bs = 0
        self.rx_idx = 0
        self.rx_ts = 0.0  # type: Union[float, EDecimal]
        self.rx_state = ISOTP_IDLE

        self.txfc_bs = 0
        self.txfc_stmin = 0
        self.tx_gap = 0

        self.tx_buf = None  # type: Optional[bytes]
        self.tx_sn = 0
        self.tx_bs = 0
        self.tx_idx = 0
        self.rx_ll_dl = 0
        self.tx_state = ISOTP_IDLE

        self.tx_timeout_handle = None  # type: Optional[TimeoutScheduler.Handle]  # noqa: E501
        self.rx_timeout_handle = None  # type: Optional[TimeoutScheduler.Handle]  # noqa: E501
        self.rx_thread_started = Event()
        self.rx_thread = AsyncSniffer(
            store=False, opened_socket=can_socket, prn=self.on_can_recv,
            started_callback=self.rx_thread_started.set)

        self.tx_mutex = Lock()
        self.rx_mutex = Lock()
        self.send_mutex = Lock()

        self.tx_done = Event()
        self.tx_exception = None  # type: Optional[str]

        self.tx_callbacks = []  # type: List[Callable[[], None]]
        self.rx_callbacks = []  # type: List[Callable[[bytes], None]]

        self.rx_thread.start()
        self.rx_thread_started.wait(5)

    def __del__(self):
        # type: () -> None
        self.close()

    def can_send(self, load):
        # type: (bytes) -> None
        if self.padding:
            load += b"\xCC" * (CAN_MAX_DLEN - len(load))
        if self.src_id is None or self.src_id <= 0x7ff:
            self.can_socket.send(CAN(identifier=self.src_id, data=load))
        else:
            self.can_socket.send(CAN(identifier=self.src_id, flags="extended",
                                     data=load))

    def on_can_recv(self, p):
        # type: (Packet) -> None
        if not isinstance(p, CAN):
            raise Scapy_Exception("argument is not a CAN frame")
        if p.identifier != self.dst_id:
            if not self.filter_warning_emitted and conf.verb >= 2:
                warning("You should put a filter for identifier=%x on your "
                        "CAN socket", self.dst_id)
                self.filter_warning_emitted = True
        else:
            self.on_recv(p)

    def close(self):
        # type: () -> None
        if self.rx_thread.thread and self.rx_thread.thread.is_alive():
            self.rx_thread.stop(True)

    def _rx_timer_handler(self):
        # type: () -> None
        """Method called every time the rx_timer times out, due to the peer not
        sending a consecutive frame within the expected time window"""

        with self.rx_mutex:
            if self.rx_state == ISOTP_WAIT_DATA:
                # we did not get new data frames in time.
                # reset rx state
                self.rx_state = ISOTP_IDLE
                if conf.verb > 2:
                    warning("RX state was reset due to timeout")

    def _tx_timer_handler(self):
        # type: () -> None
        """Method called every time the tx_timer times out, which can happen in
        two situations: either a Flow Control frame was not received in time,
        or the Separation Time Min is expired and a new frame must be sent."""

        with self.tx_mutex:
            if (self.tx_state == ISOTP_WAIT_FC or
                    self.tx_state == ISOTP_WAIT_FIRST_FC):
                # we did not get any flow control frame in time
                # reset tx state
                self.tx_state = ISOTP_IDLE
                self.tx_exception = "TX state was reset due to timeout"
                self.tx_done.set()
                raise Scapy_Exception(self.tx_exception)
            elif self.tx_state == ISOTP_SENDING:
                # push out the next segmented pdu
                src_off = len(self.ea_hdr)
                max_bytes = 7 - src_off
                if self.tx_buf is None:
                    self.tx_exception = "TX buffer is not filled"
                    raise Scapy_Exception(self.tx_exception)

                while 1:
                    load = self.ea_hdr
                    load += struct.pack("B", N_PCI_CF + self.tx_sn)
                    load += self.tx_buf[self.tx_idx:self.tx_idx + max_bytes]
                    self.can_send(load)

                    self.tx_sn = (self.tx_sn + 1) % 16
                    self.tx_bs += 1
                    self.tx_idx += max_bytes

                    if len(self.tx_buf) <= self.tx_idx:
                        # we are done
                        self.tx_state = ISOTP_IDLE
                        self.tx_done.set()
                        for cb in self.tx_callbacks:
                            cb()
                        return

                    if self.txfc_bs != 0 and self.tx_bs >= self.txfc_bs:
                        # stop and wait for FC
                        self.tx_state = ISOTP_WAIT_FC
                        self.tx_timeout_handle = TimeoutScheduler.schedule(
                            self.fc_timeout, self._tx_timer_handler)
                        return

                    if self.tx_gap == 0:
                        continue
                    else:
                        # stop and wait for tx gap
                        self.tx_timeout_handle = TimeoutScheduler.schedule(
                            self.tx_gap, self._tx_timer_handler)
                        return

    def on_recv(self, cf):
        # type: (Packet) -> None
        """Function that must be called every time a CAN frame is received, to
        advance the state machine."""

        data = bytes(cf.data)

        if len(data) < 2:
            return

        ae = 0
        if self.extended_rx_addr is not None:
            ae = 1
            if len(data) < 3:
                return
            if six.indexbytes(data, 0) != self.extended_rx_addr:
                return

        n_pci = six.indexbytes(data, ae) & 0xf0

        if n_pci == N_PCI_FC:
            with self.tx_mutex:
                self._recv_fc(data[ae:])
        elif n_pci == N_PCI_SF:
            with self.rx_mutex:
                self._recv_sf(data[ae:], cf.time)
        elif n_pci == N_PCI_FF:
            with self.rx_mutex:
                self._recv_ff(data[ae:], cf.time)
        elif n_pci == N_PCI_CF:
            with self.rx_mutex:
                self._recv_cf(data[ae:])

    def _recv_fc(self, data):
        # type: (bytes) -> None
        """Process a received 'Flow Control' frame"""
        if (self.tx_state != ISOTP_WAIT_FC and
                self.tx_state != ISOTP_WAIT_FIRST_FC):
            return

        if self.tx_timeout_handle is not None:
            self.tx_timeout_handle.cancel()
            self.tx_timeout_handle = None

        if len(data) < 3:
            self.tx_state = ISOTP_IDLE
            self.tx_exception = "CF frame discarded because it was too short"
            self.tx_done.set()
            raise Scapy_Exception(self.tx_exception)

        # get communication parameters only from the first FC frame
        if self.tx_state == ISOTP_WAIT_FIRST_FC:
            self.txfc_bs = six.indexbytes(data, 1)
            self.txfc_stmin = six.indexbytes(data, 2)

        if ((self.txfc_stmin > 0x7F) and
                ((self.txfc_stmin < 0xF1) or (self.txfc_stmin > 0xF9))):
            self.txfc_stmin = 0x7F

        if six.indexbytes(data, 2) <= 127:
            tx_gap = six.indexbytes(data, 2) / 1000.0
        elif 0xf1 <= six.indexbytes(data, 2) <= 0xf9:
            tx_gap = (six.indexbytes(data, 2) & 0x0f) / 10000.0
        else:
            tx_gap = 0
        self.tx_gap = tx_gap

        self.tx_state = ISOTP_WAIT_FC

        isotp_fc = six.indexbytes(data, 0) & 0x0f

        if isotp_fc == ISOTP_FC_CTS:
            self.tx_bs = 0
            self.tx_state = ISOTP_SENDING
            # start cyclic timer for sending CF frame
            self.tx_timeout_handle = TimeoutScheduler.schedule(
                self.tx_gap, self._tx_timer_handler)
        elif isotp_fc == ISOTP_FC_WT:
            # start timer to wait for next FC frame
            self.tx_state = ISOTP_WAIT_FC
            self.tx_timeout_handle = TimeoutScheduler.schedule(
                self.fc_timeout, self._tx_timer_handler)
        elif isotp_fc == ISOTP_FC_OVFLW:
            # overflow in receiver side
            self.tx_state = ISOTP_IDLE
            self.tx_exception = "Overflow happened at the receiver side"
            self.tx_done.set()
            raise Scapy_Exception(self.tx_exception)
        else:
            self.tx_state = ISOTP_IDLE
            self.tx_exception = "Unknown FC frame type"
            self.tx_done.set()
            raise Scapy_Exception(self.tx_exception)

    def _recv_sf(self, data, ts):
        # type: (bytes, Union[float, EDecimal]) -> None
        """Process a received 'Single Frame' frame"""
        if self.rx_timeout_handle is not None:
            self.rx_timeout_handle.cancel()
            self.rx_timeout_handle = None

        if self.rx_state != ISOTP_IDLE:
            if conf.verb > 2:
                warning("RX state was reset because single frame was received")
            self.rx_state = ISOTP_IDLE

        length = six.indexbytes(data, 0) & 0xf
        if len(data) - 1 < length:
            return

        msg = data[1:1 + length]
        self.rx_queue.send((msg, ts))
        for cb in self.rx_callbacks:
            cb(msg)

    def _recv_ff(self, data, ts):
        # type: (bytes, Union[float, EDecimal]) -> None
        """Process a received 'First Frame' frame"""
        if self.rx_timeout_handle is not None:
            self.rx_timeout_handle.cancel()
            self.rx_timeout_handle = None

        if self.rx_state != ISOTP_IDLE:
            if conf.verb > 2:
                warning("RX state was reset because first frame was received")
            self.rx_state = ISOTP_IDLE

        if len(data) < 7:
            return
        self.rx_ll_dl = len(data)

        # get the FF_DL
        self.rx_len = (six.indexbytes(data, 0) & 0x0f) * 256 + six.indexbytes(
            data, 1)
        ff_pci_sz = 2

        # Check for FF_DL escape sequence supporting 32 bit PDU length
        if self.rx_len == 0:
            # FF_DL = 0 => get real length from next 4 bytes
            self.rx_len = six.indexbytes(data, 2) << 24
            self.rx_len += six.indexbytes(data, 3) << 16
            self.rx_len += six.indexbytes(data, 4) << 8
            self.rx_len += six.indexbytes(data, 5)
            ff_pci_sz = 6

        # copy the first received data bytes
        data_bytes = data[ff_pci_sz:]
        self.rx_idx = len(data_bytes)
        self.rx_buf = data_bytes
        self.rx_ts = ts

        # initial setup for this pdu reception
        self.rx_sn = 1
        self.rx_state = ISOTP_WAIT_DATA

        # no creation of flow control frames
        if not self.listen_only:
            # send our first FC frame
            load = self.ea_hdr
            load += struct.pack("BBB", N_PCI_FC, self.rxfc_bs, self.rxfc_stmin)
            self.can_send(load)

        # wait for a CF
        self.rx_bs = 0
        self.rx_timeout_handle = TimeoutScheduler.schedule(
            self.cf_timeout, self._rx_timer_handler)

    def _recv_cf(self, data):
        # type: (bytes) -> None
        """Process a received 'Consecutive Frame' frame"""
        if self.rx_state != ISOTP_WAIT_DATA:
            return

        if self.rx_timeout_handle is not None:
            self.rx_timeout_handle.cancel()
            self.rx_timeout_handle = None

        # CFs are never longer than the FF
        if len(data) > self.rx_ll_dl:
            return

        # CFs have usually the LL_DL length
        if len(data) < self.rx_ll_dl:
            # this is only allowed for the last CF
            if self.rx_len - self.rx_idx > self.rx_ll_dl:
                if conf.verb > 2:
                    warning("Received a CF with insufficient length")
                return

        if six.indexbytes(data, 0) & 0x0f != self.rx_sn:
            # Wrong sequence number
            if conf.verb > 2:
                warning("RX state was reset because wrong sequence number was "
                        "received")
            self.rx_state = ISOTP_IDLE
            return

        if self.rx_buf is None:
            raise Scapy_Exception("rx_buf not filled with data!")

        self.rx_sn = (self.rx_sn + 1) % 16
        self.rx_buf += data[1:]
        self.rx_idx = len(self.rx_buf)

        if self.rx_idx >= self.rx_len:
            # we are done
            self.rx_buf = self.rx_buf[0:self.rx_len]
            self.rx_state = ISOTP_IDLE
            self.rx_queue.send((self.rx_buf, self.rx_ts))
            for cb in self.rx_callbacks:
                cb(self.rx_buf)
            self.rx_buf = None
            return

        # perform blocksize handling, if enabled
        if self.rxfc_bs != 0:
            self.rx_bs += 1

            # check if we reached the end of the block
            if self.rx_bs >= self.rxfc_bs and not self.listen_only:
                # send our FC frame
                load = self.ea_hdr
                load += struct.pack("BBB", N_PCI_FC, self.rxfc_bs,
                                    self.rxfc_stmin)
                self.can_send(load)

        # wait for another CF
        self.rx_timeout_handle = TimeoutScheduler.schedule(
            self.cf_timeout, self._rx_timer_handler)

    def begin_send(self, x):
        # type: (bytes) -> None
        """Begins sending an ISOTP message. This method does not block."""
        with self.tx_mutex:
            if self.tx_state != ISOTP_IDLE:
                raise Scapy_Exception("Socket is already sending, retry later")

            self.tx_done.clear()
            self.tx_exception = None
            self.tx_state = ISOTP_SENDING

            length = len(x)
            if length > ISOTP_MAX_DLEN_2015:
                raise Scapy_Exception("Too much data for ISOTP message")

            if len(self.ea_hdr) + length <= 7:
                # send a single frame
                data = self.ea_hdr
                data += struct.pack("B", length)
                data += x
                self.tx_state = ISOTP_IDLE
                self.can_send(data)
                self.tx_done.set()
                for cb in self.tx_callbacks:
                    cb()
                return

            # send the first frame
            data = self.ea_hdr
            if length > ISOTP_MAX_DLEN:
                data += struct.pack(">HI", 0x1000, length)
            else:
                data += struct.pack(">H", 0x1000 | length)
            load = x[0:8 - len(data)]
            data += load
            self.can_send(data)

            self.tx_buf = x
            self.tx_sn = 1
            self.tx_bs = 0
            self.tx_idx = len(load)

            self.tx_state = ISOTP_WAIT_FIRST_FC
            self.tx_timeout_handle = TimeoutScheduler.schedule(
                self.fc_timeout, self._tx_timer_handler)

    def send(self, p):
        # type: (bytes) -> None
        """Send an ISOTP frame and block until the message is sent or an error
        happens."""
        with self.send_mutex:
            self.begin_send(p)

            # Wait until the tx callback is called
            send_done = self.tx_done.wait(30)
            if self.tx_exception is not None:
                raise Scapy_Exception(self.tx_exception)
            if not send_done:
                raise Scapy_Exception("ISOTP send not completed in 30s")
            return

    def recv(self, timeout=None):
        # type: (Optional[int]) -> Optional[Tuple[bytes, Union[float, EDecimal]]]  # noqa: E501
        """Receive an ISOTP frame, blocking if none is available in the buffer
        for at most 'timeout' seconds."""

        try:
            return self.rx_queue.recv()
        except IndexError:
            return None
예제 #27
0
class SIPpSniffer(object):
    """
    Provides functionality to provide network capturing traffic based on a 
    network interface and capturing filter
    """
    def __init__(self, interface):
        self.__interface = interface
        self.__folder = None
        # Delegate implementation to scapy.AsyncSniffer.
        # We don't inherit from scapy.AsyncSniffer, because scapy.AsyncSniffer requires more arguments on construction,
        # than we are ready to provide on construction of SIPpSniffer.
        self.__impl = None
        logging.debug('Created sniffer for interface {0}'.format(interface))

    def start(self, filter, folder):
        logging.debug(
            'Starting sniffer for interface {0} and filter {1}'.format(
                self.__interface, filter))
        assert (not self.__impl)

        # Although Scapy docs say we could pass iface=None to sniff on all, this doesn't actually work.
        # Scapy listens on 1st available interface.
        # Therefore we need to collect list of interfaces by ourselves.
        ifaces = Network.SIPpNetwork.get_interfaces()
        if ifaces:

            class started:
                flag = False
                cond = threading.Condition()

            def started_callback():
                with started.cond:
                    started.flag = True
                    started.cond.notify()
                logging.debug('Started sniffer for interface {0}'.format(
                    self.__interface))

            self.__folder = folder
            self.__impl = AsyncSniffer(
                filter=filter,
                iface=list(
                    ifaces
                ),  # scapy waits for list(str), while we have set(str)
                started_callback=started_callback)
            self.__impl.start()
            # Need to wait for sniffing thread actually to start.
            # Otherwise we might start SIP dialogs when thread is not capturing yet,
            # and therefore miss to capture some initial messages.
            with started.cond:
                # Issue #35: Busy-loop wait.
                # Otherwise we could wait forever if Scapy threads terminates before setting the started.flag.
                # For example, due to exception inside it.
                while not started.flag:
                    started.cond.wait(1)
                    if not self.__impl.thread.is_alive():
                        raise Exception(
                            "Scapy thread has terminated while waiting for it to start"
                        )
        else:
            logging.debug(
                'Found no available real interfaces to sniff for dummy interface {0}'
                .format(self.__interface))

    def stop(self):
        if self.__impl:
            logging.debug('Stopping sniffer for interface {0}'.format(
                self.__interface))

            # Issue #1: We can't use just AsyncSniffer.stop(join=True),
            # because sometimes deadlock happens in cases when we stop immediately after start (see comment in Issue #1 for details).
            # Likely this is a bug in scapy, which doesn't expect this kind of usage.
            #
            # Initially here we just stopped AsyncSniffer with:
            # super().stop(join=True)
            #
            # I believe the issue was as follows:
            # Main thread:
            # - enters SIPpSniffer.start()
            # - starts Scapy thread and blocks on started.cond.wait()
            # - Main thread is paused
            # - Scapy thread is entered
            # Scapy thread:
            # - enters AsyncSniffer._run()
            # - calls started_callback(), which calls started.cond.notify().
            # - Scapy thread is paused
            # - Main thread is resumed
            # Main thread:
            # - continues its work, quickly finishes and calls AsyncSniffer.stop()
            # - AsyncSniffer.stop() sets AsyncSniffer.continue_sniff = False
            # - blocks on self.thread.join()
            # - Main thread is paused
            # - Scapy thread is resumed
            # Scapy thread:
            # - sets self.continue_sniff = True
            # - enters sniffing loop
            #
            # Therefore, Main thread cancels Scapy thread by setting self.continue_sniff = False,
            # but due to very short run time of Main thread, Scapy thread hasn't entered its loop.
            # Scapy thread is then resumed and sets this flag back to True and enters the loop.
            # The loop runs forever.
            #
            # To workaround this, we call AsyncSniffer.stop() continuosly, to reliably cancel Scapy thread.
            # This way, we continuosly set the self.continue_sniff = False, until the Scapy thread terminates.
            while True:
                self.__impl.stop(join=False)
                self.__impl.join(timeout=1)
                if self.__impl.thread.isAlive():
                    logging.debug(
                        'Sniffing thread is still alive for interface {0}, re-trying stopping'
                        .format(self.__interface))
                else:
                    break

            # Issue #58: Sort basing on timestamp
            time_sorted = sorted(self.__impl.results.res,
                                 key=lambda pkt: pkt.time)
            wrpcap(os.path.join(self.__folder, self.__interface + ".pcap"),
                   time_sorted)

            # restore defaults to be able to start() again
            self.__impl = None
            self.__folder = None
예제 #28
0
class DHCPWatcher(WatcherBase):
    """Class to watch dhcp requests."""
    def __init__(
        self,
        hass: HomeAssistant,
        address_data: dict[str, dict[str, str]],
        integration_matchers: list[DHCPMatcher],
    ) -> None:
        """Initialize class."""
        super().__init__(hass, address_data, integration_matchers)
        self._sniffer: AsyncSniffer | None = None
        self._started = threading.Event()

    async def async_stop(self) -> None:
        """Stop watching for new device trackers."""
        await self.hass.async_add_executor_job(self._stop)

    def _stop(self) -> None:
        """Stop the thread."""
        if self._started.is_set():
            assert self._sniffer is not None
            self._sniffer.stop()

    async def async_start(self) -> None:
        """Start watching for dhcp packets."""
        await self.hass.async_add_executor_job(self._start)

    def _start(self) -> None:
        """Start watching for dhcp packets."""
        # Local import because importing from scapy has side effects such as opening
        # sockets
        from scapy import (  # pylint: disable=import-outside-toplevel,unused-import  # noqa: F401
            arch, )
        from scapy.layers.dhcp import DHCP  # pylint: disable=import-outside-toplevel
        from scapy.layers.inet import IP  # pylint: disable=import-outside-toplevel
        from scapy.layers.l2 import Ether  # pylint: disable=import-outside-toplevel

        #
        # Importing scapy.sendrecv will cause a scapy resync which will
        # import scapy.arch.read_routes which will import scapy.sendrecv
        #
        # We avoid this circular import by importing arch above to ensure
        # the module is loaded and avoid the problem
        #
        from scapy.sendrecv import (  # pylint: disable=import-outside-toplevel
            AsyncSniffer, )

        def _handle_dhcp_packet(packet: Packet) -> None:
            """Process a dhcp packet."""
            if DHCP not in packet:
                return

            options_dict = _dhcp_options_as_dict(packet[DHCP].options)
            if options_dict.get(MESSAGE_TYPE) != DHCP_REQUEST:
                # Not a DHCP request
                return

            ip_address = options_dict.get(REQUESTED_ADDR) or cast(
                str, packet[IP].src)
            assert isinstance(ip_address, str)
            hostname = ""
            if (hostname_bytes := options_dict.get(HOSTNAME)) and isinstance(
                    hostname_bytes, bytes):
                with contextlib.suppress(AttributeError, UnicodeDecodeError):
                    hostname = hostname_bytes.decode()
            mac_address = _format_mac(cast(str, packet[Ether].src))

            if ip_address is not None and mac_address is not None:
                self.process_client(ip_address, hostname, mac_address)

        # disable scapy promiscuous mode as we do not need it
        conf.sniff_promisc = 0

        try:
            _verify_l2socket_setup(FILTER)
        except (Scapy_Exception, OSError) as ex:
            if os.geteuid() == 0:
                _LOGGER.error("Cannot watch for dhcp packets: %s", ex)
            else:
                _LOGGER.debug(
                    "Cannot watch for dhcp packets without root or CAP_NET_RAW: %s",
                    ex)
            return

        try:
            _verify_working_pcap(FILTER)
        except (Scapy_Exception, ImportError) as ex:
            _LOGGER.error(
                "Cannot watch for dhcp packets without a functional packet filter: %s",
                ex,
            )
            return

        self._sniffer = AsyncSniffer(
            filter=FILTER,
            started_callback=self._started.set,
            prn=_handle_dhcp_packet,
            store=0,
        )

        self._sniffer.start()
        if self._sniffer.thread:
            self._sniffer.thread.name = self.__class__.__name__
예제 #29
0
def asyncReceiveEthernetFrame(interface):
    # return AsyncSniffer(iface=interface, prn=lambda x: print('Da ist was reingekommen und zwar:' + str(x.show())))
    return AsyncSniffer(iface=interface, prn=lambda x: incomingFrameHandler(x))
예제 #30
0
파일: ping.py 프로젝트: RazCrimson/Lab_Main
class PingClient:
    '''Class representing the client'''
    def __init__(self, dst: str):
        self.dst = dst
        self.__last_received_sequence_number = -1
        self.__last_generated_sequence_number = -1
        self.__sniffer = AsyncSniffer(prn=self.__handle_filtered_pkts,
                                      filter=f'src {self.dst} and icmp')
        self.__received_count = 0

    def __generate_next_packet(self):
        '''function to generate the next pkt to transmit'''
        self.__last_generated_sequence_number += 1
        payload = ClientPayload("MY_PING",
                                self.__last_generated_sequence_number)
        pkt = IP(dst=self.dst) / ICMP() / str(payload)
        return pkt

    def __handle_filtered_pkts(self, pkt):
        '''captured pkts handler'''
        received_time = time.time()

        payload = pkt[ICMP].load.decode('utf-8')
        if payload.startswith('<"MY_PING", '):
            _, seq_no, sent_time = payload.strip('>').split(',')

            rtt = round((received_time - float(sent_time)) * 1000, 4)
            if rtt > 1000:
                return

            if self.__last_received_sequence_number + 1 == int(seq_no):
                print(
                    f'<{self.__last_generated_sequence_number}, {received_time}, {payload}, "Successfully_Received", {rtt} ms>'
                )
                self.__received_count += 1
            elif self.__last_received_sequence_number + 1 < int(seq_no):
                print(
                    f'<{self.__last_generated_sequence_number}, {payload}, "Received_out_of_order">'
                )
            else:
                return

        self.__last_received_sequence_number += 1

    def start_transmission(self, count):
        self.__sniffer.start()
        time.sleep(1)
        try:
            while count != 0:
                count -= 1
                pkt = self.__generate_next_packet()
                pkt_sent_time = time.time()
                send(pkt, verbose=False)

                if time.time() - pkt_sent_time < 1:
                    time.sleep(TIME_INTERVAL - time.time() + pkt_sent_time)

                if self.__last_received_sequence_number != self.__last_generated_sequence_number:
                    print(
                        f'<{self.__last_generated_sequence_number}, "Timed_Out">'
                    )
                    self.__last_received_sequence_number += 1

        except KeyboardInterrupt:
            print('\nPacket Transmission Terminated!')
        self.__sniffer.stop()
        packet_loss = round(
            ((self.__last_generated_sequence_number + 1 -
              self.__received_count) /
             (self.__last_generated_sequence_number + 1)) * 100, 2)
        print(
            f'Packet Loss: {packet_loss}% \t | Packets Sent : {self.__last_generated_sequence_number + 1} \t | Packets Received : {self.__received_count}'
        )