/* packet-ositp.c * Routines for ISO/OSI transport protocol (connection-oriented * and connectionless) packet disassembly * * $Id$ * Laurent Deniel * Ralf Schneider * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include #include #include "packet-frame.h" #include "packet-osi.h" #include "packet-osi-options.h" #include "packet-isis.h" #include "packet-esis.h" #include #include #include #include /* protocols and fields */ static int proto_clnp; static int proto_cotp = -1; static gint ett_cotp = -1; static gint ett_cotp_segments = -1; static gint ett_cotp_segment = -1; static int hf_cotp_li = -1; static int hf_cotp_type = -1; static int hf_cotp_srcref = -1; static int hf_cotp_destref = -1; static int hf_cotp_class = -1; static int hf_cotp_opts_extended_formats = -1; static int hf_cotp_opts_no_explicit_flow_control = -1; static int hf_cotp_tpdu_number = -1; static int hf_cotp_tpdu_number_extended = -1; static int hf_cotp_next_tpdu_number = -1; static int hf_cotp_next_tpdu_number_extended = -1; static int hf_cotp_eot = -1; static int hf_cotp_eot_extended = -1; static int hf_cotp_segments = -1; static int hf_cotp_segment = -1; static int hf_cotp_segment_overlap = -1; static int hf_cotp_segment_overlap_conflict = -1; static int hf_cotp_segment_multiple_tails = -1; static int hf_cotp_segment_too_long_segment = -1; static int hf_cotp_segment_error = -1; static int hf_cotp_segment_count = -1; static int hf_cotp_reassembled_in = -1; static int hf_cotp_reassembled_length = -1; static const true_false_string fragment_descriptions = { "Yes", "No" }; static int proto_cltp = -1; static gint ett_cltp = -1; static int hf_cltp_li = -1; static int hf_cltp_type = -1; static const fragment_items cotp_frag_items = { &ett_cotp_segment, &ett_cotp_segments, &hf_cotp_segments, &hf_cotp_segment, &hf_cotp_segment_overlap, &hf_cotp_segment_overlap_conflict, &hf_cotp_segment_multiple_tails, &hf_cotp_segment_too_long_segment, &hf_cotp_segment_error, &hf_cotp_segment_count, &hf_cotp_reassembled_in, &hf_cotp_reassembled_length, /* Reassembled data field */ NULL, "segments" }; static dissector_handle_t data_handle; /* * ISO8073 OSI COTP definition * See http://standards.iso.org/ittf/PubliclyAvailableStandards/index.html * (or RFC905 for historic, and now-outdated information) */ /* don't use specific TPDU types to avoid alignment problems & copy overhead */ /* TPDU definition */ #define ED_TPDU 0x1 /* COTP */ #define EA_TPDU 0x2 /* COTP */ #define UD_TPDU 0x4 /* CLTP */ #define RJ_TPDU 0x5 /* COTP */ #define AK_TPDU 0x6 /* COTP */ #define ER_TPDU 0x7 /* COTP */ #define DR_TPDU 0x8 /* COTP */ #define DC_TPDU 0xC /* COTP */ #define CC_TPDU 0xD /* COTP */ #define CR_TPDU 0xE /* COTP */ #define DT_TPDU 0xF /* COTP */ static const value_string cotp_tpdu_type_abbrev_vals[] = { { ED_TPDU, "ED Expedited Data" }, { EA_TPDU, "EA Expedited Data Acknowledgement" }, { RJ_TPDU, "RJ Reject" }, { AK_TPDU, "AK Data Acknowledgement" }, { ER_TPDU, "ER TPDU Error" }, { DR_TPDU, "DR Disconnect Request" }, { DC_TPDU, "DC Disconnect Confirm" }, { CC_TPDU, "CC Connect Confirm" }, { CR_TPDU, "CR Connect Request" }, { DT_TPDU, "DT Data" }, { 0, NULL } }; static const value_string cltp_tpdu_type_abbrev_vals[] = { { UD_TPDU, "UD" }, { 0, NULL } }; static const value_string class_option_vals[] = { {0, "Class 0"}, {1, "Class 1"}, {2, "Class 2"}, {3, "Class 3"}, {4, "Class 4"}, {0, NULL} }; /* field position */ #define P_LI 0 #define P_TPDU 1 #define P_CDT 1 #define P_DST_REF 2 #define P_SRC_REF 4 #define P_TPDU_NR_0_1 2 #define P_TPDU_NR_234 4 #define P_VAR_PART_NDT 5 #define P_VAR_PART_EDT 8 #define P_VAR_PART_DC 6 #define P_CDT_IN_AK 8 #define P_CDT_IN_RJ 8 #define P_REJECT_ER 4 #define P_REASON_IN_DR 6 #define P_CLASS_OPTION 6 /* * TPDU length indicator values. * Checksum parameter is 4 octets - 1 octet of parameter code, 1 octet * of parameter length, 2 octets of checksum. */ #define LI_NORMAL_DT_CLASS_01 2 #define LI_NORMAL_DT_WITHOUT_CHECKSUM 4 #define LI_NORMAL_DT_WITH_CHECKSUM (LI_NORMAL_DT_WITHOUT_CHECKSUM+4) #define LI_EXTENDED_DT_WITHOUT_CHECKSUM 7 #define LI_EXTENDED_DT_WITH_CHECKSUM (LI_EXTENDED_DT_WITHOUT_CHECKSUM+4) #define LI_NORMAL_EA_WITHOUT_CHECKSUM 4 #define LI_NORMAL_EA_WITH_CHECKSUM (LI_NORMAL_EA_WITHOUT_CHECKSUM+4) #define LI_EXTENDED_EA_WITHOUT_CHECKSUM 7 #define LI_EXTENDED_EA_WITH_CHECKSUM (LI_EXTENDED_EA_WITHOUT_CHECKSUM+4) #define LI_NORMAL_RJ 4 #define LI_EXTENDED_RJ 9 #define LI_MIN_DR 6 #define LI_MAX_DC 9 #define LI_MAX_AK 27 #define LI_MAX_EA 11 #define LI_MAX_ER 8 /* XXX - can we always decide this based on whether the length indicator is odd or not? What if the variable part has an odd number of octets? */ #define is_LI_NORMAL_AK(p) ((p & 0x01) == 0) /* * Modified TPDU length indicator values due to ATN 4-octet extended * checksum. * Checksum parameter is 6 octets - 1 octet of parameter code, 1 octet * of parameter length, 4 octets of checksum. That adds 2 octets to * the lengths with a 2-octet checksum. */ #define LI_ATN_NORMAL_DT_WITH_CHECKSUM (LI_NORMAL_DT_WITH_CHECKSUM+2) #define LI_ATN_EXTENDED_DT_WITH_CHECKSUM (LI_EXTENDED_DT_WITH_CHECKSUM+2) #define LI_ATN_NORMAL_EA_WITH_CHECKSUM (LI_NORMAL_EA_WITH_CHECKSUM+2) #define LI_ATN_EXTENDED_EA_WITH_CHECKSUM (LI_EXTENDED_EA_WITH_CHECKSUM+2) #define LI_ATN_NORMAL_RJ (LI_NORMAL_RJ+2) #define LI_ATN_EXTENDED_RJ (LI_EXTENDED_RJ+2) #define LI_ATN_MAX_DC (LI_MAX_DC+2) #define LI_ATN_MAX_AK (LI_MAX_AK+2+1) /* +1 for padding? */ #define LI_ATN_MAX_EA (LI_MAX_EA+2) #define LI_ATN_MAX_ER (LI_MAX_ER+2) /* variant part */ #define VP_ACK_TIME 0x85 #define VP_RES_ERROR 0x86 #define VP_PRIORITY 0x87 #define VP_TRANSIT_DEL 0x88 #define VP_THROUGHPUT 0x89 #define VP_SEQ_NR 0x8A /* in AK */ #define VP_REASSIGNMENT 0x8B #define VP_FLOW_CNTL 0x8C /* in AK */ #define VP_TPDU_SIZE 0xC0 #define VP_SRC_TSAP 0xC1 /* in CR/CC */ #define VP_DST_TSAP 0xC2 #define VP_CHECKSUM 0xC3 #define VP_VERSION_NR 0xC4 #define VP_PROTECTION 0xC5 #define VP_OPT_SEL 0xC6 #define VP_PROTO_CLASS 0xC7 #define VP_CLEARING_INFO 0xE0 /* in DR */ #define VP_PREF_MAX_TPDU_SIZE 0xF0 #define VP_INACTIVITY_TIMER 0xF2 /* ATN */ /* Parameter codes with bits 7 and 8 are explicitly not */ /* assigned by ISO/IEC 8073, nor is their use precluded. */ /* Parameter codes for ATN defined in ICAO doc 9507 Ed3 SV 5 section 5.5.2.4.3.1 */ #define VP_ATN_EC_32 0x08 /* 4 octet ATN Extended Transport Checksum parameter */ #define VP_ATN_EC_16 0x09 /* 2 octet ATN Extended Transport Checksum parameter */ /* ATN end */ static const value_string tp_vpart_type_vals[] = { { VP_ATN_EC_16, "ATN extended checksum - 16 bit" }, { VP_ATN_EC_32, "ATN extended checksum - 32 bit" }, { VP_ACK_TIME, "ack time" }, { VP_RES_ERROR, "res error" }, { VP_PRIORITY, "priority" }, { VP_TRANSIT_DEL, "transit delay" }, { VP_THROUGHPUT, "throughput" }, { VP_SEQ_NR, "seq number" }, { VP_REASSIGNMENT, "reassignment" }, { VP_FLOW_CNTL, "flow control" }, { VP_TPDU_SIZE, "tpdu-size" }, { VP_SRC_TSAP, "src-tsap" }, { VP_DST_TSAP, "dst-tsap" }, { VP_CHECKSUM, "checksum" }, { VP_VERSION_NR, "version" }, { VP_PROTECTION, "protection" }, { VP_OPT_SEL, "options" }, { VP_PROTO_CLASS, "proto class" }, { VP_CLEARING_INFO, "additional connection clearing info" }, { VP_PREF_MAX_TPDU_SIZE, "preferred max TPDU size" }, { VP_INACTIVITY_TIMER, "inactivity timer" }, { 0, NULL } }; static int hf_cotp_vp_src_tsap = -1; static int hf_cotp_vp_dst_tsap = -1; static int hf_cotp_vp_src_tsap_bytes = -1; static int hf_cotp_vp_dst_tsap_bytes = -1; /* global variables */ /* List of dissectors to call for COTP packets put atop the Inactive Subset of CLNP. */ static heur_dissector_list_t cotp_is_heur_subdissector_list; /* List of dissectors to call for COTP packets put atop CLNP */ static heur_dissector_list_t cotp_heur_subdissector_list; /* List of dissectors to call for CLTP packets put atop CLNP */ static heur_dissector_list_t cltp_heur_subdissector_list; /* * Reassembly of COTP. */ static reassembly_table cotp_reassembly_table; static guint16 cotp_dst_ref = 0; static gboolean cotp_frame_reset = FALSE; static gboolean cotp_last_fragment = FALSE; #define TSAP_DISPLAY_AUTO 0 #define TSAP_DISPLAY_STRING 1 #define TSAP_DISPLAY_BYTES 2 /* options */ static gboolean cotp_reassemble = TRUE; static gint32 tsap_display = TSAP_DISPLAY_AUTO; static gboolean cotp_decode_atn = FALSE; static const enum_val_t tsap_display_options[] = { {"auto", "As strings if printable", TSAP_DISPLAY_AUTO}, {"string", "As strings", TSAP_DISPLAY_STRING}, {"bytes", "As bytes", TSAP_DISPLAY_BYTES}, {NULL, NULL, -1} }; /* function definitions */ #define MAX_TSAP_LEN 32 static void cotp_frame_end(void) { if (!cotp_last_fragment) { /* Last COTP in frame is not fragmented. * No need for incrementing the dst_ref, so we decrement it here. */ cotp_dst_ref--; } cotp_frame_reset = TRUE; } static gboolean is_all_printable(const guchar *stringtocheck, int length) { gboolean allprintable; int i; allprintable=TRUE; for (i=0;i MAX_TSAP_LEN) g_snprintf(cur, MAX_TSAP_LEN * 2 + 3, ""); else { allprintable = is_all_printable(tsap,length); if (!allprintable) { returned_length = g_snprintf(cur, MAX_TSAP_LEN * 2 + 3, "0x"); idx += MIN(returned_length, MAX_TSAP_LEN * 2 + 3 - 1); } while (length != 0) { if (allprintable) { returned_length = g_snprintf(&cur[idx], MAX_TSAP_LEN * 2 + 3 - idx, "%c", *tsap ++); idx += MIN(returned_length, MAX_TSAP_LEN * 2 + 3 - idx - 1); } else { returned_length = g_snprintf(&cur[idx], MAX_TSAP_LEN * 2 + 3 - idx, "%02x", *tsap ++); idx += MIN(returned_length, MAX_TSAP_LEN * 2 + 3 - idx - 1); } length --; } } return cur; } /* print_tsap */ static gboolean ositp_decode_var_part(tvbuff_t *tvb, int offset, int vp_length, int class_option, int tpdu_len, packet_info *pinfo, proto_tree *tree) { guint8 code, length; guint8 c1; guint16 s, s1,s2,s3,s4; guint32 t1, t2, t3, t4; guint32 offset_iso8073_checksum = 0; gint32 i = 0; guint8 tmp_code = 0; guint tmp_len = 0; cksum_status_t cksum_status; gboolean checksum_ok = FALSE; guint32 pref_max_tpdu_size; proto_item *hidden_item; while (vp_length != 0) { code = tvb_get_guint8(tvb, offset); proto_tree_add_text(tree, tvb, offset, 1, "Parameter code: 0x%02x (%s)", code, val_to_str_const(code, tp_vpart_type_vals, "Unknown")); offset += 1; vp_length -= 1; if (vp_length == 0) break; length = tvb_get_guint8(tvb, offset); proto_tree_add_text(tree, tvb, offset, 1, "Parameter length: %u", length); offset += 1; vp_length -= 1; switch (code) { case VP_ATN_EC_16 : /* ATN */ if (cotp_decode_atn) { /* if an alternate OSI checksum is present in the currently unprocessed VP section to the checksum algorithm has to know */ /* this may be the case for backward compatible CR TPDU */ if (!offset_iso8073_checksum) { /* search following parameters in VP part for ISO checksum */ for (i = offset + length; i < vp_length;) { tmp_code = tvb_get_guint8(tvb, i++); tmp_len = tvb_get_guint8(tvb, i++); if (tmp_code == VP_CHECKSUM) { offset_iso8073_checksum = i; /* save ISO 8073 checksum offset for ATN extended checksum calculation */ break; } i += tmp_len; } } checksum_ok = check_atn_ec_16(tvb, tpdu_len , offset, offset_iso8073_checksum, pinfo->dst.len, (guint8 *)pinfo->dst.data, pinfo->src.len, (guint8 *)pinfo->src.data); proto_tree_add_text(tree, tvb, offset, length, "ATN extended checksum : 0x%04x (%s)", tvb_get_ntohs(tvb, offset), checksum_ok ? "correct" : "incorrect"); } else { proto_tree_add_text(tree, tvb, offset, length,"Parameter value: "); } offset += length; vp_length -= length; break; case VP_ATN_EC_32 : /* ATN */ if (cotp_decode_atn) { /* if an alternate OSI checksum is present in the currently unprocessed VP section the checksum algorithm has to know */ /* this may be the case for backward compatible CR TPDU */ if (!offset_iso8073_checksum) { /* search following parameters in VP part for ISO checksum */ for (i = offset + length; i < vp_length;) { tmp_code = tvb_get_guint8(tvb, i++); tmp_len = tvb_get_guint8(tvb, i++); if (tmp_code == VP_CHECKSUM) { offset_iso8073_checksum = i; /* save ISO 8073 checksum offset for ATN extended checksum calculation */ break; } i += tmp_len; } } checksum_ok = check_atn_ec_32( tvb, tpdu_len , offset, offset_iso8073_checksum, pinfo->dst.len, (guint8 *)pinfo->dst.data, pinfo->src.len, (guint8 *)pinfo->src.data); proto_tree_add_text(tree, tvb, offset, length, "ATN extended checksum : 0x%08x (%s)", tvb_get_ntohl(tvb, offset), checksum_ok ? "correct" : "incorrect"); } else { proto_tree_add_text(tree, tvb, offset, length,"Parameter value: "); } offset += length; vp_length -= length; break; case VP_ACK_TIME: s = tvb_get_ntohs(tvb, offset); proto_tree_add_text(tree, tvb, offset, length, "Ack time (ms): %u", s); offset += length; vp_length -= length; break; case VP_RES_ERROR: proto_tree_add_text(tree, tvb, offset, 1, "Residual error rate, target value: 10^%u", tvb_get_guint8(tvb, offset)); offset += 1; length -= 1; vp_length -= 1; proto_tree_add_text(tree, tvb, offset, 1, "Residual error rate, minimum acceptable: 10^%u", tvb_get_guint8(tvb, offset)); offset += 1; length -= 1; vp_length -= 1; proto_tree_add_text(tree, tvb, offset, 1, "Residual error rate, TSDU size of interest: %u", 1<