/* packet-diameter.c
 * Routines for Diameter packet disassembly
 *
 * $Id: packet-diameter.c 19751 2006-10-31 09:29:07Z sahlberg $
 *
 * Copyright (c) 2001 by David Frascone <dave@frascone.com>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * 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.
 * References:
 * 2004-03-11
 * http://www.ietf.org/rfc/rfc3588.txt
 * http://www.iana.org/assignments/radius-types
 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-cc-03.txt
 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-nasreq-14.txt
 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-mobileip-16.txt
 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-sip-app-01.txt
 * http://www.ietf.org/html.charters/aaa-charter.html
 * http://www.iana.org/assignments/address-family-numbers
 * http://www.iana.org/assignments/enterprise-numbers
 * http://www.iana.org/assignments/aaa-parameters
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <glib.h>
#include <epan/filesystem.h>
#include <epan/xmlstub.h>
#include <epan/packet.h>
#include <epan/addr_resolv.h>
#include <epan/report_err.h>
#include <epan/prefs.h>
#include <epan/sminmpec.h>
#include <epan/emem.h>
#include <epan/expert.h>
#include "packet-tcp.h"
#include "packet-sip.h"

/* This must be defined before we include packet-diameter-defs.h */

/* Valid data types */
typedef enum {
  /* Base Types */
  DIAMETER_OCTET_STRING = 1,
  DIAMETER_INTEGER32,
  DIAMETER_INTEGER64,
  DIAMETER_UNSIGNED32,
  DIAMETER_UNSIGNED32ENUM,
  DIAMETER_UNSIGNED64,
  DIAMETER_FLOAT32,
  DIAMETER_FLOAT64,
  DIAMETER_FLOAT128,
  DIAMETER_GROUPED,

  /* Derived Types */
  DIAMETER_IP_ADDRESS,         /* OctetString */
  DIAMETER_TIME,               /* Integer 32 */
  DIAMETER_UTF8STRING,         /* OctetString */
  DIAMETER_IDENTITY,           /* OctetString */
  DIAMETER_ENUMERATED,         /* Integer 32 */
  DIAMETER_IP_FILTER_RULE,     /* OctetString */
  DIAMETER_QOS_FILTER_RULE,    /* OctetString */
  DIAMETER_MIP_REG_REQ,        /* OctetString */
  DIAMETER_VENDOR_ID,          /* Integer32  */
  DIAMETER_APPLICATION_ID,     /* Integer32  */
  DIAMETER_URI,                /* OctetString */
  DIAMETER_SESSION_ID,          /* OctetString */
  DIAMETER_PUBLIC_ID,			/* OctetString */
  DIAMETER_PRIVATE_ID			/* OctetString */	
} diameterDataType;


static const value_string TypeValues[]={
  {  DIAMETER_OCTET_STRING,    "OctetString" },
  {  DIAMETER_INTEGER32,       "Integer32" },
  {  DIAMETER_INTEGER64,       "Integer64" },
  {  DIAMETER_UNSIGNED32,      "Unsigned32" },
  {  DIAMETER_UNSIGNED32ENUM,	"Unsigned32" }, /* This is needed to get value translation for Uint32:s with a value*/
  {  DIAMETER_UNSIGNED64,      "Unsigned64" },
  {  DIAMETER_FLOAT32,         "Float32" },
  {  DIAMETER_FLOAT64,         "Float64" },
  {  DIAMETER_FLOAT128,        "Float128" },
  {  DIAMETER_GROUPED,         "Grouped" },
  {  DIAMETER_IP_ADDRESS,      "IpAddress" },
  {  DIAMETER_TIME,            "Time" },
  {  DIAMETER_UTF8STRING,      "UTF8String" },
  {  DIAMETER_IDENTITY,        "DiameterIdentity" },
  {  DIAMETER_ENUMERATED,      "Enumerated" },
  {  DIAMETER_IP_FILTER_RULE,  "IPFilterRule" },
  {  DIAMETER_QOS_FILTER_RULE, "QOSFilterRule" },
  {  DIAMETER_MIP_REG_REQ,     "MIPRegistrationRequest"},
  {  DIAMETER_VENDOR_ID,       "VendorId"},
  {  DIAMETER_APPLICATION_ID,  "AppId"},
  {  DIAMETER_URI,             "DiameterURI"},
  {  DIAMETER_SESSION_ID,      "Session-Id"},
  {	 DIAMETER_PUBLIC_ID,		"Public-Id"},
  {	 DIAMETER_PRIVATE_ID,		"Private-Id"},
	
  {0, (char *)NULL}
};

typedef struct value_name {
  guint32            value;
  gchar             *name;
  struct value_name *next;
} ValueName;

typedef struct old_avp_info {
  guint32           code;
  const gchar      *name;
  diameterDataType  type;
  const value_string *values;
} oldAvpInfo;

typedef struct avp_info {
  guint32           code;
  gchar            *name;
  gchar            *vendorName;
  diameterDataType  type;
  ValueName        *values;
  struct avp_info  *next;
} avpInfo;

typedef struct command_code {
  guint32              code;
  gchar               *name;
  gchar               *vendorName;
  struct command_code *next;
} CommandCode;

typedef struct vendor_id {
  guint32              id;
  gchar               *name;
  gchar               *longName;
  struct vendor_id    *next;
} VendorId;

typedef struct application_id {
  guint32              id;
  gchar               *name;
  struct application_id    *next;
} ApplicationId;

static avpInfo         *avpListHead=NULL;
static VendorId        *vendorListHead=NULL;
static CommandCode     *commandListHead=NULL;
static ApplicationId   *ApplicationIdHead=NULL;


#include "packet-diameter-defs.h"

#define  NTP_TIME_DIFF                   (2208988800UL)

#define TCP_PORT_DIAMETER	3868
#define SCTP_PORT_DIAMETER	3868

static const true_false_string reserved_set = {
  "*** Error! Reserved Bit is Set",
  "Ok"
};

static int proto_diameter = -1;
static int hf_diameter_length = -1;
static int hf_diameter_code = -1;
static int hf_diameter_hopbyhopid =-1;
static int hf_diameter_endtoendid =-1;
static int hf_diameter_version = -1;
static int hf_diameter_vendor_id = -1;
static int hf_diameter_application_id = -1;
static int hf_diameter_flags = -1;
static int hf_diameter_flags_request = -1;
static int hf_diameter_flags_proxyable = -1;
static int hf_diameter_flags_error = -1;
static int hf_diameter_flags_T		= -1;
static int hf_diameter_flags_reserved4 = -1;
static int hf_diameter_flags_reserved5 = -1;
static int hf_diameter_flags_reserved6 = -1;
static int hf_diameter_flags_reserved7 = -1;

static int hf_diameter_avp_code = -1;
static int hf_diameter_avp_length = -1;
static int hf_diameter_avp_flags = -1;
static int hf_diameter_avp_flags_vendor_specific = -1;
static int hf_diameter_avp_flags_mandatory = -1;
static int hf_diameter_avp_flags_protected = -1;
static int hf_diameter_avp_flags_reserved3 = -1;
static int hf_diameter_avp_flags_reserved4 = -1;
static int hf_diameter_avp_flags_reserved5 = -1;
static int hf_diameter_avp_flags_reserved6 = -1;
static int hf_diameter_avp_flags_reserved7 = -1;
static int hf_diameter_avp_vendor_id = -1;


static int hf_diameter_avp_data_uint32 = -1;
static int hf_diameter_avp_data_int32 = -1;
static int hf_diameter_avp_data_uint64 = -1;
static int hf_diameter_avp_data_int64 = -1;
static int hf_diameter_avp_data_bytes = -1;
static int hf_diameter_avp_data_string = -1;
static int hf_diameter_avp_data_addrfamily = -1;
static int hf_diameter_avp_data_v4addr		= -1;
static int hf_diameter_avp_data_v6addr		= -1;
static int hf_diameter_avp_data_time		= -1;
static int hf_diameter_avp_diameter_uri		= -1;
static int hf_diameter_avp_session_id		= -1;
static int hf_diameter_avp_public_id		= -1;
static int hf_diameter_avp_private_id		= -1;

static gint ett_diameter = -1;
static gint ett_diameter_flags = -1;
static gint ett_diameter_avp = -1;
static gint ett_diameter_avp_flags = -1;
static gint ett_diameter_avpinfo = -1;

static guint gbl_diameterTcpPort=TCP_PORT_DIAMETER;
static guint gbl_diameterSctpPort=SCTP_PORT_DIAMETER;

/* desegmentation of Diameter over TCP */
static gboolean gbl_diameter_desegment = TRUE;

/* Allow zero as a valid application ID */
static gboolean allow_zero_as_app_id = TRUE;

/* Suppress console output at unknown AVP:s,Flags etc */
static gboolean suppress_console_output = TRUE;

static gboolean gbl_use_xml_dictionary = TRUE;
#define DICT_FN  "diameter/dictionary.xml"
static const gchar *gbl_diameterDictionary;

typedef struct _e_diameterhdr_v16 {
  guint32  versionLength;
  guint32  flagsCmdCode;
  guint32  vendorId;
  guint32  hopByHopId;
  guint32  endToEndId;
} e_diameterhdr_v16;

typedef struct _e_diameterhdr_rfc {
  guint32  versionLength;
  guint32  flagsCmdCode;
  guint32  applicationId;
  guint32  hopByHopId;
  guint32  endToEndId;
} e_diameterhdr_rfc;

typedef struct _e_avphdr {
  guint32 avp_code;
  guint32 avp_flagsLength;
  guint32 avp_vendorId;           /* optional */
} e_avphdr;

/* Diameter Header Flags */
/*                                      RPrrrrrrCCCCCCCCCCCCCCCCCCCCCCCC  */
#define DIAM_FLAGS_R 0x80
#define DIAM_FLAGS_P 0x40
#define DIAM_FLAGS_E 0x20
#define DIAM_FLAGS_T 0x10
#define DIAM_FLAGS_RESERVED4 0x08
#define DIAM_FLAGS_RESERVED5 0x04
#define DIAM_FLAGS_RESERVED6 0x02
#define DIAM_FLAGS_RESERVED7 0x01
#define DIAM_FLAGS_RESERVED  0x0f

#define DIAM_LENGTH_MASK  0x00ffffffl
#define DIAM_COMMAND_MASK DIAM_LENGTH_MASK
#define DIAM_GET_FLAGS(dh)                ((dh.flagsCmdCode & ~DIAM_COMMAND_MASK) >> 24)
#define DIAM_GET_VERSION(dh)              ((dh.versionLength & (~DIAM_LENGTH_MASK)) >> 24)
#define DIAM_GET_COMMAND(dh)              (dh.flagsCmdCode & DIAM_COMMAND_MASK)
#define DIAM_GET_LENGTH(dh)               (dh.versionLength & DIAM_LENGTH_MASK)

