Ejemplo n.º 1
0
class Application(ApplicationBase):
    def __init__(self):
        self.app_print_copyright(APP_CR_HOLDERS)
        self.argv = self.parse_argv()

        # Set up signal handlers
        signal.signal(signal.SIGINT, self.sig_handler)

        # Configure logging
        self.app_init_logging(self.argv)

        # Open requested capture file
        if self.argv.output_file is not None:
            self.ddf = DATADumpFile(self.argv.output_file)

    def run(self):
        # Init DATA interface with TRX or L1
        if self.argv.conn_mode == "TRX":
            self.data_if = DATAInterface(self.argv.remote_addr,
                                         self.argv.base_port + 2,
                                         self.argv.bind_addr,
                                         self.argv.base_port + 102)
        elif self.argv.conn_mode == "L1":
            self.data_if = DATAInterface(self.argv.remote_addr,
                                         self.argv.base_port + 102,
                                         self.argv.bind_addr,
                                         self.argv.base_port + 2)

        # Init random burst generator
        burst_gen = RandBurstGen()

        # Init an empty DATA message
        if self.argv.conn_mode == "TRX":
            msg = DATAMSG_L12TRX()
        elif self.argv.conn_mode == "L1":
            msg = DATAMSG_TRX2L1()

        # Generate a random frame number or use provided one
        fn_init = msg.rand_fn() if self.argv.tdma_fn is None \
         else self.argv.tdma_fn

        # Send as much bursts as required
        for i in range(self.argv.burst_count):
            # Randomize the message header
            msg.rand_hdr()

            # Increase and set frame number
            msg.fn = (fn_init + i) % GSM_HYPERFRAME

            # Set timeslot number
            if self.argv.tdma_tn is not None:
                msg.tn = self.argv.tdma_tn

            # Set transmit power level
            if self.argv.pwr is not None:
                msg.pwr = self.argv.pwr

            # Set time of arrival
            if self.argv.toa is not None:
                msg.toa256 = int(float(self.argv.toa) * 256.0 + 0.5)
            elif self.argv.toa256 is not None:
                msg.toa256 = self.argv.toa256

            # Set RSSI
            if self.argv.rssi is not None:
                msg.rssi = self.argv.rssi

            # Generate a random burst
            if self.argv.burst_type == "NB":
                burst = burst_gen.gen_nb()
            elif self.argv.burst_type == "FB":
                burst = burst_gen.gen_fb()
            elif self.argv.burst_type == "SB":
                burst = burst_gen.gen_sb()
            elif self.argv.burst_type == "AB":
                burst = burst_gen.gen_ab()

            # Convert to soft-bits in case of TRX -> L1 message
            if self.argv.conn_mode == "L1":
                burst = msg.ubit2sbit(burst)

            # Set burst
            msg.burst = burst

            log.info("Sending %d/%d %s burst %s to %s..." %
                     (i + 1, self.argv.burst_count, self.argv.burst_type,
                      msg.desc_hdr(), self.argv.conn_mode))

            # Send message
            self.data_if.send_msg(msg)

            # Append a new message to the capture
            if self.argv.output_file is not None:
                self.ddf.append_msg(msg)

    def parse_argv(self):
        parser = argparse.ArgumentParser(
            prog="burst_gen",
            description="Auxiliary tool to generate and send random bursts")

        # Register common logging options
        self.app_reg_logging_options(parser)

        trx_group = parser.add_argument_group("TRX interface")
        trx_group.add_argument("-r",
                               "--remote-addr",
                               dest="remote_addr",
                               type=str,
                               default="127.0.0.1",
                               help="Set remote address (default %(default)s)")
        trx_group.add_argument("-b",
                               "--bind-addr",
                               dest="bind_addr",
                               type=str,
                               default="0.0.0.0",
                               help="Set bind address (default %(default)s)")
        trx_group.add_argument(
            "-p",
            "--base-port",
            dest="base_port",
            type=int,
            default=6700,
            help="Set base port number (default %(default)s)")
        trx_group.add_argument(
            "-m",
            "--conn-mode",
            dest="conn_mode",
            type=str,
            choices=["TRX", "L1"],
            default="TRX",
            help="Where to send bursts (default %(default)s)")
        trx_group.add_argument("-o",
                               "--output-file",
                               dest="output_file",
                               type=str,
                               help="Write bursts to a capture file")

        bg_group = parser.add_argument_group("Burst generation")
        bg_group.add_argument("-B",
                              "--burst-type",
                              dest="burst_type",
                              type=str,
                              choices=["NB", "FB", "SB", "AB"],
                              default="NB",
                              help="Random burst type (default %(default)s)")
        bg_group.add_argument(
            "-c",
            "--burst-count",
            metavar="N",
            dest="burst_count",
            type=int,
            default=1,
            help="How many bursts to send (default %(default)s)")
        bg_group.add_argument("-f",
                              "--frame-number",
                              metavar="FN",
                              dest="tdma_fn",
                              type=int,
                              help="Set TDMA frame number (default random)")
        bg_group.add_argument("-t",
                              "--timeslot",
                              metavar="TN",
                              dest="tdma_tn",
                              type=int,
                              choices=range(0, 8),
                              help="Set TDMA timeslot (default random)")

        bg_pwr_group = bg_group.add_mutually_exclusive_group()
        bg_pwr_group.add_argument("--pwr",
                                  metavar="dBm",
                                  dest="pwr",
                                  type=int,
                                  help="Set power level (default random)")
        bg_pwr_group.add_argument("--rssi",
                                  metavar="dBm",
                                  dest="rssi",
                                  type=int,
                                  help="Set RSSI (default random)")

        bg_toa_group = bg_group.add_mutually_exclusive_group()
        bg_toa_group.add_argument(
            "--toa",
            dest="toa",
            type=int,
            help="Set Timing of Arrival in symbols (default random)")
        bg_toa_group.add_argument(
            "--toa256",
            dest="toa256",
            type=int,
            help="Set Timing of Arrival in 1/256 symbol periods")

        return parser.parse_args()

    def sig_handler(self, signum, frame):
        log.info("Signal %d received" % signum)
        if signum is signal.SIGINT:
            sys.exit(0)
