Exemple #1
0
	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)

		# Read messages from the capture
		messages = self.ddf.parse_all(
			skip = self.argv.cnt_skip, count = self.argv.cnt_count)
		if messages is False:
			log.error("Parsing failed, nothing to send")
			sys.exit(1)

		for msg in messages:
			# Pass filter
			if not self.msg_pass_filter(msg):
				continue

			log.info("Sending a burst %s to %s..."
				% (msg.desc_hdr(), self.argv.conn_mode))

			# Send message
			self.data_if.send_msg(msg)
Exemple #2
0
	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)
			l12trx = True
		elif self.conn_mode == "L1":
			self.data_if = DATAInterface(self.remote_addr, self.base_port + 102,
				self.bind_addr, self.base_port + 2)
			l12trx = False
		else:
			self.print_help("[!] Unknown connection type")
			sys.exit(2)

		# Read messages from the capture
		messages = self.ddf.parse_all(
			skip = self.msg_skip, count = self.msg_count)
		if messages is False:
			pass # FIXME!!!

		for msg in messages:
			# Pass filter
			if not self.msg_pass_filter(l12trx, msg):
				continue

			print("[i] Sending a burst %s to %s..."
				% (msg.desc_hdr(), self.conn_mode))

			# Send message
			self.data_if.send_msg(msg)
Exemple #3
0
	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)

		# Read messages from the capture
		messages = self.ddf.parse_all(
			skip = self.argv.cnt_skip, count = self.argv.cnt_count)
		if messages is False:
			log.error("Parsing failed, nothing to send")
			sys.exit(1)

		for msg in messages:
			# Pass filter
			if not self.msg_pass_filter(msg):
				continue

			log.info("Sending a burst %s to %s..."
				% (msg.desc_hdr(), self.argv.conn_mode))

			# Send message
			self.data_if.send_msg(msg)
Exemple #4
0
    def __init__(self,
                 bind_addr,
                 remote_addr,
                 base_port,
                 name=None,
                 child_idx=0,
                 clck_gen=None,
                 pwr_meas=None):
        # Connection info
        self.remote_addr = remote_addr
        self.bind_addr = bind_addr
        self.base_port = base_port
        self.child_idx = child_idx

        # Meta info
        self.name = name

        log.info("Init transceiver '%s'" % self)

        # Child transceiver cannot have its own clock
        if clck_gen is not None and child_idx > 0:
            raise TypeError("Child transceiver cannot have its own clock")

        # Init DATA interface
        self.data_if = DATAInterface(remote_addr,
                                     base_port + child_idx * 2 + 102,
                                     bind_addr, base_port + child_idx * 2 + 2)

        # Init CTRL interface
        self.ctrl_if = CTRLInterfaceTRX(self, remote_addr,
                                        base_port + child_idx * 2 + 101,
                                        bind_addr,
                                        base_port + child_idx * 2 + 1)

        # Init optional CLCK interface
        self.clck_gen = clck_gen
        if clck_gen is not None:
            self.clck_if = UDPLink(remote_addr, base_port + 100, bind_addr,
                                   base_port)

        # Optional Power Measurement interface
        self.pwr_meas = pwr_meas

        # Internal state
        self.running = False

        # Actual RX / TX frequencies
        self._rx_freq = None
        self._tx_freq = None

        # Frequency hopping parameters (set by CTRL)
        self.fh = None

        # List of active (configured) timeslots
        self.ts_list = []

        # List of child transceivers
        self.child_trx_list = TRXList()
Exemple #5
0
    def __init__(self,
                 bind_addr,
                 remote_addr,
                 base_port,
                 clck_gen=None,
                 pwr_meas=None):
        # Connection info
        self.remote_addr = remote_addr
        self.bind_addr = bind_addr
        self.base_port = base_port

        # Init DATA interface
        self.data_if = DATAInterface(remote_addr, base_port + 102, bind_addr,
                                     base_port + 2)

        # Init CTRL interface
        self.ctrl_if = CTRLInterfaceTRX(self, remote_addr, base_port + 101,
                                        bind_addr, base_port + 1)

        # Init optional CLCK interface
        self.clck_gen = clck_gen
        if clck_gen is not None:
            self.clck_if = UDPLink(remote_addr, base_port + 100, bind_addr,
                                   base_port)

        # Optional Power Measurement interface
        self.pwr_meas = pwr_meas

        # Internal state
        self.running = False

        # Actual RX / TX frequencies
        self.rx_freq = None
        self.tx_freq = None

        # List of active (configured) timeslots
        self.ts_list = []