/* Diameter AVP Flags */
#define AVP_FLAGS_P 0x20
#define AVP_FLAGS_V 0x80
#define AVP_FLAGS_M 0x40
#define AVP_FLAGS_RESERVED3 0x10
#define AVP_FLAGS_RESERVED4 0x08
#define AVP_FLAGS_RESERVED5 0x04
#define AVP_FLAGS_RESERVED6 0x02
#define AVP_FLAGS_RESERVED7 0x01
#define AVP_FLAGS_RESERVED 0x1f          /* 00011111  -- V M P X X X X X */

#define MIN_AVP_SIZE (sizeof(e_avphdr) - sizeof(guint32))
#define MIN_DIAMETER_SIZE (sizeof(e_diameterhdr_rfc))

static Version_Type gbl_version = DIAMETER_RFC;

static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
static gchar *diameter_vendor_to_str(guint32 vendorId, gboolean longName);

/*
 * This routine will do a push-parse of the passed in
 * filename.  This was taken almost verbatum from
 * the xmlsoft examples.
 */
static xmlDocPtr
xmlParseFilePush( const char *filename, int checkValid
#ifndef WIRESHARK_XML_DO_VALIDITY_CHECKING
                 _U_
#endif
) {
  FILE *f;
  xmlDocPtr doc=NULL;
#ifdef WIRESHARK_XML_DO_VALIDITY_CHECKING
  int valid=0;
#endif
  int res, size = 1024;
  char chars[1024];
  xmlParserCtxtPtr ctxt;

#ifdef WIRESHARK_XML_DO_VALIDITY_CHECKING
  /* I wonder what kind of a performance hit this is? */
  *XmlStub.xmlDoValidityCheckingDefaultValue = checkValid;
#endif

  f = fopen(filename, "r");
  if (f == NULL) {
	report_open_failure(filename, errno, FALSE);
	return NULL;
  }

  res = fread(chars, 1, 4, f);
  if (res > 0) {
	ctxt = XmlStub.xmlCreatePushParserCtxt(NULL, NULL,
										   chars, res, filename);
	while ((res = fread(chars, 1, size-1, f)) > 0) {
	  XmlStub.xmlParseChunk(ctxt, chars, res, 0);
	}
	XmlStub.xmlParseChunk(ctxt, chars, 0, 1);
	doc = ctxt->myDoc;
#ifdef WIRESHARK_XML_DO_VALIDITY_CHECKING
  valid=ctxt->valid;
#endif
	XmlStub.xmlFreeParserCtxt(ctxt);
  }
  fclose(f);

#ifdef WIRESHARK_XML_DO_VALIDITY_CHECKING
  /* Check valid */
  if (!valid) {
	report_failure( "Error!  Invalid xml in %s!  Failed DTD check!",
			   filename);
	return NULL;
  }
#endif

  return doc;
} /* xmlParseFilePush */

/*
 * This routine will add a static avp to the avp list.  It is
 * only called when the XML dictionary fails to load properly.
 */
static int
addStaticAVP(int code, const gchar *name, diameterDataType type, const value_string *values)
{
  avpInfo *entry;
  ValueName *vEntry=NULL;
  int i;

  /* Parse our values array, if we have one */
  if (values) {
	for (i=0; values[i].strptr != NULL; i++) {
	  ValueName *ve = NULL;

	  ve = g_malloc(sizeof(ValueName));
	  ve->name = strdup(values[i].strptr);
	  ve->value = values[i].value;
	  ve->next = vEntry;
	  vEntry = ve;
	}
  } /* if values */

	/* And, create the entry */
  entry = (avpInfo *)g_malloc(sizeof(avpInfo));
  entry->name = g_strdup(name);
  entry->code = code;
  entry->vendorName = NULL;
  entry->type = type;
  entry->values = vEntry;
  /* Unsigned32 might have values to ( Result-code 268 ) */
  if (vEntry){
		switch(type){
		case DIAMETER_UNSIGNED32:
			entry->type = DIAMETER_UNSIGNED32ENUM;
			break;
		case DIAMETER_VENDOR_ID:
			/* Ignore data from the xml file, use sminmpec.h vals */
			break;
		default:
			entry->type = DIAMETER_ENUMERATED;
	}
  }


  /* And, add it to the list */
  entry->next = avpListHead;
  avpListHead = entry;

  return (0);

} /* addStaticAVP */
/*
 * This routine will add a Vendor avp to the avp list.  It is
 * only called when the XML dictionary fails to load properly.
 */
static int
addVendorAVP(int code, const gchar *name, diameterDataType type, const value_string *values,int vendorId)
{
  avpInfo *entry;
  ValueName *vEntry=NULL;
  gchar *vendorName;
  int i;

  /* Parse our values array, if we have one */
  if (values) {
	for (i=0; values[i].strptr != NULL; i++) {
	  ValueName *ve = NULL;

	  ve = g_malloc(sizeof(ValueName));
	  ve->name = strdup(values[i].strptr);
	  ve->value = values[i].value;
	  ve->next = vEntry;
	  vEntry = ve;
	}
  } /* if values */

	/* And, create the entry */
  entry = (avpInfo *)g_malloc(sizeof(avpInfo));
  entry->name = g_strdup(name);
  entry->code = code;

  vendorName = diameter_vendor_to_str(vendorId, FALSE);
	
  if (vendorName)
	entry->vendorName = g_strdup(vendorName);
  else
	entry->vendorName = NULL;  
  entry->type = type;
  entry->values = vEntry;

  /* Unsigned32 might have values to ( Result-code 268 ) */
  if (vEntry){
		switch(type){
		case DIAMETER_UNSIGNED32:
			entry->type = DIAMETER_UNSIGNED32ENUM;
			break;
		case DIAMETER_VENDOR_ID:
			/* Ignore data from the xml file, use sminmpec.h vals */
			break;
		default:
			entry->type = DIAMETER_ENUMERATED;
	}
  }

  /* And, add it to the list */
  entry->next = avpListHead;
  avpListHead = entry;

  return (0);

} /* addStaticAVP */
/*
 * This routine will parse an XML avp entry, and add it to our
 * avp list.  If any values are present in the avp, it will
 * add them too.
 */
static int
xmlParseAVP(xmlNodePtr cur)
{
  char *name=NULL, *description=NULL, *code=NULL, *mayEncrypt=NULL,
	*mandatory=NULL, *protected=NULL, *vendorBit=NULL, *vendorName = NULL,
	*constrained=NULL;
  char *type=NULL;
  avpInfo *entry;
  guint32 avpType=0;
  ValueName *vEntry=NULL;
  int i;

  /* First, get our properties */
  name = XmlStub.xmlGetProp(cur, "name");
  description = XmlStub.xmlGetProp(cur, "description");
  code = XmlStub.xmlGetProp(cur, "code");
  mayEncrypt = XmlStub.xmlGetProp(cur, "may-encrypt");
  mandatory = XmlStub.xmlGetProp(cur, "mandatory");
  protected = XmlStub.xmlGetProp(cur, "protected");
  vendorBit = XmlStub.xmlGetProp(cur, "vendor-bit");
  vendorName = XmlStub.xmlGetProp(cur, "vendor-id");
  constrained = XmlStub.xmlGetProp(cur, "constrained");

  cur = cur->xmlChildrenNode;

  while (cur != NULL ) {
	if (strcasecmp((const char *)cur->name, "type") == 0) {
	  type = XmlStub.xmlGetProp(cur, "type-name");
	} else if (strcasecmp((const char *)cur->name, "enum") == 0) {
	  char *valueName=NULL, *valueCode=NULL;
	  ValueName *ve = NULL;
	  valueName = XmlStub.xmlGetProp(cur, "name");
	  valueCode = XmlStub.xmlGetProp(cur, "code");

	  if (!valueName || !valueCode) {
		report_failure( "Error, bad value on avp %s", name);
		return (-1);
	  }

	  ve = g_malloc(sizeof(ValueName));
	  ve->name = strdup(valueName);
	  ve->value = atol(valueCode);

	  ve->next = vEntry;
	  vEntry = ve;
	} else if (strcasecmp((const char *)cur->name, "grouped") == 0) {
	  /* WORK Recurse here for grouped AVPs */
	  type = "grouped";
	}
	cur=cur->next;
  } /* while */

	/*
	 * Check for the AVP Type.
	 */
  if (type) {
	for (i = 0; TypeValues[i].strptr; i++) {
	  if (!strcasecmp(type, TypeValues[i].strptr)) {
		avpType = TypeValues[i].value;
		break;
	  }
	}

	if (TypeValues[i].strptr == NULL) {
	  report_failure( "Invalid Type field in dictionary! avp %s (%s)",  name, type);
	  return (-1);
	}
  } else if (!vEntry) {
	report_failure("Missing type/enum field in dictionary avpName=%s",
			  name);
	return (-1);
  }

  /* WORK - Handle flags  -- for validation later */


  /* And, create the entry */
  entry = (avpInfo *)g_malloc(sizeof(avpInfo));
  entry->name = g_strdup(name);
  entry->code = atol(code);
  if (vendorName)
	entry->vendorName = g_strdup(vendorName);
  else
	entry->vendorName = NULL;
  entry->type = avpType;
  entry->values = vEntry;
  /* Unsigned32 might have values to ( Result-code 268 ) */
    if (vEntry)
		switch(avpType){
		case DIAMETER_UNSIGNED32:
			entry->type = DIAMETER_UNSIGNED32ENUM;
			break;
		case DIAMETER_VENDOR_ID:
			/* Ignore data from the xml file, use sminmpec.h vals */
			break;
		default:
			entry->type = DIAMETER_ENUMERATED;
	}

	/* And, add it to the list */
  entry->next = avpListHead;
  avpListHead = entry;

  return (0);
} /* xmlParseAVP */

/*
 * This routine will add a command to the list of commands.
 */
static int
addCommand(int code, const char *name, char *vendorId)
{
  CommandCode *entry;

  /*
   * Allocate the memory required for the dictionary.
   */
  entry = (CommandCode *) g_malloc(sizeof (CommandCode));

  if (entry == NULL) {
	report_failure("Unable to allocate memory");
	return (-1);
  }

  /*
   * Allocate memory for the AVPName and copy the name to the
   * structure
   */
  entry->name = g_strdup(name);
  entry->code = code;
  if (vendorId)
	entry->vendorName = g_strdup(vendorId);
  else
	entry->vendorName = "None";

  /* Add the entry to the list */
  entry->next = commandListHead;
  commandListHead = entry;

  return 0;
} /* addCommand */

/*
 * This routine will parse the XML command, and add it to our
 * list of commands.
 */
