pycrate/pycrate_mobile/TS24008_IE.py

3836 lines
116 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: UTF-8 -*-
#/**
# * Software Name : pycrate
# * Version : 0.4
# *
# * Copyright 2017. Benoit Michau. ANSSI.
# *
# * 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,
# * 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
# *
# *--------------------------------------------------------
# * 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
from time import struct_time
from socket import inet_ntop, inet_pton, AF_INET, AF_INET6
from pycrate_core.utils import *
from pycrate_core.elt import (
Envelope, Array, Sequence, Alt,
REPR_RAW, REPR_HEX, REPR_BIN, REPR_HD, REPR_HUM
)
from pycrate_core.base import *
from pycrate_core.repr import *
from pycrate_core.charpy import Charpy
from .MCC_MNC import MNC_dict
from .PPP import LCP, LCPDataConf, NCP, NCPDataConf, PAP, CHAP
from .TS23038 import *
from .TS24007 import ProtDisc_dict
#------------------------------------------------------------------------------#
# 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
#------------------------------------------------------------------------------#
# str shortcuts
#------------------------------------------------------------------------------#
_str_reserved = 'reserved'
#------------------------------------------------------------------------------#
# 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):
"""Subclass of pycrate_core.base.Buf object
with additional encode() and decode() capabilities in order to handle
BCD encoding
"""
_rep = REPR_HUM
_dic = None # dict lookup not supported for repr()
# 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
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'])
# 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)))
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}'\
.format(self._name, bcd)))
if len(ret) % 2:
ret.append( self._filler )
#
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
"""
_bl = 24 # 3 bytes
_rep = REPR_HUM
_dic = MNC_dict
# default to PLMN 001.01
DEFAULT_VAL = b'\x00\xf1\x10'
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'] )
# 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)))
def decode(self):
"""returns the encoded string of digits
"""
plmn = []
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')
else:
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')
def encode(self, plmn=u'00101'):
"""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)))
if len(plmn) == 5:
plmn += 'F'
elif len(plmn) != 6:
raise(PycrateErr('{0}: invalid PLMN string to encode, {1!r}'\
.format(self._name, plmn)))
# 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])))
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])
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
#------------------------------------------------------------------------------#
# 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)
)
encode = Envelope.set_val
def decode(self):
return (self[0].decode(), self[1].get_val())
#------------------------------------------------------------------------------#
# 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)
)
class IDTemp(Envelope):
_GEN = (
Uint('Digit1', val=0xF, bl=4, rep=REPR_HEX),
Uint('Odd', bl=1),
Uint('Type', val=IDTYPE_TMSI, bl=3, dic=IDType_dict),
Uint32('TMSI', rep=REPR_HEX)
)
class IDDigit(Envelope):
_GEN = (
Uint('Digit1', val=0xF, bl=4, rep=REPR_HEX),
Uint('Odd', bl=1),
Uint('Type', val=IDTYPE_IMSI, bl=3, dic=IDType_dict),
Buf('Digits', val=b'', rep=REPR_HEX)
)
class IDGroup(Envelope):
ENV_SEL_TRANS = False
_GEN = (
Uint('spare', bl=2),
Uint('MBMSSessInd', bl=1),
Uint('MCCMNCInd', bl=1),
Uint('Odd', bl=1),
Uint('Type', val=IDTYPE_TMGI, dic=IDType_dict),
Uint24('MBMSServID', rep=REPR_HEX),
PLMN(),
Uint8('MBMSSessID')
)
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()
def set_val(self, vals):
if isinstance(vals, dict) and 'type' in vals and 'ident' in vals:
self.encode(vals['type'], vals['ident'])
else:
Envelope.set_val(self, vals)
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()
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()
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()
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()
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()
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()
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()
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
#
else:
if not hasattr(self, '_IDNone'):
self._IDNone = IDNone()
log('WNG: ID, type unhandled, %i' % type)
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),
Uint('RevLevel', val=2, bl=2, dic=_RevLevel_dict),
Uint('EarlyCmCap', bl=1),
Uint('NoA51', bl=1),
Uint('RFClass', bl=3, dic=_RFClass_dict)
)
#------------------------------------------------------------------------------#
# 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),
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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# Priority Level
# TS 24.008, 10.5.1.11
#------------------------------------------------------------------------------#
class PriorityLevel(Envelope):
_GEN = (
Uint('spare', bl=1),
Uint('CallPriority', bl=3, dic=PriorityLevel_dict)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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',
3 : _str_reserved
}
class LocUpdateType(Envelope):
_GEN = (
Uint('FollowOnReq', bl=1),
Uint('spare', bl=1),
Uint('Type', bl=2, dic=_LocUpdType_dict)
)
#------------------------------------------------------------------------------#
# 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),
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
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:
# WNG: this will certainly fail in Python2
return self[4].get_val().decode('utf-16-be')
else:
return None
#------------------------------------------------------------------------------#
# Reject Cause
# TS 24.008, section 10.5.3.6
#------------------------------------------------------------------------------#
_RejectCause_dict = {
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
#------------------------------------------------------------------------------#
# 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
#------------------------------------------------------------------------------#
# 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
class TimeZoneTime(Envelope):
YEAR_BASE = 2000
_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
"""
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):
_GEN = (
Uint('spare', bl=6),
Uint('Value', bl=2, dic=_DLSavingTime)
)
#------------------------------------------------------------------------------#
# Supported codec list
# TS 24.008, section 10.5.4.32
#------------------------------------------------------------------------------#
class SuppCodec(Envelope):
_GEN = (
Uint8('SysID'),
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)
)
#------------------------------------------------------------------------------#
# 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')
)
def __init__(self, *args, **kw):
Envelope.__init__(self, *args, **kw)
self[2]._name = 'ServiceCat' # otherwise, it says 'slice'
self._by_name[2] = 'ServiceCat' # otherwise, it says 'slice'
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)
)
#------------------------------------------------------------------------------#
# 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)
)
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
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# Called party BCD number
# TS 24.008, 10.5.4.7
#------------------------------------------------------------------------------#
# generic BCD number
# BCDType and NumPlan are extended with values from TS 23.040, section 9.1.2.15
_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
}
_NumPlan_dict = {
0 : 'unknown',
1 : 'ISDN / telephony numbering plan (E.164 / E.163)',
2 : 'generic numbering plan',
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
8 : 'national numbering plan',
9 : 'private numbering plan',
10: 'ERMES numbering plan', # only for SMS
11: 'reserved for CTS',
15: _str_reserved
}
_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)
)
class BCDNumber(Envelope):
ENV_SEL_TRANS = False
_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'),
BufBCD('Num')
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[3].set_transauto(lambda: self[0]() == 1)
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
#------------------------------------------------------------------------------#
# Calling party BCD number
# TS 24.008, 10.5.4.9
#------------------------------------------------------------------------------#
class CallingPartyBCDNumber(BCDNumber):
pass
#------------------------------------------------------------------------------#
# 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'
}
_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'
}
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):
_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')
)
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()
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
class DetachTypeMT(Envelope):
_GEN = (
Uint('spare', bl=1),
Uint('Type', bl=3, dic=_DetachTypeMT_dict)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# Force to standby
# TS 24.008, 10.5.5.7
#------------------------------------------------------------------------------#
ForceStdby_dict = {
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)
)
#------------------------------------------------------------------------------#
# 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'})
)
#------------------------------------------------------------------------------#
# 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
#------------------------------------------------------------------------------#
# 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)
)
encode = Envelope.set_val
def decode(self):
return (self[0].decode(), self[1].get_val(), self[2].get_val())
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# Additional network feature support
# TS 24.008, 10.5.5.23A
#------------------------------------------------------------------------------#
class AddNetFeatSupp(Envelope):
_GEN = (
Uint('spare', bl=7),
Uint('GPRS_SMS', bl=1)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# Extended DRX parameters
# TS 24.008, 10.5.5.32
#------------------------------------------------------------------------------#
class ExtDRXParam(Envelope):
_GEN = (
Uint('PTX', bl=4),
Uint('eDRX', bl=4)
)
#------------------------------------------------------------------------------#
# User-Plane integrity indicator
# TS 24.008, 10.5.5.34
#------------------------------------------------------------------------------#
class UPIntegrityInd(Envelope):
_GEN = (
Uint('spare', bl=3),
Uint('Value', bl=1)
)
#------------------------------------------------------------------------------#
# 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')
)
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())
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()
#------------------------------------------------------------------------------#
# 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),
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):
# when set to True, will decode the content into NCP / LCP / PAP / CHAP
DECODE_INNER = True
_ContLUT = {
0x8021 : NCP,
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())
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:
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())
)
#------------------------------------------------------------------------------#
# 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
0 : 'reserved',
1 : 'PPP',
# IETF allocated
33 : 'IPv4',
87 : 'IPv6',
141 : 'IPv4v6',
}
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
)
#------------------------------------------------------------------------------#
# 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
_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),
)
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:
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
self._set_trans_dlbrext2(False)
self._set_trans_ulbrext2(False)
elif len(vals) > 27:
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
self._set_trans_dlbrext2(False)
elif len(vals) > 25:
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
elif len(vals) > 23:
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)
def _set_trans_dlbrext(self, trans):
self[23].set_trans(trans)
self[24].set_trans(trans)
def _set_trans_ulbrext(self, trans):
self[25].set_trans(trans)
self[26].set_trans(trans)
def _set_trans_dlbrext2(self, trans):
self[27].set_trans(trans)
self[28].set_trans(trans)
def _set_trans_ulbrext2(self, trans):
self[29].set_trans(trans)
self[30].set_trans(trans)
def _from_char(self, char):
# in case long-enough buffer is available, make extended fields non-transparent
l = char.len_byte()
if l > 18:
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
self._set_trans_dlbrext2(False)
self._set_trans_ulbrext2(False)
elif l > 16:
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
self._set_trans_dlbrext2(False)
elif l > 14:
self._set_trans_dlbrext(False)
self._set_trans_ulbrext(False)
elif l > 12:
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)
)
#------------------------------------------------------------------------------#
# 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)
)
class _CompIPv4(Envelope):
_GEN = (
IPAddr('Address', bl=32),
Buf('Netmask', bl=32, rep=REPR_HEX)
)
class _CompIPv6(Envelope):
_GEN = (
IPAddr('Address', bl=128),
Buf('Netmask', bl=128, rep=REPR_HEX)
)
class _CompIPv6Pref(Envelope):
_GEN = (
IPAddr('Address', bl=128),
Uint8('PrefixLen')
)
class _CompPortRange(Envelope):
_GEN = (
Uint16('PortLo'),
Uint16('PortHi')
)
class _CompTrafficClass(Envelope):
_GEN = (
Uint8('Class'),
Uint8('Mask')
)
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)
class TFTParameter(Envelope):
_GEN = (
Uint8('Id'),
Uint8('Len'),
Buf('Cont', val=b'', rep=REPR_HEX)
)
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]())
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())
)
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)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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()
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
#------------------------------------------------------------------------------#
# 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)
)
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
#------------------------------------------------------------------------------#
# 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
}
class GPRSTimer3(Envelope):
_GEN = (
Uint('Unit', bl=3, dic=_GPRSTimer3Unit_dict),
Uint('Value', bl=5)
)
def get_time(self):
"""returns the timer set in seconds
"""
unit, val = self.get_val()
return val * _GPRSTimer3Unit_mult[unit]
#------------------------------------------------------------------------------#
# 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) )
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)
)
#------------------------------------------------------------------------------#
# Device Properties
# TS 24.008, 10.5.7.8
#------------------------------------------------------------------------------#
class DeviceProp(Envelope):
_GEN = (
Uint('spare', bl=3),
Uint('LowPriority', bl=1)
)