pycrate/pycrate_mobile/TS24008_IE.py

3836 lines
116 KiB
Python
Raw Normal View History

2017-07-04 21:12:41 +00:00
# -*- coding: UTF-8 -*-
#/**
# * Software Name : pycrate
2019-02-25 10:26:10 +00:00
# * Version : 0.4
2017-07-04 21:12:41 +00:00
# *
2017-11-12 13:43:59 +00:00
# * Copyright 2017. Benoit Michau. ANSSI.
2017-07-04 21:12:41 +00:00
# *
# * This library is free software; you can redistribute it and/or
# * modify it under the terms of the GNU Lesser General Public
# * License as published by the Free Software Foundation; either
# * version 2.1 of the License, or (at your option) any later version.
# *
# * This library is distributed in the hope that it will be useful,
2017-07-04 21:12:41 +00:00
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# * Lesser General Public License for more details.
# *
# * You should have received a copy of the GNU Lesser General Public
# * License along with this library; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# * MA 02110-1301 USA
2017-07-04 21:12:41 +00:00
# *
# *--------------------------------------------------------
# * File Name : pycrate_mobile/TS24008_IE.py
# * Created : 2017-06-12
# * Authors : Benoit Michau
# *--------------------------------------------------------
#*/
#------------------------------------------------------------------------------#
# 3GPP TS 24.008: Mobile radio interface layer 3 specification
# release 13 (d90)
#------------------------------------------------------------------------------#
from binascii import hexlify, unhexlify
2019-11-08 15:11:02 +00:00
from time import struct_time
from socket import inet_ntop, inet_pton, AF_INET, AF_INET6
2017-07-04 21:12:41 +00:00
from pycrate_core.utils import *
2019-12-02 17:39:42 +00:00
from pycrate_core.elt import (
Envelope, Array, Sequence, Alt,
REPR_RAW, REPR_HEX, REPR_BIN, REPR_HD, REPR_HUM
)
2017-07-04 21:12:41 +00:00
from pycrate_core.base import *
from pycrate_core.repr import *
from pycrate_core.charpy import Charpy
from .MCC_MNC import MNC_dict
2018-01-14 10:03:52 +00:00
from .PPP import LCP, LCPDataConf, NCP, NCPDataConf, PAP, CHAP
from .TS23038 import *
from .TS24007 import ProtDisc_dict
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# TS 24.008 IE specified with CSN.1
#------------------------------------------------------------------------------#
from pycrate_csn1dir.classmark_3_value_part import classmark_3_value_part
from pycrate_csn1dir.ms_network_capability_value_part import ms_network_capability_value_part
from pycrate_csn1dir.ms_ra_capability_value_part import ms_ra_capability_value_part
from pycrate_csn1dir.receive_npdu_number_list_value import receive_npdu_number_list_value
2017-07-04 21:12:41 +00:00
2017-10-13 19:28:18 +00:00
#------------------------------------------------------------------------------#
# str shortcuts
#------------------------------------------------------------------------------#
_str_reserved = 'reserved'
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# std encoding / decoding routines
#------------------------------------------------------------------------------#
def encode_bcd(dig):
if len(dig) % 2:
dig += 'F'
dig = list(dig)
dig[1::2], dig[::2] = dig[::2], dig[1::2]
return unhexlify(''.join(dig))
def decode_bcd(buf):
if python_version < 3:
buf = [ord(c) for c in buf]
ret = []
for o in buf:
msb, lsb = o>>4, o&0xf
if lsb > 9:
break
else:
ret.append( str(lsb) )
if msb > 9:
break
else:
ret.append( str(msb) )
return ''.join(ret)
#------------------------------------------------------------------------------#
# TS 24.008 IE common objects
#------------------------------------------------------------------------------#
# BCD string is a string of digits, each digit being coded on a nibble (4 bits)
# Here, BufBCD is a subclass of pycrate_core.base.Buf
# with additionnal methods: encode(), decode()
class BufBCD(Buf):
2017-12-15 20:24:07 +00:00
"""Subclass of pycrate_core.base.Buf object
2017-07-04 21:12:41 +00:00
with additional encode() and decode() capabilities in order to handle
BCD encoding
"""
_rep = REPR_HUM
_dic = None # dict lookup not supported for repr()
2017-07-04 21:12:41 +00:00
# characters accepted in a BCD number
_chars = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '#', 'a', 'b', 'c')
# filler character for odd length number encoding
_filler = 0xF
2017-07-04 21:12:41 +00:00
def __init__(self, *args, **kw):
# element name in kw, or first args
if len(args):
self._name = str(args[0])
elif 'name' in kw:
self._name = str(kw['name'])
# if not provided, it's the class name
else:
self._name = self.__class__.__name__
# element description customization
if 'desc' in kw:
self._desc = str(kw['desc'])
# element representation customization
if 'rep' in kw and kw['rep'] in self.REPR_TYPES:
self._rep = kw['rep']
# element hierarchy
if 'hier' in kw:
self._hier = kw['hier']
# element bit length
if 'bl' in kw:
self._bl = kw['bl']
# element value
if 'val' in kw:
self.set_val(kw['val'])
2017-07-04 21:12:41 +00:00
# element transparency
if 'trans' in kw:
self._trans = kw['trans']
if self._SAFE_STAT:
self._chk_hier()
self._chk_bl()
self._chk_val()
self._chk_trans()
def set_val(self, val):
if isinstance(val, Buf.TYPES + (NoneType, )):
Buf.set_val(self, val)
elif isinstance(val, str_types):
self.encode(val)
else:
raise(PycrateErr('{0}: invalid BufBCD value'.format(self._name)))
2017-07-04 21:12:41 +00:00
def decode(self):
"""returns the encoded string of digits
"""
if python_version < 3:
num = [ord(c) for c in self.get_val()]
else:
num = self.get_val()
ret = []
for o in num:
msb, lsb = o>>4, o&0xf
if lsb == 0xF:
break
else:
ret.append( self._chars[lsb] )
if msb == 0xF:
break
else:
ret.append( self._chars[msb] )
return ''.join(ret)
def encode(self, bcd='12345678'):
"""encode the given BCD string and store the resulting buffer in
self._val
"""
# encode the chars
try:
ret = [self._chars.index(c) for c in bcd]
except Exception:
raise(PycrateErr('{0}: invalid character in BCD string to encode, {1!r}'\
2017-07-04 21:12:41 +00:00
.format(self._name, bcd)))
if len(ret) % 2:
ret.append( self._filler )
2017-07-04 21:12:41 +00:00
#
if python_version < 3:
self._val = ''.join([chr(c) for c in map(lambda x,y:x+(y<<4), ret[::2], ret[1::2])])
else:
self._val = bytes(map(lambda x,y:x+(y<<4), ret[::2], ret[1::2]))
def repr(self):
# special hexdump representation
if self._rep == REPR_HD:
return '\n'.join(self._repr_hd())
# additional description
if self._desc:
desc = ' [%s]' % self._desc
else:
desc = ''
# element transparency
if self.get_trans():
trans = ' [transparent]'
else:
trans = ''
# type of representation to be used
if self._rep == REPR_HUM:
val_repr = self.decode()
elif self._rep == REPR_RAW:
val_repr = repr(self.get_val())
elif self._rep == REPR_BIN:
val_repr = '0b' + self.bin()
elif self._rep == REPR_HEX:
val_repr = '0x' + self.hex()
if self.REPR_MAXLEN > 0 and len(val_repr) > self.REPR_MAXLEN:
val_repr = val_repr[:self.REPR_MAXLEN] + '...'
return '<%s%s%s : %s>' % (self._name, desc, trans, val_repr)
__repr__ = repr
# PLMN is a string of digits, each digit being coded on a nibble (4 bits)
# Here, PLMN is a subclass of pycrate_core.base.Buf
# with additionnal methods: encode(), decode()
class PLMN(Buf):
"""Custom `Buf' subclass supporting custom encode(), decode(), set_val() and repr() methods
specific for encoding a PLMN MCC-MNC code into 3 bytes in the std 3GPP BCD format
2017-07-04 21:12:41 +00:00
"""
_bl = 24 # 3 bytes
_rep = REPR_HUM
_dic = MNC_dict
# default to PLMN 001.01
DEFAULT_VAL = b'\x00\xf1\x10'
2017-07-04 21:12:41 +00:00
def __init__(self, *args, **kw):
# element name in kw, or first args
if len(args):
self._name = str(args[0])
elif 'name' in kw:
self._name = str(kw['name'])
# if not provided, it's the class name
else:
self._name = self.__class__.__name__
# element description customization
if 'desc' in kw:
self._desc = str(kw['desc'])
# element representation customization
if 'rep' in kw and kw['rep'] in self.REPR_TYPES:
self._rep = kw['rep']
# element hierarchy
if 'hier' in kw:
self._hier = kw['hier']
# element bit length
if 'bl' in kw:
self._bl = kw['bl']
2017-07-04 21:12:41 +00:00
# element value
if 'val' in kw:
self.set_val( kw['val'] )
2017-07-04 21:12:41 +00:00
# element transparency
if 'trans' in kw:
self._trans = kw['trans']
if self._SAFE_STAT:
self._chk_hier()
self._chk_bl()
self._chk_val()
self._chk_trans()
def set_val(self, val):
if isinstance(val, Buf.TYPES + (NoneType, )):
Buf.set_val(self, val)
elif isinstance(val, str_types):
self.encode(val)
else:
raise(PycrateErr('{0}: invalid PLMN value'.format(self._name)))
2017-07-04 21:12:41 +00:00
def decode(self):
"""returns the encoded string of digits
"""
plmn = []
2019-07-10 16:45:16 +00:00
if python_version < 3:
for c in self.get_val():
b = ord(c)
plmn.append(b>>4)
plmn.append(b&0xf)
try:
if plmn[2] == 0xf:
return u'%i%i%i%i%i' % (plmn[1], plmn[0], plmn[3], plmn[5], plmn[4])
else:
return u'%i%i%i%i%i%i' % (plmn[1], plmn[0], plmn[3], plmn[5], plmn[4], plmn[2])
except IndexError:
# an invalid value has been set, _SAFE_STAT / DYN is probably disabled
# for e.g. fuzzing purpose, but there is still need to not break here
return '0x%s' % hexlify(self.to_bytes()).decode('ascii')
2017-07-04 21:12:41 +00:00
else:
2019-07-10 16:45:16 +00:00
for b in self.get_val():
plmn.append(b>>4)
plmn.append(b&0xf)
try:
if plmn[2] == 0xf:
return '%i%i%i%i%i' % (plmn[1], plmn[0], plmn[3], plmn[5], plmn[4])
else:
return '%i%i%i%i%i%i' % (plmn[1], plmn[0], plmn[3], plmn[5], plmn[4], plmn[2])
except IndexError:
# an invalid value has been set, _SAFE_STAT / DYN is probably disabled
# for e.g. fuzzing purpose, but there is still need to not break here
return '0x%s' % hexlify(self.to_bytes()).decode('ascii')
2017-07-04 21:12:41 +00:00
2018-02-23 21:27:24 +00:00
def encode(self, plmn=u'00101'):
2017-07-04 21:12:41 +00:00
"""encode the given PLMN string and store the resulting buffer in
self._val
"""
if not plmn.isdigit():
raise(PycrateErr('{0}: invalid PLMN string to encode, {1!r}'\
.format(self._name, plmn)))
2017-07-04 21:12:41 +00:00
if len(plmn) == 5:
plmn += 'F'
elif len(plmn) != 6:
raise(PycrateErr('{0}: invalid PLMN string to encode, {1!r}'\
.format(self._name, plmn)))
2019-07-10 16:45:16 +00:00
# no need to distinguish Python version as join() and unhexlify() seems to
# do a good job here in both cases
self._val = unhexlify(''.join((plmn[1], plmn[0], plmn[5], plmn[2], plmn[4], plmn[3])))
2017-07-04 21:12:41 +00:00
def repr(self):
# special hexdump representation
if self._rep == REPR_HD:
return '\n'.join(self._repr_hd())
# additional description
if self._desc:
desc = ' [%s]' % self._desc
else:
desc = ''
# element transparency
if self.get_trans():
trans = ' [transparent]'
else:
trans = ''
# type of representation to be used
if self._rep == REPR_HUM:
val_repr = self.decode()
if self._dic and val_repr in self._dic:
mccmnc = self._dic[val_repr]
val_repr += ' (%s.%s)' % (mccmnc[2], mccmnc[3])
2017-07-04 21:12:41 +00:00
elif self._rep == REPR_RAW:
val_repr = repr(self.get_val())
elif self._rep == REPR_BIN:
val_repr = '0b' + self.bin()
elif self._rep == REPR_HEX:
val_repr = '0x' + self.hex()
if self.REPR_MAXLEN > 0 and len(val_repr) > self.REPR_MAXLEN:
val_repr = val_repr[:self.REPR_MAXLEN] + '...'
return '<%s%s%s : %s>' % (self._name, desc, trans, val_repr)
__repr__ = repr
class IPAddr(Buf):
"""Custom `Buf' subclass supporting custom encode(), decode(), set_val() and repr() methods
specific for an IP address:
- IPv4 when length is 4 bytes
- IPv6 when length is 16 bytes
"""
_rep = REPR_HUM
def encode(self, val):
try:
if val.count('.') == 3:
Buf.set_val(self, inet_pton(AF_INET, val))
elif val.count(':'):
Buf.set_val(self, inet_pton(AF_INET6, val))
else:
Buf.set_val(val)
except Exception as err:
raise(PycrateErr('invalid IP address string'))
def decode(self):
val = self.get_val()
if len(val) == 4:
return inet_ntop(AF_INET, val)
elif len(val) == 16:
return inet_ntop(AF_INET6, val)
else:
return val
def set_val(self, val):
if python_version == 2 and isinstance(val, unicode) \
or python_version > 2 and isinstance(val, str_types):
self.encode(val)
else:
Buf.set_val(self, val)
def repr(self):
vallen = self.get_len()
if self._rep == REPR_HUM and vallen in {4, 16}:
# element transparency
if self.get_trans():
trans = ' [transparent]'
else:
trans = ''
val_repr = self.decode()
return '<%s%s : %s>' % (self._name, trans, val_repr)
else:
return Buf.repr(self)
__repr__ = repr
#------------------------------------------------------------------------------#
# Cell Identity
# TS 24.008, 10.5.1.1
#------------------------------------------------------------------------------#
class CellId(Uint16):
_rep = REPR_HEX
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# CKSN
# TS 24.008, 10.5.1.2
#------------------------------------------------------------------------------#
CKSN_dict = {
7:'No key is available (from MS) / reserved (from network)'
}
#------------------------------------------------------------------------------#
# Local Area Identifier
# TS 24.008, 10.5.1.3
#------------------------------------------------------------------------------#
class LAI(Envelope):
_GEN = (
PLMN(),
Uint16('LAC', rep=REPR_HEX)
2017-07-04 21:12:41 +00:00
)
encode = Envelope.set_val
2017-07-04 21:12:41 +00:00
def decode(self):
2017-09-16 16:11:34 +00:00
return (self[0].decode(), self[1].get_val())
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Mobile Identity
# TS 24.008, 10.5.1.4
#------------------------------------------------------------------------------#
IDType_dict = {
0 : 'No Identity',
1 : 'IMSI',
2 : 'IMEI',
3 : 'IMEISV',
4 : 'TMSI',
5 : 'TMGI',
6 : 'ffu'
}
IDTYPE_NONE = 0
IDTYPE_IMSI = 1
IDTYPE_IMEI = 2
IDTYPE_IMEISV = 3
IDTYPE_TMSI = 4
IDTYPE_TMGI = 5
class IDNone(Envelope):
_GEN = (
Uint('spare', bl=5, rep=REPR_HEX),
Uint('Type', bl=3, dic=IDType_dict)
2017-07-04 21:12:41 +00:00
)
class IDTemp(Envelope):
_GEN = (
Uint('Digit1', val=0xF, bl=4, rep=REPR_HEX),
Uint('Odd', bl=1),
2017-07-04 21:12:41 +00:00
Uint('Type', val=IDTYPE_TMSI, bl=3, dic=IDType_dict),
Uint32('TMSI', rep=REPR_HEX)
2017-07-04 21:12:41 +00:00
)
class IDDigit(Envelope):
_GEN = (
Uint('Digit1', val=0xF, bl=4, rep=REPR_HEX),
Uint('Odd', bl=1),
2017-07-04 21:12:41 +00:00
Uint('Type', val=IDTYPE_IMSI, bl=3, dic=IDType_dict),
Buf('Digits', val=b'', rep=REPR_HEX)
)
class IDGroup(Envelope):
ENV_SEL_TRANS = False
2017-07-04 21:12:41 +00:00
_GEN = (
Uint('spare', bl=2),
Uint('MBMSSessInd', bl=1),
Uint('MCCMNCInd', bl=1),
Uint('Odd', bl=1),
2017-07-04 21:12:41 +00:00
Uint('Type', val=IDTYPE_TMGI, dic=IDType_dict),
Uint24('MBMSServID', rep=REPR_HEX),
2017-07-04 21:12:41 +00:00
PLMN(),
Uint8('MBMSSessID')
2017-07-04 21:12:41 +00:00
)
def __init__(self, *args, **kw):
Envelope.__init__(self, *args, **kw)
self[6].set_transauto(lambda: False if self[2].get_val() else True)
self[7].set_transauto(lambda: False if self[1].get_val() else True)
class ID(Envelope):
# during encode() / _from_char() processing
# specific attributes are created:
# self._IDNone = IDNone()
# self._IDTemp = IDTemp()
# self._IDDigit = IDDigit()
# self._IDGroup = IDGroup()
2017-07-04 21:12:41 +00:00
def set_val(self, vals):
if isinstance(vals, dict) and 'type' in vals and 'ident' in vals:
self.encode(vals['type'], vals['ident'])
2017-07-04 21:12:41 +00:00
else:
Envelope.set_val(self, vals)
2017-07-04 21:12:41 +00:00
def decode(self):
"""returns the mobile identity type and value
"""
type = self['Type'].get_val()
if type == IDTYPE_NONE:
return (type, None)
#
elif type == IDTYPE_TMSI:
return (type, self[3].get_val())
#
elif type in (IDTYPE_IMSI, IDTYPE_IMEI, IDTYPE_IMEISV):
return (type, str(self[0].get_val()) + decode_bcd(self[3].get_val()))
#
elif type == IDTYPE_TMGI:
if self[1].get_val():
# MBMSSessID
mid = self[7].get_val()
else:
mid = None
if self[2].get_val():
# MCCMNC
plmn = self[6].decode()
else:
plmn = None
return (type, (self[5].get_val(), plmn, mid))
def encode(self, type=IDTYPE_NONE, ident=None):
"""sets the mobile identity with given type
if type is IDTYPE_TMSI: ident must be an uint32
if type is IDTYPE_IMSI, IDTYPE_IMEI or IDTYPE_IMEISV: ident must be a
string of digits
if type is IDTYPE_TMGI: ident must be a 3-tuple (MBMSServID -uint24-,
PLMN -string of digits- or None, MBMSSessID -uint8- or None)
"""
if type == IDTYPE_NONE:
if not hasattr(self, '_IDNone'):
self._IDNone = IDNone()
2017-07-04 21:12:41 +00:00
self._content = self._IDNone._content
self._by_id = self._IDNone._by_id
self._by_name = self._IDNone._by_name
#
elif type == IDTYPE_TMSI:
if not hasattr(self, '_IDTemp'):
self._IDTemp = IDTemp()
2017-07-04 21:12:41 +00:00
self._content = self._IDTemp._content
self._by_id = self._IDTemp._by_id
self._by_name = self._IDTemp._by_name
self[3].set_val(ident)
#
elif type in (IDTYPE_IMSI, IDTYPE_IMEI, IDTYPE_IMEISV):
if not ident.isdigit():
raise(PycrateErr('{0}: invalid identity to encode, {1!r}'\
.format(self._name, ident)))
if not hasattr(self, '_IDDigit'):
self._IDDigit = IDDigit()
2017-07-04 21:12:41 +00:00
self._content = self._IDDigit._content
self._by_id = self._IDDigit._by_id
self._by_name = self._IDDigit._by_name
self[2]._val = type
if len(ident) % 2:
self[1]._val = 1
# encode digits the BCD way
self[0]._val = int(ident[0])
self[3]._val = encode_bcd(ident[1:])
#
elif type == IDTYPE_TMGI:
if not isinstance(ident, (tuple, list)) or len(ident) != 3:
raise(PycrateErr('{0}: invalid identity to encode, {1!r}'\
.format(self._name, ident)))
if not hasattr(self, '_IDGroup'):
self._IDGroup = IDGroup()
2017-07-04 21:12:41 +00:00
self._content = self._IDGroup._content
self._by_id = self._IDGroup._by_id
self._by_name = self._IDGroup._by_name
self[5].set_val( ident[0] )
if ident[1] is not None:
# MCCMNC
self[2]._val = 1
self[6].encode( ident[1] )
if ident[2] is not None:
# MBMSSessID
self[1]._val = 1
self[7].set_val( ident[2] )
def _from_char(self, char):
if not self.get_trans():
try:
spare = char.get_uint(5)
type = char.get_uint(3)
except CharpyErr as err:
raise(CharpyErr('{0} [_from_char]: {1}'.format(self._name, err)))
except Exception as err:
raise(EltErr('{0} [_from_char]: {1}'.format(self._name, err)))
#
if type == IDTYPE_TMSI:
if not hasattr(self, '_IDTemp'):
self._IDTemp = IDTemp()
2017-07-04 21:12:41 +00:00
self._content = self._IDTemp._content
self._by_id = self._IDTemp._by_id
self._by_name = self._IDTemp._by_name
self[0]._val = spare >> 1
self[1]._val = spare & 1
self[3]._from_char(char)
#
elif type in (IDTYPE_IMSI, IDTYPE_IMEI, IDTYPE_IMEISV):
if not hasattr(self, '_IDDigit'):
self._IDDigit = IDDigit()
2017-07-04 21:12:41 +00:00
self._content = self._IDDigit._content
self._by_id = self._IDDigit._by_id
self._by_name = self._IDDigit._by_name
self[0]._val = spare >> 1
self[1]._val = spare & 1
self[2]._val = type
self[3]._from_char(char)
#
elif type == IDTYPE_TMGI:
if not hasattr(self, '_IDGroup'):
self._IDGroup = IDGroup()
2017-07-04 21:12:41 +00:00
self._content = self._IDGroup._content
self._by_id = self._IDGroup._by_id
self._by_name = self._IDGroup._by_name
self[0]._val = spare >> 3
self[1]._val = (spare >> 2) & 1
self[2]._val = (spare >> 1) & 1
self[3]._val = spare & 1
self[5]._from_char(char)
if self[2]._val:
self[6]._from_char(char)
if self[1]._val:
self[7]._from_char(char)
#
elif type == IDTYPE_NONE:
if not hasattr(self, '_IDNone'):
self._IDNone = IDNone()
self._content = self._IDNone._content
self._by_id = self._IDNone._by_id
self._by_name = self._IDNone._by_name
#
2017-07-04 21:12:41 +00:00
else:
if not hasattr(self, '_IDNone'):
self._IDNone = IDNone()
log('WNG: ID, type unhandled, %i' % type)
2017-07-04 21:12:41 +00:00
self._content = self._IDNone._content
self._by_id = self._IDNone._by_id
self._by_name = self._IDNone._by_name
def repr(self):
if not self._content:
return Envelope.repr(self)
# additional description
if self._desc:
desc = ' [%s]' % self._desc
else:
desc = ''
# element transparency
if self.get_trans():
trans = ' [transparent]'
else:
trans = ''
#
type = self['Type'].get_val()
#
if type == IDTYPE_TMSI:
if self[3]._rep in (REPR_RAW, REPR_HUM):
t_repr = repr(self[3].get_val())
elif self[3]._rep == REPR_HEX:
t_repr = '0x' + self[3].hex()
elif self[3].rep == REPR_BIN:
t_repr = '0b' + self[3].bin()
else:
t_repr = ''
return '<%s%s%s [TMSI] : %s>' % (self._name, desc, trans, t_repr)
elif type in (IDTYPE_IMSI, IDTYPE_IMEI, IDTYPE_IMEISV):
return '<%s%s%s [%s] : %s>' % (self._name, desc, trans, IDType_dict[type],
str(self[0].get_val()) + decode_bcd(self[3].get_val()))
else:
return Envelope.repr(self)
__repr__ = repr
#------------------------------------------------------------------------------#
# Mobile Station Classmark 1
# TS 24.008, 10.5.1.5
#------------------------------------------------------------------------------#
_RevLevel_dict = {
0:'Reserved for GSM phase 1',
1:'GSM phase 2 MS',
2:'MS supporting R99 or later',
3:'FFU'
}
_RFClass_dict = {
0:'class 1',
1:'class 2',
2:'class 3',
3:'class 4',
4:'class 5'
}
class MSCm1(Envelope):
_GEN = (
Uint('spare', bl=1),
2017-07-04 21:12:41 +00:00
Uint('RevLevel', val=2, bl=2, dic=_RevLevel_dict),
Uint('EarlyCmCap', bl=1),
Uint('NoA51', bl=1),
Uint('RFClass', bl=3, dic=_RFClass_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Mobile Station Classmark 2
# TS 24.008, 10.5.1.6
#------------------------------------------------------------------------------#
# SS screening indicator (TS 24.080, section 3.7.1)
_SSScreen_dict = {
0:'default value of phase 1',
1:'capability of handling of ellipsis notation and phase 2 error handling',
2:'ffu',
3:'ffu'
}
class MSCm2(Envelope):
_GEN = (
Uint('spare', bl=1),
2017-07-04 21:12:41 +00:00
Uint('RevLevel', val=2, bl=2, dic=_RevLevel_dict),
Uint('EarlyCmCap', bl=1),
Uint('NoA51', bl=1),
Uint('RFClass', bl=3, dic=_RFClass_dict),
Uint('spare', bl=1),
Uint('PSCap', bl=1),
Uint('SSScreeningCap', bl=2, dic=_SSScreen_dict),
Uint('MTSMSCap', bl=1),
Uint('VBSNotifCap', bl=1),
Uint('VGCSNotifCap', bl=1),
Uint('FCFreqCap', bl=1),
Uint('MSCm3Cap', bl=1),
Uint('spare', bl=1),
Uint('LCSVACap', bl=1),
Uint('UCS2', bl=1),
Uint('SoLSACap', bl=1),
Uint('CMServPrompt', bl=1),
Uint('A53', bl=1),
Uint('A52', bl=1)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Descriptive group or broadcast call reference
# TS 24.008, 10.5.1.9
#------------------------------------------------------------------------------#
PriorityLevel_dict = {
0 : 'no priority applied',
1 : 'call priority level 4',
2 : 'call priority level 3',
3 : 'call priority level 2',
4 : 'call priority level 1',
5 : 'call priority level 0',
6 : 'call priority level B',
7 : 'call priority level A'
}
class BroadcastCallRef(Envelope):
_GEN = (
Uint('Value', bl=27, rep=REPR_HEX),
Uint('SF', bl=1, dic={0:'VBS, broadcast call', 1:'VGCS, group call'}),
Uint('AF', bl=1, dic={0:'ACK not required', 1:'ACK required'}),
Uint('CallPriority', bl=3, dic=PriorityLevel_dict),
Uint('CipheringInfo', bl=4, dic={0:'no ciphering'}),
Uint('spare', bl=4)
)
#------------------------------------------------------------------------------#
# PD and SAPI $(CCBS)$
# TS 24.008, 10.5.1.10a
#------------------------------------------------------------------------------#
class PD_SAPI(Envelope):
_GEN = (
Uint('spare', bl=2),
Uint('SAPI', bl=2),
Uint('PD', val=5, bl=4, dic=ProtDisc_dict)
)
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Priority Level
# TS 24.008, 10.5.1.11
#------------------------------------------------------------------------------#
class PriorityLevel(Envelope):
_GEN = (
Uint('spare', bl=1),
Uint('CallPriority', bl=3, dic=PriorityLevel_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# PLMN list
# TS 24.008, 10.5.1.13
#------------------------------------------------------------------------------#
class PLMNList(Array):
_GEN = PLMN()
#------------------------------------------------------------------------------#
# MS network feature support
# TS 24.008, 10.5.1.15
#------------------------------------------------------------------------------#
class MSNetFeatSupp(Envelope):
_GEN = (
Uint('spare', bl=3),
Uint('ExtPeriodTimers', bl=1)
)
#------------------------------------------------------------------------------#
# Authentication Parameter AUTN (UMTS and EPS authentication challenge)
# TS 24.008, 10.5.3.1.1
#------------------------------------------------------------------------------#
class AUTN(Envelope):
_GEN = (
Buf('SQNxAK', bl=48, rep=REPR_HEX),
Buf('AMF', bl=16, rep=REPR_HEX),
Buf('MAC', bl=64, rep=REPR_HEX)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# CM Service type
# TS 24.008, 10.5.3.3
#------------------------------------------------------------------------------#
CMService_dict = {
1:'Mobile originating call / packet mode connection',
2:'Emergency call',
4:'SMS',
8:'Supplementary service',
9:'Voice group call',
10:'Voice broadcast call',
11:'Location service'
}
#------------------------------------------------------------------------------#
# Location Updating type
# TS 24.008, 10.5.3.5
#------------------------------------------------------------------------------#
_LocUpdType_dict = {
0 : 'Normal location updating',
1 : 'Periodic updating',
2 : 'IMSI attach',
2017-10-13 19:28:18 +00:00
3 : _str_reserved
2017-07-04 21:12:41 +00:00
}
class LocUpdateType(Envelope):
_GEN = (
Uint('FollowOnReq', bl=1),
Uint('spare', bl=1),
Uint('Type', bl=2, dic=_LocUpdType_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Network Name
# section 10.5.3.5a
#------------------------------------------------------------------------------#
_CodingScheme_dict = {
0 : 'GSM 7 bit default alphabet',
1 : 'UCS2 (16 bit)'
}
CODTYPE_7B = 0
CODTYPE_UCS2 = 1
class NetworkName(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Coding', val=CODTYPE_7B, bl=3, dic=_CodingScheme_dict),
Uint('AddCountryInitials', bl=1),
Uint('SpareBits', bl=3),
2017-07-04 21:12:41 +00:00
Buf('Name', val=b'', rep=REPR_HEX)
)
def set_val(self, vals):
Name = None
if isinstance(vals, dict) and 'Name' in vals:
vals = dict(vals)
Name = vals['Name']
del vals['Name']
elif isinstance(vals, (tuple, list)) and len(vals) == 5:
vals = list(vals)
Name = vals[-1]
vals[-1] = None
Envelope.set_val(self, vals)
if Name is not None:
if isinstance(Name, Buf.TYPES + (NoneType,)):
self[4].set_val(Name)
elif isinstance(Name, str_types):
# encode it according to the Coding value
Coding = self[1]()
if Coding == CODTYPE_7B:
enc, c = encode_7b(Name)
self[4]._val = enc
self[3]._val = (8-((7*c)%8))%8
elif Coding == CODTYPE_UCS2:
self[4]._val = Name.encode('utf-16-be')
self[3]._val = 0
encode = set_val
2017-07-04 21:12:41 +00:00
def decode(self):
"""returns the textual network name
"""
Coding = self[1].get_val()
if Coding == CODTYPE_7B:
dec = decode_7b(self[4].get_val())
if self[3]() == 7:
dec = dec[:-1]
return dec
elif Coding == CODTYPE_UCS2:
2017-07-04 21:12:41 +00:00
# WNG: this will certainly fail in Python2
return self[4].get_val().decode('utf-16-be')
2017-07-04 21:12:41 +00:00
else:
return None
#------------------------------------------------------------------------------#
# Reject Cause
# TS 24.008, section 10.5.3.6
#------------------------------------------------------------------------------#
_RejectCause_dict = {
2017-07-04 21:12:41 +00:00
2:'IMSI unknown in HLR',
3:'Illegal MS',
4:'IMSI unknown in VLR',
5:'IMEI not accepted',
6:'Illegal ME',
11:'PLMN not allowed',
12:'Location Area not allowed',
13:'Roaming not allowed in this location area',
15:'No Suitable Cells In Location Area',
17:'Network failure',
20:'MAC failure',
21:'Synch failure',
22:'Congestion',
23:'GSM authentication unacceptable',
25:'Not authorized for this CSG',
32:'Service option not supported',
33:'Requested service option not subscribed',
34:'Service option temporarily out of order',
38:'Call cannot be identified',
48:'retry upon entry into a new cell',
95:'Semantically incorrect message',
96:'Invalid mandatory information',
97:'Message type non-existent or not implemented',
98:'Message type not compatible with the protocol state',
99:'Information element non-existent or not implemented',
100:'Conditional IE error',
101:'Message not compatible with the protocol state',
111:'Protocol error, unspecified'
}
class RejectCause(Uint8):
_dic = _RejectCause_dict
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Time Zone
# TS 24.008, section 10.5.3.8
#------------------------------------------------------------------------------#
# identical to TS23040_SMS._TP_SCTS_TZ structure
class TimeZone(Envelope):
_Sign_dict = {0: '+', 1: '-'}
_GEN = (
Uint('TZ1', bl=4),
Uint('TZS', bl=1, dic=_Sign_dict),
Uint('TZ0', bl=3)
)
def set_val(self, vals):
if isinstance(vals, float):
self.encode(vals)
else:
Envelope.set_val(self, vals)
def encode(self, val):
if val < 0:
self[1].set_val(1)
val = -val
else:
self[1].set_val(0)
if val != 0:
quart = ceil((val*4) % 128)
self[0].set_val( quart%16 )
self[2].set_val( quart>>4 )
else:
self[0].set_val(0)
self[2].set_val(0)
def decode(self):
if self[1]() == 1:
return -0.25 * ((self[2]()<<4) + self[0]())
else:
return 0.25 * ((self[2]()<<4) + self[0]())
def repr(self):
return '<TimeZone: %s%.2f>' % (self._Sign_dict[self[1]()], 0.25 * (self[0]() + (self[2]()<<4)))
__repr__ = repr
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Time Zone and Time
# TS 24.008, section 10.5.3.9
#------------------------------------------------------------------------------#
# identical to TS23040_SMS.TP_SCTS structure
class _TP_SCTS_Comp(Envelope):
def set_val(self, vals):
if isinstance(vals, integer_types):
self.encode(vals)
else:
Envelope.set_val(self, vals)
def encode(self, val):
self.set_val( (val%10, val//10) )
def decode(self):
return self[0]()+10*self[1]()
def repr(self):
return '<%s : %i%i>' % (self._name, self[1](), self[0]())
__repr__ = repr
2017-07-04 21:12:41 +00:00
class TimeZoneTime(Envelope):
2019-11-08 15:11:02 +00:00
YEAR_BASE = 2000
2019-11-08 15:11:02 +00:00
_GEN = (
_TP_SCTS_Comp('Year', GEN=(Uint('Y1', bl=4), Uint('Y0', bl=4))),
_TP_SCTS_Comp('Mon', GEN=(Uint('M1', bl=4), Uint('M0', bl=4))),
_TP_SCTS_Comp('Day', GEN=(Uint('D1', bl=4), Uint('D0', bl=4))),
_TP_SCTS_Comp('Hour', GEN=(Uint('H1', bl=4), Uint('H0', bl=4))),
_TP_SCTS_Comp('Min', GEN=(Uint('M1', bl=4), Uint('M0', bl=4))),
_TP_SCTS_Comp('Sec', GEN=(Uint('S1', bl=4), Uint('S0', bl=4))),
TimeZone('TimeZone')
)
def set_val(self, val):
if isinstance(val, (tuple, list)) and len(val) == 2:
self.encode(*val)
else:
Envelope.set_val(self, val)
def encode(self, ts, tz=0.0):
"""encode a Python struct_time and potential timezone shift as the value of
the TimeZoneTime
"""
self['Year'].encode( ts.tm_year-self.YEAR_BASE )
self['Mon'].encode( ts.tm_mon )
self['Day'].encode( ts.tm_mday )
self['Hour'].encode( ts.tm_hour )
self['Min'].encode( ts.tm_min )
self['Sec'].encode( ts.tm_sec )
self['TZ'].encode( tz )
def decode(self):
"""decode the value of the TimeZoneTime into a Python struct_time and
timezone shift
"""
2019-11-08 15:11:02 +00:00
ts = struct_time((
self.YEAR_BASE+self['Year'].decode(),
self['Mon'].decode(),
self['Day'].decode(),
self['Hour'].decode(),
self['Min'].decode(),
self['Sec'].decode(),
0, 0, 0))
return ts, self['TimeZone'].decode()
#------------------------------------------------------------------------------#
# Daylight Saving Time
# TS 24.008, section 10.5.3.12
#------------------------------------------------------------------------------#
_DLSavingTime = {
0 : 'No adjustment for Daylight Saving Time',
1 : '+1 hour adjustment for Daylight Saving Time',
2 : '+2 hours adjustment for Daylight Saving Time',
3 : _str_reserved
}
class DLSavingTime(Envelope):
2017-07-04 21:12:41 +00:00
_GEN = (
Uint('spare', bl=6),
Uint('Value', bl=2, dic=_DLSavingTime)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Supported codec list
# TS 24.008, section 10.5.4.32
#------------------------------------------------------------------------------#
class SuppCodec(Envelope):
_GEN = (
Uint8('SysID'),
2017-07-04 21:12:41 +00:00
Uint8('BMLen'),
Buf('CodecBM', val=b'\0', rep=REPR_BIN),
)
def __init__(self, *args, **kw):
Envelope.__init__(self, *args, **kw)
self[1].set_valauto( self[2].get_len )
self[2].set_blauto( lambda: 8*self[1]() )
class SuppCodecList(Array):
_GEN = SuppCodec()
#------------------------------------------------------------------------------#
# Emergency Service Category
# TS 24.008, section 10.5.4.33
#------------------------------------------------------------------------------#
class EmergServiceCat(Envelope):
_GEN = (
Uint('Police', bl=1),
Uint('Ambulance', bl=1),
Uint('Fire', bl=1),
Uint('Marine', bl=1),
Uint('Mountain', bl=1),
Uint('manual eCall', bl=1),
Uint('auto eCall', bl=1),
Uint('spare', bl=1)
2017-07-04 21:12:41 +00:00
)
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Emergency Number List
# TS 24.008, section 10.5.3.13
#------------------------------------------------------------------------------#
class EmergNum(Envelope):
_GEN = (
Uint8('Len'),
Uint('spare', bl=3),
EmergServiceCat('ServiceCat')[:5],
BufBCD('Num')
2017-07-04 21:12:41 +00:00
)
def __init__(self, *args, **kw):
Envelope.__init__(self, *args, **kw)
2017-09-16 16:11:34 +00:00
self[2]._name = 'ServiceCat' # otherwise, it says 'slice'
self._by_name[2] = 'ServiceCat' # otherwise, it says 'slice'
2017-07-04 21:12:41 +00:00
self[0].set_valauto( lambda: 1 + self[3].get_len() )
self[3].set_blauto( lambda: 8*(self[0]()-1) )
class EmergNumList(Array):
_GEN = EmergNum()
#------------------------------------------------------------------------------#
# Additional Update Parameters
# TS 24.008, 10.5.3.14
#------------------------------------------------------------------------------#
_CSMO_dict = {
1 : 'CS fallback MO call'
}
_CSMT_dict = {
1 : 'CS fallback MT call'
}
class AddUpdateParams(Envelope):
_GEN = (
Uint('spare', bl=2),
Uint('CSMO', bl=1, dic=_CSMO_dict),
Uint('CSMT', bl=1, dic=_CSMT_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# MM Timer
# TS 24.008, 10.5.3.16
#------------------------------------------------------------------------------#
_MMTimerUnit_dict = {
0 : '2 sec',
1 : '1 min',
2 : '6 min',
7 : 'timer deactivated'
}
class MMTimer(Envelope):
_GEN = (
Uint('Unit', bl=3, dic=_MMTimerUnit_dict),
Uint('Value', bl=5)
2017-07-04 21:12:41 +00:00
)
def get_time(self):
"""returns the timer set in seconds
"""
unit, val = self.get_val()
if unit == 0:
return val*2
elif unit == 1:
return val*60
elif unit == 2:
return val*360
else:
return 0
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Auxiliary states
# TS 24.008, 10.5.4.4
#------------------------------------------------------------------------------#
_AuxHold_dict = {
0 : 'idle',
1 : 'hold request',
2 : 'call held',
3 : 'retrieve request'
}
_AuxMPTY_dict = {
0 : 'idle',
1 : 'MPTY request',
2 : 'call in MPTY',
3 : 'split request'
}
class AuxiliaryStates(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('spare', bl=3),
Uint('Hold', bl=2, dic=_AuxHold_dict),
Uint('MPTY', bl=2, dic=_AuxMPTY_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Backup bearer capability
# TS 24.008, 10.5.4.4a
#------------------------------------------------------------------------------#
_RadioChanReq_dict = {
0:'reserved',
1:'full rate support only MS',
2:'dual rate support MS/half rate preferred',
3:'dual rate support MS/full rate preferred'
}
_BCapCodingStd_dict = {
0 : 'GSM standardized coding',
1 : _str_reserved
}
_TransferMode_dict = {
0:'circuit',
1:'packet'
}
_TransferCap_dict = {
0:'speech',
1:'unrestricted digital information',
2:'3.1 kHz audio, ex PLMN',
3:'facsimile group 3',
5:'Other ITC (See Octet 5a)',
7:'reserved, to be used in the network'
}
class _BearerCapOct4(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Compress', bl=1),
Uint('Structure', bl=2),
Uint('DuplexMode', bl=1),
Uint('Config', bl=1),
Uint('NIRR', bl=1),
Uint('Estab', bl=1)
)
class _BearerCapExt5a(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('OtherITC', bl=2),
Uint('OtherRateAdapt', bl=2),
Uint('spare', bl=3)
)
class _BUBearerCapOct5(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('AccessId', bl=2),
Uint('RateAdapt', bl=2),
Uint('SignalAccessProt', bl=3),
_BearerCapExt5a('Ext5a')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[4].set_transauto(lambda: self[0]() == 1)
class _BearerCapExt6a(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('NumStopBits', bl=1),
Uint('Negotation', bl=1),
Uint('NumDataBits', bl=1),
Uint('UserRate', bl=4)
)
class _BearerCapExt6b(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('IntermedRate', bl=2),
Uint('NICOnTx', bl=1),
Uint('NICOnRx', bl=1),
Uint('Parity', bl=3)
)
class _BearerCapExt6c(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('ConnectElt', bl=2),
Uint('ModemType', bl=5)
)
class _BearerCapExt6d(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('OtherModemType', bl=2),
Uint('FixedNetUserRate', bl=5)
)
class _BearerCapExt6e(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('AcceptableChanCodings', bl=4),
Uint('MaxNumOfTrafficChan', bl=3)
)
class _BearerCapExt6f(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('UIMI', bl=3),
Uint('WantedAirIFUserRate', bl=4)
)
class _BearerCapExt6g(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('AcceptableChanCodingsExt', bl=3),
Uint('AssymetryInd', bl=2),
Uint('spare', bl=2)
)
class _BearerCapOct6(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Layer1Id', val=1, bl=2),
Uint('UserInfoLayer1Prot', bl=4),
Uint('Sync', bl=1),
_BearerCapExt6a('Ext6a'),
_BearerCapExt6b('Ext6b'),
_BearerCapExt6c('Ext6c'),
_BearerCapExt6d('Ext6d'),
_BearerCapExt6e('Ext6e'),
_BearerCapExt6f('Ext6f'),
_BearerCapExt6g('Ext6g')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[4].set_transauto(lambda: self[0]() == 1)
self[5].set_transauto(lambda: self[4].get_trans() or self[4][0]() == 1)
self[6].set_transauto(lambda: self[5].get_trans() or self[5][0]() == 1)
self[7].set_transauto(lambda: self[6].get_trans() or self[6][0]() == 1)
self[8].set_transauto(lambda: self[7].get_trans() or self[7][0]() == 1)
self[9].set_transauto(lambda: self[8].get_trans() or self[8][0]() == 1)
self[10].set_transauto(lambda: self[9].get_trans() or self[9][0]() == 1)
class _BearerCapOct7(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Layer2Id', val=2, bl=2),
Uint('UserInfoLayer2Prot', bl=5)
)
class BackupBearerCap(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('RadioChanReq', val=1, bl=2, dic=_RadioChanReq_dict),
Uint('CodingStd', bl=1, dic=_BCapCodingStd_dict),
Uint('TransferMode', bl=1, dic=_TransferMode_dict),
Uint('InfoTransferCap', bl=3, dic=_TransferCap_dict),
_BearerCapOct4('Oct4', trans=True),
_BUBearerCapOct5('Oct5', trans=True),
_BearerCapOct6('Oct6', trans=True),
_BearerCapOct7('Oct7', trans=True)
)
def _from_char(self, char):
Envelope._from_char(self, char)
if char.len_byte():
# Oct4
self[5].set_trans(False)
self[5]._from_char(char)
if char.len_byte():
# Oct5
self[6].set_trans(False)
self[6]._from_char(char)
if char.len_byte():
# Oct6
self[7].set_trans(False)
self[7]._from_char(char)
if char.len_byte():
# Oct7
self[8].set_trans(False)
self[8]._from_char(char)
#------------------------------------------------------------------------------#
# Bearer capability
# TS 24.008, 10.5.4.5
#------------------------------------------------------------------------------#
_CTMSupp_dict = {
0 : 'CTM text telephony is not supported',
1 : 'CTM text telephony is supported'
}
_CodingExt3_dict = {
0 : 'octet used for extension of information transfer capability',
1 : 'octet used for other extension of octet 3'
}
_SpeechVersInd_dict = {
0 : 'GSM FR v1 (GSM FR)',
2 : 'GSM FR v2 (GSM EFR)',
4 : 'GSM FR v3 (FR AMR)',
6 : 'GSM FR v4',
8 : 'GSM FR v5',
1 : 'GSM HR v1 (GSM HR)',
5 : 'GSM HR v3 (HR AMR)',
7 : 'GSM HR v4',
11 : 'GSM HR v6',
15 : 'no speech version supported for GERAN'
}
class _BearerCapExt3a(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Coding', bl=1, dic=_CodingExt3_dict),
Uint('CTM', bl=1, dic=_CTMSupp_dict),
Uint('spare', bl=1),
Uint('SpeechVersionInd', bl=4, dic=_SpeechVersInd_dict)
)
class _BearerCapExt3bRec(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Coding', bl=1, dic=_CodingExt3_dict),
Uint('spare', bl=2),
Uint('SpeechVersionInd', bl=4, dic=_SpeechVersInd_dict)
)
class _BearerCapExt3b(Sequence):
_GEN = _BearerCapExt3bRec()
def _from_char(self, char):
# while Ext bit is 1, stack another sequenced element
if self.get_trans():
return
# 1) determine the number of iteration of the template within the sequence
num = None
# 2) init content
self._content = []
# 3) consume char and fill in self._content
# there is no predefined limit in the number of repeated content
# consume the charpy instance until Ext == 1
while True:
# remember charpy cursor position, to restore it when it raises
cur = char._cur
clone = self._tmpl.clone()
try:
clone._from_char(char)
except CharpyErr as err:
char._cur = cur
break
else:
self._content.append(clone)
clone._env = self
if clone[0]() == 1:
break
class _BearerCapExt5b(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Hdr', bl=1),
Uint('Multiframe', bl=1),
Uint('Mode', bl=1),
Uint('LLI', bl=1),
Uint('Assignor', bl=1),
Uint('InbNeg', bl=1),
Uint('spare', bl=1)
)
class _BearerCapOct5(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('AccessId', bl=2),
Uint('RateAdapt', bl=2),
Uint('SignalAccessProt', bl=3),
_BearerCapExt5a('Ext5a'),
_BearerCapExt5b('Ext5b')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[4].set_transauto(lambda: self[0]() == 1)
self[5].set_transauto(lambda: self[4].get_trans() or self[4][0]() == 1)
class BearerCap(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('RadioChanReq', val=1, bl=2, dic=_RadioChanReq_dict),
Uint('CodingStd', bl=1, dic=_BCapCodingStd_dict),
Uint('TransferMode', bl=1, dic=_TransferMode_dict),
Uint('InfoTransferCap', bl=3, dic=_TransferCap_dict),
_BearerCapExt3a('Ext3a'),
_BearerCapExt3b('Ext3b'),
_BearerCapOct4('Oct4', trans=True),
_BearerCapOct5('Oct5', trans=True),
_BearerCapOct6('Oct6', trans=True),
_BearerCapOct7('Oct7', trans=True)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[5].set_transauto(lambda: self[0]() == 1)
self[6].set_transauto(lambda: self[5].get_trans() or self[5][0]() == 1)
def _from_char(self, char):
Envelope._from_char(self, char)
if char.len_byte():
# Oct4
self[7].set_trans(False)
self[7]._from_char(char)
if char.len_byte():
# Oct5
self[8].set_trans(False)
self[8]._from_char(char)
if char.len_byte():
# Oct6
self[9].set_trans(False)
self[9]._from_char(char)
if char.len_byte():
# Oct7
self[10].set_trans(False)
self[10]._from_char(char)
#------------------------------------------------------------------------------#
# Call Control Capabilities
# TS 24.008, 10.5.4.5a
#------------------------------------------------------------------------------#
class CCCap(Envelope):
_GEN = (
Uint('MaxNumSupportedBearers', bl=4),
Uint('MultimediaCAT', bl=1),
Uint('ENICM', bl=1),
Uint('PCP', bl=1),
Uint('DTMF', val=1, bl=1),
Uint('spare', bl=4),
Uint('MaxNumSpeechBearers', bl=4)
)
#------------------------------------------------------------------------------#
# Call state
# TS 24.008, 10.5.4.6
#------------------------------------------------------------------------------#
_CodingStd_dict = {
0 : 'Standardized coding, as described in ITU-T Q.931',
1 : 'Reserved for other international standards',
2 : 'National standard',
3 : 'Standard defined for the GSM PLMNs'
}
_CallState_dict = {
0 : 'null',
2 : 'MM connection pending',
34 : 'CC prompt present',
35 : 'Wait for network information',
36 : 'CC-Establishment present',
37 : 'CC-Establishment confirmed',
38 : 'Recall present',
1 : 'call initiated',
3 : 'mobile originating call proceeding',
4 : 'call delivered',
6 : 'call present',
7 : 'call received',
8 : 'connect request',
9 : 'mobile terminating call confirmed',
10 : 'active',
11 : 'disconnect request',
12 : 'disconnect indication',
19 : 'release request',
26 : 'mobile originating modify',
27 : 'mobile terminating modify',
28 : 'connect indication'
}
class CallState(Envelope):
_GEN = (
Uint('CodingStd', bl=2, dic=_CodingStd_dict),
Uint('Value', bl=6, dic=_CallState_dict)
)
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Called party BCD number
# TS 24.008, 10.5.4.7
#------------------------------------------------------------------------------#
# generic BCD number
2017-12-15 20:24:07 +00:00
# BCDType and NumPlan are extended with values from TS 23.040, section 9.1.2.15
2017-07-04 21:12:41 +00:00
_BCDType_dict = {
0 : 'unknown',
1 : 'international number',
2 : 'national number',
3 : 'network specific number',
4 : 'dedicated access, short code',
5 : 'alphanumeric', # only for SMS
6 : 'abbreviated number', # only for SMS
7 : _str_reserved
2017-07-04 21:12:41 +00:00
}
2017-07-04 21:12:41 +00:00
_NumPlan_dict = {
0 : 'unknown',
1 : 'ISDN / telephony numbering plan (E.164 / E.163)',
2017-12-15 20:24:07 +00:00
2 : 'generic numbering plan',
2017-07-04 21:12:41 +00:00
3 : 'data numbering plan (X.121)',
4 : 'telex numbering plan (F.69)',
5 : 'service center specific', # only for SMS
6 : 'service center specific', # only for SMS
2017-07-04 21:12:41 +00:00
8 : 'national numbering plan',
9 : 'private numbering plan',
10: 'ERMES numbering plan', # only for SMS
2017-07-04 21:12:41 +00:00
11: 'reserved for CTS',
15: _str_reserved
2017-07-04 21:12:41 +00:00
}
_PresInd_dict = {
0 : 'presentation allowed',
1 : 'presentation restricted',
2 : 'number not available due to interworking',
3 : _str_reserved
}
_ScreenInd_dict = {
0 : 'user-provided, not screened',
1 : 'user-provided, verified and passed',
2 : 'user-provided, verified and failed',
3 : 'network provided'
}
class _BCDNumberExt3a(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('PresentationInd', bl=2, dic=_PresInd_dict),
Uint('spare', bl=3),
Uint('ScreeningInd', bl=2, dic=_ScreenInd_dict)
)
2017-07-04 21:12:41 +00:00
class BCDNumber(Envelope):
ENV_SEL_TRANS = False
2017-07-04 21:12:41 +00:00
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Type', val=1, bl=3, dic=_BCDType_dict),
Uint('NumberingPlan', val=1, bl=4, dic=_NumPlan_dict),
_BCDNumberExt3a('Ext3a'),
2017-07-04 21:12:41 +00:00
BufBCD('Num')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[3].set_transauto(lambda: self[0]() == 1)
2017-07-04 21:12:41 +00:00
class CalledPartyBCDNumber(BCDNumber):
pass
#------------------------------------------------------------------------------#
# Called party subaddress
# TS 24.008, 10.5.4.8
#------------------------------------------------------------------------------#
# generic sub-address
_SubaddrType_dict = {
0 : 'NSAP',
2 : 'user defined'
}
class Subaddress(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Type', bl=3, dic=_SubaddrType_dict),
Uint('Odd', bl=1),
Uint('spare', bl=3),
Buf('Addr', val=b'', rep=REPR_HEX)
)
class CalledPartySubaddress(Subaddress):
pass
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Calling party BCD number
# TS 24.008, 10.5.4.9
#------------------------------------------------------------------------------#
class CallingPartyBCDNumber(BCDNumber):
pass
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Calling party subaddress
# TS 24.008, 10.5.4.10
#------------------------------------------------------------------------------#
class CallingPartySubaddress(Subaddress):
pass
#------------------------------------------------------------------------------#
# Cause
# TS 24.008, 10.5.4.11
#------------------------------------------------------------------------------#
_Location_dict = {
0 : 'User',
1 : 'Private network serving the local user',
2 : 'Public network serving the local user',
4 : 'Public network serving the remote user',
5 : 'Private network serving the remote user',
10 : 'Network beyond interworking point'
2017-07-04 21:12:41 +00:00
}
_CauseClass_dict = {
0 : 'normal event',
1 : 'normal event',
2 : 'resource unavailable',
3 : 'service or option not available',
4 : 'service or option not implemented',
5 : 'invalid message (e.g. parameter out of range)',
6 : 'protocol error (e.g. unknown message)',
7 : 'interworking'
}
_CauseValue_dict = {
0 : {
1 : 'Unassigned (unallocated) number',
3 : 'No route to destination',
6 : 'Channel unacceptable',
8 : 'Operator determined barring'
},
1 : {
0 : 'Normal call clearing',
1 : 'User busy',
2 : 'No user responding',
3 : 'User alerting, no answer',
5 : 'Call rejected',
6 : 'Number changed',
8 : 'Call rejected due to feature at the destination',
9 : 'Pre-emption',
10 : 'Non selected user clearing',
11 : 'Destination out of order',
12 : 'Invalid number format (incomplete number)',
13 : 'Facility rejected',
14 : 'Response to STATUS ENQUIRY',
15 : 'Normal, unspecified'
},
2 : {
2 : 'No circuit/channel available',
6 : 'Network out of order',
9 : 'Temporary failure',
10 : 'Switching equipment congestion',
11 : 'Access information discarded',
12 : 'requested circuit/channel not available',
15 : 'Resources unavailable, unspecified'
},
3 : {
1 : 'Quality of service unavailable',
2 : 'Requested facility not subscribed',
7 : 'Incoming calls barred within the CUG',
9 : 'Bearer capability not authorized',
10 : 'Bearer capability not presently available',
15 : 'Service or option not available, unspecified'
},
4 : {
1 : 'Bearer service not implemented',
4 : 'ACM equal to or greater than ACMmax',
5 : 'Requested facility not implemented',
6 : 'Only restricted digital information bearer capability is available',
15 : 'Service or option not implemented, unspecified'
},
5 : {
1 : 'Invalid transaction identifier value',
7 : 'User not member of CUG',
8 : 'Incompatible destination',
11 : 'Invalid transit network selection',
15 : 'Semantically incorrect message'
},
6 : {
0 : 'Invalid mandatory information',
1 : 'Message type non-existent or not implemented',
2 : 'Message type not compatible with protocol state',
3 : 'Information element non-existent or not implemented',
4 : 'Conditional IE error',
5 : 'Message not compatible with protocol state',
6 : 'Recovery on timer expiry',
15 : 'Protocol error, unspecified'
},
7 : {
15 : 'Interworking, unspecified'
}
}
class _CauseExt3a(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Recommendation', bl=7)
)
class Cause(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('CodingStd', bl=2, dic=_CodingStd_dict),
Uint('spare', bl=1),
Uint('Location', bl=4, dic=_Location_dict),
_CauseExt3a('Ext3a'),
Uint('Ext', val=1, bl=1),
Uint('Class', bl=3, dic=_CauseClass_dict),
Uint('Value', bl=4),
Buf('Diagnostic', val=b'')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[4].set_transauto(lambda: self[0]() == 1)
self[7].set_dicauto(lambda: _CauseValue_dict[self[6]()])
#------------------------------------------------------------------------------#
# Congestion level
# TS 24.008, 10.5.4.12
#------------------------------------------------------------------------------#
CongestionLevel_dict = {
0 : 'receiver ready',
15 : 'receiver not ready'
}
#------------------------------------------------------------------------------#
# Connected number
# TS 24.008, 10.5.4.13
#------------------------------------------------------------------------------#
class ConnectedNumber(BCDNumber):
pass
#------------------------------------------------------------------------------#
# Connected subaddress
# TS 24.008, 10.5.4.14
#------------------------------------------------------------------------------#
class ConnectedSubaddress(Subaddress):
pass
#------------------------------------------------------------------------------#
# High layer compatibility
# TS 24.008, 10.5.4.16
#------------------------------------------------------------------------------#
class _HighLayerCompOct3(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('CodingStd', bl=2, dic=_CodingStd_dict),
Uint('Interpretation', bl=3),
Uint('PresentationMethProtProfile', bl=2)
)
class _HighLayerCompExt4a(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('HighLayerCharIdentExt', bl=7)
)
class _HighLayerCompOct4(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint('Ext', bl=1),
Uint('HighLayerCharIdent', bl=7),
_HighLayerCompExt4a('Ext4a')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[2].set_transauto(lambda: self[0]() == 1)
class HighLayerComp(Envelope):
_GEN = (
_HighLayerCompOct3('Oct3', trans=True),
_HighLayerCompOct4('Oct4', trans=True)
)
def _from_char(self, char):
l = char.len_byte()
if l > 1:
self[0].set_trans(False)
self[1].set_trans(False)
elif l == 1:
self[0].set_trans(False)
Envelope._from_char(self, char)
#------------------------------------------------------------------------------#
# Notification indicator
# TS 24.008, 10.5.4.20
#------------------------------------------------------------------------------#
_Notification_dict = {
0 : 'User suspended',
1 : 'User resumed',
2 : 'Bearer change'
}
class NotificationInd(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('Notification', bl=7, dic=_Notification_dict)
)
#------------------------------------------------------------------------------#
# Progress indicator
# TS 24.008, 10.5.4.21
#------------------------------------------------------------------------------#
_Progress_dict = {
1 : 'Call is not end-to-end PLMN/ISDN, further call progress information may be available in-band',
2 : 'Destination address in non-PLMN/ISDN',
3 : 'Origination address in non-PLMN/ISDN',
4 : 'Call has returned to the PLMN/ISDN',
8 : 'In-band information or appropriate pattern now available',
9 : 'In-band multimedia CAT available',
32: 'Call is end-to-end PLMN/ISDN',
64: 'Queueing'
}
class ProgressInd(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('CodingStd', bl=2, dic=_CodingStd_dict),
Uint('spare', bl=1),
Uint('Location', bl=4, dic=_Location_dict),
Uint('Ext', val=1, bl=1),
Uint('Progress', bl=7, dic=_Progress_dict)
)
#------------------------------------------------------------------------------#
# Recall type $(CCBS)$
# TS 24.008, 10.5.4.21a
#------------------------------------------------------------------------------#
_RecallType_dict = {
0 : 'CCBS',
7 : _str_reserved
}
class RecallType(Envelope):
_GEN = (
Uint('spare', bl=5),
Uint('Value', bl=3, dic=_RecallType_dict)
)
#------------------------------------------------------------------------------#
# Redirecting party BCD number
# TS 24.008, 10.5.4.21b
#------------------------------------------------------------------------------#
class RedirectingPartyBCDNumber(BCDNumber):
pass
#------------------------------------------------------------------------------#
# Redirecting party subaddress
# TS 24.008, 10.5.4.21c
#------------------------------------------------------------------------------#
class RedirectingPartySubaddress(Subaddress):
pass
#------------------------------------------------------------------------------#
# Repeat indicator
# TS 24.008, 10.5.4.22
#------------------------------------------------------------------------------#
RepeatInd_dict = {
1 : 'Circular for successive selection, mode 1 alternate mode 2',
2 : 'Support of fallback, mode 1 preferred, mode 2 selected if setup of mode 1 fails',
3 : _str_reserved,
4 : 'Service change and fallback, mode 1 alternate mode 2, mode 1 preferred'
}
#------------------------------------------------------------------------------#
# Signal
# TS 24.008, 10.5.4.23
#------------------------------------------------------------------------------#
_Signal_dict = {
0 : 'dial tone on',
1 : 'ring back tone on',
2 : 'intercept tone on',
3 : 'network congestion tone on',
4 : 'busy tone on',
5 : 'confirm tone on',
6 : 'answer tone on',
7 : 'call waiting tone on',
8 : 'off-hook warning tone on',
63 : 'tones off',
79 : 'alerting off'
}
class Signal(Uint8):
_dic = _Signal_dict
#------------------------------------------------------------------------------#
# User-user
# TS 24.008, 10.5.4.25
#------------------------------------------------------------------------------#
_UUType_dict = {
0 : 'User specific protocol',
1 : 'OSI high layer protocols',
2 : 'X.244',
3 : 'Reserved for system management convergence function',
4 : 'IA5 characters',
7 : 'rate adaption according to ITU-T V.120',
8 : 'user-network call control messages according to ITU-T Q.931',
79: '3GPP capability exchange protocol'
}
class UserUser(Envelope):
_GEN = (
Uint8('Type', val=4, dic=_UUType_dict),
Buf('Data', val=b'') # should be 32 or 128 bytes
)
#------------------------------------------------------------------------------#
# Alerting Pattern $(NIA)$
# TS 24.008, 10.5.4.26
#------------------------------------------------------------------------------#
class AlertingPattern(Envelope):
_GEN = (
Uint('spare', bl=4),
Uint('Value', bl=4)
)
#------------------------------------------------------------------------------#
# Allowed actions $(CCBS)$
# TS 24.008, 10.5.4.27
#------------------------------------------------------------------------------#
_CCBSAct_dict = {
0 : 'Activation of CCBS not possible',
1 : 'Activation of CCBS possible'
}
class CCBSAllowedActions(Envelope):
_GEN = (
Uint('CCBSAct', bl=1, dic=_CCBSAct_dict),
Uint('spare', bl=7)
)
#------------------------------------------------------------------------------#
# Stream Identifier
# TS 24.008, 10.5.4.28
#------------------------------------------------------------------------------#
class StreamIdent(Uint8):
pass
#------------------------------------------------------------------------------#
# Network Call Control Capabilities
# TS 24.008, 10.5.4.29
#------------------------------------------------------------------------------#
class NetCCCap(Envelope):
_GEN = (
Uint('spare', bl=7),
Uint('MultiCallSupport', bl=1)
)
#------------------------------------------------------------------------------#
# Cause of No CLI
# TS 24.008, 10.5.4.30
#------------------------------------------------------------------------------#
_CauseNoCLI_dict = {
0 : 'Unavailable',
1 : 'Reject by user',
2 : 'Interaction with other service',
3 : 'Coin line/payphone'
2017-07-04 21:12:41 +00:00
}
class CauseNoCLI(Uint8):
_dic = _CauseNoCLI_dict
#------------------------------------------------------------------------------#
# Supported codec list
# TS 24.008, 10.5.4.32
#------------------------------------------------------------------------------#
_CodecSysID_dict = {
0:'GSM',
4:'UMTS'
}
class _CodecBitmap(Envelope):
2017-07-04 21:12:41 +00:00
_GEN = (
Uint('TDMA_EFR', bl=1),
Uint('UMTS_AMR2', bl=1),
Uint('UMTS_AMR', bl=1),
Uint('HR_AMR', bl=1),
Uint('FR_AMR', bl=1),
Uint('GSM_EFR', bl=1),
Uint('GSM_HR', bl=1),
Uint('GSM_FR', bl=1),
Uint('reserved', bl=1),
Uint('reserved', bl=1),
Uint('OHR_AMR-WB', bl=1),
Uint('OFR_AMR-WB', bl=1),
Uint('OHR_AMR', bl=1),
Uint('UMTS_AMR-WB', bl=1),
Uint('FR_AMR-WB', bl=1),
Uint('PDC_EFR', bl=1),
Buf('spare', val=b'')
)
class CodecSysID(Envelope):
_GEN = (
Uint8('SysID', dic=_CodecSysID_dict),
Uint8('BMLen'),
_CodecBitmap('CodecBM')
2017-07-04 21:12:41 +00:00
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_valauto(lambda: self[2].get_len())
def _from_char(self, char):
self[0]._from_char(char)
self[1]._from_char(char)
l = self[1]()
clen = char._len_bit
char._len_bit = char._cur + 8*l
if char._len_bit > clen:
raise(EltErr('{0} [_from_char]: bit length overflow'.format(self._name)))
if l == 1:
for b in self[8:16]:
b.set_trans(True)
self[2]._from_char(char)
char._len_bit = clen
class SupportedCodecs(Sequence):
_GEN = CodecSysID()
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Attach result
# TS 24.008, 10.5.5.1
#------------------------------------------------------------------------------#
_AttachResult_dict = {
1 : 'GPRS-only attached',
3 : 'Combined GPRS/IMSI attached'
}
class AttachResult(Envelope):
_GEN = (
Uint('FollowOnProc', bl=1),
Uint('Result', bl=3, dic=_AttachResult_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Attach type
# TS 24.008, 10.5.5.2
#------------------------------------------------------------------------------#
_AttachType_dict = {
1 : 'GPRS attach',
2 : 'Not used (earlier versions)',
3 : 'Combined GPRS/IMSI attach',
4 : 'Emergency attach'
}
class AttachType(Envelope):
_GEN = (
Uint('FollowOnReq', bl=1),
Uint('Type', bl=3, dic=_AttachType_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Ciphering algorithm
# TS 24.008, 10.5.5.3
#------------------------------------------------------------------------------#
CiphAlgo_dict = {
0 : 'ciphering not used',
1 : 'GEA/1',
2 : 'GEA/2',
3 : 'GEA/3',
4 : 'GEA/4',
5 : 'GEA/5',
6 : 'GEA/6',
7 : 'GEA/7'
}
#------------------------------------------------------------------------------#
# Integrity algorithm
# TS 24.008, 10.5.5.3a
#------------------------------------------------------------------------------#
IntegAlgo_dict = {
0 : 'GIA/4',
1 : 'GIA/5',
2 : 'GIA/6',
3 : 'GIA/7'
}
#------------------------------------------------------------------------------#
# TMSI Status
# TS 24.008, 10.5.5.4
#------------------------------------------------------------------------------#
_TMSIStatus_dict = {
0 : 'no valid TMSI available',
1 : 'valid TMSI available'
}
class TMSIStatus(Envelope):
_GEN = (
Uint('spare', bl=3),
Uint('Flag', bl=1, dic=_TMSIStatus_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Detach type
# TS 24.008, 10.5.5.5
#------------------------------------------------------------------------------#
_DetachTypeMO_dict = {
1 : 'GPRS detach',
2 : 'IMSI detach',
3 : 'Combined GPRS/IMSI detach'
}
_DetachTypeMT_dict = {
1 : 're-attach required',
2 : 're-attach not required',
3 : 'IMSI detach (after VLR failure)'
}
class DetachTypeMO(Envelope):
_GEN = (
Uint('PowerOff', bl=1),
Uint('Type', bl=3, dic=_DetachTypeMO_dict)
2017-07-04 21:12:41 +00:00
)
class DetachTypeMT(Envelope):
_GEN = (
Uint('spare', bl=1),
Uint('Type', bl=3, dic=_DetachTypeMT_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# DRX Parameter
# TS 24.008, 10.5.5.6
#------------------------------------------------------------------------------#
_SplitPGCycleC_dict = {
0 : '704 (no DRX)',
65 : '71',
66 : '72',
67 : '74',
68 : '75',
69 : '77',
70 : '79',
71 : '80',
72 : '83',
73 : '86',
74 : '88',
75 : '90',
76 : '92',
77 : '96',
78 : '101',
79 : '103',
80 : '107',
81 : '112',
82 : '116',
83 : '118',
84 : '128',
85 : '141',
86 : '144',
87 : '150',
88 : '160',
89 : '171',
90 : '176',
91 : '192',
92 : '214',
93 : '224',
94 : '235',
95 : '256',
96 : '288',
97 : '320',
98 : '352'
}
_DRXCycleLen_dict = {
0 : 'DRX not specified by the MS',
6 : 'Iu coeff 6 and S1 T = 32',
7 : 'Iu coeff 7 and S1 T = 64',
8 : 'Iu coeff 8 and S1 T = 128',
9 : 'Iu coeff 9 and S1 T = 256'
}
_NonDRXTimer_dict = {
0 : 'no non-DRX mode after transfer state',
1 : 'max 1 sec non-DRX mode after transfer state',
2 : 'max 2 sec non-DRX mode after transfer state',
3 : 'max 4 sec non-DRX mode after transfer state',
4 : 'max 8 sec non-DRX mode after transfer state',
5 : 'max 16 sec non-DRX mode after transfer state',
6 : 'max 32 sec non-DRX mode after transfer state',
7 : 'max 64 sec non-DRX mode after transfer state'
}
class DRXParam(Envelope):
_GEN = (
Uint8('SPLIT_PG_CYCLE_CODE', dic=_SplitPGCycleC_dict),
Uint('DRXCycleLen', bl=4, dic=_DRXCycleLen_dict),
Uint('SPLITonCCCH', bl=1),
Uint('NonDRXTimer', bl=3, dic=_NonDRXTimer_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Force to standby
# TS 24.008, 10.5.5.7
#------------------------------------------------------------------------------#
ForceStdby_dict = {
2017-07-04 21:12:41 +00:00
0 : 'Force to standby not indicated',
1 : 'Force to standby indicated'
}
class ForceStdby(Envelope):
_GEN = (
Uint('spare', bl=1),
Uint('Value', bl=3, dic=ForceStdby_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# IMEISV request
# TS 24.008, 10.5.5.10
#------------------------------------------------------------------------------#
class IMEISVReq(Envelope):
_GEN = (
Uint('spare', bl=1),
Uint('Value', bl=3, dic={0:'IMEISV not requested', 1:'IMEISV requested'})
)
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# GMM Cause
# TS 24.008, 10.5.5.14
#------------------------------------------------------------------------------#
GMMCause_dict = {
0 : 'Protocol error, unspecified',
2 : 'IMSI unknown in HLR',
3 : 'Illegal MS',
5 : 'IMEI not accepted',
6 : 'Illegal ME',
7 : 'GPRS services not allowed',
8 : 'GPRS services and non-GPRS services not allowed',
9 : 'MS identity cannot be derived by the network',
10 : 'implicitly detached',
11 : 'PLMN not allowed',
12 : 'Location Area not allowed',
13 : 'Roaming not allowed in this location area',
14 : 'GPRS services not allowed in this PLMN',
15 : 'No Suitable Cells In Location Area',
16 : 'MSC temporarily not reachable',
17 : 'Network failure',
20 : 'MAC failure',
21 : 'Synch failure',
22 : 'Congestion',
23 : 'GSM authentication unacceptable',
25 : 'Not authorized for this CSG',
40 : 'No PDP context activated',
48 : 'retry upon entry into a new cell',
95 : 'Semantically incorrect message',
96 : 'Invalid mandatory information',
97 : 'Message type non-existent or not implemented',
98 : 'Message type not compatible with the protocol state',
99 : 'Information element non-existent or not implemented',
100: 'Conditional IE error',
101: 'Message not compatible with the protocol state',
111: 'Protocol error, unspecified'
}
class GMMCause(Uint8):
_dic = GMMCause_dict
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Routing Area Identifier
# TS 24.008, 10.5.5.15
#------------------------------------------------------------------------------#
class RAI(Envelope):
_GEN = (
PLMN(),
Uint16('LAC', rep=REPR_HEX),
Uint8('RAC', rep=REPR_HEX)
2017-07-04 21:12:41 +00:00
)
encode = Envelope.set_val
2017-07-04 21:12:41 +00:00
def decode(self):
2017-09-16 16:11:34 +00:00
return (self[0].decode(), self[1].get_val(), self[2].get_val())
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# Update result
# TS 24.008, 10.5.5.17
#------------------------------------------------------------------------------#
_UpdateResult_dict = {
0 : 'RA updated',
1 : 'combined RA/LA updated',
4 : 'RA updated and ISR activated',
5 : 'combined RA/LA updated and ISR activated',
}
class UpdateResult(Envelope):
_GEN = (
Uint('FollowOnProc', bl=1),
Uint('Value', bl=3, dic=_UpdateResult_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Update type
# TS 24.008, 10.5.5.18
#------------------------------------------------------------------------------#
_UpdType_dict = {
0 : 'RA updating',
1 : 'combined RA/LA updating',
2 : 'combined RA/LA updating with IMSI attach',
3 : 'Periodic updating'
}
class UpdateType(Envelope):
_GEN = (
Uint('FollowOnReq', bl=1),
Uint('Value', bl=3, dic=_UpdType_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Service type
# TS 24.008, 10.5.5.20
#------------------------------------------------------------------------------#
ServiceType_dict = {
0 : 'Signalling',
1 : 'Data',
2 : 'Paging Response',
3 : 'MBMS Multicast Service Reception',
4 : 'MBMS Broadcast Service Reception',
}
#------------------------------------------------------------------------------#
# PS LCS Capability
# TS 24.008, 10.5.5.22
#------------------------------------------------------------------------------#
class PSLCSCap(Envelope):
_GEN = (
Uint('spare', bl=2),
Uint('APC', bl=1),
Uint('OTD_A', bl=1),
Uint('OTD_B', bl=1),
Uint('GPS_A', bl=1),
Uint('GPS_B', bl=1),
Uint('GPS_C', bl=1)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Network feature support
# TS 24.008, 10.5.5.23
#------------------------------------------------------------------------------#
class NetFeatSupp(Envelope):
_GEN = (
Uint('LCS_MOLR', bl=1),
Uint('MBMS', bl=1),
Uint('IMS_VoPS', bl=1),
Uint('EMC_BS', bl=1)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Additional network feature support
# TS 24.008, 10.5.5.23A
#------------------------------------------------------------------------------#
class AddNetFeatSupp(Envelope):
_GEN = (
Uint('spare', bl=7),
Uint('GPRS_SMS', bl=1)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Voice Domain Preference
# TS 24.008, 10.5.5.24
#------------------------------------------------------------------------------#
_UEUsage_dict = {
0 : 'Voice centric',
1 : 'Data centric'
}
_VoiceDomPref_dict = {
0 : 'CS Voice only',
1 : 'IMS PS Voice only',
2 : 'CS voice preferred, IMS PS Voice as secondary',
3 : 'IMS PS voice preferred, CS Voice as secondary'
}
class VoiceDomPref(Envelope):
_GEN = (
Uint('spare', bl=5),
Uint('UEUsage', bl=1, dic=_UEUsage_dict),
Uint('VoiceDomPref', bl=2, dic=_VoiceDomPref_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Requested MS information
# TS 24.008, 10.5.5.25
#------------------------------------------------------------------------------#
class ReqMSInfo(Envelope):
_GEN = (
Uint('I_RAT', bl=1),
Uint('I_RAT2', bl=1),
Uint('spare', bl=2)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# P-TMSI Type
# TS 24.008, 10.5.5.29
#------------------------------------------------------------------------------#
_PTMSIType_dict = {
0 : 'Native P-TMSI',
1 : 'Mapped P-TMSI'
}
class PTMSIType(Envelope):
_GEN = (
Uint('spare', bl=3),
Uint('Value', bl=1, dic=_PTMSIType_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Network Resource Identifier
# TS 24.008, 10.5.5.31
#------------------------------------------------------------------------------#
class NRICont(Envelope):
_GEN = (
Uint('Value', bl=10, rep=REPR_HEX),
Uint('spare', bl=6)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Extended DRX parameters
# TS 24.008, 10.5.5.32
#------------------------------------------------------------------------------#
class ExtDRXParam(Envelope):
_GEN = (
Uint('PTX', bl=4),
Uint('eDRX', bl=4)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# User-Plane integrity indicator
# TS 24.008, 10.5.5.34
#------------------------------------------------------------------------------#
class UPIntegrityInd(Envelope):
_GEN = (
Uint('spare', bl=3),
Uint('Value', bl=1)
)
2019-11-08 15:11:02 +00:00
#------------------------------------------------------------------------------#
# DCN-ID
# TS 24.008, 10.5.5.35
#------------------------------------------------------------------------------#
# Dedicated Core Network ID
class DCNID(Uint16):
pass
#------------------------------------------------------------------------------#
# Non-3GPP NW provided policies
# TS 24.008, 10.5.5.37
#------------------------------------------------------------------------------#
_Non3GPPNWProvPol_dict = {
0 : 'use of non-3GPP emergency numbers not permitted',
1 : 'use of non-3GPP emergency numbers permitted'
}
class Non3GPPNWProvPol(Envelope):
_GEN = (
Uint('spare', bl=3),
Uint('Value', bl=1, dic=_Non3GPPNWProvPol_dict)
)
#------------------------------------------------------------------------------#
# Access point name
# TS 24.008, 10.5.6.1
#------------------------------------------------------------------------------#
# referring TS 23.003
class _APNItem(Envelope):
_GEN = (
Uint8('Len'),
Buf('Value')
2017-07-04 21:12:41 +00:00
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0].set_valauto(self[1].get_len)
self[1].set_blauto(lambda: 8*self[0].get_val())
2019-11-26 17:34:57 +00:00
class APN(Sequence):
_GEN = _APNItem('APNItem')
def set_val(self, val):
if isinstance(val, str_types):
self.encode(val)
else:
Sequence.set_val(self, val)
def encode(self, val):
apn_items = val.split('.')
Sequence.set_val(self, [{'Value': apn_item.encode()} for apn_item in apn_items])
def decode(self):
return '.'.join([apn_item[1].decode() for apn_item in self.get_val()])
def repr(self):
# element transparency
if self.get_trans():
trans = ' [transparent]'
else:
trans = ''
# additional description
if self._desc:
desc = ' [%s]' % self._desc
else:
desc = ''
#
return '<%s%s%s : %s>' % (self._name, desc, trans, self.decode())
__repr__ = repr
def show(self):
return self.get_hier_abs() * ' ' + self.repr()
2017-07-04 21:12:41 +00:00
2017-10-13 19:28:18 +00:00
#------------------------------------------------------------------------------#
# Network service access point identifier
# TS 24.008, 10.5.6.2
#------------------------------------------------------------------------------#
_NSAPI_dict = {
0 : _str_reserved,
1 : _str_reserved,
2 : _str_reserved,
3 : _str_reserved,
4 : _str_reserved
}
class NSAPI(Envelope):
_GEN = (
Uint('spare', bl=4),
2017-10-13 19:28:18 +00:00
Uint('Value', val=5, bl=4, dic=_NSAPI_dict)
)
#------------------------------------------------------------------------------#
# Protocol configuration options
# TS 24.008, 10.5.6.3
#------------------------------------------------------------------------------#
_ProtConfig_dict = {
# 3GPP additional parameters
0x0001 : 'P-CSCF IPv6 Address Request',
0x0002 : 'IM CN Subsystem Signaling Flag',
0x0003 : 'DNS Server IPv6 Address Request',
0x0004 : 'Policy Control rejection code',
0x0005 : 'Selected Bearer Control Mode',
0x0006 : 'Reserved',
0x0007 : 'DSMIPv6 Home Agent Address',
0x0008 : 'DSMIPv6 Home Network Prefix',
0x0009 : 'DSMIPv6 IPv4 Home Agent Address',
0x000A : 'IP address allocation via NAS signalling',
0x000B : 'Reserved',
0x000C : 'P-CSCF IPv4 Address',
0x000D : 'DNS server IPv4 address request',
0x000E : 'MSISDN Request',
0x000F : 'IFOM-Support-Request',
0x0010 : 'IPv4 Link MTU Request',
0x0011 : 'Support of Local address in TFT indicator',
0x0012 : 'P-CSCF Re-selection support',
0x0013 : 'NBIFOM request indicator',
0x0014 : 'NBIFOM mode',
0x0015 : 'Non-IP Link MTU Request',
0x0016 : 'APN rate control support indicator',
# ETSI / IETF protocol identifiers
0x8021 : 'IPCP',
0xC021 : 'LCP',
0xC023 : 'PAP',
0xC223 : 'CHAP'
}
class ProtConfigElt(Envelope):
2018-01-14 10:03:52 +00:00
# when set to True, will decode the content into NCP / LCP / PAP / CHAP
DECODE_INNER = True
_ContLUT = {
0x8021 : NCP,
2018-01-14 10:03:52 +00:00
0xC021 : LCP,
0xC023 : PAP,
0xC223 : CHAP
}
_GEN = (
Uint16('ID', val=0x8021, dic=_ProtConfig_dict),
Uint8('Len'),
Buf('Cont', val=b'', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_valauto(lambda: self[2].get_len())
self[2].set_blauto(lambda: 8*self[1].get_val())
2018-01-14 10:03:52 +00:00
def set_val(self, val):
if isinstance(val, (list, tuple)) and not isinstance(val[2], bytes_types):
# eventually replace Cont by a NCP / LCP / PAP / CHAP Envelope
if val[0] in self._ContLUT:
self.replace(self[2], self._ContLUT[val[0]]('Cont'))
elif isinstance(val, dict) and 'ID' in val and 'Cont' in val \
and not isinstance(val['Cont'], bytes_types):
# eventually replace Cont by a NCP / LCP / PAP / CHAP Envelope
if val['ID'] in self._ContLUT:
self.replace(self[2], self._ContLUT[val['ID']]('Cont'))
Envelope.set_val(self, val)
def _from_char(self, char):
self[0]._from_char(char)
self[1]._from_char(char)
ident, cont = self[0].get_val(), None
if self.DECODE_INNER and ident in self._ContLUT:
2018-01-14 10:03:52 +00:00
cont = self._ContLUT[ident]('Cont')
ccur, clen = char._cur, char._len_bit
char._len_bit = ccur + 8*self[1].get_val()
if char._len_bit > clen:
raise(EltErr('{0} [_from_char]: bit length overflow'.format(self._name)))
try:
cont._from_char(char)
except Exception:
char._cur, char._len_bit = ccur, clen
else:
if char._cur < char._len_bit:
char._cur, char._len_bit = ccur, clen
else:
self.replace(self[2], cont)
char._len_bit = clen
if cont is None:
self[2]._from_char(char)
class ProtConfig(Envelope):
_GEN = (
Uint('Ext', val=1, bl=1),
Uint('spare', bl=4),
Uint('Prot', bl=3, dic={0:'PPP with IP PDP'}),
Sequence('Config', GEN=ProtConfigElt())
)
2017-10-13 19:28:18 +00:00
#------------------------------------------------------------------------------#
# Packet data protocol address
# TS 24.008, 10.5.6.4
#------------------------------------------------------------------------------#
_PDPTypeOrg_dict = {
0 : 'ETSI allocated',
1 : 'IETF allocated',
15 : 'Empty PDP type',
}
_PDPTypeNum_dict = {
# ETSI allocated
2017-10-13 19:28:18 +00:00
0 : 'reserved',
1 : 'PPP',
# IETF allocated
2017-10-13 19:28:18 +00:00
33 : 'IPv4',
87 : 'IPv6',
141 : 'IPv4v6',
}
2017-10-13 19:28:18 +00:00
class PDPAddr(Envelope):
_GEN = (
Uint('spare', val=0, bl=4, rep=REPR_HEX),
Uint('TypeOrg', val=1, bl=4, dic=_PDPTypeOrg_dict),
Uint8('Type', val=33, dic=_PDPTypeNum_dict),
IPAddr('Addr', val=b'') # empty when used in a Request, 4, 16, 20... up to 64 bytes otherwise
2017-10-13 19:28:18 +00:00
)
2017-10-13 19:28:18 +00:00
#------------------------------------------------------------------------------#
# Quality of service
# TS 24.008, 10.5.6.5
#------------------------------------------------------------------------------#
_ReliabClass_dict = {
0 : 'subscribed reliability class',
1 : 'unused; interpreted as unack GTP, ack LLC and RLC, protected data',
2 : 'unack GTP, ack LLC and RLC, protected data',
3 : 'unack GTP and LLC, ack RLC, protected data',
4 : 'unack GTP, LLC and RLC, protected data',
5 : 'unack GTP, LLC and RLC, unprotected data',
6 : 'unack GTP and RLC, ack LLC, protected data',
7 : _str_reserved,
}
_DelayClass_dict = {
0 : 'subscribed delay class',
1 : 'delay class 1',
2 : 'delay class 2',
3 : 'delay class 3',
4 : 'delay class 4 (best effort)',
7 : _str_reserved,
}
_PrecedClass_dict = {
0 : 'subscribed precedence',
1 : 'high priority',
2 : 'normal priority',
3 : 'low priority',
4 : 'normal priority',
7 : _str_reserved,
}
_PeakTP_dict = {
0 : 'subscribed peak throughput',
1 : 'Up to 1 000 octet/s',
2 : 'Up to 2 000 octet/s',
3 : 'Up to 4 000 octet/s',
4 : 'Up to 8 000 octet/s',
5 : 'Up to 16 000 octet/s',
6 : 'Up to 32 000 octet/s',
7 : 'Up to 64 000 octet/s',
8 : 'Up to 128 000 octet/s',
9 : 'Up to 256 000 octet/s',
}
_MeanTP_dict = {
0 : 'subscribed mean throughput',
1 : '100 octet/h',
2 : '200 octet/h',
3 : '500 octet/h',
4 : '1 000 octet/h',
5 : '2 000 octet/h',
6 : '5 000 octet/h',
7 : '10 000 octet/h',
8 : '20 000 octet/h',
9 : '50 000 octet/h',
10: '100 000 octet/h',
11: '200 000 octet/h',
12: '500 000 octet/h',
13: '1 000 000 octet/h',
14: '2 000 000 octet/h',
15: '5 000 000 octet/h',
16: '10 000 000 octet/h',
17: '20 000 000 octet/h',
18: '50 000 000 octet/h',
30: _str_reserved,
31: 'Best effort'
}
_ErronSDU_dict = {
0 : 'Subscribed delivery of erroneous SDUs',
1 : 'No detect',
2 : 'Erroneous SDUs are delivered',
3 : 'Erroneous SDUs are not delivered',
7 : _str_reserved
}
_DeliverOrd_dict = {
0 : 'Subscribed delivery order',
1 : 'With delivery order',
2 : 'Without delivery order',
3 : _str_reserved
}
_TraffClass_dict = {
0 : 'Subscribed traffic class',
1 : 'Conversational class',
2 : 'Streaming class',
3 : 'Interactive class',
4 : 'Background class',
7 : _str_reserved
}
_SignalInd_dict = {
0 : 'Not optimised for signalling',
1 : 'Optimised for signalling'
}
_SourceStats_dict = {
0 : 'unknown',
1 : 'speech'
}
# TODO: build dicts for DL / UL max / guaranteed throughput
class QoS(Envelope):
ENV_SEL_TRANS = False
2017-10-13 19:28:18 +00:00
_GEN = (
Uint('spare', bl=2),
Uint('DelayClass', bl=3, dic=_DelayClass_dict),
Uint('ReliabilityClass', bl=3, dic=_ReliabClass_dict), # 1
Uint('PeakThroughput', bl=4, dic=_PeakTP_dict),
Uint('spare', bl=1),
Uint('PrecedenceClass', bl=3, dic=_PrecedClass_dict), # 2
Uint('spare', bl=3),
Uint('MeanThroughput', bl=5, dic=_MeanTP_dict), # 3
Uint('TrafficClass', bl=3, dic=_TraffClass_dict),
Uint('DeliveryOrder', bl=2, dic=_DeliverOrd_dict),
Uint('ErroneousSDU', bl=3, dic=_ErronSDU_dict), # 4
Uint8('MaxSDUSize'),
Uint8('MaxULBitrate'),
Uint8('MaxDLBitrate'),
Uint('ResidualBER', bl=4),
Uint('SDUErrorRatio', bl=4), # 8
Uint('TransferDelay', bl=6),
Uint('TrafficHandlingPriority', bl=2), # 9
Uint8('GuaranteedULBitrate'),
Uint8('GuaranteedDLBitrate'),
Uint('spare', bl=3),
Uint('SignallingInd', bl=1, dic=_SignalInd_dict),
Uint('SourceStatsDesc', bl=4, dic=_SourceStats_dict), # 12
Uint8('MaxDLBitrateExt', trans=True),
Uint8('GuaranteedDLBitrateExt', trans=True),
Uint8('MaxULBitrateExt', trans=True),
Uint8('GuaranteedULBitrateExt', trans=True),
Uint8('MaxDLBitrateExt2', trans=True),
Uint8('GuaranteedDLBitrateExt2', trans=True),
Uint8('MaxULBitrateExt2', trans=True),
Uint8('GuaranteedULBitrateExt2', trans=True),
2017-10-13 19:28:18 +00:00
)
def set_val(self, vals):
# in case extended values are set, make them non-transparent
if vals is None:
self._set_trans_dlbrext(True)
self._set_trans_ulbrext(True)
self._set_trans_dlbrext2(True)
self._set_trans_ulbrext2(True)
elif isinstance(vals, (tuple, list)):
if len(vals) > 29:
2017-10-13 19:28:18 +00:00
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
self._set_trans_dlbrext2(False)
self._set_trans_ulbrext2(False)
elif len(vals) > 27:
2017-10-13 19:28:18 +00:00
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
self._set_trans_dlbrext2(False)
elif len(vals) > 25:
2017-10-13 19:28:18 +00:00
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
elif len(vals) > 23:
2017-10-13 19:28:18 +00:00
self._set_trans_dlbrext(False)
elif isinstance(vals, dict):
if 'MaxULBitrateExt2' in vals or 'GuaranteedULBitrateExt2' in vals:
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
self._set_trans_dlbrext2(False)
self._set_trans_ulbrext2(False)
elif 'MaxDLBitrateExt2' in vals or 'GuaranteedDLBitrateExt2' in vals:
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
self._set_trans_dlbrext2(False)
elif 'MaxULBitrateExt' in vals or 'GuaranteedULBitrateExt' in vals:
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
elif 'MaxDLBitrateExt' in vals or 'GuaranteedDLBitrateExt' in vals:
self._set_trans_dlbrext(False)
Envelope.set_val(self, vals)
2017-10-13 19:28:18 +00:00
def _set_trans_dlbrext(self, trans):
self[23].set_trans(trans)
self[24].set_trans(trans)
2017-10-13 19:28:18 +00:00
def _set_trans_ulbrext(self, trans):
self[25].set_trans(trans)
self[26].set_trans(trans)
2017-10-13 19:28:18 +00:00
def _set_trans_dlbrext2(self, trans):
self[27].set_trans(trans)
self[28].set_trans(trans)
2017-10-13 19:28:18 +00:00
def _set_trans_ulbrext2(self, trans):
self[29].set_trans(trans)
self[30].set_trans(trans)
2017-10-13 19:28:18 +00:00
def _from_char(self, char):
# in case long-enough buffer is available, make extended fields non-transparent
l = char.len_byte()
if l > 18:
2017-10-13 19:28:18 +00:00
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
self._set_trans_dlbrext2(False)
self._set_trans_ulbrext2(False)
elif l > 16:
2017-10-13 19:28:18 +00:00
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
self._set_trans_dlbrext2(False)
elif l > 14:
2017-10-13 19:28:18 +00:00
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
elif l > 12:
2017-10-13 19:28:18 +00:00
self._set_trans_dlbrext(False)
Envelope._from_char(self, char)
#------------------------------------------------------------------------------#
# Re-attempt indicator
# TS 24.008, 10.5.6.5a
#------------------------------------------------------------------------------#
_EPLMNC_dict = {
0 : 'MS is allowed to re-attempt the procedure in an equivalent PLMN',
1 : 'MS is not allowed to re-attempt the procedure in an equivalent PLMN'
}
_RATC_dict = {
0 : 'MS is allowed to re-attempt the procedure in S1 mode',
1 : 'MS is not allowed to re-attempt the procedure in S1 mode'
}
class ReattemptInd(Envelope):
_GEN = (
Uint('spare', bl=6),
Uint('EPLMNC', bl=1, dic=_EPLMNC_dict),
Uint('RATC', bl=1, dic=_RATC_dict)
2017-10-13 19:28:18 +00:00
)
#------------------------------------------------------------------------------#
# SM Cause
# TS 24.008, 10.5.6.6
#------------------------------------------------------------------------------#
SMCause_dict = {
8 : 'Operator Determined Barring',
24 : 'MBMS bearer capabilities insufficient for the service',
25 : 'LLC or SNDCP failure(A/Gb mode only)',
26 : 'Insufficient resources',
27 : 'Missing or unknown APN',
28 : 'Unknown PDP address or PDP type',
29 : 'User authentication failed',
30 : 'Activation rejected by GGSN, Serving GW or PDN GW',
31 : 'Activation rejected, unspecified',
32 : 'Service option not supported',
33 : 'Requested service option not subscribed',
34 : 'Service option temporarily out of order',
35 : 'NSAPI already used (not sent)',
36 : 'Regular deactivation',
37 : 'QoS not accepted',
38 : 'Network failure',
39 : 'Reactivation requested',
40 : 'Feature not supported',
41 : 'Semantic error in the TFT operation',
42 : 'Syntactical error in the TFT operation',
43 : 'Unknown PDP context',
44 : 'Semantic errors in packet filter(s)',
45 : 'Syntactical errors in packet filter(s)',
46 : 'PDP context without TFT already activated',
47 : 'Multicast group membership time-out',
48 : 'Request rejected, BCM violation',
50 : 'PDP type IPv4 only allowed',
51 : 'PDP type IPv6 only allowed',
52 : 'Single address bearers only allowed',
56 : 'Collision with network initiated request',
60 : 'Bearer handling not supported',
65 : 'Maximum number of PDP contexts reached',
66 : 'Requested APN not supported in current RAT and PLMN combination',
81 : 'Invalid transaction identifier value',
95 : 'Semantically incorrect message',
96 : 'Invalid mandatory information',
97 : 'Message type non-existent or not implemented',
98 : 'Message type not compatible with the protocol state',
99 : 'Information element non-existent or not implemented',
100 : 'Conditional IE error',
101 : 'Message not compatible with the protocol state',
111 : 'Protocol error, unspecified',
112 : 'APN restriction value incompatible with active PDP context',
113 : 'Multiple accesses to a PDN connection not allowed'
}
class SMCause(Uint8):
_dic = SMCause_dict
#------------------------------------------------------------------------------#
# Linked TI
# TS 24.008, 10.5.6.7
#------------------------------------------------------------------------------#
# see transaction identifier in TS 24.007, section 11.2.3.1.3
class TransId(Envelope):
_GEN = (
Uint('Flag', bl=1, dic={0: 'initiator', 1: 'responder'}),
Uint('TIO', bl=3),
Uint('spare', bl=4),
Uint('Ext', val=1, bl=1, trans=True),
Uint('TIE', bl=7, trans=True),
Uint8('TI', trans=True) # virtual field to get the TI value easily
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[5].set_valauto(self._set_ti)
def _set_ti(self):
tio = self[1]()
if tio == 7 and not self[4].get_trans():
return self[4]()
else:
return tio
def set_val(self, vals):
if isinstance(vals, dict) and 'TI' in vals:
ti = vals['TI']
del vals['TI']
if 0 <= ti < 7:
self[1].set_val(ti)
self[3].set_trans(True)
self[4].set_trans(True)
elif ti < 128:
# extended
self[1].set_val(7)
self[3].set_trans(False)
self[4].set_trans(False)
self[4].set_val(ti)
Envelope.set_val(self, vals)
def _from_char(self, char):
if char.len_byte() > 1:
self[3].set_trans(False)
self[4].set_trans(False)
Envelope._from_char(self, char)
#------------------------------------------------------------------------------#
# LLC service access point identifier
# TS 24.008, 10.5.6.9
#------------------------------------------------------------------------------#
_LLC_SAPI_dict = {
0 : 'not assigned',
1 : _str_reserved,
2 : _str_reserved,
4 : _str_reserved,
6 : _str_reserved,
7 : _str_reserved,
8 : _str_reserved,
10: _str_reserved,
11: _str_reserved,
12: _str_reserved,
13: _str_reserved,
14: _str_reserved,
15: _str_reserved
}
class LLC_SAPI(Envelope):
_GEN = (
Uint('spare', bl=4),
Uint('Value', bl=4, dic=_LLC_SAPI_dict)
)
#------------------------------------------------------------------------------#
# Tear Down Indicator
# TS 24.008, 10.5.6.10
#------------------------------------------------------------------------------#
class TearDownInd(Envelope):
_GEN = (
Uint('spare', bl=3),
Uint('Value', bl=1, dic={0:'teardown not requested', 1:'teardown requested'}),
)
#------------------------------------------------------------------------------#
# Packet Flow Identifier
# TS 24.008, 10.5.6.11
#------------------------------------------------------------------------------#
_PktFlowId_dict = {
0: 'Best Effort',
1: 'Signaling',
2: 'SMS',
3: 'TOM8'
}
class PacketFlowId(Envelope):
_GEN = (
Uint('spare', bl=1),
Uint('Value', bl=7, dic=_PktFlowId_dict)
)
#------------------------------------------------------------------------------#
# Traffic Flow Template
# TS 24.008, 10.5.6.12
#------------------------------------------------------------------------------#
_TFTOpcode_dict = {
0 : 'Ignore this IE',
1 : 'Create new TFT',
2 : 'Delete existing TFT',
3 : 'Add packet filters to existing TFT',
4 : 'Replace packet filters in existing TFT',
5 : 'Delete packet filters from existing TFT',
6 : 'No TFT operation',
7 : _str_reserved
}
_PktFilterDir_dict = {
0 : 'pre Rel-7 TFT filter',
1 : 'downlink only',
2 : 'uplink only',
3 : 'bidirectional'
}
_PktFilterCompType_dict = {
16 : 'IPv4 remote address type',
17 : 'IPv4 local address type ',
32 : 'IPv6 remote address type',
33 : 'IPv6 remote address/prefix length type',
35 : 'IPv6 local address/prefix length type',
48 : 'Protocol identifier/Next header type',
64 : 'Single local port type',
65 : 'Local port range type',
80 : 'Single remote port type',
81 : 'Remote port range type',
96 : 'Security parameter index type',
112 : 'Type of service/Traffic class type',
128 : 'Flow label type'
}
# if TFT opcode == 0, E == 0 and no pkt filters must be there
# if TFT opcode == 5, only pkt filters' id are provided in the pkt filters list
# TFTPktFilter content if made of a sequence of components
class TFTPktFilterId(Envelope):
_GEN = (
Uint('spare', bl=4),
Uint('Id', bl=4)
)
2019-12-02 17:39:42 +00:00
class _CompIPv4(Envelope):
_GEN = (
IPAddr('Address', bl=32),
Buf('Netmask', bl=32, rep=REPR_HEX)
)
2019-12-02 17:39:42 +00:00
class _CompIPv6(Envelope):
_GEN = (
IPAddr('Address', bl=128),
Buf('Netmask', bl=128, rep=REPR_HEX)
)
2019-12-02 17:39:42 +00:00
class _CompIPv6Pref(Envelope):
_GEN = (
IPAddr('Address', bl=128),
Uint8('PrefixLen')
)
2019-12-02 17:39:42 +00:00
class _CompPortRange(Envelope):
_GEN = (
Uint16('PortLo'),
Uint16('PortHi')
)
2019-12-02 17:39:42 +00:00
class _CompTrafficClass(Envelope):
_GEN = (
Uint8('Class'),
Uint8('Mask')
)
2019-12-02 17:39:42 +00:00
class TFTPktFilterComp(Envelope):
_GEN = (
Uint8('Type', dic=_PktFilterCompType_dict),
Alt('Value', GEN={
16 : _CompIPv4('IPv4'),
17 : _CompIPv4('IPv4'),
32 : _CompIPv6('IPv6'),
33 : _CompIPv6Pref('IPv6Pref'),
35 : _CompIPv6Pref('IPv6Pref'),
48 : Uint8('ProtId'),
64 : Uint16('Port'),
65 : _CompPortRange('PortRange'),
80 : Uint16('Port'),
81 : _CompPortRange('PortRange'),
96 : Uint32('SPI'),
112 : _CompTrafficClass('TrafficClass'),
128 : Uint24('FlowLabel')
},
DEFAULT=Buf('unk', val=b'', rep=REPR_HEX),
sel=lambda self: self.get_env()['Type'].get_val())
)
class TFTPktFilter(Envelope):
_GEN = (
Uint('spare', bl=2),
Uint('Dir', bl=2, dic=_PktFilterDir_dict),
Uint('Id', bl=4),
Uint8('Precedence'),
Uint8('Len'),
Sequence('Cont', GEN=TFTPktFilterComp())
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self['Len'].set_valauto(lambda: self['Cont'].get_len())
self['Cont'].set_blauto(lambda: self['Len'].get_val()<<3)
2019-12-02 17:39:42 +00:00
class TFTParameter(Envelope):
_GEN = (
Uint8('Id'),
Uint8('Len'),
2019-12-02 17:39:42 +00:00
Buf('Cont', val=b'', rep=REPR_HEX)
)
2019-12-02 17:39:42 +00:00
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_valauto(self[2].get_len)
self[2].set_blauto(lambda: 8*self[1]())
2019-12-02 17:39:42 +00:00
class TFT(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint('Opcode', bl=3, dic=_TFTOpcode_dict),
Uint('E', bl=1, dic={0: 'no parameters list', 1: 'parameters list included'}),
Uint('NumPktFilters', bl=4),
Sequence('PktFilterIds', GEN=TFTPktFilterId()),
Sequence('PktFilters', GEN=TFTPktFilter()),
Sequence('Parameters', GEN=TFTParameter())
)
2019-12-02 17:39:42 +00:00
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[2].set_valauto(lambda: self[3].get_num() if \
self[0]() == 5 else self[4].get_num())
self[3].set_transauto(lambda: self[0]() != 5)
self[3].set_numauto(lambda: self[2]())
self[4].set_transauto(lambda: self[0]() == 5)
self[4].set_numauto(lambda: self[2]())
self[5].set_transauto(lambda: self[1]() == 0)
# there is no num automation of the Parameters
# hence, all remaining buffer will be consumed when calling _from_char()
#------------------------------------------------------------------------------#
# Temporary mobile group identity (TMGI)
# TS 24.008, 10.5.6.13
#------------------------------------------------------------------------------#
class TMGI(Envelope):
_GEN = (
Uint24('MBMSServID', rep=REPR_HEX),
PLMN()
)
def set_val(self, vals):
if isinstance(vals, (tuple, list)) and len(vals) == 1:
self[1].set_trans(True)
elif isinstance(vals, dict) and 'PLMN' not in vals:
self[1].set_trans(True)
Envelope.set_val(self, vals)
def _from_char(self, char):
if char.len_bit() < 48:
self[1].set_trans(True)
Envelope._from_char(self, char)
2019-11-08 15:11:02 +00:00
#------------------------------------------------------------------------------#
# MBMS bearer capabilities
# TS 24.008, 10.5.6.14
#------------------------------------------------------------------------------#
class MBMSBearerCap(Envelope):
_GEN = (
Uint8('MaxDLBitrate'),
Uint8('MaxDLBitrateExt')
)
#------------------------------------------------------------------------------#
# Enhanced network service access point identifier
# TS 24.008, 10.5.6.16
#------------------------------------------------------------------------------#
_ENSAPI_dict = {0xff: _str_reserved}
for i in range(0, 0x7f):
_ENSAPI_dict[i] = _str_reserved
for i in range(0x80, 0xfe):
_ENSAPI_dict[i] = 'NSAPI_%i_MBMS' % i
class ENSAPI(Uint8):
_dic = _ENSAPI_dict
#------------------------------------------------------------------------------#
# Request type
# TS 24.008, 10.5.6.17
#------------------------------------------------------------------------------#
RequestType_dict = {
1 : 'Initial request',
2 : 'Handover',
3 : 'Unused. Interpreted as initial request',
4 : 'Emergency',
}
#------------------------------------------------------------------------------#
# Notification indicator
# TS 24.008, 10.5.6.18
#------------------------------------------------------------------------------#
class NotificationInd(Uint8):
_dic = {0:'SRVCC handover cancelled, IMS session re-establishment required'}
#------------------------------------------------------------------------------#
# Connectivity type
# TS 24.008, 10.5.6.19
#------------------------------------------------------------------------------#
ConnectivityType_dict = {
0 : 'The PDN connection type is not indicated',
1 : 'The PDN connection is considered a LIPA PDN connection'
}
#------------------------------------------------------------------------------#
# WLAN offload acceptability
# TS 24.008, 10.5.6.20
#------------------------------------------------------------------------------#
_UTRANOffAcc_dict = {
0 : 'Offloading the traffic of the PDN connection via a WLAN when in Iu mode is not acceptable',
1 : 'Offloading the traffic of the PDN connection via a WLAN when in Iu mode is acceptable'
}
_EUTRANOffAcc_dict = {
0 : 'Offloading the traffic of the PDN connection via a WLAN when in S1 mode is not acceptable',
1 : 'Offloading the traffic of the PDN connection via a WLAN when in S1 mode is acceptable'
}
class WLANOffloadAccept(Envelope):
_GEN = (
Uint('spare', bl=2),
Uint('UTRANOffloadAccept', bl=1, dic=_UTRANOffAcc_dict),
Uint('EUTRANOffloadAccept', bl=1, dic=_EUTRANOffAcc_dict)
)
2019-11-08 15:11:02 +00:00
#------------------------------------------------------------------------------#
# NBIFOM container
# TS 24.008, 10.5.6.21
#------------------------------------------------------------------------------#
_NBIFOMParamID_dict = {
0 : 'Not assigned',
1 : 'NBIFOM mode',
2 : 'NBIFOM default access',
3 : 'NBIFOM status',
4 : 'NBIFOM routing rules',
5 : 'NBIFOM IP flow mapping',
6 : 'NBIFOM RAN rules handling',
7 : 'NBIFOM Access stratum status',
8 : 'NBIFOM access usability indication',
}
class NBIFOMParameter(Envelope):
_GEN = (
Uint8('ParamID', dic=_NBIFOMParamID_dict),
Uint8('ParamLen'),
Buf('Param', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_valauto(lambda: self[2].get_len())
self[2].set_blauto(lambda: self[1].get_val()<<3)
class NBIFOMContainer(Sequence):
_GEN = NBIFOMParameter()
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# PDP Context Status
# TS 24.008, 10.5.7.1
#------------------------------------------------------------------------------#
_PDPCtxtStat_dict = {
0 : 'PDP-INACTIVE',
1 : 'PDP-ACTIVE'
}
class PDPCtxtStat(Envelope):
_GEN = (
Uint('NSAPI_7', bl=1, dic=_PDPCtxtStat_dict),
Uint('NSAPI_6', bl=1, dic=_PDPCtxtStat_dict),
Uint('NSAPI_5', bl=1, dic=_PDPCtxtStat_dict),
Uint('NSAPI_4', bl=1),
Uint('NSAPI_3', bl=1),
Uint('NSAPI_2', bl=1),
Uint('NSAPI_1', bl=1),
Uint('NSAPI_0', bl=1),
Uint('NSAPI_15', bl=1, dic=_PDPCtxtStat_dict),
Uint('NSAPI_14', bl=1, dic=_PDPCtxtStat_dict),
Uint('NSAPI_13', bl=1, dic=_PDPCtxtStat_dict),
Uint('NSAPI_12', bl=1, dic=_PDPCtxtStat_dict),
Uint('NSAPI_11', bl=1, dic=_PDPCtxtStat_dict),
Uint('NSAPI_10', bl=1, dic=_PDPCtxtStat_dict),
Uint('NSAPI_9', bl=1, dic=_PDPCtxtStat_dict),
Uint('NSAPI_8', bl=1, dic=_PDPCtxtStat_dict)
2017-07-04 21:12:41 +00:00
)
2017-10-13 19:28:18 +00:00
#------------------------------------------------------------------------------#
# Radio Priority
# TS 24.008, 10.5.7.2 and 10.5.7.5
#------------------------------------------------------------------------------#
RadioPrio_dict = {
1 : 'priority level 1 (highest)',
2 : 'priority level 2',
3 : 'priority level 3',
4 : 'priority level 4 (lowest)'
}
class RadioPriority(Envelope):
_GEN = (
Uint('spare', bl=1),
Uint('Value', bl=3, dic=RadioPrio_dict)
)
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# GPRS Timer
# TS 24.008, 10.5.7.3
#------------------------------------------------------------------------------#
_GPRSTimerUnit_dict = _MMTimerUnit_dict
class GPRSTimer(Envelope):
_GEN = (
Uint('Unit', bl=3, dic=_GPRSTimerUnit_dict),
Uint('Value', bl=5)
2017-07-04 21:12:41 +00:00
)
def get_time(self):
"""returns the timer set in seconds
"""
unit, val = self.get_val()
if unit == 0:
return val*2
elif unit == 1:
return val*60
elif unit == 2:
return val*360
else:
return 0
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# GPRS Timer 3
# TS 24.008, 10.5.7.4a
#------------------------------------------------------------------------------#
_GPRSTimer3Unit_dict = {
0 : '10 min',
1 : '1 hour',
2 : '10 hours',
3 : '2 sec',
4 : '30 sec',
5 : '1 min',
6 : '320 hours',
7 : 'timer deactivated'
}
_GPRSTimer3Unit_mult = {
0 : 600,
1 : 3600,
2 : 36000,
3 : 2,
4 : 30,
5 : 60,
6 : 1152000,
7 : 0
}
2017-07-04 21:12:41 +00:00
class GPRSTimer3(Envelope):
_GEN = (
Uint('Unit', bl=3, dic=_GPRSTimer3Unit_dict),
Uint('Value', bl=5)
2017-07-04 21:12:41 +00:00
)
def get_time(self):
"""returns the timer set in seconds
"""
unit, val = self.get_val()
return val * _GPRSTimer3Unit_mult[unit]
2017-07-04 21:12:41 +00:00
#------------------------------------------------------------------------------#
# MBMS context status
# TS 24.008, 10.5.7.6
#------------------------------------------------------------------------------#
class MBMSCtxtStat(Envelope):
ENV_SEL_TRANS = False
#_GEN = () # built at __init__()
def __init__(self, *args, **kw):
GEN = []
for i in range(16):
for j in range(7, -1, -1):
GEN.append( Uint('NSAPI_%i' % (128+8*i+j), bl=1, dic=_PDPCtxtStat_dict) )
2017-07-04 21:12:41 +00:00
kw['GEN'] = tuple(GEN)
Envelope.__init__(self, *args, **kw)
def _from_char(self, char):
l = char.len_bit()
self.enable_upto(l-1)
self.disable_from(l-1)
Envelope._from_char(self, char)
def disable_from(self, ind):
"""disables all elements from index `ind' excluded (integer -bit offset-
or element name)
"""
if isinstance(ind, str_types) and ind in self._by_name:
ind = self._by_name.index(ind)
[e.set_trans(True) for e in self._content[ind:]]
def enable_upto(self, ind):
"""enables all elements up to index `ind' included (integer -bit offset-
or element name)
"""
if isinstance(ind, str_types) and ind in self._by_name:
ind = 1 + self._by_name.index(ind)
[e.set_trans(False) for e in self._content[:ind]]
#------------------------------------------------------------------------------#
# Uplinlk data status
# TS 24.008, 10.5.7.7
#------------------------------------------------------------------------------#
_ULDataStat_dict = {
1 : 'UL data pending'
}
class ULDataStat(Envelope):
_GEN = (
Uint('NSAPI_7', bl=1, dic=_ULDataStat_dict),
Uint('NSAPI_6', bl=1, dic=_ULDataStat_dict),
Uint('NSAPI_5', bl=1, dic=_ULDataStat_dict),
Uint('spare', bl=5),
Uint('NSAPI_15', bl=1, dic=_ULDataStat_dict),
Uint('NSAPI_14', bl=1, dic=_ULDataStat_dict),
Uint('NSAPI_13', bl=1, dic=_ULDataStat_dict),
Uint('NSAPI_12', bl=1, dic=_ULDataStat_dict),
Uint('NSAPI_11', bl=1, dic=_ULDataStat_dict),
Uint('NSAPI_10', bl=1, dic=_ULDataStat_dict),
Uint('NSAPI_9', bl=1, dic=_ULDataStat_dict),
Uint('NSAPI_8', bl=1, dic=_ULDataStat_dict)
2017-07-04 21:12:41 +00:00
)
#------------------------------------------------------------------------------#
# Device Properties
# TS 24.008, 10.5.7.8
#------------------------------------------------------------------------------#
class DeviceProp(Envelope):
_GEN = (
Uint('spare', bl=3),
Uint('LowPriority', bl=1)
2017-07-04 21:12:41 +00:00
)