From Ronnie Sahlberg: FT_UINT64 support, code to handle 64-bit integers
[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.27 2001/10/29 21:13:07 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 "packet.h"
46 #include "resolv.h"
47 #include "prefs.h"
48
49 /* This must be defined before we include packet-diameter-defs.h s*/
50
51 /* Valid data types */
52 typedef enum {
53   /* Base Types */
54   DIAMETER_OCTET_STRING = 1,
55   DIAMETER_INTEGER32,
56   DIAMETER_INTEGER64,
57   DIAMETER_UNSIGNED32,
58   DIAMETER_UNSIGNED64,
59   DIAMETER_FLOAT32,
60   DIAMETER_FLOAT64,
61   DIAMETER_FLOAT128,
62   DIAMETER_GROUPED,
63
64   /* Derived Types */
65   DIAMETER_IP_ADDRESS,         /* OctetString */
66   DIAMETER_TIME,               /* Integer 32 */
67   DIAMETER_UTF8STRING,         /* OctetString */
68   DIAMETER_IDENTITY,           /* OctetString */
69   DIAMETER_ENUMERATED,         /* Integer 32 */
70   DIAMETER_IP_FILTER_RULE,     /* OctetString */
71   DIAMETER_QOS_FILTER_RULE     /* OctetString */
72   
73 } diameterDataType;
74
75 typedef struct avp_info {
76   guint32           code;
77   gchar            *name;
78   diameterDataType  type;
79   value_string     *values;
80 } avpInfo;
81
82 #include "packet-diameter-defs.h"
83
84 #define  NTP_TIME_DIFF                   (2208988800UL)
85
86 #define TCP_PORT_DIAMETER       1812
87 #define SCTP_PORT_DIAMETER      1812
88
89 static const true_false_string flags_set_truth = {
90   "Set",
91   "Not set"
92 };
93
94 static const true_false_string reserved_set = {
95   "*** Error! Reserved Bit is Set",
96   "Ok"
97 };
98 static int proto_diameter = -1;
99 static int hf_diameter_length = -1;
100 static int hf_diameter_code = -1;
101 static int hf_diameter_hopbyhopid =-1;
102 static int hf_diameter_endtoendid =-1;
103 static int hf_diameter_reserved = -1;
104 static int hf_diameter_version = -1;
105 static int hf_diameter_vendor_id = -1;
106 static int hf_diameter_flags = -1;
107 static int hf_diameter_flags_request = -1;
108 static int hf_diameter_flags_proxyable = -1;
109 static int hf_diameter_flags_error = -1;
110 static int hf_diameter_flags_reserved3 = -1;
111 static int hf_diameter_flags_reserved4 = -1;
112 static int hf_diameter_flags_reserved5 = -1;
113 static int hf_diameter_flags_reserved6 = -1;
114 static int hf_diameter_flags_reserved7 = -1;
115
116 static int hf_diameter_avp_code = -1;
117 static int hf_diameter_avp_length = -1;
118 static int hf_diameter_avp_reserved = -1;
119 static int hf_diameter_avp_flags = -1;
120 static int hf_diameter_avp_flags_vendor_specific = -1;
121 static int hf_diameter_avp_flags_mandatory = -1;
122 static int hf_diameter_avp_flags_protected = -1;
123 static int hf_diameter_avp_flags_reserved3 = -1;
124 static int hf_diameter_avp_flags_reserved4 = -1;
125 static int hf_diameter_avp_flags_reserved5 = -1;
126 static int hf_diameter_avp_flags_reserved6 = -1;
127 static int hf_diameter_avp_flags_reserved7 = -1;
128 static int hf_diameter_avp_vendor_id = -1;
129
130
131 static int hf_diameter_avp_data_uint32 = -1;
132 static int hf_diameter_avp_data_int32 = -1;
133 static int hf_diameter_avp_data_uint64 = -1;
134 static int hf_diameter_avp_data_int64 = -1;
135 static int hf_diameter_avp_data_bytes = -1;
136 static int hf_diameter_avp_data_string = -1;
137 static int hf_diameter_avp_data_v4addr = -1;
138 static int hf_diameter_avp_data_v6addr = -1;
139 static int hf_diameter_avp_data_time = -1;
140
141 static gint ett_diameter = -1;
142 static gint ett_diameter_flags = -1;
143 static gint ett_diameter_avp = -1;
144 static gint ett_diameter_avp_flags = -1;
145 static gint ett_diameter_avpinfo = -1;
146
147 static char gbl_diameterString[200];
148 static int gbl_diameterTcpPort=TCP_PORT_DIAMETER;
149 static int gbl_diameterSctpPort=SCTP_PORT_DIAMETER;
150
151 typedef struct _e_diameterhdr {
152   guint32  versionLength;
153   guint32  flagsCmdCode;
154   guint32  vendorId;
155   guint32  hopByHopId;
156   guint32  endToEndId;
157 } e_diameterhdr;
158
159 typedef struct _e_avphdr {
160   guint32 avp_code;
161   guint32 avp_flagsLength;
162   guint32 avp_vendorId;           /* optional */
163 } e_avphdr;
164
165 #define AUTHENTICATOR_LENGTH 12
166
167 /* Diameter Header Flags */
168 /*                                      RPrrrrrrCCCCCCCCCCCCCCCCCCCCCCCC  */
169 #define DIAM_FLAGS_R 0x80
170 #define DIAM_FLAGS_P 0x40
171 #define DIAM_FLAGS_E 0x20
172 #define DIAM_FLAGS_RESERVED3 0x10
173 #define DIAM_FLAGS_RESERVED4 0x08
174 #define DIAM_FLAGS_RESERVED5 0x04
175 #define DIAM_FLAGS_RESERVED6 0x02
176 #define DIAM_FLAGS_RESERVED7 0x01
177 #define DIAM_FLAGS_RESERVED  0x1f
178
179 #define DIAM_LENGTH_MASK  0x00ffffffl
180 #define DIAM_COMMAND_MASK DIAM_LENGTH_MASK
181 #define DIAM_GET_FLAGS(dh)                ((dh.flagsCmdCode & ~DIAM_COMMAND_MASK) >> 24)
182 #define DIAM_GET_VERSION(dh)              ((dh.versionLength & (~DIAM_LENGTH_MASK)) >> 24)
183 #define DIAM_GET_COMMAND(dh)              (dh.flagsCmdCode & DIAM_COMMAND_MASK)
184 #define DIAM_GET_LENGTH(dh)               (dh.versionLength & DIAM_LENGTH_MASK)
185
186 /* Diameter AVP Flags */
187 #define AVP_FLAGS_P 0x20
188 #define AVP_FLAGS_V 0x80
189 #define AVP_FLAGS_M 0x40
190 #define AVP_FLAGS_RESERVED3 0x10
191 #define AVP_FLAGS_RESERVED4 0x08
192 #define AVP_FLAGS_RESERVED5 0x04
193 #define AVP_FLAGS_RESERVED6 0x02
194 #define AVP_FLAGS_RESERVED7 0x01
195 #define AVP_FLAGS_RESERVED 0x1f          /* 00011111  -- V M P X X X X X */
196
197 #define MIN_AVP_SIZE (sizeof(e_avphdr) - sizeof(guint32))
198 #define MIN_DIAMETER_SIZE (sizeof(e_diameterhdr) + MIN_AVP_SIZE)
199
200 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
201
202
203
204
205 /* Diameter Manipulation Routines (mess with our strucutres) */
206
207 diameterDataType
208 diameter_avp_get_type(guint32 avpCode){
209   int i;
210   for (i=0; diameter_avps[i].name; i++) {
211         if (avpCode == diameter_avps[i].code) {
212           /* We found it! */
213           return diameter_avps[i].type;
214         }
215   }
216   /* If we don't find it, assume it's data */
217   g_warning("DIAMETER: Unable to find type for avpCode %d!", avpCode);
218   return DIAMETER_OCTET_STRING;
219 } /* diameter_avp_get_type */
220
221 static gchar *
222 diameter_avp_get_name(guint32 avpCode)
223 {
224   static gchar buffer[64];
225
226   int i;
227   for (i=0; diameter_avps[i].name; i++) {
228         if (avpCode == diameter_avps[i].code) {
229           /* We found it! */
230           return diameter_avps[i].name;
231         }
232   }
233   /* If we don't find it, build a name string */
234   sprintf(buffer, "Unknown AVP:0x%08x", avpCode);
235   return buffer;
236 } /* diameter_avp_get_name */
237 static gchar *
238 diameter_avp_get_value(guint32 avpCode, guint32 avpValue)
239 {
240   static gchar buffer[64];
241
242   int i;
243   for (i=0; diameter_avps[i].name; i++) {
244         if (avpCode == diameter_avps[i].code) {
245           /* We found the code.  Now find the value! */
246           if (!diameter_avps[i].values) 
247                 break;
248           return val_to_str(avpValue, diameter_avps[i].values , "Unknown Value: 0x%08x");
249         }
250   }
251   /* If we don't find the avp, build a value string */
252   sprintf(buffer, "Unknown AVP! Value: 0x%08x", avpValue);
253   return buffer;
254 } /* diameter_avp_get_value */
255
256 static gchar *
257 diameter_time_to_string(gchar *timeValue)
258 {
259   static gchar buffer[64];
260   int intval;
261   struct tm lt;
262
263   intval=pntohl(*((guint32*)timeValue));
264   intval -= NTP_TIME_DIFF;
265   lt=*localtime((time_t *)&intval);
266   strftime(buffer, 1024, 
267                    "%a, %d %b %Y %H:%M:%S %z",&lt);
268   return buffer;
269 } /* diameter_time_to_string */
270
271
272 /* Code to actually dissect the packets */
273
274 /*
275  * Main dissector
276  */
277 static void dissect_diameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
278 {
279
280   /* Set up structures needed to add the protocol subtree and manage it */
281   proto_item      *ti;
282   proto_item      *tf;
283   proto_tree      *flags_tree;
284   tvbuff_t        *avp_tvb;
285   proto_tree      *diameter_tree;
286   e_diameterhdr    dh;
287   size_t           offset=0;
288   size_t           avplength;
289   proto_tree      *avp_tree;
290   proto_item      *avptf;
291   int              BadPacket = FALSE;
292   guint32          commandCode, pktLength;
293   guint8           version, flags;
294   gchar            flagstr[64] = "<None>";
295   gchar           *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Error", "Proxyable", "Request" };
296   gchar            commandString[64], vendorString[64];
297   gint        i;
298   guint      bpos;
299         
300   /* Make entries in Protocol column and Info column on summary display */
301   if (check_col(pinfo->fd, COL_PROTOCOL)) 
302         col_add_str(pinfo->fd, COL_PROTOCOL, "Diameter");
303   if (check_col(pinfo->fd, COL_INFO)) 
304         col_clear(pinfo->fd, COL_INFO);
305         
306   /* Copy our header */
307   tvb_memcpy(tvb, (guint8*) &dh, offset, sizeof(dh));
308         
309   /* Fix byte ordering in our static structure */
310   dh.versionLength = ntohl(dh.versionLength);
311   dh.flagsCmdCode = ntohl(dh.flagsCmdCode);
312   dh.vendorId = ntohl(dh.vendorId);
313   dh.hopByHopId = ntohl(dh.hopByHopId);
314   dh.endToEndId = ntohl(dh.endToEndId);
315
316   if (dh.vendorId) {
317         strcpy(vendorString, 
318                    val_to_str(dh.vendorId, diameter_vendor_specific_vendors, "Unknown Vendor: %08x"));
319   } else {
320         strcpy(vendorString, "None");
321   }
322
323
324   /* Do the bit twiddling */
325   version = DIAM_GET_VERSION(dh);
326   pktLength = DIAM_GET_LENGTH(dh);
327   flags = DIAM_GET_FLAGS(dh);
328   commandCode = DIAM_GET_COMMAND(dh);
329
330   /* Set up our flags */
331   if (check_col(pinfo->fd, COL_INFO) || tree) {  
332         flagstr[0]=0;
333         for (i = 0; i < 8; i++) {
334           bpos = 1 << i;
335           if (flags & bpos) {
336                 if (flagstr[0]) {
337                   strcat(flagstr, ", ");
338                 }
339                 strcat(flagstr, fstr[i]);
340           }
341         }
342         if (strlen(flagstr) == 0) {
343           strcpy(flagstr,"<None>");
344         }
345   }
346   
347   /* Set up our commandString */
348   strcpy(commandString, val_to_str(commandCode, diameter_command_code_vals, "Unknown Command: 0x%08x"));
349   if (flags & DIAM_FLAGS_R) 
350         strcat(commandString, "-Request");
351   else
352         strcat(commandString, "-Answer");
353
354   /* Short packet.  Should have at LEAST one avp */
355   if (pktLength < MIN_DIAMETER_SIZE) {
356         g_warning("DIAMETER: Packet too short: %d bytes less than min size (%d bytes))",
357                           pktLength, MIN_DIAMETER_SIZE);
358         BadPacket = TRUE;
359   }
360
361   /* And, check our reserved flags/version */
362   if ((flags & DIAM_FLAGS_RESERVED) ||
363           (version != 1)) {
364         g_warning("DIAMETER: Bad packet: Bad Flags(0x%x) or Version(%u)",
365                           flags, version);
366         BadPacket = TRUE;
367   }
368
369   if (check_col(pinfo->fd, COL_INFO)) {
370         col_add_fstr(pinfo->fd, COL_INFO,
371                                  "%s%s%s%s: %s vendor=%s (hop-id=%d) (end-id=%d) RPE=%d%d%d",
372                                  (BadPacket)?"***** Bad Packet!: ":"",
373                                  (flags & DIAM_FLAGS_P)?"Proxyable ":"",
374                                  (flags & DIAM_FLAGS_R)?"Request":"Answer",
375                                  (flags & DIAM_FLAGS_E)?" Error":"",
376                                  commandString, vendorString,
377                                  dh.hopByHopId, dh.endToEndId,
378                                  (flags & DIAM_FLAGS_R)?1:0,
379                                  (flags & DIAM_FLAGS_P)?1:0,
380                                  (flags & DIAM_FLAGS_E)?1:0);
381   }
382         
383
384   /* In the interest of speed, if "tree" is NULL, don't do any work not
385          necessary to generate protocol tree items. */
386   if (tree) {
387
388         /* create display subtree for the protocol */
389         ti = proto_tree_add_item(tree, proto_diameter, tvb, offset, tvb_length(tvb), FALSE);
390         diameter_tree = proto_item_add_subtree(ti, ett_diameter);
391
392         /* Version */
393         proto_tree_add_uint(diameter_tree,
394                                                 hf_diameter_version,
395                                                 tvb, offset, 1,
396                                                 version);
397
398         offset+=1;
399
400         /* Length */
401         proto_tree_add_uint(diameter_tree,
402                                                 hf_diameter_length, tvb,
403                                                 offset, 3, pktLength);
404         offset += 3;
405
406         /* Flags */
407         tf = proto_tree_add_uint_format(diameter_tree, hf_diameter_flags, tvb,
408                                                                         offset , 1, flags, "Flags: 0x%02x (%s)", flags,
409                                                                         flagstr);
410         flags_tree = proto_item_add_subtree(tf, ett_diameter_avp_flags);
411         proto_tree_add_boolean(flags_tree, hf_diameter_flags_request, tvb, offset, 1, flags);
412         proto_tree_add_boolean(flags_tree, hf_diameter_flags_proxyable, tvb, offset, 1, flags);
413         proto_tree_add_boolean(flags_tree, hf_diameter_flags_error, tvb, offset, 1, flags);
414         proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved3, tvb, offset, 1, flags);
415         proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved4, tvb, offset, 1, flags);
416         proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved5, tvb, offset, 1, flags);
417         proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved6, tvb, offset, 1, flags);
418         proto_tree_add_boolean(flags_tree, hf_diameter_flags_reserved7, tvb, offset, 1, flags);
419
420         offset += 1;
421
422         /* Command Code */
423         proto_tree_add_uint_format(diameter_tree, hf_diameter_code,
424                                                 tvb, offset, 3, commandCode, "Command Code: %s", commandString);
425         offset += 3;
426
427         /* Vendor Id */
428         proto_tree_add_uint_format(diameter_tree,hf_diameter_vendor_id,
429                                                 tvb, offset, 4, dh.vendorId, "Vendor-Id: %s", vendorString);
430         offset += 4;
431
432         /* Hop-by-hop Identifier */
433         proto_tree_add_uint(diameter_tree, hf_diameter_hopbyhopid,
434                                                 tvb, offset, 4, dh.hopByHopId);
435         offset += 4;
436
437         /* End-to-end Identifier */
438         proto_tree_add_uint(diameter_tree, hf_diameter_endtoendid,
439                                                 tvb, offset, 4, dh.endToEndId);
440         offset += 4;
441
442         /* If we have a bad packet, don't bother trying to parse the AVPs */
443         if (BadPacket) {
444           return;
445         }
446
447         /* Start looking at the AVPS */
448         /* Make the next tvbuff */
449
450         /* Update the lengths */
451         avplength= pktLength - sizeof(e_diameterhdr);
452     
453         avp_tvb = tvb_new_subset(tvb, offset, -1, avplength);
454         avptf = proto_tree_add_text(diameter_tree,
455                                                                 tvb, offset, tvb_length(tvb),
456                                                                 "Attribute Value Pairs");
457                 
458         avp_tree = proto_item_add_subtree(avptf,
459                                                                           ett_diameter_avp);
460         if (avp_tree != NULL) {
461           dissect_avps( avp_tvb, pinfo, avp_tree);
462         }
463   }
464 } /* dissect_diameter */
465
466 /*
467  * This function will dissect the AVPs in a diameter packet.  It handles
468  * all normal types, and even recursively calls itself for grouped AVPs
469  */
470 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *avp_tree)
471 {
472   /* adds the attribute value pairs to the tree */
473   e_avphdr avph;
474   gchar avpTypeString[64];
475   gchar avpNameString[64];
476   gchar *valstr;
477   guint32 vendorId=0;
478   gchar    vendorString[64];
479   int hdrLength;
480   int fixAmt;
481   proto_tree *avpi_tree;
482   size_t offset = 0 ;
483   char dataBuffer[4096];
484   tvbuff_t        *group_tvb;
485   proto_tree *group_tree;
486   proto_item *grouptf;
487   proto_item *avptf;
488   char buffer[1024];
489   int BadPacket = FALSE;
490   guint32 avpLength;
491   guint8 flags;
492   proto_item      *tf;
493   proto_tree      *flags_tree;
494         
495   gint32 packetLength;
496   size_t avpDataLength;
497   int avpType;
498   gchar flagstr[64] = "<None>";
499   gchar *fstr[] = {"RSVD7", "RSVD6", "RSVD5", "RSVD4", "RSVD3", "Protected", "Mandatory", "Vendor-Specific" };
500   gint        i;
501   guint      bpos;
502
503   packetLength = tvb_length(tvb);
504
505   /* Check for invalid packet lengths */
506   if (packetLength <= 0) {
507         proto_tree_add_text(avp_tree, tvb, offset, tvb_length(tvb),
508                                                 "No Attribute Value Pairs Found");
509         return;
510   }
511
512   /* Spin around until we run out of packet */
513   while (packetLength > 0 ) {
514
515         /* Check for short packet */
516         if (packetLength < (long)MIN_AVP_SIZE) {
517           g_warning("DIAMETER: AVP Payload too short: %d bytes less than min size (%d bytes))",
518                                 packetLength, MIN_AVP_SIZE);
519           BadPacket = TRUE;
520           /* Don't even bother trying to parse a short packet. */
521           return;
522         }
523         
524         /* Copy our header */
525         tvb_memcpy(tvb, (guint8*) &avph, offset, MIN((long)sizeof(avph),packetLength));
526         
527         /* Fix the byte ordering */
528         avph.avp_code = ntohl(avph.avp_code);
529         avph.avp_flagsLength = ntohl(avph.avp_flagsLength);
530         
531         flags = (avph.avp_flagsLength & 0xff000000) >> 24;
532         avpLength = avph.avp_flagsLength & 0x00ffffff;
533         
534         /* Set up our flags string */
535         if (check_col(pinfo->fd, COL_INFO) || avp_tree) {  
536           flagstr[0]=0;
537           for (i = 0; i < 8; i++) {
538                 bpos = 1 << i;
539                 if (flags & bpos) {
540                   if (flagstr[0]) {
541                         strcat(flagstr, ", ");
542                   }
543                   strcat(flagstr, fstr[i]);
544                 }
545           }
546           if (strlen(flagstr) == 0) {
547                 strcpy(flagstr,"<None>");
548           }
549         }
550
551         /* Dissect our vendor id if it exists  and set hdr length */
552         if (flags & AVP_FLAGS_V) {
553           vendorId = ntohl(avph.avp_vendorId);
554           /* Vendor id */
555           hdrLength = sizeof(e_avphdr);
556         } else {
557           /* No vendor */
558           hdrLength = sizeof(e_avphdr) - 
559                 sizeof(guint32);
560           vendorId = 0;
561         }
562
563         if (vendorId) {
564           strcpy(vendorString, 
565                          val_to_str(vendorId, diameter_vendor_specific_vendors, "Unknown Vendor: %08x"));
566         } else {
567           vendorString[0]='\0';
568         }
569
570         /* Check for bad length */
571         if (avpLength < MIN_AVP_SIZE || 
572                 ((long)avpLength > packetLength)) {
573           g_warning("DIAMETER: AVP payload size invalid: avp_length: %d bytes,  "
574                                 "min: %d bytes,    packetLen: %d",
575                                 avpLength, MIN_AVP_SIZE, packetLength);
576           BadPacket = TRUE;
577         }
578
579         /* Check for bad flags */
580         if (flags & AVP_FLAGS_RESERVED) {
581           g_warning("DIAMETER: Invalid AVP: Reserved bit set.  flags = 0x%x,"
582                                 " resFl=0x%x",
583                                 flags, AVP_FLAGS_RESERVED);
584           /* For now, don't set bad packet, since I'm accidentally setting a wrong bit */
585           // BadPacket = TRUE;
586         }
587                 
588         /*
589          * Compute amount of byte-alignment fix (Diameter AVPs are sent on 4 byte
590          * boundries)
591          */
592         fixAmt = 4 - (avpLength % 4);
593         if (fixAmt == 4) fixAmt = 0;
594         
595         /* shrink our packetLength */
596         packetLength = packetLength - (avpLength + fixAmt);
597         
598         /* Check for out of bounds */
599         if (packetLength < 0) {
600           g_warning("DIAMETER: Bad AVP: Bad new length (%d bytes) ",
601                                 packetLength);
602           BadPacket = TRUE;
603         }
604
605         /* Make avp Name & type */
606         strcpy(avpTypeString, val_to_str(diameter_avp_get_type(avph.avp_code), diameter_avp_type_vals, 
607                                                                    "Unknown-Type: 0x%08x"));
608         strcpy(avpNameString, diameter_avp_get_name(avph.avp_code));
609
610         avptf = proto_tree_add_text(avp_tree, tvb,
611                                                                 offset, avpLength + fixAmt,
612                                                                 "%s (%s) l:0x%x (%d bytes) (%d padded bytes)",
613                                                                 avpNameString, avpTypeString, avpLength,
614                                                                 avpLength, avpLength+fixAmt);
615         avpi_tree = proto_item_add_subtree(avptf,
616                                                                            ett_diameter_avpinfo);
617
618         if (avpi_tree !=NULL) {
619           /* Command Code */
620           proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_code,
621                                                   tvb, offset, 4, avph.avp_code, "AVP Code: %s", avpNameString);
622           offset += 4;
623                 
624           tf = proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_flags, tvb,
625                                                                           offset , 1, flags, "Flags: 0x%02x (%s)", flags,
626                                                                           flagstr);
627           flags_tree = proto_item_add_subtree(tf, ett_diameter_avp_flags);
628           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_vendor_specific, tvb, offset, 1, flags);
629           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_mandatory, tvb, offset, 1, flags);
630           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_protected, tvb, offset, 1, flags);
631           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved3,  tvb, offset, 1, flags);
632           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved4,  tvb, offset, 1, flags);
633           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved5,  tvb, offset, 1, flags);
634           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved6,  tvb, offset, 1, flags);
635           proto_tree_add_boolean(flags_tree, hf_diameter_avp_flags_reserved7,  tvb, offset, 1, flags);
636           offset += 1;
637
638           proto_tree_add_uint(avpi_tree, hf_diameter_avp_length,
639                                                   tvb, offset, 3, avpLength);
640           offset += 3;
641
642           if (flags & AVP_FLAGS_V) {
643                 proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_vendor_id,
644                                                                    tvb, offset, 4, vendorId, vendorString);
645                 offset += 4;
646           }
647
648           avpDataLength = avpLength - hdrLength;
649
650           /*
651            * If we've got a bad packet, just highlight the data.  Don't try
652            * to parse it, and, don't move to next AVP.
653            */
654           if (BadPacket) {
655                 offset -= hdrLength;
656                 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
657                                                                         tvb, offset, tvb_length(tvb) - offset, dataBuffer,
658                                                                         "Bad AVP (Suspect Data Not Dissected)");
659                 return;
660           }
661
662           avpType=diameter_avp_get_type(avph.avp_code);
663           tvb_memcpy(tvb, (guint8*) dataBuffer, offset, MIN(4095,avpDataLength));
664         
665           
666           switch(avpType) {
667           case DIAMETER_GROUPED:
668                 sprintf(buffer, "%s Grouped AVPs", avpNameString);
669                 /* Recursively call ourselves */
670                 grouptf = proto_tree_add_text(avpi_tree,
671                                                                           tvb, offset, tvb_length(tvb),
672                                                                           buffer);
673                                 
674                 group_tree = proto_item_add_subtree(grouptf,
675                                                                                         ett_diameter_avp);
676
677                 group_tvb = tvb_new_subset(tvb, offset,
678                                                                    MIN(avpDataLength, tvb_length(tvb)-offset), avpDataLength);
679                 if (group_tree != NULL) {
680                   dissect_avps( group_tvb, pinfo, group_tree);
681                 }
682                 break;
683
684           case DIAMETER_IDENTITY:
685                 proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
686                                                                          tvb, offset, avpDataLength, dataBuffer,
687                                                                          "Identity: %*.*s", (int)avpDataLength, (int)avpDataLength,
688                                                                          dataBuffer);
689                 break;
690           case DIAMETER_UTF8STRING:
691                 proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
692                                                                          tvb, offset, avpDataLength, dataBuffer,
693                                                                          "UTF8String: %*.*s", (int)avpDataLength, (int)avpDataLength,
694                                                                          dataBuffer);
695                 break;
696           case DIAMETER_IP_ADDRESS:
697                 if (avpDataLength == 4) {
698                   guint32 ipv4Address = ntohl((*(guint32*)dataBuffer));
699                   proto_tree_add_ipv4_format(avpi_tree, hf_diameter_avp_data_v4addr,
700                                                                          tvb, offset, avpDataLength, ipv4Address,
701                                                                          "IPv4 Address: %u.%u.%u.%u",
702                                                                          (ipv4Address&0xff000000)>>24,
703                                                                          (ipv4Address&0xff0000)>>16,
704                                                                          (ipv4Address&0xff00)>>8,
705                                                                          (ipv4Address&0xff));
706                 } else if (avpDataLength == 16) {
707                   proto_tree_add_ipv6_format(avpi_tree, hf_diameter_avp_data_v6addr,
708                                                                          tvb, offset, avpDataLength, dataBuffer, 
709                                                                          "IPv6 Address: %04x:%04x:%04x:%04x",
710                                                                          *((guint32*)dataBuffer),
711                                                                          *((guint32*)&dataBuffer[4]),
712                                                                          *((guint32*)&dataBuffer[8]),
713                                                                          *((guint32*)&dataBuffer[12]));
714                 } else {
715                   proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
716                                                                           tvb, offset, avpDataLength, dataBuffer,
717                                                                           "Error!  Bad Address Length");
718                 }
719                 break;
720
721           case DIAMETER_INTEGER32:
722                 {
723                   gint32 data;
724                   memcpy(&data, dataBuffer, 4);
725                   data = ntohl(data);
726                   proto_tree_add_int_format(avpi_tree, hf_diameter_avp_data_int32,
727                                                                         tvb, offset, avpDataLength, data,
728                                                                         "Value: %d", data );
729                 }
730                 break;
731
732           case DIAMETER_UNSIGNED32:
733                 {
734                   guint32 data;
735
736                   memcpy(&data, dataBuffer, 4);
737                   data=ntohl(data);
738                   proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
739                                                                          tvb, offset, avpDataLength, data,
740                                                                          "Value: 0x%08x (%u)", data,
741                                                                          data );
742                 }
743                 break;
744
745           case DIAMETER_INTEGER64:
746                 {
747                   gint64 data;
748                   memcpy(&data, dataBuffer, 8);
749                   /* data = ntohll(data); */
750                   proto_tree_add_int_format(avpi_tree, hf_diameter_avp_data_int64,
751                                                                         tvb, offset, avpDataLength, data,
752                                                                         "Value: 0x%016llx (%lld)", data, data );
753                 }
754                 break;
755           case DIAMETER_UNSIGNED64:
756                 proto_tree_add_item(avpi_tree, hf_diameter_avp_data_uint64, tvb, offset, 8, FALSE);
757                 break;
758
759           case DIAMETER_TIME:
760                 valstr=diameter_time_to_string(dataBuffer);
761
762                 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
763                                                                         tvb, offset, avpDataLength, dataBuffer, "Time: %s", valstr);
764                 break;
765
766           case DIAMETER_ENUMERATED:
767                 {
768                   guint32 data;
769                   
770                   memcpy(&data, dataBuffer, 4);
771                   data = ntohl(data);
772                   valstr = diameter_avp_get_value(avph.avp_code, data);
773                   proto_tree_add_uint_format(avpi_tree, hf_diameter_avp_data_uint32,
774                                                                          tvb, offset, avpDataLength, data,
775                                                                          "Value: 0x%08x (%u): %s", data, data, valstr);
776                 }
777                 break;
778           default:
779           case DIAMETER_OCTET_STRING:
780                 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
781                                                                         tvb, offset, avpDataLength, dataBuffer,
782                                                                         "Hex Data Highlighted Below");
783                 break;
784                                 
785           } /* switch type */
786         } /* avpi_tree != null */
787         offset += (avpLength - hdrLength);
788         offset += fixAmt; /* fix byte alignment */
789   } /* loop */
790 } /* dissect_avps */
791
792
793
794 void
795 proto_reg_handoff_diameter(void)
796 {
797         static int Initialized=FALSE;
798         static int TcpPort=0;
799         static int SctpPort=0;
800
801         if (Initialized) {
802                 dissector_delete("tcp.port", TcpPort, dissect_diameter);
803                 dissector_delete("sctp.port", SctpPort, dissect_diameter);
804         } else {
805                 Initialized=TRUE;
806         }
807
808         /* set port for future deletes */
809         TcpPort=gbl_diameterTcpPort;
810         SctpPort=gbl_diameterSctpPort;
811
812         strcpy(gbl_diameterString, "Diameter Protocol");
813
814         /* g_warning ("Diameter: Adding tcp dissector to port %d",
815                 gbl_diameterTcpPort); */
816         dissector_add("tcp.port", gbl_diameterTcpPort, dissect_diameter,
817             proto_diameter);
818         dissector_add("sctp.port", gbl_diameterSctpPort,
819             dissect_diameter, proto_diameter);
820 }
821
822 /* registration with the filtering engine */
823 void
824 proto_register_diameter(void)
825 {
826
827         static hf_register_info hf[] = {
828                 { &hf_diameter_version,
829                   { "Version", "diameter.version", FT_UINT8, BASE_HEX, NULL, 0x00,
830                     "", HFILL }},
831                 { &hf_diameter_length,
832                   { "Length","diameter.length", FT_UINT24, BASE_DEC, NULL, 0x0,
833                     "", HFILL }},
834
835                 { &hf_diameter_flags,
836                   { "Flags", "diameter.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
837                     "", HFILL }},
838                 { &hf_diameter_flags_request,
839                 { "Request", "diameter.flags.request", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_R,
840                         "", HFILL }},
841                 { &hf_diameter_flags_proxyable,
842                 { "Proxyable", "diameter.flags.proxyable", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_P,
843                         "", HFILL }},
844                 { &hf_diameter_flags_error,
845                 { "Error","diameter.flags.error", FT_BOOLEAN, 8, TFS(&flags_set_truth), DIAM_FLAGS_E,
846                         "", HFILL }},
847                 { &hf_diameter_flags_reserved3,
848                 { "Reserved","diameter.flags.reserved3", FT_BOOLEAN, 8, TFS(&reserved_set),
849                   DIAM_FLAGS_RESERVED3, "", HFILL }},
850                 { &hf_diameter_flags_reserved4,
851                 { "Reserved","diameter.flags.reserved4", FT_BOOLEAN, 8, TFS(&reserved_set),
852                   DIAM_FLAGS_RESERVED4, "", HFILL }},
853                 { &hf_diameter_flags_reserved5,
854                 { "Reserved","diameter.flags.reserved5", FT_BOOLEAN, 8, TFS(&reserved_set),
855                   DIAM_FLAGS_RESERVED5, "", HFILL }},
856                 { &hf_diameter_flags_reserved6,
857                 { "Reserved","diameter.flags.reserved6", FT_BOOLEAN, 8, TFS(&reserved_set),
858                   DIAM_FLAGS_RESERVED6, "", HFILL }},
859                 { &hf_diameter_flags_reserved7,
860                 { "Reserved","diameter.flags.reserved7", FT_BOOLEAN, 8, TFS(&reserved_set),
861                   DIAM_FLAGS_RESERVED7, "", HFILL }},
862
863                 { &hf_diameter_code,
864                   { "Command Code","diameter.code", FT_UINT24, BASE_DEC,
865                     NULL, 0x0, "", HFILL }},
866                 { &hf_diameter_vendor_id,
867                   { "VendorId", "diameter.vendorId", FT_UINT32, BASE_DEC, NULL,
868                         0x0,"", HFILL }},
869                 { &hf_diameter_hopbyhopid,
870                   { "Hop-by-Hop Identifier", "diameter.hopbyhopid", FT_UINT32,
871                     BASE_HEX, NULL, 0x0, "", HFILL }},
872                 { &hf_diameter_endtoendid,
873                   { "End-to-End Identifier", "diameter.endtoendid", FT_UINT32, 
874                     BASE_HEX, NULL, 0x0, "", HFILL }},
875
876                 { &hf_diameter_avp_code,
877                   { "AVP Code","diameter.avp.code", FT_UINT32, BASE_DEC,
878                     NULL, 0x0, "", HFILL }},
879                 { &hf_diameter_avp_length,
880                   { "AVP Length","diameter.avp.length", FT_UINT24, BASE_DEC,
881                     NULL, 0x0, "", HFILL }},
882
883
884                 { &hf_diameter_avp_flags,
885                   { "AVP Flags","diameter.avp.flags", FT_UINT8, BASE_HEX,
886                     NULL, 0x0, "", HFILL }},
887                 { &hf_diameter_avp_flags_vendor_specific,
888                 { "Vendor-Specific", "diameter.flags.vendorspecific", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_V,
889                         "", HFILL }},
890                 { &hf_diameter_avp_flags_mandatory,
891                 { "Mandatory", "diameter.flags.mandatory", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_M,
892                         "", HFILL }},
893                 { &hf_diameter_avp_flags_protected,
894                 { "Protected","diameter.avp.flags.protected", FT_BOOLEAN, 8, TFS(&flags_set_truth), AVP_FLAGS_P,
895                         "", HFILL }},
896                 { &hf_diameter_avp_flags_reserved3,
897                 { "Reserved","diameter.avp.flags.reserved3", FT_BOOLEAN, 8, TFS(&reserved_set),
898                   AVP_FLAGS_RESERVED3,  "", HFILL }},
899                 { &hf_diameter_avp_flags_reserved4,
900                 { "Reserved","diameter.avp.flags.reserved4", FT_BOOLEAN, 8, TFS(&reserved_set),
901                   AVP_FLAGS_RESERVED4,  "", HFILL }},
902                 { &hf_diameter_avp_flags_reserved5,
903                 { "Reserved","diameter.avp.flags.reserved5", FT_BOOLEAN, 8, TFS(&reserved_set),
904                   AVP_FLAGS_RESERVED5,  "", HFILL }},
905                 { &hf_diameter_avp_flags_reserved6,
906                 { "Reserved","diameter.avp.flags.reserved6", FT_BOOLEAN, 8, TFS(&reserved_set),
907                   AVP_FLAGS_RESERVED6,  "", HFILL }},
908                 { &hf_diameter_avp_flags_reserved7,
909                 { "Reserved","diameter.avp.flags.reserved7", FT_BOOLEAN, 8, TFS(&reserved_set),
910                   AVP_FLAGS_RESERVED7,  "", HFILL }},
911                 { &hf_diameter_avp_vendor_id,
912                   { "AVP Vendor Id","diameter.avp.vendorId", FT_UINT32, BASE_DEC,
913                     NULL, 0x0, "", HFILL }},
914                 { &hf_diameter_avp_data_uint64,
915                   { "AVP Data","diameter.avp.data.uint64", FT_UINT64, BASE_DEC,
916                     NULL, 0x0, "", HFILL }},
917                 { &hf_diameter_avp_data_int64,
918                   { "AVP Data","diameter.avp.data.int64", FT_INT32, BASE_DEC,
919                     NULL, 0x0, "", HFILL }},
920                 { &hf_diameter_avp_data_uint32,
921                   { "AVP Data","diameter.avp.data.uint32", FT_UINT32, BASE_DEC,
922                     NULL, 0x0, "", HFILL }},
923                 { &hf_diameter_avp_data_int32,
924                   { "AVP Data","diameter.avp.data.int32", FT_INT32, BASE_DEC,
925                     NULL, 0x0, "", HFILL }},
926                 { &hf_diameter_avp_data_bytes,
927                   { "AVP Data","diameter.avp.data.bytes", FT_BYTES, BASE_NONE,
928                     NULL, 0x0, "", HFILL }},
929
930                 { &hf_diameter_avp_data_string,
931                   { "AVP Data","diameter.avp.data.string", FT_STRING, BASE_NONE,
932                     NULL, 0x0, "", HFILL }},
933                 { &hf_diameter_avp_data_v4addr,
934                   { "AVP Data","diameter.avp.data.v4addr", FT_IPv4, BASE_NONE,
935                     NULL, 0x0, "", HFILL }},
936                 { &hf_diameter_avp_data_v6addr,
937                   { "AVP Data","diameter.avp.data.v6addr", FT_IPv6, BASE_NONE,
938                     NULL, 0x0, "", HFILL }},
939                 { &hf_diameter_avp_data_time,
940                   { "AVP Data","diameter.avp.data.time", FT_ABSOLUTE_TIME, BASE_NONE,
941                     NULL, 0x0, "", HFILL }},
942
943         };
944         static gint *ett[] = {
945                 &ett_diameter,
946                 &ett_diameter_flags,
947                 &ett_diameter_avp,
948                 &ett_diameter_avp_flags,
949                 &ett_diameter_avpinfo
950         };
951         module_t *diameter_module;
952
953         proto_diameter = proto_register_protocol (gbl_diameterString,
954             "DIAMETER", "diameter");
955         proto_register_field_array(proto_diameter, hf, array_length(hf));
956         proto_register_subtree_array(ett, array_length(ett));
957
958         /* Register a configuration option for port */
959         diameter_module = prefs_register_protocol(proto_diameter,
960             proto_reg_handoff_diameter);
961         prefs_register_uint_preference(diameter_module, "tcp.port",
962                                        "DIAMETER TCP Port",
963                                        "Set the TCP port for DIAMETER messages",
964                                        10,
965                                        &gbl_diameterTcpPort);
966         prefs_register_uint_preference(diameter_module, "sctp.port",
967                                        "DIAMETER SCTP Port",
968                                        "Set the SCTP port for DIAMETER messages",
969                                        10,
970                                        &gbl_diameterSctpPort);
971 }