From 842fbdb15d50588bc427e9ed75ad720e578efb38 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 27 Dec 2023 17:06:58 +0100 Subject: [PATCH] add PlmnAdapter for decoding PLMN bcd-strings like 262f01 to 262-01 The human representation of a PLMN is usually MCC-MNC like 262-01 or 262-001. Let's add a PlmnAdapter for use within construct, so we can properly decode that. Change-Id: I96f276e6dcdb54a5a3d2bcde5ee6dbaf981ed789 --- pySim/cat.py | 6 +++--- pySim/construct.py | 17 +++++++++++++++++ pySim/gsm_r.py | 6 +++--- pySim/ts_31_102.py | 14 +++++++------- pySim/ts_51_011.py | 6 +++--- .../pySim-trace_test_gsmtap.pcapng.ok | 2 +- 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/pySim/cat.py b/pySim/cat.py index e98ce4af..5925bf39 100644 --- a/pySim/cat.py +++ b/pySim/cat.py @@ -22,7 +22,7 @@ from bidict import bidict from typing import List from pySim.utils import b2h, h2b, dec_xplmn_w_act from pySim.tlv import TLV_IE, COMPR_TLV_IE, BER_TLV_IE, TLV_IE_Collection -from pySim.construct import BcdAdapter, HexAdapter, GsmStringAdapter, TonNpi +from pySim.construct import PlmnAdapter, BcdAdapter, HexAdapter, GsmStringAdapter, TonNpi from construct import Int8ub, Int16ub, Byte, Bytes, Bit, Flag, BitsInteger from construct import Struct, Enum, Tell, BitStruct, this, Padding, RepeatUntil from construct import GreedyBytes, Switch, GreedyRange, FlagsEnum @@ -597,7 +597,7 @@ class ContactlessFunctionalityState(COMPR_TLV_IE, tag=0xD4): # TS 31.111 Section 8.91 class RoutingAreaIdentification(COMPR_TLV_IE, tag=0xF3): - _construct = Struct('mcc_mnc'/BcdAdapter(Bytes(3)), + _construct = Struct('mcc_mnc'/PlmnAdapter(Bytes(3)), 'lac'/HexAdapter(Bytes(2)), 'rac'/Int8ub) @@ -645,7 +645,7 @@ class GeographicalLocationParameters(COMPR_TLV_IE, tag=0xF6): # TS 31.111 Section 8.97 class PlmnList(COMPR_TLV_IE, tag=0xF9): - _construct = GreedyRange('mcc_mnc'/HexAdapter(Bytes(3))) + _construct = GreedyRange('mcc_mnc'/PlmnAdapter(Bytes(3))) # TS 102 223 Section 8.98 class EcatSequenceNumber(COMPR_TLV_IE, tag=0xA1): diff --git a/pySim/construct.py b/pySim/construct.py index cef9557e..f78adfe8 100644 --- a/pySim/construct.py +++ b/pySim/construct.py @@ -58,6 +58,23 @@ class BcdAdapter(Adapter): def _encode(self, obj, context, path): return h2b(swap_nibbles(obj)) +class PlmnAdapter(BcdAdapter): + """convert a bytes(3) type to BCD string like 262-02 or 262-002.""" + def _decode(self, obj, context, path): + bcd = super()._decode(obj, context, path) + if bcd[3] == 'f': + return '-'.join([bcd[:3], bcd[4:]]) + else: + return '-'.join([bcd[:3], bcd[3:]]) + + def _encode(self, obj, context, path): + l = obj.split('-') + if len(l[1]) == 2: + bcd = l[0] + 'f' + l[1] + else: + bcd = l[0] + l[1] + return super()._encode(bcd, context, path) + class InvertAdapter(Adapter): """inverse logic (false->true, true->false).""" @staticmethod diff --git a/pySim/gsm_r.py b/pySim/gsm_r.py index cd111d68..db7819c4 100644 --- a/pySim/gsm_r.py +++ b/pySim/gsm_r.py @@ -202,12 +202,12 @@ class EF_Shunting(TransparentEF): class EF_GsmrPLMN(LinFixedEF): """Section 7.7""" _test_de_encode = [ - ( "22f860f86f8d6f8e01", { "plmn": "228f06", "class_of_network": { + ( "22f860f86f8d6f8e01", { "plmn": "228-06", "class_of_network": { "supported": { "vbs": True, "vgcs": True, "emlpp": True, "fn": True, "eirene": True }, "preference": 0 }, "ic_incoming_ref_tbl": "6f8d", "outgoing_ref_tbl": "6f8e", "ic_table_ref": "01" } ), - ( "22f810416f8d6f8e02", { "plmn": "228f01", "class_of_network": { + ( "22f810416f8d6f8e02", { "plmn": "228-01", "class_of_network": { "supported": { "vbs": False, "vgcs": False, "emlpp": False, "fn": True, "eirene": False }, "preference": 1 }, "ic_incoming_ref_tbl": "6f8d", "outgoing_ref_tbl": "6f8e", @@ -216,7 +216,7 @@ class EF_GsmrPLMN(LinFixedEF): def __init__(self): super().__init__(fid='6ff5', sfid=None, name='EF.GsmrPLMN', desc='GSM-R network selection', rec_len=(9, 9)) - self._construct = Struct('plmn'/BcdAdapter(Bytes(3)), + self._construct = Struct('plmn'/PlmnAdapter(Bytes(3)), 'class_of_network'/BitStruct('supported'/FlagsEnum(BitsInteger(5), vbs=1, vgcs=2, emlpp=4, fn=8, eirene=16), 'preference'/BitsInteger(3)), 'ic_incoming_ref_tbl'/HexAdapter(Bytes(2)), diff --git a/pySim/ts_31_102.py b/pySim/ts_31_102.py index b5faf083..16526c22 100644 --- a/pySim/ts_31_102.py +++ b/pySim/ts_31_102.py @@ -566,14 +566,14 @@ class EF_ECC(LinFixedEF): class EF_LOCI(TransparentEF): _test_de_encode = [ ( '47d1264a62f21037211e00', - { "tmsi": "47d1264a", "lai": { "mcc_mnc": "262f01", "lac": "3721" }, + { "tmsi": "47d1264a", "lai": { "mcc_mnc": "262-01", "lac": "3721" }, "rfu": 30, "lu_status": 0 } ), ( 'ffffffff62f2200000ff01', - {"tmsi": "ffffffff", "lai": {"mcc_mnc": "262f02", "lac": "0000"}, "rfu": 255, "lu_status": 1} ), + {"tmsi": "ffffffff", "lai": {"mcc_mnc": "262-02", "lac": "0000"}, "rfu": 255, "lu_status": 1} ), ] def __init__(self, fid='6f7e', sfid=0x0b, name='EF.LOCI', desc='Location information', size=(11, 11)): super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) - Lai = Struct('mcc_mnc'/BcdAdapter(Bytes(3)), 'lac'/HexAdapter(Bytes(2))) + Lai = Struct('mcc_mnc'/PlmnAdapter(Bytes(3)), 'lac'/HexAdapter(Bytes(2))) self._construct = Struct('tmsi'/HexAdapter(Bytes(4)), 'lai'/Lai, 'rfu'/Int8ub, 'lu_status'/Int8ub) # TS 31.102 Section 4.2.18 @@ -905,11 +905,11 @@ class EF_ePDGId(TransparentEF): # TS 31.102 Section 4.2.104 class EF_ePDGSelection(TransparentEF): _test_de_encode = [ - ( '80060001f1000100', {'e_pdg_selection': [{'plmn': '00101f', 'epdg_priority': 1, 'epdg_fqdn_format': 'operator_identified' }] }), - ( '800600011000a001', {'e_pdg_selection': [{'plmn': '001001', 'epdg_priority': 160, 'epdg_fqdn_format': 'location_based' }] }), + ( '800600f110000100', {'e_pdg_selection': [{'plmn': '001-01', 'epdg_priority': 1, 'epdg_fqdn_format': 'operator_identified' }] }), + ( '800600011000a001', {'e_pdg_selection': [{'plmn': '001-001', 'epdg_priority': 160, 'epdg_fqdn_format': 'location_based' }] }), ] class ePDGSelection(BER_TLV_IE, tag=0x80): - _construct = GreedyRange(Struct('plmn'/BcdAdapter(Bytes(3)), + _construct = GreedyRange(Struct('plmn'/PlmnAdapter(Bytes(3)), 'epdg_priority'/Int16ub, 'epdg_fqdn_format'/Enum(Int8ub, operator_identified=0, location_based=1))) @@ -980,7 +980,7 @@ class EF_UAC_AIC(TransparentEF): class EF_OPL5G(LinFixedEF): def __init__(self, fid='4f08', sfid=0x08, name='EF.OPL5G', desc='5GS Operator PLMN List', **kwargs): super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=(10, None), **kwargs) - Tai = Struct('mcc_mnc'/BcdAdapter(Bytes(3)), 'tac_min'/HexAdapter(Bytes(3)), + Tai = Struct('mcc_mnc'/PlmnAdapter(Bytes(3)), 'tac_min'/HexAdapter(Bytes(3)), 'tac_max'/HexAdapter(Bytes(3))) self._construct = Struct('tai'/Tai, 'pnn_record_id'/Int8ub) diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py index 0a803b92..422b35e7 100644 --- a/pySim/ts_51_011.py +++ b/pySim/ts_51_011.py @@ -845,11 +845,11 @@ class EF_PNN(LinFixedEF): class EF_OPL(LinFixedEF): _test_de_encode = [ ( '62f2100000fffe01', - { "lai": { "mcc_mnc": "262f01", "lac_min": "0000", "lac_max": "fffe" }, "pnn_record_id": 1 } ), + { "lai": { "mcc_mnc": "262-01", "lac_min": "0000", "lac_max": "fffe" }, "pnn_record_id": 1 } ), ] def __init__(self, fid='6fc6', sfid=None, name='EF.OPL', rec_len=(8, 8), desc='Operator PLMN List', **kwargs): super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=rec_len, **kwargs) - self._construct = Struct('lai'/Struct('mcc_mnc'/BcdAdapter(Bytes(3)), + self._construct = Struct('lai'/Struct('mcc_mnc'/PlmnAdapter(Bytes(3)), 'lac_min'/HexAdapter(Bytes(2)), 'lac_max'/HexAdapter(Bytes(2))), 'pnn_record_id'/Int8ub) # TS 51.011 Section 10.3.44 + TS 31.102 4.2.62 @@ -884,7 +884,7 @@ class EF_SPDI(TransparentEF): # TODO: a305800337f800ffffffffffffffffffffffffffffffffffffffffffffff class ServiceProviderPLMN(BER_TLV_IE, tag=0x80): # flexible numbers of 3-byte PLMN records - _construct = GreedyRange(BcdAdapter(Bytes(3))) + _construct = GreedyRange(PlmnAdapter(Bytes(3))) class SPDI(BER_TLV_IE, tag=0xA3, nested=[ServiceProviderPLMN]): pass diff --git a/pysim-testdata/pySim-trace_test_gsmtap.pcapng.ok b/pysim-testdata/pySim-trace_test_gsmtap.pcapng.ok index 873574ca..2d9d9513 100644 --- a/pysim-testdata/pySim-trace_test_gsmtap.pcapng.ok +++ b/pysim-testdata/pySim-trace_test_gsmtap.pcapng.ok @@ -57,7 +57,7 @@ CardReset(3b9f96801f878031e073fe211b674a4c753034054ba9) =============================== 00 READ BINARY MF/ADF.USIM/EF.ACC - 9000 {'ACC0': True, 'ACC1': False, 'ACC2': False, 'ACC3': False, 'ACC4': False, 'ACC5': False, 'ACC6': False, 'ACC7': False, 'ACC8': False, 'ACC9': False, 'ACC10': False, 'ACC11': False, 'ACC12': False, 'ACC13': False, 'ACC14': False, 'ACC15': False} =============================== -00 READ BINARY MF/ADF.USIM/EF.LOCI - 9000 {'tmsi': 'ffffffff', 'lai': {'mcc_mnc': 'ffffff', 'lac': 'fffe'}, 'rfu': 255, 'lu_status': 3} +00 READ BINARY MF/ADF.USIM/EF.LOCI - 9000 {'tmsi': 'ffffffff', 'lai': {'mcc_mnc': 'fff-ff', 'lac': 'fffe'}, 'rfu': 255, 'lu_status': 3} =============================== 00 READ BINARY MF/ADF.USIM/EF.PSLOCI - 9000 {'ptmsi': 'ffffffff', 'ptmsi_sig': 'ffffff', 'rai': 'ffff00fffeff', 'rau_status': 2} ===============================