Exemple #6
0
	def __init__(self, bind_addr, remote_addr, base_port, name = None,
			child_idx = 0, clck_gen = None, pwr_meas = None):
		# Connection info
		self.remote_addr = remote_addr
		self.bind_addr = bind_addr
		self.base_port = base_port
		self.child_idx = child_idx

		# Meta info
		self.name = name

		log.info("Init transceiver '%s'" % self)

		# Child transceiver cannot have its own clock
		if clck_gen is not None and child_idx > 0:
			raise TypeError("Child transceiver cannot have its own clock")

		# Init DATA interface
		self.data_if = DATAInterface(
			remote_addr, base_port + child_idx * 2 + 102,
			bind_addr, base_port + child_idx * 2 + 2)

		# Init CTRL interface
		self.ctrl_if = CTRLInterfaceTRX(self,
			remote_addr, base_port + child_idx * 2 + 101,
			bind_addr, base_port + child_idx * 2 + 1)

		# Init optional CLCK interface
		self.clck_gen = clck_gen
		if clck_gen is not None:
			self.clck_if = UDPLink(
				remote_addr, base_port + 100,
				bind_addr, base_port)

		# Optional Power Measurement interface
		self.pwr_meas = pwr_meas

		# Internal state
		self.running = False

		# Actual RX / TX frequencies
		self.rx_freq = None
		self.tx_freq = None

		# List of active (configured) timeslots
		self.ts_list = []

		# List of child transceivers
		self.child_trx_list = TRXList()
Exemple #7
0
	def __init__(self, bind_addr, remote_addr, base_port,
			clck_gen = None, pwr_meas = None):
		# Connection info
		self.remote_addr = remote_addr
		self.bind_addr = bind_addr
		self.base_port = base_port

		# Init DATA interface
		self.data_if = DATAInterface(
			remote_addr, base_port + 102,
			bind_addr, base_port + 2)

		# Init CTRL interface
		self.ctrl_if = CTRLInterfaceTRX(self,
			remote_addr, base_port + 101,
			bind_addr, base_port + 1)

		# Init optional CLCK interface
		self.clck_gen = clck_gen
		if clck_gen is not None:
			self.clck_if = UDPLink(
				remote_addr, base_port + 100,
				bind_addr, base_port)

		# Optional Power Measurement interface
		self.pwr_meas = pwr_meas

		# Internal state
		self.running = False

		# Actual RX / TX frequencies
		self.rx_freq = None
		self.tx_freq = None

		# List of active (configured) timeslots
		self.ts_list = []
Exemple #8
0
class Transceiver:
	""" Base transceiver implementation.

	Represents a single transceiver, that can be used as for the BTS side,
	as for the MS side. Each individual instance of Transceiver unifies
	three basic interfaces built on three independent UDP connections:

	  - CLCK (base port + 100/0) - clock indications from TRX to L1,
	  - CTRL (base port + 101/1) - control interface for L1,
	  - DATA (base port + 102/2) - bidirectional data interface for bursts.

	A transceiver can be either in active (i.e. working), or in idle mode.
	The active mode should ensure that both RX/TX frequencies are set.

	NOTE: CLCK is not required for some L1 implementations, so it is optional.

	== Timeslot configuration

	Transceiver has a list of active (i.e. configured) TDMA timeslots.
	The L1 should configure a timeslot before sending or expecting any
	data on it. This is done by SETSLOT control command, which also
	indicates an associated channel combination (see GSM TS 05.02).

	NOTE: we don't store the associated channel combinations,
	      as they are only useful for burst detection and demodulation.

	== Clock distribution (optional)

	The clock indications are not expected by L1 when transceiver
	is not running, so we monitor both POWERON / POWEROFF events
	from the control interface, and keep the list of CLCK links
	in a given CLCKGen instance updated. The clock generator is
	started and stopped automatically.

	NOTE: a single instance of CLCKGen can be shared between multiple
	      transceivers, as well as multiple transceivers may use
	      individual CLCKGen instances.

	== Power Measurement (optional)

	Transceiver may have an optional power measurement interface,
	that shall provide at least one method: measure(freq). This
	is required for the MS side (i.e. OsmocomBB).

	"""

	def __init__(self, bind_addr, remote_addr, base_port,
			clck_gen = None, pwr_meas = None):
		# Connection info
		self.remote_addr = remote_addr
		self.bind_addr = bind_addr
		self.base_port = base_port

		# Init DATA interface
		self.data_if = DATAInterface(
			remote_addr, base_port + 102,
			bind_addr, base_port + 2)

		# Init CTRL interface
		self.ctrl_if = CTRLInterfaceTRX(self,
			remote_addr, base_port + 101,
			bind_addr, base_port + 1)

		# Init optional CLCK interface
		self.clck_gen = clck_gen
		if clck_gen is not None:
			self.clck_if = UDPLink(
				remote_addr, base_port + 100,
				bind_addr, base_port)

		# Optional Power Measurement interface
		self.pwr_meas = pwr_meas

		# Internal state
		self.running = False

		# Actual RX / TX frequencies
		self.rx_freq = None
		self.tx_freq = None

		# List of active (configured) timeslots
		self.ts_list = []

	# To be overwritten if required,
	# no custom command handlers by default
	def ctrl_cmd_handler(self, request):
		return None

	def power_event_handler(self, event):
		# Trigger clock generator if required
		if self.clck_gen is not None:
			clck_links = self.clck_gen.clck_links
			if not self.running and (self.clck_if in clck_links):
				# Transceiver was stopped
				clck_links.remove(self.clck_if)
			elif self.running and (self.clck_if not in clck_links):
				# Transceiver was started
				clck_links.append(self.clck_if)

			if not self.clck_gen.timer and len(clck_links) > 0:
				log.info("Starting clock generator")
				self.clck_gen.start()
			elif self.clck_gen.timer and not clck_links:
				log.info("Stopping clock generator")
				self.clck_gen.stop()

	def recv_data_msg(self):
		# Read and parse data from socket
		msg = self.data_if.recv_l12trx_msg()
		if not msg:
			return None

		# Make sure that transceiver is configured and running
		if not self.running:
			log.warning("RX DATA message (%s), but transceiver "
				"is not running => dropping..." % msg.desc_hdr())
			return None

		# Make sure that indicated timeslot is configured
		if msg.tn not in self.ts_list:
			log.warning("RX DATA message (%s), but timeslot "
				"is not configured => dropping..." % msg.desc_hdr())
			return None

		return msg
