Ethereal-dev: [ethereal-dev] Code for ISAKMP, GRE, and PPTP

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

From: Brad Robel-Forrest <bradr@xxxxxxxxxxxxxx>
Date: Tue, 8 Jun 1999 17:15:54 -0700 (PDT)
Here is the diff and code to enable the parsed display of ISAKMP, GRE, and
PPTP packets. I've stubbed out the call that could be made from the GRE
dissection code to dump possible PPP encapsulated packets until the
dissect code for PPP can accept offsets (which I think Jeff Jahr
<jjahr@xxxxxxxxxxxxxx> has recently mailed a patch for).

Please let me know of any problems or anything else I can provide to have
this added to the CVS tree. The diff is based on an update I did today, so
everything should be current.

  -brad

Attachment: ethereal.diff
Description: ethereal.diff

/* packet-gre.c
 * Routines for the Internet Security Association and Key Management Protocol (ISAKMP)
 * Brad Robel-Forrest <brad.robel-forrest@xxxxxxxxxxxxxx>
 *
 * $Id$
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxx>
 * 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

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#include <stdio.h>
#include <netinet/in.h>
#include <glib.h>
#include "packet.h"

#define NUM_PROTO_TYPES	5
#define proto2str(t)	\
  ((t < NUM_PROTO_TYPES) ? prototypestr[t] : "UNKNOWN-PROTO-TYPE")

static const char *prototypestr[NUM_PROTO_TYPES] = {
  "RESERVED",
  "ISAKMP",
  "IPSEC_AH",
  "IPSEC_ESP",
  "IPCOMP"
};

#define NUM_ATT_TYPES	13
#define atttype2str(t)	\
  ((t < NUM_ATT_TYPES) ? atttypestr[t] : "UNKNOWN-ATTRIBUTE-TYPE")

static const char *atttypestr[NUM_ATT_TYPES] = {
  "UNKNOWN-ATTRIBUTE-TYPE",
  "SA-Life-Type",
  "SA-Life-Duration",
  "Group-Description",
  "Encapsulation-Mode",
  "Authentication-Algorithm",
  "Key-Length",
  "Key-Rounds",
  "Compress-Dictinary-Size",
  "Compress-Private-Algorithm"
  "UNKNOWN-ATTRIBUTE-TYPE",
  "Oakley-Life",
  "Oakley-Value",
  "Oakley-Life-Duration"
};

#define NUM_TRANS_TYPES	2
#define trans2str(t)	\
  ((t < NUM_TRANS_TYPES) ? transtypestr[t] : "UNKNOWN-TRANS-TYPE")

static const char *transtypestr[NUM_TRANS_TYPES] = {
  "RESERVED",
  "KEY_IKE"
};

#define NUM_ID_TYPES	12
#define id2str(t)	\
  ((t < NUM_ID_TYPES) ? idtypestr[t] : "UNKNOWN-ID-TYPE")

static const char *idtypestr[NUM_ID_TYPES] = {
  "RESERVED",
  "IPV4_ADDR",
  "FQDN",
  "USER_FQDN",
  "IPV4_ADDR_SUBNET",
  "IPV6_ADDR",
  "IPV6_ADDR_SUBNET",
  "IPV4_ADDR_RANGE",
  "IPV6_ADDR_RANGE",
  "DER_ASN1_DN",
  "DER_ASN1_GN",
  "KEY_ID"
};

struct isakmp_hdr {
  guint8	icookie[8];
  guint8	rcookie[8];
  guint8	next_payload;
  guint8	version;
  guint8	exch_type;
  guint8	flags;
#define E_FLAG		0x01
#define C_FLAG		0x02
#define A_FLAG		0x04
  guint32	message_id;
  guint32	length;
};

struct sa_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
  guint32	doi;
  guint32	situation;
};

struct proposal_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
  guint8	proposal_num;
  guint8	protocol_id;
  guint8	spi_size;
  guint8	num_transforms;
};

struct trans_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
  guint8	transform_num;
  guint8	transform_id;
  guint16	reserved2;
};

#define TRANS_LEN(p)	(ntohs(((struct trans_hdr *)(p))->length))

struct ke_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
};

struct id_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
  guint8	id_type;
  guint8	protocol_id;
  guint16	port;
};

struct cert_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
  guint8	cert_enc;
};

struct certreq_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
  guint8	cert_type;
};

struct hash_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
};

struct sig_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
};

struct nonce_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
};

struct notif_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
  guint32	doi;
  guint8	protocol_id;
  guint8	spi_size;
  guint16	msgtype;
};

struct delete_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
  guint32	doi;
  guint8	protocol_id;
  guint8	spi_size;
  guint16	num_spis;
};

struct vid_hdr {
  guint8	next_payload;
  guint8	reserved;
  guint16	length;
};

static void dissect_none(const u_char *, int, frame_data *, proto_tree *);
static void dissect_sa(const u_char *, int, frame_data *, proto_tree *);
static void dissect_proposal(const u_char *, int, frame_data *, proto_tree *);
static void dissect_transform(const u_char *, int, frame_data *, proto_tree *);
static void dissect_key_exch(const u_char *, int, frame_data *, proto_tree *);
static void dissect_id(const u_char *, int, frame_data *, proto_tree *);
static void dissect_cert(const u_char *, int, frame_data *, proto_tree *);
static void dissect_certreq(const u_char *, int, frame_data *, proto_tree *);
static void dissect_hash(const u_char *, int, frame_data *, proto_tree *);
static void dissect_sig(const u_char *, int, frame_data *, proto_tree *);
static void dissect_nonce(const u_char *, int, frame_data *, proto_tree *);
static void dissect_notif(const u_char *, int, frame_data *, proto_tree *);
static void dissect_delete(const u_char *, int, frame_data *, proto_tree *);
static void dissect_vid(const u_char *, int, frame_data *, proto_tree *);

static const char *payloadtype2str(guint8);
static const char *exchtype2str(guint8);
static const char *doitype2str(guint32);
static const char *msgtype2str(guint16);
static const char *situation2str(guint32);
static const char *value2str(guint16, guint16);
static const char *num2str(const guint8 *, guint16);

#define NUM_LOAD_TYPES		14
#define loadtype2str(t)	\
  ((t < NUM_LOAD_TYPES) ? strfuncs[t].str : "Unknown payload type")

static struct strfunc {
  const char *	str;
  void          (*func)(const u_char *, int, frame_data *, proto_tree *);
} strfuncs[NUM_LOAD_TYPES] = {
  {"NONE",			dissect_none      },
  {"Security Association",	dissect_sa        },
  {"Proposal",			dissect_proposal  },
  {"Transform",			dissect_transform },
  {"Key Exchange",		dissect_key_exch  },
  {"Identification",		dissect_id        },
  {"Certificate",		dissect_cert      },
  {"Certificate Request",	dissect_certreq   },
  {"Hash",			dissect_hash      },
  {"Signature",			dissect_sig       },
  {"Nonce",			dissect_nonce     },
  {"Notification",		dissect_notif     },
  {"Delete",			dissect_delete    },
  {"Vendor ID",			dissect_vid       }
};

void dissect_isakmp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  
  struct isakmp_hdr *	hdr = (struct isakmp_hdr *)(pd + offset);
  guint32		len;
  
  if (check_col(fd, COL_PROTOCOL))
    col_add_str(fd, COL_PROTOCOL, "ISAKMP");
  
  len = ntohl(hdr->length);
  
  if (check_col(fd, COL_INFO))
    col_add_fstr(fd, COL_INFO, "%s", exchtype2str(hdr->exch_type));
  
  if (fd->cap_len > offset && tree) {
    proto_item *	ti;
    proto_tree *	isakmp_tree;
    
    ti = proto_tree_add_item(tree, offset, len,
			     "Internet Security Association and Key Management Protocol");
    isakmp_tree = proto_tree_new();
    proto_item_add_subtree(ti, isakmp_tree, ETT_ISAKMP);
    
    proto_tree_add_item(isakmp_tree, offset, sizeof(hdr->icookie),
			"Initiator cookie");
    offset += sizeof(hdr->icookie);
    
    proto_tree_add_item(isakmp_tree, offset, sizeof(hdr->rcookie),
			"Responder cookie");
    offset += sizeof(hdr->rcookie);

    proto_tree_add_item(isakmp_tree, offset, sizeof(hdr->next_payload),
			"Next payload: %s (%u)",
			payloadtype2str(hdr->next_payload), hdr->next_payload);
    offset += sizeof(hdr->next_payload);

    proto_tree_add_item(isakmp_tree, offset, sizeof(hdr->version),
			"Version: %u.%u",
			hi_nibble(hdr->version), lo_nibble(hdr->version));
    offset += sizeof(hdr->version);
    
    proto_tree_add_item(isakmp_tree, offset, sizeof(hdr->exch_type),
			"Exchange type: %s (%u)",
			exchtype2str(hdr->exch_type), hdr->exch_type);
    offset += sizeof(hdr->exch_type);
    
    {
      proto_item *	fti;
      proto_tree *	ftree;
      
      fti   = proto_tree_add_item(isakmp_tree, offset, sizeof(hdr->flags), "Flags");
      ftree = proto_tree_new();
      proto_item_add_subtree(fti, ftree, ETT_ISAKMP_FLAGS);
      
      proto_tree_add_item(ftree, offset, 1, "%s",
			  decode_boolean_bitfield(hdr->flags, E_FLAG, sizeof(hdr->flags)*8,
						  "Encryption", "No encryption"));
      proto_tree_add_item(ftree, offset, 1, "%s",
			  decode_boolean_bitfield(hdr->flags, C_FLAG, sizeof(hdr->flags)*8,
						  "Commit", "No commit"));
      proto_tree_add_item(ftree, offset, 1, "%s",
			  decode_boolean_bitfield(hdr->flags, A_FLAG, sizeof(hdr->flags)*8,
						  "Authentication", "No authentication"));
      offset += sizeof(hdr->flags);
    }

    proto_tree_add_item(isakmp_tree, offset, sizeof(hdr->message_id), "Message ID");
    offset += sizeof(hdr->message_id);
    
    proto_tree_add_item(isakmp_tree, offset, sizeof(hdr->length),
			"Length: %u", len);
    offset += sizeof(hdr->length);

    if (hdr->next_payload < NUM_LOAD_TYPES)
      (*strfuncs[hdr->next_payload].func)(pd, offset, fd, isakmp_tree);
    else
      dissect_data(pd, offset, fd, isakmp_tree);
  }
}

static void
dissect_none(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
}

static void
dissect_sa(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct sa_hdr *	hdr	  = (struct sa_hdr *)(pd + offset);
  guint16		length	  = ntohs(hdr->length);
  guint32		doi	  = ntohl(hdr->doi);
  guint32		situation = ntohl(hdr->situation);
  proto_item *		ti	  = proto_tree_add_item(tree, offset, length, "Security Association payload");
  proto_tree *		ntree	  = proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, sizeof(doi),
		      "Domain of interpretation: %s (%u)",
		      doitype2str(doi), doi);
  offset += sizeof(doi);
  
  proto_tree_add_item(ntree, offset, sizeof(situation),
		      "Situation: %s (%u)",
		      situation2str(situation), situation);
  offset += sizeof(situation);
  
  dissect_proposal(pd, offset, fd, ntree);
  offset += (length - sizeof(*hdr));
  
  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_proposal(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct proposal_hdr *	hdr	= (struct proposal_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Proposal payload");
  proto_tree *		ntree	= proto_tree_new();
  guint8		i;
  
  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->proposal_num),
		      "Proposal number: %u", hdr->proposal_num);
  offset += sizeof(hdr->proposal_num);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->protocol_id),
		      "Protocol ID: %s (%u)",
		      proto2str(hdr->protocol_id), hdr->protocol_id);
  offset += sizeof(hdr->protocol_id);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->spi_size),
		      "SPI size: %u", hdr->spi_size);
  offset += sizeof(hdr->spi_size);
  
  if (hdr->spi_size) {
    proto_tree_add_item(ntree, offset, hdr->spi_size, "SPI");
    offset += hdr->spi_size;
  }

  proto_tree_add_item(ntree, offset, sizeof(hdr->num_transforms),
		      "Number of transforms: %u", hdr->num_transforms);
  offset += sizeof(hdr->num_transforms);
  
  for (i = 0; i < hdr->num_transforms; ++i) {
    dissect_transform(pd, offset, fd, ntree);
    offset += TRANS_LEN(pd+offset);
  }

  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_transform(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct trans_hdr *	hdr	= (struct trans_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Transform payload");
  proto_tree *		ntree	= proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->transform_num),
		      "Transform number: %u", hdr->transform_num);
  offset += sizeof(hdr->transform_num);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->transform_id),
		      "Transform ID: %s (%u)",
		      trans2str(hdr->transform_id), hdr->transform_id);
  offset += sizeof(hdr->transform_id) + sizeof(hdr->reserved2);
  
  length -= sizeof(*hdr);
  while (length) {
    guint16 type    = ntohs(*(guint16 *)(pd + offset)) & 0x7fff;
    guint16 val_len = ntohs(*(guint16 *)(pd + offset + 2));
    
    if (pd[offset] & 0xf0) {
      proto_tree_add_item(ntree, offset, 4,
			  "%s (%u): %s (%u)",
			  atttype2str(type), type,
			  value2str(type, val_len), val_len);
      offset += 4;
      length -= 4;
    }
    else {
      guint16	pack_len = 4 + val_len;
      
      proto_tree_add_item(ntree, offset, pack_len,
			  "%s (%u): %s",
			  atttype2str(type), type,
			  num2str(pd + offset + 4, val_len));
      offset += pack_len;
      length -= pack_len;
    }
  }

  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_key_exch(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct ke_hdr *	hdr	= (struct ke_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Key Exchange payload");
  proto_tree *		ntree	= proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, length - sizeof(*hdr), "Key Exchange Data");
  offset += (length - sizeof(*hdr));
  
  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_id(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct id_hdr *	hdr	= (struct id_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Identification payload");
  proto_tree *		ntree	= proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->id_type),
		      "ID type: %s (%u)", id2str(hdr->id_type), hdr->id_type);
  offset += sizeof(hdr->id_type);

  proto_tree_add_item(ntree, offset, sizeof(hdr->protocol_id),
		      "Protocol ID: %u", hdr->protocol_id);
  offset += sizeof(hdr->protocol_id);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->port),
		      "Port: %u", ntohs(hdr->port));
  offset += sizeof(hdr->port);
  
  switch (hdr->id_type) {
    case 1:
    case 4:
      proto_tree_add_item(ntree, offset, length-sizeof(*hdr),
			  "Identification data: %s", ip_to_str(pd+offset));
      break;
    case 2:
    case 3:
      proto_tree_add_item(ntree, offset, length-sizeof(*hdr),
			  "Identification data: %s", (char *)(pd+offset));
      break;
    default:
      proto_tree_add_item(ntree, offset, length - sizeof(*hdr), "Identification Data");
  }
  offset += (length - sizeof(*hdr));
  
  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_cert(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct cert_hdr *	hdr	= (struct cert_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Certificate payload");
  proto_tree *		ntree	= proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->cert_enc),
		      "Certificate encoding: %u", hdr->cert_enc);
  offset += sizeof(hdr->cert_enc);

  proto_tree_add_item(ntree, offset, length - sizeof(*hdr), "Certificate Data");
  offset += (length - sizeof(*hdr));
  
  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_certreq(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct certreq_hdr *	hdr	= (struct certreq_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Certificate Request payload");
  proto_tree *		ntree	= proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->cert_type),
		      "Certificate type: %u", hdr->cert_type);
  offset += sizeof(hdr->cert_type);

  proto_tree_add_item(ntree, offset, length - sizeof(*hdr), "Certificate Authority");
  offset += (length - sizeof(*hdr));
  
  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_hash(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct hash_hdr *	hdr	= (struct hash_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Hash payload");
  proto_tree *		ntree	= proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, length - sizeof(*hdr), "Hash Data");
  offset += (length - sizeof(*hdr));
  
  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_sig(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct sig_hdr *	hdr	= (struct sig_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Signature payload");
  proto_tree *		ntree	= proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, length - sizeof(*hdr), "Signature Data");
  offset += (length - sizeof(*hdr));
  
  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_nonce(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct nonce_hdr *	hdr	= (struct nonce_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Nonce payload");
  proto_tree *		ntree	= proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, length - sizeof(*hdr), "Nonce Data");
  offset += (length - sizeof(*hdr));
  
  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_notif(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct notif_hdr *	hdr	= (struct notif_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  guint32		doi	= ntohl(hdr->doi);
  guint16		msgtype = ntohs(hdr->msgtype);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Notification payload");
  proto_tree *		ntree	= proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, sizeof(doi),
		      "Domain of Interpretation: %s (%u)", doitype2str(doi), doi);
  offset += sizeof(doi);

  proto_tree_add_item(ntree, offset, sizeof(hdr->protocol_id),
		      "Protocol ID: %s (%u)",
		      proto2str(hdr->protocol_id), hdr->protocol_id);
  offset += sizeof(hdr->protocol_id);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->spi_size),
		      "SPI size: %u", hdr->spi_size);
  offset += sizeof(hdr->spi_size);
  
  proto_tree_add_item(ntree, offset, sizeof(msgtype),
		      "Message type: %s (%u)", msgtype2str(msgtype), msgtype);
  offset += sizeof(msgtype);

  if (hdr->spi_size) {
    proto_tree_add_item(ntree, offset, hdr->spi_size, "Security Parameter Index");
    offset += hdr->spi_size;
  }

  if (length - sizeof(*hdr)) {
    proto_tree_add_item(ntree, offset, length - sizeof(*hdr) - hdr->spi_size,
			"Notification Data");
    offset += (length - sizeof(*hdr) - hdr->spi_size);
  }
  
  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_delete(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct delete_hdr *	hdr	 = (struct delete_hdr *)(pd + offset);
  guint16		length	 = ntohs(hdr->length);
  guint32		doi	 = ntohl(hdr->doi);
  guint16		num_spis = ntohs(hdr->num_spis);
  proto_item *		ti	 = proto_tree_add_item(tree, offset, length, "Delete payload");
  proto_tree *		ntree	 = proto_tree_new();
  guint16		i;
  
  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, sizeof(doi),
		      "Domain of Interpretation: %s (%u)", doitype2str(doi), doi);
  offset += sizeof(doi);

  proto_tree_add_item(ntree, offset, sizeof(hdr->protocol_id),
		      "Protocol ID: %s (%u)",
		      proto2str(hdr->protocol_id), hdr->protocol_id);
  offset += sizeof(hdr->protocol_id);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->spi_size),
		      "SPI size: %u", hdr->spi_size);
  offset += sizeof(hdr->spi_size);
  
  proto_tree_add_item(ntree, offset, num_spis,
		      "Number of SPIs: %u", num_spis);
  offset += sizeof(hdr->num_spis);
  
  for (i = 0; i < num_spis; ++i) {
    proto_tree_add_item(ntree, offset, hdr->spi_size,
			"SPI (%d)", i);
    offset += hdr->spi_size;
  }

  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static void
dissect_vid(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct vid_hdr *	hdr	= (struct vid_hdr *)(pd + offset);
  guint16		length	= ntohs(hdr->length);
  proto_item *		ti	= proto_tree_add_item(tree, offset, length, "Vendor ID payload");
  proto_tree *		ntree	= proto_tree_new();

  proto_item_add_subtree(ti, ntree, ETT_ISAKMP_PAYLOAD);
  
  proto_tree_add_item(ntree, offset, sizeof(hdr->next_payload),
		      "Next payload: %s (%u)",
		      payloadtype2str(hdr->next_payload), hdr->next_payload);
  offset += sizeof(hdr->next_payload) * 2;
  
  proto_tree_add_item(ntree, offset, sizeof(length),
		      "Length: %u", length);
  offset += sizeof(length);
  
  proto_tree_add_item(ntree, offset, length - sizeof(*hdr), "Vendor ID");
  offset += (length - sizeof(*hdr));
  
  if (hdr->next_payload < NUM_LOAD_TYPES)
    (*strfuncs[hdr->next_payload].func)(pd, offset, fd, tree);
  else
    dissect_data(pd, offset, fd, tree);
}

static const char *
payloadtype2str(guint8 type) {

  if (type < NUM_LOAD_TYPES) return strfuncs[type].str;
  if (type < 128)            return "RESERVED";
  if (type < 256)            return "Private USE";

  return "Huh? You should never see this! Shame on you!";
}

static const char *
exchtype2str(guint8 type) {

#define NUM_EXCHSTRS	6
  static const char * exchstrs[NUM_EXCHSTRS] = {
    "NONE",
    "Base",
    "Identity Protection",
    "Authentication Only",
    "Aggressive",
    "Informational"
  };
  
  if (type < NUM_EXCHSTRS) return exchstrs[type];
  if (type < 32)           return "ISAKMP Future Use";
  if (type < 240)          return "DOI Specific Use";
  if (type < 256)          return "Private Use";
  
  return "Huh? You should never see this! Shame on you!";
}

static const char *
doitype2str(guint32 type) {
  if (type == 1) return "IPSEC";
  return "Unknown DOI Type";
}

static const char *
msgtype2str(guint16 type) {

#define NUM_PREDEFINED	31
  static const char *msgs[NUM_PREDEFINED] = {
    "<UNKNOWN>",
    "INVALID-PAYLOAD-TYPE",
    "DOI-NOT-SUPPORTED",
    "SITUATION-NOT-SUPPORTED",
    "INVALID-COOKIE",
    "INVALID-MAJOR-VERSION",
    "INVALID-MINOR-VERSION",
    "INVALID-EXCHANGE-TYPE",
    "INVALID-FLAGS",
    "INVALID-MESSAGE-ID",
    "INVALID-PROTOCOL-ID",
    "INVALID-SPI",
    "INVALID-TRANSFORM-ID",
    "ATTRIBUTES-NOT-SUPPORTED",
    "NO-PROPOSAL-CHOSEN",
    "BAD-PROPOSAL-SYNTAX",
    "PAYLOAD-MALFORMED",
    "INVALID-KEY-INFORMATION",
    "INVALID-ID-INFORMATION",
    "INVALID-CERT-ENCODING",
    "INVALID-CERTIFICATE",
    "CERT-TYPE-UNSUPPORTED",
    "INVALID-CERT-AUTHORITY",
    "INVALID-HASH-INFORMATION",
    "AUTHENTICATION-FAILED",
    "INVALID-SIGNATURE",
    "ADDRESS-NOTIFICATION",
    "NOTIFY-SA-LIFETIME",
    "CERTIFICATE-UNAVAILABLE",
    "UNSUPPORTED-EXCHANGE-TYPE",
    "UNEQUAL-PAYLOAD-LENGTHS"
  };

  if (type < NUM_PREDEFINED) return msgs[type];
  if (type < 8192)           return "RESERVED (Future Use)";
  if (type < 16384)          return "Private Use";
  if (type < 16385)          return "CONNECTED";
  if (type < 24576)          return "RESERVED (Future Use) - status";
  if (type < 24577)          return "RESPONDER-LIFETIME";
  if (type < 24578)          return "REPLAY-STATUS";
  if (type < 24579)          return "INITIAL-CONTACT";
  if (type < 32768)          return "DOI-specific codes";
  if (type < 40960)          return "Private Use - status";
  if (type < 65535)          return "RESERVED (Future Use) - status (2)";

  return "Huh? You should never see this! Shame on you!";
}

static const char *
situation2str(guint32 type) {

#define SIT_MSG_NUM	1024
#define SIT_IDENTITY	0x01
#define SIT_SECRECY	0x02
#define SIT_INTEGRITY	0x04

  static char	msg[SIT_MSG_NUM];
  int		n = 0;
  char *	sep = "";
  
  if (type & SIT_IDENTITY) {
    n += snprintf(msg, SIT_MSG_NUM-n, "%sIDENTITY", sep);
    sep = " & ";
  }
  if (type & SIT_SECRECY) {
    n += snprintf(msg, SIT_MSG_NUM-n, "%sSECRECY", sep);
    sep = " & ";
  }
  if (type & SIT_INTEGRITY) {
    n += snprintf(msg, SIT_MSG_NUM-n, "%sINTEGRITY", sep);
    sep = " & ";
  }

  return msg;
}

static const char *
value2str(guint16 att_type, guint16 value) {
  
  if (value == 0) return "RESERVED";
  
  switch (att_type) {
    case 1:
    case 2:
      switch (value) {
        case 1:  return "Seconds";
        case 2:  return "Kilobytes";
        default: return "UNKNOWN-SA-VALUE";
      }
    case 3:
      return "Group-Value";
    case 4:
      switch (value) {
        case 1:  return "Tunnel";
        case 2:  return "Transport";
        default: return "UNKNOWN-ENCAPSULATION-VALUE";
      }
    case 5:
      switch (value) {
        case 1:  return "HMAC-MD5";
        case 2:  return "HMAC-SHA";
        case 3:  return "DES-MAC";
        case 4:  return "KPDK";
        default: return "UNKNOWN-AUTHENTICATION-VALUE";
      }
    case 11:
      return "Life-Type";
    default: return "UNKNOWN-ATTRIBUTE-TYPE";
  }
}

static const char *
num2str(const guint8 *pd, guint16 len) {

#define NUMSTR_LEN	1024
  static char		numstr[NUMSTR_LEN];
  
  switch (len) {
  case 1:
    snprintf(numstr, NUMSTR_LEN, "%u", *pd);
    break;
  case 2:
    snprintf(numstr, NUMSTR_LEN, "%u", ntohs(*((guint16 *)pd)));
    break;
  case 3:
    snprintf(numstr, NUMSTR_LEN, "%lu", ntohl(*((guint32 *)pd) & 0x0fff));
    break;
  case 4:
    snprintf(numstr, NUMSTR_LEN, "%lu", ntohl(*((guint32 *)pd)));
    break;
  default:
    snprintf(numstr, NUMSTR_LEN, "<too big>");
  }

  return numstr;
}

/* packet-gre.c
 * Routines for the Generic Routing Encapsulation (GRE) protocol
 * Brad Robel-Forrest <brad.robel-forrest@xxxxxxxxxxxxxx>
 *
 * $Id$
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxx>
 * 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

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#include <netinet/in.h>
#include <glib.h>
#include "packet.h"

/* bit positions for flags in header */
#define GH_B_C		0x8000
#define GH_B_R		0x4000
#define GH_B_K		0x2000
#define GH_B_S		0x1000
#define GH_B_s		0x0800
#define GH_B_RECUR	0x0700
#define GH_P_A		0x0080	/* only in special PPTPized GRE header */
#define GH_P_FLAGS	0x0078	/* only in special PPTPized GRE header */
#define GH_R_FLAGS	0x00F8
#define GH_B_VER	0x0007

