CRLDP support, and assorted byg fixes, from Michael Rozhavsky.
[obnox/wireshark/wip.git] / packet-clnp.c
index 5153a18fb0ad21b8870a28a01549cf8db8267bd8..c17f4a929fec629cc2b28f7648f0a1eb1cc14deb 100644 (file)
@@ -1,14 +1,13 @@
 /* packet-clnp.c
  * Routines for ISO/OSI network and transport protocol packet disassembly
  *
- * $Id: packet-clnp.c,v 1.14 2000/11/18 10:38:24 guy Exp $
+ * $Id: packet-clnp.c,v 1.53 2002/04/07 21:54:48 guy Exp $
  * Laurent Deniel <deniel@worldnet.fr>
  * Ralf Schneider <Ralf.Schneider@t-online.de>
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1998 Gerald Combs
- *
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -23,7 +22,6 @@
  * 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 <ctype.h>
 #include <glib.h>
 #include "prefs.h"
-#include "packet.h"
+#include <epan/packet.h>
+#include "reassemble.h"
 #include "packet-osi.h"
 #include "packet-osi-options.h"
-#include "packet-clnp.h"
 #include "packet-isis.h"
 #include "packet-esis.h"
 #include "nlpid.h"
 
 static int  proto_clnp         = -1;
 static gint ett_clnp           = -1;
+static gint ett_clnp_type      = -1;
+static gint ett_clnp_segments  = -1;
+static gint ett_clnp_segment   = -1;
+static gint ett_clnp_disc_pdu  = -1;
 static int  proto_cotp         = -1;
 static gint ett_cotp           = -1;
 static int  proto_cltp         = -1;
@@ -67,6 +69,15 @@ static int hf_clnp_dest_length = -1;
 static int hf_clnp_dest        = -1;
 static int hf_clnp_src_length  = -1;
 static int hf_clnp_src         = -1;
+static int hf_clnp_segments    = -1;
+static int hf_clnp_segment     = -1;
+static int hf_clnp_segment_overlap = -1;
+static int hf_clnp_segment_overlap_conflict = -1;
+static int hf_clnp_segment_multiple_tails = -1;
+static int hf_clnp_segment_too_long_segment = -1;
+static int hf_clnp_segment_error = -1;
+
+static dissector_handle_t data_handle;
 
 /*
  * ISO 8473 OSI CLNP definition (see RFC994)
@@ -99,7 +110,7 @@ static int hf_clnp_src         = -1;
 #define ERQ_NPDU               0x1E
 #define ERP_NPDU               0x1F
 
-static const value_string npdu_type_vals[] = {
+static const value_string npdu_type_abbrev_vals[] = {
   { DT_NPDU,   "DT" },
   { MD_NPDU,   "MD" },
   { ER_NPDU,   "ER" },
@@ -108,6 +119,15 @@ static const value_string npdu_type_vals[] = {
   { 0,         NULL }
 };
 
+static const value_string npdu_type_vals[] = {
+  { DT_NPDU,   "Data" },
+  { MD_NPDU,   "Multicast Data" },
+  { ER_NPDU,   "Error Report" },
+  { ERQ_NPDU,  "Echo Request" },
+  { ERP_NPDU,  "Echo Response" },
+  { 0,         NULL }
+};
+
 /* field position */
 
 #define P_CLNP_PROTO_ID                0
@@ -250,9 +270,15 @@ static u_short dst_ref;
    Subset of CLNP. */
 static heur_dissector_list_t cotp_is_heur_subdissector_list;
 
+/*
+ * Reassembly of CLNP.
+ */
+static GHashTable *clnp_segment_table = NULL;
+
 /* options */
 static guint tp_nsap_selector = NSEL_TP;
 static gboolean always_decode_transport = FALSE;
+static gboolean clnp_reassemble = FALSE;
 
 /* function definitions */
 
@@ -507,7 +533,7 @@ static gboolean osi_decode_tp_var_part(tvbuff_t *tvb, int offset,
     case VP_TPDU_SIZE:
       c1 = tvb_get_guint8(tvb, offset) & 0x0F;
       proto_tree_add_text(tree, tvb, offset, length, 
-               "TPDU size: %u", 2 << c1);
+               "TPDU size: %u", 1 << c1);
       offset += length;
       vp_length -= length;
       break;
@@ -667,8 +693,8 @@ static int osi_decode_DR(tvbuff_t *tvb, int offset,
       break;
   }
 
-  if (check_col(pinfo->fd, COL_INFO))
-    col_append_fstr(pinfo->fd, COL_INFO,
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_append_fstr(pinfo->cinfo, COL_INFO,
                "DR TPDU src-ref: 0x%04x dst-ref: 0x%04x",
                 src_ref, dst_ref);
 
@@ -690,7 +716,7 @@ static int osi_decode_DR(tvbuff_t *tvb, int offset,
   offset += li + 1;
 
   /* User data */
-  dissect_data(tvb, offset, pinfo, tree);
+  call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,tvb_reported_length_remaining(tvb,offset)), pinfo, tree);
   offset += tvb_length_remaining(tvb, offset);
      /* we dissected all of the containing PDU */
 
@@ -762,8 +788,8 @@ static int osi_decode_DT(tvbuff_t *tvb, int offset,
       break;
   }
 