Exemple #9
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)
Exemple #10
0
    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)
class Transceiver:
    """ Base transceiver implementation.

	Represents a single transceiver, that can be used as for the BTS side,
	as for the MS side. Each individual instance of Transceiver unifies
	three basic interfaces built on three independent UDP connections:

	  - CLCK (base port + 100/0) - clock indications from TRX to L1,
	  - CTRL (base port + 101/1) - control interface for L1,
	  - DATA (base port + 102/2) - bidirectional data interface for bursts.

	A transceiver can be either in active (i.e. working), or in idle mode.
	The active mode should ensure that both RX/TX frequencies are set.

	NOTE: CLCK is not required for some L1 implementations, so it is optional.

	== Timeslot configuration

	Transceiver has a list of active (i.e. configured) TDMA timeslots.
	The L1 should configure a timeslot before sending or expecting any
	data on it. This is done by SETSLOT control command, which also
	indicates an associated channel combination (see GSM TS 05.02).

	NOTE: we don't store the associated channel combinations,
	      as they are only useful for burst detection and demodulation.

	== Child transceivers

	A BTS can (optionally) have more than one transceiver. In this case
	additional (let's say child) transceivers basically share the same
	clock source of the first transceiver, so UDP port mapping is a bit
	different, for example:

	  (trx_0) clck=5700, ctrl=5701, data=5702,
	  (trx_1)            ctrl=5703, data=5704,
	  (trx_2)            ctrl=5705, data=5706.
	  ...

	As soon as the first transceiver is powered on / off,
	all child transceivers are also powered on / off.

	== Clock distribution (optional)

	The clock indications are not expected by L1 when transceiver
	is not running, so we monitor both POWERON / POWEROFF events
	from the control interface, and keep the list of CLCK links
	in a given CLCKGen instance updated. The clock generator is
	started and stopped automatically.

	NOTE: a single instance of CLCKGen can be shared between multiple
	      transceivers, as well as multiple transceivers may use
	      individual CLCKGen instances.

	== Power Measurement (optional)

	Transceiver may have an optional power measurement interface,
	that shall provide at least one method: measure(freq). This
	is required for the MS side (i.e. OsmocomBB).

	"""
    def __init__(self,
                 bind_addr,
                 remote_addr,
                 base_port,
                 name=None,
                 child_idx=0,
                 clck_gen=None,
                 pwr_meas=None):
        # Connection info
        self.remote_addr = remote_addr
        self.bind_addr = bind_addr
        self.base_port = base_port
        self.child_idx = child_idx

        # Meta info
        self.name = name

        log.info("Init transceiver '%s'" % self)

        # Child transceiver cannot have its own clock
        if clck_gen is not None and child_idx > 0:
            raise TypeError("Child transceiver cannot have its own clock")

        # Init DATA interface
        self.data_if = DATAInterface(remote_addr,
                                     base_port + child_idx * 2 + 102,
                                     bind_addr, base_port + child_idx * 2 + 2)

        # Init CTRL interface
        self.ctrl_if = CTRLInterfaceTRX(self, remote_addr,
                                        base_port + child_idx * 2 + 101,
                                        bind_addr,
                                        base_port + child_idx * 2 + 1)

        # Init optional CLCK interface
        self.clck_gen = clck_gen
        if clck_gen is not None:
            self.clck_if = UDPLink(remote_addr, base_port + 100, bind_addr,
                                   base_port)

        # Optional Power Measurement interface
        self.pwr_meas = pwr_meas

        # Internal state
        self.running = False

        # Actual RX / TX frequencies
        self.rx_freq = None
        self.tx_freq = None

        # List of active (configured) timeslots
        self.ts_list = []

        # List of child transceivers
        self.child_trx_list = TRXList()

    def __str__(self):
        desc = "%s:%d" % (self.remote_addr, self.base_port)
        if self.child_idx > 0:
            desc += "/%d" % self.child_idx
        if self.name is not None:
            desc = "%s@%s" % (self.name, desc)

        return desc

    # To be overwritten if required,
    # no custom command handlers by default
    def ctrl_cmd_handler(self, request):
        return None

    def power_event_handler(self, event):
        # Update child transceivers
        for trx in self.child_trx_list.trx_list:
            if event == "POWERON":
                trx.running = True
            else:
                trx.running = False

        # Trigger clock generator if required
        if self.clck_gen is not None:
            clck_links = self.clck_gen.clck_links
            if not self.running and (self.clck_if in clck_links):
                # Transceiver was stopped
                clck_links.remove(self.clck_if)
            elif self.running and (self.clck_if not in clck_links):
                # Transceiver was started
                clck_links.append(self.clck_if)

            if not self.clck_gen.running and len(clck_links) > 0:
                log.info("Starting clock generator")
                self.clck_gen.start()
            elif self.clck_gen.running and not clck_links:
                log.info("Stopping clock generator")
                self.clck_gen.stop()

    def recv_data_msg(self):
        # Read and parse data from socket
        msg = self.data_if.recv_l12trx_msg()
        if not msg:
            return None

        # Make sure that transceiver is configured and running
        if not self.running:
            log.warning("(%s) RX TRXD message (%s), but transceiver "
                        "is not running => dropping..." %
                        (self, msg.desc_hdr()))
            return None

        # Make sure that indicated timeslot is configured
        if msg.tn not in self.ts_list:
            log.warning("(%s) RX TRXD message (%s), but timeslot is not "
                        "configured => dropping..." % (self, msg.desc_hdr()))
            return None

        return msg
