Huge thanks to our Platinum Members Endace and LiveAction,
and our Silver Member Veeam, for supporting the Wireshark Foundation and project.

Ethereal-dev: Re: [Ethereal-dev] Need help with protocol that spans multiple TVBs

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: "Sid Sid" <ysidelnikov@xxxxxxxxxxx>
Date: Mon, 12 Apr 2004 12:40:46 +0000
As I can see you completely rewrote(or probably,recreated) Presentation dissector.
The reason is not clear for me.
Regarding trace file that you have send me:
I do not know MMS protocol very well but as I can see from trace it nevertheless uses ACSE protocol too.So presentation dissector should call ASCE dissector in MMS case too and only ACSE dissector can find which way to go - FTAM,CMIP or MMS. I mean that MMS has in the ACSE pdu aplication context name oid 1.0.9506.2.3 and ACSE dissector can use it for calling MMS dissector.
For example, in ACSE dissector:
	unsigned char ftam_oid[] = "1.0.8571.1.1";
	unsigned char cmip_oid[] ="2.9.0.0.2";
	unsigned char MMS_oid[] ="1.0.9506.2.3";

...

if(tag == APPLICATION_CONTEXT_NAME )
{
if( !memcmp(oid_string,cmip_oid,strlen(cmip_oid))  )
  {
     /* it is CMIP   */
   type_of_application = CMIP_APP;
  app_handle = cmip_handle;
 }
else
  if( !memcmp(oid_string,ftam_oid,strlen(ftam_oid)) )
 {
/* it is FTAM   */
  type_of_application = FTAM_APP;
  app_handle = ftam_handle;
 }
else
 if( !memcmp(oid_string,MMS_oid,strlen(ftam_oid)) )
{
/* it is MMS   */
  type_of_application = MMS_APP;
  app_handle = mms_handle;
}
 else
{
     proto_tree_add_text(acse_tree,tvb,*offset,len1,"Unknown OID");
}
}

So I think we should leave presentation dissector as is and modify ACSE dissector for calling MMS dissector.
Guy, should I send it as patch ?
Regarding dissegmentation problem - I'm scrutinizing it right now.
As far as I can see problem is in CLNP/COTP dissector.I can't say where right now but I'm working.


From: "Herbert Falk" <herb@xxxxxxxxxxxx>
Reply-To: herb@xxxxxxxxxxxx
To: Sid Sid <ysidelnikov@xxxxxxxxxxx>
Subject: Re: [Ethereal-dev] Need help with protocol that spans multiple TVBs
Date: Mon, 12 Apr 2004 07:36:30 -0400

Sid Sid wrote:

Unfortunately, the session dissector does not do any reassembly at the present moment. I'm going to fix it in near future.Can you send the capture file with MMS pdus?
Can we see what you have changed in presentation dissector for MMS ?
Right now presentation dissector can only call ACSE dissector.

I fixed the problem of presentation only calling ACSE. I am attaching the trace files and the files that I have modified. The modifications were at CLNP, Transport (to add CLTP), Session (To add CL Sess), Presentation, and added MMS and modifications to ASN.1. I am sending my current CLNP, session, and presentation *.c files.

I have been able to trace the problem with Frame 67 down to the COTP reassembly in my CLNP module.

At line 942, the following code appears:


