forked from osmocom/wireshark
![Ulf Lamping](/assets/img/avatar_default.png)
I (hopefully) didn't changed any protocol fields or preference file names, but only the GUI labels appearing in the protocol display and the protocol preferences. Also added a note to the protocol preferences (where appropriate), that you have to enable "Allow subdissectors to reassemble TCP streams" at the corresponding protocol settings for TCP reassembling to take effect. If you encounter any mistakes I've made here, please let me know... svn path=/trunk/; revision=11784
1967 lines
57 KiB
C
1967 lines
57 KiB
C
/* 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/addr_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",
|
|
"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. */
|
|
/* 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 */
|