Exemple #12
0
class Transceiver:
    """ Base transceiver implementation.

	Represents a single transceiver, that can be used as for the BTS side,
	as for the MS side. Each individual instance of Transceiver unifies
	three basic interfaces built on three independent UDP connections:

	  - CLCK (base port + 100/0) - clock indications from TRX to L1,
	  - CTRL (base port + 101/1) - control interface for L1,
	  - DATA (base port + 102/2) - bidirectional data interface for bursts.

	A transceiver can be either in active (i.e. working), or in idle mode.
	The active mode should ensure that both RX/TX frequencies are set.

	NOTE: CLCK is not required for some L1 implementations, so it is optional.

	== Timeslot configuration

	Transceiver has a list of active (i.e. configured) TDMA timeslots.
	The L1 should configure a timeslot before sending or expecting any
	data on it. This is done by SETSLOT control command, which also
	indicates an associated channel combination (see GSM TS 05.02).

	NOTE: we don't store the associated channel combinations,
	      as they are only useful for burst detection and demodulation.

	== Clock distribution (optional)

	The clock indications are not expected by L1 when transceiver
	is not running, so we monitor both POWERON / POWEROFF events
	from the control interface, and keep the list of CLCK links
	in a given CLCKGen instance updated. The clock generator is
	started and stopped automatically.

	NOTE: a single instance of CLCKGen can be shared between multiple
	      transceivers, as well as multiple transceivers may use
	      individual CLCKGen instances.

	== Power Measurement (optional)

	Transceiver may have an optional power measurement interface,
	that shall provide at least one method: measure(freq). This
	is required for the MS side (i.e. OsmocomBB).

	"""
    def __init__(self,
                 bind_addr,
                 remote_addr,
                 base_port,
                 clck_gen=None,
                 pwr_meas=None):
        # Connection info
        self.remote_addr = remote_addr
        self.bind_addr = bind_addr
        self.base_port = base_port

        # Init DATA interface
        self.data_if = DATAInterface(remote_addr, base_port + 102, bind_addr,
                                     base_port + 2)

        # Init CTRL interface
        self.ctrl_if = CTRLInterfaceTRX(self, remote_addr, base_port + 101,
                                        bind_addr, base_port + 1)

        # Init optional CLCK interface
        self.clck_gen = clck_gen
        if clck_gen is not None:
            self.clck_if = UDPLink(remote_addr, base_port + 100, bind_addr,
                                   base_port)

        # Optional Power Measurement interface
        self.pwr_meas = pwr_meas

        # Internal state
        self.running = False

        # Actual RX / TX frequencies
        self.rx_freq = None
        self.tx_freq = None

        # List of active (configured) timeslots
        self.ts_list = []

    # To be overwritten if required,
    # no custom command handlers by default
    def ctrl_cmd_handler(self, request):
        return None

    def power_event_handler(self, event):
        # Trigger clock generator if required
        if self.clck_gen is not None:
            clck_links = self.clck_gen.clck_links
            if not self.running and (self.clck_if in clck_links):
                # Transceiver was stopped
                clck_links.remove(self.clck_if)
            elif self.running and (self.clck_if not in clck_links):
                # Transceiver was started
                clck_links.append(self.clck_if)

            if not self.clck_gen.timer and len(clck_links) > 0:
                log.info("Starting clock generator")
                self.clck_gen.start()
            elif self.clck_gen.timer and not clck_links:
                log.info("Stopping clock generator")
                self.clck_gen.stop()

    def recv_data_msg(self):
        # Read and parse data from socket
        msg = self.data_if.recv_l12trx_msg()
        if not msg:
            return None

        # Make sure that transceiver is configured and running
        if not self.running:
            log.warning("RX DATA message (%s), but transceiver "
                        "is not running => dropping..." % msg.desc_hdr())
            return None

        # Make sure that indicated timeslot is configured
        if msg.tn not in self.ts_list:
            log.warning("RX DATA message (%s), but timeslot "
                        "is not configured => dropping..." % msg.desc_hdr())
            return None

        return msg