static int
xmlParseCommand(xmlNodePtr cur)
{
  char *name, *code, *vendorIdString;

  /*
   * Get the Attributes
   */
  name = XmlStub.xmlGetProp(cur, "name");
  code = XmlStub.xmlGetProp(cur, "code");
  /*
  g_warning("xmlParseCommand Name: %s code %s",name,code);
  */
  if (!name || !code) {
	report_failure("Invalid command.  Name or code missing!");
	return -1;
  }
  vendorIdString = XmlStub.xmlGetProp(cur, "vendor-id");

  if (!vendorIdString || !strcasecmp(vendorIdString, "None")) {
	vendorIdString = NULL;
  }

  return (addCommand(atoi(code), name, vendorIdString));
} /* xmlParseCommand */

/* This routine adds an application to the name<-> id table */
static int
dictionaryAddApplication(char *name, guint32 id)
{
  ApplicationId *entry;

  if (!name || (id == 0 && !allow_zero_as_app_id)) {
	report_failure( "Diameter Error: Invalid application (name=%s, id=%d)",
			   name, id);
	return (-1);
  } /* Sanity Checks */

  entry = g_malloc(sizeof(ApplicationId));
  if (!entry) {
	report_failure( "Unable to allocate memory");
	return (-1);
  }

  entry->name = g_strdup(name);
  entry->id = id;

  /* Add it to the list */
  entry->next = ApplicationIdHead;
  ApplicationIdHead = entry;

  return 0;
} /* dictionaryAddApplication */

/*
 * This routine will add a vendor to the vendors list
 */
static int
addVendor(int id, const gchar *name, const gchar *longName)
{
  VendorId *vendor;

  /* add entry */
  vendor=g_malloc(sizeof(VendorId));
  if (!vendor) {
	return (-1);
  }

  vendor->id = id;
  vendor->name = g_strdup(name);
  vendor->longName = g_strdup(longName);
  vendor->next = vendorListHead;
  vendorListHead = vendor;

  return 0;
} /* addVendor */

/*
 * This routine will pars in a XML vendor entry.
 */
static int
xmlParseVendor(xmlNodePtr cur)
{
  char *name=NULL, *code=NULL, *id=NULL;

  /* First, get our properties */
  id = XmlStub.xmlGetProp(cur, "vendor-id");
  name = XmlStub.xmlGetProp(cur, "name");
  code = XmlStub.xmlGetProp(cur, "code");

  if (!id || !name || !code) {
	report_failure( "Invalid vendor section.  vendor-id, name, and code must be specified");
	return -1;
  }

  return (addVendor(atoi(code), id, name));

} /* addVendor */

/*
 * This routine will either parse in the base protocol, or an application.
 */
static int
xmlDictionaryParseSegment(xmlNodePtr cur, int base)
{
  if (!base) {
	char *name;
	char *id;

	/* Add our application */
	id = XmlStub.xmlGetProp(cur, "id");
	name = XmlStub.xmlGetProp(cur, "name");

	if (!name || !id) {
	  /* ERROR!!! */
	  report_failure("Diameter: Invalid application!: name=\"%s\", id=\"%s\"",
				name?name:"NULL", id?id:"NULL");
	  return -1;
	}
	/* Add the application */
	if (dictionaryAddApplication(name, (guint32)atol(id)) != 0) {
	  /* ERROR! */
	  return -1;
	}
  }


  /*
   * Get segment values
   */
  cur = cur->xmlChildrenNode;
  while (cur != NULL) {
	if (strcasecmp((const char *)cur->name, "avp") == 0) {
	  /* we have an avp!!! */
	  xmlParseAVP(cur);
	} else if (strcasecmp((const char *)cur->name, "vendor") == 0) {
	  /* we have a vendor */
	  xmlParseVendor(cur);
	  /* For now, ignore typedefn and text */
	} else if (strcasecmp((const char *)cur->name, "command") == 0) {
	  /* Found a command */
	  xmlParseCommand(cur);
	} else if (strcasecmp((const char *)cur->name, "text") == 0) {
	} else if (strcasecmp((const char *)cur->name, "comment") == 0) {
	} else if (strcasecmp((const char *)cur->name, "typedefn") == 0) {
	  /* WORK -- parse in valid types . . . */
	} else {
	  /* IF we got here, we're an error */
	  report_failure("Error!  expecting an avp or a typedefn (got \"%s\")",
				cur->name);
	  return (-1);
	}
	cur = cur->next;
  } /* while */
  return 0;
} /* xmlDictionaryParseSegment */

/*
 * The main xml parse routine.  This will walk through an XML
 * dictionary that has been parsed by libxml.
 */
static int
xmlDictionaryParse(xmlNodePtr cur)
{
  /* We should expect a base protocol, followed by multiple applications */
  while (cur != NULL) {
	if (strcasecmp((const char *)cur->name, "base") == 0) {
	  /* Base protocol.  Descend and parse */
	  xmlDictionaryParseSegment(cur, 1);
	} else if (strcasecmp((const char *)cur->name, "application") == 0) {
	  /* Application.  Descend and parse */
	  xmlDictionaryParseSegment(cur, 0);
	} else if (strcasecmp((const char *)cur->name, "text") == 0) {
	  /* Ignore text */
	} else if (strcasecmp((const char *)cur->name, "comment") == 0) {
	  /* Ignore text */
	} else {
	  report_failure( "Diameter: XML Expecting a base or an application  (got \"%s\")",
				 cur->name);
	  return (-1);
	}
	cur = cur->next;
  }

  return 0;

} /* xmlDictionaryParse */

/*
 * This routine will call libxml to parse in the dictionary.
 */
static int
loadXMLDictionary(void)
{
  xmlDocPtr doc;
  xmlNodePtr cur;

  /*
   * build an XML tree from the file;
   */
  XmlStub.xmlKeepBlanksDefault(0);                    /* Strip leading and trailing blanks */
  XmlStub.xmlSubstituteEntitiesDefault(1);            /* Substitute entities automagically */
  doc = xmlParseFilePush(gbl_diameterDictionary, 1);  /* Parse the XML (do validity checks)*/

  /* Check for invalid xml.
     Note that xmlParseFilePush reports details of problems found,
     and it should be obvious from the default filename that the error relates
     to Diameter.
  */
  if (doc == NULL) {
	return -1;
  }

  /*
   * Check the document is of the right kind
   */
  cur = XmlStub.xmlDocGetRootElement(doc);
  if (cur == NULL) {
	report_failure("Diameter: Error: \"%s\": empty document",
			  gbl_diameterDictionary);
	XmlStub.xmlFreeDoc(doc);
	return -1;
  }
  if (XmlStub.xmlStrcmp(cur->name, (const xmlChar *) "dictionary")) {
	report_failure("Diameter: Error: \"%s\": document of the wrong type, root node != dictionary",
			  gbl_diameterDictionary);
	XmlStub.xmlFreeDoc(doc);
	return -1;
  }

  /*
   * Ok, the dictionary has been parsed by libxml, and is valid.
   * All we have to do now is read in our information.
   */
  if (xmlDictionaryParse(cur->xmlChildrenNode) != 0) {
	/* Error has already been printed */
	return -1;
  }

  /* Once we're done parsing, free up the xml memory */
  XmlStub.xmlFreeDoc(doc);

  return 0;

} /* loadXMLDictionary */

/*
 * Fallback routine.  In the event of ANY error when loading the XML
 * dictionary, this routine will populate the new avp list structures
 * with the old static data from packet-diameter-defs.h
 */
static void
initializeDictionaryDefaults(void)
{
  int i;

  /* Add static vendors to list */
  for(i=0; sminmpec_values[i].strptr; i++) {
	addVendor(sminmpec_values[i].value,
			  sminmpec_values[i].strptr,
			  sminmpec_values[i].strptr);

  }
  /* Add static commands to list. */
  for(i=0; diameter_command_code_vals[i].strptr; i++) {
	addCommand(diameter_command_code_vals[i].value,
			   diameter_command_code_vals[i].strptr, NULL);
  }

  /* Add static AVPs to list */
  for (i=0; old_diameter_avps[i].name; i++) {
	addStaticAVP(old_diameter_avps[i].code,
				 old_diameter_avps[i].name,
				 old_diameter_avps[i].type,
				 old_diameter_avps[i].values);
  }
  /* Add 3GPP AVPs to list */
  for (i=0; ThreeGPP_vendor_diameter_avps[i].name; i++) {
	addVendorAVP(ThreeGPP_vendor_diameter_avps[i].code,
				 ThreeGPP_vendor_diameter_avps[i].name,
				 ThreeGPP_vendor_diameter_avps[i].type,
				 ThreeGPP_vendor_diameter_avps[i].values,
				 VENDOR_THE3GPP);
  }

} /* initializeDictionaryDefaults */

/*
 * This routine will attempt to load the XML dictionary if configured to.
 * Otherwise, or if load fails, it will call initializeDictionaryDefaults
 * to load in our static dictionary instead.
 */
static void
initializeDictionary(void)
{
  /*
   * First, empty the dictionary of any previous contents
   */

  ApplicationId *tmpApplicationId = ApplicationIdHead;
  VendorId      *tmpVendorId = vendorListHead;
  CommandCode   *tmpCommandCode = commandListHead;
  avpInfo       *tmpAvpInfo = avpListHead;

  /* ApplicationId list */
  while (tmpApplicationId != NULL) {
    g_free(tmpApplicationId->name);
    tmpApplicationId = tmpApplicationId->next;
  }
  ApplicationIdHead = NULL;

  /* VendorId list */
  while (tmpVendorId != NULL) {
    g_free(tmpVendorId->name);
    g_free(tmpVendorId->longName);
    tmpVendorId = tmpVendorId->next;
  }
  vendorListHead = NULL;

  /* CommandCode list */
  while (tmpCommandCode != NULL) {
    g_free(tmpCommandCode->name);
    g_free(tmpCommandCode->vendorName);
    tmpCommandCode = tmpCommandCode->next;
  }
  commandListHead = NULL;

  /* avpInfo list */
  while (tmpAvpInfo != NULL) {
    ValueName *valueNamePtr = tmpAvpInfo->values;
    g_free(tmpAvpInfo->name);
    g_free(tmpAvpInfo->vendorName);
    while (valueNamePtr) {
      g_free(valueNamePtr->name);
      valueNamePtr = valueNamePtr->next;
    }
    tmpAvpInfo = tmpAvpInfo->next;
  }
  avpListHead = NULL;


  /*
   * Using ugly ordering here.  If loadLibXML succeeds, then
   * loadXMLDictionary will be called.  This is one of the few times when
   * I think this is prettier than the nested if alternative.
   */
   if (gbl_use_xml_dictionary) {
      if (loadLibXML() || (loadXMLDictionary() != 0)) {
	     /* Something failed.  Use the static dictionary */
	     report_failure("Diameter: Using static dictionary! (Unable to use XML)");
	     initializeDictionaryDefaults();
      }
   }
   else {
      initializeDictionaryDefaults();
   }

} /* initializeDictionary */