Ejemplo n.º 2
0
class Application:
	# Application variables
	remote_addr = "127.0.0.1"
	bind_addr = "0.0.0.0"
	base_port = 5700
	conn_mode = "TRX"
	output_file = None

	burst_type = None
	burst_count = 1

	# Common header fields
	fn = None
	tn = None

	# Message specific header fields
	toa256 = None
	rssi = None
	pwr = None

	def __init__(self):
		print_copyright(CR_HOLDERS)
		self.parse_argv()
		self.check_argv()

		# Set up signal handlers
		signal.signal(signal.SIGINT, self.sig_handler)

		# Open requested capture file
		if self.output_file is not None:
			self.ddf = DATADumpFile(self.output_file)

	def run(self):
		# Init DATA interface with TRX or L1
		if self.conn_mode == "TRX":
			self.data_if = DATAInterface(self.remote_addr, self.base_port + 2,
				self.bind_addr, self.base_port + 102)
		elif self.conn_mode == "L1":
			self.data_if = DATAInterface(self.remote_addr, self.base_port + 102,
				self.bind_addr, self.base_port + 2)

		# Init random burst generator
		burst_gen = RandBurstGen()

		# Init an empty DATA message
		if self.conn_mode == "TRX":
			msg = DATAMSG_L12TRX()
		elif self.conn_mode == "L1":
			msg = DATAMSG_TRX2L1()

		# Generate a random frame number or use provided one
		fn_init = msg.rand_fn() if self.fn is None else self.fn

		# Send as much bursts as required
		for i in range(self.burst_count):
			# Randomize the message header
			msg.rand_hdr()

			# Increase and set frame number
			msg.fn = (fn_init + i) % GSM_HYPERFRAME

			# Set timeslot number
			if self.tn is not None:
				msg.tn = self.tn

			# Set transmit power level
			if self.pwr is not None:
				msg.pwr = self.pwr

			# Set time of arrival
			if self.toa256 is not None:
				msg.toa256 = self.toa256

			# Set RSSI
			if self.rssi is not None:
				msg.rssi = self.rssi

			# Generate a random burst
			if self.burst_type == "NB":
				burst = burst_gen.gen_nb()
			elif self.burst_type == "FB":
				burst = burst_gen.gen_fb()
			elif self.burst_type == "SB":
				burst = burst_gen.gen_sb()
			elif self.burst_type == "AB":
				burst = burst_gen.gen_ab()

			# Convert to soft-bits in case of TRX -> L1 message
			if self.conn_mode == "L1":
				burst = msg.ubit2sbit(burst)

			# Set burst
			msg.burst = burst

			print("[i] Sending %d/%d %s burst %s to %s..."
				% (i + 1, self.burst_count, self.burst_type,
					msg.desc_hdr(), self.conn_mode))

			# Send message
			self.data_if.send_msg(msg)

			# Append a new message to the capture
			if self.output_file is not None:
				self.ddf.append_msg(msg)

	def print_help(self, msg = None):
		s  = " Usage: " + sys.argv[0] + " [options]\n\n" \
			 " Some help...\n" \
			 "  -h --help           this text\n\n"

		s += " TRX interface specific\n" \
			 "  -o --output-file    Write bursts to a capture file\n"        \
			 "  -m --conn-mode      Send bursts to: TRX (default) / L1\n"    \
			 "  -r --remote-addr    Set remote address (default %s)\n"       \
			 "  -b --bind-addr      Set local address (default %s)\n"        \
			 "  -p --base-port      Set base port number (default %d)\n\n"

		s += " Burst generation\n" \
			 "  -b --burst-type     Random burst type (NB, FB, SB, AB)\n"    \
			 "  -c --burst-count    How much bursts to send (default 1)\n"   \
			 "  -f --frame-number   Set frame number (default random)\n"     \
			 "  -t --timeslot       Set timeslot index (default random)\n"   \
			 "     --pwr            Set power level (default random)\n"      \
			 "     --rssi           Set RSSI (default random)\n"             \
			 "     --toa            Set ToA in symbols (default random)\n"   \
			 "     --toa256         Set ToA in 1/256 symbol periods\n"

		print(s % (self.remote_addr, self.bind_addr, self.base_port))

		if msg is not None:
			print(msg)

	def parse_argv(self):
		try:
			opts, args = getopt.getopt(sys.argv[1:],
				"o:m:r:b:p:b:c:f:t:h",
				[
					"help",
					"output-file="
					"conn-mode=",
					"remote-addr=",
					"bind-addr=",
					"base-port=",
					"burst-type=",
					"burst-count=",
					"frame-number=",
					"timeslot=",
					"rssi=",
					"toa=",
					"toa256=",
					"pwr=",
				])
		except getopt.GetoptError as err:
			self.print_help("[!] " + str(err))
			sys.exit(2)

		for o, v in opts:
			if o in ("-h", "--help"):
				self.print_help()
				sys.exit(2)

			elif o in ("-o", "--output-file"):
				self.output_file = v
			elif o in ("-m", "--conn-mode"):
				self.conn_mode = v
			elif o in ("-r", "--remote-addr"):
				self.remote_addr = v
			elif o in ("-b", "--bind-addr"):
				self.bind_addr = v
			elif o in ("-p", "--base-port"):
				self.base_port = int(v)

			elif o in ("-b", "--burst-type"):
				self.burst_type = v
			elif o in ("-c", "--burst-count"):
				self.burst_count = int(v)
			elif o in ("-f", "--frame-number"):
				self.fn = int(v)
			elif o in ("-t", "--timeslot"):
				self.tn = int(v)

			# Message specific header fields
			elif o == "--pwr":
				self.pwr = int(v)
			elif o == "--rssi":
				self.rssi = int(v)
			elif o == "--toa256":
				self.toa256 = int(v)
			elif o == "--toa":
				self.toa256 = int(float(v) * 256.0 + 0.5)

	def check_argv(self):
		# Check connection mode
		if self.conn_mode not in ("TRX", "L1"):
			self.print_help("[!] Unknown connection type")
			sys.exit(2)

		# Check connection mode
		if self.burst_type not in ("NB", "FB", "SB", "AB"):
			self.print_help("[!] Unknown burst type")
			sys.exit(2)

	def sig_handler(self, signum, frame):
		print("Signal %d received" % signum)
		if signum is signal.SIGINT:
			sys.exit(0)
