aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-x25.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-x25.c')
-rw-r--r--epan/dissectors/packet-x25.c2518
1 files changed, 2518 insertions, 0 deletions
diff --git a/epan/dissectors/packet-x25.c b/epan/dissectors/packet-x25.c
new file mode 100644
index 0000000000..653b5049c1
--- /dev/null
+++ b/epan/dissectors/packet-x25.c
@@ -0,0 +1,2518 @@
+/* packet-x25.c
+ * Routines for X.25 packet disassembly
+ * Olivier Abad <oabad@noos.fr>
+ *
+ * $Id$
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+#include "llcsaps.h"
+#include <epan/packet.h>
+#include <epan/circuit.h>
+#include "reassemble.h"
+#include "prefs.h"
+#include "nlpid.h"
+#include "x264_prt_id.h"
+
+/*
+ * Direction of packet.
+ */
+typedef enum {
+ X25_FROM_DCE, /* DCE->DTE */
+ X25_FROM_DTE, /* DTE->DCE */
+ X25_UNKNOWN /* direction unknown */
+} x25_dir_t;
+
+/*
+ * 0 for data packets, 1 for non-data packets.
+ */
+#define X25_NONDATA_BIT 0x01
+
+#define X25_CALL_REQUEST 0x0B
+#define X25_CALL_ACCEPTED 0x0F
+#define X25_CLEAR_REQUEST 0x13
+#define X25_CLEAR_CONFIRMATION 0x17
+#define X25_INTERRUPT 0x23
+#define X25_INTERRUPT_CONFIRMATION 0x27
+#define X25_RESET_REQUEST 0x1B
+#define X25_RESET_CONFIRMATION 0x1F
+#define X25_RESTART_REQUEST 0xFB
+#define X25_RESTART_CONFIRMATION 0xFF
+#define X25_REGISTRATION_REQUEST 0xF3
+#define X25_REGISTRATION_CONFIRMATION 0xF7
+#define X25_DIAGNOSTIC 0xF1
+#define X25_RR 0x01
+#define X25_RNR 0x05
+#define X25_REJ 0x09
+#define X25_DATA 0x00
+
+#define PACKET_IS_DATA(type) (!(type & X25_NONDATA_BIT))
+#define PACKET_TYPE_FC(type) (type & 0x1F)
+
+#define X25_MBIT_MOD8 0x10
+#define X25_MBIT_MOD128 0x01
+
+#define X25_ABIT 0x8000
+
+#define X25_QBIT 0x8000
+#define X25_DBIT 0x4000
+
+#define X25_FAC_CLASS_MASK 0xC0
+
+#define X25_FAC_CLASS_A 0x00
+#define X25_FAC_CLASS_B 0x40
+#define X25_FAC_CLASS_C 0x80
+#define X25_FAC_CLASS_D 0xC0
+
+#define X25_FAC_COMP_MARK 0x00
+#define X25_FAC_REVERSE 0x01
+#define X25_FAC_THROUGHPUT 0x02
+#define X25_FAC_CUG 0x03
+#define X25_FAC_CALLED_MODIF 0x08
+#define X25_FAC_CUG_OUTGOING_ACC 0x09
+#define X25_FAC_THROUGHPUT_MIN 0x0A
+#define X25_FAC_EXPRESS_DATA 0x0B
+#define X25_FAC_BILATERAL_CUG 0x41
+#define X25_FAC_PACKET_SIZE 0x42
+#define X25_FAC_WINDOW_SIZE 0x43
+#define X25_FAC_RPOA_SELECTION 0x44
+#define X25_FAC_TRANSIT_DELAY 0x49
+#define X25_FAC_CALL_TRANSFER 0xC3
+#define X25_FAC_CALLED_ADDR_EXT 0xC9
+#define X25_FAC_ETE_TRANSIT_DELAY 0xCA
+#define X25_FAC_CALLING_ADDR_EXT 0xCB
+#define X25_FAC_CALL_DEFLECT 0xD1
+#define X25_FAC_PRIORITY 0xD2
+
+static int proto_x25 = -1;
+static int hf_x25_gfi = -1;
+static int hf_x25_abit = -1;
+static int hf_x25_qbit = -1;
+static int hf_x25_dbit = -1;
+static int hf_x25_mod = -1;
+static int hf_x25_lcn = -1;
+static int hf_x25_type = -1;
+static int hf_x25_type_fc_mod8 = -1;
+static int hf_x25_type_data = -1;
+static int hf_x25_p_r_mod8 = -1;
+static int hf_x25_p_r_mod128 = -1;
+static int hf_x25_mbit_mod8 = -1;
+static int hf_x25_mbit_mod128 = -1;
+static int hf_x25_p_s_mod8 = -1;
+static int hf_x25_p_s_mod128 = -1;
+
+static gint ett_x25 = -1;
+static gint ett_x25_gfi = -1;
+static gint ett_x25_fac = -1;
+static gint ett_x25_fac_unknown = -1;
+static gint ett_x25_fac_mark = -1;
+static gint ett_x25_fac_reverse = -1;
+static gint ett_x25_fac_throughput = -1;
+static gint ett_x25_fac_cug = -1;
+static gint ett_x25_fac_called_modif = -1;
+static gint ett_x25_fac_cug_outgoing_acc = -1;
+static gint ett_x25_fac_throughput_min = -1;
+static gint ett_x25_fac_express_data = -1;
+static gint ett_x25_fac_bilateral_cug = -1;
+static gint ett_x25_fac_packet_size = -1;
+static gint ett_x25_fac_window_size = -1;
+static gint ett_x25_fac_rpoa_selection = -1;
+static gint ett_x25_fac_transit_delay = -1;
+static gint ett_x25_fac_call_transfer = -1;
+static gint ett_x25_fac_called_addr_ext = -1;
+static gint ett_x25_fac_ete_transit_delay = -1;
+static gint ett_x25_fac_calling_addr_ext = -1;
+static gint ett_x25_fac_call_deflect = -1;
+static gint ett_x25_fac_priority = -1;
+static gint ett_x25_user_data = -1;
+
+static gint ett_x25_segment = -1;
+static gint ett_x25_segments = -1;
+static gint hf_x25_segments = -1;
+static gint hf_x25_segment = -1;
+static gint hf_x25_segment_overlap = -1;
+static gint hf_x25_segment_overlap_conflict = -1;
+static gint hf_x25_segment_multiple_tails = -1;
+static gint hf_x25_segment_too_long_segment = -1;
+static gint hf_x25_segment_error = -1;
+
+static const value_string vals_modulo[] = {
+ { 1, "8" },
+ { 2, "128" },
+ { 0, NULL}
+};
+
+static const value_string vals_x25_type[] = {
+ { X25_CALL_REQUEST, "Call" },
+ { X25_CALL_ACCEPTED, "Call Accepted" },
+ { X25_CLEAR_REQUEST, "Clear" },
+ { X25_CLEAR_CONFIRMATION, "Clear Confirmation" },
+ { X25_INTERRUPT, "Interrupt" },
+ { X25_INTERRUPT_CONFIRMATION, "Interrupt Confirmation" },
+ { X25_RESET_REQUEST, "Reset" },
+ { X25_RESET_CONFIRMATION, "Reset Confirmation" },
+ { X25_RESTART_REQUEST, "Restart" },
+ { X25_RESTART_CONFIRMATION, "Restart Confirmation" },
+ { X25_REGISTRATION_REQUEST, "Registration" },
+ { X25_REGISTRATION_CONFIRMATION, "Registration Confirmation" },
+ { X25_DIAGNOSTIC, "Diagnostic" },
+ { X25_RR, "RR" },
+ { X25_RNR, "RNR" },
+ { X25_REJ, "REJ" },
+ { X25_DATA, "Data" },
+ { 0, NULL}
+};
+
+static struct true_false_string m_bit_tfs = {
+ "More data follows",
+ "End of data"
+};
+
+static const fragment_items x25_frag_items = {
+ &ett_x25_segment,
+ &ett_x25_segments,
+ &hf_x25_segments,
+ &hf_x25_segment,
+ &hf_x25_segment_overlap,
+ &hf_x25_segment_overlap_conflict,
+ &hf_x25_segment_multiple_tails,
+ &hf_x25_segment_too_long_segment,
+ &hf_x25_segment_error,
+ NULL,
+ "segments"
+};
+
+static dissector_handle_t ip_handle;
+static dissector_handle_t clnp_handle;
+static dissector_handle_t ositp_handle;
+static dissector_handle_t qllc_handle;
+static dissector_handle_t data_handle;
+
+/* Preferences */
+static gboolean payload_is_qllc_sna = FALSE;
+static gboolean reassemble_x25 = FALSE;
+
+/* Reassembly of X.25 */
+
+static GHashTable *x25_segment_table = NULL;
+static GHashTable *x25_reassembled_table = NULL;
+
+static dissector_table_t x25_subdissector_table;
+static heur_dissector_list_t x25_heur_subdissector_list;
+
+static void
+x25_hash_add_proto_start(guint16 vc, guint32 frame, dissector_handle_t dissect)
+{
+ circuit_t *circuit;
+
+ /*
+ * Is there already a circuit with this VC number?
+ */
+ circuit = find_circuit(CT_X25, vc, frame);
+ if (circuit != NULL) {
+ /*
+ * Yes - close it, as we're creating a new one.
+ */
+ close_circuit(circuit, frame - 1);
+ }
+
+ /*
+ * Set up a new circuit.
+ */
+ circuit = circuit_new(CT_X25, vc, frame);
+
+ /*
+ * Set its dissector.
+ */
+ circuit_set_dissector(circuit, dissect);
+}
+
+static void
+x25_hash_add_proto_end(guint16 vc, guint32 frame)
+{
+ circuit_t *circuit;
+
+ /*
+ * Try to find the circuit.
+ */
+ circuit = find_circuit(CT_X25, vc, frame);
+
+ /*
+ * If we succeeded, close it.
+ */
+ if (circuit != NULL)
+ close_circuit(circuit, frame);
+}
+
+static char *clear_code(unsigned char code)
+{
+ static char buffer[25];
+
+ if (code == 0x00 || (code & 0x80) == 0x80)
+ return "DTE Originated";
+ if (code == 0x01)
+ return "Number Busy";
+ if (code == 0x03)
+ return "Invalid Facility Requested";
+ if (code == 0x05)
+ return "Network Congestion";
+ if (code == 0x09)
+ return "Out Of Order";
+ if (code == 0x0B)
+ return "Access Barred";
+ if (code == 0x0D)
+ return "Not Obtainable";
+ if (code == 0x11)
+ return "Remote Procedure Error";
+ if (code == 0x13)
+ return "Local Procedure Error";
+ if (code == 0x15)
+ return "RPOA Out Of Order";
+ if (code == 0x19)
+ return "Reverse Charging Acceptance Not Subscribed";
+ if (code == 0x21)
+ return "Incompatible Destination";
+ if (code == 0x29)
+ return "Fast Select Acceptance Not Subscribed";
+ if (code == 0x39)
+ return "Destination Absent";
+
+ sprintf(buffer, "Unknown %02X", code);
+
+ return buffer;
+}
+
+static char *clear_diag(unsigned char code)
+{
+ static char buffer[25];
+
+ if (code == 0)
+ return "No additional information";
+ if (code == 1)
+ return "Invalid P(S)";
+ if (code == 2)
+ return "Invalid P(R)";
+ if (code == 16)
+ return "Packet type invalid";
+ if (code == 17)
+ return "Packet type invalid for state r1";
+ if (code == 18)
+ return "Packet type invalid for state r2";
+ if (code == 19)
+ return "Packet type invalid for state r3";
+ if (code == 20)
+ return "Packet type invalid for state p1";
+ if (code == 21)
+ return "Packet type invalid for state p2";
+ if (code == 22)
+ return "Packet type invalid for state p3";
+ if (code == 23)
+ return "Packet type invalid for state p4";
+ if (code == 24)
+ return "Packet type invalid for state p5";
+ if (code == 25)
+ return "Packet type invalid for state p6";
+ if (code == 26)
+ return "Packet type invalid for state p7";
+ if (code == 27)
+ return "Packet type invalid for state d1";
+ if (code == 28)
+ return "Packet type invalid for state d2";
+ if (code == 29)
+ return "Packet type invalid for state d3";
+ if (code == 32)
+ return "Packet not allowed";
+ if (code == 33)
+ return "Unidentifiable packet";
+ if (code == 34)
+ return "Call on one-way logical channel";
+ if (code == 35)
+ return "Invalid packet type on a PVC";
+ if (code == 36)
+ return "Packet on unassigned LC";
+ if (code == 37)
+ return "Reject not subscribed to";
+ if (code == 38)
+ return "Packet too short";
+ if (code == 39)
+ return "Packet too long";
+ if (code == 40)
+ return "Invalid general format identifier";
+ if (code == 41)
+ return "Restart/registration packet with nonzero bits";
+ if (code == 42)
+ return "Packet type not compatible with facility";
+ if (code == 43)
+ return "Unauthorised interrupt confirmation";
+ if (code == 44)
+ return "Unauthorised interrupt";
+ if (code == 45)
+ return "Unauthorised reject";
+ if (code == 48)
+ return "Time expired";
+ if (code == 49)
+ return "Time expired for incoming call";
+ if (code == 50)
+ return "Time expired for clear indication";
+ if (code == 51)
+ return "Time expired for reset indication";
+ if (code == 52)
+ return "Time expired for restart indication";
+ if (code == 53)
+ return "Time expired for call deflection";
+ if (code == 64)
+ return "Call set-up/clearing or registration pb.";
+ if (code == 65)
+ return "Facility/registration code not allowed";
+ if (code == 66)
+ return "Facility parameter not allowed";
+ if (code == 67)
+ return "Invalid called DTE address";
+ if (code == 68)
+ return "Invalid calling DTE address";
+ if (code == 69)
+ return "Invalid facility/registration length";
+ if (code == 70)
+ return "Incoming call barred";
+ if (code == 71)
+ return "No logical channel available";
+ if (code == 72)
+ return "Call collision";
+ if (code == 73)
+ return "Duplicate facility requested";
+ if (code == 74)
+ return "Non zero address length";
+ if (code == 75)
+ return "Non zero facility length";
+ if (code == 76)
+ return "Facility not provided when expected";
+ if (code == 77)
+ return "Invalid CCITT-specified DTE facility";
+ if (code == 78)
+ return "Max. nb of call redir/defl. exceeded";
+ if (code == 80)
+ return "Miscellaneous";
+ if (code == 81)
+ return "Improper cause code from DTE";
+ if (code == 82)
+ return "Not aligned octet";
+ if (code == 83)
+ return "Inconsistent Q bit setting";
+ if (code == 84)
+ return "NUI problem";
+ if (code == 112)
+ return "International problem";
+ if (code == 113)
+ return "Remote network problem";
+ if (code == 114)
+ return "International protocol problem";
+ if (code == 115)
+ return "International link out of order";
+ if (code == 116)
+ return "International link busy";
+ if (code == 117)
+ return "Transit network facility problem";
+ if (code == 118)
+ return "Remote network facility problem";
+ if (code == 119)
+ return "International routing problem";
+ if (code == 120)
+ return "Temporary routing problem";
+ if (code == 121)
+ return "Unknown called DNIC";
+ if (code == 122)
+ return "Maintenance action";
+ if (code == 144)
+ return "Timer expired or retransmission count surpassed";
+ if (code == 145)
+ return "Timer expired or retransmission count surpassed for INTERRUPT";
+ if (code == 146)
+ return "Timer expired or retransmission count surpassed for DATA "
+ "packet transmission";
+ if (code == 147)
+ return "Timer expired or retransmission count surpassed for REJECT";
+ if (code == 160)
+ return "DTE-specific signals";
+ if (code == 161)
+ return "DTE operational";
+ if (code == 162)
+ return "DTE not operational";
+ if (code == 163)
+ return "DTE resource constraint";
+ if (code == 164)
+ return "Fast select not subscribed";
+ if (code == 165)
+ return "Invalid partially full DATA packet";
+ if (code == 166)
+ return "D-bit procedure not supported";
+ if (code == 167)
+ return "Registration/Cancellation confirmed";
+ if (code == 224)
+ return "OSI network service problem";
+ if (code == 225)
+ return "Disconnection (transient condition)";
+ if (code == 226)
+ return "Disconnection (permanent condition)";
+ if (code == 227)
+ return "Connection rejection - reason unspecified (transient "
+ "condition)";
+ if (code == 228)
+ return "Connection rejection - reason unspecified (permanent "
+ "condition)";
+ if (code == 229)
+ return "Connection rejection - quality of service not available "
+ "transient condition)";
+ if (code == 230)
+ return "Connection rejection - quality of service not available "
+ "permanent condition)";
+ if (code == 231)
+ return "Connection rejection - NSAP unreachable (transient condition)";
+ if (code == 232)
+ return "Connection rejection - NSAP unreachable (permanent condition)";
+ if (code == 233)
+ return "reset - reason unspecified";
+ if (code == 234)
+ return "reset - congestion";
+ if (code == 235)
+ return "Connection rejection - NSAP address unknown (permanent "
+ "condition)";
+ if (code == 240)
+ return "Higher layer initiated";
+ if (code == 241)
+ return "Disconnection - normal";
+ if (code == 242)
+ return "Disconnection - abnormal";
+ if (code == 243)
+ return "Disconnection - incompatible information in user data";
+ if (code == 244)
+ return "Connection rejection - reason unspecified (transient "
+ "condition)";
+ if (code == 245)
+ return "Connection rejection - reason unspecified (permanent "
+ "condition)";
+ if (code == 246)
+ return "Connection rejection - quality of service not available "
+ "(transient condition)";
+ if (code == 247)
+ return "Connection rejection - quality of service not available "
+ "(permanent condition)";
+ if (code == 248)
+ return "Connection rejection - incompatible information in user data";
+ if (code == 249)
+ return "Connection rejection - unrecognizable protocol indentifier "
+ "in user data";
+ if (code == 250)
+ return "Reset - user resynchronization";
+
+ sprintf(buffer, "Unknown %d", code);
+
+ return buffer;
+}
+
+static char *reset_code(unsigned char code)
+{
+ static char buffer[25];
+
+ if (code == 0x00 || (code & 0x80) == 0x80)
+ return "DTE Originated";
+ if (code == 0x01)
+ return "Out of order";
+ if (code == 0x03)
+ return "Remote Procedure Error";
+ if (code == 0x05)
+ return "Local Procedure Error";
+ if (code == 0x07)
+ return "Network Congestion";
+ if (code == 0x09)
+ return "Remote DTE operational";
+ if (code == 0x0F)
+ return "Network operational";
+ if (code == 0x11)
+ return "Incompatible Destination";
+ if (code == 0x1D)
+ return "Network out of order";
+
+ sprintf(buffer, "Unknown %02X", code);
+
+ return buffer;
+}
+
+static char *restart_code(unsigned char code)
+{
+ static char buffer[25];
+
+ if (code == 0x00 || (code & 0x80) == 0x80)
+ return "DTE Originated";
+ if (code == 0x01)
+ return "Local Procedure Error";
+ if (code == 0x03)
+ return "Network Congestion";
+ if (code == 0x07)
+ return "Network Operational";
+ if (code == 0x7F)
+ return "Registration/cancellation confirmed";
+
+ sprintf(buffer, "Unknown %02X", code);
+
+ return buffer;
+}
+
+static char *registration_code(unsigned char code)
+{
+ static char buffer[25];
+
+ if (code == 0x03)
+ return "Invalid facility request";
+ if (code == 0x05)
+ return "Network congestion";
+ if (code == 0x13)
+ return "Local procedure error";
+ if (code == 0x7F)
+ return "Registration/cancellation confirmed";
+
+ sprintf(buffer, "Unknown %02X", code);
+
+ return buffer;
+}
+
+static void
+dump_facilities(proto_tree *tree, int *offset, tvbuff_t *tvb)
+{
+ guint8 fac, byte1, byte2, byte3;
+ guint32 len; /* facilities length */
+ proto_item *ti=0;
+ proto_tree *fac_tree = 0;
+ proto_tree *fac_subtree;
+
+ len = tvb_get_guint8(tvb, *offset);
+ if (len && tree) {
+ ti = proto_tree_add_text(tree, tvb, *offset, len + 1,
+ "Facilities");
+ fac_tree = proto_item_add_subtree(ti, ett_x25_fac);
+ proto_tree_add_text(fac_tree, tvb, *offset, 1,
+ "Facilities length: %d", len);
+ }
+ (*offset)++;
+
+ while (len > 0) {
+ fac = tvb_get_guint8(tvb, *offset);
+ switch(fac & X25_FAC_CLASS_MASK) {
+ case X25_FAC_CLASS_A:
+ switch (fac) {
+ case X25_FAC_COMP_MARK:
+ if (fac_tree)
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+ "Code : 00 (Marker)");
+ switch (tvb_get_guint8(tvb, *offset + 1)) {
+ case 0x00:
+ if (fac_tree) {
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_mark);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Parameter : 00 (Network complementary "
+ "services - calling DTE)");
+ }
+ break;
+ case 0xFF:
+ if (fac_tree) {
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_mark);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Parameter : FF (Network complementary "
+ "services - called DTE)");
+ }
+ break;
+ case 0x0F:
+ if (fac_tree) {
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_mark);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Parameter : 0F (DTE complementary "
+ "services)");
+ }
+ break;
+ default:
+ if (fac_tree) {
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_mark);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Parameter : %02X (Unknown marker)",
+ tvb_get_guint8(tvb, *offset+1));
+ }
+ break;
+ }
+ break;
+ case X25_FAC_REVERSE:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Reverse charging / Fast select)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_reverse);
+ byte1 = tvb_get_guint8(tvb, *offset + 1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Parameter : %02X", byte1);
+ if (byte1 & 0xC0)
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "11.. .... = Fast select with restriction");
+ else if (byte1 & 0x80)
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "10.. .... = Fast select - no restriction");
+ else
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "00.. .... = Fast select not requested");
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ decode_boolean_bitfield(byte1, 0x01, 1*8,
+ "Reverse charging requested",
+ "Reverse charging not requested"));
+ }
+ break;
+ case X25_FAC_THROUGHPUT:
+ if (fac_tree) {
+ char tmpbuf[80];
+
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Throughput class negociation)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_throughput);
+ byte1 = tvb_get_guint8(tvb, *offset + 1);
+ switch (byte1 >> 4)
+ {
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ sprintf(tmpbuf, "From the called DTE : %%u (%d bps)",
+ 75*(1<<((byte1 >> 4)-3)));
+ break;
+ case 12:
+ sprintf(tmpbuf, "From the called DTE : %%u (48000 bps)");
+ break;
+ case 13:
+ sprintf(tmpbuf, "From the called DTE : %%u (64000 bps)");
+ break;
+ default:
+ sprintf(tmpbuf, "From the called DTE : %%u (Reserved)");
+ }
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ decode_numeric_bitfield(byte1, 0xF0, 1*8, tmpbuf));
+ switch (byte1 & 0x0F)
+ {
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ sprintf(tmpbuf, "From the calling DTE : %%u (%d bps)",
+ 75*(1<<((byte1 & 0x0F)-3)));
+ break;
+ case 12:
+ sprintf(tmpbuf, "From the calling DTE : %%u (48000 bps)");
+ break;
+ case 13:
+ sprintf(tmpbuf, "From the calling DTE : %%u (64000 bps)");
+ break;
+ default:
+ sprintf(tmpbuf, "From the calling DTE : %%u (Reserved)");
+ }
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ decode_numeric_bitfield(byte1, 0x0F, 1*8, tmpbuf));
+ }
+ break;
+ case X25_FAC_CUG:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Closed user group selection)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_cug);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Closed user group: %02X", tvb_get_guint8(tvb, *offset+1));
+ }
+ break;
+ case X25_FAC_CALLED_MODIF:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Called address modified)", fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_called_modif);
+ proto_tree_add_text(fac_tree, tvb, *offset+1, 1,
+ "Parameter %02X", tvb_get_guint8(tvb, *offset+1));
+ }
+ break;
+ case X25_FAC_CUG_OUTGOING_ACC:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Closed user group with outgoing access selection)",
+ fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_cug_outgoing_acc);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Closed user group: %02X", tvb_get_guint8(tvb, *offset+1));
+ }
+ break;
+ case X25_FAC_THROUGHPUT_MIN:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Minimum throughput class)", fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_throughput_min);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Parameter %02X", tvb_get_guint8(tvb, *offset+1));
+ }
+ break;
+ case X25_FAC_EXPRESS_DATA:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Negociation of express data)", fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_express_data);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Parameter %02X", tvb_get_guint8(tvb, *offset+1));
+ }
+ break;
+ default:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+ "Code : %02X (Unknown class A)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_unknown);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Parameter %02X", tvb_get_guint8(tvb, *offset+1));
+ }
+ break;
+ }
+ (*offset) += 2;
+ len -= 2;
+ break;
+ case X25_FAC_CLASS_B:
+ switch (fac) {
+ case X25_FAC_BILATERAL_CUG:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Bilateral closed user group selection)", fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_bilateral_cug);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 2,
+ "Bilateral CUG: %04X",
+ tvb_get_ntohs(tvb, *offset+1));
+ }
+ break;
+ case X25_FAC_PACKET_SIZE:
+ if (fac_tree)
+ {
+ char tmpbuf[80];
+
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Packet size)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_packet_size);
+ byte1 = tvb_get_guint8(tvb, *offset + 1);
+ switch (byte1)
+ {
+ case 0x04:
+ sprintf(tmpbuf, "From the called DTE : %%u (16)");
+ break;
+ case 0x05:
+ sprintf(tmpbuf, "From the called DTE : %%u (32)");
+ break;
+ case 0x06:
+ sprintf(tmpbuf, "From the called DTE : %%u (64)");
+ break;
+ case 0x07:
+ sprintf(tmpbuf, "From the called DTE : %%u (128)");
+ break;
+ case 0x08:
+ sprintf(tmpbuf, "From the called DTE : %%u (256)");
+ break;
+ case 0x0D:
+ sprintf(tmpbuf, "From the called DTE : %%u (512)");
+ break;
+ case 0x0C:
+ sprintf(tmpbuf, "From the called DTE : %%u (1024)");
+ break;
+ case 0x0E:
+ sprintf(tmpbuf, "From the called DTE : %%u (2048)");
+ break;
+ case 0x0F:
+ sprintf(tmpbuf, "From the called DTE : %%u (4096)");
+ break;
+ default:
+ sprintf(tmpbuf, "From the called DTE : %%u (Unknown)");
+ break;
+ }
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ decode_numeric_bitfield(byte1, 0x0F, 1*8, tmpbuf));
+
+ byte2 = tvb_get_guint8(tvb, *offset + 1);
+ switch (byte2)
+ {
+ case 0x04:
+ sprintf(tmpbuf, "From the calling DTE : %%u (16)");
+ break;
+ case 0x05:
+ sprintf(tmpbuf, "From the calling DTE : %%u (32)");
+ break;
+ case 0x06:
+ sprintf(tmpbuf, "From the calling DTE : %%u (64)");
+ break;
+ case 0x07:
+ sprintf(tmpbuf, "From the calling DTE : %%u (128)");
+ break;
+ case 0x08:
+ sprintf(tmpbuf, "From the calling DTE : %%u (256)");
+ break;
+ case 0x0D:
+ sprintf(tmpbuf, "From the calling DTE : %%u (512)");
+ break;
+ case 0x0C:
+ sprintf(tmpbuf, "From the calling DTE : %%u (1024)");
+ break;
+ case 0x0E:
+ sprintf(tmpbuf, "From the calling DTE : %%u (2048)");
+ break;
+ case 0x0F:
+ sprintf(tmpbuf, "From the calling DTE : %%u (4096)");
+ break;
+ default:
+ sprintf(tmpbuf, "From the calling DTE : %%u (Unknown)");
+ break;
+ }
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ decode_numeric_bitfield(byte2, 0x0F, 1*8, tmpbuf));
+ }
+ break;
+ case X25_FAC_WINDOW_SIZE:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Window size)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_window_size);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ decode_numeric_bitfield(tvb_get_guint8(tvb, *offset+1),
+ 0x7F, 1*8, "From the called DTE: %u"));
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ decode_numeric_bitfield(tvb_get_guint8(tvb, *offset+2),
+ 0x7F, 1*8, "From the calling DTE: %u"));
+ }
+ break;
+ case X25_FAC_RPOA_SELECTION:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(RPOA selection)", fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_rpoa_selection);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 2,
+ "Data network identification code : %04X",
+ tvb_get_ntohs(tvb, *offset+1));
+ }
+ break;
+ case X25_FAC_TRANSIT_DELAY:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Transit delay selection and indication)", fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_transit_delay);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 2,
+ "Transit delay: %d ms",
+ tvb_get_ntohs(tvb, *offset+1));
+ }
+ break;
+ default:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+ "Code : %02X (Unknown class B)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_unknown);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 2,
+ "Parameter %04X", tvb_get_ntohs(tvb, *offset+1));
+ }
+ break;
+ }
+ (*offset) += 3;
+ len -= 3;
+ break;
+ case X25_FAC_CLASS_C:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+ "Code : %02X (Unknown class C)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_unknown);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 3,
+ "Parameter %06X",
+ tvb_get_ntoh24(tvb, *offset+1));
+ }
+ (*offset) += 4;
+ len -= 4;
+ break;
+ case X25_FAC_CLASS_D:
+ switch (fac) {
+ case X25_FAC_CALL_TRANSFER:
+ if (fac_tree) {
+ int i;
+ char tmpbuf[256];
+
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Call redirection or deflection notification)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_call_transfer);
+ byte1 = tvb_get_guint8(tvb, *offset+1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Length : %u", byte1);
+ byte2 = tvb_get_guint8(tvb, *offset+2);
+ if ((byte2 & 0xC0) == 0xC0) {
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ "Reason : call deflection by the originally "
+ "called DTE address");
+ }
+ else {
+ switch (byte2) {
+ case 0x01:
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ "Reason : originally called DTE busy");
+ break;
+ case 0x07:
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ "Reason : call dist. within a hunt group");
+ break;
+ case 0x09:
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ "Reason : originally called DTE out of order");
+ break;
+ case 0x0F:
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ "Reason : systematic call redirection");
+ break;
+ default:
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ "Reason : unknown");
+ break;
+ }
+ }
+ byte3 = tvb_get_guint8(tvb, *offset+3);
+ proto_tree_add_text(fac_subtree, tvb, *offset+3, 1,
+ "Number of semi-octets in DTE address : %u",
+ byte3);
+ for (i = 0; i < byte3; i++) {
+ if (i % 2 == 0) {
+ tmpbuf[i] = ((tvb_get_guint8(tvb, *offset+4+i/2) >> 4)
+ & 0x0F) + '0';
+ /* if > 9, convert to the right hexadecimal letter */
+ if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+ } else {
+ tmpbuf[i] = (tvb_get_guint8(tvb, *offset+4+i/2)
+ & 0x0F) + '0';
+ /* if > 9, convert to the right hexadecimal letter */
+ if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+ }
+ }
+ tmpbuf[i] = 0;
+ proto_tree_add_text(fac_subtree, tvb, *offset+4, byte1 - 2,
+ "DTE address : %s", tmpbuf);
+ }
+ break;
+ case X25_FAC_CALLING_ADDR_EXT:
+ if (fac_tree) {
+ int i;
+ char tmpbuf[256];
+
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Calling address extension)", fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_calling_addr_ext);
+ byte1 = tvb_get_guint8(tvb, *offset+1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Length : %u", byte1);
+ byte2 = tvb_get_guint8(tvb, *offset+2) & 0x3F;
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ "Number of semi-octets in DTE address : %u", byte2);
+ for (i = 0; i < byte2; i++) {
+ if (i % 2 == 0) {
+ tmpbuf[i] = ((tvb_get_guint8(tvb, *offset+3+i/2) >> 4)
+ & 0x0F) + '0';
+ /* if > 9, convert to the right hexadecimal letter */
+ if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+ } else {
+ tmpbuf[i] = (tvb_get_guint8(tvb, *offset+3+i/2)
+ & 0x0F) + '0';
+ /* if > 9, convert to the right hexadecimal letter */
+ if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+ }
+ }
+ tmpbuf[i] = 0;
+ proto_tree_add_text(fac_subtree, tvb, *offset+3, byte1 - 1,
+ "DTE address : %s", tmpbuf);
+ }
+ break;
+ case X25_FAC_CALLED_ADDR_EXT:
+ if (fac_tree) {
+ int i;
+ char tmpbuf[256];
+
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Called address extension)", fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_called_addr_ext);
+ byte1 = tvb_get_guint8(tvb, *offset+1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Length : %u", byte1);
+ byte2 = tvb_get_guint8(tvb, *offset+2) & 0x3F;
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ "Number of semi-octets in DTE address : %u", byte2);
+ for (i = 0; i < byte2; i++) {
+ if (i % 2 == 0) {
+ tmpbuf[i] = ((tvb_get_guint8(tvb, *offset+3+i/2) >> 4)
+ & 0x0F) + '0';
+ /* if > 9, convert to the right hexadecimal letter */
+ if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+ } else {
+ tmpbuf[i] = (tvb_get_guint8(tvb, *offset+3+i/2)
+ & 0x0F) + '0';
+ /* if > 9, convert to the right hexadecimal letter */
+ if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+ }
+ }
+ tmpbuf[i] = 0;
+ proto_tree_add_text(fac_subtree, tvb, *offset+3, byte1 - 1,
+ "DTE address : %s", tmpbuf);
+ }
+ break;
+ case X25_FAC_ETE_TRANSIT_DELAY:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(End to end transit delay)", fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_ete_transit_delay);
+ byte1 = tvb_get_guint8(tvb, *offset+1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Length : %u", byte1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, byte1, "Value");
+ }
+ break;
+ case X25_FAC_CALL_DEFLECT:
+ if (fac_tree) {
+ int i;
+ char tmpbuf[256];
+
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1, "Code : %02X "
+ "(Call deflection selection)", fac);
+ fac_subtree = proto_item_add_subtree(ti,
+ ett_x25_fac_call_deflect);
+ byte1 = tvb_get_guint8(tvb, *offset+1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Length : %u", byte1);
+ byte2 = tvb_get_guint8(tvb, *offset+2);
+ if ((byte2 & 0xC0) == 0xC0)
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ "Reason : call DTE originated");
+ else
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
+ "Reason : unknown");
+ byte3 = tvb_get_guint8(tvb, *offset+3);
+ proto_tree_add_text(fac_subtree, tvb, *offset+3, 1,
+ "Number of semi-octets in the alternative DTE address : %u",
+ byte3);
+ for (i = 0; i < byte3; i++) {
+ if (i % 2 == 0) {
+ tmpbuf[i] = ((tvb_get_guint8(tvb, *offset+4+i/2) >> 4)
+ & 0x0F) + '0';
+ /* if > 9, convert to the right hexadecimal letter */
+ if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+ } else {
+ tmpbuf[i] = (tvb_get_guint8(tvb, *offset+4+i/2)
+ & 0x0F) + '0';
+ /* if > 9, convert to the right hexadecimal letter */
+ if (tmpbuf[i] > '9') tmpbuf[i] += ('A' - '0' - 10);
+ }
+ }
+ tmpbuf[i] = 0;
+ proto_tree_add_text(fac_subtree, tvb, *offset+4, byte1 - 2,
+ "Alternative DTE address : %s", tmpbuf);
+ }
+ break;
+ case X25_FAC_PRIORITY:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+ "Code : %02X (Priority)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_priority);
+ byte1 = tvb_get_guint8(tvb, *offset+1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Length : %u", byte1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, byte1, "Value");
+ }
+ break;
+ default:
+ if (fac_tree) {
+ ti = proto_tree_add_text(fac_tree, tvb, *offset, 1,
+ "Code : %02X (Unknown class D)", fac);
+ fac_subtree = proto_item_add_subtree(ti, ett_x25_fac_unknown);
+ byte1 = tvb_get_guint8(tvb, *offset+1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+1, 1,
+ "Length : %u", byte1);
+ proto_tree_add_text(fac_subtree, tvb, *offset+2, byte1, "Value");
+ }
+ }
+ byte1 = tvb_get_guint8(tvb, *offset+1);
+ (*offset) += byte1+2;
+ len -= byte1+2;
+ break;
+ }
+ }
+}
+
+static void
+x25_ntoa(proto_tree *tree, int *offset, tvbuff_t *tvb,
+ packet_info *pinfo, gboolean is_registration)
+{
+ int len1, len2;
+ int i;
+ char addr1[16], addr2[16];
+ char *first, *second;
+ guint8 byte;
+ int localoffset;
+
+ byte = tvb_get_guint8(tvb, *offset);
+ len1 = (byte >> 0) & 0x0F;
+ len2 = (byte >> 4) & 0x0F;
+
+ if (tree) {
+ proto_tree_add_text(tree, tvb, *offset, 1,
+ decode_numeric_bitfield(byte, 0xF0, 1*8,
+ is_registration ?
+ "DTE address length : %u" :
+ "Calling address length : %u"));
+ proto_tree_add_text(tree, tvb, *offset, 1,
+ decode_numeric_bitfield(byte, 0x0F, 1*8,
+ is_registration ?
+ "DCE address length : %u" :
+ "Called address length : %u"));
+ }
+ (*offset)++;
+
+ localoffset = *offset;
+ byte = tvb_get_guint8(tvb, localoffset);
+
+ first=addr1;
+ second=addr2;
+ for (i = 0; i < (len1 + len2); i++) {
+ if (i < len1) {
+ if (i % 2 != 0) {
+ *first++ = ((byte >> 0) & 0x0F) + '0';
+ localoffset++;
+ byte = tvb_get_guint8(tvb, localoffset);
+ } else {
+ *first++ = ((byte >> 4) & 0x0F) + '0';
+ }
+ } else {
+ if (i % 2 != 0) {
+ *second++ = ((byte >> 0) & 0x0F) + '0';
+ localoffset++;
+ byte = tvb_get_guint8(tvb, localoffset);
+ } else {
+ *second++ = ((byte >> 4) & 0x0F) + '0';
+ }
+ }
+ }
+
+ *first = '\0';
+ *second = '\0';
+
+ if (len1) {
+ if (check_col(pinfo->cinfo, COL_RES_DL_DST))
+ col_add_str(pinfo->cinfo, COL_RES_DL_DST, addr1);
+ if (tree)
+ proto_tree_add_text(tree, tvb, *offset,
+ (len1 + 1) / 2,
+ is_registration ?
+ "DCE address : %s" :
+ "Called address : %s",
+ addr1);
+ }
+ if (len2) {
+ if (check_col(pinfo->cinfo, COL_RES_DL_SRC))
+ col_add_str(pinfo->cinfo, COL_RES_DL_SRC, addr2);
+ if (tree)
+ proto_tree_add_text(tree, tvb, *offset + len1/2,
+ (len2+1)/2+(len1%2+(len2+1)%2)/2,
+ is_registration ?
+ "DTE address : %s" :
+ "Calling address : %s",
+ addr2);
+ }
+ (*offset) += ((len1 + len2 + 1) / 2);
+}
+
+static void
+x25_toa(proto_tree *tree, int *offset, tvbuff_t *tvb,
+ packet_info *pinfo)
+{
+ int len1, len2;
+ int i;
+ char addr1[256], addr2[256];
+ char *first, *second;
+ guint8 byte;
+ int localoffset;
+
+ len1 = tvb_get_guint8(tvb, *offset);
+ if (tree) {
+ proto_tree_add_text(tree, tvb, *offset, 1,
+ "Called address length : %u",
+ len1);
+ }
+ (*offset)++;
+
+ len2 = tvb_get_guint8(tvb, *offset);
+ if (tree) {
+ proto_tree_add_text(tree, tvb, *offset, 1,
+ "Calling address length : %u",
+ len2);
+ }
+ (*offset)++;
+
+ localoffset = *offset;
+ byte = tvb_get_guint8(tvb, localoffset);
+
+ /*
+ * XXX - the first two half-octets of the address are the TOA and
+ * NPI; process them as such and, if the TOA says an address is
+ * an alternative address, process it correctly (i.e., not as a
+ * sequence of half-octets containing digit values).
+ */
+ first=addr1;
+ second=addr2;
+ for (i = 0; i < (len1 + len2); i++) {
+ if (i < len1) {
+ if (i % 2 != 0) {
+ *first++ = ((byte >> 0) & 0x0F) + '0';
+ localoffset++;
+ byte = tvb_get_guint8(tvb, localoffset);
+ } else {
+ *first++ = ((byte >> 4) & 0x0F) + '0';
+ }
+ } else {
+ if (i % 2 != 0) {
+ *second++ = ((byte >> 0) & 0x0F) + '0';
+ localoffset++;
+ byte = tvb_get_guint8(tvb, localoffset);
+ } else {
+ *second++ = ((byte >> 4) & 0x0F) + '0';
+ }
+ }
+ }
+
+ *first = '\0';
+ *second = '\0';
+
+ if (len1) {
+ if (check_col(pinfo->cinfo, COL_RES_DL_DST))
+ col_add_str(pinfo->cinfo, COL_RES_DL_DST, addr1);
+ if (tree)
+ proto_tree_add_text(tree, tvb, *offset,
+ (len1 + 1) / 2,
+ "Called address : %s",
+ addr1);
+ }
+ if (len2) {
+ if (check_col(pinfo->cinfo, COL_RES_DL_SRC))
+ col_add_str(pinfo->cinfo, COL_RES_DL_SRC, addr2);
+ if (tree)
+ proto_tree_add_text(tree, tvb, *offset + len1/2,
+ (len2+1)/2+(len1%2+(len2+1)%2)/2,
+ "Calling address : %s",
+ addr2);
+ }
+ (*offset) += ((len1 + len2 + 1) / 2);
+}
+
+static int
+get_x25_pkt_len(tvbuff_t *tvb)
+{
+ guint length, called_len, calling_len, dte_len, dce_len;
+ guint8 byte2, bytex;
+
+ byte2 = tvb_get_guint8(tvb, 2);
+ switch (byte2)
+ {
+ case X25_CALL_REQUEST:
+ bytex = tvb_get_guint8(tvb, 3);
+ called_len = (bytex >> 0) & 0x0F;
+ calling_len = (bytex >> 4) & 0x0F;
+ length = 4 + (called_len + calling_len + 1) / 2; /* addr */
+ if (length < tvb_reported_length(tvb))
+ length += (1 + tvb_get_guint8(tvb, length)); /* facilities */
+
+ return MIN(tvb_reported_length(tvb),length);
+
+ case X25_CALL_ACCEPTED:
+ /* The calling/called address length byte (following the packet type)
+ * is not mandatory, so we must check the packet length before trying
+ * to read it */
+ if (tvb_reported_length(tvb) == 3)
+ return(3);
+ bytex = tvb_get_guint8(tvb, 3);
+ called_len = (bytex >> 0) & 0x0F;
+ calling_len = (bytex >> 4) & 0x0F;
+ length = 4 + (called_len + calling_len + 1) / 2; /* addr */
+ if (length < tvb_reported_length(tvb))
+ length += (1 + tvb_get_guint8(tvb, length)); /* facilities */
+
+ return MIN(tvb_reported_length(tvb),length);
+
+ case X25_CLEAR_REQUEST:
+ case X25_RESET_REQUEST:
+ case X25_RESTART_REQUEST:
+ return MIN(tvb_reported_length(tvb),5);
+
+ case X25_DIAGNOSTIC:
+ return MIN(tvb_reported_length(tvb),4);
+
+ case X25_CLEAR_CONFIRMATION:
+ case X25_INTERRUPT:
+ case X25_INTERRUPT_CONFIRMATION:
+ case X25_RESET_CONFIRMATION:
+ case X25_RESTART_CONFIRMATION:
+ return MIN(tvb_reported_length(tvb),3);
+
+ case X25_REGISTRATION_REQUEST:
+ bytex = tvb_get_guint8(tvb, 3);
+ dce_len = (bytex >> 0) & 0x0F;
+ dte_len = (bytex >> 4) & 0x0F;
+ length = 4 + (dte_len + dce_len + 1) / 2; /* addr */
+ if (length < tvb_reported_length(tvb))
+ length += (1 + tvb_get_guint8(tvb, length)); /* registration */
+
+ return MIN(tvb_reported_length(tvb),length);
+
+ case X25_REGISTRATION_CONFIRMATION:
+ bytex = tvb_get_guint8(tvb, 5);
+ dce_len = (bytex >> 0) & 0x0F;
+ dte_len = (bytex >> 4) & 0x0F;
+ length = 6 + (dte_len + dce_len + 1) / 2; /* addr */
+ if (length < tvb_reported_length(tvb))
+ length += (1 + tvb_get_guint8(tvb, length)); /* registration */
+
+ return MIN(tvb_reported_length(tvb),length);
+ }
+
+ if (PACKET_IS_DATA(byte2))
+ return MIN(tvb_reported_length(tvb),3);
+
+ switch (PACKET_TYPE_FC(byte2))
+ {
+ case X25_RR:
+ return MIN(tvb_reported_length(tvb),3);
+
+ case X25_RNR:
+ return MIN(tvb_reported_length(tvb),3);
+
+ case X25_REJ:
+ return MIN(tvb_reported_length(tvb),3);
+ }
+
+ return 0;
+}
+
+static const value_string prt_id_vals[] = {
+ {PRT_ID_ISO_8073, "ISO 8073 COTP"},
+ {PRT_ID_ISO_8602, "ISO 8602 CLTP"},
+ {PRT_ID_ISO_10736_ISO_8073, "ISO 10736 in conjunction with ISO 8073 COTP"},
+ {PRT_ID_ISO_10736_ISO_8602, "ISO 10736 in conjunction with ISO 8602 CLTP"},
+ {0x00, NULL}
+};
+
+static const value_string sharing_strategy_vals[] = {
+ {0x00, "No sharing"},
+ {0x00, NULL}
+};
+
+static void
+dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ x25_dir_t dir, gboolean side)
+{
+ proto_tree *x25_tree=0, *gfi_tree=0, *userdata_tree=0;
+ proto_item *ti;
+ guint localoffset=0;
+ guint x25_pkt_len;
+ int modulo;
+ guint16 vc;
+ dissector_handle_t dissect = NULL;
+ gboolean toa; /* TOA/NPI address format */
+ guint16 bytes0_1;
+ guint8 pkt_type;
+ char *short_name = NULL, *long_name = NULL;
+ tvbuff_t *next_tvb = NULL;
+ gboolean q_bit_set = FALSE;
+ gboolean m_bit_set;
+ gint payload_len;
+ guint32 frag_key;
+ void *saved_private_data;
+ fragment_data *fd_head;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "X.25");
+
+ bytes0_1 = tvb_get_ntohs(tvb, 0);
+
+ modulo = ((bytes0_1 & 0x2000) ? 128 : 8);
+ vc = (int)(bytes0_1 & 0x0FFF);
+
+ pinfo->ctype = CT_X25;
+ pinfo->circuit_id = vc;
+
+ if (bytes0_1 & X25_ABIT) toa = TRUE;
+ else toa = FALSE;
+
+ x25_pkt_len = get_x25_pkt_len(tvb);
+ if (x25_pkt_len < 3) /* packet too short */
+ {
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "Invalid/short X.25 packet");
+ if (tree)
+ proto_tree_add_protocol_format(tree, proto_x25, tvb, 0, -1,
+ "Invalid/short X.25 packet");
+ return;
+ }
+
+ pkt_type = tvb_get_guint8(tvb, 2);
+ if (PACKET_IS_DATA(pkt_type)) {
+ if (bytes0_1 & X25_QBIT)
+ q_bit_set = TRUE;
+ }
+
+ if (tree) {
+ ti = proto_tree_add_item(tree, proto_x25, tvb, 0, x25_pkt_len, FALSE);
+ x25_tree = proto_item_add_subtree(ti, ett_x25);
+ ti = proto_tree_add_item(x25_tree, hf_x25_gfi, tvb, 0, 2, FALSE);
+ gfi_tree = proto_item_add_subtree(ti, ett_x25_gfi);
+
+ if (PACKET_IS_DATA(pkt_type)) {
+ proto_tree_add_boolean(gfi_tree, hf_x25_qbit, tvb, 0, 2,
+ bytes0_1);
+ }
+ else if (pkt_type == X25_CALL_REQUEST ||
+ pkt_type == X25_CALL_ACCEPTED ||
+ pkt_type == X25_CLEAR_REQUEST ||
+ pkt_type == X25_CLEAR_CONFIRMATION) {
+ proto_tree_add_boolean(gfi_tree, hf_x25_abit, tvb, 0, 2,
+ bytes0_1);
+ }
+
+ if (pkt_type == X25_CALL_REQUEST || pkt_type == X25_CALL_ACCEPTED ||
+ PACKET_IS_DATA(pkt_type)) {
+ proto_tree_add_boolean(gfi_tree, hf_x25_dbit, tvb, 0, 2,
+ bytes0_1);
+ }
+ proto_tree_add_uint(gfi_tree, hf_x25_mod, tvb, 0, 2, bytes0_1);
+ }
+
+ switch (pkt_type) {
+ case X25_CALL_REQUEST:
+ switch (dir) {
+
+ case X25_FROM_DCE:
+ short_name = "Inc. call";
+ long_name = "Incoming call";
+ break;
+
+ case X25_FROM_DTE:
+ short_name = "Call req.";
+ long_name = "Call request";
+ break;
+
+ case X25_UNKNOWN:
+ short_name = "Inc. call/Call req.";
+ long_name = "Incoming call/Call request";
+ break;
+ }
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d", short_name, vc);
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb,
+ 0, 2, bytes0_1);
+ proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_CALL_REQUEST, "Packet Type: %s", long_name);
+ }
+ localoffset = 3;
+ if (localoffset < x25_pkt_len) { /* calling/called addresses */
+ if (toa)
+ x25_toa(x25_tree, &localoffset, tvb, pinfo);
+ else
+ x25_ntoa(x25_tree, &localoffset, tvb, pinfo, FALSE);
+ }
+
+ if (localoffset < x25_pkt_len) /* facilities */
+ dump_facilities(x25_tree, &localoffset, tvb);
+
+ if (localoffset < tvb_reported_length(tvb)) /* user data */
+ {
+ guint8 spi;
+ int is_x_264;
+ guint8 prt_id;
+
+ if (x25_tree) {
+ ti = proto_tree_add_text(x25_tree, tvb, localoffset, -1,
+ "User data");
+ userdata_tree = proto_item_add_subtree(ti, ett_x25_user_data);
+ }
+
+ /* X.263/ISO 9577 says that:
+
+ When CLNP or ESIS are run over X.25, the SPI
+ is 0x81 or 0x82, respectively; those are the
+ NLPIDs for those protocol.
+
+ When X.224/ISO 8073 COTP is run over X.25, and
+ when ISO 11570 explicit identification is being
+ used, the first octet of the user data field is
+ a TPDU length field, and the rest is "as defined
+ in ITU-T Rec. X.225 | ISO/IEC 8073, Annex B,
+ or ITU-T Rec. X.264 and ISO/IEC 11570".
+
+ When X.264/ISO 11570 default identification is
+ being used, there is no user data field in the
+ CALL REQUEST packet. This is for X.225/ISO 8073
+ COTP.
+
+ It also says that SPI values from 0x03 through 0x3f are
+ reserved and are in use by X.224/ISO 8073 Annex B and
+ X.264/ISO 11570. The note says that those values are
+ not NLPIDs, they're "used by the respective higher layer
+ protocol" and "not used for higher layer protocol
+ identification". I infer from this and from what
+ X.264/ISO 11570 says that this means that values in those
+ range are valid values for the first octet of an
+ X.224/ISO 8073 packet or for X.264/ISO 11570.
+
+ Annex B of X.225/ISO 8073 mentions some additional TPDU
+ types that can be put in what I presume is the user
+ data of connect requests. It says that:
+
+ The sending transport entity shall:
+
+ a) either not transmit any TPDU in the NS-user data
+ parameter of the N-CONNECT request primitive; or
+
+ b) transmit the UN-TPDU (see ITU-T Rec. X.264 and
+ ISO/IEC 11570) followed by the NCM-TPDU in the
+ NS-user data parameter of the N-CONNECT request
+ primitive.
+
+ I don't know if this means that the user data field
+ will contain a UN TPDU followed by an NCM TPDU or not.
+
+ X.264/ISO 11570 says that:
+
+ When default identification is being used,
+ X.225/ISO 8073 COTP is identified. No user data
+ is sent in the network-layer connection request.
+
+ When explicit identification is being used,
+ the user data is a UN TPDU ("Use of network
+ connection TPDU"), which specifies the transport
+ protocol to use over this network connection.
+ It also says that the length of a UN TPDU shall
+ not exceed 32 octets, i.e. shall not exceed 0x20;
+ it says this is "due to the desire not to conflict
+ with the protocol identifier field carried by X.25
+ CALL REQUEST/INCOMING CALL packets", and says that
+ field has values specified in X.244. X.244 has been
+ superseded by X.263/ISO 9577, so that presumably
+ means the goal is to allow a UN TPDU's length
+ field to be distinguished from an NLPID, allowing
+ you to tell whether X.264/ISO 11570 explicit
+ identification is being used or an NLPID is
+ being used as the SPI.
+
+ I read this as meaning that, if the ISO mechanisms are
+ used to identify the protocol being carried over X.25:
+
+ if there's no user data in the CALL REQUEST/
+ INCOMING CALL packet, it's COTP;
+
+ if there is user data, then:
+
+ if the first octet is less than or equal to
+ 32, it might be a UN TPDU, and that identifies
+ the transport protocol being used, and
+ it may be followed by more data, such
+ as a COTP NCM TPDU if it's COTP;
+
+ if the first octet is greater than 32, it's
+ an NLPID, *not* a TPDU length, and the
+ stuff following it is *not* a TPDU.
+
+ Figure A.2 of X.263/ISO 9577 seems to say that the
+ first octet of the user data is a TPDU length field,
+ in the range 0x03 through 0x82, and says they are
+ for X.225/ISO 8073 Annex B or X.264/ISO 11570.
+
+ However, X.264/ISO 11570 seems to imply that the length
+ field would be that of a UN TPDU, which must be less
+ than or equal to 0x20, and X.225/ISO 8073 Annex B seems
+ to indicate that the user data must begin with
+ an X.264/ISO 11570 UN TPDU, so I'd say that A.2 should
+ have said "in the range 0x03 through 0x20", instead
+ (the length value doesn't include the length field,
+ and the minimum UN TPDU has length, type, PRT-ID,
+ and SHARE, so that's 3 bytes without the length). */
+ spi = tvb_get_guint8(tvb, localoffset);
+ if (spi > 32 || spi < 3) {
+ /* First octet is > 32, or < 3, so the user data isn't an
+ X.264/ISO 11570 UN TPDU */
+ is_x_264 = FALSE;
+ } else {
+ /* First octet is >= 3 and <= 32, so the user data *might*
+ be an X.264/ISO 11570 UN TPDU. Check whether we have
+ enough data to see if it is. */
+ if (tvb_bytes_exist(tvb, localoffset+1, 1)) {
+ /* We do; check whether the second octet is 1. */
+ if (tvb_get_guint8(tvb, localoffset+1) == 0x01) {
+ /* Yes, the second byte is 1, so it looks like
+ a UN TPDU. */
+ is_x_264 = TRUE;
+ } else {
+ /* No, the second byte is not 1, so it's not a
+ UN TPDU. */
+ is_x_264 = FALSE;
+ }
+ } else {
+ /* We can't see the second byte of the putative UN
+ TPDU, so we don't know if that's what it is. */
+ is_x_264 = -1;
+ }
+ }
+ if (is_x_264 == -1) {
+ /*
+ * We don't know what it is; just skip it.
+ */
+ localoffset = tvb_length(tvb);
+ } else if (is_x_264) {
+ /* It looks like an X.264 UN TPDU, so show it as such. */
+ if (userdata_tree) {
+ proto_tree_add_text(userdata_tree, tvb, localoffset, 1,
+ "X.264 length indicator: %u",
+ spi);
+ proto_tree_add_text(userdata_tree, tvb, localoffset+1, 1,
+ "X.264 UN TPDU identifier: 0x%02X",
+ tvb_get_guint8(tvb, localoffset+1));
+ }
+ prt_id = tvb_get_guint8(tvb, localoffset+2);
+ if (userdata_tree) {
+ proto_tree_add_text(userdata_tree, tvb, localoffset+2, 1,
+ "X.264 protocol identifier: %s",
+ val_to_str(prt_id, prt_id_vals,
+ "Unknown (0x%02X)"));
+ proto_tree_add_text(userdata_tree, tvb, localoffset+3, 1,
+ "X.264 sharing strategy: %s",
+ val_to_str(tvb_get_guint8(tvb, localoffset+3),
+ sharing_strategy_vals, "Unknown (0x%02X)"));
+ }
+
+ /* XXX - dissect the variable part? */
+
+ /* The length doesn't include the length octet itself. */
+ localoffset += spi + 1;
+
+ switch (prt_id) {
+
+ case PRT_ID_ISO_8073:
+ /* ISO 8073 COTP */
+ if (!pinfo->fd->flags.visited)
+ x25_hash_add_proto_start(vc, pinfo->fd->num, ositp_handle);
+ /* XXX - dissect the rest of the user data as COTP?
+ That needs support for NCM TPDUs, etc. */
+ break;
+
+ case PRT_ID_ISO_8602:
+ /* ISO 8602 CLTP */
+ if (!pinfo->fd->flags.visited)
+ x25_hash_add_proto_start(vc, pinfo->fd->num, ositp_handle);
+ break;
+ }
+ } else if (is_x_264 == 0) {
+ /* It doesn't look like a UN TPDU, so compare the first
+ octet of the CALL REQUEST packet with various X.263/
+ ISO 9577 NLPIDs, as per Annex A of X.263/ISO 9577. */
+
+ if (userdata_tree) {
+ proto_tree_add_text(userdata_tree, tvb, localoffset, 1,
+ "X.263 secondary protocol ID: %s",
+ val_to_str(spi, nlpid_vals, "Unknown (0x%02x)"));
+ }
+
+ if (!pinfo->fd->flags.visited) {
+ /*
+ * Is there a dissector handle for this SPI?
+ * If so, assign it to this virtual circuit.
+ */
+ dissect = dissector_get_port_handle(x25_subdissector_table, spi);
+ if (dissect != NULL)
+ x25_hash_add_proto_start(vc, pinfo->fd->num, dissect);
+ }
+
+ /*
+ * If there's only one octet of user data, it's just
+ * an NLPID; don't try to dissect it.
+ */
+ if (localoffset + 1 == tvb_reported_length(tvb))
+ return;
+
+ /*
+ * There's more than one octet of user data, so we'll
+ * dissect it; for some protocols, the NLPID is considered
+ * to be part of the PDU, so, for those cases, we don't
+ * skip past it. For other protocols, we skip the NLPID.
+ */
+ switch (spi) {
+
+ case NLPID_ISO8473_CLNP:
+ case NLPID_ISO9542_ESIS:
+ case NLPID_ISO10589_ISIS:
+ case NLPID_ISO10747_IDRP:
+ case NLPID_SNDCF:
+ /*
+ * The NLPID is part of the PDU. Don't skip it.
+ * But if it's all there is to the PDU, don't
+ * bother dissecting it.
+ */
+ break;
+
+ case NLPID_SPI_X_29:
+ /*
+ * The first 4 bytes of the call user data are
+ * the SPI plus 3 reserved bytes; they are not
+ * part of the data to be dissected as X.29 data.
+ */
+ localoffset += 4;
+ break;
+
+ default:
+ /*
+ * The NLPID isn't part of the PDU - skip it.
+ * If that means there's nothing to dissect
+ */
+ localoffset++;
+ }
+ }
+ }
+ break;
+ case X25_CALL_ACCEPTED:
+ switch (dir) {
+
+ case X25_FROM_DCE:
+ short_name = "Call conn.";
+ long_name = "Call connected";
+ break;
+
+ case X25_FROM_DTE:
+ short_name = "Call acc.";
+ long_name = "Call accepted";
+ break;
+
+ case X25_UNKNOWN:
+ short_name = "Call conn./Call acc.";
+ long_name = "Call connected/Call accepted";
+ break;
+ }
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d", short_name, vc);
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+ proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_CALL_ACCEPTED, "Packet Type: %s", long_name);
+ }
+ localoffset = 3;
+ if (localoffset < x25_pkt_len) { /* calling/called addresses */
+ if (toa)
+ x25_toa(x25_tree, &localoffset, tvb, pinfo);
+ else
+ x25_ntoa(x25_tree, &localoffset, tvb, pinfo, FALSE);
+ }
+
+ if (localoffset < x25_pkt_len) /* facilities */
+ dump_facilities(x25_tree, &localoffset, tvb);
+ break;
+ case X25_CLEAR_REQUEST:
+ switch (dir) {
+
+ case X25_FROM_DCE:
+ short_name = "Clear ind.";
+ long_name = "Clear indication";
+ break;
+
+ case X25_FROM_DTE:
+ short_name = "Clear req.";
+ long_name = "Clear request";
+ break;
+
+ case X25_UNKNOWN:
+ short_name = "Clear ind./Clear req.";
+ long_name = "Clear indication/Clear request";
+ break;
+ }
+ if(check_col(pinfo->cinfo, COL_INFO)) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d %s - %s", short_name,
+ vc, clear_code(tvb_get_guint8(tvb, 3)),
+ clear_diag(tvb_get_guint8(tvb, 4)));
+ }
+ x25_hash_add_proto_end(vc, pinfo->fd->num);
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+ proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb,
+ localoffset+2, 1, X25_CLEAR_REQUEST, "Packet Type: %s",
+ long_name);
+ proto_tree_add_text(x25_tree, tvb, 3, 1,
+ "Cause : %s", clear_code(tvb_get_guint8(tvb, 3)));
+ proto_tree_add_text(x25_tree, tvb, 4, 1,
+ "Diagnostic : %s", clear_diag(tvb_get_guint8(tvb, 4)));
+ }
+ localoffset = x25_pkt_len;
+ break;
+ case X25_CLEAR_CONFIRMATION:
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Clear Conf. VC:%d", vc);
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_CLEAR_CONFIRMATION);
+ }
+ localoffset = x25_pkt_len;
+
+ if (localoffset < tvb_reported_length(tvb)) { /* extended clear conf format */
+ if (toa)
+ x25_toa(x25_tree, &localoffset, tvb, pinfo);
+ else
+ x25_ntoa(x25_tree, &localoffset, tvb, pinfo, FALSE);
+ }
+
+ if (localoffset < tvb_reported_length(tvb)) /* facilities */
+ dump_facilities(x25_tree, &localoffset, tvb);
+ break;
+ case X25_DIAGNOSTIC:
+ if(check_col(pinfo->cinfo, COL_INFO)) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Diag. %d",
+ (int)tvb_get_guint8(tvb, 3));
+ }
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_DIAGNOSTIC);
+ proto_tree_add_text(x25_tree, tvb, 3, 1,
+ "Diagnostic : %d", (int)tvb_get_guint8(tvb, 3));
+ }
+ localoffset = x25_pkt_len;
+ break;
+ case X25_INTERRUPT:
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Interrupt VC:%d", vc);
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_INTERRUPT);
+ }
+ localoffset = x25_pkt_len;
+ break;
+ case X25_INTERRUPT_CONFIRMATION:
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Interrupt Conf. VC:%d", vc);
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_INTERRUPT_CONFIRMATION);
+ }
+ localoffset = x25_pkt_len;
+ break;
+ case X25_RESET_REQUEST:
+ switch (dir) {
+
+ case X25_FROM_DCE:
+ short_name = "Reset ind.";
+ long_name = "Reset indication";
+ break;
+
+ case X25_FROM_DTE:
+ short_name = "Reset req.";
+ long_name = "Reset request";
+ break;
+
+ case X25_UNKNOWN:
+ short_name = "Reset ind./Reset req.";
+ long_name = "Reset indication/Reset request";
+ break;
+ }
+ if(check_col(pinfo->cinfo, COL_INFO)) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s VC:%d %s - Diag.:%d",
+ short_name, vc, reset_code(tvb_get_guint8(tvb, 3)),
+ (int)tvb_get_guint8(tvb, 4));
+ }
+ x25_hash_add_proto_end(vc, pinfo->fd->num);
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+ proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_RESET_REQUEST, "Packet Type: %s", long_name);
+ proto_tree_add_text(x25_tree, tvb, 3, 1,
+ "Cause : %s", reset_code(tvb_get_guint8(tvb, 3)));
+ proto_tree_add_text(x25_tree, tvb, 4, 1,
+ "Diagnostic : %d", (int)tvb_get_guint8(tvb, 4));
+ }
+ localoffset = x25_pkt_len;
+ break;
+ case X25_RESET_CONFIRMATION:
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_add_fstr(pinfo->cinfo, COL_INFO, "Reset conf. VC:%d", vc);
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, 0, 2, bytes0_1);
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_RESET_CONFIRMATION);
+ }
+ localoffset = x25_pkt_len;
+ break;
+ case X25_RESTART_REQUEST:
+ switch (dir) {
+
+ case X25_FROM_DCE:
+ short_name = "Restart ind.";
+ long_name = "Restart indication";
+ break;
+
+ case X25_FROM_DTE:
+ short_name = "Restart req.";
+ long_name = "Restart request";
+ break;
+
+ case X25_UNKNOWN:
+ short_name = "Restart ind./Restart req.";
+ long_name = "Restart indication/Restart request";
+ break;
+ }
+ if(check_col(pinfo->cinfo, COL_INFO)) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s - Diag.:%d",
+ short_name,
+ restart_code(tvb_get_guint8(tvb, 3)),
+ (int)tvb_get_guint8(tvb, 3));
+ }
+ if (x25_tree) {
+ proto_tree_add_uint_format(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_RESTART_REQUEST, "Packet Type: %s", long_name);
+ proto_tree_add_text(x25_tree, tvb, 3, 1,
+ "Cause : %s", restart_code(tvb_get_guint8(tvb, 3)));
+ proto_tree_add_text(x25_tree, tvb, 4, 1,
+ "Diagnostic : %d", (int)tvb_get_guint8(tvb, 4));
+ }
+ localoffset = x25_pkt_len;
+ break;
+ case X25_RESTART_CONFIRMATION:
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "Restart conf.");
+ if (x25_tree)
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_RESTART_CONFIRMATION);
+ localoffset = x25_pkt_len;
+ break;
+ case X25_REGISTRATION_REQUEST:
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "Registration req.");
+ if (x25_tree)
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_REGISTRATION_REQUEST);
+ localoffset = 3;
+ if (localoffset < x25_pkt_len)
+ x25_ntoa(x25_tree, &localoffset, tvb, pinfo, TRUE);
+
+ if (x25_tree) {
+ if (localoffset < x25_pkt_len)
+ proto_tree_add_text(x25_tree, tvb, localoffset, 1,
+ "Registration length: %d",
+ tvb_get_guint8(tvb, localoffset) & 0x7F);
+ if (localoffset+1 < x25_pkt_len)
+ proto_tree_add_text(x25_tree, tvb, localoffset+1,
+ tvb_get_guint8(tvb, localoffset) & 0x7F,
+ "Registration");
+ }
+ localoffset = tvb_reported_length(tvb);
+ break;
+ case X25_REGISTRATION_CONFIRMATION:
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "Registration conf.");
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb, 2, 1,
+ X25_REGISTRATION_CONFIRMATION);
+ proto_tree_add_text(x25_tree, tvb, 3, 1,
+ "Cause: %s", registration_code(tvb_get_guint8(tvb, 3)));
+ proto_tree_add_text(x25_tree, tvb, 4, 1,
+ "Diagnostic: %s", registration_code(tvb_get_guint8(tvb, 4)));
+ }
+ localoffset = 5;
+ if (localoffset < x25_pkt_len)
+ x25_ntoa(x25_tree, &localoffset, tvb, pinfo, TRUE);
+
+ if (x25_tree) {
+ if (localoffset < x25_pkt_len)
+ proto_tree_add_text(x25_tree, tvb, localoffset, 1,
+ "Registration length: %d",
+ tvb_get_guint8(tvb, localoffset) & 0x7F);
+ if (localoffset+1 < x25_pkt_len)
+ proto_tree_add_text(x25_tree, tvb, localoffset+1,
+ tvb_get_guint8(tvb, localoffset) & 0x7F,
+ "Registration");
+ }
+ localoffset = tvb_reported_length(tvb);
+ break;
+ default :
+ localoffset = 2;
+ if (PACKET_IS_DATA(pkt_type))
+ {
+ if(check_col(pinfo->cinfo, COL_INFO)) {
+ if (modulo == 8)
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Data VC:%d P(S):%d P(R):%d %s", vc,
+ (pkt_type >> 1) & 0x07,
+ (pkt_type >> 5) & 0x07,
+ (pkt_type & X25_MBIT_MOD8) ? " M" : "");
+ else
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Data VC:%d P(S):%d P(R):%d %s", vc,
+ tvb_get_guint8(tvb, localoffset+1) >> 1,
+ pkt_type >> 1,
+ (tvb_get_guint8(tvb, localoffset+1) & X25_MBIT_MOD128) ? " M" : "");
+ }
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+ 2, bytes0_1);
+ if (modulo == 8) {
+ proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
+ localoffset, 1, pkt_type);
+ proto_tree_add_boolean(x25_tree, hf_x25_mbit_mod8, tvb,
+ localoffset, 1, pkt_type);
+ proto_tree_add_uint(x25_tree, hf_x25_p_s_mod8, tvb,
+ localoffset, 1, pkt_type);
+ proto_tree_add_uint(x25_tree, hf_x25_type_data, tvb,
+ localoffset, 1, pkt_type);
+ }
+ else {
+ proto_tree_add_uint(x25_tree, hf_x25_p_r_mod128, tvb,
+ localoffset, 1, pkt_type);
+ proto_tree_add_uint(x25_tree, hf_x25_type_data, tvb,
+ localoffset, 1, pkt_type);
+ proto_tree_add_uint(x25_tree, hf_x25_p_s_mod128, tvb,
+ localoffset+1, 1,
+ tvb_get_guint8(tvb, localoffset+1));
+ proto_tree_add_boolean(x25_tree, hf_x25_mbit_mod128, tvb,
+ localoffset+1, 1,
+ tvb_get_guint8(tvb, localoffset+1));
+ }
+ }
+ if (modulo == 8) {
+ m_bit_set = pkt_type & X25_MBIT_MOD8;
+ localoffset += 1;
+ } else {
+ m_bit_set = tvb_get_guint8(tvb, localoffset+1) & X25_MBIT_MOD128;
+ localoffset += 2;
+ }
+ payload_len = tvb_reported_length_remaining(tvb, localoffset);
+ if (reassemble_x25) {
+ /*
+ * Reassemble received and sent traffic separately.
+ * We don't reassemble traffic with an unknown direction
+ * at all.
+ */
+ frag_key = vc;
+ if (side) {
+ /*
+ * OR in an extra bit to distinguish from traffic
+ * in the other direction.
+ */
+ frag_key |= 0x10000;
+ }
+ fd_head = fragment_add_seq_next(tvb, localoffset,
+ pinfo, frag_key,
+ x25_segment_table,
+ x25_reassembled_table,
+ payload_len, m_bit_set);
+ pinfo->fragmented = m_bit_set;
+
+ if (fd_head) {
+ if (fd_head->next) {
+ /* This is the last packet */
+ next_tvb = tvb_new_real_data(fd_head->data,
+ fd_head->len,
+ fd_head->len);
+ tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+ add_new_data_source(pinfo, next_tvb, "Reassembled X.25");
+ show_fragment_seq_tree(fd_head,
+ &x25_frag_items,
+ x25_tree,
+ pinfo, next_tvb);
+ }
+ }
+
+ if (m_bit_set && next_tvb == NULL) {
+ /*
+ * This isn't the last packet, so just
+ * show it as X.25 user data.
+ */
+ proto_tree_add_text(x25_tree, tvb, localoffset, -1,
+ "User data (%u byte%s)", payload_len,
+ plurality(payload_len, "", "s"));
+ return;
+ }
+ }
+ break;
+ }
+
+ /*
+ * Non-data packets (RR, RNR, REJ).
+ */
+ switch (PACKET_TYPE_FC(pkt_type))
+ {
+ case X25_RR:
+ if(check_col(pinfo->cinfo, COL_INFO)) {
+ if (modulo == 8)
+ col_add_fstr(pinfo->cinfo, COL_INFO, "RR VC:%d P(R):%d",
+ vc, (pkt_type >> 5) & 0x07);
+ else
+ col_add_fstr(pinfo->cinfo, COL_INFO, "RR VC:%d P(R):%d",
+ vc, tvb_get_guint8(tvb, localoffset+1) >> 1);
+ }
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+ 2, bytes0_1);
+ if (modulo == 8) {
+ proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
+ localoffset, 1, pkt_type);
+ proto_tree_add_uint(x25_tree, hf_x25_type_fc_mod8, tvb,
+ localoffset, 1, X25_RR);
+ }
+ else {
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
+ localoffset, 1, X25_RR);
+ proto_tree_add_item(x25_tree, hf_x25_p_r_mod128, tvb,
+ localoffset+1, 1, FALSE);
+ }
+ }
+ break;
+
+ case X25_RNR:
+ if(check_col(pinfo->cinfo, COL_INFO)) {
+ if (modulo == 8)
+ col_add_fstr(pinfo->cinfo, COL_INFO, "RNR VC:%d P(R):%d",
+ vc, (pkt_type >> 5) & 0x07);
+ else
+ col_add_fstr(pinfo->cinfo, COL_INFO, "RNR VC:%d P(R):%d",
+ vc, tvb_get_guint8(tvb, localoffset+1) >> 1);
+ }
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+ 2, bytes0_1);
+ if (modulo == 8) {
+ proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
+ localoffset, 1, pkt_type);
+ proto_tree_add_uint(x25_tree, hf_x25_type_fc_mod8, tvb,
+ localoffset, 1, X25_RNR);
+ }
+ else {
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
+ localoffset, 1, X25_RNR);
+ proto_tree_add_item(x25_tree, hf_x25_p_r_mod128, tvb,
+ localoffset+1, 1, FALSE);
+ }
+ }
+ break;
+
+ case X25_REJ:
+ if(check_col(pinfo->cinfo, COL_INFO)) {
+ if (modulo == 8)
+ col_add_fstr(pinfo->cinfo, COL_INFO, "REJ VC:%d P(R):%d",
+ vc, (pkt_type >> 5) & 0x07);
+ else
+ col_add_fstr(pinfo->cinfo, COL_INFO, "REJ VC:%d P(R):%d",
+ vc, tvb_get_guint8(tvb, localoffset+1) >> 1);
+ }
+ if (x25_tree) {
+ proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
+ 2, bytes0_1);
+ if (modulo == 8) {
+ proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb,
+ localoffset, 1, pkt_type);
+ proto_tree_add_uint(x25_tree, hf_x25_type_fc_mod8, tvb,
+ localoffset, 1, X25_REJ);
+ }
+ else {
+ proto_tree_add_uint(x25_tree, hf_x25_type, tvb,
+ localoffset, 1, X25_REJ);
+ proto_tree_add_item(x25_tree, hf_x25_p_r_mod128, tvb,
+ localoffset+1, 1, FALSE);
+ }
+ }
+ }
+ localoffset += (modulo == 8) ? 1 : 2;
+ }
+
+ if (localoffset >= tvb_reported_length(tvb))
+ return;
+ if (pinfo->fragmented)
+ return;
+
+ if (!next_tvb)
+ next_tvb = tvb_new_subset(tvb, localoffset, -1, -1);
+
+ saved_private_data = pinfo->private_data;
+ pinfo->private_data = &q_bit_set;
+
+ /* See if there's already a dissector for this circuit. */
+ if (try_circuit_dissector(CT_X25, vc, pinfo->fd->num, next_tvb, pinfo,
+ tree)) {
+ pinfo->private_data = saved_private_data;
+ return; /* found it and dissected it */
+ }
+
+ /* Did the user suggest QLLC/SNA? */
+ if (payload_is_qllc_sna) {
+ /* Yes - dissect it as QLLC/SNA. */
+ if (!pinfo->fd->flags.visited)
+ x25_hash_add_proto_start(vc, pinfo->fd->num, qllc_handle);
+ call_dissector(qllc_handle, next_tvb, pinfo, tree);
+ pinfo->private_data = saved_private_data;
+ return;
+ }
+
+ /* If the Call Req. has not been captured, let's look at the first
+ byte of the payload to see if this looks like IP or CLNP. */
+ switch (tvb_get_guint8(tvb, localoffset)) {
+
+ case 0x45:
+ /* Looks like an IP header */
+ if (!pinfo->fd->flags.visited)
+ x25_hash_add_proto_start(vc, pinfo->fd->num, ip_handle);
+ call_dissector(ip_handle, next_tvb, pinfo, tree);
+ pinfo->private_data = saved_private_data;
+ return;
+
+ case NLPID_ISO8473_CLNP:
+ if (!pinfo->fd->flags.visited)
+ x25_hash_add_proto_start(vc, pinfo->fd->num, clnp_handle);
+ call_dissector(clnp_handle, next_tvb, pinfo, tree);
+ pinfo->private_data = saved_private_data;
+ return;
+ }
+
+ /* Try the heuristic dissectors. */
+ if (dissector_try_heuristic(x25_heur_subdissector_list, next_tvb, pinfo,
+ tree)) {
+ pinfo->private_data = saved_private_data;
+ return;
+ }
+
+ /* All else failed; dissect it as raw data */
+ call_dissector(data_handle, next_tvb, pinfo, tree);
+ pinfo->private_data = saved_private_data;
+}
+
+/*
+ * X.25 dissector for use when "pinfo->pseudo_header" points to a
+ * "struct x25_phdr".
+ */
+static void
+dissect_x25_dir(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ dissect_x25_common(tvb, pinfo, tree,
+ (pinfo->pseudo_header->x25.flags & FROM_DCE) ? X25_FROM_DCE :
+ X25_FROM_DTE,
+ pinfo->pseudo_header->x25.flags & FROM_DCE);
+}
+
+/*
+ * X.25 dissector for use when "pinfo->pseudo_header" doesn't point to a
+ * "struct x25_phdr".
+ */
+static void
+dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ int direction;
+
+ /*
+ * We don't know if this packet is DTE->DCE or DCE->DCE.
+ * However, we can, at least, distinguish between the two
+ * sides of the conversation, based on the addresses and
+ * ports.
+ */
+ direction = CMP_ADDRESS(&pinfo->src, &pinfo->dst);
+ if (direction == 0)
+ direction = (pinfo->srcport > pinfo->destport)*2 - 1;
+ dissect_x25_common(tvb, pinfo, tree, X25_UNKNOWN, direction > 0);
+}
+
+static void
+x25_reassemble_init(void)
+{
+ fragment_table_init(&x25_segment_table);
+ reassembled_table_init(&x25_reassembled_table);
+}
+
+void
+proto_register_x25(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_x25_gfi,
+ { "GFI", "x.25.gfi", FT_UINT16, BASE_DEC, NULL, 0xF000,
+ "General format identifier", HFILL }},
+ { &hf_x25_abit,
+ { "A Bit", "x.25.a", FT_BOOLEAN, 16, NULL, X25_ABIT,
+ "Address Bit", HFILL }},
+ { &hf_x25_qbit,
+ { "Q Bit", "x.25.q", FT_BOOLEAN, 16, NULL, X25_QBIT,
+ "Qualifier Bit", HFILL }},
+ { &hf_x25_dbit,
+ { "D Bit", "x.25.d", FT_BOOLEAN, 16, NULL, X25_DBIT,
+ "Delivery Confirmation Bit", HFILL }},
+ { &hf_x25_mod,
+ { "Modulo", "x.25.mod", FT_UINT16, BASE_DEC, VALS(vals_modulo), 0x3000,
+ "Specifies whether the frame is modulo 8 or 128", HFILL }},
+ { &hf_x25_lcn,
+ { "Logical Channel", "x.25.lcn", FT_UINT16, BASE_DEC, NULL, 0x0FFF,
+ "Logical Channel Number", HFILL }},
+ { &hf_x25_type,
+ { "Packet Type", "x.25.type", FT_UINT8, BASE_HEX, VALS(vals_x25_type), 0x0,
+ "Packet Type", HFILL }},
+ { &hf_x25_type_fc_mod8,
+ { "Packet Type", "x.25.type", FT_UINT8, BASE_HEX, VALS(vals_x25_type), 0x1F,
+ "Packet Type", HFILL }},
+ { &hf_x25_type_data,
+ { "Packet Type", "x.25.type", FT_UINT8, BASE_HEX, VALS(vals_x25_type), 0x01,
+ "Packet Type", HFILL }},
+ { &hf_x25_p_r_mod8,
+ { "P(R)", "x.25.p_r", FT_UINT8, BASE_DEC, NULL, 0xE0,
+ "Packet Receive Sequence Number", HFILL }},
+ { &hf_x25_p_r_mod128,
+ { "P(R)", "x.25.p_r", FT_UINT8, BASE_DEC, NULL, 0xFE,
+ "Packet Receive Sequence Number", HFILL }},
+ { &hf_x25_mbit_mod8,
+ { "M Bit", "x.25.m", FT_BOOLEAN, 8, TFS(&m_bit_tfs), X25_MBIT_MOD8,
+ "More Bit", HFILL }},
+ { &hf_x25_mbit_mod128,
+ { "M Bit", "x.25.m", FT_BOOLEAN, 8, TFS(&m_bit_tfs), X25_MBIT_MOD128,
+ "More Bit", HFILL }},
+ { &hf_x25_p_s_mod8,
+ { "P(S)", "x.25.p_s", FT_UINT8, BASE_DEC, NULL, 0x0E,
+ "Packet Send Sequence Number", HFILL }},
+ { &hf_x25_p_s_mod128,
+ { "P(S)", "x.25.p_s", FT_UINT8, BASE_DEC, NULL, 0xFE,
+ "Packet Send Sequence Number", HFILL }},
+ { &hf_x25_segment_overlap,
+ { "Fragment overlap", "x25.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Fragment overlaps with other fragments", HFILL }},
+
+ { &hf_x25_segment_overlap_conflict,
+ { "Conflicting data in fragment overlap", "x25.fragment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Overlapping fragments contained conflicting data", HFILL }},
+
+ { &hf_x25_segment_multiple_tails,
+ { "Multiple tail fragments found", "x25.fragment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Several tails were found when defragmenting the packet", HFILL }},
+
+ { &hf_x25_segment_too_long_segment,
+ { "Fragment too long", "x25.fragment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Fragment contained data past end of packet", HFILL }},
+
+ { &hf_x25_segment_error,
+ { "Defragmentation error", "x25.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "Defragmentation error due to illegal fragments", HFILL }},
+
+ { &hf_x25_segment,
+ { "X.25 Fragment", "x25.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "X25 Fragment", HFILL }},
+
+ { &hf_x25_segments,
+ { "X.25 Fragments", "x25.fragments", FT_NONE, BASE_NONE, NULL, 0x0,
+ "X.25 Fragments", HFILL }},
+ };
+ static gint *ett[] = {
+ &ett_x25,
+ &ett_x25_gfi,
+ &ett_x25_fac,
+ &ett_x25_fac_unknown,
+ &ett_x25_fac_mark,
+ &ett_x25_fac_reverse,
+ &ett_x25_fac_throughput,
+ &ett_x25_fac_cug,
+ &ett_x25_fac_called_modif,
+ &ett_x25_fac_cug_outgoing_acc,
+ &ett_x25_fac_throughput_min,
+ &ett_x25_fac_express_data,
+ &ett_x25_fac_bilateral_cug,
+ &ett_x25_fac_packet_size,
+ &ett_x25_fac_window_size,
+ &ett_x25_fac_rpoa_selection,
+ &ett_x25_fac_transit_delay,
+ &ett_x25_fac_call_transfer,
+ &ett_x25_fac_called_addr_ext,
+ &ett_x25_fac_ete_transit_delay,
+ &ett_x25_fac_calling_addr_ext,
+ &ett_x25_fac_call_deflect,
+ &ett_x25_fac_priority,
+ &ett_x25_user_data,
+ &ett_x25_segment,
+ &ett_x25_segments
+ };
+ module_t *x25_module;
+
+ proto_x25 = proto_register_protocol ("X.25", "X.25", "x.25");
+ proto_register_field_array (proto_x25, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ x25_subdissector_table = register_dissector_table("x.25.spi",
+ "X.25 secondary protocol identifier", FT_UINT8, BASE_HEX);
+ register_heur_dissector_list("x.25", &x25_heur_subdissector_list);
+
+ register_dissector("x.25_dir", dissect_x25_dir, proto_x25);
+ register_dissector("x.25", dissect_x25, proto_x25);
+
+ /* Preferences */
+ x25_module = prefs_register_protocol(proto_x25, NULL);
+ prefs_register_obsolete_preference(x25_module, "non_q_bit_is_sna");
+ prefs_register_bool_preference(x25_module, "payload_is_qllc_sna",
+ "Default to QLLC/SNA",
+ "If CALL REQUEST not seen or didn't specify protocol, dissect as QLLC/SNA",
+ &payload_is_qllc_sna);
+ prefs_register_bool_preference(x25_module, "reassemble",
+ "Reassemble fragmented X.25 packets",
+ "Reassemble fragmented X.25 packets",
+ &reassemble_x25);
+ register_init_routine(&x25_reassemble_init);
+}
+
+void
+proto_reg_handoff_x25(void)
+{
+ dissector_handle_t x25_handle;
+
+ /*
+ * Get handles for various dissectors.
+ */
+ ip_handle = find_dissector("ip");
+ clnp_handle = find_dissector("clnp");
+ ositp_handle = find_dissector("ositp");
+ qllc_handle = find_dissector("qllc");
+ data_handle = find_dissector("data");
+
+ x25_handle = find_dissector("x.25");
+ dissector_add("llc.dsap", SAP_X25, x25_handle);
+}