next_tvb = tvb_new_subset(tvb, offset, -1, -1);
 if (cotp_reassemble) {
   fragment_length = tvb_length(next_tvb);
   fd_head = fragment_add_seq_check(next_tvb, 0, pinfo, dst_ref,
                    cotp_segment_table,
                    cotp_reassembled_table,
                    tpdu_nr,
                    fragment_length, fragment);
   if (fd_head) {
     if (fd_head->next) {

The fragment_add_seq_check function is called for packet , but fd_head is returned as 0. This means that the packet will not be reassembled (I think) and thus the short TVB is passed up.

Refresh_MMS is the trace file that I have been working with.

Should I merge with the latest ethereal code?


From: Guy Harris <gharris@xxxxxxxxx>
Reply-To: ethereal-dev@xxxxxxxxxxxx
To: Herbert Falk <herb@xxxxxxxxxxxx>
CC: ethereal-dev@xxxxxxxxxxxx
Subject: Re: [Ethereal-dev] Need help with protocol that spans multiple TVBs
Date: Sun, 11 Apr 2004 17:59:36 -0700

On Sun, Apr 11, 2004 at 08:13:42PM -0400, Herbert Falk wrote:
> Guy Harris wrote:
> >Presumably you're using the Session and Presentation dissectors that
> >come with Ethereal?
> >
> Correct.  However, the problem appears to be at the session layer.
> Maybe I don't have a good understanding of how Ethereal is supposed to
> do reassembly.  I would have expected that the TVB, passed up would be
> a "large" buffer.  The MMS PDU is approximately 32K, however the TVB
> being passed in has a length (e.g. MMS data length) of 26 bytes. When I
> manually analyze the COTP packets, all of the appropriate MMS data is
> present.

If the COTP packet were reassembled (you have turned COTP reassembly on,
right?), the session dissector would be handed a tvbuff containing the
payload of the reassembled COTP PDU.  If not, the session dissector
might not do any reassembly, if it concludes that it doesn't have the
full packet.

Frame 66 doesn't have "(fragment)" in its Info column, which either
means that high-order bit in the TPDU number is set or that there's a
bug somewhere in the dissector; that might be part of the problem.

If the packet were reassembled at the session layer, the presentation
dissector would be handed a tvbuff containing the payload of the
reassembled session-layer PDU.

I'd have to see a capture with the problem - and perhaps any changes
you've made to the presentation-layer dissector to make it hand off its
payload to the MMS dissector - in order to figure out what's happening
and provide further assistance.  Perhaps somebody more familiar with
those dissectors would be able to figure the problem out without that.

_______________________________________________
Ethereal-dev mailing list
Ethereal-dev@xxxxxxxxxxxx
http://www.ethereal.com/mailman/listinfo/ethereal-dev


_________________________________________________________________
MSN 8 with e-mail virus protection service: 2 months FREE* http://join.msn.com/?page=features/virus




--
Herbert Falk
SISCO
6605 19-1/2 Mile Road
Sterling Heights, MI 48314
Ph: 586-254-0020
Fx:  586-254-0053

NOTICE: This communication may contain privileged or other confidential
information. If you are not the intended recipient, or believe that you
have  received this communication in error, please do not print, copy,
retransmit, disseminate, or otherwise use the information. Also, please indicate to the sender that you have received this communication in error, and delete the
copy you received. Thank you.


/* packet-clnp.c
 * Routines for ISO/OSI network and transport protocol packet disassembly
 *
 * $Id: packet-clnp.c,v 1.79 2003/11/16 23:17:17 guy Exp $
 * Laurent Deniel <laurent.deniel@xxxxxxx>
 * Ralf Schneider <Ralf.Schneider@xxxxxxxxxxx>
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 * 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
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <glib.h>
#include "prefs.h"
#include <epan/packet.h>
#include "reassemble.h"
#include "packet-osi.h"
#include "packet-osi-options.h"
#include "packet-isis.h"
#include "packet-esis.h"
#include "nlpid.h"

/* protocols and fields */

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 hf_clnp_id          = -1;
static int hf_clnp_length      = -1;
static int hf_clnp_version     = -1;
static int hf_clnp_ttl         = -1;
static int hf_clnp_type        = -1;
static int hf_clnp_pdu_length  = -1;
static int hf_clnp_checksum    = -1;
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 int hf_clnp_reassembled_in = -1;

static int  proto_cotp         = -1;
static gint ett_cotp           = -1;
static gint ett_cotp_segments  = -1;
static gint ett_cotp_segment   = -1;

static int hf_cotp_srcref      = -1;
static int hf_cotp_destref     = -1;
static int hf_cotp_type        = -1;
static int hf_cotp_segments    = -1;
static int hf_cotp_segment     = -1;
static int hf_cotp_segment_overlap = -1;
static int hf_cotp_segment_overlap_conflict = -1;
static int hf_cotp_segment_multiple_tails = -1;
static int hf_cotp_segment_too_long_segment = -1;
static int hf_cotp_segment_error = -1;
static int hf_cotp_reassembled_in = -1;

static int  proto_cltp         = -1;
static gint ett_cltp           = -1;

static int hf_cltp_type = -1;

static const fragment_items clnp_frag_items = {
	&ett_clnp_segment,
	&ett_clnp_segments,
	&hf_clnp_segments,
	&hf_clnp_segment,
	&hf_clnp_segment_overlap,
	&hf_clnp_segment_overlap_conflict,
	&hf_clnp_segment_multiple_tails,
	&hf_clnp_segment_too_long_segment,
	&hf_clnp_segment_error,
	&hf_clnp_reassembled_in,
	"segments"
};

static const fragment_items cotp_frag_items = {
	&ett_cotp_segment,
	&ett_cotp_segments,
	&hf_cotp_segments,
	&hf_cotp_segment,
	&hf_cotp_segment_overlap,
	&hf_cotp_segment_overlap_conflict,
	&hf_cotp_segment_multiple_tails,
	&hf_cotp_segment_too_long_segment,
	&hf_cotp_segment_error,
	&hf_cotp_reassembled_in,
	"segments"
};

static dissector_handle_t clnp_handle;
static dissector_handle_t data_handle;

/*
 * ISO 8473 OSI CLNP definition (see RFC994)
 *
 *            _________________________________
 *           |           Fixed Part            |
 *           |_________________________________|
 *           |          Address Part           |
 *           |_________________________________|
 *           |   Segmentation Part (optional)  |
 *           |_________________________________|
 *           |     Options Part (optional)     |
 *           |_________________________________|
 *           |         Data (optional)         |
 *           |_________________________________|
 */

#define	ISO8473_V1  0x01    /* CLNP version 1 */

/* Fixed part */

#define CNF_TYPE		0x1f
#define CNF_ERR_OK		0x20
#define CNF_MORE_SEGS		0x40
#define CNF_SEG_OK		0x80

#define DT_NPDU			0x1C
#define MD_NPDU			0x1D
#define ER_NPDU			0x01
#define ERQ_NPDU		0x1E
#define ERP_NPDU		0x1F

static const value_string npdu_type_abbrev_vals[] = {
  { DT_NPDU,	"DT" },
  { MD_NPDU,	"MD" },
  { ER_NPDU,	"ER" },
  { ERQ_NPDU,	"ERQ" },
  { ERP_NPDU,	"ERP" },
  { 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
#define P_CLNP_HDR_LEN		1
#define P_CLNP_VERS		2
#define P_CLNP_TTL		3
#define P_CLNP_TYPE		4
#define P_CLNP_SEGLEN		5
#define P_CLNP_CKSUM		7
#define P_CLNP_ADDRESS_PART	9

/* Segmentation part */

struct clnp_segment {
  gushort	cng_id;		/* data unit identifier */
  gushort	cng_off;	/* segment offset */
  gushort	cng_tot_len;	/* total length */
};

/* NSAP selector */

#define NSEL_NET 		0x00
#define NSEL_NP  		0x20
#define NSEL_TP  		0x21

/*
 * ISO8073 OSI COTP definition (see RFC905)
 */

/* don't use specific TPDU types to avoid alignment problems & copy overhead */

/* TPDU definition */

#define ED_TPDU        		0x1	/* COTP */
#define EA_TPDU        		0x2	/* COTP */
#define UD_TPDU        		0x4	/* CLTP */
#define RJ_TPDU        		0x5	/* COTP */
#define AK_TPDU        		0x6	/* COTP */
#define ER_TPDU        		0x7	/* COTP */
#define DR_TPDU        		0x8	/* COTP */
#define DC_TPDU        		0xC	/* COTP */
#define CC_TPDU        		0xD	/* COTP */
#define CR_TPDU        		0xE	/* COTP */
#define DT_TPDU        		0xF	/* COTP */

static const value_string cotp_tpdu_type_abbrev_vals[] = {
  { ED_TPDU,	"ED" },
  { EA_TPDU,	"EA" },
  { RJ_TPDU,	"RJ" },
  { AK_TPDU,	"AK" },
  { ER_TPDU,	"ER" },
  { DR_TPDU,	"DR" },
  { DC_TPDU,	"DC" },
  { CC_TPDU,	"CC" },
  { CR_TPDU,	"CR" },
  { DT_TPDU,	"DT" },
  { 0,		NULL }
};

static const value_string cltp_tpdu_type_abbrev_vals[] = {
  { UD_TPDU,	"UD" },
  { 0,		NULL }
};

/* field position */

#define P_LI           		0
#define P_TPDU         		1
#define P_CDT          		1
#define P_DST_REF      		2
#define P_SRC_REF      		4
#define P_TPDU_NR_0_1  		2
#define P_TPDU_NR_234  		4
#define P_VAR_PART_NDT 		5
#define P_VAR_PART_EDT 		8
#define P_VAR_PART_DC           6
#define P_CDT_IN_AK    		8
#define P_CDT_IN_RJ    		8
#define P_REJECT_ER    		4
#define P_REASON_IN_DR 		6
#define P_CLASS_OPTION 		6

/* TPDU length indicator */

#define LI_NORMAL_DT_CLASS_01		 2
#define LI_NORMAL_DT_WITH_CHECKSUM       8
#define LI_NORMAL_DT_WITHOUT_CHECKSUM    4
#define LI_EXTENDED_DT_WITH_CHECKSUM     11
#define LI_EXTENDED_DT_WITHOUT_CHECKSUM  7
#define LI_NORMAL_EA_WITH_CHECKSUM       8
#define LI_NORMAL_EA_WITHOUT_CHECKSUM    4
#define LI_EXTENDED_EA_WITH_CHECKSUM     11
#define LI_EXTENDED_EA_WITHOUT_CHECKSUM  7
#define LI_NORMAL_RJ                     4
#define LI_EXTENDED_RJ                   9
#define LI_MIN_DR                        6
#define LI_MAX_DC                        9
#define LI_MAX_AK                        27
#define LI_MAX_EA                        11
#define LI_MAX_ER			 8
/* XXX - can we always decide this based on whether the length
   indicator is odd or not?  What if the variable part has an odd
   number of octets? */
#define is_LI_NORMAL_AK(p)               ( ( p & 0x01 ) == 0 )

/* variant part */

#define VP_ACK_TIME     	0x85
#define VP_RES_ERROR    	0x86
#define VP_PRIORITY     	0x87
#define VP_TRANSIT_DEL  	0x88
#define VP_THROUGHPUT   	0x89
#define VP_SEQ_NR       	0x8A         /* in AK */
#define VP_REASSIGNMENT 	0x8B
#define VP_FLOW_CNTL    	0x8C         /* in AK */
#define VP_TPDU_SIZE    	0xC0
#define VP_SRC_TSAP     	0xC1         /* in CR/CC */
#define VP_DST_TSAP     	0xC2
#define VP_CHECKSUM     	0xC3
#define VP_VERSION_NR   	0xC4
#define VP_PROTECTION   	0xC5
#define VP_OPT_SEL      	0xC6
#define VP_PROTO_CLASS  	0xC7
#define VP_PREF_MAX_TPDU_SIZE  	0xF0
#define VP_INACTIVITY_TIMER  	0xF2

static const value_string tp_vpart_type_vals[] = {
  { VP_ACK_TIME,		"ack time" },
  { VP_RES_ERROR,		"res error" },
  { VP_PRIORITY,		"priority" },
  { VP_TRANSIT_DEL,		"transit delay" },
  { VP_THROUGHPUT,		"throughput" },
  { VP_SEQ_NR,			"seq number" },
  { VP_REASSIGNMENT,		"reassignment" },
  { VP_FLOW_CNTL,		"flow control" },
  { VP_TPDU_SIZE,		"tpdu-size" },
  { VP_SRC_TSAP,		"src-tsap" },
  { VP_DST_TSAP,		"dst-tsap" },
  { VP_CHECKSUM,		"checksum" },
  { VP_VERSION_NR,		"version" },
  { VP_PROTECTION,		"protection" },
  { VP_OPT_SEL,			"options" },
  { VP_PROTO_CLASS,		"proto class" },
  { VP_PREF_MAX_TPDU_SIZE,	"preferred max TPDU size" },
  { 0,				NULL }
};

/* misc */

#define EXTRACT_SHORT(p) 	pntohs(p)
#define EXTRACT_LONG(p) 	pntohl(p)

/* global variables */

/* List of dissectors to call for COTP packets put atop the Inactive
   Subset of CLNP. */
static heur_dissector_list_t cotp_is_heur_subdissector_list;
/* List of dissectors to call for COTP packets put atop CLNP */
static heur_dissector_list_t cotp_heur_subdissector_list;
/* List of dissectors to call for CLNP packets */
static heur_dissector_list_t clnp_heur_subdissector_list;

/*
 * Reassembly of CLNP.
 */
static GHashTable *clnp_segment_table = NULL;
static GHashTable *clnp_reassembled_table = NULL;

/*
 * Reassembly of COTP.
 */
static GHashTable *cotp_segment_table = NULL;
static GHashTable *cotp_reassembled_table = NULL;

/* options */
static guint tp_nsap_selector = NSEL_TP;
static gboolean always_decode_transport = FALSE;
static gboolean clnp_reassemble = FALSE;
static gboolean cotp_reassemble = FALSE;

/* function definitions */

#define MAX_TSAP_LEN	32
static gchar *print_tsap(const guchar *tsap, int length)
{

static gchar str[3][MAX_TSAP_LEN * 2 + 3]; /* TSAP in hex + '0x' + NULL */
  static gchar *cur;
  gchar tmp[3];
  gboolean allprintable;
  int i;

  if (cur == &str[0][0]) {
    cur = &str[1][0];
  } else if (cur == &str[1][0]) {
    cur = &str[2][0];
  } else {
    cur = &str[0][0];
  }


  cur[0] = '\0';
  if (length <= 0 || length > MAX_TSAP_LEN)
    sprintf(cur, "<unsupported TSAP length>");
  else {
    allprintable=TRUE;
    for (i=0;i<length;i++) {
	/* If any byte is not printable ASCII, display the TSAP as a
	   series of hex byte values rather than as a string; this
	   means that, for example, accented letters will cause it
	   to be displayed as hex, but it also means that byte values
	   such as 0xff and 0xfe, which *are* printable ISO 8859/x
	   characters, won't be treated as printable - 0xfffffffe
	   is probably binary, not text. */
	if (!(isascii(tsap[i]) && isprint(tsap[i]))) {
	  allprintable=FALSE;
	  break;
	  }
	}
    if (!allprintable){
      strcat(cur,"0x");
      }
    while (length != 0) {
      if (allprintable)
	sprintf(tmp, "%c", *tsap ++);
      else
	sprintf(tmp, "%02x", *tsap ++);
      strcat(cur, tmp);
      length --;
    }
  }
  return cur;

} /* print_tsap */

static gboolean ositp_decode_var_part(tvbuff_t *tvb, int offset,
				      int vp_length, int class_option,
				      proto_tree *tree)
{
  guint8  code, length;
  guint8  c1;
  guint16 s, s1,s2,s3,s4;
  guint32 t1, t2, t3, t4;
  guint32 pref_max_tpdu_size;

  while (vp_length != 0) {
    code = tvb_get_guint8(tvb, offset);
    proto_tree_add_text(tree, tvb, offset, 1,
		"Parameter code:   0x%02x (%s)",
			    code,
			    val_to_str(code, tp_vpart_type_vals, "Unknown"));
    offset += 1;
    vp_length -= 1;

    if (vp_length == 0)
      break;
    length = tvb_get_guint8(tvb, offset);
    proto_tree_add_text(tree, tvb, offset, 1,
		"Parameter length: %u", length);
    offset += 1;
    vp_length -= 1;

    switch (code) {

    case VP_ACK_TIME:
      s = tvb_get_ntohs(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, length,
			      "Ack time (ms): %u", s);
      offset += length;
      vp_length -= length;
      break;

    case VP_RES_ERROR:
      proto_tree_add_text(tree, tvb, offset, 1,
		"Residual error rate, target value: 10^%u",
		tvb_get_guint8(tvb, offset));
      offset += 1;
      length -= 1;
      vp_length -= 1;

      proto_tree_add_text(tree, tvb, offset, 1,
		"Residual error rate, minimum acceptable: 10^%u",
		tvb_get_guint8(tvb, offset));
      offset += 1;
      length -= 1;
      vp_length -= 1;


      proto_tree_add_text(tree, tvb, offset, 1,
		"Residual error rate, TSDU size of interest: %u",
		1<<tvb_get_guint8(tvb, offset));
      offset += 1;
      length -= 1;
      vp_length -= 1;

      break;

    case VP_PRIORITY:
      s = tvb_get_ntohs(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, length,
		"Priority: %u", s);
      offset += length;
      vp_length -= length;
      break;

    case VP_TRANSIT_DEL:
      s1 = tvb_get_ntohs(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, 2,
		"Transit delay, target value, calling-called: %u ms", s1);
      offset += 2;
      length -= 2;
      vp_length -= 2;

      s2 = tvb_get_ntohs(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, 2,
		"Transit delay, maximum acceptable, calling-called: %u ms", s2);
      offset += 2;
      length -= 2;
      vp_length -= 2;

      s3 = tvb_get_ntohs(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, 2,
		"Transit delay, target value, called-calling: %u ms", s3);
      offset += 2;
      length -= 2;
      vp_length -= 2;

      s4 = tvb_get_ntohs(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, 2,
		"Transit delay, maximum acceptable, called-calling: %u ms", s4);
      offset += 2;
      length -= 2;
      vp_length -= 2;
      break;

    case VP_THROUGHPUT:
      t1 = tvb_get_ntoh24(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, 3,
		"Maximum throughput, target value, calling-called:       %u o/s", t1);
      offset += 3;
      length -= 3;
      vp_length -= 3;

      t2 = tvb_get_ntoh24(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, 3,
		"Maximum throughput, minimum acceptable, calling-called: %u o/s", t2);
      offset += 3;
      length -= 3;
      vp_length -= 3;

      t3 = tvb_get_ntoh24(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, 3,
		"Maximum throughput, target value, called-calling:       %u o/s", t3);
      offset += 3;
      length -= 3;
      vp_length -= 3;

      t4 = tvb_get_ntoh24(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, 3,
		"Maximum throughput, minimum acceptable, called-calling: %u o/s", t4);
      offset += 3;
      length -= 3;
      vp_length -= 3;

      if (length != 0) {	/* XXX - should be 0 or 12 */
	t1 = tvb_get_ntoh24(tvb, offset);
	proto_tree_add_text(tree, tvb, offset, 3,
		"Average throughput, target value, calling-called:       %u o/s", t1);
	offset += 3;
	length -= 3;
	vp_length -= 3;

	t2 = tvb_get_ntoh24(tvb, offset);
	proto_tree_add_text(tree, tvb, offset, 3,
		"Average throughput, minimum acceptable, calling-called: %u o/s", t2);
	offset += 3;
	length -= 3;
	vp_length -= 3;

	t3 = tvb_get_ntoh24(tvb, offset);
	proto_tree_add_text(tree, tvb, offset, 3,
		"Average throughput, target value, called-calling:       %u o/s", t3);
	offset += 3;
	length -= 3;
	vp_length -= 3;

	t4 = tvb_get_ntoh24(tvb, offset);
	proto_tree_add_text(tree, tvb, offset, 3,
		"Average throughput, minimum acceptable, called-calling: %u o/s", t4);
	offset += 3;
	length -= 3;
	vp_length -= 3;
      }
      break;

    case VP_SEQ_NR:
      proto_tree_add_text(tree, tvb, offset, 2,
		"Sequence number: 0x%04x", tvb_get_ntohs(tvb, offset));
      offset += length;
      vp_length -= length;
      break;

    case VP_REASSIGNMENT:
      proto_tree_add_text(tree, tvb, offset, 2,
		"Reassignment time: %u secs", tvb_get_ntohs(tvb, offset));
      offset += length;
      vp_length -= length;
      break;

    case VP_FLOW_CNTL:
      proto_tree_add_text(tree, tvb, offset, 4,
		"Lower window edge: 0x%08x", tvb_get_ntohl(tvb, offset));
      offset += 4;
      length -= 4;
      vp_length -= 4;

      proto_tree_add_text(tree, tvb, offset, 2,
		"Sequence number: 0x%04x", tvb_get_ntohs(tvb, offset));
      offset += 2;
      length -= 2;
      vp_length -= 2;

      proto_tree_add_text(tree, tvb, offset, 2,
		"Credit: 0x%04x", tvb_get_ntohs(tvb, offset));
      offset += 2;
      length -= 2;
      vp_length -= 2;

      break;

    case VP_TPDU_SIZE:
      c1 = tvb_get_guint8(tvb, offset) & 0x0F;
      proto_tree_add_text(tree, tvb, offset, length,
		"TPDU size: %u", 1 << c1);
      offset += length;
      vp_length -= length;
      break;

    case VP_SRC_TSAP:
      proto_tree_add_text(tree, tvb, offset, length,
		"Calling TSAP: %s",
		print_tsap(tvb_get_ptr(tvb, offset, length), length));
      offset += length;
      vp_length -= length;
      break;

    case VP_DST_TSAP:
      proto_tree_add_text(tree, tvb, offset, length,
		"Called TSAP: %s",
		print_tsap(tvb_get_ptr(tvb, offset, length), length));
      offset += length;
      vp_length -= length;
      break;

    case VP_CHECKSUM:
      proto_tree_add_text(tree, tvb, offset, length,
		"Checksum: 0x%04x", tvb_get_ntohs(tvb, offset));
      offset += length;
      vp_length -= length;
      break;

    case VP_VERSION_NR:
      c1 = tvb_get_guint8(tvb, offset);
      proto_tree_add_text(tree, tvb, offset, length,
		"Version: %u", c1);
      offset += length;
      vp_length -= length;
      break;

    case VP_OPT_SEL:
      c1 = tvb_get_guint8(tvb, offset) & 0x0F;
      switch (class_option) {

      case 1:
	if (c1 & 0x8)
	  proto_tree_add_text(tree, tvb, offset, 1,
				  "Use of network expedited data");
	else
	  proto_tree_add_text(tree, tvb, offset, 1,
				  "Non use of network expedited data");
	if (c1 & 0x4)
	  proto_tree_add_text(tree, tvb, offset, 1,
				  "Use of Receipt confirmation");
	else
	  proto_tree_add_text(tree, tvb, offset, 1,
				  "Use of explicit AK variant");
	break;

      case 4:
	if (c1 & 0x2)
	  proto_tree_add_text(tree, tvb, offset, 1,
				  "Non-use 16 bit checksum in class 4");
	else
	  proto_tree_add_text(tree, tvb, offset, 1,
				  "Use 16 bit checksum ");
	break;
      }
      if (c1 & 0x1)
	proto_tree_add_text(tree, tvb, offset, 1,
				"Use of transport expedited data transfer");
      else
	proto_tree_add_text(tree, tvb, offset, 1,
				"Non-use of transport expedited data transfer");
      offset += length;
      vp_length -= length;
      break;

    case VP_PREF_MAX_TPDU_SIZE:
      switch (length) {

      case 1:
        pref_max_tpdu_size = tvb_get_guint8(tvb, offset);
        break;

      case 2:
        pref_max_tpdu_size = tvb_get_ntohs(tvb, offset);
        break;

      case 3:
	pref_max_tpdu_size = tvb_get_ntoh24(tvb, offset);
	break;

      case 4:
        pref_max_tpdu_size = tvb_get_ntohl(tvb, offset);
        break;

      default:
        proto_tree_add_text(tree, tvb, offset, length,
		"Preferred maximum TPDU size: bogus length %u (not 1, 2, 3, or 4)",
		length);
	return FALSE;
      }
      proto_tree_add_text(tree, tvb, offset, length,
		"Preferred maximum TPDU size: %u", pref_max_tpdu_size*128);
      offset += length;
      vp_length -= length;
      break;

    case VP_INACTIVITY_TIMER:
      proto_tree_add_text(tree, tvb, offset, length,
		"Inactivity timer: %u ms", tvb_get_ntohl(tvb, offset));
      offset += length;
      vp_length -= length;
      break;

    case VP_PROTECTION:           /* user-defined */
    case VP_PROTO_CLASS:          /* todo */
    default:			  /* unknown, no decoding */
      proto_tree_add_text(tree, tvb, offset, length,
			      "Parameter value: <not shown>");
      offset += length;
      vp_length -= length;
      break;
    }
  } /* while */

  return TRUE;
}

static int ositp_decode_DR(tvbuff_t *tvb, int offset, guint8 li, guint8 tpdu,
			 packet_info *pinfo, proto_tree *tree)
{
  proto_tree *cotp_tree;
  proto_item *ti;
  guint16 dst_ref, src_ref;
  guchar  reason;
  char *str;

  if (li < LI_MIN_DR)
    return -1;

  dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);

  src_ref = tvb_get_ntohs(tvb, offset + P_SRC_REF);

  reason  = tvb_get_guint8(tvb, offset + P_REASON_IN_DR);

  pinfo->srcport = src_ref;
  pinfo->destport = dst_ref;
  switch(reason) {
    case (128+0): str = "Normal Disconnect"; break;
    case (128+1): str = "Remote transport entity congestion"; break;
    case (128+2): str = "Connection negotiation failed"; break;
    case (128+3): str = "Duplicate source reference"; break;
    case (128+4): str = "Mismatched references"; break;
    case (128+5): str = "Protocol error"; break;
    case (128+7): str = "Reference overflow"; break;
    case (128+8): str = "Connection requestion refused"; break;
    case (128+10):str = "Header or parameter length invalid"; break;
    case (0):     str = "Reason not specified"; break;
    case (1):     str = "Congestion at TSAP"; break;
    case (2):     str = "Session entity not attached to TSAP"; break;
    case (3):     str = "Address unknown"; break;
    default:      return -1;
      /*NOTREACHED*/
      break;
  }

  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);

  if (tree) {
ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
    cotp_tree = proto_item_add_subtree(ti, ett_cotp);
    proto_tree_add_text(cotp_tree, tvb, offset,      1,
			"Length indicator: %u", li);
proto_tree_add_uint_format(cotp_tree, hf_cotp_type, tvb, offset + 1, 1, tpdu,
			       "TPDU code: 0x%x (DR)", tpdu);
proto_tree_add_uint(cotp_tree, hf_cotp_destref, tvb, offset + 2, 2, dst_ref); proto_tree_add_uint(cotp_tree, hf_cotp_srcref, tvb, offset + 4, 2, src_ref);
    proto_tree_add_text(cotp_tree, tvb, offset +  6, 1,
			"Cause: %s", str);
  }

  offset += li + 1;

  /* User data */
call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
  offset += tvb_length_remaining(tvb, offset);
     /* we dissected all of the containing PDU */

  return offset;

} /* ositp_decode_DR */

static int ositp_decode_DT(tvbuff_t *tvb, int offset, guint8 li, guint8 tpdu,
			 packet_info *pinfo, proto_tree *tree,
			 gboolean uses_inactive_subset,
			 gboolean *subdissector_found)
{
  proto_tree *cotp_tree = NULL;
  proto_item *ti;
  gboolean is_extended;
  gboolean is_class_234;
  guint16  dst_ref;
  guint    tpdu_nr;
  guint    fragment = 0;
  guint32  fragment_length = 0;
  tvbuff_t *next_tvb;
  tvbuff_t *reassembled_tvb = NULL;
  fragment_data *fd_head;

  /* VP_CHECKSUM is the only parameter allowed in the variable part.
     (This means we may misdissect this if the packet is bad and
     contains other parameters.) */
  switch (li) {

    case LI_NORMAL_DT_WITH_CHECKSUM      :
      if (tvb_get_guint8(tvb, offset + P_VAR_PART_NDT) != VP_CHECKSUM)
	return -1;
      /* FALLTHROUGH */

    case LI_NORMAL_DT_WITHOUT_CHECKSUM   :
      tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);
      if ( tpdu_nr & 0x80 )
	tpdu_nr = tpdu_nr & 0x7F;
      else
	fragment = 1;
      is_extended = FALSE;
      is_class_234 = TRUE;
      dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);
      break;

    case LI_EXTENDED_DT_WITH_CHECKSUM    :
      if (tvb_get_guint8(tvb, offset + P_VAR_PART_EDT) != VP_CHECKSUM)
	return -1;
      /* FALLTHROUGH */

    case LI_EXTENDED_DT_WITHOUT_CHECKSUM :
      tpdu_nr = tvb_get_ntohl(tvb, offset + P_TPDU_NR_234);
      if ( tpdu_nr & 0x80000000 )
	tpdu_nr = tpdu_nr & 0x7FFFFFFF;
      else
	fragment = 1;
      is_extended = TRUE;
      is_class_234 = TRUE;
      dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);
      break;

    case LI_NORMAL_DT_CLASS_01           :
      tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_0_1);
      if ( tpdu_nr & 0x80 )
	tpdu_nr = tpdu_nr & 0x7F;
      else
	fragment = 1;
      is_extended = FALSE;
      is_class_234 = FALSE;
      dst_ref = 0;
      break;

    default : /* bad TPDU */
      return -1;
      /*NOTREACHED*/
      break;
  }

  pinfo->destport = dst_ref;
  pinfo->srcport = 0;
  pinfo->fragmented = fragment;
  if (check_col(pinfo->cinfo, COL_INFO)) {
    if (is_class_234) {
col_append_fstr(pinfo->cinfo, COL_INFO, "DT TPDU (%u) dst-ref: 0x%04x %s",
		 tpdu_nr,
		 dst_ref,
		 (fragment)? "(fragment)" : "");
    } else {
      col_append_fstr(pinfo->cinfo, COL_INFO, "DT TPDU (%u) %s",
		 tpdu_nr,
		 (fragment)? "(fragment)" : "");
    }
  }

  if (tree) {
ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
    cotp_tree = proto_item_add_subtree(ti, ett_cotp);
    proto_tree_add_text(cotp_tree, tvb, offset, 1,
			"Length indicator: %u", li);
  }
  offset += 1;

  if (tree) {
proto_tree_add_uint_format(cotp_tree, hf_cotp_type, tvb, offset, 1, tpdu,
			"TPDU code: 0x%x (DT)", tpdu);
  }
  offset += 1;
  li -= 1;

  if (is_class_234) {
    if (tree)
proto_tree_add_uint(cotp_tree, hf_cotp_destref, tvb, offset, 2, dst_ref);
    offset += 2;
    li -= 2;
  }

  if (is_extended) {
    if (tree) {
      proto_tree_add_text(cotp_tree, tvb, offset, 4,
			    "TPDU number: 0x%08x (%s)",
			    tpdu_nr,
			    (fragment)? "fragment":"complete");
    }
    offset += 4;
    li -= 4;
  } else {
    if (tree) {
      proto_tree_add_text(cotp_tree, tvb, offset, 1,
			    "TPDU number: 0x%02x (%s)",
			    tpdu_nr,
			    (fragment)? "fragment":"complete");
    }
    offset += 1;
    li -= 1;
  }

  if (tree)
    ositp_decode_var_part(tvb, offset, li, 4, cotp_tree);
  offset += li;

  next_tvb = tvb_new_subset(tvb, offset, -1, -1);
  if (cotp_reassemble) {
    fragment_length = tvb_length(next_tvb);
    fd_head = fragment_add_seq_check(next_tvb, 0, pinfo, dst_ref,
				     cotp_segment_table,
				     cotp_reassembled_table,
				     tpdu_nr,
				     fragment_length, fragment);
    if (fd_head) {
      if (fd_head->next) {
	/* This is the last packet */
	reassembled_tvb = tvb_new_real_data(fd_head->data,
					    fd_head->len,
					    fd_head->len);
	tvb_set_child_real_data_tvbuff(next_tvb, reassembled_tvb);
	add_new_data_source(pinfo, reassembled_tvb, "Reassembled COTP");

	show_fragment_seq_tree(fd_head,
			       &cotp_frag_items,
			       cotp_tree,
			       pinfo, reassembled_tvb);
	pinfo->fragmented = fragment;
	next_tvb = reassembled_tvb;
      }
    }
    if (fragment && reassembled_tvb == NULL) {
      proto_tree_add_text(cotp_tree, tvb, offset, -1,
			  "User data (%u byte%s)", fragment_length,
			  plurality(fragment_length, "", "s"));
    }

  }

  if (uses_inactive_subset) {
    if (dissector_try_heuristic(cotp_is_heur_subdissector_list, next_tvb,
				pinfo, tree)) {
      *subdissector_found = TRUE;
    } else {
      /* Fill in other Dissectors using inactive subset here */
      call_dissector(data_handle,next_tvb, pinfo, tree);
    }
  } else {
    /*
     * We dissect payload if one of the following is TRUE:
     *
     * - Reassembly option for COTP in preferences is unchecked
     * - Reassembly option is checked and this packet is the last fragment
     */
    if ( (!cotp_reassemble) ||
	 ((cotp_reassemble) && (!fragment))) {
      if (dissector_try_heuristic(cotp_heur_subdissector_list, next_tvb,
				  pinfo, tree)) {
        *subdissector_found = TRUE;
      } else {
        call_dissector(data_handle,next_tvb, pinfo, tree);
      }
    }
  }

  offset += tvb_length_remaining(tvb, offset);
     /* we dissected all of the containing PDU */

  return offset;

} /* ositp_decode_DT */