Ejemplo n.º 3
0
class Application(ApplicationBase):
    # Counters
    cnt_burst_dropped_num = 0
    cnt_burst_num = 0

    cnt_frame_last = None
    cnt_frame_num = 0

    # Internal variables
    lo_trigger = False

    def __init__(self):
        self.app_print_copyright(APP_CR_HOLDERS)
        self.argv = self.parse_argv()

        # Configure logging
        self.app_init_logging(self.argv)

        # Open requested capture file
        if self.argv.output_file is not None:
            self.ddf = DATADumpFile(self.argv.output_file)

    def run(self):
        # Compose a list of permitted UDP ports
        rx_port_list = [
            "port %d" % (port + 102) for port in self.argv.base_ports
        ]
        tx_port_list = [
            "port %d" % (port + 2) for port in self.argv.base_ports
        ]

        # Arguments to be passed to scapy.all.sniff()
        sniff_args = {
            "filter":
            "udp and (%s)" % " or ".join(rx_port_list + tx_port_list),
            "prn": self.pkt_handler,
            "store": 0,
        }

        if self.argv.cap_file is not None:
            log.info("Reading packets from '%s'..." % self.argv.cap_file)
            sniff_args["offline"] = self.argv.cap_file
        else:
            log.info("Listening on interface '%s'..." % self.argv.sniff_if)
            sniff_args["iface"] = self.argv.sniff_if

        if self.argv.cap_filter is not None:
            log.info("Using additional capture filter '%s'" %
                     self.argv.cap_filter)
            sniff_args["filter"] += " and (%s)" % self.argv.cap_filter

        # Start sniffing...
        scapy.all.sniff(**sniff_args)

        # Scapy registers its own signal handler
        self.shutdown()

    def pkt_handler(self, ether):
        # Prevent loopback packet duplication
        if self.argv.sniff_if == "lo" and self.argv.cap_file is None:
            self.lo_trigger = not self.lo_trigger
            if not self.lo_trigger:
                return

        # Extract a TRX payload
        ip = ether.payload
        udp = ip.payload
        trx = udp.payload

        # Convert to bytearray
        msg_raw = bytearray(trx.load)

        # Determine a burst direction (L1 <-> TRX)
        l12trx = udp.sport > udp.dport

        # Create an empty DATA message
        msg = DATAMSG_L12TRX() if l12trx else DATAMSG_TRX2L1()

        # Attempt to parse the payload as a DATA message
        try:
            msg.parse_msg(msg_raw)
            msg.validate()
        except ValueError as e:
            desc = msg.desc_hdr()
            if desc == "":
                desc = "parsing error"
            log.warning("Ignoring an incorrect message (%s): %s" % (desc, e))
            self.cnt_burst_dropped_num += 1
            return

        # Poke burst pass filter
        if not self.burst_pass_filter(msg):
            self.cnt_burst_dropped_num += 1
            return

        # Debug print
        log.debug("%s burst: %s" \
         % ("L1 -> TRX" if l12trx else "TRX -> L1", msg.desc_hdr()))

        # Poke message handler
        self.msg_handle(msg)

        # Poke burst counter
        rc = self.burst_count(msg.fn, msg.tn)
        if rc is True:
            self.shutdown()

    def burst_pass_filter(self, msg):
        # Direction filter
        if self.argv.direction is not None:
            if self.argv.direction == "TRX":  # L1 -> TRX
                if not isinstance(msg, DATAMSG_L12TRX):
                    return False
            elif self.argv.direction == "L1":  # TRX -> L1
                if not isinstance(msg, DATAMSG_TRX2L1):
                    return False

        # Timeslot filter
        if self.argv.pf_tn is not None:
            if msg.tn != self.argv.pf_tn:
                return False

        # Frame number filter
        if self.argv.pf_fn_lt is not None:
            if msg.fn > self.argv.pf_fn_lt:
                return False
        if self.argv.pf_fn_gt is not None:
            if msg.fn < self.argv.pf_fn_gt:
                return False

        # Message type specific filtering
        if isinstance(msg, DATAMSG_TRX2L1):
            # NOPE.ind filter
            if not self.argv.pf_nope_ind and msg.nope_ind:
                return False

            # RSSI filter
            if self.argv.pf_rssi_min is not None:
                if msg.rssi < self.argv.pf_rssi_min:
                    return False
            if self.argv.pf_rssi_max is not None:
                if msg.rssi > self.argv.pf_rssi_max:
                    return False

        # Burst passed ;)
        return True

    def msg_handle(self, msg):
        if self.argv.verbose:
            print(msg.burst)

        # Append a new message to the capture
        if self.argv.output_file is not None:
            self.ddf.append_msg(msg)

    def burst_count(self, fn, tn):
        # Update frame counter
        if self.cnt_frame_last is None:
            self.cnt_frame_last = fn
            self.cnt_frame_num += 1
        else:
            if fn != self.cnt_frame_last:
                self.cnt_frame_num += 1

        # Update burst counter
        self.cnt_burst_num += 1

        # Stop sniffing after N bursts
        if self.argv.burst_count is not None:
            if self.cnt_burst_num == self.argv.burst_count:
                log.info("Collected required amount of bursts")
                return True

        # Stop sniffing after N frames
        if self.argv.frame_count is not None:
            if self.cnt_frame_num == self.argv.frame_count:
                log.info("Collected required amount of frames")
                return True

        return False

    def shutdown(self):
        log.info("Shutting down...")

        # Print statistics
        log.info("%u bursts handled, %u dropped" \
         % (self.cnt_burst_num, self.cnt_burst_dropped_num))

        # Exit
        sys.exit(0)

    def parse_argv(self):
        parser = argparse.ArgumentParser(
            prog="trx_sniff", description="Scapy-based TRX interface sniffer")

        parser.add_argument("-v",
                            "--verbose",
                            dest="verbose",
                            action="store_true",
                            help="Print burst bits to stdout")

        # Register common logging options
        self.app_reg_logging_options(parser)

        trx_group = parser.add_argument_group("TRX interface")
        trx_group.add_argument(
            "-p",
            "--base-port",
            "--base-ports",
            dest="base_ports",
            type=int,
            metavar="PORT",
            default=[5700, 6700],
            nargs="*",
            help="Set base port number (default %(default)s)")
        trx_group.add_argument("-o",
                               "--output-file",
                               metavar="FILE",
                               dest="output_file",
                               type=str,
                               help="Write bursts to a capture file")

        input_group = trx_group.add_mutually_exclusive_group()
        input_group.add_argument(
            "-i",
            "--sniff-interface",
            dest="sniff_if",
            type=str,
            default="lo",
            metavar="IF",
            help="Set network interface (default '%(default)s')")
        input_group.add_argument("-r",
                                 "--capture-file",
                                 dest="cap_file",
                                 type=str,
                                 metavar="FILE",
                                 help="Read packets from a PCAP file")

        trx_group.add_argument(
            "-f",
            "--capture-filter",
            dest="cap_filter",
            type=str,
            metavar="FILTER",
            help="Set additional capture filter (e.g. 'host 192.168.1.2')")

        cnt_group = parser.add_argument_group("Count limitations (optional)")
        cnt_group.add_argument("--frame-count",
                               metavar="N",
                               dest="frame_count",
                               type=int,
                               help="Stop after sniffing N frames")
        cnt_group.add_argument("--burst-count",
                               metavar="N",
                               dest="burst_count",
                               type=int,
                               help="Stop after sniffing N bursts")

        pf_group = parser.add_argument_group("Filtering (optional)")
        pf_group.add_argument("--direction",
                              dest="direction",
                              type=str,
                              choices=["TRX", "L1"],
                              help="Burst direction")
        pf_group.add_argument("--timeslot",
                              metavar="TN",
                              dest="pf_tn",
                              type=int,
                              choices=range(0, 8),
                              help="TDMA timeslot number (equal TN)")
        pf_group.add_argument("--frame-num-lt",
                              metavar="FN",
                              dest="pf_fn_lt",
                              type=int,
                              help="TDMA frame number (lower than FN)")
        pf_group.add_argument("--frame-num-gt",
                              metavar="FN",
                              dest="pf_fn_gt",
                              type=int,
                              help="TDMA frame number (greater than FN)")
        pf_group.add_argument("--no-nope-ind",
                              dest="pf_nope_ind",
                              action="store_false",
                              help="Ignore NOPE.ind (NOPE / IDLE indications)")
        pf_group.add_argument("--rssi-min",
                              metavar="RSSI",
                              dest="pf_rssi_min",
                              type=int,
                              help="Minimum RSSI value (e.g. -75)")
        pf_group.add_argument("--rssi-max",
                              metavar="RSSI",
                              dest="pf_rssi_max",
                              type=int,
                              help="Maximum RSSI value (e.g. -50)")

        return parser.parse_args()
Ejemplo n.º 4
0
class Application(ApplicationBase):
	def __init__(self):
		self.app_print_copyright(APP_CR_HOLDERS)
		self.argv = self.parse_argv()

		# Set up signal handlers
		signal.signal(signal.SIGINT, self.sig_handler)

		# Configure logging
		self.app_init_logging(self.argv)

		# Open requested capture file
		if self.argv.output_file is not None:
			self.ddf = DATADumpFile(self.argv.output_file)

	def run(self):
		# Init DATA interface with TRX or L1
		if self.argv.conn_mode == "TRX":
			self.data_if = DATAInterface(
				self.argv.remote_addr, self.argv.base_port + 2,
				self.argv.bind_addr, self.argv.base_port + 102)
		elif self.argv.conn_mode == "L1":
			self.data_if = DATAInterface(
				self.argv.remote_addr, self.argv.base_port + 102,
				self.argv.bind_addr, self.argv.base_port + 2)

		# Init random burst generator
		burst_gen = RandBurstGen()

		# Init an empty DATA message
		if self.argv.conn_mode == "TRX":
			msg = DATAMSG_L12TRX()
		elif self.argv.conn_mode == "L1":
			msg = DATAMSG_TRX2L1()

		# Generate a random frame number or use provided one
		fn_init = msg.rand_fn() if self.argv.tdma_fn is None \
			else self.argv.tdma_fn

		# Send as much bursts as required
		for i in range(self.argv.burst_count):
			# Randomize the message header
			msg.rand_hdr()

			# Increase and set frame number
			msg.fn = (fn_init + i) % GSM_HYPERFRAME

			# Set timeslot number
			if self.argv.tdma_tn is not None:
				msg.tn = self.argv.tdma_tn

			# Set transmit power level
			if self.argv.pwr is not None:
				msg.pwr = self.argv.pwr

			# Set time of arrival
			if self.argv.toa is not None:
				msg.toa256 = int(float(self.argv.toa) * 256.0 + 0.5)
			elif self.argv.toa256 is not None:
				msg.toa256 = self.argv.toa256

			# Set RSSI
			if self.argv.rssi is not None:
				msg.rssi = self.argv.rssi

			# Generate a random burst
			if self.argv.burst_type == "NB":
				burst = burst_gen.gen_nb()
			elif self.argv.burst_type == "FB":
				burst = burst_gen.gen_fb()
			elif self.argv.burst_type == "SB":
				burst = burst_gen.gen_sb()
			elif self.argv.burst_type == "AB":
				burst = burst_gen.gen_ab()

			# Convert to soft-bits in case of TRX -> L1 message
			if self.argv.conn_mode == "L1":
				burst = msg.ubit2sbit(burst)

			# Set burst
			msg.burst = burst

			log.info("Sending %d/%d %s burst %s to %s..."
				% (i + 1, self.argv.burst_count, self.argv.burst_type,
					msg.desc_hdr(), self.argv.conn_mode))

			# Send message
			self.data_if.send_msg(msg)

			# Append a new message to the capture
			if self.argv.output_file is not None:
				self.ddf.append_msg(msg)

	def parse_argv(self):
		parser = argparse.ArgumentParser(prog = "burst_gen",
			description = "Auxiliary tool to generate and send random bursts")

		# Register common logging options
		self.app_reg_logging_options(parser)

		trx_group = parser.add_argument_group("TRX interface")
		trx_group.add_argument("-r", "--remote-addr",
			dest = "remote_addr", type = str, default = "127.0.0.1",
			help = "Set remote address (default %(default)s)")
		trx_group.add_argument("-b", "--bind-addr",
			dest = "bind_addr", type = str, default = "0.0.0.0",
			help = "Set bind address (default %(default)s)")
		trx_group.add_argument("-p", "--base-port",
			dest = "base_port", type = int, default = 6700,
			help = "Set base port number (default %(default)s)")
		trx_group.add_argument("-m", "--conn-mode",
			dest = "conn_mode", type = str,
			choices = ["TRX", "L1"], default = "TRX",
			help = "Where to send bursts (default %(default)s)")
		trx_group.add_argument("-o", "--output-file",
			dest = "output_file", type = str,
			help = "Write bursts to a capture file")

		bg_group = parser.add_argument_group("Burst generation")
		bg_group.add_argument("-B", "--burst-type",
			dest = "burst_type", type = str,
			choices = ["NB", "FB", "SB", "AB"], default = "NB",
			help = "Random burst type (default %(default)s)")
		bg_group.add_argument("-c", "--burst-count", metavar = "N",
			dest = "burst_count", type = int, default = 1,
			help = "How many bursts to send (default %(default)s)")
		bg_group.add_argument("-f", "--frame-number", metavar = "FN",
			dest = "tdma_fn", type = int,
			help = "Set TDMA frame number (default random)")
		bg_group.add_argument("-t", "--timeslot", metavar = "TN",
			dest = "tdma_tn", type = int, choices = range(0, 8),
			help = "Set TDMA timeslot (default random)")

		bg_pwr_group = bg_group.add_mutually_exclusive_group()
		bg_pwr_group.add_argument("--pwr", metavar = "dBm",
			dest = "pwr", type = int,
			help = "Set power level (default random)")
		bg_pwr_group.add_argument("--rssi", metavar = "dBm",
			dest = "rssi", type = int,
			help = "Set RSSI (default random)")

		bg_toa_group = bg_group.add_mutually_exclusive_group()
		bg_toa_group.add_argument("--toa",
			dest = "toa", type = int,
			help = "Set Timing of Arrival in symbols (default random)")
		bg_toa_group.add_argument("--toa256",
			dest = "toa256", type = int,
			help = "Set Timing of Arrival in 1/256 symbol periods")

		return parser.parse_args()

	def sig_handler(self, signum, frame):
		log.info("Signal %d received" % signum)
		if signum is signal.SIGINT:
			sys.exit(0)
