From 87782319f2a62a8d0e25a73f0710dd44200caf8b Mon Sep 17 00:00:00 2001 From: Smittix Date: Sun, 8 Feb 2026 18:49:20 +0000 Subject: [PATCH] Auto-discover tshark field names for GSM protocol compatibility tshark field names differ between Wireshark versions (3.x vs 4.x): - 3.x: gsm_a.rr.timing_advance, gsm_a.tmsi, gsm_a.cellid - 4.x: gsm_a_rr.timing_adv, gsm_a_dtap.tmsi, e212.ci Added _discover_tshark_fields() that queries `tshark -G fields` to find which field names are available on the installed version, then uses the correct ones for the capture filter and field extraction. Results are cached after first discovery. Co-Authored-By: Claude Opus 4.6 --- routes/gsm_spy.py | 92 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/routes/gsm_spy.py b/routes/gsm_spy.py index fea0834..4540382 100644 --- a/routes/gsm_spy.py +++ b/routes/gsm_spy.py @@ -238,6 +238,84 @@ def validate_band_names(bands: list[str], region: str) -> tuple[list[str], str | return bands, None +# tshark field name discovery - field names vary between Wireshark versions +_tshark_fields_cache: dict[str, str] | None = None + + +def _discover_tshark_fields() -> dict[str, str]: + """Discover correct tshark field names for GSM A protocol. + + Different Wireshark versions use different field names: + - Wireshark 3.x: gsm_a.rr.timing_advance, gsm_a.tmsi, gsm_a.imsi, gsm_a.lac, gsm_a.cellid + - Wireshark 4.x: gsm_a_rr.timing_adv, gsm_a_dtap.tmsi, e212.imsi, e212.lac, e212.ci + + Returns: + Dict mapping logical names to actual tshark field names: + {'ta': ..., 'tmsi': ..., 'imsi': ..., 'lac': ..., 'cid': ...} + """ + global _tshark_fields_cache + if _tshark_fields_cache is not None: + return _tshark_fields_cache + + # Candidate field names for each logical field, in preference order + candidates = { + 'ta': [ + 'gsm_a.rr.timing_advance', + 'gsm_a_rr.timing_adv', + 'gsm_a_rr.timing_advance', + ], + 'tmsi': [ + 'gsm_a.tmsi', + 'gsm_a_dtap.tmsi', + 'gsm_a.dtap.tmsi', + ], + 'imsi': [ + 'gsm_a.imsi', + 'e212.imsi', + 'gsm_a_dtap.imsi', + ], + 'lac': [ + 'gsm_a.lac', + 'e212.lac', + 'gsm_a_bssmap.lac', + ], + 'cid': [ + 'gsm_a.cellid', + 'e212.ci', + 'gsm_a_bssmap.cell_ci', + 'gsm_a.cell_ci', + ], + } + + # Query tshark for all available GSM fields + available_fields = set() + try: + result = subprocess.run( + ['tshark', '-G', 'fields'], + capture_output=True, text=True, timeout=10 + ) + for line in result.stdout.splitlines(): + parts = line.split('\t') + if len(parts) >= 3: + available_fields.add(parts[2]) # Field name is 3rd column + except Exception as e: + logger.warning(f"Could not query tshark fields: {e}") + + # Match each logical field to the first available candidate + resolved = {} + for logical_name, field_candidates in candidates.items(): + resolved[logical_name] = field_candidates[0] # default fallback + if available_fields: + for candidate in field_candidates: + if candidate in available_fields: + resolved[logical_name] = candidate + break + + logger.info(f"Discovered tshark fields: {resolved}") + _tshark_fields_cache = resolved + return resolved + + def _start_monitoring_processes(arfcn: int, device_index: int) -> tuple[subprocess.Popen, subprocess.Popen]: """Start grgsm_livemon and tshark processes for monitoring an ARFCN. @@ -312,18 +390,20 @@ def _start_monitoring_processes(arfcn: int, device_index: int) -> tuple[subproce unregister_process(grgsm_proc) raise FileNotFoundError('tshark not found. Please install wireshark/tshark.') + fields = _discover_tshark_fields() + display_filter = f"{fields['ta']} || {fields['tmsi']} || {fields['imsi']}" tshark_cmd = [ 'tshark', '-i', 'lo', '-l', # Line-buffered output for live capture '-f', 'udp port 4729', # Capture filter: only GSMTAP packets - '-Y', 'gsm_a.rr.timing_advance || gsm_a.tmsi || gsm_a.imsi', + '-Y', display_filter, '-T', 'fields', - '-e', 'gsm_a.rr.timing_advance', - '-e', 'gsm_a.tmsi', - '-e', 'gsm_a.imsi', - '-e', 'gsm_a.lac', - '-e', 'gsm_a.cellid' + '-e', fields['ta'], + '-e', fields['tmsi'], + '-e', fields['imsi'], + '-e', fields['lac'], + '-e', fields['cid'] ] logger.info(f"Starting tshark: {' '.join(tshark_cmd)}") tshark_proc = subprocess.Popen(