Working with cellular protocols

For the project Internet Para Todos one of the toughest challenge we put in our plate is conducting tests on the new cellular hardware with the lowest human involvement possible. To this end, we used a commercial UE simulator that offers an API which enables us to perform bandwidth tests in an automated way, check protocol compliance, etc. For the record, CTIA is giving great example of testplans the cellular industry is conducting on hardware before going to field trial and (eventually) deployments. Nevertheless, at several occurrences it was necessary to connect a real phone and get a proper diagnosis in order to have a reference discussion with the vendors.

Typically, the tests are involving an equipment connected to a faraday cage, and a physical phone running a test suite inside the faraday cage.

On Android like previous mobile phone OS, several manufacturers give access to low level testing software - I think LG phones are probably the most easy to access via a secret dialpad combination. On our trusty Nexus and Nexus5x, however, and probably for the sake of attack surface reduction, those softwares are absent or simply not accessible.

However, using a rooted phone, the QC chipset of those phones is offering a diagnostic interface that uses the DIAG protocol. Several applications are using that interface for various purposes on Android :

  • Snoopsnitch is an opensource project focused on collecting data on existing network by performing passive and active tests and recovering the event through the DIAG protocol on a rooted Android phone. While this is not my usecases it offers great insights already, and as an opensource app could been extended to help lab trials.

  • Network Signal Guru is an Android app providing several diagnoses screen extremely useful for operators. Network Signal Guru LTE details Network Signal Guru LTE Frames

There are also dedicated test apps from specialized vendors but this can get quickly expensive.

I am already using OpenSTF, which is a web-based tool for controlling a farm of testing phones. With this excellent docker-compose packaged version it gives us the perfect remote control solution for our phones that are located in the test faraday cage, so recovering the diag message over the USB cable would be convenient.

OpenSTF screenshot

A Reddit thread is describing an Android command to activate the DIAG message over USB. Looking at Snoopsnitch it becomes possible to activate the logs and to have it flowing through the reconfigured USB interface.

I patched up some crude python script using several projects, and voila :