Exemple #13
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)
Exemple #14
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
		self.ddf = DATADumpFile(self.argv.capture_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)

		# Read messages from the capture
		messages = self.ddf.parse_all(
			skip = self.argv.cnt_skip, count = self.argv.cnt_count)
		if messages is False:
			log.error("Parsing failed, nothing to send")
			sys.exit(1)

		for msg in messages:
			# Pass filter
			if not self.msg_pass_filter(msg):
				continue

			log.info("Sending a burst %s to %s..."
				% (msg.desc_hdr(), self.argv.conn_mode))

			# Send message
			self.data_if.send_msg(msg)

	def msg_pass_filter(self, msg):
		# Direction filter
		l12trx = self.argv.conn_mode == "TRX"
		if isinstance(msg, DATAMSG_L12TRX) and not l12trx:
			return False
		elif isinstance(msg, DATAMSG_TRX2L1) and l12trx:
			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

		# Burst passed ;)
		return True

	def parse_argv(self):
		parser = argparse.ArgumentParser(prog = "burst_send",
			description = "Auxiliary tool to send (reply) captured 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("-i", "--capture-file", metavar = "FILE",
			dest = "capture_file", type = str, required = True,
			help = "Capture file to read bursts from")

		cnt_group = parser.add_argument_group("Count limitations (optional)")
		cnt_group.add_argument("--skip", metavar = "N",
			dest = "cnt_skip", type = int,
			help = "Skip N messages before sending")
		cnt_group.add_argument("--count", metavar = "N",
			dest = "cnt_count", type = int,
			help = "Stop after sending N messages")

		pf_group = parser.add_argument_group("Filtering (optional)")
		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()

	def sig_handler(self, signum, frame):
		log.info("Signal %d received" % signum)
		if signum is signal.SIGINT:
			sys.exit(0)