Ejemplo n.º 5
0
class Application:
	# Application variables
	sniff_interface = "lo"
	sniff_base_port = 5700
	print_bursts = False
	output_file = None

	# Counters
	cnt_burst_dropped_num = 0
	cnt_burst_break = None
	cnt_burst_num = 0

	cnt_frame_break = None
	cnt_frame_last = None
	cnt_frame_num = 0

	# Burst direction fliter
	bf_dir_l12trx = None

	# Timeslot number filter
	bf_tn_val = None

	# Frame number fliter
	bf_fn_lt = None
	bf_fn_gt = None

	# Internal variables
	lo_trigger = False

	def __init__(self):
		print_copyright(CR_HOLDERS)
		self.parse_argv()

		# Open requested capture file
		if self.output_file is not None:
			self.ddf = DATADumpFile(self.output_file)

	def run(self):
		# Compose a packet filter
		pkt_filter = "udp and (port %d or port %d)" \
			% (self.sniff_base_port + 2, self.sniff_base_port + 102)

		print("[i] Listening on interface '%s'..." % self.sniff_interface)

		# Start sniffing...
		scapy.all.sniff(iface = self.sniff_interface, store = 0,
			filter = pkt_filter, prn = self.pkt_handler)

		# Scapy registers its own signal handler
		self.shutdown()

	def pkt_handler(self, ether):
		# Prevent loopback packet duplication
		if self.sniff_interface == "lo":
			self.lo_trigger = not self.lo_trigger
			if not self.lo_trigger:
				return

		# Extract a TRX payload
		ip = ether.payload
		udp = ip.payload
		trx = udp.payload

		# Convert to bytearray
		msg_raw = bytearray(str(trx))

		# Determine a burst direction (L1 <-> TRX)
		l12trx = udp.sport > udp.dport

		# Create an empty DATA message
		msg = DATAMSG_L12TRX() if l12trx else DATAMSG_TRX2L1()

		# Attempt to parse the payload as a DATA message
		try:
			msg.parse_msg(msg_raw)
		except:
			print("[!] Failed to parse message, dropping...")
			self.cnt_burst_dropped_num += 1
			return

		# Poke burst pass filter
		rc = self.burst_pass_filter(l12trx, msg.fn, msg.tn)
		if rc is False:
			self.cnt_burst_dropped_num += 1
			return

		# Debug print
		print("[i] %s burst: %s" \
			% ("L1 -> TRX" if l12trx else "TRX -> L1", msg.desc_hdr()))

		# Poke message handler
		self.msg_handle(msg)

		# Poke burst counter
		rc = self.burst_count(msg.fn, msg.tn)
		if rc is True:
			self.shutdown()

	def burst_pass_filter(self, l12trx, fn, tn):
		# Direction filter
		if self.bf_dir_l12trx is not None:
			if l12trx != self.bf_dir_l12trx:
				return False

		# Timeslot filter
		if self.bf_tn_val is not None:
			if tn != self.bf_tn_val:
				return False

		# Frame number filter
		if self.bf_fn_lt is not None:
			if fn > self.bf_fn_lt:
				return False
		if self.bf_fn_gt is not None:
			if fn < self.bf_fn_gt:
				return False

		# Burst passed ;)
		return True

	def msg_handle(self, msg):
		if self.print_bursts:
			print(msg.burst)

		# Append a new message to the capture
		if self.output_file is not None:
			self.ddf.append_msg(msg)

	def burst_count(self, fn, tn):
		# Update frame counter
		if self.cnt_frame_last is None:
			self.cnt_frame_last = fn
			self.cnt_frame_num += 1
		else:
			if fn != self.cnt_frame_last:
				self.cnt_frame_num += 1

		# Update burst counter
		self.cnt_burst_num += 1

		# Stop sniffing after N bursts
		if self.cnt_burst_break is not None:
			if self.cnt_burst_num == self.cnt_burst_break:
				print("[i] Collected required amount of bursts")
				return True

		# Stop sniffing after N frames
		if self.cnt_frame_break is not None:
			if self.cnt_frame_num == self.cnt_frame_break:
				print("[i] Collected required amount of frames")
				return True

		return False

	def shutdown(self):
		print("[i] Shutting down...")

		# Print statistics
		print("[i] %u bursts handled, %u dropped" \
			% (self.cnt_burst_num, self.cnt_burst_dropped_num))

		# Exit
		sys.exit(0)

	def print_help(self, msg = None):
		s  = " Usage: " + sys.argv[0] + " [options]\n\n" \
			 " Some help...\n" \
			 "  -h --help              this text\n\n"

		s += " Sniffing options\n" \
			 "  -i --sniff-interface   Set network interface (default '%s')\n"  \
			 "  -p --sniff-base-port   Set base port number (default %d)\n\n"

		s += " Processing (no processing by default)\n" \
			 "  -o --output-file       Write bursts to file\n"          \
			 "  -v --print-bits        Print burst bits to stdout\n\n"  \

		s += " Count limitations (disabled by default)\n" \
			 "  --frame-count   NUM    Stop after sniffing NUM frames\n"  \
			 "  --burst-count   NUM    Stop after sniffing NUM bursts\n\n"

		s += " Filtering (disabled by default)\n" \
			 "  --direction     DIR    Burst direction: L12TRX or TRX2L1\n"  \
			 "  --timeslot      NUM    TDMA timeslot number [0..7]\n"        \
			 "  --frame-num-lt  NUM    TDMA frame number lower than NUM\n"   \
			 "  --burst-num-gt  NUM    TDMA frame number greater than NUM\n"

		print(s % (self.sniff_interface, self.sniff_base_port))

		if msg is not None:
			print(msg)

	def parse_argv(self):
		try:
			opts, args = getopt.getopt(sys.argv[1:],
				"i:p:o:v:h", ["help", "sniff-interface=", "sniff-base-port=",
					"frame-count=", "burst-count=", "direction=",
					"timeslot=", "frame-num-lt=", "frame-num-gt=",
					"output-file=", "print-bits"])
		except getopt.GetoptError as err:
			self.print_help("[!] " + str(err))
			sys.exit(2)

		for o, v in opts:
			if o in ("-h", "--help"):
				self.print_help()
				sys.exit(2)

			elif o in ("-i", "--sniff-interface"):
				self.sniff_interface = v
			elif o in ("-p", "--sniff-base-port"):
				self.sniff_base_port = int(v)

			elif o in ("-o", "--output-file"):
				self.output_file = v
			elif o in ("-v", "--print-bits"):
				self.print_bursts = True

			# Break counters
			elif o == "--frame-count":
				self.cnt_frame_break = int(v)
			elif o == "--burst-count":
				self.cnt_burst_break = int(v)

			# Direction filter
			elif o == "--direction":
				if v == "L12TRX":
					self.bf_dir_l12trx = True
				elif v == "TRX2L1":
					self.bf_dir_l12trx = False
				else:
					self.print_help("[!] Wrong direction argument")
					sys.exit(2)

			# Timeslot pass filter
			elif o == "--timeslot":
				self.bf_tn_val = int(v)
				if self.bf_tn_val < 0 or self.bf_tn_val > 7:
					self.print_help("[!] Wrong timeslot value")
					sys.exit(2)

			# Frame number pass filter
			elif o == "--frame-num-lt":
				self.bf_fn_lt = int(v)
			elif o == "--frame-num-gt":
				self.bf_fn_gt = int(v)
