HACK: 2nd try to match RowsetProperties
[wireshark-wip.git] / epan / dissectors / packet-diameter.c
blob8f28b29d6002c15ac3bf048aa9e26ff5558fe122
1 /* packet-diameter.c
2 * Routines for Diameter packet disassembly
4 * $Id$
6 * Copyright (c) 2001 by David Frascone <dave@frascone.com>
7 * Copyright (c) 2007 by Luis E. Garcia Ontanon <luis@ontanon.org>
9 * Support for Request-Answer tracking and Tapping
10 * introduced by Abhik Sarkar
12 * Wireshark - Network traffic analyzer
13 * By Gerald Combs <gerald@wireshark.org>
14 * Copyright 1998 Gerald Combs
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 * References:
30 * 2004-03-11
31 * http://www.ietf.org/rfc/rfc3588.txt
32 * http://www.iana.org/assignments/radius-types
33 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-cc-03.txt
34 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-nasreq-14.txt
35 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-mobileip-16.txt
36 * http://www.ietf.org/internet-drafts/draft-ietf-aaa-diameter-sip-app-01.txt
37 * http://www.ietf.org/html.charters/aaa-charter.html
38 * http://www.iana.org/assignments/address-family-numbers
39 * http://www.iana.org/assignments/enterprise-numbers
40 * http://www.iana.org/assignments/aaa-parameters
43 #include "config.h"
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <errno.h>
49 #include <ctype.h>
51 #include <glib.h>
53 #include <epan/packet.h>
54 #include <epan/exceptions.h>
55 #include <epan/filesystem.h>
56 #include <epan/prefs.h>
57 #include <epan/sminmpec.h>
58 #include <epan/wmem/wmem.h>
59 #include <epan/expert.h>
60 #include <epan/conversation.h>
61 #include <epan/tap.h>
62 #include <epan/exported_pdu.h>
63 #include <epan/diam_dict.h>
64 #include <epan/sctpppids.h>
65 #include <epan/show_exception.h>
66 #include "packet-tcp.h"
67 #include "packet-diameter.h"
69 /* Diameter Header Flags */
70 /* RPETrrrrCCCCCCCCCCCCCCCCCCCCCCCC */
71 #define DIAM_FLAGS_R 0x80
72 #define DIAM_FLAGS_P 0x40
73 #define DIAM_FLAGS_E 0x20
74 #define DIAM_FLAGS_T 0x10
75 #define DIAM_FLAGS_RESERVED4 0x08
76 #define DIAM_FLAGS_RESERVED5 0x04
77 #define DIAM_FLAGS_RESERVED6 0x02
78 #define DIAM_FLAGS_RESERVED7 0x01
79 #define DIAM_FLAGS_RESERVED 0x0f
81 #define DIAM_LENGTH_MASK 0x00ffffffl
82 #define DIAM_COMMAND_MASK DIAM_LENGTH_MASK
83 #define DIAM_GET_FLAGS(dh) ((dh.flagsCmdCode & ~DIAM_COMMAND_MASK) >> 24)
84 #define DIAM_GET_VERSION(dh) ((dh.versionLength & (~DIAM_LENGTH_MASK)) >> 24)
85 #define DIAM_GET_COMMAND(dh) (dh.flagsCmdCode & DIAM_COMMAND_MASK)
86 #define DIAM_GET_LENGTH(dh) (dh.versionLength & DIAM_LENGTH_MASK)
88 /* Diameter AVP Flags */
89 #define AVP_FLAGS_P 0x20
90 #define AVP_FLAGS_V 0x80
91 #define AVP_FLAGS_M 0x40
92 #define AVP_FLAGS_RESERVED3 0x10
93 #define AVP_FLAGS_RESERVED4 0x08
94 #define AVP_FLAGS_RESERVED5 0x04
95 #define AVP_FLAGS_RESERVED6 0x02
96 #define AVP_FLAGS_RESERVED7 0x01
97 #define AVP_FLAGS_RESERVED 0x1f /* 00011111 -- V M P X X X X X */
99 #define DIAMETER_V16 16
100 #define DIAMETER_RFC 1
102 static gint exported_pdu_tap = -1;
104 /* Conversation Info */
105 typedef struct _diameter_conv_info_t {
106 wmem_tree_t *pdus_tree;
107 } diameter_conv_info_t;
109 typedef struct _diam_ctx_t {
110 proto_tree *tree;
111 packet_info *pinfo;
112 wmem_tree_t *avps;
113 gboolean version_rfc;
114 } diam_ctx_t;
116 typedef struct _diam_avp_t diam_avp_t;
117 typedef struct _avp_type_t avp_type_t;
119 typedef const char *(*diam_avp_dissector_t)(diam_ctx_t *, diam_avp_t *, tvbuff_t *, diam_sub_dis_t *);
122 typedef struct _diam_vnd_t {
123 guint32 code;
124 GArray *vs_avps;
125 value_string_ext *vs_avps_ext;
126 GArray *vs_cmds;
127 } diam_vnd_t;
129 struct _diam_avp_t {
130 guint32 code;
131 const diam_vnd_t *vendor;
132 diam_avp_dissector_t dissector_v16;
133 diam_avp_dissector_t dissector_rfc;
135 gint ett;
136 int hf_value;
137 void *type_data;
140 #define VND_AVP_VS(v) ((value_string *)(void *)((v)->vs_avps->data))
141 #define VND_AVP_VS_LEN(v) ((v)->vs_avps->len)
142 #define VND_CMD_VS(v) ((value_string *)(void *)((v)->vs_cmds->data))
144 typedef struct _diam_dictionary_t {
145 wmem_tree_t *avps;
146 wmem_tree_t *vnds;
147 value_string *applications;
148 value_string *commands;
149 } diam_dictionary_t;
151 typedef diam_avp_t *(*avp_constructor_t)(const avp_type_t *, guint32, const diam_vnd_t *, const char *, const value_string *, void *);
153 struct _avp_type_t {
154 const char *name;
155 diam_avp_dissector_t v16;
156 diam_avp_dissector_t rfc;
157 enum ftenum ft;
158 int base;
159 avp_constructor_t build;
162 struct _build_dict {
163 wmem_array_t *hf;
164 GPtrArray *ett;
165 GHashTable *types;
166 GHashTable *avps;
170 typedef struct _address_avp_t {
171 gint ett;
172 int hf_address_type;
173 int hf_ipv4;
174 int hf_ipv6;
175 int hf_other;
176 } address_avp_t;
178 typedef enum {
179 REASEMBLE_NEVER = 0,
180 REASEMBLE_AT_END,
181 REASEMBLE_BY_LENGTH
182 } avp_reassemble_mode_t;
184 typedef struct _proto_avp_t {
185 char *name;
186 dissector_handle_t handle;
187 avp_reassemble_mode_t reassemble_mode;
188 } proto_avp_t;
190 static const char *simple_avp(diam_ctx_t *, diam_avp_t *, tvbuff_t *, diam_sub_dis_t *);
192 static diam_vnd_t unknown_vendor = { 0xffffffff, NULL, NULL, NULL };
193 static diam_vnd_t no_vnd = { 0, NULL, NULL, NULL };
194 static diam_avp_t unknown_avp = {0, &unknown_vendor, simple_avp, simple_avp, -1, -1, NULL };
195 static GArray *all_cmds;
196 static diam_dictionary_t dictionary = { NULL, NULL, NULL, NULL };
197 static struct _build_dict build_dict;
198 static const value_string *vnd_short_vs;
199 static dissector_handle_t data_handle;
200 static dissector_handle_t eap_handle;
202 static const value_string diameter_avp_data_addrfamily_vals[]= {
203 {1,"IPv4"},
204 {2,"IPv6"},
205 {3,"NSAP"},
206 {4,"HDLC"},
207 {5,"BBN"},
208 {6,"IEEE-802"},
209 {7,"E-163"},
210 {8,"E-164"},
211 {9,"F-69"},
212 {10,"X-121"},
213 {11,"IPX"},
214 {12,"Appletalk"},
215 {13,"Decnet4"},
216 {14,"Vines"},
217 {15,"E-164-NSAP"},
218 {16,"DNS"},
219 {17,"DistinguishedName"},
220 {18,"AS"},
221 {19,"XTPoIPv4"},
222 {20,"XTPoIPv6"},
223 {21,"XTPNative"},
224 {22,"FibrePortName"},
225 {23,"FibreNodeName"},
226 {24,"GWID"},
227 {0,NULL}
229 static value_string_ext diameter_avp_data_addrfamily_vals_ext = VALUE_STRING_EXT_INIT(diameter_avp_data_addrfamily_vals);
231 static int proto_diameter = -1;
232 static int hf_diameter_length = -1;
233 static int hf_diameter_code = -1;
234 static int hf_diameter_hopbyhopid =-1;
235 static int hf_diameter_endtoendid =-1;
236 static int hf_diameter_version = -1;
237 static int hf_diameter_vendor_id = -1;
238 static int hf_diameter_application_id = -1;
239 static int hf_diameter_flags = -1;
240 static int hf_diameter_flags_request = -1;
241 static int hf_diameter_flags_proxyable = -1;
242 static int hf_diameter_flags_error = -1;
243 static int hf_diameter_flags_T = -1;
244 static int hf_diameter_flags_reserved4 = -1;
245 static int hf_diameter_flags_reserved5 = -1;
246 static int hf_diameter_flags_reserved6 = -1;
247 static int hf_diameter_flags_reserved7 = -1;
249 static int hf_diameter_avp = -1;
250 static int hf_diameter_avp_len = -1;
251 static int hf_diameter_avp_code = -1;
252 static int hf_diameter_avp_flags = -1;
253 static int hf_diameter_avp_flags_vendor_specific = -1;
254 static int hf_diameter_avp_flags_mandatory = -1;
255 static int hf_diameter_avp_flags_protected = -1;
256 static int hf_diameter_avp_flags_reserved3 = -1;
257 static int hf_diameter_avp_flags_reserved4 = -1;
258 static int hf_diameter_avp_flags_reserved5 = -1;
259 static int hf_diameter_avp_flags_reserved6 = -1;
260 static int hf_diameter_avp_flags_reserved7 = -1;
261 static int hf_diameter_avp_vendor_id = -1;
262 static int hf_diameter_avp_data_wrong_length = -1;
263 static int hf_diameter_avp_pad = -1;
265 static int hf_diameter_answer_in = -1;
266 static int hf_diameter_answer_to = -1;
267 static int hf_diameter_answer_time = -1;
269 /* AVPs with special/extra decoding */
270 static int hf_framed_ipv6_prefix_reserved = -1;
271 static int hf_framed_ipv6_prefix_length = -1;
272 static int hf_framed_ipv6_prefix_bytes = -1;
273 static int hf_framed_ipv6_prefix_ipv6 = -1;
275 static gint ett_diameter = -1;
276 static gint ett_diameter_flags = -1;
277 static gint ett_diameter_avp_flags = -1;
278 static gint ett_diameter_avpinfo = -1;
279 static gint ett_unknown = -1;
280 static gint ett_err = -1;
283 static expert_field ei_diameter_reserved_bit_set = EI_INIT;
284 static expert_field ei_diameter_avp_len = EI_INIT;
285 static expert_field ei_diameter_avp_no_data = EI_INIT;
286 static expert_field ei_diameter_application_id = EI_INIT;
287 static expert_field ei_diameter_version = EI_INIT;
288 static expert_field ei_diameter_avp_pad = EI_INIT;
289 static expert_field ei_diameter_code = EI_INIT;
290 static expert_field ei_diameter_avp_code = EI_INIT;
291 static expert_field ei_diameter_avp_vendor_id = EI_INIT;
293 /* Tap for Diameter */
294 static int diameter_tap = -1;
296 /* For conversations */
298 static dissector_handle_t diameter_udp_handle;
299 static dissector_handle_t diameter_tcp_handle;
300 static dissector_handle_t diameter_sctp_handle;
301 static range_t *global_diameter_tcp_port_range;
302 static range_t *global_diameter_sctp_port_range;
303 static range_t *global_diameter_udp_port_range;
304 /* This is used for TCP and SCTP */
305 #define DEFAULT_DIAMETER_PORT_RANGE "3868"
307 /* desegmentation of Diameter over TCP */
308 static gboolean gbl_diameter_desegment = TRUE;
310 /* Dissector tables */
311 static dissector_table_t diameter_dissector_table;
312 static dissector_table_t diameter_3gpp_avp_dissector_table;
313 static dissector_table_t diameter_ericsson_avp_dissector_table;
315 static const char *avpflags_str[] = {
316 "---",
317 "--P",
318 "-M-",
319 "-MP",
320 "V--",
321 "V-P",
322 "VM-",
323 "VMP",
326 static void
327 export_diameter_pdu(packet_info *pinfo, tvbuff_t *tvb)
329 exp_pdu_data_t *exp_pdu_data;
330 guint32 tags_bit_field;
332 tags_bit_field = EXP_PDU_TAG_IP_SRC_BIT + EXP_PDU_TAG_IP_DST_BIT + EXP_PDU_TAG_SRC_PORT_BIT+
333 EXP_PDU_TAG_DST_PORT_BIT + EXP_PDU_TAG_ORIG_FNO_BIT;
335 exp_pdu_data = load_export_pdu_tags(pinfo, "diameter", -1, tags_bit_field);
337 exp_pdu_data->tvb_length = tvb_length(tvb);
338 exp_pdu_data->pdu_tvb = tvb;
340 tap_queue_packet(exported_pdu_tap, pinfo, exp_pdu_data);
344 static int
345 compare_avps(const void *a, const void *b)
347 const value_string *vsa = (const value_string *)a;
348 const value_string *vsb = (const value_string *)b;
350 if(vsa->value > vsb->value)
351 return 1;
352 if(vsa->value < vsb->value)
353 return -1;
355 return 0;
358 /* Special decoding of some AVPs */
360 static int
361 dissect_diameter_vendor_id(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
363 int offset = 0;
365 proto_tree_add_item(tree, hf_diameter_vendor_id, tvb, 0, 4, ENC_BIG_ENDIAN);
367 offset++;
368 return offset;
371 static int
372 dissect_diameter_eap_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
374 gboolean save_writable;
376 /* Ensure the packet is displayed as Diameter, not EAP */
377 save_writable = col_get_writable(pinfo->cinfo);
378 col_set_writable(pinfo->cinfo, FALSE);
380 call_dissector(eap_handle, tvb, pinfo, tree);
382 col_set_writable(pinfo->cinfo, save_writable);
383 return tvb_length(tvb);
386 /* From RFC 3162 section 2.3 */
387 static int
388 dissect_diameter_base_framed_ipv6_prefix(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
390 guint8 prefix_len, prefix_len_bytes;
392 proto_tree_add_item(tree, hf_framed_ipv6_prefix_reserved, tvb, 0, 1, ENC_BIG_ENDIAN);
393 proto_tree_add_item(tree, hf_framed_ipv6_prefix_length, tvb, 1, 1, ENC_BIG_ENDIAN);
395 prefix_len = tvb_get_guint8(tvb, 1);
396 prefix_len_bytes = prefix_len / 8;
397 if (prefix_len % 8)
398 prefix_len_bytes++;
400 proto_tree_add_item(tree, hf_framed_ipv6_prefix_bytes, tvb, 2, prefix_len_bytes, ENC_NA);
402 /* If we have a fully IPv6 address, display it as such */
403 if (prefix_len_bytes == 16)
404 proto_tree_add_item(tree, hf_framed_ipv6_prefix_ipv6, tvb, 2, prefix_len_bytes, ENC_NA);
406 return(prefix_len_bytes+2);
409 /* Call subdissectors for AVPs.
410 * This is a separate function to avoid having any local variables that might
411 * get clobbered by the exception longjmp() (without having to declare the
412 * variables as volatile and deal with casting them).
414 static void
415 call_avp_subdissector(guint32 vendorid, guint32 code, tvbuff_t *subtvb, packet_info *pinfo, proto_tree *avp_tree, diam_sub_dis_t *diam_sub_dis_inf)
417 TRY {
418 switch (vendorid) {
419 case 0:
420 dissector_try_uint(diameter_dissector_table, code, subtvb, pinfo, avp_tree);
421 break;
422 case VENDOR_ERICSSON:
423 dissector_try_uint(diameter_ericsson_avp_dissector_table, code, subtvb, pinfo, avp_tree);
424 break;
425 case VENDOR_THE3GPP:
426 dissector_try_uint_new(diameter_3gpp_avp_dissector_table, code, subtvb, pinfo, avp_tree, FALSE, diam_sub_dis_inf);
427 break;
428 default:
429 break;
432 /* Debug
433 proto_tree_add_text(avp_tree, subtvb, 0, -1, "AVP %u data, Vendor Id %u ",code,vendorid);
436 CATCH_NONFATAL_ERRORS {
437 show_exception(subtvb, pinfo, avp_tree, EXCEPT_CODE, GET_MESSAGE);
439 ENDTRY;
442 /* Dissect an AVP at offset */
443 static int
444 dissect_diameter_avp(diam_ctx_t *c, tvbuff_t *tvb, int offset, diam_sub_dis_t *diam_sub_dis_inf)
446 guint32 code = tvb_get_ntohl(tvb,offset);
447 guint32 len = tvb_get_ntohl(tvb,offset+4);
448 guint32 vendor_flag = len & 0x80000000;
449 guint32 flags_bits_idx = (len & 0xE0000000) >> 29;
450 guint32 flags_bits = (len & 0xFF000000) >> 24;
451 guint32 vendorid = vendor_flag ? tvb_get_ntohl(tvb,offset+8) : 0 ;
452 wmem_tree_key_t k[] = {
453 {1,&code},
454 {1,&vendorid},
455 {0,NULL}
457 diam_avp_t *a = (diam_avp_t *)wmem_tree_lookup32_array(dictionary.avps,k);
458 proto_item *pi, *avp_item;
459 proto_tree *avp_tree, *save_tree;
460 tvbuff_t *subtvb;
461 diam_vnd_t *vendor;
462 const char *code_str;
463 const char *avp_str;
464 guint8 pad_len;
466 len &= 0x00ffffff;
467 pad_len = (len % 4) ? 4 - (len % 4) : 0 ;
469 if (!a) {
470 a = &unknown_avp;
472 if (vendor_flag) {
473 if (! (vendor = (diam_vnd_t *)wmem_tree_lookup32(dictionary.vnds,vendorid) ))
474 vendor = &unknown_vendor;
475 } else {
476 vendor = &no_vnd;
478 } else {
479 vendor = (diam_vnd_t *)a->vendor;
482 if(vendor->vs_avps_ext == NULL) {
483 g_array_sort(vendor->vs_avps, compare_avps);
484 vendor->vs_avps_ext = value_string_ext_new(VND_AVP_VS(vendor), VND_AVP_VS_LEN(vendor)+1,
485 g_strdup_printf("diameter_vendor_%s",val_to_str_ext_const(vendorid, &sminmpec_values_ext, "Unknown")));
486 #if 0
487 { /* Debug code */
488 value_string *vendor_avp_vs=VALUE_STRING_EXT_VS_P(vendor->vs_avps_ext);
489 gint i = 0;
490 while(vendor_avp_vs[i].strptr!=NULL) {
491 g_warning("%u %s",vendor_avp_vs[i].value,vendor_avp_vs[i].strptr);
492 i++;
495 #endif
498 /* Add root of tree for this AVP */
499 avp_item = proto_tree_add_item(c->tree, hf_diameter_avp, tvb, offset, len + pad_len, ENC_NA);
500 avp_tree = proto_item_add_subtree(avp_item, a->ett);
502 pi = proto_tree_add_item(avp_tree,hf_diameter_avp_code,tvb,offset,4,ENC_BIG_ENDIAN);
503 code_str = val_to_str_ext_const(code, vendor->vs_avps_ext, "Unknown");
504 proto_item_append_text(pi," %s", code_str);
506 /* Code */
507 if (a == &unknown_avp) {
508 proto_tree *tu = proto_item_add_subtree(pi,ett_unknown);
509 proto_tree_add_expert_format(tu, c->pinfo, &ei_diameter_avp_code, tvb, offset, 4,
510 "Unknown AVP %u (vendor=%s), if you know what this is you can add it to dictionary.xml", code,
511 val_to_str_ext_const(vendorid, &sminmpec_values_ext, "Unknown"));
514 offset += 4;
516 proto_item_set_text(avp_item,"AVP: %s(%u) l=%u f=%s", code_str, code, len, avpflags_str[flags_bits_idx]);
518 /* Flags */
519 pi = proto_tree_add_item(avp_tree,hf_diameter_avp_flags,tvb,offset,1,ENC_BIG_ENDIAN);
521 proto_tree *flags_tree = proto_item_add_subtree(pi,ett_diameter_avp_flags);
522 proto_tree_add_item(flags_tree,hf_diameter_avp_flags_vendor_specific,tvb,offset,1,ENC_BIG_ENDIAN);
523 proto_tree_add_item(flags_tree,hf_diameter_avp_flags_mandatory,tvb,offset,1,ENC_BIG_ENDIAN);
524 proto_tree_add_item(flags_tree,hf_diameter_avp_flags_protected,tvb,offset,1,ENC_BIG_ENDIAN);
525 pi = proto_tree_add_item(flags_tree,hf_diameter_avp_flags_reserved3,tvb,offset,1,ENC_BIG_ENDIAN);
526 if(flags_bits & 0x10) expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
527 pi = proto_tree_add_item(flags_tree,hf_diameter_avp_flags_reserved4,tvb,offset,1,ENC_BIG_ENDIAN);
528 if(flags_bits & 0x08) expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
529 pi = proto_tree_add_item(flags_tree,hf_diameter_avp_flags_reserved5,tvb,offset,1,ENC_BIG_ENDIAN);
530 if(flags_bits & 0x04) expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
531 proto_tree_add_item(flags_tree,hf_diameter_avp_flags_reserved6,tvb,offset,1,ENC_BIG_ENDIAN);
532 if(flags_bits & 0x02) expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
533 proto_tree_add_item(flags_tree,hf_diameter_avp_flags_reserved7,tvb,offset,1,ENC_BIG_ENDIAN);
534 if(flags_bits & 0x01) expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
536 offset += 1;
538 /* Length */
539 proto_tree_add_item(avp_tree,hf_diameter_avp_len,tvb,offset,3,ENC_BIG_ENDIAN);
540 offset += 3;
542 /* Vendor flag */
543 if (vendor_flag) {
544 proto_item_append_text(avp_item," vnd=%s", val_to_str(vendorid, vnd_short_vs, "%d"));
545 pi = proto_tree_add_item(avp_tree,hf_diameter_avp_vendor_id,tvb,offset,4,ENC_BIG_ENDIAN);
546 if (vendor == &unknown_vendor) {
547 proto_tree *tu = proto_item_add_subtree(pi,ett_unknown);
548 proto_tree_add_expert(tu, c->pinfo, &ei_diameter_avp_vendor_id, tvb, offset, 4);
550 offset += 4;
553 if ( len == (guint32)(vendor_flag ? 12 : 8) ) {
554 /* Data is empty so return now */
555 proto_tree_add_expert(avp_tree, c->pinfo, &ei_diameter_avp_no_data, tvb, offset, 0);
556 /* pad_len is always 0 in this case, but kept here for consistency */
557 return len+pad_len;
560 subtvb = tvb_new_subset(tvb,offset,len-(8+(vendor_flag?4:0)),len-(8+(vendor_flag?4:0)));
561 offset += len-(8+(vendor_flag?4:0));
563 save_tree = c->tree;
564 c->tree = avp_tree;
565 if (c->version_rfc) {
566 avp_str = a->dissector_rfc(c,a,subtvb, diam_sub_dis_inf);
567 } else {
568 avp_str = a->dissector_v16(c,a,subtvb, diam_sub_dis_inf);
570 c->tree = save_tree;
572 if (avp_str) proto_item_append_text(avp_item," val=%s", avp_str);
574 call_avp_subdissector(vendorid, code, subtvb, c->pinfo, avp_tree, diam_sub_dis_inf);
576 if (pad_len) {
577 guint8 i;
579 pi = proto_tree_add_item(avp_tree, hf_diameter_avp_pad, tvb, offset, pad_len, ENC_NA);
580 for (i=0; i < pad_len; i++) {
581 if (tvb_get_guint8(tvb, offset++) != 0) {
582 expert_add_info(c->pinfo, pi, &ei_diameter_avp_pad);
583 break;
588 return len+pad_len;
591 static const char *
592 address_rfc_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
594 char *label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
595 address_avp_t *t = (address_avp_t *)a->type_data;
596 proto_item *pi = proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length(tvb),ENC_BIG_ENDIAN);
597 proto_tree *pt = proto_item_add_subtree(pi,t->ett);
598 guint32 addr_type = tvb_get_ntohs(tvb,0);
599 gint len = tvb_length_remaining(tvb,2);
601 proto_tree_add_item(pt,t->hf_address_type,tvb,0,2,ENC_NA);
602 switch (addr_type ) {
603 case 1:
604 if (len != 4) {
605 proto_tree_add_expert_format(pt, c->pinfo, &ei_diameter_avp_len, tvb, 2, len, "Wrong length for IPv4 Address: %d instead of 4", len);
606 return "[Malformed]";
608 pi = proto_tree_add_item(pt,t->hf_ipv4,tvb,2,4,ENC_BIG_ENDIAN);
609 break;
610 case 2:
611 if (len != 16) {
612 proto_tree_add_expert_format(pt, c->pinfo, &ei_diameter_avp_len, tvb, 2, len, "Wrong length for IPv6 Address: %d instead of 16", len);
613 return "[Malformed]";
615 pi = proto_tree_add_item(pt,t->hf_ipv6,tvb,2,16,ENC_NA);
616 break;
617 default:
618 pi = proto_tree_add_item(pt,t->hf_other,tvb,2,-1,ENC_BIG_ENDIAN);
619 break;
622 proto_item_fill_label(PITEM_FINFO(pi), label);
623 label = strstr(label,": ")+2;
624 return label;
627 static const char *
628 proto_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf)
630 proto_avp_t *t = (proto_avp_t *)a->type_data;
632 col_set_writable(c->pinfo->cinfo, FALSE);
634 if (!t->handle) {
635 t->handle = find_dissector(t->name);
636 if(!t->handle) t->handle = data_handle;
639 TRY {
640 call_dissector_with_data(t->handle, tvb, c->pinfo, c->tree, diam_sub_dis_inf);
642 CATCH_NONFATAL_ERRORS {
643 show_exception(tvb, c->pinfo, c->tree, EXCEPT_CODE, GET_MESSAGE);
645 ENDTRY;
647 return "";
650 static const char *
651 time_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
653 int len = tvb_length(tvb);
654 char *label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
655 proto_item *pi;
657 if ( len != 4 ) {
658 proto_tree_add_expert_format(c->tree, c->pinfo, &ei_diameter_avp_len, tvb, 0, 4,
659 "Bad Timestamp Length: %d instead of 4", len);
660 return "[Malformed]";
663 pi = proto_tree_add_item(c->tree, (a->hf_value), tvb, 0, 4, ENC_TIME_NTP|ENC_BIG_ENDIAN);
664 proto_item_fill_label(PITEM_FINFO(pi), label);
665 label = strstr(label,": ")+2;
666 return label;
669 static const char *
670 address_v16_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
672 char *label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
673 address_avp_t *t = (address_avp_t *)a->type_data;
674 proto_item *pi = proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length(tvb),ENC_BIG_ENDIAN);
675 proto_tree *pt = proto_item_add_subtree(pi,t->ett);
676 guint32 len = tvb_length(tvb);
678 switch (len) {
679 case 4:
680 pi = proto_tree_add_item(pt,t->hf_ipv4,tvb,0,4,ENC_BIG_ENDIAN);
681 break;
682 case 16:
683 pi = proto_tree_add_item(pt,t->hf_ipv6,tvb,0,16,ENC_NA);
684 break;
685 default:
686 pi = proto_tree_add_item(pt,t->hf_other,tvb,0,len,ENC_BIG_ENDIAN);
687 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
688 "Bad Address Length (%u)", len);
690 break;
693 proto_item_fill_label(PITEM_FINFO(pi), label);
694 label = strstr(label,": ")+2;
695 return label;
698 static const char *
699 simple_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
701 char *label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
702 proto_item *pi = proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length(tvb),ENC_BIG_ENDIAN);
703 proto_item_fill_label(PITEM_FINFO(pi), label);
704 label = strstr(label,": ")+2;
705 return label;
708 static const char *
709 utf8_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
711 char *label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
712 proto_item *pi = proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length(tvb),ENC_UTF_8|ENC_BIG_ENDIAN);
713 proto_item_fill_label(PITEM_FINFO(pi), label);
714 label = strstr(label,": ")+2;
715 return label;
718 static const char *
719 integer32_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
721 char *label;
722 proto_item *pi;
724 /* Verify length before adding */
725 gint length = tvb_length_remaining(tvb,0);
726 if (length == 4) {
727 pi= proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length_remaining(tvb,0),ENC_BIG_ENDIAN);
728 label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
729 proto_item_fill_label(PITEM_FINFO(pi), label);
730 label = strstr(label,": ")+2;
732 else {
733 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
734 tvb, 0, length, NULL,
735 "Error! Bad Integer32 Length");
736 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
737 "Bad Integer32 Length (%u)", length);
738 PROTO_ITEM_SET_GENERATED(pi);
739 label = NULL;
741 return label;
744 static const char *
745 integer64_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
747 char *label;
748 proto_item *pi;
750 /* Verify length before adding */
751 gint length = tvb_length_remaining(tvb,0);
752 if (length == 8) {
753 pi= proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length_remaining(tvb,0),ENC_BIG_ENDIAN);
754 label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
755 proto_item_fill_label(PITEM_FINFO(pi), label);
756 label = strstr(label,": ")+2;
758 else {
759 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
760 tvb, 0, length, NULL,
761 "Error! Bad Integer64 Length");
762 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
763 "Bad Integer64 Length (%u)", length);
764 PROTO_ITEM_SET_GENERATED(pi);
765 label = NULL;
767 return label;
770 static const char *
771 unsigned32_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
773 char *label;
774 proto_item *pi;
776 /* Verify length before adding */
777 gint length = tvb_length_remaining(tvb,0);
778 if (length == 4) {
779 pi= proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length_remaining(tvb,0),ENC_BIG_ENDIAN);
780 label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
781 proto_item_fill_label(PITEM_FINFO(pi), label);
782 label = strstr(label,": ")+2;
784 else {
785 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
786 tvb, 0, length, NULL,
787 "Error! Bad Unsigned32 Length");
788 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
789 "Bad Unsigned32 Length (%u)", length);
790 PROTO_ITEM_SET_GENERATED(pi);
791 label = NULL;
793 return label;
796 static const char *
797 unsigned64_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
799 char *label;
800 proto_item *pi;
802 /* Verify length before adding */
803 gint length = tvb_length_remaining(tvb,0);
804 if (length == 8) {
805 pi= proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length_remaining(tvb,0),ENC_BIG_ENDIAN);
806 label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
807 proto_item_fill_label(PITEM_FINFO(pi), label);
808 label = strstr(label,": ")+2;
810 else {
811 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
812 tvb, 0, length, NULL,
813 "Error! Bad Unsigned64 Length");
814 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
815 "Bad Unsigned64 Length (%u)", length);
816 PROTO_ITEM_SET_GENERATED(pi);
817 label = NULL;
819 return label;
822 static const char *
823 float32_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
825 char *label;
826 proto_item *pi;
828 /* Verify length before adding */
829 gint length = tvb_length_remaining(tvb,0);
830 if (length == 4) {
831 pi= proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length_remaining(tvb,0),ENC_BIG_ENDIAN);
832 label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
833 proto_item_fill_label(PITEM_FINFO(pi), label);
834 label = strstr(label,": ")+2;
836 else {
837 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
838 tvb, 0, length, NULL,
839 "Error! Bad Float32 Length");
840 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
841 "Bad Float32 Length (%u)", length);
842 PROTO_ITEM_SET_GENERATED(pi);
843 label = NULL;
845 return label;
848 static const char *
849 float64_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
851 char *label;
852 proto_item *pi;
854 /* Verify length before adding */
855 gint length = tvb_length_remaining(tvb,0);
856 if (length == 8) {
857 pi= proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length_remaining(tvb,0),ENC_BIG_ENDIAN);
858 label = (char *)wmem_alloc(wmem_packet_scope(), ITEM_LABEL_LENGTH+1);
859 proto_item_fill_label(PITEM_FINFO(pi), label);
860 label = strstr(label,": ")+2;
862 else {
863 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
864 tvb, 0, length, NULL,
865 "Error! Bad Float64 Length");
866 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
867 "Bad Float64 Length (%u)", length);
868 PROTO_ITEM_SET_GENERATED(pi);
869 label = NULL;
871 return label;
874 static const char *
875 grouped_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf)
877 int offset = 0;
878 int len = tvb_length(tvb);
879 proto_item *pi = proto_tree_add_item(c->tree, a->hf_value, tvb , 0 , -1, ENC_BIG_ENDIAN);
880 proto_tree *pt = c->tree;
882 c->tree = proto_item_add_subtree(pi,a->ett);
884 while (offset < len) {
885 offset += dissect_diameter_avp(c, tvb, offset, diam_sub_dis_inf);
888 c->tree = pt;
890 return NULL;
893 static const char *msgflags_str[] = {
894 "----", "---T", "--E-", "--ET",
895 "-P--", "-P-T", "-PE-", "-PET",
896 "R---", "R--T", "R-E-", "R-ET",
897 "RP--", "RP-T", "RPE-", "RPET"
900 static int
901 dissect_diameter_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
903 guint32 first_word = tvb_get_ntohl(tvb,0);
904 guint32 version = (first_word & 0xff000000) >> 24;
905 guint32 flags_bits = (tvb_get_ntohl(tvb,4) & 0xff000000) >> 24;
906 int packet_len = first_word & 0x00ffffff;
907 proto_item *pi, *cmd_item, *app_item, *version_item;
908 proto_tree *diam_tree;
909 diam_ctx_t *c = (diam_ctx_t *)wmem_alloc0(wmem_packet_scope(), sizeof(diam_ctx_t));
910 int offset;
911 value_string *cmd_vs;
912 const char *cmd_str;
913 guint32 cmd = tvb_get_ntoh24(tvb,5);
914 guint32 hop_by_hop_id, end_to_end_id;
915 conversation_t *conversation;
916 diameter_conv_info_t *diameter_conv_info;
917 diameter_req_ans_pair_t *diameter_pair = NULL;
918 wmem_tree_t *pdus_tree;
919 proto_item *it;
920 nstime_t ns;
921 diam_sub_dis_t *diam_sub_dis_inf = wmem_new0(wmem_packet_scope(), diam_sub_dis_t);
924 diam_sub_dis_inf->application_id = tvb_get_ntohl(tvb,8);
926 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DIAMETER");
928 pi = proto_tree_add_item(tree,proto_diameter,tvb,0,-1,ENC_NA);
929 diam_tree = proto_item_add_subtree(pi,ett_diameter);
931 c->tree = diam_tree;
932 c->pinfo = pinfo;
934 version_item = proto_tree_add_item(diam_tree,hf_diameter_version,tvb,0,1,ENC_BIG_ENDIAN);
935 proto_tree_add_item(diam_tree,hf_diameter_length,tvb,1,3,ENC_BIG_ENDIAN);
937 pi = proto_tree_add_item(diam_tree,hf_diameter_flags,tvb,4,1,ENC_BIG_ENDIAN);
939 proto_tree *pt = proto_item_add_subtree(pi,ett_diameter_flags);
940 proto_tree_add_item(pt,hf_diameter_flags_request,tvb,4,1,ENC_BIG_ENDIAN);
941 proto_tree_add_item(pt,hf_diameter_flags_proxyable,tvb,4,1,ENC_BIG_ENDIAN);
942 proto_tree_add_item(pt,hf_diameter_flags_error,tvb,4,1,ENC_BIG_ENDIAN);
943 proto_tree_add_item(pt,hf_diameter_flags_T,tvb,4,1,ENC_BIG_ENDIAN);
944 proto_tree_add_item(pt,hf_diameter_flags_reserved4,tvb,4,1,ENC_BIG_ENDIAN);
945 if(flags_bits & 0x08) expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
946 pi = proto_tree_add_item(pt,hf_diameter_flags_reserved5,tvb,4,1,ENC_BIG_ENDIAN);
947 if(flags_bits & 0x04) expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
948 pi = proto_tree_add_item(pt,hf_diameter_flags_reserved6,tvb,4,1,ENC_BIG_ENDIAN);
949 if(flags_bits & 0x02) expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
950 pi = proto_tree_add_item(pt,hf_diameter_flags_reserved7,tvb,4,1,ENC_BIG_ENDIAN);
951 if(flags_bits & 0x01) expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
954 cmd_item = proto_tree_add_item(diam_tree,hf_diameter_code,tvb,5,3,ENC_BIG_ENDIAN);
956 switch (version) {
957 case DIAMETER_V16: {
958 guint32 vendorid = tvb_get_ntohl(tvb,8);
959 diam_vnd_t *vendor;
961 if (! ( vendor = (diam_vnd_t *)wmem_tree_lookup32(dictionary.vnds,vendorid) ) ) {
962 vendor = &unknown_vendor;
965 cmd_vs = VND_CMD_VS(vendor);
966 proto_tree_add_item(diam_tree, hf_diameter_vendor_id,tvb,8,4,ENC_BIG_ENDIAN);
968 c->version_rfc = FALSE;
969 break;
971 case DIAMETER_RFC: {
973 cmd_vs = (value_string *)(void *)all_cmds->data;
975 app_item = proto_tree_add_item(diam_tree, hf_diameter_application_id, tvb, 8, 4, ENC_BIG_ENDIAN);
977 if (try_val_to_str(diam_sub_dis_inf->application_id, dictionary.applications) == NULL) {
978 proto_tree *tu = proto_item_add_subtree(app_item,ett_unknown);
979 proto_tree_add_expert_format(tu, c->pinfo, &ei_diameter_application_id, tvb, 8, 4,
980 "Unknown Application Id (%u), if you know what this is you can add it to dictionary.xml", diam_sub_dis_inf->application_id);
983 c->version_rfc = TRUE;
984 break;
986 default:
988 proto_tree *pt = proto_item_add_subtree(version_item,ett_err);
989 proto_tree_add_expert(pt, pinfo, &ei_diameter_version, tvb, 0, 1);
990 c->version_rfc = TRUE;
991 cmd_vs = VND_CMD_VS(&no_vnd);
992 break;
995 cmd_str = val_to_str_const(cmd, cmd_vs, "Unknown");
997 col_add_fstr(pinfo->cinfo, COL_INFO,
998 "cmd=%s%s(%d) flags=%s %s=%s(%d) h2h=%x e2e=%x",
999 cmd_str,
1000 ((flags_bits>>4)&0x08) ? " Request" : " Answer",
1001 cmd,
1002 msgflags_str[((flags_bits>>4)&0x0f)],
1003 c->version_rfc ? "appl" : "vend",
1004 val_to_str_const(diam_sub_dis_inf->application_id, c->version_rfc ? dictionary.applications : vnd_short_vs, "Unknown"),
1005 diam_sub_dis_inf->application_id,
1006 tvb_get_ntohl(tvb,12),
1007 tvb_get_ntohl(tvb,16));
1009 col_append_str(pinfo->cinfo, COL_INFO, " | ");
1010 col_set_fence(pinfo->cinfo, COL_INFO);
1012 /* Append name to command item, warn if unknown */
1013 proto_item_append_text(cmd_item," %s", cmd_str);
1014 if (strcmp(cmd_str, "Unknown") == 0) {
1015 proto_tree *tu = proto_item_add_subtree(cmd_item,ett_unknown);
1016 proto_tree_add_expert(tu, c->pinfo, &ei_diameter_code, tvb, 5, 3);
1020 hop_by_hop_id = tvb_get_ntohl(tvb, 12);
1021 proto_tree_add_item(diam_tree,hf_diameter_hopbyhopid,tvb,12,4,ENC_BIG_ENDIAN);
1022 end_to_end_id = tvb_get_ntohl(tvb, 16);
1023 proto_tree_add_item(diam_tree,hf_diameter_endtoendid,tvb,16,4,ENC_BIG_ENDIAN);
1025 /* Conversation tracking stuff */
1027 * FIXME: Looking at epan/conversation.c it seems unlikely that this will work properly in
1028 * multi-homed SCTP connections. This will probably need to be fixed at some point.
1031 conversation = find_or_create_conversation(pinfo);
1033 diameter_conv_info = (diameter_conv_info_t *)conversation_get_proto_data(conversation, proto_diameter);
1034 if (!diameter_conv_info) {
1035 diameter_conv_info = wmem_new(wmem_file_scope(), diameter_conv_info_t);
1036 diameter_conv_info->pdus_tree = wmem_tree_new(wmem_file_scope());
1038 conversation_add_proto_data(conversation, proto_diameter, diameter_conv_info);
1041 /* pdus_tree is an wmem_tree keyed by frame number (in order to handle hop-by-hop collisions */
1042 pdus_tree = (wmem_tree_t *)wmem_tree_lookup32(diameter_conv_info->pdus_tree, hop_by_hop_id);
1044 if (pdus_tree == NULL && (flags_bits & DIAM_FLAGS_R)) {
1045 /* This is the first request we've seen with this hop-by-hop id */
1046 pdus_tree = wmem_tree_new(wmem_file_scope());
1047 wmem_tree_insert32(diameter_conv_info->pdus_tree, hop_by_hop_id, pdus_tree);
1050 if (pdus_tree) {
1051 if (!pinfo->fd->flags.visited) {
1052 if (flags_bits & DIAM_FLAGS_R) {
1053 /* This is a request */
1054 diameter_pair = wmem_new(wmem_file_scope(), diameter_req_ans_pair_t);
1055 diameter_pair->hop_by_hop_id = hop_by_hop_id;
1056 diameter_pair->end_to_end_id = end_to_end_id;
1057 diameter_pair->cmd_code = cmd;
1058 diameter_pair->result_code = 0;
1059 diameter_pair->cmd_str = cmd_str;
1060 diameter_pair->req_frame = PINFO_FD_NUM(pinfo);
1061 diameter_pair->ans_frame = 0;
1062 diameter_pair->req_time = pinfo->fd->abs_ts;
1063 wmem_tree_insert32(pdus_tree, PINFO_FD_NUM(pinfo), (void *)diameter_pair);
1064 } else {
1065 /* Look for a request which occurs earlier in the trace than this answer. */
1066 diameter_pair = (diameter_req_ans_pair_t *)wmem_tree_lookup32_le(pdus_tree, PINFO_FD_NUM(pinfo));
1068 /* Verify the end-to-end-id matches before declaring a match */
1069 if (diameter_pair && diameter_pair->end_to_end_id == end_to_end_id) {
1070 diameter_pair->ans_frame = PINFO_FD_NUM(pinfo);
1073 } else {
1074 /* Look for a request which occurs earlier in the trace than this answer. */
1075 diameter_pair = (diameter_req_ans_pair_t *)wmem_tree_lookup32_le(pdus_tree, PINFO_FD_NUM(pinfo));
1077 /* If the end-to-end ID doesn't match then this is not the request we were
1078 * looking for.
1080 if (diameter_pair && diameter_pair->end_to_end_id != end_to_end_id)
1081 diameter_pair = NULL;
1085 if (!diameter_pair) {
1086 /* create a "fake" diameter_pair structure */
1087 diameter_pair = (diameter_req_ans_pair_t *)wmem_alloc(wmem_packet_scope(), sizeof(diameter_req_ans_pair_t));
1088 diameter_pair->hop_by_hop_id = hop_by_hop_id;
1089 diameter_pair->cmd_code = cmd;
1090 diameter_pair->result_code = 0;
1091 diameter_pair->cmd_str = cmd_str;
1092 diameter_pair->req_frame = 0;
1093 diameter_pair->ans_frame = 0;
1094 diameter_pair->req_time = pinfo->fd->abs_ts;
1096 diameter_pair->processing_request=(flags_bits & DIAM_FLAGS_R)!=0;
1098 if (tree){
1099 /* print state tracking info in the tree */
1100 if (flags_bits & DIAM_FLAGS_R) {
1101 /* This is a request */
1102 if (diameter_pair->ans_frame) {
1103 it = proto_tree_add_uint(diam_tree, hf_diameter_answer_in,
1104 tvb, 0, 0, diameter_pair->ans_frame);
1105 PROTO_ITEM_SET_GENERATED(it);
1107 } else {
1108 /* This is an answer */
1109 if (diameter_pair->req_frame) {
1110 it = proto_tree_add_uint(diam_tree, hf_diameter_answer_to,
1111 tvb, 0, 0, diameter_pair->req_frame);
1112 PROTO_ITEM_SET_GENERATED(it);
1114 nstime_delta(&ns, &pinfo->fd->abs_ts, &diameter_pair->req_time);
1115 diameter_pair->srt_time = ns;
1116 it = proto_tree_add_time(diam_tree, hf_diameter_answer_time, tvb, 0, 0, &ns);
1117 PROTO_ITEM_SET_GENERATED(it);
1118 /* TODO: Populate result_code in tap record from AVP 268 */
1122 offset = 20;
1124 /* Dissect AVPs until the end of the packet is reached */
1125 while (offset < packet_len) {
1126 offset += dissect_diameter_avp(c, tvb, offset, diam_sub_dis_inf);
1130 /* Handle requests for which no answers were found and
1131 * anawers for which no requests were found in the tap listener.
1132 * In case if you don't need unpaired requests/answers use:
1133 * if(diameter_pair->processing_request || !diameter_pair->req_frame)
1134 * return;
1136 tap_queue_packet(diameter_tap, pinfo, diameter_pair);
1138 if(have_tap_listener(exported_pdu_tap)){
1139 export_diameter_pdu(pinfo,tvb);
1142 return tvb_length(tvb);
1145 static guint
1146 get_diameter_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
1148 /* Get the length of the Diameter packet. */
1149 return tvb_get_ntoh24(tvb, offset + 1);
1152 static gboolean
1153 check_diameter(tvbuff_t *tvb)
1155 if (tvb_length(tvb) < 1)
1156 return FALSE; /* not enough bytes to check the version */
1158 if (tvb_get_guint8(tvb, 0) != 1)
1159 return FALSE; /* not version 1 */
1162 * XXX - fetch length and make sure it's at least MIN_DIAMETER_SIZE?
1163 * Fetch flags and check that none of the DIAM_FLAGS_RESERVED bits
1164 * are set?
1166 return TRUE;
1169 /************************************************/
1170 /* Main dissection function */
1171 static int
1172 dissect_diameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1174 if (!check_diameter(tvb))
1175 return 0;
1176 return dissect_diameter_common(tvb, pinfo, tree, data);
1179 static int
1180 dissect_diameter_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
1182 /* Check if we have the start of a PDU or if this is segment */
1183 if (!check_diameter(tvb)) {
1184 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DIAMETER");
1185 col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
1186 call_dissector(data_handle, tvb, pinfo, tree);
1187 } else {
1188 tcp_dissect_pdus(tvb, pinfo, tree, gbl_diameter_desegment, 4,
1189 get_diameter_pdu_len, dissect_diameter_common, data);
1192 return tvb_length(tvb);
1196 static char *
1197 alnumerize(char *name)
1199 char *r = name;
1200 char *w = name;
1201 char c;
1203 for (;(c = *r); r++) {
1204 if (isalnum((unsigned char)c) || c == '_' || c == '-' || c == '.') {
1205 *(w++) = c;
1209 *w = '\0';
1211 return name;
1215 static guint
1216 reginfo(int *hf_ptr, const char *name, const char *abbr, const char *desc,
1217 enum ftenum ft, base_display_e base, value_string_ext *vs_ext,
1218 guint32 mask)
1220 hf_register_info hf = { hf_ptr, {
1221 name ? wmem_strdup(wmem_epan_scope(), name) : wmem_strdup(wmem_epan_scope(), abbr),
1222 wmem_strdup(wmem_epan_scope(), abbr),
1224 base,
1225 NULL,
1226 mask,
1227 wmem_strdup(wmem_epan_scope(), desc),
1228 HFILL }};
1230 if(vs_ext) {
1231 hf.hfinfo.strings = vs_ext;
1234 wmem_array_append_one(build_dict.hf,hf);
1235 return wmem_array_get_count(build_dict.hf);
1238 static void
1239 basic_avp_reginfo(diam_avp_t *a, const char *name, enum ftenum ft,
1240 base_display_e base, value_string_ext *vs_ext)
1242 hf_register_info hf[] = { { &(a->hf_value),
1243 { NULL, NULL, ft, base, NULL, 0x0,
1244 a->vendor->code ?
1245 wmem_strdup_printf(wmem_epan_scope(), "vendor=%d code=%d", a->vendor->code, a->code)
1246 : wmem_strdup_printf(wmem_epan_scope(), "code=%d", a->code),
1247 HFILL }}
1249 gint *ettp = &(a->ett);
1251 hf->hfinfo.name = wmem_strdup_printf(wmem_epan_scope(), "%s",name);
1252 hf->hfinfo.abbrev = alnumerize(wmem_strdup_printf(wmem_epan_scope(), "diameter.%s",name));
1253 if(vs_ext) {
1254 hf->hfinfo.strings = vs_ext;
1257 wmem_array_append(build_dict.hf,hf,1);
1258 g_ptr_array_add(build_dict.ett,ettp);
1261 static diam_avp_t *
1262 build_address_avp(const avp_type_t *type _U_, guint32 code,
1263 const diam_vnd_t *vendor, const char *name,
1264 const value_string *vs _U_, void *data _U_)
1266 diam_avp_t *a = wmem_new0(wmem_epan_scope(), diam_avp_t);
1267 address_avp_t *t = wmem_new(wmem_epan_scope(), address_avp_t);
1268 gint *ettp = &(t->ett);
1270 a->code = code;
1271 a->vendor = vendor;
1273 * It seems like the radius AVPs 1-255 will use the defs from RADIUS in which case:
1274 * http://www.ietf.org/rfc/rfc2865.txt?number=2865
1275 * Address
1276 * The Address field is four octets. The value 0xFFFFFFFF indicates
1277 * that the NAS Should allow the user to select an address (e.g.
1278 * Negotiated). The value 0xFFFFFFFE indicates that the NAS should
1279 * select an address for the user (e.g. Assigned from a pool of
1280 * addresses kept by the NAS). Other valid values indicate that the
1281 * NAS should use that value as the user's IP address.
1283 * Where as in Diameter:
1284 * RFC3588
1285 * Address
1286 * The Address format is derived from the OctetString AVP Base
1287 * Format. It is a discriminated union, representing, for example a
1288 * 32-bit (IPv4) [IPV4] or 128-bit (IPv6) [IPV6] address, most
1289 * significant octet first. The first two octets of the Address
1290 * AVP represents the AddressType, which contains an Address Family
1291 * defined in [IANAADFAM]. The AddressType is used to discriminate
1292 * the content and format of the remaining octets.
1294 a->dissector_v16 = address_v16_avp;
1295 if (code<256) {
1296 a->dissector_rfc = address_v16_avp;
1297 } else {
1298 a->dissector_rfc = address_rfc_avp;
1300 a->ett = -1;
1301 a->hf_value = -1;
1302 a->type_data = t;
1304 t->ett = -1;
1305 t->hf_address_type = -1;
1306 t->hf_ipv4 = -1;
1307 t->hf_ipv6 = -1;
1308 t->hf_other = -1;
1310 basic_avp_reginfo(a,name,FT_BYTES,BASE_NONE,NULL);
1312 reginfo(&(t->hf_address_type), ep_strdup_printf("%s Address Family",name),
1313 alnumerize(ep_strdup_printf("diameter.%s.addr_family",name)),
1314 NULL, FT_UINT16, (base_display_e)(BASE_DEC|BASE_EXT_STRING), &diameter_avp_data_addrfamily_vals_ext, 0);
1316 reginfo(&(t->hf_ipv4), ep_strdup_printf("%s Address",name),
1317 alnumerize(ep_strdup_printf("diameter.%s.IPv4",name)),
1318 NULL, FT_IPv4, BASE_NONE, NULL, 0);
1320 reginfo(&(t->hf_ipv6), ep_strdup_printf("%s Address",name),
1321 alnumerize(ep_strdup_printf("diameter.%s.IPv6",name)),
1322 NULL, FT_IPv6, BASE_NONE, NULL, 0);
1324 reginfo(&(t->hf_other), ep_strdup_printf("%s Address",name),
1325 alnumerize(ep_strdup_printf("diameter.%s.Bytes",name)),
1326 NULL, FT_BYTES, BASE_NONE, NULL, 0);
1328 g_ptr_array_add(build_dict.ett,ettp);
1330 return a;
1333 static diam_avp_t *
1334 build_proto_avp(const avp_type_t *type _U_, guint32 code,
1335 const diam_vnd_t *vendor, const char *name _U_,
1336 const value_string *vs _U_, void *data)
1338 diam_avp_t *a = (diam_avp_t *)g_malloc0(sizeof(diam_avp_t));
1339 proto_avp_t *t = (proto_avp_t *)g_malloc0(sizeof(proto_avp_t));
1340 gint *ettp = &(a->ett);
1342 a->code = code;
1343 a->vendor = vendor;
1344 a->dissector_v16 = proto_avp;
1345 a->dissector_rfc = proto_avp;
1346 a->ett = -1;
1347 a->hf_value = -2;
1348 a->type_data = t;
1350 t->name = (char *)data;
1351 t->handle = NULL;
1352 t->reassemble_mode = REASEMBLE_NEVER;
1354 g_ptr_array_add(build_dict.ett,ettp);
1356 return a;
1359 static diam_avp_t *
1360 build_simple_avp(const avp_type_t *type, guint32 code, const diam_vnd_t *vendor,
1361 const char *name, const value_string *vs, void *data _U_)
1363 diam_avp_t *a;
1364 value_string_ext *vs_ext = NULL;
1365 base_display_e base;
1366 guint i = 0;
1369 * Only 32-bit or shorter integral types can have a list of values.
1371 base = (base_display_e)type->base;
1372 if (vs != NULL) {
1373 switch (type->ft) {
1375 case FT_UINT8:
1376 case FT_UINT16:
1377 case FT_UINT32:
1378 case FT_INT8:
1379 case FT_INT16:
1380 case FT_INT32:
1381 break;
1383 default:
1384 fprintf(stderr,"Diameter Dictionary: AVP '%s' has a list of values but isn't of a 32-bit or shorter integral type\n",
1385 name);
1386 return NULL;
1388 while (vs[i].strptr) {
1389 i++;
1391 vs_ext = value_string_ext_new((value_string *)vs, i+1, wmem_strdup_printf(wmem_epan_scope(), "%s_vals_ext",name));
1392 base = (base_display_e)(base|BASE_EXT_STRING);
1395 a = (diam_avp_t *)wmem_alloc0(wmem_epan_scope(), sizeof(diam_avp_t));
1396 a->code = code;
1397 a->vendor = vendor;
1398 a->dissector_v16 = type->v16;
1399 a->dissector_rfc = type->rfc;
1400 a->ett = -1;
1401 a->hf_value = -1;
1403 basic_avp_reginfo(a,name,type->ft,base,vs_ext);
1405 return a;
1410 static const avp_type_t basic_types[] = {
1411 {"octetstring" , simple_avp , simple_avp , FT_BYTES , BASE_NONE , build_simple_avp },
1412 {"utf8string" , utf8_avp , utf8_avp , FT_STRING , BASE_NONE , build_simple_avp },
1413 {"grouped" , grouped_avp , grouped_avp , FT_BYTES , BASE_NONE , build_simple_avp },
1414 {"integer32" , integer32_avp , integer32_avp , FT_INT32 , BASE_DEC , build_simple_avp },
1415 {"unsigned32" , unsigned32_avp , unsigned32_avp, FT_UINT32 , BASE_DEC , build_simple_avp },
1416 {"integer64" , integer64_avp , integer64_avp , FT_INT64 , BASE_DEC , build_simple_avp },
1417 {"unsigned64" , unsigned64_avp , unsigned64_avp, FT_UINT64 , BASE_DEC , build_simple_avp },
1418 {"float32" , float32_avp , float32_avp , FT_FLOAT , BASE_NONE , build_simple_avp },
1419 {"float64" , float64_avp , float64_avp , FT_DOUBLE , BASE_NONE , build_simple_avp },
1420 {"ipaddress" , NULL , NULL , FT_NONE , BASE_NONE , build_address_avp },
1421 {"diameteruri" , utf8_avp , utf8_avp , FT_STRING , BASE_NONE , build_simple_avp },
1422 {"diameteridentity" , utf8_avp , utf8_avp , FT_STRING , BASE_NONE , build_simple_avp },
1423 {"ipfilterrule" , utf8_avp , utf8_avp , FT_STRING , BASE_NONE , build_simple_avp },
1424 {"qosfilterrule" , utf8_avp , utf8_avp , FT_STRING , BASE_NONE , build_simple_avp },
1425 {"time" , time_avp , time_avp , FT_ABSOLUTE_TIME , ABSOLUTE_TIME_UTC , build_simple_avp },
1426 {NULL, NULL, NULL, FT_NONE, BASE_NONE, NULL }
1432 * This is like g_str_hash() (as of GLib 2.4.8), but it maps all
1433 * upper-case ASCII characters to their ASCII lower-case equivalents.
1434 * We can't use g_strdown(), as that doesn't do an ASCII mapping;
1435 * in Turkish locales, for example, there are two lower-case "i"s
1436 * and two upper-case "I"s, with and without dots - the ones with
1437 * dots map between each other, as do the ones without dots, so "I"
1438 * doesn't map to "i".
1440 static guint
1441 strcase_hash(gconstpointer key)
1443 const char *p = (const char *)key;
1444 guint h = *p;
1445 char c;
1447 if (h) {
1448 if (h >= 'A' && h <= 'Z')
1449 h = h - 'A' + 'a';
1450 for (p += 1; *p != '\0'; p++) {
1451 c = *p;
1452 if (c >= 'A' && c <= 'Z')
1453 c = c - 'A' + 'a';
1454 h = (h << 5) - h + c;
1458 return h;
1462 * Again, use g_ascii_strcasecmp(), not strcasecmp(), so that only ASCII
1463 * letters are mapped, and they're mapped to the lower-case ASCII
1464 * equivalents.
1466 static gboolean
1467 strcase_equal(gconstpointer ka, gconstpointer kb)
1469 const char *a = (const char *)ka;
1470 const char *b = (const char *)kb;
1471 return g_ascii_strcasecmp(a,b) == 0;
1475 /* Note: Dynamic "value string arrays" (e.g., vs_cmds, vs_avps, ...) are constructed using */
1476 /* "zero-terminated" GArrays so that they will have the same form as standard */
1477 /* value_string arrays created at compile time. Since the last entry in a */
1478 /* value_string array must be {0, NULL}, we are assuming that NULL == 0 (hackish). */
1480 static int
1481 dictionary_load(void)
1483 ddict_t *d;
1484 ddict_application_t *p;
1485 ddict_vendor_t *v;
1486 ddict_cmd_t *c;
1487 ddict_typedefn_t *t;
1488 ddict_avp_t *a;
1489 gboolean do_debug_parser = getenv("WIRESHARK_DEBUG_DIAM_DICT_PARSER") ? TRUE : FALSE;
1490 gboolean do_dump_dict = getenv("WIRESHARK_DUMP_DIAM_DICT") ? TRUE : FALSE;
1491 char *dir = ep_strdup_printf("%s" G_DIR_SEPARATOR_S "diameter" G_DIR_SEPARATOR_S, get_datafile_dir());
1492 const avp_type_t *type;
1493 const avp_type_t *octetstring = &basic_types[0];
1494 diam_avp_t *avp;
1495 GHashTable *vendors = g_hash_table_new(strcase_hash,strcase_equal);
1496 diam_vnd_t *vnd;
1497 GArray *vnd_shrt_arr = g_array_new(TRUE,TRUE,sizeof(value_string));
1499 build_dict.hf = wmem_array_new(wmem_epan_scope(),sizeof(hf_register_info));
1500 build_dict.ett = g_ptr_array_new();
1501 build_dict.types = g_hash_table_new(strcase_hash,strcase_equal);
1502 build_dict.avps = g_hash_table_new(strcase_hash,strcase_equal);
1504 dictionary.vnds = wmem_tree_new(wmem_epan_scope());
1505 dictionary.avps = wmem_tree_new(wmem_epan_scope());
1507 unknown_vendor.vs_cmds = g_array_new(TRUE,TRUE,sizeof(value_string));
1508 unknown_vendor.vs_avps = g_array_new(TRUE,TRUE,sizeof(value_string));
1509 no_vnd.vs_cmds = g_array_new(TRUE,TRUE,sizeof(value_string));
1510 no_vnd.vs_avps = g_array_new(TRUE,TRUE,sizeof(value_string));
1512 all_cmds = g_array_new(TRUE,TRUE,sizeof(value_string));
1514 wmem_tree_insert32(dictionary.vnds,0,&no_vnd);
1515 g_hash_table_insert(vendors,(gchar *)"None",&no_vnd);
1517 /* initialize the types hash with the known basic types */
1518 for (type = basic_types; type->name; type++) {
1519 g_hash_table_insert(build_dict.types,(gchar *)type->name,(void *)type);
1522 /* load the dictionary */
1523 d = ddict_scan(dir,"dictionary.xml",do_debug_parser);
1524 if (d == NULL) {
1525 return 0;
1528 if (do_dump_dict) ddict_print(stdout, d);
1530 /* populate the types */
1531 for (t = d->typedefns; t; t = t->next) {
1532 const avp_type_t *parent = NULL;
1533 /* try to get the parent type */
1535 if (t->name == NULL) {
1536 fprintf(stderr,"Diameter Dictionary: Invalid Type (empty name): parent==%s\n",
1537 t->parent ? t->parent : "(null)");
1538 continue;
1542 if (g_hash_table_lookup(build_dict.types,t->name))
1543 continue;
1545 if (t->parent) {
1546 parent = (avp_type_t *)g_hash_table_lookup(build_dict.types,t->parent);
1549 if (!parent) parent = octetstring;
1551 /* insert the parent type for this type */
1552 g_hash_table_insert(build_dict.types,t->name,(void *)parent);
1555 /* populate the applications */
1556 if ((p = d->applications)) {
1557 GArray *arr = g_array_new(TRUE,TRUE,sizeof(value_string));
1559 for (; p; p = p->next) {
1560 value_string item = {p->code,p->name};
1561 g_array_append_val(arr,item);
1564 dictionary.applications = (value_string *)arr->data;
1565 g_array_free(arr,FALSE);
1568 if ((v = d->vendors)) {
1569 for ( ; v; v = v->next) {
1570 value_string item = {v->code,v->name};
1572 if (v->name == NULL) {
1573 fprintf(stderr,"Diameter Dictionary: Invalid Vendor (empty name): code==%d\n",v->code);
1574 continue;
1577 if (g_hash_table_lookup(vendors,v->name))
1578 continue;
1580 g_array_append_val(vnd_shrt_arr,item);
1582 vnd = wmem_new(wmem_epan_scope(), diam_vnd_t);
1583 vnd->code = v->code;
1584 vnd->vs_cmds = g_array_new(TRUE,TRUE,sizeof(value_string));
1585 vnd->vs_avps = g_array_new(TRUE,TRUE,sizeof(value_string));
1586 vnd->vs_avps_ext = NULL;
1587 wmem_tree_insert32(dictionary.vnds,vnd->code,vnd);
1588 g_hash_table_insert(vendors,v->name,vnd);
1592 vnd_short_vs = (value_string *)vnd_shrt_arr->data;
1593 g_array_free(vnd_shrt_arr,FALSE);
1595 if ((c = d->cmds)) {
1596 for (; c; c = c->next) {
1597 if (c->vendor == NULL) {
1598 fprintf(stderr,"Diameter Dictionary: Invalid Vendor (empty name) for command %s\n",
1599 c->name ? c->name : "(null)");
1600 continue;
1603 if ((vnd = (diam_vnd_t *)g_hash_table_lookup(vendors,c->vendor))) {
1604 value_string item = {c->code,c->name};
1605 g_array_append_val(vnd->vs_cmds,item);
1606 /* Also add to all_cmds as used by RFC version */
1607 g_array_append_val(all_cmds,item);
1608 } else {
1609 fprintf(stderr,"Diameter Dictionary: No Vendor: %s\n",c->vendor);
1615 for (a = d->avps; a; a = a->next) {
1616 ddict_enum_t *e;
1617 value_string *vs = NULL;
1618 const char *vend = a->vendor ? a->vendor : "None";
1619 ddict_xmlpi_t *x;
1620 void *avp_data = NULL;
1622 if (a->name == NULL) {
1623 fprintf(stderr,"Diameter Dictionary: Invalid AVP (empty name)\n");
1624 continue;
1627 if ((vnd = (diam_vnd_t *)g_hash_table_lookup(vendors,vend))) {
1628 value_string vndvs = {a->code,a->name};
1629 g_array_append_val(vnd->vs_avps,vndvs);
1630 } else {
1631 fprintf(stderr,"Diameter Dictionary: No Vendor: %s\n",vend);
1632 vnd = &unknown_vendor;
1635 if ((e = a->enums)) {
1636 wmem_array_t *arr = wmem_array_new(wmem_epan_scope(), sizeof(value_string));
1637 value_string term = {0, NULL};
1639 for (; e; e = e->next) {
1640 value_string item = {e->code,e->name};
1641 wmem_array_append_one(arr,item);
1643 wmem_array_sort(arr, compare_avps);
1644 wmem_array_append_one(arr,term);
1645 vs = (value_string *)wmem_array_get_raw(arr);
1648 type = NULL;
1650 for( x = d->xmlpis; x; x = x->next ) {
1651 if ( (strcase_equal(x->name,"avp-proto") && strcase_equal(x->key,a->name))
1652 || (a->type && strcase_equal(x->name,"type-proto") && strcase_equal(x->key,a->type))
1654 static avp_type_t proto_type = {"proto", proto_avp, proto_avp, FT_UINT32, BASE_HEX, build_proto_avp};
1655 type = &proto_type;
1657 avp_data = x->value;
1658 break;
1662 if ( (!type) && a->type )
1663 type = (avp_type_t *)g_hash_table_lookup(build_dict.types,a->type);
1665 if (!type) type = octetstring;
1667 avp = type->build( type, a->code, vnd, a->name, vs, avp_data);
1668 if (avp != NULL) {
1669 g_hash_table_insert(build_dict.avps, a->name, avp);
1672 wmem_tree_key_t k[] = {
1673 { 1, &(a->code) },
1674 { 1, &(vnd->code) },
1675 { 0 , NULL }
1677 wmem_tree_insert32_array(dictionary.avps,k,avp);
1681 g_hash_table_destroy(build_dict.types);
1682 g_hash_table_destroy(build_dict.avps);
1683 g_hash_table_destroy(vendors);
1685 return 1;
1688 /* registration with the filtering engine */
1689 void proto_reg_handoff_diameter(void);
1692 * This does most of the registration work; see proto_register_diameter()
1693 * for the reason why we split it off.
1695 static void
1696 real_proto_register_diameter(void)
1698 module_t *diameter_module;
1699 expert_module_t* expert_diameter;
1700 guint i, ett_length;
1702 hf_register_info hf_base[] = {
1703 { &hf_diameter_version,
1704 { "Version", "diameter.version", FT_UINT8, BASE_HEX, NULL, 0x00,
1705 NULL, HFILL }},
1706 { &hf_diameter_length,
1707 { "Length","diameter.length", FT_UINT24, BASE_DEC, NULL, 0x0,
1708 NULL, HFILL }},
1709 { &hf_diameter_flags,
1710 { "Flags", "diameter.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
1711 NULL, HFILL }},
1712 { &hf_diameter_flags_request,
1713 { "Request", "diameter.flags.request", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_R,
1714 NULL, HFILL }},
1715 { &hf_diameter_flags_proxyable,
1716 { "Proxyable", "diameter.flags.proxyable", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_P,
1717 NULL, HFILL }},
1718 { &hf_diameter_flags_error,
1719 { "Error","diameter.flags.error", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_E,
1720 NULL, HFILL }},
1721 { &hf_diameter_flags_T,
1722 { "T(Potentially re-transmitted message)","diameter.flags.T", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_T,
1723 NULL, HFILL }},
1724 { &hf_diameter_flags_reserved4,
1725 { "Reserved","diameter.flags.reserved4", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1726 DIAM_FLAGS_RESERVED4, NULL, HFILL }},
1727 { &hf_diameter_flags_reserved5,
1728 { "Reserved","diameter.flags.reserved5", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1729 DIAM_FLAGS_RESERVED5, NULL, HFILL }},
1730 { &hf_diameter_flags_reserved6,
1731 { "Reserved","diameter.flags.reserved6", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1732 DIAM_FLAGS_RESERVED6, NULL, HFILL }},
1733 { &hf_diameter_flags_reserved7,
1734 { "Reserved","diameter.flags.reserved7", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1735 DIAM_FLAGS_RESERVED7, NULL, HFILL }},
1736 { &hf_diameter_vendor_id,
1737 { "VendorId", "diameter.vendorId", FT_UINT32, BASE_DEC|BASE_EXT_STRING, &sminmpec_values_ext,
1738 0x0, NULL, HFILL }},
1739 { &hf_diameter_application_id,
1740 { "ApplicationId", "diameter.applicationId", FT_UINT32, BASE_DEC, VALS(dictionary.applications),
1741 0x0, NULL, HFILL }},
1742 { &hf_diameter_hopbyhopid,
1743 { "Hop-by-Hop Identifier", "diameter.hopbyhopid", FT_UINT32,
1744 BASE_HEX, NULL, 0x0, NULL, HFILL }},
1745 { &hf_diameter_endtoendid,
1746 { "End-to-End Identifier", "diameter.endtoendid", FT_UINT32,
1747 BASE_HEX, NULL, 0x0, NULL, HFILL }},
1748 { &hf_diameter_avp,
1749 { "AVP","diameter.avp", FT_BYTES, BASE_NONE,
1750 NULL, 0x0, NULL, HFILL }},
1751 { &hf_diameter_avp_len,
1752 { "AVP Length","diameter.avp.len", FT_UINT24, BASE_DEC,
1753 NULL, 0x0, NULL, HFILL }},
1754 { &hf_diameter_avp_code,
1755 { "AVP Code", "diameter.avp.code", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
1756 { &hf_diameter_avp_flags,
1757 { "AVP Flags","diameter.avp.flags", FT_UINT8, BASE_HEX,
1758 NULL, 0x0, NULL, HFILL }},
1759 { &hf_diameter_avp_flags_vendor_specific,
1760 { "Vendor-Specific", "diameter.flags.vendorspecific", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AVP_FLAGS_V,
1761 NULL, HFILL }},
1762 { &hf_diameter_avp_flags_mandatory,
1763 { "Mandatory", "diameter.flags.mandatory", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AVP_FLAGS_M,
1764 NULL, HFILL }},
1765 { &hf_diameter_avp_flags_protected,
1766 { "Protected","diameter.avp.flags.protected", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AVP_FLAGS_P,
1767 NULL, HFILL }},
1768 { &hf_diameter_avp_flags_reserved3,
1769 { "Reserved","diameter.avp.flags.reserved3", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1770 AVP_FLAGS_RESERVED3, NULL, HFILL }},
1771 { &hf_diameter_avp_flags_reserved4,
1772 { "Reserved","diameter.avp.flags.reserved4", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1773 AVP_FLAGS_RESERVED4, NULL, HFILL }},
1774 { &hf_diameter_avp_flags_reserved5,
1775 { "Reserved","diameter.avp.flags.reserved5", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1776 AVP_FLAGS_RESERVED5, NULL, HFILL }},
1777 { &hf_diameter_avp_flags_reserved6,
1778 { "Reserved","diameter.avp.flags.reserved6", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1779 AVP_FLAGS_RESERVED6, NULL, HFILL }},
1780 { &hf_diameter_avp_flags_reserved7,
1781 { "Reserved","diameter.avp.flags.reserved7", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1782 AVP_FLAGS_RESERVED7, NULL, HFILL }},
1783 { &hf_diameter_avp_vendor_id,
1784 { "AVP Vendor Id","diameter.avp.vendorId", FT_UINT32, BASE_DEC|BASE_EXT_STRING,
1785 &sminmpec_values_ext, 0x0, NULL, HFILL }},
1786 { &(unknown_avp.hf_value),
1787 { "Value","diameter.avp.unknown", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1788 { &hf_diameter_avp_data_wrong_length,
1789 { "Data","diameter.avp.invalid-data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1790 { &hf_diameter_avp_pad,
1791 { "Padding","diameter.avp.pad", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1792 { &hf_diameter_code,
1793 { "Command Code", "diameter.cmd.code", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
1794 { &hf_diameter_answer_in,
1795 { "Answer In", "diameter.answer_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1796 "The answer to this diameter request is in this frame", HFILL }},
1797 { &hf_diameter_answer_to,
1798 { "Request In", "diameter.answer_to", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1799 "This is an answer to the diameter request in this frame", HFILL }},
1800 { &hf_diameter_answer_time,
1801 { "Response Time", "diameter.resp_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1802 "The time between the request and the answer", HFILL }},
1803 { &hf_framed_ipv6_prefix_reserved,
1804 { "Framed IPv6 Prefix Reserved byte", "diameter.framed_ipv6_prefix_reserved",
1805 FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1806 { &hf_framed_ipv6_prefix_length,
1807 { "Framed IPv6 Prefix length (in bits)", "diameter.framed_ipv6_prefix_length",
1808 FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
1809 { &hf_framed_ipv6_prefix_bytes,
1810 { "Framed IPv6 Prefix as a bytestring", "diameter.framed_ipv6_prefix_bytes",
1811 FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
1812 { &hf_framed_ipv6_prefix_ipv6,
1813 { "Framed IPv6 Prefix as an IPv6 address", "diameter.framed_ipv6_prefix_ipv6",
1814 FT_IPv6, BASE_NONE, NULL, 0, "This field is present only if the prefix length is 128", HFILL }}
1817 gint *ett_base[] = {
1818 &ett_diameter,
1819 &ett_diameter_flags,
1820 &ett_diameter_avp_flags,
1821 &ett_diameter_avpinfo,
1822 &ett_unknown,
1823 &ett_err,
1824 &(unknown_avp.ett)
1827 static ei_register_info ei[] = {
1828 { &ei_diameter_reserved_bit_set, { "diameter.reserved_bit_set", PI_MALFORMED, PI_WARN, "Reserved bit set", EXPFILL }},
1829 { &ei_diameter_avp_code, { "diameter.avp.code.unknown", PI_UNDECODED, PI_WARN, "Unknown AVP, if you know what this is you can add it to dictionary.xml", EXPFILL }},
1830 { &ei_diameter_avp_vendor_id, { "diameter.unknown_vendor", PI_UNDECODED, PI_WARN, "Unknown Vendor, if you know whose this is you can add it to dictionary.xml", EXPFILL }},
1831 { &ei_diameter_avp_no_data, { "diameter.avp.no_data", PI_UNDECODED, PI_WARN, "Data is empty", EXPFILL }},
1832 { &ei_diameter_avp_pad, { "diameter.avp.pad.non_zero", PI_MALFORMED, PI_NOTE, "Padding is non-zero", EXPFILL }},
1833 { &ei_diameter_avp_len, { "diameter.avp.invalid-len", PI_MALFORMED, PI_WARN, "Wrong length", EXPFILL }},
1834 { &ei_diameter_application_id, { "diameter.applicationId.unknown", PI_UNDECODED, PI_WARN, "Unknown Application Id, if you know what this is you can add it to dictionary.xml", EXPFILL }},
1835 { &ei_diameter_version, { "diameter.version.unknown", PI_UNDECODED, PI_WARN, "Unknown Diameter Version (decoding as RFC 3588)", EXPFILL }},
1836 { &ei_diameter_code, { "diameter.cmd.code.unknown", PI_UNDECODED, PI_WARN, "Unknown command, if you know what this is you can add it to dictionary.xml", EXPFILL }},
1839 wmem_array_append(build_dict.hf, hf_base, array_length(hf_base));
1840 ett_length = array_length(ett_base);
1841 for (i = 0; i < ett_length; i++) {
1842 g_ptr_array_add(build_dict.ett, ett_base[i]);
1845 proto_diameter = proto_register_protocol ("Diameter Protocol", "DIAMETER", "diameter");
1847 proto_register_field_array(proto_diameter, (hf_register_info *)wmem_array_get_raw(build_dict.hf), wmem_array_get_count(build_dict.hf));
1848 proto_register_subtree_array((gint **)build_dict.ett->pdata, build_dict.ett->len);
1849 expert_diameter = expert_register_protocol(proto_diameter);
1850 expert_register_field_array(expert_diameter, ei, array_length(ei));
1852 g_ptr_array_free(build_dict.ett,TRUE);
1854 /* Allow dissector to find be found by name. */
1855 new_register_dissector("diameter", dissect_diameter, proto_diameter);
1857 /* Register dissector table(s) to do sub dissection of AVPs (OctetStrings) */
1858 diameter_dissector_table = register_dissector_table("diameter.base", "DIAMETER_BASE_AVPS", FT_UINT32, BASE_DEC);
1859 diameter_3gpp_avp_dissector_table = register_dissector_table("diameter.3gpp", "DIAMETER_3GPP_AVPS", FT_UINT32, BASE_DEC);
1860 diameter_ericsson_avp_dissector_table = register_dissector_table("diameter.ericsson", "DIAMETER_ERICSSON_AVPS", FT_UINT32, BASE_DEC);
1862 /* Set default TCP ports */
1863 range_convert_str(&global_diameter_tcp_port_range, DEFAULT_DIAMETER_PORT_RANGE, MAX_UDP_PORT);
1864 range_convert_str(&global_diameter_sctp_port_range, DEFAULT_DIAMETER_PORT_RANGE, MAX_SCTP_PORT);
1865 range_convert_str(&global_diameter_udp_port_range, "", MAX_UDP_PORT);
1867 /* Register configuration options for ports */
1868 diameter_module = prefs_register_protocol(proto_diameter,
1869 proto_reg_handoff_diameter);
1871 prefs_register_range_preference(diameter_module, "tcp.ports", "Diameter TCP ports",
1872 "TCP ports to be decoded as Diameter (default: "
1873 DEFAULT_DIAMETER_PORT_RANGE ")",
1874 &global_diameter_tcp_port_range, MAX_UDP_PORT);
1876 prefs_register_range_preference(diameter_module, "sctp.ports",
1877 "Diameter SCTP Ports",
1878 "SCTP ports to be decoded as Diameter (default: "
1879 DEFAULT_DIAMETER_PORT_RANGE ")",
1880 &global_diameter_sctp_port_range, MAX_SCTP_PORT);
1882 /* Desegmentation */
1883 prefs_register_bool_preference(diameter_module, "desegment",
1884 "Reassemble Diameter messages\nspanning multiple TCP segments",
1885 "Whether the Diameter dissector should reassemble messages spanning multiple TCP segments."
1886 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
1887 &gbl_diameter_desegment);
1889 prefs_register_range_preference(diameter_module, "udp.ports", "Diameter UDP ports",
1890 "UDP ports to be decoded as Diameter (default: 0 as Diameter over UDP is nonstandard)",
1891 &global_diameter_udp_port_range, MAX_UDP_PORT);
1894 /* Register some preferences we no longer support, so we can report
1895 * them as obsolete rather than just illegal.
1897 prefs_register_obsolete_preference(diameter_module, "version");
1898 prefs_register_obsolete_preference(diameter_module, "tcp.port");
1899 prefs_register_obsolete_preference(diameter_module, "sctp.port");
1900 prefs_register_obsolete_preference(diameter_module, "command_in_header");
1901 prefs_register_obsolete_preference(diameter_module, "dictionary.name");
1902 prefs_register_obsolete_preference(diameter_module, "dictionary.use");
1903 prefs_register_obsolete_preference(diameter_module, "allow_zero_as_app_id");
1904 prefs_register_obsolete_preference(diameter_module, "suppress_console_output");
1906 /* Register tap */
1907 diameter_tap = register_tap("diameter");
1910 void
1911 proto_register_diameter(void)
1914 * The hf_base[] array for Diameter refers to a variable
1915 * that is set by dictionary_load(), so we need to call
1916 * dictionary_load() before hf_base[] is initialized.
1918 * To ensure that, we call dictionary_load() and then
1919 * call a routine that defines hf_base[] and does all
1920 * the registration work.
1922 dictionary_load();
1923 real_proto_register_diameter();
1924 } /* proto_register_diameter */
1926 void
1927 proto_reg_handoff_diameter(void)
1929 static gboolean Initialized=FALSE;
1930 static range_t *diameter_tcp_port_range;
1931 static range_t *diameter_sctp_port_range;
1932 static range_t *diameter_udp_port_range;
1934 if (!Initialized) {
1935 diameter_sctp_handle = find_dissector("diameter");
1936 diameter_tcp_handle = new_create_dissector_handle(dissect_diameter_tcp,
1937 proto_diameter);
1938 diameter_udp_handle = new_create_dissector_handle(dissect_diameter, proto_diameter);
1939 data_handle = find_dissector("data");
1940 eap_handle = find_dissector("eap");
1942 dissector_add_uint("sctp.ppi", DIAMETER_PROTOCOL_ID, diameter_sctp_handle);
1944 /* Register special decoding for some AVPs */
1945 /* AVP Code: 97 Framed-IPv6-Address */
1946 dissector_add_uint("diameter.base", 97,
1947 new_create_dissector_handle(dissect_diameter_base_framed_ipv6_prefix, proto_diameter));
1948 /* AVP Code: 266 Vendor-Id */
1949 dissector_add_uint("diameter.base", 266,
1950 new_create_dissector_handle(dissect_diameter_vendor_id, proto_diameter));
1951 /* AVP Code: 462 EAP-Payload */
1952 dissector_add_uint("diameter.base", 462,
1953 new_create_dissector_handle(dissect_diameter_eap_payload, proto_diameter));
1954 /* AVP Code: 463 EAP-Reissued-Payload */
1955 dissector_add_uint("diameter.base", 463,
1956 new_create_dissector_handle(dissect_diameter_eap_payload, proto_diameter));
1958 Initialized=TRUE;
1959 } else {
1960 dissector_delete_uint_range("tcp.port", diameter_tcp_port_range, diameter_tcp_handle);
1961 dissector_delete_uint_range("sctp.port", diameter_sctp_port_range, diameter_sctp_handle);
1962 dissector_delete_uint_range("udp.port", diameter_udp_port_range, diameter_udp_handle);
1963 g_free(diameter_tcp_port_range);
1964 g_free(diameter_sctp_port_range);
1965 g_free(diameter_udp_port_range);
1968 /* set port for future deletes */
1969 diameter_tcp_port_range = range_copy(global_diameter_tcp_port_range);
1970 diameter_sctp_port_range = range_copy(global_diameter_sctp_port_range);
1971 diameter_udp_port_range = range_copy(global_diameter_udp_port_range);
1972 dissector_add_uint_range("tcp.port", diameter_tcp_port_range, diameter_tcp_handle);
1973 dissector_add_uint_range("sctp.port", diameter_sctp_port_range, diameter_sctp_handle);
1974 dissector_add_uint_range("udp.port", diameter_udp_port_range, diameter_udp_handle);
1976 exported_pdu_tap = find_tap_id(EXPORT_PDU_TAP_NAME_LAYER_7);