Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-diameter.c
blob4b37f65ca4b87e6319d544e550f2c45fd22c36ee
1 /* packet-diameter.c
2 * Routines for Diameter packet disassembly
4 * Copyright (c) 2001 by David Frascone <dave@frascone.com>
5 * Copyright (c) 2007 by Luis E. Garcia Ontanon <luis@ontanon.org>
7 * Support for Request-Answer tracking and Tapping
8 * introduced by Abhik Sarkar
10 * Wireshark - Network traffic analyzer
11 * By Gerald Combs <gerald@wireshark.org>
12 * Copyright 1998 Gerald Combs
14 * SPDX-License-Identifier: GPL-2.0-or-later
16 * References:
18 * RFC 3588, "Diameter Base Protocol" (now RFC 6733)
19 * draft-ietf-aaa-diameter-mobileip-16, "Diameter Mobile IPv4 Application"
20 * (now RFC 4004)
21 * draft-ietf-aaa-diameter-nasreq-14, "Diameter Network Access Server
22 * Application" (now RFC 4005)
23 * drafts/draft-ietf-aaa-diameter-cc-03, "Diameter Credit-Control
24 * Application" (now RFC 4006)
25 * draft-ietf-aaa-diameter-sip-app-01, "Diameter Session Initiation
26 * Protocol (SIP) Application" (now RFC 4740)
27 * RFC 5779, "Diameter Proxy Mobile IPv6: Mobile Access Gateway and
28 * Local Mobility Anchor Interaction with Diameter Server"
29 * 3GPP TS 29.273, V15.2.0
30 * http://www.ietf.org/html.charters/aaa-charter.html
31 * http://www.iana.org/assignments/radius-types
32 * http://www.iana.org/assignments/address-family-numbers
33 * http://www.iana.org/assignments/enterprise-numbers
34 * http://www.iana.org/assignments/aaa-parameters
37 #include "config.h"
39 #include <epan/packet.h>
40 #include <epan/exceptions.h>
41 #include <epan/prefs.h>
42 #include <epan/sminmpec.h>
43 #include <epan/addr_resolv.h>
44 #include <epan/expert.h>
45 #include <epan/tap.h>
46 #include <epan/srt_table.h>
47 #include <epan/exported_pdu.h>
48 #include <epan/diam_dict.h>
49 #include <epan/sctpppids.h>
50 #include <epan/show_exception.h>
51 #include <epan/to_str.h>
52 #include <epan/strutil.h>
53 #include <epan/afn.h>
54 #include <epan/tfs.h>
55 #include <wsutil/filesystem.h>
56 #include <wsutil/report_message.h>
57 #include "packet-tcp.h"
58 #include "packet-diameter.h"
59 #include "packet-tls.h"
60 #include "packet-dtls.h"
61 #include "packet-e212.h"
62 #include "packet-e164.h"
63 #include "packet-eap.h"
65 void proto_register_diameter(void);
66 void proto_reg_handoff_diameter(void);
68 /* Diameter Header Flags */
69 /* RPETrrrrCCCCCCCCCCCCCCCCCCCCCCCC */
70 #define DIAM_FLAGS_R 0x80
71 #define DIAM_FLAGS_P 0x40
72 #define DIAM_FLAGS_E 0x20
73 #define DIAM_FLAGS_T 0x10
74 #define DIAM_FLAGS_RESERVED4 0x08
75 #define DIAM_FLAGS_RESERVED5 0x04
76 #define DIAM_FLAGS_RESERVED6 0x02
77 #define DIAM_FLAGS_RESERVED7 0x01
78 #define DIAM_FLAGS_RESERVED 0x0f
80 #if 0
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)
87 #endif
89 /* Diameter AVP Flags */
90 #define AVP_FLAGS_P 0x20
91 #define AVP_FLAGS_V 0x80
92 #define AVP_FLAGS_M 0x40
93 #define AVP_FLAGS_RESERVED3 0x10
94 #define AVP_FLAGS_RESERVED4 0x08
95 #define AVP_FLAGS_RESERVED5 0x04
96 #define AVP_FLAGS_RESERVED6 0x02
97 #define AVP_FLAGS_RESERVED7 0x01
98 #define AVP_FLAGS_RESERVED 0x1f /* 00011111 -- V M P X X X X X */
100 #define DIAMETER_RFC 1
102 static int exported_pdu_tap = -1;
104 /* Conversation Info */
105 typedef struct _diameter_conv_info_t {
106 wmem_map_t *pdu_trees;
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 } diam_ctx_t;
115 typedef struct _diam_avp_t diam_avp_t;
116 typedef struct _avp_type_t avp_type_t;
118 typedef const char *(*diam_avp_dissector_t)(diam_ctx_t *, diam_avp_t *, tvbuff_t *, diam_sub_dis_t *);
121 typedef struct _diam_vnd_t {
122 uint32_t code;
123 wmem_array_t *vs_avps;
124 value_string_ext *vs_avps_ext;
125 } diam_vnd_t;
127 struct _diam_avp_t {
128 uint32_t code;
129 diam_vnd_t *vendor;
130 diam_avp_dissector_t dissector_rfc;
132 int ett;
133 int hf_value;
134 void *type_data;
137 #define VND_AVP_VS(v) ((value_string *)(void *)(wmem_array_get_raw((v)->vs_avps)))
138 #define VND_AVP_VS_LEN(v) (wmem_array_get_count((v)->vs_avps))
140 typedef struct _diam_dictionary_t {
141 wmem_tree_t *avps;
142 wmem_tree_t *vnds;
143 value_string_ext *applications;
144 value_string *commands;
145 } diam_dictionary_t;
147 typedef diam_avp_t *(*avp_constructor_t)(const avp_type_t *, uint32_t, diam_vnd_t *, const char *, const value_string *, void *);
149 struct _avp_type_t {
150 const char *name;
151 diam_avp_dissector_t rfc;
152 enum ftenum ft;
153 int base;
154 avp_constructor_t build;
157 struct _build_dict {
158 wmem_array_t *hf;
159 GPtrArray *ett;
160 GHashTable *types;
161 GHashTable *avps;
165 typedef struct _address_avp_t {
166 int ett;
167 int hf_address_type;
168 int hf_ipv4;
169 int hf_ipv6;
170 int hf_e164_str;
171 int hf_other;
172 } address_avp_t;
174 typedef enum {
175 REASEMBLE_NEVER = 0,
176 REASEMBLE_AT_END,
177 REASEMBLE_BY_LENGTH
178 } avp_reassemble_mode_t;
180 typedef struct _proto_avp_t {
181 char *name;
182 dissector_handle_t handle;
183 avp_reassemble_mode_t reassemble_mode;
184 } proto_avp_t;
186 static const char *simple_avp(diam_ctx_t *, diam_avp_t *, tvbuff_t *, diam_sub_dis_t *);
188 static diam_vnd_t unknown_vendor = { 0xffffffff, NULL, NULL };
189 static diam_vnd_t no_vnd = { 0, NULL, NULL };
190 static diam_avp_t unknown_avp = {0, &unknown_vendor, simple_avp, -1, -1, NULL };
191 static const value_string *cmd_vs;
192 static diam_dictionary_t dictionary = { NULL, NULL, NULL, NULL };
193 static struct _build_dict build_dict;
194 static const value_string *vnd_short_vs;
195 static dissector_handle_t data_handle;
196 static dissector_handle_t eap_handle;
198 static const value_string diameter_avp_data_addrfamily_vals[]= {
199 {1,"IPv4"},
200 {2,"IPv6"},
201 {3,"NSAP"},
202 {4,"HDLC"},
203 {5,"BBN"},
204 {6,"IEEE-802"},
205 {7,"E-163"},
206 {8,"E-164"},
207 {9,"F-69"},
208 {10,"X-121"},
209 {11,"IPX"},
210 {12,"Appletalk"},
211 {13,"Decnet4"},
212 {14,"Vines"},
213 {15,"E-164-NSAP"},
214 {16,"DNS"},
215 {17,"DistinguishedName"},
216 {18,"AS"},
217 {19,"XTPoIPv4"},
218 {20,"XTPoIPv6"},
219 {21,"XTPNative"},
220 {22,"FibrePortName"},
221 {23,"FibreNodeName"},
222 {24,"GWID"},
223 {0,NULL}
225 static value_string_ext diameter_avp_data_addrfamily_vals_ext = VALUE_STRING_EXT_INIT(diameter_avp_data_addrfamily_vals);
227 static int proto_diameter;
228 static int hf_diameter_length;
229 static int hf_diameter_code;
230 static int hf_diameter_hopbyhopid;
231 static int hf_diameter_endtoendid;
232 static int hf_diameter_version;
233 static int hf_diameter_vendor_id;
234 static int hf_diameter_application_id;
235 static int hf_diameter_flags;
236 static int hf_diameter_flags_request;
237 static int hf_diameter_flags_proxyable;
238 static int hf_diameter_flags_error;
239 static int hf_diameter_flags_T;
240 static int hf_diameter_flags_reserved4;
241 static int hf_diameter_flags_reserved5;
242 static int hf_diameter_flags_reserved6;
243 static int hf_diameter_flags_reserved7;
245 static int hf_diameter_avp;
246 static int hf_diameter_avp_len;
247 static int hf_diameter_avp_code;
248 static int hf_diameter_avp_flags;
249 static int hf_diameter_avp_flags_vendor_specific;
250 static int hf_diameter_avp_flags_mandatory;
251 static int hf_diameter_avp_flags_protected;
252 static int hf_diameter_avp_flags_reserved3;
253 static int hf_diameter_avp_flags_reserved4;
254 static int hf_diameter_avp_flags_reserved5;
255 static int hf_diameter_avp_flags_reserved6;
256 static int hf_diameter_avp_flags_reserved7;
257 static int hf_diameter_avp_vendor_id;
258 static int hf_diameter_avp_data_wrong_length;
259 static int hf_diameter_avp_pad;
261 static int hf_diameter_answer_in;
262 static int hf_diameter_answer_to;
263 static int hf_diameter_answer_time;
265 /* AVPs with special/extra decoding */
266 static int hf_framed_ipv6_prefix_reserved;
267 static int hf_framed_ipv6_prefix_length;
268 static int hf_framed_ipv6_prefix_bytes;
269 static int hf_framed_ipv6_prefix_ipv6;
270 static int hf_diameter_3gpp2_exp_res;
271 static int hf_diameter_other_vendor_exp_res;
272 static int hf_diameter_mip6_feature_vector;
273 static int hf_diameter_mip6_feature_vector_mip6_integrated;
274 static int hf_diameter_mip6_feature_vector_local_home_agent_assignment;
275 static int hf_diameter_mip6_feature_vector_pmip6_supported;
276 static int hf_diameter_mip6_feature_vector_ip4_hoa_supported;
277 static int hf_diameter_mip6_feature_vector_local_mag_routing_supported;
278 static int hf_diameter_3gpp_mip6_feature_vector;
279 static int hf_diameter_3gpp_mip6_feature_vector_assign_local_ip;
280 static int hf_diameter_3gpp_mip6_feature_vector_mip4_supported;
281 static int hf_diameter_3gpp_mip6_feature_vector_optimized_idle_mode_mobility;
282 static int hf_diameter_3gpp_mip6_feature_vector_gtpv2_supported;
283 static int hf_diameter_user_equipment_info_imeisv;
284 static int hf_diameter_user_equipment_info_mac;
285 static int hf_diameter_user_equipment_info_eui64;
286 static int hf_diameter_user_equipment_info_modified_eui64;
288 static int ett_diameter;
289 static int ett_diameter_flags;
290 static int ett_diameter_avp_flags;
291 static int ett_diameter_avpinfo;
292 static int ett_unknown;
293 static int ett_diameter_mip6_feature_vector;
294 static int ett_diameter_3gpp_mip6_feature_vector;
296 static expert_field ei_diameter_reserved_bit_set;
297 static expert_field ei_diameter_avp_len;
298 static expert_field ei_diameter_avp_no_data;
299 static expert_field ei_diameter_application_id;
300 static expert_field ei_diameter_version;
301 static expert_field ei_diameter_avp_pad;
302 static expert_field ei_diameter_avp_pad_missing;
303 static expert_field ei_diameter_code;
304 static expert_field ei_diameter_avp_code;
305 static expert_field ei_diameter_avp_vendor_id;
306 static expert_field ei_diameter_invalid_ipv6_prefix_len;
307 static expert_field ei_diameter_invalid_avp_len;
308 static expert_field ei_diameter_invalid_user_equipment_info_value_len;
309 static expert_field ei_diameter_unexpected_imei_as_user_equipment_info;
311 /* Tap for Diameter */
312 static int diameter_tap;
314 /* For conversations */
316 static dissector_handle_t diameter_udp_handle;
317 static dissector_handle_t diameter_tcp_handle;
318 static dissector_handle_t diameter_sctp_handle;
319 /* This is IANA registered for TCP and SCTP (and reserved for UDP) */
320 #define DEFAULT_DIAMETER_PORT_RANGE "3868"
321 /* This is IANA registered for TLS/TCP and DTLS/SCTP (and reserved for UDP) */
322 #define DEFAULT_DIAMETER_TLS_PORT 5868
324 /* desegmentation of Diameter over TCP */
325 static bool gbl_diameter_desegment = true;
327 /* Dissector tables */
328 static dissector_table_t diameter_dissector_table;
329 static dissector_table_t diameter_3gpp_avp_dissector_table;
330 static dissector_table_t diameter_ericsson_avp_dissector_table;
331 static dissector_table_t diameter_verizon_avp_dissector_table;
332 static dissector_table_t diameter_expr_result_vnd_table;
334 static const char *avpflags_str[] = {
335 "---",
336 "--P",
337 "-M-",
338 "-MP",
339 "V--",
340 "V-P",
341 "VM-",
342 "VMP",
345 #define SUBSCRIPTION_ID_TYPE_E164 0
346 #define SUBSCRIPTION_ID_TYPE_IMSI 1
347 #define SUBSCRIPTION_ID_TYPE_SIP_URI 2
348 #define SUBSCRIPTION_ID_TYPE_NAI 3
349 #define SUBSCRIPTION_ID_TYPE_PRIVATE 4
350 #define SUBSCRIPTION_ID_TYPE_UNKNOWN (uint32_t)-1
352 #define USER_EQUIPMENT_INFO_TYPE_IMEISV 0
353 #define USER_EQUIPMENT_INFO_TYPE_MAC 1
354 #define USER_EQUIPMENT_INFO_TYPE_EUI64 2
355 #define USER_EQUIPMENT_INFO_TYPE_MODIFIED_EUI64 3
356 #define USER_EQUIPMENT_INFO_TYPE_UNKNOWN (uint32_t)-1
358 static void
359 export_diameter_pdu(packet_info *pinfo, tvbuff_t *tvb)
361 exp_pdu_data_t *exp_pdu_data = export_pdu_create_common_tags(pinfo, "diameter", EXP_PDU_TAG_DISSECTOR_NAME);
363 exp_pdu_data->tvb_captured_length = tvb_captured_length(tvb);
364 exp_pdu_data->tvb_reported_length = tvb_reported_length(tvb);
365 exp_pdu_data->pdu_tvb = tvb;
367 tap_queue_packet(exported_pdu_tap, pinfo, exp_pdu_data);
371 static int
372 compare_avps(const void *a, const void *b)
374 const value_string *vsa = (const value_string *)a;
375 const value_string *vsb = (const value_string *)b;
377 if (vsa->value > vsb->value)
378 return 1;
379 if (vsa->value < vsb->value)
380 return -1;
382 return 0;
385 static GHashTable* diameterstat_cmd_str_hash;
386 #define DIAMETER_NUM_PROCEDURES 1
388 static void
389 diameterstat_init(struct register_srt* srt _U_, GArray* srt_array)
391 srt_stat_table *diameter_srt_table;
392 int* idx;
394 /* XXX - This is a hack/workaround support so resetting/freeing parameters at the dissector
395 level doesn't need to be supported. */
396 if (diameterstat_cmd_str_hash != NULL)
398 g_hash_table_destroy(diameterstat_cmd_str_hash);
401 idx = wmem_new0(wmem_epan_scope(), int);
402 diameterstat_cmd_str_hash = g_hash_table_new(g_str_hash,g_str_equal);
403 g_hash_table_insert(diameterstat_cmd_str_hash, "Unknown", idx);
405 /** @todo the filter to use in stead of NULL is "diameter.cmd.code"
406 * to enable the filter popup in the service response time dialogue
407 * Note to make it work the command code must be stored rather than the
408 * index.
410 diameter_srt_table = init_srt_table("Diameter Requests", NULL, srt_array, DIAMETER_NUM_PROCEDURES, NULL, NULL, NULL);
411 init_srt_table_row(diameter_srt_table, 0, "Unknown");
414 static tap_packet_status
415 diameterstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prv, tap_flags_t flags _U_)
417 unsigned i = 0;
418 srt_stat_table *diameter_srt_table;
419 srt_data_t *data = (srt_data_t *)pss;
420 const diameter_req_ans_pair_t *diameter=(const diameter_req_ans_pair_t *)prv;
421 int* idx = NULL;
423 /* Process only answers where corresponding request is found.
424 * Unpaired diameter messages are currently not supported by statistics.
425 * Return 0, since redraw is not needed. */
426 if(!diameter || diameter->processing_request || !diameter->req_frame)
427 return TAP_PACKET_DONT_REDRAW;
429 diameter_srt_table = g_array_index(data->srt_array, srt_stat_table*, i);
431 idx = (int*) g_hash_table_lookup(diameterstat_cmd_str_hash, diameter->cmd_str);
432 if (idx == NULL) {
433 idx = wmem_new(wmem_epan_scope(), int);
434 *idx = (int) g_hash_table_size(diameterstat_cmd_str_hash);
435 g_hash_table_insert(diameterstat_cmd_str_hash, (char*) diameter->cmd_str, idx);
436 init_srt_table_row(diameter_srt_table, *idx, (const char*) diameter->cmd_str);
439 add_srt_table_data(diameter_srt_table, *idx, &diameter->req_time, pinfo);
441 return TAP_PACKET_REDRAW;
445 /* Special decoding of some AVPs */
447 static int
448 dissect_diameter_vendor_id(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
450 int offset = 0;
452 proto_tree_add_item(tree, hf_diameter_vendor_id, tvb, 0, 4, ENC_BIG_ENDIAN);
454 offset++;
455 return offset;
458 static int
459 dissect_diameter_eap_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
461 bool save_writable;
463 /* Ensure the packet is displayed as Diameter, not EAP */
464 save_writable = col_get_writable(pinfo->cinfo, COL_PROTOCOL);
465 col_set_writable(pinfo->cinfo, COL_PROTOCOL, false);
467 call_dissector(eap_handle, tvb, pinfo, tree);
469 col_set_writable(pinfo->cinfo, COL_PROTOCOL, save_writable);
470 return tvb_reported_length(tvb);
473 /* https://www.3gpp2.org/Public_html/X/VSA-VSE.cfm */
474 static const value_string diameter_3gpp2_exp_res_vals[]= {
475 { 5001, "Diameter_Error_User_No_WLAN_Subscription"},
476 { 5002, "Diameter_Error_Roaming_Not_Allowed(Obsoleted)"},
477 { 5003, "Diameter_Error_User_No_FAP_Subscription"},
478 {0,NULL}
481 static int
482 dissect_diameter_3gpp2_exp_res(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
484 proto_item *pi;
485 diam_sub_dis_t *diam_sub_dis;
487 /* Reject the packet if data is NULL */
488 if (data == NULL)
489 return 0;
490 diam_sub_dis = (diam_sub_dis_t*)data;
492 if (tree) {
493 pi = proto_tree_add_item(tree, hf_diameter_3gpp2_exp_res, tvb, 0, 4, ENC_BIG_ENDIAN);
494 diam_sub_dis->avp_str = (char *)wmem_alloc(pinfo->pool, ITEM_LABEL_LENGTH+1);
495 proto_item_fill_label(PITEM_FINFO(pi), diam_sub_dis->avp_str, NULL);
496 diam_sub_dis->avp_str = strstr(diam_sub_dis->avp_str,": ")+2;
499 return 4;
502 static void
503 dissect_diameter_other_vendor_exp_res(diam_ctx_t *c, tvbuff_t *tvb, proto_tree *tree, diam_sub_dis_t *diam_sub_dis)
505 proto_item *pi;
507 if (tree) {
508 pi = proto_tree_add_item(tree, hf_diameter_other_vendor_exp_res, tvb, 0, 4, ENC_BIG_ENDIAN);
509 diam_sub_dis->avp_str = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
510 proto_item_fill_label(PITEM_FINFO(pi), diam_sub_dis->avp_str, NULL);
511 diam_sub_dis->avp_str = strstr(diam_sub_dis->avp_str,": ")+2;
515 /* From RFC 3162 section 2.3 */
516 static int
517 dissect_diameter_base_framed_ipv6_prefix(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
519 diam_sub_dis_t *diam_sub_dis = (diam_sub_dis_t*)data;
520 uint32_t prefix_len, prefix_len_bytes;
521 proto_item *pi;
523 proto_tree_add_item(tree, hf_framed_ipv6_prefix_reserved, tvb, 0, 1, ENC_BIG_ENDIAN);
524 pi = proto_tree_add_item_ret_uint(tree, hf_framed_ipv6_prefix_length, tvb, 1, 1, ENC_BIG_ENDIAN, &prefix_len);
526 if (prefix_len > 128) {
527 expert_add_info(pinfo, pi, &ei_diameter_invalid_ipv6_prefix_len);
529 prefix_len_bytes = prefix_len / 8;
530 if (prefix_len % 8)
531 prefix_len_bytes++;
533 proto_tree_add_item(tree, hf_framed_ipv6_prefix_bytes, tvb, 2, prefix_len_bytes, ENC_NA);
535 /* If we have a fully IPv6 address, display it as such */
536 if (prefix_len_bytes == 16) {
537 proto_tree_add_item(tree, hf_framed_ipv6_prefix_ipv6, tvb, 2, prefix_len_bytes, ENC_NA);
538 } else if (prefix_len_bytes < 16) {
539 ws_in6_addr value;
540 address addr;
542 memset(&value.bytes, 0, sizeof(value));
543 tvb_memcpy(tvb, (uint8_t *)&value.bytes, 2, prefix_len_bytes);
544 value.bytes[prefix_len_bytes] = value.bytes[prefix_len_bytes] & (0xff<<(prefix_len % 8));
545 proto_tree_add_ipv6(tree, hf_framed_ipv6_prefix_ipv6, tvb, 2, prefix_len_bytes, &value);
546 set_address(&addr, AT_IPv6, 16, value.bytes);
547 diam_sub_dis->avp_str = wmem_strdup_printf(pinfo->pool, "%s/%u", address_to_str(pinfo->pool, &addr), prefix_len);
550 return prefix_len_bytes+2;
553 /* AVP Code: 1 User-Name */
554 /* Do special decoding of the User-Name depending on the interface */
555 static int
556 dissect_diameter_user_name(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
558 diam_sub_dis_t *diam_sub_dis = (diam_sub_dis_t*)data;
559 uint32_t application_id = 0, cmd_code = 0, str_len;
561 if (diam_sub_dis) {
562 application_id = diam_sub_dis->application_id;
563 cmd_code = diam_sub_dis->cmd_code;
566 switch (application_id) {
567 case DIAM_APPID_3GPP_S6A_S6D:
568 case DIAM_APPID_3GPP_SLH:
569 case DIAM_APPID_3GPP_S7A:
570 case DIAM_APPID_3GPP_S13:
571 str_len = tvb_reported_length(tvb);
572 dissect_e212_utf8_imsi(tvb, pinfo, tree, 0, str_len);
573 return str_len;
574 case DIAM_APPID_3GPP_SWX:
575 if (cmd_code != 305) {
576 str_len = tvb_reported_length(tvb);
577 dissect_e212_utf8_imsi(tvb, pinfo, tree, 0, str_len);
578 return str_len;
580 // cmd_code 305 (Push-Profile), can be either a User Profile
581 // Update (8.1.2.3), in which case User-Name is an IMSI as
582 // above, or an HSS Reset Indication (8.1.2.4.1), in which
583 // case User-Name is a User List containing a wild card
584 // or leading digits of IMSI series.
585 break;
586 case DIAM_APPID_3GPP_SWM:
587 case DIAM_APPID_3GPP_STA:
588 case DIAM_APPID_3GPP_S6B:
589 if (cmd_code == 268) {
590 // 3GPP TS 29.273 - For cmd_code 268 (Diameter-EAP),
591 // "The identity shall be represented in NAI form as
592 // specified in IETF RFC 4282 [15] and shall be formatted
593 // as defined in clause 19 of 3GPP TS 23.003 [14]. This
594 // IE shall include the leading digit used to
595 // differentiate between authentication schemes."
597 // Note that SWa uses the STa application ID, and
598 // SWd uses the application ID associated with
599 // the proxied command (STa here as well).
601 // For other command codes, the User-Name is different
602 // and does *not* include the leading digit as in EAP.
603 str_len = tvb_reported_length(tvb);
604 dissect_eap_identity_3gpp(tvb, pinfo, tree, 0, str_len);
605 return str_len;
607 break;
610 return 0;
613 /* AVP Code: 124 MIP6-Feature-Vector */
614 /* RFC 5447, 5779 */
615 /* 3GPP TS 29.273, V15.2.0 */
616 static int
617 dissect_diameter_mip6_feature_vector(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data)
619 static int * const flags_rfc[] = {
620 &hf_diameter_mip6_feature_vector_mip6_integrated,
621 &hf_diameter_mip6_feature_vector_local_home_agent_assignment,
622 &hf_diameter_mip6_feature_vector_pmip6_supported,
623 &hf_diameter_mip6_feature_vector_ip4_hoa_supported,
624 &hf_diameter_mip6_feature_vector_local_mag_routing_supported,
625 NULL
628 static int * const flags_3gpp[] = {
629 &hf_diameter_3gpp_mip6_feature_vector_assign_local_ip,
630 &hf_diameter_3gpp_mip6_feature_vector_mip4_supported,
631 &hf_diameter_3gpp_mip6_feature_vector_optimized_idle_mode_mobility,
632 &hf_diameter_3gpp_mip6_feature_vector_gtpv2_supported,
633 NULL
636 uint32_t application_id = 0;
637 diam_sub_dis_t *diam_sub_dis_inf = (diam_sub_dis_t*)data;
638 DISSECTOR_ASSERT(diam_sub_dis_inf);
640 application_id = diam_sub_dis_inf->application_id;
642 /* Hide the item created in packet-diameter.c and only show the one created here */
643 proto_item_set_hidden(diam_sub_dis_inf->item);
645 /* Dissect values defined in RFC 5447, 5779 */
646 proto_tree_add_bitmask(tree, tvb, 0, hf_diameter_mip6_feature_vector, ett_diameter_mip6_feature_vector, flags_rfc, ENC_BIG_ENDIAN);
648 switch (application_id) {
649 case DIAM_APPID_3GPP_STA:
650 case DIAM_APPID_3GPP_SWM:
651 case DIAM_APPID_3GPP_SWX:
652 case DIAM_APPID_3GPP_S6B:
653 /* Dissect values defined in TGPP TS 29.273, V15.2.0 */
654 proto_tree_add_bitmask(tree, tvb, 0, hf_diameter_3gpp_mip6_feature_vector, ett_diameter_3gpp_mip6_feature_vector, flags_3gpp, ENC_BIG_ENDIAN);
655 break;
658 return 8;
661 /* AVP Code: 443 Subscription-Id */
662 static int
663 dissect_diameter_subscription_id(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data)
665 /* Just reset our global subscription-id-type variable */
666 diam_sub_dis_t *diam_sub_dis_inf = (diam_sub_dis_t*)data;
667 diam_sub_dis_inf->subscription_id_type = SUBSCRIPTION_ID_TYPE_UNKNOWN;
669 return 0;
672 /* AVP Code: 450 Subscription-Id-Type */
673 static int
674 dissect_diameter_subscription_id_type(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void *data)
676 diam_sub_dis_t *diam_sub_dis_inf = (diam_sub_dis_t*)data;
677 diam_sub_dis_inf->subscription_id_type = tvb_get_ntohl(tvb, 0);
679 return 0;
682 /* AVP Code: 444 Subscription-Id-Data */
683 static int
684 dissect_diameter_subscription_id_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
686 uint32_t str_len;
687 diam_sub_dis_t *diam_sub_dis_inf = (diam_sub_dis_t*)data;
688 uint32_t subscription_id_type = diam_sub_dis_inf->subscription_id_type;
690 switch (subscription_id_type) {
691 case SUBSCRIPTION_ID_TYPE_IMSI:
692 str_len = tvb_reported_length(tvb);
693 dissect_e212_utf8_imsi(tvb, pinfo, tree, 0, str_len);
694 return str_len;
695 case SUBSCRIPTION_ID_TYPE_E164:
696 str_len = tvb_reported_length(tvb);
697 dissect_e164_msisdn(tvb, tree, 0, str_len, E164_ENC_UTF8);
698 return str_len;
701 return 0;
704 /* AVP Code: 458 User-Equipment-Info */
705 static int
706 dissect_diameter_user_equipment_info(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data)
708 /* Just reset our global subscription-id-type variable */
709 diam_sub_dis_t *diam_sub_dis_inf = (diam_sub_dis_t*)data;
710 diam_sub_dis_inf->user_equipment_info_type = USER_EQUIPMENT_INFO_TYPE_UNKNOWN;
712 return 0;
715 /* AVP Code: 459 User-Equipment-Info-Type */
716 /* RFC 8506 section 8.50 */
717 static int
718 dissect_diameter_user_equipment_info_type(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void *data)
720 diam_sub_dis_t *diam_sub_dis_inf = (diam_sub_dis_t*)data;
721 diam_sub_dis_inf->user_equipment_info_type = tvb_get_ntohl(tvb, 0);
723 return 0;
726 /* AVP Code: 460 User-Equipment-Info-Value */
727 /* RFC 8506 section 8.51 */
728 static int
729 dissect_diameter_user_equipment_info_value(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
731 uint32_t len;
732 diam_sub_dis_t *diam_sub_dis_inf = (diam_sub_dis_t*)data;
733 uint32_t user_equipment_info_type = diam_sub_dis_inf->user_equipment_info_type;
735 switch (user_equipment_info_type) {
736 case USER_EQUIPMENT_INFO_TYPE_IMEISV:
737 /* RFC 8506 section 8.53, 3GPP TS 23.003 */
738 len = tvb_reported_length(tvb);
739 /* IMEISV is 16 digits, but often transmitted BCD coded in 8 octets.
740 Some implementations use IMEI (15 digits) instead of IMEISV */
741 if (len == 8) {
742 proto_tree_add_item(tree, hf_diameter_user_equipment_info_imeisv, tvb, 0, len, ENC_BCD_DIGITS_0_9|ENC_LITTLE_ENDIAN|ENC_NA);
743 return len;
744 } else if (len == 16) {
745 proto_tree_add_item(tree, hf_diameter_user_equipment_info_imeisv, tvb, 0, len, ENC_ASCII);
746 return len;
747 } else if (len == 15) {
748 proto_tree_add_item(tree, hf_diameter_user_equipment_info_imeisv, tvb, 0, len, ENC_ASCII);
749 proto_tree_add_expert(tree, pinfo, &ei_diameter_unexpected_imei_as_user_equipment_info, tvb, 0, len);
750 return len;
752 proto_tree_add_expert(tree, pinfo, &ei_diameter_invalid_user_equipment_info_value_len, tvb, 0, len);
753 break;
754 case USER_EQUIPMENT_INFO_TYPE_MAC:
755 /* RFC 8506 section 8.54, RFC 5777 section 4.1.7.8 */
756 len = tvb_reported_length(tvb);
757 if (len == FT_ETHER_LEN) {
758 proto_tree_add_item(tree, hf_diameter_user_equipment_info_mac, tvb, 0, len, ENC_NA);
759 return len;
761 proto_tree_add_expert(tree, pinfo, &ei_diameter_invalid_user_equipment_info_value_len, tvb, 0, len);
762 break;
763 case USER_EQUIPMENT_INFO_TYPE_EUI64:
764 /* RFC 8506 section 8.55 */
765 len = tvb_reported_length(tvb);
766 if (len == FT_EUI64_LEN) {
767 proto_tree_add_item(tree, hf_diameter_user_equipment_info_eui64, tvb, 0, len, ENC_BIG_ENDIAN);
768 return len;
770 proto_tree_add_expert(tree, pinfo, &ei_diameter_invalid_user_equipment_info_value_len, tvb, 0, len);
771 break;
772 case USER_EQUIPMENT_INFO_TYPE_MODIFIED_EUI64:
773 /* RFC 8506 section 8.56, RFC 4291 */
774 len = tvb_reported_length(tvb);
775 if (len == FT_EUI64_LEN) {
776 proto_tree_add_item(tree, hf_diameter_user_equipment_info_modified_eui64, tvb, 0, len, ENC_BIG_ENDIAN);
777 return len;
779 proto_tree_add_expert(tree, pinfo, &ei_diameter_invalid_user_equipment_info_value_len, tvb, 0, len);
780 break;
783 return 0;
786 /* Call subdissectors for AVPs.
787 * This is a separate function to avoid having any local variables that might
788 * get clobbered by the exception longjmp() (without having to declare the
789 * variables as volatile and deal with casting them).
791 static void
792 call_avp_subdissector(uint32_t vendorid, uint32_t code, tvbuff_t *subtvb, packet_info *pinfo, proto_tree *avp_tree, diam_sub_dis_t *diam_sub_dis_inf)
794 TRY {
795 switch (vendorid) {
796 case 0:
797 dissector_try_uint_with_data(diameter_dissector_table, code, subtvb, pinfo, avp_tree, false, diam_sub_dis_inf);
798 break;
799 case VENDOR_ERICSSON:
800 dissector_try_uint_with_data(diameter_ericsson_avp_dissector_table, code, subtvb, pinfo, avp_tree, false, diam_sub_dis_inf);
801 break;
802 case VENDOR_VERIZON:
803 dissector_try_uint_with_data(diameter_verizon_avp_dissector_table, code, subtvb, pinfo, avp_tree, false, diam_sub_dis_inf);
804 break;
805 case VENDOR_THE3GPP:
806 dissector_try_uint_with_data(diameter_3gpp_avp_dissector_table, code, subtvb, pinfo, avp_tree, false, diam_sub_dis_inf);
807 break;
808 default:
809 break;
812 /* Debug
813 proto_tree_add_subtree(avp_tree, subtvb, 0, -1, "AVP %u data, Vendor Id %u ",code,vendorid);
816 CATCH_NONFATAL_ERRORS {
817 show_exception(subtvb, pinfo, avp_tree, EXCEPT_CODE, GET_MESSAGE);
819 ENDTRY;
822 /* Dissect an AVP at offset */
823 static int
824 dissect_diameter_avp(diam_ctx_t *c, tvbuff_t *tvb, int offset, diam_sub_dis_t *diam_sub_dis_inf, bool update_col_info)
826 uint32_t code = tvb_get_ntohl(tvb,offset);
827 uint32_t len = tvb_get_ntohl(tvb,offset+4);
828 uint32_t vendor_flag = len & 0x80000000;
829 uint32_t flags_bits_idx = (len & 0xE0000000) >> 29;
830 uint32_t flags_bits = (len & 0xFF000000) >> 24;
831 uint32_t vendorid = vendor_flag ? tvb_get_ntohl(tvb,offset+8) : 0 ;
832 wmem_tree_key_t k[3];
833 diam_avp_t *a;
834 proto_item *pi, *avp_item;
835 proto_tree *avp_tree, *save_tree;
836 tvbuff_t *subtvb;
837 diam_vnd_t *vendor;
838 const char *code_str;
839 const char *avp_str = NULL;
840 uint8_t pad_len;
842 k[0].length = 1;
843 k[0].key = &code;
845 k[1].length = 1;
846 k[1].key = &vendorid;
848 k[2].length = 0;
849 k[2].key = NULL;
851 a = (diam_avp_t *)wmem_tree_lookup32_array(dictionary.avps,k);
853 len &= 0x00ffffff;
854 pad_len = (len % 4) ? 4 - (len % 4) : 0 ;
856 if (!a) {
857 a = &unknown_avp;
859 if (vendor_flag) {
860 if (! (vendor = (diam_vnd_t *)wmem_tree_lookup32(dictionary.vnds,vendorid) ))
861 vendor = &unknown_vendor;
862 } else {
863 vendor = &no_vnd;
865 } else {
866 vendor = (diam_vnd_t *)a->vendor;
869 if (vendor->vs_avps_ext == NULL) {
870 wmem_array_sort(vendor->vs_avps, compare_avps);
871 vendor->vs_avps_ext = value_string_ext_new(VND_AVP_VS(vendor),
872 VND_AVP_VS_LEN(vendor)+1,
873 wmem_strdup_printf(wmem_epan_scope(), "diameter_vendor_%s",
874 enterprises_lookup(vendorid, "Unknown")));
875 #if 0
876 { /* Debug code */
877 value_string *vendor_avp_vs = VALUE_STRING_EXT_VS_P(vendor->vs_avps_ext);
878 int i = 0;
879 while (vendor_avp_vs[i].strptr != NULL) {
880 ws_warning("%u %s", vendor_avp_vs[i].value, vendor_avp_vs[i].strptr);
881 i++;
884 #endif
886 /* Check if the length is sane */
887 if (len > (uint32_t)tvb_reported_length_remaining(tvb, offset)) {
888 proto_tree_add_expert_format(c->tree, c->pinfo, &ei_diameter_invalid_avp_len, tvb, offset + 4, 4,
889 "Wrong AVP(%u) length %u",
890 code,
891 len);
892 return tvb_reported_length(tvb);
896 * Workaround for a MS-CHAPv2 capture from Bug 15603 that lacks padding.
898 if (tvb_reported_length_remaining(tvb, offset + len) < pad_len) {
899 pad_len = (uint32_t)tvb_reported_length_remaining(tvb, offset + len);
902 /* Add root of tree for this AVP */
903 avp_item = proto_tree_add_item(c->tree, hf_diameter_avp, tvb, offset, len + pad_len, ENC_NA);
904 avp_tree = proto_item_add_subtree(avp_item, a->ett);
906 pi = proto_tree_add_item(avp_tree,hf_diameter_avp_code,tvb,offset,4,ENC_BIG_ENDIAN);
907 code_str = val_to_str_ext_const(code, vendor->vs_avps_ext, "Unknown");
908 proto_item_append_text(pi," %s", code_str);
910 /* Code */
911 if (a == &unknown_avp) {
912 proto_tree *tu = proto_item_add_subtree(pi,ett_unknown);
913 proto_tree_add_expert_format(tu, c->pinfo, &ei_diameter_avp_code, tvb, offset, 4,
914 "Unknown AVP %u (vendor=%s), if you know what this is you can add it to dictionary.xml", code,
915 enterprises_lookup(vendorid, "Unknown"));
918 offset += 4;
920 proto_item_set_text(avp_item,"AVP: %s(%u) l=%u f=%s", code_str, code, len, avpflags_str[flags_bits_idx]);
921 if (update_col_info) {
922 col_append_fstr(c->pinfo->cinfo, COL_INFO, " %s", code_str);
925 /* Flags */
927 static int * const diameter_avp_flags[] = {
928 &hf_diameter_avp_flags_vendor_specific,
929 &hf_diameter_avp_flags_mandatory,
930 &hf_diameter_avp_flags_protected,
931 &hf_diameter_avp_flags_reserved3,
932 &hf_diameter_avp_flags_reserved4,
933 &hf_diameter_avp_flags_reserved5,
934 &hf_diameter_avp_flags_reserved6,
935 &hf_diameter_avp_flags_reserved7,
936 NULL
939 pi = proto_tree_add_bitmask_with_flags(avp_tree, tvb, offset, hf_diameter_avp_flags,
940 ett_diameter_avp_flags, diameter_avp_flags, ENC_BIG_ENDIAN, BMT_NO_FALSE | BMT_NO_INT);
941 if (flags_bits & 0x1f) expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
944 offset += 1;
946 /* Length */
947 proto_tree_add_item(avp_tree,hf_diameter_avp_len,tvb,offset,3,ENC_BIG_ENDIAN);
948 offset += 3;
950 /* Vendor flag */
951 if (vendor_flag) {
952 proto_item_append_text(avp_item," vnd=%s", val_to_str(vendorid, vnd_short_vs, "%d"));
953 pi = proto_tree_add_item(avp_tree,hf_diameter_avp_vendor_id,tvb,offset,4,ENC_BIG_ENDIAN);
954 if (vendor == &unknown_vendor) {
955 proto_tree *tu = proto_item_add_subtree(pi,ett_unknown);
956 proto_tree_add_expert(tu, c->pinfo, &ei_diameter_avp_vendor_id, tvb, offset, 4);
958 offset += 4;
961 /* Data is empty so return now */
962 if ( len == (uint32_t)(vendor_flag ? 12 : 8) ) {
963 /* AVP=Requested-Service-Unit(437) may be empty.
965 * RFC 4006, 8.16 (page 64):
966 * The Requested-Service-Unit AVP MAY contain the amount of requested
967 * service units or the requested monetary value. It MUST be present in
968 * the initial interrogation and within the intermediate interrogations
969 * in which new quota is requested.
971 * Command-Code = "Credit-Control" (272)
972 * ApplicationID = "Diameter Credit Control Application" (4)
974 if (!((code == 437)
975 && (diam_sub_dis_inf->cmd_code == 272)
976 && (diam_sub_dis_inf->parent_message_is_request)
977 && (diam_sub_dis_inf->application_id == 4))) {
978 proto_tree_add_expert(avp_tree, c->pinfo, &ei_diameter_avp_no_data, tvb, offset, 0);
980 /* pad_len is always 0 in this case, but kept here for consistency */
981 return len+pad_len;
983 /* If we are dissecting a grouped AVP and find a Vendor Id AVP(266), save it */
984 if ((diam_sub_dis_inf->dis_gouped) && (!vendor_flag) && (code==266)) {
985 diam_sub_dis_inf->vendor_id = tvb_get_ntohl(tvb,offset);
988 subtvb = tvb_new_subset_length(tvb,offset,len-(8+(vendor_flag?4:0)));
989 offset += len-(8+(vendor_flag?4:0));
991 save_tree = c->tree;
992 c->tree = avp_tree;
994 /* The Experimental-Result-Code AVP (298) comes inside the Experimental-Result
995 * grouped AVP (297). The Vendor-ID AVP in the Experimental-Result specifies the
996 * name space of the Experimental-Result-Code. Unfortunately we don't have a way
997 * to specify, in XML, different Experimental-Result-Code enum values for different
998 * Vendor-IDs so we choose a Vendor-ID whose values get to go in XML (we chose
999 * 3GPP) and handle other Vendor-IDs through the "diameter.vnd_exp_res" dissector
1000 * table.
1002 if ((diam_sub_dis_inf->dis_gouped)
1003 && (!vendor_flag)
1004 && (code==298)
1005 && (diam_sub_dis_inf->vendor_id != 0)
1006 && (diam_sub_dis_inf->vendor_id != VENDOR_THE3GPP))
1008 /* call subdissector */
1009 if (!dissector_try_uint_with_data(diameter_expr_result_vnd_table, diam_sub_dis_inf->vendor_id,
1010 subtvb, c->pinfo, avp_tree, false, diam_sub_dis_inf)) {
1011 /* No subdissector for this vendor ID, use the generic one */
1012 dissect_diameter_other_vendor_exp_res(c, subtvb, avp_tree, diam_sub_dis_inf);
1015 if (diam_sub_dis_inf->avp_str) {
1016 proto_item_append_text(avp_item," val=%s", diam_sub_dis_inf->avp_str);
1018 } else {
1019 avp_str = a->dissector_rfc(c,a,subtvb, diam_sub_dis_inf);
1021 c->tree = save_tree;
1023 diam_sub_dis_inf->avp_str = NULL;
1024 call_avp_subdissector(vendorid, code, subtvb, c->pinfo, avp_tree, diam_sub_dis_inf);
1026 /* Let the subdissector have precedence filling in the avp_item string */
1027 if (diam_sub_dis_inf->avp_str) {
1028 proto_item_append_text(avp_item," val=%s", diam_sub_dis_inf->avp_str);
1029 } else if (avp_str) {
1030 proto_item_append_text(avp_item," val=%s", avp_str);
1034 if (pad_len) {
1035 uint8_t i;
1037 pi = proto_tree_add_item(avp_tree, hf_diameter_avp_pad, tvb, offset, pad_len, ENC_NA);
1038 for (i=0; i < pad_len; i++) {
1039 if (tvb_get_uint8(tvb, offset++) != 0) {
1040 expert_add_info(c->pinfo, pi, &ei_diameter_avp_pad);
1041 break;
1045 if ((len + pad_len) % 4) {
1046 proto_tree_add_expert(avp_tree, c->pinfo, &ei_diameter_avp_pad_missing, tvb, offset, pad_len);
1049 return len+pad_len;
1052 static const char *
1053 address_rfc_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
1055 char *label = NULL;
1056 address_avp_t *t = (address_avp_t *)a->type_data;
1057 int len = tvb_reported_length(tvb);
1058 proto_item *pi = proto_tree_add_item(c->tree, a->hf_value, tvb, 0, len, ENC_BIG_ENDIAN);
1059 proto_tree *pt = proto_item_add_subtree(pi,t->ett);
1060 uint32_t addr_type;
1061 len = len-2;
1063 proto_tree_add_item_ret_uint(pt, t->hf_address_type, tvb, 0, 2, ENC_NA, &addr_type);
1064 /* See afn.h and https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml */
1065 switch (addr_type ) {
1066 case AFNUM_INET:
1067 if (len != 4) {
1068 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);
1069 return "[Malformed]";
1071 pi = proto_tree_add_item(pt,t->hf_ipv4,tvb,2,4,ENC_BIG_ENDIAN);
1072 break;
1073 case AFNUM_INET6:
1074 if (len != 16) {
1075 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);
1076 return "[Malformed]";
1078 pi = proto_tree_add_item(pt,t->hf_ipv6,tvb,2,16,ENC_NA);
1079 break;
1080 case AFNUM_E164:
1081 /* It's unclear what format the e164 address would be encoded in but AVP 3GPP 2008 has
1082 * ...value 8, E.164, and the address information is UTF8 encoded.
1084 if (tvb_ascii_isprint(tvb, 2, len)) {
1085 pi = proto_tree_add_item(pt, t->hf_e164_str, tvb, 2, len, ENC_ASCII | ENC_NA);
1086 } else {
1087 pi = proto_tree_add_item(pt, t->hf_other, tvb, 2, -1, ENC_BIG_ENDIAN);
1089 break;
1090 default:
1091 pi = proto_tree_add_item(pt,t->hf_other,tvb,2,-1,ENC_BIG_ENDIAN);
1092 break;
1095 if (c->tree) {
1096 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1097 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1098 label = strstr(label,": ")+2;
1101 return label;
1104 static const char *
1105 proto_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf)
1107 proto_avp_t *t = (proto_avp_t *)a->type_data;
1109 col_set_writable(c->pinfo->cinfo, COL_PROTOCOL, false);
1110 col_set_writable(c->pinfo->cinfo, COL_INFO, false);
1112 if (!t->handle) {
1113 t->handle = find_dissector(t->name);
1114 if (!t->handle) t->handle = data_handle;
1117 TRY {
1118 call_dissector_with_data(t->handle, tvb, c->pinfo, c->tree, diam_sub_dis_inf);
1120 CATCH_NONFATAL_ERRORS {
1121 show_exception(tvb, c->pinfo, c->tree, EXCEPT_CODE, GET_MESSAGE);
1123 ENDTRY;
1125 return "";
1128 static const char *
1129 time_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
1131 int len = tvb_reported_length(tvb);
1132 char *label = NULL;
1133 proto_item *pi;
1135 if ( len != 4 ) {
1136 proto_tree_add_expert_format(c->tree, c->pinfo, &ei_diameter_avp_len, tvb, 0, 4,
1137 "Bad Timestamp Length: %d instead of 4", len);
1138 return "[Malformed]";
1141 if (c->tree) {
1142 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1143 pi = proto_tree_add_item(c->tree, (a->hf_value), tvb, 0, 4, ENC_TIME_SECS_NTP|ENC_BIG_ENDIAN);
1144 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1145 label = strstr(label,": ")+2;
1148 return label;
1151 static const char *
1152 address_radius_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
1154 char *label = NULL;
1156 address_avp_t *t = (address_avp_t *)a->type_data;
1157 proto_item *pi = proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_reported_length(tvb),ENC_BIG_ENDIAN);
1158 proto_tree *pt = proto_item_add_subtree(pi,t->ett);
1159 uint32_t len = tvb_reported_length(tvb);
1161 switch (len) {
1162 case 4:
1163 pi = proto_tree_add_item(pt,t->hf_ipv4,tvb,0,4,ENC_BIG_ENDIAN);
1164 break;
1165 case 16:
1166 pi = proto_tree_add_item(pt,t->hf_ipv6,tvb,0,16,ENC_NA);
1167 break;
1168 default:
1169 pi = proto_tree_add_item(pt,t->hf_other,tvb,0,len,ENC_BIG_ENDIAN);
1170 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
1171 "Bad Address Length (%u)", len);
1173 break;
1176 if (c->tree) {
1177 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1178 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1179 label = strstr(label,": ")+2;
1182 return label;
1185 static const char *
1186 simple_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
1188 char *label = NULL;
1190 if (c->tree) {
1191 proto_item *pi = proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_reported_length(tvb),ENC_BIG_ENDIAN);
1192 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1193 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1194 label = strstr(label,": ")+2;
1197 return label;
1200 static const char *
1201 utf8_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
1203 char *label = NULL;
1205 if (c->tree) {
1206 proto_item *pi = proto_tree_add_item(c->tree,a->hf_value,tvb,0,tvb_reported_length(tvb),ENC_UTF_8|ENC_BIG_ENDIAN);
1207 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1208 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1209 label = strstr(label,": ")+2;
1212 return label;
1215 static const char *
1216 integer32_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
1218 char *label = NULL;
1219 proto_item *pi;
1221 /* Verify length before adding */
1222 int length = tvb_reported_length(tvb);
1223 if (length == 4) {
1224 if (c->tree) {
1225 pi= proto_tree_add_item(c->tree, a->hf_value, tvb, 0, length, ENC_BIG_ENDIAN);
1226 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1227 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1228 label = strstr(label,": ")+2;
1231 else {
1232 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
1233 tvb, 0, length, NULL,
1234 "Error! Bad Integer32 Length");
1235 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
1236 "Bad Integer32 Length (%u)", length);
1237 proto_item_set_generated(pi);
1240 return label;
1243 static const char *
1244 integer64_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
1246 char *label = NULL;
1247 proto_item *pi;
1249 /* Verify length before adding */
1250 int length = tvb_reported_length(tvb);
1251 if (length == 8) {
1252 if (c->tree) {
1253 pi= proto_tree_add_item(c->tree, a->hf_value, tvb, 0, length, ENC_BIG_ENDIAN);
1254 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1255 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1256 label = strstr(label,": ")+2;
1259 else {
1260 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
1261 tvb, 0, length, NULL,
1262 "Error! Bad Integer64 Length");
1263 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
1264 "Bad Integer64 Length (%u)", length);
1265 proto_item_set_generated(pi);
1268 return label;
1271 static const char *
1272 unsigned32_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf)
1274 char *label = NULL;
1275 proto_item *pi;
1277 /* Verify length before adding */
1278 int length = tvb_reported_length(tvb);
1279 if (length == 4) {
1280 if (c->tree) {
1281 diam_sub_dis_inf->item = pi = proto_tree_add_item(c->tree, a->hf_value, tvb, 0, length, ENC_BIG_ENDIAN);
1282 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1283 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1284 label = strstr(label,": ")+2;
1287 else {
1288 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
1289 tvb, 0, length, NULL,
1290 "Error! Bad Unsigned32 Length");
1291 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
1292 "Bad Unsigned32 Length (%u)", length);
1293 proto_item_set_generated(pi);
1296 return label;
1299 static const char *
1300 unsigned64_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
1302 char *label = NULL;
1303 proto_item *pi;
1305 /* Verify length before adding */
1306 int length = tvb_reported_length(tvb);
1307 if (length == 8) {
1308 if (c->tree) {
1309 pi= proto_tree_add_item(c->tree, a->hf_value, tvb, 0, length, ENC_BIG_ENDIAN);
1310 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1311 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1312 label = strstr(label,": ")+2;
1315 else {
1316 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
1317 tvb, 0, length, NULL,
1318 "Error! Bad Unsigned64 Length");
1319 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
1320 "Bad Unsigned64 Length (%u)", length);
1321 proto_item_set_generated(pi);
1324 return label;
1327 static const char *
1328 float32_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
1330 char *label = NULL;
1331 proto_item *pi;
1333 /* Verify length before adding */
1334 int length = tvb_reported_length(tvb);
1335 if (length == 4) {
1336 if (c->tree) {
1337 pi= proto_tree_add_item(c->tree,a->hf_value, tvb, 0, length, ENC_BIG_ENDIAN);
1338 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1339 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1340 label = strstr(label,": ")+2;
1343 else {
1344 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
1345 tvb, 0, length, NULL,
1346 "Error! Bad Float32 Length");
1347 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
1348 "Bad Float32 Length (%u)", length);
1349 proto_item_set_generated(pi);
1352 return label;
1355 static const char *
1356 float64_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf _U_)
1358 char *label = NULL;
1359 proto_item *pi;
1361 /* Verify length before adding */
1362 int length = tvb_reported_length(tvb);
1363 if (length == 8) {
1364 if (c->tree) {
1365 pi= proto_tree_add_item(c->tree, a->hf_value, tvb, 0, length, ENC_BIG_ENDIAN);
1366 label = (char *)wmem_alloc(c->pinfo->pool, ITEM_LABEL_LENGTH+1);
1367 proto_item_fill_label(PITEM_FINFO(pi), label, NULL);
1368 label = strstr(label,": ")+2;
1371 else {
1372 pi = proto_tree_add_bytes_format(c->tree, hf_diameter_avp_data_wrong_length,
1373 tvb, 0, length, NULL,
1374 "Error! Bad Float64 Length");
1375 expert_add_info_format(c->pinfo, pi, &ei_diameter_avp_len,
1376 "Bad Float64 Length (%u)", length);
1377 proto_item_set_generated(pi);
1380 return label;
1383 static const char *
1384 grouped_avp(diam_ctx_t *c, diam_avp_t *a, tvbuff_t *tvb, diam_sub_dis_t *diam_sub_dis_inf)
1386 int offset = 0;
1387 int len = tvb_reported_length(tvb);
1388 proto_item *pi = proto_tree_add_item(c->tree, a->hf_value, tvb , 0 , -1, ENC_BIG_ENDIAN);
1389 proto_tree *pt = c->tree;
1391 c->tree = proto_item_add_subtree(pi,a->ett);
1393 /* Set the flag that we are dissecting a grouped AVP */
1394 diam_sub_dis_inf->dis_gouped = true;
1395 while (offset < len) {
1396 offset += dissect_diameter_avp(c, tvb, offset, diam_sub_dis_inf, false);
1398 /* Clear info collected in grouped AVP */
1399 diam_sub_dis_inf->vendor_id = 0;
1400 diam_sub_dis_inf->dis_gouped = false;
1401 diam_sub_dis_inf->avp_str = NULL;
1403 c->tree = pt;
1405 return NULL;
1408 static const char *msgflags_str[] = {
1409 "----", "---T", "--E-", "--ET",
1410 "-P--", "-P-T", "-PE-", "-PET",
1411 "R---", "R--T", "R-E-", "R-ET",
1412 "RP--", "RP-T", "RPE-", "RPET"
1415 static int * const diameter_flags_fields[] = {
1416 &hf_diameter_flags_request,
1417 &hf_diameter_flags_proxyable,
1418 &hf_diameter_flags_error,
1419 &hf_diameter_flags_T,
1420 &hf_diameter_flags_reserved4,
1421 &hf_diameter_flags_reserved5,
1422 &hf_diameter_flags_reserved6,
1423 &hf_diameter_flags_reserved7,
1424 NULL
1427 static int
1428 dissect_diameter_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
1430 uint32_t version;
1431 uint64_t flags_bits;
1432 int packet_len;
1433 proto_item *pi, *cmd_item, *app_item, *version_item;
1434 proto_tree *diam_tree;
1435 diam_ctx_t *c = wmem_new0(pinfo->pool, diam_ctx_t);
1436 int offset;
1437 const char *cmd_str;
1438 uint32_t cmd;
1439 uint32_t hop_by_hop_id, end_to_end_id;
1440 conversation_t *conversation;
1441 diameter_conv_info_t *diameter_conv_info;
1442 diameter_req_ans_pair_t *diameter_pair = NULL;
1443 wmem_tree_t *pdus_tree;
1444 proto_item *it;
1445 nstime_t ns;
1446 diam_sub_dis_t *diam_sub_dis_inf = wmem_new0(pinfo->pool, diam_sub_dis_t);
1448 /* Set default value Subscription-Id-Type and User-Equipment-Info-Type as XXX_UNKNOWN */
1449 diam_sub_dis_inf->subscription_id_type = SUBSCRIPTION_ID_TYPE_UNKNOWN;
1450 diam_sub_dis_inf->user_equipment_info_type = USER_EQUIPMENT_INFO_TYPE_UNKNOWN;
1452 /* Load header fields if not already done */
1453 if (hf_diameter_code <= 0)
1454 proto_registrar_get_byname("diameter.code");
1455 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DIAMETER");
1458 if (have_tap_listener(exported_pdu_tap)){
1459 export_diameter_pdu(pinfo,tvb);
1462 pi = proto_tree_add_item(tree,proto_diameter,tvb,0,-1,ENC_NA);
1463 diam_tree = proto_item_add_subtree(pi,ett_diameter);
1465 c->tree = diam_tree;
1466 c->pinfo = pinfo;
1468 version_item = proto_tree_add_item_ret_uint(diam_tree, hf_diameter_version, tvb, 0, 1, ENC_BIG_ENDIAN, &version);
1469 if (version != DIAMETER_RFC) {
1470 expert_add_info(c->pinfo, version_item, &ei_diameter_version);
1472 proto_tree_add_item_ret_uint(diam_tree, hf_diameter_length, tvb, 1, 3, ENC_BIG_ENDIAN, &packet_len);
1474 pi = proto_tree_add_bitmask_ret_uint64(diam_tree, tvb, 4, hf_diameter_flags, ett_diameter_flags, diameter_flags_fields, ENC_BIG_ENDIAN, &flags_bits);
1475 if (flags_bits & 0x0f) {
1476 expert_add_info(c->pinfo, pi, &ei_diameter_reserved_bit_set);
1479 diam_sub_dis_inf->parent_message_is_request = (flags_bits & DIAM_FLAGS_R) ? true : false;
1481 cmd_item = proto_tree_add_item_ret_uint(diam_tree, hf_diameter_code, tvb, 5, 3, ENC_BIG_ENDIAN, &cmd);
1482 diam_sub_dis_inf->cmd_code = cmd;
1484 app_item = proto_tree_add_item_ret_uint(diam_tree, hf_diameter_application_id, tvb, 8, 4,
1485 ENC_BIG_ENDIAN, &diam_sub_dis_inf->application_id);
1487 if (try_val_to_str_ext(diam_sub_dis_inf->application_id, dictionary.applications) == NULL) {
1488 proto_tree *tu = proto_item_add_subtree(app_item,ett_unknown);
1489 proto_tree_add_expert_format(tu, c->pinfo, &ei_diameter_application_id, tvb, 8, 4,
1490 "Unknown Application Id (%u), if you know what this is you can add it to dictionary.xml", diam_sub_dis_inf->application_id);
1493 cmd_str = val_to_str_const(cmd, cmd_vs, "Unknown");
1494 if (strcmp(cmd_str, "Unknown") == 0) {
1495 expert_add_info(c->pinfo, cmd_item, &ei_diameter_code);
1499 proto_tree_add_item_ret_uint(diam_tree, hf_diameter_hopbyhopid, tvb, 12, 4, ENC_BIG_ENDIAN, &hop_by_hop_id);
1500 proto_tree_add_item_ret_uint(diam_tree, hf_diameter_endtoendid, tvb, 16, 4, ENC_BIG_ENDIAN, &end_to_end_id);
1502 col_add_fstr(pinfo->cinfo, COL_INFO,
1503 "cmd=%s%s(%d) flags=%s %s=%s(%d) h2h=%x e2e=%x",
1504 cmd_str,
1505 ((flags_bits>>4)&0x08) ? " Request" : " Answer",
1506 cmd,
1507 msgflags_str[((flags_bits>>4)&0x0f)],
1508 "appl",
1509 val_to_str_ext_const(diam_sub_dis_inf->application_id, dictionary.applications, "Unknown"),
1510 diam_sub_dis_inf->application_id,
1511 hop_by_hop_id,
1512 end_to_end_id);
1514 col_append_str(pinfo->cinfo, COL_INFO, " | ");
1515 col_set_fence(pinfo->cinfo, COL_INFO);
1518 /* Conversation tracking stuff */
1520 * FIXME: Looking at epan/conversation.c it seems unlikely that this will work properly in
1521 * multi-homed SCTP connections. This will probably need to be fixed at some point.
1524 conversation = find_or_create_conversation(pinfo);
1526 diameter_conv_info = (diameter_conv_info_t *)conversation_get_proto_data(conversation, proto_diameter);
1527 if (!diameter_conv_info) {
1528 diameter_conv_info = wmem_new(wmem_file_scope(), diameter_conv_info_t);
1529 diameter_conv_info->pdu_trees = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
1531 conversation_add_proto_data(conversation, proto_diameter, diameter_conv_info);
1534 /* pdus_tree is an wmem_tree keyed by frame number (in order to handle hop-by-hop collisions */
1535 pdus_tree = (wmem_tree_t *)wmem_map_lookup(diameter_conv_info->pdu_trees, GUINT_TO_POINTER(hop_by_hop_id));
1537 if (pdus_tree == NULL && (flags_bits & DIAM_FLAGS_R)) {
1538 /* This is the first request we've seen with this hop-by-hop id */
1539 pdus_tree = wmem_tree_new(wmem_file_scope());
1540 wmem_map_insert(diameter_conv_info->pdu_trees, GUINT_TO_POINTER(hop_by_hop_id), pdus_tree);
1543 if (pdus_tree) {
1544 if (!pinfo->fd->visited) {
1545 if (flags_bits & DIAM_FLAGS_R) {
1546 /* This is a request */
1547 diameter_pair = wmem_new(wmem_file_scope(), diameter_req_ans_pair_t);
1548 diameter_pair->hop_by_hop_id = hop_by_hop_id;
1549 diameter_pair->end_to_end_id = end_to_end_id;
1550 diameter_pair->cmd_code = cmd;
1551 diameter_pair->result_code = 0;
1552 diameter_pair->cmd_str = cmd_str;
1553 diameter_pair->req_frame = pinfo->num;
1554 diameter_pair->ans_frame = 0;
1555 diameter_pair->req_time = pinfo->abs_ts;
1556 wmem_tree_insert32(pdus_tree, pinfo->num, (void *)diameter_pair);
1557 } else {
1558 /* Look for a request which occurs earlier in the trace than this answer. */
1559 diameter_pair = (diameter_req_ans_pair_t *)wmem_tree_lookup32_le(pdus_tree, pinfo->num);
1561 /* Verify the end-to-end-id matches before declaring a match */
1562 if (diameter_pair && diameter_pair->end_to_end_id == end_to_end_id) {
1563 diameter_pair->ans_frame = pinfo->num;
1566 } else {
1567 /* Look for a request which occurs earlier in the trace than this answer. */
1568 diameter_pair = (diameter_req_ans_pair_t *)wmem_tree_lookup32_le(pdus_tree, pinfo->num);
1570 /* If the end-to-end ID doesn't match then this is not the request we were
1571 * looking for.
1573 if (diameter_pair && diameter_pair->end_to_end_id != end_to_end_id)
1574 diameter_pair = NULL;
1578 if (!diameter_pair) {
1579 /* create a "fake" diameter_pair structure */
1580 diameter_pair = wmem_new(pinfo->pool, diameter_req_ans_pair_t);
1581 diameter_pair->hop_by_hop_id = hop_by_hop_id;
1582 diameter_pair->cmd_code = cmd;
1583 diameter_pair->result_code = 0;
1584 diameter_pair->cmd_str = cmd_str;
1585 diameter_pair->req_frame = 0;
1586 diameter_pair->ans_frame = 0;
1587 diameter_pair->req_time = pinfo->abs_ts;
1589 diameter_pair->processing_request=(flags_bits & DIAM_FLAGS_R)!= 0;
1591 /* print state tracking info in the tree */
1592 if (flags_bits & DIAM_FLAGS_R) {
1593 /* This is a request */
1594 if (diameter_pair->ans_frame) {
1595 it = proto_tree_add_uint(diam_tree, hf_diameter_answer_in,
1596 tvb, 0, 0, diameter_pair->ans_frame);
1597 proto_item_set_generated(it);
1599 } else {
1600 /* This is an answer */
1601 if (diameter_pair->req_frame) {
1602 it = proto_tree_add_uint(diam_tree, hf_diameter_answer_to,
1603 tvb, 0, 0, diameter_pair->req_frame);
1604 proto_item_set_generated(it);
1606 nstime_delta(&ns, &pinfo->abs_ts, &diameter_pair->req_time);
1607 diameter_pair->srt_time = ns;
1608 it = proto_tree_add_time(diam_tree, hf_diameter_answer_time, tvb, 0, 0, &ns);
1609 proto_item_set_generated(it);
1610 /* TODO: Populate result_code in tap record from AVP 268 */
1614 offset = 20;
1616 /* Dissect AVPs until the end of the packet is reached */
1617 while (offset < packet_len) {
1618 offset += dissect_diameter_avp(c, tvb, offset, diam_sub_dis_inf, false);
1621 /* Handle requests for which no answers were found and
1622 * answers for which no requests were found in the tap listener.
1623 * In case if you don't need unpaired requests/answers use:
1624 * if (diameter_pair->processing_request || !diameter_pair->req_frame)
1625 * return;
1627 tap_queue_packet(diameter_tap, pinfo, diameter_pair);
1629 return tvb_reported_length(tvb);
1632 static unsigned
1633 get_diameter_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb,
1634 int offset, void *data _U_)
1636 /* Get the length of the Diameter packet. */
1637 return tvb_get_ntoh24(tvb, offset + 1);
1640 #define NOT_DIAMETER 0
1641 #define IS_DIAMETER 1
1642 #define NOT_ENOUGH_DATA 2
1643 static int
1644 check_diameter(tvbuff_t *tvb)
1646 uint8_t flags;
1647 uint32_t msg_len;
1649 /* Ensure we don't throw an exception trying to do these heuristics */
1650 if (tvb_captured_length(tvb) < 5)
1651 return NOT_ENOUGH_DATA;
1653 /* Check if the Diameter version is 1 */
1654 if (tvb_get_uint8(tvb, 0) != 1)
1655 return NOT_DIAMETER;
1657 /* Diameter minimum message length:
1659 * Version+Length - 4 bytes
1660 * Flags+CC - 4 bytes
1661 * AppID - 4 bytes
1662 * HbH - 4 bytes
1663 * E2E - 4 bytes
1664 * 2 AVPs (Orig-Host, Orig-Realm), each including:
1665 * * AVP code - 4 bytes
1666 * * AVP flags + length - 4 bytes
1667 * * (no data - what would a reasonable minimum be?)
1669 * --> 36 bytes
1671 msg_len = tvb_get_ntoh24(tvb, 1);
1672 /* Diameter message length field must be a multiple of 4.
1673 * This is implicit in RFC 3588 (based on the header and that each
1674 * AVP must align on a 32-bit boundary) and explicit in RFC 6733.
1676 if ((msg_len < 36) || (msg_len & 0x3))
1677 return NOT_DIAMETER;
1679 flags = tvb_get_uint8(tvb, 4);
1681 /* Check if any of the Reserved flag bits are set */
1682 if (flags & 0x0f)
1683 return NOT_DIAMETER;
1685 /* Check if both the R- and E-bits are set */
1686 if ((flags & DIAM_FLAGS_R) && (flags & DIAM_FLAGS_E))
1687 return NOT_DIAMETER;
1689 return IS_DIAMETER;
1692 /*****************************************************************/
1693 /* Main dissection function */
1694 /* Checks if the message looks like Diameter before accepting it */
1695 /*****************************************************************/
1696 static int
1697 dissect_diameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1699 if (check_diameter(tvb) != IS_DIAMETER)
1700 return 0;
1701 return dissect_diameter_common(tvb, pinfo, tree, data);
1704 static int
1705 dissect_diameter_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1707 int is_diam = check_diameter(tvb);
1709 if (is_diam == NOT_DIAMETER) {
1710 /* We've probably been given a frame that's not the start of
1711 * a PDU.
1713 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DIAMETER");
1714 col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
1715 call_dissector(data_handle, tvb, pinfo, tree);
1716 } else if (is_diam == NOT_ENOUGH_DATA) {
1717 /* Since we're doing our heuristic checks before
1718 * tcp_dissect_pdus() (since we can't do heuristics once
1719 * we're in there) we sometimes have to ask for more data...
1721 pinfo->desegment_offset = 0;
1722 pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
1723 } else {
1724 tcp_dissect_pdus(tvb, pinfo, tree, gbl_diameter_desegment, 4,
1725 get_diameter_pdu_len, dissect_diameter_common, data);
1728 return tvb_reported_length(tvb);
1731 static bool
1732 dissect_diameter_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1734 if (check_diameter(tvb) != IS_DIAMETER) {
1735 return false;
1738 conversation_set_dissector(find_or_create_conversation(pinfo), diameter_tcp_handle);
1740 tcp_dissect_pdus(tvb, pinfo, tree, gbl_diameter_desegment, 4,
1741 get_diameter_pdu_len, dissect_diameter_common, data);
1743 return true;
1746 static int
1747 dissect_diameter_avps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
1749 proto_item *pi;
1750 proto_tree *diam_tree;
1751 int offset = 0;
1752 diam_ctx_t *c = wmem_new0(pinfo->pool, diam_ctx_t);
1753 diam_sub_dis_t *diam_sub_dis_inf = wmem_new0(pinfo->pool, diam_sub_dis_t);
1755 /* Load header fields if not already done */
1756 if (hf_diameter_code <= 0)
1757 proto_registrar_get_byname("diameter.code");
1759 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DIAMETER");
1760 col_set_str(pinfo->cinfo, COL_INFO, "AVPs:");
1762 pi = proto_tree_add_item(tree, proto_diameter, tvb, 0, -1, ENC_NA);
1763 diam_tree = proto_item_add_subtree(pi, ett_diameter);
1764 c->tree = diam_tree;
1765 c->pinfo = pinfo;
1767 /* Dissect AVPs until the end of the packet is reached */
1768 while (tvb_reported_length_remaining(tvb, offset)) {
1769 offset += dissect_diameter_avp(c, tvb, offset, diam_sub_dis_inf, true);
1771 return tvb_reported_length(tvb);
1774 static char *
1775 alnumerize(char *name)
1777 char *r = name;
1778 char *w = name;
1779 char c;
1781 for (;(c = *r); r++) {
1782 if (g_ascii_isalnum(c) || c == '_' || c == '-' || c == '.') {
1783 *(w++) = c;
1787 *w = '\0';
1789 return name;
1793 static unsigned
1794 reginfo(int *hf_ptr, const char *name, const char *abbr, const char *desc,
1795 enum ftenum ft, field_display_e base, value_string_ext *vs_ext,
1796 uint32_t mask)
1798 hf_register_info hf;
1800 hf.p_id = hf_ptr;
1801 hf.hfinfo.name = name;
1802 hf.hfinfo.abbrev = abbr;
1803 hf.hfinfo.type = ft;
1804 hf.hfinfo.display = base;
1805 hf.hfinfo.strings = NULL;
1806 hf.hfinfo.bitmask = mask;
1807 hf.hfinfo.blurb = desc;
1808 /* HFILL */
1809 HFILL_INIT(hf);
1811 if (vs_ext) {
1812 hf.hfinfo.strings = vs_ext;
1815 wmem_array_append_one(build_dict.hf,hf);
1816 return wmem_array_get_count(build_dict.hf);
1819 static void
1820 basic_avp_reginfo(diam_avp_t *a, const char *name, enum ftenum ft,
1821 field_display_e base, value_string_ext *vs_ext)
1823 hf_register_info hf;
1824 int *ettp = &(a->ett);
1826 hf.p_id = &(a->hf_value);
1827 hf.hfinfo.name = NULL;
1828 hf.hfinfo.abbrev = NULL;
1829 hf.hfinfo.type = ft;
1830 hf.hfinfo.display = base;
1831 hf.hfinfo.strings = NULL;
1832 hf.hfinfo.bitmask = 0x0;
1833 hf.hfinfo.blurb = a->vendor->code ?
1834 wmem_strdup_printf(wmem_epan_scope(), "vendor=%d code=%d", a->vendor->code, a->code)
1835 : wmem_strdup_printf(wmem_epan_scope(), "code=%d", a->code);
1836 /* HFILL */
1837 HFILL_INIT(hf);
1839 hf.hfinfo.name = wmem_strdup(wmem_epan_scope(), name);
1840 hf.hfinfo.abbrev = alnumerize(wmem_strconcat(wmem_epan_scope(), "diameter.", name, NULL));
1841 if (vs_ext) {
1842 hf.hfinfo.strings = vs_ext;
1845 wmem_array_append(build_dict.hf,&hf,1);
1846 g_ptr_array_add(build_dict.ett,ettp);
1849 static diam_avp_t *
1850 build_gen_address_avp(diam_avp_t *a, address_avp_t *t, const char *name)
1852 int *ettp = &(t->ett);
1854 a->ett = -1;
1855 a->hf_value = -1;
1856 a->type_data = t;
1858 t->ett = -1;
1859 t->hf_address_type = -1;
1860 t->hf_ipv4 = -1;
1861 t->hf_ipv6 = -1;
1862 t->hf_e164_str = -1;
1863 t->hf_other = -1;
1865 basic_avp_reginfo(a, name, FT_BYTES, BASE_NONE, NULL);
1867 reginfo(&(t->hf_address_type), wmem_strconcat(wmem_epan_scope(), name, " Address Family", NULL),
1868 alnumerize(wmem_strconcat(wmem_epan_scope(), "diameter.", name, ".addr_family", NULL)),
1869 NULL, FT_UINT16, (field_display_e)(BASE_DEC|BASE_EXT_STRING), &diameter_avp_data_addrfamily_vals_ext, 0);
1871 reginfo(&(t->hf_ipv4), wmem_strconcat(wmem_epan_scope(), name, " Address", NULL),
1872 alnumerize(wmem_strconcat(wmem_epan_scope(), "diameter.", name, ".IPv4", NULL)),
1873 NULL, FT_IPv4, BASE_NONE, NULL, 0);
1875 reginfo(&(t->hf_ipv6), wmem_strconcat(wmem_epan_scope(), name, " Address", NULL),
1876 alnumerize(wmem_strconcat(wmem_epan_scope(), "diameter.", name, ".IPv6", NULL)),
1877 NULL, FT_IPv6, BASE_NONE, NULL, 0);
1879 reginfo(&(t->hf_e164_str), wmem_strconcat(wmem_epan_scope(), name, " Address", NULL),
1880 alnumerize(wmem_strconcat(wmem_epan_scope(), "diameter.", name, ".E164", NULL)),
1881 NULL, FT_STRING, BASE_NONE, NULL, 0);
1883 reginfo(&(t->hf_other), wmem_strconcat(wmem_epan_scope(), name, " Address", NULL),
1884 alnumerize(wmem_strconcat(wmem_epan_scope(), "diameter.", name, ".Bytes", NULL)),
1885 NULL, FT_BYTES, BASE_NONE, NULL, 0);
1887 g_ptr_array_add(build_dict.ett,ettp);
1889 return a;
1893 * RFC 6733 says:
1894 * > AVP numbers 1 through 255 are reserved for reuse of RADIUS attributes,
1895 * > without setting the Vendor-Id field.
1897 * This clearly applies not to vendor dictionaries. However, some vendors seem to have
1898 * translated their RADIUS dictionaries to Diameter with that assumption in mind, while
1899 * others have not.
1901 * To make this work universally, the type `ipaddress` is assumed to be using the RADIUS
1902 * encoding for AVP < 256 and Diameter for AVPs >= 256, while the `address` type will
1903 * use Diameter encoding for all AVPs
1905 static diam_avp_t *
1906 build_ipaddress_avp(const avp_type_t *type _U_, uint32_t code,
1907 diam_vnd_t *vendor, const char *name,
1908 const value_string *vs _U_, void *data _U_)
1910 diam_avp_t *a = wmem_new0(wmem_epan_scope(), diam_avp_t);
1911 address_avp_t *t = wmem_new(wmem_epan_scope(), address_avp_t);
1913 a->code = code;
1914 a->vendor = vendor;
1916 * It seems like the radius AVPs 1-255 will use the defs from RADIUS in which case:
1917 * https://tools.ietf.org/html/rfc2685
1918 * Address
1919 * The Address field is four octets. The value 0xFFFFFFFF indicates
1920 * that the NAS Should allow the user to select an address (e.g.
1921 * Negotiated). The value 0xFFFFFFFE indicates that the NAS should
1922 * select an address for the user (e.g. Assigned from a pool of
1923 * addresses kept by the NAS). Other valid values indicate that the
1924 * NAS should use that value as the user's IP address.
1926 * Where as in Diameter:
1927 * RFC3588
1928 * Address
1929 * The Address format is derived from the OctetString AVP Base
1930 * Format. It is a discriminated union, representing, for example a
1931 * 32-bit (IPv4) [IPV4] or 128-bit (IPv6) [IPV6] address, most
1932 * significant octet first. The first two octets of the Address
1933 * AVP represents the AddressType, which contains an Address Family
1934 * defined in [IANAADFAM]. The AddressType is used to discriminate
1935 * the content and format of the remaining octets.
1937 if (code<256) {
1938 a->dissector_rfc = address_radius_avp;
1939 } else {
1940 a->dissector_rfc = address_rfc_avp;
1942 return build_gen_address_avp(a, t, name);
1945 static diam_avp_t *
1946 build_address_avp(const avp_type_t *type _U_, uint32_t code,
1947 diam_vnd_t *vendor, const char *name,
1948 const value_string *vs _U_, void *data _U_)
1950 diam_avp_t *a = wmem_new0(wmem_epan_scope(), diam_avp_t);
1951 address_avp_t *t = wmem_new(wmem_epan_scope(), address_avp_t);
1953 a->code = code;
1954 a->vendor = vendor;
1955 a->dissector_rfc = address_rfc_avp;
1957 return build_gen_address_avp(a, t, name);
1960 static diam_avp_t *
1961 build_proto_avp(const avp_type_t *type _U_, uint32_t code,
1962 diam_vnd_t *vendor, const char *name _U_,
1963 const value_string *vs _U_, void *data)
1965 diam_avp_t *a = wmem_new0(wmem_epan_scope(), diam_avp_t);
1966 proto_avp_t *t = wmem_new0(wmem_epan_scope(), proto_avp_t);
1967 int *ettp = &(a->ett);
1969 a->code = code;
1970 a->vendor = vendor;
1971 a->dissector_rfc = proto_avp;
1972 a->ett = -1;
1973 a->hf_value = -2;
1974 a->type_data = t;
1976 t->name = (char *)data;
1977 t->handle = NULL;
1978 t->reassemble_mode = REASEMBLE_NEVER;
1980 g_ptr_array_add(build_dict.ett,ettp);
1982 return a;
1985 static diam_avp_t *
1986 build_simple_avp(const avp_type_t *type, uint32_t code, diam_vnd_t *vendor,
1987 const char *name, const value_string *vs, void *data _U_)
1989 diam_avp_t *a;
1990 value_string_ext *vs_ext = NULL;
1991 field_display_e base;
1992 unsigned i = 0;
1995 * Only 32-bit or shorter integral types can have a list of values.
1997 base = (field_display_e)type->base;
1998 if (vs != NULL) {
1999 switch (type->ft) {
2001 case FT_UINT8:
2002 case FT_UINT16:
2003 case FT_UINT32:
2004 case FT_INT8:
2005 case FT_INT16:
2006 case FT_INT32:
2007 break;
2009 default:
2010 report_failure("Diameter Dictionary: AVP '%s' has a list of values but isn't of a 32-bit or shorter integral type (%s)\n",
2011 name, ftype_name(type->ft));
2012 return NULL;
2014 while (vs[i].strptr) {
2015 i++;
2017 vs_ext = value_string_ext_new(vs, i+1, wmem_strconcat(wmem_epan_scope(), name, "_vals_ext", NULL));
2018 base = (field_display_e)(base|BASE_EXT_STRING);
2021 a = wmem_new0(wmem_epan_scope(), diam_avp_t);
2022 a->code = code;
2023 a->vendor = vendor;
2024 a->dissector_rfc = type->rfc;
2025 a->ett = -1;
2026 a->hf_value = -1;
2028 basic_avp_reginfo(a, name, type->ft, base, vs_ext);
2030 return a;
2033 static diam_avp_t *
2034 build_appid_avp(const avp_type_t *type, uint32_t code, diam_vnd_t *vendor,
2035 const char *name, const value_string *vs, void *data _U_)
2037 diam_avp_t *a;
2038 field_display_e base;
2040 a = wmem_new0(wmem_epan_scope(), diam_avp_t);
2041 a->code = code;
2042 a->vendor = vendor;
2043 a->dissector_rfc = type->rfc;
2044 a->ett = -1;
2045 a->hf_value = -1;
2047 if (vs != NULL) {
2048 report_failure("Diameter Dictionary: AVP '%s' (of type AppId) has a list of values but the list won't be used\n",
2049 name);
2052 base = (field_display_e)(type->base|BASE_EXT_STRING);
2054 basic_avp_reginfo(a, name, type->ft, base, dictionary.applications);
2055 return a;
2058 static const avp_type_t basic_types[] = {
2059 {"octetstring" , simple_avp , FT_BYTES , BASE_NONE , build_simple_avp },
2060 {"octetstringorutf8" , simple_avp , FT_BYTES , BASE_SHOW_ASCII_PRINTABLE , build_simple_avp },
2061 {"utf8string" , utf8_avp , FT_STRING , BASE_NONE , build_simple_avp },
2062 {"grouped" , grouped_avp , FT_BYTES , BASE_NONE , build_simple_avp },
2063 {"integer32" , integer32_avp , FT_INT32 , BASE_DEC , build_simple_avp },
2064 {"unsigned32" , unsigned32_avp , FT_UINT32 , BASE_DEC , build_simple_avp },
2065 {"integer64" , integer64_avp , FT_INT64 , BASE_DEC , build_simple_avp },
2066 {"unsigned64" , unsigned64_avp , FT_UINT64 , BASE_DEC , build_simple_avp },
2067 {"float32" , float32_avp , FT_FLOAT , BASE_NONE , build_simple_avp },
2068 {"float64" , float64_avp , FT_DOUBLE , BASE_NONE , build_simple_avp },
2069 {"ipaddress" , NULL , FT_NONE , BASE_NONE , build_ipaddress_avp },
2070 {"address" , NULL , FT_NONE , BASE_NONE , build_address_avp },
2071 {"diameteruri" , utf8_avp , FT_STRING , BASE_NONE , build_simple_avp },
2072 {"diameteridentity" , utf8_avp , FT_STRING , BASE_NONE , build_simple_avp },
2073 {"ipfilterrule" , utf8_avp , FT_STRING , BASE_NONE , build_simple_avp },
2074 {"qosfilterrule" , utf8_avp , FT_STRING , BASE_NONE , build_simple_avp },
2075 {"time" , time_avp , FT_ABSOLUTE_TIME , ABSOLUTE_TIME_UTC , build_simple_avp },
2076 {"AppId" , simple_avp , FT_UINT32 , BASE_DEC , build_appid_avp },
2077 {NULL, NULL, FT_NONE, BASE_NONE, NULL }
2083 * This is like g_str_hash() (as of GLib 2.4.8), but it maps all
2084 * upper-case ASCII characters to their ASCII lower-case equivalents.
2085 * We can't use g_strdown(), as that doesn't do an ASCII mapping;
2086 * in Turkish locales, for example, there are two lower-case "i"s
2087 * and two upper-case "I"s, with and without dots - the ones with
2088 * dots map between each other, as do the ones without dots, so "I"
2089 * doesn't map to "i".
2091 static unsigned
2092 strcase_hash(const void *key)
2094 const char *p = (const char *)key;
2095 unsigned h = *p;
2096 char c;
2098 if (h) {
2099 if (h >= 'A' && h <= 'Z')
2100 h = h - 'A' + 'a';
2101 for (p += 1; *p != '\0'; p++) {
2102 c = *p;
2103 if (c >= 'A' && c <= 'Z')
2104 c = c - 'A' + 'a';
2105 h = (h << 5) - h + c;
2109 return h;
2113 * Again, use g_ascii_strcasecmp(), not strcasecmp(), so that only ASCII
2114 * letters are mapped, and they're mapped to the lower-case ASCII
2115 * equivalents.
2117 static gboolean
2118 strcase_equal(const void *ka, const void *kb)
2120 const char *a = (const char *)ka;
2121 const char *b = (const char *)kb;
2122 return g_ascii_strcasecmp(a,b) == 0;
2125 static bool
2126 ddict_cleanup_cb(wmem_allocator_t* allocator _U_, wmem_cb_event_t event _U_, void *user_data)
2128 ddict_t *d = (ddict_t *)user_data;
2129 ddict_free(d);
2130 return false;
2134 /* Note: Dynamic "value string arrays" (e.g., vs_avps, ...) are constructed using */
2135 /* "zero-terminated" GArrays so that they will have the same form as standard */
2136 /* value_string arrays created at compile time. Since the last entry in a */
2137 /* value_string array must be {0, NULL}, we are assuming that NULL == 0 (hackish). */
2139 static int
2140 dictionary_load(void)
2142 ddict_t *d;
2143 ddict_application_t *p;
2144 ddict_vendor_t *v;
2145 ddict_cmd_t *c;
2146 ddict_typedefn_t *t;
2147 ddict_avp_t *a;
2148 bool do_debug_parser = getenv("WIRESHARK_DEBUG_DIAM_DICT_PARSER") ? true : false;
2149 bool do_dump_dict = getenv("WIRESHARK_DUMP_DIAM_DICT") ? true : false;
2150 char *dir;
2151 const avp_type_t *type;
2152 const avp_type_t *octetstring = &basic_types[0];
2153 diam_avp_t *avp;
2154 GHashTable *vendors = g_hash_table_new(strcase_hash,strcase_equal);
2155 diam_vnd_t *vnd;
2156 GArray *vnd_shrt_arr = g_array_new(true,true,sizeof(value_string));
2157 GArray *all_cmds = g_array_new(true,true,sizeof(value_string));
2159 /* Pre allocate the arrays big enough to hold the hf:s and etts:s*/
2160 build_dict.hf = wmem_array_sized_new(wmem_epan_scope(), sizeof(hf_register_info), 4096);
2161 build_dict.ett = g_ptr_array_sized_new(4096);
2162 build_dict.types = g_hash_table_new(strcase_hash,strcase_equal);
2163 build_dict.avps = g_hash_table_new(strcase_hash,strcase_equal);
2165 dictionary.vnds = wmem_tree_new(wmem_epan_scope());
2166 dictionary.avps = wmem_tree_new(wmem_epan_scope());
2168 unknown_vendor.vs_avps = wmem_array_new(wmem_epan_scope(), sizeof(value_string));
2169 wmem_array_set_null_terminator(unknown_vendor.vs_avps);
2170 wmem_array_bzero(unknown_vendor.vs_avps);
2171 no_vnd.vs_avps = wmem_array_new(wmem_epan_scope(), sizeof(value_string));
2172 wmem_array_set_null_terminator(no_vnd.vs_avps);
2173 wmem_array_bzero(no_vnd.vs_avps);
2175 wmem_tree_insert32(dictionary.vnds,0,&no_vnd);
2176 g_hash_table_insert(vendors,"None",&no_vnd);
2178 /* initialize the types hash with the known basic types */
2179 for (type = basic_types; type->name; type++) {
2180 g_hash_table_insert(build_dict.types,(char *)type->name,(void *)type);
2183 /* load the dictionary */
2184 dir = wmem_strdup_printf(NULL, "%s" G_DIR_SEPARATOR_S "diameter" G_DIR_SEPARATOR_S, get_datafile_dir());
2185 d = ddict_scan(dir,"dictionary.xml",do_debug_parser);
2186 wmem_free(NULL, dir);
2187 if (d == NULL) {
2188 g_hash_table_destroy(vendors);
2189 g_array_free(vnd_shrt_arr, true);
2190 return 0;
2192 wmem_register_callback(wmem_epan_scope(), ddict_cleanup_cb, d);
2194 if (do_dump_dict) ddict_print(stdout, d);
2196 /* populate the types */
2197 for (t = d->typedefns; t; t = t->next) {
2198 const avp_type_t *parent = NULL;
2199 /* try to get the parent type */
2201 if (t->name == NULL) {
2202 report_failure("Diameter Dictionary: Invalid Type (empty name): parent==%s\n",
2203 t->parent ? t->parent : "(null)");
2204 continue;
2208 if (g_hash_table_lookup(build_dict.types,t->name))
2209 continue;
2211 if (t->parent) {
2212 parent = (avp_type_t *)g_hash_table_lookup(build_dict.types,t->parent);
2215 if (!parent) parent = octetstring;
2217 /* insert the parent type for this type */
2218 g_hash_table_insert(build_dict.types,t->name,(void *)parent);
2221 /* populate the applications */
2222 if ((p = d->applications)) {
2223 wmem_array_t *arr = wmem_array_new(wmem_epan_scope(), sizeof(value_string));
2224 value_string term[1];
2226 term[0].value = 0;
2227 term[0].strptr = NULL;
2229 for (; p; p = p->next) {
2230 value_string item[1];
2232 item[0].value = p->code;
2233 item[0].strptr = p->name;
2234 if (!p->name) {
2235 report_failure("Diameter Dictionary: Invalid Application (empty name): id=%d\n", p->code);
2236 continue;
2239 wmem_array_append_one(arr,item);
2242 wmem_array_sort(arr, compare_avps);
2243 wmem_array_append_one(arr,term);
2245 /* TODO: Remove duplicates */
2247 dictionary.applications = value_string_ext_new((value_string *)wmem_array_get_raw(arr),
2248 wmem_array_get_count(arr),
2249 wmem_strdup(wmem_epan_scope(), "applications_vals_ext"));
2252 if ((v = d->vendors)) {
2253 for ( ; v; v = v->next) {
2254 value_string item[1];
2256 item[0].value = v->code;
2257 item[0].strptr = v->name;
2259 if (v->name == NULL) {
2260 report_failure("Diameter Dictionary: Invalid Vendor (empty name): code==%d\n", v->code);
2261 continue;
2264 if (g_hash_table_lookup(vendors,v->name))
2265 continue;
2267 g_array_append_val(vnd_shrt_arr,item);
2269 vnd = wmem_new(wmem_epan_scope(), diam_vnd_t);
2270 vnd->code = v->code;
2271 vnd->vs_avps = wmem_array_new(wmem_epan_scope(), sizeof(value_string));
2272 wmem_array_set_null_terminator(vnd->vs_avps);
2273 wmem_array_bzero(vnd->vs_avps);
2274 vnd->vs_avps_ext = NULL;
2275 wmem_tree_insert32(dictionary.vnds,vnd->code,vnd);
2276 g_hash_table_insert(vendors,v->name,vnd);
2280 vnd_short_vs = (value_string *)g_array_free(vnd_shrt_arr, false);
2282 if ((c = d->cmds)) {
2283 for (; c; c = c->next) {
2284 if (c->vendor == NULL) {
2285 report_failure("Diameter Dictionary: Invalid Vendor (empty name) for command %s\n",
2286 c->name ? c->name : "(null)");
2287 continue;
2290 if ((diam_vnd_t *)g_hash_table_lookup(vendors,c->vendor)) {
2291 value_string item[1];
2293 item[0].value = c->code;
2294 item[0].strptr = c->name;
2296 g_array_append_val(all_cmds,item);
2297 } else {
2298 report_failure("Diameter Dictionary: No Vendor: %s\n",c->vendor);
2304 for (a = d->avps; a; a = a->next) {
2305 ddict_enum_t *e;
2306 value_string *vs = NULL;
2307 const char *vend = a->vendor ? a->vendor : "None";
2308 ddict_xmlpi_t *x;
2309 void *avp_data = NULL;
2311 if (a->name == NULL) {
2312 report_failure("Diameter Dictionary: Invalid AVP (empty name)\n");
2313 continue;
2316 if ((vnd = (diam_vnd_t *)g_hash_table_lookup(vendors,vend))) {
2317 value_string vndvs[1];
2319 vndvs[0].value = a->code;
2320 vndvs[0].strptr = a->name;
2323 wmem_array_append_one(vnd->vs_avps,vndvs);
2324 } else {
2325 report_failure("Diameter Dictionary: No Vendor: %s\n",vend);
2326 vnd = &unknown_vendor;
2329 if ((e = a->enums)) {
2330 wmem_array_t *arr = wmem_array_new(wmem_epan_scope(), sizeof(value_string));
2331 value_string term[1];
2333 term[0].value = 0;
2334 term[0].strptr = NULL;
2336 for (; e; e = e->next) {
2337 value_string item[1];
2339 item[0].value = e->code;
2340 item[0].strptr = e->name;
2341 wmem_array_append_one(arr,item);
2343 wmem_array_sort(arr, compare_avps);
2344 wmem_array_append_one(arr,term);
2345 vs = (value_string *)wmem_array_get_raw(arr);
2348 type = NULL;
2350 for( x = d->xmlpis; x; x = x->next ) {
2351 if ( (strcase_equal(x->name,"avp-proto") && strcase_equal(x->key,a->name))
2352 || (a->type && strcase_equal(x->name,"type-proto") && strcase_equal(x->key,a->type))
2354 static avp_type_t proto_type = {"proto", proto_avp, FT_UINT32, BASE_HEX, build_proto_avp};
2355 type = &proto_type;
2357 avp_data = x->value;
2358 break;
2362 if ( (!type) && a->type )
2363 type = (avp_type_t *)g_hash_table_lookup(build_dict.types,a->type);
2365 if (!type) type = octetstring;
2367 avp = type->build( type, a->code, vnd, a->name, vs, avp_data);
2368 if (avp != NULL) {
2369 g_hash_table_insert(build_dict.avps, a->name, avp);
2372 wmem_tree_key_t k[3];
2374 k[0].length = 1;
2375 k[0].key = &(a->code);
2376 k[1].length = 1;
2377 k[1].key = &(vnd->code);
2378 k[2].length = 0;
2379 k[2].key = NULL;
2381 wmem_tree_insert32_array(dictionary.avps,k,avp);
2385 g_hash_table_destroy(build_dict.types);
2386 g_hash_table_destroy(build_dict.avps);
2387 g_hash_table_destroy(vendors);
2389 cmd_vs = (const value_string *)g_array_free(all_cmds, false);
2391 return 1;
2395 * This does most of the registration work; see register_diameter_fields()
2396 * for the reason why we split it off.
2398 static void
2399 real_register_diameter_fields(void)
2401 expert_module_t* expert_diameter;
2402 unsigned i, ett_length;
2404 hf_register_info hf_base[] = {
2405 { &hf_diameter_version,
2406 { "Version", "diameter.version", FT_UINT8, BASE_HEX, NULL, 0x00,
2407 NULL, HFILL }},
2408 { &hf_diameter_length,
2409 { "Length","diameter.length", FT_UINT24, BASE_DEC, NULL, 0x0,
2410 NULL, HFILL }},
2411 { &hf_diameter_flags,
2412 { "Flags", "diameter.flags", FT_UINT8, BASE_HEX, NULL, 0x0,
2413 NULL, HFILL }},
2414 { &hf_diameter_flags_request,
2415 { "Request", "diameter.flags.request", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_R,
2416 NULL, HFILL }},
2417 { &hf_diameter_flags_proxyable,
2418 { "Proxyable", "diameter.flags.proxyable", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_P,
2419 NULL, HFILL }},
2420 { &hf_diameter_flags_error,
2421 { "Error","diameter.flags.error", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_E,
2422 NULL, HFILL }},
2423 { &hf_diameter_flags_T,
2424 { "T(Potentially re-transmitted message)","diameter.flags.T", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DIAM_FLAGS_T,
2425 NULL, HFILL }},
2426 { &hf_diameter_flags_reserved4,
2427 { "Reserved","diameter.flags.reserved4", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
2428 DIAM_FLAGS_RESERVED4, NULL, HFILL }},
2429 { &hf_diameter_flags_reserved5,
2430 { "Reserved","diameter.flags.reserved5", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
2431 DIAM_FLAGS_RESERVED5, NULL, HFILL }},
2432 { &hf_diameter_flags_reserved6,
2433 { "Reserved","diameter.flags.reserved6", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
2434 DIAM_FLAGS_RESERVED6, NULL, HFILL }},
2435 { &hf_diameter_flags_reserved7,
2436 { "Reserved","diameter.flags.reserved7", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
2437 DIAM_FLAGS_RESERVED7, NULL, HFILL }},
2438 { &hf_diameter_vendor_id,
2439 { "VendorId", "diameter.vendorId", FT_UINT32, BASE_ENTERPRISES, STRINGS_ENTERPRISES,
2440 0x0, NULL, HFILL }},
2441 { &hf_diameter_application_id,
2442 { "ApplicationId", "diameter.applicationId", FT_UINT32, BASE_DEC|BASE_EXT_STRING, VALS_EXT_PTR(dictionary.applications),
2443 0x0, NULL, HFILL }},
2444 { &hf_diameter_hopbyhopid,
2445 { "Hop-by-Hop Identifier", "diameter.hopbyhopid", FT_UINT32,
2446 BASE_HEX, NULL, 0x0, NULL, HFILL }},
2447 { &hf_diameter_endtoendid,
2448 { "End-to-End Identifier", "diameter.endtoendid", FT_UINT32,
2449 BASE_HEX, NULL, 0x0, NULL, HFILL }},
2450 { &hf_diameter_avp,
2451 { "AVP","diameter.avp", FT_BYTES, BASE_NONE,
2452 NULL, 0x0, NULL, HFILL }},
2453 { &hf_diameter_avp_len,
2454 { "AVP Length","diameter.avp.len", FT_UINT24, BASE_DEC,
2455 NULL, 0x0, NULL, HFILL }},
2456 { &hf_diameter_avp_code,
2457 { "AVP Code", "diameter.avp.code", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
2458 { &hf_diameter_avp_flags,
2459 { "AVP Flags","diameter.avp.flags", FT_UINT8, BASE_HEX,
2460 NULL, 0x0, NULL, HFILL }},
2461 { &hf_diameter_avp_flags_vendor_specific,
2462 { "Vendor-Specific", "diameter.flags.vendorspecific", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AVP_FLAGS_V,
2463 NULL, HFILL }},
2464 { &hf_diameter_avp_flags_mandatory,
2465 { "Mandatory", "diameter.flags.mandatory", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AVP_FLAGS_M,
2466 NULL, HFILL }},
2467 { &hf_diameter_avp_flags_protected,
2468 { "Protected","diameter.avp.flags.protected", FT_BOOLEAN, 8, TFS(&tfs_set_notset), AVP_FLAGS_P,
2469 NULL, HFILL }},
2470 { &hf_diameter_avp_flags_reserved3,
2471 { "Reserved","diameter.avp.flags.reserved3", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
2472 AVP_FLAGS_RESERVED3, NULL, HFILL }},
2473 { &hf_diameter_avp_flags_reserved4,
2474 { "Reserved","diameter.avp.flags.reserved4", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
2475 AVP_FLAGS_RESERVED4, NULL, HFILL }},
2476 { &hf_diameter_avp_flags_reserved5,
2477 { "Reserved","diameter.avp.flags.reserved5", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
2478 AVP_FLAGS_RESERVED5, NULL, HFILL }},
2479 { &hf_diameter_avp_flags_reserved6,
2480 { "Reserved","diameter.avp.flags.reserved6", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
2481 AVP_FLAGS_RESERVED6, NULL, HFILL }},
2482 { &hf_diameter_avp_flags_reserved7,
2483 { "Reserved","diameter.avp.flags.reserved7", FT_BOOLEAN, 8, TFS(&tfs_set_notset),
2484 AVP_FLAGS_RESERVED7, NULL, HFILL }},
2485 { &hf_diameter_avp_vendor_id,
2486 { "AVP Vendor Id","diameter.avp.vendorId", FT_UINT32, BASE_ENTERPRISES, STRINGS_ENTERPRISES,
2487 0x0, NULL, HFILL }},
2488 { &(unknown_avp.hf_value),
2489 { "Value","diameter.avp.unknown", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
2490 { &hf_diameter_avp_data_wrong_length,
2491 { "Data","diameter.avp.invalid-data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
2492 { &hf_diameter_avp_pad,
2493 { "Padding","diameter.avp.pad", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
2494 { &hf_diameter_code,
2495 { "Command Code", "diameter.cmd.code", FT_UINT32, BASE_DEC, VALS(cmd_vs), 0, NULL, HFILL }},
2496 { &hf_diameter_answer_in,
2497 { "Answer In", "diameter.answer_in", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
2498 "The answer to this diameter request is in this frame", HFILL }},
2499 { &hf_diameter_answer_to,
2500 { "Request In", "diameter.answer_to", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
2501 "This is an answer to the diameter request in this frame", HFILL }},
2502 { &hf_diameter_answer_time,
2503 { "Response Time", "diameter.resp_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
2504 "The time between the request and the answer", HFILL }},
2505 { &hf_framed_ipv6_prefix_reserved,
2506 { "Framed IPv6 Prefix Reserved byte", "diameter.framed_ipv6_prefix_reserved",
2507 FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
2508 { &hf_framed_ipv6_prefix_length,
2509 { "Framed IPv6 Prefix length (in bits)", "diameter.framed_ipv6_prefix_length",
2510 FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
2511 { &hf_framed_ipv6_prefix_bytes,
2512 { "Framed IPv6 Prefix as a bytestring", "diameter.framed_ipv6_prefix_bytes",
2513 FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
2514 { &hf_framed_ipv6_prefix_ipv6,
2515 { "Framed IPv6 Prefix as an IPv6 address", "diameter.framed_ipv6_prefix_ipv6",
2516 FT_IPv6, BASE_NONE, NULL, 0, "This field is present only if the prefix length is 128", HFILL }},
2517 { &hf_diameter_3gpp2_exp_res,
2518 { "Experimental-Result-Code", "diameter.3gpp2.exp_res",
2519 FT_UINT32, BASE_DEC, VALS(diameter_3gpp2_exp_res_vals), 0x0, NULL, HFILL }},
2520 { &hf_diameter_other_vendor_exp_res,
2521 { "Experimental-Result-Code", "diameter.other_vendor.Experimental-Result-Code",
2522 FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
2523 { &hf_diameter_mip6_feature_vector,
2524 { "MIP6-Feature-Vector", "diameter.mip6_feature_vector",
2525 FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }},
2526 { &hf_diameter_mip6_feature_vector_mip6_integrated,
2527 { "MIP6_INTEGRATED", "diameter.mip6_feature_vector.mip6_integrated.mip6_integrated",
2528 FT_BOOLEAN, 64, TFS(&tfs_set_notset), 0x0000000000000001, NULL, HFILL }},
2529 { &hf_diameter_mip6_feature_vector_local_home_agent_assignment,
2530 { "LOCAL_HOME_AGENT_ASSIGNMENT", "diameter.mip6_feature_vector.local_home_agent_assignment",
2531 FT_BOOLEAN, 64, TFS(&tfs_set_notset), 0x0000000000000002, NULL, HFILL }},
2532 { &hf_diameter_mip6_feature_vector_pmip6_supported,
2533 { "PMIP6_SUPPORTED", "diameter.mip6_feature_vector.pmip6_supported",
2534 FT_BOOLEAN, 64, TFS(&tfs_set_notset), 0x0000010000000000, NULL, HFILL }},
2535 { &hf_diameter_mip6_feature_vector_ip4_hoa_supported,
2536 { "IP4_HOA_SUPPORTED", "diameter.mip6_feature_vector.ip4_hoa_supported",
2537 FT_BOOLEAN, 64, TFS(&tfs_set_notset), 0x0000020000000000, NULL, HFILL }},
2538 { &hf_diameter_mip6_feature_vector_local_mag_routing_supported,
2539 { "LOCAL_MAG_ROUTING_SUPPORTED", "diameter.mip6_feature_vector.local_mag_routing_supported",
2540 FT_BOOLEAN, 64, TFS(&tfs_set_notset), 0x0000040000000000,NULL, HFILL }},
2541 { &hf_diameter_3gpp_mip6_feature_vector,
2542 { "MIP6-Feature-Vector [3GPP]", "diameter.3gpp.mip6_feature_vector",
2543 FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }},
2544 { &hf_diameter_3gpp_mip6_feature_vector_assign_local_ip,
2545 { "MIP6_INTEGRATED", "diameter.3gpp.mip6_feature_vector.assign_local_ip",
2546 FT_BOOLEAN, 64, TFS(&tfs_set_notset), 0x0000080000000000, NULL, HFILL }},
2547 { &hf_diameter_3gpp_mip6_feature_vector_mip4_supported,
2548 { "PMIP6_SUPPORTED", "diameter.3gpp.mip6_feature_vector.mip4_supported",
2549 FT_BOOLEAN, 64, TFS(&tfs_set_notset), 0x0000100000000000, NULL, HFILL }},
2550 { &hf_diameter_3gpp_mip6_feature_vector_optimized_idle_mode_mobility,
2551 { "OPTIMIZED_IDLE_MODE_MOBILITY", "diameter.3gpp.mip6_feature_vector.optimized_idle_mode_mobility",
2552 FT_BOOLEAN, 64, TFS(&tfs_set_notset), 0x0000200000000000, NULL, HFILL }},
2553 { &hf_diameter_3gpp_mip6_feature_vector_gtpv2_supported,
2554 { "GTPv2_SUPPORTED", "diameter.3gpp.mip6_feature_vector.gtpv2_supported",
2555 FT_BOOLEAN, 64, TFS(&tfs_set_notset), 0x0000400000000000, NULL, HFILL }},
2556 { &hf_diameter_user_equipment_info_imeisv,
2557 { "IMEISV","diameter.user_equipment_info.imeisv", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
2558 { &hf_diameter_user_equipment_info_mac,
2559 { "MAC","diameter.user_equipment_info.mac", FT_ETHER, BASE_NONE, NULL, 0x0, NULL, HFILL }},
2560 { &hf_diameter_user_equipment_info_eui64,
2561 { "EUI64","diameter.user_equipment_info.eui64", FT_EUI64, BASE_NONE, NULL, 0x0, NULL, HFILL }},
2562 { &hf_diameter_user_equipment_info_modified_eui64,
2563 { "Modified EUI64","diameter.user_equipment_info.modified_eui64", FT_EUI64, BASE_NONE, NULL, 0x0, NULL, HFILL }}
2566 int *ett_base[] = {
2567 &ett_diameter,
2568 &ett_diameter_flags,
2569 &ett_diameter_avp_flags,
2570 &ett_diameter_avpinfo,
2571 &ett_unknown,
2572 &ett_diameter_mip6_feature_vector,
2573 &ett_diameter_3gpp_mip6_feature_vector,
2574 &(unknown_avp.ett)
2577 static ei_register_info ei[] = {
2578 { &ei_diameter_reserved_bit_set, { "diameter.reserved_bit_set", PI_MALFORMED, PI_WARN, "Reserved bit set", EXPFILL }},
2579 { &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 }},
2580 { &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 }},
2581 { &ei_diameter_avp_no_data, { "diameter.avp.no_data", PI_UNDECODED, PI_WARN, "Data is empty", EXPFILL }},
2582 { &ei_diameter_avp_pad, { "diameter.avp.pad.non_zero", PI_MALFORMED, PI_NOTE, "Padding is non-zero", EXPFILL }},
2583 { &ei_diameter_avp_pad_missing, { "diameter.avp.pad.missing", PI_MALFORMED, PI_NOTE, "Padding is missing", EXPFILL }},
2584 { &ei_diameter_avp_len, { "diameter.avp.invalid-len", PI_MALFORMED, PI_WARN, "Wrong length", EXPFILL }},
2585 { &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 }},
2586 { &ei_diameter_version, { "diameter.version.unknown", PI_UNDECODED, PI_WARN, "Unknown Diameter Version (decoding as RFC 3588)", EXPFILL }},
2587 { &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 }},
2588 { &ei_diameter_invalid_ipv6_prefix_len, { "diameter.invalid_ipv6_prefix_len", PI_MALFORMED, PI_ERROR, "Invalid IPv6 Prefix length", EXPFILL }},
2589 { &ei_diameter_invalid_avp_len,{ "diameter.invalid_avp_len", PI_MALFORMED, PI_ERROR, "Invalid AVP length", EXPFILL }},
2590 { &ei_diameter_invalid_user_equipment_info_value_len,{ "diameter.invalid_user_equipment_info_value_len", PI_MALFORMED, PI_ERROR, "Invalid User-Equipment-Info-Value length", EXPFILL }},
2591 { &ei_diameter_unexpected_imei_as_user_equipment_info,{ "diameter.unexpected_imei_as_user_equipment_info", PI_MALFORMED, PI_ERROR, "Found IMEI as User-Equipment-Info-Value but IMEISV was expected", EXPFILL }},
2594 wmem_array_append(build_dict.hf, hf_base, array_length(hf_base));
2595 ett_length = array_length(ett_base);
2596 for (i = 0; i < ett_length; i++) {
2597 g_ptr_array_add(build_dict.ett, ett_base[i]);
2600 proto_register_field_array(proto_diameter, (hf_register_info *)wmem_array_get_raw(build_dict.hf), wmem_array_get_count(build_dict.hf));
2601 proto_register_subtree_array((int **)build_dict.ett->pdata, build_dict.ett->len);
2602 expert_diameter = expert_register_protocol(proto_diameter);
2603 expert_register_field_array(expert_diameter, ei, array_length(ei));
2605 g_ptr_array_free(build_dict.ett,true);
2609 static void
2610 register_diameter_fields(const char *unused _U_)
2613 * The hf_base[] array for Diameter refers to a variable
2614 * that is set by dictionary_load(), so we need to call
2615 * dictionary_load() before hf_base[] is initialized.
2617 * To ensure that, we call dictionary_load() and then
2618 * call a routine that defines hf_base[] and does all
2619 * the registration work.
2621 dictionary_load();
2622 real_register_diameter_fields();
2625 void
2626 proto_register_diameter(void)
2628 module_t *diameter_module;
2630 proto_diameter = proto_register_protocol ("Diameter Protocol", "Diameter", "diameter");
2632 /* Allow dissector to find be found by name. */
2633 diameter_sctp_handle = register_dissector("diameter", dissect_diameter, proto_diameter);
2634 diameter_udp_handle = create_dissector_handle(dissect_diameter, proto_diameter);
2635 diameter_tcp_handle = register_dissector("diameter.tcp", dissect_diameter_tcp, proto_diameter);
2636 /* Diameter AVPs without Diameter header, for EAP-TTLS (RFC 5281, Section 10) */
2637 register_dissector("diameter_avps", dissect_diameter_avps, proto_diameter);
2639 /* Delay registration of Diameter fields */
2640 proto_register_prefix("diameter", register_diameter_fields);
2642 /* Register dissector table(s) to do sub dissection of AVPs (OctetStrings) */
2643 diameter_dissector_table = register_dissector_table("diameter.base", "Diameter Base AVP", proto_diameter, FT_UINT32, BASE_DEC);
2644 diameter_3gpp_avp_dissector_table = register_dissector_table("diameter.3gpp", "Diameter 3GPP AVP", proto_diameter, FT_UINT32, BASE_DEC);
2645 diameter_ericsson_avp_dissector_table = register_dissector_table("diameter.ericsson", "Diameter Ericsson AVP", proto_diameter, FT_UINT32, BASE_DEC);
2646 diameter_verizon_avp_dissector_table = register_dissector_table("diameter.verizon", "DIAMETER_VERIZON_AVPS", proto_diameter, FT_UINT32, BASE_DEC);
2648 diameter_expr_result_vnd_table = register_dissector_table("diameter.vnd_exp_res", "Diameter Experimental-Result-Code", proto_diameter, FT_UINT32, BASE_DEC);
2650 /* Register configuration options */
2651 diameter_module = prefs_register_protocol(proto_diameter, NULL);
2652 /* For reading older preference files with "Diameter." preferences */
2653 prefs_register_module_alias("Diameter", diameter_module);
2655 /* Desegmentation */
2656 prefs_register_bool_preference(diameter_module, "desegment",
2657 "Reassemble Diameter messages spanning multiple TCP segments",
2658 "Whether the Diameter dissector should reassemble messages spanning multiple TCP segments."
2659 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
2660 &gbl_diameter_desegment);
2662 /* Register some preferences we no longer support, so we can report
2663 * them as obsolete rather than just illegal.
2665 prefs_register_obsolete_preference(diameter_module, "version");
2666 prefs_register_obsolete_preference(diameter_module, "command_in_header");
2667 prefs_register_obsolete_preference(diameter_module, "dictionary.name");
2668 prefs_register_obsolete_preference(diameter_module, "dictionary.use");
2669 prefs_register_obsolete_preference(diameter_module, "allow_zero_as_app_id");
2670 prefs_register_obsolete_preference(diameter_module, "suppress_console_output");
2672 /* Register tap */
2673 diameter_tap = register_tap("diameter");
2675 register_srt_table(proto_diameter, NULL, 1, diameterstat_packet, diameterstat_init, NULL);
2677 } /* proto_register_diameter */
2679 void
2680 proto_reg_handoff_diameter(void)
2682 data_handle = find_dissector("data");
2683 eap_handle = find_dissector_add_dependency("eap", proto_diameter);
2685 dissector_add_uint("sctp.ppi", DIAMETER_PROTOCOL_ID, diameter_sctp_handle);
2687 heur_dissector_add("tcp", dissect_diameter_tcp_heur, "Diameter over TCP", "diameter_tcp", proto_diameter, HEURISTIC_DISABLE);
2689 ssl_dissector_add(DEFAULT_DIAMETER_TLS_PORT, diameter_tcp_handle);
2690 dtls_dissector_add(DEFAULT_DIAMETER_TLS_PORT, diameter_sctp_handle);
2692 /* Register special decoding for some AVPs */
2694 /* AVP Code: 1 User-Name */
2695 dissector_add_uint("diameter.base", 1, create_dissector_handle(dissect_diameter_user_name, proto_diameter));
2697 /* AVP Code: 79 EAP-Message (defined in RFC 2869, but used for EAP-TTLS, RFC 5281) */
2698 dissector_add_uint("diameter.base", 79, create_dissector_handle(dissect_diameter_eap_payload, proto_diameter));
2700 /* AVP Code: 97 Framed-IPv6-Address */
2701 dissector_add_uint("diameter.base", 97, create_dissector_handle(dissect_diameter_base_framed_ipv6_prefix, proto_diameter));
2703 /* AVP Code: 124 MIP6-Feature-Vector */
2704 dissector_add_uint("diameter.base", 124, create_dissector_handle(dissect_diameter_mip6_feature_vector, proto_diameter));
2706 /* AVP Code: 265 Supported-Vendor-Id */
2707 dissector_add_uint("diameter.base", 265, create_dissector_handle(dissect_diameter_vendor_id, proto_diameter));
2709 /* AVP Code: 266 Vendor-Id */
2710 dissector_add_uint("diameter.base", 266, create_dissector_handle(dissect_diameter_vendor_id, proto_diameter));
2712 /* AVP Code: 443 Subscription-Id */
2713 dissector_add_uint("diameter.base", 443, create_dissector_handle(dissect_diameter_subscription_id, proto_diameter));
2715 /* AVP Code: 450 Subscription-Id-Type */
2716 dissector_add_uint("diameter.base", 450, create_dissector_handle(dissect_diameter_subscription_id_type, proto_diameter));
2718 /* AVP Code: 444 Subscription-Id-Data */
2719 dissector_add_uint("diameter.base", 444, create_dissector_handle(dissect_diameter_subscription_id_data, proto_diameter));
2721 /* AVP Code: 458 User-Equipment-Info */
2722 dissector_add_uint("diameter.base", 458, create_dissector_handle(dissect_diameter_user_equipment_info, proto_diameter));
2724 /* AVP Code: 459 User-Equipment-Info-Type */
2725 dissector_add_uint("diameter.base", 459, create_dissector_handle(dissect_diameter_user_equipment_info_type, proto_diameter));
2727 /* AVP Code: 460 User-Equipment-Info-Value */
2728 dissector_add_uint("diameter.base", 460, create_dissector_handle(dissect_diameter_user_equipment_info_value, proto_diameter));
2730 /* AVP Code: 462 EAP-Payload */
2731 dissector_add_uint("diameter.base", 462, create_dissector_handle(dissect_diameter_eap_payload, proto_diameter));
2732 /* AVP Code: 463 EAP-Reissued-Payload */
2733 dissector_add_uint("diameter.base", 463, create_dissector_handle(dissect_diameter_eap_payload, proto_diameter));
2735 /* Register dissector for Experimental result code, with 3GPP2's vendor Id */
2736 dissector_add_uint("diameter.vnd_exp_res", VENDOR_THE3GPP2, create_dissector_handle(dissect_diameter_3gpp2_exp_res, proto_diameter));
2738 dissector_add_uint_range_with_preference("tcp.port", DEFAULT_DIAMETER_PORT_RANGE, diameter_tcp_handle);
2739 dissector_add_uint_range_with_preference("udp.port", "", diameter_udp_handle);
2740 dissector_add_uint_range_with_preference("sctp.port", DEFAULT_DIAMETER_PORT_RANGE, diameter_sctp_handle);
2742 exported_pdu_tap = find_tap_id(EXPORT_PDU_TAP_NAME_LAYER_7);
2748 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2750 * Local variables:
2751 * c-basic-offset: 8
2752 * tab-width: 8
2753 * indent-tabs-mode: t
2754 * End:
2756 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2757 * :indentSize=8:tabSize=8:noTabs=false: