From 4431c23d408c017e55e2db923825da7a8369a2dd Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 6 Mar 2024 19:38:41 +0100 Subject: [PATCH] add basic ITU-T T.70 and T.62 LUA dissectors --- osmocom.mate | 4 +- t62.lua | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++ t70.lua | 127 ++++++++++++++++++++++++++ 3 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 t62.lua create mode 100644 t70.lua diff --git a/osmocom.mate b/osmocom.mate index cc692fe..e7dcd77 100644 --- a/osmocom.mate +++ b/osmocom.mate @@ -47,13 +47,13 @@ Pdu rsl_pdu Proto gsm_abis_rsl Transport gsm_ipa/tcp/ip { Criteria Accept Strict (rsl_msg_dsc {4|1|63}); // DCHAN || RLL || IPA }; +/* somehow this definition for the Gop doesn't work, as ip_addr exists only one if source and dest are + * identical */ Gop rsl_lchan On rsl_pdu Match (ip_addr, ip_addr, port, port, rsl_cbits, rsl_tn) { Start (rsl_msg_type = 33); // CHAN_ACT Stop (rsl_msg_type {36|51}); // CHAN_ACT_NACK || RF_CHAN_REL_ACK }; - - /*********************************************************************** * SCCP ***********************************************************************/ diff --git a/t62.lua b/t62.lua new file mode 100644 index 0000000..c594a21 --- /dev/null +++ b/t62.lua @@ -0,0 +1,247 @@ +-- wireshark LUA dissector for the ITU-T T.62 protocol +-- (C) 2024 by Harald Welte +-- SPDX-License-Identifier: GPL-2.0+ +-- +-- Usage: Move this file to your "personal lua plugins" folder that +-- can be found in the Wireshark Help->About Wireshark->Folders tab +-- Windows: %APPDATA%\Wireshark\plugins. +-- Unix-like systems: ~/.local/lib/wireshark/plugins. + +t62_protocol = Proto("T.62", "ITU-T T.62 Control Procedures for Teletex and Group 4 Facsimile Services") + + +local session_cri_names_long = { + -- Table 4/T.62: CRI for session elements + [0x0d] = "Command Session Start", + [0x09] = "Command Session End", + [0x19] = "Command Session Abort", + [0x15] = "Command Session Change Control", + [0x01] = "Command Session User Information", + [0x0e] = "Response Session Start Positive", + [0x0c] = "Response Session Start Negative", + [0x0a] = "Response Session End Positive", + [0x1a] = "Response Session Abort Positive", + [0x16] = "Response Session Change Control Positive", + [0x02] = "Response Session User Information", +} +local session_cri_names = { + -- Table 4/T.62: CRI for session elements + [0x0d] = "CSS", + [0x09] = "CSE", + [0x19] = "CSA", + [0x15] = "CSCC", + [0x01] = "CSUI", + [0x0e] = "RSSP", + [0x0c] = "RSSN", + [0x0a] = "RSEP", + [0x1a] = "RSAP", + [0x16] = "RSCCP", + [0x02] = "RSUI", +} +local hf_session_cri = ProtoField.uint8("t62.session.cri", "Command/Response Identifier", base.HEX, session_cri_names) +local hf_session_li = ProtoField.uint8("t62.session.li", "Length Identifier", base.DEC) + +-- Table 7/T.62 +local session_pi_pgi_names = { + [0x00] = "Reserved for extension", + [0x01] = "Session reference", + [0x09] = "Terminal identifier of the called terminal", + [0x0a] = "Terminal identifier of the calling terminal", + [0x0b] = "Date and time", + [0x0c] = "Additional session reference number", + [0x02] = "Non-basic session capabilities", + [0x0d] = "Miscellaneous session capabilities", + [0x0e] = "Window size", + -- No PGI associated with these PIs + [0x08] = "Service identifier", + [0x10] = "Session control functions", + [0x11] = "Session termination parameter", + [0x12] = "Inactivity timer", + [0x14] = "Session service functions", + [0x32] = "Reason", + [0x41] = "Non-basic Teletex terminal capabilities", + [0x49] = "Control character set", + [0x4a] = "Teletex page format", + [0x4b] = "Miscellaneous Teletex terminal capabilities", + [0xc1] = "Session user data", + [0xe0] = "Private use 0", + [0xe1] = "Private use 1", + [0xe2] = "Private use 2", + [0xe3] = "Private use 3", + [0xe4] = "Private use 4", + [0xe5] = "Private use 5", + [0xe6] = "Private use 6", + [0xe7] = "Private use 7", + [0xe8] = "Non-standardixed capabilities", +} +local hf_session_pi_pgi = ProtoField.uint8("t64.session.pi", "Parameter (Group) Identifier", base.HEX, session_pi_pgi_names) + +local doc_cri_names_long = { + -- Table 5/T.62: CRI for document command identifiers + [0x2d] = "Command Document Start", + [0x1d] = "Command Document Continue", + [0x29] = "Command Document End", + [0x19] = "Command Document Resynchronize", + [0x39] = "Command Document Discard", + [0x31] = "Comman Document Page Boundary", + [0x3d] = "Command Document Capability List", + [0x01] = "Command Document User Information", + -- Table 6/T.62: CRI for document response identifiers + [0x2a] = "Response Document End Potve", + [0x1a] = "Response Document Resynchronize Positive", + [0x3a] = "Response Document Discard Positive", + [0x32] = "Response Document Page Boundary Positive", + [0x30] = "Response Document Page Boundary Negative", + [0x3e] = "Response Document Capability List Positive", + [0x00] = "Resposne Documnet General Reject", +} +local doc_cri_names = { + -- Table 5/T.62: CRI for document command identifiers + [0x2d] = "CDS", + [0x1d] = "CDC", + [0x29] = "CDE", + [0x19] = "CDR", + [0x39] = "CDD", + [0x31] = "CDPB", + [0x3d] = "CDCL", + [0x01] = "CDUI", + -- Table 6/T.62: CRI for document response identifiers + [0x2a] = "RDEP", + [0x1a] = "RDRP", + [0x3a] = "RDDP", + [0x32] = "RDPBP", + [0x30] = "RDPBN", + [0x3e] = "RDCLP", + [0x00] = "RDGR", +} +local hf_document_cri = ProtoField.uint8("t62.document.cri", "Command/Response Identifier", base.HEX, doc_cri_names) +local hf_document_li = ProtoField.uint8("t62.document.li", "Length Identifier", base.DEC) + +local hf_document_data = ProtoField.bytes("t62.document.data", "Data") + +-- Table 8/T.62 +local doc_pi_pgi_names = { + [0x20] = "Reserved fro extension", + [0x21] = "Document linking", + [0x09] = "Terminal identifier of the called terminal", + [0x0a] = "Terminal identifier of the calling terminal", + [0x0b] = "Date and time", + [0x0c] = "Additional session reference number", + [0x29] = "Document reference number", + [0x2a] = "Checkpoint reference number", + -- no PGI associated wih these PI + [0x12] = "Inactivity timer", + [0x28] = "Service interworking identifier", + [0x29] = "Document reference number", + [0x2a] = "Checkpoint reference number", + [0x2c] = "Acceptance of CDCL parameters", + [0x2d] = "Storage capacity negotiation", + [0x2e] = "Receiving ability jeopardized", + [0x2f] = "Reserved", + [0x30] = "Document type identifier", + [0x31] = "Reflect parameter values", + [0x32] = "Reason", + [0x40] = "Reserved for extension", + [0x41] = "Non-basic Teletex terminal capabilities", + [0x48] = "Graphic character set", + [0x49] = "Control character set", + [0x4a] = "Teletex page format", + [0x4b] = "Miscellaneous Teletex terminal capabilities", + [0x4d] = "Character box height", + [0x4e] = "Character box width", + [0xc1] = "Session user data", + [0xe0] = "Private use 0", + [0xe1] = "Private use 1", + [0xe2] = "Private use 2", + [0xe3] = "Private use 3", + [0xe4] = "Private use 4", + [0xe5] = "Private use 5", + [0xe6] = "Private use 6", + [0xe7] = "Private use 7", + [0xe8] = "Non-standardized capabilities", +} +local hf_doc_pi_pgi = ProtoField.uint8("t64.document.pi", "Parameter (Group) Identifier", base.HEX, doc_pi_pgi_names) + +-- 5.7.2.10 Reason (session or document) +local reason_names = { + [0x00] = "No specific reason stated (used for session or document reasons other than those listed)", + [0x01] = "Temporarily unable to enter into, or to continue, a session (e.g. due to memory full or out of recording paper)", + [0x02] = "Explicit text message only for use with RSSN", + [0x03] = "Sequence error", + [0x05] = "Local terminal error", + [0x06] = "Unrecoverable procedural error", +} + +-- 5.7.3.7 Document type identiifer +local doc_type_id_names = { + [0x01] = "Operator document", + [0x02] = "Control document", + [0x03] = "Monitor document", +} + +-- Table 11/T.62 +local teletex_page_format_names = { + [0x01] = "ISO A4, horizontal and vertical", + [0x02] = "North American, horizontal and vertical", + [0x84] = "ISO A4 extended (ISO standard 3535), vertical", + [0x44] = "ISO A4 extended (ISO standard 3535), horizontal", + [0x88] = "North American legal, vertical", + [0x48] = "North American legal, horizontal", + [0x03] = "ISO A4, horizontal and vertical (for use by Japanese Kanji and Chinese ideogram terminals)", + [0x10] = "ISO B5, horizontal and vertical (for use by Japanese Kanji and Chinese ideogram terminals)", + [0x20] = "ISO B4, horizontal and vertical (for use by Japanese Kanji and Chinese ideogram terminals)", +} + +t62_protocol.fields = { + hf_session_cri, hf_session_li, hf_session_pi_pgi, + hf_document_cri, hf_document_li, hf_doc_pi_pgi, + hf_document_data, +} + +function decode_t62_document(buffer, pinfo, tree) + local d_cri = buffer(0, 1):uint() + local d_li = buffer(1, 1):uint() + + pinfo.cols.info:append(" " .. doc_cri_names[d_cri]) + tree:append_text(" " .. doc_cri_names[d_cri] .. "(" .. doc_cri_names_long[d_cri] .. ")") + tree:add(hf_document_cri, buffer(0, 1)) + tree:add(hf_document_li, buffer(1, 1)) + + if (d_li > 0) then + -- FIXME: document parameters + end + + -- user data only exists in CDUI + if (d_cri == 0x01) then + tree:add(hf_document_data, buffer(2)) + end +end + +function t62_protocol.dissector(buffer, pinfo, tree) + pinfo.cols.protocol = t62_protocol.name + + local subtree = tree:add(t62_protocol, buffer(), "T.62") + local offset = 0 + + local s_cri = buffer(0, 1):uint() + local s_li = buffer(1, 1):uint() + + pinfo.cols.info:append(" " .. session_cri_names[s_cri]) + subtree:append_text(" " .. session_cri_names[s_cri] .. " (" .. session_cri_names_long[s_cri] .. ")") + subtree:add(hf_session_cri, buffer(0, 1)) + subtree:add(hf_session_li, buffer(1, 1)) + + if (s_li > 0) then + -- FIXME: session parameters + end + + -- document layer data only exists in CSUI / RSUI + if (s_cri == 0x01 or s_cri == 0x02) then + decode_t62_document(buffer(2+s_li), pinfo, tree) + end +end + +function t62_protocol.init() + local t70_dissectors = DissectorTable.get("t70.subdissector") + t70_dissectors:add_for_decode_as(t62_protocol) +end diff --git a/t70.lua b/t70.lua new file mode 100644 index 0000000..b82375b --- /dev/null +++ b/t70.lua @@ -0,0 +1,127 @@ +-- wireshark LUA dissector for the ITU-T T.70 protocol +-- (C) 2024 by Harald Welte +-- SPDX-License-Identifier: GPL-2.0+ +-- +-- Usage: Move this file to your "personal lua plugins" folder that +-- can be found in the Wireshark Help->About Wireshark->Folders tab +-- Windows: %APPDATA%\Wireshark\plugins. +-- Unix-like systems: ~/.local/lib/wireshark/plugins. + +t70_protocol = Proto("T.70", "ITU-T T.70 Network-Independent Basic Transport Service for the Telematic services") + +t70_subdissectors = DissectorTable.new("t70.subdissector", "T.70 payload subdissector", ftypes.NONE, base.NONE, t70_protocol) + +local hf_net_block_hdr_len = ProtoField.uint8("t70.net_block_hdr_len", "Network Block Header Length", base.DEC) +local hf_net_block_hdr = ProtoField.bytes("t70.net_block_hdr", "Network Block Header") +local hf_net_block_hdr_m = ProtoField.bool("70.net_block_hdr.m", "M(ore data mark) bit", base.HEX, nil, 0x80) +local hf_net_block_hdr_q = ProtoField.bool("70.net_block_hdr.q", "Q(ualifier) bit", base.HEX, nil, 0x40) + +local hf_src_ref = ProtoField.uint16("t70.src_ref", "Source Reference", base.HEX) +local hf_dst_ref = ProtoField.uint16("t70.dst_ref", "Destination Reference", base.HEX) + +-- Fogure 15/T.70 +local reject_cause_names = { + [0x00] = "Reason not specified", + [0x01] = "Function not implemented", + [0x02] = "Invalid block", + [0x03] = "Invalid parameter", +} +local hf_rej_cause = ProtoField.uint8("t70.rej_cause", "Reject Cause", base.HEX, reject_cause_names) +local hf_block_len = ProtoField.uint8("t70.block_len", "Block Length", base.DEC) +local block_types = { + [0xe0] = "TCR", + [0xd0] = "TCA", + [0x80] = "TCC", + [0x70] = "TBR", + [0xf0] = "TDT", +} +local block_types_long = { + [0xe0] = "Transport Connection Request", + [0xd0] = "Transport Connection Accept", + [0x80] = "Transport Connection Clear", + [0x70] = "Transport Block Reject", + [0xf0] = "Transport Data", +} +local hf_block_type = ProtoField.uint8("t70.block_type", "Block Type", base.HEX, block_types, 0xff) +local hf_data = ProtoField.bytes("t70.data", "Data") + +local param_names = { + [0xc0] = "Transport data block size", -- Figure 11/T.70 + [0xc1] = "Calling address", -- Figure 10/T.70 (but also Figure 16/T.70: Rejected block) + [0xc2] = "Called address", -- Fiure 10/T.70 +} + +-- Figure 13/T.70 +local clearing_cause_names = { + [0x00] = "Reason not specified", + [0x01] = "Terminal occupied", + [0x02] = "Terminal out of order", + [0x03] = "Address unknown", +} +local hf_clearing_cause = ProtoField.uint8("t70.clearing_cause", "Clearing Cause", base.HEX, clearing_cause_names) + +local hf_tsdu_end_mark = ProtoField.bool("t70.tsdu_end_mark", "TSDU end mark", base.HEX, nil, 0x80) + +t70_protocol.fields = { + hf_net_block_hdr_len, hf_net_block_hdr, hf_net_block_hdr_m, hf_net_block_hdr_q, hf_tsdu_end_mark, + hf_src_ref, hf_dst_ref, hf_rej_cause, hf_block_len, hf_block_type, hf_data, hf_clearing_cause, +} + +function dissect_block(buffer, pinfo, tree, maintree) + local blen = buffer(0, 1):uint() + local btype = buffer(1, 1):uint() + + pinfo.cols.info = block_types[btype] + tree:append_text(" " .. block_types[btype] .. " (" .. block_types_long[btype] .. ")") + + tree:add(hf_block_len, buffer(0, 1)) + tree:add(hf_block_type, buffer(1, 1)) + + if (btype == 0xe0) then -- TCR (Section 5.5.4) + tree:add(hf_src_ref, buffer(4, 2)) + -- TODO: parameters (calling, called, block size) + elseif (btype == 0xd0) then -- TCA (Section 5.5.5) + tree:add(hf_dst_ref, buffer(2, 2)) + tree:add(hf_src_ref, buffer(4, 2)) + -- TODO: parameters (calling, called, block size) + elseif (btype == 0x80) then -- TCC (Section 5.5.6) + tree:add(hf_dst_ref, buffer(2, 2)) + tree:add(hf_src_ref, buffer(4, 2)) + tree:add(hf_clearing_cause, buffer(6, 1)) + -- TODO: parameters (addl clearing info) + elseif (btype == 0x70) then -- TBR (Section 5.5.7) + tree:add(hf_dst_ref, buffer(2, 2)) + tree:add(hf_rej_cause, buffer(4, 1)) + -- TODO: parameters (rejected block [mandatory]) + elseif (btype == 0xf0) then -- TDT (Section 5.5.8) + -- TSDU end mark + tree:add(hf_tsdu_end_mark, buffer(2, 1)) + -- data + if (not t70_subdissectors:try(0, buffer(3):tvb(), pinfo, maintree)) then + tree:add(hf_data, buffer(3)) + end + else + -- unknown + end +end + +function t70_protocol.dissector(buffer, pinfo, tree) + pinfo.cols.protocol = t70_protocol.name + + local subtree = tree:add(t70_protocol, buffer(), "T.70") + local net_bhl = buffer(0, 1):uint() + + -- 2-byte network block header, see Figure 4/T.70 + subtree:add(hf_net_block_hdr_len, buffer(0, 1)) + subtree:add(hf_net_block_hdr, buffer(1, net_bhl)) + subtree:add(hf_net_block_hdr_m, buffer(1, 1)) + subtree:add(hf_net_block_hdr_q, buffer(1, 1)) + + -- actual block + dissect_block(buffer(1+net_bhl), pinfo, subtree, tree) +end + +function t70_protocol.init() + local x75_dissectors = DissectorTable.get("x75.subdissector") + x75_dissectors:add_for_decode_as(t70_protocol) +end