/*
 * These routines manipulate the diameter structures.
 */

/* return vendor string, based on the id */
static gchar *
diameter_vendor_to_str(guint32 vendorId, gboolean longName) {
  VendorId *probe;
  gchar *buffer;

  for (probe=vendorListHead; probe; probe=probe->next) {
	if (vendorId == probe->id) {
	  if (longName)
		return probe->longName;
	  else
		return probe->name;
	}
  }

  buffer=ep_alloc(64);
  g_snprintf(buffer, 64, "Vendor 0x%08x", vendorId);
  return buffer;
} /*diameter_vendor_to_str */

/* return command string, based on the code */
static gchar *
diameter_command_to_str(guint32 commandCode, guint32 vendorId)
{
  CommandCode *probe;
  gchar *buffer=NULL;
  gchar *vendorName=NULL;

  switch(gbl_version) {
    case DIAMETER_V16:
      /* In draft-v16 version, command code is depending on vendorID */
  if (vendorId)
	vendorName = diameter_vendor_to_str(vendorId, FALSE);

  for (probe=commandListHead; probe; probe=probe->next) {
	if (commandCode == probe->code) {
	  if (vendorId) {
/* 		g_warning("Command: Comparing \"%s\" to \"%s\"", */
/* 				  vendorName?vendorName:"(null)", */
/* 				  probe->vendorName?probe->vendorName:"(null)"); */
		/* Now check the vendor name */
		if (!strcmp(vendorName, probe->vendorName))
		  /* We found it */
		  return probe->name;
	  } else {
		/* With no vendor id, the Command's entry should be "None" */
		if (!strcmp(probe->vendorName, "None")) {
		  /* We found it */
		  return probe->name;
		}
	  }
	}
  }

  if ( suppress_console_output == FALSE )
	  g_warning("Diameter: Unable to find name for command code 0x%08x, Vendor \"%u\"!",
			commandCode, vendorId);
  buffer=ep_alloc(64);
  g_snprintf(buffer, 64,
		   "Cmd-0x%08x", commandCode);
    break;
    case DIAMETER_RFC:
      /* In RFC3588 version, command code is independant on vendorID */
      for (probe=commandListHead; probe; probe=probe->next) {
        if (commandCode == probe->code) {
          /* We found it */
          return probe->name;
        }
      }
  
    if ( suppress_console_output == FALSE )
          g_warning("Diameter: Unable to find name for command code 0x%08x!",
                        commandCode);
    buffer=ep_alloc(64);
    g_snprintf(buffer, 64,
                   "Cmd-0x%08x", commandCode);
    break;
  }
  return buffer;
}/*diameter_command_to_str */

/* return application string, based on the id */
static gchar *
diameter_app_to_str(guint32 appId) {
  ApplicationId *probe;
  gchar *buffer;

  for (probe=ApplicationIdHead; probe; probe=probe->next) {
    if (appId == probe->id) {
      return probe->name;
    }
  }

  buffer=ep_alloc(64);
  g_snprintf(buffer, 64, "Unknown");
  return buffer;
} /*diameter_app_to_str */

/* return an avp type, based on the code */
static diameterDataType
diameter_avp_get_type(guint32 avpCode, guint32 vendorId){
  avpInfo *probe;
  gchar *vendorName=NULL;

  if (vendorId)
	vendorName = diameter_vendor_to_str(vendorId, FALSE);

  for (probe=avpListHead; probe; probe=probe->next) {
	if (avpCode == probe->code) {

	  if (vendorId) {
/* 		g_warning("AvpType: Comparing \"%s\" to \"%s\"", */
/* 				  vendorName?vendorName:"(null)", */
/* 				  probe->vendorName?probe->vendorName:"(null)"); */
		/* Now check the vendor name */
		if (probe->vendorName && (!strcmp(vendorName, probe->vendorName)))
		  /* We found it! */
		  return probe->type;
	  } else {
		/* No Vendor ID -- vendorName should be null */
		if (!probe->vendorName)
		  /* We found it! */
		  return probe->type;
	  }
	}
  }

  /* If we don't find it, assume it's data */
  if ( suppress_console_output == FALSE )
	  g_warning("Diameter: Unable to find type for avpCode %u, Vendor %u!", avpCode,
			vendorId);
  return DIAMETER_OCTET_STRING;
} /* diameter_avp_get_type */

/* return an avp name from the code */
static gchar *
diameter_avp_get_name(guint32 avpCode, guint32 vendorId, gboolean *AVPFound)
{
  gchar *buffer;
  avpInfo *probe;
  gchar *vendorName=NULL;
  *AVPFound = TRUE;    /* will set to FALSE only if fail to match */

  if (vendorId)
	vendorName = diameter_vendor_to_str(vendorId, FALSE);

  for (probe=avpListHead; probe; probe=probe->next) {
	if (avpCode == probe->code) {
	  if (vendorId) {
/* 		g_warning("AvpName: Comparing \"%s\" to \"%s\"", */
/* 				  vendorName?vendorName:"(null)", */
/* 				  probe->vendorName?probe->vendorName:"(null)"); */
		/* Now check the vendor name */
		if (probe->vendorName && (!strcmp(vendorName, probe->vendorName)))
		  /* We found it! */
		  return probe->name;
	  } else {
		/* No Vendor ID -- vendorName should be null */
		if (!probe->vendorName)
		  /* We found it! */
		  return probe->name;
	  }
	}
  }
  if ( suppress_console_output == FALSE )
	  g_warning("Diameter: Unable to find name for AVP 0x%08x, Vendor %u!",
			avpCode, vendorId);

  /* If we don't find it, build a name string */
  buffer=ep_alloc(64);
  g_snprintf(buffer, 64, "Unknown AVP:0x%08x (%d)", avpCode, avpCode);
  *AVPFound = FALSE;
  return buffer;
} /* diameter_avp_get_name */

static const gchar *
diameter_avp_get_value(guint32 avpCode, guint32 vendorId, guint32 avpValue)
{
  avpInfo *probe;
  gchar *vendorName=NULL;

  if (vendorId)
	vendorName = diameter_vendor_to_str(vendorId, FALSE);

  for (probe=avpListHead; probe; probe=probe->next) {
	if (avpCode == probe->code) {
	  if (vendorId) {
/* 		g_warning("AvpValue: Comparing \"%s\" to \"%s\"", */
/* 				  vendorName?vendorName:"(null)", */
/* 				  probe->vendorName?probe->vendorName:"(null)"); */
		/* Now check the vendor name */
		if (probe->vendorName && (!strcmp(vendorName, probe->vendorName))) {
		  ValueName *vprobe;
		  for(vprobe=probe->values; vprobe; vprobe=vprobe->next) {
			if (avpValue == vprobe->value) {
			  return vprobe->name;
			}
		  }
		  return "(Unknown value)";
		}
	  } else {
		if (!probe->vendorName) {
		  ValueName *vprobe;
		  for(vprobe=probe->values; vprobe; vprobe=vprobe->next) {
			if (avpValue == vprobe->value) {
			  return vprobe->name;
			}
		  }
		  return "(Unknown value)";
		}
	  }
	}
  }
  /* We didn't find the avp */
  return "(Unknown AVP)";
} /* diameter_avp_get_value */


/* Code to actually dissect the packets */

static gboolean
check_diameter(tvbuff_t *tvb)
{
  if (!tvb_bytes_exist(tvb, 0, 1))
	return FALSE;	/* not enough bytes to check the version */
  if (tvb_get_guint8(tvb, 0) != 1)
	return FALSE;	/* not version 1 */

  /* XXX - fetch length and make sure it's at least MIN_DIAMETER_SIZE?
     Fetch flags and check that none of the DIAM_FLAGS_RESERVED bits
     are set? */
  return TRUE;
}

/*
 * Main dissector
 */
