2 * Routines for packet dissection of
3 * ETSI TS 102 127 v6.13.0 (Release 6 / 2009-0r45)
4 * Card Application Toolkit - Transport Protocol over UDP
6 * Copyright 2014-2014 by Sebastian Kloeppel <sk [at] nakedape.net>
7 * Cristina E. Vintila <cristina.vintila [at] gmail.com>
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include <epan/packet.h>
18 #include <epan/prefs.h>
19 #include <epan/expert.h>
20 #include <epan/in_cksum.h>
21 #include <epan/unit_strings.h>
23 #define CATTP_SHORTNAME "CAT-TP"
24 #define CATTP_HBLEN 18
33 /* bit masks for the first header byte. */
34 #define M_FLAGS 0xFC /* flags only, no version */
35 #define M_PDU_SYN 0xB8 /* SYN (ACK, SEG don't care) without version */
36 #define M_PDU_ACK 0xD0 /* ACK (EAK, SEG, NUL don't care) without version */
37 #define M_PDU_RST 0xBC /* RST (ACK don't care) without version */
38 #define M_VERSION 0x03 /* only Version */
40 #define ICCID_PREFIX 0x98
42 static dissector_handle_t cattp_handle
;
44 static int proto_cattp
;
47 static int ett_cattp_id
;
48 static int ett_cattp_flags
;
49 static int ett_cattp_eaks
;
51 static int hf_cattp_flags
;
54 static int hf_cattp_flag_syn
;
55 static int hf_cattp_flag_ack
;
56 static int hf_cattp_flag_eak
;
57 static int hf_cattp_flag_rst
;
58 static int hf_cattp_flag_nul
;
59 static int hf_cattp_flag_seg
;
60 static int hf_cattp_version
;
62 /* structure of flag components */
63 static int * const cattp_flags
[] = {
74 static int hf_cattp_hlen
;
75 static int hf_cattp_srcport
;
76 static int hf_cattp_dstport
;
77 static int hf_cattp_datalen
;
78 static int hf_cattp_seq
;
79 static int hf_cattp_ack
;
80 static int hf_cattp_windowsize
;
81 static int hf_cattp_checksum
;
82 static int hf_cattp_checksum_status
;
83 static int hf_cattp_identification
;
84 static int hf_cattp_iccid
;
85 static int hf_cattp_idlen
;
86 static int hf_cattp_maxpdu
;
87 static int hf_cattp_maxsdu
;
88 static int hf_cattp_rc
;
89 static int hf_cattp_eaklen
;
90 static int hf_cattp_eaks
;
92 static expert_field ei_cattp_checksum
;
94 /* Preference to control whether to check the CATTP checksum */
95 static bool cattp_check_checksum
= true;
97 /* Reason code mapping */
98 static const value_string cattp_reset_reason
[] = {
99 { 0, "Normal Ending" },
100 { 1, "Connection set-up failed, illegal parameters" },
101 { 2, "Temporarily unable to set up this connection" },
102 { 3, "Requested Port not available" },
103 { 4, "Unexpected PDU received" },
104 { 5, "Maximum retries exceeded" },
105 { 6, "Version not supported" },
110 static const unit_name_string units_pdu
= { "PDU", "PDUs" };
112 /* Forward declaration due to use of heuristic dissection preference. */
113 void proto_reg_handoff_cattp(void);
114 void proto_register_cattp(void);
116 /* Dissection of SYN PDUs */
118 dissect_cattp_synpdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*cattp_tree
, uint32_t offset
)
120 proto_item
*idi
, *id_tree
;
123 proto_tree_add_item(cattp_tree
, hf_cattp_maxpdu
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
126 proto_tree_add_item(cattp_tree
, hf_cattp_maxsdu
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
129 idlen
= tvb_get_uint8(tvb
, offset
);
130 idi
= proto_tree_add_uint(cattp_tree
, hf_cattp_idlen
, tvb
, offset
, 1, idlen
);
133 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " IdLen=%u ", idlen
);
135 id_tree
= proto_item_add_subtree(idi
, ett_cattp_id
);
138 uint8_t first_id_byte
;
140 first_id_byte
= tvb_get_uint8(tvb
, offset
);
141 proto_tree_add_item(id_tree
, hf_cattp_identification
, tvb
, offset
, idlen
, ENC_NA
);
143 /* Optional code. Checks whether identification field may be an ICCID.
144 * It has to be considered to move this logic to another layer / dissector.
145 * However it is common to send ICCID as Identification for OTA download. */
146 if (idlen
<= 10 && idlen
>= 9 && ICCID_PREFIX
== first_id_byte
) {
150 buf
= wmem_strbuf_new(pinfo
->pool
, "");
153 for (i
= 0; i
< idlen
; i
++) {
156 c
= tvb_get_uint8(tvb
, offset
+ i
);
157 n
= ((c
& 0xF0) >> 4) + ((c
& 0x0F) << 4);
158 wmem_strbuf_append_printf(buf
, "%02X", n
);
161 proto_tree_add_string(id_tree
, hf_cattp_iccid
, tvb
, offset
,
162 idlen
, wmem_strbuf_get_str(buf
));
169 /* Dissection of Extended Acknowledgement PDUs */
171 dissect_cattp_eakpdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*cattp_tree
, uint32_t offset
, uint8_t hlen
)
176 eak_count
= (hlen
- offset
) >> 1;
177 eaki
= proto_tree_add_uint(cattp_tree
, hf_cattp_eaklen
, tvb
, offset
, eak_count
* 2, eak_count
);
180 proto_item
*eak_tree
;
183 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " EAKs=%u", eak_count
);
184 eak_tree
= proto_item_add_subtree(eaki
, ett_cattp_eaks
);
186 for (i
= 0; i
< eak_count
; i
++) {
187 proto_tree_add_item(eak_tree
, hf_cattp_eaks
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
195 /* Dissection of Extended Acknowledgement PDUs */
197 dissect_cattp_rstpdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*cattp_tree
, uint32_t offset
)
202 rc
= tvb_get_uint8(tvb
, offset
); /* reason code of RST */
203 rc_str
= val_to_str(rc
, cattp_reset_reason
, "Unknown reason code: 0x%02x");
204 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " Reason=\"%s\" ", rc_str
);
206 proto_tree_add_item(cattp_tree
, hf_cattp_rc
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
210 /* Dissection of the base header */
212 dissect_cattp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
214 const char *pdutype
= "[Unknown PDU]";
215 proto_item
*ti
, *cattp_tree
;
219 unsigned cksum_data_len
;
220 uint8_t flags
, first_byte
, hlen
, ver
;
221 uint16_t plen
, ackno
, seqno
, wsize
, sport
, dport
;
223 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, CATTP_SHORTNAME
);
225 /* Clear out stuff in the info column */
226 col_clear(pinfo
->cinfo
, COL_INFO
);
228 hlen
= tvb_get_uint8(tvb
, 3); /* lookahead header len. */
231 ti
= proto_tree_add_protocol_format(tree
, proto_cattp
, tvb
, offset
, hlen
,
232 "Card Application Toolkit Transport Protocol");
234 cattp_tree
= proto_item_add_subtree(ti
, ett_cattp
);
236 /* render flags tree */
237 first_byte
= tvb_get_uint8(tvb
, offset
);
238 flags
= first_byte
& M_FLAGS
; /* discard version from first byte for flags */
239 ver
= first_byte
& M_VERSION
; /* discard flags for version */
240 proto_tree_add_bitmask(cattp_tree
, tvb
, offset
, hf_cattp_flags
, ett_cattp_flags
, cattp_flags
, ENC_BIG_ENDIAN
);
241 offset
+= 3; /* skip RFU and header len */
243 /* Header length, varies for SYN(identification) and EAKs */
244 proto_tree_add_uint(cattp_tree
, hf_cattp_hlen
, tvb
, offset
, 1, hlen
);
247 /* Parse cattp source port. */
248 sport
= tvb_get_ntohs(tvb
, offset
);
249 proto_tree_add_uint(cattp_tree
, hf_cattp_srcport
, tvb
, offset
, 2, sport
);
252 /* Parse cattp destination port. */
253 dport
= tvb_get_ntohs(tvb
, offset
);
254 proto_tree_add_uint(cattp_tree
, hf_cattp_dstport
, tvb
, offset
, 2, dport
);
257 proto_item_append_text(ti
, " (v%u, Src Port: %u, Dst Port: %u)", ver
, sport
, dport
);
258 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%u > %u ", sport
, dport
);
260 /* Parse length of payload. */
261 plen
= tvb_get_ntohs(tvb
, offset
);
262 proto_tree_add_uint(cattp_tree
, hf_cattp_datalen
, tvb
, offset
, 2, plen
);
265 /* Parse sequence number. */
266 seqno
= tvb_get_ntohs(tvb
, offset
);
267 proto_tree_add_uint(cattp_tree
, hf_cattp_seq
, tvb
, offset
, 2, seqno
);
270 /* Parse acknowledgement number. */
271 ackno
= tvb_get_ntohs(tvb
, offset
);
272 proto_tree_add_uint(cattp_tree
, hf_cattp_ack
, tvb
, offset
, 2, ackno
);
275 /* Parse window size. */
276 wsize
= tvb_get_ntohs(tvb
, offset
);
277 proto_tree_add_uint(cattp_tree
, hf_cattp_windowsize
, tvb
, offset
, 2, wsize
);
281 pdutype
= "[SYN PDU]";
282 else if (flags
& F_ACK
)
283 pdutype
= "[ACK PDU]";
284 else if (flags
& F_RST
)
285 pdutype
= "[RST PDU]";
287 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%s Flags=0x%02X Ack=%u Seq=%u WSize=%u", pdutype
, flags
, ackno
, seqno
, wsize
);
289 /* Parse and verify checksum */
291 cksum_data_len
= hlen
+ plen
;
292 if (!cattp_check_checksum
) {
293 /* We have turned checksum checking off; we do NOT checksum it. */
294 proto_tree_add_checksum(cattp_tree
, tvb
, offset
, hf_cattp_checksum
, hf_cattp_checksum_status
, &ei_cattp_checksum
,
295 pinfo
, 0, ENC_BIG_ENDIAN
, PROTO_CHECKSUM_NO_FLAGS
);
297 /* We haven't turned checksum checking off; checksum it. */
299 /* Unlike TCP, CATTP does not make use of a pseudo-header for checksum */
300 SET_CKSUM_VEC_TVB(cksum_vec
[0], tvb
, header_offset
, cksum_data_len
);
301 proto_tree_add_checksum(cattp_tree
, tvb
, offset
, hf_cattp_checksum
, hf_cattp_checksum_status
, &ei_cattp_checksum
,
302 pinfo
, in_cksum(cksum_vec
, 1), ENC_BIG_ENDIAN
, PROTO_CHECKSUM_VERIFY
|PROTO_CHECKSUM_IN_CKSUM
);
303 } /* End of checksum code */
307 offset
= dissect_cattp_synpdu(tvb
, pinfo
, cattp_tree
, offset
);
308 else if (flags
& F_EAK
)
309 offset
= dissect_cattp_eakpdu(tvb
, pinfo
, cattp_tree
, offset
, hlen
);
310 else if (flags
& F_RST
)
311 offset
= dissect_cattp_rstpdu(tvb
, pinfo
, cattp_tree
, offset
);
312 /* for other PDU types nothing special to be displayed in detail tree. */
314 if (plen
> 0) { /* Call generic data handle if data exists. */
315 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " DataLen=%u", plen
);
316 tvb
= tvb_new_subset_remaining(tvb
, offset
);
317 call_data_dissector(tvb
, pinfo
, tree
);
319 return tvb_captured_length(tvb
);
322 /* The heuristic dissector function checks if the UDP packet may be a cattp packet */
324 dissect_cattp_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
326 if (tvb_captured_length(tvb
) >= CATTP_HBLEN
) { /* check of data is big enough for base header. */
327 uint8_t flags
, ver
, hlen
;
330 hlen
= tvb_get_uint8(tvb
, 3); /* header len */
331 plen
= tvb_get_ntohs(tvb
, 8); /* payload len */
333 if (hlen
+plen
!= tvb_reported_length(tvb
)) /* check if data length is ok. */
336 /* ETSI TS 102 127 V15.0.0 and earlier releases say explicitly that
337 the version bits must be 0. */
338 ver
= tvb_get_uint8(tvb
, 0) & M_VERSION
;
342 flags
= tvb_get_uint8(tvb
, 0) & M_FLAGS
;
343 if ( (flags
& M_PDU_SYN
) == F_SYN
||
344 (flags
& M_PDU_RST
) == F_RST
||
345 (flags
& M_PDU_ACK
) == F_ACK
) { /* check if flag combi is valid */
346 dissect_cattp(tvb
, pinfo
, tree
, data
);
353 /* Function to register the dissector, called by infrastructure. */
355 proto_register_cattp(void)
357 static hf_register_info hf
[] = {
361 "Flags", "cattp.flags", FT_UINT8
, BASE_HEX
, NULL
, 0x0,
368 "Synchronize Flag", "cattp.flags.syn", FT_UINT8
, BASE_DEC
, NULL
, F_SYN
,
375 "Acknowledge Flag", "cattp.flags.ack", FT_UINT8
, BASE_DEC
, NULL
, F_ACK
,
382 "Extended Acknowledge Flag", "cattp.flags.eak", FT_UINT8
, BASE_DEC
, NULL
, F_EAK
,
389 "Reset Flag", "cattp.flags.rst", FT_UINT8
, BASE_DEC
, NULL
, F_RST
,
396 "NULL Flag", "cattp.flags.nul", FT_UINT8
, BASE_DEC
, NULL
, F_NUL
,
403 "Segmentation Flag", "cattp.flags.seg", FT_UINT8
, BASE_DEC
, NULL
, F_SEG
,
410 "Version", "cattp.version", FT_UINT8
, BASE_HEX
, NULL
, M_VERSION
,
417 "Header Length", "cattp.hlen", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
424 "Source Port", "cattp.srcport", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
431 "Destination Port", "cattp.dstport", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
438 "Data Length", "cattp.datalen", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
445 "Sequence Number", "cattp.seq", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
452 "Acknowledgement Number", "cattp.ack", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
457 &hf_cattp_windowsize
,
459 "Window Size", "cattp.windowsize", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
466 "Checksum", "cattp.checksum", FT_UINT16
, BASE_HEX
, NULL
, 0x0,
471 &hf_cattp_checksum_status
,
473 "Checksum Status", "cattp.checksum.status", FT_UINT8
, BASE_NONE
, VALS(proto_checksum_vals
), 0x0,
478 &hf_cattp_identification
,
480 "Identification", "cattp.identification", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
487 "ICCID", "cattp.iccid", FT_STRING
, BASE_NONE
, NULL
, 0x0,
494 "Maxpdu", "cattp.maxpdu", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
501 "Maxsdu", "cattp.maxsdu", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
508 "Reason Code", "cattp.rc", FT_UINT8
, BASE_DEC
, VALS(cattp_reset_reason
), 0x0,
515 "Identification Length", "cattp.idlen", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
522 "Acknowledgement Number", "cattp.eak", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
529 "Extended Acknowledgement Numbers", "cattp.eaks", FT_UINT16
, BASE_DEC
|BASE_UNIT_STRING
, UNS(&units_pdu
), 0x0,
535 /* Setup protocol subtree array */
536 static int *ett
[] = {
543 static ei_register_info ei
[] = {
544 { &ei_cattp_checksum
, { "cattp.bad_checksum", PI_CHECKSUM
, PI_ERROR
, "Bad checksum", EXPFILL
}},
547 module_t
*cattp_module
;
548 expert_module_t
* expert_cattp
;
550 proto_cattp
= proto_register_protocol (
551 "ETSI Card Application Toolkit Transport Protocol", /* name */
552 CATTP_SHORTNAME
, /* short name */
556 proto_register_field_array(proto_cattp
, hf
, array_length(hf
));
557 proto_register_subtree_array(ett
, array_length(ett
));
558 expert_cattp
= expert_register_protocol(proto_cattp
);
559 expert_register_field_array(expert_cattp
, ei
, array_length(ei
));
561 cattp_module
= prefs_register_protocol(proto_cattp
, NULL
);
562 prefs_register_bool_preference(cattp_module
, "checksum",
563 "Validate checksum of all messages",
564 "Whether the checksum of all messages should be validated or not",
565 &cattp_check_checksum
);
567 prefs_register_obsolete_preference(cattp_module
, "enable");
569 /* Register dissector handle */
570 cattp_handle
= register_dissector("cattp", dissect_cattp
, proto_cattp
);
576 proto_reg_handoff_cattp(void)
578 heur_dissector_add("udp", dissect_cattp_heur
, "CAT-TP over UDP", "cattp_udp", proto_cattp
, HEURISTIC_DISABLE
);
579 dissector_add_for_decode_as_with_preference("udp.port", cattp_handle
);
583 * Editor modelines - https://www.wireshark.org/tools/modelines.html
588 * indent-tabs-mode: nil
591 * vi: set shiftwidth=4 tabstop=8 expandtab:
592 * :indentSize=4:tabSize=8:noTabs=true: