Get rid of a now-unused variable; it's the only variable of type
[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.23 2001/06/18 02:17:45 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 typedef struct _value_value_pair {
51         guint32 val1;
52         guint32 val2;
53 } value_value_pair;
54
55 /* Valid data types */
56 typedef enum {
57         DIAMETER_DATA=1,
58         DIAMETER_STRING,
59         DIAMETER_ADDRESS,
60         DIAMETER_INTEGER32,
61         DIAMETER_INTEGER64,
62         DIAMETER_UNSIGNED32,
63         DIAMETER_UNSIGNED64,
64         DIAMETER_FLOAT32,
65         DIAMETER_FLOAT64,
66         DIAMETER_FLOAT128,
67         DIAMETER_TIME,
68         DIAMETER_GROUPED
69 } diameterDataTypes;
70
71 #include "packet-diameter-defs.h"
72
73 #define  NTP_TIME_DIFF                   (2208988800UL)
74
75 #define TCP_PORT_DIAMETER       1812
76 #define SCTP_PORT_DIAMETER      1812
77
78 static int proto_diameter = -1;
79 static int hf_diameter_length = -1;
80 static int hf_diameter_code = -1;
81 static int hf_diameter_hopbyhopid =-1;
82 static int hf_diameter_endtoendid =-1;
83 static int hf_diameter_reserved = -1;
84 static int hf_diameter_flags = -1;
85 static int hf_diameter_version = -1;
86 static int hf_diameter_vendor_id = -1;
87
88 static int hf_diameter_avp_code = -1;
89 static int hf_diameter_avp_length = -1;
90 static int hf_diameter_avp_reserved = -1;
91 static int hf_diameter_avp_flags = -1;
92 static int hf_diameter_avp_vendor_id = -1;
93
94
95 static int hf_diameter_avp_data_uint32 = -1;
96 static int hf_diameter_avp_data_int32 = -1;
97 #if 0
98 static int hf_diameter_avp_data_uint64 = -1;
99 static int hf_diameter_avp_data_int64 = -1;
100 #endif
101 static int hf_diameter_avp_data_bytes = -1;
102 static int hf_diameter_avp_data_string = -1;
103 static int hf_diameter_avp_data_v4addr = -1;
104 static int hf_diameter_avp_data_v6addr = -1;
105 static int hf_diameter_avp_data_time = -1;
106
107 static gint ett_diameter = -1;
108 static gint ett_diameter_avp = -1;
109 static gint ett_diameter_avpinfo = -1;
110
111 static char gbl_diameterString[200];
112 static int gbl_diameterTcpPort=TCP_PORT_DIAMETER;
113 static int gbl_diameterSctpPort=SCTP_PORT_DIAMETER;
114
115 typedef struct _e_diameterhdr {
116         guint8 reserved;
117         guint8 flagsVer;
118         guint16 pktLength;
119         guint32 hopByHopId;
120         guint32 endToEndId;
121         guint32 commandCode;
122         guint32 vendorId;
123 } e_diameterhdr;
124
125 typedef struct _e_avphdr {
126         guint32 avp_code;
127         guint16 avp_length;
128         guint8  avp_reserved;
129         guint8  avp_flags;
130         guint32 avp_vendorId;           /* optional */
131 } e_avphdr;
132
133 #define AUTHENTICATOR_LENGTH 12
134
135 /* Diameter Header Flags */
136 #define DIAM_FLAGS_E 0x20
137 #define DIAM_FLAGS_I 0x10
138 #define DIAM_FLAGS_R 0x08
139 #define DIAM_FLAGS_RESERVED 0xc0         /* 11000000  -- X X E I R V V V */
140
141 /* Diameter AVP Flags */
142 #define AVP_FLAGS_P 0x0020
143 #define AVP_FLAGS_V 0x0004
144 #define AVP_FLAGS_M 0x0001
145 #define AVP_FLAGS_RESERVED 0xea          /* 11101010  -- X X X P X V X M */
146
147 #define MIN_AVP_SIZE (sizeof(e_avphdr) - sizeof(guint32))
148 #define MIN_DIAMETER_SIZE (sizeof(e_diameterhdr) + MIN_AVP_SIZE)
149
150 static gchar *rd_value_to_str(e_avphdr *avph,const u_char *input, int length);
151 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
152 static guint32 match_numval(guint32 val, const value_value_pair *vs);
153 static gchar *DetermineMessageType(char flagsVer);
154
155 /* Code to actually dissect the packets */
156
157 /*
158  * Main dissector
159  */
160 static void dissect_diameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
161 {
162
163 /* Set up structures needed to add the protocol subtree and manage it */
164         proto_item *ti;
165         tvbuff_t        *avp_tvb;
166         proto_tree *diameter_tree;
167         e_diameterhdr dh;
168         char *codestrval;
169         size_t offset=0;
170         size_t avplength;
171         proto_tree *avp_tree;
172         proto_item *avptf;
173         int BadPacket = FALSE;
174         
175 /* Make entries in Protocol column and Info column on summary display */
176         if (check_col(pinfo->fd, COL_PROTOCOL)) 
177                 col_add_str(pinfo->fd, COL_PROTOCOL, "Diameter");
178         if (check_col(pinfo->fd, COL_INFO)) 
179                 col_clear(pinfo->fd, COL_INFO);
180         
181         /* Copy our header */
182         tvb_memcpy(tvb, (guint8*) &dh, offset, sizeof(dh));
183         
184         /* Fix byte ordering in our static structure */
185         dh.pktLength = ntohs(dh.pktLength);
186         dh.hopByHopId = ntohl(dh.hopByHopId);
187         dh.endToEndId = ntohl(dh.endToEndId);
188         
189         dh.commandCode = ntohl(dh.commandCode);
190         dh.vendorId = ntohl(dh.vendorId);
191         
192         codestrval=  match_strval(dh.commandCode,diameter_command_code_vals);
193         if (codestrval==NULL) {
194                 codestrval="Unknown Command Code";
195         }
196
197         /* Short packet.  Should have at LEAST one avp */
198         if (dh.pktLength < MIN_DIAMETER_SIZE) {
199                 g_warning("DIAMETER: Packet too short: %d bytes less than min size (%d bytes))",
200                         dh.pktLength, MIN_DIAMETER_SIZE);
201                 BadPacket = TRUE;
202         }
203
204         /* And, check our reserved flags/version */
205         if (dh.reserved || (dh.flagsVer & DIAM_FLAGS_RESERVED) ||
206                 ((dh.flagsVer & 0x7) != 1)) {
207                 g_warning("DIAMETER: Bad packet: Bad Flags or Version");
208                 BadPacket = TRUE;
209         }
210
211         if (check_col(pinfo->fd, COL_INFO)) {
212                 col_add_fstr(pinfo->fd, COL_INFO,
213                     "%s%s: %s(%d) vendor=%d (hop-id=%d) (end-id=%d) EIR=%d%d%d",
214                     (BadPacket)?"***** Bad Packet!: ":"",
215                     DetermineMessageType(dh.flagsVer),
216                     codestrval, dh.commandCode, dh.vendorId,
217                     dh.hopByHopId, dh.endToEndId,
218                     (dh.flagsVer & DIAM_FLAGS_E)?1:0,
219                     (dh.flagsVer & DIAM_FLAGS_I)?1:0,
220                     (dh.flagsVer & DIAM_FLAGS_R)?1:0);
221         }
222         
223
224 /* In the interest of speed, if "tree" is NULL, don't do any work not
225    necessary to generate protocol tree items. */
226         if (tree) {
227
228 /* create display subtree for the protocol */
229                 ti = proto_tree_add_item(tree, proto_diameter, tvb, offset, tvb_length(tvb), FALSE);
230                 diameter_tree = proto_item_add_subtree(ti, ett_diameter);
231
232                 /* Reserved */
233                 proto_tree_add_uint(diameter_tree, hf_diameter_reserved, tvb, offset, 1, dh.reserved);
234                 offset +=1;
235
236                 /* Flags */
237                 proto_tree_add_uint_format(diameter_tree,
238                     hf_diameter_flags,
239                     tvb, offset, 1,
240                     dh.flagsVer,
241                     "Packet flags: 0x%02x  E:%d I:%d R:%d (%s)",
242                     (dh.flagsVer&0xf8)>>3,
243                     (dh.flagsVer & DIAM_FLAGS_E)?1:0,
244                     (dh.flagsVer & DIAM_FLAGS_I)?1:0,
245                     (dh.flagsVer & DIAM_FLAGS_R)?1:0,
246                     DetermineMessageType(dh.flagsVer));
247
248                 /* Version */
249                 proto_tree_add_uint(diameter_tree,
250                     hf_diameter_version,
251                     tvb, offset, 1,
252                     dh.flagsVer);
253
254                 offset+=1;
255
256                 
257                 /* Length */
258                 proto_tree_add_uint(diameter_tree,
259                     hf_diameter_length, tvb,
260                     offset, 2, dh.pktLength);
261                 offset +=2;
262
263                 /* Hop-by-hop Identifier */
264                 proto_tree_add_uint(diameter_tree, hf_diameter_hopbyhopid,
265                     tvb, offset, 4, dh.hopByHopId);
266                 offset += 4;
267
268                 /* End-to-end Identifier */
269                 proto_tree_add_uint(diameter_tree, hf_diameter_endtoendid,
270                     tvb, offset, 4, dh.endToEndId);
271                 offset += 4;
272
273                 /* Command Code */
274                 proto_tree_add_uint(diameter_tree, hf_diameter_code,
275                     tvb, offset, 4, dh.commandCode);
276                 offset += 4;
277
278                 /* Vendor Id */
279                 proto_tree_add_uint(diameter_tree,hf_diameter_vendor_id,
280                     tvb, offset, 4,
281                     dh.vendorId);
282                 offset += 4;
283
284                 /* If we have a bad packet, don't bother trying to parse the AVPs */
285                 if (BadPacket) {
286                         return;
287                 }
288
289                 /* Start looking at the AVPS */
290                 /* Make the next tvbuff */
291
292                 /* Update the lengths */
293                 avplength= dh.pktLength - sizeof(e_diameterhdr);
294     
295                 avp_tvb = tvb_new_subset(tvb, offset, -1, avplength);
296                 avptf = proto_tree_add_text(diameter_tree,
297                     tvb, offset, tvb_length(tvb),
298                     "Attribute Value Pairs");
299                 
300                 avp_tree = proto_item_add_subtree(avptf,
301                     ett_diameter_avp);
302                 if (avp_tree != NULL) {
303                         dissect_avps( avp_tvb, pinfo, avp_tree);
304                 }
305         }
306 } /* dissect_diameter */
307
308 /*
309  * This function will dissect the AVPs in a diameter packet.  It handles
310  * all normal types, and even recursively calls itself for grouped AVPs
311  */
312 static void dissect_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *avp_tree)
313 {
314 /* adds the attribute value pairs to the tree */
315         e_avphdr avph;
316         gchar *avptpstrval;
317         gchar *valstr;
318         guint32 vendorId=0;
319         int hdrLength;
320         int fixAmt;
321         proto_tree *avpi_tree;
322         int vendorOffset;
323         size_t offset = 0 ;
324         char dataBuffer[4096];
325         tvbuff_t        *group_tvb;
326         proto_tree *group_tree;
327         proto_item *grouptf;
328         proto_item *avptf;
329         char buffer[1024];
330         int BadPacket = FALSE;
331         
332         size_t packetLength;
333         size_t avpDataLength;
334         int avpType;
335
336         packetLength = tvb_length(tvb);
337
338         /* Check for invalid packet lengths */
339         if (packetLength <= 0) {
340                 proto_tree_add_text(avp_tree, tvb, offset, tvb_length(tvb),
341                     "No Attribute Value Pairs Found");
342                 return;
343         }
344         
345
346         /* Spin around until we run out of packet */
347         while (packetLength > 0 ) {
348                 vendorOffset = 0;
349                 
350                 /* Check for short packet */
351                 if (packetLength < MIN_AVP_SIZE) {
352                         g_warning("DIAMETER: AVP Payload too short: %d bytes less than min size (%d bytes))",
353                                 packetLength, MIN_AVP_SIZE);
354                         BadPacket = TRUE;
355                         /* Don't even bother trying to parse a short packet. */
356                         return;
357                 }
358
359                 /* Copy our header */
360                 tvb_memcpy(tvb, (guint8*) &avph, offset, sizeof(avph));
361
362                 /* Fix the byte ordering */
363                 avph.avp_code = ntohl(avph.avp_code);
364                 avph.avp_length = ntohs(avph.avp_length);
365
366                 /* Dissect our vendor id if it exists  and set hdr length*/
367                 if (avph.avp_flags & AVP_FLAGS_V) {
368                         vendorId = ntohl(avph.avp_vendorId);
369                         /* Vendor id */
370                         hdrLength = sizeof(e_avphdr);
371                 } else {
372                         /* No vendor */
373                         hdrLength = sizeof(e_avphdr) - 
374                             sizeof(guint32);
375                 }
376
377                 /* Check for bad length */
378                 if (avph.avp_length < MIN_AVP_SIZE || 
379                     (avph.avp_length > packetLength)) {
380                         g_warning("DIAMETER: AVP payload size invalid: avp_length: %d bytes,  min: %d bytes,    packetLen: %d",
381                                 avph.avp_length, MIN_AVP_SIZE, packetLength);
382                         BadPacket = TRUE;
383                 }
384
385                 /* Check for bad flags */
386                 if (avph.avp_reserved || 
387                     (avph.avp_flags & AVP_FLAGS_RESERVED)) {
388                         g_warning("DIAMETER: Invalid AVP: avph.avp_reserved = 0x%x, avph.avp_flags = 0x%x, resFl=0x%x",
389                         avph.avp_reserved, avph.avp_flags, AVP_FLAGS_RESERVED);
390                         BadPacket = TRUE;
391                 }
392                 
393                 /*
394                  * Fix byte-alignment (Diameter AVPs are sent on 4 byte
395                  * boundries)
396                  */
397                 fixAmt = 4 - (avph.avp_length % 4);
398                 if (fixAmt == 4) fixAmt = 0;
399
400                 packetLength = packetLength - (avph.avp_length + fixAmt);
401
402                 /* Check for out of bounds */
403                 if (packetLength < 0) {
404                         g_warning("DIAMETER: Bad AVP: Bad new length (%d bytes)",
405                                 packetLength);
406                         BadPacket = TRUE;
407                 }
408
409                 avptpstrval = match_strval(avph.avp_code, diameter_attrib_type_vals);
410                 if (avptpstrval == NULL) avptpstrval="Unknown Type";
411
412                 avptf = proto_tree_add_text(avp_tree, tvb,
413                     offset, avph.avp_length,
414                     "%s(%d) l:0x%x (%d bytes)",
415                     avptpstrval, avph.avp_code, avph.avp_length,
416                     avph.avp_length);
417                 avpi_tree = proto_item_add_subtree(avptf,
418                     ett_diameter_avpinfo);
419
420                 if (avpi_tree !=NULL) {
421                         /* Command Code */
422                         proto_tree_add_uint(avpi_tree, hf_diameter_avp_code,
423                             tvb, offset, 4, avph.avp_code);
424                         offset += 4;
425                 
426                         proto_tree_add_uint(avpi_tree, hf_diameter_avp_length,
427                             tvb, offset, 2, avph.avp_length);
428                         offset += 2;
429
430                         proto_tree_add_uint(avpi_tree, hf_diameter_avp_reserved,
431                             tvb, offset, 1, avph.avp_reserved);
432                         offset += 1;
433
434                         proto_tree_add_uint_format(avpi_tree,
435                             hf_diameter_avp_flags, tvb,
436                             offset, 1, avph.avp_flags,
437                             "Flags: P:%d V:%d M:%d",
438                             (avph.avp_flags & AVP_FLAGS_P)?1:0,
439                             (avph.avp_flags & AVP_FLAGS_V)?1:0,
440                             (avph.avp_flags & AVP_FLAGS_M)?1:0);
441                         offset += 1;
442
443                         if (avph.avp_flags & AVP_FLAGS_V) {
444                                 proto_tree_add_uint(avpi_tree, hf_diameter_avp_vendor_id,
445                                     tvb, offset, 4, avph.avp_vendorId);
446                                 offset += 4;
447                         }
448
449                         avpDataLength = avph.avp_length - hdrLength;
450
451                         /*
452                          * If we've got a bad packet, just highlight the data.  Don't try
453                          * to parse it, and, don't move to next AVP.
454                          */
455                         if (BadPacket) {
456                                 offset -= hdrLength;
457                                 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
458                                     tvb, offset, tvb_length(tvb) - offset, dataBuffer,
459                                     "Bad AVP (Suspect Data Not Dissected)");
460                                 return;
461                         }
462
463                         avpType=match_numval(avph.avp_code, diameter_printinfo);
464                         tvb_memcpy(tvb, (guint8*) dataBuffer, offset, MIN(4095,
465                                        avph.avp_length - hdrLength));
466                         
467                         switch(avpType) {
468                         case DIAMETER_GROUPED:
469                                 sprintf(buffer, "%s Grouped AVPs", avptpstrval);
470                                 /* Recursively call ourselves */
471                                 grouptf = proto_tree_add_text(avpi_tree,
472                                     tvb, offset, tvb_length(tvb),
473                                     buffer);
474                                 
475                                 group_tree = proto_item_add_subtree(grouptf,
476                                     ett_diameter_avp);
477
478                                 group_tvb = tvb_new_subset(tvb, offset,
479                                     MIN(avpDataLength, tvb_length(tvb)-offset), avpDataLength);
480                                 if (group_tree != NULL) {
481                                         dissect_avps( group_tvb, pinfo, group_tree);
482                                 }
483                                 break;
484                                 
485                         case DIAMETER_STRING:
486                                 proto_tree_add_string_format(avpi_tree, hf_diameter_avp_data_string,
487                                     tvb, offset, avpDataLength, dataBuffer,
488                                     "String: %*.*s", (int)avpDataLength, (int)avpDataLength,
489                                     dataBuffer);
490                                 break;
491                         case DIAMETER_ADDRESS:
492                                 if (avpDataLength == 4) {
493                                         guint32 ipv4Address = ntohl((*(guint32*)dataBuffer));
494                                         proto_tree_add_ipv4_format(avpi_tree, hf_diameter_avp_data_v4addr,
495                                             tvb, offset, avpDataLength, ipv4Address,
496                                             "IPv4 Address: %u.%u.%u.%u",
497                                             (ipv4Address&0xff000000)>>24,
498                                             (ipv4Address&0xff0000)>>16,
499                                             (ipv4Address&0xff00)>>8,
500                                             (ipv4Address&0xff));
501                                 } else if (avpDataLength == 16) {
502                                         proto_tree_add_ipv6_format(avpi_tree, hf_diameter_avp_data_v6addr,
503                                             tvb, offset, avpDataLength, dataBuffer, 
504                                             "IPv6 Address: %04x:%04x:%04x:%04x",
505                                             *((guint32*)dataBuffer),
506                                             *((guint32*)&dataBuffer[4]),
507                                             *((guint32*)&dataBuffer[8]),
508                                             *((guint32*)&dataBuffer[12]));
509                                 } else {
510                                         proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
511                                             tvb, offset, avpDataLength, dataBuffer,
512                                             "Error!  Bad Address Length");
513                                 }
514                                 break;
515
516                         case DIAMETER_INTEGER32:
517                         case DIAMETER_UNSIGNED32:
518                         case DIAMETER_INTEGER64:
519                         case DIAMETER_UNSIGNED64:
520                                 valstr=rd_value_to_str(&avph, dataBuffer, offset);
521                                 
522                                 proto_tree_add_int_format(avpi_tree, hf_diameter_avp_data_int32,
523                                     tvb, offset, avpDataLength, (*(guint32*)dataBuffer),
524                                     "Value: %s",  valstr);
525
526                                 break;
527
528                         case DIAMETER_TIME:
529                                 valstr=rd_value_to_str(&avph, dataBuffer, offset);
530
531                                 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
532                                     tvb, offset, avpDataLength, dataBuffer, "Time: %s", valstr);
533                                 break;
534                                 
535                         default:
536                         case DIAMETER_DATA:
537                                 proto_tree_add_bytes_format(avpi_tree, hf_diameter_avp_data_bytes,
538                                     tvb, offset, avpDataLength, dataBuffer,
539                                     "Data");
540                                 break;
541                                 
542                         }
543                         offset += avph.avp_length - hdrLength;
544                 }
545                 offset += fixAmt; /* fix byte alignment */
546         }
547 } /* dissect_avps */
548
549 /* Generic routine to work with value value pairs */
550 static guint32 match_numval(guint32 val, const value_value_pair *vs)
551 {
552   guint32 i = 0;
553
554   while (vs[i].val1) {
555     if (vs[i].val1 == val)
556       return(vs[i].val2);
557     i++;
558   }
559
560   return(0);
561 }
562
563 static gchar *rd_match_strval(guint32 val, const value_string *vs) {
564         gchar           *result;
565         result=match_strval(val,vs);
566         if (result == NULL ) {
567                 result="Undefined";
568         }
569         return result;
570 }
571 static char *customValCheck(int code, int value)
572 {
573         switch (code) {
574         case DIAMETER_ATT_QOS_SERVICE_TYPE:
575                 return rd_match_strval(value, diameter_qos_service_type_vals);
576                 break;
577         case DIAMETER_ATT_SERVICE_TYPE:
578                 return rd_match_strval(value, diameter_service_type_vals);
579                 break;
580         case DIAMETER_ATT_PROHIBIT:
581                 return rd_match_strval(value, diameter_prohibit_vals);
582                 break;
583         case DIAMETER_ATT_PROMPT:
584                 return rd_match_strval(value, diameter_prompt_vals);
585                 break;
586         case DIAMETER_ATT_SOURCE_PORT:
587                 return rd_match_strval(value, diameter_source_port_vals);
588                 break;
589         case DIAMETER_ATT_NAS_PORT_TYPE:
590                 return rd_match_strval(value, diameter_nas_port_type_vals);
591                 break;
592         case DIAMETER_ATT_INTERFACE_ADDRESS:
593                 return rd_match_strval(value, diameter_interface_address_vals);
594                 break;
595         case DIAMETER_ATT_FRAMED_ROUTING:
596                 return rd_match_strval(value, diameter_framed_routing_vals);
597                 break;
598         case DIAMETER_ATT_ARAP_ZONE_ACCESS:
599                 return rd_match_strval(value, diameter_arap_zone_access_vals);
600                 break;
601         case DIAMETER_ATT_ACCT_AUTHENTIC:
602                 return rd_match_strval(value, diameter_acct_authentic_vals);
603                 break;
604         case DIAMETER_ATT_FRAMED_PROTOCOL:
605                 return rd_match_strval(value, diameter_framed_protocol_vals);
606                 break;
607         case DIAMETER_ATT_FRAMED_COMPRESSION:
608                 return rd_match_strval(value, diameter_framed_compression_vals);
609                 break;
610         case DIAMETER_ATT_AUTHENTICATION_TYPE:
611                 return rd_match_strval(value, diameter_authentication_type_vals);
612                 break;
613         case DIAMETER_ATT_ACCT_TERMINATE_CAUSE:
614                 return rd_match_strval(value, diameter_acct_terminate_cause_vals);
615                 break;
616         case DIAMETER_ATT_PROTOCOL:
617                 return rd_match_strval(value, diameter_protocol_vals);
618                 break;
619         case DIAMETER_ATT_DESTINATION_PORT:
620                 return rd_match_strval(value, diameter_destination_port_vals);
621                 break;
622         case DIAMETER_ATT_TERMINATION_ACTION:
623                 return rd_match_strval(value, diameter_termination_action_vals);
624                 break;
625         case DIAMETER_ATT_EXTENSION_ID:
626                 return rd_match_strval(value, diameter_extension_id_vals);
627                 break;
628         case DIAMETER_ATT_MERIT_LAS_CODE:
629                 return rd_match_strval(value, diameter_merit_las_code_vals);
630                 break;
631         case DIAMETER_ATT_LOGIN_SERVICE:
632                 return rd_match_strval(value, diameter_login_service_vals);
633                 break;
634         case DIAMETER_ATT_RSVP_SERVICE_TYPE:
635                 return rd_match_strval(value, diameter_rsvp_service_type_vals);
636                 break;
637         case DIAMETER_ATT_ACCT_STATUS_TYPE:
638                 return rd_match_strval(value, diameter_acct_status_type_vals);
639                 break;
640         }
641
642         return NULL;
643 }
644
645 static gchar *rd_value_to_str(e_avphdr *avph, const u_char *input, int length)
646 {
647         int print_type;
648         guint32 intval;
649         char *valstr;
650         static char buffer[1024];
651
652 /* prints the values of the attribute value pairs into a text buffer */
653         
654         print_type=match_numval(avph->avp_code,diameter_printinfo);
655
656         /* Set the Default */
657         strcpy(buffer, "Unknown Value");
658
659         /* Default begin */
660         switch(print_type)
661                 {
662                 case DIAMETER_INTEGER32:
663                         /* Check for custom values */
664                         intval=pntohl(input);
665                         valstr=customValCheck(avph->avp_code, intval);
666                         if (valstr) {
667                                 sprintf(buffer,"%s (%u)", valstr, intval);
668                         } else {
669                                 sprintf(buffer,"%d", intval);
670                         }
671                         break;
672                 case DIAMETER_UNSIGNED32:
673                         /* Check for custom values */
674                         intval=pntohl(input);
675                         valstr=customValCheck(avph->avp_code, intval);
676                         if (valstr) {
677                                 sprintf(buffer,"%s (%u)", valstr, intval);
678                         } else {
679                                 sprintf(buffer,"%u", intval);
680                         }
681                         break;
682 #ifdef G_HAVE_GINT64
683                 /* XXX - have to handle platforms without 64-bit integral
684                    types.
685                    Have to handle platforms where "%lld" and "%llu"
686                    aren't the right formats to use to print 64-bit integral
687                    types. */
688                 case DIAMETER_INTEGER64:
689                 {
690                         gint64 llval;
691                         llval = pntohll(input);
692                         sprintf(buffer,"%lld", llval);
693                 }
694                         break;
695                 case DIAMETER_UNSIGNED64:
696                 {
697                         guint64 llval;
698                         llval = pntohll(input);
699                         sprintf(buffer,"%llu", llval);
700                 }
701                         break;
702 #endif
703                 case DIAMETER_TIME:
704                 {
705                         struct tm lt;
706                         intval=pntohl(input);
707                         intval -= NTP_TIME_DIFF;
708                         lt=*localtime((time_t *)&intval);
709                         strftime(buffer, 1024, 
710                             "%a, %d %b %Y %H:%M:%S %z",&lt);
711                 }
712                 default:
713                         /* Do nothing */
714                         ;
715                 }
716         return buffer;
717 } /* rd value to str */
718
719 static gchar *
720 DetermineMessageType(char flagsVer)
721 {
722         /* Get rid of version */
723         flagsVer = flagsVer >> 3;
724
725         /* Mask out reserved bits */
726         flagsVer = flagsVer & 0x7;
727
728         switch (flagsVer) {
729         case 0x0: /* Indication */
730                 return "Indication";
731         case 0x4: /* Request */
732                 return "Request";
733         case 0x1: /* Answer */
734                 return "Answer";
735         case 0x6: /* Query */
736                 return "Query";
737         case 0x3: /* Reply */
738                 return "Reply";
739         default:
740                 return "Illegal Command Type";
741         }
742 } /* DetermineMessageType */
743
744
745 void
746 proto_reg_handoff_diameter(void)
747 {
748         static int Initialized=FALSE;
749         static int TcpPort=0;
750         static int SctpPort=0;
751
752         if (Initialized) {
753                 dissector_delete("tcp.port", TcpPort, dissect_diameter);
754                 dissector_delete("sctp.port", SctpPort, dissect_diameter);
755         } else {
756                 Initialized=TRUE;
757         }
758
759         /* set port for future deletes */
760         TcpPort=gbl_diameterTcpPort;
761         SctpPort=gbl_diameterSctpPort;
762
763         strcpy(gbl_diameterString, "Diameter Protocol");
764
765         /* g_warning ("Diameter: Adding tcp dissector to port %d",
766                 gbl_diameterTcpPort); */
767         dissector_add("tcp.port", gbl_diameterTcpPort, dissect_diameter,
768             proto_diameter);
769         dissector_add("sctp.port", gbl_diameterSctpPort,
770             dissect_diameter, proto_diameter);
771 }
772
773 /* registration with the filtering engine */
774 void
775 proto_register_diameter(void)
776 {
777
778         static hf_register_info hf[] = {
779                 { &hf_diameter_reserved,
780                   { "Reserved", "diameter.reserved", FT_UINT8, BASE_HEX, NULL, 0x0,
781                     "Should be zero", HFILL }},
782                 { &hf_diameter_flags,
783                   { "Flags", "diameter.flags", FT_UINT8, BASE_HEX, NULL, 0xf8,
784                     "", HFILL }},
785                 { &hf_diameter_version,
786                   { "Version", "diameter.version", FT_UINT8, BASE_HEX, NULL, 0x07,
787                     "", HFILL }},
788                 { &hf_diameter_length,
789                   { "Length","diameter.length", FT_UINT16, BASE_DEC, NULL, 0x0,
790                     "", HFILL }},
791                 { &hf_diameter_hopbyhopid,
792                   { "Hop-by-Hop Identifier", "diameter.hopbyhopid", FT_UINT32,
793                     BASE_HEX, NULL, 0x0, "", HFILL }},
794                 { &hf_diameter_endtoendid,
795                   { "End-to-End Identifier", "diameter.endtoendid", FT_UINT32, 
796                     BASE_HEX, NULL, 0x0, "", HFILL }},
797                 { &hf_diameter_code,
798                   { "Command Code","diameter.code", FT_UINT32, BASE_DEC,
799                     VALS(diameter_command_code_vals), 0x0, "", HFILL }},
800                 { &hf_diameter_vendor_id,
801                   { "VendorId", "diameter.vendorId", FT_UINT32, BASE_DEC, NULL, 0x0,
802                     "", HFILL }},
803
804                 { &hf_diameter_avp_code,
805                   { "AVP Code","diameter.avp.code", FT_UINT32, BASE_DEC,
806                     VALS(diameter_attrib_type_vals), 0x0, "", HFILL }},
807                 { &hf_diameter_avp_length,
808                   { "AVP length","diameter.avp.length", FT_UINT16, BASE_DEC,
809                     NULL, 0x0, "", HFILL }},
810                 { &hf_diameter_avp_reserved,
811                   { "AVP Reserved","diameter.avp.reserved", FT_UINT8, BASE_HEX,
812                     NULL, 0x0, "Should be Zero", HFILL }},
813                 { &hf_diameter_avp_flags,
814                   { "AVP Flags","diameter.avp.flags", FT_UINT8, BASE_HEX,
815                     NULL, 0x1f, "", HFILL }},
816                 { &hf_diameter_avp_vendor_id,
817                   { "AVP Vendor Id","diameter.avp.vendorId", FT_UINT32, BASE_DEC,
818                     NULL, 0x0, "", HFILL }},
819                 { &hf_diameter_avp_data_uint32,
820                   { "AVP Data","diameter.avp.data.uint32", FT_UINT32, BASE_DEC,
821                     NULL, 0x0, "", HFILL }},
822 #if 0
823                 { &hf_diameter_avp_data_uint64,
824                   { "AVP Data","diameter.avp.data.uint64", FT_UINT64, BASE_DEC,
825                     NULL, 0x0, "", HFILL }},
826 #endif
827                 { &hf_diameter_avp_data_int32,
828                   { "AVP Data","diameter.avp.data.int32", FT_INT32, BASE_DEC,
829                     NULL, 0x0, "", HFILL }},
830 #if 0
831                 { &hf_diameter_avp_data_int64,
832                   { "AVP Data","diameter.avp.data.int64", FT_INT_64, BASE_DEC,
833                     NULL, 0x0, "", HFILL }},
834 #endif
835                 { &hf_diameter_avp_data_bytes,
836                   { "AVP Data","diameter.avp.data.bytes", FT_BYTES, BASE_NONE,
837                     NULL, 0x0, "", HFILL }},
838
839                 { &hf_diameter_avp_data_string,
840                   { "AVP Data","diameter.avp.data.string", FT_STRING, BASE_NONE,
841                     NULL, 0x0, "", HFILL }},
842                 { &hf_diameter_avp_data_v4addr,
843                   { "AVP Data","diameter.avp.data.v4addr", FT_IPv4, BASE_NONE,
844                     NULL, 0x0, "", HFILL }},
845                 { &hf_diameter_avp_data_v6addr,
846                   { "AVP Data","diameter.avp.data.v6addr", FT_IPv6, BASE_NONE,
847                     NULL, 0x0, "", HFILL }},
848                 { &hf_diameter_avp_data_time,
849                   { "AVP Data","diameter.avp.data.time", FT_ABSOLUTE_TIME, BASE_NONE,
850                     NULL, 0x0, "", HFILL }},
851
852         };
853         static gint *ett[] = {
854                 &ett_diameter,
855                 &ett_diameter_avp,
856                 &ett_diameter_avpinfo
857         };
858         module_t *diameter_module;
859
860         proto_diameter = proto_register_protocol (gbl_diameterString,
861             "DIAMETER", "diameter");
862         proto_register_field_array(proto_diameter, hf, array_length(hf));
863         proto_register_subtree_array(ett, array_length(ett));
864
865         /* Register a configuration option for port */
866         diameter_module = prefs_register_protocol(proto_diameter,
867             proto_reg_handoff_diameter);
868         prefs_register_uint_preference(diameter_module, "tcp.port",
869                                        "DIAMETER TCP Port",
870                                        "Set the TCP port for DIAMETER messages",
871                                        10,
872                                        &gbl_diameterTcpPort);
873         prefs_register_uint_preference(diameter_module, "sctp.port",
874                                        "DIAMETER SCTP Port",
875                                        "Set the SCTP port for DIAMETER messages",
876                                        10,
877                                        &gbl_diameterSctpPort);
878 }