#define GRE_PPP		0x880B

static int calc_len(guint16, int);
static void add_flags_and_ver(proto_tree *, guint16, int, int);

void
dissect_gre(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  
  guint16	flags_and_ver = ntohs(*((guint16 *)(pd + offset)));
  guint16	type	      = ntohs(*((guint16 *)(pd + offset + sizeof(flags_and_ver))));
  static const value_string typevals[] = {
    { GRE_PPP, "PPP" },
    { 0,       NULL  }
  };

  if (check_col(fd, COL_PROTOCOL))
    col_add_str(fd, COL_PROTOCOL, "GRE");
	
  if (check_col(fd, COL_INFO)) {
    if (type == GRE_PPP)
      col_add_str(fd, COL_INFO, "Encapsulated PPP");
    else
      col_add_str(fd, COL_INFO, "Encapsulated unknown");
  }
		
  if (fd->cap_len > offset && tree) {
    int			is_ppp;
    proto_item *	ti;
    proto_tree *	gre_tree;

    if (type == GRE_PPP) {
      is_ppp = 1;
      ti = proto_tree_add_item(tree, offset, calc_len(flags_and_ver, 1), "GRE (PPP)");
      gre_tree = proto_tree_new();
      proto_item_add_subtree(ti, gre_tree, ETT_GRE);
      add_flags_and_ver(gre_tree, flags_and_ver, offset, 1);
    }
    else {
      is_ppp = 0;
      ti = proto_tree_add_item(tree, offset, calc_len(flags_and_ver, 1), "GRE");
      gre_tree = proto_tree_new();
      proto_item_add_subtree(ti, gre_tree, ETT_GRE);
      add_flags_and_ver(gre_tree, flags_and_ver, offset, 0);
    }

    offset += sizeof(flags_and_ver);

    proto_tree_add_item(gre_tree, offset, sizeof(type),
			"Protocol Type: %s (%#04x)",
			val_to_str(type, typevals, "Unknown"), type);
    offset += sizeof(type);    

    if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R) {
      guint16 checksum = ntohs(*((guint16 *)(pd + offset)));
      proto_tree_add_item(gre_tree, offset, sizeof(checksum),
			  "Checksum: %u", checksum);
      offset += sizeof(checksum);
    }
    
    if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R) {
      guint16 rtoffset = ntohs(*((guint16 *)(pd + offset)));
      proto_tree_add_item(gre_tree, offset, sizeof(rtoffset),
			  "Offset: %u", rtoffset);
      offset += sizeof(rtoffset);
    }

    if (flags_and_ver & GH_B_K) {
      if (is_ppp) {
	guint16	paylen;
	guint16 callid;
	
	paylen = ntohs(*((guint16 *)(pd + offset)));
	proto_tree_add_item(gre_tree, offset, sizeof(paylen),
			    "Payload length: %u", paylen);
	offset += sizeof(paylen);

	callid = ntohs(*((guint16 *)(pd + offset)));
	proto_tree_add_item(gre_tree, offset, sizeof(callid),
			    "Call ID: %u", callid);
	offset += sizeof(callid);
      }
      else {
	guint32 key = ntohl(*((guint32 *)(pd + offset)));
	proto_tree_add_item(gre_tree, offset, sizeof(key),
			    "Key: %u", key);
	offset += sizeof(key);
      }
    }
    
    if (flags_and_ver & GH_B_S) {
      guint32 seqnum = ntohl(*((guint32 *)(pd + offset)));
      proto_tree_add_item(gre_tree, offset, sizeof(seqnum),
			  "Sequence number: %u", seqnum);
      offset += sizeof(seqnum);
    }

    if (is_ppp && flags_and_ver & GH_P_A) {
      guint32 acknum = ntohl(*((guint32 *)(pd + offset)));
      proto_tree_add_item(gre_tree, offset, sizeof(acknum),
			  "Acknowledgement number: %u", acknum);
      offset += sizeof(acknum);
    }

    if (flags_and_ver & GH_B_R) {
      proto_tree_add_item(gre_tree, offset, sizeof(guint16),
			  "Address family: %u", ntohs(*((guint16 *)(pd + offset))));
      offset += sizeof(guint16);
      proto_tree_add_item(gre_tree, offset, 1,
			  "SRE offset: %u", pd[offset++]);
      proto_tree_add_item(gre_tree, offset, 1,
			  "SRE length: %u", pd[offset++]);
    }

    switch (type) {
/*       case GRE_PPP: */
/* 	dissect_ppp(pd, offset, fd, tree); */
/* 	break; */
      default:
	dissect_data(pd, offset, fd, gre_tree);
    }
  }
}

