Include "conversation.h", as the plugin API now includes the routines to
[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.33 2001/11/04 02:50:19 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 <filesystem.h>
46 #include "xmlstub.h"
47 #include "packet.h"
48 #include "resolv.h"
49 #include "prefs.h"
50
51 /* This must be defined before we include packet-diameter-defs.h */
52
53 /* Valid data types */
54 typedef enum {
55   /* Base Types */
56   DIAMETER_OCTET_STRING = 1,
57   DIAMETER_INTEGER32,
58   DIAMETER_INTEGER64,
59   DIAMETER_UNSIGNED32,
60   DIAMETER_UNSIGNED64,
61   DIAMETER_FLOAT32,
62   DIAMETER_FLOAT64,
63   DIAMETER_FLOAT128,
64   DIAMETER_GROUPED,
65
66   /* Derived Types */
67   DIAMETER_IP_ADDRESS,         /* OctetString */
68   DIAMETER_TIME,               /* Integer 32 */
69   DIAMETER_UTF8STRING,         /* OctetString */
70   DIAMETER_IDENTITY,           /* OctetString */
71   DIAMETER_ENUMERATED,         /* Integer 32 */
72   DIAMETER_IP_FILTER_RULE,     /* OctetString */
73   DIAMETER_QOS_FILTER_RULE,    /* OctetString */
74   DIAMETER_MIP_REG_REQ,        /* OctetString */
75   DIAMETER_VENDOR_ID,           /* Integer32  */
76   DIAMETER_APPLICATION_ID
77   
78 } diameterDataType;
79
80
81 static value_string TypeValues[]={
82   {  DIAMETER_OCTET_STRING,    "OctetString" },
83   {  DIAMETER_INTEGER32,       "Integer32" },
84   {  DIAMETER_INTEGER64,       "Integer64" },
85   {  DIAMETER_UNSIGNED32,      "Unsigned32" },
86   {  DIAMETER_UNSIGNED64,      "Unsigned64" },
87   {  DIAMETER_FLOAT32,         "Float32" },
88   {  DIAMETER_FLOAT64,         "Float64" },
89   {  DIAMETER_FLOAT128,        "Float128" },
90   {  DIAMETER_GROUPED,         "Grouped" },
91   {  DIAMETER_IP_ADDRESS,      "IpAddress" },
92   {  DIAMETER_TIME,            "Time" },
93   {  DIAMETER_UTF8STRING,      "UTF8String" },
94   {  DIAMETER_IDENTITY,        "DiameterIdentity" },
95   {  DIAMETER_ENUMERATED,      "Enumerated" },
96   {  DIAMETER_IP_FILTER_RULE,  "IPFilterRule" },
97   {  DIAMETER_QOS_FILTER_RULE, "QOSFilterRule" },
98   {  DIAMETER_MIP_REG_REQ,     "MIPRegistrationRequest"},
99   {  DIAMETER_VENDOR_ID,       "VendorId"},
100   {  DIAMETER_APPLICATION_ID,  "AppId"},
101   {0, (char *)NULL}
102 };
103
104 typedef struct value_name {
105   guint32            value;
106   gchar             *name;
107   struct value_name *next;
108 } ValueName;
109
110 typedef struct old_avp_info {
111   guint32           code;
112   gchar            *name;
113   diameterDataType  type;
114   value_string     *values;
115 } oldAvpInfo;
116
117 typedef struct avp_info {
118   guint32           code;
119   gchar            *name;
120   gchar            *vendorName;
121   diameterDataType  type;
122   ValueName        *values;
123   struct avp_info  *next;
124 } avpInfo;
125
126 typedef struct command_code {
127   guint32              code;
128   gchar               *name;
129   gchar               *vendorString;
130   struct command_code *next;
131 } CommandCode;
132
133 typedef struct vendor_id {
134   guint32              id;
135   gchar               *name;
136   gchar               *longName;
137   struct vendor_id    *next;
138 } VendorId;
139
140 typedef struct application_id {
141   guint32              id;
142   gchar               *name;
143   struct application_id    *next;
144 } ApplicationId;
145
146 static avpInfo         *avpListHead=NULL;
147 static VendorId        *vendorListHead=NULL;
148 static CommandCode     *commandListHead=NULL;
149 static ApplicationId   *ApplicationIdHead=NULL;
150
151
152 #include "packet-diameter-defs.h"
153
154 #define  NTP_TIME_DIFF                   (2208988800UL)
155
156 #define TCP_PORT_DIAMETER       1812
157 #define SCTP_PORT_DIAMETER      1812
158
159 static const true_false_string flags_set_truth = {
160   "Set",
161   "Not set"
162 };
163
164 static const true_false_string reserved_set = {
165   "*** Error! Reserved Bit is Set",
166   "Ok"
167 };
168 static int proto_diameter = -1;
169 static int hf_diameter_length = -1;
170 static int hf_diameter_code = -1;
171 static int hf_diameter_hopbyhopid =-1;
172 static int hf_diameter_endtoendid =-1;
173 static int hf_diameter_reserved = -1;
174 static int hf_diameter_version = -1;
175 static int hf_diameter_vendor_id = -1;
176 static int hf_diameter_flags = -1;
177 static int hf_diameter_flags_request = -1;
178 static int hf_diameter_flags_proxyable = -1;
179 static int hf_diameter_flags_error = -1;
180 static int hf_diameter_flags_reserved3 = -1;
181 static int hf_diameter_flags_reserved4 = -1;
182 static int hf_diameter_flags_reserved5 = -1;
183 static int hf_diameter_flags_reserved6 = -1;
184 static int hf_diameter_flags_reserved7 = -1;
185
186 static int hf_diameter_avp_code = -1;
187 static int hf_diameter_avp_length = -1;
188 static int hf_diameter_avp_reserved = -1;
189 static int hf_diameter_avp_flags = -1;
190 static int hf_diameter_avp_flags_vendor_specific = -1;
191 static int hf_diameter_avp_flags_mandatory = -1;
192 static int hf_diameter_avp_flags_protected = -1;
193 static int hf_diameter_avp_flags_reserved3 = -1;
194 static int hf_diameter_avp_flags_reserved4 = -1;
195 static int hf_diameter_avp_flags_reserved5 = -1;
196 static int hf_diameter_avp_flags_reserved6 = -1;
197 static int hf_diameter_avp_flags_reserved7 = -1;
198 static int hf_diameter_avp_vendor_id = -1;
199
200
201 static int hf_diameter_avp_data_uint32 = -1;
202 static int hf_diameter_avp_data_int32 = -1;
203 static int hf_diameter_avp_data_uint64 = -1;
204 static int hf_diameter_avp_data_int64 = -1;
205 static int hf_diameter_avp_data_bytes = -1;
206 static int hf_diameter_avp_data_string = -1;
207 static int hf_diameter_avp_data_v4addr = -1;
208 static int hf_diameter_avp_data_v6addr = -1;
209 static int hf_diameter_avp_data_time = -1;
210
211 static gint ett_diameter = -1;
212 static gint ett_diameter_flags = -1;
213 static gint ett_diameter_avp = -1;
214 static gint ett_diameter_avp_flags = -1;
215 static gint ett_diameter_avpinfo = -1;
216
217 static char gbl_diameterString[200];
218 static int gbl_diameterTcpPort=TCP_PORT_DIAMETER;
219 static int gbl_diameterSctpPort=SCTP_PORT_DIAMETER;
220
221 /* desegmentation of Diameter over TCP */
222 static gboolean gbl_diameter_desegment = TRUE;
223
224 #define DIAMETER_DIR "diameter"
225 #define DICT_FN "dictionary.xml"
226 static gchar *gbl_diameterDictionary = NULL;
227
228 typedef struct _e_diameterhdr {
229   guint32  versionLength;
230   guint32  flagsCmdCode;
231   guint32  vendorId;
232   guint32  hopByHopId;
233   guint32  endToEndId;
234 } e_diameterhdr;
235
236 typedef struct _e_avphdr {
237   guint32 avp_code;
238   guint32 avp_flagsLength;
239   guint32 avp_vendorId;           /* optional */
240 } e_avphdr;
241
242 /* Diameter Header Flags */
243 /*                                      RPrrrrrrCCCCCCCCCCCCCCCCCCCCCCCC  */
244 #define DIAM_FLAGS_R 0x80
245 #define DIAM_FLAGS_P 0x40
246 #define DIAM_FLAGS_E 0x20
247 #define DIAM_FLAGS_RESERVED3 0x10
248 #define DIAM_FLAGS_RESERVED4 0x08
249 #define DIAM_FLAGS_RESERVED5 0x04
250 #define DIAM_FLAGS_RESERVED6 0x02
251 #define DIAM_FLAGS_RESERVED7 0x01
252 #define DIAM_FLAGS_RESERVED  0x1f
253
254 #define DIAM_LENGTH_MASK  0x00ffffffl
255 #define DIAM_COMMAND_MASK DIAM_LENGTH_MASK
256 #define DIAM_GET_FLAGS(dh)                ((dh.flagsCmdCode & ~DIAM_COMMAND_MASK) >> 24)
257 #define DIAM_GET_VERSION(dh)              ((dh.versionLength & (~DIAM_LENGTH_MASK)) >> 24)
258 #define DIAM_GET_COMMAND(dh)              (dh.flagsCmdCode & DIAM_COMMAND_MASK)
259 #define DIAM_GET_LENGTH(dh)               (dh.versionLength & DIAM_LENGTH_MASK)
260
261 /* Diameter AVP Flags */
262 #define AVP_FLAGS_P 0x20
263 #define AVP_FLAGS_V 0x80
264 #define AVP_FLAGS_M 0x40
265 #define AVP_FLAGS_RESERVED3 0x10
266 #define AVP_FLAGS_RESERVED4 0x08
267 #define AVP_FLAGS_RESERVED5 0x04
268 #define AVP_FLAGS_RESERVED6 0x02
269 #define AVP_FLAGS_RESERVED7 0x01
270 #define AVP_FLAGS_RESERVED 0x1f          /* 00011111  -- V M P X X X X X */
271
272 #define MIN_AVP_SIZE (sizeof(e_avphdr) - sizeof(guint32))
273 #define MIN_DIAMETER_SIZE (sizeof(e_diameterhdr))
274
275 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
276
277
278 /*
279  * This routine will do a push-parse of the passed in
280  * filename.  This was taken almost verbatum from
281  * the xmlsoft examples.
282  */
283 static xmlDocPtr
284 xmlParseFilePush( char *filename, int checkValid) {
285   FILE *f;
286   xmlDocPtr doc=NULL;
287   int valid=0;
288   int res, size = 1024;
289   char chars[1024];
290   xmlParserCtxtPtr ctxt;
291         
292   /* I wonder what kind of a performance hit this is? */
293   *XmlStub.xmlDoValidityCheckingDefaultValue = checkValid;
294   
295   f = fopen(filename, "r");
296   if (f == NULL) {
297         g_warning("Diameter: Unable to open %s", filename);
298         return NULL;
299   }
300
301   res = fread(chars, 1, 4, f);
302   if (res > 0) {
303         ctxt = XmlStub.xmlCreatePushParserCtxt(NULL, NULL,
304                                                                                    chars, res, filename);
305         while ((res = fread(chars, 1, size-1, f)) > 0) {
306           XmlStub.xmlParseChunk(ctxt, chars, res, 0);
307         }
308         XmlStub.xmlParseChunk(ctxt, chars, 0, 1);
309         doc = ctxt->myDoc;
310         valid=ctxt->valid;
311         XmlStub.xmlFreeParserCtxt(ctxt);
312   }
313   fclose(f); 
314
315   /* Check valid */
316   if (!valid) {
317         g_warning( "Error!  Invalid xml in %s!  Failed DTD check!",
318                            filename);
319         return NULL;
320   }
321   return doc;
322 } /* xmlParseFilePush */
323
324 /*
325  * This routine will add a static avp to the avp list.  It is
326  * only called when the XML dictionary fails to load properly.
327  */
328 static int
329 addStaticAVP(int code, gchar *name, diameterDataType type, value_string *values)
330 {
331   avpInfo *entry;
332   ValueName *vEntry=NULL;
333   int i;
334
335   /* Parse our values array, if we have one */
336   if (values) {
337         for (i=0; values[i].strptr != NULL; i++) {
338           char *valueName=NULL, *valueCode=NULL;
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(xmlDocPtr doc, 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->vendorString = g_strdup(vendorId);
496   else
497         entry->vendorString = NULL;
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(xmlDocPtr doc, xmlNodePtr cur)
512 {
513   guint32 vendorId = 0;
514   char *name, *code, *vendorIdString;
515
516   /*
517    * Get the Attributes
518    */
519   name = XmlStub.xmlGetProp(cur, "name");
520   code = XmlStub.xmlGetProp(cur, "code");
521   if (!name || !code) {
522         g_warning("Invalid command.  Name or code missing!");
523         return -1;
524   }
525   vendorIdString = XmlStub.xmlGetProp(cur, "vendor-id");
526
527   if (!vendorIdString || !strcasecmp(vendorIdString, "None")) {
528         vendorIdString = NULL;
529   }
530
531   return (addCommand(atoi(code), name, vendorIdString));
532 } /* xmlParseCommand */
533
534 /* This routine adds an application to the name<-> id table */
535 static int
536 dictionaryAddApplication(char *name, int id)
537 {
538   ApplicationId *entry;
539
540   if (!name || (id <= 0)) {
541         g_warning( "Diameter Error: Inavlid application (name=%p, id=%d)",
542                            name, id);
543         return (-1);
544   } /* Sanity Checks */
545
546   entry = g_malloc(sizeof(ApplicationId));
547   if (!entry) {
548         g_warning( "Unable to allocate memory");
549         return (-1);
550   }
551         
552   entry->name = g_strdup(name);
553   entry->id = id;
554         
555   /* Add it to the list */
556   entry->next = ApplicationIdHead;
557   ApplicationIdHead = entry;
558
559   return 0;
560 } /* dictionaryAddApplication */
561
562 /*
563  * This routine will add a vendor to the vendors list
564  */
565 static int
566 addVendor(int id, gchar *name, gchar *longName)
567 {
568   VendorId *vendor;
569
570   /* add entry */
571   vendor=g_malloc(sizeof(VendorId));
572   if (!vendor) {
573         return (-1);
574   }
575
576   vendor->id = id;
577   vendor->name = g_strdup(name);
578   vendor->longName = g_strdup(longName);
579   vendor->next = vendorListHead;
580   vendorListHead = vendor;
581
582   return 0;
583 } /* addVendor */
584
585 /*
586  * This routine will pars in a XML vendor entry.
587  */
588 static int
589 xmlParseVendor(xmlDocPtr doc, xmlNodePtr cur)
590 {
591   char *name=NULL, *code=NULL, *id=NULL;
592
593   /* First, get our properties */
594   id = XmlStub.xmlGetProp(cur, "vendor-id");
595   name = XmlStub.xmlGetProp(cur, "name");
596   code = XmlStub.xmlGetProp(cur, "code");
597
598   if (!id || !name || !code) {
599         g_warning( "Invalid vendor section.  vendor-id, name, and code must be specified");
600         return -1;
601   }
602
603   return (addVendor(atoi(code), id, name));
604 } /* addVendor */
605
606 /*
607  * This routine will either parse in the base protocol, or an application.
608  */
609 static int
610 xmlDictionaryParseSegment(xmlDocPtr doc, xmlNodePtr cur, int base)
611 {
612   if (!base) {
613         char *name;
614         char *id;
615                 
616         /* Add our application */
617         id = XmlStub.xmlGetProp(cur, "id");
618         name = XmlStub.xmlGetProp(cur, "name");
619                 
620         if (!name || !id) {
621           /* ERROR!!! */
622           g_warning("Diameter: Invalid application!: name=\"%s\", id=\"%s\"",
623                                 name?name:"NULL", id?id:"NULL");
624           return -1;
625         }
626                 
627         /* Add the application */
628         if (dictionaryAddApplication(name, atol(id)) != 0) {
629           /* ERROR! */
630           return -1;
631         }
632   }
633
634         
635   /*
636    * Get segment values
637    */
638   cur = cur->xmlChildrenNode;
639   while (cur != NULL) {
640         if (!strcasecmp((char *)cur->name, "avp")) {
641           /* we have an avp!!! */
642           xmlParseAVP(doc, cur);
643         } else if (!strcasecmp((char *)cur->name, "vendor")) {
644           /* we have a vendor */
645           xmlParseVendor(doc, cur);
646           /* For now, ignore typedefn and text */
647         } else if (!strcasecmp((char *)cur->name, "command")) {
648           /* Found a command */
649           xmlParseCommand(doc,cur);
650         } else if (!strcasecmp((char *)cur->name, "text")) {
651         } else if (!strcasecmp((char *)cur->name, "comment")) {
652         } else if (!strcasecmp((char *)cur->name, "typedefn")) {
653           /* WORK -- parse in valid types . . . */
654         } else {
655           /* IF we got here, we're an error */
656           g_warning("Error!  expecting an avp or a typedefn (got \"%s\")",
657                                 cur->name);
658           return (-1);
659         }
660         cur = cur->next;
661   } /* while */
662   return 0;
663 } /* xmlDictionaryParseSegment */
664
665 /*
666  * The main xml parse routine.  This will walk through an XML 
667  * dictionary that has been parsed by libxml.
668  */
669 static int
670 xmlDictionaryParse(xmlDocPtr doc, xmlNodePtr cur)
671 {
672   /* We should expect a base protocol, followed by multiple applicaitons */
673   while (cur != NULL) {
674         if (!strcasecmp((char *)cur->name, "base")) {
675           /* Base protocol.  Descend and parse */
676           xmlDictionaryParseSegment(doc, cur, 1);
677         } else if (!strcasecmp((char *)cur->name, "application")) {
678           /* Application.  Descend and parse */
679           xmlDictionaryParseSegment(doc, cur, 0);
680         } else if (!strcasecmp((char *)cur->name, "text")) {
681           /* Ignore text */
682         } else {
683           g_warning( "Diameter: XML Expecting a base or an application  (got \"%s\")",
684                                  cur->name);
685           return (-1);
686         }
687         cur = cur->next;
688   }
689
690   return 0;
691
692 } /* xmlDictionaryParse */
693
694 /*
695  * This routine will call libxml to parse in the dictionary.
696  */
697 static int
698 loadXMLDictionary()
699 {
700   xmlDocPtr doc;
701   xmlNodePtr cur;
702
703   /*
704    * build an XML tree from a the file;
705    */
706   XmlStub.xmlKeepBlanksDefault(0);                    /* Strip leading and trailing blanks */
707   XmlStub.xmlSubstituteEntitiesDefault(1);            /* Substitute entities automagically */
708   doc = xmlParseFilePush(gbl_diameterDictionary, 1);  /* Parse the XML (do validity checks)*/
709
710   /* Check for invalid xml */
711   if (doc == NULL) {
712         g_warning("Diameter: Unable to parse xmldictionary %s",
713                           gbl_diameterDictionary);
714         return -1;
715   }
716         
717   /*
718    * Check the document is of the right kind
719    */
720   cur = XmlStub.xmlDocGetRootElement(doc);
721   if (cur == NULL) {
722         g_warning("Diameter: Error: \"%s\": empty document",
723                           gbl_diameterDictionary);
724         XmlStub.xmlFreeDoc(doc);
725         return -1;
726   }
727   if (XmlStub.xmlStrcmp(cur->name, (const xmlChar *) "dictionary")) {
728         g_warning("Diameter: Error: \"%s\": document of the wrong type, root node != dictionary",
729                           gbl_diameterDictionary);
730         XmlStub.xmlFreeDoc(doc);
731         return -1;
732   }
733         
734   /*
735    * Ok, the dictionary has been parsed by libxml, and is valid.
736    * All we have to do now is read in our information.
737    */
738   if (xmlDictionaryParse(doc, cur->xmlChildrenNode) != 0) {
739         /* Error has already been printed */
740         return -1;
741   }
742
743   /* Once we're done parsing, free up the xml memory */
744   XmlStub.xmlFreeDoc(doc);
745
746   return 0;
747
748 } /* loadXMLDictionary */
749
750 /*
751  * Fallback routine.  In the event of ANY error when loading the XML
752  * dictionary, this routine will populate the new avp list structures
753  * with the old static data from packet-diameter-defs.h
754  */
755 static void
756 initializeDictionaryDefaults()
757 {
758   int i;
759
760   /* Add static vendors to list */
761   for(i=0; diameter_vendor_specific_vendors[i].strptr; i++) {
762         addVendor(diameter_vendor_specific_vendors[i].value,
763                           diameter_vendor_specific_vendors[i].strptr,
764                           diameter_vendor_specific_vendors[i].strptr);
765   }
766   /* Add static commands to list. */
767   for(i=0; diameter_command_code_vals[i].strptr; i++) {
768         addCommand(diameter_command_code_vals[i].value,
769                            diameter_command_code_vals[i].strptr, NULL);
770   }
771
772   /* Add static AVPs to list */
773   for (i=0; old_diameter_avps[i].name; i++) {
774         addStaticAVP(old_diameter_avps[i].code,
775                                  old_diameter_avps[i].name,
776                                  old_diameter_avps[i].type,
777                                  old_diameter_avps[i].values);
778   }
779
780 } /* initializeDictionaryDefaults */
781
782 /* 
783  * This routine will attempt to load the XML dictionary, and on 
784  * failure, will call initializeDictionaryDefaults to load in
785  * our static dictionary.
786  */
787 static void
788 initializeDictionary()
789 {
790   /*
791    * Using ugly ordering here.  If loadLibXML succeeds, then 
792    * loadXMLDictionary will be called.  This is one of the few times when
793    * I think this is prettier than the nested if alternative.
794    */
795   if (loadLibXML() ||
796           (loadXMLDictionary() != 0)) {
797         /* Something failed.  Use the static dictionary */
798         g_warning("Diameter: Using static dictionary! (Unable to use XML)");
799         initializeDictionaryDefaults();
800   }
801 } /* initializeDictionary */
802
803
804
805 /*
806  * These routines manipulate the diameter structures.
807  */
808
809 /* return command string, based on the code */
810 static gchar *
811 diameter_command_to_str(guint32 commandCode)
812 {
813   CommandCode *probe;
814   static gchar buffer[64];
815
816   for (probe=commandListHead; probe; probe=probe->next) {
817         if (commandCode == probe->code) {
818           return probe->name;
819         }
820   }
821
822   snprintf(buffer, sizeof(buffer),
823                    "Cmd-0x%08x", commandCode);
824   return buffer;
825 }/*diameter_command_to_str */
826 /* return vendor string, based on the id */
827 static gchar *
828 diameter_vendor_to_str(guint32 vendorId) {
829   VendorId *probe;
830   static gchar buffer[64];
831
832   for (probe=vendorListHead; probe; probe=probe->next) {
833         if (vendorId == probe->id) {
834           return probe->longName;
835         }
836   }
837
838   snprintf(buffer, sizeof(buffer),
839                    "Vendor 0x%08x", vendorId);
840   return buffer;
841 } /*diameter_vendor_to_str */
842 /* return application string, based on the id */
843 static gchar *
844 diameter_app_to_str(guint32 vendorId) {
845   ApplicationId *probe;
846   static gchar buffer[64];
847
848   for (probe=ApplicationIdHead; probe; probe=probe->next) {
849         if (vendorId == probe->id) {
850           return probe->name;
851         }
852   }
853
854   snprintf(buffer, sizeof(buffer),
855                    "AppId 0x%08x", vendorId);
856   return buffer;
857 } /*diameter_app_to_str */
858
859 /* return an avp type, based on the code */
860 diameterDataType
861 diameter_avp_get_type(guint32 avpCode){
862   avpInfo *probe;
863
864   for (probe=avpListHead; probe; probe=probe->next) {
865         if (avpCode == probe->code) {
866           /* We found it! */
867           return probe->type;
868         }
869   }
870         
871   /* If we don't find it, assume it's data */
872   g_warning("Diameter: Unable to find type for avpCode %d!", avpCode);
873   return DIAMETER_OCTET_STRING;
874 } /* diameter_avp_get_type */
875
876 /* return an avp name from the code */
877 static gchar *
878 diameter_avp_get_name(guint32 avpCode)
879 {
880   static gchar buffer[64];
881   avpInfo *probe;
882
883   for (probe=avpListHead; probe; probe=probe->next) {
884         if (avpCode == probe->code) {
885           /* We found it! */
886           return probe->name;
887         }
888   }
889   /* If we don't find it, build a name string */
890   sprintf(buffer, "Unknown AVP:0x%08x", avpCode);
891   return buffer;
892 } /* diameter_avp_get_name */
893 static gchar *
894 diameter_avp_get_value(guint32 avpCode, guint32 avpValue)
895 {
896   static gchar buffer[64];
897
898   avpInfo *probe;
899
900   for (probe=avpListHead; probe; probe=probe->next) {
901         if (avpCode == probe->code) {
902           ValueName *vprobe;
903           for(vprobe=probe->values; vprobe; vprobe=vprobe->next) {
904                 if (avpValue == vprobe->value) {
905                   return vprobe->name;
906                 }
907           }
908           sprintf(buffer, "Unknown Value: 0x%08x", avpValue);
909           return buffer;
910         }
911   }
912   /* If we don't find the avp, build a value string */
913   sprintf(buffer, "Unknown AVP! Value: 0x%08x", avpValue);
914   return buffer;
915 } /* diameter_avp_get_value */
916
917 static gchar *
918 diameter_time_to_string(gchar *timeValue)
919 {
920   static gchar buffer[64];
921   int intval;
922   struct tm lt;
923
924   intval=pntohl(*((guint32*)timeValue));
925   intval -= NTP_TIME_DIFF;
926   lt=*localtime((time_t *)&intval);
927   strftime(buffer, 1024, 
928                    "%a, %d %b %Y %H:%M:%S %z",&lt);
929   return buffer;
930 } /* diameter_time_to_string */
931
932
933 /* Code to actually dissect the packets */
934
935 /*
936  * Main dissector
937  */
938 static guint32 dissect_diameter_common(tvbuff_t *tvb, size_t start, packet_info *pinfo,
939                                                                            proto_tree *tree)
940 {
941
942   /* Set up structures needed to add the protocol subtree and manage it */
943   proto_item      *ti;
944   proto_item      *tf;
945   proto_tree      *flags_tree;
946   tvbuff_t        *avp_tvb;
947   proto_tree      *diameter_tree;
948   e_diameterhdr    dh;
949   size_t           offset=0;
950   size_t           avplength;
951   proto_tree      *avp_tree;
952   proto_item      *avptf;
953   int              BadPacket = FALSE;
954   guint32          commandCode, pktLength;
955   guint8           version, flags;
956   gchar            flagstr[64] = "<None>";
957   gchar           *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Error", "Proxyable", "Request" };
958   gchar            commandString[64], vendorString[64];
959   gint        i;
960   guint      bpos;
961   static  int initialized=FALSE;
962
963   /* set our offset */
964   offset=start;
965
966   /*
967    * Only parse in dictionary if there are diameter packets to
968    * dissect.
969    */
970   if (!initialized) {
971           /* Read in our dictionary, if it exists. */
972           initializeDictionary();
973           initialized=TRUE;
974   }
975         
976   /* Make entries in Protocol column and Info column on summary display */
977   if (check_col(pinfo->fd, COL_PROTOCOL)) 
978         col_add_str(pinfo->fd, COL_PROTOCOL, "Diameter");
979   if (check_col(pinfo->fd, COL_INFO)) 
980         col_clear(pinfo->fd, COL_INFO);
981         
982   /* Copy our header */
983   tvb_memcpy(tvb, (guint8*) &dh, offset, sizeof(dh));
984         
985   /* Fix byte ordering in our static structure */
986   dh.versionLength = ntohl(dh.versionLength);
987   dh.flagsCmdCode = ntohl(dh.flagsCmdCode);
988   dh.vendorId = ntohl(dh.vendorId);
989   dh.hopByHopId = ntohl(dh.hopByHopId);
990   dh.endToEndId = ntohl(dh.endToEndId);
991
992   if (dh.vendorId) {
993         strcpy(vendorString, 
994                    diameter_vendor_to_str(dh.vendorId));
995   } else {
996         strcpy(vendorString, "None");
997   }
998
999
1000   /* Do the bit twiddling */
1001   version = DIAM_GET_VERSION(dh);
1002   pktLength = DIAM_GET_LENGTH(dh);
1003   flags = DIAM_GET_FLAGS(dh);
1004   commandCode = DIAM_GET_COMMAND(dh);
1005
1006   /* Set up our flags */
1007   if (check_col(pinfo->fd, COL_INFO) || tree) {  
1008         flagstr[0]=0;
1009         for (i = 0; i < 8; i++) {
1010           bpos = 1 << i;
1011           if (flags & bpos) {
1012                 if (flagstr[0]) {
1013                   strcat(flagstr, ", ");
1014                 }
1015                 strcat(flagstr, fstr[i]);
1016           }
1017         }
1018         if (strlen(flagstr) == 0) {
1019           strcpy(flagstr,"<None>");
1020         }
1021   }
1022   
1023   /* Set up our commandString */
1024   strcpy(commandString, diameter_command_to_str(commandCode));
1025   if (flags & DIAM_FLAGS_R) 
1026         strcat(commandString, "-Request");
1027   else
1028         strcat(commandString, "-Answer");
1029
1030   /* Short packet.  Should have at LEAST one avp */
1031   if (pktLength < MIN_DIAMETER_SIZE) {
1032         g_warning("Diameter: Packet too short: %d bytes less than min size (%d bytes))",
1033                           pktLength, MIN_DIAMETER_SIZE);
1034         BadPacket = TRUE;
1035   }
1036
1037   /* And, check our reserved flags/version */
1038   if ((flags & DIAM_FLAGS_RESERVED) ||
1039           (version != 1)) {
1040         g_warning("Diameter: Bad packet: Bad Flags(0x%x) or Version(%u)",
1041                           flags, version);
1042         BadPacket = TRUE;
1043   }
1044
1045   if (check_col(pinfo->fd, COL_INFO)) {
1046         col_add_fstr(pinfo->fd, COL_INFO,
1047                                  "%s%s%s%s%s vendor=%s (hop-id=%d) (end-id=%d) RPE=%d%d%d",
1048                                  (BadPacket)?"***** Bad Packet!: ":"",
1049                                  (flags & DIAM_FLAGS_P)?"Proxyable ":"",
1050                                  (flags & DIAM_FLAGS_E)?" Error":"",
1051                                  ((BadPacket ||
1052                                    (flags & (DIAM_FLAGS_P|DIAM_FLAGS_E))) ?
1053                                    ": " : ""),
1054                                  commandString, vendorString,
1055                                  dh.hopByHopId, dh.endToEndId,
1056                                  (flags & DIAM_FLAGS_R)?1:0,
1057                                  (flags & DIAM_FLAGS_P)?1:0,
1058                                  (flags & DIAM_FLAGS_E)?1:0);
1059   }
1060         
1061
1062   /* In the interest of speed, if "tree" is NULL, don't do any work not
1063          necessary to generate protocol tree items. */
1064   if (tree) {
1065
1066         /* create display subtree for the protocol */
1067         ti = proto_tree_add_item(tree, proto_diameter, tvb, offset,
1068                                                          MAX(pktLength,MIN_DIAMETER_SIZE), FALSE);
1069         diameter_tree = proto_item_add_subtree(ti, ett_diameter);
1070
1071         /* Version */
1072         proto_tree_add_uint(diameter_tree,
1073                                                 hf_diameter_version,
1074                                                 tvb, offset, 1,
1075                                                 version);
1076
1077         offset+=1;
1078
1079         /* Length */
1080         proto_tree_add_uint(diameter_tree,
1081                                                 hf_diameter_length, tvb,
1082                                                 offset, 3, pktLength);
1083         offset += 3;
1084
1085         /* Flags */
1086         tf = proto_tree_add_uint_format(diameter_tree, hf_diameter_flags, tvb,
1087                                                                         offset , 1, flags, "Flags: 0x%02x (%s)", flags,
1088                                                                         flagstr);
1089         flags_tree = proto_item_add_subtree(tf, ett_diameter_avp_flags);
1090         proto_tree_add_boolean(flags_tree, hf_diameter_flags_request, tvb, offset, 1, flags);
1091         proto_tree_add_boolean(flags_tree, hf_diameter_flags_proxyable, tvb, offset, 1, flags);
1092         proto_tree_add_boolean(flags_tree, hf_diameter_flags_error, tvb, offset, 1, flags);
1093         proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved3, tvb, offset, 1, flags);
1094         proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved4, tvb, offset, 1, flags);
1095         proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved5, tvb, offset, 1, flags);
1096         proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved6, tvb, offset, 1, flags);
1097         proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved7, tvb, offset, 1, flags);
1098
1099         offset += 1;
1100
1101         /* Command Code */
1102         proto_tree_add_uint_format(diameter_tree, hf_diameter_code,
1103                                                            tvb, offset, 3, commandCode, "Command Code: %s", commandString);
1104         offset += 3;
1105
1106         /* Vendor Id */
1107         proto_tree_add_uint_format(diameter_tree,hf_diameter_vendor_id,
1108                                                            tvb, offset, 4,      dh.vendorId, "Vendor-Id: %s", vendorString);
1109         offset += 4;
1110
1111         /* Hop-by-hop Identifier */
1112         proto_tree_add_uint(diameter_tree, hf_diameter_hopbyhopid,
1113                                                 tvb, offset, 4, dh.hopByHopId);
1114         offset += 4;
1115
1116         /* End-to-end Identifier */
1117         proto_tree_add_uint(diameter_tree, hf_diameter_endtoendid,
1118                                                 tvb, offset, 4, dh.endToEndId);
1119         offset += 4;
1120
1121         /* If we have a bad packet, don't bother trying to parse the AVPs */
1122         if (BadPacket) {
1123           return (offset + MAX(pktLength,MIN_DIAMETER_SIZE));
1124         }
1125
1126         /* Start looking at the AVPS */
1127         /* Make the next tvbuff */
1128
1129         /* Update the lengths */
1130         avplength= pktLength - sizeof(e_diameterhdr);
1131     
1132         avp_tvb = tvb_new_subset(tvb, offset, avplength, avplength);
1133         avptf = proto_tree_add_text(diameter_tree,
1134                                                                 tvb, offset, avplength,
1135                                                                 "Attribute Value Pairs");
1136                 
1137         avp_tree = proto_item_add_subtree(avptf,
1138                                                                           ett_diameter_avp);
1139         if (avp_tree != NULL) {
1140           dissect_avps( avp_tvb, pinfo, avp_tree);
1141         }
1142         return MAX((offset + avplength), MIN_DIAMETER_SIZE);
1143   }
1144   return (offset + MAX(pktLength, MIN_DIAMETER_SIZE));
1145
1146 } /* dissect_diameter_common */
1147
1148 static void
1149 dissect_diameter_sctp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1150 {
1151         dissect_diameter_common(tvb, 0, pinfo, tree);
1152 }
1153
1154 static void
1155 dissect_diameter_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1156 {
1157         size_t offset = 0;
1158         guint32 plen;
1159         guint32 available_bytes;
1160 /*      guint32 noffset; */
1161         
1162         /* Loop through the packet, dissecting multiple diameter messages */
1163         do {
1164                 available_bytes = tvb_length_remaining(tvb, offset);
1165                 if (available_bytes < 4) {
1166                         g_warning("Diameter:  Bailing because only %d bytes of packet are available",
1167                                           available_bytes);
1168                         return; /* Bail.  We can't even get our length */
1169                 }
1170
1171                 /* get our packet length */
1172                 plen = tvb_get_ntohl(tvb, offset);
1173                 plen &= 0x00ffffff; /* get rid of the flags */
1174
1175                 /*Desegmentation */
1176                 if (gbl_diameter_desegment) {
1177                         if (pinfo->can_desegment
1178                                 && plen > available_bytes) {
1179                                 pinfo->desegment_offset = offset;
1180                                 pinfo->desegment_len = plen - available_bytes;
1181 /*                              g_warning("Diameter: Bailing for deseg because plen(%d) > available(%d)", */
1182 /*                                                plen, available_bytes); */
1183                                 return;
1184                         }
1185                 }
1186         
1187                 /* Otherwise, dissect our packet */
1188                 offset = dissect_diameter_common(tvb, offset, pinfo, tree);
1189
1190 /*              g_warning("dissected from %d to %d bytes out of %d (available was %d plen was %d)", */
1191 /*                                offset, noffset, tvb_length(tvb), available_bytes, plen); */
1192 /*              offset=noffset; */
1193         } while (offset < tvb_reported_length(tvb));
1194
1195 } /* dissect_diameter_tcp */
1196
1197 /*
1198  * Call the mip_dissector, after saving our pinfo variables
1199  * so it doesn't write to our column display.
1200  */
1201 static void
1202 safe_dissect_mip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1203                                  size_t offset, size_t length)
1204 {
1205   static dissector_handle_t mip_handle;
1206   static int mipInitialized=FALSE;
1207   tvbuff_t *mip_tvb;
1208   address save_dl_src;
1209   address save_dl_dst;
1210   address save_net_src;
1211   address save_net_dst;
1212   address save_src;
1213   address save_dst;
1214   gboolean save_in_error_pkt;
1215
1216   if (!mipInitialized) {
1217         mip_handle = find_dissector("mip");
1218         mipInitialized=TRUE;
1219   }
1220
1221   mip_tvb = tvb_new_subset(tvb, offset,
1222                                                    MIN(length, tvb_length(tvb)-offset),
1223                                                    length);
1224         
1225   /* The contained packet is a MIP registration request;
1226          dissect it with the MIP dissector. */
1227   col_set_writable(pinfo->fd, FALSE);
1228
1229   /* Also, save the current values of the addresses, and restore
1230          them when we're finished dissecting the contained packet, so
1231          that the address columns in the summary don't reflect the
1232          contained packet, but reflect this packet instead. */
1233   save_dl_src = pinfo->dl_src;
1234   save_dl_dst = pinfo->dl_dst;
1235   save_net_src = pinfo->net_src;
1236   save_net_dst = pinfo->net_dst;
1237   save_src = pinfo->src;
1238   save_dst = pinfo->dst;
1239   save_in_error_pkt = pinfo->in_error_pkt;
1240
1241   call_dissector(mip_handle, mip_tvb, pinfo, tree);
1242
1243   /* Restore the "we're inside an error packet" flag. */
1244   pinfo->in_error_pkt = save_in_error_pkt;
1245   pinfo->dl_src = save_dl_src;
1246   pinfo->dl_dst = save_dl_dst;
1247   pinfo->net_src = save_net_src;
1248   pinfo->net_dst = save_net_dst;
1249   pinfo->src = save_src;
1250   pinfo->dst = save_dst;
1251
1252
1253 } /* safe_dissect_mip */
1254
1255 /*
1256  * This function will dissect the AVPs in a diameter packet.  It handles
1257  * all normal types, and even recursively calls itself for grouped AVPs
1258  */
1259 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *avp_tree)
1260 {
1261   /* adds the attribute value pairs to the tree */
1262   e_avphdr avph;
1263   gchar avpTypeString[64];
1264   gchar avpNameString[64];
1265   gchar *valstr;
1266   guint32 vendorId=0;
1267   gchar    vendorString[64];
1268   int hdrLength;
1269   int fixAmt;
1270   proto_tree *avpi_tree;
1271   size_t offset = 0 ;
1272   char dataBuffer[4096];
1273   tvbuff_t        *group_tvb;
1274   tvbuff_t        *mip_tvb;
1275   proto_tree *group_tree;
1276   proto_item *grouptf;
1277   proto_item *avptf;
1278   char buffer[1024];
1279   int BadPacket = FALSE;
1280   guint32 avpLength;
1281   guint8 flags;
1282   proto_item      *tf;
1283   proto_tree      *flags_tree;
1284         
1285   gint32 packetLength;
1286   size_t avpDataLength;
1287   int avpType;
1288   gchar flagstr[64] = "<None>";
1289   gchar *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Protected", "Mandatory", "Vendor-Specific" };
1290   gint        i;
1291   guint      bpos;
1292
1293   packetLength = tvb_length(tvb);
1294
1295   /* Check for invalid packet lengths */
1296   if (packetLength <= 0) {
1297         proto_tree_add_text(avp_tree, tvb, offset, tvb_length(tvb),
1298                                                 "No Attribute Value Pairs Found");
1299         return;
1300   }
1301
1302   /* Spin around until we run out of packet */
1303   while (packetLength > 0 ) {
1304
1305         /* Check for short packet */
1306         if (packetLength < (long)MIN_AVP_SIZE) {
1307           g_warning("Diameter: AVP Payload too short: %d bytes less than min size (%d bytes))",
1308                                 packetLength, MIN_AVP_SIZE);
1309           BadPacket = TRUE;
1310           /* Don't even bother trying to parse a short packet. */
1311           return;
1312         }
1313         
1314         /* Copy our header */
1315         tvb_memcpy(tvb, (guint8*) &avph, offset, MIN((long)sizeof(avph),packetLength));
1316         
1317         /* Fix the byte ordering */
1318         avph.avp_code = ntohl(avph.avp_code);
1319         avph.avp_flagsLength = ntohl(avph.avp_flagsLength);
1320         
1321         flags = (avph.avp_flagsLength & 0xff000000) >> 24;
1322         avpLength = avph.avp_flagsLength & 0x00ffffff;
1323         
1324         /* Set up our flags string */
1325         if (check_col(pinfo->fd, COL_INFO) || avp_tree) {  
1326           flagstr[0]=0;
1327           for (i = 0; i < 8; i++) {
1328                 bpos = 1 << i;
1329                 if (flags & bpos) {
1330                   if (flagstr[0]) {
1331                         strcat(flagstr, ", ");
1332                   }
1333                   strcat(flagstr, fstr[i]);
1334                 }
1335           }
1336           if (strlen(flagstr) == 0) {
1337                 strcpy(flagstr,"<None>");
1338           }
1339         }
1340
1341         /* Dissect our vendor id if it exists  and set hdr length */
1342         if (flags & AVP_FLAGS_V) {
1343           vendorId = ntohl(avph.avp_vendorId);
1344           /* Vendor id */
1345           hdrLength = sizeof(e_avphdr);
1346         } else {
1347           /* No vendor */
1348           hdrLength = sizeof(e_avphdr) - 
1349                 sizeof(guint32);
1350           vendorId = 0;
1351         }
1352
1353         if (vendorId) {
1354           strcpy(vendorString, 
1355                          diameter_vendor_to_str(vendorId));
1356         } else {
1357           vendorString[0]='\0';
1358         }
1359
1360         /* Check for bad length */
1361         if (avpLength < MIN_AVP_SIZE || 
1362                 ((long)avpLength > packetLength)) {
1363           g_warning("Diameter: AVP payload size invalid: avp_length: %d bytes,  "
1364                                 "min: %d bytes,    packetLen: %d",
1365                                 avpLength, MIN_AVP_SIZE, packetLength);
1366           BadPacket = TRUE;
1367         }
1368
1369         /* Check for bad flags */
1370         if (flags & AVP_FLAGS_RESERVED) {
1371           g_warning("Diameter: Invalid AVP: Reserved bit set.  flags = 0x%x,"
1372                                 " resFl=0x%x",
1373                                 flags, AVP_FLAGS_RESERVED);
1374           /* For now, don't set bad packet, since I'm accidentally setting a wrong bit */
1375           // BadPacket = TRUE;
1376         }
1377                 
1378         /*
1379          * Compute amount of byte-alignment fix (Diameter AVPs are sent on 4 byte
1380          * boundries)
1381          */
1382         fixAmt = 4 - (avpLength % 4);
1383         if (fixAmt == 4) fixAmt = 0;
1384         
1385         /* shrink our packetLength */
1386         packetLength = packetLength - (avpLength + fixAmt);
1387         
1388         /* Check for out of bounds */
1389         if (packetLength < 0) {
1390           g_warning("Diameter: Bad AVP: Bad new length (%d bytes) ",
1391                                 packetLength);
1392           BadPacket = TRUE;
1393         }
1394
1395         /* Make avp Name & type */
1396         strcpy(avpTypeString, val_to_str(diameter_avp_get_type(avph.avp_code), TypeValues, 
1397                                                                          "Unknown-Type: 0x%08x"));
1398         strcpy(avpNameString, diameter_avp_get_name(avph.avp_code));
1399
1400         avptf = proto_tree_add_text(avp_tree, tvb,
1401                                                                 offset, avpLength + fixAmt,
1402                                                                 "%s (%s) l:0x%x (%d bytes) (%d padded bytes)",
1403                                                                 avpNameString, avpTypeString, avpLength,
1404                                                                 avpLength, avpLength+fixAmt);
1405         avpi_tree = proto_item_add_subtree(avptf,
1406                                                                            ett_diameter_avpinfo);
1407
1408         if (avpi_tree !=NULL) {
1409           /* Command Code */
1410           proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_code,
1411                                                                  tvb, offset, 4, avph.avp_code, "AVP Code: %s", avpNameString);
1412           offset += 4;
1413                 
1414           tf = proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_flags, tvb,
1415                                                                           offset , 1, flags, "Flags: 0x%02x (%s)", flags,
1416                                                                           flagstr);
1417           flags_tree = proto_item_add_subtree(tf, ett_diameter_avp_flags);
1418           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_vendor_specific, tvb, offset, 1, flags);
1419           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_mandatory, tvb, offset, 1, flags);
1420           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_protected, tvb, offset, 1, flags);
1421           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved3,  tvb, offset, 1, flags);
1422           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved4,  tvb, offset, 1, flags);
1423           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved5,  tvb, offset, 1, flags);
1424           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved6,  tvb, offset, 1, flags);
1425           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved7,  tvb, offset, 1, flags);
1426           offset += 1;
1427
1428           proto_tree_add_uint(avpi_tree, hf_diameter_avp_length,
1429                                                   tvb, offset, 3, avpLength);
1430           offset += 3;
1431
1432           if (flags & AVP_FLAGS_V) {
1433                 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_vendor_id,
1434                                                                    tvb, offset, 4, vendorId, vendorString);
1435                 offset += 4;
1436           }
1437
1438           avpDataLength = avpLength - hdrLength;
1439
1440           /*
1441            * If we've got a bad packet, just highlight the data.  Don't try
1442            * to parse it, and, don't move to next AVP.
1443            */
1444           if (BadPacket) {
1445                 offset -= hdrLength;
1446                 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1447                                                                         tvb, offset, tvb_length(tvb) - offset, dataBuffer,
1448                                                                         "Bad AVP (Suspect Data Not Dissected)");
1449                 return;
1450           }
1451
1452           avpType=diameter_avp_get_type(avph.avp_code);
1453           tvb_memcpy(tvb, (guint8*) dataBuffer, offset, MIN(4095,avpDataLength));
1454         
1455           
1456           switch(avpType) {
1457           case DIAMETER_GROUPED:
1458                 sprintf(buffer, "%s Grouped AVPs", avpNameString);
1459                 /* Recursively call ourselves */
1460                 grouptf = proto_tree_add_text(avpi_tree,
1461                                                                           tvb, offset, tvb_length(tvb),
1462                                                                           buffer);
1463                                 
1464                 group_tree = proto_item_add_subtree(grouptf,
1465                                                                                         ett_diameter_avp);
1466
1467                 group_tvb = tvb_new_subset(tvb, offset,
1468                                                                    MIN(avpDataLength, tvb_length(tvb)-offset), avpDataLength);
1469                 if (group_tree != NULL) {
1470                   dissect_avps( group_tvb, pinfo, group_tree);
1471                 }
1472                 break;
1473
1474           case DIAMETER_IDENTITY:
1475                 proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
1476                                                                          tvb, offset, avpDataLength, dataBuffer,
1477                                                                          "Identity: %*.*s", (int)avpDataLength, (int)avpDataLength,
1478                                                                          dataBuffer);
1479                 break;
1480           case DIAMETER_UTF8STRING:
1481                 proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
1482                                                                          tvb, offset, avpDataLength, dataBuffer,
1483                                                                          "UTF8String: %*.*s", (int)avpDataLength, (int)avpDataLength,
1484                                                                          dataBuffer);
1485                 break;
1486           case DIAMETER_IP_ADDRESS:
1487                 if (avpDataLength == 4) {
1488                   guint32 ipv4Address = ntohl((*(guint32*)dataBuffer));
1489                   proto_tree_add_ipv4_format(avpi_tree, hf_diameter_avp_data_v4addr,
1490                                                                          tvb, offset, avpDataLength, ipv4Address,
1491                                                                          "IPv4 Address: %u.%u.%u.%u",
1492                                                                          (ipv4Address&0xff000000)>>24,
1493                                                                          (ipv4Address&0xff0000)>>16,
1494                                                                          (ipv4Address&0xff00)>>8,
1495                                                                          (ipv4Address&0xff));
1496                 } else if (avpDataLength == 16) {
1497                   proto_tree_add_ipv6_format(avpi_tree, hf_diameter_avp_data_v6addr,
1498                                                                          tvb, offset, avpDataLength, dataBuffer, 
1499                                                                          "IPv6 Address: %04x:%04x:%04x:%04x",
1500                                                                          *((guint32*)dataBuffer),
1501                                                                          *((guint32*)&dataBuffer[4]),
1502                                                                          *((guint32*)&dataBuffer[8]),
1503                                                                          *((guint32*)&dataBuffer[12]));
1504                 } else {
1505                   proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1506                                                                           tvb, offset, avpDataLength, dataBuffer,
1507                                                                           "Error!  Bad Address Length");
1508                 }
1509                 break;
1510
1511           case DIAMETER_INTEGER32:
1512                 {
1513                   gint32 data;
1514                   memcpy(&data, dataBuffer, 4);
1515                   data = ntohl(data);
1516                   proto_tree_add_int_format(avpi_tree, hf_diameter_avp_data_int32,
1517                                                                         tvb, offset, avpDataLength, data,
1518                                                                         "Value: %d", data );
1519                 }
1520                 break;
1521
1522           case DIAMETER_UNSIGNED32:
1523                 {
1524                   guint32 data;
1525
1526                   memcpy(&data, dataBuffer, 4);
1527                   data=ntohl(data);
1528                   proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1529                                                                          tvb, offset, avpDataLength, data,
1530                                                                          "Value: 0x%08x (%u)", data,
1531                                                                          data );
1532                 }
1533                 break;
1534
1535           case DIAMETER_INTEGER64:
1536                 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_int64, tvb, offset, 8, FALSE);
1537                 break;
1538
1539           case DIAMETER_UNSIGNED64:
1540                 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_uint64, tvb, offset, 8, FALSE);
1541                 break;
1542
1543           case DIAMETER_TIME:
1544                 valstr=diameter_time_to_string(dataBuffer);
1545
1546                 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1547                                                                         tvb, offset, avpDataLength, dataBuffer, "Time: %s", valstr);
1548                 break;
1549
1550           case DIAMETER_ENUMERATED:
1551                 {
1552                   guint32 data;
1553                   
1554                   memcpy(&data, dataBuffer, 4);
1555                   data = ntohl(data);
1556                   valstr = diameter_avp_get_value(avph.avp_code, data);
1557                   proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1558                                                                          tvb, offset, avpDataLength, data,
1559                                                                          "Value: 0x%08x (%u): %s", data, data, valstr);
1560                 }
1561                 break;
1562           case DIAMETER_VENDOR_ID:
1563                 {
1564                   guint32 data;
1565                   
1566                   memcpy(&data, dataBuffer, 4);
1567                   data = ntohl(data);
1568                   valstr = diameter_vendor_to_str(data);
1569                   proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1570                                                                          tvb, offset, avpDataLength, data,
1571                                                                          "%s (0x%08x)", valstr, data);
1572                 }
1573                 break;
1574           case DIAMETER_APPLICATION_ID:
1575                 {
1576                   guint32 data;
1577                   
1578                   memcpy(&data, dataBuffer, 4);
1579                   data = ntohl(data);
1580                   valstr = diameter_app_to_str(data);
1581                   proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
1582                                                                          tvb, offset, avpDataLength, data,
1583                                                                          "%s (0x%08x)", valstr, data);
1584                 }
1585                 break;
1586           case DIAMETER_MIP_REG_REQ:
1587
1588                 /* Make a new tvb */
1589                 safe_dissect_mip(tvb, pinfo, avpi_tree, offset, avpDataLength);
1590                 break;
1591
1592           default:
1593           case DIAMETER_OCTET_STRING:
1594                 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
1595                                                                         tvb, offset, avpDataLength, dataBuffer,
1596                                                                         "Hex Data Highlighted Below");
1597                 break;
1598                                 
1599           } /* switch type */
1600         } /* avpi_tree != null */
1601         offset += (avpLength - hdrLength);
1602         offset += fixAmt; /* fix byte alignment */
1603   } /* loop */
1604 } /* dissect_avps */
1605
1606
1607
1608 void
1609 proto_reg_handoff_diameter(void)
1610 {
1611   static int Initialized=FALSE;
1612   static int TcpPort=0;
1613   static int SctpPort=0;
1614
1615   if (Initialized) {
1616         dissector_delete("tcp.port", TcpPort, dissect_diameter_tcp);
1617         dissector_delete("sctp.port", SctpPort, dissect_diameter_sctp);
1618   } else {
1619         Initialized=TRUE;
1620   }
1621
1622   /* set port for future deletes */
1623   TcpPort=gbl_diameterTcpPort;
1624   SctpPort=gbl_diameterSctpPort;
1625
1626   strcpy(gbl_diameterString, "Diameter Protocol");
1627
1628   /* g_warning ("Diameter: Adding tcp dissector to port %d",
1629          gbl_diameterTcpPort); */
1630   dissector_add("tcp.port", gbl_diameterTcpPort, dissect_diameter_tcp,
1631                                 proto_diameter);
1632   dissector_add("sctp.port", gbl_diameterSctpPort,
1633                                 dissect_diameter_sctp, proto_diameter);
1634 }
1635
1636 /* registration with the filtering engine */
1637 void
1638 proto_register_diameter(void)
1639 {
1640         static hf_register_info hf[] = {
1641                 { &hf_diameter_version,
1642                   { "Version", "diameter.version", FT_UINT8, BASE_HEX, NULL, 0x00,
1643                     "", HFILL }},
1644                 { &hf_diameter_length,
1645                   { "Length","diameter.length", FT_UINT24, BASE_DEC, NULL, 0x0,
1646                     "", HFILL }},
1647                 
1648                 { &hf_diameter_flags,
1649                   { "Flags", "diameter.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
1650                     "", HFILL }},
1651                 { &hf_diameter_flags_request,
1652                   { "Request", "diameter.flags.request", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_R,
1653                         "", HFILL }},
1654                 { &hf_diameter_flags_proxyable,
1655                   { "Proxyable", "diameter.flags.proxyable", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_P,
1656                         "", HFILL }},
1657                 { &hf_diameter_flags_error,
1658                   { "Error","diameter.flags.error", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_E,
1659                         "", HFILL }},
1660                 { &hf_diameter_flags_reserved3,
1661                   { "Reserved","diameter.flags.reserved3", FT_BOOLEAN, 8, TFS(&reserved_set),
1662                         DIAM_FLAGS_RESERVED3, "", HFILL }},
1663                 { &hf_diameter_flags_reserved4,
1664                   { "Reserved","diameter.flags.reserved4", FT_BOOLEAN, 8, TFS(&reserved_set),
1665                         DIAM_FLAGS_RESERVED4, "", HFILL }},
1666                 { &hf_diameter_flags_reserved5,
1667                   { "Reserved","diameter.flags.reserved5", FT_BOOLEAN, 8, TFS(&reserved_set),
1668                         DIAM_FLAGS_RESERVED5, "", HFILL }},
1669                 { &hf_diameter_flags_reserved6,
1670                   { "Reserved","diameter.flags.reserved6", FT_BOOLEAN, 8, TFS(&reserved_set),
1671                         DIAM_FLAGS_RESERVED6, "", HFILL }},
1672                 { &hf_diameter_flags_reserved7,
1673                   { "Reserved","diameter.flags.reserved7", FT_BOOLEAN, 8, TFS(&reserved_set),
1674                         DIAM_FLAGS_RESERVED7, "", HFILL }},
1675
1676                 { &hf_diameter_code,
1677                   { "Command Code","diameter.code", FT_UINT24, BASE_DEC,
1678                     NULL, 0x0, "", HFILL }},
1679                 { &hf_diameter_vendor_id,
1680                   { "VendorId", "diameter.vendorId", FT_UINT32, BASE_DEC, NULL,
1681                         0x0,"", HFILL }},
1682                 { &hf_diameter_hopbyhopid,
1683                   { "Hop-by-Hop Identifier", "diameter.hopbyhopid", FT_UINT32,
1684                     BASE_HEX, NULL, 0x0, "", HFILL }},
1685                 { &hf_diameter_endtoendid,
1686                   { "End-to-End Identifier", "diameter.endtoendid", FT_UINT32, 
1687                     BASE_HEX, NULL, 0x0, "", HFILL }},
1688                 
1689                 { &hf_diameter_avp_code,
1690                   { "AVP Code","diameter.avp.code", FT_UINT32, BASE_DEC,
1691                     NULL, 0x0, "", HFILL }},
1692                 { &hf_diameter_avp_length,
1693                   { "AVP Length","diameter.avp.length", FT_UINT24, BASE_DEC,
1694                     NULL, 0x0, "", HFILL }},
1695
1696
1697                 { &hf_diameter_avp_flags,
1698                   { "AVP Flags","diameter.avp.flags", FT_UINT8, BASE_HEX,
1699                     NULL, 0x0, "", HFILL }},
1700                 { &hf_diameter_avp_flags_vendor_specific,
1701                   { "Vendor-Specific", "diameter.flags.vendorspecific", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_V,
1702                         "", HFILL }},
1703                 { &hf_diameter_avp_flags_mandatory,
1704                   { "Mandatory", "diameter.flags.mandatory", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_M,
1705                         "", HFILL }},
1706                 { &hf_diameter_avp_flags_protected,
1707                   { "Protected","diameter.avp.flags.protected", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_P,
1708                         "", HFILL }},
1709                 { &hf_diameter_avp_flags_reserved3,
1710                   { "Reserved","diameter.avp.flags.reserved3", FT_BOOLEAN, 8, TFS(&reserved_set),
1711                         AVP_FLAGS_RESERVED3,    "", HFILL }},
1712                 { &hf_diameter_avp_flags_reserved4,
1713                   { "Reserved","diameter.avp.flags.reserved4", FT_BOOLEAN, 8, TFS(&reserved_set),
1714                         AVP_FLAGS_RESERVED4,    "", HFILL }},
1715                 { &hf_diameter_avp_flags_reserved5,
1716                   { "Reserved","diameter.avp.flags.reserved5", FT_BOOLEAN, 8, TFS(&reserved_set),
1717                         AVP_FLAGS_RESERVED5,    "", HFILL }},
1718                 { &hf_diameter_avp_flags_reserved6,
1719                   { "Reserved","diameter.avp.flags.reserved6", FT_BOOLEAN, 8, TFS(&reserved_set),
1720                         AVP_FLAGS_RESERVED6,    "", HFILL }},
1721                 { &hf_diameter_avp_flags_reserved7,
1722                   { "Reserved","diameter.avp.flags.reserved7", FT_BOOLEAN, 8, TFS(&reserved_set),
1723                         AVP_FLAGS_RESERVED7,    "", HFILL }},
1724                 { &hf_diameter_avp_vendor_id,
1725                   { "AVP Vendor Id","diameter.avp.vendorId", FT_UINT32, BASE_DEC,
1726                     NULL, 0x0, "", HFILL }},
1727                 { &hf_diameter_avp_data_uint64,
1728                   { "AVP Data","diameter.avp.data.uint64", FT_UINT64, BASE_DEC,
1729                     NULL, 0x0, "", HFILL }},
1730                 { &hf_diameter_avp_data_int64,
1731                   { "AVP Data","diameter.avp.data.int64", FT_INT64, BASE_DEC,
1732                     NULL, 0x0, "", HFILL }},
1733                 { &hf_diameter_avp_data_uint32,
1734                   { "AVP Data","diameter.avp.data.uint32", FT_UINT32, BASE_DEC,
1735                     NULL, 0x0, "", HFILL }},
1736                 { &hf_diameter_avp_data_int32,
1737                   { "AVP Data","diameter.avp.data.int32", FT_INT32, BASE_DEC,
1738                     NULL, 0x0, "", HFILL }},
1739                 { &hf_diameter_avp_data_bytes,
1740                   { "AVP Data","diameter.avp.data.bytes", FT_BYTES, BASE_NONE,
1741                     NULL, 0x0, "", HFILL }},
1742                 
1743                 { &hf_diameter_avp_data_string,
1744                   { "AVP Data","diameter.avp.data.string", FT_STRING, BASE_NONE,
1745                     NULL, 0x0, "", HFILL }},
1746                 { &hf_diameter_avp_data_v4addr,
1747                   { "AVP Data","diameter.avp.data.v4addr", FT_IPv4, BASE_NONE,
1748                     NULL, 0x0, "", HFILL }},
1749                 { &hf_diameter_avp_data_v6addr,
1750                   { "AVP Data","diameter.avp.data.v6addr", FT_IPv6, BASE_NONE,
1751                     NULL, 0x0, "", HFILL }},
1752                 { &hf_diameter_avp_data_time,
1753                   { "AVP Data","diameter.avp.data.time", FT_ABSOLUTE_TIME, BASE_NONE,
1754                     NULL, 0x0, "", HFILL }},
1755
1756         };
1757         static gint *ett[] = {
1758                 &ett_diameter,
1759                 &ett_diameter_flags,
1760                 &ett_diameter_avp,
1761                 &ett_diameter_avp_flags,
1762                 &ett_diameter_avpinfo
1763         };
1764         module_t *diameter_module;
1765
1766         proto_diameter = proto_register_protocol (gbl_diameterString,
1767                                                                                           "Diameter", "diameter");
1768         proto_register_field_array(proto_diameter, hf, array_length(hf));
1769         proto_register_subtree_array(ett, array_length(ett));
1770
1771         /* Register a configuration option for port */
1772         diameter_module = prefs_register_protocol(proto_diameter,
1773                                                                                           proto_reg_handoff_diameter);
1774         prefs_register_uint_preference(diameter_module, "tcp.port",
1775                                                                    "Diameter TCP Port",
1776                                                                    "Set the TCP port for Diameter messages",
1777                                                                    10,
1778                                                                    &gbl_diameterTcpPort);
1779         prefs_register_uint_preference(diameter_module, "sctp.port",
1780                                                                    "Diameter SCTP Port",
1781                                                                    "Set the SCTP port for Diameter messages",
1782                                                                    10,
1783                                                                    &gbl_diameterSctpPort);
1784         /*
1785          * Build our default dictionary filename
1786          */
1787         if (! gbl_diameterDictionary) {
1788                 gbl_diameterDictionary = (gchar *) g_malloc(strlen(get_datafile_dir()) +
1789                                                                                                         1 + strlen(DICT_FN) + 1); /* slash + fn + null */
1790                 sprintf(gbl_diameterDictionary, "%s" G_DIR_SEPARATOR_S "%s",
1791                                 get_datafile_dir(), DICT_FN );
1792         }
1793         /* Now register its preferences so it can be changed. */
1794         prefs_register_string_preference(diameter_module, "dictionary.name",
1795                                                                          "Diameter XML Dictionary",
1796                                                                          "Set the dictionary used for Diameter messages",
1797                                                                          &gbl_diameterDictionary);
1798
1799         /* Desegmentation */
1800         prefs_register_bool_preference(diameter_module, "diameter.desegment",
1801                                                                    "Desegment all Diameter messages spanning multiple TCP segments",
1802                                                                    "Whether the Diameter dissector should desegment all messages spanning multiple TCP segments",
1803                                                                    &gbl_diameter_desegment);
1804
1805         /* Register some preferences we no longer support, so we can report
1806            them as obsolete rather than just illegal. */
1807         prefs_register_obsolete_preference(diameter_module, "udp.port");
1808         prefs_register_obsolete_preference(diameter_module, "command_in_header");
1809 } /* proto_register_diameter */