/* packet-x25.c * Routines for x25 packet disassembly * Olivier Abad * * $Id: packet-x25.c,v 1.50 2001/07/05 22:10:09 oabad Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs * 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 #ifdef HAVE_SYS_TYPES_H # include #endif #include #include #include #include #include "llcsaps.h" #include "packet.h" #include "nlpid.h" #define FROM_DCE 0x80 #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 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_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 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 dissector_handle_t ip_handle; static dissector_handle_t ositp_handle; /* * each vc_info node contains : * the time of the first frame using this dissector (secs and usecs) * the time of the last frame using this dissector (0 if it is unknown) * a pointer to the dissector * * the "time of first frame" is initialized when a Call Req. is received * the "time of last frame" is initialized when a Clear, Reset, or Restart * is received */ typedef struct _vc_info { guint32 first_frame_secs, first_frame_usecs; guint32 last_frame_secs, last_frame_usecs; dissector_handle_t dissect; struct _vc_info *next; } vc_info; /* * the hash table will contain linked lists of global_vc_info * each global_vc_info struct contains : * the VC number (the hash table is indexed with VC % 64) * a linked list of vc_info */ typedef struct _global_vc_info { int vc_num; vc_info *info; struct _global_vc_info *next; } global_vc_info; static global_vc_info *hash_table[64]; static void free_vc_info(vc_info *pt) { vc_info *vci = pt; while (pt) { vci = pt; pt = pt->next; g_free(vci); } } static void reinit_x25_hashtable(void) { int i; for (i=0; i<64; i++) { if (hash_table[i]) /* not NULL ==> free */ { global_vc_info *hash_ent, *hash_ent2; hash_ent2 = hash_ent = hash_table[i]; while (hash_ent) { hash_ent2 = hash_ent; hash_ent = hash_ent->next; free_vc_info(hash_ent2->info); g_free(hash_ent2); } hash_table[i]=0; } } } static void x25_hash_add_proto_start(guint16 vc, guint32 frame_secs, guint32 frame_usecs, dissector_handle_t dissect) { int idx = vc % 64; global_vc_info *hash_ent; global_vc_info *hash_ent2; if (hash_table[idx] == 0) { hash_ent = (global_vc_info *)g_malloc(sizeof(global_vc_info)); if (!hash_ent) { fprintf(stderr, "Could not allocate space for hash structure in dissect_x25\n"); exit(1); } hash_ent->vc_num = vc; hash_ent->next=0; hash_ent->info = (vc_info *)g_malloc(sizeof(vc_info)); if (!hash_ent->info) { fprintf(stderr, "Could not allocate space for hash structure in dissect_x25\n"); exit(1); } hash_ent->info->first_frame_secs = frame_secs; hash_ent->info->first_frame_usecs = frame_usecs; hash_ent->info->last_frame_secs = 0; hash_ent->info->last_frame_usecs = 0; hash_ent->info->dissect = dissect; hash_ent->info->next = 0; hash_table[idx] = hash_ent; } else { hash_ent2 = hash_ent = hash_table[idx]; /* search an entry with the same VC number */ while (hash_ent != NULL && hash_ent->vc_num != vc) { hash_ent2 = hash_ent; hash_ent = hash_ent->next; } if (hash_ent != NULL) /* hash_ent->vc_num == vc */ { vc_info *vci = hash_ent->info; while (vci->next) vci = vci->next; /* last element */ if (vci->dissect == dissect) { vci->last_frame_secs = 0; vci->last_frame_usecs = 0; } else { vci->next = (vc_info *)g_malloc(sizeof(vc_info)); if (vci->next == 0) { fprintf(stderr, "Could not allocate space for hash structure in dissect_x25\n"); exit(1); } vci->next->first_frame_secs = frame_secs; vci->next->first_frame_usecs = frame_usecs; vci->next->last_frame_secs = 0; vci->next->last_frame_usecs = 0; vci->next->dissect = dissect; vci->next->next = 0; } } else /* new vc number */ { hash_ent2->next = (global_vc_info *)g_malloc(sizeof(global_vc_info)); if (!hash_ent2->next) { fprintf(stderr, "Could not allocate space for hash structure in dissect_x25\n"); exit(1); } hash_ent2->next->info = (vc_info *)g_malloc(sizeof(vc_info)); if (!hash_ent2->next->info) { fprintf(stderr, "Could not allocate space for hash structure in dissect_x25\n"); exit(1); } hash_ent2->next->info->first_frame_secs = frame_secs; hash_ent2->next->info->first_frame_usecs = frame_usecs; hash_ent2->next->info->last_frame_secs = 0; hash_ent2->next->info->last_frame_usecs = 0; hash_ent2->next->info->dissect = dissect; hash_ent2->next->info->next = 0; } } } static void x25_hash_add_proto_end(guint16 vc, guint32 frame_secs, guint32 frame_usecs) { global_vc_info *hash_ent = hash_table[vc%64]; vc_info *vci; if (!hash_ent) return; while(hash_ent->vc_num != vc) hash_ent = hash_ent->next; if (!hash_ent) return; vci = hash_ent->info; while (vci->next) vci = vci->next; vci->last_frame_secs = frame_secs; vci->last_frame_usecs = frame_usecs; } static dissector_handle_t x25_hash_get_dissect(guint32 frame_secs, guint32 frame_usecs, guint16 vc) { global_vc_info *hash_ent = hash_table[vc%64]; vc_info *vci; vc_info *vci2; if (!hash_ent) return 0; while(hash_ent && hash_ent->vc_num != vc) hash_ent = hash_ent->next; if (!hash_ent) return 0; /* a hash_ent was found for this VC number */ vci2 = vci = hash_ent->info; /* looking for an entry matching our frame time */ while (vci && (vci->last_frame_secs < frame_secs || (vci->last_frame_secs == frame_secs && vci->last_frame_usecs < frame_usecs))) { vci2 = vci; vci = vci->next; } /* we reached last record, and previous record has a non zero * last frame time ==> no dissector */ if (!vci && (vci2->last_frame_secs || vci2->last_frame_usecs)) return 0; /* we reached last record, and previous record has a zero last frame time * ==> dissector for previous frame has not been "stopped" by a Clear, etc */ if (!vci) { /* if the start time for vci2 is greater than our frame time * ==> no dissector */ if (frame_secs < vci2->first_frame_secs || (frame_secs == vci2->first_frame_secs && frame_usecs < vci2->first_frame_usecs)) return 0; else return vci2->dissect; } /* our frame time is before vci's end. Check if it is adter vci's start */ if (frame_secs < vci->first_frame_secs || (frame_secs == vci->first_frame_secs && frame_usecs < vci->first_frame_usecs)) return 0; else return vci->dissect; } 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 == 0x09) return "Out Of Order"; if (code == 0x11) return "Remote Procedure Error"; 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"; if (code == 0x03) return "Invalid Facility Requested"; if (code == 0x0B) return "Access Barred"; if (code == 0x13) return "Local Procedure Error"; if (code == 0x05) return "Network Congestion"; if (code == 0x0D) return "Not Obtainable"; if (code == 0x15) return "RPOA Out Of Order"; 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"; 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); 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); 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, frame_data *fd, gboolean toa) { 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 >> 4) & 0x0F; len2 = (byte >> 0) & 0x0F; if (tree) { proto_tree_add_text(tree, tvb, *offset, 1, decode_numeric_bitfield(byte, 0xF0, 1*8, toa ? "Called address length : %u" : "Calling address length : %u")); proto_tree_add_text(tree, tvb, *offset, 1, decode_numeric_bitfield(byte, 0x0F, 1*8, toa ? "Calling 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 (toa) { if (check_col(fd, COL_RES_DL_DST)) col_add_str(fd, COL_RES_DL_DST, addr1); } else { if(check_col(fd, COL_RES_DL_SRC)) col_add_str(fd, COL_RES_DL_SRC, addr1); } if (tree) proto_tree_add_text(tree, tvb, *offset, (len1 + 1) / 2, "%s address : %s", toa ? "Called" : "Calling", addr1); } if (len2) { if (toa) { if (check_col(fd, COL_RES_DL_SRC)) col_add_str(fd, COL_RES_DL_SRC, addr2); } else { if(check_col(fd, COL_RES_DL_DST)) col_add_str(fd, COL_RES_DL_DST, addr2); } if (tree) proto_tree_add_text(tree, tvb, *offset + len1/2, (len2+1)/2+(len1%2+(len2+1)%2)/2, "%s address : %s", toa ? "Calling" : "Called", 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: 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 ((byte2 & 0x01) == X25_DATA) return MIN(tvb_reported_length(tvb),3); switch (byte2 & 0x1F) { 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; } #define PRT_ID_ISO_8073 0x01 static const value_string prt_id_vals[] = { {PRT_ID_ISO_8073, "ISO 8073 COTP"}, {0x02, "ISO 8602"}, {0x03, "ISO 10732 in conjunction with ISO 8073"}, {0x04, "ISO 10736 in conjunction with ISO 8602"}, {0x00, NULL} }; static const value_string sharing_strategy_vals[] = { {0x00, "No sharing"}, {0x00, NULL} }; static void dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *x25_tree=0, *gfi_tree=0; proto_item *ti; guint localoffset=0; guint x25_pkt_len; int modulo; guint16 vc; dissector_handle_t dissect; gboolean toa; /* TOA/NPI address format */ guint16 bytes0_1; guint8 pkt_type; tvbuff_t *next_tvb; if (check_col(pinfo->fd, COL_PROTOCOL)) col_set_str(pinfo->fd, COL_PROTOCOL, "X.25"); bytes0_1 = tvb_get_ntohs(tvb, 0); modulo = ((bytes0_1 & 0x2000) ? 128 : 8); vc = (int)(bytes0_1 & 0x0FFF); if (bytes0_1 & 0x8000) 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->fd, COL_INFO)) col_set_str(pinfo->fd, COL_INFO, "Invalid/short X.25 packet"); if (tree) proto_tree_add_protocol_format(tree, proto_x25, tvb, 0, tvb_length(tvb), "Invalid/short X.25 packet"); return; } pkt_type = tvb_get_guint8(tvb, 2); 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 ((pkt_type & 0x01) == X25_DATA) 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 || (pkt_type & 0x01) == X25_DATA) 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: if (check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "%s VC:%d", (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Inc. call" : "Call req." , 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, (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Incoming call" : "Call request"); } localoffset = 3; if (localoffset < x25_pkt_len) /* calling/called addresses */ x25_ntoa(x25_tree, &localoffset, tvb, pinfo->fd, toa); if (localoffset < x25_pkt_len) /* facilities */ dump_facilities(x25_tree, &localoffset, tvb); if (localoffset < tvb_reported_length(tvb)) /* user data */ { guint8 spi; guint8 prt_id; /* Compare the first octet of the CALL REQUEST packet with various ISO 9577 NLPIDs, as per Annex A of ISO 9577. */ spi = tvb_get_guint8(tvb, localoffset); switch (spi) { /* * XXX - handle other NLPIDs, e.g. PPP? * See RFC 1356 for information on at least some other * ways of running other protocols atop X.25. */ case NLPID_IP: x25_hash_add_proto_start(vc, pinfo->fd->abs_secs, pinfo->fd->abs_usecs, ip_handle); if (x25_tree) proto_tree_add_text(x25_tree, tvb, localoffset, 1, "X.224 secondary protocol ID: IP"); localoffset++; break; default: if ((spi >= 0x03 && spi <= 0x82) && tvb_get_guint8(tvb, localoffset+1) == 0x01) { /* ISO 9577 claims that a SPI in that range is a length field for X.224/ISO 8073 or X.264/ISO 11570; however, some of them collide with NLPIDs such as 0x81 for ISO 8473 CLNP or ISO 8542 ESIS, so I don't know how you run those over X.25, assuming you do. I'm also not sure what the "or" means there; it looks as if X.264 specifies the layout of a "UN TPDU" ("Use of network connection TPDU"), which specifies the transport protocol to use over this network connection, and 0x03 0x01 0x01 0x00 is such a TPDU, with a length of 3, a UN field of 1 (as is required), a PRT-ID ("protocol identifier") field of 1 (X.224/ISO 8073, a/k/a COTP service), and a SHARE ("sharing strategy") field of 0 ("no sharing", which is the only one allowed). So we'll assume that's what it is, as the SPI is in the right range for a length, and the UN field is 0x01. */ prt_id = tvb_get_guint8(tvb, localoffset+2); if (x25_tree) { proto_tree_add_text(x25_tree, tvb, localoffset, 1, "X.264 length indicator: %u", spi); proto_tree_add_text(x25_tree, tvb, localoffset+1, 1, "X.264 UN TPDU identifier: 0x%02X", tvb_get_guint8(tvb, localoffset+1)); proto_tree_add_text(x25_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(x25_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 */ x25_hash_add_proto_start(vc, pinfo->fd->abs_secs, pinfo->fd->abs_usecs, ositp_handle); break; default: goto unknown; } } else { unknown: if (x25_tree) { proto_tree_add_text(x25_tree, tvb, localoffset, tvb_reported_length(tvb)-localoffset, "Data"); } localoffset = tvb_reported_length(tvb); } } } break; case X25_CALL_ACCEPTED: if(check_col(pinfo->fd, COL_INFO)) col_add_fstr(pinfo->fd, COL_INFO, "%s VC:%d", (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Call conn." : "Call acc." , 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, (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Call connected" : "Call accepted"); } localoffset = 3; if (localoffset < x25_pkt_len) /* calling/called addresses */ x25_ntoa(x25_tree, &localoffset, tvb, pinfo->fd, toa); if (localoffset < x25_pkt_len) /* facilities */ dump_facilities(x25_tree, &localoffset, tvb); if (localoffset < tvb_reported_length(tvb)) { /* user data */ if (x25_tree) proto_tree_add_text(x25_tree, tvb, localoffset, tvb_reported_length(tvb)-localoffset, "Data"); localoffset=tvb_reported_length(tvb); } break; case X25_CLEAR_REQUEST: if(check_col(pinfo->fd, COL_INFO)) { col_add_fstr(pinfo->fd, COL_INFO, "%s VC:%d %s - %s", (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Clear ind." : "Clear req." , vc, clear_code(tvb_get_guint8(tvb, 3)), clear_diag(tvb_get_guint8(tvb, 4))); } x25_hash_add_proto_end(vc, pinfo->fd->abs_secs, pinfo->fd->abs_usecs); 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, (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Clear indication" : "Clear request"); 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->fd, COL_INFO)) col_add_fstr(pinfo->fd, 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 */ x25_ntoa(x25_tree, &localoffset, tvb, pinfo->fd, toa); if (localoffset < tvb_reported_length(tvb)) /* facilities */ dump_facilities(x25_tree, &localoffset, tvb); break; case X25_DIAGNOSTIC: if(check_col(pinfo->fd, COL_INFO)) { col_add_fstr(pinfo->fd, 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->fd, COL_INFO)) col_add_fstr(pinfo->fd, 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->fd, COL_INFO)) col_add_fstr(pinfo->fd, 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: if(check_col(pinfo->fd, COL_INFO)) { col_add_fstr(pinfo->fd, COL_INFO, "%s VC:%d %s - Diag.:%d", (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Reset ind." : "Reset req.", vc, reset_code(tvb_get_guint8(tvb, 3)), (int)tvb_get_guint8(tvb, 4)); } x25_hash_add_proto_end(vc, pinfo->fd->abs_secs, pinfo->fd->abs_usecs); 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, (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Reset indication" : "Reset request"); 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->fd, COL_INFO)) col_add_fstr(pinfo->fd, 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: if(check_col(pinfo->fd, COL_INFO)) { col_add_fstr(pinfo->fd, COL_INFO, "%s %s - Diag.:%d", (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Restart ind." : "Restart req.", 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, (pinfo->pseudo_header->x25.flags & FROM_DCE) ? "Restart indication" : "Restart request"); 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->fd, COL_INFO)) col_set_str(pinfo->fd, 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->fd, COL_INFO)) col_set_str(pinfo->fd, 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->fd, FALSE); 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->fd, COL_INFO)) col_set_str(pinfo->fd, 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->fd, 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 ((pkt_type & 0x01) == X25_DATA) { if(check_col(pinfo->fd, COL_INFO)) { if (modulo == 8) col_add_fstr(pinfo->fd, COL_INFO, "Data VC:%d P(S):%d P(R):%d %s", vc, (pkt_type >> 1) & 0x07, (pkt_type >> 5) & 0x07, ((pkt_type >> 4) & 0x01) ? " M" : ""); else col_add_fstr(pinfo->fd, 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) & 0x01) ? " M" : ""); } if (x25_tree) { proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2, 2, bytes0_1); proto_tree_add_uint_hidden(x25_tree, hf_x25_type, tvb, localoffset, 1, X25_DATA); if (modulo == 8) { proto_tree_add_uint(x25_tree, hf_x25_p_r_mod8, tvb, localoffset, 1, pkt_type); if (pkt_type & 0x10) 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_text(x25_tree, tvb, localoffset, 1, decode_boolean_bitfield(pkt_type, 0x01, 1*8, NULL, "DATA")); } 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_p_s_mod128, tvb, localoffset+1, 1, tvb_get_guint8(tvb, localoffset+1)); if (tvb_get_guint8(tvb, localoffset+1) & 0x01) proto_tree_add_boolean(x25_tree, hf_x25_mbit_mod128, tvb, localoffset+1, 1, tvb_get_guint8(tvb, localoffset+1)); } } localoffset += (modulo == 8) ? 1 : 2; break; } switch (pkt_type & 0x1F) { case X25_RR: if(check_col(pinfo->fd, COL_INFO)) { if (modulo == 8) col_add_fstr(pinfo->fd, COL_INFO, "RR VC:%d P(R):%d", vc, (pkt_type >> 5) & 0x07); else col_add_fstr(pinfo->fd, 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, 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->fd, COL_INFO)) { if (modulo == 8) col_add_fstr(pinfo->fd, COL_INFO, "RNR VC:%d P(R):%d", vc, (pkt_type >> 5) & 0x07); else col_add_fstr(pinfo->fd, 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, 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->fd, COL_INFO)) { if (modulo == 8) col_add_fstr(pinfo->fd, COL_INFO, "REJ VC:%d P(R):%d", vc, (pkt_type >> 5) & 0x07); else col_add_fstr(pinfo->fd, 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, 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; next_tvb = tvb_new_subset(tvb, localoffset, -1, -1); /* search the dissector in the hash table */ if ((dissect = x25_hash_get_dissect(pinfo->fd->abs_secs, pinfo->fd->abs_usecs, vc))) call_dissector(dissect, next_tvb, pinfo, tree); else { /* If the Call Req. has not been captured, assume these packets carry IP */ if (tvb_get_guint8(tvb, localoffset) == 0x45) { x25_hash_add_proto_start(vc, pinfo->fd->abs_secs, pinfo->fd->abs_usecs, ip_handle); call_dissector(ip_handle, next_tvb, pinfo, tree); } else { dissect_data(next_tvb, 0, pinfo, tree); } } } void proto_register_x25(void) { static hf_register_info hf[] = { { &hf_x25_gfi, { "GFI", "x.25.gfi", FT_UINT16, BASE_BIN, NULL, 0xF000, "General format identifier", HFILL }}, { &hf_x25_abit, { "A Bit", "x.25.a", FT_BOOLEAN, 16, NULL, 0x8000, "Address Bit", HFILL }}, { &hf_x25_qbit, { "Q Bit", "x.25.q", FT_BOOLEAN, 16, NULL, 0x8000, "Qualifier Bit", HFILL }}, { &hf_x25_dbit, { "D Bit", "x.25.d", FT_BOOLEAN, 16, NULL, 0x4000, "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_p_r_mod8, { "P(R)", "x.25.p_r", FT_UINT8, BASE_HEX, NULL, 0xE0, "Packet Receive Sequence Number", HFILL }}, { &hf_x25_p_r_mod128, { "P(R)", "x.25.p_r", FT_UINT8, BASE_HEX, NULL, 0xFE, "Packet Receive Sequence Number", HFILL }}, { &hf_x25_mbit_mod8, { "M Bit", "x.25.m", FT_BOOLEAN, 8, NULL, 0x10, "More Bit", HFILL }}, { &hf_x25_mbit_mod128, { "M Bit", "x.25.m", FT_BOOLEAN, 8, NULL, 0x01, "More Bit", HFILL }}, { &hf_x25_p_s_mod8, { "P(S)", "x.25.p_s", FT_UINT8, BASE_HEX, NULL, 0x0E, "Packet Send Sequence Number", HFILL }}, { &hf_x25_p_s_mod128, { "P(S)", "x.25.p_s", FT_UINT8, BASE_HEX, NULL, 0xFE, "Packet Send Sequence Number", 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 }; 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)); register_init_routine(&reinit_x25_hashtable); register_dissector("x.25", dissect_x25, proto_x25); } void proto_reg_handoff_x25(void) { /* * Get handles for the IP and OSI TP (COTP/CLTP) dissectors. */ ip_handle = find_dissector("ip"); ositp_handle = find_dissector("ositp"); dissector_add("llc.dsap", SAP_X25, dissect_x25, proto_x25); }