Files
pysim/pySim/apdu_source/pyshark_rspro.py
Philipp Maier 162ba3af3e pySim-trace: mark card reset in the trace
The trace log currently does not contain any information about card
resets. This makes the trace difficult to follow. Let's use the
CardReset object to display the ATR in the trace.

Related: OS#6094
Change-Id: Ia550a8bd2f45d2ad622cb2ac2a2905397db76bce
2023-07-28 10:14:19 +02:00

161 lines
6.9 KiB
Python

# coding=utf-8
# (C) 2022 by Harald Welte <laforge@osmocom.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
import logging
from pprint import pprint as pp
from typing import Tuple
import pyshark
from pySim.utils import h2b, b2h
from pySim.apdu import Tpdu
from . import ApduSource, PacketType, CardReset
logger = logging.getLogger(__name__)
class _PysharkRspro(ApduSource):
"""APDU Source [provider] base class for reading RSPRO (osmo-remsim) via tshark."""
def __init__(self, pyshark_inst):
self.pyshark = pyshark_inst
self.bank_id = None
self.bank_slot = None
self.cmd_tpdu = None
super().__init__()
@staticmethod
def get_bank_slot(bank_slot) -> Tuple[int, int]:
"""Convert a 'bankSlot_element' field into a tuple of bank_id, slot_nr"""
bank_id = bank_slot.get_field('bankId')
slot_nr = bank_slot.get_field('slotNr')
return int(bank_id), int(slot_nr)
@staticmethod
def get_client_slot(client_slot) -> Tuple[int, int]:
"""Convert a 'clientSlot_element' field into a tuple of client_id, slot_nr"""
client_id = client_slot.get_field('clientId')
slot_nr = client_slot.get_field('slotNr')
return int(client_id), int(slot_nr)
@staticmethod
def get_pstatus(pstatus) -> Tuple[int, int, int]:
"""Convert a 'slotPhysStatus_element' field into a tuple of vcc, reset, clk"""
vccPresent = int(pstatus.get_field('vccPresent'))
resetActive = int(pstatus.get_field('resetActive'))
clkActive = int(pstatus.get_field('clkActive'))
return vccPresent, resetActive, clkActive
def read_packet(self) -> PacketType:
p = self.pyshark.next()
return self._parse_packet(p)
def _set_or_verify_bank_slot(self, bsl: Tuple[int, int]):
"""Keep track of the bank:slot to make sure we don't mix traces of multiple cards"""
if not self.bank_id:
self.bank_id = bsl[0]
self.bank_slot = bsl[1]
else:
if self.bank_id != bsl[0] or self.bank_slot != bsl[1]:
raise ValueError('Received data for unexpected B(%u:%u)' % (bsl[0], bsl[1]))
def _parse_packet(self, p) -> PacketType:
rspro_layer = p['rspro']
#print("Layer: %s" % rspro_layer)
rspro_element = rspro_layer.get_field('RsproPDU_element')
#print("Element: %s" % rspro_element)
msg_type = rspro_element.get_field('msg')
rspro_msg = rspro_element.get_field('msg_tree')
if msg_type == '12': # tpduModemToCard
modem2card = rspro_msg.get_field('tpduModemToCard_element')
#print(modem2card)
client_slot = modem2card.get_field('fromClientSlot_element')
csl = self.get_client_slot(client_slot)
bank_slot = modem2card.get_field('toBankSlot_element')
bsl = self.get_bank_slot(bank_slot)
self._set_or_verify_bank_slot(bsl)
data = modem2card.get_field('data').replace(':','')
logger.debug("C(%u:%u) -> B(%u:%u): %s" % (csl[0], csl[1], bsl[0], bsl[1], data))
# store the CMD portion until the RSP portion arrives later
self.cmd_tpdu = h2b(data)
elif msg_type == '13': # tpduCardToModem
card2modem = rspro_msg.get_field('tpduCardToModem_element')
#print(card2modem)
client_slot = card2modem.get_field('toClientSlot_element')
csl = self.get_client_slot(client_slot)
bank_slot = card2modem.get_field('fromBankSlot_element')
bsl = self.get_bank_slot(bank_slot)
self._set_or_verify_bank_slot(bsl)
data = card2modem.get_field('data').replace(':','')
logger.debug("C(%u:%u) <- B(%u:%u): %s" % (csl[0], csl[1], bsl[0], bsl[1], data))
rsp_tpdu = h2b(data)
if self.cmd_tpdu:
# combine this R-TPDU with the C-TPDU we saw earlier
r = Tpdu(self.cmd_tpdu, rsp_tpdu)
self.cmd_tpdu = False
return r
elif msg_type == '14': # clientSlotStatus
cl_slotstatus = rspro_msg.get_field('clientSlotStatusInd_element')
#print(cl_slotstatus)
client_slot = cl_slotstatus.get_field('fromClientSlot_element')
bank_slot = cl_slotstatus.get_field('toBankSlot_element')
slot_pstatus = cl_slotstatus.get_field('slotPhysStatus_element')
vccPresent, resetActive, clkActive = self.get_pstatus(slot_pstatus)
if vccPresent and clkActive and not resetActive:
logger.debug("RESET")
#TODO: extract ATR from RSPRO message and use it here
return CardReset(None)
else:
print("Unhandled msg type %s: %s" % (msg_type, rspro_msg))
class PysharkRsproPcap(_PysharkRspro):
"""APDU Source [provider] class for reading RSPRO (osmo-remsim) from a PCAP
file via pyshark, which in turn uses tshark (part of wireshark).
In order to use this, you need a wireshark patched with RSPRO support,
such as can be found at https://gitea.osmocom.org/osmocom/wireshark/src/branch/laforge/rspro
A STANDARD UPSTREAM WIRESHARK *DOES NOT WORK*.
"""
def __init__(self, pcap_filename):
"""
Args:
pcap_filename: File name of the pcap file to be opened
"""
pyshark_inst = pyshark.FileCapture(pcap_filename, display_filter='rspro', use_json=True, keep_packets=False)
super().__init__(pyshark_inst)
class PysharkRsproLive(_PysharkRspro):
"""APDU Source [provider] class for reading RSPRO (osmo-remsim) from a live capture
via pyshark, which in turn uses tshark (part of wireshark).
In order to use this, you need a wireshark patched with RSPRO support,
such as can be found at https://gitea.osmocom.org/osmocom/wireshark/src/branch/laforge/rspro
A STANDARD UPSTREAM WIRESHARK *DOES NOT WORK*.
"""
def __init__(self, interface, bpf_filter='tcp port 9999 or tcp port 9998'):
"""
Args:
interface: Network interface name to capture packets on (like "eth0")
bfp_filter: libpcap capture filter to use
"""
pyshark_inst = pyshark.LiveCapture(interface=interface, display_filter='rspro', bpf_filter=bpf_filter,
use_json=True)
super().__init__(pyshark_inst)