P1sec / pycrate

A Python library to ease the development of encoders and decoders for various protocols and file formats; contains ASN.1 and CSN.1 compilers.
GNU Lesser General Public License v2.1
381 stars 132 forks source link

Question: how to properly access values of CSN1Alt data, as in rest_octets #155

Closed jewalt closed 2 years ago

jewalt commented 2 years ago

I have been struggling in trying to find the proper way to access data values in GSM SIB rest_octet fields. For example, SIB13 RAC field.

In this SIB13 message: 010600901278487CC932A62C25A4CB2B2B2B2B2B2B2B2B

I know the RAC is 73 by using tshark to parse the SIB13 message, but I am not sure the best way to index into the decoded object to get that data. Any help would be really appreciated.

p1-bmu commented 2 years ago

You are touching here some limits of pycrate. The CSN.1 part has been developped with pure decoding in mind, but no specific routines were developped to ease encoding fields and accessing subfields. There are 2 crude approaches to accessing a subfield value in CSN.1. The first by going directly through the value of the SIB13, the 2nd by browsing the JSON representation:

In [53]: from pycrate_mobile.NAS import *

In [54]: m, e = parse_NAS_MT(unhexlify('010600901278487CC932A62C25A4CB2B2B2B2B2B2B2B2B'), True)

In [55]: e # 0 means no error during decoding
Out[55]: 0

In [56]: print(m['SI13RestOctets']['si_13_rest_octets'].show()) # focusing on the SI13 rest octets
<si_13_rest_octets (CSN1List): [
 <(CSN1Alt): { H : [
  <bcch_change_mark (CSN1Bit): 1>
  <si_change_field (CSN1Bit): 0>
  <(CSN1Alt): { 0 : []}>
  <(CSN1Alt): { 0 : [
   <rac (CSN1Bit): 73>
   <spgc_ccch_sup (CSN1Bit): 1>
   <priority_access_thr (CSN1Bit): 6>
   <network_control_order (CSN1Bit): 0>
   <gprs_cell_options (CSN1Ref): <gprs_cell_options_ie (CSN1List): [
    <nmo (CSN1Bit): 1>
    <t3168 (CSN1Bit): 1>
    <t3192 (CSN1Bit): 0>
    <drx_timer_max (CSN1Bit): 3>
    <access_burst_type (CSN1Bit): 1>
    <control_ack_type (CSN1Bit): 1>
    <bs_cv_max (CSN1Bit): 9>
    <(CSN1Alt): { 1 : [
     <pan_dec (CSN1Bit): 1>
     <pan_inc (CSN1Bit): 1>
     <pan_max (CSN1Bit): 1>]}>
    <(CSN1Alt): { 1 : [
     <extension_length (CSN1Bit): 10>
     <(CSN1Ref): <extension_information (CSN1List): [
      <(CSN1List): [
       <(CSN1Alt): { 1 : [
        <egprs_packet_channel_request (CSN1Bit): 0>
        <bep_period (CSN1Bit): 6>]}>
       <pfc_feature_mode (CSN1Bit): 0>
       <dtm_support (CSN1Bit): 0>
       <bss_paging_coordination (CSN1Bit): 1>]>
      <(CSN1List): [
       <ccn_active (CSN1Bit): 0>
       <nw_ext_utbf (CSN1Bit): 1>]>]>>]}>]>>
   <gprs_power_control_parameters (CSN1Ref): <gprs_power_control_parameters_ie (CSN1List): [
    <alpha (CSN1Bit): 8>
    <t_avg_w (CSN1Bit): 9>
    <t_avg_t (CSN1Bit): 13>
    <pc_meas_chan (CSN1Bit): 0>
    <n_avg_i (CSN1Bit): 4>]>>]}>
  <(CSN1Alt): { H : [
   <sgsnr (CSN1Bit): 1>
   <(CSN1Alt): { H : [
    <si_status_ind (CSN1Bit): 0>
    <(CSN1Alt): { L : []}>]}>]}>]}>
 <(CSN1Ref): <spare_padding (CSN1Val): L**>>]>

In [57]: m['SI13RestOctets']['si_13_rest_octets'].get_val() # we access here the CSN.1 value decoded
Out[57]: 
[['H',
  1,
  0,
  ['0'],
  ['0',
   73,
   1,
   6,
   0,
   [1,
    1,
    0,
    3,
    1,
    1,
    9,
    ['1', 1, 1, 1],
    ['1', 10, [[['1', 0, 6], 0, 0, 1], [0, 1]]]],
   [8, 9, 13, 0, 4]],
  ['H', 1, ['H', 0, ['L']]]],
 ['L',
  'L',
  [...]
  'L']]

