fake_trx: use DATAMSG classes for DATA messages

The DATAMSG API, that was introduced and extended a few commits
before, provides all required methods to create, validate,
generate and parse DATA messages. Let's use it now.

Change-Id: Ibc99126dc05d873c1ba538a5f4e74866de563f56
This commit is contained in:
Vadim Yanitskiy 2018-01-27 21:47:31 +07:00
parent 263ccef268
commit d273ebf611
4 changed files with 114 additions and 157 deletions

View File

@ -4,7 +4,7 @@
# Auxiliary tool to generate and send random bursts via TRX DATA # Auxiliary tool to generate and send random bursts via TRX DATA
# interface, which may be useful for fuzzing and testing # interface, which may be useful for fuzzing and testing
# #
# (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com> # (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com>
# #
# All Rights Reserved # All Rights Reserved
# #
@ -22,7 +22,6 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import random
import signal import signal
import getopt import getopt
import sys import sys
@ -30,6 +29,7 @@ import sys
from rand_burst_gen import RandBurstGen from rand_burst_gen import RandBurstGen
from data_if import DATAInterface from data_if import DATAInterface
from gsm_shared import * from gsm_shared import *
from data_msg import *
COPYRIGHT = \ COPYRIGHT = \
"Copyright (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>\n" \ "Copyright (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
@ -70,38 +70,56 @@ class Application:
# Init random burst generator # Init random burst generator
burst_gen = RandBurstGen() 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 # Generate a random frame number or use provided one
if self.fn is None: fn_init = msg.rand_fn() if self.fn is None else self.fn
fn = random.randint(0, GSM_HYPERFRAME)
else:
fn = self.fn
# Send as much bursts as required # Send as much bursts as required
for i in range(self.burst_count): 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
# TODO: also set TRX2L1 specific fields
# Generate a random burst # Generate a random burst
if self.burst_type == "NB": if self.burst_type == "NB":
buf = burst_gen.gen_nb() burst = burst_gen.gen_nb()
elif self.burst_type == "FB": elif self.burst_type == "FB":
buf = burst_gen.gen_fb() burst = burst_gen.gen_fb()
elif self.burst_type == "SB": elif self.burst_type == "SB":
buf = burst_gen.gen_sb() burst = burst_gen.gen_sb()
elif self.burst_type == "AB": elif self.burst_type == "AB":
buf = burst_gen.gen_ab() burst = burst_gen.gen_ab()
print("[i] Sending %d/%d %s burst (fn=%u) to %s..." # 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, % (i + 1, self.burst_count, self.burst_type,
fn, self.conn_mode)) msg.desc_hdr(), self.conn_mode))
# Send to TRX or L1 # Send message
if self.conn_mode == "TRX": self.data_if.send_msg(msg)
self.data_if.send_trx_msg(buf,
self.tn, fn, self.pwr)
elif self.conn_mode == "L1":
self.data_if.send_l1_msg(buf,
self.tn, fn, self.pwr)
# Increase frame number (for count > 1)
fn = (fn + 1) % GSM_HYPERFRAME
self.shutdown() self.shutdown()

View File

