wireshark/packet-diameter.c

979 lines
30 KiB
C

/* packet-diameter.c
* Routines for DIAMETER packet disassembly
*
* $Id: packet-diameter.c,v 1.26 2001/08/04 19:50:33 guy Exp $
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <glib.h>
#include "packet.h"
#include "resolv.h"
#include "prefs.h"
/* This must be defined before we include packet-diameter-defs.h s*/
/* 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 */
} diameterDataType;
typedef struct avp_info {
guint32 code;
gchar *name;
diameterDataType type;
value_string *values;
} avpInfo;
#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 flags_set_truth = {
"Set",
"Not set"
};
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_reserved = -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_reserved3 = -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_reserved = -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 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 char gbl_diameterString[200];
static int gbl_diameterTcpPort=TCP_PORT_DIAMETER;
static int gbl_diameterSctpPort=SCTP_PORT_DIAMETER;
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;
#define AUTHENTICATOR_LENGTH 12
/* Diameter Header Flags */
/* RPrrrrrrCCCCCCCCCCCCCCCCCCCCCCCC */
#define DIAM_FLAGS_R 0x80
#define DIAM_FLAGS_P 0x40
#define DIAM_FLAGS_E 0x20
#define DIAM_FLAGS_RESERVED3 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 0x1f
#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) + MIN_AVP_SIZE)
static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
/* Diameter Manipulation Routines (mess with our strucutres) */
diameterDataType
diameter_avp_get_type(guint32 avpCode){
int i;
for (i=0; diameter_avps[i].name; i++) {
if (avpCode == diameter_avps[i].code) {
/* We found it! */
return diameter_avps[i].type;
}
}
/* If we don't find it, assume it's data */
g_warning("DIAMETER: Unable to find type for avpCode %d!", avpCode);
return DIAMETER_OCTET_STRING;
} /* diameter_avp_get_type */
static gchar *
diameter_avp_get_name(guint32 avpCode)
{
static gchar buffer[64];
int i;
for (i=0; diameter_avps[i].name; i++) {
if (avpCode == diameter_avps[i].code) {
/* We found it! */
return diameter_avps[i].name;
}
}
/* 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 avpValue)
{
static gchar buffer[64];
int i;
for (i=0; diameter_avps[i].name; i++) {
if (avpCode == diameter_avps[i].code) {
/* We found the code. Now find the value! */
if (!diameter_avps[i].values)
break;
return val_to_str(avpValue, diameter_avps[i].values , "Unknown Value: 0x%08x");
}
}
/* If we don't find the avp, build a value string */
sprintf(buffer, "Unknown AVP! Value: 0x%08x", avpValue);
return buffer;
} /* diameter_avp_get_value */
static gchar *
diameter_time_to_string(gchar *timeValue)
{
static gchar buffer[64];
int intval;
struct tm lt;
intval=pntohl(*((guint32*)timeValue));
intval -= NTP_TIME_DIFF;
lt=*localtime((time_t *)&intval);
strftime(buffer, 1024,
"%a, %d %b %Y %H:%M:%S %z",&lt);
return buffer;
} /* diameter_time_to_string */
/* Code to actually dissect the packets */
/*
* Main dissector
*/
static void dissect_diameter(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;
size_t 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], vendorString[64];
gint i;
guint bpos;
/* Make entries in Protocol column and Info column on summary display */
if (check_col(pinfo->fd, COL_PROTOCOL))
col_add_str(pinfo->fd, COL_PROTOCOL, "Diameter");
if (check_col(pinfo->fd, COL_INFO))
col_clear(pinfo->fd, COL_INFO);
/* Copy our header */
tvb_memcpy(tvb, (guint8*) &dh, offset, sizeof(dh));
/* Fix byte ordering in our static structure */
dh.versionLength = ntohl(dh.versionLength);
dh.flagsCmdCode = ntohl(dh.flagsCmdCode);
dh.vendorId = ntohl(dh.vendorId);
dh.hopByHopId = ntohl(dh.hopByHopId);
dh.endToEndId = ntohl(dh.endToEndId);
if (dh.vendorId) {
strcpy(vendorString,
val_to_str(dh.vendorId, diameter_vendor_specific_vendors, "Unknown Vendor: %08x"));
} else {
strcpy(vendorString, "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->fd, 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, val_to_str(commandCode, diameter_command_code_vals, "Unknown Command: 0x%08x"));
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) {
g_warning("DIAMETER: Packet too short: %d bytes less than min size (%d bytes))",
pktLength, MIN_DIAMETER_SIZE);
BadPacket = TRUE;
}
/* And, check our reserved flags/version */
if ((flags & DIAM_FLAGS_RESERVED) ||
(version != 1)) {
g_warning("DIAMETER: Bad packet: Bad Flags(0x%x) or Version(%u)",
flags, version);
BadPacket = TRUE;
}
if (check_col(pinfo->fd, COL_INFO)) {
col_add_fstr(pinfo->fd, COL_INFO,
"%s%s%s%s: %s vendor=%s (hop-id=%d) (end-id=%d) RPE=%d%d%d",
(BadPacket)?"***** Bad Packet!: ":"",
(flags & DIAM_FLAGS_P)?"Proxyable ":"",
(flags & DIAM_FLAGS_R)?"Request":"Answer",
(flags & DIAM_FLAGS_E)?" Error":"",
commandString, vendorString,
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, tvb_length(tvb), 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_reserved3, 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", vendorString);
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, -1, avplength);
avptf = proto_tree_add_text(diameter_tree,
tvb, offset, tvb_length(tvb),
"Attribute Value Pairs");
avp_tree = proto_item_add_subtree(avptf,
ett_diameter_avp);
if (avp_tree != NULL) {
dissect_avps( avp_tvb, pinfo, avp_tree);
}
}
} /* dissect_diameter */
/*
* 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 vendorString[64];
int hdrLength;
int fixAmt;
proto_tree *avpi_tree;
size_t offset = 0 ;
char dataBuffer[4096];
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) {
g_warning("DIAMETER: AVP Payload too short: %d bytes less than min size (%d bytes))",
packetLength, 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 = ntohl(avph.avp_code);
avph.avp_flagsLength = ntohl(avph.avp_flagsLength);
flags = (avph.avp_flagsLength & 0xff000000) >> 24;
avpLength = avph.avp_flagsLength & 0x00ffffff;
/* Set up our flags string */
if (check_col(pinfo->fd, 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 = ntohl(avph.avp_vendorId);
/* Vendor id */
hdrLength = sizeof(e_avphdr);
} else {
/* No vendor */
hdrLength = sizeof(e_avphdr) -
sizeof(guint32);
vendorId = 0;
}
if (vendorId) {
strcpy(vendorString,
val_to_str(vendorId, diameter_vendor_specific_vendors, "Unknown Vendor: %08x"));
} else {
vendorString[0]='\0';
}
/* Check for bad length */
if (avpLength < MIN_AVP_SIZE ||
((long)avpLength > packetLength)) {
g_warning("DIAMETER: AVP payload size invalid: avp_length: %d bytes, "
"min: %d bytes, packetLen: %d",
avpLength, MIN_AVP_SIZE, packetLength);
BadPacket = TRUE;
}
/* Check for bad flags */
if (flags & AVP_FLAGS_RESERVED) {
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) {
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), diameter_avp_type_vals,
"Unknown-Type: 0x%08x"));
strcpy(avpNameString, diameter_avp_get_name(avph.avp_code));
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, vendorString);
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, dataBuffer,
"Bad AVP (Suspect Data Not Dissected)");
return;
}
avpType=diameter_avp_get_type(avph.avp_code);
tvb_memcpy(tvb, (guint8*) dataBuffer, offset, MIN(4095,avpDataLength));
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:
proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
tvb, offset, avpDataLength, dataBuffer,
"Identity: %*.*s", (int)avpDataLength, (int)avpDataLength,
dataBuffer);
break;
case DIAMETER_UTF8STRING:
proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
tvb, offset, avpDataLength, dataBuffer,
"UTF8String: %*.*s", (int)avpDataLength, (int)avpDataLength,
dataBuffer);
break;
case DIAMETER_IP_ADDRESS:
if (avpDataLength == 4) {
guint32 ipv4Address = ntohl((*(guint32*)dataBuffer));
proto_tree_add_ipv4_format(avpi_tree, hf_diameter_avp_data_v4addr,
tvb, offset, avpDataLength, ipv4Address,
"IPv4 Address: %u.%u.%u.%u",
(ipv4Address&0xff000000)>>24,
(ipv4Address&0xff0000)>>16,
(ipv4Address&0xff00)>>8,
(ipv4Address&0xff));
} else if (avpDataLength == 16) {
proto_tree_add_ipv6_format(avpi_tree, hf_diameter_avp_data_v6addr,
tvb, offset, avpDataLength, dataBuffer,
"IPv6 Address: %04x:%04x:%04x:%04x",
*((guint32*)dataBuffer),
*((guint32*)&dataBuffer[4]),
*((guint32*)&dataBuffer[8]),
*((guint32*)&dataBuffer[12]));
} else {
proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
tvb, offset, avpDataLength, dataBuffer,
"Error! Bad Address Length");
}
break;
case DIAMETER_INTEGER32:
{
gint32 data;
memcpy(&data, dataBuffer, 4);
data = ntohl(data);
proto_tree_add_int_format(avpi_tree, hf_diameter_avp_data_int32,
tvb, offset, avpDataLength, data,
"Value: %d", data );
}
break;
case DIAMETER_UNSIGNED32:
{
guint32 data;
memcpy(&data, dataBuffer, 4);
data=ntohl(data);
proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
tvb, offset, avpDataLength, data,
"Value: 0x%08x (%u)", data,
data );
}
break;
case DIAMETER_INTEGER64:
{
gint64 data;
memcpy(&data, dataBuffer, 8);
/* data = ntohll(data); */
proto_tree_add_int_format(avpi_tree, hf_diameter_avp_data_int64,
tvb, offset, avpDataLength, data,
"Value: 0x%016llx (%lld)", data, data );
}
break;
case DIAMETER_UNSIGNED64:
{
guint64 data;
memcpy(&data, dataBuffer, 8);
/* data = ntohll(data); */
proto_tree_add_int_format(avpi_tree, hf_diameter_avp_data_uint64,
tvb, offset, avpDataLength, data,
"Value: 0x%016llx (%llu)", data, data );
}
break;
case DIAMETER_TIME:
valstr=diameter_time_to_string(dataBuffer);
proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
tvb, offset, avpDataLength, dataBuffer, "Time: %s", valstr);
break;
case DIAMETER_ENUMERATED:
{
guint32 data;
memcpy(&data, dataBuffer, 4);
data = ntohl(data);
valstr = diameter_avp_get_value(avph.avp_code, 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);
}
break;
default:
case DIAMETER_OCTET_STRING:
proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
tvb, offset, avpDataLength, dataBuffer,
"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;
if (Initialized) {
dissector_delete("tcp.port", TcpPort, dissect_diameter);
dissector_delete("sctp.port", SctpPort, dissect_diameter);
} else {
Initialized=TRUE;
}
/* set port for future deletes */
TcpPort=gbl_diameterTcpPort;
SctpPort=gbl_diameterSctpPort;
strcpy(gbl_diameterString, "Diameter Protocol");
/* g_warning ("Diameter: Adding tcp dissector to port %d",
gbl_diameterTcpPort); */
dissector_add("tcp.port", gbl_diameterTcpPort, dissect_diameter,
proto_diameter);
dissector_add("sctp.port", gbl_diameterSctpPort,
dissect_diameter, proto_diameter);
}
/* 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_reserved3,
{ "Reserved","diameter.flags.reserved3", FT_BOOLEAN, 8, TFS(&reserved_set),
DIAM_FLAGS_RESERVED3, "", 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,
{ "AVP Data","diameter.avp.data.uint64", FT_UINT32, BASE_DEC,
NULL, 0x0, "", HFILL }},
{ &hf_diameter_avp_data_int64,
{ "AVP Data","diameter.avp.data.int64", FT_INT32, BASE_DEC,
NULL, 0x0, "", HFILL }},
{ &hf_diameter_avp_data_uint32,
{ "AVP Data","diameter.avp.data.uint32", FT_UINT32, BASE_DEC,
NULL, 0x0, "", HFILL }},
{ &hf_diameter_avp_data_int32,
{ "AVP Data","diameter.avp.data.int32", FT_INT32, BASE_DEC,
NULL, 0x0, "", HFILL }},
{ &hf_diameter_avp_data_bytes,
{ "AVP Data","diameter.avp.data.bytes", FT_BYTES, BASE_NONE,
NULL, 0x0, "", HFILL }},
{ &hf_diameter_avp_data_string,
{ "AVP Data","diameter.avp.data.string", FT_STRING, BASE_NONE,
NULL, 0x0, "", HFILL }},
{ &hf_diameter_avp_data_v4addr,
{ "AVP Data","diameter.avp.data.v4addr", FT_IPv4, BASE_NONE,
NULL, 0x0, "", HFILL }},
{ &hf_diameter_avp_data_v6addr,
{ "AVP Data","diameter.avp.data.v6addr", FT_IPv6, BASE_NONE,
NULL, 0x0, "", HFILL }},
{ &hf_diameter_avp_data_time,
{ "AVP Data","diameter.avp.data.time", FT_ABSOLUTE_TIME, 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;
proto_diameter = proto_register_protocol (gbl_diameterString,
"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);
}