* http://www.ietf.org/rfc/rfc3320.txt?number=3320
* http://www.ietf.org/rfc/rfc3321.txt?number=3321
* Useful links :
- * http://www.ietf.org/internet-drafts/draft-ietf-rohc-sigcomp-impl-guide-02.txt
+ * http://www.ietf.org/internet-drafts/draft-ietf-rohc-sigcomp-impl-guide-03.txt
* http://www.ietf.org/internet-drafts/draft-ietf-rohc-sigcomp-sip-01.txt
*/
#endif
#include <epan/packet.h>
-#include <epan/prefs.h>
+#include "prefs.h"
+#include "sigcomp-udvm.h"
/* Initialize the protocol and registered fields */
static int proto_sigcomp = -1;
+static int proto_raw_sigcomp = -1;
static int hf_sigcomp_t_bit = -1;
static int hf_sigcomp_len = -1;
static int hf_sigcomp_returned_feedback_item = -1;
static int hf_udvm_literal_bytecode = -1;
static int hf_udvm_operand = -1;
static int hf_udvm_length = -1;
+static int hf_udvm_addr_length = -1;
static int hf_udvm_destination = -1;
+static int hf_udvm_addr_destination = -1;
static int hf_udvm_at_address = -1;
static int hf_udvm_address = -1;
static int hf_udvm_literal_num = -1;
static int hf_udvm_operand_2 = -1;
static int hf_udvm_operand_2_addr = -1;
static int hf_udvm_j = -1;
+static int hf_udvm_addr_j = -1;
static int hf_udvm_output_start = -1;
-static int hf_udvm_output_start_addr = -1;
+static int hf_udvm_addr_output_start = -1;
static int hf_udvm_output_length = -1;
static int hf_udvm_output_length_addr = -1;
static int hf_udvm_req_feedback_loc = -1;
static int hf_udvm_upper_bound = -1;
static int hf_udvm_uncompressed = -1;
static int hf_udvm_offset = -1;
+static int hf_udvm_addr_offset = -1;
static int hf_udvm_start_value = -1;
/* Initialize the subtree pointers */
-static gint ett_sigcomp = -1;
-static gint ett_sigcomp_udvm = -1;
-
+static gint ett_sigcomp = -1;
+static gint ett_sigcomp_udvm = -1;
+static gint ett_sigcomp_udvm_exe = -1;
+static gint ett_raw_text = -1;
+static dissector_handle_t sip_handle;
/* set the tcp port */
static guint SigCompUDPPort1 = 5555;
static guint SigCompUDPPort2 = 6666;
static gboolean display_udvm_bytecode = FALSE;
/* Default preference wether to dissect the UDVM code or not */
static gboolean dissect_udvm_code = TRUE;
+static gboolean display_raw_txt = FALSE;
+/* Default preference wether to print debug info at execution of UDVM */
+static gint udvm_print_detail_level = 0;
/* Value strings */
static const value_string length_encoding_vals[] = {
static int dissect_udvm_reference_operand(tvbuff_t *udvm_tvb, proto_tree *sigcomp_udvm_tree,
gint offset, gint *start_offset, guint16 *value);
+static void tvb_raw_text_add(tvbuff_t *tvb, proto_tree *tree);
/* Code to actually dissect the packets */
{
/* Set up structures needed to add the protocol subtree and manage it */
- tvbuff_t *udvm_tvb;
- proto_item *ti, *udvm_item;
- proto_tree *sigcomp_tree, *sigcomp_udvm_tree;
+ tvbuff_t *udvm_tvb, *msg_tvb;
+ tvbuff_t *decomp_tvb = NULL;
+ proto_item *ti, *udvm_bytecode_item, *udvm_exe_item;
+ proto_tree *sigcomp_tree, *sigcomp_udvm_tree, *sigcomp_udvm_exe_tree;
gint offset = 0;
+ gint bytecode_offset;
gint partial_state_len;
guint octet;
guint8 returned_feedback_field[128];
guint8 partial_state[12];
guint tbit;
guint16 len = 0;
+ guint16 bytecode_len = 0;
guint destination;
+ gint msg_len = 0;
+
/* Is this a SigComp message or not ? */
octet = tvb_get_guint8(tvb, offset);
if ((octet & 0xf8) != 0xf8)
return 0;
/* Make entries in Protocol column and Info column on summary display */
- if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SIGCOMP");
if (check_col(pinfo->cinfo, COL_INFO))
proto_tree_add_item(sigcomp_tree,hf_sigcomp_destination, tvb, (offset+ 1), 1, FALSE);
offset = offset +2;
- udvm_item = proto_tree_add_text(sigcomp_tree, tvb, offset, len,
- "Uploaded UDVM bytecode %u (0x%x) bytes", len, len);
- sigcomp_udvm_tree = proto_item_add_subtree( udvm_item, ett_sigcomp_udvm);
+ bytecode_len = len;
+ bytecode_offset = offset;
+ udvm_bytecode_item = proto_tree_add_text(sigcomp_tree, tvb, bytecode_offset, bytecode_len,
+ "Uploaded UDVM bytecode %u (0x%x) bytes", bytecode_len, bytecode_len);
+ sigcomp_udvm_tree = proto_item_add_subtree( udvm_bytecode_item, ett_sigcomp_udvm);
udvm_tvb = tvb_new_subset(tvb, offset, len, len);
if ( dissect_udvm_code )
dissect_udvm_bytecode(udvm_tvb, sigcomp_udvm_tree, destination);
- offset = offset + len;
+
+ offset = offset + len;
+ msg_len = tvb_reported_length_remaining(tvb, offset);
proto_tree_add_text(sigcomp_tree, tvb, offset, -1, "Remaining SigComp message %u bytes",
tvb_reported_length_remaining(tvb, offset));
+
+ msg_tvb = tvb_new_subset(tvb, offset, msg_len, msg_len);
+
+ udvm_exe_item = proto_tree_add_text(sigcomp_tree, tvb, bytecode_offset, bytecode_len,
+ "UDVM execution trace");
+ sigcomp_udvm_exe_tree = proto_item_add_subtree( udvm_exe_item, ett_sigcomp_udvm_exe);
+ decomp_tvb = decompress_sigcomp_message(udvm_tvb, msg_tvb, pinfo,
+ sigcomp_udvm_exe_tree, destination, udvm_print_detail_level);
+ if ( decomp_tvb ){
+ proto_tree_add_text(sigcomp_tree, decomp_tvb, 0, -1,"SigComp message Decompressed WOHO!!");
+ if ( display_raw_txt )
+ tvb_raw_text_add(decomp_tvb, tree);
+ if (check_col(pinfo->cinfo, COL_PROTOCOL)){
+ col_append_str(pinfo->cinfo, COL_PROTOCOL, "/");
+ col_set_fence(pinfo->cinfo,COL_PROTOCOL);
+ }
+ call_dissector(sip_handle, decomp_tvb, pinfo, tree);
+ }
+
}
return tvb_length(tvb);
}
-
-/* Continue adding tree items to process the packet here */
-/* If this protocol has a sub-dissector call it here, see section 1.8 */
-
#define SIGCOMP_INSTR_DECOMPRESSION_FAILURE 0
#define SIGCOMP_INSTR_AND 1
#define SIGCOMP_INSTR_OR 2
break;
case SIGCOMP_INSTR_SORT_DESCENDING: /* 12 SORT-DESCENDING (%start, %n, %k) */
- /* while programming stop while loop */
offset = offset + tvb_reported_length_remaining(udvm_tvb, offset);
break;
case SIGCOMP_INSTR_SHA_1: /* 13 SHA-1 (%position, %length, %destination) */
/* %length, */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_length,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
+ udvm_tvb, start_offset, len, value);
+ }
/* $destination */
offset = dissect_udvm_reference_operand(udvm_tvb, sigcomp_udvm_tree, offset, &start_offset, &value);
break;
case SIGCOMP_INSTR_POP: /* 17 POP (%address) */
- /* %address */ offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, TRUE, &start_offset, &value, &is_memory_address);
+ /* %address */
+ offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, TRUE, &start_offset, &value, &is_memory_address);
len = offset - start_offset;
proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_address,
/* %length, */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_length,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
+ udvm_tvb, start_offset, len, value);
+ }
/* $destination */
offset = dissect_udvm_reference_operand(udvm_tvb, sigcomp_udvm_tree, offset, &start_offset, &value);
/* %length, */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_length,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
+ udvm_tvb, start_offset, len, value);
+ }
/* $destination */
offset = dissect_udvm_reference_operand(udvm_tvb, sigcomp_udvm_tree, offset, &start_offset, &value);
/* %offset */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_offset,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_offset,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_offset,
+ udvm_tvb, start_offset, len, value);
+ }
/* %length, */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_length,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
+ udvm_tvb, start_offset, len, value);
+ }
/* $destination */
offset = dissect_udvm_reference_operand(udvm_tvb, sigcomp_udvm_tree, offset, &start_offset, &value);
/* %length, */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE, &start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_length,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
+ udvm_tvb, start_offset, len, value);
+ }
/* %start_value */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
udvm_tvb, start_offset, len, value);
break;
case SIGCOMP_INSTR_RETURN: /* 25 POP and return */
+
break;
case SIGCOMP_INSTR_SWITCH: /* 26 SWITCH (#n, %j, @address_0, @address_1, ... , @address_n-1) */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
if ( is_memory_address ){
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_value,
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_j,
udvm_tvb, start_offset, len, value);
}else{
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_value,
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_j,
udvm_tvb, start_offset, len, value);
}
/* %length */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_length,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
+ udvm_tvb, start_offset, len, value);
+ }
/* @address */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, TRUE, &start_offset, &value, &is_memory_address);
/* %length */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_length,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
+ udvm_tvb, start_offset, len, value);
+ }
/* %destination */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_destination,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_destination,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_destination,
+ udvm_tvb, start_offset, len, value);
+ }
/* @address */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, TRUE, &start_offset, &value, &is_memory_address);
/* %length */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_length,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_length,
+ udvm_tvb, start_offset, len, value);
+ }
/* %destination */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_destination,
- udvm_tvb, start_offset, len, value);
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_destination,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_destination,
+ udvm_tvb, start_offset, len, value);
+ }
/* @address */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, TRUE, &start_offset, &value, &is_memory_address);
/* %destination */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, FALSE,&start_offset, &value, &is_memory_address);
len = offset - start_offset;
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_destination,
- udvm_tvb, start_offset, len, value);
-
+ if ( is_memory_address ){
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_destination,
+ udvm_tvb, start_offset, len, value);
+ }else{
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_destination,
+ udvm_tvb, start_offset, len, value);
+ }
/* @address */
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, TRUE, &start_offset, &value, &is_memory_address);
len = offset - start_offset;
offset = dissect_udvm_multitype_operand(udvm_tvb, sigcomp_udvm_tree, offset, TRUE, &start_offset, &value, &is_memory_address);
len = offset - start_offset;
if ( is_memory_address ) {
- proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_output_start,
+ proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_addr_output_start,
udvm_tvb, start_offset, len, value);
}else{
proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_output_start,
break;
default:
- /* while programming stop while loop */
offset = offset + tvb_reported_length_remaining(udvm_tvb, offset);
break;
}
if ( display_udvm_bytecode )
proto_tree_add_uint(sigcomp_udvm_tree, hf_udvm_reference_bytecode,
udvm_tvb, offset, 1, display_bytecode);
- operand = ( bytecode & 0x3f);
+ operand = ( bytecode & 0x7f);
*value = (operand * 2);
*start_offset = offset;
offset ++;
}
return offset;
}
+
+static void
+tvb_raw_text_add(tvbuff_t *tvb, proto_tree *tree)
+{
+ proto_tree *raw_tree = NULL;
+ proto_item *ti = NULL;
+ int offset, next_offset, linelen;
+
+ if(tree) {
+ ti = proto_tree_add_item(tree, proto_raw_sigcomp, tvb, 0, -1, FALSE);
+ raw_tree = proto_item_add_subtree(ti, ett_raw_text);
+ }
+
+ offset = 0;
+
+ while (tvb_offset_exists(tvb, offset)) {
+ tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
+ linelen = next_offset - offset;
+ if(raw_tree) {
+ proto_tree_add_text(raw_tree, tvb, offset, linelen,
+ "%s", tvb_format_text(tvb, offset, linelen));
+ }
+ offset = next_offset;
+ }
+}
+
/* Register the protocol with Ethereal */
dissector_add("udp.port", SigCompUDPPort1, sigcomp_handle);
dissector_add("udp.port", SigCompUDPPort2, sigcomp_handle);
+ sip_handle = find_dissector("sip");
+
}
FT_UINT16, BASE_DEC, NULL, 0x0,
"Length", HFILL }
},
+ { &hf_udvm_addr_length,
+ { " %Length[memory address]", "sigcomp.udvm.addr.length",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Length", HFILL }
+ },
{ &hf_udvm_destination,
{ " %Destination", "sigcomp.udvm.destination",
FT_UINT16, BASE_DEC, NULL, 0x0,
"Destination", HFILL }
},
+ { &hf_udvm_addr_destination,
+ { " %Destination[memory address]", "sigcomp.udvm.addr.destination",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Destination", HFILL }
+ },
{ &hf_udvm_at_address,
{ " @Address(mem_add_of_inst + D) mod 2^16)", "sigcomp.udvm.at.address",
FT_UINT16, BASE_DEC, NULL, 0x0,
FT_UINT16, BASE_DEC, NULL, 0x0,
"j", HFILL }
},
+ { &hf_udvm_addr_j,
+ { " %j[memory address]", "sigcomp.udvm.addr.j",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "j", HFILL }
+ },
{ &hf_udvm_output_start,
{ " %Output_start", "sigcomp.output.start",
FT_UINT16, BASE_DEC, NULL, 0x0,
"Output start", HFILL }
},
- { &hf_udvm_output_start_addr,
- { " %Output_start[memory address]", "sigcomp.output.start.addr",
+ { &hf_udvm_addr_output_start,
+ { " %Output_start[memory address]", "sigcomp.addr.output.start",
FT_UINT16, BASE_DEC, NULL, 0x0,
"Output start", HFILL }
},
FT_UINT16, BASE_DEC, NULL, 0x0,
"Offset", HFILL }
},
+ { &hf_udvm_addr_offset,
+ { " %Offset[memory address]", "sigcomp.udvm.addr.offset",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Offset", HFILL }
+ },
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_sigcomp,
&ett_sigcomp_udvm,
+ &ett_sigcomp_udvm_exe,
+ };
+ static gint *ett_raw[] = {
+ &ett_raw_text,
};
module_t *sigcomp_module;
+ static enum_val_t udvm_detail_vals[] = {
+ {"no-printout", "No-Printout", 0},
+ {"low-detail", "Low-detail", 1},
+ {"medium-detail", "medium-detail", 2},
+ {"high-detail", "High-detail", 3},
+ {NULL, NULL, -1}
+ };
+
/* Register the protocol name and description */
proto_sigcomp = proto_register_protocol("Signaling Compression",
"SIGCOMP", "sigcomp");
+ proto_raw_sigcomp = proto_register_protocol("Decompressed SigComp message as raw text",
+ "Raw_SigComp", "raw_sigcomp");
+
+ register_dissector("sigcomp", dissect_sigcomp, proto_sigcomp);
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_sigcomp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
+ proto_register_subtree_array(ett_raw, array_length(ett_raw));
/* Register a configuration option for port */
sigcomp_module = prefs_register_protocol(proto_sigcomp,
"Display the bytecode of operands",
"preference wether to display the bytecode in UDVM operands or not",
&display_udvm_bytecode);
+ prefs_register_bool_preference(sigcomp_module, "display.decomp.msg.as.txt",
+ "Displays the decompressed message as text",
+ "preference wether to display the decompressed message as raw text or not",
+ &display_raw_txt);
+ prefs_register_enum_preference(sigcomp_module, "show.udvm.execution",
+ "Level of detail of UDVM execution",
+ "0 = UDVM executes silently, then incrising detail about execution of UDVM instructions, Warning! CPU intense at high detail",
+ &udvm_print_detail_level, udvm_detail_vals, FALSE);
}