summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVadim Yanitskiy <axilirator@gmail.com>2018-02-20 19:21:54 +0700
committerVadim Yanitskiy <axilirator@gmail.com>2018-02-20 19:29:00 +0700
commitd406afd23e5e0efb38a11bd40a7ff6f0a01ee7f6 (patch)
tree32e948458b2bf2ab1b3709e6d3c773b48d5265ec
parent711e2f256e125398cca69b841493d3063389cef1 (diff)
fake_trx/burst_send.py: implement DATA capture support
Previously, this tool was only able to read a hand-crafted text file with bursts and send them via the DATA interface. This is not so useful... This change implements support of reading DATA capture files, which can be generated e.g. by trx_sniff.py or burst_gen.py. Both standart input (stdio) and text-files are not supported anymore. Usage example: ./burst_send.py -m L1 -i capture.bin --timeslot 2 Change-Id: I626662bd1897c874421ab5178970ec19325f8a47
-rw-r--r--src/target/fake_trx/README6
-rwxr-xr-xsrc/target/fake_trx/burst_send.py205
2 files changed, 103 insertions, 108 deletions
diff --git a/src/target/fake_trx/README b/src/target/fake_trx/README
index 5d4960f4..7b27414b 100644
--- a/src/target/fake_trx/README
+++ b/src/target/fake_trx/README
@@ -28,9 +28,9 @@ Brief description of available applications:
Currently it is only possible to generate random bursts of
different types: NB, FB, SB, AB.
- - burst_send.py - a tool for sending existing bursts from file
- or standard input either to L1 (OsmoBTS or OsmocomBB) or to
- TRX (OsmoTRX and GR-GSM TRX).
+ - burst_send.py - a tool for sending existing bursts from a
+ capture file either to L1 (OsmoBTS or OsmocomBB) or to
+ TRX (e.g. OsmoTRX or GR-GSM TRX).
- trx_sniff.py - Scapy-based TRX protocol sniffer. Allows one
to observe a single connection between TRX and L1, and vice
diff --git a/src/target/fake_trx/burst_send.py b/src/target/fake_trx/burst_send.py
index b51ce5d4..87f9228b 100755
--- a/src/target/fake_trx/burst_send.py
+++ b/src/target/fake_trx/burst_send.py
@@ -25,12 +25,13 @@ import signal
import getopt
import sys
+from data_dump import DATADumpFile
from data_if import DATAInterface
from gsm_shared import *
from data_msg import *
COPYRIGHT = \
- "Copyright (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
+ "Copyright (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
"License GPLv2+: GNU GPL version 2 or later " \
"<http://gnu.org/licenses/gpl.html>\n" \
"This is free software: you are free to change and redistribute it.\n" \
@@ -42,16 +43,17 @@ class Application:
base_port = 5700
conn_mode = "TRX"
- burst_src = None
-
- # Common header fields
- fn = None
- tn = None
+ # Burst source
+ capture_file = None
- # Message specific header fields
- rssi = None
- toa = None
- pwr = 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):
self.print_copyright()
@@ -60,81 +62,37 @@ class Application:
# 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.base_port + 102)
+ l12trx = True
elif self.conn_mode == "L1":
self.data_if = DATAInterface(self.remote_addr,
self.base_port + 102, self.base_port + 2)
+ l12trx = False
else:
self.print_help("[!] Unknown connection type")
sys.exit(2)
- # Open the burst source (file or stdin)
- if self.burst_src is not None:
- print("[i] Reading bursts from file '%s'..." % self.burst_src)
- src = open(self.burst_src, "r")
- else:
- print("[i] Reading bursts from stdin...")
- src = sys.stdin
-
- # 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 = msg.rand_fn() if self.fn is None else self.fn
-
- # Read the burst source line-by-line
- for line in src:
- # Strip spaces
- burst_str = line.strip()
- burst = []
+ # Read messages from the capture
+ messages = self.ddf.parse_all(
+ skip = self.msg_skip, count = self.msg_count)
+ if messages is False:
+ pass # FIXME!!!
- # Check length
- if len(burst_str) not in (GSM_BURST_LEN, EDGE_BURST_LEN):
- print("[!] Dropping burst due to incorrect length")
+ for msg in messages:
+ # Pass filter
+ if not self.msg_pass_filter(l12trx, msg):
continue
- # Randomize the message header
- msg.rand_hdr()
-
- # Set frame number
- msg.fn = fn
-
- # 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.toa is not None:
- msg.toa = self.toa
-
- # Set RSSI
- if self.rssi is not None:
- msg.rssi = self.rssi
-
- # Parse a string
- for bit in burst_str:
- if bit == "1":
- burst.append(1)
- else:
- burst.append(0)
-
- # Convert to soft-bits in case of TRX -> L1 message
- if self.conn_mode == "L1":
- burst = msg.ubit2sbit(burst)
-
- # Set burst
- msg.burst = burst
+ # HACK: as ToA parsing is not implemented yet,
+ # we have to use a fixed 0.00 value for now...
+ msg.toa = 0.00
print("[i] Sending a burst %s to %s..."
% (msg.desc_hdr(), self.conn_mode))
@@ -142,32 +100,56 @@ class Application:
# Send message
self.data_if.send_msg(msg)
- # Increase frame number (for count > 1)
- fn = (fn + 1) % GSM_HYPERFRAME
-
# Finish
self.shutdown()
+ 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_copyright(self):
print(COPYRIGHT)
def print_help(self, msg = None):
s = " Usage: " + sys.argv[0] + " [options]\n\n" \
" Some help...\n" \
- " -h --help this text\n\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" \
- " -p --base-port Set base port number (default %d)\n\n"
-
- s += " Burst generation\n" \
- " -i --burst-file Read bursts from file (default stdin)\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 (default random)\n\n"
+ " -m --conn-mode Send bursts to: TRX (default) / L1\n" \
+ " -r --remote-addr Set remote 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.base_port))
@@ -177,18 +159,18 @@ class Application:
def parse_argv(self):
try:
opts, args = getopt.getopt(sys.argv[1:],
- "m:r:p:i:f:t:h",
+ "m:r:p:i:h",
[
"help",
"conn-mode=",
"remote-addr=",
"base-port=",
- "burst-file=",
- "frame-number=",
+ "capture-file=",
+ "msg-skip=",
+ "msg-count=",
"timeslot=",
- "rssi=",
- "toa=",
- "pwr=",
+ "frame-num-lt=",
+ "frame-num-gt=",
])
except getopt.GetoptError as err:
self.print_help("[!] " + str(err))
@@ -199,6 +181,11 @@ class Application:
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"):
@@ -206,20 +193,28 @@ class Application:
elif o in ("-p", "--base-port"):
self.base_port = int(v)
- elif o in ("-i", "--burst-file"):
- self.burst_src = 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 == "--toa":
- self.toa = float(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 shutdown(self):
self.data_if.shutdown()