static void
dissect_diameter_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{

  /* Set up structures needed to add the protocol subtree and manage it */
  proto_item      *ti;
  proto_item      *tf;
  proto_tree      *flags_tree;
  tvbuff_t        *avp_tvb;
  proto_tree      *diameter_tree;
  e_diameterhdr_v16   dh;
  e_diameterhdr_rfc   dh2;
  int              offset=0;
  size_t           avplength=0;
  proto_tree      *avp_tree;
  proto_item      *avptf;
  int              BadPacket = FALSE;
  guint32          commandCode=0, pktLength=0;
  guint8           version=0, flags=0;
  gchar            *flagstr="<None>";
  const gchar     *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Error", "Proxyable", "Request" };
  gchar            *commandString=NULL, *vendorName=NULL, *applicationName=NULL, *commandStringType=NULL;
  gint        i;
  guint      bpos;
  static  int initialized=FALSE;

  /* Keep track of preference settings affecting dictionary source */
  static  gboolean previous_use_xml_dictionary=FALSE;
  #define MAX_DICT_NAME_SIZE 256
  static  gchar    previous_diameterDictionary[MAX_DICT_NAME_SIZE];

  /*
   * Only parse in dictionary if there are diameter packets to
   * dissect.
   * Keeps track of preference settings and frees/reinitializes the
   * dictionary when appropriate.
   */
  if (!initialized ||
      (gbl_use_xml_dictionary != previous_use_xml_dictionary) ||
      (strncmp(gbl_diameterDictionary,
               previous_diameterDictionary,
               MAX_DICT_NAME_SIZE) != 0)) {
      /* Populate dictionary according to preferences */
      initializeDictionary();
      initialized=TRUE;

      /* Record current preference settings */
      previous_use_xml_dictionary = gbl_use_xml_dictionary;
      strncpy(previous_diameterDictionary, gbl_diameterDictionary, MAX_DICT_NAME_SIZE);
  }

  /* Make entries in Protocol column and Info column on summary display */
  if (check_col(pinfo->cinfo, COL_PROTOCOL))
	col_set_str(pinfo->cinfo, COL_PROTOCOL, "Diameter");
  if (check_col(pinfo->cinfo, COL_INFO))
	col_clear(pinfo->cinfo, COL_INFO);

  /* Copy our header */
  switch(gbl_version) {
    case DIAMETER_V16:
  tvb_memcpy(tvb, (guint8*) &dh, offset, sizeof(dh));
  /* Fix byte ordering in our static structure */
  dh.versionLength = g_ntohl(dh.versionLength);
  dh.flagsCmdCode = g_ntohl(dh.flagsCmdCode);
  dh.vendorId = g_ntohl(dh.vendorId);
  dh.hopByHopId = g_ntohl(dh.hopByHopId);
  dh.endToEndId = g_ntohl(dh.endToEndId);
  if (dh.vendorId) {
	vendorName=diameter_vendor_to_str(dh.vendorId, TRUE);
  } else {
	vendorName="None";
  }
  /* Do the bit twiddling */
  version = DIAM_GET_VERSION(dh);
  pktLength = DIAM_GET_LENGTH(dh);
  flags = DIAM_GET_FLAGS(dh);
  commandCode = DIAM_GET_COMMAND(dh);
    break;
    case DIAMETER_RFC:
      tvb_memcpy(tvb, (guint8*) &dh2, offset, sizeof(dh2));
      /* Fix byte ordering in our static structure */
      dh2.versionLength = g_ntohl(dh2.versionLength);
      dh2.flagsCmdCode = g_ntohl(dh2.flagsCmdCode);
      dh2.applicationId = g_ntohl(dh2.applicationId);
      dh2.hopByHopId = g_ntohl(dh2.hopByHopId);
      dh2.endToEndId = g_ntohl(dh2.endToEndId);
      if (dh2.applicationId) {
        applicationName=diameter_app_to_str(dh2.applicationId);
        /* If not found, it might be a vendor ID? */
        if (strcmp(applicationName, "Unknown") == 0){
          applicationName=diameter_vendor_to_str(dh2.applicationId,FALSE);
        }
      } else {
        applicationName="None";
      }
      /* Do the bit twiddling */
      version = DIAM_GET_VERSION(dh2);
      pktLength = DIAM_GET_LENGTH(dh2);
      flags = DIAM_GET_FLAGS(dh2);
      commandCode = DIAM_GET_COMMAND(dh2);
    break;
  }


  /* Set up our flags */
  if (check_col(pinfo->cinfo, COL_INFO) || tree) {
	int fslen;

#define FLAG_STR_LEN 64
	flagstr=ep_alloc(FLAG_STR_LEN);
	flagstr[0]=0;
	fslen=0;
	for (i = 0; i < 8; i++) {
	  bpos = 1 << i;
	  if (flags & bpos) {
		if (flagstr[0]) {
		  fslen+=MIN(FLAG_STR_LEN-fslen,
			     g_snprintf(flagstr+fslen, FLAG_STR_LEN-fslen, ", "));
		}
		fslen+=MIN(FLAG_STR_LEN-fslen,
			   g_snprintf(flagstr+fslen, FLAG_STR_LEN-fslen, "%s", fstr[i]));
	  }
	}
	if (flagstr[0] == 0) {
	  flagstr="<None>";
	}
  }

  /* Set up our commandString */
  switch(gbl_version) {
    case DIAMETER_V16:
      commandString=diameter_command_to_str(commandCode, dh.vendorId);
    break;
    case DIAMETER_RFC:
      /* FIXME: in RFC, is applicationID needed to decode the command code?  */
      commandString=diameter_command_to_str(commandCode, dh2.applicationId);
    break;
  }

  if (flags & DIAM_FLAGS_R)
	commandStringType="Request";
  else
	commandStringType="Answer";

  /* Short packet.  Should have at LEAST one avp */
  if (pktLength < MIN_DIAMETER_SIZE) {
	  if ( suppress_console_output == FALSE )
		  g_warning("Diameter: Packet too short: %u bytes less than min size (%lu bytes))",
			  pktLength, (unsigned long)MIN_DIAMETER_SIZE);
	  BadPacket = TRUE;
  }

  /* And, check our reserved flags/version */
  if ((flags & DIAM_FLAGS_RESERVED) ||
	  (version != 1)) {
	  if ( suppress_console_output == FALSE )
		  g_warning("Diameter: Bad packet: Bad Flags(0x%x) or Version(%u)",
			  flags, version);
	  BadPacket = TRUE;
  }

  if (check_col(pinfo->cinfo, COL_INFO)) {
    switch(gbl_version) {
      case DIAMETER_V16:
        col_add_fstr(pinfo->cinfo, COL_INFO,
                     "%s%s%s%s%s-%s vendor=%s (hop-id=%u) (end-id=%u) RPE=%d%d%d",
                     (BadPacket)?"***** Bad Packet!: ":"",
                     (flags & DIAM_FLAGS_P)?"Proxyable ":"",
                     (flags & DIAM_FLAGS_E)?" Error":"",
                     ((BadPacket ||
                       (flags & (DIAM_FLAGS_P|DIAM_FLAGS_E))) ?
                       ": " : ""),
                     commandString, commandStringType, vendorName,
                     dh.hopByHopId, dh.endToEndId,
                     (flags & DIAM_FLAGS_R)?1:0,
                     (flags & DIAM_FLAGS_P)?1:0,
                     (flags & DIAM_FLAGS_E)?1:0);
      break;
      case DIAMETER_RFC:
        col_add_fstr(pinfo->cinfo, COL_INFO,
                     "%s%s%s%s%s-%s app=%s (hop-id=%u) (end-id=%u) RPE=%d%d%d",
                     (BadPacket)?"***** Bad Packet!: ":"",
                     (flags & DIAM_FLAGS_P)?"Proxyable ":"",
                     (flags & DIAM_FLAGS_E)?" Error":"",
                     ((BadPacket ||
                       (flags & (DIAM_FLAGS_P|DIAM_FLAGS_E))) ?
                       ": " : ""),
                     commandString, commandStringType, applicationName,
                     dh2.hopByHopId, dh2.endToEndId,
                     (flags & DIAM_FLAGS_R)?1:0,
                     (flags & DIAM_FLAGS_P)?1:0,
                     (flags & DIAM_FLAGS_E)?1:0);
      break;
    }
  }


  /* In the interest of speed, if "tree" is NULL, don't do any work not
	 necessary to generate protocol tree items. */
  if (tree) {

	/* create display subtree for the protocol */
	ti = proto_tree_add_item(tree, proto_diameter, tvb, offset,
							 MAX(pktLength,MIN_DIAMETER_SIZE), FALSE);
	diameter_tree = proto_item_add_subtree(ti, ett_diameter);

	/* Version */
	proto_tree_add_uint(diameter_tree,
						hf_diameter_version,
						tvb, offset, 1,
						version);

	offset+=1;

	/* Length */
	proto_tree_add_uint(diameter_tree,
						hf_diameter_length, tvb,
						offset, 3, pktLength);
	offset += 3;

	/* Flags */
	tf = proto_tree_add_uint_format_value(diameter_tree, hf_diameter_flags, tvb,
					      offset, 1, flags, "0x%02x (%s)", flags,
					      flagstr);
	flags_tree = proto_item_add_subtree(tf, ett_diameter_avp_flags);
	proto_tree_add_boolean(flags_tree, hf_diameter_flags_request, tvb, offset, 1, flags);
	proto_tree_add_boolean(flags_tree, hf_diameter_flags_proxyable, tvb, offset, 1, flags);
	proto_tree_add_boolean(flags_tree, hf_diameter_flags_error, tvb, offset, 1, flags);
	proto_tree_add_boolean(flags_tree, hf_diameter_flags_T, tvb, offset, 1, flags);
	proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved4, tvb, offset, 1, flags);
	proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved5, tvb, offset, 1, flags);
	proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved6, tvb, offset, 1, flags);
	proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved7, tvb, offset, 1, flags);

	offset += 1;

	/* Command Code */
	proto_tree_add_uint_format_value(diameter_tree, hf_diameter_code,
	                                 tvb, offset, 3, commandCode, "%s-%s (%d)",
	                                 commandString, commandStringType, commandCode);
	offset += 3;

        switch(gbl_version) {
          case DIAMETER_V16:

			/* Vendor Id */
			proto_tree_add_item(diameter_tree, hf_diameter_vendor_id, tvb, offset, 4, FALSE);
			offset += 4;
			/* Hop-by-hop Identifier */
			proto_tree_add_uint(diameter_tree, hf_diameter_hopbyhopid,
						tvb, offset, 4, dh.hopByHopId);
			offset += 4;
			/* End-to-end Identifier */
			proto_tree_add_uint(diameter_tree, hf_diameter_endtoendid,
						tvb, offset, 4, dh.endToEndId);
			offset += 4;
			break;
          case DIAMETER_RFC:
		    /* Application Id */
			proto_tree_add_item(diameter_tree, hf_diameter_application_id, tvb, offset, 4, FALSE);
		    offset += 4;
		    /* Hop-by-hop Identifier */
		    proto_tree_add_uint(diameter_tree, hf_diameter_hopbyhopid,
						tvb, offset, 4, dh2.hopByHopId);
		    offset += 4;
		    /* End-to-end Identifier */
		    proto_tree_add_uint(diameter_tree, hf_diameter_endtoendid,
						tvb, offset, 4, dh2.endToEndId);
		    offset += 4;
	          break;
        }


	/* If we have a bad packet, don't bother trying to parse the AVPs */
	if (BadPacket) {
	  return;
	}

	/* Start looking at the AVPS */
	/* Make the next tvbuff */

	/* Update the lengths */
        switch(gbl_version) {
          case DIAMETER_V16:
	    avplength= pktLength - sizeof(e_diameterhdr_v16);
	  break;
	  case DIAMETER_RFC:
	    avplength= pktLength - sizeof(e_diameterhdr_rfc);
	  break;
	}

	avp_tvb = tvb_new_subset(tvb, offset, avplength, avplength);
	avptf = proto_tree_add_text(diameter_tree,
								tvb, offset, avplength,
								"Attribute Value Pairs");

	avp_tree = proto_item_add_subtree(avptf,
									  ett_diameter_avp);
	if (avp_tree != NULL) {
	  dissect_avps( avp_tvb, pinfo, avp_tree);
	}
	return;
  }
} /* dissect_diameter_common */


static guint
get_diameter_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
{
  /* Get the length of the Diameter packet. */
  return tvb_get_ntoh24(tvb, offset + 1);
}

static int
dissect_diameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  if (!check_diameter(tvb))
	return 0;
  dissect_diameter_common(tvb, pinfo, tree);
  return tvb_length(tvb);
}

static void
dissect_diameter_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
  tcp_dissect_pdus(tvb, pinfo, tree, gbl_diameter_desegment, 4,
	get_diameter_pdu_len, dissect_diameter_common);
} /* dissect_diameter_tcp */

/*
 * Call the mip_dissector, after saving our pinfo variables
 * so it doesn't write to our column display.
 */
