2 * Routines for Diameter packet disassembly
4 * $Id: packet-diameter.c,v 1.48 2002/05/10 23:20:38 guy Exp $
6 * Copyright (c) 2001 by David Frascone <dave@frascone.com>
8 * Ethereal - Network traffic analyzer
9 * By Gerald Combs <gerald@ethereal.com>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
45 #include <epan/filesystem.h>
47 #include <epan/packet.h>
48 #include <epan/resolv.h>
51 #ifdef NEED_SNPRINTF_H
52 # include "snprintf.h"
55 /* This must be defined before we include packet-diameter-defs.h */
57 /* Valid data types */
60 DIAMETER_OCTET_STRING = 1,
71 DIAMETER_IP_ADDRESS, /* OctetString */
72 DIAMETER_TIME, /* Integer 32 */
73 DIAMETER_UTF8STRING, /* OctetString */
74 DIAMETER_IDENTITY, /* OctetString */
75 DIAMETER_ENUMERATED, /* Integer 32 */
76 DIAMETER_IP_FILTER_RULE, /* OctetString */
77 DIAMETER_QOS_FILTER_RULE, /* OctetString */
78 DIAMETER_MIP_REG_REQ, /* OctetString */
79 DIAMETER_VENDOR_ID, /* Integer32 */
80 DIAMETER_APPLICATION_ID
85 static value_string TypeValues[]={
86 { DIAMETER_OCTET_STRING, "OctetString" },
87 { DIAMETER_INTEGER32, "Integer32" },
88 { DIAMETER_INTEGER64, "Integer64" },
89 { DIAMETER_UNSIGNED32, "Unsigned32" },
90 { DIAMETER_UNSIGNED64, "Unsigned64" },
91 { DIAMETER_FLOAT32, "Float32" },
92 { DIAMETER_FLOAT64, "Float64" },
93 { DIAMETER_FLOAT128, "Float128" },
94 { DIAMETER_GROUPED, "Grouped" },
95 { DIAMETER_IP_ADDRESS, "IpAddress" },
96 { DIAMETER_TIME, "Time" },
97 { DIAMETER_UTF8STRING, "UTF8String" },
98 { DIAMETER_IDENTITY, "DiameterIdentity" },
99 { DIAMETER_ENUMERATED, "Enumerated" },
100 { DIAMETER_IP_FILTER_RULE, "IPFilterRule" },
101 { DIAMETER_QOS_FILTER_RULE, "QOSFilterRule" },
102 { DIAMETER_MIP_REG_REQ, "MIPRegistrationRequest"},
103 { DIAMETER_VENDOR_ID, "VendorId"},
104 { DIAMETER_APPLICATION_ID, "AppId"},
108 typedef struct value_name {
111 struct value_name *next;
114 typedef struct old_avp_info {
117 diameterDataType type;
118 value_string *values;
121 typedef struct avp_info {
125 diameterDataType type;
127 struct avp_info *next;
130 typedef struct command_code {
134 struct command_code *next;
137 typedef struct vendor_id {
141 struct vendor_id *next;
144 typedef struct application_id {
147 struct application_id *next;
150 static avpInfo *avpListHead=NULL;
151 static VendorId *vendorListHead=NULL;
152 static CommandCode *commandListHead=NULL;
153 static ApplicationId *ApplicationIdHead=NULL;
156 #include "packet-diameter-defs.h"
158 #define NTP_TIME_DIFF (2208988800UL)
160 #define TCP_PORT_DIAMETER 1812
161 #define SCTP_PORT_DIAMETER 1812
163 static const true_false_string flags_set_truth = {
168 static const true_false_string reserved_set = {
169 "*** Error! Reserved Bit is Set",
172 static int proto_diameter = -1;
173 static int hf_diameter_length = -1;
174 static int hf_diameter_code = -1;
175 static int hf_diameter_hopbyhopid =-1;
176 static int hf_diameter_endtoendid =-1;
177 static int hf_diameter_version = -1;
178 static int hf_diameter_vendor_id = -1;
179 static int hf_diameter_flags = -1;
180 static int hf_diameter_flags_request = -1;
181 static int hf_diameter_flags_proxyable = -1;
182 static int hf_diameter_flags_error = -1;
183 static int hf_diameter_flags_reserved3 = -1;
184 static int hf_diameter_flags_reserved4 = -1;
185 static int hf_diameter_flags_reserved5 = -1;
186 static int hf_diameter_flags_reserved6 = -1;
187 static int hf_diameter_flags_reserved7 = -1;
189 static int hf_diameter_avp_code = -1;
190 static int hf_diameter_avp_length = -1;
191 static int hf_diameter_avp_flags = -1;
192 static int hf_diameter_avp_flags_vendor_specific = -1;
193 static int hf_diameter_avp_flags_mandatory = -1;
194 static int hf_diameter_avp_flags_protected = -1;
195 static int hf_diameter_avp_flags_reserved3 = -1;
196 static int hf_diameter_avp_flags_reserved4 = -1;
197 static int hf_diameter_avp_flags_reserved5 = -1;
198 static int hf_diameter_avp_flags_reserved6 = -1;
199 static int hf_diameter_avp_flags_reserved7 = -1;
200 static int hf_diameter_avp_vendor_id = -1;
203 static int hf_diameter_avp_data_uint32 = -1;
204 static int hf_diameter_avp_data_int32 = -1;
205 static int hf_diameter_avp_data_uint64 = -1;
206 static int hf_diameter_avp_data_int64 = -1;
207 static int hf_diameter_avp_data_bytes = -1;
208 static int hf_diameter_avp_data_string = -1;
209 static int hf_diameter_avp_data_v4addr = -1;
210 static int hf_diameter_avp_data_v6addr = -1;
211 static int hf_diameter_avp_data_time = -1;
213 static gint ett_diameter = -1;
214 static gint ett_diameter_flags = -1;
215 static gint ett_diameter_avp = -1;
216 static gint ett_diameter_avp_flags = -1;
217 static gint ett_diameter_avpinfo = -1;
219 static char gbl_diameterString[200];
220 static int gbl_diameterTcpPort=TCP_PORT_DIAMETER;
221 static int gbl_diameterSctpPort=SCTP_PORT_DIAMETER;
223 /* desegmentation of Diameter over TCP */
224 static gboolean gbl_diameter_desegment = TRUE;
226 #define DICT_FN "diameter/dictionary.xml"
227 static gchar *gbl_diameterDictionary = NULL;
229 typedef struct _e_diameterhdr {
230 guint32 versionLength;
231 guint32 flagsCmdCode;
237 typedef struct _e_avphdr {
239 guint32 avp_flagsLength;
240 guint32 avp_vendorId; /* optional */
243 /* Diameter Header Flags */
244 /* RPrrrrrrCCCCCCCCCCCCCCCCCCCCCCCC */
245 #define DIAM_FLAGS_R 0x80
246 #define DIAM_FLAGS_P 0x40
247 #define DIAM_FLAGS_E 0x20
248 #define DIAM_FLAGS_RESERVED3 0x10
249 #define DIAM_FLAGS_RESERVED4 0x08
250 #define DIAM_FLAGS_RESERVED5 0x04
251 #define DIAM_FLAGS_RESERVED6 0x02
252 #define DIAM_FLAGS_RESERVED7 0x01
253 #define DIAM_FLAGS_RESERVED 0x1f
255 #define DIAM_LENGTH_MASK 0x00ffffffl
256 #define DIAM_COMMAND_MASK DIAM_LENGTH_MASK
257 #define DIAM_GET_FLAGS(dh) ((dh.flagsCmdCode & ~DIAM_COMMAND_MASK) >> 24)
258 #define DIAM_GET_VERSION(dh) ((dh.versionLength & (~DIAM_LENGTH_MASK)) >> 24)
259 #define DIAM_GET_COMMAND(dh) (dh.flagsCmdCode & DIAM_COMMAND_MASK)
260 #define DIAM_GET_LENGTH(dh) (dh.versionLength & DIAM_LENGTH_MASK)
262 /* Diameter AVP Flags */
263 #define AVP_FLAGS_P 0x20
264 #define AVP_FLAGS_V 0x80
265 #define AVP_FLAGS_M 0x40
266 #define AVP_FLAGS_RESERVED3 0x10
267 #define AVP_FLAGS_RESERVED4 0x08
268 #define AVP_FLAGS_RESERVED5 0x04
269 #define AVP_FLAGS_RESERVED6 0x02
270 #define AVP_FLAGS_RESERVED7 0x01
271 #define AVP_FLAGS_RESERVED 0x1f /* 00011111 -- V M P X X X X X */
273 #define MIN_AVP_SIZE (sizeof(e_avphdr) - sizeof(guint32))
274 #define MIN_DIAMETER_SIZE (sizeof(e_diameterhdr))
276 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
280 * This routine will do a push-parse of the passed in
281 * filename. This was taken almost verbatum from
282 * the xmlsoft examples.
285 xmlParseFilePush( char *filename, int checkValid) {
289 int res, size = 1024;
291 xmlParserCtxtPtr ctxt;
293 /* I wonder what kind of a performance hit this is? */
294 *XmlStub.xmlDoValidityCheckingDefaultValue = checkValid;
296 f = fopen(filename, "r");
298 g_warning("Diameter: Unable to open %s", filename);
302 res = fread(chars, 1, 4, f);
304 ctxt = XmlStub.xmlCreatePushParserCtxt(NULL, NULL,
305 chars, res, filename);
306 while ((res = fread(chars, 1, size-1, f)) > 0) {
307 XmlStub.xmlParseChunk(ctxt, chars, res, 0);
309 XmlStub.xmlParseChunk(ctxt, chars, 0, 1);
312 XmlStub.xmlFreeParserCtxt(ctxt);
318 g_warning( "Error! Invalid xml in %s! Failed DTD check!",
323 } /* xmlParseFilePush */
326 * This routine will add a static avp to the avp list. It is
327 * only called when the XML dictionary fails to load properly.
330 addStaticAVP(int code, gchar *name, diameterDataType type, value_string *values)
333 ValueName *vEntry=NULL;
336 /* Parse our values array, if we have one */
338 for (i=0; values[i].strptr != NULL; i++) {
339 ValueName *ve = NULL;
341 ve = g_malloc(sizeof(ValueName));
342 ve->name = strdup(values[i].strptr);
343 ve->value = values[i].value;
349 /* And, create the entry */
350 entry = (avpInfo *)g_malloc(sizeof(avpInfo));
351 entry->name = g_strdup(name);
353 entry->vendorName = NULL;
355 entry->values = vEntry;
357 entry->type = DIAMETER_INTEGER32;
359 /* And, add it to the list */
360 entry->next = avpListHead;
368 * This routine will parse an XML avp entry, and add it to our
369 * avp list. If any values are present in the avp, it will
373 xmlParseAVP(xmlNodePtr cur)
375 char *name=NULL, *description=NULL, *code=NULL, *mayEncrypt=NULL,
376 *mandatory=NULL, *protected=NULL, *vendorBit=NULL, *vendorName = NULL,
381 ValueName *vEntry=NULL;
384 /* First, get our properties */
385 name = XmlStub.xmlGetProp(cur, "name");
386 description = XmlStub.xmlGetProp(cur, "description");
387 code = XmlStub.xmlGetProp(cur, "code");
388 mayEncrypt = XmlStub.xmlGetProp(cur, "may-encrypt");
389 mandatory = XmlStub.xmlGetProp(cur, "mandatory");
390 protected = XmlStub.xmlGetProp(cur, "protected");
391 vendorBit = XmlStub.xmlGetProp(cur, "vendor-bit");
392 vendorName = XmlStub.xmlGetProp(cur, "vendor-id");
393 constrained = XmlStub.xmlGetProp(cur, "constrained");
395 cur = cur->xmlChildrenNode;
397 while (cur != NULL ) {
398 if (!strcasecmp((char *)cur->name, "type")) {
399 type = XmlStub.xmlGetProp(cur, "type-name");
401 if (!strcasecmp((char *)cur->name, "enum")) {
402 char *valueName=NULL, *valueCode=NULL;
403 ValueName *ve = NULL;
404 valueName = XmlStub.xmlGetProp(cur, "name");
405 valueCode = XmlStub.xmlGetProp(cur, "code");
407 if (!valueName || !valueCode) {
408 g_warning( "Error, bad value on avp %s", name);
412 ve = g_malloc(sizeof(ValueName));
413 ve->name = strdup(valueName);
414 ve->value = atol(valueCode);
419 if (!strcasecmp((char *)cur->name, "grouped")) {
420 /* WORK Recurse here for grouped AVPs */
427 * Check for the AVP Type.
430 for (i = 0; TypeValues[i].strptr; i++) {
431 if (!strcasecmp(type, TypeValues[i].strptr)) {
432 avpType = TypeValues[i].value;
437 if (TypeValues[i].strptr == NULL) {
438 g_warning( "Invalid Type field in dictionary! avp %s (%s)", name, type);
441 } else if (!vEntry) {
442 g_warning("Missing type/enum field in dictionary avpName=%s",
447 /* WORK - Handle flags -- for validation later */
450 /* And, create the entry */
451 entry = (avpInfo *)g_malloc(sizeof(avpInfo));
452 entry->name = g_strdup(name);
453 entry->code = atol(code);
455 entry->vendorName = g_strdup(vendorName);
457 entry->vendorName = NULL;
458 entry->type = avpType;
459 entry->values = vEntry;
461 entry->type = DIAMETER_INTEGER32;
463 /* And, add it to the list */
464 entry->next = avpListHead;
471 * This routine will add a command to the list of commands.
474 addCommand(int code, char *name, char *vendorId)
479 * Allocate the memory required for the dictionary.
481 entry = (CommandCode *) g_malloc(sizeof (CommandCode));
484 g_warning("Unable to allocate memory");
489 * Allocate memory for the AVPName and copy the name to the
492 entry->name = g_strdup(name);
495 entry->vendorName = g_strdup(vendorId);
497 entry->vendorName = "None";
499 /* Add the entry to the list */
500 entry->next = commandListHead;
501 commandListHead = entry;
507 * This routine will parse the XML command, and add it to our
511 xmlParseCommand(xmlNodePtr cur)
513 char *name, *code, *vendorIdString;
518 name = XmlStub.xmlGetProp(cur, "name");
519 code = XmlStub.xmlGetProp(cur, "code");
520 if (!name || !code) {
521 g_warning("Invalid command. Name or code missing!");
524 vendorIdString = XmlStub.xmlGetProp(cur, "vendor-id");
526 if (!vendorIdString || !strcasecmp(vendorIdString, "None")) {
527 vendorIdString = NULL;
530 return (addCommand(atoi(code), name, vendorIdString));
531 } /* xmlParseCommand */
533 /* This routine adds an application to the name<-> id table */
535 dictionaryAddApplication(char *name, int id)
537 ApplicationId *entry;
539 if (!name || (id <= 0)) {
540 g_warning( "Diameter Error: Inavlid application (name=%p, id=%d)",
543 } /* Sanity Checks */
545 entry = g_malloc(sizeof(ApplicationId));
547 g_warning( "Unable to allocate memory");
551 entry->name = g_strdup(name);
554 /* Add it to the list */
555 entry->next = ApplicationIdHead;
556 ApplicationIdHead = entry;
559 } /* dictionaryAddApplication */
562 * This routine will add a vendor to the vendors list
565 addVendor(int id, gchar *name, gchar *longName)
570 vendor=g_malloc(sizeof(VendorId));
576 vendor->name = g_strdup(name);
577 vendor->longName = g_strdup(longName);
578 vendor->next = vendorListHead;
579 vendorListHead = vendor;
585 * This routine will pars in a XML vendor entry.
588 xmlParseVendor(xmlNodePtr cur)
590 char *name=NULL, *code=NULL, *id=NULL;
592 /* First, get our properties */
593 id = XmlStub.xmlGetProp(cur, "vendor-id");
594 name = XmlStub.xmlGetProp(cur, "name");
595 code = XmlStub.xmlGetProp(cur, "code");
597 if (!id || !name || !code) {
598 g_warning( "Invalid vendor section. vendor-id, name, and code must be specified");
602 return (addVendor(atoi(code), id, name));
606 * This routine will either parse in the base protocol, or an application.
609 xmlDictionaryParseSegment(xmlNodePtr cur, int base)
615 /* Add our application */
616 id = XmlStub.xmlGetProp(cur, "id");
617 name = XmlStub.xmlGetProp(cur, "name");
621 g_warning("Diameter: Invalid application!: name=\"%s\", id=\"%s\"",
622 name?name:"NULL", id?id:"NULL");
626 /* Add the application */
627 if (dictionaryAddApplication(name, atol(id)) != 0) {
637 cur = cur->xmlChildrenNode;
638 while (cur != NULL) {
639 if (!strcasecmp((char *)cur->name, "avp")) {
640 /* we have an avp!!! */
642 } else if (!strcasecmp((char *)cur->name, "vendor")) {
643 /* we have a vendor */
645 /* For now, ignore typedefn and text */
646 } else if (!strcasecmp((char *)cur->name, "command")) {
647 /* Found a command */
648 xmlParseCommand(cur);
649 } else if (!strcasecmp((char *)cur->name, "text")) {
650 } else if (!strcasecmp((char *)cur->name, "comment")) {
651 } else if (!strcasecmp((char *)cur->name, "typedefn")) {
652 /* WORK -- parse in valid types . . . */
654 /* IF we got here, we're an error */
655 g_warning("Error! expecting an avp or a typedefn (got \"%s\")",
662 } /* xmlDictionaryParseSegment */
665 * The main xml parse routine. This will walk through an XML
666 * dictionary that has been parsed by libxml.
669 xmlDictionaryParse(xmlNodePtr cur)
671 /* We should expect a base protocol, followed by multiple applicaitons */
672 while (cur != NULL) {
673 if (!strcasecmp((char *)cur->name, "base")) {
674 /* Base protocol. Descend and parse */
675 xmlDictionaryParseSegment(cur, 1);
676 } else if (!strcasecmp((char *)cur->name, "application")) {
677 /* Application. Descend and parse */
678 xmlDictionaryParseSegment(cur, 0);
679 } else if (!strcasecmp((char *)cur->name, "text")) {
682 g_warning( "Diameter: XML Expecting a base or an application (got \"%s\")",
691 } /* xmlDictionaryParse */
694 * This routine will call libxml to parse in the dictionary.
703 * build an XML tree from a the file;
705 XmlStub.xmlKeepBlanksDefault(0); /* Strip leading and trailing blanks */
706 XmlStub.xmlSubstituteEntitiesDefault(1); /* Substitute entities automagically */
707 doc = xmlParseFilePush(gbl_diameterDictionary, 1); /* Parse the XML (do validity checks)*/
709 /* Check for invalid xml */
711 g_warning("Diameter: Unable to parse xmldictionary %s",
712 gbl_diameterDictionary);
717 * Check the document is of the right kind
719 cur = XmlStub.xmlDocGetRootElement(doc);
721 g_warning("Diameter: Error: \"%s\": empty document",
722 gbl_diameterDictionary);
723 XmlStub.xmlFreeDoc(doc);
726 if (XmlStub.xmlStrcmp(cur->name, (const xmlChar *) "dictionary")) {
727 g_warning("Diameter: Error: \"%s\": document of the wrong type, root node != dictionary",
728 gbl_diameterDictionary);
729 XmlStub.xmlFreeDoc(doc);
734 * Ok, the dictionary has been parsed by libxml, and is valid.
735 * All we have to do now is read in our information.
737 if (xmlDictionaryParse(cur->xmlChildrenNode) != 0) {
738 /* Error has already been printed */
742 /* Once we're done parsing, free up the xml memory */
743 XmlStub.xmlFreeDoc(doc);
747 } /* loadXMLDictionary */
750 * Fallback routine. In the event of ANY error when loading the XML
751 * dictionary, this routine will populate the new avp list structures
752 * with the old static data from packet-diameter-defs.h
755 initializeDictionaryDefaults()
759 /* Add static vendors to list */
760 for(i=0; diameter_vendor_specific_vendors[i].strptr; i++) {
761 addVendor(diameter_vendor_specific_vendors[i].value,
762 diameter_vendor_specific_vendors[i].strptr,
763 diameter_vendor_specific_vendors[i].strptr);
765 /* Add static commands to list. */
766 for(i=0; diameter_command_code_vals[i].strptr; i++) {
767 addCommand(diameter_command_code_vals[i].value,
768 diameter_command_code_vals[i].strptr, NULL);
771 /* Add static AVPs to list */
772 for (i=0; old_diameter_avps[i].name; i++) {
773 addStaticAVP(old_diameter_avps[i].code,
774 old_diameter_avps[i].name,
775 old_diameter_avps[i].type,
776 old_diameter_avps[i].values);
779 } /* initializeDictionaryDefaults */
782 * This routine will attempt to load the XML dictionary, and on
783 * failure, will call initializeDictionaryDefaults to load in
784 * our static dictionary.
787 initializeDictionary()
790 * Using ugly ordering here. If loadLibXML succeeds, then
791 * loadXMLDictionary will be called. This is one of the few times when
792 * I think this is prettier than the nested if alternative.
795 (loadXMLDictionary() != 0)) {
796 /* Something failed. Use the static dictionary */
797 g_warning("Diameter: Using static dictionary! (Unable to use XML)");
798 initializeDictionaryDefaults();
800 } /* initializeDictionary */
805 * These routines manipulate the diameter structures.
808 /* return vendor string, based on the id */
810 diameter_vendor_to_str(guint32 vendorId, gboolean longName) {
812 static gchar buffer[64];
814 for (probe=vendorListHead; probe; probe=probe->next) {
815 if (vendorId == probe->id) {
817 return probe->longName;
823 snprintf(buffer, sizeof(buffer),
824 "Vendor 0x%08x", vendorId);
826 } /*diameter_vendor_to_str */
828 /* return command string, based on the code */
830 diameter_command_to_str(guint32 commandCode, guint32 vendorId)
833 static gchar buffer[64];
834 gchar *vendorName=NULL;
837 vendorName = diameter_vendor_to_str(vendorId, FALSE);
839 for (probe=commandListHead; probe; probe=probe->next) {
840 if (commandCode == probe->code) {
842 /* g_warning("Command: Comparing \"%s\" to \"%s\"", */
843 /* vendorName?vendorName:"(null)", */
844 /* probe->vendorName?probe->vendorName:"(null)"); */
845 /* Now check the vendor name */
846 if (!strcmp(vendorName, probe->vendorName))
850 /* With no vendor id, the Command's entry should be "None" */
851 if (!strcmp(probe->vendorName, "None")) {
859 g_warning("Diameter: Unable to find name for command code 0x%08x, Vendor \"%u\"!",
860 commandCode, vendorId);
861 snprintf(buffer, sizeof(buffer),
862 "Cmd-0x%08x", commandCode);
864 }/*diameter_command_to_str */
866 /* return application string, based on the id */
868 diameter_app_to_str(guint32 vendorId) {
869 ApplicationId *probe;
870 static gchar buffer[64];
872 for (probe=ApplicationIdHead; probe; probe=probe->next) {
873 if (vendorId == probe->id) {
878 snprintf(buffer, sizeof(buffer),
879 "AppId 0x%08x", vendorId);
881 } /*diameter_app_to_str */
883 /* return an avp type, based on the code */
884 static diameterDataType
885 diameter_avp_get_type(guint32 avpCode, guint32 vendorId){
887 gchar *vendorName=NULL;
890 vendorName = diameter_vendor_to_str(vendorId, FALSE);
892 for (probe=avpListHead; probe; probe=probe->next) {
893 if (avpCode == probe->code) {
896 /* g_warning("AvpType: Comparing \"%s\" to \"%s\"", */
897 /* vendorName?vendorName:"(null)", */
898 /* probe->vendorName?probe->vendorName:"(null)"); */
899 /* Now check the vendor name */
900 if (probe->vendorName && (!strcmp(vendorName, probe->vendorName)))
904 /* No Vendor ID -- vendorName should be null */
905 if (!probe->vendorName)
912 /* If we don't find it, assume it's data */
913 g_warning("Diameter: Unable to find type for avpCode %u, Vendor %u!", avpCode,
915 return DIAMETER_OCTET_STRING;
916 } /* diameter_avp_get_type */
918 /* return an avp name from the code */
920 diameter_avp_get_name(guint32 avpCode, guint32 vendorId)
922 static gchar buffer[64];
924 gchar *vendorName=NULL;
927 vendorName = diameter_vendor_to_str(vendorId, FALSE);
929 for (probe=avpListHead; probe; probe=probe->next) {
930 if (avpCode == probe->code) {
932 /* g_warning("AvpName: Comparing \"%s\" to \"%s\"", */
933 /* vendorName?vendorName:"(null)", */
934 /* probe->vendorName?probe->vendorName:"(null)"); */
935 /* Now check the vendor name */
936 if (probe->vendorName && (!strcmp(vendorName, probe->vendorName)))
940 /* No Vendor ID -- vendorName should be null */
941 if (!probe->vendorName)
948 g_warning("Diameter: Unable to find name for AVP 0x%08x, Vendor %u!",
951 /* If we don't find it, build a name string */
952 sprintf(buffer, "Unknown AVP:0x%08x", avpCode);
954 } /* diameter_avp_get_name */
956 diameter_avp_get_value(guint32 avpCode, guint32 vendorId, guint32 avpValue)
958 static gchar buffer[64];
961 gchar *vendorName=NULL;
964 vendorName = diameter_vendor_to_str(vendorId, FALSE);
966 for (probe=avpListHead; probe; probe=probe->next) {
967 if (avpCode == probe->code) {
969 /* g_warning("AvpValue: Comparing \"%s\" to \"%s\"", */
970 /* vendorName?vendorName:"(null)", */
971 /* probe->vendorName?probe->vendorName:"(null)"); */
972 /* Now check the vendor name */
973 if (probe->vendorName && (!strcmp(vendorName, probe->vendorName))) {
975 for(vprobe=probe->values; vprobe; vprobe=vprobe->next) {
976 if (avpValue == vprobe->value) {
980 sprintf(buffer, "Unknown Value: 0x%08x", avpValue);
984 if (!probe->vendorName) {
986 for(vprobe=probe->values; vprobe; vprobe=vprobe->next) {
987 if (avpValue == vprobe->value) {
991 sprintf(buffer, "Unknown Value: 0x%08x", avpValue);
997 /* If we don't find the avp, build a value string */
998 sprintf(buffer, "Unknown AVP! Value: 0x%08x", avpValue);
1000 } /* diameter_avp_get_value */
1003 /* Code to actually dissect the packets */
1008 static guint32 dissect_diameter_common(tvbuff_t *tvb, size_t start, packet_info *pinfo,
1012 /* Set up structures needed to add the protocol subtree and manage it */
1015 proto_tree *flags_tree;
1017 proto_tree *diameter_tree;
1021 proto_tree *avp_tree;
1023 int BadPacket = FALSE;
1024 guint32 commandCode, pktLength;
1025 guint8 version, flags;
1026 gchar flagstr[64] = "<None>";
1027 gchar *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Error", "Proxyable", "Request" };
1028 gchar commandString[64], vendorName[64];
1031 static int initialized=FALSE;
1033 /* set our offset */
1037 * Only parse in dictionary if there are diameter packets to
1041 /* Read in our dictionary, if it exists. */
1042 initializeDictionary();
1046 /* Make entries in Protocol column and Info column on summary display */
1047 if (check_col(pinfo->cinfo, COL_PROTOCOL))
1048 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Diameter");
1049 if (check_col(pinfo->cinfo, COL_INFO))
1050 col_clear(pinfo->cinfo, COL_INFO);
1052 /* Copy our header */
1053 tvb_memcpy(tvb, (guint8*) &dh, offset, sizeof(dh));
1055 /* Fix byte ordering in our static structure */
1056 dh.versionLength = ntohl(dh.versionLength);
1057 dh.flagsCmdCode = ntohl(dh.flagsCmdCode);
1058 dh.vendorId = ntohl(dh.vendorId);
1059 dh.hopByHopId = ntohl(dh.hopByHopId);
1060 dh.endToEndId = ntohl(dh.endToEndId);
1064 diameter_vendor_to_str(dh.vendorId, TRUE));
1066 strcpy(vendorName, "None");
1070 /* Do the bit twiddling */
1071 version = DIAM_GET_VERSION(dh);
1072 pktLength = DIAM_GET_LENGTH(dh);
1073 flags = DIAM_GET_FLAGS(dh);
1074 commandCode = DIAM_GET_COMMAND(dh);
1076 /* Set up our flags */
1077 if (check_col(pinfo->cinfo, COL_INFO) || tree) {
1079 for (i = 0; i < 8; i++) {
1083 strcat(flagstr, ", ");
1085 strcat(flagstr, fstr[i]);
1088 if (strlen(flagstr) == 0) {
1089 strcpy(flagstr,"<None>");
1093 /* Set up our commandString */
1094 strcpy(commandString, diameter_command_to_str(commandCode, dh.vendorId));
1095 if (flags & DIAM_FLAGS_R)
1096 strcat(commandString, "-Request");
1098 strcat(commandString, "-Answer");
1100 /* Short packet. Should have at LEAST one avp */
1101 if (pktLength < MIN_DIAMETER_SIZE) {
1102 g_warning("Diameter: Packet too short: %u bytes less than min size (%lu bytes))",
1103 pktLength, (unsigned long)MIN_DIAMETER_SIZE);
1107 /* And, check our reserved flags/version */
1108 if ((flags & DIAM_FLAGS_RESERVED) ||
1110 g_warning("Diameter: Bad packet: Bad Flags(0x%x) or Version(%u)",
1115 if (check_col(pinfo->cinfo, COL_INFO)) {
1116 col_add_fstr(pinfo->cinfo, COL_INFO,
1117 "%s%s%s%s%s vendor=%s (hop-id=%u) (end-id=%u) RPE=%d%d%d",
1118 (BadPacket)?"***** Bad Packet!: ":"",
1119 (flags & DIAM_FLAGS_P)?"Proxyable ":"",
1120 (flags & DIAM_FLAGS_E)?" Error":"",
1122 (flags & (DIAM_FLAGS_P|DIAM_FLAGS_E))) ?
1124 commandString, vendorName,
1125 dh.hopByHopId, dh.endToEndId,
1126 (flags & DIAM_FLAGS_R)?1:0,
1127 (flags & DIAM_FLAGS_P)?1:0,
1128 (flags & DIAM_FLAGS_E)?1:0);
1132 /* In the interest of speed, if "tree" is NULL, don't do any work not
1133 necessary to generate protocol tree items. */
1136 /* create display subtree for the protocol */
1137 ti = proto_tree_add_item(tree, proto_diameter, tvb, offset,
1138 MAX(pktLength,MIN_DIAMETER_SIZE), FALSE);
1139 diameter_tree = proto_item_add_subtree(ti, ett_diameter);
1142 proto_tree_add_uint(diameter_tree,
1143 hf_diameter_version,
1150 proto_tree_add_uint(diameter_tree,
1151 hf_diameter_length, tvb,
1152 offset, 3, pktLength);
1156 tf = proto_tree_add_uint_format(diameter_tree, hf_diameter_flags, tvb,
1157 offset , 1, flags, "Flags: 0x%02x (%s)", flags,
1159 flags_tree = proto_item_add_subtree(tf, ett_diameter_avp_flags);
1160 proto_tree_add_boolean(flags_tree, hf_diameter_flags_request, tvb, offset, 1, flags);
1161 proto_tree_add_boolean(flags_tree, hf_diameter_flags_proxyable, tvb, offset, 1, flags);
1162 proto_tree_add_boolean(flags_tree, hf_diameter_flags_error, tvb, offset, 1, flags);
1163 proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved3, tvb, offset, 1, flags);
1164 proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved4, tvb, offset, 1, flags);
1165 proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved5, tvb, offset, 1, flags);
1166 proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved6, tvb, offset, 1, flags);
1167 proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved7, tvb, offset, 1, flags);
1172 proto_tree_add_uint_format(diameter_tree, hf_diameter_code,
1173 tvb, offset, 3, commandCode, "Command Code: %s", commandString);
1177 proto_tree_add_uint_format(diameter_tree,hf_diameter_vendor_id,
1178 tvb, offset, 4, dh.vendorId, "Vendor-Id: %s", vendorName);
1181 /* Hop-by-hop Identifier */
1182 proto_tree_add_uint(diameter_tree, hf_diameter_hopbyhopid,
1183 tvb, offset, 4, dh.hopByHopId);
1186 /* End-to-end Identifier */
1187 proto_tree_add_uint(diameter_tree, hf_diameter_endtoendid,
1188 tvb, offset, 4, dh.endToEndId);
1191 /* If we have a bad packet, don't bother trying to parse the AVPs */
1193 return (offset + MAX(pktLength,MIN_DIAMETER_SIZE));
1196 /* Start looking at the AVPS */
1197 /* Make the next tvbuff */
1199 /* Update the lengths */
1200 avplength= pktLength - sizeof(e_diameterhdr);
1202 avp_tvb = tvb_new_subset(tvb, offset, avplength, avplength);
1203 avptf = proto_tree_add_text(diameter_tree,
1204 tvb, offset, avplength,
1205 "Attribute Value Pairs");
1207 avp_tree = proto_item_add_subtree(avptf,
1209 if (avp_tree != NULL) {
1210 dissect_avps( avp_tvb, pinfo, avp_tree);
1212 return MAX((offset + avplength), MIN_DIAMETER_SIZE);
1214 return (offset + MAX(pktLength, MIN_DIAMETER_SIZE));
1216 } /* dissect_diameter_common */
1219 dissect_diameter_sctp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1221 dissect_diameter_common(tvb, 0, pinfo, tree);
1225 dissect_diameter_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1229 guint32 available_bytes;
1230 /* guint32 noffset; */
1232 /* Loop through the packet, dissecting multiple diameter messages */
1234 available_bytes = tvb_length_remaining(tvb, offset);
1235 if (available_bytes < 4) {
1236 g_warning("Diameter: Bailing because only %d bytes of packet are available",
1238 return; /* Bail. We can't even get our length */
1241 /* get our packet length */
1242 plen = tvb_get_ntohl(tvb, offset);
1243 plen &= 0x00ffffff; /* get rid of the flags */
1246 if (gbl_diameter_desegment) {
1247 if (pinfo->can_desegment
1248 && plen > available_bytes) {
1249 pinfo->desegment_offset = offset;
1250 pinfo->desegment_len = plen - available_bytes;
1251 /* g_warning("Diameter: Bailing for deseg because plen(%d) > available(%d)", */
1252 /* plen, available_bytes); */
1257 /* Otherwise, dissect our packet */
1258 offset = dissect_diameter_common(tvb, offset, pinfo, tree);
1260 /* g_warning("dissected from %d to %d bytes out of %d (available was %d plen was %d)", */
1261 /* offset, noffset, tvb_length(tvb), available_bytes, plen); */
1262 /* offset=noffset; */
1263 } while (offset < tvb_reported_length(tvb));
1265 } /* dissect_diameter_tcp */
1268 * Call the mip_dissector, after saving our pinfo variables
1269 * so it doesn't write to our column display.
1272 safe_dissect_mip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1273 size_t offset, size_t length)
1275 static dissector_handle_t mip_handle;
1276 static int mipInitialized=FALSE;
1278 address save_dl_src;
1279 address save_dl_dst;
1280 address save_net_src;
1281 address save_net_dst;
1284 gboolean save_in_error_pkt;
1286 if (!mipInitialized) {
1287 mip_handle = find_dissector("mip");
1288 mipInitialized=TRUE;
1291 mip_tvb = tvb_new_subset(tvb, offset,
1292 MIN(length, tvb_length(tvb)-offset),
1295 /* The contained packet is a MIP registration request;
1296 dissect it with the MIP dissector. */
1297 col_set_writable(pinfo->cinfo, FALSE);
1299 /* Also, save the current values of the addresses, and restore
1300 them when we're finished dissecting the contained packet, so
1301 that the address columns in the summary don't reflect the
1302 contained packet, but reflect this packet instead. */
1303 save_dl_src = pinfo->dl_src;
1304 save_dl_dst = pinfo->dl_dst;
1305 save_net_src = pinfo->net_src;
1306 save_net_dst = pinfo->net_dst;
1307 save_src = pinfo->src;
1308 save_dst = pinfo->dst;
1309 save_in_error_pkt = pinfo->in_error_pkt;
1311 call_dissector(mip_handle, mip_tvb, pinfo, tree);
1313 /* Restore the "we're inside an error packet" flag. */
1314 pinfo->in_error_pkt = save_in_error_pkt;
1315 pinfo->dl_src = save_dl_src;
1316 pinfo->dl_dst = save_dl_dst;
1317 pinfo->net_src = save_net_src;
1318 pinfo->net_dst = save_net_dst;
1319 pinfo->src = save_src;
1320 pinfo->dst = save_dst;
1323 } /* safe_dissect_mip */
1326 * This function will dissect the AVPs in a diameter packet. It handles
1327 * all normal types, and even recursively calls itself for grouped AVPs
1329 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *avp_tree)
1331 /* adds the attribute value pairs to the tree */
1333 gchar avpTypeString[64];
1334 gchar avpNameString[64];
1337 gchar vendorName[64];
1340 proto_tree *avpi_tree;
1342 tvbuff_t *group_tvb;
1343 proto_tree *group_tree;
1344 proto_item *grouptf;
1347 int BadPacket = FALSE;
1351 proto_tree *flags_tree;
1353 gint32 packetLength;
1354 size_t avpDataLength;
1356 gchar flagstr[64] = "<None>";
1357 gchar *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Protected", "Mandatory", "Vendor-Specific" };
1361 packetLength = tvb_length(tvb);
1363 /* Check for invalid packet lengths */
1364 if (packetLength <= 0) {
1365 proto_tree_add_text(avp_tree, tvb, offset, tvb_length(tvb),
1366 "No Attribute Value Pairs Found");
1370 /* Spin around until we run out of packet */
1371 while (packetLength > 0 ) {
1373 /* Check for short packet */
1374 if (packetLength < (long)MIN_AVP_SIZE) {
1375 g_warning("Diameter: AVP Payload too short: %d bytes less than min size (%ld bytes))",
1376 packetLength, (long)MIN_AVP_SIZE);
1378 /* Don't even bother trying to parse a short packet. */
1382 /* Copy our header */
1383 tvb_memcpy(tvb, (guint8*) &avph, offset, MIN((long)sizeof(avph),packetLength));
1385 /* Fix the byte ordering */
1386 avph.avp_code = ntohl(avph.avp_code);
1387 avph.avp_flagsLength = ntohl(avph.avp_flagsLength);
1389 flags = (avph.avp_flagsLength & 0xff000000) >> 24;
1390 avpLength = avph.avp_flagsLength & 0x00ffffff;
1392 /* Set up our flags string */
1393 if (check_col(pinfo->cinfo, COL_INFO) || avp_tree) {
1395 for (i = 0; i < 8; i++) {
1399 strcat(flagstr, ", ");
1401 strcat(flagstr, fstr[i]);
1404 if (strlen(flagstr) == 0) {
1405 strcpy(flagstr,"<None>");
1409 /* Dissect our vendor id if it exists and set hdr length */
1410 if (flags & AVP_FLAGS_V) {
1411 vendorId = ntohl(avph.avp_vendorId);
1413 hdrLength = sizeof(e_avphdr);
1416 hdrLength = sizeof(e_avphdr) -
1423 diameter_vendor_to_str(vendorId, TRUE));
1428 /* Check for bad length */
1429 if (avpLength < MIN_AVP_SIZE ||
1430 ((long)avpLength > packetLength)) {
1431 g_warning("Diameter: AVP payload size invalid: avp_length: %ld bytes, "
1432 "min: %ld bytes, packetLen: %d",
1433 (long)avpLength, (long)MIN_AVP_SIZE,
1438 /* Check for bad flags */
1439 if (flags & AVP_FLAGS_RESERVED) {
1440 g_warning("Diameter: Invalid AVP: Reserved bit set. flags = 0x%x,"
1442 flags, AVP_FLAGS_RESERVED);
1443 /* For now, don't set bad packet, since I'm accidentally setting a wrong bit */
1444 /* BadPacket = TRUE; */
1448 * Compute amount of byte-alignment fix (Diameter AVPs are sent on 4 byte
1451 fixAmt = 4 - (avpLength % 4);
1452 if (fixAmt == 4) fixAmt = 0;
1454 /* shrink our packetLength */
1455 packetLength = packetLength - (avpLength + fixAmt);
1457 /* Check for out of bounds */
1458 if (packetLength < 0) {
1459 g_warning("Diameter: Bad AVP: Bad new length (%d bytes) ",
1464 /* Make avp Name & type */
1465 strcpy(avpTypeString, val_to_str(diameter_avp_get_type(avph.avp_code,vendorId),
1467 "Unknown-Type: 0x%08x"));
1468 strcpy(avpNameString, diameter_avp_get_name(avph.avp_code, vendorId));
1470 avptf = proto_tree_add_text(avp_tree, tvb,
1471 offset, avpLength + fixAmt,
1472 "%s (%s) l:0x%x (%d bytes) (%d padded bytes)",
1473 avpNameString, avpTypeString, avpLength,
1474 avpLength, avpLength+fixAmt);
1475 avpi_tree = proto_item_add_subtree(avptf,
1476 ett_diameter_avpinfo);
1478 if (avpi_tree !=NULL) {
1480 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_code,
1481 tvb, offset, 4, avph.avp_code, "AVP Code: %s", avpNameString);
1484 tf = proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_flags, tvb,
1485 offset , 1, flags, "Flags: 0x%02x (%s)", flags,
1487 flags_tree = proto_item_add_subtree(tf, ett_diameter_avp_flags);
1488 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_vendor_specific, tvb, offset, 1, flags);
1489 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_mandatory, tvb, offset, 1, flags);
1490 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_protected, tvb, offset, 1, flags);
1491 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved3, tvb, offset, 1, flags);
1492 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved4, tvb, offset, 1, flags);
1493 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved5, tvb, offset, 1, flags);
1494 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved6, tvb, offset, 1, flags);
1495 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved7, tvb, offset, 1, flags);
1498 proto_tree_add_uint(avpi_tree, hf_diameter_avp_length,
1499 tvb, offset, 3, avpLength);
1502 if (flags & AVP_FLAGS_V) {
1503 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_vendor_id,
1504 tvb, offset, 4, vendorId, vendorName);
1508 avpDataLength = avpLength - hdrLength;
1511 * If we've got a bad packet, just highlight the data. Don't try
1512 * to parse it, and, don't move to next AVP.
1515 offset -= hdrLength;
1516 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1517 tvb, offset, tvb_length(tvb) - offset,
1518 tvb_get_ptr(tvb, offset, tvb_length(tvb) - offset),
1519 "Bad AVP (Suspect Data Not Dissected)");
1523 avpType=diameter_avp_get_type(avph.avp_code,vendorId);
1526 case DIAMETER_GROUPED:
1527 sprintf(buffer, "%s Grouped AVPs", avpNameString);
1528 /* Recursively call ourselves */
1529 grouptf = proto_tree_add_text(avpi_tree,
1530 tvb, offset, tvb_length(tvb),
1533 group_tree = proto_item_add_subtree(grouptf,
1536 group_tvb = tvb_new_subset(tvb, offset,
1537 MIN(avpDataLength, tvb_length(tvb)-offset), avpDataLength);
1538 if (group_tree != NULL) {
1539 dissect_avps( group_tvb, pinfo, group_tree);
1543 case DIAMETER_IDENTITY:
1547 data = tvb_get_ptr(tvb, offset, avpDataLength);
1548 proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
1549 tvb, offset, avpDataLength, data,
1552 (int)avpDataLength, data);
1555 case DIAMETER_UTF8STRING:
1559 data = tvb_get_ptr(tvb, offset, avpDataLength);
1560 proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
1561 tvb, offset, avpDataLength, data,
1562 "UTF8String: %*.*s",
1564 (int)avpDataLength, data);
1567 case DIAMETER_IP_ADDRESS:
1568 if (avpDataLength == 4) {
1569 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_v4addr,
1570 tvb, offset, avpDataLength, FALSE);
1571 } else if (avpDataLength == 16) {
1572 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_v6addr,
1573 tvb, offset, avpDataLength, FALSE);
1575 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1576 tvb, offset, avpDataLength,
1577 tvb_get_ptr(tvb, offset, avpDataLength),
1578 "Error! Bad Address Length");
1582 case DIAMETER_INTEGER32:
1583 if (avpDataLength == 4) {
1584 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_int32,
1585 tvb, offset, avpDataLength, FALSE);
1587 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1588 tvb, offset, avpDataLength,
1589 tvb_get_ptr(tvb, offset, avpDataLength),
1590 "Error! Bad Integer32 Length");
1594 case DIAMETER_UNSIGNED32:
1595 if (avpDataLength == 4) {
1598 data = tvb_get_ntohl(tvb, offset);
1599 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1600 tvb, offset, avpDataLength, data,
1601 "Value: 0x%08x (%u)", data, data);
1603 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1604 tvb, offset, avpDataLength,
1605 tvb_get_ptr(tvb, offset, avpDataLength),
1606 "Error! Bad Unsigned32 Length");
1610 case DIAMETER_INTEGER64:
1611 if (avpDataLength == 8) {
1612 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_int64,
1613 tvb, offset, 8, FALSE);
1615 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1616 tvb, offset, avpDataLength,
1617 tvb_get_ptr(tvb, offset, avpDataLength),
1618 "Error! Bad Integer64 Length");
1622 case DIAMETER_UNSIGNED64:
1623 if (avpDataLength == 8) {
1624 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_uint64,
1625 tvb, offset, 8, FALSE);
1627 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1628 tvb, offset, avpDataLength,
1629 tvb_get_ptr(tvb, offset, avpDataLength),
1630 "Error! Bad Unsigned64 Length");
1635 if (avpDataLength == 4) {
1640 data.secs = tvb_get_ntohl(tvb, offset);
1641 data.secs -= NTP_TIME_DIFF;
1644 ltp = localtime(&data.secs);
1645 strftime(buffer, 64,
1646 "%a, %d %b %Y %H:%M:%S %z", ltp);
1648 proto_tree_add_time_format(avpi_tree, hf_diameter_avp_data_time,
1649 tvb, offset, avpDataLength, &data,
1650 "Time: %s", buffer);
1652 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1653 tvb, offset, avpDataLength,
1654 tvb_get_ptr(tvb, offset, avpDataLength),
1655 "Error! Bad Time Length");
1659 case DIAMETER_ENUMERATED:
1660 if (avpDataLength == 4) {
1663 data = tvb_get_ntohl(tvb, offset);
1664 valstr = diameter_avp_get_value(avph.avp_code, vendorId, data);
1665 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1666 tvb, offset, avpDataLength, data,
1667 "Value: 0x%08x (%u): %s", data,
1670 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1671 tvb, offset, avpDataLength,
1672 tvb_get_ptr(tvb, offset, avpDataLength),
1673 "Error! Bad Enumerated Length");
1676 case DIAMETER_VENDOR_ID:
1677 if (avpDataLength == 4) {
1680 data = tvb_get_ntohl(tvb, offset);
1681 valstr = diameter_vendor_to_str(data, TRUE);
1682 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1683 tvb, offset, avpDataLength, data,
1684 "Vendor ID: %s (0x%08x)", valstr,
1687 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1688 tvb, offset, avpDataLength,
1689 tvb_get_ptr(tvb, offset, avpDataLength),
1690 "Error! Bad Vendor ID Length");
1693 case DIAMETER_APPLICATION_ID:
1694 if (avpDataLength == 4) {
1697 data = tvb_get_ntohl(tvb, offset);
1698 valstr = diameter_app_to_str(data);
1699 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1700 tvb, offset, avpDataLength, data,
1701 "Application ID: %s (0x%08x)",
1704 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1705 tvb, offset, avpDataLength,
1706 tvb_get_ptr(tvb, offset, avpDataLength),
1707 "Error! Bad Application ID Length");
1710 case DIAMETER_MIP_REG_REQ:
1711 safe_dissect_mip(tvb, pinfo, avpi_tree, offset, avpDataLength);
1715 case DIAMETER_OCTET_STRING:
1716 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1717 tvb, offset, avpDataLength,
1718 tvb_get_ptr(tvb, offset, avpDataLength),
1719 "Hex Data Highlighted Below");
1723 } /* avpi_tree != null */
1724 offset += (avpLength - hdrLength);
1725 offset += fixAmt; /* fix byte alignment */
1727 } /* dissect_avps */
1732 proto_reg_handoff_diameter(void)
1734 static int Initialized=FALSE;
1735 static int TcpPort=0;
1736 static int SctpPort=0;
1737 static dissector_handle_t diameter_tcp_handle;
1738 static dissector_handle_t diameter_sctp_handle;
1741 diameter_tcp_handle = create_dissector_handle(dissect_diameter_tcp,
1743 diameter_sctp_handle = create_dissector_handle(dissect_diameter_sctp,
1747 dissector_delete("tcp.port", TcpPort, diameter_tcp_handle);
1748 dissector_delete("sctp.port", SctpPort, diameter_sctp_handle);
1751 /* set port for future deletes */
1752 TcpPort=gbl_diameterTcpPort;
1753 SctpPort=gbl_diameterSctpPort;
1755 strcpy(gbl_diameterString, "Diameter Protocol");
1757 /* g_warning ("Diameter: Adding tcp dissector to port %d",
1758 gbl_diameterTcpPort); */
1759 dissector_add("tcp.port", gbl_diameterTcpPort, diameter_tcp_handle);
1760 dissector_add("sctp.port", gbl_diameterSctpPort, diameter_sctp_handle);
1763 /* registration with the filtering engine */
1765 proto_register_diameter(void)
1767 static hf_register_info hf[] = {
1768 { &hf_diameter_version,
1769 { "Version", "diameter.version", FT_UINT8, BASE_HEX, NULL, 0x00,
1771 { &hf_diameter_length,
1772 { "Length","diameter.length", FT_UINT24, BASE_DEC, NULL, 0x0,
1775 { &hf_diameter_flags,
1776 { "Flags", "diameter.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
1778 { &hf_diameter_flags_request,
1779 { "Request", "diameter.flags.request", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_R,
1781 { &hf_diameter_flags_proxyable,
1782 { "Proxyable", "diameter.flags.proxyable", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_P,
1784 { &hf_diameter_flags_error,
1785 { "Error","diameter.flags.error", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_E,
1787 { &hf_diameter_flags_reserved3,
1788 { "Reserved","diameter.flags.reserved3", FT_BOOLEAN, 8, TFS(&reserved_set),
1789 DIAM_FLAGS_RESERVED3, "", HFILL }},
1790 { &hf_diameter_flags_reserved4,
1791 { "Reserved","diameter.flags.reserved4", FT_BOOLEAN, 8, TFS(&reserved_set),
1792 DIAM_FLAGS_RESERVED4, "", HFILL }},
1793 { &hf_diameter_flags_reserved5,
1794 { "Reserved","diameter.flags.reserved5", FT_BOOLEAN, 8, TFS(&reserved_set),
1795 DIAM_FLAGS_RESERVED5, "", HFILL }},
1796 { &hf_diameter_flags_reserved6,
1797 { "Reserved","diameter.flags.reserved6", FT_BOOLEAN, 8, TFS(&reserved_set),
1798 DIAM_FLAGS_RESERVED6, "", HFILL }},
1799 { &hf_diameter_flags_reserved7,
1800 { "Reserved","diameter.flags.reserved7", FT_BOOLEAN, 8, TFS(&reserved_set),
1801 DIAM_FLAGS_RESERVED7, "", HFILL }},
1803 { &hf_diameter_code,
1804 { "Command Code","diameter.code", FT_UINT24, BASE_DEC,
1805 NULL, 0x0, "", HFILL }},
1806 { &hf_diameter_vendor_id,
1807 { "VendorId", "diameter.vendorId", FT_UINT32, BASE_DEC, NULL,
1809 { &hf_diameter_hopbyhopid,
1810 { "Hop-by-Hop Identifier", "diameter.hopbyhopid", FT_UINT32,
1811 BASE_HEX, NULL, 0x0, "", HFILL }},
1812 { &hf_diameter_endtoendid,
1813 { "End-to-End Identifier", "diameter.endtoendid", FT_UINT32,
1814 BASE_HEX, NULL, 0x0, "", HFILL }},
1816 { &hf_diameter_avp_code,
1817 { "AVP Code","diameter.avp.code", FT_UINT32, BASE_DEC,
1818 NULL, 0x0, "", HFILL }},
1819 { &hf_diameter_avp_length,
1820 { "AVP Length","diameter.avp.length", FT_UINT24, BASE_DEC,
1821 NULL, 0x0, "", HFILL }},
1824 { &hf_diameter_avp_flags,
1825 { "AVP Flags","diameter.avp.flags", FT_UINT8, BASE_HEX,
1826 NULL, 0x0, "", HFILL }},
1827 { &hf_diameter_avp_flags_vendor_specific,
1828 { "Vendor-Specific", "diameter.flags.vendorspecific", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_V,
1830 { &hf_diameter_avp_flags_mandatory,
1831 { "Mandatory", "diameter.flags.mandatory", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_M,
1833 { &hf_diameter_avp_flags_protected,
1834 { "Protected","diameter.avp.flags.protected", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_P,
1836 { &hf_diameter_avp_flags_reserved3,
1837 { "Reserved","diameter.avp.flags.reserved3", FT_BOOLEAN, 8, TFS(&reserved_set),
1838 AVP_FLAGS_RESERVED3, "", HFILL }},
1839 { &hf_diameter_avp_flags_reserved4,
1840 { "Reserved","diameter.avp.flags.reserved4", FT_BOOLEAN, 8, TFS(&reserved_set),
1841 AVP_FLAGS_RESERVED4, "", HFILL }},
1842 { &hf_diameter_avp_flags_reserved5,
1843 { "Reserved","diameter.avp.flags.reserved5", FT_BOOLEAN, 8, TFS(&reserved_set),
1844 AVP_FLAGS_RESERVED5, "", HFILL }},
1845 { &hf_diameter_avp_flags_reserved6,
1846 { "Reserved","diameter.avp.flags.reserved6", FT_BOOLEAN, 8, TFS(&reserved_set),
1847 AVP_FLAGS_RESERVED6, "", HFILL }},
1848 { &hf_diameter_avp_flags_reserved7,
1849 { "Reserved","diameter.avp.flags.reserved7", FT_BOOLEAN, 8, TFS(&reserved_set),
1850 AVP_FLAGS_RESERVED7, "", HFILL }},
1851 { &hf_diameter_avp_vendor_id,
1852 { "AVP Vendor Id","diameter.avp.vendorId", FT_UINT32, BASE_DEC,
1853 NULL, 0x0, "", HFILL }},
1854 { &hf_diameter_avp_data_uint64,
1855 { "Value","diameter.avp.data.uint64", FT_UINT64, BASE_DEC,
1856 NULL, 0x0, "", HFILL }},
1857 { &hf_diameter_avp_data_int64,
1858 { "Value","diameter.avp.data.int64", FT_INT64, BASE_DEC,
1859 NULL, 0x0, "", HFILL }},
1860 { &hf_diameter_avp_data_uint32,
1861 { "Value","diameter.avp.data.uint32", FT_UINT32, BASE_DEC,
1862 NULL, 0x0, "", HFILL }},
1863 { &hf_diameter_avp_data_int32,
1864 { "Value","diameter.avp.data.int32", FT_INT32, BASE_DEC,
1865 NULL, 0x0, "", HFILL }},
1866 { &hf_diameter_avp_data_bytes,
1867 { "Value","diameter.avp.data.bytes", FT_BYTES, BASE_NONE,
1868 NULL, 0x0, "", HFILL }},
1869 { &hf_diameter_avp_data_string,
1870 { "Value","diameter.avp.data.string", FT_STRING, BASE_NONE,
1871 NULL, 0x0, "", HFILL }},
1872 { &hf_diameter_avp_data_v4addr,
1873 { "IPv4 Address","diameter.avp.data.v4addr", FT_IPv4, BASE_NONE,
1874 NULL, 0x0, "", HFILL }},
1875 { &hf_diameter_avp_data_v6addr,
1876 { "IPv6 Address","diameter.avp.data.v6addr", FT_IPv6, BASE_NONE,
1877 NULL, 0x0, "", HFILL }},
1878 { &hf_diameter_avp_data_time,
1879 { "Time","diameter.avp.data.time", FT_ABSOLUTE_TIME, BASE_NONE,
1880 NULL, 0x0, "", HFILL }},
1883 static gint *ett[] = {
1885 &ett_diameter_flags,
1887 &ett_diameter_avp_flags,
1888 &ett_diameter_avpinfo
1890 module_t *diameter_module;
1892 proto_diameter = proto_register_protocol (gbl_diameterString,
1893 "Diameter", "diameter");
1894 proto_register_field_array(proto_diameter, hf, array_length(hf));
1895 proto_register_subtree_array(ett, array_length(ett));
1897 /* Register a configuration option for port */
1898 diameter_module = prefs_register_protocol(proto_diameter,
1899 proto_reg_handoff_diameter);
1900 prefs_register_uint_preference(diameter_module, "tcp.port",
1901 "Diameter TCP Port",
1902 "Set the TCP port for Diameter messages",
1904 &gbl_diameterTcpPort);
1905 prefs_register_uint_preference(diameter_module, "sctp.port",
1906 "Diameter SCTP Port",
1907 "Set the SCTP port for Diameter messages",
1909 &gbl_diameterSctpPort);
1911 * Build our default dictionary filename
1913 if (! gbl_diameterDictionary) {
1914 gbl_diameterDictionary = (gchar *) g_malloc(strlen(get_datafile_dir()) +
1915 1 + strlen(DICT_FN) + 1); /* slash + fn + null */
1916 sprintf(gbl_diameterDictionary, "%s" G_DIR_SEPARATOR_S "%s",
1917 get_datafile_dir(), DICT_FN );
1919 /* Now register its preferences so it can be changed. */
1920 prefs_register_string_preference(diameter_module, "dictionary.name",
1921 "Diameter XML Dictionary",
1922 "Set the dictionary used for Diameter messages",
1923 &gbl_diameterDictionary);
1925 /* Desegmentation */
1926 prefs_register_bool_preference(diameter_module, "desegment",
1927 "Desegment all Diameter messages spanning multiple TCP segments",
1928 "Whether the Diameter dissector should desegment all messages spanning multiple TCP segments",
1929 &gbl_diameter_desegment);
1931 /* Register some preferences we no longer support, so we can report
1932 them as obsolete rather than just illegal. */
1933 prefs_register_obsolete_preference(diameter_module, "udp.port");
1934 prefs_register_obsolete_preference(diameter_module, "command_in_header");
1935 } /* proto_register_diameter */