In [58]: rac = m['SI13RestOctets']['si_13_rest_octets'].get_val()[0][4][1] # here, [0][4][1] is the path to the RAC value

In [59]: print(m['SI13RestOctets']['si_13_rest_octets'].to_json()) # here is the JSON representation of the rest octets
{
 "si_13_rest_octets": [
  [
   "H",
   {
    "bcch_change_mark": "001"
   },
   {
    "si_change_field": "0000"
   },
   [
    "0"
   ],
   [
    "0",
    {
     "rac": "01001001"
    },
    {
     "spgc_ccch_sup": "1"
    },
    {
     "priority_access_thr": "110"
    },
    {
     "network_control_order": "00"
    },
    {
     "gprs_cell_options": {
      "gprs_cell_options_ie": [
       {
        "nmo": "01"
       },
       {
        "t3168": "001"
       },
       {
        "t3192": "000"
       },
       {
        "drx_timer_max": "011"
       },
       {
        "access_burst_type": "1"
       },
       {
        "control_ack_type": "1"
       },
       {
        "bs_cv_max": "1001"
       },
       [
        "1",
        {
         "pan_dec": "001"
        },
        {
         "pan_inc": "001"
        },
        {
         "pan_max": "001"
        }
       ],
       [
        "1",
        {
         "extension_length": "001010"
        },
        {
         "extension_information": [
          [
           [
            "1",
            {
             "egprs_packet_channel_request": "0"
            },
            {
             "bep_period": "0110"
            }
           ],
           {
            "pfc_feature_mode": "0"
           },
           {
            "dtm_support": "0"
           },
           {
            "bss_paging_coordination": "1"
           }
          ],
          [
           {
            "ccn_active": "0"
           },
           {
            "nw_ext_utbf": "1"
           }
          ]
         ]
        }
       ]
      ]
     }
    },
    {
     "gprs_power_control_parameters": {
      "gprs_power_control_parameters_ie": [
       {
        "alpha": "1000"
       },
       {
        "t_avg_w": "01001"
       },
       {
        "t_avg_t": "01101"
       },
       {
        "pc_meas_chan": "0"
       },
       {
        "n_avg_i": "0100"
       }
      ]
     }
    }
   ],
   [
    "H",
    {
     "sgsnr": "1"
    },
    [
     "H",
     {
      "si_status_ind": "0"
     },
     [
      "L"
     ]
    ]
   ]
  ],
  {
   "spare_padding": [
    "L",
    "L",
    [...]
    "L"
   ]
  }
 ]
}

In [60]: m['SI13RestOctets']['si_13_rest_octets']._to_jval() # and here is the underlying Python value ready for JSON encoding
Out[60]: 
[['H',
  {'bcch_change_mark': '001'},
  {'si_change_field': '0000'},
  ['0'],
  ['0',
   {'rac': '01001001'},
   {'spgc_ccch_sup': '1'},
   {'priority_access_thr': '110'},
   {'network_control_order': '00'},
   {'gprs_cell_options': {'gprs_cell_options_ie': [{'nmo': '01'},
      {'t3168': '001'},
      {'t3192': '000'},
      {'drx_timer_max': '011'},
      {'access_burst_type': '1'},
      {'control_ack_type': '1'},
      {'bs_cv_max': '1001'},
      ['1', {'pan_dec': '001'}, {'pan_inc': '001'}, {'pan_max': '001'}],
      ['1',
       {'extension_length': '001010'},
       {'extension_information': [[['1',
           {'egprs_packet_channel_request': '0'},
           {'bep_period': '0110'}],
          {'pfc_feature_mode': '0'},
          {'dtm_support': '0'},
          {'bss_paging_coordination': '1'}],
         [{'ccn_active': '0'}, {'nw_ext_utbf': '1'}]]}]]}},
   {'gprs_power_control_parameters': {'gprs_power_control_parameters_ie': [{'alpha': '1000'},
      {'t_avg_w': '01001'},
      {'t_avg_t': '01101'},
      {'pc_meas_chan': '0'},
      {'n_avg_i': '0100'}]}}],
  ['H', {'sgsnr': '1'}, ['H', {'si_status_ind': '0'}, ['L']]]],
 {'spare_padding': ['L',
   'L',
   'L',
   [...]
   'L']}]

In [61]: m['SI13RestOctets']['si_13_rest_octets']._to_jval()[0][4][1] # so we can also access the RAC value here in a similar way
Out[61]: {'rac': '01001001'}

As you can see however, there is no magic to browse through all the different subfields of a CSN.1 structure. You need to know the path of what you are looking for.

jewalt commented 2 years ago

Thank you! This gets me what I needed.