static int ositp_decode_ED(tvbuff_t *tvb, int offset, guint8 li, guint8 tpdu,
			 packet_info *pinfo, proto_tree *tree)
{
  proto_tree *cotp_tree = NULL;
  proto_item *ti;
  gboolean is_extended;
  guint16  dst_ref;
  guint    tpdu_nr;
  tvbuff_t *next_tvb;

  /* ED TPDUs are never fragmented */

  /* VP_CHECKSUM is the only parameter allowed in the variable part.
     (This means we may misdissect this if the packet is bad and
     contains other parameters.) */
  switch (li) {

    case LI_NORMAL_DT_WITH_CHECKSUM      :
      if (tvb_get_guint8(tvb, offset + P_VAR_PART_NDT) != VP_CHECKSUM)
	return -1;
      /* FALLTHROUGH */

    case LI_NORMAL_DT_WITHOUT_CHECKSUM   :
      tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);
      if ( tpdu_nr & 0x80 )
	tpdu_nr = tpdu_nr & 0x7F;
      else
	return -1;
      is_extended = FALSE;
      break;

    case LI_EXTENDED_DT_WITH_CHECKSUM    :
      if (tvb_get_guint8(tvb, offset + P_VAR_PART_EDT) != VP_CHECKSUM)
	return -1;
      /* FALLTHROUGH */

    case LI_EXTENDED_DT_WITHOUT_CHECKSUM :
      tpdu_nr = tvb_get_ntohl(tvb, offset + P_TPDU_NR_234);
      if ( tpdu_nr & 0x80000000 )
	tpdu_nr = tpdu_nr & 0x7FFFFFFF;
      else
	return -1;
      is_extended = TRUE;
      break;

    default : /* bad TPDU */
      return -1;
      /*NOTREACHED*/
      break;
  } /* li */

  dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);

  pinfo->destport = dst_ref;
  pinfo->srcport = 0;
  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) {
ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
    cotp_tree = proto_item_add_subtree(ti, ett_cotp);
    proto_tree_add_text(cotp_tree, tvb, offset, 1,
			"Length indicator: %u", li);
  }
  offset += 1;

  if (tree) {
proto_tree_add_uint_format(cotp_tree, hf_cotp_type, tvb, offset, 1, tpdu,
			"TPDU code: 0x%x (ED)", tpdu);
  }
  offset += 1;
  li -= 1;

  if (tree)
proto_tree_add_uint(cotp_tree, hf_cotp_destref, tvb, offset, 2, dst_ref);
  offset += 2;
  li -= 2;

  if (is_extended) {
    if (tree) {
      proto_tree_add_text(cotp_tree, tvb, offset, 4,
			    "TPDU number: 0x%02x", tpdu_nr);
    }
    offset += 4;
    li -= 4;
  } else {
    if (tree) {
      proto_tree_add_text(cotp_tree, tvb, offset, 1,
			    "TPDU number: 0x%02x", tpdu_nr);
    }
    offset += 1;
    li -= 1;
  }

  if (tree)
    ositp_decode_var_part(tvb, offset, li, 4, cotp_tree);
  offset += li;

  next_tvb = tvb_new_subset(tvb, offset, -1, -1);
  call_dissector(data_handle,next_tvb, pinfo, tree);

  offset += tvb_length_remaining(tvb, offset);
     /* we dissected all of the containing PDU */

  return offset;

} /* ositp_decode_ED */

static int ositp_decode_RJ(tvbuff_t *tvb, int offset, guint8 li, guint8 tpdu,
			 guint8 cdt, packet_info *pinfo, proto_tree *tree)
{
  proto_tree *cotp_tree;
  proto_item *ti;
  guint16  dst_ref;
  guint    tpdu_nr;
  gushort  credit = 0;

  switch(li) {
    case LI_NORMAL_RJ   :
      tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);
      break;
    case LI_EXTENDED_RJ :
      tpdu_nr = tvb_get_ntohl(tvb, offset + P_TPDU_NR_234);
      credit = tvb_get_ntohs(tvb, offset + P_CDT_IN_RJ);
      break;
    default :
      return -1;
      /*NOTREACHED*/
      break;
  }

  dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);

  pinfo->destport = dst_ref;
  pinfo->srcport = 0;
  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) {
ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
    cotp_tree = proto_item_add_subtree(ti, ett_cotp);
    proto_tree_add_text(cotp_tree, tvb, offset,      1,
			"Length indicator: %u", li);
proto_tree_add_uint_format(cotp_tree, hf_cotp_type, tvb, offset + 1, 1, tpdu,
			"TPDU code: 0x%x (RJ)", tpdu);
    if (li == LI_NORMAL_RJ)
      proto_tree_add_text(cotp_tree, tvb, offset +  1, 1,
			  "Credit: %u", cdt);
proto_tree_add_uint(cotp_tree, hf_cotp_destref, tvb, offset + 2, 2, dst_ref);
    if (li == LI_NORMAL_RJ)
      proto_tree_add_text(cotp_tree, tvb, offset +  4, 1,
			  "Your TPDU number: 0x%02x", tpdu_nr);
    else {
      proto_tree_add_text(cotp_tree, tvb, offset +  4, 4,
			  "Your TPDU number: 0x%02x", tpdu_nr);
      proto_tree_add_text(cotp_tree, tvb, offset +  8, 2,
			  "Credit: 0x%02x", credit);
    }
  }

  offset += li + 1;

  return offset;

} /* ositp_decode_RJ */

static int ositp_decode_CC(tvbuff_t *tvb, int offset, guint8 li, guint8 tpdu,
			 packet_info *pinfo, proto_tree *tree,
			 gboolean uses_inactive_subset,
			 gboolean *subdissector_found)
{

  /* CC & CR decoding in the same function */

  proto_tree *cotp_tree = NULL;
  proto_item *ti;
  guint16 dst_ref, src_ref;
  guchar  class_option;
  tvbuff_t *next_tvb;

  src_ref = tvb_get_ntohs(tvb, offset + P_SRC_REF);

class_option = (tvb_get_guint8(tvb, offset + P_CLASS_OPTION) >> 4 ) & 0x0F;
  if (class_option > 4)
    return -1;

  dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);
  pinfo->srcport = src_ref;
  pinfo->destport = dst_ref;
  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,
		 dst_ref);

  if (tree) {
ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
    cotp_tree = proto_item_add_subtree(ti, ett_cotp);
    proto_tree_add_text(cotp_tree, tvb, offset, 1,
			"Length indicator: %u", li);
  }
  offset += 1;

  if (tree) {
proto_tree_add_uint_format(cotp_tree, hf_cotp_type, tvb, offset, 1, tpdu,
			"TPDU code: 0x%x (%s)", tpdu,
			(tpdu == CR_TPDU) ? "CR" : "CC");
  }
  offset += 1;
  li -= 1;

  if (tree)
proto_tree_add_uint(cotp_tree, hf_cotp_destref, tvb, offset, 2, dst_ref);
  offset += 2;
  li -= 2;

  if (tree)
proto_tree_add_uint(cotp_tree, hf_cotp_srcref, tvb, offset, 2, src_ref);
  offset += 2;
  li -= 2;

  if (tree) {
    proto_tree_add_text(cotp_tree, tvb, offset, 1,
			"Class option: 0x%02x", class_option);
  }
  offset += 1;
  li -= 1;

  if (tree)
    ositp_decode_var_part(tvb, offset, li, class_option, cotp_tree);
  offset += li;

  next_tvb = tvb_new_subset(tvb, offset, -1, -1);
  if (!uses_inactive_subset){
    if (dissector_try_heuristic(cotp_heur_subdissector_list, next_tvb,
				pinfo, tree)) {
      *subdissector_found = TRUE;
    } else {
      call_dissector(data_handle,next_tvb, pinfo, tree);
    }
  }
  else
    call_dissector(data_handle, next_tvb, pinfo, tree);
  offset += tvb_length_remaining(tvb, offset);
     /* we dissected all of the containing PDU */

  return offset;

} /* ositp_decode_CC */

static int ositp_decode_DC(tvbuff_t *tvb, int offset, guint8 li, guint8 tpdu,
			 packet_info *pinfo, proto_tree *tree)
{
  proto_tree *cotp_tree = NULL;
  proto_item *ti;
  guint16 dst_ref, src_ref;

  if (li > LI_MAX_DC)
    return -1;

  dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);
  src_ref = tvb_get_ntohs(tvb, offset + P_SRC_REF);

  pinfo->srcport = src_ref;
  pinfo->destport = dst_ref;
  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);

  if (tree) {
ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
    cotp_tree = proto_item_add_subtree(ti, ett_cotp);
    proto_tree_add_text(cotp_tree, tvb, offset, 1,
			"Length indicator: %u", li);
  }
  offset += 1;

  if (tree) {
proto_tree_add_uint_format(cotp_tree, hf_cotp_type, tvb, offset, 1, tpdu,
			"TPDU code: 0x%x (DC)", tpdu);
  }
  offset += 1;
  li -= 1;

  if (tree)
proto_tree_add_uint(cotp_tree, hf_cotp_destref, tvb, offset, 2, dst_ref);
  offset += 2;
  li -= 2;

  if (tree)
proto_tree_add_uint(cotp_tree, hf_cotp_srcref, tvb, offset, 2, src_ref);
  offset += 2;
  li -= 2;

  if (tree)
    ositp_decode_var_part(tvb, offset, li, 4, cotp_tree);
  offset += li;

  return offset;

} /* ositp_decode_DC */

static int ositp_decode_AK(tvbuff_t *tvb, int offset, guint8 li, guint8 tpdu,
			 guint8 cdt, packet_info *pinfo, proto_tree *tree)
{
  proto_tree *cotp_tree = NULL;
  proto_item *ti;
  guint16    dst_ref;
  guint      tpdu_nr;
  gushort    cdt_in_ak;

  if (li > LI_MAX_AK)
    return -1;

  if (is_LI_NORMAL_AK(li)) {

    dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);
    tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);

    pinfo->srcport = 0;
    pinfo->destport = dst_ref;
    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) {
ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
      cotp_tree = proto_item_add_subtree(ti, ett_cotp);
      proto_tree_add_text(cotp_tree, tvb, offset, 1,
			  "Length indicator: %u", li);
    }
    offset += 1;

    if (tree) {
proto_tree_add_uint_format(cotp_tree, hf_cotp_type, tvb, offset, 1, tpdu,
			  "TPDU code: 0x%x (AK)", tpdu);
      proto_tree_add_text(cotp_tree, tvb, offset, 1,
			  "Credit: %u", cdt);
    }
    offset += 1;
    li -= 1;

    if (tree)
proto_tree_add_uint(cotp_tree, hf_cotp_destref, tvb, offset, 2, dst_ref);
    offset += 2;
    li -= 2;

    if (tree) {
      proto_tree_add_text(cotp_tree, tvb, offset, 1,
			  "Your TPDU number: 0x%02x", tpdu_nr);
    }
    offset += 1;
    li -= 1;

    if (tree)
      ositp_decode_var_part(tvb, offset, li, 4, cotp_tree);
    offset += li;

  } else { /* extended format */

    dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);
    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->cinfo, COL_INFO))
col_append_fstr(pinfo->cinfo, COL_INFO, "AK TPDU (%u) dst-ref: 0x%04x",
		   tpdu_nr, dst_ref);

    if (tree) {
ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
      cotp_tree = proto_item_add_subtree(ti, ett_cotp);
      proto_tree_add_text(cotp_tree, tvb, offset, 1,
			  "Length indicator: %u", li);
    }
    offset += 1;

    if (tree) {
proto_tree_add_uint_format(cotp_tree, hf_cotp_type, tvb, offset, 1, tpdu,
			  "TPDU code: 0x%x (AK)", tpdu);
    }
    offset += 1;
    li -= 1;

    if (tree)
proto_tree_add_uint(cotp_tree, hf_cotp_destref, tvb, offset, 2, dst_ref);
    offset += 2;
    li -= 2;

    if (tree) {
      proto_tree_add_text(cotp_tree, tvb, offset, 4,
			  "Your TPDU number: 0x%08x", tpdu_nr);
    }
    offset += 4;
    li -= 4;

    if (tree) {
      proto_tree_add_text(cotp_tree, tvb, offset, 2,
			  "Credit: 0x%04x", cdt_in_ak);
    }
    offset += 2;
    li -= 2;

    if (tree)
      ositp_decode_var_part(tvb, offset, li, 4, cotp_tree);
    offset += li;

  } /* is_LI_NORMAL_AK */

  return offset;

} /* ositp_decode_AK */

