From Michael Tuexen: update for the ASAP dissector which provides
[obnox/wireshark/wip.git] / packet-diameter.c
1 /* packet-diameter.c
2  * Routines for Diameter packet disassembly
3  *
4  * $Id: packet-diameter.c,v 1.48 2002/05/10 23:20:38 guy Exp $
5  *
6  * Copyright (c) 2001 by David Frascone <dave@frascone.com>
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
10  * Copyright 1998 Gerald Combs
11  *
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.
16  *
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.
21  *
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.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
37 #endif
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <time.h>
44 #include <glib.h>
45 #include <epan/filesystem.h>
46 #include "xmlstub.h"
47 #include <epan/packet.h>
48 #include <epan/resolv.h>
49 #include "prefs.h"
50
51 #ifdef NEED_SNPRINTF_H
52 # include "snprintf.h"
53 #endif
54
55 /* This must be defined before we include packet-diameter-defs.h */
56
57 /* Valid data types */
58 typedef enum {
59   /* Base Types */
60   DIAMETER_OCTET_STRING = 1,
61   DIAMETER_INTEGER32,
62   DIAMETER_INTEGER64,
63   DIAMETER_UNSIGNED32,
64   DIAMETER_UNSIGNED64,
65   DIAMETER_FLOAT32,
66   DIAMETER_FLOAT64,
67   DIAMETER_FLOAT128,
68   DIAMETER_GROUPED,
69
70   /* Derived Types */
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
81   
82 } diameterDataType;
83
84
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"},
105   {0, (char *)NULL}
106 };
107
108 typedef struct value_name {
109   guint32            value;
110   gchar             *name;
111   struct value_name *next;
112 } ValueName;
113
114 typedef struct old_avp_info {
115   guint32           code;
116   gchar            *name;
117   diameterDataType  type;
118   value_string     *values;
119 } oldAvpInfo;
120
121 typedef struct avp_info {
122   guint32           code;
123   gchar            *name;
124   gchar            *vendorName;
125   diameterDataType  type;
126   ValueName        *values;
127   struct avp_info  *next;
128 } avpInfo;
129
130 typedef struct command_code {
131   guint32              code;
132   gchar               *name;
133   gchar               *vendorName;
134   struct command_code *next;
135 } CommandCode;
136
137 typedef struct vendor_id {
138   guint32              id;
139   gchar               *name;
140   gchar               *longName;
141   struct vendor_id    *next;
142 } VendorId;
143
144 typedef struct application_id {
145   guint32              id;
146   gchar               *name;
147   struct application_id    *next;
148 } ApplicationId;
149
150 static avpInfo         *avpListHead=NULL;
151 static VendorId        *vendorListHead=NULL;
152 static CommandCode     *commandListHead=NULL;
153 static ApplicationId   *ApplicationIdHead=NULL;
154
155
156 #include "packet-diameter-defs.h"
157
158 #define  NTP_TIME_DIFF                   (2208988800UL)
159
160 #define TCP_PORT_DIAMETER       1812
161 #define SCTP_PORT_DIAMETER      1812
162
163 static const true_false_string flags_set_truth = {
164   "Set",
165   "Not set"
166 };
167
168 static const true_false_string reserved_set = {
169   "*** Error! Reserved Bit is Set",
170   "Ok"
171 };
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;
188
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;
201
202
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;
212
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;
218
219 static char gbl_diameterString[200];
220 static int gbl_diameterTcpPort=TCP_PORT_DIAMETER;
221 static int gbl_diameterSctpPort=SCTP_PORT_DIAMETER;
222
223 /* desegmentation of Diameter over TCP */
224 static gboolean gbl_diameter_desegment = TRUE;
225
226 #define DICT_FN  "diameter/dictionary.xml"
227 static gchar *gbl_diameterDictionary = NULL;
228
229 typedef struct _e_diameterhdr {
230   guint32  versionLength;
231   guint32  flagsCmdCode;
232   guint32  vendorId;
233   guint32  hopByHopId;
234   guint32  endToEndId;
235 } e_diameterhdr;
236
237 typedef struct _e_avphdr {
238   guint32 avp_code;
239   guint32 avp_flagsLength;
240   guint32 avp_vendorId;           /* optional */
241 } e_avphdr;
242
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
254
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)
261
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 */
272
273 #define MIN_AVP_SIZE (sizeof(e_avphdr) - sizeof(guint32))
274 #define MIN_DIAMETER_SIZE (sizeof(e_diameterhdr))
275
276 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
277
278
279 /*
280  * This routine will do a push-parse of the passed in
281  * filename.  This was taken almost verbatum from
282  * the xmlsoft examples.
283  */
284 static xmlDocPtr
285 xmlParseFilePush( char *filename, int checkValid) {
286   FILE *f;
287   xmlDocPtr doc=NULL;
288   int valid=0;
289   int res, size = 1024;
290   char chars[1024];
291   xmlParserCtxtPtr ctxt;
292         
293   /* I wonder what kind of a performance hit this is? */
294   *XmlStub.xmlDoValidityCheckingDefaultValue = checkValid;
295   
296   f = fopen(filename, "r");
297   if (f == NULL) {
298         g_warning("Diameter: Unable to open %s", filename);
299         return NULL;
300   }
301
302   res = fread(chars, 1, 4, f);
303   if (res > 0) {
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);
308         }
309         XmlStub.xmlParseChunk(ctxt, chars, 0, 1);
310         doc = ctxt->myDoc;
311         valid=ctxt->valid;
312         XmlStub.xmlFreeParserCtxt(ctxt);
313   }
314   fclose(f); 
315
316   /* Check valid */
317   if (!valid) {
318         g_warning( "Error!  Invalid xml in %s!  Failed DTD check!",
319                            filename);
320         return NULL;
321   }
322   return doc;
323 } /* xmlParseFilePush */
324
325 /*
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.
328  */
329 static int
330 addStaticAVP(int code, gchar *name, diameterDataType type, value_string *values)
331 {
332   avpInfo *entry;
333   ValueName *vEntry=NULL;
334   int i;
335
336   /* Parse our values array, if we have one */
337   if (values) {
338         for (i=0; values[i].strptr != NULL; i++) {
339           ValueName *ve = NULL;
340                         
341           ve = g_malloc(sizeof(ValueName));
342           ve->name = strdup(values[i].strptr);
343           ve->value = values[i].value;
344           ve->next = vEntry;
345           vEntry = ve;
346         }
347   } /* if values */
348
349         /* And, create the entry */
350   entry = (avpInfo *)g_malloc(sizeof(avpInfo));
351   entry->name = g_strdup(name);
352   entry->code = code;
353   entry->vendorName = NULL;
354   entry->type = type;
355   entry->values = vEntry;
356   if (vEntry)
357         entry->type = DIAMETER_INTEGER32;
358
359   /* And, add it to the list */
360   entry->next = avpListHead;
361   avpListHead = entry;
362
363   return (0);
364         
365 } /* addStaticAVP */
366
367 /*
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
370  * add them too.
371  */
372 static int
373 xmlParseAVP(xmlNodePtr cur)
374 {
375   char *name=NULL, *description=NULL, *code=NULL, *mayEncrypt=NULL,
376         *mandatory=NULL, *protected=NULL, *vendorBit=NULL, *vendorName = NULL,
377         *constrained=NULL;
378   char *type=NULL;
379   avpInfo *entry;
380   guint32 avpType=0;
381   ValueName *vEntry=NULL;
382   int i;
383
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");
394
395   cur = cur->xmlChildrenNode;
396
397   while (cur != NULL ) {
398         if (!strcasecmp((char *)cur->name, "type")) {
399           type = XmlStub.xmlGetProp(cur, "type-name");
400         }
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");
406                         
407           if (!valueName || !valueCode) {
408                 g_warning( "Error, bad value on avp %s", name);
409                 return (-1);
410           }
411                         
412           ve = g_malloc(sizeof(ValueName));
413           ve->name = strdup(valueName);
414           ve->value = atol(valueCode);
415
416           ve->next = vEntry;
417           vEntry = ve;
418         }
419         if (!strcasecmp((char *)cur->name, "grouped")) {
420           /* WORK Recurse here for grouped AVPs */
421           type = "grouped";
422         }
423         cur=cur->next;
424   } /* while */
425
426         /*
427          * Check for the AVP Type.
428          */
429   if (type) {
430         for (i = 0; TypeValues[i].strptr; i++) {
431           if (!strcasecmp(type, TypeValues[i].strptr)) {
432                 avpType = TypeValues[i].value;
433                 break;
434           }
435         }
436
437         if (TypeValues[i].strptr == NULL) {
438           g_warning( "Invalid Type field in dictionary! avp %s (%s)",  name, type);
439           return (-1);
440         }
441   } else if (!vEntry) {
442         g_warning("Missing type/enum field in dictionary avpName=%s",
443                           name);
444         return (-1);
445   }
446
447   /* WORK - Handle flags  -- for validation later */
448         
449
450   /* And, create the entry */
451   entry = (avpInfo *)g_malloc(sizeof(avpInfo));
452   entry->name = g_strdup(name);
453   entry->code = atol(code);
454   if (vendorName) 
455         entry->vendorName = g_strdup(vendorName);
456   else
457         entry->vendorName = NULL;
458   entry->type = avpType;
459   entry->values = vEntry;
460   if (vEntry)
461         entry->type = DIAMETER_INTEGER32;
462
463   /* And, add it to the list */
464   entry->next = avpListHead;
465   avpListHead = entry;
466
467   return (0);
468 } /* xmlParseAVP */
469
470 /*
471  * This routine will add a command to the list of commands.
472  */
473 static int
474 addCommand(int code, char *name, char *vendorId)
475 {
476   CommandCode *entry;
477
478   /*
479    * Allocate the memory required for the dictionary.
480    */
481   entry = (CommandCode *) g_malloc(sizeof (CommandCode));
482
483   if (entry == NULL) {
484         g_warning("Unable to allocate memory");
485         return (-1);
486   }
487
488   /*
489    * Allocate memory for the AVPName and copy the name to the
490    * structure
491    */
492   entry->name = g_strdup(name);
493   entry->code = code;
494   if (vendorId)
495         entry->vendorName = g_strdup(vendorId);
496   else
497         entry->vendorName = "None";
498
499   /* Add the entry to the list */
500   entry->next = commandListHead;
501   commandListHead = entry;
502
503   return 0;
504 } /* addCommand */
505
506 /*
507  * This routine will parse the XML command, and add it to our
508  * list of commands.
509  */
510 static int
511 xmlParseCommand(xmlNodePtr cur)
512 {
513   char *name, *code, *vendorIdString;
514
515   /*
516    * Get the Attributes
517    */
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!");
522         return -1;
523   }
524   vendorIdString = XmlStub.xmlGetProp(cur, "vendor-id");
525
526   if (!vendorIdString || !strcasecmp(vendorIdString, "None")) {
527         vendorIdString = NULL;
528   }
529
530   return (addCommand(atoi(code), name, vendorIdString));
531 } /* xmlParseCommand */
532
533 /* This routine adds an application to the name<-> id table */
534 static int
535 dictionaryAddApplication(char *name, int id)
536 {
537   ApplicationId *entry;
538
539   if (!name || (id <= 0)) {
540         g_warning( "Diameter Error: Inavlid application (name=%p, id=%d)",
541                            name, id);
542         return (-1);
543   } /* Sanity Checks */
544
545   entry = g_malloc(sizeof(ApplicationId));
546   if (!entry) {
547         g_warning( "Unable to allocate memory");
548         return (-1);
549   }
550         
551   entry->name = g_strdup(name);
552   entry->id = id;
553         
554   /* Add it to the list */
555   entry->next = ApplicationIdHead;
556   ApplicationIdHead = entry;
557
558   return 0;
559 } /* dictionaryAddApplication */
560
561 /*
562  * This routine will add a vendor to the vendors list
563  */
564 static int
565 addVendor(int id, gchar *name, gchar *longName)
566 {
567   VendorId *vendor;
568
569   /* add entry */
570   vendor=g_malloc(sizeof(VendorId));
571   if (!vendor) {
572         return (-1);
573   }
574
575   vendor->id = id;
576   vendor->name = g_strdup(name);
577   vendor->longName = g_strdup(longName);
578   vendor->next = vendorListHead;
579   vendorListHead = vendor;
580
581   return 0;
582 } /* addVendor */
583
584 /*
585  * This routine will pars in a XML vendor entry.
586  */
587 static int
588 xmlParseVendor(xmlNodePtr cur)
589 {
590   char *name=NULL, *code=NULL, *id=NULL;
591
592   /* First, get our properties */
593   id = XmlStub.xmlGetProp(cur, "vendor-id");
594   name = XmlStub.xmlGetProp(cur, "name");
595   code = XmlStub.xmlGetProp(cur, "code");
596
597   if (!id || !name || !code) {
598         g_warning( "Invalid vendor section.  vendor-id, name, and code must be specified");
599         return -1;
600   }
601
602   return (addVendor(atoi(code), id, name));
603 } /* addVendor */
604
605 /*
606  * This routine will either parse in the base protocol, or an application.
607  */
608 static int
609 xmlDictionaryParseSegment(xmlNodePtr cur, int base)
610 {
611   if (!base) {
612         char *name;
613         char *id;
614                 
615         /* Add our application */
616         id = XmlStub.xmlGetProp(cur, "id");
617         name = XmlStub.xmlGetProp(cur, "name");
618                 
619         if (!name || !id) {
620           /* ERROR!!! */
621           g_warning("Diameter: Invalid application!: name=\"%s\", id=\"%s\"",
622                                 name?name:"NULL", id?id:"NULL");
623           return -1;
624         }
625                 
626         /* Add the application */
627         if (dictionaryAddApplication(name, atol(id)) != 0) {
628           /* ERROR! */
629           return -1;
630         }
631   }
632
633         
634   /*
635    * Get segment values
636    */
637   cur = cur->xmlChildrenNode;
638   while (cur != NULL) {
639         if (!strcasecmp((char *)cur->name, "avp")) {
640           /* we have an avp!!! */
641           xmlParseAVP(cur);
642         } else if (!strcasecmp((char *)cur->name, "vendor")) {
643           /* we have a vendor */
644           xmlParseVendor(cur);
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 . . . */
653         } else {
654           /* IF we got here, we're an error */
655           g_warning("Error!  expecting an avp or a typedefn (got \"%s\")",
656                                 cur->name);
657           return (-1);
658         }
659         cur = cur->next;
660   } /* while */
661   return 0;
662 } /* xmlDictionaryParseSegment */
663
664 /*
665  * The main xml parse routine.  This will walk through an XML 
666  * dictionary that has been parsed by libxml.
667  */
668 static int
669 xmlDictionaryParse(xmlNodePtr cur)
670 {
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")) {
680           /* Ignore text */
681         } else {
682           g_warning( "Diameter: XML Expecting a base or an application  (got \"%s\")",
683                                  cur->name);
684           return (-1);
685         }
686         cur = cur->next;
687   }
688
689   return 0;
690
691 } /* xmlDictionaryParse */
692
693 /*
694  * This routine will call libxml to parse in the dictionary.
695  */
696 static int
697 loadXMLDictionary()
698 {
699   xmlDocPtr doc;
700   xmlNodePtr cur;
701
702   /*
703    * build an XML tree from a the file;
704    */
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)*/
708
709   /* Check for invalid xml */
710   if (doc == NULL) {
711         g_warning("Diameter: Unable to parse xmldictionary %s",
712                           gbl_diameterDictionary);
713         return -1;
714   }
715         
716   /*
717    * Check the document is of the right kind
718    */
719   cur = XmlStub.xmlDocGetRootElement(doc);
720   if (cur == NULL) {
721         g_warning("Diameter: Error: \"%s\": empty document",
722                           gbl_diameterDictionary);
723         XmlStub.xmlFreeDoc(doc);
724         return -1;
725   }
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);
730         return -1;
731   }
732         
733   /*
734    * Ok, the dictionary has been parsed by libxml, and is valid.
735    * All we have to do now is read in our information.
736    */
737   if (xmlDictionaryParse(cur->xmlChildrenNode) != 0) {
738         /* Error has already been printed */
739         return -1;
740   }
741
742   /* Once we're done parsing, free up the xml memory */
743   XmlStub.xmlFreeDoc(doc);
744
745   return 0;
746
747 } /* loadXMLDictionary */
748
749 /*
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
753  */
754 static void
755 initializeDictionaryDefaults()
756 {
757   int i;
758
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);
764   }
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);
769   }
770
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);
777   }
778
779 } /* initializeDictionaryDefaults */
780
781 /* 
782  * This routine will attempt to load the XML dictionary, and on 
783  * failure, will call initializeDictionaryDefaults to load in
784  * our static dictionary.
785  */
786 static void
787 initializeDictionary()
788 {
789   /*
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.
793    */
794   if (loadLibXML() ||
795           (loadXMLDictionary() != 0)) {
796         /* Something failed.  Use the static dictionary */
797         g_warning("Diameter: Using static dictionary! (Unable to use XML)");
798         initializeDictionaryDefaults();
799   }
800 } /* initializeDictionary */
801
802
803
804 /*
805  * These routines manipulate the diameter structures.
806  */
807
808 /* return vendor string, based on the id */
809 static gchar *
810 diameter_vendor_to_str(guint32 vendorId, gboolean longName) {
811   VendorId *probe;
812   static gchar buffer[64];
813
814   for (probe=vendorListHead; probe; probe=probe->next) {
815         if (vendorId == probe->id) {
816           if (longName)
817                 return probe->longName;
818           else
819                 return probe->name;
820         }
821   }
822
823   snprintf(buffer, sizeof(buffer),
824                    "Vendor 0x%08x", vendorId);
825   return buffer;
826 } /*diameter_vendor_to_str */
827
828 /* return command string, based on the code */
829 static gchar *
830 diameter_command_to_str(guint32 commandCode, guint32 vendorId)
831 {
832   CommandCode *probe;
833   static gchar buffer[64];
834   gchar *vendorName=NULL;
835
836   if (vendorId)
837         vendorName = diameter_vendor_to_str(vendorId, FALSE);
838
839   for (probe=commandListHead; probe; probe=probe->next) {
840         if (commandCode == probe->code) {
841           if (vendorId) {
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))
847                   /* We found it */
848                   return probe->name;
849           } else {
850                 /* With no vendor id, the Command's entry should be "None" */
851                 if (!strcmp(probe->vendorName, "None")) {
852                   /* We found it */
853                   return probe->name;
854                 }
855           }
856         }
857   }
858   
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);
863   return buffer;
864 }/*diameter_command_to_str */
865
866 /* return application string, based on the id */
867 static gchar *
868 diameter_app_to_str(guint32 vendorId) {
869   ApplicationId *probe;
870   static gchar buffer[64];
871
872   for (probe=ApplicationIdHead; probe; probe=probe->next) {
873         if (vendorId == probe->id) {
874           return probe->name;
875         }
876   }
877
878   snprintf(buffer, sizeof(buffer),
879                    "AppId 0x%08x", vendorId);
880   return buffer;
881 } /*diameter_app_to_str */
882
883 /* return an avp type, based on the code */
884 static diameterDataType
885 diameter_avp_get_type(guint32 avpCode, guint32 vendorId){
886   avpInfo *probe;
887   gchar *vendorName=NULL;
888   
889   if (vendorId)
890         vendorName = diameter_vendor_to_str(vendorId, FALSE);
891   
892   for (probe=avpListHead; probe; probe=probe->next) {
893         if (avpCode == probe->code) {
894           
895           if (vendorId) {
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)))
901                   /* We found it! */
902                   return probe->type;
903           } else {
904                 /* No Vendor ID -- vendorName should be null */
905                 if (!probe->vendorName)
906                   /* We found it! */
907                   return probe->type;
908           }
909         }
910   }
911         
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,
914                         vendorId);
915   return DIAMETER_OCTET_STRING;
916 } /* diameter_avp_get_type */
917
918 /* return an avp name from the code */
919 static gchar *
920 diameter_avp_get_name(guint32 avpCode, guint32 vendorId)
921 {
922   static gchar buffer[64];
923   avpInfo *probe;
924   gchar *vendorName=NULL;
925
926   if (vendorId)
927         vendorName = diameter_vendor_to_str(vendorId, FALSE);
928
929   for (probe=avpListHead; probe; probe=probe->next) {
930         if (avpCode == probe->code) {
931           if (vendorId) {
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)))
937                   /* We found it! */
938                   return probe->name;
939           } else {
940                 /* No Vendor ID -- vendorName should be null */
941                 if (!probe->vendorName)
942                   /* We found it! */
943                   return probe->name;
944           }
945         }
946   }
947
948   g_warning("Diameter: Unable to find name for AVP 0x%08x, Vendor %u!", 
949                         avpCode, vendorId);
950
951   /* If we don't find it, build a name string */
952   sprintf(buffer, "Unknown AVP:0x%08x", avpCode);
953   return buffer;
954 } /* diameter_avp_get_name */
955 static gchar *
956 diameter_avp_get_value(guint32 avpCode, guint32 vendorId, guint32 avpValue)
957 {
958   static gchar buffer[64];
959
960   avpInfo *probe;
961   gchar *vendorName=NULL;
962
963   if (vendorId)
964         vendorName = diameter_vendor_to_str(vendorId, FALSE);
965
966   for (probe=avpListHead; probe; probe=probe->next) {
967         if (avpCode == probe->code) {
968           if (vendorId) {
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))) {
974                   ValueName *vprobe;
975                   for(vprobe=probe->values; vprobe; vprobe=vprobe->next) {
976                         if (avpValue == vprobe->value) {
977                           return vprobe->name;
978                         }
979                   }
980                   sprintf(buffer, "Unknown Value: 0x%08x", avpValue);
981                   return buffer;
982                 }
983           } else {
984                 if (!probe->vendorName) {
985                   ValueName *vprobe;
986                   for(vprobe=probe->values; vprobe; vprobe=vprobe->next) {
987                         if (avpValue == vprobe->value) {
988                           return vprobe->name;
989                         }
990                   }
991                   sprintf(buffer, "Unknown Value: 0x%08x", avpValue);
992                   return buffer;
993                 }
994           }
995         }
996   }
997   /* If we don't find the avp, build a value string */
998   sprintf(buffer, "Unknown AVP! Value: 0x%08x", avpValue);
999   return buffer;
1000 } /* diameter_avp_get_value */
1001
1002
1003 /* Code to actually dissect the packets */
1004
1005 /*
1006  * Main dissector
1007  */
1008 static guint32 dissect_diameter_common(tvbuff_t *tvb, size_t start, packet_info *pinfo,
1009                                                                            proto_tree *tree)
1010 {
1011
1012   /* Set up structures needed to add the protocol subtree and manage it */
1013   proto_item      *ti;
1014   proto_item      *tf;
1015   proto_tree      *flags_tree;
1016   tvbuff_t        *avp_tvb;
1017   proto_tree      *diameter_tree;
1018   e_diameterhdr    dh;
1019   size_t           offset=0;
1020   size_t           avplength;
1021   proto_tree      *avp_tree;
1022   proto_item      *avptf;
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];
1029   gint        i;
1030   guint      bpos;
1031   static  int initialized=FALSE;
1032
1033   /* set our offset */
1034   offset=start;
1035
1036   /*
1037    * Only parse in dictionary if there are diameter packets to
1038    * dissect.
1039    */
1040   if (!initialized) {
1041           /* Read in our dictionary, if it exists. */
1042           initializeDictionary();
1043           initialized=TRUE;
1044   }
1045         
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);
1051         
1052   /* Copy our header */
1053   tvb_memcpy(tvb, (guint8*) &dh, offset, sizeof(dh));
1054         
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);
1061
1062   if (dh.vendorId) {
1063         strcpy(vendorName, 
1064                    diameter_vendor_to_str(dh.vendorId, TRUE));
1065   } else {
1066         strcpy(vendorName, "None");
1067   }
1068
1069
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);
1075
1076   /* Set up our flags */
1077   if (check_col(pinfo->cinfo, COL_INFO) || tree) {  
1078         flagstr[0]=0;
1079         for (i = 0; i < 8; i++) {
1080           bpos = 1 << i;
1081           if (flags & bpos) {
1082                 if (flagstr[0]) {
1083                   strcat(flagstr, ", ");
1084                 }
1085                 strcat(flagstr, fstr[i]);
1086           }
1087         }
1088         if (strlen(flagstr) == 0) {
1089           strcpy(flagstr,"<None>");
1090         }
1091   }
1092   
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");
1097   else
1098         strcat(commandString, "-Answer");
1099
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);
1104         BadPacket = TRUE;
1105   }
1106
1107   /* And, check our reserved flags/version */
1108   if ((flags & DIAM_FLAGS_RESERVED) ||
1109           (version != 1)) {
1110         g_warning("Diameter: Bad packet: Bad Flags(0x%x) or Version(%u)",
1111                           flags, version);
1112         BadPacket = TRUE;
1113   }
1114
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":"",
1121                                  ((BadPacket ||
1122                                    (flags & (DIAM_FLAGS_P|DIAM_FLAGS_E))) ?
1123                                    ": " : ""),
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);
1129   }
1130         
1131
1132   /* In the interest of speed, if "tree" is NULL, don't do any work not
1133          necessary to generate protocol tree items. */
1134   if (tree) {
1135
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);
1140
1141         /* Version */
1142         proto_tree_add_uint(diameter_tree,
1143                                                 hf_diameter_version,
1144                                                 tvb, offset, 1,
1145                                                 version);
1146
1147         offset+=1;
1148
1149         /* Length */
1150         proto_tree_add_uint(diameter_tree,
1151                                                 hf_diameter_length, tvb,
1152                                                 offset, 3, pktLength);
1153         offset += 3;
1154
1155         /* Flags */
1156         tf = proto_tree_add_uint_format(diameter_tree, hf_diameter_flags, tvb,
1157                                                                         offset , 1, flags, "Flags: 0x%02x (%s)", flags,
1158                                                                         flagstr);
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);
1168
1169         offset += 1;
1170
1171         /* Command Code */
1172         proto_tree_add_uint_format(diameter_tree, hf_diameter_code,
1173                                                            tvb, offset, 3, commandCode, "Command Code: %s", commandString);
1174         offset += 3;
1175
1176         /* Vendor Id */
1177         proto_tree_add_uint_format(diameter_tree,hf_diameter_vendor_id,
1178                                                            tvb, offset, 4,      dh.vendorId, "Vendor-Id: %s", vendorName);
1179         offset += 4;
1180
1181         /* Hop-by-hop Identifier */
1182         proto_tree_add_uint(diameter_tree, hf_diameter_hopbyhopid,
1183                                                 tvb, offset, 4, dh.hopByHopId);
1184         offset += 4;
1185
1186         /* End-to-end Identifier */
1187         proto_tree_add_uint(diameter_tree, hf_diameter_endtoendid,
1188                                                 tvb, offset, 4, dh.endToEndId);
1189         offset += 4;
1190
1191         /* If we have a bad packet, don't bother trying to parse the AVPs */
1192         if (BadPacket) {
1193           return (offset + MAX(pktLength,MIN_DIAMETER_SIZE));
1194         }
1195
1196         /* Start looking at the AVPS */
1197         /* Make the next tvbuff */
1198
1199         /* Update the lengths */
1200         avplength= pktLength - sizeof(e_diameterhdr);
1201     
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");
1206                 
1207         avp_tree = proto_item_add_subtree(avptf,
1208                                                                           ett_diameter_avp);
1209         if (avp_tree != NULL) {
1210           dissect_avps( avp_tvb, pinfo, avp_tree);
1211         }
1212         return MAX((offset + avplength), MIN_DIAMETER_SIZE);
1213   }
1214   return (offset + MAX(pktLength, MIN_DIAMETER_SIZE));
1215
1216 } /* dissect_diameter_common */
1217
1218 static void
1219 dissect_diameter_sctp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1220 {
1221         dissect_diameter_common(tvb, 0, pinfo, tree);
1222 }
1223
1224 static void
1225 dissect_diameter_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1226 {
1227         size_t offset = 0;
1228         guint32 plen;
1229         guint32 available_bytes;
1230 /*      guint32 noffset; */
1231         
1232         /* Loop through the packet, dissecting multiple diameter messages */
1233         do {
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",
1237                                           available_bytes);
1238                         return; /* Bail.  We can't even get our length */
1239                 }
1240
1241                 /* get our packet length */
1242                 plen = tvb_get_ntohl(tvb, offset);
1243                 plen &= 0x00ffffff; /* get rid of the flags */
1244
1245                 /*Desegmentation */
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); */
1253                                 return;
1254                         }
1255                 }
1256         
1257                 /* Otherwise, dissect our packet */
1258                 offset = dissect_diameter_common(tvb, offset, pinfo, tree);
1259
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));
1264
1265 } /* dissect_diameter_tcp */
1266
1267 /*
1268  * Call the mip_dissector, after saving our pinfo variables
1269  * so it doesn't write to our column display.
1270  */
1271 static void
1272 safe_dissect_mip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1273                                  size_t offset, size_t length)
1274 {
1275   static dissector_handle_t mip_handle;
1276   static int mipInitialized=FALSE;
1277   tvbuff_t *mip_tvb;
1278   address save_dl_src;
1279   address save_dl_dst;
1280   address save_net_src;
1281   address save_net_dst;
1282   address save_src;
1283   address save_dst;
1284   gboolean save_in_error_pkt;
1285
1286   if (!mipInitialized) {
1287         mip_handle = find_dissector("mip");
1288         mipInitialized=TRUE;
1289   }
1290
1291   mip_tvb = tvb_new_subset(tvb, offset,
1292                                                    MIN(length, tvb_length(tvb)-offset),
1293                                                    length);
1294         
1295   /* The contained packet is a MIP registration request;
1296          dissect it with the MIP dissector. */
1297   col_set_writable(pinfo->cinfo, FALSE);
1298
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;
1310
1311   call_dissector(mip_handle, mip_tvb, pinfo, tree);
1312
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;
1321
1322
1323 } /* safe_dissect_mip */
1324
1325 /*
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
1328  */
1329 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *avp_tree)
1330 {
1331   /* adds the attribute value pairs to the tree */
1332   e_avphdr avph;
1333   gchar avpTypeString[64];
1334   gchar avpNameString[64];
1335   gchar *valstr;
1336   guint32 vendorId=0;
1337   gchar    vendorName[64];
1338   int hdrLength;
1339   int fixAmt;
1340   proto_tree *avpi_tree;
1341   size_t offset = 0;
1342   tvbuff_t        *group_tvb;
1343   proto_tree *group_tree;
1344   proto_item *grouptf;
1345   proto_item *avptf;
1346   char buffer[1024];
1347   int BadPacket = FALSE;
1348   guint32 avpLength;
1349   guint8 flags;
1350   proto_item      *tf;
1351   proto_tree      *flags_tree;
1352         
1353   gint32 packetLength;
1354   size_t avpDataLength;
1355   int avpType;
1356   gchar flagstr[64] = "<None>";
1357   gchar *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Protected", "Mandatory", "Vendor-Specific" };
1358   gint        i;
1359   guint      bpos;
1360
1361   packetLength = tvb_length(tvb);
1362
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");
1367         return;
1368   }
1369
1370   /* Spin around until we run out of packet */
1371   while (packetLength > 0 ) {
1372
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);
1377           BadPacket = TRUE;
1378           /* Don't even bother trying to parse a short packet. */
1379           return;
1380         }
1381         
1382         /* Copy our header */
1383         tvb_memcpy(tvb, (guint8*) &avph, offset, MIN((long)sizeof(avph),packetLength));
1384         
1385         /* Fix the byte ordering */
1386         avph.avp_code = ntohl(avph.avp_code);
1387         avph.avp_flagsLength = ntohl(avph.avp_flagsLength);
1388         
1389         flags = (avph.avp_flagsLength & 0xff000000) >> 24;
1390         avpLength = avph.avp_flagsLength & 0x00ffffff;
1391         
1392         /* Set up our flags string */
1393         if (check_col(pinfo->cinfo, COL_INFO) || avp_tree) {  
1394           flagstr[0]=0;
1395           for (i = 0; i < 8; i++) {
1396                 bpos = 1 << i;
1397                 if (flags & bpos) {
1398                   if (flagstr[0]) {
1399                         strcat(flagstr, ", ");
1400                   }
1401                   strcat(flagstr, fstr[i]);
1402                 }
1403           }
1404           if (strlen(flagstr) == 0) {
1405                 strcpy(flagstr,"<None>");
1406           }
1407         }
1408
1409         /* Dissect our vendor id if it exists  and set hdr length */
1410         if (flags & AVP_FLAGS_V) {
1411           vendorId = ntohl(avph.avp_vendorId);
1412           /* Vendor id */
1413           hdrLength = sizeof(e_avphdr);
1414         } else {
1415           /* No vendor */
1416           hdrLength = sizeof(e_avphdr) - 
1417                 sizeof(guint32);
1418           vendorId = 0;
1419         }
1420
1421         if (vendorId) {
1422           strcpy(vendorName, 
1423                          diameter_vendor_to_str(vendorId, TRUE));
1424         } else {
1425           vendorName[0]='\0';
1426         }
1427
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,
1434                                 packetLength);
1435           BadPacket = TRUE;
1436         }
1437
1438         /* Check for bad flags */
1439         if (flags & AVP_FLAGS_RESERVED) {
1440           g_warning("Diameter: Invalid AVP: Reserved bit set.  flags = 0x%x,"
1441                                 " resFl=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; */
1445         }
1446                 
1447         /*
1448          * Compute amount of byte-alignment fix (Diameter AVPs are sent on 4 byte
1449          * boundries)
1450          */
1451         fixAmt = 4 - (avpLength % 4);
1452         if (fixAmt == 4) fixAmt = 0;
1453         
1454         /* shrink our packetLength */
1455         packetLength = packetLength - (avpLength + fixAmt);
1456         
1457         /* Check for out of bounds */
1458         if (packetLength < 0) {
1459           g_warning("Diameter: Bad AVP: Bad new length (%d bytes) ",
1460                                 packetLength);
1461           BadPacket = TRUE;
1462         }
1463
1464         /* Make avp Name & type */
1465         strcpy(avpTypeString, val_to_str(diameter_avp_get_type(avph.avp_code,vendorId),
1466                                                                          TypeValues, 
1467                                                                          "Unknown-Type: 0x%08x"));
1468         strcpy(avpNameString, diameter_avp_get_name(avph.avp_code, vendorId));
1469
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);
1477
1478         if (avpi_tree !=NULL) {
1479           /* Command Code */
1480           proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_code,
1481                                                                  tvb, offset, 4, avph.avp_code, "AVP Code: %s", avpNameString);
1482           offset += 4;
1483                 
1484           tf = proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_flags, tvb,
1485                                                                           offset , 1, flags, "Flags: 0x%02x (%s)", flags,
1486                                                                           flagstr);
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);
1496           offset += 1;
1497
1498           proto_tree_add_uint(avpi_tree, hf_diameter_avp_length,
1499                                                   tvb, offset, 3, avpLength);
1500           offset += 3;
1501
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);
1505                 offset += 4;
1506           }
1507
1508           avpDataLength = avpLength - hdrLength;
1509
1510           /*
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.
1513            */
1514           if (BadPacket) {
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)");
1520                 return;
1521           }
1522
1523           avpType=diameter_avp_get_type(avph.avp_code,vendorId);
1524
1525           switch(avpType) {
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),
1531                                                                           buffer);
1532                                 
1533                 group_tree = proto_item_add_subtree(grouptf,
1534                                                                                         ett_diameter_avp);
1535
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);
1540                 }
1541                 break;
1542
1543           case DIAMETER_IDENTITY:
1544                 {
1545                   const guint8 *data;
1546
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,
1550                                                "Identity: %*.*s",
1551                                                (int)avpDataLength,
1552                                                (int)avpDataLength, data);
1553                 }
1554                 break;
1555           case DIAMETER_UTF8STRING:
1556                 {
1557                   const guint8 *data;
1558
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",
1563                                                (int)avpDataLength,
1564                                                (int)avpDataLength, data);
1565                 }
1566                 break;
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);
1574                 } else {
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");
1579                 }
1580                 break;
1581
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);
1586                 } else {
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");
1591                 }
1592                 break;
1593
1594           case DIAMETER_UNSIGNED32:
1595                 if (avpDataLength == 4) {
1596                   guint32 data;
1597
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);
1602                 } else {
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");
1607                 }
1608                 break;
1609
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);
1614                 } else {
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");
1619                 }
1620                 break;
1621
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);
1626                 } else {
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");
1631                 }
1632                 break;
1633
1634           case DIAMETER_TIME:
1635                 if (avpDataLength == 4) {
1636                   nstime_t data;
1637                   gchar buffer[64];
1638                   struct tm *ltp;
1639
1640                   data.secs = tvb_get_ntohl(tvb, offset);
1641                   data.secs -= NTP_TIME_DIFF;
1642                   data.nsecs = 0;
1643
1644                   ltp = localtime(&data.secs);
1645                   strftime(buffer, 64, 
1646                            "%a, %d %b %Y %H:%M:%S %z", ltp);
1647
1648                   proto_tree_add_time_format(avpi_tree, hf_diameter_avp_data_time,
1649                                              tvb, offset, avpDataLength, &data,
1650                                              "Time: %s", buffer);
1651                 } else {
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");
1656                 }
1657                 break;
1658
1659           case DIAMETER_ENUMERATED:
1660                 if (avpDataLength == 4) {
1661                   guint32 data;
1662                   
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,
1668                                              data, valstr);
1669                 } else {
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");
1674                 }
1675                 break;
1676           case DIAMETER_VENDOR_ID:
1677                 if (avpDataLength == 4) {
1678                   guint32 data;
1679                   
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,
1685                                              data);
1686                 } else {
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");
1691                 }
1692                 break;
1693           case DIAMETER_APPLICATION_ID:
1694                 if (avpDataLength == 4) {
1695                   guint32 data;
1696                   
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)",
1702                                              valstr, data);
1703                 } else {
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");
1708                 }
1709                 break;
1710           case DIAMETER_MIP_REG_REQ:
1711                 safe_dissect_mip(tvb, pinfo, avpi_tree, offset, avpDataLength);
1712                 break;
1713
1714           default:
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");
1720                 break;
1721                                 
1722           } /* switch type */
1723         } /* avpi_tree != null */
1724         offset += (avpLength - hdrLength);
1725         offset += fixAmt; /* fix byte alignment */
1726   } /* loop */
1727 } /* dissect_avps */
1728
1729
1730
1731 void
1732 proto_reg_handoff_diameter(void)
1733 {
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;
1739
1740   if (!Initialized) {
1741         diameter_tcp_handle = create_dissector_handle(dissect_diameter_tcp,
1742             proto_diameter);
1743         diameter_sctp_handle = create_dissector_handle(dissect_diameter_sctp,
1744             proto_diameter);
1745         Initialized=TRUE;
1746   } else {
1747         dissector_delete("tcp.port", TcpPort, diameter_tcp_handle);
1748         dissector_delete("sctp.port", SctpPort, diameter_sctp_handle);
1749   }
1750
1751   /* set port for future deletes */
1752   TcpPort=gbl_diameterTcpPort;
1753   SctpPort=gbl_diameterSctpPort;
1754
1755   strcpy(gbl_diameterString, "Diameter Protocol");
1756
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);
1761 }
1762
1763 /* registration with the filtering engine */
1764 void
1765 proto_register_diameter(void)
1766 {
1767         static hf_register_info hf[] = {
1768                 { &hf_diameter_version,
1769                   { "Version", "diameter.version", FT_UINT8, BASE_HEX, NULL, 0x00,
1770                     "", HFILL }},
1771                 { &hf_diameter_length,
1772                   { "Length","diameter.length", FT_UINT24, BASE_DEC, NULL, 0x0,
1773                     "", HFILL }},
1774                 
1775                 { &hf_diameter_flags,
1776                   { "Flags", "diameter.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
1777                     "", HFILL }},
1778                 { &hf_diameter_flags_request,
1779                   { "Request", "diameter.flags.request", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_R,
1780                         "", HFILL }},
1781                 { &hf_diameter_flags_proxyable,
1782                   { "Proxyable", "diameter.flags.proxyable", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_P,
1783                         "", HFILL }},
1784                 { &hf_diameter_flags_error,
1785                   { "Error","diameter.flags.error", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_E,
1786                         "", HFILL }},
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 }},
1802
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,
1808                         0x0,"", HFILL }},
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 }},
1815                 
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 }},
1822
1823
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,
1829                         "", HFILL }},
1830                 { &hf_diameter_avp_flags_mandatory,
1831                   { "Mandatory", "diameter.flags.mandatory", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_M,
1832                         "", HFILL }},
1833                 { &hf_diameter_avp_flags_protected,
1834                   { "Protected","diameter.avp.flags.protected", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_P,
1835                         "", HFILL }},
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 }},
1881
1882         };
1883         static gint *ett[] = {
1884                 &ett_diameter,
1885                 &ett_diameter_flags,
1886                 &ett_diameter_avp,
1887                 &ett_diameter_avp_flags,
1888                 &ett_diameter_avpinfo
1889         };
1890         module_t *diameter_module;
1891
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));
1896
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",
1903                                                                    10,
1904                                                                    &gbl_diameterTcpPort);
1905         prefs_register_uint_preference(diameter_module, "sctp.port",
1906                                                                    "Diameter SCTP Port",
1907                                                                    "Set the SCTP port for Diameter messages",
1908                                                                    10,
1909                                                                    &gbl_diameterSctpPort);
1910         /*
1911          * Build our default dictionary filename
1912          */
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 );
1918         }
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);
1924
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);
1930
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 */