static void
safe_dissect_mip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
				 size_t offset, size_t length)
{
  static dissector_handle_t mip_handle;
  static int mipInitialized=FALSE;
  tvbuff_t *mip_tvb;
  address save_dl_src;
  address save_dl_dst;
  address save_net_src;
  address save_net_dst;
  address save_src;
  address save_dst;
  gboolean save_in_error_pkt;

  if (!mipInitialized) {
	mip_handle = find_dissector("mip");
	mipInitialized=TRUE;
  }

  mip_tvb = tvb_new_subset(tvb, offset,
						   MIN(length, tvb_length(tvb)-offset),
						   length);

  /* The contained packet is a MIP registration request;
	 dissect it with the MIP dissector. */
  col_set_writable(pinfo->cinfo, FALSE);

  /* Also, save the current values of the addresses, and restore
	 them when we're finished dissecting the contained packet, so
	 that the address columns in the summary don't reflect the
	 contained packet, but reflect this packet instead. */
  save_dl_src = pinfo->dl_src;
  save_dl_dst = pinfo->dl_dst;
  save_net_src = pinfo->net_src;
  save_net_dst = pinfo->net_dst;
  save_src = pinfo->src;
  save_dst = pinfo->dst;
  save_in_error_pkt = pinfo->in_error_pkt;

  call_dissector(mip_handle, mip_tvb, pinfo, tree);

  /* Restore the "we're inside an error packet" flag. */
  pinfo->in_error_pkt = save_in_error_pkt;
  pinfo->dl_src = save_dl_src;
  pinfo->dl_dst = save_dl_dst;
  pinfo->net_src = save_net_src;
  pinfo->net_dst = save_net_dst;
  pinfo->src = save_src;
  pinfo->dst = save_dst;


} /* safe_dissect_mip */

/*
 * This function will dissect the AVPs in a diameter packet.  It handles
 * all normal types, and even recursively calls itself for grouped AVPs
 */
