Put the value(s) of a parameter into the top-level item for that
[obnox/wireshark/wip.git] / packet-x25.c
index 863141f4e6606256a36ce05ea3069e4551a3f5ed..9e5496bf6b620b200afc9d8b1ccd98154ff05692 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for X.25 packet disassembly
  * Olivier Abad <oabad@noos.fr>
  *
- * $Id: packet-x25.c,v 1.76 2003/01/31 03:17:47 guy Exp $
+ * $Id: packet-x25.c,v 1.85 2003/10/07 18:19:59 oabad Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -34,6 +34,7 @@
 #include "llcsaps.h"
 #include <epan/packet.h>
 #include <epan/circuit.h>
+#include "reassemble.h"
 #include "prefs.h"
 #include "nlpid.h"
 #include "x264_prt_id.h"
@@ -73,6 +74,14 @@ typedef enum {
 #define PACKET_IS_DATA(type)           (!(type & X25_NONDATA_BIT))
 #define PACKET_TYPE_FC(type)           (type & 0x1F)
 
+#define X25_MBIT_MOD8                  0x10
+#define X25_MBIT_MOD128                        0x01
+
+#define X25_ABIT                       0x8000
+
+#define X25_QBIT                       0x8000
+#define X25_DBIT                       0x4000
+
 #define X25_FAC_CLASS_MASK             0xC0
 
 #define X25_FAC_CLASS_A                        0x00
@@ -142,6 +151,16 @@ static gint ett_x25_fac_call_deflect = -1;
 static gint ett_x25_fac_priority = -1;
 static gint ett_x25_user_data = -1;
 
+static gint ett_x25_segment = -1;
+static gint ett_x25_segments = -1;
+static gint hf_x25_segments = -1;
+static gint hf_x25_segment = -1;
+static gint hf_x25_segment_overlap = -1;
+static gint hf_x25_segment_overlap_conflict = -1;
+static gint hf_x25_segment_multiple_tails = -1;
+static gint hf_x25_segment_too_long_segment = -1;
+static gint hf_x25_segment_error = -1;
+
 static const value_string vals_modulo[] = {
        { 1, "8" },
        { 2, "128" },
@@ -174,6 +193,20 @@ static struct true_false_string m_bit_tfs = {
        "End of data"
 };
 
+static const fragment_items x25_frag_items = {
+       &ett_x25_segment,
+       &ett_x25_segments,
+       &hf_x25_segments,
+       &hf_x25_segment,
+       &hf_x25_segment_overlap,
+       &hf_x25_segment_overlap_conflict,
+       &hf_x25_segment_multiple_tails,
+       &hf_x25_segment_too_long_segment,
+       &hf_x25_segment_error,
+       NULL,
+       "segments"
+};
+
 static dissector_handle_t ip_handle;
 static dissector_handle_t clnp_handle;
 static dissector_handle_t ositp_handle;
@@ -182,6 +215,12 @@ static dissector_handle_t data_handle;
 
 /* Preferences */
 static gboolean payload_is_qllc_sna = FALSE;
+static gboolean reassemble_x25 = FALSE;
+
+/* Reassembly of X.25 */
+
+static GHashTable *x25_segment_table = NULL;
+static GHashTable *x25_reassembled_table = NULL;
 
 static dissector_table_t x25_subdissector_table;
 static heur_dissector_list_t x25_heur_subdissector_list;
@@ -1003,7 +1042,7 @@ dump_facilities(proto_tree *tree, int *offset, tvbuff_t *tvb)
                    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);
+                   byte2 = tvb_get_guint8(tvb, *offset+2) & 0x3F;
                    proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
                            "Number of semi-octets in DTE address : %u", byte2);
                    for (i = 0; i < byte2; i++) {
@@ -1036,7 +1075,7 @@ dump_facilities(proto_tree *tree, int *offset, tvbuff_t *tvb)
                    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);
+                   byte2 = tvb_get_guint8(tvb, *offset+2) & 0x3F;
                    proto_tree_add_text(fac_subtree, tvb, *offset+2, 1,
                            "Number of semi-octets in DTE address : %u", byte2);
                    for (i = 0; i < byte2; i++) {
@@ -1407,7 +1446,7 @@ static const value_string sharing_strategy_vals[] = {
 
 static void
 dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
-    x25_dir_t dir)
+    x25_dir_t dir, gboolean side)
 {
     proto_tree *x25_tree=0, *gfi_tree=0, *userdata_tree=0;
     proto_item *ti;
@@ -1415,14 +1454,18 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
     guint x25_pkt_len;
     int modulo;
     guint16 vc;
-    dissector_handle_t dissect;
+    dissector_handle_t dissect = NULL;
     gboolean toa;         /* TOA/NPI address format */
     guint16 bytes0_1;
     guint8 pkt_type;
     char *short_name = NULL, *long_name = NULL;
-    tvbuff_t *next_tvb;
+    tvbuff_t *next_tvb = NULL;
     gboolean q_bit_set = FALSE;
+    gboolean m_bit_set;
+    gint payload_len;
+    guint32 frag_key;
     void *saved_private_data;
+    fragment_data *fd_head;
 
     if (check_col(pinfo->cinfo, COL_PROTOCOL))
        col_set_str(pinfo->cinfo, COL_PROTOCOL, "X.25");
@@ -1435,7 +1478,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
     pinfo->ctype = CT_X25;
     pinfo->circuit_id = vc;
 
-    if (bytes0_1 & 0x8000) toa = TRUE;
+    if (bytes0_1 & X25_ABIT) toa = TRUE;
     else toa = FALSE;
 
     x25_pkt_len = get_x25_pkt_len(tvb);
@@ -1451,7 +1494,7 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 
     pkt_type = tvb_get_guint8(tvb, 2);
     if (PACKET_IS_DATA(pkt_type)) {
-       if (bytes0_1 & 0x8000)
+       if (bytes0_1 & X25_QBIT)
            q_bit_set = TRUE;
     }
 
@@ -1674,11 +1717,11 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                }
                prt_id = tvb_get_guint8(tvb, localoffset+2);
                if (userdata_tree) {
-                   proto_tree_add_text(x25_tree, tvb, localoffset+2, 1,
+                   proto_tree_add_text(userdata_tree, tvb, localoffset+2, 1,
                                        "X.264 protocol identifier: %s",
                                        val_to_str(prt_id, prt_id_vals,
                                               "Unknown (0x%02X)"));
-                   proto_tree_add_text(x25_tree, tvb, localoffset+3, 1,
+                   proto_tree_add_text(userdata_tree, tvb, localoffset+3, 1,
                                        "X.264 sharing strategy: %s",
                                        val_to_str(tvb_get_guint8(tvb, localoffset+3),
                                        sharing_strategy_vals, "Unknown (0x%02X)"));
@@ -1715,7 +1758,6 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                        "X.263 secondary protocol ID: %s",
                                        val_to_str(spi, nlpid_vals, "Unknown (0x%02x)"));
                }
-               localoffset++;
 
                if (!pinfo->fd->flags.visited) {
                    /*
@@ -1726,13 +1768,50 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                    if (dissect != NULL)
                        x25_hash_add_proto_start(vc, pinfo->fd->num, dissect);
                }
-           }
-           if (localoffset < tvb_length(tvb)) {
-               if (userdata_tree) {
-                   proto_tree_add_text(userdata_tree, tvb, localoffset, -1,
-                               "Data");
+
+               /*
+                * If there's only one octet of user data, it's just
+                * an NLPID; don't try to dissect it.
+                */
+               if (localoffset + 1 == tvb_reported_length(tvb))
+                   return;
+
+               /*
+                * There's more than one octet of user data, so we'll
+                * dissect it; for some protocols, the NLPID is considered
+                * to be part of the PDU, so, for those cases, we don't
+                * skip past it.  For other protocols, we skip the NLPID.
+                */
+               switch (spi) {
+
+               case NLPID_ISO8473_CLNP:
+               case NLPID_ISO9542_ESIS:
+               case NLPID_ISO10589_ISIS:
+               case NLPID_ISO10747_IDRP:
+               case NLPID_SNDCF:
+                   /*
+                    * The NLPID is part of the PDU.  Don't skip it.
+                    * But if it's all there is to the PDU, don't
+                    * bother dissecting it.
+                    */
+                   break;
+
+               case NLPID_SPI_X_29:
+                   /*
+                    * The first 4 bytes of the call user data are
+                    * the SPI plus 3 reserved bytes; they are not
+                    * part of the data to be dissected as X.29 data.
+                    */
+                   localoffset += 4;
+                   break;
+
+               default:
+                   /*
+                    * The NLPID isn't part of the PDU - skip it.
+                    * If that means there's nothing to dissect
+                    */
+                   localoffset++;
                }
-               localoffset = tvb_length(tvb);
            }
        }
        break;
@@ -1771,13 +1850,6 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 
        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:
         switch (dir) {
@@ -2014,13 +2086,13 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                            "Data VC:%d P(S):%d P(R):%d %s", vc,
                            (pkt_type >> 1) & 0x07,
                            (pkt_type >> 5) & 0x07,
-                           ((pkt_type >> 4) & 0x01) ? " M" : "");
+                           (pkt_type & X25_MBIT_MOD8) ? " M" : "");
                else
                    col_add_fstr(pinfo->cinfo, COL_INFO,
                            "Data VC:%d P(S):%d P(R):%d %s", vc,
                            tvb_get_guint8(tvb, localoffset+1) >> 1,
                            pkt_type >> 1,
-                           (tvb_get_guint8(tvb, localoffset+1) & 0x01) ? " M" : "");
+                           (tvb_get_guint8(tvb, localoffset+1) & X25_MBIT_MOD128) ? " M" : "");
            }
            if (x25_tree) {
                proto_tree_add_uint(x25_tree, hf_x25_lcn, tvb, localoffset-2,
@@ -2048,9 +2120,67 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                            tvb_get_guint8(tvb, localoffset+1));
                }
            }
-           localoffset += (modulo == 8) ? 1 : 2;
+           if (modulo == 8) {
+               m_bit_set = pkt_type & X25_MBIT_MOD8;
+               localoffset += 1;
+           } else {
+               m_bit_set = tvb_get_guint8(tvb, localoffset+1) & X25_MBIT_MOD128;
+               localoffset += 2;
+           }
+           payload_len = tvb_reported_length_remaining(tvb, localoffset);
+           if (reassemble_x25) {
+               /*
+                * Reassemble received and sent traffic separately.
+                * We don't reassemble traffic with an unknown direction
+                * at all.
+                */
+               frag_key = vc;
+               if (side) {
+                   /*
+                    * OR in an extra bit to distinguish from traffic
+                    * in the other direction.
+                    */
+                   frag_key |= 0x10000;
+               }
+               fd_head = fragment_add_seq_next(tvb, localoffset, 
+                                               pinfo, frag_key,
+                                               x25_segment_table,
+                                               x25_reassembled_table,
+                                               payload_len, m_bit_set);
+               pinfo->fragmented = m_bit_set;
+             
+               if (fd_head) {
+                   if (fd_head->next) {
+                       /* This is the last packet */
+                       next_tvb = tvb_new_real_data(fd_head->data, 
+                                                    fd_head->len,
+                                                    fd_head->len);
+                       tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+                       add_new_data_source(pinfo, next_tvb, "Reassembled X.25");
+                       show_fragment_seq_tree(fd_head, 
+                                              &x25_frag_items, 
+                                              x25_tree, 
+                                              pinfo, next_tvb);
+                   }
+               }
+
+               if (m_bit_set && next_tvb == NULL) {
+                   /*
+                    * This isn't the last packet, so just
+                    * show it as X.25 user data.
+                    */
+                   proto_tree_add_text(x25_tree, tvb, localoffset, -1,
+                       "User data (%u byte%s)", payload_len,
+                       plurality(payload_len, "", "s"));
+                   return;
+               }
+           }
            break;
        }
+
+       /*
+        * Non-data packets (RR, RNR, REJ).
+        */
        switch (PACKET_TYPE_FC(pkt_type))
        {
        case X25_RR:
@@ -2136,9 +2266,13 @@ dissect_x25_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        localoffset += (modulo == 8) ? 1 : 2;
     }
 
-    if (localoffset >= tvb_reported_length(tvb)) return;
+    if (localoffset >= tvb_reported_length(tvb))
+      return;
+    if (pinfo->fragmented)
+      return;
 
-    next_tvb = tvb_new_subset(tvb, localoffset, -1, -1);
+    if (!next_tvb)
+      next_tvb = tvb_new_subset(tvb, localoffset, -1, -1);
 
     saved_private_data = pinfo->private_data;
     pinfo->private_data = &q_bit_set;
@@ -2201,7 +2335,8 @@ dissect_x25_dir(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
     dissect_x25_common(tvb, pinfo, tree,
        (pinfo->pseudo_header->x25.flags & FROM_DCE) ? X25_FROM_DCE :
-                                                      X25_FROM_DTE);
+                                                      X25_FROM_DTE,
+       pinfo->pseudo_header->x25.flags & FROM_DCE);
 }
 
 /*
@@ -2211,10 +2346,25 @@ dissect_x25_dir(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 static void
 dissect_x25(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
+    int direction;
+
     /*
      * We don't know if this packet is DTE->DCE or DCE->DCE.
+     * However, we can, at least, distinguish between the two
+     * sides of the conversation, based on the addresses and
+     * ports.
      */
-    dissect_x25_common(tvb, pinfo, tree, X25_UNKNOWN);
+    direction = CMP_ADDRESS(&pinfo->src, &pinfo->dst);
+    if (direction == 0)
+       direction = (pinfo->srcport > pinfo->destport)*2 - 1;
+    dissect_x25_common(tvb, pinfo, tree, X25_UNKNOWN, direction > 0);
+}
+
+static void
+x25_reassemble_init(void)
+{
+  fragment_table_init(&x25_segment_table);
+  reassembled_table_init(&x25_reassembled_table);
 }
 
 void
@@ -2225,13 +2375,13 @@ proto_register_x25(void)
          { "GFI", "x.25.gfi", FT_UINT16, BASE_DEC, NULL, 0xF000,
                "General format identifier", HFILL }},
        { &hf_x25_abit,
-         { "A Bit", "x.25.a", FT_BOOLEAN, 16, NULL, 0x8000,
+         { "A Bit", "x.25.a", FT_BOOLEAN, 16, NULL, X25_ABIT,
                "Address Bit", HFILL }},
        { &hf_x25_qbit,
-         { "Q Bit", "x.25.q", FT_BOOLEAN, 16, NULL, 0x8000,
+         { "Q Bit", "x.25.q", FT_BOOLEAN, 16, NULL, X25_QBIT,
                "Qualifier Bit", HFILL }},
        { &hf_x25_dbit,
-         { "D Bit", "x.25.d", FT_BOOLEAN, 16, NULL, 0x4000,
+         { "D Bit", "x.25.d", FT_BOOLEAN, 16, NULL, X25_DBIT,
                "Delivery Confirmation Bit", HFILL }},
        { &hf_x25_mod,
          { "Modulo", "x.25.mod", FT_UINT16, BASE_DEC, VALS(vals_modulo), 0x3000,
@@ -2255,10 +2405,10 @@ proto_register_x25(void)
          { "P(R)", "x.25.p_r", FT_UINT8, BASE_DEC, NULL, 0xFE,
                "Packet Receive Sequence Number", HFILL }},
        { &hf_x25_mbit_mod8,
-         { "M Bit", "x.25.m", FT_BOOLEAN, 8, TFS(&m_bit_tfs), 0x10,
+         { "M Bit", "x.25.m", FT_BOOLEAN, 8, TFS(&m_bit_tfs), X25_MBIT_MOD8,
                "More Bit", HFILL }},
        { &hf_x25_mbit_mod128,
-         { "M Bit", "x.25.m", FT_BOOLEAN, 8, TFS(&m_bit_tfs), 0x01,
+         { "M Bit", "x.25.m", FT_BOOLEAN, 8, TFS(&m_bit_tfs), X25_MBIT_MOD128,
                "More Bit", HFILL }},
        { &hf_x25_p_s_mod8,
          { "P(S)", "x.25.p_s", FT_UINT8, BASE_DEC, NULL, 0x0E,
@@ -2266,6 +2416,33 @@ proto_register_x25(void)
        { &hf_x25_p_s_mod128,
          { "P(S)", "x.25.p_s", FT_UINT8, BASE_DEC, NULL, 0xFE,
                "Packet Send Sequence Number", HFILL }},
+       { &hf_x25_segment_overlap,
+         { "Fragment overlap", "x25.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+           "Fragment overlaps with other fragments", HFILL }},
+       
+       { &hf_x25_segment_overlap_conflict,
+         { "Conflicting data in fragment overlap",     "x25.fragment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+           "Overlapping fragments contained conflicting data", HFILL }},
+       
+       { &hf_x25_segment_multiple_tails,
+         { "Multiple tail fragments found",    "x25.fragment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+           "Several tails were found when defragmenting the packet", HFILL }},
+       
+       { &hf_x25_segment_too_long_segment,
+         { "Fragment too long",        "x25.fragment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+           "Fragment contained data past end of packet", HFILL }},
+       
+       { &hf_x25_segment_error,
+         { "Defragmentation error", "x25.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+           "Defragmentation error due to illegal fragments", HFILL }},
+       
+       { &hf_x25_segment,
+         { "X.25 Fragment", "x25.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+           "X25 Fragment", HFILL }},
+       
+       { &hf_x25_segments,
+         { "X.25 Fragments", "x25.fragments", FT_NONE, BASE_NONE, NULL, 0x0,
+           "X.25 Fragments", HFILL }},
     };
     static gint *ett[] = {
         &ett_x25,
@@ -2291,7 +2468,9 @@ proto_register_x25(void)
        &ett_x25_fac_calling_addr_ext,
        &ett_x25_fac_call_deflect,
        &ett_x25_fac_priority,
-       &ett_x25_user_data
+       &ett_x25_user_data,
+       &ett_x25_segment,
+       &ett_x25_segments
     };
     module_t *x25_module;
 
@@ -2313,6 +2492,11 @@ proto_register_x25(void)
             "Default to QLLC/SNA",
             "If CALL REQUEST not seen or didn't specify protocol, dissect as QLLC/SNA",
             &payload_is_qllc_sna);
+    prefs_register_bool_preference(x25_module, "reassemble",
+                                  "Reassemble fragmented X.25 packets",
+                                  "Reassemble fragmented X.25 packets",
+                                  &reassemble_x25);
+    register_init_routine(&x25_reassemble_init);
 }
 
 void