@ -3,7 +3,7 @@
# Auxiliary tool to send existing bursts via TRX DATA interface # Auxiliary tool to send existing bursts via TRX DATA interface
# #
# (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com> # (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com>
# #
# All Rights Reserved # All Rights Reserved
# #
@ -21,13 +21,13 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import random
import signal import signal
import getopt import getopt
import sys import sys
from data_if import DATAInterface from data_if import DATAInterface
from gsm_shared import * from gsm_shared import *
from data_msg import *
COPYRIGHT = \ COPYRIGHT = \
"Copyright (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>\n" \ "Copyright (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
@ -74,40 +74,61 @@ class Application:
print("[i] Reading bursts from stdin...") print("[i] Reading bursts from stdin...")
src = sys.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 # Generate a random frame number or use provided one
if self.fn is None: fn = msg.rand_fn() if self.fn is None else self.fn
fn = random.randint(0, GSM_HYPERFRAME)
else:
fn = self.fn
# Read the burst source line-by-line # Read the burst source line-by-line
for line in src: for line in src:
# Strip spaces # Strip spaces
burst = line.strip() burst_str = line.strip()
buf = [] burst = []
# Check length # Check length
if len(burst) != 148: if len(burst_str) != 148:
print("[!] Dropping burst due to length != 148") print("[!] Dropping burst due to length != 148")
continue continue
print("[i] Sending a burst (fn=%u) to %s..." # Randomize the message header
% (fn, self.conn_mode)) 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
# TODO: also set TRX2L1 specific fields
# Parse a string # Parse a string
for bit in burst: for bit in burst_str:
if bit == "1": if bit == "1":
buf.append(1) burst.append(1)
else: else:
buf.append(0) burst.append(0)
# Send to TRX or L1 # Convert to soft-bits in case of TRX -> L1 message
if self.conn_mode == "TRX": if self.conn_mode == "L1":
self.data_if.send_trx_msg(buf, burst = msg.ubit2sbit(burst)
self.tn, fn, self.pwr)
elif self.conn_mode == "L1": # Set burst
self.data_if.send_l1_msg(buf, msg.burst = burst
self.tn, fn, self.pwr)
print("[i] Sending a burst %s to %s..."
% (msg.desc_hdr(), self.conn_mode))
# Send message
self.data_if.send_msg(msg)
# Increase frame number (for count > 1) # Increase frame number (for count > 1)
fn = (fn + 1) % GSM_HYPERFRAME fn = (fn + 1) % GSM_HYPERFRAME

View File

@ -4,7 +4,7 @@
# Virtual Um-interface (fake transceiver) # Virtual Um-interface (fake transceiver)
# DATA interface implementation # DATA interface implementation
# #
# (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com> # (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com>
# #
# All Rights Reserved # All Rights Reserved
# #
@ -22,85 +22,18 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import random
from udp_link import UDPLink from udp_link import UDPLink
from gsm_shared import * from data_msg import *
class DATAInterface(UDPLink): class DATAInterface(UDPLink):
def send_l1_msg(self, burst, def send_msg(self, msg):
tn = None, fn = None, rssi = None): # Validate a message
# Generate random timeslot index if not preset if not msg.validate():
if tn is None: raise ValueError("Message incomplete or incorrect")
tn = random.randint(0, 7)
# Generate random frame number if not preset # Generate TRX message
if fn is None: payload = msg.gen_msg()
fn = random.randint(0, GSM_HYPERFRAME)
# Generate random RSSI if not preset
if rssi is None:
rssi = -random.randint(-75, -50)
# Prepare a buffer for header and burst
buf = []
# Put timeslot index
buf.append(tn)
# Put frame number
buf.append((fn >> 24) & 0xff)
buf.append((fn >> 16) & 0xff)
buf.append((fn >> 8) & 0xff)
buf.append((fn >> 0) & 0xff)
# Put RSSI
buf.append(rssi)
# HACK: put fake TOA value
buf += [0x00] * 2
# Put burst
buf += burst
# Put two unused bytes
buf += [0x00] * 2
# Send message # Send message
self.send(bytearray(buf)) self.send(payload)
def send_trx_msg(self, burst,
tn = None, fn = None, pwr = None):
# Generate random timeslot index if not preset
if tn is None:
tn = random.randint(0, 7)
# Generate random frame number if not preset
if fn is None:
fn = random.randint(0, GSM_HYPERFRAME)
# Generate random power level if not preset
if pwr is None:
pwr = random.randint(0, 34)
# Prepare a buffer for header and burst
buf = []
# Put timeslot index
buf.append(tn)
# Put frame number
buf.append((fn >> 24) & 0xff)
buf.append((fn >> 16) & 0xff)
buf.append((fn >> 8) & 0xff)
buf.append((fn >> 0) & 0xff)
# Put transmit power level
buf.append(pwr)
# Put burst
buf += burst
# Send message
self.send(bytearray(buf))