static int
calc_len(guint16 flags_and_ver, int is_ppp) {
  
  int	len = 4;
  
  if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R) len += 4;
  if (flags_and_ver & GH_B_K) len += 4;
  if (flags_and_ver & GH_B_S) len += 4;
  if (is_ppp && flags_and_ver & GH_P_A) len += 4;
  
  return len;
}

static void
add_flags_and_ver(proto_tree *tree, guint16 flags_and_ver, int offset, int is_ppp) {

  proto_item *	ti;
  proto_tree *	fv_tree;
  int		nbits = sizeof(flags_and_ver) * 8;
  
  ti = proto_tree_add_item(tree, offset, 2, 
			   "Flags and version: %#08x", flags_and_ver);
  fv_tree = proto_tree_new();
  proto_item_add_subtree(ti, fv_tree, ETT_GRE_FLAGS);
  
  proto_tree_add_item(fv_tree, offset, sizeof(flags_and_ver), "%s",
		      decode_boolean_bitfield(flags_and_ver, GH_B_C, nbits,
					      "Checksum", "No checksum"));
  proto_tree_add_item(fv_tree, offset, sizeof(flags_and_ver), "%s",
		      decode_boolean_bitfield(flags_and_ver, GH_B_R, nbits,
					      "Routing", "No routing"));
  proto_tree_add_item(fv_tree, offset, sizeof(flags_and_ver), "%s",
		      decode_boolean_bitfield(flags_and_ver, GH_B_K, nbits,
					      "Key", "No key"));
  proto_tree_add_item(fv_tree, offset, sizeof(flags_and_ver), "%s",
		      decode_boolean_bitfield(flags_and_ver, GH_B_S, nbits,
					      "Sequence number", "No sequence number"));
  proto_tree_add_item(fv_tree, offset, sizeof(flags_and_ver), "%s",
		      decode_boolean_bitfield(flags_and_ver, GH_B_s, nbits,
					      "Strict source route", "No strict source route"));
  proto_tree_add_item(fv_tree, offset, sizeof(flags_and_ver), "%s",
		      decode_numeric_bitfield(flags_and_ver, GH_B_RECUR, nbits,
					      "Recursion control: %u"));
  if (is_ppp) {
    proto_tree_add_item(fv_tree, offset, sizeof(flags_and_ver), "%s",
			decode_boolean_bitfield(flags_and_ver, GH_P_A, nbits,
						"Acknowledgment number", "No acknowledgment number"));
    proto_tree_add_item(fv_tree, offset, sizeof(flags_and_ver), "%s",
			decode_numeric_bitfield(flags_and_ver, GH_P_FLAGS, nbits,
						"Flags: %u"));
  }
  else {
    proto_tree_add_item(fv_tree, offset, sizeof(flags_and_ver), "%s",
			decode_numeric_bitfield(flags_and_ver, GH_R_FLAGS, nbits,
						"Flags: %u"));
  }

  proto_tree_add_item(fv_tree, offset, sizeof(flags_and_ver), "%s",
		      decode_numeric_bitfield(flags_and_ver, GH_B_VER, nbits,
					      "Version: %u"));
 }
 
/* packet-pptp.c
 * Routines for the Point-to-Point Tunnelling Protocol (PPTP)
 * Brad Robel-Forrest <brad.robel-forrest@xxxxxxxxxxxxxx>
 *
 * $Id$
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxx>
 * 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

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#include <stdio.h>
#include <netinet/in.h>
#include <glib.h>
#include "packet.h"

#define NUM_MSG_TYPES		3
#define msgtype2str(t)	\
  ((t < NUM_MSG_TYPES) ? msgtypestr[t] : "UNKNOWN-MESSAGES-TYPE")

static const char *msgtypestr[NUM_MSG_TYPES] = {
  "UNKNOWN-MESSAGE-TYPE",
  "CONTROL-MESSAGE",
  "MANAGEMENT-MESSAGE"
};

#define NUM_FRAME_TYPES		4
#define frametype2str(t)	\
  ((t < NUM_FRAME_TYPES) ? frametypestr[t] : "UNKNOWN-FRAMING-TYPE")

static const char *frametypestr[NUM_FRAME_TYPES] = {
  "UNKNOWN-FRAMING-TYPE",
  "ASYNCHRONOUS",
  "SYNCHRONOUS",
  "EITHER"
};

#define NUM_BEARER_TYPES	4
#define bearertype2str(t)	\
  ((t < NUM_BEARER_TYPES) ? bearertypestr[t] : "UNKNOWN-BEARER-TYPE")

static const char *bearertypestr[NUM_BEARER_TYPES] = {
  "UNKNOWN-BEARER-TYPE",
  "ANALOG",
  "DIGITAL",
  "EITHER"
};

#define NUM_CNTRLRESULT_TYPES	6
#define cntrlresulttype2str(t)	\
  ((t < NUM_CNTRLRESULT_TYPES) ? cntrlresulttypestr[t] : "UNKNOWN-CNTRLRESULT-TYPE")

static const char *cntrlresulttypestr[NUM_CNTRLRESULT_TYPES] = {
  "UNKNOWN-CNTRLRESULT-TYPE",
  "SUCCESS",
  "GENERAL-ERROR",
  "COMMAND-CHANNEL-EXISTS",
  "NOT-AUTHORIZED",
  "VERSION-NOT-SUPPORTED"
};

#define NUM_ERROR_TYPES		7
#define errortype2str(t)	\
  ((t < NUM_ERROR_TYPES) ? errortypestr[t] : "UNKNOWN-ERROR-TYPE")

static const char *errortypestr[NUM_ERROR_TYPES] = {
  "NONE",
  "NOT-CONNECTED",
  "BAD-FORMAT",
  "BAD-VALUE",
  "NO-RESOURCE",
  "BAD-CALL-ID",
  "PAC-ERROR"
};

#define NUM_REASON_TYPES	4
#define reasontype2str(t)	\
  ((t < NUM_REASON_TYPES) ? reasontypestr[t] : "UNKNOWN-REASON-TYPE")

static const char *reasontypestr[NUM_REASON_TYPES] = {
  "UNKNOWN-REASON-TYPE",
  "NONE",
  "STOP-PROTOCOL",
  "STOP-LOCAL-SHUTDOWN"
};

#define NUM_STOPRESULT_TYPES	3
#define stopresulttype2str(t)	\
  ((t < NUM_STOPRESULT_TYPES) ? stopresulttypestr[t] : "UNKNOWN-STOPRESULT-TYPE")

static const char *stopresulttypestr[NUM_STOPRESULT_TYPES] = {
  "UNKNOWN-STOPRESULT-TYPE",
  "SUCCESS",
  "GENERAL-ERROR"
};

#define NUM_ECHORESULT_TYPES	3
#define echoresulttype2str(t)	\
  ((t < NUM_ECHORESULT_TYPES) ? echoresulttypestr[t] : "UNKNOWN-ECHORESULT-TYPE")

static const char *echoresulttypestr[NUM_ECHORESULT_TYPES] = {
  "UNKNOWN-ECHORESULT-TYPE",
  "SUCCESS",
  "GENERAL-ERROR"
};

#define NUM_OUTRESULT_TYPES	8
#define outresulttype2str(t)	\
  ((t < NUM_OUTRESULT_TYPES) ? outresulttypestr[t] : "UNKNOWN-OUTRESULT-TYPE")

static const char *outresulttypestr[NUM_OUTRESULT_TYPES] = {
  "UNKNOWN-OUTRESULT-TYPE",
  "CONNECTED",
  "GENERAL-ERROR",
  "NO-CARRIER",
  "BUSY",
  "NO-DIAL-TONE",
  "TIME-OUT",
  "DO-NOT-ACCEPT"
};

#define NUM_INRESULT_TYPES	4
#define inresulttype2str(t)	\
  ((t < NUM_INRESULT_TYPES) ? inresulttypestr[t] : "UNKNOWN-INRESULT-TYPE")

static const char *inresulttypestr[NUM_INRESULT_TYPES] = {
  "UNKNOWN-INRESULT-TYPE",
  "CONNECT",
  "GENERAL-ERROR",
  "DO-NOT-ACCEPT"
};

#define NUM_DISCRESULT_TYPES	5
#define discresulttype2str(t)	\
  ((t < NUM_DISCRESULT_TYPES) ? discresulttypestr[t] : "UNKNOWN-DISCRESULT-TYPE")

static const char *discresulttypestr[NUM_DISCRESULT_TYPES] = {
  "UNKNOWN-DISCRESULT-TYPE",
  "LOST-CARRIER",
  "GENERAL-ERROR",
  "ADMIN-SHUTDOWN",
  "REQUEST"
};

static void dissect_unknown(const u_char *, int, frame_data *, proto_tree *);
static void dissect_cntrl_req(const u_char *, int, frame_data *, proto_tree *);
static void dissect_cntrl_reply(const u_char *, int, frame_data *, proto_tree *);
static void dissect_stop_req(const u_char *, int, frame_data *, proto_tree *);
static void dissect_stop_reply(const u_char *, int, frame_data *, proto_tree *);
static void dissect_echo_req(const u_char *, int, frame_data *, proto_tree *);
static void dissect_echo_reply(const u_char *, int, frame_data *, proto_tree *);
static void dissect_out_req(const u_char *, int, frame_data *, proto_tree *);
static void dissect_out_reply(const u_char *, int, frame_data *, proto_tree *);
static void dissect_in_req(const u_char *, int, frame_data *, proto_tree *);
static void dissect_in_reply(const u_char *, int, frame_data *, proto_tree *);
static void dissect_in_connected(const u_char *, int, frame_data *, proto_tree *);
static void dissect_clear_req(const u_char *, int, frame_data *, proto_tree *);
static void dissect_disc_notify(const u_char *, int, frame_data *, proto_tree *);
static void dissect_error_notify(const u_char *, int, frame_data *, proto_tree *);
static void dissect_set_link(const u_char *, int, frame_data *, proto_tree *);

#define NUM_CNTRL_TYPES		16
#define cntrltype2str(t)	\
  ((t < NUM_CNTRL_TYPES) ? strfuncs[t].str : "UNKNOWN-CONTROL-TYPE")

static struct strfunc {
  const char *	str;
  void          (*func)(const u_char *, int, frame_data *, proto_tree *);
} strfuncs[NUM_CNTRL_TYPES] = {
  {"UNKNOWN-CONTROL-TYPE",    dissect_unknown      },
  {"START-CONTROL-REQUEST",   dissect_cntrl_req    },
  {"START-CONTROL-REPLY",     dissect_cntrl_reply  },
  {"STOP-CONTROL-REQUEST",    dissect_stop_req     },
  {"STOP-CONTROL-REPLY",      dissect_stop_reply   },
  {"ECHO-REQUEST",            dissect_echo_req     },
  {"ECHO-REPLY",              dissect_echo_reply   },
  {"OUTGOING-CALL-REQUEST",   dissect_out_req      },
  {"OUTGOING-CALL-REPLY",     dissect_out_reply    },
  {"INCOMING-CALL-REQUEST",   dissect_in_req       },
  {"INCOMING-CALL-REPLY",     dissect_in_reply     },
  {"INCOMING-CALL-CONNECTED", dissect_in_connected },
  {"CLEAR-CALL-REQUEST",      dissect_clear_req    },
  {"DISCONNECT-NOTIFY",       dissect_disc_notify  },
  {"ERROR-NOTIFY",            dissect_error_notify },
  {"SET-LINK",                dissect_set_link     }
};

struct pptp_hdr
{
  guint16	len;
  guint16	type;
  guint32	cookie;
  guint16	cntrl_type;
  guint16	resv;
};

struct cntrl_req
{
  guint8	major_ver;
  guint8	minor_ver;
  guint16	resv;
  guint32	frame;
  guint32	bearer;
  guint16	max_chan;
  guint16	firm_rev;
  guint8	host[64];
  guint8	vendor[64];
};

struct cntrl_reply
{
  guint8	major_ver;
  guint8	minor_ver;
  guint8	result;
  guint8	error;
  guint32	frame;
  guint32	bearer;
  guint16	max_chan;
  guint16	firm_rev;
  guint8	host[64];
  guint8	vendor[64];
};

struct stop_req
{
  guint8	reason;
  guint8	resv0;
  guint16	resv1;
};

struct stop_reply
{
  guint8	result;
  guint8	error;
  guint16	resv;
};

struct echo_req
{
  guint32	ident;
};

struct echo_reply
{
  guint32	ident;
  guint8	result;
  guint8	error;
  guint16	resv;
};

struct out_req
{
  guint16	call_id;
  guint16	call_serial;
  guint32	min_bps;
  guint32	max_bps;
  guint32	bearer;
  guint32	frame;
  guint16	win_size;
  guint16	delay;
  guint16	phone_len;
  guint16	resv;
  guint8	phone[64];
  guint8	subaddr[64];
};

struct out_reply
{
  guint16	call_id;
  guint16	peer_id;
  guint8	result;
  guint8	error;
  guint16	cause;
  guint32	speed;
  guint16	win_size;
  guint16	delay;
  guint32	channel_id;
};

struct in_req
{
  guint16	call_id;
  guint16	call_serial;
  guint32	bearer;
  guint32	channel_id;
  guint16	dialed_len;
  guint16	dialing_len;
  guint8	dialed[64];
  guint8	dialing[64];
  guint8	subaddr[64];
};

struct in_reply
{
  guint16	call_id;
  guint16	peer_id;
  guint8	result;
  guint8	error;
  guint16	win_size;
  guint16	delay;
  guint16	resv;
};

struct in_connected
{
  guint16	peer_id;
  guint16	resv;
  guint32	speed;
  guint16	win_size;
  guint16	delay;
  guint32	frame;
};

struct clear_req
{
  guint16	call_id;
  guint16	resv;
};

struct disc_notify
{
  guint16	call_id;
  guint8	result;
  guint8	error;
  guint16	cause;
  guint16	resv;
  guint8	stats[128];
};

struct error_notify
{
  guint16	peer_id;
  guint16	resv;
  guint32	crc;
  guint32	frame;
  guint32	hardware;
  guint32	buffer;
  guint32	timeout;
  guint32	alignment;
};

struct set_link
{
  guint16	peer_id;
  guint16	resv;
  guint32	send_acm;
  guint32	recv_acm;
};

void
dissect_pptp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct pptp_hdr *	hdr = (struct pptp_hdr *)(pd + offset);
  guint16		len;
  guint16		cntrl_type;
  
  if (check_col(fd, COL_PROTOCOL))
    col_add_str(fd, COL_PROTOCOL, "PPTP");
  
  len	     = ntohs(hdr->len);
  cntrl_type = ntohs(hdr->cntrl_type);

  if (check_col(fd, COL_INFO))
    col_add_fstr(fd, COL_INFO, "%s", cntrltype2str(cntrl_type));
    
  if (fd->cap_len > offset && tree) {
    guint16		msg_type;
    proto_item *	ti;
    proto_tree *	pptp_tree;

    ti = proto_tree_add_item(tree, offset, len, "PPTP Control Channel");
    pptp_tree = proto_tree_new();
    proto_item_add_subtree(ti, pptp_tree, ETT_PPTP);
    
    proto_tree_add_item(pptp_tree, offset, sizeof(hdr->len), 
			"Length: %u", len);
    offset += sizeof(hdr->len);

    msg_type = ntohs(hdr->type);
    proto_tree_add_item(pptp_tree, offset, sizeof(hdr->type),
			"Message type: %s (%u)", msgtype2str(msg_type), msg_type);
    offset += sizeof(hdr->type);

    proto_tree_add_item(pptp_tree, offset, sizeof(hdr->cookie),
			"Cookie: %#08x", ntohl(hdr->cookie));
    offset += sizeof(hdr->cookie);
    
    proto_tree_add_item(pptp_tree, offset, sizeof(hdr->cntrl_type),
			"Control type: %s (%u)", cntrltype2str(cntrl_type), cntrl_type);
    offset += sizeof(hdr->cntrl_type);

    proto_tree_add_item(pptp_tree, offset, sizeof(hdr->resv),
			"Reserved: %u", ntohs(hdr->resv));
    offset += sizeof(hdr->resv);

    if (cntrl_type < NUM_CNTRL_TYPES)
      ( *(strfuncs[cntrl_type].func))(pd, offset, fd, pptp_tree);
    else
      dissect_data(pd, offset, fd, pptp_tree);
  }
}

static void
dissect_unknown(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  dissect_data(pd, offset, fd, tree);
}

static void
dissect_cntrl_req(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {

  struct cntrl_req *	hdr = (struct cntrl_req *)(pd + offset);
  guint32		frame;
  guint32		bearer;
  
  proto_tree_add_item(tree, offset, sizeof(hdr->major_ver) + sizeof(hdr->minor_ver), 
		      "Protocol version: %u.%u", hdr->major_ver, hdr->minor_ver );
  offset += sizeof(hdr->major_ver) + sizeof(hdr->minor_ver);

  proto_tree_add_item(tree, offset, sizeof(hdr->resv),
		      "Reserved: %u", hdr->resv);
  offset += sizeof(hdr->resv);

  frame = ntohl(hdr->frame);
  proto_tree_add_item(tree, offset, sizeof(hdr->frame),
		      "Framing capabilities: %s (%u)", frametype2str(frame), frame);
  offset += sizeof(hdr->frame);

  bearer = ntohl(hdr->bearer);
  proto_tree_add_item(tree, offset, sizeof(hdr->bearer),
		      "Bearer capabilities: %s (%u)", bearertype2str(bearer), bearer);
  offset += sizeof(hdr->bearer);

  proto_tree_add_item(tree, offset, sizeof(hdr->max_chan),
		      "Maximum channels: %u", hdr->max_chan);
  offset += sizeof(hdr->max_chan);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->firm_rev),
		      "Firmware revision: %u", hdr->firm_rev);
  offset += sizeof(hdr->firm_rev);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->host),
		      "Hostname: %s", hdr->host);
  offset += sizeof(hdr->host);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->vendor),
		      "Vendor: %s", hdr->vendor);
}

static void
dissect_cntrl_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct cntrl_reply *	hdr = (struct cntrl_reply *)(pd + offset);
  guint32		frame;
  guint32		bearer;

  proto_tree_add_item(tree, offset, sizeof(hdr->major_ver) + sizeof(hdr->minor_ver), 
		      "Protocol version: %u.%u", hdr->major_ver, hdr->minor_ver );
  offset += sizeof(hdr->major_ver) + sizeof(hdr->minor_ver);

  proto_tree_add_item(tree, offset, sizeof(hdr->result),
		      "Result: %s (%u)", cntrlresulttype2str(hdr->result), hdr->result);
  offset += sizeof(hdr->result);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->error),
		      "Error: %s (%u)", errortype2str(hdr->error), hdr->error);
  offset += sizeof(hdr->error);
  
  frame = ntohl(hdr->frame);
  proto_tree_add_item(tree, offset, sizeof(hdr->frame),
		      "Framing capabilities: %s (%u)", frametype2str(frame), frame);
  offset += sizeof(hdr->frame);

  bearer = ntohl(hdr->bearer);
  proto_tree_add_item(tree, offset, sizeof(hdr->bearer),
		      "Bearer capabilities: %s (%u)", bearertype2str(bearer), bearer);
  offset += sizeof(hdr->bearer);

  proto_tree_add_item(tree, offset, sizeof(hdr->max_chan),
		      "Maximum channels: %u", hdr->max_chan);
  offset += sizeof(hdr->max_chan);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->firm_rev),
		      "Firmware revision: %u", hdr->firm_rev);
  offset += sizeof(hdr->firm_rev);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->host),
		      "Hostname: %s", hdr->host);
  offset += sizeof(hdr->host);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->vendor),
		      "Vendor: %s", hdr->vendor);
}

static void
dissect_stop_req(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct stop_req *	hdr = (struct stop_req *)(pd + offset);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->reason),
		      "Reason: %s (%u)", reasontype2str(hdr->reason), hdr->reason);
  offset += sizeof(hdr->reason);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->resv0),
		      "Reserved: %u", hdr->resv0);
  offset += sizeof(hdr->resv0);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->resv1),
		      "Reserved: %u", hdr->resv1);
  offset += sizeof(hdr->resv1);
}

static void
dissect_stop_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct stop_reply *	hdr = (struct stop_reply *)(pd + offset);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->result),
		      "Result: %s (%u)", stopresulttype2str(hdr->result), hdr->result);
  offset += sizeof(hdr->result);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->error),
		      "Error: %s (%u)", errortype2str(hdr->error), hdr->error);
  offset += sizeof(hdr->error);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->resv),
		      "Reserved: %u", hdr->resv);
  offset += sizeof(hdr->resv);
}

static void
dissect_echo_req(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct echo_req *	hdr = (struct echo_req *)(pd + offset);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->ident),
		      "Identifier: %u", hdr->ident);
  offset += sizeof(hdr->ident);
}

static void
dissect_echo_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct echo_reply *	hdr = (struct echo_reply *)(pd + offset);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->ident),
		      "Identifier: %u", hdr->ident);
  offset += sizeof(hdr->ident);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->result),
		      "Result: %s (%u)", echoresulttype2str(hdr->result), hdr->result);
  offset += sizeof(hdr->result);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->error),
		      "Error: %s (%u)", errortype2str(hdr->error), hdr->error);
  offset += sizeof(hdr->error);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->resv),
		      "Reserved: %u", hdr->resv);
  offset += sizeof(hdr->resv);
}

static void
dissect_out_req(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct out_req *	hdr = (struct out_req *)(pd + offset);
  guint32		bearer;
  guint32		frame;
  
  proto_tree_add_item(tree, offset, sizeof(hdr->call_id),
		      "Call ID: %u", hdr->call_id);
  offset += sizeof(hdr->call_id);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->call_serial),
		      "Call Serial Number: %u", hdr->call_serial);
  offset += sizeof(hdr->call_serial);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->min_bps),
		      "Minimum BPS: %u", hdr->min_bps);
  offset += sizeof(hdr->min_bps);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->max_bps),
		      "Maximum BPS: %u", hdr->max_bps);
  offset += sizeof(hdr->max_bps);
  
  bearer = ntohl(hdr->bearer);
  proto_tree_add_item(tree, offset, sizeof(hdr->bearer),
		      "Bearer capabilities: %s (%u)", bearertype2str(bearer), bearer);
  offset += sizeof(hdr->bearer);

  frame = ntohl(hdr->frame);
  proto_tree_add_item(tree, offset, sizeof(hdr->frame),
		      "Framing capabilities: %s (%u)", frametype2str(frame), frame);
  offset += sizeof(hdr->frame);

  proto_tree_add_item(tree, offset, sizeof(hdr->win_size),
		      "Receive window size: %u", hdr->win_size);
  offset += sizeof(hdr->win_size);

  proto_tree_add_item(tree, offset, sizeof(hdr->delay),
		      "Processing delay: %u", hdr->delay);
  offset += sizeof(hdr->delay);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->phone_len),
		      "Phone number length: %u", hdr->phone_len);
  offset += sizeof(hdr->phone_len);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->resv),
		      "Reserved: %u", hdr->resv);
  offset += sizeof(hdr->resv);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->phone),
		      "Phone number: %s", hdr->phone);
  offset += sizeof(hdr->phone);

  proto_tree_add_item(tree, offset, sizeof(hdr->subaddr),
		      "Subaddress: %s", hdr->subaddr);
  offset += sizeof(hdr->subaddr);
}

static void
dissect_out_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct out_reply *	hdr = (struct out_reply *)(pd + offset);

  proto_tree_add_item(tree, offset, sizeof(hdr->call_id),
		      "Call ID: %u", hdr->call_id);
  offset += sizeof(hdr->call_id);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->peer_id),
		      "Peer's call ID: %u", hdr->peer_id);
  offset += sizeof(hdr->peer_id);

  proto_tree_add_item(tree, offset, sizeof(hdr->result),
		      "Result: %s (%u)", outresulttype2str(hdr->result), hdr->result);
  offset += sizeof(hdr->result);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->error),
		      "Error: %s (%u)", errortype2str(hdr->error), hdr->error);
  offset += sizeof(hdr->error);

  proto_tree_add_item(tree, offset, sizeof(hdr->cause),
		      "Cause code: %u", hdr->cause);
  offset += sizeof(hdr->cause);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->speed),
		      "Connect speed: %u", hdr->speed);
  offset += sizeof(hdr->speed);

  proto_tree_add_item(tree, offset, sizeof(hdr->win_size),
		      "Receive window size: %u", hdr->win_size);
  offset += sizeof(hdr->win_size);

  proto_tree_add_item(tree, offset, sizeof(hdr->delay),
		      "Processing delay: %u", hdr->delay);
  offset += sizeof(hdr->delay);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->channel_id),
		      "Physical channel ID: %u", hdr->channel_id);
  offset += sizeof(hdr->channel_id);
}


static void
dissect_in_req(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct in_req *	hdr = (struct in_req *)(pd + offset);
  guint32		bearer;
  
  proto_tree_add_item(tree, offset, sizeof(hdr->call_id),
		      "Call ID: %u", hdr->call_id);
  offset += sizeof(hdr->call_id);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->call_serial),
		      "Call serial number: %u", hdr->call_serial);
  offset += sizeof(hdr->call_serial);
  
  bearer = ntohl(hdr->bearer);
  proto_tree_add_item(tree, offset, sizeof(hdr->bearer),
		      "Bearer capabilities: %s (%u)", bearertype2str(bearer), bearer);
  offset += sizeof(hdr->bearer);

  proto_tree_add_item(tree, offset, sizeof(hdr->channel_id),
		      "Physical channel ID: %u", hdr->channel_id);
  offset += sizeof(hdr->channel_id);

  proto_tree_add_item(tree, offset, sizeof(hdr->dialed_len),
		      "Dialed number length: %u", hdr->dialed_len);
  offset += sizeof(hdr->dialed_len);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->dialing_len),
		      "Dialing number length: %u", hdr->dialing_len);
  offset += sizeof(hdr->dialing_len);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->dialed),
		      "Dialed number: %s", hdr->dialed);
  offset += sizeof(hdr->dialed);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->dialing),
		      "Dialing number: %s", hdr->dialing);
  offset += sizeof(hdr->dialing);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->subaddr),
		      "Subaddress: %s", hdr->subaddr);
  offset += sizeof(hdr->subaddr);
}

static void
dissect_in_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct in_reply *	hdr = (struct in_reply *)(pd + offset);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->call_id),
		      "Call ID: %u", hdr->call_id);
  offset += sizeof(hdr->call_id);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->peer_id),
		      "Peer's call ID: %u", hdr->peer_id);
  offset += sizeof(hdr->peer_id);

  proto_tree_add_item(tree, offset, sizeof(hdr->result),
		      "Result: %s (%u)", inresulttype2str(hdr->result), hdr->result);
  offset += sizeof(hdr->result);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->error),
		      "Error: %s (%u)", errortype2str(hdr->error), hdr->error);
  offset += sizeof(hdr->error);

  proto_tree_add_item(tree, offset, sizeof(hdr->win_size),
		      "Receive window size: %u", hdr->win_size);
  offset += sizeof(hdr->win_size);

  proto_tree_add_item(tree, offset, sizeof(hdr->delay),
		      "Processing delay: %u", hdr->delay);
  offset += sizeof(hdr->delay);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->resv),
		      "Reserved: %u", hdr->resv);
  offset += sizeof(hdr->resv);
}

static void
dissect_in_connected(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct in_connected *	hdr = (struct in_connected *)(pd + offset);
  guint32		frame;
  
  proto_tree_add_item(tree, offset, sizeof(hdr->peer_id),
		      "Peer's call ID: %u", hdr->peer_id);
  offset += sizeof(hdr->peer_id);

  proto_tree_add_item(tree, offset, sizeof(hdr->resv),
		      "Reserved: %u", hdr->resv);
  offset += sizeof(hdr->resv);

  proto_tree_add_item(tree, offset, sizeof(hdr->speed),
		      "Connect speed: %u", hdr->speed);
  offset += sizeof(hdr->speed);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->win_size),
		      "Receive window size: %u", hdr->win_size);
  offset += sizeof(hdr->win_size);

  proto_tree_add_item(tree, offset, sizeof(hdr->delay),
		      "Processing delay: %u", hdr->delay);
  offset += sizeof(hdr->delay);
  
  frame = ntohl(hdr->frame);
  proto_tree_add_item(tree, offset, sizeof(hdr->frame),
		      "Framing capabilities: %s (%u)", frametype2str(frame), frame);
  offset += sizeof(hdr->frame);
}

static void
dissect_clear_req(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct clear_req *	hdr = (struct clear_req *)(pd + offset);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->call_id),
		      "Call ID: %u", hdr->call_id);
  offset += sizeof(hdr->call_id);

  proto_tree_add_item(tree, offset, sizeof(hdr->resv),
		      "Reserved: %u", hdr->resv);
  offset += sizeof(hdr->resv);
}

static void
dissect_disc_notify(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct disc_notify *	hdr = (struct disc_notify *)(pd + offset);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->call_id),
		      "Call ID: %u", hdr->call_id);
  offset += sizeof(hdr->call_id);

  proto_tree_add_item(tree, offset, sizeof(hdr->result),
		      "Result: %s (%u)", discresulttype2str(hdr->result), hdr->result);
  offset += sizeof(hdr->result);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->error),
		      "Error: %s (%u)", errortype2str(hdr->error), hdr->error);
  offset += sizeof(hdr->error);

  proto_tree_add_item(tree, offset, sizeof(hdr->cause),
		      "Cause code: %u", hdr->cause);
  offset += sizeof(hdr->cause);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->resv),
		      "Reserved: %u", hdr->resv);
  offset += sizeof(hdr->resv);

  proto_tree_add_item(tree, offset, sizeof(hdr->stats),
		      "Call statistics: %s", hdr->stats);
  offset += sizeof(hdr->stats);
}

static void
dissect_error_notify(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct error_notify *	hdr = (struct error_notify *)(pd + offset);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->peer_id),
		      "Peer's call ID: %u", hdr->peer_id);
  offset += sizeof(hdr->peer_id);

  proto_tree_add_item(tree, offset, sizeof(hdr->resv),
		      "Reserved: %u", hdr->resv);
  offset += sizeof(hdr->resv);

  proto_tree_add_item(tree, offset, sizeof(hdr->crc),
		      "CRC errors: %u", hdr->crc);
  offset += sizeof(hdr->crc);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->frame),
		      "Framing errors: %u", hdr->frame);
  offset += sizeof(hdr->frame);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->hardware),
		      "Hardware overruns: %u", hdr->hardware);
  offset += sizeof(hdr->hardware);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->buffer),
		      "Buffer overruns: %u", hdr->buffer);
  offset += sizeof(hdr->buffer);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->timeout),
		      "Time-out errors: %u", hdr->timeout);
  offset += sizeof(hdr->timeout);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->alignment),
		      "Alignment errors: %u", hdr->alignment);
  offset += sizeof(hdr->alignment);
}

static void
dissect_set_link(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
  struct set_link *	hdr = (struct set_link *)(pd + offset);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->peer_id),
		      "Peer's call ID: %u", hdr->peer_id);
  offset += sizeof(hdr->peer_id);

  proto_tree_add_item(tree, offset, sizeof(hdr->resv),
		      "Reserved: %u", hdr->resv);
  offset += sizeof(hdr->resv);

  proto_tree_add_item(tree, offset, sizeof(hdr->send_acm),
		      "Send ACCM: %#08x", hdr->send_acm);
  offset += sizeof(hdr->send_acm);
  
  proto_tree_add_item(tree, offset, sizeof(hdr->recv_acm),
		      "Recv ACCM: %#08x", hdr->recv_acm);
  offset += sizeof(hdr->recv_acm);
}