static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *avp_tree)
{
	/* adds the attribute value pairs to the tree */
	e_avphdr avph;
	const gchar *avpTypeString;
	const gchar *avpNameString;
	const gchar *valstr;
	guint32 vendorId=0;
	gchar    *vendorName;
	int hdrLength;
	int fixAmt;
	proto_tree *avpi_tree;
	size_t offset = 0;
	tvbuff_t        *group_tvb;
	proto_tree *group_tree;
	proto_item *grouptf;
	proto_item *avptf;
	char *buffer;
	int BadPacket = FALSE;
	guint32 avpLength;
	guint8 flags;
	proto_item      *tf;
	proto_tree      *flags_tree;

	gint32 packetLength;
	size_t avpDataLength;
	int avpType;
	gchar *flagstr="<None>";
	const gchar *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Protected", "Mandatory", "Vendor-Specific" };
	gint       i;
	guint      bpos;

	packetLength = tvb_length(tvb);
	
	/* Check for invalid packet lengths */
	if (packetLength <= 0) {
		proto_tree_add_text(avp_tree, tvb, offset, tvb_length(tvb),
		                    "No Attribute Value Pairs Found");
		return;
	}

	/* Spin around until we run out of packet */
	while (packetLength > 0 )
	{
		gboolean AVP_code_found;
		
		/* Check for short packet */
		if (packetLength < (long)MIN_AVP_SIZE) {
			if ( suppress_console_output == FALSE )
				g_warning("Diameter: AVP Payload too short: %d bytes less than min size (%ld bytes))",
				          packetLength, (long)MIN_AVP_SIZE);
			BadPacket = TRUE;
			/* Don't even bother trying to parse a short packet. */
			return;
		}

		/* Copy our header */
		tvb_memcpy(tvb, (guint8*) &avph, offset, MIN((long)sizeof(avph),packetLength));
	
		/* Fix the byte ordering */
		avph.avp_code = g_ntohl(avph.avp_code);
		avph.avp_flagsLength = g_ntohl(avph.avp_flagsLength);
	
		flags = (avph.avp_flagsLength & 0xff000000) >> 24;
		avpLength = avph.avp_flagsLength & 0x00ffffff;
	
		/* Set up our flags string */
		if (check_col(pinfo->cinfo, COL_INFO) || avp_tree) {
			int fslen;

#define FLAG_STR_LEN 64
			flagstr=ep_alloc(FLAG_STR_LEN);
			flagstr[0]=0;
			fslen=0;
			for (i = 0; i < 8; i++) {
				bpos = 1 << i;
				if (flags & bpos) {
					if (flagstr[0]) {
						fslen+=MIN(FLAG_STR_LEN-fslen,
						           g_snprintf(flagstr+fslen, FLAG_STR_LEN-fslen, ", "));
					}
					fslen+=MIN(FLAG_STR_LEN-fslen,
					               g_snprintf(flagstr+fslen, FLAG_STR_LEN-fslen, "%s", fstr[i]));
				}
			}
			if (flagstr[0] == 0) {
				flagstr="<None>";
			}
		}

		/* Dissect our vendor id if it exists  and set hdr length */
		if (flags & AVP_FLAGS_V) {
			vendorId = g_ntohl(avph.avp_vendorId);
			/* Vendor id */
			hdrLength = sizeof(e_avphdr);
		} else {
			/* No vendor */
			hdrLength = sizeof(e_avphdr) - sizeof(guint32);
			vendorId = 0;
		}

		if (vendorId) {
			vendorName=diameter_vendor_to_str(vendorId, TRUE);
		} else {
			vendorName="";
		}

		/* Check for bad length */
		if (avpLength < MIN_AVP_SIZE ||
		    ((long)avpLength > packetLength))
		{
			if ( suppress_console_output == FALSE )
				g_warning("Diameter: AVP payload size invalid: avp_length: %ld bytes,  "
				          "min: %ld bytes,    packetLen: %d",
				          (long)avpLength, (long)MIN_AVP_SIZE, packetLength);
			BadPacket = TRUE;
		}

		/* Check for bad flags */
		if (flags & AVP_FLAGS_RESERVED) {
			if ( suppress_console_output == FALSE )
				g_warning("Diameter: Invalid AVP: Reserved bit set.  flags = 0x%x,"
				          " resFl=0x%x",
				          flags, AVP_FLAGS_RESERVED);
				/* For now, don't set bad packet, since I'm accidentally setting a wrong bit 
				BadPacket = TRUE; 
				*/
		}

		/*
		 * Compute amount of byte-alignment fix (Diameter AVPs are sent on 4 byte
		 * boundries)
		 */
		fixAmt = 4 - (avpLength % 4);
		if (fixAmt == 4)
			fixAmt = 0;

		/* shrink our packetLength */
		packetLength = packetLength - (avpLength + fixAmt);

		/* Check for out of bounds */
		if (packetLength < 0) {
			if ( suppress_console_output == FALSE )
				g_warning("Diameter: Bad AVP: Bad new length (%d bytes) ",
				          packetLength);
			BadPacket = TRUE;
		}

		/* Make avp Name & type */
		avpTypeString=val_to_str(diameter_avp_get_type(avph.avp_code,vendorId),
		                         TypeValues,
		                         "Unknown-Type: 0x%08x");
		avpNameString=diameter_avp_get_name(avph.avp_code, vendorId, &AVP_code_found);

		avptf = proto_tree_add_text(avp_tree, tvb,
		                            offset, avpLength + fixAmt,
		                            "%s (%s) l:0x%x (%d bytes) (%d padded bytes)",
		                            avpNameString, avpTypeString, avpLength,
		                            avpLength, avpLength+fixAmt);
		avpi_tree = proto_item_add_subtree(avptf, ett_diameter_avpinfo);

		if (avpi_tree !=NULL)
		{
			/* Command Code */
			proto_item *ti;
			ti = proto_tree_add_uint_format_value(avpi_tree, hf_diameter_avp_code,
			                                      tvb, offset, 4, avph.avp_code, "%s (%u)",
			                                      avpNameString,avph.avp_code);
			if (!AVP_code_found)
			{
				expert_add_info_format(pinfo, ti,
				                       PI_UNDECODED, PI_NOTE,
				                       "AVP info not available (code %u)",
				                       avph.avp_code);
			}
			offset += 4;

			tf = proto_tree_add_uint_format_value(avpi_tree, hf_diameter_avp_flags, tvb,
			                                      offset, 1, flags, "0x%02x (%s)", flags,
			                                      flagstr);
			flags_tree = proto_item_add_subtree(tf, ett_diameter_avp_flags);
			proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_vendor_specific, tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_mandatory, tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_protected, tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved3,  tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved4,  tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved5,  tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved6,  tvb, offset, 1, flags);
			proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved7,  tvb, offset, 1, flags);
			offset += 1;

			proto_tree_add_uint(avpi_tree, hf_diameter_avp_length,
			                    tvb, offset, 3, avpLength);
			offset += 3;

			if (flags & AVP_FLAGS_V) {
				proto_tree_add_uint_format_value(avpi_tree, hf_diameter_avp_vendor_id,
				                                 tvb, offset, 4, vendorId, "%s (%u)", vendorName, vendorId);
				offset += 4;
			}

			avpDataLength = avpLength - hdrLength;

			/*
			* If we've got a bad packet, just highlight the data.  Don't try
			* to parse it, and, don't move to next AVP.
			*/
			if (BadPacket) {
				offset -= hdrLength;
				proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
				                            tvb, offset, tvb_length(tvb) - offset,
				                            tvb_get_ptr(tvb, offset, tvb_length(tvb) - offset),
				                            "Bad AVP (Suspect Data Not Dissected)");
				return;
			}

			avpType=diameter_avp_get_type(avph.avp_code,vendorId);

			switch(avpType)
			{
				case DIAMETER_GROUPED:
					buffer=ep_alloc(256);
					g_snprintf(buffer, 256, "%s Grouped AVPs", avpNameString);
					/* Recursively call ourselves */
					grouptf = proto_tree_add_text(avpi_tree,
					                              tvb, offset, tvb_length(tvb),
					                              buffer);

					group_tree = proto_item_add_subtree(grouptf, ett_diameter_avp);

					group_tvb = tvb_new_subset(tvb, offset,
					                           MIN(avpDataLength, tvb_length(tvb)-offset),
					                           avpDataLength);
					if (group_tree != NULL) {
						dissect_avps( group_tvb, pinfo, group_tree);
					}
					break;
	
				case DIAMETER_IDENTITY:
				{
					const guint8 *data;

					data = tvb_get_ptr(tvb, offset, avpDataLength);
					proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
					                             tvb, offset, avpDataLength, data,
					                             "Identity: %*.*s",
					                             (int)avpDataLength,
					                             (int)avpDataLength, data);
				}
				break;
				case DIAMETER_UTF8STRING:
				{
					const guint8 *data;
		
					data = tvb_get_ptr(tvb, offset, avpDataLength);
					proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
					                             tvb, offset, avpDataLength, data,
					                             "UTF8String: %*.*s",
					                             (int)avpDataLength,
					                             (int)avpDataLength, data);
				}
					break;
				case DIAMETER_IP_ADDRESS:
				{
					switch(gbl_version)
					{
						case DIAMETER_V16:
							if (avpDataLength == 4) {
								proto_tree_add_item(avpi_tree, hf_diameter_avp_data_v4addr,
								                    tvb, offset, avpDataLength, FALSE);
							} else if (avpDataLength == 16) {
								proto_tree_add_item(avpi_tree, hf_diameter_avp_data_v6addr,
								                    tvb, offset, avpDataLength, FALSE);
							} else {
								ti = proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
								                                 tvb, offset, avpDataLength,
								                                 tvb_get_ptr(tvb, offset, avpDataLength),
								                                 "Error! Bad Address Length (Address in RFC3588 format?)");
								expert_add_info_format(pinfo, ti,
								                       PI_MALFORMED, PI_NOTE,
								                       "Bad Address Length (%u)",
								                       avpDataLength);
							}
							break;
						case DIAMETER_RFC:
							/* Indicate the address family */
							proto_tree_add_item(avpi_tree, hf_diameter_avp_data_addrfamily,
							                    tvb, offset, 2, FALSE);
							if (tvb_get_ntohs(tvb, offset) == 0x0001) {
								proto_tree_add_item(avpi_tree, hf_diameter_avp_data_v4addr,
								                    tvb, offset+2, avpDataLength-2, FALSE);
							} else if (tvb_get_ntohs(tvb, offset) == 0x0002) {
								proto_tree_add_item(avpi_tree, hf_diameter_avp_data_v6addr,
								                    tvb, offset+2, avpDataLength-2, FALSE);
							} else {
								proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
								                            tvb, offset, avpDataLength,
								                            tvb_get_ptr(tvb, offset, avpDataLength),
								                            "Error! Can't Parse Address Family %d (Address in draft v16 format?)",
								                            (int)tvb_get_ntohs(tvb, offset));
							}
							break;
					}
				}
					break;

				case DIAMETER_INTEGER32:
					if (avpDataLength == 4) {
						proto_tree_add_item(avpi_tree, hf_diameter_avp_data_int32,
						                    tvb, offset, avpDataLength, FALSE);
					} else {
						ti = proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
						                                 tvb, offset, avpDataLength,
						                                 tvb_get_ptr(tvb, offset, avpDataLength),
						                                "Error!  Bad Integer32 Length");
						expert_add_info_format(pinfo, ti,
						                       PI_MALFORMED, PI_NOTE,
						                       "Bad Integer32 Length (%u)",
						                       avpDataLength);
					}
					break;

				case DIAMETER_UNSIGNED32:
					if (avpDataLength == 4) {
						guint32 data;
		
						data = tvb_get_ntohl(tvb, offset);
						proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
						                           tvb, offset, avpDataLength, data,
						                           "Value: 0x%08x (%u)", data, data);
					} else {
						ti = proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
						                                 tvb, offset, avpDataLength,
						                                 tvb_get_ptr(tvb, offset, avpDataLength),
						                                 "Error!  Bad Unsigned32 Length");
						expert_add_info_format(pinfo, ti,
						                       PI_MALFORMED, PI_NOTE,
						                       "Bad Unsigned32 Length (%u)",
						                       avpDataLength);
					}
					break;

				case DIAMETER_UNSIGNED32ENUM:
					if (avpDataLength == 4) {
						guint32 data;
			
						data = tvb_get_ntohl(tvb, offset);
						valstr = diameter_avp_get_value(avph.avp_code, vendorId, data);
						proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
						                           tvb, offset, avpDataLength, data,
						                           "Value: 0x%08x (%u): %s", data,
						                           data, valstr);
					} else {
						ti = proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
						                                tvb, offset, avpDataLength,
						                                tvb_get_ptr(tvb, offset, avpDataLength),
						                                "Error!  Bad Enumerated Length");
						expert_add_info_format(pinfo, ti,
						                       PI_MALFORMED, PI_NOTE,
						                       "Bad Enumerated Length (%u)",
						                       avpDataLength);
					}
					break;

				case DIAMETER_INTEGER64:
					if (avpDataLength == 8) {
						proto_tree_add_item(avpi_tree, hf_diameter_avp_data_int64,
						                    tvb, offset, 8, FALSE);
					} else {
						ti = proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
						                            tvb, offset, avpDataLength,
						                            tvb_get_ptr(tvb, offset, avpDataLength),
						                            "Error!  Bad Integer64 Length");
						expert_add_info_format(pinfo, ti,
						                       PI_MALFORMED, PI_NOTE,
						                       "Bad Integer64 Length (%u)",
						                       avpDataLength);
					}
					break;

				case DIAMETER_UNSIGNED64:
					if (avpDataLength == 8) {
						proto_tree_add_item(avpi_tree, hf_diameter_avp_data_uint64,
						                    tvb, offset, 8, FALSE);
					} else {
						ti = proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
						                                tvb, offset, avpDataLength,
						                                tvb_get_ptr(tvb, offset, avpDataLength),
						                               "Error!  Bad Unsigned64 Length");
						expert_add_info_format(pinfo, ti,
						                       PI_MALFORMED, PI_NOTE,
						                       "Bad Unsigned64 Length (%u)",
						                       avpDataLength);
					}
					break;

				case DIAMETER_TIME:
					if (avpDataLength == 4) {
						nstime_t data;
						struct tm *gmtp;
		
						data.secs = tvb_get_ntohl(tvb, offset);
						/* Present the time as UTC, Time before 00:00:00 UTC, January 1, 1970 can't be presented correctly  */
						if ( data.secs >= NTP_TIME_DIFF){
							data.secs -= NTP_TIME_DIFF;
							data.nsecs = 0;
		
							gmtp = gmtime(&data.secs);
							buffer=ep_alloc(64);
							strftime(buffer, 64, "%a, %d %b %Y %H:%M:%S UTC", gmtp);
		
							proto_tree_add_time_format(avpi_tree, hf_diameter_avp_data_time,
							                           tvb, offset, avpDataLength, &data,
							                           "Time: %s", buffer);
						}else{
							proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
							                            tvb, offset, avpDataLength,
							                            tvb_get_ptr(tvb, offset, avpDataLength),
							                            "Error!  Time before 00:00:00 UTC, January 1, 1970");
						}
					} else {
						ti = proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
						                                 tvb, offset, avpDataLength,
						                                 tvb_get_ptr(tvb, offset, avpDataLength),
						                                 "Error!  Bad Time Length");
						expert_add_info_format(pinfo, ti,
						                       PI_MALFORMED, PI_NOTE,
						                       "Bad Time Length (%u)",
						                       avpDataLength);
					}
					break;

				case DIAMETER_ENUMERATED:
					if (avpDataLength == 4) {
						guint32 data;
		
						data = tvb_get_ntohl(tvb, offset);
						valstr = diameter_avp_get_value(avph.avp_code, vendorId, data);
						proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
						                           tvb, offset, avpDataLength, data,
						                           "Value: 0x%08x (%u): %s", data,
						                           data, valstr);
					} else {
						ti = proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
						                                 tvb, offset, avpDataLength,
						                                 tvb_get_ptr(tvb, offset, avpDataLength),
						                                 "Error!  Bad Enumerated Length");
						expert_add_info_format(pinfo, ti,
						                       PI_MALFORMED, PI_NOTE,
						                       "Bad Enumerated Length (%u)",
						                       avpDataLength);
					}
					break;

				case DIAMETER_VENDOR_ID:
					if (avpDataLength == 4) {
						proto_tree_add_item(avpi_tree, hf_diameter_vendor_id, tvb, offset, avpDataLength, FALSE);
					} else {
						ti = proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
						                            tvb, offset, avpDataLength,
						                            tvb_get_ptr(tvb, offset, avpDataLength),
						                            "Error!  Bad Vendor ID Length");
						expert_add_info_format(pinfo, ti,
						                       PI_MALFORMED, PI_NOTE,
						                       "Bad Vendor ID Length (%u)",
						                       avpDataLength);
					}
					break;

				case DIAMETER_APPLICATION_ID:
					if (avpDataLength == 4) {
						guint32 data;

						data = tvb_get_ntohl(tvb, offset);
						valstr = diameter_app_to_str(data);
						proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
						                           tvb, offset, avpDataLength, data,
						                           "Application ID: %s %d (0x%08x)",
						                           valstr, data, data);
					} else {
						ti = proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
						                                 tvb, offset, avpDataLength,
						                                 tvb_get_ptr(tvb, offset, avpDataLength),
						                                 "Error!  Bad Application ID Length");
						expert_add_info_format(pinfo, ti,
						                       PI_MALFORMED, PI_NOTE,
						                       "Bad Application ID Length (%u)",
						                       avpDataLength);
					}
					break;

				case DIAMETER_MIP_REG_REQ:
					safe_dissect_mip(tvb, pinfo, avpi_tree, offset, avpDataLength);
					break;

				case DIAMETER_URI:
					proto_tree_add_item(avpi_tree, hf_diameter_avp_diameter_uri,
					                    tvb, offset, avpDataLength, FALSE);
					break;

				case DIAMETER_SESSION_ID:
					proto_tree_add_item(avpi_tree, hf_diameter_avp_session_id,
					                    tvb, offset, avpDataLength, FALSE);
					break;

				case DIAMETER_PUBLIC_ID:
				{
					proto_tree_add_item(avpi_tree, hf_diameter_avp_public_id,
					                    tvb, offset, avpDataLength, FALSE);
					/* This is a SIP address, to be able to filter the SIP messages
					* belonging to this Diameter session add this to the SIP filter.
					*/
					dfilter_store_sip_from_addr(tvb, avpi_tree, offset, avpDataLength);
				}
					break;
				case DIAMETER_PRIVATE_ID:
					proto_tree_add_item(avpi_tree, hf_diameter_avp_private_id,
					                    tvb, offset, avpDataLength, FALSE);
					break;

				default:
				case DIAMETER_OCTET_STRING:
					proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
					                            tvb, offset, avpDataLength,
					                            tvb_get_ptr(tvb, offset, avpDataLength),
					                            "Hex Data Highlighted Below");
					break;
			} /* switch type */
		} /* avpi_tree != null */
		offset += (avpLength - hdrLength);
		offset += fixAmt; /* fix byte alignment */
	} /* loop */
} /* dissect_avps */



void
proto_reg_handoff_diameter(void)
{
  static int Initialized=FALSE;
  static int TcpPort=0;
  static int SctpPort=0;
  static dissector_handle_t diameter_tcp_handle;
  static dissector_handle_t diameter_handle;

  if (!Initialized) {
	diameter_tcp_handle = create_dissector_handle(dissect_diameter_tcp,
	    proto_diameter);
	diameter_handle = new_create_dissector_handle(dissect_diameter,
	    proto_diameter);
	Initialized=TRUE;
  } else {
	dissector_delete("tcp.port", TcpPort, diameter_tcp_handle);
	dissector_delete("sctp.port", SctpPort, diameter_handle);
  }

  /* set port for future deletes */
  TcpPort=gbl_diameterTcpPort;
  SctpPort=gbl_diameterSctpPort;

  /* g_warning ("Diameter: Adding tcp dissector to port %d",
	 gbl_diameterTcpPort); */
  dissector_add("tcp.port", gbl_diameterTcpPort, diameter_tcp_handle);
  dissector_add("sctp.port", gbl_diameterSctpPort, diameter_handle);
}