-  if (check_col(pinfo->fd, COL_INFO))
-    col_append_fstr(pinfo->fd, COL_INFO, "DT TPDU (%u) dst-ref: 0x%04x %s", 
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_append_fstr(pinfo->cinfo, COL_INFO, "DT TPDU (%u) dst-ref: 0x%04x %s", 
                 tpdu_nr,
                 dst_ref,
                 (fragment)? "(fragment)" : "");
@@ -824,10 +850,10 @@ static int osi_decode_DT(tvbuff_t *tvb, int offset,
                *subdissector_found = TRUE;
        } else {
          /* Fill in other Dissectors using inactive subset here */
-         dissect_data(next_tvb, 0, pinfo, tree);
+         call_dissector(data_handle,next_tvb, pinfo, tree);
        }
   } else
-       dissect_data(next_tvb, 0, pinfo, tree);
+       call_dissector(data_handle,next_tvb, pinfo, tree);
   offset += tvb_length_remaining(tvb, offset);
      /* we dissected all of the containing PDU */
 
@@ -885,8 +911,8 @@ static int osi_decode_ED(tvbuff_t *tvb, int offset,
       break;
   } /* li */
 
-  if (check_col(pinfo->fd, COL_INFO))
-    col_append_fstr(pinfo->fd, COL_INFO, "ED TPDU (%u) dst-ref: 0x%04x", 
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_append_fstr(pinfo->cinfo, COL_INFO, "ED TPDU (%u) dst-ref: 0x%04x", 
                 tpdu_nr, dst_ref);
 
   if (tree) {
@@ -932,7 +958,7 @@ static int osi_decode_ED(tvbuff_t *tvb, int offset,
   offset += li;
 
   next_tvb = tvb_new_subset(tvb, offset, -1, -1);
-  dissect_data(next_tvb, 0, pinfo, tree);
+  call_dissector(data_handle,next_tvb, pinfo, tree);
 
   offset += tvb_length_remaining(tvb, offset);
      /* we dissected all of the containing PDU */
@@ -963,8 +989,8 @@ static int osi_decode_RJ(tvbuff_t *tvb, int offset,
       break;
   }
 
-  if (check_col(pinfo->fd, COL_INFO))
-    col_append_fstr(pinfo->fd, COL_INFO, "RJ TPDU (%u) dst-ref: 0x%04x", 
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_append_fstr(pinfo->cinfo, COL_INFO, "RJ TPDU (%u) dst-ref: 0x%04x", 
                 tpdu_nr, dst_ref);
 
   if (tree) {
@@ -1012,8 +1038,8 @@ static int osi_decode_CC(tvbuff_t *tvb, int offset,
   if (class_option > 4)
     return -1;
 
-  if (check_col(pinfo->fd, COL_INFO))
-    col_append_fstr(pinfo->fd, COL_INFO,
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_append_fstr(pinfo->cinfo, COL_INFO,
                 "%s TPDU src-ref: 0x%04x dst-ref: 0x%04x",
                 (tpdu == CR_TPDU) ? "CR" : "CC",
                 src_ref,
@@ -1061,7 +1087,7 @@ static int osi_decode_CC(tvbuff_t *tvb, int offset,
   offset += li;
 
   /* User data */
-  dissect_data(tvb, offset, pinfo, tree);
+  call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,tvb_reported_length_remaining(tvb,offset)), pinfo, tree);
   offset += tvb_length_remaining(tvb, offset);
      /* we dissected all of the containing PDU */
 
@@ -1081,8 +1107,8 @@ static int osi_decode_DC(tvbuff_t *tvb, int offset,
 
   src_ref = tvb_get_ntohs(tvb, offset + P_SRC_REF);
 
-  if (check_col(pinfo->fd, COL_INFO))
-    col_append_fstr(pinfo->fd, COL_INFO,
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_append_fstr(pinfo->cinfo, COL_INFO,
                 "DC TPDU src-ref: 0x%04x dst-ref: 0x%04x", 
                 src_ref,
                 dst_ref);
@@ -1139,8 +1165,8 @@ static int osi_decode_AK(tvbuff_t *tvb, int offset,
 
     tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);
 
-    if (check_col(pinfo->fd, COL_INFO))
-      col_append_fstr(pinfo->fd, COL_INFO, "AK TPDU (%u) dst-ref: 0x%04x", 
+    if (check_col(pinfo->cinfo, COL_INFO))
+      col_append_fstr(pinfo->cinfo, COL_INFO, "AK TPDU (%u) dst-ref: 0x%04x", 
                   tpdu_nr, dst_ref);
 
     if (tree) {
@@ -1183,8 +1209,8 @@ static int osi_decode_AK(tvbuff_t *tvb, int offset,
     tpdu_nr   = tvb_get_ntohl(tvb, offset + P_TPDU_NR_234);
     cdt_in_ak = tvb_get_ntohs(tvb, offset + P_CDT_IN_AK);
 
-    if (check_col(pinfo->fd, COL_INFO))
-      col_append_fstr(pinfo->fd, COL_INFO, "AK TPDU (%u) dst-ref: 0x%04x", 
+    if (check_col(pinfo->cinfo, COL_INFO))
+      col_append_fstr(pinfo->cinfo, COL_INFO, "AK TPDU (%u) dst-ref: 0x%04x", 
                   tpdu_nr, dst_ref);
     
     if (tree) {
@@ -1277,8 +1303,8 @@ static int osi_decode_EA(tvbuff_t *tvb, int offset,
       break;
   } /* li */
 
-  if (check_col(pinfo->fd, COL_INFO))
-    col_append_fstr(pinfo->fd, COL_INFO, 
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_append_fstr(pinfo->cinfo, COL_INFO, 
                 "EA TPDU (%u) dst-ref: 0x%04x", tpdu_nr, dst_ref);
 
   if (tree) {
@@ -1356,8 +1382,8 @@ static int osi_decode_ER(tvbuff_t *tvb, int offset,
       break;
   }
 
-  if (check_col(pinfo->fd, COL_INFO))
-    col_append_fstr(pinfo->fd, COL_INFO, "ER TPDU dst-ref: 0x%04x", dst_ref);
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_append_fstr(pinfo->cinfo, COL_INFO, "ER TPDU dst-ref: 0x%04x", dst_ref);
 
   if (tree) {
     ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
@@ -1379,15 +1405,14 @@ static int osi_decode_ER(tvbuff_t *tvb, int offset,
 } /* osi_decode_ER */
 
 static int osi_decode_UD(tvbuff_t *tvb, int offset, 
-                        packet_info *pinfo, proto_tree *tree,
-                        gboolean *subdissector_found)
+                        packet_info *pinfo, proto_tree *tree)
 {
   proto_item *ti;
   proto_tree *cltp_tree = NULL;
   tvbuff_t   *next_tvb;
 
-  if (check_col(pinfo->fd, COL_INFO))
-    col_append_str(pinfo->fd, COL_INFO, "UD TPDU");
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_append_str(pinfo->cinfo, COL_INFO, "UD TPDU");
 
   if (tree) {
     ti = proto_tree_add_item(tree, proto_cltp, tvb, offset, li + 1, FALSE);
@@ -1409,7 +1434,7 @@ static int osi_decode_UD(tvbuff_t *tvb, int offset,
   offset += li;
 
   next_tvb = tvb_new_subset(tvb, offset, -1, -1);
-  dissect_data(next_tvb, 0, pinfo, tree);
+  call_dissector(data_handle,next_tvb, pinfo, tree);
   offset += tvb_length_remaining(tvb, offset);
      /* we dissected all of the containing PDU */
 
@@ -1443,19 +1468,19 @@ static gboolean dissect_ositp_internal(tvbuff_t *tvb, packet_info *pinfo,
 
   /* Initialize the COL_INFO field; each of the TPDUs will have its
      information appended. */
-  if (check_col(pinfo->fd, COL_INFO))
-    col_add_str(pinfo->fd, COL_INFO, "");
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_add_str(pinfo->cinfo, COL_INFO, "");
 
   while (tvb_offset_exists(tvb, offset)) {
     if (!first_tpdu) {
-      if (check_col(pinfo->fd, COL_INFO))
-        col_append_str(pinfo->fd, COL_INFO, ", ");
+      if (check_col(pinfo->cinfo, COL_INFO))
+        col_append_str(pinfo->cinfo, COL_INFO, ", ");
     }
     if ((li = tvb_get_guint8(tvb, offset + P_LI)) == 0) {
-      if (check_col(pinfo->fd, COL_INFO))
-        col_append_str(pinfo->fd, COL_INFO, "Length indicator is zero");
+      if (check_col(pinfo->cinfo, COL_INFO))
+        col_append_str(pinfo->cinfo, COL_INFO, "Length indicator is zero");
       if (!first_tpdu)
-        dissect_data(tvb, offset, pinfo, tree);
+        call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,tvb_reported_length_remaining(tvb,offset)), pinfo, tree);
       return found_ositp;
     }
 
@@ -1496,28 +1521,27 @@ static gboolean dissect_ositp_internal(tvbuff_t *tvb, packet_info *pinfo,
         new_offset = osi_decode_ER(tvb, offset, pinfo, tree);
         break;
       case UD_TPDU :
-        new_offset = osi_decode_UD(tvb, offset, pinfo, tree,
-                                  &subdissector_found);
+        new_offset = osi_decode_UD(tvb, offset, pinfo, tree);
         is_cltp = TRUE;
         break;
       default      :
-        if (first_tpdu && check_col(pinfo->fd, COL_INFO))
-          col_append_fstr(pinfo->fd, COL_INFO, "Unknown TPDU type (0x%x)", tpdu);
+        if (first_tpdu && check_col(pinfo->cinfo, COL_INFO))
+          col_append_fstr(pinfo->cinfo, COL_INFO, "Unknown TPDU type (0x%x)", tpdu);
         new_offset = -1;       /* bad PDU type */
         break;
     }
 
     if (new_offset == -1) { /* incorrect TPDU */
       if (!first_tpdu)
-        dissect_data(tvb, offset, pinfo, tree);
+        call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,tvb_reported_length_remaining(tvb,offset)), pinfo, tree);
       break;
     }
 
     if (first_tpdu) {
       /* Well, we found at least one valid COTP or CLTP PDU, so I guess this
          is either COTP or CLTP. */
-      if (!subdissector_found && check_col(pinfo->fd, COL_PROTOCOL))
-        col_add_str(pinfo->fd, COL_PROTOCOL, is_cltp ? "CLTP" : "COTP");
+      if (!subdissector_found && check_col(pinfo->cinfo, COL_PROTOCOL))
+        col_set_str(pinfo->cinfo, COL_PROTOCOL, is_cltp ? "CLTP" : "COTP");
       found_ositp = TRUE;
     }
 
@@ -1527,10 +1551,10 @@ static gboolean dissect_ositp_internal(tvbuff_t *tvb, packet_info *pinfo,
   return found_ositp;
 } /* dissect_ositp_internal */
 
-void dissect_ositp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) 
+static void dissect_ositp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) 
 {
   if (!dissect_ositp_internal(tvb, pinfo, tree, FALSE))
-    dissect_data(tvb, 0, pinfo, tree);
+    call_dissector(data_handle,tvb, pinfo, tree);
 }
 
 
@@ -1549,26 +1573,39 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   guint8      cnf_type;
   char        flag_string[6+1];
   char       *pdu_type_string;
+  proto_tree *type_tree;
   guint16     segment_length;
+  guint16     du_id = 0;
   guint16     segment_offset = 0;
   guint16     cnf_cksum;
+  cksum_status_t cksum_status;
   int         offset;
   u_char      src_len, dst_len, nsel, opt_len = 0;
-  guint8     *dst_addr, *src_addr;
-  guint       len;
-  tvbuff_t   *next_tvb;
-
-  CHECK_DISPLAY_AS_DATA(proto_clnp, tvb, pinfo, tree);
-
-  pinfo->current_proto = "CLNP";
-
-  if (check_col(pinfo->fd, COL_PROTOCOL))
-    col_add_str(pinfo->fd, COL_PROTOCOL, "CLNP");
+  const guint8     *dst_addr, *src_addr;
+  gint        len;
+  guint       next_length;
+  proto_tree *discpdu_tree;
+  volatile address save_dl_src;
+  volatile address save_dl_dst;
+  volatile address save_net_src;
+  volatile address save_net_dst;
+  volatile address save_src;
+  volatile address save_dst;
+  gboolean    save_in_error_pkt;
+  fragment_data *fd_head;
+  tvbuff_t   *volatile next_tvb;
+  gboolean    update_col_info = TRUE;
+  gboolean    save_fragmented;
+
+  if (check_col(pinfo->cinfo, COL_PROTOCOL))
+    col_set_str(pinfo->cinfo, COL_PROTOCOL, "CLNP");
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_clear(pinfo->cinfo, COL_INFO);
 
   cnf_proto_id = tvb_get_guint8(tvb, P_CLNP_PROTO_ID);
   if (cnf_proto_id == NLPID_NULL) {
-    if (check_col(pinfo->fd, COL_INFO))
-      col_add_str(pinfo->fd, COL_INFO, "Inactive subset");
+    if (check_col(pinfo->cinfo, COL_INFO))
+      col_set_str(pinfo->cinfo, COL_INFO, "Inactive subset");
     if (tree) {
       ti = proto_tree_add_item(tree, proto_clnp, tvb, P_CLNP_PROTO_ID, 1, FALSE);
       clnp_tree = proto_item_add_subtree(ti, ett_clnp);
@@ -1584,7 +1621,7 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   /* return if version not known */
   cnf_vers = tvb_get_guint8(tvb, P_CLNP_VERS);
   if (cnf_vers != ISO8473_V1) {
-    dissect_data(tvb, 0, pinfo, tree);
+    call_dissector(data_handle,tvb, pinfo, tree);
     return;
   }
 
@@ -1604,12 +1641,12 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     cnf_ttl = tvb_get_guint8(tvb, P_CLNP_TTL);
     proto_tree_add_uint_format(clnp_tree, hf_clnp_ttl, tvb, P_CLNP_TTL, 1, 
                               cnf_ttl,
-                              "Holding Time : %u (%u secs)", 
-                              cnf_ttl, cnf_ttl / 2);
+                              "Holding Time : %u (%u.%u secs)", 
+                              cnf_ttl, cnf_ttl / 2, (cnf_ttl % 2) * 5);
   }
 
   cnf_type = tvb_get_guint8(tvb, P_CLNP_TYPE);
-  pdu_type_string = val_to_str(cnf_type & CNF_TYPE, npdu_type_vals,
+  pdu_type_string = val_to_str(cnf_type & CNF_TYPE, npdu_type_abbrev_vals,
                                "Unknown (0x%02x)");
   flag_string[0] = '\0';
   if (cnf_type & CNF_SEG_OK)
@@ -1619,12 +1656,28 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   if (cnf_type & CNF_ERR_OK)
     strcat(flag_string, "E ");
   if (tree) {
-    proto_tree_add_uint_format(clnp_tree, hf_clnp_type, tvb, P_CLNP_TYPE, 1,
+    ti = proto_tree_add_uint_format(clnp_tree, hf_clnp_type, tvb, P_CLNP_TYPE, 1,
                               cnf_type,
                               "PDU Type     : 0x%02x (%s%s)",
                               cnf_type,
                               flag_string,
                               pdu_type_string);
+    type_tree = proto_item_add_subtree(ti, ett_clnp_type);
+    proto_tree_add_text(type_tree, tvb, P_CLNP_TYPE, 1, "%s",
+                       decode_boolean_bitfield(cnf_type, CNF_SEG_OK, 8,
+                                     "Segmentation permitted",
+                                     "Segmentation not permitted"));
+    proto_tree_add_text(type_tree, tvb, P_CLNP_TYPE, 1, "%s",
+                       decode_boolean_bitfield(cnf_type, CNF_MORE_SEGS, 8,
+                                     "More segments",
+                                     "Last segment"));
+    proto_tree_add_text(type_tree, tvb, P_CLNP_TYPE, 1, "%s",
+                       decode_boolean_bitfield(cnf_type, CNF_ERR_OK, 8,
+                                     "Report error if PDU discarded",
+                                     "Don't report error if PDU discarded"));
+    proto_tree_add_text(type_tree, tvb, P_CLNP_TYPE, 1, "%s",
+                       decode_enumerated_bitfield(cnf_type, CNF_TYPE, 8,
+                                     npdu_type_vals, "%s"));
   }
 
   /* If we don't have the full header - i.e., not enough to see the
@@ -1633,19 +1686,52 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
      we set it otherwise. */
 
   if (!tvb_bytes_exist(tvb, 0, cnf_hdr_len)) {
-    if (check_col(pinfo->fd, COL_INFO))
-      col_add_fstr(pinfo->fd, COL_INFO, "%s NPDU %s", pdu_type_string, flag_string);
+    if (check_col(pinfo->cinfo, COL_INFO))
+      col_add_fstr(pinfo->cinfo, COL_INFO, "%s NPDU %s", pdu_type_string, flag_string);
   }
 
   segment_length = tvb_get_ntohs(tvb, P_CLNP_SEGLEN);
+  cnf_cksum = tvb_get_ntohs(tvb, P_CLNP_CKSUM);
+  cksum_status = calc_checksum(tvb, 0, cnf_hdr_len, cnf_cksum);
   if (tree) {
     proto_tree_add_uint(clnp_tree, hf_clnp_pdu_length, tvb, P_CLNP_SEGLEN, 2,
                        segment_length);
-    cnf_cksum = tvb_get_ntohs(tvb, P_CLNP_CKSUM);
-    proto_tree_add_uint_format(clnp_tree, hf_clnp_checksum, tvb, P_CLNP_CKSUM, 2,
+    switch (cksum_status) {
+
+    default:
+       /*
+        * No checksum present, or not enough of the header present to
+        * checksum it.
+        */
+       proto_tree_add_uint_format(clnp_tree, hf_clnp_checksum, tvb,
+                              P_CLNP_CKSUM, 2,
                               cnf_cksum,
                               "Checksum     : 0x%04x",
                               cnf_cksum);
+       break;
+
+    case CKSUM_OK:
+       /*
+        * Checksum is correct.
+        */
+       proto_tree_add_uint_format(clnp_tree, hf_clnp_checksum, tvb,
+                              P_CLNP_CKSUM, 2,
+                              cnf_cksum,
+                              "Checksum     : 0x%04x (correct)",
+                              cnf_cksum);
+       break;
+
+    case CKSUM_NOT_OK:
+       /*
+        * Checksum is not correct.
+        */
+       proto_tree_add_uint_format(clnp_tree, hf_clnp_checksum, tvb,
+                              P_CLNP_CKSUM, 2,
+                              cnf_cksum,
+                              "Checksum     : 0x%04x (incorrect)",
+                              cnf_cksum);
+       break;
+    }
     opt_len -= 9; /* Fixed part of Hesder */
   } /* tree */
 
@@ -1676,10 +1762,10 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     opt_len -= dst_len + src_len +2;
   }
 
-  if (check_col(pinfo->fd, COL_RES_NET_SRC))
-    col_add_fstr(pinfo->fd, COL_RES_NET_SRC, "%s", print_nsap_net(src_addr, src_len));
-  if (check_col(pinfo->fd, COL_RES_NET_DST))
-    col_add_fstr(pinfo->fd, COL_RES_NET_DST, "%s", print_nsap_net(dst_addr, dst_len));
+  SET_ADDRESS(&pinfo->net_src, AT_OSI, src_len, src_addr);
+  SET_ADDRESS(&pinfo->src, AT_OSI, src_len, src_addr);
+  SET_ADDRESS(&pinfo->net_dst, AT_OSI, dst_len, dst_addr);
+  SET_ADDRESS(&pinfo->dst, AT_OSI, dst_len, dst_addr);
 
   /* Segmentation Part */
 
@@ -1690,10 +1776,11 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     tvb_memcpy(tvb, (guint8 *)&seg, offset, sizeof(seg));      /* XXX - not used */
     
     segment_offset = tvb_get_ntohs(tvb, offset + 2);
+    du_id = tvb_get_ntohs(tvb, offset);
     if (tree) {
       proto_tree_add_text(clnp_tree, tvb, offset, 2, 
                        "Data unit identifier: %06u",
-                       tvb_get_ntohs(tvb, offset));
+                       du_id);
       proto_tree_add_text(clnp_tree, tvb, offset + 2 , 2,
                        "Segment offset      : %6u", 
                        segment_offset);
@@ -1717,33 +1804,154 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
     dissect_osi_options( 0xff, 
                          opt_len,
-                         tvb, offset, pinfo, clnp_tree ); 
+                         tvb, offset, clnp_tree ); 
   }
 
   /* Length of CLNP datagram plus headers above it. */
   len = segment_length;
 
-  /* Set the payload and captured-payload lengths to the minima of (the
-     datagram length plus the length of the headers above it) and the
-     frame lengths. */
-  if (pinfo->len > len)
-    pinfo->len = len;
-  if (pinfo->captured_len > len)
-    pinfo->captured_len = len;
-
   offset = cnf_hdr_len;
 
-  /* For now, dissect the payload of segments other than the initial
-     segment as data, rather than handing them off to the transport
-     protocol, just as we do with fragments other than the first
-     fragment in a fragmented IP datagram; in the future, we will
-     probably reassemble fragments for IP, and may reassemble segments
-     for CLNP. */
-  if ((cnf_type & CNF_SEG_OK) && segment_offset != 0) {
-    if (check_col(pinfo->fd, COL_INFO))
-      col_add_fstr(pinfo->fd, COL_INFO, "Fragmented %s NPDU %s(off=%u)",
+  /* If clnp_reassemble is on, and this is a segment, we have all the
+   * data in the segment, and the checksum is valid, then just add the
+   * segment to the hashtable.
+   */
+  save_fragmented = pinfo->fragmented;
+  if (clnp_reassemble && (cnf_type & CNF_SEG_OK) &&
+       ((cnf_type & CNF_MORE_SEGS) || segment_offset != 0) &&
+       (tvb_reported_length(tvb) <= tvb_length(tvb)) &&
+       cksum_status != CKSUM_NOT_OK) {
+    fd_head = fragment_add(tvb, offset, pinfo, du_id, clnp_segment_table,
+                          segment_offset, segment_length - cnf_hdr_len,
+                          cnf_type & CNF_MORE_SEGS);
+
+    if (fd_head != NULL) {
+      fragment_data *fd;
+      proto_tree *ft=NULL;
+      proto_item *fi=NULL;
+
+      /* OK, we have the complete reassembled payload.
+         Allocate a new tvbuff, referring to the reassembled payload. */
+      next_tvb = tvb_new_real_data(fd_head->data, fd_head->datalen,
+       fd_head->datalen);
+
+      /* Add the tvbuff to the list of tvbuffs to which the tvbuff we
+         were handed refers, so it'll get cleaned up when that tvbuff
+         is cleaned up. */
+      tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+
+      /* Add the defragmented data to the data source list. */
+      add_new_data_source(pinfo->fd, next_tvb, "Reassembled CLNP");
+
+      /* It's not fragmented. */
+      pinfo->fragmented = FALSE;
+
+      /* show all segments */
+      fi = proto_tree_add_item(clnp_tree, hf_clnp_segments, 
+                next_tvb, 0, -1, FALSE);
+      ft = proto_item_add_subtree(fi, ett_clnp_segments);
+      for (fd = fd_head->next; fd != NULL; fd = fd->next){
+        if (fd->flags & (FD_OVERLAP|FD_OVERLAPCONFLICT
+                          |FD_MULTIPLETAILS|FD_TOOLONGFRAGMENT) ) {
+          /* this segment has some flags set, create a subtree 
+           * for it and display the flags.
+           */
+          proto_tree *fet = NULL;
+          proto_item *fei = NULL;
+          int hf;
+
+          if (fd->flags & (FD_OVERLAPCONFLICT
+                      |FD_MULTIPLETAILS|FD_TOOLONGFRAGMENT) ) {
+            hf = hf_clnp_segment_error;
+          } else {
+            hf = hf_clnp_segment;
+          }
+          fei = proto_tree_add_none_format(ft, hf, 
+                   next_tvb, fd->offset, fd->len,
+                   "Frame:%u payload:%u-%u",
+                   fd->frame,
+                   fd->offset,
+                   fd->offset+fd->len-1
+          );
+          fet = proto_item_add_subtree(fei, ett_clnp_segment);
+          if (fd->flags&FD_OVERLAP) {
+            proto_tree_add_boolean(fet, 
+                 hf_clnp_segment_overlap, next_tvb, 0, 0, 
+                 TRUE);
+          }
+          if (fd->flags&FD_OVERLAPCONFLICT) {
+            proto_tree_add_boolean(fet, 
+                 hf_clnp_segment_overlap_conflict, next_tvb, 0, 0, 
+                 TRUE);
+          }
+          if (fd->flags&FD_MULTIPLETAILS) {
+            proto_tree_add_boolean(fet, 
+                 hf_clnp_segment_multiple_tails, next_tvb, 0, 0, 
+                 TRUE);
+          }
+          if (fd->flags&FD_TOOLONGFRAGMENT) {
+            proto_tree_add_boolean(fet, 
+                 hf_clnp_segment_too_long_segment, next_tvb, 0, 0, 
+                 TRUE);
+          }
+        } else {
+          /* nothing of interest for this segment */
+          proto_tree_add_none_format(ft, hf_clnp_segment, 
+                   next_tvb, fd->offset, fd->len,
+                   "Frame:%u payload:%u-%u",
+                   fd->frame,
+                   fd->offset,
+                   fd->offset+fd->len-1
+          );
+        }
+      }
+      if (fd_head->flags & (FD_OVERLAPCONFLICT
+                        |FD_MULTIPLETAILS|FD_TOOLONGFRAGMENT) ) {
+        if (check_col(pinfo->cinfo, COL_INFO)) {
+          col_set_str(pinfo->cinfo, COL_INFO, "[Illegal segments]");
+          update_col_info = FALSE;
+        }
+      }
+    } else {
+      /* We don't have the complete reassembled payload. */
+      next_tvb = NULL;
+    }
+  } else {
+    /* If this is the first segment, dissect its contents, otherwise
+       just show it as a segment.
+
+       XXX - if we eventually don't save the reassembled contents of all
+       segmented datagrams, we may want to always reassemble. */
+    if ((cnf_type & CNF_SEG_OK) && segment_offset != 0) {
+      /* Not the first segment - don't dissect it. */
+      next_tvb = NULL;
+    } else {
+      /* First segment, or not segmented.  Dissect what we have here. */
+
+      /* Get a tvbuff for the payload. */
+      next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+
+      /*
+       * If this is the first segment, but not the only segment,
+       * tell the next protocol that.
+       */
+      if ((cnf_type & (CNF_SEG_OK|CNF_MORE_SEGS)) == (CNF_SEG_OK|CNF_MORE_SEGS))
+        pinfo->fragmented = TRUE;
+      else
+        pinfo->fragmented = FALSE;
+    }
+  }
+
+  if (next_tvb == NULL) {
+    /* Just show this as a segment. */
+    if (check_col(pinfo->cinfo, COL_INFO))
+      col_add_fstr(pinfo->cinfo, COL_INFO, "Fragmented %s NPDU %s(off=%u)",
                pdu_type_string, flag_string, segment_offset);
-    dissect_data(tvb, offset, pinfo, tree);
+
+    /* As we haven't reassembled anything, we haven't changed "pi", so
+       we don't have to restore it. */
+    call_dissector(data_handle,tvb_new_subset(tvb, offset,-1,tvb_reported_length_remaining(tvb,offset)), pinfo, tree);
+    pinfo->fragmented = save_fragmented;
     return;
   }
 
@@ -1757,19 +1965,78 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
          PDU, skip that? */
 
       if (nsel == (char)tp_nsap_selector || always_decode_transport) { 
-        next_tvb = tvb_new_subset(tvb, offset, -1, -1);
-        if (dissect_ositp_internal(next_tvb, pinfo, tree, FALSE))
+        if (dissect_ositp_internal(next_tvb, pinfo, tree, FALSE)) {
+          pinfo->fragmented = save_fragmented;
           return;      /* yes, it appears to be COTP or CLTP */
+        }
       }
       break;
 
     case ER_NPDU:
       /* The payload is the header and "none, some, or all of the data
          part of the discarded PDU", i.e. it's like an ICMP error;
-        just as we don't yet trust ourselves to be able to dissect
-        the payload of an ICMP error packet, we don't yet trust
-        ourselves to dissect the payload of a CLNP ER packet. */
-      break;
+        dissect it as a CLNP PDU. */
+      if (check_col(pinfo->cinfo, COL_INFO))
+        col_add_fstr(pinfo->cinfo, COL_INFO, "%s NPDU %s", pdu_type_string, flag_string);
+      if (tree) {
+        next_length = tvb_length_remaining(tvb, offset);
+        if (next_length != 0) {
+          /* We have payload; dissect it.
+             Make the columns non-writable, so the packet isn't shown
+             in the summary based on what the discarded PDU's contents
+             are. */
+          col_set_writable(pinfo->cinfo, FALSE);
+
+          /* Also, save the current values of the addresses, and restore
+             them when we're finished dissecting the contained packet, so
+             that the address columns in the summary don't reflect the
+             contained packet, but reflect this packet instead. */
+          save_dl_src = pinfo->dl_src;
+          save_dl_dst = pinfo->dl_dst;
+          save_net_src = pinfo->net_src;
+          save_net_dst = pinfo->net_dst;
+          save_src = pinfo->src;
+          save_dst = pinfo->dst;
+
+          /* Save the current value of the "we're inside an error packet"
+             flag, and set that flag; subdissectors may treat packets
+             that are the payload of error packets differently from
+             "real" packets. */
+          save_in_error_pkt = pinfo->in_error_pkt;
+          pinfo->in_error_pkt = TRUE;
+
+          /* Dissect the contained packet.
+             Catch ReportedBoundsError, and do nothing if we see it,
+             because it's not an error if the contained packet is short;
+             there's no guarantee that all of it was included.
+
+             XXX - should catch BoundsError, and re-throw it after cleaning
+             up. */
+          ti = proto_tree_add_text(clnp_tree, tvb, offset, next_length,
+            "Discarded PDU");
+          discpdu_tree = proto_item_add_subtree(ti, ett_clnp_disc_pdu);
+          TRY {
+            dissect_clnp(next_tvb, pinfo, discpdu_tree);
+          }
+          CATCH(ReportedBoundsError) {
+            ; /* do nothing */
+          }
+          ENDTRY;
+
+          /* Restore the "we're inside an error packet" flag. */
+          pinfo->in_error_pkt = save_in_error_pkt;
+
+          /* Restore the addresses. */
+          pinfo->dl_src = save_dl_src;
+          pinfo->dl_dst = save_dl_dst;
+          pinfo->net_src = save_net_src;
+          pinfo->net_dst = save_net_dst;
+          pinfo->src = save_src;
+          pinfo->dst = save_dst;
+        }
+      }
+      pinfo->fragmented = save_fragmented;
+      return;  /* we're done with this PDU */
 
     case ERQ_NPDU:
     case ERP_NPDU:
@@ -1777,62 +2044,98 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
       break;
     }
   }
-  if (check_col(pinfo->fd, COL_INFO))
-    col_add_fstr(pinfo->fd, COL_INFO, "%s NPDU %s", pdu_type_string, flag_string);
-  next_tvb = tvb_new_subset(tvb, offset, -1, -1);
-  dissect_data(next_tvb, 0, pinfo, tree);
-
+  if (check_col(pinfo->cinfo, COL_INFO))
+    col_add_fstr(pinfo->cinfo, COL_INFO, "%s NPDU %s", pdu_type_string, flag_string);
+  call_dissector(data_handle,next_tvb, pinfo, tree);
+  pinfo->fragmented = save_fragmented;
 } /* dissect_clnp */
 
+static void
+clnp_reassemble_init(void)
+{
+  fragment_table_init(&clnp_segment_table);
+}
 
 void proto_register_clnp(void)
 {
   static hf_register_info hf[] = {
     { &hf_clnp_id,
       { "Network Layer Protocol Identifier", "clnp.nlpi", FT_UINT8, BASE_HEX, 
-        VALS(nlpid_vals), 0x0, "" }},
+        VALS(nlpid_vals), 0x0, "", HFILL }},
 
     { &hf_clnp_length,
-      { "HDR Length   ", "clnp.len",      FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
+      { "HDR Length   ", "clnp.len",      FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
 
     { &hf_clnp_version,
-      { "Version      ", "clnp.version",  FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
+      { "Version      ", "clnp.version",  FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
 
     { &hf_clnp_ttl,
-      { "Holding Time ", "clnp.ttl",      FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
+      { "Holding Time ", "clnp.ttl",      FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
 
     { &hf_clnp_type,
-      { "PDU Type     ", "clnp.type",     FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
+      { "PDU Type     ", "clnp.type",     FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
 
     { &hf_clnp_pdu_length,
-      { "PDU length   ", "clnp.pdu.len",  FT_UINT16, BASE_DEC, NULL, 0x0, "" }},
+      { "PDU length   ", "clnp.pdu.len",  FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
 
     { &hf_clnp_checksum,
-      { "Checksum     ", "clnp.checksum", FT_UINT16, BASE_DEC, NULL, 0x0, "" }},
+      { "Checksum     ", "clnp.checksum", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
 
     { &hf_clnp_dest_length,
-      { "DAL ", "clnp.dsap.len", FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
+      { "DAL ", "clnp.dsap.len", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
 
     { &hf_clnp_dest,
-      { " DA ", "clnp.dsap",     FT_BYTES, BASE_NONE, NULL, 0x0, "" }},
+      { " DA ", "clnp.dsap",     FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL }},
 
     { &hf_clnp_src_length,
-      { "SAL ", "clnp.ssap.len", FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
+      { "SAL ", "clnp.ssap.len", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
 
     { &hf_clnp_src,
-      { " SA ", "clnp.ssap",     FT_BYTES, BASE_NONE, NULL, 0x0, "" }},
+      { " SA ", "clnp.ssap",     FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL }},
+
+    { &hf_clnp_segment_overlap,
+      { "Segment overlap", "clnp.segment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+       "Segment overlaps with other segments", HFILL }},
+
+    { &hf_clnp_segment_overlap_conflict,
+      { "Conflicting data in segment overlap", "clnp.segment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+       "Overlapping segments contained conflicting data", HFILL }},
+
+    { &hf_clnp_segment_multiple_tails,
+      { "Multiple tail segments found", "clnp.segment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+       "Several tails were found when reassembling the packet", HFILL }},
+
+    { &hf_clnp_segment_too_long_segment,
+      { "Segment too long", "clnp.segment.toolongsegment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+       "Segment contained data past end of packet", HFILL }},
+
+    { &hf_clnp_segment_error,
+      { "Reassembly error", "clnp.segment.error", FT_NONE, BASE_DEC, NULL, 0x0,
+       "Reassembly error due to illegal segments", HFILL }},
+
+    { &hf_clnp_segment,
+      { "CLNP Segment", "clnp.segment", FT_NONE, BASE_DEC, NULL, 0x0,
+       "CLNP Segment", HFILL }},
+
+    { &hf_clnp_segments,
+      { "CLNP Segments", "clnp.segments", FT_NONE, BASE_DEC, NULL, 0x0,
+       "CLNP Segments", HFILL }},
   };
   static gint *ett[] = {
     &ett_clnp,
+    &ett_clnp_type,
+    &ett_clnp_segments,
+    &ett_clnp_segment,
+    &ett_clnp_disc_pdu,
   };
 
   module_t *clnp_module;
 
-  proto_clnp = proto_register_protocol(PROTO_STRING_CLNP, "clnp");
+  proto_clnp = proto_register_protocol(PROTO_STRING_CLNP, "CLNP", "clnp");
   proto_register_field_array(proto_clnp, hf, array_length(hf));
   proto_register_subtree_array(ett, array_length(ett));
 
-  clnp_module = prefs_register_module("clnp", "CLNP", NULL);
+  clnp_module = prefs_register_protocol(proto_clnp, NULL);
   prefs_register_uint_preference(clnp_module, "tp_nsap_selector",
        "NSAP selector for Transport Protocol (last byte in hexa)",
        "NSAP selector for Transport Protocol (last byte in hexa)",
@@ -1841,7 +2144,10 @@ void proto_register_clnp(void)
        "Always try to decode NSDU as transport PDUs",
        "Always try to decode NSDU as transport PDUs",
                &always_decode_transport);
-
+  prefs_register_bool_preference(clnp_module, "reassemble",
+       "Reassemble segmented CLNP datagrams",
+       "Whether segmented CLNP datagrams should be reassembled",
+       &clnp_reassemble);
 }
 
 void proto_register_cotp(void)
@@ -1854,12 +2160,15 @@ void proto_register_cotp(void)
                &ett_cotp,
        };
 
-        proto_cotp = proto_register_protocol(PROTO_STRING_COTP, "cotp");
+        proto_cotp = proto_register_protocol(PROTO_STRING_COTP, "COTP", "cotp");
  /*       proto_register_field_array(proto_cotp, hf, array_length(hf));*/
        proto_register_subtree_array(ett, array_length(ett));
 
 /* subdissector code */
        register_heur_dissector_list("cotp_is", &cotp_is_heur_subdissector_list);
+
+       /* XXX - what about CLTP? */
+       register_dissector("ositp", dissect_ositp, proto_cotp);
 }
 
 void proto_register_cltp(void)
@@ -1872,14 +2181,21 @@ void proto_register_cltp(void)
                &ett_cltp,
        };
 
-        proto_cltp = proto_register_protocol(PROTO_STRING_CLTP, "cltp");
+        proto_cltp = proto_register_protocol(PROTO_STRING_CLTP, "CLTP", "cltp");
  /*       proto_register_field_array(proto_cotp, hf, array_length(hf));*/
        proto_register_subtree_array(ett, array_length(ett));
+       register_init_routine(clnp_reassemble_init);
 }
 
 void
 proto_reg_handoff_clnp(void)
 {
-       dissector_add("osinl", NLPID_ISO8473_CLNP, dissect_clnp);
-       dissector_add("osinl", NLPID_NULL, dissect_clnp);       /* Inactive subset */
+       dissector_handle_t clnp_handle;
+
+        data_handle = find_dissector("data");
+
+       clnp_handle = create_dissector_handle(dissect_clnp, proto_clnp);
+       dissector_add("osinl", NLPID_ISO8473_CLNP, clnp_handle);
+       dissector_add("osinl", NLPID_NULL, clnp_handle); /* Inactive subset */
+       dissector_add("x.25.spi", NLPID_ISO8473_CLNP, clnp_handle);
 }