laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/37658?usp=email )
Change subject: pySim.esim.saip.templates: Build tree from template files
......................................................................
pySim.esim.saip.templates: Build tree from template files
Change-Id: I13e80e9dbddbb145411378a0d9e01461aef75db4
---
M pySim/esim/saip/templates.py
1 file changed, 60 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/58/37658/1
diff --git a/pySim/esim/saip/templates.py b/pySim/esim/saip/templates.py
index 417c83b..8a19d34 100644
--- a/pySim/esim/saip/templates.py
+++ b/pySim/esim/saip/templates.py
@@ -59,23 +59,69 @@
return "FileTemplate(%s/%s, %s, %s, arr=%s, sfi=%s)" % (self.name, self.pe_name, s_fid,
self.file_type, s_arr, s_sfi)
+ def print_tree(self, indent:str = ""):
+ """recursive printing of FileTemplate tree structure."""
+ print("%s%s" % (indent, repr(self)))
+ indent += " "
+ for c in self.children:
+ c.print_tree(indent)
+
+ def get_file_by_path(self, path: List[str]) -> Optional['FileTemplate']:
+ """Return a FileTemplate matching the given path within this ProfileTemplate."""
+ if path[0].lower() != self.name.lower():
+ return None
+ for c in self.children:
+ if path[1].lower() == c.name.lower():
+ return c.get_file_by_path(path[1:])
+
class ProfileTemplate:
"""Representation of a SimAlliance/TCA Profile Template. Each Template is identified by its OID and
consists of a number of file definitions. We implement each profile template as a class derived from this
base class. Each such derived class is a singleton and has no instances."""
created_by_default: bool = False
+ optional: bool = False
oid: Optional[OID.eOID] = None
files: List[FileTemplate] = []
- files_by_pename: dict[str,FileTemplate] = {}
def __init_subclass__(cls, **kwargs):
"""This classmethod is called automatically after executing the subclass body. We use it to
initialize the cls.files_by_pename from the cls.files"""
super().__init_subclass__(**kwargs)
+ cur_df = None
+
+ cls.files_by_pename: dict[str,FileTemplate] = {}
+ cls.tree: List[FileTemplate] = []
+
+ if not cls.optional and not cls.files[0].file_type in ['MF', 'DF', 'ADF']:
+ raise ValueError('First file in non-optional template must be MF, DF or ADF (is: %s)' % cls.files[0])
for f in cls.files:
+ if f.file_type in ['MF', 'DF', 'ADF']:
+ if cur_df == None:
+ cls.tree.append(f)
+ cur_df = f
+ f.parent = None
+ else:
+ # "cd .."
+ if cur_df.parent:
+ cur_df = cur_df.parent
+ cur_df.children.append(f)
+ f.parent = cur_df
+ cur_df = f
+ else:
+ if cur_df == None:
+ cls.tree.append(f)
+ f.parent = None
+ else:
+ cur_df.children.append(f)
+ f.parent = cur_df
cls.files_by_pename[f.pe_name] = f
ProfileTemplateRegistry.add(cls)
+ @classmethod
+ def print_tree(cls):
+ for c in cls.tree:
+ c.print_tree()
+
class ProfileTemplateRegistry:
"""A registry of profile templates. Exists as a singleton class with no instances and only
classmethods."""
@@ -318,6 +364,7 @@
# Section 9.5.2 v2.3.1
class FilesUsimOptional(ProfileTemplate):
created_by_default = False
+ optional = True
oid = OID.ADF_USIMopt_not_by_default
files = [
FileTemplate(0x6f05, 'EF.LI', 'TR', None, 6, 1, 0x02, 'FF...FF', False),
@@ -400,6 +447,7 @@
# Section 9.5.2
class FilesUsimOptionalV2(ProfileTemplate):
created_by_default = False
+ optional = True
oid = OID.ADF_USIMopt_not_by_default_v2
files = [
FileTemplate(0x6f05, 'EF.LI', 'TR', None, 6, 1, 0x02, 'FF...FF', False),
@@ -601,6 +649,7 @@
# Section 9.6.2 v2.3.1
class FilesIsimOptional(ProfileTemplate):
created_by_default = False
+ optional = True
oid = OID.ADF_ISIMopt_not_by_default
files = [
FileTemplate(0x6f09, 'EF.P-CSCF', 'LF', 1, None, 2, None, None, True, ['size'], ass_serv=[1,5]),
@@ -618,6 +667,7 @@
# Section 9.6.2
class FilesIsimOptionalv2(ProfileTemplate):
created_by_default = False
+ optional = True
oid = OID.ADF_ISIMopt_not_by_default_v2
files = [
FileTemplate(0x6f09, 'EF.PCSCF', 'LF', 1, None, 2, None, None, True, ['size'], ass_serv=[1,5]),
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/37658?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I13e80e9dbddbb145411378a0d9e01461aef75db4
Gerrit-Change-Number: 37658
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange
Attention is currently required from: daniel, lynxis lazus.
pespin has posted comments on this change. ( https://gerrit.osmocom.org/c/osmo-sgsn/+/37636?usp=email )
Change subject: Fix DeactPDPCtxAcc when UE goes PMM ENABLED but lost its PDP context
......................................................................
Patch Set 1:
(1 comment)
File include/osmocom/sgsn/pdpctx.h:
https://gerrit.osmocom.org/c/osmo-sgsn/+/37636/comment/629826da_d5493090
PS1, Line 71: bool ue_pdp_active; /* PDP Context is active for this NSAPI? */
> Maybe add more context, if the UE loses it state and reports PDP context not anymore active?
Not sure what you mean. This tracks the SGSN vision of the PDP context towards the UE.
--
To view, visit https://gerrit.osmocom.org/c/osmo-sgsn/+/37636?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-sgsn
Gerrit-Branch: master
Gerrit-Change-Id: I0ccd9228d71c29248b5f510356dbfdb09565dc30
Gerrit-Change-Number: 37636
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann(a)sysmocom.de>
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu>
Gerrit-Attention: daniel <dwillmann(a)sysmocom.de>
Gerrit-Attention: lynxis lazus <lynxis(a)fe80.eu>
Gerrit-Comment-Date: Mon, 29 Jul 2024 11:01:27 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: lynxis lazus <lynxis(a)fe80.eu>
Gerrit-MessageType: comment
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/37543?usp=email )
Change subject: pySim-shell: add "fsdump" command
......................................................................
pySim-shell: add "fsdump" command
This command exports the entire filesystem state as one JSON document,
which can be useful for storing it in a noSQL database, or for doing a
structured diff between different such dumps.
It's similar to "export", but then reasonably different to rectify a
separate command.
Change-Id: Ib179f57bc04d394efe11003ba191dca6098192d3
---
M docs/shell.rst
M pySim-shell.py
M pySim/exceptions.py
3 files changed, 233 insertions(+), 7 deletions(-)
Approvals:
laforge: Looks good to me, approved
fixeria: Looks good to me, but someone else must approve
Jenkins Builder: Verified
diff --git a/docs/shell.rst b/docs/shell.rst
index 111c502..768765e 100644
--- a/docs/shell.rst
+++ b/docs/shell.rst
@@ -145,6 +145,32 @@
trying to SELECT them.
+fsdump
+~~~~~~
+.. argparse::
+ :module: pySim-shell
+ :func: PySimCommands.fsdump_parser
+
+Please note that `fsdump` works relative to the current working
+directory, so if you are in `MF`, then the dump will contain all known
+files on the card. However, if you are in `ADF.ISIM`, only files below
+that ADF will be part of the dump.
+
+Furthermore, it is strongly advised to first enter the ADM1 pin
+(`verify_adm`) to maximize the chance of having permission to read
+all/most files.
+
+One use case for this is to systematically analyze the differences between the contents of two
+cards. To do this, you can create fsdumps of the two cards, and then use some general-purpose JSON
+diffing tool like `jycm --show` (see https://github.com/eggachecat/jycm).
+
+Example:
+::
+
+ pySIM-shell (00:MF)> fsdump > /tmp/fsdump.json
+ pySIM-shell (00:MF)>
+
+
tree
~~~~
Display a tree of the card filesystem. It is important to note that this displays a tree
diff --git a/pySim-shell.py b/pySim-shell.py
index 168c916..6673963 100755
--- a/pySim-shell.py
+++ b/pySim-shell.py
@@ -554,10 +554,10 @@
context['COUNT'] += 1
df = self._cmd.lchan.selected_file
- # The currently selected file (not the file we are going to export)
- # must always be an ADF or DF. From this starting point we select
- # the EF we want to export. To maintain consistency we will then
- # select the current DF again (see comment below).
+ # The currently selected file (not the file we are going to export)
+ # must always be an ADF or DF. From this starting point we select
+ # the EF we want to export. To maintain consistency we will then
+ # select the current DF again (see comment below).
if not isinstance(df, CardDF):
raise RuntimeError(
"currently selected file %s is not a DF or ADF" % str(df))
@@ -640,6 +640,181 @@
raise RuntimeError(
"unable to export %i dedicated files(s)%s" % (context['ERR'], exception_str_add))
+ def fsdump_df(self, context, as_json):
+ """Dump information about currently selected [A]DF"""
+ df = self._cmd.lchan.selected_file
+ df_path_list = df.fully_qualified_path(True)
+ df_path = df.fully_qualified_path_str(True)
+
+ res = {
+ 'path': df_path_list,
+ }
+
+ try:
+ if not self._cmd.lchan.selected_file_fcp_hex:
+ # An application without a real ADF (like ADF.ARA-M) / filesystem
+ return
+
+ fcp_dec = self._cmd.lchan.selected_file_fcp
+ res['fcp_raw'] = str(self._cmd.lchan.selected_file_fcp_hex)
+ res['fcp'] = fcp_dec
+
+ except SwMatchError as e:
+ res['error'] = {
+ 'sw_actual': e.sw_actual,
+ 'sw_expected': e.sw_expected,
+ 'message': e.description,
+ }
+ except Exception as e:
+ raise(e)
+ res['error'] = {
+ 'message': str(e)
+ }
+
+ context['result']['files'][df_path] = res
+
+ def fsdump_ef(self, filename, context, as_json):
+ """Select and dump a single elementary file (EF) """
+ # TODO: this is very similar to export_ef(), but I couldn't really come up with a way to share
+ # code between the two. They only hypothetical option could be turn "export" into a mere
+ # post-processing / printing function that works on the fsdump-generated dict/json?
+ df = self._cmd.lchan.selected_file
+
+ # The currently selected file (not the file we are going to export)
+ # must always be an ADF or DF. From this starting point we select
+ # the EF we want to export. To maintain consistency we will then
+ # select the current DF again (see comment below).
+ if not isinstance(df, CardDF):
+ raise RuntimeError("currently selected file %s is not a DF or ADF" % str(df))
+
+ df_path_list = df.fully_qualified_path(True)
+ df_path = df.fully_qualified_path_str(True)
+ df_path_fid = df.fully_qualified_path_str(False)
+
+ file_str = df_path + "/" + str(filename)
+
+ res = {
+ 'path': df_path_list + [str(filename)],
+ }
+
+ try:
+ fcp_dec = self._cmd.lchan.select(filename, self._cmd)
+
+ res['fcp_raw'] = str(self._cmd.lchan.selected_file_fcp_hex)
+ res['fcp'] = fcp_dec
+
+ structure = self._cmd.lchan.selected_file_structure()
+ if structure == 'transparent':
+ if as_json:
+ result = self._cmd.lchan.read_binary_dec()
+ body = result[0]
+ else:
+ result = self._cmd.lchan.read_binary()
+ body = str(result[0])
+ elif structure == 'cyclic' or structure == 'linear_fixed':
+ body = []
+ # Use number of records specified in select response
+ num_of_rec = self._cmd.lchan.selected_file_num_of_rec()
+ if num_of_rec:
+ for r in range(1, num_of_rec + 1):
+ if as_json:
+ result = self._cmd.lchan.read_record_dec(r)
+ body.append(result[0])
+ else:
+ result = self._cmd.lchan.read_record(r)
+ body.append(str(result[0]))
+
+ # When the select response does not return the number of records, read until we hit the
+ # first record that cannot be read.
+ else:
+ r = 1
+ while True:
+ try:
+ if as_json:
+ result = self._cmd.lchan.read_record_dec(r)
+ body.append(result[0])
+ else:
+ result = self._cmd.lchan.read_record(r)
+ body.append(str(result[0]))
+ except SwMatchError as e:
+ # We are past the last valid record - stop
+ if e.sw_actual == "9402":
+ break
+ # Some other problem occurred
+ raise e
+ r = r + 1
+ elif structure == 'ber_tlv':
+ tags = self._cmd.lchan.retrieve_tags()
+ body = {}
+ for t in tags:
+ result = self._cmd.lchan.retrieve_data(t)
+ (tag, l, val, remainer) = bertlv_parse_one(h2b(result[0]))
+ body[t] = b2h(val)
+ else:
+ raise RuntimeError('Unsupported structure "%s" of file "%s"' % (structure, filename))
+ res['body'] = body
+
+ except SwMatchError as e:
+ res['error'] = {
+ 'sw_actual': e.sw_actual,
+ 'sw_expected': e.sw_expected,
+ 'message': e.description,
+ }
+ except Exception as e:
+ raise(e)
+ res['error'] = {
+ 'message': str(e)
+ }
+
+ context['result']['files'][file_str] = res
+
+ # When reading the file is done, make sure the parent file is
+ # selected again. This will be the usual case, however we need
+ # to check before since we must not select the same DF twice
+ if df != self._cmd.lchan.selected_file:
+ self._cmd.lchan.select(df.fid or df.aid, self._cmd)
+
+
+ fsdump_parser = argparse.ArgumentParser()
+ fsdump_parser.add_argument(
+ '--filename', type=str, default=None, help='only export specific (named) file')
+ fsdump_parser.add_argument(
+ '--json', action='store_true', help='export file contents as JSON (less reliable)')
+
+ @cmd2.with_argparser(fsdump_parser)
+ def do_fsdump(self, opts):
+ """Export filesystem metadata and file contents of all files below current DF in
+ machine-readable json format. This is similar to "export", but much easier to parse by
+ downstream processing tools. You usually may want to call this from the MF and verify
+ the ADM1 PIN (if available) to maximize the amount of readable files."""
+ result = {
+ 'name': self._cmd.card.name,
+ 'atr': self._cmd.rs.identity['ATR'],
+ 'eid': self._cmd.rs.identity.get('EID', None),
+ 'iccid': self._cmd.rs.identity.get('ICCID', None),
+ 'aids': {x.aid:{} for x in self._cmd.rs.mf.applications.values()},
+ 'files': {},
+ }
+ context = {'result': result, 'DF_SKIP': 0, 'DF_SKIP_REASON': []}
+ kwargs_export = {'as_json': opts.json}
+ exception_str_add = ""
+
+ if opts.filename:
+ # export only that one specified file
+ self.fsdump_ef(opts.filename, context, **kwargs_export)
+ else:
+ # export an entire subtree
+ try:
+ self.walk(0, self.fsdump_ef, self.fsdump_df, context, **kwargs_export)
+ except Exception as e:
+ print("# Stopping early here due to exception: " + str(e))
+ print("#")
+ exception_str_add = ", also had to stop early due to exception:" + str(e)
+ #raise e
+
+ self._cmd.poutput_json(context['result'])
+
+
def do_desc(self, opts):
"""Display human readable file description for the currently selected file"""
desc = self._cmd.lchan.selected_file.desc
diff --git a/pySim/exceptions.py b/pySim/exceptions.py
index f726aa6..1c9e3b8 100644
--- a/pySim/exceptions.py
+++ b/pySim/exceptions.py
@@ -49,9 +49,18 @@
self.sw_expected = sw_expected
self.rs = rs
- def __str__(self):
+ @property
+ def description(self):
if self.rs and self.rs.lchan[0]:
r = self.rs.lchan[0].interpret_sw(self.sw_actual)
if r:
- return "SW match failed! Expected %s and got %s: %s - %s" % (self.sw_expected, self.sw_actual, r[0], r[1])
- return "SW match failed! Expected %s and got %s." % (self.sw_expected, self.sw_actual)
+ return "%s - %s" % (r[0], r[1])
+ return ''
+
+ def __str__(self):
+ description = self.description
+ if description:
+ description = ": " + description
+ else:
+ description = "."
+ return "SW match failed! Expected %s and got %s%s" % (self.sw_expected, self.sw_actual, description)
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/37543?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Ib179f57bc04d394efe11003ba191dca6098192d3
Gerrit-Change-Number: 37543
Gerrit-PatchSet: 4
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: merged
Attention is currently required from: pespin.
lynxis lazus has posted comments on this change. ( https://gerrit.osmocom.org/c/osmo-sgsn/+/37596?usp=email )
Change subject: gtp: Set Direct Tunnel Flags DTI during UpdatePDPCtx
......................................................................
Patch Set 2:
(2 comments)
File src/sgsn/gprs_mm_state_iu_fsm.c:
https://gerrit.osmocom.org/c/osmo-sgsn/+/37596/comment/8ac05db4_c27e338b
PS2, Line 58: sgsn_pdp_upd_gtp_u(pdp,
Use a define here to replce 0xFE?
File src/sgsn/gprs_ranap.c:
https://gerrit.osmocom.org/c/osmo-sgsn/+/37596/comment/bef59d4b_144d71ee
PS2, Line 107: pdp->lib->dir_tun_flags.v[0] = 0x01; /* Set DTI flag in Direct Tunnel Flags */
same
--
To view, visit https://gerrit.osmocom.org/c/osmo-sgsn/+/37596?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-sgsn
Gerrit-Branch: master
Gerrit-Change-Id: Iefe73eeea41df0c55db673194c9e9547504cbf0d
Gerrit-Change-Number: 37596
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-CC: lynxis lazus <lynxis(a)fe80.eu>
Gerrit-Attention: pespin <pespin(a)sysmocom.de>
Gerrit-Comment-Date: Mon, 29 Jul 2024 10:49:45 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment