2 * Routines for Diameter packet disassembly
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
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
54 #include <epan/filesystem.h>
55 #include <epan/packet.h>
56 #include <epan/prefs.h>
57 #include <epan/sminmpec.h>
58 #include <epan/emem.h>
59 #include <epan/expert.h>
60 #include <epan/conversation.h>
62 #include <epan/diam_dict.h>
63 #include "packet-tcp.h"
64 #include "packet-ntp.h"
65 #include "packet-diameter.h"
67 #define SCTP_PORT_DIAMETER 3868
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 typedef struct _diam_ctx_t {
106 gboolean version_rfc;
109 typedef struct _diam_avp_t diam_avp_t;
110 typedef struct _avp_type_t avp_type_t;
112 typedef const char* (*diam_avp_dissector_t)(diam_ctx_t*, diam_avp_t*, tvbuff_t*);
115 typedef struct _diam_vnd_t {
118 value_string_ext* vs_avps_ext;
124 const diam_vnd_t* vendor;
125 diam_avp_dissector_t dissector_v16;
126 diam_avp_dissector_t dissector_rfc;
133 #define VND_AVP_VS(v) ((value_string*)(void*)((v)->vs_avps->data))
134 #define VND_CMD_VS(v) ((value_string*)(void*)((v)->vs_cmds->data))
136 typedef struct _diam_dictionary_t {
139 value_string* applications;
140 value_string* commands;
143 typedef diam_avp_t* (*avp_constructor_t)(const avp_type_t*, guint32, const diam_vnd_t*, const char*, const value_string*, void*);
147 diam_avp_dissector_t v16;
148 diam_avp_dissector_t rfc;
151 avp_constructor_t build;
162 typedef struct _address_avp_t {
174 } avp_reassemble_mode_t;
176 typedef struct _proto_avp_t {
178 dissector_handle_t handle;
179 avp_reassemble_mode_t reassemble_mode;
182 static const char* simple_avp(diam_ctx_t*, diam_avp_t*, tvbuff_t*);
184 static const value_string no_vs[] = {{0, NULL} };
185 static GArray no_garr = { (void*)no_vs, 1 };
186 static value_string_ext no_vs_avps_ext = { (value_string_match_t) match_strval_ext_init, 0, (void*)no_vs};
187 static diam_vnd_t unknown_vendor = { 0xffffffff, &no_garr, &no_vs_avps_ext, &no_garr };
188 static diam_vnd_t no_vnd = { 0, NULL, NULL, NULL };
189 static diam_avp_t unknown_avp = {0, &unknown_vendor, simple_avp, simple_avp, -1, -1, NULL };
190 static GArray* all_cmds;
191 static diam_dictionary_t dictionary = { NULL, NULL, NULL, NULL };
192 static struct _build_dict build_dict;
193 static const value_string* vnd_short_vs;
194 static dissector_handle_t data_handle;
195 static dissector_handle_t eap_handle;
197 static const value_string diameter_avp_data_addrfamily_vals[]= {
214 {17,"DistinguishedName"},
219 {22,"FibrePortName"},
220 {23,"FibreNodeName"},
225 static int proto_diameter = -1;
226 static int hf_diameter_length = -1;
227 static int hf_diameter_code = -1;
228 static int hf_diameter_hopbyhopid =-1;
229 static int hf_diameter_endtoendid =-1;
230 static int hf_diameter_version = -1;
231 static int hf_diameter_vendor_id = -1;
232 static int hf_diameter_application_id = -1;
233 static int hf_diameter_flags = -1;
234 static int hf_diameter_flags_request = -1;
235 static int hf_diameter_flags_proxyable = -1;
236 static int hf_diameter_flags_error = -1;
237 static int hf_diameter_flags_T = -1;
238 static int hf_diameter_flags_reserved4 = -1;
239 static int hf_diameter_flags_reserved5 = -1;
240 static int hf_diameter_flags_reserved6 = -1;
241 static int hf_diameter_flags_reserved7 = -1;
243 static int hf_diameter_avp = -1;
244 static int hf_diameter_avp_len = -1;
245 static int hf_diameter_avp_code = -1;
246 static int hf_diameter_avp_flags = -1;
247 static int hf_diameter_avp_flags_vendor_specific = -1;
248 static int hf_diameter_avp_flags_mandatory = -1;
249 static int hf_diameter_avp_flags_protected = -1;
250 static int hf_diameter_avp_flags_reserved3 = -1;
251 static int hf_diameter_avp_flags_reserved4 = -1;
252 static int hf_diameter_avp_flags_reserved5 = -1;
253 static int hf_diameter_avp_flags_reserved6 = -1;
254 static int hf_diameter_avp_flags_reserved7 = -1;
255 static int hf_diameter_avp_vendor_id = -1;
256 static int hf_diameter_avp_data_wrong_length = -1;
258 static int hf_diameter_answer_in = -1;
259 static int hf_diameter_answer_to = -1;
260 static int hf_diameter_answer_time = -1;
262 static gint ett_diameter = -1;
263 static gint ett_diameter_flags = -1;
264 static gint ett_diameter_avp_flags = -1;
265 static gint ett_diameter_avpinfo = -1;
266 static gint ett_unknown = -1;
267 static gint ett_err = -1;
269 /* Tap for Diameter */
270 static int diameter_tap = -1;
272 /* For conversations */
275 static guint gbl_diameterSctpPort=SCTP_PORT_DIAMETER;
277 static dissector_handle_t diameter_tcp_handle;
278 static range_t *global_diameter_tcp_port_range;
279 #define DEFAULT_DIAMETER_PORT_RANGE "3868"
281 /* desegmentation of Diameter over TCP */
282 static gboolean gbl_diameter_desegment = TRUE;
284 /* Dissector tables */
285 static dissector_table_t diameter_dissector_table;
286 static dissector_table_t diameter_3gpp_avp_dissector_table;
287 static dissector_table_t diameter_ericsson_avp_dissector_table;
289 static const char* avpflags_str[] = {
301 compare_avps (gconstpointer a, gconstpointer b)
303 value_string* vsa = (value_string*)a;
304 value_string* vsb = (value_string*)b;
306 if(vsa->value > vsb->value)
308 if(vsa->value < vsb->value)
314 /* Special decoding of some AVP:s */
317 dissect_diameter_vedor_id(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_)
322 proto_tree_add_item(tree, hf_diameter_vendor_id, tvb, 0, 4, FALSE);
330 dissect_diameter_eap_payload(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_) {
331 gboolean save_writable;
333 /* Ensure the packet is displayed as Diameter, not EAP */
334 save_writable = col_get_writable(pinfo->cinfo);
335 col_set_writable(pinfo->cinfo, FALSE);
337 call_dissector(eap_handle, tvb, pinfo, tree);
339 col_set_writable(pinfo->cinfo, save_writable);
340 return tvb_length(tvb);
343 /* Dissect an AVP at offset */
345 dissect_diameter_avp(diam_ctx_t* c, tvbuff_t* tvb, int offset)
347 guint32 code = tvb_get_ntohl(tvb,offset);
348 guint32 len = tvb_get_ntohl(tvb,offset+4);
349 guint32 vendor_flag = len & 0x80000000;
350 guint32 flags_bits_idx = (len & 0xE0000000) >> 29;
351 guint32 flags_bits = (len & 0xFF000000) >> 24;
352 guint32 vendorid = vendor_flag ? tvb_get_ntohl(tvb,offset+8) : 0 ;
353 emem_tree_key_t k[] = {
358 diam_avp_t* a = emem_tree_lookup32_array(dictionary.avps,k);
359 proto_item *pi, *avp_item;
360 proto_tree *avp_tree, *save_tree;
362 const diam_vnd_t* vendor;
363 value_string* vendor_avp_vs;
364 const char* code_str;
375 if (! (vendor = emem_tree_lookup32(dictionary.vnds,vendorid) ))
376 vendor = &unknown_vendor;
384 if(vendor->vs_avps_ext->vals == NULL){
385 g_array_sort(vendor->vs_avps, compare_avps);
386 /* Get dictionary of AVPs matching found vendor */
387 vendor_avp_vs = VND_AVP_VS(vendor);
388 vendor->vs_avps_ext->vals = vendor_avp_vs;
391 while(vendor_avp_vs[i].strptr!=NULL){
392 g_warning("%u %s",vendor_avp_vs[i].value,vendor_avp_vs[i].strptr);
397 /* Get dictionary of AVPs matching found vendor */
398 vendor_avp_vs = VND_AVP_VS(vendor);
401 /* Add root of tree for this AVP */
402 avp_item = proto_tree_add_item(c->tree,hf_diameter_avp,tvb,offset,len,FALSE);
403 avp_tree = proto_item_add_subtree(avp_item,a->ett);
405 pi = proto_tree_add_item(avp_tree,hf_diameter_avp_code,tvb,offset,4,FALSE);
406 code_str = val_to_str_ext(code, vendor->vs_avps_ext, "Unknown");
407 proto_item_append_text(pi," %s", code_str);
410 if (a == &unknown_avp) {
411 proto_tree* tu = proto_item_add_subtree(pi,ett_unknown);
412 proto_item* iu = proto_tree_add_text(tu,tvb,offset,4,"Unknown AVP, "
413 "if you know what this is you can add it to dictionary.xml");
414 expert_add_info_format(c->pinfo, iu, PI_UNDECODED, PI_WARN,
415 "Unknown AVP %u (vendor=%s)", code,
416 val_to_str(vendorid, vnd_short_vs, "Unknown"));
417 PROTO_ITEM_SET_GENERATED(iu);
422 proto_item_set_text(avp_item,"AVP: %s(%u) l=%u f=%s", code_str, code, len, avpflags_str[flags_bits_idx]);
425 pi = proto_tree_add_item(avp_tree,hf_diameter_avp_flags,tvb,offset,1,FALSE);
427 proto_tree* flags_tree = proto_item_add_subtree(pi,ett_diameter_avp_flags);
428 proto_tree_add_item(flags_tree,hf_diameter_avp_flags_vendor_specific,tvb,offset,1,FALSE);
429 proto_tree_add_item(flags_tree,hf_diameter_avp_flags_mandatory,tvb,offset,1,FALSE);
430 proto_tree_add_item(flags_tree,hf_diameter_avp_flags_protected,tvb,offset,1,FALSE);
431 pi = proto_tree_add_item(flags_tree,hf_diameter_avp_flags_reserved3,tvb,offset,1,FALSE);
432 if(flags_bits & 0x10) proto_item_set_expert_flags(pi, PI_MALFORMED, PI_WARN);
433 pi = proto_tree_add_item(flags_tree,hf_diameter_avp_flags_reserved4,tvb,offset,1,FALSE);
434 if(flags_bits & 0x08) proto_item_set_expert_flags(pi, PI_MALFORMED, PI_WARN);
435 pi = proto_tree_add_item(flags_tree,hf_diameter_avp_flags_reserved5,tvb,offset,1,FALSE);
436 if(flags_bits & 0x04) proto_item_set_expert_flags(pi, PI_MALFORMED, PI_WARN);
437 proto_tree_add_item(flags_tree,hf_diameter_avp_flags_reserved6,tvb,offset,1,FALSE);
438 if(flags_bits & 0x02) proto_item_set_expert_flags(pi, PI_MALFORMED, PI_WARN);
439 proto_tree_add_item(flags_tree,hf_diameter_avp_flags_reserved7,tvb,offset,1,FALSE);
440 if(flags_bits & 0x01) proto_item_set_expert_flags(pi, PI_MALFORMED, PI_WARN);
445 proto_tree_add_item(avp_tree,hf_diameter_avp_len,tvb,offset,3,FALSE);
450 proto_item_append_text(avp_item," vnd=%s", val_to_str(vendorid, vnd_short_vs, "%d"));
451 pi = proto_tree_add_item(avp_tree,hf_diameter_avp_vendor_id,tvb,offset,4,FALSE);
452 if (vendor == &unknown_vendor) {
453 proto_tree* tu = proto_item_add_subtree(pi,ett_unknown);
454 proto_item* iu = proto_tree_add_text(tu,tvb,offset,4,"Unknown Vendor, "
455 "if you know whose this is you can add it to dictionary.xml");
456 expert_add_info_format(c->pinfo, iu, PI_UNDECODED, PI_WARN, "Unknown Vendor");
457 PROTO_ITEM_SET_GENERATED(iu);
462 if ( len == (guint32)(vendor_flag ? 12 : 8) ) {
463 /* Data is empty so return now */
464 proto_item* iu = proto_tree_add_text(avp_tree,tvb,offset,0,"No data");
465 expert_add_info_format(c->pinfo, iu, PI_UNDECODED, PI_WARN, "Data is empty");
466 PROTO_ITEM_SET_GENERATED(iu);
470 subtvb = tvb_new_subset(tvb,offset,len-(8+(vendor_flag?4:0)),len-(8+(vendor_flag?4:0)));
474 if (c->version_rfc) {
475 avp_str = a->dissector_rfc(c,a,subtvb);
477 avp_str = a->dissector_v16(c,a,subtvb);
481 if (avp_str) proto_item_append_text(avp_item," val=%s", avp_str);
483 /* Call subdissectors for AVP:s */
486 dissector_try_port(diameter_dissector_table, code, subtvb, c->pinfo, avp_tree);
488 case VENDOR_ERICSSON:
489 dissector_try_port(diameter_ericsson_avp_dissector_table, code, subtvb, c->pinfo, avp_tree);
492 dissector_try_port(diameter_3gpp_avp_dissector_table, code, subtvb, c->pinfo, avp_tree);
498 proto_tree_add_text(avp_tree, subtvb, 0, -1, "AVP %u data, Vendor Id %u ",code,vendorid);
505 address_rfc_avp(diam_ctx_t* c, diam_avp_t* a, tvbuff_t* tvb)
507 char* label = ep_alloc(ITEM_LABEL_LENGTH+1);
508 address_avp_t* t = a->type_data;
509 proto_item* pi = proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length(tvb),FALSE);
510 proto_tree* pt = proto_item_add_subtree(pi,t->ett);
511 guint32 addr_type = tvb_get_ntohs(tvb,0);
512 guint32 len = tvb_length_remaining(tvb,2);
514 proto_tree_add_item(pt,t->hf_address_type,tvb,0,2,FALSE);
515 switch (addr_type ) {
518 pi = proto_tree_add_text(pt,tvb,2,len,"Wrong length for IPv4 Address: %d instead of 4",len);
519 expert_add_info_format(c->pinfo, pi, PI_MALFORMED, PI_WARN, "Wrong length for IPv4 Address");
520 return "[Malformed]";
522 pi = proto_tree_add_item(pt,t->hf_ipv4,tvb,2,4,FALSE);
526 pi = proto_tree_add_text(pt,tvb,2,len,"Wrong length for IPv6 Address: %d instead of 16",len);
527 expert_add_info_format(c->pinfo, pi, PI_MALFORMED, PI_WARN, "Wrong length for IPv6 Address");
528 return "[Malformed]";
530 pi = proto_tree_add_item(pt,t->hf_ipv6,tvb,2,16,FALSE);
533 pi = proto_tree_add_item(pt,t->hf_other,tvb,2,-1,FALSE);
534 pt = proto_item_add_subtree(pi,t->ett);
538 proto_item_fill_label(PITEM_FINFO(pi), label);
539 label = strstr(label,": ")+2;
544 proto_avp(diam_ctx_t* c, diam_avp_t* a, tvbuff_t* tvb)
546 proto_avp_t* t = a->type_data;
548 col_set_writable(c->pinfo->cinfo, FALSE);
551 t->handle = find_dissector(t->name);
552 if(!t->handle) t->handle = data_handle;
555 call_dissector(t->handle, tvb, c->pinfo, c->tree);
561 time_avp(diam_ctx_t* c, diam_avp_t* a, tvbuff_t* tvb)
563 int len = tvb_length(tvb);
564 guint8 ntptime[8] = {0,0,0,0,0,0,0,0};
569 proto_item* pi_local = proto_tree_add_text(c->tree, tvb, 0, 4,
570 "Error! AVP value MUST be 4 bytes");
571 expert_add_info_format(c->pinfo, pi_local, PI_MALFORMED, PI_NOTE,
572 "Bad Timestamp Length (%u)", len);
573 return "[Malformed]";
576 pi = proto_tree_add_item(c->tree, (a->hf_value), tvb, 0, 4, FALSE);
577 tvb_memcpy(tvb,ntptime,0,4);
578 label = ntp_fmt_ts(ntptime);
579 proto_item_append_text(pi," %s",label);
584 address_v16_avp(diam_ctx_t* c, diam_avp_t* a, tvbuff_t* tvb)
586 char* label = ep_alloc(ITEM_LABEL_LENGTH+1);
587 address_avp_t* t = a->type_data;
588 proto_item* pi = proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length(tvb),FALSE);
589 proto_tree* pt = proto_item_add_subtree(pi,t->ett);
590 guint32 len = tvb_length(tvb);
594 pi = proto_tree_add_item(pt,t->hf_ipv4,tvb,0,4,FALSE);
597 pi = proto_tree_add_item(pt,t->hf_ipv6,tvb,0,16,FALSE);
600 pi = proto_tree_add_item(pt,t->hf_other,tvb,0,len,FALSE);
601 pt = proto_item_add_subtree(pi,t->ett);
602 expert_add_info_format(c->pinfo, pi, PI_MALFORMED, PI_NOTE,
603 "Bad Address Length (%u)", len);
608 proto_item_fill_label(PITEM_FINFO(pi), label);
609 label = strstr(label,": ")+2;
614 simple_avp(diam_ctx_t* c, diam_avp_t* a, tvbuff_t* tvb)
616 char* label = ep_alloc(ITEM_LABEL_LENGTH+1);
617 proto_item* pi = proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length(tvb),FALSE);
618 proto_item_fill_label(PITEM_FINFO(pi), label);
619 label = strstr(label,": ")+2;
624 unsigned32_avp(diam_ctx_t* c, diam_avp_t* a, tvbuff_t* tvb)
626 char* label = ep_alloc(ITEM_LABEL_LENGTH+1);
629 /* Verify length before adding */
630 gint length = tvb_length_remaining(tvb,0);
632 pi= proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_length_remaining(tvb,0),FALSE);
633 proto_item_fill_label(PITEM_FINFO(pi), label);
634 label = strstr(label,": ")+2;
637 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
639 tvb_get_ptr(tvb, 0, length),
640 "Error! Bad Integer32 Length");
641 expert_add_info_format(c->pinfo, pi, PI_MALFORMED, PI_NOTE,
642 "Bad Integer32 Length (%u)", length);
643 PROTO_ITEM_SET_GENERATED(pi);
649 grouped_avp(diam_ctx_t* c, diam_avp_t* a, tvbuff_t* tvb)
652 int len = tvb_length(tvb);
653 proto_item* pi = proto_tree_add_item(c->tree, a->hf_value, tvb , 0 , -1, FALSE);
654 proto_tree* pt = c->tree;
656 c->tree = proto_item_add_subtree(pi,a->ett);
658 while (offset < len) {
659 offset += dissect_diameter_avp(c, tvb, offset);
660 offset += (offset % 4) ? 4 - (offset % 4) : 0 ;
668 static const char* msgflags_str[] = {
669 "----", "---T", "--E-", "--ET",
670 "-P--", "-P-T", "-PE-", "-PET",
671 "R---", "R--T", "R-E-", "R-ET",
672 "RP--", "RP-T", "RPE-", "RPET"
676 dissect_diameter_common(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree)
678 guint32 first_word = tvb_get_ntohl(tvb,0);
679 guint32 version = (first_word & 0xff000000) >> 24;
680 guint32 flags_bits = (tvb_get_ntohl(tvb,4) & 0xff000000) >> 24;
681 int packet_len = first_word & 0x00ffffff;
682 proto_item *pi, *cmd_item, *app_item, *version_item;
683 proto_tree* diam_tree;
684 diam_ctx_t* c = ep_alloc0(sizeof(diam_ctx_t));
686 value_string* cmd_vs;
688 guint32 cmd = tvb_get_ntoh24(tvb,5);
689 guint32 fourth = tvb_get_ntohl(tvb,8);
690 guint32 hop_by_hop_id = 0;
691 conversation_t *conversation;
692 diameter_conv_info_t *diameter_conv_info;
693 diameter_req_ans_pair_t *diameter_pair;
697 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DIAMETER");
699 pi = proto_tree_add_item(tree,proto_diameter,tvb,0,-1,FALSE);
700 diam_tree = proto_item_add_subtree(pi,ett_diameter);
705 version_item = proto_tree_add_item(diam_tree,hf_diameter_version,tvb,0,1,FALSE);
706 proto_tree_add_item(diam_tree,hf_diameter_length,tvb,1,3,FALSE);
708 pi = proto_tree_add_item(diam_tree,hf_diameter_flags,tvb,4,1,FALSE);
710 proto_tree* pt = proto_item_add_subtree(pi,ett_diameter_flags);
711 proto_tree_add_item(pt,hf_diameter_flags_request,tvb,4,1,FALSE);
712 proto_tree_add_item(pt,hf_diameter_flags_proxyable,tvb,4,1,FALSE);
713 proto_tree_add_item(pt,hf_diameter_flags_error,tvb,4,1,FALSE);
714 proto_tree_add_item(pt,hf_diameter_flags_T,tvb,4,1,FALSE);
715 proto_tree_add_item(pt,hf_diameter_flags_reserved4,tvb,4,1,FALSE);
716 if(flags_bits & 0x08) proto_item_set_expert_flags(pi, PI_MALFORMED, PI_WARN);
717 pi = proto_tree_add_item(pt,hf_diameter_flags_reserved5,tvb,4,1,FALSE);
718 if(flags_bits & 0x04) proto_item_set_expert_flags(pi, PI_MALFORMED, PI_WARN);
719 pi = proto_tree_add_item(pt,hf_diameter_flags_reserved6,tvb,4,1,FALSE);
720 if(flags_bits & 0x02) proto_item_set_expert_flags(pi, PI_MALFORMED, PI_WARN);
721 pi = proto_tree_add_item(pt,hf_diameter_flags_reserved7,tvb,4,1,FALSE);
722 if(flags_bits & 0x01) proto_item_set_expert_flags(pi, PI_MALFORMED, PI_WARN);
725 cmd_item = proto_tree_add_item(diam_tree,hf_diameter_code,tvb,5,3,FALSE);
729 guint32 vendorid = tvb_get_ntohl(tvb,8);
732 if (! ( vendor = emem_tree_lookup32(dictionary.vnds,vendorid) ) ) {
733 vendor = &unknown_vendor;
736 cmd_vs = VND_CMD_VS(vendor);
737 proto_tree_add_item(diam_tree, hf_diameter_vendor_id,tvb,8,4,FALSE);
739 c->version_rfc = FALSE;
743 cmd_vs = (value_string*)(void*)all_cmds->data;
744 app_item = proto_tree_add_item(diam_tree, hf_diameter_application_id,tvb,8,4,FALSE);
745 if (strcmp(val_to_str(tvb_get_ntohl(tvb, 8), dictionary.applications,
746 "Unknown"), "Unknown") == 0) {
747 proto_tree* tu = proto_item_add_subtree(app_item,ett_unknown);
748 proto_item* iu = proto_tree_add_text(tu,tvb, 8 ,4,"Unknown Application Id, "
749 "if you know what this is you can add it to dictionary.xml");
750 expert_add_info_format(c->pinfo, iu, PI_UNDECODED, PI_WARN,
751 "Unknown Application Id (%u)",
752 tvb_get_ntohl(tvb, 8));
753 PROTO_ITEM_SET_GENERATED(iu);
757 c->version_rfc = TRUE;
762 proto_tree* pt = proto_item_add_subtree(version_item,ett_err);
763 proto_item* pi_local = proto_tree_add_text(pt,tvb,0,1,"Unknown Diameter Version (decoding as RFC 3588)");
764 expert_add_info_format(pinfo, pi_local, PI_UNDECODED, PI_WARN, "Unknown Diameter Version");
765 PROTO_ITEM_SET_GENERATED(pi);
766 c->version_rfc = TRUE;
767 cmd_vs = VND_CMD_VS(&no_vnd);
771 cmd_str = val_to_str(cmd, cmd_vs, "Unknown");
773 col_add_fstr(pinfo->cinfo, COL_INFO,
774 "cmd=%s%s(%d) flags=%s %s=%s(%d) h2h=%x e2e=%x",
776 ((flags_bits>>4)&0x08) ? "Request" : "Answer",
778 msgflags_str[((flags_bits>>4)&0x0f)],
779 c->version_rfc ? "appl" : "vend",
780 val_to_str(fourth, c->version_rfc ? dictionary.applications : vnd_short_vs, "Unknown"),
782 tvb_get_ntohl(tvb,12),
783 tvb_get_ntohl(tvb,16));
785 /* Append name to command item, warn if unknown */
786 proto_item_append_text(cmd_item," %s", cmd_str);
787 if (strcmp(cmd_str, "Unknown") == 0) {
788 proto_tree* tu = proto_item_add_subtree(cmd_item,ett_unknown);
789 proto_item* iu = proto_tree_add_text(tu,tvb, 5 ,3,"Unknown command, "
790 "if you know what this is you can add it to dictionary.xml");
791 expert_add_info_format(c->pinfo, iu, PI_UNDECODED, PI_WARN, "Unknown command (%u)", cmd);
792 PROTO_ITEM_SET_GENERATED(iu);
796 hop_by_hop_id = tvb_get_ntohl(tvb, 12);
797 proto_tree_add_item(diam_tree,hf_diameter_hopbyhopid,tvb,12,4,FALSE);
798 proto_tree_add_item(diam_tree,hf_diameter_endtoendid,tvb,16,4,FALSE);
800 /* Conversation tracking stuff */
802 * FIXME: Looking at epan/conversation.c it seems unlike that this will work properly in
803 * multi-homed SCTP connections. This will probably need to be fixed at some point.
806 conversation = find_or_create_conversation(pinfo);
808 diameter_conv_info = conversation_get_proto_data(conversation, proto_diameter);
809 if (!diameter_conv_info) {
810 diameter_conv_info = se_alloc(sizeof(diameter_conv_info_t));
811 diameter_conv_info->pdus = se_tree_create_non_persistent(
812 EMEM_TREE_TYPE_RED_BLACK, "diameter_pdus");
814 conversation_add_proto_data(conversation, proto_diameter, diameter_conv_info);
817 if (!pinfo->fd->flags.visited) {
818 if (flags_bits & 0x80) {
819 /* This is a request */
820 diameter_pair = se_alloc(sizeof(diameter_req_ans_pair_t));
821 diameter_pair->hop_by_hop_id = hop_by_hop_id;
822 diameter_pair->cmd_code = cmd;
823 diameter_pair->result_code = 0;
824 diameter_pair->cmd_str = cmd_str;
825 diameter_pair->req_frame = pinfo->fd->num;
826 diameter_pair->ans_frame = 0;
827 diameter_pair->req_time = pinfo->fd->abs_ts;
828 se_tree_insert32(diameter_conv_info->pdus, hop_by_hop_id, (void *)diameter_pair);
830 diameter_pair = se_tree_lookup32(diameter_conv_info->pdus, hop_by_hop_id);
832 diameter_pair->ans_frame = pinfo->fd->num;
836 diameter_pair = se_tree_lookup32(diameter_conv_info->pdus, hop_by_hop_id);
839 if (!diameter_pair) {
840 /* create a "fake" diameter_pair structure */
841 diameter_pair = ep_alloc(sizeof(diameter_req_ans_pair_t));
842 diameter_pair->hop_by_hop_id = hop_by_hop_id;
843 diameter_pair->cmd_code = cmd;
844 diameter_pair->result_code = 0;
845 diameter_pair->cmd_str = cmd_str;
846 diameter_pair->req_frame = 0;
847 diameter_pair->ans_frame = 0;
848 diameter_pair->req_time = pinfo->fd->abs_ts;
850 diameter_pair->processing_request=(flags_bits & 0x80)!=0;
854 /* print state tracking info in the tree */
855 if (flags_bits & 0x80) {
856 /* This is a request */
857 if (diameter_pair->ans_frame) {
858 it = proto_tree_add_uint(diam_tree, hf_diameter_answer_in,
859 tvb, 0, 0, diameter_pair->ans_frame);
860 PROTO_ITEM_SET_GENERATED(it);
863 /* This is an answer */
864 if (diameter_pair->req_frame) {
865 it = proto_tree_add_uint(diam_tree, hf_diameter_answer_to,
866 tvb, 0, 0, diameter_pair->req_frame);
867 PROTO_ITEM_SET_GENERATED(it);
869 nstime_delta(&ns, &pinfo->fd->abs_ts, &diameter_pair->req_time);
870 diameter_pair->srt_time = ns;
871 it = proto_tree_add_time(diam_tree, hf_diameter_answer_time, tvb, 0, 0, &ns);
872 PROTO_ITEM_SET_GENERATED(it);
873 /* TODO: Populate result_code in tap record from AVP 268 */
879 /* Dissect AVPs until the end of the packet is reached */
880 while (offset < packet_len) {
881 offset += dissect_diameter_avp(c, tvb, offset);
883 /* Skip to next 4-byte boundary */
884 offset += (offset % 4) ? 4 - (offset % 4) : 0 ;
888 /* Handle requests for which no answers were found and
889 * anawers for which no requests were found in the tap listener.
890 * In case if you don't need unpaired requests/answers use:
891 * if(diameter_pair->processing_request || !diameter_pair->req_frame)
894 tap_queue_packet(diameter_tap, pinfo, diameter_pair);
898 get_diameter_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
900 /* Get the length of the Diameter packet. */
901 return tvb_get_ntoh24(tvb, offset + 1);
905 check_diameter(tvbuff_t *tvb)
907 if (tvb_length(tvb) < 1)
908 return FALSE; /* not enough bytes to check the version */
910 if (tvb_get_guint8(tvb, 0) != 1)
911 return FALSE; /* not version 1 */
914 * XXX - fetch length and make sure it's at least MIN_DIAMETER_SIZE?
915 * Fetch flags and check that none of the DIAM_FLAGS_RESERVED bits
921 /************************************************/
922 /* Main dissection function */
924 dissect_diameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
926 if (!check_diameter(tvb))
928 dissect_diameter_common(tvb, pinfo, tree);
929 return tvb_length(tvb);
933 dissect_diameter_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
935 tcp_dissect_pdus(tvb, pinfo, tree, gbl_diameter_desegment, 4,
936 get_diameter_pdu_len, dissect_diameter_common);
937 } /* dissect_diameter_tcp */
941 alnumerize(char* name)
947 for (;(c = *r); r++) {
948 if (isalnum((unsigned char)c) || c == '_' || c == '-' || c == '.') {
960 reginfo(int* hf_ptr, const char* name, const char* abbr, const char* desc,
961 enum ftenum ft, base_display_e base, const value_string* vs,
964 hf_register_info hf = { hf_ptr, {
965 name ? g_strdup(name) : g_strdup(abbr),
974 g_array_append_vals(build_dict.hf,&hf,1);
975 return build_dict.hf->len - 1;
979 basic_avp_reginfo(diam_avp_t* a, const char* name, enum ftenum ft,
980 base_display_e base, const value_string* vs)
982 hf_register_info hf[] = { { &(a->hf_value),
983 { NULL, NULL, ft, base, VALS(vs), 0x0,
985 g_strdup_printf("vendor=%d code=%d", a->vendor->code, a->code)
986 : g_strdup_printf("code=%d", a->code),
989 gint* ettp = &(a->ett);
991 hf->hfinfo.name = g_strdup_printf("%s",name);
992 hf->hfinfo.abbrev = alnumerize(g_strdup_printf("diameter.%s",name));
994 g_array_append_vals(build_dict.hf,hf,1);
995 g_ptr_array_add(build_dict.ett,ettp);
999 build_address_avp(const avp_type_t* type _U_, guint32 code,
1000 const diam_vnd_t* vendor, const char* name,
1001 const value_string* vs _U_, void* data _U_)
1003 diam_avp_t* a = g_malloc0(sizeof(diam_avp_t));
1004 address_avp_t* t = g_malloc(sizeof(address_avp_t));
1005 gint* ettp = &(t->ett);
1010 It seems like the radius AVP:s 1-255 will use the defs from RADIUS in which case:
1011 http://www.ietf.org/rfc/rfc2865.txt?number=2865
1014 The Address field is four octets. The value 0xFFFFFFFF indicates
1015 that the NAS Should allow the user to select an address (e.g.
1016 Negotiated). The value 0xFFFFFFFE indicates that the NAS should
1017 select an address for the user (e.g. Assigned from a pool of
1018 addresses kept by the NAS). Other valid values indicate that the
1019 NAS should use that value as the user's IP address.
1020 Where as in Diameter:
1023 The Address format is derived from the OctetString AVP Base
1024 Format. It is a discriminated union, representing, for example a
1025 32-bit (IPv4) [IPV4] or 128-bit (IPv6) [IPV6] address, most
1026 significant octet first. The first two octets of the Address
1027 AVP represents the AddressType, which contains an Address Family
1028 defined in [IANAADFAM]. The AddressType is used to discriminate
1029 the content and format of the remaining octets.
1032 a->dissector_v16 = address_v16_avp;
1034 a->dissector_rfc = address_v16_avp;
1036 a->dissector_rfc = address_rfc_avp;
1043 t->hf_address_type = -1;
1048 basic_avp_reginfo(a,name,FT_BYTES,BASE_NONE,NULL);
1050 reginfo(&(t->hf_address_type), ep_strdup_printf("%s Address Family",name),
1051 alnumerize(ep_strdup_printf("diameter.%s.addr_family",name)),
1052 NULL, FT_UINT16, BASE_DEC, diameter_avp_data_addrfamily_vals, 0);
1054 reginfo(&(t->hf_ipv4), ep_strdup_printf("%s Address",name),
1055 alnumerize(ep_strdup_printf("diameter.%s",name)),
1056 NULL, FT_IPv4, BASE_NONE, NULL, 0);
1058 reginfo(&(t->hf_ipv6), ep_strdup_printf("%s Address",name),
1059 alnumerize(ep_strdup_printf("diameter.%s",name)),
1060 NULL, FT_IPv6, BASE_NONE, NULL, 0);
1062 reginfo(&(t->hf_other), ep_strdup_printf("%s Address",name),
1063 alnumerize(ep_strdup_printf("diameter.%s",name)),
1064 NULL, FT_BYTES, BASE_NONE, NULL, 0);
1066 g_ptr_array_add(build_dict.ett,ettp);
1072 build_proto_avp(const avp_type_t* type _U_, guint32 code,
1073 const diam_vnd_t* vendor, const char* name _U_,
1074 const value_string* vs _U_, void* data)
1076 diam_avp_t* a = g_malloc0(sizeof(diam_avp_t));
1077 proto_avp_t* t = g_malloc0(sizeof(proto_avp_t));
1078 gint* ettp = &(a->ett);
1082 a->dissector_v16 = proto_avp;
1083 a->dissector_rfc = proto_avp;
1090 t->reassemble_mode = 0;
1092 g_ptr_array_add(build_dict.ett,ettp);
1098 build_simple_avp(const avp_type_t* type, guint32 code, const diam_vnd_t* vendor,
1099 const char* name, const value_string* vs, void* data _U_)
1104 * Only 32-bit or shorter integral types can have a list of values.
1118 fprintf(stderr,"Diameter Dictionary: AVP %s has a list of values but isn't of a 32-bit or shorter integral type\n",
1124 a = g_malloc0(sizeof(diam_avp_t));
1127 a->dissector_v16 = type->v16;
1128 a->dissector_rfc = type->rfc;
1132 basic_avp_reginfo(a,name,type->ft,type->base,vs);
1139 static const avp_type_t basic_types[] = {
1140 {"octetstring" , simple_avp , simple_avp , FT_BYTES , BASE_NONE , build_simple_avp },
1141 {"utf8string" , simple_avp , simple_avp , FT_STRING , BASE_NONE , build_simple_avp },
1142 {"grouped" , grouped_avp , grouped_avp , FT_BYTES , BASE_NONE , build_simple_avp },
1143 {"integer32" , simple_avp , simple_avp , FT_INT32 , BASE_DEC , build_simple_avp },
1144 {"unsigned32" , unsigned32_avp, unsigned32_avp, FT_UINT32 , BASE_DEC , build_simple_avp },
1145 {"integer64" , simple_avp , simple_avp , FT_INT64 , BASE_DEC , build_simple_avp },
1146 {"unsigned64" , simple_avp , simple_avp , FT_UINT64 , BASE_DEC , build_simple_avp },
1147 {"float32" , simple_avp , simple_avp , FT_FLOAT , BASE_NONE , build_simple_avp },
1148 {"float64" , simple_avp , simple_avp , FT_DOUBLE , BASE_NONE , build_simple_avp },
1149 {"ipaddress" , NULL , NULL , FT_NONE , BASE_NONE , build_address_avp },
1150 {"diameteruri" , simple_avp , simple_avp , FT_STRING , BASE_NONE , build_simple_avp },
1151 {"diameteridentity" , simple_avp , simple_avp , FT_STRING , BASE_NONE , build_simple_avp },
1152 {"ipfilterrule" , simple_avp , simple_avp , FT_STRING , BASE_NONE , build_simple_avp },
1153 {"qosfilterrule" , simple_avp , simple_avp , FT_STRING , BASE_NONE , build_simple_avp },
1154 {"time" , time_avp , time_avp , FT_UINT32 , BASE_DEC , build_simple_avp },
1155 {NULL, NULL, NULL, FT_NONE, BASE_NONE, NULL }
1161 * This is like g_str_hash() (as of GLib 2.4.8), but it maps all
1162 * upper-case ASCII characters to their ASCII lower-case equivalents.
1163 * We can't use g_strdown(), as that doesn't do an ASCII mapping;
1164 * in Turkish locales, for example, there are two lower-case "i"s
1165 * and two upper-case "I"s, with and without dots - the ones with
1166 * dots map between each other, as do the ones without dots, so "I"
1167 * doesn't map to "i".
1170 strcase_hash(gconstpointer key)
1172 const char *p = key;
1177 if (h >= 'A' && h <= 'Z')
1179 for (p += 1; *p != '\0'; p++) {
1181 if (c >= 'A' && c <= 'Z')
1183 h = (h << 5) - h + c;
1191 * Again, use g_ascii_strcasecmp(), not strcasecmp(), so that only ASCII
1192 * letters are mapped, and they're mapped to the lower-case ASCII
1196 strcase_equal(gconstpointer ka, gconstpointer kb)
1200 return g_ascii_strcasecmp(a,b) == 0;
1205 dictionary_load(void)
1208 ddict_application_t* p;
1211 ddict_typedefn_t* t;
1213 gboolean do_debug_parser = getenv("WIRESHARK_DEBUG_DIAM_DICT_PARSER") ? TRUE : FALSE;
1214 gboolean do_dump_dict = getenv("WIRESHARK_DUMP_DIAM_DICT") ? TRUE : FALSE;
1215 char* dir = ep_strdup_printf("%s" G_DIR_SEPARATOR_S "diameter" G_DIR_SEPARATOR_S, get_datafile_dir());
1216 const avp_type_t* type;
1217 const avp_type_t* octetstring = &basic_types[0];
1219 GHashTable* vendors = g_hash_table_new(strcase_hash,strcase_equal);
1221 GArray* vnd_shrt_arr = g_array_new(TRUE,TRUE,sizeof(value_string));
1223 build_dict.hf = g_array_new(FALSE,TRUE,sizeof(hf_register_info));
1224 build_dict.ett = g_ptr_array_new();
1225 build_dict.types = g_hash_table_new(strcase_hash,strcase_equal);
1226 build_dict.avps = g_hash_table_new(strcase_hash,strcase_equal);
1228 dictionary.vnds = pe_tree_create(EMEM_TREE_TYPE_RED_BLACK,"diameter_vnds");
1229 dictionary.avps = pe_tree_create(EMEM_TREE_TYPE_RED_BLACK,"diameter_avps");
1231 no_vnd.vs_cmds = g_array_new(TRUE,TRUE,sizeof(value_string));
1232 no_vnd.vs_avps = g_array_new(TRUE,TRUE,sizeof(value_string));
1233 no_vnd.vs_avps_ext = g_malloc0(sizeof(value_string_ext));
1234 no_vnd.vs_avps_ext->match = (value_string_match_t) match_strval_ext_init;
1235 no_vnd.vs_avps_ext->length = 0;
1237 all_cmds = g_array_new(TRUE,TRUE,sizeof(value_string));
1239 pe_tree_insert32(dictionary.vnds,0,&no_vnd);
1240 g_hash_table_insert(vendors,(gchar *)"None",&no_vnd);
1242 /* initialize the types hash with the known basic types */
1243 for (type = basic_types; type->name; type++) {
1244 g_hash_table_insert(build_dict.types,(gchar *)type->name,(void*)type);
1247 /* load the dictionary */
1248 d = ddict_scan(dir,"dictionary.xml",do_debug_parser);
1253 if (do_dump_dict) ddict_print(stdout, d);
1255 /* populate the types */
1256 for (t = d->typedefns; t; t = t->next) {
1257 const avp_type_t* parent = NULL;
1258 /* try to get the parent type */
1260 if (g_hash_table_lookup(build_dict.types,t->name))
1264 parent = g_hash_table_lookup(build_dict.types,t->parent);
1267 if (!parent) parent = octetstring;
1269 /* insert the parent type for this type */
1270 g_hash_table_insert(build_dict.types,t->name,(void*)parent);
1273 /* populate the applications */
1274 if ((p = d->applications)) {
1275 GArray* arr = g_array_new(TRUE,TRUE,sizeof(value_string));
1277 for (; p; p = p->next) {
1278 value_string item = {p->code,p->name};
1279 g_array_append_val(arr,item);
1282 dictionary.applications = (void*)arr->data;
1283 g_array_free(arr,FALSE);
1286 if ((v = d->vendors)) {
1287 for ( ; v; v = v->next) {
1288 value_string item = {v->code,v->name};
1290 if (g_hash_table_lookup(vendors,v->name))
1293 g_array_append_val(vnd_shrt_arr,item);
1295 vnd = g_malloc(sizeof(diam_vnd_t));
1296 vnd->code = v->code;
1297 vnd->vs_cmds = g_array_new(TRUE,TRUE,sizeof(value_string));
1298 vnd->vs_avps = g_array_new(TRUE,TRUE,sizeof(value_string));
1299 vnd->vs_avps_ext = g_malloc0(sizeof(value_string_ext));
1300 vnd->vs_avps_ext->match = (value_string_match_t) match_strval_ext_init;
1301 vnd->vs_avps_ext->length= 0;
1302 pe_tree_insert32(dictionary.vnds,vnd->code,vnd);
1303 g_hash_table_insert(vendors,v->name,vnd);
1307 vnd_short_vs = (void*)vnd_shrt_arr->data;
1308 g_array_free(vnd_shrt_arr,FALSE);
1310 if ((c = d->cmds)) {
1311 for (; c; c = c->next) {
1312 if ((vnd = g_hash_table_lookup(vendors,c->vendor))) {
1313 value_string item = {c->code,c->name};
1314 g_array_append_val(vnd->vs_cmds,item);
1315 /* Also add to all_cmds as used by RFC version */
1316 g_array_append_val(all_cmds,item);
1318 fprintf(stderr,"Diameter Dictionary: No Vendor: %s",c->vendor);
1324 for (a = d->avps; a; a = a->next) {
1326 value_string* vs = NULL;
1327 const char* vend = a->vendor ? a->vendor : "None";
1329 void* avp_data = NULL;
1331 if ((vnd = g_hash_table_lookup(vendors,vend))) {
1332 value_string vndvs = {a->code,a->name};
1333 g_array_append_val(vnd->vs_avps,vndvs);
1334 vnd->vs_avps_ext->length++;
1336 fprintf(stderr,"Diameter Dictionary: No Vendor: %s",vend);
1337 vnd = &unknown_vendor;
1340 if ((e = a->enums)) {
1341 GArray* arr = g_array_new(TRUE,TRUE,sizeof(value_string));
1343 for (; e; e = e->next) {
1344 value_string item = {e->code,e->name};
1345 g_array_append_val(arr,item);
1347 vs = (void*)arr->data;
1352 for( x = d->xmlpis; x; x = x->next ) {
1353 if ( (strcase_equal(x->name,"avp-proto") && strcase_equal(x->key,a->name))
1354 || (a->type && strcase_equal(x->name,"type-proto") && strcase_equal(x->key,a->type))
1356 static avp_type_t proto_type = {"proto", proto_avp, proto_avp, FT_UINT32, BASE_NONE, build_proto_avp};
1359 avp_data = x->value;
1364 if ( (!type) && a->type )
1365 type = g_hash_table_lookup(build_dict.types,a->type);
1367 if (!type) type = octetstring;
1369 avp = type->build( type, a->code, vnd, a->name, vs, avp_data);
1371 g_hash_table_insert(build_dict.avps, a->name, avp);
1374 emem_tree_key_t k[] = {
1376 { 1, &(vnd->code) },
1379 pe_tree_insert32_array(dictionary.avps,k,avp);
1383 g_hash_table_destroy(build_dict.types);
1384 g_hash_table_destroy(build_dict.avps);
1385 g_hash_table_destroy(vendors);
1391 range_delete_callback(guint32 port)
1393 dissector_delete("tcp.port", port, diameter_tcp_handle);
1397 range_add_callback(guint32 port)
1399 dissector_add("tcp.port", port, diameter_tcp_handle);
1402 /* registration with the filtering engine */
1404 void proto_reg_handoff_diameter(void);
1407 proto_register_diameter(void)
1409 module_t *diameter_module;
1410 guint i, ett_length;
1412 hf_register_info hf_base[] = {
1413 { &hf_diameter_version,
1414 { "Version", "diameter.version", FT_UINT8, BASE_HEX, NULL, 0x00,
1416 { &hf_diameter_length,
1417 { "Length","diameter.length", FT_UINT24, BASE_DEC, NULL, 0x0,
1419 { &hf_diameter_flags,
1420 { "Flags", "diameter.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
1422 { &hf_diameter_flags_request,
1423 { "Request", "diameter.flags.request", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_R,
1425 { &hf_diameter_flags_proxyable,
1426 { "Proxyable", "diameter.flags.proxyable", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_P,
1428 { &hf_diameter_flags_error,
1429 { "Error","diameter.flags.error", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_E,
1431 { &hf_diameter_flags_T,
1432 { "T(Potentially re-transmitted message)","diameter.flags.T", FT_BOOLEAN, 8, TFS(&tfs_set_notset),DIAM_FLAGS_T,
1434 { &hf_diameter_flags_reserved4,
1435 { "Reserved","diameter.flags.reserved4", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1436 DIAM_FLAGS_RESERVED4, NULL, HFILL }},
1437 { &hf_diameter_flags_reserved5,
1438 { "Reserved","diameter.flags.reserved5", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1439 DIAM_FLAGS_RESERVED5, NULL, HFILL }},
1440 { &hf_diameter_flags_reserved6,
1441 { "Reserved","diameter.flags.reserved6", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1442 DIAM_FLAGS_RESERVED6, NULL, HFILL }},
1443 { &hf_diameter_flags_reserved7,
1444 { "Reserved","diameter.flags.reserved7", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1445 DIAM_FLAGS_RESERVED7, NULL, HFILL }},
1446 { &hf_diameter_vendor_id,
1447 { "VendorId", "diameter.vendorId", FT_UINT32, BASE_DEC|BASE_EXT_STRING, &sminmpec_values_ext,
1449 { &hf_diameter_application_id,
1450 { "ApplicationId", "diameter.applicationId", FT_UINT32, BASE_DEC, dictionary.applications,
1452 { &hf_diameter_hopbyhopid,
1453 { "Hop-by-Hop Identifier", "diameter.hopbyhopid", FT_UINT32,
1454 BASE_HEX, NULL, 0x0, NULL, HFILL }},
1455 { &hf_diameter_endtoendid,
1456 { "End-to-End Identifier", "diameter.endtoendid", FT_UINT32,
1457 BASE_HEX, NULL, 0x0, NULL, HFILL }},
1459 { "AVP","diameter.avp", FT_BYTES, BASE_NONE,
1460 NULL, 0x0, NULL, HFILL }},
1461 { &hf_diameter_avp_len,
1462 { "AVP Length","diameter.avp.len", FT_UINT24, BASE_DEC,
1463 NULL, 0x0, NULL, HFILL }},
1464 { &hf_diameter_avp_flags,
1465 { "AVP Flags","diameter.avp.flags", FT_UINT8, BASE_HEX,
1466 NULL, 0x0, NULL, HFILL }},
1467 { &hf_diameter_avp_flags_vendor_specific,
1468 { "Vendor-Specific", "diameter.flags.vendorspecific", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AVP_FLAGS_V,
1470 { &hf_diameter_avp_flags_mandatory,
1471 { "Mandatory", "diameter.flags.mandatory", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AVP_FLAGS_M,
1473 { &hf_diameter_avp_flags_protected,
1474 { "Protected","diameter.avp.flags.protected", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AVP_FLAGS_P,
1476 { &hf_diameter_avp_flags_reserved3,
1477 { "Reserved","diameter.avp.flags.reserved3", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1478 AVP_FLAGS_RESERVED3, NULL, HFILL }},
1479 { &hf_diameter_avp_flags_reserved4,
1480 { "Reserved","diameter.avp.flags.reserved4", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1481 AVP_FLAGS_RESERVED4, NULL, HFILL }},
1482 { &hf_diameter_avp_flags_reserved5,
1483 { "Reserved","diameter.avp.flags.reserved5", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1484 AVP_FLAGS_RESERVED5, NULL, HFILL }},
1485 { &hf_diameter_avp_flags_reserved6,
1486 { "Reserved","diameter.avp.flags.reserved6", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1487 AVP_FLAGS_RESERVED6, NULL, HFILL }},
1488 { &hf_diameter_avp_flags_reserved7,
1489 { "Reserved","diameter.avp.flags.reserved7", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1490 AVP_FLAGS_RESERVED7, NULL, HFILL }},
1491 { &hf_diameter_avp_vendor_id,
1492 { "AVP Vendor Id","diameter.avp.vendorId", FT_UINT32, BASE_DEC|BASE_EXT_STRING,
1493 &sminmpec_values_ext, 0x0, NULL, HFILL }},
1494 { &(unknown_avp.hf_value),
1495 { "Value","diameter.avp.unknown", FT_BYTES, BASE_NONE,
1496 NULL, 0x0, NULL, HFILL }},
1497 { &hf_diameter_avp_data_wrong_length,
1498 { "Data","diameter.avp.invalid-data", FT_BYTES, BASE_NONE,
1499 NULL, 0x0, NULL, HFILL }},
1500 { &hf_diameter_code,
1501 { "Command Code", "diameter.cmd.code", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
1502 { &hf_diameter_avp_code,
1503 { "AVP Code", "diameter.avp.code", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
1504 { &hf_diameter_answer_in,
1505 { "Answer In", "diameter.answer_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1506 "The answer to this diameter request is in this frame", HFILL }},
1507 { &hf_diameter_answer_to,
1508 { "Request In", "diameter.answer_to", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1509 "This is an answer to the diameter request in this frame", HFILL }},
1510 { &hf_diameter_answer_time,
1511 { "Response Time", "diameter.resp_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1512 "The time between the request and the answer", HFILL }},
1516 gint *ett_base[] = {
1518 &ett_diameter_flags,
1519 &ett_diameter_avp_flags,
1520 &ett_diameter_avpinfo,
1528 g_array_append_vals(build_dict.hf, hf_base, array_length(hf_base));
1529 ett_length = array_length(ett_base);
1530 for (i = 0; i < ett_length; i++)
1532 g_ptr_array_add(build_dict.ett, ett_base[i]);
1535 proto_diameter = proto_register_protocol ("Diameter Protocol", "DIAMETER", "diameter");
1537 proto_register_field_array(proto_diameter, (hf_register_info*)(void*)build_dict.hf->data, build_dict.hf->len);
1538 proto_register_subtree_array((gint**)build_dict.ett->pdata, build_dict.ett->len);
1540 g_array_free(build_dict.hf,FALSE);
1541 g_ptr_array_free(build_dict.ett,TRUE);
1543 /* Allow dissector to find be found by name. */
1544 new_register_dissector("diameter", dissect_diameter, proto_diameter);
1546 /* Register dissector table(s) to do sub dissection of AVP:s ( OctetStrings) */
1547 diameter_dissector_table = register_dissector_table("diameter.base", "DIAMETER_BASE_AVPS", FT_UINT32, BASE_DEC);
1548 diameter_3gpp_avp_dissector_table = register_dissector_table("diameter.3gpp", "DIAMETER_3GPP_AVPS", FT_UINT32, BASE_DEC);
1549 diameter_ericsson_avp_dissector_table = register_dissector_table("diameter.ericsson", "DIAMETER_ERICSSON_AVPS", FT_UINT32, BASE_DEC);
1551 /* Set default TCP ports */
1552 range_convert_str(&global_diameter_tcp_port_range, DEFAULT_DIAMETER_PORT_RANGE, MAX_UDP_PORT);
1554 /* Register configuration options for ports */
1555 diameter_module = prefs_register_protocol(proto_diameter,
1556 proto_reg_handoff_diameter);
1558 prefs_register_range_preference(diameter_module, "tcp.ports", "Diameter TCP ports",
1559 "TCP ports to be decoded as Diameter (default: "
1560 DEFAULT_DIAMETER_PORT_RANGE ")",
1561 &global_diameter_tcp_port_range, MAX_UDP_PORT);
1563 prefs_register_uint_preference(diameter_module, "sctp.port",
1564 "Diameter SCTP Port",
1565 "Set the SCTP port for Diameter messages",
1567 &gbl_diameterSctpPort);
1569 /* Desegmentation */
1570 prefs_register_bool_preference(diameter_module, "desegment",
1571 "Reassemble Diameter messages\nspanning multiple TCP segments",
1572 "Whether the Diameter dissector should reassemble messages spanning multiple TCP segments."
1573 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
1574 &gbl_diameter_desegment);
1576 /* Register some preferences we no longer support, so we can report
1577 * them as obsolete rather than just illegal.
1579 prefs_register_obsolete_preference(diameter_module, "version");
1580 prefs_register_obsolete_preference(diameter_module, "udp.port");
1581 prefs_register_obsolete_preference(diameter_module, "tcp.port");
1582 prefs_register_obsolete_preference(diameter_module, "command_in_header");
1583 prefs_register_obsolete_preference(diameter_module, "dictionary.name");
1584 prefs_register_obsolete_preference(diameter_module, "dictionary.use");
1585 prefs_register_obsolete_preference(diameter_module, "allow_zero_as_app_id");
1586 prefs_register_obsolete_preference(diameter_module, "suppress_console_output");
1589 diameter_tap = register_tap("diameter");
1591 } /* proto_register_diameter */
1594 proto_reg_handoff_diameter(void)
1596 static gboolean Initialized=FALSE;
1597 static guint SctpPort;
1598 static dissector_handle_t diameter_handle;
1599 static range_t *diameter_tcp_port_range;
1602 diameter_handle = find_dissector("diameter");
1603 diameter_tcp_handle = create_dissector_handle(dissect_diameter_tcp,
1605 data_handle = find_dissector("data");
1606 eap_handle = find_dissector("eap");
1607 /* Register special decoding for some AVP:s */
1608 /* AVP Code: 266 Vendor-Id */
1609 dissector_add("diameter.base", 266,
1610 new_create_dissector_handle(dissect_diameter_vedor_id, proto_diameter));
1611 /* AVP Code: 462 EAP-Payload */
1612 dissector_add("diameter.base", 462,
1613 new_create_dissector_handle(dissect_diameter_eap_payload, proto_diameter));
1614 /* AVP Code: 463 EAP-Reissued-Payload */
1615 dissector_add("diameter.base", 463,
1616 new_create_dissector_handle(dissect_diameter_eap_payload, proto_diameter));
1620 range_foreach(diameter_tcp_port_range, range_delete_callback);
1621 g_free(diameter_tcp_port_range);
1622 dissector_delete("sctp.port", SctpPort, diameter_handle);
1625 /* set port for future deletes */
1626 diameter_tcp_port_range = range_copy(global_diameter_tcp_port_range);
1627 range_foreach(diameter_tcp_port_range, range_add_callback);
1629 SctpPort=gbl_diameterSctpPort;
1630 dissector_add("sctp.port", gbl_diameterSctpPort, diameter_handle);