static int ositp_decode_EA(tvbuff_t *tvb, int offset, guint8 li, guint8 tpdu,
			 packet_info *pinfo, proto_tree *tree)
{
  proto_tree *cotp_tree = NULL;
  proto_item *ti;
  gboolean is_extended;
  guint16  dst_ref;
  guint    tpdu_nr;

  if (li > LI_MAX_EA)
    return -1;

  /* VP_CHECKSUM is the only parameter allowed in the variable part.
     (This means we may misdissect this if the packet is bad and
     contains other parameters.) */
  switch (li) {

    case LI_NORMAL_EA_WITH_CHECKSUM      :
      if (tvb_get_guint8(tvb, offset + P_VAR_PART_NDT) != VP_CHECKSUM ||
		tvb_get_guint8(tvb, offset + P_VAR_PART_NDT + 1) != 2)
	return -1;
      /* FALLTHROUGH */

    case LI_NORMAL_EA_WITHOUT_CHECKSUM   :
      tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);
      is_extended = FALSE;
      break;

    case LI_EXTENDED_EA_WITH_CHECKSUM    :
      if (tvb_get_guint8(tvb, offset + P_VAR_PART_EDT) != VP_CHECKSUM ||
		tvb_get_guint8(tvb, offset + P_VAR_PART_EDT + 1) != 2)
	return -1;
      /* FALLTHROUGH */

    case LI_EXTENDED_EA_WITHOUT_CHECKSUM :
      tpdu_nr = tvb_get_ntohl(tvb, offset + P_TPDU_NR_234);
      is_extended = TRUE;
      break;

    default : /* bad TPDU */
      return -1;
      /*NOTREACHED*/
      break;
  } /* li */

  dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);
  pinfo->srcport = 0;
  pinfo->destport = dst_ref;
  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) {
ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
    cotp_tree = proto_item_add_subtree(ti, ett_cotp);
    proto_tree_add_text(cotp_tree, tvb, offset, 1,
			"Length indicator: %u", li);
  }
  offset += 1;

  if (tree) {
proto_tree_add_uint_format(cotp_tree, hf_cotp_type, tvb, offset, 1, tpdu,
			"TPDU code: 0x%x (EA)", tpdu);
  }
  offset += 1;
  li -= 1;

  if (tree)
proto_tree_add_uint(cotp_tree, hf_cotp_destref, tvb, offset, 2, dst_ref);
  offset += 2;
  li -= 2;

  if (is_extended) {
    if (tree) {
      proto_tree_add_text(cotp_tree, tvb, offset, 4,
			    "Your TPDU number: 0x%08x", tpdu_nr);
    }
    offset += 4;
    li -= 4;
  } else {
    if (tree) {
      proto_tree_add_text(cotp_tree, tvb, offset, 1,
			    "Your TPDU number: 0x%02x", tpdu_nr);
    }
    offset += 1;
    li -= 1;
  }

  if (tree)
    ositp_decode_var_part(tvb, offset, li, 4, cotp_tree);
  offset += li;

  return offset;

} /* ositp_decode_EA */

static int ositp_decode_ER(tvbuff_t *tvb, int offset, guint8 li, guint8 tpdu,
			 packet_info *pinfo, proto_tree *tree)
{
  proto_tree *cotp_tree;
  proto_item *ti;
  guchar *str;
  guint16 dst_ref;

  if (li > LI_MAX_ER)
    return -1;

  switch(tvb_get_guint8(tvb, offset + P_REJECT_ER)) {
    case 0 :
      str = "Reason not specified";
      break;
    case 1 :
      str = "Invalid parameter code";
      break;
    case 2 :
      str = "Invalid TPDU type";
      break;
    case 3 :
      str = "Invalid parameter value";
      break;
    default:
      return -1;
      /*NOTREACHED*/
      break;
  }

  dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);
  pinfo->srcport = 0;
  pinfo->destport = 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);
    cotp_tree = proto_item_add_subtree(ti, ett_cotp);
    proto_tree_add_text(cotp_tree, tvb, offset,      1,
			"Length indicator: %u", li);
proto_tree_add_uint_format(cotp_tree, hf_cotp_type, tvb, offset + 1, 1, tpdu,
			"TPDU code: 0x%x (ER)", tpdu);
proto_tree_add_uint(cotp_tree, hf_cotp_destref, tvb, offset + 2, 2, dst_ref);
    proto_tree_add_text(cotp_tree, tvb, offset +  4, 1,
			"Reject cause: %s", str);
  }

  offset += li + 1;

  return offset;

} /* ositp_decode_ER */

static int ositp_decode_UD(tvbuff_t *tvb, int offset, guint8 li, guint8 tpdu,
			 packet_info *pinfo, proto_tree *tree)
{
  proto_item *ti;
  proto_tree *cltp_tree = NULL;
  tvbuff_t   *next_tvb;

  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);
    cltp_tree = proto_item_add_subtree(ti, ett_cltp);
    proto_tree_add_text(cltp_tree, tvb, offset, 1,
			"Length indicator: %u", li);
  }
  offset += 1;

  if (tree) {
proto_tree_add_uint_format(cltp_tree, hf_cltp_type, tvb, offset, 1, tpdu,
			"TPDU code: 0x%x (UD)", tpdu);
  }
  offset += 1;
  li -= 1;

  if (tree)
    ositp_decode_var_part(tvb, offset, li, 0, cltp_tree);
  offset += li;

  next_tvb = tvb_new_subset(tvb, offset, -1, -1);
/* added for IEC GSSE		*/
   if (!(dissector_try_heuristic(cotp_is_heur_subdissector_list, next_tvb,
				pinfo, tree))) {
      /* Fill in other Dissectors using inactive subset here */
      call_dissector(data_handle,next_tvb, pinfo, tree);
   }
#if 0
  call_dissector(data_handle,next_tvb, pinfo, tree);
#endif
  offset += tvb_length_remaining(tvb, offset);
     /* we dissected all of the containing PDU */

  return offset;

} /* ositp_decode_UD */

/* Returns TRUE if we found at least one valid COTP or CLTP PDU, FALSE
   otherwise.

There doesn't seem to be any way in which the OSI network layer protocol
   distinguishes between COTP and CLTP, but the first two octets of both
   protocols' headers mean the same thing - length and PDU type - and the
   only valid CLTP PDU type is not a valid COTP PDU type, so we'll handle
   both of them here. */
static gboolean dissect_ositp_internal(tvbuff_t *tvb, packet_info *pinfo,
		  proto_tree *tree, gboolean uses_inactive_subset)
{
  int offset = 0;
  guint8 li, tpdu, cdt;
  gboolean first_tpdu = TRUE;
  int new_offset;
  gboolean found_ositp = FALSE;
  gboolean is_cltp = FALSE;
  gboolean subdissector_found = FALSE;

  if (!proto_is_protocol_enabled(find_protocol_by_id(proto_cotp)))
    return FALSE;	/* COTP has been disabled */
  /* XXX - what about CLTP? */

  pinfo->current_proto = "COTP";

  /* Initialize the COL_INFO field; each of the TPDUs will have its
     information appended. */
  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->cinfo, COL_INFO))
        col_append_str(pinfo->cinfo, COL_INFO, ", ");
    }
    if ((li = tvb_get_guint8(tvb, offset + P_LI)) == 0) {
      if (check_col(pinfo->cinfo, COL_INFO))
col_append_str(pinfo->cinfo, COL_INFO, "Length indicator is zero");
      if (!first_tpdu)
        call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1),
                       pinfo, tree);
      return found_ositp;
    }

    tpdu    = (tvb_get_guint8(tvb, offset + P_TPDU) >> 4) & 0x0F;
    if (tpdu == UD_TPDU)
      pinfo->current_proto = "CLTP";	/* connectionless transport */
    cdt     = tvb_get_guint8(tvb, offset + P_CDT) & 0x0F;

    switch (tpdu) {
      case CC_TPDU :
      case CR_TPDU :
        new_offset = ositp_decode_CC(tvb, offset, li, tpdu, pinfo, tree,
				     uses_inactive_subset, &subdissector_found);
        break;
      case DR_TPDU :
        new_offset = ositp_decode_DR(tvb, offset, li, tpdu, pinfo, tree);
        break;
      case DT_TPDU :
        new_offset = ositp_decode_DT(tvb, offset, li, tpdu, pinfo, tree,
				   uses_inactive_subset, &subdissector_found);
        break;
      case ED_TPDU :
        new_offset = ositp_decode_ED(tvb, offset, li, tpdu, pinfo, tree);
        break;
      case RJ_TPDU :
new_offset = ositp_decode_RJ(tvb, offset, li, tpdu, cdt, pinfo, tree);
        break;
      case DC_TPDU :
        new_offset = ositp_decode_DC(tvb, offset, li, tpdu, pinfo, tree);
        break;
      case AK_TPDU :
new_offset = ositp_decode_AK(tvb, offset, li, tpdu, cdt, pinfo, tree);
        break;
      case EA_TPDU :
        new_offset = ositp_decode_EA(tvb, offset, li, tpdu, pinfo, tree);
        break;
      case ER_TPDU :
        new_offset = ositp_decode_ER(tvb, offset, li, tpdu, pinfo, tree);
        break;


      case UD_TPDU :
        new_offset = ositp_decode_UD(tvb, offset, li, tpdu, pinfo, tree);
        is_cltp = TRUE;
        break;
      default      :
        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)
        call_dissector(data_handle, tvb_new_subset(tvb, offset, -1, -1),
                       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->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, is_cltp ? "CLTP" : "COTP");
      found_ositp = TRUE;
    }

    offset = new_offset;
    first_tpdu = FALSE;
  }
  return found_ositp;
} /* dissect_ositp_internal */

static void dissect_ositp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  if (!dissect_ositp_internal(tvb, pinfo, tree, FALSE))
    call_dissector(data_handle,tvb, pinfo, tree);
}

/*
 *  CLNP part / main entry point
*/

static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  proto_tree *clnp_tree = NULL;
  proto_item *ti;
  guint8      cnf_proto_id;
  guint8      cnf_hdr_len;
  guint8      cnf_vers;
  guint8      cnf_ttl;
  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;
  guchar      src_len, dst_len, nsel, opt_len = 0;
  const guint8     *dst_addr, *src_addr;
  gint        len;
  guint       next_length;
  proto_tree *discpdu_tree;
  gboolean    save_in_error_pkt;
  fragment_data *fd_head;
  tvbuff_t   *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->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);
proto_tree_add_uint_format(clnp_tree, hf_clnp_id, tvb, P_CLNP_PROTO_ID, 1,
				 cnf_proto_id,
				 "Inactive subset");
    }
    next_tvb = tvb_new_subset(tvb, 1, -1, -1);
    dissect_ositp_internal(next_tvb, pinfo, tree, TRUE);
    return;
  }

  /* return if version not known */
  cnf_vers = tvb_get_guint8(tvb, P_CLNP_VERS);
  if (cnf_vers != ISO8473_V1) {
    call_dissector(data_handle,tvb, pinfo, tree);
    return;
  }

  /* fixed part decoding */
  cnf_hdr_len = tvb_get_guint8(tvb, P_CLNP_HDR_LEN);
  opt_len = cnf_hdr_len;

  if (tree) {
ti = proto_tree_add_item(tree, proto_clnp, tvb, 0, cnf_hdr_len, FALSE);
    clnp_tree = proto_item_add_subtree(ti, ett_clnp);
    proto_tree_add_uint(clnp_tree, hf_clnp_id, tvb, P_CLNP_PROTO_ID, 1,
			       cnf_proto_id);
    proto_tree_add_uint(clnp_tree, hf_clnp_length, tvb, P_CLNP_HDR_LEN, 1,
			cnf_hdr_len);
    proto_tree_add_uint(clnp_tree, hf_clnp_version, tvb, P_CLNP_VERS, 1,
			cnf_vers);
    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.%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_abbrev_vals,
				"Unknown (0x%02x)");
  flag_string[0] = '\0';
  if (cnf_type & CNF_SEG_OK)
    strcat(flag_string, "S ");
  if (cnf_type & CNF_MORE_SEGS)
    strcat(flag_string, "M ");
  if (cnf_type & CNF_ERR_OK)
    strcat(flag_string, "E ");
  if (tree) {
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
     segmentation part and determine whether this datagram is segmented
     or not - set the Info column now; we'll get an exception before
     we set it otherwise. */

  if (!tvb_bytes_exist(tvb, 0, cnf_hdr_len)) {
    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);
    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 */

  /* address part */

  offset = P_CLNP_ADDRESS_PART;
  dst_len  = tvb_get_guint8(tvb, offset);
  dst_addr = tvb_get_ptr(tvb, offset + 1, dst_len);
  nsel     = tvb_get_guint8(tvb, offset + dst_len);
  src_len  = tvb_get_guint8(tvb, offset + dst_len + 1);
  src_addr = tvb_get_ptr(tvb, offset + dst_len + 2, src_len);

  if (tree) {
    proto_tree_add_uint(clnp_tree, hf_clnp_dest_length, tvb, offset, 1,
			dst_len);
proto_tree_add_bytes_format(clnp_tree, hf_clnp_dest, tvb, offset + 1 , dst_len,
			       dst_addr,
			       " DA : %s",
			       print_nsap_net(dst_addr, dst_len));
    proto_tree_add_uint(clnp_tree, hf_clnp_src_length, tvb,
			offset + 1 + dst_len, 1, src_len);
    proto_tree_add_bytes_format(clnp_tree, hf_clnp_src, tvb,
			       offset + dst_len + 2, src_len,
			       src_addr,
			       " SA : %s",
			       print_nsap_net(src_addr, src_len));

    opt_len -= dst_len + src_len +2;
  }

  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 */

  offset += dst_len + src_len + 2;

  if (cnf_type & CNF_SEG_OK) {
    struct clnp_segment seg;			/* XXX - not used */
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",
			du_id);
      proto_tree_add_text(clnp_tree, tvb, offset + 2 , 2,
			"Segment offset      : %6u",
			segment_offset);
      proto_tree_add_text(clnp_tree, tvb, offset + 4 , 2,
			"Total length        : %6u",
			tvb_get_ntohs(tvb, offset + 4));
    }

    offset  += 6;
    opt_len -= 6;
  }

  if (tree) {
    /* To do : decode options  */
/*
    proto_tree_add_text(clnp_tree, tvb, offset,
			cnf_hdr_len - offset,
			"Options/Data: <not shown>");
*/
/* QUICK HACK Option Len:= PDU_Hd_length-( FixedPart+AddresPart+SegmentPart )*/

    dissect_osi_options( opt_len,
                         tvb, offset, clnp_tree );
  }

  /* Length of CLNP datagram plus headers above it. */
  len = segment_length;

  offset = cnf_hdr_len;

  /* If clnp_reassemble is on, 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_bytes_exist(tvb, offset, segment_length - cnf_hdr_len) &&
	cksum_status != CKSUM_NOT_OK) {
fd_head = fragment_add_check(tvb, offset, pinfo, du_id, clnp_segment_table,
			   clnp_reassembled_table, segment_offset,
			   segment_length - cnf_hdr_len,
			   cnf_type & CNF_MORE_SEGS);

next_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled CLNP",
        fd_head, &clnp_frag_items, &update_col_info, clnp_tree);
  } 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);

    /* 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, -1), pinfo,
                   tree);
    pinfo->fragmented = save_fragmented;
    return;
  }

  if (tvb_offset_exists(tvb, offset)) {
    switch (cnf_type & CNF_TYPE) {

    case DT_NPDU:
    case MD_NPDU:
      /* Continue with COTP if any data.
         XXX - if this isn't the first Derived PDU of a segmented Initial
         PDU, skip that? */

      if (nsel == (char)tp_nsap_selector || always_decode_transport) {
        if (dissect_ositp_internal(next_tvb, pinfo, tree, FALSE)) {
          pinfo->fragmented = save_fragmented;
          return;	/* yes, it appears to be COTP or CLTP */
        }
      }

      if (dissector_try_heuristic(clnp_heur_subdissector_list, next_tvb,
				  pinfo, tree))	{
          pinfo->fragmented = save_fragmented;


          return;	/* yes, it appears to be COTP or CLTP */
      }
	  else		/* for GSSE, need to dissect as if COTP (faked inactive layer )*/
        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;
	 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. */
          ti = proto_tree_add_text(clnp_tree, tvb, offset, next_length,
            "Discarded PDU");
          discpdu_tree = proto_item_add_subtree(ti, ett_clnp_disc_pdu);

          /* 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;

          call_dissector(clnp_handle, next_tvb, pinfo, discpdu_tree);

          /* Restore the "we're inside an error packet" flag. */
          pinfo->in_error_pkt = save_in_error_pkt;
        }
      }
      pinfo->fragmented = save_fragmented;
      return;	/* we're done with this PDU */

    case ERQ_NPDU:
    case ERP_NPDU:
      /* XXX - dissect this */
      break;
    }
  }
  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);
  reassembled_table_init(&clnp_reassembled_table);
}

static void
cotp_reassemble_init(void)
{
  fragment_table_init(&cotp_segment_table);
  reassembled_table_init(&cotp_reassembled_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, "", HFILL }},

    { &hf_clnp_length,
{ "HDR Length ", "clnp.len", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},

    { &hf_clnp_version,
{ "Version ", "clnp.version", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},

    { &hf_clnp_ttl,
{ "Holding Time ", "clnp.ttl", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},

    { &hf_clnp_type,
{ "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, "", HFILL }},

    { &hf_clnp_checksum,
{ "Checksum ", "clnp.checksum", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},

    { &hf_clnp_dest_length,
{ "DAL ", "clnp.dsap.len", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},

    { &hf_clnp_dest,
{ " DA ", "clnp.dsap", FT_BYTES, BASE_NONE, NULL, 0x0, "", HFILL }},

    { &hf_clnp_src_length,
{ "SAL ", "clnp.ssap.len", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},

    { &hf_clnp_src,
{ " 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_FRAMENUM, BASE_NONE, NULL, 0x0,
	"Reassembly error due to illegal segments", HFILL }},

    { &hf_clnp_segment,
      { "CLNP Segment", "clnp.segment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
	"CLNP Segment", HFILL }},

    { &hf_clnp_segments,
      { "CLNP Segments", "clnp.segments", FT_NONE, BASE_DEC, NULL, 0x0,
	"CLNP Segments", HFILL }},

    { &hf_clnp_reassembled_in,
{ "Reassembled CLNP in frame", "clnp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
	"This CLNP packet is reassembled in this frame", 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", "clnp");
  proto_register_field_array(proto_clnp, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));
  register_dissector("clnp", dissect_clnp, proto_clnp);
  register_heur_dissector_list("clnp", &clnp_heur_subdissector_list);
  register_init_routine(clnp_reassemble_init);
  register_init_routine(cotp_reassemble_init);

  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)",
       	16, &tp_nsap_selector);
  prefs_register_bool_preference(clnp_module, "always_decode_transport",
	"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_reg_handoff_clnp(void)
{
  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);
}

void proto_register_cotp(void)
{
  static hf_register_info hf[] = {
    { &hf_cotp_srcref,
      { "Source reference", "cotp.srcref", FT_UINT16, BASE_HEX, NULL, 0x0,
        "Source address reference", HFILL}},
    { &hf_cotp_destref,
{ "Destination reference", "cotp.destref", FT_UINT16, BASE_HEX, NULL, 0x0,
        "Destination address reference", HFILL}},
    { &hf_cotp_type,
{ "COTP PDU Type", "cotp.type", FT_UINT8, BASE_HEX, VALS(cotp_tpdu_type_abbrev_vals), 0x0,
        "COTP PDU Type", HFILL}},
    { &hf_cotp_segment_overlap,
{ "Segment overlap", "cotp.segment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
	"Segment overlaps with other segments", HFILL }},
    { &hf_cotp_segment_overlap_conflict,
{ "Conflicting data in segment overlap", "cotp.segment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
	"Overlapping segments contained conflicting data", HFILL }},
    { &hf_cotp_segment_multiple_tails,
{ "Multiple tail segments found", "cotp.segment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
	"Several tails were found when reassembling the packet", HFILL }},
    { &hf_cotp_segment_too_long_segment,
{ "Segment too long", "cotp.segment.toolongsegment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
	"Segment contained data past end of packet", HFILL }},
    { &hf_cotp_segment_error,
{ "Reassembly error", "cotp.segment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
	"Reassembly error due to illegal segments", HFILL }},
    { &hf_cotp_segment,
      { "COTP Segment", "cotp.segment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
	"COTP Segment", HFILL }},
    { &hf_cotp_segments,
      { "COTP Segments", "cotp.segments", FT_NONE, BASE_DEC, NULL, 0x0,
	"COTP Segments", HFILL }},
    { &hf_cotp_reassembled_in,
{ "Reassembled COTP in frame", "cotp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
	"This COTP packet is reassembled in this frame", HFILL }},

  };
  static gint *ett[] = {
	&ett_cotp,
	&ett_cotp_segment,
	&ett_cotp_segments,
  };

  module_t *cotp_module;

  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));
  cotp_module = prefs_register_protocol(proto_cotp, NULL);

  prefs_register_bool_preference(cotp_module, "reassemble",
	 "Reassemble segmented COTP datagrams",
	 "Whether segmented COTP datagrams should be reassembled",
	&cotp_reassemble);

  /* subdissector code in inactive subset */
register_heur_dissector_list("cotp_is", &cotp_is_heur_subdissector_list);

  /* other COTP/ISO 8473 subdissectors */
  register_heur_dissector_list("cotp", &cotp_heur_subdissector_list);

  /* XXX - what about CLTP and proto_cltp? */
  register_dissector("ositp", dissect_ositp, proto_cotp);
}

void proto_register_cltp(void)
{
  static hf_register_info hf[] = {
    { &hf_cltp_type,
{ "CLTP PDU Type", "cltp.type", FT_UINT8, BASE_HEX, VALS(cltp_tpdu_type_abbrev_vals), 0x0,
        "CLTP PDU Type", HFILL}},
  };
  static gint *ett[] = {
	&ett_cltp,
  };

  proto_cltp = proto_register_protocol(PROTO_STRING_CLTP, "CLTP", "cltp");
  proto_register_field_array(proto_cltp, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));
}
/* packet-ses.c
*
* Routine to dissect ISO 8327-1 OSI Session Protocol packets
*
* $Id: packet-ses.c,v 1.5 2003/12/12 22:19:45 guy Exp $
*
* Yuriy Sidelnikov <YSidelnikov@xxxxxxxxxxx>
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@xxxxxxxxxxxx>
* 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
* 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 <glib.h>
#include <epan/packet.h>

#include <stdio.h>
#include <string.h>

#include "packet-ses.h"
#include "packet-frame.h"
#include "prefs.h"

#include <epan/strutil.h>

/* ses header fields             */
static int proto_ses          = -1;
static int proto_clses		  = -1;
static int hf_ses_type        = -1;
static int hf_ses_type_0      = -1;
static int hf_ses_length      = -1;
static int hf_ses_version     = -1;
static int hf_ses_reserved    = -1;
static int proto_ses_is		  = -1;

/* ses fields defining a sub tree */
static gint ett_ses           = -1;
static gint ett_ses_param     = -1;

/*
----------------------------------------------------------------------------------------------------------*/
static dissector_handle_t pres_handle = NULL;
/*
----------------------------------------------------------------------------------------------------------*/


/* flags */
static int hf_connect_protocol_options_flags = -1;
static int hf_version_number_options_flags = -1;
static int hf_enclosure_item_options_flags = -1;
static int hf_token_item_options_flags = -1;

static gint ett_connect_protocol_options_flags = -1;
static gint ett_protocol_version_flags = -1;
static gint ett_enclosure_item_flags = -1;
static gint ett_token_item_flags = -1;
static gint ett_ses_req_options_flags = -1;

/* called SS user reference */
static int hf_called_ss_user_reference = -1;

/* calling SS user reference */
static int hf_calling_ss_user_reference = -1;

/* common reference */
static int hf_common_reference = -1;

/* additional reference information */
static int hf_additional_reference_information = -1;

/* token item */
static int hf_release_token = -1;
static int hf_major_activity_token = -1;
static int hf_synchronize_minor_token = -1;
static int hf_data_token = -1;

/* protocol options */
static int hf_able_to_receive_extended_concatenated_SPDU = -1;

/* session requirement */
static int hf_session_user_req_flags = -1;
static int hf_session_exception_report= -1;
static int hf_data_separation_function_unit= -1;
static int hf_symmetric_synchronize_function_unit= -1;
static int hf_typed_data_function_unit= -1;
static int hf_exception_function_unit= -1;
static int hf_capability_function_unit=-1;
static int hf_negotiated_release_function_unit= -1;
static int hf_activity_management_function_unit= -1;
static int hf_resynchronize_function_unit= -1;
static int hf_major_resynchronize_function_unit= -1;
static int hf_minor_resynchronize_function_unit= -1;
static int hf_expedited_data_resynchronize_function_unit= -1;
static int hf_duplex_function_unit= -1;
static int hf_half_duplex_function_unit = -1;

/* TSDU maximum size */
static int hf_proposed_tsdu_maximum_size_i2r = -1;
static int hf_proposed_tsdu_maximum_size_r2i = -1;

/* protocol version */
static int hf_protocol_version_1 = -1;
static int hf_protocol_version_2 = -1;

/* initial serial number */
static int hf_initial_serial_number = -1;

/* enclosure item */
static int hf_beginning_of_SSDU = -1;
static int hf_end_of_SSDU = -1;

/* token setting item */

static const value_string token_setting_vals[] = {
	{ 0x00, "initiator's side" },
	{ 0x01, "responder's side" },
	{ 0x02, "called SS user's choice" },
	{ 0x03, "reserved" },
	{ 0, NULL }
};

static int hf_release_token_setting = -1;
static int hf_major_activity_token_setting = -1;
static int hf_synchronize_minor_token_setting = -1;
static int hf_data_token_setting = -1;

/* calling session selector */
static int hf_calling_session_selector = -1;

/* called session selector */
static int hf_called_session_selector = -1;

/* serial number */
static int hf_serial_number = -1;

/* second serial number */
static int hf_second_serial_number = -1;

/* second initial serial number */
static int hf_second_initial_serial_number = -1;

/* large initial serial number */
static int hf_large_initial_serial_number = -1;

/* large second initial serial number */
static int hf_large_second_initial_serial_number = -1;

static const value_string ses_vals[] =
{
  {SES_CONNECTION_REQUEST,  "Connection request PDU" },
  {SES_CONNECTION_ACCEPT,    "Connection accept PDU"   },
  {SES_EXCEPTION_REPORT,    "Exception report PDU"   },
  {SES_DATA_TRANSFER,  "Data transfer PDU" },
  {SES_PLEASE_TOKENS,    "Please tokens PDU"   },
  {SES_EXPEDITED,    "Expedited PDU"   },
  {SES_PREPARE,    "Prepare PDU"   },
  {SES_NOT_FINISHED,    "Not finished PDU"   },
  {SES_FINISH,    "Finish PDU"   },
  {SES_DISCONNECT,    "Disconnect PDU"   },
  {SES_REFUSE,    "Refuse PDU"   },
  {SES_CONNECTION_DATA_OVERFLOW,    "Data overflow PDU"   },
  {SES_OVERFLOW_ACCEPT,    "Overflow accept PDU"   },
  {SES_GIVE_TOKENS_CONFIRM,    "Tokens confirm PDU"   },
  {SES_GIVE_TOKENS_ACK,    "Give tokens ACK PDU"   },
  {SES_ABORT,    "Abort PDU"   },
  {SES_ABORT_ACCEPT,    "Abort accept PDU"   },
  {SES_ACTIVITY_RESUME,    "Activity resume PDU"   },
  {SES_TYPED_DATA,    "Typed data PDU"   },
  {SES_RESYNCHRONIZE_ACK,    "Resynchronize ACK PDU"   },
  {SES_MAJOR_SYNC_POINT,    "Session major sync point PDU"   },
  {SES_MAJOR_SYNC_ACK,    "Session major sync ACK PDU"   },
  {SES_ACTIVITY_START,    "Activity start PDU"   },
  {SES_EXCEPTION_DATA,    "Exception data PDU"   },
  {SES_MINOR_SYNC_POINT,    "Minor sync point PDU"   },
  {SES_MINOR_SYNC_ACK,    "Minor sync ACK PDU"   },
  {SES_RESYNCHRONIZE,    "Resynchronize PDU"   },
  {SES_ACTIVITY_DISCARD,    "Activity discard PDU"   },
  {SES_ACTIVITY_DISCARD_ACK,    "Activity discard ACK PDU"   },
  {SES_CAPABILITY,    "Capability PDU"   },
  {SES_CAPABILITY_DATA_ACK,    "Capability data ACK PDU"   },
  {CONN_LESS_SESS,    "Connectionless Session"   },
  {0,             NULL           }
};

static const value_string ses_category0_vals[] =
{
  {SES_PLEASE_TOKENS,    "Please tokens PDU"   },
  {SES_GIVE_TOKENS,    "Give tokens PDU"   },
  {0,             NULL           }
};


static const value_string param_vals[] =
{
  {Connection_Identifier, "Connection Identifier"},
  {Connect_Accept_Item, "Connect Accept Item"},
  {Called_SS_user_Reference, "Called SS user Reference"},
  {Calling_SS_user_Reference, "Calling SS user Reference"},
  {Common_Reference, "Common Reference"},
  {Sync_Type_Item, "Sync Type Item"},
  {Token_Item, "Token Item"},
  {Transport_Disconnect, "Transport_Disconnect"},
  {Additional_Reference_Information, "Additional Reference Information"},
  {Protocol_Options, "Protocol Options"},
  {TSDU_Maximum_Size, "TSDU Maximum Size"},
  {Version_Number, "Version Number"},
  {Initial_Serial_Number, "Initial Serial Number"},
  {Prepare_Type, "Prepare Type"},
  {EnclosureItem, "Enclosure Item"},
  {Token_Setting_Item, "Token Setting Item"},
  {Resync_Type, "Resync Type"},
  {Serial_Number, "Serial Number"},
  {Linking_Information, "Linking Information"},
  {Reflect_Parameter, "Reflect Parameter"},
  {Reason_Code, "Reason Code"},
  {Calling_Session_Selector, "Calling Session Selector"},
  {Called_Session_Selector, "Called Session Selector"},
  {Second_Resync_Type, "Second Resync Type"},
  {Second_Serial_Number, "Second Serial Number"},
  {Second_Initial_Serial_Number, "Second Initial Serial Number"},
  {Upper_Limit_Serial_Number, "Upper Limit Serial Number"},
  {Large_Initial_Serial_Number, "Large Initial Serial Number"},
{Large_Second_Initial_Serial_Number, "Large Second Initial Serial Number"},
  {Data_Overflow, "Data Overflow"},
  {Session_Requirement, "Session Requirement"},
  {User_Data, "Session user data"},
  {Extended_User_Data, "Session extended user data"},
  {0, NULL}
};

static const value_string reason_vals[] =
{
{reason_not_specified, "Rejection by called SS-user; reason not specified" }, {temporary_congestion, "Rejection by called SS-user due to temporary congestion" },
  {Subsequent,    "Rejection by called SS-user."   },
  {Session_Selector_unknown,  "Session Selector unknown" },
  {SS_user_not_attached_to_SSAP,    "SS-user not attached to SSAP"   },
{SPM_congestion_at_connect_time, "SPM congestion at connect time" }, {versions_not_supported, "Proposed protocol versions not supported" }, {SPM_reason_not_specified, "Rejection by the SPM; reason not specified" },
  {SPM_implementation_restriction,    "Finish PDU"   },
{SES_DISCONNECT, "Rejection by the SPM; implementation restriction stated in the PICS" },
  {0,             NULL           }
};

/* desegmentation of OSI over ses  */
/*static gboolean ses_desegment = TRUE;*/


/* find the dissector for data */
static dissector_handle_t data_handle;
static dissector_handle_t cltp_handle;

static void
call_pres_dissector(tvbuff_t *tvb, int offset, guint16 param_len,
    packet_info *pinfo, proto_tree *tree, proto_tree *param_tree)
{
	/* do we have OSI presentation packet dissector ? */
	if(!pres_handle)
	{
		/* No - display as data */
		if (tree)
		{
			proto_tree_add_text(param_tree, tvb, offset, param_len,
			    "User data");
		}
	}
	else
	{
		/* Yes - call presentation dissector */
		tvbuff_t *next_tvb;

		next_tvb = tvb_new_subset(tvb, offset, param_len, param_len);
		TRY
		{
			call_dissector(pres_handle, next_tvb, pinfo, tree);
		}
		CATCH_ALL
		{
			show_exception(tvb, pinfo, tree, EXCEPT_CODE);
		}
		ENDTRY;
	}
}

/* this routine returns length of parameter field, parameter group,
   or parameter */
static int
get_item_len(tvbuff_t *tvb, int offset, int *len_len)
{
	guint16 len;

	len = tvb_get_guint8(tvb, offset);
	if(len == TWO_BYTE_LEN)
	{
		len = tvb_get_ntohs(tvb, offset+1);
		*len_len = 3;
	}
	else
		*len_len = 1;
	return len;
}

static gboolean
dissect_parameter(tvbuff_t *tvb, int offset, proto_tree *tree,
    proto_tree *param_tree, packet_info *pinfo, guint8 param_type,
    guint16 param_len)
{
	gboolean has_user_information = TRUE;
	guint16       flags;
	proto_item   *tf;
	proto_tree   *flags_tree;

	switch (param_type)
	{
	case Called_SS_user_Reference:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_called_ss_user_reference,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case Calling_SS_user_Reference:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_calling_ss_user_reference,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case Common_Reference:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_common_reference,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case Additional_Reference_Information:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_additional_reference_information,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case Token_Item:
		if (param_len != 1)
		{
			proto_tree_add_text(param_tree, tvb, offset,
			    param_len, "Length is %u, should be 1",
			    param_len);
			break;
		}
		if (tree)
		{
			flags = tvb_get_guint8(tvb, offset);
			tf = proto_tree_add_uint(param_tree,
			    hf_token_item_options_flags, tvb, offset, 1,
			    flags);
			flags_tree = proto_item_add_subtree(tf,
			    ett_token_item_flags);
			proto_tree_add_boolean(flags_tree, hf_release_token,
			    tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_major_activity_token, tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_synchronize_minor_token, tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree, hf_data_token, tvb,
			    offset, 1, flags);
		}
		break;

	case Transport_Disconnect:
		if (param_len != 1)
		{
			proto_tree_add_text(param_tree, tvb, offset,
			    param_len, "Length is %u, should be 1",
			    param_len);
			break;
		}
		if (tree)
		{
			guint8       flags;

			flags = tvb_get_guint8(tvb, offset);
			if(flags & transport_connection_is_released )
			{
				proto_tree_add_text(param_tree, tvb, offset, 1,
				    "transport connection is released");
			}
			else
			{
				proto_tree_add_text(param_tree, tvb, offset, 1,
				    "transport connection is kept");
			}

			if(flags & user_abort )
			{
				proto_tree_add_text(param_tree, tvb, offset, 1,
				    "user abort");
			}

			if(flags & protocol_error )
			{
				proto_tree_add_text(param_tree, tvb, offset, 1,
				    "protocol error");
			}

			if(flags & no_reason )
			{
				proto_tree_add_text(param_tree, tvb, offset, 1,
				    "no reason");
			}

			if(flags & implementation_restriction )
			{
				proto_tree_add_text(param_tree, tvb, offset, 1,
				    "implementation restriction");
			}
		}
		break;

	case Protocol_Options:
		if (param_len != 1)
		{
			proto_tree_add_text(param_tree, tvb, offset,
			    param_len, "Length is %u, should be 1",
			    param_len);
			break;
		}
		if (tree)
		{
			flags = tvb_get_guint8(tvb, offset);
			tf = proto_tree_add_uint(param_tree,
			    hf_connect_protocol_options_flags, tvb, offset, 1,
			    flags);
			flags_tree = proto_item_add_subtree(tf,
			    ett_connect_protocol_options_flags);
			proto_tree_add_boolean(flags_tree,
			    hf_able_to_receive_extended_concatenated_SPDU,
			    tvb, offset, 1, flags);
		}
		break;

	case Session_Requirement:
		if (param_len != 2)
		{
			proto_tree_add_text(param_tree, tvb, offset,
			    param_len, "Length is %u, should be 2",
			    param_len);
			break;
		}
		if (tree)
		{
			flags = tvb_get_ntohs(tvb, offset);
			tf = proto_tree_add_uint(param_tree,
			    hf_session_user_req_flags, tvb, offset, 2,
			    flags);
			flags_tree = proto_item_add_subtree(tf,
			    ett_ses_req_options_flags);
			proto_tree_add_boolean(flags_tree,
			    hf_session_exception_report, tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_data_separation_function_unit, tvb, offset, 2,
			    flags);
			proto_tree_add_boolean(flags_tree,
			    hf_symmetric_synchronize_function_unit,
			    tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_typed_data_function_unit, tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_exception_function_unit, tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_capability_function_unit, tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_negotiated_release_function_unit,
			    tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_activity_management_function_unit,
			    tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_resynchronize_function_unit, tvb, offset, 2,
			    flags);
			proto_tree_add_boolean(flags_tree,
			    hf_major_resynchronize_function_unit,
			    tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_minor_resynchronize_function_unit,
			    tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_expedited_data_resynchronize_function_unit,
			    tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_duplex_function_unit, tvb, offset, 2, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_half_duplex_function_unit,
			    tvb, offset, 2, flags);
		}
		break;

	case TSDU_Maximum_Size:
		if (param_len != 4)
		{
			proto_tree_add_text(param_tree, tvb, offset,
			    param_len, "Length is %u, should be 4",
			    param_len);
			break;
		}
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_proposed_tsdu_maximum_size_i2r,
			    tvb, offset, 2, FALSE);
			proto_tree_add_item(param_tree,
			    hf_proposed_tsdu_maximum_size_r2i,
			    tvb, offset+2, 2, FALSE);
		}
		break;

	case Version_Number:
		if (param_len != 1)
		{
			proto_tree_add_text(param_tree, tvb, offset,
			    param_len, "Length is %u, should be 1",
			    param_len);
			break;
		}
		if (tree)
		{
			flags = tvb_get_guint8(tvb, offset);
			tf = proto_tree_add_uint(param_tree,
			    hf_version_number_options_flags, tvb, offset, 1,
			    flags);
			flags_tree = proto_item_add_subtree(tf,
			    ett_protocol_version_flags);
			proto_tree_add_boolean(flags_tree,
			    hf_protocol_version_2, tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree,
			    hf_protocol_version_1, tvb, offset, 1, flags);
		}
		break;

	case Initial_Serial_Number:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_initial_serial_number,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case EnclosureItem:
		if (param_len != 1)
		{
			proto_tree_add_text(param_tree, tvb, offset,
			    param_len, "Length is %u, should be 1",
			    param_len);
			break;
		}
		flags = tvb_get_guint8(tvb, offset);
		if (tree)
		{
			tf = proto_tree_add_uint(param_tree,
			    hf_enclosure_item_options_flags, tvb, offset, 1,
			    flags);
			flags_tree = proto_item_add_subtree(tf,
			    ett_enclosure_item_flags);
			proto_tree_add_boolean(flags_tree, hf_end_of_SSDU,
			    tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree, hf_beginning_of_SSDU,
			    tvb, offset, 1, flags);
		}
		if (flags & END_SPDU) {
			/*
			 * In Data Transfer and Typed Data SPDUs,
			 * "The User Information Field shall be present
			 * if the Enclosure Item is not present, or has
			 * bit 2 = 0", which presumably means it shall
			 * *not* be present if the Enclosure item *is*
			 * present and has bit 2 = 1.
			 */
			has_user_information = FALSE;
		}
		break;

	case Token_Setting_Item:
		if (param_len != 1)
		{
			proto_tree_add_text(param_tree, tvb, offset,
			    param_len, "Length is %u, should be 1",
			    param_len);
			break;
		}
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_release_token_setting,
			    tvb, offset, 1, FALSE);
			proto_tree_add_item(param_tree,
			    hf_major_activity_token_setting,
			    tvb, offset, 1, FALSE);
			proto_tree_add_item(param_tree,
			    hf_synchronize_minor_token_setting,
			    tvb, offset, 1, FALSE);
			proto_tree_add_item(param_tree,
			    hf_data_token_setting,
			    tvb, offset, 1, FALSE);
		}
		break;

	case Serial_Number:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_serial_number,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case Reason_Code:
/*
	0:	Rejection by called SS-user; reason not specified.
	1:	Rejection by called SS-user due to temporary congestion.
2: Rejection by called SS-user. Subsequent octets may be used for user data up to a length of 512 octets if Protocol Version 1 has been selected, and up
to a length such that the total length (including SI and LI)  of the SPDU
does not exceed 65 539 octets if Protocol Version 2 has been selected.
	128 + 1:	Session Selector unknown.
	128 + 2:	SS-user not attached to SSAP.
	128 + 3:	SPM congestion at connect time.
	128 + 4:	Proposed protocol versions not supported.
	128 + 5:	Rejection by the SPM; reason not specified.
	128 + 6:	Rejection by the SPM; implementation restriction stated in the
PICS.    */
		if (param_len < 1)
		{
			proto_tree_add_text(param_tree, tvb, offset,
			    param_len, "Length is %u, should be >= 1",
			    param_len);
			break;
		}
		if (tree)
		{
			guint8      reason_code;

			reason_code = tvb_get_guint8(tvb, offset);
			proto_tree_add_text(param_tree, tvb, offset, 1,
			    "Reason Code: %s",
			    val_to_str(reason_code, reason_vals, "Unknown (%u)"));
		}
		offset++;
		param_len--;
		if (param_len != 0)
		{
			call_pres_dissector(tvb, offset, param_len,
			    pinfo, tree, param_tree);
		}
		break;

	case Calling_Session_Selector:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_calling_session_selector,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case Called_Session_Selector:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_called_session_selector,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case Second_Serial_Number:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_second_serial_number,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case Second_Initial_Serial_Number:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_second_initial_serial_number,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case Large_Initial_Serial_Number:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_large_initial_serial_number,
			    tvb, offset, param_len, FALSE);
		}
		break;

	case Large_Second_Initial_Serial_Number:
		if (param_len == 0)
			break;
		if (tree)
		{
			proto_tree_add_item(param_tree,
			    hf_large_second_initial_serial_number,
			    tvb, offset, param_len, FALSE);
		}
		break;

	default:
		break;
	}
	return has_user_information;
}

static gboolean
dissect_parameter_group(tvbuff_t *tvb, int offset, proto_tree *tree,
    proto_tree *pg_tree, packet_info *pinfo, guint16 pg_len)
{
	gboolean has_user_information = TRUE;
	proto_item *ti;
	proto_tree *param_tree;
	guint8 param_type;
	char *param_str;
	int len_len;
	guint16 param_len;

	while(pg_len != 0)
	{
		param_type = tvb_get_guint8(tvb, offset);
		ti = proto_tree_add_text(pg_tree, tvb, offset, -1,
		    val_to_str(param_type, param_vals,
		      "Unknown parameter type (0x%02x)"));
		param_tree = proto_item_add_subtree(ti, ett_ses_param);
		param_str = match_strval(param_type, param_vals);
		proto_tree_add_text(param_tree, tvb, offset, 1,
		    "Parameter type: %s",
		    param_str != NULL ? param_str : "Unknown");
		offset++;
		pg_len--;
		param_len = get_item_len(tvb, offset, &len_len);
		if (len_len > pg_len) {
			proto_item_set_len(ti, pg_len + 1);
			proto_tree_add_text(param_tree, tvb, offset, pg_len,
			    "Parameter length doesn't fit in parameter");
			return has_user_information;
		}
		pg_len -= len_len;
		if (param_len > pg_len) {
			proto_item_set_len(ti, pg_len + 1 + len_len);
			proto_tree_add_text(param_tree, tvb, offset, pg_len,
			    "Parameter length: %u, should be <= %u",
			    param_len, pg_len);
			return has_user_information;
		}
		proto_item_set_len(ti, 1 + len_len + param_len);
		proto_tree_add_text(param_tree, tvb, offset, len_len,
		    "Parameter length: %u", param_len);
		offset += len_len;

		if (param_str != NULL)
		{
			switch(param_type)
			{
			/* PG's in PG's are invalid, presumably */
			case Extended_User_Data:
			case User_Data:
			case Connect_Accept_Item:
			case Connection_Identifier:
			case Linking_Information:
				proto_tree_add_text(param_tree, tvb, offset,
				    param_len,
				    "Parameter group inside parameter group");
				break;

			default:
				if (!dissect_parameter(tvb, offset, tree,
				    param_tree, pinfo, param_type, param_len))
					has_user_information = FALSE;
				break;
			}
		}
		offset += param_len;
		pg_len -= param_len;
	}
	return has_user_information;
}

/*
 * Returns TRUE if there's a User Information field in this SPDU, FALSE
 * otherwise.
 */
static gboolean
dissect_parameters(tvbuff_t *tvb, int offset, guint16 len, proto_tree *tree,
    proto_tree *ses_tree, packet_info *pinfo)
{
	gboolean has_user_information = TRUE;
	proto_item *ti;
	proto_tree *param_tree;
	guint8 param_type;
	char *param_str;
	int len_len;
	guint16 param_len;

	while(len != 0)
	{
		param_type = tvb_get_guint8(tvb, offset);
		ti = proto_tree_add_text(ses_tree, tvb, offset, -1,
		    val_to_str(param_type, param_vals,
		      "Unknown parameter type (0x%02x)"));
		param_tree = proto_item_add_subtree(ti, ett_ses_param);
		param_str = match_strval(param_type, param_vals);
		proto_tree_add_text(param_tree, tvb, offset, 1,
		    "Parameter type: %s",
		    param_str != NULL ? param_str : "Unknown");
		offset++;
		len--;
		param_len = get_item_len(tvb, offset, &len_len);
		if (len_len > len) {
			proto_item_set_len(ti, len + 1 );
			proto_tree_add_text(param_tree, tvb, offset, len,
			    "Parameter length doesn't fit in parameter");
			return has_user_information;
		}
		len -= len_len;
		if (param_len > len) {
			proto_item_set_len(ti, len + 1 + len_len);
			proto_tree_add_text(param_tree, tvb, offset, len,
			    "Parameter length: %u, should be <= %u",
			    param_len, len);
			return has_user_information;
		}
		proto_item_set_len(ti, 1 + len_len + param_len);
		proto_tree_add_text(param_tree, tvb, offset, len_len,
		    "Parameter length: %u", param_len);
		offset += len_len;

		if (param_str != NULL)
		{
			switch(param_type)
			{
			case Extended_User_Data:
				break;

			case User_Data:
				call_pres_dissector(tvb, offset, param_len,
				    pinfo, tree, param_tree);
				break;

			/* handle PGI's  */
			case Connect_Accept_Item:
			case Connection_Identifier:
			case Linking_Information:
				/* Yes. */
				if (!dissect_parameter_group(tvb, offset, tree,
				    param_tree, pinfo, param_len))
					has_user_information = FALSE;
				break;

			/* everything else is a PI */
			default:
				if (!dissect_parameter(tvb, offset, tree,
				    param_tree, pinfo, param_type, param_len))
					has_user_information = FALSE;
				break;
			}
		}
		offset += param_len;
		len -= param_len;
	}
	return has_user_information;
}

/*
 * Dissect an SPDU.
 */
static int
dissect_spdu(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree,
    gboolean tokens)
{
	gboolean has_user_information = FALSE;
	guint8 type;
	proto_item *ti = NULL;
	proto_tree *ses_tree = NULL;
	int len_len;
	guint16 parameters_len;
	tvbuff_t *next_tvb;

	/*
	 * Get SPDU type.
	 */
	type = tvb_get_guint8(tvb, offset);

	if (tokens) {
	  	if (check_col(pinfo->cinfo, COL_INFO))
			col_add_str(pinfo->cinfo, COL_INFO,
			    val_to_str(type, ses_category0_vals, "Unknown SPDU type (0x%02x)"));
		if (tree) {
			ti = proto_tree_add_item(tree, proto_ses, tvb, offset,
			    -1, FALSE);
			ses_tree = proto_item_add_subtree(ti, ett_ses);
			proto_tree_add_uint(ses_tree, hf_ses_type_0, tvb,
			    offset, 1, type);
		}
	} else {
	  	if (check_col(pinfo->cinfo, COL_INFO))
			col_add_str(pinfo->cinfo, COL_INFO,
			    val_to_str(type, ses_vals, "Unknown SPDU type (0x%02x)"));
		if (tree) {
			ti = proto_tree_add_item(tree, proto_ses, tvb, offset,
			    -1, FALSE);
			ses_tree = proto_item_add_subtree(ti, ett_ses);
			proto_tree_add_uint(ses_tree, hf_ses_type, tvb,
			    offset, 1, type);
		}

		/*
		 * Might this SPDU have a User Information field?
		 */
		switch (type) {

		case SES_DATA_TRANSFER:
		case SES_EXPEDITED:
		case SES_TYPED_DATA:
		case CONN_LESS_SESS:				/*added Connectionless Session  */
			has_user_information = TRUE;
			break;
		}
	}
	offset++;

	/* get length of SPDU parameter field */
	parameters_len = get_item_len(tvb, offset, &len_len);
	if (tree)
		proto_tree_add_uint(ses_tree, hf_ses_length, tvb, offset,
		    len_len, parameters_len);
	offset += len_len;

	/* Dissect parameters. */
	if (!dissect_parameters(tvb, offset, parameters_len, tree, ses_tree,
	    pinfo))
		has_user_information = FALSE;
	offset += parameters_len;

	proto_item_set_end(ti, tvb, offset);

	/* Dissect user information, if present */
	if (has_user_information)
	{
		if (tvb_reported_length_remaining(tvb, offset) > 0)
			next_tvb = tvb_new_subset(tvb, offset, -1, -1);

			if(!pres_handle)
			{
				/* do we have OSI presentation packet dissector ? */
				call_dissector(data_handle, next_tvb, pinfo,
				    tree);
			}
		    else
			{
				call_dissector(pres_handle, next_tvb, pinfo,
				    tree);
			}


			/*
			 * No more SPDUs to dissect.  Set the offset to the
			 * end of the tvbuff.
			 */


		offset = tvb_length(tvb);
	}


	return offset;
}

/*
 * Dissect SPDUs inside a TSDU.
 */
static void
dissect_clses(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	int offset = 0;
	guint8 type;

	if (check_col(pinfo->cinfo, COL_PROTOCOL))
		col_set_str(pinfo->cinfo, COL_PROTOCOL, "CLSES");
  	if (check_col(pinfo->cinfo, COL_INFO))
  		col_clear(pinfo->cinfo, COL_INFO);

	if(!tree)
	{
     if(data_handle)
	  call_dissector(data_handle, tvb, pinfo, tree);
	}
	else
	{
	type = tvb_get_guint8(tvb, offset);
	if (type == SES_PLEASE_TOKENS || type == SES_GIVE_TOKENS)
		offset = dissect_spdu(tvb, offset, pinfo, tree, TOKENS_SPDU);

	/* Dissect the remaining SPDUs. */
	while (tvb_reported_length_remaining(tvb, offset) > 0)
		offset = dissect_spdu(tvb, offset, pinfo, tree, NON_TOKENS_SPDU);
	}
}

static void
dissect_ses(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	int offset = 0;
	guint8 type;

	if (check_col(pinfo->cinfo, COL_PROTOCOL))
		col_set_str(pinfo->cinfo, COL_PROTOCOL, "SES");
  	if (check_col(pinfo->cinfo, COL_INFO))
  		col_clear(pinfo->cinfo, COL_INFO);

	/*
	 * Do we have a category 0 SPDU (GIVE_TOKENS/PLEASE_TOKENS) as
	 * the first SPDU?
	 *
	 * If so, dissect it as such (GIVE_TOKENS and DATA_TRANSFER have
	 * the smae SPDU type value).
	 */
	type = tvb_get_guint8(tvb, offset);
	if (type == SES_PLEASE_TOKENS || type == SES_GIVE_TOKENS)
		offset = dissect_spdu(tvb, offset, pinfo, tree, TOKENS_SPDU);

	/* Dissect the remaining SPDUs. */
	while (tvb_reported_length_remaining(tvb, offset) > 0)
		offset = dissect_spdu(tvb, offset, pinfo, tree, NON_TOKENS_SPDU);
}

void
proto_register_ses(void)
{
	static hf_register_info hf[] =
	{
		{
			&hf_ses_type,
			{
				"SPDU Type",
				"ses.type",
				FT_UINT8,
				BASE_DEC,
				VALS(ses_vals),
				0x0,
				"", HFILL
			}
		},
		{
			&hf_ses_type_0,
			{
				"SPDU Type",
				"ses.type",
				FT_UINT8,
				BASE_DEC,
				VALS(ses_category0_vals),
				0x0,
				"", HFILL
			}
		},
		{
			&hf_ses_length,
			{
				"Length",
				"ses.length",
				FT_UINT16,
				BASE_DEC,
				NULL,
				0x0,
				"", HFILL
			}
		},

		{
			&hf_ses_version,
			{
				"Version",
				"ses.version",
				FT_UINT8,
				BASE_DEC,
				NULL,
				0x0,
				"", HFILL
			}
		},
		{
			&hf_ses_reserved,
			{
				"Reserved",
				"ses.reserved",
				FT_UINT8,
				BASE_DEC,
				NULL,
				0x0,
				"", HFILL
			}
		},
		{
			&hf_called_ss_user_reference,
			{
				"Called SS User Reference",
				"ses.called_ss_user_reference",
				FT_BYTES, BASE_NONE,
				NULL,
				0x0,
				"Called SS User Reference",
				HFILL
			}
		},
		{
			&hf_calling_ss_user_reference,
			{
				"Calling SS User Reference",
				"ses.calling_ss_user_reference",
				FT_BYTES, BASE_NONE,
				NULL,
				0x0,
				"Calling SS User Reference",
				HFILL
			}
		},
		{
			&hf_common_reference,
			{
				"Common Reference",
				"ses.common_reference",
				FT_BYTES, BASE_NONE,
				NULL,
				0x0,
				"Common Reference",
				HFILL
			}
		},
		{
			&hf_additional_reference_information,
			{
				"Additional Reference Information",
				"ses.additional_reference_information",
				FT_BYTES, BASE_NONE,
				NULL,
				0x0,
				"Additional Reference Information",
				HFILL
			}
		},
		{
			&hf_release_token,
			{
				"release token",
				"ses.release_token",
				FT_BOOLEAN, 8,
				NULL,
				RELEASE_TOKEN,
				"release token",
				HFILL
			}
		},
		{
			&hf_major_activity_token,
			{
				"major/activity token",
				"ses.major.token",
				FT_BOOLEAN, 8,
				NULL,
				MAJOR_ACTIVITY_TOKEN,
				"major/activity token",
				HFILL
			}
		},
		{
			&hf_synchronize_minor_token,
			{
				"synchronize minor token",
				"ses.synchronize_token",
				FT_BOOLEAN, 8,
				NULL,
				SYNCHRONIZE_MINOR_TOKEN,
				"synchronize minor token",
				HFILL
			}
		},
		{
			&hf_data_token,
			{
				"data token",
				"ses.data_token",
				FT_BOOLEAN, 8,
				NULL,
				DATA_TOKEN,
				"data  token",
				HFILL
			}
		},
		{
			&hf_able_to_receive_extended_concatenated_SPDU,
			{
				"Able to receive extended concatenated SPDU",
				"ses.connect.f1",
				FT_BOOLEAN, 8,
				NULL,
				SES_EXT_CONT,
				"Able to receive extended concatenated SPDU",
				HFILL
			}
		},
		{
			&hf_session_user_req_flags,
			{
				"Flags",
				"ses.req.flags",
				FT_UINT16,
				BASE_HEX,
				NULL,
				0x0,
				"",
				HFILL
			}
		},
		{
			&hf_session_exception_report,
			{
				"Session exception report",
				"ses.exception_report.",
				FT_BOOLEAN, 16,
				NULL,
				SES_EXCEPTION_REPORT,
				"Session exception report",
				HFILL
			}
		},
		{
			&hf_data_separation_function_unit,
			{
				"Data separation function unit",
				"ses.data_sep",
				FT_BOOLEAN, 16,
				NULL,
				DATA_SEPARATION_FUNCTION_UNIT,
				"Data separation function unit",
				HFILL
			}
		},
		{
			&hf_symmetric_synchronize_function_unit,
			{
				"Symmetric synchronize function unit",
				"ses.symm_sync",
				FT_BOOLEAN, 16,
				NULL,
				SYMMETRIC_SYNCHRONIZE_FUNCTION_UNIT,
				"Symmetric synchronize function unit",
				HFILL
			}
		},
		{
			&hf_typed_data_function_unit,
			{
				"Typed data function unit",
				"ses.typed_data",
				FT_BOOLEAN, 16,
				NULL,
				TYPED_DATA_FUNCTION_UNIT,
				"Typed data function unit",
				HFILL
			}
		},
		{
			&hf_exception_function_unit,
			{
				"Exception function unit",
				"ses.exception_data",
				FT_BOOLEAN, 16,
				NULL,
				EXCEPTION_FUNCTION_UNIT,
				"Exception function unit",
				HFILL
			}
		},
		{
			&hf_capability_function_unit,
			{
				"Capability function unit",
				"ses.capability_data",
				FT_BOOLEAN, 16,
				NULL,
				CAPABILITY_DATA_FUNCTION_UNIT,
				"Capability function unit",
				HFILL
			}
		},
		{
			&hf_negotiated_release_function_unit,
			{
				"Negotiated release function unit",
				"ses.negotiated_release",
				FT_BOOLEAN, 16,
				NULL,
				NEGOTIATED_RELEASE_FUNCTION_UNIT,
				"Negotiated release function unit",
				HFILL
			}
		},
		{
			&hf_activity_management_function_unit,
			{
				"Activity management function unit",
				"ses.activity_management",
				FT_BOOLEAN, 16,
				NULL,
				ACTIVITY_MANAGEMENT_FUNCTION_UNIT,
				"Activity management function unit",
				HFILL
			}
		},
		{
			&hf_resynchronize_function_unit,
			{
				"Resynchronize function unit",
				"ses.resynchronize",
				FT_BOOLEAN, 16,
				NULL,
				RESYNCHRONIZE_FUNCTION_UNIT,
				"Resynchronize function unit",
				HFILL
			}
		},
		{
			&hf_major_resynchronize_function_unit,
			{
				"Major resynchronize function unit",
				"ses.major_resynchronize",
				FT_BOOLEAN, 16,
				NULL,
				MAJOR_SYNCHRONIZE_FUNCTION_UNIT,
				"Major resynchronize function unit",
				HFILL
			}
		},
		{
			&hf_minor_resynchronize_function_unit,
			{
				"Minor resynchronize function unit",
				"ses.minor_resynchronize",
				FT_BOOLEAN, 16,
				NULL,
				MINOR_SYNCHRONIZE_FUNCTION_UNIT,
				"Minor resynchronize function unit",
				HFILL
			}
		},
		{
			&hf_expedited_data_resynchronize_function_unit,
			{
				"Expedited data function unit",
				"ses.expedited_data",
				FT_BOOLEAN, 16,
				NULL,
				EXPEDITED_DATA_FUNCTION_UNIT,
				"Expedited data function unit",
				HFILL
			}
		},
		{
			&hf_duplex_function_unit,
			{
				"Duplex functional unit",
				"ses.duplex",
				FT_BOOLEAN, 16,
				NULL,
				DUPLEX_FUNCTION_UNIT,
				"Duplex functional unit",
				HFILL
			}
		},
		{
			&hf_half_duplex_function_unit,
			{
				"Half-duplex functional unit",
				"ses.half_duplex",
				FT_BOOLEAN, 16,
				NULL,
				HALF_DUPLEX_FUNCTION_UNIT,
				"Half-duplex functional unit",
				HFILL
			}
		},
		{
			&hf_proposed_tsdu_maximum_size_i2r,
			{
				"Proposed TSDU Maximum Size, Initiator to Responder",
				"ses.proposed_tsdu_maximum_size_i2r",
				FT_UINT16,
				BASE_DEC,
				NULL,
				0x0,
				"Proposed TSDU Maximum Size, Initiator to Responder",
				HFILL
			}
		},
		{
			&hf_proposed_tsdu_maximum_size_r2i,
			{
				"Proposed TSDU Maximum Size, Responder to Initiator",
				"ses.proposed_tsdu_maximum_size_r2i",
				FT_UINT16,
				BASE_DEC,
				NULL,
				0x0,
				"Proposed TSDU Maximum Size, Responder to Initiator",
				HFILL
			}
		},
		{
			&hf_protocol_version_1,
			{
				"Protocol Version 1",
				"ses.protocol_version1",
				FT_BOOLEAN, 8,
				NULL,
				PROTOCOL_VERSION_1,
				"Protocol Version 1",
				HFILL
			}
		},
		{
			&hf_protocol_version_2,
			{
				"Protocol Version 2",
				"ses.protocol_version2",
				FT_BOOLEAN, 8,
				NULL,
				PROTOCOL_VERSION_2,
				"Protocol Version 2",
				HFILL
			}
		},
		{
			&hf_initial_serial_number,
			{
				"Initial Serial Number",
				"ses.initial_serial_number",
				FT_STRING, BASE_NONE,
				NULL,
				0x0,
				"Initial Serial Number",
				HFILL
			}
		},
		{
			&hf_beginning_of_SSDU,
			{
				"beginning of SSDU",
				"ses.begininng_of_SSDU",
				FT_BOOLEAN, 8,
				NULL,
				BEGINNING_SPDU,
				"beginning of SSDU",
				HFILL
			}
		},
		{
			&hf_end_of_SSDU,
			{
				"end of SSDU",
				"ses.end_of_SSDU",
				FT_BOOLEAN, 8,
				NULL,
				END_SPDU,
				"end of SSDU",
				HFILL
			}
		},
		{
			&hf_release_token_setting,
			{
				"release token setting",
				"ses.release_token_setting",
				FT_UINT8, BASE_HEX,
				VALS(token_setting_vals),
				0xC0,
				"release token setting",
				HFILL
			}
		},
		{
			&hf_major_activity_token_setting,
			{
				"major/activity setting",
				"ses.major_activity_token_setting",
				FT_UINT8, BASE_HEX,
				VALS(token_setting_vals),
				0x30,
				"major/activity token setting",
				HFILL
			}
		},
		{
			&hf_synchronize_minor_token_setting,
			{
				"synchronize-minor token setting",
				"ses.synchronize_minor_token_setting",
				FT_UINT8, BASE_HEX,
				VALS(token_setting_vals),
				0x0C,
				"synchronize-minor token setting",
				HFILL
			}
		},
		{
			&hf_data_token_setting,
			{
				"data token setting",
				"ses.data_token_setting",
				FT_UINT8, BASE_HEX,
				VALS(token_setting_vals),
				0x03,
				"data token setting",
				HFILL
			}
		},
		{
			&hf_serial_number,
			{
				"Serial Number",
				"ses.serial_number",
				FT_STRING, BASE_NONE,
				NULL,
				0x0,
				"Serial Number",
				HFILL
			}
		},
		{
			&hf_calling_session_selector,
			{
				"Calling Session Selector",
				"ses.calling_session_selector",
				FT_BYTES, BASE_NONE,
				NULL,
				0x0,
				"Calling Session Selector",
				HFILL
			}
		},
		{
			&hf_called_session_selector,
			{
				"Called Session Selector",
				"ses.called_session_selector",
				FT_BYTES, BASE_NONE,
				NULL,
				0x0,
				"Called Session Selector",
				HFILL
			}
		},
		{
			&hf_second_serial_number,
			{
				"Second Serial Number",
				"ses.second_serial_number",
				FT_STRING, BASE_NONE,
				NULL,
				0x0,
				"Second Serial Number",
				HFILL
			}
		},
		{
			&hf_second_initial_serial_number,
			{
				"Second Initial Serial Number",
				"ses.second_initial_serial_number",
				FT_STRING, BASE_NONE,
				NULL,
				0x0,
				"Second Initial Serial Number",
				HFILL
			}
		},
		{
			&hf_large_initial_serial_number,
			{
				"Large Initial Serial Number",
				"ses.large_initial_serial_number",
				FT_STRING, BASE_NONE,
				NULL,
				0x0,
				"Large Initial Serial Number",
				HFILL
			}
		},
		{
			&hf_large_second_initial_serial_number,
			{
				"Large Second Initial Serial Number",
				"ses.large_second_initial_serial_number",
				FT_STRING, BASE_NONE,
				NULL,
				0x0,
				"Large Second Initial Serial Number",
				HFILL
			}
		},
		{
			&hf_connect_protocol_options_flags,
			{
				"Flags",
				"ses.connect.flags",
				FT_UINT8,
				BASE_HEX,
				NULL,
				0x0,
				"",
				HFILL
			}
		},
		{
			&hf_version_number_options_flags,

			{
				"Flags",
				"ses.version.flags",
				FT_UINT8,
				BASE_HEX,
				NULL,
				0x0,
				"",
				HFILL
			}
		},

		{
			&hf_token_item_options_flags,

			{
				"Flags",
				"ses.tken_item.flags",
				FT_UINT8,
				BASE_HEX,
				NULL,
				0x0,
				"",
				HFILL
			}
		},

		{
			&hf_enclosure_item_options_flags,

			{
				"Flags",
				"ses.enclosure.flags",
				FT_UINT8,
				BASE_HEX,
				NULL,
				0x0,
				"",
				HFILL
			}
		},
	};

	static gint *ett[] =
	{
		&ett_ses,
		&ett_ses_param,
		&ett_connect_protocol_options_flags,
		&ett_protocol_version_flags,
		&ett_enclosure_item_flags,
		&ett_token_item_flags,
		&ett_ses_req_options_flags,
	};
	module_t *ses_module;
	module_t *clses_module;


	proto_ses = proto_register_protocol(PROTO_STRING_SES, "SES", "ses");
proto_clses = proto_register_protocol(PROTO_STRING_CLSES, "CLSES", "clses");
	proto_register_field_array(proto_ses, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));

	ses_module = prefs_register_protocol(proto_ses, NULL);
	clses_module = prefs_register_protocol(proto_clses,NULL);
/*
	prefs_register_bool_preference(ses_module, "desegment",
	    "Desegment all session packets ",
"Whether the session dissector should desegment all messages spanning multiple SES segments",
	    &ses_desegment);  */

	/*
	 * Register the dissector by name, so other dissectors can
	 * grab it by name rather than just referring to it directly
	 * (you can't refer to it directly from a plugin dissector
	 * on Windows without stuffing it into the Big Transfer Vector).
	 */
	register_dissector("ses", dissect_ses, proto_ses);
	register_dissector("clses",dissect_clses, proto_clses);
}