Exemple #15
0
class Transceiver:
    """ Base transceiver implementation.

	Represents a single transceiver, that can be used as for the BTS side,
	as for the MS side. Each individual instance of Transceiver unifies
	three basic interfaces built on three independent UDP connections:

	  - CLCK (base port + 100/0) - clock indications from TRX to L1,
	  - CTRL (base port + 101/1) - control interface for L1,
	  - DATA (base port + 102/2) - bidirectional data interface for bursts.

	A transceiver can be either in active (i.e. working), or in idle mode.
	The active mode should ensure that both RX/TX frequencies are set.

	NOTE: CLCK is not required for some L1 implementations, so it is optional.

	== Timeslot configuration

	Transceiver has a list of active (i.e. configured) TDMA timeslots.
	The L1 should configure a timeslot before sending or expecting any
	data on it. This is done by SETSLOT control command, which also
	indicates an associated channel combination (see GSM TS 05.02).

	NOTE: we don't store the associated channel combinations,
	      as they are only useful for burst detection and demodulation.

	== Child transceivers

	A BTS can (optionally) have more than one transceiver. In this case
	additional (let's say child) transceivers basically share the same
	clock source of the first transceiver, so UDP port mapping is a bit
	different, for example:

	  (trx_0) clck=5700, ctrl=5701, data=5702,
	  (trx_1)            ctrl=5703, data=5704,
	  (trx_2)            ctrl=5705, data=5706.
	  ...

	As soon as the first transceiver is powered on / off,
	all child transceivers are also powered on / off.

	== Clock distribution (optional)

	The clock indications are not expected by L1 when transceiver
	is not running, so we monitor both POWERON / POWEROFF events
	from the control interface, and keep the list of CLCK links
	in a given CLCKGen instance updated. The clock generator is
	started and stopped automatically.

	NOTE: a single instance of CLCKGen can be shared between multiple
	      transceivers, as well as multiple transceivers may use
	      individual CLCKGen instances.

	== Power Measurement (optional)

	Transceiver may have an optional power measurement interface,
	that shall provide at least one method: measure(freq). This
	is required for the MS side (i.e. OsmocomBB).

	== Frequency hopping (optional)

	There are two ways to implement frequency hopping:

	  a) The Transceiver is configured with the hopping parameters, in
	     particular HSN, MAIO, and the list of ARFCNs (channels), so the
	     actual Rx/Tx frequencies are changed by the Transceiver itself
	     depending on the current TDMA frame number.

	  b) The L1 maintains several Transceivers (two or more), so each
	     instance is assigned one dedicated RF carrier frequency, and
	     hence the number of available hopping frequencies is equal to
	     the number of Transceivers. In this case, it's the task of
	     the L1 to commutate bursts between Transceivers (frequencies).

	Variant a) is commonly known as "synthesizer frequency hopping"
	whereas b) is known as "baseband frequency hopping".

	For the MS side, a) is preferred, because a phone usually has only
	one Transceiver (per RAT). On the other hand, b) is more suitable
	for the BTS side, because it's relatively easy to implement and
	there is no technical limitation on the amount of Transceivers.

	FakeTRX obviously does support b) since multi-TRX feature has been
	implemented, as well as a) by resolving UL/DL frequencies using a
	preconfigured (by the L1) set of the hopping parameters. The later
	can be enabled using the SETFH control command.

	NOTE: in the current implementation, mode a) applies to the whole
	Transceiver and all its timeslots, so using in for the BTS side
	does not make any sense (imagine BCCH hopping together with DCCH).

	"""
    def __init__(self,
                 bind_addr,
                 remote_addr,
                 base_port,
                 name=None,
                 child_idx=0,
                 clck_gen=None,
                 pwr_meas=None):
        # Connection info
        self.remote_addr = remote_addr
        self.bind_addr = bind_addr
        self.base_port = base_port
        self.child_idx = child_idx

        # Meta info
        self.name = name

        log.info("Init transceiver '%s'" % self)

        # Child transceiver cannot have its own clock
        if clck_gen is not None and child_idx > 0:
            raise TypeError("Child transceiver cannot have its own clock")

        # Init DATA interface
        self.data_if = DATAInterface(remote_addr,
                                     base_port + child_idx * 2 + 102,
                                     bind_addr, base_port + child_idx * 2 + 2)

        # Init CTRL interface
        self.ctrl_if = CTRLInterfaceTRX(self, remote_addr,
                                        base_port + child_idx * 2 + 101,
                                        bind_addr,
                                        base_port + child_idx * 2 + 1)

        # Init optional CLCK interface
        self.clck_gen = clck_gen
        if clck_gen is not None:
            self.clck_if = UDPLink(remote_addr, base_port + 100, bind_addr,
                                   base_port)

        # Optional Power Measurement interface
        self.pwr_meas = pwr_meas

        # Internal state
        self.running = False

        # Actual RX / TX frequencies
        self._rx_freq = None
        self._tx_freq = None

        # Frequency hopping parameters (set by CTRL)
        self.fh = None

        # List of active (configured) timeslots
        self.ts_list = []

        # List of child transceivers
        self.child_trx_list = TRXList()

    def __str__(self):
        desc = "%s:%d" % (self.remote_addr, self.base_port)
        if self.child_idx > 0:
            desc += "/%d" % self.child_idx
        if self.name is not None:
            desc = "%s@%s" % (self.name, desc)

        return desc

    @property
    def ready(self):
        # Make sure that either both Rx/Tx frequencies are set
        if self._rx_freq is None or self._tx_freq is None:
            # ... or frequency hopping is in use
            if self.fh is None:
                return False

        return True

    def get_rx_freq(self, fn):
        if self.fh is None:
            return self._rx_freq

        # Frequency hopping in use, resolve by TDMA fn
        (rx_freq, _) = self.fh.resolve(fn)
        return rx_freq

    def get_tx_freq(self, fn):
        if self.fh is None:
            return self._tx_freq

        # Frequency hopping in use, resolve by TDMA fn
        (_, tx_freq) = self.fh.resolve(fn)
        return tx_freq

    def enable_fh(self, *args):
        self.fh = HoppingParams(*args)
        log.info("(%s) Frequency hopping configured: %s" % (self, self.fh))

    def disable_fh(self):
        if self.fh is not None:
            log.info("(%s) Frequency hopping disabled" % self)
            self.fh = None

    # To be overwritten if required,
    # no custom command handlers by default
    def ctrl_cmd_handler(self, request):
        return None

    def power_event_handler(self, event):
        # Update child transceivers
        for trx in self.child_trx_list.trx_list:
            if event == "POWERON":
                trx.running = True
            elif event == "POWEROFF":
                trx.running = False
                trx.disable_fh()

        # Reset frequency hopping parameters
        if event == "POWEROFF":
            self.disable_fh()

        # Trigger clock generator if required
        if self.clck_gen is not None:
            clck_links = self.clck_gen.clck_links
            if not self.running and (self.clck_if in clck_links):
                # Transceiver was stopped
                clck_links.remove(self.clck_if)
            elif self.running and (self.clck_if not in clck_links):
                # Transceiver was started
                clck_links.append(self.clck_if)

            if not self.clck_gen.running and len(clck_links) > 0:
                log.info("Starting clock generator")
                self.clck_gen.start()
            elif self.clck_gen.running and not clck_links:
                log.info("Stopping clock generator")
                self.clck_gen.stop()

    def recv_data_msg(self):
        # Read and parse data from socket
        msg = self.data_if.recv_tx_msg()
        if not msg:
            return None

        # Make sure that transceiver is configured and running
        if not self.running:
            log.warning("(%s) RX TRXD message (%s), but transceiver "
                        "is not running => dropping..." %
                        (self, msg.desc_hdr()))
            return None

        # Make sure that indicated timeslot is configured
        # Pass PDUs without burst bits, they will be sent as NOPE.ind
        if msg.tn not in self.ts_list and msg.burst:
            log.warning("(%s) RX TRXD message (%s), but timeslot is not "
                        "configured => dropping..." % (self, msg.desc_hdr()))
            return None

        return msg

    def handle_data_msg(self, msg):
        # TODO: make legacy mode configurable (via argv?)
        self.data_if.send_msg(msg, legacy=True)