{
  "rrc_version": 113,
  "timestamp": 1536095014.738498,
  "diag_length": 89,
  "length": 60,
  "raw": "201215880004039cdc400a14114a84a070f78b26f5c100290285970811925a1b8400004016685068703ff738107d44dc07aee71439118b77498e4870",
  "earfcn": 1849,
  "pci": 39,
  "time": "Tue, 04 Sep 2018 21:03:34 +0000",
  "message": [
    "c1",
    [
      "rrcConnectionReconfiguration",
      {
        "rrc-TransactionIdentifier": 0,
        "criticalExtensions": [
          "c1",
          [
            "rrcConnectionReconfiguration-r8",
            {
              "radioResourceConfigDedicated": {
                "srb-ToAddModList": [
                  {
                    "logicalChannelConfig": [
                      "defaultValue",
                      0
                    ],
                    "rlc-Config": [
                      "explicitValue",
                      [
                        "am",
                        {
                          "dl-AM-RLC": {
                            "t-StatusProhibit": "ms0",
                            "t-Reordering": "ms35"
                          },
                          "ul-AM-RLC": {
                            "pollPDU": "pInfinity",
                            "t-PollRetransmit": "ms80",
                            "maxRetxThreshold": "t32",
                            "pollByte": "kBinfinity"
                          }
                        }
                      ]
                    ],
                    "srb-Identity": 2
                  }
                ],
                "drb-ToAddModList": [
                  {
                    "drb-Identity": 3,
                    "logicalChannelConfig": {
                      "ul-SpecificParameters": {
                        "priority": 10,
                        "prioritisedBitRate": "kBps8",
                        "bucketSizeDuration": "ms50",
                        "logicalChannelGroup": 3
                      }
                    },
                    "rlc-Config": [
                      "am",
                      {
                        "dl-AM-RLC": {
                          "t-StatusProhibit": "ms25",
                          "t-Reordering": "ms35"
                        },
                        "ul-AM-RLC": {
                          "pollPDU": "p16",
                          "t-PollRetransmit": "ms80",
                          "maxRetxThreshold": "t32",
                          "pollByte": "kBinfinity"
                        }
                      }
                    ],
                    "pdcp-Config": {
                      "headerCompression": [
                        "notUsed",
                        0
                      ],
                      "discardTimer": "infinity",
                      "rlc-AM": {
                        "statusReportRequired": false
                      }
                    },
                    "eps-BearerIdentity": 5,
                    "logicalChannelIdentity": 3
                  }
                ],
                "mac-MainConfig": [
                  "explicitValue",
                  {
                    "timeAlignmentTimerDedicated": "infinity",
                    "drx-Config": [
                      "setup",
                      {
                        "shortDRX": {
                          "shortDRX-Cycle": "sf80",
                          "drxShortCycleTimer": 1
                        },
                        "longDRX-CycleStartOffset": [
                          "sf320",
                          284
                        ],
                        "drx-RetransmissionTimer": "psf8",
                        "onDurationTimer": "psf10",
                        "drx-InactivityTimer": "psf100"
                      }
                    ]
                  }
                ]
              },
              "measConfig": {
                "reportConfigToAddModList": [
                  {
                    "reportConfig": [
                      "reportConfigEUTRA",
                      {
                        "maxReportCells": 4,
                        "reportQuantity": "both",
                        "triggerQuantity": "rsrp",
                        "triggerType": [
                          "event",
                          {
                            "eventId": [
                              "eventA3",
                              {
                                "a3-Offset": 6,
                                "reportOnLeave": false
                              }
                            ],
                            "timeToTrigger": "ms320",
                            "hysteresis": 2
                          }
                        ],
                        "reportInterval": "ms480",
                        "reportAmount": "infinity"
                      }
                    ],
                    "reportConfigId": 1
                  },
                  {
                    "reportConfig": [
                      "reportConfigEUTRA",
                      {
                        "maxReportCells": 1,
                        "reportQuantity": "both",
                        "triggerQuantity": "rsrp",
                        "triggerType": [
                          "event",
                          {
                            "eventId": [
                              "eventA2",
                              {
                                "a2-Threshold": [
                                  "threshold-RSRP",
                                  25
                                ]
                              }
                            ],
                            "timeToTrigger": "ms640",
                            "hysteresis": 4
                          }
                        ],
                        "reportInterval": "ms5120",
                        "reportAmount": "infinity"
                      }
                    ],
                    "reportConfigId": 2
                  }
                ],
                "measObjectToAddModList": [
                  {
                    "measObjectId": 1,
                    "measObject": [
                      "measObjectEUTRA",
                      {
                        "presenceAntennaPort1": true,
                        "carrierFreq": 1849,
                        "cellsToAddModList": [
                          {
                            "cellIndividualOffset": "dB-5",
                            "cellIndex": 1,
                            "physCellId": 40
                          },
                          {
                            "cellIndividualOffset": "dB-5",
                            "cellIndex": 2,
                            "physCellId": 41
                          },
                          {
                            "cellIndividualOffset": "dB-1",
                            "cellIndex": 3,
                            "physCellId": 160
                          },
                          {
                            "cellIndividualOffset": "dB-4",
                            "cellIndex": 4,
                            "physCellId": 444
                          },
                          {
                            "cellIndividualOffset": "dB-1",
                            "cellIndex": 5,
                            "physCellId": 445
                          }
                        ],
                        "allowedMeasBandwidth": "mbw100",
                        "neighCellConfig": [
                          2,
                          2
                        ],
                        "offsetFreq": "dB0"
                      }
                    ]
                  }
                ],
                "speedStatePars": [
                  "release",
                  0
                ],
                "quantityConfig": {
                  "quantityConfigEUTRA": {
                    "filterCoefficientRSRQ": "fc11",
                    "filterCoefficientRSRP": "fc8"
                  },
                  "quantityConfigUTRA": {
                    "measQuantityUTRA-FDD": "cpich-RSCP",
                    "measQuantityUTRA-TDD": "pccpch-RSCP",
                    "filterCoefficient": "fc4"
                  }
                },
                "measIdToAddModList": [
                  {
                    "measObjectId": 1,
                    "measId": 1,
                    "reportConfigId": 1
                  },
                  {
                    "measObjectId": 1,
                    "measId": 2,
                    "reportConfigId": 2
                  }
                ]
              }
            }
          ]
        ]
      }
    ]
  ],
  "type": "DL-DCCH-Message",
  "rrc_release": 10
}

This enables easy recovery of eNB parameters, such as measurement Reports triggers thresholds (A2,A3), DRX, etc, but also MIB event, SIB1, etc. I have implemented only the couple of 4G events I was interested in, but this could be extended to cover other events, including in 2G and 3G.

I found myself using the online 3GPP decoder websites such as this one and that one to help identify the frames content, as well as diag-parser to have more insights on the messages format. I then used pycrate to actually decode the binary messages.