diff options
Diffstat (limited to 'epan/dissectors/packet-diameter.c')
-rw-r--r-- | epan/dissectors/packet-diameter.c | 1965 |
1 files changed, 1965 insertions, 0 deletions
diff --git a/epan/dissectors/packet-diameter.c b/epan/dissectors/packet-diameter.c new file mode 100644 index 0000000000..acb9466014 --- /dev/null +++ b/epan/dissectors/packet-diameter.c @@ -0,0 +1,1965 @@ +/* packet-diameter.c + * Routines for Diameter packet disassembly + * + * $Id$ + * + * Copyright (c) 2001 by David Frascone <dave@frascone.com> + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * 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 + */ + +#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 "xmlstub.h" +#include <epan/packet.h> +#include <epan/resolv.h> +#include <epan/report_err.h> +#include "prefs.h" +#include "packet-tcp.h" + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +/* 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_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, + DIAMETER_URI, /* OctetString */ + DIAMETER_SESSION_ID /* OctetString */ + +} diameterDataType; + + +static value_string TypeValues[]={ + { DIAMETER_OCTET_STRING, "OctetString" }, + { DIAMETER_INTEGER32, "Integer32" }, + { DIAMETER_INTEGER64, "Integer64" }, + { DIAMETER_UNSIGNED32, "Unsigned32" }, + { 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"}, + + {0, (char *)NULL} +}; + +typedef struct value_name { + guint32 value; + gchar *name; + struct value_name *next; +} ValueName; + +typedef struct old_avp_info { + guint32 code; + gchar *name; + diameterDataType type; + 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 1812 +#define SCTP_PORT_DIAMETER 1812 + +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_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_v4addr = -1; +static int hf_diameter_avp_data_v6addr = -1; +static int hf_diameter_avp_data_time = -1; +static int hf_diameter_avp_session_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 = FALSE; + +/* Supress console output at unknown AVP:s,Flags etc */ +static gboolean suppress_console_output = TRUE; + +#define DICT_FN "diameter/dictionary.xml" +static gchar *gbl_diameterDictionary; + +typedef struct _e_diameterhdr { + guint32 versionLength; + guint32 flagsCmdCode; + guint32 vendorId; + guint32 hopByHopId; + guint32 endToEndId; +} e_diameterhdr; + +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)) + +static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); + + +/* + * This routine will do a push-parse of the passed in + * filename. This was taken almost verbatum from + * the xmlsoft examples. + */ +static xmlDocPtr +xmlParseFilePush( char *filename, int checkValid) { + FILE *f; + xmlDocPtr doc=NULL; + int valid=0; + int res, size = 1024; + char chars[1024]; + xmlParserCtxtPtr ctxt; + + /* I wonder what kind of a performance hit this is? */ + *XmlStub.xmlDoValidityCheckingDefaultValue = checkValid; + + 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; + valid=ctxt->valid; + XmlStub.xmlFreeParserCtxt(ctxt); + } + fclose(f); + + /* Check valid */ + if (!valid) { + report_failure( "Error! Invalid xml in %s! Failed DTD check!", + filename); + return NULL; + } + 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, gchar *name, diameterDataType type, 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; + if (vEntry) + 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(cur->name, "type") == 0) { + type = XmlStub.xmlGetProp(cur, "type-name"); + } else if (strcasecmp(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(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; + if (vEntry) + entry->type = DIAMETER_INTEGER32; + + /* 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, 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"); + 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, int id) +{ + ApplicationId *entry; + + if (!name || (id < 0) || (id == 0 && !allow_zero_as_app_id)) { + report_failure( "Diameter Error: Invalid application (name=%p, 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, gchar *name, 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, atol(id)) != 0) { + /* ERROR! */ + return -1; + } + } + + + /* + * Get segment values + */ + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if (strcasecmp(cur->name, "avp") == 0) { + /* we have an avp!!! */ + xmlParseAVP(cur); + } else if (strcasecmp(cur->name, "vendor") == 0) { + /* we have a vendor */ + xmlParseVendor(cur); + /* For now, ignore typedefn and text */ + } else if (strcasecmp(cur->name, "command") == 0) { + /* Found a command */ + xmlParseCommand(cur); + } else if (strcasecmp(cur->name, "text") == 0) { + } else if (strcasecmp(cur->name, "comment") == 0) { + } else if (strcasecmp(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(cur->name, "base") == 0) { + /* Base protocol. Descend and parse */ + xmlDictionaryParseSegment(cur, 1); + } else if (strcasecmp(cur->name, "application") == 0) { + /* Application. Descend and parse */ + xmlDictionaryParseSegment(cur, 0); + } else if (strcasecmp(cur->name, "text") == 0) { + /* Ignore text */ + } else if (strcasecmp(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 a 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 */ + if (doc == NULL) { + report_failure("Diameter: Unable to parse xmldictionary %s", + gbl_diameterDictionary); + 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; diameter_vendor_specific_vendors[i].strptr; i++) { + addVendor(diameter_vendor_specific_vendors[i].value, + diameter_vendor_specific_vendors[i].strptr, + diameter_vendor_specific_vendors[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); + } + +} /* initializeDictionaryDefaults */ + +/* + * This routine will attempt to load the XML dictionary, and on + * failure, will call initializeDictionaryDefaults to load in + * our static dictionary. + */ +static void +initializeDictionary(void) +{ + /* + * 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 (loadLibXML() || + (loadXMLDictionary() != 0)) { + /* Something failed. Use the static dictionary */ + report_failure("Diameter: Using static dictionary! (Unable to use XML)"); + 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; + static gchar buffer[64]; + + for (probe=vendorListHead; probe; probe=probe->next) { + if (vendorId == probe->id) { + if (longName) + return probe->longName; + else + return probe->name; + } + } + + snprintf(buffer, sizeof(buffer), + "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; + static gchar buffer[64]; + gchar *vendorName=NULL; + + 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); + snprintf(buffer, sizeof(buffer), + "Cmd-0x%08x", commandCode); + + return buffer; +}/*diameter_command_to_str */ + +/* return application string, based on the id */ +static gchar * +diameter_app_to_str(guint32 vendorId) { + ApplicationId *probe; + static gchar buffer[64]; + + for (probe=ApplicationIdHead; probe; probe=probe->next) { + if (vendorId == probe->id) { + return probe->name; + } + } + + snprintf(buffer, sizeof(buffer), + "AppId 0x%08x", vendorId); + 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) +{ + static gchar buffer[64]; + 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("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 */ + sprintf(buffer, "Unknown AVP:0x%08x", avpCode); + return buffer; +} /* diameter_avp_get_name */ +static 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 dh; + int offset=0; + size_t avplength; + proto_tree *avp_tree; + proto_item *avptf; + int BadPacket = FALSE; + guint32 commandCode, pktLength; + guint8 version, flags; + gchar flagstr[64] = "<None>"; + gchar *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Error", "Proxyable", "Request" }; + gchar commandString[64], vendorName[64]; + gint i; + guint bpos; + static int initialized=FALSE; + + /* + * Only parse in dictionary if there are diameter packets to + * dissect. + */ + if (!initialized) { + /* Read in our dictionary, if it exists. */ + initializeDictionary(); + initialized=TRUE; + } + + /* 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 */ + 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) { + strcpy(vendorName, + diameter_vendor_to_str(dh.vendorId, TRUE)); + } else { + strcpy(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); + + /* Set up our flags */ + if (check_col(pinfo->cinfo, COL_INFO) || tree) { + flagstr[0]=0; + for (i = 0; i < 8; i++) { + bpos = 1 << i; + if (flags & bpos) { + if (flagstr[0]) { + strcat(flagstr, ", "); + } + strcat(flagstr, fstr[i]); + } + } + if (strlen(flagstr) == 0) { + strcpy(flagstr,"<None>"); + } + } + + /* Set up our commandString */ + strcpy(commandString, diameter_command_to_str(commandCode, dh.vendorId)); + if (flags & DIAM_FLAGS_R) + strcat(commandString, "-Request"); + else + strcat(commandString, "-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)) { + col_add_fstr(pinfo->cinfo, COL_INFO, + "%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, vendorName, + dh.hopByHopId, dh.endToEndId, + (flags & DIAM_FLAGS_R)?1:0, + (flags & DIAM_FLAGS_P)?1:0, + (flags & DIAM_FLAGS_E)?1:0); + } + + + /* 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(diameter_tree, hf_diameter_flags, tvb, + offset , 1, flags, "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(diameter_tree, hf_diameter_code, + tvb, offset, 3, commandCode, "Command Code: %s", commandString); + offset += 3; + + /* Vendor Id */ + proto_tree_add_uint_format(diameter_tree,hf_diameter_vendor_id, + tvb, offset, 4, dh.vendorId, "Vendor-Id: %s", vendorName); + 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; + + /* 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 */ + avplength= pktLength - sizeof(e_diameterhdr); + + 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(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 int +dissect_diameter_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + if (!check_diameter(tvb)) + return 0; + tcp_dissect_pdus(tvb, pinfo, tree, gbl_diameter_desegment, 4, + get_diameter_pdu_len, dissect_diameter_common); + return tvb_length(tvb); +} /* 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; + gchar avpTypeString[64]; + gchar avpNameString[64]; + gchar *valstr; + guint32 vendorId=0; + gchar vendorName[64]; + 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[1024]; + int BadPacket = FALSE; + guint32 avpLength; + guint8 flags; + proto_item *tf; + proto_tree *flags_tree; + + gint32 packetLength; + size_t avpDataLength; + int avpType; + gchar flagstr[64] = "<None>"; + 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 ) { + + /* 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) { + flagstr[0]=0; + for (i = 0; i < 8; i++) { + bpos = 1 << i; + if (flags & bpos) { + if (flagstr[0]) { + strcat(flagstr, ", "); + } + strcat(flagstr, fstr[i]); + } + } + if (strlen(flagstr) == 0) { + strcpy(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) { + strcpy(vendorName, + diameter_vendor_to_str(vendorId, TRUE)); + } else { + vendorName[0]='\0'; + } + + /* 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 */ + strcpy(avpTypeString, val_to_str(diameter_avp_get_type(avph.avp_code,vendorId), + TypeValues, + "Unknown-Type: 0x%08x")); + strcpy(avpNameString, diameter_avp_get_name(avph.avp_code, vendorId)); + + 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_tree_add_uint_format(avpi_tree, hf_diameter_avp_code, + tvb, offset, 4, avph.avp_code, "AVP Code: %s", avpNameString); + offset += 4; + + tf = proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_flags, tvb, + offset , 1, flags, "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(avpi_tree, hf_diameter_avp_vendor_id, + tvb, offset, 4, vendorId, vendorName); + 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: + sprintf(buffer, "%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: + 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 { + 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"); + } + break; + + case DIAMETER_INTEGER32: + if (avpDataLength == 4) { + proto_tree_add_item(avpi_tree, hf_diameter_avp_data_int32, + tvb, offset, avpDataLength, FALSE); + } else { + 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"); + } + 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 { + 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"); + } + break; + + case DIAMETER_INTEGER64: + if (avpDataLength == 8) { + proto_tree_add_item(avpi_tree, hf_diameter_avp_data_int64, + tvb, offset, 8, FALSE); + } else { + 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"); + } + break; + + case DIAMETER_UNSIGNED64: + if (avpDataLength == 8) { + proto_tree_add_item(avpi_tree, hf_diameter_avp_data_uint64, + tvb, offset, 8, FALSE); + } else { + 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"); + } + break; + + case DIAMETER_TIME: + if (avpDataLength == 4) { + nstime_t data; + gchar buffer[64]; + struct tm *ltp; + + data.secs = tvb_get_ntohl(tvb, offset); + data.secs -= NTP_TIME_DIFF; + data.nsecs = 0; + + ltp = localtime(&data.secs); + strftime(buffer, 64, + "%a, %d %b %Y %H:%M:%S %z", ltp); + + 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! Bad Time Length"); + } + 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 { + 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"); + } + break; + + case DIAMETER_VENDOR_ID: + if (avpDataLength == 4) { + guint32 data; + + data = tvb_get_ntohl(tvb, offset); + valstr = diameter_vendor_to_str(data, TRUE); + proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32, + tvb, offset, avpDataLength, data, + "Vendor ID: %s (0x%08x)", valstr, + data); + } else { + 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"); + } + 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 (0x%08x)", + valstr, data); + } else { + 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"); + } + break; + + case DIAMETER_MIP_REG_REQ: + safe_dissect_mip(tvb, pinfo, avpi_tree, offset, avpDataLength); + break; + + case DIAMETER_SESSION_ID: + proto_tree_add_item(avpi_tree, hf_diameter_avp_session_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 = new_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, NULL, + 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_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_session_id, + { "Session ID","diameter.avp.session_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)); + + /* Register a configuration option for port */ + diameter_module = prefs_register_protocol(proto_diameter, + proto_reg_handoff_diameter); + 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); + + /* Desegmentation */ + prefs_register_bool_preference(diameter_module, "desegment", + "Desegment all Diameter messages\nspanning multiple TCP segments", + "Whether the Diameter dissector should desegment all messages spanning multiple TCP segments", + &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. */ + /* Supress 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 */ |