Exemple #16
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
		self.ddf = DATADumpFile(self.argv.capture_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)

		# Read messages from the capture
		messages = self.ddf.parse_all(
			skip = self.argv.cnt_skip, count = self.argv.cnt_count)
		if messages is False:
			log.error("Parsing failed, nothing to send")
			sys.exit(1)

		for msg in messages:
			# Pass filter
			if not self.msg_pass_filter(msg):
				continue

			log.info("Sending a burst %s to %s..."
				% (msg.desc_hdr(), self.argv.conn_mode))

			# Send message
			self.data_if.send_msg(msg)

	def msg_pass_filter(self, msg):
		# Direction filter
		if isinstance(msg, RxMsg) and self.argv.conn_mode == "TRX":
			return False # cannot send RxMsg to TRX
		if isinstance(msg, TxMsg) and self.argv.conn_mode == "L1":
			return False # cannot send TxMsg to L1

		# 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

		# Burst passed ;)
		return True

	def parse_argv(self):
		parser = argparse.ArgumentParser(prog = "burst_send",
			description = "Auxiliary tool to send (reply) captured 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("-i", "--capture-file", metavar = "FILE",
			dest = "capture_file", type = str, required = True,
			help = "Capture file to read bursts from")

		cnt_group = parser.add_argument_group("Count limitations (optional)")
		cnt_group.add_argument("--skip", metavar = "N",
			dest = "cnt_skip", type = int,
			help = "Skip N messages before sending")
		cnt_group.add_argument("--count", metavar = "N",
			dest = "cnt_count", type = int,
			help = "Stop after sending N messages")

		pf_group = parser.add_argument_group("Filtering (optional)")
		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()

	def sig_handler(self, signum, frame):
		log.info("Signal %d received" % signum)
		if signum == signal.SIGINT:
			sys.exit(0)
Exemple #17
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)
Exemple #18
0
class Application:
	# Application variables
	remote_addr = "127.0.0.1"
	bind_addr = "0.0.0.0"
	base_port = 5700
	conn_mode = "TRX"

	# Burst source
	capture_file = None

	# Count limitations
	msg_skip = None
	msg_count = None

	# Pass filtering
	pf_fn_lt = None
	pf_fn_gt = None
	pf_tn = None

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

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

		# Open requested capture file
		self.ddf = DATADumpFile(self.capture_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)
			l12trx = True
		elif self.conn_mode == "L1":
			self.data_if = DATAInterface(self.remote_addr, self.base_port + 102,
				self.bind_addr, self.base_port + 2)
			l12trx = False
		else:
			self.print_help("[!] Unknown connection type")
			sys.exit(2)

		# Read messages from the capture
		messages = self.ddf.parse_all(
			skip = self.msg_skip, count = self.msg_count)
		if messages is False:
			pass # FIXME!!!

		for msg in messages:
			# Pass filter
			if not self.msg_pass_filter(l12trx, msg):
				continue

			print("[i] Sending a burst %s to %s..."
				% (msg.desc_hdr(), self.conn_mode))

			# Send message
			self.data_if.send_msg(msg)

	def msg_pass_filter(self, l12trx, msg):
		# Direction filter
		if isinstance(msg, DATAMSG_L12TRX) and not l12trx:
			return False
		elif isinstance(msg, DATAMSG_TRX2L1) and l12trx:
			return False

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

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

		# Burst passed ;)
		return True

	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" \
			 "  -m --conn-mode         Send bursts to: TRX (default) / L1\n"    \
			 "  -r --remote-addr       Set remote address (default %s)\n"       \
			 "  -b --bind-addr         Set bind address (default %s)\n"         \
			 "  -p --base-port         Set base port number (default %d)\n\n"

		s += " Burst source\n" \
			 "  -i --capture-file      Read bursts from capture file\n\n" \

		s += " Count limitations (disabled by default)\n" \
			 "  --msg-skip      NUM    Skip NUM messages before sending\n"   \
			 "  --msg-count     NUM    Stop after sending NUM messages\n\n"  \

		s += " Filtering (disabled by default)\n" \
			 "  --timeslot      NUM    TDMA timeslot number [0..7]\n"        \
			 "  --frame-num-lt  NUM    TDMA frame number lower than NUM\n"   \
			 "  --frame-num-gt  NUM    TDMA frame number greater than NUM\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:],
				"m:r:b:p:i:h",
				[
					"help",
					"conn-mode=",
					"remote-addr=",
					"bind-addr=",
					"base-port=",
					"capture-file=",
					"msg-skip=",
					"msg-count=",
					"timeslot=",
					"frame-num-lt=",
					"frame-num-gt=",
				])
		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)

			# Capture file
			elif o in ("-i", "--capture-file"):
				self.capture_file = v

			# TRX interface specific
			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)

			# Count limitations
			elif o == "--msg-skip":
				self.msg_skip = int(v)
			elif o == "--msg-count":
				self.msg_count = int(v)

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

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

		if self.capture_file is None:
			self.print_help("[!] Please specify a capture file")
			sys.exit(2)

	def sig_handler(self, signum, frame):
		print("Signal %d received" % signum)
		if signum is signal.SIGINT:
			sys.exit(0)
Exemple #19
0
	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)