View File

@ -27,6 +27,8 @@ import sys
import scapy.all import scapy.all
from data_msg import *
COPYRIGHT = \ COPYRIGHT = \
"Copyright (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>\n" \ "Copyright (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
"License GPLv2+: GNU GPL version 2 or later " \ "License GPLv2+: GNU GPL version 2 or later " \
@ -98,32 +100,39 @@ class Application:
trx = udp.payload trx = udp.payload
# Convert to bytearray # Convert to bytearray
trx = bytearray(str(trx)) msg_raw = bytearray(str(trx))
# Parse GSM TDMA specific data
fn = (trx[1] << 24) | (trx[2] << 16) | (trx[3] << 8) | trx[4]
tn = trx[0]
# Determine a burst direction (L1 <-> TRX) # Determine a burst direction (L1 <-> TRX)
l12trx = udp.sport > udp.dport 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 # Poke burst pass filter
rc = self.burst_pass_filter(l12trx, fn, tn) rc = self.burst_pass_filter(l12trx, msg.fn, msg.tn)
if rc is False: if rc is False:
self.cnt_burst_dropped_num += 1 self.cnt_burst_dropped_num += 1
return return
# Debug print # Debug print
print("[i] %s burst: fn=%u, tn=%d" \ print("[i] %s burst: %s" \
% ("L1 -> TRX" if l12trx else "TRX -> L1", fn, tn)) % ("L1 -> TRX" if l12trx else "TRX -> L1", msg.desc_hdr()))
# Poke burst handler # Poke burst handler
rc = self.burst_handle(trx, l12trx, fn, tn) rc = self.burst_handle(l12trx, msg_raw, msg)
if rc is False: if rc is False:
self.shutdown() self.shutdown()
# Poke burst counter # Poke burst counter
rc = self.burst_count(fn, tn) rc = self.burst_count(msg.fn, msg.tn)
if rc is True: if rc is True:
self.shutdown() self.shutdown()
@ -149,47 +158,23 @@ class Application:
# Burst passed ;) # Burst passed ;)
return True return True
def burst_handle(self, trx_burst, l12trx, fn, tn): def burst_handle(self, l12trx, msg_raw, msg):
if self.print_bursts: if self.print_bursts:
self.burst_dump_bits(sys.stdout, trx_burst, l12trx) print(msg.burst)
sys.stdout.flush()
if self.output_file is not None: if self.output_file is not None:
# TLV: tag defines burst direction (one byte, BE) # TLV: tag defines burst direction (one byte, BE)
self.output_file.write('\x01' if l12trx else '\x02') self.output_file.write('\x01' if l12trx else '\x02')
# TLV: length of value (one byte, BE) # TLV: length of value (one byte, BE)
length = len(trx_burst) length = len(msg_raw)
self.output_file.write(chr(length)) self.output_file.write(chr(length))
# TLV: raw value # TLV: raw value
self.output_file.write(trx_burst) self.output_file.write(msg_raw)
return True return True
def burst_dump_bits(self, dst, trx_burst, l12trx):
# Split out burst header
if l12trx:
burst = trx_burst[6:]
else:
burst = trx_burst[8:]
# Print normal bits: 0 or 1
for i in range(0, 148):
# Convert bits to chars
if l12trx:
# Convert bits as is
bit = '1' if burst[i] else '0'
else:
# Convert trx bits {254..0} to sbits {-127..127}
bit = -127 if burst[i] == 255 else 127 - burst[i]
# Convert sbits {-127..127} to ubits {0..1}
bit = '1' if bit < 0 else '0'
# Write a normal bit
dst.write(bit)
dst.write("\n")
def burst_count(self, fn, tn): def burst_count(self, fn, tn):
# Update frame counter # Update frame counter
if self.cnt_frame_last is None: if self.cnt_frame_last is None: