mobile / csn1: extend GSM RR support and include unit tests

This commit is contained in:
mich 2018-11-26 17:57:11 +01:00
parent e592af3cbb
commit 7d3e13c8db
7 changed files with 291 additions and 182 deletions

View File

@ -111,6 +111,7 @@ class CSN1Obj(Element):
ret.append('H')
return ''.join(ret)
def __init__(self, **kw):
if 'name' in kw and kw['name']:
self._name = kw['name']
@ -216,7 +217,7 @@ class CSN1Obj(Element):
return self._val
def __call__(self):
return self._val
return self.get_val()
def _resolve_ref(self, ref):
"""resolves the reference for dynamic values
@ -256,6 +257,11 @@ class CSN1Obj(Element):
# raise(CSN1Err('not implemented'))
def _from_char(self, char):
# TODO: ultimately, this offset reset could be removed...
self._off = 0
self._from_char_csn(char)
def _from_char_csn(self, char):
global _root_obj
#
if self._par is None:
@ -279,6 +285,7 @@ class CSN1Obj(Element):
else:
# static number of repetitions
num = self._num
#csnlog('%-20s: offset %i' % (self._name, self._off))
if num == 1:
self._from_char_obj(char)
elif num > 1:
@ -316,6 +323,11 @@ class CSN1Obj(Element):
_root_obj = root_obj
def _to_pack(self):
# TODO: ultimately, this offset reset could be removed...
self._off = 0
return self._to_pack_csn()
def _to_pack_csn(self):
global _root_obj
#
if self._par is None:
@ -332,6 +344,7 @@ class CSN1Obj(Element):
else:
# static number of repetitions
num = self._num
#csnlog('%-20s: offset %i' % (self._name, self._off))
if num == 1:
ret.extend( self._to_pack_obj() )
elif num == -1 or num > 1:
@ -422,8 +435,8 @@ class CSN1Bit(CSN1Obj):
self._bit = kw['bit']
if 'type' in kw and kw['type'] != self.__class__._type:
self._type = kw['type']
if 'dict' in kw and kw['dict'] is not None:
self._dic = kw['dict']
if 'dic' in kw and kw['dic'] is not None:
self._dic = kw['dic']
def _repr_val(self):
if self._REPR == 'B' and self._type == CSN1T_UINT:
@ -502,27 +515,47 @@ class CSN1Val(CSN1Obj):
"""Class to handle a CSN.1 value
specific internal attributes:
- stat: fixed bit string or 'null'
- val: bit string or 'null'
specific init args:
- val: fixed bit string or 'null' to set _stat attribute
"""
_val = ''
_val = None
def __init__(self, **kw):
CSN1Obj.__init__(self, **kw)
if 'L' in self._val or 'H' in self._val:
if 'name' in kw and kw['name']:
self._name = kw['name']
if 'num' in kw and kw['num'] != 1:
self._num = kw['num']
if 'lref' in kw and kw['lref'] is not None:
self._lref = kw['lref']
# self._val can be used temporarly during encoding / decoding
# hence the need to keep the static value into another attribute _stat
if 'val' in kw:
self._stat = kw['val']
else:
assert()
# offset for dealing with L / H bits
self._off = 0
# verification and constraint on the static value
if 'L' in self._stat or 'H' in self._stat:
# TODO: support mixed padding L/H and non-padding 0/1 values
assert( '0' not in self._val )
assert( '1' not in self._val )
assert( '0' not in self._stat )
assert( '1' not in self._stat )
self._pad_gsm = 1
else:
self._pad_gsm = 0
if self._val[-2:] == '**':
self._val = self._val[:-2]
if self._stat[-2:] == '**':
self._stat = self._stat[:-2]
self._num = -1
#def set_val(self, val):
# # CSN1Val is static, should never be updated
# pass
def set_val(self, val):
self._val = val
def get_val(self):
return self._val
def _repr_val(self):
return self._val.__repr__()
@ -530,52 +563,55 @@ class CSN1Val(CSN1Obj):
_show_val = _repr_val
def _from_char_obj(self, char):
if self._val == 'null':
if self._stat == 'null':
return
bit = len(self._val)
if bit:
try:
val = char.get_uint(bit)
except CharpyErr:
raise(CSN1NoCharErr())
else:
if self._pad_gsm:
# convert val to a bit-string to be compared to self._val
val_p = CSN1Obj.conv_b_pad_gsm(uint_to_bitstr(val, bit), self._off)
if self._val != val_p:
raise(CSN1InvalidValueErr())
else:
if int(self._val, 2) != val:
raise(CSN1InvalidValueErr())
self._off = (self._off + bit) % 8
def _to_pack_obj(self):
if self._val == 'null':
return []
bit = len(self._val)
bit = len(self._stat)
if not bit:
return []
return
try:
val = char.get_uint(bit)
except CharpyErr:
raise(CSN1NoCharErr())
else:
if self._pad_gsm:
bl = []
for i, c in enumerate(self._val):
p = self.Lv[self._off+i]
if c == 'L':
# padding value
bl.append(p)
else:
# non-padding value
bl.append(p^1)
self._off = (self._off + bit) % 8
return [(CSN1T_UINT, bitlist_to_uint(bl), bit)]
# convert val to a bit-string to be compared to self._stat
val_p = CSN1Obj.conv_b_pad_gsm(uint_to_bitstr(val, bit), self._off)
if self._stat != val_p:
raise(CSN1InvalidValueErr())
else:
self._val = self._stat
else:
self._off = (self._off + bit) % 8
return [(CSN1T_UINT, int(self._val, 2), bit)]
if int(self._stat, 2) != val:
raise(CSN1InvalidValueErr())
else:
self._val = self._stat
self._off = (self._off + bit) % 8
def _to_pack_obj(self):
if self._stat == 'null':
return []
bit = len(self._stat)
if not bit:
return []
if self._pad_gsm:
bl = []
for i, c in enumerate(self._stat):
p = self.Lv[self._off+i]
if c == 'L':
# padding value
bl.append(p)
else:
# non-padding value
bl.append(p^1)
self._off = (self._off + bit) % 8
return [(CSN1T_UINT, bitlist_to_uint(bl), bit)]
else:
self._off = (self._off + bit) % 8
return [(CSN1T_UINT, int(self._stat, 2), bit)]
def clone(self):
kw = self._clone_get_kw()
if self._val != self.__class__._val:
kw['val'] = self._val
kw['val'] = self._stat
return self.__class__(**kw)
@ -585,6 +621,9 @@ class CSN1Ref(CSN1Obj):
specific internal attributes:
- obj: ASN1Obj instance
- val: value according to obj
specific init args:
- obj
"""
_obj = None
@ -613,7 +652,7 @@ class CSN1Ref(CSN1Obj):
obj_off, obj_val = self._obj._off, self._obj._val
self._obj._off = self._off
try:
self._obj._from_char(char)
self._obj._from_char_csn(char)
except (CSN1NoCharErr, CSN1InvalidValueErr) as err:
# restore offset and val
self._obj._off, self._obj._val = obj_off, obj_val
@ -628,7 +667,7 @@ class CSN1Ref(CSN1Obj):
# transfer offset and value to self._obj
obj_off, obj_val = self._obj._off, self._obj._val
self._obj._off, self._obj._val = self._off, self._val
ret = self._obj._to_pack()
ret = self._obj._to_pack_csn()
# restore offset and value
self._off = self._obj._off
self._obj._off, self._obj._val = obj_off, obj_val
@ -647,6 +686,10 @@ class CSN1List(CSN1Obj):
- list : list of CSN1Obj instances
- trunc: enables the list of values to be truncated
- val : list of CSN1Obj values
specific init args:
- list
- trunc
"""
_list = []
@ -694,7 +737,7 @@ class CSN1List(CSN1Obj):
obj_off, obj_val = Obj._off, Obj._val
Obj._off = self._off
try:
Obj._from_char(char)
Obj._from_char_csn(char)
except (CSN1NoCharErr, CSN1InvalidValueErr) as err:
# restore offset and val
Obj._off, Obj._val = obj_off, obj_val
@ -715,7 +758,7 @@ class CSN1List(CSN1Obj):
# transfer offset and value to Obj
obj_off, obj_val = Obj._off, Obj._val
Obj._off, Obj._val = self._off, val
ret.extend( Obj._to_pack() )
ret.extend( Obj._to_pack_csn() )
# restore offset and value
self._off = Obj._off
Obj._off, Obj._val = obj_off, obj_val
@ -861,7 +904,7 @@ class CSN1Alt(CSN1Obj):
obj_off, obj_val = Obj._off, Obj._val
Obj._off = self._off
try:
Obj._from_char(char)
Obj._from_char_csn(char)
except (CSN1NoCharErr, CSN1InvalidValueErr) as err:
# restore offset and val
Obj._off, Obj._val = obj_off, obj_val
@ -905,7 +948,7 @@ class CSN1Alt(CSN1Obj):
# transfer offset and value to Obj
obj_off, obj_val = Obj._off, Obj._val
Obj._off, Obj._val = self._off, val
ret.extend( Obj._to_pack() )
ret.extend( Obj._to_pack_csn() )
# restore offset and value
self._off = Obj._off
Obj._off, Obj._val = obj_off, obj_val
@ -928,7 +971,7 @@ class CSN1Alt(CSN1Obj):
class CSN1SelfRef(CSN1Obj):
"""Class to handle a reference to the root CSN1 object
"""Class to handle a reference to a root CSN1 object
specific internal attributes:
- val: value according to the root object
@ -956,23 +999,24 @@ class CSN1SelfRef(CSN1Obj):
raise(CSN1Err('{0}: no root object referenced'.format(self._name)))
# preserve already existing root object value and offset
# and transfer offset to it
root_obj_off, root_obj_val = _root_obj._off, _root_obj._val
obj_off, obj_val = _root_obj._off, _root_obj._val
_root_obj._off = self._off
_root_obj._from_char(char)
_root_obj._from_char_csn(char)
self._off, self._val = _root_obj._off, _root_obj._val
# restore initial value and offset
_root_obj._off, _root_obj._val = root_obj_off, root_obj_val
_root_obj._off, _root_obj._val = obj_off, obj_val
def _to_pack_obj(self):
global _root_obj
if _root_obj is None:
raise(CSN1Err('{0}: no root object referenced'.format(self._name)))
# preserve already existing root object value
root_obj_off, root_obj_val = _root_obj._off, _root_obj._val
obj_off, obj_val = _root_obj._off, _root_obj._val
_root_obj._off, _root_obj._val = self._off, self._val
ret = _root_obj._to_pack()
# restore initial value
_root_obj._off, _root_obj._val = root_obj_off, root_obj_val
ret = _root_obj._to_pack_csn()
# restore initial offset and value
self._off = _root_obj._off
_root_obj._off, _root_obj._val = obj_off, obj_val
return ret
def clone(self):

View File

@ -107,6 +107,8 @@ WARN: unable to process arithmetic expression, (M-1)
## 24.008
## ms_ra_capability_value_part.py
## 44.018
## ia_rest_octets.py
# create a dict:
_AccessTechnoType_dict = {
@ -126,7 +128,9 @@ _AccessTechnoType_dict = {
13 : 'GSM T 810',
}
# ref it with a kdic=_AccessTechnoType_dict
# within the object ms_ra_capability_value_part_struct
# within the object ms_ra_capability_value_part.ms_ra_capability_value_part_struct
# and a dic=_AccessTechnoType_dict
# within the object ia_rest_octets.access_technologies_request_struct
## 44.018

View File

@ -37,6 +37,23 @@ from pycrate_csn1dir.egprs_window_size_ie import egprs_window_size_ie
from pycrate_csn1dir.packet_timing_advance_ie import packet_timing_advance_ie
from pycrate_csn1dir.egprs_level_ie import egprs_level_ie
_AccessTechnoType_dict = {
0 : 'GSM P',
1 : 'GSM E --note that GSM E covers GSM P',
2 : 'GSM R --note that GSM R covers GSM E and GSM P',
3 : 'GSM 1800',
4 : 'GSM 1900',
5 : 'GSM 450',
6 : 'GSM 480',
7 : 'GSM 850',
8 : 'GSM 750',
9 : 'GSM T 380',
10 : 'GSM T 410',
11 : 'unused',
12 : 'GSM 710',
13 : 'GSM T 810',
}
# code automatically generated by pycrate_csn1
# change object type with type=CSN1T_BSTR (default type is CSN1T_UINT) in init
# add dict for value interpretation with dic={...} in CSN1Bit init
@ -49,7 +66,7 @@ Spare_padding = spare_padding
Spare_Padding = spare_padding
access_technologies_request_struct = CSN1List(name='access_technologies_request_struct', list=[
CSN1Bit(name='access_technology_type', bit=4),
CSN1Bit(name='access_technology_type', bit=4, dic=_AccessTechnoType_dict),
CSN1Alt(alt={
'0': ('', []),
'1': ('', [

View File

@ -89,7 +89,7 @@ class Layer3(Envelope):
self._sec = sec
# build a list of (tag length, tag value) for the optional part
# configure IE set by **kw as non-transparent and set their value
self._opts = []
self._opts, self._rest = [], None
if val is None:
# go faster by just looking for optional IE
for ie in self._content:
@ -97,6 +97,9 @@ class Layer3(Envelope):
# optional IE
T = ie[0]
self._opts.append( (T.get_bl(), T(), ie) )
elif isinstance(ie, RestOctets):
# rest octets
self._rest = ie
else:
for ie in self._content:
if isinstance(ie, (Type1V, Type1TV)):
@ -128,6 +131,8 @@ class Layer3(Envelope):
self._opts.append( (8, ie[0](), ie) )
if ie._name in val:
ie._trans = False
elif isinstance(ie, RestOctets):
self._rest = ie
elif ie._name in val:
ie.set_val(val[ie._name])
@ -145,9 +150,13 @@ class Layer3(Envelope):
# in case some optional IE are set (with transparency enabled)
# they are decoded as much as the char buffer allows it
# 1) decode mandatory part
if self._rest is not None:
self._rest.set_trans(True)
dec_brk = self.DEC_BREAK_ON_UNK_IE
self.DEC_BREAK_ON_UNK_IE = True
Envelope._from_char(self, char)
# 2) decode optional part
opts = self._opts[:]
opts, dec = self._opts[:], False
while char.len_bit() >= 8:
T4, T8, dec = char.to_uint(4), char.to_uint(8), False
for i, opt in enumerate(opts):
@ -163,11 +172,16 @@ class Layer3(Envelope):
if not dec:
# unknown IEI
if self.DEC_BREAK_ON_UNK_IE:
log('%s, unknown IE remaining, not decoded' % self._name)
#log('%s, unknown IE remaining, not decoded' % self._name)
break
else:
char._cur += 8
self._dec_unk_ie(T8, char)
# 3) decode rest octets
if not dec and self._rest is not None:
self._rest.set_trans(False)
self.DEC_BREAK_ON_UNK_IE = dec_brk
self._rest._from_char(char)
def _dec_unk_ie(self, T8, char):
if T8 & 0x80:
@ -515,17 +529,23 @@ class Type6TLVE(IE):
self[2].set_blauto(lambda: 8*self[1].get_val())
class RestOctets(Type3V):
class RestOctets(IE):
"""Rest octets (or Type5) IE is a specific IE only used in GSM / GPRS
its content is a single buffer of variable length, which is tied to the
L2PseudoLength at the beginning of the L3 GSM message containing it
"""
_GEN = (
BufAuto('V', rep=REPR_HEX),
)
def __init__(self, *args, **kwargs):
Type3V.__init__(self, *args, **kwargs)
# The length of V is not fixed, but tied to the L2PseudoLength element
# prefixing the envelope element
self[0].set_blauto(lambda: 176 - (self.get_env()[0][0].get_val()<<3))
IE.__init__(self, *args, **kwargs)
self[0].PAD_VAL = b'+' # 0x2b, GSM padding
if self[0]._bl is None:
# in case the length is not fixed at init, it is handled in
# a dynamic way, tied to the L2PseudoLength element prefixing the
# parent Layer3 envelope
self[0].set_blauto(lambda: 176 - (self.get_env()[0][0].get_val()<<3))
#------------------------------------------------------------------------------#

View File

@ -92,8 +92,11 @@ from pycrate_csn1dir.gprs_broadcast_information_value_part import gprs_broa
from pycrate_csn1dir.mprach_description_value_part import mprach_description_value_part
from pycrate_csn1dir.mbms_p_t_m_channel_description_value_part import mbms_p_t_m_channel_description_value_part
from pycrate_csn1dir.mbms_session_parameters_list_value_part import mbms_session_parameters_list_value_part
from pycrate_csn1dir.ec_packet_channel_description_type_1 import ec_packet_channel_description_type_1
from pycrate_csn1dir.rr_packet_downlink_assignment_type_2_value_part import \
rr_packet_downlink_assignment_type_2_value_part
from pycrate_csn1dir.ec_immediate_assignment_type_2_message_content import \
ec_immediate_assignment_type_2_message_content
from pycrate_csn1dir.cell_selection_indicator_after_release_of_all_tch_and_sdcch_value_part import \
cell_selection_indicator_after_release_of_all_tch_and_sdcch_value_part
@ -102,45 +105,13 @@ from pycrate_csn1dir.cell_selection_indicator_after_release_of_all_tch_and_sdcch
# generic objects
#------------------------------------------------------------------------------#
'''to be removed
def smod(n, m):
"""
offset remainder of the euclidian division of n by m:
1 <= (n smod m) <= m and there exists k such that
n = (k*m) + (n smod m);
"""
r = n%m
if r == 0:
return m
else:
return r
def build_ext_dict(d, kend=None):
"""extends a dict d that has only integral keys
"""
if not d:
return {}
di = sorted(d.items())
ret = [di[0]]
for i in range(1, len(di)):
while di[i][0] > 1+ret[-1][0]:
# hole in d, extend ret
ret.append( (ret[-1][0]+1, ret[-1][1]) )
ret.append( di[i] )
if kend:
while kend > ret[-1][0]:
ret.append( (ret[-1][0]+1, ret[-1][1]) )
return dict(ret)
'''
class BitMap(Buf):
"""handles bit map
derives from the Buf object and includes get() / set() / unset() methods
for handling bit value at given offset
"""
_pre = REPR_BIN
_rep = REPR_HEX
# dedicated method to get, set and unset at a given offset
def get(self, off):
@ -401,8 +372,8 @@ class FreqListBitmap0(BitMap):
"""returns the list of ARFCNs set
"""
arfcns = []
ar_uint = self.to_uint()
for i in range(0, 124):
ar_uint, ar_bl = self.to_uint(), 124
for i in range(0, ar_bl):
if ar_uint & (1<<i):
arfcns.append(1+i)
arfcns.sort()
@ -411,9 +382,9 @@ class FreqListBitmap0(BitMap):
def encode(self, arfcns):
"""sets a list of ARFCNs
"""
ar_uint = 0
ar_uint, ar_bl = 0, 124
for ar in set(arfcns):
if isinstance(ar, integer_types) and 0 < ar <= 124:
if isinstance(ar, integer_types) and 0 < ar <= ar_bl:
ar_uint += 1<<(124-ar)
self.set_val(uint_to_bytes(ar_uint, 124))
@ -446,7 +417,7 @@ class CellChanAlt2(_FreqListAlt2):
0: CellChanRange512(),
1: CellChanRange256(),
2: CellChanRange128(),
3: FreqListBitmapVar('CellChanBitmapVar')},
3: FreqListBitmapVar('CellChanBitmapVar', bl=111)},
sel=lambda self: self.get_env()[0].get_val())
)
@ -825,7 +796,7 @@ class CtrlChanDesc(Envelope):
Uint('BS-AG-BLKS-RES', bl=3),
Uint('CCCHConf', bl=3, dic=_CCCHConf_dict),
Uint('SI22Ind', bl=1, dic={1:'SI22 broadcasted'}),
Uint('CBQ3', bl=1, dic=_CBQ3_dict),
Uint('CBQ3', bl=2, dic=_CBQ3_dict),
Uint('spare', bl=2),
Uint('BS-PA-MFRMS', bl=3),
Uint8('T3212', dic={0:'periodic location updating shall not be used in the cell'})
@ -902,7 +873,7 @@ class FreqListAlt2(_FreqListAlt2):
)
class FreqListAlt1(_FreqList):
class FreqListAlt1(_FreqListAlt1):
_GEN = (
Uint('FmtExt', bl=1, dic={0:'range 1024'}),
Alt(GEN={
@ -958,7 +929,7 @@ class FreqShortListAlt2(_FreqListAlt2):
0: FreqShortListRange512(),
1: FreqShortListRange256(),
2: FreqShortListRange128(),
3: FreqListBitmapVar('FreqShortListBitmapVar')},
3: FreqListBitmapVar('FreqShortListBitmapVar', bl=55)},
sel=lambda self: self.get_env()[0].get_val())
)
@ -1087,7 +1058,10 @@ class L2PseudoLength(Envelope):
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0].set_valauto(lambda: self._get_l2pl())
if self[0]._val is None:
# in case the Value is not fixed at init, it is handled in
# a dynamic way
self[0].set_valauto(lambda: self._get_l2pl())
def _get_l2pl(self):
l2pl = 0
@ -1191,6 +1165,20 @@ class NeighbourCellChan(Envelope):
DEFAULT=Buf('undefined', rep=REPR_HEX),
sel=lambda self: self.get_env()[0].get_val())
)
def decode(self):
"""returns the list of ARFCNs set
"""
try:
return self[3].get_alt().decode()
except:
return []
def encode(self, arfcns):
"""sets the list of ARFCNs
"""
# TODO: choose the best possible encoding ?!
raise(PycrateErr('not implemented'))
#------------------------------------------------------------------------------#
@ -1210,6 +1198,20 @@ class NeighbourCellChan2(Envelope):
1: CellChanAlt1()},
sel=lambda self: self.get_env()[0].get_val())
)
def decode(self):
"""returns the list of ARFCNs set
"""
try:
return self[3].get_alt().decode()
except:
return []
def encode(self, arfcns):
"""sets the list of ARFCNs
"""
# TODO: choose the best possible encoding ?!
raise(PycrateErr('not implemented'))
#------------------------------------------------------------------------------#

View File

@ -749,7 +749,7 @@ def get_tbf(l3msg):
class RRImmediateAssignment(Layer3):
_GEN = (
L2PseudoLength(excl=(0, 9, 10)),
L2PseudoLength(),
RRHeader(val={'Type':63}),
Type1V('DedicatedModeOrTBF', val={'V':0}, IE=DedicatedModeOrTBF()),
Type1V('PageMode', val={'V':0}, dic=PageMode_dict),
@ -815,7 +815,7 @@ class RRImmediateAssignmentExt(Layer3):
class RRImmediateAssignmentReject(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':19}),
RRHeader(val={'Type':58}),
Type1V('FeatureInd', val={'V':0}, IE=FeatureInd()),
Type1V('PageMode', val={'V':0}, dic=PageMode_dict),
@ -827,7 +827,7 @@ class RRImmediateAssignmentReject(Layer3):
Type3V('WaitInd3', val={'V':b'\0'}, bl={'V':8}, IE=T3122()),
Type3V('RequestRef4', val={'V':b'\0\0\0'}, bl={'V':24}, IE=RequestRef()),
Type3V('WaitInd4', val={'V':b'\0'}, bl={'V':8}, IE=T3122()),
RestOctets('IARRestOctets', IE=iar_rest_octets)
RestOctets('IARRestOctets', bl={'V': 24}, IE=iar_rest_octets)
)
@ -858,9 +858,9 @@ class RRMeasurementReport(Layer3):
class RRNotificationNCH(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':32}),
RestOctets('NTNRestOctets', IE=ntn_rest_octets),
RestOctets('NTNRestOctets', bl={'V':160}, IE=ntn_rest_octets),
)
@ -985,7 +985,7 @@ class RRPagingReq2(Layer3):
class RRPagingReq3(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':19}),
RRHeader(val={'Type':36}),
Type1V('ChanNeeded', val={'V':0}, IE=ChanNeeded()),
Type1V('PageMode', val={'V':0}, dic=PageMode_dict),
@ -993,7 +993,7 @@ class RRPagingReq3(Layer3):
Type3V('ID2', val={'V':4*b'\0'}, bl={'V':32}, IE=TMSI()),
Type3V('ID3', val={'V':4*b'\0'}, bl={'V':32}, IE=TMSI()),
Type3V('ID4', val={'V':4*b'\0'}, bl={'V':32}, IE=TMSI()),
RestOctets('P3RestOctets', IE=p3_rest_octets)
RestOctets('P3RestOctets', bl={'V':24}, IE=p3_rest_octets)
)
@ -1088,11 +1088,11 @@ class RRStatus(Layer3):
class RRSystemInfo1(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':21}),
RRHeader(val={'Type':25}),
Type3V('CellChan', val={'V':16*b'\0'}, bl={'V':128}, IE=CellChan()),
Type3V('RACHCtrl', val={'V':b'\0\0\0'}, bl={'V':24}, IE=RACHCtrl()),
RestOctets('SI1RestOctets', IE=si1_rest_octets)
RestOctets('SI1RestOctets', bl={'V':8}, IE=si1_rest_octets)
)
@ -1103,7 +1103,7 @@ class RRSystemInfo1(Layer3):
class RRSystemInfo2(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':22}),
RRHeader(val={'Type':26}),
Type3V('BCCHFreqList', val={'V':16*b'\0'}, bl={'V':128}, IE=NeighbourCellChan()),
Type3V('NCCPermitted', val={'V':b'\0'}, bl={'V':8}, IE=NCCPermitted()),
@ -1118,11 +1118,11 @@ class RRSystemInfo2(Layer3):
class RRSystemInfo2bis(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':21}),
RRHeader(val={'Type':2}),
Type3V('ExtBCCHFreqList', val={'V':16*b'\0'}, bl={'V':128}, IE=NeighbourCellChan()),
Type3V('RACHCtrl', val={'V':b'\0\0\0'}, bl={'V':24}, IE=RACHCtrl()),
RestOctets('SI2bisRestOctets', IE=si2bis_rest_octets)
RestOctets('SI2bisRestOctets', bl={'V':8}, IE=si2bis_rest_octets)
)
@ -1133,10 +1133,10 @@ class RRSystemInfo2bis(Layer3):
class RRSystemInfo2ter(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':18}),
RRHeader(val={'Type':3}),
Type3V('ExtBCCHFreqList', val={'V':16*b'\0'}, bl={'V':128}, IE=NeighbourCellChan2()),
RestOctets('SI2terRestOctets', IE=si2ter_rest_octets)
RestOctets('SI2terRestOctets', bl={'V':32}, IE=si2ter_rest_octets)
)
@ -1147,9 +1147,9 @@ class RRSystemInfo2ter(Layer3):
class RRSystemInfo2quater(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':7}),
RestOctets('SI2quaterRestOctets', IE=si2quater_rest_octets)
RestOctets('SI2quaterRestOctets', bl={'V':160}, IE=si2quater_rest_octets)
)
@ -1160,9 +1160,9 @@ class RRSystemInfo2quater(Layer3):
class RRSystemInfo2n(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':69}),
RestOctets('SI2nRestOctets', IE=si2n_rest_octets)
RestOctets('SI2nRestOctets', bl={'V':160}, IE=si2n_rest_octets)
)
@ -1173,7 +1173,7 @@ class RRSystemInfo2n(Layer3):
class RRSystemInfo3(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':18}),
RRHeader(val={'Type':27}),
Type3V('CellId', val={'V':b'\0\0'}, bl={'V':16}, IE=CellId()),
Type3V('LAI', val={'V': b'\0\xf1\x10\0\0'}, bl={'V':40}, IE=LAI()),
@ -1181,7 +1181,7 @@ class RRSystemInfo3(Layer3):
Type3V('CellOpt', val={'V':b'\0'}, bl={'V':8}, IE=CellOpt()),
Type3V('CellSelParams', val={'V':b'\0\0'}, bl={'V':16}, IE=CellSelParams()),
Type3V('RACHCtrl', val={'V':b'\0\0\0'}, bl={'V':24}, IE=RACHCtrl()),
RestOctets('SI3RestOctets', IE=si3_rest_octet)
RestOctets('SI3RestOctets', bl={'V':32}, IE=si3_rest_octet)
)
@ -1210,7 +1210,7 @@ class RRSystemInfo4(Layer3):
class RRSystemInfo5(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':18}),
RRHeaderSACCH(val={'Type':29}),
Type3V('BCCHFreqList', val={'V':16*b'\0'}, bl={'V':128}, IE=NeighbourCellChan())
)
@ -1223,7 +1223,7 @@ class RRSystemInfo5(Layer3):
class RRSystemInfo5bis(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':18}),
RRHeaderSACCH(val={'Type':5}),
Type3V('ExtBCCHFreqList', val={'V':16*b'\0'}, bl={'V':128}, IE=NeighbourCellChan())
)
@ -1236,7 +1236,7 @@ class RRSystemInfo5bis(Layer3):
class RRSystemInfo5ter(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':18}),
RRHeaderSACCH(val={'Type':6}),
Type3V('ExtBCCHFreqList', val={'V':16*b'\0'}, bl={'V':128}, IE=NeighbourCellChan2())
)
@ -1249,13 +1249,13 @@ class RRSystemInfo5ter(Layer3):
class RRSystemInfo6(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':11}),
RRHeaderSACCH(val={'Type':30}),
Type3V('CellId', val={'V':b'\0\0'}, bl={'V':16}, IE=CellId()),
Type3V('LAI', val={'V': b'\0\xf1\x10\0\0'}, bl={'V':40}, IE=LAI()),
Type3V('CellOpt', val={'V':b'\0'}, bl={'V':8}, IE=CellOpt()),
Type3V('NCCPermitted', val={'V':b'\0'}, bl={'V':8}, IE=NCCPermitted()),
RestOctets('SI6RestOctets', IE=si6_rest_octets)
RestOctets('SI6RestOctets', bl={'V':56}, IE=si6_rest_octets)
)
@ -1266,9 +1266,9 @@ class RRSystemInfo6(Layer3):
class RRSystemInfo7(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':31}),
RestOctets('SI7RestOctets', IE=si4_rest_octets)
RestOctets('SI7RestOctets', bl={'V':160}, IE=si4_rest_octets)
)
@ -1279,9 +1279,9 @@ class RRSystemInfo7(Layer3):
class RRSystemInfo8(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':24}),
RestOctets('SI7RestOctets', IE=si4_rest_octets)
RestOctets('SI7RestOctets', bl={'V':160}, IE=si4_rest_octets)
)
@ -1292,10 +1292,10 @@ class RRSystemInfo8(Layer3):
class RRSystemInfo9(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':4}),
Type3V('RACHCtrl', val={'V':b'\0\0\0'}, bl={'V':24}, IE=RACHCtrl()),
RestOctets('SI9RestOctets', IE=si9_rest_octets)
RestOctets('SI9RestOctets', bl={'V':136}, IE=si9_rest_octets)
)
@ -1306,9 +1306,9 @@ class RRSystemInfo9(Layer3):
class RRSystemInfo13(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':0}),
RRHeader(val={'Type':0}),
RestOctets('SI13RestOctets', IE=si_13_rest_octets)
RestOctets('SI13RestOctets', bl={'V':160}, IE=si_13_rest_octets)
)
@ -1319,9 +1319,9 @@ class RRSystemInfo13(Layer3):
class RRSystemInfo16(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':61}),
RestOctets('SI16RestOctets', IE=si16_rest_octets)
RestOctets('SI16RestOctets', bl={'V':160}, IE=si16_rest_octets)
)
@ -1332,9 +1332,9 @@ class RRSystemInfo16(Layer3):
class RRSystemInfo17(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':62}),
RestOctets('SI17RestOctets', IE=si17_rest_octets)
RestOctets('SI17RestOctets', bl={'V':160}, IE=si17_rest_octets)
)
@ -1345,9 +1345,9 @@ class RRSystemInfo17(Layer3):
class RRSystemInfo19(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':65}),
RestOctets('SI19RestOctets', IE=si_19_rest_octets)
RestOctets('SI19RestOctets', bl={'V':160}, IE=si_19_rest_octets)
)
@ -1358,9 +1358,9 @@ class RRSystemInfo19(Layer3):
class RRSystemInfo18(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':64}),
RestOctets('SI18RestOctets', IE=si_18_rest_octets)
RestOctets('SI18RestOctets', bl={'V':160}, IE=si_18_rest_octets)
)
@ -1371,9 +1371,9 @@ class RRSystemInfo18(Layer3):
class RRSystemInfo20(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':66}),
RestOctets('SI20RestOctets', IE=si_18_rest_octets)
RestOctets('SI20RestOctets', bl={'V':160}, IE=si_18_rest_octets)
)
@ -1384,9 +1384,9 @@ class RRSystemInfo20(Layer3):
class RRSystemInfo14(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':18}), # this 44.018 spec does not make sens at all !!!
RRHeaderSACCH(val={'Type':1}),
RestOctets('SI14RestOctets', IE=si14_rest_octets)
RestOctets('SI14RestOctets', bl={'V':128}, IE=si14_rest_octets)
)
@ -1397,9 +1397,9 @@ class RRSystemInfo14(Layer3):
class RRSystemInfo15(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':1}),
RRHeader(val={'Type':67}),
RestOctets('SI15RestOctets', IE=si15_rest_octets)
RestOctets('SI15RestOctets', bl={'V':160}, IE=si15_rest_octets)
)
@ -1410,9 +1410,9 @@ class RRSystemInfo15(Layer3):
class RRSystemInfo13alt(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':0}),
RRHeader(val={'Type':68}),
RestOctets('SI13altRestOctets', IE=si_13alt_rest_octets)
RestOctets('SI13altRestOctets', bl={'V':160}, IE=si_13alt_rest_octets)
)
@ -1423,9 +1423,9 @@ class RRSystemInfo13alt(Layer3):
class RRSystemInfo21(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':2}),
RRHeader(val={'Type':70}),
RestOctets('SI21RestOctets', IE=si_21_rest_octets)
RestOctets('SI21RestOctets', bl={'V':160}, IE=si_21_rest_octets)
)
@ -1436,9 +1436,9 @@ class RRSystemInfo21(Layer3):
class RRSystemInfo22(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':2}),
RRHeader(val={'Type':71}),
RestOctets('SI22RestOctets', IE=si_22_rest_octets)
RestOctets('SI22RestOctets', bl={'V':160}, IE=si_22_rest_octets)
)
@ -1449,9 +1449,9 @@ class RRSystemInfo22(Layer3):
class RRSystemInfo23(Layer3):
_GEN = (
L2PseudoLength(),
L2PseudoLength(val={'Value':2}),
RRHeader(val={'Type':79}),
RestOctets('SI23RestOctets', IE=si_23_rest_octets)
RestOctets('SI23RestOctets', bl={'V':160}, IE=si_23_rest_octets)
)
@ -1736,9 +1736,14 @@ class RRECImmediateAssignment1(Layer3):
Type1V('FeatureInd', val={'V':0}, IE=FeatureInd()),
Type1V('PageMode', val={'V':0}, dic=PageMode_dict),
Type3V('RequestRef', val={'V':b'\0\0\0'}, bl={'V':24}, IE=RequestRef()),
Type3V('ECPacketChannelDesc1', val={'V':b'\0\0'}, bl={'V':16}, ),
RestOctets('ECFixedUplinkAlloc', )
)
Type3V('ECPacketChannelDesc1', val={'V':b'\0\0'}, bl={'V':16}, IE=ec_packet_channel_description_type_1),
Type3V('ECFixedUplinkAlloc', val={'V':4*b'\0'}, IE=ec_immediate_assignment_type_2_message_content),
RestOctets('rest')
)
def __init__(self, *args, **kwargs):
Layer3.__init__(self, *args, **kwargs)
self[0][0].set_valauto(lambda: (64+self[6].get_bl())>>3)
self[6][0].set_blauto(lambda: (self[0][0].get_val()<<3)-64)
#------------------------------------------------------------------------------#

View File

@ -47,6 +47,12 @@ rr_pdu_mo = tuple(map(unhexlify, (
)))
rr_pdu_mt = tuple(map(unhexlify, (
# DCCH
'063505', # ciphering mode cmd
'060d00', # channel release
)))
rr_pdu_l2_mt = tuple(map(unhexlify, (
# BCCH
'2d063f110e600c7f1d3800004bc26b0284b510f32b2b2b', # immediate assignment
'35063f0178b18207ec1704021fff2b2b2b2b2b2b2b2b2b',
@ -63,13 +69,11 @@ rr_pdu_mt = tuple(map(unhexlify, (
'49061bfae102f8100310c8021e1785407900008000029b', # SI type 3
'31061c02f810031085407900008000572b2b2b2b2b2b2b', # SI type 4
# SACCH
'061d00000000000000000000000000007eff', # SI type 5
'061e87e902f810031097ff2b2b2b2b2b2b2b', # SI type 6
# DCCH
'063505', # ciphering mode cmd
'060d00', # channel release
'49061d00000000000000000000000000007eff', # SI type 5
'2d061e87e902f810031097ff2b2b2b2b2b2b2b', # SI type 6
)))
def test_gsmrr_mo(rr_pdu=rr_pdu_mo):
for pdu in rr_pdu:
m, e = parse_NAS_MO(pdu)
@ -79,6 +83,15 @@ def test_gsmrr_mo(rr_pdu=rr_pdu_mo):
m.set_val(v)
assert( m.to_bytes() == pdu )
def test_gsmrr_l2_mt(rr_pdu=rr_pdu_l2_mt):
for pdu in rr_pdu:
m, e = parse_NAS_MT(pdu, wl2=True)
assert( e == 0 )
m.reautomate()
v = m.get_val()
m.set_val(v)
assert( m.to_bytes() == pdu )
def test_gsmrr_mt(rr_pdu=rr_pdu_mt):
for pdu in rr_pdu:
m, e = parse_NAS_MT(pdu)
@ -91,11 +104,15 @@ def test_gsmrr_mt(rr_pdu=rr_pdu_mt):
def test_perf():
print('[+] GSM RR MO decoding and re-encoding')
Ta = timeit(test_gsmrr_mo, number=10)
Ta = timeit(test_gsmrr_mo, number=20)
print('test_nas_mo: {0:.4f}'.format(Ta))
print('[+] GSM RR MT decoding and re-encoding')
Tb = timeit(test_gsmrr_mt, number=10)
Tb = timeit(test_gsmrr_mt, number=100)
print('test_nas_mt: {0:.4f}'.format(Tb))
print('[+] GSM RR L2 MT decoding and re-encoding')
Tb = timeit(test_gsmrr_l2_mt, number=5)
print('test_nas_mt: {0:.4f}'.format(Tb))
print('[+] GSM RR total time: {0:.4f}'.format(Ta+Tb))