Ejemplo n.º 6
0
class Application(ApplicationBase):
    # Counters
    cnt_burst_dropped_num = 0
    cnt_burst_num = 0

    cnt_frame_last = None
    cnt_frame_num = 0

    # Internal variables
    lo_trigger = False

    def __init__(self):
        self.app_print_copyright(APP_CR_HOLDERS)
        self.argv = self.parse_argv()

        # Configure logging
        self.app_init_logging(self.argv)

        # Open requested capture file
        if self.argv.output_file is not None:
            self.ddf = DATADumpFile(self.argv.output_file)

    def run(self):
        # Compose a packet filter
        pkt_filter = "udp and (port %d or port %d)" \
         % (self.argv.base_port + 2, self.argv.base_port + 102)

        log.info("Listening on interface '%s'..." % self.argv.sniff_if)

        # Start sniffing...
        scapy.all.sniff(iface=self.argv.sniff_if,
                        store=0,
                        filter=pkt_filter,
                        prn=self.pkt_handler)

        # Scapy registers its own signal handler
        self.shutdown()

    def pkt_handler(self, ether):
        # Prevent loopback packet duplication
        if self.argv.sniff_if == "lo":
            self.lo_trigger = not self.lo_trigger
            if not self.lo_trigger:
                return

        # Extract a TRX payload
        ip = ether.payload
        udp = ip.payload
        trx = udp.payload

        # Convert to bytearray
        msg_raw = bytearray(str(trx))

        # Determine a burst direction (L1 <-> TRX)
        l12trx = udp.sport > udp.dport

        # Create an empty DATA message
        msg = DATAMSG_L12TRX() if l12trx else DATAMSG_TRX2L1()

        # Attempt to parse the payload as a DATA message
        try:
            msg.parse_msg(msg_raw)
        except:
            log.warning("Failed to parse message, dropping...")
            self.cnt_burst_dropped_num += 1
            return

        # Poke burst pass filter
        rc = self.burst_pass_filter(l12trx, msg.fn, msg.tn)
        if rc is False:
            self.cnt_burst_dropped_num += 1
            return

        # Debug print
        log.debug("%s burst: %s" \
         % ("L1 -> TRX" if l12trx else "TRX -> L1", msg.desc_hdr()))

        # Poke message handler
        self.msg_handle(msg)

        # Poke burst counter
        rc = self.burst_count(msg.fn, msg.tn)
        if rc is True:
            self.shutdown()

    def burst_pass_filter(self, l12trx, fn, tn):
        # Direction filter
        if self.argv.direction is not None:
            if self.argv.direction == "TRX" and not l12trx:
                return False
            elif self.argv.direction == "L1" and l12trx:
                return False

        # Timeslot filter
        if self.argv.pf_tn is not None:
            if tn != self.argv.pf_tn:
                return False

        # Frame number filter
        if self.argv.pf_fn_lt is not None:
            if fn > self.argv.pf_fn_lt:
                return False
        if self.argv.pf_fn_gt is not None:
            if fn < self.argv.pf_fn_gt:
                return False

        # Burst passed ;)
        return True

    def msg_handle(self, msg):
        if self.argv.verbose:
            print(msg.burst)

        # Append a new message to the capture
        if self.argv.output_file is not None:
            self.ddf.append_msg(msg)

    def burst_count(self, fn, tn):
        # Update frame counter
        if self.cnt_frame_last is None:
            self.cnt_frame_last = fn
            self.cnt_frame_num += 1
        else:
            if fn != self.cnt_frame_last:
                self.cnt_frame_num += 1

        # Update burst counter
        self.cnt_burst_num += 1

        # Stop sniffing after N bursts
        if self.argv.burst_count is not None:
            if self.cnt_burst_num == self.argv.burst_count:
                log.info("Collected required amount of bursts")
                return True

        # Stop sniffing after N frames
        if self.argv.frame_count is not None:
            if self.cnt_frame_num == self.argv.frame_count:
                log.info("Collected required amount of frames")
                return True

        return False

    def shutdown(self):
        log.info("Shutting down...")

        # Print statistics
        log.info("%u bursts handled, %u dropped" \
         % (self.cnt_burst_num, self.cnt_burst_dropped_num))

        # Exit
        sys.exit(0)

    def parse_argv(self):
        parser = argparse.ArgumentParser(
            prog="trx_sniff", description="Scapy-based TRX interface sniffer")

        parser.add_argument("-v",
                            "--verbose",
                            dest="verbose",
                            action="store_true",
                            help="Print burst bits to stdout")

        # Register common logging options
        self.app_reg_logging_options(parser)

        trx_group = parser.add_argument_group("TRX interface")
        trx_group.add_argument(
            "-i",
            "--sniff-interface",
            dest="sniff_if",
            type=str,
            default="lo",
            metavar="IF",
            help="Set network interface (default '%(default)s')")
        trx_group.add_argument(
            "-p",
            "--base-port",
            dest="base_port",
            type=int,
            default=6700,
            help="Set base port number (default %(default)s)")
        trx_group.add_argument("-o",
                               "--output-file",
                               metavar="FILE",
                               dest="output_file",
                               type=str,
                               help="Write bursts to a capture file")

        cnt_group = parser.add_argument_group("Count limitations (optional)")
        cnt_group.add_argument("--frame-count",
                               metavar="N",
                               dest="frame_count",
                               type=int,
                               help="Stop after sniffing N frames")
        cnt_group.add_argument("--burst-count",
                               metavar="N",
                               dest="burst_count",
                               type=int,
                               help="Stop after sniffing N bursts")

        pf_group = parser.add_argument_group("Filtering (optional)")
        pf_group.add_argument("--direction",
                              dest="direction",
                              type=str,
                              choices=["TRX", "L1"],
                              help="Burst direction")
        pf_group.add_argument("--timeslot",
                              metavar="TN",
                              dest="pf_tn",
                              type=int,
                              choices=range(0, 8),
                              help="TDMA timeslot number (equal TN)")
        pf_group.add_argument("--frame-num-lt",
                              metavar="FN",
                              dest="pf_fn_lt",
                              type=int,
                              help="TDMA frame number (lower than FN)")
        pf_group.add_argument("--frame-num-gt",
                              metavar="FN",
                              dest="pf_fn_gt",
                              type=int,
                              help="TDMA frame number (greater than FN)")

        return parser.parse_args()