Exemple #20
0
class Transceiver:
	""" Base transceiver implementation.

	Represents a single transceiver, that can be used as for the BTS side,
	as for the MS side. Each individual instance of Transceiver unifies
	three basic interfaces built on three independent UDP connections:

	  - CLCK (base port + 100/0) - clock indications from TRX to L1,
	  - CTRL (base port + 101/1) - control interface for L1,
	  - DATA (base port + 102/2) - bidirectional data interface for bursts.

	A transceiver can be either in active (i.e. working), or in idle mode.
	The active mode should ensure that both RX/TX frequencies are set.

	NOTE: CLCK is not required for some L1 implementations, so it is optional.

	== Timeslot configuration

	Transceiver has a list of active (i.e. configured) TDMA timeslots.
	The L1 should configure a timeslot before sending or expecting any
	data on it. This is done by SETSLOT control command, which also
	indicates an associated channel combination (see GSM TS 05.02).

	NOTE: we don't store the associated channel combinations,
	      as they are only useful for burst detection and demodulation.

	== Child transceivers

	A BTS can (optionally) have more than one transceiver. In this case
	additional (let's say child) transceivers basically share the same
	clock source of the first transceiver, so UDP port mapping is a bit
	different, for example:

	  (trx_0) clck=5700, ctrl=5701, data=5702,
	  (trx_1)            ctrl=5703, data=5704,
	  (trx_2)            ctrl=5705, data=5706.
	  ...

	As soon as the first transceiver is powered on / off,
	all child transceivers are also powered on / off.

	== Clock distribution (optional)

	The clock indications are not expected by L1 when transceiver
	is not running, so we monitor both POWERON / POWEROFF events
	from the control interface, and keep the list of CLCK links
	in a given CLCKGen instance updated. The clock generator is
	started and stopped automatically.

	NOTE: a single instance of CLCKGen can be shared between multiple
	      transceivers, as well as multiple transceivers may use
	      individual CLCKGen instances.

	== Power Measurement (optional)

	Transceiver may have an optional power measurement interface,
	that shall provide at least one method: measure(freq). This
	is required for the MS side (i.e. OsmocomBB).

	"""

	def __init__(self, bind_addr, remote_addr, base_port, name = None,
			child_idx = 0, clck_gen = None, pwr_meas = None):
		# Connection info
		self.remote_addr = remote_addr
		self.bind_addr = bind_addr
		self.base_port = base_port
		self.child_idx = child_idx

		# Meta info
		self.name = name

		log.info("Init transceiver '%s'" % self)

		# Child transceiver cannot have its own clock
		if clck_gen is not None and child_idx > 0:
			raise TypeError("Child transceiver cannot have its own clock")

		# Init DATA interface
		self.data_if = DATAInterface(
			remote_addr, base_port + child_idx * 2 + 102,
			bind_addr, base_port + child_idx * 2 + 2)

		# Init CTRL interface
		self.ctrl_if = CTRLInterfaceTRX(self,
			remote_addr, base_port + child_idx * 2 + 101,
			bind_addr, base_port + child_idx * 2 + 1)

		# Init optional CLCK interface
		self.clck_gen = clck_gen
		if clck_gen is not None:
			self.clck_if = UDPLink(
				remote_addr, base_port + 100,
				bind_addr, base_port)

		# Optional Power Measurement interface
		self.pwr_meas = pwr_meas

		# Internal state
		self.running = False

		# Actual RX / TX frequencies
		self.rx_freq = None
		self.tx_freq = None

		# List of active (configured) timeslots
		self.ts_list = []

		# List of child transceivers
		self.child_trx_list = TRXList()

	def __str__(self):
		desc = "%s:%d" % (self.remote_addr, self.base_port)
		if self.child_idx > 0:
			desc += "/%d" % self.child_idx
		if self.name is not None:
			desc = "%s@%s" % (self.name, desc)

		return desc

	# To be overwritten if required,
	# no custom command handlers by default
	def ctrl_cmd_handler(self, request):
		return None

	def power_event_handler(self, event):
		# Update child transceivers
		for trx in self.child_trx_list.trx_list:
			if event == "POWERON":
				trx.running = True
			else:
				trx.running = False

		# Trigger clock generator if required
		if self.clck_gen is not None:
			clck_links = self.clck_gen.clck_links
			if not self.running and (self.clck_if in clck_links):
				# Transceiver was stopped
				clck_links.remove(self.clck_if)
			elif self.running and (self.clck_if not in clck_links):
				# Transceiver was started
				clck_links.append(self.clck_if)

			if not self.clck_gen.timer and len(clck_links) > 0:
				log.info("Starting clock generator")
				self.clck_gen.start()
			elif self.clck_gen.timer and not clck_links:
				log.info("Stopping clock generator")
				self.clck_gen.stop()

	def recv_data_msg(self):
		# Read and parse data from socket
		msg = self.data_if.recv_l12trx_msg()
		if not msg:
			return None

		# Make sure that transceiver is configured and running
		if not self.running:
			log.warning("(%s) RX TRXD message (%s), but transceiver "
				"is not running => dropping..." % (self, msg.desc_hdr()))
			return None

		# Make sure that indicated timeslot is configured
		if msg.tn not in self.ts_list:
			log.warning("(%s) RX TRXD message (%s), but timeslot is not "
				"configured => dropping..." % (self, msg.desc_hdr()))
			return None

		return msg