/* registration with the filtering engine */
void
proto_register_diameter(void)
{
	static hf_register_info hf[] = {
		{ &hf_diameter_version,
		  { "Version", "diameter.version", FT_UINT8, BASE_HEX, NULL, 0x00,
		    "", HFILL }},
		{ &hf_diameter_length,
		  { "Length","diameter.length", FT_UINT24, BASE_DEC, NULL, 0x0,
		    "", HFILL }},

		{ &hf_diameter_flags,
		  { "Flags", "diameter.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
		    "", HFILL }},
		{ &hf_diameter_flags_request,
		  { "Request", "diameter.flags.request", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_R,
			"", HFILL }},
		{ &hf_diameter_flags_proxyable,
		  { "Proxyable", "diameter.flags.proxyable", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_P,
			"", HFILL }},
		{ &hf_diameter_flags_error,
		  { "Error","diameter.flags.error", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_E,
			"", HFILL }},
		{ &hf_diameter_flags_T,
		  { "T(Potentially re-transmitted message)","diameter.flags.T", FT_BOOLEAN, 8, TFS(&flags_set_truth),DIAM_FLAGS_T,
			"", HFILL }},
		{ &hf_diameter_flags_reserved4,
		  { "Reserved","diameter.flags.reserved4", FT_BOOLEAN, 8, TFS(&reserved_set),
			DIAM_FLAGS_RESERVED4, "", HFILL }},
		{ &hf_diameter_flags_reserved5,
		  { "Reserved","diameter.flags.reserved5", FT_BOOLEAN, 8, TFS(&reserved_set),
			DIAM_FLAGS_RESERVED5, "", HFILL }},
		{ &hf_diameter_flags_reserved6,
		  { "Reserved","diameter.flags.reserved6", FT_BOOLEAN, 8, TFS(&reserved_set),
			DIAM_FLAGS_RESERVED6, "", HFILL }},
		{ &hf_diameter_flags_reserved7,
		  { "Reserved","diameter.flags.reserved7", FT_BOOLEAN, 8, TFS(&reserved_set),
			DIAM_FLAGS_RESERVED7, "", HFILL }},

		{ &hf_diameter_code,
		  { "Command Code","diameter.code", FT_UINT24, BASE_DEC,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_vendor_id,
		  { "VendorId",	"diameter.vendorId", FT_UINT32, BASE_DEC, VALS(sminmpec_values),
			0x0,"", HFILL }},
		{ &hf_diameter_application_id,
		  { "ApplicationId",	"diameter.applicationId", FT_UINT32, BASE_DEC, VALS(diameter_application_id_vals),
			0x0,"", HFILL }},
		{ &hf_diameter_hopbyhopid,
		  { "Hop-by-Hop Identifier", "diameter.hopbyhopid", FT_UINT32,
		    BASE_HEX, NULL, 0x0, "", HFILL }},
		{ &hf_diameter_endtoendid,
		  { "End-to-End Identifier", "diameter.endtoendid", FT_UINT32,
		    BASE_HEX, NULL, 0x0, "", HFILL }},

		{ &hf_diameter_avp_code,
		  { "AVP Code","diameter.avp.code", FT_UINT32, BASE_DEC,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_length,
		  { "AVP Length","diameter.avp.length", FT_UINT24, BASE_DEC,
		    NULL, 0x0, "", HFILL }},


		{ &hf_diameter_avp_flags,
		  { "AVP Flags","diameter.avp.flags", FT_UINT8, BASE_HEX,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_flags_vendor_specific,
		  { "Vendor-Specific", "diameter.flags.vendorspecific", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_V,
			"", HFILL }},
		{ &hf_diameter_avp_flags_mandatory,
		  { "Mandatory", "diameter.flags.mandatory", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_M,
			"", HFILL }},
		{ &hf_diameter_avp_flags_protected,
		  { "Protected","diameter.avp.flags.protected", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_P,
			"", HFILL }},
		{ &hf_diameter_avp_flags_reserved3,
		  { "Reserved","diameter.avp.flags.reserved3", FT_BOOLEAN, 8, TFS(&reserved_set),
			AVP_FLAGS_RESERVED3,	"", HFILL }},
		{ &hf_diameter_avp_flags_reserved4,
		  { "Reserved","diameter.avp.flags.reserved4", FT_BOOLEAN, 8, TFS(&reserved_set),
			AVP_FLAGS_RESERVED4,	"", HFILL }},
		{ &hf_diameter_avp_flags_reserved5,
		  { "Reserved","diameter.avp.flags.reserved5", FT_BOOLEAN, 8, TFS(&reserved_set),
			AVP_FLAGS_RESERVED5,	"", HFILL }},
		{ &hf_diameter_avp_flags_reserved6,
		  { "Reserved","diameter.avp.flags.reserved6", FT_BOOLEAN, 8, TFS(&reserved_set),
			AVP_FLAGS_RESERVED6,	"", HFILL }},
		{ &hf_diameter_avp_flags_reserved7,
		  { "Reserved","diameter.avp.flags.reserved7", FT_BOOLEAN, 8, TFS(&reserved_set),
			AVP_FLAGS_RESERVED7,	"", HFILL }},
		{ &hf_diameter_avp_vendor_id,
		  { "AVP Vendor Id","diameter.avp.vendorId", FT_UINT32, BASE_DEC,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_data_uint64,
		  { "Value","diameter.avp.data.uint64", FT_UINT64, BASE_DEC,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_data_int64,
		  { "Value","diameter.avp.data.int64", FT_INT64, BASE_DEC,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_data_uint32,
		  { "Value","diameter.avp.data.uint32", FT_UINT32, BASE_DEC,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_data_int32,
		  { "Value","diameter.avp.data.int32", FT_INT32, BASE_DEC,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_data_bytes,
		  { "Value","diameter.avp.data.bytes", FT_BYTES, BASE_NONE,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_data_string,
		  { "Value","diameter.avp.data.string", FT_STRING, BASE_NONE,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_data_addrfamily,
		  { "Address Family","diameter.avp.data.addrfamily", FT_UINT16, BASE_DEC,
		    VALS(diameter_avp_data_addrfamily_vals), 0x0, "", HFILL }},
		{ &hf_diameter_avp_data_v4addr,
		  { "IPv4 Address","diameter.avp.data.v4addr", FT_IPv4, BASE_NONE,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_data_v6addr,
		  { "IPv6 Address","diameter.avp.data.v6addr", FT_IPv6, BASE_NONE,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_data_time,
		  { "Time","diameter.avp.data.time", FT_ABSOLUTE_TIME, BASE_NONE,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_diameter_uri,
		  { "Diameter URI","diameter.avp.diameter_uri", FT_STRING, BASE_NONE,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_session_id,
		  { "Session ID","diameter.avp.session_id", FT_STRING, BASE_NONE,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_public_id,
		  { "Public ID","diameter.avp.public_id", FT_STRING, BASE_NONE,
		    NULL, 0x0, "", HFILL }},
		{ &hf_diameter_avp_private_id,
		  { "Private ID","diameter.avp.private_id", FT_STRING, BASE_NONE,
		    NULL, 0x0, "", HFILL }},

	};
	static gint *ett[] = {
		&ett_diameter,
		&ett_diameter_flags,
		&ett_diameter_avp,
		&ett_diameter_avp_flags,
		&ett_diameter_avpinfo
	};
	module_t *diameter_module;
	gchar *default_diameterDictionary;

	proto_diameter = proto_register_protocol ("Diameter Protocol", "DIAMETER", "diameter");
	proto_register_field_array(proto_diameter, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));

	/* Allow dissector to find be found by name. */
	new_register_dissector("diameter", dissect_diameter, proto_diameter);

	/* Register a configuration option for port */
	diameter_module = prefs_register_protocol(proto_diameter,
											  proto_reg_handoff_diameter);
	/* Register a configuration option for Diameter version */
	prefs_register_enum_preference(diameter_module, "version", "Diameter version", "Standard version used for decoding", (gint *)&gbl_version, options, FALSE);

	prefs_register_uint_preference(diameter_module, "tcp.port",
								   "Diameter TCP Port",
								   "Set the TCP port for Diameter messages",
								   10,
								   &gbl_diameterTcpPort);
	prefs_register_uint_preference(diameter_module, "sctp.port",
								   "Diameter SCTP Port",
								   "Set the SCTP port for Diameter messages",
								   10,
								   &gbl_diameterSctpPort);
	/*
	 * Build our default dictionary filename
	 */
	default_diameterDictionary = get_datafile_path(DICT_FN);

	/*
	 * Now register the dictionary filename as a preference,
	 * so it can be changed.
	 */
	gbl_diameterDictionary = default_diameterDictionary;
	prefs_register_string_preference(diameter_module, "dictionary.name",
									 "Diameter XML Dictionary",
									 "Set the dictionary used for Diameter messages",
									 &gbl_diameterDictionary);

	/*
	 * We don't need the default dictionary, so free it (a copy was made
	 * of it in "gbl_diameterDictionary" by
	 * "prefs_register_string_preference()").
	 */
	g_free(default_diameterDictionary);

	/*
	 * Make use of the dictionary optional.  Avoids error popups if xml library
	 * or dictionary file aren't available.
	 */
	prefs_register_bool_preference(diameter_module, "dictionary.use",
	                               "Attempt to load/use Diameter XML Dictionary",
	                               "Only attempt to load and use the Diameter XML "
	                               "Dictionary when this option is selected",
	                               &gbl_use_xml_dictionary);

	/* Desegmentation */
	prefs_register_bool_preference(diameter_module, "desegment",
                                   "Reassemble Diameter messages\nspanning multiple TCP segments",
								   "Whether the Diameter dissector should reassemble messages spanning multiple TCP segments."
								   " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
								   &gbl_diameter_desegment);
	/* Allow zero as valid application ID */
	prefs_register_bool_preference(diameter_module, "allow_zero_as_app_id",
			"Allow 0 as valid application ID",
			"If set, the value 0 (zero) can be used as a valid "
			"application ID. This is used in experimental cases.",
			&allow_zero_as_app_id);
	/* Register some preferences we no longer support, so we can report
	   them as obsolete rather than just illegal. */
	/* Suppress console output or not */
	prefs_register_bool_preference(diameter_module, "suppress_console_output",
			"Suppress console output for unknown AVP:s Flags etc.",
			"If console output for errors should be suppressed or not",
			&suppress_console_output);
	/* Register some preferences we no longer support, so we can report
	   them as obsolete rather than just illegal. */
	prefs_register_obsolete_preference(diameter_module, "udp.port");
	prefs_register_obsolete_preference(diameter_module, "command_in_header");
} /* proto_register_diameter */
