Olivier Abad's patch to add dissectors for LAP-B and X.25, and wiretap
authorguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Mon, 2 Aug 1999 02:26:22 +0000 (02:26 +0000)
committerguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Mon, 2 Aug 1999 02:26:22 +0000 (02:26 +0000)
support for RADCOM Ltd.'s WAN/LAN analyzers (see

http://www.radcom-inc.com/

).  Note: a

Make "S" a mnemonic for "Summary" in the "Tools" menu.

Move the routine, used for the "Tools/Summary" display, that turns a
wiretap file type into a descriptive string for it into the wiretap
library itself, expand on some of its descriptions, and add an entry for
files from a RADCOM analyzer.

Have "Tools/Summary" display the snapshot length for the capture.

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@417 f5534014-38df-0310-8fa8-9805f1628bb7

packet-lapb.c [new file with mode: 0644]
packet-x25.c [new file with mode: 0644]
wiretap/radcom.c [new file with mode: 0644]
wiretap/radcom.h [new file with mode: 0644]

diff --git a/packet-lapb.c b/packet-lapb.c
new file mode 100644 (file)
index 0000000..026c8e9
--- /dev/null
@@ -0,0 +1,453 @@
+/* packet-lapb.c
+ * Routines for lapb frame disassembly
+ * Olivier Abad <abad@daba.dhis.org>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * 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 <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <glib.h>
+#include <string.h>
+#include "packet.h"
+
+#define LAPB_I          0x00    /* Information frames */
+#define LAPB_S          0x01    /* Supervisory frames */
+#define LAPB_U          0x03    /* Unnumbered frames */
+
+#define LAPB_RR         0x01    /* Receiver ready */
+#define LAPB_RNR        0x05    /* Receiver not ready */
+#define LAPB_REJ        0x09    /* Reject */
+#define LAPB_SABM       0x2F    /* Set Asynchronous Balanced Mode */
+#define LAPB_SABME      0x6F    /* Set Asynchronous Balanced Mode Extended */
+#define LAPB_DISC       0x43    /* Disconnect */
+#define LAPB_DM         0x0F    /* Disconnected mode */
+#define LAPB_UA         0x63    /* Unnumbered acknowledge */
+#define LAPB_FRMR       0x87    /* Frame reject */
+
+#define FROM_DCE       0x80
+
+int proto_lapb = -1;
+int hf_lapb_address = -1;
+int hf_lapb_control = -1;
+
+void
+dissect_lapb(const u_char *pd, frame_data *fd, proto_tree *tree)
+{
+    proto_tree *lapb_tree, *ti;
+    char lapb_addr[3];
+    char info[80];
+
+    if (check_col(fd, COL_PROTOCOL))
+       col_add_str(fd, COL_PROTOCOL, "LAPB");
+
+    sprintf(lapb_addr, "%2d", (int)pd[0]);
+    if(check_col(fd, COL_RES_DL_SRC))
+       col_add_str(fd, COL_RES_DL_SRC, lapb_addr);
+
+    switch (pd[1] & 0x0F) {
+    case LAPB_RR:
+       if(check_col(fd, COL_INFO)) {
+           sprintf(info, "RR N(R):%d", (pd[1] >> 5) & 0x7);
+           if ((pd[1] >> 4) && 0x01) { /* P/F bit */
+               if (((fd->flags & FROM_DCE) && pd[0] == 0x01) ||
+                   (!(fd->flags & FROM_DCE) && pd[0] == 0x03))
+                   strcat(info, " F");
+               else
+                   strcat(info, " P");
+           }
+           col_add_str(fd, COL_INFO, info);
+       }
+       if (fd->flags & FROM_DCE) {
+           if(check_col(fd, COL_RES_DL_DST))
+               col_add_str(fd, COL_RES_DL_DST, "DTE");
+           if(check_col(fd, COL_RES_DL_SRC))
+               col_add_str(fd, COL_RES_DL_SRC, "DCE");
+       }
+       else {
+           if(check_col(fd, COL_RES_DL_DST))
+               col_add_str(fd, COL_RES_DL_DST, "DCE");
+           if(check_col(fd, COL_RES_DL_SRC))
+               col_add_str(fd, COL_RES_DL_SRC, "DTE");
+       }
+       if (tree)
+       {
+           ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
+                                           "LAPB");
+           lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
+           proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1, pd[0],
+                                      "Address : 0x%02X", (int)pd[0]);
+           proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1, "RR",
+                                      "Control field : 0x%02X", (int)pd[1]);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     %d%d%d..... : N(R) = %d",
+                               (pd[1] >> 7) & 0x1,
+                               (pd[1] >> 6) & 0x1,
+                               (pd[1] >> 5) & 0x1,
+                               (pd[1] >> 5) & 0x7);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     ...%d.... : Poll/Final bit",
+                               (pd[1] >> 4) & 0x1);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     ....0001 : Receive Ready (RR)");
+       }
+       return;
+    case LAPB_RNR:
+       if(check_col(fd, COL_INFO)) {
+           sprintf(info, "RNR N(R):%d", (pd[1] >> 5) & 0x7);
+           if ((pd[1] >> 4) && 0x01) { /* P/F bit */
+               if (((fd->flags & FROM_DCE) && pd[0] == 0x01) ||
+                   (!(fd->flags & FROM_DCE) && pd[0] == 0x03))
+                   strcat(info, " F");
+               else
+                   strcat(info, " P");
+           }
+           col_add_str(fd, COL_INFO, info);
+       }
+       if (fd->flags & FROM_DCE) {
+           if(check_col(fd, COL_RES_DL_DST))
+               col_add_str(fd, COL_RES_DL_DST, "DTE");
+           if(check_col(fd, COL_RES_DL_SRC))
+               col_add_str(fd, COL_RES_DL_SRC, "DCE");
+       }
+       else {
+           if(check_col(fd, COL_RES_DL_DST))
+               col_add_str(fd, COL_RES_DL_DST, "DCE");
+           if(check_col(fd, COL_RES_DL_SRC))
+               col_add_str(fd, COL_RES_DL_SRC, "DTE");
+       }
+       if (tree)
+       {
+           ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
+                                           "LAPB");
+           lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
+           proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1, pd[0],
+                                      "Address : 0x%02X", (int)pd[0]);
+           proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1, "RNR",
+                                      "Control field : 0x%02X", (int)pd[1]);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     %d%d%d..... : N(R) = %d",
+                               (pd[1] >> 7) & 0x1,
+                               (pd[1] >> 6) & 0x1,
+                               (pd[1] >> 5) & 0x1,
+                               (pd[1] >> 5) & 0x7);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     ...%d.... : Poll/Final bit",
+                               (pd[1] >> 4) & 0x1);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     ....0101 : Receive Not Ready (RNR)");
+       }
+       return;
+    case LAPB_REJ:
+       if(check_col(fd, COL_INFO)) {
+           sprintf(info, "REJ N(R):%d", (pd[1] >> 5) & 0x7);
+           if ((pd[1] >> 4) && 0x01) { /* P/F bit */
+               if (((fd->flags & FROM_DCE) && pd[0] == 0x01) ||
+                   (!(fd->flags & FROM_DCE) && pd[0] == 0x03))
+                   strcat(info, " F");
+               else
+                   strcat(info, " P");
+           }
+           col_add_str(fd, COL_INFO, info);
+       }
+       if (fd->flags & FROM_DCE) {
+           if(check_col(fd, COL_RES_DL_DST))
+               col_add_str(fd, COL_RES_DL_DST, "DTE");
+           if(check_col(fd, COL_RES_DL_SRC))
+               col_add_str(fd, COL_RES_DL_SRC, "DCE");
+       }
+       else {
+           if(check_col(fd, COL_RES_DL_DST))
+               col_add_str(fd, COL_RES_DL_DST, "DCE");
+           if(check_col(fd, COL_RES_DL_SRC))
+               col_add_str(fd, COL_RES_DL_SRC, "DTE");
+       }
+       if (tree)
+       {
+           ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
+                                           "LAPB");
+           lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
+           proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1, pd[0],
+                                      "Address : 0x%02X", (int)pd[0]);
+           proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1, "REJ",
+                                      "Control field : 0x%02X", (int)pd[1]);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     %d%d%d..... : N(R) = %d",
+                               (pd[1] >> 7) & 0x1,
+                               (pd[1] >> 6) & 0x1,
+                               (pd[1] >> 5) & 0x1,
+                               (pd[1] >> 5) & 0x7);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     ...%d.... : Poll/Final bit",
+                               (pd[1] >> 4) & 0x1);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     ....1001 : Reeject (REJ)");
+       }
+       return;
+    }
+
+    /* not a RR/RNR/REJ frame */
+
+    if (pd[1] & 0x01) { /* not an information frame */
+       switch (pd[1] & 0xEF) { /* don't check Poll/Final bit */
+       case LAPB_SABM:
+           if (fd->flags & FROM_DCE) {
+               if(check_col(fd, COL_RES_DL_DST))
+                   col_add_str(fd, COL_RES_DL_DST, "DTE");
+               if(check_col(fd, COL_RES_DL_SRC))
+                   col_add_str(fd, COL_RES_DL_SRC, "DCE");
+           }
+           else {
+               if(check_col(fd, COL_RES_DL_DST))
+                   col_add_str(fd, COL_RES_DL_DST, "DCE");
+               if(check_col(fd, COL_RES_DL_SRC))
+                   col_add_str(fd, COL_RES_DL_SRC, "DTE");
+           }
+           if(check_col(fd, COL_INFO)) {
+               if (pd[1] & 0x10)
+                   col_add_str(fd, COL_INFO, "SABM P");
+               else
+                   col_add_str(fd, COL_INFO, "SABM");
+           }
+           if (tree) {
+               ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
+                                               "LAPB");
+               lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
+               proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
+                                          pd[0], "Address: 0x%02X",
+                                          (int)pd[0]);
+               proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
+                                          "SABM",
+                                          "Set Asynchronous Balanced Mode (SABM)");
+               proto_tree_add_text(lapb_tree, 1, 1,
+                                   "...%d.... : Poll bit",
+                                   (pd[1] >> 4) & 0x1);
+           }
+           break;
+       case LAPB_DISC:
+           if (fd->flags & FROM_DCE) {
+               if(check_col(fd, COL_RES_DL_DST))
+                   col_add_str(fd, COL_RES_DL_DST, "DTE");
+               if(check_col(fd, COL_RES_DL_SRC))
+                   col_add_str(fd, COL_RES_DL_SRC, "DCE");
+           }
+           else {
+               if(check_col(fd, COL_RES_DL_DST))
+                   col_add_str(fd, COL_RES_DL_DST, "DCE");
+               if(check_col(fd, COL_RES_DL_SRC))
+                   col_add_str(fd, COL_RES_DL_SRC, "DTE");
+           }
+           if(check_col(fd, COL_INFO)) {
+               if (pd[1] & 0x10)
+                   col_add_str(fd, COL_INFO, "DISC P");
+               else
+                   col_add_str(fd, COL_INFO, "DISC");
+           }
+           if (tree) {
+               ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
+                                               "LAPB");
+               lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
+               proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
+                                          pd[0], "Address: 0x%02X",
+                                          (int)pd[0]);
+               proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
+                                          "DISC", "Disconnect (DISC)");
+               proto_tree_add_text(lapb_tree, 1, 1,
+                                   "...%d.... : Poll bit",
+                                   (pd[1] >> 4) & 0x1);
+           }
+           break;
+       case LAPB_DM:
+           if (fd->flags & FROM_DCE) {
+               if(check_col(fd, COL_RES_DL_DST))
+                   col_add_str(fd, COL_RES_DL_DST, "DTE");
+               if(check_col(fd, COL_RES_DL_SRC))
+                   col_add_str(fd, COL_RES_DL_SRC, "DCE");
+           }
+           else {
+               if(check_col(fd, COL_RES_DL_DST))
+                   col_add_str(fd, COL_RES_DL_DST, "DCE");
+               if(check_col(fd, COL_RES_DL_SRC))
+                   col_add_str(fd, COL_RES_DL_SRC, "DTE");
+           }
+           if(check_col(fd, COL_INFO)) {
+               if (pd[1] & 0x10)
+                   col_add_str(fd, COL_INFO, "DM F");
+               else
+                   col_add_str(fd, COL_INFO, "DM");
+           }
+           if (tree) {
+               ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
+                                               "LAPB");
+               lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
+               proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
+                                          pd[0], "Address: 0x%02X",
+                                          (int)pd[0]);
+               proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
+                                          "DM", "Disconnect Mode (DM)");
+               proto_tree_add_text(lapb_tree, 1, 1,
+                                   "...%d.... : Final bit",
+                                   (pd[1] >> 4) & 0x1);
+           }
+           break;
+       case LAPB_UA:
+           if (fd->flags & FROM_DCE) {
+               if(check_col(fd, COL_RES_DL_DST))
+                   col_add_str(fd, COL_RES_DL_DST, "DTE");
+               if(check_col(fd, COL_RES_DL_SRC))
+                   col_add_str(fd, COL_RES_DL_SRC, "DCE");
+           }
+           else {
+               if(check_col(fd, COL_RES_DL_DST))
+                   col_add_str(fd, COL_RES_DL_DST, "DCE");
+               if(check_col(fd, COL_RES_DL_SRC))
+                   col_add_str(fd, COL_RES_DL_SRC, "DTE");
+           }
+           if(check_col(fd, COL_INFO)) {
+               if (pd[1] & 0x10)
+                   col_add_str(fd, COL_INFO, "UA F");
+               else
+                   col_add_str(fd, COL_INFO, "UA");
+           }
+           if (tree) {
+               ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
+                                               "LAPB");
+               lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
+               proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
+                                          pd[0], "Address: 0x%02X",
+                                          (int)pd[0]);
+               proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
+                                          "UA", "Unnumbered Acknowledge (UA)");
+               proto_tree_add_text(lapb_tree, 1, 1,
+                                   "...%d.... : Final bit",
+                                   (pd[1] >> 4) & 0x1);
+           }
+           break;
+       case LAPB_FRMR:
+           if (fd->flags & FROM_DCE) {
+               if(check_col(fd, COL_RES_DL_DST))
+                   col_add_str(fd, COL_RES_DL_DST, "DTE");
+               if(check_col(fd, COL_RES_DL_SRC))
+                   col_add_str(fd, COL_RES_DL_SRC, "DCE");
+           }
+           else {
+               if(check_col(fd, COL_RES_DL_DST))
+                   col_add_str(fd, COL_RES_DL_DST, "DCE");
+               if(check_col(fd, COL_RES_DL_SRC))
+                   col_add_str(fd, COL_RES_DL_SRC, "DTE");
+           }
+           if(check_col(fd, COL_INFO)) {
+               if (pd[1] & 0x10)
+                   col_add_str(fd, COL_INFO, "FRMR F");
+               else
+                   col_add_str(fd, COL_INFO, "FRMR");
+           }
+           if (tree) {
+               ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
+                                               "LAPB");
+               lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
+               proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
+                                          pd[0], "Address: 0x%02X",
+                                          (int)pd[0]);
+               proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
+                                          "FRMR", "Frame Reject (FRMR)");
+               proto_tree_add_text(lapb_tree, 1, 1,
+                                   "...%d.... : Final bit",
+                                   (pd[1] >> 4) & 0x1);
+           }
+           break;
+       }
+    }
+    else /* information frame */
+    {
+       if (fd->flags & FROM_DCE) {
+           if(check_col(fd, COL_RES_DL_DST))
+               col_add_str(fd, COL_RES_DL_DST, "DTE");
+           if(check_col(fd, COL_RES_DL_SRC))
+               col_add_str(fd, COL_RES_DL_SRC, "DCE");
+       }
+       else {
+           if(check_col(fd, COL_RES_DL_DST))
+               col_add_str(fd, COL_RES_DL_DST, "DCE");
+           if(check_col(fd, COL_RES_DL_SRC))
+               col_add_str(fd, COL_RES_DL_SRC, "DTE");
+       }
+       if(check_col(fd, COL_INFO)) {
+           sprintf(info, "I N(R):%d N(S):%d",
+                   (pd[1] >> 5) & 0x7,
+                   (pd[1] >> 1) & 0x7);
+           if ((pd[1] >> 4) && 0x01) /* P/F bit */
+               strcat(info, " P");
+           col_add_str(fd, COL_INFO, info);
+       }
+       if (tree) {
+           ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
+                                           "LAPB");
+           lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
+           proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
+                                      pd[0], "Address: 0x%02X",
+                                      (int)pd[0]);
+           proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
+                                      "I", "Control field : 0x%02X",
+                                      (int)pd[1]);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     %d%d%d..... : N(R) = %d",
+                               (pd[1] >> 7) & 0x1,
+                               (pd[1] >> 6) & 0x1,
+                               (pd[1] >> 5) & 0x1,
+                               (pd[1] >> 5) & 0x7);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     ...%d.... : Poll/Final bit",
+                               (pd[1] >> 4) & 0x1);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     ....%d%d%d. : N(S) = %d",
+                               (pd[1] >> 3) & 0x1,
+                               (pd[1] >> 2) & 0x1,
+                               (pd[1] >> 1) & 0x1,
+                               (pd[1] >> 1) & 0x7);
+           proto_tree_add_text(lapb_tree, 1, 1,
+                               "     .......0 : Information Transfer (I)");
+       }
+    }
+
+    /* not end of frame ==> X.25 */
+    if (fd->cap_len > 2) dissect_x25(pd, 2, fd, tree);
+}
+
+void
+proto_register_lapb(void)
+{
+    static hf_register_info hf[] = {
+       { &hf_lapb_address,
+         { "Address Field", "lapb.address", FT_UINT8, NULL} },
+       { &hf_lapb_control,
+         { "Control Field", "lapb.control", FT_STRING, NULL} },
+    };
+
+    proto_lapb = proto_register_protocol ("LAPB", "lapb");
+    proto_register_field_array (proto_lapb, hf, array_length(hf));
+}
diff --git a/packet-x25.c b/packet-x25.c
new file mode 100644 (file)
index 0000000..befaf18
--- /dev/null
@@ -0,0 +1,995 @@
+/* packet-x25.c
+ * Routines for x25 packet disassembly
+ * Olivier Abad <abad@daba.dhis.org>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * 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 <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+#include "packet.h"
+
+#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_THROUGHPUT_MIN         0x0A
+#define X25_FAC_EXPRESS_DATA           0x0B
+#define X25_FAC_PACKET_SIZE            0x42
+#define X25_FAC_WINDOW_SIZE            0x43
+#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_ADDR_EXT               0xCB
+
+int proto_x25 = -1;
+int hf_x25_lcn = -1;
+int hf_x25_type = -1;
+
+/*
+ * 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;
+       void (*dissect)(const u_char *, int, frame_data *, proto_tree *);
+       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;
+
+global_vc_info *hash_table[64];
+
+void
+free_vc_info(vc_info *pt)
+{
+  vc_info *vci = pt;
+
+  while (pt) {
+    vci = pt;
+    pt = pt->next;
+    g_free(vci);
+  }
+}
+
+void
+init_dissect_x25()
+{
+  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;
+    }
+  }
+}
+
+void
+x25_hash_add_proto_start(guint16 vc, guint32 frame_secs, guint32 frame_usecs,
+                        void (*dissect)(const u_char *, int, frame_data *,
+                                      proto_tree *))
+{
+  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;
+    }
+  }
+}
+
+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;
+}
+
+void (*x25_hash_get_dissect(guint32 frame_secs, guint32 frame_usecs, guint16 vc))(const u_char *, int, frame_data *, proto_tree *)
+{
+  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 *reset_code(unsigned char code)
+{
+    static char buffer[25];
+
+    if (code == 0x00 || (code & 0x80) == 0x80)
+       return "DTE Originated";
+    if (code == 0x03)
+       return "Remote Procedure Error";
+    if (code == 0x11)
+       return "Incompatible Destination";
+    if (code == 0x05)
+       return "Local Procedure Error";
+    if (code == 0x07)
+       return "Network Congestion";
+
+    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";
+
+    sprintf(buffer, "Unknown %02X", code);
+
+    return buffer;
+}
+
+void
+dump_facilities(proto_tree *tree, int *offset, const guint8 *p)
+{
+    const guint8 *ptr = p;
+    guint32 len;      /* facilities length */
+
+    len = *ptr++;
+    if (len && tree)
+       proto_tree_add_text(tree, *offset, 1,
+                           "Facilities length: %d", len);
+    (*offset)++;
+
+    while (len > 0) {
+       switch(*ptr & X25_FAC_CLASS_MASK) {
+       case X25_FAC_CLASS_A:
+           switch (*ptr) {
+           case X25_FAC_COMP_MARK:
+               switch (ptr[1]) {
+               case 0x00:
+                   if (tree)
+                       proto_tree_add_text(tree, *offset, 2,
+                                           "Network complementary services - calling DTE");
+                   break;
+               case 0xFF:
+                   if (tree)
+                       proto_tree_add_text(tree, *offset, 2,
+                                           "Network complementary services - called DTE");
+                   break;
+               case 0x0F:
+                   if (tree)
+                       proto_tree_add_text(tree, *offset, 2,
+                                           "DTE complementary services");
+                   break;
+               default:
+                   if (tree)
+                       proto_tree_add_text(tree, *offset, 2,
+                                           "Unknown marker");
+                   break;
+               }
+               break;
+           case X25_FAC_REVERSE:
+               if (tree) {
+                   if (ptr[1] & 0x01)
+                       proto_tree_add_text(tree, *offset, 2,
+                                           "Reverse Charging");
+                   else
+                       proto_tree_add_text(tree, *offset, 2,
+                                           "No Reverse Charging");
+                   if (ptr[1] & 0xC0)
+                       proto_tree_add_text(tree, *offset, 2,
+                                           "Fast select with restriction");
+                   else if (ptr[1] & 0x80)
+                       proto_tree_add_text(tree, *offset, 2,
+                                           "Fast select - no restriction");
+                   else
+                       proto_tree_add_text(tree, *offset, 2,
+                                           "No Fast select");
+               }
+               break;
+           case X25_FAC_THROUGHPUT:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2,
+                                       "Throughput: %02X", ptr[1]);
+               break;
+           case X25_FAC_CUG:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2,
+                                       "CUG: %02X",
+                                       ptr[1]);
+               break;
+           case X25_FAC_CALLED_MODIF:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2,
+                                       "Called address modified: %02X",
+                                       ptr[1]);
+               break;
+           case X25_FAC_THROUGHPUT_MIN:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2,
+                                       "Minimum throughput class");
+               break;
+           case X25_FAC_EXPRESS_DATA:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2,
+                                       "Negociation of express data");
+               break;
+           default:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2,
+                                       "Unknown facility %02X, value %02X",
+                                       ptr[0], ptr[1]);
+               break;
+           }
+           (*offset) += 2;
+           len -= 2;
+           ptr += 2;
+           break;
+       case X25_FAC_CLASS_B:
+           switch (*ptr) {
+           case X25_FAC_PACKET_SIZE:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 3,
+                                       "Packet Size: %02X %02X", ptr[1], ptr[2]);
+               break;
+           case X25_FAC_WINDOW_SIZE:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 3,
+                                       "Window Size: %2d %2d", ptr[1],
+                                       ptr[2]);
+               break;
+           case X25_FAC_TRANSIT_DELAY:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 3,
+                                       "Transit delay: %d",
+                                       (ptr[1]<<8) + ptr[2]);
+               break;
+           default:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 3,
+                                       "Unknown facility %02X, values %02X%02X",
+                                       ptr[0], ptr[1], ptr[2]);
+               break;
+           }
+           (*offset) += 3;
+           len -= 3;
+           ptr += 3;
+           break;
+       case X25_FAC_CLASS_C:
+           if (tree)
+               proto_tree_add_text(tree, *offset, 4,
+                                   "Unknown facility %02X, values %02X%02X%02X",
+                                   ptr[0], ptr[1], ptr[2], ptr[3]);
+           (*offset) += 4;
+           len -= 4;
+           ptr += 4;
+           break;
+       case X25_FAC_CLASS_D:
+           switch (*ptr) {
+           case X25_FAC_CALL_TRANSFER:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2+ptr[1],
+                                       "Call Transfer: reason = %02X",
+                                       ptr[2]);
+               break;
+           case X25_FAC_ADDR_EXT:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2+ptr[1],
+                                       "Address extension");
+               break;
+           case X25_FAC_CALLED_ADDR_EXT:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2+ptr[1],
+                                       "Called address extension");
+               break;
+           case X25_FAC_ETE_TRANSIT_DELAY:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2+ptr[1],
+                                       "End to end transit delay");
+               break;
+           default:
+               if (tree)
+                   proto_tree_add_text(tree, *offset, 2+ptr[1],
+                                       "Unknown facility %02X, length %02X",
+                                       ptr[0], ptr[1]);
+           }
+           (*offset) += ptr[1]+2;
+           len -= ptr[1]+2;
+           ptr += ptr[1]+2;
+           break;
+       }
+    }
+}
+
+void
+x25_ntoa(proto_tree *tree, int *offset, const guint8 *p, frame_data *fd)
+{
+    int called_len, calling_len;
+    int i;
+    char called_addr[16], calling_addr[16];
+    char *called, *calling;
+
+    called_len  = (*p >> 0) & 0x0F;
+    calling_len = (*p >> 4) & 0x0F;
+    if (tree) {
+       proto_tree_add_text(tree, *offset, 1,
+                           "Calling address length : %d", calling_len);
+       proto_tree_add_text(tree, *offset, 1,
+                           "Called address length : %d", called_len);
+    }
+    (*offset)++;
+
+    p++;
+
+    called=called_addr;
+    calling=calling_addr;
+    for (i = 0; i < (called_len + calling_len); i++) {
+       if (i < called_len) {
+           if (i % 2 != 0) {
+               *called++ = ((*p >> 0) & 0x0F) + '0';
+               p++;
+           } else {
+               *called++ = ((*p >> 4) & 0x0F) + '0';
+           }
+       } else {
+           if (i % 2 != 0) {
+               *calling++ = ((*p >> 0) & 0x0F) + '0';
+               p++;
+           } else {
+               *calling++ = ((*p >> 4) & 0x0F) + '0';
+           }
+       }
+    }
+
+    *called  = '\0';
+    *calling = '\0';
+
+    if (called_len) {
+       if(check_col(fd, COL_RES_DL_DST))
+           col_add_str(fd, COL_RES_DL_DST, called_addr);
+       if (tree)
+           proto_tree_add_text(tree, *offset,
+                               (called_len + 1) / 2,
+                               "Called address : %s", called_addr);
+    }
+    if (calling_len) {
+       if(check_col(fd, COL_RES_DL_SRC))
+           col_add_str(fd, COL_RES_DL_SRC, calling_addr);
+       if (tree)
+           proto_tree_add_text(tree, *offset + called_len/2,
+                               (calling_len+1)/2+(called_len%2+(calling_len+1)%2)/2,
+                               "Calling address : %s", calling_addr);
+    }
+    (*offset) += ((called_len + calling_len + 1) / 2);
+}
+
+int
+get_x25_pkt_len(const char *data)
+{
+    int length, called_len, calling_len;
+
+    switch ((guint8)data[2])
+    {
+    case X25_CALL_REQUEST:
+       called_len  = (data[3] >> 0) & 0x0F;
+       calling_len = (data[3] >> 4) & 0x0F;
+       length = 4 + (called_len + calling_len + 1) / 2; /* addr */
+       length += (1 + data[length]); /* facilities */
+       return length;
+
+    case X25_CALL_ACCEPTED:
+       called_len  = (data[3] >> 0) & 0x0F;
+       calling_len = (data[3] >> 4) & 0x0F;
+       length = 4 + (called_len + calling_len + 1) / 2; /* addr */
+       length += (1 + data[length]); /* facilities */
+       return length;
+
+    case X25_CLEAR_REQUEST:
+       return 5;
+
+    case X25_CLEAR_CONFIRMATION:
+       return 3;
+
+    case X25_DIAGNOSTIC:
+       return 4;
+
+    case X25_INTERRUPT:
+       return 3;
+
+    case X25_INTERRUPT_CONFIRMATION:
+       return 3;
+
+    case X25_RESET_REQUEST:
+       return 5;
+
+    case X25_RESET_CONFIRMATION:
+       return 3;
+               
+    case X25_RESTART_REQUEST:
+       return 5;
+               
+    case X25_RESTART_CONFIRMATION:
+       return 3;
+
+    case X25_REGISTRATION_REQUEST:
+       return 3;
+               
+    case X25_REGISTRATION_CONFIRMATION:
+       return 3;
+    }
+           
+    if ((data[2] & 0x01) == X25_DATA) return 3;
+
+    switch (data[2])
+    {
+    case X25_RR:
+       return 3;
+
+    case X25_RNR:
+       return 3;
+
+    case X25_REJ:
+       return 3;
+    }
+
+    return 0;
+}
+
+void
+dissect_x25(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+{
+    proto_tree *x25_tree=0, *ti;
+    int localoffset=offset;
+    int x25_pkt_len;
+    int modulo;
+    guint16 vc;
+    void (*dissect)(const u_char *, int, frame_data *, proto_tree *);
+
+    if (check_col(fd, COL_PROTOCOL))
+       col_add_str(fd, COL_PROTOCOL, "X.25");
+
+    modulo = ((pd[localoffset] & 0x20) ? 128 : 8);
+    vc = (int)(pd[localoffset] & 0x0F) + (int)pd[localoffset+1];
+    x25_pkt_len = get_x25_pkt_len(&pd[localoffset]);
+    if (tree) {
+       ti = proto_tree_add_item(tree, proto_x25, localoffset, x25_pkt_len,
+                                NULL);
+       x25_tree = proto_item_add_subtree(ti, ETT_X25);
+       proto_tree_add_text(x25_tree, localoffset, 1,
+                           "GFI : Q: %d, D: %d, Mod: %d",
+                           (pd[localoffset] & 0x80) ? 1 : 0,
+                           (pd[localoffset] & 0x40) ? 1 : 0,
+                           modulo);
+       proto_tree_add_item_format(x25_tree, hf_x25_lcn, localoffset, 2,
+                                  (int)(pd[localoffset] & 0x0F) +
+                                  (int)pd[localoffset+1],
+                                  "Logical channel : %3.3X",
+                                  vc);
+    }
+    switch (pd[localoffset+2]) {
+    case X25_CALL_REQUEST:
+       if(check_col(fd, COL_INFO))
+           col_add_fstr(fd, COL_INFO, "Call Req. VC:%d", vc);
+       if (x25_tree)
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "CALL REQ", "CALL REQUEST");
+       localoffset += 3;
+       x25_ntoa(x25_tree, &localoffset, &pd[localoffset], fd);
+
+       if (localoffset < x25_pkt_len+2) /* facilities */
+           dump_facilities(x25_tree, &localoffset, &pd[localoffset]);
+
+       if (localoffset < fd->cap_len) /* user data */
+       {
+           if (pd[localoffset] == 0xCC)
+           {
+               x25_hash_add_proto_start(vc, fd->abs_secs,
+                                        fd->abs_usecs, dissect_ip);
+               if (x25_tree)
+                   proto_tree_add_text(x25_tree, localoffset, 1,
+                                       "pid = IP");
+               localoffset++;
+           }
+           else if (pd[localoffset] == 0x03 &&
+                    pd[localoffset+1] == 0x01 &&
+                    pd[localoffset+2] == 0x01 &&
+                    pd[localoffset+3] == 0x00)
+           {
+               x25_hash_add_proto_start(vc, fd->abs_secs,
+                                        fd->abs_usecs, dissect_cotp);
+               if (x25_tree)
+                   proto_tree_add_text(x25_tree, localoffset, 4,
+                                       "pid = COTP");
+               localoffset += 4;
+           }
+           else {
+               if (x25_tree)
+                   proto_tree_add_text(x25_tree, localoffset,
+                                       fd->cap_len-localoffset, "Data");
+               localoffset = fd->cap_len;
+           }
+       }
+       break;
+    case X25_CALL_ACCEPTED:
+       if(check_col(fd, COL_INFO))
+           col_add_fstr(fd, COL_INFO, "Call Acc. VC:%d", vc);
+       if (x25_tree)
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "CALL ACC", "CALL ACCEPTED");
+       localoffset += 3;
+       x25_ntoa(x25_tree, &localoffset, &pd[localoffset], fd);
+
+       if (localoffset < x25_pkt_len+2) /* facilities */
+           dump_facilities(x25_tree, &localoffset, &pd[localoffset]);
+
+       if (localoffset < fd->cap_len) { /* user data */
+           if (x25_tree)
+               proto_tree_add_text(x25_tree, localoffset,
+                                   fd->cap_len-localoffset, "Data");
+           localoffset=fd->cap_len;
+       }
+       break;
+    case X25_CLEAR_REQUEST:
+       if(check_col(fd, COL_INFO)) {
+           col_add_fstr(fd, COL_INFO, "Clear Req. VC:%d %s - Diag.:%d",
+                   vc, clear_code(pd[localoffset+3]),
+                   (int)pd[localoffset+4]);
+       }
+       x25_hash_add_proto_end(vc, fd->abs_secs, fd->abs_usecs);
+       if (x25_tree) {
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "CLEAR REQ", "CLEAR REQUEST");
+           proto_tree_add_text(x25_tree, localoffset+3, 1,
+                               "Cause : %s", clear_code(pd[localoffset+3]));
+           proto_tree_add_text(x25_tree, localoffset+4, 1,
+                               "Diagnostic : %d", (int)pd[localoffset+4]);
+       }
+       localoffset += x25_pkt_len;
+       break;
+    case X25_CLEAR_CONFIRMATION:
+       if(check_col(fd, COL_INFO))
+           col_add_fstr(fd, COL_INFO, "Clear Conf. VC:%d", vc);
+       if (x25_tree)
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "CLEAR CONF", "CLEAR CONFIRMATION");
+       localoffset += x25_pkt_len;
+       break;
+    case X25_DIAGNOSTIC:
+       if(check_col(fd, COL_INFO)) {
+           col_add_fstr(fd, COL_INFO, "Diag. VC:%d %d",
+                   vc, (int)pd[localoffset+3]);
+       }
+       if (x25_tree) {
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "DIAG", "DIAGNOSTIC");
+           proto_tree_add_text(x25_tree, localoffset+3, 1,
+                               "Diagnostic : %d", (int)pd[localoffset+3]);
+       }
+       localoffset += x25_pkt_len;
+       break;
+    case X25_INTERRUPT:
+       if(check_col(fd, COL_INFO))
+           col_add_fstr(fd, COL_INFO, "Interrupt VC:%d", vc);
+       if (x25_tree)
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "INTR", "INTERRUPT");
+       localoffset += x25_pkt_len;
+       break;
+    case X25_INTERRUPT_CONFIRMATION:
+       if(check_col(fd, COL_INFO))
+           col_add_fstr(fd, COL_INFO, "Interrupt Conf. VC:%d", vc);
+       if (x25_tree)
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "INTR CONF", "INTERRUPT CONFIRMATION");
+       localoffset += x25_pkt_len;
+       break;
+    case X25_RESET_REQUEST:
+       if(check_col(fd, COL_INFO)) {
+           col_add_fstr(fd, COL_INFO, "Reset Req. VC:%d %s - Diag.:%d",
+                   vc, reset_code(pd[localoffset+3]),
+                   (int)pd[localoffset+4]);
+       }
+       x25_hash_add_proto_end(vc, fd->abs_secs, fd->abs_usecs);
+       if (x25_tree) {
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "RESET REQ", "RESET REQUEST");
+           proto_tree_add_text(x25_tree, localoffset+3, 1,
+                               "Cause : %s", reset_code(pd[localoffset+3]));
+           proto_tree_add_text(x25_tree, localoffset+4, 1,
+                               "Diagnostic : %d", (int)pd[localoffset+4]);
+       }
+       localoffset += x25_pkt_len;
+       break;
+    case X25_RESET_CONFIRMATION:
+       if(check_col(fd, COL_INFO))
+           col_add_fstr(fd, COL_INFO, "Reset Conf. VC:%d", vc);
+       if (x25_tree)
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "RESET CONF", "RESET CONFIRMATION");
+       localoffset += x25_pkt_len;
+       break;
+    case X25_RESTART_REQUEST:
+       if(check_col(fd, COL_INFO)) {
+           col_add_fstr(fd, COL_INFO, "Restart Req. VC:%d %s - Diag.:%d",
+                   vc, restart_code(pd[localoffset+3]),
+                   (int)pd[localoffset+4]);
+       }
+       x25_hash_add_proto_end(vc, fd->abs_secs, fd->abs_usecs);
+       if (x25_tree) {
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "RESTART REQ", "RESTART REQUEST");
+           proto_tree_add_text(x25_tree, localoffset+3, 1,
+                               "Cause : %s", restart_code(pd[localoffset+3]));
+           proto_tree_add_text(x25_tree, localoffset+4, 1,
+                               "Diagnostic : %d", (int)pd[localoffset+4]);
+       }
+       localoffset += x25_pkt_len;
+       break;
+    case X25_RESTART_CONFIRMATION:
+       if(check_col(fd, COL_INFO))
+           col_add_fstr(fd, COL_INFO, "Restart Conf. VC:%d", vc);
+       if (x25_tree)
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "RESTART CONF", "RESTART CONFIRMATION");
+       localoffset += x25_pkt_len;
+       break;
+    case X25_REGISTRATION_REQUEST:
+       if(check_col(fd, COL_INFO))
+           col_add_fstr(fd, COL_INFO, "Registration Req. VC:%d", vc);
+       if (x25_tree)
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "REG REQ", "REGISTRATION REQUEST");
+       localoffset += x25_pkt_len;
+       break;
+    case X25_REGISTRATION_CONFIRMATION:
+       if(check_col(fd, COL_INFO))
+           col_add_fstr(fd, COL_INFO, "Registration Conf. VC:%d", vc);
+       if (x25_tree)
+           proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset+2, 1,
+                                      "REG CONF", "REGISTRATION CONFIRMATION");
+       localoffset += x25_pkt_len;
+       break;
+    default :
+       localoffset += 2;
+       if ((pd[localoffset] & 0x01) == X25_DATA)
+       {
+           if(check_col(fd, COL_INFO)) {
+               col_add_fstr(fd, COL_INFO, "Data VC:%d P(S):%d P(R):%d %s", vc,
+                            (pd[localoffset] >> 1) & 0x07,
+                            (pd[localoffset] >> 5) & 0x07,
+                            ((pd[localoffset]>>4) & 0x01) ? " M" : "");
+           }
+           if (x25_tree) {
+               proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset,
+                                          1, "DATA",
+                                          "Packet type identifier : 0x%02X",
+                                          pd[localoffset]);
+               proto_tree_add_text(x25_tree, localoffset, 1,
+                                   "              %d%d%d..... : P(R) = %d",
+                                   (pd[localoffset] >> 7) & 0x01,
+                                   (pd[localoffset] >> 6) & 0x01,
+                                   (pd[localoffset] >> 5) & 0x01,
+                                   (pd[localoffset] >> 5) & 0x07);
+               proto_tree_add_text(x25_tree, localoffset, 1,
+                                   "              ...%d.... : More bit",
+                                   (pd[localoffset] >> 4) & 0x01);
+               proto_tree_add_text(x25_tree, localoffset, 1,
+                                   "              ....%d%d%d. : P(S) = %d",
+                                   (pd[localoffset] >> 3) & 0x01,
+                                   (pd[localoffset] >> 2) & 0x01,
+                                   (pd[localoffset] >> 1) & 0x01,
+                                   (pd[localoffset] >> 1) & 0x07);
+               proto_tree_add_text(x25_tree, localoffset, 1,
+                                   "              .......0 : Packet type id = DATA");
+           }
+           localoffset++;
+           break;
+       }
+       switch (pd[localoffset] & 0x1F)
+       {
+       case X25_RR:
+           if(check_col(fd, COL_INFO)) {
+               col_add_fstr(fd, COL_INFO, "RR VC:%d P(R):%d",
+                            vc, (pd[localoffset] >> 5) & 0x07);
+           }
+           if (x25_tree)
+               proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset,
+                                          1, "RR", "RR P(R):%d",
+                                          (pd[localoffset] >> 5) & 0x07);
+           break;
+
+       case X25_RNR:
+           if(check_col(fd, COL_INFO)) {
+               col_add_fstr(fd, COL_INFO, "RNR VC:%d P(R):%d",
+                            vc, (pd[localoffset] >> 5) & 0x07);
+           }
+           if (x25_tree)
+               proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset,
+                                          1, "RNR", "RNR P(R):%d",
+                                          (pd[localoffset] >> 5) & 0x07);
+           break;
+
+       case X25_REJ:
+           if(check_col(fd, COL_INFO)) {
+               col_add_fstr(fd, COL_INFO, "REJ VC:%d P(R):%d",
+                            vc, (pd[localoffset] >> 5) & 0x07);
+           }
+           if (x25_tree)
+               proto_tree_add_item_format(x25_tree, hf_x25_type, localoffset,
+                                          1, "REJ", "REJ P(R):%d",
+                                          (pd[localoffset] >> 5) & 0x07);
+       }
+       localoffset++;
+    }
+
+    if (localoffset >= fd->cap_len) return;
+
+    /* search the dissector in the hash table */
+    if ((dissect = x25_hash_get_dissect(fd->abs_secs, fd->abs_usecs, vc)))
+      (*dissect)(pd, localoffset, fd, tree);
+    else {
+      if (pd[localoffset] == 0x45) /* If the Call Req. has not been captured,
+                                   * assume these packets carry IP */
+      {
+         x25_hash_add_proto_start(vc, fd->abs_secs,
+                                  fd->abs_usecs, dissect_ip);
+         dissect_ip(pd, localoffset, fd, tree);
+      }
+      else {
+         dissect_data(pd, localoffset, fd, tree);
+      }
+    }
+}
+
+void
+proto_register_x25(void)
+{
+    static hf_register_info hf[] = {
+       { &hf_x25_lcn,
+         { "Logical Channel", "x25.lcn", FT_UINT16, NULL} },
+       { &hf_x25_type,
+         { "Packet Type", "x25.type", FT_STRING, NULL} },
+    };
+
+    proto_x25 = proto_register_protocol ("X.25", "x25");
+    proto_register_field_array (proto_x25, hf, array_length(hf));
+}
diff --git a/wiretap/radcom.c b/wiretap/radcom.c
new file mode 100644 (file)
index 0000000..4ec27bf
--- /dev/null
@@ -0,0 +1,238 @@
+/* radcom.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@verdict.uthscsa.edu>
+ * 
+ * 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 <stdlib.h>
+#include <time.h>
+#include "wtap.h"
+#include "buffer.h"
+#include "radcom.h"
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+struct frame_date {
+       guint16 year;
+       guint8  month;
+       guint8  day;
+       guint32 sec;            /* seconds since midnight */
+       guint32 usec;
+};
+
+static char radcom_magic[8] = {
+       0x42, 0xD2, 0x00, 0x34, 0x12, 0x66, 0x22, 0x88
+};
+
+/* Returns WTAP_FILE_RADCOM on success, WTAP_FILE_UNKNOWN on failure */
+int radcom_open(wtap *wth)
+{
+       int bytes_read;
+       char magic[8];
+       struct frame_date start_date;
+       struct tm tm;
+       char byte;
+       char encap_magic[7] = {0x54, 0x43, 0x50, 0x00, 0x42, 0x43, 0x09};
+       char search_encap[7];
+
+       /* Read in the string that should be at the start of a Sniffer file */
+       fseek(wth->fh, 0, SEEK_SET);
+       bytes_read = fread(magic, 1, 8, wth->fh);
+
+       if (bytes_read != 8) {
+               return WTAP_FILE_UNKNOWN;
+       }
+
+       if (memcmp(magic, radcom_magic, 8)) {
+               return WTAP_FILE_UNKNOWN;
+       }
+
+       /* This is a radcom file */
+       wth->capture.radcom = g_malloc(sizeof(radcom_t));
+       wth->subtype_read = radcom_read;
+       wth->snapshot_length = 16384;   /* not available in header, only in frame */
+       fseek(wth->fh, 0x8B, SEEK_SET);
+       bytes_read = fread(&byte, 1, 1, wth->fh);
+       if (bytes_read != 1) {
+               return WTAP_FILE_UNKNOWN;
+       }
+       while (byte) {
+               bytes_read = fread(&byte, 1, 1, wth->fh);
+               if (bytes_read != 1) {
+                       return WTAP_FILE_UNKNOWN;
+               }
+       }
+       fseek(wth->fh, 1, SEEK_CUR);
+
+       /* Get capture start time */
+       bytes_read = fread(&start_date, 1, sizeof(struct frame_date), wth->fh);
+
+       if (bytes_read != sizeof(struct frame_date)) {
+               return WTAP_FILE_UNKNOWN;
+       }
+
+       tm.tm_year = start_date.year-1900;
+       tm.tm_mon = start_date.month-1;
+       tm.tm_mday = start_date.day;
+       tm.tm_hour = start_date.sec/3600;
+       tm.tm_min = (start_date.sec%3600)/60;
+       tm.tm_sec = start_date.sec%60;
+       tm.tm_isdst = -1;
+       wth->capture.radcom->start = mktime(&tm);
+
+       fseek(wth->fh, sizeof(struct frame_date), SEEK_CUR);
+
+       bytes_read = fread(search_encap, 1, 7, wth->fh);
+       if (bytes_read != 7) {
+               return WTAP_FILE_UNKNOWN;
+       }
+       while (memcmp(encap_magic, search_encap, 7)) {
+               fseek(wth->fh, -6, SEEK_CUR);
+               bytes_read = fread(search_encap, 1, 7, wth->fh);
+               if (bytes_read != 7) {
+                       return WTAP_FILE_UNKNOWN;
+               }
+       }
+       fseek(wth->fh, 12, SEEK_CUR);
+       bytes_read = fread(search_encap, 1, 4, wth->fh);
+       if (bytes_read != 4) {
+               return WTAP_FILE_UNKNOWN;
+       }
+       if (!memcmp(search_encap, "LAPB", 4))
+               wth->file_encap = WTAP_ENCAP_LAPB;
+       else if (!memcmp(search_encap, "Ethe", 4))
+               wth->file_encap = WTAP_ENCAP_ETHERNET;
+       else
+               return WTAP_FILE_UNKNOWN;
+
+       /*bytes_read = fread(&next_date, 1, sizeof(struct frame_date), wth->fh);
+       if (bytes_read != sizeof(struct frame_date)) {
+               return WTAP_FILE_UNKNOWN;
+       }
+
+       while (memcmp(&start_date, &next_date, 4)) {
+               fseek(wth->fh, 1-sizeof(struct frame_date), SEEK_CUR);
+               bytes_read = fread(&next_date, 1, sizeof(struct frame_date),
+                                  wth->fh);
+               if (bytes_read != sizeof(struct frame_date)) {
+                       return WTAP_FILE_UNKNOWN;
+               }
+       }*/
+
+       if (wth->file_encap == WTAP_ENCAP_ETHERNET)
+               fseek(wth->fh, 294, SEEK_CUR);
+       else if (wth->file_encap == WTAP_ENCAP_LAPB)
+               fseek(wth->fh, 297, SEEK_CUR);
+
+       return WTAP_FILE_RADCOM;
+}
+
+/* Read the next packet */
+int radcom_read(wtap *wth)
+{
+       int     bytes_read;
+       guint16 length;
+       struct frame_date date;
+       int     data_offset;
+       struct tm tm;
+       char dce;
+
+       fseek(wth->fh, 4, SEEK_CUR);
+
+       /*
+        * Read the frame size
+        */
+       bytes_read = fread(&length, 1, 2, wth->fh);
+       if (bytes_read != 2) {
+               /*
+                * End of file or error.
+                */
+               g_message("radcom_read: not enough frame data (%d bytes)",
+                       bytes_read);
+               return 0;
+       }
+
+       if (wth->file_encap == WTAP_ENCAP_LAPB) length -= 2; /* FCS */
+
+       wth->phdr.len = length;
+       wth->phdr.caplen = length;
+
+       fseek(wth->fh, 5, SEEK_CUR);
+       bytes_read = fread(&date, 1, sizeof(struct frame_date), wth->fh);
+       if (bytes_read != sizeof(struct frame_date)) {
+               /*
+                * End of file or error.
+                */
+               g_message("radcom_read: not enough frame data (%d bytes)",
+                       bytes_read);
+               return 0;
+       }
+
+       tm.tm_year = date.year-1900;
+       tm.tm_mon = date.month-1;
+       tm.tm_mday = date.day;
+       tm.tm_hour = date.sec/3600;
+       tm.tm_min = (date.sec%3600)/60;
+       tm.tm_sec = date.sec%60;
+       tm.tm_isdst = -1;
+       wth->phdr.ts.tv_sec = mktime(&tm);
+       wth->phdr.ts.tv_usec = date.usec;
+
+       fseek(wth->fh, 6, SEEK_CUR);
+       bytes_read = fread(&dce, 1, 1, wth->fh);
+       if (bytes_read != 1) {
+               /*
+                * End of file or error.
+                */
+               g_message("radcom_read: not enough frame data (%d bytes)",
+                       bytes_read);
+               return 0;
+       }
+       wth->phdr.flags = (dce & 0x1) ? 0x00 : 0x80;
+
+       fseek(wth->fh, 9, SEEK_CUR);
+
+       /*
+        * Read the packet data.
+        */
+       buffer_assure_space(wth->frame_buffer, length);
+       data_offset = ftell(wth->fh);
+       bytes_read = fread(buffer_start_ptr(wth->frame_buffer), 1,
+                       length, wth->fh);
+
+       if (bytes_read != length) {
+               if (ferror(wth->fh)) {
+                       g_message("radcom_read: fread for data: read error\n");
+               } else {
+                       g_message("radcom_read: fread for data: %d bytes out of %d",
+                               bytes_read, length);
+               }
+               return -1;
+       }
+
+       wth->phdr.pkt_encap = wth->file_encap;
+
+       if (wth->file_encap == WTAP_ENCAP_LAPB)
+               fseek(wth->fh, 2, SEEK_CUR); /* FCS */
+
+       return data_offset;
+}
diff --git a/wiretap/radcom.h b/wiretap/radcom.h
new file mode 100644 (file)
index 0000000..044ad68
--- /dev/null
@@ -0,0 +1,23 @@
+/* radcom.h
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@verdict.uthscsa.edu>
+ * 
+ * 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.
+ *
+ */
+
+int radcom_open(wtap *wth);
+int radcom_read(wtap *wth);