Ejemplo n.º 7
0
class Application(ApplicationBase):
	# Counters
	cnt_burst_dropped_num = 0
	cnt_burst_num = 0

	cnt_frame_last = None
	cnt_frame_num = 0

	# Internal variables
	lo_trigger = False

	def __init__(self):
		self.app_print_copyright(APP_CR_HOLDERS)
		self.argv = self.parse_argv()

		# Configure logging
		self.app_init_logging(self.argv)

		# Open requested capture file
		if self.argv.output_file is not None:
			self.ddf = DATADumpFile(self.argv.output_file)

	def run(self):
		# Compose a packet filter
		pkt_filter = "udp and (port %d or port %d)" \
			% (self.argv.base_port + 2, self.argv.base_port + 102)

		log.info("Listening on interface '%s'..." % self.argv.sniff_if)

		# Start sniffing...
		scapy.all.sniff(iface = self.argv.sniff_if, store = 0,
			filter = pkt_filter, prn = self.pkt_handler)

		# Scapy registers its own signal handler
		self.shutdown()

	def pkt_handler(self, ether):
		# Prevent loopback packet duplication
		if self.argv.sniff_if == "lo":
			self.lo_trigger = not self.lo_trigger
			if not self.lo_trigger:
				return

		# Extract a TRX payload
		ip = ether.payload
		udp = ip.payload
		trx = udp.payload

		# Convert to bytearray
		msg_raw = bytearray(str(trx))

		# Determine a burst direction (L1 <-> TRX)
		l12trx = udp.sport > udp.dport

		# Create an empty DATA message
		msg = DATAMSG_L12TRX() if l12trx else DATAMSG_TRX2L1()

		# Attempt to parse the payload as a DATA message
		try:
			msg.parse_msg(msg_raw)
		except:
			log.warning("Failed to parse message, dropping...")
			self.cnt_burst_dropped_num += 1
			return

		# Poke burst pass filter
		rc = self.burst_pass_filter(l12trx, msg.fn, msg.tn)
		if rc is False:
			self.cnt_burst_dropped_num += 1
			return

		# Debug print
		log.debug("%s burst: %s" \
			% ("L1 -> TRX" if l12trx else "TRX -> L1", msg.desc_hdr()))

		# Poke message handler
		self.msg_handle(msg)

		# Poke burst counter
		rc = self.burst_count(msg.fn, msg.tn)
		if rc is True:
			self.shutdown()

	def burst_pass_filter(self, l12trx, fn, tn):
		# Direction filter
		if self.argv.direction is not None:
			if self.argv.direction == "TRX" and not l12trx:
				return False
			elif self.argv.direction == "L1" and l12trx:
				return False

		# Timeslot filter
		if self.argv.pf_tn is not None:
			if tn != self.argv.pf_tn:
				return False

		# Frame number filter
		if self.argv.pf_fn_lt is not None:
			if fn > self.argv.pf_fn_lt:
				return False
		if self.argv.pf_fn_gt is not None:
			if fn < self.argv.pf_fn_gt:
				return False

		# Burst passed ;)
		return True

	def msg_handle(self, msg):
		if self.argv.verbose:
			print(msg.burst)

		# Append a new message to the capture
		if self.argv.output_file is not None:
			self.ddf.append_msg(msg)

	def burst_count(self, fn, tn):
		# Update frame counter
		if self.cnt_frame_last is None:
			self.cnt_frame_last = fn
			self.cnt_frame_num += 1
		else:
			if fn != self.cnt_frame_last:
				self.cnt_frame_num += 1

		# Update burst counter
		self.cnt_burst_num += 1

		# Stop sniffing after N bursts
		if self.argv.burst_count is not None:
			if self.cnt_burst_num == self.argv.burst_count:
				log.info("Collected required amount of bursts")
				return True

		# Stop sniffing after N frames
		if self.argv.frame_count is not None:
			if self.cnt_frame_num == self.argv.frame_count:
				log.info("Collected required amount of frames")
				return True

		return False

	def shutdown(self):
		log.info("Shutting down...")

		# Print statistics
		log.info("%u bursts handled, %u dropped" \
			% (self.cnt_burst_num, self.cnt_burst_dropped_num))

		# Exit
		sys.exit(0)

	def parse_argv(self):
		parser = argparse.ArgumentParser(prog = "trx_sniff",
			description = "Scapy-based TRX interface sniffer")

		parser.add_argument("-v", "--verbose",
			dest = "verbose", action = "store_true",
			help = "Print burst bits to stdout")

		# Register common logging options
		self.app_reg_logging_options(parser)

		trx_group = parser.add_argument_group("TRX interface")
		trx_group.add_argument("-i", "--sniff-interface",
			dest = "sniff_if", type = str, default = "lo", metavar = "IF",
			help = "Set network interface (default '%(default)s')")
		trx_group.add_argument("-p", "--base-port",
			dest = "base_port", type = int, default = 6700,
			help = "Set base port number (default %(default)s)")
		trx_group.add_argument("-o", "--output-file", metavar = "FILE",
			dest = "output_file", type = str,
			help = "Write bursts to a capture file")

		cnt_group = parser.add_argument_group("Count limitations (optional)")
		cnt_group.add_argument("--frame-count", metavar = "N",
			dest = "frame_count", type = int,
			help = "Stop after sniffing N frames")
		cnt_group.add_argument("--burst-count", metavar = "N",
			dest = "burst_count", type = int,
			help = "Stop after sniffing N bursts")

		pf_group = parser.add_argument_group("Filtering (optional)")
		pf_group.add_argument("--direction",
			dest = "direction", type = str, choices = ["TRX", "L1"],
			help = "Burst direction")
		pf_group.add_argument("--timeslot", metavar = "TN",
			dest = "pf_tn", type = int, choices = range(0, 8),
			help = "TDMA timeslot number (equal TN)")
		pf_group.add_argument("--frame-num-lt", metavar = "FN",
			dest = "pf_fn_lt", type = int,
			help = "TDMA frame number (lower than FN)")
		pf_group.add_argument("--frame-num-gt", metavar = "FN",
			dest = "pf_fn_gt", type = int,
			help = "TDMA frame number (greater than FN)")

		return parser.parse_args()