static gboolean
dissect_ses_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
{
	/* must check that this really is a ses packet */
	int offset = 0;
	guint8 type;
	int len_len;
	guint16 len;

	/* first, check do we have at least 4 bytes (type+length) */
	if (!tvb_bytes_exist(tvb, 0, 4))
		return FALSE;	/* no */

	/* can we regognize session PDU ? Return FALSE if  not */
	/*   get SPDU type */
	type = tvb_get_guint8(tvb, offset);
	/* check SPDU type */
	if (match_strval(type, ses_vals) == NULL)
	{
		return FALSE;  /* no, it isn't a session PDU */
	}

	/*  OK,let's check SPDU length  */
	/*  get length of SPDU */
	len = get_item_len(tvb, offset, &len_len);
	/* do we have enough bytes ? */
	if (!tvb_bytes_exist(tvb, 0, len))
		return FALSE;	/* no */

	dissect_ses(tvb, pinfo, parent_tree);
	return TRUE;
}

void
proto_reg_handoff_ses(void)
{
guchar myname[32];

	/*   find data dissector  */
	data_handle = find_dissector("data");

	/* define sub dissector */
	pres_handle = find_dissector("pres");

	strcpy(myname,"cotp");
	/* add our session dissector to cotp dissector list */
	heur_dissector_add(myname, dissect_ses_heur, proto_ses);

	strcpy(myname,"cotp_is");
	/* added in for IEC GSSE, need to dissect session even if inactive */
	heur_dissector_add(myname, dissect_ses_heur, proto_ses_is);

}
/* packet-isopres.c
*
* Routine to dissect ISO 8823-1 OSI Presentation Protocol packets
*
* $Id: packet-isopres.c,v 1.0 2004/012/12  Exp $
*
* Herbert Falk <herb@xxxxxxxxxxxx>
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@xxxxxxxxxxxx>
* 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
* 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 <glib.h>
#include <epan/packet.h>

#include <stdio.h>
#include <string.h>
#include <epan/conversation.h>
#include <epan/tvbuff.h>
#include "prefs.h"
#include "reassemble.h"
#include "tap.h"
#include "packet-frame.h"

#include "packet-isopres.h"
#include "asn1.h"
#include "asn1_helper.h"
#include "packet-frame.h"

#include <epan/strutil.h>





/* find the dissector for data */
static dissector_handle_t adata_handle;
static dissector_handle_t acse_handle;
static dissector_handle_t mms_handle;


static int proto_isopres          = -1;
static int proto_clisopres          = -1;
static gint ett_isopres           = -1;


#define ISO_PRES_CO				0			/*Connection Oriented Presentation	*/
#define ISO_PRES_CL				1			/*Connectionless Presentation	*/
static int hf_isopres_type        = -1;

#define CL_PRES_SEQ 0x30 /*sequence code for Connectionless Presentation */
#define CO_PRES_PDATA	(ASN1H_APP | ASN1H_CONSTRUCT |1)		/*PDATA			*/

#define CL_PROTO_VER	(ASN1H_CTX | 0 )
#define CALL_PSEL		(ASN1H_CTX | 1 )
#define CALLED_PSEL		(ASN1H_CTX | 2 )
#define P_CONTEXT_LIST	(ASN1H_CTX | ASN1H_CONSTRUCT| 4)
#define P_FULLY_ENCODED_DATA (ASN1H_APP | ASN1H_CONSTRUCT | 1)


#define PROTO_STRING_PRES	"ISO 8823 Presentation"
#define PROTO_STRING_CLPRES  "ISO 8823 CL Presentation"
#define ASN1H_SEQ_TAG	(ASN1H_UNI | ASN1H_CONSTRUCT | ASN1_SEQ)
#define ASN1H_INTEGER_ID (ASN1H_UNI | ASN1_INT) /*ASN1_INT is defined in ASN1.h*/ #define ASN1H_OBJECT_ID (ASN1H_UNI | ASN1_OJI) /*ASN1_OJI is defined in ASN1.h*/

 proto_item *data_item;
static guchar PSEL_PROTO [] = "PROTOCOL VERSION: ";
static guchar CALL_PSEL_TEXT [] = "Calling PSEL: ";
static guchar CALLED_PSEL_TEXT [] = "Called PSEL: ";
static guchar PCTXT_ID[] = "PCTXT ID: ";
static guchar AS_ID[] = "Abstract Syntax: ";
static guchar TS_ID[] = "Transfer Syntax: ";
static guchar TS_NAME[] = "Transfer ID: ";
static gboolean desegment_pres = TRUE;


static gboolean dissect_isopres_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree);


/************************* Beginning of Connectionless Presentation Tables */

extern ASN1_TBL_ENTRY pres_ts_seq[2];

static ASN1_TBL_ENTRY end_of_pres[]={
	{ASN1H_END_OF_TABLE	,NULL,NULL,NULL}
};

static ASN1_TBL_ENTRY pdv_list_start[] ={
{ASN1H_OBJECT_ID,TS_ID, ASN1H_OBJID_DISPLAY, pdv_list_start}, /*Transfer syntax name */
	{ASN1H_INTEGER_ID,PCTXT_ID,ASN1H_INTEGER_DISPLAY,pdv_list_start},
	{ASN1H_SEQ_TAG,NULL, ASN1H_SKIP_TAG_AND_LENGTH, pdv_list_start},
{(ASN1H_CTX | ASN1H_CONSTRUCT),"Single ASN1 Type", ASN1H_DO_NOT_PROCESS,end_of_pres},
	{(ASN1H_CTX | 1),"Octet Aligned",ASN1H_DO_NOT_PROCESS,end_of_pres},
	{(ASN1H_CTX | 2),"Arbitray",ASN1H_DO_NOT_PROCESS, end_of_pres},
	{ASN1H_END_OF_TABLE	,NULL,NULL,NULL}
};





static ASN1_TBL_ENTRY fully_encoded_data [] = {
	{ASN1H_SEQ_TAG,"PDV LIST", ASN1H_SKIP_TAG_AND_LENGTH, pdv_list_start},
	{ASN1H_END_OF_TABLE	,NULL,NULL,NULL}
};

static ASN1_TBL_ENTRY pres_ctxt_list[] ={
	{ASN1H_INTEGER_ID,PCTXT_ID,ASN1H_INTEGER_DISPLAY,pres_ctxt_list},
	{ASN1H_OBJECT_ID,AS_ID, ASN1H_OBJID_DISPLAY, pres_ctxt_list},
	{ASN1H_SEQ_TAG,"TS Names", ASN1H_INDENT, pres_ts_seq},
	{ASN1H_END_OF_TABLE	,NULL,NULL,NULL}
	};


static ASN1_TBL_ENTRY pres_ctxt_list_start[]={
		{ASN1H_SEQ_TAG, NULL, ASN1H_INDENT, pres_ctxt_list },
		{ASN1H_END_OF_TABLE	,NULL,NULL,NULL}
		};

static ASN1_TBL_ENTRY pres_ts_seq[]={
	{ASN1H_OBJECT_ID, TS_ID, ASN1H_OBJID_DISPLAY, pres_ctxt_list_start},
	{ASN1H_END_OF_TABLE	,NULL,NULL,NULL}
};

static ASN1_TBL_ENTRY isopres_main[]={
		{CL_PROTO_VER, PSEL_PROTO, ASN1H_INTEGER_DISPLAY, isopres_main },
		{CALL_PSEL, CALL_PSEL_TEXT, ASN1H_OCTETSTRING_DISPLAY, isopres_main },
{CALLED_PSEL, CALLED_PSEL_TEXT, ASN1H_OCTETSTRING_DISPLAY, isopres_main }, {P_FULLY_ENCODED_DATA,"FULL CODED DATA", ASN1H_INDENT, fully_encoded_data}, {P_CONTEXT_LIST,"P_CTXT_LIST:", ASN1H_INDENT, pres_ctxt_list_start}, /*don't display the p_context list */
		{ASN1H_END_OF_TABLE	,NULL,NULL,NULL}
		};

/************************* End of connectionless presentation tables 			*/


/************************ Beginning of Connection Oriented Presentation Tables */

static ASN1_TBL_ENTRY pdata_list_start[] ={
	{ASN1H_SEQ_TAG,NULL, ASN1H_SKIP_TAG_AND_LENGTH, pdata_list_start},
	{ASN1H_INTEGER_ID,PCTXT_ID,ASN1H_INTEGER_DISPLAY,pdv_list_start},
};

static ASN1_TBL_ENTRY iso_co_pres_main[]={
		{CO_PRES_PDATA, "PDATA", ASN1H_TBL_SEQ , pdata_list_start},
		{ASN1H_END_OF_TABLE	,NULL,NULL,NULL}
		};



static int
get_item_len(tvbuff_t *tvb, int offset, int *len_len)
{
	guint16 len;

	len = tvb_get_guint8(tvb, offset);
	if(len == 0x82)
	{
		len = tvb_get_ntohs(tvb, offset+1);
		*len_len = 3;
	}
	else
		*len_len = 1;
	return len;
}

static void
dissect_cl_presentation(ASN1_SCK *asn1, guchar *display_string, guint32 *total_len, guint32 init_offset, proto_item *subtree, tvbuff_t *tvb)
{
guint tag;
guint total_length;
gboolean not_end = FALSE;
guchar *mysel;
guint i;
guchar display_text[32];
gint ret;
gboolean def;
guint init_offset1;

asn1h_do_parse (asn1, isopres_main, "", total_len, asn1->offset, subtree, tvb, ASN1H_TBL_SET);
}

static void
dissect_co_presentation(ASN1_SCK *asn1, guchar *display_string, guint32 *total_len, guint32 init_offset, proto_item *subtree, tvbuff_t *tvb)
{
guint tag;
guint total_length;
gboolean not_end = FALSE;
guchar *mysel;
guint i;
guchar display_text[32];
gint ret;
gboolean def;
guint init_offset1;

asn1h_do_parse (asn1, iso_co_pres_main, "", total_len, asn1->offset, subtree, tvb, ASN1H_TBL_SEQ);
}

static void
dissect_isopres(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{

	int offset = 0;
	guint8 type;
	proto_item *ti;
    proto_tree  *isopres_tree;
	guint tag;
	int ret;
	gboolean def;
	ASN1_SCK asn1;
	guint total_length;
	tvbuff_t *next_tvb;
	guint saved_length;
	guint8 *cont_buffer;




	if (check_col(pinfo->cinfo, COL_PROTOCOL))
		col_set_str(pinfo->cinfo, COL_PROTOCOL, "PRES");
  	if (check_col(pinfo->cinfo, COL_INFO))
  		col_clear(pinfo->cinfo, COL_INFO);

    if (!tree)
	{
      asn1_open(&asn1, tvb, offset);
      ret = asn1_tag_decode (&asn1, &tag);
	  if(tag ==CL_PRES_SEQ)
	  {
	    if(adata_handle)
	      call_dissector(adata_handle, next_tvb, pinfo, tree);
	  }
	  else
	  {
		if (tag == 0x61)
		{
		  if(mms_handle)
		      call_dissector(mms_handle, next_tvb, pinfo, tree);
		}
	  }

	}

	if (tree) {
	  /*open the ASN1 tree, offset is tvb->offset */
      asn1_open(&asn1, tvb, offset);

    	ret = asn1_tag_decode (&asn1, &tag);


	  if (tag ==CL_PRES_SEQ)
	    {
		ret = asn1_length_decode (&asn1, &def, &total_length);
		ti = proto_tree_add_item(tree, proto_clisopres, tvb, 0, -1, FALSE);
  		data_item = proto_item_add_subtree(ti, ett_isopres);

dissect_cl_presentation (&asn1, tree, &total_length, offset, data_item, tvb);
		 /*call to parse AUNIT-DATA */

	    proto_item_set_end(ti, tvb, offset);

		if (tvb_reported_length_remaining(tvb, asn1.offset) > 0)
			next_tvb = tvb_new_subset(tvb, asn1.offset, -1, -1);

		 if(adata_handle)
		     call_dissector(adata_handle, next_tvb, pinfo, tree);



		}
	  else
	    {
#if 0
		if(tvb->length < tvb->reported_length)
			tvb->length = tvb->reported_length;
#endif
		asn1.offset -=1;			/*move back one byte		*/

		switch (tag)
		  {
		  case 0x61:

	     	ti = proto_tree_add_item(tree, proto_isopres, tvb, 0, -1, FALSE);

	 		data_item = proto_item_add_subtree(ti, ett_isopres);
			total_length = tvb->reported_length;

			saved_length = total_length;

dissect_co_presentation (&asn1, tree, &total_length, offset, data_item, tvb);

/*			ret = asn1h_tag_get (&asn1, &tag);
	        proto_item_set_end(ti, tvb, offset); */

		    ret = asn1h_length_decode (&asn1, &def, &total_length);

			if (tvb_reported_length_remaining(tvb, asn1.offset) > 0)
			  next_tvb = tvb_new_subset(tvb, asn1.offset, -1, total_length);


			if(mms_handle)
		      call_dissector(mms_handle, next_tvb, pinfo, tree);
			break;

		  default:
		    break;
		  }
        }

	}
}

void
proto_register_isopres(void)
{
	static hf_register_info hf[] =
	{

		{
			&hf_isopres_type,
			{
				"SPDU Type",
				"ses.type",
				FT_UINT8,
				BASE_DEC,
				NULL,
				0x0,
				"", HFILL
			}
		},
	};

	static gint *ett[] =
	{
		&ett_isopres,
	};
	module_t *pres_module;


proto_isopres = proto_register_protocol(PROTO_STRING_PRES, "PRES", "pres"); proto_clisopres = proto_register_protocol(PROTO_STRING_CLPRES, "CLPR", "prcl");
	proto_register_field_array(proto_isopres, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));

	pres_module = prefs_register_protocol(proto_isopres, NULL);
/*
	prefs_register_bool_preference(ses_module, "desegment",
	    "Desegment all session packets ",
"Whether the session dissector should desegment all messages spanning multiple SES segments",
	    &ses_desegment);  */

	/*
	 * Register the dissector by name, so other dissectors can
	 * grab it by name rather than just referring to it directly
	 * (you can't refer to it directly from a plugin dissector
	 * on Windows without stuffing it into the Big Transfer Vector).
	 */
/*	register_dissector("pres", dissect_isopres, proto_isopres); */
	register_dissector("pres", dissect_isopres_heur, proto_isopres);
}


static guint
get_pres_pdu_len(tvbuff_t *tvb, int offset)
{
	ASN1_SCK asn1;
	guint tag;
	int ret;
	guint total_length;
	gboolean def;


	/*  OK,let's check SPDU length  */
	/*  get length of SPDU */
    asn1_open(&asn1, tvb, offset);
   	ret = asn1_tag_decode (&asn1, &tag);
	ret = asn1_length_decode (&asn1, &def, &total_length);
	return(total_length);
}


/*static gboolean*/
static gboolean
dissect_isopres_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
{
	/* must check that this really is a ses packet */
	int offset = 0;
	guint8 type;
	int len_len;
	guint16 len;
	ASN1_SCK asn1;
	guint tag;
	int ret;
	guint total_length;
	gboolean def;



	/* can we regognize session PDU ? Return FALSE if  not */
	/*   get SPDU type */
	type = tvb_get_guint8(tvb, offset);
	/* check SPDU type */

	/*  OK,let's check SPDU length  */
	/*  get length of SPDU */
    asn1_open(&asn1, tvb, offset);
   	ret = asn1_tag_decode (&asn1, &tag);
	ret = asn1_length_decode (&asn1, &def, &total_length);



	/* do we have enough bytes ? */

	if (!tvb_bytes_exist(tvb, 0, total_length))
	{
		desegment_pres = TRUE;
		pinfo->can_desegment = 2;
        tcp_dissect_pdus(tvb, pinfo, parent_tree,
                   desegment_pres,        /* desegment or not   */
                   8,                     /* magic + length */
                   get_pres_pdu_len,      /* use first 4, calc data len */
                   dissect_isopres); /* the naive dissector */
		return(0);
	}
	else
	{
	  	dissect_isopres(tvb, pinfo, parent_tree);
		return(1);
	}
#if 0
	  	dissect_isopres(tvb, pinfo, parent_tree);
		return(1);
#endif
}

void
proto_reg_handoff_isopress(void)
{
guchar myname[32];

	/*   find data dissector  */
    strcpy (myname, "acse");
	acse_handle = find_dissector(myname);

    strcpy(myname, "mms");
	mms_handle = find_dissector(myname);

	/* define sub dissector */
	strcpy(myname, "adata");
	adata_handle = find_dissector(myname);

	strcpy(myname,"ses");
	/* add our session dissector to cotp dissector list */
/*	heur_dissector_add(myname, dissect_isopres_heur, proto_isopres); */

}
<< refresh_mms >>

_________________________________________________________________
MSN 8 with e-mail virus protection service: 2 months FREE* http://join.msn.com/?page=features/virus