2 * Routines for Diameter packet disassembly
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.
27 * http://www.ietf.org/rfc/rfc3588.txt
28 * http://www.iana.org/assignments/radius-types
29 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-cc-03.txt
30 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-nasreq-14.txt
31 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-mobileip-16.txt
32 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-sip-app-01.txt
33 * http://www.ietf.org/html.charters/aaa-charter.html
47 #include <epan/filesystem.h>
49 #include <epan/packet.h>
50 #include <epan/addr_resolv.h>
51 #include <epan/report_err.h>
52 #include <epan/prefs.h>
53 #include "packet-tcp.h"
55 #ifdef NEED_SNPRINTF_H
56 # include "snprintf.h"
59 /* This must be defined before we include packet-diameter-defs.h */
61 /* Valid data types */
64 DIAMETER_OCTET_STRING = 1,
75 DIAMETER_IP_ADDRESS, /* OctetString */
76 DIAMETER_TIME, /* Integer 32 */
77 DIAMETER_UTF8STRING, /* OctetString */
78 DIAMETER_IDENTITY, /* OctetString */
79 DIAMETER_ENUMERATED, /* Integer 32 */
80 DIAMETER_IP_FILTER_RULE, /* OctetString */
81 DIAMETER_QOS_FILTER_RULE, /* OctetString */
82 DIAMETER_MIP_REG_REQ, /* OctetString */
83 DIAMETER_VENDOR_ID, /* Integer32 */
84 DIAMETER_APPLICATION_ID,
85 DIAMETER_URI, /* OctetString */
86 DIAMETER_SESSION_ID /* OctetString */
91 static value_string TypeValues[]={
92 { DIAMETER_OCTET_STRING, "OctetString" },
93 { DIAMETER_INTEGER32, "Integer32" },
94 { DIAMETER_INTEGER64, "Integer64" },
95 { DIAMETER_UNSIGNED32, "Unsigned32" },
96 { DIAMETER_UNSIGNED64, "Unsigned64" },
97 { DIAMETER_FLOAT32, "Float32" },
98 { DIAMETER_FLOAT64, "Float64" },
99 { DIAMETER_FLOAT128, "Float128" },
100 { DIAMETER_GROUPED, "Grouped" },
101 { DIAMETER_IP_ADDRESS, "IpAddress" },
102 { DIAMETER_TIME, "Time" },
103 { DIAMETER_UTF8STRING, "UTF8String" },
104 { DIAMETER_IDENTITY, "DiameterIdentity" },
105 { DIAMETER_ENUMERATED, "Enumerated" },
106 { DIAMETER_IP_FILTER_RULE, "IPFilterRule" },
107 { DIAMETER_QOS_FILTER_RULE, "QOSFilterRule" },
108 { DIAMETER_MIP_REG_REQ, "MIPRegistrationRequest"},
109 { DIAMETER_VENDOR_ID, "VendorId"},
110 { DIAMETER_APPLICATION_ID, "AppId"},
111 { DIAMETER_URI, "DiameterURI"},
112 { DIAMETER_SESSION_ID, "Session-Id"},
117 typedef struct value_name {
120 struct value_name *next;
123 typedef struct old_avp_info {
126 diameterDataType type;
127 value_string *values;
130 typedef struct avp_info {
134 diameterDataType type;
136 struct avp_info *next;
139 typedef struct command_code {
143 struct command_code *next;
146 typedef struct vendor_id {
150 struct vendor_id *next;
153 typedef struct application_id {
156 struct application_id *next;
159 static avpInfo *avpListHead=NULL;
160 static VendorId *vendorListHead=NULL;
161 static CommandCode *commandListHead=NULL;
162 static ApplicationId *ApplicationIdHead=NULL;
165 #include "packet-diameter-defs.h"
167 #define NTP_TIME_DIFF (2208988800UL)
169 #define TCP_PORT_DIAMETER 1812
170 #define SCTP_PORT_DIAMETER 1812
172 static const true_false_string reserved_set = {
173 "*** Error! Reserved Bit is Set",
177 static int proto_diameter = -1;
178 static int hf_diameter_length = -1;
179 static int hf_diameter_code = -1;
180 static int hf_diameter_hopbyhopid =-1;
181 static int hf_diameter_endtoendid =-1;
182 static int hf_diameter_version = -1;
183 static int hf_diameter_vendor_id = -1;
184 static int hf_diameter_flags = -1;
185 static int hf_diameter_flags_request = -1;
186 static int hf_diameter_flags_proxyable = -1;
187 static int hf_diameter_flags_error = -1;
188 static int hf_diameter_flags_T = -1;
189 static int hf_diameter_flags_reserved4 = -1;
190 static int hf_diameter_flags_reserved5 = -1;
191 static int hf_diameter_flags_reserved6 = -1;
192 static int hf_diameter_flags_reserved7 = -1;
194 static int hf_diameter_avp_code = -1;
195 static int hf_diameter_avp_length = -1;
196 static int hf_diameter_avp_flags = -1;
197 static int hf_diameter_avp_flags_vendor_specific = -1;
198 static int hf_diameter_avp_flags_mandatory = -1;
199 static int hf_diameter_avp_flags_protected = -1;
200 static int hf_diameter_avp_flags_reserved3 = -1;
201 static int hf_diameter_avp_flags_reserved4 = -1;
202 static int hf_diameter_avp_flags_reserved5 = -1;
203 static int hf_diameter_avp_flags_reserved6 = -1;
204 static int hf_diameter_avp_flags_reserved7 = -1;
205 static int hf_diameter_avp_vendor_id = -1;
208 static int hf_diameter_avp_data_uint32 = -1;
209 static int hf_diameter_avp_data_int32 = -1;
210 static int hf_diameter_avp_data_uint64 = -1;
211 static int hf_diameter_avp_data_int64 = -1;
212 static int hf_diameter_avp_data_bytes = -1;
213 static int hf_diameter_avp_data_string = -1;
214 static int hf_diameter_avp_data_v4addr = -1;
215 static int hf_diameter_avp_data_v6addr = -1;
216 static int hf_diameter_avp_data_time = -1;
217 static int hf_diameter_avp_session_id = -1;
218 static gint ett_diameter = -1;
219 static gint ett_diameter_flags = -1;
220 static gint ett_diameter_avp = -1;
221 static gint ett_diameter_avp_flags = -1;
222 static gint ett_diameter_avpinfo = -1;
224 static guint gbl_diameterTcpPort=TCP_PORT_DIAMETER;
225 static guint gbl_diameterSctpPort=SCTP_PORT_DIAMETER;
227 /* desegmentation of Diameter over TCP */
228 static gboolean gbl_diameter_desegment = TRUE;
230 /* Allow zero as a valid application ID */
231 static gboolean allow_zero_as_app_id = FALSE;
233 /* Supress console output at unknown AVP:s,Flags etc */
234 static gboolean suppress_console_output = TRUE;
236 #define DICT_FN "diameter/dictionary.xml"
237 static gchar *gbl_diameterDictionary;
239 typedef struct _e_diameterhdr {
240 guint32 versionLength;
241 guint32 flagsCmdCode;
247 typedef struct _e_avphdr {
249 guint32 avp_flagsLength;
250 guint32 avp_vendorId; /* optional */
253 /* Diameter Header Flags */
254 /* RPrrrrrrCCCCCCCCCCCCCCCCCCCCCCCC */
255 #define DIAM_FLAGS_R 0x80
256 #define DIAM_FLAGS_P 0x40
257 #define DIAM_FLAGS_E 0x20
258 #define DIAM_FLAGS_T 0x10
259 #define DIAM_FLAGS_RESERVED4 0x08
260 #define DIAM_FLAGS_RESERVED5 0x04
261 #define DIAM_FLAGS_RESERVED6 0x02
262 #define DIAM_FLAGS_RESERVED7 0x01
263 #define DIAM_FLAGS_RESERVED 0x0f
265 #define DIAM_LENGTH_MASK 0x00ffffffl
266 #define DIAM_COMMAND_MASK DIAM_LENGTH_MASK
267 #define DIAM_GET_FLAGS(dh) ((dh.flagsCmdCode & ~DIAM_COMMAND_MASK) >> 24)
268 #define DIAM_GET_VERSION(dh) ((dh.versionLength & (~DIAM_LENGTH_MASK)) >> 24)
269 #define DIAM_GET_COMMAND(dh) (dh.flagsCmdCode & DIAM_COMMAND_MASK)
270 #define DIAM_GET_LENGTH(dh) (dh.versionLength & DIAM_LENGTH_MASK)
272 /* Diameter AVP Flags */
273 #define AVP_FLAGS_P 0x20
274 #define AVP_FLAGS_V 0x80
275 #define AVP_FLAGS_M 0x40
276 #define AVP_FLAGS_RESERVED3 0x10
277 #define AVP_FLAGS_RESERVED4 0x08
278 #define AVP_FLAGS_RESERVED5 0x04
279 #define AVP_FLAGS_RESERVED6 0x02
280 #define AVP_FLAGS_RESERVED7 0x01
281 #define AVP_FLAGS_RESERVED 0x1f /* 00011111 -- V M P X X X X X */
283 #define MIN_AVP_SIZE (sizeof(e_avphdr) - sizeof(guint32))
284 #define MIN_DIAMETER_SIZE (sizeof(e_diameterhdr))
286 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
290 * This routine will do a push-parse of the passed in
291 * filename. This was taken almost verbatum from
292 * the xmlsoft examples.
295 xmlParseFilePush( char *filename, int checkValid) {
299 int res, size = 1024;
301 xmlParserCtxtPtr ctxt;
303 /* I wonder what kind of a performance hit this is? */
304 *XmlStub.xmlDoValidityCheckingDefaultValue = checkValid;
306 f = fopen(filename, "r");
308 report_open_failure(filename, errno, FALSE);
312 res = fread(chars, 1, 4, f);
314 ctxt = XmlStub.xmlCreatePushParserCtxt(NULL, NULL,
315 chars, res, filename);
316 while ((res = fread(chars, 1, size-1, f)) > 0) {
317 XmlStub.xmlParseChunk(ctxt, chars, res, 0);
319 XmlStub.xmlParseChunk(ctxt, chars, 0, 1);
322 XmlStub.xmlFreeParserCtxt(ctxt);
328 report_failure( "Error! Invalid xml in %s! Failed DTD check!",
333 } /* xmlParseFilePush */
336 * This routine will add a static avp to the avp list. It is
337 * only called when the XML dictionary fails to load properly.
340 addStaticAVP(int code, gchar *name, diameterDataType type, value_string *values)
343 ValueName *vEntry=NULL;
346 /* Parse our values array, if we have one */
348 for (i=0; values[i].strptr != NULL; i++) {
349 ValueName *ve = NULL;
351 ve = g_malloc(sizeof(ValueName));
352 ve->name = strdup(values[i].strptr);
353 ve->value = values[i].value;
359 /* And, create the entry */
360 entry = (avpInfo *)g_malloc(sizeof(avpInfo));
361 entry->name = g_strdup(name);
363 entry->vendorName = NULL;
365 entry->values = vEntry;
367 entry->type = DIAMETER_ENUMERATED;
369 /* And, add it to the list */
370 entry->next = avpListHead;
378 * This routine will parse an XML avp entry, and add it to our
379 * avp list. If any values are present in the avp, it will
383 xmlParseAVP(xmlNodePtr cur)
385 char *name=NULL, *description=NULL, *code=NULL, *mayEncrypt=NULL,
386 *mandatory=NULL, *protected=NULL, *vendorBit=NULL, *vendorName = NULL,
391 ValueName *vEntry=NULL;
394 /* First, get our properties */
395 name = XmlStub.xmlGetProp(cur, "name");
396 description = XmlStub.xmlGetProp(cur, "description");
397 code = XmlStub.xmlGetProp(cur, "code");
398 mayEncrypt = XmlStub.xmlGetProp(cur, "may-encrypt");
399 mandatory = XmlStub.xmlGetProp(cur, "mandatory");
400 protected = XmlStub.xmlGetProp(cur, "protected");
401 vendorBit = XmlStub.xmlGetProp(cur, "vendor-bit");
402 vendorName = XmlStub.xmlGetProp(cur, "vendor-id");
403 constrained = XmlStub.xmlGetProp(cur, "constrained");
405 cur = cur->xmlChildrenNode;
407 while (cur != NULL ) {
408 if (strcasecmp(cur->name, "type") == 0) {
409 type = XmlStub.xmlGetProp(cur, "type-name");
410 } else if (strcasecmp(cur->name, "enum") == 0) {
411 char *valueName=NULL, *valueCode=NULL;
412 ValueName *ve = NULL;
413 valueName = XmlStub.xmlGetProp(cur, "name");
414 valueCode = XmlStub.xmlGetProp(cur, "code");
416 if (!valueName || !valueCode) {
417 report_failure( "Error, bad value on avp %s", name);
421 ve = g_malloc(sizeof(ValueName));
422 ve->name = strdup(valueName);
423 ve->value = atol(valueCode);
427 } else if (strcasecmp(cur->name, "grouped") == 0) {
428 /* WORK Recurse here for grouped AVPs */
435 * Check for the AVP Type.
438 for (i = 0; TypeValues[i].strptr; i++) {
439 if (!strcasecmp(type, TypeValues[i].strptr)) {
440 avpType = TypeValues[i].value;
445 if (TypeValues[i].strptr == NULL) {
446 report_failure( "Invalid Type field in dictionary! avp %s (%s)", name, type);
449 } else if (!vEntry) {
450 report_failure("Missing type/enum field in dictionary avpName=%s",
455 /* WORK - Handle flags -- for validation later */
458 /* And, create the entry */
459 entry = (avpInfo *)g_malloc(sizeof(avpInfo));
460 entry->name = g_strdup(name);
461 entry->code = atol(code);
463 entry->vendorName = g_strdup(vendorName);
465 entry->vendorName = NULL;
466 entry->type = avpType;
467 entry->values = vEntry;
469 entry->type = DIAMETER_INTEGER32;
471 /* And, add it to the list */
472 entry->next = avpListHead;
479 * This routine will add a command to the list of commands.
482 addCommand(int code, char *name, char *vendorId)
487 * Allocate the memory required for the dictionary.
489 entry = (CommandCode *) g_malloc(sizeof (CommandCode));
492 report_failure("Unable to allocate memory");
497 * Allocate memory for the AVPName and copy the name to the
500 entry->name = g_strdup(name);
503 entry->vendorName = g_strdup(vendorId);
505 entry->vendorName = "None";
507 /* Add the entry to the list */
508 entry->next = commandListHead;
509 commandListHead = entry;
515 * This routine will parse the XML command, and add it to our
519 xmlParseCommand(xmlNodePtr cur)
521 char *name, *code, *vendorIdString;
526 name = XmlStub.xmlGetProp(cur, "name");
527 code = XmlStub.xmlGetProp(cur, "code");
528 if (!name || !code) {
529 report_failure("Invalid command. Name or code missing!");
532 vendorIdString = XmlStub.xmlGetProp(cur, "vendor-id");
534 if (!vendorIdString || !strcasecmp(vendorIdString, "None")) {
535 vendorIdString = NULL;
538 return (addCommand(atoi(code), name, vendorIdString));
539 } /* xmlParseCommand */
541 /* This routine adds an application to the name<-> id table */
543 dictionaryAddApplication(char *name, int id)
545 ApplicationId *entry;
547 if (!name || (id < 0) || (id == 0 && !allow_zero_as_app_id)) {
548 report_failure( "Diameter Error: Invalid application (name=%p, id=%d)",
551 } /* Sanity Checks */
553 entry = g_malloc(sizeof(ApplicationId));
555 report_failure( "Unable to allocate memory");
559 entry->name = g_strdup(name);
562 /* Add it to the list */
563 entry->next = ApplicationIdHead;
564 ApplicationIdHead = entry;
567 } /* dictionaryAddApplication */
570 * This routine will add a vendor to the vendors list
573 addVendor(int id, gchar *name, gchar *longName)
578 vendor=g_malloc(sizeof(VendorId));
584 vendor->name = g_strdup(name);
585 vendor->longName = g_strdup(longName);
586 vendor->next = vendorListHead;
587 vendorListHead = vendor;
593 * This routine will pars in a XML vendor entry.
596 xmlParseVendor(xmlNodePtr cur)
598 char *name=NULL, *code=NULL, *id=NULL;
600 /* First, get our properties */
601 id = XmlStub.xmlGetProp(cur, "vendor-id");
602 name = XmlStub.xmlGetProp(cur, "name");
603 code = XmlStub.xmlGetProp(cur, "code");
605 if (!id || !name || !code) {
606 report_failure( "Invalid vendor section. vendor-id, name, and code must be specified");
610 return (addVendor(atoi(code), id, name));
614 * This routine will either parse in the base protocol, or an application.
617 xmlDictionaryParseSegment(xmlNodePtr cur, int base)
623 /* Add our application */
624 id = XmlStub.xmlGetProp(cur, "id");
625 name = XmlStub.xmlGetProp(cur, "name");
629 report_failure("Diameter: Invalid application!: name=\"%s\", id=\"%s\"",
630 name?name:"NULL", id?id:"NULL");
634 /* Add the application */
635 if (dictionaryAddApplication(name, atol(id)) != 0) {
645 cur = cur->xmlChildrenNode;
646 while (cur != NULL) {
647 if (strcasecmp(cur->name, "avp") == 0) {
648 /* we have an avp!!! */
650 } else if (strcasecmp(cur->name, "vendor") == 0) {
651 /* we have a vendor */
653 /* For now, ignore typedefn and text */
654 } else if (strcasecmp(cur->name, "command") == 0) {
655 /* Found a command */
656 xmlParseCommand(cur);
657 } else if (strcasecmp(cur->name, "text") == 0) {
658 } else if (strcasecmp(cur->name, "comment") == 0) {
659 } else if (strcasecmp(cur->name, "typedefn") == 0) {
660 /* WORK -- parse in valid types . . . */
662 /* IF we got here, we're an error */
663 report_failure("Error! expecting an avp or a typedefn (got \"%s\")",
670 } /* xmlDictionaryParseSegment */
673 * The main xml parse routine. This will walk through an XML
674 * dictionary that has been parsed by libxml.
677 xmlDictionaryParse(xmlNodePtr cur)
679 /* We should expect a base protocol, followed by multiple applications */
680 while (cur != NULL) {
681 if (strcasecmp(cur->name, "base") == 0) {
682 /* Base protocol. Descend and parse */
683 xmlDictionaryParseSegment(cur, 1);
684 } else if (strcasecmp(cur->name, "application") == 0) {
685 /* Application. Descend and parse */
686 xmlDictionaryParseSegment(cur, 0);
687 } else if (strcasecmp(cur->name, "text") == 0) {
689 } else if (strcasecmp(cur->name, "comment") == 0) {
692 report_failure( "Diameter: XML Expecting a base or an application (got \"%s\")",
701 } /* xmlDictionaryParse */
704 * This routine will call libxml to parse in the dictionary.
707 loadXMLDictionary(void)
713 * build an XML tree from a the file;
715 XmlStub.xmlKeepBlanksDefault(0); /* Strip leading and trailing blanks */
716 XmlStub.xmlSubstituteEntitiesDefault(1); /* Substitute entities automagically */
717 doc = xmlParseFilePush(gbl_diameterDictionary, 1); /* Parse the XML (do validity checks)*/
719 /* Check for invalid xml */
721 report_failure("Diameter: Unable to parse xmldictionary %s",
722 gbl_diameterDictionary);
727 * Check the document is of the right kind
729 cur = XmlStub.xmlDocGetRootElement(doc);
731 report_failure("Diameter: Error: \"%s\": empty document",
732 gbl_diameterDictionary);
733 XmlStub.xmlFreeDoc(doc);
736 if (XmlStub.xmlStrcmp(cur->name, (const xmlChar *) "dictionary")) {
737 report_failure("Diameter: Error: \"%s\": document of the wrong type, root node != dictionary",
738 gbl_diameterDictionary);
739 XmlStub.xmlFreeDoc(doc);
744 * Ok, the dictionary has been parsed by libxml, and is valid.
745 * All we have to do now is read in our information.
747 if (xmlDictionaryParse(cur->xmlChildrenNode) != 0) {
748 /* Error has already been printed */
752 /* Once we're done parsing, free up the xml memory */
753 XmlStub.xmlFreeDoc(doc);
757 } /* loadXMLDictionary */
760 * Fallback routine. In the event of ANY error when loading the XML
761 * dictionary, this routine will populate the new avp list structures
762 * with the old static data from packet-diameter-defs.h
765 initializeDictionaryDefaults(void)
769 /* Add static vendors to list */
770 for(i=0; diameter_vendor_specific_vendors[i].strptr; i++) {
771 addVendor(diameter_vendor_specific_vendors[i].value,
772 diameter_vendor_specific_vendors[i].strptr,
773 diameter_vendor_specific_vendors[i].strptr);
775 /* Add static commands to list. */
776 for(i=0; diameter_command_code_vals[i].strptr; i++) {
777 addCommand(diameter_command_code_vals[i].value,
778 diameter_command_code_vals[i].strptr, NULL);
781 /* Add static AVPs to list */
782 for (i=0; old_diameter_avps[i].name; i++) {
783 addStaticAVP(old_diameter_avps[i].code,
784 old_diameter_avps[i].name,
785 old_diameter_avps[i].type,
786 old_diameter_avps[i].values);
789 } /* initializeDictionaryDefaults */
792 * This routine will attempt to load the XML dictionary, and on
793 * failure, will call initializeDictionaryDefaults to load in
794 * our static dictionary.
797 initializeDictionary(void)
800 * Using ugly ordering here. If loadLibXML succeeds, then
801 * loadXMLDictionary will be called. This is one of the few times when
802 * I think this is prettier than the nested if alternative.
805 (loadXMLDictionary() != 0)) {
806 /* Something failed. Use the static dictionary */
807 report_failure("Diameter: Using static dictionary! (Unable to use XML)");
808 initializeDictionaryDefaults();
810 } /* initializeDictionary */
815 * These routines manipulate the diameter structures.
818 /* return vendor string, based on the id */
820 diameter_vendor_to_str(guint32 vendorId, gboolean longName) {
822 static gchar buffer[64];
824 for (probe=vendorListHead; probe; probe=probe->next) {
825 if (vendorId == probe->id) {
827 return probe->longName;
833 snprintf(buffer, sizeof(buffer),
834 "Vendor 0x%08x", vendorId);
836 } /*diameter_vendor_to_str */
838 /* return command string, based on the code */
840 diameter_command_to_str(guint32 commandCode, guint32 vendorId)
843 static gchar buffer[64];
844 gchar *vendorName=NULL;
847 vendorName = diameter_vendor_to_str(vendorId, FALSE);
849 for (probe=commandListHead; probe; probe=probe->next) {
850 if (commandCode == probe->code) {
852 /* g_warning("Command: Comparing \"%s\" to \"%s\"", */
853 /* vendorName?vendorName:"(null)", */
854 /* probe->vendorName?probe->vendorName:"(null)"); */
855 /* Now check the vendor name */
856 if (!strcmp(vendorName, probe->vendorName))
860 /* With no vendor id, the Command's entry should be "None" */
861 if (!strcmp(probe->vendorName, "None")) {
869 if ( suppress_console_output == FALSE )
870 g_warning("Diameter: Unable to find name for command code 0x%08x, Vendor \"%u\"!",
871 commandCode, vendorId);
872 snprintf(buffer, sizeof(buffer),
873 "Cmd-0x%08x", commandCode);
876 }/*diameter_command_to_str */
878 /* return application string, based on the id */
880 diameter_app_to_str(guint32 vendorId) {
881 ApplicationId *probe;
882 static gchar buffer[64];
884 for (probe=ApplicationIdHead; probe; probe=probe->next) {
885 if (vendorId == probe->id) {
890 snprintf(buffer, sizeof(buffer),
891 "AppId 0x%08x", vendorId);
893 } /*diameter_app_to_str */
895 /* return an avp type, based on the code */
896 static diameterDataType
897 diameter_avp_get_type(guint32 avpCode, guint32 vendorId){
899 gchar *vendorName=NULL;
902 vendorName = diameter_vendor_to_str(vendorId, FALSE);
904 for (probe=avpListHead; probe; probe=probe->next) {
905 if (avpCode == probe->code) {
908 /* g_warning("AvpType: Comparing \"%s\" to \"%s\"", */
909 /* vendorName?vendorName:"(null)", */
910 /* probe->vendorName?probe->vendorName:"(null)"); */
911 /* Now check the vendor name */
912 if (probe->vendorName && (!strcmp(vendorName, probe->vendorName)))
916 /* No Vendor ID -- vendorName should be null */
917 if (!probe->vendorName)
924 /* If we don't find it, assume it's data */
925 if ( suppress_console_output == FALSE )
926 g_warning("Diameter: Unable to find type for avpCode %u, Vendor %u!", avpCode,
928 return DIAMETER_OCTET_STRING;
929 } /* diameter_avp_get_type */
931 /* return an avp name from the code */
933 diameter_avp_get_name(guint32 avpCode, guint32 vendorId)
935 static gchar buffer[64];
937 gchar *vendorName=NULL;
940 vendorName = diameter_vendor_to_str(vendorId, FALSE);
942 for (probe=avpListHead; probe; probe=probe->next) {
943 if (avpCode == probe->code) {
945 /* g_warning("AvpName: Comparing \"%s\" to \"%s\"", */
946 /* vendorName?vendorName:"(null)", */
947 /* probe->vendorName?probe->vendorName:"(null)"); */
948 /* Now check the vendor name */
949 if (probe->vendorName && (!strcmp(vendorName, probe->vendorName)))
953 /* No Vendor ID -- vendorName should be null */
954 if (!probe->vendorName)
960 if ( suppress_console_output == FALSE )
961 g_warning("Diameter: Unable to find name for AVP 0x%08x, Vendor %u!",
964 /* If we don't find it, build a name string */
965 sprintf(buffer, "Unknown AVP:0x%08x", avpCode);
967 } /* diameter_avp_get_name */
969 diameter_avp_get_value(guint32 avpCode, guint32 vendorId, guint32 avpValue)
972 gchar *vendorName=NULL;
975 vendorName = diameter_vendor_to_str(vendorId, FALSE);
977 for (probe=avpListHead; probe; probe=probe->next) {
978 if (avpCode == probe->code) {
980 /* g_warning("AvpValue: Comparing \"%s\" to \"%s\"", */
981 /* vendorName?vendorName:"(null)", */
982 /* probe->vendorName?probe->vendorName:"(null)"); */
983 /* Now check the vendor name */
984 if (probe->vendorName && (!strcmp(vendorName, probe->vendorName))) {
986 for(vprobe=probe->values; vprobe; vprobe=vprobe->next) {
987 if (avpValue == vprobe->value) {
991 return "(Unknown value)";
994 if (!probe->vendorName) {
996 for(vprobe=probe->values; vprobe; vprobe=vprobe->next) {
997 if (avpValue == vprobe->value) {
1001 return "(Unknown value)";
1006 /* We didn't find the avp */
1007 return "(Unknown AVP)";
1008 } /* diameter_avp_get_value */
1011 /* Code to actually dissect the packets */
1014 check_diameter(tvbuff_t *tvb)
1016 if (!tvb_bytes_exist(tvb, 0, 1))
1017 return FALSE; /* not enough bytes to check the version */
1018 if (tvb_get_guint8(tvb, 0) != 1)
1019 return FALSE; /* not version 1 */
1021 /* XXX - fetch length and make sure it's at least MIN_DIAMETER_SIZE?
1022 Fetch flags and check that none of the DIAM_FLAGS_RESERVED bits
1031 dissect_diameter_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1034 /* Set up structures needed to add the protocol subtree and manage it */
1037 proto_tree *flags_tree;
1039 proto_tree *diameter_tree;
1043 proto_tree *avp_tree;
1045 int BadPacket = FALSE;
1046 guint32 commandCode, pktLength;
1047 guint8 version, flags;
1048 gchar flagstr[64] = "<None>";
1049 gchar *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Error", "Proxyable", "Request" };
1050 gchar commandString[64], vendorName[64];
1053 static int initialized=FALSE;
1056 * Only parse in dictionary if there are diameter packets to
1060 /* Read in our dictionary, if it exists. */
1061 initializeDictionary();
1065 /* Make entries in Protocol column and Info column on summary display */
1066 if (check_col(pinfo->cinfo, COL_PROTOCOL))
1067 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Diameter");
1068 if (check_col(pinfo->cinfo, COL_INFO))
1069 col_clear(pinfo->cinfo, COL_INFO);
1071 /* Copy our header */
1072 tvb_memcpy(tvb, (guint8*) &dh, offset, sizeof(dh));
1074 /* Fix byte ordering in our static structure */
1075 dh.versionLength = g_ntohl(dh.versionLength);
1076 dh.flagsCmdCode = g_ntohl(dh.flagsCmdCode);
1077 dh.vendorId = g_ntohl(dh.vendorId);
1078 dh.hopByHopId = g_ntohl(dh.hopByHopId);
1079 dh.endToEndId = g_ntohl(dh.endToEndId);
1083 diameter_vendor_to_str(dh.vendorId, TRUE));
1085 strcpy(vendorName, "None");
1089 /* Do the bit twiddling */
1090 version = DIAM_GET_VERSION(dh);
1091 pktLength = DIAM_GET_LENGTH(dh);
1092 flags = DIAM_GET_FLAGS(dh);
1093 commandCode = DIAM_GET_COMMAND(dh);
1095 /* Set up our flags */
1096 if (check_col(pinfo->cinfo, COL_INFO) || tree) {
1098 for (i = 0; i < 8; i++) {
1102 strcat(flagstr, ", ");
1104 strcat(flagstr, fstr[i]);
1107 if (strlen(flagstr) == 0) {
1108 strcpy(flagstr,"<None>");
1112 /* Set up our commandString */
1113 strcpy(commandString, diameter_command_to_str(commandCode, dh.vendorId));
1114 if (flags & DIAM_FLAGS_R)
1115 strcat(commandString, "-Request");
1117 strcat(commandString, "-Answer");
1119 /* Short packet. Should have at LEAST one avp */
1120 if (pktLength < MIN_DIAMETER_SIZE) {
1121 if ( suppress_console_output == FALSE )
1122 g_warning("Diameter: Packet too short: %u bytes less than min size (%lu bytes))",
1123 pktLength, (unsigned long)MIN_DIAMETER_SIZE);
1127 /* And, check our reserved flags/version */
1128 if ((flags & DIAM_FLAGS_RESERVED) ||
1130 if ( suppress_console_output == FALSE )
1131 g_warning("Diameter: Bad packet: Bad Flags(0x%x) or Version(%u)",
1136 if (check_col(pinfo->cinfo, COL_INFO)) {
1137 col_add_fstr(pinfo->cinfo, COL_INFO,
1138 "%s%s%s%s%s vendor=%s (hop-id=%u) (end-id=%u) RPE=%d%d%d",
1139 (BadPacket)?"***** Bad Packet!: ":"",
1140 (flags & DIAM_FLAGS_P)?"Proxyable ":"",
1141 (flags & DIAM_FLAGS_E)?" Error":"",
1143 (flags & (DIAM_FLAGS_P|DIAM_FLAGS_E))) ?
1145 commandString, vendorName,
1146 dh.hopByHopId, dh.endToEndId,
1147 (flags & DIAM_FLAGS_R)?1:0,
1148 (flags & DIAM_FLAGS_P)?1:0,
1149 (flags & DIAM_FLAGS_E)?1:0);
1153 /* In the interest of speed, if "tree" is NULL, don't do any work not
1154 necessary to generate protocol tree items. */
1157 /* create display subtree for the protocol */
1158 ti = proto_tree_add_item(tree, proto_diameter, tvb, offset,
1159 MAX(pktLength,MIN_DIAMETER_SIZE), FALSE);
1160 diameter_tree = proto_item_add_subtree(ti, ett_diameter);
1163 proto_tree_add_uint(diameter_tree,
1164 hf_diameter_version,
1171 proto_tree_add_uint(diameter_tree,
1172 hf_diameter_length, tvb,
1173 offset, 3, pktLength);
1177 tf = proto_tree_add_uint_format(diameter_tree, hf_diameter_flags, tvb,
1178 offset , 1, flags, "Flags: 0x%02x (%s)", flags,
1180 flags_tree = proto_item_add_subtree(tf, ett_diameter_avp_flags);
1181 proto_tree_add_boolean(flags_tree, hf_diameter_flags_request, tvb, offset, 1, flags);
1182 proto_tree_add_boolean(flags_tree, hf_diameter_flags_proxyable, tvb, offset, 1, flags);
1183 proto_tree_add_boolean(flags_tree, hf_diameter_flags_error, tvb, offset, 1, flags);
1184 proto_tree_add_boolean(flags_tree, hf_diameter_flags_T, tvb, offset, 1, flags);
1185 proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved4, tvb, offset, 1, flags);
1186 proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved5, tvb, offset, 1, flags);
1187 proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved6, tvb, offset, 1, flags);
1188 proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved7, tvb, offset, 1, flags);
1193 proto_tree_add_uint_format(diameter_tree, hf_diameter_code,
1194 tvb, offset, 3, commandCode, "Command Code: %s", commandString);
1198 proto_tree_add_uint_format(diameter_tree,hf_diameter_vendor_id,
1199 tvb, offset, 4, dh.vendorId, "Vendor-Id: %s", vendorName);
1202 /* Hop-by-hop Identifier */
1203 proto_tree_add_uint(diameter_tree, hf_diameter_hopbyhopid,
1204 tvb, offset, 4, dh.hopByHopId);
1207 /* End-to-end Identifier */
1208 proto_tree_add_uint(diameter_tree, hf_diameter_endtoendid,
1209 tvb, offset, 4, dh.endToEndId);
1212 /* If we have a bad packet, don't bother trying to parse the AVPs */
1217 /* Start looking at the AVPS */
1218 /* Make the next tvbuff */
1220 /* Update the lengths */
1221 avplength= pktLength - sizeof(e_diameterhdr);
1223 avp_tvb = tvb_new_subset(tvb, offset, avplength, avplength);
1224 avptf = proto_tree_add_text(diameter_tree,
1225 tvb, offset, avplength,
1226 "Attribute Value Pairs");
1228 avp_tree = proto_item_add_subtree(avptf,
1230 if (avp_tree != NULL) {
1231 dissect_avps( avp_tvb, pinfo, avp_tree);
1235 } /* dissect_diameter_common */
1239 get_diameter_pdu_len(tvbuff_t *tvb, int offset)
1241 /* Get the length of the Diameter packet. */
1242 return tvb_get_ntoh24(tvb, offset + 1);
1246 dissect_diameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1248 if (!check_diameter(tvb))
1250 dissect_diameter_common(tvb, pinfo, tree);
1251 return tvb_length(tvb);
1255 dissect_diameter_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1257 if (!check_diameter(tvb))
1259 tcp_dissect_pdus(tvb, pinfo, tree, gbl_diameter_desegment, 4,
1260 get_diameter_pdu_len, dissect_diameter_common);
1261 return tvb_length(tvb);
1262 } /* dissect_diameter_tcp */
1265 * Call the mip_dissector, after saving our pinfo variables
1266 * so it doesn't write to our column display.
1269 safe_dissect_mip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1270 size_t offset, size_t length)
1272 static dissector_handle_t mip_handle;
1273 static int mipInitialized=FALSE;
1275 address save_dl_src;
1276 address save_dl_dst;
1277 address save_net_src;
1278 address save_net_dst;
1281 gboolean save_in_error_pkt;
1283 if (!mipInitialized) {
1284 mip_handle = find_dissector("mip");
1285 mipInitialized=TRUE;
1288 mip_tvb = tvb_new_subset(tvb, offset,
1289 MIN(length, tvb_length(tvb)-offset),
1292 /* The contained packet is a MIP registration request;
1293 dissect it with the MIP dissector. */
1294 col_set_writable(pinfo->cinfo, FALSE);
1296 /* Also, save the current values of the addresses, and restore
1297 them when we're finished dissecting the contained packet, so
1298 that the address columns in the summary don't reflect the
1299 contained packet, but reflect this packet instead. */
1300 save_dl_src = pinfo->dl_src;
1301 save_dl_dst = pinfo->dl_dst;
1302 save_net_src = pinfo->net_src;
1303 save_net_dst = pinfo->net_dst;
1304 save_src = pinfo->src;
1305 save_dst = pinfo->dst;
1306 save_in_error_pkt = pinfo->in_error_pkt;
1308 call_dissector(mip_handle, mip_tvb, pinfo, tree);
1310 /* Restore the "we're inside an error packet" flag. */
1311 pinfo->in_error_pkt = save_in_error_pkt;
1312 pinfo->dl_src = save_dl_src;
1313 pinfo->dl_dst = save_dl_dst;
1314 pinfo->net_src = save_net_src;
1315 pinfo->net_dst = save_net_dst;
1316 pinfo->src = save_src;
1317 pinfo->dst = save_dst;
1320 } /* safe_dissect_mip */
1323 * This function will dissect the AVPs in a diameter packet. It handles
1324 * all normal types, and even recursively calls itself for grouped AVPs
1326 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *avp_tree)
1328 /* adds the attribute value pairs to the tree */
1330 gchar avpTypeString[64];
1331 gchar avpNameString[64];
1334 gchar vendorName[64];
1337 proto_tree *avpi_tree;
1339 tvbuff_t *group_tvb;
1340 proto_tree *group_tree;
1341 proto_item *grouptf;
1344 int BadPacket = FALSE;
1348 proto_tree *flags_tree;
1350 gint32 packetLength;
1351 size_t avpDataLength;
1353 gchar flagstr[64] = "<None>";
1354 gchar *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Protected", "Mandatory", "Vendor-Specific" };
1358 packetLength = tvb_length(tvb);
1360 /* Check for invalid packet lengths */
1361 if (packetLength <= 0) {
1362 proto_tree_add_text(avp_tree, tvb, offset, tvb_length(tvb),
1363 "No Attribute Value Pairs Found");
1367 /* Spin around until we run out of packet */
1368 while (packetLength > 0 ) {
1370 /* Check for short packet */
1371 if (packetLength < (long)MIN_AVP_SIZE) {
1372 if ( suppress_console_output == FALSE )
1373 g_warning("Diameter: AVP Payload too short: %d bytes less than min size (%ld bytes))",
1374 packetLength, (long)MIN_AVP_SIZE);
1376 /* Don't even bother trying to parse a short packet. */
1380 /* Copy our header */
1381 tvb_memcpy(tvb, (guint8*) &avph, offset, MIN((long)sizeof(avph),packetLength));
1383 /* Fix the byte ordering */
1384 avph.avp_code = g_ntohl(avph.avp_code);
1385 avph.avp_flagsLength = g_ntohl(avph.avp_flagsLength);
1387 flags = (avph.avp_flagsLength & 0xff000000) >> 24;
1388 avpLength = avph.avp_flagsLength & 0x00ffffff;
1390 /* Set up our flags string */
1391 if (check_col(pinfo->cinfo, COL_INFO) || avp_tree) {
1393 for (i = 0; i < 8; i++) {
1397 strcat(flagstr, ", ");
1399 strcat(flagstr, fstr[i]);
1402 if (strlen(flagstr) == 0) {
1403 strcpy(flagstr,"<None>");
1407 /* Dissect our vendor id if it exists and set hdr length */
1408 if (flags & AVP_FLAGS_V) {
1409 vendorId = g_ntohl(avph.avp_vendorId);
1411 hdrLength = sizeof(e_avphdr);
1414 hdrLength = sizeof(e_avphdr) -
1421 diameter_vendor_to_str(vendorId, TRUE));
1426 /* Check for bad length */
1427 if (avpLength < MIN_AVP_SIZE ||
1428 ((long)avpLength > packetLength)) {
1429 if ( suppress_console_output == FALSE )
1430 g_warning("Diameter: AVP payload size invalid: avp_length: %ld bytes, "
1431 "min: %ld bytes, packetLen: %d",
1432 (long)avpLength, (long)MIN_AVP_SIZE,
1437 /* Check for bad flags */
1438 if (flags & AVP_FLAGS_RESERVED) {
1439 if ( suppress_console_output == FALSE )
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
1449 * Compute amount of byte-alignment fix (Diameter AVPs are sent on 4 byte
1452 fixAmt = 4 - (avpLength % 4);
1453 if (fixAmt == 4) fixAmt = 0;
1455 /* shrink our packetLength */
1456 packetLength = packetLength - (avpLength + fixAmt);
1458 /* Check for out of bounds */
1459 if (packetLength < 0) {
1460 if ( suppress_console_output == FALSE )
1461 g_warning("Diameter: Bad AVP: Bad new length (%d bytes) ",
1466 /* Make avp Name & type */
1467 strcpy(avpTypeString, val_to_str(diameter_avp_get_type(avph.avp_code,vendorId),
1469 "Unknown-Type: 0x%08x"));
1470 strcpy(avpNameString, diameter_avp_get_name(avph.avp_code, vendorId));
1472 avptf = proto_tree_add_text(avp_tree, tvb,
1473 offset, avpLength + fixAmt,
1474 "%s (%s) l:0x%x (%d bytes) (%d padded bytes)",
1475 avpNameString, avpTypeString, avpLength,
1476 avpLength, avpLength+fixAmt);
1477 avpi_tree = proto_item_add_subtree(avptf,
1478 ett_diameter_avpinfo);
1480 if (avpi_tree !=NULL) {
1482 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_code,
1483 tvb, offset, 4, avph.avp_code, "AVP Code: %s", avpNameString);
1486 tf = proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_flags, tvb,
1487 offset , 1, flags, "Flags: 0x%02x (%s)", flags,
1489 flags_tree = proto_item_add_subtree(tf, ett_diameter_avp_flags);
1490 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_vendor_specific, tvb, offset, 1, flags);
1491 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_mandatory, tvb, offset, 1, flags);
1492 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_protected, tvb, offset, 1, flags);
1493 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved3, tvb, offset, 1, flags);
1494 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved4, tvb, offset, 1, flags);
1495 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved5, tvb, offset, 1, flags);
1496 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved6, tvb, offset, 1, flags);
1497 proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved7, tvb, offset, 1, flags);
1500 proto_tree_add_uint(avpi_tree, hf_diameter_avp_length,
1501 tvb, offset, 3, avpLength);
1504 if (flags & AVP_FLAGS_V) {
1505 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_vendor_id,
1506 tvb, offset, 4, vendorId, vendorName);
1510 avpDataLength = avpLength - hdrLength;
1513 * If we've got a bad packet, just highlight the data. Don't try
1514 * to parse it, and, don't move to next AVP.
1517 offset -= hdrLength;
1518 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1519 tvb, offset, tvb_length(tvb) - offset,
1520 tvb_get_ptr(tvb, offset, tvb_length(tvb) - offset),
1521 "Bad AVP (Suspect Data Not Dissected)");
1525 avpType=diameter_avp_get_type(avph.avp_code,vendorId);
1528 case DIAMETER_GROUPED:
1529 sprintf(buffer, "%s Grouped AVPs", avpNameString);
1530 /* Recursively call ourselves */
1531 grouptf = proto_tree_add_text(avpi_tree,
1532 tvb, offset, tvb_length(tvb),
1535 group_tree = proto_item_add_subtree(grouptf,
1538 group_tvb = tvb_new_subset(tvb, offset,
1539 MIN(avpDataLength, tvb_length(tvb)-offset), avpDataLength);
1540 if (group_tree != NULL) {
1541 dissect_avps( group_tvb, pinfo, group_tree);
1545 case DIAMETER_IDENTITY:
1549 data = tvb_get_ptr(tvb, offset, avpDataLength);
1550 proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
1551 tvb, offset, avpDataLength, data,
1554 (int)avpDataLength, data);
1557 case DIAMETER_UTF8STRING:
1561 data = tvb_get_ptr(tvb, offset, avpDataLength);
1562 proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
1563 tvb, offset, avpDataLength, data,
1564 "UTF8String: %*.*s",
1566 (int)avpDataLength, data);
1569 case DIAMETER_IP_ADDRESS:
1570 if (avpDataLength == 4) {
1571 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_v4addr,
1572 tvb, offset, avpDataLength, FALSE);
1573 } else if (avpDataLength == 16) {
1574 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_v6addr,
1575 tvb, offset, avpDataLength, FALSE);
1577 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1578 tvb, offset, avpDataLength,
1579 tvb_get_ptr(tvb, offset, avpDataLength),
1580 "Error! Bad Address Length");
1584 case DIAMETER_INTEGER32:
1585 if (avpDataLength == 4) {
1586 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_int32,
1587 tvb, offset, avpDataLength, FALSE);
1589 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1590 tvb, offset, avpDataLength,
1591 tvb_get_ptr(tvb, offset, avpDataLength),
1592 "Error! Bad Integer32 Length");
1596 case DIAMETER_UNSIGNED32:
1597 if (avpDataLength == 4) {
1600 data = tvb_get_ntohl(tvb, offset);
1601 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1602 tvb, offset, avpDataLength, data,
1603 "Value: 0x%08x (%u)", data, data);
1605 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1606 tvb, offset, avpDataLength,
1607 tvb_get_ptr(tvb, offset, avpDataLength),
1608 "Error! Bad Unsigned32 Length");
1612 case DIAMETER_INTEGER64:
1613 if (avpDataLength == 8) {
1614 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_int64,
1615 tvb, offset, 8, FALSE);
1617 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1618 tvb, offset, avpDataLength,
1619 tvb_get_ptr(tvb, offset, avpDataLength),
1620 "Error! Bad Integer64 Length");
1624 case DIAMETER_UNSIGNED64:
1625 if (avpDataLength == 8) {
1626 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_uint64,
1627 tvb, offset, 8, FALSE);
1629 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1630 tvb, offset, avpDataLength,
1631 tvb_get_ptr(tvb, offset, avpDataLength),
1632 "Error! Bad Unsigned64 Length");
1637 if (avpDataLength == 4) {
1642 data.secs = tvb_get_ntohl(tvb, offset);
1643 data.secs -= NTP_TIME_DIFF;
1646 ltp = localtime(&data.secs);
1647 strftime(buffer, 64,
1648 "%a, %d %b %Y %H:%M:%S %z", ltp);
1650 proto_tree_add_time_format(avpi_tree, hf_diameter_avp_data_time,
1651 tvb, offset, avpDataLength, &data,
1652 "Time: %s", buffer);
1654 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1655 tvb, offset, avpDataLength,
1656 tvb_get_ptr(tvb, offset, avpDataLength),
1657 "Error! Bad Time Length");
1661 case DIAMETER_ENUMERATED:
1662 if (avpDataLength == 4) {
1665 data = tvb_get_ntohl(tvb, offset);
1666 valstr = diameter_avp_get_value(avph.avp_code, vendorId, data);
1667 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1668 tvb, offset, avpDataLength, data,
1669 "Value: 0x%08x (%u): %s", data,
1672 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1673 tvb, offset, avpDataLength,
1674 tvb_get_ptr(tvb, offset, avpDataLength),
1675 "Error! Bad Enumerated Length");
1679 case DIAMETER_VENDOR_ID:
1680 if (avpDataLength == 4) {
1683 data = tvb_get_ntohl(tvb, offset);
1684 valstr = diameter_vendor_to_str(data, TRUE);
1685 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1686 tvb, offset, avpDataLength, data,
1687 "Vendor ID: %s (0x%08x)", valstr,
1690 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1691 tvb, offset, avpDataLength,
1692 tvb_get_ptr(tvb, offset, avpDataLength),
1693 "Error! Bad Vendor ID Length");
1697 case DIAMETER_APPLICATION_ID:
1698 if (avpDataLength == 4) {
1701 data = tvb_get_ntohl(tvb, offset);
1702 valstr = diameter_app_to_str(data);
1703 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1704 tvb, offset, avpDataLength, data,
1705 "Application ID: %s (0x%08x)",
1708 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1709 tvb, offset, avpDataLength,
1710 tvb_get_ptr(tvb, offset, avpDataLength),
1711 "Error! Bad Application ID Length");
1715 case DIAMETER_MIP_REG_REQ:
1716 safe_dissect_mip(tvb, pinfo, avpi_tree, offset, avpDataLength);
1719 case DIAMETER_SESSION_ID:
1720 proto_tree_add_item(avpi_tree, hf_diameter_avp_session_id,
1721 tvb, offset, avpDataLength, FALSE);
1725 case DIAMETER_OCTET_STRING:
1726 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1727 tvb, offset, avpDataLength,
1728 tvb_get_ptr(tvb, offset, avpDataLength),
1729 "Hex Data Highlighted Below");
1733 } /* avpi_tree != null */
1734 offset += (avpLength - hdrLength);
1735 offset += fixAmt; /* fix byte alignment */
1737 } /* dissect_avps */
1742 proto_reg_handoff_diameter(void)
1744 static int Initialized=FALSE;
1745 static int TcpPort=0;
1746 static int SctpPort=0;
1747 static dissector_handle_t diameter_tcp_handle;
1748 static dissector_handle_t diameter_handle;
1751 diameter_tcp_handle = new_create_dissector_handle(dissect_diameter_tcp,
1753 diameter_handle = new_create_dissector_handle(dissect_diameter,
1757 dissector_delete("tcp.port", TcpPort, diameter_tcp_handle);
1758 dissector_delete("sctp.port", SctpPort, diameter_handle);
1761 /* set port for future deletes */
1762 TcpPort=gbl_diameterTcpPort;
1763 SctpPort=gbl_diameterSctpPort;
1765 /* g_warning ("Diameter: Adding tcp dissector to port %d",
1766 gbl_diameterTcpPort); */
1767 dissector_add("tcp.port", gbl_diameterTcpPort, diameter_tcp_handle);
1768 dissector_add("sctp.port", gbl_diameterSctpPort, diameter_handle);
1771 /* registration with the filtering engine */
1773 proto_register_diameter(void)
1775 static hf_register_info hf[] = {
1776 { &hf_diameter_version,
1777 { "Version", "diameter.version", FT_UINT8, BASE_HEX, NULL, 0x00,
1779 { &hf_diameter_length,
1780 { "Length","diameter.length", FT_UINT24, BASE_DEC, NULL, 0x0,
1783 { &hf_diameter_flags,
1784 { "Flags", "diameter.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
1786 { &hf_diameter_flags_request,
1787 { "Request", "diameter.flags.request", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_R,
1789 { &hf_diameter_flags_proxyable,
1790 { "Proxyable", "diameter.flags.proxyable", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_P,
1792 { &hf_diameter_flags_error,
1793 { "Error","diameter.flags.error", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_E,
1795 { &hf_diameter_flags_T,
1796 { "T(Potentially re-transmitted message)","diameter.flags.T", FT_BOOLEAN, 8, TFS(&flags_set_truth),DIAM_FLAGS_T,
1798 { &hf_diameter_flags_reserved4,
1799 { "Reserved","diameter.flags.reserved4", FT_BOOLEAN, 8, TFS(&reserved_set),
1800 DIAM_FLAGS_RESERVED4, "", HFILL }},
1801 { &hf_diameter_flags_reserved5,
1802 { "Reserved","diameter.flags.reserved5", FT_BOOLEAN, 8, TFS(&reserved_set),
1803 DIAM_FLAGS_RESERVED5, "", HFILL }},
1804 { &hf_diameter_flags_reserved6,
1805 { "Reserved","diameter.flags.reserved6", FT_BOOLEAN, 8, TFS(&reserved_set),
1806 DIAM_FLAGS_RESERVED6, "", HFILL }},
1807 { &hf_diameter_flags_reserved7,
1808 { "Reserved","diameter.flags.reserved7", FT_BOOLEAN, 8, TFS(&reserved_set),
1809 DIAM_FLAGS_RESERVED7, "", HFILL }},
1811 { &hf_diameter_code,
1812 { "Command Code","diameter.code", FT_UINT24, BASE_DEC,
1813 NULL, 0x0, "", HFILL }},
1814 { &hf_diameter_vendor_id,
1815 { "VendorId", "diameter.vendorId", FT_UINT32, BASE_DEC, NULL,
1817 { &hf_diameter_hopbyhopid,
1818 { "Hop-by-Hop Identifier", "diameter.hopbyhopid", FT_UINT32,
1819 BASE_HEX, NULL, 0x0, "", HFILL }},
1820 { &hf_diameter_endtoendid,
1821 { "End-to-End Identifier", "diameter.endtoendid", FT_UINT32,
1822 BASE_HEX, NULL, 0x0, "", HFILL }},
1824 { &hf_diameter_avp_code,
1825 { "AVP Code","diameter.avp.code", FT_UINT32, BASE_DEC,
1826 NULL, 0x0, "", HFILL }},
1827 { &hf_diameter_avp_length,
1828 { "AVP Length","diameter.avp.length", FT_UINT24, BASE_DEC,
1829 NULL, 0x0, "", HFILL }},
1832 { &hf_diameter_avp_flags,
1833 { "AVP Flags","diameter.avp.flags", FT_UINT8, BASE_HEX,
1834 NULL, 0x0, "", HFILL }},
1835 { &hf_diameter_avp_flags_vendor_specific,
1836 { "Vendor-Specific", "diameter.flags.vendorspecific", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_V,
1838 { &hf_diameter_avp_flags_mandatory,
1839 { "Mandatory", "diameter.flags.mandatory", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_M,
1841 { &hf_diameter_avp_flags_protected,
1842 { "Protected","diameter.avp.flags.protected", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_P,
1844 { &hf_diameter_avp_flags_reserved3,
1845 { "Reserved","diameter.avp.flags.reserved3", FT_BOOLEAN, 8, TFS(&reserved_set),
1846 AVP_FLAGS_RESERVED3, "", HFILL }},
1847 { &hf_diameter_avp_flags_reserved4,
1848 { "Reserved","diameter.avp.flags.reserved4", FT_BOOLEAN, 8, TFS(&reserved_set),
1849 AVP_FLAGS_RESERVED4, "", HFILL }},
1850 { &hf_diameter_avp_flags_reserved5,
1851 { "Reserved","diameter.avp.flags.reserved5", FT_BOOLEAN, 8, TFS(&reserved_set),
1852 AVP_FLAGS_RESERVED5, "", HFILL }},
1853 { &hf_diameter_avp_flags_reserved6,
1854 { "Reserved","diameter.avp.flags.reserved6", FT_BOOLEAN, 8, TFS(&reserved_set),
1855 AVP_FLAGS_RESERVED6, "", HFILL }},
1856 { &hf_diameter_avp_flags_reserved7,
1857 { "Reserved","diameter.avp.flags.reserved7", FT_BOOLEAN, 8, TFS(&reserved_set),
1858 AVP_FLAGS_RESERVED7, "", HFILL }},
1859 { &hf_diameter_avp_vendor_id,
1860 { "AVP Vendor Id","diameter.avp.vendorId", FT_UINT32, BASE_DEC,
1861 NULL, 0x0, "", HFILL }},
1862 { &hf_diameter_avp_data_uint64,
1863 { "Value","diameter.avp.data.uint64", FT_UINT64, BASE_DEC,
1864 NULL, 0x0, "", HFILL }},
1865 { &hf_diameter_avp_data_int64,
1866 { "Value","diameter.avp.data.int64", FT_INT64, BASE_DEC,
1867 NULL, 0x0, "", HFILL }},
1868 { &hf_diameter_avp_data_uint32,
1869 { "Value","diameter.avp.data.uint32", FT_UINT32, BASE_DEC,
1870 NULL, 0x0, "", HFILL }},
1871 { &hf_diameter_avp_data_int32,
1872 { "Value","diameter.avp.data.int32", FT_INT32, BASE_DEC,
1873 NULL, 0x0, "", HFILL }},
1874 { &hf_diameter_avp_data_bytes,
1875 { "Value","diameter.avp.data.bytes", FT_BYTES, BASE_NONE,
1876 NULL, 0x0, "", HFILL }},
1877 { &hf_diameter_avp_data_string,
1878 { "Value","diameter.avp.data.string", FT_STRING, BASE_NONE,
1879 NULL, 0x0, "", HFILL }},
1880 { &hf_diameter_avp_data_v4addr,
1881 { "IPv4 Address","diameter.avp.data.v4addr", FT_IPv4, BASE_NONE,
1882 NULL, 0x0, "", HFILL }},
1883 { &hf_diameter_avp_data_v6addr,
1884 { "IPv6 Address","diameter.avp.data.v6addr", FT_IPv6, BASE_NONE,
1885 NULL, 0x0, "", HFILL }},
1886 { &hf_diameter_avp_data_time,
1887 { "Time","diameter.avp.data.time", FT_ABSOLUTE_TIME, BASE_NONE,
1888 NULL, 0x0, "", HFILL }},
1889 { &hf_diameter_avp_session_id,
1890 { "Session ID","diameter.avp.session_id", FT_STRING, BASE_NONE,
1891 NULL, 0x0, "", HFILL }},
1894 static gint *ett[] = {
1896 &ett_diameter_flags,
1898 &ett_diameter_avp_flags,
1899 &ett_diameter_avpinfo
1901 module_t *diameter_module;
1902 gchar *default_diameterDictionary;
1904 proto_diameter = proto_register_protocol ("Diameter Protocol", "Diameter", "diameter");
1905 proto_register_field_array(proto_diameter, hf, array_length(hf));
1906 proto_register_subtree_array(ett, array_length(ett));
1908 /* Register a configuration option for port */
1909 diameter_module = prefs_register_protocol(proto_diameter,
1910 proto_reg_handoff_diameter);
1911 prefs_register_uint_preference(diameter_module, "tcp.port",
1912 "Diameter TCP Port",
1913 "Set the TCP port for Diameter messages",
1915 &gbl_diameterTcpPort);
1916 prefs_register_uint_preference(diameter_module, "sctp.port",
1917 "Diameter SCTP Port",
1918 "Set the SCTP port for Diameter messages",
1920 &gbl_diameterSctpPort);
1922 * Build our default dictionary filename
1924 default_diameterDictionary = get_datafile_path(DICT_FN);
1927 * Now register the dictionary filename as a preference,
1928 * so it can be changed.
1930 gbl_diameterDictionary = default_diameterDictionary;
1931 prefs_register_string_preference(diameter_module, "dictionary.name",
1932 "Diameter XML Dictionary",
1933 "Set the dictionary used for Diameter messages",
1934 &gbl_diameterDictionary);
1937 * We don't need the default dictionary, so free it (a copy was made
1938 * of it in "gbl_diameterDictionary" by
1939 * "prefs_register_string_preference()").
1941 g_free(default_diameterDictionary);
1943 /* Desegmentation */
1944 prefs_register_bool_preference(diameter_module, "desegment",
1945 "Reassemble Diameter messages\nspanning multiple TCP segments",
1946 "Whether the Diameter dissector should reassemble messages spanning multiple TCP segments."
1947 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
1948 &gbl_diameter_desegment);
1949 /* Allow zero as valid application ID */
1950 prefs_register_bool_preference(diameter_module, "allow_zero_as_app_id",
1951 "Allow 0 as valid application ID",
1952 "If set, the value 0 (zero) can be used as a valid "
1953 "application ID. This is used in experimental cases.",
1954 &allow_zero_as_app_id);
1955 /* Register some preferences we no longer support, so we can report
1956 them as obsolete rather than just illegal. */
1957 /* Supress console output or not */
1958 prefs_register_bool_preference(diameter_module, "suppress_console_output",
1959 "Suppress console output for unknown AVP:s Flags etc.",
1960 "If console output for errors should be suppressed or not",
1961 &suppress_console_output);
1962 /* Register some preferences we no longer support, so we can report
1963 them as obsolete rather than just illegal. */
1964 prefs_register_obsolete_preference(diameter_module, "udp.port");
1965 prefs_register_obsolete_preference(diameter_module, "command_in_header");
1966 } /* proto_register_diameter */