Ejemplo n.º 8
0
class Application:
    # Application variables
    sniff_interface = "lo"
    sniff_base_port = 5700
    print_bursts = False
    output_file = None

    # Counters
    cnt_burst_dropped_num = 0
    cnt_burst_break = None
    cnt_burst_num = 0

    cnt_frame_break = None
    cnt_frame_last = None
    cnt_frame_num = 0

    # Burst direction fliter
    bf_dir_l12trx = None

    # Timeslot number filter
    bf_tn_val = None

    # Frame number fliter
    bf_fn_lt = None
    bf_fn_gt = None

    # Internal variables
    lo_trigger = False

    def __init__(self):
        print_copyright(CR_HOLDERS)
        self.parse_argv()

        # Open requested capture file
        if self.output_file is not None:
            self.ddf = DATADumpFile(self.output_file)

    def run(self):
        # Compose a packet filter
        pkt_filter = "udp and (port %d or port %d)" \
         % (self.sniff_base_port + 2, self.sniff_base_port + 102)

        print("[i] Listening on interface '%s'..." % self.sniff_interface)

        # Start sniffing...
        scapy.all.sniff(iface=self.sniff_interface,
                        store=1,
                        filter=pkt_filter,
                        prn=self.pkt_handler)

        # Scapy registers its own signal handler
        self.shutdown()

    def pkt_handler(self, ether):
        # Prevent loopback packet duplication
        if self.sniff_interface == "lo":
            self.lo_trigger = not self.lo_trigger
            if not self.lo_trigger:
                return

        # Extract a TRX payload
        ip = ether.payload
        udp = ip.payload
        trx = udp.payload

        # Convert to bytearray
        msg_raw = bytearray(str(trx))

        # Determine a burst direction (L1 <-> TRX)
        l12trx = udp.sport > udp.dport

        # Create an empty DATA message
        msg = DATAMSG_L12TRX() if l12trx else DATAMSG_TRX2L1()

        # Attempt to parse the payload as a DATA message
        try:
            msg.parse_msg(msg_raw)
        except:
            print("[!] Failed to parse message, dropping...")
            self.cnt_burst_dropped_num += 1
            return

        # Poke burst pass filter
        rc = self.burst_pass_filter(l12trx, msg.fn, msg.tn)
        if rc is False:
            self.cnt_burst_dropped_num += 1
            return

        # Debug print
        print("[i] %s burst: %s" \
         % ("L1 -> TRX" if l12trx else "TRX -> L1", msg.desc_hdr()))

        # Poke message handler
        self.msg_handle(msg)

        # Poke burst counter
        rc = self.burst_count(msg.fn, msg.tn)
        if rc is True:
            self.shutdown()

    def burst_pass_filter(self, l12trx, fn, tn):
        # Direction filter
        if self.bf_dir_l12trx is not None:
            if l12trx != self.bf_dir_l12trx:
                return False

        # Timeslot filter
        if self.bf_tn_val is not None:
            if tn != self.bf_tn_val:
                return False

        # Frame number filter
        if self.bf_fn_lt is not None:
            if fn > self.bf_fn_lt:
                return False
        if self.bf_fn_gt is not None:
            if fn < self.bf_fn_gt:
                return False

        # Burst passed ;)
        return True

    def msg_handle(self, msg):
        if self.print_bursts:
            print(msg.burst)

        # Append a new message to the capture
        if self.output_file is not None:
            self.ddf.append_msg(msg)

    def burst_count(self, fn, tn):
        # Update frame counter
        if self.cnt_frame_last is None:
            self.cnt_frame_last = fn
            self.cnt_frame_num += 1
        else:
            if fn != self.cnt_frame_last:
                self.cnt_frame_num += 1

        # Update burst counter
        self.cnt_burst_num += 1

        # Stop sniffing after N bursts
        if self.cnt_burst_break is not None:
            if self.cnt_burst_num == self.cnt_burst_break:
                print("[i] Collected required amount of bursts")
                return True

        # Stop sniffing after N frames
        if self.cnt_frame_break is not None:
            if self.cnt_frame_num == self.cnt_frame_break:
                print("[i] Collected required amount of frames")
                return True

        return False

    def shutdown(self):
        print("[i] Shutting down...")

        # Print statistics
        print("[i] %u bursts handled, %u dropped" \
         % (self.cnt_burst_num, self.cnt_burst_dropped_num))

        # Exit
        sys.exit(0)

    def print_help(self, msg=None):
        s  = " Usage: " + sys.argv[0] + " [options]\n\n" \
          " Some help...\n" \
          "  -h --help              this text\n\n"

        s += " Sniffing options\n" \
          "  -i --sniff-interface   Set network interface (default '%s')\n"  \
          "  -p --sniff-base-port   Set base port number (default %d)\n\n"

        s += " Processing (no processing by default)\n" \
          "  -o --output-file       Write bursts to file\n"          \
          "  -v --print-bits        Print burst bits to stdout\n\n"  \

        s += " Count limitations (disabled by default)\n" \
          "  --frame-count   NUM    Stop after sniffing NUM frames\n"  \
          "  --burst-count   NUM    Stop after sniffing NUM bursts\n\n"

        s += " Filtering (disabled by default)\n" \
          "  --direction     DIR    Burst direction: L12TRX or TRX2L1\n"  \
          "  --timeslot      NUM    TDMA timeslot number [0..7]\n"        \
          "  --frame-num-lt  NUM    TDMA frame number lower than NUM\n"   \
          "  --burst-num-gt  NUM    TDMA frame number greater than NUM\n"

        print(s % (self.sniff_interface, self.sniff_base_port))

        if msg is not None:
            print(msg)

    def parse_argv(self):
        try:
            opts, args = getopt.getopt(sys.argv[1:], "i:p:o:v:h", [
                "help", "sniff-interface=", "sniff-base-port=", "frame-count=",
                "burst-count=", "direction=", "timeslot=", "frame-num-lt=",
                "frame-num-gt=", "output-file=", "print-bits"
            ])
        except getopt.GetoptError as err:
            self.print_help("[!] " + str(err))
            sys.exit(2)

        for o, v in opts:
            if o in ("-h", "--help"):
                self.print_help()
                sys.exit(2)

            elif o in ("-i", "--sniff-interface"):
                self.sniff_interface = v
            elif o in ("-p", "--sniff-base-port"):
                self.sniff_base_port = int(v)

            elif o in ("-o", "--output-file"):
                self.output_file = v
            elif o in ("-v", "--print-bits"):
                self.print_bursts = True

            # Break counters
            elif o == "--frame-count":
                self.cnt_frame_break = int(v)
            elif o == "--burst-count":
                self.cnt_burst_break = int(v)

            # Direction filter
            elif o == "--direction":
                if v == "L12TRX":
                    self.bf_dir_l12trx = True
                elif v == "TRX2L1":
                    self.bf_dir_l12trx = False
                else:
                    self.print_help("[!] Wrong direction argument")
                    sys.exit(2)

            # Timeslot pass filter
            elif o == "--timeslot":
                self.bf_tn_val = int(v)
                if self.bf_tn_val < 0 or self.bf_tn_val > 7:
                    self.print_help("[!] Wrong timeslot value")
                    sys.exit(2)

            # Frame number pass filter
            elif o == "--frame-num-lt":
                self.bf_fn_lt = int(v)
            elif o == "--frame-num-gt":
                self.bf_fn_gt = int(v)