TLDR; the trace
To document and improve the understanding of VoLTE, I've created pcap traces of the QMI message of the VoLTE registration.
The original pcap file has been created using a Oneplus 6T running lineageos/Android with a german 1&1 simcard of the brand maxxim, which uses the Vodafone network in Germany.
To capture the traffic I've used SCAT and qmi-frida-tracer.
SCAT is used to capture radio packets within the same trace, it both exports NAS-EPS (radio management), but also user traffic. It makes it easier to put QMI messages into relation with messages on the radio side and improve the understanding. E.g. a NAS-EPS or SIP message trigger an event on the QMI side. Also if a QMI TLV is unknown, we might use the radio message to decipher it.
qmi-frida-tracer was ran against the following daemons:
- qcrild (first pid)
 - qcrild (second pid)
 - imsdatadaemon
 - imsqmidaemon
 
I've put the Oneplus 6T into an usb mode supporting all 3: diag,adb and rmnet.
rmnet is required to use qmicli later to set the correct PDC profile to allow the Android to register to VoLTE.
Test setup
The setup consist of 2x VMs:
- 1x VM where all the tools ran and the Oneplus 6T is connected (192.168.56.108)
 - 1x VM where the traffic is captured and wireshark runs. (192.168.56.1)
 
Put the Oneplus 6T into the correct usb mode:
adb root; sleep 1; adb shell setprop sys.usb.config diag,serial_cdev,rmnet,adb
Running SCAT (git version 7eac9c5bea) with:
scat -u -t qc -v 0x05c6 -p 0x9091 -i 0 -H 192.168.56.1
qmi-frida-trace to pass traffic to 192.168.56.1 (modify line where 127.0.0.1 is set as target):
./qmi-frida-tracer.py -n imsdatadaemon
./qmi-frida-tracer.py -n imsqmidaemon
./qmi-frida-tracer.py -p 1584 # first qcrild pid
./qmi-frida-tracer.py -p 1598 # second qcrild pid
Additional I'll set later a VoLTE capable PDC profile using
qmicli -d /dev/cdc-wdm0 --pdc-activate-config=software,2F:1D:0C:4C:28:2D:50:FC:03:84:D8:9E:F3:92:81:C7:DB:A5:1A:7E.
Modification of the trace
For privacy reasons the pcap file which has been published here, has been filtered, but the essential parts are present. All packets which contain either the IMSI, IMEI, phone number or location have been removed. Further:
- Service 0x44 was removed. The service is still unknown, but not suspected to interact with IMS (yet).
 - All radio packets (from SCAT) except a couple ones, have been removed, because most of them contains privacy related informations.
 - All data packets have been removed including SIP, RTP traffic, because those contains both the IMEI, phone number and called phone number.
 - The responses of the simcard (service: uim) to Read Transparent File or Read Record and request/responses of Send APDU.
 - NAS: Get Signal Info has been removed, because it clutters the pcap.
 - Voice service: Dial Call and All Call Status were removed, but no IMS specific parts.
 
To get the most use out of the pcap, please use the wireshark dissector embedded into the .tar.gz of the pcap.
wireshark -X lua_script:qmi_dissector_gen.lua -X lua_script:scat.lua ./trace_volte.pcapng
I've commented on a couple packets, there are some QMI messages which are unknown to libqmi, I've looked them up, but all aren't related. To most I've added comments to the packet in the pcap. I recommend adding the comment coloumn to the packet overview. It is possible to use the source port of the UDP/GSMTAP message to determine which process received or sent the QMI message.
From the traffic I deduct:
- 44303: imsdatadaemon
 - 39675: qcrild (it could be also imsqmidaemon, but the traffic contains to much additional traffic)
 - 58075: qcrild
 - 49595: scat (radio packets)
 
The most interesting parts of the pcap are:
- filter 
qmi.service_id == 0x0012 || qmi.service_id == 0x302showing IMS subsystem traffic - filter 
udp.srcport == 44303showing traffic of the imsdatadaemon 
If you would like to know more about the pcap or have questions about the removed parts, you can reach us by the matrix channel #openimsd:postmarketos.org
Test report
All times in UTC:
- 10:48: Reboot phone
 - 10:49: Put phone into airplane without simcard, enable adb root, switch mode to rmnet/qmi + diag + adb
 - 10:49: start scat
 - 10:50: attach frida to current rild processes
 - 10:54: put simcard into the phone
 - 11:01: enter required pin 4556
 - 11:02: check if qcrild still have correct pids (sometime they change)
 - 11:15: enable frida also for imsqmidaemon & imsdatadaemon
 - 11:18: disable airplane
 - 11:20: enable airplane
 - 11:23: check if qcrild still have correct pids (sometime they change)
 - 11:24: libqmi: set pdc profile, failed CID allocation failed. timed out
 - 11:25: qmicli: pdc: exceute monitor-refresh, get software list, re-do libqmi set pdc profile again, works now
 - 11:26: disable airplane
 - 11:30: check VoLTE registration via phone info debug ui (
*#*#4636#*#*), VoLTE registered! - 11:39: calling another phone
 - 11:42: get called by another phone
 - 11:43: terminate call
 - 11:44: enable airplane
 
wireshark filter
To filter for privacy I used the following chaos filter:
((frame.marked == true) || ((((!(qmi.service_id == 0x0047) ) && !( (qmi.message_id == 0x0039) && qmi.service_id == 0x0b)) && !((qmi.message_id == 0x0020) && (qmi.service_id == 0xb) && (qmi.trans_response == 1)) && !((qmi.message_id == 0x0021) && (qmi.service_id == 0x0b)  && (qmi.trans_response == 1)) && !((qmi.message_id == 0x0024) && (qmi.service_id == 0x02)  && (qmi.trans_response == 1))) && !lte_rrc &&  !((qmi.message_id == 0x0043) && (qmi.service_id == 0x03)  && (qmi.trans_response == 1)) &&  !((qmi.message_id == 0x004d || qmi.message_id == 0x00ac) && (qmi.service_id == 0x03)  && (qmi.trans_response == 1)) &&  !(((qmi.message_id == 0x0082) || (qmi.message_id == 0x00c3 || qmi.message_id == 0x004e || qmi.message_id == 0x0051) ) && (qmi.service_id == 0x03)  && (qmi.trans_indication == 1)) && ((qmi.message_id == 0x003b) && (qmi.service_id == 0x0b))  && !gsm_a.ccch  && !lte_rrc  && (!qmi.service_id ==0x44) && !evs && !amr && !udp.port == 47290 && !(ip.flags.mf == True) && !lte_rrc && !nas-eps))